├── .gitignore ├── README.md ├── config-overrides.js ├── package.json ├── public └── index.html ├── src ├── component │ ├── app │ │ ├── App.css │ │ └── App.tsx │ ├── editor │ │ └── Editor.tsx │ ├── settings │ │ ├── Settings.css │ │ └── Settings.tsx │ └── sidebar │ │ ├── Sidebar.css │ │ ├── Sidebar.tsx │ │ ├── SidebarIcon.css │ │ └── SidebarIcon.tsx ├── index.css ├── index.tsx ├── language │ └── index.ts └── react-app-env.d.ts ├── tsconfig.json ├── wasm-remapper ├── .gitignore ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ └── utils.rs ├── wasm2wat ├── .cargo-ok ├── .gitignore ├── Cargo.toml └── src │ ├── lib.rs │ └── utils.rs └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wasm-remapper-web 2 | A web app to help reverse engineer WebAssembly binaries. 3 | 4 | ## Demo 5 | wasm-remapper-web is live at https://remapper.zebulon.dev/ 6 | 7 | ## How it works 8 | Wasm-remapper-web works by cross referencing every function inside the input WebAssembly binary with a **reference** WebAssembly binary with debug symbols. If you are able to determine the compiler version used to generate the input binary you can generally compile a binary that includes many functions from the standard library and generate a remapped output binary. 9 | 10 | ## As a library 11 | wasm-remapper-web is built on top of my Rust library called [wasm_remapper](https://github.com/vlakreeh/wasm_remapper). 12 | 13 | [![crates.io](https://img.shields.io/crates/v/wasm-remapper.svg)](https://crates.io/crates/wasm_remapper) 14 | [![Documentation](https://docs.rs/wasm_remapper/badge.svg)](https://docs.rs/wasm_remapper/) 15 | -------------------------------------------------------------------------------- /config-overrides.js: -------------------------------------------------------------------------------- 1 | const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin"); 2 | const path = require('path'); 3 | 4 | module.exports = function override(config, env) { 5 | const wasmExtensionRegExp = /\.wasm$/; 6 | 7 | config.resolve.extensions.push('.wasm'); 8 | 9 | config.module.rules.forEach(rule => { 10 | (rule.oneOf || []).forEach(oneOf => { 11 | if (oneOf.loader && oneOf.loader.indexOf('file-loader') >= 0) { 12 | // make file-loader ignore WASM files 13 | oneOf.exclude.push(wasmExtensionRegExp); 14 | } 15 | }); 16 | }); 17 | 18 | // add a dedicated loader for WASM 19 | config.module.rules.push({ 20 | test: wasmExtensionRegExp, 21 | include: path.resolve(__dirname, 'src'), 22 | use: [{ loader: require.resolve('wasm-loader'), options: {} }] 23 | }); 24 | config.plugins.push(new MonacoWebpackPlugin({ 25 | languages: ["javascript"] 26 | })); 27 | return config; 28 | } 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wasm-remapper-web", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@babel/plugin-syntax-import-meta": "^7.10.4", 7 | "@material-ui/core": "^4.11.0", 8 | "@testing-library/jest-dom": "^4.2.4", 9 | "@testing-library/react": "^9.3.2", 10 | "@testing-library/user-event": "^7.1.2", 11 | "@types/jest": "^24.0.0", 12 | "@types/node": "^12.0.0", 13 | "@types/react": "^16.9.0", 14 | "@types/react-dom": "^16.9.0", 15 | "eva-icons": "^1.1.3", 16 | "file-saver": "^2.0.2", 17 | "monaco-editor": "^0.20.0", 18 | "monaco-editor-webpack-plugin": "^1.9.0", 19 | "react": "^16.13.1", 20 | "react-dom": "^16.13.1", 21 | "react-dropzone": "^11.2.0", 22 | "react-monaco-editor": "^0.40.0", 23 | "react-resize-detector": "^5.2.0", 24 | "react-scripts": "3.4.3", 25 | "react-tooltip": "^4.2.10", 26 | "typescript": "~3.7.2", 27 | "wasm-remapper": "./wasm-remapper/pkg", 28 | "wasm2wat": "./wasm2wat/pkg" 29 | }, 30 | "scripts": { 31 | "start": "react-app-rewired start", 32 | "build": "react-app-rewired build", 33 | "test": "react-app-rewired test", 34 | "eject": "react-app-rewired eject" 35 | }, 36 | "eslintConfig": { 37 | "extends": "react-app" 38 | }, 39 | "browserslist": { 40 | "production": [ 41 | ">0.2%", 42 | "not dead", 43 | "not op_mini all" 44 | ], 45 | "development": [ 46 | "last 1 chrome version", 47 | "last 1 firefox version", 48 | "last 1 safari version" 49 | ] 50 | }, 51 | "devDependencies": { 52 | "@types/file-saver": "^2.0.1", 53 | "@types/react-resize-detector": "^5.0.0", 54 | "react-app-rewired": "^2.1.6", 55 | "wasm-loader": "^1.3.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Wasm Remapper 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /src/component/app/App.css: -------------------------------------------------------------------------------- 1 | @import url("https://dev-cats.github.io/code-snippets/JetBrainsMono.css"); 2 | 3 | #app { 4 | height: 100vh; 5 | display: flex; 6 | flex-direction: row; 7 | overflow: hidden; 8 | } -------------------------------------------------------------------------------- /src/component/app/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { createContext, useState } from "react"; 2 | import Editor from "../editor/Editor"; 3 | import Settings from "../settings/Settings"; 4 | import Sidebar from "../sidebar/Sidebar"; 5 | import "./App.css"; 6 | 7 | function readWasmFromLocalStorage(key: string): Uint8Array | undefined { 8 | const fromStorage = localStorage.getItem(key); 9 | 10 | if (fromStorage) { 11 | return new Uint8Array(Buffer.from(fromStorage, "base64")); 12 | } 13 | 14 | return undefined; 15 | } 16 | 17 | export type AppContextData = { 18 | inputWasm?: Uint8Array, 19 | referenceWasm?: Uint8Array, 20 | matchingThreshold: number, 21 | setMatchingThreshold?: (x: number) => void, 22 | ignoreDataSectionConstants: boolean, 23 | requireExactFunctionLocals: boolean, 24 | setIgnoreDataSectionConstants?: (value: boolean) => void, 25 | setRequireExactFunctionLocals?: (value: boolean) => void, 26 | }; 27 | 28 | export const AppContext = createContext({ 29 | matchingThreshold: 0.9, 30 | ignoreDataSectionConstants: false, 31 | requireExactFunctionLocals: true 32 | }); 33 | 34 | const createSaveHook = (setWasmFunc: (wasm: Uint8Array) => void, key: string): (arg0: Uint8Array) => void => { 35 | return (wasm) => { 36 | const base64String = Buffer.from(wasm).toString("base64"); 37 | localStorage.setItem(key, base64String); 38 | setWasmFunc(wasm); 39 | }; 40 | }; 41 | 42 | function App() { 43 | const [inputWasm, setInputWasm] = useState(readWasmFromLocalStorage("wasm.input")); 44 | const [referenceWasm, setReferenceWasm] = useState(readWasmFromLocalStorage("wasm.reference")); 45 | 46 | const [matchingThreshold, setMatchingThreshold] = useState(0.9); 47 | const [ignoreDataSectionConstants, setIgnoreDataSectionConstants] = useState(false); 48 | const [requireExactFunctionLocals, setRequireExactFunctionLocals] = useState(true); 49 | 50 | const [opened, setOpened] = useState(false); 51 | 52 | const providerValue = { 53 | referenceWasm, 54 | inputWasm, 55 | matchingThreshold, 56 | setMatchingThreshold, 57 | ignoreDataSectionConstants, 58 | requireExactFunctionLocals, 59 | setIgnoreDataSectionConstants, 60 | setRequireExactFunctionLocals, 61 | }; 62 | 63 | return ( 64 | 65 | setOpened(false)}> 66 |
67 | setOpened(true)}/> 68 | 69 | 70 |
71 |
72 | ); 73 | } 74 | 75 | export default App; 76 | -------------------------------------------------------------------------------- /src/component/editor/Editor.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import MonacoEditor from "react-monaco-editor"; 3 | import monaco from "monaco-editor"; 4 | import { useDropzone } from "react-dropzone"; 5 | 6 | // Global editor options 7 | const options: monaco.editor.IStandaloneEditorConstructionOptions = { 8 | fontFamily: "JetBrains Mono", 9 | readOnly: true, 10 | minimap: { 11 | renderCharacters: false 12 | }, 13 | automaticLayout: true 14 | }; 15 | 16 | export type EditorProps = { 17 | type: "input" | "reference", 18 | setWasm: (wasm: Uint8Array) => void, 19 | }; 20 | 21 | export default function Editor(props: EditorProps) { 22 | const { type, setWasm } = props; 23 | const localCode = localStorage.getItem(`wat.${type}`) || `;; Drop the ${type} wasm here`; 24 | const [code, setCode] = useState(localCode); 25 | 26 | const onDrop = async ([wasmBlob]: Blob[]) => { 27 | const { wasm2wat } = await import("wasm2wat"); 28 | const buffer = await wasmBlob.arrayBuffer(); 29 | const wasm = new Uint8Array(buffer); 30 | const wat = wasm2wat(wasm); 31 | 32 | setCode(wat); 33 | setWasm(wasm); 34 | localStorage.setItem(`wat.${type}`, wat); 35 | }; 36 | const { getRootProps, getInputProps } = useDropzone({ onDrop, noClick: true }); 37 | 38 | return (
39 | 40 | 41 |
); 42 | } -------------------------------------------------------------------------------- /src/component/settings/Settings.css: -------------------------------------------------------------------------------- 1 | #settings-container { 2 | position: absolute; 3 | left: 0px; 4 | top: 0px; 5 | width: 100vw; 6 | height: 100vh; 7 | 8 | margin: 0px; 9 | padding: 0px; 10 | 11 | display: flex; 12 | align-items: center; 13 | justify-content: center; 14 | 15 | color: #EEE; 16 | } 17 | 18 | #settings-panel { 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | 23 | padding: 1rem; 24 | width: max(300px, 10vw); 25 | background-color: #212121; 26 | box-shadow: 0px 5px 10px -4px black; 27 | } 28 | -------------------------------------------------------------------------------- /src/component/settings/Settings.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import Button from '@material-ui/core/Button'; 3 | import Slider from '@material-ui/core/Slider'; 4 | import "./Settings.css"; 5 | import FormControlLabel from "@material-ui/core/FormControlLabel"; 6 | import Checkbox from "@material-ui/core/Checkbox"; 7 | import { AppContext } from "../app/App"; 8 | 9 | export type SettingsProps = { 10 | opened: boolean, 11 | closeSettings: () => void, 12 | }; 13 | 14 | export default function Settings({ opened, closeSettings }: SettingsProps) { 15 | const { 16 | matchingThreshold, setMatchingThreshold, 17 | ignoreDataSectionConstants, setIgnoreDataSectionConstants, 18 | requireExactFunctionLocals, setRequireExactFunctionLocals 19 | } = useContext(AppContext); 20 | 21 | const style = { zIndex: opened ? 1000 : -1, display: opened ? undefined : "none" }; 22 | 23 | return ( 24 |
25 |
26 |

Settings

27 | Matching Threshold 28 | `${Math.round(value)}%`} 32 | valueLabelDisplay="auto" 33 | step={1} 34 | min={0} 35 | max={100} 36 | onChange={(_, value: number | number[]) => setMatchingThreshold!(value as number / 100.0)} 37 | style={{ width: "200px" }} 38 | /> 39 | setIgnoreDataSectionConstants!(!ignoreDataSectionConstants)} 44 | name="checkedB" 45 | color="primary" 46 | /> 47 | } 48 | label="Ignore data section constants" 49 | /> 50 | setRequireExactFunctionLocals!(!requireExactFunctionLocals)} 55 | name="checkedB" 56 | color="primary" 57 | /> 58 | } 59 | label="Require exact function locals" 60 | /> 61 | 62 |
63 |
64 | ); 65 | } -------------------------------------------------------------------------------- /src/component/sidebar/Sidebar.css: -------------------------------------------------------------------------------- 1 | #sidebar { 2 | height: 100%; 3 | background-color: #212121; 4 | display: flex; 5 | flex-direction: column; 6 | } -------------------------------------------------------------------------------- /src/component/sidebar/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import SidebarIcon from "./SidebarIcon"; 3 | import ReactTooltip from "react-tooltip"; 4 | import { saveAs } from "file-saver"; 5 | import "./Sidebar.css" 6 | import { AppContext } from "../app/App"; 7 | 8 | export type SidebarProps = { 9 | openSettings: () => void, 10 | }; 11 | 12 | export default function Sidebar(props: SidebarProps) { 13 | const { inputWasm, referenceWasm, matchingThreshold, ignoreDataSectionConstants, requireExactFunctionLocals } = useContext(AppContext); 14 | 15 | const remapClickHandler = async () => { 16 | const { remap } = await import("wasm-remapper"); 17 | 18 | if (!inputWasm) { 19 | alert("Missing input binary"); 20 | return; 21 | } 22 | 23 | if (!referenceWasm) { 24 | alert("Missing input binary"); 25 | return; 26 | } 27 | 28 | const outputWasm = remap( 29 | inputWasm, 30 | referenceWasm, 31 | matchingThreshold, 32 | ignoreDataSectionConstants, 33 | requireExactFunctionLocals 34 | ); 35 | const outputBlob = new Blob([outputWasm], { type: "application/wasm" }); 36 | saveAs(outputBlob, "output.wasm"); 37 | }; 38 | 39 | return (); 55 | } -------------------------------------------------------------------------------- /src/component/sidebar/SidebarIcon.css: -------------------------------------------------------------------------------- 1 | .sidebarIcon { 2 | width: 3.5rem; 3 | height: 3.5rem; 4 | transition: 0.3s; 5 | } 6 | 7 | .sidebarIcon > svg { 8 | padding: 1rem; 9 | transition: 0.3s; 10 | filter: invert() brightness(60%); 11 | } 12 | 13 | .sidebarIcon:hover { 14 | background-color: #313131; 15 | } 16 | 17 | .sidebarIcon:hover > svg { 18 | filter: invert() brightness(80%); 19 | } -------------------------------------------------------------------------------- /src/component/sidebar/SidebarIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import "./SidebarIcon.css"; 3 | 4 | const eva = require("eva-icons"); 5 | 6 | export type SidebarIconProps = { 7 | src: string, 8 | alt?: string, 9 | dataFor?: string, 10 | onClick?: () => void, 11 | }; 12 | 13 | export default function SidebarIcon({ src, alt, dataFor, onClick }: SidebarIconProps) { 14 | useEffect(() => eva.replace(), []); 15 | return (
16 | {alt} 17 |
); 18 | } -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #1e1e1e; 3 | margin: 0px; 4 | font-family: Arial, Helvetica, sans-serif; 5 | } 6 | 7 | .overflowingContentWidgets { 8 | display: none; 9 | } -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./component/app/App"; 4 | import { register } from "./language"; 5 | import "./index.css"; 6 | 7 | register(); 8 | 9 | function Root() { 10 | return ( 11 | 12 | 13 | 14 | ); 15 | } 16 | 17 | ReactDOM.render( 18 | , 19 | document.getElementById("root") 20 | ); 21 | -------------------------------------------------------------------------------- /src/language/index.ts: -------------------------------------------------------------------------------- 1 | import * as monaco from "monaco-editor"; 2 | 3 | type WatLang = { 4 | keywords: string[], 5 | typeKeywords: string[], 6 | specialKeywords: string[], 7 | } 8 | 9 | const wat: monaco.languages.IMonarchLanguage | WatLang = { 10 | keywords: ["module", 11 | "table", 12 | "memory", 13 | "export", 14 | "import", 15 | "func", 16 | "result", 17 | "offset", 18 | "anyfunc", 19 | "type", 20 | "data", 21 | "start", 22 | "element", 23 | "global", 24 | "local", 25 | "mut", 26 | "param", 27 | "result"], 28 | specialKeywords: ["block", "loop", "end"], 29 | typeKeywords: ["i32", "i64", "f32", "f64", "funcref", "call"], 30 | tokenizer: { 31 | root: [ 32 | [/\$\w+/, "identifier"], 33 | [/\d*\.\d+([eE][-+]?\d+)?/, "number.float"], 34 | [/0[xX][0-9a-fA-F]+/, "number.hex"], 35 | [/\d+/, "number"], 36 | [/".*"/, "string"], 37 | [/\(;.*;\)/, "comment"], 38 | [/;;.*/, "comment"], 39 | [/(\w+)\./, { token: "type.keyword" }], 40 | [/[a-zA-Z_]\w*/, { 41 | cases: { 42 | "@typeKeywords": "type.keyword", 43 | "@specialKeywords": "control", 44 | "@keywords": { token: "keyword.$0" } 45 | } 46 | }] 47 | ] 48 | } 49 | }; 50 | 51 | export function register() { 52 | monaco.languages.register({ id: "wat" }); 53 | monaco.languages.setMonarchTokensProvider("wat", wat as unknown as monaco.languages.IMonarchLanguage); 54 | monaco.editor.defineTheme("vs-dark-wat", { 55 | base: "vs-dark", 56 | inherit: true, 57 | colors: {}, 58 | rules: [ 59 | { token: "control", foreground: "C586C0" }, 60 | { token: "identifier", foreground: "DCDCAA" }, 61 | ] 62 | }); 63 | } -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface Blob { 4 | arrayBuffer: () => Promise 5 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "react" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /wasm-remapper/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | bin/ 5 | pkg/ 6 | wasm-pack.log 7 | -------------------------------------------------------------------------------- /wasm-remapper/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm-remapper" 3 | version = "0.1.0" 4 | authors = ["vlakreeh "] 5 | edition = "2018" 6 | 7 | [package.metadata.wasm-pack.profile.release] 8 | wasm-opt = false 9 | 10 | [lib] 11 | crate-type = ["cdylib", "rlib"] 12 | 13 | [features] 14 | default = ["console_error_panic_hook"] 15 | 16 | [dependencies] 17 | wasm-bindgen = "0.2.63" 18 | 19 | # The `console_error_panic_hook` crate provides better debugging of panics by 20 | # logging them with `console.error`. This is great for development, but requires 21 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for 22 | # code size when deploying. 23 | console_error_panic_hook = { version = "0.1.6", optional = true } 24 | 25 | # `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size 26 | # compared to the default allocator's ~10K. It is slower than the default 27 | # allocator, however. 28 | # 29 | # Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. 30 | wee_alloc = { version = "0.4.5", optional = true } 31 | wasm_remapper = "0.1.0" 32 | 33 | [dev-dependencies] 34 | wasm-bindgen-test = "0.3.13" 35 | 36 | [profile.release] 37 | # Tell `rustc` to optimize for small code size. 38 | opt-level = "s" 39 | -------------------------------------------------------------------------------- /wasm-remapper/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

wasm-pack-template

4 | 5 | A template for kick starting a Rust and WebAssembly project using wasm-pack. 6 | 7 |

8 | Build Status 9 |

10 | 11 |

12 | Tutorial 13 | | 14 | Chat 15 |

16 | 17 | Built with 🦀🕸 by The Rust and WebAssembly Working Group 18 |
19 | 20 | ## About 21 | 22 | [**📚 Read this template tutorial! 📚**][template-docs] 23 | 24 | This template is designed for compiling Rust libraries into WebAssembly and 25 | publishing the resulting package to NPM. 26 | 27 | Be sure to check out [other `wasm-pack` tutorials online][tutorials] for other 28 | templates and usages of `wasm-pack`. 29 | 30 | [tutorials]: https://rustwasm.github.io/docs/wasm-pack/tutorials/index.html 31 | [template-docs]: https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html 32 | 33 | ## 🚴 Usage 34 | 35 | ### 🐑 Use `cargo generate` to Clone this Template 36 | 37 | [Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate) 38 | 39 | ``` 40 | cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project 41 | cd my-project 42 | ``` 43 | 44 | ### 🛠️ Build with `wasm-pack build` 45 | 46 | ``` 47 | wasm-pack build 48 | ``` 49 | 50 | ### 🔬 Test in Headless Browsers with `wasm-pack test` 51 | 52 | ``` 53 | wasm-pack test --headless --firefox 54 | ``` 55 | 56 | ### 🎁 Publish to NPM with `wasm-pack publish` 57 | 58 | ``` 59 | wasm-pack publish 60 | ``` 61 | 62 | ## 🔋 Batteries Included 63 | 64 | * [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating 65 | between WebAssembly and JavaScript. 66 | * [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook) 67 | for logging panic messages to the developer console. 68 | * [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized 69 | for small code size. 70 | -------------------------------------------------------------------------------- /wasm-remapper/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | 3 | use wasm_bindgen::prelude::*; 4 | use wasm_remapper::*; 5 | 6 | // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global 7 | // allocator. 8 | #[cfg(feature = "wee_alloc")] 9 | #[global_allocator] 10 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; 11 | 12 | #[wasm_bindgen] 13 | #[allow(non_snake_case)] 14 | pub fn remap( 15 | input: &[u8], 16 | reference: &[u8], 17 | matchingThreshold: f32, 18 | ignoreDataSectionConstants: bool, 19 | requireExactFunctionLocals: bool, 20 | ) -> Result, JsValue> { 21 | Remapper::builder() 22 | .input(input) 23 | .reference(reference) 24 | .matching_threshold(matchingThreshold) 25 | .ingore_constant_data_section_pointers(ignoreDataSectionConstants) 26 | .require_exact_function_locals(requireExactFunctionLocals) 27 | .build() 28 | .map_err(|error| JsValue::from_str(&error.to_string()))? 29 | .remap() 30 | .map(|output| output.output) 31 | .map_err(|error| JsValue::from_str(&error.to_string())) 32 | } 33 | -------------------------------------------------------------------------------- /wasm-remapper/src/utils.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused)] 2 | pub fn set_panic_hook() { 3 | // When the `console_error_panic_hook` feature is enabled, we can call the 4 | // `set_panic_hook` function at least once during initialization, and then 5 | // we will get better error messages if our code ever panics. 6 | // 7 | // For more details see 8 | // https://github.com/rustwasm/console_error_panic_hook#readme 9 | #[cfg(feature = "console_error_panic_hook")] 10 | console_error_panic_hook::set_once(); 11 | } 12 | -------------------------------------------------------------------------------- /wasm2wat/.cargo-ok: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zebp/wasm-remapper-web/5ce7fc79843f7b4d142965657242ece86cafe4ab/wasm2wat/.cargo-ok -------------------------------------------------------------------------------- /wasm2wat/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | bin/ 5 | pkg/ 6 | wasm-pack.log 7 | -------------------------------------------------------------------------------- /wasm2wat/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm2wat" 3 | version = "0.1.0" 4 | authors = ["vlakreeh "] 5 | edition = "2018" 6 | 7 | [package.metadata.wasm-pack.profile.release] 8 | wasm-opt = false 9 | 10 | [lib] 11 | crate-type = ["cdylib", "rlib"] 12 | 13 | [features] 14 | default = ["console_error_panic_hook"] 15 | 16 | [dependencies] 17 | wasm-bindgen = "0.2.63" 18 | 19 | # The `console_error_panic_hook` crate provides better debugging of panics by 20 | # logging them with `console.error`. This is great for development, but requires 21 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for 22 | # code size when deploying. 23 | console_error_panic_hook = { version = "0.1.6", optional = true } 24 | 25 | # `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size 26 | # compared to the default allocator's ~10K. It is slower than the default 27 | # allocator, however. 28 | # 29 | # Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. 30 | wee_alloc = { version = "0.4.5", optional = true } 31 | wasmprinter = "0.2.9" 32 | 33 | [profile.release] 34 | # Tell `rustc` to optimize for small code size. 35 | opt-level = "s" 36 | -------------------------------------------------------------------------------- /wasm2wat/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | 3 | use wasm_bindgen::prelude::*; 4 | 5 | // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global 6 | // allocator. 7 | #[cfg(feature = "wee_alloc")] 8 | #[global_allocator] 9 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; 10 | 11 | #[wasm_bindgen] 12 | pub fn wasm2wat(input: &[u8]) -> Result { 13 | wasmprinter::print_bytes(input).map_err(|error| error.to_string().into()) 14 | } 15 | -------------------------------------------------------------------------------- /wasm2wat/src/utils.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused)] 2 | pub fn set_panic_hook() { 3 | // When the `console_error_panic_hook` feature is enabled, we can call the 4 | // `set_panic_hook` function at least once during initialization, and then 5 | // we will get better error messages if our code ever panics. 6 | // 7 | // For more details see 8 | // https://github.com/rustwasm/console_error_panic_hook#readme 9 | #[cfg(feature = "console_error_panic_hook")] 10 | console_error_panic_hook::set_once(); 11 | } 12 | --------------------------------------------------------------------------------