mirror of https://github.com/encounter/objdiff.git
Overall wasm refactoring & improvements
This commit is contained in:
parent
0fccae1049
commit
1f4175dc21
|
@ -780,6 +780,16 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_log"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f"
|
||||
dependencies = [
|
||||
"log",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const_format"
|
||||
version = "0.2.32"
|
||||
|
@ -2851,6 +2861,8 @@ dependencies = [
|
|||
"anyhow",
|
||||
"arm-attr",
|
||||
"byteorder",
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"cpp_demangle",
|
||||
"cwdemangle",
|
||||
"cwextab",
|
||||
|
@ -2876,6 +2888,7 @@ dependencies = [
|
|||
"serde_yaml",
|
||||
"similar",
|
||||
"strum",
|
||||
"tsify-next",
|
||||
"unarm",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
@ -3860,6 +3873,17 @@ dependencies = [
|
|||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-wasm-bindgen"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.199"
|
||||
|
@ -3871,6 +3895,17 @@ dependencies = [
|
|||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive_internals"
|
||||
version = "0.29.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.116"
|
||||
|
@ -4447,6 +4482,30 @@ version = "0.2.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "tsify-next"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f4a645dca4ee0800f5ab60ce166deba2db6a0315de795a2691e138a3d55d756"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"tsify-next-macros",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tsify-next-macros"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d5c06f8a51d759bb58129e30b2631739e7e1e4579fad1f30ac09a6c88e488a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.20.0"
|
||||
|
|
|
@ -23,7 +23,7 @@ mips = ["any-arch", "rabbitizer"]
|
|||
ppc = ["any-arch", "cwdemangle", "cwextab", "ppc750cl"]
|
||||
x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"]
|
||||
arm = ["any-arch", "cpp_demangle", "unarm", "arm-attr"]
|
||||
wasm = ["serde_json"]
|
||||
wasm = ["serde_json", "console_error_panic_hook", "console_log"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.82"
|
||||
|
@ -40,6 +40,9 @@ serde = { version = "1", features = ["derive"] }
|
|||
similar = { version = "2.5.0", default-features = false }
|
||||
strum = { version = "0.26.2", features = ["derive"] }
|
||||
wasm-bindgen = "0.2.93"
|
||||
tsify-next = { version = "0.5.4", default-features = false, features = ["js"] }
|
||||
console_log = { version = "1.0.0", optional = true }
|
||||
console_error_panic_hook = { version = "0.1.7", optional = true }
|
||||
|
||||
# config
|
||||
globset = { version = "0.4.14", features = ["serde1"], optional = true }
|
||||
|
|
|
@ -1,53 +1,78 @@
|
|||
use anyhow::Context;
|
||||
use prost::Message;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::{bindings::diff::DiffResult, diff, obj};
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn run_diff(
|
||||
fn parse_object(
|
||||
data: Option<Box<[u8]>>,
|
||||
config: &diff::DiffObjConfig,
|
||||
) -> Result<Option<obj::ObjInfo>, JsError> {
|
||||
data.as_ref().map(|data| obj::read::parse(data, config)).transpose().to_js()
|
||||
}
|
||||
|
||||
fn parse_and_run_diff(
|
||||
left: Option<Box<[u8]>>,
|
||||
right: Option<Box<[u8]>>,
|
||||
config: diff::DiffObjConfig,
|
||||
) -> Result<String, JsError> {
|
||||
let target = left
|
||||
.as_ref()
|
||||
.map(|data| obj::read::parse(data, &config).context("Loading target"))
|
||||
.transpose()
|
||||
.map_err(|e| JsError::new(&e.to_string()))?;
|
||||
let base = right
|
||||
.as_ref()
|
||||
.map(|data| obj::read::parse(data, &config).context("Loading base"))
|
||||
.transpose()
|
||||
.map_err(|e| JsError::new(&e.to_string()))?;
|
||||
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)
|
||||
.map_err(|e| JsError::new(&e.to_string()))?;
|
||||
let left = target.as_ref().and_then(|o| result.left.as_ref().map(|d| (o, d)));
|
||||
let right = base.as_ref().and_then(|o| result.right.as_ref().map(|d| (o, d)));
|
||||
let out = DiffResult::new(left, right);
|
||||
serde_json::to_string(&out).map_err(|e| JsError::new(&e.to_string()))
|
||||
) -> Result<DiffResult, JsError> {
|
||||
let target = parse_object(left, &config)?;
|
||||
let base = parse_object(right, &config)?;
|
||||
run_diff(target.as_ref(), base.as_ref(), config)
|
||||
}
|
||||
|
||||
fn run_diff(
|
||||
left: Option<&obj::ObjInfo>,
|
||||
right: Option<&obj::ObjInfo>,
|
||||
config: diff::DiffObjConfig,
|
||||
) -> Result<DiffResult, JsError> {
|
||||
log::debug!("Running diff with config: {:?}", config);
|
||||
let result = diff::diff_objs(&config, left, right, None).to_js()?;
|
||||
let left = left.and_then(|o| result.left.as_ref().map(|d| (o, d)));
|
||||
let right = right.and_then(|o| result.right.as_ref().map(|d| (o, d)));
|
||||
Ok(DiffResult::new(left, right))
|
||||
}
|
||||
|
||||
// #[wasm_bindgen]
|
||||
// pub fn run_diff_json(
|
||||
// left: Option<Box<[u8]>>,
|
||||
// right: Option<Box<[u8]>>,
|
||||
// config: diff::DiffObjConfig,
|
||||
// ) -> Result<String, JsError> {
|
||||
// let out = run_diff_opt_box(left, right, config)?;
|
||||
// serde_json::to_string(&out).map_err(|e| JsError::new(&e.to_string()))
|
||||
// }
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn run_diff_proto(
|
||||
left: Option<Box<[u8]>>,
|
||||
right: Option<Box<[u8]>>,
|
||||
config: diff::DiffObjConfig,
|
||||
) -> Result<Box<[u8]>, JsError> {
|
||||
let target = left
|
||||
.as_ref()
|
||||
.map(|data| obj::read::parse(data, &config).context("Loading target"))
|
||||
.transpose()
|
||||
.map_err(|e| JsError::new(&e.to_string()))?;
|
||||
let base = right
|
||||
.as_ref()
|
||||
.map(|data| obj::read::parse(data, &config).context("Loading base"))
|
||||
.transpose()
|
||||
.map_err(|e| JsError::new(&e.to_string()))?;
|
||||
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)
|
||||
.map_err(|e| JsError::new(&e.to_string()))?;
|
||||
let left = target.as_ref().and_then(|o| result.left.as_ref().map(|d| (o, d)));
|
||||
let right = base.as_ref().and_then(|o| result.right.as_ref().map(|d| (o, d)));
|
||||
let out = DiffResult::new(left, right);
|
||||
let out = parse_and_run_diff(left, right, config)?;
|
||||
Ok(out.encode_to_vec().into_boxed_slice())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
fn start() -> Result<(), JsError> {
|
||||
console_error_panic_hook::set_once();
|
||||
#[cfg(debug_assertions)]
|
||||
console_log::init_with_level(log::Level::Debug).to_js()?;
|
||||
#[cfg(not(debug_assertions))]
|
||||
console_log::init_with_level(log::Level::Info).to_js()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_js_error(e: impl std::fmt::Display) -> JsError { JsError::new(&e.to_string()) }
|
||||
|
||||
trait ToJsResult {
|
||||
type Output;
|
||||
|
||||
fn to_js(self) -> Result<Self::Output, JsError>;
|
||||
}
|
||||
|
||||
impl<T, E: std::fmt::Display> ToJsResult for Result<T, E> {
|
||||
type Output = T;
|
||||
|
||||
fn to_js(self) -> Result<T, JsError> { self.map_err(to_js_error) }
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use anyhow::Result;
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
use crate::{
|
||||
diff::{
|
||||
|
@ -18,7 +17,6 @@ pub mod code;
|
|||
pub mod data;
|
||||
pub mod display;
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
|
@ -30,6 +28,7 @@ pub mod display;
|
|||
serde::Serialize,
|
||||
strum::VariantArray,
|
||||
strum::EnumMessage,
|
||||
tsify_next::Tsify,
|
||||
)]
|
||||
pub enum X86Formatter {
|
||||
#[default]
|
||||
|
@ -43,7 +42,6 @@ pub enum X86Formatter {
|
|||
Masm,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
|
@ -55,6 +53,7 @@ pub enum X86Formatter {
|
|||
serde::Serialize,
|
||||
strum::VariantArray,
|
||||
strum::EnumMessage,
|
||||
tsify_next::Tsify,
|
||||
)]
|
||||
pub enum MipsAbi {
|
||||
#[default]
|
||||
|
@ -68,7 +67,6 @@ pub enum MipsAbi {
|
|||
N64,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
|
@ -80,6 +78,7 @@ pub enum MipsAbi {
|
|||
serde::Serialize,
|
||||
strum::VariantArray,
|
||||
strum::EnumMessage,
|
||||
tsify_next::Tsify,
|
||||
)]
|
||||
pub enum MipsInstrCategory {
|
||||
#[default]
|
||||
|
@ -97,7 +96,6 @@ pub enum MipsInstrCategory {
|
|||
R5900,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
|
@ -109,6 +107,7 @@ pub enum MipsInstrCategory {
|
|||
serde::Serialize,
|
||||
strum::VariantArray,
|
||||
strum::EnumMessage,
|
||||
tsify_next::Tsify,
|
||||
)]
|
||||
pub enum ArmArchVersion {
|
||||
#[default]
|
||||
|
@ -122,7 +121,6 @@ pub enum ArmArchVersion {
|
|||
V6K,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
|
@ -134,6 +132,7 @@ pub enum ArmArchVersion {
|
|||
serde::Serialize,
|
||||
strum::VariantArray,
|
||||
strum::EnumMessage,
|
||||
tsify_next::Tsify,
|
||||
)]
|
||||
pub enum ArmR9Usage {
|
||||
#[default]
|
||||
|
@ -154,8 +153,8 @@ pub enum ArmR9Usage {
|
|||
#[inline]
|
||||
const fn default_true() -> bool { true }
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize, tsify_next::Tsify)]
|
||||
#[tsify(from_wasm_abi)]
|
||||
#[serde(default)]
|
||||
pub struct DiffObjConfig {
|
||||
pub relax_reloc_diffs: bool,
|
||||
|
@ -207,9 +206,6 @@ impl DiffObjConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn default_diff_obj_config() -> DiffObjConfig { Default::default() }
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjSectionDiff {
|
||||
pub symbols: Vec<ObjSymbolDiff>,
|
||||
|
|
|
@ -7,5 +7,22 @@ export default [
|
|||
{languageOptions: {globals: globals.browser}},
|
||||
pluginJs.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{rules: {"semi": [2, "always"]}},
|
||||
{
|
||||
rules: {
|
||||
"semi": [2, "always"],
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
// https://typescript-eslint.io/rules/no-unused-vars/#benefits-over-typescript
|
||||
{
|
||||
"args": "all",
|
||||
"argsIgnorePattern": "^_",
|
||||
"caughtErrors": "all",
|
||||
"caughtErrorsIgnorePattern": "^_",
|
||||
"destructuredArrayIgnorePattern": "^_",
|
||||
"varsIgnorePattern": "^_",
|
||||
"ignoreRestSiblings": true
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
];
|
|
@ -1,24 +1,24 @@
|
|||
{
|
||||
"name": "objdiff-wasm",
|
||||
"version": "2.0.0-beta.6",
|
||||
"version": "2.0.0-beta.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "objdiff-wasm",
|
||||
"version": "2.0.0-beta.6",
|
||||
"version": "2.0.0-beta.10",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@protobuf-ts/runtime": "2.9.4"
|
||||
"@protobuf-ts/runtime": "^2.9.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.9.0",
|
||||
"@protobuf-ts/plugin": "2.9.4",
|
||||
"@types/node": "22.4.1",
|
||||
"esbuild": "0.23.1",
|
||||
"@protobuf-ts/plugin": "^2.9.4",
|
||||
"@types/node": "^22.4.1",
|
||||
"esbuild": "^0.23.1",
|
||||
"eslint": "^9.9.0",
|
||||
"globals": "^15.9.0",
|
||||
"tsup": "8.2.4",
|
||||
"tsup": "^8.2.4",
|
||||
"typescript-eslint": "^8.2.0"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "objdiff-wasm",
|
||||
"version": "2.0.0-beta.6",
|
||||
"version": "2.0.0-beta.10",
|
||||
"description": "A local diffing tool for decompilation projects.",
|
||||
"author": {
|
||||
"name": "Luke Street",
|
||||
|
@ -19,21 +19,21 @@
|
|||
"types": "dist/main.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"build:all": "npm run build && npm run build:proto && npm run build:wasm",
|
||||
"build:all": "npm run build:wasm && npm run build:proto && npm run build",
|
||||
"build:proto": "protoc --ts_out=gen --ts_opt add_pb_suffix,eslint_disable,ts_nocheck,use_proto_field_name --proto_path=../objdiff-core/protos ../objdiff-core/protos/*.proto",
|
||||
"build:wasm": "cd ../objdiff-core && wasm-pack build --out-dir ../objdiff-wasm/pkg --target web -- --features arm,dwarf,ppc,x86,wasm"
|
||||
},
|
||||
"dependencies": {
|
||||
"@protobuf-ts/runtime": "2.9.4"
|
||||
"@protobuf-ts/runtime": "^2.9.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.9.0",
|
||||
"@protobuf-ts/plugin": "2.9.4",
|
||||
"@types/node": "22.4.1",
|
||||
"esbuild": "0.23.1",
|
||||
"@protobuf-ts/plugin": "^2.9.4",
|
||||
"@types/node": "^22.4.1",
|
||||
"esbuild": "^0.23.1",
|
||||
"eslint": "^9.9.0",
|
||||
"globals": "^15.9.0",
|
||||
"tsup": "8.2.4",
|
||||
"tsup": "^8.2.4",
|
||||
"typescript-eslint": "^8.2.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,61 +2,94 @@ import {ArgumentValue, DiffResult, InstructionDiff, RelocationTarget} from "../g
|
|||
import type {
|
||||
ArmArchVersion,
|
||||
ArmR9Usage,
|
||||
DiffObjConfig as WasmDiffObjConfig,
|
||||
DiffObjConfig,
|
||||
MipsAbi,
|
||||
MipsInstrCategory,
|
||||
X86Formatter
|
||||
} from '../pkg';
|
||||
import {InMessage, OutMessage} from './worker';
|
||||
import {AnyHandlerData, InMessage, OutMessage} from './worker';
|
||||
|
||||
// Export wasm types
|
||||
export type DiffObjConfig = Omit<Partial<WasmDiffObjConfig>, 'free'>;
|
||||
export {ArmArchVersion, ArmR9Usage, MipsAbi, MipsInstrCategory, X86Formatter};
|
||||
export {ArmArchVersion, ArmR9Usage, MipsAbi, MipsInstrCategory, X86Formatter, DiffObjConfig};
|
||||
|
||||
// Export protobuf types
|
||||
export * from '../gen/diff_pb';
|
||||
|
||||
interface PromiseCallbacks {
|
||||
interface PromiseCallbacks<T> {
|
||||
start: number;
|
||||
resolve: (value: unknown) => void;
|
||||
reject: (reason?: unknown) => void;
|
||||
resolve: (value: T | PromiseLike<T>) => void;
|
||||
reject: (reason?: string) => void;
|
||||
}
|
||||
|
||||
let workerInit = false;
|
||||
let workerCallbacks: PromiseCallbacks | null = null;
|
||||
let workerCallbacks: PromiseCallbacks<Worker>;
|
||||
const workerReady = new Promise<Worker>((resolve, reject) => {
|
||||
workerCallbacks = {start: performance.now(), resolve, reject};
|
||||
});
|
||||
|
||||
export function initialize(workerUrl?: string | URL) {
|
||||
export async function initialize(data?: {
|
||||
workerUrl?: string | URL,
|
||||
wasmUrl?: string | URL, // Relative to worker URL
|
||||
}): Promise<Worker> {
|
||||
if (workerInit) {
|
||||
return;
|
||||
return workerReady;
|
||||
}
|
||||
workerInit = true;
|
||||
const worker = new Worker(workerUrl || 'worker.js', {type: 'module'});
|
||||
worker.onmessage = onMessage.bind(null, worker);
|
||||
worker.onerror = (error) => {
|
||||
console.error("Worker error", error);
|
||||
workerCallbacks.reject(error);
|
||||
let {workerUrl, wasmUrl} = data || {};
|
||||
if (!workerUrl) {
|
||||
try {
|
||||
// Bundlers will convert this into an asset URL
|
||||
workerUrl = new URL('./worker.js', import.meta.url);
|
||||
} catch (_) {
|
||||
workerUrl = 'worker.js';
|
||||
}
|
||||
}
|
||||
if (!wasmUrl) {
|
||||
try {
|
||||
// Bundlers will convert this into an asset URL
|
||||
wasmUrl = new URL('./objdiff_core_bg.wasm', import.meta.url);
|
||||
} catch (_) {
|
||||
wasmUrl = 'objdiff_core_bg.js';
|
||||
}
|
||||
}
|
||||
const worker = new Worker(workerUrl, {
|
||||
name: 'objdiff',
|
||||
type: 'module',
|
||||
});
|
||||
worker.onmessage = onMessage;
|
||||
worker.onerror = (event) => {
|
||||
console.error("Worker error", event);
|
||||
workerCallbacks.reject("Worker failed to initialize, wrong URL?");
|
||||
};
|
||||
defer<void>({
|
||||
type: 'init',
|
||||
// URL can't be sent directly
|
||||
wasmUrl: wasmUrl.toString(),
|
||||
}, worker).then(() => {
|
||||
workerCallbacks.resolve(worker);
|
||||
}, (e) => {
|
||||
workerCallbacks.reject(e);
|
||||
});
|
||||
return workerReady;
|
||||
}
|
||||
|
||||
let globalMessageId = 0;
|
||||
const messageCallbacks = new Map<number, PromiseCallbacks>();
|
||||
const messageCallbacks = new Map<number, PromiseCallbacks<never>>();
|
||||
|
||||
function onMessage(worker: Worker, event: MessageEvent<OutMessage>) {
|
||||
function onMessage(event: MessageEvent<OutMessage>) {
|
||||
switch (event.data.type) {
|
||||
case 'ready':
|
||||
workerCallbacks.resolve(worker);
|
||||
break;
|
||||
case 'result': {
|
||||
const {messageId, result} = event.data;
|
||||
const {result, error, messageId} = event.data;
|
||||
const callbacks = messageCallbacks.get(messageId);
|
||||
if (callbacks) {
|
||||
const end = performance.now();
|
||||
console.debug(`Message ${messageId} took ${end - callbacks.start}ms`);
|
||||
messageCallbacks.delete(messageId);
|
||||
callbacks.resolve(result);
|
||||
if (error != null) {
|
||||
callbacks.reject(error);
|
||||
} else {
|
||||
callbacks.resolve(result as never);
|
||||
}
|
||||
} else {
|
||||
console.warn(`Unknown message ID ${messageId}`);
|
||||
}
|
||||
|
@ -65,11 +98,8 @@ function onMessage(worker: Worker, event: MessageEvent<OutMessage>) {
|
|||
}
|
||||
}
|
||||
|
||||
async function defer<T>(message: Omit<InMessage, 'messageId'>): Promise<T> {
|
||||
if (!workerInit) {
|
||||
throw new Error('Worker not initialized');
|
||||
}
|
||||
const worker = await workerReady;
|
||||
async function defer<T>(message: AnyHandlerData, worker?: Worker): Promise<T> {
|
||||
worker = worker || await initialize();
|
||||
const messageId = globalMessageId++;
|
||||
const promise = new Promise<T>((resolve, reject) => {
|
||||
messageCallbacks.set(messageId, {start: performance.now(), resolve, reject});
|
||||
|
@ -77,17 +107,17 @@ async function defer<T>(message: Omit<InMessage, 'messageId'>): Promise<T> {
|
|||
worker.postMessage({
|
||||
...message,
|
||||
messageId
|
||||
});
|
||||
} as InMessage);
|
||||
return promise;
|
||||
}
|
||||
|
||||
export async function runDiff(left: Uint8Array | undefined, right: Uint8Array | undefined, config?: DiffObjConfig): Promise<DiffResult> {
|
||||
const data = await defer<Uint8Array>({
|
||||
type: 'run_diff',
|
||||
type: 'run_diff_proto',
|
||||
left,
|
||||
right,
|
||||
config
|
||||
} as InMessage);
|
||||
});
|
||||
const parseStart = performance.now();
|
||||
const result = DiffResult.fromBinary(data, {readUnknownField: false});
|
||||
const end = performance.now();
|
||||
|
@ -148,11 +178,6 @@ export type DiffTextSpacing = DiffTextBase & {
|
|||
count: number,
|
||||
};
|
||||
|
||||
// TypeScript workaround for oneof types
|
||||
export function oneof<T extends { oneofKind: string }>(type: T): T & { oneofKind: string } {
|
||||
return type as T & { oneofKind: string };
|
||||
}
|
||||
|
||||
// Native JavaScript implementation of objdiff_core::diff::display::display_diff
|
||||
export function displayDiff(diff: InstructionDiff, baseAddr: bigint, cb: (text: DiffText) => void) {
|
||||
const ins = diff.instruction;
|
||||
|
@ -173,7 +198,7 @@ export function displayDiff(diff: InstructionDiff, baseAddr: bigint, cb: (text:
|
|||
if (i === 0) {
|
||||
cb({type: 'spacing', count: 1});
|
||||
}
|
||||
const arg = oneof(ins.arguments[i].value);
|
||||
const arg = ins.arguments[i].value;
|
||||
const diff_index = diff.arg_diff[i]?.diff_index;
|
||||
switch (arg.oneofKind) {
|
||||
case "plain_text":
|
||||
|
@ -184,7 +209,7 @@ export function displayDiff(diff: InstructionDiff, baseAddr: bigint, cb: (text:
|
|||
break;
|
||||
case "relocation": {
|
||||
const reloc = ins.relocation!;
|
||||
cb({type: 'symbol', target: reloc.target, diff_index});
|
||||
cb({type: 'symbol', target: reloc.target!, diff_index});
|
||||
break;
|
||||
}
|
||||
case "branch_dest":
|
||||
|
|
|
@ -1,81 +1,93 @@
|
|||
import wasmInit, {default_diff_obj_config, run_diff_proto} from '../pkg';
|
||||
import {DiffObjConfig} from "./main";
|
||||
import wasmInit, * as exports from '../pkg';
|
||||
|
||||
self.postMessage({type: 'init'} as OutMessage);
|
||||
await wasmInit({});
|
||||
self.postMessage({type: 'ready'} as OutMessage);
|
||||
|
||||
type ExtractParam<T> = {
|
||||
[K in keyof T]: T[K] extends (arg1: infer U, ...args: any[]) => any ? U & { type: K } : never;
|
||||
}[keyof T];
|
||||
type HandlerData = ExtractParam<{
|
||||
run_diff: typeof run_diff,
|
||||
}>;
|
||||
const handlers: {
|
||||
[K in HandlerData['type']]: (data: Omit<HandlerData, 'type'>) => unknown
|
||||
} = {
|
||||
'run_diff': run_diff,
|
||||
const handlers = {
|
||||
init: init,
|
||||
// run_diff_json: run_diff_json,
|
||||
run_diff_proto: run_diff_proto,
|
||||
} as const;
|
||||
type ExtractData<T> = T extends (arg: infer U) => Promise<unknown> ? U : never;
|
||||
type HandlerData = {
|
||||
[K in keyof typeof handlers]: { type: K } & ExtractData<typeof handlers[K]>;
|
||||
};
|
||||
|
||||
function run_diff({left, right, config}: {
|
||||
left: Uint8Array | undefined,
|
||||
right: Uint8Array | undefined,
|
||||
config?: DiffObjConfig
|
||||
}): Uint8Array {
|
||||
const cfg = default_diff_obj_config();
|
||||
if (config) {
|
||||
for (const key in config) {
|
||||
if (key in config) {
|
||||
cfg[key] = config[key];
|
||||
}
|
||||
}
|
||||
let wasmReady: Promise<void> | null = null;
|
||||
|
||||
async function init({wasmUrl}: { wasmUrl?: string }): Promise<void> {
|
||||
if (wasmReady != null) {
|
||||
throw new Error('Already initialized');
|
||||
}
|
||||
return run_diff_proto(left, right, cfg);
|
||||
wasmReady = wasmInit({module_or_path: wasmUrl})
|
||||
.then(() => {
|
||||
});
|
||||
return wasmReady;
|
||||
}
|
||||
|
||||
export type InMessage = HandlerData & { messageId: number };
|
||||
async function initIfNeeded() {
|
||||
if (wasmReady == null) {
|
||||
await init({});
|
||||
}
|
||||
return wasmReady;
|
||||
}
|
||||
|
||||
export type OutMessage = ({
|
||||
// async function run_diff_json({left, right, config}: {
|
||||
// left: Uint8Array | undefined,
|
||||
// right: Uint8Array | undefined,
|
||||
// config?: exports.DiffObjConfig,
|
||||
// }): Promise<string> {
|
||||
// config = config || exports.default_diff_obj_config();
|
||||
// return exports.run_diff_json(left, right, cfg);
|
||||
// }
|
||||
|
||||
async function run_diff_proto({left, right, config}: {
|
||||
left: Uint8Array | undefined,
|
||||
right: Uint8Array | undefined,
|
||||
config?: exports.DiffObjConfig,
|
||||
}): Promise<Uint8Array> {
|
||||
config = config || {};
|
||||
return exports.run_diff_proto(left, right, config);
|
||||
}
|
||||
|
||||
export type AnyHandlerData = HandlerData[keyof HandlerData];
|
||||
export type InMessage = AnyHandlerData & { messageId: number };
|
||||
|
||||
export type OutMessage = {
|
||||
type: 'result',
|
||||
result: unknown | null,
|
||||
error: unknown | null,
|
||||
} | {
|
||||
type: 'init',
|
||||
msg: string
|
||||
} | {
|
||||
type: 'ready',
|
||||
msg: string
|
||||
}) & { messageId: number };
|
||||
error: string | null,
|
||||
messageId: number,
|
||||
};
|
||||
|
||||
self.onmessage = async (event: MessageEvent<InMessage>) => {
|
||||
self.onmessage = (event: MessageEvent<InMessage>) => {
|
||||
const data = event.data;
|
||||
const handler = handlers[data.type];
|
||||
if (handler) {
|
||||
try {
|
||||
const messageId = data?.messageId;
|
||||
(async () => {
|
||||
if (!data) {
|
||||
throw new Error('No data');
|
||||
}
|
||||
const handler = handlers[data.type];
|
||||
if (handler) {
|
||||
if (data.type !== 'init') {
|
||||
await initIfNeeded();
|
||||
}
|
||||
const start = performance.now();
|
||||
const result = handler(data);
|
||||
const result = await handler(data as never);
|
||||
const end = performance.now();
|
||||
console.debug(`Worker message ${data.messageId} took ${end - start}ms`);
|
||||
self.postMessage({
|
||||
type: 'result',
|
||||
result: result,
|
||||
error: null,
|
||||
messageId: data.messageId
|
||||
});
|
||||
} catch (error) {
|
||||
self.postMessage({
|
||||
type: 'result',
|
||||
result: null,
|
||||
error: error,
|
||||
messageId: data.messageId
|
||||
});
|
||||
messageId,
|
||||
} as OutMessage);
|
||||
} else {
|
||||
throw new Error(`No handler for ${data.type}`);
|
||||
}
|
||||
} else {
|
||||
})().catch(error => {
|
||||
self.postMessage({
|
||||
type: 'result',
|
||||
result: null,
|
||||
error: `No handler for ${data.type}`,
|
||||
messageId: data.messageId
|
||||
});
|
||||
}
|
||||
error: error.toString(),
|
||||
messageId,
|
||||
} as OutMessage);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "Node",
|
||||
"strict": true,
|
||||
"target": "ES2022",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,33 @@
|
|||
import {defineConfig} from 'tsup';
|
||||
import fs from 'node:fs/promises';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/main.ts', 'src/worker.ts'],
|
||||
clean: true,
|
||||
dts: true,
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
splitting: false,
|
||||
target: ['es2022', 'chrome89', 'edge89', 'firefox89', 'safari15', 'node14.8'],
|
||||
async onSuccess() {
|
||||
await fs.copyFile('pkg/objdiff_core_bg.wasm', 'dist/objdiff_core_bg.wasm');
|
||||
export default defineConfig([
|
||||
// Build main library
|
||||
{
|
||||
entry: ['src/main.ts'],
|
||||
clean: true,
|
||||
dts: true,
|
||||
format: 'esm',
|
||||
outDir: 'dist',
|
||||
skipNodeModulesBundle: true,
|
||||
sourcemap: true,
|
||||
splitting: false,
|
||||
target: 'es2022',
|
||||
},
|
||||
// Build web worker
|
||||
{
|
||||
entry: ['src/worker.ts'],
|
||||
clean: true,
|
||||
dts: true,
|
||||
format: 'esm', // type: 'module'
|
||||
minify: true,
|
||||
outDir: 'dist',
|
||||
sourcemap: true,
|
||||
splitting: false,
|
||||
target: 'es2022',
|
||||
// https://github.com/egoist/tsup/issues/278
|
||||
async onSuccess() {
|
||||
await fs.copyFile('pkg/objdiff_core_bg.wasm', 'dist/objdiff_core_bg.wasm');
|
||||
}
|
||||
}
|
||||
});
|
||||
]);
|
||||
|
|
Loading…
Reference in New Issue