WebAssembly: implement Module imports/exports
authorjfbastien@apple.com <jfbastien@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Mar 2017 18:41:53 +0000 (18:41 +0000)
committerjfbastien@apple.com <jfbastien@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Mar 2017 18:41:53 +0000 (18:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=166982

Reviewed by Saam Barati.

JSTests:

* wasm/js-api/Module.exports.js: Added.
(assert.throws.WebAssembly.Module.prototype.exports):
(assert.eq):
* wasm/js-api/Module.imports.js: Added.
(assert.throws.WebAssembly.Module.prototype.imports):
(assert.eq):

Source/JavaScriptCore:

As defined in: https://github.com/WebAssembly/design/commit/18cbacb90cd3584dd5c9aa3d392e4e55f66af6ab

* wasm/WasmFormat.h:
(JSC::Wasm::makeString): use uppercase instead, it was only used
for diagnostic but is now used for the expected JS property's
capitalization
* wasm/js/WebAssemblyModulePrototype.cpp:
(JSC::webAssemblyModuleProtoImports):
(JSC::webAssemblyModuleProtoExports):

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

JSTests/ChangeLog
JSTests/wasm/js-api/Module.exports.js [new file with mode: 0644]
JSTests/wasm/js-api/Module.imports.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/wasm/WasmFormat.h
Source/JavaScriptCore/wasm/js/WebAssemblyModulePrototype.cpp

index 0d76b9b..affa06d 100644 (file)
@@ -1,3 +1,17 @@
+2017-03-28  JF Bastien  <jfbastien@apple.com>
+
+        WebAssembly: implement Module imports/exports
+        https://bugs.webkit.org/show_bug.cgi?id=166982
+
+        Reviewed by Saam Barati.
+
+        * wasm/js-api/Module.exports.js: Added.
+        (assert.throws.WebAssembly.Module.prototype.exports):
+        (assert.eq):
+        * wasm/js-api/Module.imports.js: Added.
+        (assert.throws.WebAssembly.Module.prototype.imports):
+        (assert.eq):
+
 2017-03-27  JF Bastien  <jfbastien@apple.com>
 
         WebAssembly: misc memory testing
diff --git a/JSTests/wasm/js-api/Module.exports.js b/JSTests/wasm/js-api/Module.exports.js
new file mode 100644 (file)
index 0000000..514047b
--- /dev/null
@@ -0,0 +1,42 @@
+import Builder from '../Builder.js';
+import * as assert from '../assert.js';
+
+assert.throws(() => WebAssembly.Module.prototype.exports(undefined, ""), TypeError, `WebAssembly.Module.prototype.exports called with non WebAssembly.Module |this| value`);
+
+{
+    const m = new WebAssembly.Module((new Builder()).WebAssembly().get());
+    assert.isArray(m.exports);
+    assert.eq(m.exports.length, 0);
+    assert.truthy(m.exports !== m.exports);
+}
+
+{
+    const m = new WebAssembly.Module(
+        (new Builder())
+            .Type().End()
+            .Function().End()
+            .Table()
+                .Table({initial: 20, maximum: 30, element: "anyfunc"})
+            .End()
+            .Memory().InitialMaxPages(1, 1).End()
+            .Global().I32(42, "immutable").End()
+            .Export()
+                .Function("func")
+                .Table("tab", 0)
+                .Memory("mem", 0)
+                .Global("glob", 0)
+            .End()
+            .Code()
+                .Function("func", { params: [] }).Return().End()
+            .End()
+            .WebAssembly().get());
+    assert.eq(m.exports.length, 4);
+    assert.eq(m.exports[0].name, "func");
+    assert.eq(m.exports[0].kind, "function");
+    assert.eq(m.exports[1].name, "tab");
+    assert.eq(m.exports[1].kind, "table");
+    assert.eq(m.exports[2].name, "mem");
+    assert.eq(m.exports[2].kind, "memory");
+    assert.eq(m.exports[3].name, "glob");
+    assert.eq(m.exports[3].kind, "global");
+}
diff --git a/JSTests/wasm/js-api/Module.imports.js b/JSTests/wasm/js-api/Module.imports.js
new file mode 100644 (file)
index 0000000..273514d
--- /dev/null
@@ -0,0 +1,37 @@
+import Builder from '../Builder.js';
+import * as assert from '../assert.js';
+
+assert.throws(() => WebAssembly.Module.prototype.imports(undefined, ""), TypeError, `WebAssembly.Module.prototype.imports called with non WebAssembly.Module |this| value`);
+
+{
+    const m = new WebAssembly.Module((new Builder()).WebAssembly().get());
+    assert.isArray(m.imports);
+    assert.eq(m.imports.length, 0);
+    assert.truthy(m.exports !== m.exports);
+}
+
+{
+    const m = new WebAssembly.Module(
+        (new Builder())
+            .Type().End()
+            .Import()
+                .Function("fooFunction", "barFunction", { params: [] })
+                .Table("fooTable", "barTable", {initial: 20, element: "anyfunc"})
+                .Memory("fooMemory", "barMemory", {initial: 20})
+                .Global().I32("fooGlobal", "barGlobal", "immutable").End()
+            .End()
+            .WebAssembly().get());
+    assert.eq(m.imports.length, 4);
+    assert.eq(m.imports[0].module, "fooFunction");
+    assert.eq(m.imports[0].name, "barFunction");
+    assert.eq(m.imports[0].kind, "function");
+    assert.eq(m.imports[1].module, "fooTable");
+    assert.eq(m.imports[1].name, "barTable");
+    assert.eq(m.imports[1].kind, "table");
+    assert.eq(m.imports[2].module, "fooMemory");
+    assert.eq(m.imports[2].name, "barMemory");
+    assert.eq(m.imports[2].kind, "memory");
+    assert.eq(m.imports[3].module, "fooGlobal");
+    assert.eq(m.imports[3].name, "barGlobal");
+    assert.eq(m.imports[3].kind, "global");
+}
index 53ad44b..f3ac823 100644 (file)
@@ -1,3 +1,20 @@
+2017-03-28  JF Bastien  <jfbastien@apple.com>
+
+        WebAssembly: implement Module imports/exports
+        https://bugs.webkit.org/show_bug.cgi?id=166982
+
+        Reviewed by Saam Barati.
+
+        As defined in: https://github.com/WebAssembly/design/commit/18cbacb90cd3584dd5c9aa3d392e4e55f66af6ab
+
+        * wasm/WasmFormat.h:
+        (JSC::Wasm::makeString): use uppercase instead, it was only used
+        for diagnostic but is now used for the expected JS property's
+        capitalization
+        * wasm/js/WebAssemblyModulePrototype.cpp:
+        (JSC::webAssemblyModuleProtoImports):
+        (JSC::webAssemblyModuleProtoExports):
+
 2017-03-27  JF Bastien  <jfbastien@apple.com>
 
         WebAssembly: JSWebAssemblyCodeBlock.h belongs in JavaScriptCore/wasm/js not JavaScriptCore/wasm
index 9a7a672..b59676a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -92,10 +92,10 @@ static_assert(static_cast<int>(ExternalKind::Global)   == 3, "Wasm needs Global
 static inline const char* makeString(ExternalKind kind)
 {
     switch (kind) {
-    case ExternalKind::Function: return "Function";
-    case ExternalKind::Table: return "Table";
-    case ExternalKind::Memory: return "Memory";
-    case ExternalKind::Global: return "Global";
+    case ExternalKind::Function: return "function";
+    case ExternalKind::Table: return "table";
+    case ExternalKind::Memory: return "memory";
+    case ExternalKind::Global: return "global";
     }
     RELEASE_ASSERT_NOT_REACHED();
     return "?";
index 5d3c645..aa2f376 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "JSArrayBuffer.h"
 #include "JSCInlines.h"
 #include "JSWebAssemblyModule.h"
+#include "ObjectConstructor.h"
 
 namespace JSC {
 static EncodedJSValue JSC_HOST_CALL webAssemblyModuleProtoCustomSections(ExecState*);
+static EncodedJSValue JSC_HOST_CALL webAssemblyModuleProtoImports(ExecState*);
+static EncodedJSValue JSC_HOST_CALL webAssemblyModuleProtoExports(ExecState*);
 }
 
 #include "WebAssemblyModulePrototype.lut.h"
@@ -47,6 +50,8 @@ const ClassInfo WebAssemblyModulePrototype::s_info = { "WebAssembly.Module.proto
 /* Source for WebAssemblyModulePrototype.lut.h
  @begin prototypeTableWebAssemblyModule
  customSections webAssemblyModuleProtoCustomSections DontEnum|Function 1
+ imports        webAssemblyModuleProtoImports        DontEnum|Accessor 0
+ exports        webAssemblyModuleProtoExports        DontEnum|Accessor 0
  @end
  */
 
@@ -58,8 +63,7 @@ EncodedJSValue JSC_HOST_CALL webAssemblyModuleProtoCustomSections(ExecState* exe
 
     JSWebAssemblyModule* module = jsDynamicCast<JSWebAssemblyModule*>(vm, exec->thisValue());
     if (!module)
-        throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Module.prototype.customSections called with non WebAssembly.Module |this| value")));
-    RETURN_IF_EXCEPTION(throwScope, { });
+        return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Module.prototype.customSections called with non WebAssembly.Module |this| value"))));
 
     const String sectionNameString = exec->argument(0).getString(exec);
     RETURN_IF_EXCEPTION(throwScope, { });
@@ -72,7 +76,7 @@ EncodedJSValue JSC_HOST_CALL webAssemblyModuleProtoCustomSections(ExecState* exe
         if (section.name == sectionNameString) {
             auto buffer = ArrayBuffer::tryCreate(section.payload.data(), section.payload.size());
             if (!buffer)
-                throwException(exec, throwScope, createOutOfMemoryError(exec));
+                return JSValue::encode(throwException(exec, throwScope, createOutOfMemoryError(exec)));
 
             Structure* arrayBufferStructure = InternalFunction::createSubclassStructure(exec, JSValue(), globalObject->arrayBufferStructure(ArrayBufferSharingMode::Default));
             RETURN_IF_EXCEPTION(throwScope, { });
@@ -85,6 +89,68 @@ EncodedJSValue JSC_HOST_CALL webAssemblyModuleProtoCustomSections(ExecState* exe
     return JSValue::encode(result);
 }
 
+EncodedJSValue JSC_HOST_CALL webAssemblyModuleProtoImports(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto* globalObject = exec->lexicalGlobalObject();
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+    JSWebAssemblyModule* module = jsDynamicCast<JSWebAssemblyModule*>(vm, exec->thisValue());
+    if (!module)
+        return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Module.prototype.imports called with non WebAssembly.Module |this| value"))));
+
+    JSArray* result = constructEmptyArray(exec, nullptr, globalObject);
+    RETURN_IF_EXCEPTION(throwScope, { });
+
+    const auto& imports = module->moduleInformation().imports;
+    if (imports.size()) {
+        Identifier module = Identifier::fromString(exec, "module");
+        Identifier name = Identifier::fromString(exec, "name");
+        Identifier kind = Identifier::fromString(exec, "kind");
+        for (const Wasm::Import& imp : imports) {
+            JSObject* obj = constructEmptyObject(exec);
+            RETURN_IF_EXCEPTION(throwScope, { });
+            obj->putDirect(vm, module, jsString(exec, imp.module.string()));
+            obj->putDirect(vm, name, jsString(exec, imp.field.string()));
+            obj->putDirect(vm, kind, jsString(exec, String(makeString(imp.kind))));
+            result->push(exec, obj);
+            RETURN_IF_EXCEPTION(throwScope, { });
+        }
+    }
+
+    return JSValue::encode(result);
+}
+
+EncodedJSValue JSC_HOST_CALL webAssemblyModuleProtoExports(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto* globalObject = exec->lexicalGlobalObject();
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+    JSWebAssemblyModule* module = jsDynamicCast<JSWebAssemblyModule*>(vm, exec->thisValue());
+    if (!module)
+        return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Module.prototype.exports called with non WebAssembly.Module |this| value"))));
+
+    JSArray* result = constructEmptyArray(exec, nullptr, globalObject);
+    RETURN_IF_EXCEPTION(throwScope, { });
+
+    const auto& exports = module->moduleInformation().exports;
+    if (exports.size()) {
+        Identifier name = Identifier::fromString(exec, "name");
+        Identifier kind = Identifier::fromString(exec, "kind");
+        for (const Wasm::Export& exp : exports) {
+            JSObject* obj = constructEmptyObject(exec);
+            RETURN_IF_EXCEPTION(throwScope, { });
+            obj->putDirect(vm, name, jsString(exec, exp.field.string()));
+            obj->putDirect(vm, kind, jsString(exec, String(makeString(exp.kind))));
+            result->push(exec, obj);
+            RETURN_IF_EXCEPTION(throwScope, { });
+        }
+    }
+
+    return JSValue::encode(result);
+}
+
 WebAssemblyModulePrototype* WebAssemblyModulePrototype::create(VM& vm, JSGlobalObject*, Structure* structure)
 {
     auto* object = new (NotNull, allocateCell<WebAssemblyModulePrototype>(vm.heap)) WebAssemblyModulePrototype(vm, structure);