diff options
author | Alon Zakai <alonzakai@gmail.com> | 2019-04-11 14:01:51 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-11 14:01:51 -0700 |
commit | b769b4ede65eb014376b67f78ba5e6cb04e0cef8 (patch) | |
tree | f0e4925b2d2e3328750c3e7d0190b0801f211faf | |
parent | 4905c3d3b4be20a89920ea2a56c1868294e77b65 (diff) | |
download | binaryen-b769b4ede65eb014376b67f78ba5e6cb04e0cef8.tar.gz binaryen-b769b4ede65eb014376b67f78ba5e6cb04e0cef8.tar.bz2 binaryen-b769b4ede65eb014376b67f78ba5e6cb04e0cef8.zip |
Wasm2js refactoring (#1997)
Early work for #1929
* Leave core wasm module - the "asm.js function" - to Wasm2JSBuilder, and add Wasm2JSGlue which emits the code before and after that. Currently that's some ES6 code, but we may want to change that later.
* Add add AssertionEmitter class for the sole purpose of emitting modules + assertions for testing. This avoids some hacks from before like starting from index 1 (assuming the module at first position was already parsed and printed) and printing of the f32Equal etc. functions not at the very top (which was due to technical limitations before).
Logic-wise, there should be no visible change, except some whitespace and reodering, and that I made the exceptions print out the source of the assertion that failed from the wast:
-if (!check2()) fail2();
+if (!check2()) throw 'assertion failed: ( assert_return ( call add ( i32.const 1 ) ( i32.const 1 ) ) ( i32.const 2 ) )';
(fail2 etc. did not exist, and seems to just have given a unique number for each assertion?)
61 files changed, 698 insertions, 620 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 927cae574..2ae3da650 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -2468,13 +2468,16 @@ void BinaryenModulePrintAsmjs(BinaryenModuleRef module) { } Module* wasm = (Module*)module; - Wasm2JSBuilder::Flags builderFlags; - Wasm2JSBuilder wasm2js(builderFlags); + Wasm2JSBuilder::Flags flags; + Wasm2JSBuilder wasm2js(flags); Ref asmjs = wasm2js.processWasm(wasm); JSPrinter jser(true, true, asmjs); + Output out("", Flags::Text, Flags::Release); // stdout + Wasm2JSGlue glue(*wasm, out, flags, "asmFunc"); + glue.emitPre(); jser.printAst(); - - std::cout << jser.buffer; + std::cout << jser.buffer << std::endl; + glue.emitPost(); } int BinaryenModuleValidate(BinaryenModuleRef module) { diff --git a/src/support/base64.h b/src/support/base64.h new file mode 100644 index 000000000..0c87c37c1 --- /dev/null +++ b/src/support/base64.h @@ -0,0 +1,66 @@ +/* + * Copyright 2019 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef wasm_support_base64_h +#define wasm_support_base64_h + +#include <cassert> +#include <string> +#include <vector> + +inline std::string base64Encode(std::vector<char> &data) { + std::string ret; + size_t i = 0; + + const char* alphabet = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + while (i + 3 <= data.size()) { + uint32_t bits = + (((uint32_t)(uint8_t) data[i + 0]) << 16) | + (((uint32_t)(uint8_t) data[i + 1]) << 8) | + (((uint32_t)(uint8_t) data[i + 2]) << 0); + ret += alphabet[(bits >> 18) & 0x3f]; + ret += alphabet[(bits >> 12) & 0x3f]; + ret += alphabet[(bits >> 6) & 0x3f]; + ret += alphabet[(bits >> 0) & 0x3f]; + i += 3; + } + + if (i + 2 == data.size()) { + uint32_t bits = + (((uint32_t)(uint8_t) data[i + 0]) << 8) | + (((uint32_t)(uint8_t) data[i + 1]) << 0); + ret += alphabet[(bits >> 10) & 0x3f]; + ret += alphabet[(bits >> 4) & 0x3f]; + ret += alphabet[(bits << 2) & 0x3f]; + ret += '='; + } else if (i + 1 == data.size()) { + uint32_t bits = (uint32_t)(uint8_t) data[i + 0]; + ret += alphabet[(bits >> 2) & 0x3f]; + ret += alphabet[(bits << 4) & 0x3f]; + ret += '='; + ret += '='; + } else { + assert(i == data.size()); + } + + return ret; +} + +#endif // wasm_support_base64_h diff --git a/src/tools/wasm2js.cpp b/src/tools/wasm2js.cpp index 6a55c4483..76a724f86 100644 --- a/src/tools/wasm2js.cpp +++ b/src/tools/wasm2js.cpp @@ -28,8 +28,329 @@ using namespace cashew; using namespace wasm; +// helpers + +namespace { + +static void emitWasm(Module& wasm, Output& output, Wasm2JSBuilder::Flags flags, Name name) { + Wasm2JSBuilder wasm2js(flags); + auto js = wasm2js.processWasm(&wasm, name); + Wasm2JSGlue glue(wasm, output, flags, name); + glue.emitPre(); + JSPrinter jser(true, true, js); + jser.printAst(); + output << jser.buffer << std::endl; + glue.emitPost(); +} + +class AssertionEmitter { +public: + AssertionEmitter(Element& root, + SExpressionWasmBuilder& sexpBuilder, + Output& out, + Wasm2JSBuilder::Flags flags) : root(root), sexpBuilder(sexpBuilder), out(out), flags(flags) {} + + void emit(); + +private: + Element& root; + SExpressionWasmBuilder& sexpBuilder; + Output& out; + Wasm2JSBuilder::Flags flags; + + Ref emitAssertReturnFunc(Builder& wasmBuilder, + Element& e, + Name testFuncName, + Name asmModule); + Ref emitAssertReturnNanFunc(Builder& wasmBuilder, + Element& e, + Name testFuncName, + Name asmModule); + Ref emitAssertTrapFunc(Builder& wasmBuilder, + Element& e, + Name testFuncName, + Name asmModule); + bool isAssertHandled(Element& e); + void fixCalls(Ref asmjs, Name asmModule); + + Ref processFunction(Function* func) { + Wasm2JSBuilder sub(flags); + return sub.processFunction(nullptr, func); + } + + void emitFunction(Ref func) { + JSPrinter jser(true, true, func); + jser.printAst(); + out << jser.buffer << std::endl; + } +}; + +Ref AssertionEmitter::emitAssertReturnFunc(Builder& wasmBuilder, + Element& e, + Name testFuncName, + Name asmModule) { + Expression* actual = sexpBuilder.parseExpression(e[1]); + Expression* body = nullptr; + if (e.size() == 2) { + if (actual->type == none) { + body = wasmBuilder.blockify( + actual, + wasmBuilder.makeConst(Literal(uint32_t(1))) + ); + } else { + body = actual; + } + } else if (e.size() == 3) { + Expression* expected = sexpBuilder.parseExpression(e[2]); + Type resType = expected->type; + actual->type = resType; + switch (resType) { + case i32: + body = wasmBuilder.makeBinary(EqInt32, actual, expected); + break; + + case i64: + body = wasmBuilder.makeCall( + "i64Equal", + {actual, wasmBuilder.makeCall(WASM_FETCH_HIGH_BITS, {}, i32), expected}, + i32 + ); + break; + + case f32: { + body = wasmBuilder.makeCall("f32Equal", {actual, expected}, i32); + break; + } + case f64: { + body = wasmBuilder.makeCall("f64Equal", {actual, expected}, i32); + break; + } + + default: { + std::cerr << "Unhandled type in assert: " << resType << std::endl; + abort(); + } + } + } else { + assert(false && "Unexpected number of parameters in assert_return"); + } + std::unique_ptr<Function> testFunc( + wasmBuilder.makeFunction( + testFuncName, + std::vector<NameType>{}, + body->type, + std::vector<NameType>{}, + body + ) + ); + Ref jsFunc = processFunction(testFunc.get()); + fixCalls(jsFunc, asmModule); + emitFunction(jsFunc); + return jsFunc; +} + +Ref AssertionEmitter::emitAssertReturnNanFunc(Builder& wasmBuilder, + Element& e, + Name testFuncName, + Name asmModule) { + Expression* actual = sexpBuilder.parseExpression(e[1]); + Expression* body = wasmBuilder.makeCall("isNaN", {actual}, i32); + std::unique_ptr<Function> testFunc( + wasmBuilder.makeFunction( + testFuncName, + std::vector<NameType>{}, + body->type, + std::vector<NameType>{}, + body + ) + ); + Ref jsFunc = processFunction(testFunc.get()); + fixCalls(jsFunc, asmModule); + emitFunction(jsFunc); + return jsFunc; +} + +Ref AssertionEmitter::emitAssertTrapFunc(Builder& wasmBuilder, + Element& e, + Name testFuncName, + Name asmModule) { + Name innerFuncName("f"); + Expression* expr = sexpBuilder.parseExpression(e[1]); + std::unique_ptr<Function> exprFunc( + wasmBuilder.makeFunction(innerFuncName, + std::vector<NameType>{}, + expr->type, + std::vector<NameType>{}, + expr) + ); + IString expectedErr = e[2]->str(); + Ref innerFunc = processFunction(exprFunc.get()); + fixCalls(innerFunc, asmModule); + Ref outerFunc = ValueBuilder::makeFunction(testFuncName); + outerFunc[3]->push_back(innerFunc); + Ref tryBlock = ValueBuilder::makeBlock(); + ValueBuilder::appendToBlock(tryBlock, ValueBuilder::makeCall(innerFuncName)); + Ref catchBlock = ValueBuilder::makeBlock(); + ValueBuilder::appendToBlock( + catchBlock, + ValueBuilder::makeReturn( + ValueBuilder::makeCall( + ValueBuilder::makeDot( + ValueBuilder::makeName(IString("e")), + ValueBuilder::makeName(IString("message")), + ValueBuilder::makeName(IString("includes")) + ), + ValueBuilder::makeString(expectedErr) + ) + ) + ); + outerFunc[3]->push_back(ValueBuilder::makeTry( + tryBlock, + ValueBuilder::makeName((IString("e"))), + catchBlock)); + outerFunc[3]->push_back(ValueBuilder::makeReturn(ValueBuilder::makeInt(0))); + emitFunction(outerFunc); + return outerFunc; +} + +bool AssertionEmitter::isAssertHandled(Element& e) { + return e.isList() && e.size() >= 2 && e[0]->isStr() + && (e[0]->str() == Name("assert_return") || + e[0]->str() == Name("assert_return_nan") || + (flags.pedantic && e[0]->str() == Name("assert_trap"))) + && e[1]->isList() && e[1]->size() >= 2 && (*e[1])[0]->isStr() + && (*e[1])[0]->str() == Name("invoke"); +} + +void AssertionEmitter::fixCalls(Ref asmjs, Name asmModule) { + if (asmjs->isArray()) { + ArrayStorage& arr = asmjs->getArray(); + for (Ref& r : arr) { + fixCalls(r, asmModule); + } + if (arr.size() > 0 && arr[0]->isString() && arr[0]->getIString() == cashew::CALL) { + assert(arr.size() >= 2); + if (arr[1]->getIString() == "f32Equal" || + arr[1]->getIString() == "f64Equal" || + arr[1]->getIString() == "i64Equal" || + arr[1]->getIString() == "isNaN") { + // ... + } else if (arr[1]->getIString() == "Math_fround") { + arr[1]->setString("Math.fround"); + } else { + Ref fixed = ValueBuilder::makeDot(ValueBuilder::makeName(asmModule), + arr[1]->getIString()); + arr[1]->setArray(fixed->getArray()); + } + } + } + + if (asmjs->isAssign()) { + fixCalls(asmjs->asAssign()->target(), asmModule); + fixCalls(asmjs->asAssign()->value(), asmModule); + } + if (asmjs->isAssignName()) { + fixCalls(asmjs->asAssignName()->value(), asmModule); + } +} + +void AssertionEmitter::emit() { + // TODO: nan and infinity shouldn't be needed once literal asm.js code isn't + // generated + out << R"( + var nan = NaN; + var infinity = Infinity; + )"; + + // When equating floating point values in spec tests we want to use bitwise + // equality like wasm does. Unfortunately though NaN makes this tricky. JS + // implementations like Spidermonkey and JSC will canonicalize NaN loads from + // `Float32Array`, but V8 will not. This means that NaN representations are + // kind of all over the place and difficult to bitwise equate. + // + // To work around this problem we just use a small shim which considers all + // NaN representations equivalent and otherwise tests for bitwise equality. + out << R"( + function f32Equal(a, b) { + var i = new Int32Array(1); + var f = new Float32Array(i.buffer); + f[0] = a; + var ai = f[0]; + f[0] = b; + var bi = f[0]; + + return (isNaN(a) && isNaN(b)) || a == b; + } + + function f64Equal(a, b) { + var i = new Int32Array(2); + var f = new Float64Array(i.buffer); + f[0] = a; + var ai1 = i[0]; + var ai2 = i[1]; + f[0] = b; + var bi1 = i[0]; + var bi2 = i[1]; + + return (isNaN(a) && isNaN(b)) || (ai1 == bi1 && ai2 == bi2); + } + + function i64Equal(actual_lo, actual_hi, expected_lo, expected_hi) { + return actual_lo == (expected_lo | 0) && actual_hi == (expected_hi | 0); + } + )"; + + Builder wasmBuilder(sexpBuilder.getAllocator()); + Name asmModule = std::string("ret") + ASM_FUNC.str; + for (size_t i = 0; i < root.size(); ++i) { + Element& e = *root[i]; + if (e.isList() && e.size() >= 1 && e[0]->isStr() && e[0]->str() == Name("module")) { + std::stringstream funcNameS; + funcNameS << ASM_FUNC.c_str() << i; + std::stringstream moduleNameS; + moduleNameS << "ret" << ASM_FUNC.c_str() << i; + Name funcName(funcNameS.str().c_str()); + asmModule = Name(moduleNameS.str().c_str()); + Module wasm; + SExpressionWasmBuilder builder(wasm, e); + emitWasm(wasm, out, flags, funcName); + continue; + } + if (!isAssertHandled(e)) { + std::cerr << "skipping " << e << std::endl; + continue; + } + Name testFuncName(IString(("check" + std::to_string(i)).c_str(), false)); + bool isReturn = (e[0]->str() == Name("assert_return")); + bool isReturnNan = (e[0]->str() == Name("assert_return_nan")); + Element& testOp = *e[1]; + // Replace "invoke" with "call" + testOp[0]->setString(IString("call"), false, false); + // Need to claim dollared to get string as function target + testOp[1]->setString(testOp[1]->str(), /*dollared=*/true, false); + + if (isReturn) { + emitAssertReturnFunc(wasmBuilder, e, testFuncName, asmModule); + } else if (isReturnNan) { + emitAssertReturnNanFunc(wasmBuilder, e, testFuncName, asmModule); + } else { + emitAssertTrapFunc(wasmBuilder, e, testFuncName, asmModule); + } + + out << "if (!" + << testFuncName.str + << "()) throw 'assertion failed: " + << e + << "';\n"; + } +} + +} // anonymous namespace + +// Main + int main(int argc, const char *argv[]) { - Wasm2JSBuilder::Flags builderFlags; + Wasm2JSBuilder::Flags flags; ToolOptions options("wasm2js", "Transform .wasm/.wast files to asm.js"); options .add("--output", "-o", "Output file (stdout if not specified)", @@ -41,24 +362,24 @@ int main(int argc, const char *argv[]) { .add("--allow-asserts", "", "Allow compilation of .wast testing asserts", Options::Arguments::Zero, [&](Options* o, const std::string& argument) { - builderFlags.allowAsserts = true; + flags.allowAsserts = true; o->extra["asserts"] = "1"; }) .add("--pedantic", "", "Emulate WebAssembly trapping behavior", Options::Arguments::Zero, [&](Options* o, const std::string& argument) { - builderFlags.pedantic = true; + flags.pedantic = true; }) .add_positional("INFILE", Options::Arguments::One, [](Options *o, const std::string& argument) { o->extra["infile"] = argument; }); options.parse(argc, argv); - if (options.debug) builderFlags.debug = true; + if (options.debug) flags.debug = true; Element* root = nullptr; Module wasm; - Ref asmjs; + Ref js; std::unique_ptr<SExpressionParser> sexprParser; std::unique_ptr<SExpressionWasmBuilder> sexprBuilder; @@ -106,28 +427,13 @@ int main(int argc, const char *argv[]) { } } - if (options.debug) std::cerr << "asming..." << std::endl; - Wasm2JSBuilder wasm2js(builderFlags); - asmjs = wasm2js.processWasm(&wasm); - - if (!binaryInput) { - if (options.extra["asserts"] == "1") { - if (options.debug) std::cerr << "asserting..." << std::endl; - flattenAppend(asmjs, wasm2js.processAsserts(&wasm, *root, *sexprBuilder)); - } - } - - if (options.debug) { - std::cerr << "a-printing..." << std::endl; - asmjs->stringify(std::cout, true); - std::cout << '\n'; - } - if (options.debug) std::cerr << "j-printing..." << std::endl; - JSPrinter jser(true, true, asmjs); - jser.printAst(); Output output(options.extra["output"], Flags::Text, options.debug ? Flags::Debug : Flags::Release); - output << jser.buffer << std::endl; + if (!binaryInput && options.extra["asserts"] == "1") { + AssertionEmitter(*root, *sexprBuilder, output, flags).emit(); + } else { + emitWasm(wasm, output, flags, "asmFunc"); + } if (options.debug) std::cerr << "done." << std::endl; } diff --git a/src/wasm2js.h b/src/wasm2js.h index fde436722..c6970e3e8 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -15,8 +15,8 @@ */ // -// WebAssembly-to-asm.js translator. Uses the Emscripten optimizer -// infrastructure. +// WebAssembly-to-JS code translator. Converts wasm functions into +// valid JavaScript (with a somewhat asm.js-ish flavor). // #ifndef wasm_wasm2js_h @@ -40,6 +40,7 @@ #include "ir/names.h" #include "ir/utils.h" #include "passes/passes.h" +#include "support/base64.h" namespace wasm { @@ -87,15 +88,15 @@ static uint64_t constOffset(const T& segment) { } // -// Wasm2JSBuilder - converts a WebAssembly module into asm.js +// Wasm2JSBuilder - converts a WebAssembly module's functions into JS // -// In general, asm.js => wasm is very straightforward, as can +// In general, JS (asm.js) => wasm is very straightforward, as can // be seen in asm2wasm.h. Just a single pass, plus a little // state bookkeeping (breakStack, etc.), and a few after-the -// fact corrections for imports, etc. However, wasm => asm.js +// fact corrections for imports, etc. However, wasm => JS // is tricky because wasm has statements == expressions, or in // other words, things like `break` and `if` can show up -// in places where asm.js can't handle them, like inside an +// in places where JS can't handle them, like inside an // a loop's condition check. // // We therefore need the ability to lower an expression into @@ -154,14 +155,12 @@ public: void scanFunctionBody(Expression* curr); // The second pass on an expression: process it fully, generating - // asm.js + // JS // @param result Whether the context we are in receives a value, // and its type, or if not, then we can drop our return, // if we have one. Ref processFunctionBody(Module* m, Function* func, IString result); - Ref processAsserts(Module* wasm, Element& e, SExpressionWasmBuilder& sexpBuilder); - // Get a temp var. IString getTemp(Type type, Function* func) { IString ret; @@ -272,8 +271,6 @@ private: bool almostASM = false; - void addEsmImports(Ref ast, Module* wasm); - void addEsmExportsAndInstantiate(Ref ast, Module* wasm, Name funcName); void addBasics(Ref ast); void addFunctionImport(Ref ast, Function* import); void addGlobalImport(Ref ast, Global* import); @@ -282,25 +279,7 @@ private: void addGlobal(Ref ast, Global* global); void setNeedsAlmostASM(const char *reason); void addMemoryGrowthFuncs(Ref ast); - bool isAssertHandled(Element& e); - Ref makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder, - Module* wasm, - Builder& wasmBuilder, - Element& e, - Name testFuncName, - Name asmModule); - Ref makeAssertReturnNanFunc(SExpressionWasmBuilder& sexpBuilder, - Module* wasm, - Builder& wasmBuilder, - Element& e, - Name testFuncName, - Name asmModule); - Ref makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder, - Module* wasm, - Builder& wasmBuilder, - Element& e, - Name testFuncName, - Name asmModule); + Wasm2JSBuilder() = delete; Wasm2JSBuilder(const Wasm2JSBuilder &) = delete; Wasm2JSBuilder &operator=(const Wasm2JSBuilder&) = delete; @@ -315,7 +294,7 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) { // i64-to-i32 lowering pass. runner.add("remove-non-js-ops"); // Currently the i64-to-32 lowering pass requires that `flatten` be run before - // it produce correct code. For some more details about this see #1480 + // it to produce correct code. For some more details about this see #1480 runner.add("flatten"); runner.add("i64-to-i32-lowering"); runner.add("flatten"); @@ -335,10 +314,8 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) { #endif Ref ret = ValueBuilder::makeToplevel(); - addEsmImports(ret, wasm); Ref asmFunc = ValueBuilder::makeFunction(funcName); ret[1]->push_back(asmFunc); - addEsmExportsAndInstantiate(ret, wasm, funcName); ValueBuilder::appendArgumentToFunction(asmFunc, GLOBAL); ValueBuilder::appendArgumentToFunction(asmFunc, ENV); ValueBuilder::appendArgumentToFunction(asmFunc, BUFFER); @@ -409,204 +386,6 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) { return ret; } -void Wasm2JSBuilder::addEsmImports(Ref ast, Module* wasm) { - std::unordered_map<Name, Name> baseModuleMap; - - auto noteImport = [&](Name module, Name base) { - // Right now codegen requires a flat namespace going into the module, - // meaning we don't support importing the same name from multiple namespaces yet. - if (baseModuleMap.count(base) && baseModuleMap[base] != module) { - Fatal() << "the name " << base << " cannot be imported from " - << "two different modules yet\n"; - abort(); - } - baseModuleMap[base] = module; - - std::ostringstream out; - out << "import { " - << base.str - << " } from '" - << module.str - << "'"; - std::string os = out.str(); - Name name(os.c_str()); - flattenAppend(ast, ValueBuilder::makeName(name)); - }; - - ImportInfo imports(*wasm); - - ModuleUtils::iterImportedGlobals(*wasm, [&](Global* import) { - Fatal() << "non-function imports aren't supported yet\n"; - noteImport(import->module, import->base); - }); - ModuleUtils::iterImportedFunctions(*wasm, [&](Function* import) { - noteImport(import->module, import->base); - }); -} - -static std::string base64Encode(std::vector<char> &data) { - std::string ret; - size_t i = 0; - - const char* alphabet = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - - while (i + 3 <= data.size()) { - uint32_t bits = - (((uint32_t)(uint8_t) data[i + 0]) << 16) | - (((uint32_t)(uint8_t) data[i + 1]) << 8) | - (((uint32_t)(uint8_t) data[i + 2]) << 0); - ret += alphabet[(bits >> 18) & 0x3f]; - ret += alphabet[(bits >> 12) & 0x3f]; - ret += alphabet[(bits >> 6) & 0x3f]; - ret += alphabet[(bits >> 0) & 0x3f]; - i += 3; - } - - if (i + 2 == data.size()) { - uint32_t bits = - (((uint32_t)(uint8_t) data[i + 0]) << 8) | - (((uint32_t)(uint8_t) data[i + 1]) << 0); - ret += alphabet[(bits >> 10) & 0x3f]; - ret += alphabet[(bits >> 4) & 0x3f]; - ret += alphabet[(bits << 2) & 0x3f]; - ret += '='; - } else if (i + 1 == data.size()) { - uint32_t bits = (uint32_t)(uint8_t) data[i + 0]; - ret += alphabet[(bits >> 2) & 0x3f]; - ret += alphabet[(bits << 4) & 0x3f]; - ret += '='; - ret += '='; - } else { - assert(i == data.size()); - } - - return ret; -} - -void Wasm2JSBuilder::addEsmExportsAndInstantiate(Ref ast, Module *wasm, Name funcName) { - // Create an initial `ArrayBuffer` and populate it with static data. - // Currently we use base64 encoding to encode static data and we decode it at - // instantiation time. - // - // Note that the translation here expects that the lower values of this memory - // can be used for conversions, so make sure there's at least one page. - { - auto pages = wasm->memory.initial == 0 ? 1 : wasm->memory.initial.addr; - std::ostringstream out; - out << "const mem" << funcName.str << " = new ArrayBuffer(" - << pages * Memory::kPageSize - << ")"; - std::string os = out.str(); - IString name(os.c_str(), false); - flattenAppend(ast, ValueBuilder::makeName(name)); - } - - if (wasm->memory.segments.size() > 0) { - auto expr = R"( - function(mem) { - const _mem = new Uint8Array(mem); - return function(offset, s) { - if (typeof Buffer === 'undefined') { - const bytes = atob(s); - for (let i = 0; i < bytes.length; i++) - _mem[offset + i] = bytes.charCodeAt(i); - } else { - const bytes = Buffer.from(s, 'base64'); - for (let i = 0; i < bytes.length; i++) - _mem[offset + i] = bytes[i]; - } - } - } - )"; - - // const assign$name = ($expr)(mem$name); - std::ostringstream out; - out << "const assign" << funcName.str - << " = (" << expr << ")(mem" << funcName.str << ")"; - std::string os = out.str(); - IString name(os.c_str(), false); - flattenAppend(ast, ValueBuilder::makeName(name)); - } - for (auto& seg : wasm->memory.segments) { - assert(!seg.isPassive && "passive segments not implemented yet"); - std::ostringstream out; - out << "assign" << funcName.str << "(" - << constOffset(seg) - << ", \"" - << base64Encode(seg.data) - << "\")"; - std::string os = out.str(); - IString name(os.c_str(), false); - flattenAppend(ast, ValueBuilder::makeName(name)); - } - - // Actually invoke the `asmFunc` generated function, passing in all global - // values followed by all imports (imported via addEsmImports above) - std::ostringstream construct; - construct << "const ret" << funcName.str << " = " << funcName.str << "({" - << "Math," - << "Int8Array," - << "Uint8Array," - << "Int16Array," - << "Uint16Array," - << "Int32Array," - << "Uint32Array," - << "Float32Array," - << "Float64Array," - << "NaN," - << "Infinity" - << "}, {"; - - construct << "abort:function() { throw new Error('abort'); }"; - - ModuleUtils::iterImportedFunctions(*wasm, [&](Function* import) { - construct << "," << import->base.str; - }); - construct << "},mem" << funcName.str << ")"; - std::string sconstruct = construct.str(); - IString name(sconstruct.c_str(), false); - flattenAppend(ast, ValueBuilder::makeName(name)); - - if (flags.allowAsserts) { - return; - } - - // And now that we have our returned instance, export all our functions - // that are hanging off it. - for (auto& exp : wasm->exports) { - switch (exp->kind) { - case ExternalKind::Function: - case ExternalKind::Memory: - break; - - // Exported globals and function tables aren't supported yet - default: - continue; - } - std::ostringstream export_name; - for (auto *ptr = exp->name.str; *ptr; ptr++) { - if (*ptr == '-') { - export_name << '_'; - } else { - export_name << *ptr; - } - } - std::ostringstream out; - out << "export const " - << fromName(exp->name, NameScope::Top).str - << " = ret" - << funcName.str - << "." - << fromName(exp->name, NameScope::Top).str; - std::string os = out.str(); - IString name(os.c_str(), false); - flattenAppend(ast, ValueBuilder::makeName(name)); - } -} - void Wasm2JSBuilder::addBasics(Ref ast) { // heaps, var HEAP8 = new global.Int8Array(buffer); etc auto addHeap = [&](IString name, IString view) { @@ -2050,218 +1829,6 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, IString resul return ExpressionProcessor(this, m, func).visit(func->body, result); } -static void makeHelpers(Ref ret, Name funcName, Name moduleName, bool first) { - if (first) { - // TODO: nan and infinity shouldn't be needed once literal asm.js code isn't - // generated - flattenAppend(ret, ValueBuilder::makeName(R"( - var nan = NaN; - var infinity = Infinity; - )")); - - // When equating floating point values in spec tests we want to use bitwise - // equality like wasm does. Unfortunately though NaN makes this tricky. JS - // implementations like Spidermonkey and JSC will canonicalize NaN loads from - // `Float32Array`, but V8 will not. This means that NaN representations are - // kind of all over the place and difficult to bitwise equate. - // - // To work around this problem we just use a small shim which considers all - // NaN representations equivalent and otherwise tests for bitwise equality. - flattenAppend(ret, ValueBuilder::makeName(R"( - function f32Equal(a, b) { - var i = new Int32Array(1); - var f = new Float32Array(i.buffer); - f[0] = a; - var ai = f[0]; - f[0] = b; - var bi = f[0]; - - return (isNaN(a) && isNaN(b)) || a == b; - } - - function f64Equal(a, b) { - var i = new Int32Array(2); - var f = new Float64Array(i.buffer); - f[0] = a; - var ai1 = i[0]; - var ai2 = i[1]; - f[0] = b; - var bi1 = i[0]; - var bi2 = i[1]; - - return (isNaN(a) && isNaN(b)) || (ai1 == bi1 && ai2 == bi2); - } - - function i64Equal(actual_lo, actual_hi, expected_lo, expected_hi) { - return actual_lo == (expected_lo | 0) && actual_hi == (expected_hi | 0); - } - )")); - } -} - -static void prefixCalls(Ref asmjs, Name asmModule) { - if (asmjs->isArray()) { - ArrayStorage& arr = asmjs->getArray(); - for (Ref& r : arr) { - prefixCalls(r, asmModule); - } - if (arr.size() > 0 && arr[0]->isString() && arr[0]->getIString() == CALL) { - assert(arr.size() >= 2); - if (arr[1]->getIString() == "f32Equal" || - arr[1]->getIString() == "f64Equal" || - arr[1]->getIString() == "i64Equal" || - arr[1]->getIString() == "isNaN") { - // ... - } else if (arr[1]->getIString() == "Math_fround") { - arr[1]->setString("Math.fround"); - } else { - Ref prefixed = ValueBuilder::makeDot(ValueBuilder::makeName(asmModule), - arr[1]->getIString()); - arr[1]->setArray(prefixed->getArray()); - } - } - } - - if (asmjs->isAssign()) { - prefixCalls(asmjs->asAssign()->target(), asmModule); - prefixCalls(asmjs->asAssign()->value(), asmModule); - } - if (asmjs->isAssignName()) { - prefixCalls(asmjs->asAssignName()->value(), asmModule); - } -} - -Ref Wasm2JSBuilder::makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder, - Module* wasm, - Builder& wasmBuilder, - Element& e, - Name testFuncName, - Name asmModule) { - Expression* actual = sexpBuilder.parseExpression(e[1]); - Expression* body = nullptr; - if (e.size() == 2) { - if (actual->type == none) { - body = wasmBuilder.blockify( - actual, - wasmBuilder.makeConst(Literal(uint32_t(1))) - ); - } else { - body = actual; - } - } else if (e.size() == 3) { - Expression* expected = sexpBuilder.parseExpression(e[2]); - Type resType = expected->type; - actual->type = resType; - switch (resType) { - case i32: - body = wasmBuilder.makeBinary(EqInt32, actual, expected); - break; - - case i64: - body = wasmBuilder.makeCall( - "i64Equal", - {actual, wasmBuilder.makeCall(WASM_FETCH_HIGH_BITS, {}, i32), expected}, - i32 - ); - break; - - case f32: { - body = wasmBuilder.makeCall("f32Equal", {actual, expected}, i32); - break; - } - case f64: { - body = wasmBuilder.makeCall("f64Equal", {actual, expected}, i32); - break; - } - - default: { - std::cerr << "Unhandled type in assert: " << resType << std::endl; - abort(); - } - } - } else { - assert(false && "Unexpected number of parameters in assert_return"); - } - std::unique_ptr<Function> testFunc( - wasmBuilder.makeFunction( - testFuncName, - std::vector<NameType>{}, - body->type, - std::vector<NameType>{}, - body - ) - ); - Ref jsFunc = processFunction(wasm, testFunc.get()); - prefixCalls(jsFunc, asmModule); - return jsFunc; -} - -Ref Wasm2JSBuilder::makeAssertReturnNanFunc(SExpressionWasmBuilder& sexpBuilder, - Module* wasm, - Builder& wasmBuilder, - Element& e, - Name testFuncName, - Name asmModule) { - Expression* actual = sexpBuilder.parseExpression(e[1]); - Expression* body = wasmBuilder.makeCall("isNaN", {actual}, i32); - std::unique_ptr<Function> testFunc( - wasmBuilder.makeFunction( - testFuncName, - std::vector<NameType>{}, - body->type, - std::vector<NameType>{}, - body - ) - ); - Ref jsFunc = processFunction(wasm, testFunc.get()); - prefixCalls(jsFunc, asmModule); - return jsFunc; -} - -Ref Wasm2JSBuilder::makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder, - Module* wasm, - Builder& wasmBuilder, - Element& e, - Name testFuncName, - Name asmModule) { - Name innerFuncName("f"); - Expression* expr = sexpBuilder.parseExpression(e[1]); - std::unique_ptr<Function> exprFunc( - wasmBuilder.makeFunction(innerFuncName, - std::vector<NameType>{}, - expr->type, - std::vector<NameType>{}, - expr) - ); - IString expectedErr = e[2]->str(); - Ref innerFunc = processFunction(wasm, exprFunc.get()); - prefixCalls(innerFunc, asmModule); - Ref outerFunc = ValueBuilder::makeFunction(testFuncName); - outerFunc[3]->push_back(innerFunc); - Ref tryBlock = ValueBuilder::makeBlock(); - ValueBuilder::appendToBlock(tryBlock, ValueBuilder::makeCall(innerFuncName)); - Ref catchBlock = ValueBuilder::makeBlock(); - ValueBuilder::appendToBlock( - catchBlock, - ValueBuilder::makeReturn( - ValueBuilder::makeCall( - ValueBuilder::makeDot( - ValueBuilder::makeName(IString("e")), - ValueBuilder::makeName(IString("message")), - ValueBuilder::makeName(IString("includes")) - ), - ValueBuilder::makeString(expectedErr) - ) - ) - ); - outerFunc[3]->push_back(ValueBuilder::makeTry( - tryBlock, - ValueBuilder::makeName((IString("e"))), - catchBlock)); - outerFunc[3]->push_back(ValueBuilder::makeReturn(ValueBuilder::makeInt(0))); - return outerFunc; -} - void Wasm2JSBuilder::setNeedsAlmostASM(const char *reason) { if (!almostASM) { almostASM = true; @@ -2420,74 +1987,157 @@ void Wasm2JSBuilder::addMemoryGrowthFuncs(Ref ast) { ast->push_back(currentMemoryFunc); } -bool Wasm2JSBuilder::isAssertHandled(Element& e) { - return e.isList() && e.size() >= 2 && e[0]->isStr() - && (e[0]->str() == Name("assert_return") || - e[0]->str() == Name("assert_return_nan") || - (flags.pedantic && e[0]->str() == Name("assert_trap"))) - && e[1]->isList() && e[1]->size() >= 2 && (*e[1])[0]->isStr() - && (*e[1])[0]->str() == Name("invoke"); +// Wasm2JSGlue emits the core of the module - the functions etc. that would +// be the asm.js function in an asm.js world. This class emits the rest of the +// "glue" around that. +class Wasm2JSGlue { +public: + Wasm2JSGlue(Module& wasm, Output& out, Wasm2JSBuilder::Flags flags, Name moduleName) : wasm(wasm), out(out), flags(flags), moduleName(moduleName) {} + + void emitPre(); + void emitPost(); + +private: + Module& wasm; + Output& out; + Wasm2JSBuilder::Flags flags; + Name moduleName; +}; + +void Wasm2JSGlue::emitPre() { + std::unordered_map<Name, Name> baseModuleMap; + + auto noteImport = [&](Name module, Name base) { + // Right now codegen requires a flat namespace going into the module, + // meaning we don't support importing the same name from multiple namespaces yet. + if (baseModuleMap.count(base) && baseModuleMap[base] != module) { + Fatal() << "the name " << base << " cannot be imported from " + << "two different modules yet\n"; + abort(); + } + baseModuleMap[base] = module; + + out << "import { " + << base.str + << " } from '" + << module.str + << "';\n"; + }; + + ImportInfo imports(wasm); + + ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) { + Fatal() << "non-function imports aren't supported yet\n"; + noteImport(import->module, import->base); + }); + ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) { + noteImport(import->module, import->base); + }); + + out << '\n'; } -Ref Wasm2JSBuilder::processAsserts(Module* wasm, - Element& root, - SExpressionWasmBuilder& sexpBuilder) { - Builder wasmBuilder(sexpBuilder.getAllocator()); - Ref ret = ValueBuilder::makeBlock(); - std::stringstream asmModuleS; - asmModuleS << "ret" << ASM_FUNC.c_str(); - Name asmModule(asmModuleS.str().c_str()); - makeHelpers(ret, ASM_FUNC, asmModule, true); - for (size_t i = 1; i < root.size(); ++i) { - Element& e = *root[i]; - if (e.isList() && e.size() >= 1 && e[0]->isStr() && e[0]->str() == Name("module")) { - std::stringstream funcNameS; - funcNameS << ASM_FUNC.c_str() << i; - std::stringstream moduleNameS; - moduleNameS << "ret" << ASM_FUNC.c_str() << i; - Name funcName(funcNameS.str().c_str()); - asmModule = Name(moduleNameS.str().c_str()); - Module wasm; - SExpressionWasmBuilder builder(wasm, e); - flattenAppend(ret, processWasm(&wasm, funcName)); - makeHelpers(ret, funcName, asmModule, false); - continue; - } - if (!isAssertHandled(e)) { - std::cerr << "skipping " << e << std::endl; - continue; - } - Name testFuncName(IString(("check" + std::to_string(i)).c_str(), false)); - bool isReturn = (e[0]->str() == Name("assert_return")); - bool isReturnNan = (e[0]->str() == Name("assert_return_nan")); - Element& testOp = *e[1]; - // Replace "invoke" with "call" - testOp[0]->setString(IString("call"), false, false); - // Need to claim dollared to get string as function target - testOp[1]->setString(testOp[1]->str(), /*dollared=*/true, false); - - Ref testFunc = isReturn ? - makeAssertReturnFunc(sexpBuilder, wasm, wasmBuilder, e, testFuncName, asmModule) : - (isReturnNan ? - makeAssertReturnNanFunc(sexpBuilder, wasm, wasmBuilder, e, testFuncName, asmModule) : - makeAssertTrapFunc(sexpBuilder, wasm, wasmBuilder, e, testFuncName, asmModule)); - - flattenAppend(ret, testFunc); - std::stringstream failFuncName; - failFuncName << "fail" << std::to_string(i); - IString testName = fromName(testFuncName, NameScope::Top); - flattenAppend( - ret, - ValueBuilder::makeIf( - ValueBuilder::makeUnary(L_NOT, ValueBuilder::makeCall(testName)), - ValueBuilder::makeCall(IString(failFuncName.str().c_str(), false)), - Ref() - ) - ); +void Wasm2JSGlue::emitPost() { + // Create an initial `ArrayBuffer` and populate it with static data. + // Currently we use base64 encoding to encode static data and we decode it at + // instantiation time. + // + // Note that the translation here expects that the lower values of this memory + // can be used for conversions, so make sure there's at least one page. + { + auto pages = wasm.memory.initial == 0 ? 1 : wasm.memory.initial.addr; + out << "const mem" << moduleName.str << " = new ArrayBuffer(" + << pages * Memory::kPageSize + << ");\n"; } - return ret; -} + if (wasm.memory.segments.size() > 0) { + auto expr = R"( + function(mem) { + const _mem = new Uint8Array(mem); + return function(offset, s) { + if (typeof Buffer === 'undefined') { + const bytes = atob(s); + for (let i = 0; i < bytes.length; i++) + _mem[offset + i] = bytes.charCodeAt(i); + } else { + const bytes = Buffer.from(s, 'base64'); + for (let i = 0; i < bytes.length; i++) + _mem[offset + i] = bytes[i]; + } + } + } + )"; + + // const assign$name = ($expr)(mem$name); + out << "const assign" << moduleName.str + << " = (" << expr << ")(mem" << moduleName.str << ");\n"; + } + for (auto& seg : wasm.memory.segments) { + assert(!seg.isPassive && "passive segments not implemented yet"); + out << "assign" << moduleName.str << "(" + << constOffset(seg) + << ", \"" + << base64Encode(seg.data) + << "\");\n"; + } + + // Actually invoke the `asmFunc` generated function, passing in all global + // values followed by all imports + out << "const ret" << moduleName.str << " = " << moduleName.str << "({" + << "Math," + << "Int8Array," + << "Uint8Array," + << "Int16Array," + << "Uint16Array," + << "Int32Array," + << "Uint32Array," + << "Float32Array," + << "Float64Array," + << "NaN," + << "Infinity" + << "}, {"; + + out << "abort:function() { throw new Error('abort'); }"; + + ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) { + out << "," << import->base.str; + }); + out << "},mem" << moduleName.str << ");\n"; + + if (flags.allowAsserts) { + return; + } + + // And now that we have our returned instance, export all our functions + // that are hanging off it. + for (auto& exp : wasm.exports) { + switch (exp->kind) { + case ExternalKind::Function: + case ExternalKind::Memory: + break; + + // Exported globals and function tables aren't supported yet + default: + continue; + } + std::ostringstream export_name; + for (auto *ptr = exp->name.str; *ptr; ptr++) { + if (*ptr == '-') { + export_name << '_'; + } else { + export_name << *ptr; + } + } + out << "export const " + << asmangle(exp->name.str) + << " = ret" + << moduleName.str + << "." + << asmangle(exp->name.str) + << ";\n"; + } +} } // namespace wasm diff --git a/test/binaryen.js/emit_asmjs.js.txt b/test/binaryen.js/emit_asmjs.js.txt index 0bff3896c..5d959fbdc 100644 --- a/test/binaryen.js/emit_asmjs.js.txt +++ b/test/binaryen.js/emit_asmjs.js.txt @@ -33,4 +33,5 @@ function asmFunc(global, env, buffer) { const memasmFunc = new ArrayBuffer(65536); const retasmFunc = asmFunc({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); }},memasmFunc); +export const main = retasmFunc.main; diff --git a/test/wasm2js.asserts.js b/test/wasm2js.asserts.js index b097a5aaa..9d20ffe57 100644 --- a/test/wasm2js.asserts.js +++ b/test/wasm2js.asserts.js @@ -1,4 +1,36 @@ -function asmFunc(global, env, buffer) { + + var nan = NaN; + var infinity = Infinity; + + function f32Equal(a, b) { + var i = new Int32Array(1); + var f = new Float32Array(i.buffer); + f[0] = a; + var ai = f[0]; + f[0] = b; + var bi = f[0]; + + return (isNaN(a) && isNaN(b)) || a == b; + } + + function f64Equal(a, b) { + var i = new Int32Array(2); + var f = new Float64Array(i.buffer); + f[0] = a; + var ai1 = i[0]; + var ai2 = i[1]; + f[0] = b; + var bi1 = i[0]; + var bi2 = i[1]; + + return (isNaN(a) && isNaN(b)) || (ai1 == bi1 && ai2 == bi2); + } + + function i64Equal(actual_lo, actual_hi, expected_lo, expected_hi) { + return actual_lo == (expected_lo | 0) && actual_hi == (expected_hi | 0); + } + +function asmFunc0(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); var HEAP16 = new global.Int16Array(buffer); @@ -49,51 +81,18 @@ function asmFunc(global, env, buffer) { }; } -const memasmFunc = new ArrayBuffer(65536); -const retasmFunc = asmFunc({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); }},memasmFunc); - - var nan = NaN; - var infinity = Infinity; - ; - - function f32Equal(a, b) { - var i = new Int32Array(1); - var f = new Float32Array(i.buffer); - f[0] = a; - var ai = f[0]; - f[0] = b; - var bi = f[0]; - - return (isNaN(a) && isNaN(b)) || a == b; - } - - function f64Equal(a, b) { - var i = new Int32Array(2); - var f = new Float64Array(i.buffer); - f[0] = a; - var ai1 = i[0]; - var ai2 = i[1]; - f[0] = b; - var bi1 = i[0]; - var bi2 = i[1]; - - return (isNaN(a) && isNaN(b)) || (ai1 == bi1 && ai2 == bi2); - } - - function i64Equal(actual_lo, actual_hi, expected_lo, expected_hi) { - return actual_lo == (expected_lo | 0) && actual_hi == (expected_hi | 0); - } - ; +const memasmFunc0 = new ArrayBuffer(65536); +const retasmFunc0 = asmFunc0({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); }},memasmFunc0); function check1() { var wasm2js_i32$0 = 0; - retasmFunc.empty(); + retasmFunc0.empty(); wasm2js_i32$0 = 1; return wasm2js_i32$0 | 0; } -if (!check1()) fail1(); +if (!check1()) throw 'assertion failed: ( assert_return ( call empty ) )'; function check2() { - return (retasmFunc.add(1 | 0, 1 | 0) | 0 | 0) == (2 | 0) | 0; + return (retasmFunc0.add(1 | 0, 1 | 0) | 0 | 0) == (2 | 0) | 0; } -if (!check2()) fail2(); +if (!check2()) throw 'assertion failed: ( assert_return ( call add ( i32.const 1 ) ( i32.const 1 ) ) ( i32.const 2 ) )'; diff --git a/test/wasm2js.traps.js b/test/wasm2js.traps.js index 290076432..1dbd01507 100644 --- a/test/wasm2js.traps.js +++ b/test/wasm2js.traps.js @@ -1,4 +1,36 @@ -function asmFunc(global, env, buffer) { + + var nan = NaN; + var infinity = Infinity; + + function f32Equal(a, b) { + var i = new Int32Array(1); + var f = new Float32Array(i.buffer); + f[0] = a; + var ai = f[0]; + f[0] = b; + var bi = f[0]; + + return (isNaN(a) && isNaN(b)) || a == b; + } + + function f64Equal(a, b) { + var i = new Int32Array(2); + var f = new Float64Array(i.buffer); + f[0] = a; + var ai1 = i[0]; + var ai2 = i[1]; + f[0] = b; + var bi1 = i[0]; + var bi2 = i[1]; + + return (isNaN(a) && isNaN(b)) || (ai1 == bi1 && ai2 == bi2); + } + + function i64Equal(actual_lo, actual_hi, expected_lo, expected_hi) { + return actual_lo == (expected_lo | 0) && actual_hi == (expected_hi | 0); + } + +function asmFunc0(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); var HEAP16 = new global.Int16Array(buffer); @@ -49,57 +81,24 @@ function asmFunc(global, env, buffer) { }; } -const memasmFunc = new ArrayBuffer(65536); -const retasmFunc = asmFunc({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); }},memasmFunc); - - var nan = NaN; - var infinity = Infinity; - ; - - function f32Equal(a, b) { - var i = new Int32Array(1); - var f = new Float32Array(i.buffer); - f[0] = a; - var ai = f[0]; - f[0] = b; - var bi = f[0]; - - return (isNaN(a) && isNaN(b)) || a == b; - } - - function f64Equal(a, b) { - var i = new Int32Array(2); - var f = new Float64Array(i.buffer); - f[0] = a; - var ai1 = i[0]; - var ai2 = i[1]; - f[0] = b; - var bi1 = i[0]; - var bi2 = i[1]; - - return (isNaN(a) && isNaN(b)) || (ai1 == bi1 && ai2 == bi2); - } - - function i64Equal(actual_lo, actual_hi, expected_lo, expected_hi) { - return actual_lo == (expected_lo | 0) && actual_hi == (expected_hi | 0); - } - ; +const memasmFunc0 = new ArrayBuffer(65536); +const retasmFunc0 = asmFunc0({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); }},memasmFunc0); function check1() { var wasm2js_i32$0 = 0; - retasmFunc.empty(); + retasmFunc0.empty(); wasm2js_i32$0 = 1; return wasm2js_i32$0 | 0; } -if (!check1()) fail1(); +if (!check1()) throw 'assertion failed: ( assert_return ( call empty ) )'; function check2() { - return (retasmFunc.add(1 | 0, 1 | 0) | 0 | 0) == (2 | 0) | 0; + return (retasmFunc0.add(1 | 0, 1 | 0) | 0 | 0) == (2 | 0) | 0; } -if (!check2()) fail2(); +if (!check2()) throw 'assertion failed: ( assert_return ( call add ( i32.const 1 ) ( i32.const 1 ) ) ( i32.const 2 ) )'; function check3() { function f() { - retasmFunc.div_s(0 | 0, 0 | 0); + retasmFunc0.div_s(0 | 0, 0 | 0); } try { @@ -110,10 +109,10 @@ function check3() { return 0; } -if (!check3()) fail3(); +if (!check3()) throw 'assertion failed: ( assert_trap ( call div_s ( i32.const 0 ) ( i32.const 0 ) ) integer divide by zero )'; function check4() { function f() { - retasmFunc.div_s(2147483648 | 0, 4294967295 | 0); + retasmFunc0.div_s(2147483648 | 0, 4294967295 | 0); } try { @@ -124,4 +123,4 @@ function check4() { return 0; } -if (!check4()) fail4(); +if (!check4()) throw 'assertion failed: ( assert_trap ( call div_s ( i32.const 0x80000000 ) ( i32.const -1 ) ) integer overflow )'; diff --git a/test/wasm2js/address.2asm.js b/test/wasm2js/address.2asm.js index 32b839db2..9a79aef3f 100644 --- a/test/wasm2js/address.2asm.js +++ b/test/wasm2js/address.2asm.js @@ -1,4 +1,5 @@ import { print } from 'spectest'; + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/base64.2asm.js b/test/wasm2js/base64.2asm.js index 203680dc7..d6107dd88 100644 --- a/test/wasm2js/base64.2asm.js +++ b/test/wasm2js/base64.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/block.2asm.js b/test/wasm2js/block.2asm.js index 10706a646..b069ec6c9 100644 --- a/test/wasm2js/block.2asm.js +++ b/test/wasm2js/block.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/br.2asm.js b/test/wasm2js/br.2asm.js index 90822c599..62cafe37c 100644 --- a/test/wasm2js/br.2asm.js +++ b/test/wasm2js/br.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/br_if.2asm.js b/test/wasm2js/br_if.2asm.js index 628da8ee9..0e5e84a23 100644 --- a/test/wasm2js/br_if.2asm.js +++ b/test/wasm2js/br_if.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/br_table.2asm.js b/test/wasm2js/br_table.2asm.js index 863fe53b5..135733e4b 100644 --- a/test/wasm2js/br_table.2asm.js +++ b/test/wasm2js/br_table.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/br_table_temp.2asm.js b/test/wasm2js/br_table_temp.2asm.js index adcdc270d..e5209e148 100644 --- a/test/wasm2js/br_table_temp.2asm.js +++ b/test/wasm2js/br_table_temp.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/break-drop.2asm.js b/test/wasm2js/break-drop.2asm.js index 5917b678b..178d21a19 100644 --- a/test/wasm2js/break-drop.2asm.js +++ b/test/wasm2js/break-drop.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/call.2asm.js b/test/wasm2js/call.2asm.js index 470e63c21..a36f59811 100644 --- a/test/wasm2js/call.2asm.js +++ b/test/wasm2js/call.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/call_indirect.2asm.js b/test/wasm2js/call_indirect.2asm.js index af9e9874c..9ee3d9480 100644 --- a/test/wasm2js/call_indirect.2asm.js +++ b/test/wasm2js/call_indirect.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/comments.2asm.js b/test/wasm2js/comments.2asm.js index c8ff78cb4..d515f48f4 100644 --- a/test/wasm2js/comments.2asm.js +++ b/test/wasm2js/comments.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/conversions-modified.2asm.js b/test/wasm2js/conversions-modified.2asm.js index 78254f600..a9dd9f0cb 100644 --- a/test/wasm2js/conversions-modified.2asm.js +++ b/test/wasm2js/conversions-modified.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/empty_table.2asm.js b/test/wasm2js/empty_table.2asm.js index c8ff78cb4..d515f48f4 100644 --- a/test/wasm2js/empty_table.2asm.js +++ b/test/wasm2js/empty_table.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/endianness.2asm.js b/test/wasm2js/endianness.2asm.js index 3796af318..8d63eeac2 100644 --- a/test/wasm2js/endianness.2asm.js +++ b/test/wasm2js/endianness.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/f32.2asm.js b/test/wasm2js/f32.2asm.js index 454fb8d8b..e8a12f19c 100644 --- a/test/wasm2js/f32.2asm.js +++ b/test/wasm2js/f32.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/f32_cmp.2asm.js b/test/wasm2js/f32_cmp.2asm.js index 84bf67293..87e9e2506 100644 --- a/test/wasm2js/f32_cmp.2asm.js +++ b/test/wasm2js/f32_cmp.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/f64.2asm.js b/test/wasm2js/f64.2asm.js index ac5f78a49..83aad0565 100644 --- a/test/wasm2js/f64.2asm.js +++ b/test/wasm2js/f64.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/f64_cmp.2asm.js b/test/wasm2js/f64_cmp.2asm.js index 39b64c386..0bc1bcb71 100644 --- a/test/wasm2js/f64_cmp.2asm.js +++ b/test/wasm2js/f64_cmp.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/fac.2asm.js b/test/wasm2js/fac.2asm.js index 794fd2806..f244548db 100644 --- a/test/wasm2js/fac.2asm.js +++ b/test/wasm2js/fac.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/float-ops.2asm.js b/test/wasm2js/float-ops.2asm.js index 8a23294b4..7bbf81de9 100644 --- a/test/wasm2js/float-ops.2asm.js +++ b/test/wasm2js/float-ops.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/float_literals-modified.2asm.js b/test/wasm2js/float_literals-modified.2asm.js index b93b82832..4bff08e88 100644 --- a/test/wasm2js/float_literals-modified.2asm.js +++ b/test/wasm2js/float_literals-modified.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/float_misc.2asm.js b/test/wasm2js/float_misc.2asm.js index 469e70e86..c654b4060 100644 --- a/test/wasm2js/float_misc.2asm.js +++ b/test/wasm2js/float_misc.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/forward.2asm.js b/test/wasm2js/forward.2asm.js index dfdf6b2bf..c7421abb8 100644 --- a/test/wasm2js/forward.2asm.js +++ b/test/wasm2js/forward.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/func-ptr-offset.2asm.js b/test/wasm2js/func-ptr-offset.2asm.js index 93b844826..3d75062a7 100644 --- a/test/wasm2js/func-ptr-offset.2asm.js +++ b/test/wasm2js/func-ptr-offset.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/func.2asm.js b/test/wasm2js/func.2asm.js index 83919f943..585c28552 100644 --- a/test/wasm2js/func.2asm.js +++ b/test/wasm2js/func.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/func_ptrs.2asm.js b/test/wasm2js/func_ptrs.2asm.js index ec4d11d86..6841925db 100644 --- a/test/wasm2js/func_ptrs.2asm.js +++ b/test/wasm2js/func_ptrs.2asm.js @@ -1,4 +1,5 @@ import { print } from 'spectest'; + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/get-set-local.2asm.js b/test/wasm2js/get-set-local.2asm.js index 69c6a16ae..5f50972ba 100644 --- a/test/wasm2js/get-set-local.2asm.js +++ b/test/wasm2js/get-set-local.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/get_local.2asm.js b/test/wasm2js/get_local.2asm.js index 5a71697b9..2b8688bef 100644 --- a/test/wasm2js/get_local.2asm.js +++ b/test/wasm2js/get_local.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/grow-memory-tricky.2asm.js b/test/wasm2js/grow-memory-tricky.2asm.js index 81c5952cb..62b615bdc 100644 --- a/test/wasm2js/grow-memory-tricky.2asm.js +++ b/test/wasm2js/grow-memory-tricky.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "almost asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/grow_memory.2asm.js b/test/wasm2js/grow_memory.2asm.js index 31e3dd525..ad8ff14b0 100644 --- a/test/wasm2js/grow_memory.2asm.js +++ b/test/wasm2js/grow_memory.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "almost asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/hello_world.2asm.js b/test/wasm2js/hello_world.2asm.js index 9fef6f97c..403fdd4e2 100644 --- a/test/wasm2js/hello_world.2asm.js +++ b/test/wasm2js/hello_world.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/i32.2asm.js b/test/wasm2js/i32.2asm.js index 261f9df1c..adf917e9b 100644 --- a/test/wasm2js/i32.2asm.js +++ b/test/wasm2js/i32.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/i64-add-sub.2asm.js b/test/wasm2js/i64-add-sub.2asm.js index 249b4844e..521264318 100644 --- a/test/wasm2js/i64-add-sub.2asm.js +++ b/test/wasm2js/i64-add-sub.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/i64-ctz.2asm.js b/test/wasm2js/i64-ctz.2asm.js index 481c42e61..bb217bc66 100644 --- a/test/wasm2js/i64-ctz.2asm.js +++ b/test/wasm2js/i64-ctz.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/i64-lowering.2asm.js b/test/wasm2js/i64-lowering.2asm.js index 9d673d20b..dbbbeb4a2 100644 --- a/test/wasm2js/i64-lowering.2asm.js +++ b/test/wasm2js/i64-lowering.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/i64-rotate.2asm.js b/test/wasm2js/i64-rotate.2asm.js index 1981e0d0f..e2031cca0 100644 --- a/test/wasm2js/i64-rotate.2asm.js +++ b/test/wasm2js/i64-rotate.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/i64-select.2asm.js b/test/wasm2js/i64-select.2asm.js index 99562534d..e715515b0 100644 --- a/test/wasm2js/i64-select.2asm.js +++ b/test/wasm2js/i64-select.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/i64-shifts.2asm.js b/test/wasm2js/i64-shifts.2asm.js index 05ac5e62b..9c8d32f34 100644 --- a/test/wasm2js/i64-shifts.2asm.js +++ b/test/wasm2js/i64-shifts.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/i64.2asm.js b/test/wasm2js/i64.2asm.js index 065cf071e..4352850ee 100644 --- a/test/wasm2js/i64.2asm.js +++ b/test/wasm2js/i64.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/int_exprs.2asm.js b/test/wasm2js/int_exprs.2asm.js index e3aac0e2b..e6a010a68 100644 --- a/test/wasm2js/int_exprs.2asm.js +++ b/test/wasm2js/int_exprs.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/int_literals.2asm.js b/test/wasm2js/int_literals.2asm.js index deb6b99c9..7e30964f0 100644 --- a/test/wasm2js/int_literals.2asm.js +++ b/test/wasm2js/int_literals.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/labels.2asm.js b/test/wasm2js/labels.2asm.js index e99b6db8e..a6bfe109a 100644 --- a/test/wasm2js/labels.2asm.js +++ b/test/wasm2js/labels.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/left-to-right.2asm.js b/test/wasm2js/left-to-right.2asm.js index 835175ee6..e70eff986 100644 --- a/test/wasm2js/left-to-right.2asm.js +++ b/test/wasm2js/left-to-right.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/loop.2asm.js b/test/wasm2js/loop.2asm.js index 9b9df3ff6..bf53d0318 100644 --- a/test/wasm2js/loop.2asm.js +++ b/test/wasm2js/loop.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/nested-selects.2asm.js b/test/wasm2js/nested-selects.2asm.js index 15e262f9d..c08be2b2b 100644 --- a/test/wasm2js/nested-selects.2asm.js +++ b/test/wasm2js/nested-selects.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/reinterpret.2asm.js b/test/wasm2js/reinterpret.2asm.js index 7aca3ba38..f0915fa8e 100644 --- a/test/wasm2js/reinterpret.2asm.js +++ b/test/wasm2js/reinterpret.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/select.2asm.js b/test/wasm2js/select.2asm.js index d0e6f8fdd..ec21d2002 100644 --- a/test/wasm2js/select.2asm.js +++ b/test/wasm2js/select.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/set_local.2asm.js b/test/wasm2js/set_local.2asm.js index 50222d6a2..6728bb35e 100644 --- a/test/wasm2js/set_local.2asm.js +++ b/test/wasm2js/set_local.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/stack-modified.2asm.js b/test/wasm2js/stack-modified.2asm.js index 1b65bb24b..babd7bdda 100644 --- a/test/wasm2js/stack-modified.2asm.js +++ b/test/wasm2js/stack-modified.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/switch.2asm.js b/test/wasm2js/switch.2asm.js index e793696df..053736116 100644 --- a/test/wasm2js/switch.2asm.js +++ b/test/wasm2js/switch.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/tee_local.2asm.js b/test/wasm2js/tee_local.2asm.js index b29a167b8..f07a4ca7e 100644 --- a/test/wasm2js/tee_local.2asm.js +++ b/test/wasm2js/tee_local.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/traps.2asm.js b/test/wasm2js/traps.2asm.js index c31231653..603d08e3e 100644 --- a/test/wasm2js/traps.2asm.js +++ b/test/wasm2js/traps.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/unaligned.2asm.js b/test/wasm2js/unaligned.2asm.js index 849833501..7cd350b7f 100644 --- a/test/wasm2js/unaligned.2asm.js +++ b/test/wasm2js/unaligned.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); diff --git a/test/wasm2js/unary-ops.2asm.js b/test/wasm2js/unary-ops.2asm.js index f06c136b3..8c9114540 100644 --- a/test/wasm2js/unary-ops.2asm.js +++ b/test/wasm2js/unary-ops.2asm.js @@ -1,3 +1,4 @@ + function asmFunc(global, env, buffer) { "use asm"; var HEAP8 = new global.Int8Array(buffer); |