From e797853ee6b3edec4739dfc5441ebcf702b62d24 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Wed, 10 Apr 2024 10:06:21 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1259877 - Update DoCallFallback to use js::CallFromStack. r=jandem (16a263ebc8) - Bug 1259877 - Intl.cpp:GetInternals should directly return its result, not go via outparam. r=till (6e3f5a508f) - Bug 1165052 - Part 7: Implement native ArraySpeciesCreate wrapper. r=efaust (3b113b8862) - Bug 1233862 - Add a WeakCache builtin to automatically manage sweeping; r=sfink (e4a63eeae3) - Bug 1198701 - ArrayIterator gets length property after iteration has finished. r=till (e91e514ce7) - Bug 1254853 - Add a Helper to Report a ScriptError to the Main Thread. r=khuey (4fb04c563e) - Bug 1259294: Part 1 - Add MOZ_ALWAYS_SUCCEEDS. r=froydnj (646513400c) - Bug 1257758 - Adjust step numbering in scripted proxy code to be ES6-correct. r=evilpie (bbd060f0d0) - Bug 1265594 - Expose FromPropertyDescriptor() as a public JS API; r=jorendorff (b84183bdbd) - reapply Bug 1258163 - Implement Array.prototype[@@unscopables]. r=shu, r=bholley. (7790e0e824) - Bug 1253371 - make atomics range checking conformant. r=jolesen (8830769fd8) - Bug 1253351 - count argument of futexWake defaults to +Infinity. r=bbouvier (e6da823106) - Bug 1259544 - remove futexWakeOrRequeue. r=bbouvier (35e922f333) - Bug 1260696: Update wasm text format spewing; r=luke (62b1f8577a) - Bug 1260737: wasm: Implement Reinterpret opcodes; r=luke (d3a6117e59) - Bug 1225028 - remove Atomics.fence. r=bbouvier (51d9f55f83) - Bug 1253344: Defer br/br_if/br_table then-block definition to avoid indirections; r=sunfish (e28b024386) - Bug 1254142: Baldr: make control flow opcodes yield subexpressions; r=luke (b82d9c89fc) - Bug 1260910 - introduce 'wait' and 'wake'. r=bbouvier (cd3e94119f) - Bug 1260835 - Atomics.wait returns strings + remove symbolic constants. r=jolesen (33ef0d61d8) - Bug 1041586 - Autogenerate symbol names. r=jorendorff (12fa06b6a5) - Bug 1241088: SharedStubs - part 1: move NewArray and NewObject baseline stubs to shared stubs, r=efaust (846d537ca8) - Bug 1241088: SharedStubs - part 2: port NewArray and NewObject shared stubs to work in ion, r=efaust (c073756376) - Bug 1246061. r=jandem, r=bz, r=luke, r=froydnj (c065b403d4) - Bug 1263870: Check allocation in WasmAstModule::declare; r=luke (d3b2a2acd7) - Bug 1242040. Fix the GlobalObject usage in ExtendableEvent::GetPromise. r=bkelly (d9e284ad62) - Bug 1255832. Propagate out JS exceptions properly from some methods in dom/mobilemessage. r=bholley (8c8d9ce3db) - Bug 1255817 part 2. Get rid of AutoJSAPI::OwnsErrorReporting and AutoJSAPI::TakeOwnershipOfErrorReporting. r=bholley (4f1c052fe6) - Bug 1259936: Baldr: Simplify calls stack bytes management; r=luke (30c58b319f) - Bug 1259936: Extra test case; r=luke (116079c082) - Bug 1261577: Always set the result of a dead loop in wasm; r=luke (67014be844) - Bug 1261404: Ensure BrTable only set successors once; r=sunfish (6989814189) - Bug 875433 - Implement Array.prototype.values. r=jorendorff. (423fa799dc) - Bug 1250947. Fill area outside blur with solid color. r=mstange (bc54b70a1c) - Bug 1259578 - Use GC infrastructure to allocate proxy's malloced blob; r=jonco (fa28d8f8c4) - Bug 1259580 - Hide as many Proxy details as possible behind a detail namespace; r=efaust (86bed5cab5) - Bug 1215265 - Shut PImageBridge down properly. r=sotaro (f387f89418) - Bug 1215265 - Shut PCompositorBridge down properly. r=sotaro (a573020692) - Bug 1262367: Baldr: Rename "trap" to "unreachable"; r=luke (2f9d10ade7) - Bug 1263884 - Don't report OOM when speculative shape table shrink fails r=jandem (cabd97f681) - Bug 1259877 - Update miscellaneous code to use js::Call instead of js::Invoke. r=till (a2f417c8a1) - Bug 1262402: Add i64 testing infrastructure in Baldr; r=luke (d9d82c0acb) --- dom/base/ScriptSettings.cpp | 7 - dom/base/ScriptSettings.h | 9 +- dom/base/WebSocket.cpp | 1 - dom/base/nsContentUtils.cpp | 4 - dom/base/nsFrameLoader.cpp | 2 +- dom/base/nsJSEnvironment.cpp | 1 - dom/base/nsJSUtils.cpp | 1 - dom/base/nsScriptLoader.cpp | 1 - dom/bindings/CallbackObject.cpp | 1 - dom/bindings/Codegen.py | 1 - dom/bindings/Exceptions.cpp | 1 - dom/canvas/WebGLContextUtils.cpp | 2 - dom/events/EventListenerManager.cpp | 1 - dom/indexedDB/IDBDatabase.cpp | 150 +----- dom/indexedDB/IDBDatabase.h | 3 - dom/indexedDB/IDBRequest.cpp | 1 - dom/indexedDB/IndexedDatabaseManager.cpp | 46 +- dom/indexedDB/ScriptErrorHelper.cpp | 249 +++++++++ dom/indexedDB/ScriptErrorHelper.h | 41 ++ dom/indexedDB/moz.build | 1 + dom/mobilemessage/MobileMessageCallback.cpp | 8 +- dom/mobilemessage/MobileMessageManager.cpp | 3 +- dom/plugins/base/nsJSNPRuntime.cpp | 1 - dom/promise/Promise.cpp | 2 - dom/workers/ScriptLoader.cpp | 1 - dom/workers/ServiceWorkerEvents.cpp | 12 +- dom/workers/WorkerPrivate.cpp | 1 - dom/workers/WorkerRunnable.cpp | 1 - dom/workers/XMLHttpRequest.cpp | 1 - dom/xbl/nsXBLProtoImpl.cpp | 1 - dom/xbl/nsXBLPrototypeHandler.cpp | 1 - gfx/layers/Compositor.h | 1 + gfx/layers/basic/BasicCompositor.cpp | 8 +- gfx/layers/basic/BasicCompositor.h | 4 +- gfx/layers/d3d11/CompositorD3D11.h | 1 + gfx/layers/d3d9/CompositorD3D9.h | 1 + gfx/layers/ipc/CompositorBridgeChild.cpp | 92 ++-- gfx/layers/ipc/CompositorBridgeChild.h | 12 +- gfx/layers/ipc/CompositorBridgeParent.cpp | 99 ++-- gfx/layers/ipc/CompositorBridgeParent.h | 8 +- gfx/layers/ipc/ImageBridgeChild.cpp | 4 +- gfx/layers/ipc/ImageBridgeParent.cpp | 51 +- gfx/layers/ipc/ImageBridgeParent.h | 8 +- gfx/layers/ipc/PCompositorBridge.ipdl | 5 +- gfx/layers/ipc/PImageBridge.ipdl | 11 +- gfx/layers/opengl/CompositorOGL.h | 2 + gfx/thebes/gfxBlur.cpp | 376 ++++++------- gfx/thebes/gfxBlur.h | 16 +- gfx/thebes/gfxPlatform.cpp | 30 +- js/ipc/WrapperAnswer.cpp | 17 - js/public/GCHashTable.h | 37 +- js/public/GCPolicyAPI.h | 7 +- js/public/HashTable.h | 8 +- js/public/Proxy.h | 39 +- js/public/SweepingAPI.h | 62 +++ js/public/TracingAPI.h | 1 + js/src/asmjs/AsmJS.cpp | 15 - js/src/asmjs/Wasm.cpp | 45 +- js/src/asmjs/WasmBinary.h | 1 - js/src/asmjs/WasmBinaryToText.cpp | 179 +++++-- js/src/asmjs/WasmIonCompile.cpp | 498 +++++++++--------- js/src/asmjs/WasmModule.cpp | 105 +++- js/src/asmjs/WasmModule.h | 2 +- js/src/asmjs/WasmStubs.cpp | 106 +++- js/src/asmjs/WasmTextToBinary.cpp | 75 ++- js/src/asmjs/WasmTypes.cpp | 55 +- js/src/asmjs/WasmTypes.h | 5 + js/src/builtin/Array.js | 32 +- js/src/builtin/AtomicsObject.cpp | 199 ++----- js/src/builtin/AtomicsObject.h | 39 +- js/src/builtin/Intl.cpp | 43 +- js/src/builtin/Object.cpp | 14 +- js/src/builtin/SIMD.cpp | 69 +-- js/src/builtin/SymbolObject.cpp | 2 +- js/src/builtin/TestingFunctions.cpp | 6 +- js/src/builtin/TypedObject.cpp | 24 +- js/src/gc/Allocator.cpp | 2 +- js/src/gc/Tracer.h | 1 - js/src/gc/Zone.cpp | 6 + js/src/gc/Zone.h | 6 + js/src/jit-test/tests/asm.js/testAtomics.js | 21 +- js/src/jit-test/tests/atomics/basic-tests.js | 17 +- js/src/jit-test/tests/atomics/inline-fence.js | 31 -- js/src/jit-test/tests/gc/bug-1263884.js | 10 + .../jit-test/tests/wasm/basic-control-flow.js | 122 ++++- .../jit-test/tests/wasm/basic-conversion.js | 10 +- .../tests/wasm/random-control-flow.js | 113 ++++ js/src/jit/BaselineCompiler.cpp | 8 +- js/src/jit/BaselineIC.cpp | 155 +----- js/src/jit/BaselineIC.h | 92 ---- js/src/jit/CodeGenerator.cpp | 57 +- js/src/jit/CodeGenerator.h | 1 + js/src/jit/InlinableNatives.h | 1 - js/src/jit/IonAnalysis.cpp | 2 + js/src/jit/IonBuilder.cpp | 237 +++++++-- js/src/jit/IonBuilder.h | 12 +- js/src/jit/IonCaches.cpp | 4 +- js/src/jit/JitOptions.cpp | 4 + js/src/jit/JitOptions.h | 1 + js/src/jit/Lowering.cpp | 29 +- js/src/jit/Lowering.h | 3 +- js/src/jit/MCallOptimize.cpp | 51 +- js/src/jit/MIR.cpp | 47 +- js/src/jit/MIR.h | 141 +++-- js/src/jit/MIRGenerator.h | 17 +- js/src/jit/MIRGraph.cpp | 2 +- js/src/jit/MOpcodes.h | 3 +- js/src/jit/MacroAssembler.h | 7 + js/src/jit/RegisterSets.h | 7 + js/src/jit/Registers.h | 8 + js/src/jit/SharedIC.cpp | 167 +++++- js/src/jit/SharedIC.h | 105 +++- js/src/jit/arm/Assembler-arm.h | 13 + js/src/jit/arm/CodeGenerator-arm.cpp | 26 + js/src/jit/arm/CodeGenerator-arm.h | 1 + js/src/jit/arm/MacroAssembler-arm-inl.h | 26 + js/src/jit/arm/MacroAssembler-arm.h | 5 - js/src/jit/arm64/Assembler-arm64.h | 13 + js/src/jit/arm64/MacroAssembler-arm64-inl.h | 21 + js/src/jit/arm64/MacroAssembler-arm64.h | 9 - .../jit/mips-shared/Assembler-mips-shared.h | 12 + js/src/jit/mips32/MacroAssembler-mips32-inl.h | 26 + js/src/jit/mips64/MacroAssembler-mips64-inl.h | 22 + js/src/jit/mips64/MacroAssembler-mips64.h | 5 - js/src/jit/none/MacroAssembler-none.h | 4 +- js/src/jit/shared/Assembler-shared.h | 11 + js/src/jit/shared/CodeGenerator-shared.cpp | 2 +- js/src/jit/shared/LIR-shared.h | 59 ++- js/src/jit/shared/LOpcodes-shared.h | 4 + js/src/jit/shared/Lowering-shared-inl.h | 2 +- js/src/jit/x64/Assembler-x64.h | 1 + js/src/jit/x64/CodeGenerator-x64.cpp | 16 + js/src/jit/x64/CodeGenerator-x64.h | 2 + js/src/jit/x64/MacroAssembler-x64-inl.h | 22 + js/src/jit/x64/MacroAssembler-x64.h | 5 - js/src/jit/x86-shared/Assembler-x86-shared.h | 12 + .../x86-shared/CodeGenerator-x86-shared.cpp | 31 +- .../jit/x86-shared/CodeGenerator-x86-shared.h | 1 + js/src/jit/x86/Assembler-x86.h | 1 + js/src/jit/x86/MacroAssembler-x86-inl.h | 26 + js/src/js.msg | 1 - js/src/jsapi-tests/moz.build | 1 + js/src/jsapi-tests/testGCWeakCache.cpp | 92 ++++ .../jsapi-tests/testGetPropertyDescriptor.cpp | 18 + js/src/jsapi.cpp | 5 + js/src/jsapi.h | 13 +- js/src/jsarray.cpp | 24 +- js/src/jsarray.h | 3 - js/src/jsatom.cpp | 6 + js/src/jsfriendapi.h | 4 +- js/src/jsfun.cpp | 50 +- js/src/jsgc.cpp | 5 + js/src/jsiter.cpp | 21 +- js/src/jsnum.cpp | 49 ++ js/src/jsnum.h | 20 + js/src/jsobj.cpp | 21 +- js/src/jsobj.h | 12 +- js/src/jsobjinlines.h | 12 +- js/src/jsstr.cpp | 12 +- js/src/moz.build | 3 +- js/src/proxy/DirectProxyHandler.cpp | 7 +- js/src/proxy/Proxy.cpp | 2 +- js/src/proxy/ScriptedDirectProxyHandler.cpp | 461 ++++++++-------- js/src/shell/js.cpp | 8 +- .../tests/ecma_6/Array/iterator_edge_cases.js | 23 + js/src/tests/ecma_6/Array/unscopables.js | 51 ++ js/src/tests/ecma_6/Array/values.js | 18 + js/src/tests/shell/futex-apis.js | 23 +- js/src/tests/shell/futex.js | 70 ++- js/src/vm/CommonPropertyNames.h | 26 +- js/src/vm/Debugger.cpp | 5 +- js/src/vm/GeneratorObject.cpp | 10 +- js/src/vm/Interpreter.cpp | 4 +- js/src/vm/Interpreter.h | 20 +- js/src/vm/ObjectGroup.h | 1 + js/src/vm/ProxyObject.cpp | 14 +- js/src/vm/ProxyObject.h | 14 +- js/src/vm/Runtime.cpp | 2 +- js/src/vm/Runtime.h | 18 +- js/src/vm/SelfHosting.cpp | 9 +- js/src/vm/Shape.cpp | 18 +- js/src/vm/Shape.h | 10 +- js/xpconnect/loader/mozJSComponentLoader.cpp | 4 - js/xpconnect/tests/chrome/test_xrayToJS.xul | 13 +- ...xshadow-inset-large-border-radius-ref.html | 17 + .../boxshadow-inset-large-border-radius.html | 35 ++ .../boxshadow-inset-large-offset-ref.html | 23 + .../boxshadow-inset-large-offset.html | 26 + layout/reftests/box-shadow/reftest.list | 4 + widget/nsBaseWidget.cpp | 2 + xpcom/base/nsError.h | 2 + 191 files changed, 3851 insertions(+), 2308 deletions(-) create mode 100644 dom/indexedDB/ScriptErrorHelper.cpp create mode 100644 dom/indexedDB/ScriptErrorHelper.h create mode 100644 js/public/SweepingAPI.h delete mode 100644 js/src/jit-test/tests/atomics/inline-fence.js create mode 100644 js/src/jit-test/tests/gc/bug-1263884.js create mode 100644 js/src/jsapi-tests/testGCWeakCache.cpp create mode 100644 js/src/tests/ecma_6/Array/unscopables.js create mode 100644 js/src/tests/ecma_6/Array/values.js create mode 100644 layout/reftests/box-shadow/boxshadow-inset-large-border-radius-ref.html create mode 100644 layout/reftests/box-shadow/boxshadow-inset-large-border-radius.html create mode 100644 layout/reftests/box-shadow/boxshadow-inset-large-offset-ref.html create mode 100644 layout/reftests/box-shadow/boxshadow-inset-large-offset.html diff --git a/dom/base/ScriptSettings.cpp b/dom/base/ScriptSettings.cpp index cd75830fa2..11491776be 100644 --- a/dom/base/ScriptSettings.cpp +++ b/dom/base/ScriptSettings.cpp @@ -549,11 +549,6 @@ WarningOnlyErrorReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aR xpcReport->LogToConsole(); } -void -AutoJSAPI::TakeOwnershipOfErrorReporting() -{ -} - void AutoJSAPI::ReportException() { @@ -657,8 +652,6 @@ AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject, if (aIsMainThread && gRunToCompletionListeners > 0) { mDocShellEntryMonitor.emplace(cx(), aReason); } - - TakeOwnershipOfErrorReporting(); } AutoEntryScript::AutoEntryScript(JSObject* aObject, diff --git a/dom/base/ScriptSettings.h b/dom/base/ScriptSettings.h index f003cbb19e..9f6f33cb24 100644 --- a/dom/base/ScriptSettings.h +++ b/dom/base/ScriptSettings.h @@ -267,14 +267,7 @@ public: bool CxPusherIsStackTop() const { return mCxPusher->IsStackTop(); } - // We're moving towards a world where the AutoJSAPI always handles - // exceptions that bubble up from the JS engine. In order to make this - // process incremental, we allow consumers to opt-in to the new behavior - // while keeping the old behavior as the default. - void TakeOwnershipOfErrorReporting(); - bool OwnsErrorReporting() { return true; } - // If HasException, report it. Otherwise, a no-op. This must be - // called only if OwnsErrorReporting(). + // If HasException, report it. Otherwise, a no-op. void ReportException(); bool HasException() const { diff --git a/dom/base/WebSocket.cpp b/dom/base/WebSocket.cpp index e8b7d6cfe2..cc64f1f738 100644 --- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -1891,7 +1891,6 @@ WebSocket::CreateAndDispatchMessageEvent(const nsACString& aData, } } - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); nsresult rv = CheckInnerWindowCorrectness(); diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 640430a875..07304fc2cf 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -6697,10 +6697,6 @@ nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern, jsapi.Init(); JSContext* cx = jsapi.cx(); - // Failure to create or run the regexp results in the invalid pattern - // matching, but we can still report the error to the console. - jsapi.TakeOwnershipOfErrorReporting(); - // We can use the junk scope here, because we're just using it for // regexp evaluation, not actual script execution. JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope()); diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 2eef06ea4b..e82df29275 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -1687,7 +1687,7 @@ nsFrameLoader::ShouldUseRemoteProcess() // Don't try to launch nested children if we don't have OMTC. // They won't render! if (XRE_IsContentProcess() && - !CompositorBridgeChild::ChildProcessHasCompositor()) { + !CompositorBridgeChild::ChildProcessHasCompositorBridge()) { return false; } diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index abbe378a63..5d994b8b6d 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -842,7 +842,6 @@ nsJSContext::SetProperty(JS::Handle aTarget, const char* aPropName, n if (NS_WARN_IF(!jsapi.Init(GetGlobalObject()))) { return NS_ERROR_FAILURE; } - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); JS::AutoValueVector args(cx); diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp index 5c914d8804..12220a5d55 100644 --- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -97,7 +97,6 @@ nsJSUtils::CompileFunction(AutoJSAPI& jsapi, const nsAString& aBody, JSObject** aFunctionObject) { - MOZ_ASSERT(jsapi.OwnsErrorReporting()); JSContext* cx = jsapi.cx(); MOZ_ASSERT(js::GetEnterCompartmentDepth(cx) > 0); MOZ_ASSERT_IF(aScopeChain.length() != 0, diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index e473e9edaf..01251db434 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -852,7 +852,6 @@ nsScriptLoader::AttemptAsyncScriptCompile(nsScriptLoadRequest* aRequest) if (!jsapi.Init(globalObject)) { return NS_ERROR_FAILURE; } - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); JS::Rooted global(cx, globalObject->GetGlobalJSObject()); diff --git a/dom/bindings/CallbackObject.cpp b/dom/bindings/CallbackObject.cpp index 92b0855db8..04d418d403 100644 --- a/dom/bindings/CallbackObject.cpp +++ b/dom/bindings/CallbackObject.cpp @@ -245,7 +245,6 @@ CallbackObject::CallSetup::~CallSetup() if ((mCompartment && mExceptionHandling == eRethrowContentExceptions) || mExceptionHandling == eRethrowExceptions) { mErrorResult.MightThrowJSException(); - MOZ_ASSERT(mAutoEntryScript->OwnsErrorReporting()); if (needToDealWithException) { JS::Rooted exn(mCx); if (mAutoEntryScript->PeekException(&exn) && diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 7dc043b24a..11d0cbdb99 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -15765,7 +15765,6 @@ class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember): MOZ_ASSERT(self); AutoJSAPI jsapi; jsapi.Init(); - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here because // all we want is to wrap into _some_ scope and then unwrap to find diff --git a/dom/bindings/Exceptions.cpp b/dom/bindings/Exceptions.cpp index f90b63911a..6757ec964e 100644 --- a/dom/bindings/Exceptions.cpp +++ b/dom/bindings/Exceptions.cpp @@ -142,7 +142,6 @@ ThrowAndReport(nsPIDOMWindow* aWindow, nsresult aRv) if (NS_WARN_IF(!jsapi.Init(aWindow))) { return; } - jsapi.TakeOwnershipOfErrorReporting(); Throw(jsapi.cx(), aRv); } diff --git a/dom/canvas/WebGLContextUtils.cpp b/dom/canvas/WebGLContextUtils.cpp index 162205909f..8cc4bdece0 100644 --- a/dom/canvas/WebGLContextUtils.cpp +++ b/dom/canvas/WebGLContextUtils.cpp @@ -89,8 +89,6 @@ WebGLContext::GenerateWarning(const char* fmt, va_list ap) return; } - api.TakeOwnershipOfErrorReporting(); - JSContext* cx = api.cx(); JS_ReportWarning(cx, "WebGL: %s", buf); if (!ShouldGenerateWarnings()) { diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp index a967d1fa7b..85ccc7da1a 100644 --- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -910,7 +910,6 @@ EventListenerManager::CompileEventHandlerInternal(Listener* aListener, if (NS_WARN_IF(!jsapi.Init(global))) { return NS_ERROR_UNEXPECTED; } - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); nsCOMPtr typeAtom = aListener->mTypeAtom; diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index 57825c2bda..c74fd32d4d 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -39,8 +39,6 @@ #include "mozilla/ipc/InputStreamParams.h" #include "mozilla/ipc/InputStreamUtils.h" #include "nsCOMPtr.h" -#include "nsContentUtils.h" -#include "nsIConsoleService.h" #include "nsIDocument.h" #include "nsIObserver.h" #include "nsIObserverService.h" @@ -49,6 +47,7 @@ #include "nsThreadUtils.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" +#include "ScriptErrorHelper.h" #include "nsQueryObject.h" // Include this last to avoid path problems on Windows. @@ -132,50 +131,6 @@ private: } // namespace -class IDBDatabase::LogWarningRunnable final - : public nsRunnable -{ - nsCString mMessageName; - nsString mFilename; - uint32_t mLineNumber; - uint32_t mColumnNumber; - uint64_t mInnerWindowID; - bool mIsChrome; - -public: - LogWarningRunnable(const char* aMessageName, - const nsAString& aFilename, - uint32_t aLineNumber, - uint32_t aColumnNumber, - bool aIsChrome, - uint64_t aInnerWindowID) - : mMessageName(aMessageName) - , mFilename(aFilename) - , mLineNumber(aLineNumber) - , mColumnNumber(aColumnNumber) - , mInnerWindowID(aInnerWindowID) - , mIsChrome(aIsChrome) - { - MOZ_ASSERT(!NS_IsMainThread()); - } - - static void - LogWarning(const char* aMessageName, - const nsAString& aFilename, - uint32_t aLineNumber, - uint32_t aColumnNumber, - bool aIsChrome, - uint64_t aInnerWindowID); - - NS_DECL_ISUPPORTS_INHERITED - -private: - ~LogWarningRunnable() - { } - - NS_DECL_NSIRUNNABLE -}; - class IDBDatabase::Observer final : public nsIObserver { @@ -1262,23 +1217,13 @@ IDBDatabase::LogWarning(const char* aMessageName, AssertIsOnOwningThread(); MOZ_ASSERT(aMessageName); - if (NS_IsMainThread()) { - LogWarningRunnable::LogWarning(aMessageName, - aFilename, - aLineNumber, - aColumnNumber, - mFactory->IsChrome(), - mFactory->InnerWindowID()); - } else { - RefPtr runnable = - new LogWarningRunnable(aMessageName, - aFilename, - aLineNumber, - aColumnNumber, - mFactory->IsChrome(), - mFactory->InnerWindowID()); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); - } + ScriptErrorHelper::DumpLocalizedMessage(nsDependentCString(aMessageName), + aFilename, + aLineNumber, + aColumnNumber, + nsIScriptError::warningFlag, + mFactory->IsChrome(), + mFactory->InnerWindowID()); } NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache) @@ -1361,85 +1306,6 @@ CancelableRunnableWrapper::Cancel() return NS_ERROR_UNEXPECTED; } -// static -void -IDBDatabase:: -LogWarningRunnable::LogWarning(const char* aMessageName, - const nsAString& aFilename, - uint32_t aLineNumber, - uint32_t aColumnNumber, - bool aIsChrome, - uint64_t aInnerWindowID) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aMessageName); - - nsXPIDLString localizedMessage; - if (NS_WARN_IF(NS_FAILED( - nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, - aMessageName, - localizedMessage)))) { - return; - } - - nsAutoCString category; - if (aIsChrome) { - category.AssignLiteral("chrome "); - } else { - category.AssignLiteral("content "); - } - category.AppendLiteral("javascript"); - - nsCOMPtr consoleService = - do_GetService(NS_CONSOLESERVICE_CONTRACTID); - MOZ_ASSERT(consoleService); - - nsCOMPtr scriptError = - do_CreateInstance(NS_SCRIPTERROR_CONTRACTID); - MOZ_ASSERT(consoleService); - - if (aInnerWindowID) { - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - scriptError->InitWithWindowID(localizedMessage, - aFilename, - /* aSourceLine */ EmptyString(), - aLineNumber, - aColumnNumber, - nsIScriptError::warningFlag, - category, - aInnerWindowID))); - } else { - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - scriptError->Init(localizedMessage, - aFilename, - /* aSourceLine */ EmptyString(), - aLineNumber, - aColumnNumber, - nsIScriptError::warningFlag, - category.get()))); - } - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(consoleService->LogMessage(scriptError))); -} - -NS_IMPL_ISUPPORTS_INHERITED0(IDBDatabase::LogWarningRunnable, nsRunnable) - -NS_IMETHODIMP -IDBDatabase:: -LogWarningRunnable::Run() -{ - MOZ_ASSERT(NS_IsMainThread()); - - LogWarning(mMessageName.get(), - mFilename, - mLineNumber, - mColumnNumber, - mIsChrome, - mInnerWindowID); - - return NS_OK; -} - NS_IMPL_ISUPPORTS(IDBDatabase::Observer, nsIObserver) NS_IMETHODIMP diff --git a/dom/indexedDB/IDBDatabase.h b/dom/indexedDB/IDBDatabase.h index f04dc9613d..96988f3baa 100644 --- a/dom/indexedDB/IDBDatabase.h +++ b/dom/indexedDB/IDBDatabase.h @@ -53,9 +53,6 @@ class IDBDatabase final typedef mozilla::dom::StorageType StorageType; typedef mozilla::dom::quota::PersistenceType PersistenceType; - class LogWarningRunnable; - friend class LogWarningRunnable; - class Observer; friend class Observer; diff --git a/dom/indexedDB/IDBRequest.cpp b/dom/indexedDB/IDBRequest.cpp index 61d9dfb1bc..efc2c1a247 100644 --- a/dom/indexedDB/IDBRequest.cpp +++ b/dom/indexedDB/IDBRequest.cpp @@ -374,7 +374,6 @@ IDBRequest::SetResultCallback(ResultCallback* aCallback) return; } } - autoJS.TakeOwnershipOfErrorReporting(); JSContext* cx = autoJS.cx(); diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index 12c2f73c65..6a6626e75b 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -38,6 +38,7 @@ #include "IDBKeyRange.h" #include "IDBRequest.h" #include "ProfilerHelpers.h" +#include "ScriptErrorHelper.h" #include "WorkerScope.h" #include "WorkerPrivate.h" @@ -569,45 +570,14 @@ IndexedDatabaseManager::CommonPostHandleEvent(EventChainPostVisitor& aVisitor, return NS_OK; } - nsAutoCString category; - if (aFactory->IsChrome()) { - category.AssignLiteral("chrome "); - } else { - category.AssignLiteral("content "); - } - category.AppendLiteral("javascript"); - // Log the error to the error console. - nsCOMPtr consoleService = - do_GetService(NS_CONSOLESERVICE_CONTRACTID); - MOZ_ASSERT(consoleService); - - nsCOMPtr scriptError = - do_CreateInstance(NS_SCRIPTERROR_CONTRACTID); - MOZ_ASSERT(consoleService); - - if (uint64_t innerWindowID = aFactory->InnerWindowID()) { - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - scriptError->InitWithWindowID(errorName, - init.mFilename, - /* aSourceLine */ EmptyString(), - init.mLineno, - init.mColno, - nsIScriptError::errorFlag, - category, - innerWindowID))); - } else { - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - scriptError->Init(errorName, - init.mFilename, - /* aSourceLine */ EmptyString(), - init.mLineno, - init.mColno, - nsIScriptError::errorFlag, - category.get()))); - } - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(consoleService->LogMessage(scriptError))); + ScriptErrorHelper::Dump(errorName, + init.mFilename, + init.mLineno, + init.mColno, + nsIScriptError::errorFlag, + aFactory->IsChrome(), + aFactory->InnerWindowID()); return NS_OK; } diff --git a/dom/indexedDB/ScriptErrorHelper.cpp b/dom/indexedDB/ScriptErrorHelper.cpp new file mode 100644 index 0000000000..161eb9f71b --- /dev/null +++ b/dom/indexedDB/ScriptErrorHelper.cpp @@ -0,0 +1,249 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ScriptErrorHelper.h" + +#include "MainThreadUtils.h" +#include "nsCOMPtr.h" +#include "nsContentUtils.h" +#include "nsIConsoleService.h" +#include "nsIScriptError.h" +#include "nsString.h" +#include "nsThreadUtils.h" + +namespace { + +class ScriptErrorRunnable final : public nsRunnable +{ + nsString mMessage; + nsCString mMessageName; + nsString mFilename; + uint32_t mLineNumber; + uint32_t mColumnNumber; + uint32_t mSeverityFlag; + uint64_t mInnerWindowID; + bool mIsChrome; + +public: + ScriptErrorRunnable(const nsAString& aMessage, + const nsAString& aFilename, + uint32_t aLineNumber, + uint32_t aColumnNumber, + uint32_t aSeverityFlag, + bool aIsChrome, + uint64_t aInnerWindowID) + : mMessage(aMessage) + , mFilename(aFilename) + , mLineNumber(aLineNumber) + , mColumnNumber(aColumnNumber) + , mSeverityFlag(aSeverityFlag) + , mInnerWindowID(aInnerWindowID) + , mIsChrome(aIsChrome) + { + MOZ_ASSERT(!NS_IsMainThread()); + mMessageName.SetIsVoid(true); + } + + ScriptErrorRunnable(const nsACString& aMessageName, + const nsAString& aFilename, + uint32_t aLineNumber, + uint32_t aColumnNumber, + uint32_t aSeverityFlag, + bool aIsChrome, + uint64_t aInnerWindowID) + : mMessageName(aMessageName) + , mFilename(aFilename) + , mLineNumber(aLineNumber) + , mColumnNumber(aColumnNumber) + , mSeverityFlag(aSeverityFlag) + , mInnerWindowID(aInnerWindowID) + , mIsChrome(aIsChrome) + { + MOZ_ASSERT(!NS_IsMainThread()); + mMessage.SetIsVoid(true); + } + + static void + DumpLocalizedMessage(const nsCString& aMessageName, + const nsAString& aFilename, + uint32_t aLineNumber, + uint32_t aColumnNumber, + uint32_t aSeverityFlag, + bool aIsChrome, + uint64_t aInnerWindowID) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aMessageName.IsEmpty()); + + nsXPIDLString localizedMessage; + if (NS_WARN_IF(NS_FAILED( + nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, + aMessageName.get(), + localizedMessage)))) { + return; + } + + Dump(localizedMessage, + aFilename, + aLineNumber, + aColumnNumber, + aSeverityFlag, + aIsChrome, + aInnerWindowID); + } + + static void + Dump(const nsAString& aMessage, + const nsAString& aFilename, + uint32_t aLineNumber, + uint32_t aColumnNumber, + uint32_t aSeverityFlag, + bool aIsChrome, + uint64_t aInnerWindowID) + { + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoCString category; + if (aIsChrome) { + category.AssignLiteral("chrome "); + } else { + category.AssignLiteral("content "); + } + category.AppendLiteral("javascript"); + + nsCOMPtr consoleService = + do_GetService(NS_CONSOLESERVICE_CONTRACTID); + MOZ_ASSERT(consoleService); + + nsCOMPtr scriptError = + do_CreateInstance(NS_SCRIPTERROR_CONTRACTID); + MOZ_ASSERT(scriptError); + + if (aInnerWindowID) { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + scriptError->InitWithWindowID(aMessage, + aFilename, + /* aSourceLine */ EmptyString(), + aLineNumber, + aColumnNumber, + aSeverityFlag, + category, + aInnerWindowID))); + } else { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + scriptError->Init(aMessage, + aFilename, + /* aSourceLine */ EmptyString(), + aLineNumber, + aColumnNumber, + aSeverityFlag, + category.get()))); + } + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(consoleService->LogMessage(scriptError))); + } + + NS_IMETHOD + Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mMessage.IsVoid() != mMessageName.IsVoid()); + + if (!mMessage.IsVoid()) { + Dump(mMessage, + mFilename, + mLineNumber, + mColumnNumber, + mSeverityFlag, + mIsChrome, + mInnerWindowID); + return NS_OK; + } + + DumpLocalizedMessage(mMessageName, + mFilename, + mLineNumber, + mColumnNumber, + mSeverityFlag, + mIsChrome, + mInnerWindowID); + + return NS_OK; + } + +private: + virtual ~ScriptErrorRunnable() {} +}; + +} // namespace + +namespace mozilla { +namespace dom { +namespace indexedDB { + +/*static*/ void +ScriptErrorHelper::Dump(const nsAString& aMessage, + const nsAString& aFilename, + uint32_t aLineNumber, + uint32_t aColumnNumber, + uint32_t aSeverityFlag, + bool aIsChrome, + uint64_t aInnerWindowID) +{ + if (NS_IsMainThread()) { + ScriptErrorRunnable::Dump(aMessage, + aFilename, + aLineNumber, + aColumnNumber, + aSeverityFlag, + aIsChrome, + aInnerWindowID); + } else { + RefPtr runnable = + new ScriptErrorRunnable(aMessage, + aFilename, + aLineNumber, + aColumnNumber, + aSeverityFlag, + aIsChrome, + aInnerWindowID); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); + } +} + +/*static*/ void +ScriptErrorHelper::DumpLocalizedMessage(const nsACString& aMessageName, + const nsAString& aFilename, + uint32_t aLineNumber, + uint32_t aColumnNumber, + uint32_t aSeverityFlag, + bool aIsChrome, + uint64_t aInnerWindowID) +{ + if (NS_IsMainThread()) { + ScriptErrorRunnable::DumpLocalizedMessage(nsAutoCString(aMessageName), + aFilename, + aLineNumber, + aColumnNumber, + aSeverityFlag, + aIsChrome, + aInnerWindowID); + } else { + RefPtr runnable = + new ScriptErrorRunnable(aMessageName, + aFilename, + aLineNumber, + aColumnNumber, + aSeverityFlag, + aIsChrome, + aInnerWindowID); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); + } +} + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla \ No newline at end of file diff --git a/dom/indexedDB/ScriptErrorHelper.h b/dom/indexedDB/ScriptErrorHelper.h new file mode 100644 index 0000000000..d1cd8c7493 --- /dev/null +++ b/dom/indexedDB/ScriptErrorHelper.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_scripterrorhelper_h__ +#define mozilla_dom_indexeddb_scripterrorhelper_h__ + +class nsAString; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +// Helper to report a script error to the main thread. +class ScriptErrorHelper +{ +public: + static void Dump(const nsAString& aMessage, + const nsAString& aFilename, + uint32_t aLineNumber, + uint32_t aColumnNumber, + uint32_t aSeverityFlag, /* nsIScriptError::xxxFlag */ + bool aIsChrome, + uint64_t aInnerWindowID); + + static void DumpLocalizedMessage(const nsACString& aMessageName, + const nsAString& aFilename, + uint32_t aLineNumber, + uint32_t aColumnNumber, + uint32_t aSeverityFlag, /* nsIScriptError::xxxFlag */ + bool aIsChrome, + uint64_t aInnerWindowID); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_indexeddb_scripterrorhelper_h__ \ No newline at end of file diff --git a/dom/indexedDB/moz.build b/dom/indexedDB/moz.build index 6ab2321535..47555b3606 100644 --- a/dom/indexedDB/moz.build +++ b/dom/indexedDB/moz.build @@ -67,6 +67,7 @@ UNIFIED_SOURCES += [ 'KeyPath.cpp', 'PermissionRequestBase.cpp', 'ReportInternalError.cpp', + 'ScriptErrorHelper.cpp', ] SOURCES += [ diff --git a/dom/mobilemessage/MobileMessageCallback.cpp b/dom/mobilemessage/MobileMessageCallback.cpp index 65d8f95a93..b49b59f738 100644 --- a/dom/mobilemessage/MobileMessageCallback.cpp +++ b/dom/mobilemessage/MobileMessageCallback.cpp @@ -261,8 +261,14 @@ MobileMessageCallback::NotifyMessageDeleted(bool *aDeleted, uint32_t aSize) JSContext* cx = jsapi.cx(); JS::Rooted deleteArrayObj(cx, JS_NewArrayObject(cx, aSize)); + if (!deleteArrayObj) { + return NS_ERROR_OUT_OF_MEMORY; + } for (uint32_t i = 0; i < aSize; i++) { - JS_DefineElement(cx, deleteArrayObj, i, aDeleted[i], JSPROP_ENUMERATE); + if (!JS_DefineElement(cx, deleteArrayObj, i, aDeleted[i], + JSPROP_ENUMERATE)) { + return NS_ERROR_UNEXPECTED; + } } JS::Rooted deleteArrayVal(cx, JS::ObjectValue(*deleteArrayObj)); diff --git a/dom/mobilemessage/MobileMessageManager.cpp b/dom/mobilemessage/MobileMessageManager.cpp index d13b53bcd8..3e67191333 100644 --- a/dom/mobilemessage/MobileMessageManager.cpp +++ b/dom/mobilemessage/MobileMessageManager.cpp @@ -277,8 +277,9 @@ MobileMessageManager::SendMMS(const MmsParameters& aParams, JSContext *cx = jsapi.cx(); JS::Rooted val(cx); + aRv.MightThrowJSException(); if (!ToJSValue(cx, aParams, &val)) { - aRv.Throw(NS_ERROR_TYPE_ERR); + aRv.StealExceptionFromJSContext(cx); return nullptr; } diff --git a/dom/plugins/base/nsJSNPRuntime.cpp b/dom/plugins/base/nsJSNPRuntime.cpp index 30496bb182..e98b4fd1e8 100644 --- a/dom/plugins/base/nsJSNPRuntime.cpp +++ b/dom/plugins/base/nsJSNPRuntime.cpp @@ -144,7 +144,6 @@ public: : mAes(aes) , mIsDestroyPending(aWrapper->mDestroyPending) { - MOZ_ASSERT(aes.OwnsErrorReporting()); } ~AutoJSExceptionSuppressor() diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp index c2d0a6c086..ad44e8e109 100644 --- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -770,7 +770,6 @@ Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable) // happen anyway. return; } - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); JS::Rooted handlerWrapper(cx); @@ -1761,7 +1760,6 @@ public: if (!jsapi.Init(mValues)) { return; } - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); JS::Rooted value(cx, aValue); diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index 2c1d4694c9..2839dce67f 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -1743,7 +1743,6 @@ ScriptExecutorRunnable::PreRun(WorkerPrivate* aWorkerPrivate) AutoJSAPI jsapi; jsapi.Init(); - jsapi.TakeOwnershipOfErrorReporting(); WorkerGlobalScope* globalScope = aWorkerPrivate->GetOrCreateGlobalScope(jsapi.cx()); diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index 70c00d1313..096eed4142 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -891,11 +891,19 @@ ExtendableEvent::GetPromise() MOZ_ASSERT(worker); worker->AssertIsOnWorkerThread(); - GlobalObject global(worker->GetJSContext(), worker->GlobalScope()->GetGlobalJSObject()); + nsIGlobalObject* globalObj = worker->GlobalScope(); + + AutoJSAPI jsapi; + if (!jsapi.Init(globalObj)) { + return nullptr; + } + JSContext* cx = jsapi.cx(); + + GlobalObject global(cx, globalObj->GetGlobalJSObject()); ErrorResult result; RefPtr p = Promise::All(global, Move(mPromises), result); - if (NS_WARN_IF(result.Failed())) { + if (NS_WARN_IF(result.MaybeSetPendingException(cx))) { return nullptr; } diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 29735ce31d..f1985aab9d 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -4410,7 +4410,6 @@ WorkerPrivate::DoRunLoop(JSContext* aCx) AutoJSAPI jsapi; jsapi.Init(); MOZ_ASSERT(jsapi.cx() == aCx); - jsapi.TakeOwnershipOfErrorReporting(); EnableMemoryReporter(); diff --git a/dom/workers/WorkerRunnable.cpp b/dom/workers/WorkerRunnable.cpp index f3d08a0a61..0f28a5f070 100644 --- a/dom/workers/WorkerRunnable.cpp +++ b/dom/workers/WorkerRunnable.cpp @@ -327,7 +327,6 @@ WorkerRunnable::Run() maybeJSAPI->Init(); jsapi = maybeJSAPI.ptr(); cx = jsapi->cx(); - jsapi->TakeOwnershipOfErrorReporting(); } // Note that we can't assert anything about mWorkerPrivate->GetWrapper() diff --git a/dom/workers/XMLHttpRequest.cpp b/dom/workers/XMLHttpRequest.cpp index 5ae9808c7a..e234f0cd9c 100644 --- a/dom/workers/XMLHttpRequest.cpp +++ b/dom/workers/XMLHttpRequest.cpp @@ -1183,7 +1183,6 @@ EventRunnable::PreDispatch(WorkerPrivate* /* unused */) AutoJSAPI jsapi; DebugOnly ok = jsapi.Init(xpc::NativeGlobal(mScopeObj)); MOZ_ASSERT(ok); - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); // Now keep the mScopeObj alive for the duration JS::Rooted scopeObj(cx, mScopeObj); diff --git a/dom/xbl/nsXBLProtoImpl.cpp b/dom/xbl/nsXBLProtoImpl.cpp index 3d787f6fa2..3218e45ae9 100644 --- a/dom/xbl/nsXBLProtoImpl.cpp +++ b/dom/xbl/nsXBLProtoImpl.cpp @@ -245,7 +245,6 @@ nsXBLProtoImpl::CompilePrototypeMembers(nsXBLPrototypeBinding* aBinding) AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(xpc::CompilationScope()))) return NS_ERROR_FAILURE; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); mPrecompiledMemberHolder = JS_NewObjectWithGivenProto(cx, nullptr, nullptr); diff --git a/dom/xbl/nsXBLPrototypeHandler.cpp b/dom/xbl/nsXBLPrototypeHandler.cpp index 389288b9d2..18f3b63b02 100644 --- a/dom/xbl/nsXBLPrototypeHandler.cpp +++ b/dom/xbl/nsXBLPrototypeHandler.cpp @@ -277,7 +277,6 @@ nsXBLPrototypeHandler::ExecuteHandler(EventTarget* aTarget, if (NS_WARN_IF(!jsapi.Init(boundGlobal))) { return NS_OK; } - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); JS::Rooted handler(cx); diff --git a/gfx/layers/Compositor.h b/gfx/layers/Compositor.h index b2a333ff94..fe622e7e81 100644 --- a/gfx/layers/Compositor.h +++ b/gfx/layers/Compositor.h @@ -200,6 +200,7 @@ public: virtual bool Initialize() = 0; virtual void Destroy() = 0; + virtual void DetachWidget() {} /** * Return true if the effect type is supported. diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp index 12433ea332..deddd7166e 100644 --- a/gfx/layers/basic/BasicCompositor.cpp +++ b/gfx/layers/basic/BasicCompositor.cpp @@ -121,10 +121,12 @@ BasicCompositingRenderTarget::BindRenderTarget() } } -void BasicCompositor::Destroy() +void BasicCompositor::DetachWidget() { - mWidget->CleanupRemoteDrawing(); - mWidget = nullptr; + if (mWidget) { + mWidget->CleanupRemoteDrawing(); + mWidget = nullptr; + } } TextureFactoryIdentifier diff --git a/gfx/layers/basic/BasicCompositor.h b/gfx/layers/basic/BasicCompositor.h index e4696e1b3e..dbbdc37ce6 100644 --- a/gfx/layers/basic/BasicCompositor.h +++ b/gfx/layers/basic/BasicCompositor.h @@ -50,7 +50,9 @@ protected: public: virtual bool Initialize() override; - virtual void Destroy() override; + virtual void Destroy() override {} + + virtual void DetachWidget() override; virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override; diff --git a/gfx/layers/d3d11/CompositorD3D11.h b/gfx/layers/d3d11/CompositorD3D11.h index 4f363828b2..2158c0f7a7 100644 --- a/gfx/layers/d3d11/CompositorD3D11.h +++ b/gfx/layers/d3d11/CompositorD3D11.h @@ -47,6 +47,7 @@ public: virtual bool Initialize() override; virtual void Destroy() override {} + virtual void DetachWidget() override { mWidget = nullptr; } virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override; diff --git a/gfx/layers/d3d9/CompositorD3D9.h b/gfx/layers/d3d9/CompositorD3D9.h index 5330db54e4..45ec6580b4 100644 --- a/gfx/layers/d3d9/CompositorD3D9.h +++ b/gfx/layers/d3d9/CompositorD3D9.h @@ -26,6 +26,7 @@ public: virtual bool Initialize() override; virtual void Destroy() override {} + virtual void DetachWidget() override { mWidget = nullptr; } virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override; diff --git a/gfx/layers/ipc/CompositorBridgeChild.cpp b/gfx/layers/ipc/CompositorBridgeChild.cpp index fd8e741e71..287ccc1234 100644 --- a/gfx/layers/ipc/CompositorBridgeChild.cpp +++ b/gfx/layers/ipc/CompositorBridgeChild.cpp @@ -34,7 +34,7 @@ using mozilla::Unused; namespace mozilla { namespace layers { -/*static*/ CompositorBridgeChild* CompositorBridgeChild::sCompositor; +static StaticRefPtr sCompositorBridge; Atomic CompositableForwarder::sSerialCounter(0); @@ -54,12 +54,20 @@ CompositorBridgeChild::~CompositorBridgeChild() } } +bool +CompositorBridgeChild::IsSameProcess() const +{ + return OtherPid() == base::GetCurrentProcId(); +} + static void DeferredDestroyCompositor(RefPtr aCompositorBridgeParent, RefPtr aCompositorBridgeChild) { - // Bug 848949 needs to be fixed before - // we can close the channel properly - //aCompositorBridgeChild->Close(); + aCompositorBridgeChild->Close(); + + if (sCompositorBridge == aCompositorBridgeChild) { + sCompositorBridge = nullptr; + } } void @@ -72,24 +80,11 @@ CompositorBridgeChild::Destroy() return; } - mCanSend = false; - // Destroying the layer manager may cause all sorts of things to happen, so // let's make sure there is still a reference to keep this alive whatever // happens. RefPtr selfRef = this; - SendWillStop(); - // The call just made to SendWillStop can result in IPC from the - // CompositorBridgeParent to the CompositorBridgeChild (e.g. caused by the destruction - // of shared memory). We need to ensure this gets processed by the - // CompositorBridgeChild before it gets destroyed. It suffices to ensure that - // events already in the MessageLoop get processed before the - // CompositorBridgeChild is destroyed, so we add a task to the MessageLoop to - // handle compositor desctruction. - - // From now on the only message we can send is Stop. - if (mLayerManager) { mLayerManager->Destroy(); mLayerManager = nullptr; @@ -103,14 +98,35 @@ CompositorBridgeChild::Destroy() layers->Destroy(); } - SendStop(); + SendWillClose(); + mCanSend = false; - // The DeferredDestroyCompositor task takes ownership of compositorParent and - // will release them when it runs. + + // The call just made to SendWillClose can result in IPC from the + // CompositorBridgeParent to the CompositorBridgeChild (e.g. caused by the destruction + // of shared memory). We need to ensure this gets processed by the + // CompositorBridgeChild before it gets destroyed. It suffices to ensure that + // events already in the MessageLoop get processed before the + // CompositorBridgeChild is destroyed, so we add a task to the MessageLoop to + // handle compositor desctruction. + + // From now on we can't send any message message. MessageLoop::current()->PostTask(FROM_HERE, NewRunnableFunction(DeferredDestroyCompositor, mCompositorBridgeParent, selfRef)); } +// static +void +CompositorBridgeChild::ShutDown() +{ + if (sCompositorBridge) { + sCompositorBridge->Destroy(); + do { + NS_ProcessNextEvent(nullptr, true); + } while (sCompositorBridge); + } +} + bool CompositorBridgeChild::LookupCompositorFrameMetrics(const FrameMetrics::ViewID aId, FrameMetrics& aFrame) @@ -127,7 +143,7 @@ CompositorBridgeChild::LookupCompositorFrameMetrics(const FrameMetrics::ViewID a CompositorBridgeChild::Create(Transport* aTransport, ProcessId aOtherPid) { // There's only one compositor per child process. - MOZ_ASSERT(!sCompositor); + MOZ_ASSERT(!sCompositorBridge); RefPtr child(new CompositorBridgeChild(nullptr)); if (!child->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ChildSide)) { @@ -137,16 +153,15 @@ CompositorBridgeChild::Create(Transport* aTransport, ProcessId aOtherPid) child->mCanSend = true; - // We release this ref in ActorDestroy(). - sCompositor = child.forget().take(); + // We release this ref in DeferredDestroyCompositor. + sCompositorBridge = child; int32_t width; int32_t height; - sCompositor->SendGetTileSize(&width, &height); + sCompositorBridge->SendGetTileSize(&width, &height); gfxPlatform::GetPlatform()->SetTileSize(width, height); - // We release this ref in ActorDestroy(). - return sCompositor; + return sCompositorBridge; } bool @@ -166,7 +181,14 @@ CompositorBridgeChild::Get() { // This is only expected to be used in child processes. MOZ_ASSERT(!XRE_IsParentProcess()); - return sCompositor; + return sCompositorBridge; +} + +// static +bool +CompositorBridgeChild::ChildProcessHasCompositorBridge() +{ + return sCompositorBridge != nullptr; } PLayerTransactionChild* @@ -416,8 +438,6 @@ CompositorBridgeChild::RecvClearCachedResources(const uint64_t& aId) void CompositorBridgeChild::ActorDestroy(ActorDestroyReason aWhy) { - MOZ_ASSERT(sCompositor == this); - if (aWhy == AbnormalShutdown) { #ifdef MOZ_B2G // Due to poor lifetime management of gralloc (and possibly shmems) we will @@ -430,12 +450,8 @@ CompositorBridgeChild::ActorDestroy(ActorDestroyReason aWhy) // If the parent side runs into a problem then the actor will be destroyed. // There is nothing we can do in the child side, here sets mCanSend as false. mCanSend = false; - gfxCriticalNote << "Receive IPC close with reason=" << aWhy; + gfxCriticalNote << "Receive IPC close with reason=AbnormalShutdown"; } - - MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &CompositorBridgeChild::Release)); } bool @@ -570,9 +586,13 @@ CompositorBridgeChild::CancelNotifyAfterRemotePaint(TabChild* aTabChild) } bool -CompositorBridgeChild::SendWillStop() +CompositorBridgeChild::SendWillClose() { - return PCompositorBridgeChild::SendWillStop(); + MOZ_ASSERT(mCanSend); + if (!mCanSend) { + return true; + } + return PCompositorBridgeChild::SendWillClose(); } bool diff --git a/gfx/layers/ipc/CompositorBridgeChild.h b/gfx/layers/ipc/CompositorBridgeChild.h index 21bdeda20d..0ec6fb8815 100644 --- a/gfx/layers/ipc/CompositorBridgeChild.h +++ b/gfx/layers/ipc/CompositorBridgeChild.h @@ -66,7 +66,7 @@ public: static CompositorBridgeChild* Get(); - static bool ChildProcessHasCompositor() { return sCompositor != nullptr; } + static bool ChildProcessHasCompositorBridge(); void AddOverfillObserver(ClientLayerManager* aLayerManager); @@ -112,7 +112,7 @@ public: // send a message) and forward the call to the super-class's equivalent method. // This means that it is correct to call directly the super-class methods, but // you won't get the extra safety provided here. - bool SendWillStop(); + bool SendWillClose(); bool SendPause(); bool SendResume(); bool SendNotifyHidden(const uint64_t& id); @@ -129,6 +129,9 @@ public: bool SendClearApproximatelyVisibleRegions(uint64_t aLayersId, uint32_t aPresShellId); bool SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, const mozilla::CSSIntRegion& aRegion); + bool IsSameProcess() const; + + static void ShutDown(); private: // Private destructor, to discourage deletion outside of Release(): @@ -190,11 +193,6 @@ private: // While this should be safe to use since the ViewID is unique nsClassHashtable mFrameMetricsTable; - // When we're in a child process, this is the process-global - // compositor that we use to forward transactions directly to the - // compositor context in another process. - static CompositorBridgeChild* sCompositor; - // Weakly hold the TabChild that made a request to be alerted when // the transaction has been received. nsWeakPtr mWeakTabChild; // type is TabChild diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp index 968b73f43f..69293cf124 100644 --- a/gfx/layers/ipc/CompositorBridgeParent.cpp +++ b/gfx/layers/ipc/CompositorBridgeParent.cpp @@ -401,6 +401,10 @@ CompositorVsyncScheduler::CancelSetDisplayTask() void CompositorVsyncScheduler::Destroy() { + if (!mVsyncObserver) { + // Destroy was already called on this object. + return; + } MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread()); UnobserveVsync(); mVsyncObserver->Destroy(); @@ -642,6 +646,9 @@ void CompositorBridgeParent::ShutDown() while (!sFinishedCompositorShutDown) { NS_ProcessNextEvent(nullptr, true); } + + // TODO: this should be empty by now... + sIndirectLayerTrees.clear(); } MessageLoop* CompositorBridgeParent::CompositorLoop() @@ -726,6 +733,9 @@ CompositorBridgeParent::CompositorBridgeParent(nsIWidget* aWidget, mCompositorScheduler = new CompositorVsyncScheduler(this, aWidget); LayerScope::SetPixelScale(mWidget->GetDefaultScale().scale); + + // mSelfRef is cleared in DeferredDestroy. + mSelfRef = this; } bool @@ -746,33 +756,6 @@ CompositorBridgeParent::~CompositorBridgeParent() MOZ_COUNT_DTOR(CompositorBridgeParent); } -void -CompositorBridgeParent::Destroy() -{ - MOZ_ASSERT(ManagedPLayerTransactionParent().Count() == 0, - "CompositorBridgeParent destroyed before managed PLayerTransactionParent"); - - MOZ_ASSERT(mPaused); // Ensure RecvWillStop was called - // Ensure that the layer manager is destructed on the compositor thread. - mLayerManager = nullptr; - if (mCompositor) { - mCompositor->Destroy(); - } - mCompositor = nullptr; - - mCompositionManager = nullptr; - if (mApzcTreeManager) { - mApzcTreeManager->ClearTree(); - mApzcTreeManager = nullptr; - } - { // scope lock - MonitorAutoLock lock(*sIndirectLayerTreesLock); - sIndirectLayerTrees.erase(mRootLayerTreeID); - } - - mCompositorScheduler->Destroy(); -} - void CompositorBridgeParent::ForceIsFirstPaint() { @@ -780,7 +763,7 @@ CompositorBridgeParent::ForceIsFirstPaint() } bool -CompositorBridgeParent::RecvWillStop() +CompositorBridgeParent::RecvWillClose() { mPaused = true; RemoveCompositor(mCompositorID); @@ -798,6 +781,10 @@ CompositorBridgeParent::RecvWillStop() mCompositionManager = nullptr; } + if (mCompositor) { + mCompositor->DetachWidget(); + } + return true; } @@ -806,22 +793,7 @@ void CompositorBridgeParent::DeferredDestroy() MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(mCompositorThreadHolder); mCompositorThreadHolder = nullptr; - Release(); -} - -bool -CompositorBridgeParent::RecvStop() -{ - Destroy(); - // There are chances that the ref count reaches zero on the main thread shortly - // after this function returns while some ipdl code still needs to run on - // this thread. - // We must keep the compositor parent alive untill the code handling message - // reception is finished on this thread. - this->AddRef(); // Corresponds to DeferredDestroy's Release - MessageLoop::current()->PostTask(FROM_HERE, - NewRunnableMethod(this,&CompositorBridgeParent::DeferredDestroy)); - return true; + mSelfRef = nullptr; } bool @@ -987,11 +959,32 @@ CompositorBridgeParent::ActorDestroy(ActorDestroyReason why) mLayerManager = nullptr; { // scope lock MonitorAutoLock lock(*sIndirectLayerTreesLock); - sIndirectLayerTrees[mRootLayerTreeID].mLayerManager = nullptr; + sIndirectLayerTrees.erase(mRootLayerTreeID); } - mCompositionManager = nullptr; + } + + if (mCompositor) { + mCompositor->Destroy(); mCompositor = nullptr; } + + mCompositionManager = nullptr; + + if (mApzcTreeManager) { + mApzcTreeManager->ClearTree(); + mApzcTreeManager = nullptr; + } + + mCompositorScheduler->Destroy(); + + // There are chances that the ref count reaches zero on the main thread shortly + // after this function returns while some ipdl code still needs to run on + // this thread. + // We must keep the compositor parent alive untill the code handling message + // reception is finished on this thread. + mSelfRef = this; + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableMethod(this,&CompositorBridgeParent::DeferredDestroy)); } @@ -1940,6 +1933,7 @@ public: explicit CrossProcessCompositorBridgeParent(Transport* aTransport) : mTransport(aTransport) , mNotifyAfterRemotePaint(false) + , mDestroyCalled(false) { MOZ_ASSERT(NS_IsMainThread()); } @@ -1954,8 +1948,7 @@ public: // FIXME/bug 774388: work out what shutdown protocol we need. virtual bool RecvRequestOverfill() override { return true; } - virtual bool RecvWillStop() override { return true; } - virtual bool RecvStop() override { return true; } + virtual bool RecvWillClose() override { return true; } virtual bool RecvPause() override { return true; } virtual bool RecvResume() override { return true; } virtual bool RecvNotifyHidden(const uint64_t& id) override; @@ -2077,6 +2070,7 @@ private: // If true, we should send a RemotePaintIsReady message when the layer transaction // is received bool mNotifyAfterRemotePaint; + bool mDestroyCalled; }; PCompositorBridgeParent* @@ -2285,10 +2279,10 @@ CrossProcessCompositorBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { RefPtr lru = CompositorLRU::GetSingleton(); lru->Remove(this); - - MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &CrossProcessCompositorBridgeParent::DeferredDestroy)); + // We must keep this object alive untill the code handling message + // reception is finished on this thread. + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableMethod(this, &CrossProcessCompositorBridgeParent::DeferredDestroy)); } PLayerTransactionParent* @@ -2718,7 +2712,6 @@ CrossProcessCompositorBridgeParent::GetCompositionManager(LayerTransactionParent void CrossProcessCompositorBridgeParent::DeferredDestroy() { - MOZ_ASSERT(mCompositorThreadHolder); mCompositorThreadHolder = nullptr; mSelfRef = nullptr; } diff --git a/gfx/layers/ipc/CompositorBridgeParent.h b/gfx/layers/ipc/CompositorBridgeParent.h index b0178614cb..86fb89b6b4 100644 --- a/gfx/layers/ipc/CompositorBridgeParent.h +++ b/gfx/layers/ipc/CompositorBridgeParent.h @@ -227,8 +227,7 @@ public: virtual bool RecvGetFrameUniformity(FrameUniformityData* aOutData) override; virtual bool RecvRequestOverfill() override; - virtual bool RecvWillStop() override; - virtual bool RecvStop() override; + virtual bool RecvWillClose() override; virtual bool RecvPause() override; virtual bool RecvResume() override; virtual bool RecvNotifyHidden(const uint64_t& id) override { return true; } @@ -305,7 +304,6 @@ public: * SetFirstPaintViewport on the next frame of composition. */ void ForceIsFirstPaint(); - void Destroy(); static void SetShadowProperties(Layer* aLayer); @@ -578,6 +576,10 @@ protected: RefPtr mCompositorThreadHolder; RefPtr mCompositorScheduler; + // This makes sure the compositorParent is not destroyed before receiving + // confirmation that the channel is closed. + // mSelfRef is cleared in DeferredDestroy which is scheduled by ActorDestroy. + RefPtr mSelfRef; #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) // cached plugin data used to reduce the number of updates we request. diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp index e33ca4e05a..883e6975c3 100644 --- a/gfx/layers/ipc/ImageBridgeChild.cpp +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -236,7 +236,7 @@ static void ImageBridgeShutdownStep1(ReentrantMonitor *aBarrier, bool *aDone) } sImageBridgeChildSingleton->FallbackDestroyActors(); - sImageBridgeChildSingleton->SendWillStop(); + sImageBridgeChildSingleton->SendWillClose(); sImageBridgeChildSingleton->MarkShutDown(); // From now on, no message can be sent through the image bridge from the // client side except the final Stop message. @@ -254,7 +254,7 @@ static void ImageBridgeShutdownStep2(ReentrantMonitor *aBarrier, bool *aDone) MOZ_ASSERT(InImageBridgeChildThread(), "Should be in ImageBridgeChild thread."); - sImageBridgeChildSingleton->SendStop(); + sImageBridgeChildSingleton->Close(); *aDone = true; aBarrier->NotifyAll(); diff --git a/gfx/layers/ipc/ImageBridgeParent.cpp b/gfx/layers/ipc/ImageBridgeParent.cpp index aaf63cb157..b4643bae21 100644 --- a/gfx/layers/ipc/ImageBridgeParent.cpp +++ b/gfx/layers/ipc/ImageBridgeParent.cpp @@ -58,7 +58,7 @@ ImageBridgeParent::ImageBridgeParent(MessageLoop* aLoop, : mMessageLoop(aLoop) , mTransport(aTransport) , mSetChildThreadPriority(false) - , mStopped(false) + , mClosed(false) { MOZ_ASSERT(NS_IsMainThread()); sMainLoop = MessageLoop::current(); @@ -71,6 +71,8 @@ ImageBridgeParent::ImageBridgeParent(MessageLoop* aLoop, CompositableMap::Create(); sImageBridges[aChildProcessId] = this; SetOtherProcessId(aChildProcessId); + // DeferredDestroy clears mSelfRef. + mSelfRef = this; } ImageBridgeParent::~ImageBridgeParent() @@ -95,9 +97,19 @@ ImageBridgeParent::~ImageBridgeParent() void ImageBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { + // Can't alloc/dealloc shmems from now on. + mClosed = true; + MessageLoop::current()->PostTask( FROM_HERE, NewRunnableMethod(this, &ImageBridgeParent::DeferredDestroy)); + + // It is very important that this method gets called at shutdown (be it a clean + // or an abnormal shutdown), because DeferredDestroy is what clears mSelfRef. + // If mSelfRef is not null and ActorDestroy is not called, the ImageBridgeParent + // is leaked which causes the CompositorThreadHolder to be leaked and + // CompsoitorParent's shutdown ends up spinning the event loop forever, waiting + // for the compositor thread to terminate. } bool @@ -186,14 +198,13 @@ ImageBridgeParent::Create(Transport* aTransport, ProcessId aChildProcessId) { MessageLoop* loop = CompositorBridgeParent::CompositorLoop(); RefPtr bridge = new ImageBridgeParent(loop, aTransport, aChildProcessId); - bridge->mSelfRef = bridge; loop->PostTask(FROM_HERE, NewRunnableFunction(ConnectImageBridgeInParentProcess, bridge.get(), aTransport, aChildProcessId)); return bridge.get(); } -bool ImageBridgeParent::RecvWillStop() +bool ImageBridgeParent::RecvWillClose() { // If there is any texture still alive we have to force it to deallocate the // device data (GL textures, etc.) now because shortly after SenStop() returns @@ -208,31 +219,6 @@ bool ImageBridgeParent::RecvWillStop() return true; } -static void -ReleaseImageBridgeParent(ImageBridgeParent* aImageBridgeParent) -{ - RELEASE_MANUALLY(aImageBridgeParent); -} - -bool ImageBridgeParent::RecvStop() -{ - // This message mostly serves as synchronization between the - // child and parent threads during shutdown. - - // Can't alloc/dealloc shmems from now on. - mStopped = true; - - // There is one thing that we need to do here: temporarily addref, so that - // the handling of this sync message can't race with the destruction of - // the ImageBridgeParent, which would trigger the dreaded "mismatched CxxStackFrames" - // assertion of MessageChannel. - ADDREF_MANUALLY(this); - MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableFunction(&ReleaseImageBridgeParent, this)); - return true; -} - static uint64_t GenImageContainerID() { static uint64_t sNextImageID = 1; @@ -353,9 +339,8 @@ ImageBridgeParent::NotifyImageComposites(nsTArray& a void ImageBridgeParent::DeferredDestroy() { - MOZ_ASSERT(mCompositorThreadHolder); mCompositorThreadHolder = nullptr; - mSelfRef = nullptr; + mSelfRef = nullptr; // "this" ImageBridge may get deleted here. } ImageBridgeParent* @@ -398,7 +383,7 @@ ImageBridgeParent::AllocShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem) { - if (mStopped) { + if (mClosed) { return false; } return PImageBridgeParent::AllocShmem(aSize, aType, aShmem); @@ -409,7 +394,7 @@ ImageBridgeParent::AllocUnsafeShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem) { - if (mStopped) { + if (mClosed) { return false; } return PImageBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem); @@ -418,7 +403,7 @@ ImageBridgeParent::AllocUnsafeShmem(size_t aSize, void ImageBridgeParent::DeallocShmem(ipc::Shmem& aShmem) { - if (mStopped) { + if (mClosed) { return; } PImageBridgeParent::DeallocShmem(aShmem); diff --git a/gfx/layers/ipc/ImageBridgeParent.h b/gfx/layers/ipc/ImageBridgeParent.h index b95cb04365..da1968f394 100644 --- a/gfx/layers/ipc/ImageBridgeParent.h +++ b/gfx/layers/ipc/ImageBridgeParent.h @@ -93,9 +93,7 @@ public: RecvChildAsyncMessages(InfallibleTArray&& aMessages) override; // Shutdown step 1 - virtual bool RecvWillStop() override; - // Shutdown step 2 - virtual bool RecvStop() override; + virtual bool RecvWillClose() override; MessageLoop* GetMessageLoop() const { return mMessageLoop; } @@ -142,7 +140,7 @@ public: virtual bool UsesImageBridge() const override { return true; } - virtual bool IPCOpen() const override { return !mStopped; } + virtual bool IPCOpen() const override { return !mClosed; } protected: void OnChannelConnected(int32_t pid) override; @@ -156,7 +154,7 @@ private: RefPtr mSelfRef; bool mSetChildThreadPriority; - bool mStopped; + bool mClosed; /** * Map of all living ImageBridgeParent instances diff --git a/gfx/layers/ipc/PCompositorBridge.ipdl b/gfx/layers/ipc/PCompositorBridge.ipdl index 97b0b235f0..c6dac5d9b2 100644 --- a/gfx/layers/ipc/PCompositorBridge.ipdl +++ b/gfx/layers/ipc/PCompositorBridge.ipdl @@ -107,10 +107,7 @@ parent: sync GetFrameUniformity() returns (FrameUniformityData data); // The child is about to be destroyed, so perform any necessary cleanup. - sync WillStop(); - - // Clean up in preparation for own destruction. - sync Stop(); + sync WillClose(); // Pause/resume the compositor. These are intended to be used on mobile, when // the compositor needs to pause/resume in lockstep with the application. diff --git a/gfx/layers/ipc/PImageBridge.ipdl b/gfx/layers/ipc/PImageBridge.ipdl index 116419e530..ac15a8bf60 100644 --- a/gfx/layers/ipc/PImageBridge.ipdl +++ b/gfx/layers/ipc/PImageBridge.ipdl @@ -49,15 +49,12 @@ parent: // First step of the destruction sequence. This puts ImageBridge // in a state in which it can't send asynchronous messages - // so as to not race with the upcomming Stop message and destruction. - // In the child side, the Stop message is not sent right after WillStop, + // so as to not race with the channel getting closed. + // In the child side, the Closing the channel does not happen right after WillClose, // it is scheduled in the ImageBridgeChild's message queue in order to ensure // that all of the messages from the parent side have been received and processed - // before sending Stop, and that after Stop returns, there is no message in - // flight on any side and we can safely destroy the channel and threads. - sync WillStop(); - // Second step - sync Stop(); + // before sending closing the channel. + sync WillClose(); sync PCompositable(TextureInfo aInfo, nullable PImageContainer aImageContainer) returns (uint64_t id); diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h index 845c16ff6d..a702712225 100644 --- a/gfx/layers/opengl/CompositorOGL.h +++ b/gfx/layers/opengl/CompositorOGL.h @@ -207,6 +207,8 @@ public: virtual void Destroy() override; + virtual void DetachWidget() override { mWidget = nullptr; } + virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override { TextureFactoryIdentifier result = diff --git a/gfx/thebes/gfxBlur.cpp b/gfx/thebes/gfxBlur.cpp index c05daedd92..578491980c 100644 --- a/gfx/thebes/gfxBlur.cpp +++ b/gfx/thebes/gfxBlur.cpp @@ -175,16 +175,15 @@ struct BlurCacheKey : public PLDHashEntryHdr { // Only used for inset blurs bool mHasBorderRadius; - IntSize mSpreadRadius; IntSize mInnerMinSize; BlurCacheKey(IntSize aMinSize, IntSize aBlurRadius, RectCornerRadii* aCornerRadii, const Color& aShadowColor, BackendType aBackendType) : BlurCacheKey(aMinSize, IntSize(0, 0), - aBlurRadius, IntSize(0, 0), - aCornerRadii, aShadowColor, - false, false, aBackendType) + aBlurRadius, aCornerRadii, + aShadowColor, false, + false, aBackendType) {} explicit BlurCacheKey(const BlurCacheKey* aOther) @@ -195,12 +194,11 @@ struct BlurCacheKey : public PLDHashEntryHdr { , mCornerRadii(aOther->mCornerRadii) , mIsInset(aOther->mIsInset) , mHasBorderRadius(aOther->mHasBorderRadius) - , mSpreadRadius(aOther->mSpreadRadius) , mInnerMinSize(aOther->mInnerMinSize) { } explicit BlurCacheKey(IntSize aOuterMinSize, IntSize aInnerMinSize, - IntSize aBlurRadius, IntSize aSpreadRadius, + IntSize aBlurRadius, const RectCornerRadii* aCornerRadii, const Color& aShadowColor, bool aIsInset, bool aHasBorderRadius, BackendType aBackendType) @@ -211,7 +209,6 @@ struct BlurCacheKey : public PLDHashEntryHdr { , mCornerRadii(aCornerRadii ? *aCornerRadii : RectCornerRadii()) , mIsInset(aIsInset) , mHasBorderRadius(aHasBorderRadius) - , mSpreadRadius(aSpreadRadius) , mInnerMinSize(aInnerMinSize) { } @@ -238,7 +235,6 @@ struct BlurCacheKey : public PLDHashEntryHdr { hash = AddToHash(hash, (uint32_t)aKey->mBackend); if (aKey->mIsInset) { - hash = AddToHash(hash, aKey->mSpreadRadius.width, aKey->mSpreadRadius.height); hash = AddToHash(hash, aKey->mInnerMinSize.width, aKey->mInnerMinSize.height); hash = AddToHash(hash, HashBytes(&aKey->mHasBorderRadius, sizeof(bool))); } @@ -256,8 +252,7 @@ struct BlurCacheKey : public PLDHashEntryHdr { if (mIsInset) { return (mHasBorderRadius == aKey->mHasBorderRadius) && - (mInnerMinSize == aKey->mInnerMinSize) && - (mSpreadRadius == aKey->mSpreadRadius); + (mInnerMinSize == aKey->mInnerMinSize); } return true; @@ -340,16 +335,16 @@ class BlurCache final : public nsExpirationTracker BlurCacheData* LookupInsetBoxShadow(const IntSize aOuterMinSize, const IntSize aInnerMinSize, const IntSize& aBlurRadius, - const IntSize& aSpreadRadius, const RectCornerRadii* aCornerRadii, const Color& aShadowColor, const bool& aHasBorderRadius, BackendType aBackendType) { + bool insetBoxShadow = true; BlurCacheKey key(aOuterMinSize, aInnerMinSize, - aBlurRadius, aSpreadRadius, - aCornerRadii, aShadowColor, - true, aHasBorderRadius, aBackendType); + aBlurRadius, aCornerRadii, + aShadowColor, insetBoxShadow, + aHasBorderRadius, aBackendType); BlurCacheData* blur = mHashEntries.Get(key); if (blur) { MarkUsed(blur); @@ -635,7 +630,7 @@ DrawBoxShadows(DrawTarget& aDestDrawTarget, SourceSurface* aSourceBlur, aDstInner.Y(), aDstOuter.X()), RectWithEdgesTRBL(aSrcOuter.Y(), aSrcInner.X(), aSrcInner.Y(), aSrcOuter.X()), - aSkipRect); + aSkipRect); DrawCorner(aDestDrawTarget, aSourceBlur, RectWithEdgesTRBL(aDstOuter.Y(), aDstOuter.XMost(), @@ -671,6 +666,7 @@ DrawBoxShadows(DrawTarget& aDestDrawTarget, SourceSurface* aSourceBlur, RectWithEdgesTRBL(aSrcInner.Y(), aSrcInner.X(), aSrcInner.YMost(), aSrcOuter.X()), aSkipRect); + RepeatOrStretchSurface(aDestDrawTarget, aSourceBlur, RectWithEdgesTRBL(aDstInner.Y(), aDstOuter.XMost(), aDstInner.YMost(), aDstInner.XMost()), @@ -803,73 +799,6 @@ GetBoxShadowInsetPath(DrawTarget* aDrawTarget, return builder->Finish(); } -static void -ComputeRectsForInsetBoxShadow(IntSize aBlurRadius, - IntSize aSpreadRadius, - IntRect& aOutOuterRect, - IntRect& aOutInnerRect, - IntMargin& aOutPathMargins, - const Rect& aDestRect, - const Rect& aShadowClipRect, - bool aHasBorderRadius, - const RectCornerRadii& aInnerClipRectRadii) -{ - IntSize rectBufferSize = aBlurRadius + aSpreadRadius; - float cornerWidth = 0; - float cornerHeight = 0; - if (aHasBorderRadius) { - for (size_t i = 0; i < 4; i++) { - cornerWidth = std::max(cornerWidth, aInnerClipRectRadii[i].width); - cornerHeight = std::max(cornerHeight, aInnerClipRectRadii[i].height); - } - } - - // Create the inner rect to be the smallest possible size based on - // blur / spread / corner radii - IntMargin innerMargin = IntMargin(ceil(cornerHeight) + rectBufferSize.height, - ceil(cornerWidth) + rectBufferSize.width, - ceil(cornerHeight) + rectBufferSize.height, - ceil(cornerWidth) + rectBufferSize.width); - aOutPathMargins = innerMargin; - - // If we have a negative spread radius, we would not have enough - // size to actually do the blur. So the min size must be the abs() of the blur - // and spread radius. - IntSize minBlurSize(std::abs(aSpreadRadius.width) + std::abs(aBlurRadius.width), - std::abs(aSpreadRadius.height) + std::abs(aBlurRadius.height)); - - IntMargin minInnerMargins = IntMargin(ceil(cornerHeight) + minBlurSize.height, - ceil(cornerWidth) + minBlurSize.width, - ceil(cornerHeight) + minBlurSize.height, - ceil(cornerWidth) + minBlurSize.width); - - IntSize minInnerSize(minInnerMargins.LeftRight() + 1, - minInnerMargins.TopBottom() + 1); - - if (aShadowClipRect.height < minInnerSize.height) { - minInnerSize.height = aShadowClipRect.height; - } - - if (aShadowClipRect.width < minInnerSize.width) { - minInnerSize.width = aShadowClipRect.width; - } - - // Then expand the outer rect based on the size between the inner/outer rects - IntSize minOuterSize(minInnerSize); - IntMargin outerRectMargin(rectBufferSize.height, rectBufferSize.width, - rectBufferSize.height, rectBufferSize.width); - minOuterSize.width += outerRectMargin.LeftRight(); - minOuterSize.height += outerRectMargin.TopBottom(); - - aOutOuterRect = IntRect(IntPoint(), minOuterSize); - aOutInnerRect = IntRect(IntPoint(rectBufferSize.width, rectBufferSize.height), minInnerSize); - - if (aShadowClipRect.IsEmpty()) { - aOutInnerRect.width = 0; - aOutInnerRect.height = 0; - } -} - static void FillDestinationPath(gfxContext* aDestinationCtx, const Rect aDestinationRect, @@ -890,98 +819,84 @@ FillDestinationPath(gfxContext* aDestinationCtx, aDestinationCtx->Fill(); } -void +static void CacheInsetBlur(const IntSize aMinOuterSize, const IntSize aMinInnerSize, const IntSize& aBlurRadius, - const IntSize& aSpreadRadius, const RectCornerRadii* aCornerRadii, const Color& aShadowColor, const bool& aHasBorderRadius, BackendType aBackendType, - IntMargin aExtendBy, SourceSurface* aBoxShadow) { + bool isInsetBlur = true; BlurCacheKey key(aMinOuterSize, aMinInnerSize, - aBlurRadius, aSpreadRadius, - aCornerRadii, aShadowColor, - true, aHasBorderRadius, aBackendType); - BlurCacheData* data = new BlurCacheData(aBoxShadow, aExtendBy, key); + aBlurRadius, aCornerRadii, + aShadowColor, isInsetBlur, + aHasBorderRadius, aBackendType); + IntMargin extendDestBy(0, 0, 0, 0); + BlurCacheData* data = new BlurCacheData(aBoxShadow, extendDestBy, key); if (!gBlurCache->RegisterEntry(data)) { delete data; } } already_AddRefed -gfxAlphaBoxBlur::GetInsetBlur(IntMargin& aExtendDestBy, - IntMargin& aSlice, - const Rect aDestinationRect, - const Rect aShadowClipRect, - const IntSize& aBlurRadius, - const IntSize& aSpreadRadius, +gfxAlphaBoxBlur::GetInsetBlur(const mozilla::gfx::Rect aOuterRect, + const mozilla::gfx::Rect aWhitespaceRect, + const bool aIsDestRect, + const mozilla::gfx::Color& aShadowColor, + const mozilla::gfx::IntSize& aBlurRadius, + const bool aHasBorderRadius, const RectCornerRadii& aInnerClipRadii, - const Color& aShadowColor, - const bool& aHasBorderRadius, - const Point aShadowOffset, - bool& aMovedOffset, DrawTarget* aDestDrawTarget) { if (!gBlurCache) { gBlurCache = new BlurCache(); } - IntRect outerRect; - IntRect innerRect; - ComputeRectsForInsetBoxShadow(aBlurRadius, aSpreadRadius, - outerRect, innerRect, - aSlice, aDestinationRect, - aShadowClipRect, aHasBorderRadius, - aInnerClipRadii); - - // If we have a shadow offset larger than the min rect, - // there's no clean way we can properly create a min rect with the offset - // in the correct place and still render correctly. In those cases, - // fallback to just rendering the dest rect as is. - bool useDestRect = (std::abs(aShadowOffset.x) > aSlice.left) || - (std::abs(aShadowOffset.y) > aSlice.top); - aMovedOffset = false; - if (useDestRect) { - aDestinationRect.ToIntRect(&outerRect); - aShadowClipRect.ToIntRect(&innerRect); - aMovedOffset = true; - } - + IntSize outerSize((int)aOuterRect.width, (int)aOuterRect.height); + IntSize whitespaceSize((int)aWhitespaceRect.width, (int)aWhitespaceRect.height); BlurCacheData* cached = - gBlurCache->LookupInsetBoxShadow(outerRect.Size(), innerRect.Size(), - aBlurRadius, aSpreadRadius, - &aInnerClipRadii, aShadowColor, - aHasBorderRadius, + gBlurCache->LookupInsetBoxShadow(outerSize, whitespaceSize, + aBlurRadius, &aInnerClipRadii, + aShadowColor, aHasBorderRadius, aDestDrawTarget->GetBackendType()); - if (cached && !useDestRect) { - aExtendDestBy = cached->mExtendDest; - // Need to extend it twice: once for the outer rect and once for the inner rect. - aSlice += aExtendDestBy; - aSlice += aExtendDestBy; + if (cached && !aIsDestRect) { // So we don't forget the actual cached blur RefPtr cachedBlur = cached->mBlur; return cachedBlur.forget(); } - // Dirty rect and skip rect are null for the min inset shadow. - // When rendering inset box shadows, we respect the spread radius by changing - // the shape of the unblurred shadow, and can pass a spread radius of zero here. + // If we can do a min rect, the whitespace rect will be expanded in Init to + // aOuterRect. + Rect blurRect = aIsDestRect ? aOuterRect : aWhitespaceRect; IntSize zeroSpread(0, 0); - gfxContext* minGfxContext = Init(ThebesRect(outerRect), - zeroSpread, aBlurRadius, nullptr, nullptr); + gfxContext* minGfxContext = Init(ThebesRect(blurRect), + zeroSpread, aBlurRadius, + nullptr, nullptr); if (!minGfxContext) { return nullptr; } + // This is really annoying. When we create the AlphaBoxBlur, the gfxContext + // has a translation applied to it that is the topLeft point. This is actually + // the rect we gave it plus the blur radius. The rects we give this for the outer + // and whitespace rects are based at (0, 0). We could either translate those rects + // when we don't have a destination rect or ignore the translation when using + // the dest rect. The dest rects layout gives us expect this translation. + if (!aIsDestRect) { + minGfxContext->SetMatrix(gfxMatrix()); + } + DrawTarget* minDrawTarget = minGfxContext->GetDrawTarget(); + + // Fill in the path between the inside white space / outer rects + // NOT the inner frame RefPtr maskPath = - GetBoxShadowInsetPath(minDrawTarget, IntRectToRect(outerRect), - IntRectToRect(innerRect), aHasBorderRadius, + GetBoxShadowInsetPath(minDrawTarget, aOuterRect, + aWhitespaceRect, aHasBorderRadius, aInnerClipRadii); Color black(0.f, 0.f, 0.f, 1.f); @@ -989,48 +904,115 @@ gfxAlphaBoxBlur::GetInsetBlur(IntMargin& aExtendDestBy, minGfxContext->SetPath(maskPath); minGfxContext->Fill(); + // Create the A8 mask IntPoint topLeft; RefPtr minMask = DoBlur(minDrawTarget, &topLeft); if (!minMask) { return nullptr; } + // Fill in with the color we actually wanted RefPtr minInsetBlur = CreateBoxShadow(minMask, aShadowColor); if (!minInsetBlur) { return nullptr; } - IntRect blurRect(topLeft, minInsetBlur->GetSize()); - aExtendDestBy = blurRect - outerRect; - - if (useDestRect) { - // Since we're just going to paint the actual rect to the destination - aSlice.SizeTo(0, 0, 0, 0); - } else { - aSlice += aExtendDestBy; - aSlice += aExtendDestBy; - - CacheInsetBlur(outerRect.Size(), innerRect.Size(), - aBlurRadius, aSpreadRadius, - &aInnerClipRadii, aShadowColor, - aHasBorderRadius, aDestDrawTarget->GetBackendType(), - aExtendDestBy, minInsetBlur); - + if (!aIsDestRect) { + CacheInsetBlur(outerSize, whitespaceSize, + aBlurRadius, &aInnerClipRadii, + aShadowColor, aHasBorderRadius, + aDestDrawTarget->GetBackendType(), + minInsetBlur); } return minInsetBlur.forget(); } /*** - * Blur an inset box shadow by doing: - * 1) Create a minimal box shadow path that creates a frame. - * 2) Draw the box shadow portion over the destination surface. - * 3) The "inset" part is created by a clip rect that properly clips - * the alpha mask so that it has clean edges. We still create the full - * proper alpha mask, but let the clip deal with the clean edges. + * We create our minimal rect with 2 rects. + * The first is the inside whitespace rect, that is "cut out" + * from the box. This is (1). This must be the size + * of the blur radius + corner radius so we can have a big enough + * inside cut. * - * All parameters should already be in device pixels. + * The second (2) is one blur radius surrounding the inner + * frame of (1). This is the amount of blur space required + * to get a proper blend. + * + * B = one blur size + * W = one blur + corner radii - known as inner margin + * ___________________________________ + * | | + * | | | | + * | (2) | (1) | (2) | + * | B | W | B | + * | | | | + * | | | | + * | | | + * |________________________________| */ +static void GetBlurMargins(const bool aHasBorderRadius, + const RectCornerRadii& aInnerClipRadii, + const IntSize aBlurRadius, + Margin& aOutBlurMargin, + Margin& aOutInnerMargin) +{ + float cornerWidth = 0; + float cornerHeight = 0; + if (aHasBorderRadius) { + for (size_t i = 0; i < 4; i++) { + cornerWidth = std::max(cornerWidth, aInnerClipRadii[i].width); + cornerHeight = std::max(cornerHeight, aInnerClipRadii[i].height); + } + } + + // Only the inside whitespace size cares about the border radius size. + // Outer sizes only care about blur. + int width = cornerWidth + aBlurRadius.width; + int height = cornerHeight + aBlurRadius.height; + + aOutInnerMargin.SizeTo(height, width, height, width); + aOutBlurMargin.SizeTo(aBlurRadius.height, aBlurRadius.width, + aBlurRadius.height, aBlurRadius.width); +} + +static bool +GetInsetBoxShadowRects(const Margin aBlurMargin, + const Margin aInnerMargin, + const Rect aShadowClipRect, + const Rect aDestinationRect, + Rect& aOutWhitespaceRect, + Rect& aOutOuterRect) +{ + // We always copy (2 * blur radius) + corner radius worth of data to the destination rect + // This covers the blend of the path + the actual blur + // Need +1 so that we copy the edges correctly as we'll copy + // over the min box shadow corners then the +1 for the edges between + // Note, the (x,y) coordinates are from the blur margin + // since the frame outside the whitespace rect is 1 blur radius extra space. + Rect insideWhiteSpace(aBlurMargin.left, + aBlurMargin.top, + aInnerMargin.LeftRight() + 1, + aInnerMargin.TopBottom() + 1); + + // If the inner white space rect is larger than the shadow clip rect + // our approach does not work as we'll just copy one corner + // and cover the destination. In those cases, fallback to the destination rect + bool useDestRect = (aShadowClipRect.width <= aInnerMargin.LeftRight()) || + (aShadowClipRect.height <= aInnerMargin.TopBottom()); + + if (useDestRect) { + aOutWhitespaceRect = aShadowClipRect; + aOutOuterRect = aDestinationRect; + } else { + aOutWhitespaceRect = insideWhiteSpace; + aOutOuterRect = aOutWhitespaceRect; + aOutOuterRect.Inflate(aBlurMargin); + } + + return useDestRect; +} + void gfxAlphaBoxBlur::BlurInsetBox(gfxContext* aDestinationCtx, const Rect aDestinationRect, @@ -1043,45 +1025,63 @@ gfxAlphaBoxBlur::BlurInsetBox(gfxContext* aDestinationCtx, const Rect aSkipRect, const Point aShadowOffset) { - DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget(); - - // Blur inset shadows ALWAYS have a 0 spread radius. - if ((aBlurRadius.width <= 0 && aBlurRadius.height <= 0)) { + if ((aBlurRadius.width == 0 && aBlurRadius.height == 0) + || aShadowClipRect.IsEmpty()) { FillDestinationPath(aDestinationCtx, aDestinationRect, aShadowClipRect, aShadowColor, aHasBorderRadius, aInnerClipRadii); return; } - IntMargin extendDest; - IntMargin slice; - bool didMoveOffset; - RefPtr minInsetBlur = GetInsetBlur(extendDest, slice, - aDestinationRect, aShadowClipRect, - aBlurRadius, aSpreadRadius, - aInnerClipRadii, aShadowColor, - aHasBorderRadius, aShadowOffset, - didMoveOffset, destDrawTarget); - if (!minInsetBlur) { + DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget(); + + Margin innerMargin; + Margin blurMargin; + GetBlurMargins(aHasBorderRadius, aInnerClipRadii, aBlurRadius, + blurMargin, innerMargin); + + Rect whitespaceRect; + Rect outerRect; + bool useDestRect = GetInsetBoxShadowRects(blurMargin, innerMargin, aShadowClipRect, + aDestinationRect, whitespaceRect, outerRect); + + RefPtr minBlur = GetInsetBlur(outerRect, whitespaceRect, useDestRect, aShadowColor, + aBlurRadius, aHasBorderRadius, aInnerClipRadii, + destDrawTarget); + if (!minBlur) { return; } - Rect srcOuter(Point(), Size(minInsetBlur->GetSize())); - Rect srcInner = srcOuter; - srcInner.Deflate(Margin(slice)); + if (useDestRect) { + IntSize blurSize = minBlur->GetSize(); + Rect srcBlur(0, 0, blurSize.width, blurSize.height); + Rect destBlur = aDestinationRect; - Rect dstOuter(aDestinationRect); - if (!didMoveOffset) { - dstOuter.MoveBy(aShadowOffset); - } - dstOuter.Inflate(Margin(extendDest)); - Rect dstInner = dstOuter; - dstInner.Deflate(Margin(slice)); - - if (dstOuter.Size() == srcOuter.Size()) { - destDrawTarget->DrawSurface(minInsetBlur, dstOuter, srcOuter); + // The blur itself expands the rect by the blur margin, so we + // have to mimic that here. + destBlur.Inflate(blurMargin); + MOZ_ASSERT(srcBlur.Size() == destBlur.Size()); + destDrawTarget->DrawSurface(minBlur, destBlur, srcBlur); } else { - DrawBoxShadows(*destDrawTarget, minInsetBlur, - dstOuter, dstInner, + Rect srcOuter(outerRect); + Rect srcInner(srcOuter); + srcInner.Deflate(blurMargin); // The outer color fill + srcInner.Deflate(innerMargin); // The inner whitespace + + // The shadow clip rect already takes into account the spread radius + Rect outerFillRect(aShadowClipRect); + outerFillRect.Inflate(blurMargin); + FillDestinationPath(aDestinationCtx, aDestinationRect, outerFillRect, aShadowColor, false, RectCornerRadii()); + + // Inflate once for the frame around the whitespace + Rect destRect(aShadowClipRect); + destRect.Inflate(blurMargin); + + // Deflate for the blurred in white space + Rect destInnerRect(aShadowClipRect); + destInnerRect.Deflate(innerMargin); + + DrawBoxShadows(*destDrawTarget, minBlur, + destRect, destInnerRect, srcOuter, srcInner, aSkipRect); } diff --git a/gfx/thebes/gfxBlur.h b/gfx/thebes/gfxBlur.h index d76c7236bf..563bf71b99 100644 --- a/gfx/thebes/gfxBlur.h +++ b/gfx/thebes/gfxBlur.h @@ -167,17 +167,13 @@ public: protected: already_AddRefed - GetInsetBlur(mozilla::gfx::IntMargin& aExtendDestBy, - mozilla::gfx::IntMargin& aSlice, - const mozilla::gfx::Rect aDestinationRect, - const mozilla::gfx::Rect aShadowClipRect, - const mozilla::gfx::IntSize& aBlurRadius, - const mozilla::gfx::IntSize& aSpreadRadius, - const RectCornerRadii& aInnerClipRadii, + GetInsetBlur(const mozilla::gfx::Rect aOuterRect, + const mozilla::gfx::Rect aWhitespaceRect, + const bool aIsDestRect, const mozilla::gfx::Color& aShadowColor, - const bool& aHasBorderRadius, - const mozilla::gfx::Point aShadowOffset, - bool& aMovedOffset, + const mozilla::gfx::IntSize& aBlurRadius, + const bool aHasBorderRadius, + const RectCornerRadii& aInnerClipRadii, DrawTarget* aDestDrawTarget); /** diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 72cbdac292..f9f824dd90 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -839,20 +839,28 @@ gfxPlatform::ShutdownLayersIPC() } sLayersIPCIsUp = false; - if (XRE_IsParentProcess()) - { - // This must happen after the shutdown of media and widgets, which - // are triggered by the NS_XPCOM_SHUTDOWN_OBSERVER_ID notification. - gfx::VRManagerChild::ShutDown(); - layers::ImageBridgeChild::ShutDown(); + if (XRE_IsContentProcess()) { + + gfx::VRManagerChild::ShutDown(); + layers::ImageBridgeChild::ShutDown(); + layers::CompositorBridgeChild::ShutDown(); + + } else if (XRE_IsParentProcess()) { + + gfx::VRManagerChild::ShutDown(); + layers::ImageBridgeChild::ShutDown(); + layers::CompositorBridgeChild::ShutDown(); + #ifdef MOZ_WIDGET_GONK - layers::SharedBufferManagerChild::ShutDown(); + layers::SharedBufferManagerChild::ShutDown(); #endif - layers::CompositorBridgeParent::ShutDown(); - } else if (XRE_GetProcessType() == GeckoProcessType_Content) { - gfx::VRManagerChild::ShutDown(); - } + // This has to happen after shutting down the child protocols. + layers::CompositorBridgeParent::ShutDown(); + } else { + // TODO: There are other kind of processes and we should make sure gfx + // stuff is either not created there or shut down properly. + } } gfxPlatform::~gfxPlatform() diff --git a/js/ipc/WrapperAnswer.cpp b/js/ipc/WrapperAnswer.cpp index d4ecd7e101..07be205c86 100644 --- a/js/ipc/WrapperAnswer.cpp +++ b/js/ipc/WrapperAnswer.cpp @@ -78,7 +78,6 @@ WrapperAnswer::RecvPreventExtensions(const ObjectId& objId, ReturnStatus* rs) AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); RootedObject obj(cx, findObjectById(cx, objId)); @@ -110,7 +109,6 @@ WrapperAnswer::RecvGetPropertyDescriptor(const ObjectId& objId, const JSIDVarian AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); EmptyDesc(out); @@ -141,7 +139,6 @@ WrapperAnswer::RecvGetOwnPropertyDescriptor(const ObjectId& objId, const JSIDVar AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); EmptyDesc(out); @@ -172,7 +169,6 @@ WrapperAnswer::RecvDefineProperty(const ObjectId& objId, const JSIDVariant& idVa AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); RootedObject obj(cx, findObjectById(cx, objId)); @@ -201,7 +197,6 @@ WrapperAnswer::RecvDelete(const ObjectId& objId, const JSIDVariant& idVar, Retur AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); RootedObject obj(cx, findObjectById(cx, objId)); @@ -227,7 +222,6 @@ WrapperAnswer::RecvHas(const ObjectId& objId, const JSIDVariant& idVar, ReturnSt AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); *foundp = false; @@ -253,7 +247,6 @@ WrapperAnswer::RecvHasOwn(const ObjectId& objId, const JSIDVariant& idVar, Retur AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); *foundp = false; @@ -349,7 +342,6 @@ WrapperAnswer::RecvIsExtensible(const ObjectId& objId, ReturnStatus* rs, bool* r AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); *result = false; @@ -479,7 +471,6 @@ WrapperAnswer::RecvHasInstance(const ObjectId& objId, const JSVariant& vVar, Ret AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); RootedObject obj(cx, findObjectById(cx, objId)); @@ -507,7 +498,6 @@ WrapperAnswer::RecvGetBuiltinClass(const ObjectId& objId, ReturnStatus* rs, AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); RootedObject obj(cx, findObjectById(cx, objId)); @@ -533,7 +523,6 @@ WrapperAnswer::RecvIsArray(const ObjectId& objId, ReturnStatus* rs, AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); RootedObject obj(cx, findObjectById(cx, objId)); @@ -556,7 +545,6 @@ WrapperAnswer::RecvClassName(const ObjectId& objId, nsCString* name) AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); RootedObject obj(cx, findObjectById(cx, objId)); @@ -579,7 +567,6 @@ WrapperAnswer::RecvGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectO AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); RootedObject obj(cx, findObjectById(cx, objId)); @@ -605,7 +592,6 @@ WrapperAnswer::RecvRegExpToShared(const ObjectId& objId, ReturnStatus* rs, AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); RootedObject obj(cx, findObjectById(cx, objId)); @@ -632,7 +618,6 @@ WrapperAnswer::RecvGetPropertyKeys(const ObjectId& objId, const uint32_t& flags, AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); RootedObject obj(cx, findObjectById(cx, objId)); @@ -663,7 +648,6 @@ WrapperAnswer::RecvInstanceOf(const ObjectId& objId, const JSIID& iid, ReturnSta AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); *instanceof = false; @@ -691,7 +675,6 @@ WrapperAnswer::RecvDOMInstanceOf(const ObjectId& objId, const int& prototypeID, AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) return false; - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); *instanceof = false; diff --git a/js/public/GCHashTable.h b/js/public/GCHashTable.h index 3a61930ba2..113b8d3a2d 100644 --- a/js/public/GCHashTable.h +++ b/js/public/GCHashTable.h @@ -10,6 +10,7 @@ #include "js/GCPolicyAPI.h" #include "js/HashTable.h" #include "js/RootingAPI.h" +#include "js/SweepingAPI.h" #include "js/TracingAPI.h" namespace js { @@ -143,7 +144,6 @@ class GCHashMapOperations bool empty() const { return map().empty(); } uint32_t count() const { return map().count(); } size_t capacity() const { return map().capacity(); } - uint32_t generation() const { return map().generation(); } bool has(const Lookup& l) const { return map().lookup(l).found(); } }; @@ -209,6 +209,11 @@ class HandleBase> : public GCHashMapOperations>, A,B,C,D,E> {}; +template +class WeakCacheBase> + : public MutableGCHashMapOperations>, A,B,C,D,E> +{}; + // A GCHashSet is a HashSet with an additional trace method that knows // be traced to be kept alive will generally want to use this GCHashSet // specializeation in lieu of HashSet. @@ -272,7 +277,7 @@ class GCHashSetOperations using Range = typename Set::Range; using Enum = typename Set::Enum; - const Set& set() const { return static_cast(this)->extract(); } + const Set& set() const { return static_cast(this)->get(); } public: bool initialized() const { return set().initialized(); } @@ -282,7 +287,6 @@ class GCHashSetOperations bool empty() const { return set().empty(); } uint32_t count() const { return set().count(); } size_t capacity() const { return set().capacity(); } - uint32_t generation() const { return set().generation(); } bool has(const Lookup& l) const { return set().lookup(l).found(); } }; @@ -297,7 +301,7 @@ class MutableGCHashSetOperations using Range = typename Set::Range; using Enum = typename Set::Enum; - Set& set() { return static_cast(this)->extract(); } + Set& set() { return static_cast(this)->get(); } public: bool init(uint32_t len = 16) { return set().init(len); } @@ -336,37 +340,24 @@ template class RootedBase> : public MutableGCHashSetOperations>, T, HP, AP> { - using Set = GCHashSet; - - friend class GCHashSetOperations, T, HP, AP>; - const Set& extract() const { return *static_cast*>(this)->address(); } - - friend class MutableGCHashSetOperations, T, HP, AP>; - Set& extract() { return *static_cast*>(this)->address(); } }; template class MutableHandleBase> : public MutableGCHashSetOperations>, T, HP, AP> { - using Set = GCHashSet; - - friend class GCHashSetOperations, T, HP, AP>; - const Set& extract() const { - return *static_cast*>(this)->address(); - } - - friend class MutableGCHashSetOperations, T, HP, AP>; - Set& extract() { return *static_cast*>(this)->address(); } }; template class HandleBase> : public GCHashSetOperations>, T, HP, AP> { - using Set = GCHashSet; - friend class GCHashSetOperations, T, HP, AP>; - const Set& extract() const { return *static_cast*>(this)->address(); } +}; + +template +class WeakCacheBase> + : public MutableGCHashSetOperations>, T, HP, AP> +{ }; } /* namespace js */ diff --git a/js/public/GCPolicyAPI.h b/js/public/GCPolicyAPI.h index 4d249629e6..05ab4fbd33 100644 --- a/js/public/GCPolicyAPI.h +++ b/js/public/GCPolicyAPI.h @@ -61,6 +61,10 @@ struct StructGCPolicy tp->trace(trc); } + static void sweep(T* tp) { + return tp->sweep(); + } + static bool needsSweep(T* tp) { return tp->needsSweep(); } @@ -90,9 +94,6 @@ struct GCPointerPolicy if (*vp) js::UnsafeTraceManuallyBarrieredEdge(trc, vp, name); } - static bool needsSweep(T* vp) { - return js::gc::EdgeNeedsSweep(vp); - } }; template <> struct GCPolicy : public GCPointerPolicy {}; template <> struct GCPolicy : public GCPointerPolicy {}; diff --git a/js/public/HashTable.h b/js/public/HashTable.h index 3b0a5faa75..11745d49a6 100644 --- a/js/public/HashTable.h +++ b/js/public/HashTable.h @@ -1083,9 +1083,9 @@ class HashTable : private AllocPolicy static const size_t CAP_BITS = 30; public: - Entry* table; // entry storage - uint32_t gen:24; // entry storage generation number - uint32_t hashShift:8; // multiplicative hash shift + uint64_t gen:56; // entry storage generation number + uint64_t hashShift:8; // multiplicative hash shift + Entry* table; // entry storage uint32_t entryCount; // number of entries in table uint32_t removedCount; // removed entry sentinels in table @@ -1182,9 +1182,9 @@ class HashTable : private AllocPolicy public: explicit HashTable(AllocPolicy ap) : AllocPolicy(ap) - , table(nullptr) , gen(0) , hashShift(sHashBits) + , table(nullptr) , entryCount(0) , removedCount(0) #ifdef JS_DEBUG diff --git a/js/public/Proxy.h b/js/public/Proxy.h index c7f839d066..1939c4849d 100644 --- a/js/public/Proxy.h +++ b/js/public/Proxy.h @@ -59,7 +59,7 @@ class JS_FRIEND_API(Wrapper); * * ### Proxies and internal methods * - * ES2016 specifies 13 internal methods. The runtime semantics of just + * ES2016 specifies 13 internal methods. The runtime semantics of just * about everything a script can do to an object is specified in terms * of these internal methods. For example: * @@ -427,6 +427,7 @@ inline bool IsProxy(const JSObject* obj) return GetObjectClass(obj)->isProxy(); } +namespace detail { const uint32_t PROXY_EXTRA_SLOTS = 2; // Layout of the values stored by a proxy. Note that API clients require the @@ -463,7 +464,6 @@ struct ProxyDataLayout const uint32_t ProxyDataOffset = 2 * sizeof(void*); -// This method should only be used internally and by the accessors below. inline ProxyDataLayout* GetProxyDataLayout(JSObject* obj) { @@ -471,16 +471,25 @@ GetProxyDataLayout(JSObject* obj) return reinterpret_cast(reinterpret_cast(obj) + ProxyDataOffset); } -inline const BaseProxyHandler* -GetProxyHandler(JSObject* obj) +inline const ProxyDataLayout* +GetProxyDataLayout(const JSObject* obj) { - return GetProxyDataLayout(obj)->handler; + MOZ_ASSERT(IsProxy(obj)); + return reinterpret_cast(reinterpret_cast(obj) + + ProxyDataOffset); +} +} // namespace detail + +inline const BaseProxyHandler* +GetProxyHandler(const JSObject* obj) +{ + return detail::GetProxyDataLayout(obj)->handler; } inline const Value& -GetProxyPrivate(JSObject* obj) +GetProxyPrivate(const JSObject* obj) { - return GetProxyDataLayout(obj)->values->privateSlot; + return detail::GetProxyDataLayout(obj)->values->privateSlot; } inline JSObject* @@ -490,16 +499,16 @@ GetProxyTargetObject(JSObject* obj) } inline const Value& -GetProxyExtra(JSObject* obj, size_t n) +GetProxyExtra(const JSObject* obj, size_t n) { - MOZ_ASSERT(n < PROXY_EXTRA_SLOTS); - return GetProxyDataLayout(obj)->values->extraSlots[n]; + MOZ_ASSERT(n < detail::PROXY_EXTRA_SLOTS); + return detail::GetProxyDataLayout(obj)->values->extraSlots[n]; } inline void SetProxyHandler(JSObject* obj, const BaseProxyHandler* handler) { - GetProxyDataLayout(obj)->handler = handler; + detail::GetProxyDataLayout(obj)->handler = handler; } JS_FRIEND_API(void) @@ -508,8 +517,8 @@ SetValueInProxy(Value* slot, const Value& value); inline void SetProxyExtra(JSObject* obj, size_t n, const Value& extra) { - MOZ_ASSERT(n < PROXY_EXTRA_SLOTS); - Value* vp = &GetProxyDataLayout(obj)->values->extraSlots[n]; + MOZ_ASSERT(n < detail::PROXY_EXTRA_SLOTS); + Value* vp = &detail::GetProxyDataLayout(obj)->values->extraSlots[n]; // Trigger a barrier before writing the slot. if (vp->isMarkable() || extra.isMarkable()) @@ -519,13 +528,13 @@ SetProxyExtra(JSObject* obj, size_t n, const Value& extra) } inline bool -IsScriptedProxy(JSObject* obj) +IsScriptedProxy(const JSObject* obj) { return IsProxy(obj) && GetProxyHandler(obj)->isScripted(); } inline const Value& -GetReservedOrProxyPrivateSlot(JSObject* obj, size_t slot) +GetReservedOrProxyPrivateSlot(const JSObject* obj, size_t slot) { MOZ_ASSERT(slot == 0); MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)) || IsProxy(obj)); diff --git a/js/public/SweepingAPI.h b/js/public/SweepingAPI.h new file mode 100644 index 0000000000..f6d98da954 --- /dev/null +++ b/js/public/SweepingAPI.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef js_SweepingAPI_h +#define js_SweepingAPI_h + +#include "js/HeapAPI.h" + +namespace js { +template +class WeakCacheBase {}; +} // namespace js + +namespace JS { +template class WeakCache; + +namespace shadow { +JS_PUBLIC_API(void) +RegisterWeakCache(JS::Zone* zone, JS::WeakCache* cachep); +} // namespace shadow + +// A WeakCache stores the given Sweepable container and links itself into a +// list of such caches that are swept during each GC. +template +class WeakCache : public js::WeakCacheBase, + private mozilla::LinkedListElement> +{ + friend class mozilla::LinkedListElement>; + friend class mozilla::LinkedList>; + + WeakCache(const WeakCache&) = delete; + + using SweepFn = void (*)(T*); + SweepFn sweeper; + T cache; + + public: + template + WeakCache(Zone* zone, U&& initial) + : cache(mozilla::Forward(initial)) + { + sweeper = js::GCPolicy::sweep; + shadow::RegisterWeakCache(zone, reinterpret_cast*>(this)); + } + WeakCache(WeakCache&& other) + : sweeper(other.sweeper), + cache(mozilla::Forward(other.cache)) + { + } + + const T& get() const { return cache; } + T& get() { return cache; } + + void sweep() { sweeper(&cache); } +}; + +} // namespace JS + +#endif // js_SweepingAPI_h diff --git a/js/public/TracingAPI.h b/js/public/TracingAPI.h index 9472fb7e4f..ad378b0a0c 100644 --- a/js/public/TracingAPI.h +++ b/js/public/TracingAPI.h @@ -347,6 +347,7 @@ extern JS_PUBLIC_API(void) UnsafeTraceManuallyBarrieredEdge(JSTracer* trc, T* edgep, const char* name); namespace gc { +// Return true if the given edge is not live and is about to be swept. template extern JS_PUBLIC_API(bool) EdgeNeedsSweep(JS::Heap* edgep); diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index 7766070d02..cd83688e4e 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -96,7 +96,6 @@ enum AsmJSAtomicsBuiltinFunction AsmJSAtomicsBuiltin_exchange, AsmJSAtomicsBuiltin_load, AsmJSAtomicsBuiltin_store, - AsmJSAtomicsBuiltin_fence, AsmJSAtomicsBuiltin_add, AsmJSAtomicsBuiltin_sub, AsmJSAtomicsBuiltin_and, @@ -1735,7 +1734,6 @@ class MOZ_STACK_CLASS ModuleValidator !addStandardLibraryAtomicsName("exchange", AsmJSAtomicsBuiltin_exchange) || !addStandardLibraryAtomicsName("load", AsmJSAtomicsBuiltin_load) || !addStandardLibraryAtomicsName("store", AsmJSAtomicsBuiltin_store) || - !addStandardLibraryAtomicsName("fence", AsmJSAtomicsBuiltin_fence) || !addStandardLibraryAtomicsName("add", AsmJSAtomicsBuiltin_add) || !addStandardLibraryAtomicsName("sub", AsmJSAtomicsBuiltin_sub) || !addStandardLibraryAtomicsName("and", AsmJSAtomicsBuiltin_and) || @@ -4106,16 +4104,6 @@ CheckSharedArrayAtomicAccess(FunctionValidator& f, ParseNode* viewName, ParseNod return true; } -static bool -CheckAtomicsFence(FunctionValidator& f, ParseNode* call, Type* type) -{ - if (CallArgListLength(call) != 0) - return f.fail(call, "Atomics.fence must be passed 0 arguments"); - - *type = Type::Void; - return f.encoder().writeExpr(Expr::AtomicsFence); -} - static bool WriteAtomicOperator(FunctionValidator& f, Expr opcode, size_t* viewTypeAt) { @@ -4309,8 +4297,6 @@ CheckAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSAtomicsB return CheckAtomicsLoad(f, callNode, type); case AsmJSAtomicsBuiltin_store: return CheckAtomicsStore(f, callNode, type); - case AsmJSAtomicsBuiltin_fence: - return CheckAtomicsFence(f, callNode, type); case AsmJSAtomicsBuiltin_add: return CheckAtomicsBinop(f, callNode, type, AtomicFetchAddOp); case AsmJSAtomicsBuiltin_sub: @@ -7371,7 +7357,6 @@ ValidateAtomicsBuiltinFunction(JSContext* cx, const AsmJSGlobal& global, HandleV case AsmJSAtomicsBuiltin_exchange: native = atomics_exchange; break; case AsmJSAtomicsBuiltin_load: native = atomics_load; break; case AsmJSAtomicsBuiltin_store: native = atomics_store; break; - case AsmJSAtomicsBuiltin_fence: native = atomics_fence; break; case AsmJSAtomicsBuiltin_add: native = atomics_add; break; case AsmJSAtomicsBuiltin_sub: native = atomics_sub; break; case AsmJSAtomicsBuiltin_and: native = atomics_and; break; diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp index a6c5884844..2d42d5aff1 100644 --- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -81,6 +81,16 @@ Unify(ExprType one, ExprType two) return ExprType::Void; } +static bool +IsI64Implemented() +{ +#ifdef JS_CPU_X64 + return true; +#else + return false; +#endif +} + class FunctionDecoder { JSContext* cx_; @@ -108,11 +118,9 @@ class FunctionDecoder return Fail(cx_, d_, str); } bool checkI64Support() { -#ifdef JS_CPU_X64 + if (!IsI64Implemented()) + return fail("i64 NYI on this platform"); return true; -#else - return fail("i64 NYI on this platform"); -#endif } MOZ_WARN_UNUSED_RESULT bool pushBlock() { @@ -562,15 +570,12 @@ DecodeBranch(FunctionDecoder& f, Expr expr, ExprType* type) if (!f.d().readVarU32(&relativeDepth)) return f.fail("expected relative depth"); - if (!f.branchWithType(relativeDepth, ExprType::Void)) - return f.fail("branch depth exceeds current nesting level"); - - Expr value; - if (!f.d().readExpr(&value)) + ExprType brType; + if (!DecodeExpr(f, &brType)) return f.fail("expected branch value"); - if (value != Expr::Nop) - return f.fail("NYI: branch values"); + if (!f.branchWithType(relativeDepth, brType)) + return f.fail("branch depth exceeds current nesting level"); if (expr == Expr::BrIf) { ExprType actual; @@ -805,9 +810,8 @@ DecodeExpr(FunctionDecoder& f, ExprType* type) DecodeConversionOperator(f, ValType::I32, ValType::I64, type); case Expr::I32TruncSF32: case Expr::I32TruncUF32: - return DecodeConversionOperator(f, ValType::I32, ValType::F32, type); case Expr::I32ReinterpretF32: - return f.fail("NYI: reinterpret"); + return DecodeConversionOperator(f, ValType::I32, ValType::F32, type); case Expr::I32TruncSF64: case Expr::I32TruncUF64: return DecodeConversionOperator(f, ValType::I32, ValType::F64, type); @@ -821,15 +825,13 @@ DecodeExpr(FunctionDecoder& f, ExprType* type) DecodeConversionOperator(f, ValType::I64, ValType::F32, type); case Expr::I64TruncSF64: case Expr::I64TruncUF64: + case Expr::I64ReinterpretF64: return f.checkI64Support() && DecodeConversionOperator(f, ValType::I64, ValType::F64, type); - case Expr::I64ReinterpretF64: - return f.fail("NYI: i64"); case Expr::F32ConvertSI32: case Expr::F32ConvertUI32: - return DecodeConversionOperator(f, ValType::F32, ValType::I32, type); case Expr::F32ReinterpretI32: - return f.fail("NYI: reinterpret"); + return DecodeConversionOperator(f, ValType::F32, ValType::I32, type); case Expr::F32ConvertSI64: case Expr::F32ConvertUI64: return f.checkI64Support() && @@ -841,10 +843,9 @@ DecodeExpr(FunctionDecoder& f, ExprType* type) return DecodeConversionOperator(f, ValType::F64, ValType::I32, type); case Expr::F64ConvertSI64: case Expr::F64ConvertUI64: + case Expr::F64ReinterpretI64: return f.checkI64Support() && DecodeConversionOperator(f, ValType::F64, ValType::I64, type); - case Expr::F64ReinterpretI64: - return f.fail("NYI: i64"); case Expr::F64PromoteF32: return DecodeConversionOperator(f, ValType::F64, ValType::F32, type); case Expr::I32Load8S: @@ -1083,14 +1084,16 @@ DecodeFunctionTable(JSContext* cx, Decoder& d, ModuleGeneratorData* init) static bool CheckTypeForJS(JSContext* cx, Decoder& d, const Sig& sig) { + bool allowI64 = IsI64Implemented() && JitOptions.wasmTestMode; + for (ValType argType : sig.args()) { - if (argType == ValType::I64) + if (argType == ValType::I64 && !allowI64) return Fail(cx, d, "cannot import/export i64 argument"); if (IsSimdType(argType)) return Fail(cx, d, "cannot import/export SIMD argument"); } - if (sig.ret() == ExprType::I64) + if (sig.ret() == ExprType::I64 && !allowI64) return Fail(cx, d, "cannot import/export i64 return type"); if (IsSimdType(sig.ret())) return Fail(cx, d, "cannot import/export SIMD return type"); diff --git a/js/src/asmjs/WasmBinary.h b/js/src/asmjs/WasmBinary.h index 159b281815..b5a5416fdb 100644 --- a/js/src/asmjs/WasmBinary.h +++ b/js/src/asmjs/WasmBinary.h @@ -275,7 +275,6 @@ enum class Expr F64Atan2, // Atomics - AtomicsFence, I32AtomicsCompareExchange, I32AtomicsExchange, I32AtomicsLoad, diff --git a/js/src/asmjs/WasmBinaryToText.cpp b/js/src/asmjs/WasmBinaryToText.cpp index ee4cd8ce4d..379a988160 100644 --- a/js/src/asmjs/WasmBinaryToText.cpp +++ b/js/src/asmjs/WasmBinaryToText.cpp @@ -32,7 +32,9 @@ using namespace js; using namespace js::wasm; using mozilla::CheckedInt; +using mozilla::IsInfinite; using mozilla::IsNaN; +using mozilla::IsNegativeZero; struct WasmRenderContext { @@ -43,11 +45,13 @@ struct WasmRenderContext DeclaredSigVector signatures; Uint32Vector funcSigs; + Uint32Vector funcLocals; Uint32Vector importSigs; - bool has_return; + + uint32_t currentFuncIndex; WasmRenderContext(JSContext* cx, Decoder& d, StringBuffer& buffer) - : cx(cx), d(d), buffer(buffer), indent(0) + : cx(cx), d(d), buffer(buffer), indent(0), currentFuncIndex(0) {} }; @@ -80,9 +84,46 @@ RenderInt32(WasmRenderContext& c, int32_t num) return NumberValueToStringBuffer(c.cx, Int32Value(num), c.buffer); } +static bool +RenderInt64(WasmRenderContext& c, int64_t num) +{ + if (num < 0 && !c.buffer.append("-")) + return false; + if (!num) + return c.buffer.append("0"); + + int64_t abs = mozilla::Abs(num); + int64_t n = abs; + uint64_t pow = 1; + while (n) { + pow *= 10; + n /= 10; + } + pow /= 10; + + n = abs; + while (pow) { + if (!c.buffer.append("0123456789"[n / pow])) + return false; + n -= (n / pow) * pow; + pow /= 10; + } + + return true; +} + static bool RenderDouble(WasmRenderContext& c, double num) { + if (IsNegativeZero(num)) + return c.buffer.append("-0"); + if (IsNaN(num)) + return c.buffer.append("nan"); + if (IsInfinite(num)) { + if (num > 0) + return c.buffer.append("infinity"); + return c.buffer.append("-infinity"); + } return NumberValueToStringBuffer(c.cx, DoubleValue(num), c.buffer); } @@ -191,6 +232,9 @@ RenderCall(WasmRenderContext& c) if (!RenderInt32(c, funcIndex)) return false; + if (funcIndex >= c.funcSigs.length()) + return RenderFail(c, "callee index out of range"); + uint32_t sigIndex = c.funcSigs[funcIndex]; if (!RenderCallWithSig(c, sigIndex)) return false; @@ -211,6 +255,9 @@ RenderCallImport(WasmRenderContext& c) if (!RenderInt32(c, importIndex)) return false; + if (importIndex >= c.importSigs.length()) + return RenderFail(c, "import index out of range"); + uint32_t sigIndex = c.importSigs[importIndex]; if (!RenderCallWithSig(c, sigIndex)) return false; @@ -273,14 +320,11 @@ RenderConstI64(WasmRenderContext& c) if (!c.buffer.append("(i64.const ")) return false; - uint64_t u64; - if (!c.d.readVarU64(&u64)) + int64_t i64; + if (!c.d.readVarS64(&i64)) return RenderFail(c, "unable to read i64.const immediate"); - if (u64 != (uint64_t)(double)u64) - return RenderFail(c, "unable to render i64.const immediate"); - - if (!RenderDouble(c, u64)) + if (!RenderInt64(c, i64)) return false; if (!c.buffer.append(")")) @@ -343,6 +387,9 @@ RenderGetLocal(WasmRenderContext& c) if (!c.d.readVarU32(&localIndex)) return RenderFail(c, "unable to read get_local index"); + if (localIndex >= c.funcLocals[c.currentFuncIndex]) + return RenderFail(c, "get_local index out of range"); + if (!c.buffer.append("(get_local $var$")) return false; if (!RenderInt32(c, localIndex)) @@ -359,6 +406,9 @@ RenderSetLocal(WasmRenderContext& c) if (!c.d.readVarU32(&localIndex)) return RenderFail(c, "unable to read set_local index"); + if (localIndex >= c.funcLocals[c.currentFuncIndex]) + return RenderFail(c, "set_local index out of range"); + if (!c.buffer.append("(set_local $var$")) return false; if (!RenderInt32(c, localIndex)) @@ -434,6 +484,7 @@ RenderUnaryOperator(WasmRenderContext& c, Expr expr, ValType argType) bool success = false; switch (expr) { + case Expr::I32Eqz: success = c.buffer.append("i32.eqz"); break; case Expr::I32Clz: success = c.buffer.append("i32.clz"); break; case Expr::I32Ctz: success = c.buffer.append("i32.ctz"); break; case Expr::I32Popcnt: success = c.buffer.append("i32.popcnt"); break; @@ -514,7 +565,7 @@ RenderBinaryOperator(WasmRenderContext& c, Expr expr, ValType argType) case Expr::F64Mul: success = c.buffer.append("f64.mul"); break; case Expr::F64Div: success = c.buffer.append("f64.div"); break; case Expr::F64Min: success = c.buffer.append("f64.min"); break; - case Expr::F64Max: success = c.buffer.append("f64.mix"); break; + case Expr::F64Max: success = c.buffer.append("f64.max"); break; default: return false; } if (!success) @@ -598,7 +649,7 @@ RenderConversionOperator(WasmRenderContext& c, Expr expr, ValType to, ValType ar bool success = false; switch (expr) { - case Expr::I32WrapI64: success = c.buffer.append("i32.warp/i64"); break; + case Expr::I32WrapI64: success = c.buffer.append("i32.wrap/i64"); break; case Expr::I32TruncSF32: success = c.buffer.append("i32.trunc_s/f32"); break; case Expr::I32TruncUF32: success = c.buffer.append("i32.trunc_u/f32"); break; case Expr::I32ReinterpretF32: success = c.buffer.append("i32.reinterpret/f32"); break; @@ -689,6 +740,12 @@ RenderLoadStoreAddress(WasmRenderContext& c) return false; } + uint32_t alignLog2 = flags; + if (!c.buffer.append(" align=")) + return false; + if (!RenderInt32(c, 1 << alignLog2)) + return false; + if (!c.buffer.append(" ")) return false; @@ -794,34 +851,31 @@ RenderStore(WasmRenderContext& c, Expr expr, ValType storeType) static bool RenderBranch(WasmRenderContext& c, Expr expr) { + MOZ_ASSERT(expr == Expr::BrIf || expr == Expr::Br); + uint32_t relativeDepth; if (!c.d.readVarU32(&relativeDepth)) return RenderFail(c, "expected relative depth"); - Expr value; - if (!c.d.readExpr(&value)) - return RenderFail(c, "expected branch value"); - if (value != Expr::Nop) - return RenderFail(c, "NYI: branch values"); + if (expr == Expr::BrIf ? !c.buffer.append("(br_if ") : !c.buffer.append("(br ")) + return false; + + if (!RenderInt32(c, relativeDepth)) + return false; + + if (!c.buffer.append(" ")) + return false; + + if (!RenderExpr(c)) + return false; if (expr == Expr::BrIf) { - if (!c.buffer.append("(br_if ")) - return false; - - if (!RenderInt32(c, relativeDepth)) - return false; - if (!c.buffer.append(" ")) return false; - if (!RenderExpr(c)) return false; - } else { - if (!c.buffer.append("(br ")) - return false; - if (!RenderInt32(c, relativeDepth)) - return false; } + if (!c.buffer.append(")")) return false; return true; @@ -874,7 +928,7 @@ RenderReturn(WasmRenderContext& c) if (!c.buffer.append("(return")) return false; - if (c.has_return) { + if (c.signatures[c.funcSigs[c.currentFuncIndex]].ret() != ExprType::Void) { if (!c.buffer.append(" ")) return false; if (!RenderExpr(c)) @@ -886,6 +940,30 @@ RenderReturn(WasmRenderContext& c) return true; } +static bool +RenderSelect(WasmRenderContext& c) +{ + if (!c.buffer.append("(select ")) + return false; + + if (!RenderExpr(c)) + return false; + + if (!c.buffer.append(" ")) + return false; + + if (!RenderExpr(c)) + return false; + + if (!c.buffer.append(" ")) + return false; + + if (!RenderExpr(c)) + return false; + + return c.buffer.append(")"); +} + static bool RenderExpr(WasmRenderContext& c) { @@ -927,12 +1005,13 @@ RenderExpr(WasmRenderContext& c) case Expr::I32Clz: case Expr::I32Ctz: case Expr::I32Popcnt: + case Expr::I32Eqz: return RenderUnaryOperator(c, expr, ValType::I32); case Expr::I64Clz: case Expr::I64Ctz: case Expr::I64Popcnt: return RenderFail(c, "NYI: i64") && - RenderUnaryOperator(c, expr, ValType::I64); + RenderUnaryOperator(c, expr, ValType::I64); case Expr::F32Abs: case Expr::F32Neg: case Expr::F32Ceil: @@ -1039,9 +1118,8 @@ RenderExpr(WasmRenderContext& c) return RenderConversionOperator(c, expr, ValType::I32, ValType::I64); case Expr::I32TruncSF32: case Expr::I32TruncUF32: - return RenderConversionOperator(c, expr, ValType::I32, ValType::F32); case Expr::I32ReinterpretF32: - return RenderFail(c, "NYI: reinterpret"); + return RenderConversionOperator(c, expr, ValType::I32, ValType::F32); case Expr::I32TruncSF64: case Expr::I32TruncUF64: return RenderConversionOperator(c, expr, ValType::I32, ValType::F64); @@ -1053,18 +1131,15 @@ RenderExpr(WasmRenderContext& c) return RenderConversionOperator(c, expr, ValType::I64, ValType::F32); case Expr::I64TruncSF64: case Expr::I64TruncUF64: - return RenderConversionOperator(c, expr, ValType::I64, ValType::F64); case Expr::I64ReinterpretF64: - return RenderFail(c, "NYI: i64"); + return RenderConversionOperator(c, expr, ValType::I64, ValType::F64); case Expr::F32ConvertSI32: case Expr::F32ConvertUI32: - return RenderConversionOperator(c, expr, ValType::F32, ValType::I32); case Expr::F32ReinterpretI32: - return RenderFail(c, "NYI: reinterpret"); + return RenderConversionOperator(c, expr, ValType::F32, ValType::I32); case Expr::F32ConvertSI64: case Expr::F32ConvertUI64: - return RenderFail(c, "NYI: i64") && - RenderConversionOperator(c, expr, ValType::F32, ValType::I64); + return RenderConversionOperator(c, expr, ValType::F32, ValType::I64); case Expr::F32DemoteF64: return RenderConversionOperator(c, expr, ValType::F32, ValType::I64); case Expr::F64ConvertSI32: @@ -1073,8 +1148,7 @@ RenderExpr(WasmRenderContext& c) case Expr::F64ConvertSI64: case Expr::F64ConvertUI64: case Expr::F64ReinterpretI64: - return RenderFail(c, "NYI: i64") && - RenderConversionOperator(c, expr, ValType::F64, ValType::I64); + return RenderConversionOperator(c, expr, ValType::F64, ValType::I64); case Expr::F64PromoteF32: return RenderConversionOperator(c, expr, ValType::F64, ValType::F32); case Expr::I32Load8S: @@ -1120,6 +1194,8 @@ RenderExpr(WasmRenderContext& c) return RenderBrTable(c); case Expr::Return: return RenderReturn(c); + case Expr::Select: + return RenderSelect(c); default: // Note: it's important not to remove this default since readExpr() // can return Expr values for which there is no enumerator. @@ -1274,18 +1350,22 @@ RenderFunctionTable(WasmRenderContext& c) if (!c.d.readVarU32(&numTableElems)) return RenderFail(c, "expected number of table elems"); - Uint32Vector elems; - if (!elems.resize(numTableElems)) + if (!c.buffer.append("(table ")) return false; for (uint32_t i = 0; i < numTableElems; i++) { uint32_t funcIndex; if (!c.d.readVarU32(&funcIndex)) return RenderFail(c, "expected table element"); - - elems[i] = funcIndex; + if (!RenderInt32(c, funcIndex)) + return false; + if (!c.buffer.append(" ")) + return false; } + if (!c.buffer.append(")")) + return false; + if (!c.d.finishSection(sectionStart)) return RenderFail(c, "table section byte size mismatch"); @@ -1398,6 +1478,9 @@ RenderMemory(WasmRenderContext& c, uint32_t* memInitial, uint32_t* memMax) if (!c.d.finishSection(sectionStart)) return RenderFail(c, "memory section byte size mismatch"); + if (exported && !c.buffer.append("(export \"memory\" memory)")) + return false; + return true; } @@ -1423,6 +1506,9 @@ RenderFunctionExport(WasmRenderContext& c) if (!c.d.readVarU32(&funcIndex)) return RenderFail(c, "expected export internal index"); + if (funcIndex >= c.funcSigs.length()) + return RenderFail(c, "export function index out of range"); + if (!RenderIndent(c)) return false; if (!c.buffer.append("(export \"")) @@ -1507,6 +1593,11 @@ RenderFunctionBody(WasmRenderContext& c, uint32_t funcIndex, uint32_t paramsNum) return false; } + if (funcIndex >= c.funcLocals.length() && !c.funcLocals.resize(funcIndex + 1)) + return false; + + c.funcLocals[funcIndex] = localsNum + paramsNum; + while (c.d.currentPosition() < bodyEnd) { if (!RenderFullLine(c)) return false; @@ -1553,7 +1644,7 @@ RenderFunctionBodies(WasmRenderContext& c) if (!c.buffer.append("\n")) return false; - c.has_return = sig.ret() != ExprType::Void; + c.currentFuncIndex = funcIndex; c.indent++; if (!RenderFunctionBody(c, funcIndex, sig.args().length())) diff --git a/js/src/asmjs/WasmIonCompile.cpp b/js/src/asmjs/WasmIonCompile.cpp index e0de6026d5..d277c4f5a4 100644 --- a/js/src/asmjs/WasmIonCompile.cpp +++ b/js/src/asmjs/WasmIonCompile.cpp @@ -17,6 +17,7 @@ */ #include "asmjs/WasmIonCompile.h" + #include "asmjs/WasmGenerator.h" #include "jit/CodeGenerator.h" @@ -36,7 +37,23 @@ typedef Vector BlockVector; class FunctionCompiler { private: - typedef Vector BlocksVector; + struct ControlFlowPatch { + MControlInstruction* ins; + uint32_t index; + ControlFlowPatch(MControlInstruction* ins, uint32_t index) + : ins(ins), + index(index) + {} + }; + + typedef Vector ControlFlowPatchVector; + typedef Vector ControlFlowPatchsVector; + + public: + class Call; + + private: + typedef Vector CallVector; ModuleGeneratorThreadView& mg_; Decoder& decoder_; @@ -50,10 +67,12 @@ class FunctionCompiler MIRGenerator& mirGen_; MBasicBlock* curBlock_; + CallVector callStack_; + uint32_t maxStackArgBytes_; uint32_t loopDepth_; uint32_t blockDepth_; - BlocksVector targets_; + ControlFlowPatchsVector blockPatches_; FuncCompileResults& compileResults_; @@ -74,6 +93,7 @@ class FunctionCompiler info_(mirGen.info()), mirGen_(mirGen), curBlock_(nullptr), + maxStackArgBytes_(0), loopDepth_(0), blockDepth_(0), compileResults_(compileResults) @@ -143,13 +163,16 @@ class FunctionCompiler return true; } - void checkPostconditions() + void finish() { + mirGen().initWasmMaxStackArgBytes(maxStackArgBytes_); + + MOZ_ASSERT(callStack_.empty()); MOZ_ASSERT(loopDepth_ == 0); MOZ_ASSERT(blockDepth_ == 0); #ifdef DEBUG - for (BlockVector& vec : targets_) { - MOZ_ASSERT(vec.empty()); + for (ControlFlowPatchVector& patches : blockPatches_) { + MOZ_ASSERT(patches.empty()); } #endif MOZ_ASSERT(inDeadCode()); @@ -485,6 +508,15 @@ class FunctionCompiler return ins; } + MDefinition* reinterpret(MDefinition* op, MIRType to) + { + if (inDeadCode()) + return nullptr; + auto* ins = MAsmReinterpret::New(alloc(), op, to); + curBlock_->add(ins); + return ins; + } + template MDefinition* truncate(MDefinition* op, bool isUnsigned) { @@ -557,14 +589,6 @@ class FunctionCompiler curBlock_->add(store); } - void memoryBarrier(MemoryBarrierBits type) - { - if (inDeadCode()) - return; - MMemoryBarrier* ins = MMemoryBarrier::New(alloc(), type); - curBlock_->add(ins); - } - MDefinition* atomicLoadHeap(MDefinition* base, const MAsmJSHeapAccess& access) { if (inDeadCode()) @@ -651,8 +675,8 @@ class FunctionCompiler // always ABIStackAlignment-aligned, but don't forget to account for // ShadowStackSpace and any other ABI warts. ABIArgGenerator abi; - if (abi.stackBytesConsumedSoFar() > mirGen_.maxAsmJSStackArgBytes()) - mirGen_.setAsmJSMaxStackArgBytes(abi.stackBytesConsumedSoFar()); + + propagateMaxStackArgBytes(abi.stackBytesConsumedSoFar()); CallSiteDesc callDesc(0, CallSiteDesc::Relative); curBlock_->add(MAsmJSInterruptCheck::New(alloc())); @@ -706,7 +730,6 @@ class FunctionCompiler { uint32_t lineOrBytecode_; ABIArgGenerator abi_; - uint32_t prevMaxStackBytes_; uint32_t maxChildStackBytes_; uint32_t spIncrement_; MAsmJSCall::Args regArgs_; @@ -718,18 +741,17 @@ class FunctionCompiler public: Call(FunctionCompiler& f, uint32_t lineOrBytecode) : lineOrBytecode_(lineOrBytecode), - prevMaxStackBytes_(0), maxChildStackBytes_(0), spIncrement_(0), childClobbers_(false) { } }; - void startCallArgs(Call* call) + bool startCallArgs(Call* call) { - if (inDeadCode()) - return; - call->prevMaxStackBytes_ = mirGen().resetAsmJSMaxStackArgBytes(); + // Always push calls to maintain the invariant that if we're inDeadCode + // in finishCallArgs, we have something to pop. + return callStack_.append(call); } bool passArg(MDefinition* argDef, ValType type, Call* call) @@ -737,43 +759,52 @@ class FunctionCompiler if (inDeadCode()) return true; - uint32_t childStackBytes = mirGen().resetAsmJSMaxStackArgBytes(); - call->maxChildStackBytes_ = Max(call->maxChildStackBytes_, childStackBytes); - if (childStackBytes > 0 && !call->stackArgs_.empty()) - call->childClobbers_ = true; - ABIArg arg = call->abi_.next(ToMIRType(type)); - if (arg.kind() == ABIArg::Stack) { - MAsmJSPassStackArg* mir = MAsmJSPassStackArg::New(alloc(), arg.offsetFromArgBase(), - argDef); - curBlock_->add(mir); - if (!call->stackArgs_.append(mir)) - return false; - } else { - if (!call->regArgs_.append(MAsmJSCall::Arg(arg.reg(), argDef))) - return false; + if (arg.kind() != ABIArg::Stack) + return call->regArgs_.append(MAsmJSCall::Arg(arg.reg(), argDef)); + + auto* mir = MAsmJSPassStackArg::New(alloc(), arg.offsetFromArgBase(), argDef); + curBlock_->add(mir); + return call->stackArgs_.append(mir); + } + + void propagateMaxStackArgBytes(uint32_t stackBytes) + { + if (callStack_.empty()) { + // Outermost call + maxStackArgBytes_ = Max(maxStackArgBytes_, stackBytes); + return; } - return true; + + // Non-outermost call + Call* outer = callStack_.back(); + outer->maxChildStackBytes_ = Max(outer->maxChildStackBytes_, stackBytes); + if (stackBytes && !outer->stackArgs_.empty()) + outer->childClobbers_ = true; } void finishCallArgs(Call* call) { - if (inDeadCode()) + MOZ_ALWAYS_TRUE(callStack_.popCopy() == call); + + if (inDeadCode()) { + propagateMaxStackArgBytes(call->maxChildStackBytes_); return; - uint32_t parentStackBytes = call->abi_.stackBytesConsumedSoFar(); - uint32_t newStackBytes; + } + + uint32_t stackBytes = call->abi_.stackBytesConsumedSoFar(); + if (call->childClobbers_) { call->spIncrement_ = AlignBytes(call->maxChildStackBytes_, AsmJSStackAlignment); - for (unsigned i = 0; i < call->stackArgs_.length(); i++) - call->stackArgs_[i]->incrementOffset(call->spIncrement_); - newStackBytes = Max(call->prevMaxStackBytes_, - call->spIncrement_ + parentStackBytes); + for (MAsmJSPassStackArg* stackArg : call->stackArgs_) + stackArg->incrementOffset(call->spIncrement_); + stackBytes += call->spIncrement_; } else { call->spIncrement_ = 0; - newStackBytes = Max(call->prevMaxStackBytes_, - Max(call->maxChildStackBytes_, parentStackBytes)); + stackBytes = Max(stackBytes, call->maxChildStackBytes_); } - mirGen_.setAsmJSMaxStackArgBytes(newStackBytes); + + propagateMaxStackArgBytes(stackBytes); } private: @@ -883,15 +914,14 @@ class FunctionCompiler curBlock_ = nullptr; } - bool unreachableTrap() + void unreachableTrap() { if (inDeadCode()) - return true; + return; auto* ins = MAsmThrowUnreachable::New(alloc()); curBlock_->end(ins); curBlock_ = nullptr; - return true; } bool branchAndStartThen(MDefinition* cond, MBasicBlock** thenBlock, MBasicBlock** elseBlock) @@ -928,46 +958,35 @@ class FunctionCompiler return numPushed; } - static void push(MBasicBlock* block, MDefinition* def) - { - MOZ_ASSERT(!hasPushed(block)); - block->push(def); - } - - static void popAll(BlockVector* blocks) - { - for (MBasicBlock* block : *blocks) - block->pop(); - } - public: - bool addJoinPredecessor(MDefinition* def, BlockVector* blocks) + void pushDef(MDefinition* def) { if (inDeadCode()) - return true; + return; + MOZ_ASSERT(!hasPushed(curBlock_)); + if (def && def->type() != MIRType_None) + curBlock_->push(def); + } - // Preserve the invariant that, for every MBasicBlock in 'blocks', + template + void ensurePushInvariants(const GetBlock& getBlock, size_t numBlocks) + { + // Preserve the invariant that, for every iterated MBasicBlock, // either: every MBasicBlock has a non-void pushed expression OR no // MBasicBlock has any pushed expression. This is required by // MBasicBlock::addPredecessor. - if (def) { - if (blocks->empty()) { - if (def->type() != MIRType_None) - push(curBlock_, def); - } else { - if (hasPushed((*blocks)[0])) { - if (def->type() == MIRType_None) - popAll(blocks); - else - push(curBlock_, def); - } - } - } else { - if (!blocks->empty() && hasPushed((*blocks)[0])) - popAll(blocks); - } + bool allPushed = true; - return blocks->append(curBlock_); + for (size_t i = 0; allPushed && i < numBlocks; i++) + allPushed = hasPushed(getBlock(i)); + + if (!allPushed) { + for (size_t i = 0; i < numBlocks; i++) { + MBasicBlock* block = getBlock(i); + if (hasPushed(block)) + block->pop(); + } + } } bool joinIf(MBasicBlock* joinBlock, BlockVector* blocks, MDefinition** def) @@ -985,11 +1004,22 @@ class FunctionCompiler mirGraph().moveBlockToEnd(curBlock_); } + bool addJoinPredecessor(MDefinition* def, BlockVector* blocks) + { + if (inDeadCode()) + return true; + pushDef(def); + return blocks->append(curBlock_); + } + bool joinIfElse(MDefinition* elseDef, BlockVector* blocks, MDefinition** def) { if (!addJoinPredecessor(elseDef, blocks)) return false; + auto getBlock = [&](size_t i) -> MBasicBlock* { return (*blocks)[i]; }; + ensurePushInvariants(getBlock, blocks->length()); + if (blocks->empty()) { *def = nullptr; return true; @@ -1034,16 +1064,16 @@ class FunctionCompiler bool startBlock() { - MOZ_ASSERT_IF(blockDepth_ < targets_.length(), targets_[blockDepth_].empty()); + MOZ_ASSERT_IF(blockDepth_ < blockPatches_.length(), blockPatches_[blockDepth_].empty()); blockDepth_++; return true; } - bool finishBlock() + bool finishBlock(MDefinition** def) { MOZ_ASSERT(blockDepth_); uint32_t topLabel = --blockDepth_; - return bindBranches(topLabel); + return bindBranches(topLabel, def); } bool startLoop(MBasicBlock** loopHeader) @@ -1084,8 +1114,7 @@ class FunctionCompiler } } - bool setLoopBackedge(MBasicBlock* loopEntry, MBasicBlock* loopBody, MBasicBlock* backedge, - MDefinition** loopResult) + bool setLoopBackedge(MBasicBlock* loopEntry, MBasicBlock* loopBody, MBasicBlock* backedge) { if (!loopEntry->setBackedgeAsmJS(backedge)) return false; @@ -1097,13 +1126,10 @@ class FunctionCompiler phi->setUnused(); } - // The loop result may also be referencing a recycled phi. - if (*loopResult && (*loopResult)->isUnused()) - *loopResult = (*loopResult)->toPhi()->getOperand(0); - // Fix up phis stored in the slots Vector of pending blocks. - for (BlockVector& vec : targets_) { - for (MBasicBlock* block : vec) { + for (ControlFlowPatchVector& patches : blockPatches_) { + for (ControlFlowPatch& p : patches) { + MBasicBlock* block = p.ins->block(); if (block->loopDepth() >= loopEntry->loopDepth()) fixupRedundantPhis(block); } @@ -1138,8 +1164,9 @@ class FunctionCompiler if (!loopHeader) { MOZ_ASSERT(inDeadCode()); - MOZ_ASSERT(afterLabel >= targets_.length() || targets_[afterLabel].empty()); - MOZ_ASSERT(headerLabel >= targets_.length() || targets_[headerLabel].empty()); + MOZ_ASSERT(afterLabel >= blockPatches_.length() || blockPatches_[afterLabel].empty()); + MOZ_ASSERT(headerLabel >= blockPatches_.length() || blockPatches_[headerLabel].empty()); + *loopResult = nullptr; blockDepth_ -= 2; loopDepth_--; return true; @@ -1153,23 +1180,27 @@ class FunctionCompiler // TODO (bug 1253544): blocks branching to the top join to a single // backedge block. Could they directly be set as backedges of the loop // instead? - if (!bindBranches(headerLabel)) + MDefinition* _; + if (!bindBranches(headerLabel, &_)) return false; MOZ_ASSERT(loopHeader->loopDepth() == loopDepth_); if (curBlock_) { // We're on the loop backedge block, created by bindBranches. + if (hasPushed(curBlock_)) + curBlock_->pop(); + MOZ_ASSERT(curBlock_->loopDepth() == loopDepth_); curBlock_->end(MGoto::New(alloc(), loopHeader)); - if (!setLoopBackedge(loopHeader, loopBody, curBlock_, loopResult)) + if (!setLoopBackedge(loopHeader, loopBody, curBlock_)) return false; } curBlock_ = loopBody; loopDepth_--; - if (!bindBranches(afterLabel)) + if (!bindBranches(afterLabel, loopResult)) return false; // If we have not created a new block in bindBranches, we're still on @@ -1185,106 +1216,102 @@ class FunctionCompiler return true; } - bool startSwitch(MDefinition* expr, uint32_t numCases, MBasicBlock** switchBlock) - { - if (inDeadCode()) { - *switchBlock = nullptr; - return true; - } - MOZ_ASSERT(numCases <= INT32_MAX); - MOZ_ASSERT(numCases); - curBlock_->end(MTableSwitch::New(alloc(), expr, 0, int32_t(numCases - 1))); - *switchBlock = curBlock_; - curBlock_ = nullptr; - return true; - } + bool addControlFlowPatch(MControlInstruction* ins, uint32_t relative, uint32_t index) { + MOZ_ASSERT(relative < blockDepth_); + uint32_t absolute = blockDepth_ - 1 - relative; - bool startSwitchCase(MBasicBlock* switchBlock, MBasicBlock** next) - { - MOZ_ASSERT(inDeadCode()); - if (!switchBlock) { - *next = nullptr; - return true; - } - if (!newBlock(switchBlock, next)) - return false; - curBlock_ = *next; - return true; - } - - bool joinSwitch(MBasicBlock* switchBlock, const BlockVector& cases, MBasicBlock* defaultBlock) - { - MOZ_ASSERT(inDeadCode()); - if (!switchBlock) - return true; - - MTableSwitch* mir = switchBlock->lastIns()->toTableSwitch(); - size_t defaultIndex; - if (!mir->addDefault(defaultBlock, &defaultIndex)) + if (absolute >= blockPatches_.length() && !blockPatches_.resize(absolute + 1)) return false; - for (MBasicBlock* caseBlock : cases) { - if (!caseBlock) { - if (!mir->addCase(defaultIndex)) - return false; - } else { - size_t caseIndex; - if (!mir->addSuccessor(caseBlock, &caseIndex)) - return false; - if (!mir->addCase(caseIndex)) - return false; - } - } - - return true; + return blockPatches_[absolute].append(ControlFlowPatch(ins, index)); } - bool br(uint32_t relativeDepth) + bool br(uint32_t relativeDepth, MDefinition* maybeValue) { if (inDeadCode()) return true; - MOZ_ASSERT(relativeDepth < blockDepth_); - uint32_t absolute = blockDepth_ - 1 - relativeDepth; - if (absolute >= targets_.length()) { - if (!targets_.resize(absolute + 1)) - return false; - } - - if (!targets_[absolute].append(curBlock_)) + MGoto* jump = MGoto::NewAsm(alloc()); + if (!addControlFlowPatch(jump, relativeDepth, MGoto::TargetIndex)) return false; + pushDef(maybeValue); + + curBlock_->end(jump); curBlock_ = nullptr; return true; } - bool brIf(uint32_t relativeDepth, MDefinition* condition) + bool brIf(uint32_t relativeDepth, MDefinition* maybeValue, MDefinition* condition) { if (inDeadCode()) return true; - // TODO (bug 1253334): we could use MTest with the right jump target, - // here. If it's backward, it's trivial; if it's forward, we need to - // memorize it, then fix it later when we actually encounter the target. - MBasicBlock* thenBlock = nullptr; MBasicBlock* joinBlock = nullptr; - if (!newBlock(curBlock_, &thenBlock)) - return false; if (!newBlock(curBlock_, &joinBlock)) return false; - curBlock_->end(MTest::New(alloc(), condition, thenBlock, joinBlock)); - curBlock_ = thenBlock; - mirGraph().moveBlockToEnd(curBlock_); - - if (!br(relativeDepth)) + MTest* test = MTest::NewAsm(alloc(), condition, joinBlock); + if (!addControlFlowPatch(test, relativeDepth, MTest::TrueBranchIndex)) return false; - MOZ_ASSERT(inDeadCode()); + pushDef(maybeValue); + + curBlock_->end(test); curBlock_ = joinBlock; return true; } + bool brTable(MDefinition* expr, uint32_t defaultDepth, const Uint32Vector& depths) + { + if (inDeadCode()) + return true; + + size_t numCases = depths.length(); + MOZ_ASSERT(numCases <= INT32_MAX); + MOZ_ASSERT(numCases); + + MTableSwitch* table = MTableSwitch::New(alloc(), expr, 0, int32_t(numCases - 1)); + + size_t defaultIndex; + if (!table->addDefault(nullptr, &defaultIndex)) + return false; + if (!addControlFlowPatch(table, defaultDepth, defaultIndex)) + return false; + + typedef HashMap, SystemAllocPolicy> + IndexToCaseMap; + + IndexToCaseMap indexToCase; + if (!indexToCase.init() || !indexToCase.put(defaultDepth, defaultIndex)) + return false; + + for (size_t i = 0; i < numCases; i++) { + uint32_t depth = depths[i]; + + size_t caseIndex; + IndexToCaseMap::AddPtr p = indexToCase.lookupForAdd(depth); + if (!p) { + if (!table->addSuccessor(nullptr, &caseIndex)) + return false; + if (!addControlFlowPatch(table, depth, caseIndex)) + return false; + if (!indexToCase.add(p, depth, caseIndex)) + return false; + } else { + caseIndex = p->value(); + } + + if (!table->addCase(caseIndex)) + return false; + } + + curBlock_->end(table); + curBlock_ = nullptr; + + return true; + } + /************************************************************ DECODING ***/ uint8_t readU8() { return decoder_.uncheckedReadFixedU8(); } @@ -1348,28 +1375,43 @@ class FunctionCompiler return next->addPredecessor(alloc(), prev); } - bool bindBranches(uint32_t absolute) + bool bindBranches(uint32_t absolute, MDefinition** def) { - if (absolute >= targets_.length() || targets_[absolute].empty()) + if (absolute >= blockPatches_.length() || blockPatches_[absolute].empty()) { + *def = !inDeadCode() && hasPushed(curBlock_) ? curBlock_->pop() : nullptr; return true; + } - BlockVector& preds = targets_[absolute]; + ControlFlowPatchVector& patches = blockPatches_[absolute]; - MBasicBlock* join; - if (!goToNewBlock(preds[0], &join)) + auto getBlock = [&](size_t i) -> MBasicBlock* { + if (i < patches.length()) + return patches[i].ins->block(); + return curBlock_; + }; + ensurePushInvariants(getBlock, patches.length() + !!curBlock_); + + MBasicBlock* join = nullptr; + MControlInstruction* ins = patches[0].ins; + if (!newBlock(ins->block(), &join)) return false; - for (size_t i = 1; i < preds.length(); i++) { - if (!mirGen_.ensureBallast()) - return false; - if (!goToExistingBlock(preds[i], join)) + + ins->replaceSuccessor(patches[0].index, join); + + for (size_t i = 1; i < patches.length(); i++) { + ins = patches[i].ins; + if (!join->addPredecessor(alloc(), ins->block())) return false; + ins->replaceSuccessor(patches[i].index, join); } if (curBlock_ && !goToExistingBlock(curBlock_, join)) return false; curBlock_ = join; - preds.clear(); + *def = hasPushed(curBlock_) ? curBlock_->pop() : nullptr; + + patches.clear(); return true; } }; @@ -1675,7 +1717,8 @@ EmitAtomicsExchange(FunctionCompiler& f, MDefinition** def) static bool EmitCallArgs(FunctionCompiler& f, const Sig& sig, FunctionCompiler::Call* call) { - f.startCallArgs(call); + if (!f.startCallArgs(call)) + return false; for (ValType argType : sig.args()) { MDefinition* arg; if (!EmitExpr(f, &arg)) @@ -1746,7 +1789,8 @@ EmitF32MathBuiltinCall(FunctionCompiler& f, uint32_t callOffset, Expr f32, MDefi uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(callOffset); FunctionCompiler::Call call(f, lineOrBytecode); - f.startCallArgs(&call); + if (!f.startCallArgs(&call)) + return false; MDefinition* firstArg; if (!EmitExpr(f, &firstArg) || !f.passArg(firstArg, ValType::F32, &call)) @@ -1764,7 +1808,8 @@ EmitF64MathBuiltinCall(FunctionCompiler& f, uint32_t callOffset, Expr f64, MDefi uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(callOffset); FunctionCompiler::Call call(f, lineOrBytecode); - f.startCallArgs(&call); + if (!f.startCallArgs(&call)) + return false; MDefinition* firstArg; if (!EmitExpr(f, &firstArg) || !f.passArg(firstArg, ValType::F64, &call)) @@ -2440,6 +2485,16 @@ EmitConvertI64ToFloatingPoint(FunctionCompiler& f, ValType type, bool isUnsigned return true; } +static bool +EmitReinterpret(FunctionCompiler& f, ValType to, MDefinition** def) +{ + MDefinition* in; + if (!EmitExpr(f, &in)) + return false; + *def = f.reinterpret(in, ToMIRType(to)); + return true; +} + static bool EmitSimdOp(FunctionCompiler& f, ValType type, SimdOperation op, SimdSign sign, MDefinition** def) { @@ -2544,10 +2599,10 @@ EmitLoop(FunctionCompiler& f, MDefinition** def) if (!EmitExpr(f, &_)) return false; } - if (!EmitExpr(f, def)) + MDefinition* last = nullptr; + if (!EmitExpr(f, &last)) return false; - } else { - *def = nullptr; + f.pushDef(last); } return f.closeLoop(loopHeader, def); @@ -2605,10 +2660,6 @@ EmitBrTable(FunctionCompiler& f, MDefinition** def) { uint32_t numCases = f.readVarU32(); - BlockVector cases; - if (!cases.resize(numCases)) - return false; - Uint32Vector depths; if (!depths.resize(numCases)) return false; @@ -2626,36 +2677,9 @@ EmitBrTable(FunctionCompiler& f, MDefinition** def) // Empty table if (!numCases) - return f.br(defaultDepth); + return f.br(defaultDepth, nullptr); - MBasicBlock* switchBlock; - if (!f.startSwitch(index, numCases, &switchBlock)) - return false; - - MBasicBlock* defaultBlock = nullptr; - if (!f.startSwitchCase(switchBlock, &defaultBlock)) - return false; - if (!f.br(defaultDepth)) - return false; - - // TODO (bug 1253334): we could avoid one indirection here, by - // jump-threading by hand the jump to the right enclosing block. - for (uint32_t i = 0; i < numCases; i++) { - uint32_t depth = depths[i]; - // Don't emit blocks for the default case, to reduce the number of - // MBasicBlocks created. - if (depth == defaultDepth) - continue; - if (!f.startSwitchCase(switchBlock, &cases[i])) - return false; - if (!f.br(depth)) - return false; - } - - if (!f.joinSwitch(switchBlock, cases, defaultBlock)) - return false; - - return true; + return f.brTable(index, defaultDepth, depths); } static bool @@ -2683,7 +2707,8 @@ static bool EmitUnreachable(FunctionCompiler& f, MDefinition** def) { *def = nullptr; - return f.unreachableTrap(); + f.unreachableTrap(); + return true; } static bool @@ -2693,16 +2718,16 @@ EmitBlock(FunctionCompiler& f, MDefinition** def) return false; if (uint32_t numStmts = f.readVarU32()) { for (uint32_t i = 0; i < numStmts - 1; i++) { - MDefinition* _; + MDefinition* _ = nullptr; if (!EmitExpr(f, &_)) return false; } - if (!EmitExpr(f, def)) + MDefinition* last = nullptr; + if (!EmitExpr(f, &last)) return false; - } else { - *def = nullptr; + f.pushDef(last); } - return f.finishBlock(); + return f.finishBlock(def); } static bool @@ -2712,17 +2737,19 @@ EmitBranch(FunctionCompiler& f, Expr op, MDefinition** def) uint32_t relativeDepth = f.readVarU32(); - MOZ_ALWAYS_TRUE(f.readExpr() == Expr::Nop); + MDefinition* maybeValue = nullptr; + if (!EmitExpr(f, &maybeValue)) + return false; if (op == Expr::Br) { - if (!f.br(relativeDepth)) + if (!f.br(relativeDepth, maybeValue)) return false; } else { MDefinition* condition; if (!EmitExpr(f, &condition)) return false; - if (!f.brIf(relativeDepth, condition)) + if (!f.brIf(relativeDepth, maybeValue, condition)) return false; } @@ -2886,6 +2913,8 @@ EmitExpr(FunctionCompiler& f, MDefinition** def) case Expr::F64Gt: case Expr::F64Ge: return EmitComparison(f, op, def); + case Expr::I32ReinterpretF32: + return EmitReinterpret(f, ValType::I32, def); // I64 case Expr::I64Const: @@ -2923,6 +2952,8 @@ EmitExpr(FunctionCompiler& f, MDefinition** def) case Expr::I64RemS: case Expr::I64RemU: return EmitDivOrMod(f, ValType::I64, IsDiv(false), IsUnsigned(op == Expr::I64RemU), def); + case Expr::I64ReinterpretF64: + return EmitReinterpret(f, ValType::I64, def); // F32 case Expr::F32Const: @@ -2958,12 +2989,15 @@ EmitExpr(FunctionCompiler& f, MDefinition** def) case Expr::F32ConvertUI64: return EmitConvertI64ToFloatingPoint(f, ValType::F32, IsUnsigned(op == Expr::F32ConvertUI64), def); + case Expr::F32Load: return EmitLoad(f, Scalar::Float32, def); case Expr::F32Store: return EmitStore(f, Scalar::Float32, def); case Expr::F32StoreF64: return EmitStoreWithCoercion(f, Scalar::Float32, Scalar::Float64, def); + case Expr::F32ReinterpretI32: + return EmitReinterpret(f, ValType::F32, def); // F64 case Expr::F64Const: @@ -3017,6 +3051,8 @@ EmitExpr(FunctionCompiler& f, MDefinition** def) return EmitStore(f, Scalar::Float64, def); case Expr::F64StoreF32: return EmitStoreWithCoercion(f, Scalar::Float64, Scalar::Float32, def); + case Expr::F64ReinterpretI64: + return EmitReinterpret(f, ValType::F64, def); // SIMD #define CASE(TYPE, OP, SIGN) \ @@ -3063,10 +3099,6 @@ EmitExpr(FunctionCompiler& f, MDefinition** def) SimdSign::Unsigned, def); // Atomics - case Expr::AtomicsFence: - *def = nullptr; - f.memoryBarrier(MembarFull); - return true; case Expr::I32AtomicsCompareExchange: return EmitAtomicsCompareExchange(f, def); case Expr::I32AtomicsExchange: @@ -3085,10 +3117,6 @@ EmitExpr(FunctionCompiler& f, MDefinition** def) case Expr::F64CopySign: case Expr::F64Nearest: case Expr::F64Trunc: - case Expr::I64ReinterpretF64: - case Expr::F64ReinterpretI64: - case Expr::I32ReinterpretF32: - case Expr::F32ReinterpretI32: case Expr::I64Load8S: case Expr::I64Load16S: case Expr::I64Load32S: @@ -3164,7 +3192,7 @@ wasm::IonCompileFunction(IonCompileTask* task) else f.returnExpr(last); - f.checkPostconditions(); + f.finish(); } // Compile MIR graph diff --git a/js/src/asmjs/WasmModule.cpp b/js/src/asmjs/WasmModule.cpp index 3e922e8dd4..e44dba3c1a 100644 --- a/js/src/asmjs/WasmModule.cpp +++ b/js/src/asmjs/WasmModule.cpp @@ -1274,6 +1274,24 @@ Module::deoptimizeImportExit(uint32_t importIndex) exit.baselineScript = nullptr; } +static JSObject* +CreateI64Object(JSContext* cx, int64_t i64) +{ + RootedObject result(cx, JS_NewPlainObject(cx)); + if (!result) + return nullptr; + + RootedValue val(cx, Int32Value(uint32_t(i64))); + if (!JS_DefineProperty(cx, result, "low", val, JSPROP_ENUMERATE)) + return nullptr; + + val = Int32Value(uint32_t(i64 >> 32)); + if (!JS_DefineProperty(cx, result, "high", val, JSPROP_ENUMERATE)) + return nullptr; + + return result; +} + bool Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args) { @@ -1311,7 +1329,10 @@ Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args) return false; break; case ValType::I64: - MOZ_CRASH("int64"); + MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in asm.js/wasm"); + if (!ReadI64Object(cx, v, (int64_t*)&coercedArgs[i])) + return false; + break; case ValType::F32: if (!RoundFloat32(cx, v, (float*)&coercedArgs[i])) return false; @@ -1374,57 +1395,104 @@ Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args) return true; } - JSObject* simdObj; + void* retAddr = &coercedArgs[0]; + JSObject* retObj = nullptr; switch (exp.sig().ret()) { case ExprType::Void: args.rval().set(UndefinedValue()); break; case ExprType::I32: - args.rval().set(Int32Value(*(int32_t*)&coercedArgs[0])); + args.rval().set(Int32Value(*(int32_t*)retAddr)); break; case ExprType::I64: - MOZ_CRASH("int64"); + MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in asm.js/wasm"); + retObj = CreateI64Object(cx, *(int64_t*)retAddr); + if (!retObj) + return false; + break; case ExprType::F32: + // The entry stub has converted the F32 into a double for us. case ExprType::F64: - args.rval().set(NumberValue(*(double*)&coercedArgs[0])); + args.rval().set(NumberValue(*(double*)retAddr)); break; case ExprType::I32x4: - simdObj = CreateSimd(cx, (int32_t*)&coercedArgs[0]); - if (!simdObj) + retObj = CreateSimd(cx, (int32_t*)retAddr); + if (!retObj) return false; - args.rval().set(ObjectValue(*simdObj)); break; case ExprType::F32x4: - simdObj = CreateSimd(cx, (float*)&coercedArgs[0]); - if (!simdObj) + retObj = CreateSimd(cx, (float*)retAddr); + if (!retObj) return false; - args.rval().set(ObjectValue(*simdObj)); break; case ExprType::B32x4: - simdObj = CreateSimd(cx, (int32_t*)&coercedArgs[0]); - if (!simdObj) + retObj = CreateSimd(cx, (int32_t*)retAddr); + if (!retObj) return false; - args.rval().set(ObjectValue(*simdObj)); break; case ExprType::Limit: MOZ_CRASH("Limit"); } + if (retObj) + args.rval().set(ObjectValue(*retObj)); + return true; } bool -Module::callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Value* argv, +Module::callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const uint64_t* argv, MutableHandleValue rval) { MOZ_ASSERT(dynamicallyLinked_); const Import& import = imports()[importIndex]; - RootedValue fval(cx, ObjectValue(*importToExit(import).fun)); - if (!Invoke(cx, UndefinedValue(), fval, argc, argv, rval)) + InvokeArgs args(cx); + if (!args.init(argc)) return false; + bool hasI64Arg = false; + MOZ_ASSERT(import.sig().args().length() == argc); + for (size_t i = 0; i < argc; i++) { + switch (import.sig().args()[i]) { + case ValType::I32: + args[i].set(Int32Value(*(int32_t*)&argv[i])); + break; + case ValType::F32: + args[i].set(JS::CanonicalizedDoubleValue(*(float*)&argv[i])); + break; + case ValType::F64: + args[i].set(JS::CanonicalizedDoubleValue(*(double*)&argv[i])); + break; + case ValType::I64: { + MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in asm.js/wasm"); + RootedObject obj(cx, CreateI64Object(cx, *(int64_t*)&argv[i])); + if (!obj) + return false; + args[i].set(ObjectValue(*obj)); + hasI64Arg = true; + break; + } + case ValType::I32x4: + case ValType::F32x4: + case ValType::B32x4: + case ValType::Limit: + MOZ_CRASH("unhandled type in callImport"); + } + } + + RootedValue fval(cx, ObjectValue(*importToExit(import).fun)); + RootedValue thisv(cx, UndefinedValue()); + if (!Call(cx, fval, thisv, args, rval)) + return false; + + // Don't try to optimize if the function has at least one i64 arg or if + // it returns an int64. GenerateJitExit relies on this, as does the + // type inference code below in this function. + if (hasI64Arg || import.sig().ret() == ExprType::I64) + return true; + ImportExit& exit = importToExit(import); // The exit may already have become optimized. @@ -1435,6 +1503,7 @@ Module::callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Val // Test if the function is JIT compiled. if (!exit.fun->hasScript()) return true; + JSScript* script = exit.fun->nonLazyScript(); if (!script->hasBaselineScript()) { MOZ_ASSERT(!script->hasIonScript()); @@ -1465,7 +1534,7 @@ Module::callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Val TypeSet::Type type = TypeSet::UnknownType(); switch (import.sig().args()[i]) { case ValType::I32: type = TypeSet::Int32Type(); break; - case ValType::I64: MOZ_CRASH("NYI"); + case ValType::I64: MOZ_CRASH("can't happen because of above guard"); case ValType::F32: type = TypeSet::DoubleType(); break; case ValType::F64: type = TypeSet::DoubleType(); break; case ValType::I32x4: MOZ_CRASH("NYI"); diff --git a/js/src/asmjs/WasmModule.h b/js/src/asmjs/WasmModule.h index 0ef20e849a..e42db6db60 100644 --- a/js/src/asmjs/WasmModule.h +++ b/js/src/asmjs/WasmModule.h @@ -600,7 +600,7 @@ class Module : public mozilla::LinkedListElement // directly into the JIT code. If the JIT code is released, the Module must // be notified so it can go back to the generic callImport. - bool callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Value* argv, + bool callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const uint64_t* argv, MutableHandleValue rval); void deoptimizeImportExit(uint32_t importIndex); diff --git a/js/src/asmjs/WasmStubs.cpp b/js/src/asmjs/WasmStubs.cpp index 18aec115ae..66f4701219 100644 --- a/js/src/asmjs/WasmStubs.cpp +++ b/js/src/asmjs/WasmStubs.cpp @@ -135,6 +135,8 @@ wasm::GenerateEntry(MacroAssembler& masm, unsigned target, const Sig& sig, bool // Use a second non-argument/return register as temporary scratch. Register argv = ABIArgGenerator::NonArgReturnReg0; Register scratch = ABIArgGenerator::NonArgReturnReg1; + Register64 scratch64(scratch); + #if defined(JS_CODEGEN_X86) masm.loadPtr(Address(masm.getStackPointer(), EntryFrameSize + masm.framePushed()), argv); #else @@ -164,9 +166,13 @@ wasm::GenerateEntry(MacroAssembler& masm, unsigned target, const Sig& sig, bool unsigned argOffset = iter.index() * Module::SizeOfEntryArg; Address src(argv, argOffset); MIRType type = iter.mirType(); + MOZ_ASSERT_IF(type == MIRType_Int64, JitOptions.wasmTestMode); switch (iter->kind()) { case ABIArg::GPR: - masm.load32(src, iter->gpr()); + if (type == MIRType_Int32) + masm.load32(src, iter->gpr()); + else if (type == MIRType_Int64) + masm.load64(src, iter->gpr64()); break; #ifdef JS_CODEGEN_REGISTER_PAIR case ABIArg::GPR_PAIR: @@ -202,6 +208,10 @@ wasm::GenerateEntry(MacroAssembler& masm, unsigned target, const Sig& sig, bool masm.load32(src, scratch); masm.storePtr(scratch, Address(masm.getStackPointer(), iter->offsetFromArgBase())); break; + case MIRType_Int64: + masm.load64(src, scratch64); + masm.store64(scratch64, Address(masm.getStackPointer(), iter->offsetFromArgBase())); + break; case MIRType_Double: masm.loadDouble(src, ScratchDoubleReg); masm.storeDouble(ScratchDoubleReg, Address(masm.getStackPointer(), iter->offsetFromArgBase())); @@ -245,10 +255,12 @@ wasm::GenerateEntry(MacroAssembler& masm, unsigned target, const Sig& sig, bool case ExprType::Void: break; case ExprType::I32: - masm.storeValue(JSVAL_TYPE_INT32, ReturnReg, Address(argv, 0)); + masm.store32(ReturnReg, Address(argv, 0)); break; case ExprType::I64: - MOZ_CRASH("no int64 in asm.js"); + MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in asm.js/wasm"); + masm.store64(ReturnReg64, Address(argv, 0)); + break; case ExprType::F32: masm.convertFloat32ToDouble(ReturnFloat32Reg, ReturnDoubleReg); MOZ_FALLTHROUGH; // as ReturnDoubleReg now contains a Double @@ -280,15 +292,35 @@ wasm::GenerateEntry(MacroAssembler& masm, unsigned target, const Sig& sig, bool return offsets; } +typedef bool ToValue; + static void FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args, unsigned argOffset, - unsigned offsetToCallerStackArgs, Register scratch) + unsigned offsetToCallerStackArgs, Register scratch, ToValue toValue) { + Register64 scratch64(scratch); for (ABIArgValTypeIter i(args); !i.done(); i++) { Address dstAddr(masm.getStackPointer(), argOffset + i.index() * sizeof(Value)); + + MIRType type = i.mirType(); + MOZ_ASSERT_IF(type == MIRType_Int64, JitOptions.wasmTestMode); + switch (i->kind()) { case ABIArg::GPR: - masm.storeValue(JSVAL_TYPE_INT32, i->gpr(), dstAddr); + if (type == MIRType_Int32) { + if (toValue) + masm.storeValue(JSVAL_TYPE_INT32, i->gpr(), dstAddr); + else + masm.store32(i->gpr(), dstAddr); + } else if (type == MIRType_Int64) { + // We can't box int64 into Values (yet). + if (toValue) + masm.breakpoint(); + else + masm.store64(i->gpr64(), dstAddr); + } else { + MOZ_CRASH("unexpected input type?"); + } break; #ifdef JS_CODEGEN_REGISTER_PAIR case ABIArg::GPR_PAIR: @@ -296,35 +328,49 @@ FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args, unsigned argO break; #endif case ABIArg::FPU: { - MOZ_ASSERT(IsFloatingPointType(i.mirType())); + MOZ_ASSERT(IsFloatingPointType(type)); FloatRegister srcReg = i->fpu(); - if (i.mirType() == MIRType_Float32) { - masm.convertFloat32ToDouble(i->fpu(), ScratchDoubleReg); - srcReg = ScratchDoubleReg; + if (toValue) { + if (type == MIRType_Float32) { + masm.convertFloat32ToDouble(i->fpu(), ScratchDoubleReg); + srcReg = ScratchDoubleReg; + } + masm.canonicalizeDouble(srcReg); } - masm.canonicalizeDouble(srcReg); masm.storeDouble(srcReg, dstAddr); break; } case ABIArg::Stack: - if (i.mirType() == MIRType_Int32) { + if (type == MIRType_Int32) { Address src(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase()); -#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) masm.load32(src, scratch); - masm.storeValue(JSVAL_TYPE_INT32, scratch, dstAddr); -#else - masm.memIntToValue(src, dstAddr); -#endif + if (toValue) + masm.storeValue(JSVAL_TYPE_INT32, scratch, dstAddr); + else + masm.store32(scratch, dstAddr); + } else if (type == MIRType_Int64) { + // We can't box int64 into Values (yet). + if (toValue) { + masm.breakpoint(); + } else { + Address src(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase()); + masm.load64(src, scratch64); + masm.store64(scratch64, dstAddr); + } } else { - MOZ_ASSERT(IsFloatingPointType(i.mirType())); + MOZ_ASSERT(IsFloatingPointType(type)); Address src(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase()); - if (i.mirType() == MIRType_Float32) { - masm.loadFloat32(src, ScratchFloat32Reg); - masm.convertFloat32ToDouble(ScratchFloat32Reg, ScratchDoubleReg); + if (toValue) { + if (type == MIRType_Float32) { + masm.loadFloat32(src, ScratchFloat32Reg); + masm.convertFloat32ToDouble(ScratchFloat32Reg, ScratchDoubleReg); + } else { + masm.loadDouble(src, ScratchDoubleReg); + } + masm.canonicalizeDouble(ScratchDoubleReg); } else { masm.loadDouble(src, ScratchDoubleReg); } - masm.canonicalizeDouble(ScratchDoubleReg); masm.storeDouble(ScratchDoubleReg, dstAddr); } break; @@ -363,7 +409,7 @@ wasm::GenerateInterpExit(MacroAssembler& masm, const Import& import, uint32_t im // Fill the argument array. unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed(); Register scratch = ABIArgGenerator::NonArgReturnReg0; - FillArgumentArray(masm, sig.args(), argOffset, offsetToCallerStackArgs, scratch); + FillArgumentArray(masm, sig.args(), argOffset, offsetToCallerStackArgs, scratch, ToValue(false)); // Prepare the arguments for the call to InvokeImport_*. ABIArgMIRTypeIter i(invokeArgTypes); @@ -404,10 +450,14 @@ wasm::GenerateInterpExit(MacroAssembler& masm, const Import& import, uint32_t im case ExprType::I32: masm.call(SymbolicAddress::InvokeImport_I32); masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw); - masm.unboxInt32(argv, ReturnReg); + masm.load32(argv, ReturnReg); break; case ExprType::I64: - MOZ_CRASH("no int64 in asm.js"); + MOZ_ASSERT(JitOptions.wasmTestMode); + masm.call(SymbolicAddress::InvokeImport_I64); + masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw); + masm.load64(argv, ReturnReg64); + break; case ExprType::F32: masm.call(SymbolicAddress::InvokeImport_F64); masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw); @@ -509,7 +559,7 @@ wasm::GenerateJitExit(MacroAssembler& masm, const Import& import, bool usesHeap) // 5. Fill the arguments unsigned offsetToCallerStackArgs = jitFramePushed + sizeof(AsmJSFrame); - FillArgumentArray(masm, sig.args(), argOffset, offsetToCallerStackArgs, scratch); + FillArgumentArray(masm, sig.args(), argOffset, offsetToCallerStackArgs, scratch, ToValue(true)); argOffset += sig.args().length() * sizeof(Value); MOZ_ASSERT(argOffset == jitFrameBytes); @@ -659,7 +709,11 @@ wasm::GenerateJitExit(MacroAssembler& masm, const Import& import, bool usesHeap) /* -0 check */ false); break; case ExprType::I64: - MOZ_CRASH("no int64 in asm.js"); + MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in asm.js/wasm"); + // We don't expect int64 to be returned from Ion yet, because of a + // guard in callImport. + masm.breakpoint(); + break; case ExprType::F32: masm.convertValueToFloat(JSReturnOperand, ReturnFloat32Reg, &oolConvert); break; diff --git a/js/src/asmjs/WasmTextToBinary.cpp b/js/src/asmjs/WasmTextToBinary.cpp index aea27cd87a..0fc3a50931 100644 --- a/js/src/asmjs/WasmTextToBinary.cpp +++ b/js/src/asmjs/WasmTextToBinary.cpp @@ -211,8 +211,8 @@ enum class WasmAstExprKind SetLocal, Store, TernaryOperator, - Trap, UnaryOperator, + Unreachable }; class WasmAstExpr : public WasmAstNode @@ -241,10 +241,10 @@ struct WasmAstNop : WasmAstExpr {} }; -struct WasmAstTrap : WasmAstExpr +struct WasmAstUnreachable : WasmAstExpr { - WasmAstTrap() - : WasmAstExpr(WasmAstExprKind::Trap) + WasmAstUnreachable() + : WasmAstExpr(WasmAstExprKind::Unreachable) {} }; @@ -325,18 +325,22 @@ class WasmAstBranch : public WasmAstExpr Expr expr_; WasmAstExpr* cond_; WasmRef target_; + WasmAstExpr* value_; public: static const WasmAstExprKind Kind = WasmAstExprKind::Branch; - explicit WasmAstBranch(Expr expr, WasmAstExpr* cond, WasmRef target) + explicit WasmAstBranch(Expr expr, WasmAstExpr* cond, WasmRef target, WasmAstExpr* value) : WasmAstExpr(Kind), expr_(expr), cond_(cond), - target_(target) + target_(target), + value_(value) {} + Expr expr() const { return expr_; } WasmRef& target() { return target_; } WasmAstExpr& cond() const { MOZ_ASSERT(cond_); return *cond_; } + WasmAstExpr* maybeValue() const { return value_; } }; class WasmAstCall : public WasmAstExpr @@ -633,7 +637,9 @@ class WasmAstModule : public WasmAstNode return true; } *sigIndex = sigs_.length(); - return sigs_.append(new (lifo_) WasmAstSig(WasmName(), Move(sig))) && + auto* lifoSig = new (lifo_) WasmAstSig(WasmName(), Move(sig)); + return lifoSig && + sigs_.append(lifoSig) && sigMap_.add(p, sigs_.back(), *sigIndex); } bool append(WasmAstSig* sig) { @@ -827,9 +833,9 @@ class WasmToken Table, TernaryOpcode, Text, - Trap, Type, UnaryOpcode, + Unreachable, ValueType }; private: @@ -1621,6 +1627,11 @@ WasmTokenStream::next() return WasmToken(WasmToken::ConversionOpcode, Expr::F64PromoteF32, begin, cur_); break; + case 'r': + if (consume(MOZ_UTF16("reinterpret/i64"))) + return WasmToken(WasmToken::UnaryOpcode, Expr::F64ReinterpretI64, + begin, cur_); + break; case 's': if (consume(MOZ_UTF16("sqrt"))) return WasmToken(WasmToken::UnaryOpcode, Expr::F64Sqrt, begin, cur_); @@ -1987,8 +1998,11 @@ WasmTokenStream::next() return WasmToken(WasmToken::Table, begin, cur_); if (consume(MOZ_UTF16("type"))) return WasmToken(WasmToken::Type, begin, cur_); - if (consume(MOZ_UTF16("trap"))) - return WasmToken(WasmToken::Trap, begin, cur_); + break; + + case 'u': + if (consume(MOZ_UTF16("unreachable"))) + return WasmToken(WasmToken::Unreachable, begin, cur_); break; default: @@ -2078,14 +2092,30 @@ ParseBranch(WasmParseContext& c, Expr expr) if (!c.ts.matchRef(&target, c.error)) return nullptr; - WasmAstExpr* cond = nullptr; - if (expr == Expr::BrIf) { - cond = ParseExpr(c); - if (!cond) + WasmAstExpr* value = nullptr; + if (c.ts.getIf(WasmToken::OpenParen)) { + value = ParseExprInsideParens(c); + if (!value) + return nullptr; + if (!c.ts.match(WasmToken::CloseParen, c.error)) return nullptr; } - return new(c.lifo) WasmAstBranch(expr, cond, target); + WasmAstExpr* cond = nullptr; + if (expr == Expr::BrIf) { + if (c.ts.getIf(WasmToken::OpenParen)) { + cond = ParseExprInsideParens(c); + if (!cond) + return nullptr; + if (!c.ts.match(WasmToken::CloseParen, c.error)) + return nullptr; + } else { + cond = value; + value = nullptr; + } + } + + return new(c.lifo) WasmAstBranch(expr, cond, target, value); } static bool @@ -2433,7 +2463,7 @@ ParseConst(WasmParseContext& c, WasmToken constToken) case WasmToken::SignedInteger: return new(c.lifo) WasmAstConst(Val(uint64_t(val.sint()))); case WasmToken::NegativeZero: - return new(c.lifo) WasmAstConst(Val(uint32_t(0))); + return new(c.lifo) WasmAstConst(Val(uint64_t(0))); default: break; } @@ -2747,8 +2777,8 @@ ParseExprInsideParens(WasmParseContext& c) switch (token.kind()) { case WasmToken::Nop: return new(c.lifo) WasmAstNop; - case WasmToken::Trap: - return new(c.lifo) WasmAstTrap; + case WasmToken::Unreachable: + return new(c.lifo) WasmAstUnreachable; case WasmToken::BinaryOpcode: return ParseBinaryOperator(c, token.expr()); case WasmToken::Block: @@ -3307,6 +3337,9 @@ ResolveBranch(Resolver& r, WasmAstBranch& br) if (!r.resolveBranchTarget(br.target())) return false; + if (br.maybeValue() && !ResolveExpr(r, *br.maybeValue())) + return false; + if (br.expr() == Expr::BrIf) { if (!ResolveExpr(r, br.cond())) return false; @@ -3463,7 +3496,7 @@ ResolveExpr(Resolver& r, WasmAstExpr& expr) { switch (expr.kind()) { case WasmAstExprKind::Nop: - case WasmAstExprKind::Trap: + case WasmAstExprKind::Unreachable: return true; case WasmAstExprKind::BinaryOperator: return ResolveBinaryOperator(r, expr.as()); @@ -3609,7 +3642,7 @@ EncodeBranch(Encoder& e, WasmAstBranch& br) if (!e.writeVarU32(br.target().index())) return false; - if (!e.writeExpr(Expr::Nop)) + if (br.maybeValue() ? !EncodeExpr(e, *br.maybeValue()) : !e.writeExpr(Expr::Nop)) return false; if (br.expr() == Expr::BrIf) { @@ -3805,7 +3838,7 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr) switch (expr.kind()) { case WasmAstExprKind::Nop: return e.writeExpr(Expr::Nop); - case WasmAstExprKind::Trap: + case WasmAstExprKind::Unreachable: return e.writeExpr(Expr::Unreachable); case WasmAstExprKind::BinaryOperator: return EncodeBinaryOperator(e, expr.as()); diff --git a/js/src/asmjs/WasmTypes.cpp b/js/src/asmjs/WasmTypes.cpp index 19117821f3..c5f068a0be 100644 --- a/js/src/asmjs/WasmTypes.cpp +++ b/js/src/asmjs/WasmTypes.cpp @@ -113,7 +113,7 @@ CoerceInPlace_ToNumber(MutableHandleValue val) // Use an int32_t return type instead of bool since bool does not have a // specified width and the caller is assuming a word-sized return. static int32_t -InvokeImport_Void(int32_t importIndex, int32_t argc, Value* argv) +InvokeImport_Void(int32_t importIndex, int32_t argc, uint64_t* argv) { WasmActivation* activation = JSRuntime::innermostWasmActivation(); JSContext* cx = activation->cx(); @@ -125,7 +125,7 @@ InvokeImport_Void(int32_t importIndex, int32_t argc, Value* argv) // Use an int32_t return type instead of bool since bool does not have a // specified width and the caller is assuming a word-sized return. static int32_t -InvokeImport_I32(int32_t importIndex, int32_t argc, Value* argv) +InvokeImport_I32(int32_t importIndex, int32_t argc, uint64_t* argv) { WasmActivation* activation = JSRuntime::innermostWasmActivation(); JSContext* cx = activation->cx(); @@ -138,14 +138,57 @@ InvokeImport_I32(int32_t importIndex, int32_t argc, Value* argv) if (!ToInt32(cx, rval, &i32)) return false; - argv[0] = Int32Value(i32); + argv[0] = i32; + return true; +} + +bool +js::wasm::ReadI64Object(JSContext* cx, HandleValue v, int64_t* i64) +{ + if (!v.isObject()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL, + "i64 JS value must be an object"); + return false; + } + + RootedObject obj(cx, &v.toObject()); + + int32_t* i32 = (int32_t*)i64; + + RootedValue val(cx); + if (!JS_GetProperty(cx, obj, "low", &val)) + return false; + if (!ToInt32(cx, val, &i32[0])) + return false; + + if (!JS_GetProperty(cx, obj, "high", &val)) + return false; + if (!ToInt32(cx, val, &i32[1])) + return false; + + return true; +} + +static int32_t +InvokeImport_I64(int32_t importIndex, int32_t argc, uint64_t* argv) +{ + WasmActivation* activation = JSRuntime::innermostWasmActivation(); + JSContext* cx = activation->cx(); + + RootedValue rval(cx); + if (!activation->module().callImport(cx, importIndex, argc, argv, &rval)) + return false; + + if (!ReadI64Object(cx, rval, (int64_t*)argv)) + return false; + return true; } // Use an int32_t return type instead of bool since bool does not have a // specified width and the caller is assuming a word-sized return. static int32_t -InvokeImport_F64(int32_t importIndex, int32_t argc, Value* argv) +InvokeImport_F64(int32_t importIndex, int32_t argc, uint64_t* argv) { WasmActivation* activation = JSRuntime::innermostWasmActivation(); JSContext* cx = activation->cx(); @@ -158,7 +201,7 @@ InvokeImport_F64(int32_t importIndex, int32_t argc, Value* argv) if (!ToNumber(cx, rval, &dbl)) return false; - argv[0] = DoubleValue(dbl); + ((double*)argv)[0] = dbl; return true; } @@ -199,6 +242,8 @@ wasm::AddressOf(SymbolicAddress imm, ExclusiveContext* cx) return FuncCast(InvokeImport_Void, Args_General3); case SymbolicAddress::InvokeImport_I32: return FuncCast(InvokeImport_I32, Args_General3); + case SymbolicAddress::InvokeImport_I64: + return FuncCast(InvokeImport_I64, Args_General3); case SymbolicAddress::InvokeImport_F64: return FuncCast(InvokeImport_F64, Args_General3); case SymbolicAddress::CoerceInPlace_ToInt32: diff --git a/js/src/asmjs/WasmTypes.h b/js/src/asmjs/WasmTypes.h index 54408fab83..4b2546ac4e 100644 --- a/js/src/asmjs/WasmTypes.h +++ b/js/src/asmjs/WasmTypes.h @@ -539,6 +539,7 @@ enum class SymbolicAddress HandleExecutionInterrupt, InvokeImport_Void, InvokeImport_I32, + InvokeImport_I64, InvokeImport_F64, CoerceInPlace_ToInt32, CoerceInPlace_ToNumber, @@ -548,6 +549,10 @@ enum class SymbolicAddress void* AddressOf(SymbolicAddress imm, ExclusiveContext* cx); +// Extracts low and high from an int64 object {low: int32, high: int32}, for +// testing purposes mainly. +bool ReadI64Object(JSContext* cx, HandleValue v, int64_t* val); + // A wasm::JumpTarget represents one of a special set of stubs that can be // jumped to from any function. Because wasm modules can be larger than the // range of a plain jump, these potentially out-of-range jumps must be recorded diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index fbd8bb422a..d2ea92df5d 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -715,43 +715,61 @@ function CreateArrayIterator(obj, kind) { return CreateArrayIteratorAt(obj, kind, 0); } - +// ES6, 22.1.5.2.1 +// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%.next function ArrayIteratorNext() { + // Step 1-3. if (!IsObject(this) || !IsArrayIterator(this)) { return callFunction(CallArrayIteratorMethodIfWrapped, this, "ArrayIteratorNext"); } - var a = UnsafeGetObjectFromReservedSlot(this, ITERATOR_SLOT_TARGET); + + // Step 4. + var a = UnsafeGetReservedSlot(this, ITERATOR_SLOT_TARGET); + var result = { value: undefined, done: false }; + + // Step 5. + if (a === null) { + result.done = true; + return result; + } + + // Step 6. // The index might not be an integer, so we have to do a generic get here. var index = UnsafeGetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX); + + // Step 7. var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND); - var result = { value: undefined, done: false }; + + // Step 8-9. var len = IsPossiblyWrappedTypedArray(a) ? PossiblyWrappedTypedArrayLength(a) : TO_UINT32(a.length); - // FIXME: This should be ToLength, which clamps at 2**53. Bug 924058. + // Step 10. if (index >= len) { - // When the above is changed to ToLength, use +1/0 here instead - // of MAX_UINT32. - UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, 0xffffffff); + UnsafeSetReservedSlot(this, ITERATOR_SLOT_TARGET, null); result.done = true; return result; } + // Step 11. UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + 1); + // Step 16. if (itemKind === ITEM_KIND_VALUE) { result.value = a[index]; return result; } + // Step 13. if (itemKind === ITEM_KIND_KEY_AND_VALUE) { var pair = [index, a[index]]; result.value = pair; return result; } + // Step 12. assert(itemKind === ITEM_KIND_KEY, itemKind); result.value = index; return result; diff --git a/js/src/builtin/AtomicsObject.cpp b/js/src/builtin/AtomicsObject.cpp index 038a886ec0..739bef6720 100644 --- a/js/src/builtin/AtomicsObject.cpp +++ b/js/src/builtin/AtomicsObject.cpp @@ -52,6 +52,7 @@ #include "jsapi.h" #include "jsfriendapi.h" +#include "jsnum.h" #include "asmjs/WasmModule.h" #include "jit/AtomicOperations.h" @@ -80,7 +81,10 @@ ReportBadArrayType(JSContext* cx) static bool ReportOutOfRange(JSContext* cx) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_BAD_INDEX); + // Use JSMSG_BAD_INDEX here even if it is generic, since that is + // the message used by ToIntegerIndex for its initial range + // checking. + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX); return false; } @@ -108,22 +112,12 @@ GetSharedTypedArray(JSContext* cx, HandleValue v, static bool GetTypedArrayIndex(JSContext* cx, HandleValue v, Handle view, uint32_t* offset) { - RootedId id(cx); - if (!ValueToId(cx, v, &id)) - return false; uint64_t index; - if (!IsTypedArrayIndex(id, &index) || index >= view->length()) + if (!js::ToIntegerIndex(cx, v, &index)) + return false; + if (index >= view->length()) return ReportOutOfRange(cx); - *offset = (uint32_t)index; - return true; -} - -bool -js::atomics_fence(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - jit::AtomicOperations::fenceSeqCst(); - args.rval().setUndefined(); + *offset = uint32_t(index); return true; } @@ -754,7 +748,7 @@ class AutoUnlockFutexAPI } // namespace js bool -js::atomics_futexWait(JSContext* cx, unsigned argc, Value* vp) +js::atomics_wait(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); HandleValue objv = args.get(0); @@ -797,7 +791,7 @@ js::atomics_futexWait(JSContext* cx, unsigned argc, Value* vp) SharedMem(addr) = view->viewDataShared().cast() + offset; if (jit::AtomicOperations::loadSafeWhenRacy(addr) != value) { - r.setInt32(AtomicsObject::FutexNotequal); + r.setString(cx->names().futexNotEqual); return true; } @@ -815,10 +809,18 @@ js::atomics_futexWait(JSContext* cx, unsigned argc, Value* vp) sarb->setWaiters(&w); } - AtomicsObject::FutexWaitResult result = AtomicsObject::FutexOK; + FutexRuntime::WaitResult result = FutexRuntime::FutexOK; bool retval = rt->fx.wait(cx, timeout_ms, &result); - if (retval) - r.setInt32(result); + if (retval) { + switch (result) { + case FutexRuntime::FutexOK: + r.setString(cx->names().futexOK); + break; + case FutexRuntime::FutexTimedOut: + r.setString(cx->names().futexTimedOut); + break; + } + } if (w.lower_pri == &w) { sarb->setWaiters(nullptr); @@ -832,7 +834,7 @@ js::atomics_futexWait(JSContext* cx, unsigned argc, Value* vp) } bool -js::atomics_futexWake(JSContext* cx, unsigned argc, Value* vp) +js::atomics_wake(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); HandleValue objv = args.get(0); @@ -849,10 +851,14 @@ js::atomics_futexWake(JSContext* cx, unsigned argc, Value* vp) if (!GetTypedArrayIndex(cx, idxv, view, &offset)) return false; double count; - if (!ToInteger(cx, countv, &count)) - return false; - if (count < 0) - count = 0; + if (countv.isUndefined()) { + count = mozilla::PositiveInfinity(); + } else { + if (!ToInteger(cx, countv, &count)) + return false; + if (count < 0.0) + count = 0.0; + } AutoLockFutexAPI lock; @@ -878,120 +884,6 @@ js::atomics_futexWake(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::atomics_futexWakeOrRequeue(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - HandleValue objv = args.get(0); - HandleValue idx1v = args.get(1); - HandleValue countv = args.get(2); - HandleValue idx2v = args.get(3); - HandleValue valv = args.get(4); - MutableHandleValue r = args.rval(); - - Rooted view(cx, nullptr); - if (!GetSharedTypedArray(cx, objv, &view)) - return false; - if (view->type() != Scalar::Int32) - return ReportBadArrayType(cx); - uint32_t offset1; - if (!GetTypedArrayIndex(cx, idx1v, view, &offset1)) - return false; - double count; - if (!ToInteger(cx, countv, &count)) - return false; - if (count < 0) - count = 0; - int32_t value; - if (!ToInt32(cx, valv, &value)) - return false; - uint32_t offset2; - if (!GetTypedArrayIndex(cx, idx2v, view, &offset2)) - return false; - - AutoLockFutexAPI lock; - - SharedMem addr = view->viewDataShared().cast() + offset1; - if (jit::AtomicOperations::loadSafeWhenRacy(addr) != value) { - r.setInt32(AtomicsObject::FutexNotequal); - return true; - } - - Rooted sab(cx, view->bufferShared()); - SharedArrayRawBuffer* sarb = sab->rawBufferObject(); - - // Walk the list of waiters looking for those waiting on offset1. - // Wake some and requeue the others. There may already be other - // waiters on offset2, so those that are requeued must be moved to - // the back of the list. Offset1 may equal offset2. The list's - // first node may change, and the list may be emptied out by the - // operation. - - FutexWaiter* waiters = sarb->waiters(); - if (!waiters) { - r.setInt32(0); - return true; - } - - int32_t woken = 0; - FutexWaiter whead((uint32_t)-1, nullptr); // Header node for waiters - FutexWaiter* first = waiters; - FutexWaiter* last = waiters->back; - whead.lower_pri = first; - whead.back = last; - first->back = &whead; - last->lower_pri = &whead; - - FutexWaiter rhead((uint32_t)-1, nullptr); // Header node for requeued - rhead.lower_pri = rhead.back = &rhead; - - FutexWaiter* iter = whead.lower_pri; - while (iter != &whead) { - FutexWaiter* c = iter; - iter = iter->lower_pri; - if (c->offset != offset1 || !c->rt->fx.isWaiting()) - continue; - if (count > 0) { - c->rt->fx.wake(FutexRuntime::WakeExplicit); - ++woken; - --count; - } else { - c->offset = offset2; - - // Remove the node from the waiters list. - c->back->lower_pri = c->lower_pri; - c->lower_pri->back = c->back; - - // Insert the node at the back of the requeuers list. - c->lower_pri = &rhead; - c->back = rhead.back; - rhead.back->lower_pri = c; - rhead.back = c; - } - } - - // If there are any requeuers, append them to the waiters. - if (rhead.lower_pri != &rhead) { - whead.back->lower_pri = rhead.lower_pri; - rhead.lower_pri->back = whead.back; - - whead.back = rhead.back; - rhead.back->lower_pri = &whead; - } - - // Make the final list and install it. - waiters = nullptr; - if (whead.lower_pri != &whead) { - whead.back->lower_pri = whead.lower_pri; - whead.lower_pri->back = whead.back; - waiters = whead.lower_pri; - } - sarb->setWaiters(waiters); - - r.setInt32(woken); - return true; -} - /* static */ bool js::FutexRuntime::initialize() { @@ -1070,7 +962,7 @@ js::FutexRuntime::isWaiting() } bool -js::FutexRuntime::wait(JSContext* cx, double timeout_ms, AtomicsObject::FutexWaitResult* result) +js::FutexRuntime::wait(JSContext* cx, double timeout_ms, WaitResult* result) { MOZ_ASSERT(&cx->runtime()->fx == this); MOZ_ASSERT(cx->runtime()->fx.canWait()); @@ -1127,14 +1019,14 @@ js::FutexRuntime::wait(JSContext* cx, double timeout_ms, AtomicsObject::FutexWai if (timed) { uint64_t now = PRMJ_Now(); if (now >= finalEnd) { - *result = AtomicsObject::FutexTimedout; + *result = FutexTimedOut; goto finished; } } break; case FutexRuntime::Woken: - *result = AtomicsObject::FutexOK; + *result = FutexOK; goto finished; case FutexRuntime::WaitingNotifiedForInterrupt: @@ -1146,10 +1038,10 @@ js::FutexRuntime::wait(JSContext* cx, double timeout_ms, AtomicsObject::FutexWai // should be woken when the interrupt handler returns. // To that end, we flag the thread as interrupted around // the interrupt and check state_ when the interrupt - // handler returns. A futexWake() call that reaches the + // handler returns. A wake() call that reaches the // runtime during the interrupt sets state_ to Woken. // - // - It is in principle possible for futexWait() to be + // - It is in principle possible for wait() to be // reentered on the same thread/runtime and waiting on the // same location and to yet again be interrupted and enter // the interrupt handler. In this case, it is important @@ -1175,7 +1067,7 @@ js::FutexRuntime::wait(JSContext* cx, double timeout_ms, AtomicsObject::FutexWai if (!retval) goto finished; if (state_ == Woken) { - *result = AtomicsObject::FutexOK; + *result = FutexOK; goto finished; } break; @@ -1219,26 +1111,19 @@ const JSFunctionSpec AtomicsMethods[] = { JS_INLINABLE_FN("load", atomics_load, 2,0, AtomicsLoad), JS_INLINABLE_FN("store", atomics_store, 3,0, AtomicsStore), JS_INLINABLE_FN("exchange", atomics_exchange, 3,0, AtomicsExchange), - JS_INLINABLE_FN("fence", atomics_fence, 0,0, AtomicsFence), JS_INLINABLE_FN("add", atomics_add, 3,0, AtomicsAdd), JS_INLINABLE_FN("sub", atomics_sub, 3,0, AtomicsSub), JS_INLINABLE_FN("and", atomics_and, 3,0, AtomicsAnd), JS_INLINABLE_FN("or", atomics_or, 3,0, AtomicsOr), JS_INLINABLE_FN("xor", atomics_xor, 3,0, AtomicsXor), JS_INLINABLE_FN("isLockFree", atomics_isLockFree, 1,0, AtomicsIsLockFree), - JS_FN("futexWait", atomics_futexWait, 4,0), - JS_FN("futexWake", atomics_futexWake, 3,0), - JS_FN("futexWakeOrRequeue", atomics_futexWakeOrRequeue, 5,0), + JS_FN("wait", atomics_wait, 4,0), + JS_FN("futexWait", atomics_wait, 4,0), + JS_FN("wake", atomics_wake, 3,0), + JS_FN("futexWake", atomics_wake, 3,0), JS_FS_END }; -static const JSConstDoubleSpec AtomicsConstants[] = { - {"OK", AtomicsObject::FutexOK}, - {"TIMEDOUT", AtomicsObject::FutexTimedout}, - {"NOTEQUAL", AtomicsObject::FutexNotequal}, - {0, 0} -}; - JSObject* AtomicsObject::initClass(JSContext* cx, Handle global) { @@ -1253,8 +1138,6 @@ AtomicsObject::initClass(JSContext* cx, Handle global) if (!JS_DefineFunctions(cx, Atomics, AtomicsMethods)) return nullptr; - if (!JS_DefineConstDoubles(cx, Atomics, AtomicsConstants)) - return nullptr; RootedValue AtomicsValue(cx, ObjectValue(*Atomics)); diff --git a/js/src/builtin/AtomicsObject.h b/js/src/builtin/AtomicsObject.h index 64745a379c..82e5272bfc 100644 --- a/js/src/builtin/AtomicsObject.h +++ b/js/src/builtin/AtomicsObject.h @@ -18,31 +18,20 @@ class AtomicsObject : public JSObject static const Class class_; static JSObject* initClass(JSContext* cx, Handle global); static bool toString(JSContext* cx, unsigned int argc, Value* vp); - - // Defined return values for futexWait. - // The error values must be negative because APIs such as futexWaitOrRequeue - // return a value that is either the number of tasks woken or an error code. - enum FutexWaitResult : int32_t { - FutexOK = 0, - FutexNotequal = -1, - FutexTimedout = -2 - }; }; bool atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp); bool atomics_exchange(JSContext* cx, unsigned argc, Value* vp); bool atomics_load(JSContext* cx, unsigned argc, Value* vp); bool atomics_store(JSContext* cx, unsigned argc, Value* vp); -bool atomics_fence(JSContext* cx, unsigned argc, Value* vp); bool atomics_add(JSContext* cx, unsigned argc, Value* vp); bool atomics_sub(JSContext* cx, unsigned argc, Value* vp); bool atomics_and(JSContext* cx, unsigned argc, Value* vp); bool atomics_or(JSContext* cx, unsigned argc, Value* vp); bool atomics_xor(JSContext* cx, unsigned argc, Value* vp); bool atomics_isLockFree(JSContext* cx, unsigned argc, Value* vp); -bool atomics_futexWait(JSContext* cx, unsigned argc, Value* vp); -bool atomics_futexWake(JSContext* cx, unsigned argc, Value* vp); -bool atomics_futexWakeOrRequeue(JSContext* cx, unsigned argc, Value* vp); +bool atomics_wait(JSContext* cx, unsigned argc, Value* vp); +bool atomics_wake(JSContext* cx, unsigned argc, Value* vp); /* asm.js callouts */ int32_t atomics_add_asm_callout(int32_t vt, int32_t offset, int32_t value); @@ -72,6 +61,12 @@ public: WakeForJSInterrupt // Interrupt requested }; + // Result code from wait(). + enum WaitResult { + FutexOK, + FutexTimedOut + }; + // Block the calling thread and wait. // // The futex lock must be held around this call. @@ -81,30 +76,30 @@ public: // // wait() will not wake up spuriously. It will return true and // set *result to a return code appropriate for - // Atomics.futexWait() on success, and return false on error. - bool wait(JSContext* cx, double timeout, AtomicsObject::FutexWaitResult* result); + // Atomics.wait() on success, and return false on error. + bool wait(JSContext* cx, double timeout, WaitResult* result); // Wake the thread represented by this Runtime. // // The futex lock must be held around this call. (The sleeping - // thread will not wake up until the caller of futexWake() + // thread will not wake up until the caller of Atomics.wake() // releases the lock.) // // If the thread is not waiting then this method does nothing. // - // If the thread is waiting in a call to futexWait() and the - // reason is WakeExplicit then the futexWait() call will return + // If the thread is waiting in a call to wait() and the + // reason is WakeExplicit then the wait() call will return // with Woken. // - // If the thread is waiting in a call to futexWait() and the - // reason is WakeForJSInterrupt then the futexWait() will return + // If the thread is waiting in a call to wait() and the + // reason is WakeForJSInterrupt then the wait() will return // with WaitingNotifiedForInterrupt; in the latter case the caller - // of futexWait() must handle the interrupt. + // of wait() must handle the interrupt. void wake(WakeReason reason); bool isWaiting(); - // If canWait() returns false (the default) then futexWait is disabled + // If canWait() returns false (the default) then wait() is disabled // on the runtime to which the FutexRuntime belongs. bool canWait() { return canWait_; diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp index c780d14347..0438425af7 100644 --- a/js/src/builtin/Intl.cpp +++ b/js/src/builtin/Intl.cpp @@ -473,17 +473,15 @@ IntlInitialize(JSContext* cx, HandleObject obj, Handle initialize MOZ_ASSERT(initializerValue.isObject()); MOZ_ASSERT(initializerValue.toObject().is()); - InvokeArgs args(cx); - if (!args.init(3)) - return false; + FixedInvokeArgs<3> args(cx); - args.setCallee(initializerValue); - args.setThis(NullValue()); args[0].setObject(*obj); args[1].set(locales); args[2].set(options); - return Invoke(cx, args); + RootedValue thisv(cx, NullValue()); + RootedValue ignored(cx); + return js::Call(cx, initializerValue, thisv, args, &ignored); } static bool @@ -540,30 +538,27 @@ intl_availableLocales(JSContext* cx, CountAvailable countAvailable, /** * Returns the object holding the internal properties for obj. */ -static bool -GetInternals(JSContext* cx, HandleObject obj, MutableHandleObject internals) +static JSObject* +GetInternals(JSContext* cx, HandleObject obj) { RootedValue getInternalsValue(cx); if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().getInternals, &getInternalsValue)) { - return false; + return nullptr; } MOZ_ASSERT(getInternalsValue.isObject()); MOZ_ASSERT(getInternalsValue.toObject().is()); - InvokeArgs args(cx); - if (!args.init(1)) - return false; + FixedInvokeArgs<1> args(cx); - args.setCallee(getInternalsValue); - args.setThis(NullValue()); args[0].setObject(*obj); - if (!Invoke(cx, args)) - return false; - internals.set(&args.rval().toObject()); - return true; + RootedValue v(cx, NullValue()); + if (!js::Call(cx, getInternalsValue, v, args, &v)) + return nullptr; + + return &v.toObject(); } static bool @@ -903,8 +898,8 @@ NewUCollator(JSContext* cx, HandleObject collator) { RootedValue value(cx); - RootedObject internals(cx); - if (!GetInternals(cx, collator, &internals)) + RootedObject internals(cx, GetInternals(cx, collator)); + if (!internals) return nullptr; if (!GetProperty(cx, internals, internals, cx->names().locale, &value)) @@ -1368,8 +1363,8 @@ NewUNumberFormat(JSContext* cx, HandleObject numberFormat) { RootedValue value(cx); - RootedObject internals(cx); - if (!GetInternals(cx, numberFormat, &internals)) + RootedObject internals(cx, GetInternals(cx, numberFormat)); + if (!internals) return nullptr; if (!GetProperty(cx, internals, internals, cx->names().locale, &value)) @@ -1957,8 +1952,8 @@ NewUDateFormat(JSContext* cx, HandleObject dateTimeFormat) { RootedValue value(cx); - RootedObject internals(cx); - if (!GetInternals(cx, dateTimeFormat, &internals)) + RootedObject internals(cx, GetInternals(cx, dateTimeFormat)); + if (!internals) return nullptr; if (!GetProperty(cx, internals, internals, cx->names().locale, &value)) diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index a5af21d27d..182825714f 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -434,10 +434,16 @@ js::WatchHandler(JSContext* cx, JSObject* obj_, jsid id_, JS::Value old, if (resolving.alreadyStarted()) return true; - JSObject* callable = (JSObject*)closure; - Value argv[] = { IdToValue(id), old, *nvp }; + FixedInvokeArgs<3> args(cx); + + args[0].set(IdToValue(id)); + args[1].set(old); + args[2].set(*nvp); + + RootedValue callable(cx, ObjectValue(*static_cast(closure))); + RootedValue thisv(cx, ObjectValue(*obj)); RootedValue rv(cx); - if (!Invoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable), ArrayLength(argv), argv, &rv)) + if (!Call(cx, callable, thisv, args, &rv)) return false; *nvp = rv; @@ -672,7 +678,7 @@ js::obj_getOwnPropertyDescriptor(JSContext* cx, unsigned argc, Value* vp) // Steps 5-7. Rooted desc(cx); return GetOwnPropertyDescriptor(cx, obj, id, &desc) && - FromPropertyDescriptor(cx, desc, args.rval()); + JS::FromPropertyDescriptor(cx, desc, args.rval()); } enum EnumerableOwnPropertiesKind { diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index 9e1cd7e8e5..d833b83a7e 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -18,6 +18,7 @@ #include "jsapi.h" #include "jsfriendapi.h" +#include "jsnum.h" #include "jsprf.h" #include "builtin/TypedObject.h" @@ -1254,70 +1255,6 @@ Select(JSContext* cx, unsigned argc, Value* vp) return StoreResult(cx, args, result); } -// Get an integer array index from a function argument. Coerce if necessary. -// -// When a JS function argument represents an integer index into an array, it is -// laundered like this: -// -// 1. numericIndex = ToNumber(argument) (may throw TypeError) -// 2. intIndex = ToInteger(numericIndex) -// 3. if intIndex != numericIndex throw RangeError -// -// This function additionally bounds the range to the non-negative contiguous -// integers: -// -// 4. if intIndex < 0 or intIndex > 2^53 throw RangeError -// -// Return true and set |*index| to the integer value if |argument| is a valid -// array index argument. Otherwise report an TypeError or RangeError and return -// false. -// -// The returned index will always be in the range 0 <= *index <= 2^53. -static bool -ArgumentToIntegerIndex(JSContext* cx, JS::HandleValue v, uint64_t* index) -{ - // Fast common case. - if (v.isInt32()) { - int32_t i = v.toInt32(); - if (i >= 0) { - *index = i; - return true; - } - } - - // Slow case. Use ToNumber() to coerce. This may throw a TypeError. - double d; - if (!ToNumber(cx, v, &d)) - return false; - - // Check that |d| is an integer in the valid range. - // - // Not all floating point integers fit in the range of a uint64_t, so we - // need a rough range check before the real range check in our caller. We - // could limit indexes to UINT64_MAX, but this would mean that our callers - // have to be very careful about integer overflow. The contiguous integer - // floating point numbers end at 2^53, so make that our upper limit. If we - // ever support arrays with more than 2^53 elements, this will need to - // change. - // - // Reject infinities, NaNs, and numbers outside the contiguous integer range - // with a RangeError. - - // Write relation so NaNs throw a RangeError. - if (!(0 <= d && d <= (uint64_t(1) << 53))) - return ErrorBadIndex(cx); - - // Check that d is an integer, throw a RangeError if not. - // Note that this conversion could invoke undefined behaviour without the - // range check above. - uint64_t i(d); - if (d != double(i)) - return ErrorBadIndex(cx); - - *index = i; - return true; -} - // Extract an integer lane index from a function argument. // // Register an exception and return false if the argument is not suitable. @@ -1325,7 +1262,7 @@ static bool ArgumentToLaneIndex(JSContext* cx, JS::HandleValue v, unsigned limit, unsigned* lane) { uint64_t arg; - if (!ArgumentToIntegerIndex(cx, v, &arg)) + if (!ToIntegerIndex(cx, v, &arg)) return false; if (arg >= limit) return ErrorBadIndex(cx); @@ -1352,7 +1289,7 @@ TypedArrayFromArgs(JSContext* cx, const CallArgs& args, uint32_t accessBytes, typedArray.set(&argobj); uint64_t index; - if (!ArgumentToIntegerIndex(cx, args[1], &index)) + if (!ToIntegerIndex(cx, args[1], &index)) return false; // Do the range check in 64 bits even when size_t is 32 bits. diff --git a/js/src/builtin/SymbolObject.cpp b/js/src/builtin/SymbolObject.cpp index b96c89defe..d5fc9d34b3 100644 --- a/js/src/builtin/SymbolObject.cpp +++ b/js/src/builtin/SymbolObject.cpp @@ -67,7 +67,7 @@ SymbolObject::initClass(JSContext* cx, HandleObject obj) return nullptr; // Define the well-known symbol properties, such as Symbol.iterator. - ImmutablePropertyNamePtr* names = &cx->names().iterator; + ImmutablePropertyNamePtr* names = cx->names().wellKnownSymbolNames(); RootedValue value(cx); unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT; WellKnownSymbols* wks = cx->runtime()->wellKnownSymbols; diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 77ffa4680f..bbd808152f 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -569,8 +569,10 @@ WasmBinaryToText(JSContext* cx, unsigned argc, Value* vp) StringBuffer buffer(cx); if (!wasm::BinaryToText(cx, bytes, length, buffer)) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL, - "print error"); + if (!cx->isExceptionPending()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_DECODE_FAIL, + "print error"); + } return false; } diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index fc3e25784f..eacccef46b 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -114,16 +114,12 @@ ConvertAndCopyTo(JSContext* cx, HandleAtom name, HandleValue val) { - RootedFunction func( - cx, SelfHostedFunction(cx, cx->names().ConvertAndCopyTo)); + RootedFunction func(cx, SelfHostedFunction(cx, cx->names().ConvertAndCopyTo)); if (!func) return false; - InvokeArgs args(cx); - if (!args.init(5)) - return false; + FixedInvokeArgs<5> args(cx); - args.setCallee(ObjectValue(*func)); args[0].setObject(*typeObj); args[1].setObject(*typedObj); args[2].setInt32(offset); @@ -133,7 +129,9 @@ ConvertAndCopyTo(JSContext* cx, args[3].setNull(); args[4].set(val); - return Invoke(cx, args); + RootedValue fval(cx, ObjectValue(*func)); + RootedValue dummy(cx); // ignored by ConvertAndCopyTo + return js::Call(cx, fval, dummy, args, &dummy); } static bool @@ -158,20 +156,14 @@ Reify(JSContext* cx, if (!func) return false; - InvokeArgs args(cx); - if (!args.init(3)) - return false; + FixedInvokeArgs<3> args(cx); - args.setCallee(ObjectValue(*func)); args[0].setObject(*type); args[1].setObject(*typedObj); args[2].setInt32(offset); - if (!Invoke(cx, args)) - return false; - - to.set(args.rval()); - return true; + RootedValue fval(cx, ObjectValue(*func)); + return js::Call(cx, fval, UndefinedHandleValue, args, to); } // Extracts the `prototype` property from `obj`, throwing if it is diff --git a/js/src/gc/Allocator.cpp b/js/src/gc/Allocator.cpp index 97cc601713..0683406038 100644 --- a/js/src/gc/Allocator.cpp +++ b/js/src/gc/Allocator.cpp @@ -110,7 +110,7 @@ js::Allocate(ExclusiveContext* cx, AllocKind kind, size_t nDynamicSlots, Initial static_assert(sizeof(JSObject_Slots0) >= CellSize, "All allocations must be at least the allocator-imposed minimum size."); - MOZ_ASSERT_IF(nDynamicSlots != 0, clasp->isNative()); + MOZ_ASSERT_IF(nDynamicSlots != 0, clasp->isNative() || clasp->isProxy()); // Off-main-thread alloc cannot trigger GC or make runtime assertions. if (!cx->isJSContext()) diff --git a/js/src/gc/Tracer.h b/js/src/gc/Tracer.h index a1aa218478..b083ea4f29 100644 --- a/js/src/gc/Tracer.h +++ b/js/src/gc/Tracer.h @@ -10,7 +10,6 @@ #include "jsfriendapi.h" #include "gc/Barrier.h" -#include "js/GCHashTable.h" namespace js { diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index 98fd6aa799..1c2e34288f 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -423,3 +423,9 @@ ZoneList::clear() while (!isEmpty()) removeFront(); } + +JS_PUBLIC_API(void) +JS::shadow::RegisterWeakCache(JS::Zone* zone, WeakCache* cachep) +{ + zone->registerWeakCache(cachep); +} diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index a7d24dbec6..8fda18b9b3 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -309,6 +309,12 @@ struct Zone : public JS::shadow::Zone, using WeakEdges = js::Vector; WeakEdges gcWeakRefs; + // List of non-ephemeron weak containers to sweep during beginSweepingZoneGroup. + mozilla::LinkedList> weakCaches_; + void registerWeakCache(WeakCache* cachep) { + weakCaches_.insertBack(cachep); + } + /* * Mapping from not yet marked keys to a vector of all values that the key * maps to in any live weak map. diff --git a/js/src/jit-test/tests/asm.js/testAtomics.js b/js/src/jit-test/tests/asm.js/testAtomics.js index db50e02791..3b09525b83 100644 --- a/js/src/jit-test/tests/asm.js/testAtomics.js +++ b/js/src/jit-test/tests/asm.js/testAtomics.js @@ -11,7 +11,6 @@ load(libdir + "asserts.js"); var loadModule_int32_code = USE_ASM + ` - var atomic_fence = stdlib.Atomics.fence; var atomic_load = stdlib.Atomics.load; var atomic_store = stdlib.Atomics.store; var atomic_cmpxchg = stdlib.Atomics.compareExchange; @@ -24,10 +23,6 @@ var loadModule_int32_code = var i32a = new stdlib.Int32Array(heap); - function do_fence() { - atomic_fence(); - } - // Load element 0 function do_load() { var v = 0; @@ -204,8 +199,7 @@ var loadModule_int32_code = return v|0; } - return { fence: do_fence, - load: do_load, + return { load: do_load, load_i: do_load_i, store: do_store, store_i: do_store_i, @@ -238,8 +232,6 @@ function test_int32(heap) { var size = Int32Array.BYTES_PER_ELEMENT; - i32m.fence(); - i32a[0] = 12345; assertEq(i32m.load(), 12345); assertEq(i32m.load_i(size*0), 12345); @@ -328,7 +320,6 @@ function test_int32(heap) { var loadModule_uint32_code = USE_ASM + ` - var atomic_fence = stdlib.Atomics.fence; var atomic_load = stdlib.Atomics.load; var atomic_store = stdlib.Atomics.store; var atomic_cmpxchg = stdlib.Atomics.compareExchange; @@ -609,7 +600,6 @@ function test_uint32(heap) { var loadModule_int16_code = USE_ASM + ` - var atomic_fence = stdlib.Atomics.fence; var atomic_load = stdlib.Atomics.load; var atomic_store = stdlib.Atomics.store; var atomic_cmpxchg = stdlib.Atomics.compareExchange; @@ -622,10 +612,6 @@ var loadModule_int16_code = var i16a = new stdlib.Int16Array(heap); - function do_fence() { - atomic_fence(); - } - // Load element 0 function do_load() { var v = 0; @@ -776,8 +762,7 @@ var loadModule_int16_code = return v|0; } - return { fence: do_fence, - load: do_load, + return { load: do_load, load_i: do_load_i, store: do_store, store_i: do_store_i, @@ -807,8 +792,6 @@ function test_int16(heap) { var size = Int16Array.BYTES_PER_ELEMENT; - i16m.fence(); - i16a[0] = 12345; assertEq(i16m.load(), 12345); assertEq(i16m.load_i(size*0), 12345); diff --git a/js/src/jit-test/tests/atomics/basic-tests.js b/js/src/jit-test/tests/atomics/basic-tests.js index 1a5235fe5d..e22f25f3a8 100644 --- a/js/src/jit-test/tests/atomics/basic-tests.js +++ b/js/src/jit-test/tests/atomics/basic-tests.js @@ -3,7 +3,7 @@ // These do not test atomicity, just that calling and coercions and // indexing and exception behavior all work right. // -// These do not test the futex operations. +// These do not test the wait/wake operations. load(libdir + "asserts.js"); @@ -66,8 +66,6 @@ function testMethod(a, ...indices) { Atomics.store(a, x, 0); // val = 0 - Atomics.fence(); - // val = 0 assertEq(Atomics.add(a, x, 3), 0); // val = 3 @@ -137,8 +135,6 @@ function testFunction(a, ...indices) { gAtomics_store(a, x, 0); // val = 0 - gAtomics_fence(); - // val = 0 assertEq(gAtomics_add(a, x, 3), 0); // val = 3 @@ -211,7 +207,7 @@ var globlength = 0; // Will be set later function testRangeCAS(a) { dprint("Range: " + a.constructor.name); - var msg = /out-of-range index for atomic access/; + var msg = /out-of-range index/; // A generic message assertErrorMessage(() => Atomics.compareExchange(a, -1, 0, 1), RangeError, msg); assertEq(a[0], 0); @@ -456,6 +452,13 @@ function testUint8Clamped(sab) { assertEq(thrown, true); } +function testWeirdIndices() { + var a = new Int8Array(new SharedArrayBuffer(16)); + a[3] = 10; + assertEq(Atomics.load(a, "0x03"), 10); + assertEq(Atomics.load(a, {valueOf: () => 3}), 10); +} + function isLittleEndian() { var xxx = new ArrayBuffer(2); var xxa = new Int16Array(xxx); @@ -497,7 +500,6 @@ function runTests() { gAtomics_exchange = Atomics.exchange; gAtomics_load = Atomics.load; gAtomics_store = Atomics.store; - gAtomics_fence = Atomics.fence; gAtomics_add = Atomics.add; gAtomics_sub = Atomics.sub; gAtomics_and = Atomics.and; @@ -551,6 +553,7 @@ function runTests() { // Misc testIsLockFree(); testIsLockFree2(); + testWeirdIndices(); } if (this.Atomics && this.SharedArrayBuffer) diff --git a/js/src/jit-test/tests/atomics/inline-fence.js b/js/src/jit-test/tests/atomics/inline-fence.js deleted file mode 100644 index 7362802811..0000000000 --- a/js/src/jit-test/tests/atomics/inline-fence.js +++ /dev/null @@ -1,31 +0,0 @@ -// |jit-test| slow; -// -// This is intended to be run manually with IONFLAGS=logs and -// postprocessing by iongraph to verify manually (by inspecting the -// MIR) that: -// -// - the fence operation is inlined as it should be -// - loads and stores are not moved across the fence -// -// Be sure to run with --ion-eager --ion-offthread-compile=off. - -function fence(ta) { - var x = ta[0]; - Atomics.fence(); - var y = ta[1]; - var z = y + 1; - var w = x + z; - return w; -} - -if (!this.SharedArrayBuffer || !this.Atomics) - quit(0); - -var sab = new SharedArrayBuffer(4096); -var ia = new Int32Array(sab); -for ( var i=0, limit=ia.length ; i < limit ; i++ ) - ia[i] = 37; -var v = 0; -for ( var i=0 ; i < 1000 ; i++ ) - v += fence(ia); -//print(v); diff --git a/js/src/jit-test/tests/gc/bug-1263884.js b/js/src/jit-test/tests/gc/bug-1263884.js new file mode 100644 index 0000000000..50c3527562 --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1263884.js @@ -0,0 +1,10 @@ +if (!('oomTest' in this)) + quit(); + +oomTest(function() { + eval(` + var argObj = function () { return arguments }() + for (var p in argObj); + delete argObj.callee; + `); +}); diff --git a/js/src/jit-test/tests/wasm/basic-control-flow.js b/js/src/jit-test/tests/wasm/basic-control-flow.js index c9c5e13142..733d32a4f2 100644 --- a/js/src/jit-test/tests/wasm/basic-control-flow.js +++ b/js/src/jit-test/tests/wasm/basic-control-flow.js @@ -144,8 +144,6 @@ assertThrowsInstanceOf(() => wasmEvalText('(module (func (result i32) (return)) // TODO: Reenable when syntactic arities are added for returns //assertThrowsInstanceOf(() => wasmEvalText('(module (func (return (i32.const 1))) (export "" 0))'), TypeError); -// TODO: convert these to wasmEval and assert some results once they are implemented - // ---------------------------------------------------------------------------- // br / br_if @@ -269,6 +267,116 @@ assertEq(isNonZero(0), 0); assertEq(isNonZero(1), 1); assertEq(isNonZero(-1), 1); +// branches with values +// br/br_if and block +assertErrorMessage(() => wasmEvalText('(module (func (result i32) (block (br 0))))'), TypeError, mismatchError("void", "i32")); +assertErrorMessage(() => wasmEvalText('(module (func (result i32) (block (br 0 (f32.const 42)))))'), TypeError, mismatchError("f32", "i32")); + +assertErrorMessage(() => wasmEvalText(`(module (func (result i32) (param i32) (block (if (get_local 0) (br 0 (i32.const 42))))) (export "" 0))`), TypeError, mismatchError("void", "i32")); +assertErrorMessage(() => wasmEvalText(`(module (func (result i32) (param i32) (block (if (get_local 0) (br 0 (i32.const 42))) (br 0 (f32.const 42)))) (export "" 0))`), TypeError, mismatchError("void", "i32")); + +assertEq(wasmEvalText('(module (func (result i32) (block (br 0 (i32.const 42)) (i32.const 13))) (export "" 0))')(), 42); + +assertEq(wasmEvalText('(module (func) (func (block (br 0 (call 0)) (i32.const 13))) (export "" 0))')(), undefined); +assertEq(wasmEvalText('(module (func) (func (block (br_if 0 (call 0) (i32.const 1)) (i32.const 13))) (export "" 0))')(), undefined); + +var f = wasmEvalText(`(module (func (result i32) (param i32) (block (if (get_local 0) (br 0 (i32.const 42))) (i32.const 43))) (export "" 0))`); +assertEq(f(0), 43); +assertEq(f(1), 42); + +var f = wasmEvalText(`(module (func (result i32) (param i32) (block (br_if 0 (i32.const 42) (get_local 0)) (i32.const 43))) (export "" 0))`); +assertEq(f(0), 43); +assertEq(f(1), 42); + +var f = wasmEvalText(`(module (func (result i32) (param i32) (block (if (get_local 0) (br 0 (i32.const 42))) (br 0 (i32.const 43)))) (export "" 0))`); +assertEq(f(0), 43); +assertEq(f(1), 42); + +var f = wasmEvalText(`(module (func (result i32) (param i32) (block (br_if 0 (i32.const 42) (get_local 0)) (br 0 (i32.const 43)))) (export "" 0))`); +assertEq(f(0), 43); +assertEq(f(1), 42); + +var f = wasmEvalText(`(module (func (param i32) (result i32) (i32.add (i32.const 1) (block (if (get_local 0) (br 0 (i32.const 99))) (i32.const -1)))) (export "" 0))`); +assertEq(f(0), 0); +assertEq(f(1), 100); + +var f = wasmEvalText(`(module (func (param i32) (result i32) (i32.add (i32.const 1) (block (br_if 0 (i32.const 99) (get_local 0)) (i32.const -1)))) (export "" 0))`); +assertEq(f(0), 0); +assertEq(f(1), 100); + +assertEq(wasmEvalText(`(module (func (result i32) (block (br 0 (return (i32.const 42))) (i32.const 0))) (export "" 0))`)(), 42); +assertEq(wasmEvalText(`(module (func (result i32) (block (return (br 0 (i32.const 42))))) (export "" 0))`)(), 42); +assertEq(wasmEvalText(`(module (func (result i32) (block (return (br 0 (i32.const 42))) (i32.const 0))) (export "" 0))`)(), 42); + +assertEq(wasmEvalText(`(module (func (result f32) (block (br 0 (i32.const 0))) (block (br 0 (f32.const 42)))) (export "" 0))`)(), 42); + +var called = 0; +var imports = { + sideEffects: { + ifTrue(x) {assertEq(x, 13); called++;}, + ifFalse(x) {assertEq(x, 37); called--;} + } +} +var f = wasmEvalText(`(module + (import "sideEffects" "ifTrue" (param i32)) + (import "sideEffects" "ifFalse" (param i32)) + (func + (param i32) (result i32) + (block $outer + (if + (get_local 0) + (br $outer (call_import 0 (i32.const 13))) + ) + (if + (i32.eqz (get_local 0)) + (br $outer (call_import 1 (i32.const 37))) + ) + ) + (i32.const 42) + ) +(export "" 0))`, imports); +assertEq(f(0), 42); +assertEq(called, -1); +assertEq(f(1), 42); +assertEq(called, 0); + +// br/br_if and loop +assertEq(wasmEvalText(`(module (func (param i32) (result i32) (loop $out $in (br $out (get_local 0)))) (export "" 0))`)(1), 1); + +assertEq(wasmEvalText(`(module (func (param i32) (result i32) + (loop $out $in + (if (get_local 0) (br $in (i32.const 1))) + (if (get_local 0) (br $in (f32.const 2))) + (if (get_local 0) (br $in (f64.const 3))) + (if (get_local 0) (br $in)) + (i32.const 7) + ) + ) (export "" 0))`)(0), 7); + +assertEq(wasmEvalText(`(module + (func + (result i32) + (local i32) + (loop $out $in + (set_local 0 (i32.add (get_local 0) (i32.const 1))) + (if (i32.ge_s (get_local 0) (i32.const 7)) (br $out (get_local 0))) + (br $in) + ) + ) +(export "" 0))`)(), 7); + +assertEq(wasmEvalText(`(module + (func + (result i32) + (local i32) + (loop $out $in + (set_local 0 (i32.add (get_local 0) (i32.const 1))) + (br_if $out (get_local 0) (i32.ge_s (get_local 0) (i32.const 7))) + (br $in) + ) + ) +(export "" 0))`)(), 7); + // ---------------------------------------------------------------------------- // loop @@ -410,8 +518,8 @@ assertEq(f(3), -1); // unreachable const UNREACHABLE = /unreachable/; -assertErrorMessage(wasmEvalText(`(module (func (trap)) (export "" 0))`), Error, UNREACHABLE); -assertErrorMessage(wasmEvalText(`(module (func (if (trap) (i32.const 0))) (export "" 0))`), Error, UNREACHABLE); -assertErrorMessage(wasmEvalText(`(module (func (block (br_if 0 (trap)))) (export "" 0))`), Error, UNREACHABLE); -assertErrorMessage(wasmEvalText(`(module (func (block (br_table 0 (trap)))) (export "" 0))`), Error, UNREACHABLE); -assertErrorMessage(wasmEvalText(`(module (func (result i32) (i32.add (i32.const 0) (trap))) (export "" 0))`), Error, UNREACHABLE); +assertErrorMessage(wasmEvalText(`(module (func (unreachable)) (export "" 0))`), Error, UNREACHABLE); +assertErrorMessage(wasmEvalText(`(module (func (if (unreachable) (i32.const 0))) (export "" 0))`), Error, UNREACHABLE); +assertErrorMessage(wasmEvalText(`(module (func (block (br_if 0 (unreachable)))) (export "" 0))`), Error, UNREACHABLE); +assertErrorMessage(wasmEvalText(`(module (func (block (br_table 0 (unreachable)))) (export "" 0))`), Error, UNREACHABLE); +assertErrorMessage(wasmEvalText(`(module (func (result i32) (i32.add (i32.const 0) (unreachable))) (export "" 0))`), Error, UNREACHABLE); diff --git a/js/src/jit-test/tests/wasm/basic-conversion.js b/js/src/jit-test/tests/wasm/basic-conversion.js index 7ebb920c29..c79fc962b0 100644 --- a/js/src/jit-test/tests/wasm/basic-conversion.js +++ b/js/src/jit-test/tests/wasm/basic-conversion.js @@ -178,6 +178,9 @@ if (getBuildConfiguration().x64) { testConversion('i64', 'trunc_u', 'f32', "nan", "0x8000000000000000"); testConversion('i64', 'trunc_u', 'f32', "infinity", "0x8000000000000000"); testConversion('i64', 'trunc_u', 'f32', "-infinity", "0x8000000000000000"); + + testConversion('i64', 'reinterpret', 'f64', 40.09999999999968, 4630840390592548000); + testConversion('f64', 'reinterpret', 'i64', 4630840390592548000, 40.09999999999968); } else { // Sleeper test: once i64 works on more platforms, remove this if-else. try { @@ -192,16 +195,13 @@ testConversion('i32', 'trunc_s', 'f32', 40.1, 40); testConversion('i32', 'trunc_u', 'f32', 40.1, 40); testConversion('i32', 'trunc_s', 'f64', 40.1, 40); testConversion('i32', 'trunc_u', 'f64', 40.1, 40); -//testConversion('i32', 'reinterpret', 'f32', 40.1, 1109419622); // TODO: NYI - -//testConversion('i64', 'reinterpret', 'f64', 40.1, 1109419622); // TODO: NYI +testConversion('i32', 'reinterpret', 'f32', 40.1, 1109419622); testConversion('f32', 'convert_s', 'i32', 40, 40); testConversion('f32', 'convert_u', 'i32', 40, 40); testConversion('f32', 'demote', 'f64', 40.1, 40.099998474121094); -//testConversion('f32', 'reinterpret', 'i32', 40, 5.605193857299268e-44); // TODO: NYI +testConversion('f32', 'reinterpret', 'i32', 40, 5.605193857299268e-44); testConversion('f64', 'convert_s', 'i32', 40, 40); testConversion('f64', 'convert_u', 'i32', 40, 40); testConversion('f64', 'promote', 'f32', 40.1, 40.099998474121094); -//testConversion('f64', 'reinterpret', 'i64', 40.1, 4630840390592548045); // TODO: NYI diff --git a/js/src/jit-test/tests/wasm/random-control-flow.js b/js/src/jit-test/tests/wasm/random-control-flow.js index cd4a1c7a55..e9221663ce 100644 --- a/js/src/jit-test/tests/wasm/random-control-flow.js +++ b/js/src/jit-test/tests/wasm/random-control-flow.js @@ -10,3 +10,116 @@ wasmEvalText(`(module (func $func$0 (block (if (i32.const 1) (loop (br_table 0 (br 0))))) ) )`); + +wasmEvalText(`(module (func + (select + (block + (block + (br_table + 1 + 0 + (i32.const 1) + ) + ) + (i32.const 2) + ) + (i32.const 3) + (i32.const 4) + ) +)) +`); + +wasmEvalText(`(module + (func (result i32) (param i32) (param i32) (i32.const 0)) + (func (result i32) + (call 0 (i32.const 1) (call 0 (i32.const 2) (i32.const 3))) + (call 0 (unreachable) (i32.const 4)) + ) +)`); + +wasmEvalText(` +(module + + (func + (param i32) (param i32) (param i32) (param i32) + (result i32) + (i32.const 0) + ) + + (func (result i32) + (call 0 + (i32.const 42) + (i32.const 53) + (call 0 (i32.const 100) (i32.const 13) (i32.const 37) (i32.const 128)) + (return (i32.const 42)) + ) + ) + + (export "" 1) +) +`)(); + +wasmEvalText(` +(module + (import "check" "one" (param i32)) + (import "check" "two" (param i32) (param i32)) + (func (param i32) (call_import 0 (get_local 0))) + (func (param i32) (param i32) (call_import 1 (get_local 0) (get_local 1))) + (func + (call 1 + (i32.const 43) + (block $b + (if (i32.const 1) + (call 0 + (block + (call 0 (i32.const 42)) + (br $b (i32.const 10))))) + (i32.const 44)))) + (export "foo" 2)) +`, { + check: { + one(x) { + assertEq(x, 42); + }, + two(x, y) { + assertEq(x, 43); + assertEq(y, 10); + } + } +}).foo(); + +wasmEvalText(`(module (func + (return (i32.const 0)) + (select + (loop (i32.const 1)) + (loop (i32.const 2)) + (i32.const 3) + ) +))`); + +wasmEvalText(`(module (func + (block $return + (block $beforeReturn + (loop $out $in + (block $otherTable + (br_table + $return + $return + $otherTable + $beforeReturn + (i32.const 0) + ) + ) + (block $backTop + (br_table + $backTop + $backTop + $beforeReturn + (i32.const 0) + ) + ) + (br $in) + ) + ) + ) +))`); diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 3e9ceea3a3..335f2221ec 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -1902,7 +1902,7 @@ BaselineCompiler::emit_JSOP_NEWARRAY() if (!group) return false; - ICNewArray_Fallback::Compiler stubCompiler(cx, group); + ICNewArray_Fallback::Compiler stubCompiler(cx, group, ICStubCompiler::Engine::Baseline); if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false; @@ -1972,7 +1972,7 @@ BaselineCompiler::emit_JSOP_NEWOBJECT() { frame.syncStack(0); - ICNewObject_Fallback::Compiler stubCompiler(cx); + ICNewObject_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::Baseline); if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false; @@ -1994,13 +1994,13 @@ BaselineCompiler::emit_JSOP_NEWINIT() if (!group) return false; - ICNewArray_Fallback::Compiler stubCompiler(cx, group); + ICNewArray_Fallback::Compiler stubCompiler(cx, group, ICStubCompiler::Engine::Baseline); if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false; } else { MOZ_ASSERT(key == JSProto_Object); - ICNewObject_Fallback::Compiler stubCompiler(cx); + ICNewObject_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::Baseline); if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false; } diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 2de1025288..d2e02ff7de 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -433,159 +433,6 @@ typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, M static const VMFunction DoCallNativeGetterInfo = FunctionInfo(DoCallNativeGetter); -// -// NewArray_Fallback -// - -static bool -DoNewArray(JSContext* cx, BaselineFrame* frame, ICNewArray_Fallback* stub, uint32_t length, - MutableHandleValue res) -{ - FallbackICSpew(cx, stub, "NewArray"); - - RootedObject obj(cx); - if (stub->templateObject()) { - RootedObject templateObject(cx, stub->templateObject()); - obj = NewArrayOperationWithTemplate(cx, templateObject); - if (!obj) - return false; - } else { - RootedScript script(cx, frame->script()); - jsbytecode* pc = stub->icEntry()->pc(script); - obj = NewArrayOperation(cx, script, pc, length); - if (!obj) - return false; - - if (obj && !obj->isSingleton() && !obj->group()->maybePreliminaryObjects()) { - JSObject* templateObject = NewArrayOperation(cx, script, pc, length, TenuredObject); - if (!templateObject) - return false; - stub->setTemplateObject(templateObject); - } - } - - res.setObject(*obj); - return true; -} - -typedef bool(*DoNewArrayFn)(JSContext*, BaselineFrame*, ICNewArray_Fallback*, uint32_t, - MutableHandleValue); -static const VMFunction DoNewArrayInfo = FunctionInfo(DoNewArray, TailCall); - -bool -ICNewArray_Fallback::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - EmitRestoreTailCallReg(masm); - - masm.push(R0.scratchReg()); // length - masm.push(ICStubReg); // stub. - pushStubPayload(masm, R0.scratchReg()); - - return tailCallVM(DoNewArrayInfo, masm); -} - -// -// NewObject_Fallback -// - -// Unlike typical baseline IC stubs, the code for NewObject_WithTemplate is -// specialized for the template object being allocated. -static JitCode* -GenerateNewObjectWithTemplateCode(JSContext* cx, JSObject* templateObject) -{ - JitContext jctx(cx, nullptr); - MacroAssembler masm; -#ifdef JS_CODEGEN_ARM - masm.setSecondScratchReg(BaselineSecondScratchReg); -#endif - - Label failure; - Register objReg = R0.scratchReg(); - Register tempReg = R1.scratchReg(); - masm.movePtr(ImmGCPtr(templateObject->group()), tempReg); - masm.branchTest32(Assembler::NonZero, Address(tempReg, ObjectGroup::offsetOfFlags()), - Imm32(OBJECT_FLAG_PRE_TENURE), &failure); - masm.branchPtr(Assembler::NotEqual, AbsoluteAddress(cx->compartment()->addressOfMetadataBuilder()), - ImmWord(0), &failure); - masm.createGCObject(objReg, tempReg, templateObject, gc::DefaultHeap, &failure); - masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0); - - EmitReturnFromIC(masm); - masm.bind(&failure); - EmitStubGuardFailure(masm); - - Linker linker(masm); - AutoFlushICache afc("GenerateNewObjectWithTemplateCode"); - return linker.newCode(cx, BASELINE_CODE); -} - -static bool -DoNewObject(JSContext* cx, BaselineFrame* frame, ICNewObject_Fallback* stub, MutableHandleValue res) -{ - FallbackICSpew(cx, stub, "NewObject"); - - RootedObject obj(cx); - - RootedObject templateObject(cx, stub->templateObject()); - if (templateObject) { - MOZ_ASSERT(!templateObject->group()->maybePreliminaryObjects()); - obj = NewObjectOperationWithTemplate(cx, templateObject); - } else { - RootedScript script(cx, frame->script()); - jsbytecode* pc = stub->icEntry()->pc(script); - obj = NewObjectOperation(cx, script, pc); - - if (obj && !obj->isSingleton() && !obj->group()->maybePreliminaryObjects()) { - JSObject* templateObject = NewObjectOperation(cx, script, pc, TenuredObject); - if (!templateObject) - return false; - - if (templateObject->is() || - !templateObject->as().hasDynamicSlots()) - { - JitCode* code = GenerateNewObjectWithTemplateCode(cx, templateObject); - if (!code) - return false; - - ICStubSpace* space = - ICStubCompiler::StubSpaceForKind(ICStub::NewObject_WithTemplate, script, - ICStubCompiler::Engine::Baseline); - ICStub* templateStub = ICStub::New(cx, space, code); - if (!templateStub) - return false; - - stub->addNewStub(templateStub); - } - - stub->setTemplateObject(templateObject); - } - } - - if (!obj) - return false; - - res.setObject(*obj); - return true; -} - -typedef bool(*DoNewObjectFn)(JSContext*, BaselineFrame*, ICNewObject_Fallback*, MutableHandleValue); -static const VMFunction DoNewObjectInfo = FunctionInfo(DoNewObject, TailCall); - -bool -ICNewObject_Fallback::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - EmitRestoreTailCallReg(masm); - - masm.push(ICStubReg); // stub. - pushStubPayload(masm, R0.scratchReg()); - - return tailCallVM(DoNewObjectInfo, masm); -} - // // ToBool_Fallback // @@ -6119,7 +5966,7 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint return false; } - if (!InternalInvoke(cx, callArgs)) + if (!CallFromStack(cx, callArgs)) return false; res.set(callArgs.rval()); diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index 2cc45aa497..18195b0b29 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -190,98 +190,6 @@ class ICTypeUpdate_ObjectGroup : public ICStub }; }; -class ICNewArray_Fallback : public ICFallbackStub -{ - friend class ICStubSpace; - - HeapPtrObject templateObject_; - - // The group used for objects created here is always available, even if the - // template object itself is not. - HeapPtrObjectGroup templateGroup_; - - ICNewArray_Fallback(JitCode* stubCode, ObjectGroup* templateGroup) - : ICFallbackStub(ICStub::NewArray_Fallback, stubCode), - templateObject_(nullptr), templateGroup_(templateGroup) - {} - - public: - class Compiler : public ICStubCompiler { - RootedObjectGroup templateGroup; - bool generateStubCode(MacroAssembler& masm); - - public: - Compiler(JSContext* cx, ObjectGroup* templateGroup) - : ICStubCompiler(cx, ICStub::NewArray_Fallback, Engine::Baseline), - templateGroup(cx, templateGroup) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode(), templateGroup); - } - }; - - HeapPtrObject& templateObject() { - return templateObject_; - } - - void setTemplateObject(JSObject* obj) { - MOZ_ASSERT(obj->group() == templateGroup()); - templateObject_ = obj; - } - - HeapPtrObjectGroup& templateGroup() { - return templateGroup_; - } - - void setTemplateGroup(ObjectGroup* group) { - templateObject_ = nullptr; - templateGroup_ = group; - } -}; - -class ICNewObject_Fallback : public ICFallbackStub -{ - friend class ICStubSpace; - - HeapPtrObject templateObject_; - - explicit ICNewObject_Fallback(JitCode* stubCode) - : ICFallbackStub(ICStub::NewObject_Fallback, stubCode), templateObject_(nullptr) - {} - - public: - class Compiler : public ICStubCompiler { - bool generateStubCode(MacroAssembler& masm); - - public: - explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::NewObject_Fallback, Engine::Baseline) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode()); - } - }; - - HeapPtrObject& templateObject() { - return templateObject_; - } - - void setTemplateObject(JSObject* obj) { - templateObject_ = obj; - } -}; - -class ICNewObject_WithTemplate : public ICStub -{ - friend class ICStubSpace; - - explicit ICNewObject_WithTemplate(JitCode* stubCode) - : ICStub(ICStub::NewObject_WithTemplate, stubCode) - {} -}; - // ToBool // JSOP_IFNE diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 168d37eb8d..9fdb0ec20b 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -8,6 +8,7 @@ #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" +#include "mozilla/Casting.h" #include "mozilla/DebugOnly.h" #include "mozilla/EnumeratedArray.h" #include "mozilla/EnumeratedRange.h" @@ -54,6 +55,7 @@ using namespace js; using namespace js::jit; +using mozilla::AssertedCast; using mozilla::DebugOnly; using mozilla::FloatingPoint; using mozilla::Maybe; @@ -2372,6 +2374,41 @@ CodeGenerator::visitUnarySharedStub(LUnarySharedStub* lir) } } +void +CodeGenerator::visitNullarySharedStub(LNullarySharedStub* lir) +{ + jsbytecode* pc = lir->mir()->resumePoint()->pc(); + JSOp jsop = JSOp(*pc); + switch (jsop) { + case JSOP_NEWARRAY: { + uint32_t length = GET_UINT32(pc); + MOZ_ASSERT(length <= INT32_MAX, + "the bytecode emitter must fail to compile code that would " + "produce JSOP_NEWARRAY with a length exceeding int32_t range"); + + // Pass length in R0. + masm.move32(Imm32(AssertedCast(length)), R0.scratchReg()); + emitSharedStub(ICStub::Kind::NewArray_Fallback, lir); + break; + } + case JSOP_NEWOBJECT: + emitSharedStub(ICStub::Kind::NewObject_Fallback, lir); + break; + case JSOP_NEWINIT: { + JSProtoKey key = JSProtoKey(GET_UINT8(pc)); + if (key == JSProto_Array) { + masm.move32(Imm32(0), R0.scratchReg()); + emitSharedStub(ICStub::Kind::NewArray_Fallback, lir); + } else { + emitSharedStub(ICStub::Kind::NewObject_Fallback, lir); + } + break; + } + default: + MOZ_CRASH("Unsupported jsop in shared stubs."); + } +} + typedef JSObject* (*LambdaFn)(JSContext*, HandleFunction, HandleObject); static const VMFunction LambdaInfo = FunctionInfo(js::Lambda); @@ -5141,7 +5178,7 @@ CodeGenerator::visitNewArray(LNewArray* lir) MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT); - if (lir->mir()->shouldUseVM()) { + if (!templateObject) { visitNewArrayCallVM(lir); return; } @@ -5398,7 +5435,7 @@ CodeGenerator::visitNewObject(LNewObject* lir) Register tempReg = ToRegister(lir->temp()); JSObject* templateObject = lir->mir()->templateObject(); - if (lir->mir()->shouldUseVM()) { + if (!templateObject) { visitNewObjectVMCall(lir); return; } @@ -8847,6 +8884,22 @@ CodeGenerator::linkSharedStubs(JSContext* cx) stub = stubCompiler.getStub(&stubSpace_); break; } + case ICStub::Kind::NewArray_Fallback: { + JSScript* script = sharedStubs_[i].entry.script(); + jsbytecode* pc = sharedStubs_[i].entry.pc(script); + ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); + if (!group) + return false; + + ICNewArray_Fallback::Compiler stubCompiler(cx, group, ICStubCompiler::Engine::IonMonkey); + stub = stubCompiler.getStub(&stubSpace_); + break; + } + case ICStub::Kind::NewObject_Fallback: { + ICNewObject_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::IonMonkey); + stub = stubCompiler.getStub(&stubSpace_); + break; + } default: MOZ_CRASH("Unsupported shared stub."); } diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 86f279a3f4..89ea417822 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -126,6 +126,7 @@ class CodeGenerator : public CodeGeneratorSpecific void emitSharedStub(ICStub::Kind kind, LInstruction* lir); void visitBinarySharedStub(LBinarySharedStub* lir); void visitUnarySharedStub(LUnarySharedStub* lir); + void visitNullarySharedStub(LNullarySharedStub* lir); void visitLambda(LLambda* lir); void visitOutOfLineLambdaArrow(OutOfLineLambdaArrow* ool); void visitLambdaArrow(LLambdaArrow* lir); diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index fe0e734d1c..444d7371f2 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -21,7 +21,6 @@ _(AtomicsExchange) \ _(AtomicsLoad) \ _(AtomicsStore) \ - _(AtomicsFence) \ _(AtomicsAdd) \ _(AtomicsSub) \ _(AtomicsAnd) \ diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index c24d3ef7a9..a10376cd14 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -2382,6 +2382,8 @@ jit::AssertBasicGraphCoherency(MIRGraph& graph) MOZ_ASSERT(control->resumePoint() == nullptr); for (uint32_t i = 0, end = control->numOperands(); i < end; i++) CheckOperand(control, control->getUseFor(i), &usesBalance); + for (size_t i = 0; i < control->numSuccessors(); i++) + MOZ_ASSERT(control->getSuccessor(i)); } // In case issues, see the _DEBUG_CHECK_OPERANDS_USES_BALANCE macro above. diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index b8f874f3ba..13500c4a87 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -7148,34 +7148,120 @@ IonBuilder::compareTrySharedStub(bool* emitted, JSOp op, MDefinition* left, MDef } bool -IonBuilder::jsop_newarray(uint32_t length) +IonBuilder::newArrayTryTemplateObject(bool* emitted, JSObject* templateObject, uint32_t length) { - JSObject* templateObject = inspector->getTemplateObject(pc); - gc::InitialHeap heap; - MConstant* templateConst; + MOZ_ASSERT(*emitted == false); - if (templateObject) { - heap = templateObject->group()->initialHeap(constraints()); - templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); - } else { - heap = gc::DefaultHeap; - templateConst = MConstant::New(alloc(), NullValue()); + if (!templateObject) + return true; + + if (templateObject->is()) { + MOZ_ASSERT(templateObject->as().capacity() >= length); + if (!templateObject->as().hasInlineElements()) + return true; } + + MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT); + + size_t arraySlots = + gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER; + + if (length > arraySlots) + return true; + + // Emit fastpath. + + gc::InitialHeap heap = templateObject->group()->initialHeap(constraints()); + MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); current->add(templateConst); MNewArray* ins = MNewArray::New(alloc(), constraints(), length, templateConst, heap, pc); current->add(ins); current->push(ins); + *emitted = true; + return true; +} + +bool +IonBuilder::newArrayTrySharedStub(bool* emitted) +{ + MOZ_ASSERT(*emitted == false); + + // Try to emit a shared stub cache. + + if (JitOptions.disableSharedStubs) + return true; + + if (*pc != JSOP_NEWINIT && *pc != JSOP_NEWARRAY) + return true; + + MInstruction* stub = MNullarySharedStub::New(alloc()); + current->add(stub); + current->push(stub); + + if (!resumeAfter(stub)) + return false; + + *emitted = true; + return true; +} + +bool +IonBuilder::newArrayTryVM(bool* emitted, uint32_t length) +{ + MOZ_ASSERT(*emitted == false); + + // Emit a VM call. + + gc::InitialHeap heap = gc::DefaultHeap; + MConstant* templateConst = MConstant::New(alloc(), NullValue()); + current->add(templateConst); + + MNewArray* ins = MNewArray::New(alloc(), constraints(), length, templateConst, heap, pc); + current->add(ins); + current->push(ins); + + *emitted = true; + return true; +} + +bool +IonBuilder::jsop_newarray(uint32_t length) +{ + JSObject* templateObject = inspector->getTemplateObject(pc); + if (!jsop_newarray(templateObject, length)) + return false; + + // Improve resulting typeset. ObjectGroup* templateGroup = inspector->getTemplateObjectGroup(pc); if (templateGroup) { TemporaryTypeSet* types = MakeSingletonTypeSet(constraints(), templateGroup); - ins->setResultTypeSet(types); + current->peek(-1)->setResultTypeSet(types); } return true; } +bool +IonBuilder::jsop_newarray(JSObject* templateObject, uint32_t length) +{ + bool emitted = false; + + if (!forceInlineCaches()) { + if (!newArrayTryTemplateObject(&emitted, templateObject, length) || emitted) + return emitted; + } + + if (!newArrayTrySharedStub(&emitted) || emitted) + return emitted; + + if (!newArrayTryVM(&emitted, length) || emitted) + return emitted; + + MOZ_CRASH("newarray should have been emited"); +} + bool IonBuilder::jsop_newarray_copyonwrite() { @@ -7199,28 +7285,98 @@ IonBuilder::jsop_newarray_copyonwrite() } bool -IonBuilder::jsop_newobject() +IonBuilder::newObjectTryTemplateObject(bool* emitted, JSObject* templateObject) { - JSObject* templateObject = inspector->getTemplateObject(pc); - gc::InitialHeap heap; - MConstant* templateConst; + MOZ_ASSERT(*emitted == false); - if (templateObject) { - heap = templateObject->group()->initialHeap(constraints()); - templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); - } else { - heap = gc::DefaultHeap; - templateConst = MConstant::New(alloc(), NullValue()); - } + if (!templateObject) + return true; + if (templateObject->is() && templateObject->as().hasDynamicSlots()) + return true; + + // Emit fastpath. + + MNewObject::Mode mode; + if (JSOp(*pc) == JSOP_NEWOBJECT || JSOp(*pc) == JSOP_NEWINIT) + mode = MNewObject::ObjectLiteral; + else + mode = MNewObject::ObjectCreate; + + gc::InitialHeap heap = templateObject->group()->initialHeap(constraints()); + MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); current->add(templateConst); - MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst, heap, - MNewObject::ObjectLiteral); + MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst, heap, mode); current->add(ins); current->push(ins); - return resumeAfter(ins); + if (!resumeAfter(ins)) + return false; + + *emitted = true; + return true; +} + +bool +IonBuilder::newObjectTrySharedStub(bool* emitted) +{ + MOZ_ASSERT(*emitted == false); + + // Try to emit a shared stub cache. + + if (JitOptions.disableSharedStubs) + return true; + + MInstruction* stub = MNullarySharedStub::New(alloc()); + current->add(stub); + current->push(stub); + + if (!resumeAfter(stub)) + return false; + + *emitted = true; + return true; +} + +bool +IonBuilder::newObjectTryVM(bool* emitted) +{ + // Emit a VM call. + + gc::InitialHeap heap = gc::DefaultHeap; + MConstant* templateConst = MConstant::New(alloc(), NullValue()); + current->add(templateConst); + + MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst, heap, + MNewObject::ObjectLiteral); + current->add(ins); + current->push(ins); + + if (!resumeAfter(ins)) + return false; + + *emitted = true; + return true; +} + +bool +IonBuilder::jsop_newobject() +{ + bool emitted = false; + + if (!forceInlineCaches()) { + JSObject* templateObject = inspector->getTemplateObject(pc); + if (!newObjectTryTemplateObject(&emitted, templateObject) || emitted) + return emitted; + } + if (!newObjectTrySharedStub(&emitted) || emitted) + return emitted; + + if (!newObjectTryVM(&emitted) || emitted) + return emitted; + + MOZ_CRASH("newobject should have been emited"); } bool @@ -7312,7 +7468,10 @@ IonBuilder::initializeArrayElement(MDefinition* obj, size_t index, MDefinition* if (NeedsPostBarrier(value)) current->add(MPostWriteBarrier::New(alloc(), obj, value)); - if (obj->toNewArray()->convertDoubleElements()) { + if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) || + (obj->isNullarySharedStub() && + obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles)) + { MInstruction* valueDouble = MToDouble::New(alloc(), value); current->add(valueDouble); value = valueDouble; @@ -7358,14 +7517,19 @@ IonBuilder::jsop_initprop(PropertyName* name) MDefinition* obj = current->peek(-2); if (obj->isLambda()) { useSlowPath = true; - } else if (JSObject* templateObject = obj->toNewObject()->templateObject()) { - if (templateObject->is()) { - if (!templateObject->as().containsPure(name)) - useSlowPath = true; + } else if (obj->isNewObject()) { + if (JSObject* templateObject = obj->toNewObject()->templateObject()) { + if (templateObject->is()) { + if (!templateObject->as().containsPure(name)) + useSlowPath = true; + } else { + MOZ_ASSERT(templateObject->as().layout().lookup(name)); + } } else { - MOZ_ASSERT(templateObject->as().layout().lookup(name)); + useSlowPath = true; } } else { + MOZ_ASSERT(obj->isNullarySharedStub()); useSlowPath = true; } @@ -10385,20 +10549,16 @@ IonBuilder::jsop_rest() unsigned numFormals = info().nargs() - 1; unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0; - MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); - current->add(templateConst); - - MNewArray* array = MNewArray::New(alloc(), constraints(), numRest, templateConst, - templateObject->group()->initialHeap(constraints()), pc); - current->add(array); + if (!jsop_newarray(numRest)) + return false; if (numRest == 0) { // No more updating to do. (Note that in this one case the length from // the template object is already correct.) - current->push(array); return true; } + MDefinition *array = current->peek(-1); MElements* elements = MElements::New(alloc(), array); current->add(elements); @@ -10429,7 +10589,6 @@ IonBuilder::jsop_rest() MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, index); current->add(initLength); - current->push(array); return true; } diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 82d6cfe3a9..e131fc3c20 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -509,6 +509,16 @@ class IonBuilder MDefinition* right); bool compareTrySharedStub(bool* emitted, JSOp op, MDefinition* left, MDefinition* right); + // jsop_newarray helpers. + bool newArrayTrySharedStub(bool* emitted); + bool newArrayTryTemplateObject(bool* emitted, JSObject* templateObject, uint32_t length); + bool newArrayTryVM(bool* emitted, uint32_t length); + + // jsop_newobject helpers. + bool newObjectTrySharedStub(bool* emitted); + bool newObjectTryTemplateObject(bool* emitted, JSObject* templateObject); + bool newObjectTryVM(bool* emitted); + // jsop_in helpers. bool inTryDense(bool* emitted, MDefinition* obj, MDefinition* id); bool inTryFold(bool* emitted, MDefinition* obj, MDefinition* id); @@ -719,6 +729,7 @@ class IonBuilder bool jsop_delprop(PropertyName* name); bool jsop_delelem(); bool jsop_newarray(uint32_t length); + bool jsop_newarray(JSObject* templateObject, uint32_t length); bool jsop_newarray_copyonwrite(); bool jsop_newobject(); bool jsop_initelem(); @@ -838,7 +849,6 @@ class IonBuilder InliningStatus inlineAtomicsExchange(CallInfo& callInfo); InliningStatus inlineAtomicsLoad(CallInfo& callInfo); InliningStatus inlineAtomicsStore(CallInfo& callInfo); - InliningStatus inlineAtomicsFence(CallInfo& callInfo); InliningStatus inlineAtomicsBinop(CallInfo& callInfo, InlinableNative target); InliningStatus inlineAtomicsIsLockFree(CallInfo& callInfo); diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index 70f9e52add..3c959fbe29 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -700,10 +700,10 @@ CheckDOMProxyExpandoDoesNotShadow(JSContext* cx, MacroAssembler& masm, JSObject* ExpandoAndGeneration* expandoAndGeneration = (ExpandoAndGeneration*)expandoVal.toPrivate(); masm.movePtr(ImmPtr(expandoAndGeneration), tempVal.scratchReg()); - masm.branch32(Assembler::NotEqual, + masm.branch64(Assembler::NotEqual, Address(tempVal.scratchReg(), ExpandoAndGeneration::offsetOfGeneration()), - Imm32(expandoAndGeneration->generation), + Imm64(expandoAndGeneration->generation), &failDOMProxyCheck); expandoVal = expandoAndGeneration->expando; diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp index fa142bee49..e04d13908c 100644 --- a/js/src/jit/JitOptions.cpp +++ b/js/src/jit/JitOptions.cpp @@ -56,6 +56,7 @@ T overrideDefault(const char* param, T dflt) { } return dflt; } + #define SET_DEFAULT(var, dflt) var = overrideDefault("JIT_OPTION_" #var, dflt) DefaultJitOptions::DefaultJitOptions() { @@ -188,6 +189,9 @@ DefaultJitOptions::DefaultJitOptions() // Toggles whether unboxed plain objects can be created by the VM. SET_DEFAULT(disableUnboxedObjects, false); + + // Test whether wasm int64 / double NaN bits testing is enabled. + SET_DEFAULT(wasmTestMode, false); } bool diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h index 84ab7b9a25..6b3d8cbc48 100644 --- a/js/src/jit/JitOptions.h +++ b/js/src/jit/JitOptions.h @@ -66,6 +66,7 @@ struct DefaultJitOptions bool forceInlineCaches; bool limitScriptSize; bool osr; + bool wasmTestMode; uint32_t baselineWarmUpThreshold; uint32_t exceptionBailoutThreshold; uint32_t frequentBailoutThreshold; diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index f5d0f0f754..f1449d93a5 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2361,6 +2361,17 @@ LIRGenerator::visitUnarySharedStub(MUnarySharedStub* ins) assignSafepoint(lir, ins); } +void +LIRGenerator::visitNullarySharedStub(MNullarySharedStub* ins) +{ + MOZ_ASSERT(ins->type() == MIRType_Value); + + LNullarySharedStub* lir = new(alloc()) LNullarySharedStub(); + + defineSharedStubReturn(lir, ins); + assignSafepoint(lir, ins); +} + void LIRGenerator::visitLambda(MLambda* ins) { @@ -2494,6 +2505,17 @@ LIRGenerator::visitAsmThrowUnreachable(MAsmThrowUnreachable* ins) add(new(alloc()) LAsmThrowUnreachable, ins); } +void +LIRGenerator::visitAsmReinterpret(MAsmReinterpret* ins) +{ + if (ins->type() == MIRType_Int64) + defineInt64(new(alloc()) LAsmReinterpretToI64(useRegisterAtStart(ins->input())), ins); + else if (ins->input()->type() == MIRType_Int64) + define(new(alloc()) LAsmReinterpretFromI64(useInt64RegisterAtStart(ins->input())), ins); + else + define(new(alloc()) LAsmReinterpret(useRegisterAtStart(ins->input())), ins); +} + void LIRGenerator::visitStoreSlot(MStoreSlot* ins) { @@ -4208,13 +4230,6 @@ LIRGenerator::visitRecompileCheck(MRecompileCheck* ins) assignSafepoint(lir, ins); } -void -LIRGenerator::visitMemoryBarrier(MMemoryBarrier* ins) -{ - LMemoryBarrier* lir = new(alloc()) LMemoryBarrier(ins->type()); - add(lir, ins); -} - void LIRGenerator::visitSimdBox(MSimdBox* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 4eae84c2e7..4e72400632 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -171,6 +171,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitStringReplace(MStringReplace* ins); void visitBinarySharedStub(MBinarySharedStub* ins); void visitUnarySharedStub(MUnarySharedStub* ins); + void visitNullarySharedStub(MNullarySharedStub* ins); void visitLambda(MLambda* ins); void visitLambdaArrow(MLambdaArrow* ins); void visitKeepAliveObject(MKeepAliveObject* ins); @@ -186,6 +187,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitInterruptCheck(MInterruptCheck* ins); void visitAsmJSInterruptCheck(MAsmJSInterruptCheck* ins); void visitAsmThrowUnreachable(MAsmThrowUnreachable* ins); + void visitAsmReinterpret(MAsmReinterpret* ins); void visitStoreSlot(MStoreSlot* ins); void visitFilterTypeSet(MFilterTypeSet* ins); void visitTypeBarrier(MTypeBarrier* ins); @@ -287,7 +289,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitGetDOMProperty(MGetDOMProperty* ins); void visitGetDOMMember(MGetDOMMember* ins); void visitRecompileCheck(MRecompileCheck* ins); - void visitMemoryBarrier(MMemoryBarrier* ins); void visitSimdBox(MSimdBox* ins); void visitSimdUnbox(MSimdUnbox* ins); void visitSimdExtractElement(MSimdExtractElement* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 3584cb55b7..a4ad908510 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -94,8 +94,6 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineAtomicsLoad(callInfo); case InlinableNative::AtomicsStore: return inlineAtomicsStore(callInfo); - case InlinableNative::AtomicsFence: - return inlineAtomicsFence(callInfo); case InlinableNative::AtomicsAdd: case InlinableNative::AtomicsSub: case InlinableNative::AtomicsAnd: @@ -503,23 +501,19 @@ IonBuilder::inlineArray(CallInfo& callInfo) callInfo.setImplicitlyUsedUnchecked(); - MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); - current->add(templateConst); - - MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst, - templateObject->group()->initialHeap(constraints()), pc); - current->add(ins); - current->push(ins); + if (!jsop_newarray(templateObject, initLength)) + return InliningStatus_Error; + MDefinition* array = current->peek(-1); if (callInfo.argc() >= 2) { JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject); for (uint32_t i = 0; i < initLength; i++) { MDefinition* value = callInfo.getArg(i); - if (!initializeArrayElement(ins, i, value, unboxedType, /* addResumePoint = */ false)) + if (!initializeArrayElement(array, i, value, unboxedType, /* addResumePoint = */ false)) return InliningStatus_Error; } - MInstruction* setLength = setInitializedLength(ins, unboxedType, initLength); + MInstruction* setLength = setInitializedLength(array, unboxedType, initLength); if (!resumeAfter(setLength)) return InliningStatus_Error; } @@ -2150,16 +2144,11 @@ IonBuilder::inlineObjectCreate(CallInfo& callInfo) callInfo.setImplicitlyUsedUnchecked(); - MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); - current->add(templateConst); - MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst, - templateObject->group()->initialHeap(constraints()), - MNewObject::ObjectCreate); - current->add(ins); - current->push(ins); - if (!resumeAfter(ins)) + bool emitted = false; + if (!newObjectTryTemplateObject(&emitted, templateObject)) return InliningStatus_Error; + MOZ_ASSERT(emitted); return InliningStatus_Inlined; } @@ -3069,30 +3058,6 @@ IonBuilder::inlineAtomicsStore(CallInfo& callInfo) return InliningStatus_Inlined; } -IonBuilder::InliningStatus -IonBuilder::inlineAtomicsFence(CallInfo& callInfo) -{ - if (callInfo.argc() != 0 || callInfo.constructing()) { - trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); - return InliningStatus_NotInlined; - } - - if (!JitSupportsAtomics()) - return InliningStatus_NotInlined; - - callInfo.setImplicitlyUsedUnchecked(); - - MMemoryBarrier* fence = MMemoryBarrier::New(alloc()); - current->add(fence); - pushConstant(UndefinedValue()); - - // Fences are considered effectful (they execute a memory barrier). - if (!resumeAfter(fence)) - return InliningStatus_Error; - - return InliningStatus_Inlined; -} - IonBuilder::InliningStatus IonBuilder::inlineAtomicsBinop(CallInfo& callInfo, InlinableNative target) { diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index ec9910999b..335027fbf1 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -351,6 +351,12 @@ MTest::New(TempAllocator& alloc, MDefinition* ins, MBasicBlock* ifTrue, MBasicBl return new(alloc) MTest(ins, ifTrue, ifFalse); } +MTest* +MTest::NewAsm(TempAllocator& alloc, MDefinition* ins, MBasicBlock* ifFalse) +{ + return new(alloc) MTest(ins, nullptr, ifFalse); +} + void MTest::cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints) { @@ -1384,8 +1390,12 @@ void MControlInstruction::printOpcode(GenericPrinter& out) const { MDefinition::printOpcode(out); - for (size_t j = 0; j < numSuccessors(); j++) - out.printf(" block%u", getSuccessor(j)->id()); + for (size_t j = 0; j < numSuccessors(); j++) { + if (getSuccessor(j)) + out.printf(" block%u", getSuccessor(j)->id()); + else + out.printf(" (null-to-be-patched)"); + } } void @@ -1830,6 +1840,12 @@ MGoto::New(TempAllocator& alloc, MBasicBlock* target) return new(alloc) MGoto(target); } +MGoto* +MGoto::NewAsm(TempAllocator& alloc) +{ + return new(alloc) MGoto(nullptr); +} + void MUnbox::printOpcode(GenericPrinter& out) const { @@ -4253,14 +4269,6 @@ MBeta::printOpcode(GenericPrinter& out) const comparison_->dump(out); } -bool -MNewObject::shouldUseVM() const -{ - if (JSObject* obj = templateObject()) - return obj->is() && obj->as().hasDynamicSlots(); - return true; -} - bool MCreateThisWithTemplate::canRecoverOnBailout() const { @@ -4489,25 +4497,6 @@ MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t length, MCons } } -bool -MNewArray::shouldUseVM() const -{ - if (!templateObject()) - return true; - - if (templateObject()->is()) { - MOZ_ASSERT(templateObject()->as().capacity() >= length()); - return !templateObject()->as().hasInlineElements(); - } - - MOZ_ASSERT(length() <= NativeObject::MAX_DENSE_ELEMENTS_COUNT); - - size_t arraySlots = - gc::GetGCKindSlots(templateObject()->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER; - - return length() > arraySlots; -} - bool MLoadFixedSlot::mightAlias(const MDefinition* store) const { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 1e93587b4a..ca4ec0db02 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2813,6 +2813,11 @@ class MGoto INSTRUCTION_HEADER(Goto) static MGoto* New(TempAllocator& alloc, MBasicBlock* target); + // Factory for asm, which may patch the target later. + static MGoto* NewAsm(TempAllocator& alloc); + + static const size_t TargetIndex = 0; + MBasicBlock* target() { return getSuccessor(0); } @@ -2840,12 +2845,12 @@ class MTest { bool operandMightEmulateUndefined_; - MTest(MDefinition* ins, MBasicBlock* if_true, MBasicBlock* if_false) + MTest(MDefinition* ins, MBasicBlock* trueBranch, MBasicBlock* falseBranch) : operandMightEmulateUndefined_(true) { initOperand(0, ins); - setSuccessor(0, if_true); - setSuccessor(1, if_false); + setSuccessor(0, trueBranch); + setSuccessor(1, falseBranch); } public: @@ -2853,6 +2858,11 @@ class MTest static MTest* New(TempAllocator& alloc, MDefinition* ins, MBasicBlock* ifTrue, MBasicBlock* ifFalse); + // Factory for asm, which may patch the ifTrue branch later. + static MTest* NewAsm(TempAllocator& alloc, MDefinition* ins, MBasicBlock* ifFalse); + + static const size_t TrueBranchIndex = 0; + MDefinition* input() const { return getOperand(0); } @@ -3081,10 +3091,6 @@ class MNewArray return convertDoubleElements_; } - // Returns true if the code generator should call through to the - // VM rather than the fast path. - bool shouldUseVM() const; - // NewArray is marked as non-effectful because all our allocations are // either lazy when we are using "new Array(length)" or bounded by the // script or the stack size when we are using "new Array(...)" or "[...]" @@ -3203,7 +3209,7 @@ class MNewObject initialHeap_(initialHeap), mode_(mode) { - MOZ_ASSERT_IF(mode != ObjectLiteral, !shouldUseVM()); + MOZ_ASSERT_IF(mode != ObjectLiteral, templateObject()); setResultType(MIRType_Object); if (JSObject* obj = templateObject()) @@ -3228,10 +3234,6 @@ class MNewObject return new(alloc) MNewObject(constraints, templateConst, initialHeap, mode); } - // Returns true if the code generator should call through to the - // VM rather than the fast path. - bool shouldUseVM() const; - Mode mode() const { return mode_; } @@ -7583,6 +7585,24 @@ class MUnarySharedStub } }; +class MNullarySharedStub + : public MNullaryInstruction +{ + explicit MNullarySharedStub() + : MNullaryInstruction() + { + setResultType(MIRType_Value); + } + + public: + INSTRUCTION_HEADER(NullarySharedStub) + + static MNullarySharedStub* New(TempAllocator& alloc) + { + return new(alloc) MNullarySharedStub(); + } +}; + // Check the current frame for over-recursion past the global stack limit. class MCheckOverRecursed : public MNullaryInstruction @@ -9968,7 +9988,21 @@ class MArrayJoin MDefinition* foldsTo(TempAllocator& alloc) override; }; -// See comments above MMemoryBarrier, below. +// All barriered operations - MCompareExchangeTypedArrayElement, +// MExchangeTypedArrayElement, and MAtomicTypedArrayElementBinop, as +// well as MLoadUnboxedScalar and MStoreUnboxedScalar when they are +// marked as requiring a memory barrer - have the following +// attributes: +// +// - Not movable +// - Not removable +// - Not congruent with any other instruction +// - Effectful (they alias every TypedArray store) +// +// The intended effect of those constraints is to prevent all loads +// and stores preceding the barriered operation from being moved to +// after the barriered operation, and vice versa, and to prevent the +// barriered operation from being removed or hoisted. enum MemoryBarrierRequirement { @@ -9976,7 +10010,7 @@ enum MemoryBarrierRequirement DoesRequireMemoryBarrier }; -// Also see comments above MMemoryBarrier, below. +// Also see comments at MMemoryBarrierRequirement, above. // Load an unboxed scalar value from a typed array or other object. class MLoadUnboxedScalar @@ -13647,49 +13681,6 @@ class MRecompileCheck : public MNullaryInstruction } }; -// All barriered operations - MMemoryBarrier, MCompareExchangeTypedArrayElement, -// MExchangeTypedArrayElement, and MAtomicTypedArrayElementBinop, as well as -// MLoadUnboxedScalar and MStoreUnboxedScalar when they are marked as requiring -// a memory barrer - have the following attributes: -// -// - Not movable -// - Not removable -// - Not congruent with any other instruction -// - Effectful (they alias every TypedArray store) -// -// The intended effect of those constraints is to prevent all loads -// and stores preceding the barriered operation from being moved to -// after the barriered operation, and vice versa, and to prevent the -// barriered operation from being removed or hoisted. - -class MMemoryBarrier - : public MNullaryInstruction -{ - // The type is a combination of the memory barrier types in AtomicOp.h. - const MemoryBarrierBits type_; - - explicit MMemoryBarrier(MemoryBarrierBits type) - : type_(type) - { - MOZ_ASSERT((type_ & ~MembarAllbits) == MembarNobits); - setGuard(); // Not removable - } - - public: - INSTRUCTION_HEADER(MemoryBarrier) - - static MMemoryBarrier* New(TempAllocator& alloc, MemoryBarrierBits type = MembarFull) { - return new(alloc) MMemoryBarrier(type); - } - MemoryBarrierBits type() const { - return type_; - } - - AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::UnboxedElement); - } -}; - class MAtomicIsLockFree : public MUnaryInstruction, public ConvertToInt32Policy<0>::Data @@ -14549,6 +14540,42 @@ class MAsmSelect ALLOW_CLONE(MAsmSelect) }; +class MAsmReinterpret + : public MUnaryInstruction, + public NoTypePolicy::Data +{ + MAsmReinterpret(MDefinition* val, MIRType toType) + : MUnaryInstruction(val) + { + switch (val->type()) { + case MIRType_Int32: MOZ_ASSERT(toType == MIRType_Float32); break; + case MIRType_Float32: MOZ_ASSERT(toType == MIRType_Int32); break; + case MIRType_Double: MOZ_ASSERT(toType == MIRType_Int64); break; + case MIRType_Int64: MOZ_ASSERT(toType == MIRType_Double); break; + default: MOZ_CRASH("unexpected reinterpret conversion"); + } + setMovable(); + setResultType(toType); + } + + public: + INSTRUCTION_HEADER(AsmReinterpret) + + static MAsmReinterpret* New(TempAllocator& alloc, MDefinition* val, MIRType toType) + { + return new(alloc) MAsmReinterpret(val, toType); + } + + AliasSet getAliasSet() const override { + return AliasSet::None(); + } + bool congruentTo(const MDefinition* ins) const override { + return congruentIfOperandsEqual(ins); + } + + ALLOW_CLONE(MAsmReinterpret) +}; + class MUnknownValue : public MNullaryInstruction { protected: diff --git a/js/src/jit/MIRGenerator.h b/js/src/jit/MIRGenerator.h index e932dd6316..7681d6bd8a 100644 --- a/js/src/jit/MIRGenerator.h +++ b/js/src/jit/MIRGenerator.h @@ -135,19 +135,14 @@ class MIRGenerator return info_->compilingAsmJS(); } - uint32_t maxAsmJSStackArgBytes() const { + uint32_t wasmMaxStackArgBytes() const { MOZ_ASSERT(compilingAsmJS()); - return maxAsmJSStackArgBytes_; + return wasmMaxStackArgBytes_; } - uint32_t resetAsmJSMaxStackArgBytes() { + void initWasmMaxStackArgBytes(uint32_t n) { MOZ_ASSERT(compilingAsmJS()); - uint32_t old = maxAsmJSStackArgBytes_; - maxAsmJSStackArgBytes_ = 0; - return old; - } - void setAsmJSMaxStackArgBytes(uint32_t n) { - MOZ_ASSERT(compilingAsmJS()); - maxAsmJSStackArgBytes_ = n; + MOZ_ASSERT(wasmMaxStackArgBytes_ == 0); + wasmMaxStackArgBytes_ = n; } uint32_t minAsmJSHeapLength() const { return minAsmJSHeapLength_; @@ -189,7 +184,7 @@ class MIRGenerator mozilla::Atomic* pauseBuild_; mozilla::Atomic cancelBuild_; - uint32_t maxAsmJSStackArgBytes_; + uint32_t wasmMaxStackArgBytes_; bool performsCall_; bool usesSimd_; bool usesSimdCached_; diff --git a/js/src/jit/MIRGraph.cpp b/js/src/jit/MIRGraph.cpp index 0ac874ebbb..196a616d29 100644 --- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -31,7 +31,7 @@ MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOpti error_(false), pauseBuild_(nullptr), cancelBuild_(false), - maxAsmJSStackArgBytes_(0), + wasmMaxStackArgBytes_(0), performsCall_(false), usesSimd_(false), usesSimdCached_(false), diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 437100c1a2..ed834249a9 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -52,6 +52,7 @@ namespace jit { _(ReturnFromCtor) \ _(BinarySharedStub) \ _(UnarySharedStub) \ + _(NullarySharedStub) \ _(CheckOverRecursed) \ _(DefVar) \ _(DefLexical) \ @@ -279,9 +280,9 @@ namespace jit { _(AsmJSPassStackArg) \ _(AsmJSCall) \ _(AsmSelect) \ + _(AsmReinterpret) \ _(NewDerivedTypedObject) \ _(RecompileCheck) \ - _(MemoryBarrier) \ _(AsmJSCompareExchangeHeap) \ _(AsmJSAtomicExchangeHeap) \ _(AsmJSAtomicBinopHeap) \ diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index b023fd65be..579d4861c0 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -823,6 +823,13 @@ class MacroAssembler : public MacroAssemblerSpecific inline void branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, Label* label) DEFINED_ON(arm, arm64, mips_shared, x86, x64); + inline void branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) PER_ARCH; + + // Compare the value at |lhs| with the value at |rhs|. The scratch + // register *must not* be the base of |lhs| or |rhs|. + inline void branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch, + Label* label) PER_ARCH; + inline void branchPtr(Condition cond, Register lhs, Register rhs, Label* label) PER_SHARED_ARCH; inline void branchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH; inline void branchPtr(Condition cond, Register lhs, ImmPtr rhs, Label* label) PER_SHARED_ARCH; diff --git a/js/src/jit/RegisterSets.h b/js/src/jit/RegisterSets.h index 87912ed5d7..0437d249dd 100644 --- a/js/src/jit/RegisterSets.h +++ b/js/src/jit/RegisterSets.h @@ -1287,6 +1287,13 @@ class ABIArg MOZ_ASSERT(kind() == GPR); return Register::FromCode(u.gpr_); } + Register64 gpr64() const { +#ifdef JS_PUNBOX64 + return Register64(gpr()); +#else + MOZ_CRASH("NYI"); +#endif + } Register evenGpr() const { MOZ_ASSERT(isGeneralRegPair()); return Register::FromCode(u.gpr_); diff --git a/js/src/jit/Registers.h b/js/src/jit/Registers.h index 18ca85b2bf..300e25c6eb 100644 --- a/js/src/jit/Registers.h +++ b/js/src/jit/Registers.h @@ -46,6 +46,10 @@ struct Register { Register r = { Encoding(code) }; return r; } + static Register Invalid() { + Register r = { Encoding(Codes::Invalid) }; + return r; + } MOZ_CONSTEXPR Code code() const { return Code(reg_); } @@ -109,6 +113,10 @@ struct Register64 : reg(r) {} #else + explicit Register64(Register r) + : high(Register::Invalid()), low(Register::Invalid()) + {} + MOZ_CONSTEXPR Register64(Register h, Register l) : high(h), low(l) {} diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index b6581651e0..62ef2aee8f 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -8,6 +8,7 @@ #include "mozilla/Casting.h" #include "mozilla/DebugOnly.h" +#include "mozilla/IntegerPrintfMacros.h" #include "mozilla/SizePrintfMacros.h" #include "jslibmath.h" @@ -2177,10 +2178,10 @@ UpdateExistingGenerationalDOMProxyStub(ICGetProp_Fallback* stub, iter->toGetProp_CallDOMProxyWithGenerationNative(); if (updateStub->expandoAndGeneration() == expandoAndGeneration) { // Update generation - uint32_t generation = expandoAndGeneration->generation; + uint64_t generation = expandoAndGeneration->generation; JitSpew(JitSpew_BaselineIC, " Updating existing stub with generation, old value: %i, " - "new value: %i", updateStub->generation(), + "new value: %" PRIu64 "", updateStub->generation(), generation); updateStub->setGeneration(generation); return true; @@ -3574,9 +3575,9 @@ CheckDOMProxyExpandoDoesNotShadow(JSContext* cx, MacroAssembler& masm, Register masm.branchPrivatePtr(Assembler::NotEqual, expandoAddr, tempVal.scratchReg(), &failDOMProxyCheck); - masm.load32(*generationAddr, scratch); - masm.branch32(Assembler::NotEqual, - Address(tempVal.scratchReg(), offsetof(ExpandoAndGeneration, generation)), + masm.branch64(Assembler::NotEqual, + Address(tempVal.scratchReg(), ExpandoAndGeneration::offsetOfGeneration()), + *generationAddr, scratch, &failDOMProxyCheck); masm.loadValue(Address(tempVal.scratchReg(), 0), tempVal); @@ -3704,7 +3705,7 @@ ICGetPropCallDOMProxyNativeCompiler::getStub(ICStubSpace* space) Value expandoSlot = GetProxyExtra(proxy_, GetDOMProxyExpandoSlot()); RootedShape expandoShape(cx, nullptr); ExpandoAndGeneration* expandoAndGeneration; - int32_t generation; + uint64_t generation; Value expandoVal; if (kind == ICStub::GetProp_CallDOMProxyNative) { expandoVal = expandoSlot; @@ -4648,5 +4649,159 @@ ICUpdatedStub::addUpdateStubForValue(JSContext* cx, HandleScript outerScript, Ha return true; } +// +// NewArray_Fallback +// + +static bool +DoNewArray(JSContext* cx, void* payload, ICNewArray_Fallback* stub, uint32_t length, + MutableHandleValue res) +{ + SharedStubInfo info(cx, payload, stub->icEntry()); + + FallbackICSpew(cx, stub, "NewArray"); + + RootedObject obj(cx); + if (stub->templateObject()) { + RootedObject templateObject(cx, stub->templateObject()); + obj = NewArrayOperationWithTemplate(cx, templateObject); + if (!obj) + return false; + } else { + HandleScript script = info.script(); + jsbytecode* pc = info.pc(); + obj = NewArrayOperation(cx, script, pc, length); + if (!obj) + return false; + + if (obj && !obj->isSingleton() && !obj->group()->maybePreliminaryObjects()) { + JSObject* templateObject = NewArrayOperation(cx, script, pc, length, TenuredObject); + if (!templateObject) + return false; + stub->setTemplateObject(templateObject); + } + } + + res.setObject(*obj); + return true; +} + +typedef bool(*DoNewArrayFn)(JSContext*, void*, ICNewArray_Fallback*, uint32_t, + MutableHandleValue); +static const VMFunction DoNewArrayInfo = FunctionInfo(DoNewArray, TailCall); + +bool +ICNewArray_Fallback::Compiler::generateStubCode(MacroAssembler& masm) +{ + EmitRestoreTailCallReg(masm); + + masm.push(R0.scratchReg()); // length + masm.push(ICStubReg); // stub. + pushStubPayload(masm, R0.scratchReg()); + + return tailCallVM(DoNewArrayInfo, masm); +} + +// +// NewObject_Fallback +// + +// Unlike typical baseline IC stubs, the code for NewObject_WithTemplate is +// specialized for the template object being allocated. +static JitCode* +GenerateNewObjectWithTemplateCode(JSContext* cx, JSObject* templateObject) +{ + JitContext jctx(cx, nullptr); + MacroAssembler masm; +#ifdef JS_CODEGEN_ARM + masm.setSecondScratchReg(BaselineSecondScratchReg); +#endif + + Label failure; + Register objReg = R0.scratchReg(); + Register tempReg = R1.scratchReg(); + masm.movePtr(ImmGCPtr(templateObject->group()), tempReg); + masm.branchTest32(Assembler::NonZero, Address(tempReg, ObjectGroup::offsetOfFlags()), + Imm32(OBJECT_FLAG_PRE_TENURE), &failure); + masm.branchPtr(Assembler::NotEqual, AbsoluteAddress(cx->compartment()->addressOfMetadataBuilder()), + ImmWord(0), &failure); + masm.createGCObject(objReg, tempReg, templateObject, gc::DefaultHeap, &failure); + masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0); + + EmitReturnFromIC(masm); + masm.bind(&failure); + EmitStubGuardFailure(masm); + + Linker linker(masm); + AutoFlushICache afc("GenerateNewObjectWithTemplateCode"); + return linker.newCode(cx, BASELINE_CODE); +} + +static bool +DoNewObject(JSContext* cx, void* payload, ICNewObject_Fallback* stub, MutableHandleValue res) +{ + SharedStubInfo info(cx, payload, stub->icEntry()); + + FallbackICSpew(cx, stub, "NewObject"); + + RootedObject obj(cx); + + RootedObject templateObject(cx, stub->templateObject()); + if (templateObject) { + MOZ_ASSERT(!templateObject->group()->maybePreliminaryObjects()); + obj = NewObjectOperationWithTemplate(cx, templateObject); + } else { + HandleScript script = info.script(); + jsbytecode* pc = info.pc(); + obj = NewObjectOperation(cx, script, pc); + + if (obj && !obj->isSingleton() && !obj->group()->maybePreliminaryObjects()) { + JSObject* templateObject = NewObjectOperation(cx, script, pc, TenuredObject); + if (!templateObject) + return false; + + if (!stub->invalid() && + (templateObject->is() || + !templateObject->as().hasDynamicSlots())) + { + JitCode* code = GenerateNewObjectWithTemplateCode(cx, templateObject); + if (!code) + return false; + + ICStubSpace* space = + ICStubCompiler::StubSpaceForKind(ICStub::NewObject_WithTemplate, script, + ICStubCompiler::Engine::Baseline); + ICStub* templateStub = ICStub::New(cx, space, code); + if (!templateStub) + return false; + + stub->addNewStub(templateStub); + } + + stub->setTemplateObject(templateObject); + } + } + + if (!obj) + return false; + + res.setObject(*obj); + return true; +} + +typedef bool(*DoNewObjectFn)(JSContext*, void*, ICNewObject_Fallback*, MutableHandleValue); +static const VMFunction DoNewObjectInfo = FunctionInfo(DoNewObject, TailCall); + +bool +ICNewObject_Fallback::Compiler::generateStubCode(MacroAssembler& masm) +{ + EmitRestoreTailCallReg(masm); + + masm.push(ICStubReg); // stub. + pushStubPayload(masm, R0.scratchReg()); + + return tailCallVM(DoNewObjectInfo, masm); +} + } // namespace jit } // namespace js diff --git a/js/src/jit/SharedIC.h b/js/src/jit/SharedIC.h index e4e2a03024..e6fca65a34 100644 --- a/js/src/jit/SharedIC.h +++ b/js/src/jit/SharedIC.h @@ -3103,13 +3103,13 @@ class ICGetProp_CallDOMProxyWithGenerationNative : public ICGetPropCallDOMProxyN { protected: ExpandoAndGeneration* expandoAndGeneration_; - uint32_t generation_; + uint64_t generation_; public: ICGetProp_CallDOMProxyWithGenerationNative(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape, ExpandoAndGeneration* expandoAndGeneration, - uint32_t generation, Shape* expandoShape, + uint64_t generation, Shape* expandoShape, JSObject* holder, Shape* holderShape, JSFunction* getter, uint32_t pcOffset) : ICGetPropCallDOMProxyNativeStub(ICStub::GetProp_CallDOMProxyWithGenerationNative, @@ -3127,11 +3127,11 @@ class ICGetProp_CallDOMProxyWithGenerationNative : public ICGetPropCallDOMProxyN void* expandoAndGeneration() const { return expandoAndGeneration_; } - uint32_t generation() const { + uint64_t generation() const { return generation_; } - void setGeneration(uint32_t value) { + void setGeneration(uint64_t value) { generation_ = value; } @@ -3285,6 +3285,103 @@ class ICGetProp_ArgumentsCallee : public ICMonitoredStub }; }; +// JSOP_NEWARRAY +// JSOP_NEWINIT + +class ICNewArray_Fallback : public ICFallbackStub +{ + friend class ICStubSpace; + + HeapPtrObject templateObject_; + + // The group used for objects created here is always available, even if the + // template object itself is not. + HeapPtrObjectGroup templateGroup_; + + ICNewArray_Fallback(JitCode* stubCode, ObjectGroup* templateGroup) + : ICFallbackStub(ICStub::NewArray_Fallback, stubCode), + templateObject_(nullptr), templateGroup_(templateGroup) + {} + + public: + class Compiler : public ICStubCompiler { + RootedObjectGroup templateGroup; + bool generateStubCode(MacroAssembler& masm); + + public: + Compiler(JSContext* cx, ObjectGroup* templateGroup, Engine engine) + : ICStubCompiler(cx, ICStub::NewArray_Fallback, engine), + templateGroup(cx, templateGroup) + {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode(), templateGroup); + } + }; + + HeapPtrObject& templateObject() { + return templateObject_; + } + + void setTemplateObject(JSObject* obj) { + MOZ_ASSERT(obj->group() == templateGroup()); + templateObject_ = obj; + } + + HeapPtrObjectGroup& templateGroup() { + return templateGroup_; + } + + void setTemplateGroup(ObjectGroup* group) { + templateObject_ = nullptr; + templateGroup_ = group; + } +}; + +// JSOP_NEWOBJECT + +class ICNewObject_Fallback : public ICFallbackStub +{ + friend class ICStubSpace; + + HeapPtrObject templateObject_; + + explicit ICNewObject_Fallback(JitCode* stubCode) + : ICFallbackStub(ICStub::NewObject_Fallback, stubCode), templateObject_(nullptr) + {} + + public: + class Compiler : public ICStubCompiler { + bool generateStubCode(MacroAssembler& masm); + + public: + explicit Compiler(JSContext* cx, Engine engine) + : ICStubCompiler(cx, ICStub::NewObject_Fallback, engine) + {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode()); + } + }; + + HeapPtrObject& templateObject() { + return templateObject_; + } + + void setTemplateObject(JSObject* obj) { + templateObject_ = obj; + } +}; + +class ICNewObject_WithTemplate : public ICStub +{ + friend class ICStubSpace; + + explicit ICNewObject_WithTemplate(JitCode* stubCode) + : ICStub(ICStub::NewObject_WithTemplate, stubCode) + {} +}; + } // namespace jit } // namespace js diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index 2ab605134f..d7d7b47145 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -118,6 +118,7 @@ static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = r2; static MOZ_CONSTEXPR_VAR Register StackPointer = sp; static MOZ_CONSTEXPR_VAR Register FramePointer = InvalidReg; static MOZ_CONSTEXPR_VAR Register ReturnReg = r0; +static MOZ_CONSTEXPR_VAR Register64 ReturnReg64(InvalidReg, InvalidReg); static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32Reg = { FloatRegisters::d0, VFPRegister::Single }; static MOZ_CONSTEXPR_VAR FloatRegister ReturnDoubleReg = { FloatRegisters::d0, VFPRegister::Double}; static MOZ_CONSTEXPR_VAR FloatRegister ReturnSimd128Reg = InvalidFloatReg; @@ -1136,6 +1137,18 @@ class Operand } }; +inline Imm32 +Imm64::firstHalf() const +{ + return low(); +} + +inline Imm32 +Imm64::secondHalf() const +{ + return hi(); +} + void PatchJump(CodeLocationJump& jump_, CodeLocationLabel label, ReprotectCode reprotect = DontReprotect); diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index f8d73005f4..8e2c53effb 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -2129,6 +2129,32 @@ CodeGeneratorARM::visitAsmSelect(LAsmSelect* ins) MOZ_CRASH("unhandled type in visitAsmSelect!"); } +void +CodeGeneratorARM::visitAsmReinterpret(LAsmReinterpret* lir) +{ + MOZ_ASSERT(gen->compilingAsmJS()); + MAsmReinterpret* ins = lir->mir(); + + MIRType to = ins->type(); + DebugOnly from = ins->input()->type(); + + switch (to) { + case MIRType_Int32: + MOZ_ASSERT(from == MIRType_Float32); + masm.ma_vxfer(ToFloatRegister(lir->input()), ToRegister(lir->output())); + break; + case MIRType_Float32: + MOZ_ASSERT(from == MIRType_Int32); + masm.ma_vxfer(ToRegister(lir->input()), ToFloatRegister(lir->output())); + break; + case MIRType_Double: + case MIRType_Int64: + MOZ_CRASH("not handled by this LIR opcode"); + default: + MOZ_CRASH("unexpected AsmReinterpret"); + } +} + void CodeGeneratorARM::visitAsmJSCall(LAsmJSCall* ins) { diff --git a/js/src/jit/arm/CodeGenerator-arm.h b/js/src/jit/arm/CodeGenerator-arm.h index 4b0115a13a..325ed49ab6 100644 --- a/js/src/jit/arm/CodeGenerator-arm.h +++ b/js/src/jit/arm/CodeGenerator-arm.h @@ -201,6 +201,7 @@ class CodeGeneratorARM : public CodeGeneratorShared void visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir); void visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir); void visitAsmSelect(LAsmSelect* ins); + void visitAsmReinterpret(LAsmReinterpret* ins); void visitAsmJSCall(LAsmJSCall* ins); void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins); void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins); diff --git a/js/src/jit/arm/MacroAssembler-arm-inl.h b/js/src/jit/arm/MacroAssembler-arm-inl.h index 21925cf0a3..1203c3be62 100644 --- a/js/src/jit/arm/MacroAssembler-arm-inl.h +++ b/js/src/jit/arm/MacroAssembler-arm-inl.h @@ -515,6 +515,32 @@ MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, L branch32(cond, scratch, rhs, label); } +void +MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) +{ + MOZ_ASSERT(cond == Assembler::NotEqual, + "other condition codes not supported"); + + branch32(cond, lhs, val.firstHalf(), label); + branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), val.secondHalf(), label); +} + +void +MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch, + Label* label) +{ + MOZ_ASSERT(cond == Assembler::NotEqual, + "other condition codes not supported"); + MOZ_ASSERT(lhs.base != scratch); + MOZ_ASSERT(rhs.base != scratch); + + load32(rhs, scratch); + branch32(cond, lhs, scratch, label); + + load32(Address(rhs.base, rhs.offset + sizeof(uint32_t)), scratch); + branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), scratch, label); +} + void MacroAssembler::branchPtr(Condition cond, Register lhs, Register rhs, Label* label) { diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index 3202b9c59d..8b0ec13965 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -1420,11 +1420,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM ma_b(handleNotAnInt, Above); } - void memIntToValue(Address Source, Address Dest) { - load32(Source, lr); - storeValue(JSVAL_TYPE_INT32, lr, Dest); - } - void lea(Operand addr, Register dest) { ma_add(addr.baseReg(), Imm32(addr.disp()), dest); } diff --git a/js/src/jit/arm64/Assembler-arm64.h b/js/src/jit/arm64/Assembler-arm64.h index cddc976aa8..b6213443b5 100644 --- a/js/src/jit/arm64/Assembler-arm64.h +++ b/js/src/jit/arm64/Assembler-arm64.h @@ -55,6 +55,7 @@ static constexpr Register CallTempReg5 = { Registers::x14 }; static constexpr Register PreBarrierReg = { Registers::x1 }; static constexpr Register ReturnReg = { Registers::x0 }; +static constexpr Register64 ReturnReg64(ReturnReg); static constexpr Register JSReturnReg = { Registers::x2 }; static constexpr Register FramePointer = { Registers::fp }; static constexpr Register ZeroRegister = { Registers::sp }; @@ -502,6 +503,18 @@ GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register* out) return true; } +inline Imm32 +Imm64::firstHalf() const +{ + return low(); +} + +inline Imm32 +Imm64::secondHalf() const +{ + return hi(); +} + void PatchJump(CodeLocationJump& jump_, CodeLocationLabel label, ReprotectCode reprotect = DontReprotect); diff --git a/js/src/jit/arm64/MacroAssembler-arm64-inl.h b/js/src/jit/arm64/MacroAssembler-arm64-inl.h index f0314757e5..2ccc1d4fda 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h +++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h @@ -560,6 +560,27 @@ MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, L branch32(cond, Address(scratch, 0), rhs, label); } +void +MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) +{ + MOZ_ASSERT(cond == Assembler::NotEqual, + "other condition codes not supported"); + + branchPtr(cond, lhs, ImmWord(val.value), label); +} + +void +MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch, + Label* label) +{ + MOZ_ASSERT(cond == Assembler::NotEqual, + "other condition codes not supported"); + MOZ_ASSERT(lhs.base != scratch); + MOZ_ASSERT(rhs.base != scratch); + + loadPtr(rhs, scratch); + branchPtr(cond, lhs, scratch, label); +} void MacroAssembler::branchPtr(Condition cond, Register lhs, Register rhs, Label* label) { diff --git a/js/src/jit/arm64/MacroAssembler-arm64.h b/js/src/jit/arm64/MacroAssembler-arm64.h index c04b2ea647..7637257abe 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.h +++ b/js/src/jit/arm64/MacroAssembler-arm64.h @@ -1916,15 +1916,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler MOZ_CRASH("patchAsmJSGlobalAccess"); } - void memIntToValue(const Address& src, const Address& dest) { - vixl::UseScratchRegisterScope temps(this); - const Register scratch = temps.AcquireX().asUnsized(); - MOZ_ASSERT(scratch != src.base); - MOZ_ASSERT(scratch != dest.base); - load32(src, scratch); - storeValue(JSVAL_TYPE_INT32, scratch, dest); - } - void profilerEnterFrame(Register framePtr, Register scratch) { AbsoluteAddress activation(GetJitContext()->runtime->addressOfProfilingActivation()); loadPtr(activation, scratch); diff --git a/js/src/jit/mips-shared/Assembler-mips-shared.h b/js/src/jit/mips-shared/Assembler-mips-shared.h index c208475312..96eb4b5edc 100644 --- a/js/src/jit/mips-shared/Assembler-mips-shared.h +++ b/js/src/jit/mips-shared/Assembler-mips-shared.h @@ -636,6 +636,18 @@ class Operand } }; +inline Imm32 +Imm64::firstHalf() const +{ + return low(); +} + +inline Imm32 +Imm64::secondHalf() const +{ + return hi(); +} + void PatchJump(CodeLocationJump& jump_, CodeLocationLabel label, ReprotectCode reprotect = DontReprotect); diff --git a/js/src/jit/mips32/MacroAssembler-mips32-inl.h b/js/src/jit/mips32/MacroAssembler-mips32-inl.h index 9bb6b828c7..18c4b18bb9 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h +++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h @@ -268,6 +268,32 @@ MacroAssembler::rshift64(Imm32 imm, Register64 dest) // =============================================================== // Branch functions +void +MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) +{ + MOZ_ASSERT(cond == Assembler::NotEqual, + "other condition codes not supported"); + + branch32(cond, lhs, val.firstHalf(), label); + branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), val.secondHalf(), label); +} + +void +MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch, + Label* label) +{ + MOZ_ASSERT(cond == Assembler::NotEqual, + "other condition codes not supported"); + MOZ_ASSERT(lhs.base != scratch); + MOZ_ASSERT(rhs.base != scratch); + + load32(rhs, scratch); + branch32(cond, lhs, scratch, label); + + load32(Address(rhs.base, rhs.offset + sizeof(uint32_t)), scratch); + branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), scratch, label); +} + void MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label) { diff --git a/js/src/jit/mips64/MacroAssembler-mips64-inl.h b/js/src/jit/mips64/MacroAssembler-mips64-inl.h index 4d5991a96a..4fc044f559 100644 --- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h +++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h @@ -207,6 +207,28 @@ MacroAssembler::rshift64(Imm32 imm, Register64 dest) // =============================================================== // Branch functions +void +MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) +{ + MOZ_ASSERT(cond == Assembler::NotEqual, + "other condition codes not supported"); + + branchPtr(cond, lhs, ImmWord(val.value), label); +} + +void +MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch, + Label* label) +{ + MOZ_ASSERT(cond == Assembler::NotEqual, + "other condition codes not supported"); + MOZ_ASSERT(lhs.base != scratch); + MOZ_ASSERT(rhs.base != scratch); + + loadPtr(rhs, scratch); + branchPtr(cond, lhs, scratch, label); +} + void MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label) { diff --git a/js/src/jit/mips64/MacroAssembler-mips64.h b/js/src/jit/mips64/MacroAssembler-mips64.h index ebfde1e351..6ac001f4f4 100644 --- a/js/src/jit/mips64/MacroAssembler-mips64.h +++ b/js/src/jit/mips64/MacroAssembler-mips64.h @@ -1002,11 +1002,6 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64 return CodeOffset(nextOffset().getOffset()); } - void memIntToValue(Address Source, Address Dest) { - load32(Source, ScratchRegister); - storeValue(JSVAL_TYPE_INT32, ScratchRegister, Dest); - } - void lea(Operand addr, Register dest) { ma_daddu(dest, addr.baseReg(), Imm32(addr.disp())); } diff --git a/js/src/jit/none/MacroAssembler-none.h b/js/src/jit/none/MacroAssembler-none.h index b5a402f687..bd7e11a7d5 100644 --- a/js/src/jit/none/MacroAssembler-none.h +++ b/js/src/jit/none/MacroAssembler-none.h @@ -11,7 +11,6 @@ #include "jit/MoveResolver.h" #include "jit/shared/Assembler-shared.h" - namespace js { namespace jit { @@ -72,8 +71,10 @@ static MOZ_CONSTEXPR_VAR Register JSReturnReg = { Registers::invalid_reg }; #if defined(JS_NUNBOX32) static MOZ_CONSTEXPR_VAR ValueOperand JSReturnOperand(InvalidReg, InvalidReg); +static MOZ_CONSTEXPR_VAR Register64 ReturnReg64(InvalidReg, InvalidReg); #elif defined(JS_PUNBOX64) static MOZ_CONSTEXPR_VAR ValueOperand JSReturnOperand(InvalidReg); +static MOZ_CONSTEXPR_VAR Register64 ReturnReg64(InvalidReg); #else #error "Bad architecture" #endif @@ -396,7 +397,6 @@ class MacroAssemblerNone : public Assembler bool buildOOLFakeExitFrame(void*) { MOZ_CRASH(); } void loadWasmActivation(Register) { MOZ_CRASH(); } void loadAsmJSHeapRegisterFromGlobalData() { MOZ_CRASH(); } - void memIntToValue(Address, Address) { MOZ_CRASH(); } void setPrinter(Sprinter*) { MOZ_CRASH(); } Operand ToPayload(Operand base) { MOZ_CRASH(); } diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index 2ef46b5a31..f6a320d15f 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -133,6 +133,17 @@ struct Imm64 explicit Imm64(uint64_t value) : value(value) { } + + Imm32 low() const { + return Imm32(int32_t(value)); + } + + Imm32 hi() const { + return Imm32(int32_t(value >> 32)); + } + + inline Imm32 firstHalf() const; + inline Imm32 secondHalf() const; }; #ifdef DEBUG diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index fd31d58962..71e83c42d7 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -83,7 +83,7 @@ CodeGeneratorShared::CodeGeneratorShared(MIRGenerator* gen, LIRGraph* graph, Mac // regular array where all slots are sizeof(Value), it maintains the max // argument stack depth separately. MOZ_ASSERT(graph->argumentSlotCount() == 0); - frameDepth_ += gen->maxAsmJSStackArgBytes(); + frameDepth_ += gen->wasmMaxStackArgBytes(); if (gen->usesSimd()) { // If the function uses any SIMD then we may need to insert padding diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index d25de585bd..6e7ae212ff 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -891,7 +891,7 @@ class LNewArray : public LInstructionHelper<1, 0, 1> } const char* extraName() const { - return mir()->shouldUseVM() ? "VMCall" : nullptr; + return mir()->templateObject() ? "VMCall" : nullptr; } const LDefinition* temp() { @@ -953,7 +953,7 @@ class LNewObject : public LInstructionHelper<1, 0, 1> } const char* extraName() const { - return mir()->shouldUseVM() ? "VMCall" : nullptr; + return mir()->templateObject() ? "VMCall" : nullptr; } const LDefinition* temp() { @@ -1256,6 +1256,47 @@ class LAsmThrowUnreachable : public LInstructionHelper<0, 0, 0> { } }; +template +class LAsmReinterpretBase : public LInstructionHelper +{ + typedef LInstructionHelper Base; + + public: + const LAllocation* input() { + return Base::getOperand(0); + } + MAsmReinterpret* mir() const { + return Base::mir_->toAsmReinterpret(); + } +}; + +class LAsmReinterpret : public LAsmReinterpretBase<1, 1> +{ + public: + LIR_HEADER(AsmReinterpret); + explicit LAsmReinterpret(const LAllocation& input) { + setOperand(0, input); + } +}; + +class LAsmReinterpretFromI64 : public LAsmReinterpretBase<1, INT64_PIECES> +{ + public: + LIR_HEADER(AsmReinterpretFromI64); + explicit LAsmReinterpretFromI64(const LInt64Allocation& input) { + setInt64Operand(0, input); + } +}; + +class LAsmReinterpretToI64 : public LAsmReinterpretBase +{ + public: + LIR_HEADER(AsmReinterpretToI64); + explicit LAsmReinterpretToI64(const LAllocation& input) { + setOperand(0, input); + } +}; + class LInterruptCheck : public LInstructionHelper<0, 0, 0> { Label* oolEntry_; @@ -4472,6 +4513,16 @@ class LUnarySharedStub : public LCallInstructionHelper +{ + public: + LIR_HEADER(NullarySharedStub) + + const MNullarySharedStub* mir() const { + return mir_->toNullarySharedStub(); + } +}; + class LLambdaForSingleton : public LCallInstructionHelper<1, 1, 0> { public: @@ -7959,10 +8010,6 @@ class LMemoryBarrier : public LInstructionHelper<0, 0, 0> MemoryBarrierBits type() const { return type_; } - - const MMemoryBarrier* mir() const { - return mir_->toMemoryBarrier(); - } }; class LDebugger : public LCallInstructionHelper<0, 0, 2> diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index e6c78a9905..a65e08921e 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -215,6 +215,7 @@ _(Substr) \ _(BinarySharedStub) \ _(UnarySharedStub) \ + _(NullarySharedStub) \ _(Lambda) \ _(LambdaArrow) \ _(LambdaForSingleton) \ @@ -347,6 +348,9 @@ _(InterruptCheck) \ _(AsmJSInterruptCheck) \ _(AsmThrowUnreachable) \ + _(AsmReinterpret) \ + _(AsmReinterpretToI64) \ + _(AsmReinterpretFromI64) \ _(GetDOMProperty) \ _(GetDOMMemberV) \ _(GetDOMMemberT) \ diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h index 312fb61b0d..d9c3346ed3 100644 --- a/js/src/jit/shared/Lowering-shared-inl.h +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -196,7 +196,7 @@ LIRGeneratorShared::defineSharedStubReturn(LInstruction* lir, MDefinition* mir) { lir->setMir(mir); - MOZ_ASSERT(lir->isBinarySharedStub() || lir->isUnarySharedStub()); + MOZ_ASSERT(lir->isBinarySharedStub() || lir->isUnarySharedStub() || lir->isNullarySharedStub()); MOZ_ASSERT(mir->type() == MIRType_Value); uint32_t vreg = getVirtualRegister(); diff --git a/js/src/jit/x64/Assembler-x64.h b/js/src/jit/x64/Assembler-x64.h index 54a245f0ab..44e44579e0 100644 --- a/js/src/jit/x64/Assembler-x64.h +++ b/js/src/jit/x64/Assembler-x64.h @@ -83,6 +83,7 @@ struct ScratchRegisterScope : public AutoRegisterScope static MOZ_CONSTEXPR_VAR Register ReturnReg = rax; static MOZ_CONSTEXPR_VAR Register HeapReg = r15; +static MOZ_CONSTEXPR_VAR Register64 ReturnReg64(rax); static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32Reg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Single); static MOZ_CONSTEXPR_VAR FloatRegister ReturnDoubleReg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Double); static MOZ_CONSTEXPR_VAR FloatRegister ReturnSimd128Reg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Simd128); diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index a8848f98af..bff92eb479 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -492,6 +492,22 @@ CodeGeneratorX64::visitAsmSelectI64(LAsmSelectI64* lir) masm.cmovzq(falseExpr, out); } +void +CodeGeneratorX64::visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir) +{ + MOZ_ASSERT(lir->mir()->type() == MIRType_Double); + MOZ_ASSERT(lir->mir()->input()->type() == MIRType_Int64); + masm.vmovq(ToRegister(lir->input()), ToFloatRegister(lir->output())); +} + +void +CodeGeneratorX64::visitAsmReinterpretToI64(LAsmReinterpretToI64* lir) +{ + MOZ_ASSERT(lir->mir()->type() == MIRType_Int64); + MOZ_ASSERT(lir->mir()->input()->type() == MIRType_Double); + masm.vmovq(ToFloatRegister(lir->input()), ToRegister(lir->output())); +} + void CodeGeneratorX64::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir) { diff --git a/js/src/jit/x64/CodeGenerator-x64.h b/js/src/jit/x64/CodeGenerator-x64.h index b9384f5723..4ae961734f 100644 --- a/js/src/jit/x64/CodeGenerator-x64.h +++ b/js/src/jit/x64/CodeGenerator-x64.h @@ -73,6 +73,8 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins); void visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir); void visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir); + void visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir); + void visitAsmReinterpretToI64(LAsmReinterpretToI64* lir); }; typedef CodeGeneratorX64 CodeGeneratorSpecific; diff --git a/js/src/jit/x64/MacroAssembler-x64-inl.h b/js/src/jit/x64/MacroAssembler-x64-inl.h index 5dd9ea5148..cffef89d41 100644 --- a/js/src/jit/x64/MacroAssembler-x64-inl.h +++ b/js/src/jit/x64/MacroAssembler-x64-inl.h @@ -307,6 +307,28 @@ MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, L branch32(cond, Address(scratch, 0), rhs, label); } +void +MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) +{ + MOZ_ASSERT(cond == Assembler::NotEqual, + "other condition codes not supported"); + + branchPtr(cond, lhs, ImmWord(val.value), label); +} + +void +MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch, + Label* label) +{ + MOZ_ASSERT(cond == Assembler::NotEqual, + "other condition codes not supported"); + MOZ_ASSERT(lhs.base != scratch); + MOZ_ASSERT(rhs.base != scratch); + + loadPtr(rhs, scratch); + branchPtr(cond, lhs, scratch, label); +} + void MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label) { diff --git a/js/src/jit/x64/MacroAssembler-x64.h b/js/src/jit/x64/MacroAssembler-x64.h index 060f0e101f..3e66f466e3 100644 --- a/js/src/jit/x64/MacroAssembler-x64.h +++ b/js/src/jit/x64/MacroAssembler-x64.h @@ -942,11 +942,6 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared uint8_t* target = globalData + globalDataOffset; ((int32_t*)nextInsn)[-1] = target - nextInsn; } - void memIntToValue(Address Source, Address Dest) { - ScratchRegisterScope scratch(asMasm()); - load32(Source, scratch); - storeValue(JSVAL_TYPE_INT32, scratch, Dest); - } // Instrumentation for entering and leaving the profiler. void profilerEnterFrame(Register framePtr, Register scratch); diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h index af6835afda..968d58076a 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -173,6 +173,18 @@ class Operand } }; +inline Imm32 +Imm64::firstHalf() const +{ + return low(); +} + +inline Imm32 +Imm64::secondHalf() const +{ + return hi(); +} + class CPUInfo { public: diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index db90b5caa8..d56bb29adb 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -24,6 +24,7 @@ using namespace js; using namespace js::jit; using mozilla::Abs; +using mozilla::DebugOnly; using mozilla::FloatingPoint; using mozilla::FloorLog2; using mozilla::NegativeInfinity; @@ -341,6 +342,32 @@ CodeGeneratorX86Shared::visitAsmSelect(LAsmSelect* ins) return; } +void +CodeGeneratorX86Shared::visitAsmReinterpret(LAsmReinterpret* lir) +{ + MOZ_ASSERT(gen->compilingAsmJS()); + MAsmReinterpret* ins = lir->mir(); + + MIRType to = ins->type(); + DebugOnly from = ins->input()->type(); + + switch (to) { + case MIRType_Int32: + MOZ_ASSERT(from == MIRType_Float32); + masm.vmovd(ToFloatRegister(lir->input()), ToRegister(lir->output())); + break; + case MIRType_Float32: + MOZ_ASSERT(from == MIRType_Int32); + masm.vmovd(ToRegister(lir->input()), ToFloatRegister(lir->output())); + break; + case MIRType_Double: + case MIRType_Int64: + MOZ_CRASH("not handled by this LIR opcode"); + default: + MOZ_CRASH("unexpected AsmReinterpret"); + } +} + void CodeGeneratorX86Shared::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds* ool) { @@ -932,7 +959,7 @@ CodeGeneratorX86Shared::visitOutOfLineUndoALUOperation(OutOfLineUndoALUOperation LInstruction* ins = ool->ins(); Register reg = ToRegister(ins->getDef(0)); - mozilla::DebugOnly lhs = ins->getOperand(0); + DebugOnly lhs = ins->getOperand(0); LAllocation* rhs = ins->getOperand(1); MOZ_ASSERT(reg == ToRegister(lhs)); @@ -1216,7 +1243,7 @@ void CodeGeneratorX86Shared::visitDivPowTwoI(LDivPowTwoI* ins) { Register lhs = ToRegister(ins->numerator()); - mozilla::DebugOnly output = ToRegister(ins->output()); + DebugOnly output = ToRegister(ins->output()); int32_t shift = ins->shift(); bool negativeDivisor = ins->negativeDivisor(); diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index 6452312c15..e9188d348c 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -262,6 +262,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared virtual void visitUDivOrModConstant(LUDivOrModConstant *ins); virtual void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins); virtual void visitAsmSelect(LAsmSelect* ins); + virtual void visitAsmReinterpret(LAsmReinterpret* lir); virtual void visitMemoryBarrier(LMemoryBarrier* ins); virtual void visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* lir); virtual void visitAtomicTypedArrayElementBinopForEffect(LAtomicTypedArrayElementBinopForEffect* lir); diff --git a/js/src/jit/x86/Assembler-x86.h b/js/src/jit/x86/Assembler-x86.h index cd4307093a..54b098789f 100644 --- a/js/src/jit/x86/Assembler-x86.h +++ b/js/src/jit/x86/Assembler-x86.h @@ -44,6 +44,7 @@ static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = edx; static MOZ_CONSTEXPR_VAR Register StackPointer = esp; static MOZ_CONSTEXPR_VAR Register FramePointer = ebp; static MOZ_CONSTEXPR_VAR Register ReturnReg = eax; +static MOZ_CONSTEXPR_VAR Register64 ReturnReg64(InvalidReg, InvalidReg); static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32Reg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Single); static MOZ_CONSTEXPR_VAR FloatRegister ReturnDoubleReg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Double); static MOZ_CONSTEXPR_VAR FloatRegister ReturnSimd128Reg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Simd128); diff --git a/js/src/jit/x86/MacroAssembler-x86-inl.h b/js/src/jit/x86/MacroAssembler-x86-inl.h index b7b116c5d0..7cc6c2dd4d 100644 --- a/js/src/jit/x86/MacroAssembler-x86-inl.h +++ b/js/src/jit/x86/MacroAssembler-x86-inl.h @@ -308,6 +308,32 @@ MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, L j(cond, label); } +void +MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) +{ + MOZ_ASSERT(cond == Assembler::NotEqual, + "other condition codes not supported"); + + branch32(cond, lhs, val.firstHalf(), label); + branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), val.secondHalf(), label); +} + +void +MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch, + Label* label) +{ + MOZ_ASSERT(cond == Assembler::NotEqual, + "other condition codes not supported"); + MOZ_ASSERT(lhs.base != scratch); + MOZ_ASSERT(rhs.base != scratch); + + load32(rhs, scratch); + branch32(cond, lhs, scratch, label); + + load32(Address(rhs.base, rhs.offset + sizeof(uint32_t)), scratch); + branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), scratch, label); +} + void MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label) { diff --git a/js/src/js.msg b/js/src/js.msg index 9faf32f286..f6d0006b09 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -501,7 +501,6 @@ MSG_DEF(JSMSG_SYMBOL_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert symbol t MSG_DEF(JSMSG_ATOMICS_BAD_ARRAY, 0, JSEXN_TYPEERR, "invalid array type for the operation") MSG_DEF(JSMSG_ATOMICS_TOO_LONG, 0, JSEXN_RANGEERR, "timeout value too large") MSG_DEF(JSMSG_ATOMICS_WAIT_NOT_ALLOWED, 0, JSEXN_ERR, "waiting is not allowed on this thread") -MSG_DEF(JSMSG_ATOMICS_BAD_INDEX, 0, JSEXN_RANGEERR, "out-of-range index for atomic access") // XPConnect wrappers and DOM bindings MSG_DEF(JSMSG_CANT_SET_INTERPOSED, 1, JSEXN_TYPEERR, "unable to set interposed data property '{0}'") diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index c4c7f30d91..1f5772d59c 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -45,6 +45,7 @@ UNIFIED_SOURCES += [ 'testGCOutOfMemory.cpp', 'testGCStoreBufferRemoval.cpp', 'testGCUniqueId.cpp', + 'testGCWeakCache.cpp', 'testGCWeakRef.cpp', 'testGetPropertyDescriptor.cpp', 'testHashTable.cpp', diff --git a/js/src/jsapi-tests/testGCWeakCache.cpp b/js/src/jsapi-tests/testGCWeakCache.cpp new file mode 100644 index 0000000000..05b2cd0ede --- /dev/null +++ b/js/src/jsapi-tests/testGCWeakCache.cpp @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +* vim: set ts=8 sts=4 et sw=4 tw=99: +*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gc/Policy.h" +#include "js/GCHashTable.h" +#include "js/RootingAPI.h" +#include "js/SweepingAPI.h" + +#include "jsapi-tests/tests.h" + +// Exercise WeakCache. +BEGIN_TEST(testWeakCacheSet) +{ + // Create two objects tenured and two in the nursery. If zeal is on, + // this may fail and we'll get more tenured objects. That's fine: + // the test will continue to work, it will just not test as much. + JS::RootedObject tenured1(cx, JS_NewPlainObject(cx)); + JS::RootedObject tenured2(cx, JS_NewPlainObject(cx)); + JS_GC(rt); + JS::RootedObject nursery1(cx, JS_NewPlainObject(cx)); + JS::RootedObject nursery2(cx, JS_NewPlainObject(cx)); + + using ObjectSet = js::GCHashSet, js::MovableCellHasher>>; + using Cache = JS::WeakCache; + auto cache = Cache(JS::GetObjectZone(tenured1), ObjectSet(cx)); + CHECK(cache.init()); + + cache.put(tenured1); + cache.put(tenured2); + cache.put(nursery1); + cache.put(nursery2); + + // Verify relocation and that we don't sweep too aggressively. + JS_GC(rt); + CHECK(cache.has(tenured1)); + CHECK(cache.has(tenured2)); + CHECK(cache.has(nursery1)); + CHECK(cache.has(nursery2)); + + // Unroot two entries and verify that they get removed. + tenured2 = nursery2 = nullptr; + JS_GC(rt); + CHECK(cache.has(tenured1)); + CHECK(cache.has(nursery1)); + CHECK(cache.count() == 2); + + return true; +} +END_TEST(testWeakCacheSet) + +// Exercise WeakCache. +BEGIN_TEST(testWeakCacheMap) +{ + // Create two objects tenured and two in the nursery. If zeal is on, + // this may fail and we'll get more tenured objects. That's fine: + // the test will continue to work, it will just not test as much. + JS::RootedObject tenured1(cx, JS_NewPlainObject(cx)); + JS::RootedObject tenured2(cx, JS_NewPlainObject(cx)); + JS_GC(rt); + JS::RootedObject nursery1(cx, JS_NewPlainObject(cx)); + JS::RootedObject nursery2(cx, JS_NewPlainObject(cx)); + + using ObjectMap = js::GCHashMap, uint32_t, + js::MovableCellHasher>>; + using Cache = JS::WeakCache; + auto cache = Cache(JS::GetObjectZone(tenured1), ObjectMap(cx)); + CHECK(cache.init()); + + cache.put(tenured1, 1); + cache.put(tenured2, 2); + cache.put(nursery1, 3); + cache.put(nursery2, 4); + + JS_GC(rt); + CHECK(cache.has(tenured1)); + CHECK(cache.has(tenured2)); + CHECK(cache.has(nursery1)); + CHECK(cache.has(nursery2)); + + tenured2 = nursery2 = nullptr; + JS_GC(rt); + CHECK(cache.has(tenured1)); + CHECK(cache.has(nursery1)); + CHECK(cache.count() == 2); + + return true; +} +END_TEST(testWeakCacheMap) diff --git a/js/src/jsapi-tests/testGetPropertyDescriptor.cpp b/js/src/jsapi-tests/testGetPropertyDescriptor.cpp index ace3e6333a..6310eb6ca8 100644 --- a/js/src/jsapi-tests/testGetPropertyDescriptor.cpp +++ b/js/src/jsapi-tests/testGetPropertyDescriptor.cpp @@ -17,6 +17,24 @@ BEGIN_TEST(test_GetPropertyDescriptor) CHECK_EQUAL(desc.object(), obj); CHECK_SAME(desc.value(), JS::Int32Value(123)); + JS::RootedValue descValue(cx); + CHECK(JS::FromPropertyDescriptor(cx, desc, &descValue)); + CHECK(descValue.isObject()); + JS::RootedObject descObj(cx, &descValue.toObject()); + JS::RootedValue value(cx); + CHECK(JS_GetProperty(cx, descObj, "value", &value)); + CHECK_EQUAL(value.toInt32(), 123); + CHECK(JS_GetProperty(cx, descObj, "get", &value)); + CHECK(value.isUndefined()); + CHECK(JS_GetProperty(cx, descObj, "set", &value)); + CHECK(value.isUndefined()); + CHECK(JS_GetProperty(cx, descObj, "writable", &value)); + CHECK(value.isTrue()); + CHECK(JS_GetProperty(cx, descObj, "configurable", &value)); + CHECK(value.isTrue()); + CHECK(JS_GetProperty(cx, descObj, "enumerable", &value)); + CHECK(value.isTrue()); + CHECK(JS_GetPropertyDescriptor(cx, obj, "not-here", &desc)); CHECK_EQUAL(desc.object(), nullptr); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 1e58eaf34b..678b513a6c 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -6126,6 +6126,9 @@ JS_SetGlobalJitCompilerOption(JSRuntime* rt, JSJitCompilerOption opt, uint32_t v } jit::JitOptions.jumpThreshold = value; break; + case JSJITCOMPILER_WASM_TEST_MODE: + jit::JitOptions.wasmTestMode = !!value; + break; default: break; } @@ -6152,6 +6155,8 @@ JS_GetGlobalJitCompilerOption(JSRuntime* rt, JSJitCompilerOption opt) return rt->canUseOffthreadIonCompilation(); case JSJITCOMPILER_SIGNALS_ENABLE: return rt->canUseSignalHandlers(); + case JSJITCOMPILER_WASM_TEST_MODE: + return jit::JitOptions.wasmTestMode ? 1 : 0; default: break; } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 888f046e37..55133c02e2 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2846,6 +2846,16 @@ ObjectToCompletePropertyDescriptor(JSContext* cx, JS::HandleValue descriptor, JS::MutableHandle desc); +/* + * ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc). + * + * If desc.object() is null, then vp is set to undefined. + */ +extern JS_PUBLIC_API(bool) +FromPropertyDescriptor(JSContext* cx, + JS::Handle desc, + JS::MutableHandleValue vp); + } // namespace JS @@ -5521,7 +5531,8 @@ JS_SetOffthreadIonCompilationEnabled(JSRuntime* rt, bool enabled); Register(BASELINE_ENABLE, "baseline.enable") \ Register(OFFTHREAD_COMPILATION_ENABLE, "offthread-compilation.enable") \ Register(SIGNALS_ENABLE, "signals.enable") \ - Register(JUMP_THRESHOLD, "jump-threshold") + Register(JUMP_THRESHOLD, "jump-threshold") \ + Register(WASM_TEST_MODE, "wasm.test-mode") typedef enum JSJitCompilerOption { #define JIT_COMPILER_DECLARE(key, str) \ diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 34ec3edbe9..d4b6d26684 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -925,6 +925,27 @@ IsArraySpecies(JSContext* cx, HandleObject origArray) return IsSelfHostedFunctionWithName(getter, cx->names().ArraySpecies); } +/* static */ bool +ArraySpeciesCreate(JSContext* cx, HandleObject origArray, uint32_t length, MutableHandleObject arr) +{ + RootedId createId(cx, NameToId(cx->names().ArraySpeciesCreate)); + RootedFunction create(cx, JS::GetSelfHostedFunction(cx, "ArraySpeciesCreate", createId, 2)); + + FixedInvokeArgs<2> args(cx); + + args[0].setObject(*origArray); + args[1].set(NumberValue(length)); + + RootedValue callee(cx, ObjectValue(*create)); + RootedValue rval(cx); + if (!Call(cx, callee, UndefinedHandleValue, args, &rval)) + return false; + + MOZ_ASSERT(rval.isObject()); + arr.set(&rval.toObject()); + return true; +} + #if JS_HAS_TOSOURCE static bool @@ -3043,6 +3064,7 @@ static const JSFunctionSpec array_methods[] = { JS_SELF_HOSTED_SYM_FN(iterator, "ArrayValues", 0,0), JS_SELF_HOSTED_FN("entries", "ArrayEntries", 0,0), JS_SELF_HOSTED_FN("keys", "ArrayKeys", 0,0), + JS_SELF_HOSTED_FN("values", "ArrayValues", 0,0), /* ES7 additions */ JS_SELF_HOSTED_FN("includes", "ArrayIncludes", 2,0), @@ -3167,7 +3189,7 @@ CreateArrayPrototype(JSContext* cx, JSProtoKey key) static bool array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto) { - // Add Array.prototype[@@unscopables]. ECMA-262 6.0 22.1.3.31. + // Add Array.prototype[@@unscopables]. ECMA-262 draft (2016 Mar 19) 22.1.3.32. RootedObject unscopables(cx, NewObjectWithGivenProto(cx, nullptr, TenuredObject)); if (!unscopables) return false; diff --git a/js/src/jsarray.h b/js/src/jsarray.h index 5128842c18..cfba874f98 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -34,9 +34,6 @@ IdIsIndex(jsid id, uint32_t* indexp) return js::StringIsArrayIndex(JSID_TO_ATOM(id), indexp); } -extern JSObject* -InitArrayClass(JSContext* cx, js::HandleObject obj); - // The methods below only create dense boxed arrays. /* Create a dense array with no capacity allocated, length set to 0. */ diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index c6d50169d4..c515915d8e 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -133,6 +133,12 @@ JSRuntime::initializeAtoms(JSContext* cx) #undef COMMON_NAME_INFO #define COMMON_NAME_INFO(name, code, init, clasp) { js_##name##_str, sizeof(#name) - 1 }, JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO) +#undef COMMON_NAME_INFO +#define COMMON_NAME_INFO(name) { #name, sizeof(#name) - 1 }, + JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INFO) +#undef COMMON_NAME_INFO +#define COMMON_NAME_INFO(name) { "Symbol." #name, sizeof("Symbol." #name) - 1 }, + JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INFO) #undef COMMON_NAME_INFO }; diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 010a615ade..dd3aaf5dc8 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1172,7 +1172,7 @@ NukeCrossCompartmentWrappers(JSContext* cx, * * If DoesntShadowUnique is returned then the slot at listBaseExpandoSlot * should contain a private pointer to a ExpandoAndGeneration, which contains * a JS::Value that should either be undefined or point to an expando object, - * and a uint32 value. If that value changes then the IC for getting a + * and a uint64 value. If that value changes then the IC for getting a * property will be invalidated. * * If Shadows is returned, that means the property is an own property of the * proxy but doesn't live on the expando object. @@ -1201,7 +1201,7 @@ struct ExpandoAndGeneration { } JS::Heap expando; - uint32_t generation; + uint64_t generation; }; typedef enum DOMProxyShadowsResult { diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 71511c7c91..2aeda5b944 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1246,22 +1246,35 @@ js::fun_call(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - HandleValue fval = args.thisv(); - if (!IsCallable(fval)) { + HandleValue func = args.thisv(); + + // We don't need to do this -- Call would do it for us -- but the error + // message is *much* better if we do this here. (Without this, + // JSDVG_SEARCH_STACK tries to decompile |func| as if it were |this| in + // the scripted caller's frame -- so for example + // + // Function.prototype.call.call({}); + // + // would identify |{}| as |this| as being the result of evaluating + // |Function.prototype.call| and would conclude, "Function.prototype.call + // is not a function". Grotesque.) + if (!IsCallable(func)) { ReportIncompatibleMethod(cx, args, &JSFunction::class_); return false; } - args.setCallee(fval); - args.setThis(args.get(0)); + size_t argCount = args.length(); + if (argCount > 0) + argCount--; // strip off provided |this| - if (args.length() > 0) { - for (size_t i = 0; i < args.length() - 1; i++) - args[i].set(args[i + 1]); - args = CallArgsFromVp(args.length() - 1, vp); - } + InvokeArgs iargs(cx); + if (!iargs.init(argCount)) + return false; - return InternalInvoke(cx, args); + for (size_t i = 0; i < argCount; i++) + iargs[i].set(args[i + 1]); + + return Call(cx, func, args.get(0), iargs, args.rval()); } // ES5 15.3.4.3 @@ -1271,6 +1284,10 @@ js::fun_apply(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); // Step 1. + // + // Note that we must check callability here, not at actual call time, + // because extracting argument values from the provided arraylike might + // have side effects or throw an exception. HandleValue fval = args.thisv(); if (!IsCallable(fval)) { ReportIncompatibleMethod(cx, args, &JSFunction::class_); @@ -1294,9 +1311,6 @@ js::fun_apply(JSContext* cx, unsigned argc, Value* vp) if (!args2.init(iter.numActualArgs())) return false; - args2.setCallee(fval); - args2.setThis(args[0]); - // Steps 7-8. iter.unaliasedForEachActual(cx, CopyTo(args2.array())); } else { @@ -1323,21 +1337,13 @@ js::fun_apply(JSContext* cx, unsigned argc, Value* vp) if (!args2.init(length)) return false; - // Push fval, obj, and aobj's elements as args. - args2.setCallee(fval); - args2.setThis(args[0]); - // Steps 7-8. if (!GetElements(cx, aobj, length, args2.array())) return false; } // Step 9. - if (!Invoke(cx, args2)) - return false; - - args.rval().set(args2.rval()); - return true; + return Call(cx, fval, args[0], args2, args.rval()); } bool diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index ec52f168e1..a5f764af64 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2420,6 +2420,8 @@ GCRuntime::sweepZoneAfterCompacting(Zone* zone) sweepTypesAfterCompacting(zone); zone->sweepBreakpoints(fop); zone->sweepWeakMaps(); + for (auto* cache : zone->weakCaches_) + cache->sweep(); for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) { c->sweepInnerViews(); @@ -5131,6 +5133,9 @@ GCRuntime::beginSweepingZoneGroup() } zone->gcWeakRefs.clear(); + for (JS::WeakCache* cache : zone->weakCaches_) + cache->sweep(); + /* No need to look up any more weakmap keys from this zone group. */ AutoEnterOOMUnsafeRegion oomUnsafe; if (!zone->gcWeakKeys.clear()) diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 9611331ddb..9396e90495 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -497,9 +497,16 @@ GetCustomIterator(JSContext* cx, HandleObject obj, unsigned flags, MutableHandle ++sCustomIteratorCount; /* Otherwise call it and return that object. */ - Value arg = BooleanValue((flags & JSITER_FOREACH) == 0); - if (!Invoke(cx, ObjectValue(*obj), rval, 1, &arg, &rval)) - return false; + { + FixedInvokeArgs<1> args(cx); + + args[0].setBoolean((flags & JSITER_FOREACH) == 0); + + RootedValue thisv(cx, ObjectValue(*obj)); + if (!js::Call(cx, rval, thisv, args, &rval)) + return false; + } + if (rval.isPrimitive()) { // Ignore the stack when throwing. We can't tell whether we were // supposed to skip over a new.target or not. @@ -1378,7 +1385,12 @@ js::IteratorMore(JSContext* cx, HandleObject iterobj, MutableHandleValue rval) if (!GetProperty(cx, iterobj, iterobj, cx->names().next, rval)) return false; - if (!Invoke(cx, ObjectValue(*iterobj), rval, 0, nullptr, rval)) { + // Call the .next method. Fall through to the error-handling cases in the + // unlikely event that either one of the fallible operations performed + // during the call process fails. + FixedInvokeArgs<0> args(cx); + RootedValue iterval(cx, ObjectValue(*iterobj)); + if (!js::Call(cx, rval, iterval, args, rval)) { // Check for StopIteration. if (!cx->isExceptionPending()) return false; @@ -1390,7 +1402,6 @@ js::IteratorMore(JSContext* cx, HandleObject iterobj, MutableHandleValue rval) cx->clearPendingException(); rval.setMagic(JS_NO_ITER_VALUE); - return true; } return true; diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 285d5dfa57..4c95d20a8c 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -1748,6 +1748,55 @@ js::ToLengthClamped(JSContext*, HandleValue, uint32_t*, bool*); template bool js::ToLengthClamped(ExclusiveContext*, HandleValue, uint32_t*, bool*); +bool +js::ToIntegerIndex(JSContext* cx, JS::HandleValue v, uint64_t* index) +{ + // Fast common case. + if (v.isInt32()) { + int32_t i = v.toInt32(); + if (i >= 0) { + *index = i; + return true; + } + } + + // Slow case. Use ToNumber() to coerce. This may throw a TypeError. + double d; + if (!ToNumber(cx, v, &d)) + return false; + + // Check that |d| is an integer in the valid range. + // + // Not all floating point integers fit in the range of a uint64_t, so we + // need a rough range check before the real range check in our caller. We + // could limit indexes to UINT64_MAX, but this would mean that our callers + // have to be very careful about integer overflow. The contiguous integer + // floating point numbers end at 2^53, so make that our upper limit. If we + // ever support arrays with more than 2^53 elements, this will need to + // change. + // + // Reject infinities, NaNs, and numbers outside the contiguous integer range + // with a RangeError. + + // Write relation so NaNs throw a RangeError. + if (!(0 <= d && d <= (uint64_t(1) << 53))) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX); + return false; + } + + // Check that d is an integer, throw a RangeError if not. + // Note that this conversion could invoke undefined behaviour without the + // range check above. + uint64_t i(d); + if (d != double(i)) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX); + return false; + } + + *index = i; + return true; +} + template bool js_strtod(ExclusiveContext* cx, const CharT* begin, const CharT* end, const CharT** dEnd, diff --git a/js/src/jsnum.h b/js/src/jsnum.h index f94bf085b3..ee5d4b9fe7 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -275,6 +275,26 @@ ToInteger(JSContext* cx, HandleValue v, double* dp) template bool ToLengthClamped(T* cx, HandleValue v, uint32_t* out, bool* overflow); +/* Convert and range check an index value as for DataView, SIMD, and Atomics + * operations, eg ES7 24.2.1.1, DataView's GetViewValue(): + * + * 1. numericIndex = ToNumber(argument) (may throw TypeError) + * 2. intIndex = ToInteger(numericIndex) + * 3. if intIndex != numericIndex throw RangeError + * + * This function additionally bounds the range to the non-negative contiguous + * integers: + * + * 4. if intIndex < 0 or intIndex > 2^53 throw RangeError + * + * Return true and set |*index| to the integer value if |argument| is a valid + * array index argument. Otherwise report an TypeError or RangeError and return + * false. + * + * The returned index will always be in the range 0 <= *index <= 2^53. + */ +bool ToIntegerIndex(JSContext* cx, JS::HandleValue v, uint64_t* index); + inline bool SafeAdd(int32_t one, int32_t two, int32_t* res) { diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index a339f64e63..0555371510 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -108,9 +108,13 @@ js::InformalValueTypeName(const Value& v) } // ES6 draft rev37 6.2.4.4 FromPropertyDescriptor -bool -js::FromPropertyDescriptor(JSContext* cx, Handle desc, MutableHandleValue vp) +JS_PUBLIC_API(bool) +JS::FromPropertyDescriptor(JSContext* cx, Handle desc, MutableHandleValue vp) { + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, desc); + // Step 1. if (!desc.object()) { vp.setUndefined(); @@ -1123,7 +1127,7 @@ CopyProxyObject(JSContext* cx, Handle from, Handle t } RootedValue v(cx); - for (size_t n = 0; n < PROXY_EXTRA_SLOTS; n++) { + for (size_t n = 0; n < js::detail::PROXY_EXTRA_SLOTS; n++) { v = GetProxyExtra(from, n); if (!cx->compartment()->wrap(cx, &v)) return false; @@ -3959,14 +3963,13 @@ js::SpeciesConstructor(JSContext* cx, HandleObject obj, HandleValue defaultCtor, RootedValue func(cx); if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), shName, shName, 2, &func)) return false; - InvokeArgs args(cx); - if (!args.init(2)) - return false; - args.setCallee(func); - args.setThis(UndefinedValue()); + + FixedInvokeArgs<2> args(cx); + args[0].setObject(*obj); args[1].set(defaultCtor); - if (!Invoke(cx, args)) + + if (!Call(cx, func, UndefinedHandleValue, args, pctor)) return false; pctor.set(args.rval()); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index de0f15577a..0975daad02 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1250,18 +1250,10 @@ bool GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp); /* - * ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc). - * - * If desc.object() is null, then vp is set to undefined. - */ -extern bool -FromPropertyDescriptor(JSContext* cx, Handle desc, MutableHandleValue vp); - -/* - * Like FromPropertyDescriptor, but ignore desc.object() and always set vp + * Like JS::FromPropertyDescriptor, but ignore desc.object() and always set vp * to an object on success. * - * Use FromPropertyDescriptor for getOwnPropertyDescriptor, since desc.object() + * Use JS::FromPropertyDescriptor for getOwnPropertyDescriptor, since desc.object() * is used to indicate whether a result was found or not. Use this instead for * defineProperty: it would be senseless to define a "missing" property. */ diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 5743d21a8a..f2bb74debe 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -337,8 +337,16 @@ JSObject::create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::Initi MOZ_ASSERT_IF(!group->clasp()->isNative(), shape->slotSpan() == 0); const js::Class* clasp = group->clasp(); - size_t nDynamicSlots = - js::NativeObject::dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp); + + size_t nDynamicSlots = 0; + if (group->clasp()->isNative()) { + nDynamicSlots = js::NativeObject::dynamicSlotsCount(shape->numFixedSlots(), + shape->slotSpan(), clasp); + } else if (group->clasp()->isProxy()) { + // Proxy objects overlay the |slots| field with a ProxyValueArray. + MOZ_ASSERT(sizeof(js::detail::ProxyValueArray) % sizeof(js::HeapSlot) == 0); + nDynamicSlots = sizeof(js::detail::ProxyValueArray) / sizeof(js::HeapSlot); + } JSObject* obj = js::Allocate(cx, kind, nDynamicSlots, heap, clasp); if (!obj) diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index a5fb93f1b0..9f51da4959 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3836,15 +3836,13 @@ CallIsStringOptimizable(JSContext* cx, const char* name, bool* result) if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), propName, propName, 0, &funcVal)) return false; - InvokeArgs args(cx); - if (!args.init(0)) - return false; - args.setCallee(funcVal); - args.setThis(UndefinedValue()); - if (!Invoke(cx, args)) + FixedInvokeArgs<0> args(cx); + + RootedValue rval(cx); + if (!Call(cx, funcVal, UndefinedHandleValue, args, &rval)) return false; - *result = args.rval().toBoolean(); + *result = rval.toBoolean(); return true; } #endif diff --git a/js/src/moz.build b/js/src/moz.build index b1d67212f8..1b44c49586 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -33,7 +33,7 @@ with Files('jit/**'): for gcfile in ['jsgc*', 'devtools/rootAnalysis', 'devtools/gc-ubench', 'devtools/gctrace']: with Files(gcfile): BUG_COMPONENT = component_gc -for header in ('GCAnnotations.h', 'GCAPI.h', 'HeapAPI.h', 'RootingAPI.h', 'SliceBudget.h', 'TraceKind.h', 'TracingAPI.h', 'WeakMapPtr.h'): +for header in ('GCAnnotations.h', 'GCAPI.h', 'HeapAPI.h', 'RootingAPI.h', 'SliceBudget.h', 'SweepingAPI.h', 'TraceKind.h', 'TracingAPI.h', 'WeakMapPtr.h'): with Files('../public/' + header): BUG_COMPONENT = component_gc @@ -134,6 +134,7 @@ EXPORTS.js += [ '../public/RootingAPI.h', '../public/SliceBudget.h', '../public/StructuredClone.h', + '../public/SweepingAPI.h', '../public/TraceKind.h', '../public/TracingAPI.h', '../public/TrackedOptimizationInfo.h', diff --git a/js/src/proxy/DirectProxyHandler.cpp b/js/src/proxy/DirectProxyHandler.cpp index da59c773b2..683b389e0f 100644 --- a/js/src/proxy/DirectProxyHandler.cpp +++ b/js/src/proxy/DirectProxyHandler.cpp @@ -74,7 +74,12 @@ DirectProxyHandler::call(JSContext* cx, HandleObject proxy, const CallArgs& args { assertEnteredPolicy(cx, proxy, JSID_VOID, CALL); RootedValue target(cx, proxy->as().private_()); - return Invoke(cx, args.thisv(), target, args.length(), args.array(), args.rval()); + + InvokeArgs iargs(cx); + if (!FillArgumentsFromArraylike(cx, iargs, args)) + return false; + + return js::Call(cx, target, args.thisv(), iargs, args.rval()); } bool diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index f0361d1345..876645cef3 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -653,7 +653,7 @@ js::proxy_Finalize(FreeOp* fop, JSObject* obj) MOZ_ASSERT(obj->is()); obj->as().handler()->finalize(fop, obj); - js_free(GetProxyDataLayout(obj)->values); + js_free(detail::GetProxyDataLayout(obj)->values); } void diff --git a/js/src/proxy/ScriptedDirectProxyHandler.cpp b/js/src/proxy/ScriptedDirectProxyHandler.cpp index 0ece17e630..1d28d0f8e4 100644 --- a/js/src/proxy/ScriptedDirectProxyHandler.cpp +++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp @@ -18,21 +18,23 @@ using namespace js; using JS::IsArrayAnswer; using mozilla::ArrayLength; -// ES6 (5 April 2014) ValidateAndApplyPropertyDescriptor(O, P, Extensible, Desc, Current) -// Since we are actually performing 9.1.6.2 IsCompatiblePropertyDescriptor(Extensible, Desc, -// Current), some parameters are omitted. +// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 +// 9.1.6.2 IsCompatiblePropertyDescriptor. BUT that method just calls +// 9.1.6.3 ValidateAndApplyPropertyDescriptor with two additional constant +// arguments. Therefore step numbering is from the latter method, and +// resulting dead code has been removed. static bool -ValidatePropertyDescriptor(JSContext* cx, bool extensible, Handle desc, - Handle current, bool* bp) +IsCompatiblePropertyDescriptor(JSContext* cx, bool extensible, Handle desc, + Handle current, bool* bp) { - // step 2 + // Step 2. if (!current.object()) { - // Since |O| is always undefined, substeps c and d fall away. + // Step 2a-b,e. As |O| is always undefined, steps 2c-d fall away. *bp = extensible; return true; } - // step 3 + // Step 3. if (!desc.hasValue() && !desc.hasWritable() && !desc.hasGetterObject() && !desc.hasSetterObject() && !desc.hasEnumerable() && !desc.hasConfigurable()) @@ -41,7 +43,7 @@ ValidatePropertyDescriptor(JSContext* cx, bool extensible, Handleas().extra(ScriptedDirectProxyHandler::HANDLER_EXTRA).toObjectOrNull(); } -// ES7 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 7.3.9 GetMethod, +// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 7.3.9 GetMethod, // reimplemented for proxy handler trap-getting to produce better error // messages. static bool @@ -204,7 +209,7 @@ ScriptedDirectProxyHandler::setImmutablePrototype(JSContext* cx, HandleObject pr return SetImmutablePrototype(cx, target, succeeded); } -// ES6 draft rev 32 (2 Feb 2015) 9.5.4 Proxy.[[PreventExtensions]]() +// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.4 Proxy.[[PreventExtensions]]() bool ScriptedDirectProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result) const @@ -218,65 +223,76 @@ ScriptedDirectProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy, // Step 4. RootedObject target(cx, proxy->as().target()); + MOZ_ASSERT(target); - // Steps 5-6. + // Step 5. RootedValue trap(cx); if (!GetProxyTrap(cx, handler, cx->names().preventExtensions, &trap)) return false; - // Step 7. + // Step 6. if (trap.isUndefined()) return PreventExtensions(cx, target, result); - // Steps 8-9. - Value argv[] = { - ObjectValue(*target) - }; - RootedValue trapResult(cx); - if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) - return false; - - // Steps 10-11. - if (ToBoolean(trapResult)) { - bool extensible; - if (!IsExtensible(cx, target, &extensible)) + // Step 7. + bool booleanTrapResult; + { + Value argv[] = { + ObjectValue(*target) + }; + RootedValue trapResult(cx); + if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) return false; - if (extensible) { + + booleanTrapResult = ToBoolean(trapResult); + } + + // Step 8. + if (booleanTrapResult) { + // Step 8a. + bool targetIsExtensible; + if (!IsExtensible(cx, target, &targetIsExtensible)) + return false; + + if (targetIsExtensible) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE); return false; } + + // Step 9. return result.succeed(); } + + // Also step 9. return result.fail(JSMSG_PROXY_PREVENTEXTENSIONS_RETURNED_FALSE); } -// ES6 (5 April, 2014) 9.5.3 Proxy.[[IsExtensible]]() +// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.3 Proxy.[[IsExtensible]]() bool ScriptedDirectProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const { - // step 1 + // Steps 1-3. RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 2 if (!handler) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } - // step 3 + // Step 4. RootedObject target(cx, proxy->as().target()); + MOZ_ASSERT(target); - // step 4-5 + // Step 5. RootedValue trap(cx); if (!GetProxyTrap(cx, handler, cx->names().isExtensible, &trap)) return false; - // step 6 + // Step 6. if (trap.isUndefined()) return IsExtensible(cx, target, extensible); - // step 7, 9 + // Step 7. Value argv[] = { ObjectValue(*target) }; @@ -284,52 +300,50 @@ ScriptedDirectProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, bool if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) return false; - // step 8 bool booleanTrapResult = ToBoolean(trapResult); - // step 10-11 + // Steps 8. bool targetResult; if (!IsExtensible(cx, target, &targetResult)) return false; - // step 12 + // Step 9. if (targetResult != booleanTrapResult) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_EXTENSIBILITY); return false; } - // step 13 + // Step 10. *extensible = booleanTrapResult; return true; } -// ES6 (5 April 2014) 9.5.5 Proxy.[[GetOwnProperty]](P) +// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.5 Proxy.[[GetOwnProperty]](P) bool ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, MutableHandle desc) const { - // step 2 + // Steps 2-4. RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 3 if (!handler) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } - // step 4 + // Step 5. RootedObject target(cx, proxy->as().target()); + MOZ_ASSERT(target); - // step 5-6 + // Step 6. RootedValue trap(cx); if (!GetProxyTrap(cx, handler, cx->names().getOwnPropertyDescriptor, &trap)) return false; - // step 7 + // Step 7. if (trap.isUndefined()) return GetOwnPropertyDescriptor(cx, target, id, desc); - // step 8-9 + // Step 8. RootedValue propKey(cx); if (!IdToStringOrSymbol(cx, id, &propKey)) return false; @@ -342,70 +356,72 @@ ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) return false; - // step 10 + // Step 9. if (!trapResult.isUndefined() && !trapResult.isObject()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_GETOWN_OBJORUNDEF); return false; } - //step 11-12 + // Step 10. Rooted targetDesc(cx); if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc)) return false; - // step 13 + // Step 11. if (trapResult.isUndefined()) { - // substep a + // Step 11a. if (!targetDesc.object()) { desc.object().set(nullptr); return true; } - // substep b + // Step 11b. if (!targetDesc.configurable()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE); return false; } - // substep c-e + // Steps 11c-d. bool extensibleTarget; if (!IsExtensible(cx, target, &extensibleTarget)) return false; + + // Step 11e. if (!extensibleTarget) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE); return false; } - // substep f + // Step 11f. desc.object().set(nullptr); return true; } - // step 14-15 + // Step 12. bool extensibleTarget; if (!IsExtensible(cx, target, &extensibleTarget)) return false; - // step 16-17 + // Step 13. Rooted resultDesc(cx); if (!ToPropertyDescriptor(cx, trapResult, true, &resultDesc)) return false; - // step 18 + // Step 14. CompletePropertyDescriptor(&resultDesc); - // step 19 + // Step 15. bool valid; - if (!ValidatePropertyDescriptor(cx, extensibleTarget, resultDesc, targetDesc, &valid)) + if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, resultDesc, targetDesc, &valid)) return false; - // step 20 + // Step 16. if (!valid) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_INVALID); return false; } - // step 21 + // Step 17. if (!resultDesc.configurable()) { if (!targetDesc.object()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NE_AS_NC); @@ -418,43 +434,44 @@ ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject } } - // step 22 + // Step 18. desc.set(resultDesc); desc.object().set(proxy); return true; } -// ES6 draft rev 31 (15 Jan 2015) 9.5.6 Proxy.[[DefineOwnProperty]](P, Desc) +// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.6 Proxy.[[DefineOwnProperty]](P, Desc) bool ScriptedDirectProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id, Handle desc, ObjectOpResult& result) const { - // steps 2-4 + // Steps 2-4. RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); if (!handler) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } - // step 5 + // Step 5. RootedObject target(cx, proxy->as().target()); + MOZ_ASSERT(target); - // steps 6-7 + // Step 6. RootedValue trap(cx); if (!GetProxyTrap(cx, handler, cx->names().defineProperty, &trap)) return false; - // step 8 + // Step 7. if (trap.isUndefined()) return DefineProperty(cx, target, id, desc, result); - // step 9 + // Step 8. RootedValue descObj(cx); if (!FromPropertyDescriptorToObject(cx, desc, &descObj)) return false; - // steps 10-11 + // Step 9. RootedValue propKey(cx); if (!IdToStringOrSymbol(cx, id, &propKey)) return false; @@ -468,115 +485,124 @@ ScriptedDirectProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, Ha if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) return false; - // step 12 + // Step 10. if (!ToBoolean(trapResult)) return result.fail(JSMSG_PROXY_DEFINE_RETURNED_FALSE); - // step 13-14 + // Step 11. Rooted targetDesc(cx); if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc)) return false; - // step 15-16 + // Step 12. bool extensibleTarget; if (!IsExtensible(cx, target, &extensibleTarget)) return false; - // step 17-18 + // Steps 13-14. bool settingConfigFalse = desc.hasConfigurable() && !desc.configurable(); + + // Steps 15-16. if (!targetDesc.object()) { - // step 19.a + // Step 15a. if (!extensibleTarget) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NEW); return false; } - // step 19.b + + // Step 15b. if (settingConfigFalse) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NE_AS_NC); return false; } } else { - // step 20 + // Steps 16a-b. bool valid; - if (!ValidatePropertyDescriptor(cx, extensibleTarget, desc, targetDesc, &valid)) + if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, desc, targetDesc, &valid)) return false; + if (!valid || (settingConfigFalse && targetDesc.configurable())) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_INVALID); return false; } } - // step 21 + // Step 17. return result.succeed(); } -// ES6 7.3.17 But elementTypes is is fixed to symbol/string. +// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 +// 7.3.17 CreateListFromArrayLike with elementTypes fixed to symbol/string. static bool CreateFilteredListFromArrayLike(JSContext* cx, HandleValue v, AutoIdVector& props) { - // Step 3. + // Step 2. RootedObject obj(cx, NonNullObject(cx, v)); if (!obj) return false; - // Steps 4-5. + // Step 3. uint32_t len; if (!GetLengthProperty(cx, obj, &len)) return false; - // Steps 6-8. + // Steps 4-6. RootedValue next(cx); RootedId id(cx); - for (uint32_t index = 0; index < len; index++) { + uint32_t index = 0; + while (index < len) { + // Steps 6a-b. if (!GetElement(cx, obj, obj, index, &next)) return false; + // Step 6c. if (!next.isString() && !next.isSymbol()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ONWKEYS_STR_SYM); return false; } - // Unobservable for strings/symbols. if (!ValueToId(cx, next, &id)) return false; + // Step 6d. if (!props.append(id)) return false; + + // Step 6e. + index++; } - // Step 9. + // Step 7. return true; } -// ES6 9.5.12 Proxy.[[OwnPropertyKeys]]() +// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.11 Proxy.[[OwnPropertyKeys]]() bool ScriptedDirectProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const { - // Step 1. + // Steps 1-3. RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // Step 2. if (!handler) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } - // Step 3. Superfluous assertion. // Step 4. RootedObject target(cx, proxy->as().target()); + MOZ_ASSERT(target); - // Steps 5-6. + // Step 5. RootedValue trap(cx); if (!GetProxyTrap(cx, handler, cx->names().ownKeys, &trap)) return false; - // Step 7. + // Step 6. if (trap.isUndefined()) return GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props); - // Step 8. + // Step 7. Value argv[] = { ObjectValue(*target) }; @@ -584,35 +610,33 @@ ScriptedDirectProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResultArray)) return false; - // Steps 9-10. + // Step 8. AutoIdVector trapResult(cx); if (!CreateFilteredListFromArrayLike(cx, trapResultArray, trapResult)) return false; - // Steps 11-12. + // Step 9. bool extensibleTarget; if (!IsExtensible(cx, target, &extensibleTarget)) return false; - // Steps 13-14. + // Steps 10-11. AutoIdVector targetKeys(cx); if (!GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &targetKeys)) return false; - // Step 15. Superfluous assertion. - - // Steps 16-17. + // Steps 12-13. AutoIdVector targetConfigurableKeys(cx); AutoIdVector targetNonconfigurableKeys(cx); - // Step 18. + // Step 14. Rooted desc(cx); for (size_t i = 0; i < targetKeys.length(); ++i) { - // Steps a-b. + // Step 14a. if (!GetOwnPropertyDescriptor(cx, target, targetKeys[i], &desc)) return false; - // Steps c-d. + // Steps 14b-c. if (desc.object() && !desc.configurable()) { if (!targetNonconfigurableKeys.append(targetKeys[i])) return false; @@ -622,18 +646,19 @@ ScriptedDirectProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, } } - // Step 19. + // Step 15. if (extensibleTarget && targetNonconfigurableKeys.empty()) return props.appendAll(trapResult); - // Step 20. + // Step 16. AutoIdVector uncheckedResultKeys(cx); if (!uncheckedResultKeys.appendAll(trapResult)) return false; - // Step 21. + // Step 17. + RootedId key(cx); for (size_t i = 0; i < targetNonconfigurableKeys.length(); ++i) { - RootedId key(cx, targetNonconfigurableKeys[i]); + key = targetNonconfigurableKeys[i]; MOZ_ASSERT(key != JSID_VOID); bool found = false; @@ -651,13 +676,13 @@ ScriptedDirectProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, } } - // Step 22. + // Step 18. if (extensibleTarget) return props.appendAll(trapResult); - // Step 23. + // Step 19. for (size_t i = 0; i < targetConfigurableKeys.length(); ++i) { - RootedId key(cx, targetConfigurableKeys[i]); + key = targetConfigurableKeys[i]; MOZ_ASSERT(key != JSID_VOID); bool found = false; @@ -675,7 +700,7 @@ ScriptedDirectProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, } } - // Step 24. + // Step 20. for (size_t i = 0; i < uncheckedResultKeys.length(); ++i) { if (uncheckedResultKeys[i].get() != JSID_VOID) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW); @@ -683,94 +708,98 @@ ScriptedDirectProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, } } - // Step 25. + // Step 21. return props.appendAll(trapResult); } -// ES6 draft rev 32 (2 Feb 2014) 9.5.10 Proxy.[[Delete]](P) +// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.10 Proxy.[[Delete]](P) bool ScriptedDirectProxyHandler::delete_(JSContext* cx, HandleObject proxy, HandleId id, ObjectOpResult& result) const { - // step 2 + // Steps 2-4. RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 3 if (!handler) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } - // steps 4-5 + // Step 5. RootedObject target(cx, proxy->as().target()); + MOZ_ASSERT(target); - // steps 6-7 + // Step 6. RootedValue trap(cx); if (!GetProxyTrap(cx, handler, cx->names().deleteProperty, &trap)) return false; - // step 8 + // Step 7. if (trap.isUndefined()) return DeleteProperty(cx, target, id, result); - // steps 9-10 - RootedValue value(cx); - if (!IdToStringOrSymbol(cx, id, &value)) - return false; - Value argv[] = { - ObjectValue(*target), - value - }; - RootedValue trapResult(cx); - if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) - return false; + // Step 8. + bool booleanTrapResult; + { + RootedValue value(cx); + if (!IdToStringOrSymbol(cx, id, &value)) + return false; - // step 11 - if (!ToBoolean(trapResult)) + Value argv[] = { + ObjectValue(*target), + value + }; + RootedValue trapResult(cx); + if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) + return false; + + booleanTrapResult = ToBoolean(trapResult); + } + + // Step 9. + if (!booleanTrapResult) return result.fail(JSMSG_PROXY_DELETE_RETURNED_FALSE); - // steps 12-13 + // Step 10. Rooted desc(cx); if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) return false; - // step 14-15 + // Step 12. if (desc.object() && !desc.configurable()) { RootedValue v(cx, IdToValue(id)); ReportValueError(cx, JSMSG_CANT_DELETE, JSDVG_IGNORE_STACK, v, nullptr); return false; } - // step 16 + // Steps 11,13. return result.succeed(); } -// ES6 (22 May, 2014) 9.5.7 Proxy.[[HasProperty]](P) +// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.7 Proxy.[[HasProperty]](P) bool ScriptedDirectProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const { - // step 2 + // Steps 2-4. RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 3 if (!handler) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } - // step 4 + // Step 5. RootedObject target(cx, proxy->as().target()); + MOZ_ASSERT(target); - // step 5-6 + // Step 6. RootedValue trap(cx); if (!GetProxyTrap(cx, handler, cx->names().has, &trap)) return false; - // step 7 + // Step 7. if (trap.isUndefined()) return HasProperty(cx, target, id, bp); - // step 8,10 + // Step 8. RootedValue value(cx); if (!IdToStringOrSymbol(cx, id, &value)) return false; @@ -782,24 +811,29 @@ ScriptedDirectProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) return false; - // step 9 - bool success = ToBoolean(trapResult); + bool booleanTrapResult = ToBoolean(trapResult); - // step 11 - if (!success) { + // Step 9. + if (!booleanTrapResult) { + // Step 9a. Rooted desc(cx); if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) return false; + // Step 9b. if (desc.object()) { + // Step 9b(i). if (!desc.configurable()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE); return false; } + // Step 9b(ii). bool extensible; if (!IsExtensible(cx, target, &extensible)) return false; + + // Step 9b(iii). if (!extensible) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE); return false; @@ -807,38 +841,37 @@ ScriptedDirectProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, } } - // step 12 - *bp = success; + // Step 10. + *bp = booleanTrapResult; return true; } -// ES6 (22 May, 2014) 9.5.8 Proxy.[[GetP]](P, Receiver) +// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.8 Proxy.[[GetP]](P, Receiver) bool ScriptedDirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id, MutableHandleValue vp) const { - // step 2 + // Steps 2-4. RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 3 if (!handler) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } - // step 4 + // Step 5. RootedObject target(cx, proxy->as().target()); + MOZ_ASSERT(target); - // step 5-6 + // Steps 6. RootedValue trap(cx); if (!GetProxyTrap(cx, handler, cx->names().get, &trap)) return false; - // step 7 + // Step 7. if (trap.isUndefined()) return GetProperty(cx, target, receiver, id, vp); - // step 8-9 + // Step 8. RootedValue value(cx); if (!IdToStringOrSymbol(cx, id, &value)) return false; @@ -851,13 +884,14 @@ ScriptedDirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue r if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) return false; - // step 10-11 + // Step 9. Rooted desc(cx); if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) return false; - // step 12 + // Step 10. if (desc.object()) { + // Step 10a. if (desc.isDataDescriptor() && !desc.configurable() && !desc.writable()) { bool same; if (!SameValue(cx, trapResult, desc.value(), &same)) @@ -868,6 +902,7 @@ ScriptedDirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue r } } + // Step 10b. if (desc.isAccessorDescriptor() && !desc.configurable() && desc.getterObject() == nullptr) { if (!trapResult.isUndefined()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MUST_REPORT_UNDEFINED); @@ -876,34 +911,37 @@ ScriptedDirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue r } } - // step 13 + // Step 11. vp.set(trapResult); return true; } -// ES6 draft rev 32 (2015 Feb 2) 9.5.9 Proxy.[[Set]](P, V, Receiver) +// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.9 Proxy.[[Set]](P, V, Receiver) bool ScriptedDirectProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver, ObjectOpResult& result) const { - // step 2-3 (Steps 1 and 4 are irrelevant assertions.) + // Steps 2-4. RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); if (!handler) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } - // step 5-7 + // Step 5. RootedObject target(cx, proxy->as().target()); + MOZ_ASSERT(target); + + // Step 6. RootedValue trap(cx); if (!GetProxyTrap(cx, handler, cx->names().set, &trap)) return false; - // step 8 + // Step 7. if (trap.isUndefined()) return SetProperty(cx, target, id, v, receiver, result); - // step 9-10 + // Step 8. RootedValue value(cx); if (!IdToStringOrSymbol(cx, id, &value)) return false; @@ -917,17 +955,18 @@ ScriptedDirectProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id, if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) return false; - // step 11 + // Step 9. if (!ToBoolean(trapResult)) return result.fail(JSMSG_PROXY_SET_RETURNED_FALSE); - // step 12-13 + // Step 10. Rooted desc(cx); if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) return false; - // step 14 + // Step 11. if (desc.object()) { + // Step 11a. if (desc.isDataDescriptor() && !desc.configurable() && !desc.writable()) { bool same; if (!SameValue(cx, v, desc.value(), &same)) @@ -938,87 +977,81 @@ ScriptedDirectProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id, } } + // Step 11b. if (desc.isAccessorDescriptor() && !desc.configurable() && desc.setterObject() == nullptr) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_WO_SETTER); return false; } } - // step 15 + // Step 12. return result.succeed(); } -// ES6 (22 May, 2014) 9.5.13 Proxy.[[Call]] +// ES7 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.13 Proxy.[[Call]] bool ScriptedDirectProxyHandler::call(JSContext* cx, HandleObject proxy, const CallArgs& args) const { - // step 1 + // Steps 1-3. RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 2 if (!handler) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } - // step 3 + // Step 4. RootedObject target(cx, proxy->as().target()); + MOZ_ASSERT(target); MOZ_ASSERT(target->isCallable()); - // step 7 - RootedObject argsArray(cx, NewDenseCopiedArray(cx, args.length(), args.array())); - if (!argsArray) - return false; - - // step 4-5 + // Step 5. RootedValue trap(cx); if (!GetProxyTrap(cx, handler, cx->names().apply, &trap)) return false; - // step 6 + // Step 6. if (trap.isUndefined()) { RootedValue targetv(cx, ObjectValue(*target)); return Invoke(cx, args.thisv(), targetv, args.length(), args.array(), args.rval()); } - // step 8 + // Step 7. + RootedObject argArray(cx, NewDenseCopiedArray(cx, args.length(), args.array())); + if (!argArray) + return false; + + // Step 8. Value argv[] = { ObjectValue(*target), args.thisv(), - ObjectValue(*argsArray) + ObjectValue(*argArray) }; RootedValue thisValue(cx, ObjectValue(*handler)); return Invoke(cx, thisValue, trap, ArrayLength(argv), argv, args.rval()); } -// ES6 (22 May, 2014) 9.5.14 Proxy.[[Construct]] +// ES7 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.14 Proxy.[[Construct]] bool ScriptedDirectProxyHandler::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const { - // step 1 + // Steps 1-3. RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 2 if (!handler) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } - // step 3 + // Step 4. RootedObject target(cx, proxy->as().target()); + MOZ_ASSERT(target); MOZ_ASSERT(target->isConstructor()); - // step 7 - RootedObject argsArray(cx, NewDenseCopiedArray(cx, args.length(), args.array())); - if (!argsArray) - return false; - - // step 4-5 + // Step 5. RootedValue trap(cx); if (!GetProxyTrap(cx, handler, cx->names().construct, &trap)) return false; - // step 6 + // Step 6. if (trap.isUndefined()) { ConstructArgs cargs(cx); if (!FillArgumentsFromArraylike(cx, cargs, args)) @@ -1033,21 +1066,27 @@ ScriptedDirectProxyHandler::construct(JSContext* cx, HandleObject proxy, const C return true; } - // step 8-9 + // Step 7. + RootedObject argArray(cx, NewDenseCopiedArray(cx, args.length(), args.array())); + if (!argArray) + return false; + + // Steps 8, 10. Value constructArgv[] = { ObjectValue(*target), - ObjectValue(*argsArray), + ObjectValue(*argArray), args.newTarget() }; RootedValue thisValue(cx, ObjectValue(*handler)); if (!Invoke(cx, thisValue, trap, ArrayLength(constructArgv), constructArgv, args.rval())) return false; - // step 10 + // Step 9. if (!args.rval().isObject()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_CONSTRUCT_OBJECT); return false; } + return true; } @@ -1153,9 +1192,9 @@ IsRevokedScriptedProxy(JSObject* obj) return obj && IsScriptedProxy(obj) && !obj->as().target(); } -// ES6 draft rc4 9.5.15. +// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.14 ProxyCreate. static bool -NewScriptedProxy(JSContext* cx, CallArgs& args, const char* callerName) +ProxyCreate(JSContext* cx, CallArgs& args, const char* callerName) { if (args.length() < 2) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, @@ -1185,7 +1224,7 @@ NewScriptedProxy(JSContext* cx, CallArgs& args, const char* callerName) return false; } - // Steps 5-6, and 8 (reordered). + // Steps 5-6, 8. RootedValue priv(cx, ObjectValue(*target)); JSObject* proxy_ = NewProxyObject(cx, &ScriptedDirectProxyHandler::singleton, @@ -1197,11 +1236,11 @@ NewScriptedProxy(JSContext* cx, CallArgs& args, const char* callerName) Rooted proxy(cx, &proxy_->as()); proxy->setExtra(ScriptedDirectProxyHandler::HANDLER_EXTRA, ObjectValue(*handler)); - // Step 7, Assign [[Call]] and [[Construct]]. + // Step 7. uint32_t callable = target->isCallable() ? ScriptedDirectProxyHandler::IS_CALLABLE : 0; uint32_t constructor = target->isConstructor() ? ScriptedDirectProxyHandler::IS_CONSTRUCTOR : 0; - proxy->as().setExtra(ScriptedDirectProxyHandler::IS_CALLCONSTRUCT_EXTRA, - PrivateUint32Value(callable | constructor)); + proxy->setExtra(ScriptedDirectProxyHandler::IS_CALLCONSTRUCT_EXTRA, + PrivateUint32Value(callable | constructor)); // Step 10. args.rval().setObject(*proxy); @@ -1216,7 +1255,7 @@ js::proxy(JSContext* cx, unsigned argc, Value* vp) if (!ThrowIfNotConstructing(cx, args, "Proxy")) return false; - return NewScriptedProxy(cx, args, "Proxy"); + return ProxyCreate(cx, args, "Proxy"); } static bool @@ -1245,7 +1284,7 @@ js::proxy_revocable(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - if (!NewScriptedProxy(cx, args, "Proxy.revocable")) + if (!ProxyCreate(cx, args, "Proxy.revocable")) return false; RootedValue proxyVal(cx, args.rval()); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index c804d4c198..43de4b012e 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3252,9 +3252,13 @@ InvokeInterruptCallbackWrapper(JSContext* cx, unsigned argc, Value* vp) return false; JS::AutoSaveExceptionState savedExc(cx); - Value argv[1] = { BooleanValue(interruptRv) }; + + FixedInvokeArgs<1> iargs(cx); + + iargs[0].setBoolean(interruptRv); + RootedValue rv(cx); - if (!Invoke(cx, UndefinedValue(), args[0], 1, argv, &rv)) + if (!js::Call(cx, args[0], UndefinedHandleValue, iargs, &rv)) return false; args.rval().setUndefined(); diff --git a/js/src/tests/ecma_6/Array/iterator_edge_cases.js b/js/src/tests/ecma_6/Array/iterator_edge_cases.js index 079245ebfb..156aaba5f1 100644 --- a/js/src/tests/ecma_6/Array/iterator_edge_cases.js +++ b/js/src/tests/ecma_6/Array/iterator_edge_cases.js @@ -23,5 +23,28 @@ if (typeof newGlobal === "function") { TestArrayIteratorWrappers(); } +// Tests that calling |next| on an array iterator after iteration has finished +// doesn't get the array's |length| property. +function TestIteratorNextGetLength() { + var lengthCalledTimes = 0; + var array = { + __proto__: Array.prototype, + get length() { + lengthCalledTimes += 1; + return { + valueOf() { + return 0; + } + }; + } + }; + var it = array[Symbol.iterator](); + it.next(); + it.next(); + assertEq(1, lengthCalledTimes); +} +TestIteratorNextGetLength(); + + if (typeof reportCompare === "function") reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Array/unscopables.js b/js/src/tests/ecma_6/Array/unscopables.js new file mode 100644 index 0000000000..6685309a06 --- /dev/null +++ b/js/src/tests/ecma_6/Array/unscopables.js @@ -0,0 +1,51 @@ +let Array_unscopables = Array.prototype[Symbol.unscopables]; + +let desc = Reflect.getOwnPropertyDescriptor(Array.prototype, Symbol.unscopables); +assertDeepEq(desc, { + value: Array_unscopables, + writable: false, + enumerable: false, + configurable: true +}); + +assertEq(Reflect.getPrototypeOf(Array_unscopables), null); + +let desc2 = Object.getOwnPropertyDescriptor(Array_unscopables, "values"); +assertDeepEq(desc2, { + value: true, + writable: true, + enumerable: true, + configurable: true +}); + +let keys = Reflect.ownKeys(Array_unscopables); +print(uneval(keys)); +assertDeepEq(keys, [ + "copyWithin", + "entries", + "fill", + "find", + "findIndex", + "includes", + "keys", + "values" +]); + +for (let key of keys) + assertEq(Array_unscopables[key], true); + +// Test that it actually works +assertThrowsInstanceOf(() => { + with ([]) { + return entries; + } +}, ReferenceError); + +{ + let fill = 33; + with (Array.prototype) { + assertEq(fill, 33); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Array/values.js b/js/src/tests/ecma_6/Array/values.js new file mode 100644 index 0000000000..52a0b63c6c --- /dev/null +++ b/js/src/tests/ecma_6/Array/values.js @@ -0,0 +1,18 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +assertEq(Array.prototype.values, Array.prototype[Symbol.iterator]); +assertEq(Array.prototype.values.name, "values"); +assertEq(Array.prototype.values.length, 0); + +function valuesUnscopeable() { + var values = "foo"; + with ([1, 2, 3]) { + assertEq(indexOf, Array.prototype.indexOf); + assertEq(values, "foo"); + } +} +valuesUnscopeable(); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/shell/futex-apis.js b/js/src/tests/shell/futex-apis.js index e05deb7183..b3e0acd554 100644 --- a/js/src/tests/shell/futex-apis.js +++ b/js/src/tests/shell/futex-apis.js @@ -10,7 +10,7 @@ if (!(this.SharedArrayBuffer && this.Atomics)) { quit(0); } -// Checks for parameter validation of futex API. ALl of these test +// Checks for parameter validation of wait/wake API. All of these test // cases should throw exceptions during parameter validation, before // we check whether any waiting should be done. @@ -57,9 +57,8 @@ let sab = new SharedArrayBuffer(16); for ( let i=0 ; i < values.length ; i++ ) { let view = values[i]; - assertThrowsInstanceOf(() => Atomics.futexWait(view, 0, 0), TypeError); - assertThrowsInstanceOf(() => Atomics.futexWake(view, 0), TypeError); - assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 0, 1, 0), TypeError); + assertThrowsInstanceOf(() => Atomics.wait(view, 0, 0), TypeError); + assertThrowsInstanceOf(() => Atomics.wake(view, 0), TypeError); } } @@ -71,9 +70,8 @@ let sab = new SharedArrayBuffer(16); for ( let View of views ) { let view = new View(ab); - assertThrowsInstanceOf(() => Atomics.futexWait(view, 0, 0), TypeError); - assertThrowsInstanceOf(() => Atomics.futexWake(view, 0), TypeError); - assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 0, 1, 0), TypeError); + assertThrowsInstanceOf(() => Atomics.wait(view, 0, 0), TypeError); + assertThrowsInstanceOf(() => Atomics.wake(view, 0), TypeError); } } @@ -86,9 +84,8 @@ let sab = new SharedArrayBuffer(16); for ( let View of views ) { let view = new View(sab); - assertThrowsInstanceOf(() => Atomics.futexWait(view, 0, 0), TypeError); - assertThrowsInstanceOf(() => Atomics.futexWake(view, 0), TypeError); - assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 0, 1, 0), TypeError); + assertThrowsInstanceOf(() => Atomics.wait(view, 0, 0), TypeError); + assertThrowsInstanceOf(() => Atomics.wake(view, 0), TypeError); } } @@ -108,10 +105,8 @@ let sab = new SharedArrayBuffer(16); for ( let iidx=0 ; iidx < indices.length ; iidx++ ) { let Idx = indices[iidx](view); - assertThrowsInstanceOf(() => Atomics.futexWait(view, Idx, 10), RangeError); - assertThrowsInstanceOf(() => Atomics.futexWake(view, Idx), RangeError); - assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, Idx, 5, 0, 0), RangeError); - assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 5, Idx, 0), RangeError); + assertThrowsInstanceOf(() => Atomics.wait(view, Idx, 10), RangeError); + assertThrowsInstanceOf(() => Atomics.wake(view, Idx), RangeError); } } diff --git a/js/src/tests/shell/futex.js b/js/src/tests/shell/futex.js index bd3f1b0c4f..3800a396d1 100644 --- a/js/src/tests/shell/futex.js +++ b/js/src/tests/shell/futex.js @@ -17,7 +17,7 @@ function dprint(s) { } // Tests the SharedArrayBuffer mailbox in the shell. -// Tests the futex functionality in the shell. +// Tests the wait/wake functionality in the shell. var sab = new SharedArrayBuffer(12); var mem = new Int32Array(sab); @@ -61,19 +61,34 @@ assertThrowsInstanceOf(() => setSharedArrayBuffer(() => 37), Error); // Futex test +if (helperThreadCount() === 0) { + // Abort if there is no helper thread. + reportCompare(true,true); + quit(); +} + +//////////////////////////////////////////////////////////// + +// wait() returns "not-equal" if the value is not the expected one. + +mem[0] = 42; + +assertEq(Atomics.wait(mem, 0, 33), "not-equal"); + +// wait() returns "timed-out" if it times out + +assertEq(Atomics.wait(mem, 0, 42, 100), "timed-out"); + +//////////////////////////////////////////////////////////// + // Main is sharing the buffer with the worker; the worker is clearing // the buffer. mem[0] = 42; mem[1] = 37; mem[2] = DEBUG; -setSharedArrayBuffer(mem.buffer); -if (helperThreadCount() === 0) { - // Abort if there is no helper thread. - reportCompare(true,true); - quit(); -} +setSharedArrayBuffer(mem.buffer); evalInWorker(` var mem = new Int32Array(getSharedArrayBuffer()); @@ -83,40 +98,61 @@ function dprint(s) { assertEq(mem[0], 42); // what was written in the main thread assertEq(mem[1], 37); // is read in the worker mem[1] = 1337; -dprint("Sleeping for 3 seconds"); -sleep(3); +dprint("Sleeping for 2 seconds"); +sleep(2); dprint("Waking the main thread now"); setSharedArrayBuffer(null); -Atomics.futexWake(mem, 0, 1); +assertEq(Atomics.wake(mem, 0, 1), 1); // Can fail spuriously but very unlikely `); var then = Date.now(); -assertEq(Atomics.futexWait(mem, 0, 42), Atomics.OK); +assertEq(Atomics.wait(mem, 0, 42), "ok"); dprint("Woke up as I should have in " + (Date.now() - then)/1000 + "s"); assertEq(mem[1], 1337); // what was written in the worker is read in the main thread assertEq(getSharedArrayBuffer(), null); // The worker's clearing of the mbx is visible +//////////////////////////////////////////////////////////// + +// Test the default argument to atomics.wake() + +setSharedArrayBuffer(mem.buffer); + +evalInWorker(` +var mem = new Int32Array(getSharedArrayBuffer()); +sleep(2); // Probably long enough to avoid a spurious error next +assertEq(Atomics.wake(mem, 0), 1); // Last argument to wake should default to +Infinity +`); + +var then = Date.now(); +dprint("Main thread waiting on wakeup (2s)"); +assertEq(Atomics.wait(mem, 0, 42), "ok"); +dprint("Woke up as I should have in " + (Date.now() - then)/1000 + "s"); + +//////////////////////////////////////////////////////////// + // A tricky case: while in the wait there will be an interrupt, and in -// the interrupt handler we will execute a futexWait. This is +// the interrupt handler we will execute a wait. This is // explicitly prohibited (for now), so there should be a catchable exception. timeout(2, function () { - dprint("In the interrupt, starting inner wait"); - Atomics.futexWait(mem, 0, 42); // Should throw and propagate all the way out + dprint("In the interrupt, starting inner wait with timeout 2s"); + Atomics.wait(mem, 0, 42); // Should throw and propagate all the way out }); var exn = false; try { dprint("Starting outer wait"); - assertEq(Atomics.futexWait(mem, 0, 42, 5000), Atomics.OK); + assertEq(Atomics.wait(mem, 0, 42, 5000), "ok"); } catch (e) { - dprint("Got the exception!"); + dprint("Got the timeout exception!"); exn = true; } finally { timeout(-1); } assertEq(exn, true); -dprint("Done"); +//////////////////////////////////////////////////////////// + +dprint("Done"); reportCompare(true,true); diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index e1fa6f61b3..0a57ae07a3 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -22,6 +22,7 @@ macro(ArrayBufferSpecies, ArrayBufferSpecies, "ArrayBufferSpecies") \ macro(ArrayIteratorNext, ArrayIteratorNext, "ArrayIteratorNext") \ macro(ArraySpecies, ArraySpecies, "ArraySpecies") \ + macro(ArraySpeciesCreate, ArraySpeciesCreate, "ArraySpeciesCreate") \ macro(ArrayType, ArrayType, "ArrayType") \ macro(ArrayValues, ArrayValues, "ArrayValues") \ macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \ @@ -115,6 +116,9 @@ macro(formatToParts, formatToParts, "formatToParts") \ macro(frame, frame, "frame") \ macro(from, from, "from") \ + macro(futexOK, futexOK, "ok") \ + macro(futexNotEqual, futexNotEqual, "not-equal") \ + macro(futexTimedOut, futexTimedOut, "timed-out") \ macro(gcCycleNumber, gcCycleNumber, "gcCycleNumber") \ macro(GeneratorFunction, GeneratorFunction, "GeneratorFunction") \ macro(get, get, "get") \ @@ -303,28 +307,6 @@ macro(boolean, boolean, "boolean") \ macro(null, null, "null") \ macro(symbol, symbol, "symbol") \ - /* Well-known atom names must be continuous and ordered, matching \ - * enum JS::SymbolCode in jsapi.h. */ \ - macro(iterator, iterator, "iterator") \ - macro(match, match, "match") \ - macro(replace, replace, "replace") \ - macro(search, search, "search") \ - macro(species, species, "species") \ - macro(split, split, "split") \ - macro(toPrimitive, toPrimitive, "toPrimitive") \ - macro(unscopables, unscopables, "unscopables") \ - /* Same goes for the descriptions of the well-known symbols. */ \ - macro(Symbol_create, Symbol_create, "Symbol.create") \ - macro(Symbol_hasInstance, Symbol_hasInstance, "Symbol.hasInstance") \ - macro(Symbol_isConcatSpreadable, Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") \ - macro(Symbol_iterator, Symbol_iterator, "Symbol.iterator") \ - macro(Symbol_match, Symbol_match, "Symbol.match") \ - macro(Symbol_replace, Symbol_replace, "Symbol.replace") \ - macro(Symbol_search, Symbol_search, "Symbol.search") \ - macro(Symbol_species, Symbol_species, "Symbol.species") \ - macro(Symbol_split, Symbol_split, "Symbol.split") \ - macro(Symbol_toPrimitive, Symbol_toPrimitive, "Symbol.toPrimitive") \ - macro(Symbol_unscopables, Symbol_unscopables, "Symbol.unscopables") \ /* Function names for properties named by symbols. */ \ macro(Symbol_iterator_fun, Symbol_iterator_fun, "[Symbol.iterator]") \ diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 5bca806d0c..4b8ba2099b 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -8125,9 +8125,12 @@ DebuggerObject_getOwnPropertyDescriptor(JSContext* cx, unsigned argc, Value* vp) return false; desc.setSetterObject(set.toObjectOrNull()); } + + // Avoid tripping same-compartment assertions in JS::FromPropertyDescriptor(). + desc.object().set(&args.thisv().toObject()); } - return FromPropertyDescriptor(cx, desc, args.rval()); + return JS::FromPropertyDescriptor(cx, desc, args.rval()); } diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index b436328c0e..548e6aa283 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -214,14 +214,10 @@ LegacyGeneratorObject::close(JSContext* cx, HandleObject obj) MOZ_ASSERT(closeValue.isObject()); MOZ_ASSERT(closeValue.toObject().is()); - InvokeArgs args(cx); - if (!args.init(0)) - return false; + FixedInvokeArgs<0> args(cx); - args.setCallee(closeValue); - args.setThis(ObjectValue(*genObj)); - - return Invoke(cx, args); + RootedValue v(cx, ObjectValue(*genObj)); + return Call(cx, closeValue, v, args, &v); } const Class LegacyGeneratorObject::class_ = { diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index e9dd05c1d0..0cd878a4bb 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -525,8 +525,8 @@ InternalCall(JSContext* cx, const AnyInvokeArgs& args) return InternalCallOrConstruct(cx, args, NO_CONSTRUCT); } -static bool -CallFromStack(JSContext* cx, const CallArgs& args) +bool +js::CallFromStack(JSContext* cx, const CallArgs& args) { return InternalCall(cx, static_cast(args)); } diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index dbf4b91200..083d300fae 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -72,17 +72,6 @@ Invoke(JSContext* cx, const AnyInvokeArgs& args) return InternalCallOrConstruct(cx, args, NO_CONSTRUCT); } -/* - * Similar to InternalCallOrConstruct, but for use in places that really - * shouldn't use such an internal method directly (and won't, later in this - * patch stack). - */ -inline bool -InternalInvoke(JSContext* cx, const CallArgs& args) -{ - return InternalCallOrConstruct(cx, args, NO_CONSTRUCT); -} - // DEPRECATED. TO BE REMOVED. DO NOT ADD NEW USES. extern bool Invoke(JSContext* cx, const Value& thisv, const Value& fval, unsigned argc, const Value* argv, @@ -163,6 +152,15 @@ Call(JSContext* cx, HandleValue fval, JSObject* thisObj, return Call(cx, fval, thisv, args, rval); } +// Perform the above Call() operation using the given arguments. Similar to +// ConstructFromStack() below, this handles |!IsCallable(args.calleev())|. +// +// This internal operation is intended only for use with arguments known to be +// on the JS stack, or at least in carefully-rooted memory. The vast majority of +// potential users should instead use InvokeArgs in concert with Call(). +extern bool +CallFromStack(JSContext* cx, const CallArgs& args); + // ES6 7.3.13 Construct(F, argumentsList, newTarget). All parameters are // required, hopefully forcing callers to be careful not to (say) blindly pass // callee as |newTarget| when a different value should have been passed. diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h index dca261675a..350c9c6530 100644 --- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -12,6 +12,7 @@ #include "ds/IdValuePair.h" #include "gc/Barrier.h" +#include "js/GCHashTable.h" #include "vm/TaggedProto.h" #include "vm/TypeInference.h" diff --git a/js/src/vm/ProxyObject.cpp b/js/src/vm/ProxyObject.cpp index ef79dc9a12..c44ffbc0a0 100644 --- a/js/src/vm/ProxyObject.cpp +++ b/js/src/vm/ProxyObject.cpp @@ -45,27 +45,17 @@ ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler, HandleValue pri if (handler->finalizeInBackground(priv)) allocKind = GetBackgroundAllocKind(allocKind); - ProxyValueArray* values = cx->zone()->new_(); - if (!values) { - ReportOutOfMemory(cx); - return nullptr; - } - AutoSetNewObjectMetadata metadata(cx); // Note: this will initialize the object's |data| to strange values, but we // will immediately overwrite those below. RootedObject obj(cx, NewObjectWithGivenTaggedProto(cx, clasp, proto, allocKind, newKind)); - if (!obj) { - js_free(values); + if (!obj) return nullptr; - } Rooted proxy(cx, &obj->as()); - - proxy->data.values = values; + new (proxy->data.values) detail::ProxyValueArray; proxy->data.handler = handler; - proxy->setCrossCompartmentPrivate(priv); /* Don't track types of properties of non-DOM and non-singleton proxies. */ diff --git a/js/src/vm/ProxyObject.h b/js/src/vm/ProxyObject.h index b4bb880400..4064e31207 100644 --- a/js/src/vm/ProxyObject.h +++ b/js/src/vm/ProxyObject.h @@ -19,12 +19,12 @@ class ProxyObject : public JSObject HeapPtrShape shape; // GetProxyDataLayout computes the address of this field. - ProxyDataLayout data; + detail::ProxyDataLayout data; void static_asserts() { static_assert(sizeof(ProxyObject) == sizeof(JSObject_Slots0), "proxy object size must match GC thing size"); - static_assert(offsetof(ProxyObject, data) == ProxyDataOffset, + static_assert(offsetof(ProxyObject, data) == detail::ProxyDataOffset, "proxy object layout must match shadow interface"); } @@ -40,7 +40,7 @@ class ProxyObject : public JSObject void setSameCompartmentPrivate(const Value& priv); HeapValue* slotOfPrivate() { - return reinterpret_cast(&GetProxyDataLayout(this)->values->privateSlot); + return reinterpret_cast(&detail::GetProxyDataLayout(this)->values->privateSlot); } JSObject* target() const { @@ -62,8 +62,8 @@ class ProxyObject : public JSObject return offsetof(ProxyObject, data.handler); } static size_t offsetOfExtraSlotInValues(size_t slot) { - MOZ_ASSERT(slot < PROXY_EXTRA_SLOTS); - return offsetof(ProxyValueArray, extraSlots) + slot * sizeof(Value); + MOZ_ASSERT(slot < detail::PROXY_EXTRA_SLOTS); + return offsetof(detail::ProxyValueArray, extraSlots) + slot * sizeof(Value); } const Value& extra(size_t n) const { @@ -76,8 +76,8 @@ class ProxyObject : public JSObject private: HeapValue* slotOfExtra(size_t n) { - MOZ_ASSERT(n < PROXY_EXTRA_SLOTS); - return reinterpret_cast(&GetProxyDataLayout(this)->values->extraSlots[n]); + MOZ_ASSERT(n < detail::PROXY_EXTRA_SLOTS); + return reinterpret_cast(&detail::GetProxyDataLayout(this)->values->extraSlots[n]); } static bool isValidProxyClass(const Class* clasp) { diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index ea06a86564..c39c4368f6 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -638,7 +638,7 @@ JSRuntime::requestInterrupt(InterruptMode mode) // collection among others), take additional steps to // interrupt corner cases where the above fields are not // regularly polled. Wake both ilooping JIT code and - // futexWait. + // Atomics.wait(). fx.lock(); if (fx.isWaiting()) fx.wake(FutexRuntime::WakeForJSInterrupt); diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 1df53ca865..dadd44f7fe 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -428,9 +428,23 @@ struct JSAtomState #define PROPERTYNAME_FIELD(name, code, init, clasp) js::ImmutablePropertyNamePtr name; JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD) #undef PROPERTYNAME_FIELD +#define PROPERTYNAME_FIELD(name) js::ImmutablePropertyNamePtr name; + JS_FOR_EACH_WELL_KNOWN_SYMBOL(PROPERTYNAME_FIELD) +#undef PROPERTYNAME_FIELD +#define PROPERTYNAME_FIELD(name) js::ImmutablePropertyNamePtr Symbol_##name; + JS_FOR_EACH_WELL_KNOWN_SYMBOL(PROPERTYNAME_FIELD) +#undef PROPERTYNAME_FIELD + + js::ImmutablePropertyNamePtr* wellKnownSymbolNames() { +#define FIRST_PROPERTYNAME_FIELD(name) return &name; + JS_FOR_EACH_WELL_KNOWN_SYMBOL(FIRST_PROPERTYNAME_FIELD) +#undef FIRST_PROPERTYNAME_FIELD + } js::ImmutablePropertyNamePtr* wellKnownSymbolDescriptions() { - return &Symbol_iterator; +#define FIRST_PROPERTYNAME_FIELD(name) return &Symbol_ ##name; + JS_FOR_EACH_WELL_KNOWN_SYMBOL(FIRST_PROPERTYNAME_FIELD) +#undef FIRST_PROPERTYNAME_FIELD } }; @@ -958,7 +972,7 @@ struct JSRuntime : public JS::shadow::Runtime, /* Default JSVersion. */ JSVersion defaultVersion_; - /* Futex state, used by futexWait and futexWake on the Atomics object */ + /* Futex state, used by Atomics.wait() and Atomics.wake() on the Atomics object */ js::FutexRuntime fx; private: diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index b71b5b5245..feb778bc0b 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -1712,17 +1712,10 @@ CallSelfHostedNonGenericMethod(JSContext* cx, const CallArgs& args) if (!args2.init(args.length() - 1)) return false; - args2.setCallee(selfHostedFun); - args2.setThis(args.thisv()); - for (size_t i = 0; i < args.length() - 1; i++) args2[i].set(args[i]); - if (!Invoke(cx, args2)) - return false; - - args.rval().set(args2.rval()); - return true; + return js::Call(cx, selfHostedFun, args.thisv(), args2, args.rval()); } bool diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index 9c10565d2a..8b88a7e0d7 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -47,10 +47,6 @@ ShapeTable::init(ExclusiveContext* cx, Shape* lastProp) if (sizeLog2 < MIN_SIZE_LOG2) sizeLog2 = MIN_SIZE_LOG2; - /* - * Use rt->calloc for memory accounting and overpressure handling - * without OOM reporting. See ShapeTable::change. - */ size = JS_BIT(sizeLog2); entries_ = cx->pod_calloc(size); if (!entries_) @@ -268,7 +264,7 @@ template ShapeTable::Entry& ShapeTable::search(jsid id); template ShapeTable::Entry& ShapeTable::search(jsid id); bool -ShapeTable::change(int log2Delta, ExclusiveContext* cx) +ShapeTable::change(ExclusiveContext* cx, int log2Delta) { MOZ_ASSERT(entries_); MOZ_ASSERT(-1 <= log2Delta && log2Delta <= 1); @@ -280,7 +276,7 @@ ShapeTable::change(int log2Delta, ExclusiveContext* cx) uint32_t newLog2 = oldLog2 + log2Delta; uint32_t oldSize = JS_BIT(oldLog2); uint32_t newSize = JS_BIT(newLog2); - Entry* newTable = cx->pod_calloc(newSize); + Entry* newTable = cx->maybe_pod_calloc(newSize); if (!newTable) return false; @@ -318,11 +314,11 @@ ShapeTable::grow(ExclusiveContext* cx) MOZ_ASSERT(entryCount_ + removedCount_ <= size - 1); - if (!change(delta, cx)) { - if (entryCount_ + removedCount_ == size - 1) + if (!change(cx, delta)) { + if (entryCount_ + removedCount_ == size - 1) { + ReportOutOfMemory(cx); return false; - - cx->recoverFromOutOfMemory(); + } } return true; @@ -1045,7 +1041,7 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_) /* Consider shrinking table if its load factor is <= .25. */ uint32_t size = table.capacity(); if (size > ShapeTable::MIN_SIZE && table.entryCount() <= size >> 2) - (void) table.change(-1, cx); + (void) table.change(cx, -1); } else { /* * Non-dictionary-mode shape tables are shared immutables, so all we diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index 2886ddf91f..4da9dedc2d 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -217,13 +217,11 @@ class ShapeTable { return mallocSizeOf(this) + mallocSizeOf(entries_); } - /* - * NB: init and change are fallible but do not report OOM, so callers can - * cope or ignore. They do however use the context's calloc method in - * order to update the malloc counter on success. - */ + // init() is fallible and reports OOM to the context. bool init(ExclusiveContext* cx, Shape* lastProp); - bool change(int log2Delta, ExclusiveContext* cx); + + // change() is fallible but does not report OOM. + bool change(ExclusiveContext* cx, int log2Delta); template Entry& search(jsid id); diff --git a/js/xpconnect/loader/mozJSComponentLoader.cpp b/js/xpconnect/loader/mozJSComponentLoader.cpp index 6cf18c9276..74e25c6534 100644 --- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -364,7 +364,6 @@ mozJSComponentLoader::LoadModule(FileLocation& aFile) dom::AutoJSAPI jsapi; jsapi.Init(); - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); nsAutoPtr entry(new ModuleEntry(cx)); @@ -654,7 +653,6 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo, dom::AutoJSAPI jsapi; jsapi.Init(); - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); bool realFile = false; @@ -982,7 +980,6 @@ mozJSComponentLoader::UnloadModules() dom::AutoJSAPI jsapi; jsapi.Init(); - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); RootedObject global(cx, mLoaderGlobal->GetJSObject()); if (global) { @@ -1216,7 +1213,6 @@ mozJSComponentLoader::ImportInto(const nsACString& aLocation, // not an AutoEntryScript. dom::AutoJSAPI jsapi; jsapi.Init(); - jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); JSAutoCompartment ac(cx, mod->obj); diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul index 289306ba18..92f42bcda1 100644 --- a/js/xpconnect/tests/chrome/test_xrayToJS.xul +++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul @@ -181,7 +181,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push", "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf", "includes", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find", - "findIndex", "copyWithin", "fill", Symbol.iterator, "entries", "keys", "constructor"]; + "findIndex", "copyWithin", "fill", Symbol.iterator, Symbol.unscopables, "entries", "keys", + "values", "constructor"]; gConstructorProperties['Array'] = constructorProps(["join", "reverse", "sort", "push", "pop", "shift", "unshift", "splice", "concat", "slice", "isArray", @@ -371,7 +372,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 is(Object.getOwnPropertyNames(xrayProto).sort().toSource(), protoProps.toSource(), "getOwnPropertyNames works"); is(Object.getOwnPropertySymbols(xrayProto).map(uneval).sort().toSource(), - gPrototypeProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(), + gPrototypeProperties[classname].filter(id => typeof id !== "string" && !propsToSkip.includes(id)) + .map(uneval).sort().toSource(), "getOwnPropertySymbols works"); is(xrayProto.constructor, iwin[classname], "constructor property works"); @@ -531,7 +533,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 // show that over Xrays, and generally want .length to just appear as an // |own| data property. So we add it to the ignore list here, and check it // separately. - let propsToSkip = ['length']; + // + // |Symbol.unscopables| should in principle be exposed, but it is + // inconvenient (as it's a data property, unsupported by ClassSpec) and + // low value. + let propsToSkip = ['length', Symbol.unscopables]; + // On the constructor, we want to skip all the non-standard "generic" // functions. We're trying to remove them anyway; no point doing extra work // to expose them over Xrays. diff --git a/layout/reftests/box-shadow/boxshadow-inset-large-border-radius-ref.html b/layout/reftests/box-shadow/boxshadow-inset-large-border-radius-ref.html new file mode 100644 index 0000000000..77c8d299b6 --- /dev/null +++ b/layout/reftests/box-shadow/boxshadow-inset-large-border-radius-ref.html @@ -0,0 +1,17 @@ + + + + + + +
+ + diff --git a/layout/reftests/box-shadow/boxshadow-inset-large-border-radius.html b/layout/reftests/box-shadow/boxshadow-inset-large-border-radius.html new file mode 100644 index 0000000000..87ce0f01c6 --- /dev/null +++ b/layout/reftests/box-shadow/boxshadow-inset-large-border-radius.html @@ -0,0 +1,35 @@ + + + + + + +
+
+
+ + diff --git a/layout/reftests/box-shadow/boxshadow-inset-large-offset-ref.html b/layout/reftests/box-shadow/boxshadow-inset-large-offset-ref.html new file mode 100644 index 0000000000..f117b53ed9 --- /dev/null +++ b/layout/reftests/box-shadow/boxshadow-inset-large-offset-ref.html @@ -0,0 +1,23 @@ + + + + +
+
+ + diff --git a/layout/reftests/box-shadow/boxshadow-inset-large-offset.html b/layout/reftests/box-shadow/boxshadow-inset-large-offset.html new file mode 100644 index 0000000000..fe8db9f07a --- /dev/null +++ b/layout/reftests/box-shadow/boxshadow-inset-large-offset.html @@ -0,0 +1,26 @@ + + + + +
+
+
+
+ + diff --git a/layout/reftests/box-shadow/reftest.list b/layout/reftests/box-shadow/reftest.list index f317a21a7d..7348fb992e 100644 --- a/layout/reftests/box-shadow/reftest.list +++ b/layout/reftests/box-shadow/reftest.list @@ -27,6 +27,10 @@ fuzzy-if(d2d,2,1080) == boxshadow-border-radius-int.html boxshadow-border-radius == boxshadow-inset-neg-spread.html about:blank == boxshadow-inset-neg-spread2.html boxshadow-inset-neg-spread2-ref.html fuzzy(26,3610) == boxshadow-rotated.html boxshadow-rotated-ref.html # Bug 1211264 +== boxshadow-inset-large-border-radius.html boxshadow-inset-large-border-radius-ref.html + +# fuzzy due to blur going inside, but as long as it's essentially black instead of a light gray its ok. +fuzzy(12,9445) == boxshadow-inset-large-offset.html boxshadow-inset-large-offset-ref.html == overflow-not-scrollable-1.html overflow-not-scrollable-1-ref.html == overflow-not-scrollable-1.html overflow-not-scrollable-1-ref2.html diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index 1b1e1f9b13..180087e2eb 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -276,12 +276,14 @@ void nsBaseWidget::DestroyCompositor() RefPtr compositorChild = mCompositorBridgeChild; RefPtr compositorParent = mCompositorBridgeParent; mCompositorBridgeChild->Destroy(); + mCompositorBridgeChild = nullptr; } // Can have base widgets that are things like tooltips // which don't have CompositorVsyncDispatchers if (mCompositorVsyncDispatcher) { mCompositorVsyncDispatcher->Shutdown(); + mCompositorVsyncDispatcher = nullptr; } } diff --git a/xpcom/base/nsError.h b/xpcom/base/nsError.h index cc8f47d0b8..a559344210 100644 --- a/xpcom/base/nsError.h +++ b/xpcom/base/nsError.h @@ -159,6 +159,8 @@ static_assert(((nsresult)0) < ((nsresult)-1), static_assert(sizeof(nsresult) == sizeof(uint32_t), "nsresult must be 32 bits"); +#define MOZ_ALWAYS_SUCCEEDS(expr) MOZ_ALWAYS_TRUE(NS_SUCCEEDED(expr)) + /** * @name Standard Error Generating Macros */