<commit_before>import ReactDOM from "react-dom";

import { docReady } from "../../components/utils";
import * as Settings from "../../components/settings";
import * as Metrics from "../../components/metrics";

import init, * as Lancelot from "../../../../jslancelot/pkg/jslancelot";
import NOP from "./nop.bin";

export const APP = {
    set_theme: (name: string) => {
        document.getElementsByTagName("html")[0].className = `lancelot-theme-${name}`;
        Settings.set("theme", name);
    },
};

// helpers for jslancelot
type u64 = bigint | BigInt;
type address = u64;

// an address that may be valid in this workspace.
const Address = ({address} : {address: address}) => (
    <span className="lancelot-address bp4-monospace-text">0x{address.toString(0x10)}</span>
);

// a size of an item or delta/difference between two addresses.
const Size = ({size} : {size: u64}) => (
    <span className="lancelot-size bp4-monospace-text">0x{size.toString(0x10)}</span>
);

// the name of a location for which the address is known.
const NamedLocation = ({name, address} : {name: string, address: address}) => (
    <span className="lancelot-named-location bp4-monospace-text">{name}</span>
);

const AppPage = ({ version, buf, ws, pe }: any) => (
    <div id="app">
        <header style={{padding: "8px"}}>
            <nav>lancelot version: {version}</nav>
        </header>
        <div style={{padding: "8px"}}>
            <p>size: <Size size={buf.length} /></p>

            <p>arch: {ws.arch}</p>

            <p>sections:</p>

            <table>
                <thead>
                    <tr>
                        <th>name</th>
                        <th>start</th>
                        <th>end</th>
                        <th>size</th>
                    </tr>
                </thead>
                <tbody>
                {Array.prototype.map.call(ws.sections, (section: Lancelot.Section) => (
                    <tr key={section.name}>
                        <td><NamedLocation name={section.name} address={section.virtual_range.start} /></td>
                        <td><Address address={section.virtual_range.start} /></td>
                        <td><Address address={section.virtual_range.end} /></td>
                        <td><Size size={section.virtual_range.size} /></td>
                    </tr>
                ))}
                </tbody>
            </table>

            <p>functions:</p>

            <ul>
                {Array.prototype.map.call(ws.functions, (f: BigInt) => (
                    <li key={f.toString()}><Address address={f} /></li>
                ))}
            </ul>
        </div>
    </div>
);

function to_js<T>(obj: T): T {
    if (Array.isArray(obj)) {
        const ret = obj.map(to_js);
        for (const v in obj) {
            (v as any).free();
        }
        return ret as any as T;
    } else {
        const obj_ = obj as any;
        const ret: any = {};
        for (const [name, desc] of Object.entries(Object.getOwnPropertyDescriptors(Object.getPrototypeOf(obj)))) {
            if (desc.get !== undefined) {
                // is a getter
                let v = obj_[name];

                if (Object.hasOwnProperty.call(v, "ptr")) {
                    // is a wasm-bindgen object
                    // recursively convert to JS.
                    v = to_js(v);
                }

                ret[name] = v;
            }
        }
        obj_.free();
        return ret;
    }
}

// fixup the PE instance to return JS objects
// rather than wasm-allocated objects.
//
// notably, the wasm memory for these objects is immediately free'd,
// rather than JS having to track when to .free() each object.
//
// alternatively, we could have used serde to construct objects via JSON,
// but u64 is translated to BigInt, which isn't supported by JSON.
function pe_from_bytes(buf: Uint8Array): Lancelot.PE {
    const proxy: any = {
        get: function(target: Lancelot.PE, prop: string) {
            if (prop === "sections") {<commit_msg>pwa: more doc around wasm_to_js helper<commit_after>import ReactDOM from "react-dom";

import { docReady } from "../../components/utils";
import * as Settings from "../../components/settings";
import * as Metrics from "../../components/metrics";

import init, * as Lancelot from "../../../../jslancelot/pkg/jslancelot";
import NOP from "./nop.bin";

export const APP = {
    set_theme: (name: string) => {
        document.getElementsByTagName("html")[0].className = `lancelot-theme-${name}`;
        Settings.set("theme", name);
    },
};

// helpers for jslancelot
type u64 = bigint | BigInt;
type address = u64;

// an address that may be valid in this workspace.
const Address = ({address} : {address: address}) => (
    <span className="lancelot-address bp4-monospace-text">0x{address.toString(0x10)}</span>
);

// a size of an item or delta/difference between two addresses.
const Size = ({size} : {size: u64}) => (
    <span className="lancelot-size bp4-monospace-text">0x{size.toString(0x10)}</span>
);

// the name of a location for which the address is known.
const NamedLocation = ({name, address} : {name: string, address: address}) => (
    <span className="lancelot-named-location bp4-monospace-text">{name}</span>
);

const AppPage = ({ version, buf, ws, pe }: any) => (
    <div id="app">
        <header style={{padding: "8px"}}>
            <nav>lancelot version: {version}</nav>
        </header>
        <div style={{padding: "8px"}}>
            <p>size: <Size size={buf.length} /></p>

            <p>arch: {ws.arch}</p>

            <p>sections:</p>

            <table>
                <thead>
                    <tr>
                        <th>name</th>
                        <th>start</th>
                        <th>end</th>
                        <th>size</th>
                    </tr>
                </thead>
                <tbody>
                {Array.prototype.map.call(ws.sections, (section: Lancelot.Section) => (
                    <tr key={section.name}>
                        <td><NamedLocation name={section.name} address={section.virtual_range.start} /></td>
                        <td><Address address={section.virtual_range.start} /></td>
                        <td><Address address={section.virtual_range.end} /></td>
                        <td><Size size={section.virtual_range.size} /></td>
                    </tr>
                ))}
                </tbody>
            </table>

            <p>functions:</p>

            <ul>
                {Array.prototype.map.call(ws.functions, (f: BigInt) => (
                    <li key={f.toString()}><Address address={f} /></li>
                ))}
            </ul>
        </div>
    </div>
);

// convert the given object from a wasm-allocated object to
// a real JS object. then, free the wasm-allocated object.
//
// the assumes the given object is either:
//  - a wasm-bindgen created object (e.g. with .ptr), or
//  - a list of wasm-bindgen created objects.
function to_js<T>(obj: T): T {
    if (Array.isArray(obj)) {
        const ret = obj.map(to_js);
        for (const v in obj) {
            (v as any).free();
        }
        return ret as any as T;
    } else {
        const obj_ = obj as any;
        const ret: any = {};
        for (const [name, desc] of Object.entries(Object.getOwnPropertyDescriptors(Object.getPrototypeOf(obj)))) {
            if (desc.get !== undefined) {
                // is a getter
                let v = obj_[name];

                if (Object.hasOwnProperty.call(v, "ptr")) {
                    // is a wasm-bindgen object
                    // recursively convert to JS.
                    v = to_js(v);
                }

                // TODO: handle lists

                ret[name] = v;
            }
        }
        obj_.free();
        return ret;
    }
}

// fixup the PE instance to return JS objects
// rather than wasm-allocated objects.
//
// notably, the wasm memory for these objects is immediately free'd,
// rather than JS having to track when to .free() each object.
//
// alternatively, we could have used serde to construct objects via JSON,
// but u64 is translated to BigInt, which isn't supported by JSON.
//
// or, we could use #[wasm_bindgen(inspectable)] and .toJSON(),
// but this doesn't apply recusively, see:
// https://github.com/rustwasm/wasm-bindgen/issues/2292
function pe_from_bytes(buf: Uint8Array): Lancelot.PE {
    const proxy: any = {
        get: function(target: Lancelot.PE, prop: string) {
            if (prop === "sections") {
{'fraction_non_alphanumeric': 0.1510380219267553, 'fraction_numerical': 0.005831583858175881, 'mean_word_length': 1.7886178861788617, 'pattern_counts': {'":': 0, '<': 121, '<?xml version=': 0, '>': 135, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7533110', 'n_tokens_mistral': 2605, 'n_tokens_neox': 2500, 'n_words': 679}