├── test ├── asmjs │ ├── Makefile │ ├── out.asm.js.mem │ ├── main.c │ ├── out.asm.js.map │ └── out.asm.js ├── wasm │ ├── emcc.wasm │ ├── funcs.wasm │ ├── addTwo.wasm │ ├── switch.wasm │ ├── switch.wast │ └── emcc.wast ├── wast-tests │ ├── addTwo.wast │ ├── Makefile │ └── funcs.wast ├── emcc │ ├── Makefile │ └── main.c ├── switch │ ├── Makefile │ └── switch.c ├── test-spec.sh ├── disasm │ ├── funcs.c │ ├── addTwo.c │ ├── switch.c │ ├── switch_wast.c │ └── emcc.c └── tests.sh ├── wasmdec.js ├── wasmdec.wasm └── wasmdec.js ├── src ├── parsers │ ├── unreachable.cc │ ├── atomics.cc │ ├── nop.cc │ ├── get_local.cc │ ├── block.cc │ ├── const.cc │ ├── get_global.cc │ ├── host.cc │ ├── unary.cc │ ├── call.cc │ ├── load.cc │ ├── drop.cc │ ├── call_indirect.cc │ ├── binary.cc │ ├── return.cc │ ├── select.cc │ ├── break.cc │ ├── loop.cc │ ├── if.cc │ ├── set_global.cc │ ├── store.cc │ ├── set_local.cc │ ├── parser.h │ ├── switch.cc │ └── parser.cc ├── decompiler │ ├── DecompilerCtx.h │ ├── DisasmConfig.h │ ├── MultiDecompiler.h │ ├── Decompiler.h │ ├── MultiDecompiler.cc │ └── Decompiler.cc ├── wasm │ ├── WasmContext.cc │ ├── WasmContext.h │ ├── WasmUtils.h │ └── WasmUtils.cc ├── Emitter.h ├── convert │ ├── IntermediateLocal.h │ ├── Conversion.h │ ├── BlockConverter.cc │ └── Conversion.cc ├── Emitter.cc ├── wasm_api.cc └── wasmdec.cc ├── .gitignore ├── .gitmodules ├── mkrelease.sh ├── release └── install.sh ├── LICENSE ├── Makefile └── README.md /test/asmjs/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | emcc -Os -s WASM=0 main.c -o out.asm.js -------------------------------------------------------------------------------- /test/wasm/emcc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwg/wasmdec/HEAD/test/wasm/emcc.wasm -------------------------------------------------------------------------------- /test/wasm/funcs.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwg/wasmdec/HEAD/test/wasm/funcs.wasm -------------------------------------------------------------------------------- /test/wasm/addTwo.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwg/wasmdec/HEAD/test/wasm/addTwo.wasm -------------------------------------------------------------------------------- /test/wasm/switch.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwg/wasmdec/HEAD/test/wasm/switch.wasm -------------------------------------------------------------------------------- /test/asmjs/out.asm.js.mem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwg/wasmdec/HEAD/test/asmjs/out.asm.js.mem -------------------------------------------------------------------------------- /wasmdec.js/wasmdec.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwg/wasmdec/HEAD/wasmdec.js/wasmdec.wasm -------------------------------------------------------------------------------- /test/asmjs/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | int i = 1; 5 | ++i; 6 | printf("i is %d\n", i); 7 | return 0; 8 | } -------------------------------------------------------------------------------- /test/wast-tests/addTwo.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func $addTwo (param i32 i32) (result i32) 3 | (return 4 | (i32.add 5 | (get_local 0) 6 | (get_local 1)))) 7 | (export "addTwo" $addTwo)) -------------------------------------------------------------------------------- /src/parsers/unreachable.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::unreachable(Context* ctx, Expression* ex) { 5 | return "/* Unreachable */"; 6 | } -------------------------------------------------------------------------------- /src/parsers/atomics.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::atomics(Context* ctx, Expression* ex) { 5 | return "/* Atomic operation unsupported */\n"; 6 | } -------------------------------------------------------------------------------- /src/parsers/nop.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::nop(Context* ctx, Expression* ex) { 5 | return util::tab(ctx->depth) + "// \n"; // Nop expressions do nothing 6 | } -------------------------------------------------------------------------------- /src/decompiler/DecompilerCtx.h: -------------------------------------------------------------------------------- 1 | #ifndef DECOMPILER_CTX_H_ 2 | #define DECOMPILER_CTX_H_ 3 | 4 | namespace wasmdec { 5 | class DecompilerCtx { 6 | int stackOverflowAbortId; 7 | }; 8 | }; 9 | 10 | #endif // DECOMPILER_CTX_H_ -------------------------------------------------------------------------------- /src/parsers/get_local.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::get_local(Context* ctx, Expression* ex) { 5 | GetLocal* spex = ex->cast(); 6 | return Convert::getLocal(spex->index); 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | wasmdec 3 | libbinaryen.so 4 | *.o 5 | *.bin 6 | .gdb_history 7 | out.c 8 | release/wasmdec 9 | release/libbinaryen.so 10 | release*.tar.gz 11 | wasmdec*.tar.gz 12 | wasmdec*.wast 13 | wasmdec*.map 14 | emcc_out/ 15 | .vscode/ -------------------------------------------------------------------------------- /test/emcc/Makefile: -------------------------------------------------------------------------------- 1 | SRC=$(wildcard *.c) 2 | CC=emcc 3 | OPT=-g -s WASM=1 -s SIDE_MODULE=1 4 | OUT=../wasm/emcc.wasm 5 | 6 | all: 7 | $(CC) $(OPT) $(SRC) -o $(OUT) 8 | rm -f *.map # No source maps needed 9 | rm -f ../wasm/*.map # No source maps needed -------------------------------------------------------------------------------- /test/switch/Makefile: -------------------------------------------------------------------------------- 1 | SRC=$(wildcard *.c) 2 | CC=emcc 3 | OPT=-g -s WASM=1 -s SIDE_MODULE=1 4 | OUT=../wasm/switch.wasm 5 | 6 | default: 7 | $(CC) $(OPT) $(SRC) -o $(OUT) 8 | # No source maps needed 9 | rm -f *.map 10 | rm -f ../wasm/*.map -------------------------------------------------------------------------------- /test/wast-tests/Makefile: -------------------------------------------------------------------------------- 1 | ASM=wasm-as 2 | OUTDIR=../wasm/ 3 | 4 | all: 5 | mkdir -p $(OUTDIR) 6 | mkdir -p ../disasm/ 7 | $(ASM) addTwo.wast -o $(OUTDIR)addTwo.wasm 8 | $(ASM) funcs.wast -o $(OUTDIR)funcs.wasm 9 | $(ASM) conditions.wast -o $(OUTDIR)conditions.wasm -------------------------------------------------------------------------------- /src/parsers/block.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::block(Context* ctx, Expression* ex) { 5 | string ret; 6 | Block* blck = ex->cast(); 7 | ctx->depth++; 8 | ret += Convert::getBlockBody(ctx, blck); 9 | ctx->depth--; 10 | return ret; 11 | } -------------------------------------------------------------------------------- /src/parsers/const.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::_const(Context* ctx, Expression* ex) { 5 | // Resolve constant's literal value to a syntactically valid C literal 6 | Literal* val = &(ex->cast()->value); 7 | return util::getLiteralValue(val); 8 | } -------------------------------------------------------------------------------- /src/parsers/get_global.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::get_global(Context* ctx, Expression* ex) { 5 | string ret; 6 | // Global variable lookup 7 | ret += ex->cast()->name.str; 8 | // ret += "\n"; 9 | return ret; 10 | } -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "binaryen"] 2 | path = external/binaryen 3 | url = https://github.com/WebAssembly/binaryen.git 4 | [submodule "cxxopts"] 5 | path = external/cxxopts 6 | url = https://github.com/jarro2783/cxxopts.git 7 | [submodule "testsuite"] 8 | path = external/testsuite 9 | url = https://github.com/webassembly/testsuite -------------------------------------------------------------------------------- /test/wast-tests/funcs.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $varg_i32 (func (result i32))) 3 | (func $addTwo (param i32 i32) (result i32) 4 | (return 5 | (i32.add 6 | (get_local 0) 7 | (get_local 1) 8 | ) 9 | ) 10 | ) 11 | (func $getOne (type $varg_i32) 12 | (return 13 | (i32.const 1) 14 | ) 15 | ) 16 | (export "addTwo" $addTwo) 17 | ) -------------------------------------------------------------------------------- /mkrelease.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # simple script to export files to the release/ directory 4 | 5 | if [[ $EUID -ne 0 ]]; then 6 | echo "you must be root" 7 | exit 1 8 | fi 9 | 10 | mkdir -p release/ 11 | 12 | make binaryen 13 | make default 14 | 15 | cp wasmdec release/ 16 | cp external/binaryen/lib/libbinaryen.so release/ 17 | 18 | tar -czvf release.tar.gz release/ -------------------------------------------------------------------------------- /test/test-spec.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | for wast in ../external/testsuite/*.wast; do 3 | out_file=${wast}.decompiled.c 4 | echo "test-spec.sh : decompiling '$wast' to '$out_file'" 5 | ../wasmdec $wast -d -e -o $out_file 6 | returned=$? 7 | if [ $returned -ne 0 ]; then 8 | echo "test-spec.sh : wasmdec failed on '$wast', abort" 9 | exit 1 10 | fi 11 | done -------------------------------------------------------------------------------- /src/parsers/host.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::host(Context* ctx, Expression* ex) { 5 | string ret; 6 | Host* hexp = ex->cast(); 7 | string hoperands = Convert::parseOperandList(ctx, &(hexp->operands)); 8 | string hfunc = Convert::getHostFunc(hexp->op); 9 | ret += "/* Host call */\n" + hfunc + hoperands; 10 | return ret; 11 | } -------------------------------------------------------------------------------- /src/parsers/unary.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::unary(Context* ctx, Expression* ex) { 5 | string ret; 6 | Unary* uex = ex->cast(); 7 | ctx->lastExpr = ex; 8 | ctx->functionLevelExpression = false; 9 | string unaryEx = Convert::parseExpr(ctx, uex->value); 10 | ret += Convert::getUnary(unaryEx, uex->op); 11 | return ret; 12 | } -------------------------------------------------------------------------------- /test/emcc/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int doubleValue(int value) { 4 | return value * 2; 5 | } 6 | int main() { 7 | // Loop testing 8 | int val; 9 | while (1) { 10 | val += 1; 11 | if (val < 10) { 12 | break; 13 | } 14 | } 15 | printf("%s\n", "Hello, wasmdec!"); 16 | // Load / store testing 17 | val = 0 + 1 + 2 + 3; 18 | // Function call testing 19 | int secondVal = doubleValue(val); 20 | printf("secondVal: %d\n", secondVal); 21 | return 0; 22 | } -------------------------------------------------------------------------------- /src/parsers/call.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::call(Context* ctx, Expression* ex) { 5 | string ret; 6 | Call* fnCall = ex->cast(); 7 | if (ctx->depth < 1) { 8 | ret += util::tab(1); 9 | } else { 10 | ret += util::tab(ctx->depth); 11 | } 12 | ret += Convert::getFName(fnCall->target) + Convert::parseOperandList(ctx, &(fnCall->operands)); 13 | ret += ";\n"; 14 | return ret; 15 | } -------------------------------------------------------------------------------- /src/parsers/load.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::load(Context* ctx, Expression* ex) { 5 | string ret; 6 | // Memory loading 7 | Load* lxp = ex->cast(); 8 | ctx->lastExpr = ex; 9 | ctx->functionLevelExpression = false; 10 | string var = Convert::parseExpr(ctx, lxp->ptr); 11 | ret += util::tab(ctx->depth); 12 | ret += "*(void*)("; 13 | ret += var; 14 | ret += ")"; 15 | return ret; 16 | } -------------------------------------------------------------------------------- /src/parsers/drop.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::drop(Context* ctx, Expression* ex) { 5 | string ret; 6 | 7 | Drop* dex = ex->cast(); 8 | ret += util::tab(1); 9 | ret += "/* Drop routine */\n"; 10 | ctx->functionLevelExpression = false; 11 | ctx->lastExpr = ex; 12 | ret += Convert::parseExpr(ctx, dex->value); 13 | ret += util::tab(1); 14 | ret += "/* End of drop routine */\n"; 15 | 16 | return ret; 17 | } -------------------------------------------------------------------------------- /src/wasm/WasmContext.cc: -------------------------------------------------------------------------------- 1 | #include "WasmContext.h" 2 | using namespace wasmdec; 3 | 4 | Context::Context(Function* _fn, Module* _md, DecompilerCtx* _dctx) { 5 | isGlobal = false; 6 | fn = _fn; 7 | mod = _md; 8 | depth = 0; 9 | if (_dctx) { 10 | hasDecompilerCtx = true; 11 | dctx = _dctx; 12 | } else { 13 | hasDecompilerCtx = false; 14 | dctx = nullptr; 15 | } 16 | } 17 | Context::Context(Module* _md) { 18 | isGlobal = true; 19 | fn = nullptr; // No function context in global 20 | mod = _md; 21 | } -------------------------------------------------------------------------------- /src/parsers/call_indirect.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::call_indirect(Context* ctx, Expression* ex) { 5 | string ret; 6 | 7 | CallIndirect* ci = ex->cast(); 8 | ctx->lastExpr = ex; 9 | ctx->functionLevelExpression = false; 10 | string _icall = Convert::parseExpr(ctx, ci->target); 11 | ret += "// Indirect call:\n"; 12 | ret += "(" + _icall + ")"; 13 | ret += Convert::parseOperandList(ctx, &(ci->operands)); 14 | ret += "; \n"; 15 | 16 | return ret; 17 | } -------------------------------------------------------------------------------- /release/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # super simple script to help install wasmdec 4 | 5 | if [[ $EUID -ne 0 ]]; then 6 | echo "you must be root to install wasmdec" 7 | exit 1 8 | fi 9 | 10 | if [ -e wasmdec ] 11 | then 12 | echo "installing wasmdec" 13 | if [ -d "/usr/lib64" ]; then cp libbinaryen.so /usr/lib64/; else cp libbinaryen.so /usr/lib/; fi 14 | cp ./wasmdec /usr/bin 15 | echo "wasmdec installed successfully." 16 | exit 0 17 | else 18 | echo "no wasmdec binary exists in this directory, something is horribly wrong." 19 | exit 1 20 | fi -------------------------------------------------------------------------------- /src/Emitter.h: -------------------------------------------------------------------------------- 1 | #ifndef _wasmdec_EMITTER_H 2 | #define _wasmdec_EMITTER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | 12 | namespace wasmdec { 13 | class Emitter { 14 | public: 15 | Emitter(); 16 | stringstream& operator<<(string); 17 | void comment(string); 18 | void preamble(); 19 | void ln(); 20 | string getCode(); 21 | protected: 22 | stringstream str; 23 | }; 24 | } // namespace wasmdec 25 | 26 | #endif // _wasmdec_EMITTER_H -------------------------------------------------------------------------------- /src/parsers/binary.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::binary(Context* ctx, Expression* ex) { 5 | string ret; 6 | // Binary operations, including conditionals and arithmetic 7 | Binary* spex = ex->cast(); 8 | ctx->lastExpr = ex; 9 | ctx->functionLevelExpression = false; 10 | string e1 = Convert::parseExpr(ctx, spex->left); 11 | ctx->lastExpr = ex; 12 | ctx->functionLevelExpression = false; 13 | string e2 = Convert::parseExpr(ctx, spex->right); 14 | ret += Convert::getBinOperator(e1, spex->op, e2); 15 | return ret; 16 | } -------------------------------------------------------------------------------- /test/switch/switch.c: -------------------------------------------------------------------------------- 1 | /* 2 | Simple test for switch expressions in wasm 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | int main(int argc, char** argv) { 9 | volatile int a = 42; 10 | switch (a) { 11 | case 1: 12 | printf("one\n"); 13 | break; 14 | case 2: 15 | printf("two\n"); 16 | break; 17 | case 42: 18 | printf("forty two!\n"); 19 | break; 20 | default: 21 | printf("default\n"); 22 | break; 23 | } 24 | return 0; 25 | } -------------------------------------------------------------------------------- /src/decompiler/DisasmConfig.h: -------------------------------------------------------------------------------- 1 | #ifndef _DISASM_CONFIG_H 2 | #define _DISASM_CONFIG_H 3 | 4 | enum DisasmMode { 5 | Wasm, 6 | AsmJs, 7 | Wast, 8 | None = 0 9 | }; 10 | 11 | class DisasmConfig { 12 | public: 13 | bool debug; 14 | bool extra; 15 | bool includePreamble; 16 | string fnPreface; 17 | DisasmMode mode; 18 | inline DisasmConfig(bool _debug, bool _extra, DisasmMode _mode) { 19 | debug = _debug; 20 | extra = _extra; 21 | mode = _mode; 22 | includePreamble = true; 23 | fnPreface = ""; 24 | } 25 | }; 26 | 27 | #endif -------------------------------------------------------------------------------- /src/decompiler/MultiDecompiler.h: -------------------------------------------------------------------------------- 1 | #ifndef _MULTI_DECOMP_H 2 | #define _MULTI_DECOMP_H 3 | 4 | #include "Decompiler.h" 5 | using namespace std; 6 | 7 | namespace wasmdec { 8 | class MultiDecompiler { 9 | public: 10 | MultiDecompiler(vector, DisasmConfig); 11 | string getOutput(void); 12 | bool failed; 13 | protected: 14 | stringstream codeStream; 15 | bool readFile(vector*, string); 16 | string getFileExt(string); 17 | string getEverythingButFileExt(string); 18 | DisasmMode getDisasmMode(string); 19 | 20 | vector infiles; 21 | }; 22 | }; 23 | 24 | #endif // _MULTI_DECOMP_H -------------------------------------------------------------------------------- /test/disasm/funcs.c: -------------------------------------------------------------------------------- 1 | // Decompiled Web Assembly generated by wasmdec 2 | // Preamble: 3 | #include 4 | #include 5 | typedef float float32_t; 6 | typedef double float64_t; 7 | 8 | // No WASM imports. 9 | 10 | // WASM functions: 11 | 12 | /* 13 | Function '$0' 14 | Local variables: 0 15 | Parameters: 2 16 | Immediate block expressions: 1 17 | */ 18 | int32_t fn_0(int32_t local_0, int32_t local_1) { 19 | return local_0 + local_1; 20 | } 21 | /* 22 | Function '$1' 23 | Local variables: 0 24 | Parameters: 0 25 | Immediate block expressions: 1 26 | */ 27 | int32_t fn_1() { 28 | return 1; 29 | } 30 | -------------------------------------------------------------------------------- /src/parsers/return.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::_return(Context* ctx, Expression* ex) { 5 | string ret; 6 | Return* spex = ex->cast(); 7 | if (ctx->depth < 1) { 8 | ret += util::tab(1); 9 | } else { 10 | ret += util::tab(ctx->depth); 11 | } 12 | if (spex->value) { 13 | // Insert expression as function return value 14 | ret += "return "; 15 | ctx->lastExpr = ex; 16 | ctx->functionLevelExpression = false; 17 | ret += Convert::parseExpr(ctx, spex->value) + ";\n"; 18 | } else { 19 | ret += "return;\n"; // For void functions 20 | } 21 | return ret; 22 | } -------------------------------------------------------------------------------- /test/tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # hacky test script for wasmdec 4 | # make sure you have wasmdec installed before running (run "make install" in the root dir) 5 | 6 | do_test () { 7 | wasmdec -i $1 -o test.c -e -d 8 | if [ $? -eq 0 ]; then 9 | # test succeeded! 10 | echo "TEST SUCCESS: test $1 passed" 11 | else 12 | # test fail 13 | echo "TEST FAIL: test $1 failed horribly" 14 | exit 1 15 | fi 16 | rm -f test.c 17 | } 18 | 19 | # perform test for each binary 20 | 21 | do_test "wasm/emcc.wast" 22 | do_test "wasm/emcc.wasm" 23 | do_test "wasm/funcs.wasm" 24 | do_test "wasm/switch.wasm" 25 | do_test "wasm/switch.wast" 26 | do_test "wast-tests/addTwo.wast" 27 | do_test "wast-tests/funcs.wast" -------------------------------------------------------------------------------- /src/parsers/select.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | using namespace wasmdec; 3 | 4 | string wasmdec::parsers::select(Context* ctx, Expression* ex) { 5 | string ret; 6 | // Select is the WASM equivalent of C's ternary operator. 7 | Select* slex = ex->cast()) { 26 | return wasmdec::parsers::select(ctx, ex); 27 | } else if (ex->is()) { 28 | return wasmdec::parsers::drop(ctx, ex); 29 | } else if (ex->is()) { 30 | return wasmdec::parsers::host(ctx, ex); 31 | } else if (ex->is()) { 32 | return wasmdec::parsers::unreachable(ctx, ex); 33 | } else if (ex->is()) { 34 | return wasmdec::parsers::set_global(ctx, ex); 35 | } else if (ex->is()) { 36 | return wasmdec::parsers::get_global(ctx, ex); 37 | } else if (ex->is()) { 38 | return wasmdec::parsers::loop(ctx, ex); 39 | } else if (ex->is()) { 40 | return wasmdec::parsers::_switch(ctx, ex); 41 | } else if (ex->is()) { 42 | return wasmdec::parsers::call(ctx, ex); 43 | } else if (ex->is()) { 44 | return wasmdec::parsers::_if(ctx, ex); 45 | } else if (ex->is()) { 46 | return wasmdec::parsers::nop(ctx, ex); 47 | } else if (ex->is()) { 48 | return wasmdec::parsers::_if(ctx, ex); 49 | } else if (ex->is()) { 50 | return wasmdec::parsers::_const(ctx, ex); 51 | } else if (ex->is()) { 52 | return wasmdec::parsers::_return(ctx, ex); 53 | } else if (ex->is()) { 54 | return wasmdec::parsers::_break(ctx, ex); 55 | } 56 | return "/* UNKNOWN EXPRESSION */"; 57 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SRC=$(wildcard src/*.cc src/**/*.cc !(src/wasm_api.cc)) 2 | EMCC_SRC=$(wildcard src/*.cc src/**/*.cc !(src/wasmdec.cc)) 3 | OBJS=$(SRC:.cc=.o) 4 | OUT=wasmdec 5 | CC=g++ 6 | CCOPTS=-std=c++14 -Iexternal/binaryen/src -Iexternal/cxxopts/include -c -Wall -g 7 | RELEASE_CCOPTS=-std=c++14 -Iexternal/binaryen/src -c -Wall -O3 8 | LDOPTS=-Lexternal/binaryen/lib -lbinaryen -lpthread 9 | 10 | default: $(SRC) $(OUT) 11 | 12 | $(OUT): $(OBJS) 13 | @echo -n "Link " 14 | @echo $@ 15 | $(CC) $(OBJS) $(LDOPTS) -o $@ 16 | 17 | .cc.o: 18 | @echo -n "Build source " 19 | @echo $< 20 | $(CC) $(CCOPTS) $< -o $@ 21 | wasm: 22 | # make wasmBinaryen 23 | mkdir -p emcc_out 24 | EMCC_DEBUG=1 em++ external/binaryen/lib/libbinaryen.so $(EMCC_SRC) \ 25 | -std=c++14 -Iexternal/binaryen/src -Iexternal/cxxopts/include -Wall -O3 \ 26 | -Wall -o emcc_out/wasmdec.js \ 27 | -s EXPORTED_FUNCTIONS='["_wasmdec_create_decompiler", "_wasmdec_decompile", "_wasmdec_get_decompiled_code", "_wasmdec_destroy_decompiler"]' \ 28 | -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap", "addOnPostRun"]' -s ASSERTIONS=1 -s SAFE_HEAP=1 29 | cp emcc_out/wasmdec.wasm wasmdec.js/ 30 | echo "(function(){" > wasmdec.js/wasmdec.wasm.js 31 | cat emcc_out/wasmdec.js >> wasmdec.js/wasmdec.wasm.js 32 | sed -i -e 's/rrides={};/rrides={};window\.Wasmdec\.Module=Module;/g' wasmdec.js/wasmdec.wasm.js 33 | echo "})();" >> wasmdec.js/wasmdec.wasm.js 34 | 35 | clean: 36 | rm -f *.o wasmdec 37 | rm -f src/*.o 38 | rm -f src/**/*.o 39 | 40 | # To build binaryen 41 | binaryen: 42 | cd external/binaryen && cmake . && make 43 | # To build binaryen as a webassembly library 44 | wasmBinaryen: 45 | cd external/binaryen && EMCC_DEBUG=1 em++ src/*.cpp src/**/*.cpp -I/home/p/wasmdec/external/binaryen/src -std=c++11 -o lib/libinaryen.bc 46 | 47 | # To install binaryen 48 | installBinaryen: 49 | make installBinaryen`(uname -s)` 50 | 51 | installBinaryenLinux: 52 | if [ -d "/usr/lib64" ]; then cp external/binaryen/lib/libbinaryen.so /usr/lib64/; else cp external/binaryen/lib/libbinaryen.so /usr/lib/; fi 53 | 54 | installBinaryenDarwin: 55 | cp external/binaryen/lib/libbinaryen.dylib /usr/local/lib/ 56 | 57 | # To install wasmdec 58 | install: 59 | make install`(uname -s)` 60 | installLinux: 61 | cp ./wasmdec /usr/bin 62 | installDarwin: 63 | cp ./wasmdec /usr/local/bin 64 | 65 | # To build and install everything 66 | all: 67 | make binaryen 68 | make installBinaryen 69 | make default 70 | make install 71 | -------------------------------------------------------------------------------- /src/wasm_api.cc: -------------------------------------------------------------------------------- 1 | // Definitions for functions that can be used when wasmdec is compiled to wasm 2 | #include "decompiler/MultiDecompiler.h" 3 | #include 4 | 5 | extern "C" { 6 | /* 7 | Creates a wasmdec decompiler 8 | 9 | debug: log debug information 10 | extra: output extra binary information in the decompilation 11 | mode: "wasm" or "wast", whether or not the input is an ast or binary 12 | input: the input binary / webassembly text 13 | 14 | return value: a pointer to the configured decompiler (for use in other wasmdec api functions) 15 | */ 16 | Decompiler* wasmdec_create_decompiler(bool debug, bool extra, 17 | char* mode, char* input) { 18 | // convert mode to a DisasmMode 19 | string smode = string(mode); 20 | DisasmMode dmode = DisasmMode::None; 21 | if (smode == "wasm") { 22 | dmode = DisasmMode::Wasm; 23 | } else if (smode == "wast") { 24 | dmode = DisasmMode::Wast; 25 | } else { 26 | printf("WARN: wasmdec: invalid input mode"); 27 | return nullptr; 28 | } 29 | // Create the config 30 | DisasmConfig conf(debug, extra, dmode); 31 | 32 | // Convert input char* to vector for decompiler class 33 | vector inv; 34 | size_t input_size = strlen(input); 35 | for (unsigned int i = 0; i < input_size; ++i) { 36 | char c = input[i]; 37 | inv.push_back(c); 38 | } 39 | // Create decompiler 40 | Decompiler* ret = new Decompiler(conf, inv); 41 | return ret; 42 | } 43 | /* 44 | Decompiles the input binary / ast 45 | 46 | decomp: the decompiler generated in wasmdec_create_decompiler() 47 | 48 | return value: whether or not the decompiler succeeded 49 | */ 50 | bool wasmdec_decompile(Decompiler* decomp) { 51 | if (decomp == nullptr) 52 | return false; 53 | decomp->decompile(); 54 | if (decomp->failed()) { 55 | return false; 56 | } 57 | return true; 58 | } 59 | /* 60 | Gets the decompiler's output 61 | 62 | decomp: the decompiler generated in wasmdec_create_decompiler() 63 | 64 | return value: resulting decompiled C code 65 | */ 66 | char* wasmdec_get_decompiled_code(Decompiler* decomp) { 67 | if (decomp->failed()) 68 | return nullptr; 69 | string emitted = decomp->getEmittedCode(); 70 | size_t ret_size = emitted.size() + 1; 71 | char* ret = (char*)malloc(ret_size); 72 | memset(ret, 0x0, ret_size); 73 | for (unsigned int i = 0 ; i < emitted.size(); ++i) { 74 | ret[i] = emitted.at(i); 75 | } 76 | return ret; 77 | } 78 | /* 79 | Delete decompiler 80 | 81 | decomp: the decompiler generated in wasmdec_create_decompiler() 82 | */ 83 | void wasmdec_destroy_decompiler(Decompiler* decomp) { 84 | delete decomp; 85 | } 86 | } -------------------------------------------------------------------------------- /src/decompiler/MultiDecompiler.cc: -------------------------------------------------------------------------------- 1 | #include "MultiDecompiler.h" 2 | 3 | // helper functions 4 | string MultiDecompiler::getFileExt(string fname) { 5 | string::size_type idx = fname.rfind('.'); 6 | if(idx != string::npos) { 7 | string extension = fname.substr(idx + 1); 8 | return extension; 9 | } else { 10 | return ""; 11 | } 12 | } 13 | string MultiDecompiler::getEverythingButFileExt(string a) { 14 | size_t l = a.find_last_of("."); 15 | if (l == string::npos) return ""; 16 | string r = a.substr(0, l); 17 | return r; 18 | } 19 | DisasmMode MultiDecompiler::getDisasmMode(string infile) { 20 | // Convert file extension to disassembler mode 21 | string ext = getFileExt(infile); 22 | if (ext == "wasm") { 23 | return DisasmMode::Wasm; 24 | } else if (ext == "wast") { 25 | return DisasmMode::Wast; 26 | } else if (ext == "js") { 27 | return DisasmMode::AsmJs; 28 | } else { 29 | return DisasmMode::Wasm; 30 | } 31 | } 32 | bool MultiDecompiler::readFile(vector* data, string path) { 33 | ifstream file(path); 34 | if (!file.eof() && !file.fail()) { 35 | file.seekg(0, ios_base::end); 36 | streampos fileSize = file.tellg(); 37 | data->resize(fileSize); 38 | file.seekg(0, ios_base::beg); 39 | file.read(&(data->operator[](0)), fileSize); 40 | return true; 41 | } else { 42 | return false; 43 | } 44 | } 45 | 46 | MultiDecompiler::MultiDecompiler(vector _infiles, DisasmConfig conf) { 47 | infiles = _infiles; 48 | failed = false; 49 | // Read all the infiles 50 | for (unsigned int i = 0; i < infiles.size(); ++i) { 51 | vector raw; 52 | if (!readFile(&raw, infiles.at(i))) { 53 | failed = true; 54 | break; 55 | } 56 | // create config 57 | DisasmConfig thisConf = conf; 58 | string fnPreface = getEverythingButFileExt(infiles.at(i)) + "_"; 59 | for (unsigned int i = 0; i < fnPreface.length(); ++i) { 60 | if (fnPreface[i] == '/') 61 | fnPreface[i] = '_'; 62 | } 63 | if (fnPreface == "") 64 | fnPreface = "WASMDEC_UNKNOWN_MODULE_"; 65 | conf.fnPreface = fnPreface; 66 | 67 | if (i == 0) { 68 | thisConf.includePreamble = true; 69 | } else { 70 | thisConf.includePreamble = false; 71 | } 72 | thisConf.mode = getDisasmMode(infiles.at(i)); 73 | // create decompiler 74 | Decompiler* d = new Decompiler(thisConf, raw); 75 | // do decompilation 76 | d->decompile(); 77 | if (d->failed()) { 78 | failed = true; 79 | break; 80 | } 81 | if (i != 0) 82 | codeStream << endl << endl; 83 | 84 | codeStream << "// Module '" << infiles.at(i) << "':" << endl 85 | << d->getEmittedCode(); 86 | } 87 | } 88 | string MultiDecompiler::getOutput() { 89 | return codeStream.str(); 90 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wasmdec 2 | wasmdec is a program that converts WebAssembly binaries to C. 3 | 4 | # Demo 5 | [An online real-time WebAssembly decompiler utilizing wasmdec is avalible here](https://wwwg.github.io/web-wasmdec/) 6 | 7 | # Simple Example 8 | wasmdec will translate this WebAssembly binary: 9 | ```wasm 10 | (module 11 | (func $addTwo (param i32 i32) (result i32) 12 | (return 13 | (i32.add (get_local 0) (get_local 1)) 14 | ) 15 | ) 16 | (export "addTwo" $addTwo) 17 | ) 18 | ``` 19 | To the following pseudo-C code: 20 | ```c 21 | int fn_addTwo(int arg0, int arg1) { 22 | return arg0 + arg1; 23 | } 24 | ``` 25 | # More practical examples 26 | 27 | ### [Diep.io](https://diep.io) (HTML5 web game written in C++ and compiled to WASM) 28 | Diep.io is a real time web game written in C++ and compiled to WebAssembly via Emscripten. 29 | * The WebAssembly binary is is always `http://static.diep.io/build_.wasm.wasm` 30 | * [The decompiled binary is avalible here](examples/diep_decompiled.c) 31 | 32 | ### wasmdec 33 | wasmdec is capable of decompiling itself back to C. 34 | * The makefile has a `wasm` target that uses [Emscripten](https://github.com/kripken/emscripten) to compile wasmdec to WebAssembly 35 | * [The decompiled binary is avalible here](examples/wasmdec_decompiled.c) 36 | 37 | ### [WebDSP](https://github.com/shamadee/web-dsp) (a signal processing library compiled to WASM) 38 | From the [WebDSP repository](https://github.com/shamadee/web-dsp): 39 | ``` 40 | WebDSP is a collection of highly performant algorithms, which are designed to be building blocks for web applications that aim to operate on media data. The methods are written in C++ and compiled to WASM, and exposed as simple vanilla Javascript functions developers can run on the client side. 41 | ``` 42 | * A compiled version of the library is avalible on the WebDSP demo page 43 | * [The decompiled library is avalible here](examples/webdsp_decompiled.c) 44 | 45 | # Applications 46 | [A CTF write-up which uses wasmdec to reverse engineer a WASM binary](http://maroueneboubakri.blogspot.com/2018/04/nuit-du-hack-ctf-quals-2018-assemblyme.html) 47 | 48 | # Installing with release 49 | 50 | - Grab a release on the releases page and select the correct tarball for your OS and arch. 51 | - Extract and run `install.sh` as root. 52 | 53 | # Installing manually 54 | 55 | ## Getting the code 56 | Clone the repository with 57 | ```bash 58 | git clone https://github.com/wwwg/wasmdec.git --recursive 59 | ``` 60 | Make sure the recursive flag is set to clone all the submodules. 61 | ## Building 62 | To build wasmdec and install all of it's dependencies, run `sudo make all` in the `wasmdec` directory. GCC 7 or higher is reccomended. 63 | 64 | # Usage 65 | ```bash 66 | wasmdec -o (output file) (options) [input files] 67 | ``` 68 | Where options is one of: 69 | - `-e` or `--extra` : Emits extra function information as comments: 70 | * Raw WebAssembly names of functions 71 | * Number of local variables and parameters of functions 72 | - `-m` or `--memdump` : 73 | * Dumps the binary's memory and table to disk 74 | * NOTE : memdump ONLY dumps memory and doesn't actually do any decompilation 75 | - `-d` or `--debug` : Print extra debug information to stdout 76 | - If no output file is specified, the default is `out.c` 77 | - When more than one input file is provided, wasmdec will decompile each WebAssembly to the same output file. Functions from more than one file are prefixed by their module name in order to prevent ambiguous function definitions. 78 | 79 | -------------------------------------------------------------------------------- /test/disasm/switch.c: -------------------------------------------------------------------------------- 1 | // Decompiled Web Assembly generated by wasmdec. Preamble: 2 | #include // For the bit size specific types 3 | #include // For certian WASM operations 4 | typedef const char* wasm_table_t; // WASM tables 5 | // Bit size specific types not declared in stdint.h: 6 | typedef float float32_t; 7 | typedef double float64_t; 8 | // C implementation of WASM expressions: 9 | unsigned int _rotl(const unsigned int value, int shift) { 10 | if ((shift &= sizeof(value) * 8 - 1) == 0) 11 | return value; 12 | return (value << shift) | (value >> (sizeof(value)*8 - shift)); 13 | } 14 | unsigned int _rotr(const unsigned int value, int shift) { 15 | if ((shift &= sizeof(value) * 8 - 1) == 0) 16 | return value; 17 | return (value >> shift) | (value << (sizeof(value)*8 - shift)); 18 | } 19 | #define MAX(a,b) ((a) > (b) ? a : b) 20 | #define MIN(a,b) ((a) < (b) ? a : b) 21 | // Host functions: used to request information from host machine. 22 | extern int32_t host_has_feature(int32_t feature_opcode); 23 | extern void host_grow_memory(int32_t size); 24 | extern int32_t host_get_current_memory(void); 25 | extern int32_t host_get_page_size(void); 26 | // End of preamble 27 | 28 | // WASM imports: 29 | /* 30 | Import 'import$0': 31 | Module: 'env' 32 | Base: 'memory' 33 | */ 34 | extern const char* import$0; // 35 | /* 36 | Import 'import$1': 37 | Module: 'env' 38 | Base: 'table' 39 | */ 40 | extern wasm_table_t import$1; // 41 | /* 42 | Import 'import$2': 43 | Module: 'env' 44 | Base: 'memoryBase' 45 | */ 46 | extern int import$2; 47 | /* 48 | Import 'import$3': 49 | Module: 'env' 50 | Base: 'tableBase' 51 | */ 52 | extern int import$3; 53 | /* 54 | Import 'import$4': 55 | Module: 'env' 56 | Base: 'DYNAMICTOP_PTR' 57 | */ 58 | extern int import$4; 59 | /* 60 | Import 'import$5': 61 | Module: 'env' 62 | Base: 'tempDoublePtr' 63 | */ 64 | extern int import$5; 65 | /* 66 | Import 'import$6': 67 | Module: 'env' 68 | Base: 'ABORT' 69 | */ 70 | extern int import$6; 71 | /* 72 | Import 'import$7': 73 | Module: 'global' 74 | Base: 'NaN' 75 | */ 76 | extern double import$7; 77 | /* 78 | Import 'import$8': 79 | Module: 'global' 80 | Base: 'Infinity' 81 | */ 82 | extern double import$8; 83 | /* 84 | Import 'abortStackOverflow': 85 | Module: 'env' 86 | Base: 'abortStackOverflow' 87 | */ 88 | extern void abortStackOverflow(int local_0) 89 | /* 90 | Import '_printf': 91 | Module: 'env' 92 | Base: '_printf' 93 | */ 94 | extern int _printf(int local_0, int local_1) 95 | // WASM globals: 96 | int global$0 = import$4; 97 | int global$1 = import$5; 98 | int global$2 = import$6; 99 | int global$3 = 0; 100 | int global$4 = 0; 101 | int global$5 = 0; 102 | int global$6 = 0; 103 | int global$7 = 0; 104 | int global$8 = 0; 105 | double global$9 = import$7; 106 | double global$10 = import$8; 107 | int global$11 = 0; 108 | int global$12 = 0; 109 | int global$13 = 0; 110 | int global$14 = 0; 111 | double global$15 = 0.000000; 112 | int global$16 = 0; 113 | float global$17 = 0.000000; 114 | float global$18 = 0.000000; 115 | 116 | // WASM functions: 117 | 118 | int fn_stackAlloc(int local_0) { 119 | // Parsed WASM function locals: 120 | int local_1; 121 | local_1 = global$3; 122 | global$3 = global$3 + local_0; 123 | global$3 = global$3 + 15 && -16; 124 | if (global$3 >= global$4) { 125 | abortStackOverflow(local_0); 126 | 127 | } // 128 | return local_1; 129 | } 130 | int fn_stackSave() { 131 | return global$3; 132 | } 133 | void fn_stackRestore(int local_0) { 134 | global$3 = local_0; 135 | } 136 | void fn_establishStackSpace(int local_0, int local_1) { 137 | global$3 = local_0; 138 | global$4 = local_1; 139 | } 140 | void fn_setThrew(int local_0, int local_1) { 141 | if (global$5 == 0) { 142 | global$5 = local_0; 143 | global$6 = local_1; 144 | 145 | } // 146 | } 147 | int fn__main(int local_0, int local_1) { 148 | // Parsed WASM function locals: 149 | int local_2; 150 | int local_3; 151 | int local_4; 152 | int local_5; 153 | int local_6; 154 | int local_7; 155 | int local_8; 156 | int local_9; 157 | int local_10; 158 | int local_11; 159 | int local_12; 160 | local_12 = global$3; 161 | global$3 = global$3 + 48; 162 | if (global$3 >= global$4) { 163 | abortStackOverflow(48); 164 | 165 | } // 166 | local_10 = local_12 + 24; 167 | local_9 = local_12 + 16; 168 | local_8 = local_12 + 8; 169 | local_7 = local_12; 170 | local_2 = 0; 171 | local_3 = local_0; 172 | local_4 = local_1; 173 | local_5 = 42; 174 | local_6 = local_5; 175 | } 176 | /* Drop routine */ 177 | _printf(import$2 + 0, local_7); 178 | /* End of drop routine */ 179 | break; /* Drop routine */ 180 | _printf(import$2 + 5, local_8); 181 | /* End of drop routine */ 182 | break; /* Drop routine */ 183 | _printf(import$2 + 10, local_9); 184 | /* End of drop routine */ 185 | break; /* Drop routine */ 186 | _printf(import$2 + 22, local_10); 187 | /* End of drop routine */ 188 | global$3 = local_12; 189 | return 0; 190 | } 191 | void fn_runPostSets() { 192 | // Parsed WASM function locals: 193 | int local_0; 194 | // 195 | } 196 | void fn___post_instantiate() { 197 | global$3 = import$2 + 32; 198 | global$4 = global$3 + 5242880; 199 | fn_runPostSets(); 200 | } 201 | 202 | /* 203 | Exported WASM functions: 204 | Function 'fn___post_instantiate': 205 | WASM name: '__post_instantiate' 206 | Export name: '__post_instantiate' 207 | 208 | Function 'fn__main': 209 | WASM name: '_main' 210 | Export name: '_main' 211 | 212 | Function 'fn_runPostSets': 213 | WASM name: 'runPostSets' 214 | Export name: 'runPostSets' 215 | 216 | */ 217 | -------------------------------------------------------------------------------- /test/disasm/switch_wast.c: -------------------------------------------------------------------------------- 1 | // Decompiled Web Assembly generated by wasmdec. Preamble: 2 | #include // For the bit size specific types 3 | #include // For certian WASM operations 4 | typedef const char* wasm_table_t; // WASM tables 5 | // Bit size specific types not declared in stdint.h: 6 | typedef float float32_t; 7 | typedef double float64_t; 8 | // C implementation of WASM expressions: 9 | unsigned int _rotl(const unsigned int value, int shift) { 10 | if ((shift &= sizeof(value) * 8 - 1) == 0) 11 | return value; 12 | return (value << shift) | (value >> (sizeof(value)*8 - shift)); 13 | } 14 | unsigned int _rotr(const unsigned int value, int shift) { 15 | if ((shift &= sizeof(value) * 8 - 1) == 0) 16 | return value; 17 | return (value >> shift) | (value << (sizeof(value)*8 - shift)); 18 | } 19 | #define MAX(a,b) ((a) > (b) ? a : b) 20 | #define MIN(a,b) ((a) < (b) ? a : b) 21 | // Host functions: used to request information from host machine. 22 | extern int32_t host_has_feature(int32_t feature_opcode); 23 | extern void host_grow_memory(int32_t size); 24 | extern int32_t host_get_current_memory(void); 25 | extern int32_t host_get_page_size(void); 26 | // End of preamble 27 | 28 | // WASM imports: 29 | /* 30 | Import '0': 31 | Module: 'env' 32 | Base: 'memory' 33 | */ 34 | extern const char* 0; // 35 | /* 36 | Import 'import$table$0': 37 | Module: 'env' 38 | Base: 'table' 39 | */ 40 | extern wasm_table_t import$table$0; // 41 | /* 42 | Import 'memoryBase': 43 | Module: 'env' 44 | Base: 'memoryBase' 45 | */ 46 | extern int memoryBase; 47 | /* 48 | Import 'tableBase': 49 | Module: 'env' 50 | Base: 'tableBase' 51 | */ 52 | extern int tableBase; 53 | /* 54 | Import 'DYNAMICTOP_PTR$asm2wasm$import': 55 | Module: 'env' 56 | Base: 'DYNAMICTOP_PTR' 57 | */ 58 | extern int DYNAMICTOP_PTR$asm2wasm$import; 59 | /* 60 | Import 'tempDoublePtr$asm2wasm$import': 61 | Module: 'env' 62 | Base: 'tempDoublePtr' 63 | */ 64 | extern int tempDoublePtr$asm2wasm$import; 65 | /* 66 | Import 'ABORT$asm2wasm$import': 67 | Module: 'env' 68 | Base: 'ABORT' 69 | */ 70 | extern int ABORT$asm2wasm$import; 71 | /* 72 | Import 'nan$asm2wasm$import': 73 | Module: 'global' 74 | Base: 'NaN' 75 | */ 76 | extern double nan$asm2wasm$import; 77 | /* 78 | Import 'inf$asm2wasm$import': 79 | Module: 'global' 80 | Base: 'Infinity' 81 | */ 82 | extern double inf$asm2wasm$import; 83 | /* 84 | Import 'abortStackOverflow': 85 | Module: 'env' 86 | Base: 'abortStackOverflow' 87 | */ 88 | extern void abortStackOverflow(int local_0) 89 | /* 90 | Import '_printf': 91 | Module: 'env' 92 | Base: '_printf' 93 | */ 94 | extern int _printf(int local_0, int local_1) 95 | // WASM globals: 96 | int DYNAMICTOP_PTR = DYNAMICTOP_PTR$asm2wasm$import; 97 | int tempDoublePtr = tempDoublePtr$asm2wasm$import; 98 | int ABORT = ABORT$asm2wasm$import; 99 | int STACKTOP = 0; 100 | int STACK_MAX = 0; 101 | int __THREW__ = 0; 102 | int threwValue = 0; 103 | int setjmpId = 0; 104 | int undef = 0; 105 | double nan = nan$asm2wasm$import; 106 | double inf = inf$asm2wasm$import; 107 | int tempInt = 0; 108 | int tempBigInt = 0; 109 | int tempBigIntS = 0; 110 | int tempValue = 0; 111 | double tempDouble = 0.000000; 112 | int tempRet0 = 0; 113 | float tempFloat = 0.000000; 114 | float f0 = 0.000000; 115 | 116 | // WASM functions: 117 | 118 | int fn_stackAlloc(int local_0) { 119 | // Parsed WASM function locals: 120 | int local_1; 121 | local_1 = STACKTOP; 122 | STACKTOP = STACKTOP + local_0; 123 | STACKTOP = STACKTOP + 15 && -16; 124 | if (STACKTOP >= STACK_MAX) { 125 | abortStackOverflow(local_0); 126 | 127 | } // 128 | return local_1; 129 | } 130 | int fn_stackSave() { 131 | return STACKTOP; 132 | } 133 | void fn_stackRestore(int local_0) { 134 | STACKTOP = local_0; 135 | } 136 | void fn_establishStackSpace(int local_0, int local_1) { 137 | STACKTOP = local_0; 138 | STACK_MAX = local_1; 139 | } 140 | void fn_setThrew(int local_0, int local_1) { 141 | if (__THREW__ == 0) { 142 | __THREW__ = local_0; 143 | threwValue = local_1; 144 | 145 | } // 146 | } 147 | int fn__main(int local_0, int local_1) { 148 | // Parsed WASM function locals: 149 | int local_2; 150 | int local_3; 151 | int local_4; 152 | int local_5; 153 | int local_6; 154 | int local_7; 155 | int local_8; 156 | int local_9; 157 | int local_10; 158 | int local_11; 159 | int local_12; 160 | local_12 = STACKTOP; 161 | STACKTOP = STACKTOP + 48; 162 | if (STACKTOP >= STACK_MAX) { 163 | abortStackOverflow(48); 164 | 165 | } // 166 | local_10 = local_12 + 24; 167 | local_9 = local_12 + 16; 168 | local_8 = local_12 + 8; 169 | local_7 = local_12; 170 | local_2 = 0; 171 | local_3 = local_0; 172 | local_4 = local_1; 173 | local_5 = 42; 174 | local_6 = local_5; 175 | } 176 | /* Drop routine */ 177 | _printf(memoryBase + 0, local_7); 178 | /* End of drop routine */ 179 | break; /* Drop routine */ 180 | _printf(memoryBase + 5, local_8); 181 | /* End of drop routine */ 182 | break; /* Drop routine */ 183 | _printf(memoryBase + 10, local_9); 184 | /* End of drop routine */ 185 | break; /* Drop routine */ 186 | _printf(memoryBase + 22, local_10); 187 | /* End of drop routine */ 188 | STACKTOP = local_12; 189 | return 0; 190 | } 191 | void fn_runPostSets() { 192 | // Parsed WASM function locals: 193 | int local_0; 194 | // 195 | } 196 | void fn___post_instantiate() { 197 | STACKTOP = memoryBase + 32; 198 | STACK_MAX = STACKTOP + 5242880; 199 | fn_runPostSets(); 200 | } 201 | 202 | /* 203 | Exported WASM functions: 204 | Function 'fn___post_instantiate': 205 | WASM name: '__post_instantiate' 206 | Export name: '__post_instantiate' 207 | 208 | Function 'fn__main': 209 | WASM name: '_main' 210 | Export name: '_main' 211 | 212 | Function 'fn_runPostSets': 213 | WASM name: 'runPostSets' 214 | Export name: 'runPostSets' 215 | 216 | */ 217 | -------------------------------------------------------------------------------- /src/wasmdec.cc: -------------------------------------------------------------------------------- 1 | #define __WASMDEC_VERSION "1.2.1" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "cxxopts.hpp" 11 | #include "decompiler/MultiDecompiler.h" 12 | 13 | // Global variables to be passed to the decompiler 14 | bool debugging = false, 15 | extra = false, 16 | memdump = false; 17 | std::string infile, outfile; 18 | std::vector infiles; // will be empty if there's only one file to decompile 19 | DisasmMode dmode; 20 | 21 | // Helper functions 22 | bool readFile(vector* data, string path) { 23 | ifstream file(path); 24 | if (!file.eof() && !file.fail()) { 25 | file.seekg(0, ios_base::end); 26 | streampos fileSize = file.tellg(); 27 | data->resize(fileSize); 28 | file.seekg(0, ios_base::beg); 29 | file.read(&(data->operator[](0)), fileSize); 30 | return true; 31 | } else { 32 | return false; 33 | } 34 | } 35 | bool writeFile(string path, string data) { 36 | ofstream file(path); 37 | if (!file.eof() && !file.fail()) { 38 | file << data; 39 | file.close(); 40 | return true; 41 | } else { 42 | return false; 43 | } 44 | } 45 | string getFileExt(string fname) { 46 | string::size_type idx = fname.rfind('.'); 47 | if(idx != string::npos) { 48 | string extension = fname.substr(idx + 1); 49 | return extension; 50 | } else { 51 | return ""; 52 | } 53 | } 54 | DisasmMode getDisasmMode(string infile) { 55 | // Convert file extension to disassembler mode 56 | string ext = getFileExt(infile); 57 | if (ext == "wasm") { 58 | return DisasmMode::Wasm; 59 | } else if (ext == "wast") { 60 | return DisasmMode::Wast; 61 | } else if (ext == "js") { 62 | return DisasmMode::AsmJs; 63 | } else { 64 | return DisasmMode::None; 65 | } 66 | } 67 | int printVersion() { 68 | cerr << "wasmdec v" << __WASMDEC_VERSION << endl; 69 | return 0; 70 | } 71 | void enableDebugging() { 72 | debugging = true; 73 | } 74 | void enableExtra() { 75 | extra = true; 76 | } 77 | void enableMemdump() { 78 | memdump = true; 79 | } 80 | void setOutfile(string _outf) { 81 | outfile = _outf; 82 | } 83 | void setInfile(string _inf) { 84 | infile = _inf; 85 | } 86 | int performMemdump() { 87 | // Initialize a decompiler for memory dumping 88 | dmode = getDisasmMode(infile); 89 | DisasmConfig conf(debugging, extra, dmode); 90 | std::vector* input = new std::vector(); 91 | if (!readFile(input, infile)) { 92 | std::cout << "ERROR: failed to read the input file!" << std::endl; 93 | return 1; 94 | } 95 | Decompiler decompiler(conf, input); 96 | 97 | // Dump the memory and table 98 | std::vector mem = decompiler.dumpMemory(); 99 | std::vector table = decompiler.dumpTable(); 100 | std::string memOutFile = outfile + ".mem", 101 | tableOutFile = outfile + ".table.bin", 102 | stringMemory = std::string(mem.begin(), mem.end()), 103 | stringTable = std::string(table.begin(), table.end()); 104 | if (!writeFile(memOutFile, stringMemory)) { 105 | std::cout << "ERROR: failed to write memory file '" << memOutFile << '"' << std::endl; 106 | return 1; 107 | } else if (!writeFile(tableOutFile, stringTable)) { 108 | std::cout << "ERROR: failed to write memory file '" << tableOutFile << '"' << std::endl; 109 | return 1; 110 | } 111 | return 0; 112 | } 113 | int decompile(Decompiler* decompiler) { 114 | decompiler->decompile(); 115 | if (decompiler->failed()) { 116 | std::cout << "ERROR: failed to decompile the binary." << std::endl; 117 | return 1; 118 | } 119 | string decompiledCode = decompiler->getEmittedCode(); 120 | if (!writeFile(outfile, decompiledCode)) { 121 | std::cout << "ERROR: failed to write the output file." << std::endl; 122 | return 1; 123 | } 124 | return 0; 125 | } 126 | int multiDecompile(void) { 127 | DisasmConfig conf(debugging, extra, DisasmMode::Wasm); 128 | MultiDecompiler m(infiles, conf); 129 | if (m.failed) { 130 | std::cout << "ERROR: MultiDecompiler failed to decompile input." << std::endl; 131 | return 1; 132 | } 133 | string out = m.getOutput(); 134 | if (!writeFile(outfile, out)) { 135 | std::cout << "ERROR: failed to write output file to disk!" << std::endl; 136 | return 1; 137 | } 138 | return 0; 139 | } 140 | int main(int argc, char* argv[]) { 141 | // Set up options 142 | cxxopts::Options opt("wasmdec", "WebAssembly to C decompiler"); 143 | opt.add_options() 144 | ("v,version", "Print wasmdec version") 145 | ("d,debug", "Enable debug output") 146 | ("m,memdump", "Dump memory instead of decompiling") 147 | ("e,extra", "Output extra information to decompiled binary") 148 | ("o,output", "Output C file", cxxopts::value(outfile)) 149 | ("positional", "Input file", cxxopts::value>()) 150 | ("h,help", "Print usage") 151 | ; 152 | opt.parse_positional({"positional"}); 153 | opt.positional_help("[input file]") 154 | .show_positional_help(); 155 | auto res = opt.parse(argc, argv); 156 | // Help and version, boring 157 | if (res.count("v")) { 158 | // version argument passed 159 | return printVersion(); 160 | } 161 | if (res.count("h")) { 162 | std::cout << opt.help({"", "Group"}) << std::endl; 163 | return 0; 164 | } 165 | // Set default output file if there is none 166 | if (!res.count("o")) { 167 | outfile = "out.c"; 168 | } 169 | // Parse decompiler flags 170 | if (res.count("d")) { 171 | enableDebugging(); 172 | } 173 | if (res.count("m")) { 174 | enableMemdump(); 175 | } 176 | 177 | if (res.count("e")) { 178 | enableExtra(); 179 | } 180 | // Parse input file(s) 181 | if (res.count("positional")) { 182 | std::vector _infiles; 183 | try { 184 | _infiles = res["positional"].as>(); 185 | } catch (std::exception& e) { 186 | std::cout << "ERROR: invalid input files!" << std::endl 187 | << std::endl << opt.help({"", "Group"}) << std::endl; 188 | return 1; 189 | } 190 | if (_infiles.size() > 1) { 191 | infile = ""; 192 | infiles = _infiles; 193 | } else { 194 | infile = _infiles.at(0); 195 | } 196 | } else { 197 | std::cout << "ERROR: no input file provided!" << std::endl 198 | << opt.help({"", "Group"}) << std::endl; 199 | return 1; 200 | } 201 | 202 | if (!memdump) { 203 | if (!infile.size()) { 204 | return multiDecompile(); 205 | } else { 206 | // there's only one infile, use a regular decompiler 207 | 208 | // Configure the decompiler 209 | dmode = getDisasmMode(infile); 210 | DisasmConfig conf(debugging, extra, dmode); 211 | std::vector* input = new std::vector(); 212 | if (!readFile(input, infile)) { 213 | std::cout << "ERROR: failed to read the input file!" << std::endl; 214 | return 1; 215 | } 216 | 217 | // Now that everything is parsed, initialize the decompiler 218 | Decompiler decompiler(conf, input); 219 | return decompile(&decompiler); 220 | } 221 | } else { 222 | return performMemdump(); 223 | } 224 | 225 | return 0; 226 | } -------------------------------------------------------------------------------- /test/wasm/switch.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $FUNCSIG$vii (func (param i32 i32))) 3 | (type $FUNCSIG$vi (func (param i32))) 4 | (type $FUNCSIG$iii (func (param i32 i32) (result i32))) 5 | (import "env" "memory" (memory $0 256)) 6 | (import "env" "table" (table 0 anyfunc)) 7 | (import "env" "memoryBase" (global $memoryBase i32)) 8 | (import "env" "tableBase" (global $tableBase i32)) 9 | (import "env" "DYNAMICTOP_PTR" (global $DYNAMICTOP_PTR$asm2wasm$import i32)) 10 | (import "env" "tempDoublePtr" (global $tempDoublePtr$asm2wasm$import i32)) 11 | (import "env" "ABORT" (global $ABORT$asm2wasm$import i32)) 12 | (import "global" "NaN" (global $nan$asm2wasm$import f64)) 13 | (import "global" "Infinity" (global $inf$asm2wasm$import f64)) 14 | (import "env" "abortStackOverflow" (func $abortStackOverflow (param i32))) 15 | (import "env" "_printf" (func $_printf (param i32 i32) (result i32))) 16 | (global $DYNAMICTOP_PTR (mut i32) (get_global $DYNAMICTOP_PTR$asm2wasm$import)) 17 | (global $tempDoublePtr (mut i32) (get_global $tempDoublePtr$asm2wasm$import)) 18 | (global $ABORT (mut i32) (get_global $ABORT$asm2wasm$import)) 19 | (global $STACKTOP (mut i32) (i32.const 0)) 20 | (global $STACK_MAX (mut i32) (i32.const 0)) 21 | (global $__THREW__ (mut i32) (i32.const 0)) 22 | (global $threwValue (mut i32) (i32.const 0)) 23 | (global $setjmpId (mut i32) (i32.const 0)) 24 | (global $undef (mut i32) (i32.const 0)) 25 | (global $nan (mut f64) (get_global $nan$asm2wasm$import)) 26 | (global $inf (mut f64) (get_global $inf$asm2wasm$import)) 27 | (global $tempInt (mut i32) (i32.const 0)) 28 | (global $tempBigInt (mut i32) (i32.const 0)) 29 | (global $tempBigIntS (mut i32) (i32.const 0)) 30 | (global $tempValue (mut i32) (i32.const 0)) 31 | (global $tempDouble (mut f64) (f64.const 0)) 32 | (global $tempRet0 (mut i32) (i32.const 0)) 33 | (global $tempFloat (mut f32) (f32.const 0)) 34 | (global $f0 (mut f32) (f32.const 0)) 35 | (data (get_global $memoryBase) "one\n\00two\n\00forty two!\n\00default\n") 36 | (export "__post_instantiate" (func $__post_instantiate)) 37 | (export "_main" (func $_main)) 38 | (export "runPostSets" (func $runPostSets)) 39 | (func $stackAlloc (; 2 ;) (param $size i32) (result i32) 40 | (local $ret i32) 41 | (set_local $ret 42 | (get_global $STACKTOP) 43 | ) 44 | (set_global $STACKTOP 45 | (i32.add 46 | (get_global $STACKTOP) 47 | (get_local $size) 48 | ) 49 | ) 50 | (set_global $STACKTOP 51 | (i32.and 52 | (i32.add 53 | (get_global $STACKTOP) 54 | (i32.const 15) 55 | ) 56 | (i32.const -16) 57 | ) 58 | ) 59 | (if 60 | (i32.ge_s 61 | (get_global $STACKTOP) 62 | (get_global $STACK_MAX) 63 | ) 64 | (call $abortStackOverflow 65 | (get_local $size) 66 | ) 67 | ) 68 | (return 69 | (get_local $ret) 70 | ) 71 | ) 72 | (func $stackSave (; 3 ;) (result i32) 73 | (return 74 | (get_global $STACKTOP) 75 | ) 76 | ) 77 | (func $stackRestore (; 4 ;) (param $top i32) 78 | (set_global $STACKTOP 79 | (get_local $top) 80 | ) 81 | ) 82 | (func $establishStackSpace (; 5 ;) (param $stackBase i32) (param $stackMax i32) 83 | (set_global $STACKTOP 84 | (get_local $stackBase) 85 | ) 86 | (set_global $STACK_MAX 87 | (get_local $stackMax) 88 | ) 89 | ) 90 | (func $setThrew (; 6 ;) (param $threw i32) (param $value i32) 91 | (if 92 | (i32.eq 93 | (get_global $__THREW__) 94 | (i32.const 0) 95 | ) 96 | (block 97 | (set_global $__THREW__ 98 | (get_local $threw) 99 | ) 100 | (set_global $threwValue 101 | (get_local $value) 102 | ) 103 | ) 104 | ) 105 | ) 106 | (func $_main (; 7 ;) (param $$0 i32) (param $$1 i32) (result i32) 107 | (local $$2 i32) 108 | (local $$3 i32) 109 | (local $$4 i32) 110 | (local $$5 i32) 111 | (local $$6 i32) 112 | (local $$vararg_buffer i32) 113 | (local $$vararg_buffer1 i32) 114 | (local $$vararg_buffer3 i32) 115 | (local $$vararg_buffer5 i32) 116 | (local $label i32) 117 | (local $sp i32) 118 | (set_local $sp 119 | (get_global $STACKTOP) 120 | ) 121 | (set_global $STACKTOP 122 | (i32.add 123 | (get_global $STACKTOP) 124 | (i32.const 48) 125 | ) 126 | ) 127 | (if 128 | (i32.ge_s 129 | (get_global $STACKTOP) 130 | (get_global $STACK_MAX) 131 | ) 132 | (call $abortStackOverflow 133 | (i32.const 48) 134 | ) 135 | ) 136 | (set_local $$vararg_buffer5 137 | (i32.add 138 | (get_local $sp) 139 | (i32.const 24) 140 | ) 141 | ) 142 | (set_local $$vararg_buffer3 143 | (i32.add 144 | (get_local $sp) 145 | (i32.const 16) 146 | ) 147 | ) 148 | (set_local $$vararg_buffer1 149 | (i32.add 150 | (get_local $sp) 151 | (i32.const 8) 152 | ) 153 | ) 154 | (set_local $$vararg_buffer 155 | (get_local $sp) 156 | ) 157 | (set_local $$2 158 | (i32.const 0) 159 | ) 160 | (set_local $$3 161 | (get_local $$0) 162 | ) 163 | (set_local $$4 164 | (get_local $$1) 165 | ) 166 | ;;@ switch.c:9:0 167 | (set_local $$5 168 | (i32.const 42) 169 | ) 170 | ;;@ switch.c:10:0 171 | (set_local $$6 172 | (get_local $$5) 173 | ) 174 | (block $switch 175 | (block $switch-default 176 | (block $switch-case1 177 | (block $switch-case0 178 | (block $switch-case 179 | (br_table $switch-case $switch-case0 $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-default $switch-case1 $switch-default 180 | (i32.sub 181 | (get_local $$6) 182 | (i32.const 1) 183 | ) 184 | ) 185 | ) 186 | (block 187 | ;;@ switch.c:12:0 188 | (drop 189 | (call $_printf 190 | (i32.add 191 | (get_global $memoryBase) 192 | (i32.const 0) 193 | ) 194 | (get_local $$vararg_buffer) 195 | ) 196 | ) 197 | (br $switch) 198 | ) 199 | ) 200 | (block 201 | ;;@ switch.c:15:0 202 | (drop 203 | (call $_printf 204 | (i32.add 205 | (get_global $memoryBase) 206 | (i32.const 5) 207 | ) 208 | (get_local $$vararg_buffer1) 209 | ) 210 | ) 211 | (br $switch) 212 | ) 213 | ) 214 | (block 215 | ;;@ switch.c:18:0 216 | (drop 217 | (call $_printf 218 | (i32.add 219 | (get_global $memoryBase) 220 | (i32.const 10) 221 | ) 222 | (get_local $$vararg_buffer3) 223 | ) 224 | ) 225 | (br $switch) 226 | ) 227 | ) 228 | ;;@ switch.c:21:0 229 | (drop 230 | (call $_printf 231 | (i32.add 232 | (get_global $memoryBase) 233 | (i32.const 22) 234 | ) 235 | (get_local $$vararg_buffer5) 236 | ) 237 | ) 238 | ) 239 | (set_global $STACKTOP 240 | (get_local $sp) 241 | ) 242 | ;;@ switch.c:24:0 243 | (return 244 | (i32.const 0) 245 | ) 246 | ) 247 | (func $runPostSets (; 8 ;) 248 | (local $temp i32) 249 | (nop) 250 | ) 251 | (func $__post_instantiate (; 9 ;) 252 | (set_global $STACKTOP 253 | (i32.add 254 | (get_global $memoryBase) 255 | (i32.const 32) 256 | ) 257 | ) 258 | (set_global $STACK_MAX 259 | (i32.add 260 | (get_global $STACKTOP) 261 | (i32.const 5242880) 262 | ) 263 | ) 264 | (call $runPostSets) 265 | ) 266 | ) 267 | -------------------------------------------------------------------------------- /test/wasm/emcc.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $FUNCSIG$vii (func (param i32 i32))) 3 | (type $FUNCSIG$vi (func (param i32))) 4 | (type $FUNCSIG$iii (func (param i32 i32) (result i32))) 5 | (import "env" "memory" (memory $0 256)) 6 | (import "env" "table" (table 4 anyfunc)) 7 | (import "env" "memoryBase" (global $memoryBase i32)) 8 | (import "env" "tableBase" (global $tableBase i32)) 9 | (import "env" "DYNAMICTOP_PTR" (global $DYNAMICTOP_PTR$asm2wasm$import i32)) 10 | (import "env" "tempDoublePtr" (global $tempDoublePtr$asm2wasm$import i32)) 11 | (import "global" "NaN" (global $nan$asm2wasm$import f64)) 12 | (import "global" "Infinity" (global $inf$asm2wasm$import f64)) 13 | (import "env" "abortStackOverflow" (func $abortStackOverflow (param i32))) 14 | (import "env" "nullFunc_X" (func $nullFunc_X (param i32))) 15 | (import "env" "_printf" (func $_printf (param i32 i32) (result i32))) 16 | (global $DYNAMICTOP_PTR (mut i32) (get_global $DYNAMICTOP_PTR$asm2wasm$import)) 17 | (global $tempDoublePtr (mut i32) (get_global $tempDoublePtr$asm2wasm$import)) 18 | (global $STACKTOP (mut i32) (i32.const 0)) 19 | (global $STACK_MAX (mut i32) (i32.const 0)) 20 | (global $__THREW__ (mut i32) (i32.const 0)) 21 | (global $threwValue (mut i32) (i32.const 0)) 22 | (global $setjmpId (mut i32) (i32.const 0)) 23 | (global $undef (mut i32) (i32.const 0)) 24 | (global $nan (mut f64) (get_global $nan$asm2wasm$import)) 25 | (global $inf (mut f64) (get_global $inf$asm2wasm$import)) 26 | (global $tempInt (mut i32) (i32.const 0)) 27 | (global $tempBigInt (mut i32) (i32.const 0)) 28 | (global $tempBigIntS (mut i32) (i32.const 0)) 29 | (global $tempValue (mut i32) (i32.const 0)) 30 | (global $tempDouble (mut f64) (f64.const 0)) 31 | (global $tempRet0 (mut i32) (i32.const 0)) 32 | (global $tempFloat (mut f32) (f32.const 0)) 33 | (global $f0 (mut f32) (f32.const 0)) 34 | (elem (get_global $tableBase) $b0 $_doubleValue $_main $b0) 35 | (data (get_global $memoryBase) "%s\n\00Hello, wasmdec!\00secondVal: %d\n") 36 | (export "__post_instantiate" (func $__post_instantiate)) 37 | (export "_doubleValue" (func $_doubleValue)) 38 | (export "_main" (func $_main)) 39 | (export "runPostSets" (func $runPostSets)) 40 | (func $stackAlloc (; 3 ;) (param $size i32) (result i32) 41 | (local $ret i32) 42 | (set_local $ret 43 | (get_global $STACKTOP) 44 | ) 45 | (set_global $STACKTOP 46 | (i32.add 47 | (get_global $STACKTOP) 48 | (get_local $size) 49 | ) 50 | ) 51 | (set_global $STACKTOP 52 | (i32.and 53 | (i32.add 54 | (get_global $STACKTOP) 55 | (i32.const 15) 56 | ) 57 | (i32.const -16) 58 | ) 59 | ) 60 | (if 61 | (i32.ge_s 62 | (get_global $STACKTOP) 63 | (get_global $STACK_MAX) 64 | ) 65 | (call $abortStackOverflow 66 | (get_local $size) 67 | ) 68 | ) 69 | (return 70 | (get_local $ret) 71 | ) 72 | ) 73 | (func $stackSave (; 4 ;) (result i32) 74 | (return 75 | (get_global $STACKTOP) 76 | ) 77 | ) 78 | (func $stackRestore (; 5 ;) (param $top i32) 79 | (set_global $STACKTOP 80 | (get_local $top) 81 | ) 82 | ) 83 | (func $establishStackSpace (; 6 ;) (param $stackBase i32) (param $stackMax i32) 84 | (set_global $STACKTOP 85 | (get_local $stackBase) 86 | ) 87 | (set_global $STACK_MAX 88 | (get_local $stackMax) 89 | ) 90 | ) 91 | (func $setThrew (; 7 ;) (param $threw i32) (param $value i32) 92 | (if 93 | (i32.eq 94 | (get_global $__THREW__) 95 | (i32.const 0) 96 | ) 97 | (block 98 | (set_global $__THREW__ 99 | (get_local $threw) 100 | ) 101 | (set_global $threwValue 102 | (get_local $value) 103 | ) 104 | ) 105 | ) 106 | ) 107 | (func $_doubleValue (; 8 ;) (param $$0 i32) (result i32) 108 | (local $$1 i32) 109 | (local $$2 i32) 110 | (local $$3 i32) 111 | (local $label i32) 112 | (local $sp i32) 113 | (set_local $sp 114 | (get_global $STACKTOP) 115 | ) 116 | (set_global $STACKTOP 117 | (i32.add 118 | (get_global $STACKTOP) 119 | (i32.const 16) 120 | ) 121 | ) 122 | (if 123 | (i32.ge_s 124 | (get_global $STACKTOP) 125 | (get_global $STACK_MAX) 126 | ) 127 | (call $abortStackOverflow 128 | (i32.const 16) 129 | ) 130 | ) 131 | (set_local $$1 132 | (get_local $$0) 133 | ) 134 | ;;@ main.c:4:0 135 | (set_local $$2 136 | (get_local $$1) 137 | ) 138 | (set_local $$3 139 | (i32.shl 140 | (get_local $$2) 141 | (i32.const 1) 142 | ) 143 | ) 144 | (set_global $STACKTOP 145 | (get_local $sp) 146 | ) 147 | (return 148 | (get_local $$3) 149 | ) 150 | ) 151 | (func $_main (; 9 ;) (result i32) 152 | (local $$0 i32) 153 | (local $$1 i32) 154 | (local $$2 i32) 155 | (local $$3 i32) 156 | (local $$4 i32) 157 | (local $$5 i32) 158 | (local $$6 i32) 159 | (local $$7 i32) 160 | (local $$8 i32) 161 | (local $$9 i32) 162 | (local $$vararg_buffer i32) 163 | (local $$vararg_buffer1 i32) 164 | (local $label i32) 165 | (local $sp i32) 166 | (set_local $sp 167 | (get_global $STACKTOP) 168 | ) 169 | (set_global $STACKTOP 170 | (i32.add 171 | (get_global $STACKTOP) 172 | (i32.const 32) 173 | ) 174 | ) 175 | (if 176 | (i32.ge_s 177 | (get_global $STACKTOP) 178 | (get_global $STACK_MAX) 179 | ) 180 | (call $abortStackOverflow 181 | (i32.const 32) 182 | ) 183 | ) 184 | (set_local $$vararg_buffer1 185 | (i32.add 186 | (get_local $sp) 187 | (i32.const 8) 188 | ) 189 | ) 190 | (set_local $$vararg_buffer 191 | (get_local $sp) 192 | ) 193 | (set_local $$0 194 | (i32.const 0) 195 | ) 196 | (loop $while-in 197 | (block $while-out 198 | ;;@ main.c:10:0 199 | (set_local $$3 200 | (get_local $$1) 201 | ) 202 | (set_local $$4 203 | (i32.add 204 | (get_local $$3) 205 | (i32.const 1) 206 | ) 207 | ) 208 | (set_local $$1 209 | (get_local $$4) 210 | ) 211 | ;;@ main.c:11:0 212 | (set_local $$5 213 | (get_local $$1) 214 | ) 215 | (set_local $$6 216 | (i32.lt_s 217 | (get_local $$5) 218 | (i32.const 10) 219 | ) 220 | ) 221 | (if 222 | (get_local $$6) 223 | (br $while-out) 224 | ) 225 | (br $while-in) 226 | ) 227 | ) 228 | ;;@ main.c:15:0 229 | (i32.store 230 | (get_local $$vararg_buffer) 231 | (i32.add 232 | (get_global $memoryBase) 233 | (i32.const 4) 234 | ) 235 | ) 236 | (drop 237 | (call $_printf 238 | (i32.add 239 | (get_global $memoryBase) 240 | (i32.const 0) 241 | ) 242 | (get_local $$vararg_buffer) 243 | ) 244 | ) 245 | ;;@ main.c:17:0 246 | (set_local $$1 247 | (i32.const 6) 248 | ) 249 | ;;@ main.c:19:0 250 | (set_local $$7 251 | (get_local $$1) 252 | ) 253 | (set_local $$8 254 | (call $_doubleValue 255 | (get_local $$7) 256 | ) 257 | ) 258 | (set_local $$2 259 | (get_local $$8) 260 | ) 261 | ;;@ main.c:20:0 262 | (set_local $$9 263 | (get_local $$2) 264 | ) 265 | (i32.store 266 | (get_local $$vararg_buffer1) 267 | (get_local $$9) 268 | ) 269 | (drop 270 | (call $_printf 271 | (i32.add 272 | (get_global $memoryBase) 273 | (i32.const 20) 274 | ) 275 | (get_local $$vararg_buffer1) 276 | ) 277 | ) 278 | (set_global $STACKTOP 279 | (get_local $sp) 280 | ) 281 | ;;@ main.c:21:0 282 | (return 283 | (i32.const 0) 284 | ) 285 | ) 286 | (func $runPostSets (; 10 ;) 287 | (local $temp i32) 288 | (nop) 289 | ) 290 | (func $__post_instantiate (; 11 ;) 291 | (set_global $STACKTOP 292 | (i32.add 293 | (get_global $memoryBase) 294 | (i32.const 48) 295 | ) 296 | ) 297 | (set_global $STACK_MAX 298 | (i32.add 299 | (get_global $STACKTOP) 300 | (i32.const 5242880) 301 | ) 302 | ) 303 | (call $runPostSets) 304 | ) 305 | (func $b0 (; 12 ;) (result f64) 306 | (call $nullFunc_X 307 | (i32.const 0) 308 | ) 309 | (return 310 | (f64.const 0) 311 | ) 312 | ) 313 | ) 314 | -------------------------------------------------------------------------------- /test/disasm/emcc.c: -------------------------------------------------------------------------------- 1 | // Decompiled Web Assembly generated by wasmdec. Preamble: 2 | #include // For the bit size specific types 3 | #include // For certian WASM operations 4 | typedef const char* wasm_table_t; // WASM tables 5 | // Bit size specific types not declared in stdint.h: 6 | typedef float float32_t; 7 | typedef double float64_t; 8 | // C implementation of WASM expressions: 9 | unsigned int _rotl(const unsigned int value, int shift) { 10 | if ((shift &= sizeof(value) * 8 - 1) == 0) 11 | return value; 12 | return (value << shift) | (value >> (sizeof(value)*8 - shift)); 13 | } 14 | unsigned int _rotr(const unsigned int value, int shift) { 15 | if ((shift &= sizeof(value) * 8 - 1) == 0) 16 | return value; 17 | return (value >> shift) | (value << (sizeof(value)*8 - shift)); 18 | } 19 | #define MAX(a,b) ((a) > (b) ? a : b) 20 | #define MIN(a,b) ((a) < (b) ? a : b) 21 | // Host functions: used to request information from host machine. 22 | extern int32_t host_has_feature(int32_t feature_opcode); 23 | extern void host_grow_memory(int32_t size); 24 | extern int32_t host_get_current_memory(void); 25 | extern int32_t host_get_page_size(void); 26 | // End of preamble 27 | 28 | // WASM imports: 29 | /* 30 | Import 'import$0': 31 | Module: 'env' 32 | Base: 'DYNAMICTOP_PTR' 33 | */ 34 | extern int import$0; 35 | /* 36 | Import 'import$1': 37 | Module: 'env' 38 | Base: 'tempDoublePtr' 39 | */ 40 | extern int import$1; 41 | /* 42 | Import 'import$2': 43 | Module: 'env' 44 | Base: 'ABORT' 45 | */ 46 | extern int import$2; 47 | /* 48 | Import 'import$3': 49 | Module: 'env' 50 | Base: 'memoryBase' 51 | */ 52 | extern int import$3; 53 | /* 54 | Import 'import$4': 55 | Module: 'env' 56 | Base: 'tableBase' 57 | */ 58 | extern int import$4; 59 | /* 60 | Import 'import$5': 61 | Module: 'global' 62 | Base: 'NaN' 63 | */ 64 | extern double import$5; 65 | /* 66 | Import 'import$6': 67 | Module: 'global' 68 | Base: 'Infinity' 69 | */ 70 | extern double import$6; 71 | /* 72 | Import 'import$7': 73 | Module: 'env' 74 | Base: 'abortStackOverflow' 75 | */ 76 | extern void import$7(int local_0) 77 | /* 78 | Import 'import$8': 79 | Module: 'env' 80 | Base: '_printf' 81 | */ 82 | extern int import$8(int local_0, int local_1) 83 | /* 84 | Import 'import$9': 85 | Module: 'env' 86 | Base: 'memory' 87 | */ 88 | extern const char* import$9; // 89 | /* 90 | Import 'import$10': 91 | Module: 'env' 92 | Base: 'table' 93 | */ 94 | extern wasm_table_t import$10; // 95 | // WASM globals: 96 | int global$0 = import$0; 97 | int global$1 = import$1; 98 | int global$2 = import$2; 99 | int global$3 = 0; 100 | int global$4 = 0; 101 | int global$5 = 0; 102 | int global$6 = 0; 103 | int global$7 = 0; 104 | int global$8 = 0; 105 | double global$9 = import$5; 106 | double global$10 = import$6; 107 | int global$11 = 0; 108 | int global$12 = 0; 109 | int global$13 = 0; 110 | int global$14 = 0; 111 | double global$15 = 0.000000; 112 | int global$16 = 0; 113 | float global$17 = 0.000000; 114 | float global$18 = 0.000000; 115 | 116 | // WASM functions: 117 | 118 | /* 119 | Function '$stackAlloc' 120 | Local variables: 1 121 | Parameters: 1 122 | */ 123 | int fn_stackAlloc(int local_0) { 124 | // Parsed WASM function locals: 125 | int local_1 = 0; // Local with index '1' 126 | 127 | local_1 = global$3; 128 | global$3 = global$3 + local_0; 129 | global$3 = global$3 + 15 && -16; 130 | if (global$3 >= global$4) { 131 | import$7(local_0); 132 | 133 | } // 134 | return local_1; 135 | } 136 | /* 137 | Function '$stackSave' 138 | Local variables: 0 139 | Parameters: 0 140 | */ 141 | int fn_stackSave() { 142 | // Function contains no non-parameter locals 143 | return global$3; 144 | } 145 | /* 146 | Function '$stackRestore' 147 | Local variables: 0 148 | Parameters: 1 149 | */ 150 | void fn_stackRestore(int local_0) { 151 | // Function contains no non-parameter locals 152 | global$3 = local_0; 153 | } 154 | /* 155 | Function '$establishStackSpace' 156 | Local variables: 0 157 | Parameters: 2 158 | */ 159 | void fn_establishStackSpace(int local_0, int local_1) { 160 | // Function contains no non-parameter locals 161 | global$3 = local_0; 162 | global$4 = local_1; 163 | } 164 | /* 165 | Function '$setThrew' 166 | Local variables: 0 167 | Parameters: 2 168 | */ 169 | void fn_setThrew(int local_0, int local_1) { 170 | // Function contains no non-parameter locals 171 | if (global$5 == 0) { 172 | global$5 = local_0; 173 | global$6 = local_1; 174 | 175 | } // 176 | } 177 | /* 178 | Function '$_doubleValue' 179 | Local variables: 5 180 | Parameters: 1 181 | */ 182 | int fn__doubleValue(int local_0) { 183 | // Parsed WASM function locals: 184 | int local_1 = 0; // Local with index '1' 185 | int local_2 = 0; // Local with index '2' 186 | int local_3 = 0; // Local with index '3' 187 | int local_4 = 0; // Local with index '4' 188 | int local_5 = 0; // Local with index '5' 189 | 190 | local_5 = global$3; 191 | global$3 = global$3 + 16; 192 | if (global$3 >= global$4) { 193 | import$7(16); 194 | 195 | } // 196 | local_1 = local_0; 197 | local_2 = local_1; 198 | local_3 = local_2 << 1; 199 | global$3 = local_5; 200 | return local_3; 201 | } 202 | /* 203 | Function '$_main' 204 | Local variables: 14 205 | Parameters: 0 206 | */ 207 | int fn__main() { 208 | // Parsed WASM function locals: 209 | int local_0 = 0; // Local with index '0' 210 | int local_1 = 0; // Local with index '1' 211 | int local_2 = 0; // Local with index '2' 212 | int local_3 = 0; // Local with index '3' 213 | int local_4 = 0; // Local with index '4' 214 | int local_5 = 0; // Local with index '5' 215 | int local_6 = 0; // Local with index '6' 216 | int local_7 = 0; // Local with index '7' 217 | int local_8 = 0; // Local with index '8' 218 | int local_9 = 0; // Local with index '9' 219 | int local_10 = 0; // Local with index '10' 220 | int local_11 = 0; // Local with index '11' 221 | int local_12 = 0; // Local with index '12' 222 | int local_13 = 0; // Local with index '13' 223 | 224 | local_13 = global$3; 225 | global$3 = global$3 + 32; 226 | if (global$3 >= global$4) { 227 | import$7(32); 228 | 229 | } // 230 | local_11 = local_13 + 8; 231 | local_10 = local_13; 232 | local_0 = 0; 233 | while (1) { // Loop name: 'label$3' 234 | local_3 = local_1; 235 | local_4 = local_3 + 1; 236 | local_1 = local_4; 237 | local_5 = local_1; 238 | local_6 = local_5 < 10; 239 | if (local_6) { 240 | break; 241 | } // 242 | break; 243 | } // End of loop 'label$3' 244 | /* Store: 245 | Offset: 0x00000000 246 | Align: 0x00000004 247 | Bytes: 0x00000004 248 | Atomic: false */ 249 | local_10 = import$3 + 4; 250 | /* Drop routine */ 251 | import$8(import$3 + 0, local_10); 252 | /* End of drop routine */ 253 | local_1 = 6; 254 | local_7 = local_1; 255 | local_8 = fn__doubleValue(local_7); 256 | ; 257 | local_2 = local_8; 258 | local_9 = local_2; 259 | /* Store: 260 | Offset: 0x00000000 261 | Align: 0x00000004 262 | Bytes: 0x00000004 263 | Atomic: false */ 264 | local_11 = local_9; 265 | /* Drop routine */ 266 | import$8(import$3 + 20, local_11); 267 | /* End of drop routine */ 268 | global$3 = local_13; 269 | return 0; 270 | } 271 | /* 272 | Function '$runPostSets' 273 | Local variables: 1 274 | Parameters: 0 275 | */ 276 | void fn_runPostSets() { 277 | // Parsed WASM function locals: 278 | int local_0 = 0; // Local with index '0' 279 | 280 | // 281 | } 282 | /* 283 | Function '$__post_instantiate' 284 | Local variables: 0 285 | Parameters: 0 286 | */ 287 | void fn___post_instantiate() { 288 | // Function contains no non-parameter locals 289 | global$3 = import$3 + 48; 290 | global$4 = global$3 + 5242880; 291 | fn_runPostSets(); 292 | } 293 | 294 | /* 295 | Exported WASM functions: 296 | Function 'fn__main': 297 | WASM name: '_main' 298 | Export name: '_main' 299 | 300 | Function 'fn___post_instantiate': 301 | WASM name: '__post_instantiate' 302 | Export name: '__post_instantiate' 303 | 304 | Function 'fn_runPostSets': 305 | WASM name: 'runPostSets' 306 | Export name: 'runPostSets' 307 | 308 | Function 'fn__doubleValue': 309 | WASM name: '_doubleValue' 310 | Export name: '_doubleValue' 311 | 312 | */ 313 | -------------------------------------------------------------------------------- /src/decompiler/Decompiler.cc: -------------------------------------------------------------------------------- 1 | #include "Decompiler.h" 2 | 3 | #ifdef ASM_JS_DECOMP 4 | 5 | #ifndef wasm_asm2wasm_h 6 | #include "asm2wasm.h" 7 | #endif 8 | 9 | #endif 10 | 11 | Decompiler::Decompiler(DisasmConfig conf, vector* inbin) 12 | : binary((*inbin)) { 13 | if (conf.includePreamble) { 14 | emit.preamble(); 15 | } 16 | functionPreface = conf.fnPreface; 17 | rawMemory = vector(); 18 | rawTable = vector(); 19 | isDebug = conf.debug; 20 | emitExtraData = conf.extra; 21 | mode = conf.mode; 22 | 23 | if (mode == DisasmMode::Wasm) { 24 | debug("Creating WasmBinaryBuilder\n"); 25 | // Create parser 26 | wasm::WasmBinaryBuilder parser(module, binary, conf.debug); 27 | debug("Parsing wasm binary...\n"); 28 | // Attempt to parse binary via Binaryen's AST parser 29 | try { 30 | parser.read(); 31 | parserFailed = false; 32 | } catch (exception& err) { 33 | cerr << "wasmdec: FAILED to parse wasm binary: " << endl; 34 | cerr << err.what() << endl; 35 | cerr << endl; 36 | fail(); 37 | return; 38 | } 39 | } else if (mode == DisasmMode::Wast) { 40 | try { 41 | debug("Starting SExpressionParser\n"); 42 | string binary_s(binary.begin(), binary.end()); 43 | SExpressionParser parser(const_cast(binary_s.c_str())); 44 | Element& _root = *(parser.root); 45 | debug("Starting SExpressionWasmBuilder\n"); 46 | SExpressionWasmBuilder sbuilder(module, *_root[0]); 47 | parserFailed = false; 48 | } catch (wasm::ParseException& err) { 49 | cerr << "wasmdec: FAILED to parse wast: " << endl; 50 | err.dump(cerr); 51 | cerr << endl; 52 | fail(); 53 | return; 54 | } 55 | } 56 | 57 | #ifdef ASM_JS_DECOMP 58 | 59 | else if (mode == DisasmMode::AsmJs) { 60 | // preprocess 61 | debug("Preprocessing asm.js\n"); 62 | Asm2WasmPreProcessor a2wp; 63 | string binary_s(binary.begin(), binary.end()); 64 | char* begin = a2wp.process(const_cast(binary_s.c_str())); 65 | 66 | // parse 67 | debug("Initializing parser\n"); 68 | cashew::Parser parser_builder; 69 | Ref js = parser_builder.parseToplevel(begin); 70 | 71 | // compile to wasm 72 | debug("Compiling to wasm\n"); 73 | PassOptions popts; 74 | popts.debug = isDebug; 75 | Asm2WasmBuilder a2w(module, 76 | a2wp, 77 | isDebug, 78 | TrapMode::JS, 79 | popts, 80 | true, true, false); 81 | a2w.processAsm(js); 82 | debug("Compilation finished"); 83 | } 84 | 85 | #endif 86 | 87 | debug("Parsed bin successfully.\n"); 88 | dctx = new DecompilerCtx(); 89 | } 90 | void Decompiler::fail() { 91 | debug("Decompiler::fail() called!\n"); 92 | parserFailed = true; 93 | } 94 | Decompiler::Decompiler(DisasmConfig conf, vector inbin) 95 | : binary(inbin) { 96 | if (conf.includePreamble) { 97 | emit.preamble(); 98 | } 99 | functionPreface = conf.fnPreface; 100 | rawMemory = vector(); 101 | rawTable = vector(); 102 | isDebug = conf.debug; 103 | emitExtraData = conf.extra; 104 | mode = conf.mode; 105 | if (mode == DisasmMode::Wasm) { 106 | debug("Creating WasmBinaryBuilder\n"); 107 | // Create parser 108 | wasm::WasmBinaryBuilder parser(module, binary, conf.debug); 109 | debug("Parsing wasm binary...\n"); 110 | // Attempt to parse binary via Binaryen's AST parser 111 | try { 112 | parser.read(); 113 | parserFailed = false; 114 | } catch (exception& err) { 115 | cerr << "wasmdec: FAILED to parse wasm binary: " << endl; 116 | cerr << err.what() << endl; 117 | cerr << endl; 118 | fail(); 119 | return; 120 | } 121 | } else if (mode == DisasmMode::Wast) { 122 | try { 123 | debug("Starting SExpressionParser\n"); 124 | string binary_s(binary.begin(), binary.end()); 125 | SExpressionParser parser(const_cast(binary_s.c_str())); 126 | Element& _root = *(parser.root); 127 | debug("Starting SExpressionWasmBuilder\n"); 128 | SExpressionWasmBuilder sbuilder(module, *_root[0]); 129 | parserFailed = false; 130 | } catch (wasm::ParseException& err) { 131 | cerr << "wasmdec: FAILED to parse wast: " << endl; 132 | err.dump(cerr); 133 | cerr << endl; 134 | fail(); 135 | return; 136 | } 137 | } 138 | 139 | #ifdef ASM_JS_DECOMP 140 | 141 | else if (mode == DisasmMode::AsmJs) { 142 | // preprocess 143 | debug("Preprocessing asm.js\n"); 144 | Asm2WasmPreProcessor a2wp; 145 | string binary_s(binary.begin(), binary.end()); 146 | char* begin = a2wp.process(const_cast(binary_s.c_str())); 147 | 148 | // parse 149 | debug("Initializing parser\n"); 150 | cashew::Parser parser_builder; 151 | Ref js = parser_builder.parseToplevel(begin); 152 | 153 | // compile to wasm 154 | debug("Compiling to wasm\n"); 155 | PassOptions popts; 156 | popts.debug = isDebug; 157 | Asm2WasmBuilder a2w(module, 158 | a2wp, 159 | isDebug, 160 | TrapMode::JS, 161 | popts, 162 | true, true, false); 163 | a2w.processAsm(js); 164 | debug("Compilation finished"); 165 | } 166 | 167 | #endif 168 | 169 | debug("Parsed bin successfully.\n"); 170 | dctx = new DecompilerCtx(); 171 | } 172 | void Decompiler::decompile() { 173 | if (parserFailed) { 174 | return; 175 | } 176 | debug("Starting code generation...\n"); 177 | // Process globals 178 | if (module.globals.size()) { 179 | debug("Processing globals...\n"); 180 | Context gctx = Context(&module); // Initialize a global context to parse expressions with 181 | gctx.isGlobal = true; 182 | emit.comment("WASM globals:"); 183 | for (auto& glb : module.globals) { 184 | bool isImported = glb->imported(); 185 | string globalType = Convert::resolveType(glb->type); 186 | if (!isImported) { 187 | string globalInitializer = Convert::parseExpr(&gctx, glb->init); 188 | if (!glb->mutable_) { // Non-mutable global is represented by const 189 | emit << "const "; 190 | } 191 | emit << globalType << " " << glb->name.str << " = " << globalInitializer << ";" << endl; 192 | } else { 193 | emit << "extern " 194 | << globalType 195 | << " " 196 | << glb->name.str 197 | << "; /* import */" 198 | << endl; 199 | } 200 | } 201 | debug("Processed globals.\n"); 202 | } else { 203 | debug("No wasm globals.\n"); 204 | /* 205 | // No globals, so just leave a comment 206 | emit.comment("No WASM globals."); 207 | */ 208 | } 209 | emit.ln(); 210 | // Process functions 211 | if (module.functions.size()) { 212 | debug("Processing wasm functions...\n"); 213 | /* 214 | emit.comment("WASM functions:"); 215 | emit.ln(); 216 | */ 217 | int funcNumber = 0; 218 | for (const auto &func : module.functions) { 219 | Function* fn = func.get(); 220 | if (fn->imported()) { 221 | debug("Processing function (import) #" + to_string(funcNumber) + "\n"); 222 | debug(" (name: '" + string(fn->name.str) + "')\n"); 223 | string signature = Convert::getDecl(fn, functionPreface); 224 | emit << "extern " 225 | << signature 226 | << "; /* import */" 227 | << endl; 228 | } else { 229 | debug("Processing function #" + to_string(funcNumber) + "\n"); 230 | debug(" (name: '" + string(fn->name.str) + "')\n"); 231 | funcNumber++; 232 | if (emitExtraData) { 233 | // Emit information about the function as a comment 234 | emit << "/*" << endl 235 | << "\tFunction '" << fn->name << "'" << endl 236 | << "\tLocal variables: " << fn->vars.size() << endl 237 | << "\tParameters: " << fn->params.size() << endl 238 | << "*/" << endl; 239 | } 240 | Context ctx = Context(fn, &module, dctx); 241 | ctx.functionLevelExpression = true; 242 | emit << Convert::getDecl(fn, functionPreface) << Convert::getFuncBody(ctx, emitExtraData) << endl; 243 | } 244 | } 245 | } else { 246 | emit.comment("No WASM functions."); 247 | emit.ln(); 248 | } 249 | // Process exports 250 | if (module.exports.size()) { 251 | debug("Processing wasm exports...\n"); 252 | if (emitExtraData) { 253 | emit << "\n/*" << endl 254 | << "\tExported WASM functions:" << endl; 255 | for (const auto& expt : module.exports) { 256 | // Stringify export to comment 257 | emit << "\tFunction '" << Convert::getFName(expt->value) << "':" << endl 258 | << "\t\tWASM name: '" << expt->value.str << "'" << endl 259 | << "\t\tExport name: '" << expt->name.str << "'" << endl << endl; 260 | } 261 | emit << "*/" << endl; 262 | } 263 | debug("Processed wasm exports.\n"); 264 | } else { 265 | emit.comment("No WASM exports."); 266 | emit.ln(); 267 | } 268 | debug("Code generation complete.\n"); 269 | vector().swap(binary); 270 | } 271 | string Decompiler::getEmittedCode() { 272 | debug("Exporting emitted code.\n"); 273 | return emit.getCode(); 274 | } 275 | // Debug functions 276 | void Decompiler::debug(string msg) { 277 | if (isDebug) { 278 | cerr << "wasmdec CodeGen: " << msg; 279 | } 280 | } 281 | void Decompiler::debugf(string msg) { 282 | if (isDebug) { 283 | cerr << msg; 284 | } 285 | } 286 | bool Decompiler::failed() { 287 | // TODO : Develop this function to support other code generation failures 288 | return parserFailed; 289 | } 290 | vector Decompiler::dumpMemory() { 291 | if (module.memory.exists && module.memory.imported()) { 292 | for (const auto &seg : module.memory.segments) { 293 | // Push each raw byte from each segment into raw memory vector 294 | for (const char byte : seg.data) { 295 | rawMemory.push_back(byte); 296 | } 297 | } 298 | return rawMemory; 299 | } else { 300 | return vector(); 301 | } 302 | } 303 | vector Decompiler::dumpTable() { 304 | if (module.table.exists && module.table.imported()) { 305 | for (const auto &seg : module.table.segments) { 306 | for (const auto &name : seg.data) { 307 | const char* _data = name.c_str(); 308 | char* i = (char*)_data; 309 | while (i[0] != '\0') { 310 | rawTable.push_back((char)i[0]); 311 | ++i; 312 | } 313 | } 314 | } 315 | return rawTable; 316 | } else { 317 | return vector(); 318 | } 319 | } -------------------------------------------------------------------------------- /src/convert/Conversion.cc: -------------------------------------------------------------------------------- 1 | #include "Conversion.h" 2 | 3 | string wasmdec::Convert::parseExpr(Context* ctx, wasm::Expression* e) { 4 | string ret; 5 | ret = wasmdec::parsers::expression(ctx, e); 6 | return ret; 7 | } 8 | string wasmdec::Convert::getFName(wasm::Name name) { 9 | // Convert WASM names to C function names 10 | // an 'f' is prepended because webassembly function names can be numbers 11 | return "f" + string(name.str); 12 | } 13 | string wasmdec::Convert::getLocal(wasm::Index argIdx) { 14 | // Convert WASM function locals to C variable names 15 | return "local" + to_string((int)argIdx); 16 | } 17 | string wasmdec::Convert::voidCall(wasm::Function* fn) { 18 | // Call a void function 19 | return getFName(fn->name) + "();"; 20 | } 21 | string wasmdec::Convert::getBinOperator(string e1, wasm::BinaryOp bop, string e2) { // TODO : Add more binary operations 22 | // Convert WASM binary operations to their respective C representation 23 | string op; 24 | switch (bop) { 25 | case AddInt32: 26 | case AddInt64: 27 | case AddFloat32: 28 | case AddFloat64: 29 | op = "+"; 30 | return e1 + " " + op + " " + e2; 31 | break; 32 | case SubInt32: 33 | case SubInt64: 34 | case SubFloat32: 35 | case SubFloat64: 36 | op = "-"; 37 | return e1 + " " + op + " " + e2; 38 | break; 39 | case XorInt64: 40 | case XorInt32: 41 | op = "^"; 42 | return e1 + " " + op + " " + e2; 43 | break; 44 | case OrInt64: 45 | case OrInt32: 46 | op = "|"; return e1 + " " + op + " " + e2; 47 | break; 48 | case MulInt32: 49 | case MulInt64: 50 | case MulFloat32: 51 | case MulFloat64: 52 | op = "*"; return e1 + " " + op + " " + e2; 53 | break; 54 | case EqInt32: 55 | case EqInt64: 56 | case EqFloat32: 57 | case EqFloat64: 58 | op = "=="; return e1 + " " + op + " " + e2; 59 | break; 60 | case NeInt32: 61 | case NeInt64: 62 | case NeFloat32: 63 | case NeFloat64: 64 | op = "!="; return e1 + " " + op + " " + e2; 65 | break; 66 | case AndInt32: 67 | case AndInt64: 68 | op = "&"; return e1 + " " + op + " " + e2; 69 | break; 70 | case LeSInt64: 71 | case LeSInt32: 72 | case LeUInt32: 73 | case LeFloat32: 74 | case LeFloat64: 75 | case LeUInt64: 76 | op = "<="; return e1 + " " + op + " " + e2; 77 | break; 78 | case LtUInt32: 79 | case LtSInt32: 80 | case LtSInt64: 81 | case LtFloat32: 82 | case LtFloat64: 83 | op = "<"; return e1 + " " + op + " " + e2; 84 | break; 85 | case DivSInt32: 86 | case DivUInt32: 87 | case DivSInt64: 88 | case DivUInt64: 89 | case DivFloat32: 90 | case DivFloat64: 91 | op = "/"; return e1 + " " + op + " " + e2; 92 | break; 93 | case GtSInt64: 94 | case GtUInt64: 95 | case GtFloat64: 96 | case GtFloat32: 97 | case GtSInt32: 98 | case GtUInt32: 99 | case LtUInt64: 100 | op = ">"; return e1 + " " + op + " " + e2; 101 | break; 102 | case GeSInt64: 103 | case GeSInt32: 104 | case GeFloat64: 105 | case GeFloat32: 106 | case GeUInt32: 107 | case GeUInt64: 108 | op = ">="; return e1 + " " + op + " " + e2; 109 | break; 110 | case CopySignFloat32: 111 | case CopySignFloat64: 112 | return "copysign(" + e1 + ", " + e2 + ")"; 113 | break; 114 | case RemSInt64: 115 | case RemUInt64: 116 | case RemSInt32: 117 | case RemUInt32: 118 | op = "%"; return e1 + " " + op + " " + e2; 119 | break; 120 | case RotLInt64: 121 | case RotLInt32: 122 | return "_rotl(" + e1 + ", " + e2 + ")"; 123 | break; 124 | case RotRInt32: 125 | case RotRInt64: 126 | return "_rotr(" + e1 + ", " + e2 + ")"; 127 | break; 128 | case ShlInt32: 129 | case ShlInt64: 130 | op = "<<"; return e1 + " " + op + " " + e2; 131 | break; 132 | case ShrUInt32: 133 | case ShrSInt32: 134 | case ShrUInt64: 135 | case ShrSInt64: 136 | op = ">>"; return e1 + " " + op + " " + e2; 137 | break; 138 | case MinFloat32: 139 | case MinFloat64: 140 | return "MIN(" + e1 + ", " + e2 + ")"; 141 | break; 142 | case MaxFloat32: 143 | case MaxFloat64: 144 | return "MAX(" + e1 + ", " + e2 + ")"; 145 | break; 146 | } 147 | op = "/* Unsupported binary operator */"; // Operation unimplemented or an unknown enumeration 148 | return op; 149 | } 150 | string wasmdec::Convert::resolveType(wasm::Type typ) { 151 | // Resolve wasm::Type to a C type 152 | switch (typ) { 153 | case wasm::Type::none: 154 | case wasm::Type::unreachable: 155 | return "void"; 156 | break; 157 | case wasm::Type::i32: 158 | return "int"; 159 | break; 160 | case wasm::Type::i64: 161 | return "long"; 162 | break; 163 | case wasm::Type::f32: 164 | return "float"; 165 | break; 166 | case wasm::Type::f64: 167 | return "double"; 168 | break; 169 | } 170 | return "void* /* Unknown type */"; 171 | } 172 | string wasmdec::Convert::getDecl(wasm::FunctionType* typ, string fname) { 173 | // Get a C function decloration from a FunctionType and name 174 | string ret = resolveType(typ->result); // Return type 175 | ret += " "; // Space between ret type and name 176 | ret += fname; 177 | ret += "("; // Argument list 178 | for (unsigned int i = 0; i < typ->params.size(); ++i) { 179 | ret += resolveType(typ->params.at(i)); 180 | ret += " "; 181 | ret += getLocal(i); 182 | if (i != (typ->params.size() - 1)) { 183 | ret += ", "; 184 | } 185 | } 186 | ret += ")"; 187 | return ret; 188 | } 189 | string wasmdec::Convert::getDecl(wasm::FunctionType* typ, wasm::Name fname) { 190 | // Overload to support WASM names 191 | string ret = resolveType(typ->result); // Return type 192 | ret += " "; // Space between ret type and name 193 | ret += fname.str; 194 | ret += "("; // Argument list 195 | for (unsigned int i = 0; i < typ->params.size(); ++i) { 196 | ret += resolveType(typ->params.at(i)); 197 | ret += " "; 198 | ret += getLocal(i); 199 | if (i != (typ->params.size() - 1)) { 200 | ret += ", "; 201 | } 202 | } 203 | ret += ")"; 204 | return ret; 205 | } 206 | string wasmdec::Convert::getDecl(wasm::Function* fn) { 207 | // Get C function decloration from WASM function 208 | string ret = resolveType(fn->result); // Return type 209 | ret += " "; // Space between ret type and name 210 | ret += getFName(fn->name); // name 211 | ret += "("; // Argument list 212 | for (unsigned int i = 0; i < fn->params.size(); ++i) { 213 | ret += resolveType(fn->params.at(i)); 214 | ret += " "; 215 | ret += getLocal(i); 216 | if (i != (fn->params.size() - 1)) { 217 | // Only append comma if the argument list isn't finished 218 | ret += ", "; 219 | } 220 | } 221 | ret += ")"; 222 | return ret; 223 | } 224 | string wasmdec::Convert::getDecl(wasm::Function* fn, string preface) { 225 | // Get C function decloration from WASM function 226 | string ret = resolveType(fn->result); // Return type 227 | ret += " "; // Space between ret type and name 228 | ret += preface; // function preface for decompiling multiple files 229 | ret += getFName(fn->name); // name 230 | ret += "("; // Argument list 231 | for (unsigned int i = 0; i < fn->params.size(); ++i) { 232 | ret += resolveType(fn->params.at(i)); 233 | ret += " "; 234 | ret += getLocal(i); 235 | if (i != (fn->params.size() - 1)) { 236 | // Only append comma if the argument list isn't finished 237 | ret += ", "; 238 | } 239 | } 240 | ret += ")"; 241 | return ret; 242 | } 243 | string wasmdec::Convert::parseOperandList(Context* ctx, ExpressionList* list) { 244 | if (list->size()) { 245 | string ret = "("; 246 | for (unsigned int i = 0; i < list->size(); ++i) { 247 | Expression* operand = list->operator[](i); 248 | string soperand = Convert::parseExpr(ctx, operand); 249 | ret += soperand; 250 | if (i != (list->size() - 1)) { 251 | // Only append comment if iterator is at the vector end 252 | ret += ", "; 253 | } 254 | } 255 | ret += ")"; 256 | return ret; 257 | } else { 258 | // Void operand list, no arguments needed 259 | return "()"; 260 | } 261 | } 262 | string wasmdec::Convert::getUnary(string exp, UnaryOp op) { 263 | switch (op) { 264 | case ClzInt32: 265 | case ClzInt64: 266 | return "__builtin_clz(" + exp + ")"; 267 | break; 268 | case CtzInt32: 269 | case CtzInt64: 270 | return "__builtin_ctz(" + exp + ")"; 271 | break; 272 | case PopcntInt32: 273 | case PopcntInt64: 274 | return "__builtin_popcount(" + exp + ")"; 275 | break; 276 | case NegFloat32: 277 | case NegFloat64: 278 | return "-(" + exp + ")"; 279 | break; 280 | case AbsFloat32: 281 | case AbsFloat64: 282 | return "abs(" + exp + ")"; 283 | break; 284 | case CeilFloat32: 285 | case CeilFloat64: 286 | return "ceil(" + exp + ")"; 287 | break; 288 | case FloorFloat32: 289 | case FloorFloat64: 290 | return "floor(" + exp + ")"; 291 | break; 292 | case TruncFloat32: 293 | case TruncFloat64: 294 | return "trunc(" + exp + ")"; 295 | break; 296 | case NearestFloat32: 297 | case NearestFloat64: 298 | return "fromfp(" + exp + ", FP_INT_TONEAREST) /* Round to nearest integer, ties to even */"; 299 | break; 300 | case SqrtFloat32: 301 | case SqrtFloat64: 302 | return "sqrt(" + exp + ")"; 303 | break; 304 | case EqZInt32: // Equals 0 305 | case EqZInt64: 306 | return exp + " == 0"; 307 | break; 308 | case ExtendSInt32: 309 | case ExtendUInt32: 310 | case TruncSFloat32ToInt64: 311 | case TruncUFloat32ToInt64: 312 | case TruncSFloat64ToInt64: 313 | case TruncUFloat64ToInt64: 314 | case ReinterpretFloat64: 315 | return "(long)" + exp; 316 | break; 317 | case WrapInt64: 318 | case TruncSFloat32ToInt32: 319 | case TruncUFloat32ToInt32: 320 | case TruncSFloat64ToInt32: 321 | case TruncUFloat64ToInt32: 322 | case ReinterpretFloat32: 323 | return "(int)" + exp; 324 | break; 325 | case ConvertSInt32ToFloat32: 326 | case ConvertUInt32ToFloat32: 327 | case ConvertSInt64ToFloat32: 328 | case ConvertUInt64ToFloat32: 329 | case DemoteFloat64: 330 | case ReinterpretInt32: 331 | return "(float)" + exp; 332 | break; 333 | case ConvertSInt32ToFloat64: 334 | case ConvertUInt32ToFloat64: 335 | case ConvertSInt64ToFloat64: 336 | case ConvertUInt64ToFloat64: 337 | case PromoteFloat32: 338 | case ReinterpretInt64: 339 | return "(double)" + exp; 340 | break; 341 | } 342 | return "/* Unsupported unary operator */"; 343 | } 344 | string wasmdec::Convert::getHostFunc(HostOp hop) { 345 | switch (hop) { 346 | /* 347 | case PageSize: 348 | return "host_get_page_size"; 349 | break; 350 | case HasFeature: 351 | return "host_has_feature"; 352 | break; 353 | */ 354 | case GrowMemory: 355 | return "host_grow_memory"; 356 | break; 357 | case CurrentMemory: 358 | return "host_get_current_memory"; 359 | break; 360 | } 361 | return "/* unknown host call */ host_unknown"; 362 | } -------------------------------------------------------------------------------- /test/asmjs/out.asm.js: -------------------------------------------------------------------------------- 1 | var asm=(/** @suppress {uselessCode} */ function(global,env,buffer) { 2 | "use asm";var a=new global.Int8Array(buffer);var b=new global.Int16Array(buffer);var c=new global.Int32Array(buffer);var d=new global.Uint8Array(buffer);var e=new global.Uint16Array(buffer);var f=new global.Uint32Array(buffer);var g=new global.Float32Array(buffer);var h=new global.Float64Array(buffer);var i=env.DYNAMICTOP_PTR|0;var j=env.tempDoublePtr|0;var k=env.ABORT|0;var l=env.STACKTOP|0;var m=env.STACK_MAX|0;var n=0;var o=0;var p=0;var q=0;var r=global.NaN,s=global.Infinity;var t=0,u=0,v=0,w=0,x=0.0;var y=0;var z=global.Math.floor;var A=global.Math.abs;var B=global.Math.sqrt;var C=global.Math.pow;var D=global.Math.cos;var E=global.Math.sin;var F=global.Math.tan;var G=global.Math.acos;var H=global.Math.asin;var I=global.Math.atan;var J=global.Math.atan2;var K=global.Math.exp;var L=global.Math.log;var M=global.Math.ceil;var N=global.Math.imul;var O=global.Math.min;var P=global.Math.max;var Q=global.Math.clz32;var R=env.abort;var S=env.assert;var T=env.enlargeMemory;var U=env.getTotalMemory;var V=env.abortOnCannotGrowMemory;var W=env.invoke_ii;var X=env.invoke_iiii;var Y=env.___setErrNo;var Z=env.___syscall140;var _=env.___syscall146;var $=env.___syscall54;var aa=env.___syscall6;var ba=env._emscripten_memcpy_big;var ca=env.flush_NO_FILESYSTEM;var da=0.0; 3 | // EMSCRIPTEN_START_FUNCS 4 | function ga(a){a=a|0;var b=0;b=l;l=l+a|0;l=l+15&-16;return b|0}function ha(){return l|0}function ia(a){a=a|0;l=a}function ja(a,b){a=a|0;b=b|0;l=a;m=b}function ka(a,b){a=a|0;b=b|0;if(!n){n=a;o=b}}function la(a){a=a|0;y=a}function ma(){return y|0}function na(){var a=0,b=0;a=l;l=l+16|0;b=a;c[b>>2]=2;ab(2772,b)|0;l=a;return 0}function oa(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0;x=l;l=l+16|0;o=x;do if(a>>>0<245){k=a>>>0<11?16:a+11&-8;a=k>>>3;n=c[972]|0;d=n>>>a;if(d&3|0){b=(d&1^1)+a|0;a=3928+(b<<1<<2)|0;d=a+8|0;e=c[d>>2]|0;f=e+8|0;g=c[f>>2]|0;if((g|0)==(a|0))c[972]=n&~(1<>2]=a;c[d>>2]=g}w=b<<3;c[e+4>>2]=w|3;w=e+w+4|0;c[w>>2]=c[w>>2]|1;w=f;l=x;return w|0}m=c[974]|0;if(k>>>0>m>>>0){if(d|0){b=2<>>12&16;b=b>>>i;d=b>>>5&8;b=b>>>d;g=b>>>2&4;b=b>>>g;a=b>>>1&2;b=b>>>a;e=b>>>1&1;e=(d|i|g|a|e)+(b>>>e)|0;b=3928+(e<<1<<2)|0;a=b+8|0;g=c[a>>2]|0;i=g+8|0;d=c[i>>2]|0;if((d|0)==(b|0)){a=n&~(1<>2]=b;c[a>>2]=d;a=n}w=e<<3;h=w-k|0;c[g+4>>2]=k|3;f=g+k|0;c[f+4>>2]=h|1;c[g+w>>2]=h;if(m|0){e=c[977]|0;b=m>>>3;d=3928+(b<<1<<2)|0;b=1<>2]|0}c[a>>2]=e;c[b+12>>2]=e;c[e+8>>2]=b;c[e+12>>2]=d}c[974]=h;c[977]=f;w=i;l=x;return w|0}g=c[973]|0;if(g){d=(g&0-g)+-1|0;f=d>>>12&16;d=d>>>f;e=d>>>5&8;d=d>>>e;h=d>>>2&4;d=d>>>h;i=d>>>1&2;d=d>>>i;j=d>>>1&1;j=c[4192+((e|f|h|i|j)+(d>>>j)<<2)>>2]|0;d=j;i=j;j=(c[j+4>>2]&-8)-k|0;while(1){a=c[d+16>>2]|0;if(!a){a=c[d+20>>2]|0;if(!a)break}h=(c[a+4>>2]&-8)-k|0;f=h>>>0>>0;d=a;i=f?a:i;j=f?h:j}h=i+k|0;if(h>>>0>i>>>0){f=c[i+24>>2]|0;b=c[i+12>>2]|0;do if((b|0)==(i|0)){a=i+20|0;b=c[a>>2]|0;if(!b){a=i+16|0;b=c[a>>2]|0;if(!b){d=0;break}}while(1){e=b+20|0;d=c[e>>2]|0;if(!d){e=b+16|0;d=c[e>>2]|0;if(!d)break;else{b=d;a=e}}else{b=d;a=e}}c[a>>2]=0;d=b}else{d=c[i+8>>2]|0;c[d+12>>2]=b;c[b+8>>2]=d;d=b}while(0);do if(f|0){b=c[i+28>>2]|0;a=4192+(b<<2)|0;if((i|0)==(c[a>>2]|0)){c[a>>2]=d;if(!d){c[973]=g&~(1<>2]|0)==(i|0)?w:f+20|0)>>2]=d;if(!d)break}c[d+24>>2]=f;b=c[i+16>>2]|0;if(b|0){c[d+16>>2]=b;c[b+24>>2]=d}b=c[i+20>>2]|0;if(b|0){c[d+20>>2]=b;c[b+24>>2]=d}}while(0);if(j>>>0<16){w=j+k|0;c[i+4>>2]=w|3;w=i+w+4|0;c[w>>2]=c[w>>2]|1}else{c[i+4>>2]=k|3;c[h+4>>2]=j|1;c[h+j>>2]=j;if(m|0){e=c[977]|0;b=m>>>3;d=3928+(b<<1<<2)|0;b=1<>2]|0}c[a>>2]=e;c[b+12>>2]=e;c[e+8>>2]=b;c[e+12>>2]=d}c[974]=j;c[977]=h}w=i+8|0;l=x;return w|0}else n=k}else n=k}else n=k}else if(a>>>0<=4294967231){a=a+11|0;k=a&-8;e=c[973]|0;if(e){f=0-k|0;a=a>>>8;if(a)if(k>>>0>16777215)j=31;else{n=(a+1048320|0)>>>16&8;r=a<>>16&4;r=r<>>16&2;j=14-(i|n|j)+(r<>>15)|0;j=k>>>(j+7|0)&1|j<<1}else j=0;d=c[4192+(j<<2)>>2]|0;a:do if(!d){d=0;a=0;r=61}else{a=0;i=k<<((j|0)==31?0:25-(j>>>1)|0);g=0;while(1){h=(c[d+4>>2]&-8)-k|0;if(h>>>0>>0)if(!h){a=d;f=0;r=65;break a}else{a=d;f=h}r=c[d+20>>2]|0;d=c[d+16+(i>>>31<<2)>>2]|0;g=(r|0)==0|(r|0)==(d|0)?g:r;if(!d){d=g;r=61;break}else i=i<<1}}while(0);if((r|0)==61){if((d|0)==0&(a|0)==0){a=2<>>12&16;n=n>>>h;g=n>>>5&8;n=n>>>g;i=n>>>2&4;n=n>>>i;j=n>>>1&2;n=n>>>j;d=n>>>1&1;a=0;d=c[4192+((g|h|i|j|d)+(n>>>d)<<2)>>2]|0}if(!d){i=a;h=f}else r=65}if((r|0)==65){g=d;while(1){n=(c[g+4>>2]&-8)-k|0;d=n>>>0>>0;f=d?n:f;a=d?g:a;d=c[g+16>>2]|0;if(!d)d=c[g+20>>2]|0;if(!d){i=a;h=f;break}else g=d}}if(((i|0)!=0?h>>>0<((c[974]|0)-k|0)>>>0:0)?(m=i+k|0,m>>>0>i>>>0):0){g=c[i+24>>2]|0;b=c[i+12>>2]|0;do if((b|0)==(i|0)){a=i+20|0;b=c[a>>2]|0;if(!b){a=i+16|0;b=c[a>>2]|0;if(!b){b=0;break}}while(1){f=b+20|0;d=c[f>>2]|0;if(!d){f=b+16|0;d=c[f>>2]|0;if(!d)break;else{b=d;a=f}}else{b=d;a=f}}c[a>>2]=0}else{w=c[i+8>>2]|0;c[w+12>>2]=b;c[b+8>>2]=w}while(0);do if(g){a=c[i+28>>2]|0;d=4192+(a<<2)|0;if((i|0)==(c[d>>2]|0)){c[d>>2]=b;if(!b){e=e&~(1<>2]|0)==(i|0)?w:g+20|0)>>2]=b;if(!b)break}c[b+24>>2]=g;a=c[i+16>>2]|0;if(a|0){c[b+16>>2]=a;c[a+24>>2]=b}a=c[i+20>>2]|0;if(a){c[b+20>>2]=a;c[a+24>>2]=b}}while(0);b:do if(h>>>0<16){w=h+k|0;c[i+4>>2]=w|3;w=i+w+4|0;c[w>>2]=c[w>>2]|1}else{c[i+4>>2]=k|3;c[m+4>>2]=h|1;c[m+h>>2]=h;b=h>>>3;if(h>>>0<256){d=3928+(b<<1<<2)|0;a=c[972]|0;b=1<>2]|0}c[a>>2]=m;c[b+12>>2]=m;c[m+8>>2]=b;c[m+12>>2]=d;break}b=h>>>8;if(b)if(h>>>0>16777215)d=31;else{v=(b+1048320|0)>>>16&8;w=b<>>16&4;w=w<>>16&2;d=14-(u|v|d)+(w<>>15)|0;d=h>>>(d+7|0)&1|d<<1}else d=0;b=4192+(d<<2)|0;c[m+28>>2]=d;a=m+16|0;c[a+4>>2]=0;c[a>>2]=0;a=1<>2]=m;c[m+24>>2]=b;c[m+12>>2]=m;c[m+8>>2]=m;break}b=c[b>>2]|0;c:do if((c[b+4>>2]&-8|0)!=(h|0)){e=h<<((d|0)==31?0:25-(d>>>1)|0);while(1){d=b+16+(e>>>31<<2)|0;a=c[d>>2]|0;if(!a)break;if((c[a+4>>2]&-8|0)==(h|0)){b=a;break c}else{e=e<<1;b=a}}c[d>>2]=m;c[m+24>>2]=b;c[m+12>>2]=m;c[m+8>>2]=m;break b}while(0);v=b+8|0;w=c[v>>2]|0;c[w+12>>2]=m;c[v>>2]=m;c[m+8>>2]=w;c[m+12>>2]=b;c[m+24>>2]=0}while(0);w=i+8|0;l=x;return w|0}else n=k}else n=k}else n=-1;while(0);d=c[974]|0;if(d>>>0>=n>>>0){b=d-n|0;a=c[977]|0;if(b>>>0>15){w=a+n|0;c[977]=w;c[974]=b;c[w+4>>2]=b|1;c[a+d>>2]=b;c[a+4>>2]=n|3}else{c[974]=0;c[977]=0;c[a+4>>2]=d|3;w=a+d+4|0;c[w>>2]=c[w>>2]|1}w=a+8|0;l=x;return w|0}h=c[975]|0;if(h>>>0>n>>>0){u=h-n|0;c[975]=u;w=c[978]|0;v=w+n|0;c[978]=v;c[v+4>>2]=u|1;c[w+4>>2]=n|3;w=w+8|0;l=x;return w|0}if(!(c[1090]|0)){c[1092]=4096;c[1091]=4096;c[1093]=-1;c[1094]=-1;c[1095]=0;c[1083]=0;c[1090]=o&-16^1431655768;a=4096}else a=c[1092]|0;i=n+48|0;j=n+47|0;g=a+j|0;f=0-a|0;k=g&f;if(k>>>0<=n>>>0){w=0;l=x;return w|0}a=c[1082]|0;if(a|0?(m=c[1080]|0,o=m+k|0,o>>>0<=m>>>0|o>>>0>a>>>0):0){w=0;l=x;return w|0}d:do if(!(c[1083]&4)){d=c[978]|0;e:do if(d){e=4336;while(1){o=c[e>>2]|0;if(o>>>0<=d>>>0?(o+(c[e+4>>2]|0)|0)>>>0>d>>>0:0)break;a=c[e+8>>2]|0;if(!a){r=128;break e}else e=a}b=g-h&f;if(b>>>0<2147483647){a=ob(b|0)|0;if((a|0)==((c[e>>2]|0)+(c[e+4>>2]|0)|0)){if((a|0)!=(-1|0)){h=b;g=a;r=145;break d}}else{e=a;r=136}}else b=0}else r=128;while(0);do if((r|0)==128){d=ob(0)|0;if((d|0)!=(-1|0)?(b=d,p=c[1091]|0,q=p+-1|0,b=((q&b|0)==0?0:(q+b&0-p)-b|0)+k|0,p=c[1080]|0,q=b+p|0,b>>>0>n>>>0&b>>>0<2147483647):0){o=c[1082]|0;if(o|0?q>>>0<=p>>>0|q>>>0>o>>>0:0){b=0;break}a=ob(b|0)|0;if((a|0)==(d|0)){h=b;g=d;r=145;break d}else{e=a;r=136}}else b=0}while(0);do if((r|0)==136){d=0-b|0;if(!(i>>>0>b>>>0&(b>>>0<2147483647&(e|0)!=(-1|0))))if((e|0)==(-1|0)){b=0;break}else{h=b;g=e;r=145;break d}a=c[1092]|0;a=j-b+a&0-a;if(a>>>0>=2147483647){h=b;g=e;r=145;break d}if((ob(a|0)|0)==(-1|0)){ob(d|0)|0;b=0;break}else{h=a+b|0;g=e;r=145;break d}}while(0);c[1083]=c[1083]|4;r=143}else{b=0;r=143}while(0);if(((r|0)==143?k>>>0<2147483647:0)?(u=ob(k|0)|0,q=ob(0)|0,s=q-u|0,t=s>>>0>(n+40|0)>>>0,!((u|0)==(-1|0)|t^1|u>>>0>>0&((u|0)!=(-1|0)&(q|0)!=(-1|0))^1)):0){h=t?s:b;g=u;r=145}if((r|0)==145){b=(c[1080]|0)+h|0;c[1080]=b;if(b>>>0>(c[1081]|0)>>>0)c[1081]=b;j=c[978]|0;f:do if(j){b=4336;while(1){a=c[b>>2]|0;d=c[b+4>>2]|0;if((g|0)==(a+d|0)){r=154;break}e=c[b+8>>2]|0;if(!e)break;else b=e}if(((r|0)==154?(v=b+4|0,(c[b+12>>2]&8|0)==0):0)?g>>>0>j>>>0&a>>>0<=j>>>0:0){c[v>>2]=d+h;w=(c[975]|0)+h|0;u=j+8|0;u=(u&7|0)==0?0:0-u&7;v=j+u|0;u=w-u|0;c[978]=v;c[975]=u;c[v+4>>2]=u|1;c[j+w+4>>2]=40;c[979]=c[1094];break}if(g>>>0<(c[976]|0)>>>0)c[976]=g;d=g+h|0;b=4336;while(1){if((c[b>>2]|0)==(d|0)){r=162;break}a=c[b+8>>2]|0;if(!a)break;else b=a}if((r|0)==162?(c[b+12>>2]&8|0)==0:0){c[b>>2]=g;m=b+4|0;c[m>>2]=(c[m>>2]|0)+h;m=g+8|0;m=g+((m&7|0)==0?0:0-m&7)|0;b=d+8|0;b=d+((b&7|0)==0?0:0-b&7)|0;k=m+n|0;i=b-m-n|0;c[m+4>>2]=n|3;g:do if((j|0)==(b|0)){w=(c[975]|0)+i|0;c[975]=w;c[978]=k;c[k+4>>2]=w|1}else{if((c[977]|0)==(b|0)){w=(c[974]|0)+i|0;c[974]=w;c[977]=k;c[k+4>>2]=w|1;c[k+w>>2]=w;break}a=c[b+4>>2]|0;if((a&3|0)==1){h=a&-8;e=a>>>3;h:do if(a>>>0<256){a=c[b+8>>2]|0;d=c[b+12>>2]|0;if((d|0)==(a|0)){c[972]=c[972]&~(1<>2]=d;c[d+8>>2]=a;break}}else{g=c[b+24>>2]|0;a=c[b+12>>2]|0;do if((a|0)==(b|0)){d=b+16|0;e=d+4|0;a=c[e>>2]|0;if(!a){a=c[d>>2]|0;if(!a){a=0;break}}else d=e;while(1){f=a+20|0;e=c[f>>2]|0;if(!e){f=a+16|0;e=c[f>>2]|0;if(!e)break;else{a=e;d=f}}else{a=e;d=f}}c[d>>2]=0}else{w=c[b+8>>2]|0;c[w+12>>2]=a;c[a+8>>2]=w}while(0);if(!g)break;d=c[b+28>>2]|0;e=4192+(d<<2)|0;do if((c[e>>2]|0)!=(b|0)){w=g+16|0;c[((c[w>>2]|0)==(b|0)?w:g+20|0)>>2]=a;if(!a)break h}else{c[e>>2]=a;if(a|0)break;c[973]=c[973]&~(1<>2]=g;d=b+16|0;e=c[d>>2]|0;if(e|0){c[a+16>>2]=e;c[e+24>>2]=a}d=c[d+4>>2]|0;if(!d)break;c[a+20>>2]=d;c[d+24>>2]=a}while(0);b=b+h|0;f=h+i|0}else f=i;b=b+4|0;c[b>>2]=c[b>>2]&-2;c[k+4>>2]=f|1;c[k+f>>2]=f;b=f>>>3;if(f>>>0<256){d=3928+(b<<1<<2)|0;a=c[972]|0;b=1<>2]|0}c[a>>2]=k;c[b+12>>2]=k;c[k+8>>2]=b;c[k+12>>2]=d;break}b=f>>>8;do if(!b)e=0;else{if(f>>>0>16777215){e=31;break}v=(b+1048320|0)>>>16&8;w=b<>>16&4;w=w<>>16&2;e=14-(u|v|e)+(w<>>15)|0;e=f>>>(e+7|0)&1|e<<1}while(0);b=4192+(e<<2)|0;c[k+28>>2]=e;a=k+16|0;c[a+4>>2]=0;c[a>>2]=0;a=c[973]|0;d=1<>2]=k;c[k+24>>2]=b;c[k+12>>2]=k;c[k+8>>2]=k;break}b=c[b>>2]|0;i:do if((c[b+4>>2]&-8|0)!=(f|0)){e=f<<((e|0)==31?0:25-(e>>>1)|0);while(1){d=b+16+(e>>>31<<2)|0;a=c[d>>2]|0;if(!a)break;if((c[a+4>>2]&-8|0)==(f|0)){b=a;break i}else{e=e<<1;b=a}}c[d>>2]=k;c[k+24>>2]=b;c[k+12>>2]=k;c[k+8>>2]=k;break g}while(0);v=b+8|0;w=c[v>>2]|0;c[w+12>>2]=k;c[v>>2]=k;c[k+8>>2]=w;c[k+12>>2]=b;c[k+24>>2]=0}while(0);w=m+8|0;l=x;return w|0}b=4336;while(1){a=c[b>>2]|0;if(a>>>0<=j>>>0?(w=a+(c[b+4>>2]|0)|0,w>>>0>j>>>0):0)break;b=c[b+8>>2]|0}f=w+-47|0;a=f+8|0;a=f+((a&7|0)==0?0:0-a&7)|0;f=j+16|0;a=a>>>0>>0?j:a;b=a+8|0;d=h+-40|0;u=g+8|0;u=(u&7|0)==0?0:0-u&7;v=g+u|0;u=d-u|0;c[978]=v;c[975]=u;c[v+4>>2]=u|1;c[g+d+4>>2]=40;c[979]=c[1094];d=a+4|0;c[d>>2]=27;c[b>>2]=c[1084];c[b+4>>2]=c[1085];c[b+8>>2]=c[1086];c[b+12>>2]=c[1087];c[1084]=g;c[1085]=h;c[1087]=0;c[1086]=b;b=a+24|0;do{v=b;b=b+4|0;c[b>>2]=7}while((v+8|0)>>>0>>0);if((a|0)!=(j|0)){g=a-j|0;c[d>>2]=c[d>>2]&-2;c[j+4>>2]=g|1;c[a>>2]=g;b=g>>>3;if(g>>>0<256){d=3928+(b<<1<<2)|0;a=c[972]|0;b=1<>2]|0}c[a>>2]=j;c[b+12>>2]=j;c[j+8>>2]=b;c[j+12>>2]=d;break}b=g>>>8;if(b)if(g>>>0>16777215)e=31;else{v=(b+1048320|0)>>>16&8;w=b<>>16&4;w=w<>>16&2;e=14-(u|v|e)+(w<>>15)|0;e=g>>>(e+7|0)&1|e<<1}else e=0;d=4192+(e<<2)|0;c[j+28>>2]=e;c[j+20>>2]=0;c[f>>2]=0;b=c[973]|0;a=1<>2]=j;c[j+24>>2]=d;c[j+12>>2]=j;c[j+8>>2]=j;break}b=c[d>>2]|0;j:do if((c[b+4>>2]&-8|0)!=(g|0)){e=g<<((e|0)==31?0:25-(e>>>1)|0);while(1){d=b+16+(e>>>31<<2)|0;a=c[d>>2]|0;if(!a)break;if((c[a+4>>2]&-8|0)==(g|0)){b=a;break j}else{e=e<<1;b=a}}c[d>>2]=j;c[j+24>>2]=b;c[j+12>>2]=j;c[j+8>>2]=j;break f}while(0);v=b+8|0;w=c[v>>2]|0;c[w+12>>2]=j;c[v>>2]=j;c[j+8>>2]=w;c[j+12>>2]=b;c[j+24>>2]=0}}else{w=c[976]|0;if((w|0)==0|g>>>0>>0)c[976]=g;c[1084]=g;c[1085]=h;c[1087]=0;c[981]=c[1090];c[980]=-1;c[985]=3928;c[984]=3928;c[987]=3936;c[986]=3936;c[989]=3944;c[988]=3944;c[991]=3952;c[990]=3952;c[993]=3960;c[992]=3960;c[995]=3968;c[994]=3968;c[997]=3976;c[996]=3976;c[999]=3984;c[998]=3984;c[1001]=3992;c[1e3]=3992;c[1003]=4e3;c[1002]=4e3;c[1005]=4008;c[1004]=4008;c[1007]=4016;c[1006]=4016;c[1009]=4024;c[1008]=4024;c[1011]=4032;c[1010]=4032;c[1013]=4040;c[1012]=4040;c[1015]=4048;c[1014]=4048;c[1017]=4056;c[1016]=4056;c[1019]=4064;c[1018]=4064;c[1021]=4072;c[1020]=4072;c[1023]=4080;c[1022]=4080;c[1025]=4088;c[1024]=4088;c[1027]=4096;c[1026]=4096;c[1029]=4104;c[1028]=4104;c[1031]=4112;c[1030]=4112;c[1033]=4120;c[1032]=4120;c[1035]=4128;c[1034]=4128;c[1037]=4136;c[1036]=4136;c[1039]=4144;c[1038]=4144;c[1041]=4152;c[1040]=4152;c[1043]=4160;c[1042]=4160;c[1045]=4168;c[1044]=4168;c[1047]=4176;c[1046]=4176;w=h+-40|0;u=g+8|0;u=(u&7|0)==0?0:0-u&7;v=g+u|0;u=w-u|0;c[978]=v;c[975]=u;c[v+4>>2]=u|1;c[g+w+4>>2]=40;c[979]=c[1094]}while(0);b=c[975]|0;if(b>>>0>n>>>0){u=b-n|0;c[975]=u;w=c[978]|0;v=w+n|0;c[978]=v;c[v+4>>2]=u|1;c[w+4>>2]=n|3;w=w+8|0;l=x;return w|0}}c[(ua()|0)>>2]=12;w=0;l=x;return w|0}function pa(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0;if(!a)return;d=a+-8|0;f=c[976]|0;a=c[a+-4>>2]|0;b=a&-8;j=d+b|0;do if(!(a&1)){e=c[d>>2]|0;if(!(a&3))return;h=d+(0-e)|0;g=e+b|0;if(h>>>0>>0)return;if((c[977]|0)==(h|0)){a=j+4|0;b=c[a>>2]|0;if((b&3|0)!=3){i=h;b=g;break}c[974]=g;c[a>>2]=b&-2;c[h+4>>2]=g|1;c[h+g>>2]=g;return}d=e>>>3;if(e>>>0<256){a=c[h+8>>2]|0;b=c[h+12>>2]|0;if((b|0)==(a|0)){c[972]=c[972]&~(1<>2]=b;c[b+8>>2]=a;i=h;b=g;break}}f=c[h+24>>2]|0;a=c[h+12>>2]|0;do if((a|0)==(h|0)){b=h+16|0;d=b+4|0;a=c[d>>2]|0;if(!a){a=c[b>>2]|0;if(!a){a=0;break}}else b=d;while(1){e=a+20|0;d=c[e>>2]|0;if(!d){e=a+16|0;d=c[e>>2]|0;if(!d)break;else{a=d;b=e}}else{a=d;b=e}}c[b>>2]=0}else{i=c[h+8>>2]|0;c[i+12>>2]=a;c[a+8>>2]=i}while(0);if(f){b=c[h+28>>2]|0;d=4192+(b<<2)|0;if((c[d>>2]|0)==(h|0)){c[d>>2]=a;if(!a){c[973]=c[973]&~(1<>2]|0)==(h|0)?i:f+20|0)>>2]=a;if(!a){i=h;b=g;break}}c[a+24>>2]=f;b=h+16|0;d=c[b>>2]|0;if(d|0){c[a+16>>2]=d;c[d+24>>2]=a}b=c[b+4>>2]|0;if(b){c[a+20>>2]=b;c[b+24>>2]=a;i=h;b=g}else{i=h;b=g}}else{i=h;b=g}}else{i=d;h=d}while(0);if(h>>>0>=j>>>0)return;a=j+4|0;e=c[a>>2]|0;if(!(e&1))return;if(!(e&2)){if((c[978]|0)==(j|0)){j=(c[975]|0)+b|0;c[975]=j;c[978]=i;c[i+4>>2]=j|1;if((i|0)!=(c[977]|0))return;c[977]=0;c[974]=0;return}if((c[977]|0)==(j|0)){j=(c[974]|0)+b|0;c[974]=j;c[977]=h;c[i+4>>2]=j|1;c[h+j>>2]=j;return}f=(e&-8)+b|0;d=e>>>3;do if(e>>>0<256){b=c[j+8>>2]|0;a=c[j+12>>2]|0;if((a|0)==(b|0)){c[972]=c[972]&~(1<>2]=a;c[a+8>>2]=b;break}}else{g=c[j+24>>2]|0;a=c[j+12>>2]|0;do if((a|0)==(j|0)){b=j+16|0;d=b+4|0;a=c[d>>2]|0;if(!a){a=c[b>>2]|0;if(!a){d=0;break}}else b=d;while(1){e=a+20|0;d=c[e>>2]|0;if(!d){e=a+16|0;d=c[e>>2]|0;if(!d)break;else{a=d;b=e}}else{a=d;b=e}}c[b>>2]=0;d=a}else{d=c[j+8>>2]|0;c[d+12>>2]=a;c[a+8>>2]=d;d=a}while(0);if(g|0){a=c[j+28>>2]|0;b=4192+(a<<2)|0;if((c[b>>2]|0)==(j|0)){c[b>>2]=d;if(!d){c[973]=c[973]&~(1<>2]|0)==(j|0)?e:g+20|0)>>2]=d;if(!d)break}c[d+24>>2]=g;a=j+16|0;b=c[a>>2]|0;if(b|0){c[d+16>>2]=b;c[b+24>>2]=d}a=c[a+4>>2]|0;if(a|0){c[d+20>>2]=a;c[a+24>>2]=d}}}while(0);c[i+4>>2]=f|1;c[h+f>>2]=f;if((i|0)==(c[977]|0)){c[974]=f;return}}else{c[a>>2]=e&-2;c[i+4>>2]=b|1;c[h+b>>2]=b;f=b}a=f>>>3;if(f>>>0<256){d=3928+(a<<1<<2)|0;b=c[972]|0;a=1<>2]|0}c[b>>2]=i;c[a+12>>2]=i;c[i+8>>2]=a;c[i+12>>2]=d;return}a=f>>>8;if(a)if(f>>>0>16777215)e=31;else{h=(a+1048320|0)>>>16&8;j=a<>>16&4;j=j<>>16&2;e=14-(g|h|e)+(j<>>15)|0;e=f>>>(e+7|0)&1|e<<1}else e=0;a=4192+(e<<2)|0;c[i+28>>2]=e;c[i+20>>2]=0;c[i+16>>2]=0;b=c[973]|0;d=1<>2]=i;c[i+24>>2]=a;c[i+12>>2]=i;c[i+8>>2]=i}else{a=c[a>>2]|0;b:do if((c[a+4>>2]&-8|0)!=(f|0)){e=f<<((e|0)==31?0:25-(e>>>1)|0);while(1){d=a+16+(e>>>31<<2)|0;b=c[d>>2]|0;if(!b)break;if((c[b+4>>2]&-8|0)==(f|0)){a=b;break b}else{e=e<<1;a=b}}c[d>>2]=i;c[i+24>>2]=a;c[i+12>>2]=i;c[i+8>>2]=i;break a}while(0);h=a+8|0;j=c[h>>2]|0;c[j+12>>2]=i;c[h>>2]=i;c[i+8>>2]=j;c[i+12>>2]=a;c[i+24>>2]=0}while(0);j=(c[980]|0)+-1|0;c[980]=j;if(j|0)return;a=4344;while(1){a=c[a>>2]|0;if(!a)break;else a=a+8|0}c[980]=-1;return}function qa(a){a=a|0;var b=0,d=0;b=l;l=l+16|0;d=b;c[d>>2]=wa(c[a+60>>2]|0)|0;a=ta(aa(6,d|0)|0)|0;l=b;return a|0}function ra(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0;g=l;l=l+32|0;f=g;c[b+36>>2]=3;if((c[b>>2]&64|0)==0?(c[f>>2]=c[b+60>>2],c[f+4>>2]=21523,c[f+8>>2]=g+16,$(54,f|0)|0):0)a[b+75>>0]=-1;f=va(b,d,e)|0;l=g;return f|0}function sa(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0;f=l;l=l+32|0;g=f;e=f+20|0;c[g>>2]=c[a+60>>2];c[g+4>>2]=0;c[g+8>>2]=b;c[g+12>>2]=e;c[g+16>>2]=d;if((ta(Z(140,g|0)|0)|0)<0){c[e>>2]=-1;a=-1}else a=c[e>>2]|0;l=f;return a|0}function ta(a){a=a|0;if(a>>>0>4294963200){c[(ua()|0)>>2]=0-a;a=-1}return a|0}function ua(){return 4448}function va(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,m=0,n=0,o=0,p=0;n=l;l=l+48|0;k=n+32|0;g=n+16|0;f=n;i=a+28|0;e=c[i>>2]|0;c[f>>2]=e;j=a+20|0;e=(c[j>>2]|0)-e|0;c[f+4>>2]=e;c[f+8>>2]=b;c[f+12>>2]=d;e=e+d|0;h=a+60|0;c[g>>2]=c[h>>2];c[g+4>>2]=f;c[g+8>>2]=2;g=ta(_(146,g|0)|0)|0;a:do if((e|0)!=(g|0)){b=2;while(1){if((g|0)<0)break;e=e-g|0;p=c[f+4>>2]|0;o=g>>>0>p>>>0;f=o?f+8|0:f;b=b+(o<<31>>31)|0;p=g-(o?p:0)|0;c[f>>2]=(c[f>>2]|0)+p;o=f+4|0;c[o>>2]=(c[o>>2]|0)-p;c[k>>2]=c[h>>2];c[k+4>>2]=f;c[k+8>>2]=b;g=ta(_(146,k|0)|0)|0;if((e|0)==(g|0)){m=3;break a}}c[a+16>>2]=0;c[i>>2]=0;c[j>>2]=0;c[a>>2]=c[a>>2]|32;if((b|0)==2)d=0;else d=d-(c[f+4>>2]|0)|0}else m=3;while(0);if((m|0)==3){p=c[a+44>>2]|0;c[a+16>>2]=p+(c[a+48>>2]|0);c[i>>2]=p;c[j>>2]=p}l=n;return d|0}function wa(a){a=a|0;return a|0}function xa(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0;h=d&255;f=(e|0)!=0;a:do if(f&(b&3|0)!=0){g=d&255;while(1){if((a[b>>0]|0)==g<<24>>24){i=6;break a}b=b+1|0;e=e+-1|0;f=(e|0)!=0;if(!(f&(b&3|0)!=0)){i=5;break}}}else i=5;while(0);if((i|0)==5)if(f)i=6;else i=16;b:do if((i|0)==6){g=d&255;if((a[b>>0]|0)==g<<24>>24)if(!e){i=16;break}else break;f=N(h,16843009)|0;c:do if(e>>>0>3)while(1){h=c[b>>2]^f;if((h&-2139062144^-2139062144)&h+-16843009|0)break c;b=b+4|0;e=e+-4|0;if(e>>>0<=3){i=11;break}}else i=11;while(0);if((i|0)==11)if(!e){i=16;break}while(1){if((a[b>>0]|0)==g<<24>>24)break b;e=e+-1|0;if(!e){i=16;break}else b=b+1|0}}while(0);if((i|0)==16)b=0;return b|0}function ya(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0,j=0,k=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;s=l;l=l+224|0;n=s+208|0;p=s+160|0;q=s+80|0;r=s;f=p;g=f+40|0;do{c[f>>2]=0;f=f+4|0}while((f|0)<(g|0));c[n>>2]=c[e>>2];if((za(0,d,n,q,p)|0)<0)e=-1;else{if((c[b+76>>2]|0)>-1)o=Aa(b)|0;else o=0;e=c[b>>2]|0;m=e&32;if((a[b+74>>0]|0)<1)c[b>>2]=e&-33;f=b+48|0;if(!(c[f>>2]|0)){g=b+44|0;h=c[g>>2]|0;c[g>>2]=r;i=b+28|0;c[i>>2]=r;j=b+20|0;c[j>>2]=r;c[f>>2]=80;k=b+16|0;c[k>>2]=r+80;e=za(b,d,n,q,p)|0;if(h){fa[c[b+36>>2]&3](b,0,0)|0;e=(c[j>>2]|0)==0?-1:e;c[g>>2]=h;c[f>>2]=0;c[k>>2]=0;c[i>>2]=0;c[j>>2]=0}}else e=za(b,d,n,q,p)|0;f=c[b>>2]|0;c[b>>2]=f|m;if(o|0)Ba(b);e=(f&32|0)==0?e:-1}l=s;return e|0}function za(d,e,f,g,i){d=d|0;e=e|0;f=f|0;g=g|0;i=i|0;var j=0,k=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0;I=l;l=l+64|0;D=I+56|0;E=I+40|0;A=I;G=I+48|0;H=I+60|0;c[D>>2]=e;w=(d|0)!=0;x=A+40|0;z=x;A=A+39|0;B=G+4|0;j=0;e=0;m=0;a:while(1){do{do if((e|0)>-1)if((j|0)>(2147483647-e|0)){c[(ua()|0)>>2]=75;e=-1;break}else{e=j+e|0;break}while(0);s=c[D>>2]|0;j=a[s>>0]|0;if(!(j<<24>>24)){v=94;break a}k=s;b:while(1){switch(j<<24>>24){case 37:{v=10;break b}case 0:{j=k;break b}default:{}}u=k+1|0;c[D>>2]=u;j=a[u>>0]|0;k=u}c:do if((v|0)==10){v=0;j=k;do{if((a[k+1>>0]|0)!=37)break c;j=j+1|0;k=k+2|0;c[D>>2]=k}while((a[k>>0]|0)==37)}while(0);j=j-s|0;if(w)Ca(d,s,j)}while((j|0)!=0);u=(Da(a[(c[D>>2]|0)+1>>0]|0)|0)==0;k=c[D>>2]|0;if(!u?(a[k+2>>0]|0)==36:0){q=(a[k+1>>0]|0)+-48|0;o=1;j=3}else{q=-1;o=m;j=1}j=k+j|0;c[D>>2]=j;k=a[j>>0]|0;m=(k<<24>>24)+-32|0;if(m>>>0>31|(1<>2]=j;k=a[j>>0]|0;m=(k<<24>>24)+-32|0}while(!(m>>>0>31|(1<>24==42){if((Da(a[j+1>>0]|0)|0)!=0?(F=c[D>>2]|0,(a[F+2>>0]|0)==36):0){j=F+1|0;c[i+((a[j>>0]|0)+-48<<2)>>2]=10;j=c[g+((a[j>>0]|0)+-48<<3)>>2]|0;m=1;k=F+3|0}else{if(o|0){e=-1;break}if(w){u=(c[f>>2]|0)+(4-1)&~(4-1);j=c[u>>2]|0;c[f>>2]=u+4}else j=0;m=0;k=(c[D>>2]|0)+1|0}c[D>>2]=k;u=(j|0)<0;t=u?0-j|0:j;n=u?n|8192:n;u=m}else{j=Ea(D)|0;if((j|0)<0){e=-1;break}t=j;u=o;k=c[D>>2]|0}do if((a[k>>0]|0)==46){j=k+1|0;if((a[j>>0]|0)!=42){c[D>>2]=j;j=Ea(D)|0;k=c[D>>2]|0;break}if(Da(a[k+2>>0]|0)|0?(C=c[D>>2]|0,(a[C+3>>0]|0)==36):0){j=C+2|0;c[i+((a[j>>0]|0)+-48<<2)>>2]=10;j=c[g+((a[j>>0]|0)+-48<<3)>>2]|0;k=C+4|0;c[D>>2]=k;break}if(u|0){e=-1;break a}if(w){r=(c[f>>2]|0)+(4-1)&~(4-1);j=c[r>>2]|0;c[f>>2]=r+4}else j=0;k=(c[D>>2]|0)+2|0;c[D>>2]=k}else j=-1;while(0);r=0;while(1){if(((a[k>>0]|0)+-65|0)>>>0>57){e=-1;break a}m=k;k=k+1|0;c[D>>2]=k;m=a[(a[m>>0]|0)+-65+(16+(r*58|0))>>0]|0;o=m&255;if((o+-1|0)>>>0>=8)break;else r=o}if(!(m<<24>>24)){e=-1;break}p=(q|0)>-1;do if(m<<24>>24==19)if(p){e=-1;break a}else v=54;else{if(p){c[i+(q<<2)>>2]=o;p=g+(q<<3)|0;q=c[p+4>>2]|0;v=E;c[v>>2]=c[p>>2];c[v+4>>2]=q;v=54;break}if(!w){e=0;break a}Fa(E,o,f);k=c[D>>2]|0;v=55}while(0);if((v|0)==54){v=0;if(w)v=55;else j=0}d:do if((v|0)==55){v=0;k=a[k+-1>>0]|0;k=(r|0)!=0&(k&15|0)==3?k&-33:k;m=n&-65537;q=(n&8192|0)==0?n:m;e:do switch(k|0){case 110:switch((r&255)<<24>>24){case 0:{c[c[E>>2]>>2]=e;j=0;break d}case 1:{c[c[E>>2]>>2]=e;j=0;break d}case 2:{j=c[E>>2]|0;c[j>>2]=e;c[j+4>>2]=((e|0)<0)<<31>>31;j=0;break d}case 3:{b[c[E>>2]>>1]=e;j=0;break d}case 4:{a[c[E>>2]>>0]=e;j=0;break d}case 6:{c[c[E>>2]>>2]=e;j=0;break d}case 7:{j=c[E>>2]|0;c[j>>2]=e;c[j+4>>2]=((e|0)<0)<<31>>31;j=0;break d}default:{j=0;break d}}case 112:{k=120;j=j>>>0>8?j:8;m=q|8;v=67;break}case 88:case 120:{m=q;v=67;break}case 111:{m=E;k=c[m>>2]|0;m=c[m+4>>2]|0;p=Ha(k,m,x)|0;v=z-p|0;n=0;o=2781;j=(q&8|0)==0|(j|0)>(v|0)?j:v+1|0;v=73;break}case 105:case 100:{m=E;k=c[m>>2]|0;m=c[m+4>>2]|0;if((m|0)<0){k=fb(0,0,k|0,m|0)|0;m=y;n=E;c[n>>2]=k;c[n+4>>2]=m;n=1;o=2781;v=72;break e}else{n=(q&2049|0)!=0&1;o=(q&2048|0)==0?((q&1|0)==0?2781:2783):2782;v=72;break e}}case 117:{m=E;n=0;o=2781;k=c[m>>2]|0;m=c[m+4>>2]|0;v=72;break}case 99:{a[A>>0]=c[E>>2];r=A;n=0;o=2781;p=1;j=z;break}case 109:{k=Ja(c[(ua()|0)>>2]|0)|0;v=77;break}case 115:{k=c[E>>2]|0;k=(k|0)==0?2791:k;v=77;break}case 67:{c[G>>2]=c[E>>2];c[B>>2]=0;c[E>>2]=G;o=-1;v=81;break}case 83:{if(!j){Ka(d,32,t,0,q);j=0;v=91}else{o=j;v=81}break}case 65:case 71:case 70:case 69:case 97:case 103:case 102:case 101:{j=Ma(d,+h[E>>3],t,j,q,k)|0;break d}default:{r=s;n=0;o=2781;p=j;m=q;j=z}}while(0);f:do if((v|0)==67){s=E;r=c[s>>2]|0;s=c[s+4>>2]|0;p=Ga(r,s,x,k&32)|0;o=(m&8|0)==0|(r|0)==0&(s|0)==0;n=o?0:2;o=o?2781:2781+(k>>>4)|0;q=m;k=r;m=s;v=73}else if((v|0)==72){p=Ia(k,m,x)|0;v=73}else if((v|0)==77){v=0;s=xa(k,0,j)|0;q=(s|0)==0;r=k;n=0;o=2781;p=q?j:s-k|0;j=q?k+j|0:s}else if((v|0)==81){v=0;n=c[E>>2]|0;j=0;while(1){k=c[n>>2]|0;if(!k)break;k=La(H,k)|0;m=(k|0)<0;if(m|k>>>0>(o-j|0)>>>0){v=85;break}j=k+j|0;if(o>>>0>j>>>0)n=n+4|0;else break}if((v|0)==85){v=0;if(m){e=-1;break a}}Ka(d,32,t,j,q);if(!j){j=0;v=91}else{m=c[E>>2]|0;n=0;while(1){k=c[m>>2]|0;if(!k){v=91;break f}k=La(H,k)|0;n=k+n|0;if((n|0)>(j|0)){v=91;break f}Ca(d,H,k);if(n>>>0>=j>>>0){v=91;break}else m=m+4|0}}}while(0);if((v|0)==73){v=0;m=(k|0)!=0|(m|0)!=0;s=(j|0)!=0|m;m=z-p+((m^1)&1)|0;r=s?p:x;p=s?((j|0)>(m|0)?j:m):0;m=(j|0)>-1?q&-65537:q;j=z}else if((v|0)==91){v=0;Ka(d,32,t,j,q^8192);j=(t|0)>(j|0)?t:j;break}q=j-r|0;p=(p|0)<(q|0)?q:p;s=p+n|0;j=(t|0)<(s|0)?s:t;Ka(d,32,j,s,m);Ca(d,o,n);Ka(d,48,j,s,m^65536);Ka(d,48,p,q,0);Ca(d,r,q);Ka(d,32,j,s,m^8192)}while(0);m=u}g:do if((v|0)==94)if(!d)if(!m)e=0;else{e=1;while(1){j=c[i+(e<<2)>>2]|0;if(!j)break;Fa(g+(e<<3)|0,j,f);e=e+1|0;if(e>>>0>=10){e=1;break g}}while(1){if(c[i+(e<<2)>>2]|0){e=-1;break g}e=e+1|0;if(e>>>0>=10){e=1;break}}}while(0);l=I;return e|0}function Aa(a){a=a|0;return 0}function Ba(a){a=a|0;return}function Ca(a,b,d){a=a|0;b=b|0;d=d|0;if(!(c[a>>2]&32))_a(b,d,a)|0;return}function Da(a){a=a|0;return (a+-48|0)>>>0<10|0}function Ea(b){b=b|0;var d=0,e=0;if(!(Da(a[c[b>>2]>>0]|0)|0))d=0;else{d=0;do{e=c[b>>2]|0;d=(d*10|0)+-48+(a[e>>0]|0)|0;e=e+1|0;c[b>>2]=e}while((Da(a[e>>0]|0)|0)!=0)}return d|0}function Fa(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0.0;a:do if(b>>>0<=20)do switch(b|0){case 9:{e=(c[d>>2]|0)+(4-1)&~(4-1);b=c[e>>2]|0;c[d>>2]=e+4;c[a>>2]=b;break a}case 10:{e=(c[d>>2]|0)+(4-1)&~(4-1);b=c[e>>2]|0;c[d>>2]=e+4;e=a;c[e>>2]=b;c[e+4>>2]=((b|0)<0)<<31>>31;break a}case 11:{e=(c[d>>2]|0)+(4-1)&~(4-1);b=c[e>>2]|0;c[d>>2]=e+4;e=a;c[e>>2]=b;c[e+4>>2]=0;break a}case 12:{e=(c[d>>2]|0)+(8-1)&~(8-1);b=e;f=c[b>>2]|0;b=c[b+4>>2]|0;c[d>>2]=e+8;e=a;c[e>>2]=f;c[e+4>>2]=b;break a}case 13:{f=(c[d>>2]|0)+(4-1)&~(4-1);e=c[f>>2]|0;c[d>>2]=f+4;e=(e&65535)<<16>>16;f=a;c[f>>2]=e;c[f+4>>2]=((e|0)<0)<<31>>31;break a}case 14:{f=(c[d>>2]|0)+(4-1)&~(4-1);e=c[f>>2]|0;c[d>>2]=f+4;f=a;c[f>>2]=e&65535;c[f+4>>2]=0;break a}case 15:{f=(c[d>>2]|0)+(4-1)&~(4-1);e=c[f>>2]|0;c[d>>2]=f+4;e=(e&255)<<24>>24;f=a;c[f>>2]=e;c[f+4>>2]=((e|0)<0)<<31>>31;break a}case 16:{f=(c[d>>2]|0)+(4-1)&~(4-1);e=c[f>>2]|0;c[d>>2]=f+4;f=a;c[f>>2]=e&255;c[f+4>>2]=0;break a}case 17:{f=(c[d>>2]|0)+(8-1)&~(8-1);g=+h[f>>3];c[d>>2]=f+8;h[a>>3]=g;break a}case 18:{f=(c[d>>2]|0)+(8-1)&~(8-1);g=+h[f>>3];c[d>>2]=f+8;h[a>>3]=g;break a}default:break a}while(0);while(0);return}function Ga(b,c,e,f){b=b|0;c=c|0;e=e|0;f=f|0;if(!((b|0)==0&(c|0)==0))do{e=e+-1|0;a[e>>0]=d[480+(b&15)>>0]|0|f;b=jb(b|0,c|0,4)|0;c=y}while(!((b|0)==0&(c|0)==0));return e|0}function Ha(b,c,d){b=b|0;c=c|0;d=d|0;if(!((b|0)==0&(c|0)==0))do{d=d+-1|0;a[d>>0]=b&7|48;b=jb(b|0,c|0,3)|0;c=y}while(!((b|0)==0&(c|0)==0));return d|0}function Ia(b,c,d){b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0;if(c>>>0>0|(c|0)==0&b>>>0>4294967295){do{e=b;b=ib(b|0,c|0,10,0)|0;f=c;c=y;g=db(b|0,c|0,10,0)|0;g=fb(e|0,f|0,g|0,y|0)|0;d=d+-1|0;a[d>>0]=g&255|48}while(f>>>0>9|(f|0)==9&e>>>0>4294967295);c=b}else c=b;if(c)do{g=c;c=(c>>>0)/10|0;d=d+-1|0;a[d>>0]=g-(c*10|0)|48}while(g>>>0>=10);return d|0}function Ja(a){a=a|0;return Ua(a,c[(Ta()|0)+188>>2]|0)|0}function Ka(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;var f=0,g=0;g=l;l=l+256|0;f=g;if((c|0)>(d|0)&(e&73728|0)==0){e=c-d|0;nb(f|0,b<<24>>24|0,(e>>>0<256?e:256)|0)|0;if(e>>>0>255){b=c-d|0;do{Ca(a,f,256);e=e+-256|0}while(e>>>0>255);e=b&255}Ca(a,f,e)}l=g;return}function La(a,b){a=a|0;b=b|0;if(!a)a=0;else a=Qa(a,b,0)|0;return a|0}function Ma(b,e,f,g,h,i){b=b|0;e=+e;f=f|0;g=g|0;h=h|0;i=i|0;var j=0,k=0,m=0,n=0,o=0,p=0,q=0,r=0.0,s=0,t=0,u=0,v=0,w=0,x=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0;H=l;l=l+560|0;m=H+32|0;v=H+536|0;G=H;F=G;n=H+540|0;c[v>>2]=0;E=n+12|0;Na(e)|0;j=y;if((j|0)<0){e=-e;Na(e)|0;D=1;C=2798;j=y}else{D=(h&2049|0)!=0&1;C=(h&2048|0)==0?((h&1|0)==0?2799:2804):2801}do if(0==0&(j&2146435072|0)==2146435072){G=(i&32|0)!=0;j=D+3|0;Ka(b,32,f,j,h&-65537);Ca(b,C,D);Ca(b,e!=e|0.0!=0.0?(G?2825:2829):G?2817:2821,3);Ka(b,32,f,j,h^8192)}else{r=+Oa(e,v)*2.0;j=r!=0.0;if(j)c[v>>2]=(c[v>>2]|0)+-1;u=i|32;if((u|0)==97){p=i&32;s=(p|0)==0?C:C+9|0;q=D|2;j=12-g|0;do if(!(g>>>0>11|(j|0)==0)){e=8.0;do{j=j+-1|0;e=e*16.0}while((j|0)!=0);if((a[s>>0]|0)==45){e=-(e+(-r-e));break}else{e=r+e-e;break}}else e=r;while(0);k=c[v>>2]|0;j=(k|0)<0?0-k|0:k;j=Ia(j,((j|0)<0)<<31>>31,E)|0;if((j|0)==(E|0)){j=n+11|0;a[j>>0]=48}a[j+-1>>0]=(k>>31&2)+43;o=j+-2|0;a[o>>0]=i+15;k=(g|0)<1;m=(h&8|0)==0;n=G;do{D=~~e;j=n+1|0;a[n>>0]=p|d[480+D>>0];e=(e-+(D|0))*16.0;if((j-F|0)==1?!(m&(k&e==0.0)):0){a[j>>0]=46;n=n+2|0}else n=j}while(e!=0.0);if((g|0)!=0?(-2-F+n|0)<(g|0):0){k=E;m=o;j=g+2+k-m|0}else{k=E;m=o;j=k-F-m+n|0}E=j+q|0;Ka(b,32,f,E,h);Ca(b,s,q);Ka(b,48,f,E,h^65536);F=n-F|0;Ca(b,G,F);G=k-m|0;Ka(b,48,j-(F+G)|0,0,0);Ca(b,o,G);Ka(b,32,f,E,h^8192);j=E;break}k=(g|0)<0?6:g;if(j){j=(c[v>>2]|0)+-28|0;c[v>>2]=j;e=r*268435456.0}else{e=r;j=c[v>>2]|0}B=(j|0)<0?m:m+288|0;m=B;do{z=~~e>>>0;c[m>>2]=z;m=m+4|0;e=(e-+(z>>>0))*1.0e9}while(e!=0.0);z=B;if((j|0)>0){p=B;while(1){o=(j|0)<29?j:29;j=m+-4|0;if(j>>>0>=p>>>0){n=0;do{t=kb(c[j>>2]|0,0,o|0)|0;t=eb(t|0,y|0,n|0,0)|0;w=y;n=ib(t|0,w|0,1e9,0)|0;x=db(n|0,y|0,1e9,0)|0;x=fb(t|0,w|0,x|0,y|0)|0;c[j>>2]=x;j=j+-4|0}while(j>>>0>=p>>>0);if(n){x=p+-4|0;c[x>>2]=n;n=x}else n=p}else n=p;a:do if(m>>>0>n>>>0){j=m;while(1){m=j+-4|0;if(c[m>>2]|0){m=j;break a}if(m>>>0>n>>>0)j=m;else break}}while(0);j=(c[v>>2]|0)-o|0;c[v>>2]=j;if((j|0)>0)p=n;else break}}else n=B;if((j|0)<0){g=((k+25|0)/9|0)+1|0;t=(u|0)==102;do{s=0-j|0;s=(s|0)<9?s:9;if(n>>>0>>0){o=(1<>>s;q=0;j=n;do{x=c[j>>2]|0;c[j>>2]=(x>>>s)+q;q=N(x&o,p)|0;j=j+4|0}while(j>>>0>>0);n=(c[n>>2]|0)==0?n+4|0:n;if(q){c[m>>2]=q;m=m+4|0}}else n=(c[n>>2]|0)==0?n+4|0:n;j=t?B:n;m=(m-j>>2|0)>(g|0)?j+(g<<2)|0:m;j=(c[v>>2]|0)+s|0;c[v>>2]=j}while((j|0)<0);t=n}else t=n;if(t>>>0>>0){j=(z-t>>2)*9|0;o=c[t>>2]|0;if(o>>>0>=10){n=10;do{n=n*10|0;j=j+1|0}while(o>>>0>=n>>>0)}}else j=0;w=(u|0)==103;x=(k|0)!=0;n=k-((u|0)==102?0:j)+((x&w)<<31>>31)|0;if((n|0)<(((m-z>>2)*9|0)+-9|0)){v=n+9216|0;n=(v|0)/9|0;g=B+4+(n+-1024<<2)|0;n=v-(n*9|0)|0;if((n|0)<8){o=10;while(1){o=o*10|0;if((n|0)<7)n=n+1|0;else break}}else o=10;q=c[g>>2]|0;n=(q>>>0)/(o>>>0)|0;s=q-(N(n,o)|0)|0;p=(g+4|0)==(m|0);if(!(p&(s|0)==0)){r=(n&1|0)==0?9007199254740992.0:9007199254740994.0;v=o>>>1;e=s>>>0>>0?.5:p&(s|0)==(v|0)?1.0:1.5;if(D){v=(a[C>>0]|0)==45;e=v?-e:e;r=v?-r:r}n=q-s|0;c[g>>2]=n;if(r+e!=r){v=n+o|0;c[g>>2]=v;if(v>>>0>999999999){o=g;j=t;while(1){n=o+-4|0;c[o>>2]=0;if(n>>>0>>0){j=j+-4|0;c[j>>2]=0}v=(c[n>>2]|0)+1|0;c[n>>2]=v;if(v>>>0>999999999)o=n;else{o=j;break}}}else{n=g;o=t}j=(z-o>>2)*9|0;q=c[o>>2]|0;if(q>>>0>=10){p=10;do{p=p*10|0;j=j+1|0}while(q>>>0>=p>>>0)}}else{n=g;o=t}}else{n=g;o=t}v=n+4|0;m=m>>>0>v>>>0?v:m}else o=t;g=0-j|0;b:do if(m>>>0>o>>>0)while(1){n=m+-4|0;if(c[n>>2]|0){v=m;u=1;break b}if(n>>>0>o>>>0)m=n;else{v=n;u=0;break}}else{v=m;u=0}while(0);do if(w){k=k+((x^1)&1)|0;if((k|0)>(j|0)&(j|0)>-5){p=i+-1|0;k=k+-1-j|0}else{p=i+-2|0;k=k+-1|0}if(!(h&8)){if(u?(A=c[v+-4>>2]|0,(A|0)!=0):0)if(!((A>>>0)%10|0)){n=0;m=10;do{m=m*10|0;n=n+1|0}while(!((A>>>0)%(m>>>0)|0|0))}else n=0;else n=9;m=((v-z>>2)*9|0)+-9|0;if((p|32|0)==102){i=m-n|0;i=(i|0)>0?i:0;k=(k|0)<(i|0)?k:i;break}else{i=m+j-n|0;i=(i|0)>0?i:0;k=(k|0)<(i|0)?k:i;break}}}else p=i;while(0);t=(k|0)!=0;q=t?1:h>>>3&1;s=(p|32|0)==102;if(s){w=0;j=(j|0)>0?j:0}else{m=(j|0)<0?g:j;m=Ia(m,((m|0)<0)<<31>>31,E)|0;n=E;if((n-m|0)<2)do{m=m+-1|0;a[m>>0]=48}while((n-m|0)<2);a[m+-1>>0]=(j>>31&2)+43;j=m+-2|0;a[j>>0]=p;w=j;j=n-j|0}j=D+1+k+q+j|0;Ka(b,32,f,j,h);Ca(b,C,D);Ka(b,48,f,j,h^65536);if(s){q=o>>>0>B>>>0?B:o;s=G+9|0;o=s;p=G+8|0;n=q;do{m=Ia(c[n>>2]|0,0,s)|0;if((n|0)==(q|0)){if((m|0)==(s|0)){a[p>>0]=48;m=p}}else if(m>>>0>G>>>0){nb(G|0,48,m-F|0)|0;do m=m+-1|0;while(m>>>0>G>>>0)}Ca(b,m,o-m|0);n=n+4|0}while(n>>>0<=B>>>0);if(!((h&8|0)==0&(t^1)))Ca(b,2833,1);if(n>>>0>>0&(k|0)>0)while(1){m=Ia(c[n>>2]|0,0,s)|0;if(m>>>0>G>>>0){nb(G|0,48,m-F|0)|0;do m=m+-1|0;while(m>>>0>G>>>0)}Ca(b,m,(k|0)<9?k:9);n=n+4|0;m=k+-9|0;if(!(n>>>0>>0&(k|0)>9)){k=m;break}else k=m}Ka(b,48,k+9|0,9,0)}else{v=u?v:o+4|0;if(o>>>0>>0&(k|0)>-1){g=G+9|0;t=(h&8|0)==0;u=g;q=0-F|0;s=G+8|0;p=o;do{m=Ia(c[p>>2]|0,0,g)|0;if((m|0)==(g|0)){a[s>>0]=48;m=s}do if((p|0)==(o|0)){n=m+1|0;Ca(b,m,1);if(t&(k|0)<1){m=n;break}Ca(b,2833,1);m=n}else{if(m>>>0<=G>>>0)break;nb(G|0,48,m+q|0)|0;do m=m+-1|0;while(m>>>0>G>>>0)}while(0);F=u-m|0;Ca(b,m,(k|0)>(F|0)?F:k);k=k-F|0;p=p+4|0}while(p>>>0>>0&(k|0)>-1)}Ka(b,48,k+18|0,18,0);Ca(b,w,E-w|0)}Ka(b,32,f,j,h^8192)}while(0);l=H;return ((j|0)<(f|0)?f:j)|0}function Na(a){a=+a;var b=0;h[j>>3]=a;b=c[j>>2]|0;y=c[j+4>>2]|0;return b|0}function Oa(a,b){a=+a;b=b|0;return +(+Pa(a,b))}function Pa(a,b){a=+a;b=b|0;var d=0,e=0,f=0;h[j>>3]=a;d=c[j>>2]|0;e=c[j+4>>2]|0;f=jb(d|0,e|0,52)|0;switch(f&2047){case 0:{if(a!=0.0){a=+Pa(a*18446744073709551616.0,b);d=(c[b>>2]|0)+-64|0}else d=0;c[b>>2]=d;break}case 2047:break;default:{c[b>>2]=(f&2047)+-1022;c[j>>2]=d;c[j+4>>2]=e&-2146435073|1071644672;a=+h[j>>3]}}return +a}function Qa(b,d,e){b=b|0;d=d|0;e=e|0;do if(b){if(d>>>0<128){a[b>>0]=d;b=1;break}if(!(c[c[(Ra()|0)+188>>2]>>2]|0))if((d&-128|0)==57216){a[b>>0]=d;b=1;break}else{c[(ua()|0)>>2]=84;b=-1;break}if(d>>>0<2048){a[b>>0]=d>>>6|192;a[b+1>>0]=d&63|128;b=2;break}if(d>>>0<55296|(d&-8192|0)==57344){a[b>>0]=d>>>12|224;a[b+1>>0]=d>>>6&63|128;a[b+2>>0]=d&63|128;b=3;break}if((d+-65536|0)>>>0<1048576){a[b>>0]=d>>>18|240;a[b+1>>0]=d>>>12&63|128;a[b+2>>0]=d>>>6&63|128;a[b+3>>0]=d&63|128;b=4;break}else{c[(ua()|0)>>2]=84;b=-1;break}}else b=1;while(0);return b|0}function Ra(){return Sa()|0}function Sa(){return 2528}function Ta(){return Sa()|0}function Ua(b,e){b=b|0;e=e|0;var f=0,g=0;f=0;while(1){if((d[496+f>>0]|0)==(b|0)){g=4;break}f=f+1|0;if((f|0)==87){b=87;g=5;break}}if((g|0)==4)if(!f)f=592;else{b=f;g=5}if((g|0)==5){f=592;do{do{g=f;f=f+1|0}while((a[g>>0]|0)!=0);b=b+-1|0}while((b|0)!=0)}return Va(f,c[e+20>>2]|0)|0}function Va(a,b){a=a|0;b=b|0;return Wa(a,b)|0}function Wa(a,b){a=a|0;b=b|0;if(!b)b=0;else b=Xa(c[b>>2]|0,c[b+4>>2]|0,a)|0;return ((b|0)==0?a:b)|0}function Xa(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0;o=(c[b>>2]|0)+1794895138|0;h=Ya(c[b+8>>2]|0,o)|0;f=Ya(c[b+12>>2]|0,o)|0;g=Ya(c[b+16>>2]|0,o)|0;a:do if((h>>>0>>2>>>0?(n=d-(h<<2)|0,f>>>0>>0&g>>>0>>0):0)?((g|f)&3|0)==0:0){n=f>>>2;m=g>>>2;l=0;while(1){j=h>>>1;k=l+j|0;i=k<<1;g=i+n|0;f=Ya(c[b+(g<<2)>>2]|0,o)|0;g=Ya(c[b+(g+1<<2)>>2]|0,o)|0;if(!(g>>>0>>0&f>>>0<(d-g|0)>>>0)){f=0;break a}if(a[b+(g+f)>>0]|0){f=0;break a}f=Za(e,b+g|0)|0;if(!f)break;f=(f|0)<0;if((h|0)==1){f=0;break a}l=f?l:k;h=f?j:h-j|0}f=i+m|0;g=Ya(c[b+(f<<2)>>2]|0,o)|0;f=Ya(c[b+(f+1<<2)>>2]|0,o)|0;if(f>>>0>>0&g>>>0<(d-f|0)>>>0)f=(a[b+(f+g)>>0]|0)==0?b+f|0:0;else f=0}else f=0;while(0);return f|0}function Ya(a,b){a=a|0;b=b|0;var c=0;c=lb(a|0)|0;return ((b|0)==0?a:c)|0}function Za(b,c){b=b|0;c=c|0;var d=0,e=0;d=a[b>>0]|0;e=a[c>>0]|0;if(d<<24>>24==0?1:d<<24>>24!=e<<24>>24)b=e;else{do{b=b+1|0;c=c+1|0;d=a[b>>0]|0;e=a[c>>0]|0}while(!(d<<24>>24==0?1:d<<24>>24!=e<<24>>24));b=e}return (d&255)-(b&255)|0}function _a(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0,j=0;f=e+16|0;g=c[f>>2]|0;if(!g)if(!($a(e)|0)){g=c[f>>2]|0;h=5}else f=0;else h=5;a:do if((h|0)==5){j=e+20|0;i=c[j>>2]|0;f=i;if((g-i|0)>>>0>>0){f=fa[c[e+36>>2]&3](e,b,d)|0;break}b:do if((a[e+75>>0]|0)<0|(d|0)==0){h=0;g=b}else{i=d;while(1){g=i+-1|0;if((a[b+g>>0]|0)==10)break;if(!g){h=0;g=b;break b}else i=g}f=fa[c[e+36>>2]&3](e,b,i)|0;if(f>>>0>>0)break a;h=i;g=b+i|0;d=d-i|0;f=c[j>>2]|0}while(0);mb(f|0,g|0,d|0)|0;c[j>>2]=(c[j>>2]|0)+d;f=h+d|0}while(0);return f|0}function $a(b){b=b|0;var d=0,e=0;d=b+74|0;e=a[d>>0]|0;a[d>>0]=e+255|e;d=c[b>>2]|0;if(!(d&8)){c[b+8>>2]=0;c[b+4>>2]=0;e=c[b+44>>2]|0;c[b+28>>2]=e;c[b+20>>2]=e;c[b+16>>2]=e+(c[b+48>>2]|0);b=0}else{c[b>>2]=d|32;b=-1}return b|0}function ab(a,b){a=a|0;b=b|0;var d=0,e=0;d=l;l=l+16|0;e=d;c[e>>2]=b;b=ya(c[600]|0,a,e)|0;l=d;return b|0}function bb(){}function cb(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;f=a&65535;e=b&65535;c=N(e,f)|0;d=a>>>16;a=(c>>>16)+(N(e,d)|0)|0;e=b>>>16;b=N(e,f)|0;return (y=(a>>>16)+(N(e,d)|0)+(((a&65535)+b|0)>>>16)|0,a+b<<16|c&65535|0)|0}function db(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0;e=a;f=c;c=cb(e,f)|0;a=y;return (y=(N(b,f)|0)+(N(d,e)|0)+a|a&0,c|0|0)|0}function eb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;c=a+c>>>0;return (y=b+d+(c>>>0>>0|0)>>>0,c|0)|0}function fb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;d=b-d-(c>>>0>a>>>0|0)>>>0;return (y=d,a-c>>>0|0)|0}function gb(a){a=a|0;return (a?31-(Q(a^a-1)|0)|0:32)|0}function hb(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;l=a;j=b;k=j;h=d;n=e;i=n;if(!k){g=(f|0)!=0;if(!i){if(g){c[f>>2]=(l>>>0)%(h>>>0);c[f+4>>2]=0}n=0;f=(l>>>0)/(h>>>0)>>>0;return (y=n,f)|0}else{if(!g){n=0;f=0;return (y=n,f)|0}c[f>>2]=a|0;c[f+4>>2]=b&0;n=0;f=0;return (y=n,f)|0}}g=(i|0)==0;do if(h){if(!g){g=(Q(i|0)|0)-(Q(k|0)|0)|0;if(g>>>0<=31){m=g+1|0;i=31-g|0;b=g-31>>31;h=m;a=l>>>(m>>>0)&b|k<>>(m>>>0)&b;g=0;i=l<>2]=a|0;c[f+4>>2]=j|b&0;n=0;f=0;return (y=n,f)|0}g=h-1|0;if(g&h|0){i=(Q(h|0)|0)+33-(Q(k|0)|0)|0;p=64-i|0;m=32-i|0;j=m>>31;o=i-32|0;b=o>>31;h=i;a=m-1>>31&k>>>(o>>>0)|(k<>>(i>>>0))&b;b=b&k>>>(i>>>0);g=l<>>(o>>>0))&j|l<>31;break}if(f|0){c[f>>2]=g&l;c[f+4>>2]=0}if((h|0)==1){o=j|b&0;p=a|0|0;return (y=o,p)|0}else{p=gb(h|0)|0;o=k>>>(p>>>0)|0;p=k<<32-p|l>>>(p>>>0)|0;return (y=o,p)|0}}else{if(g){if(f|0){c[f>>2]=(k>>>0)%(h>>>0);c[f+4>>2]=0}o=0;p=(k>>>0)/(h>>>0)>>>0;return (y=o,p)|0}if(!l){if(f|0){c[f>>2]=0;c[f+4>>2]=(k>>>0)%(i>>>0)}o=0;p=(k>>>0)/(i>>>0)>>>0;return (y=o,p)|0}g=i-1|0;if(!(g&i)){if(f|0){c[f>>2]=a|0;c[f+4>>2]=g&k|b&0}o=0;p=k>>>((gb(i|0)|0)>>>0);return (y=o,p)|0}g=(Q(i|0)|0)-(Q(k|0)|0)|0;if(g>>>0<=30){b=g+1|0;i=31-g|0;h=b;a=k<>>(b>>>0);b=k>>>(b>>>0);g=0;i=l<>2]=a|0;c[f+4>>2]=j|b&0;o=0;p=0;return (y=o,p)|0}while(0);if(!h){k=i;j=0;i=0}else{m=d|0|0;l=n|e&0;k=eb(m|0,l|0,-1,-1)|0;d=y;j=i;i=0;do{e=j;j=g>>>31|j<<1;g=i|g<<1;e=a<<1|e>>>31|0;n=a>>>31|b<<1|0;fb(k|0,d|0,e|0,n|0)|0;p=y;o=p>>31|((p|0)<0?-1:0)<<1;i=o&1;a=fb(e|0,n|0,o&m|0,(((p|0)<0?-1:0)>>31|((p|0)<0?-1:0)<<1)&l|0)|0;b=y;h=h-1|0}while((h|0)!=0);k=j;j=0}h=0;if(f|0){c[f>>2]=a;c[f+4>>2]=b}o=(g|0)>>>31|(k|h)<<1|(h<<1|g>>>31)&0|j;p=(g<<1|0>>>31)&-2|i;return (y=o,p)|0}function ib(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return hb(a,b,c,d,0)|0}function jb(a,b,c){a=a|0;b=b|0;c=c|0;if((c|0)<32){y=b>>>c;return a>>>c|(b&(1<>>c-32|0}function kb(a,b,c){a=a|0;b=b|0;c=c|0;if((c|0)<32){y=b<>>32-c;return a<>8&255)<<16|(a>>16&255)<<8|a>>>24|0}function mb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0;if((e|0)>=8192)return ba(b|0,d|0,e|0)|0;h=b|0;g=b+e|0;if((b&3)==(d&3)){while(b&3){if(!e)return h|0;a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0;e=e-1|0}e=g&-4|0;f=e-64|0;while((b|0)<=(f|0)){c[b>>2]=c[d>>2];c[b+4>>2]=c[d+4>>2];c[b+8>>2]=c[d+8>>2];c[b+12>>2]=c[d+12>>2];c[b+16>>2]=c[d+16>>2];c[b+20>>2]=c[d+20>>2];c[b+24>>2]=c[d+24>>2];c[b+28>>2]=c[d+28>>2];c[b+32>>2]=c[d+32>>2];c[b+36>>2]=c[d+36>>2];c[b+40>>2]=c[d+40>>2];c[b+44>>2]=c[d+44>>2];c[b+48>>2]=c[d+48>>2];c[b+52>>2]=c[d+52>>2];c[b+56>>2]=c[d+56>>2];c[b+60>>2]=c[d+60>>2];b=b+64|0;d=d+64|0}while((b|0)<(e|0)){c[b>>2]=c[d>>2];b=b+4|0;d=d+4|0}}else{e=g-4|0;while((b|0)<(e|0)){a[b>>0]=a[d>>0]|0;a[b+1>>0]=a[d+1>>0]|0;a[b+2>>0]=a[d+2>>0]|0;a[b+3>>0]=a[d+3>>0]|0;b=b+4|0;d=d+4|0}}while((b|0)<(g|0)){a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0}return h|0}function nb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0;h=b+e|0;d=d&255;if((e|0)>=67){while(b&3){a[b>>0]=d;b=b+1|0}f=h&-4|0;g=f-64|0;i=d|d<<8|d<<16|d<<24;while((b|0)<=(g|0)){c[b>>2]=i;c[b+4>>2]=i;c[b+8>>2]=i;c[b+12>>2]=i;c[b+16>>2]=i;c[b+20>>2]=i;c[b+24>>2]=i;c[b+28>>2]=i;c[b+32>>2]=i;c[b+36>>2]=i;c[b+40>>2]=i;c[b+44>>2]=i;c[b+48>>2]=i;c[b+52>>2]=i;c[b+56>>2]=i;c[b+60>>2]=i;b=b+64|0}while((b|0)<(f|0)){c[b>>2]=i;b=b+4|0}}while((b|0)<(h|0)){a[b>>0]=d;b=b+1|0}return h-e|0}function ob(a){a=a|0;var b=0,d=0;d=c[i>>2]|0;b=d+a|0;if((a|0)>0&(b|0)<(d|0)|(b|0)<0){V()|0;Y(12);return -1}c[i>>2]=b;if((b|0)>(U()|0)?(T()|0)==0:0){c[i>>2]=d;Y(12);return -1}return d|0}function pb(a,b){a=a|0;b=b|0;return ea[a&1](b|0)|0}function qb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return fa[a&3](b|0,c|0,d|0)|0}function rb(a){a=a|0;R(0);return 0}function sb(a,b,c){a=a|0;b=b|0;c=c|0;R(1);return 0} 5 | 6 | // EMSCRIPTEN_END_FUNCS 7 | var ea=[rb,qa];var fa=[sb,ra,sa,va];return{___errno_location:ua,___muldi3:db,___udivdi3:ib,_bitshift64Lshr:jb,_bitshift64Shl:kb,_free:pa,_i64Add:eb,_i64Subtract:fb,_llvm_bswap_i32:lb,_main:na,_malloc:oa,_memcpy:mb,_memset:nb,_sbrk:ob,dynCall_ii:pb,dynCall_iiii:qb,establishStackSpace:ja,getTempRet0:ma,runPostSets:bb,setTempRet0:la,setThrew:ka,stackAlloc:ga,stackRestore:ia,stackSave:ha}}) 8 | 9 | 10 | // EMSCRIPTEN_END_ASM 11 | (Module.asmGlobalArg,Module.asmLibraryArg,buffer);var ___errno_location=Module["___errno_location"]=asm["___errno_location"];var ___muldi3=Module["___muldi3"]=asm["___muldi3"];var ___udivdi3=Module["___udivdi3"]=asm["___udivdi3"];var _bitshift64Lshr=Module["_bitshift64Lshr"]=asm["_bitshift64Lshr"];var _bitshift64Shl=Module["_bitshift64Shl"]=asm["_bitshift64Shl"];var _free=Module["_free"]=asm["_free"];var _i64Add=Module["_i64Add"]=asm["_i64Add"];var _i64Subtract=Module["_i64Subtract"]=asm["_i64Subtract"];var _llvm_bswap_i32=Module["_llvm_bswap_i32"]=asm["_llvm_bswap_i32"];var _main=Module["_main"]=asm["_main"];var _malloc=Module["_malloc"]=asm["_malloc"];var _memcpy=Module["_memcpy"]=asm["_memcpy"];var _memset=Module["_memset"]=asm["_memset"];var _sbrk=Module["_sbrk"]=asm["_sbrk"];var establishStackSpace=Module["establishStackSpace"]=asm["establishStackSpace"];var getTempRet0=Module["getTempRet0"]=asm["getTempRet0"];var runPostSets=Module["runPostSets"]=asm["runPostSets"];var setTempRet0=Module["setTempRet0"]=asm["setTempRet0"];var setThrew=Module["setThrew"]=asm["setThrew"];var stackAlloc=Module["stackAlloc"]=asm["stackAlloc"];var stackRestore=Module["stackRestore"]=asm["stackRestore"];var stackSave=Module["stackSave"]=asm["stackSave"];var dynCall_ii=Module["dynCall_ii"]=asm["dynCall_ii"];var dynCall_iiii=Module["dynCall_iiii"]=asm["dynCall_iiii"];Module["asm"]=asm;if(memoryInitializer){if(!isDataURI(memoryInitializer)){memoryInitializer=locateFile(memoryInitializer)}if(ENVIRONMENT_IS_NODE||ENVIRONMENT_IS_SHELL){var data=Module["readBinary"](memoryInitializer);HEAPU8.set(data,GLOBAL_BASE)}else{addRunDependency("memory initializer");var applyMemoryInitializer=(function(data){if(data.byteLength)data=new Uint8Array(data);HEAPU8.set(data,GLOBAL_BASE);if(Module["memoryInitializerRequest"])delete Module["memoryInitializerRequest"].response;removeRunDependency("memory initializer")});function doBrowserLoad(){Module["readAsync"](memoryInitializer,applyMemoryInitializer,(function(){throw"could not load memory initializer "+memoryInitializer}))}if(Module["memoryInitializerRequest"]){function useRequest(){var request=Module["memoryInitializerRequest"];var response=request.response;if(request.status!==200&&request.status!==0){console.warn("a problem seems to have happened with Module.memoryInitializerRequest, status: "+request.status+", retrying "+memoryInitializer);doBrowserLoad();return}applyMemoryInitializer(response)}if(Module["memoryInitializerRequest"].response){setTimeout(useRequest,0)}else{Module["memoryInitializerRequest"].addEventListener("load",useRequest)}}else{doBrowserLoad()}}}function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;var initialStackTop;var calledMain=false;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"])run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};Module["callMain"]=function callMain(args){args=args||[];ensureInitRuntime();var argc=args.length+1;var argv=stackAlloc((argc+1)*4);HEAP32[argv>>2]=allocateUTF8OnStack(Module["thisProgram"]);for(var i=1;i>2)+i]=allocateUTF8OnStack(args[i-1])}HEAP32[(argv>>2)+argc]=0;try{var ret=Module["_main"](argc,argv,0);exit(ret,true)}catch(e){if(e instanceof ExitStatus){return}else if(e=="SimulateInfiniteLoop"){Module["noExitRuntime"]=true;return}else{var toLog=e;if(e&&typeof e==="object"&&e.stack){toLog=[e,e.stack]}err("exception thrown: "+toLog);Module["quit"](1,e)}}finally{calledMain=true}};function run(args){args=args||Module["arguments"];if(runDependencies>0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();if(Module["_main"]&&shouldRunNow)Module["callMain"](args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout((function(){setTimeout((function(){Module["setStatus"]("")}),1);doRun()}),1)}else{doRun()}}Module["run"]=run;function exit(status,implicit){if(implicit&&Module["noExitRuntime"]&&status===0){return}if(Module["noExitRuntime"]){}else{ABORT=true;EXITSTATUS=status;STACKTOP=initialStackTop;exitRuntime();if(Module["onExit"])Module["onExit"](status)}Module["quit"](status,new ExitStatus(status))}function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}if(what!==undefined){out(what);err(what);what=JSON.stringify(what)}else{what=""}ABORT=true;EXITSTATUS=1;throw"abort("+what+"). Build with -s ASSERTIONS=1 for more info."}Module["abort"]=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=true;if(Module["noInitialRun"]){shouldRunNow=false}Module["noExitRuntime"]=true;run() 12 | 13 | 14 | 15 | 16 | --------------------------------------------------------------------------------