Skip to content

Commit

Permalink
feat: selected node reflects in url hash
Browse files Browse the repository at this point in the history
Signed-off-by: Zxilly <[email protected]>
  • Loading branch information
Zxilly committed Jun 26, 2024
1 parent e91a516 commit ec66e5e
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 36 deletions.
3 changes: 0 additions & 3 deletions ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ Uses code from [rollup-plugin-visualizer](https://github.com/btd/rollup-plugin-v

## Build

You should create a `data.json` with `gsa -f json --compact -o data.json <file>` and place it in the root of the project.
This file will be used as a data source when `pnpm dev` run.

```bash
pnpm install
pnpm run build:ui
Expand Down
8 changes: 1 addition & 7 deletions ui/src/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React, {useLayoutEffect, useMemo, useRef} from "react";
import {Entry} from "./tool/entry.ts";
import {NodeColorGetter} from "./tool/color.ts";
import {PADDING, TOP_PADDING} from "./tool/const.ts";
import {trimPrefix} from "./tool/utils.ts";

type NodeEventHandler = (event: HierarchyRectangularNode<Entry>) => void;

Expand Down Expand Up @@ -42,12 +41,7 @@ export const Node: React.FC<NodeProps> = (
}, [children, height, width])

const title = useMemo(() => {
const t = node.data.getName()
let display = trimPrefix(t, "/")
if (node.data.getType() === "symbol") {
display = trimPrefix(display, ".")
}
return display
return node.data.getName()
}, [node.data])

useLayoutEffect(() => {
Expand Down
56 changes: 53 additions & 3 deletions ui/src/TreeMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,20 @@ import {Tooltip} from "./Tooltip.tsx";
import {Node} from "./Node.tsx";

import "./style.scss"
import {trimPrefix} from "./tool/utils.ts";
import {useHash} from "./tool/useHash.ts";

interface TreeMapProps {
entry: Entry
}

function TreeMap({entry}: TreeMapProps) {
// Set the document title to the name of the entry
useTitle(entry.getName())
useTitle(entry.getName(), {
restoreOnUnmount: true
})

const [hash, setHash] = useHash()

// Get the window size
const {width, height} = useWindowSize()
Expand Down Expand Up @@ -94,6 +100,50 @@ function TreeMap({entry}: TreeMapProps) {
return cache;
}, [root])

const setSelectedNodeWithHash = useCallback((node: HierarchyRectangularNode<Entry> | null) => {
setSelectedNode(node)

if (node === null) {
setHash("")
return
}

setHash(
node
.ancestors()
.map((d) => {
return d.data.getURLSafeName()
})
.reverse()
.join("#")
)
}, [setHash])

useEffect(() => {
const parts = trimPrefix(hash, "#").split("#")
if (parts.length >= 1) {
const base = parts[0]
if (base !== entry.getURLSafeName()) {
return
}
}
let cur = root
for (let i = 1; i < parts.length; i++) {
const part = parts[i]
if (!cur.children) {
return;
}

const found = cur.children.find((d) => d.data.getURLSafeName() === part)
if (!found) {
return;
}
cur = found
}

setSelectedNode(cur)
}, [hash, root, entry])

const [showTooltip, setShowTooltip] = useState(false);
const [tooltipNode, setTooltipNode] =
useState<HierarchyRectangularNode<Entry> | undefined>(undefined);
Expand Down Expand Up @@ -161,10 +211,10 @@ function TreeMap({entry}: TreeMapProps) {
nestedData={nestedData}
selectedNode={selectedNode}
getModuleColor={getModuleColor}
setSelectedNode={setSelectedNode}
setSelectedNode={setSelectedNodeWithHash}
/>
)
}, [getModuleColor, nestedData, selectedNode])
}, [getModuleColor, nestedData, selectedNode, setSelectedNodeWithHash])

return (
<>
Expand Down
8 changes: 0 additions & 8 deletions ui/src/explorer/Explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,6 @@ export const Explorer: React.FC = () => {
}
}, [loadError, loading, file, result, analyzing, inst, entry])

useEffect(() => {
if (entry) {
window.location.hash = entry.getName()
} else {
history.replaceState(null, "", ' ');
}
}, [entry])

return <>
<Dialog
open={modalState.isOpen}
Expand Down
41 changes: 29 additions & 12 deletions ui/src/tool/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export interface EntryLike<T extends EntryType> {

getName(): string;

getURLSafeName(): string;

getChildren(): EntryChildren[T]

getID(): number;
Expand All @@ -36,6 +38,14 @@ class BaseImpl {
getID(): number {
return this.id;
}

getName(): string {
throw new Error("Not implemented");
}

getURLSafeName(): string {
return this.getName();
}
}

export class SectionImpl extends BaseImpl implements EntryLike<"section"> {
Expand All @@ -48,7 +58,7 @@ export class SectionImpl extends BaseImpl implements EntryLike<"section"> {
}


getName(): string {
override getName(): string {
return this.data.name;
}

Expand Down Expand Up @@ -84,7 +94,7 @@ export class FileImpl extends BaseImpl implements EntryLike<"file"> {
return [];
}

getName(): string {
override getName(): string {
return this.data.file_path.split("/").pop()!;
}

Expand Down Expand Up @@ -123,7 +133,7 @@ export class PackageImpl extends BaseImpl implements EntryLike<"package"> {
}

for (const s of data.symbols) {
children.push(new SymbolImpl(s));
children.push(new SymbolImpl(s, data.name));
}

const leftSize = data.size - children.reduce((acc, child) => acc + child.getSize(), 0);
Expand All @@ -139,9 +149,9 @@ export class PackageImpl extends BaseImpl implements EntryLike<"package"> {
return this.children;
}

getName(): string {
override getName(): string {
if (this.parent != null) {
return trimPrefix(this.data.name, this.parent);
return trimPrefix(this.data.name, this.parent + "/");
}

return this.data.name;
Expand Down Expand Up @@ -173,7 +183,7 @@ export class DisasmImpl extends BaseImpl implements EntryLike<"disasm"> {
return [];
}

getName(): string {
override getName(): string {
return this.name;
}

Expand All @@ -198,16 +208,16 @@ export class DisasmImpl extends BaseImpl implements EntryLike<"disasm"> {
}

export class SymbolImpl extends BaseImpl implements EntryLike<"symbol"> {
constructor(private readonly data: FileSymbol) {
constructor(private readonly data: FileSymbol, private readonly parent: string) {
super();
}

getChildren(): EntryChildren["symbol"] {
return [];
}

getName(): string {
return this.data.name;
override getName(): string {
return trimPrefix(this.data.name, this.parent + ".")
}

getSize(): number {
Expand All @@ -230,17 +240,22 @@ export class SymbolImpl extends BaseImpl implements EntryLike<"symbol"> {

export class ContainerImpl extends BaseImpl implements EntryLike<"container"> {
constructor(private readonly name: string,
private readonly shortName: string,
private readonly size: number,
private readonly children: EntryChildren["container"],
private readonly explain: string = "") {
super();
}

override getURLSafeName(): string {
return this.shortName;
}

getChildren(): EntryChildren["container"] {
return this.children;
}

getName(): string {
override getName(): string {
return this.name;
}

Expand Down Expand Up @@ -270,7 +285,7 @@ export class UnknownImpl extends BaseImpl implements EntryLike<"unknown"> {
return [];
}

getName(): string {
override getName(): string {
return "Unknown";
}

Expand Down Expand Up @@ -309,6 +324,7 @@ export class ResultImpl extends BaseImpl implements EntryLike<"result"> {
const sectionContainerSize = sectionContainerChildren.reduce((acc, child) => acc + child.getSize(), 0);
const sectionContainer = new ContainerImpl(
"Unknown Sections Size",
"unk-sections",
sectionContainerSize,
sectionContainerChildren,
"The unknown size of the sections in the binary.");
Expand All @@ -330,6 +346,7 @@ export class ResultImpl extends BaseImpl implements EntryLike<"result"> {
const packageContainerSize = packageContainerChildren.reduce((acc, child) => acc + child.getSize(), 0);
const packageContainer = new ContainerImpl(
`${title(type)} Packages Size`,
`${type}-packages`,
packageContainerSize,
packageContainerChildren,
`The size of the ${type} packages in the binary.`
Expand All @@ -351,7 +368,7 @@ export class ResultImpl extends BaseImpl implements EntryLike<"result"> {
return this.children;
}

getName(): string {
override getName(): string {
return this.data.name;
}

Expand Down
38 changes: 38 additions & 0 deletions ui/src/tool/useHash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {useCallback, useState} from 'react';
import {useLifecycles} from "react-use";
import {off, on} from "react-use/lib/misc/util";

/**
* read and write url hash, response to url hash change
*/
export const useHash = () => {
const [hash, setHash] = useState(() => window.location.hash);

const onHashChange = useCallback(() => {
setHash(window.location.hash);
}, []);

useLifecycles(
() => {
on(window, 'hashchange', onHashChange);
},
() => {
off(window, 'hashchange', onHashChange);
}
);

const _setHash = useCallback(
(newHash: string) => {
if (newHash !== hash) {
if (newHash !== "") {
window.location.hash = newHash;
} else {
history.pushState(null, "", ' ');
}
}
},
[hash]
);

return [hash, _setHash] as const;
};
5 changes: 2 additions & 3 deletions ui/src/tool/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ export function title(s: string): string {

export function trimPrefix(str: string, prefix: string) {
if (str.startsWith(prefix)) {
return str.slice(prefix.length)
} else {
return str
return str.slice(prefix.length);
}
return str;
}

0 comments on commit ec66e5e

Please sign in to comment.