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",
|
"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]]
|
[[package]]
|
||||||
name = "const_format"
|
name = "const_format"
|
||||||
version = "0.2.32"
|
version = "0.2.32"
|
||||||
|
@ -2851,6 +2861,8 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arm-attr",
|
"arm-attr",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"console_error_panic_hook",
|
||||||
|
"console_log",
|
||||||
"cpp_demangle",
|
"cpp_demangle",
|
||||||
"cwdemangle",
|
"cwdemangle",
|
||||||
"cwextab",
|
"cwextab",
|
||||||
|
@ -2876,6 +2888,7 @@ dependencies = [
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"similar",
|
"similar",
|
||||||
"strum",
|
"strum",
|
||||||
|
"tsify-next",
|
||||||
"unarm",
|
"unarm",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
@ -3860,6 +3873,17 @@ dependencies = [
|
||||||
"serde_derive",
|
"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]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.199"
|
version = "1.0.199"
|
||||||
|
@ -3871,6 +3895,17 @@ dependencies = [
|
||||||
"syn 2.0.60",
|
"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]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.116"
|
version = "1.0.116"
|
||||||
|
@ -4447,6 +4482,30 @@ version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
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]]
|
[[package]]
|
||||||
name = "ttf-parser"
|
name = "ttf-parser"
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
|
|
|
@ -23,7 +23,7 @@ mips = ["any-arch", "rabbitizer"]
|
||||||
ppc = ["any-arch", "cwdemangle", "cwextab", "ppc750cl"]
|
ppc = ["any-arch", "cwdemangle", "cwextab", "ppc750cl"]
|
||||||
x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"]
|
x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"]
|
||||||
arm = ["any-arch", "cpp_demangle", "unarm", "arm-attr"]
|
arm = ["any-arch", "cpp_demangle", "unarm", "arm-attr"]
|
||||||
wasm = ["serde_json"]
|
wasm = ["serde_json", "console_error_panic_hook", "console_log"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.82"
|
anyhow = "1.0.82"
|
||||||
|
@ -40,6 +40,9 @@ serde = { version = "1", features = ["derive"] }
|
||||||
similar = { version = "2.5.0", default-features = false }
|
similar = { version = "2.5.0", default-features = false }
|
||||||
strum = { version = "0.26.2", features = ["derive"] }
|
strum = { version = "0.26.2", features = ["derive"] }
|
||||||
wasm-bindgen = "0.2.93"
|
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
|
# config
|
||||||
globset = { version = "0.4.14", features = ["serde1"], optional = true }
|
globset = { version = "0.4.14", features = ["serde1"], optional = true }
|
||||||
|
|
|
@ -1,53 +1,78 @@
|
||||||
use anyhow::Context;
|
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
use crate::{bindings::diff::DiffResult, diff, obj};
|
use crate::{bindings::diff::DiffResult, diff, obj};
|
||||||
|
|
||||||
#[wasm_bindgen]
|
fn parse_object(
|
||||||
pub fn run_diff(
|
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]>>,
|
left: Option<Box<[u8]>>,
|
||||||
right: Option<Box<[u8]>>,
|
right: Option<Box<[u8]>>,
|
||||||
config: diff::DiffObjConfig,
|
config: diff::DiffObjConfig,
|
||||||
) -> Result<String, JsError> {
|
) -> Result<DiffResult, JsError> {
|
||||||
let target = left
|
let target = parse_object(left, &config)?;
|
||||||
.as_ref()
|
let base = parse_object(right, &config)?;
|
||||||
.map(|data| obj::read::parse(data, &config).context("Loading target"))
|
run_diff(target.as_ref(), base.as_ref(), config)
|
||||||
.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()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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]
|
#[wasm_bindgen]
|
||||||
pub fn run_diff_proto(
|
pub fn run_diff_proto(
|
||||||
left: Option<Box<[u8]>>,
|
left: Option<Box<[u8]>>,
|
||||||
right: Option<Box<[u8]>>,
|
right: Option<Box<[u8]>>,
|
||||||
config: diff::DiffObjConfig,
|
config: diff::DiffObjConfig,
|
||||||
) -> Result<Box<[u8]>, JsError> {
|
) -> Result<Box<[u8]>, JsError> {
|
||||||
let target = left
|
let out = parse_and_run_diff(left, right, config)?;
|
||||||
.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);
|
|
||||||
Ok(out.encode_to_vec().into_boxed_slice())
|
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 std::collections::HashSet;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use wasm_bindgen::prelude::wasm_bindgen;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diff::{
|
diff::{
|
||||||
|
@ -18,7 +17,6 @@ pub mod code;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod display;
|
pub mod display;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
Copy,
|
Copy,
|
||||||
|
@ -30,6 +28,7 @@ pub mod display;
|
||||||
serde::Serialize,
|
serde::Serialize,
|
||||||
strum::VariantArray,
|
strum::VariantArray,
|
||||||
strum::EnumMessage,
|
strum::EnumMessage,
|
||||||
|
tsify_next::Tsify,
|
||||||
)]
|
)]
|
||||||
pub enum X86Formatter {
|
pub enum X86Formatter {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -43,7 +42,6 @@ pub enum X86Formatter {
|
||||||
Masm,
|
Masm,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
Copy,
|
Copy,
|
||||||
|
@ -55,6 +53,7 @@ pub enum X86Formatter {
|
||||||
serde::Serialize,
|
serde::Serialize,
|
||||||
strum::VariantArray,
|
strum::VariantArray,
|
||||||
strum::EnumMessage,
|
strum::EnumMessage,
|
||||||
|
tsify_next::Tsify,
|
||||||
)]
|
)]
|
||||||
pub enum MipsAbi {
|
pub enum MipsAbi {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -68,7 +67,6 @@ pub enum MipsAbi {
|
||||||
N64,
|
N64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
Copy,
|
Copy,
|
||||||
|
@ -80,6 +78,7 @@ pub enum MipsAbi {
|
||||||
serde::Serialize,
|
serde::Serialize,
|
||||||
strum::VariantArray,
|
strum::VariantArray,
|
||||||
strum::EnumMessage,
|
strum::EnumMessage,
|
||||||
|
tsify_next::Tsify,
|
||||||
)]
|
)]
|
||||||
pub enum MipsInstrCategory {
|
pub enum MipsInstrCategory {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -97,7 +96,6 @@ pub enum MipsInstrCategory {
|
||||||
R5900,
|
R5900,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
Copy,
|
Copy,
|
||||||
|
@ -109,6 +107,7 @@ pub enum MipsInstrCategory {
|
||||||
serde::Serialize,
|
serde::Serialize,
|
||||||
strum::VariantArray,
|
strum::VariantArray,
|
||||||
strum::EnumMessage,
|
strum::EnumMessage,
|
||||||
|
tsify_next::Tsify,
|
||||||
)]
|
)]
|
||||||
pub enum ArmArchVersion {
|
pub enum ArmArchVersion {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -122,7 +121,6 @@ pub enum ArmArchVersion {
|
||||||
V6K,
|
V6K,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
Copy,
|
Copy,
|
||||||
|
@ -134,6 +132,7 @@ pub enum ArmArchVersion {
|
||||||
serde::Serialize,
|
serde::Serialize,
|
||||||
strum::VariantArray,
|
strum::VariantArray,
|
||||||
strum::EnumMessage,
|
strum::EnumMessage,
|
||||||
|
tsify_next::Tsify,
|
||||||
)]
|
)]
|
||||||
pub enum ArmR9Usage {
|
pub enum ArmR9Usage {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -154,8 +153,8 @@ pub enum ArmR9Usage {
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn default_true() -> bool { true }
|
const fn default_true() -> bool { true }
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize, tsify_next::Tsify)]
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
#[tsify(from_wasm_abi)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct DiffObjConfig {
|
pub struct DiffObjConfig {
|
||||||
pub relax_reloc_diffs: bool,
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjSectionDiff {
|
pub struct ObjSectionDiff {
|
||||||
pub symbols: Vec<ObjSymbolDiff>,
|
pub symbols: Vec<ObjSymbolDiff>,
|
||||||
|
|
|
@ -7,5 +7,22 @@ export default [
|
||||||
{languageOptions: {globals: globals.browser}},
|
{languageOptions: {globals: globals.browser}},
|
||||||
pluginJs.configs.recommended,
|
pluginJs.configs.recommended,
|
||||||
...tseslint.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",
|
"name": "objdiff-wasm",
|
||||||
"version": "2.0.0-beta.6",
|
"version": "2.0.0-beta.10",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "objdiff-wasm",
|
"name": "objdiff-wasm",
|
||||||
"version": "2.0.0-beta.6",
|
"version": "2.0.0-beta.10",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@protobuf-ts/runtime": "2.9.4"
|
"@protobuf-ts/runtime": "^2.9.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.9.0",
|
"@eslint/js": "^9.9.0",
|
||||||
"@protobuf-ts/plugin": "2.9.4",
|
"@protobuf-ts/plugin": "^2.9.4",
|
||||||
"@types/node": "22.4.1",
|
"@types/node": "^22.4.1",
|
||||||
"esbuild": "0.23.1",
|
"esbuild": "^0.23.1",
|
||||||
"eslint": "^9.9.0",
|
"eslint": "^9.9.0",
|
||||||
"globals": "^15.9.0",
|
"globals": "^15.9.0",
|
||||||
"tsup": "8.2.4",
|
"tsup": "^8.2.4",
|
||||||
"typescript-eslint": "^8.2.0"
|
"typescript-eslint": "^8.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "objdiff-wasm",
|
"name": "objdiff-wasm",
|
||||||
"version": "2.0.0-beta.6",
|
"version": "2.0.0-beta.10",
|
||||||
"description": "A local diffing tool for decompilation projects.",
|
"description": "A local diffing tool for decompilation projects.",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Luke Street",
|
"name": "Luke Street",
|
||||||
|
@ -19,21 +19,21 @@
|
||||||
"types": "dist/main.d.ts",
|
"types": "dist/main.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsup",
|
"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: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"
|
"build:wasm": "cd ../objdiff-core && wasm-pack build --out-dir ../objdiff-wasm/pkg --target web -- --features arm,dwarf,ppc,x86,wasm"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@protobuf-ts/runtime": "2.9.4"
|
"@protobuf-ts/runtime": "^2.9.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.9.0",
|
"@eslint/js": "^9.9.0",
|
||||||
"@protobuf-ts/plugin": "2.9.4",
|
"@protobuf-ts/plugin": "^2.9.4",
|
||||||
"@types/node": "22.4.1",
|
"@types/node": "^22.4.1",
|
||||||
"esbuild": "0.23.1",
|
"esbuild": "^0.23.1",
|
||||||
"eslint": "^9.9.0",
|
"eslint": "^9.9.0",
|
||||||
"globals": "^15.9.0",
|
"globals": "^15.9.0",
|
||||||
"tsup": "8.2.4",
|
"tsup": "^8.2.4",
|
||||||
"typescript-eslint": "^8.2.0"
|
"typescript-eslint": "^8.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,61 +2,94 @@ import {ArgumentValue, DiffResult, InstructionDiff, RelocationTarget} from "../g
|
||||||
import type {
|
import type {
|
||||||
ArmArchVersion,
|
ArmArchVersion,
|
||||||
ArmR9Usage,
|
ArmR9Usage,
|
||||||
DiffObjConfig as WasmDiffObjConfig,
|
DiffObjConfig,
|
||||||
MipsAbi,
|
MipsAbi,
|
||||||
MipsInstrCategory,
|
MipsInstrCategory,
|
||||||
X86Formatter
|
X86Formatter
|
||||||
} from '../pkg';
|
} from '../pkg';
|
||||||
import {InMessage, OutMessage} from './worker';
|
import {AnyHandlerData, InMessage, OutMessage} from './worker';
|
||||||
|
|
||||||
// Export wasm types
|
// Export wasm types
|
||||||
export type DiffObjConfig = Omit<Partial<WasmDiffObjConfig>, 'free'>;
|
export {ArmArchVersion, ArmR9Usage, MipsAbi, MipsInstrCategory, X86Formatter, DiffObjConfig};
|
||||||
export {ArmArchVersion, ArmR9Usage, MipsAbi, MipsInstrCategory, X86Formatter};
|
|
||||||
|
|
||||||
// Export protobuf types
|
// Export protobuf types
|
||||||
export * from '../gen/diff_pb';
|
export * from '../gen/diff_pb';
|
||||||
|
|
||||||
interface PromiseCallbacks {
|
interface PromiseCallbacks<T> {
|
||||||
start: number;
|
start: number;
|
||||||
resolve: (value: unknown) => void;
|
resolve: (value: T | PromiseLike<T>) => void;
|
||||||
reject: (reason?: unknown) => void;
|
reject: (reason?: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let workerInit = false;
|
let workerInit = false;
|
||||||
let workerCallbacks: PromiseCallbacks | null = null;
|
let workerCallbacks: PromiseCallbacks<Worker>;
|
||||||
const workerReady = new Promise<Worker>((resolve, reject) => {
|
const workerReady = new Promise<Worker>((resolve, reject) => {
|
||||||
workerCallbacks = {start: performance.now(), 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) {
|
if (workerInit) {
|
||||||
return;
|
return workerReady;
|
||||||
}
|
}
|
||||||
workerInit = true;
|
workerInit = true;
|
||||||
const worker = new Worker(workerUrl || 'worker.js', {type: 'module'});
|
let {workerUrl, wasmUrl} = data || {};
|
||||||
worker.onmessage = onMessage.bind(null, worker);
|
if (!workerUrl) {
|
||||||
worker.onerror = (error) => {
|
try {
|
||||||
console.error("Worker error", error);
|
// Bundlers will convert this into an asset URL
|
||||||
workerCallbacks.reject(error);
|
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;
|
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) {
|
switch (event.data.type) {
|
||||||
case 'ready':
|
|
||||||
workerCallbacks.resolve(worker);
|
|
||||||
break;
|
|
||||||
case 'result': {
|
case 'result': {
|
||||||
const {messageId, result} = event.data;
|
const {result, error, messageId} = event.data;
|
||||||
const callbacks = messageCallbacks.get(messageId);
|
const callbacks = messageCallbacks.get(messageId);
|
||||||
if (callbacks) {
|
if (callbacks) {
|
||||||
const end = performance.now();
|
const end = performance.now();
|
||||||
console.debug(`Message ${messageId} took ${end - callbacks.start}ms`);
|
console.debug(`Message ${messageId} took ${end - callbacks.start}ms`);
|
||||||
messageCallbacks.delete(messageId);
|
messageCallbacks.delete(messageId);
|
||||||
callbacks.resolve(result);
|
if (error != null) {
|
||||||
|
callbacks.reject(error);
|
||||||
|
} else {
|
||||||
|
callbacks.resolve(result as never);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn(`Unknown message ID ${messageId}`);
|
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> {
|
async function defer<T>(message: AnyHandlerData, worker?: Worker): Promise<T> {
|
||||||
if (!workerInit) {
|
worker = worker || await initialize();
|
||||||
throw new Error('Worker not initialized');
|
|
||||||
}
|
|
||||||
const worker = await workerReady;
|
|
||||||
const messageId = globalMessageId++;
|
const messageId = globalMessageId++;
|
||||||
const promise = new Promise<T>((resolve, reject) => {
|
const promise = new Promise<T>((resolve, reject) => {
|
||||||
messageCallbacks.set(messageId, {start: performance.now(), 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({
|
worker.postMessage({
|
||||||
...message,
|
...message,
|
||||||
messageId
|
messageId
|
||||||
});
|
} as InMessage);
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runDiff(left: Uint8Array | undefined, right: Uint8Array | undefined, config?: DiffObjConfig): Promise<DiffResult> {
|
export async function runDiff(left: Uint8Array | undefined, right: Uint8Array | undefined, config?: DiffObjConfig): Promise<DiffResult> {
|
||||||
const data = await defer<Uint8Array>({
|
const data = await defer<Uint8Array>({
|
||||||
type: 'run_diff',
|
type: 'run_diff_proto',
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
config
|
config
|
||||||
} as InMessage);
|
});
|
||||||
const parseStart = performance.now();
|
const parseStart = performance.now();
|
||||||
const result = DiffResult.fromBinary(data, {readUnknownField: false});
|
const result = DiffResult.fromBinary(data, {readUnknownField: false});
|
||||||
const end = performance.now();
|
const end = performance.now();
|
||||||
|
@ -148,11 +178,6 @@ export type DiffTextSpacing = DiffTextBase & {
|
||||||
count: number,
|
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
|
// Native JavaScript implementation of objdiff_core::diff::display::display_diff
|
||||||
export function displayDiff(diff: InstructionDiff, baseAddr: bigint, cb: (text: DiffText) => void) {
|
export function displayDiff(diff: InstructionDiff, baseAddr: bigint, cb: (text: DiffText) => void) {
|
||||||
const ins = diff.instruction;
|
const ins = diff.instruction;
|
||||||
|
@ -173,7 +198,7 @@ export function displayDiff(diff: InstructionDiff, baseAddr: bigint, cb: (text:
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
cb({type: 'spacing', count: 1});
|
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;
|
const diff_index = diff.arg_diff[i]?.diff_index;
|
||||||
switch (arg.oneofKind) {
|
switch (arg.oneofKind) {
|
||||||
case "plain_text":
|
case "plain_text":
|
||||||
|
@ -184,7 +209,7 @@ export function displayDiff(diff: InstructionDiff, baseAddr: bigint, cb: (text:
|
||||||
break;
|
break;
|
||||||
case "relocation": {
|
case "relocation": {
|
||||||
const reloc = ins.relocation!;
|
const reloc = ins.relocation!;
|
||||||
cb({type: 'symbol', target: reloc.target, diff_index});
|
cb({type: 'symbol', target: reloc.target!, diff_index});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "branch_dest":
|
case "branch_dest":
|
||||||
|
|
|
@ -1,81 +1,93 @@
|
||||||
import wasmInit, {default_diff_obj_config, run_diff_proto} from '../pkg';
|
import wasmInit, * as exports from '../pkg';
|
||||||
import {DiffObjConfig} from "./main";
|
|
||||||
|
|
||||||
self.postMessage({type: 'init'} as OutMessage);
|
const handlers = {
|
||||||
await wasmInit({});
|
init: init,
|
||||||
self.postMessage({type: 'ready'} as OutMessage);
|
// run_diff_json: run_diff_json,
|
||||||
|
run_diff_proto: run_diff_proto,
|
||||||
type ExtractParam<T> = {
|
} as const;
|
||||||
[K in keyof T]: T[K] extends (arg1: infer U, ...args: any[]) => any ? U & { type: K } : never;
|
type ExtractData<T> = T extends (arg: infer U) => Promise<unknown> ? U : never;
|
||||||
}[keyof T];
|
type HandlerData = {
|
||||||
type HandlerData = ExtractParam<{
|
[K in keyof typeof handlers]: { type: K } & ExtractData<typeof handlers[K]>;
|
||||||
run_diff: typeof run_diff,
|
|
||||||
}>;
|
|
||||||
const handlers: {
|
|
||||||
[K in HandlerData['type']]: (data: Omit<HandlerData, 'type'>) => unknown
|
|
||||||
} = {
|
|
||||||
'run_diff': run_diff,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function run_diff({left, right, config}: {
|
let wasmReady: Promise<void> | null = null;
|
||||||
left: Uint8Array | undefined,
|
|
||||||
right: Uint8Array | undefined,
|
async function init({wasmUrl}: { wasmUrl?: string }): Promise<void> {
|
||||||
config?: DiffObjConfig
|
if (wasmReady != null) {
|
||||||
}): Uint8Array {
|
throw new Error('Already initialized');
|
||||||
const cfg = default_diff_obj_config();
|
|
||||||
if (config) {
|
|
||||||
for (const key in config) {
|
|
||||||
if (key in config) {
|
|
||||||
cfg[key] = config[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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',
|
type: 'result',
|
||||||
result: unknown | null,
|
result: unknown | null,
|
||||||
error: unknown | null,
|
error: string | null,
|
||||||
} | {
|
messageId: number,
|
||||||
type: 'init',
|
};
|
||||||
msg: string
|
|
||||||
} | {
|
|
||||||
type: 'ready',
|
|
||||||
msg: string
|
|
||||||
}) & { messageId: number };
|
|
||||||
|
|
||||||
self.onmessage = async (event: MessageEvent<InMessage>) => {
|
self.onmessage = (event: MessageEvent<InMessage>) => {
|
||||||
const data = event.data;
|
const data = event.data;
|
||||||
const handler = handlers[data.type];
|
const messageId = data?.messageId;
|
||||||
if (handler) {
|
(async () => {
|
||||||
try {
|
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 start = performance.now();
|
||||||
const result = handler(data);
|
const result = await handler(data as never);
|
||||||
const end = performance.now();
|
const end = performance.now();
|
||||||
console.debug(`Worker message ${data.messageId} took ${end - start}ms`);
|
console.debug(`Worker message ${data.messageId} took ${end - start}ms`);
|
||||||
self.postMessage({
|
self.postMessage({
|
||||||
type: 'result',
|
type: 'result',
|
||||||
result: result,
|
result: result,
|
||||||
error: null,
|
error: null,
|
||||||
messageId: data.messageId
|
messageId,
|
||||||
});
|
} as OutMessage);
|
||||||
} catch (error) {
|
} else {
|
||||||
self.postMessage({
|
throw new Error(`No handler for ${data.type}`);
|
||||||
type: 'result',
|
|
||||||
result: null,
|
|
||||||
error: error,
|
|
||||||
messageId: data.messageId
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
})().catch(error => {
|
||||||
self.postMessage({
|
self.postMessage({
|
||||||
type: 'result',
|
type: 'result',
|
||||||
result: null,
|
result: null,
|
||||||
error: `No handler for ${data.type}`,
|
error: error.toString(),
|
||||||
messageId: data.messageId
|
messageId,
|
||||||
});
|
} as OutMessage);
|
||||||
}
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"esModuleInterop": true,
|
||||||
"module": "ES2022",
|
"module": "ES2022",
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
|
"strict": true,
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"esModuleInterop": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,15 +1,33 @@
|
||||||
import {defineConfig} from 'tsup';
|
import {defineConfig} from 'tsup';
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig([
|
||||||
entry: ['src/main.ts', 'src/worker.ts'],
|
// Build main library
|
||||||
clean: true,
|
{
|
||||||
dts: true,
|
entry: ['src/main.ts'],
|
||||||
format: 'esm',
|
clean: true,
|
||||||
sourcemap: true,
|
dts: true,
|
||||||
splitting: false,
|
format: 'esm',
|
||||||
target: ['es2022', 'chrome89', 'edge89', 'firefox89', 'safari15', 'node14.8'],
|
outDir: 'dist',
|
||||||
async onSuccess() {
|
skipNodeModulesBundle: true,
|
||||||
await fs.copyFile('pkg/objdiff_core_bg.wasm', 'dist/objdiff_core_bg.wasm');
|
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