From b5a255ff58ca1c266d82982c1100cde7d09e0491 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Thu, 3 Mar 2016 10:20:21 -0600 Subject: [PATCH] Bug 1252498 - Baldr: add Wasm object behind pref, default off (r=jorendorff) MozReview-Commit-ID: BlhrURAX26H --HG-- extra : rebase_source : e1b540f06c2f3976f91242ac7b0b8ede29fbc5f2 --- dom/workers/RuntimeService.cpp | 1 + js/src/asmjs/Wasm.cpp | 77 ++++++++++++++++++++++++++++ js/src/asmjs/Wasm.h | 19 +++++-- js/src/builtin/TestingFunctions.cpp | 42 +-------------- js/src/jit-test/lib/wasm.js | 2 +- js/src/jit-test/tests/wasm/basic.js | 4 +- js/src/jit-test/tests/wasm/binary.js | 2 + js/src/js.msg | 2 + js/src/jsapi.h | 12 +++++ js/src/jsprototypes.h | 7 +-- js/src/shell/js.cpp | 2 + js/src/vm/GlobalObject.cpp | 4 ++ js/src/vm/Xdr.h | 4 +- js/xpconnect/src/XPCJSRuntime.cpp | 2 + js/xpconnect/src/XPCShellImpl.cpp | 3 +- modules/libpref/init/all.js | 1 + 16 files changed, 130 insertions(+), 54 deletions(-) diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 19b9aad7c236..d619cbbb00e6 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -301,6 +301,7 @@ LoadRuntimeOptions(const char* aPrefName, void* /* aClosure */) // Runtime options. JS::RuntimeOptions runtimeOptions; runtimeOptions.setAsmJS(GetWorkerPref(NS_LITERAL_CSTRING("asmjs"))) + .setWasm(GetWorkerPref(NS_LITERAL_CSTRING("wasm"))) .setThrowOnAsmJSValidationFailure(GetWorkerPref( NS_LITERAL_CSTRING("throw_on_asmjs_validation_failure"))) .setBaseline(GetWorkerPref(NS_LITERAL_CSTRING("baselinejit"))) diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp index ddcfd35fa4ac..358e5928ec38 100644 --- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -1333,3 +1333,80 @@ wasm::Eval(JSContext* cx, Handle code, return true; } + +static bool +InstantiateModule(JSContext* cx, unsigned argc, Value* vp) +{ + MOZ_ASSERT(cx->runtime()->options().wasm()); + CallArgs args = CallArgsFromVp(argc, vp); + + if (!args.get(0).isObject() || !args.get(0).toObject().is()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG); + return false; + } + + RootedObject importObj(cx); + if (!args.get(1).isUndefined()) { + if (!args.get(1).isObject()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMPORT_ARG); + return false; + } + importObj = &args[1].toObject(); + } + + Rooted code(cx, &args[0].toObject().as()); + + RootedObject exportObj(cx); + if (!Eval(cx, code, importObj, &exportObj)) + return false; + + args.rval().setObject(*exportObj); + return true; +} + +#if JS_HAS_TOSOURCE +static bool +wasm_toSource(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + args.rval().setString(cx->names().Wasm); + return true; +} +#endif + +static JSFunctionSpec wasm_static_methods[] = { +#if JS_HAS_TOSOURCE + JS_FN(js_toSource_str, wasm_toSource, 0, 0), +#endif + JS_FN("instantiateModule", InstantiateModule, 1, 0), + JS_FS_END +}; + +const Class js::WasmClass = { + js_Wasm_str, + JSCLASS_HAS_CACHED_PROTO(JSProto_Wasm) +}; + +JSObject* +js::InitWasmClass(JSContext* cx, HandleObject global) +{ + MOZ_ASSERT(cx->runtime()->options().wasm()); + + RootedObject proto(cx, global->as().getOrCreateObjectPrototype(cx)); + if (!proto) + return nullptr; + + RootedObject Wasm(cx, NewObjectWithGivenProto(cx, &WasmClass, proto, SingletonObject)); + if (!Wasm) + return nullptr; + + if (!JS_DefineProperty(cx, global, js_Wasm_str, Wasm, JSPROP_RESOLVING)) + return nullptr; + + if (!JS_DefineFunctions(cx, Wasm, wasm_static_methods)) + return nullptr; + + global->as().setConstructor(JSProto_Wasm, ObjectValue(*Wasm)); + return Wasm; +} + diff --git a/js/src/asmjs/Wasm.h b/js/src/asmjs/Wasm.h index 6c131575cee6..04a0cb13a925 100644 --- a/js/src/asmjs/Wasm.h +++ b/js/src/asmjs/Wasm.h @@ -19,7 +19,10 @@ #ifndef wasm_h #define wasm_h +#include "NamespaceImports.h" + #include "gc/Rooting.h" +#include "js/Class.h" namespace js { @@ -31,8 +34,8 @@ namespace wasm { bool HasCompilerSupport(ExclusiveContext* cx); -// The WebAssembly spec hard-codes the virtual page size to be 64KiB and limits -// forces the linear memory to always be a multiple of 64KiB. +// The WebAssembly spec hard-codes the virtual page size to be 64KiB and +// requires linear memory to always be a multiple of 64KiB. static const unsigned PageSize = 64 * 1024; // When signal handling is used for bounds checking, MappedSize bytes are @@ -46,10 +49,18 @@ static const uint64_t MappedSize = 2 * Uint32Range + PageSize; // Compiles the given binary wasm module given the ArrayBufferObject // and links the module's imports with the given import object. bool -Eval(JSContext* cx, JS::Handle code, - JS::HandleObject importObj, JS::MutableHandleObject exportObj); +Eval(JSContext* cx, Handle code, HandleObject importObj, + MutableHandleObject exportObj); } // namespace wasm + +// Initialization of the Wasm global object and its properties. + +extern const Class WasmClass; + +JSObject* +InitWasmClass(JSContext* cx, HandleObject global); + } // namespace js #endif // namespace wasm_h diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 096d4173544c..b4f1543821bf 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -501,42 +501,7 @@ static bool WasmIsSupported(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - args.rval().setBoolean(wasm::HasCompilerSupport(cx)); - return true; -} - -static bool -WasmEval(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - RootedObject callee(cx, &args.callee()); - - if (args.length() < 1 || args.length() > 2) { - ReportUsageError(cx, callee, "Wrong number of arguments"); - return false; - } - - if (!args[0].isObject() || !args[0].toObject().is()) { - ReportUsageError(cx, callee, "First argument must be an ArrayBuffer"); - return false; - } - - RootedObject importObj(cx); - if (!args.get(1).isUndefined()) { - if (!args.get(1).isObject()) { - ReportUsageError(cx, callee, "Second argument, if present, must be an Object"); - return false; - } - importObj = &args[1].toObject(); - } - - Rooted code(cx, &args[0].toObject().as()); - - RootedObject exportObj(cx); - if (!wasm::Eval(cx, code, importObj, &exportObj)) - return false; - - args.rval().setObject(*exportObj); + args.rval().setBoolean(wasm::HasCompilerSupport(cx) && cx->runtime()->options().wasm()); return true; } @@ -3711,11 +3676,6 @@ gc::ZealModeHelpText), "wasmIsSupported()", " Returns a boolean indicating whether WebAssembly is supported on the current device."), - JS_FN_HELP("wasmEval", WasmEval, 2, 0, -"wasmEval(buffer, imports)", -" Compiles the given binary wasm module given by 'buffer' (which must be an ArrayBuffer)\n" -" and links the module's imports with the given 'imports' object."), - JS_FN_HELP("wasmTextToBinary", WasmTextToBinary, 1, 0, "wasmTextToBinary(str)", " Translates the given text wasm module into its binary encoding."), diff --git a/js/src/jit-test/lib/wasm.js b/js/src/jit-test/lib/wasm.js index 14ddb85a5353..55da91ead6ea 100644 --- a/js/src/jit-test/lib/wasm.js +++ b/js/src/jit-test/lib/wasm.js @@ -4,7 +4,7 @@ if (!wasmIsSupported()) load(libdir + "asserts.js"); function wasmEvalText(str, imports) { - return wasmEval(wasmTextToBinary(str), imports); + return Wasm.instantiateModule(wasmTextToBinary(str), imports); } function mismatchError(actual, expect) { diff --git a/js/src/jit-test/tests/wasm/basic.js b/js/src/jit-test/tests/wasm/basic.js index 5883b336761f..24495eac9378 100644 --- a/js/src/jit-test/tests/wasm/basic.js +++ b/js/src/jit-test/tests/wasm/basic.js @@ -96,8 +96,8 @@ if (!hasI64) { // ---------------------------------------------------------------------------- // imports -assertErrorMessage(() => wasmEvalText('(module (import "a" "b"))', 1), Error, /Second argument, if present, must be an Object/); -assertErrorMessage(() => wasmEvalText('(module (import "a" "b"))', null), Error, /Second argument, if present, must be an Object/); +assertErrorMessage(() => wasmEvalText('(module (import "a" "b"))', 1), Error, /second argument, if present, must be an object/); +assertErrorMessage(() => wasmEvalText('(module (import "a" "b"))', null), Error, /second argument, if present, must be an object/); const noImportObj = /no import object given/; const notObject = /import object field is not an Object/; diff --git a/js/src/jit-test/tests/wasm/binary.js b/js/src/jit-test/tests/wasm/binary.js index d80aff4e6803..8c56f3afa210 100644 --- a/js/src/jit-test/tests/wasm/binary.js +++ b/js/src/jit-test/tests/wasm/binary.js @@ -53,6 +53,8 @@ function moduleHeaderThen(...rest) { return [magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, ...rest]; } +const wasmEval = Wasm.instantiateModule; + assertErrorMessage(() => wasmEval(toBuf([])), TypeError, magicError); assertErrorMessage(() => wasmEval(toBuf([42])), TypeError, magicError); assertErrorMessage(() => wasmEval(toBuf([magic0, magic1, magic2])), TypeError, magicError); diff --git a/js/src/js.msg b/js/src/js.msg index 6189a6efbe92..750c8d76fdc3 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -366,6 +366,8 @@ 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 an ArrayBuffer") +MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG, 0, JSEXN_TYPEERR, "second argument, if present, must be an object") // Proxy MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value") diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 8de21d68a9be..e418f245edbb 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1109,6 +1109,7 @@ class JS_PUBLIC_API(RuntimeOptions) { : baseline_(true), ion_(true), asmJS_(true), + wasm_(false), throwOnAsmJSValidationFailure_(false), nativeRegExp_(true), unboxedArrays_(false), @@ -1156,6 +1157,16 @@ class JS_PUBLIC_API(RuntimeOptions) { return *this; } + bool wasm() const { return wasm_; } + RuntimeOptions& setWasm(bool flag) { + wasm_ = flag; + return *this; + } + RuntimeOptions& toggleWasm() { + wasm_ = !wasm_; + return *this; + } + bool throwOnAsmJSValidationFailure() const { return throwOnAsmJSValidationFailure_; } RuntimeOptions& setThrowOnAsmJSValidationFailure(bool flag) { throwOnAsmJSValidationFailure_ = flag; @@ -1236,6 +1247,7 @@ class JS_PUBLIC_API(RuntimeOptions) { bool baseline_ : 1; bool ion_ : 1; bool asmJS_ : 1; + bool wasm_ : 1; bool throwOnAsmJSValidationFailure_ : 1; bool nativeRegExp_ : 1; bool unboxedArrays_ : 1; diff --git a/js/src/jsprototypes.h b/js/src/jsprototypes.h index d4d1841d4e1c..744fa2518d84 100644 --- a/js/src/jsprototypes.h +++ b/js/src/jsprototypes.h @@ -106,9 +106,10 @@ IF_BDATA(real,imaginary)(TypedObject, 40, InitTypedObjectModuleObj 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(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)) \ #define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro) diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 5ea3a4040f60..ce636710d4ef 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -6586,6 +6586,7 @@ SetRuntimeOptions(JSRuntime* rt, const OptionParser& op) JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline) .setIon(enableIon) .setAsmJS(enableAsmJS) + .setWasm(true) .setNativeRegExp(enableNativeRegExp) .setUnboxedArrays(enableUnboxedArrays); @@ -6843,6 +6844,7 @@ SetWorkerRuntimeOptions(JSRuntime* rt) JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline) .setIon(enableIon) .setAsmJS(enableAsmJS) + .setWasm(true) .setNativeRegExp(enableNativeRegExp) .setUnboxedArrays(enableUnboxedArrays); rt->setOffthreadIonCompilationEnabled(offthreadCompilation); diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index e1602f4aaf9e..c679505d2627 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -15,6 +15,7 @@ #include "jsprototypes.h" #include "jsweakmap.h" +#include "asmjs/Wasm.h" #include "builtin/AtomicsObject.h" #include "builtin/Eval.h" #if EXPOSE_INTL_API @@ -94,6 +95,9 @@ js::GlobalObject::getTypedObjectModule() const { /* static */ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) { + if (key == JSProto_Wasm) + return !cx->runtime()->options().wasm(); + #ifdef ENABLE_SHARED_ARRAY_BUFFER // Return true if the given constructor has been disabled at run-time. switch (key) { diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 42e6f164454e..c8900aba75e7 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -31,11 +31,11 @@ namespace js { * * (If you're wondering, 0xb973c0de is used because it looks like "bytecode".) */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 348; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 349; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); -static_assert(JSErr_Limit == 445, +static_assert(JSErr_Limit == 447, "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or " "removed MSG_DEFs from js.msg, you should increment " "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's " diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 5cdc693ca7cb..12a5217673cf 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1542,6 +1542,7 @@ ReloadPrefsCallback(const char* pref, void* data) bool useBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "baselinejit") && !safeMode; bool useIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion") && !safeMode; bool useAsmJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "asmjs") && !safeMode; + bool useWasm = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm") && !safeMode; bool throwOnAsmJSValidationFailure = Preferences::GetBool(JS_OPTIONS_DOT_STR "throw_on_asmjs_validation_failure"); bool useNativeRegExp = Preferences::GetBool(JS_OPTIONS_DOT_STR "native_regexp") && !safeMode; @@ -1576,6 +1577,7 @@ ReloadPrefsCallback(const char* pref, void* data) JS::RuntimeOptionsRef(rt).setBaseline(useBaseline) .setIon(useIon) .setAsmJS(useAsmJS) + .setWasm(useWasm) .setThrowOnAsmJSValidationFailure(throwOnAsmJSValidationFailure) .setNativeRegExp(useNativeRegExp) .setAsyncStack(useAsyncStack) diff --git a/js/xpconnect/src/XPCShellImpl.cpp b/js/xpconnect/src/XPCShellImpl.cpp index b0ea72924b13..a20e2f72ccb4 100644 --- a/js/xpconnect/src/XPCShellImpl.cpp +++ b/js/xpconnect/src/XPCShellImpl.cpp @@ -984,7 +984,8 @@ ProcessArgsForCompartment(JSContext* cx, char** argv, int argc) break; case 'I': RuntimeOptionsRef(cx).toggleIon() - .toggleAsmJS(); + .toggleAsmJS() + .toggleWasm(); break; } } diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index b765cb10dbcf..b5754e97af4d 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1143,6 +1143,7 @@ pref("javascript.options.strict.debug", false); pref("javascript.options.baselinejit", true); pref("javascript.options.ion", true); pref("javascript.options.asmjs", true); +pref("javascript.options.wasm", false); pref("javascript.options.native_regexp", true); pref("javascript.options.parallel_parsing", true); #if !defined(RELEASE_BUILD) && !defined(ANDROID) && !defined(MOZ_B2G) && !defined(XP_IOS)