use crate::{Error, OutputFormat};
use serde::de::DeserializeOwned;
use serde::Deserialize;
use wasm_bindgen::prelude::*;
use super::api::{decompile_report, disasm_report, info_report_with_manifest_options};
use super::options::{WebDecompileOptions, WebDisasmOptions};
#[derive(Debug, Default, Deserialize)]
#[serde(default)]
struct JsInfoOptions {
manifest_json: Option<String>,
strict_manifest: bool,
}
#[derive(Debug, Default, Deserialize)]
#[serde(default)]
struct JsDisasmOptions {
fail_on_unknown_opcodes: bool,
}
#[derive(Debug, Deserialize)]
#[serde(default)]
struct JsDecompileOptions {
manifest_json: Option<String>,
strict_manifest: bool,
fail_on_unknown_opcodes: bool,
inline_single_use_temps: bool,
emit_trace_comments: bool,
typed_declarations: bool,
output_format: Option<String>,
}
impl Default for JsDecompileOptions {
fn default() -> Self {
let defaults = WebDecompileOptions::default();
Self {
manifest_json: None,
strict_manifest: false,
fail_on_unknown_opcodes: false,
inline_single_use_temps: defaults.inline_single_use_temps,
emit_trace_comments: defaults.emit_trace_comments,
typed_declarations: defaults.typed_declarations,
output_format: None,
}
}
}
#[wasm_bindgen(js_name = initPanicHook)]
pub fn init_panic_hook() {
console_error_panic_hook::set_once();
}
#[wasm_bindgen(js_name = infoReport)]
pub fn info_report_wasm(
nef_bytes: &[u8],
options: JsValue,
) -> std::result::Result<JsValue, JsValue> {
let options: JsInfoOptions = parse_js_options(options)?;
let report = info_report_with_manifest_options(
nef_bytes,
options.manifest_json.as_deref(),
options.strict_manifest,
)
.map_err(to_js_error)?;
report_to_js(&report)
}
#[wasm_bindgen(js_name = disasmReport)]
pub fn disasm_report_wasm(
nef_bytes: &[u8],
options: JsValue,
) -> std::result::Result<JsValue, JsValue> {
let options: JsDisasmOptions = parse_js_options(options)?;
let report = disasm_report(
nef_bytes,
WebDisasmOptions {
fail_on_unknown_opcodes: options.fail_on_unknown_opcodes,
},
)
.map_err(to_js_error)?;
report_to_js(&report)
}
#[wasm_bindgen(js_name = decompileReport)]
pub fn decompile_report_wasm(
nef_bytes: &[u8],
options: JsValue,
) -> std::result::Result<JsValue, JsValue> {
let options: JsDecompileOptions = parse_js_options(options)?;
let output_format = parse_output_format(options.output_format.as_deref())
.map_err(|err| JsValue::from_str(&err))?;
let report = decompile_report(
nef_bytes,
WebDecompileOptions {
manifest_json: options.manifest_json,
strict_manifest: options.strict_manifest,
fail_on_unknown_opcodes: options.fail_on_unknown_opcodes,
inline_single_use_temps: options.inline_single_use_temps,
emit_trace_comments: options.emit_trace_comments,
typed_declarations: options.typed_declarations,
output_format,
},
)
.map_err(to_js_error)?;
report_to_js(&report)
}
fn report_to_js<T: serde::Serialize>(value: &T) -> std::result::Result<JsValue, JsValue> {
let serializer =
serde_wasm_bindgen::Serializer::new().serialize_large_number_types_as_bigints(true);
serde::Serialize::serialize(value, &serializer)
.map_err(|err| JsValue::from_str(&err.to_string()))
}
fn parse_js_options<T>(value: JsValue) -> std::result::Result<T, JsValue>
where
T: Default + DeserializeOwned,
{
if value.is_null() || value.is_undefined() {
Ok(T::default())
} else {
serde_wasm_bindgen::from_value(value)
.map_err(|err| JsValue::from_str(&format!("invalid options: {err}")))
}
}
fn parse_output_format(value: Option<&str>) -> std::result::Result<OutputFormat, String> {
match value.unwrap_or("all") {
"all" => Ok(OutputFormat::All),
"pseudocode" => Ok(OutputFormat::Pseudocode),
"high_level" | "highLevel" => Ok(OutputFormat::HighLevel),
"csharp" | "c_sharp" => Ok(OutputFormat::CSharp),
other => Err(format!("invalid output_format: {other}")),
}
}
fn to_js_error(err: Error) -> JsValue {
JsValue::from_str(&err.to_string())
}