Bug 1276028 - Baldr: add WebAssembly.(Module, Instance) (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Fri, 01 Jul 2016 12:48:56 -0500
changeset 303430 6668e747f0dfe4e134b66d14a5197e41ecc58c01
parent 303429 fdadd7ef691eb0797103d95193d01c8483de3547
child 303431 304be069e80b37526390ad62c59f7a78f2211186
push id30388
push usercbook@mozilla.com
push dateSat, 02 Jul 2016 09:15:23 +0000
treeherdermozilla-central@39dffbba7642 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1276028
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1276028 - Baldr: add WebAssembly.(Module, Instance) (r=bbouvier) MozReview-Commit-ID: Lg8aq6BLnZ2
js/src/asmjs/AsmJS.cpp
js/src/asmjs/WasmInstance.cpp
js/src/asmjs/WasmInstance.h
js/src/asmjs/WasmJS.cpp
js/src/asmjs/WasmJS.h
js/src/asmjs/WasmModule.cpp
js/src/asmjs/WasmModule.h
js/src/jit-test/tests/wasm/jsapi.js
js/src/js.msg
js/src/jsprototypes.h
js/src/vm/GlobalObject.cpp
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -7874,16 +7874,20 @@ TryInstantiate(JSContext* cx, CallArgs a
     }
 
     Rooted<FunctionVector> funcImports(cx, FunctionVector(cx));
     for (const AsmJSImport& import : metadata.asmJSImports) {
         if (!funcImports.append(ffis[import.ffiIndex()]))
             return false;
     }
 
+    instanceObj.set(WasmInstanceObject::create(cx));
+    if (!instanceObj)
+        return false;
+
     if (!module.instantiate(cx, funcImports, heap, instanceObj))
         return false;
 
     // Now write the imported values into global data.
     uint8_t* globalData = instanceObj->instance().codeSegment().globalData();
     uint32_t valIndex = 0;
     for (const AsmJSGlobal& global : metadata.asmJSGlobals) {
         if (global.which() == AsmJSGlobal::Variable)
--- a/js/src/asmjs/WasmInstance.cpp
+++ b/js/src/asmjs/WasmInstance.cpp
@@ -465,24 +465,19 @@ static const char ExportField[] = "expor
 Instance::create(JSContext* cx,
                  UniqueCodeSegment codeSegment,
                  const Metadata& metadata,
                  const ShareableBytes* maybeBytecode,
                  TypedFuncTableVector&& typedFuncTables,
                  HandleArrayBufferObjectMaybeShared heap,
                  Handle<FunctionVector> funcImports,
                  const ExportMap& exportMap,
-                 MutableHandleWasmInstanceObject instanceObj)
+                 HandleWasmInstanceObject instanceObj)
 {
-    // Ensure that the Instance is traceable via WasmInstanceObject before any
-    // GC can occur.
-
-    instanceObj.set(WasmInstanceObject::create(cx));
-    if (!instanceObj)
-        return false;
+    // Ensure that the Instance is traceable via 'instanceObj' before any GC.
 
     {
         auto instance = cx->make_unique<Instance>(Move(codeSegment), metadata, maybeBytecode,
                                                   Move(typedFuncTables), heap);
         if (!instance)
             return false;
 
         instanceObj->init(Move(instance));
--- a/js/src/asmjs/WasmInstance.h
+++ b/js/src/asmjs/WasmInstance.h
@@ -95,17 +95,17 @@ class Instance
     static bool create(JSContext* cx,
                        UniqueCodeSegment codeSegment,
                        const Metadata& metadata,
                        const ShareableBytes* maybeBytecode,
                        TypedFuncTableVector&& typedFuncTables,
                        HandleArrayBufferObjectMaybeShared heap,
                        Handle<FunctionVector> funcImports,
                        const ExportMap& exports,
-                       MutableHandle<WasmInstanceObject*> instanceObj);
+                       HandleWasmInstanceObject instanceObj);
     ~Instance();
     void trace(JSTracer* trc);
 
     const CodeSegment& codeSegment() const { return *codeSegment_; }
     const Metadata& metadata() const { return *metadata_; }
     SharedMem<uint8_t*> heap() const;
     size_t heapLength() const;
 
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -19,16 +19,18 @@
 #include "asmjs/WasmJS.h"
 
 #include "asmjs/WasmCompile.h"
 #include "asmjs/WasmInstance.h"
 #include "asmjs/WasmModule.h"
 
 #include "jsobjinlines.h"
 
+#include "vm/NativeObject-inl.h"
+
 using namespace js;
 using namespace js::wasm;
 
 bool
 wasm::HasCompilerSupport(ExclusiveContext* cx)
 {
     if (!cx->jitSupportsFloatingPoint())
         return false;
@@ -36,16 +38,33 @@ wasm::HasCompilerSupport(ExclusiveContex
 #if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_ARM64)
     return false;
 #else
     return true;
 #endif
 }
 
 static bool
+CheckCompilerSupport(JSContext* cx)
+{
+    if (!HasCompilerSupport(cx)) {
+#ifdef JS_MORE_DETERMINISTIC
+        fprintf(stderr, "WebAssembly is not supported on the current device.\n");
+#endif
+        JS_ReportError(cx, "WebAssembly is not supported on the current device.");
+        return false;
+    }
+
+    return true;
+}
+
+// ============================================================================
+// (Temporary) Wasm class and static methods
+
+static bool
 Throw(JSContext* cx, const char* str)
 {
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL, str);
     return false;
 }
 
 static bool
 GetProperty(JSContext* cx, HandleObject obj, const char* chars, MutableHandleValue v)
@@ -88,44 +107,43 @@ ImportFunctions(JSContext* cx, HandleObj
 
     return true;
 }
 
 bool
 wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code, HandleObject importObj,
            MutableHandleWasmInstanceObject instanceObj)
 {
-    if (!HasCompilerSupport(cx)) {
-#ifdef JS_MORE_DETERMINISTIC
-        fprintf(stderr, "WebAssembly is not supported on the current device.\n");
-#endif
-        JS_ReportError(cx, "WebAssembly is not supported on the current device.");
+    if (!CheckCompilerSupport(cx))
         return false;
-    }
 
     Bytes bytecode;
     if (!bytecode.append((uint8_t*)code->viewDataEither().unwrap(), code->byteLength()))
         return false;
 
-    JS::AutoFilename filename;
-    if (!DescribeScriptedCaller(cx, &filename))
-        return false;
+    UniqueChars filename;
+    JS::AutoFilename af;
+    if (DescribeScriptedCaller(cx, &af)) {
+        filename = DuplicateString(cx, af.get());
+        if (!filename)
+            return false;
+    }
 
-    UniqueChars file = DuplicateString(filename.get());
-    if (!file)
-        return false;
-
-    UniqueModule module = Compile(cx, Move(file), Move(bytecode));
+    UniqueModule module = Compile(cx, Move(filename), Move(bytecode));
     if (!module)
         return false;
 
     Rooted<FunctionVector> funcImports(cx, FunctionVector(cx));
     if (!ImportFunctions(cx, importObj, module->importNames(), &funcImports))
         return false;
 
+    instanceObj.set(WasmInstanceObject::create(cx));
+    if (!instanceObj)
+        return false;
+
     return module->instantiate(cx, funcImports, nullptr, instanceObj);
 }
 
 static bool
 InstantiateModule(JSContext* cx, unsigned argc, Value* vp)
 {
     MOZ_ASSERT(cx->runtime()->options().wasm());
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -198,61 +216,137 @@ js::InitWasmClass(JSContext* cx, HandleO
 
     if (!JS_DefineFunctions(cx, Wasm, wasm_static_methods))
         return nullptr;
 
     global->as<GlobalObject>().setConstructor(JSProto_Wasm, ObjectValue(*Wasm));
     return Wasm;
 }
 
+// ============================================================================
+// WebAssembly.Module class and methods
+
 const ClassOps WasmModuleObject::classOps_ =
 {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     WasmModuleObject::finalize
 };
 
 const Class WasmModuleObject::class_ =
 {
-    "WasmModuleObject",
+    "WebAssembly.Module",
     JSCLASS_DELAY_METADATA_BUILDER |
     JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS),
     &WasmModuleObject::classOps_,
 };
 
 /* static */ void
 WasmModuleObject::finalize(FreeOp* fop, JSObject* obj)
 {
     fop->delete_(&obj->as<WasmModuleObject>().module());
 }
 
 /* static */ WasmModuleObject*
-WasmModuleObject::create(ExclusiveContext* cx, UniqueModule module)
+WasmModuleObject::create(ExclusiveContext* cx, UniqueModule module, HandleObject proto)
 {
     AutoSetNewObjectMetadata metadata(cx);
-    auto* obj = NewObjectWithGivenProto<WasmModuleObject>(cx, nullptr);
+    auto* obj = NewObjectWithGivenProto<WasmModuleObject>(cx, proto);
     if (!obj)
         return nullptr;
 
     obj->setReservedSlot(MODULE_SLOT, PrivateValue((void*)module.release()));
     return obj;
 }
 
+static JSObject&
+GetConstructorPrototype(JSContext* cx, Native native, CallArgs args)
+{
+    RootedObject callee(cx, &args.callee());
+    MOZ_ASSERT(callee->as<JSFunction>().native() == native);
+
+    RootedValue proto(cx);
+    if (!GetProperty(cx, callee, callee, cx->names().prototype, &proto))
+        MOZ_CRASH("non-configurable data property of known constructor");
+
+    if (!proto.isObject())
+        MOZ_CRASH("readonly property of known constructor");
+
+    return proto.toObject();
+}
+
+static bool
+ModuleConstructor(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (!ThrowIfNotConstructing(cx, args, "Module"))
+        return false;
+
+    if (!args.requireAtLeast(cx, "WebAssembly.Module", 1))
+        return false;
+
+    if (!args.get(0).isObject()) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
+        return false;
+    }
+
+    Bytes bytecode;
+    if (args[0].toObject().is<TypedArrayObject>()) {
+        TypedArrayObject& view = args[0].toObject().as<TypedArrayObject>();
+        if (!bytecode.append((uint8_t*)view.viewDataEither().unwrap(), view.byteLength()))
+            return false;
+    } else if (args[0].toObject().is<ArrayBufferObject>()) {
+        ArrayBufferObject& buffer = args[0].toObject().as<ArrayBufferObject>();
+        if (!bytecode.append(buffer.dataPointer(), buffer.byteLength()))
+            return false;
+    } else {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
+        return false;
+    }
+
+    UniqueChars filename;
+    JS::AutoFilename af;
+    if (DescribeScriptedCaller(cx, &af)) {
+        filename = DuplicateString(cx, af.get());
+        if (!filename)
+            return false;
+    }
+
+    if (!CheckCompilerSupport(cx))
+        return false;
+
+    UniqueModule module = Compile(cx, Move(filename), Move(bytecode));
+    if (!module)
+        return false;
+
+    RootedObject proto(cx, &GetConstructorPrototype(cx, ModuleConstructor, args));
+    RootedObject moduleObj(cx, WasmModuleObject::create(cx, Move(module), proto));
+    if (!moduleObj)
+        return false;
+
+    args.rval().setObject(*moduleObj);
+    return true;
+}
+
 Module&
 WasmModuleObject::module() const
 {
     MOZ_ASSERT(is<WasmModuleObject>());
     return *(Module*)getReservedSlot(MODULE_SLOT).toPrivate();
 }
 
+// ============================================================================
+// WebAssembly.Instance class and methods
+
 const ClassOps WasmInstanceObject::classOps_ =
 {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
@@ -261,17 +355,17 @@ const ClassOps WasmInstanceObject::class
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     WasmInstanceObject::trace
 };
 
 const Class WasmInstanceObject::class_ =
 {
-    "WasmInstanceObject",
+    "WebAssembly.Instance",
     JSCLASS_DELAY_METADATA_BUILDER |
     JSCLASS_HAS_RESERVED_SLOTS(WasmInstanceObject::RESERVED_SLOTS),
     &WasmInstanceObject::classOps_,
 };
 
 bool
 WasmInstanceObject::isNewborn() const
 {
@@ -289,20 +383,20 @@ WasmInstanceObject::finalize(FreeOp* fop
 /* static */ void
 WasmInstanceObject::trace(JSTracer* trc, JSObject* obj)
 {
     if (!obj->as<WasmInstanceObject>().isNewborn())
         obj->as<WasmInstanceObject>().instance().trace(trc);
 }
 
 /* static */ WasmInstanceObject*
-WasmInstanceObject::create(ExclusiveContext* cx)
+WasmInstanceObject::create(ExclusiveContext* cx, HandleObject proto)
 {
     AutoSetNewObjectMetadata metadata(cx);
-    auto obj = NewObjectWithGivenProto<WasmInstanceObject>(cx, nullptr);
+    auto* obj = NewObjectWithGivenProto<WasmInstanceObject>(cx, proto);
     if (!obj)
         return nullptr;
 
     MOZ_ASSERT(obj->isNewborn());
     return obj;
 }
 
 void
@@ -314,21 +408,149 @@ WasmInstanceObject::init(UniqueInstance 
 }
 
 void
 WasmInstanceObject::initExportsObject(HandleObject exportObj)
 {
     initReservedSlot(EXPORTS_SLOT, ObjectValue(*exportObj));
 }
 
+static bool
+InstanceConstructor(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (!ThrowIfNotConstructing(cx, args, "Instance"))
+        return false;
+
+    if (!args.requireAtLeast(cx, "WebAssembly.Instance", 1))
+        return false;
+
+    if (!args.get(0).isObject() || !args[0].toObject().is<WasmModuleObject>()) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MOD_ARG);
+        return false;
+    }
+
+    const Module& module = args[0].toObject().as<WasmModuleObject>().module();
+
+    RootedObject importObj(cx);
+    if (!args.get(1).isUndefined()) {
+        if (!args[1].isObject()) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMPORT_ARG);
+            return false;
+        }
+        importObj = &args[1].toObject();
+    }
+
+    Rooted<FunctionVector> funcImports(cx, FunctionVector(cx));
+    if (!ImportFunctions(cx, importObj, module.importNames(), &funcImports))
+        return false;
+
+    RootedObject proto(cx, &GetConstructorPrototype(cx, InstanceConstructor, args));
+    RootedWasmInstanceObject instanceObj(cx, WasmInstanceObject::create(cx, proto));
+    if (!instanceObj)
+        return false;
+
+    if (!module.instantiate(cx, funcImports, nullptr, instanceObj))
+        return false;
+
+    args.rval().setObject(*instanceObj);
+    return true;
+}
+
 Instance&
 WasmInstanceObject::instance() const
 {
     MOZ_ASSERT(!isNewborn());
     return *(Instance*)getReservedSlot(INSTANCE_SLOT).toPrivate();
 }
 
 JSObject&
 WasmInstanceObject::exportsObject() const
 {
     MOZ_ASSERT(!isNewborn());
     return getReservedSlot(EXPORTS_SLOT).toObject();
 }
+
+// ============================================================================
+// WebAssembly class and static methods
+
+#if JS_HAS_TOSOURCE
+static bool
+WebAssembly_toSource(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    args.rval().setString(cx->names().WebAssembly);
+    return true;
+}
+#endif
+
+static const JSFunctionSpec WebAssembly_static_methods[] =
+{
+#if JS_HAS_TOSOURCE
+    JS_FN(js_toSource_str, WebAssembly_toSource, 0, 0),
+#endif
+    JS_FS_END
+};
+
+const Class js::WebAssemblyClass =
+{
+    js_WebAssembly_str,
+    JSCLASS_HAS_CACHED_PROTO(JSProto_WebAssembly)
+};
+
+template <class Class>
+static bool
+InitConstructor(JSContext* cx, HandleObject global, HandleObject wasm, const char* name,
+                Native native)
+{
+    RootedObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx, SingletonObject));
+    if (!proto)
+        return false;
+
+    RootedAtom className(cx, Atomize(cx, name, strlen(name)));
+    if (!className)
+        return false;
+
+    RootedFunction ctor(cx, NewNativeConstructor(cx, native, 1, className));
+    if (!ctor)
+        return false;
+
+    if (!LinkConstructorAndPrototype(cx, ctor, proto))
+        return false;
+
+    RootedId id(cx, AtomToId(className));
+    RootedValue ctorValue(cx, ObjectValue(*ctor));
+    return DefineProperty(cx, wasm, id, ctorValue, nullptr, nullptr, 0);
+}
+
+JSObject*
+js::InitWebAssemblyClass(JSContext* cx, HandleObject global)
+{
+    MOZ_ASSERT(cx->runtime()->options().wasm());
+
+    RootedObject proto(cx, global->as<GlobalObject>().getOrCreateObjectPrototype(cx));
+    if (!proto)
+        return nullptr;
+
+    RootedObject wasm(cx, NewObjectWithGivenProto(cx, &WebAssemblyClass, proto, SingletonObject));
+    if (!wasm)
+        return nullptr;
+
+    if (!JS_DefineProperty(cx, global, js_WebAssembly_str, wasm, JSPROP_RESOLVING))
+        return nullptr;
+
+    // This property will be removed before the initial WebAssembly release.
+    if (!JS_DefineProperty(cx, wasm, "experimentalVersion", EncodingVersion, JSPROP_RESOLVING))
+        return nullptr;
+
+    if (!InitConstructor<WasmModuleObject>(cx, global, wasm, "Module", ModuleConstructor))
+        return nullptr;
+    if (!InitConstructor<WasmInstanceObject>(cx, global, wasm, "Instance", InstanceConstructor))
+        return nullptr;
+
+    if (!JS_DefineFunctions(cx, wasm, WebAssembly_static_methods))
+        return nullptr;
+
+    global->as<GlobalObject>().setConstructor(JSProto_WebAssembly, ObjectValue(*wasm));
+    return wasm;
+}
+
--- a/js/src/asmjs/WasmJS.h
+++ b/js/src/asmjs/WasmJS.h
@@ -33,49 +33,61 @@ namespace wasm {
 
 class Module;
 class Instance;
 
 typedef UniquePtr<Module> UniqueModule;
 typedef UniquePtr<Instance> UniqueInstance;
 
 // Return whether WebAssembly can be compiled on this platform.
+// This must be checked and must be true to call any of the top-level wasm
+// eval/compile methods.
 
 bool
 HasCompilerSupport(ExclusiveContext* cx);
 
 // Compiles the given binary wasm module given the ArrayBufferObject
 // and links the module's imports with the given import object.
 
 MOZ_MUST_USE bool
 Eval(JSContext* cx, Handle<TypedArrayObject*> code, HandleObject importObj,
      MutableHandle<WasmInstanceObject*> instanceObj);
 
 } // namespace wasm
 
-// The class of the Wasm global namespace object.
+// 'Wasm' and its one function 'instantiateModule' are transitional APIs and
+// will be removed (replaced by 'WebAssembly') before release.
 
 extern const Class WasmClass;
 
 JSObject*
 InitWasmClass(JSContext* cx, HandleObject global);
 
+// The class of the WebAssembly global namespace object.
+
+extern const Class WebAssemblyClass;
+
+JSObject*
+InitWebAssemblyClass(JSContext* cx, HandleObject global);
+
 // The class of wasm module object wrappers. Each WasmModuleObject owns a
 // wasm::Module. These objects are currently not exposed directly to JS.
 
 class WasmModuleObject : public NativeObject
 {
     static const unsigned MODULE_SLOT = 0;
     static const ClassOps classOps_;
     static void finalize(FreeOp* fop, JSObject* obj);
   public:
     static const unsigned RESERVED_SLOTS = 1;
     static const Class class_;
 
-    static WasmModuleObject* create(ExclusiveContext* cx, wasm::UniqueModule module);
+    static WasmModuleObject* create(ExclusiveContext* cx,
+                                    wasm::UniqueModule module,
+                                    HandleObject proto = nullptr);
     wasm::Module& module() const;
 };
 
 typedef Rooted<WasmModuleObject*> RootedWasmModuleObject;
 typedef Handle<WasmModuleObject*> HandleWasmModuleObject;
 typedef MutableHandle<WasmModuleObject*> MutableHandleWasmModuleObject;
 
 // The class of wasm instance object wrappers. Each WasmInstanceObject owns a
@@ -88,17 +100,18 @@ class WasmInstanceObject : public Native
     static const ClassOps classOps_;
     bool isNewborn() const;
     static void finalize(FreeOp* fop, JSObject* obj);
     static void trace(JSTracer* trc, JSObject* obj);
   public:
     static const unsigned RESERVED_SLOTS = 2;
     static const Class class_;
 
-    static WasmInstanceObject* create(ExclusiveContext* cx);
+    static WasmInstanceObject* create(ExclusiveContext* cx,
+                                      HandleObject proto = nullptr);
     void init(wasm::UniqueInstance module);
     void initExportsObject(HandleObject exportObj);
     wasm::Instance& instance() const;
     JSObject& exportsObject() const;
 };
 
 typedef GCVector<WasmInstanceObject*> WasmInstanceObjectVector;
 typedef Rooted<WasmInstanceObject*> RootedWasmInstanceObject;
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -323,17 +323,17 @@ Module::addSizeOfMisc(MallocSizeOf mallo
              metadata_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenMetadata) +
              bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
 }
 
 bool
 Module::instantiate(JSContext* cx,
                     Handle<FunctionVector> funcImports,
                     Handle<ArrayBufferObjectMaybeShared*> asmJSHeap,
-                    MutableHandleWasmInstanceObject instanceObj) const
+                    HandleWasmInstanceObject instanceObj) const
 {
     MOZ_ASSERT(funcImports.length() == metadata_->imports.length());
     MOZ_ASSERT_IF(asmJSHeap, metadata_->isAsmJS());
 
     // asm.js module instantiation supplies its own heap, but for wasm, create
     // and initialize the heap if one is requested.
 
     Rooted<ArrayBufferObjectMaybeShared*> heap(cx, asmJSHeap);
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -189,17 +189,17 @@ class Module
     const Metadata& metadata() const { return *metadata_; }
     const ImportNameVector& importNames() const { return importNames_; }
 
     // Instantiate this module with the given imports:
 
     bool instantiate(JSContext* cx,
                      Handle<FunctionVector> funcImports,
                      Handle<ArrayBufferObjectMaybeShared*> asmJSHeap,
-                     MutableHandle<WasmInstanceObject*> instanceObj) const;
+                     HandleWasmInstanceObject instanceObj) const;
 
     // Structured clone support:
 
     size_t serializedSize() const;
     uint8_t* serialize(uint8_t* cursor) const;
     static const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor,
                                       UniquePtr<Module>* module,
                                       Metadata* maybeMetadata = nullptr);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/jsapi.js
@@ -0,0 +1,120 @@
+load(libdir + 'wasm.js');
+load(libdir + 'asserts.js');
+
+const emptyModule = wasmTextToBinary('(module)');
+
+// 'WebAssembly' property on global object
+const wasmDesc = Object.getOwnPropertyDescriptor(this, 'WebAssembly');
+assertEq(typeof wasmDesc.value, "object");
+assertEq(wasmDesc.writable, true);
+assertEq(wasmDesc.enumerable, false);
+assertEq(wasmDesc.configurable, true);
+
+// 'WebAssembly' object
+assertEq(WebAssembly, wasmDesc.value);
+assertEq(String(WebAssembly), "[object WebAssembly]");
+
+// 'WebAssembly.Module' property
+const moduleDesc = Object.getOwnPropertyDescriptor(WebAssembly, 'Module');
+assertEq(typeof moduleDesc.value, "function");
+assertEq(moduleDesc.writable, true);
+assertEq(moduleDesc.enumerable, false);
+assertEq(moduleDesc.configurable, true);
+
+// 'WebAssembly.Module' constructor function
+const Module = WebAssembly.Module;
+assertEq(Module, moduleDesc.value);
+assertEq(Module.length, 1);
+assertEq(Module.name, "Module");
+assertErrorMessage(() => Module(), TypeError, /constructor without new is forbidden/);
+assertErrorMessage(() => new Module(1), TypeError, "first argument must be an ArrayBuffer or typed array object");
+assertErrorMessage(() => new Module({}), TypeError, "first argument must be an ArrayBuffer or typed array object");
+assertErrorMessage(() => new Module(new Uint8Array()), /* TODO: WebAssembly.CompileError */ TypeError, /wasm validation error/);
+assertErrorMessage(() => new Module(new ArrayBuffer()), /* TODO: WebAssembly.CompileError */ TypeError, /wasm validation error/);
+assertEq(new Module(emptyModule) instanceof Module, true);
+assertEq(new Module(emptyModule.buffer) instanceof Module, true);
+
+// 'WebAssembly.Module.prototype' property
+const moduleProtoDesc = Object.getOwnPropertyDescriptor(Module, 'prototype');
+assertEq(typeof moduleProtoDesc.value, "object");
+assertEq(moduleProtoDesc.writable, false);
+assertEq(moduleProtoDesc.enumerable, false);
+assertEq(moduleProtoDesc.configurable, false);
+
+// 'WebAssembly.Module.prototype' object
+const moduleProto = Module.prototype;
+assertEq(moduleProto, moduleProtoDesc.value);
+assertEq(String(moduleProto), "[object Object]");
+assertEq(Object.getPrototypeOf(moduleProto), Object.prototype);
+
+// 'WebAssembly.Module' instance objects
+const m1 = new Module(emptyModule);
+assertEq(typeof m1, "object");
+assertEq(String(m1), "[object WebAssembly.Module]");
+assertEq(Object.getPrototypeOf(m1), moduleProto);
+
+// 'WebAssembly.Instance' property
+const instanceDesc = Object.getOwnPropertyDescriptor(WebAssembly, 'Instance');
+assertEq(typeof instanceDesc.value, "function");
+assertEq(instanceDesc.writable, true);
+assertEq(instanceDesc.enumerable, false);
+assertEq(instanceDesc.configurable, true);
+
+// 'WebAssembly.Instance' constructor function
+const Instance = WebAssembly.Instance;
+assertEq(Instance, instanceDesc.value);
+assertEq(Instance.length, 1);
+assertEq(Instance.name, "Instance");
+assertErrorMessage(() => Instance(), TypeError, /constructor without new is forbidden/);
+assertErrorMessage(() => new Instance(1), TypeError, "first argument must be a WebAssembly.Module");
+assertErrorMessage(() => new Instance({}), TypeError, "first argument must be a WebAssembly.Module");
+assertErrorMessage(() => new Instance(m1, null), TypeError, "second argument, if present, must be an object");
+assertEq(new Instance(m1) instanceof Instance, true);
+assertEq(new Instance(m1, {}) instanceof Instance, true);
+
+// 'WebAssembly.Instance.prototype' property
+const instanceProtoDesc = Object.getOwnPropertyDescriptor(Instance, 'prototype');
+assertEq(typeof instanceProtoDesc.value, "object");
+assertEq(instanceProtoDesc.writable, false);
+assertEq(instanceProtoDesc.enumerable, false);
+assertEq(instanceProtoDesc.configurable, false);
+
+// 'WebAssembly.Instance.prototype' object
+const instanceProto = Instance.prototype;
+assertEq(instanceProto, instanceProtoDesc.value);
+assertEq(String(instanceProto), "[object Object]");
+assertEq(Object.getPrototypeOf(instanceProto), Object.prototype);
+
+// 'WebAssembly.Instance' instance objects
+const i1 = new Instance(m1);
+assertEq(typeof i1, "object");
+assertEq(String(i1), "[object WebAssembly.Instance]");
+assertEq(Object.getPrototypeOf(i1), instanceProto);
+
+// 'WebAssembly.Instance' 'exports' property
+const exportsDesc = Object.getOwnPropertyDescriptor(i1, 'exports');
+assertEq(typeof exportsDesc.value, "object");
+assertEq(exportsDesc.writable, true);
+assertEq(exportsDesc.enumerable, true);
+assertEq(exportsDesc.configurable, true);
+
+// Exports object:
+// Note: at some point the exports object should become an ES6 module namespace
+// exotic object. For now, don't probe too hard on the property descriptors or
+// the exports object itself.
+
+const e1 = i1.exports;
+assertEq(e1, exportsDesc.value);
+assertEq(Object.keys(e1).length, 0);
+
+var code = wasmTextToBinary('(module (func) (export "foo" 0))');
+var e = new Instance(new Module(code)).exports;
+assertEq(Object.keys(e).join(), "foo");
+assertEq(e.foo(), undefined);
+
+var code = wasmTextToBinary('(module (func) (export "foo" 0) (export "bar" 0))');
+var e = new Instance(new Module(code)).exports;
+assertEq(Object.keys(e).join(), "foo,bar");
+assertEq(e.foo(), undefined);
+assertEq(e.bar(), undefined);
+assertEq(e.foo, e.bar);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -340,17 +340,18 @@ MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL,       1
 MSG_DEF(JSMSG_USE_ASM_LINK_FAIL,       1, JSEXN_TYPEERR, "asm.js link error: {0}")
 MSG_DEF(JSMSG_USE_ASM_TYPE_OK,         1, JSEXN_WARN,    "Successfully compiled asm.js code ({0})")
 
 // wasm
 MSG_DEF(JSMSG_WASM_FAIL,               1, JSEXN_TYPEERR,     "wasm error: {0}")
 MSG_DEF(JSMSG_WASM_DECODE_FAIL,        2, JSEXN_TYPEERR,     "wasm validation error at offset {0}: {1}")
 MSG_DEF(JSMSG_WASM_TEXT_FAIL,          1, JSEXN_SYNTAXERR,   "wasm text error: {0}")
 MSG_DEF(JSMSG_WASM_BAD_IND_CALL,       0, JSEXN_ERR,         "wasm indirect call signature mismatch")
-MSG_DEF(JSMSG_WASM_BAD_BUF_ARG,        0, JSEXN_TYPEERR,     "first argument must be a typed array")
+MSG_DEF(JSMSG_WASM_BAD_BUF_ARG,        0, JSEXN_TYPEERR,     "first argument must be an ArrayBuffer or typed array object")
+MSG_DEF(JSMSG_WASM_BAD_MOD_ARG,        0, JSEXN_TYPEERR,     "first argument must be a WebAssembly.Module")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG,     0, JSEXN_TYPEERR,     "second argument, if present, must be an object")
 MSG_DEF(JSMSG_WASM_UNREACHABLE,        0, JSEXN_ERR,         "unreachable executed")
 MSG_DEF(JSMSG_WASM_INTEGER_OVERFLOW,   0, JSEXN_ERR,         "integer overflow")
 MSG_DEF(JSMSG_WASM_INVALID_CONVERSION, 0, JSEXN_ERR,         "invalid conversion to integer")
 MSG_DEF(JSMSG_WASM_INT_DIVIDE_BY_ZERO, 0, JSEXN_ERR,         "integer divide by zero")
 MSG_DEF(JSMSG_WASM_OVERRECURSED,       0, JSEXN_INTERNALERR, "call stack exhausted")
 
 // Proxy
--- a/js/src/jsprototypes.h
+++ b/js/src/jsprototypes.h
@@ -111,13 +111,14 @@ IF_INTL(real,imaginary) (Intl,          
 IF_BDATA(real,imaginary)(TypedObject,           40,     InitTypedObjectModuleObject,   OCLASP(TypedObjectModule)) \
     real(Reflect,               41,     InitReflect,            nullptr) \
 IF_SIMD(real,imaginary)(SIMD,                   42,     InitSimdClass, OCLASP(Simd)) \
     real(WeakSet,               43,     InitWeakSetClass,       OCLASP(WeakSet)) \
     real(TypedArray,            44,     InitViaClassSpec,       &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
 IF_SAB(real,imaginary)(Atomics, 45,     InitAtomicsClass, OCLASP(Atomics)) \
     real(SavedFrame,            46,     InitViaClassSpec,       &js::SavedFrame::class_) \
     real(Wasm,                  47,     InitWasmClass,          CLASP(Wasm)) \
-IF_PROMISE(real,imaginary)(Promise,             48,     InitViaClassSpec, OCLASP(Promise)) \
+    real(WebAssembly,           48,     InitWebAssemblyClass,   CLASP(WebAssembly)) \
+IF_PROMISE(real,imaginary)(Promise,             49,     InitViaClassSpec, OCLASP(Promise)) \
 
 #define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
 
 #endif /* jsprototypes_h */
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -95,17 +95,17 @@ js::GlobalObject::getTypedObjectModule()
     // only gets called from contexts where TypedObject must be initialized
     MOZ_ASSERT(v.isObject());
     return v.toObject().as<TypedObjectModuleObject>();
 }
 
 /* static */ bool
 GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key)
 {
-    if (key == JSProto_Wasm)
+    if (key == JSProto_Wasm || key == JSProto_WebAssembly)
         return !cx->runtime()->options().wasm();
 
 #ifdef ENABLE_SHARED_ARRAY_BUFFER
     // Return true if the given constructor has been disabled at run-time.
     switch (key) {
       case JSProto_Atomics:
       case JSProto_SharedArrayBuffer:
         return !cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled();