WebAssembly JS API: improve Instance
authorjfbastien@apple.com <jfbastien@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 Nov 2016 07:22:17 +0000 (07:22 +0000)
committerjfbastien@apple.com <jfbastien@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 Nov 2016 07:22:17 +0000 (07:22 +0000)
https://bugs.webkit.org/show_bug.cgi?id=164757

Reviewed by Keith Miller.

JSTests:

An Instance's `exports` property wasn't populated with exports.

A follow-up patch will do imports.

A few things of note:

 - LowLevelBinary: support 3-byte integers.
 - LowLevelBinary: support proper UTF-8 2003 code points (instead of UTF-16).

* wasm/Builder.js:
* wasm/Builder_WebAssemblyBinary.js: wire up exports, stub other things out some more
(const.emitters.Export):
* wasm/LowLevelBinary.js:
(export.default.LowLevelBinary.prototype.uint24): add, used for UTF-8
(export.default.LowLevelBinary.prototype.string): support UTF-8
(export.default.LowLevelBinary.prototype.getUint24): add, used for UTF-8
(export.default.LowLevelBinary.prototype.getVaruint1): was missing
(export.default.LowLevelBinary.prototype.getString): support UTF-8
(export.default.LowLevelBinary):
* wasm/js-api/test_Instance.js: instance.exports.answer() // <-- this is where the magic of this entire patch is
(ExportedAnswerI32):
* wasm/js-api/test_basic_api.js: punt test to later
(const.c.in.constructorProperties.switch):
* wasm/self-test/test_BuilderWebAssembly.js: UTF-8
(CustomSection):
* wasm/self-test/test_LowLevelBinary_string.js: UTF-8 now works
* wasm/self-test/test_LowLevelBinary_uint16.js: was missing one value
* wasm/self-test/test_LowLevelBinary_uint24.js: Copied from JSTests/wasm/self-test/test_LowLevelBinary_uint8.js.
* wasm/self-test/test_LowLevelBinary_uint8.js: was missing one value
* wasm/self-test/test_LowLevelBinary_varuint1.js: Added.
* wasm/utilities.js: this `dump` thing was useful
(const._dump):

Source/JavaScriptCore:

An Instance's `exports` property wasn't populated with exports.

According to the spec [0], `exports` should present itself as a WebAssembly
Module Record. In order to do this we need to split JSModuleRecord into
AbstractModuleRecord (without the `link` and `evaluate` functions), and
JSModuleRecord (which implements link and evaluate). We can then have a separate
WebAssemblyModuleRecord which shares most of the implementation.

`exports` then maps function names to WebAssemblyFunction and
WebAssemblyFunctionCell, which call into the B3-generated WebAssembly code.

A follow-up patch will do imports.

A few things of note:

 - Use Identifier instead of String. They get uniqued, we need them for the JSModuleNamespaceObject. This is safe because JSWebAssemblyModule creation is on the main thread.
 - JSWebAssemblyInstance needs to refer to the JSWebAssemblyModule used to create it, because the module owns the code, identifiers, etc. The world would be very sad if it got GC'd.
 - Instance.exports shouldn't use putWithoutTransition because it affects all Structures, whereas here each instance needs its own exports.
 - Expose the compiled functions, and pipe them to the InstanceConstructor. Start moving things around to split JSModuleRecord out into JS and WebAssembly parts.

  [0]: https://github.com/WebAssembly/design/blob/master/JS.md#webassemblyinstance-constructor

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* runtime/AbstractModuleRecord.cpp: Copied from Source/JavaScriptCore/runtime/JSModuleRecord.cpp, which I split in two
(JSC::AbstractModuleRecord::AbstractModuleRecord):
(JSC::AbstractModuleRecord::destroy):
(JSC::AbstractModuleRecord::finishCreation):
(JSC::AbstractModuleRecord::visitChildren):
(JSC::AbstractModuleRecord::appendRequestedModule):
(JSC::AbstractModuleRecord::addStarExportEntry):
(JSC::AbstractModuleRecord::addImportEntry):
(JSC::AbstractModuleRecord::addExportEntry):
(JSC::identifierToJSValue):
(JSC::AbstractModuleRecord::hostResolveImportedModule):
(JSC::AbstractModuleRecord::ResolveQuery::ResolveQuery):
(JSC::AbstractModuleRecord::ResolveQuery::isEmptyValue):
(JSC::AbstractModuleRecord::ResolveQuery::isDeletedValue):
(JSC::AbstractModuleRecord::ResolveQuery::Hash::hash):
(JSC::AbstractModuleRecord::ResolveQuery::Hash::equal):
(JSC::AbstractModuleRecord::cacheResolution):
(JSC::getExportedNames):
(JSC::AbstractModuleRecord::getModuleNamespace):
(JSC::printableName):
(JSC::AbstractModuleRecord::dump):
* runtime/AbstractModuleRecord.h: Copied from Source/JavaScriptCore/runtime/JSModuleRecord.h.
(JSC::AbstractModuleRecord::ImportEntry::isNamespace):
(JSC::AbstractModuleRecord::sourceCode):
(JSC::AbstractModuleRecord::moduleKey):
(JSC::AbstractModuleRecord::requestedModules):
(JSC::AbstractModuleRecord::exportEntries):
(JSC::AbstractModuleRecord::importEntries):
(JSC::AbstractModuleRecord::starExportEntries):
(JSC::AbstractModuleRecord::declaredVariables):
(JSC::AbstractModuleRecord::lexicalVariables):
(JSC::AbstractModuleRecord::moduleEnvironment):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::webAssemblyModuleRecordStructure):
(JSC::JSGlobalObject::webAssemblyFunctionStructure):
* runtime/JSModuleEnvironment.cpp:
(JSC::JSModuleEnvironment::create):
(JSC::JSModuleEnvironment::finishCreation):
(JSC::JSModuleEnvironment::getOwnPropertySlot):
(JSC::JSModuleEnvironment::getOwnNonIndexPropertyNames):
(JSC::JSModuleEnvironment::put):
(JSC::JSModuleEnvironment::deleteProperty):
* runtime/JSModuleEnvironment.h:
(JSC::JSModuleEnvironment::create):
(JSC::JSModuleEnvironment::offsetOfModuleRecord):
(JSC::JSModuleEnvironment::allocationSize):
(JSC::JSModuleEnvironment::moduleRecord):
(JSC::JSModuleEnvironment::moduleRecordSlot):
* runtime/JSModuleNamespaceObject.cpp:
(JSC::JSModuleNamespaceObject::finishCreation):
(JSC::JSModuleNamespaceObject::getOwnPropertySlot):
* runtime/JSModuleNamespaceObject.h:
(JSC::JSModuleNamespaceObject::create):
(JSC::JSModuleNamespaceObject::moduleRecord):
* runtime/JSModuleRecord.cpp:
(JSC::JSModuleRecord::createStructure):
(JSC::JSModuleRecord::create):
(JSC::JSModuleRecord::JSModuleRecord):
(JSC::JSModuleRecord::destroy):
(JSC::JSModuleRecord::finishCreation):
(JSC::JSModuleRecord::visitChildren):
(JSC::JSModuleRecord::instantiateDeclarations):
* runtime/JSModuleRecord.h:
* runtime/JSScope.cpp:
(JSC::abstractAccess):
(JSC::JSScope::collectClosureVariablesUnderTDZ):
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:
* wasm/JSWebAssembly.h:
* wasm/WasmFormat.h: use Identifier instead of String
* wasm/WasmModuleParser.cpp:
(JSC::Wasm::ModuleParser::parse):
(JSC::Wasm::ModuleParser::parseType):
(JSC::Wasm::ModuleParser::parseImport): fix off-by-one
(JSC::Wasm::ModuleParser::parseFunction):
(JSC::Wasm::ModuleParser::parseExport):
* wasm/WasmModuleParser.h:
(JSC::Wasm::ModuleParser::ModuleParser):
* wasm/WasmPlan.cpp:
(JSC::Wasm::Plan::run):
* wasm/js/JSWebAssemblyInstance.cpp:
(JSC::JSWebAssemblyInstance::create):
(JSC::JSWebAssemblyInstance::finishCreation):
(JSC::JSWebAssemblyInstance::visitChildren):
* wasm/js/JSWebAssemblyInstance.h:
(JSC::JSWebAssemblyInstance::module):
* wasm/js/JSWebAssemblyModule.cpp:
(JSC::JSWebAssemblyModule::create):
(JSC::JSWebAssemblyModule::finishCreation):
(JSC::JSWebAssemblyModule::visitChildren):
* wasm/js/JSWebAssemblyModule.h:
(JSC::JSWebAssemblyModule::moduleInformation):
(JSC::JSWebAssemblyModule::compiledFunctions):
(JSC::JSWebAssemblyModule::exportSymbolTable):
* wasm/js/WebAssemblyFunction.cpp: Added.
(JSC::callWebAssemblyFunction):
(JSC::WebAssemblyFunction::create):
(JSC::WebAssemblyFunction::createStructure):
(JSC::WebAssemblyFunction::WebAssemblyFunction):
(JSC::WebAssemblyFunction::visitChildren):
(JSC::WebAssemblyFunction::finishCreation):
* wasm/js/WebAssemblyFunction.h: Copied from Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h.
(JSC::CallableWebAssemblyFunction::CallableWebAssemblyFunction):
(JSC::WebAssemblyFunction::webAssemblyFunctionCell):
* wasm/js/WebAssemblyFunctionCell.cpp: Copied from Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h.
(JSC::WebAssemblyFunctionCell::create):
(JSC::WebAssemblyFunctionCell::WebAssemblyFunctionCell):
(JSC::WebAssemblyFunctionCell::destroy):
(JSC::WebAssemblyFunctionCell::createStructure):
* wasm/js/WebAssemblyFunctionCell.h: Copied from Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h.
(JSC::WebAssemblyFunctionCell::function):
* wasm/js/WebAssemblyInstanceConstructor.cpp:
(JSC::constructJSWebAssemblyInstance):
* wasm/js/WebAssemblyModuleConstructor.cpp:
(JSC::constructJSWebAssemblyModule):
* wasm/js/WebAssemblyModuleRecord.cpp: Added.
(JSC::WebAssemblyModuleRecord::createStructure):
(JSC::WebAssemblyModuleRecord::create):
(JSC::WebAssemblyModuleRecord::WebAssemblyModuleRecord):
(JSC::WebAssemblyModuleRecord::destroy):
(JSC::WebAssemblyModuleRecord::finishCreation):
(JSC::WebAssemblyModuleRecord::visitChildren):
(JSC::WebAssemblyModuleRecord::link):
(JSC::WebAssemblyModuleRecord::evaluate):
* wasm/js/WebAssemblyModuleRecord.h: Copied from Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h.

Source/WTF:

* wtf/Expected.h:
(WTF::ExpectedDetail::destroy): silence a warning

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

50 files changed:
JSTests/ChangeLog
JSTests/wasm/Builder.js
JSTests/wasm/Builder_WebAssemblyBinary.js
JSTests/wasm/LowLevelBinary.js
JSTests/wasm/README.md
JSTests/wasm/js-api/test_Instance.js
JSTests/wasm/js-api/test_basic_api.js
JSTests/wasm/self-test/test_BuilderWebAssembly.js
JSTests/wasm/self-test/test_LowLevelBinary_string.js
JSTests/wasm/self-test/test_LowLevelBinary_uint16.js
JSTests/wasm/self-test/test_LowLevelBinary_uint24.js [new file with mode: 0644]
JSTests/wasm/self-test/test_LowLevelBinary_uint8.js
JSTests/wasm/self-test/test_LowLevelBinary_varuint1.js [new file with mode: 0644]
JSTests/wasm/test.sh
JSTests/wasm/utilities.js
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/runtime/AbstractModuleRecord.cpp [new file with mode: 0644]
Source/JavaScriptCore/runtime/AbstractModuleRecord.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSModuleEnvironment.cpp
Source/JavaScriptCore/runtime/JSModuleEnvironment.h
Source/JavaScriptCore/runtime/JSModuleNamespaceObject.cpp
Source/JavaScriptCore/runtime/JSModuleNamespaceObject.h
Source/JavaScriptCore/runtime/JSModuleRecord.cpp
Source/JavaScriptCore/runtime/JSModuleRecord.h
Source/JavaScriptCore/runtime/JSScope.cpp
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/wasm/JSWebAssembly.h
Source/JavaScriptCore/wasm/WasmFormat.h
Source/JavaScriptCore/wasm/WasmModuleParser.cpp
Source/JavaScriptCore/wasm/WasmModuleParser.h
Source/JavaScriptCore/wasm/WasmPlan.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp [new file with mode: 0644]
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.h [new file with mode: 0644]
Source/JavaScriptCore/wasm/js/WebAssemblyFunctionCell.cpp [new file with mode: 0644]
Source/JavaScriptCore/wasm/js/WebAssemblyFunctionCell.h [new file with mode: 0644]
Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyModuleConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp [new file with mode: 0644]
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.h [new file with mode: 0644]
Source/WTF/ChangeLog
Source/WTF/wtf/Expected.h

index 3c102a7..29c533c 100644 (file)
@@ -1,3 +1,43 @@
+2016-11-29  JF Bastien  <jfbastien@apple.com>
+
+        WebAssembly JS API: improve Instance
+        https://bugs.webkit.org/show_bug.cgi?id=164757
+
+        Reviewed by Keith Miller.
+
+        An Instance's `exports` property wasn't populated with exports.
+
+        A follow-up patch will do imports.
+
+        A few things of note:
+
+         - LowLevelBinary: support 3-byte integers.
+         - LowLevelBinary: support proper UTF-8 2003 code points (instead of UTF-16).
+
+        * wasm/Builder.js:
+        * wasm/Builder_WebAssemblyBinary.js: wire up exports, stub other things out some more
+        (const.emitters.Export):
+        * wasm/LowLevelBinary.js:
+        (export.default.LowLevelBinary.prototype.uint24): add, used for UTF-8
+        (export.default.LowLevelBinary.prototype.string): support UTF-8
+        (export.default.LowLevelBinary.prototype.getUint24): add, used for UTF-8
+        (export.default.LowLevelBinary.prototype.getVaruint1): was missing
+        (export.default.LowLevelBinary.prototype.getString): support UTF-8
+        (export.default.LowLevelBinary):
+        * wasm/js-api/test_Instance.js: instance.exports.answer() // <-- this is where the magic of this entire patch is
+        (ExportedAnswerI32):
+        * wasm/js-api/test_basic_api.js: punt test to later
+        (const.c.in.constructorProperties.switch):
+        * wasm/self-test/test_BuilderWebAssembly.js: UTF-8
+        (CustomSection):
+        * wasm/self-test/test_LowLevelBinary_string.js: UTF-8 now works
+        * wasm/self-test/test_LowLevelBinary_uint16.js: was missing one value
+        * wasm/self-test/test_LowLevelBinary_uint24.js: Copied from JSTests/wasm/self-test/test_LowLevelBinary_uint8.js.
+        * wasm/self-test/test_LowLevelBinary_uint8.js: was missing one value
+        * wasm/self-test/test_LowLevelBinary_varuint1.js: Added.
+        * wasm/utilities.js: this `dump` thing was useful
+        (const._dump):
+
 2016-11-29  Saam Barati  <sbarati@apple.com>
 
         We should be able optimize the pattern where we spread a function's rest parameter to another call
index c39db5f..61c9c49 100644 (file)
@@ -216,7 +216,10 @@ const _checkImms = (op, imms, expectedImms, ret) => {
             // Control:
         case "default_target": throw new Error(`Unimplemented: "${expect.name}" on "${op}"`);
         case "relative_depth": throw new Error(`Unimplemented: "${expect.name}" on "${op}"`);
-        case "sig": throw new Error(`Unimplemented: "${expect.name}" on "${op}"`);
+        case "sig":
+            // FIXME this should be isValidBlockType https://bugs.webkit.org/show_bug.cgi?id=164724
+            assert.truthy(WASM.isValidValueType(imms[idx]), `Invalid block type on ${op}: "${imms[idx]}"`);
+            break;
         case "target_count": throw new Error(`Unimplemented: "${expect.name}" on "${op}"`);
         case "target_table": throw new Error(`Unimplemented: "${expect.name}" on "${op}"`);
         default: throw new Error(`Implementation problem: unhandled immediate "${expect.name}" on "${op}"`);
@@ -355,6 +358,7 @@ export default class Builder {
                     return typeBuilder;
                 };
                 break;
+
             case "Import":
                 this[section] = function() {
                     const s = this._addSection(section);
@@ -369,20 +373,22 @@ export default class Builder {
                 };
                 break;
 
-            case "Export":
+            case "Function":
                 this[section] = function() {
                     const s = this._addSection(section);
                     const exportBuilder = {
-                        End: () => this,
-                        Table: () => { throw new Error(`Unimplemented: export table`); },
-                        Memory: () => { throw new Error(`Unimplemented: export memory`); },
-                        Global: () => { throw new Error(`Unimplemented: export global`); },
+                        End: () => this
+                        // FIXME: add ability to add this with whatever.
                     };
-                    exportBuilder.Function = _exportFunctionContinuation(this, s, exportBuilder);
                     return exportBuilder;
                 };
                 break;
 
+            case "Table":
+                // FIXME Implement table https://bugs.webkit.org/show_bug.cgi?id=164135
+                this[section] = () => { throw new Error(`Unimplemented: section type "${section}"`); };
+                break;
+
             case "Memory":
                 this[section] = function() {
                     const s = this._addSection(section);
@@ -394,18 +400,36 @@ export default class Builder {
                         }
                     };
                     return exportBuilder;
-                }
+                };
                 break;
 
-            case "Function":
+            case "Global":
+                // FIXME implement global https://bugs.webkit.org/show_bug.cgi?id=164133
+                this[section] = () => { throw new Error(`Unimplemented: section type "${section}"`); };
+                break;
+
+            case "Export":
                 this[section] = function() {
                     const s = this._addSection(section);
                     const exportBuilder = {
-                        End: () => this
-                        // FIXME: add ability to add this with whatever.
-                    }
+                        End: () => this,
+                        Table: () => { throw new Error(`Unimplemented: export table`); },
+                        Memory: () => { throw new Error(`Unimplemented: export memory`); },
+                        Global: () => { throw new Error(`Unimplemented: export global`); },
+                    };
+                    exportBuilder.Function = _exportFunctionContinuation(this, s, exportBuilder);
                     return exportBuilder;
-                }
+                };
+                break;
+
+            case "Start":
+                // FIXME implement start https://bugs.webkit.org/show_bug.cgi?id=161709
+                this[section] = () => { throw new Error(`Unimplemented: section type "${section}"`); };
+                break;
+
+            case "Element":
+                // FIXME implement element https://bugs.webkit.org/show_bug.cgi?id=161709
+                this[section] = () => { throw new Error(`Unimplemented: section type "${section}"`); };
                 break;
 
             case "Code":
@@ -453,11 +477,17 @@ export default class Builder {
                 };
                 break;
 
-            default:
+            case "Data":
+                // FIXME implement data https://bugs.webkit.org/show_bug.cgi?id=161709
                 this[section] = () => { throw new Error(`Unimplemented: section type "${section}"`); };
                 break;
+
+            default:
+                this[section] = () => { throw new Error(`Unknown section type "${section}"`); };
+                break;
             }
         }
+
         this.Unknown = function(name) {
             const s = this._addSection(name);
             const builder = this;
index 0ab058f..2103ed3 100644 (file)
@@ -82,7 +82,20 @@ const emitters = {
     },
 
     Global: (section, bin) => { throw new Error(`Not yet implemented`); },
-    Export: (section, bin) => { throw new Error(`Not yet implemented`); },
+    Export: (section, bin) => {
+        put(bin, "varuint32", section.data.length);
+        for (const entry of section.data) {
+            put(bin, "string", entry.field);
+            put(bin, "uint8", WASM.externalKindValue[entry.kind]);
+            switch (entry.kind) {
+            default: throw new Error(`Implementation problem: unexpected kind ${entry.kind}`);
+            case "Function": put(bin, "varuint32", entry.index); break;
+            case "Table": throw new Error(`Not yet implemented`);
+            case "Memory": throw new Error(`Not yet implemented`);
+            case "Global": throw new Error(`Not yet implemented`);
+            }
+        }
+    },
     Start: (section, bin) => { throw new Error(`Not yet implemented`); },
     Element: (section, bin) => { throw new Error(`Not yet implemented`); },
 
index e43b346..0bd4237 100644 (file)
@@ -23,6 +23,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import * as assert from 'assert.js';
 import * as WASM from 'WASM.js';
 
 const _initialAllocationSize = 1024;
@@ -104,6 +105,14 @@ export default class LowLevelBinary {
         this._push8(v);
         this._push8(v >>> 8);
     }
+    uint24(v) {
+        if ((v & 0xFFFFFF) >>> 0 !== v)
+            throw new RangeError(`Invalid uint24 ${v}`);
+        this._maybeGrow(3);
+        this._push8(v);
+        this._push8(v >>> 8);
+        this._push8(v >>> 16);
+    }
     uint32(v) {
         if ((v & 0xFFFFFFFF) >>> 0 !== v)
             throw new RangeError(`Invalid uint32 ${v}`);
@@ -155,8 +164,24 @@ export default class LowLevelBinary {
     }
     string(str) {
         let patch = this.newPatchable("varuint32");
-        for (const char of str)
-            patch.uint16(char.charCodeAt());
+        for (const char of str) {
+            // Encode UTF-8 2003 code points.
+            const code = char.codePointAt();
+            if (code <= 0x007F) {
+                const utf8 = code;
+                patch.uint8(utf8);
+            } else if (code <= 0x07FF) {
+                const utf8 = 0x80C0 | ((code & 0x7C0) >> 6) | ((code & 0x3F) << 8);
+                patch.uint16(utf8);
+            } else if (code <= 0xFFFF) {
+                const utf8 = 0x8080E0 | ((code & 0xF000) >> 12) | ((code & 0xFC0) << 2) | ((code & 0x3F) << 16);
+                patch.uint24(utf8);
+            } else if (code <= 0x10FFFF) {
+                const utf8 = (0x808080F0 | ((code & 0x1C0000) >> 18) | ((code & 0x3F000) >> 4) | ((code & 0xFC0) << 10) | ((code & 0x3F) << 24)) >>> 0;
+                patch.uint32(utf8);
+            } else
+                throw new Error(`Unexpectedly large UTF-8 character code point '${char}' 0x${code.toString(16)}`);
+        }
         patch.apply();
     }
 
@@ -170,6 +195,10 @@ export default class LowLevelBinary {
         _getterRangeCheck(this, at, 2);
         return this._buf[at] | (this._buf[at + 1] << 8);
     }
+    getUint24(at) {
+        _getterRangeCheck(this, at, 3);
+        return this._buf[at] | (this._buf[at + 1] << 8) | (this._buf[at + 2] << 16);
+    }
     getUint32(at) {
         _getterRangeCheck(this, at, 4);
         return (this._buf[at] | (this._buf[at + 1] << 8) | (this._buf[at + 2] << 16) | (this._buf[at + 3] << 24)) >>> 0;
@@ -205,6 +234,11 @@ export default class LowLevelBinary {
         }
         return { value: v, next: at };
     }
+    getVaruint1(at) {
+        const res = this.getVaruint32(at);
+        if (res.value !== 0 && res.value !== 1) throw new Error(`Expected a varuint1, got value ${res.value}`);
+        return res;
+    }
     getVaruint7(at) {
         const res = this.getVaruint32(at);
         if (res.value > varuint7Max) throw new Error(`Expected a varuint7, got value ${res.value}`);
@@ -212,9 +246,39 @@ export default class LowLevelBinary {
     }
     getString(at) {
         const size = this.getVaruint32(at);
+        const last = size.next + size.value;
+        let i = size.next;
         let str = "";
-        for (let i = size.next; i !== size.next + size.value; i += 2)
-            str += String.fromCharCode(this.getUint16(i));
+        while (i < last) {
+            // Decode UTF-8 2003 code points.
+            const peek = this.getUint8(i);
+            let code;
+            if ((peek & 0x80) === 0x0) {
+                const utf8 = this.getUint8(i);
+                assert.eq(utf8 & 0x80, 0x00);
+                i += 1;
+                code = utf8;
+            } else if ((peek & 0xE0) === 0xC0) {
+                const utf8 = this.getUint16(i);
+                assert.eq(utf8 & 0xC0E0, 0x80C0);
+                i += 2;
+                code = ((utf8 & 0x1F) << 6) | ((utf8 & 0x3F00) >> 8);
+            } else if ((peek & 0xF0) === 0xE0) {
+                const utf8 = this.getUint24(i);
+                assert.eq(utf8 & 0xC0C0F0, 0x8080E0);
+                i += 3;
+                code = ((utf8 & 0xF) << 12) | ((utf8 & 0x3F00) >> 2) | ((utf8 & 0x3F0000) >> 16);
+            } else if ((peek & 0xF8) === 0xF0) {
+                const utf8 = this.getUint32(i);
+                assert.eq((utf8 & 0xC0C0C0F8) | 0, 0x808080F0 | 0);
+                i += 4;
+                code = ((utf8 & 0x7) << 18) | ((utf8 & 0x3F00) << 4) | ((utf8 & 0x3F0000) >> 10) | ((utf8 & 0x3F000000) >> 24);
+            } else
+                throw new Error(`Unexpectedly large UTF-8 initial byte 0x${peek.toString(16)}`);
+            str += String.fromCodePoint(code);
+        }
+        if (i !== last)
+            throw new Error(`String decoding read up to ${i}, expected ${last}, UTF-8 decoding was too greedy`);
         return str;
     }
 };
index f7504cf..31eb926 100644 (file)
@@ -1,9 +1,10 @@
 # `wasmjs`: JavaScript tooling for WebAssembly
 
-`wasmjs` is a self-contained collection of JavaScript tools which can manipulate
-WebAssembly. At its core is `wasm.json`, a JSON decription of the WebAssembly
-format and other interesting facts about WebAssembly as used by the Webkit
-project (such as the names of associated JavaScriptCore opcodes).
+`wasmjs` is a self-contained collection of JavaScript tools which can create and
+manipulate WebAssembly representations and binaries. At its core is `wasm.json`,
+a JSON decription of the WebAssembly format and other interesting facts about
+WebAssembly as used by the Webkit project (such as the names of associated
+JavaScriptCore B3 opcodes).
 
 `wasmjs` requires modern JavaScript features such as ES6 modules, which is
 acceptable because WebAssembly is itself contemporary to these other features.
@@ -12,7 +13,18 @@ acceptable because WebAssembly is itself contemporary to these other features.
 # `Builder` API
 
 The current core API of `wasmjs` is the `Builder` API from `Builder.js`. It is
-used to build WebAssembly modules.
+used to build WebAssembly modules, and assemble them to valid `.wasm` binaries
+(held in an `ArrayBuffer`). The `Builder` is a small DSL which looks similar to
+the WebAssembly [binary format][]. Each section is declared through a property
+on the `Builder` object, and each declaration returns a proxy object which has
+properties specific to that section. Proxies are "popped" back by invoking their
+`.End()` property.
+
+The `Code` section has properties for each [WebAssembly opcode][]. Opcode which
+create a scope can be nested using a lambda.
+
+  [binary format]: https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#high-level-structure
+  [WebAssembly opcode]: https://github.com/WebAssembly/design/blob/master/Semantics.md
 
 A simple example:
 
@@ -21,26 +33,60 @@ import Builder from 'Builder.js';
 
 const builder = new Builder();
 
-// Construct the equivalent of: (module (func (nop) (nop)))
+// Construct the equivalent of: (module (func "answer" (i32.const 42) (return)))
 builder
+    .setChecked(false) // FIXME remove once checking is better implemented.
+    // Declare a Type section, which the builder will auto-fill as functions are defined.
+    .Type().End()
+    // Declare a Function section, which the builder will auto-fill as functions are defined.
+    .Function().End()
+    .Export()
+        // Export the "answer" function (defined below) to JavaScript.
+        .Function("answer")
+    .End()
     .Code()
-        .Function()
-            .Nop()
-            .Nop()
-        .End()
-    .End();
+        // The "answer" function takes an i32 parameters, and returns an i32.
+        .Function("answer", { params: ["i32"], ret: "i32" })
+            // Create a block returning an i32, whose body is the enclosed lambda.
+            .Block("i32", b => b
+                .GetLocal(0) // Parameters are in the same index space as locals.
+                .I32Const(0) // Generate an i32 constant.
+                .I32Eq()     // A comparison, using the two values currently on the stack.
+                .If("i32")   // Consume the comparison result, returning an i32.
+                    .I32Const(42)
+                .Else()
+                    .I32Const(1)
+                .End()
+            )
+            .Return() // Return the top of the stack: the value returned by the block.
+        .End() // End the current function.
+    .End(); // End the Code section.
 
 // Create an ArrayBuffer which is a valid WebAssembly `.wasm` file.
-const binary = builder.WebAssembly();
-```
+const bin = builder.WebAssembly().get();
+
+// Use the standard WebAssembly JavaScript API to compile the module, and instantiate it.
+const module = new WebAssembly.Module(bin);
+const instance = new WebAssembly.Instance(module);
 
-Code such as the above can then be used with JavaScript's `WebAssembly` global
-object.
+// Invoke the compiled WebAssembly function.
+const result0 = instance.exports.answer(0);
+if (result0 !== 42)
+    throw new Error(`Expected 42, got ${result0}.`);
+
+const result1 = instance.exports.answer(1);
+if (result1 !== 1)
+    throw new Error(`Expected 1, got ${result1}.`);
+```
 
 
 # Testing
 
-Tests can be executed using:
+* `self-test` tests `wasmjs` itself.
+* `js-api` tests the [WebAssembly JavaScript API](https://github.com/WebAssembly/design/blob/master/JS.md).
+* `function-tests` tests the WebAssembly compiler's implementation.
+
+All tests can be executed using:
 
 ```bash
 JSSHELL=/path/to/my/js-shell test.sh
@@ -51,7 +97,3 @@ They can also be executed by using WebKit's `run-javascriptcore-tests` tool:
 ```bash
 ./Tools/Scripts/run-javascriptcore-tests --release --filter wasm -arch x86_64
 ```
-
-The `self-test` folder contains tests for `wasmjs` itself. Future additions will
-also test the JavaScript engine's WebAssembly implementation (both JavaScript
-API and usage of that API to compile and execute WebAssembly modules).
index 3f4e5ee..89f2056 100644 (file)
@@ -8,3 +8,24 @@ import Builder from '../Builder.js';
     const instance = new WebAssembly.Instance(module);
     assert.instanceof(instance, WebAssembly.Instance);
 })();
+
+(function ExportedAnswerI32() {
+    const builder = (new Builder())
+        .Type().End()
+        .Function().End()
+        .Export()
+            .Function("answer")
+        .End()
+        .Code()
+            .Function("answer", { params: [], ret: "i32" })
+                .I32Const(42)
+                .Return()
+            .End()
+        .End();
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const instance = new WebAssembly.Instance(module);
+    const result = instance.exports.answer();
+    assert.isA(result, "number");
+    assert.eq(result, 42);
+})();
index 80ece29..73c5a83 100644 (file)
@@ -68,10 +68,11 @@ for (const c in constructorProperties) {
             assert.throws(() => new WebAssembly[c](new WebAssembly.Module(emptyModuleArray), invalid), TypeError, `second argument to WebAssembly.Instance must be undefined or an Object (evaluating 'new WebAssembly[c](new WebAssembly.Module(emptyModuleArray), invalid)')`);
         assert.isNotUndef(instance.exports);
         checkOwnPropertyDescriptor(instance, "exports", { typeofvalue: "object", writable: true, configurable: true, enumerable: true });
-        assert.isUndef(instance.exports.__proto__);
-        assert.eq(Reflect.isExtensible(instance.exports), false);
-        assert.eq(Symbol.iterator in instance.exports, true);
-        assert.eq(Symbol.toStringTag in instance.exports, true);
+        // FIXME these should pass, requires a module namespace object. https://bugs.webkit.org/show_bug.cgi?id=165121
+        // assert.isUndef(instance.exports.__proto__);
+        // assert.eq(Reflect.isExtensible(instance.exports), false);
+        // assert.eq(Symbol.iterator in instance.exports, true);
+        // assert.eq(Symbol.toStringTag in instance.exports, true);
         break;
     case "Memory":
         // FIXME Implement and test these APIs further. For now they just throw. https://bugs.webkit.org/show_bug.cgi?id=159775
index ac57d37..8d8112d 100644 (file)
@@ -27,6 +27,6 @@ import Builder from '../Builder.js';
         .End()
         .WebAssembly();
     assert.eq(bin.hexdump().trim(),
-              ["00000000 00 61 73 6d 0c 00 00 00 00 0f 0a 4f 00 48 00 48  |·asm·······O·H·H|",
-               "00000010 00 41 00 49 00 de ad c0 fe                       |·A·I·····       |"].join("\n"));
+              ["00000000 00 61 73 6d 0c 00 00 00 00 0a 05 4f 48 48 41 49  |·asm·······OHHAI|",
+               "00000010 de ad c0 fe                                      |····            |"].join("\n"));
 })();
index 153be50..2f63505 100644 (file)
@@ -7,9 +7,8 @@ const values = [
     "Il dit non avec la tête, mais il dit oui avec le cœur",
     "焼きたて!! ジャぱん",
     "(╯°□°)╯︵ ┻━┻",
-    "�",
-    // Should we use code points instead of UTF-16?
-    //        The following doesn't work: "👨‍❤️‍💋‍👨",
+    "$¢€𐍈�",
+    "👨‍❤️‍💋‍👨",
 ];
 
 for (const i of values) {
index a415dd9..ef3499a 100644 (file)
@@ -1,7 +1,7 @@
 import LowLevelBinary from '../LowLevelBinary.js';
 
 let values = [];
-for (let i = 0; i !== 0xFFFF; ++i) values.push(i);
+for (let i = 0; i <= 0xFFFF; ++i) values.push(i);
 
 for (const i of values) {
     let b = new LowLevelBinary();
diff --git a/JSTests/wasm/self-test/test_LowLevelBinary_uint24.js b/JSTests/wasm/self-test/test_LowLevelBinary_uint24.js
new file mode 100644 (file)
index 0000000..4dfd827
--- /dev/null
@@ -0,0 +1,13 @@
+import LowLevelBinary from '../LowLevelBinary.js';
+
+let values = [];
+for (let i = 0; i <= 0xFFFF; ++i) values.push(i);
+for (let i = 0xFFFFFF - 0xFFFF; i <= 0xFFFFFF; ++i) values.push(i);
+
+for (const i of values) {
+    let b = new LowLevelBinary();
+    b.uint24(i);
+    const v = b.getUint24(0);
+    if (v !== i)
+        throw new Error(`Wrote "${i}" and read back "${v}"`);
+}
index 1d0511c..9a544a3 100644 (file)
@@ -1,7 +1,7 @@
 import LowLevelBinary from '../LowLevelBinary.js';
 
 let values = [];
-for (let i = 0; i !== 0xFF; ++i) values.push(i);
+for (let i = 0; i <= 0xFF; ++i) values.push(i);
 
 for (const i of values) {
     let b = new LowLevelBinary();
diff --git a/JSTests/wasm/self-test/test_LowLevelBinary_varuint1.js b/JSTests/wasm/self-test/test_LowLevelBinary_varuint1.js
new file mode 100644 (file)
index 0000000..573bd2e
--- /dev/null
@@ -0,0 +1,14 @@
+import LowLevelBinary, * as LLB from '../LowLevelBinary.js';
+
+let values = [];
+for (let i = 0; i <= 1; ++i) values.push(i);
+
+for (const i of values) {
+    let b = new LowLevelBinary();
+    b.varuint1(i);
+    const v = b.getVaruint1(0);
+    if (v.value !== i)
+        throw new Error(`Wrote "${i}" and read back "${v}"`);
+    if (v.next !== b.getSize())
+        throw new Error(`Size ${v.next}, expected ${b.getSize()}`);
+}
index 6208797..25f8547 100755 (executable)
@@ -8,6 +8,4 @@ JSSHELL="${JSSHELL:-jsc}"
 find . -name "test_*.js" -type f | sort | \
     xargs -n1 -t -I{} "$JSSHELL" -m {}
 
-"$JSSHELL" -m generate-wasmops-header.js > /dev/null
-
 echo "All tests passed"
index 6daa7be..19f9b4e 100644 (file)
@@ -66,5 +66,25 @@ const _json = filename => {
     }
 }
 
+const _dump = (what, name, pad = '    ') => {
+    const value = v => {
+        try { return `"${v}"`; }
+        catch (e) { return `Error: "${e.message}"`; }
+    };
+    let s = `${pad}${name} ${typeof what}: ${value(what)}`;
+    for (let p in what) {
+        s += `\n${pad}${pad}${p}: ${value(what[p])} ${typeof v}`;
+        s += '\n' + _dump(what[p], p, pad + pad);
+    }
+    return s;
+};
+
 // Use underscore names to avoid clashing with builtin names.
-export { _eval as eval, _read as read, _load as load, _json as json, _global as global };
+export {
+    _dump as dump,
+    _eval as eval,
+    _read as read,
+    _load as load,
+    _json as json,
+    _global as global
+};
index 0bc2e73..60eba1b 100644 (file)
@@ -644,6 +644,7 @@ set(JavaScriptCore_SOURCES
     profiler/ProfilerProfiledBytecodes.cpp
     profiler/ProfilerUID.cpp
 
+    runtime/AbstractModuleRecord.cpp
     runtime/ArgList.cpp
     runtime/ArrayBuffer.cpp
     runtime/ArrayBufferNeuteringWatchpoint.cpp
@@ -912,12 +913,15 @@ set(JavaScriptCore_SOURCES
     wasm/js/JSWebAssemblyTable.cpp
     wasm/js/WebAssemblyCompileErrorConstructor.cpp
     wasm/js/WebAssemblyCompileErrorPrototype.cpp
+    wasm/js/WebAssemblyFunction.cpp
+    wasm/js/WebAssemblyFunctionCell.cpp
     wasm/js/WebAssemblyInstanceConstructor.cpp
     wasm/js/WebAssemblyInstancePrototype.cpp
     wasm/js/WebAssemblyMemoryConstructor.cpp
     wasm/js/WebAssemblyMemoryPrototype.cpp
     wasm/js/WebAssemblyModuleConstructor.cpp
     wasm/js/WebAssemblyModulePrototype.cpp
+    wasm/js/WebAssemblyModuleRecord.cpp
     wasm/js/WebAssemblyPrototype.cpp
     wasm/js/WebAssemblyRuntimeErrorConstructor.cpp
     wasm/js/WebAssemblyRuntimeErrorPrototype.cpp
index 2517975..0967f80 100644 (file)
@@ -1,3 +1,164 @@
+2016-11-29  JF Bastien  <jfbastien@apple.com>
+
+        WebAssembly JS API: improve Instance
+        https://bugs.webkit.org/show_bug.cgi?id=164757
+
+        Reviewed by Keith Miller.
+
+        An Instance's `exports` property wasn't populated with exports.
+
+        According to the spec [0], `exports` should present itself as a WebAssembly
+        Module Record. In order to do this we need to split JSModuleRecord into
+        AbstractModuleRecord (without the `link` and `evaluate` functions), and
+        JSModuleRecord (which implements link and evaluate). We can then have a separate
+        WebAssemblyModuleRecord which shares most of the implementation.
+
+        `exports` then maps function names to WebAssemblyFunction and
+        WebAssemblyFunctionCell, which call into the B3-generated WebAssembly code.
+
+        A follow-up patch will do imports.
+
+        A few things of note:
+
+         - Use Identifier instead of String. They get uniqued, we need them for the JSModuleNamespaceObject. This is safe because JSWebAssemblyModule creation is on the main thread.
+         - JSWebAssemblyInstance needs to refer to the JSWebAssemblyModule used to create it, because the module owns the code, identifiers, etc. The world would be very sad if it got GC'd.
+         - Instance.exports shouldn't use putWithoutTransition because it affects all Structures, whereas here each instance needs its own exports.
+         - Expose the compiled functions, and pipe them to the InstanceConstructor. Start moving things around to split JSModuleRecord out into JS and WebAssembly parts.
+
+          [0]: https://github.com/WebAssembly/design/blob/master/JS.md#webassemblyinstance-constructor
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * runtime/AbstractModuleRecord.cpp: Copied from Source/JavaScriptCore/runtime/JSModuleRecord.cpp, which I split in two
+        (JSC::AbstractModuleRecord::AbstractModuleRecord):
+        (JSC::AbstractModuleRecord::destroy):
+        (JSC::AbstractModuleRecord::finishCreation):
+        (JSC::AbstractModuleRecord::visitChildren):
+        (JSC::AbstractModuleRecord::appendRequestedModule):
+        (JSC::AbstractModuleRecord::addStarExportEntry):
+        (JSC::AbstractModuleRecord::addImportEntry):
+        (JSC::AbstractModuleRecord::addExportEntry):
+        (JSC::identifierToJSValue):
+        (JSC::AbstractModuleRecord::hostResolveImportedModule):
+        (JSC::AbstractModuleRecord::ResolveQuery::ResolveQuery):
+        (JSC::AbstractModuleRecord::ResolveQuery::isEmptyValue):
+        (JSC::AbstractModuleRecord::ResolveQuery::isDeletedValue):
+        (JSC::AbstractModuleRecord::ResolveQuery::Hash::hash):
+        (JSC::AbstractModuleRecord::ResolveQuery::Hash::equal):
+        (JSC::AbstractModuleRecord::cacheResolution):
+        (JSC::getExportedNames):
+        (JSC::AbstractModuleRecord::getModuleNamespace):
+        (JSC::printableName):
+        (JSC::AbstractModuleRecord::dump):
+        * runtime/AbstractModuleRecord.h: Copied from Source/JavaScriptCore/runtime/JSModuleRecord.h.
+        (JSC::AbstractModuleRecord::ImportEntry::isNamespace):
+        (JSC::AbstractModuleRecord::sourceCode):
+        (JSC::AbstractModuleRecord::moduleKey):
+        (JSC::AbstractModuleRecord::requestedModules):
+        (JSC::AbstractModuleRecord::exportEntries):
+        (JSC::AbstractModuleRecord::importEntries):
+        (JSC::AbstractModuleRecord::starExportEntries):
+        (JSC::AbstractModuleRecord::declaredVariables):
+        (JSC::AbstractModuleRecord::lexicalVariables):
+        (JSC::AbstractModuleRecord::moduleEnvironment):
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        (JSC::JSGlobalObject::visitChildren):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::webAssemblyModuleRecordStructure):
+        (JSC::JSGlobalObject::webAssemblyFunctionStructure):
+        * runtime/JSModuleEnvironment.cpp:
+        (JSC::JSModuleEnvironment::create):
+        (JSC::JSModuleEnvironment::finishCreation):
+        (JSC::JSModuleEnvironment::getOwnPropertySlot):
+        (JSC::JSModuleEnvironment::getOwnNonIndexPropertyNames):
+        (JSC::JSModuleEnvironment::put):
+        (JSC::JSModuleEnvironment::deleteProperty):
+        * runtime/JSModuleEnvironment.h:
+        (JSC::JSModuleEnvironment::create):
+        (JSC::JSModuleEnvironment::offsetOfModuleRecord):
+        (JSC::JSModuleEnvironment::allocationSize):
+        (JSC::JSModuleEnvironment::moduleRecord):
+        (JSC::JSModuleEnvironment::moduleRecordSlot):
+        * runtime/JSModuleNamespaceObject.cpp:
+        (JSC::JSModuleNamespaceObject::finishCreation):
+        (JSC::JSModuleNamespaceObject::getOwnPropertySlot):
+        * runtime/JSModuleNamespaceObject.h:
+        (JSC::JSModuleNamespaceObject::create):
+        (JSC::JSModuleNamespaceObject::moduleRecord):
+        * runtime/JSModuleRecord.cpp:
+        (JSC::JSModuleRecord::createStructure):
+        (JSC::JSModuleRecord::create):
+        (JSC::JSModuleRecord::JSModuleRecord):
+        (JSC::JSModuleRecord::destroy):
+        (JSC::JSModuleRecord::finishCreation):
+        (JSC::JSModuleRecord::visitChildren):
+        (JSC::JSModuleRecord::instantiateDeclarations):
+        * runtime/JSModuleRecord.h:
+        * runtime/JSScope.cpp:
+        (JSC::abstractAccess):
+        (JSC::JSScope::collectClosureVariablesUnderTDZ):
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        * runtime/VM.h:
+        * wasm/JSWebAssembly.h:
+        * wasm/WasmFormat.h: use Identifier instead of String
+        * wasm/WasmModuleParser.cpp:
+        (JSC::Wasm::ModuleParser::parse):
+        (JSC::Wasm::ModuleParser::parseType):
+        (JSC::Wasm::ModuleParser::parseImport): fix off-by-one
+        (JSC::Wasm::ModuleParser::parseFunction):
+        (JSC::Wasm::ModuleParser::parseExport):
+        * wasm/WasmModuleParser.h:
+        (JSC::Wasm::ModuleParser::ModuleParser):
+        * wasm/WasmPlan.cpp:
+        (JSC::Wasm::Plan::run):
+        * wasm/js/JSWebAssemblyInstance.cpp:
+        (JSC::JSWebAssemblyInstance::create):
+        (JSC::JSWebAssemblyInstance::finishCreation):
+        (JSC::JSWebAssemblyInstance::visitChildren):
+        * wasm/js/JSWebAssemblyInstance.h:
+        (JSC::JSWebAssemblyInstance::module):
+        * wasm/js/JSWebAssemblyModule.cpp:
+        (JSC::JSWebAssemblyModule::create):
+        (JSC::JSWebAssemblyModule::finishCreation):
+        (JSC::JSWebAssemblyModule::visitChildren):
+        * wasm/js/JSWebAssemblyModule.h:
+        (JSC::JSWebAssemblyModule::moduleInformation):
+        (JSC::JSWebAssemblyModule::compiledFunctions):
+        (JSC::JSWebAssemblyModule::exportSymbolTable):
+        * wasm/js/WebAssemblyFunction.cpp: Added.
+        (JSC::callWebAssemblyFunction):
+        (JSC::WebAssemblyFunction::create):
+        (JSC::WebAssemblyFunction::createStructure):
+        (JSC::WebAssemblyFunction::WebAssemblyFunction):
+        (JSC::WebAssemblyFunction::visitChildren):
+        (JSC::WebAssemblyFunction::finishCreation):
+        * wasm/js/WebAssemblyFunction.h: Copied from Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h.
+        (JSC::CallableWebAssemblyFunction::CallableWebAssemblyFunction):
+        (JSC::WebAssemblyFunction::webAssemblyFunctionCell):
+        * wasm/js/WebAssemblyFunctionCell.cpp: Copied from Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h.
+        (JSC::WebAssemblyFunctionCell::create):
+        (JSC::WebAssemblyFunctionCell::WebAssemblyFunctionCell):
+        (JSC::WebAssemblyFunctionCell::destroy):
+        (JSC::WebAssemblyFunctionCell::createStructure):
+        * wasm/js/WebAssemblyFunctionCell.h: Copied from Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h.
+        (JSC::WebAssemblyFunctionCell::function):
+        * wasm/js/WebAssemblyInstanceConstructor.cpp:
+        (JSC::constructJSWebAssemblyInstance):
+        * wasm/js/WebAssemblyModuleConstructor.cpp:
+        (JSC::constructJSWebAssemblyModule):
+        * wasm/js/WebAssemblyModuleRecord.cpp: Added.
+        (JSC::WebAssemblyModuleRecord::createStructure):
+        (JSC::WebAssemblyModuleRecord::create):
+        (JSC::WebAssemblyModuleRecord::WebAssemblyModuleRecord):
+        (JSC::WebAssemblyModuleRecord::destroy):
+        (JSC::WebAssemblyModuleRecord::finishCreation):
+        (JSC::WebAssemblyModuleRecord::visitChildren):
+        (JSC::WebAssemblyModuleRecord::link):
+        (JSC::WebAssemblyModuleRecord::evaluate):
+        * wasm/js/WebAssemblyModuleRecord.h: Copied from Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h.
+
 2016-11-29  Saam Barati  <sbarati@apple.com>
 
         We should be able optimize the pattern where we spread a function's rest parameter to another call
index 2d2e0f8..9f0952c 100644 (file)
                AD2FCC301DB83D4900B3E736 /* JSWebAssembly.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD2FCC2E1DB839F700B3E736 /* JSWebAssembly.cpp */; };
                AD2FCC311DB83D4900B3E736 /* JSWebAssembly.h in Headers */ = {isa = PBXBuildFile; fileRef = AD2FCC2F1DB839F700B3E736 /* JSWebAssembly.h */; };
                AD2FCC331DC4045400B3E736 /* WasmFormat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD2FCC321DC4045300B3E736 /* WasmFormat.cpp */; };
+               AD4937C31DDBE6140077C807 /* AbstractModuleRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD4937C11DDBE60A0077C807 /* AbstractModuleRecord.cpp */; };
+               AD4937C41DDBE6140077C807 /* AbstractModuleRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = AD4937C21DDBE60A0077C807 /* AbstractModuleRecord.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               AD4937C71DDD0AAE0077C807 /* WebAssemblyModuleRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD4937C51DDCDCF00077C807 /* WebAssemblyModuleRecord.cpp */; };
+               AD4937C81DDD0AAE0077C807 /* WebAssemblyModuleRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = AD4937C61DDCDCF00077C807 /* WebAssemblyModuleRecord.h */; };
+               AD4937D11DDD27DE0077C807 /* WebAssemblyFunctionCell.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD4937CD1DDD27D90077C807 /* WebAssemblyFunctionCell.cpp */; };
+               AD4937D21DDD27DE0077C807 /* WebAssemblyFunctionCell.h in Headers */ = {isa = PBXBuildFile; fileRef = AD4937CE1DDD27D90077C807 /* WebAssemblyFunctionCell.h */; };
+               AD4937D31DDD27DE0077C807 /* WebAssemblyFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD4937C91DDD27340077C807 /* WebAssemblyFunction.cpp */; };
+               AD4937D41DDD27DE0077C807 /* WebAssemblyFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = AD4937CA1DDD27340077C807 /* WebAssemblyFunction.h */; };
                AD86A93E1AA4D88D002FE77F /* WeakGCMapInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = AD86A93D1AA4D87C002FE77F /* WeakGCMapInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                ADDB1F6318D77DBE009B58A8 /* OpaqueRootSet.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDB1F6218D77DB7009B58A8 /* OpaqueRootSet.h */; settings = {ATTRIBUTES = (Private, ); }; };
                ADE39FFF16DD144B0003CD4A /* PropertyTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD1CF06816DCAB2D00B97123 /* PropertyTable.cpp */; };
                AD2FCC2E1DB839F700B3E736 /* JSWebAssembly.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSWebAssembly.cpp; sourceTree = "<group>"; };
                AD2FCC2F1DB839F700B3E736 /* JSWebAssembly.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSWebAssembly.h; sourceTree = "<group>"; };
                AD2FCC321DC4045300B3E736 /* WasmFormat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmFormat.cpp; sourceTree = "<group>"; };
+               AD4937C11DDBE60A0077C807 /* AbstractModuleRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AbstractModuleRecord.cpp; sourceTree = "<group>"; };
+               AD4937C21DDBE60A0077C807 /* AbstractModuleRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AbstractModuleRecord.h; sourceTree = "<group>"; };
+               AD4937C51DDCDCF00077C807 /* WebAssemblyModuleRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebAssemblyModuleRecord.cpp; path = js/WebAssemblyModuleRecord.cpp; sourceTree = "<group>"; };
+               AD4937C61DDCDCF00077C807 /* WebAssemblyModuleRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebAssemblyModuleRecord.h; path = js/WebAssemblyModuleRecord.h; sourceTree = "<group>"; };
+               AD4937C91DDD27340077C807 /* WebAssemblyFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebAssemblyFunction.cpp; path = js/WebAssemblyFunction.cpp; sourceTree = "<group>"; };
+               AD4937CA1DDD27340077C807 /* WebAssemblyFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebAssemblyFunction.h; path = js/WebAssemblyFunction.h; sourceTree = "<group>"; };
+               AD4937CD1DDD27D90077C807 /* WebAssemblyFunctionCell.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebAssemblyFunctionCell.cpp; path = js/WebAssemblyFunctionCell.cpp; sourceTree = "<group>"; };
+               AD4937CE1DDD27D90077C807 /* WebAssemblyFunctionCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebAssemblyFunctionCell.h; path = js/WebAssemblyFunctionCell.h; sourceTree = "<group>"; };
                AD86A93D1AA4D87C002FE77F /* WeakGCMapInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WeakGCMapInlines.h; sourceTree = "<group>"; };
                ADDB1F6218D77DB7009B58A8 /* OpaqueRootSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpaqueRootSet.h; sourceTree = "<group>"; };
                B59F89371891AD3300D5CCDC /* UnlinkedInstructionStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnlinkedInstructionStream.h; sourceTree = "<group>"; };
                7EF6E0BB0EB7A1EC0079AFAF /* runtime */ = {
                        isa = PBXGroup;
                        children = (
+                               AD4937C11DDBE60A0077C807 /* AbstractModuleRecord.cpp */,
+                               AD4937C21DDBE60A0077C807 /* AbstractModuleRecord.h */,
                                BCF605110E203EF800B9A64D /* ArgList.cpp */,
                                BCF605120E203EF800B9A64D /* ArgList.h */,
                                0FE0500C1AA9091100D33B33 /* ArgumentsMode.h */,
                AD2FCB8A1DB5840000B3E736 /* js */ = {
                        isa = PBXGroup;
                        children = (
+                               AD4937CD1DDD27D90077C807 /* WebAssemblyFunctionCell.cpp */,
+                               AD4937CE1DDD27D90077C807 /* WebAssemblyFunctionCell.h */,
+                               AD4937C91DDD27340077C807 /* WebAssemblyFunction.cpp */,
+                               AD4937CA1DDD27340077C807 /* WebAssemblyFunction.h */,
+                               AD4937C51DDCDCF00077C807 /* WebAssemblyModuleRecord.cpp */,
+                               AD4937C61DDCDCF00077C807 /* WebAssemblyModuleRecord.h */,
                                AD2FCC261DB838C400B3E736 /* WebAssemblyPrototype.cpp */,
                                AD2FCC271DB838C400B3E736 /* WebAssemblyPrototype.h */,
                                AD2FCBA61DB58DA400B3E736 /* JSWebAssemblyCompileError.cpp */,
                                0F7B294D14C3CD4C007C3DB1 /* DFGCommon.h in Headers */,
                                53529A4C1C457B75000B49C6 /* APIUtils.h in Headers */,
                                0FEA0A32170D40BF00BB722C /* DFGCommonData.h in Headers */,
+                               AD4937D21DDD27DE0077C807 /* WebAssemblyFunctionCell.h in Headers */,
                                0F725CB01C506D3B00AD943A /* B3FoldPathConstants.h in Headers */,
                                0F38B01817CFE75500B144D3 /* DFGCompilationKey.h in Headers */,
                                0F9D4C111C3E2C74006CD984 /* FTLPatchpointExceptionHandle.h in Headers */,
                                A7D9A29617A0BC7400EE2618 /* DFGEdgeDominates.h in Headers */,
                                A7986D5717A0BB1E00A95DD0 /* DFGEdgeUsesStructure.h in Headers */,
                                0F8F14341ADF090100ED792C /* DFGEpoch.h in Headers */,
+                               AD4937C81DDD0AAE0077C807 /* WebAssemblyModuleRecord.h in Headers */,
                                0FBC0AE81496C7C700D4FBDD /* DFGExitProfile.h in Headers */,
                                A78A9775179738B8009DF744 /* DFGFailedFinalizer.h in Headers */,
                                A7BFF3C0179868940002F462 /* DFGFiltrationResult.h in Headers */,
                                0F34B14A16D42013001CDA5A /* DFGUseKind.h in Headers */,
                                DCFDFBDA1D1F5D9E00FE3D72 /* B3TypeMap.h in Headers */,
                                0F3B3A2C15475002003ED0FF /* DFGValidate.h in Headers */,
+                               AD4937C41DDBE6140077C807 /* AbstractModuleRecord.h in Headers */,
                                0F2BDC481522802900CD8910 /* DFGValueSource.h in Headers */,
                                0F0123331944EA1B00843A0C /* DFGValueStrength.h in Headers */,
                                0FE254F71ABDDD2200A7C6D2 /* DFGVarargsForwardingPhase.h in Headers */,
                                AD2FCC211DB59CB200B3E736 /* WebAssemblyTablePrototype.lut.h in Headers */,
                                0FC712E317CD8793008CC93C /* JITToDFGDeferredCompilationCallback.h in Headers */,
                                AD2FCBE31DB58DAD00B3E736 /* JSWebAssemblyCompileError.h in Headers */,
+                               AD4937D41DDD27DE0077C807 /* WebAssemblyFunction.h in Headers */,
                                840480131021A1D9008E7F01 /* JSAPIValueWrapper.h in Headers */,
                                C2CF39C216E15A8100DD69BE /* JSAPIWrapperObject.h in Headers */,
                                BC18C4170E16F5CD00B34460 /* JSArray.h in Headers */,
                                0F338E121BF0276C0013C88F /* B3OpaqueByproducts.cpp in Sources */,
                                0FDF67D31D9C6D2A001B9825 /* B3Kind.cpp in Sources */,
                                0F8F2B99172F04FF007DBDA5 /* DFGDesiredIdentifiers.cpp in Sources */,
+                               AD4937C31DDBE6140077C807 /* AbstractModuleRecord.cpp in Sources */,
                                C2C0F7CD17BBFC5B00464FE4 /* DFGDesiredTransitions.cpp in Sources */,
                                0FE8534B1723CDA500B618F5 /* DFGDesiredWatchpoints.cpp in Sources */,
                                C2981FD817BAEE4B00A3BC98 /* DFGDesiredWeakReferences.cpp in Sources */,
                                147F39CF107EC37600427A48 /* InternalFunction.cpp in Sources */,
                                1429D7D40ED2128200B89619 /* Interpreter.cpp in Sources */,
                                0FE34C191C4B39AE0003A512 /* AirLogRegisterPressure.cpp in Sources */,
+                               AD4937D31DDD27DE0077C807 /* WebAssemblyFunction.cpp in Sources */,
                                A1B9E2391B4E0D6700BC7FED /* IntlCollator.cpp in Sources */,
                                A1B9E23B1B4E0D6700BC7FED /* IntlCollatorConstructor.cpp in Sources */,
                                0F6DB7EA1D6124B800CDBF8E /* StackFrame.cpp in Sources */,
                                1482B74E0A43032800517CFC /* JSStringRef.cpp in Sources */,
                                146AAB380B66A94400E55F16 /* JSStringRefCF.cpp in Sources */,
                                0F919D0C157EE09F004A4E7D /* JSSymbolTableObject.cpp in Sources */,
+                               AD4937D11DDD27DE0077C807 /* WebAssemblyFunctionCell.cpp in Sources */,
                                70ECA6051AFDBEA200449739 /* JSTemplateRegistryKey.cpp in Sources */,
                                0F2B66FA17B6B5AB00A7AE3F /* JSTypedArrayConstructors.cpp in Sources */,
                                0F9630391D4192C6005609D9 /* AllocatorAttributes.cpp in Sources */,
                                65FB63A41C8EA09C0020719B /* YarrCanonicalizeUnicode.cpp in Sources */,
                                86704B8412DBA33700A9FE7B /* YarrInterpreter.cpp in Sources */,
                                86704B8612DBA33700A9FE7B /* YarrJIT.cpp in Sources */,
+                               AD4937C71DDD0AAE0077C807 /* WebAssemblyModuleRecord.cpp in Sources */,
                                86704B8912DBA33700A9FE7B /* YarrPattern.cpp in Sources */,
                                86704B4212DB8A8100A9FE7B /* YarrSyntaxChecker.cpp in Sources */,
                                0F2BBD991C5FF3F50023EF23 /* B3VariableValue.cpp in Sources */,
diff --git a/Source/JavaScriptCore/runtime/AbstractModuleRecord.cpp b/Source/JavaScriptCore/runtime/AbstractModuleRecord.cpp
new file mode 100644 (file)
index 0000000..a5be604
--- /dev/null
@@ -0,0 +1,771 @@
+/*
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AbstractModuleRecord.h"
+
+#include "Error.h"
+#include "Interpreter.h"
+#include "JSCInlines.h"
+#include "JSMap.h"
+#include "JSModuleEnvironment.h"
+#include "JSModuleNamespaceObject.h"
+#include "UnlinkedModuleProgramCodeBlock.h"
+
+namespace JSC {
+
+const ClassInfo AbstractModuleRecord::s_info = { "AbstractModuleRecord", &Base::s_info, 0, CREATE_METHOD_TABLE(AbstractModuleRecord) };
+
+AbstractModuleRecord::AbstractModuleRecord(VM& vm, Structure* structure, const Identifier& moduleKey, const SourceCode& sourceCode, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables)
+    : Base(vm, structure)
+    , m_moduleKey(moduleKey)
+    , m_sourceCode(sourceCode)
+    , m_declaredVariables(declaredVariables)
+    , m_lexicalVariables(lexicalVariables)
+{
+}
+
+void AbstractModuleRecord::destroy(JSCell* cell)
+{
+    AbstractModuleRecord* thisObject = jsCast<AbstractModuleRecord*>(cell);
+    thisObject->AbstractModuleRecord::~AbstractModuleRecord();
+}
+
+void AbstractModuleRecord::finishCreation(ExecState* exec, VM& vm)
+{
+    Base::finishCreation(vm);
+    ASSERT(inherits(info()));
+    putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("registryEntry")), jsUndefined());
+    putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("evaluated")), jsBoolean(false));
+
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    JSMap* map = JSMap::create(exec, vm, globalObject()->mapStructure());
+    RELEASE_ASSERT(!scope.exception());
+    m_dependenciesMap.set(vm, this, map);
+    putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("dependenciesMap")), m_dependenciesMap.get());
+}
+
+void AbstractModuleRecord::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+    AbstractModuleRecord* thisObject = jsCast<AbstractModuleRecord*>(cell);
+    Base::visitChildren(thisObject, visitor);
+    visitor.append(&thisObject->m_moduleEnvironment);
+    visitor.append(&thisObject->m_moduleNamespaceObject);
+    visitor.append(&thisObject->m_dependenciesMap);
+}
+
+void AbstractModuleRecord::appendRequestedModule(const Identifier& moduleName)
+{
+    m_requestedModules.add(moduleName.impl());
+}
+
+void AbstractModuleRecord::addStarExportEntry(const Identifier& moduleName)
+{
+    m_starExportEntries.add(moduleName.impl());
+}
+
+void AbstractModuleRecord::addImportEntry(const ImportEntry& entry)
+{
+    bool isNewEntry = m_importEntries.add(entry.localName.impl(), entry).isNewEntry;
+    ASSERT_UNUSED(isNewEntry, isNewEntry); // This is guaranteed by the parser.
+}
+
+void AbstractModuleRecord::addExportEntry(const ExportEntry& entry)
+{
+    bool isNewEntry = m_exportEntries.add(entry.exportName.impl(), entry).isNewEntry;
+    ASSERT_UNUSED(isNewEntry, isNewEntry); // This is guaranteed by the parser.
+}
+
+auto AbstractModuleRecord::tryGetImportEntry(UniquedStringImpl* localName) -> std::optional<ImportEntry>
+{
+    const auto iterator = m_importEntries.find(localName);
+    if (iterator == m_importEntries.end())
+        return std::nullopt;
+    return std::optional<ImportEntry>(iterator->value);
+}
+
+auto AbstractModuleRecord::tryGetExportEntry(UniquedStringImpl* exportName) -> std::optional<ExportEntry>
+{
+    const auto iterator = m_exportEntries.find(exportName);
+    if (iterator == m_exportEntries.end())
+        return std::nullopt;
+    return std::optional<ExportEntry>(iterator->value);
+}
+
+auto AbstractModuleRecord::ExportEntry::createLocal(const Identifier& exportName, const Identifier& localName) -> ExportEntry
+{
+    return ExportEntry { Type::Local, exportName, Identifier(), Identifier(), localName };
+}
+
+auto AbstractModuleRecord::ExportEntry::createIndirect(const Identifier& exportName, const Identifier& importName, const Identifier& moduleName) -> ExportEntry
+{
+    return ExportEntry { Type::Indirect, exportName, moduleName, importName, Identifier() };
+}
+
+auto AbstractModuleRecord::Resolution::notFound() -> Resolution
+{
+    return Resolution { Type::NotFound, nullptr, Identifier() };
+}
+
+auto AbstractModuleRecord::Resolution::error() -> Resolution
+{
+    return Resolution { Type::Error, nullptr, Identifier() };
+}
+
+auto AbstractModuleRecord::Resolution::ambiguous() -> Resolution
+{
+    return Resolution { Type::Ambiguous, nullptr, Identifier() };
+}
+
+static JSValue identifierToJSValue(ExecState* exec, const Identifier& identifier)
+{
+    if (identifier.isSymbol())
+        return Symbol::create(exec->vm(), static_cast<SymbolImpl&>(*identifier.impl()));
+    return jsString(&exec->vm(), identifier.impl());
+}
+
+AbstractModuleRecord* AbstractModuleRecord::hostResolveImportedModule(ExecState* exec, const Identifier& moduleName)
+{
+    JSValue moduleNameValue = identifierToJSValue(exec, moduleName);
+    JSValue pair = m_dependenciesMap->JSMap::get(exec, moduleNameValue);
+    return jsCast<AbstractModuleRecord*>(pair.get(exec, Identifier::fromString(exec, "value")));
+}
+
+auto AbstractModuleRecord::resolveImport(ExecState* exec, const Identifier& localName) -> Resolution
+{
+    std::optional<ImportEntry> optionalImportEntry = tryGetImportEntry(localName.impl());
+    if (!optionalImportEntry)
+        return Resolution::notFound();
+
+    const ImportEntry& importEntry = *optionalImportEntry;
+    if (importEntry.isNamespace(exec->vm()))
+        return Resolution::notFound();
+
+    AbstractModuleRecord* importedModule = hostResolveImportedModule(exec, importEntry.moduleRequest);
+    return importedModule->resolveExport(exec, importEntry.importName);
+}
+
+struct AbstractModuleRecord::ResolveQuery {
+    struct Hash {
+        static unsigned hash(const ResolveQuery&);
+        static bool equal(const ResolveQuery&, const ResolveQuery&);
+        static const bool safeToCompareToEmptyOrDeleted = true;
+    };
+
+    ResolveQuery(AbstractModuleRecord* moduleRecord, UniquedStringImpl* exportName)
+        : moduleRecord(moduleRecord)
+        , exportName(exportName)
+    {
+    }
+
+    ResolveQuery(AbstractModuleRecord* moduleRecord, const Identifier& exportName)
+        : ResolveQuery(moduleRecord, exportName.impl())
+    {
+    }
+
+    enum EmptyValueTag { EmptyValue };
+    ResolveQuery(EmptyValueTag)
+    {
+    }
+
+    enum DeletedValueTag { DeletedValue };
+    ResolveQuery(DeletedValueTag)
+        : moduleRecord(nullptr)
+        , exportName(WTF::HashTableDeletedValue)
+    {
+    }
+
+    bool isEmptyValue() const
+    {
+        return !exportName;
+    }
+
+    bool isDeletedValue() const
+    {
+        return exportName.isHashTableDeletedValue();
+    }
+
+    // The module record is not marked from the GC. But these records are reachable from the JSGlobalObject.
+    // So we don't care the reachability to this record.
+    AbstractModuleRecord* moduleRecord;
+    RefPtr<UniquedStringImpl> exportName;
+};
+
+inline unsigned AbstractModuleRecord::ResolveQuery::Hash::hash(const ResolveQuery& query)
+{
+    return WTF::PtrHash<AbstractModuleRecord*>::hash(query.moduleRecord) + IdentifierRepHash::hash(query.exportName);
+}
+
+inline bool AbstractModuleRecord::ResolveQuery::Hash::equal(const ResolveQuery& lhs, const ResolveQuery& rhs)
+{
+    return lhs.moduleRecord == rhs.moduleRecord && lhs.exportName == rhs.exportName;
+}
+
+auto AbstractModuleRecord::tryGetCachedResolution(UniquedStringImpl* exportName) -> std::optional<Resolution>
+{
+    const auto iterator = m_resolutionCache.find(exportName);
+    if (iterator == m_resolutionCache.end())
+        return std::nullopt;
+    return std::optional<Resolution>(iterator->value);
+}
+
+void AbstractModuleRecord::cacheResolution(UniquedStringImpl* exportName, const Resolution& resolution)
+{
+    m_resolutionCache.add(exportName, resolution);
+}
+
+auto AbstractModuleRecord::resolveExportImpl(ExecState* exec, const ResolveQuery& root) -> Resolution
+{
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-resolveexport
+
+    // How to avoid C++ recursion in this function:
+    // This function avoids C++ recursion of the naive ResolveExport implementation.
+    // Flatten the recursion to the loop with the task queue and frames.
+    //
+    // 1. pendingTasks
+    //     We enqueue the recursive resolveExport call to this queue to avoid recursive calls in C++.
+    //     The task has 3 types. (1) Query, (2) IndirectFallback and (3) GatherStars.
+    //     (1) Query
+    //         Querying the resolution to the current module.
+    //     (2) IndirectFallback
+    //         Examine the result of the indirect export resolution. Only when the indirect export resolution fails,
+    //         we look into the star exports. (step 5-a-vi).
+    //     (3) GatherStars
+    //         Examine the result of the star export resolutions.
+    //
+    // 2. frames
+    //     When the spec calls the resolveExport recursively, instead we append the frame
+    //     (that holds the result resolution) to the frames and enqueue the task to the pendingTasks.
+    //     The entry in the frames means the *local* resolution result of the specific recursive resolveExport.
+    //
+    // We should maintain the local resolution result instead of holding the global resolution result only.
+    // For example,
+    //
+    //     star
+    // (1) ---> (2) "Resolve"
+    //      |
+    //      |
+    //      +-> (3) "NotFound"
+    //      |
+    //      |       star
+    //      +-> (4) ---> (5) "Resolve" [here]
+    //               |
+    //               |
+    //               +-> (6) "Error"
+    //
+    // Consider the above graph. The numbers represents the modules. Now we are [here].
+    // If we only hold the global resolution result during the resolveExport operation, [here],
+    // we decide the entire result of resolveExport is "Ambiguous", because there are multiple
+    // "Resolve" (in module (2) and (5)). However, this should become "Error" because (6) will
+    // propagate "Error" state to the (4), (4) will become "Error" and then, (1) will become
+    // "Error". We should aggregate the results at the star exports point ((4) and (1)).
+    //
+    // Usually, both "Error" and "Ambiguous" states will throw the syntax error. So except for the content of the
+    // error message, there are no difference. (And if we fix the (6) that raises "Error", next, it will produce
+    // the "Ambiguous" error due to (5). Anyway, user need to fix the both. So which error should be raised at first
+    // doesn't matter so much.
+    //
+    // However, this may become the problem under the module namespace creation.
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-getmodulenamespace
+    // section 15.2.1.18, step 3-d-ii
+    // Here, we distinguish "Ambiguous" and "Error". When "Error" state is produced, we need to throw the propagated error.
+    // But if "Ambiguous" state comes, we just ignore the result.
+    // To follow the requirement strictly, in this implementation, we keep the local resolution result to produce the
+    // correct result under the above complex cases.
+
+    // Caching strategy:
+    // The resolveExport operation is frequently called. So caching results is important.
+    // We observe the following aspects and based on them construct the caching strategy.
+    // Here, we attempt to cache the resolution by constructing the map in module records.
+    // That means  Module -> ExportName -> Maybe<Resolution>.
+    // Technically, all the AbstractModuleRecords have the Map<ExportName, Resolution> for caching.
+    //
+    // The important observations are that,
+    //
+    //  - *cacheable* means that traversing to this node from a path will produce the same results as starting from this node.
+    //
+    //    Here, we define the resovling route. We represent [?] as the module that has the local binding.
+    //    And (?) as the module without the local binding.
+    //
+    //      @ -> (A) -> (B) -> [C]
+    //
+    //    We list the resolving route for each node.
+    //
+    //    (A): (A) -> (B) -> [C]
+    //    (B): (B) -> [C]
+    //    [C]: [C]
+    //
+    //    In this case, if we start the tracing from (B), the resolving route becomes (B) -> [C].
+    //    So this is the same. At that time, we can say (B) is cacheable in the first tracing.
+    //
+    //  - The cache ability of a node depends on the resolving route from this node.
+    //
+    // 1. The starting point is always cacheable.
+    //
+    // 2. A module that has resolved a local binding is always cacheable.
+    //
+    //  @ -> (A) -> [B]
+    //
+    //  In the above case, we can see the [B] as cacheable.
+    //  This is because when starting from [B] node, we immediately resolve with the local binding.
+    //  So the resolving route from [B] does not depend on the starting point.
+    //
+    // 3. If we don't follow any star links during the resolution, we can see all the traced nodes are cacheable.
+    //
+    //  If there are non star links, it means that there is *no branch* in the module dependency graph.
+    //  This *no branch* feature makes all the modules cachable.
+    //
+    //  I.e, if we traverse one star link (even if we successfully resolve that star link),
+    //  we must still traverse all other star links. I would also explain we don't run into
+    //  this when resolving a local/indirect link. When resolving a local/indirect link,
+    //  we won't traverse any star links.
+    //  And since the module can hold only one local/indirect link for the specific export name (if there
+    //  are multiple local/indirect links that has the same export name, it should be syntax error in the
+    //  parsing phase.), there is no multiple outgoing links from a module.
+    //
+    //  @ -> (A) --> (B) -> [C] -> (D) -> (E) -+
+    //                ^                        |
+    //                |                        |
+    //                +------------------------+
+    //
+    //  When starting from @, [C] will be found as the module resolving the given binding.
+    //  In this case, (B) can cache this resolution. Since the resolving route is the same to the one when
+    //  starting from (B). After caching the above result, we attempt to resolve the same binding from (D).
+    //
+    //                              @
+    //                              |
+    //                              v
+    //  @ -> (A) --> (B) -> [C] -> (D) -> (E) -+
+    //                ^                        |
+    //                |                        |
+    //                +------------------------+
+    //
+    //  In this case, we can use the (B)'s cached result. And (E) can be cached.
+    //
+    //    (E): The resolving route is now (E) -> (B) -> [C]. That is the same when starting from (E).
+    //
+    //  No branching makes that the problematic *once-visited* node cannot be seen.
+    //  The *once-visited* node makes the resolving route changed since when we see the *once-visited* node,
+    //  we stop tracing this.
+    //
+    //  If there is no star links and if we look *once-visited* node under no branching graph, *once-visited*
+    //  node cannot resolve the requested binding. If the *once-visited* node can resolve the binding, we
+    //  should have already finished the resolution before reaching this *once-visited* node.
+    //
+    // 4. Once we follow star links, we should not retrieve the result from the cache and should not cache.
+    //
+    //  Star links are only the way to introduce branch.
+    //  Once we follow the star links during the resolution, we cannot cache naively.
+    //  This is because the cacheability depends on the resolving route. And branching produces the problematic *once-visited*
+    //  nodes. Since we don't follow the *once-visited* node, the resolving route from the node becomes different from
+    //  the resolving route when starting from this node.
+    //
+    //  The following example explains when we should not retrieve the cache and cache the result.
+    //
+    //               +----> (D) ------+
+    //               |                |
+    //               |                v
+    //      (A) *----+----> (B) ---> [C]
+    //                       ^
+    //                       |
+    //                       @
+    //
+    //  When starting from (B), we find [C]. In this resolving route, we don't find any star link.
+    //  And by definition, (B) and [C] are cachable. (B) is the starting point. And [C] has the local binding.
+    //
+    //               +----> (D) ------+
+    //               |                |
+    //               |                v
+    //  @-> (A) *----+----> (B) ---> [C]
+    //
+    //  But when starting from (A), we should not get the value from the cache. Because,
+    //
+    //    1. When looking (D), we reach [C] and make both resolved.
+    //    2. When looking (B), if we retrieved the last cache from (B), (B) becomes resolved.
+    //    3. But actually, (B) is not-found in this trial because (C) is already *once-visited*.
+    //    4. If we accidentally make (B) resolved, (A) becomes ambiguous. But the correct answer is resolved.
+    //
+    //  Why is this problem caused? This is because the *once-visited* node makes the result not-found.
+    //  In the second trial, (B) -> [C] result is changed from resolved to not-found.
+    //
+    //  When does this become a problem? If the status of the *once-visited* node group is resolved,
+    //  changing the result to not-found makes the result changed.
+    //
+    //  This problem does not happen when we don't see any star link yet. Now, consider the minimum case.
+    //
+    //  @-> (A) -> [ some graph ]
+    //       ^            |
+    //       |            |
+    //       +------------+
+    //
+    //  In (A), we don't see any star link yet. So we can say that all the visited nodes does not have any local
+    //  resolution. Because if they had a local/indirect resolution, we should have already finished the tracing.
+    //
+    //  And even if the some graph will see the *once-visited* node (in this case, (A)), that does not affect the
+    //  result of the resolution. Because even if we follow the link to (A) or not follow the link to (A), the status
+    //  of the link is always not-found since (A) does not have any local resolution.
+    //  In the above case, we can use the result of the [some graph].
+    //
+    // 5. Once we see star links, even if we have not yet traversed that star link path, we should disable caching.
+    //
+    //  Here is the reason why:
+    //
+    //       +-------------+
+    //       |             |
+    //       v             |
+    //      (A) -> (B) -> (C) *-> [E]
+    //       *             ^
+    //       |             |
+    //       v             @
+    //      [D]
+    //
+    //  In the above case, (C) will be resolved with [D].
+    //  (C) will see (A) and (A) gives up in (A) -> (B) -> (C) route. So, (A) will fallback to [D].
+    //
+    //       +-------------+
+    //       |             |
+    //       v             |
+    //  @-> (A) -> (B) -> (C) *-> [E]
+    //       *
+    //       |
+    //       v
+    //      [D]
+    //
+    //  But in this case, (A) will be resolved with [E] (not [D]).
+    //  (C) will attempt to follow the link to (A), but it fails.
+    //  So (C) will fallback to the star link and found [E]. In this senario,
+    //  (C) is now resolved with [E]'s result.
+    //
+    //  The cause of this problem is also the same to 4.
+    //  In the latter case, when looking (C), we cannot use the cached result in (C).
+    //  Because the cached result of (C) depends on the *once-visited* node (A) and
+    //  (A) has the fallback system with the star link.
+    //  In the latter trial, we now assume that (A)'s status is not-found.
+    //  But, actually, in the former trial, (A)'s status becomes resolved due to the fallback to the [D].
+    //
+    // To summarize the observations.
+    //
+    //  1. The starting point is always cacheable.
+    //  2. A module that has resolved a local binding is always cacheable.
+    //  3. If we don't follow any star links during the resolution, we can see all the traced nodes are cacheable.
+    //  4. Once we follow star links, we should not retrieve the result from the cache and should not cache the result.
+    //  5. Once we see star links, even if we have not yet traversed that star link path, we should disable caching.
+
+    typedef WTF::HashSet<ResolveQuery, ResolveQuery::Hash, WTF::CustomHashTraits<ResolveQuery>> ResolveSet;
+    enum class Type { Query, IndirectFallback, GatherStars };
+    struct Task {
+        ResolveQuery query;
+        Type type;
+    };
+
+    Vector<Task, 8> pendingTasks;
+    ResolveSet resolveSet;
+    HashSet<AbstractModuleRecord*> starSet;
+
+    Vector<Resolution, 8> frames;
+
+    bool foundStarLinks = false;
+
+    frames.append(Resolution::notFound());
+
+    // Call when the query is not resolved in the current module.
+    // It will enqueue the star resolution requests. Return "false" if the error occurs.
+    auto resolveNonLocal = [&](const ResolveQuery& query) -> bool {
+        // http://www.ecma-international.org/ecma-262/6.0/#sec-resolveexport
+        // section 15.2.1.16.3, step 6
+        // If the "default" name is not resolved in the current module, we need to throw an error and stop resolution immediately,
+        // Rationale to this error: A default export cannot be provided by an export *.
+        if (query.exportName == exec->propertyNames().defaultKeyword.impl())
+            return false;
+
+        // step 7, If exportStarSet contains module, then return null.
+        if (!starSet.add(query.moduleRecord).isNewEntry)
+            return true;
+
+        // Enqueue the task to gather the results of the stars.
+        // And append the new Resolution frame to gather the local result of the stars.
+        pendingTasks.append(Task { query, Type::GatherStars });
+        foundStarLinks = true;
+        frames.append(Resolution::notFound());
+
+
+        // Enqueue the tasks in reverse order.
+        for (auto iterator = query.moduleRecord->starExportEntries().rbegin(), end = query.moduleRecord->starExportEntries().rend(); iterator != end; ++iterator) {
+            const RefPtr<UniquedStringImpl>& starModuleName = *iterator;
+            AbstractModuleRecord* importedModuleRecord = query.moduleRecord->hostResolveImportedModule(exec, Identifier::fromUid(exec, starModuleName.get()));
+            pendingTasks.append(Task { ResolveQuery(importedModuleRecord, query.exportName.get()), Type::Query });
+        }
+        return true;
+    };
+
+    // Return the current resolution value of the top frame.
+    auto currentTop = [&] () -> Resolution& {
+        ASSERT(!frames.isEmpty());
+        return frames.last();
+    };
+
+    // Merge the given resolution to the current resolution value of the top frame.
+    // If there is ambiguity, return "false". When the "false" is returned, we should make the result "ambiguous".
+    auto mergeToCurrentTop = [&] (const Resolution& resolution) -> bool {
+        if (resolution.type == Resolution::Type::NotFound)
+            return true;
+
+        if (currentTop().type == Resolution::Type::NotFound) {
+            currentTop() = resolution;
+            return true;
+        }
+
+        if (currentTop().moduleRecord != resolution.moduleRecord || currentTop().localName != resolution.localName)
+            return false;
+
+        return true;
+    };
+
+    auto cacheResolutionForQuery = [] (const ResolveQuery& query, const Resolution& resolution) {
+        ASSERT(resolution.type == Resolution::Type::Resolved);
+        query.moduleRecord->cacheResolution(query.exportName.get(), resolution);
+    };
+
+    pendingTasks.append(Task { root, Type::Query });
+    while (!pendingTasks.isEmpty()) {
+        const Task task = pendingTasks.takeLast();
+        const ResolveQuery& query = task.query;
+
+        switch (task.type) {
+        case Type::Query: {
+            AbstractModuleRecord* moduleRecord = query.moduleRecord;
+
+            if (!resolveSet.add(task.query).isNewEntry)
+                continue;
+
+            //  5. Once we see star links, even if we have not yet traversed that star link path, we should disable caching.
+            if (!moduleRecord->starExportEntries().isEmpty())
+                foundStarLinks = true;
+
+            //  4. Once we follow star links, we should not retrieve the result from the cache and should not cache the result.
+            if (!foundStarLinks) {
+                if (std::optional<Resolution> cachedResolution = moduleRecord->tryGetCachedResolution(query.exportName.get())) {
+                    if (!mergeToCurrentTop(*cachedResolution))
+                        return Resolution::ambiguous();
+                    continue;
+                }
+            }
+
+            const std::optional<ExportEntry> optionalExportEntry = moduleRecord->tryGetExportEntry(query.exportName.get());
+            if (!optionalExportEntry) {
+                // If there is no matched exported binding in the current module,
+                // we need to look into the stars.
+                if (!resolveNonLocal(task.query))
+                    return Resolution::error();
+                continue;
+            }
+
+            const ExportEntry& exportEntry = *optionalExportEntry;
+            switch (exportEntry.type) {
+            case ExportEntry::Type::Local: {
+                ASSERT(!exportEntry.localName.isNull());
+                Resolution resolution { Resolution::Type::Resolved, moduleRecord, exportEntry.localName };
+                //  2. A module that has resolved a local binding is always cacheable.
+                cacheResolutionForQuery(query, resolution);
+                if (!mergeToCurrentTop(resolution))
+                    return Resolution::ambiguous();
+                continue;
+            }
+
+            case ExportEntry::Type::Indirect: {
+                AbstractModuleRecord* importedModuleRecord = moduleRecord->hostResolveImportedModule(exec, exportEntry.moduleName);
+
+                // When the imported module does not produce any resolved binding, we need to look into the stars in the *current*
+                // module. To do this, we append the `IndirectFallback` task to the task queue.
+                pendingTasks.append(Task { query, Type::IndirectFallback });
+                // And append the new Resolution frame to check the indirect export will be resolved or not.
+                frames.append(Resolution::notFound());
+                pendingTasks.append(Task { ResolveQuery(importedModuleRecord, exportEntry.importName), Type::Query });
+                continue;
+            }
+            }
+            break;
+        }
+
+        case Type::IndirectFallback: {
+            Resolution resolution = frames.takeLast();
+
+            if (resolution.type == Resolution::Type::NotFound) {
+                // Indirect export entry does not produce any resolved binding.
+                // So we will investigate the stars.
+                if (!resolveNonLocal(task.query))
+                    return Resolution::error();
+                continue;
+            }
+
+            ASSERT_WITH_MESSAGE(resolution.type == Resolution::Type::Resolved, "When we see Error and Ambiguous, we immediately return from this loop. So here, only Resolved comes.");
+
+            //  3. If we don't follow any star links during the resolution, we can see all the traced nodes are cacheable.
+            //  4. Once we follow star links, we should not retrieve the result from the cache and should not cache the result.
+            if (!foundStarLinks)
+                cacheResolutionForQuery(query, resolution);
+
+            // If indirect export entry produces Resolved, we should merge it to the upper frame.
+            // And do not investigate the stars of the current module.
+            if (!mergeToCurrentTop(resolution))
+                return Resolution::ambiguous();
+            break;
+        }
+
+        case Type::GatherStars: {
+            Resolution resolution = frames.takeLast();
+            ASSERT_WITH_MESSAGE(resolution.type == Resolution::Type::Resolved || resolution.type == Resolution::Type::NotFound, "When we see Error and Ambiguous, we immediately return from this loop. So here, only Resolved and NotFound comes.");
+
+            // Merge the star resolution to the upper frame.
+            if (!mergeToCurrentTop(resolution))
+                return Resolution::ambiguous();
+            break;
+        }
+        }
+    }
+
+    ASSERT(frames.size() == 1);
+    //  1. The starting point is always cacheable.
+    if (frames[0].type == Resolution::Type::Resolved)
+        cacheResolutionForQuery(root, frames[0]);
+    return frames[0];
+}
+
+auto AbstractModuleRecord::resolveExport(ExecState* exec, const Identifier& exportName) -> Resolution
+{
+    // Look up the cached resolution first before entering the resolving loop, since the loop setup takes some cost.
+    if (std::optional<Resolution> cachedResolution = tryGetCachedResolution(exportName.impl()))
+        return *cachedResolution;
+    return resolveExportImpl(exec, ResolveQuery(this, exportName.impl()));
+}
+
+static void getExportedNames(ExecState* exec, AbstractModuleRecord* root, IdentifierSet& exportedNames)
+{
+    HashSet<AbstractModuleRecord*> exportStarSet;
+    Vector<AbstractModuleRecord*, 8> pendingModules;
+
+    pendingModules.append(root);
+
+    while (!pendingModules.isEmpty()) {
+        AbstractModuleRecord* moduleRecord = pendingModules.takeLast();
+        if (exportStarSet.contains(moduleRecord))
+            continue;
+        exportStarSet.add(moduleRecord);
+
+        for (const auto& pair : moduleRecord->exportEntries()) {
+            const AbstractModuleRecord::ExportEntry& exportEntry = pair.value;
+            if (moduleRecord == root || exec->propertyNames().defaultKeyword != exportEntry.exportName)
+                exportedNames.add(exportEntry.exportName.impl());
+        }
+
+        for (const auto& starModuleName : moduleRecord->starExportEntries()) {
+            AbstractModuleRecord* requestedModuleRecord = moduleRecord->hostResolveImportedModule(exec, Identifier::fromUid(exec, starModuleName.get()));
+            pendingModules.append(requestedModuleRecord);
+        }
+    }
+}
+
+JSModuleNamespaceObject* AbstractModuleRecord::getModuleNamespace(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-getmodulenamespace
+    if (m_moduleNamespaceObject)
+        return m_moduleNamespaceObject.get();
+
+    JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+    IdentifierSet exportedNames;
+    getExportedNames(exec, this, exportedNames);
+
+    IdentifierSet unambiguousNames;
+    for (auto& name : exportedNames) {
+        const AbstractModuleRecord::Resolution resolution = resolveExport(exec, Identifier::fromUid(exec, name.get()));
+        switch (resolution.type) {
+        case Resolution::Type::NotFound:
+            throwSyntaxError(exec, scope, makeString("Exported binding name '", String(name.get()), "' is not found."));
+            return nullptr;
+
+        case Resolution::Type::Error:
+            throwSyntaxError(exec, scope, makeString("Exported binding name 'default' cannot be resolved by star export entries."));
+            return nullptr;
+
+        case Resolution::Type::Ambiguous:
+            break;
+
+        case Resolution::Type::Resolved:
+            unambiguousNames.add(name);
+            break;
+        }
+    }
+
+    m_moduleNamespaceObject.set(vm, this, JSModuleNamespaceObject::create(exec, globalObject, globalObject->moduleNamespaceObjectStructure(), this, unambiguousNames));
+    return m_moduleNamespaceObject.get();
+}
+
+static String printableName(const RefPtr<UniquedStringImpl>& uid)
+{
+    if (uid->isSymbol())
+        return uid.get();
+    return WTF::makeString("'", String(uid.get()), "'");
+}
+
+static String printableName(const Identifier& ident)
+{
+    return printableName(ident.impl());
+}
+
+void AbstractModuleRecord::dump()
+{
+    dataLog("\nAnalyzing ModuleRecord key(", printableName(m_moduleKey), ")\n");
+
+    dataLog("    Dependencies: ", m_requestedModules.size(), " modules\n");
+    for (const auto& moduleName : m_requestedModules)
+        dataLog("      module(", printableName(moduleName), ")\n");
+
+    dataLog("    Import: ", m_importEntries.size(), " entries\n");
+    for (const auto& pair : m_importEntries) {
+        const ImportEntry& importEntry = pair.value;
+        dataLog("      import(", printableName(importEntry.importName), "), local(", printableName(importEntry.localName), "), module(", printableName(importEntry.moduleRequest), ")\n");
+    }
+
+    dataLog("    Export: ", m_exportEntries.size(), " entries\n");
+    for (const auto& pair : m_exportEntries) {
+        const ExportEntry& exportEntry = pair.value;
+        switch (exportEntry.type) {
+        case ExportEntry::Type::Local:
+            dataLog("      [Local] ", "export(", printableName(exportEntry.exportName), "), local(", printableName(exportEntry.localName), ")\n");
+            break;
+
+        case ExportEntry::Type::Indirect:
+            dataLog("      [Indirect] ", "export(", printableName(exportEntry.exportName), "), import(", printableName(exportEntry.importName), "), module(", printableName(exportEntry.moduleName), ")\n");
+            break;
+        }
+    }
+    for (const auto& moduleName : m_starExportEntries)
+        dataLog("      [Star] module(", printableName(moduleName.get()), ")\n");
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/AbstractModuleRecord.h b/Source/JavaScriptCore/runtime/AbstractModuleRecord.h
new file mode 100644 (file)
index 0000000..ec6992d
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "Identifier.h"
+#include "JSDestructibleObject.h"
+#include "SourceCode.h"
+#include "VariableEnvironment.h"
+#include <wtf/HashMap.h>
+#include <wtf/ListHashSet.h>
+#include <wtf/Optional.h>
+
+namespace JSC {
+
+class JSModuleEnvironment;
+class JSModuleNamespaceObject;
+class JSMap;
+
+// Based on the Source Text Module Record
+// http://www.ecma-international.org/ecma-262/6.0/#sec-source-text-module-records
+class AbstractModuleRecord : public JSDestructibleObject {
+    friend class LLIntOffsetsExtractor;
+public:
+    typedef JSDestructibleObject Base;
+
+    // https://tc39.github.io/ecma262/#sec-source-text-module-records
+    struct ExportEntry {
+        enum class Type {
+            Local,
+            Indirect
+        };
+
+        static ExportEntry createLocal(const Identifier& exportName, const Identifier& localName);
+        static ExportEntry createIndirect(const Identifier& exportName, const Identifier& importName, const Identifier& moduleName);
+
+        Type type;
+        Identifier exportName;
+        Identifier moduleName;
+        Identifier importName;
+        Identifier localName;
+    };
+
+    struct ImportEntry {
+        Identifier moduleRequest;
+        Identifier importName;
+        Identifier localName;
+
+        bool isNamespace(VM& vm) const
+        {
+            return importName == vm.propertyNames->timesIdentifier;
+        }
+    };
+
+    typedef WTF::ListHashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> OrderedIdentifierSet;
+    typedef HashMap<RefPtr<UniquedStringImpl>, ImportEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> ImportEntries;
+    typedef HashMap<RefPtr<UniquedStringImpl>, ExportEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> ExportEntries;
+
+    DECLARE_EXPORT_INFO;
+
+    void appendRequestedModule(const Identifier&);
+    void addStarExportEntry(const Identifier&);
+    void addImportEntry(const ImportEntry&);
+    void addExportEntry(const ExportEntry&);
+
+    std::optional<ImportEntry> tryGetImportEntry(UniquedStringImpl* localName);
+    std::optional<ExportEntry> tryGetExportEntry(UniquedStringImpl* exportName);
+
+    const SourceCode& sourceCode() const { return m_sourceCode; }
+    const Identifier& moduleKey() const { return m_moduleKey; }
+    const OrderedIdentifierSet& requestedModules() const { return m_requestedModules; }
+    const ExportEntries& exportEntries() const { return m_exportEntries; }
+    const ImportEntries& importEntries() const { return m_importEntries; }
+    const OrderedIdentifierSet& starExportEntries() const { return m_starExportEntries; }
+
+    const VariableEnvironment& declaredVariables() const { return m_declaredVariables; }
+    const VariableEnvironment& lexicalVariables() const { return m_lexicalVariables; }
+
+    void dump();
+
+    struct Resolution {
+        enum class Type { Resolved, NotFound, Ambiguous, Error };
+
+        static Resolution notFound();
+        static Resolution error();
+        static Resolution ambiguous();
+
+        Type type;
+        AbstractModuleRecord* moduleRecord;
+        Identifier localName;
+    };
+
+    Resolution resolveExport(ExecState*, const Identifier& exportName);
+    Resolution resolveImport(ExecState*, const Identifier& localName);
+
+    AbstractModuleRecord* hostResolveImportedModule(ExecState*, const Identifier& moduleName);
+
+    JSModuleNamespaceObject* getModuleNamespace(ExecState*);
+    
+    JSModuleEnvironment* moduleEnvironment()
+    {
+        ASSERT(m_moduleEnvironment);
+        return m_moduleEnvironment.get();
+    }
+
+protected:
+    AbstractModuleRecord(VM&, Structure*, const Identifier&, const SourceCode&, const VariableEnvironment&, const VariableEnvironment&);
+    void finishCreation(ExecState*, VM&);
+
+    static void visitChildren(JSCell*, SlotVisitor&);
+    static void destroy(JSCell*);
+
+    WriteBarrier<JSModuleEnvironment> m_moduleEnvironment;
+
+private:
+    struct ResolveQuery;
+    static Resolution resolveExportImpl(ExecState*, const ResolveQuery&);
+    std::optional<Resolution> tryGetCachedResolution(UniquedStringImpl* exportName);
+    void cacheResolution(UniquedStringImpl* exportName, const Resolution&);
+
+    // The loader resolves the given module name to the module key. The module key is the unique value to represent this module.
+    Identifier m_moduleKey;
+
+    SourceCode m_sourceCode;
+
+    VariableEnvironment m_declaredVariables;
+    VariableEnvironment m_lexicalVariables;
+
+    // Currently, we don't keep the occurrence order of the import / export entries.
+    // So, we does not guarantee the order of the errors.
+    // e.g. The import declaration that occurr later than the another import declaration may
+    //      throw the error even if the former import declaration also has the invalid content.
+    //
+    //      import ... // (1) this has some invalid content.
+    //      import ... // (2) this also has some invalid content.
+    //
+    //      In the above case, (2) may throw the error earlier than (1)
+    //
+    // But, in all the cases, we will throw the syntax error. So except for the content of the syntax error,
+    // there are no difference.
+
+    // Map localName -> ImportEntry.
+    ImportEntries m_importEntries;
+
+    // Map exportName -> ExportEntry.
+    ExportEntries m_exportEntries;
+
+    // Save the occurrence order since resolveExport requires it.
+    OrderedIdentifierSet m_starExportEntries;
+
+    // Save the occurrence order since the module loader loads and runs the modules in this order.
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-moduleevaluation
+    OrderedIdentifierSet m_requestedModules;
+
+    WriteBarrier<JSMap> m_dependenciesMap;
+    
+    WriteBarrier<JSModuleNamespaceObject> m_moduleNamespaceObject;
+
+    // We assume that all the AbstractModuleRecord are retained by JSModuleLoader's registry.
+    // So here, we don't visit each object for GC. The resolution cache map caches the once
+    // looked up correctly resolved resolution, since (1) we rarely looked up the non-resolved one,
+    // and (2) if we cache all the attempts the size of the map becomes infinitely large.
+    typedef HashMap<RefPtr<UniquedStringImpl>, Resolution, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> Resolutions;
+    Resolutions m_resolutionCache;
+};
+
+} // namespace JSC
index fdbc138..a4cd428 100644 (file)
@@ -876,6 +876,8 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
     if (Options::useWebAssembly()) {
         auto* webAssemblyPrototype = WebAssemblyPrototype::create(vm, this, WebAssemblyPrototype::createStructure(vm, this, m_objectPrototype.get()));
         m_webAssemblyStructure.set(vm, this, JSWebAssembly::createStructure(vm, this, webAssemblyPrototype));
+        m_webAssemblyModuleRecordStructure.set(vm, this, WebAssemblyModuleRecord::createStructure(vm, this, m_objectPrototype.get()));
+        m_webAssemblyFunctionStructure.set(vm, this, WebAssemblyFunction::createStructure(vm, this, m_objectPrototype.get()));
         auto* webAssembly = JSWebAssembly::create(vm, this, m_webAssemblyStructure.get());
         putDirectWithoutTransition(vm, Identifier::fromString(exec, "WebAssembly"), webAssembly, DontEnum);
 
@@ -1253,6 +1255,8 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
     
 #if ENABLE(WEBASSEMBLY)
     visitor.append(&thisObject->m_webAssemblyStructure);
+    visitor.append(&thisObject->m_webAssemblyModuleRecordStructure);
+    visitor.append(&thisObject->m_webAssemblyFunctionStructure);
     FOR_EACH_WEBASSEMBLY_CONSTRUCTOR_TYPE(VISIT_SIMPLE_TYPE)
 #endif // ENABLE(WEBASSEMBLY)
 
index 575be9c..71d9802 100644 (file)
@@ -342,6 +342,8 @@ public:
     
 #if ENABLE(WEBASSEMBLY)
     WriteBarrier<Structure> m_webAssemblyStructure;
+    WriteBarrier<Structure> m_webAssemblyModuleRecordStructure;
+    WriteBarrier<Structure> m_webAssemblyFunctionStructure;
     FOR_EACH_WEBASSEMBLY_CONSTRUCTOR_TYPE(DEFINE_STORAGE_FOR_SIMPLE_TYPE)
 #endif // ENABLE(WEBASSEMBLY)
 
@@ -625,6 +627,10 @@ public:
     Structure* proxyRevokeStructure() const { return m_proxyRevokeStructure.get(); }
     Structure* moduleLoaderStructure() const { return m_moduleLoaderStructure.get(); }
     Structure* restParameterStructure() const { return arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous); }
+#if ENABLE(WEBASSEMBLY)
+    Structure* webAssemblyModuleRecordStructure() const { return m_webAssemblyModuleRecordStructure.get(); }
+    Structure* webAssemblyFunctionStructure() const { return m_webAssemblyFunctionStructure.get(); }
+#endif // ENABLE(WEBASSEMBLY)
 
     JS_EXPORT_PRIVATE void setRemoteDebuggingEnabled(bool);
     JS_EXPORT_PRIVATE bool remoteDebuggingEnabled() const;
index 7971032..8168b78 100644 (file)
@@ -29,6 +29,7 @@
 #include "config.h"
 #include "JSModuleEnvironment.h"
 
+#include "AbstractModuleRecord.h"
 #include "Interpreter.h"
 #include "JSCInlines.h"
 #include "JSFunction.h"
@@ -40,12 +41,12 @@ namespace JSC {
 const ClassInfo JSModuleEnvironment::s_info = { "JSModuleEnvironment", &Base::s_info, 0, CREATE_METHOD_TABLE(JSModuleEnvironment) };
 
 JSModuleEnvironment* JSModuleEnvironment::create(
-    VM& vm, Structure* structure, JSScope* currentScope, SymbolTable* symbolTable, JSValue initialValue, JSModuleRecord* moduleRecord)
+    VM& vm, Structure* structure, JSScope* currentScope, SymbolTable* symbolTable, JSValue initialValue, AbstractModuleRecord* moduleRecord)
 {
     // JSLexicalEnvironment (precisely, JSEnvironmentRecord) has the storage to store the variable slots after the its class storage.
     // Because the offset of the variable slots are fixed in the JSEnvironmentRecord, inheritting these class and adding new member field is not allowed,
     // the new member will overlap the variable slots.
-    // To keep the JSModuleEnvironment compatible to the JSLexicalEnvironment but add the new member to store the JSModuleRecord, we additionally allocate
+    // To keep the JSModuleEnvironment compatible to the JSLexicalEnvironment but add the new member to store the AbstractModuleRecord, we additionally allocate
     // the storage after the variable slots.
     //
     // JSLexicalEnvironment:
@@ -62,7 +63,7 @@ JSModuleEnvironment* JSModuleEnvironment::create(
     return result;
 }
 
-void JSModuleEnvironment::finishCreation(VM& vm, JSValue initialValue, JSModuleRecord* moduleRecord)
+void JSModuleEnvironment::finishCreation(VM& vm, JSValue initialValue, AbstractModuleRecord* moduleRecord)
 {
     Base::finishCreation(vm, initialValue);
     this->moduleRecordSlot().set(vm, this, moduleRecord);
@@ -81,8 +82,8 @@ bool JSModuleEnvironment::getOwnPropertySlot(JSObject* cell, ExecState* exec, Pr
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
     JSModuleEnvironment* thisObject = jsCast<JSModuleEnvironment*>(cell);
-    JSModuleRecord::Resolution resolution = thisObject->moduleRecord()->resolveImport(exec, Identifier::fromUid(exec, propertyName.uid()));
-    if (resolution.type == JSModuleRecord::Resolution::Type::Resolved) {
+    AbstractModuleRecord::Resolution resolution = thisObject->moduleRecord()->resolveImport(exec, Identifier::fromUid(exec, propertyName.uid()));
+    if (resolution.type == AbstractModuleRecord::Resolution::Type::Resolved) {
         // When resolveImport resolves the resolution, the imported module environment must have the binding.
         JSModuleEnvironment* importedModuleEnvironment = resolution.moduleRecord->moduleEnvironment();
         PropertySlot redirectSlot(importedModuleEnvironment, PropertySlot::InternalMethodType::Get);
@@ -102,7 +103,7 @@ void JSModuleEnvironment::getOwnNonIndexPropertyNames(JSObject* cell, ExecState*
     JSModuleEnvironment* thisObject = jsCast<JSModuleEnvironment*>(cell);
     if (propertyNamesArray.includeStringProperties()) {
         for (const auto& pair : thisObject->moduleRecord()->importEntries()) {
-            const JSModuleRecord::ImportEntry& importEntry = pair.value;
+            const AbstractModuleRecord::ImportEntry& importEntry = pair.value;
             if (!importEntry.isNamespace(exec->vm()))
                 propertyNamesArray.add(importEntry.localName);
         }
@@ -117,8 +118,8 @@ bool JSModuleEnvironment::put(JSCell* cell, ExecState* exec, PropertyName proper
 
     JSModuleEnvironment* thisObject = jsCast<JSModuleEnvironment*>(cell);
     // All imported bindings are immutable.
-    JSModuleRecord::Resolution resolution = thisObject->moduleRecord()->resolveImport(exec, Identifier::fromUid(exec, propertyName.uid()));
-    if (resolution.type == JSModuleRecord::Resolution::Type::Resolved) {
+    AbstractModuleRecord::Resolution resolution = thisObject->moduleRecord()->resolveImport(exec, Identifier::fromUid(exec, propertyName.uid()));
+    if (resolution.type == AbstractModuleRecord::Resolution::Type::Resolved) {
         throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError));
         return false;
     }
@@ -129,8 +130,8 @@ bool JSModuleEnvironment::deleteProperty(JSCell* cell, ExecState* exec, Property
 {
     JSModuleEnvironment* thisObject = jsCast<JSModuleEnvironment*>(cell);
     // All imported bindings are immutable.
-    JSModuleRecord::Resolution resolution = thisObject->moduleRecord()->resolveImport(exec, Identifier::fromUid(exec, propertyName.uid()));
-    if (resolution.type == JSModuleRecord::Resolution::Type::Resolved)
+    AbstractModuleRecord::Resolution resolution = thisObject->moduleRecord()->resolveImport(exec, Identifier::fromUid(exec, propertyName.uid()));
+    if (resolution.type == AbstractModuleRecord::Resolution::Type::Resolved)
         return false;
     return Base::deleteProperty(thisObject, exec, propertyName);
 }
index 4c76149..c3fd16f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #pragma once
 
 #include "JSLexicalEnvironment.h"
-#include "JSModuleRecord.h"
 
 namespace JSC {
 
+class AbstractModuleRecord;
 class Register;
 
 class JSModuleEnvironment : public JSLexicalEnvironment {
@@ -42,9 +42,9 @@ public:
     typedef JSLexicalEnvironment Base;
     static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames;
 
-    static JSModuleEnvironment* create(VM&, Structure*, JSScope*, SymbolTable*, JSValue initialValue, JSModuleRecord*);
+    static JSModuleEnvironment* create(VM&, Structure*, JSScope*, SymbolTable*, JSValue initialValue, AbstractModuleRecord*);
 
-    static JSModuleEnvironment* create(VM& vm, JSGlobalObject* globalObject, JSScope* currentScope, SymbolTable* symbolTable, JSValue initialValue, JSModuleRecord* moduleRecord)
+    static JSModuleEnvironment* create(VM& vm, JSGlobalObject* globalObject, JSScope* currentScope, SymbolTable* symbolTable, JSValue initialValue, AbstractModuleRecord* moduleRecord)
     {
         Structure* structure = globalObject->moduleEnvironmentStructure();
         return create(vm, structure, currentScope, symbolTable, initialValue, moduleRecord);
@@ -60,16 +60,16 @@ public:
     static size_t offsetOfModuleRecord(SymbolTable* symbolTable)
     {
         size_t offset = Base::allocationSize(symbolTable);
-        ASSERT(WTF::roundUpToMultipleOf<sizeof(WriteBarrier<JSModuleRecord>)>(offset) == offset);
+        ASSERT(WTF::roundUpToMultipleOf<sizeof(WriteBarrier<AbstractModuleRecord>)>(offset) == offset);
         return offset;
     }
 
     static size_t allocationSize(SymbolTable* symbolTable)
     {
-        return offsetOfModuleRecord(symbolTable) + sizeof(WriteBarrier<JSModuleRecord>);
+        return offsetOfModuleRecord(symbolTable) + sizeof(WriteBarrier<AbstractModuleRecord>);
     }
 
-    JSModuleRecord* moduleRecord()
+    AbstractModuleRecord* moduleRecord()
     {
         return moduleRecordSlot().get();
     }
@@ -82,11 +82,11 @@ public:
 private:
     JSModuleEnvironment(VM&, Structure*, JSScope*, SymbolTable*);
 
-    void finishCreation(VM&, JSValue initialValue, JSModuleRecord*);
+    void finishCreation(VM&, JSValue initialValue, AbstractModuleRecord*);
 
-    WriteBarrierBase<JSModuleRecord>& moduleRecordSlot()
+    WriteBarrierBase<AbstractModuleRecord>& moduleRecordSlot()
     {
-        return *bitwise_cast<WriteBarrierBase<JSModuleRecord>*>(bitwise_cast<char*>(this) + offsetOfModuleRecord(symbolTable()));
+        return *bitwise_cast<WriteBarrierBase<AbstractModuleRecord>*>(bitwise_cast<char*>(this) + offsetOfModuleRecord(symbolTable()));
     }
 
     static void visitChildren(JSCell*, SlotVisitor&);
index 9958dca..82724ff 100644 (file)
 #include "config.h"
 #include "JSModuleNamespaceObject.h"
 
+#include "AbstractModuleRecord.h"
 #include "Error.h"
 #include "JSCInlines.h"
 #include "JSModuleEnvironment.h"
-#include "JSModuleRecord.h"
 #include "JSPropertyNameIterator.h"
 
 namespace JSC {
@@ -45,7 +45,7 @@ JSModuleNamespaceObject::JSModuleNamespaceObject(VM& vm, Structure* structure)
 {
 }
 
-void JSModuleNamespaceObject::finishCreation(ExecState* exec, JSGlobalObject* globalObject, JSModuleRecord* moduleRecord, const IdentifierSet& exports)
+void JSModuleNamespaceObject::finishCreation(ExecState* exec, JSGlobalObject* globalObject, AbstractModuleRecord* moduleRecord, const IdentifierSet& exports)
 {
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
@@ -120,12 +120,12 @@ bool JSModuleNamespaceObject::getOwnPropertySlot(JSObject* cell, ExecState* exec
     switch (slot.internalMethodType()) {
     case PropertySlot::InternalMethodType::Get:
     case PropertySlot::InternalMethodType::GetOwnProperty: {
-        JSModuleRecord* moduleRecord = thisObject->moduleRecord();
+        AbstractModuleRecord* moduleRecord = thisObject->moduleRecord();
 
-        JSModuleRecord::Resolution resolution = moduleRecord->resolveExport(exec, Identifier::fromUid(exec, propertyName.uid()));
-        ASSERT(resolution.type != JSModuleRecord::Resolution::Type::NotFound && resolution.type != JSModuleRecord::Resolution::Type::Ambiguous);
+        AbstractModuleRecord::Resolution resolution = moduleRecord->resolveExport(exec, Identifier::fromUid(exec, propertyName.uid()));
+        ASSERT(resolution.type != AbstractModuleRecord::Resolution::Type::NotFound && resolution.type != AbstractModuleRecord::Resolution::Type::Ambiguous);
 
-        JSModuleRecord* targetModule = resolution.moduleRecord;
+        AbstractModuleRecord* targetModule = resolution.moduleRecord;
         JSModuleEnvironment* targetEnvironment = targetModule->moduleEnvironment();
 
         PropertySlot trampolineSlot(targetEnvironment, PropertySlot::InternalMethodType::Get);
index 8f5baf7..4060b39 100644 (file)
 
 namespace JSC {
 
-class JSModuleRecord;
+class AbstractModuleRecord;
 
 class JSModuleNamespaceObject : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
     static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesGetPropertyNames | GetOwnPropertySlotIsImpureForPropertyAbsence;
 
-    static JSModuleNamespaceObject* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, JSModuleRecord* moduleRecord, const IdentifierSet& exports)
+    static JSModuleNamespaceObject* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, AbstractModuleRecord* moduleRecord, const IdentifierSet& exports)
     {
         JSModuleNamespaceObject* object = new (NotNull, allocateCell<JSModuleNamespaceObject>(exec->vm().heap)) JSModuleNamespaceObject(exec->vm(), structure);
         object->finishCreation(exec, globalObject, moduleRecord, exports);
@@ -58,10 +58,10 @@ public:
         return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
     }
 
-    JSModuleRecord* moduleRecord() { return m_moduleRecord.get(); }
+    AbstractModuleRecord* moduleRecord() { return m_moduleRecord.get(); }
 
 protected:
-    JS_EXPORT_PRIVATE void finishCreation(ExecState*, JSGlobalObject*, JSModuleRecord*, const IdentifierSet& exports);
+    JS_EXPORT_PRIVATE void finishCreation(ExecState*, JSGlobalObject*, AbstractModuleRecord*, const IdentifierSet& exports);
     JS_EXPORT_PRIVATE JSModuleNamespaceObject(VM&, Structure*);
 
 private:
@@ -71,7 +71,7 @@ private:
     typedef WTF::ListHashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> OrderedIdentifierSet;
 
     OrderedIdentifierSet m_exports;
-    WriteBarrier<JSModuleRecord> m_moduleRecord;
+    WriteBarrier<AbstractModuleRecord> m_moduleRecord;
 };
 
 } // namespace JSC
index 7617573..c2e6156 100644 (file)
@@ -29,7 +29,6 @@
 #include "Error.h"
 #include "Interpreter.h"
 #include "JSCInlines.h"
-#include "JSMap.h"
 #include "JSModuleEnvironment.h"
 #include "JSModuleNamespaceObject.h"
 #include "UnlinkedModuleProgramCodeBlock.h"
@@ -38,683 +37,40 @@ namespace JSC {
 
 const ClassInfo JSModuleRecord::s_info = { "ModuleRecord", &Base::s_info, 0, CREATE_METHOD_TABLE(JSModuleRecord) };
 
-void JSModuleRecord::destroy(JSCell* cell)
-{
-    JSModuleRecord* thisObject = jsCast<JSModuleRecord*>(cell);
-    thisObject->JSModuleRecord::~JSModuleRecord();
-}
 
-void JSModuleRecord::finishCreation(ExecState* exec, VM& vm)
+Structure* JSModuleRecord::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
 {
-    Base::finishCreation(vm);
-    ASSERT(inherits(info()));
-    putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("registryEntry")), jsUndefined());
-    putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("evaluated")), jsBoolean(false));
-
-    auto scope = DECLARE_THROW_SCOPE(vm);
-    JSMap* map = JSMap::create(exec, vm, globalObject()->mapStructure());
-    RELEASE_ASSERT(!scope.exception());
-    m_dependenciesMap.set(vm, this, map);
-    putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("dependenciesMap")), m_dependenciesMap.get());
+    return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
 }
 
-void JSModuleRecord::visitChildren(JSCell* cell, SlotVisitor& visitor)
+JSModuleRecord* JSModuleRecord::create(ExecState* exec, VM& vm, Structure* structure, const Identifier& moduleKey, const SourceCode& sourceCode, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables)
 {
-    JSModuleRecord* thisObject = jsCast<JSModuleRecord*>(cell);
-    Base::visitChildren(thisObject, visitor);
-    visitor.append(&thisObject->m_moduleEnvironment);
-    visitor.append(&thisObject->m_moduleNamespaceObject);
-    visitor.append(&thisObject->m_moduleProgramExecutable);
-    visitor.append(&thisObject->m_dependenciesMap);
-}
-
-void JSModuleRecord::appendRequestedModule(const Identifier& moduleName)
-{
-    m_requestedModules.add(moduleName.impl());
+    JSModuleRecord* instance = new (NotNull, allocateCell<JSModuleRecord>(vm.heap)) JSModuleRecord(vm, structure, moduleKey, sourceCode, declaredVariables, lexicalVariables);
+    instance->finishCreation(exec, vm);
+    return instance;
 }
-
-void JSModuleRecord::addStarExportEntry(const Identifier& moduleName)
-{
-    m_starExportEntries.add(moduleName.impl());
-}
-
-void JSModuleRecord::addImportEntry(const ImportEntry& entry)
-{
-    bool isNewEntry = m_importEntries.add(entry.localName.impl(), entry).isNewEntry;
-    ASSERT_UNUSED(isNewEntry, isNewEntry); // This is guaranteed by the parser.
-}
-
-void JSModuleRecord::addExportEntry(const ExportEntry& entry)
+JSModuleRecord::JSModuleRecord(VM& vm, Structure* structure, const Identifier& moduleKey, const SourceCode& sourceCode, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables)
+    : Base(vm, structure, moduleKey, sourceCode, declaredVariables, lexicalVariables)
 {
-    bool isNewEntry = m_exportEntries.add(entry.exportName.impl(), entry).isNewEntry;
-    ASSERT_UNUSED(isNewEntry, isNewEntry); // This is guaranteed by the parser.
 }
 
-auto JSModuleRecord::tryGetImportEntry(UniquedStringImpl* localName) -> std::optional<ImportEntry>
-{
-    const auto iterator = m_importEntries.find(localName);
-    if (iterator == m_importEntries.end())
-        return std::nullopt;
-    return std::optional<ImportEntry>(iterator->value);
-}
-
-auto JSModuleRecord::tryGetExportEntry(UniquedStringImpl* exportName) -> std::optional<ExportEntry>
-{
-    const auto iterator = m_exportEntries.find(exportName);
-    if (iterator == m_exportEntries.end())
-        return std::nullopt;
-    return std::optional<ExportEntry>(iterator->value);
-}
-
-auto JSModuleRecord::ExportEntry::createLocal(const Identifier& exportName, const Identifier& localName) -> ExportEntry
-{
-    return ExportEntry { Type::Local, exportName, Identifier(), Identifier(), localName };
-}
-
-auto JSModuleRecord::ExportEntry::createIndirect(const Identifier& exportName, const Identifier& importName, const Identifier& moduleName) -> ExportEntry
-{
-    return ExportEntry { Type::Indirect, exportName, moduleName, importName, Identifier() };
-}
-
-auto JSModuleRecord::Resolution::notFound() -> Resolution
-{
-    return Resolution { Type::NotFound, nullptr, Identifier() };
-}
-
-auto JSModuleRecord::Resolution::error() -> Resolution
-{
-    return Resolution { Type::Error, nullptr, Identifier() };
-}
-
-auto JSModuleRecord::Resolution::ambiguous() -> Resolution
-{
-    return Resolution { Type::Ambiguous, nullptr, Identifier() };
-}
-
-static JSValue identifierToJSValue(ExecState* exec, const Identifier& identifier)
-{
-    if (identifier.isSymbol())
-        return Symbol::create(exec->vm(), static_cast<SymbolImpl&>(*identifier.impl()));
-    return jsString(&exec->vm(), identifier.impl());
-}
-
-JSModuleRecord* JSModuleRecord::hostResolveImportedModule(ExecState* exec, const Identifier& moduleName)
-{
-    JSValue moduleNameValue = identifierToJSValue(exec, moduleName);
-    JSValue pair = m_dependenciesMap->JSMap::get(exec, moduleNameValue);
-    return jsCast<JSModuleRecord*>(pair.get(exec, Identifier::fromString(exec, "value")));
-}
-
-auto JSModuleRecord::resolveImport(ExecState* exec, const Identifier& localName) -> Resolution
-{
-    std::optional<ImportEntry> optionalImportEntry = tryGetImportEntry(localName.impl());
-    if (!optionalImportEntry)
-        return Resolution::notFound();
-
-    const ImportEntry& importEntry = *optionalImportEntry;
-    if (importEntry.isNamespace(exec->vm()))
-        return Resolution::notFound();
-
-    JSModuleRecord* importedModule = hostResolveImportedModule(exec, importEntry.moduleRequest);
-    return importedModule->resolveExport(exec, importEntry.importName);
-}
-
-struct JSModuleRecord::ResolveQuery {
-    struct Hash {
-        static unsigned hash(const ResolveQuery&);
-        static bool equal(const ResolveQuery&, const ResolveQuery&);
-        static const bool safeToCompareToEmptyOrDeleted = true;
-    };
-
-    ResolveQuery(JSModuleRecord* moduleRecord, UniquedStringImpl* exportName)
-        : moduleRecord(moduleRecord)
-        , exportName(exportName)
-    {
-    }
-
-    ResolveQuery(JSModuleRecord* moduleRecord, const Identifier& exportName)
-        : ResolveQuery(moduleRecord, exportName.impl())
-    {
-    }
-
-    enum EmptyValueTag { EmptyValue };
-    ResolveQuery(EmptyValueTag)
-    {
-    }
-
-    enum DeletedValueTag { DeletedValue };
-    ResolveQuery(DeletedValueTag)
-        : moduleRecord(nullptr)
-        , exportName(WTF::HashTableDeletedValue)
-    {
-    }
-
-    bool isEmptyValue() const
-    {
-        return !exportName;
-    }
-
-    bool isDeletedValue() const
-    {
-        return exportName.isHashTableDeletedValue();
-    }
-
-    // The module record is not marked from the GC. But these records are reachable from the JSGlobalObject.
-    // So we don't care the reachability to this record.
-    JSModuleRecord* moduleRecord;
-    RefPtr<UniquedStringImpl> exportName;
-};
-
-inline unsigned JSModuleRecord::ResolveQuery::Hash::hash(const ResolveQuery& query)
-{
-    return WTF::PtrHash<JSModuleRecord*>::hash(query.moduleRecord) + IdentifierRepHash::hash(query.exportName);
-}
-
-inline bool JSModuleRecord::ResolveQuery::Hash::equal(const ResolveQuery& lhs, const ResolveQuery& rhs)
-{
-    return lhs.moduleRecord == rhs.moduleRecord && lhs.exportName == rhs.exportName;
-}
-
-auto JSModuleRecord::tryGetCachedResolution(UniquedStringImpl* exportName) -> std::optional<Resolution>
-{
-    const auto iterator = m_resolutionCache.find(exportName);
-    if (iterator == m_resolutionCache.end())
-        return std::nullopt;
-    return std::optional<Resolution>(iterator->value);
-}
-
-void JSModuleRecord::cacheResolution(UniquedStringImpl* exportName, const Resolution& resolution)
-{
-    m_resolutionCache.add(exportName, resolution);
-}
-
-auto JSModuleRecord::resolveExportImpl(ExecState* exec, const ResolveQuery& root) -> Resolution
-{
-    // http://www.ecma-international.org/ecma-262/6.0/#sec-resolveexport
-
-    // How to avoid C++ recursion in this function:
-    // This function avoids C++ recursion of the naive ResolveExport implementation.
-    // Flatten the recursion to the loop with the task queue and frames.
-    //
-    // 1. pendingTasks
-    //     We enqueue the recursive resolveExport call to this queue to avoid recursive calls in C++.
-    //     The task has 3 types. (1) Query, (2) IndirectFallback and (3) GatherStars.
-    //     (1) Query
-    //         Querying the resolution to the current module.
-    //     (2) IndirectFallback
-    //         Examine the result of the indirect export resolution. Only when the indirect export resolution fails,
-    //         we look into the star exports. (step 5-a-vi).
-    //     (3) GatherStars
-    //         Examine the result of the star export resolutions.
-    //
-    // 2. frames
-    //     When the spec calls the resolveExport recursively, instead we append the frame
-    //     (that holds the result resolution) to the frames and enqueue the task to the pendingTasks.
-    //     The entry in the frames means the *local* resolution result of the specific recursive resolveExport.
-    //
-    // We should maintain the local resolution result instead of holding the global resolution result only.
-    // For example,
-    //
-    //     star
-    // (1) ---> (2) "Resolve"
-    //      |
-    //      |
-    //      +-> (3) "NotFound"
-    //      |
-    //      |       star
-    //      +-> (4) ---> (5) "Resolve" [here]
-    //               |
-    //               |
-    //               +-> (6) "Error"
-    //
-    // Consider the above graph. The numbers represents the modules. Now we are [here].
-    // If we only hold the global resolution result during the resolveExport operation, [here],
-    // we decide the entire result of resolveExport is "Ambiguous", because there are multiple
-    // "Reslove" (in module (2) and (5)). However, this should become "Error" because (6) will
-    // propagate "Error" state to the (4), (4) will become "Error" and then, (1) will become
-    // "Error". We should aggregate the results at the star exports point ((4) and (1)).
-    //
-    // Usually, both "Error" and "Ambiguous" states will throw the syntax error. So except for the content of the
-    // error message, there are no difference. (And if we fix the (6) that raises "Error", next, it will produce
-    // the "Ambiguous" error due to (5). Anyway, user need to fix the both. So which error should be raised at first
-    // doesn't matter so much.
-    //
-    // However, this may become the problem under the module namespace creation.
-    // http://www.ecma-international.org/ecma-262/6.0/#sec-getmodulenamespace
-    // section 15.2.1.18, step 3-d-ii
-    // Here, we distinguish "Ambiguous" and "Error". When "Error" state is produced, we need to throw the propagated error.
-    // But if "Ambiguous" state comes, we just ignore the result.
-    // To follow the requirement strictly, in this implementation, we keep the local resolution result to produce the
-    // correct result under the above complex cases.
-
-    // Caching strategy:
-    // The resolveExport operation is frequently called. So caching results is important.
-    // We observe the following aspects and based on them construct the caching strategy.
-    // Here, we attempt to cache the resolution by constructing the map in module records.
-    // That means  Module -> ExportName -> Maybe<Resolution>.
-    // Technically, all the JSModuleRecords have the Map<ExportName, Resolution> for caching.
-    //
-    // The important observations are that,
-    //
-    //  - *cacheable* means that traversing to this node from a path will produce the same results as starting from this node.
-    //
-    //    Here, we define the resovling route. We represent [?] as the module that has the local binding.
-    //    And (?) as the module without the local binding.
-    //
-    //      @ -> (A) -> (B) -> [C]
-    //
-    //    We list the resolving route for each node.
-    //
-    //    (A): (A) -> (B) -> [C]
-    //    (B): (B) -> [C]
-    //    [C]: [C]
-    //
-    //    In this case, if we start the tracing from (B), the resolving route becomes (B) -> [C].
-    //    So this is the same. At that time, we can say (B) is cacheable in the first tracing.
-    //
-    //  - The cache ability of a node depends on the resolving route from this node.
-    //
-    // 1. The starting point is always cacheable.
-    //
-    // 2. A module that has resolved a local binding is always cacheable.
-    //
-    //  @ -> (A) -> [B]
-    //
-    //  In the above case, we can see the [B] as cacheable.
-    //  This is because when starting from [B] node, we immediately resolve with the local binding.
-    //  So the resolving route from [B] does not depend on the starting point.
-    //
-    // 3. If we don't follow any star links during the resolution, we can see all the traced nodes are cacheable.
-    //
-    //  If there are non star links, it means that there is *no branch* in the module dependency graph.
-    //  This *no branch* feature makes all the modules cachable.
-    //
-    //  I.e, if we traverse one star link (even if we successfully resolve that star link),
-    //  we must still traverse all other star links. I would also explain we don't run into
-    //  this when resolving a local/indirect link. When resolving a local/indirect link,
-    //  we won't traverse any star links.
-    //  And since the module can hold only one local/indirect link for the specific export name (if there
-    //  are multiple local/indirect links that has the same export name, it should be syntax error in the
-    //  parsing phase.), there is no multiple outgoing links from a module.
-    //
-    //  @ -> (A) --> (B) -> [C] -> (D) -> (E) -+
-    //                ^                        |
-    //                |                        |
-    //                +------------------------+
-    //
-    //  When starting from @, [C] will be found as the module resolving the given binding.
-    //  In this case, (B) can cache this resolution. Since the resolving route is the same to the one when
-    //  starting from (B). After caching the above result, we attempt to resolve the same binding from (D).
-    //
-    //                              @
-    //                              |
-    //                              v
-    //  @ -> (A) --> (B) -> [C] -> (D) -> (E) -+
-    //                ^                        |
-    //                |                        |
-    //                +------------------------+
-    //
-    //  In this case, we can use the (B)'s cached result. And (E) can be cached.
-    //
-    //    (E): The resolving route is now (E) -> (B) -> [C]. That is the same when starting from (E).
-    //
-    //  No branching makes that the problematic *once-visited* node cannot be seen.
-    //  The *once-visited* node makes the resolving route changed since when we see the *once-visited* node,
-    //  we stop tracing this.
-    //
-    //  If there is no star links and if we look *once-visited* node under no branching graph, *once-visited*
-    //  node cannot resolve the requested binding. If the *once-visited* node can resolve the binding, we
-    //  should have already finished the resolution before reaching this *once-visited* node.
-    //
-    // 4. Once we follow star links, we should not retrieve the result from the cache and should not cache.
-    //
-    //  Star links are only the way to introduce branch.
-    //  Once we follow the star links during the resolution, we cannot cache naively.
-    //  This is because the cacheability depends on the resolving route. And branching produces the problematic *once-visited*
-    //  nodes. Since we don't follow the *once-visited* node, the resolving route from the node becomes different from
-    //  the resolving route when starting from this node.
-    //
-    //  The following example explains when we should not retrieve the cache and cache the result.
-    //
-    //               +----> (D) ------+
-    //               |                |
-    //               |                v
-    //      (A) *----+----> (B) ---> [C]
-    //                       ^
-    //                       |
-    //                       @
-    //
-    //  When starting from (B), we find [C]. In this resolving route, we don't find any star link.
-    //  And by definition, (B) and [C] are cachable. (B) is the starting point. And [C] has the local binding.
-    //
-    //               +----> (D) ------+
-    //               |                |
-    //               |                v
-    //  @-> (A) *----+----> (B) ---> [C]
-    //
-    //  But when starting from (A), we should not get the value from the cache. Because,
-    //
-    //    1. When looking (D), we reach [C] and make both resolved.
-    //    2. When looking (B), if we retrieved the last cache from (B), (B) becomes resolved.
-    //    3. But actually, (B) is not-found in this trial because (C) is already *once-visited*.
-    //    4. If we accidentally make (B) resolved, (A) becomes ambiguous. But the correct answer is resolved.
-    //
-    //  Why is this problem caused? This is because the *once-visited* node makes the result not-found.
-    //  In the second trial, (B) -> [C] result is changed from resolved to not-found.
-    //
-    //  When does this become a problem? If the status of the *once-visited* node group is resolved,
-    //  changing the result to not-found makes the result changed.
-    //
-    //  This problem does not happen when we don't see any star link yet. Now, consider the minimum case.
-    //
-    //  @-> (A) -> [ some graph ]
-    //       ^            |
-    //       |            |
-    //       +------------+
-    //
-    //  In (A), we don't see any star link yet. So we can say that all the visited nodes does not have any local
-    //  resolution. Because if they had a local/indirect resolution, we should have already finished the tracing.
-    //
-    //  And even if the some graph will see the *once-visited* node (in this case, (A)), that does not affect the
-    //  result of the resolution. Because even if we follow the link to (A) or not follow the link to (A), the status
-    //  of the link is always not-found since (A) does not have any local resolution.
-    //  In the above case, we can use the result of the [some graph].
-    //
-    // 5. Once we see star links, even if we have not yet traversed that star link path, we should disable caching.
-    //
-    //  Here is the reason why:
-    //
-    //       +-------------+
-    //       |             |
-    //       v             |
-    //      (A) -> (B) -> (C) *-> [E]
-    //       *             ^
-    //       |             |
-    //       v             @
-    //      [D]
-    //
-    //  In the above case, (C) will be resolved with [D].
-    //  (C) will see (A) and (A) gives up in (A) -> (B) -> (C) route. So, (A) will fallback to [D].
-    //
-    //       +-------------+
-    //       |             |
-    //       v             |
-    //  @-> (A) -> (B) -> (C) *-> [E]
-    //       *
-    //       |
-    //       v
-    //      [D]
-    //
-    //  But in this case, (A) will be resolved with [E] (not [D]).
-    //  (C) will attempt to follow the link to (A), but it fails.
-    //  So (C) will fallback to the star link and found [E]. In this senario,
-    //  (C) is now resolved with [E]'s result.
-    //
-    //  The cause of this problem is also the same to 4.
-    //  In the latter case, when looking (C), we cannot use the cached result in (C).
-    //  Because the cached result of (C) depends on the *once-visited* node (A) and
-    //  (A) has the fallback system with the star link.
-    //  In the latter trial, we now assume that (A)'s status is not-found.
-    //  But, actually, in the former trial, (A)'s status becomes resolved due to the fallback to the [D].
-    //
-    // To summarize the observations.
-    //
-    //  1. The starting point is always cacheable.
-    //  2. A module that has resolved a local binding is always cacheable.
-    //  3. If we don't follow any star links during the resolution, we can see all the traced nodes are cacheable.
-    //  4. Once we follow star links, we should not retrieve the result from the cache and should not cache the result.
-    //  5. Once we see star links, even if we have not yet traversed that star link path, we should disable caching.
-
-    typedef WTF::HashSet<ResolveQuery, ResolveQuery::Hash, WTF::CustomHashTraits<ResolveQuery>> ResolveSet;
-    enum class Type { Query, IndirectFallback, GatherStars };
-    struct Task {
-        ResolveQuery query;
-        Type type;
-    };
-
-    Vector<Task, 8> pendingTasks;
-    ResolveSet resolveSet;
-    HashSet<JSModuleRecord*> starSet;
-
-    Vector<Resolution, 8> frames;
-
-    bool foundStarLinks = false;
-
-    frames.append(Resolution::notFound());
-
-    // Call when the query is not resolved in the current module.
-    // It will enqueue the star resolution requests. Return "false" if the error occurs.
-    auto resolveNonLocal = [&](const ResolveQuery& query) -> bool {
-        // http://www.ecma-international.org/ecma-262/6.0/#sec-resolveexport
-        // section 15.2.1.16.3, step 6
-        // If the "default" name is not resolved in the current module, we need to throw an error and stop resolution immediately,
-        // Rationale to this error: A default export cannot be provided by an export *.
-        if (query.exportName == exec->propertyNames().defaultKeyword.impl())
-            return false;
-
-        // step 7, If exportStarSet contains module, then return null.
-        if (!starSet.add(query.moduleRecord).isNewEntry)
-            return true;
-
-        // Enqueue the task to gather the results of the stars.
-        // And append the new Resolution frame to gather the local result of the stars.
-        pendingTasks.append(Task { query, Type::GatherStars });
-        foundStarLinks = true;
-        frames.append(Resolution::notFound());
-
-
-        // Enqueue the tasks in reverse order.
-        for (auto iterator = query.moduleRecord->starExportEntries().rbegin(), end = query.moduleRecord->starExportEntries().rend(); iterator != end; ++iterator) {
-            const RefPtr<UniquedStringImpl>& starModuleName = *iterator;
-            JSModuleRecord* importedModuleRecord = query.moduleRecord->hostResolveImportedModule(exec, Identifier::fromUid(exec, starModuleName.get()));
-            pendingTasks.append(Task { ResolveQuery(importedModuleRecord, query.exportName.get()), Type::Query });
-        }
-        return true;
-    };
-
-    // Return the current resolution value of the top frame.
-    auto currentTop = [&] () -> Resolution& {
-        ASSERT(!frames.isEmpty());
-        return frames.last();
-    };
-
-    // Merge the given resolution to the current resolution value of the top frame.
-    // If there is ambiguity, return "false". When the "false" is returned, we should make the result "ambiguous".
-    auto mergeToCurrentTop = [&] (const Resolution& resolution) -> bool {
-        if (resolution.type == Resolution::Type::NotFound)
-            return true;
-
-        if (currentTop().type == Resolution::Type::NotFound) {
-            currentTop() = resolution;
-            return true;
-        }
-
-        if (currentTop().moduleRecord != resolution.moduleRecord || currentTop().localName != resolution.localName)
-            return false;
-
-        return true;
-    };
-
-    auto cacheResolutionForQuery = [] (const ResolveQuery& query, const Resolution& resolution) {
-        ASSERT(resolution.type == Resolution::Type::Resolved);
-        query.moduleRecord->cacheResolution(query.exportName.get(), resolution);
-    };
-
-    pendingTasks.append(Task { root, Type::Query });
-    while (!pendingTasks.isEmpty()) {
-        const Task task = pendingTasks.takeLast();
-        const ResolveQuery& query = task.query;
-
-        switch (task.type) {
-        case Type::Query: {
-            JSModuleRecord* moduleRecord = query.moduleRecord;
-
-            if (!resolveSet.add(task.query).isNewEntry)
-                continue;
-
-            //  5. Once we see star links, even if we have not yet traversed that star link path, we should disable caching.
-            if (!moduleRecord->starExportEntries().isEmpty())
-                foundStarLinks = true;
-
-            //  4. Once we follow star links, we should not retrieve the result from the cache and should not cache the result.
-            if (!foundStarLinks) {
-                if (std::optional<Resolution> cachedResolution = moduleRecord->tryGetCachedResolution(query.exportName.get())) {
-                    if (!mergeToCurrentTop(*cachedResolution))
-                        return Resolution::ambiguous();
-                    continue;
-                }
-            }
-
-            const std::optional<ExportEntry> optionalExportEntry = moduleRecord->tryGetExportEntry(query.exportName.get());
-            if (!optionalExportEntry) {
-                // If there is no matched exported binding in the current module,
-                // we need to look into the stars.
-                if (!resolveNonLocal(task.query))
-                    return Resolution::error();
-                continue;
-            }
-
-            const ExportEntry& exportEntry = *optionalExportEntry;
-            switch (exportEntry.type) {
-            case ExportEntry::Type::Local: {
-                ASSERT(!exportEntry.localName.isNull());
-                Resolution resolution { Resolution::Type::Resolved, moduleRecord, exportEntry.localName };
-                //  2. A module that has resolved a local binding is always cacheable.
-                cacheResolutionForQuery(query, resolution);
-                if (!mergeToCurrentTop(resolution))
-                    return Resolution::ambiguous();
-                continue;
-            }
-
-            case ExportEntry::Type::Indirect: {
-                JSModuleRecord* importedModuleRecord = moduleRecord->hostResolveImportedModule(exec, exportEntry.moduleName);
-
-                // When the imported module does not produce any resolved binding, we need to look into the stars in the *current*
-                // module. To do this, we append the `IndirectFallback` task to the task queue.
-                pendingTasks.append(Task { query, Type::IndirectFallback });
-                // And append the new Resolution frame to check the indirect export will be resolved or not.
-                frames.append(Resolution::notFound());
-                pendingTasks.append(Task { ResolveQuery(importedModuleRecord, exportEntry.importName), Type::Query });
-                continue;
-            }
-            }
-            break;
-        }
-
-        case Type::IndirectFallback: {
-            Resolution resolution = frames.takeLast();
-
-            if (resolution.type == Resolution::Type::NotFound) {
-                // Indirect export entry does not produce any resolved binding.
-                // So we will investigate the stars.
-                if (!resolveNonLocal(task.query))
-                    return Resolution::error();
-                continue;
-            }
-
-            ASSERT_WITH_MESSAGE(resolution.type == Resolution::Type::Resolved, "When we see Error and Ambiguous, we immediately return from this loop. So here, only Resolved comes.");
-
-            //  3. If we don't follow any star links during the resolution, we can see all the traced nodes are cacheable.
-            //  4. Once we follow star links, we should not retrieve the result from the cache and should not cache the result.
-            if (!foundStarLinks)
-                cacheResolutionForQuery(query, resolution);
-
-            // If indirect export entry produces Resolved, we should merge it to the upper frame.
-            // And do not investigate the stars of the current module.
-            if (!mergeToCurrentTop(resolution))
-                return Resolution::ambiguous();
-            break;
-        }
-
-        case Type::GatherStars: {
-            Resolution resolution = frames.takeLast();
-            ASSERT_WITH_MESSAGE(resolution.type == Resolution::Type::Resolved || resolution.type == Resolution::Type::NotFound, "When we see Error and Ambiguous, we immediately return from this loop. So here, only Resolved and NotFound comes.");
-
-            // Merge the star resolution to the upper frame.
-            if (!mergeToCurrentTop(resolution))
-                return Resolution::ambiguous();
-            break;
-        }
-        }
-    }
-
-    ASSERT(frames.size() == 1);
-    //  1. The starting point is always cacheable.
-    if (frames[0].type == Resolution::Type::Resolved)
-        cacheResolutionForQuery(root, frames[0]);
-    return frames[0];
-}
-
-auto JSModuleRecord::resolveExport(ExecState* exec, const Identifier& exportName) -> Resolution
+void JSModuleRecord::destroy(JSCell* cell)
 {
-    // Look up the cached resolution first before entering the resolving loop, since the loop setup takes some cost.
-    if (std::optional<Resolution> cachedResolution = tryGetCachedResolution(exportName.impl()))
-        return *cachedResolution;
-    return resolveExportImpl(exec, ResolveQuery(this, exportName.impl()));
+    JSModuleRecord* thisObject = jsCast<JSModuleRecord*>(cell);
+    thisObject->JSModuleRecord::~JSModuleRecord();
 }
 
-static void getExportedNames(ExecState* exec, JSModuleRecord* root, IdentifierSet& exportedNames)
+void JSModuleRecord::finishCreation(ExecState* exec, VM& vm)
 {
-    HashSet<JSModuleRecord*> exportStarSet;
-    Vector<JSModuleRecord*, 8> pendingModules;
-
-    pendingModules.append(root);
-
-    while (!pendingModules.isEmpty()) {
-        JSModuleRecord* moduleRecord = pendingModules.takeLast();
-        if (exportStarSet.contains(moduleRecord))
-            continue;
-        exportStarSet.add(moduleRecord);
-
-        for (const auto& pair : moduleRecord->exportEntries()) {
-            const JSModuleRecord::ExportEntry& exportEntry = pair.value;
-            if (moduleRecord == root || exec->propertyNames().defaultKeyword != exportEntry.exportName)
-                exportedNames.add(exportEntry.exportName.impl());
-        }
-
-        for (const auto& starModuleName : moduleRecord->starExportEntries()) {
-            JSModuleRecord* requestedModuleRecord = moduleRecord->hostResolveImportedModule(exec, Identifier::fromUid(exec, starModuleName.get()));
-            pendingModules.append(requestedModuleRecord);
-        }
-    }
+    Base::finishCreation(exec, vm);
+    ASSERT(inherits(info()));
 }
 
-JSModuleNamespaceObject* JSModuleRecord::getModuleNamespace(ExecState* exec)
+void JSModuleRecord::visitChildren(JSCell* cell, SlotVisitor& visitor)
 {
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    // http://www.ecma-international.org/ecma-262/6.0/#sec-getmodulenamespace
-    if (m_moduleNamespaceObject)
-        return m_moduleNamespaceObject.get();
-
-    JSGlobalObject* globalObject = exec->lexicalGlobalObject();
-    IdentifierSet exportedNames;
-    getExportedNames(exec, this, exportedNames);
-
-    IdentifierSet unambiguousNames;
-    for (auto& name : exportedNames) {
-        const JSModuleRecord::Resolution resolution = resolveExport(exec, Identifier::fromUid(exec, name.get()));
-        switch (resolution.type) {
-        case Resolution::Type::NotFound:
-            throwSyntaxError(exec, scope, makeString("Exported binding name '", String(name.get()), "' is not found."));
-            return nullptr;
-
-        case Resolution::Type::Error:
-            throwSyntaxError(exec, scope, makeString("Exported binding name 'default' cannot be resolved by star export entries."));
-            return nullptr;
-
-        case Resolution::Type::Ambiguous:
-            break;
-
-        case Resolution::Type::Resolved:
-            unambiguousNames.add(name);
-            break;
-        }
-    }
-
-    m_moduleNamespaceObject.set(vm, this, JSModuleNamespaceObject::create(exec, globalObject, globalObject->moduleNamespaceObjectStructure(), this, unambiguousNames));
-    return m_moduleNamespaceObject.get();
+    JSModuleRecord* thisObject = jsCast<JSModuleRecord*>(cell);
+    Base::visitChildren(thisObject, visitor);
+    visitor.append(&thisObject->m_moduleProgramExecutable);
 }
 
 void JSModuleRecord::link(ExecState* exec)
@@ -746,7 +102,7 @@ void JSModuleRecord::instantiateDeclarations(ExecState* exec, ModuleProgramExecu
     // Ensure all the indirect exports are correctly resolved to unique bindings.
     // Even if we avoided duplicate exports in the parser, still ambiguous exports occur due to the star export (`export * from "mod"`).
     // When we see this type of ambiguity for the indirect exports here, throw a syntax error.
-    for (const auto& pair : m_exportEntries) {
+    for (const auto& pair : exportEntries()) {
         const ExportEntry& exportEntry = pair.value;
         if (exportEntry.type == JSModuleRecord::ExportEntry::Type::Indirect) {
             Resolution resolution = resolveExport(exec, exportEntry.exportName);
@@ -773,9 +129,9 @@ void JSModuleRecord::instantiateDeclarations(ExecState* exec, ModuleProgramExecu
     // section 15.2.1.16.4 step 12.
     // Instantiate namespace objects and initialize the bindings with them if required.
     // And ensure that all the imports correctly resolved to unique bindings.
-    for (const auto& pair : m_importEntries) {
+    for (const auto& pair : importEntries()) {
         const ImportEntry& importEntry = pair.value;
-        JSModuleRecord* importedModule = hostResolveImportedModule(exec, importEntry.moduleRequest);
+        AbstractModuleRecord* importedModule = hostResolveImportedModule(exec, importEntry.moduleRequest);
         if (importEntry.isNamespace(vm)) {
             JSModuleNamespaceObject* namespaceObject = importedModule->getModuleNamespace(exec);
             RETURN_IF_EXCEPTION(scope, void());
@@ -807,7 +163,7 @@ void JSModuleRecord::instantiateDeclarations(ExecState* exec, ModuleProgramExecu
     // Module environment contains the heap allocated "var", "function", "let", "const", and "class".
     // When creating the environment, we initialized all the slots with empty, it's ok for lexical values.
     // But for "var" and "function", we should initialize it with undefined. They are contained in the declared variables.
-    for (const auto& variable : m_declaredVariables) {
+    for (const auto& variable : declaredVariables()) {
         SymbolTableEntry entry = symbolTable->get(variable.key.get());
         VarOffset offset = entry.varOffset();
         if (!offset.isStack()) {
@@ -850,47 +206,4 @@ JSValue JSModuleRecord::evaluate(ExecState* exec)
     return result;
 }
 
-static String printableName(const RefPtr<UniquedStringImpl>& uid)
-{
-    if (uid->isSymbol())
-        return uid.get();
-    return WTF::makeString("'", String(uid.get()), "'");
-}
-
-static String printableName(const Identifier& ident)
-{
-    return printableName(ident.impl());
-}
-
-void JSModuleRecord::dump()
-{
-    dataLog("\nAnalyzing ModuleRecord key(", printableName(m_moduleKey), ")\n");
-
-    dataLog("    Dependencies: ", m_requestedModules.size(), " modules\n");
-    for (const auto& moduleName : m_requestedModules)
-        dataLog("      module(", printableName(moduleName), ")\n");
-
-    dataLog("    Import: ", m_importEntries.size(), " entries\n");
-    for (const auto& pair : m_importEntries) {
-        const ImportEntry& importEntry = pair.value;
-        dataLog("      import(", printableName(importEntry.importName), "), local(", printableName(importEntry.localName), "), module(", printableName(importEntry.moduleRequest), ")\n");
-    }
-
-    dataLog("    Export: ", m_exportEntries.size(), " entries\n");
-    for (const auto& pair : m_exportEntries) {
-        const ExportEntry& exportEntry = pair.value;
-        switch (exportEntry.type) {
-        case ExportEntry::Type::Local:
-            dataLog("      [Local] ", "export(", printableName(exportEntry.exportName), "), local(", printableName(exportEntry.localName), ")\n");
-            break;
-
-        case ExportEntry::Type::Indirect:
-            dataLog("      [Indirect] ", "export(", printableName(exportEntry.exportName), "), import(", printableName(exportEntry.importName), "), module(", printableName(exportEntry.moduleName), ")\n");
-            break;
-        }
-    }
-    for (const auto& moduleName : m_starExportEntries)
-        dataLog("      [Star] module(", printableName(moduleName.get()), ")\n");
-}
-
 } // namespace JSC
index 88c9e78..af0161c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #pragma once
 
-#include "Identifier.h"
-#include "JSDestructibleObject.h"
-#include "SourceCode.h"
-#include "VariableEnvironment.h"
-#include <wtf/HashMap.h>
-#include <wtf/ListHashSet.h>
-#include <wtf/Optional.h>
+#include "AbstractModuleRecord.h"
 
 namespace JSC {
 
-class JSModuleNamespaceObject;
-class JSModuleEnvironment;
-class JSMap;
 class ModuleProgramExecutable;
 
 // Based on the Source Text Module Record
 // http://www.ecma-international.org/ecma-262/6.0/#sec-source-text-module-records
-class JSModuleRecord : public JSDestructibleObject {
+class JSModuleRecord : public AbstractModuleRecord {
     friend class LLIntOffsetsExtractor;
 public:
-    typedef JSDestructibleObject Base;
-
-    // https://tc39.github.io/ecma262/#sec-source-text-module-records
-    struct ExportEntry {
-        enum class Type {
-            Local,
-            Indirect
-        };
-
-        static ExportEntry createLocal(const Identifier& exportName, const Identifier& localName);
-        static ExportEntry createIndirect(const Identifier& exportName, const Identifier& importName, const Identifier& moduleName);
-
-        Type type;
-        Identifier exportName;
-        Identifier moduleName;
-        Identifier importName;
-        Identifier localName;
-    };
-
-    struct ImportEntry {
-        Identifier moduleRequest;
-        Identifier importName;
-        Identifier localName;
-
-        bool isNamespace(VM& vm) const
-        {
-            return importName == vm.propertyNames->timesIdentifier;
-        }
-    };
-
-    typedef WTF::ListHashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> OrderedIdentifierSet;
-    typedef HashMap<RefPtr<UniquedStringImpl>, ImportEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> ImportEntries;
-    typedef HashMap<RefPtr<UniquedStringImpl>, ExportEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> ExportEntries;
+    typedef AbstractModuleRecord Base;
 
     DECLARE_EXPORT_INFO;
 
-    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
-    {
-        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
-    }
-
-    static JSModuleRecord* create(ExecState* exec, VM& vm, Structure* structure, const Identifier& moduleKey, const SourceCode& sourceCode, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables)
-    {
-        JSModuleRecord* instance = new (NotNull, allocateCell<JSModuleRecord>(vm.heap)) JSModuleRecord(vm, structure, moduleKey, sourceCode, declaredVariables, lexicalVariables);
-        instance->finishCreation(exec, vm);
-        return instance;
-    }
-
-    void appendRequestedModule(const Identifier&);
-    void addStarExportEntry(const Identifier&);
-    void addImportEntry(const ImportEntry&);
-    void addExportEntry(const ExportEntry&);
-
-    std::optional<ImportEntry> tryGetImportEntry(UniquedStringImpl* localName);
-    std::optional<ExportEntry> tryGetExportEntry(UniquedStringImpl* exportName);
-
-    const SourceCode& sourceCode() const { return m_sourceCode; }
-    const Identifier& moduleKey() const { return m_moduleKey; }
-    const OrderedIdentifierSet& requestedModules() const { return m_requestedModules; }
-    const ExportEntries& exportEntries() const { return m_exportEntries; }
-    const ImportEntries& importEntries() const { return m_importEntries; }
-    const OrderedIdentifierSet& starExportEntries() const { return m_starExportEntries; }
-
-    const VariableEnvironment& declaredVariables() const { return m_declaredVariables; }
-    const VariableEnvironment& lexicalVariables() const { return m_lexicalVariables; }
-
-    void dump();
-
     JSModuleEnvironment* moduleEnvironment()
     {
         ASSERT(m_moduleEnvironment);
         return m_moduleEnvironment.get();
     }
+    static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
+    static JSModuleRecord* create(ExecState*, VM&, Structure*, const Identifier&, const SourceCode&, const VariableEnvironment&, const VariableEnvironment&);
 
     void link(ExecState*);
     JS_EXPORT_PRIVATE JSValue evaluate(ExecState*);
 
     ModuleProgramExecutable* moduleProgramExecutable() const { return m_moduleProgramExecutable.get(); }
 
-    struct Resolution {
-        enum class Type { Resolved, NotFound, Ambiguous, Error };
-
-        static Resolution notFound();
-        static Resolution error();
-        static Resolution ambiguous();
-
-        Type type;
-        JSModuleRecord* moduleRecord;
-        Identifier localName;
-    };
-
-    Resolution resolveExport(ExecState*, const Identifier& exportName);
-    Resolution resolveImport(ExecState*, const Identifier& localName);
-
-    JSModuleRecord* hostResolveImportedModule(ExecState*, const Identifier& moduleName);
-
 private:
-    JSModuleRecord(VM& vm, Structure* structure, const Identifier& moduleKey, const SourceCode& sourceCode, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables)
-        : Base(vm, structure)
-        , m_moduleKey(moduleKey)
-        , m_sourceCode(sourceCode)
-        , m_declaredVariables(declaredVariables)
-        , m_lexicalVariables(lexicalVariables)
-    {
-    }
+    JSModuleRecord(VM&, Structure*, const Identifier&, const SourceCode&, const VariableEnvironment&, const VariableEnvironment&);
 
     void finishCreation(ExecState*, VM&);
 
-    JSModuleNamespaceObject* getModuleNamespace(ExecState*);
-
     static void visitChildren(JSCell*, SlotVisitor&);
     static void destroy(JSCell*);
 
     void instantiateDeclarations(ExecState*, ModuleProgramExecutable*);
 
-    struct ResolveQuery;
-    static Resolution resolveExportImpl(ExecState*, const ResolveQuery&);
-    std::optional<Resolution> tryGetCachedResolution(UniquedStringImpl* exportName);
-    void cacheResolution(UniquedStringImpl* exportName, const Resolution&);
-
-    // The loader resolves the given module name to the module key. The module key is the unique value to represent this module.
-    Identifier m_moduleKey;
-
-    SourceCode m_sourceCode;
-
-    VariableEnvironment m_declaredVariables;
-    VariableEnvironment m_lexicalVariables;
-
-    // Currently, we don't keep the occurrence order of the import / export entries.
-    // So, we does not guarantee the order of the errors.
-    // e.g. The import declaration that occurr later than the another import declaration may
-    //      throw the error even if the former import declaration also has the invalid content.
-    //
-    //      import ... // (1) this has some invalid content.
-    //      import ... // (2) this also has some invalid content.
-    //
-    //      In the above case, (2) may throw the error earlier than (1)
-    //
-    // But, in all the cases, we will throw the syntax error. So except for the content of the syntax error,
-    // there are no difference.
-
-    // Map localName -> ImportEntry.
-    ImportEntries m_importEntries;
-
-    // Map exportName -> ExportEntry.
-    ExportEntries m_exportEntries;
-
-    // Save the occurrence order since resolveExport requires it.
-    OrderedIdentifierSet m_starExportEntries;
-
-    // Save the occurrence order since the module loader loads and runs the modules in this order.
-    // http://www.ecma-international.org/ecma-262/6.0/#sec-moduleevaluation
-    OrderedIdentifierSet m_requestedModules;
-
-    WriteBarrier<JSMap> m_dependenciesMap;
-
     WriteBarrier<ModuleProgramExecutable> m_moduleProgramExecutable;
-    WriteBarrier<JSModuleEnvironment> m_moduleEnvironment;
-    WriteBarrier<JSModuleNamespaceObject> m_moduleNamespaceObject;
-
-    // We assume that all the JSModuleRecord are retained by JSModuleLoader's registry.
-    // So here, we don't visit each object for GC. The resolution cache map caches the once
-    // looked up correctly resolved resolution, since (1) we rarely looked up the non-resolved one,
-    // and (2) if we cache all the attempts the size of the map becomes infinitely large.
-    typedef HashMap<RefPtr<UniquedStringImpl>, Resolution, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> Resolutions;
-    Resolutions m_resolutionCache;
 };
 
 } // namespace JSC
index 9387d2c..c890fb1 100644 (file)
 #include "config.h"
 #include "JSScope.h"
 
+#include "AbstractModuleRecord.h"
 #include "Exception.h"
 #include "JSGlobalObject.h"
 #include "JSLexicalEnvironment.h"
 #include "JSModuleEnvironment.h"
-#include "JSModuleRecord.h"
 #include "JSWithScope.h"
 #include "JSCInlines.h"
 #include "VariableEnvironment.h"
@@ -78,10 +78,10 @@ static inline bool abstractAccess(ExecState* exec, JSScope* scope, const Identif
 
         if (scope->type() == ModuleEnvironmentType) {
             JSModuleEnvironment* moduleEnvironment = jsCast<JSModuleEnvironment*>(scope);
-            JSModuleRecord* moduleRecord = moduleEnvironment->moduleRecord();
-            JSModuleRecord::Resolution resolution = moduleRecord->resolveImport(exec, ident);
-            if (resolution.type == JSModuleRecord::Resolution::Type::Resolved) {
-                JSModuleRecord* importedRecord = resolution.moduleRecord;
+            AbstractModuleRecord* moduleRecord = moduleEnvironment->moduleRecord();
+            AbstractModuleRecord::Resolution resolution = moduleRecord->resolveImport(exec, ident);
+            if (resolution.type == AbstractModuleRecord::Resolution::Type::Resolved) {
+                AbstractModuleRecord* importedRecord = resolution.moduleRecord;
                 JSModuleEnvironment* importedEnvironment = importedRecord->moduleEnvironment();
                 SymbolTable* symbolTable = importedEnvironment->symbolTable();
                 ConcurrentJSLocker locker(symbolTable->m_lock);
@@ -278,7 +278,7 @@ void JSScope::collectClosureVariablesUnderTDZ(JSScope* scope, VariableEnvironmen
             continue;
 
         if (scope->isModuleScope()) {
-            JSModuleRecord* moduleRecord = jsCast<JSModuleEnvironment*>(scope)->moduleRecord();
+            AbstractModuleRecord* moduleRecord = jsCast<JSModuleEnvironment*>(scope)->moduleRecord();
             for (const auto& pair : moduleRecord->importEntries())
                 result.add(pair.key);
         }
index 34e8b95..f91e63f 100644 (file)
@@ -72,6 +72,7 @@
 #include "JSPromiseDeferred.h"
 #include "JSPropertyNameEnumerator.h"
 #include "JSTemplateRegistryKey.h"
+#include "JSWebAssembly.h"
 #include "JSWithScope.h"
 #include "LLIntData.h"
 #include "Lexer.h"
@@ -259,6 +260,7 @@ VM::VM(VMType vmType, HeapType heapType)
     functionCodeBlockStructure.set(*this, FunctionCodeBlock::createStructure(*this, 0, jsNull()));
 #if ENABLE(WEBASSEMBLY)
     webAssemblyCodeBlockStructure.set(*this, WebAssemblyCodeBlock::createStructure(*this, 0, jsNull()));
+    webAssemblyFunctionCellStructure.set(*this, WebAssemblyFunctionCell::createStructure(*this, 0, jsNull()));
 #endif
     hashMapBucketSetStructure.set(*this, HashMapBucket<HashMapBucketDataKey>::createStructure(*this, 0, jsNull()));
     hashMapBucketMapStructure.set(*this, HashMapBucket<HashMapBucketDataKeyValue>::createStructure(*this, 0, jsNull()));
index c6d2684..af3614b 100644 (file)
@@ -310,6 +310,7 @@ public:
     Strong<Structure> functionExecutableStructure;
 #if ENABLE(WEBASSEMBLY)
     Strong<Structure> webAssemblyExecutableStructure;
+    Strong<Structure> webAssemblyFunctionCellStructure;
 #endif
     Strong<Structure> moduleProgramExecutableStructure;
     Strong<Structure> regExpStructure;
index e71a15a..dba5a75 100644 (file)
 #include "js/JSWebAssemblyTable.h"
 #include "js/WebAssemblyCompileErrorConstructor.h"
 #include "js/WebAssemblyCompileErrorPrototype.h"
+#include "js/WebAssemblyFunction.h"
+#include "js/WebAssemblyFunctionCell.h"
 #include "js/WebAssemblyInstanceConstructor.h"
 #include "js/WebAssemblyInstancePrototype.h"
 #include "js/WebAssemblyMemoryConstructor.h"
 #include "js/WebAssemblyMemoryPrototype.h"
 #include "js/WebAssemblyModuleConstructor.h"
 #include "js/WebAssemblyModulePrototype.h"
+#include "js/WebAssemblyModuleRecord.h"
 #include "js/WebAssemblyPrototype.h"
 #include "js/WebAssemblyRuntimeErrorConstructor.h"
 #include "js/WebAssemblyRuntimeErrorPrototype.h"
index 499644c..29f0603 100644 (file)
@@ -30,8 +30,8 @@
 #include "B3Compilation.h"
 #include "B3Type.h"
 #include "CodeLocation.h"
+#include "Identifier.h"
 #include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
 
 namespace JSC {
 
@@ -115,8 +115,8 @@ struct Signature {
 };
     
 struct Import {
-    String module;
-    String field;
+    Identifier module;
+    Identifier field;
     External::Kind kind;
     union {
         Signature* functionSignature;
@@ -135,10 +135,10 @@ struct FunctionInformation {
 class Memory;
 
 struct Export {
-    String field;
+    Identifier field;
     External::Kind kind;
     union {
-        Signature* functionSignature;
+        uint32_t functionIndex;
         // FIXME implement Table https://bugs.webkit.org/show_bug.cgi?id=164135
         // FIXME implement Memory https://bugs.webkit.org/show_bug.cgi?id=164134
         // FIXME implement Global https://bugs.webkit.org/show_bug.cgi?id=164133
@@ -155,9 +155,6 @@ struct ModuleInformation {
     ~ModuleInformation();
 };
 
-struct FunctionCompilation;
-typedef Vector<std::unique_ptr<FunctionCompilation>> CompiledFunctions;
-
 struct UnlinkedCall {
     CodeLocationCall callLocation;
     size_t functionIndex;
@@ -169,6 +166,8 @@ struct FunctionCompilation {
     std::unique_ptr<B3::Compilation> jsEntryPoint;
 };
 
+typedef Vector<std::unique_ptr<FunctionCompilation>> CompiledFunctions;
+
 } } // namespace JSC::Wasm
 
 #endif // ENABLE(WEBASSEMBLY)
index a0a0843..61c3920 100644 (file)
@@ -28,6 +28,7 @@
 
 #if ENABLE(WEBASSEMBLY)
 
+#include "IdentifierInlines.h"
 #include "WasmFormat.h"
 #include "WasmMemory.h"
 #include "WasmOps.h"
@@ -123,7 +124,8 @@ bool ModuleParser::parse()
                 m_errorMessage = "couldn't parse section " #NAME ": " DESCRIPTION; \
                 return false; \
             } \
-        } break;
+            break; \
+        }
         FOR_EACH_WASM_SECTION(WASM_SECTION_PARSE)
 #undef WASM_SECTION_PARSE
 
@@ -167,7 +169,7 @@ bool ModuleParser::parseType()
         int8_t type;
         if (!parseInt7(type))
             return false;
-        if (type != -0x20) // Function type constant.
+        if (type != -0x20) // Function type constant. FIXME auto-generate from JSON file.
             return false;
 
         if (verbose)
@@ -221,41 +223,49 @@ bool ModuleParser::parseImport()
         return false;
 
     for (uint32_t importNumber = 0; importNumber != importCount; ++importNumber) {
-        Import i;
+        Import imp;
         uint32_t moduleLen;
         uint32_t fieldLen;
         if (!parseVarUInt32(moduleLen))
             return false;
-        if (!consumeUTF8String(i.module, moduleLen))
+        String moduleString;
+        if (!consumeUTF8String(moduleString, moduleLen))
             return false;
+        imp.module = Identifier::fromString(m_vm, moduleString);
         if (!parseVarUInt32(fieldLen))
             return false;
-        if (!consumeUTF8String(i.field, fieldLen))
+        String fieldString;
+        if (!consumeUTF8String(fieldString, fieldLen))
             return false;
-        if (!parseExternalKind(i.kind))
+        imp.field = Identifier::fromString(m_vm, fieldString);
+        if (!parseExternalKind(imp.kind))
             return false;
-        switch (i.kind) {
+        switch (imp.kind) {
         case External::Function: {
             uint32_t functionSignatureIndex;
             if (!parseVarUInt32(functionSignatureIndex))
                 return false;
-            if (functionSignatureIndex > m_module->signatures.size())
+            if (functionSignatureIndex >= m_module->signatures.size())
                 return false;
-            i.functionSignature = &m_module->signatures[functionSignatureIndex];
-        } break;
-        case External::Table:
+            imp.functionSignature = &m_module->signatures[functionSignatureIndex];
+            break;
+        }
+        case External::Table: {
             // FIXME https://bugs.webkit.org/show_bug.cgi?id=164135
             break;
-        case External::Memory:
+        }
+        case External::Memory: {
             // FIXME https://bugs.webkit.org/show_bug.cgi?id=164134
             break;
-        case External::Global:
+        }
+        case External::Global: {
             // FIXME https://bugs.webkit.org/show_bug.cgi?id=164133
             // In the MVP, only immutable global variables can be imported.
             break;
         }
+        }
 
-        m_module->imports.uncheckedAppend(i);
+        m_module->imports.uncheckedAppend(imp);
     }
 
     return true;
@@ -277,7 +287,10 @@ bool ModuleParser::parseFunction()
         if (typeNumber >= m_module->signatures.size())
             return false;
 
-        m_module->functions.uncheckedAppend({ &m_module->signatures[typeNumber], 0, 0 });
+        // The Code section fixes up start and end.
+        size_t start = 0;
+        size_t end = 0;
+        m_module->functions.uncheckedAppend({ &m_module->signatures[typeNumber], start, end });
     }
 
     return true;
@@ -339,36 +352,40 @@ bool ModuleParser::parseExport()
         return false;
 
     for (uint32_t exportNumber = 0; exportNumber != exportCount; ++exportNumber) {
-        Export e;
+        Export exp;
         uint32_t fieldLen;
         if (!parseVarUInt32(fieldLen))
             return false;
-        if (!consumeUTF8String(e.field, fieldLen))
+        String fieldString;
+        if (!consumeUTF8String(fieldString, fieldLen))
             return false;
-        if (!parseExternalKind(e.kind))
+        exp.field = Identifier::fromString(m_vm, fieldString);
+        if (!parseExternalKind(exp.kind))
             return false;
-        switch (e.kind) {
+        switch (exp.kind) {
         case External::Function: {
-            uint32_t functionSignatureIndex;
-            if (!parseVarUInt32(functionSignatureIndex))
+            if (!parseVarUInt32(exp.functionIndex))
                 return false;
-            if (functionSignatureIndex >= m_module->signatures.size())
+            if (exp.functionIndex >= m_module->functions.size())
                 return false;
-            e.functionSignature = &m_module->signatures[functionSignatureIndex];
-        } break;
-        case External::Table:
+            break;
+        }
+        case External::Table: {
             // FIXME https://bugs.webkit.org/show_bug.cgi?id=164135
             break;
-        case External::Memory:
+        }
+        case External::Memory: {
             // FIXME https://bugs.webkit.org/show_bug.cgi?id=164134
             break;
-        case External::Global:
+        }
+        case External::Global: {
             // FIXME https://bugs.webkit.org/show_bug.cgi?id=164133
             // In the MVP, only immutable global variables can be exported.
             break;
         }
+        }
 
-        m_module->exports.uncheckedAppend(e);
+        m_module->exports.uncheckedAppend(exp);
     }
 
     return true;
index 899bf21..2583315 100644 (file)
@@ -39,12 +39,13 @@ public:
 
     static const unsigned magicNumber = 0xc;
 
-    ModuleParser(const uint8_t* sourceBuffer, size_t sourceLength)
+    ModuleParser(VM* vm, const uint8_t* sourceBuffer, size_t sourceLength)
         : Parser(sourceBuffer, sourceLength)
+        , m_vm(vm)
     {
     }
-    ModuleParser(const Vector<uint8_t>& sourceBuffer)
-        : Parser(sourceBuffer.data(), sourceBuffer.size())
+    ModuleParser(VM* vm, const Vector<uint8_t>& sourceBuffer)
+        : ModuleParser(vm, sourceBuffer.data(), sourceBuffer.size())
     {
     }
 
@@ -67,6 +68,7 @@ private:
     FOR_EACH_WASM_SECTION(WASM_SECTION_DECLARE_PARSER)
 #undef WASM_SECTION_DECLARE_PARSER
 
+    VM* m_vm;
     std::unique_ptr<ModuleInformation> m_module;
     bool m_failed { true };
     String m_errorMessage;
index 9f8e0fa..1e8ea4a 100644 (file)
@@ -58,7 +58,7 @@ void Plan::run()
     if (verbose)
         dataLogLn("Starting plan.");
     {
-        ModuleParser moduleParser(m_source, m_sourceLength);
+        ModuleParser moduleParser(m_vm, m_source, m_sourceLength);
         if (!moduleParser.parse()) {
             if (verbose)
                 dataLogLn("Parsing module failed: ", moduleParser.errorMessage());
index 1d7bc33..ce3fdc7 100644 (file)
 
 #if ENABLE(WEBASSEMBLY)
 
+#include "AbstractModuleRecord.h"
 #include "JSCInlines.h"
+#include "JSModuleEnvironment.h"
 #include "JSModuleNamespaceObject.h"
+#include "JSWebAssemblyModule.h"
 
 namespace JSC {
 
-JSWebAssemblyInstance* JSWebAssemblyInstance::create(VM& vm, Structure* structure, JSModuleNamespaceObject* moduleNamespaceObject)
+JSWebAssemblyInstance* JSWebAssemblyInstance::create(VM& vm, Structure* structure, JSWebAssemblyModule* module, JSModuleNamespaceObject* moduleNamespaceObject)
 {
     auto* instance = new (NotNull, allocateCell<JSWebAssemblyInstance>(vm.heap)) JSWebAssemblyInstance(vm, structure);
-    instance->finishCreation(vm, moduleNamespaceObject);
+    instance->finishCreation(vm, module, moduleNamespaceObject);
     return instance;
 }
 
@@ -50,11 +53,12 @@ JSWebAssemblyInstance::JSWebAssemblyInstance(VM& vm, Structure* structure)
 {
 }
 
-void JSWebAssemblyInstance::finishCreation(VM& vm, JSModuleNamespaceObject* moduleNamespaceObject)
+void JSWebAssemblyInstance::finishCreation(VM& vm, JSWebAssemblyModule* module, JSModuleNamespaceObject* moduleNamespaceObject)
 {
     Base::finishCreation(vm);
+    m_module.set(vm, this, module);
     m_moduleNamespaceObject.set(vm, this, moduleNamespaceObject);
-    putDirectWithoutTransition(vm, Identifier::fromString(&vm, "exports"), m_moduleNamespaceObject.get(), None);
+    // FIXME this should put the module namespace object onto the exports object, instead of moduleEnvironment in WebAssemblyInstanceConstructor. https://bugs.webkit.org/show_bug.cgi?id=165121
     ASSERT(inherits(info()));
 }
 
@@ -69,6 +73,7 @@ void JSWebAssemblyInstance::visitChildren(JSCell* cell, SlotVisitor& visitor)
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
 
     Base::visitChildren(thisObject, visitor);
+    visitor.append(&thisObject->m_module);
     visitor.append(&thisObject->m_moduleNamespaceObject);
 }
 
index 87acc97..b6a3c3a 100644 (file)
 #include "JSObject.h"
 
 namespace JSC {
-    
+
 class JSModuleNamespaceObject;
+class JSWebAssemblyModule;
 
 class JSWebAssemblyInstance : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
 
-    static JSWebAssemblyInstance* create(VM&, Structure*, JSModuleNamespaceObject*);
+
+    static JSWebAssemblyInstance* create(VM&, Structure*, JSWebAssemblyModule*, JSModuleNamespaceObject*);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_INFO;
 
+    JSWebAssemblyModule* module()
+    {
+        ASSERT(m_module);
+        return m_module.get();
+    }
+
 protected:
     JSWebAssemblyInstance(VM&, Structure*);
-    void finishCreation(VM&, JSModuleNamespaceObject*);
+    void finishCreation(VM&, JSWebAssemblyModule*, JSModuleNamespaceObject*);
     static void destroy(JSCell*);
     static void visitChildren(JSCell*, SlotVisitor&);
 
 private:
+    WriteBarrier<JSWebAssemblyModule> m_module;
     WriteBarrier<JSModuleNamespaceObject> m_moduleNamespaceObject;
 };
 
index d6ba381..88feb98 100644 (file)
 
 namespace JSC {
 
-JSWebAssemblyModule* JSWebAssemblyModule::create(VM& vm, Structure* structure, std::unique_ptr<Wasm::ModuleInformation>& moduleInformation, Wasm::CompiledFunctions& compiledFunctions)
+JSWebAssemblyModule* JSWebAssemblyModule::create(VM& vm, Structure* structure, std::unique_ptr<Wasm::ModuleInformation>& moduleInformation, Wasm::CompiledFunctions& compiledFunctions, SymbolTable* exportSymbolTable)
 {
     auto* instance = new (NotNull, allocateCell<JSWebAssemblyModule>(vm.heap)) JSWebAssemblyModule(vm, structure, moduleInformation, compiledFunctions);
-    instance->finishCreation(vm);
+    instance->finishCreation(vm, exportSymbolTable);
     return instance;
 }
 
@@ -54,10 +54,11 @@ JSWebAssemblyModule::JSWebAssemblyModule(VM& vm, Structure* structure, std::uniq
 {
 }
 
-void JSWebAssemblyModule::finishCreation(VM& vm)
+void JSWebAssemblyModule::finishCreation(VM& vm, SymbolTable* exportSymbolTable)
 {
     Base::finishCreation(vm);
     ASSERT(inherits(info()));
+    m_exportSymbolTable.set(vm, this, exportSymbolTable);
 }
 
 void JSWebAssemblyModule::destroy(JSCell* cell)
@@ -71,6 +72,7 @@ void JSWebAssemblyModule::visitChildren(JSCell* cell, SlotVisitor& visitor)
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
 
     Base::visitChildren(thisObject, visitor);
+    visitor.append(&thisObject->m_exportSymbolTable);
 }
 
 const ClassInfo JSWebAssemblyModule::s_info = { "WebAssembly.Module", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyModule) };
index 9da2122..3134673 100644 (file)
 
 namespace JSC {
 
+class SymbolTable;
+
 class JSWebAssemblyModule : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
 
-    static JSWebAssemblyModule* create(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&, Wasm::CompiledFunctions&);
+    static JSWebAssemblyModule* create(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&, Wasm::CompiledFunctions&, SymbolTable*);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_INFO;
 
-    const Wasm::ModuleInformation* moduleInformation() const
-    {
-        return m_moduleInformation.get();
-    }
+    const Wasm::ModuleInformation& moduleInformation() const { return *m_moduleInformation.get(); }
+    const Wasm::CompiledFunctions& compiledFunctions() const { return m_compiledFunctions; }
+    SymbolTable* exportSymbolTable() const { return m_exportSymbolTable.get(); }
 
 protected:
     JSWebAssemblyModule(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&, Wasm::CompiledFunctions&);
-    void finishCreation(VM&);
+    void finishCreation(VM&, SymbolTable*);
     static void destroy(JSCell*);
     static void visitChildren(JSCell*, SlotVisitor&);
 private:
     std::unique_ptr<Wasm::ModuleInformation> m_moduleInformation;
     Wasm::CompiledFunctions m_compiledFunctions;
+    WriteBarrier<SymbolTable> m_exportSymbolTable;
 };
 
 } // namespace JSC
diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp
new file mode 100644 (file)
index 0000000..9ff6c2a
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WebAssemblyFunction.h"
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "B3Compilation.h"
+#include "JSCInlines.h"
+#include "JSFunctionInlines.h"
+#include "JSObject.h"
+#include "JSWebAssemblyInstance.h"
+#include "LLintThunks.h"
+#include "ProtoCallFrame.h"
+#include "VM.h"
+#include "WasmFormat.h"
+#include "WebAssemblyFunctionCell.h"
+
+namespace JSC {
+
+const ClassInfo WebAssemblyFunction::s_info = { "WebAssemblyFunction", &Base::s_info, nullptr, CREATE_METHOD_TABLE(WebAssemblyFunction) };
+
+static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* state)
+{
+    auto& vm = state->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    WebAssemblyFunction* callee = jsDynamicCast<WebAssemblyFunction*>(state->callee());
+    if (!callee)
+        return JSValue::encode(throwException(state, scope, createTypeError(state, "expected a WebAssembly function", defaultSourceAppender, runtimeTypeForValue(state->callee()))));
+    const CallableWebAssemblyFunction& callable = callee->webAssemblyFunctionCell()->function();
+    const B3::Compilation* jsEntryPoint = callable.jsEntryPoint;
+    const Wasm::Signature* signature = callable.signature;
+
+    // FIXME is this the right behavior? https://bugs.webkit.org/show_bug.cgi?id=164876
+    if (state->argumentCount() != signature->arguments.size())
+        return JSValue::encode(throwException(state, scope, createNotEnoughArgumentsError(state, defaultSourceAppender)));
+
+    // FIXME is this boxing correct? https://bugs.webkit.org/show_bug.cgi?id=164876
+    Vector<JSValue> boxedArgs;
+    for (unsigned argIndex = 0; argIndex < state->argumentCount(); ++argIndex) {
+        JSValue arg = state->uncheckedArgument(argIndex);
+        switch (signature->arguments[argIndex]) {
+        case Wasm::Void:
+        case Wasm::I64:
+            RELEASE_ASSERT_NOT_REACHED();
+            break;
+        case Wasm::I32:
+            arg = JSValue::decode(arg.toInt32(state));
+            break;
+        case Wasm::F32:
+            arg = JSValue::decode(bitwise_cast<uint32_t>(arg.toFloat(state)));
+            break;
+        case Wasm::F64:
+            arg = JSValue::decode(bitwise_cast<uint64_t>(arg.toNumber(state)));
+            break;
+        }
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
+        boxedArgs.append(arg);
+    }
+
+    JSValue firstArgument = JSValue();
+    int argCount = 1;
+    JSValue* remainingArgs = nullptr;
+    if (boxedArgs.size()) {
+        remainingArgs = boxedArgs.data();
+        firstArgument = *remainingArgs;
+        remainingArgs++;
+        argCount = boxedArgs.size();
+    }
+
+    ProtoCallFrame protoCallFrame;
+    protoCallFrame.init(nullptr, callee, firstArgument, argCount, remainingArgs);
+    
+    EncodedJSValue rawResult = vmEntryToWasm(jsEntryPoint->code().executableAddress(), &vm, &protoCallFrame);
+    // FIXME is this correct? https://bugs.webkit.org/show_bug.cgi?id=164876
+    switch (signature->returnType) {
+    case Wasm::Void:
+        return JSValue::encode(jsUndefined());
+    case Wasm::I64:
+        RELEASE_ASSERT_NOT_REACHED();
+    case Wasm::I32:
+        return JSValue::encode(JSValue(static_cast<int32_t>(rawResult)));
+    case Wasm::F32:
+        return JSValue::encode(JSValue(bitwise_cast<float>(static_cast<int32_t>(rawResult))));
+    case Wasm::F64:
+        return JSValue::encode(JSValue(bitwise_cast<double>(rawResult)));
+    }
+
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+WebAssemblyFunction* WebAssemblyFunction::create(VM& vm, JSGlobalObject* globalObject, int length, const String& name, JSWebAssemblyInstance* instance, CallableWebAssemblyFunction&& callable)
+{
+    NativeExecutable* executable = vm.getHostFunction(callWebAssemblyFunction, NoIntrinsic, callHostFunctionAsConstructor, nullptr, name);
+    WebAssemblyFunctionCell* functionCell = WebAssemblyFunctionCell::create(vm, WTFMove(callable));
+    Structure* structure = globalObject->webAssemblyFunctionStructure();
+    WebAssemblyFunction* function = new (NotNull, allocateCell<WebAssemblyFunction>(vm.heap)) WebAssemblyFunction(vm, globalObject, structure);
+    function->finishCreation(vm, executable, length, name, instance, functionCell);
+    return function;
+}
+
+Structure* WebAssemblyFunction::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+{
+    ASSERT(globalObject);
+    return Structure::create(vm, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), info());
+}
+
+WebAssemblyFunction::WebAssemblyFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure)
+    : Base(vm, globalObject, structure)
+{
+}
+
+void WebAssemblyFunction::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+    WebAssemblyFunction* thisObject = jsCast<WebAssemblyFunction*>(cell);
+    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+    Base::visitChildren(thisObject, visitor);
+    visitor.append(&thisObject->m_instance);
+    visitor.append(&thisObject->m_functionCell);
+}
+
+void WebAssemblyFunction::finishCreation(VM& vm, NativeExecutable* executable, int length, const String& name, JSWebAssemblyInstance* instance, WebAssemblyFunctionCell* functionCell)
+{
+    Base::finishCreation(vm, executable, length, name);
+    ASSERT(inherits(info()));
+    m_instance.set(vm, this, instance);
+    m_functionCell.set(vm, this, functionCell);
+}
+
+} // namespace JSC
+
+#endif // ENABLE(WEBASSEMBLY)
diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.h b/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.h
new file mode 100644 (file)
index 0000000..9c0eda6
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "JSFunction.h"
+#include <wtf/Noncopyable.h>
+
+namespace JSC {
+
+class JSGlobalObject;
+class WebAssemblyFunctionCell;
+class WebAssemblyInstance;
+
+namespace B3 {
+class Compilation;
+}
+
+namespace Wasm {
+struct Signature;
+}
+
+class CallableWebAssemblyFunction {
+    WTF_MAKE_NONCOPYABLE(CallableWebAssemblyFunction);
+    CallableWebAssemblyFunction() = delete;
+
+public:
+    CallableWebAssemblyFunction(CallableWebAssemblyFunction&&) = default;
+
+    const B3::Compilation* jsEntryPoint;
+    const Wasm::Signature* signature;
+    CallableWebAssemblyFunction(const B3::Compilation* jsEntryPoint, const Wasm::Signature* signature)
+        : jsEntryPoint(jsEntryPoint)
+        , signature(signature)
+    {
+    }
+};
+
+class WebAssemblyFunction : public JSFunction {
+public:
+    typedef JSFunction Base;
+
+    const static unsigned StructureFlags = Base::StructureFlags;
+
+    DECLARE_EXPORT_INFO;
+
+    JS_EXPORT_PRIVATE static WebAssemblyFunction* create(VM&, JSGlobalObject*, int, const String&, JSWebAssemblyInstance*, CallableWebAssemblyFunction&&);
+    static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
+
+    const WebAssemblyFunctionCell* webAssemblyFunctionCell() const { return m_functionCell.get(); }
+
+protected:
+    static void visitChildren(JSCell*, SlotVisitor&);
+
+    void finishCreation(VM&, NativeExecutable*, int length, const String& name, JSWebAssemblyInstance*, WebAssemblyFunctionCell*);
+
+private:
+    WebAssemblyFunction(VM&, JSGlobalObject*, Structure*);
+
+    WriteBarrier<JSWebAssemblyInstance> m_instance;
+    WriteBarrier<WebAssemblyFunctionCell> m_functionCell;
+};
+
+} // namespace JSC
+
+#endif // ENABLE(WEBASSEMBLY)
diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyFunctionCell.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyFunctionCell.cpp
new file mode 100644 (file)
index 0000000..3d27115
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WebAssemblyFunctionCell.h"
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "JSCInlines.h"
+
+namespace JSC {
+
+const ClassInfo WebAssemblyFunctionCell::s_info = { "WebAssemblyFunctionCell", nullptr, nullptr, CREATE_METHOD_TABLE(WebAssemblyFunctionCell) };
+
+WebAssemblyFunctionCell* WebAssemblyFunctionCell::create(VM& vm, CallableWebAssemblyFunction&& callable)
+{
+    WebAssemblyFunctionCell* nativeFunction = new (NotNull, allocateCell<WebAssemblyFunctionCell>(vm.heap)) WebAssemblyFunctionCell(vm, WTFMove(callable));
+    nativeFunction->finishCreation(vm);
+    return nativeFunction;
+}
+
+WebAssemblyFunctionCell::WebAssemblyFunctionCell(VM& vm, CallableWebAssemblyFunction&& callable)
+    : Base(vm, vm.webAssemblyFunctionCellStructure.get())
+    , m_function(WTFMove(callable))
+{
+}
+
+void WebAssemblyFunctionCell::destroy(JSCell* cell)
+{
+    WebAssemblyFunctionCell* nativeFunction = static_cast<WebAssemblyFunctionCell*>(cell);
+    nativeFunction->WebAssemblyFunctionCell::~WebAssemblyFunctionCell();
+}
+
+Structure* WebAssemblyFunctionCell::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+{
+    return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info());
+}
+
+}
+
+#endif // ENABLE(WEBASSEMBLY)
diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyFunctionCell.h b/Source/JavaScriptCore/wasm/js/WebAssemblyFunctionCell.h
new file mode 100644 (file)
index 0000000..f177425
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "JSCell.h"
+#include "WebAssemblyFunction.h"
+
+namespace JSC {
+
+class WebAssemblyFunctionCell : public JSCell {
+public:
+    typedef JSCell Base;
+    static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
+    static const bool needsDestruction = true;
+
+    static WebAssemblyFunctionCell* create(VM&, CallableWebAssemblyFunction&&);
+    static void destroy(JSCell*);
+    static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
+
+    DECLARE_INFO;
+
+    const CallableWebAssemblyFunction& function() const { return m_function; }
+
+private:
+    WebAssemblyFunctionCell(VM&, CallableWebAssemblyFunction&&);
+
+    CallableWebAssemblyFunction m_function;
+};
+
+} // namespace JSC
+
+#endif // ENABLE(WEBASSEMBLY)
index e51dee4..88cf973 100644 (file)
 
 #include "FunctionPrototype.h"
 #include "JSCInlines.h"
-#include "JSModuleNamespaceObject.h"
-#include "JSModuleRecord.h"
+#include "JSModuleEnvironment.h"
 #include "JSWebAssemblyInstance.h"
 #include "JSWebAssemblyModule.h"
 #include "WebAssemblyInstancePrototype.h"
+#include "WebAssemblyModuleRecord.h"
 
 #include "WebAssemblyInstanceConstructor.lut.h"
 
 namespace JSC {
 
+static const bool verbose = false;
+
 const ClassInfo WebAssemblyInstanceConstructor::s_info = { "Function", &Base::s_info, &constructorTableWebAssemblyInstance, CREATE_METHOD_TABLE(WebAssemblyInstanceConstructor) };
 
 /* Source for WebAssemblyInstanceConstructor.lut.h
@@ -54,9 +56,10 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* st
     auto* globalObject = state->lexicalGlobalObject();
 
     // If moduleObject is not a WebAssembly.Module instance, a TypeError is thrown.
-    JSWebAssemblyModule* module = jsDynamicCast<JSWebAssemblyModule*>(state->argument(0));
-    if (!module)
+    JSWebAssemblyModule* jsModule = jsDynamicCast<JSWebAssemblyModule*>(state->argument(0));
+    if (!jsModule)
         return JSValue::encode(throwException(state, scope, createTypeError(state, ASCIILiteral("first argument to WebAssembly.Instance must be a WebAssembly.Module"), defaultSourceAppender, runtimeTypeForValue(state->argument(0)))));
+    const Wasm::ModuleInformation& moduleInformation = jsModule->moduleInformation();
 
     // If the importObject parameter is not undefined and Type(importObject) is not Object, a TypeError is thrown.
     JSValue importArgument = state->argument(1);
@@ -65,29 +68,33 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* st
         return JSValue::encode(throwException(state, scope, createTypeError(state, ASCIILiteral("second argument to WebAssembly.Instance must be undefined or an Object"), defaultSourceAppender, runtimeTypeForValue(importArgument))));
 
     // If the list of module.imports is not empty and Type(importObject) is not Object, a TypeError is thrown.
-    if (module->moduleInformation()->imports.size() && !importObject)
+    if (moduleInformation.imports.size() && !importObject)
         return JSValue::encode(throwException(state, scope, createTypeError(state, ASCIILiteral("second argument to WebAssembly.Instance must be Object because the WebAssembly.Module has imports"), defaultSourceAppender, runtimeTypeForValue(importArgument))));
 
-    // FIXME String things from https://bugs.webkit.org/show_bug.cgi?id=164023
-    // Let exports be a list of (string, JS value) pairs that is mapped from each external value e in instance.exports as follows:
-    IdentifierSet instanceExports;
-    for (const auto& name : instanceExports) {
-        // FIXME validate according to Module.Instance spec.
-        (void)name;
-    }
-    Identifier moduleKey;
+    Identifier moduleKey = Identifier::fromUid(PrivateName(PrivateName::Description, "WebAssemblyInstance"));
     SourceCode sourceCode;
     VariableEnvironment declaredVariables;
     VariableEnvironment lexicalVariables;
-    auto* moduleRecord = JSModuleRecord::create(state, vm, globalObject->moduleRecordStructure(), moduleKey, sourceCode, declaredVariables, lexicalVariables);
+    WebAssemblyModuleRecord* moduleRecord = WebAssemblyModuleRecord::create(state, vm, globalObject->webAssemblyModuleRecordStructure(), moduleKey, sourceCode, declaredVariables, lexicalVariables);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    auto* moduleNamespaceObject = JSModuleNamespaceObject::create(state, globalObject, globalObject->moduleNamespaceObjectStructure(), moduleRecord, instanceExports);
+
+    Structure* instanceStructure = InternalFunction::createSubclassStructure(state, state->newTarget(), globalObject->WebAssemblyInstanceStructure());
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    auto* structure = InternalFunction::createSubclassStructure(state, state->newTarget(), globalObject->WebAssemblyInstanceStructure());
+    JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, instanceStructure, jsModule, moduleRecord->getModuleNamespace(state));
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+    moduleRecord->link(state, instance);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    if (verbose)
+        moduleRecord->dump();
+    // FIXME the following should be in JSWebAssemblyInstance instead of here. https://bugs.webkit.org/show_bug.cgi?id=165121
+    instance->putDirect(vm, Identifier::fromString(&vm, "exports"), JSValue(moduleRecord->moduleEnvironment()), None);
+    JSValue startResult = moduleRecord->evaluate(state);
+    UNUSED_PARAM(startResult);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    return JSValue::encode(JSWebAssemblyInstance::create(vm, structure, moduleNamespaceObject));
+    return JSValue::encode(instance);
 }
 
 static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyInstance(ExecState* state)
index f03a0a8..4496d3a 100644 (file)
 #include "JSTypedArrays.h"
 #include "JSWebAssemblyCompileError.h"
 #include "JSWebAssemblyModule.h"
+#include "SymbolTable.h"
 #include "WasmPlan.h"
 #include "WebAssemblyModulePrototype.h"
+#include <wtf/StdLibExtras.h>
 
 #include "WebAssemblyModuleConstructor.lut.h"
 
@@ -78,7 +80,14 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyModule(ExecState* stat
     auto* structure = InternalFunction::createSubclassStructure(state, state->newTarget(), asInternalFunction(state->callee())->globalObject()->WebAssemblyModuleStructure());
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    return JSValue::encode(JSWebAssemblyModule::create(vm, structure, plan.getModuleInformation(), plan.getCompiledFunctions()));
+    // The export symbol table is the same for all Instances of a Module.
+    SymbolTable* exportSymbolTable = SymbolTable::create(vm);
+    for (auto& exp : plan.getModuleInformation()->exports) {
+        auto offset = exportSymbolTable->takeNextScopeOffset(NoLockingNecessary);
+        exportSymbolTable->set(NoLockingNecessary, exp.field.impl(), SymbolTableEntry(VarOffset(offset)));
+    }
+
+    return JSValue::encode(JSWebAssemblyModule::create(vm, structure, plan.getModuleInformation(), plan.getCompiledFunctions(), exportSymbolTable));
 }
 
 static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyModule(ExecState* state)
diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp
new file mode 100644 (file)
index 0000000..dab5b82
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WebAssemblyModuleRecord.h"
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "Error.h"
+#include "JSCInlines.h"
+#include "JSLexicalEnvironment.h"
+#include "JSModuleEnvironment.h"
+#include "JSWebAssemblyInstance.h"
+#include "JSWebAssemblyModule.h"
+#include "WasmFormat.h"
+#include "WebAssemblyFunction.h"
+
+namespace JSC {
+
+const ClassInfo WebAssemblyModuleRecord::s_info = { "WebAssemblyModuleRecord", &Base::s_info, nullptr, CREATE_METHOD_TABLE(WebAssemblyModuleRecord) };
+
+Structure* WebAssemblyModuleRecord::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+{
+    return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
+}
+
+WebAssemblyModuleRecord* WebAssemblyModuleRecord::create(ExecState* exec, VM& vm, Structure* structure, const Identifier& moduleKey, const SourceCode& sourceCode, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables)
+{
+    WebAssemblyModuleRecord* instance = new (NotNull, allocateCell<WebAssemblyModuleRecord>(vm.heap)) WebAssemblyModuleRecord(vm, structure, moduleKey, sourceCode, declaredVariables, lexicalVariables);
+    instance->finishCreation(exec, vm);
+    return instance;
+}
+
+WebAssemblyModuleRecord::WebAssemblyModuleRecord(VM& vm, Structure* structure, const Identifier& moduleKey, const SourceCode& sourceCode, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables)
+    : Base(vm, structure, moduleKey, sourceCode, declaredVariables, lexicalVariables)
+{
+}
+
+void WebAssemblyModuleRecord::destroy(JSCell* cell)
+{
+    WebAssemblyModuleRecord* thisObject = jsCast<WebAssemblyModuleRecord*>(cell);
+    thisObject->WebAssemblyModuleRecord::~WebAssemblyModuleRecord();
+}
+
+void WebAssemblyModuleRecord::finishCreation(ExecState* exec, VM& vm)
+{
+    Base::finishCreation(exec, vm);
+    ASSERT(inherits(info()));
+}
+
+void WebAssemblyModuleRecord::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+    WebAssemblyModuleRecord* thisObject = jsCast<WebAssemblyModuleRecord*>(cell);
+    Base::visitChildren(thisObject, visitor);
+    visitor.append(&thisObject->m_instance);
+}
+
+void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* instance)
+{
+    VM& vm = state->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    UNUSED_PARAM(scope);
+    auto* globalObject = state->lexicalGlobalObject();
+
+    const Wasm::ModuleInformation& moduleInformation = instance->module()->moduleInformation();
+    const Wasm::CompiledFunctions& compiledFunctions = instance->module()->compiledFunctions();
+    SymbolTable* exportSymbolTable = instance->module()->exportSymbolTable();
+
+    // FIXME wire up the imports. https://bugs.webkit.org/show_bug.cgi?id=165118
+
+    // Let exports be a list of (string, JS value) pairs that is mapped from each external value e in instance.exports as follows:
+    JSModuleEnvironment* moduleEnvironment = JSModuleEnvironment::create(vm, globalObject, nullptr, exportSymbolTable, JSValue(), this);
+    unsigned offset = 0;
+    for (const auto& exp : moduleInformation.exports) {
+        JSValue exportedValue;
+        PutPropertySlot slot(this);
+        slot.setNewProperty(this, offset++);
+        switch (exp.kind) {
+        case Wasm::External::Function: {
+            // 1. If e is a closure c:
+            //   i. If there is an Exported Function Exotic Object func in funcs whose func.[[Closure]] equals c, then return func.
+            //   ii. (Note: At most one wrapper is created for any closure, so func is unique, even if there are multiple occurrances in the list. Moreover, if the item was an import that is already an Exported Function Exotic Object, then the original function object will be found. For imports that are regular JS functions, a new wrapper will be created.)
+            //   iii. Otherwise:
+            //     a. Let func be an Exported Function Exotic Object created from c.
+            //     b. Append func to funcs.
+            //     c. Return func.
+            const Wasm::FunctionCompilation* compiledFunction = compiledFunctions.at(exp.functionIndex).get();
+            const B3::Compilation* jsEntryPoint = compiledFunction->jsEntryPoint.get();
+            const Wasm::Signature* signature = moduleInformation.functions.at(exp.functionIndex).signature;
+            WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->arguments.size(), exp.field.string(), instance, CallableWebAssemblyFunction(jsEntryPoint, signature));
+            exportedValue = function;
+            break;
+        }
+        case Wasm::External::Table: {
+            // FIXME https://bugs.webkit.org/show_bug.cgi?id=164135
+            break;
+        }
+        case Wasm::External::Memory: {
+            // FIXME https://bugs.webkit.org/show_bug.cgi?id=164134
+            break;
+        }
+        case Wasm::External::Global: {
+            // FIXME https://bugs.webkit.org/show_bug.cgi?id=164133
+            // In the MVP, only immutable global variables can be exported.
+            break;
+        }
+        }
+
+        bool shouldThrowReadOnlyError = false;
+        bool ignoreReadOnlyErrors = true;
+        bool putResult = false;
+        symbolTablePutTouchWatchpointSet(moduleEnvironment, state, exp.field, exportedValue, shouldThrowReadOnlyError, ignoreReadOnlyErrors, putResult);
+        RELEASE_ASSERT(putResult);
+    }
+
+    RELEASE_ASSERT(!m_instance);
+    m_instance.set(vm, this, instance);
+    m_moduleEnvironment.set(vm, this, moduleEnvironment);
+}
+
+JSValue WebAssemblyModuleRecord::evaluate(ExecState* state)
+{
+    // FIXME this should call the module's `start` function, if any. https://bugs.webkit.org/show_bug.cgi?id=165150
+    // https://github.com/WebAssembly/design/blob/master/Modules.md#module-start-function
+    // The start function must not take any arguments or return anything
+    UNUSED_PARAM(state);
+    return jsUndefined();
+}
+
+} // namespace JSC
+
+#endif // ENABLE(WEBASSEMBLY)
diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.h b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.h
new file mode 100644 (file)
index 0000000..aab353a
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "AbstractModuleRecord.h"
+
+namespace JSC {
+
+class JSWebAssemblyInstance;
+
+// Based on the WebAssembly.Instance specification
+// https://github.com/WebAssembly/design/blob/master/JS.md#webassemblyinstance-constructor
+class WebAssemblyModuleRecord : public AbstractModuleRecord {
+    friend class LLIntOffsetsExtractor;
+public:
+    typedef AbstractModuleRecord Base;
+
+    DECLARE_EXPORT_INFO;
+
+    static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
+    static WebAssemblyModuleRecord* create(ExecState*, VM&, Structure*, const Identifier&, const SourceCode&, const VariableEnvironment&, const VariableEnvironment&);
+
+    void link(ExecState*, JSWebAssemblyInstance*);
+    JS_EXPORT_PRIVATE JSValue evaluate(ExecState*);
+
+private:
+    WebAssemblyModuleRecord(VM&, Structure*, const Identifier&, const SourceCode&, const VariableEnvironment&, const VariableEnvironment&);
+
+    void finishCreation(ExecState*, VM&);
+    static void destroy(JSCell*);
+
+    static void visitChildren(JSCell*, SlotVisitor&);
+
+    WriteBarrier<JSWebAssemblyInstance> m_instance;
+};
+
+} // namespace JSC
+
+#endif // ENABLE(WEBASSEMBLY)
index 71c33f8..6eec919 100644 (file)
@@ -1,3 +1,13 @@
+2016-11-29  JF Bastien  <jfbastien@apple.com>
+
+        WebAssembly JS API: improve Instance
+        https://bugs.webkit.org/show_bug.cgi?id=164757
+
+        Reviewed by Keith Miller.
+
+        * wtf/Expected.h:
+        (WTF::ExpectedDetail::destroy): silence a warning
+
 2016-11-29  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r209058 and r209074.
index cd7fa56..342e686 100644 (file)
@@ -75,7 +75,7 @@ inline NO_RETURN_DUE_TO_CRASH void Throw() { RELEASE_ASSERT_NOT_REACHED(); }
 static constexpr enum class ValueTagType { } ValueTag { };
 static constexpr enum class ErrorTagType { } ErrorTag { };
 
-template<class T, std::enable_if_t<std::is_trivially_destructible<T>::value>* = nullptr> void destroy(T& t) { }
+template<class T, std::enable_if_t<std::is_trivially_destructible<T>::value>* = nullptr> void destroy(T&) { }
 template<class T, std::enable_if_t<!std::is_trivially_destructible<T>::value && (std::is_class<T>::value || std::is_union<T>::value)>* = nullptr> void destroy(T& t) { t.~T(); }
 
 template <class T, class E>