Implement linear memory instructions in WebAssembly
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 Sep 2015 23:06:47 +0000 (23:06 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 Sep 2015 23:06:47 +0000 (23:06 +0000)
https://bugs.webkit.org/show_bug.cgi?id=149326

Patch by Sukolsak Sakshuwong <sukolsak@gmail.com> on 2015-09-18
Reviewed by Geoffrey Garen.

This patch implements linear memory instructions in WebAssembly.[1] To
use the linear memory, an ArrayBuffer must be passed to loadWebAssembly().

Notes:
- We limit the ArrayBuffer's byte length to 2^31 - 1. This enables us to
  use only one comparison (unsigned greater than) to check for
  out-of-bounds access.
- There is no consensus yet on what should happen when an out-of-bounds
  access occurs.[2] For now, we throw an error when that happens.
- In asm.js, a heap access looks like this: int32Array[i >> 2]. Note
  that ">> 2" is part of the syntax and is required. pack-asmjs will
  produce bytecodes that look something like "LoadI32, i" (not
  "LoadI32, ShiftRightI32, i, 2"). The requirement of the shift operator
  prevents unaligned accesses in asm.js. (There is a proposal to support
  unaligned accesses in the future version of asm.js using DataView.[3])
  The WebAssembly spec allows unaligned accesses.[4] But since we use
  asm.js for testing, we follow asm.js's behaviors for now.

[1]: https://github.com/WebAssembly/design/blob/master/AstSemantics.md#linear-memory
[2]: https://github.com/WebAssembly/design/blob/master/AstSemantics.md#out-of-bounds
[3]: https://wiki.mozilla.org/Javascript:SpiderMonkey:OdinMonkey#Possible_asm.js_extensions_that_don.27t_require_new_JS_features
[4]: https://github.com/WebAssembly/design/blob/master/AstSemantics.md#alignment

* jit/JITOperations.cpp:
* jit/JITOperations.h:
* jsc.cpp:
(GlobalObject::finishCreation):
(functionLoadWebAssembly):
* tests/stress/wasm-linear-memory.js: Added.
(shouldBe):
(shouldThrow):
* tests/stress/wasm/linear-memory.wasm: Added.
* wasm/JSWASMModule.cpp:
(JSC::JSWASMModule::JSWASMModule):
(JSC::JSWASMModule::visitChildren):
* wasm/JSWASMModule.h:
(JSC::JSWASMModule::create):
(JSC::JSWASMModule::arrayBuffer):
(JSC::JSWASMModule::JSWASMModule): Deleted.
* wasm/WASMConstants.h:
* wasm/WASMFunctionCompiler.h:
(JSC::sizeOfMemoryType):
(JSC::WASMFunctionCompiler::MemoryAddress::MemoryAddress):
(JSC::WASMFunctionCompiler::endFunction):
(JSC::WASMFunctionCompiler::buildLoad):
(JSC::WASMFunctionCompiler::buildStore):
* wasm/WASMFunctionParser.cpp:
(JSC::WASMFunctionParser::parseStatement):
(JSC::WASMFunctionParser::parseExpressionI32):
(JSC::WASMFunctionParser::parseExpressionF32):
(JSC::WASMFunctionParser::parseExpressionF64):
(JSC::WASMFunctionParser::parseMemoryAddress):
(JSC::WASMFunctionParser::parseLoad):
(JSC::WASMFunctionParser::parseStore):
* wasm/WASMFunctionParser.h:
* wasm/WASMFunctionSyntaxChecker.h:
(JSC::WASMFunctionSyntaxChecker::MemoryAddress::MemoryAddress):
(JSC::WASMFunctionSyntaxChecker::buildLoad):
(JSC::WASMFunctionSyntaxChecker::buildStore):
* wasm/WASMModuleParser.cpp:
(JSC::WASMModuleParser::WASMModuleParser):
(JSC::WASMModuleParser::parseModule):
(JSC::parseWebAssembly):
(JSC::WASMModuleParser::parse): Deleted.
* wasm/WASMModuleParser.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@189993 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/tests/stress/wasm-linear-memory.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/wasm/linear-memory.wasm [new file with mode: 0644]
Source/JavaScriptCore/wasm/JSWASMModule.cpp
Source/JavaScriptCore/wasm/JSWASMModule.h
Source/JavaScriptCore/wasm/WASMConstants.h
Source/JavaScriptCore/wasm/WASMFunctionCompiler.h
Source/JavaScriptCore/wasm/WASMFunctionParser.cpp
Source/JavaScriptCore/wasm/WASMFunctionParser.h
Source/JavaScriptCore/wasm/WASMFunctionSyntaxChecker.h
Source/JavaScriptCore/wasm/WASMModuleParser.cpp
Source/JavaScriptCore/wasm/WASMModuleParser.h

index 6059c17..f5c22bb 100644 (file)
@@ -1,5 +1,78 @@
 2015-09-18  Sukolsak Sakshuwong  <sukolsak@gmail.com>
 
+        Implement linear memory instructions in WebAssembly
+        https://bugs.webkit.org/show_bug.cgi?id=149326
+
+        Reviewed by Geoffrey Garen.
+
+        This patch implements linear memory instructions in WebAssembly.[1] To
+        use the linear memory, an ArrayBuffer must be passed to loadWebAssembly().
+
+        Notes:
+        - We limit the ArrayBuffer's byte length to 2^31 - 1. This enables us to
+          use only one comparison (unsigned greater than) to check for
+          out-of-bounds access.
+        - There is no consensus yet on what should happen when an out-of-bounds
+          access occurs.[2] For now, we throw an error when that happens.
+        - In asm.js, a heap access looks like this: int32Array[i >> 2]. Note
+          that ">> 2" is part of the syntax and is required. pack-asmjs will
+          produce bytecodes that look something like "LoadI32, i" (not
+          "LoadI32, ShiftRightI32, i, 2"). The requirement of the shift operator
+          prevents unaligned accesses in asm.js. (There is a proposal to support
+          unaligned accesses in the future version of asm.js using DataView.[3])
+          The WebAssembly spec allows unaligned accesses.[4] But since we use
+          asm.js for testing, we follow asm.js's behaviors for now.
+
+        [1]: https://github.com/WebAssembly/design/blob/master/AstSemantics.md#linear-memory
+        [2]: https://github.com/WebAssembly/design/blob/master/AstSemantics.md#out-of-bounds
+        [3]: https://wiki.mozilla.org/Javascript:SpiderMonkey:OdinMonkey#Possible_asm.js_extensions_that_don.27t_require_new_JS_features
+        [4]: https://github.com/WebAssembly/design/blob/master/AstSemantics.md#alignment
+
+        * jit/JITOperations.cpp:
+        * jit/JITOperations.h:
+        * jsc.cpp:
+        (GlobalObject::finishCreation):
+        (functionLoadWebAssembly):
+        * tests/stress/wasm-linear-memory.js: Added.
+        (shouldBe):
+        (shouldThrow):
+        * tests/stress/wasm/linear-memory.wasm: Added.
+        * wasm/JSWASMModule.cpp:
+        (JSC::JSWASMModule::JSWASMModule):
+        (JSC::JSWASMModule::visitChildren):
+        * wasm/JSWASMModule.h:
+        (JSC::JSWASMModule::create):
+        (JSC::JSWASMModule::arrayBuffer):
+        (JSC::JSWASMModule::JSWASMModule): Deleted.
+        * wasm/WASMConstants.h:
+        * wasm/WASMFunctionCompiler.h:
+        (JSC::sizeOfMemoryType):
+        (JSC::WASMFunctionCompiler::MemoryAddress::MemoryAddress):
+        (JSC::WASMFunctionCompiler::endFunction):
+        (JSC::WASMFunctionCompiler::buildLoad):
+        (JSC::WASMFunctionCompiler::buildStore):
+        * wasm/WASMFunctionParser.cpp:
+        (JSC::WASMFunctionParser::parseStatement):
+        (JSC::WASMFunctionParser::parseExpressionI32):
+        (JSC::WASMFunctionParser::parseExpressionF32):
+        (JSC::WASMFunctionParser::parseExpressionF64):
+        (JSC::WASMFunctionParser::parseMemoryAddress):
+        (JSC::WASMFunctionParser::parseLoad):
+        (JSC::WASMFunctionParser::parseStore):
+        * wasm/WASMFunctionParser.h:
+        * wasm/WASMFunctionSyntaxChecker.h:
+        (JSC::WASMFunctionSyntaxChecker::MemoryAddress::MemoryAddress):
+        (JSC::WASMFunctionSyntaxChecker::buildLoad):
+        (JSC::WASMFunctionSyntaxChecker::buildStore):
+        * wasm/WASMModuleParser.cpp:
+        (JSC::WASMModuleParser::WASMModuleParser):
+        (JSC::WASMModuleParser::parseModule):
+        (JSC::parseWebAssembly):
+        (JSC::WASMModuleParser::parse): Deleted.
+        * wasm/WASMModuleParser.h:
+
+2015-09-18  Sukolsak Sakshuwong  <sukolsak@gmail.com>
+
         Implement type conversion instructions in WebAssembly
         https://bugs.webkit.org/show_bug.cgi?id=149340
 
index d005ac9..8e7ea65 100644 (file)
@@ -105,6 +105,17 @@ void JIT_OPERATION operationThrowDivideError(ExecState* exec)
     ErrorHandlingScope errorScope(*vm);
     vm->throwException(callerFrame, createError(callerFrame, ASCIILiteral("Division by zero or division overflow.")));
 }
+
+void JIT_OPERATION operationThrowOutOfBoundsAccessError(ExecState* exec)
+{
+    VM* vm = &exec->vm();
+    VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame;
+    CallFrame* callerFrame = exec->callerFrame(vmEntryFrame);
+
+    NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame);
+    ErrorHandlingScope errorScope(*vm);
+    vm->throwException(callerFrame, createError(callerFrame, ASCIILiteral("Out-of-bounds access.")));
+}
 #endif
 
 int32_t JIT_OPERATION operationCallArityCheck(ExecState* exec)
index b9c7248..aaca35a 100644 (file)
@@ -251,6 +251,7 @@ void JIT_OPERATION operationVMHandleException(ExecState*) WTF_INTERNAL;
 void JIT_OPERATION operationThrowStackOverflowError(ExecState*, CodeBlock*) WTF_INTERNAL;
 #if ENABLE(WEBASSEMBLY)
 void JIT_OPERATION operationThrowDivideError(ExecState*) WTF_INTERNAL;
+void JIT_OPERATION operationThrowOutOfBoundsAccessError(ExecState*) WTF_INTERNAL;
 #endif
 int32_t JIT_OPERATION operationCallArityCheck(ExecState*) WTF_INTERNAL;
 int32_t JIT_OPERATION operationConstructArityCheck(ExecState*) WTF_INTERNAL;
index 1f23ca1..d2e4d58 100644 (file)
@@ -677,7 +677,7 @@ protected:
         addFunction(vm, "drainMicrotasks", functionDrainMicrotasks, 0);
 
 #if ENABLE(WEBASSEMBLY)
-        addFunction(vm, "loadWebAssembly", functionLoadWebAssembly, 2);
+        addFunction(vm, "loadWebAssembly", functionLoadWebAssembly, 3);
 #endif
         addFunction(vm, "loadModule", functionLoadModule, 1);
         addFunction(vm, "checkModuleSyntax", functionCheckModuleSyntax, 1);
@@ -1450,9 +1450,10 @@ EncodedJSValue JSC_HOST_CALL functionLoadWebAssembly(ExecState* exec)
     RefPtr<WebAssemblySourceProvider> sourceProvider = WebAssemblySourceProvider::create(reinterpret_cast<Vector<uint8_t>&>(buffer), fileName);
     SourceCode source(sourceProvider);
     JSObject* imports = exec->argument(1).getObject();
+    JSArrayBuffer* arrayBuffer = jsDynamicCast<JSArrayBuffer*>(exec->argument(2));
 
     String errorMessage;
-    JSWASMModule* module = parseWebAssembly(exec, source, imports, errorMessage);
+    JSWASMModule* module = parseWebAssembly(exec, source, imports, arrayBuffer, errorMessage);
     if (!module)
         return JSValue::encode(exec->vm().throwException(exec, createSyntaxError(exec, errorMessage)));
     return JSValue::encode(module);
diff --git a/Source/JavaScriptCore/tests/stress/wasm-linear-memory.js b/Source/JavaScriptCore/tests/stress/wasm-linear-memory.js
new file mode 100644 (file)
index 0000000..c356aef
--- /dev/null
@@ -0,0 +1,204 @@
+//@ skip
+
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function shouldThrow(func, message) {
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        error = e;
+    }
+    if (!error)
+        throw new Error("not thrown.");
+    if (String(error) !== message)
+        throw new Error("bad error: " + String(error));
+}
+
+/*
+wasm/linear-memory.wasm is generated by pack-asmjs <https://github.com/WebAssembly/polyfill-prototype-1> from the following script:
+
+function asmModule(global, imports, buffer) {
+    "use asm";
+
+    var fround = global.Math.fround;
+    var int8Array = new global.Int8Array(buffer);
+    var uint8Array = new global.Uint8Array(buffer);
+    var int16Array = new global.Int16Array(buffer);
+    var uint16Array = new global.Uint16Array(buffer);
+    var int32Array = new global.Int32Array(buffer);
+    var float32Array = new global.Float32Array(buffer);
+    var float64Array = new global.Float64Array(buffer);
+
+    function getInt8(i) {
+        i = i | 0;
+        return int8Array[i] | 0;
+    }
+
+    function getUint8(i) {
+        i = i | 0;
+        return uint8Array[i] | 0;
+    }
+
+    function getInt16(i) {
+        i = i | 0;
+        return int16Array[i >> 1] | 0;
+    }
+
+    function getUint16(i) {
+        i = i | 0;
+        return uint16Array[i >> 1] | 0;
+    }
+
+    function getInt32(i) {
+        i = i | 0;
+        return int32Array[i >> 2] | 0;
+    }
+
+    function getFloat32(i) {
+        i = i | 0;
+        return fround(float32Array[i >> 2]);
+    }
+
+    function getFloat64(i) {
+        i = i | 0;
+        return +float64Array[i >> 3];
+    }
+
+    function setInt8(i, x) {
+        i = i | 0;
+        x = x | 0;
+        int8Array[i] = x;
+    }
+
+    function setUint8(i, x) {
+        i = i | 0;
+        x = x | 0;
+        uint8Array[i] = x;
+    }
+
+    function setInt16(i, x) {
+        i = i | 0;
+        x = x | 0;
+        int16Array[i >> 1] = x;
+    }
+
+    function setUint16(i, x) {
+        i = i | 0;
+        x = x | 0;
+        uint16Array[i >> 1] = x;
+    }
+
+    function setInt32(i, x) {
+        i = i | 0;
+        x = x | 0;
+        int32Array[i >> 2] = x;
+    }
+
+    function setFloat32(i, x) {
+        i = i | 0;
+        x = fround(x);
+        float32Array[i >> 2] = x;
+    }
+
+    function setFloat64(i, x) {
+        i = i | 0;
+        x = +x;
+        float64Array[i >> 3] = x;
+    }
+
+    return {
+        getInt8: getInt8,
+        getUint8: getUint8,
+        getInt16: getInt16,
+        getUint16: getUint16,
+        getInt32: getInt32,
+        getFloat32: getFloat32,
+        getFloat64: getFloat64,
+        setInt8: setInt8,
+        setUint8: setUint8,
+        setInt16: setInt16,
+        setUint16: setUint16,
+        setInt32: setInt32,
+        setFloat32: setFloat32,
+        setFloat64: setFloat64,
+    };
+}
+*/
+
+var buffer = new ArrayBuffer(16);
+var module = loadWebAssembly("wasm/linear-memory.wasm", { }, buffer);
+
+var int32Array = new Int32Array(buffer);
+
+// Basic gets and sets.
+module.setInt8(0, 1);
+shouldBe(module.getInt8(0), 1);
+module.setUint8(0, 2);
+shouldBe(module.getUint8(0), 2);
+module.setInt16(0, 3);
+shouldBe(module.getInt16(0), 3);
+module.setUint16(0, 4);
+shouldBe(module.getUint16(0), 4);
+module.setInt32(0, 5);
+shouldBe(module.getInt32(0), 5);
+module.setFloat32(0, 6.7);
+shouldBe(module.getFloat32(0), 6.699999809265137);
+module.setFloat64(0, 8.9);
+shouldBe(module.getFloat64(0), 8.9);
+
+int32Array[0] = 0x00FFFF00;
+shouldBe(module.getUint8(0), 0);
+shouldBe(module.getUint8(1), 255);
+shouldBe(module.getUint8(2), 255);
+shouldBe(module.getUint8(3), 0);
+
+// Type punning.
+module.setInt8(0, -1);
+shouldBe(module.getInt8(0), -1);
+shouldBe(module.getUint8(0), 255);
+
+module.setInt8(1, -1);
+shouldBe(module.getInt8(1), -1);
+shouldBe(module.getUint8(1), 255);
+shouldBe(module.getInt16(0), -1);
+shouldBe(module.getUint16(0), 65535);
+
+// Out-of-bound accesses.
+module.getInt8(15);
+shouldThrow(() => {
+    module.getInt8(16);
+}, "Error: Out-of-bounds access.");
+shouldThrow(() => {
+    module.getInt8(-1);
+}, "Error: Out-of-bounds access.");
+
+// Unaligned accesses
+int32Array[0] = 0;
+int32Array[1] = 0x01234567;
+int32Array[2] = 0;
+shouldBe(module.getInt32(3), 0);
+shouldBe(module.getInt32(4), 0x01234567);
+shouldBe(module.getInt32(5), 0x01234567);
+shouldBe(module.getInt32(6), 0x01234567);
+shouldBe(module.getInt32(7), 0x01234567);
+shouldBe(module.getInt32(8), 0);
+
+int32Array[0] = 0;
+int32Array[1] = 0;
+int32Array[2] = 0;
+module.setInt32(3, 1);
+shouldBe(int32Array[0] == 1 && int32Array[1] == 0 && int32Array[2] == 0, true);
+module.setInt32(4, 2);
+shouldBe(int32Array[0] == 1 && int32Array[1] == 2 && int32Array[2] == 0, true);
+module.setInt32(5, 3);
+shouldBe(int32Array[0] == 1 && int32Array[1] == 3 && int32Array[2] == 0, true);
+module.setInt32(6, 4);
+shouldBe(int32Array[0] == 1 && int32Array[1] == 4 && int32Array[2] == 0, true);
+module.setInt32(7, 5);
+shouldBe(int32Array[0] == 1 && int32Array[1] == 5 && int32Array[2] == 0, true);
+module.setInt32(8, 6);
+shouldBe(int32Array[0] == 1 && int32Array[1] == 5 && int32Array[2] == 6, true);
diff --git a/Source/JavaScriptCore/tests/stress/wasm/linear-memory.wasm b/Source/JavaScriptCore/tests/stress/wasm/linear-memory.wasm
new file mode 100644 (file)
index 0000000..a7c772f
Binary files /dev/null and b/Source/JavaScriptCore/tests/stress/wasm/linear-memory.wasm differ
index f1d4640..8b8d90c 100644 (file)
@@ -28,6 +28,7 @@
 
 #if ENABLE(WEBASSEMBLY)
 
+#include "JSArrayBuffer.h"
 #include "JSFunction.h"
 #include "SlotVisitorInlines.h"
 
@@ -35,6 +36,13 @@ namespace JSC {
 
 const ClassInfo JSWASMModule::s_info = { "WASMModule", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWASMModule) };
 
+JSWASMModule::JSWASMModule(VM& vm, Structure* structure, JSArrayBuffer* arrayBuffer)
+    : Base(vm, structure)
+{
+    if (arrayBuffer)
+        m_arrayBuffer.set(vm, this, arrayBuffer);
+}
+
 void JSWASMModule::destroy(JSCell* cell)
 {
     JSWASMModule* thisObject = jsCast<JSWASMModule*>(cell);
@@ -46,6 +54,7 @@ void JSWASMModule::visitChildren(JSCell* cell, SlotVisitor& visitor)
     JSWASMModule* thisObject = jsCast<JSWASMModule*>(cell);
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
     Base::visitChildren(thisObject, visitor);
+    visitor.append(&thisObject->m_arrayBuffer);
     for (auto function : thisObject->m_functions)
         visitor.append(&function);
     for (auto importedFunction : thisObject->m_importedFunctions)
index c63b585..2d16791 100644 (file)
@@ -56,9 +56,9 @@ public:
         double doubleValue;
     };
 
-    static JSWASMModule* create(VM& vm, Structure* structure)
+    static JSWASMModule* create(VM& vm, Structure* structure, JSArrayBuffer* arrayBuffer)
     {
-        JSWASMModule* module = new (NotNull, allocateCell<JSWASMModule>(vm.heap)) JSWASMModule(vm, structure);
+        JSWASMModule* module = new (NotNull, allocateCell<JSWASMModule>(vm.heap)) JSWASMModule(vm, structure, arrayBuffer);
         module->finishCreation(vm);
         return module;
     }
@@ -83,6 +83,7 @@ public:
     Vector<WASMFunctionDeclaration>& functionDeclarations() { return m_functionDeclarations; }
     Vector<WASMFunctionPointerTable>& functionPointerTables() { return m_functionPointerTables; }
 
+    const JSArrayBuffer* arrayBuffer() const { return m_arrayBuffer.get(); }
     Vector<WriteBarrier<JSFunction>>& functions() { return m_functions; }
     Vector<unsigned>& functionStartOffsetsInSource() { return m_functionStartOffsetsInSource; }
     Vector<unsigned>& functionStackHeights() { return m_functionStackHeights; }
@@ -90,10 +91,7 @@ public:
     Vector<WriteBarrier<JSFunction>>& importedFunctions() { return m_importedFunctions; }
 
 private:
-    JSWASMModule(VM& vm, Structure* structure)
-        : Base(vm, structure)
-    {
-    }
+    JSWASMModule(VM&, Structure*, JSArrayBuffer*);
 
     Vector<uint32_t> m_i32Constants;
     Vector<float> m_f32Constants;
@@ -105,6 +103,7 @@ private:
     Vector<WASMFunctionDeclaration> m_functionDeclarations;
     Vector<WASMFunctionPointerTable> m_functionPointerTables;
 
+    WriteBarrier<JSArrayBuffer> m_arrayBuffer;
     Vector<WriteBarrier<JSFunction>> m_functions;
     Vector<unsigned> m_functionStartOffsetsInSource;
     Vector<unsigned> m_functionStackHeights;
index 3f6dd5f..566c3fd 100644 (file)
@@ -291,6 +291,17 @@ enum class WASMTypeConversion {
     Demote,
 };
 
+enum class WASMMemoryType {
+    I8,
+    I16,
+    I32,
+    F32,
+    F64
+};
+
+enum class MemoryAccessOffsetMode { NoOffset, WithOffset };
+enum class MemoryAccessConversion { NoConversion, SignExtend, ZeroExtend };
+
 static const uint8_t hasImmediateInOpFlag = 0x80;
 
 static const unsigned opWithImmediateBits = 2;
index 36d61a0..4735f60 100644 (file)
@@ -32,6 +32,7 @@
 #include "CCallHelpers.h"
 #include "JIT.h"
 #include "JITOperations.h"
+#include "JSArrayBuffer.h"
 #include "LinkBuffer.h"
 #include "MaxFrameExtentForSlowPathCall.h"
 
@@ -78,11 +79,36 @@ static double JIT_OPERATION operationConvertUnsignedInt32ToDouble(uint32_t value
 }
 #endif
 
+static size_t sizeOfMemoryType(WASMMemoryType memoryType)
+{
+    switch (memoryType) {
+    case WASMMemoryType::I8:
+        return 1;
+    case WASMMemoryType::I16:
+        return 2;
+    case WASMMemoryType::I32:
+    case WASMMemoryType::F32:
+        return 4;
+    case WASMMemoryType::F64:
+        return 8;
+    default:
+        ASSERT_NOT_REACHED();
+    }
+}
+
 class WASMFunctionCompiler : private CCallHelpers {
 public:
     typedef int Expression;
     typedef int Statement;
     typedef int ExpressionList;
+    struct MemoryAddress {
+        MemoryAddress(void*) { }
+        MemoryAddress(int, uint32_t offset)
+            : offset(offset)
+        {
+        }
+        uint32_t offset;
+    };
     struct JumpTarget {
         Label label;
         JumpList jumpList;
@@ -199,6 +225,13 @@ public:
             appendCallWithExceptionCheck(operationThrowDivideError);
         }
 
+        if (!m_outOfBoundsErrorJumpList.empty()) {
+            m_outOfBoundsErrorJumpList.link(this);
+
+            setupArgumentsExecState();
+            appendCallWithExceptionCheck(operationThrowOutOfBoundsAccessError);
+        }
+
         if (!m_exceptionChecks.empty()) {
             m_exceptionChecks.link(this);
 
@@ -436,6 +469,117 @@ public:
         return UNUSED;
     }
 
+    int buildLoad(const MemoryAddress& memoryAddress, WASMExpressionType expressionType, WASMMemoryType memoryType, MemoryAccessConversion conversion)
+    {
+        const ArrayBuffer* arrayBuffer = m_module->arrayBuffer()->impl();
+        move(TrustedImmPtr(arrayBuffer->data()), GPRInfo::regT0);
+        load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1);
+        if (memoryAddress.offset)
+            add32(TrustedImm32(memoryAddress.offset), GPRInfo::regT1, GPRInfo::regT1);
+        and32(TrustedImm32(~(sizeOfMemoryType(memoryType) - 1)), GPRInfo::regT1);
+
+        ASSERT(arrayBuffer->byteLength() < (unsigned)(1 << 31));
+        if (arrayBuffer->byteLength() >= sizeOfMemoryType(memoryType))
+            m_outOfBoundsErrorJumpList.append(branch32(Above, GPRInfo::regT1, TrustedImm32(arrayBuffer->byteLength() - sizeOfMemoryType(memoryType))));
+        else
+            m_outOfBoundsErrorJumpList.append(jump());
+
+        BaseIndex address = BaseIndex(GPRInfo::regT0, GPRInfo::regT1, TimesOne);
+
+        switch (expressionType) {
+        case WASMExpressionType::I32:
+            switch (memoryType) {
+            case WASMMemoryType::I8:
+                if (conversion == MemoryAccessConversion::SignExtend)
+                    load8SignedExtendTo32(address, GPRInfo::regT0);
+                else {
+                    ASSERT(conversion == MemoryAccessConversion::ZeroExtend);
+                    load8(address, GPRInfo::regT0);
+                }
+                break;
+            case WASMMemoryType::I16:
+                if (conversion == MemoryAccessConversion::SignExtend)
+                    load16SignedExtendTo32(address, GPRInfo::regT0);
+                else {
+                    ASSERT(conversion == MemoryAccessConversion::ZeroExtend);
+                    load16(address, GPRInfo::regT0);
+                }
+                break;
+            case WASMMemoryType::I32:
+                load32(address, GPRInfo::regT0);
+                break;
+            default:
+                ASSERT_NOT_REACHED();
+            }
+            store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1));
+            break;
+        case WASMExpressionType::F32:
+            ASSERT(memoryType == WASMMemoryType::F32 && conversion == MemoryAccessConversion::NoConversion);
+            load32(address, GPRInfo::regT0);
+            store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1));
+            break;
+        case WASMExpressionType::F64:
+            ASSERT(memoryType == WASMMemoryType::F64 && conversion == MemoryAccessConversion::NoConversion);
+            loadDouble(address, FPRInfo::fpRegT0);
+            storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1));
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+        }
+        return UNUSED;
+    }
+
+    int buildStore(const MemoryAddress& memoryAddress, WASMExpressionType expressionType, WASMMemoryType memoryType, int)
+    {
+        const ArrayBuffer* arrayBuffer = m_module->arrayBuffer()->impl();
+        move(TrustedImmPtr(arrayBuffer->data()), GPRInfo::regT0);
+        load32(temporaryAddress(m_tempStackTop - 2), GPRInfo::regT1);
+        if (memoryAddress.offset)
+            add32(TrustedImm32(memoryAddress.offset), GPRInfo::regT1, GPRInfo::regT1);
+        and32(TrustedImm32(~(sizeOfMemoryType(memoryType) - 1)), GPRInfo::regT1);
+
+        ASSERT(arrayBuffer->byteLength() < (1u << 31));
+        if (arrayBuffer->byteLength() >= sizeOfMemoryType(memoryType))
+            m_outOfBoundsErrorJumpList.append(branch32(Above, GPRInfo::regT1, TrustedImm32(arrayBuffer->byteLength() - sizeOfMemoryType(memoryType))));
+        else
+            m_outOfBoundsErrorJumpList.append(jump());
+
+        BaseIndex address = BaseIndex(GPRInfo::regT0, GPRInfo::regT1, TimesOne);
+
+        switch (expressionType) {
+        case WASMExpressionType::I32:
+            load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT2);
+            switch (memoryType) {
+            case WASMMemoryType::I8:
+                store8(GPRInfo::regT2, address);
+                break;
+            case WASMMemoryType::I16:
+                store16(GPRInfo::regT2, address);
+                break;
+            case WASMMemoryType::I32:
+                store32(GPRInfo::regT2, address);
+                break;
+            default:
+                ASSERT_NOT_REACHED();
+            }
+            break;
+        case WASMExpressionType::F32:
+            ASSERT(memoryType == WASMMemoryType::F32);
+            load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT2);
+            store32(GPRInfo::regT2, address);
+            break;
+        case WASMExpressionType::F64:
+            ASSERT(memoryType == WASMMemoryType::F64);
+            loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0);
+            storeDouble(FPRInfo::fpRegT0, address);
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+        }
+        m_tempStackTop -= 2;
+        return UNUSED;
+    }
+
     int buildUnaryI32(int, WASMOpExpressionI32 op)
     {
         load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0);
@@ -1186,6 +1330,7 @@ private:
     Label m_beginLabel;
     Jump m_stackOverflow;
     JumpList m_divideErrorJumpList;
+    JumpList m_outOfBoundsErrorJumpList;
     JumpList m_exceptionChecks;
 
     Vector<std::pair<Call, void*>> m_calls;
index 176e6e3..53eab15 100644 (file)
@@ -143,6 +143,36 @@ ContextStatement WASMFunctionParser::parseStatement(Context& context)
         case WASMOpStatement::SetGlobal:
             parseSetGlobalStatement(context);
             break;
+        case WASMOpStatement::I32Store8:
+            parseStore(context, WASMExpressionType::I32, WASMMemoryType::I8, MemoryAccessOffsetMode::NoOffset);
+            break;
+        case WASMOpStatement::I32StoreWithOffset8:
+            parseStore(context, WASMExpressionType::I32, WASMMemoryType::I8, MemoryAccessOffsetMode::WithOffset);
+            break;
+        case WASMOpStatement::I32Store16:
+            parseStore(context, WASMExpressionType::I32, WASMMemoryType::I16, MemoryAccessOffsetMode::NoOffset);
+            break;
+        case WASMOpStatement::I32StoreWithOffset16:
+            parseStore(context, WASMExpressionType::I32, WASMMemoryType::I16, MemoryAccessOffsetMode::WithOffset);
+            break;
+        case WASMOpStatement::I32Store32:
+            parseStore(context, WASMExpressionType::I32, WASMMemoryType::I32, MemoryAccessOffsetMode::NoOffset);
+            break;
+        case WASMOpStatement::I32StoreWithOffset32:
+            parseStore(context, WASMExpressionType::I32, WASMMemoryType::I32, MemoryAccessOffsetMode::WithOffset);
+            break;
+        case WASMOpStatement::F32Store:
+            parseStore(context, WASMExpressionType::F32, WASMMemoryType::F32, MemoryAccessOffsetMode::NoOffset);
+            break;
+        case WASMOpStatement::F32StoreWithOffset:
+            parseStore(context, WASMExpressionType::F32, WASMMemoryType::F32, MemoryAccessOffsetMode::WithOffset);
+            break;
+        case WASMOpStatement::F64Store:
+            parseStore(context, WASMExpressionType::F64, WASMMemoryType::F64, MemoryAccessOffsetMode::NoOffset);
+            break;
+        case WASMOpStatement::F64StoreWithOffset:
+            parseStore(context, WASMExpressionType::F64, WASMMemoryType::F64, MemoryAccessOffsetMode::WithOffset);
+            break;
         case WASMOpStatement::Return:
             parseReturnStatement(context);
             break;
@@ -179,16 +209,6 @@ ContextStatement WASMFunctionParser::parseStatement(Context& context)
         case WASMOpStatement::Switch:
             parseSwitchStatement(context);
             break;
-        case WASMOpStatement::I32Store8:
-        case WASMOpStatement::I32StoreWithOffset8:
-        case WASMOpStatement::I32Store16:
-        case WASMOpStatement::I32StoreWithOffset16:
-        case WASMOpStatement::I32Store32:
-        case WASMOpStatement::I32StoreWithOffset32:
-        case WASMOpStatement::F32Store:
-        case WASMOpStatement::F32StoreWithOffset:
-        case WASMOpStatement::F64Store:
-        case WASMOpStatement::F64StoreWithOffset:
         case WASMOpStatement::CallInternal:
         case WASMOpStatement::CallIndirect:
         case WASMOpStatement::CallImport:
@@ -528,6 +548,26 @@ ContextExpression WASMFunctionParser::parseExpressionI32(Context& context)
             return parseGetLocalExpressionI32(context);
         case WASMOpExpressionI32::GetGlobal:
             return parseGetGlobalExpressionI32(context);
+        case WASMOpExpressionI32::SLoad8:
+            return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I8, MemoryAccessOffsetMode::NoOffset, MemoryAccessConversion::SignExtend);
+        case WASMOpExpressionI32::SLoadWithOffset8:
+            return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I8, MemoryAccessOffsetMode::WithOffset, MemoryAccessConversion::SignExtend);
+        case WASMOpExpressionI32::ULoad8:
+            return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I8, MemoryAccessOffsetMode::NoOffset, MemoryAccessConversion::ZeroExtend);
+        case WASMOpExpressionI32::ULoadWithOffset8:
+            return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I8, MemoryAccessOffsetMode::WithOffset, MemoryAccessConversion::ZeroExtend);
+        case WASMOpExpressionI32::SLoad16:
+            return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I16, MemoryAccessOffsetMode::NoOffset, MemoryAccessConversion::SignExtend);
+        case WASMOpExpressionI32::SLoadWithOffset16:
+            return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I16, MemoryAccessOffsetMode::WithOffset, MemoryAccessConversion::SignExtend);
+        case WASMOpExpressionI32::ULoad16:
+            return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I16, MemoryAccessOffsetMode::NoOffset, MemoryAccessConversion::ZeroExtend);
+        case WASMOpExpressionI32::ULoadWithOffset16:
+            return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I16, MemoryAccessOffsetMode::WithOffset, MemoryAccessConversion::ZeroExtend);
+        case WASMOpExpressionI32::Load32:
+            return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I32, MemoryAccessOffsetMode::NoOffset);
+        case WASMOpExpressionI32::LoadWithOffset32:
+            return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I32, MemoryAccessOffsetMode::WithOffset);
         case WASMOpExpressionI32::CallInternal:
             return parseCallInternal(context, WASMExpressionType::I32);
         case WASMOpExpressionI32::CallIndirect:
@@ -585,16 +625,6 @@ ContextExpression WASMFunctionParser::parseExpressionI32(Context& context)
             return parseRelationalF64ExpressionI32(context, op);
         case WASMOpExpressionI32::SetLocal:
         case WASMOpExpressionI32::SetGlobal:
-        case WASMOpExpressionI32::SLoad8:
-        case WASMOpExpressionI32::SLoadWithOffset8:
-        case WASMOpExpressionI32::ULoad8:
-        case WASMOpExpressionI32::ULoadWithOffset8:
-        case WASMOpExpressionI32::SLoad16:
-        case WASMOpExpressionI32::SLoadWithOffset16:
-        case WASMOpExpressionI32::ULoad16:
-        case WASMOpExpressionI32::ULoadWithOffset16:
-        case WASMOpExpressionI32::Load32:
-        case WASMOpExpressionI32::LoadWithOffset32:
         case WASMOpExpressionI32::Store8:
         case WASMOpExpressionI32::StoreWithOffset8:
         case WASMOpExpressionI32::Store16:
@@ -748,6 +778,10 @@ ContextExpression WASMFunctionParser::parseExpressionF32(Context& context)
             return parseGetLocalExpressionF32(context);
         case WASMOpExpressionF32::GetGlobal:
             return parseGetGlobalExpressionF32(context);
+        case WASMOpExpressionF32::Load:
+            return parseLoad(context, WASMExpressionType::F32, WASMMemoryType::F32, MemoryAccessOffsetMode::NoOffset);
+        case WASMOpExpressionF32::LoadWithOffset:
+            return parseLoad(context, WASMExpressionType::F32, WASMMemoryType::F32, MemoryAccessOffsetMode::WithOffset);
         case WASMOpExpressionF32::CallInternal:
             return parseCallInternal(context, WASMExpressionType::F32);
         case WASMOpExpressionF32::CallIndirect:
@@ -771,8 +805,6 @@ ContextExpression WASMFunctionParser::parseExpressionF32(Context& context)
             return parseBinaryExpressionF32(context, op);
         case WASMOpExpressionF32::SetLocal:
         case WASMOpExpressionF32::SetGlobal:
-        case WASMOpExpressionF32::Load:
-        case WASMOpExpressionF32::LoadWithOffset:
         case WASMOpExpressionF32::Store:
         case WASMOpExpressionF32::StoreWithOffset:
         case WASMOpExpressionF32::Conditional:
@@ -880,6 +912,10 @@ ContextExpression WASMFunctionParser::parseExpressionF64(Context& context)
             return parseGetLocalExpressionF64(context);
         case WASMOpExpressionF64::GetGlobal:
             return parseGetGlobalExpressionF64(context);
+        case WASMOpExpressionF64::Load:
+            return parseLoad(context, WASMExpressionType::F64, WASMMemoryType::F64, MemoryAccessOffsetMode::NoOffset);
+        case WASMOpExpressionF64::LoadWithOffset:
+            return parseLoad(context, WASMExpressionType::F64, WASMMemoryType::F64, MemoryAccessOffsetMode::WithOffset);
         case WASMOpExpressionF64::CallInternal:
             return parseCallInternal(context, WASMExpressionType::F64);
         case WASMOpExpressionF64::CallImport:
@@ -894,8 +930,6 @@ ContextExpression WASMFunctionParser::parseExpressionF64(Context& context)
             return parseConvertType(context, WASMExpressionType::F32, WASMExpressionType::F64, WASMTypeConversion::Promote);
         case WASMOpExpressionF64::SetLocal:
         case WASMOpExpressionF64::SetGlobal:
-        case WASMOpExpressionF64::Load:
-        case WASMOpExpressionF64::LoadWithOffset:
         case WASMOpExpressionF64::Store:
         case WASMOpExpressionF64::StoreWithOffset:
         case WASMOpExpressionF64::Conditional:
@@ -990,6 +1024,38 @@ ContextExpression WASMFunctionParser::parseGetGlobalExpressionF64(Context& conte
 }
 
 template <class Context>
+ContextMemoryAddress WASMFunctionParser::parseMemoryAddress(Context& context, MemoryAccessOffsetMode offsetMode)
+{
+    uint32_t offset = 0;
+    if (offsetMode == MemoryAccessOffsetMode::WithOffset)
+        READ_COMPACT_UINT32_OR_FAIL(offset, "Cannot read the address offset.");
+    ContextExpression index = parseExpressionI32(context);
+    PROPAGATE_ERROR();
+    return ContextMemoryAddress(index, offset);
+}
+
+template <class Context>
+ContextExpression WASMFunctionParser::parseLoad(Context& context, WASMExpressionType expressionType, WASMMemoryType memoryType, MemoryAccessOffsetMode offsetMode, MemoryAccessConversion conversion)
+{
+    FAIL_IF_FALSE(m_module->arrayBuffer(), "An ArrayBuffer is not provided.");
+    const ContextMemoryAddress& memoryAddress = parseMemoryAddress(context, offsetMode);
+    PROPAGATE_ERROR();
+    return context.buildLoad(memoryAddress, expressionType, memoryType, conversion);
+}
+
+template <class Context>
+ContextExpression WASMFunctionParser::parseStore(Context& context, WASMExpressionType expressionType, WASMMemoryType memoryType, MemoryAccessOffsetMode offsetMode)
+{
+    FAIL_IF_FALSE(m_module->arrayBuffer(), "An ArrayBuffer is not provided.");
+    const ContextMemoryAddress& memoryAddress = parseMemoryAddress(context, offsetMode);
+    PROPAGATE_ERROR();
+
+    ContextExpression value = parseExpression(context, expressionType);
+    PROPAGATE_ERROR();
+    return context.buildStore(memoryAddress, expressionType, memoryType, value);
+}
+
+template <class Context>
 ContextExpressionList WASMFunctionParser::parseCallArguments(Context& context, const Vector<WASMType>& arguments)
 {
     ContextExpressionList argumentList;
index f6c9a76..1631118 100644 (file)
@@ -34,6 +34,7 @@
 #define ContextExpression typename Context::Expression
 #define ContextStatement typename Context::Statement
 #define ContextExpressionList typename Context::ExpressionList
+#define ContextMemoryAddress typename Context::MemoryAddress
 #define ContextJumpTarget typename Context::JumpTarget
 
 namespace JSC {
@@ -113,6 +114,10 @@ private:
     template <class Context> ContextExpression parseGetLocalExpressionF64(Context&);
     template <class Context> ContextExpression parseGetGlobalExpressionF64(Context&);
 
+    template <class Context> ContextMemoryAddress parseMemoryAddress(Context&, MemoryAccessOffsetMode);
+    template <class Context> ContextExpression parseLoad(Context&, WASMExpressionType, WASMMemoryType, MemoryAccessOffsetMode, MemoryAccessConversion = MemoryAccessConversion::NoConversion);
+    template <class Context> ContextExpression parseStore(Context&, WASMExpressionType, WASMMemoryType, MemoryAccessOffsetMode);
+
     template <class Context> ContextExpressionList parseCallArguments(Context&, const Vector<WASMType>& arguments);
     template <class Context> ContextExpression parseCallInternal(Context&, WASMExpressionType returnType);
     template <class Context> ContextExpression parseCallIndirect(Context&, WASMExpressionType returnType);
index 2d3b2c2..46aca5d 100644 (file)
@@ -37,6 +37,10 @@ public:
     typedef int Expression;
     typedef int Statement;
     typedef int ExpressionList;
+    struct MemoryAddress {
+        MemoryAddress(void*) { }
+        MemoryAddress(int, uint32_t) { }
+    };
     typedef int JumpTarget;
     enum class JumpCondition { Zero, NonZero };
 
@@ -106,6 +110,17 @@ public:
         return UNUSED;
     }
 
+    int buildLoad(const MemoryAddress&, WASMExpressionType, WASMMemoryType, MemoryAccessConversion)
+    {
+        return UNUSED;
+    }
+
+    int buildStore(const MemoryAddress&, WASMExpressionType, WASMMemoryType, int)
+    {
+        m_tempStackTop -= 2;
+        return UNUSED;
+    }
+
     int buildUnaryI32(int, WASMOpExpressionI32)
     {
         return UNUSED;
index c9442a1..715b5e1 100644 (file)
@@ -28,6 +28,7 @@
 
 #if ENABLE(WEBASSEMBLY)
 
+#include "JSArrayBuffer.h"
 #include "JSCInlines.h"
 #include "JSWASMModule.h"
 #include "StrongInlines.h"
 
 namespace JSC {
 
-WASMModuleParser::WASMModuleParser(VM& vm, JSGlobalObject* globalObject, const SourceCode& source, JSObject* imports)
+WASMModuleParser::WASMModuleParser(VM& vm, JSGlobalObject* globalObject, const SourceCode& source, JSObject* imports, JSArrayBuffer* arrayBuffer)
     : m_vm(vm)
     , m_globalObject(vm, globalObject)
     , m_source(source)
     , m_imports(vm, imports)
     , m_reader(static_cast<WebAssemblySourceProvider*>(source.provider())->data())
+    , m_module(vm, JSWASMModule::create(vm, globalObject->wasmModuleStructure(), arrayBuffer))
 {
 }
 
 JSWASMModule* WASMModuleParser::parse(ExecState* exec, String& errorMessage)
 {
-    m_module.set(m_vm, JSWASMModule::create(m_vm, m_globalObject->wasmModuleStructure()));
     parseModule(exec);
     if (!m_errorMessage.isNull()) {
         errorMessage = m_errorMessage;
@@ -93,6 +94,9 @@ void WASMModuleParser::parseModule(ExecState* exec)
     parseFunctionDefinitionSection();
     PROPAGATE_ERROR();
     parseExportSection();
+    PROPAGATE_ERROR();
+
+    FAIL_IF_FALSE(!m_module->arrayBuffer() || m_module->arrayBuffer()->impl()->byteLength() < (1u << 31), "The ArrayBuffer's length must be less than 2^31.");
 }
 
 void WASMModuleParser::parseConstantPoolSection()
@@ -362,9 +366,9 @@ void WASMModuleParser::getImportedValue(ExecState* exec, const String& importNam
     value = slot.getValue(exec, identifier);
 }
 
-JSWASMModule* parseWebAssembly(ExecState* exec, const SourceCode& source, JSObject* imports, String& errorMessage)
+JSWASMModule* parseWebAssembly(ExecState* exec, const SourceCode& source, JSObject* imports, JSArrayBuffer* arrayBuffer, String& errorMessage)
 {
-    WASMModuleParser moduleParser(exec->vm(), exec->lexicalGlobalObject(), source, imports);
+    WASMModuleParser moduleParser(exec->vm(), exec->lexicalGlobalObject(), source, imports, arrayBuffer);
     return moduleParser.parse(exec, errorMessage);
 }
 
index f8b3113..5017bc1 100644 (file)
@@ -35,6 +35,7 @@
 namespace JSC {
 
 class ExecState;
+class JSArrayBuffer;
 class JSGlobalObject;
 class JSWASMModule;
 class SourceCode;
@@ -42,7 +43,7 @@ class VM;
 
 class WASMModuleParser {
 public:
-    WASMModuleParser(VM&, JSGlobalObject*, const SourceCode&, JSObject* imports);
+    WASMModuleParser(VM&, JSGlobalObject*, const SourceCode&, JSObject* imports, JSArrayBuffer*);
     JSWASMModule* parse(ExecState*, String& errorMessage);
 
 private:
@@ -67,7 +68,7 @@ private:
     String m_errorMessage;
 };
 
-JS_EXPORT_PRIVATE JSWASMModule* parseWebAssembly(ExecState*, const SourceCode&, JSObject* imports, String& errorMessage);
+JS_EXPORT_PRIVATE JSWASMModule* parseWebAssembly(ExecState*, const SourceCode&, JSObject* imports, JSArrayBuffer*, String& errorMessage);
 
 } // namespace JSC