From 16988569b99f7b16b8dfe4399f1dcf3442bf73e6 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Fri, 30 Oct 2020 11:49:49 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1177892 part 1 - Remove BOOLEAN_TO_JSVAL and STRING_TO_JSVAL. r=evilpie (1fac17ff3) - Bug 1177892 part 2 - Remove PRIVATE_TO_JSVAL. r=evilpie (4d7e80200) - pointer style (cd27bd41a) - Bug 1177892 part 3 - Remove OBJECT_TO_JSVAL. r=evilpie (ff66aebe5) - Bug 1155342 - Disallow flagging a [NewObject] method with a [DependsOn] value that implies it might return the same value when called twice. r=bzbarsky (7ba1bb806) - Bug 1129239. Don't require 'optional' keyword on trailing dictionary arguments if the dictionary has a required member. r=smaug (3ee24f57b) - Bug 1152902 part 1. Add a way to flag a method in webidl as being identity-testable, so we'll expose such an identity test from bindings to other C++ code. r=peterv (43e038677) - Bug 1152902 part 2. Add a fast path for the case when a Promise is resolved with another Promise. r=nsm (59f56603e) - Bug 1172785 - Adding StaticClassOverride routing for JS implemented WebIDL, r=peterv (6ae76e198) - Bug 1172785 - Using RTCCertificate for WebRTC, r=ekr (a69ee6167) - fix build, file built twice (04f7e3d75) - Bug 1155942. Treat USVString and ByteString as serializable values. r=bkelly (33e635737) - Bug 913053. Split up IDLTypedef and IDLTypedefType, and likewise for IDLCallback vs IDLCallbackType, so it's clearer whether we're operating on types or objects represented by those types. r=khuey (661f45319) - Bug 1168471 - Implement support for SharedArrayBuffers and SharedArrayViews in WebIDL. r=bz, r=lth, r=luke (77d63babc) - Bug 1151269 - Fix the test used by Ion ICs for whether values can definitely be written to an unboxed object, r=jandem. (0463d6b53) - Bug 1139474 - Watch for unboxed object properties when attaching Ion SETPROP ICs, r=jandem. (67bc9884d) - Bug 1162199 - Use unboxed objects by default, r=jandem. (4dba9da90) - Bug 1166678 - Inline first ObjectGroup::maybeSweep test, r=jandem. (ff179acb3) - Bug 1166709 - After converting unboxed objects created by some initializer to natives, create native objects at that allocation site in the future, r=jandem. (14a5c92b5) - Bug 1166709 - Mark definite properties when replacing the unboxed group used for literals at some pc, r=jandem. (487017105) - Bug 1170372 - Use unboxed arrays for Array() and other functions keyed to allocation sites, r=jandem. (9153a5313) - Bug 1175535 - Don't require objects embedded in MIR nodes to always be tenured, r=jandem. (75399d353) - Bug 1190272 - Improve type checks when storing values into unboxed objects in Ion code, r=jandem. (0a91a08d5) - Bug 1216130 - Fix extra checks for unboxed objects in PropertyWriteNeedsTypeBarrier. r=bhackett (4862b91ca) - Bug 1166700 - Tolerate null failures targets when storing to an unboxed object must fail, r=jandem. (87a07aa3d) - backport of Bug 1389436 - Explicitly instantiate gfxFont::GetShapedWord for its use in gfxTextRun.cpp. r=jfkthame (5f6fc9f18) - Bug 1164374 - Use StaticMutex in BrowserProcessSubThread. r=froydnj. (b0ad93d17) - Bug 1167771 - Simplify the pre-barrier verifier's tracer use; r=sfink (0fc926804) - Bug 1166037 - Part 2 - Modify common Baseline code for ARM64. r=djvj (739b36d83) - Bug 1168864 followup - Remove unnecessary addProperty check from CheckHasNoSuchProperty. r=bhackett (3a464d07e) - Bug 1166037 - Follow-up - Revert SimulatorType to Simulator. no_r=me (613b095d3) - Bug 1166944 - Inline the only user of TraceObjectSlots; r=jonco (d830d12f0) - pointer style (d567d3be2) - Bug 1167291 - Generalize the marking tracer's cross-compartment check; r=jonco (b5f753147) - Bug 1167318 - Remove GetGCThingTraceKind in favor of the typed Cell variants; r=jonco (f698f3a64) - Bug 1167323 - Use internal interfaces to implement MarkCycleCollectorChildren; r=jonco (4acf85bdb) - Bug 1167433 - AccessorShape is an AllocKind but not a TraceKind; r=jonco (ea92cd26a) - pointer style (403e83b64) - remove namespace hack (e3663d39b) - Bug 1167453 - Rename JSGCTraceKind and make it a C++11 enum class; r=jonco (f7f435a16) - Bug 1165966 - Update destructor assertions to handle failed initalization r=terrence (48237ed24) - pointer style (f637042fc) - Bug 774364 - Part 1: Inline Math.random() in Ion on x86_64. r=sstangl (8379ab0b2) - Bug 1167677 - Try harder to find scratch registers for memory->memory MoveGroup moves, r=sunfish. (14d4b9962) - pointer style (93bb53345) - Bug 1185653 - Fix enumerate hook on unboxed objects to skip non-enume#able properties. r=jorendorff (4ecf41a10) - Bug 1125624, part 1 - Implement ValidateAndApplyPropertyDescriptor steps 3-4, so that (once the corresponding code in StandardDefineProperty is deleted) freezing an already-frozen object with an addProperty class hook will not call the hook. r=Waldo. (f67f98231) - Bug 1125624, part 2 - Change js::StandardDefineProperty to forward to s::DefineProperty. r=Waldo. (3ac5d9e30) - Bug 1148568 - In JSON.parse with a reviver callback, ignore failure when defining properties. r=Waldo. (30c35a758) - pointer style (826d092cf) - Bug 1166950 - Only give constructor functions a prototype. r=efaust (d1b909603) - Bug 1140482 - Add JSPROP_RESOLVING. Give NativeDefineProperty standard behavior in cases where a non-resolving define needs to trigger a resolve hook. r=Waldo. (b2d650abd) - Bug 1140482 followup: Update ResolveInterpretedFunctionPrototype() failure-cases to return false instead of nullptr, now that return type is bool. rs=jorendorff (38eef1812) - Bug 1148188 - part1: defaultShims. r=billm (9eb98a8fa) - pointer style (ec74889f1) - Bug 1101182 - One interpose call for one property access. r=bholley (111afff12) - pointer style (b8dd593e9) - Bug 1148188 - part2: interposeCall. r=billm (6fce5829e) - Bug 1125624, part 3 - Remove js::StandardDefineProperty and js::DefineOwnProperty. r=Waldo. (a8a228f6e) - Bug 1167244 - Handle nullptr return from maybeGetProperty(). r=bhackett (91958bdfc) - pointer style (ebfc629a6) - Bug 1170355 - Watch for indexes that don't fit in a jsid in Array.shift, r=jandem. (6c2f0a8a2) - pointer style (a31367643) - Bug 1165348 - Move Scalar Replacement after GVN. r=jandem (9296335d1) - Bug 1161584 - Add TrackedStrategy::SetProp_InlineCache. r=shu (612ea0b32) - Bug 923717 - Add IC fuzzing mode. r=efaust (a6a6a7460) - Bug 1166711 part 2.1 - Check Scalar Replacement with both unboxed object and without. r=bhackett (e839f034a) - Bug 1172943 - Use unboxed arrays for JSON and script literal arrays, r=jandem. (964d5a42f) - Bug 1162986 - Relax type requirements for using baseline cache information when compiling GETPROP, r=jandem. (68e234a08) - Bug 1168500 - Replace the operator, by variadic templates. r=Waldo (ccb173cb7) - Bug 1166711 part 0.1 - Use JitSpewPrinter instead of stderr. r=bhackett (239022b72) - Bug 1166711 part 0.2 - JitSpew add scope-base indentation level. r=bhackett (89288621e) - Bug 1129313 - Scalar Replacement: Remove PostWriteBarrier at the same time as the stores. r=h4writer (f9df0503e) --- dom/archivereader/ArchiveRequest.cpp | 2 +- dom/base/EventSource.cpp | 2 +- dom/base/WebSocket.cpp | 4 +- dom/base/nsDOMClassInfo.cpp | 12 +- dom/base/nsDOMDataChannel.cpp | 4 +- dom/base/nsGlobalWindow.h | 2 + dom/base/nsJSEnvironment.cpp | 8 +- dom/bindings/BindingUtils.cpp | 4 +- dom/bindings/BindingUtils.h | 4 +- dom/bindings/Codegen.py | 80 ++- dom/bindings/Configuration.py | 15 +- dom/bindings/DOMJSProxyHandler.cpp | 4 +- dom/bindings/TypedArray.h | 33 + dom/bindings/parser/WebIDL.py | 412 ++++++++---- .../parser/tests/test_distinguishability.py | 17 +- dom/bindings/test/TestBindingHeader.h | 1 + dom/bindings/test/TestCodeGen.webidl | 3 + dom/bindings/test/TestExampleGen.webidl | 1 + dom/bindings/test/TestJSImplGen.webidl | 1 + dom/bluetooth/BluetoothUtils.cpp | 6 +- dom/indexedDB/KeyPath.cpp | 2 +- dom/media/PeerConnection.js | 80 ++- dom/media/tests/mochitest/mochitest.ini | 2 + .../test_peerConnection_certificates.html | 173 +++++ dom/media/webrtc/moz.build | 3 + dom/mobilemessage/MobileMessageCallback.cpp | 2 +- dom/plugins/base/nsJSNPRuntime.cpp | 11 +- dom/system/OSFileConstants.cpp | 6 +- dom/workers/WorkerScope.cpp | 2 +- dom/workers/XMLHttpRequest.cpp | 2 +- dom/xbl/nsXBLBinding.cpp | 2 +- gfx/thebes/gfxFont.cpp | 11 + ipc/glue/BrowserProcessSubThread.cpp | 9 +- ipc/glue/BrowserProcessSubThread.h | 6 +- js/ipc/JavaScriptShared.cpp | 2 +- js/ipc/WrapperAnswer.cpp | 2 +- js/public/Class.h | 25 +- js/public/GCAPI.h | 4 +- js/public/HeapAPI.h | 62 +- js/public/Id.h | 4 +- js/public/MemoryMetrics.h | 20 +- js/public/TracingAPI.h | 54 +- js/public/TrackedOptimizationInfo.h | 1 + js/public/UbiNode.h | 2 +- js/public/Value.h | 58 +- js/src/builtin/Array.js | 10 +- js/src/builtin/AtomicsObject.cpp | 5 +- js/src/builtin/Intl.cpp | 5 +- js/src/builtin/Intl.js | 31 +- js/src/builtin/Object.cpp | 5 +- js/src/builtin/SIMD.cpp | 5 +- js/src/builtin/TestingFunctions.cpp | 20 +- js/src/builtin/TypedObject.cpp | 8 +- js/src/builtin/TypedObject.h | 3 +- js/src/builtin/Utilities.js | 4 +- js/src/ctypes/CTypes.cpp | 61 +- js/src/ctypes/Library.cpp | 6 +- js/src/frontend/BytecodeEmitter.cpp | 41 +- js/src/frontend/ParseNode.h | 6 +- js/src/gc/Barrier.cpp | 7 + js/src/gc/Barrier.h | 41 +- js/src/gc/GCInternals.h | 2 +- js/src/gc/Heap.h | 71 +- js/src/gc/Iteration.cpp | 2 +- js/src/gc/Marking.cpp | 220 ++---- js/src/gc/Marking.h | 9 - js/src/gc/RootMarking.cpp | 6 +- js/src/gc/Tracer.cpp | 160 ++++- js/src/gc/Tracer.h | 18 +- js/src/gc/Verifier.cpp | 57 +- js/src/gdb/tests/test-Root.cpp | 2 +- js/src/jit-test/tests/baseline/bug945223.js | 19 +- js/src/jit-test/tests/basic/bug1170355.js | 3 + js/src/jit-test/tests/basic/bug1185653.js | 28 + js/src/jit-test/tests/ion/ic-fuzz-0.js | 3 + js/src/jit-test/tests/ion/recover-arrays.js | 6 + .../tests/ion/recover-empty-new-object.js | 8 + .../tests/ion/recover-iterator-next.js | 43 ++ js/src/jit-test/tests/ion/recover-objects.js | 66 +- js/src/jit-test/tests/ion/testFloat32.js | 6 + js/src/jit/BacktrackingAllocator.cpp | 22 +- js/src/jit/BacktrackingAllocator.h | 2 +- js/src/jit/BaselineBailouts.cpp | 2 +- js/src/jit/BaselineCompiler.cpp | 7 +- js/src/jit/BaselineIC.cpp | 241 ++++--- js/src/jit/BaselineIC.h | 27 +- js/src/jit/BaselineInspector.cpp | 6 +- js/src/jit/BaselineInspector.h | 2 +- js/src/jit/CodeGenerator.cpp | 213 +++--- js/src/jit/CodeGenerator.h | 8 +- js/src/jit/Ion.cpp | 40 +- js/src/jit/IonBuilder.cpp | 282 ++++---- js/src/jit/IonBuilder.h | 40 +- js/src/jit/IonCaches.cpp | 11 +- js/src/jit/JitOptions.cpp | 6 + js/src/jit/JitOptions.h | 4 + js/src/jit/JitSpewer.cpp | 12 +- js/src/jit/LIR-Common.h | 47 +- js/src/jit/LOpcodes.h | 1 + js/src/jit/Lowering.cpp | 15 +- js/src/jit/Lowering.h | 2 +- js/src/jit/MCallOptimize.cpp | 288 ++------ js/src/jit/MIR.cpp | 79 ++- js/src/jit/MIR.h | 187 ++++-- js/src/jit/MOpcodes.h | 1 + js/src/jit/MacroAssembler.cpp | 34 +- js/src/jit/MoveResolver.cpp | 88 ++- js/src/jit/MoveResolver.h | 10 +- js/src/jit/OptimizationTracking.cpp | 3 + js/src/jit/Recover.cpp | 6 +- js/src/jit/Recover.h | 1 - js/src/jit/ScalarReplacement.cpp | 12 +- js/src/jit/ScalarReplacement.h | 2 +- js/src/jit/VMFunctions.cpp | 5 +- js/src/jit/arm/CodeGenerator-arm.cpp | 16 + js/src/jit/arm/CodeGenerator-arm.h | 2 + js/src/jit/arm/LIR-arm.h | 17 + js/src/jit/arm/Lowering-arm.cpp | 7 + js/src/jit/arm/Lowering-arm.h | 1 + js/src/jit/arm/MacroAssembler-arm.cpp | 6 + js/src/jit/arm/MacroAssembler-arm.h | 1 + js/src/jit/mips/CodeGenerator-mips.cpp | 16 + js/src/jit/mips/CodeGenerator-mips.h | 2 + js/src/jit/mips/LIR-mips.h | 17 + js/src/jit/mips/Lowering-mips.cpp | 7 + js/src/jit/mips/Lowering-mips.h | 1 + js/src/jit/mips/MacroAssembler-mips.cpp | 6 + js/src/jit/mips/MacroAssembler-mips.h | 1 + js/src/jit/shared/CodeGenerator-shared.h | 89 ++- js/src/jit/x64/Assembler-x64.h | 7 + js/src/jit/x64/CodeGenerator-x64.cpp | 126 ++++ js/src/jit/x64/CodeGenerator-x64.h | 4 + js/src/jit/x64/LIR-x64.h | 35 + js/src/jit/x64/Lowering-x64.cpp | 11 + js/src/jit/x64/Lowering-x64.h | 1 + .../jit/x86-shared/BaseAssembler-x86-shared.h | 15 + .../jit/x86-shared/MoveEmitter-x86-shared.cpp | 79 ++- .../jit/x86-shared/MoveEmitter-x86-shared.h | 23 +- js/src/jit/x86/CodeGenerator-x86.cpp | 16 + js/src/jit/x86/CodeGenerator-x86.h | 2 + js/src/jit/x86/LIR-x86.h | 17 + js/src/jit/x86/Lowering-x86.cpp | 7 + js/src/jit/x86/Lowering-x86.h | 1 + .../jsapi-tests/testAddPropertyPropcache.cpp | 4 +- .../testDefineGetterSetterNonEnumerable.cpp | 10 +- js/src/jsapi-tests/testGCCellPtr.cpp | 10 +- js/src/jsapi-tests/testGCMarking.cpp | 10 +- js/src/jsapi-tests/testLookup.cpp | 2 +- js/src/jsapi-tests/testMappedArrayBuffer.cpp | 6 +- js/src/jsapi-tests/testNewObject.cpp | 6 +- js/src/jsapi-tests/testOps.cpp | 2 +- js/src/jsapi-tests/testParseJSON.cpp | 10 +- js/src/jsapi-tests/testResolveRecursion.cpp | 51 +- js/src/jsapi.cpp | 18 +- js/src/jsapi.h | 73 +- js/src/jsarray.cpp | 393 +++++------ js/src/jsarray.h | 60 +- js/src/jscntxt.h | 4 +- js/src/jscompartment.cpp | 8 +- js/src/jscompartment.h | 4 + js/src/jsfriendapi.cpp | 44 +- js/src/jsfriendapi.h | 64 +- js/src/jsfun.cpp | 63 +- js/src/jsgc.cpp | 54 +- js/src/jsgc.h | 73 +- js/src/jsgcinlines.h | 10 - js/src/jsiter.cpp | 16 +- js/src/jsmath.cpp | 13 +- js/src/jsmath.h | 9 +- js/src/jsnum.cpp | 24 +- js/src/jsobj.cpp | 627 ++---------------- js/src/jsobj.h | 35 +- js/src/json.cpp | 28 +- js/src/jsscript.cpp | 8 +- js/src/jsscript.h | 1 + js/src/jsstr.cpp | 70 +- js/src/jsweakmap.cpp | 5 +- js/src/jsweakmap.h | 2 +- js/src/proxy/DirectProxyHandler.cpp | 2 +- js/src/proxy/Proxy.cpp | 4 +- js/src/proxy/ScriptedDirectProxyHandler.cpp | 2 +- js/src/shell/js.cpp | 13 +- js/src/tests/ecma_6/Class/methDefn.js | 4 + js/src/tests/ecma_6/Class/methDefnGen.js | 3 + js/src/tests/ecma_6/Class/methodsPrototype.js | 45 ++ js/src/tests/lib/jittests.py | 25 +- js/src/vm/ArgumentsObject.cpp | 3 +- js/src/vm/ArrayBufferObject.cpp | 24 + js/src/vm/Debugger.cpp | 6 +- js/src/vm/GlobalObject.cpp | 2 +- js/src/vm/Interpreter-inl.h | 1 + js/src/vm/Interpreter.cpp | 5 +- js/src/vm/JSONParser.cpp | 6 +- js/src/vm/MemoryMetrics.cpp | 22 +- js/src/vm/NativeObject-inl.h | 23 - js/src/vm/NativeObject.cpp | 118 +++- js/src/vm/NativeObject.h | 39 +- js/src/vm/ObjectGroup-inl.h | 127 ++++ js/src/vm/ObjectGroup.cpp | 164 +++-- js/src/vm/ObjectGroup.h | 129 ++-- js/src/vm/ReceiverGuard.cpp | 2 +- js/src/vm/Runtime.cpp | 1 + js/src/vm/Runtime.h | 68 ++ js/src/vm/SelfHosting.cpp | 128 +--- js/src/vm/Shape.h | 10 +- js/src/vm/SharedArrayObject.cpp | 29 +- js/src/vm/SharedTypedArrayObject.cpp | 99 +++ js/src/vm/Stack.cpp | 7 +- js/src/vm/TypeInference-inl.h | 25 +- js/src/vm/TypeInference.cpp | 67 +- js/src/vm/TypeInference.h | 11 + js/src/vm/TypedArrayObject.cpp | 12 + js/src/vm/UbiNode.cpp | 11 +- js/src/vm/UnboxedObject-inl.h | 68 +- js/src/vm/UnboxedObject.cpp | 156 +++-- js/src/vm/UnboxedObject.h | 63 +- js/src/vm/Xdr.h | 2 +- js/xpconnect/idl/nsIAddonInterposition.idl | 25 +- js/xpconnect/idl/xpccomponents.idl | 8 +- js/xpconnect/src/Sandbox.cpp | 2 +- js/xpconnect/src/XPCComponents.cpp | 30 +- js/xpconnect/src/XPCJSID.cpp | 2 +- js/xpconnect/src/XPCJSRuntime.cpp | 2 +- js/xpconnect/src/XPCLocale.cpp | 2 +- js/xpconnect/src/XPCShellImpl.cpp | 2 +- js/xpconnect/src/XPCWrappedJSClass.cpp | 2 +- js/xpconnect/src/XPCWrappedNativeInfo.cpp | 2 +- js/xpconnect/src/XPCWrappedNativeJSOps.cpp | 14 +- js/xpconnect/src/XPCWrappedNativeScope.cpp | 14 +- js/xpconnect/src/nsXPConnect.cpp | 2 +- js/xpconnect/src/xpcprivate.h | 17 +- js/xpconnect/tests/unit/test_interposition.js | 24 +- js/xpconnect/wrappers/AddonWrapper.cpp | 97 ++- js/xpconnect/wrappers/AddonWrapper.h | 28 +- js/xpconnect/wrappers/XrayWrapper.cpp | 6 +- media/mtransport/dtlsidentity.cpp | 77 ++- media/mtransport/dtlsidentity.h | 54 +- media/mtransport/transportlayerdtls.cpp | 2 +- media/webrtc/moz.build | 1 - .../peerconnection/MediaPipelineFactory.cpp | 2 +- .../src/peerconnection/PeerConnectionImpl.cpp | 148 +++-- .../src/peerconnection/PeerConnectionImpl.h | 22 +- storage/mozStorageAsyncStatementJSHelper.cpp | 2 +- storage/mozStorageAsyncStatementParams.cpp | 6 +- storage/mozStorageStatementJSHelper.cpp | 8 +- storage/mozStorageStatementParams.cpp | 6 +- storage/mozStorageStatementRow.cpp | 7 +- .../addoncompat/addoncompat.manifest | 2 + .../components/addoncompat/defaultShims.js | 35 + .../addoncompat/multiprocessShims.js | 7 +- xpcom/base/CycleCollectedJSRuntime.cpp | 12 +- xpcom/base/CycleCollectedJSRuntime.h | 6 +- xpcom/base/nsCycleCollector.cpp | 2 +- .../directory/nsDirectoryViewer.cpp | 2 +- 254 files changed, 4875 insertions(+), 3534 deletions(-) create mode 100644 dom/media/tests/mochitest/test_peerConnection_certificates.html create mode 100644 js/src/jit-test/tests/basic/bug1170355.js create mode 100644 js/src/jit-test/tests/basic/bug1185653.js create mode 100644 js/src/jit-test/tests/ion/ic-fuzz-0.js create mode 100644 js/src/jit-test/tests/ion/recover-iterator-next.js create mode 100644 js/src/tests/ecma_6/Class/methodsPrototype.js create mode 100644 js/src/vm/ObjectGroup-inl.h create mode 100644 toolkit/components/addoncompat/defaultShims.js diff --git a/dom/archivereader/ArchiveRequest.cpp b/dom/archivereader/ArchiveRequest.cpp index b840a82b2d..57ddfb9022 100644 --- a/dom/archivereader/ArchiveRequest.cpp +++ b/dom/archivereader/ArchiveRequest.cpp @@ -201,7 +201,7 @@ ArchiveRequest::GetFilenamesResult(JSContext* aCx, return NS_ERROR_FAILURE; } - *aValue = OBJECT_TO_JSVAL(array); + aValue->setObject(*array); return NS_OK; } diff --git a/dom/base/EventSource.cpp b/dom/base/EventSource.cpp index 66babd66af..21af38d9f6 100644 --- a/dom/base/EventSource.cpp +++ b/dom/base/EventSource.cpp @@ -1271,7 +1271,7 @@ EventSource::DispatchAllMessageEvents() message->mData.Length()); NS_ENSURE_TRUE_VOID(jsString); - jsData = STRING_TO_JSVAL(jsString); + jsData.setString(jsString); } // create an event that uses the MessageEvent interface, diff --git a/dom/base/WebSocket.cpp b/dom/base/WebSocket.cpp index b830ffe6f0..e3b7786e16 100644 --- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -1814,7 +1814,7 @@ WebSocket::CreateAndDispatchMessageEvent(JSContext* aCx, nsresult rv = nsContentUtils::CreateArrayBuffer(aCx, aData, arrayBuf.address()); NS_ENSURE_SUCCESS(rv, rv); - jsData = OBJECT_TO_JSVAL(arrayBuf); + jsData.setObject(*arrayBuf); } else { NS_RUNTIMEABORT("Unknown binary type!"); return NS_ERROR_UNEXPECTED; @@ -1826,7 +1826,7 @@ WebSocket::CreateAndDispatchMessageEvent(JSContext* aCx, jsString = JS_NewUCStringCopyN(aCx, utf16Data.get(), utf16Data.Length()); NS_ENSURE_TRUE(jsString, NS_ERROR_FAILURE); - jsData = STRING_TO_JSVAL(jsString); + jsData.setString(jsString); } // create an event that uses the MessageEvent interface, diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 58f26004dd..cc32394b99 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -1169,10 +1169,12 @@ nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * aProto) mData, nullptr, nameSpaceManager, proto, &desc); NS_ENSURE_SUCCESS(rv, rv); - if (!contentDefinedProperty && desc.object() && !desc.value().isUndefined() && - !JS_DefineUCProperty(cx, global, mData->mNameUTF16, - NS_strlen(mData->mNameUTF16), desc)) { - return NS_ERROR_UNEXPECTED; + if (!contentDefinedProperty && desc.object() && !desc.value().isUndefined()) { + desc.attributesRef() |= JSPROP_RESOLVING; + if (!JS_DefineUCProperty(cx, global, mData->mNameUTF16, + NS_strlen(mData->mNameUTF16), desc)) { + return NS_ERROR_UNEXPECTED; + } } return NS_OK; @@ -1906,7 +1908,7 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx, } } - v = OBJECT_TO_JSVAL(dot_prototype); + v.setObject(*dot_prototype); JSAutoCompartment ac(cx, class_obj); diff --git a/dom/base/nsDOMDataChannel.cpp b/dom/base/nsDOMDataChannel.cpp index 13185d6fcd..002c6b7f88 100644 --- a/dom/base/nsDOMDataChannel.cpp +++ b/dom/base/nsDOMDataChannel.cpp @@ -392,7 +392,7 @@ nsDOMDataChannel::DoOnMessageAvailable(const nsACString& aData, JS::Rooted arrayBuf(cx); rv = nsContentUtils::CreateArrayBuffer(cx, aData, arrayBuf.address()); NS_ENSURE_SUCCESS(rv, rv); - jsData = OBJECT_TO_JSVAL(arrayBuf); + jsData.setObject(*arrayBuf); } else { NS_RUNTIMEABORT("Unknown binary type!"); return NS_ERROR_UNEXPECTED; @@ -402,7 +402,7 @@ nsDOMDataChannel::DoOnMessageAvailable(const nsACString& aData, JSString* jsString = JS_NewUCStringCopyN(cx, utf16data.get(), utf16data.Length()); NS_ENSURE_TRUE(jsString, NS_ERROR_FAILURE); - jsData = STRING_TO_JSVAL(jsString); + jsData.setString(jsString); } nsCOMPtr event; diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index dc565da584..8976b98342 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -145,6 +145,8 @@ NS_CreateJSTimeoutHandler(JSContext* aCx, nsGlobalWindow *aWindow, const nsAString& aExpression, mozilla::ErrorResult& aError); +extern const js::Class OuterWindowProxyClass; + /* * Timeout struct that holds information about each script * timeout. Holds a strong reference to an nsIScriptTimeoutHandler, which diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 3bde923ee0..2648f0251c 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -945,7 +945,7 @@ nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv) JSString *str = ::JS_NewStringCopyN(cx, data.get(), data.Length()); NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY); - *aArgv = STRING_TO_JSVAL(str); + aArgv->setString(str); break; } @@ -963,7 +963,7 @@ nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv) ::JS_NewUCStringCopyN(cx, data.get(), data.Length()); NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY); - *aArgv = STRING_TO_JSVAL(str); + aArgv->setString(str); break; } case nsISupportsPrimitive::TYPE_PRBOOL : { @@ -974,7 +974,7 @@ nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv) p->GetData(&data); - *aArgv = BOOLEAN_TO_JSVAL(data); + aArgv->setBoolean(data); break; } @@ -1025,7 +1025,7 @@ nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv) JSString *str = ::JS_NewStringCopyN(cx, &data, 1); NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY); - *aArgv = STRING_TO_JSVAL(str); + aArgv->setString(str); break; } diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 6681f0726c..4a8945a060 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -549,7 +549,7 @@ DefineConstructor(JSContext* cx, JS::Handle global, const char* name, // This is Enumerable: False per spec. return alreadyDefined || - JS_DefineProperty(cx, global, name, constructor, 0); + JS_DefineProperty(cx, global, name, constructor, JSPROP_RESOLVING); } static JSObject* @@ -2407,7 +2407,7 @@ IsInCertifiedApp(JSContext* aCx, JSObject* aObj) #ifdef DEBUG void VerifyTraceProtoAndIfaceCacheCalled(JS::CallbackTracer *trc, void **thingp, - JSGCTraceKind kind) + JS::TraceKind kind) { // We don't do anything here, we only want to verify that // TraceProtoAndIfaceCache was called. diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index c9bf8be8d2..4ddcc6439a 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -522,7 +522,7 @@ AllocateProtoAndIfaceCache(JSObject* obj, ProtoAndIfaceCache::Kind aKind) #ifdef DEBUG void VerifyTraceProtoAndIfaceCacheCalled(JS::CallbackTracer *trc, void **thingp, - JSGCTraceKind kind); + JS::TraceKind kind); struct VerifyTraceProtoAndIfaceCacheCalledTracer : public JS::CallbackTracer { @@ -3035,7 +3035,7 @@ CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache, JSAutoCompartment ac(aCx, aGlobal); { - js::SetReservedSlot(aGlobal, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aNative)); + js::SetReservedSlot(aGlobal, DOM_OBJECT_SLOT, JS::PrivateValue(aNative)); NS_ADDREF(aNative); aCache->SetWrapper(aGlobal); diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index acafb5a95c..2ec3b3d327 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1081,8 +1081,7 @@ class CGHeaders(CGWrapper): elif unrolled.isDictionary(): headerSet.add(self.getDeclarationFilename(unrolled.inner)) elif unrolled.isCallback(): - # Callbacks are both a type and an object - headerSet.add(self.getDeclarationFilename(unrolled)) + headerSet.add(self.getDeclarationFilename(unrolled.callback)) elif unrolled.isFloat() and not unrolled.isUnrestricted(): # Restricted floats are tested for finiteness bindingHeaders.add("mozilla/FloatingPoint.h") @@ -1130,6 +1129,9 @@ class CGHeaders(CGWrapper): for m in desc.interface.members: addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func")) + staticTypeOverride = PropertyDefiner.getStringAttr(m, "StaticClassOverride") + if staticTypeOverride: + bindingHeaders.add("/".join(staticTypeOverride.split("::")) + ".h") # getExtendedAttribute() returns a list, extract the entry. funcList = desc.interface.getExtendedAttribute("Func") if funcList is not None: @@ -1271,7 +1273,7 @@ def UnionTypes(unionTypes, config): # Callbacks always use strong refs, so we need to include # the right header to be able to Release() in our inlined # code. - headers.add(CGHeaders.getDeclarationFilename(f)) + headers.add(CGHeaders.getDeclarationFilename(f.callback)) elif f.isMozMap(): headers.add("mozilla/dom/MozMap.h") # And add headers for the type we're parametrized over @@ -4704,7 +4706,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, extraConditionForNull=extraConditionForNull) elif (not type.hasNullableType and defaultValue and isinstance(defaultValue, IDLNullValue)): - assert type.hasDictionaryType + assert type.hasDictionaryType() assert defaultValue.type.isDictionary() if not isOwningUnion and typeNeedsRooting(defaultValue.type): ctorArgs = "cx" @@ -5062,7 +5064,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, assert not type.treatNonObjectAsNull() or type.nullable() assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull() - name = type.unroll().identifier.name + callback = type.unroll().callback + name = callback.identifier.name if type.nullable(): declType = CGGeneric("nsRefPtr<%s>" % name) else: @@ -6165,7 +6168,7 @@ def getRetvalDeclarationForType(returnType, descriptorProvider, result = CGGeneric("auto") return result, None, None, None, conversion if returnType.isCallback(): - name = returnType.unroll().identifier.name + name = returnType.unroll().callback.identifier.name return CGGeneric("nsRefPtr<%s>" % name), None, None, None, None if returnType.isAny(): if isMember: @@ -6629,8 +6632,11 @@ class CGPerSignatureCall(CGThing): argsPre = [] if static: - nativeMethodName = "%s::%s" % (descriptor.nativeType, - nativeMethodName) + nativeType = descriptor.nativeType + staticTypeOverride = PropertyDefiner.getStringAttr(idlNode, "StaticClassOverride") + if (staticTypeOverride): + nativeType = staticTypeOverride + nativeMethodName = "%s::%s" % (nativeType, nativeMethodName) # If we're a constructor, "obj" may not be a function, so calling # XrayAwareCalleeGlobal() on it is not safe. Of course in the # constructor case either "obj" is an Xray or we're already in the @@ -6667,9 +6673,10 @@ class CGPerSignatureCall(CGThing): needsUnwrappedVar = False unwrappedVar = "obj" elif descriptor.interface.isJSImplemented(): - needsUnwrap = True - needsUnwrappedVar = True - argsPost.append("js::GetObjectCompartment(unwrappedObj ? *unwrappedObj : obj)") + if not idlNode.isStatic(): + needsUnwrap = True + needsUnwrappedVar = True + argsPost.append("js::GetObjectCompartment(unwrappedObj ? *unwrappedObj : obj)") elif needScopeObject(returnType, arguments, self.extendedAttributes, descriptor.wrapperCache, True, idlNode.getExtendedAttribute("StoreInSlot")): @@ -7732,9 +7739,11 @@ class CGResolveHook(CGAbstractClassHook): // If desc.value() is undefined, then the DoResolve call // has already defined it on the object. Don't try to also // define it. - if (!desc.value().isUndefined() && - !JS_DefinePropertyById(cx, obj, id, desc)) { - return false; + if (!desc.value().isUndefined()) { + desc.attributesRef() |= JSPROP_RESOLVING; + if (!JS_DefinePropertyById(cx, obj, id, desc)) { + return false; + } } *resolvedp = true; return true; @@ -8571,6 +8580,26 @@ class CGStaticMethodJitinfo(CGGeneric): IDLToCIdentifier(method.identifier.name)))) +class CGMethodIdentityTest(CGAbstractMethod): + """ + A class to generate a method-identity test for a given IDL operation. + """ + def __init__(self, descriptor, method): + self.method = method + name = "Is%sMethod" % MakeNativeName(method.identifier.name) + CGAbstractMethod.__init__(self, descriptor, name, 'bool', + [Argument('JS::Handle', 'aObj')]) + + def definition_body(self): + return dedent( + """ + MOZ_ASSERT(aObj); + return js::IsFunctionObject(aObj) && + js::FunctionObjectIsNative(aObj) && + FUNCTION_VALUE_TO_JITINFO(JS::ObjectValue(*aObj)) == &%s_methodinfo; + """ % IDLToCIdentifier(self.method.identifier.name)) + + def getEnumValueName(value): # Some enum values can be empty strings. Others might have weird # characters in them. Deal with the former by returning "_empty", @@ -8694,7 +8723,7 @@ def getUnionAccessorSignatureType(type, descriptorProvider): return CGGeneric(type.inner.identifier.name) if type.isCallback(): - return CGGeneric("%s&" % type.unroll().identifier.name) + return CGGeneric("%s&" % type.unroll().callback.identifier.name) if type.isAny(): return CGGeneric("JS::Value") @@ -10900,7 +10929,7 @@ class CGDOMJSProxyHandler_getElements(ClassMethod): 'jsvalRef': 'temp', 'jsvalHandle': '&temp', 'obj': 'proxy', - 'successCode': ("adder->append(cx, temp);\n" + 'successCode': ("if (!adder->append(cx, temp)) return false;\n" "continue;\n") } get = CGProxyIndexedGetter(self.descriptor, templateValues, False, False).define() @@ -11171,6 +11200,8 @@ class CGDescriptor(CGThing): cgThings.append(CGMemberJITInfo(descriptor, m)) if props.isCrossOriginMethod: crossOriginMethods.add(m.identifier.name) + if m.getExtendedAttribute("MethodIdentityTestable"): + cgThings.append(CGMethodIdentityTest(descriptor, m)) elif m.isAttr(): if m.stringifier: raise TypeError("Stringifier attributes not supported yet. " @@ -12341,7 +12372,7 @@ class CGForwardDeclarations(CGWrapper): # Note: Spidermonkey interfaces are typedefs, so can't be # forward-declared elif t.isCallback(): - builder.addInMozillaDom(str(t)) + builder.addInMozillaDom(t.callback.identifier.name) elif t.isDictionary(): builder.addInMozillaDom(t.inner.identifier.name, isStruct=True) elif t.isCallbackInterface(): @@ -12368,12 +12399,12 @@ class CGForwardDeclarations(CGWrapper): builder.add(d.nativeType + "Atoms", isStruct=True) for callback in mainCallbacks: - forwardDeclareForType(callback) + builder.addInMozillaDom(callback.identifier.name) for t in getTypesFromCallback(callback): forwardDeclareForType(t, workerness='mainthreadonly') for callback in workerCallbacks: - forwardDeclareForType(callback) + builder.addInMozillaDom(callback.identifier.name) for t in getTypesFromCallback(callback): forwardDeclareForType(t, workerness='workeronly') @@ -12756,7 +12787,7 @@ class CGNativeMember(ClassMethod): # .forget() to get our already_AddRefed. return result.define(), "nullptr", "return ${declName}.forget();\n" if type.isCallback(): - return ("already_AddRefed<%s>" % type.unroll().identifier.name, + return ("already_AddRefed<%s>" % type.unroll().callback.identifier.name, "nullptr", "return ${declName}.forget();\n") if type.isAny(): if isMember: @@ -12986,7 +13017,7 @@ class CGNativeMember(ClassMethod): else: declType = "%s&" if type.isCallback(): - name = type.unroll().identifier.name + name = type.unroll().callback.identifier.name else: name = type.unroll().inner.identifier.name return declType % name, False, False @@ -13100,7 +13131,7 @@ class CGBindingImplClass(CGClass): """ Common codegen for generating a C++ implementation of a WebIDL interface """ - def __init__(self, descriptor, cgMethod, cgGetter, cgSetter, wantGetParent=True, wrapMethodName="WrapObject"): + def __init__(self, descriptor, cgMethod, cgGetter, cgSetter, wantGetParent=True, wrapMethodName="WrapObject", skipStaticMethods=False): """ cgMethod, cgGetter and cgSetter are classes used to codegen methods, getters and setters. @@ -13131,7 +13162,8 @@ class CGBindingImplClass(CGClass): if m.isMethod(): if m.isIdentifierLess(): continue - appendMethod(m) + if not m.isStatic() or not skipStaticMethods: + appendMethod(m) elif m.isAttr(): self.methodDecls.append(cgGetter(descriptor, m)) if not m.readonly: @@ -13592,7 +13624,7 @@ class CGJSImplSetter(CGJSImplMember): class CGJSImplClass(CGBindingImplClass): def __init__(self, descriptor): - CGBindingImplClass.__init__(self, descriptor, CGJSImplMethod, CGJSImplGetter, CGJSImplSetter) + CGBindingImplClass.__init__(self, descriptor, CGJSImplMethod, CGJSImplGetter, CGJSImplSetter, skipStaticMethods=True) if descriptor.interface.parent: parentClass = descriptor.getDescriptor( diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index d1f9d934a4..3588776a9b 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -47,11 +47,10 @@ class Configuration: "%s\n" "%s" % (thing.location, thing.implementor.location)) - # Some toplevel things are sadly types, and those have an - # isInterface that doesn't mean the same thing as IDLObject's - # isInterface()... - if (not isinstance(thing, IDLInterface) and - not isinstance(thing, IDLExternalInterface)): + + assert not thing.isType(); + + if not thing.isInterface(): continue iface = thing self.interfaces[iface.identifier.name] = iface @@ -788,9 +787,9 @@ def findCallbacksAndDictionaries(inputTypes): def doFindCallbacksAndDictionaries(types, callbacks, dictionaries): unhandledTypes = set() for type in types: - if type.isCallback() and type not in callbacks: - unhandledTypes |= getFlatTypes(getTypesFromCallback(type)) - callbacks.add(type) + if type.isCallback() and type.callback not in callbacks: + unhandledTypes |= getFlatTypes(getTypesFromCallback(type.callback)) + callbacks.add(type.callback) elif type.isDictionary() and type.inner not in dictionaries: d = type.inner unhandledTypes |= getFlatTypes(getTypesFromDictionary(d)) diff --git a/dom/bindings/DOMJSProxyHandler.cpp b/dom/bindings/DOMJSProxyHandler.cpp index 4f93e0fbe2..decd668617 100644 --- a/dom/bindings/DOMJSProxyHandler.cpp +++ b/dom/bindings/DOMJSProxyHandler.cpp @@ -210,12 +210,12 @@ DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle proxy, JS:: return result.succeed(); } - JSObject* expando = EnsureExpandoObject(cx, proxy); + JS::Rooted expando(cx, EnsureExpandoObject(cx, proxy)); if (!expando) { return false; } - if (!js::DefineOwnProperty(cx, expando, id, desc, result)) { + if (!JS_DefinePropertyById(cx, expando, id, desc, result)) { return false; } *defined = true; diff --git a/dom/bindings/TypedArray.h b/dom/bindings/TypedArray.h index cc424c4773..b721e8ec21 100644 --- a/dom/bindings/TypedArray.h +++ b/dom/bindings/TypedArray.h @@ -223,6 +223,39 @@ typedef TypedArray ArrayBuffer; +typedef TypedArray + SharedInt8Array; +typedef TypedArray + SharedUint8Array; +typedef TypedArray + SharedUint8ClampedArray; +typedef TypedArray + SharedInt16Array; +typedef TypedArray + SharedUint16Array; +typedef TypedArray + SharedInt32Array; +typedef TypedArray + SharedUint32Array; +typedef TypedArray + SharedFloat32Array; +typedef TypedArray + SharedFloat64Array; +typedef TypedArray_base + SharedArrayBufferView; +typedef TypedArray + SharedArrayBuffer; + // A class for converting an nsTArray to a TypedArray // Note: A TypedArrayCreator must not outlive the nsTArray it was created from. // So this is best used to pass from things that understand nsTArray to diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 50a8dccfa8..e25e901b60 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -164,6 +164,9 @@ class IDLObject(object): def isUnion(self): return False + def isTypedef(self): + return False + def getUserData(self, key, default): return self.userData.get(key, default) @@ -1429,6 +1432,14 @@ class IDLDictionary(IDLObjectWithScope): def isDictionary(self): return True; + def canBeEmpty(self): + """ + Returns true if this dictionary can be empty (that is, it has no + required members and neither do any of its ancestors). + """ + return (all(member.optional for member in self.members) and + (not self.parent or self.parent.canBeEmpty())) + def finish(self, scope): if self._finished: return @@ -1676,9 +1687,18 @@ class IDLType(IDLObject): def isArrayBufferView(self): return False + def isSharedArrayBuffer(self): + return False + + def isSharedArrayBufferView(self): + return False + def isTypedArray(self): return False + def isSharedTypedArray(self): + return False + def isCallbackInterface(self): return False @@ -1698,7 +1718,10 @@ class IDLType(IDLObject): only returns true for the types from the TypedArray spec. """ return self.isInterface() and (self.isArrayBuffer() or \ self.isArrayBufferView() or \ - self.isTypedArray()) + self.isSharedArrayBuffer() or \ + self.isSharedArrayBufferView() or \ + self.isTypedArray() or \ + self.isSharedTypedArray()) def isDictionary(self): return False @@ -1739,11 +1762,11 @@ class IDLType(IDLObject): def treatNonCallableAsNull(self): assert self.tag() == IDLType.Tags.callback - return self.nullable() and self.inner._treatNonCallableAsNull + return self.nullable() and self.inner.callback._treatNonCallableAsNull def treatNonObjectAsNull(self): assert self.tag() == IDLType.Tags.callback - return self.nullable() and self.inner._treatNonObjectAsNull + return self.nullable() and self.inner.callback._treatNonObjectAsNull def addExtendedAttributes(self, attrs): assert len(attrs) == 0 @@ -1783,11 +1806,17 @@ class IDLUnresolvedType(IDLType): assert obj if obj.isType(): - # obj itself might not be complete; deal with that. - assert obj != self - if not obj.isComplete(): - obj = obj.complete(scope) - return obj + print obj + assert not obj.isType() + if obj.isTypedef(): + assert self.name.name == obj.identifier.name + typedefType = IDLTypedefType(self.location, obj.innerType, + obj.identifier) + assert not typedefType.isComplete() + return typedefType.complete(scope) + elif obj.isCallback() and not obj.isInterface(): + assert self.name.name == obj.identifier.name + return IDLCallbackType(self.location, obj) if self._promiseInnerType and not self._promiseInnerType.isComplete(): self._promiseInnerType = self._promiseInnerType.complete(scope) @@ -1874,9 +1903,18 @@ class IDLNullableType(IDLType): def isArrayBufferView(self): return self.inner.isArrayBufferView() + def isSharedArrayBuffer(self): + return self.inner.isSharedArrayBuffer() + + def isSharedArrayBufferView(self): + return self.inner.isSharedArrayBufferView() + def isTypedArray(self): return self.inner.isTypedArray() + def isSharedTypedArray(self): + return self.inner.isSharedTypedArray() + def isDictionary(self): return self.inner.isDictionary() @@ -2091,7 +2129,7 @@ class IDLUnionType(IDLType): IDLType.__init__(self, location, "") self.memberTypes = memberTypes self.hasNullableType = False - self.hasDictionaryType = False + self._dictionaryType = None self.flatMemberTypes = None self.builtin = False @@ -2147,10 +2185,10 @@ class IDLUnionType(IDLType): if self.hasNullableType: raise WebIDLError("Can't have more than one nullable types in a union", [nullableType.location, self.flatMemberTypes[i].location]) - if self.hasDictionaryType: + if self.hasDictionaryType(): raise WebIDLError("Can't have a nullable type and a " "dictionary type in a union", - [dictionaryType.location, + [self._dictionaryType.location, self.flatMemberTypes[i].location]) self.hasNullableType = True nullableType = self.flatMemberTypes[i] @@ -2162,8 +2200,7 @@ class IDLUnionType(IDLType): "dictionary type in a union", [nullableType.location, self.flatMemberTypes[i].location]) - self.hasDictionaryType = True - dictionaryType = self.flatMemberTypes[i] + self._dictionaryType = self.flatMemberTypes[i] elif self.flatMemberTypes[i].isUnion(): self.flatMemberTypes[i:i + 1] = self.flatMemberTypes[i].memberTypes continue @@ -2202,6 +2239,13 @@ class IDLUnionType(IDLType): return False return True + def hasDictionaryType(self): + return self._dictionaryType is not None + + def hasPossiblyEmptyDictionaryType(self): + return (self._dictionaryType is not None and + self._dictionaryType.inner.canBeEmpty()) + def _getDependentObjects(self): return set(self.memberTypes) @@ -2302,23 +2346,17 @@ class IDLArrayType(IDLType): def _getDependentObjects(self): return self.inner._getDependentObjects() -class IDLTypedefType(IDLType, IDLObjectWithIdentifier): +class IDLTypedefType(IDLType): def __init__(self, location, innerType, name): - IDLType.__init__(self, location, innerType.name) - - identifier = IDLUnresolvedIdentifier(location, name) - - IDLObjectWithIdentifier.__init__(self, location, None, identifier) - + IDLType.__init__(self, location, name) self.inner = innerType - self.name = name self.builtin = False def __eq__(self, other): return isinstance(other, IDLTypedefType) and self.inner == other.inner def __str__(self): - return self.identifier.name + return self.name def nullable(self): return self.inner.nullable() @@ -2365,9 +2403,18 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier): def isArrayBufferView(self): return self.inner.isArrayBufferView() + def isSharedArrayBuffer(self): + return self.inner.isSharedArrayBuffer() + + def isSharedArrayBufferView(self): + return self.inner.isSharedArrayBufferView() + def isTypedArray(self): return self.inner.isTypedArray() + def isSharedTypedArray(self): + return self.inner.isSharedTypedArray() + def isInterface(self): return self.inner.isInterface() @@ -2386,16 +2433,6 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier): assert self.inner.isComplete() return self.inner - def finish(self, parentScope): - # Maybe the IDLObjectWithIdentifier for the typedef should be - # a separate thing from the type? If that happens, we can - # remove some hackery around avoiding isInterface() in - # Configuration.py. - self.complete(parentScope) - - def validate(self): - pass - # Do we need a resolveType impl? I don't think it's particularly useful.... def tag(self): @@ -2410,6 +2447,31 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier): def _getDependentObjects(self): return self.inner._getDependentObjects() +class IDLTypedef(IDLObjectWithIdentifier): + def __init__(self, location, parentScope, innerType, name): + identifier = IDLUnresolvedIdentifier(location, name) + IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier) + self.innerType = innerType + + def __str__(self): + return "Typedef %s %s" % (self.identifier.name, self.innerType) + + def finish(self, parentScope): + if not self.innerType.isComplete(): + self.innerType = self.innerType.complete(parentScope) + + def validate(self): + pass + + def isTypedef(self): + return True + + def addExtendedAttributes(self, attrs): + assert len(attrs) == 0 + + def _getDependentObjects(self): + return self.innerType._getDependentObjects() + class IDLWrapperType(IDLType): def __init__(self, location, inner, promiseInnerType=None): IDLType.__init__(self, location, inner.identifier.name) @@ -2610,6 +2672,8 @@ class IDLBuiltinType(IDLType): # Funny stuff 'ArrayBuffer', 'ArrayBufferView', + 'SharedArrayBuffer', + 'SharedArrayBufferView', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray', @@ -2618,7 +2682,16 @@ class IDLBuiltinType(IDLType): 'Int32Array', 'Uint32Array', 'Float32Array', - 'Float64Array' + 'Float64Array', + 'SharedInt8Array', + 'SharedUint8Array', + 'SharedUint8ClampedArray', + 'SharedInt16Array', + 'SharedUint16Array', + 'SharedInt32Array', + 'SharedUint32Array', + 'SharedFloat32Array', + 'SharedFloat64Array' ) TagLookup = { @@ -2644,6 +2717,8 @@ class IDLBuiltinType(IDLType): Types.void: IDLType.Tags.void, Types.ArrayBuffer: IDLType.Tags.interface, Types.ArrayBufferView: IDLType.Tags.interface, + Types.SharedArrayBuffer: IDLType.Tags.interface, + Types.SharedArrayBufferView: IDLType.Tags.interface, Types.Int8Array: IDLType.Tags.interface, Types.Uint8Array: IDLType.Tags.interface, Types.Uint8ClampedArray: IDLType.Tags.interface, @@ -2652,7 +2727,16 @@ class IDLBuiltinType(IDLType): Types.Int32Array: IDLType.Tags.interface, Types.Uint32Array: IDLType.Tags.interface, Types.Float32Array: IDLType.Tags.interface, - Types.Float64Array: IDLType.Tags.interface + Types.Float64Array: IDLType.Tags.interface, + Types.SharedInt8Array: IDLType.Tags.interface, + Types.SharedUint8Array: IDLType.Tags.interface, + Types.SharedUint8ClampedArray: IDLType.Tags.interface, + Types.SharedInt16Array: IDLType.Tags.interface, + Types.SharedUint16Array: IDLType.Tags.interface, + Types.SharedInt32Array: IDLType.Tags.interface, + Types.SharedUint32Array: IDLType.Tags.interface, + Types.SharedFloat32Array: IDLType.Tags.interface, + Types.SharedFloat64Array: IDLType.Tags.interface } def __init__(self, location, name, type): @@ -2692,17 +2776,30 @@ class IDLBuiltinType(IDLType): def isArrayBufferView(self): return self._typeTag == IDLBuiltinType.Types.ArrayBufferView + def isSharedArrayBuffer(self): + return self._typeTag == IDLBuiltinType.Types.SharedArrayBuffer + + def isSharedArrayBufferView(self): + return self._typeTag == IDLBuiltinType.Types.SharedArrayBufferView + def isTypedArray(self): return self._typeTag >= IDLBuiltinType.Types.Int8Array and \ self._typeTag <= IDLBuiltinType.Types.Float64Array + def isSharedTypedArray(self): + return self._typeTag >= IDLBuiltinType.Types.SharedInt8Array and \ + self._typeTag <= IDLBuiltinType.Types.SharedFloat64Array + def isInterface(self): # TypedArray things are interface types per the TypedArray spec, # but we handle them as builtins because SpiderMonkey implements # all of it internally. return self.isArrayBuffer() or \ self.isArrayBufferView() or \ - self.isTypedArray() + self.isSharedArrayBuffer() or \ + self.isSharedArrayBufferView() or \ + self.isTypedArray() or \ + self.isSharedTypedArray() def isNonCallbackInterface(self): # All the interfaces we can be are non-callback @@ -2720,7 +2817,7 @@ class IDLBuiltinType(IDLType): self._typeTag == IDLBuiltinType.Types.unrestricted_double def isSerializable(self): - return self.isPrimitive() or self.isDOMString() or self.isDate() + return self.isPrimitive() or self.isString() or self.isDate() def includesRestrictedFloat(self): return self.isFloat() and not self.isUnrestricted() @@ -2773,15 +2870,20 @@ class IDLBuiltinType(IDLType): # ArrayBuffer is distinguishable from everything # that's not an ArrayBuffer or a callback interface (self.isArrayBuffer() and not other.isArrayBuffer()) or + (self.isSharedArrayBuffer() and not other.isSharedArrayBuffer()) or # ArrayBufferView is distinguishable from everything # that's not an ArrayBufferView or typed array. (self.isArrayBufferView() and not other.isArrayBufferView() and not other.isTypedArray()) or + (self.isSharedArrayBufferView() and not other.isSharedArrayBufferView() and + not other.isSharedTypedArray()) or # Typed arrays are distinguishable from everything # except ArrayBufferView and the same type of typed # array (self.isTypedArray() and not other.isArrayBufferView() and not - (other.isTypedArray() and other.name == self.name))))) + (other.isTypedArray() and other.name == self.name)) or + (self.isSharedTypedArray() and not other.isSharedArrayBufferView() and not + (other.isSharedTypedArray() and other.name == self.name))))) def _getDependentObjects(self): return set() @@ -2853,6 +2955,12 @@ BuiltinTypes = { IDLBuiltinType.Types.ArrayBufferView: IDLBuiltinType(BuiltinLocation(""), "ArrayBufferView", IDLBuiltinType.Types.ArrayBufferView), + IDLBuiltinType.Types.SharedArrayBuffer: + IDLBuiltinType(BuiltinLocation(""), "SharedArrayBuffer", + IDLBuiltinType.Types.SharedArrayBuffer), + IDLBuiltinType.Types.SharedArrayBufferView: + IDLBuiltinType(BuiltinLocation(""), "SharedArrayBufferView", + IDLBuiltinType.Types.SharedArrayBufferView), IDLBuiltinType.Types.Int8Array: IDLBuiltinType(BuiltinLocation(""), "Int8Array", IDLBuiltinType.Types.Int8Array), @@ -2879,7 +2987,34 @@ BuiltinTypes = { IDLBuiltinType.Types.Float32Array), IDLBuiltinType.Types.Float64Array: IDLBuiltinType(BuiltinLocation(""), "Float64Array", - IDLBuiltinType.Types.Float64Array) + IDLBuiltinType.Types.Float64Array), + IDLBuiltinType.Types.SharedInt8Array: + IDLBuiltinType(BuiltinLocation(""), "SharedInt8Array", + IDLBuiltinType.Types.SharedInt8Array), + IDLBuiltinType.Types.SharedUint8Array: + IDLBuiltinType(BuiltinLocation(""), "SharedUint8Array", + IDLBuiltinType.Types.SharedUint8Array), + IDLBuiltinType.Types.SharedUint8ClampedArray: + IDLBuiltinType(BuiltinLocation(""), "SharedUint8ClampedArray", + IDLBuiltinType.Types.SharedUint8ClampedArray), + IDLBuiltinType.Types.SharedInt16Array: + IDLBuiltinType(BuiltinLocation(""), "SharedInt16Array", + IDLBuiltinType.Types.SharedInt16Array), + IDLBuiltinType.Types.SharedUint16Array: + IDLBuiltinType(BuiltinLocation(""), "SharedUint16Array", + IDLBuiltinType.Types.SharedUint16Array), + IDLBuiltinType.Types.SharedInt32Array: + IDLBuiltinType(BuiltinLocation(""), "SharedInt32Array", + IDLBuiltinType.Types.SharedInt32Array), + IDLBuiltinType.Types.SharedUint32Array: + IDLBuiltinType(BuiltinLocation(""), "SharedUint32Array", + IDLBuiltinType.Types.SharedUint32Array), + IDLBuiltinType.Types.SharedFloat32Array: + IDLBuiltinType(BuiltinLocation(""), "SharedFloat32Array", + IDLBuiltinType.Types.SharedFloat32Array), + IDLBuiltinType.Types.SharedFloat64Array: + IDLBuiltinType(BuiltinLocation(""), "SharedFloat64Array", + IDLBuiltinType.Types.SharedFloat64Array) } @@ -2994,14 +3129,14 @@ class IDLNullValue(IDLObject): def coerceToType(self, type, location): if (not isinstance(type, IDLNullableType) and not (type.isUnion() and type.hasNullableType) and - not (type.isUnion() and type.hasDictionaryType) and + not (type.isUnion() and type.hasDictionaryType()) and not type.isDictionary() and not type.isAny()): raise WebIDLError("Cannot coerce null value to type %s." % type, [location]) nullValue = IDLNullValue(self.location) - if type.isUnion() and not type.nullable() and type.hasDictionaryType: + if type.isUnion() and not type.nullable() and type.hasDictionaryType(): # We're actually a default value for the union's dictionary member. # Use its type. for t in type.flatMemberTypes: @@ -3131,6 +3266,12 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins): "That seems rather unlikely.", [self.location]) + if self.getExtendedAttribute("NewObject"): + if self.dependsOn == "Nothing" or self.dependsOn == "DOMState": + raise WebIDLError("A [NewObject] method is not idempotent, " + "so it has to depend on something other than DOM state.", + [self.location]) + def _setDependsOn(self, dependsOn): if self.dependsOn != "Everything": raise WebIDLError("Trying to specify multiple different DependsOn, " @@ -3568,7 +3709,7 @@ class IDLArgument(IDLObjectWithIdentifier): self.type = type if ((self.type.isDictionary() or - self.type.isUnion() and self.type.unroll().hasDictionaryType) and + self.type.isUnion() and self.type.unroll().hasDictionaryType()) and self.optional and not self.defaultValue and not self.variadic): # Default optional non-variadic dictionaries to null, # for simplicity, so the codegen doesn't have to special-case this. @@ -3601,12 +3742,10 @@ class IDLArgument(IDLObjectWithIdentifier): def canHaveMissingValue(self): return self.optional and not self.defaultValue -class IDLCallbackType(IDLType, IDLObjectWithScope): +class IDLCallback(IDLObjectWithScope): def __init__(self, location, parentScope, identifier, returnType, arguments): assert isinstance(returnType, IDLType) - IDLType.__init__(self, location, identifier.name) - self._returnType = returnType # Clone the list self._arguments = list(arguments) @@ -3626,9 +3765,6 @@ class IDLCallbackType(IDLType, IDLObjectWithScope): def signatures(self): return [(self._returnType, self._arguments)] - def tag(self): - return IDLType.Tags.callback - def finish(self, scope): if not self._returnType.isComplete(): type = self._returnType.complete(scope) @@ -3652,14 +3788,6 @@ class IDLCallbackType(IDLType, IDLObjectWithScope): def validate(self): pass - def isDistinguishableFrom(self, other): - if other.isUnion(): - # Just forward to the union; it'll deal - return other.isDistinguishableFrom(self) - return (other.isPrimitive() or other.isString() or other.isEnum() or - other.isNonCallbackInterface() or other.isDate() or - other.isSequence()) - def addExtendedAttributes(self, attrs): unhandledAttrs = [] for attr in attrs: @@ -3678,6 +3806,28 @@ class IDLCallbackType(IDLType, IDLObjectWithScope): def _getDependentObjects(self): return set([self._returnType] + self._arguments) +class IDLCallbackType(IDLType): + def __init__(self, location, callback): + IDLType.__init__(self, location, callback.identifier.name) + self.callback = callback + + def isCallback(self): + return True + + def tag(self): + return IDLType.Tags.callback + + def isDistinguishableFrom(self, other): + if other.isUnion(): + # Just forward to the union; it'll deal + return other.isDistinguishableFrom(self) + return (other.isPrimitive() or other.isString() or other.isEnum() or + other.isNonCallbackInterface() or other.isDate() or + other.isSequence()) + + def _getDependentObjects(self): + return self.callback._getDependentObjects() + class IDLMethodOverload: """ A class that represents a single overload of a WebIDL method. This is not @@ -3889,45 +4039,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope): def finish(self, scope): IDLInterfaceMember.finish(self, scope) - overloadWithPromiseReturnType = None - overloadWithoutPromiseReturnType = None for overload in self._overloads: - variadicArgument = None - - arguments = overload.arguments - for (idx, argument) in enumerate(arguments): - if not argument.isComplete(): - argument.complete(scope) - assert argument.type.isComplete() - - if (argument.type.isDictionary() or - (argument.type.isUnion() and - argument.type.unroll().hasDictionaryType)): - # Dictionaries and unions containing dictionaries at the - # end of the list or followed by optional arguments must be - # optional. - if (not argument.optional and - all(arg.optional for arg in arguments[idx+1:])): - raise WebIDLError("Dictionary argument or union " - "argument containing a dictionary " - "not followed by a required argument " - "must be optional", - [argument.location]) - - # An argument cannot be a Nullable Dictionary - if argument.type.nullable(): - raise WebIDLError("An argument cannot be a nullable " - "dictionary or nullable union " - "containing a dictionary", - [argument.location]) - - # Only the last argument can be variadic - if variadicArgument: - raise WebIDLError("Variadic argument is not last argument", - [variadicArgument.location]) - if argument.variadic: - variadicArgument = argument - returnType = overload.returnType if not returnType.isComplete(): returnType = returnType.complete(scope) @@ -3936,22 +4048,10 @@ class IDLMethod(IDLInterfaceMember, IDLScope): assert not isinstance(returnType.name, IDLUnresolvedIdentifier) overload.returnType = returnType - if returnType.isPromise(): - overloadWithPromiseReturnType = overload - else: - overloadWithoutPromiseReturnType = overload - - # Make sure either all our overloads return Promises or none do - if overloadWithPromiseReturnType and overloadWithoutPromiseReturnType: - raise WebIDLError("We have overloads with both Promise and " - "non-Promise return types", - [overloadWithPromiseReturnType.location, - overloadWithoutPromiseReturnType.location]) - - if overloadWithPromiseReturnType and self._legacycaller: - raise WebIDLError("May not have a Promise return type for a " - "legacycaller.", - [overloadWithPromiseReturnType.location]) + for argument in overload.arguments: + if not argument.isComplete(): + argument.complete(scope) + assert argument.type.isComplete() # Now compute various information that will be used by the # WebIDL overload resolution algorithm. @@ -3981,12 +4081,73 @@ class IDLMethod(IDLInterfaceMember, IDLScope): distinguishingIndex), [self.location, overload.location]) + overloadWithPromiseReturnType = None + overloadWithoutPromiseReturnType = None for overload in self._overloads: - if not overload.returnType.unroll().isExposedInAllOf(self.exposureSet): + returnType = overload.returnType + if not returnType.unroll().isExposedInAllOf(self.exposureSet): raise WebIDLError("Overload returns a type that is not exposed " "everywhere where the method is exposed", [overload.location]) + variadicArgument = None + + arguments = overload.arguments + for (idx, argument) in enumerate(arguments): + assert argument.type.isComplete() + + if ((argument.type.isDictionary() and + argument.type.inner.canBeEmpty())or + (argument.type.isUnion() and + argument.type.unroll().hasPossiblyEmptyDictionaryType())): + # Optional dictionaries and unions containing optional + # dictionaries at the end of the list or followed by + # optional arguments must be optional. + if (not argument.optional and + all(arg.optional for arg in arguments[idx+1:])): + raise WebIDLError("Dictionary argument or union " + "argument containing a dictionary " + "not followed by a required argument " + "must be optional", + [argument.location]) + + # An argument cannot be a Nullable Dictionary + if argument.type.nullable(): + raise WebIDLError("An argument cannot be a nullable " + "dictionary or nullable union " + "containing a dictionary", + [argument.location]) + + # Only the last argument can be variadic + if variadicArgument: + raise WebIDLError("Variadic argument is not last argument", + [variadicArgument.location]) + if argument.variadic: + variadicArgument = argument + + if returnType.isPromise(): + overloadWithPromiseReturnType = overload + else: + overloadWithoutPromiseReturnType = overload + + # Make sure either all our overloads return Promises or none do + if overloadWithPromiseReturnType and overloadWithoutPromiseReturnType: + raise WebIDLError("We have overloads with both Promise and " + "non-Promise return types", + [overloadWithPromiseReturnType.location, + overloadWithoutPromiseReturnType.location]) + + if overloadWithPromiseReturnType and self._legacycaller: + raise WebIDLError("May not have a Promise return type for a " + "legacycaller.", + [overloadWithPromiseReturnType.location]) + + if self.getExtendedAttribute("StaticClassOverride") and not \ + (self.identifier.scope.isJSImplemented() and self.isStatic()): + raise WebIDLError("StaticClassOverride can be applied to static" + " methods on JS-implemented classes only.", + [self.location]) + def overloadsForArgCount(self, argc): return [overload for overload in self._overloads if len(overload.arguments) == argc or @@ -4106,7 +4267,9 @@ class IDLMethod(IDLInterfaceMember, IDLScope): identifier == "Func" or identifier == "AvailableIn" or identifier == "CheckPermissions" or - identifier == "BinaryName"): + identifier == "BinaryName" or + identifier == "MethodIdentityTestable" or + identifier == "StaticClassOverride"): # Known attributes that we don't need to do anything with here pass else: @@ -4322,6 +4485,7 @@ class Tokenizer(object): "<": "LT", ">": "GT", "ArrayBuffer": "ARRAYBUFFER", + "SharedArrayBuffer": "SHAREDARRAYBUFFER", "or": "OR" } @@ -4680,8 +4844,8 @@ class Parser(Tokenizer): CallbackRest : IDENTIFIER EQUALS ReturnType LPAREN ArgumentList RPAREN SEMICOLON """ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1]) - p[0] = IDLCallbackType(self.getLocation(p, 1), self.globalScope(), - identifier, p[3], p[5]) + p[0] = IDLCallback(self.getLocation(p, 1), self.globalScope(), + identifier, p[3], p[5]) def p_ExceptionMembers(self, p): """ @@ -4694,8 +4858,8 @@ class Parser(Tokenizer): """ Typedef : TYPEDEF Type IDENTIFIER SEMICOLON """ - typedef = IDLTypedefType(self.getLocation(p, 1), p[2], p[3]) - typedef.resolve(self.globalScope()) + typedef = IDLTypedef(self.getLocation(p, 1), self.globalScope(), + p[2], p[3]) p[0] = typedef def p_ImplementsStatement(self, p): @@ -5369,12 +5533,15 @@ class Parser(Tokenizer): """ NonAnyType : PrimitiveOrStringType TypeSuffix | ARRAYBUFFER TypeSuffix + | SHAREDARRAYBUFFER TypeSuffix | OBJECT TypeSuffix """ if p[1] == "object": type = BuiltinTypes[IDLBuiltinType.Types.object] elif p[1] == "ArrayBuffer": type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer] + elif p[1] == "SharedArrayBuffer": + type = BuiltinTypes[IDLBuiltinType.Types.SharedArrayBuffer] else: type = BuiltinTypes[p[1]] @@ -5430,8 +5597,12 @@ class Parser(Tokenizer): try: if self.globalScope()._lookupIdentifier(p[1]): obj = self.globalScope()._lookupIdentifier(p[1]) - if obj.isType(): - type = obj + assert not obj.isType() + if obj.isTypedef(): + type = IDLTypedefType(self.getLocation(p, 1), obj.innerType, + obj.identifier.name) + elif obj.isCallback() and not obj.isInterface(): + type = IDLCallbackType(self.getLocation(p, 1), obj) else: type = IDLWrapperType(self.getLocation(p, 1), p[1]) p[0] = self.handleModifiers(type, p[2]) @@ -5764,12 +5935,10 @@ class Parser(Tokenizer): assert isinstance(scope, IDLScope) # xrange omits the last value. - for x in xrange(IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.Float64Array + 1): + for x in xrange(IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.SharedFloat64Array + 1): builtin = BuiltinTypes[x] name = builtin.name - - typedef = IDLTypedefType(BuiltinLocation(""), builtin, name) - typedef.resolve(scope) + typedef = IDLTypedef(BuiltinLocation(""), scope, builtin, name) @ staticmethod def handleModifiers(type, modifiers): @@ -5827,6 +5996,7 @@ class Parser(Tokenizer): _builtins = """ typedef unsigned long long DOMTimeStamp; typedef (ArrayBufferView or ArrayBuffer) BufferSource; + typedef (SharedArrayBufferView or SharedArrayBuffer) SharedBufferSource; """ def main(): diff --git a/dom/bindings/parser/tests/test_distinguishability.py b/dom/bindings/parser/tests/test_distinguishability.py index dbee9227c7..cbcd098585 100644 --- a/dom/bindings/parser/tests/test_distinguishability.py +++ b/dom/bindings/parser/tests/test_distinguishability.py @@ -160,7 +160,8 @@ def WebIDLTest(parser, harness): "optional Dict2", "sequence", "sequence", "MozMap", "MozMap", "MozMap", "long[]", "short[]", "Date", "Date?", "any", - "USVString" ] + "USVString", "ArrayBuffer", "ArrayBufferView", "SharedArrayBuffer", "SharedArrayBufferView", + "Uint8Array", "SharedUint8Array", "Uint16Array", "SharedUint16Array" ] # When we can parse Date and RegExp, we need to add them here. # Try to categorize things a bit to keep list lengths down @@ -175,8 +176,10 @@ def WebIDLTest(parser, harness): nonStrings = allBut(argTypes, strings) nonObjects = primitives + strings objects = allBut(argTypes, nonObjects ) + bufferSourceTypes = ["ArrayBuffer", "ArrayBufferView", "Uint8Array", "Uint16Array"] + sharedBufferSourceTypes = ["SharedArrayBuffer", "SharedArrayBufferView", "SharedUint8Array", "SharedUint16Array"] interfaces = [ "Interface", "Interface?", "AncestorInterface", - "UnrelatedInterface", "ImplementedInterface" ] + "UnrelatedInterface", "ImplementedInterface" ] + bufferSourceTypes + sharedBufferSourceTypes nullables = ["long?", "short?", "boolean?", "Interface?", "CallbackInterface?", "optional Dict", "optional Dict2", "Date?", "any"] @@ -186,7 +189,7 @@ def WebIDLTest(parser, harness): nonUserObjects = nonObjects + interfaces + dates + sequences otherObjects = allBut(argTypes, nonUserObjects + ["object"]) notRelatedInterfaces = (nonObjects + ["UnrelatedInterface"] + - otherObjects + dates + sequences) + otherObjects + dates + sequences + bufferSourceTypes + sharedBufferSourceTypes) mozMaps = [ "MozMap", "MozMap", "MozMap" ] # Build a representation of the distinguishability table as a dict @@ -235,6 +238,14 @@ def WebIDLTest(parser, harness): setDistinguishable("Date", allBut(argTypes, dates + ["object"])) setDistinguishable("Date?", allBut(argTypes, dates + nullables + ["object"])) setDistinguishable("any", []) + setDistinguishable("ArrayBuffer", allBut(argTypes, ["ArrayBuffer", "object"])) + setDistinguishable("ArrayBufferView", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "Uint16Array", "object"])) + setDistinguishable("Uint8Array", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "object"])) + setDistinguishable("Uint16Array", allBut(argTypes, ["ArrayBufferView", "Uint16Array", "object"])) + setDistinguishable("SharedArrayBuffer", allBut(argTypes, ["SharedArrayBuffer", "object"])) + setDistinguishable("SharedArrayBufferView", allBut(argTypes, ["SharedArrayBufferView", "SharedUint8Array", "SharedUint16Array", "object"])) + setDistinguishable("SharedUint8Array", allBut(argTypes, ["SharedArrayBufferView", "SharedUint8Array", "object"])) + setDistinguishable("SharedUint16Array", allBut(argTypes, ["SharedArrayBufferView", "SharedUint16Array", "object"])) def areDistinguishable(type1, type2): return data[type1].get(type2, False) diff --git a/dom/bindings/test/TestBindingHeader.h b/dom/bindings/test/TestBindingHeader.h index b355350311..6093204956 100644 --- a/dom/bindings/test/TestBindingHeader.h +++ b/dom/bindings/test/TestBindingHeader.h @@ -713,6 +713,7 @@ public: // Dictionary tests void PassDictionary(JSContext*, const Dict&); + void PassDictionary2(JSContext*, const Dict&); void GetReadonlyDictionary(JSContext*, Dict&); void GetReadonlyNullableDictionary(JSContext*, Nullable&); void GetWritableDictionary(JSContext*, Dict&); diff --git a/dom/bindings/test/TestCodeGen.webidl b/dom/bindings/test/TestCodeGen.webidl index 7bdd6f2757..faa83e8ba8 100644 --- a/dom/bindings/test/TestCodeGen.webidl +++ b/dom/bindings/test/TestCodeGen.webidl @@ -7,6 +7,7 @@ typedef long myLong; typedef TestInterface AnotherNameForTestInterface; typedef TestInterface? NullableTestInterface; +typedef CustomEventInit TestDictionaryTypedef; interface TestExternalInterface; @@ -685,6 +686,7 @@ interface TestInterface { attribute byte otherAttributeRenamedFrom; void passDictionary(optional Dict x); + void passDictionary2(Dict x); [Cached, Pure] readonly attribute Dict readonlyDictionary; [Cached, Pure] @@ -1018,6 +1020,7 @@ dictionary Dict : ParentDict { required object requiredObject; CustomEventInit customEventInit; + TestDictionaryTypedef dictionaryTypedef; }; dictionary ParentDict : GrandparentDict { diff --git a/dom/bindings/test/TestExampleGen.webidl b/dom/bindings/test/TestExampleGen.webidl index 0534fc9a28..490050baab 100644 --- a/dom/bindings/test/TestExampleGen.webidl +++ b/dom/bindings/test/TestExampleGen.webidl @@ -549,6 +549,7 @@ interface TestExampleInterface { attribute byte otherAttributeRenamedFrom; void passDictionary(optional Dict x); + void passDictionary2(Dict x); [Cached, Pure] readonly attribute Dict readonlyDictionary; [Cached, Pure] diff --git a/dom/bindings/test/TestJSImplGen.webidl b/dom/bindings/test/TestJSImplGen.webidl index 028e3cc97d..484361c162 100644 --- a/dom/bindings/test/TestJSImplGen.webidl +++ b/dom/bindings/test/TestJSImplGen.webidl @@ -562,6 +562,7 @@ interface TestJSImplInterface { attribute byte otherAttributeRenamedFrom; void passDictionary(optional Dict x); + void passDictionary2(Dict x); [Cached, Pure] readonly attribute Dict readonlyDictionary; [Cached, Pure] diff --git a/dom/bluetooth/BluetoothUtils.cpp b/dom/bluetooth/BluetoothUtils.cpp index 0cf7b40918..8a5e66d846 100644 --- a/dom/bluetooth/BluetoothUtils.cpp +++ b/dom/bluetooth/BluetoothUtils.cpp @@ -152,14 +152,14 @@ SetJsObject(JSContext* aContext, v.get_nsString().BeginReading(), v.get_nsString().Length()); NS_ENSURE_TRUE(jsData, false); - val = STRING_TO_JSVAL(jsData); + val.setString(jsData); break; } case BluetoothValue::Tuint32_t: val = INT_TO_JSVAL(v.get_uint32_t()); break; case BluetoothValue::Tbool: - val = BOOLEAN_TO_JSVAL(v.get_bool()); + val.setBoolean(v.get_bool()); break; default: BT_WARNING("SetJsObject: Parameter is not handled"); @@ -194,7 +194,7 @@ BroadcastSystemMessage(const nsAString& aType, JSString* jsData = JS_NewUCStringCopyN(cx, aData.get_nsString().BeginReading(), aData.get_nsString().Length()); - value = STRING_TO_JSVAL(jsData); + value.setString(jsData); } else if (aData.type() == BluetoothValue::TArrayOfBluetoothNamedValue) { JS::Rooted obj(cx, JS_NewPlainObject(cx)); if (!obj) { diff --git a/dom/indexedDB/KeyPath.cpp b/dom/indexedDB/KeyPath.cpp index e761e63b7c..bd804fbe5b 100644 --- a/dom/indexedDB/KeyPath.cpp +++ b/dom/indexedDB/KeyPath.cpp @@ -365,7 +365,7 @@ KeyPath::ExtractKeyAsJSVal(JSContext* aCx, const JS::Value& aValue, } } - *aOutVal = OBJECT_TO_JSVAL(arrayObj); + aOutVal->setObject(*arrayObj); return NS_OK; } diff --git a/dom/media/PeerConnection.js b/dom/media/PeerConnection.js index b7a0c9e820..6f6f62ce9c 100644 --- a/dom/media/PeerConnection.js +++ b/dom/media/PeerConnection.js @@ -385,6 +385,7 @@ RTCPeerConnection.prototype = { this._impl.initialize(this._observer, this._win, rtcConfig, Services.tm.currentThread); + this._initCertificate(rtcConfig.certificates); this._initIdp(); _globalPCList.notifyLifecycleObservers(this, "initialized"); }, @@ -398,6 +399,30 @@ RTCPeerConnection.prototype = { return this._pc; }, + _initCertificate: function(certificates) { + let certPromise; + if (certificates && certificates.length > 0) { + if (certificates.length > 1) { + throw new this._win.DOMException( + "RTCPeerConnection does not currently support multiple certificates", + "NotSupportedError"); + } + let cert = certificates.find(c => c.expires.getTime() > Date.now()); + if (!cert) { + throw new this._win.DOMException( + "Unable to create RTCPeerConnection with an expired certificate", + "InvalidParameterError"); + } + certPromise = Promise.resolve(cert); + } else { + certPromise = this._win.mozRTCPeerConnection.generateCertificate({ + name: "ECDSA", namedCurve: "P-256" + }); + } + this._certificateReady = certPromise + .then(cert => this._impl.certificate = cert); + }, + _initIdp: function() { this._peerIdentity = new this._win.Promise((resolve, reject) => { this._resolvePeerIdentity = resolve; @@ -641,11 +666,13 @@ RTCPeerConnection.prototype = { let origin = Cu.getWebIDLCallerPrincipal().origin; return this._chain(() => { - let p = new this._win.Promise((resolve, reject) => { - this._onCreateOfferSuccess = resolve; - this._onCreateOfferFailure = reject; - this._impl.createOffer(options); - }); + let p = this._certificateReady.then( + () => new this._win.Promise((resolve, reject) => { + this._onCreateOfferSuccess = resolve; + this._onCreateOfferFailure = reject; + this._impl.createOffer(options); + }) + ); p = this._addIdentityAssertion(p, origin); return p.then( sdp => new this._win.mozRTCSessionDescription({ type: "offer", sdp: sdp })); @@ -657,22 +684,24 @@ RTCPeerConnection.prototype = { return this._legacyCatch(onSuccess, onError, () => { let origin = Cu.getWebIDLCallerPrincipal().origin; return this._chain(() => { - let p = new this._win.Promise((resolve, reject) => { - // We give up line-numbers in errors by doing this here, but do all - // state-checks inside the chain, to support the legacy feature that - // callers don't have to wait for setRemoteDescription to finish. - if (!this.remoteDescription) { - throw new this._win.DOMException("setRemoteDescription not called", - "InvalidStateError"); - } - if (this.remoteDescription.type != "offer") { - throw new this._win.DOMException("No outstanding offer", - "InvalidStateError"); - } - this._onCreateAnswerSuccess = resolve; - this._onCreateAnswerFailure = reject; - this._impl.createAnswer(); - }); + let p = this._certificateReady.then( + () => new this._win.Promise((resolve, reject) => { + // We give up line-numbers in errors by doing this here, but do all + // state-checks inside the chain, to support the legacy feature that + // callers don't have to wait for setRemoteDescription to finish. + if (!this.remoteDescription) { + throw new this._win.DOMException("setRemoteDescription not called", + "InvalidStateError"); + } + if (this.remoteDescription.type != "offer") { + throw new this._win.DOMException("No outstanding offer", + "InvalidStateError"); + } + this._onCreateAnswerSuccess = resolve; + this._onCreateAnswerFailure = reject; + this._impl.createAnswer(); + }) + ); p = this._addIdentityAssertion(p, origin); return p.then(sdp => { return new this._win.mozRTCSessionDescription({ type: "answer", sdp: sdp }); @@ -681,7 +710,6 @@ RTCPeerConnection.prototype = { }); }, - setLocalDescription: function(desc, onSuccess, onError) { return this._legacyCatch(onSuccess, onError, () => { this._localType = desc.type; @@ -797,9 +825,11 @@ RTCPeerConnection.prototype = { getIdentityAssertion: function() { let origin = Cu.getWebIDLCallerPrincipal().origin; - return this._chain(() => { - return this._localIdp.getIdentityAssertion(this._impl.fingerprint, origin); - }); + return this._chain( + () => this._certificateReady.then( + () => this._localIdp.getIdentityAssertion(this._impl.fingerprint, origin) + ) + ); }, updateIce: function(config) { diff --git a/dom/media/tests/mochitest/mochitest.ini b/dom/media/tests/mochitest/mochitest.ini index 42ca1daeae..9bbef9c1a1 100644 --- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -105,6 +105,8 @@ skip-if = toolkit == 'gonk' # b2g emulator seems to be too slow (Bug 1016498 and skip-if = buildapp == 'b2g' || os == 'android' # bug 1043403 [test_peerConnection_capturedVideo.html] skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) +[test_peerConnection_certificates.html] +skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867) [test_peerConnection_close.html] skip-if = toolkit == 'gonk' # b2g (Bug 1059867) [test_peerConnection_errorCallbacks.html] diff --git a/dom/media/tests/mochitest/test_peerConnection_certificates.html b/dom/media/tests/mochitest/test_peerConnection_certificates.html new file mode 100644 index 0000000000..d6b93194c6 --- /dev/null +++ b/dom/media/tests/mochitest/test_peerConnection_certificates.html @@ -0,0 +1,173 @@ + + + + + + +
+
+
+ + diff --git a/dom/media/webrtc/moz.build b/dom/media/webrtc/moz.build index 6905e84db7..974487a516 100644 --- a/dom/media/webrtc/moz.build +++ b/dom/media/webrtc/moz.build @@ -61,6 +61,9 @@ UNIFIED_SOURCES += [ EXPORTS.mozilla += [ 'PeerIdentity.h', ] +EXPORTS.mozilla.dom += [ + 'RTCCertificate.h', +] include('/ipc/chromium/chromium-config.mozbuild') diff --git a/dom/mobilemessage/MobileMessageCallback.cpp b/dom/mobilemessage/MobileMessageCallback.cpp index 614740928f..c4555fcc47 100644 --- a/dom/mobilemessage/MobileMessageCallback.cpp +++ b/dom/mobilemessage/MobileMessageCallback.cpp @@ -271,7 +271,7 @@ MobileMessageCallback::NotifyGetSmscAddress(const nsAString& aSmscAddress) return NotifyError(nsIMobileMessageCallback::INTERNAL_ERROR); } - JS::Rooted val(cx, STRING_TO_JSVAL(smsc)); + JS::Rooted val(cx, JS::StringValue(smsc)); return NotifySuccess(val); } diff --git a/dom/plugins/base/nsJSNPRuntime.cpp b/dom/plugins/base/nsJSNPRuntime.cpp index 1423ad39db..bee8da8677 100644 --- a/dom/plugins/base/nsJSNPRuntime.cpp +++ b/dom/plugins/base/nsJSNPRuntime.cpp @@ -177,7 +177,8 @@ static bool NPObjWrapper_GetProperty(JSContext *cx, JS::Handle obj, JS::Handle id, JS::MutableHandle vp); static bool -NPObjWrapper_Enumerate(JSContext *cx, JS::Handle obj, JS::AutoIdVector &properties); +NPObjWrapper_Enumerate(JSContext *cx, JS::Handle obj, JS::AutoIdVector &properties, + bool enumerableOnly); static bool NPObjWrapper_Resolve(JSContext *cx, JS::Handle obj, JS::Handle id, @@ -498,7 +499,7 @@ NPVariantToJSVal(NPP npp, JSContext *cx, const NPVariant *variant) case NPVariantType_Null : return JSVAL_NULL; case NPVariantType_Bool : - return BOOLEAN_TO_JSVAL(NPVARIANT_TO_BOOLEAN(*variant)); + return JS::BooleanValue(NPVARIANT_TO_BOOLEAN(*variant)); case NPVariantType_Int32 : { // Don't use INT_TO_JSVAL directly to prevent bugs when dealing @@ -518,7 +519,7 @@ NPVariantToJSVal(NPP npp, JSContext *cx, const NPVariant *variant) ::JS_NewUCStringCopyN(cx, utf16String.get(), utf16String.Length()); if (str) { - return STRING_TO_JSVAL(str); + return JS::StringValue(str); } break; @@ -530,7 +531,7 @@ NPVariantToJSVal(NPP npp, JSContext *cx, const NPVariant *variant) nsNPObjWrapper::GetNewOrUsed(npp, cx, NPVARIANT_TO_OBJECT(*variant)); if (obj) { - return OBJECT_TO_JSVAL(obj); + return JS::ObjectValue(*obj); } } @@ -1627,7 +1628,7 @@ CallNPMethod(JSContext *cx, unsigned argc, JS::Value *vp) static bool NPObjWrapper_Enumerate(JSContext *cx, JS::Handle obj, - JS::AutoIdVector &properties) + JS::AutoIdVector &properties, bool enumerableOnly) { NPObject *npobj = GetNPObject(cx, obj); if (!npobj || !npobj->_class) { diff --git a/dom/system/OSFileConstants.cpp b/dom/system/OSFileConstants.cpp index 3c55db25ae..f0e58f3f93 100644 --- a/dom/system/OSFileConstants.cpp +++ b/dom/system/OSFileConstants.cpp @@ -825,7 +825,7 @@ bool SetStringProperty(JSContext *cx, JS::Handle aObject, const char } JSString* strValue = JS_NewUCStringCopyZ(cx, aValue.get()); NS_ENSURE_TRUE(strValue, false); - JS::Rooted valValue(cx, STRING_TO_JSVAL(strValue)); + JS::Rooted valValue(cx, JS::StringValue(strValue)); return JS_SetProperty(cx, aObject, aProperty, valValue); } @@ -890,7 +890,7 @@ bool DefineOSFileConstants(JSContext *cx, JS::Handle global) if (!strVersion){ return false; } - JS::Rooted valVersion(cx, STRING_TO_JSVAL(strVersion)); + JS::Rooted valVersion(cx, JS::StringValue(strVersion)); if (!JS_SetProperty(cx, objSys, "Name", valVersion)) { return false; } @@ -906,7 +906,7 @@ bool DefineOSFileConstants(JSContext *cx, JS::Handle global) return false; } - JS::Rooted valVersion(cx, STRING_TO_JSVAL(strVersion)); + JS::Rooted valVersion(cx, JS::StringValue(strVersion)); if (!JS_SetProperty(cx, objSys, "Name", valVersion)) { return false; } diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 4e50652995..e15745193e 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -598,7 +598,7 @@ workerdebuggersandbox_convert(JSContext *cx, JS::Handle obj, JSType type, JS::MutableHandle vp) { if (type == JSTYPE_OBJECT) { - vp.set(OBJECT_TO_JSVAL(obj)); + vp.setObject(*obj); return true; } diff --git a/dom/workers/XMLHttpRequest.cpp b/dom/workers/XMLHttpRequest.cpp index 7284dd5bdf..ac41871fb8 100644 --- a/dom/workers/XMLHttpRequest.cpp +++ b/dom/workers/XMLHttpRequest.cpp @@ -2459,7 +2459,7 @@ XMLHttpRequest::GetResponse(JSContext* /* unused */, return; } - mStateData.mResponse = STRING_TO_JSVAL(str); + mStateData.mResponse.setString(str); } } diff --git a/dom/xbl/nsXBLBinding.cpp b/dom/xbl/nsXBLBinding.cpp index 0c5edbfeb8..75741f402f 100644 --- a/dom/xbl/nsXBLBinding.cpp +++ b/dom/xbl/nsXBLBinding.cpp @@ -1039,7 +1039,7 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, nsXBLDocumentInfo* docInfo = aProtoBinding->XBLDocumentInfo(); ::JS_SetPrivate(proto, docInfo); NS_ADDREF(docInfo); - JS_SetReservedSlot(proto, 0, PRIVATE_TO_JSVAL(aProtoBinding)); + JS_SetReservedSlot(proto, 0, JS::PrivateValue(aProtoBinding)); // Next, enter the compartment of the property holder, wrap the proto, and // stick it on. diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 8eb6cc391a..bc79e27947 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -2430,6 +2430,17 @@ gfxFont::GetShapedWord(gfxContext *aContext, return sw; } +template gfxShapedWord* +gfxFont::GetShapedWord(gfxContext *aContext, + const uint8_t *aText, + uint32_t aLength, + uint32_t aHash, + int32_t aRunScript, + bool aVertical, + int32_t aAppUnitsPerDevUnit, + uint32_t aFlags, + gfxTextPerfMetrics *aTextPerf GFX_MAYBE_UNUSED); + bool gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const { diff --git a/ipc/glue/BrowserProcessSubThread.cpp b/ipc/glue/BrowserProcessSubThread.cpp index f0a6ae3957..95b1b7faf7 100644 --- a/ipc/glue/BrowserProcessSubThread.cpp +++ b/ipc/glue/BrowserProcessSubThread.cpp @@ -30,7 +30,7 @@ static const char* kBrowserThreadNames[BrowserProcessSubThread::ID_COUNT] = { #endif }; -Lock BrowserProcessSubThread::sLock; +/* static */ StaticMutex BrowserProcessSubThread::sLock; BrowserProcessSubThread* BrowserProcessSubThread::sBrowserThreads[ID_COUNT] = { nullptr, // IO // nullptr, // FILE @@ -46,7 +46,7 @@ BrowserProcessSubThread::BrowserProcessSubThread(ID aId) : mIdentifier(aId), mNotificationService(nullptr) { - AutoLock lock(sLock); + StaticMutexAutoLock lock(sLock); DCHECK(aId >= 0 && aId < ID_COUNT); DCHECK(sBrowserThreads[aId] == nullptr); sBrowserThreads[aId] = this; @@ -55,7 +55,8 @@ BrowserProcessSubThread::BrowserProcessSubThread(ID aId) : BrowserProcessSubThread::~BrowserProcessSubThread() { Stop(); - {AutoLock lock(sLock); + { + StaticMutexAutoLock lock(sLock); sBrowserThreads[mIdentifier] = nullptr; } @@ -88,7 +89,7 @@ BrowserProcessSubThread::CleanUp() MessageLoop* BrowserProcessSubThread::GetMessageLoop(ID aId) { - AutoLock lock(sLock); + StaticMutexAutoLock lock(sLock); DCHECK(aId >= 0 && aId < ID_COUNT); if (sBrowserThreads[aId]) diff --git a/ipc/glue/BrowserProcessSubThread.h b/ipc/glue/BrowserProcessSubThread.h index 6faff5dc44..8788a4e8aa 100644 --- a/ipc/glue/BrowserProcessSubThread.h +++ b/ipc/glue/BrowserProcessSubThread.h @@ -9,7 +9,7 @@ #define mozilla_ipc_BrowserProcessSubThread_h #include "base/thread.h" -#include "base/lock.h" +#include "mozilla/StaticMutex.h" #include "nsDebug.h" @@ -60,9 +60,7 @@ private: // This lock protects |browser_threads_|. Do not read or modify that array // without holding this lock. Do not block while holding this lock. - // FIXME/cjones: XPCOM doesn't like static vars, so can't use - // mozilla::Mutex - static Lock sLock; + static StaticMutex sLock; // An array of the ChromeThread objects. This array is protected by |lock_|. // The threads are not owned by this array. Typically, the threads are owned diff --git a/js/ipc/JavaScriptShared.cpp b/js/ipc/JavaScriptShared.cpp index ba5b9e7822..f30bb80bab 100644 --- a/js/ipc/JavaScriptShared.cpp +++ b/js/ipc/JavaScriptShared.cpp @@ -362,7 +362,7 @@ JavaScriptShared::fromVariant(JSContext* cx, const JSVariant& from, MutableHandl return true; case JSVariant::Tbool: - to.set(BOOLEAN_TO_JSVAL(from.get_bool())); + to.setBoolean(from.get_bool()); return true; case JSVariant::TnsString: diff --git a/js/ipc/WrapperAnswer.cpp b/js/ipc/WrapperAnswer.cpp index 2b8e5315eb..bdf6b80f2b 100644 --- a/js/ipc/WrapperAnswer.cpp +++ b/js/ipc/WrapperAnswer.cpp @@ -189,7 +189,7 @@ WrapperAnswer::RecvDefineProperty(const ObjectId& objId, const JSIDVariant& idVa return fail(jsapi, rs); ObjectOpResult success; - if (!js::DefineOwnProperty(cx, obj, id, desc, success)) + if (!JS_DefinePropertyById(cx, obj, id, desc, success)) return fail(jsapi, rs); return ok(rs, success); } diff --git a/js/public/Class.h b/js/public/Class.h index 3daa193629..f56a5e1847 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -253,19 +253,20 @@ typedef bool (* JSDeletePropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::ObjectOpResult& result); -// The type of ObjectOps::enumerate. This callback overrides a portion of SpiderMonkey's default -// [[Enumerate]] internal method. When an ordinary object is enumerated, that object and each object -// on its prototype chain is tested for an enumerate op, and those ops are called in order. -// The properties each op adds to the 'properties' vector are added to the set of values the -// for-in loop will iterate over. All of this is nonstandard. +// The type of ObjectOps::enumerate. This callback overrides a portion of +// SpiderMonkey's default [[Enumerate]] internal method. When an ordinary object +// is enumerated, that object and each object on its prototype chain is tested +// for an enumerate op, and those ops are called in order. The properties each +// op adds to the 'properties' vector are added to the set of values the for-in +// loop will iterate over. All of this is nonstandard. // -// An object is "enumerated" when it's the target of a for-in loop or JS_Enumerate(). -// All other property inspection, including Object.keys(obj), goes through [[OwnKeys]]. -// -// The callback's job is to populate 'properties' with all property keys that the for-in loop -// should visit. +// An object is "enumerated" when it's the target of a for-in loop or +// JS_Enumerate(). The callback's job is to populate 'properties' with the +// object's property keys. If `enumerableOnly` is true, the callback should only +// add enumerable properties. typedef bool -(* JSNewEnumerateOp)(JSContext *cx, JS::HandleObject obj, JS::AutoIdVector &properties); +(* JSNewEnumerateOp)(JSContext* cx, JS::HandleObject obj, JS::AutoIdVector& properties, + bool enumerableOnly); // The old-style JSClass.enumerate op should define all lazy properties not // yet reflected in obj. @@ -404,7 +405,7 @@ class JS_FRIEND_API(ElementAdder) GetBehavior getBehavior() const { return getBehavior_; } - void append(JSContext* cx, JS::HandleValue v); + bool append(JSContext* cx, JS::HandleValue v); void appendHole(); }; diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index 8b840d5eb2..b91c3f0944 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -545,7 +545,7 @@ class JS_PUBLIC_API(AutoCheckCannotGC) : public AutoAssertOnGC /* * Unsets the gray bit for anything reachable from |thing|. |kind| should not be - * JSTRACE_SHAPE. |thing| should be non-null. + * JS::TraceKind::Shape. |thing| should be non-null. */ extern JS_FRIEND_API(bool) UnmarkGrayGCThingRecursively(GCCellPtr thing); @@ -558,7 +558,7 @@ namespace gc { static MOZ_ALWAYS_INLINE void ExposeGCThingToActiveJS(JS::GCCellPtr thing) { - MOZ_ASSERT(thing.kind() != JSTRACE_SHAPE); + MOZ_ASSERT(thing.kind() != JS::TraceKind::Shape); /* * GC things residing in the nursery cannot be gray: they have no mark bits. diff --git a/js/public/HeapAPI.h b/js/public/HeapAPI.h index eb1746f8f6..f1b9cc6882 100644 --- a/js/public/HeapAPI.h +++ b/js/public/HeapAPI.h @@ -81,10 +81,10 @@ const uintptr_t ChunkLocationAnyNursery = ChunkLocationBitNursery; #ifdef JS_DEBUG /* When downcasting, ensure we are actually the right type. */ extern JS_FRIEND_API(void) -AssertGCThingHasType(js::gc::Cell* cell, JSGCTraceKind kind); +AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind); #else inline void -AssertGCThingHasType(js::gc::Cell* cell, JSGCTraceKind kind) {} +AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind) {} #endif MOZ_ALWAYS_INLINE bool IsInsideNursery(const js::gc::Cell* cell); @@ -155,60 +155,60 @@ class JS_FRIEND_API(GCCellPtr) { public: // Construction from a void* and trace kind. - GCCellPtr(void* gcthing, JSGCTraceKind traceKind) : ptr(checkedCast(gcthing, traceKind)) {} + GCCellPtr(void* gcthing, JS::TraceKind traceKind) : ptr(checkedCast(gcthing, traceKind)) {} // Automatically construct a null GCCellPtr from nullptr. - MOZ_IMPLICIT GCCellPtr(decltype(nullptr)) : ptr(checkedCast(nullptr, JSTRACE_NULL)) {} + MOZ_IMPLICIT GCCellPtr(decltype(nullptr)) : ptr(checkedCast(nullptr, JS::TraceKind::Null)) {} // Construction from an explicit type. - explicit GCCellPtr(JSObject* obj) : ptr(checkedCast(obj, JSTRACE_OBJECT)) { } - explicit GCCellPtr(JSFunction* fun) : ptr(checkedCast(fun, JSTRACE_OBJECT)) { } - explicit GCCellPtr(JSString* str) : ptr(checkedCast(str, JSTRACE_STRING)) { } - explicit GCCellPtr(JSFlatString* str) : ptr(checkedCast(str, JSTRACE_STRING)) { } - explicit GCCellPtr(JSScript* script) : ptr(checkedCast(script, JSTRACE_SCRIPT)) { } + explicit GCCellPtr(JSObject* obj) : ptr(checkedCast(obj, JS::TraceKind::Object)) { } + explicit GCCellPtr(JSFunction* fun) : ptr(checkedCast(fun, JS::TraceKind::Object)) { } + explicit GCCellPtr(JSString* str) : ptr(checkedCast(str, JS::TraceKind::String)) { } + explicit GCCellPtr(JSFlatString* str) : ptr(checkedCast(str, JS::TraceKind::String)) { } + explicit GCCellPtr(JSScript* script) : ptr(checkedCast(script, JS::TraceKind::Script)) { } explicit GCCellPtr(const Value& v); - JSGCTraceKind kind() const { - JSGCTraceKind traceKind = JSGCTraceKind(ptr & JSTRACE_OUTOFLINE); - if (traceKind != JSTRACE_OUTOFLINE) + JS::TraceKind kind() const { + JS::TraceKind traceKind = JS::TraceKind(ptr & OutOfLineTraceKindMask); + if (uintptr_t(traceKind) != OutOfLineTraceKindMask) return traceKind; return outOfLineKind(); } // Allow GCCellPtr to be used in a boolean context. explicit operator bool() const { - MOZ_ASSERT(bool(asCell()) == (kind() != JSTRACE_NULL)); + MOZ_ASSERT(bool(asCell()) == (kind() != JS::TraceKind::Null)); return asCell(); } // Simplify checks to the kind. - bool isObject() const { return kind() == JSTRACE_OBJECT; } - bool isScript() const { return kind() == JSTRACE_SCRIPT; } - bool isString() const { return kind() == JSTRACE_STRING; } - bool isSymbol() const { return kind() == JSTRACE_SYMBOL; } - bool isShape() const { return kind() == JSTRACE_SHAPE; } - bool isObjectGroup() const { return kind() == JSTRACE_OBJECT_GROUP; } + bool isObject() const { return kind() == JS::TraceKind::Object; } + bool isScript() const { return kind() == JS::TraceKind::Script; } + bool isString() const { return kind() == JS::TraceKind::String; } + bool isSymbol() const { return kind() == JS::TraceKind::Symbol; } + bool isShape() const { return kind() == JS::TraceKind::Shape; } + bool isObjectGroup() const { return kind() == JS::TraceKind::ObjectGroup; } // Conversions to more specific types must match the kind. Access to // further refined types is not allowed directly from a GCCellPtr. JSObject* toObject() const { - MOZ_ASSERT(kind() == JSTRACE_OBJECT); + MOZ_ASSERT(kind() == JS::TraceKind::Object); return reinterpret_cast(asCell()); } JSString* toString() const { - MOZ_ASSERT(kind() == JSTRACE_STRING); + MOZ_ASSERT(kind() == JS::TraceKind::String); return reinterpret_cast(asCell()); } JSScript* toScript() const { - MOZ_ASSERT(kind() == JSTRACE_SCRIPT); + MOZ_ASSERT(kind() == JS::TraceKind::Script); return reinterpret_cast(asCell()); } Symbol* toSymbol() const { - MOZ_ASSERT(kind() == JSTRACE_SYMBOL); + MOZ_ASSERT(kind() == JS::TraceKind::Symbol); return reinterpret_cast(asCell()); } js::gc::Cell* asCell() const { - return reinterpret_cast(ptr & ~JSTRACE_OUTOFLINE); + return reinterpret_cast(ptr & ~OutOfLineTraceKindMask); } // The CC's trace logger needs an identity that is XPIDL serializable. @@ -225,18 +225,18 @@ class JS_FRIEND_API(GCCellPtr) bool mayBeOwnedByOtherRuntime() const; private: - uintptr_t checkedCast(void* p, JSGCTraceKind traceKind) { + static uintptr_t checkedCast(void* p, JS::TraceKind traceKind) { js::gc::Cell* cell = static_cast(p); - MOZ_ASSERT((uintptr_t(p) & JSTRACE_OUTOFLINE) == 0); + MOZ_ASSERT((uintptr_t(p) & OutOfLineTraceKindMask) == 0); AssertGCThingHasType(cell, traceKind); - // Note: the JSTRACE_OUTOFLINE bits are set on all out-of-line kinds + // Note: the OutOfLineTraceKindMask bits are set on all out-of-line kinds // so that we can mask instead of branching. - MOZ_ASSERT_IF(traceKind >= JSTRACE_OUTOFLINE, - (traceKind & JSTRACE_OUTOFLINE) == JSTRACE_OUTOFLINE); - return uintptr_t(p) | (traceKind & JSTRACE_OUTOFLINE); + MOZ_ASSERT_IF(uintptr_t(traceKind) >= OutOfLineTraceKindMask, + (uintptr_t(traceKind) & OutOfLineTraceKindMask) == OutOfLineTraceKindMask); + return uintptr_t(p) | (uintptr_t(traceKind) & OutOfLineTraceKindMask); } - JSGCTraceKind outOfLineKind() const; + JS::TraceKind outOfLineKind() const; uintptr_t ptr; }; diff --git a/js/public/Id.h b/js/public/Id.h index 49ed43cb44..fc199879ec 100644 --- a/js/public/Id.h +++ b/js/public/Id.h @@ -141,9 +141,9 @@ JSID_TO_GCTHING(jsid id) { void* thing = (void*)(JSID_BITS(id) & ~(size_t)JSID_TYPE_MASK); if (JSID_IS_STRING(id)) - return JS::GCCellPtr(thing, JSTRACE_STRING); + return JS::GCCellPtr(thing, JS::TraceKind::String); MOZ_ASSERT(JSID_IS_SYMBOL(id)); - return JS::GCCellPtr(thing, JSTRACE_SYMBOL); + return JS::GCCellPtr(thing, JS::TraceKind::Symbol); } static MOZ_ALWAYS_INLINE bool diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index b35eadae74..88f3a24a70 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -463,17 +463,17 @@ struct GCThingSizes dummy() {} - void addToKind(JSGCTraceKind kind, intptr_t n) { + void addToKind(JS::TraceKind kind, intptr_t n) { switch (kind) { - case JSTRACE_OBJECT: object += n; break; - case JSTRACE_STRING: string += n; break; - case JSTRACE_SYMBOL: symbol += n; break; - case JSTRACE_SCRIPT: script += n; break; - case JSTRACE_SHAPE: shape += n; break; - case JSTRACE_BASE_SHAPE: baseShape += n; break; - case JSTRACE_JITCODE: jitcode += n; break; - case JSTRACE_LAZY_SCRIPT: lazyScript += n; break; - case JSTRACE_OBJECT_GROUP: objectGroup += n; break; + case JS::TraceKind::Object: object += n; break; + case JS::TraceKind::String: string += n; break; + case JS::TraceKind::Symbol: symbol += n; break; + case JS::TraceKind::Script: script += n; break; + case JS::TraceKind::Shape: shape += n; break; + case JS::TraceKind::BaseShape: baseShape += n; break; + case JS::TraceKind::JitCode: jitcode += n; break; + case JS::TraceKind::LazyScript: lazyScript += n; break; + case JS::TraceKind::ObjectGroup: objectGroup += n; break; default: MOZ_CRASH("Bad trace kind for GCThingSizes"); } diff --git a/js/public/TracingAPI.h b/js/public/TracingAPI.h index 56832cb11d..6022f3e433 100644 --- a/js/public/TracingAPI.h +++ b/js/public/TracingAPI.h @@ -18,7 +18,6 @@ namespace JS { class JS_PUBLIC_API(CallbackTracer); template class Heap; template class TenuredHeap; -} // When tracing a thing, the GC needs to know about the layout of the object it // is looking at. There are a fixed number of different layouts that the GC @@ -29,41 +28,40 @@ template class TenuredHeap; // the matching C++ types are exposed, and those that are, are opaque. // // See Value::gcKind() and JSTraceCallback in Tracer.h for more details. -enum JSGCTraceKind +enum class TraceKind { // These trace kinds have a publicly exposed, although opaque, C++ type. // Note: The order here is determined by our Value packing. Other users // should sort alphabetically, for consistency. - JSTRACE_OBJECT = 0x00, - JSTRACE_STRING = 0x01, - JSTRACE_SYMBOL = 0x02, - JSTRACE_SCRIPT = 0x03, + Object = 0x00, + String = 0x01, + Symbol = 0x02, + Script = 0x03, // Shape details are exposed through JS_TraceShapeCycleCollectorChildren. - JSTRACE_SHAPE = 0x04, + Shape = 0x04, // ObjectGroup details are exposed through JS_TraceObjectGroupCycleCollectorChildren. - JSTRACE_OBJECT_GROUP = 0x05, + ObjectGroup = 0x05, // The kind associated with a nullptr. - JSTRACE_NULL = 0x06, - - // A kind that indicates the real kind should be looked up in the arena. - JSTRACE_OUTOFLINE = 0x07, + Null = 0x06, // The following kinds do not have an exposed C++ idiom. - JSTRACE_BASE_SHAPE = 0x0F, - JSTRACE_JITCODE = 0x1F, - JSTRACE_LAZY_SCRIPT = 0x2F, - - JSTRACE_LAST = JSTRACE_OBJECT_GROUP + BaseShape = 0x0F, + JitCode = 0x1F, + LazyScript = 0x2F }; +const static uintptr_t OutOfLineTraceKindMask = 0x07; +static_assert(uintptr_t(JS::TraceKind::BaseShape) & OutOfLineTraceKindMask, "mask bits are set"); +static_assert(uintptr_t(JS::TraceKind::JitCode) & OutOfLineTraceKindMask, "mask bits are set"); +static_assert(uintptr_t(JS::TraceKind::LazyScript) & OutOfLineTraceKindMask, "mask bits are set"); -namespace JS { // Returns a static string equivalent of |kind|. JS_FRIEND_API(const char*) -GCTraceKindToAscii(JSGCTraceKind kind); -} +GCTraceKindToAscii(JS::TraceKind kind); + +} // namespace JS // Tracer callback, called for each traceable thing directly referenced by a // particular object or runtime structure. It is the callback responsibility @@ -71,16 +69,16 @@ GCTraceKindToAscii(JSGCTraceKind kind); // JS_TraceChildren on the passed thing. In this case the callback must be // prepared to deal with cycles in the traversal graph. // -// kind argument is one of JSTRACE_OBJECT, JSTRACE_STRING or a tag denoting -// internal implementation-specific traversal kind. In the latter case the only -// operations on thing that the callback can do is to call JS_TraceChildren or -// JS_GetTraceThingInfo. +// kind argument is one of JS::TraceKind::Object, JS::TraceKind::String or a +// tag denoting internal implementation-specific traversal kind. In the latter +// case the only operations on thing that the callback can do is to call +// JS_TraceChildren or JS_GetTraceThingInfo. // // If eagerlyTraceWeakMaps is true, when we trace a WeakMap visit all // of its mappings. This should be used in cases where the tracer // wants to use the existing liveness of entries. typedef void -(* JSTraceCallback)(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind); +(* JSTraceCallback)(JS::CallbackTracer* trc, void** thingp, JS::TraceKind kind); enum WeakMapTraceKind { DoNotTraceWeakMaps = 0, @@ -146,7 +144,7 @@ class JS_PUBLIC_API(CallbackTracer) : public JSTracer } // Call the callback. - void invoke(void** thing, JSGCTraceKind kind) { + void invoke(void** thing, JS::TraceKind kind) { callback(this, thing, kind); } @@ -350,7 +348,7 @@ extern JS_PUBLIC_API(void) JS_CallTenuredObjectTracer(JSTracer* trc, JS::TenuredHeap* objp, const char* name); extern JS_PUBLIC_API(void) -JS_TraceChildren(JSTracer* trc, void* thing, JSGCTraceKind kind); +JS_TraceChildren(JSTracer* trc, void* thing, JS::TraceKind kind); namespace JS { typedef js::HashSet, js::SystemAllocPolicy> ZoneSet; @@ -363,6 +361,6 @@ JS_TraceIncomingCCWs(JSTracer* trc, const JS::ZoneSet& zones); extern JS_PUBLIC_API(void) JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc, - void* thing, JSGCTraceKind kind, bool includeDetails); + void* thing, JS::TraceKind kind, bool includeDetails); #endif /* js_TracingAPI_h */ diff --git a/js/public/TrackedOptimizationInfo.h b/js/public/TrackedOptimizationInfo.h index 364c3a5b1d..254236ff5a 100644 --- a/js/public/TrackedOptimizationInfo.h +++ b/js/public/TrackedOptimizationInfo.h @@ -31,6 +31,7 @@ namespace JS { _(SetProp_DefiniteSlot) \ _(SetProp_Unboxed) \ _(SetProp_InlineAccess) \ + _(SetProp_InlineCache) \ \ _(GetElem_TypedObject) \ _(GetElem_Dense) \ diff --git a/js/public/UbiNode.h b/js/public/UbiNode.h index 41e466153a..d3a133460e 100644 --- a/js/public/UbiNode.h +++ b/js/public/UbiNode.h @@ -302,7 +302,7 @@ class Node { // JS::ubi::Node are both essentially tagged references to other sorts of // objects, so letting conversions happen automatically is appropriate. MOZ_IMPLICIT Node(JS::HandleValue value); - Node(JSGCTraceKind kind, void* ptr); + Node(JS::TraceKind kind, void* ptr); // copy construction and copy assignment just use memcpy, since we know // instances contain nothing but a vtable pointer and a data pointer. diff --git a/js/public/Value.h b/js/public/Value.h index 67971ccfdd..e312589f5f 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -613,12 +613,12 @@ JSVAL_TO_GCTHING_IMPL(jsval_layout l) static inline uint32_t JSVAL_TRACE_KIND_IMPL(jsval_layout l) { - static_assert((JSVAL_TAG_STRING & 0x03) == JSTRACE_STRING, - "Value type tags must correspond with JSGCTraceKinds."); - static_assert((JSVAL_TAG_SYMBOL & 0x03) == JSTRACE_SYMBOL, - "Value type tags must correspond with JSGCTraceKinds."); - static_assert((JSVAL_TAG_OBJECT & 0x03) == JSTRACE_OBJECT, - "Value type tags must correspond with JSGCTraceKinds."); + static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String), + "Value type tags must correspond with JS::TraceKinds."); + static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol), + "Value type tags must correspond with JS::TraceKinds."); + static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object), + "Value type tags must correspond with JS::TraceKinds."); return l.s.tag & 0x03; } @@ -854,12 +854,12 @@ JSVAL_TO_GCTHING_IMPL(jsval_layout l) static inline uint32_t JSVAL_TRACE_KIND_IMPL(jsval_layout l) { - static_assert((JSVAL_TAG_STRING & 0x03) == JSTRACE_STRING, - "Value type tags must correspond with JSGCTraceKinds."); - static_assert((JSVAL_TAG_SYMBOL & 0x03) == JSTRACE_SYMBOL, - "Value type tags must correspond with JSGCTraceKinds."); - static_assert((JSVAL_TAG_OBJECT & 0x03) == JSTRACE_OBJECT, - "Value type tags must correspond with JSGCTraceKinds."); + static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String), + "Value type tags must correspond with JS::TraceKinds."); + static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol), + "Value type tags must correspond with JS::TraceKinds."); + static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object), + "Value type tags must correspond with JS::TraceKinds."); return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) & 0x03; } @@ -1179,9 +1179,9 @@ class Value return JSVAL_IS_TRACEABLE_IMPL(data); } - JSGCTraceKind gcKind() const { + JS::TraceKind traceKind() const { MOZ_ASSERT(isMarkable()); - return JSGCTraceKind(JSVAL_TRACE_KIND_IMPL(data)); + return JS::TraceKind(JSVAL_TRACE_KIND_IMPL(data)); } JSWhyMagic whyMagic() const { @@ -1249,7 +1249,7 @@ class Value } GCCellPtr toGCCellPtr() const { - return GCCellPtr(toGCThing(), gcKind()); + return GCCellPtr(toGCThing(), traceKind()); } bool toBoolean() const { @@ -1969,34 +1969,6 @@ UINT_TO_JSVAL(uint32_t i) : DOUBLE_TO_JSVAL((double)i); } -static inline jsval -STRING_TO_JSVAL(JSString* str) -{ - return IMPL_TO_JSVAL(STRING_TO_JSVAL_IMPL(str)); -} - -static inline jsval -OBJECT_TO_JSVAL(JSObject* obj) -{ - if (obj) - return IMPL_TO_JSVAL(OBJECT_TO_JSVAL_IMPL(obj)); - return IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_NULL, 0)); -} - -static inline jsval -BOOLEAN_TO_JSVAL(bool b) -{ - return IMPL_TO_JSVAL(BOOLEAN_TO_JSVAL_IMPL(b)); -} - -/* To be GC-safe, privates are tagged as doubles. */ - -static inline jsval -PRIVATE_TO_JSVAL(void* ptr) -{ - return IMPL_TO_JSVAL(PRIVATE_PTR_TO_JSVAL_IMPL(ptr)); -} - // JS constants. For efficiency, prefer predicates (e.g. v.isNull()) and // constructing values from scratch (e.g. Int32Value(0)). These constants are // stored in memory and initialized at startup, so testing against them and diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index fccb051299..2e26339e15 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -253,8 +253,7 @@ function ArrayMap(callbackfn/*, thisArg*/) { if (k in O) { /* Step c.i-iii. */ var mappedValue = callFunction(callbackfn, T, O[k], k, O); - // UnsafePutElements doesn't invoke setters, so we can use it here. - UnsafePutElements(A, k, mappedValue); + _DefineDataProperty(A, k, mappedValue); } } @@ -714,9 +713,6 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) { ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, mapfn)); var T = thisArg; - // All elements defined by this algorithm have the same attrs: - var attrs = ATTR_CONFIGURABLE | ATTR_ENUMERABLE | ATTR_WRITABLE; - // Steps 4-5. var usingIterator = GetMethod(items, std_iterator); @@ -753,7 +749,7 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) { var mappedValue = mapping ? callFunction(mapfn, thisArg, nextValue, k) : nextValue; // Steps 6.g.ix-xi. - _DefineDataProperty(A, k++, mappedValue, attrs); + _DefineDataProperty(A, k++, mappedValue); } } @@ -778,7 +774,7 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) { var mappedValue = mapping ? callFunction(mapfn, thisArg, kValue, k) : kValue; // Steps 16.f-g. - _DefineDataProperty(A, k, mappedValue, attrs); + _DefineDataProperty(A, k, mappedValue); } // Steps 17-18. diff --git a/js/src/builtin/AtomicsObject.cpp b/js/src/builtin/AtomicsObject.cpp index 98a11d4d50..7be84e40f5 100644 --- a/js/src/builtin/AtomicsObject.cpp +++ b/js/src/builtin/AtomicsObject.cpp @@ -1220,8 +1220,11 @@ AtomicsObject::initClass(JSContext* cx, Handle global) RootedValue AtomicsValue(cx, ObjectValue(*Atomics)); // Everything is set up, install Atomics on the global object. - if (!DefineProperty(cx, global, cx->names().Atomics, AtomicsValue, nullptr, nullptr, 0)) + if (!DefineProperty(cx, global, cx->names().Atomics, AtomicsValue, nullptr, nullptr, + JSPROP_RESOLVING)) + { return nullptr; + } global->setConstructor(JSProto_Atomics, AtomicsValue); return Atomics; diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp index 72868c6802..257ee5173b 100644 --- a/js/src/builtin/Intl.cpp +++ b/js/src/builtin/Intl.cpp @@ -2049,8 +2049,11 @@ js::InitIntlClass(JSContext *cx, HandleObject obj) return nullptr; RootedValue IntlValue(cx, ObjectValue(*Intl)); - if (!DefineProperty(cx, global, cx->names().Intl, IntlValue, nullptr, nullptr, 0)) + if (!DefineProperty(cx, global, cx->names().Intl, IntlValue, nullptr, nullptr, + JSPROP_RESOLVING)) + { return nullptr; + } if (!JS_DefineFunctions(cx, Intl, intl_static_methods)) return nullptr; diff --git a/js/src/builtin/Intl.js b/js/src/builtin/Intl.js index d806a5c405..d2f47aa7a3 100644 --- a/js/src/builtin/Intl.js +++ b/js/src/builtin/Intl.js @@ -1068,15 +1068,6 @@ function GetNumberOption(options, property, minimum, maximum, fallback) { /********** Property access for Intl objects **********/ -/** - * Set a normal public property p of o to value v, but use Object.defineProperty - * to avoid interference from setters on Object.prototype. - */ -function defineProperty(o, p, v) { - _DefineDataProperty(o, p, v, ATTR_ENUMERABLE | ATTR_CONFIGURABLE | ATTR_WRITABLE); -} - - /** * Weak map used to track the initialize-as-Intl status (and, if an object has * been so initialized, the Intl-specific internal properties) of all objects. @@ -1599,7 +1590,7 @@ function Intl_Collator_resolvedOptions() { for (var i = 0; i < relevantExtensionKeys.length; i++) { var key = relevantExtensionKeys[i]; var property = (key === "co") ? "collation" : collatorKeyMappings[key].property; - defineProperty(result, property, internals[property]); + _DefineDataProperty(result, property, internals[property]); } return result; } @@ -2045,7 +2036,7 @@ function Intl_NumberFormat_resolvedOptions() { for (var i = 0; i < optionalProperties.length; i++) { var p = optionalProperties[i]; if (callFunction(std_Object_hasOwnProperty, internals, p)) - defineProperty(result, p, internals[p]); + _DefineDataProperty(result, p, internals[p]); } return result; } @@ -2510,17 +2501,17 @@ function ToDateTimeOptions(options, required, defaults) { // the Throw parameter, while Object.defineProperty uses true. For the // calls here, the difference doesn't matter because we're adding // properties to a new object. - defineProperty(options, "year", "numeric"); - defineProperty(options, "month", "numeric"); - defineProperty(options, "day", "numeric"); + _DefineDataProperty(options, "year", "numeric"); + _DefineDataProperty(options, "month", "numeric"); + _DefineDataProperty(options, "day", "numeric"); } // Step 8. if (needDefaults && (defaults === "time" || defaults === "all")) { // See comment for step 7. - defineProperty(options, "hour", "numeric"); - defineProperty(options, "minute", "numeric"); - defineProperty(options, "second", "numeric"); + _DefineDataProperty(options, "hour", "numeric"); + _DefineDataProperty(options, "minute", "numeric"); + _DefineDataProperty(options, "second", "numeric"); } // Step 9. @@ -2826,11 +2817,11 @@ function resolveICUPattern(pattern, result) { // skip other pattern characters and literal text } if (callFunction(std_Object_hasOwnProperty, icuPatternCharToComponent, c)) - defineProperty(result, icuPatternCharToComponent[c], value); + _DefineDataProperty(result, icuPatternCharToComponent[c], value); if (c === "h" || c === "K") - defineProperty(result, "hour12", true); + _DefineDataProperty(result, "hour12", true); else if (c === "H" || c === "k") - defineProperty(result, "hour12", false); + _DefineDataProperty(result, "hour12", false); } } } diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index d576bc59de..49cbb81c0a 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -823,7 +823,7 @@ js::obj_defineProperty(JSContext *cx, unsigned argc, Value *vp) return false; // Steps 6-8. - if (!StandardDefineProperty(cx, obj, id, desc)) + if (!DefineProperty(cx, obj, id, desc)) return false; args.rval().setObject(*obj); return true; @@ -1125,7 +1125,8 @@ FinishObjectClassInit(JSContext* cx, JS::HandleObject ctor, JS::HandleObject pro /* ES5 15.1.2.1. */ RootedId evalId(cx, NameToId(cx->names().eval)); - JSObject* evalobj = DefineFunction(cx, self, evalId, IndirectEval, 1, JSFUN_STUB_GSOPS); + JSObject* evalobj = DefineFunction(cx, self, evalId, IndirectEval, 1, + JSFUN_STUB_GSOPS | JSPROP_RESOLVING); if (!evalobj) return false; self->setOriginalEval(evalobj); diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index 93c67a7f97..0b30781bfa 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -475,8 +475,11 @@ SIMDObject::initClass(JSContext* cx, Handle global) // Everything is set up, install SIMD on the global object. RootedValue SIMDValue(cx, ObjectValue(*SIMD)); - if (!DefineProperty(cx, global, cx->names().SIMD, SIMDValue, nullptr, nullptr, 0)) + if (!DefineProperty(cx, global, cx->names().SIMD, SIMDValue, nullptr, nullptr, + JSPROP_RESOLVING)) + { return nullptr; + } global->setConstructor(JSProto_SIMD, SIMDValue); return SIMD; diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index ae656852fc..0c47ee82a5 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -789,7 +789,7 @@ NondeterministicGetWeakMapKeys(JSContext* cx, unsigned argc, jsval* vp) struct JSCountHeapNode { void* thing; - JSGCTraceKind kind; + JS::TraceKind kind; JSCountHeapNode* next; }; @@ -807,7 +807,7 @@ class CountHeapTracer : public JS::CallbackTracer }; static void -CountHeapNotify(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind) +CountHeapNotify(JS::CallbackTracer* trc, void** thingp, JS::TraceKind kind) { CountHeapTracer* countTracer = (CountHeapTracer*)trc; void* thing = *thingp; @@ -842,12 +842,12 @@ CountHeapNotify(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind) static const struct TraceKindPair { const char* name; - int32_t kind; + int32_t kind; } traceKindNames[] = { - { "all", -1 }, - { "object", JSTRACE_OBJECT }, - { "string", JSTRACE_STRING }, - { "symbol", JSTRACE_SYMBOL }, + { "all", -1 }, + { "object", int32_t(JS::TraceKind::Object) }, + { "string", int32_t(JS::TraceKind::String) }, + { "symbol", int32_t(JS::TraceKind::Symbol) }, }; static bool @@ -926,7 +926,7 @@ CountHeap(JSContext* cx, unsigned argc, jsval* vp) while ((node = countTracer.traceList) != nullptr) { if (traceThing == nullptr) { // We are looking for all nodes with a specific kind - if (traceKind == -1 || node->kind == traceKind) + if (traceKind == -1 || int32_t(node->kind) == traceKind) counter++; } else { // We are looking for some specific thing @@ -951,10 +951,6 @@ CountHeap(JSContext* cx, unsigned argc, jsval* vp) return true; } -// Stolen from jsmath.cpp -static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL; -static const uint64_t RNG_MASK = (1LL << 48) - 1; - static bool SetSavedStacksRNGState(JSContext* cx, unsigned argc, jsval* vp) { diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 0240a4439b..7b8d27c40f 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -1325,8 +1325,11 @@ GlobalObject::initTypedObjectModule(JSContext* cx, Handle global) // Everything is setup, install module on the global object: RootedValue moduleValue(cx, ObjectValue(*module)); global->setConstructor(JSProto_TypedObject, moduleValue); - if (!DefineProperty(cx, global, cx->names().TypedObject, moduleValue, nullptr, nullptr, 0)) + if (!DefineProperty(cx, global, cx->names().TypedObject, moduleValue, nullptr, nullptr, + JSPROP_RESOLVING)) + { return false; + } return module; } @@ -2056,7 +2059,8 @@ TypedObject::obj_deleteProperty(JSContext *cx, HandleObject obj, HandleId id, Ob } bool -TypedObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties) +TypedObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, + bool enumerableOnly) { MOZ_ASSERT(obj->is()); Rooted typedObj(cx, &obj->as()); diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h index 0eaae61ed0..abe77a46f2 100644 --- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -549,7 +549,8 @@ class TypedObject : public JSObject static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result); - static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties); + static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, + bool enumerableOnly); public: TypedProto& typedProto() const { diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js index c546c0b290..bd22c7856e 100644 --- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -14,9 +14,7 @@ AssertionFailed: false, MakeConstructible: false, DecompileArg: false, RuntimeDefaultLocale: false, - ParallelDo: false, ParallelSlices: false, NewDenseArray: false, - UnsafePutElements: false, - ParallelTestsShouldPass: false, + NewDenseArray: false, Dump: false, callFunction: false, TO_UINT32: false, diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 99b14e2ab8..ddfa54b9cb 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -1633,7 +1633,7 @@ InitTypeConstructor(JSContext* cx, // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of // the type constructor, for faster lookup. - js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto)); + js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO, ObjectValue(*typeProto)); // Create an object to serve as the common ancestor for all CData objects // created from the given type constructor. This has ctypes.CData.prototype @@ -1653,7 +1653,7 @@ InitTypeConstructor(JSContext* cx, return false; // Link the type prototype to the data prototype. - JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(dataProto)); + JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, ObjectValue(*dataProto)); if (!JS_FreezeObject(cx, obj) || //!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212! @@ -1690,8 +1690,7 @@ InitInt64Class(JSContext* cx, if (!fun) return nullptr; - js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO, - OBJECT_TO_JSVAL(prototype)); + js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO, ObjectValue(*prototype)); if (!JS_FreezeObject(cx, ctor)) return nullptr; @@ -1708,7 +1707,7 @@ AttachProtos(JSObject* proto, const AutoObjectVector& protos) // to the appropriate CTypeProtoSlot. (SLOT_CTYPES is the last slot // of [[Class]] "CTypeProto" that we fill in this automated manner.) for (uint32_t i = 0; i <= SLOT_CTYPES; ++i) - JS_SetReservedSlot(proto, i, OBJECT_TO_JSVAL(protos[i])); + JS_SetReservedSlot(proto, i, ObjectOrNullValue(protos[i])); } static bool @@ -1747,7 +1746,7 @@ InitTypeClasses(JSContext* cx, HandleObject ctypesObj) return false; // Link CTypeProto to CDataProto. - JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(CDataProto)); + JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO, ObjectValue(*CDataProto)); // Create and attach the special class constructors: ctypes.PointerType, // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType. @@ -1997,7 +1996,7 @@ JS_SetCTypesCallbacks(JSObject* ctypesObj, const JSCTypesCallbacks* callbacks) // Set the callbacks on a reserved slot. JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS, - PRIVATE_TO_JSVAL(const_cast(callbacks))); + PrivateValue(const_cast(callbacks))); } namespace js { @@ -3916,9 +3915,9 @@ CType::Create(JSContext* cx, // Set up the reserved slots. JS_SetReservedSlot(typeObj, SLOT_TYPECODE, INT_TO_JSVAL(type)); if (ffiType) - JS_SetReservedSlot(typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType)); + JS_SetReservedSlot(typeObj, SLOT_FFITYPE, PrivateValue(ffiType)); if (name) - JS_SetReservedSlot(typeObj, SLOT_NAME, STRING_TO_JSVAL(name)); + JS_SetReservedSlot(typeObj, SLOT_NAME, StringValue(name)); JS_SetReservedSlot(typeObj, SLOT_SIZE, size); JS_SetReservedSlot(typeObj, SLOT_ALIGN, align); @@ -3935,7 +3934,7 @@ CType::Create(JSContext* cx, // Set the 'prototype' object. //if (!JS_FreezeObject(cx, prototype)) // XXX fixme - see bug 541212! // return nullptr; - JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)); + JS_SetReservedSlot(typeObj, SLOT_PROTO, ObjectValue(*prototype)); } if (!JS_FreezeObject(cx, typeObj)) @@ -4255,7 +4254,7 @@ CType::GetFFIType(JSContext* cx, JSObject* obj) if (!result) return nullptr; - JS_SetReservedSlot(obj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(result.get())); + JS_SetReservedSlot(obj, SLOT_FFITYPE, PrivateValue(result.get())); return result.release(); } @@ -4272,7 +4271,7 @@ CType::GetName(JSContext* cx, HandleObject obj) JSString* name = BuildTypeName(cx, obj); if (!name) return nullptr; - JS_SetReservedSlot(obj, SLOT_NAME, STRING_TO_JSVAL(name)); + JS_SetReservedSlot(obj, SLOT_NAME, StringValue(name)); return name; } @@ -4611,10 +4610,10 @@ PointerType::CreateInternal(JSContext* cx, HandleObject baseType) return nullptr; // Set the target type. (This will be 'null' for an opaque pointer type.) - JS_SetReservedSlot(typeObj, SLOT_TARGET_T, OBJECT_TO_JSVAL(baseType)); + JS_SetReservedSlot(typeObj, SLOT_TARGET_T, ObjectValue(*baseType)); // Finally, cache our newly-created PointerType on our pointed-to CType. - JS_SetReservedSlot(baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj)); + JS_SetReservedSlot(baseType, SLOT_PTR, ObjectValue(*typeObj)); return typeObj; } @@ -4936,7 +4935,7 @@ ArrayType::CreateInternal(JSContext* cx, return nullptr; // Set the element type. - JS_SetReservedSlot(typeObj, SLOT_ELEMENT_T, OBJECT_TO_JSVAL(baseType)); + JS_SetReservedSlot(typeObj, SLOT_ELEMENT_T, ObjectValue(*baseType)); // Set the length. JS_SetReservedSlot(typeObj, SLOT_LENGTH, lengthVal); @@ -5596,13 +5595,13 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj_, JSObject* fieldsOb if (!SizeTojsval(cx, structSize, &sizeVal)) return false; - JS_SetReservedSlot(typeObj, SLOT_FIELDINFO, PRIVATE_TO_JSVAL(fields.release())); + JS_SetReservedSlot(typeObj, SLOT_FIELDINFO, PrivateValue(fields.release())); JS_SetReservedSlot(typeObj, SLOT_SIZE, sizeVal); JS_SetReservedSlot(typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign)); //if (!JS_FreezeObject(cx, prototype)0 // XXX fixme - see bug 541212! // return false; - JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)); + JS_SetReservedSlot(typeObj, SLOT_PROTO, ObjectValue(*prototype)); return true; } @@ -5882,7 +5881,7 @@ StructType::FieldsArrayGetter(JSContext* cx, const JS::CallArgs& args) JSObject* fields = BuildFieldsArray(cx, obj); if (!fields) return false; - JS_SetReservedSlot(obj, SLOT_FIELDS, OBJECT_TO_JSVAL(fields)); + JS_SetReservedSlot(obj, SLOT_FIELDS, ObjectValue(*fields)); args.rval().setObject(*fields); } @@ -6167,7 +6166,7 @@ PrepareCIF(JSContext* cx, FunctionInfo* fninfo) { ffi_abi abi; - if (!GetABI(cx, OBJECT_TO_JSVAL(fninfo->mABI), &abi)) { + if (!GetABI(cx, ObjectOrNullValue(fninfo->mABI), &abi)) { JS_ReportError(cx, "Invalid ABI specification"); return false; } @@ -6257,7 +6256,7 @@ CreateFunctionInfo(JSContext* cx, } // Stash the FunctionInfo in a reserved slot. - JS_SetReservedSlot(typeObj, SLOT_FNINFO, PRIVATE_TO_JSVAL(fninfo)); + JS_SetReservedSlot(typeObj, SLOT_FNINFO, PrivateValue(fninfo)); ffi_abi abi; if (!GetABI(cx, abiType, &abi)) { @@ -6432,7 +6431,7 @@ FunctionType::ConstructData(JSContext* cx, return false; // Set the closure object as the referent of the new CData object. - JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(closureObj)); + JS_SetReservedSlot(dataObj, SLOT_REFERENT, ObjectValue(*closureObj)); // Seal the CData object, to prevent modification of the function pointer. // This permanently associates this object with the closure, and avoids @@ -6554,7 +6553,7 @@ FunctionType::Call(JSContext* cx, return false; } if (!(type = CData::GetCType(obj)) || - !(type = PrepareType(cx, OBJECT_TO_JSVAL(type))) || + !(type = PrepareType(cx, ObjectValue(*type))) || // Relying on ImplicitConvert only for the limited purpose of // converting one CType to another (e.g., T[] to T*). !ConvertArgument(cx, obj, i, args[i], type, &values[i], &strings) || @@ -6803,7 +6802,7 @@ CClosure::Create(JSContext* cx, cinfo->jsfnObj = fnObj; // Stash the ClosureInfo struct on our new object. - JS_SetReservedSlot(result, SLOT_CLOSUREINFO, PRIVATE_TO_JSVAL(cinfo)); + JS_SetReservedSlot(result, SLOT_CLOSUREINFO, PrivateValue(cinfo)); // Create an ffi_closure object and initialize it. void* code; @@ -7045,14 +7044,14 @@ CData::Create(JSContext* cx, return nullptr; // set the CData's associated type - JS_SetReservedSlot(dataObj, SLOT_CTYPE, OBJECT_TO_JSVAL(typeObj)); + JS_SetReservedSlot(dataObj, SLOT_CTYPE, ObjectValue(*typeObj)); // Stash the referent object, if any, for GC safety. if (refObj) - JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(refObj)); + JS_SetReservedSlot(dataObj, SLOT_REFERENT, ObjectValue(*refObj)); // Set our ownership flag. - JS_SetReservedSlot(dataObj, SLOT_OWNS, BOOLEAN_TO_JSVAL(ownResult)); + JS_SetReservedSlot(dataObj, SLOT_OWNS, BooleanValue(ownResult)); // attach the buffer. since it might not be 2-byte aligned, we need to // allocate an aligned space for it and store it there. :( @@ -7083,7 +7082,7 @@ CData::Create(JSContext* cx, } *buffer = data; - JS_SetReservedSlot(dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer)); + JS_SetReservedSlot(dataObj, SLOT_DATA, PrivateValue(buffer)); return dataObj; } @@ -7743,15 +7742,15 @@ CDataFinalizer::Construct(JSContext* cx, unsigned argc, jsval* vp) // Used by GetCType JS_SetReservedSlot(objResult, SLOT_DATAFINALIZER_VALTYPE, - OBJECT_TO_JSVAL(objBestArgType)); + ObjectOrNullValue(objBestArgType)); // Used by ToSource JS_SetReservedSlot(objResult, SLOT_DATAFINALIZER_CODETYPE, - OBJECT_TO_JSVAL(objCodePtrType)); + ObjectValue(*objCodePtrType)); ffi_abi abi; - if (!GetABI(cx, OBJECT_TO_JSVAL(funInfoFinalizer->mABI), &abi)) { + if (!GetABI(cx, ObjectOrNullValue(funInfoFinalizer->mABI), &abi)) { JS_ReportError(cx, "Internal Error: " "Invalid ABI specification in CDataFinalizer"); return false; @@ -8027,7 +8026,7 @@ Int64Base::Construct(JSContext* cx, return nullptr; } - JS_SetReservedSlot(result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer)); + JS_SetReservedSlot(result, SLOT_INT64, PrivateValue(buffer)); if (!JS_FreezeObject(cx, result)) return nullptr; diff --git a/js/src/ctypes/Library.cpp b/js/src/ctypes/Library.cpp index dbbd146cf4..8d5188f5a4 100644 --- a/js/src/ctypes/Library.cpp +++ b/js/src/ctypes/Library.cpp @@ -89,7 +89,7 @@ Library::Create(JSContext* cx, jsval path_, const JSCTypesCallbacks* callbacks) return nullptr; // initialize the library - JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(nullptr)); + JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PrivateValue(nullptr)); // attach API functions if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions)) @@ -166,7 +166,7 @@ Library::Create(JSContext* cx, jsval path_, const JSCTypesCallbacks* callbacks) #endif // stash the library - JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(library)); + JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PrivateValue(library)); return libraryObj; } @@ -244,7 +244,7 @@ Library::Close(JSContext* cx, unsigned argc, jsval* vp) // delete our internal objects UnloadLibrary(obj); - JS_SetReservedSlot(obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(nullptr)); + JS_SetReservedSlot(obj, SLOT_LIBRARY, PrivateValue(nullptr)); args.rval().setUndefined(); return true; diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 4cf8231bd1..b3d9349f06 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -4347,7 +4347,6 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje return true; case PNK_CALLSITEOBJ: case PNK_ARRAY: { - RootedValue value(cx); unsigned count; ParseNode* pn; @@ -4355,8 +4354,12 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje vp.setMagic(JS_GENERIC_MAGIC); return true; } - if (allowObjects == DontAllowNestedObjects) + + ObjectGroup::NewArrayKind arrayKind = ObjectGroup::NewArrayKind::Normal; + if (allowObjects == ForCopyOnWriteArray) { + arrayKind = ObjectGroup::NewArrayKind::CopyOnWrite; allowObjects = DontAllowObjects; + } if (getKind() == PNK_CALLSITEOBJ) { count = pn_count - 1; @@ -4367,26 +4370,25 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje pn = pn_head; } - RootedArrayObject obj(cx, NewDenseFullyAllocatedArray(cx, count, nullptr, newKind)); - if (!obj) + AutoValueVector values(cx); + if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), count)) return false; - - unsigned idx = 0; - RootedId id(cx); - for (; pn; idx++, pn = pn->pn_next) { - if (!pn->getConstantValue(cx, allowObjects, &value)) + size_t idx; + for (idx = 0; pn; idx++, pn = pn->pn_next) { + if (!pn->getConstantValue(cx, allowObjects, values[idx])) return false; - if (value.isMagic(JS_GENERIC_MAGIC)) { + if (values[idx].isMagic(JS_GENERIC_MAGIC)) { vp.setMagic(JS_GENERIC_MAGIC); return true; } - id = INT_TO_JSID(idx); - if (!DefineProperty(cx, obj, id, value, nullptr, nullptr, JSPROP_ENUMERATE)) - return false; } MOZ_ASSERT(idx == count); - ObjectGroup::fixArrayGroup(cx, obj); + JSObject* obj = ObjectGroup::newArrayObject(cx, values.begin(), values.length(), + newKind, arrayKind); + if (!obj) + return false; + vp.setObject(*obj); return true; } @@ -4398,8 +4400,7 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje vp.setMagic(JS_GENERIC_MAGIC); return true; } - if (allowObjects == DontAllowNestedObjects) - allowObjects = DontAllowObjects; + MOZ_ASSERT(allowObjects == AllowObjects); AutoIdValueVector properties(cx); @@ -7635,7 +7636,7 @@ BytecodeEmitter::emitTree(ParseNode* pn) // every time the initializer executes. if (emitterMode != BytecodeEmitter::SelfHosting && pn->pn_count != 0) { RootedValue value(cx); - if (!pn->getConstantValue(cx, ParseNode::DontAllowNestedObjects, &value)) + if (!pn->getConstantValue(cx, ParseNode::ForCopyOnWriteArray, &value)) return false; if (!value.isMagic(JS_GENERIC_MAGIC)) { // Note: the group of the template object might not yet reflect @@ -7645,9 +7646,9 @@ BytecodeEmitter::emitTree(ParseNode* pn) // group for the template is accurate. We don't do this here as we // want to use ObjectGroup::allocationSiteGroup, which requires a // finished script. - NativeObject* obj = &value.toObject().as(); - if (!ObjectElements::MakeElementsCopyOnWrite(cx, obj)) - return false; + JSObject* obj = &value.toObject(); + MOZ_ASSERT(obj->is() && + obj->as().denseElementsAreCopyOnWrite()); ObjectBox* objbox = parser->newObjectBox(obj); if (!objbox) diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 7425bdab7c..2b645704bd 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -903,11 +903,11 @@ class ParseNode enum AllowConstantObjects { DontAllowObjects = 0, - DontAllowNestedObjects, - AllowObjects + AllowObjects, + ForCopyOnWriteArray }; - bool getConstantValue(ExclusiveContext *cx, AllowConstantObjects allowObjects, MutableHandleValue vp, + bool getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObjects, MutableHandleValue vp, NewObjectKind newKind = TenuredObject); inline bool isConstant(); diff --git a/js/src/gc/Barrier.cpp b/js/src/gc/Barrier.cpp index 01aca5d3c4..d5a785e2ff 100644 --- a/js/src/gc/Barrier.cpp +++ b/js/src/gc/Barrier.cpp @@ -50,6 +50,13 @@ js::CurrentThreadIsGCSweeping() return js::TlsPerThreadData.get()->gcSweeping; } +bool +js::CurrentThreadIsHandlingInitFailure() +{ + JSRuntime* rt = js::TlsPerThreadData.get()->runtimeIfOnOwnerThread(); + return rt && rt->handlingInitFailure; +} + #endif // DEBUG template diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index de21ad17e8..d78eadbdf3 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -104,7 +104,7 @@ * reference them. The solution is to maintain information about these pointers, * and mark their targets when we start a minor collection. * - * The pointers can be thoughs of as edges in object graph, and the set of edges + * The pointers can be thought of as edges in object graph, and the set of edges * from the tenured generation into the nursery is know as the remembered set. * Post barriers are used to track this remembered set. * @@ -195,42 +195,13 @@ CurrentThreadIsIonCompiling(); bool CurrentThreadIsGCSweeping(); + +bool +CurrentThreadIsHandlingInitFailure(); #endif namespace gc { -template struct MapTypeToTraceKind {}; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind{ static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind{ static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind{ static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_SYMBOL; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_STRING; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_STRING; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_STRING; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_SCRIPT; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_STRING; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_LAZY_SCRIPT; }; -template <> struct MapTypeToTraceKind{ static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_STRING; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_SHAPE; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_SHAPE; }; -template <> struct MapTypeToTraceKind{ static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind{ static const JSGCTraceKind kind = JSTRACE_OBJECT; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_JITCODE; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_OBJECT_GROUP; }; - // Marking.h depends on these barrier definitions, so we need a separate // entry point for marking to implement the pre-barrier. void MarkValueForBarrier(JSTracer* trc, Value* v, const char* name); @@ -423,7 +394,9 @@ class HeapPtr : public BarrieredBase explicit HeapPtr(const HeapPtr& v) : BarrieredBase(v) { post(); } #ifdef DEBUG ~HeapPtr() { - MOZ_ASSERT(CurrentThreadIsGCSweeping()); + // No prebarrier necessary as this only happens when we are sweeping or + // before the containing obect becomes part of the GC graph. + MOZ_ASSERT(CurrentThreadIsGCSweeping() || CurrentThreadIsHandlingInitFailure()); } #endif diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h index ad7c3375fe..e6e2bd4b5e 100644 --- a/js/src/gc/GCInternals.h +++ b/js/src/gc/GCInternals.h @@ -141,7 +141,7 @@ CheckHashTablesAfterMovingGC(JSRuntime* rt); struct MovingTracer : JS::CallbackTracer { explicit MovingTracer(JSRuntime* rt) : CallbackTracer(rt, Visit, TraceWeakMapKeysValues) {} - static void Visit(JS::CallbackTracer* jstrc, void** thingp, JSGCTraceKind kind); + static void Visit(JS::CallbackTracer* jstrc, void** thingp, JS::TraceKind kind); static bool IsMovingTracer(JSTracer* trc) { return trc->isCallbackTracer() && trc->asCallbackTracer()->hasCallback(Visit); } diff --git a/js/src/gc/Heap.h b/js/src/gc/Heap.h index 2e061d886e..0758584cee 100644 --- a/js/src/gc/Heap.h +++ b/js/src/gc/Heap.h @@ -51,7 +51,7 @@ CurrentThreadIsIonCompiling(); #endif extern bool -UnmarkGrayCellRecursively(gc::Cell* cell, JSGCTraceKind kind); +UnmarkGrayCellRecursively(gc::Cell* cell, JS::TraceKind kind); extern void TraceManuallyBarrieredGenericPointerEdge(JSTracer* trc, gc::Cell** thingp, const char* name); @@ -167,35 +167,35 @@ template using AllAllocKindArray = template using ObjectAllocKindArray = mozilla::EnumeratedArray; -static inline JSGCTraceKind +static inline JS::TraceKind MapAllocToTraceKind(AllocKind kind) { - static const JSGCTraceKind map[] = { - JSTRACE_OBJECT, /* AllocKind::FUNCTION */ - JSTRACE_OBJECT, /* AllocKind::FUNCTION_EXTENDED */ - JSTRACE_OBJECT, /* AllocKind::OBJECT0 */ - JSTRACE_OBJECT, /* AllocKind::OBJECT0_BACKGROUND */ - JSTRACE_OBJECT, /* AllocKind::OBJECT2 */ - JSTRACE_OBJECT, /* AllocKind::OBJECT2_BACKGROUND */ - JSTRACE_OBJECT, /* AllocKind::OBJECT4 */ - JSTRACE_OBJECT, /* AllocKind::OBJECT4_BACKGROUND */ - JSTRACE_OBJECT, /* AllocKind::OBJECT8 */ - JSTRACE_OBJECT, /* AllocKind::OBJECT8_BACKGROUND */ - JSTRACE_OBJECT, /* AllocKind::OBJECT12 */ - JSTRACE_OBJECT, /* AllocKind::OBJECT12_BACKGROUND */ - JSTRACE_OBJECT, /* AllocKind::OBJECT16 */ - JSTRACE_OBJECT, /* AllocKind::OBJECT16_BACKGROUND */ - JSTRACE_SCRIPT, /* AllocKind::SCRIPT */ - JSTRACE_LAZY_SCRIPT, /* AllocKind::LAZY_SCRIPT */ - JSTRACE_SHAPE, /* AllocKind::SHAPE */ - JSTRACE_SHAPE, /* AllocKind::ACCESSOR_SHAPE */ - JSTRACE_BASE_SHAPE, /* AllocKind::BASE_SHAPE */ - JSTRACE_OBJECT_GROUP, /* AllocKind::OBJECT_GROUP */ - JSTRACE_STRING, /* AllocKind::FAT_INLINE_STRING */ - JSTRACE_STRING, /* AllocKind::STRING */ - JSTRACE_STRING, /* AllocKind::EXTERNAL_STRING */ - JSTRACE_SYMBOL, /* AllocKind::SYMBOL */ - JSTRACE_JITCODE, /* AllocKind::JITCODE */ + static const JS::TraceKind map[] = { + JS::TraceKind::Object, /* AllocKind::FUNCTION */ + JS::TraceKind::Object, /* AllocKind::FUNCTION_EXTENDED */ + JS::TraceKind::Object, /* AllocKind::OBJECT0 */ + JS::TraceKind::Object, /* AllocKind::OBJECT0_BACKGROUND */ + JS::TraceKind::Object, /* AllocKind::OBJECT2 */ + JS::TraceKind::Object, /* AllocKind::OBJECT2_BACKGROUND */ + JS::TraceKind::Object, /* AllocKind::OBJECT4 */ + JS::TraceKind::Object, /* AllocKind::OBJECT4_BACKGROUND */ + JS::TraceKind::Object, /* AllocKind::OBJECT8 */ + JS::TraceKind::Object, /* AllocKind::OBJECT8_BACKGROUND */ + JS::TraceKind::Object, /* AllocKind::OBJECT12 */ + JS::TraceKind::Object, /* AllocKind::OBJECT12_BACKGROUND */ + JS::TraceKind::Object, /* AllocKind::OBJECT16 */ + JS::TraceKind::Object, /* AllocKind::OBJECT16_BACKGROUND */ + JS::TraceKind::Script, /* AllocKind::SCRIPT */ + JS::TraceKind::LazyScript, /* AllocKind::LAZY_SCRIPT */ + JS::TraceKind::Shape, /* AllocKind::SHAPE */ + JS::TraceKind::Shape, /* AllocKind::ACCESSOR_SHAPE */ + JS::TraceKind::BaseShape, /* AllocKind::BASE_SHAPE */ + JS::TraceKind::ObjectGroup, /* AllocKind::OBJECT_GROUP */ + JS::TraceKind::String, /* AllocKind::FAT_INLINE_STRING */ + JS::TraceKind::String, /* AllocKind::STRING */ + JS::TraceKind::String, /* AllocKind::EXTERNAL_STRING */ + JS::TraceKind::Symbol, /* AllocKind::SYMBOL */ + JS::TraceKind::JitCode, /* AllocKind::JITCODE */ }; static_assert(MOZ_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT), @@ -228,9 +228,12 @@ struct Cell inline JSRuntime* runtimeFromAnyThread() const; inline JS::shadow::Runtime* shadowRuntimeFromAnyThread() const; + // May be overridden by GC thing kinds that have a compartment pointer. + inline JSCompartment* maybeCompartment() const { return nullptr; } + inline StoreBuffer* storeBuffer() const; - inline JSGCTraceKind getTraceKind() const; + inline JS::TraceKind getTraceKind() const; static MOZ_ALWAYS_INLINE bool needWriteBarrierPre(JS::Zone* zone); @@ -265,7 +268,7 @@ class TenuredCell : public Cell // Access to the arena header. inline ArenaHeader* arenaHeader() const; inline AllocKind getAllocKind() const; - inline JSGCTraceKind getTraceKind() const; + inline JS::TraceKind getTraceKind() const; inline JS::Zone* zone() const; inline JS::Zone* zoneFromAnyThread() const; inline bool isInsideZone(JS::Zone* zone) const; @@ -1311,10 +1314,10 @@ Cell::storeBuffer() const return chunk()->info.trailer.storeBuffer; } -inline JSGCTraceKind +inline JS::TraceKind Cell::getTraceKind() const { - return isTenured() ? asTenured().getTraceKind() : JSTRACE_OBJECT; + return isTenured() ? asTenured().getTraceKind() : JS::TraceKind::Object; } inline bool @@ -1396,7 +1399,7 @@ TenuredCell::getAllocKind() const return arenaHeader()->getAllocKind(); } -JSGCTraceKind +JS::TraceKind TenuredCell::getTraceKind() const { return MapAllocToTraceKind(getAllocKind()); @@ -1458,7 +1461,7 @@ static MOZ_ALWAYS_INLINE void AssertValidToSkipBarrier(TenuredCell* thing) { MOZ_ASSERT(!IsInsideNursery(thing)); - MOZ_ASSERT_IF(thing, MapAllocToTraceKind(thing->getAllocKind()) != JSTRACE_OBJECT); + MOZ_ASSERT_IF(thing, MapAllocToTraceKind(thing->getAllocKind()) != JS::TraceKind::Object); } /* static */ MOZ_ALWAYS_INLINE void diff --git a/js/src/gc/Iteration.cpp b/js/src/gc/Iteration.cpp index 937339fd28..19322f94b1 100644 --- a/js/src/gc/Iteration.cpp +++ b/js/src/gc/Iteration.cpp @@ -39,7 +39,7 @@ IterateCompartmentsArenasCells(JSRuntime* rt, Zone* zone, void* data, (*compartmentCallback)(rt, data, comp); for (auto thingKind : AllAllocKinds()) { - JSGCTraceKind traceKind = MapAllocToTraceKind(thingKind); + JS::TraceKind traceKind = MapAllocToTraceKind(thingKind); size_t thingSize = Arena::thingSize(thingKind); for (ArenaIter aiter(zone, thingKind); !aiter.done(); aiter.next()) { diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 1f063e3396..8577bf843e 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -30,6 +30,7 @@ #include "gc/Nursery-inl.h" #include "vm/String-inl.h" +#include "vm/UnboxedObject-inl.h" using namespace js; using namespace js::gc; @@ -203,7 +204,7 @@ js::CheckTracedThing(JSTracer* trc, T thing) MOZ_ASSERT(thing->isAligned()); MOZ_ASSERT(MapTypeToTraceKind::Type>::kind == - GetGCThingTraceKind(thing)); + thing->getTraceKind()); /* * Do not check IsMarkingTracer directly -- it should only be used in paths @@ -348,14 +349,8 @@ AssertRootMarkingPhase(JSTracer* trc) /*** Tracing Interface ***************************************************************************/ -// A C++ version of JSGCTraceKind -enum class TraceKind { -#define NAMES(name, _, __) name, -FOR_EACH_GC_LAYOUT(NAMES) -#undef NAMES -}; - #define FOR_EACH_GC_POINTER_TYPE(D) \ + D(AccessorShape*) \ D(BaseShape*) \ D(UnownedBaseShape*) \ D(jit::JitCode*) \ @@ -402,18 +397,18 @@ FOR_EACH_GC_LAYOUT(NAMES) // BaseGCType::type => BaseShape // etc. template ::value ? TraceKind::Object - : IsBaseOf::value ? TraceKind::String - : IsBaseOf::value ? TraceKind::Symbol - : IsBaseOf::value ? TraceKind::Script - : IsBaseOf::value ? TraceKind::Shape - : IsBaseOf::value ? TraceKind::BaseShape - : IsBaseOf::value ? TraceKind::JitCode - : IsBaseOf::value ? TraceKind::LazyScript - : TraceKind::ObjectGroup> + JS::TraceKind = IsBaseOf::value ? JS::TraceKind::Object + : IsBaseOf::value ? JS::TraceKind::String + : IsBaseOf::value ? JS::TraceKind::Symbol + : IsBaseOf::value ? JS::TraceKind::Script + : IsBaseOf::value ? JS::TraceKind::Shape + : IsBaseOf::value ? JS::TraceKind::BaseShape + : IsBaseOf::value ? JS::TraceKind::JitCode + : IsBaseOf::value ? JS::TraceKind::LazyScript + : JS::TraceKind::ObjectGroup> struct BaseGCType; #define IMPL_BASE_GC_TYPE(name, type_, _) \ - template struct BaseGCType { typedef type_ type; }; + template struct BaseGCType { typedef type_ type; }; FOR_EACH_GC_LAYOUT(IMPL_BASE_GC_TYPE); #undef IMPL_BASE_GC_TYPE @@ -530,7 +525,7 @@ js::TraceProcessGlobalRoot(JSTracer* trc, T* thing, const char* name) // things so they do not need to go through the mark stack and may simply // be marked directly. Moreover, well-known symbols can refer only to // permanent atoms, so likewise require no subsquent marking. - CheckTracedThing(trc, thing); + CheckTracedThing(trc, *ConvertToBase(&thing)); if (trc->isMarkingTracer()) thing->markIfUnmarked(gc::BLACK); else @@ -539,18 +534,6 @@ js::TraceProcessGlobalRoot(JSTracer* trc, T* thing, const char* name) template void js::TraceProcessGlobalRoot(JSTracer*, JSAtom*, const char*); template void js::TraceProcessGlobalRoot(JSTracer*, JS::Symbol*, const char*); -void -js::TraceObjectSlots(JSTracer* trc, NativeObject* obj, uint32_t start, uint32_t nslots) -{ - JS::AutoTracingIndex index(trc, start); - for (uint32_t i = start; i < (start + nslots); ++i) { - HeapSlot& slot = obj->getSlotRef(i); - if (InternalGCMethods::isMarkable(slot)) - DispatchToTracer(trc, slot.unsafeGet(), "object slot"); - ++index; - } -} - // A typed functor adaptor for TraceRoot. struct TraceRootFunctor { template @@ -760,21 +743,23 @@ template void js::GCMarker::traverseEdge(S source, T target) { + // Atoms and Symbols do not have or mark their internal pointers, respectively. + MOZ_ASSERT(!ThingIsPermanentAtomOrWellKnownSymbol(source)); + + // The Zones must match, unless the target is an atom. MOZ_ASSERT_IF(!ThingIsPermanentAtomOrWellKnownSymbol(target), target->zone()->isAtomsZone() || target->zone() == source->zone()); - traverse(target); -} -namespace js { -// Special-case JSObject->JSObject edges to check the compartment too. -template <> -void -GCMarker::traverseEdge(JSObject* source, JSObject* target) -{ - MOZ_ASSERT(target->compartment() == source->compartment()); + // Atoms and Symbols do not have access to a compartment pointer, or we'd need + // to adjust the subsequent check to catch that case. + MOZ_ASSERT_IF(ThingIsPermanentAtomOrWellKnownSymbol(target), !target->maybeCompartment()); + MOZ_ASSERT_IF(target->zoneFromAnyThread()->isAtomsZone(), !target->maybeCompartment()); + // If we have access to a compartment pointer for both things, they must match. + MOZ_ASSERT_IF(source->maybeCompartment() && target->maybeCompartment(), + source->maybeCompartment() == target->maybeCompartment()); + traverse(target); } -} // namespace js template struct TraverseEdgeFunctor : public VoidDefaultAdaptor { template void operator()(T t, GCMarker* gcmarker, S s) { @@ -951,9 +936,9 @@ js::GCMarker::eagerlyMarkChildren(JSRope* rope) // other ropes or linear strings, it cannot refer to GC things of other // types. ptrdiff_t savedPos = stack.position(); - JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING); + JS_DIAGNOSTICS_ASSERT(rope->getTraceKind() == JS::TraceKind::String); while (true) { - JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING); + JS_DIAGNOSTICS_ASSERT(rope->getTraceKind() == JS::TraceKind::String); JS_DIAGNOSTICS_ASSERT(rope->JSString::isRope()); AssertZoneIsMarking(rope); MOZ_ASSERT(rope->isMarked()); @@ -1817,8 +1802,8 @@ void js::gc::StoreBuffer::WholeCellEdges::trace(TenuringTracer& mover) const { MOZ_ASSERT(edge->isTenured()); - JSGCTraceKind kind = GetGCThingTraceKind(edge); - if (kind <= JSTRACE_OBJECT) { + JS::TraceKind kind = edge->getTraceKind(); + if (kind == JS::TraceKind::Object) { JSObject *object = static_cast(edge); mover.traceObject(object); @@ -1835,7 +1820,7 @@ js::gc::StoreBuffer::WholeCellEdges::trace(TenuringTracer& mover) const return; } - MOZ_ASSERT(kind == JSTRACE_JITCODE); + MOZ_ASSERT(kind == JS::TraceKind::JitCode); static_cast(edge)->traceChildren(&mover); } @@ -1845,7 +1830,7 @@ js::gc::StoreBuffer::CellPtrEdge::trace(TenuringTracer& mover) const if (!*edge) return; - MOZ_ASSERT(GetGCThingTraceKind(*edge) == JSTRACE_OBJECT); + MOZ_ASSERT((*edge)->getTraceKind() == JS::TraceKind::Object); mover.traverse(reinterpret_cast(edge)); } @@ -2288,135 +2273,12 @@ TypeSet::MarkTypeUnbarriered(JSTracer* trc, TypeSet::Type* v, const char* name) } } - -/*** Cycle Collector Helpers **********************************************************************/ - -// This function is used by the cycle collector to trace through a shape. The -// cycle collector does not care about shapes or base shapes, so those are not -// marked. Instead, any shapes or base shapes that are encountered have their -// children marked. Stack space is bounded. -// -// Note: The canonical way for an embedding to implement this functionality -// would be through a custom CallbackTracer that ignores unrequired children -// and pushes to a separate mark stack in order to bound the call stack usage. -// We've implemented like this purely for performance. -void -gc::MarkCycleCollectorChildren(JSTracer* trc, Shape* shape) -{ - // We need to mark the global, but it's OK to only do this once instead of - // doing it for every Shape in our lineage, since it's always the same - // global. - JSObject* global = shape->compartment()->unsafeUnbarrieredMaybeGlobal(); - MOZ_ASSERT(global); - TraceManuallyBarrieredEdge(trc, &global, "global"); - - do { - MOZ_ASSERT(global == shape->compartment()->unsafeUnbarrieredMaybeGlobal()); - - MOZ_ASSERT(shape->base()); - shape->base()->assertConsistency(); - - TraceEdge(trc, &shape->propidRef(), "propid"); - - if (shape->hasGetterObject()) { - JSObject* tmp = shape->getterObject(); - TraceManuallyBarrieredEdge(trc, &tmp, "getter"); - MOZ_ASSERT(tmp == shape->getterObject()); - } - - if (shape->hasSetterObject()) { - JSObject* tmp = shape->setterObject(); - TraceManuallyBarrieredEdge(trc, &tmp, "setter"); - MOZ_ASSERT(tmp == shape->setterObject()); - } - - shape = shape->previous(); - } while (shape); -} - -void -TraceObjectGroupCycleCollectorChildrenCallback(JS::CallbackTracer* trc, - void** thingp, JSGCTraceKind kind); - -// Object groups can point to other object groups via an UnboxedLayout or the -// the original unboxed group link. There can potentially be deep or cyclic -// chains of such groups to trace through without going through a thing that -// participates in cycle collection. These need to be handled iteratively to -// avoid blowing the stack when running the cycle collector's callback tracer. -struct ObjectGroupCycleCollectorTracer : public JS::CallbackTracer -{ - explicit ObjectGroupCycleCollectorTracer(JS::CallbackTracer* innerTracer) - : JS::CallbackTracer(innerTracer->runtime(), - TraceObjectGroupCycleCollectorChildrenCallback, - DoNotTraceWeakMaps), - innerTracer(innerTracer) - {} - - JS::CallbackTracer* innerTracer; - Vector seen, worklist; -}; - -void -TraceObjectGroupCycleCollectorChildrenCallback(JS::CallbackTracer* trcArg, - void** thingp, JSGCTraceKind kind) -{ - ObjectGroupCycleCollectorTracer* trc = static_cast(trcArg); - JS::GCCellPtr thing(*thingp, kind); - - if (thing.isObject() || thing.isScript()) { - // Invoke the inner cycle collector callback on this child. It will not - // recurse back into TraceChildren. - trc->innerTracer->invoke(thingp, kind); - return; - } - - if (thing.isObjectGroup()) { - // If this group is required to be in an ObjectGroup chain, trace it - // via the provided worklist rather than continuing to recurse. - ObjectGroup* group = static_cast(thing.asCell()); - if (group->maybeUnboxedLayout()) { - for (size_t i = 0; i < trc->seen.length(); i++) { - if (trc->seen[i] == group) - return; - } - if (trc->seen.append(group) && trc->worklist.append(group)) { - return; - } else { - // If append fails, keep tracing normally. The worst that will - // happen is we end up overrecursing. - } - } - } - - TraceChildren(trc, thing.asCell(), thing.kind()); -} - -void -gc::MarkCycleCollectorChildren(JSTracer* trc, ObjectGroup* group) -{ - MOZ_ASSERT(trc->isCallbackTracer()); - - // Early return if this group is not required to be in an ObjectGroup chain. - if (!group->maybeUnboxedLayout()) { - TraceChildren(trc, group, JSTRACE_OBJECT_GROUP); - return; - } - - ObjectGroupCycleCollectorTracer groupTracer(trc->asCallbackTracer()); - TraceChildren(&groupTracer, group, JSTRACE_OBJECT_GROUP); - - while (!groupTracer.worklist.empty()) { - ObjectGroup* innerGroup = groupTracer.worklist.popCopy(); - TraceChildren(&groupTracer, innerGroup, JSTRACE_OBJECT_GROUP); - } -} - /*** Cycle Collector Barrier Implementation *******************************************************/ #ifdef DEBUG static void -AssertNonGrayGCThing(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind) +AssertNonGrayGCThing(JS::CallbackTracer* trc, void** thingp, JS::TraceKind kind) { DebugOnly thing(static_cast(*thingp)); MOZ_ASSERT_IF(thing->isTenured(), !thing->asTenured().isMarked(js::gc::GRAY)); @@ -2424,7 +2286,7 @@ AssertNonGrayGCThing(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind) #endif static void -UnmarkGrayChildren(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind); +UnmarkGrayChildren(JS::CallbackTracer* trc, void** thingp, JS::TraceKind kind); struct UnmarkGrayTracer : public JS::CallbackTracer { @@ -2487,7 +2349,7 @@ struct UnmarkGrayTracer : public JS::CallbackTracer * containers. */ static void -UnmarkGrayChildren(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind) +UnmarkGrayChildren(JS::CallbackTracer* trc, void** thingp, JS::TraceKind kind) { int stackDummy; if (!JS_CHECK_STACK_SIZE(trc->runtime()->mainThread.nativeStackLimit[StackForSystemCode], @@ -2526,16 +2388,16 @@ UnmarkGrayChildren(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind) // The parent will later trace |tenured|. This is done to avoid increasing // the stack depth during shape tracing. It is safe to do because a shape // can only have one child that is a shape. - UnmarkGrayTracer childTracer(tracer, kind == JSTRACE_SHAPE); + UnmarkGrayTracer childTracer(tracer, kind == JS::TraceKind::Shape); - if (kind != JSTRACE_SHAPE) { + if (kind != JS::TraceKind::Shape) { TraceChildren(&childTracer, &tenured, kind); MOZ_ASSERT(!childTracer.previousShape); tracer->unmarkedAny |= childTracer.unmarkedAny; return; } - MOZ_ASSERT(kind == JSTRACE_SHAPE); + MOZ_ASSERT(kind == JS::TraceKind::Shape); Shape* shape = static_cast(&tenured); if (tracer->tracingShape) { MOZ_ASSERT(!tracer->previousShape); @@ -2545,7 +2407,7 @@ UnmarkGrayChildren(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind) do { MOZ_ASSERT(!shape->isMarked(js::gc::GRAY)); - TraceChildren(&childTracer, shape, JSTRACE_SHAPE); + TraceChildren(&childTracer, shape, JS::TraceKind::Shape); shape = childTracer.previousShape; childTracer.previousShape = nullptr; } while (shape); @@ -2553,7 +2415,7 @@ UnmarkGrayChildren(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind) } bool -js::UnmarkGrayCellRecursively(gc::Cell* cell, JSGCTraceKind kind) +js::UnmarkGrayCellRecursively(gc::Cell* cell, JS::TraceKind kind) { MOZ_ASSERT(cell); @@ -2582,7 +2444,7 @@ js::UnmarkGrayCellRecursively(gc::Cell* cell, JSGCTraceKind kind) bool js::UnmarkGrayShapeRecursively(Shape* shape) { - return js::UnmarkGrayCellRecursively(shape, JSTRACE_SHAPE); + return js::UnmarkGrayCellRecursively(shape, JS::TraceKind::Shape); } JS_FRIEND_API(bool) diff --git a/js/src/gc/Marking.h b/js/src/gc/Marking.h index 5c6e131e62..f0d38962a9 100644 --- a/js/src/gc/Marking.h +++ b/js/src/gc/Marking.h @@ -307,15 +307,6 @@ namespace gc { /*** Special Cases ***/ -/* - * Trace through a shape or group iteratively during cycle collection to avoid - * deep or infinite recursion. - */ -void -MarkCycleCollectorChildren(JSTracer* trc, Shape* shape); -void -MarkCycleCollectorChildren(JSTracer* trc, ObjectGroup* group); - void PushArena(GCMarker* gcmarker, ArenaHeader* aheader); diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 4bb27d5122..846912c0a0 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -553,14 +553,14 @@ class BufferGrayRootsTracer : public JS::CallbackTracer // Set to false if we OOM while buffering gray roots. bool bufferingGrayRootsFailed; - void appendGrayRoot(gc::TenuredCell* thing, JSGCTraceKind kind); + void appendGrayRoot(gc::TenuredCell* thing, JS::TraceKind kind); public: explicit BufferGrayRootsTracer(JSRuntime* rt) : JS::CallbackTracer(rt, grayTraceCallback), bufferingGrayRootsFailed(false) {} - static void grayTraceCallback(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind) { + static void grayTraceCallback(JS::CallbackTracer* trc, void** thingp, JS::TraceKind kind) { auto tracer = static_cast(trc); tracer->appendGrayRoot(gc::TenuredCell::fromPointer(*thingp), kind); } @@ -596,7 +596,7 @@ struct SetMaybeAliveFunctor { }; void -BufferGrayRootsTracer::appendGrayRoot(TenuredCell* thing, JSGCTraceKind kind) +BufferGrayRootsTracer::appendGrayRoot(TenuredCell* thing, JS::TraceKind kind) { MOZ_ASSERT(runtime()->isHeapBusy()); diff --git a/js/src/gc/Tracer.cpp b/js/src/gc/Tracer.cpp index f5c676eccc..acd7d261b3 100644 --- a/js/src/gc/Tracer.cpp +++ b/js/src/gc/Tracer.cpp @@ -26,6 +26,8 @@ #include "jsgcinlines.h" +#include "vm/ObjectGroup-inl.h" + using namespace js; using namespace js::gc; using mozilla::DebugOnly; @@ -44,7 +46,7 @@ T DoCallback(JS::CallbackTracer* trc, T* thingp, const char* name) { CheckTracedThing(trc, *thingp); - JSGCTraceKind kind = MapTypeToTraceKind::Type>::kind; + JS::TraceKind kind = MapTypeToTraceKind::Type>::kind; JS::AutoTracingName ctx(trc, name); trc->invoke((void**)thingp, kind); return *thingp; @@ -174,7 +176,7 @@ JS_CallTenuredObjectTracer(JSTracer* trc, JS::TenuredHeap* objp, cons } JS_PUBLIC_API(void) -JS_TraceChildren(JSTracer* trc, void* thing, JSGCTraceKind kind) +JS_TraceChildren(JSTracer* trc, void* thing, JS::TraceKind kind) { js::TraceChildren(trc, thing, kind); } @@ -187,7 +189,7 @@ struct TraceChildrenFunctor { }; void -js::TraceChildren(JSTracer* trc, void* thing, JSGCTraceKind kind) +js::TraceChildren(JSTracer* trc, void* thing, JS::TraceKind kind) { MOZ_ASSERT(thing); TraceChildrenFunctor f; @@ -248,6 +250,130 @@ JS_TraceIncomingCCWs(JSTracer* trc, const JS::ZoneSet& zones) } } + +/*** Cycle Collector Helpers **********************************************************************/ + +// This function is used by the Cycle Collector (CC) to trace through -- or in +// CC parlance, traverse -- a Shape tree. The CC does not care about Shapes or +// BaseShapes, only the JSObjects held live by them. Thus, we walk the Shape +// lineage, but only report non-Shape things. This effectively makes the entire +// shape lineage into a single node in the CC, saving tremendous amounts of +// space and time in its algorithms. +// +// The algorithm implemented here uses only bounded stack space. This would be +// possible to implement outside the engine, but would require much extra +// infrastructure and many, many more slow GOT lookups. We have implemented it +// inside SpiderMonkey, despite the lack of general applicability, for the +// simplicity and performance of FireFox's embedding of this engine. +void +gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, Shape* shape) +{ + // We need to mark the global, but it's OK to only do this once instead of + // doing it for every Shape in our lineage, since it's always the same + // global. + JSObject* global = shape->compartment()->unsafeUnbarrieredMaybeGlobal(); + MOZ_ASSERT(global); + DoCallback(trc, &global, "global"); + + do { + MOZ_ASSERT(global == shape->compartment()->unsafeUnbarrieredMaybeGlobal()); + + MOZ_ASSERT(shape->base()); + shape->base()->assertConsistency(); + + TraceEdge(trc, &shape->propidRef(), "propid"); + + if (shape->hasGetterObject()) { + JSObject* tmp = shape->getterObject(); + DoCallback(trc, &tmp, "getter"); + MOZ_ASSERT(tmp == shape->getterObject()); + } + + if (shape->hasSetterObject()) { + JSObject* tmp = shape->setterObject(); + DoCallback(trc, &tmp, "setter"); + MOZ_ASSERT(tmp == shape->setterObject()); + } + + shape = shape->previous(); + } while (shape); +} + +void +TraceObjectGroupCycleCollectorChildrenCallback(JS::CallbackTracer* trc, + void** thingp, JS::TraceKind kind); + +// Object groups can point to other object groups via an UnboxedLayout or the +// the original unboxed group link. There can potentially be deep or cyclic +// chains of such groups to trace through without going through a thing that +// participates in cycle collection. These need to be handled iteratively to +// avoid blowing the stack when running the cycle collector's callback tracer. +struct ObjectGroupCycleCollectorTracer : public JS::CallbackTracer +{ + explicit ObjectGroupCycleCollectorTracer(JS::CallbackTracer* innerTracer) + : JS::CallbackTracer(innerTracer->runtime(), + TraceObjectGroupCycleCollectorChildrenCallback, + DoNotTraceWeakMaps), + innerTracer(innerTracer) + {} + + JS::CallbackTracer* innerTracer; + Vector seen, worklist; +}; + +void +TraceObjectGroupCycleCollectorChildrenCallback(JS::CallbackTracer* trcArg, + void** thingp, JS::TraceKind kind) +{ + ObjectGroupCycleCollectorTracer* trc = static_cast(trcArg); + JS::GCCellPtr thing(*thingp, kind); + + if (thing.isObject() || thing.isScript()) { + // Invoke the inner cycle collector callback on this child. It will not + // recurse back into TraceChildren. + trc->innerTracer->invoke(thingp, kind); + return; + } + + if (thing.isObjectGroup()) { + // If this group is required to be in an ObjectGroup chain, trace it + // via the provided worklist rather than continuing to recurse. + ObjectGroup* group = static_cast(thing.asCell()); + if (group->maybeUnboxedLayout()) { + for (size_t i = 0; i < trc->seen.length(); i++) { + if (trc->seen[i] == group) + return; + } + if (trc->seen.append(group) && trc->worklist.append(group)) { + return; + } else { + // If append fails, keep tracing normally. The worst that will + // happen is we end up overrecursing. + } + } + } + + TraceChildren(trc, thing.asCell(), thing.kind()); +} + +void +gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, ObjectGroup* group) +{ + MOZ_ASSERT(trc->isCallbackTracer()); + + // Early return if this group is not required to be in an ObjectGroup chain. + if (!group->maybeUnboxedLayout()) + return group->traceChildren(trc); + + ObjectGroupCycleCollectorTracer groupTracer(trc->asCallbackTracer()); + group->traceChildren(&groupTracer); + + while (!groupTracer.worklist.empty()) { + ObjectGroup* innerGroup = groupTracer.worklist.popCopy(); + innerGroup->traceChildren(&groupTracer); + } +} + /*** Traced Edge Printer *************************************************************************/ @@ -265,7 +391,7 @@ CountDecimalDigits(size_t num) JS_PUBLIC_API(void) JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc, void* thing, - JSGCTraceKind kind, bool details) + JS::TraceKind kind, bool details) { const char* name = nullptr; /* silence uninitialized warning */ size_t n; @@ -274,43 +400,43 @@ JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc, void* thing, return; switch (kind) { - case JSTRACE_OBJECT: + case JS::TraceKind::Object: { name = static_cast(thing)->getClass()->name; break; } - case JSTRACE_SCRIPT: + case JS::TraceKind::Script: name = "script"; break; - case JSTRACE_STRING: + case JS::TraceKind::String: name = ((JSString*)thing)->isDependent() ? "substring" : "string"; break; - case JSTRACE_SYMBOL: + case JS::TraceKind::Symbol: name = "symbol"; break; - case JSTRACE_BASE_SHAPE: + case JS::TraceKind::BaseShape: name = "base_shape"; break; - case JSTRACE_JITCODE: + case JS::TraceKind::JitCode: name = "jitcode"; break; - case JSTRACE_LAZY_SCRIPT: + case JS::TraceKind::LazyScript: name = "lazyscript"; break; - case JSTRACE_SHAPE: + case JS::TraceKind::Shape: name = "shape"; break; - case JSTRACE_OBJECT_GROUP: + case JS::TraceKind::ObjectGroup: name = "object_group"; break; @@ -329,7 +455,7 @@ JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc, void* thing, if (details && bufsize > 2) { switch (kind) { - case JSTRACE_OBJECT: + case JS::TraceKind::Object: { JSObject* obj = (JSObject*)thing; if (obj->is()) { @@ -347,14 +473,14 @@ JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc, void* thing, break; } - case JSTRACE_SCRIPT: + case JS::TraceKind::Script: { JSScript* script = static_cast(thing); JS_snprintf(buf, bufsize, " %s:%" PRIuSIZE, script->filename(), script->lineno()); break; } - case JSTRACE_STRING: + case JS::TraceKind::String: { *buf++ = ' '; bufsize--; @@ -377,7 +503,7 @@ JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc, void* thing, break; } - case JSTRACE_SYMBOL: + case JS::TraceKind::Symbol: { JS::Symbol* sym = static_cast(thing); if (JSString* desc = sym->description()) { diff --git a/js/src/gc/Tracer.h b/js/src/gc/Tracer.h index 7741739fb8..b81b790072 100644 --- a/js/src/gc/Tracer.h +++ b/js/src/gc/Tracer.h @@ -109,14 +109,20 @@ TraceGenericPointerRoot(JSTracer* trc, gc::Cell** thingp, const char* name); void TraceManuallyBarrieredGenericPointerEdge(JSTracer* trc, gc::Cell** thingp, const char* name); -// Object slots are not stored as a contiguous vector, so marking them as such -// will lead to the wrong indicies, if such are requested when tracing. -void -TraceObjectSlots(JSTracer* trc, NativeObject* obj, uint32_t start, uint32_t nslots); - // Depricated. Please use one of the strongly typed variants above. void -TraceChildren(JSTracer* trc, void* thing, JSGCTraceKind kind); +TraceChildren(JSTracer* trc, void* thing, JS::TraceKind kind); + +namespace gc { + +// Trace through a shape or group iteratively during cycle collection to avoid +// deep or infinite recursion. +void +TraceCycleCollectorChildren(JS::CallbackTracer* trc, Shape* shape); +void +TraceCycleCollectorChildren(JS::CallbackTracer* trc, ObjectGroup* group); + +} // namespace gc } // namespace js diff --git a/js/src/gc/Verifier.cpp b/js/src/gc/Verifier.cpp index e68baceddf..d4d0e78b81 100644 --- a/js/src/gc/Verifier.cpp +++ b/js/src/gc/Verifier.cpp @@ -50,20 +50,23 @@ using namespace js::gc; struct EdgeValue { void* thing; - JSGCTraceKind kind; + JS::TraceKind kind; const char* label; }; struct VerifyNode { void* thing; - JSGCTraceKind kind; + JS::TraceKind kind; uint32_t count; EdgeValue edges[1]; }; typedef HashMap, SystemAllocPolicy> NodeMap; +static void +AccumulateEdge(JS::CallbackTracer* jstrc, void** thingp, JS::TraceKind kind); + /* * The verifier data structures are simple. The entire graph is stored in a * single block of memory. At the beginning is a VerifyNode for the root @@ -94,8 +97,8 @@ struct VerifyPreTracer : JS::CallbackTracer char* term; NodeMap nodemap; - VerifyPreTracer(JSRuntime* rt, JSTraceCallback callback) - : JS::CallbackTracer(rt, callback), noggc(rt), number(rt->gc.gcNumber()), count(0), + explicit VerifyPreTracer(JSRuntime* rt) + : JS::CallbackTracer(rt, AccumulateEdge), noggc(rt), number(rt->gc.gcNumber()), count(0), root(nullptr) {} @@ -109,9 +112,9 @@ struct VerifyPreTracer : JS::CallbackTracer * node. */ static void -AccumulateEdge(JS::CallbackTracer* jstrc, void** thingp, JSGCTraceKind kind) +AccumulateEdge(JS::CallbackTracer* jstrc, void** thingp, JS::TraceKind kind) { - VerifyPreTracer* trc = (VerifyPreTracer*)jstrc; + VerifyPreTracer* trc = static_cast(jstrc); MOZ_ASSERT(!IsInsideNursery(*reinterpret_cast(thingp))); @@ -131,7 +134,7 @@ AccumulateEdge(JS::CallbackTracer* jstrc, void** thingp, JSGCTraceKind kind) } static VerifyNode* -MakeNode(VerifyPreTracer* trc, void* thing, JSGCTraceKind kind) +MakeNode(VerifyPreTracer* trc, void* thing, JS::TraceKind kind) { NodeMap::AddPtr p = trc->nodemap.lookupForAdd(thing); if (!p) { @@ -179,18 +182,12 @@ gc::GCRuntime::startVerifyPreBarriers() number++; - VerifyPreTracer* trc = js_new(rt, JSTraceCallback(nullptr)); + VerifyPreTracer* trc = js_new(rt); if (!trc) return; gcstats::AutoPhase ap(stats, gcstats::PHASE_TRACE_HEAP); - /* - * Passing a function pointer directly to js_new trips a compiler bug in - * MSVC. Work around by filling the pointer after allocating with nullptr. - */ - trc->setTraceCallback(AccumulateEdge); - const size_t size = 64 * 1024 * 1024; trc->root = (VerifyNode*)js_malloc(size); if (!trc->root) @@ -202,7 +199,7 @@ gc::GCRuntime::startVerifyPreBarriers() goto oom; /* Create the root node. */ - trc->curnode = MakeNode(trc, nullptr, JSGCTraceKind(0)); + trc->curnode = MakeNode(trc, nullptr, JS::TraceKind(0)); incrementalState = MARK_ROOTS; @@ -255,6 +252,14 @@ IsMarkedOrAllocated(TenuredCell* cell) return cell->isMarked() || cell->arenaHeader()->allocatedDuringIncremental; } +static void +CheckEdge(JS::CallbackTracer* jstrc, void** thingp, JS::TraceKind kind); + +struct CheckEdgeTracer : public JS::CallbackTracer { + VerifyNode* node; + explicit CheckEdgeTracer(JSRuntime* rt) : JS::CallbackTracer(rt, CheckEdge), node(nullptr) {} +}; + static const uint32_t MAX_VERIFIER_EDGES = 1000; /* @@ -265,10 +270,10 @@ static const uint32_t MAX_VERIFIER_EDGES = 1000; * been modified) must point to marked objects. */ static void -CheckEdge(JS::CallbackTracer* jstrc, void** thingp, JSGCTraceKind kind) +CheckEdge(JS::CallbackTracer* jstrc, void** thingp, JS::TraceKind kind) { - VerifyPreTracer* trc = (VerifyPreTracer*)jstrc; - VerifyNode* node = trc->curnode; + CheckEdgeTracer* trc = static_cast(jstrc); + VerifyNode* node = trc->node; /* Avoid n^2 behavior. */ if (node->count > MAX_VERIFIER_EDGES) @@ -290,9 +295,9 @@ AssertMarkedOrAllocated(const EdgeValue& edge) return; // Permanent atoms and well-known symbols aren't marked during graph traversal. - if (edge.kind == JSTRACE_STRING && static_cast(edge.thing)->isPermanentAtom()) + if (edge.kind == JS::TraceKind::String && static_cast(edge.thing)->isPermanentAtom()) return; - if (edge.kind == JSTRACE_SYMBOL && static_cast(edge.thing)->isWellKnownSymbol()) + if (edge.kind == JS::TraceKind::Symbol && static_cast(edge.thing)->isWellKnownSymbol()) return; char msgbuf[1024]; @@ -304,7 +309,7 @@ AssertMarkedOrAllocated(const EdgeValue& edge) bool gc::GCRuntime::endVerifyPreBarriers() { - VerifyPreTracer* trc = (VerifyPreTracer*)verifyPreData; + VerifyPreTracer* trc = static_cast(verifyPreData); if (!trc) return false; @@ -336,13 +341,13 @@ gc::GCRuntime::endVerifyPreBarriers() incrementalState = NO_INCREMENTAL; if (!compartmentCreated && IsIncrementalGCSafe(rt)) { - trc->setTraceCallback(CheckEdge); + CheckEdgeTracer cetrc(rt); /* Start after the roots. */ VerifyNode* node = NextNode(trc->root); while ((char*)node < trc->edgeptr) { - trc->curnode = node; - JS_TraceChildren(trc, node->thing, node->kind); + cetrc.node = node; + JS_TraceChildren(&cetrc, node->thing, node->kind); if (node->count <= MAX_VERIFIER_EDGES) { for (uint32_t i = 0; i < node->count; i++) @@ -387,7 +392,7 @@ gc::GCRuntime::maybeVerifyPreBarriers(bool always) if (rt->mainThread.suppressGC) return; - if (VerifyPreTracer* trc = (VerifyPreTracer*)verifyPreData) { + if (VerifyPreTracer* trc = static_cast(verifyPreData)) { if (++trc->count < zealFrequency && !always) return; @@ -407,7 +412,7 @@ js::gc::MaybeVerifyBarriers(JSContext* cx, bool always) void js::gc::GCRuntime::finishVerifier() { - if (VerifyPreTracer* trc = (VerifyPreTracer*)verifyPreData) { + if (VerifyPreTracer* trc = static_cast(verifyPreData)) { js_delete(trc); verifyPreData = nullptr; } diff --git a/js/src/gdb/tests/test-Root.cpp b/js/src/gdb/tests/test-Root.cpp index d877418587..e067a0bea0 100644 --- a/js/src/gdb/tests/test-Root.cpp +++ b/js/src/gdb/tests/test-Root.cpp @@ -28,7 +28,7 @@ FRAGMENT(Root, handle) { } FRAGMENT(Root, HeapSlot) { - JS::Rooted plinth(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, "plinth"))); + JS::Rooted plinth(cx, JS::StringValue(JS_NewStringCopyZ(cx, "plinth"))); JS::Rooted array(cx, JS_NewArrayObject(cx, JS::HandleValueArray(plinth))); breakpoint(); diff --git a/js/src/jit-test/tests/baseline/bug945223.js b/js/src/jit-test/tests/baseline/bug945223.js index ce72cc6a94..ff1cf07a07 100644 --- a/js/src/jit-test/tests/baseline/bug945223.js +++ b/js/src/jit-test/tests/baseline/bug945223.js @@ -1,12 +1,29 @@ // |jit-test| error: InternalError +// This test case creates an array, with no third element. When we iterate over +// the third element of the array (GetElement) see that it is "undefined" and +// start looking for __noSuchMethod__. Iterating over the protoype chain end on +// the Proxy defined as the __proto__ of Array. +// +// When looking for the __noSuchMethod__ property (BaseProxyHandler::get) on the +// proxy, the function getPropertyDescriptor is called which cause an error. +// +// Unfortunately, IonMonkey GetElementIC do not have supports for +// __noSuchMethod__ (Bug 964574) at the moment, which cause a differential +// behaviour. +// +// As we hope to remote __noSuchMethod__ soon (Bug 683218), we disable the mode +// which force the selection of IonMonkey ICs. +if (getJitCompilerOptions()["ion.forceinlineCaches"]) + setJitCompilerOption("ion.forceinlineCaches", 0); + Array.prototype.__proto__ = Proxy.create({ getPropertyDescriptor: function(name) { return (560566); }, }, null); function f() {} -function g() { } +function g() {} var x = [f,f,f,undefined,g]; for (var i = 0; i < 5; ++i) y = x[i]("x"); diff --git a/js/src/jit-test/tests/basic/bug1170355.js b/js/src/jit-test/tests/basic/bug1170355.js new file mode 100644 index 0000000000..cddb3c547d --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1170355.js @@ -0,0 +1,3 @@ +x = Array(4294967295); +x[1] = 0; +Array.prototype.shift.call(x); diff --git a/js/src/jit-test/tests/basic/bug1185653.js b/js/src/jit-test/tests/basic/bug1185653.js new file mode 100644 index 0000000000..b29fd6e0cd --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1185653.js @@ -0,0 +1,28 @@ +function f() { + var arr = []; + for (var i=0; i<80; i++) { + var o3 = {foo: i}; + var o2 = {owner: o3}; + arr.push(o2); + } + for (var i=0; i<80; i++) { + var o2 = arr[i]; + var o3 = o2.owner; + Object.defineProperty(o3, "bar", {value: arr, enumerable: false}); + } + assertEq(JSON.stringify(arr).length, 1671); +} +f(); + +function g() { + var arr = []; + for (var i=0; i<100; i++) { + arr.push([1, i]); + } + for (var i=0; i<100; i++) { + for (var p in arr[i]) { + assertEq(p === "0" || p === "1", true); + } + } +} +g(); diff --git a/js/src/jit-test/tests/ion/ic-fuzz-0.js b/js/src/jit-test/tests/ion/ic-fuzz-0.js new file mode 100644 index 0000000000..9a14a1b148 --- /dev/null +++ b/js/src/jit-test/tests/ion/ic-fuzz-0.js @@ -0,0 +1,3 @@ +setJitCompilerOption("ion.warmup.trigger", 50); +for (var i = 0; i < 150; i++) + Infinity; diff --git a/js/src/jit-test/tests/ion/recover-arrays.js b/js/src/jit-test/tests/ion/recover-arrays.js index 9cf8651cae..120d9b9261 100644 --- a/js/src/jit-test/tests/ion/recover-arrays.js +++ b/js/src/jit-test/tests/ion/recover-arrays.js @@ -4,6 +4,12 @@ if (getJitCompilerOptions()["ion.warmup.trigger"] <= 100) setJitCompilerOption("ion.warmup.trigger", 100); +// This test checks that we are able to remove the getelem & setelem with scalar +// replacement, so we should not force inline caches, as this would skip the +// generation of getelem & setelem instructions. +if (getJitCompilerOptions()["ion.forceinlineCaches"]) + setJitCompilerOption("ion.forceinlineCaches", 0); + // This function is used to force a bailout when it is inlined, and to recover // the frame which is inlining this function. var resumeHere = function (i) { if (i >= 99) bailout(); }; diff --git a/js/src/jit-test/tests/ion/recover-empty-new-object.js b/js/src/jit-test/tests/ion/recover-empty-new-object.js index 459f11f3b7..a34f0d36fb 100644 --- a/js/src/jit-test/tests/ion/recover-empty-new-object.js +++ b/js/src/jit-test/tests/ion/recover-empty-new-object.js @@ -1,3 +1,8 @@ +// |jit-test| test-join=--no-unboxed-objects +// +// Unboxed object optimization might not trigger in all cases, thus we ensure +// that Sink optimization is working well independently of the +// object representation. // Ion eager fails the test below because we have not yet created any // template object in baseline before running the content of the top-level @@ -5,6 +10,9 @@ if (getJitCompilerOptions()["ion.warmup.trigger"] <= 20) setJitCompilerOption("ion.warmup.trigger", 20); +// These arguments have to be computed by baseline, and thus captured in a +// resume point. The next function checks that we can remove the computation of +// these arguments. function f(a, b, c, d) { } function topLevel() { diff --git a/js/src/jit-test/tests/ion/recover-iterator-next.js b/js/src/jit-test/tests/ion/recover-iterator-next.js new file mode 100644 index 0000000000..56e4f94149 --- /dev/null +++ b/js/src/jit-test/tests/ion/recover-iterator-next.js @@ -0,0 +1,43 @@ +// |jit-test| test-join=--no-unboxed-objects +// +// Unboxed object optimization might not trigger in all cases, thus we ensure +// that Scalar Replacement optimization is working well independently of the +// object representation. + +// Ion eager fails the test below because we have not yet created any +// template object in baseline before running the content of the top-level +// function. +if (getJitCompilerOptions()["ion.warmup.trigger"] <= 90) + setJitCompilerOption("ion.warmup.trigger", 90); + +// This test checks that we are able to remove the getprop & setprop with scalar +// replacement, so we should not force inline caches, as this would skip the +// generation of getprop & setprop instructions. +if (getJitCompilerOptions()["ion.forceinlineCaches"]) + setJitCompilerOption("ion.forceinlineCaches", 0); + +// Frequent GCs can interfere with the tests being performed here. +if (typeof gczeal != "undefined") + gczeal(0); + +var arr = new Array(); +var max = 2000; +for (var i=0; i < max; i++) + arr[i] = i; + +function f() { + var res = 0; + var nextObj; + var itr = arr[Symbol.iterator](); + do { + nextObj = itr.next(); + if (nextObj.done) + break; + res += nextObj.value; + assertRecoveredOnBailout(nextObj, true); + } while (true); + return res; +} + +for (var j = 0; j < 10; j++) + assertEq(f(), max * (max - 1) / 2); diff --git a/js/src/jit-test/tests/ion/recover-objects.js b/js/src/jit-test/tests/ion/recover-objects.js index 07a990b606..30274f7efa 100644 --- a/js/src/jit-test/tests/ion/recover-objects.js +++ b/js/src/jit-test/tests/ion/recover-objects.js @@ -1,15 +1,29 @@ +// |jit-test| test-join=--no-unboxed-objects +// +// Unboxed object optimization might not trigger in all cases, thus we ensure +// that Scalar Replacement optimization is working well independently of the +// object representation. + // Ion eager fails the test below because we have not yet created any // template object in baseline before running the content of the top-level // function. if (getJitCompilerOptions()["ion.warmup.trigger"] <= 90) setJitCompilerOption("ion.warmup.trigger", 90); +// This test checks that we are able to remove the getprop & setprop with scalar +// replacement, so we should not force inline caches, as this would skip the +// generation of getprop & setprop instructions. +if (getJitCompilerOptions()["ion.forceinlineCaches"]) + setJitCompilerOption("ion.forceinlineCaches", 0); + +function resumeHere() {} var uceFault = function (i) { if (i > 98) uceFault = function (i) { return true; }; return false; }; + // Without "use script" in the inner function, the arguments might be // obersvable. function inline_notSoEmpty1(a, b, c, d) { @@ -19,11 +33,11 @@ function inline_notSoEmpty1(a, b, c, d) { } var uceFault_notSoEmpty1 = eval(uneval(uceFault).replace('uceFault', 'uceFault_notSoEmpty1')); function notSoEmpty1() { - var a = { v: i, notunboxed: undefined }; - var b = { v: 1 + a.v, notunboxed: undefined }; - var c = { v: 2 + b.v, notunboxed: undefined }; - var d = { v: 3 + c.v, notunboxed: undefined }; - var unused = { v: 4 + d.v, notunboxed: undefined }; + var a = { v: i }; + var b = { v: 1 + a.v }; + var c = { v: 2 + b.v }; + var d = { v: 3 + c.v }; + var unused = { v: 4 + d.v }; var res = inline_notSoEmpty1(a, b, c, d); if (uceFault_notSoEmpty1(i) || uceFault_notSoEmpty1(i)) assertEq(i, res.v); @@ -36,23 +50,23 @@ function notSoEmpty1() { assertRecoveredOnBailout(c, true); assertRecoveredOnBailout(d, true); assertRecoveredOnBailout(unused, true); - // Scalar Replacement is coming after the branch removal made by GVN, and - // the ucefault branch is not taken yet. - assertRecoveredOnBailout(res, false); + // The ucefault branch is not taken yet, and GVN removes it. Scalar + // Replacement thus removes the creation of the object. + assertRecoveredOnBailout(res, true); } // Check that we can recover objects with their content. function inline_notSoEmpty2(a, b, c, d) { "use strict"; - return { v: (a.v + b.v + c.v + d.v - 10) / 4, notunboxed: undefined }; + return { v: (a.v + b.v + c.v + d.v - 10) / 4 }; } var uceFault_notSoEmpty2 = eval(uneval(uceFault).replace('uceFault', 'uceFault_notSoEmpty2')); function notSoEmpty2(i) { - var a = { v: i, notunboxed: undefined }; - var b = { v: 1 + a.v, notunboxed: undefined }; - var c = { v: 2 + b.v, notunboxed: undefined }; - var d = { v: 3 + c.v, notunboxed: undefined }; - var unused = { v: 4 + d.v, notunboxed: undefined }; + var a = { v: i }; + var b = { v: 1 + a.v }; + var c = { v: 2 + b.v }; + var d = { v: 3 + c.v }; + var unused = { v: 4 + d.v }; var res = inline_notSoEmpty2(a, b, c, d); if (uceFault_notSoEmpty2(i) || uceFault_notSoEmpty2(i)) assertEq(i, res.v); @@ -61,22 +75,22 @@ function notSoEmpty2(i) { assertRecoveredOnBailout(c, true); assertRecoveredOnBailout(d, true); assertRecoveredOnBailout(unused, true); - // Scalar Replacement is coming after the branch removal made by GVN, and - // the ucefault branch is not taken yet. - assertRecoveredOnBailout(res, false); + // The ucefault branch is not taken yet, and GVN removes it. Scalar + // Replacement thus removes the creation of the object. + assertRecoveredOnBailout(res, true); } // Check that we can recover objects with their content. var argFault_observeArg = function (i) { if (i > 98) return inline_observeArg.arguments[0]; - return { test : i, notunboxed: undefined }; + return { test : i }; }; function inline_observeArg(obj, i) { return argFault_observeArg(i); } function observeArg(i) { - var obj = { test: i, notunboxed: undefined }; + var obj = { test: i }; var res = inline_observeArg(obj, i); assertEq(res.test, i); assertRecoveredOnBailout(obj, true); @@ -84,7 +98,7 @@ function observeArg(i) { // Check case where one successor can have multiple times the same predecessor. function complexPhi(i) { - var obj = { test: i, notunboxed: undefined }; + var obj = { test: i }; switch (i) { // TableSwitch case 0: obj.test = 0; break; case 1: obj.test = 1; break; @@ -103,12 +117,12 @@ function complexPhi(i) { function withinIf(i) { var x = undefined; if (i % 2 == 0) { - let obj = { foo: i, notunboxed: undefined }; + let obj = { foo: i }; x = obj.foo; assertRecoveredOnBailout(obj, true); obj = undefined; } else { - let obj = { bar: i, notunboxed: undefined }; + let obj = { bar: i }; x = obj.bar; assertRecoveredOnBailout(obj, true); obj = undefined; @@ -118,22 +132,20 @@ function withinIf(i) { // Check case where one successor can have multiple times the same predecessor. function unknownLoad(i) { - var obj = { foo: i, notunboxed: undefined }; + var obj = { foo: i }; assertEq(obj.bar, undefined); // Unknown properties are using GetPropertyCache. assertRecoveredOnBailout(obj, false); } // Check with dynamic slots. -function resumeHere() {} function dynamicSlots(i) { var obj = { p0: i + 0, p1: i + 1, p2: i + 2, p3: i + 3, p4: i + 4, p5: i + 5, p6: i + 6, p7: i + 7, p8: i + 8, p9: i + 9, p10: i + 10, p11: i + 11, p12: i + 12, p13: i + 13, p14: i + 14, p15: i + 15, p16: i + 16, p17: i + 17, p18: i + 18, p19: i + 19, p20: i + 20, p21: i + 21, p22: i + 22, p23: i + 23, p24: i + 24, p25: i + 25, p26: i + 26, p27: i + 27, p28: i + 28, p29: i + 29, p30: i + 30, p31: i + 31, p32: i + 32, p33: i + 33, p34: i + 34, p35: i + 35, p36: i + 36, p37: i + 37, p38: i + 38, p39: i + 39, p40: i + 40, - p41: i + 41, p42: i + 42, p43: i + 43, p44: i + 44, p45: i + 45, p46: i + 46, p47: i + 47, p48: i + 48, p49: i + 49, p50: i + 50, - notunboxed: undefined + p41: i + 41, p42: i + 42, p43: i + 43, p44: i + 44, p45: i + 45, p46: i + 46, p47: i + 47, p48: i + 48, p49: i + 49, p50: i + 50 }; // Add a function call to capture a resumepoint at the end of the call or // inside the inlined block, such as the bailout does not rewind to the @@ -148,7 +160,6 @@ function Point(x, y) { this.x = x; this.y = y; - this.notUnboxed = undefined; } function createThisWithTemplate(i) @@ -159,7 +170,6 @@ function createThisWithTemplate(i) assertRecoveredOnBailout(p, true); } - for (var i = 0; i < 100; i++) { notSoEmpty1(i); notSoEmpty2(i); diff --git a/js/src/jit-test/tests/ion/testFloat32.js b/js/src/jit-test/tests/ion/testFloat32.js index dfbc1e0cd5..18ebb6c6f5 100644 --- a/js/src/jit-test/tests/ion/testFloat32.js +++ b/js/src/jit-test/tests/ion/testFloat32.js @@ -1,3 +1,9 @@ +// This test checks that we are able to optimize float32 inputs. As +// GetElementIC (float32 array accesses) output is not specialized with Float32 +// output types, we should not force inline caches. +if (getJitCompilerOptions()["ion.forceinlineCaches"]) + setJitCompilerOption("ion.forceinlineCaches", 0); + // Fuzz tests (function(){ // diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp index d718a2ba91..096b865a21 100644 --- a/js/src/jit/BacktrackingAllocator.cpp +++ b/js/src/jit/BacktrackingAllocator.cpp @@ -5,6 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "jit/BacktrackingAllocator.h" + +#include "jsprf.h" + #include "jit/BitSet.h" using namespace js; @@ -322,14 +325,19 @@ VirtualRegister::setInitialDefinition(CodePosition from) } LiveRange* -VirtualRegister::rangeFor(CodePosition pos) const +VirtualRegister::rangeFor(CodePosition pos, bool preferRegister /* = false */) const { + LiveRange* found = nullptr; for (LiveRange::RegisterLinkIterator iter = rangesBegin(); iter; iter++) { LiveRange* range = LiveRange::get(*iter); - if (range->covers(pos)) - return range; + if (range->covers(pos)) { + if (!preferRegister || range->bundle()->allocation().isRegister()) + return range; + if (!found) + found = range; + } } - return nullptr; + return found; } void @@ -1702,7 +1710,7 @@ BacktrackingAllocator::resolveControlFlow() if (skip) continue; - LiveRange* predecessorRange = reg.rangeFor(start.previous()); + LiveRange* predecessorRange = reg.rangeFor(start.previous(), /* preferRegister = */ true); if (start.subpos() == CodePosition::INPUT) { if (!moveInput(ins->toInstruction(), predecessorRange, range, reg.type())) return false; @@ -1738,7 +1746,7 @@ BacktrackingAllocator::resolveControlFlow() MOZ_ASSERT(predecessor->mir()->numSuccessors() == 1); LAllocation* input = phi->getOperand(k); - LiveRange* from = vreg(input).rangeFor(exitOf(predecessor)); + LiveRange* from = vreg(input).rangeFor(exitOf(predecessor), /* preferRegister = */ true); MOZ_ASSERT(from); if (!moveAtExit(predecessor, from, to, def->type())) @@ -1763,7 +1771,7 @@ BacktrackingAllocator::resolveControlFlow() if (to->covers(exitOf(predecessor))) continue; - LiveRange* from = reg.rangeFor(exitOf(predecessor)); + LiveRange* from = reg.rangeFor(exitOf(predecessor), /* preferRegister = */ true); if (mSuccessor->numPredecessors() > 1) { MOZ_ASSERT(predecessor->mir()->numSuccessors() == 1); diff --git a/js/src/jit/BacktrackingAllocator.h b/js/src/jit/BacktrackingAllocator.h index abc7988483..0e1ab44b3b 100644 --- a/js/src/jit/BacktrackingAllocator.h +++ b/js/src/jit/BacktrackingAllocator.h @@ -524,7 +524,7 @@ class VirtualRegister LiveRange* lastRange() const { return LiveRange::get(ranges_.back()); } - LiveRange* rangeFor(CodePosition pos) const; + LiveRange* rangeFor(CodePosition pos, bool preferRegister = false) const; void removeRange(LiveRange* range); void addRange(LiveRange* range); diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp index b5f7f1b3ec..5b5b2ad6de 100644 --- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -1573,7 +1573,7 @@ jit::BailoutIonToBaseline(JSContext* cx, JitActivation* activation, JitFrameIter BaselineBailoutInfo *info = builder.info(); uint8_t* newsp = info->incomingStack - (info->copyStackTop - info->copyStackBottom); #if defined(JS_ARM_SIMULATOR) || defined(JS_ARM64_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - if (SimulatorType::Current()->overRecursed(uintptr_t(newsp))) + if (Simulator::Current()->overRecursed(uintptr_t(newsp))) overRecursed = true; #else JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, newsp, overRecursed = true); diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 7f7f8461c0..624a36bbe3 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -3448,13 +3448,14 @@ BaselineCompiler::emit_JSOP_REST() { frame.syncStack(0); - ArrayObject* templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject); + JSObject* templateObject = + ObjectGroup::newArrayObject(cx, nullptr, 0, TenuredObject, + ObjectGroup::NewArrayKind::UnknownIndex); if (!templateObject) return false; - ObjectGroup::fixRestArgumentsGroup(cx, templateObject); // Call IC. - ICRest_Fallback::Compiler compiler(cx, templateObject); + ICRest_Fallback::Compiler compiler(cx, &templateObject->as()); if (!emitOpIC(compiler.getStub(&stubSpace_))) return false; diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 0f9f306907..330bc90045 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -261,8 +261,8 @@ ICWarmUpCounter_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // Call DoWarmUpCounterFallback to compile/check-for Ion-compiled function { // Push IonOsrTempData pointer storage - masm.subPtr(Imm32(sizeof(void*)), BaselineStackReg); - masm.push(BaselineStackReg); + masm.subFromStackPtr(Imm32(sizeof(void*))); + masm.push(masm.getStackPointer()); // Push stub pointer. masm.push(ICStubReg); @@ -298,7 +298,7 @@ ICWarmUpCounter_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // [...Baseline-Frame...] // Restore the stack pointer to point to the saved frame pointer. - masm.movePtr(BaselineFrameReg, BaselineStackReg); + masm.moveToStackPtr(BaselineFrameReg); // Discard saved frame pointer, so that the return address is on top of // the stack. @@ -319,9 +319,9 @@ ICWarmUpCounter_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // callee frame), turn on profiling, then return to this frame, // and then OSR with profiling turned on. In this case, allow for // lastProfilingFrame to be null. - masm.branchPtr(Assembler::Equal, scratchReg, Imm32(0), &checkOk); + masm.branchPtr(Assembler::Equal, scratchReg, ImmWord(0), &checkOk); - masm.branchPtr(Assembler::Equal, scratchReg, BaselineStackReg, &checkOk); + masm.branchStackPtr(Assembler::Equal, scratchReg, &checkOk); masm.assumeUnreachable("Baseline OSR lastProfilingFrame mismatch."); masm.bind(&checkOk); } @@ -2670,11 +2670,8 @@ CheckHasNoSuchProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, while (curObj) { if (curObj->isNative()) { // Don't handle proto chains with resolve hooks. - if (ClassMayResolveId(cx->names(), curObj->getClass(), NameToId(name), curObj) || - curObj->getClass()->addProperty) - { + if (ClassMayResolveId(cx->names(), curObj->getClass(), NameToId(name), curObj)) return false; - } if (curObj->as().contains(cx, NameToId(name))) return false; } else if (curObj != obj) { @@ -3690,7 +3687,7 @@ ICGetElemNativeCompiler::emitCallScripted(MacroAssembler& masm, Register objReg) masm.movePtr(ImmGCPtr(argumentsRectifier), code); masm.loadPtr(Address(code, JitCode::offsetOfCode()), code); - masm.mov(ImmWord(0), ArgumentsRectifierReg); + masm.movePtr(ImmWord(0), ArgumentsRectifierReg); } masm.bind(&noUnderflow); @@ -3867,12 +3864,12 @@ ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm) } else { masm.loadValue(valAddr, R0); if (popR1) - masm.addPtr(ImmWord(sizeof(size_t)), BaselineStackReg); + masm.addToStackPtr(ImmWord(sizeof(size_t))); } #else masm.loadValue(valAddr, R0); if (popR1) - masm.addPtr(ImmWord(sizeof(size_t)), BaselineStackReg); + masm.addToStackPtr(ImmWord(sizeof(size_t))); #endif } else { @@ -3886,7 +3883,7 @@ ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm) // GetElem key (R1) is no longer needed. if (popR1) - masm.addPtr(ImmWord(sizeof(size_t)), BaselineStackReg); + masm.addToStackPtr(ImmWord(sizeof(size_t))); emitCallNative(masm, objReg); @@ -3902,7 +3899,7 @@ ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm) // At this point, we are guaranteed to successfully complete. if (popR1) - masm.addPtr(Imm32(sizeof(size_t)), BaselineStackReg); + masm.addToStackPtr(Imm32(sizeof(size_t))); emitCallScripted(masm, objReg); } @@ -4762,8 +4759,8 @@ ICSetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // so we push the index, then overwrite the rhs Value with R0 // and push the rhs value. masm.pushValue(R1); - masm.loadValue(Address(BaselineStackReg, sizeof(Value)), R1); - masm.storeValue(R0, Address(BaselineStackReg, sizeof(Value))); + masm.loadValue(Address(masm.getStackPointer(), sizeof(Value)), R1); + masm.storeValue(R0, Address(masm.getStackPointer(), sizeof(Value))); masm.pushValue(R1); // Push arguments. @@ -4771,13 +4768,13 @@ ICSetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // Push index. On x86 and ARM two push instructions are emitted so use a // separate register to store the old stack pointer. - masm.mov(BaselineStackReg, R1.scratchReg()); + masm.moveStackPtrTo(R1.scratchReg()); masm.pushValue(Address(R1.scratchReg(), 2 * sizeof(Value))); masm.pushValue(R0); // Object. // Push pointer to stack values, so that the stub can overwrite the object // (pushed for the decompiler) with the rhs. - masm.computeEffectiveAddress(Address(BaselineStackReg, 3 * sizeof(Value)), R0.scratchReg()); + masm.computeEffectiveAddress(Address(masm.getStackPointer(), 3 * sizeof(Value)), R0.scratchReg()); masm.push(R0.scratchReg()); masm.push(ICStubReg); @@ -4845,7 +4842,7 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) // Stack is now: { ..., rhs-value, object-value, key-value, maybe?-RET-ADDR } // Load rhs-value into R0 - masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + ICStackValueOffset), R0); + masm.loadValue(Address(masm.getStackPointer(), 2 * sizeof(Value) + ICStackValueOffset), R0); // Call the type-update stub. if (!callTypeUpdateIC(masm, sizeof(Value))) @@ -4860,7 +4857,7 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) // Trigger post barriers here on the value being written. Fields which // objects can be written to also need update stubs. masm.Push(R1); - masm.loadValue(Address(BaselineStackReg, sizeof(Value) + ICStackValueOffset), R1); + masm.loadValue(Address(masm.getStackPointer(), sizeof(Value) + ICStackValueOffset), R1); LiveGeneralRegisterSet saveRegs; saveRegs.add(R0); @@ -4908,7 +4905,7 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) regs.takeUnchecked(obj); regs.takeUnchecked(key); - Address valueAddr(BaselineStackReg, ICStackValueOffset); + Address valueAddr(masm.getStackPointer(), ICStackValueOffset); // We need to convert int32 values being stored into doubles. In this case // the heap typeset is guaranteed to contain both int32 and double, so it's @@ -4943,7 +4940,7 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) EmitUnboxedPreBarrierForBaseline(masm, address, unboxedType_); - Address valueAddr(BaselineStackReg, ICStackValueOffset + sizeof(Value)); + Address valueAddr(masm.getStackPointer(), ICStackValueOffset + sizeof(Value)); masm.Push(R0); masm.loadValue(valueAddr, R0); masm.storeUnboxedProperty(address, unboxedType_, @@ -5057,7 +5054,7 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm) if (needsUpdateStubs()) { // Stack is now: { ..., rhs-value, object-value, key-value, maybe?-RET-ADDR } // Load rhs-value in to R0 - masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + ICStackValueOffset), R0); + masm.loadValue(Address(masm.getStackPointer(), 2 * sizeof(Value) + ICStackValueOffset), R0); // Call the type-update stub. if (!callTypeUpdateIC(masm, sizeof(Value))) @@ -5074,7 +5071,7 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm) // Trigger post barriers here on the value being written. Fields which // objects can be written to also need update stubs. masm.Push(R1); - masm.loadValue(Address(BaselineStackReg, sizeof(Value) + ICStackValueOffset), R1); + masm.loadValue(Address(masm.getStackPointer(), sizeof(Value) + ICStackValueOffset), R1); LiveGeneralRegisterSet saveRegs; saveRegs.add(R0); @@ -5136,7 +5133,7 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm) Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS), &dontConvertDoubles); - Address valueAddr(BaselineStackReg, ICStackValueOffset); + Address valueAddr(masm.getStackPointer(), ICStackValueOffset); // Note that double arrays are only created by IonMonkey, so if we have no // floating-point support Ion is disabled and there should be no double arrays. @@ -5169,7 +5166,7 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm) // Write the value first, since this can fail. No need for pre-barrier // since we're not overwriting an old value. masm.Push(R0); - Address valueAddr(BaselineStackReg, ICStackValueOffset + sizeof(Value)); + Address valueAddr(masm.getStackPointer(), ICStackValueOffset + sizeof(Value)); masm.loadValue(valueAddr, R0); BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_))); masm.storeUnboxedProperty(address, unboxedType_, @@ -5326,7 +5323,7 @@ ICSetElem_TypedArray::Compiler::generateStubCode(MacroAssembler& masm) LoadTypedThingData(masm, layout_, obj, scratchReg); BaseIndex dest(scratchReg, key, ScaleFromElemWidth(Scalar::byteSize(type_))); - Address value(BaselineStackReg, ICStackValueOffset); + Address value(masm.getStackPointer(), ICStackValueOffset); // We need a second scratch register. It's okay to clobber the type tag of // R0 or R1, as long as it's restored before jumping to the next stub. @@ -7366,7 +7363,7 @@ ICGetPropNativeCompiler::generateStubCode(MacroAssembler& masm) masm.bind(&skipNoSuchMethod); // Pop pushed objReg. - masm.addPtr(Imm32(sizeof(void*)), BaselineStackReg); + masm.addToStackPtr(Imm32(sizeof(void*))); masm.bind(&afterNoSuchMethod); } else { masm.loadValue(result, R0); @@ -7534,7 +7531,7 @@ ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) masm.movePtr(ImmGCPtr(argumentsRectifier), code); masm.loadPtr(Address(code, JitCode::offsetOfCode()), code); - masm.mov(ImmWord(0), ArgumentsRectifierReg); + masm.movePtr(ImmWord(0), ArgumentsRectifierReg); } masm.bind(&noUnderflow); @@ -8698,7 +8695,7 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm) // Load RHS into R0 for TypeUpdate check. // Stack is currently: [..., ObjValue, RHSValue, MaybeReturnAddr? ] - masm.loadValue(Address(BaselineStackReg, ICStackValueOffset), R0); + masm.loadValue(Address(masm.getStackPointer(), ICStackValueOffset), R0); // Call the type-update stub. if (!callTypeUpdateIC(masm, sizeof(Value))) @@ -8928,7 +8925,7 @@ ICSetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm) masm.addPtr(secondScratch, scratch); Address dest(scratch, 0); - Address value(BaselineStackReg, 0); + Address value(masm.getStackPointer(), 0); if (fieldDescr_->is()) { Scalar::Type type = fieldDescr_->as().type(); @@ -9062,7 +9059,7 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) masm.movePtr(ImmGCPtr(argumentsRectifier), code); masm.loadPtr(Address(code, JitCode::offsetOfCode()), code); - masm.mov(ImmWord(1), ArgumentsRectifierReg); + masm.movePtr(ImmWord(1), ArgumentsRectifierReg); } masm.bind(&noUnderflow); @@ -9148,7 +9145,7 @@ ICSetProp_CallNative::Compiler::generateStubCode(MacroAssembler& masm) // To Push R1, read it off of the stowed values on stack. // Stack: [ ..., R0, R1, ..STUBFRAME-HEADER.. ] - masm.movePtr(BaselineStackReg, scratch); + masm.moveStackPtrTo(scratch); masm.pushValue(Address(scratch, STUB_FRAME_SIZE)); masm.push(objReg); masm.push(callee); @@ -9264,8 +9261,8 @@ TryAttachFunCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, } static bool -GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc, - Native native, const CallArgs& args, MutableHandleObject res) +GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args, + MutableHandleObject res) { // Check for natives to which template objects can be attached. This is // done to provide templates to Ion for inlining these natives later on. @@ -9279,27 +9276,16 @@ GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc, count = args.length(); else if (args.length() == 1 && args[0].isInt32() && args[0].toInt32() >= 0) count = args[0].toInt32(); - res.set(NewDenseUnallocatedArray(cx, count, nullptr, TenuredObject)); - if (!res) - return false; - ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); - if (!group) - return false; - res->setGroup(group); - return true; - } - - if (native == intrinsic_NewDenseArray) { - res.set(NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject)); - if (!res) - return false; - - ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); - if (!group) - return false; - res->setGroup(group); - return true; + if (count <= ArrayObject::EagerAllocationMaxLength) { + // With this and other array templates, set forceAnalyze so that we + // don't end up with a template whose structure might change later. + res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject, + /* forceAnalyze = */ true)); + if (!res) + return false; + return true; + } } if (native == js::array_concat || native == js::array_slice) { @@ -9312,14 +9298,10 @@ GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc, } if (native == js::str_split && args.length() == 1 && args[0].isString()) { - res.set(NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject)); + res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, 0, TenuredObject, + /* forceAnalyze = */ true)); if (!res) return false; - - ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); - if (!group) - return false; - res->setGroup(group); return true; } @@ -9613,7 +9595,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb RootedObject templateObject(cx); if (MOZ_LIKELY(!isSpread)) { CallArgs args = CallArgsFromVp(argc, vp); - if (!GetTemplateObjectForNative(cx, script, pc, fun->native(), args, &templateObject)) + if (!GetTemplateObjectForNative(cx, fun->native(), args, &templateObject)) return false; } @@ -9635,24 +9617,16 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb } static bool -CopyArray(JSContext* cx, HandleArrayObject obj, MutableHandleValue result) +CopyArray(JSContext* cx, HandleObject obj, MutableHandleValue result) { - MOZ_ASSERT(obj->is()); - uint32_t length = obj->as().length(); - MOZ_ASSERT(obj->getDenseInitializedLength() == length); - - RootedObjectGroup group(cx, obj->getGroup(cx)); - if (!group) + uint32_t length = GetAnyBoxedOrUnboxedArrayLength(obj); + JSObject* nobj = NewFullyAllocatedArrayTryReuseGroup(cx, obj, length, TenuredObject, + /* forceAnalyze = */ true); + if (!nobj) return false; + CopyAnyBoxedOrUnboxedDenseElements(cx, nobj, obj, 0, 0, length); - RootedArrayObject newObj(cx, NewDenseFullyAllocatedArray(cx, length, nullptr, TenuredObject)); - if (!newObj) - return false; - - newObj->setGroup(group); - newObj->setDenseInitializedLength(length); - newObj->initDenseElements(0, obj->getDenseElements(), length); - result.setObject(*newObj); + result.setObject(*nobj); return true; } @@ -9680,7 +9654,7 @@ TryAttachStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript script, RootedString thisString(cx, thisv.toString()); RootedString argString(cx, args[0].toString()); - RootedArrayObject obj(cx, &res.toObject().as()); + RootedObject obj(cx, &res.toObject()); RootedValue arr(cx); // Copy the array before storing in stub. @@ -9688,14 +9662,17 @@ TryAttachStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript script, return false; // Atomize all elements of the array. - RootedArrayObject arrObj(cx, &arr.toObject().as()); - uint32_t initLength = arrObj->length(); + RootedObject arrObj(cx, &arr.toObject()); + uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(arrObj); for (uint32_t i = 0; i < initLength; i++) { - JSAtom* str = js::AtomizeString(cx, arrObj->getDenseElement(i).toString()); + JSAtom* str = js::AtomizeString(cx, GetAnyBoxedOrUnboxedDenseElement(arrObj, i).toString()); if (!str) return false; - arrObj->setDenseElement(i, StringValue(str)); + if (!SetAnyBoxedOrUnboxedDenseElement(cx, arrObj, i, StringValue(str))) { + // The value could not be stored to an unboxed dense element. + return true; + } } ICCall_StringSplit::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(), @@ -9852,7 +9829,7 @@ ICCallStubCompiler::pushCallArguments(MacroAssembler& masm, AllocatableGeneralRe // Account for new.target Register count = regs.takeAny(); - masm.mov(argcReg, count); + masm.move32(argcReg, count); // If we are setting up for a jitcall, we have to align the stack taking // into account the args and newTarget. We could also count callee and |this|, @@ -9868,7 +9845,7 @@ ICCallStubCompiler::pushCallArguments(MacroAssembler& masm, AllocatableGeneralRe // argPtr initially points to the last argument. Register argPtr = regs.takeAny(); - masm.mov(BaselineStackReg, argPtr); + masm.moveStackPtrTo(argPtr); // Skip 4 pointers pushed on top of the arguments: the frame descriptor, // return address, old frame pointer and stub reg. @@ -9901,7 +9878,8 @@ void ICCallStubCompiler::guardSpreadCall(MacroAssembler& masm, Register argcReg, Label* failure, bool isConstructing) { - masm.unboxObject(Address(BaselineStackReg, isConstructing * sizeof(Value) + ICStackValueOffset), argcReg); + masm.unboxObject(Address(masm.getStackPointer(), + isConstructing * sizeof(Value) + ICStackValueOffset), argcReg); masm.loadPtr(Address(argcReg, NativeObject::offsetOfElements()), argcReg); masm.load32(Address(argcReg, ObjectElements::offsetOfLength()), argcReg); @@ -9921,7 +9899,8 @@ ICCallStubCompiler::pushSpreadCallArguments(MacroAssembler& masm, { // Pull the array off the stack before aligning. Register startReg = regs.takeAny(); - masm.unboxObject(Address(BaselineStackReg, (isConstructing * sizeof(Value)) + STUB_FRAME_SIZE), startReg); + masm.unboxObject(Address(masm.getStackPointer(), + (isConstructing * sizeof(Value)) + STUB_FRAME_SIZE), startReg); masm.loadPtr(Address(startReg, NativeObject::offsetOfElements()), startReg); // Align the stack such that the JitFrameLayout is aligned on the @@ -9930,7 +9909,7 @@ ICCallStubCompiler::pushSpreadCallArguments(MacroAssembler& masm, Register alignReg = argcReg; if (isConstructing) { alignReg = regs.takeAny(); - masm.mov(argcReg, alignReg); + masm.movePtr(argcReg, alignReg); masm.addPtr(Imm32(1), alignReg); } masm.alignJitStackBasedOnNArgs(alignReg); @@ -9946,7 +9925,7 @@ ICCallStubCompiler::pushSpreadCallArguments(MacroAssembler& masm, // Push arguments: set up endReg to point to &array[argc] Register endReg = regs.takeAny(); - masm.mov(argcReg, endReg); + masm.movePtr(argcReg, endReg); static_assert(sizeof(Value) == 8, "Value must be 8 bytes"); masm.lshiftPtr(Imm32(3), endReg); masm.addPtr(startReg, endReg); @@ -9985,7 +9964,7 @@ ICCallStubCompiler::guardFunApply(MacroAssembler& masm, AllocatableGeneralRegist // Stack looks like: // [..., CalleeV, ThisV, Arg0V, Arg1V ] - Address secondArgSlot(BaselineStackReg, ICStackValueOffset); + Address secondArgSlot(masm.getStackPointer(), ICStackValueOffset); if (applyThing == FunApply_MagicArgs) { // Ensure that the second arg is magic arguments. masm.branchTestMagic(Assembler::NotEqual, secondArgSlot, failure); @@ -10058,7 +10037,7 @@ ICCallStubCompiler::guardFunApply(MacroAssembler& masm, AllocatableGeneralRegist // Load the callee, ensure that it's fun_apply ValueOperand val = regs.takeAnyValue(); - Address calleeSlot(BaselineStackReg, ICStackValueOffset + (3 * sizeof(Value))); + Address calleeSlot(masm.getStackPointer(), ICStackValueOffset + (3 * sizeof(Value))); masm.loadValue(calleeSlot, val); masm.branchTestObject(Assembler::NotEqual, val, failure); @@ -10072,7 +10051,7 @@ ICCallStubCompiler::guardFunApply(MacroAssembler& masm, AllocatableGeneralRegist // Load the |thisv|, ensure that it's a scripted function with a valid baseline or ion // script, or a native function. - Address thisSlot(BaselineStackReg, ICStackValueOffset + (2 * sizeof(Value))); + Address thisSlot(masm.getStackPointer(), ICStackValueOffset + (2 * sizeof(Value))); masm.loadValue(thisSlot, val); masm.branchTestObject(Assembler::NotEqual, val, failure); @@ -10194,7 +10173,7 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // callee masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE)); - masm.push(BaselineStackReg); + masm.push(masm.getStackPointer()); masm.push(ICStubReg); pushFramePtr(masm, R0.scratchReg()); @@ -10217,7 +10196,7 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm) pushCallArguments(masm, regs, R0.scratchReg(), /* isJitCall = */ false, isConstructing_); - masm.push(BaselineStackReg); + masm.push(masm.getStackPointer()); masm.push(R0.scratchReg()); masm.push(ICStubReg); @@ -10240,7 +10219,7 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // Load passed-in ThisV into R1 just in case it's needed. Need to do this before // we leave the stub frame since that info will be lost. // Current stack: [...., ThisV, ActualArgc, CalleeToken, Descriptor ] - masm.loadValue(Address(BaselineStackReg, 3 * sizeof(size_t)), R1); + masm.loadValue(Address(masm.getStackPointer(), 3 * sizeof(size_t)), R1); leaveStubFrame(masm, true); @@ -10309,11 +10288,11 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm) // Stack Layout: [ ..., CalleeVal, ThisVal, Arg0Val, ..., ArgNVal, [newTarget] +ICStackValueOffset+ ] if (isSpread_) { unsigned skipToCallee = (2 + isConstructing_) * sizeof(Value); - masm.loadValue(Address(BaselineStackReg, skipToCallee + ICStackValueOffset), R1); + masm.loadValue(Address(masm.getStackPointer(), skipToCallee + ICStackValueOffset), R1); } else { // Account for newTarget, if necessary unsigned nonArgsSkip = (1 + isConstructing_) * sizeof(Value); - BaseValueIndex calleeSlot(BaselineStackReg, argcReg, ICStackValueOffset + nonArgsSkip); + BaseValueIndex calleeSlot(masm.getStackPointer(), argcReg, ICStackValueOffset + nonArgsSkip); masm.loadValue(calleeSlot, R1); } regs.take(R1); @@ -10375,10 +10354,10 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm) // Stack now looks like: // [..., Callee, ThisV, Arg0V, ..., ArgNV, NewTarget, StubFrameHeader, ArgC ] if (isSpread_) { - masm.loadValue(Address(BaselineStackReg, + masm.loadValue(Address(masm.getStackPointer(), 3 * sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t)), R1); } else { - BaseValueIndex calleeSlot2(BaselineStackReg, argcReg, + BaseValueIndex calleeSlot2(masm.getStackPointer(), argcReg, 2 * sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t)); masm.loadValue(calleeSlot2, R1); } @@ -10409,14 +10388,16 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm) // Stack now looks like: // [..., Callee, ThisV, Arg0V, ..., ArgNV, [NewTarget], StubFrameHeader ] if (isSpread_) { - masm.storeValue(R0, Address(BaselineStackReg, (1 + isConstructing_) * sizeof(Value) + STUB_FRAME_SIZE)); + masm.storeValue(R0, Address(masm.getStackPointer(), + (1 + isConstructing_) * sizeof(Value) + STUB_FRAME_SIZE)); } else { - BaseValueIndex thisSlot(BaselineStackReg, argcReg, STUB_FRAME_SIZE + isConstructing_ * sizeof(Value)); + BaseValueIndex thisSlot(masm.getStackPointer(), argcReg, + STUB_FRAME_SIZE + isConstructing_ * sizeof(Value)); masm.storeValue(R0, thisSlot); } // Restore the stub register from the baseline stub frame. - masm.loadPtr(Address(BaselineStackReg, STUB_FRAME_SAVED_STUB_OFFSET), ICStubReg); + masm.loadPtr(Address(masm.getStackPointer(), STUB_FRAME_SAVED_STUB_OFFSET), ICStubReg); // Reload callee script. Note that a GC triggered by CreateThis may // have destroyed the callee BaselineScript and IonScript. CreateThis is @@ -10426,11 +10407,11 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm) // Just need to load the script now. if (isSpread_) { unsigned skipForCallee = (2 + isConstructing_) * sizeof(Value); - masm.loadValue(Address(BaselineStackReg, skipForCallee + STUB_FRAME_SIZE), R0); + masm.loadValue(Address(masm.getStackPointer(), skipForCallee + STUB_FRAME_SIZE), R0); } else { // Account for newTarget, if necessary unsigned nonArgsSkip = (1 + isConstructing_) * sizeof(Value); - BaseValueIndex calleeSlot3(BaselineStackReg, argcReg, nonArgsSkip + STUB_FRAME_SIZE); + BaseValueIndex calleeSlot3(masm.getStackPointer(), argcReg, nonArgsSkip + STUB_FRAME_SIZE); masm.loadValue(calleeSlot3, R0); } callee = masm.extractObject(R0, ExtractTemp0); @@ -10487,7 +10468,7 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm) masm.movePtr(ImmGCPtr(argumentsRectifier), code); masm.loadPtr(Address(code, JitCode::offsetOfCode()), code); - masm.mov(argcReg, ArgumentsRectifierReg); + masm.movePtr(argcReg, ArgumentsRectifierReg); } masm.bind(&noUnderflow); @@ -10511,11 +10492,11 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm) // + sizeof(Descriptor) + sizeof(Callee) + sizeof(ActualArgc) // + stubFrameSize(Descriptor) // - sizeof(ICStubReg) - sizeof(BaselineFrameReg) - Address descriptorAddr(BaselineStackReg, 0); + Address descriptorAddr(masm.getStackPointer(), 0); masm.loadPtr(descriptorAddr, BaselineFrameReg); masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), BaselineFrameReg); masm.addPtr(Imm32((3 - 2) * sizeof(size_t)), BaselineFrameReg); - masm.addPtr(BaselineStackReg, BaselineFrameReg); + masm.addStackPtrTo(BaselineFrameReg); // Load the number of arguments present before the stub frame. Register argcReg = JSReturnOperand.scratchReg(); @@ -10523,7 +10504,7 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm) // Account for the Array object. masm.move32(Imm32(1), argcReg); } else { - Address argcAddr(BaselineStackReg, 2 * sizeof(size_t)); + Address argcAddr(masm.getStackPointer(), 2 * sizeof(size_t)); masm.loadPtr(argcAddr, argcReg); } @@ -10552,14 +10533,14 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm) inStubFrame_ = true; leaveStubFrame(masm, false); if (argcReg != R0.scratchReg()) - masm.mov(argcReg, R0.scratchReg()); + masm.movePtr(argcReg, R0.scratchReg()); masm.bind(&failure); EmitStubGuardFailure(masm); return true; } -typedef bool (*CopyArrayFn)(JSContext*, HandleArrayObject, MutableHandleValue); +typedef bool (*CopyArrayFn)(JSContext*, HandleObject, MutableHandleValue); static const VMFunction CopyArrayInfo = FunctionInfo(CopyArray); bool @@ -10581,7 +10562,7 @@ ICCall_StringSplit::Compiler::generateStubCode(MacroAssembler& masm) // Guard that callee is native function js::str_split. { - Address calleeAddr(BaselineStackReg, ICStackValueOffset + (2 * sizeof(Value))); + Address calleeAddr(masm.getStackPointer(), ICStackValueOffset + (2 * sizeof(Value))); ValueOperand calleeVal = regs.takeAnyValue(); // Ensure that callee is an object. @@ -10603,7 +10584,7 @@ ICCall_StringSplit::Compiler::generateStubCode(MacroAssembler& masm) // Guard argument. { // Ensure that arg is a string. - Address argAddr(BaselineStackReg, ICStackValueOffset); + Address argAddr(masm.getStackPointer(), ICStackValueOffset); ValueOperand argVal = regs.takeAnyValue(); masm.loadValue(argAddr, argVal); @@ -10618,7 +10599,7 @@ ICCall_StringSplit::Compiler::generateStubCode(MacroAssembler& masm) // Guard this-value. { // Ensure that thisv is a string. - Address thisvAddr(BaselineStackReg, ICStackValueOffset + sizeof(Value)); + Address thisvAddr(masm.getStackPointer(), ICStackValueOffset + sizeof(Value)); ValueOperand thisvVal = regs.takeAnyValue(); masm.loadValue(thisvAddr, thisvVal); @@ -10667,7 +10648,7 @@ ICCall_IsSuspendedStarGenerator::Compiler::generateStubCode(MacroAssembler& masm AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); // Load the argument. - Address argAddr(BaselineStackReg, ICStackValueOffset); + Address argAddr(masm.getStackPointer(), ICStackValueOffset); ValueOperand argVal = regs.takeAnyValue(); masm.loadValue(argAddr, argVal); @@ -10716,10 +10697,10 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm) // Load the callee in R1. if (isSpread_) { - masm.loadValue(Address(BaselineStackReg, ICStackValueOffset + 2 * sizeof(Value)), R1); + masm.loadValue(Address(masm.getStackPointer(), ICStackValueOffset + 2 * sizeof(Value)), R1); } else { unsigned nonArgsSlots = (1 + isConstructing_) * sizeof(Value); - BaseValueIndex calleeSlot(BaselineStackReg, argcReg, ICStackValueOffset + nonArgsSlots); + BaseValueIndex calleeSlot(masm.getStackPointer(), argcReg, ICStackValueOffset + nonArgsSlots); masm.loadValue(calleeSlot, R1); } regs.take(R1); @@ -10749,10 +10730,9 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm) if (isConstructing_) { // Stack looks like: [ ..., Arg0Val, ThisVal, CalleeVal ] // Replace ThisVal with MagicValue(JS_IS_CONSTRUCTING) - masm.storeValue(MagicValue(JS_IS_CONSTRUCTING), Address(BaselineStackReg, sizeof(Value))); + masm.storeValue(MagicValue(JS_IS_CONSTRUCTING), Address(masm.getStackPointer(), sizeof(Value))); } - masm.checkStackAlignment(); // Native functions have the signature: // @@ -10781,7 +10761,7 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm) masm.passABIArg(argcReg); masm.passABIArg(vpReg); -#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) +#if defined(JS_ARM_SIMULATOR) || defined(JS_ARM64_SIMULATOR) || defined(JS_MIPS_SIMULATOR) // The simulator requires VM calls to be redirected to a special swi // instruction to handle them, so we store the redirected pointer in the // stub and use that instead of the original one. @@ -10820,7 +10800,7 @@ ICCall_ClassHook::Compiler::generateStubCode(MacroAssembler& masm) // Load the callee in R1. unsigned nonArgSlots = (1 + isConstructing_) * sizeof(Value); - BaseValueIndex calleeSlot(BaselineStackReg, argcReg, ICStackValueOffset + nonArgSlots); + BaseValueIndex calleeSlot(masm.getStackPointer(), argcReg, ICStackValueOffset + nonArgSlots); masm.loadValue(calleeSlot, R1); regs.take(R1); @@ -10848,7 +10828,7 @@ ICCall_ClassHook::Compiler::generateStubCode(MacroAssembler& masm) if (isConstructing_) { // Stack looks like: [ ..., Arg0Val, ThisVal, CalleeVal ] // Replace ThisVal with MagicValue(JS_IS_CONSTRUCTING) - masm.storeValue(MagicValue(JS_IS_CONSTRUCTING), Address(BaselineStackReg, sizeof(Value))); + masm.storeValue(MagicValue(JS_IS_CONSTRUCTING), Address(masm.getStackPointer(), sizeof(Value))); } masm.checkStackAlignment(); @@ -10983,7 +10963,7 @@ ICCall_ScriptedApplyArray::Compiler::generateStubCode(MacroAssembler& masm) masm.movePtr(ImmGCPtr(argumentsRectifier), target); masm.loadPtr(Address(target, JitCode::offsetOfCode()), target); - masm.mov(argcReg, ArgumentsRectifierReg); + masm.movePtr(argcReg, ArgumentsRectifierReg); } masm.bind(&noUnderflow); regs.add(argcReg); @@ -11081,7 +11061,7 @@ ICCall_ScriptedApplyArguments::Compiler::generateStubCode(MacroAssembler& masm) masm.movePtr(ImmGCPtr(argumentsRectifier), target); masm.loadPtr(Address(target, JitCode::offsetOfCode()), target); - masm.mov(argcReg, ArgumentsRectifierReg); + masm.movePtr(argcReg, ArgumentsRectifierReg); } masm.bind(&noUnderflow); regs.add(argcReg); @@ -11116,7 +11096,7 @@ ICCall_ScriptedFunCall::Compiler::generateStubCode(MacroAssembler& masm) // Load the callee in R1. // Stack Layout: [ ..., CalleeVal, ThisVal, Arg0Val, ..., ArgNVal, +ICStackValueOffset+ ] - BaseValueIndex calleeSlot(BaselineStackReg, argcReg, ICStackValueOffset + sizeof(Value)); + BaseValueIndex calleeSlot(masm.getStackPointer(), argcReg, ICStackValueOffset + sizeof(Value)); masm.loadValue(calleeSlot, R1); regs.take(R1); @@ -11130,7 +11110,7 @@ ICCall_ScriptedFunCall::Compiler::generateStubCode(MacroAssembler& masm) masm.branchPtr(Assembler::NotEqual, callee, ImmPtr(fun_call), &failure); // Ensure |this| is a scripted function with JIT code. - BaseIndex thisSlot(BaselineStackReg, argcReg, TimesEight, ICStackValueOffset); + BaseIndex thisSlot(masm.getStackPointer(), argcReg, TimesEight, ICStackValueOffset); masm.loadValue(thisSlot, R1); masm.branchTestObject(Assembler::NotEqual, R1, &failure); @@ -11162,6 +11142,7 @@ ICCall_ScriptedFunCall::Compiler::generateStubCode(MacroAssembler& masm) // Values are on the stack left-to-right. Calling convention wants them // right-to-left so duplicate them on the stack in reverse order. + pushCallArguments(masm, regs, argcReg, /* isJitCall = */ true); // Pop scripted callee (the original |this|). @@ -11209,7 +11190,7 @@ ICCall_ScriptedFunCall::Compiler::generateStubCode(MacroAssembler& masm) masm.movePtr(ImmGCPtr(argumentsRectifier), code); masm.loadPtr(Address(code, JitCode::offsetOfCode()), code); - masm.mov(argcReg, ArgumentsRectifierReg); + masm.movePtr(argcReg, ArgumentsRectifierReg); } masm.bind(&noUnderflow); @@ -11281,7 +11262,7 @@ ICTableSwitch::Compiler::generateStubCode(MacroAssembler& masm) // If the function returns |true|, the value has been converted to // int32. - masm.mov(ReturnReg, scratch); + masm.movePtr(ReturnReg, scratch); masm.popValue(R0); masm.branchIfFalseBool(scratch, &outOfRange); masm.unboxInt32(R0, key); @@ -12411,7 +12392,7 @@ ICCall_Native::ICCall_Native(JitCode* stubCode, ICStub* firstMonitorStub, templateObject_(templateObject), pcOffset_(pcOffset) { -#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) +#if defined(JS_ARM_SIMULATOR) || defined(JS_ARM64_SIMULATOR) || defined(JS_MIPS_SIMULATOR) // The simulator requires VM calls to be redirected to a special swi // instruction to handle them. To make this work, we store the redirected // pointer in the stub. @@ -12437,7 +12418,7 @@ ICCall_ClassHook::ICCall_ClassHook(JitCode* stubCode, ICStub* firstMonitorStub, templateObject_(templateObject), pcOffset_(pcOffset) { -#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) +#if defined(JS_ARM_SIMULATOR) || defined(JS_ARM64_SIMULATOR) || defined(JS_MIPS_SIMULATOR) // The simulator requires VM calls to be redirected to a special swi // instruction to handle them. To make this work, we store the redirected // pointer in the stub. @@ -12575,10 +12556,10 @@ static bool DoRestFallback(JSContext* cx, BaselineFrame* frame, ICRest_Fallback* unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0; Value* rest = frame->argv() + numFormals; - ArrayObject* obj = NewDenseCopiedArray(cx, numRest, rest, nullptr); + JSObject* obj = ObjectGroup::newArrayObject(cx, rest, numRest, GenericObject, + ObjectGroup::NewArrayKind::UnknownIndex); if (!obj) return false; - ObjectGroup::fixRestArgumentsGroup(cx, obj); res.setObject(*obj); return true; } diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index 3526bea386..bcd0974508 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -609,6 +609,11 @@ class ICNewArray_Fallback : public ICFallbackStub HeapPtrObjectGroup& templateGroup() { return templateGroup_; } + + void setTemplateGroup(ObjectGroup* group) { + templateObject_ = nullptr; + templateGroup_ = group; + } }; class ICNewObject_Fallback : public ICFallbackStub @@ -1940,7 +1945,7 @@ class ICGetElem_UnboxedArray : public ICMonitoredStub : ICStubCompiler(cx, ICStub::GetElem_UnboxedArray, Engine::Baseline), firstMonitorStub_(firstMonitorStub), group_(cx, group), - elementType_(group->unboxedLayout().elementType()) + elementType_(group->unboxedLayoutDontCheckGeneration().elementType()) {} ICStub* getStub(ICStubSpace* space) { @@ -2152,7 +2157,9 @@ class ICSetElem_DenseOrUnboxedArray : public ICUpdatedStub : ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArray, Engine::Baseline), shape_(cx, shape), group_(cx, group), - unboxedType_(shape ? JSVAL_TYPE_MAGIC : group->unboxedLayout().elementType()) + unboxedType_(shape + ? JSVAL_TYPE_MAGIC + : group->unboxedLayoutDontCheckGeneration().elementType()) {} ICUpdatedStub* getStub(ICStubSpace* space) { @@ -4610,8 +4617,8 @@ class ICCall_Native : public ICMonitoredStub HeapPtrObject templateObject_; uint32_t pcOffset_; -#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) - void* native_; +#if defined(JS_ARM_SIMULATOR) || defined(JS_ARM64_SIMULATOR) || defined(JS_MIPS_SIMULATOR) + void *native_; #endif ICCall_Native(JitCode* stubCode, ICStub* firstMonitorStub, @@ -4636,7 +4643,7 @@ class ICCall_Native : public ICMonitoredStub return offsetof(ICCall_Native, pcOffset_); } -#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) +#if defined(JS_ARM_SIMULATOR) || defined(JS_ARM64_SIMULATOR) || defined(JS_MIPS_SIMULATOR) static size_t offsetOfNative() { return offsetof(ICCall_Native, native_); } @@ -4912,10 +4919,10 @@ class ICCall_StringSplit : public ICMonitoredStub uint32_t pcOffset_; HeapPtrString expectedThis_; HeapPtrString expectedArg_; - HeapPtrArrayObject templateObject_; + HeapPtrObject templateObject_; ICCall_StringSplit(JitCode* stubCode, ICStub* firstMonitorStub, uint32_t pcOffset, JSString* thisString, - JSString* argString, ArrayObject* templateObject) + JSString* argString, JSObject* templateObject) : ICMonitoredStub(ICStub::Call_StringSplit, stubCode, firstMonitorStub), pcOffset_(pcOffset), expectedThis_(thisString), expectedArg_(argString), templateObject_(templateObject) @@ -4942,7 +4949,7 @@ class ICCall_StringSplit : public ICMonitoredStub return expectedArg_; } - HeapPtrArrayObject& templateObject() { + HeapPtrObject& templateObject() { return templateObject_; } @@ -4952,7 +4959,7 @@ class ICCall_StringSplit : public ICMonitoredStub uint32_t pcOffset_; RootedString expectedThis_; RootedString expectedArg_; - RootedArrayObject templateObject_; + RootedObject templateObject_; bool generateStubCode(MacroAssembler& masm); @@ -4969,7 +4976,7 @@ class ICCall_StringSplit : public ICMonitoredStub pcOffset_(pcOffset), expectedThis_(cx, thisString), expectedArg_(cx, argString), - templateObject_(cx, &templateObject.toObject().as()) + templateObject_(cx, &templateObject.toObject()) { } ICStub* getStub(ICStubSpace* space) { diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index d1281dd736..90edefe1e0 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -10,6 +10,8 @@ #include "jit/BaselineIC.h" +#include "vm/ObjectGroup-inl.h" + using namespace js; using namespace js::jit; @@ -519,7 +521,7 @@ BaselineInspector::getTemplateObjectForNative(jsbytecode* pc, Native native) bool BaselineInspector::isOptimizableCallStringSplit(jsbytecode* pc, JSString** stringOut, JSString** stringArg, - NativeObject** objOut) + JSObject** objOut) { if (!hasBaselineScript()) return false; @@ -731,6 +733,7 @@ BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc) return MIRType_Value; case ICStub::GetProp_ArrayLength: + case ICStub::GetProp_UnboxedArrayLength: case ICStub::GetProp_Native: case ICStub::GetProp_NativeDoesNotExist: case ICStub::GetProp_NativePrototype: @@ -748,6 +751,7 @@ BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc) case ICStub::GetElem_String: case ICStub::GetElem_Dense: case ICStub::GetElem_TypedArray: + case ICStub::GetElem_UnboxedArray: stubType = MIRType_Object; break; diff --git a/js/src/jit/BaselineInspector.h b/js/src/jit/BaselineInspector.h index 10d61d849e..dacdfe0e37 100644 --- a/js/src/jit/BaselineInspector.h +++ b/js/src/jit/BaselineInspector.h @@ -113,7 +113,7 @@ class BaselineInspector bool hasSeenNonStringIterMore(jsbytecode* pc); bool isOptimizableCallStringSplit(jsbytecode* pc, JSString** stringOut, JSString** stringArg, - NativeObject** objOut); + JSObject** objOut); JSObject* getTemplateObject(jsbytecode* pc); JSObject* getTemplateObjectForNative(jsbytecode* pc, Native native); JSObject* getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 156ef84efa..7e25664420 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -189,7 +189,7 @@ CodeGenerator::visitValueToInt32(LValueToInt32* lir) Register stringReg; if (input->mightBeType(MIRType_String)) { stringReg = ToRegister(lir->temp()); - OutOfLineCode* oolString = oolCallVM(StringToNumberInfo, lir, (ArgList(), stringReg), + OutOfLineCode* oolString = oolCallVM(StringToNumberInfo, lir, ArgList(stringReg), StoreFloatRegisterTo(temp)); stringEntry = oolString->entry(); stringRejoin = oolString->rejoin(); @@ -850,7 +850,7 @@ CodeGenerator::visitIntToString(LIntToString* lir) Register input = ToRegister(lir->input()); Register output = ToRegister(lir->output()); - OutOfLineCode* ool = oolCallVM(IntToStringInfo, lir, (ArgList(), input), + OutOfLineCode* ool = oolCallVM(IntToStringInfo, lir, ArgList(input), StoreRegisterTo(output)); emitIntToString(input, output, ool->entry()); @@ -868,7 +868,7 @@ CodeGenerator::visitDoubleToString(LDoubleToString* lir) Register temp = ToRegister(lir->tempInt()); Register output = ToRegister(lir->output()); - OutOfLineCode* ool = oolCallVM(DoubleToStringInfo, lir, (ArgList(), input), + OutOfLineCode* ool = oolCallVM(DoubleToStringInfo, lir, ArgList(input), StoreRegisterTo(output)); // Try double to integer conversion and run integer to string code. @@ -887,7 +887,7 @@ CodeGenerator::visitValueToString(LValueToString* lir) ValueOperand input = ToValue(lir, LValueToString::Input); Register output = ToRegister(lir->output()); - OutOfLineCode* ool = oolCallVM(PrimitiveToStringInfo, lir, (ArgList(), input), + OutOfLineCode* ool = oolCallVM(PrimitiveToStringInfo, lir, ArgList(input), StoreRegisterTo(output)); Label done; @@ -982,7 +982,7 @@ CodeGenerator::visitValueToObjectOrNull(LValueToObjectOrNull* lir) ValueOperand input = ToValue(lir, LValueToObjectOrNull::Input); Register output = ToRegister(lir->output()); - OutOfLineCode* ool = oolCallVM(ToObjectInfo, lir, (ArgList(), input, Imm32(0)), + OutOfLineCode* ool = oolCallVM(ToObjectInfo, lir, ArgList(input, Imm32(0)), StoreRegisterTo(output)); Label done; @@ -1701,7 +1701,7 @@ CodeGenerator::visitLambda(LLambda* lir) Register tempReg = ToRegister(lir->temp()); const LambdaFunctionInfo& info = lir->mir()->info(); - OutOfLineCode* ool = oolCallVM(LambdaInfo, lir, (ArgList(), ImmGCPtr(info.fun), scopeChain), + OutOfLineCode* ool = oolCallVM(LambdaInfo, lir, ArgList(ImmGCPtr(info.fun), scopeChain), StoreRegisterTo(output)); MOZ_ASSERT(!info.singletonType); @@ -2208,8 +2208,14 @@ CodeGenerator::visitMoveGroup(LMoveGroup* group) masm.propagateOOM(resolver.resolve()); MoveEmitter emitter(masm); + +#ifdef JS_CODEGEN_X86 if (group->maybeScratchRegister().isGeneralReg()) emitter.setScratchRegister(group->maybeScratchRegister().toGeneralReg()->reg()); + else + resolver.sortMemoryToMemoryMoves(); +#endif + emitter.emit(resolver); emitter.finish(); } @@ -2509,7 +2515,7 @@ CodeGenerator::visitConvertElementsToDoubles(LConvertElementsToDoubles* lir) Register elements = ToRegister(lir->elements()); OutOfLineCode* ool = oolCallVM(ConvertElementsToDoublesInfo, lir, - (ArgList(), elements), StoreNothing()); + ArgList(elements), StoreNothing()); Address convertedAddress(elements, ObjectElements::offsetOfFlags()); Imm32 bit(ObjectElements::CONVERT_DOUBLE_ELEMENTS); @@ -2555,7 +2561,7 @@ CodeGenerator::visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite* lir) Register temp = ToRegister(lir->temp()); OutOfLineCode* ool = oolCallVM(CopyElementsForWriteInfo, lir, - (ArgList(), object), StoreNothing()); + ArgList(object), StoreNothing()); if (lir->mir()->checkNative()) { masm.loadObjClass(object, temp); @@ -4109,9 +4115,20 @@ typedef JSObject* (*NewArrayOperationFn)(JSContext*, HandleScript, jsbytecode*, static const VMFunction NewArrayOperationInfo = FunctionInfo(NewArrayOperation); -typedef ArrayObject* (*NewDenseArrayFn)(ExclusiveContext*, uint32_t, HandleObjectGroup, - AllocatingBehaviour, bool); -static const VMFunction NewDenseArrayInfo = FunctionInfo(NewDenseArray); +static JSObject* +NewArrayWithGroup(JSContext* cx, uint32_t length, HandleObjectGroup group, + bool convertDoubleElements) +{ + JSObject* res = NewFullyAllocatedArrayTryUseGroup(cx, group, length); + if (!res) + return nullptr; + if (convertDoubleElements) + res->as().setShouldConvertDoubleElements(); + return res; +} + +typedef JSObject* (*NewArrayWithGroupFn)(JSContext*, uint32_t, HandleObjectGroup, bool); +static const VMFunction NewArrayWithGroupInfo = FunctionInfo(NewArrayWithGroup); void CodeGenerator::visitNewArrayCallVM(LNewArray* lir) @@ -4123,13 +4140,12 @@ CodeGenerator::visitNewArrayCallVM(LNewArray* lir) JSObject* templateObject = lir->mir()->templateObject(); - if (templateObject && !templateObject->is()) { + if (templateObject) { pushArg(Imm32(lir->mir()->convertDoubleElements())); - pushArg(Imm32(lir->mir()->allocatingBehaviour())); pushArg(ImmGCPtr(templateObject->group())); pushArg(Imm32(lir->mir()->count())); - callVM(NewDenseArrayInfo, lir); + callVM(NewArrayWithGroupInfo, lir); } else { pushArg(Imm32(GenericObject)); pushArg(Imm32(lir->mir()->count())); @@ -4244,7 +4260,7 @@ CodeGenerator::visitNewArrayCopyOnWrite(LNewArrayCopyOnWrite* lir) // If we have a template object, we can inline call object creation. OutOfLineCode* ool = oolCallVM(NewArrayCopyOnWriteInfo, lir, - (ArgList(), ImmGCPtr(templateObject), Imm32(initialHeap)), + ArgList(ImmGCPtr(templateObject), Imm32(initialHeap)), StoreRegisterTo(objReg)); masm.createGCObject(objReg, tempReg, templateObject, initialHeap, ool->entry()); @@ -4252,7 +4268,7 @@ CodeGenerator::visitNewArrayCopyOnWrite(LNewArrayCopyOnWrite* lir) masm.bind(ool->rejoin()); } -typedef ArrayObject* (*ArrayConstructorOneArgFn)(JSContext*, HandleObjectGroup, int32_t length); +typedef JSObject* (*ArrayConstructorOneArgFn)(JSContext*, HandleObjectGroup, int32_t length); static const VMFunction ArrayConstructorOneArgInfo = FunctionInfo(ArrayConstructorOneArg); @@ -4263,32 +4279,47 @@ CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir) Register objReg = ToRegister(lir->output()); Register tempReg = ToRegister(lir->temp()); - ArrayObject* templateObject = lir->mir()->templateObject(); + JSObject* templateObject = lir->mir()->templateObject(); gc::InitialHeap initialHeap = lir->mir()->initialHeap(); OutOfLineCode* ool = oolCallVM(ArrayConstructorOneArgInfo, lir, - (ArgList(), ImmGCPtr(templateObject->group()), lengthReg), + ArgList(ImmGCPtr(templateObject->group()), lengthReg), StoreRegisterTo(objReg)); - size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()); - size_t inlineLength = numSlots >= ObjectElements::VALUES_PER_HEADER - ? numSlots - ObjectElements::VALUES_PER_HEADER - : 0; + bool canInline = true; + size_t inlineLength = 0; + if (templateObject->is()) { + if (templateObject->as().hasFixedElements()) { + size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()); + inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER; + } else { + canInline = false; + } + } else { + if (templateObject->as().hasInlineElements()) { + size_t nbytes = + templateObject->tenuredSizeOfThis() - UnboxedArrayObject::offsetOfInlineElements(); + inlineLength = nbytes / templateObject->as().elementSize(); + } else { + canInline = false; + } + } - // Try to do the allocation inline if the template object is big enough - // for the length in lengthReg. If the length is bigger we could still - // use the template object and not allocate the elements, but it's more - // efficient to do a single big allocation than (repeatedly) reallocating - // the array later on when filling it. - if (!templateObject->isSingleton() && templateObject->length() <= inlineLength) - masm.branch32(Assembler::Above, lengthReg, Imm32(templateObject->length()), ool->entry()); - else + if (canInline) { + // Try to do the allocation inline if the template object is big enough + // for the length in lengthReg. If the length is bigger we could still + // use the template object and not allocate the elements, but it's more + // efficient to do a single big allocation than (repeatedly) reallocating + // the array later on when filling it. + masm.branch32(Assembler::Above, lengthReg, Imm32(inlineLength), ool->entry()); + + masm.createGCObject(objReg, tempReg, templateObject, initialHeap, ool->entry()); + + size_t lengthOffset = NativeObject::offsetOfFixedElements() + ObjectElements::offsetOfLength(); + masm.store32(lengthReg, Address(objReg, lengthOffset)); + } else { masm.jump(ool->entry()); - - masm.createGCObject(objReg, tempReg, templateObject, initialHeap, ool->entry()); - - size_t lengthOffset = NativeObject::offsetOfFixedElements() + ObjectElements::offsetOfLength(); - masm.store32(lengthReg, Address(objReg, lengthOffset)); + } masm.bind(ool->rejoin()); } @@ -4489,7 +4520,7 @@ CodeGenerator::visitNewTypedObject(LNewTypedObject* lir) gc::InitialHeap initialHeap = lir->mir()->initialHeap(); OutOfLineCode* ool = oolCallVM(NewTypedObjectInfo, lir, - (ArgList(), ImmGCPtr(templateObject), Imm32(initialHeap)), + ArgList(ImmGCPtr(templateObject), Imm32(initialHeap)), StoreRegisterTo(object)); masm.createGCObject(object, temp, templateObject, initialHeap, ool->entry()); @@ -4511,7 +4542,7 @@ CodeGenerator::visitSimdBox(LSimdBox* lir) MOZ_ASSERT(lir->safepoint()->liveRegs().has(in), "Save the input register across the oolCallVM"); OutOfLineCode* ool = oolCallVM(NewTypedObjectInfo, lir, - (ArgList(), ImmGCPtr(templateObject), Imm32(initialHeap)), + ArgList(ImmGCPtr(templateObject), Imm32(initialHeap)), StoreRegisterTo(object)); masm.createGCObject(object, temp, templateObject, initialHeap, ool->entry()); @@ -4635,8 +4666,7 @@ CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject* lir) // If we have a template object, we can inline call object creation. OutOfLineCode* ool = oolCallVM(NewDeclEnvObjectInfo, lir, - (ArgList(), ImmGCPtr(info.funMaybeLazy()), - Imm32(gc::DefaultHeap)), + ArgList(ImmGCPtr(info.funMaybeLazy()), Imm32(gc::DefaultHeap)), StoreRegisterTo(objReg)); bool initContents = ShouldInitFixedSlots(lir, templateObj); @@ -4661,9 +4691,9 @@ CodeGenerator::visitNewCallObject(LNewCallObject* lir) JSScript* script = lir->mir()->block()->info().script(); uint32_t lexicalBegin = script->bindings.aliasedBodyLevelLexicalBegin(); OutOfLineCode* ool = oolCallVM(NewCallObjectInfo, lir, - (ArgList(), ImmGCPtr(templateObj->lastProperty()), - ImmGCPtr(templateObj->group()), - Imm32(lexicalBegin)), + ArgList(ImmGCPtr(templateObj->lastProperty()), + ImmGCPtr(templateObj->group()), + Imm32(lexicalBegin)), StoreRegisterTo(objReg)); // Inline call object creation, using the OOL path only for tricky cases. @@ -4689,8 +4719,8 @@ CodeGenerator::visitNewSingletonCallObject(LNewSingletonCallObject* lir) uint32_t lexicalBegin = script->bindings.aliasedBodyLevelLexicalBegin(); OutOfLineCode* ool; ool = oolCallVM(NewSingletonCallObjectInfo, lir, - (ArgList(), ImmGCPtr(templateObj->as().lastProperty()), - Imm32(lexicalBegin)), + ArgList(ImmGCPtr(templateObj->as().lastProperty()), + Imm32(lexicalBegin)), StoreRegisterTo(objReg)); // Objects can only be given singleton types in VM calls. We make the call @@ -4712,7 +4742,7 @@ CodeGenerator::visitNewStringObject(LNewStringObject* lir) StringObject* templateObj = lir->mir()->templateObj(); - OutOfLineCode* ool = oolCallVM(NewStringObjectInfo, lir, (ArgList(), input), + OutOfLineCode* ool = oolCallVM(NewStringObjectInfo, lir, ArgList(input), StoreRegisterTo(output)); masm.createGCObject(output, temp, templateObj, gc::DefaultHeap, ool->entry()); @@ -4864,7 +4894,7 @@ CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate* lir) Register tempReg = ToRegister(lir->temp()); OutOfLineCode* ool = oolCallVM(NewInitObjectWithTemplateInfo, lir, - (ArgList(), ImmGCPtr(templateObject)), + ArgList(ImmGCPtr(templateObject)), StoreRegisterTo(objReg)); // Allocate. If the FreeList is empty, call to VM, which may GC. @@ -4967,7 +4997,7 @@ CodeGenerator::visitComputeThis(LComputeThis* lir) ValueOperand value = ToValue(lir, LComputeThis::ValueIndex); Register output = ToRegister(lir->output()); - OutOfLineCode* ool = oolCallVM(BoxNonStrictThisInfo, lir, (ArgList(), value), + OutOfLineCode* ool = oolCallVM(BoxNonStrictThisInfo, lir, ArgList(value), StoreRegisterTo(output)); masm.branchTestObject(Assembler::NotEqual, value, ool->entry()); @@ -5193,21 +5223,6 @@ CodeGenerator::visitPowD(LPowD* ins) MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg); } -void -CodeGenerator::visitRandom(LRandom* ins) -{ - Register temp = ToRegister(ins->temp()); - Register temp2 = ToRegister(ins->temp2()); - - masm.loadJSContext(temp); - - masm.setupUnalignedABICall(1, temp2); - masm.passABIArg(temp); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE); - - MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg); -} - void CodeGenerator::visitMathFunctionD(LMathFunctionD* ins) { @@ -5410,10 +5425,10 @@ CodeGenerator::emitCompareS(LInstruction* lir, JSOp op, Register left, Register OutOfLineCode* ool = nullptr; if (op == JSOP_EQ || op == JSOP_STRICTEQ) { - ool = oolCallVM(StringsEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output)); + ool = oolCallVM(StringsEqualInfo, lir, ArgList(left, right), StoreRegisterTo(output)); } else { MOZ_ASSERT(op == JSOP_NE || op == JSOP_STRICTNE); - ool = oolCallVM(StringsNotEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output)); + ool = oolCallVM(StringsNotEqualInfo, lir, ArgList(left, right), StoreRegisterTo(output)); } masm.compareStrings(op, left, right, output, ool->entry()); @@ -5764,7 +5779,7 @@ static const VMFunction ConcatStringsInfo = FunctionInfo(Concat void CodeGenerator::emitConcat(LInstruction* lir, Register lhs, Register rhs, Register output) { - OutOfLineCode* ool = oolCallVM(ConcatStringsInfo, lir, (ArgList(), lhs, rhs), + OutOfLineCode* ool = oolCallVM(ConcatStringsInfo, lir, ArgList(lhs, rhs), StoreRegisterTo(output)); JitCode* stringConcatStub = gen->compartment->jitCompartment()->stringConcatStubNoBarrier(); @@ -5954,8 +5969,8 @@ CodeGenerator::visitSubstr(LSubstr* lir) // can be handled by allocate in ool code and returning to jit code to fill // in all data. OutOfLineCode* ool = oolCallVM(SubstringKernelInfo, lir, - (ArgList(), string, begin, length), - StoreRegisterTo(output)); + ArgList(string, begin, length), + StoreRegisterTo(output)); Label* slowPath = ool->entry(); Label* done = ool->rejoin(); @@ -6283,7 +6298,7 @@ CodeGenerator::visitCharCodeAt(LCharCodeAt* lir) Register index = ToRegister(lir->index()); Register output = ToRegister(lir->output()); - OutOfLineCode* ool = oolCallVM(CharCodeAtInfo, lir, (ArgList(), str, index), StoreRegisterTo(output)); + OutOfLineCode* ool = oolCallVM(CharCodeAtInfo, lir, ArgList(str, index), StoreRegisterTo(output)); masm.branchIfRope(str, ool->entry()); masm.loadStringChar(str, index, output); @@ -6300,7 +6315,7 @@ CodeGenerator::visitFromCharCode(LFromCharCode* lir) Register code = ToRegister(lir->code()); Register output = ToRegister(lir->output()); - OutOfLineCode* ool = oolCallVM(StringFromCharCodeInfo, lir, (ArgList(), code), StoreRegisterTo(output)); + OutOfLineCode* ool = oolCallVM(StringFromCharCodeInfo, lir, ArgList(code), StoreRegisterTo(output)); // OOL path if code >= UNIT_STATIC_LIMIT. masm.branch32(Assembler::AboveOrEqual, code, Imm32(StaticStrings::UNIT_STATIC_LIMIT), @@ -6368,6 +6383,25 @@ CodeGenerator::visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArra masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); } +void +CodeGenerator::visitSetUnboxedArrayInitializedLength(LSetUnboxedArrayInitializedLength* lir) +{ + Register obj = ToRegister(lir->object()); + Int32Key key = ToInt32Key(lir->length()); + Register temp = ToRegister(lir->temp()); + + Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); + masm.load32(initLengthAddr, temp); + masm.and32(Imm32(UnboxedArrayObject::CapacityMask), temp); + + if (key.isRegister()) + masm.or32(key.reg(), temp); + else + masm.or32(Imm32(key.constant()), temp); + + masm.store32(temp, initLengthAddr); +} + void CodeGenerator::visitNotO(LNotO* lir) { @@ -6922,7 +6956,7 @@ CodeGenerator::visitConvertUnboxedObjectToNative(LConvertUnboxedObjectToNative* OutOfLineCode* ool = oolCallVM(lir->mir()->group()->unboxedLayoutDontCheckGeneration().isArray() ? ConvertUnboxedArrayObjectToNativeInfo : ConvertUnboxedPlainObjectToNativeInfo, - lir, (ArgList(), object), StoreNothing()); + lir, ArgList(object), StoreNothing()); masm.branchPtr(Assembler::Equal, Address(object, JSObject::offsetOfGroup()), ImmGCPtr(lir->mir()->group()), ool->entry()); @@ -6940,10 +6974,10 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R OutOfLineCode* ool; if (mir->mode() == MArrayPopShift::Pop) { - ool = oolCallVM(ArrayPopDenseInfo, lir, (ArgList(), obj), StoreValueTo(out)); + ool = oolCallVM(ArrayPopDenseInfo, lir, ArgList(obj), StoreValueTo(out)); } else { MOZ_ASSERT(mir->mode() == MArrayPopShift::Shift); - ool = oolCallVM(ArrayShiftDenseInfo, lir, (ArgList(), obj), StoreValueTo(out)); + ool = oolCallVM(ArrayShiftDenseInfo, lir, ArgList(obj), StoreValueTo(out)); } // VM call if a write barrier is necessary. @@ -7064,7 +7098,7 @@ void CodeGenerator::emitArrayPush(LInstruction* lir, const MArrayPush* mir, Register obj, ConstantOrRegister value, Register elementsTemp, Register length) { - OutOfLineCode* ool = oolCallVM(ArrayPushDenseInfo, lir, (ArgList(), obj, value), StoreRegisterTo(length)); + OutOfLineCode* ool = oolCallVM(ArrayPushDenseInfo, lir, ArgList(obj, value), StoreRegisterTo(length)); Int32Key key = Int32Key(length); if (mir->unboxedType() == JSVAL_TYPE_MAGIC) { @@ -7254,7 +7288,7 @@ CodeGenerator::visitIteratorStart(LIteratorStart* lir) uint32_t flags = lir->mir()->flags(); OutOfLineCode* ool = oolCallVM(GetIteratorObjectInfo, lir, - (ArgList(), obj, Imm32(flags)), StoreRegisterTo(output)); + ArgList(obj, Imm32(flags)), StoreRegisterTo(output)); const Register temp1 = ToRegister(lir->temp1()); const Register temp2 = ToRegister(lir->temp2()); @@ -7383,7 +7417,7 @@ CodeGenerator::visitIteratorMore(LIteratorMore* lir) const ValueOperand output = ToOutValue(lir); const Register temp = ToRegister(lir->temp()); - OutOfLineCode* ool = oolCallVM(IteratorMoreInfo, lir, (ArgList(), obj), StoreValueTo(output)); + OutOfLineCode* ool = oolCallVM(IteratorMoreInfo, lir, ArgList(obj), StoreValueTo(output)); Register outputScratch = output.scratchReg(); LoadNativeIterator(masm, obj, outputScratch, ool->entry()); @@ -7438,7 +7472,7 @@ CodeGenerator::visitIteratorEnd(LIteratorEnd* lir) const Register temp2 = ToRegister(lir->temp2()); const Register temp3 = ToRegister(lir->temp3()); - OutOfLineCode* ool = oolCallVM(CloseIteratorInfo, lir, (ArgList(), obj), StoreNothing()); + OutOfLineCode* ool = oolCallVM(CloseIteratorInfo, lir, ArgList(obj), StoreNothing()); LoadNativeIterator(masm, obj, temp1, ool->entry()); @@ -8797,11 +8831,10 @@ CodeGenerator::visitToIdV(LToIdV* lir) ValueOperand index = ToValue(lir, LToIdV::Index); OutOfLineCode* ool = oolCallVM(ToIdInfo, lir, - (ArgList(), - ImmGCPtr(current->mir()->info().script()), - ImmPtr(lir->mir()->resumePoint()->pc()), - ToValue(lir, LToIdV::Object), - ToValue(lir, LToIdV::Index)), + ArgList(ImmGCPtr(current->mir()->info().script()), + ImmPtr(lir->mir()->resumePoint()->pc()), + ToValue(lir, LToIdV::Object), + ToValue(lir, LToIdV::Index)), StoreValueTo(out)); Register tag = masm.splitTagForTest(index); @@ -9274,7 +9307,7 @@ CodeGenerator::visitClampVToUint8(LClampVToUint8* lir) Label* stringEntry; Label* stringRejoin; if (input->mightBeType(MIRType_String)) { - OutOfLineCode* oolString = oolCallVM(StringToNumberInfo, lir, (ArgList(), output), + OutOfLineCode* oolString = oolCallVM(StringToNumberInfo, lir, ArgList(output), StoreFloatRegisterTo(tempFloat)); stringEntry = oolString->entry(); stringRejoin = oolString->rejoin(); @@ -9326,13 +9359,13 @@ CodeGenerator::visitInArray(LInArray* lir) MOZ_ASSERT_IF(index < 0, mir->needsNegativeIntCheck()); if (mir->needsNegativeIntCheck()) { ool = oolCallVM(OperatorInIInfo, lir, - (ArgList(), Imm32(index), ToRegister(lir->object())), + ArgList(Imm32(index), ToRegister(lir->object())), StoreRegisterTo(output)); failedInitLength = ool->entry(); } masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index), failedInitLength); - if (mir->needsHoleCheck()) { + if (mir->needsHoleCheck() && mir->unboxedType() == JSVAL_TYPE_MAGIC) { NativeObject::elementsSizeMustNotOverflow(); Address address = Address(elements, index * sizeof(Value)); masm.branchTestMagic(Assembler::Equal, address, &falseBranch); @@ -9345,7 +9378,7 @@ CodeGenerator::visitInArray(LInArray* lir) failedInitLength = &negativeIntCheck; masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength); - if (mir->needsHoleCheck()) { + if (mir->needsHoleCheck() && mir->unboxedType() == JSVAL_TYPE_MAGIC) { BaseIndex address = BaseIndex(elements, ToRegister(lir->index()), TimesEight); masm.branchTestMagic(Assembler::Equal, address, &falseBranch); } @@ -9354,7 +9387,7 @@ CodeGenerator::visitInArray(LInArray* lir) if (mir->needsNegativeIntCheck()) { masm.bind(&negativeIntCheck); ool = oolCallVM(OperatorInIInfo, lir, - (ArgList(), index, ToRegister(lir->object())), + ArgList(index, ToRegister(lir->object())), StoreRegisterTo(output)); masm.branch32(Assembler::LessThan, index, Imm32(0), ool->entry()); @@ -9456,7 +9489,7 @@ CodeGenerator::emitInstanceOf(LInstruction* ins, JSObject* prototypeObject) // register is already correct. OutOfLineCode* ool = oolCallVM(IsDelegateObjectInfo, ins, - (ArgList(), ImmGCPtr(prototypeObject), objReg), + ArgList(ImmGCPtr(prototypeObject), objReg), StoreRegisterTo(output)); // Regenerate the original lhs object for the VM call. @@ -9988,7 +10021,7 @@ CodeGenerator::visitAssertRangeV(LAssertRangeV* ins) void CodeGenerator::visitInterruptCheck(LInterruptCheck* lir) { - OutOfLineCode* ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing()); + OutOfLineCode* ool = oolCallVM(InterruptCheckInfo, lir, ArgList(), StoreNothing()); AbsoluteAddress interruptAddr(GetJitContext()->runtime->addressOfInterruptUint32()); masm.branch32(Assembler::NotEqual, interruptAddr, Imm32(0), ool->entry()); @@ -10024,9 +10057,9 @@ CodeGenerator::visitRecompileCheck(LRecompileCheck* ins) Register tmp = ToRegister(ins->scratch()); OutOfLineCode* ool; if (ins->mir()->forceRecompilation()) - ool = oolCallVM(ForcedRecompileFnInfo, ins, (ArgList()), StoreRegisterTo(tmp)); + ool = oolCallVM(ForcedRecompileFnInfo, ins, ArgList(), StoreRegisterTo(tmp)); else - ool = oolCallVM(RecompileFnInfo, ins, (ArgList()), StoreRegisterTo(tmp)); + ool = oolCallVM(RecompileFnInfo, ins, ArgList(), StoreRegisterTo(tmp)); // Check if warm-up counter is high enough. AbsoluteAddress warmUpCount = AbsoluteAddress(ins->mir()->script()->addressOfWarmUpCounter()); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 4eaf2fe6e1..3ff1718ca8 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -196,6 +196,7 @@ class CodeGenerator : public CodeGeneratorSpecific void visitUnboxedArrayLength(LUnboxedArrayLength* lir); void visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir); void visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir); + void visitSetUnboxedArrayInitializedLength(LSetUnboxedArrayInitializedLength* lir); void visitNotO(LNotO* ins); void visitNotV(LNotV* ins); void visitBoundsCheck(LBoundsCheck* lir); @@ -219,10 +220,9 @@ class CodeGenerator : public CodeGeneratorSpecific void visitHypot(LHypot* lir); void visitPowI(LPowI* lir); void visitPowD(LPowD* lir); - void visitRandom(LRandom* lir); - void visitMathFunctionD(LMathFunctionD *ins); - void visitMathFunctionF(LMathFunctionF *ins); - void visitModD(LModD *ins); + void visitMathFunctionD(LMathFunctionD* ins); + void visitMathFunctionF(LMathFunctionF* ins); + void visitModD(LModD* ins); void visitMinMaxI(LMinMaxI* lir); void visitBinaryV(LBinaryV* lir); void emitCompareS(LInstruction* lir, JSOp op, Register left, Register right, Register output); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index a807b8055f..1ce210a433 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -1350,16 +1350,14 @@ OptimizeMIR(MIRGenerator* mir) return false; } - if (mir->optimizationInfo().scalarReplacementEnabled()) { - AutoTraceLog log(logger, TraceLogger_ScalarReplacement); - if (!ScalarReplacement(mir, graph)) - return false; - gs.spewPass("Scalar Replacement"); - AssertGraphCoherency(graph); + ValueNumberer gvn(mir, graph); + if (!gvn.init()) + return false; - if (mir->shouldCancel("Scalar Replacement")) - return false; - } + size_t doRepeatOptimizations = 0; + repeatOptimizations: + doRepeatOptimizations++; + MOZ_ASSERT(doRepeatOptimizations <= 2); if (!mir->compilingAsmJS()) { AutoTraceLog log(logger, TraceLogger_ApplyTypes); @@ -1395,10 +1393,6 @@ OptimizeMIR(MIRGenerator* mir) return false; } - ValueNumberer gvn(mir, graph); - if (!gvn.init()) - return false; - // Alias analysis is required for LICM and GVN so that we don't move // loads across stores. if (mir->optimizationInfo().licmEnabled() || @@ -1454,6 +1448,26 @@ OptimizeMIR(MIRGenerator* mir) } } + if (mir->optimizationInfo().scalarReplacementEnabled() && doRepeatOptimizations <= 1) { + AutoTraceLog log(logger, TraceLogger_ScalarReplacement); + bool success = false; + if (!ScalarReplacement(mir, graph, &success)) + return false; + gs.spewPass("Scalar Replacement"); + AssertGraphCoherency(graph); + + if (mir->shouldCancel("Scalar Replacement")) + return false; + + // We got some success at removing objects allocation and removing the + // loads and stores, unfortunately, this phase is terrible at keeping + // the type consistency, so we re-run the Apply Type phase. As this + // optimization folds loads and stores, it might also introduce new + // opportunities for GVN and LICM, so re-run them as well. + if (success) + goto repeatOptimizations; + } + if (mir->optimizationInfo().rangeAnalysisEnabled()) { AutoTraceLog log(logger, TraceLogger_RangeAnalysis); RangeAnalysis r(mir, graph); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 8bbd08f362..d808f2d5a2 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -29,7 +29,9 @@ #include "jit/CompileInfo-inl.h" #include "vm/NativeObject-inl.h" +#include "vm/ObjectGroup-inl.h" #include "vm/ScopeObject-inl.h" +#include "vm/UnboxedObject-inl.h" using namespace js; using namespace js::jit; @@ -6199,9 +6201,6 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing) bool IonBuilder::testShouldDOMCall(TypeSet* inTypes, JSFunction* func, JSJitInfo::OpType opType) { - if (IsInsideNursery(func)) - return false; - if (!func->isNative() || !func->jitInfo()) return false; @@ -6521,8 +6520,7 @@ IonBuilder::jsop_newarray(uint32_t count) } current->add(templateConst); - MNewArray* ins = MNewArray::New(alloc(), constraints(), count, templateConst, - heap, NewArray_FullyAllocating, pc); + MNewArray* ins = MNewArray::New(alloc(), constraints(), count, templateConst, heap, pc); current->add(ins); current->push(ins); @@ -6640,7 +6638,15 @@ IonBuilder::jsop_initelem_array() return resumeAfter(store); } - MConstant* id = MConstant::New(alloc(), Int32Value(GET_UINT24(pc))); + return initializeArrayElement(obj, GET_UINT24(pc), value, unboxedType, /* addResumePoint = */ true); +} + +bool +IonBuilder::initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value, + JSValueType unboxedType, + bool addResumePointAndIncrementInitializedLength) +{ + MConstant* id = MConstant::New(alloc(), Int32Value(index)); current->add(id); // Get the elements vector. @@ -6651,11 +6657,13 @@ IonBuilder::jsop_initelem_array() // Note: storeUnboxedValue takes care of any post barriers on the value. storeUnboxedValue(obj, elements, 0, id, unboxedType, value, /* preBarrier = */ false); - MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj); - current->add(increment); + if (addResumePointAndIncrementInitializedLength) { + MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj); + current->add(increment); - if (!resumeAfter(increment)) - return false; + if (!resumeAfter(increment)) + return false; + } } else { if (NeedsPostBarrier(info(), value)) current->add(MPostWriteBarrier::New(alloc(), obj, value)); @@ -6671,14 +6679,16 @@ IonBuilder::jsop_initelem_array() /* needsHoleCheck = */ false); current->add(store); - // Update the initialized length. (The template object for this array has - // the array's ultimate length, so the length field is already correct: no - // updating needed.) - MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id); - current->add(initLength); + if (addResumePointAndIncrementInitializedLength) { + // Update the initialized length. (The template object for this + // array has the array's ultimate length, so the length field is + // already correct: no updating needed.) + MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id); + current->add(initLength); - if (!resumeAfter(initLength)) - return false; + if (!resumeAfter(initLength)) + return false; + } } return true; @@ -7477,6 +7487,8 @@ bool IonBuilder::getStaticName(JSObject* staticObject, PropertyName* name, bool* psucceeded, MDefinition* lexicalCheck) { + MOZ_ASSERT(*psucceeded == false); + jsid id = NameToId(name); MOZ_ASSERT(staticObject->is() || staticObject->is()); @@ -7558,8 +7570,13 @@ IonBuilder::getStaticName(JSObject* staticObject, PropertyName* name, bool* psuc if (barrier != BarrierKind::NoBarrier) rvalType = MIRType_Value; - return loadSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject), - rvalType, barrier, types); + if (!loadSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject), + rvalType, barrier, types)) { + *psucceeded = false; + return false; + } + + return true; } // Whether a write of the given value may need a post-write barrier for GC purposes. @@ -7623,19 +7640,21 @@ bool IonBuilder::jsop_getgname(PropertyName* name) { JSObject* obj = &script()->global(); - bool succeeded; - if (!getStaticName(obj, name, &succeeded)) - return false; - if (succeeded) - return true; + bool emitted = false; + if (!getStaticName(obj, name, &emitted) || emitted) + return emitted; - TemporaryTypeSet* types = bytecodeTypes(pc); - MDefinition* globalObj = constant(ObjectValue(*obj)); - if (!getPropTryCommonGetter(&succeeded, globalObj, name, types)) - return false; - if (succeeded) - return true; + if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches)) + goto do_InlineCache; + { + TemporaryTypeSet* types = bytecodeTypes(pc); + MDefinition* globalObj = constant(ObjectValue(*obj)); + if (!getPropTryCommonGetter(&emitted, globalObj, name, types) || emitted) + return emitted; + } + + do_InlineCache: return jsop_getname(name); } @@ -7759,6 +7778,8 @@ IonBuilder::jsop_getelem() obj = maybeUnboxForPropertyAccess(obj); bool emitted = false; + if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches)) + goto do_InlineCache; trackOptimizationAttempt(TrackedStrategy::GetElem_TypedObject); if (!getElemTryTypedObject(&emitted, obj, index) || emitted) @@ -7788,6 +7809,7 @@ IonBuilder::jsop_getelem() if (!getElemTryArgumentsInlined(&emitted, obj, index) || emitted) return emitted; + do_InlineCache: if (script()->argumentsHasVarBinding() && obj->mightBeType(MIRType_MagicOptimizedArguments)) return abort("Type is not definitely lazy arguments."); @@ -8532,12 +8554,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType // Note: to help GVN, use the original MElements instruction and not // MConvertElementsToDoubles as operand. This is fine because converting // elements to double does not change the initialized length. - MInstruction* initLength; - if (unboxedType != JSVAL_TYPE_MAGIC) - initLength = MUnboxedArrayInitializedLength::New(alloc(), obj); - else - initLength = MInitializedLength::New(alloc(), elements); - current->add(initLength); + MInstruction* initLength = initializedLength(obj, elements, unboxedType); // If we can load the element as a definite double, make sure to check that // the array has been converted to homogenous doubles first. @@ -8789,6 +8806,9 @@ IonBuilder::jsop_setelem() return resumeAfter(ins); } + if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches)) + goto do_InlineCache; + trackOptimizationAttempt(TrackedStrategy::SetElem_TypedObject); if (!setElemTryTypedObject(&emitted, object, index, value) || emitted) return emitted; @@ -8801,14 +8821,19 @@ IonBuilder::jsop_setelem() if (!setElemTryTypedArray(&emitted, object, index, value) || emitted) return emitted; - trackOptimizationAttempt(TrackedStrategy::SetElem_Dense); - if (!setElemTryDense(&emitted, object, index, value) || emitted) - return emitted; + { + trackOptimizationAttempt(TrackedStrategy::SetElem_Dense); + SetElemICInspector icInspect(inspector->setElemICInspector(pc)); + bool writeHole = icInspect.sawOOBDenseWrite(); + if (!setElemTryDense(&emitted, object, index, value, writeHole) || emitted) + return emitted; + } trackOptimizationAttempt(TrackedStrategy::SetElem_Arguments); if (!setElemTryArguments(&emitted, object, index, value) || emitted) return emitted; + do_InlineCache: if (script()->argumentsHasVarBinding() && object->mightBeType(MIRType_MagicOptimizedArguments) && info().analysisMode() != Analysis_ArgumentsUsage) @@ -8995,7 +9020,7 @@ IonBuilder::setElemTryTypedArray(bool* emitted, MDefinition* object, } // Emit typed setelem variant. - if (!jsop_setelem_typed(arrayType, SetElem_Normal, object, index, value)) + if (!jsop_setelem_typed(arrayType, object, index, value)) return false; trackOptimizationSuccess(); @@ -9005,7 +9030,7 @@ IonBuilder::setElemTryTypedArray(bool* emitted, MDefinition* object, bool IonBuilder::setElemTryDense(bool* emitted, MDefinition* object, - MDefinition* index, MDefinition* value) + MDefinition* index, MDefinition* value, bool writeHole) { MOZ_ASSERT(*emitted == false); @@ -9048,7 +9073,7 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object, } // Emit dense setelem variant. - if (!jsop_setelem_dense(conversion, SetElem_Normal, object, index, value, unboxedType)) + if (!jsop_setelem_dense(conversion, object, index, value, unboxedType, writeHole)) return false; trackOptimizationSuccess(); @@ -9134,9 +9159,8 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object, bool IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, - SetElemSafety safety, MDefinition* obj, MDefinition* id, MDefinition* value, - JSValueType unboxedType) + JSValueType unboxedType, bool writeHole) { MIRType elementType = MIRType_None; if (unboxedType == JSVAL_TYPE_MAGIC) @@ -9188,20 +9212,12 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, MOZ_CRASH("Unknown double conversion"); } - bool writeHole = false; - if (safety == SetElem_Normal) { - SetElemICInspector icInspect(inspector->setElemICInspector(pc)); - writeHole = icInspect.sawOOBDenseWrite(); - } - // Use MStoreElementHole if this SETELEM has written to out-of-bounds // indexes in the past. Otherwise, use MStoreElement so that we can hoist // the initialized length and bounds check. MInstruction* store; MStoreElementCommon *common = nullptr; if (writeHole && writeOutOfBounds) { - MOZ_ASSERT(safety == SetElem_Normal); - MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType); store = ins; common = ins; @@ -9209,20 +9225,10 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, current->add(ins); current->push(value); } else { - MInstruction* initLength; - if (unboxedType != JSVAL_TYPE_MAGIC) - initLength = MUnboxedArrayInitializedLength::New(alloc(), obj); - else - initLength = MInitializedLength::New(alloc(), elements); - current->add(initLength); + MInstruction* initLength = initializedLength(obj, elements, unboxedType); - bool needsHoleCheck; - if (safety == SetElem_Normal) { - id = addBoundsCheck(id, initLength); - needsHoleCheck = !packed && !writeOutOfBounds; - } else { - needsHoleCheck = false; - } + id = addBoundsCheck(id, initLength); + bool needsHoleCheck = !packed && !writeOutOfBounds; if (unboxedType != JSVAL_TYPE_MAGIC) { store = storeUnboxedValue(obj, elements, 0, id, unboxedType, newValue); @@ -9234,8 +9240,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, current->add(store); } - if (safety == SetElem_Normal) - current->push(value); + current->push(value); } if (!resumeAfter(store)) @@ -9255,16 +9260,11 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, bool -IonBuilder::jsop_setelem_typed(Scalar::Type arrayType, SetElemSafety safety, +IonBuilder::jsop_setelem_typed(Scalar::Type arrayType, MDefinition* obj, MDefinition* id, MDefinition* value) { - bool expectOOB; - if (safety == SetElem_Normal) { - SetElemICInspector icInspect(inspector->setElemICInspector(pc)); - expectOOB = icInspect.sawOOBTypedArrayWrite(); - } else { - expectOOB = false; - } + SetElemICInspector icInspect(inspector->setElemICInspector(pc)); + bool expectOOB = icInspect.sawOOBTypedArrayWrite(); if (expectOOB) spew("Emitting OOB TypedArray SetElem"); @@ -9277,9 +9277,7 @@ IonBuilder::jsop_setelem_typed(Scalar::Type arrayType, SetElemSafety safety, // Get length, bounds-check, then get elements, and add all instructions. MInstruction* length; MInstruction* elements; - BoundsChecking checking = (!expectOOB && safety == SetElem_Normal) - ? DoBoundsCheck - : SkipBoundsCheck; + BoundsChecking checking = expectOOB ? SkipBoundsCheck : DoBoundsCheck; addTypedArrayLengthAndData(obj, checking, &id, &length, &elements); // Clamp value to [0, 255] for Uint8ClampedArray. @@ -9300,31 +9298,11 @@ IonBuilder::jsop_setelem_typed(Scalar::Type arrayType, SetElemSafety safety, } current->add(ins); - - if (safety == SetElem_Normal) - current->push(value); + current->push(value); return resumeAfter(ins); } -bool -IonBuilder::jsop_setelem_typed_object(Scalar::Type arrayType, SetElemSafety safety, - MDefinition* object, MDefinition* index, MDefinition* value) -{ - MOZ_ASSERT(safety == SetElem_Unsafe); // Can be fixed, but there's been no reason to as of yet - - MInstruction* intIndex = MToInt32::New(alloc(), index); - current->add(intIndex); - - size_t elemSize = ScalarTypeDescr::alignment(arrayType); - - LinearSum byteOffset(alloc()); - if (!byteOffset.add(intIndex, elemSize)) - setForceAbort(); - - return storeScalarTypedObjectValue(object, byteOffset, arrayType, value); -} - bool IonBuilder::jsop_length() { @@ -9498,8 +9476,7 @@ IonBuilder::jsop_rest() current->add(templateConst); MNewArray* array = MNewArray::New(alloc(), constraints(), numRest, templateConst, - templateObject->group()->initialHeap(constraints()), - NewArray_FullyAllocating, pc); + templateObject->group()->initialHeap(constraints()), pc); current->add(array); if (numRest == 0) { @@ -10147,6 +10124,9 @@ IonBuilder::jsop_getprop(PropertyName* name) if (!getPropTryInnerize(&emitted, obj, name, types) || emitted) return emitted; + if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches)) + goto do_InlineCache; + // Try to hardcode known constants. trackOptimizationAttempt(TrackedStrategy::GetProp_Constant); if (!getPropTryConstant(&emitted, obj, name, types) || emitted) @@ -10182,6 +10162,7 @@ IonBuilder::jsop_getprop(PropertyName* name) if (!getPropTryInlineAccess(&emitted, obj, name, barrier, types) || emitted) return emitted; + do_InlineCache: // Try to emit a polymorphic cache. trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache); if (!getPropTryCache(&emitted, obj, name, barrier, types) || emitted) @@ -10869,8 +10850,7 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName } } - JSFunction* tenuredCommonGetter = IsInsideNursery(commonGetter) ? nullptr : commonGetter; - if (!makeCall(tenuredCommonGetter, callInfo)) + if (!makeCall(commonGetter, callInfo)) return false; // If the getter could have been inlined, don't track success. The call to @@ -10934,11 +10914,6 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName { MOZ_ASSERT(*emitted == false); - if (obj->type() != MIRType_Object) { - trackOptimizationOutcome(TrackedOutcome::NotObject); - return true; - } - BaselineInspector::ReceiverVector receivers(alloc()); BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups)) @@ -11176,6 +11151,9 @@ IonBuilder::getPropTryInnerize(bool* emitted, MDefinition* obj, PropertyName* na if (inner == obj) return true; + if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches)) + goto do_InlineCache; + // Note: the Baseline ICs don't know about this optimization, so it's // possible the global property's HeapTypeSet has not been initialized // yet. In this case we'll fall back to getPropTryCache for now. @@ -11197,6 +11175,7 @@ IonBuilder::getPropTryInnerize(bool* emitted, MDefinition* obj, PropertyName* na if (!getPropTryCommonGetter(emitted, inner, name, types) || *emitted) return *emitted; + do_InlineCache: // Passing the inner object to GetProperty IC is safe, see the // needsOuterizedThisObject check in IsCacheableGetPropCallNative. BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), @@ -11230,6 +11209,9 @@ IonBuilder::jsop_setprop(PropertyName* name) return resumeAfter(ins); } + if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches)) + goto do_InlineCache_step1; + // Try to inline a common property setter, or make a call. trackOptimizationAttempt(TrackedStrategy::SetProp_CommonSetter); if (!setPropTryCommonSetter(&emitted, obj, name, value) || emitted) @@ -11240,20 +11222,28 @@ IonBuilder::jsop_setprop(PropertyName* name) if (!setPropTryTypedObject(&emitted, obj, name, value) || emitted) return emitted; + do_InlineCache_step1: TemporaryTypeSet* objTypes = obj->resultTypeSet(); bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value, /* canModify = */ true); + if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches)) + goto do_InlineCache_step2; + // Try to emit stores to unboxed objects. trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed); if (!setPropTryUnboxed(&emitted, obj, name, value, barrier, objTypes) || emitted) return emitted; + do_InlineCache_step2: // Add post barrier if needed. The instructions above manage any post // barriers they need directly. if (NeedsPostBarrier(info(), value)) current->add(MPostWriteBarrier::New(alloc(), obj, value)); + if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches)) + goto do_InlineCache_step3; + // Try to emit store from definite slots. trackOptimizationAttempt(TrackedStrategy::SetProp_DefiniteSlot); if (!setPropTryDefiniteSlot(&emitted, obj, name, value, barrier, objTypes) || emitted) @@ -11264,7 +11254,9 @@ IonBuilder::jsop_setprop(PropertyName* name) if (!setPropTryInlineAccess(&emitted, obj, name, value, barrier, objTypes) || emitted) return emitted; + do_InlineCache_step3: // Emit a polymorphic cache. + trackOptimizationAttempt(TrackedStrategy::SetProp_InlineCache); return setPropTryCache(&emitted, obj, name, value, barrier, objTypes); } @@ -11360,8 +11352,7 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj, } } - JSFunction* tenuredCommonSetter = IsInsideNursery(commonSetter) ? nullptr : commonSetter; - MCall* call = makeCallHelper(tenuredCommonSetter, callInfo); + MCall* call = makeCallHelper(commonSetter, callInfo); if (!call) return false; @@ -11783,6 +11774,7 @@ IonBuilder::setPropTryCache(bool* emitted, MDefinition* obj, if (!resumeAfter(ins)) return false; + trackOptimizationSuccess(); *emitted = true; return true; } @@ -12294,11 +12286,9 @@ IonBuilder::jsop_getaliasedvar(ScopeCoordinate sc) JSObject* call = nullptr; if (hasStaticScopeObject(sc, &call) && call) { PropertyName* name = ScopeCoordinateName(scopeCoordinateNameCache, script(), pc); - bool succeeded; - if (!getStaticName(call, name, &succeeded, takeLexicalCheck())) - return false; - if (succeeded) - return true; + bool emitted = false; + if (!getStaticName(call, name, &emitted, takeLexicalCheck()) || emitted) + return emitted; } // See jsop_checkaliasedlet. @@ -12368,12 +12358,21 @@ IonBuilder::jsop_in() MDefinition* obj = current->peek(-1); MDefinition* id = current->peek(-2); - if (!shouldAbortOnPreliminaryGroups(obj) && - ElementAccessIsDenseNative(constraints(), obj, id) && - !ElementAccessHasExtraIndexedProperty(constraints(), obj)) - { - return jsop_in_dense(); - } + do { + if (shouldAbortOnPreliminaryGroups(obj)) + break; + + JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, id); + if (unboxedType == JSVAL_TYPE_MAGIC) { + if (!ElementAccessIsDenseNative(constraints(), obj, id)) + break; + } + + if (ElementAccessHasExtraIndexedProperty(constraints(), obj)) + break; + + return jsop_in_dense(unboxedType); + } while (false); current->pop(); current->pop(); @@ -12386,7 +12385,7 @@ IonBuilder::jsop_in() } bool -IonBuilder::jsop_in_dense() +IonBuilder::jsop_in_dense(JSValueType unboxedType) { MDefinition* obj = current->pop(); MDefinition* id = current->pop(); @@ -12399,11 +12398,10 @@ IonBuilder::jsop_in_dense() id = idInt32; // Get the elements vector. - MElements* elements = MElements::New(alloc(), obj); + MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); current->add(elements); - MInitializedLength* initLength = MInitializedLength::New(alloc(), elements); - current->add(initLength); + MInstruction* initLength = initializedLength(obj, elements, unboxedType); // If there are no holes, speculate the InArray check will not fail. if (!needsHoleCheck && !failedBoundsCheck_) { @@ -12412,7 +12410,8 @@ IonBuilder::jsop_in_dense() } // Check if id < initLength and elem[id] not a hole. - MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck); + MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck, + unboxedType); current->add(ins); current->push(ins); @@ -13048,6 +13047,37 @@ IonBuilder::constantInt(int32_t i) return constant(Int32Value(i)); } +MInstruction* +IonBuilder::initializedLength(MDefinition* obj, MDefinition* elements, JSValueType unboxedType) +{ + MInstruction* res; + if (unboxedType != JSVAL_TYPE_MAGIC) + res = MUnboxedArrayInitializedLength::New(alloc(), obj); + else + res = MInitializedLength::New(alloc(), elements); + current->add(res); + return res; +} + +MInstruction* +IonBuilder::setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count) +{ + MOZ_ASSERT(count); + + MInstruction* res; + if (unboxedType != JSVAL_TYPE_MAGIC) { + res = MSetUnboxedArrayInitializedLength::New(alloc(), obj, constant(Int32Value(count))); + } else { + // MSetInitializedLength takes the index of the last element, rather + // than the count itself. + MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false); + current->add(elements); + res = MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1))); + } + current->add(res); + return res; +} + MDefinition* IonBuilder::getCallee() { diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 9b595c3e9b..3de1bd09ec 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -46,16 +46,6 @@ class IonBuilder ControlStatus_None // No control flow. }; - enum SetElemSafety { - // Normal write like a[b] = c. - SetElem_Normal, - - // Write due to UnsafePutElements: - // - assumed to be in bounds, - // - not checked for data races - SetElem_Unsafe, - }; - struct DeferredEdge : public TempObject { MBasicBlock* block; @@ -352,6 +342,9 @@ class IonBuilder MConstant* constant(const Value& v); MConstant* constantInt(int32_t i); + MInstruction* initializedLength(MDefinition* obj, MDefinition* elements, + JSValueType unboxedType); + MInstruction* setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count); // Improve the type information at tests bool improveTypesAtTest(MDefinition* ins, bool trueBranch, MTest* test); @@ -548,7 +541,7 @@ class IonBuilder bool setElemTryTypedStatic(bool* emitted, MDefinition* object, MDefinition* index, MDefinition* value); bool setElemTryDense(bool* emitted, MDefinition* object, - MDefinition* index, MDefinition* value); + MDefinition* index, MDefinition* value, bool writeHole); bool setElemTryArguments(bool* emitted, MDefinition* object, MDefinition* index, MDefinition* value); bool setElemTryCache(bool* emitted, MDefinition* object, @@ -566,6 +559,9 @@ class IonBuilder MDefinition* value, TypedObjectPrediction elemTypeReprs, int32_t elemSize); + bool initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value, + JSValueType unboxedType, + bool addResumePointAndIncrementInitializedLength); // jsop_getelem() helpers. bool getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index); @@ -661,15 +657,10 @@ class IonBuilder bool jsop_getelem_typed(MDefinition* obj, MDefinition* index, ScalarTypeDescr::Type arrayType); bool jsop_setelem(); bool jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, - SetElemSafety safety, MDefinition* object, MDefinition* index, MDefinition* value, - JSValueType unboxedType); + JSValueType unboxedType, bool writeHole); bool jsop_setelem_typed(ScalarTypeDescr::Type arrayType, - SetElemSafety safety, MDefinition* object, MDefinition* index, MDefinition* value); - bool jsop_setelem_typed_object(ScalarTypeDescr::Type arrayType, - SetElemSafety safety, - MDefinition* object, MDefinition* index, MDefinition* value); bool jsop_length(); bool jsop_length_fastPath(); bool jsop_arguments(); @@ -702,7 +693,7 @@ class IonBuilder bool jsop_isnoiter(); bool jsop_iterend(); bool jsop_in(); - bool jsop_in_dense(); + bool jsop_in_dense(JSValueType unboxedType); bool jsop_instanceof(); bool jsop_getaliasedvar(ScopeCoordinate sc); bool jsop_setaliasedvar(ScopeCoordinate sc); @@ -781,8 +772,9 @@ class IonBuilder InliningStatus inlineRegExpExec(CallInfo& callInfo); InliningStatus inlineRegExpTest(CallInfo& callInfo); - // Object natives. + // Object natives and intrinsics. InliningStatus inlineObjectCreate(CallInfo& callInfo); + InliningStatus inlineDefineDataProperty(CallInfo& callInfo); // Atomics natives. InliningStatus inlineAtomicsCompareExchange(CallInfo& callInfo); @@ -791,14 +783,6 @@ class IonBuilder InliningStatus inlineAtomicsFence(CallInfo& callInfo); InliningStatus inlineAtomicsBinop(CallInfo& callInfo, JSFunction* target); - // Array intrinsics. - InliningStatus inlineUnsafePutElements(CallInfo& callInfo); - bool inlineUnsafeSetDenseArrayElement(CallInfo& callInfo, uint32_t base); - bool inlineUnsafeSetTypedArrayElement(CallInfo& callInfo, uint32_t base, - ScalarTypeDescr::Type arrayType); - bool inlineUnsafeSetTypedObjectArrayElement(CallInfo& callInfo, uint32_t base, - ScalarTypeDescr::Type arrayType); - // Slot intrinsics. InliningStatus inlineUnsafeSetReservedSlot(CallInfo& callInfo); InliningStatus inlineUnsafeGetReservedSlot(CallInfo& callInfo, @@ -815,8 +799,6 @@ class IonBuilder // TypedObject intrinsics and natives. InliningStatus inlineObjectIsTypeDescr(CallInfo& callInfo); InliningStatus inlineSetTypedObjectOffset(CallInfo& callInfo); - bool elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefinition* id, - ScalarTypeDescr::Type* arrayType); InliningStatus inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* target); // SIMD intrinsics and natives. diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index f89fdcc3b6..d2876b3bfd 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -3028,6 +3028,9 @@ CanAttachNativeSetProp(JSContext* cx, HandleObject obj, HandleId id, ConstantOrR if (obj->isNative() && (!shape || (obj != holder && shape->hasDefaultSetter() && shape->hasSlot()))) return SetPropertyIC::MaybeCanAttachAddSlot; + if (IsImplicitNonNativeProperty(shape)) + return SetPropertyIC::CanAttachNone; + if (IsCacheableSetPropCallPropertyOp(obj, holder, shape) || IsCacheableSetPropCallNative(obj, holder, shape) || IsCacheableSetPropCallScripted(obj, holder, shape)) @@ -3067,13 +3070,7 @@ GenerateSetUnboxed(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(unboxedType)); } - // If the unboxed object's type has known properties, then instances have - // never been converted to native objects and the type set check performed - // above ensures the value being written can be stored in the unboxed - // object. - Label* storeFailure = obj->group()->unknownProperties() ? &failure : nullptr; - - masm.storeUnboxedProperty(address, unboxedType, value, storeFailure); + masm.storeUnboxedProperty(address, unboxedType, value, &failure); attacher.jumpRejoin(masm); diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp index c2cc1b52ef..e2980f523e 100644 --- a/js/src/jit/JitOptions.cpp +++ b/js/src/jit/JitOptions.cpp @@ -117,6 +117,9 @@ JitOptions::JitOptions() // Whether functions are compiled immediately. SET_DEFAULT(eagerCompilation, false); + // Whether IonBuilder should prefer IC generation above specialized MIR. + SET_DEFAULT(forceInlineCaches, false); + // Force how many invocation or loop iterations are needed before compiling // a function with the highest ionmonkey optimization level. // (i.e. OptimizationLevel_Normal) @@ -165,6 +168,9 @@ JitOptions::JitOptions() // The bytecode length limit for small function. SET_DEFAULT(smallFunctionMaxBytecodeLength_, 100); + + // Toggles whether unboxed plain objects can be created by the VM. + SET_DEFAULT(disableUnboxedObjects, false); } bool diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h index c2caf9b976..acf9d5df31 100644 --- a/js/src/jit/JitOptions.h +++ b/js/src/jit/JitOptions.h @@ -56,6 +56,7 @@ struct JitOptions bool disableEaa; bool disableAma; bool eagerCompilation; + bool forceInlineCaches; mozilla::Maybe forcedDefaultIonWarmUpThreshold; mozilla::Maybe forcedRegisterAllocator; bool limitScriptSize; @@ -67,6 +68,9 @@ struct JitOptions uint32_t osrPcMismatchesBeforeRecompile; uint32_t smallFunctionMaxBytecodeLength_; + // The options below affect the rest of the VM, and not just the JIT. + bool disableUnboxedObjects; + JitOptions(); bool isSmallFunction(JSScript* script) const; void setEagerCompilation(); diff --git a/js/src/jit/JitSpewer.cpp b/js/src/jit/JitSpewer.cpp index 12f441c8b2..c6ce142c52 100644 --- a/js/src/jit/JitSpewer.cpp +++ b/js/src/jit/JitSpewer.cpp @@ -505,7 +505,8 @@ jit::JitSpewStartVA(JitSpewChannel channel, const char* fmt, va_list ap) return; JitSpewHeader(channel); - vfprintf(stderr, fmt, ap); + Fprinter& out = JitSpewPrinter(); + out.vprintf(fmt, ap); } void @@ -514,7 +515,8 @@ jit::JitSpewContVA(JitSpewChannel channel, const char* fmt, va_list ap) if (!JitSpewEnabled(channel)) return; - vfprintf(stderr, fmt, ap); + Fprinter& out = JitSpewPrinter(); + out.vprintf(fmt, ap); } void @@ -523,7 +525,8 @@ jit::JitSpewFin(JitSpewChannel channel) if (!JitSpewEnabled(channel)) return; - fprintf(stderr, "\n"); + Fprinter& out = JitSpewPrinter(); + out.put("\n"); } void @@ -578,7 +581,8 @@ jit::JitSpewHeader(JitSpewChannel channel) if (!JitSpewEnabled(channel)) return; - fprintf(stderr, "[%s] ", ChannelNames[channel]); + Fprinter& out = JitSpewPrinter(); + out.printf("[%s] ", ChannelNames[channel]); } bool diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 8445de2fa3..70b27de3b3 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -119,14 +119,10 @@ class LMoveGroup : public LInstructionHelper<0, 0, 0> void setScratchRegister(Register reg) { scratchRegister_ = LGeneralReg(reg); } -#endif LAllocation maybeScratchRegister() { -#ifdef JS_CODEGEN_X86 return scratchRegister_; -#else - return LAllocation(); -#endif } +#endif bool uses(Register reg) { for (size_t i = 0; i < numMoves(); i++) { @@ -3052,23 +3048,6 @@ class LPowD : public LCallInstructionHelper<1, 2, 1> } }; -// Math.random(). -class LRandom : public LCallInstructionHelper<1, 0, 2> -{ - public: - LIR_HEADER(Random) - LRandom(const LDefinition& temp, const LDefinition& temp2) { - setTemp(0, temp); - setTemp(1, temp2); - } - const LDefinition* temp() { - return getTemp(0); - } - const LDefinition* temp2() { - return getTemp(1); - } -}; - class LMathFunctionD : public LCallInstructionHelper<1, 1, 1> { public: @@ -4162,6 +4141,30 @@ class LIncrementUnboxedArrayInitializedLength : public LInstructionHelper<0, 1, } }; +class LSetUnboxedArrayInitializedLength : public LInstructionHelper<0, 2, 1> +{ + public: + LIR_HEADER(SetUnboxedArrayInitializedLength) + + explicit LSetUnboxedArrayInitializedLength(const LAllocation& object, + const LAllocation& length, + const LDefinition& temp) { + setOperand(0, object); + setOperand(1, length); + setTemp(0, temp); + } + + const LAllocation* object() { + return getOperand(0); + } + const LAllocation* length() { + return getOperand(1); + } + const LDefinition* temp() { + return getTemp(0); + } +}; + // Load the length from an elements header. class LArrayLength : public LInstructionHelper<1, 1, 0> { diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index 6593792a99..5ddba67444 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -215,6 +215,7 @@ _(UnboxedArrayLength) \ _(UnboxedArrayInitializedLength) \ _(IncrementUnboxedArrayInitializedLength) \ + _(SetUnboxedArrayInitializedLength) \ _(BoundsCheck) \ _(BoundsCheckRange) \ _(BoundsCheckLower) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index a5a730c974..68881a3398 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1376,13 +1376,6 @@ LIRGenerator::visitPow(MPow* ins) defineReturn(lir, ins); } -void -LIRGenerator::visitRandom(MRandom* ins) -{ - LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1)); - defineReturn(lir, ins); -} - void LIRGenerator::visitMathFunction(MMathFunction* ins) { @@ -2501,6 +2494,14 @@ LIRGenerator::visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArray add(new(alloc()) LIncrementUnboxedArrayInitializedLength(useRegister(ins->object())), ins); } +void +LIRGenerator::visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins) +{ + add(new(alloc()) LSetUnboxedArrayInitializedLength(useRegister(ins->object()), + useRegisterOrConstant(ins->length()), + temp()), ins); +} + void LIRGenerator::visitNot(MNot* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 2e67acd3ba..adaa2ebe78 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -129,7 +129,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitAtan2(MAtan2* ins); void visitHypot(MHypot* ins); void visitPow(MPow* ins); - void visitRandom(MRandom* ins); void visitMathFunction(MMathFunction* ins); void visitAdd(MAdd* ins); void visitSub(MSub* ins); @@ -190,6 +189,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitUnboxedArrayLength(MUnboxedArrayLength* ins); void visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins); void visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins); + void visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins); void visitNot(MNot* ins); void visitBoundsCheck(MBoundsCheck* ins); void visitBoundsCheckLower(MBoundsCheckLower* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 54fe4d8e70..a3ab226313 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -21,6 +21,7 @@ #include "vm/NativeObject-inl.h" #include "vm/StringObject-inl.h" +#include "vm/UnboxedObject-inl.h" using mozilla::ArrayLength; @@ -183,10 +184,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) // Object natives. if (native == obj_create) return inlineObjectCreate(callInfo); - - // Array intrinsics. - if (native == intrinsic_UnsafePutElements) - return inlineUnsafePutElements(callInfo); + if (native == intrinsic_DefineDataProperty) + return inlineDefineDataProperty(callInfo); // Slot intrinsics. if (native == intrinsic_UnsafeSetReservedSlot) @@ -516,21 +515,23 @@ IonBuilder::InliningStatus IonBuilder::inlineArray(CallInfo& callInfo) { uint32_t initLength = 0; - AllocatingBehaviour allocating = NewArray_Unallocating; JSObject* templateObject = inspector->getTemplateObjectForNative(pc, ArrayConstructor); if (!templateObject) { trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoTemplateObj); return InliningStatus_NotInlined; } - ArrayObject* templateArray = &templateObject->as(); + + if (templateObject->is()) { + if (templateObject->group()->unboxedLayout().nativeGroup()) + return InliningStatus_NotInlined; + } // Multiple arguments imply array initialization, not just construction. if (callInfo.argc() >= 2) { initLength = callInfo.argc(); - allocating = NewArray_FullyAllocating; - TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(templateArray); + TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(templateObject); if (!key->unknownProperties()) { HeapTypeSetKey elemTypes = key->property(JSID_VOID); @@ -553,8 +554,8 @@ IonBuilder::inlineArray(CallInfo& callInfo) if (!arg->isConstantValue()) { callInfo.setImplicitlyUsedUnchecked(); MNewArrayDynamicLength* ins = - MNewArrayDynamicLength::New(alloc(), constraints(), templateArray, - templateArray->group()->initialHeap(constraints()), + MNewArrayDynamicLength::New(alloc(), constraints(), templateObject, + templateObject->group()->initialHeap(constraints()), arg); current->add(ins); current->push(ins); @@ -572,63 +573,34 @@ IonBuilder::inlineArray(CallInfo& callInfo) // Make sure initLength matches the template object's length. This is // not guaranteed to be the case, for instance if we're inlining the // MConstant may come from an outer script. - if (initLength != templateArray->as().length()) + if (initLength != GetAnyBoxedOrUnboxedArrayLength(templateObject)) return InliningStatus_NotInlined; // Don't inline large allocations. if (initLength > ArrayObject::EagerAllocationMaxLength) return InliningStatus_NotInlined; - - allocating = NewArray_FullyAllocating; } callInfo.setImplicitlyUsedUnchecked(); - MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateArray); + MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); current->add(templateConst); MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst, - templateArray->group()->initialHeap(constraints()), - allocating, pc); + templateObject->group()->initialHeap(constraints()), pc); current->add(ins); current->push(ins); if (callInfo.argc() >= 2) { - // Get the elements vector. - MElements* elements = MElements::New(alloc(), ins); - current->add(elements); - - // Store all values, no need to initialize the length after each as - // jsop_initelem_array is doing because we do not expect to bailout - // because the memory is supposed to be allocated by now. - MConstant* id = nullptr; + JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject); for (uint32_t i = 0; i < initLength; i++) { - id = MConstant::New(alloc(), Int32Value(i)); - current->add(id); - MDefinition* value = callInfo.getArg(i); - if (ins->convertDoubleElements()) { - MInstruction* valueDouble = MToDouble::New(alloc(), value); - current->add(valueDouble); - value = valueDouble; - } - - // There is normally no need for a post barrier on these writes - // because the new array will be in the nursery. However, this - // assumption is volated if we specifically requested pre-tenuring. - if (ins->initialHeap() == gc::TenuredHeap) - current->add(MPostWriteBarrier::New(alloc(), ins, value)); - - MStoreElement* store = MStoreElement::New(alloc(), elements, id, value, - /* needsHoleCheck = */ false); - current->add(store); + if (!initializeArrayElement(ins, i, value, unboxedType, /* addResumePoint = */ false)) + return InliningStatus_Error; } - // Update the length. - MSetInitializedLength* length = MSetInitializedLength::New(alloc(), elements, id); - current->add(length); - - if (!resumeAfter(length)) + MInstruction* setLength = setInitializedLength(ins, unboxedType, initLength); + if (!resumeAfter(setLength)) return InliningStatus_Error; } @@ -942,6 +914,13 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo) HeapTypeSetKey elemTypes = key->property(JSID_VOID); if (!elemTypes.knownSubset(constraints(), thisElemTypes)) return InliningStatus_NotInlined; + + if (thisGroup->clasp() == &UnboxedArrayObject::class_ && + !CanStoreUnboxedType(alloc(), thisGroup->unboxedLayout().elementType(), + MIRType_Value, elemTypes.maybeTypes())) + { + return InliningStatus_NotInlined; + } } // Inline the call. @@ -1652,14 +1631,13 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo) // Check if exist a template object in stub. JSString* stringThis = nullptr; JSString* stringArg = nullptr; - NativeObject* templateObject = nullptr; + JSObject* templateObject = nullptr; if (!inspector->isOptimizableCallStringSplit(pc, &stringThis, &stringArg, &templateObject)) return InliningStatus_NotInlined; MOZ_ASSERT(stringThis); MOZ_ASSERT(stringArg); MOZ_ASSERT(templateObject); - MOZ_ASSERT(templateObject->is()); if (strval->toString() != stringThis) return InliningStatus_NotInlined; @@ -1679,13 +1657,13 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo) if (!key.maybeTypes()->hasType(TypeSet::StringType())) return InliningStatus_NotInlined; - uint32_t initLength = templateObject->as().length(); - if (templateObject->getDenseInitializedLength() != initLength) + uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(templateObject); + if (GetAnyBoxedOrUnboxedInitializedLength(templateObject) != initLength) return InliningStatus_NotInlined; Vector arrayValues; for (uint32_t i = 0; i < initLength; i++) { - Value str = templateObject->getDenseElement(i); + Value str = GetAnyBoxedOrUnboxedDenseElement(templateObject, i); MOZ_ASSERT(str.toString()->isAtom()); MConstant* value = MConstant::New(alloc(), str, constraints()); if (!TypeSetIncludes(key.maybeTypes(), value->type(), value->resultTypeSet())) @@ -1705,8 +1683,7 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo) current->add(templateConst); MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst, - templateObject->group()->initialHeap(constraints()), - NewArray_FullyAllocating, pc); + templateObject->group()->initialHeap(constraints()), pc); current->add(ins); current->push(ins); @@ -1717,37 +1694,21 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo) return InliningStatus_Inlined; } - // Get the elements vector. - MElements* elements = MElements::New(alloc(), ins); - current->add(elements); + JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject); // Store all values, no need to initialize the length after each as // jsop_initelem_array is doing because we do not expect to bailout // because the memory is supposed to be allocated by now. - MConstant* id = nullptr; for (uint32_t i = 0; i < initLength; i++) { - id = MConstant::New(alloc(), Int32Value(i)); - current->add(id); - MConstant* value = arrayValues[i]; current->add(value); - MStoreElement* store = MStoreElement::New(alloc(), elements, id, value, - /* needsHoleCheck = */ false); - current->add(store); - - // There is normally no need for a post barrier on these writes - // because the new array will be in the nursery. However, this - // assumption is volated if we specifically requested pre-tenuring. - if (ins->initialHeap() == gc::TenuredHeap) - current->add(MPostWriteBarrier::New(alloc(), ins, value)); + if (!initializeArrayElement(ins, i, value, unboxedType, /* addResumePoint = */ false)) + return InliningStatus_Error; } - // Update the length. - MSetInitializedLength* length = MSetInitializedLength::New(alloc(), elements, id); - current->add(length); - - if (!resumeAfter(length)) + MInstruction* setLength = setInitializedLength(ins, unboxedType, initLength); + if (!resumeAfter(setLength)) return InliningStatus_Error; return InliningStatus_Inlined; @@ -1773,7 +1734,6 @@ IonBuilder::inlineStringSplit(CallInfo& callInfo) JSObject* templateObject = inspector->getTemplateObjectForNative(pc, js::str_split); if (!templateObject) return InliningStatus_NotInlined; - MOZ_ASSERT(templateObject->is()); TypeSet::ObjectKey* retKey = TypeSet::ObjectKey::get(templateObject); if (retKey->unknownProperties()) @@ -2128,174 +2088,36 @@ IonBuilder::inlineObjectCreate(CallInfo& callInfo) } IonBuilder::InliningStatus -IonBuilder::inlineUnsafePutElements(CallInfo& callInfo) +IonBuilder::inlineDefineDataProperty(CallInfo& callInfo) { - uint32_t argc = callInfo.argc(); - if (argc < 3 || (argc % 3) != 0 || callInfo.constructing()) { - trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); + MOZ_ASSERT(!callInfo.constructing()); + + // Only handle definitions of plain data properties. + if (callInfo.argc() != 3) return InliningStatus_NotInlined; - } - /* Important: - * - * Here we inline each of the stores resulting from a call to - * UnsafePutElements(). It is essential that these stores occur - * atomically and cannot be interrupted by a stack or recursion - * check. If this is not true, race conditions can occur. - */ + MDefinition* obj = callInfo.getArg(0); + MDefinition* id = callInfo.getArg(1); + MDefinition* value = callInfo.getArg(2); - for (uint32_t base = 0; base < argc; base += 3) { - uint32_t arri = base + 0; - uint32_t idxi = base + 1; - uint32_t elemi = base + 2; + if (ElementAccessHasExtraIndexedProperty(constraints(), obj)) + return InliningStatus_NotInlined; - MDefinition* obj = callInfo.getArg(arri); - MDefinition* id = callInfo.getArg(idxi); - MDefinition* elem = callInfo.getArg(elemi); + // setElemTryDense will push the value as the result of the define instead + // of |undefined|, but this is fine if the rval is ignored (as it should be + // in self hosted code.) + MOZ_ASSERT(*GetNextPc(pc) == JSOP_POP); - bool isDenseNative = ElementAccessIsDenseNative(constraints(), obj, id); - - bool writeNeedsBarrier = false; - if (isDenseNative) { - writeNeedsBarrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, - &obj, nullptr, &elem, - /* canModify = */ false); - } - - // We can only inline setelem on dense arrays that do not need type - // barriers and on typed arrays and on typed object arrays. - Scalar::Type arrayType; - if ((!isDenseNative || writeNeedsBarrier) && - !ElementAccessIsAnyTypedArray(constraints(), obj, id, &arrayType) && - !elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType)) - { - return InliningStatus_NotInlined; - } - } + bool emitted = false; + if (!setElemTryDense(&emitted, obj, id, value, /* writeHole = */ true)) + return InliningStatus_Error; + if (!emitted) + return InliningStatus_NotInlined; callInfo.setImplicitlyUsedUnchecked(); - - // Push the result first so that the stack depth matches up for - // the potential bailouts that will occur in the stores below. - MConstant* udef = MConstant::New(alloc(), UndefinedValue()); - current->add(udef); - current->push(udef); - - for (uint32_t base = 0; base < argc; base += 3) { - uint32_t arri = base + 0; - uint32_t idxi = base + 1; - - MDefinition* obj = callInfo.getArg(arri); - MDefinition* id = callInfo.getArg(idxi); - - if (ElementAccessIsDenseNative(constraints(), obj, id)) { - if (!inlineUnsafeSetDenseArrayElement(callInfo, base)) - return InliningStatus_Error; - continue; - } - - Scalar::Type arrayType; - if (ElementAccessIsAnyTypedArray(constraints(), obj, id, &arrayType)) { - if (!inlineUnsafeSetTypedArrayElement(callInfo, base, arrayType)) - return InliningStatus_Error; - continue; - } - - if (elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType)) { - if (!inlineUnsafeSetTypedObjectArrayElement(callInfo, base, arrayType)) - return InliningStatus_Error; - continue; - } - - MOZ_CRASH("Element access not dense array nor typed array"); - } - return InliningStatus_Inlined; } -bool -IonBuilder::elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefinition* id, - Scalar::Type* arrayType) -{ - if (obj->type() != MIRType_Object) // lookupTypeDescrSet() tests for TypedObject - return false; - - if (id->type() != MIRType_Int32 && id->type() != MIRType_Double) - return false; - - TypedObjectPrediction prediction = typedObjectPrediction(obj); - if (prediction.isUseless() || !prediction.ofArrayKind()) - return false; - - TypedObjectPrediction elemPrediction = prediction.arrayElementType(); - if (elemPrediction.isUseless() || elemPrediction.kind() != type::Scalar) - return false; - - *arrayType = elemPrediction.scalarType(); - return true; -} - -bool -IonBuilder::inlineUnsafeSetDenseArrayElement(CallInfo& callInfo, uint32_t base) -{ - // Note: we do not check the conditions that are asserted as true - // in intrinsic_UnsafePutElements(): - // - arr is a dense array - // - idx < initialized length - // Furthermore, note that inlineUnsafePutElements ensures the type of the - // value is reflected in the JSID_VOID property of the array. - - MDefinition* obj = callInfo.getArg(base + 0); - MDefinition* id = callInfo.getArg(base + 1); - MDefinition* elem = callInfo.getArg(base + 2); - - TemporaryTypeSet::DoubleConversion conversion = - obj->resultTypeSet()->convertDoubleElements(constraints()); - if (!jsop_setelem_dense(conversion, SetElem_Unsafe, obj, id, elem, JSVAL_TYPE_MAGIC)) - return false; - return true; -} - -bool -IonBuilder::inlineUnsafeSetTypedArrayElement(CallInfo& callInfo, - uint32_t base, - Scalar::Type arrayType) -{ - // Note: we do not check the conditions that are asserted as true - // in intrinsic_UnsafePutElements(): - // - arr is a typed array - // - idx < length - - MDefinition* obj = callInfo.getArg(base + 0); - MDefinition* id = callInfo.getArg(base + 1); - MDefinition* elem = callInfo.getArg(base + 2); - - if (!jsop_setelem_typed(arrayType, SetElem_Unsafe, obj, id, elem)) - return false; - - return true; -} - -bool -IonBuilder::inlineUnsafeSetTypedObjectArrayElement(CallInfo& callInfo, - uint32_t base, - Scalar::Type arrayType) -{ - // Note: we do not check the conditions that are asserted as true - // in intrinsic_UnsafePutElements(): - // - arr is a typed array - // - idx < length - - MDefinition* obj = callInfo.getArg(base + 0); - MDefinition* id = callInfo.getArg(base + 1); - MDefinition* elem = callInfo.getArg(base + 2); - - if (!jsop_setelem_typed_object(arrayType, SetElem_Unsafe, obj, id, elem)) - return false; - - return true; -} - IonBuilder::InliningStatus IonBuilder::inlineHasClass(CallInfo& callInfo, const Class* clasp1, const Class* clasp2, diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 7dddc27acf..079c01a772 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -687,6 +687,26 @@ MakeUnknownTypeSet() return alloc->new_(alloc, TypeSet::UnknownType()); } +#ifdef DEBUG + +bool +jit::IonCompilationCanUseNurseryPointers() +{ + // If we are doing backend compilation, which could occur on a helper + // thread but might actually be on the main thread, check the flag set on + // the PerThreadData by AutoEnterIonCompilation. + if (CurrentThreadIsIonCompiling()) + return !CurrentThreadIsIonCompilingSafeForMinorGC(); + + // Otherwise, we must be on the main thread during MIR construction. The + // store buffer must have been notified that minor GCs must cancel pending + // or in progress Ion compilations. + JSRuntime* rt = TlsPerThreadData.get()->runtimeFromMainThread(); + return rt->gc.storeBuffer.cancelIonCompilations(); +} + +#endif // DEBUG + MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints) : value_(vp) { @@ -1815,6 +1835,40 @@ jit::EqualTypes(MIRType type1, TemporaryTypeSet* typeset1, return typeset1->equals(typeset2); } +// Tests whether input/inputTypes can always be stored to an unboxed +// object/array property with the given unboxed type. +bool +jit::CanStoreUnboxedType(TempAllocator& alloc, + JSValueType unboxedType, MIRType input, TypeSet* inputTypes) +{ + TemporaryTypeSet types; + + switch (unboxedType) { + case JSVAL_TYPE_BOOLEAN: + case JSVAL_TYPE_INT32: + case JSVAL_TYPE_DOUBLE: + case JSVAL_TYPE_STRING: + types.addType(TypeSet::PrimitiveType(unboxedType), alloc.lifoAlloc()); + break; + + case JSVAL_TYPE_OBJECT: + types.addType(TypeSet::AnyObjectType(), alloc.lifoAlloc()); + types.addType(TypeSet::NullType(), alloc.lifoAlloc()); + break; + + default: + MOZ_CRASH("Bad unboxed type"); + } + + return TypeSetIncludes(&types, input, inputTypes); +} + +static bool +CanStoreUnboxedType(TempAllocator& alloc, JSValueType unboxedType, MDefinition* value) +{ + return CanStoreUnboxedType(alloc, unboxedType, value->type(), value->resultTypeSet()); +} + bool MPhi::specializeType() { @@ -3994,11 +4048,10 @@ MArrayState::Copy(TempAllocator& alloc, MArrayState* state) } MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst, - gc::InitialHeap initialHeap, AllocatingBehaviour allocating, jsbytecode* pc) + gc::InitialHeap initialHeap, jsbytecode* pc) : MUnaryInstruction(templateConst), count_(count), initialHeap_(initialHeap), - allocating_(allocating), convertDoubleElements_(false), pc_(pc) { @@ -4017,11 +4070,6 @@ MNewArray::shouldUseVM() const if (!templateObject()) return true; - // Allocate space using the VMCall when mir hints it needs to get allocated - // immediately, but only when data doesn't fit the available array slots. - if (allocatingBehaviour() == NewArray_Unallocating) - return false; - if (templateObject()->is()) { MOZ_ASSERT(templateObject()->as().capacity() >= count()); return !templateObject()->as().hasInlineElements(); @@ -5186,6 +5234,23 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* } } + // Perform additional filtering to make sure that any unboxed property + // being written can accommodate the value. + for (size_t i = 0; i < types->getObjectCount(); i++) { + TypeSet::ObjectKey* key = types->getObject(i); + if (key && key->isGroup() && key->group()->maybeUnboxedLayout()) { + const UnboxedLayout& layout = key->group()->unboxedLayout(); + if (name) { + const UnboxedLayout::Property* property = layout.lookup(name); + if (property && !CanStoreUnboxedType(alloc, property->type, *pvalue)) + return true; + } else { + if (layout.isArray() && !CanStoreUnboxedType(alloc, layout.elementType(), *pvalue)) + return true; + } + } + } + if (success) return false; diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 4cbed58a0a..e7a53c68f5 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2864,22 +2864,32 @@ bool EqualTypes(MIRType type1, TemporaryTypeSet* typeset1, MIRType type2, TemporaryTypeSet* typeset2); -// Helper class to assert all GC pointers embedded in MIR instructions are -// tenured. Off-thread Ion compilation and nursery GCs can happen in parallel, -// so it's invalid to store pointers to nursery things. There's no need to root -// these pointers, as GC is suppressed during compilation and off-thread -// compilations are canceled on every major GC. +bool +CanStoreUnboxedType(TempAllocator& alloc, + JSValueType unboxedType, MIRType input, TypeSet* inputTypes); + +#ifdef DEBUG +bool +IonCompilationCanUseNurseryPointers(); +#endif + +// Helper class to check that GC pointers embedded in MIR instructions are in +// in the nursery only when the store buffer has been marked as needing to +// cancel all ion compilations. Otherwise, off-thread Ion compilation and +// nursery GCs can happen in parallel, so it's invalid to store pointers to +// nursery things. There's no need to root these pointers, as GC is suppressed +// during compilation and off-thread compilations are canceled on major GCs. template -class AlwaysTenured +class CompilerGCPointer { js::gc::Cell* ptr_; public: - explicit AlwaysTenured(T ptr) + explicit CompilerGCPointer(T ptr) : ptr_(ptr) { + MOZ_ASSERT_IF(IsInsideNursery(ptr), IonCompilationCanUseNurseryPointers()); #ifdef DEBUG - MOZ_ASSERT(!IsInsideNursery(ptr_)); PerThreadData* pt = TlsPerThreadData.get(); MOZ_ASSERT_IF(pt->runtimeIfOnOwnerThread(), pt->suppressGC); #endif @@ -2889,17 +2899,18 @@ class AlwaysTenured T operator->() const { return static_cast(ptr_); } private: - AlwaysTenured() = delete; - AlwaysTenured(const AlwaysTenured&) = delete; - AlwaysTenured& operator=(const AlwaysTenured&) = delete; + CompilerGCPointer() = delete; + CompilerGCPointer(const CompilerGCPointer&) = delete; + CompilerGCPointer& operator=(const CompilerGCPointer&) = delete; }; -typedef AlwaysTenured AlwaysTenuredObject; -typedef AlwaysTenured AlwaysTenuredNativeObject; -typedef AlwaysTenured AlwaysTenuredFunction; -typedef AlwaysTenured AlwaysTenuredScript; -typedef AlwaysTenured AlwaysTenuredPropertyName; -typedef AlwaysTenured AlwaysTenuredShape; +typedef CompilerGCPointer CompilerObject; +typedef CompilerGCPointer CompilerNativeObject; +typedef CompilerGCPointer CompilerFunction; +typedef CompilerGCPointer CompilerScript; +typedef CompilerGCPointer CompilerPropertyName; +typedef CompilerGCPointer CompilerShape; +typedef CompilerGCPointer CompilerObjectGroup; class MNewArray : public MUnaryInstruction, @@ -2911,8 +2922,6 @@ class MNewArray // Heap where the array should be allocated. gc::InitialHeap initialHeap_; - // Allocate space at initialization or not - AllocatingBehaviour allocating_; // Whether values written to this array should be converted to double first. bool convertDoubleElements_; @@ -2920,18 +2929,16 @@ class MNewArray jsbytecode* pc_; MNewArray(CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst, - gc::InitialHeap initialHeap, AllocatingBehaviour allocating, jsbytecode* pc); + gc::InitialHeap initialHeap, jsbytecode* pc); public: INSTRUCTION_HEADER(NewArray) static MNewArray* New(TempAllocator& alloc, CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst, - gc::InitialHeap initialHeap, AllocatingBehaviour allocating, - jsbytecode* pc) + gc::InitialHeap initialHeap, jsbytecode* pc) { - return new(alloc) MNewArray(constraints, count, templateConst, - initialHeap, allocating, pc); + return new(alloc) MNewArray(constraints, count, templateConst, initialHeap, pc); } uint32_t count() const { @@ -2946,10 +2953,6 @@ class MNewArray return initialHeap_; } - AllocatingBehaviour allocatingBehaviour() const { - return allocating_; - } - jsbytecode* pc() const { return pc_; } @@ -2982,7 +2985,7 @@ class MNewArray class MNewArrayCopyOnWrite : public MNullaryInstruction { - AlwaysTenured templateObject_; + CompilerGCPointer templateObject_; gc::InitialHeap initialHeap_; MNewArrayCopyOnWrite(CompilerConstraintList* constraints, ArrayObject* templateObject, @@ -3023,10 +3026,10 @@ class MNewArrayDynamicLength : public MUnaryInstruction, public IntPolicy<0>::Data { - AlwaysTenured templateObject_; + CompilerObject templateObject_; gc::InitialHeap initialHeap_; - MNewArrayDynamicLength(CompilerConstraintList* constraints, ArrayObject* templateObject, + MNewArrayDynamicLength(CompilerConstraintList* constraints, JSObject* templateObject, gc::InitialHeap initialHeap, MDefinition* length) : MUnaryInstruction(length), templateObject_(templateObject), @@ -3042,7 +3045,7 @@ class MNewArrayDynamicLength INSTRUCTION_HEADER(NewArrayDynamicLength) static MNewArrayDynamicLength* New(TempAllocator& alloc, CompilerConstraintList* constraints, - ArrayObject* templateObject, gc::InitialHeap initialHeap, + JSObject* templateObject, gc::InitialHeap initialHeap, MDefinition* length) { return new(alloc) MNewArrayDynamicLength(constraints, templateObject, initialHeap, length); @@ -3051,7 +3054,7 @@ class MNewArrayDynamicLength MDefinition* length() const { return getOperand(0); } - ArrayObject* templateObject() const { + JSObject* templateObject() const { return templateObject_; } gc::InitialHeap initialHeap() const { @@ -3131,7 +3134,7 @@ class MNewObject class MNewTypedObject : public MNullaryInstruction { - AlwaysTenured templateObject_; + CompilerGCPointer templateObject_; gc::InitialHeap initialHeap_; MNewTypedObject(CompilerConstraintList* constraints, @@ -3206,7 +3209,7 @@ class MSimdBox public NoTypePolicy::Data { protected: - AlwaysTenured templateObject_; + CompilerGCPointer templateObject_; gc::InitialHeap initialHeap_; MSimdBox(CompilerConstraintList* constraints, @@ -3512,7 +3515,7 @@ class MInitProp : public MAryInstruction<2>, public MixPolicy, BoxPolicy<1> >::Data { - AlwaysTenuredPropertyName name_; + CompilerPropertyName name_; protected: MInitProp(MDefinition* obj, PropertyName* name, MDefinition* value) @@ -3552,7 +3555,7 @@ class MInitPropGetterSetter : public MBinaryInstruction, public MixPolicy, ObjectPolicy<1> >::Data { - AlwaysTenuredPropertyName name_; + CompilerPropertyName name_; MInitPropGetterSetter(MDefinition* obj, PropertyName* name, MDefinition* value) : MBinaryInstruction(obj, value), @@ -3654,7 +3657,7 @@ class MCall protected: // Monomorphic cache of single target from TI, or nullptr. - AlwaysTenuredFunction target_; + CompilerFunction target_; // Original value of argc from the bytecode. uint32_t numActualArgs_; @@ -3829,7 +3832,7 @@ class MApplyArgs { protected: // Monomorphic cache of single target from TI, or nullptr. - AlwaysTenuredFunction target_; + CompilerFunction target_; MApplyArgs(JSFunction* target, MDefinition* fun, MDefinition* argc, MDefinition* self) : target_(target) @@ -7142,7 +7145,7 @@ class MDefVar : public MUnaryInstruction, public NoTypePolicy::Data { - AlwaysTenuredPropertyName name_; // Target name to be defined. + CompilerPropertyName name_; // Target name to be defined. unsigned attrs_; // Attributes to be set. private: @@ -7180,7 +7183,7 @@ class MDefFun : public MUnaryInstruction, public NoTypePolicy::Data { - AlwaysTenuredFunction fun_; + CompilerFunction fun_; private: MDefFun(JSFunction* fun, MDefinition* scopeChain) @@ -7208,7 +7211,7 @@ class MDefFun class MRegExp : public MNullaryInstruction { - AlwaysTenured source_; + CompilerGCPointer source_; bool mustClone_; MRegExp(CompilerConstraintList* constraints, RegExpObject* source, bool mustClone) @@ -7464,7 +7467,7 @@ struct LambdaFunctionInfo // The functions used in lambdas are the canonical original function in // the script, and are immutable except for delazification. Record this // information while still on the main thread to avoid races. - AlwaysTenuredFunction fun; + CompilerFunction fun; uint16_t flags; uint16_t nargs; gc::Cell* scriptOrLazyScript; @@ -7949,6 +7952,36 @@ class MIncrementUnboxedArrayInitializedLength ALLOW_CLONE(MIncrementUnboxedArrayInitializedLength) }; +// Set the initialized length of an unboxed array object. +class MSetUnboxedArrayInitializedLength + : public MBinaryInstruction, + public SingleObjectPolicy::Data +{ + explicit MSetUnboxedArrayInitializedLength(MDefinition* obj, MDefinition* length) + : MBinaryInstruction(obj, length) + {} + + public: + INSTRUCTION_HEADER(SetUnboxedArrayInitializedLength) + + static MSetUnboxedArrayInitializedLength* New(TempAllocator& alloc, MDefinition* obj, + MDefinition* length) { + return new(alloc) MSetUnboxedArrayInitializedLength(obj, length); + } + + MDefinition* object() const { + return getOperand(0); + } + MDefinition* length() const { + return getOperand(1); + } + AliasSet getAliasSet() const override { + return AliasSet::Store(AliasSet::ObjectFields); + } + + ALLOW_CLONE(MSetUnboxedArrayInitializedLength) +}; + // Load the array length from an elements header. class MArrayLength : public MUnaryInstruction, @@ -8945,7 +8978,7 @@ class MConvertUnboxedObjectToNative : public MUnaryInstruction, public SingleObjectPolicy::Data { - AlwaysTenured group_; + CompilerObjectGroup group_; explicit MConvertUnboxedObjectToNative(MDefinition* obj, ObjectGroup* group) : MUnaryInstruction(obj), @@ -9092,7 +9125,7 @@ class MArrayConcat : public MBinaryInstruction, public MixPolicy, ObjectPolicy<1> >::Data { - AlwaysTenuredObject templateObj_; + CompilerObject templateObj_; gc::InitialHeap initialHeap_; JSValueType unboxedType_; @@ -9145,7 +9178,7 @@ class MArraySlice : public MTernaryInstruction, public Mix3Policy, IntPolicy<1>, IntPolicy<2>>::Data { - AlwaysTenuredObject templateObj_; + CompilerObject templateObj_; gc::InitialHeap initialHeap_; JSValueType unboxedType_; @@ -9439,7 +9472,8 @@ class MLoadTypedArrayElementStatic setResultType(MIRType_Int32); } - AlwaysTenured someTypedArray_; + CompilerObject someTypedArray_; + // An offset to be encoded in the load instruction - taking advantage of the // addressing modes. This is only non-zero when the access is proven to be // within bounds. @@ -9685,7 +9719,7 @@ class MStoreTypedArrayElementStatic : offset_(offset), needsBoundsCheck_(needsBoundsCheck) {} - AlwaysTenured someTypedArray_; + CompilerObject someTypedArray_; // An offset to be encoded in the store instruction - taking advantage of the // addressing modes. This is only non-zero when the access is proven to be @@ -9905,8 +9939,8 @@ typedef Vector BoolVector; class InlinePropertyTable : public TempObject { struct Entry : public TempObject { - AlwaysTenured group; - AlwaysTenuredFunction func; + CompilerObjectGroup group; + CompilerFunction func; Entry(ObjectGroup* group, JSFunction* func) : group(group), func(func) @@ -9982,7 +10016,7 @@ class MGetPropertyCache : public MUnaryInstruction, public SingleObjectPolicy::Data { - AlwaysTenuredPropertyName name_; + CompilerPropertyName name_; bool idempotent_; bool monitoredResult_; @@ -10086,7 +10120,7 @@ class MGetPropertyPolymorphic }; Vector receivers_; - AlwaysTenuredPropertyName name_; + CompilerPropertyName name_; MGetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, PropertyName* name) : MUnaryInstruction(obj), @@ -10166,7 +10200,7 @@ class MSetPropertyPolymorphic }; Vector receivers_; - AlwaysTenuredPropertyName name_; + CompilerPropertyName name_; bool needsBarrier_; MSetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, MDefinition* value, @@ -10429,8 +10463,8 @@ class MBindNameCache : public MUnaryInstruction, public SingleObjectPolicy::Data { - AlwaysTenuredPropertyName name_; - AlwaysTenuredScript script_; + CompilerPropertyName name_; + CompilerScript script_; jsbytecode* pc_; MBindNameCache(MDefinition* scopeChain, PropertyName* name, JSScript* script, jsbytecode* pc) @@ -10467,7 +10501,7 @@ class MGuardShape : public MUnaryInstruction, public SingleObjectPolicy::Data { - AlwaysTenuredShape shape_; + CompilerShape shape_; BailoutKind bailoutKind_; MGuardShape(MDefinition* obj, Shape* shape, BailoutKind bailoutKind) @@ -10566,7 +10600,7 @@ class MGuardObjectGroup : public MUnaryInstruction, public SingleObjectPolicy::Data { - AlwaysTenured group_; + CompilerObjectGroup group_; bool bailOnEquality_; BailoutKind bailoutKind_; @@ -10583,7 +10617,8 @@ class MGuardObjectGroup // Unboxed groups which might be converted to natives can't be guarded // on, due to MConvertUnboxedObjectToNative. - MOZ_ASSERT_IF(group->maybeUnboxedLayout(), !group->unboxedLayout().nativeGroup()); + MOZ_ASSERT_IF(group->maybeUnboxedLayoutDontCheckGeneration(), + !group->unboxedLayoutDontCheckGeneration().nativeGroup()); } public: @@ -10940,7 +10975,7 @@ class MGetNameCache }; private: - AlwaysTenuredPropertyName name_; + CompilerPropertyName name_; AccessKind kind_; MGetNameCache(MDefinition* obj, PropertyName* name, AccessKind kind) @@ -10972,7 +11007,7 @@ class MGetNameCache class MCallGetIntrinsicValue : public MNullaryInstruction { - AlwaysTenuredPropertyName name_; + CompilerPropertyName name_; explicit MCallGetIntrinsicValue(PropertyName* name) : name_(name) @@ -10999,7 +11034,7 @@ class MCallGetIntrinsicValue : public MNullaryInstruction class MSetPropertyInstruction : public MBinaryInstruction { - AlwaysTenuredPropertyName name_; + CompilerPropertyName name_; bool strict_; bool needsBarrier_; @@ -11061,7 +11096,7 @@ class MDeleteProperty : public MUnaryInstruction, public BoxInputsPolicy::Data { - AlwaysTenuredPropertyName name_; + CompilerPropertyName name_; bool strict_; protected: @@ -11209,7 +11244,7 @@ class MCallGetProperty : public MUnaryInstruction, public BoxInputsPolicy::Data { - AlwaysTenuredPropertyName name_; + CompilerPropertyName name_; bool idempotent_; bool callprop_; @@ -11836,13 +11871,15 @@ class MInArray { bool needsHoleCheck_; bool needsNegativeIntCheck_; + JSValueType unboxedType_; MInArray(MDefinition* elements, MDefinition* index, MDefinition* initLength, MDefinition* object, - bool needsHoleCheck) + bool needsHoleCheck, JSValueType unboxedType) : MQuaternaryInstruction(elements, index, initLength, object), needsHoleCheck_(needsHoleCheck), - needsNegativeIntCheck_(true) + needsNegativeIntCheck_(true), + unboxedType_(unboxedType) { setResultType(MIRType_Boolean); setMovable(); @@ -11856,9 +11893,10 @@ class MInArray static MInArray* New(TempAllocator& alloc, MDefinition* elements, MDefinition* index, MDefinition* initLength, MDefinition* object, - bool needsHoleCheck) + bool needsHoleCheck, JSValueType unboxedType) { - return new(alloc) MInArray(elements, index, initLength, object, needsHoleCheck); + return new(alloc) MInArray(elements, index, initLength, object, needsHoleCheck, + unboxedType); } MDefinition* elements() const { @@ -11879,6 +11917,9 @@ class MInArray bool needsNegativeIntCheck() const { return needsNegativeIntCheck_; } + JSValueType unboxedType() const { + return unboxedType_; + } void collectRangeInfoPreTrunc() override; AliasSet getAliasSet() const override { return AliasSet::Load(AliasSet::Element); @@ -11891,6 +11932,8 @@ class MInArray return false; if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) return false; + if (unboxedType() != other->unboxedType()) + return false; return congruentIfOperandsEqual(other); } }; @@ -11900,7 +11943,7 @@ class MInstanceOf : public MUnaryInstruction, public InstanceOfPolicy::Data { - AlwaysTenuredObject protoObj_; + CompilerObject protoObj_; MInstanceOf(MDefinition* obj, JSObject* proto) : MUnaryInstruction(obj), @@ -12073,7 +12116,7 @@ class MSetFrameArgument class MRestCommon { unsigned numFormals_; - AlwaysTenured templateObject_; + CompilerGCPointer templateObject_; protected: MRestCommon(unsigned numFormals, ArrayObject* templateObject) @@ -12297,7 +12340,7 @@ class MPostWriteBarrier : public MBinaryInstruction, public ObjectPolicy<0>::Dat class MNewDeclEnvObject : public MNullaryInstruction { - AlwaysTenured templateObj_; + CompilerGCPointer templateObj_; explicit MNewDeclEnvObject(DeclEnvObject* templateObj) : MNullaryInstruction(), @@ -12323,7 +12366,7 @@ class MNewDeclEnvObject : public MNullaryInstruction class MNewCallObjectBase : public MNullaryInstruction { - AlwaysTenured templateObj_; + CompilerGCPointer templateObj_; protected: explicit MNewCallObjectBase(CallObject* templateObj) @@ -12378,7 +12421,7 @@ class MNewStringObject : public MUnaryInstruction, public ConvertToStringPolicy<0>::Data { - AlwaysTenuredObject templateObj_; + CompilerObject templateObj_; MNewStringObject(MDefinition* input, JSObject* templateObj) : MUnaryInstruction(input), diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index fa7ce25350..0ff24b5010 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -181,6 +181,7 @@ namespace jit { _(UnboxedArrayLength) \ _(UnboxedArrayInitializedLength) \ _(IncrementUnboxedArrayInitializedLength) \ + _(SetUnboxedArrayInitializedLength) \ _(Not) \ _(BoundsCheck) \ _(BoundsCheckLower) \ diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 445c3f497b..193976e23b 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -880,6 +880,20 @@ template void MacroAssembler::loadUnboxedProperty(BaseIndex address, JSValueType type, TypedOrValueRegister output); +static void +StoreUnboxedFailure(MacroAssembler& masm, Label* failure) +{ + // Storing a value to an unboxed property is a fallible operation and + // the caller must provide a failure label if a particular unboxed store + // might fail. Sometimes, however, a store that cannot succeed (such as + // storing a string to an int32 property) will be marked as infallible. + // This can only happen if the code involved is unreachable. + if (failure) + masm.jump(failure); + else + masm.assumeUnreachable("Incompatible write to unboxed property"); +} + template void MacroAssembler::storeUnboxedProperty(T address, JSValueType type, @@ -891,12 +905,12 @@ MacroAssembler::storeUnboxedProperty(T address, JSValueType type, if (value.value().isBoolean()) store8(Imm32(value.value().toBoolean()), address); else - jump(failure); + StoreUnboxedFailure(*this, failure); } else if (value.reg().hasTyped()) { if (value.reg().type() == MIRType_Boolean) store8(value.reg().typedReg().gpr(), address); else - jump(failure); + StoreUnboxedFailure(*this, failure); } else { if (failure) branchTestBoolean(Assembler::NotEqual, value.reg().valueReg(), failure); @@ -909,12 +923,12 @@ MacroAssembler::storeUnboxedProperty(T address, JSValueType type, if (value.value().isInt32()) store32(Imm32(value.value().toInt32()), address); else - jump(failure); + StoreUnboxedFailure(*this, failure); } else if (value.reg().hasTyped()) { if (value.reg().type() == MIRType_Int32) store32(value.reg().typedReg().gpr(), address); else - jump(failure); + StoreUnboxedFailure(*this, failure); } else { if (failure) branchTestInt32(Assembler::NotEqual, value.reg().valueReg(), failure); @@ -928,7 +942,7 @@ MacroAssembler::storeUnboxedProperty(T address, JSValueType type, loadConstantDouble(value.value().toNumber(), ScratchDoubleReg); storeDouble(ScratchDoubleReg, address); } else { - jump(failure); + StoreUnboxedFailure(*this, failure); } } else if (value.reg().hasTyped()) { if (value.reg().type() == MIRType_Int32) { @@ -937,7 +951,7 @@ MacroAssembler::storeUnboxedProperty(T address, JSValueType type, } else if (value.reg().type() == MIRType_Double) { storeDouble(value.reg().typedReg().fpu(), address); } else { - jump(failure); + StoreUnboxedFailure(*this, failure); } } else { ValueOperand reg = value.reg().valueReg(); @@ -959,13 +973,13 @@ MacroAssembler::storeUnboxedProperty(T address, JSValueType type, if (value.value().isObjectOrNull()) storePtr(ImmGCPtr(value.value().toObjectOrNull()), address); else - jump(failure); + StoreUnboxedFailure(*this, failure); } else if (value.reg().hasTyped()) { MOZ_ASSERT(value.reg().type() != MIRType_Null); if (value.reg().type() == MIRType_Object) storePtr(value.reg().typedReg().gpr(), address); else - jump(failure); + StoreUnboxedFailure(*this, failure); } else { if (failure) { Label ok; @@ -982,12 +996,12 @@ MacroAssembler::storeUnboxedProperty(T address, JSValueType type, if (value.value().isString()) storePtr(ImmGCPtr(value.value().toString()), address); else - jump(failure); + StoreUnboxedFailure(*this, failure); } else if (value.reg().hasTyped()) { if (value.reg().type() == MIRType_String) storePtr(value.reg().typedReg().gpr(), address); else - jump(failure); + StoreUnboxedFailure(*this, failure); } else { if (failure) branchTestString(Assembler::NotEqual, value.reg().valueReg(), failure); diff --git a/js/src/jit/MoveResolver.cpp b/js/src/jit/MoveResolver.cpp index 8b607869d7..2945b0e673 100644 --- a/js/src/jit/MoveResolver.cpp +++ b/js/src/jit/MoveResolver.cpp @@ -201,14 +201,90 @@ MoveResolver::addOrderedMove(const MoveOp& move) } } - if (existing.to().aliases(move.from()) || - existing.to().aliases(move.to()) || - existing.from().aliases(move.to()) || - existing.from().aliases(move.from())) - { + if (existing.aliases(move)) break; - } } return orderedMoves_.append(move); } + +void +MoveResolver::reorderMove(size_t from, size_t to) +{ + MOZ_ASSERT(from != to); + + MoveOp op = orderedMoves_[from]; + if (from < to) { + for (size_t i = from; i < to; i++) + orderedMoves_[i] = orderedMoves_[i + 1]; + } else { + for (size_t i = from; i > to; i--) + orderedMoves_[i] = orderedMoves_[i - 1]; + } + orderedMoves_[to] = op; +} + +void +MoveResolver::sortMemoryToMemoryMoves() +{ + // Try to reorder memory->memory moves so that they are executed right + // before a move that clobbers some register. This will allow the move + // emitter to use that clobbered register as a scratch register for the + // memory->memory move, if necessary. + for (size_t i = 0; i < orderedMoves_.length(); i++) { + const MoveOp& base = orderedMoves_[i]; + if (!base.from().isMemory() || !base.to().isMemory()) + continue; + if (base.type() != MoveOp::GENERAL && base.type() != MoveOp::INT32) + continue; + + // Look for an earlier move clobbering a register. + bool found = false; + for (int j = i - 1; j >= 0; j--) { + const MoveOp& previous = orderedMoves_[j]; + if (previous.aliases(base) || previous.isCycleBegin() || previous.isCycleEnd()) + break; + + if (previous.to().isGeneralReg()) { + reorderMove(i, j); + found = true; + break; + } + } + if (found) + continue; + + // Look for a later move clobbering a register. + if (i + 1 < orderedMoves_.length()) { + bool found = false, skippedRegisterUse = false; + for (size_t j = i + 1; j < orderedMoves_.length(); j++) { + const MoveOp& later = orderedMoves_[j]; + if (later.aliases(base) || later.isCycleBegin() || later.isCycleEnd()) + break; + + if (later.to().isGeneralReg()) { + if (skippedRegisterUse) { + reorderMove(i, j); + found = true; + } else { + // There is no move that uses a register between the + // original memory->memory move and this move that + // clobbers a register. The move should already be able + // to use a scratch register, so don't shift anything + // around. + } + break; + } + + if (later.from().isGeneralReg()) + skippedRegisterUse = true; + } + + if (found) { + // Redo the search for memory->memory moves at the current + // index, so we don't skip the move just shifted back. + i--; + } + } + } +} diff --git a/js/src/jit/MoveResolver.h b/js/src/jit/MoveResolver.h index 5afbe5fda8..cd1ac00aac 100644 --- a/js/src/jit/MoveResolver.h +++ b/js/src/jit/MoveResolver.h @@ -197,6 +197,12 @@ class MoveOp MOZ_ASSERT(isCycleBegin()); return endCycleType_; } + bool aliases(const MoveOperand& op) const { + return from().aliases(op) || to().aliases(op); + } + bool aliases(const MoveOp& other) const { + return aliases(other.from()) || aliases(other.to()); + } }; class MoveResolver @@ -229,8 +235,6 @@ class MoveResolver typedef InlineList::iterator PendingMoveIterator; private: - // Moves that are definitely unblocked (constants to registers). These are - // emitted last. js::Vector orderedMoves_; int numCycles_; int curCycles_; @@ -241,6 +245,7 @@ class MoveResolver PendingMove* findBlockingMove(const PendingMove* last); PendingMove* findCycledMove(PendingMoveIterator* stack, PendingMoveIterator end, const PendingMove* first); bool addOrderedMove(const MoveOp& move); + void reorderMove(size_t from, size_t to); // Internal reset function. Does not clear lists. void resetState(); @@ -257,6 +262,7 @@ class MoveResolver // cycle resolution algorithm. Calling addMove() again resets the resolver. bool addMove(const MoveOperand& from, const MoveOperand& to, MoveOp::Type type); bool resolve(); + void sortMemoryToMemoryMoves(); size_t numMoves() const { return orderedMoves_.length(); diff --git a/js/src/jit/OptimizationTracking.cpp b/js/src/jit/OptimizationTracking.cpp index 763a25987c..2e27ca3a09 100644 --- a/js/src/jit/OptimizationTracking.cpp +++ b/js/src/jit/OptimizationTracking.cpp @@ -16,6 +16,9 @@ #include "jit/JitSpewer.h" #include "js/TrackedOptimizationInfo.h" +#include "vm/ObjectGroup-inl.h" +#include "vm/TypeInference-inl.h" + using namespace js; using namespace js::jit; diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp index bdfe101fe3..d13c8963e7 100644 --- a/js/src/jit/Recover.cpp +++ b/js/src/jit/Recover.cpp @@ -1197,14 +1197,12 @@ MNewArray::writeRecoverData(CompactBufferWriter& writer) const MOZ_ASSERT(canRecoverOnBailout()); writer.writeUnsigned(uint32_t(RInstruction::Recover_NewArray)); writer.writeUnsigned(count()); - writer.writeByte(uint8_t(allocatingBehaviour())); return true; } RNewArray::RNewArray(CompactBufferReader& reader) { count_ = reader.readUnsigned(); - allocatingBehaviour_ = AllocatingBehaviour(reader.readByte()); } bool @@ -1212,9 +1210,9 @@ RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const { RootedObject templateObject(cx, &iter.read().toObject()); RootedValue result(cx); - RootedObjectGroup group(cx); + RootedObjectGroup group(cx, templateObject->group()); - JSObject* resultObject = NewDenseArray(cx, count_, group, allocatingBehaviour_); + JSObject* resultObject = NewFullyAllocatedArrayTryUseGroup(cx, group, count_); if (!resultObject) return false; diff --git a/js/src/jit/Recover.h b/js/src/jit/Recover.h index e50487d7d6..ecd400d38d 100644 --- a/js/src/jit/Recover.h +++ b/js/src/jit/Recover.h @@ -635,7 +635,6 @@ class RNewArray final : public RInstruction { private: uint32_t count_; - AllocatingBehaviour allocatingBehaviour_; public: RINSTRUCTION_HEADER_(NewArray) diff --git a/js/src/jit/ScalarReplacement.cpp b/js/src/jit/ScalarReplacement.cpp index 0aaf282125..d1371b811d 100644 --- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -568,13 +568,6 @@ IsArrayEscaped(MInstruction* ins) MOZ_ASSERT(ins->isNewArray()); uint32_t count = ins->toNewArray()->count(); - // The array is probably too large to be represented efficiently with - // MArrayState, and we do not want to make huge allocations during bailouts. - if (ins->toNewArray()->allocatingBehaviour() == NewArray_Unallocating) { - JitSpewDef(JitSpew_Escape, "Array is not allocated\n", ins); - return true; - } - JSObject* obj = ins->toNewArray()->templateObject(); if (!obj || obj->is()) return true; @@ -1037,11 +1030,12 @@ ArrayMemoryView::visitArrayLength(MArrayLength* ins) } bool -ScalarReplacement(MIRGenerator* mir, MIRGraph& graph) +ScalarReplacement(MIRGenerator* mir, MIRGraph& graph, bool* success) { EmulateStateOf replaceObject(mir, graph); EmulateStateOf replaceArray(mir, graph); bool addedPhi = false; + *success = false; for (ReversePostorderIterator block = graph.rpoBegin(); block != graph.rpoEnd(); block++) { if (mir->shouldCancel("Scalar Replacement (main loop)")) @@ -1055,6 +1049,7 @@ ScalarReplacement(MIRGenerator* mir, MIRGraph& graph) if (!replaceObject.run(view)) return false; view.assertSuccess(); + *success = true; addedPhi = true; continue; } @@ -1064,6 +1059,7 @@ ScalarReplacement(MIRGenerator* mir, MIRGraph& graph) if (!replaceArray.run(view)) return false; view.assertSuccess(); + *success = true; addedPhi = true; continue; } diff --git a/js/src/jit/ScalarReplacement.h b/js/src/jit/ScalarReplacement.h index 836367f0f0..1869471a31 100644 --- a/js/src/jit/ScalarReplacement.h +++ b/js/src/jit/ScalarReplacement.h @@ -15,7 +15,7 @@ class MIRGenerator; class MIRGraph; bool -ScalarReplacement(MIRGenerator* mir, MIRGraph& graph); +ScalarReplacement(MIRGenerator* mir, MIRGraph& graph, bool* success); } // namespace jit } // namespace js diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index c8b2553b54..6e5003287d 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -289,7 +289,8 @@ ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length) { *length = GetAnyBoxedOrUnboxedArrayLength(obj); DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, *length, v.address(), 1, DontUpdateTypes); + SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, *length, v.address(), 1, + ShouldUpdateTypes::DontUpdate); if (result != DenseElementResult::Incomplete) { (*length)++; return result == DenseElementResult::Success; @@ -1085,7 +1086,7 @@ SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index, DenseElementResult result = SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, index, value.address(), 1, - DontUpdateTypes); + ShouldUpdateTypes::DontUpdate); if (result != DenseElementResult::Incomplete) return result == DenseElementResult::Success; diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index fc4b7f5381..ec52b3eaa6 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -10,6 +10,7 @@ #include "jscntxt.h" #include "jscompartment.h" +#include "jsmath.h" #include "jsnum.h" #include "jit/CodeGenerator.h" @@ -2320,3 +2321,18 @@ CodeGeneratorARM::visitMemoryBarrier(LMemoryBarrier* ins) { memoryBarrier(ins->type()); } + +void +CodeGeneratorARM::visitRandom(LRandom* ins) +{ + Register temp = ToRegister(ins->temp()); + Register temp2 = ToRegister(ins->temp2()); + + masm.loadJSContext(temp); + + masm.setupUnalignedABICall(1, temp2); + masm.passABIArg(temp); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE); + + MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg); +} diff --git a/js/src/jit/arm/CodeGenerator-arm.h b/js/src/jit/arm/CodeGenerator-arm.h index e3aea5e0c1..5160a99862 100644 --- a/js/src/jit/arm/CodeGenerator-arm.h +++ b/js/src/jit/arm/CodeGenerator-arm.h @@ -216,6 +216,8 @@ class CodeGeneratorARM : public CodeGeneratorShared void generateInvalidateEpilogue(); + void visitRandom(LRandom* ins); + protected: void visitEffectiveAddress(LEffectiveAddress* ins); void visitUDiv(LUDiv* ins); diff --git a/js/src/jit/arm/LIR-arm.h b/js/src/jit/arm/LIR-arm.h index b47b29ba38..2589d327ba 100644 --- a/js/src/jit/arm/LIR-arm.h +++ b/js/src/jit/arm/LIR-arm.h @@ -512,6 +512,23 @@ class LAsmJSAtomicBinopCallout : public LInstructionHelper<1, 2, 0> } }; +// Math.random(). +class LRandom : public LCallInstructionHelper<1, 0, 2> +{ + public: + LIR_HEADER(Random) + LRandom(const LDefinition& temp, const LDefinition& temp2) { + setTemp(0, temp); + setTemp(1, temp2); + } + const LDefinition* temp() { + return getTemp(0); + } + const LDefinition* temp2() { + return getTemp(1); + } +}; + } // namespace jit } // namespace js diff --git a/js/src/jit/arm/Lowering-arm.cpp b/js/src/jit/arm/Lowering-arm.cpp index 8c799ed177..52abf792b0 100644 --- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -715,3 +715,10 @@ LIRGeneratorARM::visitSubstr(MSubstr* ins) define(lir, ins); assignSafepoint(lir, ins); } + +void +LIRGeneratorARM::visitRandom(MRandom* ins) +{ + LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1)); + defineReturn(lir, ins); +} diff --git a/js/src/jit/arm/Lowering-arm.h b/js/src/jit/arm/Lowering-arm.h index 5bfaaca15b..426fc07c87 100644 --- a/js/src/jit/arm/Lowering-arm.h +++ b/js/src/jit/arm/Lowering-arm.h @@ -105,6 +105,7 @@ class LIRGeneratorARM : public LIRGeneratorShared void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins); void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins); void visitSubstr(MSubstr* ins); + void visitRandom(MRandom* ins); }; typedef LIRGeneratorARM LIRGeneratorSpecific; diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index cc0437d8d2..05d9324f28 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -1992,6 +1992,12 @@ MacroAssemblerARMCompat::or32(Imm32 imm, Register dest) ma_orr(imm, dest); } +void +MacroAssemblerARMCompat::or32(Register src, Register dest) +{ + ma_orr(src, dest); +} + void MacroAssemblerARMCompat::xorPtr(Imm32 imm, Register dest) { diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index d66932b26a..bb01421241 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -1271,6 +1271,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void and32(Imm32 imm, Register dest); void and32(Imm32 imm, const Address& dest); void and32(const Address& src, Register dest); + void or32(Register src, Register dest); void or32(Imm32 imm, Register dest); void or32(Imm32 imm, const Address& dest); void xorPtr(Imm32 imm, Register dest); diff --git a/js/src/jit/mips/CodeGenerator-mips.cpp b/js/src/jit/mips/CodeGenerator-mips.cpp index f261cb30ac..9586cc8c34 100644 --- a/js/src/jit/mips/CodeGenerator-mips.cpp +++ b/js/src/jit/mips/CodeGenerator-mips.cpp @@ -10,6 +10,7 @@ #include "jscntxt.h" #include "jscompartment.h" +#include "jsmath.h" #include "jsnum.h" #include "jit/CodeGenerator.h" @@ -2174,3 +2175,18 @@ CodeGeneratorMIPS::visitNegF(LNegF* ins) masm.as_negs(output, input); } + +void +CodeGeneratorMIPS::visitRandom(LRandom* ins) +{ + Register temp = ToRegister(ins->temp()); + Register temp2 = ToRegister(ins->temp2()); + + masm.loadJSContext(temp); + + masm.setupUnalignedABICall(1, temp2); + masm.passABIArg(temp); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE); + + MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg); +} diff --git a/js/src/jit/mips/CodeGenerator-mips.h b/js/src/jit/mips/CodeGenerator-mips.h index 85058d486e..817795a7d2 100644 --- a/js/src/jit/mips/CodeGenerator-mips.h +++ b/js/src/jit/mips/CodeGenerator-mips.h @@ -245,6 +245,8 @@ class CodeGeneratorMIPS : public CodeGeneratorShared void generateInvalidateEpilogue(); + void visitRandom(LRandom* ins); + protected: void visitEffectiveAddress(LEffectiveAddress* ins); void visitUDiv(LUDiv* ins); diff --git a/js/src/jit/mips/LIR-mips.h b/js/src/jit/mips/LIR-mips.h index 9465ef53ce..0b71cb3626 100644 --- a/js/src/jit/mips/LIR-mips.h +++ b/js/src/jit/mips/LIR-mips.h @@ -390,6 +390,23 @@ class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 0> } }; +// Math.random(). +class LRandom : public LCallInstructionHelper<1, 0, 2> +{ + public: + LIR_HEADER(Random) + LRandom(const LDefinition& temp, const LDefinition& temp2) { + setTemp(0, temp); + setTemp(1, temp2); + } + const LDefinition* temp() { + return getTemp(0); + } + const LDefinition* temp2() { + return getTemp(1); + } +}; + } // namespace jit } // namespace js diff --git a/js/src/jit/mips/Lowering-mips.cpp b/js/src/jit/mips/Lowering-mips.cpp index 5b0dd58bc1..88c3a88205 100644 --- a/js/src/jit/mips/Lowering-mips.cpp +++ b/js/src/jit/mips/Lowering-mips.cpp @@ -584,3 +584,10 @@ LIRGeneratorMIPS::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBino { MOZ_CRASH("NYI"); } + +void +LIRGeneratorMIPS::visitRandom(MRandom* ins) +{ + LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1)); + defineReturn(lir, ins); +} diff --git a/js/src/jit/mips/Lowering-mips.h b/js/src/jit/mips/Lowering-mips.h index 92614292ee..5d4f94842a 100644 --- a/js/src/jit/mips/Lowering-mips.h +++ b/js/src/jit/mips/Lowering-mips.h @@ -105,6 +105,7 @@ class LIRGeneratorMIPS : public LIRGeneratorShared void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins); void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins); void visitSubstr(MSubstr* ins); + void visitRandom(MRandom* ins); }; typedef LIRGeneratorMIPS LIRGeneratorSpecific; diff --git a/js/src/jit/mips/MacroAssembler-mips.cpp b/js/src/jit/mips/MacroAssembler-mips.cpp index 7be3671404..767879007f 100644 --- a/js/src/jit/mips/MacroAssembler-mips.cpp +++ b/js/src/jit/mips/MacroAssembler-mips.cpp @@ -1666,6 +1666,12 @@ MacroAssemblerMIPSCompat::or32(Imm32 imm, const Address& dest) store32(SecondScratchReg, dest); } +void +MacroAssemblerMIPSCompat::or32(Register src, Register dest) +{ + ma_or(dest, src); +} + void MacroAssemblerMIPSCompat::xor32(Imm32 imm, Register dest) { diff --git a/js/src/jit/mips/MacroAssembler-mips.h b/js/src/jit/mips/MacroAssembler-mips.h index ee1763a9a5..47438dba63 100644 --- a/js/src/jit/mips/MacroAssembler-mips.h +++ b/js/src/jit/mips/MacroAssembler-mips.h @@ -1197,6 +1197,7 @@ public: void and32(const Address& src, Register dest); void or32(Imm32 imm, Register dest); void or32(Imm32 imm, const Address& dest); + void or32(Register src, Register dest); void xor32(Imm32 imm, Register dest); void xorPtr(Imm32 imm, Register dest); void xorPtr(Register src, Register dest); diff --git a/js/src/jit/shared/CodeGenerator-shared.h b/js/src/jit/shared/CodeGenerator-shared.h index e7365c1cab..4967d8c0aa 100644 --- a/js/src/jit/shared/CodeGenerator-shared.h +++ b/js/src/jit/shared/CodeGenerator-shared.h @@ -8,6 +8,7 @@ #define jit_shared_CodeGenerator_shared_h #include "mozilla/Alignment.h" +#include "mozilla/Move.h" #include "mozilla/TypeTraits.h" #include "jit/JitFrames.h" @@ -577,69 +578,57 @@ class OutOfLineCodeBase : public OutOfLineCode // ArgSeq store arguments for OutOfLineCallVM. // // OutOfLineCallVM are created with "oolCallVM" function. The third argument of -// this function is an instance of a class which provides a "generate" function -// to call the "pushArg" needed by the VMFunction call. The list of argument -// can be created by using the ArgList function which create an empty list of -// arguments. Arguments are added to this list by using the comma operator. -// The type of the argument list is returned by the comma operator, and due to -// templates arguments, it is quite painful to write by hand. It is recommended -// to use it directly as argument of a template function which would get its -// arguments infered by the compiler (such as oolCallVM). The list of arguments -// must be written in the same order as if you were calling the function in C++. +// this function is an instance of a class which provides a "generate" in charge +// of pushing the argument, with "pushArg", for a VMFunction. +// +// Such list of arguments can be created by using the "ArgList" function which +// creates one instance of "ArgSeq", where the type of the arguments are inferred +// from the type of the arguments. +// +// The list of arguments must be written in the same order as if you were +// calling the function in C++. // // Example: -// (ArgList(), ToRegister(lir->lhs()), ToRegister(lir->rhs())) +// ArgList(ToRegister(lir->lhs()), ToRegister(lir->rhs())) -template -class ArgSeq : public SeqType -{ - private: - typedef ArgSeq ThisType; - LastType last_; +template +class ArgSeq; - public: - ArgSeq(const SeqType& seq, const LastType& last) - : SeqType(seq), - last_(last) - { } - - template - inline ArgSeq - operator, (const NextType& last) const { - return ArgSeq(*this, last); - } - - inline void generate(CodeGeneratorShared *codegen) const { - codegen->pushArg(last_); - this->SeqType::generate(codegen); - } -}; - -// Mark the end of an argument list. template <> -class ArgSeq +class ArgSeq<> { - private: - typedef ArgSeq ThisType; - public: ArgSeq() { } - ArgSeq(const ThisType&) { } - template - inline ArgSeq - operator, (const NextType& last) const { - return ArgSeq(*this, last); - } - - inline void generate(CodeGeneratorShared *codegen) const { + inline void generate(CodeGeneratorShared* codegen) const { } }; -inline ArgSeq -ArgList() +template +class ArgSeq : public ArgSeq { - return ArgSeq(); + private: + HeadType head_; + + public: + explicit ArgSeq(HeadType&& head, TailTypes&&... tail) + : ArgSeq(mozilla::Move(tail)...), + head_(mozilla::Move(head)) + { } + + // Arguments are pushed in reverse order, from last argument to first + // argument. + inline void generate(CodeGeneratorShared* codegen) const { + this->ArgSeq::generate(codegen); + codegen->pushArg(head_); + } +}; + +template +inline ArgSeq +ArgList(ArgTypes... args) +{ + return ArgSeq(mozilla::Move(args)...); } // Store wrappers, to generate the right move of data after the VM call. diff --git a/js/src/jit/x64/Assembler-x64.h b/js/src/jit/x64/Assembler-x64.h index 74cfb3a0e9..d2445140d4 100644 --- a/js/src/jit/x64/Assembler-x64.h +++ b/js/src/jit/x64/Assembler-x64.h @@ -565,6 +565,13 @@ class Assembler : public AssemblerX86Shared masm.xorq_ir(imm.value, dest.encoding()); } + void imulq(Register src, Register dest) { + masm.imulq_rr(src.encoding(), dest.encoding()); + } + void vcvtsi2sdq(Register src, FloatRegister dest) { + masm.vcvtsi2sdq_rr(src.encoding(), dest.encoding()); + } + void mov(ImmWord word, Register dest) { // Use xor for setting registers to zero, as it is specially optimized // for this purpose on modern hardware. Note that it does clobber FLAGS diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index a8a60ad395..fafa9751f1 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -819,3 +819,129 @@ CodeGeneratorX64::visitTruncateFToInt32(LTruncateFToInt32* ins) // call a stub if it fails. emitTruncateFloat32(input, output, ins->mir()); } + +namespace js { +namespace jit { + +// Out-of-line math_random_no_outparam call for LRandom. +class OutOfLineRandom : public OutOfLineCodeBase +{ + LRandom* lir_; + + public: + explicit OutOfLineRandom(LRandom* lir) + : lir_(lir) + { } + + void accept(CodeGeneratorX64* codegen) { + codegen->visitOutOfLineRandom(this); + } + + LRandom* lir() const { + return lir_; + } +}; + +} // namespace jit +} // namespace js + +static const double RNG_DSCALE_INV = 1 / RNG_DSCALE; + +void +CodeGeneratorX64::visitRandom(LRandom* ins) +{ + FloatRegister output = ToFloatRegister(ins->output()); + + Register JSCompartmentReg = ToRegister(ins->temp()); + Register rngStateReg = ToRegister(ins->temp2()); + Register highReg = ToRegister(ins->temp3()); + Register lowReg = ToRegister(ins->temp4()); + Register rngMaskReg = ToRegister(ins->temp5()); + + // rngState = cx->compartment()->rngState + masm.loadJSContext(JSCompartmentReg); + masm.loadPtr(Address(JSCompartmentReg, JSContext::offsetOfCompartment()), JSCompartmentReg); + masm.loadPtr(Address(JSCompartmentReg, JSCompartment::offsetOfRngState()), rngStateReg); + + // if rngState == 0, escape from inlined code and call + // math_random_no_outparam. + OutOfLineRandom* ool = new(alloc()) OutOfLineRandom(ins); + addOutOfLineCode(ool, ins->mir()); + masm.branchTestPtr(Assembler::Zero, rngStateReg, rngStateReg, ool->entry()); + + // nextstate = rngState * RNG_MULTIPLIER; + Register& rngMultiplierReg = lowReg; + masm.movq(ImmWord(RNG_MULTIPLIER), rngMultiplierReg); + masm.imulq(rngMultiplierReg, rngStateReg); + + // nextstate += RNG_ADDEND; + masm.addq(Imm32(RNG_ADDEND), rngStateReg); + + // nextstate &= RNG_MASK; + masm.movq(ImmWord(RNG_MASK), rngMaskReg); + masm.andq(rngMaskReg, rngStateReg); + + // rngState = nextstate + + // if rngState == 0, escape from inlined code and call + // math_random_no_outparam. + masm.j(Assembler::Zero, ool->entry()); + + // high = (nextstate >> (RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS; + masm.movq(rngStateReg, highReg); + masm.shrq(Imm32(RNG_STATE_WIDTH - RNG_HIGH_BITS), highReg); + masm.shlq(Imm32(RNG_LOW_BITS), highReg); + + // nextstate = rngState * RNG_MULTIPLIER; + masm.imulq(rngMultiplierReg, rngStateReg); + + // nextstate += RNG_ADDEND; + masm.addq(Imm32(RNG_ADDEND), rngStateReg); + + // nextstate &= RNG_MASK; + masm.andq(rngMaskReg, rngStateReg); + + // low = nextstate >> (RNG_STATE_WIDTH - RNG_LOW_BITS); + masm.movq(rngStateReg, lowReg); + masm.shrq(Imm32(RNG_STATE_WIDTH - RNG_LOW_BITS), lowReg); + + // output = double(high | low); + masm.orq(highReg, lowReg); + masm.vcvtsi2sdq(lowReg, output); + + // output = output * RNG_DSCALE_INV; + Register& rngDscaleInvReg = lowReg; + masm.movq(ImmPtr(&RNG_DSCALE_INV), rngDscaleInvReg); + masm.vmulsd(Operand(rngDscaleInvReg, 0), output, output); + + // cx->compartment()->rngState = nextstate + masm.storePtr(rngStateReg, Address(JSCompartmentReg, JSCompartment::offsetOfRngState())); + + masm.bind(ool->rejoin()); +} + +void +CodeGeneratorX64::visitOutOfLineRandom(OutOfLineRandom* ool) +{ + LRandom* ins = ool->lir(); + Register temp = ToRegister(ins->temp()); + Register temp2 = ToRegister(ins->temp2()); + MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg); + + LiveRegisterSet regs; + regs.add(ReturnFloat32Reg); + regs.add(ReturnDoubleReg); + regs.add(ReturnInt32x4Reg); + regs.add(ReturnFloat32x4Reg); + saveVolatile(regs); + + masm.loadJSContext(temp); + + masm.setupUnalignedABICall(1, temp2); + masm.passABIArg(temp); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE); + + restoreVolatile(regs); + + masm.jump(ool->rejoin()); +} diff --git a/js/src/jit/x64/CodeGenerator-x64.h b/js/src/jit/x64/CodeGenerator-x64.h index d9feb99534..218b414726 100644 --- a/js/src/jit/x64/CodeGenerator-x64.h +++ b/js/src/jit/x64/CodeGenerator-x64.h @@ -12,6 +12,8 @@ namespace js { namespace jit { +class OutOfLineRandom; + class CodeGeneratorX64 : public CodeGeneratorX86Shared { CodeGeneratorX64* thisFromCtor() { @@ -58,6 +60,8 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins); void visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir); void visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir); + void visitRandom(LRandom* ins); + void visitOutOfLineRandom(OutOfLineRandom* ool); }; typedef CodeGeneratorX64 CodeGeneratorSpecific; diff --git a/js/src/jit/x64/LIR-x64.h b/js/src/jit/x64/LIR-x64.h index acb3edd4a7..6940b898a1 100644 --- a/js/src/jit/x64/LIR-x64.h +++ b/js/src/jit/x64/LIR-x64.h @@ -121,6 +121,41 @@ class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 1> } }; +// Math.random(). +class LRandom : public LInstructionHelper<1, 0, 5> +{ + public: + LIR_HEADER(Random) + LRandom(const LDefinition &temp, const LDefinition &temp2, const LDefinition &temp3, + const LDefinition &temp4, const LDefinition &temp5) + { + setTemp(0, temp); + setTemp(1, temp2); + setTemp(2, temp3); + setTemp(3, temp4); + setTemp(4, temp5); + } + const LDefinition* temp() { + return getTemp(0); + } + const LDefinition* temp2() { + return getTemp(1); + } + const LDefinition *temp3() { + return getTemp(2); + } + const LDefinition *temp4() { + return getTemp(3); + } + const LDefinition *temp5() { + return getTemp(4); + } + + MRandom* mir() const { + return mir_->toRandom(); + } +}; + } // namespace jit } // namespace js diff --git a/js/src/jit/x64/Lowering-x64.cpp b/js/src/jit/x64/Lowering-x64.cpp index 457aa09887..c7bcb27a09 100644 --- a/js/src/jit/x64/Lowering-x64.cpp +++ b/js/src/jit/x64/Lowering-x64.cpp @@ -295,3 +295,14 @@ LIRGeneratorX64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic { MOZ_CRASH("NYI"); } + +void +LIRGeneratorX64::visitRandom(MRandom* ins) +{ + LRandom *lir = new(alloc()) LRandom(temp(), + temp(), + temp(), + temp(), + temp()); + defineFixed(lir, ins, LFloatReg(ReturnDoubleReg)); +} diff --git a/js/src/jit/x64/Lowering-x64.h b/js/src/jit/x64/Lowering-x64.h index 225f7abe93..ca3c2b68a1 100644 --- a/js/src/jit/x64/Lowering-x64.h +++ b/js/src/jit/x64/Lowering-x64.h @@ -51,6 +51,7 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins); void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins); void visitSubstr(MSubstr* ins); + void visitRandom(MRandom* ins); }; typedef LIRGeneratorX64 LIRGeneratorSpecific; diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h index bb48c9df20..ba4e8e6a5b 100644 --- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h @@ -1251,6 +1251,14 @@ public: } } +#ifdef JS_CODEGEN_X64 + void imulq_rr(RegisterID src, RegisterID dst) + { + spew("imulq %s, %s", GPReg64Name(src), GPReg64Name(dst)); + m_formatter.twoByteOp64(OP2_IMUL_GvEv, src, dst); + } +#endif + void idivl_r(RegisterID divisor) { spew("idivl %s", GPReg32Name(divisor)); @@ -2711,6 +2719,13 @@ public: } #endif +#ifdef JS_CODEGEN_X64 + void vcvtsi2sdq_rr(RegisterID src, XMMRegisterID dst) + { + twoByteOpInt64Simd("vcvtsi2sdq", VEX_SD, OP2_CVTSI2SD_VsdEd, src, invalid_xmm, dst); + } +#endif + void vcvttsd2si_rr(XMMRegisterID src, RegisterID dst) { twoByteOpSimdInt32("vcvttsd2si", VEX_SD, OP2_CVTTSD2SI_GdWsd, src, dst); diff --git a/js/src/jit/x86-shared/MoveEmitter-x86-shared.cpp b/js/src/jit/x86-shared/MoveEmitter-x86-shared.cpp index e48bfed9b0..4a93bc1d3b 100644 --- a/js/src/jit/x86-shared/MoveEmitter-x86-shared.cpp +++ b/js/src/jit/x86-shared/MoveEmitter-x86-shared.cpp @@ -11,6 +11,8 @@ using namespace js; using namespace js::jit; +using mozilla::Maybe; + MoveEmitterX86::MoveEmitterX86(MacroAssembler& masm) : inCycle_(false), masm(masm), @@ -101,11 +103,19 @@ MoveEmitterX86::emit(const MoveResolver& moves) { #if defined(JS_CODEGEN_X86) && defined(DEBUG) // Clobber any scratch register we have, to make regalloc bugs more visible. - if (hasScratchRegister()) - masm.mov(ImmWord(0xdeadbeef), scratchRegister()); + if (scratchRegister_.isSome()) + masm.mov(ImmWord(0xdeadbeef), scratchRegister_.value()); #endif for (size_t i = 0; i < moves.numMoves(); i++) { +#if defined(JS_CODEGEN_X86) && defined(DEBUG) + if (!scratchRegister_.isSome()) { + Maybe reg = findScratchRegister(moves, i); + if (reg.isSome()) + masm.mov(ImmWord(0xdeadbeef), reg.value()); + } +#endif + const MoveOp& move = moves.getMove(i); const MoveOperand& from = move.from(); const MoveOperand& to = move.to(); @@ -144,10 +154,10 @@ MoveEmitterX86::emit(const MoveResolver& moves) emitDoubleMove(from, to); break; case MoveOp::INT32: - emitInt32Move(from, to); + emitInt32Move(from, to, moves, i); break; case MoveOp::GENERAL: - emitGeneralMove(from, to); + emitGeneralMove(from, to, moves, i); break; case MoveOp::INT32X4: emitInt32X4Move(from, to); @@ -363,7 +373,8 @@ MoveEmitterX86::completeCycle(const MoveOperand& to, MoveOp::Type type) } void -MoveEmitterX86::emitInt32Move(const MoveOperand& from, const MoveOperand& to) +MoveEmitterX86::emitInt32Move(const MoveOperand& from, const MoveOperand& to, + const MoveResolver& moves, size_t i) { if (from.isGeneralReg()) { masm.move32(from.reg(), toOperand(to)); @@ -373,10 +384,10 @@ MoveEmitterX86::emitInt32Move(const MoveOperand& from, const MoveOperand& to) } else { // Memory to memory gpr move. MOZ_ASSERT(from.isMemory()); - if (hasScratchRegister()) { - Register reg = scratchRegister(); - masm.load32(toAddress(from), reg); - masm.move32(reg, toOperand(to)); + Maybe reg = findScratchRegister(moves, i); + if (reg.isSome()) { + masm.load32(toAddress(from), reg.value()); + masm.move32(reg.value(), toOperand(to)); } else { // No scratch register available; bounce it off the stack. masm.Push(toOperand(from)); @@ -386,7 +397,8 @@ MoveEmitterX86::emitInt32Move(const MoveOperand& from, const MoveOperand& to) } void -MoveEmitterX86::emitGeneralMove(const MoveOperand& from, const MoveOperand& to) +MoveEmitterX86::emitGeneralMove(const MoveOperand& from, const MoveOperand& to, + const MoveResolver& moves, size_t i) { if (from.isGeneralReg()) { masm.mov(from.reg(), toOperand(to)); @@ -398,10 +410,10 @@ MoveEmitterX86::emitGeneralMove(const MoveOperand& from, const MoveOperand& to) masm.lea(toOperand(from), to.reg()); } else if (from.isMemory()) { // Memory to memory gpr move. - if (hasScratchRegister()) { - Register reg = scratchRegister(); - masm.loadPtr(toAddress(from), reg); - masm.mov(reg, toOperand(to)); + Maybe reg = findScratchRegister(moves, i); + if (reg.isSome()) { + masm.loadPtr(toAddress(from), reg.value()); + masm.mov(reg.value(), toOperand(to)); } else { // No scratch register available; bounce it off the stack. masm.Push(toOperand(from)); @@ -410,10 +422,10 @@ MoveEmitterX86::emitGeneralMove(const MoveOperand& from, const MoveOperand& to) } else { // Effective address to memory move. MOZ_ASSERT(from.isEffectiveAddress()); - if (hasScratchRegister()) { - Register reg = scratchRegister(); - masm.lea(toOperand(from), reg); - masm.mov(reg, toOperand(to)); + Maybe reg = findScratchRegister(moves, i); + if (reg.isSome()) { + masm.lea(toOperand(from), reg.value()); + masm.mov(reg.value(), toOperand(to)); } else { // This is tricky without a scratch reg. We can't do an lea. Bounce the // base register off the stack, then add the offset in place. Note that @@ -523,3 +535,34 @@ MoveEmitterX86::finish() masm.freeStack(masm.framePushed() - pushedAtStart_); } +Maybe +MoveEmitterX86::findScratchRegister(const MoveResolver& moves, size_t initial) +{ +#ifdef JS_CODEGEN_X86 + if (scratchRegister_.isSome()) + return scratchRegister_; + + // All registers are either in use by this move group or are live + // afterwards. Look through the remaining moves for a register which is + // clobbered before it is used, and is thus dead at this point. + AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); + for (size_t i = initial; i < moves.numMoves(); i++) { + const MoveOp& move = moves.getMove(i); + if (move.from().isGeneralReg()) + regs.takeUnchecked(move.from().reg()); + else if (move.from().isMemoryOrEffectiveAddress()) + regs.takeUnchecked(move.from().base()); + if (move.to().isGeneralReg()) { + if (i != initial && !move.isCycleBegin() && regs.has(move.to().reg())) + return mozilla::Some(move.to().reg()); + regs.takeUnchecked(move.to().reg()); + } else if (move.to().isMemoryOrEffectiveAddress()) { + regs.takeUnchecked(move.to().base()); + } + } + + return mozilla::Nothing(); +#else + return mozilla::Some(ScratchReg); +#endif +} diff --git a/js/src/jit/x86-shared/MoveEmitter-x86-shared.h b/js/src/jit/x86-shared/MoveEmitter-x86-shared.h index 59b4e55678..eb382bed05 100644 --- a/js/src/jit/x86-shared/MoveEmitter-x86-shared.h +++ b/js/src/jit/x86-shared/MoveEmitter-x86-shared.h @@ -40,8 +40,10 @@ class MoveEmitterX86 bool* allGeneralRegs, bool* allFloatRegs); bool maybeEmitOptimizedCycle(const MoveResolver& moves, size_t i, bool allGeneralRegs, bool allFloatRegs, size_t swapCount); - void emitInt32Move(const MoveOperand& from, const MoveOperand& to); - void emitGeneralMove(const MoveOperand& from, const MoveOperand& to); + void emitInt32Move(const MoveOperand& from, const MoveOperand& to, + const MoveResolver& moves, size_t i); + void emitGeneralMove(const MoveOperand& from, const MoveOperand& to, + const MoveResolver& moves, size_t i); void emitFloat32Move(const MoveOperand& from, const MoveOperand& to); void emitDoubleMove(const MoveOperand& from, const MoveOperand& to); void emitFloat32X4Move(const MoveOperand& from, const MoveOperand& to); @@ -61,22 +63,7 @@ class MoveEmitterX86 #endif } - bool hasScratchRegister() { -#ifdef JS_CODEGEN_X86 - return scratchRegister_.isSome(); -#else - return true; -#endif - } - - Register scratchRegister() { - MOZ_ASSERT(hasScratchRegister()); -#ifdef JS_CODEGEN_X86 - return scratchRegister_.value(); -#else - return ScratchReg; -#endif - } + mozilla::Maybe findScratchRegister(const MoveResolver& moves, size_t i); }; typedef MoveEmitterX86 MoveEmitter; diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp index 7f308cc570..1d91d77e95 100644 --- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -9,6 +9,7 @@ #include "mozilla/Casting.h" #include "mozilla/DebugOnly.h" +#include "jsmath.h" #include "jsnum.h" #include "jit/IonCaches.h" @@ -1114,3 +1115,18 @@ CodeGeneratorX86::visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32* ool) masm.jump(ool->rejoin()); } + +void +CodeGeneratorX86::visitRandom(LRandom* ins) +{ + Register temp = ToRegister(ins->temp()); + Register temp2 = ToRegister(ins->temp2()); + + masm.loadJSContext(temp); + + masm.setupUnalignedABICall(1, temp2); + masm.passABIArg(temp); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE); + + MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg); +} diff --git a/js/src/jit/x86/CodeGenerator-x86.h b/js/src/jit/x86/CodeGenerator-x86.h index 3c00c8a95a..f470014600 100644 --- a/js/src/jit/x86/CodeGenerator-x86.h +++ b/js/src/jit/x86/CodeGenerator-x86.h @@ -71,6 +71,8 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared void visitOutOfLineTruncate(OutOfLineTruncate* ool); void visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32* ool); + void visitRandom(LRandom* ins); + private: void asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, bool boundsCheck, int32_t offset, int32_t endOffset, Register out, diff --git a/js/src/jit/x86/LIR-x86.h b/js/src/jit/x86/LIR-x86.h index 3e9c931434..7009fdcfa9 100644 --- a/js/src/jit/x86/LIR-x86.h +++ b/js/src/jit/x86/LIR-x86.h @@ -143,6 +143,23 @@ class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 0> } }; +// Math.random(). +class LRandom : public LCallInstructionHelper<1, 0, 2> +{ + public: + LIR_HEADER(Random) + LRandom(const LDefinition& temp, const LDefinition& temp2) { + setTemp(0, temp); + setTemp(1, temp2); + } + const LDefinition* temp() { + return getTemp(0); + } + const LDefinition* temp2() { + return getTemp(1); + } +}; + } // namespace jit } // namespace js diff --git a/js/src/jit/x86/Lowering-x86.cpp b/js/src/jit/x86/Lowering-x86.cpp index 1fb8c4d975..eb8b832d53 100644 --- a/js/src/jit/x86/Lowering-x86.cpp +++ b/js/src/jit/x86/Lowering-x86.cpp @@ -409,3 +409,10 @@ LIRGeneratorX86::visitSubstr(MSubstr* ins) define(lir, ins); assignSafepoint(lir, ins); } + +void +LIRGeneratorX86::visitRandom(MRandom* ins) +{ + LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1)); + defineReturn(lir, ins); +} diff --git a/js/src/jit/x86/Lowering-x86.h b/js/src/jit/x86/Lowering-x86.h index 044b5d4f42..a64a1e82ce 100644 --- a/js/src/jit/x86/Lowering-x86.h +++ b/js/src/jit/x86/Lowering-x86.h @@ -57,6 +57,7 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins); void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins); void visitSubstr(MSubstr* ins); + void visitRandom(MRandom* ins); void lowerPhi(MPhi* phi); static bool allowTypedElementHoleCheck() { diff --git a/js/src/jsapi-tests/testAddPropertyPropcache.cpp b/js/src/jsapi-tests/testAddPropertyPropcache.cpp index d3212fdcd5..a079c8c437 100644 --- a/js/src/jsapi-tests/testAddPropertyPropcache.cpp +++ b/js/src/jsapi-tests/testAddPropertyPropcache.cpp @@ -32,13 +32,13 @@ BEGIN_TEST(testAddPropertyHook) JS::RootedObject obj(cx, JS_NewPlainObject(cx)); CHECK(obj); - JS::RootedValue proto(cx, OBJECT_TO_JSVAL(obj)); + JS::RootedValue proto(cx, JS::ObjectValue(*obj)); JS_InitClass(cx, global, obj, &AddPropertyClass, nullptr, 0, nullptr, nullptr, nullptr, nullptr); obj = JS_NewArrayObject(cx, 0); CHECK(obj); - JS::RootedValue arr(cx, OBJECT_TO_JSVAL(obj)); + JS::RootedValue arr(cx, JS::ObjectValue(*obj)); CHECK(JS_DefineProperty(cx, global, "arr", arr, JSPROP_ENUMERATE, diff --git a/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp b/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp index 048d9be1a8..adf2bffc41 100644 --- a/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp +++ b/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp @@ -20,17 +20,17 @@ BEGIN_TEST(testDefineGetterSetterNonEnumerable) JS::RootedValue vobj(cx); JS::RootedObject obj(cx, JS_NewPlainObject(cx)); CHECK(obj); - vobj = OBJECT_TO_JSVAL(obj); + vobj.setObject(*obj); - JSFunction *funGet = JS_NewFunction(cx, NativeGetterSetter, 0, 0, "get"); + JSFunction* funGet = JS_NewFunction(cx, NativeGetterSetter, 0, 0, "get"); CHECK(funGet); JS::RootedObject funGetObj(cx, JS_GetFunctionObject(funGet)); - JS::RootedValue vget(cx, OBJECT_TO_JSVAL(funGetObj)); + JS::RootedValue vget(cx, JS::ObjectValue(*funGetObj)); - JSFunction *funSet = JS_NewFunction(cx, NativeGetterSetter, 1, 0, "set"); + JSFunction* funSet = JS_NewFunction(cx, NativeGetterSetter, 1, 0, "set"); CHECK(funSet); JS::RootedObject funSetObj(cx, JS_GetFunctionObject(funSet)); - JS::RootedValue vset(cx, OBJECT_TO_JSVAL(funSetObj)); + JS::RootedValue vset(cx, JS::ObjectValue(*funSetObj)); JS::RootedObject vObject(cx, vobj.toObjectOrNull()); CHECK(JS_DefineProperty(cx, vObject, PROPERTY_NAME, diff --git a/js/src/jsapi-tests/testGCCellPtr.cpp b/js/src/jsapi-tests/testGCCellPtr.cpp index a4b88c83bb..f9de51e4c2 100644 --- a/js/src/jsapi-tests/testGCCellPtr.cpp +++ b/js/src/jsapi-tests/testGCCellPtr.cpp @@ -35,16 +35,16 @@ BEGIN_TEST(testGCCellPtr) CHECK(!JS::GCCellPtr(nullptr)); CHECK(JS::GCCellPtr(obj.get())); - CHECK(JS::GCCellPtr(obj.get()).kind() == JSTRACE_OBJECT); - CHECK(JS::GCCellPtr(JS::ObjectValue(*obj)).kind() == JSTRACE_OBJECT); + CHECK(JS::GCCellPtr(obj.get()).kind() == JS::TraceKind::Object); + CHECK(JS::GCCellPtr(JS::ObjectValue(*obj)).kind() == JS::TraceKind::Object); CHECK(JS::GCCellPtr(str.get())); - CHECK(JS::GCCellPtr(str.get()).kind() == JSTRACE_STRING); - CHECK(JS::GCCellPtr(JS::StringValue(str)).kind() == JSTRACE_STRING); + CHECK(JS::GCCellPtr(str.get()).kind() == JS::TraceKind::String); + CHECK(JS::GCCellPtr(JS::StringValue(str)).kind() == JS::TraceKind::String); CHECK(JS::GCCellPtr(script.get())); CHECK(!JS::GCCellPtr(nullptr)); - CHECK(JS::GCCellPtr(script.get()).kind() == JSTRACE_SCRIPT); + CHECK(JS::GCCellPtr(script.get()).kind() == JS::TraceKind::Script); JS::GCCellPtr objcell(obj.get()); JS::GCCellPtr scriptcell = JS::GCCellPtr(script.get()); diff --git a/js/src/jsapi-tests/testGCMarking.cpp b/js/src/jsapi-tests/testGCMarking.cpp index 27453dcd66..d45fec26af 100644 --- a/js/src/jsapi-tests/testGCMarking.cpp +++ b/js/src/jsapi-tests/testGCMarking.cpp @@ -8,11 +8,11 @@ #include "jsapi-tests/tests.h" class CCWTestTracer : public JS::CallbackTracer { - static void staticCallback(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind) { + static void staticCallback(JS::CallbackTracer* trc, void** thingp, JS::TraceKind kind) { static_cast(trc)->callback(thingp, kind); } - void callback(void** thingp, JSGCTraceKind kind) { + void callback(void** thingp, JS::TraceKind kind) { numberOfThingsTraced++; printf("*thingp = %p\n", *thingp); @@ -29,9 +29,9 @@ class CCWTestTracer : public JS::CallbackTracer { bool okay; size_t numberOfThingsTraced; void** expectedThingp; - JSGCTraceKind expectedKind; + JS::TraceKind expectedKind; - CCWTestTracer(JSContext* cx, void** expectedThingp, JSGCTraceKind expectedKind) + CCWTestTracer(JSContext* cx, void** expectedThingp, JS::TraceKind expectedKind) : JS::CallbackTracer(JS_GetRuntime(cx), staticCallback), okay(true), numberOfThingsTraced(0), @@ -69,7 +69,7 @@ BEGIN_TEST(testTracingIncomingCCWs) CHECK(zones.put(global1->zone())); void* thing = obj.get(); - CCWTestTracer trc(cx, &thing, JSTRACE_OBJECT); + CCWTestTracer trc(cx, &thing, JS::TraceKind::Object); JS_TraceIncomingCCWs(&trc, zones); CHECK(trc.numberOfThingsTraced == 1); CHECK(trc.okay); diff --git a/js/src/jsapi-tests/testLookup.cpp b/js/src/jsapi-tests/testLookup.cpp index 25df14f5b5..f912a2fac8 100644 --- a/js/src/jsapi-tests/testLookup.cpp +++ b/js/src/jsapi-tests/testLookup.cpp @@ -61,7 +61,7 @@ document_resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* res return false; JS::Rooted allValue(cx, JS::ObjectValue(*docAll)); - if (!JS_DefinePropertyById(cx, obj, id, allValue, 0)) + if (!JS_DefinePropertyById(cx, obj, id, allValue, JSPROP_RESOLVING)) return false; *resolvedp = true; diff --git a/js/src/jsapi-tests/testMappedArrayBuffer.cpp b/js/src/jsapi-tests/testMappedArrayBuffer.cpp index 0e858e714a..fe598ca10a 100644 --- a/js/src/jsapi-tests/testMappedArrayBuffer.cpp +++ b/js/src/jsapi-tests/testMappedArrayBuffer.cpp @@ -125,7 +125,7 @@ bool TestCloneObject() JS::RootedObject obj1(cx, CreateNewObject(8, 12)); CHECK(obj1); JSAutoStructuredCloneBuffer cloned_buffer; - JS::RootedValue v1(cx, OBJECT_TO_JSVAL(obj1)); + JS::RootedValue v1(cx, JS::ObjectValue(*obj1)); const JSStructuredCloneCallbacks* callbacks = js::GetContextStructuredCloneCallbacks(cx); CHECK(cloned_buffer.write(cx, v1, callbacks, nullptr)); JS::RootedValue v2(cx); @@ -152,14 +152,14 @@ bool TestTransferObject() { JS::RootedObject obj1(cx, CreateNewObject(8, 12)); CHECK(obj1); - JS::RootedValue v1(cx, OBJECT_TO_JSVAL(obj1)); + JS::RootedValue v1(cx, JS::ObjectValue(*obj1)); // Create an Array of transferable values. JS::AutoValueVector argv(cx); argv.append(v1); JS::RootedObject obj(cx, JS_NewArrayObject(cx, JS::HandleValueArray::subarray(argv, 0, 1))); CHECK(obj); - JS::RootedValue transferable(cx, OBJECT_TO_JSVAL(obj)); + JS::RootedValue transferable(cx, JS::ObjectValue(*obj)); JSAutoStructuredCloneBuffer cloned_buffer; const JSStructuredCloneCallbacks* callbacks = js::GetContextStructuredCloneCallbacks(cx); diff --git a/js/src/jsapi-tests/testNewObject.cpp b/js/src/jsapi-tests/testNewObject.cpp index 335c3736b8..d33b33746a 100644 --- a/js/src/jsapi-tests/testNewObject.cpp +++ b/js/src/jsapi-tests/testNewObject.cpp @@ -75,7 +75,7 @@ BEGIN_TEST(testNewObject_1) argv[0].setInt32(4); obj = JS_New(cx, Array, JS::HandleValueArray::subarray(argv, 0, 1)); CHECK(obj); - rt = OBJECT_TO_JSVAL(obj); + rt = JS::ObjectValue(*obj); CHECK(JS_IsArrayObject(cx, obj)); CHECK(JS_GetArrayLength(cx, obj, &len)); CHECK_EQUAL(len, 4u); @@ -85,7 +85,7 @@ BEGIN_TEST(testNewObject_1) argv[i].setInt32(i); obj = JS_New(cx, Array, JS::HandleValueArray::subarray(argv, 0, N)); CHECK(obj); - rt = OBJECT_TO_JSVAL(obj); + rt = JS::ObjectValue(*obj); CHECK(JS_IsArrayObject(cx, obj)); CHECK(JS_GetArrayLength(cx, obj, &len)); CHECK_EQUAL(len, N); @@ -102,7 +102,7 @@ BEGIN_TEST(testNewObject_1) }; JS::RootedObject ctor(cx, JS_NewObject(cx, &cls)); CHECK(ctor); - JS::RootedValue rt2(cx, OBJECT_TO_JSVAL(ctor)); + JS::RootedValue rt2(cx, JS::ObjectValue(*ctor)); obj = JS_New(cx, ctor, JS::HandleValueArray::subarray(argv, 0, 3)); CHECK(obj); CHECK(JS_GetElement(cx, ctor, 0, &v)); diff --git a/js/src/jsapi-tests/testOps.cpp b/js/src/jsapi-tests/testOps.cpp index c3030f7c61..a1fe6a18d2 100644 --- a/js/src/jsapi-tests/testOps.cpp +++ b/js/src/jsapi-tests/testOps.cpp @@ -34,7 +34,7 @@ createMyObject(JSContext* context, unsigned argc, jsval* vp) //JS_GC(context); //<- if we make GC here, all is ok JSObject* myObject = JS_NewObject(context, &myClass); - *vp = OBJECT_TO_JSVAL(myObject); + *vp = JS::ObjectOrNullValue(myObject); JS_EndRequest(context); diff --git a/js/src/jsapi-tests/testParseJSON.cpp b/js/src/jsapi-tests/testParseJSON.cpp index 7df841cc74..538f44e853 100644 --- a/js/src/jsapi-tests/testParseJSON.cpp +++ b/js/src/jsapi-tests/testParseJSON.cpp @@ -74,26 +74,26 @@ BEGIN_TEST(testParseJSON_success) const char16_t emptystr[] = { '\0' }; str = js::NewStringCopyN(cx, emptystr, 0); CHECK(str); - expected = STRING_TO_JSVAL(str); + expected = JS::StringValue(str); CHECK(TryParse(cx, "\"\"", expected)); const char16_t nullstr[] = { '\0' }; str = NewString(cx, nullstr); CHECK(str); - expected = STRING_TO_JSVAL(str); + expected = JS::StringValue(str); CHECK(TryParse(cx, "\"\\u0000\"", expected)); const char16_t backstr[] = { '\b' }; str = NewString(cx, backstr); CHECK(str); - expected = STRING_TO_JSVAL(str); + expected = JS::StringValue(str); CHECK(TryParse(cx, "\"\\b\"", expected)); CHECK(TryParse(cx, "\"\\u0008\"", expected)); const char16_t newlinestr[] = { '\n', }; str = NewString(cx, newlinestr); CHECK(str); - expected = STRING_TO_JSVAL(str); + expected = JS::StringValue(str); CHECK(TryParse(cx, "\"\\n\"", expected)); CHECK(TryParse(cx, "\"\\u000A\"", expected)); @@ -341,7 +341,7 @@ BEGIN_TEST(testParseJSON_reviver) JSFunction *fun = JS_NewFunction(cx, Censor, 0, 0, "censor"); CHECK(fun); - JS::RootedValue filter(cx, OBJECT_TO_JSVAL(JS_GetFunctionObject(fun))); + JS::RootedValue filter(cx, JS::ObjectValue(*JS_GetFunctionObject(fun))); CHECK(TryParse(cx, "true", filter)); CHECK(TryParse(cx, "false", filter)); diff --git a/js/src/jsapi-tests/testResolveRecursion.cpp b/js/src/jsapi-tests/testResolveRecursion.cpp index 791194b1d9..1cc98e7c87 100644 --- a/js/src/jsapi-tests/testResolveRecursion.cpp +++ b/js/src/jsapi-tests/testResolveRecursion.cpp @@ -11,7 +11,6 @@ * Test that resolve hook recursion for the same object and property is * prevented. */ - BEGIN_TEST(testResolveRecursion) { static const JSClass my_resolve_class = { @@ -88,7 +87,7 @@ doResolve(JS::HandleObject obj, JS::HandleId id, bool* resolvedp) CHECK_EQUAL(resolveEntryCount, 1); EVAL("obj2.y = true", &v); CHECK_SAME(v, JSVAL_TRUE); - CHECK(JS_DefinePropertyById(cx, obj, id, JS::FalseHandleValue, 0)); + CHECK(JS_DefinePropertyById(cx, obj, id, JS::FalseHandleValue, JSPROP_RESOLVING)); *resolvedp = true; return true; } @@ -100,7 +99,7 @@ doResolve(JS::HandleObject obj, JS::HandleId id, bool* resolvedp) } else if (JS_FlatStringEqualsAscii(str, "y")) { if (obj == obj2) { CHECK_EQUAL(resolveEntryCount, 2); - CHECK(JS_DefinePropertyById(cx, obj, id, JS::NullHandleValue, 0)); + CHECK(JS_DefinePropertyById(cx, obj, id, JS::NullHandleValue, JSPROP_RESOLVING)); EVAL("obj1.x", &v); CHECK(v.isUndefined()); EVAL("obj1.y", &v); @@ -134,5 +133,49 @@ my_resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp return static_cast(JS_GetPrivate(obj))-> doResolve(obj, id, resolvedp); } - END_TEST(testResolveRecursion) + +/* + * Test that JS_InitStandardClasses does not cause resolve hooks to be called. + * + * (XPConnect apparently does have global classes, such as the one created by + * nsMessageManagerScriptExecutor::InitChildGlobalInternal(), that have resolve + * hooks which can call back into JS, and on which JS_InitStandardClasses is + * called. Calling back into JS in the middle of resolving `undefined` is bad.) + */ +BEGIN_TEST(testResolveRecursion_InitStandardClasses) +{ + CHECK(JS_InitStandardClasses(cx, global)); + return true; +} + +const JSClass* getGlobalClass() override { + static const JSClass myGlobalClass = { + "testResolveRecursion_InitStandardClasses_myGlobalClass", + JSCLASS_GLOBAL_FLAGS, + nullptr, // add + nullptr, // delete + nullptr, // get + nullptr, // set + nullptr, // enumerate + my_resolve, + nullptr, // mayResolve + nullptr, // convert + nullptr, // finalize + nullptr, // call + nullptr, // hasInstance + nullptr, // construct + JS_GlobalObjectTraceHook + }; + + return &myGlobalClass; +} + +static bool +my_resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp) +{ + MOZ_ASSERT_UNREACHABLE("resolve hook should not be called from InitStandardClasses"); + JS_ReportError(cx, "FAIL"); + return false; +} +END_TEST(testResolveRecursion_InitStandardClasses) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 09127d94d5..58815dc8b5 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -262,7 +262,7 @@ JS_GetPositiveInfinityValue(JSContext* cx) JS_PUBLIC_API(jsval) JS_GetEmptyStringValue(JSContext* cx) { - return STRING_TO_JSVAL(cx->runtime()->emptyString); + return StringValue(cx->runtime()->emptyString); } JS_PUBLIC_API(JSString*) @@ -1209,9 +1209,8 @@ JS_ResolveStandardClass(JSContext* cx, HandleObject obj, HandleId id, bool* reso JSAtom* undefinedAtom = cx->names().undefined; if (idAtom == undefinedAtom) { *resolved = true; - return DefineProperty(cx, obj, undefinedAtom->asPropertyName(), - UndefinedHandleValue, nullptr, nullptr, - JSPROP_PERMANENT | JSPROP_READONLY); + return DefineProperty(cx, global, id, UndefinedHandleValue, nullptr, nullptr, + JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING); } /* Try for class constructors/prototypes named by well-known atoms. */ @@ -5658,6 +5657,15 @@ JS_SetGlobalJitCompilerOption(JSRuntime* rt, JSJitCompilerOption opt, uint32_t v JitSpew(js::jit::JitSpew_IonScripts, "Enable ion's GVN"); } break; + case JSJITCOMPILER_ION_FORCE_IC: + if (value == 0) { + jit::js_JitOptions.forceInlineCaches = false; + JitSpew(js::jit::JitSpew_IonScripts, "IonBuilder: Enable non-IC optimizations."); + } else { + jit::js_JitOptions.forceInlineCaches = true; + JitSpew(js::jit::JitSpew_IonScripts, "IonBuilder: Disable non-IC optimizations."); + } + break; case JSJITCOMPILER_ION_ENABLE: if (value == 1) { JS::RuntimeOptionsRef(rt).setIon(true); @@ -5712,6 +5720,8 @@ JS_GetGlobalJitCompilerOption(JSRuntime* rt, JSJitCompilerOption opt) return jit::js_JitOptions.forcedDefaultIonWarmUpThreshold.isSome() ? jit::js_JitOptions.forcedDefaultIonWarmUpThreshold.ref() : jit::OptimizationInfo::CompilerWarmupThreshold; + case JSJITCOMPILER_ION_FORCE_IC: + return jit::js_JitOptions.forceInlineCaches; case JSJITCOMPILER_ION_ENABLE: return JS::RuntimeOptionsRef(rt).ion(); case JSJITCOMPILER_BASELINE_ENABLE: diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 861620104f..76d2ad43af 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -865,26 +865,6 @@ class MOZ_STACK_CLASS SourceBufferHolder final #define JSFUN_CONSTRUCTOR 0x400 /* native that can be called as a ctor */ -#define JSPROP_REDEFINE_NONCONFIGURABLE 0x800 /* If set, will allow redefining a - non-configurable property, but - only on a non-DOM global. This - is a temporary hack that will - need to go away in bug - 1105518 */ - -#define JSPROP_IGNORE_ENUMERATE 0x1000 /* ignore the value in JSPROP_ENUMERATE. - This flag only valid when defining over - an existing property. */ -#define JSPROP_IGNORE_READONLY 0x2000 /* ignore the value in JSPROP_READONLY. - This flag only valid when defining over - an existing property. */ -#define JSPROP_IGNORE_PERMANENT 0x4000 /* ignore the value in JSPROP_PERMANENT. - This flag only valid when defining over - an existing property. */ -#define JSPROP_IGNORE_VALUE 0x8000 /* ignore the Value in the descriptor. Nothing was - specified when passed to Object.defineProperty - from script. */ - /* * Specify a generic native prototype methods, i.e., methods of a class * prototype that are exposed as static methods taking an extra leading @@ -899,6 +879,40 @@ class MOZ_STACK_CLASS SourceBufferHolder final #define JSFUN_FLAGS_MASK 0xe00 /* | of all the JSFUN_* flags */ +/* + * If set, will allow redefining a non-configurable property, but only on a + * non-DOM global. This is a temporary hack that will need to go away in bug + * 1105518. + */ +#define JSPROP_REDEFINE_NONCONFIGURABLE 0x1000 + +/* + * Resolve hooks and enumerate hooks must pass this flag when calling + * JS_Define* APIs to reify lazily-defined properties. + * + * JSPROP_RESOLVING is used only with property-defining APIs. It tells the + * engine to skip the resolve hook when performing the lookup at the beginning + * of property definition. This keeps the resolve hook from accidentally + * triggering itself: unchecked recursion. + * + * For enumerate hooks, triggering the resolve hook would be merely silly, not + * fatal, except in some cases involving non-configurable properties. + */ +#define JSPROP_RESOLVING 0x2000 + +#define JSPROP_IGNORE_ENUMERATE 0x4000 /* ignore the value in JSPROP_ENUMERATE. + This flag only valid when defining over + an existing property. */ +#define JSPROP_IGNORE_READONLY 0x8000 /* ignore the value in JSPROP_READONLY. + This flag only valid when defining over + an existing property. */ +#define JSPROP_IGNORE_PERMANENT 0x10000 /* ignore the value in JSPROP_PERMANENT. + This flag only valid when defining over + an existing property. */ +#define JSPROP_IGNORE_VALUE 0x20000 /* ignore the Value in the descriptor. Nothing was + specified when passed to Object.defineProperty + from script. */ + /* * The first call to JS_CallOnce by any thread in a process will call 'func'. * Later calls to JS_CallOnce with the same JSCallOnceType object will be @@ -1141,8 +1155,7 @@ class JS_PUBLIC_API(RuntimeOptions) { ion_(true), asmJS_(true), nativeRegExp_(true), - unboxedObjects_(false), // Not enabled by default yet - unboxedArrays_(false), // Ditto + unboxedArrays_(false), werror_(false), strictMode_(false), extraWarnings_(false), @@ -1186,12 +1199,6 @@ class JS_PUBLIC_API(RuntimeOptions) { return *this; } - bool unboxedObjects() const { return unboxedObjects_; } - RuntimeOptions& setUnboxedObjects(bool flag) { - unboxedObjects_ = flag; - return *this; - } - bool unboxedArrays() const { return unboxedArrays_; } RuntimeOptions& setUnboxedArrays(bool flag) { unboxedArrays_ = flag; @@ -1243,7 +1250,6 @@ class JS_PUBLIC_API(RuntimeOptions) { bool ion_ : 1; bool asmJS_ : 1; bool nativeRegExp_ : 1; - bool unboxedObjects_ : 1; bool unboxedArrays_ : 1; bool werror_ : 1; bool strictMode_ : 1; @@ -2563,6 +2569,7 @@ class PropertyDescriptorOperations JSPROP_SETTER | JSPROP_SHARED | JSPROP_REDEFINE_NONCONFIGURABLE | + JSPROP_RESOLVING | SHADOWABLE)) == 0); MOZ_ASSERT(!hasAll(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE)); MOZ_ASSERT(!hasAll(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT)); @@ -2581,6 +2588,12 @@ class PropertyDescriptorOperations } MOZ_ASSERT(getter() != JS_PropertyStub); MOZ_ASSERT(setter() != JS_StrictPropertyStub); + + MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_ENUMERATE)); + MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_PERMANENT)); + MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_READONLY)); + MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_VALUE)); + MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_REDEFINE_NONCONFIGURABLE)); #endif } @@ -2594,6 +2607,7 @@ class PropertyDescriptorOperations JSPROP_SETTER | JSPROP_SHARED | JSPROP_REDEFINE_NONCONFIGURABLE | + JSPROP_RESOLVING | SHADOWABLE)) == 0); MOZ_ASSERT_IF(isAccessorDescriptor(), has(JSPROP_GETTER) && has(JSPROP_SETTER)); #endif @@ -4892,6 +4906,7 @@ JS_SetOffthreadIonCompilationEnabled(JSRuntime* rt, bool enabled); Register(BASELINE_WARMUP_TRIGGER, "baseline.warmup.trigger") \ Register(ION_WARMUP_TRIGGER, "ion.warmup.trigger") \ Register(ION_GVN_ENABLE, "ion.gvn.enable") \ + Register(ION_FORCE_IC, "ion.forceinlineCaches") \ Register(ION_ENABLE, "ion.enable") \ Register(BASELINE_ENABLE, "baseline.enable") \ Register(OFFTHREAD_COMPILATION_ENABLE, "offthread-compilation.enable") \ diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index d782f85db3..2fe9f577e6 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -243,14 +243,24 @@ GetElement(JSContext* cx, HandleObject obj, IndexType index, bool* hole, Mutable return GetElement(cx, obj, obj, index, hole, vp); } -void +bool ElementAdder::append(JSContext* cx, HandleValue v) { MOZ_ASSERT(index_ < length_); - if (resObj_) - resObj_->as().setDenseElementWithType(cx, index_++, v); - else - vp_[index_++] = v; + if (resObj_) { + DenseElementResult result = + SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_, v.address(), 1); + if (result == DenseElementResult::Failure) + return false; + if (result == DenseElementResult::Incomplete) { + if (!DefineElement(cx, resObj_, index_, v)) + return false; + } + } else { + vp_[index_] = v; + } + index_++; + return true; } void @@ -258,12 +268,9 @@ ElementAdder::appendHole() { MOZ_ASSERT(getBehavior_ == ElementAdder::CheckHasElemPreserveHoles); MOZ_ASSERT(index_ < length_); - if (resObj_) { - MOZ_ASSERT(resObj_->as().getDenseElement(index_).isMagic(JS_ELEMENTS_HOLE)); - index_++; - } else { - vp_[index_++].setMagic(JS_ELEMENTS_HOLE); - } + if (!resObj_) + vp_[index_].setMagic(JS_ELEMENTS_HOLE); + index_++; } bool @@ -287,7 +294,8 @@ js::GetElementsWithAdder(JSContext* cx, HandleObject obj, HandleObject receiver, if (!GetElement(cx, obj, receiver, i, &val)) return false; } - adder->append(cx, val); + if (!adder->append(cx, val)) + return false; } return true; @@ -357,8 +365,7 @@ SetArrayElement(JSContext* cx, HandleObject obj, double index, HandleValue v) if ((obj->is() || obj->is()) && !obj->isIndexed() && index <= UINT32_MAX) { DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(index), v.address(), 1, - UpdateTypes); + SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(index), v.address(), 1); if (result != DenseElementResult::Incomplete) return result == DenseElementResult::Success; } @@ -1199,7 +1206,9 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp) /* vector must point to rooted memory. */ static bool -InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start, uint32_t count, const Value* vector, ShouldUpdateTypes updateTypes) +InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start, + uint32_t count, const Value* vector, + ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update) { MOZ_ASSERT(count <= MAX_ARRAY_INDEX); @@ -1950,8 +1959,8 @@ js::array_sort(JSContext* cx, unsigned argc, Value* vp) } ShouldUpdateTypes updateTypes = (allStrings || allInts) && defaultOrMatch - ? ShouldUpdateTypes::DontUpdateTypes - : ShouldUpdateTypes::UpdateTypes; + ? ShouldUpdateTypes::DontUpdate + : ShouldUpdateTypes::Update; if (!InitArrayElements(cx, obj, 0, uint32_t(n), vec.begin(), updateTypes)) return false; } @@ -2011,7 +2020,7 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp) if (!ObjectMayHaveExtraIndexedProperties(obj)) { DenseElementResult result = SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, length, - args.array(), args.length(), UpdateTypes); + args.array(), args.length()); if (result != DenseElementResult::Incomplete) { if (result == DenseElementResult::Failure) return false; @@ -2029,7 +2038,7 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp) } /* Steps 4-5. */ - if (!InitArrayElements(cx, obj, length, args.length(), args.array(), UpdateTypes)) + if (!InitArrayElements(cx, obj, length, args.length(), args.array())) return false; /* Steps 6-7. */ @@ -2176,7 +2185,10 @@ js::array_shift(JSContext* cx, unsigned argc, Value* vp) if (!SetLengthProperty(cx, obj, newlen)) return false; - return SuppressDeletedProperty(cx, obj, INT_TO_JSID(newlen)); + RootedId id(cx); + if (!IndexToId(cx, newlen, &id)) + return false; + return SuppressDeletedProperty(cx, obj, id); } /* Steps 5, 10. */ @@ -2272,7 +2284,7 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp) } /* Copy from args to the bottom of the array. */ - if (!InitArrayElements(cx, obj, 0, args.length(), args.array(), UpdateTypes)) + if (!InitArrayElements(cx, obj, 0, args.length(), args.array())) return false; newlen += args.length(); @@ -2285,84 +2297,6 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp) return true; } -// Return a new array with the default prototype and specified allocated -// capacity and length. If possible, try to reuse the group of the input -// object. The resulting array will have the same boxed/unboxed elements -// representation as the input object, and will either reuse the input -// object's group or will have unknown property types. -JSObject* -js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length, - NewObjectKind newKind, bool forceAnalyze) -{ - if (!obj->is() && !obj->is()) - return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind); - - if (obj->getProto() != cx->global()->maybeGetArrayPrototype()) - return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind); - - RootedObjectGroup group(cx, obj->getGroup(cx)); - if (!group) - return nullptr; - - if (group->maybePreliminaryObjects()) - group->maybePreliminaryObjects()->maybeAnalyze(cx, group, forceAnalyze); - - if (group->shouldPreTenure() || group->maybePreliminaryObjects()) - newKind = TenuredObject; - - if (group->maybeUnboxedLayout()) { - if (length > UnboxedArrayObject::MaximumCapacity) - return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind); - - return UnboxedArrayObject::create(cx, group, length, newKind); - } - - ArrayObject* res = NewDenseFullyAllocatedArray(cx, length, nullptr, newKind); - if (!res) - return nullptr; - - res->setGroup(group); - - if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects()) - preliminaryObjects->registerNewObject(res); - - return res; -} - -// As above, except this might not allocate space up to |length| and will -// definitely return a normal boxed array, instead of an unboxed array. This -// should be used when the result might need sparse elements. -static inline ArrayObject* -NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length) -{ - if (!obj->is() && !obj->is()) - return NewDensePartlyAllocatedArray(cx, length); - - if (obj->getProto() != cx->global()->maybeGetArrayPrototype()) - return NewDensePartlyAllocatedArray(cx, length); - - RootedObjectGroup group(cx, obj->getGroup(cx)); - if (!group) - return nullptr; - - if (group->maybePreliminaryObjects()) - group->maybePreliminaryObjects()->maybeAnalyze(cx, group); - - NewObjectKind newKind = GenericObject; - if (group->shouldPreTenure() || group->maybePreliminaryObjects()) - newKind = TenuredObject; - - if (group->maybeUnboxedLayout()) - return NewDensePartlyAllocatedArray(cx, length, nullptr, newKind); - - ArrayObject* res = NewDensePartlyAllocatedArray(cx, length, nullptr, newKind); - if (!res) - return nullptr; - - res->setGroup(group); - return res; -} - /* * Returns true if this is a dense or unboxed array whose |count| properties * starting from |startingIndex| may be accessed (get, set, delete) directly @@ -2964,28 +2898,17 @@ js::array_slice(JSContext* cx, unsigned argc, Value* vp) return true; } - RootedArrayObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin)); + RootedObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin)); if (!narr) return false; if (js::GetElementsOp op = obj->getOps()->getElements) { - // Ensure that we have dense elements, so that ElementAdder::append can - // use setDenseElementWithType. - DenseElementResult result = narr->ensureDenseElements(cx, 0, end - begin); - if (result == DenseElementResult::Failure) + ElementAdder adder(cx, narr, end - begin, ElementAdder::CheckHasElemPreserveHoles); + if (!op(cx, obj, begin, end, &adder)) return false; - if (result == DenseElementResult::Success) { - ElementAdder adder(cx, narr, end - begin, ElementAdder::CheckHasElemPreserveHoles); - if (!op(cx, obj, begin, end, &adder)) - return false; - - args.rval().setObject(*narr); - return true; - } - - // Fallthrough - MOZ_ASSERT(result == DenseElementResult::Incomplete); + args.rval().setObject(*narr); + return true; } if (obj->isNative() && obj->isIndexed() && end - begin > 1000) { @@ -3080,13 +3003,9 @@ array_filter(JSContext* cx, unsigned argc, Value* vp) RootedValue thisv(cx, args.length() >= 2 ? args[1] : UndefinedValue()); /* Step 6. */ - RootedObject arr(cx, NewDenseFullyAllocatedArray(cx, 0)); + RootedObject arr(cx, NewFullyAllocatedArrayForCallingAllocationSite(cx, 0)); if (!arr) return false; - ObjectGroup* newGroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array); - if (!newGroup) - return false; - arr->setGroup(newGroup); /* Step 7. */ uint32_t k = 0; @@ -3161,19 +3080,11 @@ IsArrayConstructor(const Value& v) } static bool -ArrayFromCallArgs(JSContext* cx, HandleObjectGroup group, CallArgs& args) +ArrayFromCallArgs(JSContext* cx, CallArgs& args) { - JSObject* obj = NewDenseFullyAllocatedArray(cx, args.length()); + JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length()); if (!obj) return false; - obj->setGroup(group); - - DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, args.array(), args.length(), - UpdateTypes); - if (result == DenseElementResult::Failure) - return false; - MOZ_ASSERT(result == DenseElementResult::Success); args.rval().setObject(*obj); return true; @@ -3187,10 +3098,7 @@ array_of(JSContext* cx, unsigned argc, Value* vp) if (IsArrayConstructor(args.thisv()) || !IsConstructor(args.thisv())) { // IsArrayConstructor(this) will usually be true in practice. This is // the most common path. - RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); - if (!group) - return false; - return ArrayFromCallArgs(cx, group, args); + return ArrayFromCallArgs(cx, args); } // Step 4. @@ -3291,16 +3199,12 @@ bool js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - + if (args.isConstructing()) MOZ_ASSERT(args.newTarget().toObject().as().native() == js::ArrayConstructor); - - RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); - if (!group) - return false; if (args.length() != 1 || !args[0].isNumber()) - return ArrayFromCallArgs(cx, group, args); + return ArrayFromCallArgs(cx, args); uint32_t length; if (args[0].isInt32()) { @@ -3319,14 +3223,7 @@ js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp) } } - /* - * Allocate up to |EagerAllocationMaxLength| dense elements eagerly, to - * avoid reallocating elements when filling the array. - */ - AllocatingBehaviour allocating = (length <= ArrayObject::EagerAllocationMaxLength) - ? NewArray_FullyAllocating - : NewArray_PartlyAllocating; - RootedObject obj(cx, NewDenseArray(cx, length, group, allocating)); + JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length); if (!obj) return false; @@ -3334,7 +3231,7 @@ js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp) return true; } -ArrayObject* +JSObject* js::ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt) { if (lengthInt < 0) { @@ -3343,10 +3240,7 @@ js::ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengt } uint32_t length = uint32_t(lengthInt); - AllocatingBehaviour allocating = (length <= ArrayObject::EagerAllocationMaxLength) - ? NewArray_FullyAllocating - : NewArray_PartlyAllocating; - return NewDenseArray(cx, length, group, allocating); + return NewPartlyAllocatedArrayTryUseGroup(cx, group, length); } static JSObject* @@ -3563,7 +3457,7 @@ js::NewDenseFullyAllocatedArray(ExclusiveContext* cx, uint32_t length, HandleObject proto /* = nullptr */, NewObjectKind newKind /* = GenericObject */) { - return NewArray(cx, length, proto, newKind); + return NewArray(cx, length, proto, newKind); } ArrayObject * JS_FASTCALL @@ -3583,46 +3477,12 @@ js::NewDenseUnallocatedArray(ExclusiveContext* cx, uint32_t length, } ArrayObject* -js::NewDenseArray(ExclusiveContext* cx, uint32_t length, HandleObjectGroup group, - AllocatingBehaviour allocating, bool convertDoubleElements) -{ - NewObjectKind newKind = !group ? SingletonObject : GenericObject; - if (group && group->shouldPreTenure()) - newKind = TenuredObject; - - ArrayObject* arr; - if (allocating == NewArray_Unallocating) { - arr = NewDenseUnallocatedArray(cx, length, nullptr, newKind); - } else if (allocating == NewArray_PartlyAllocating) { - arr = NewDensePartlyAllocatedArray(cx, length, nullptr, newKind); - } else { - MOZ_ASSERT(allocating == NewArray_FullyAllocating); - arr = NewDenseFullyAllocatedArray(cx, length, nullptr, newKind); - } - if (!arr) - return nullptr; - - if (group) - arr->setGroup(group); - - if (convertDoubleElements) - arr->setShouldConvertDoubleElements(); - - // If the length calculation overflowed, make sure that is marked for the - // new group. - if (arr->length() > INT32_MAX) - arr->setLength(cx, arr->length()); - - return arr; -} - -ArrayObject* -js::NewDenseCopiedArray(JSContext* cx, uint32_t length, HandleArrayObject src, +js::NewDenseCopiedArray(ExclusiveContext* cx, uint32_t length, HandleArrayObject src, uint32_t elementOffset, HandleObject proto /* = nullptr */) { MOZ_ASSERT(!src->isIndexed()); - ArrayObject* arr = NewArray(cx, length, proto); + ArrayObject* arr = NewArray(cx, length, proto); if (!arr) return nullptr; @@ -3637,11 +3497,11 @@ js::NewDenseCopiedArray(JSContext* cx, uint32_t length, HandleArrayObject src, // values must point at already-rooted Value objects ArrayObject* -js::NewDenseCopiedArray(JSContext* cx, uint32_t length, const Value* values, +js::NewDenseCopiedArray(ExclusiveContext* cx, uint32_t length, const Value* values, HandleObject proto /* = nullptr */, NewObjectKind newKind /* = GenericObject */) { - ArrayObject* arr = NewArray(cx, length, proto); + ArrayObject* arr = NewArray(cx, length, proto, newKind); if (!arr) return nullptr; @@ -3667,7 +3527,7 @@ js::NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSOb gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_); Rooted arr(cx, ArrayObject::createArray(cx, allocKind, - heap, shape, group, length)); + heap, shape, group, length)); if (!arr) return nullptr; @@ -3692,6 +3552,153 @@ js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc return arr; } +// Return a new boxed or unboxed array with the specified length and allocated +// capacity (up to maxLength), using the specified group if possible. +template +static inline JSObject* +NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length, + NewObjectKind newKind = GenericObject, bool forceAnalyze = false) +{ + MOZ_ASSERT(newKind != SingletonObject); + + if (group->maybePreliminaryObjects()) + group->maybePreliminaryObjects()->maybeAnalyze(cx, group, forceAnalyze); + + if (group->shouldPreTenure() || group->maybePreliminaryObjects()) + newKind = TenuredObject; + + if (group->maybeUnboxedLayout()) { + if (length > UnboxedArrayObject::MaximumCapacity) + return NewArray(cx, length, nullptr, newKind); + + return UnboxedArrayObject::create(cx, group, length, newKind, maxLength); + } + + ArrayObject* res = NewArray(cx, length, nullptr, newKind); + if (!res) + return nullptr; + + res->setGroup(group); + + // If the length calculation overflowed, make sure that is marked for the + // new group. + if (res->length() > INT32_MAX) + res->setLength(cx, res->length()); + + if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects()) + preliminaryObjects->registerNewObject(res); + + return res; +} + +JSObject* +js::NewFullyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length, + NewObjectKind newKind) +{ + return NewArrayTryUseGroup(cx, group, length, newKind); +} + +JSObject* +js::NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length) +{ + return NewArrayTryUseGroup(cx, group, length); +} + +// Return a new array with the default prototype and specified allocated +// capacity and length. If possible, try to reuse the group of the input +// object. The resulting array will either reuse the input object's group or +// will have unknown property types. Additionally, the result will have the +// same boxed/unboxed elements representation as the input object, unless +// |length| is larger than the input object's initialized length (in which case +// UnboxedArrayObject::MaximumCapacity might be exceeded). +template +static inline JSObject* +NewArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length, + NewObjectKind newKind = GenericObject, bool forceAnalyze = false) +{ + if (!obj->is() && !obj->is()) + return NewArray(cx, length, nullptr, newKind); + + if (obj->getProto() != cx->global()->maybeGetArrayPrototype()) + return NewArray(cx, length, nullptr, newKind); + + RootedObjectGroup group(cx, obj->getGroup(cx)); + if (!group) + return nullptr; + + return NewArrayTryUseGroup(cx, group, length, newKind, forceAnalyze); +} + +JSObject* +js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length, + NewObjectKind newKind, bool forceAnalyze) +{ + return NewArrayTryReuseGroup(cx, obj, length, newKind, forceAnalyze); +} + +JSObject* +js::NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length) +{ + return NewArrayTryReuseGroup(cx, obj, length); +} + +JSObject* +js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, + NewObjectKind newKind, bool forceAnalyze) +{ + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); + if (!group) + return nullptr; + return NewArrayTryUseGroup(cx, group, length, newKind, forceAnalyze); +} + +JSObject* +js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length) +{ + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); + if (!group) + return nullptr; + return NewArrayTryUseGroup(cx, group, length); +} + +JSObject* +js::NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, + const Value* vp, size_t length, NewObjectKind newKind, + ShouldUpdateTypes updateTypes) +{ + JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length, newKind); + if (!obj) + return nullptr; + + DenseElementResult result = + SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, updateTypes); + if (result == DenseElementResult::Failure) + return nullptr; + if (result == DenseElementResult::Success) + return obj; + + MOZ_ASSERT(obj->is()); + if (!UnboxedArrayObject::convertToNative(cx->asJSContext(), obj)) + return nullptr; + + result = SetOrExtendBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, + updateTypes); + MOZ_ASSERT(result != DenseElementResult::Incomplete); + if (result == DenseElementResult::Failure) + return nullptr; + + return obj; +} + +JSObject* +js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length) +{ + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); + if (!group) + return nullptr; + return NewCopiedArrayTryUseGroup(cx, group, vp, length); +} + #ifdef DEBUG bool js::ArrayInfo(JSContext* cx, unsigned argc, Value* vp) diff --git a/js/src/jsarray.h b/js/src/jsarray.h index 2ac01847bc..d9cfadeb6b 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -12,6 +12,8 @@ #include "jsobj.h" #include "jspubtd.h" +#include "vm/ArrayObject.h" + namespace js { /* 2^32-2, inclusive */ const uint32_t MAX_ARRAY_INDEX = 4294967294u; @@ -35,7 +37,7 @@ IdIsIndex(jsid id, uint32_t* indexp) extern JSObject* InitArrayClass(JSContext* cx, js::HandleObject obj); -class ArrayObject; +// The methods below only create dense boxed arrays. /* Create a dense array with no capacity allocated, length set to 0. */ extern ArrayObject * JS_FASTCALL @@ -63,28 +65,14 @@ extern ArrayObject * JS_FASTCALL NewDenseFullyAllocatedArray(ExclusiveContext* cx, uint32_t length, HandleObject proto = nullptr, NewObjectKind newKind = GenericObject); -enum AllocatingBehaviour { - NewArray_Unallocating, - NewArray_PartlyAllocating, - NewArray_FullyAllocating -}; - -/* - * Create a dense array with a set length, but only allocates space for the - * contents if the length is not excessive. - */ -extern ArrayObject* -NewDenseArray(ExclusiveContext* cx, uint32_t length, HandleObjectGroup group, - AllocatingBehaviour allocating, bool convertDoubleElements = false); - /* Create a dense array with a copy of the dense array elements in src. */ extern ArrayObject* -NewDenseCopiedArray(JSContext* cx, uint32_t length, HandleArrayObject src, +NewDenseCopiedArray(ExclusiveContext* cx, uint32_t length, HandleArrayObject src, uint32_t elementOffset, HandleObject proto = nullptr); /* Create a dense array from the given array values, which must be rooted */ extern ArrayObject* -NewDenseCopiedArray(JSContext* cx, uint32_t length, const Value* values, +NewDenseCopiedArray(ExclusiveContext* cx, uint32_t length, const Value* values, HandleObject proto = nullptr, NewObjectKind newKind = GenericObject); /* Create a dense array based on templateObject with the given length. */ @@ -95,12 +83,46 @@ NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSObject extern JSObject* NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap); -/* Create a dense or unboxed array, using the same group as |obj| if possible. */ +// The methods below can create either boxed or unboxed arrays. + +extern JSObject* +NewFullyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length, + NewObjectKind newKind = GenericObject); + +extern JSObject* +NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length); + extern JSObject* NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length, NewObjectKind newKind = GenericObject, bool forceAnalyze = false); +extern JSObject* +NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length); + +extern JSObject* +NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, + NewObjectKind newKind = GenericObject, + bool forceAnalyze = false); + +extern JSObject* +NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length); + +enum class ShouldUpdateTypes +{ + Update, + DontUpdate +}; + +extern JSObject* +NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, + const Value* vp, size_t length, + NewObjectKind newKind = GenericObject, + ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update); + +extern JSObject* +NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length); + /* * Determines whether a write to the given element on |obj| should fail because * |obj| is an Array with a non-writable length, and writing that element would @@ -193,7 +215,7 @@ array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end, H extern bool NewbornArrayPush(JSContext* cx, HandleObject obj, const Value& v); -extern ArrayObject* +extern JSObject* ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt); #ifdef DEBUG diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 00f100767b..302ac089c1 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -292,6 +292,9 @@ struct JSContext : public js::ExclusiveContext, static size_t offsetOfRuntime() { return offsetof(JSContext, runtime_); } + static size_t offsetOfCompartment() { + return offsetof(JSContext, compartment_); + } friend class js::ExclusiveContext; friend class JS::AutoSaveExceptionState; @@ -791,7 +794,6 @@ bool intrinsic_NewDenseArray(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_UnsafePutElements(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_UnsafeSetReservedSlot(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_UnsafeGetReservedSlot(JSContext* cx, unsigned argc, Value* vp); diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index e0337f5379..ee2adb1c9e 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -212,7 +212,7 @@ class WrapperMapRef : public BufferableRef key.kind == CrossCompartmentKey::DebuggerSource) { MOZ_ASSERT(IsInsideNursery(key.wrapped) || - key.wrapped->asTenured().getTraceKind() == JSTRACE_OBJECT); + key.wrapped->asTenured().getTraceKind() == JS::TraceKind::Object); TraceManuallyBarrieredEdge(trc, reinterpret_cast(&key.wrapped), "CCW wrapped object"); } @@ -613,17 +613,17 @@ JSCompartment::sweepCrossCompartmentWrappers() case CrossCompartmentKey::DebuggerEnvironment: case CrossCompartmentKey::DebuggerSource: MOZ_ASSERT(IsInsideNursery(key.wrapped) || - key.wrapped->asTenured().getTraceKind() == JSTRACE_OBJECT); + key.wrapped->asTenured().getTraceKind() == JS::TraceKind::Object); keyDying = IsAboutToBeFinalizedUnbarriered( reinterpret_cast(&key.wrapped)); break; case CrossCompartmentKey::StringWrapper: - MOZ_ASSERT(key.wrapped->asTenured().getTraceKind() == JSTRACE_STRING); + MOZ_ASSERT(key.wrapped->asTenured().getTraceKind() == JS::TraceKind::String); keyDying = IsAboutToBeFinalizedUnbarriered( reinterpret_cast(&key.wrapped)); break; case CrossCompartmentKey::DebuggerScript: - MOZ_ASSERT(key.wrapped->asTenured().getTraceKind() == JSTRACE_SCRIPT); + MOZ_ASSERT(key.wrapped->asTenured().getTraceKind() == JS::TraceKind::Script); keyDying = IsAboutToBeFinalizedUnbarriered( reinterpret_cast(&key.wrapped)); break; diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index d83e72de76..50c9dce7ee 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -455,6 +455,10 @@ struct JSCompartment /* Random number generator state, used by jsmath.cpp. */ uint64_t rngState; + static size_t offsetOfRngState() { + return offsetof(JSCompartment, rngState); + } + private: JSCompartment* thisForCtor() { return this; } diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 1dc1a42f2c..1c536c844e 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -211,17 +211,17 @@ JS_WrapPropertyDescriptor(JSContext* cx, JS::MutableHandle(shape.asCell())); + TraceCycleCollectorChildren(trc, static_cast(shape.asCell())); } JS_FRIEND_API(void) -JS_TraceObjectGroupCycleCollectorChildren(JSTracer* trc, JS::GCCellPtr group) +JS_TraceObjectGroupCycleCollectorChildren(JS::CallbackTracer* trc, JS::GCCellPtr group) { MOZ_ASSERT(group.isObjectGroup()); - MarkCycleCollectorChildren(trc, static_cast(group.asCell())); + TraceCycleCollectorChildren(trc, static_cast(group.asCell())); } static bool @@ -592,11 +592,11 @@ js::ZoneGlobalsAreAllGray(JS::Zone* zone) return true; } -JS_FRIEND_API(JSGCTraceKind) +JS_FRIEND_API(JS::TraceKind) js::GCThingTraceKind(void* thing) { MOZ_ASSERT(thing); - return gc::GetGCThingTraceKind(thing); + return static_cast(thing)->getTraceKind(); } JS_FRIEND_API(void) @@ -922,7 +922,7 @@ DumpHeapVisitCompartment(JSRuntime* rt, void* data, JSCompartment* comp) static void DumpHeapVisitArena(JSRuntime* rt, void* data, gc::Arena* arena, - JSGCTraceKind traceKind, size_t thingSize) + JS::TraceKind traceKind, size_t thingSize) { DumpHeapTracer* dtrc = static_cast(data); fprintf(dtrc->output, "# arena allockind=%u size=%u\n", @@ -931,7 +931,7 @@ DumpHeapVisitArena(JSRuntime* rt, void* data, gc::Arena* arena, static void DumpHeapVisitCell(JSRuntime* rt, void* data, void* thing, - JSGCTraceKind traceKind, size_t thingSize) + JS::TraceKind traceKind, size_t thingSize) { DumpHeapTracer* dtrc = static_cast(data); char cellDesc[1024 * 32]; @@ -941,7 +941,7 @@ DumpHeapVisitCell(JSRuntime* rt, void* data, void* thing, } static void -DumpHeapVisitChild(JS::CallbackTracer *trc, void** thingp, JSGCTraceKind kind) +DumpHeapVisitChild(JS::CallbackTracer* trc, void** thingp, JS::TraceKind kind) { if (gc::IsInsideNursery((js::gc::Cell*)*thingp)) return; @@ -953,7 +953,7 @@ DumpHeapVisitChild(JS::CallbackTracer *trc, void** thingp, JSGCTraceKind kind) } static void -DumpHeapVisitRoot(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind) +DumpHeapVisitRoot(JS::CallbackTracer* trc, void** thingp, JS::TraceKind kind) { if (gc::IsInsideNursery((js::gc::Cell*)*thingp)) return; @@ -1184,23 +1184,6 @@ js::GetObjectMetadata(JSObject *obj) return nullptr; } -JS_FRIEND_API(bool) -js::DefineOwnProperty(JSContext* cx, JSObject* objArg, jsid idArg, - JS::Handle descriptor, ObjectOpResult &result) -{ - RootedObject obj(cx, objArg); - RootedId id(cx, idArg); - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, obj, id, descriptor.value()); - if (descriptor.hasGetterObject()) - assertSameCompartment(cx, descriptor.getterObject()); - if (descriptor.hasSetterObject()) - assertSameCompartment(cx, descriptor.setterObject()); - - return StandardDefineProperty(cx, obj, id, descriptor, result); -} - JS_FRIEND_API(bool) js::ReportIsNotFunction(JSContext* cx, HandleValue v) { @@ -1255,6 +1238,13 @@ js::ForwardToNative(JSContext* cx, JSNative native, const CallArgs& args) return native(cx, args.length(), args.base()); } +JS_FRIEND_API(JSObject*) +js::ConvertArgsToArray(JSContext* cx, const CallArgs& args) +{ + RootedObject argsArray(cx, NewDenseCopiedArray(cx, args.length(), args.array())); + return argsArray; +} + JS_FRIEND_API(JSAtom*) js::GetPropertyNameFromPC(JSScript* script, jsbytecode* pc) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 55c9c4e8d7..531e2cc4d7 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -96,9 +96,9 @@ JS_IsDeadWrapper(JSObject* obj); * all cycle-participating data it reaches, using bounded stack space. */ extern JS_FRIEND_API(void) -JS_TraceShapeCycleCollectorChildren(JSTracer* trc, JS::GCCellPtr shape); +JS_TraceShapeCycleCollectorChildren(JS::CallbackTracer* trc, JS::GCCellPtr shape); extern JS_FRIEND_API(void) -JS_TraceObjectGroupCycleCollectorChildren(JSTracer* trc, JS::GCCellPtr group); +JS_TraceObjectGroupCycleCollectorChildren(JS::CallbackTracer* trc, JS::GCCellPtr group); extern JS_FRIEND_API(JSPrincipals*) JS_GetCompartmentPrincipals(JSCompartment* compartment); @@ -499,7 +499,7 @@ VisitGrayWrapperTargets(JS::Zone* zone, GCThingCallback callback, void* closure) extern JS_FRIEND_API(JSObject*) GetWeakmapKeyDelegate(JSObject* key); -JS_FRIEND_API(JSGCTraceKind) +JS_FRIEND_API(JS::TraceKind) GCThingTraceKind(void* thing); /* @@ -1670,6 +1670,12 @@ extern JS_FRIEND_API(JSObject*) JS_NewSharedFloat64ArrayWithBuffer(JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset, uint32_t length); +/* + * Create a new SharedArrayBuffer with the given byte length. + */ +extern JS_FRIEND_API(JSObject*) +JS_NewSharedArrayBuffer(JSContext* cx, uint32_t nbytes); + /* * Create a new ArrayBuffer with the given byte length. */ @@ -1794,6 +1800,13 @@ UnwrapSharedFloat32Array(JSObject* obj); extern JS_FRIEND_API(JSObject*) UnwrapSharedFloat64Array(JSObject* obj); +extern JS_FRIEND_API(JSObject*) +UnwrapSharedArrayBuffer(JSObject* obj); + +extern JS_FRIEND_API(JSObject*) +UnwrapSharedArrayBufferView(JSObject* obj); + + namespace detail { extern JS_FRIEND_DATA(const Class* const) Int8ArrayClassPtr; @@ -1845,6 +1858,16 @@ JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint32, uint32_t) JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float32, float) JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float64, double) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(SharedInt8, int8_t) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(SharedUint8, uint8_t) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(SharedUint8Clamped, uint8_t) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(SharedInt16, int16_t) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(SharedUint16, uint16_t) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(SharedInt32, int32_t) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(SharedUint32, uint32_t) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(SharedFloat32, float) +JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(SharedFloat64, double) + #undef JS_DEFINE_DATA_AND_LENGTH_ACCESSOR // This one isn't inlined because it's rather tricky (by dint of having to deal @@ -1852,11 +1875,17 @@ JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float64, double) extern JS_FRIEND_API(void) GetArrayBufferViewLengthAndData(JSObject* obj, uint32_t* length, uint8_t** data); +extern JS_FRIEND_API(void) +GetSharedArrayBufferViewLengthAndData(JSObject* obj, uint32_t* length, uint8_t** data); + // This one isn't inlined because there are a bunch of different ArrayBuffer // classes that would have to be individually handled here. extern JS_FRIEND_API(void) GetArrayBufferLengthAndData(JSObject* obj, uint32_t* length, uint8_t** data); +extern JS_FRIEND_API(void) +GetSharedArrayBufferLengthAndData(JSObject* obj, uint32_t* length, uint8_t** data); + } // namespace js /* @@ -1897,6 +1926,9 @@ JS_GetObjectAsArrayBuffer(JSObject* obj, uint32_t* length, uint8_t** data); extern JS_FRIEND_API(js::Scalar::Type) JS_GetArrayBufferViewType(JSObject* obj); +extern JS_FRIEND_API(js::Scalar::Type) +JS_GetSharedArrayBufferViewType(JSObject* obj); + /* * Check whether obj supports the JS_GetArrayBuffer* APIs. Note that this may * return false if a security wrapper is encountered that denies the @@ -2016,6 +2048,27 @@ JS_GetFloat32ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&); extern JS_FRIEND_API(double*) JS_GetFloat64ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(uint8_t*) +JS_GetSharedArrayBufferData(JSObject* obj, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(int8_t*) +JS_GetSharedInt8ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(uint8_t*) +JS_GetSharedUint8ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(uint8_t*) +JS_GetSharedUint8ClampedArrayData(JSObject* obj, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(int16_t*) +JS_GetSharedInt16ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(uint16_t*) +JS_GetSharedUint16ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(int32_t*) +JS_GetSharedInt32ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(uint32_t*) +JS_GetSharedUint32ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(float*) +JS_GetSharedFloat32ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&); +extern JS_FRIEND_API(double*) +JS_GetSharedFloat64ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&); + /* * Same as above, but for any kind of ArrayBufferView. Prefer the type-specific * versions when possible. @@ -2718,9 +2771,8 @@ GetFirstSubsumedSavedFrame(JSContext* cx, JS::HandleObject savedFrame); extern JS_FRIEND_API(bool) ReportIsNotFunction(JSContext* cx, JS::HandleValue v); -extern JS_FRIEND_API(bool) -DefineOwnProperty(JSContext* cx, JSObject* objArg, jsid idArg, - JS::Handle descriptor, JS::ObjectOpResult &result); +extern JS_FRIEND_API(JSObject*) +ConvertArgsToArray(JSContext* cx, const JS::CallArgs& args); } /* namespace js */ diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index dbfb588aaa..ec372443ad 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -355,60 +355,53 @@ static const JSPropertySpec function_properties[] = { JS_PS_END }; -static JSObject* -ResolveInterpretedFunctionPrototype(JSContext* cx, HandleObject obj) +static bool +ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId id) { -#ifdef DEBUG - JSFunction* fun = &obj->as(); MOZ_ASSERT(fun->isInterpreted() || fun->isAsmJSNative()); MOZ_ASSERT(!fun->isFunctionPrototype()); -#endif + MOZ_ASSERT(id == NameToId(cx->names().prototype)); // Assert that fun is not a compiler-created function object, which // must never leak to script or embedding code and then be mutated. - // Also assert that obj is not bound, per the ES5 15.3.4.5 ref above. - MOZ_ASSERT(!IsInternalFunctionObject(obj)); - MOZ_ASSERT(!obj->isBoundFunction()); + // Also assert that fun is not bound, per the ES5 15.3.4.5 ref above. + MOZ_ASSERT(!IsInternalFunctionObject(fun)); + MOZ_ASSERT(!fun->isBoundFunction()); // Make the prototype object an instance of Object with the same parent as // the function object itself, unless the function is an ES6 generator. In // that case, per the 15 July 2013 ES6 draft, section 15.19.3, its parent is // the GeneratorObjectPrototype singleton. - bool isStarGenerator = obj->as().isStarGenerator(); - Rooted global(cx, &obj->global()); + bool isStarGenerator = fun->isStarGenerator(); + Rooted global(cx, &fun->global()); RootedObject objProto(cx); if (isStarGenerator) objProto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global); else - objProto = obj->global().getOrCreateObjectPrototype(cx); + objProto = fun->global().getOrCreateObjectPrototype(cx); if (!objProto) - return nullptr; + return false; RootedPlainObject proto(cx, NewObjectWithGivenProto(cx, objProto, SingletonObject)); if (!proto) - return nullptr; - - // Per ES5 15.3.5.2 a user-defined function's .prototype property is - // initially non-configurable, non-enumerable, and writable. - RootedValue protoVal(cx, ObjectValue(*proto)); - if (!DefineProperty(cx, obj, cx->names().prototype, protoVal, nullptr, nullptr, - JSPROP_PERMANENT)) - { - return nullptr; - } + return false; // Per ES5 13.2 the prototype's .constructor property is configurable, // non-enumerable, and writable. However, per the 15 July 2013 ES6 draft, // section 15.19.3, the .prototype of a generator function does not link // back with a .constructor. if (!isStarGenerator) { - RootedValue objVal(cx, ObjectValue(*obj)); + RootedValue objVal(cx, ObjectValue(*fun)); if (!DefineProperty(cx, proto, cx->names().constructor, objVal, nullptr, nullptr, 0)) - return nullptr; + return false; } - return proto; + // Per ES5 15.3.5.2 a user-defined function's .prototype property is + // initially non-configurable, non-enumerable, and writable. + RootedValue protoVal(cx, ObjectValue(*proto)); + return DefineProperty(cx, fun, id, protoVal, nullptr, nullptr, + JSPROP_PERMANENT | JSPROP_RESOLVING); } static bool @@ -435,19 +428,22 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) * or (Object.prototype, Function.prototype, etc.) have that property * created eagerly. * - * ES5 15.3.4: the non-native function object named Function.prototype - * does not have a .prototype property. - * * ES5 15.3.4.5: bound functions don't have a prototype property. The * isBuiltin() test covers this case because bound functions are native * (and thus built-in) functions by definition/construction. * - * ES6 19.2.4.3: arrow functions also don't have a prototype property. + * In ES6 9.2.8 MakeConstructor the .prototype property is only assigned + * to constructors. + * + * Thus all of the following don't get a .prototype property: + * - Methods (that are not class-constructors or generators) + * - Arrow functions + * - Function.prototype */ - if (fun->isBuiltin() || fun->isArrow() || fun->isFunctionPrototype()) + if (fun->isBuiltin() || !fun->isConstructor()) return true; - if (!ResolveInterpretedFunctionPrototype(cx, fun)) + if (!ResolveInterpretedFunctionPrototype(cx, fun, id)) return false; *resolvedp = true; @@ -490,8 +486,11 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) v.setString(fun->atom() == nullptr ? cx->runtime()->emptyString : fun->atom()); } - if (!NativeDefineProperty(cx, fun, id, v, nullptr, nullptr, JSPROP_READONLY)) + if (!NativeDefineProperty(cx, fun, id, v, nullptr, nullptr, + JSPROP_READONLY | JSPROP_RESOLVING)) + { return false; + } if (isLength) fun->setResolvedLength(); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index d68450a017..a4f2d5029a 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2209,12 +2209,12 @@ GCRuntime::relocateArenas(Zone *zone, JS::gcreason::Reason reason, SliceBudget & void -MovingTracer::Visit(JS::CallbackTracer* jstrc, void** thingp, JSGCTraceKind kind) +MovingTracer::Visit(JS::CallbackTracer* jstrc, void** thingp, JS::TraceKind kind) { TenuredCell* thing = TenuredCell::fromPointer(*thingp); // Currently we only relocate objects. - if (kind != JSTRACE_OBJECT) { + if (kind != JS::TraceKind::Object) { MOZ_ASSERT(!RelocationOverlay::isCellForwarded(thing)); return; } @@ -2272,7 +2272,7 @@ GCRuntime::sweepZoneAfterCompacting(Zone* zone) template static void -UpdateCellPointersTyped(MovingTracer* trc, ArenaHeader* arena, JSGCTraceKind traceKind) +UpdateCellPointersTyped(MovingTracer* trc, ArenaHeader* arena, JS::TraceKind traceKind) { for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) { T* cell = reinterpret_cast(i.getCell()); @@ -2288,7 +2288,7 @@ static void UpdateCellPointers(MovingTracer* trc, ArenaHeader* arena) { AllocKind kind = arena->getAllocKind(); - JSGCTraceKind traceKind = MapAllocToTraceKind(kind); + JS::TraceKind traceKind = MapAllocToTraceKind(kind); switch (kind) { case AllocKind::FUNCTION: @@ -3685,17 +3685,17 @@ class CompartmentCheckTracer : public JS::CallbackTracer {} Cell* src; - JSGCTraceKind srcKind; + JS::TraceKind srcKind; Zone* zone; JSCompartment* compartment; }; static bool -InCrossCompartmentMap(JSObject* src, Cell* dst, JSGCTraceKind dstKind) +InCrossCompartmentMap(JSObject* src, Cell* dst, JS::TraceKind dstKind) { JSCompartment* srccomp = src->compartment(); - if (dstKind == JSTRACE_OBJECT) { + if (dstKind == JS::TraceKind::Object) { Value key = ObjectValue(*static_cast(dst)); if (WrapperMap::Ptr p = srccomp->lookupWrapper(key)) { if (*p->value().unsafeGet() == ObjectValue(*src)) @@ -3717,11 +3717,11 @@ InCrossCompartmentMap(JSObject* src, Cell* dst, JSGCTraceKind dstKind) static void CheckCompartment(CompartmentCheckTracer* trc, JSCompartment* thingCompartment, - Cell* thing, JSGCTraceKind kind) + Cell* thing, JS::TraceKind kind) { MOZ_ASSERT(thingCompartment == trc->compartment || trc->runtime()->isAtomsCompartment(thingCompartment) || - (trc->srcKind == JSTRACE_OBJECT && + (trc->srcKind == JS::TraceKind::Object && InCrossCompartmentMap((JSObject*)trc->src, thing, kind))); } @@ -3730,7 +3730,7 @@ struct MaybeCompartmentFunctor { }; static void -CheckCompartmentCallback(JS::CallbackTracer* trcArg, void** thingp, JSGCTraceKind kind) +CheckCompartmentCallback(JS::CallbackTracer* trcArg, void** thingp, JS::TraceKind kind) { CompartmentCheckTracer* trc = static_cast(trcArg); TenuredCell* thing = TenuredCell::fromPointer(*thingp); @@ -6867,16 +6867,16 @@ JS_FRIEND_API(void) JS::AssertGCThingIsNotAnObjectSubclass(Cell* cell) { MOZ_ASSERT(cell); - MOZ_ASSERT(cell->getTraceKind() != JSTRACE_OBJECT); + MOZ_ASSERT(cell->getTraceKind() != JS::TraceKind::Object); } JS_FRIEND_API(void) -js::gc::AssertGCThingHasType(js::gc::Cell* cell, JSGCTraceKind kind) +js::gc::AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind) { if (!cell) - MOZ_ASSERT(kind == JSTRACE_NULL); + MOZ_ASSERT(kind == JS::TraceKind::Null); else if (IsInsideNursery(cell)) - MOZ_ASSERT(kind == JSTRACE_OBJECT); + MOZ_ASSERT(kind == JS::TraceKind::Object); else MOZ_ASSERT(MapAllocToTraceKind(cell->asTenured().getAllocKind()) == kind); } @@ -6965,18 +6965,12 @@ JS::AutoAssertGCCallback::AutoAssertGCCallback(JSObject* obj) } JS_FRIEND_API(const char*) -JS::GCTraceKindToAscii(JSGCTraceKind kind) +JS::GCTraceKindToAscii(JS::TraceKind kind) { switch(kind) { - case JSTRACE_OBJECT: return "Object"; - case JSTRACE_SCRIPT: return "Script"; - case JSTRACE_STRING: return "String"; - case JSTRACE_SYMBOL: return "Symbol"; - case JSTRACE_SHAPE: return "Shape"; - case JSTRACE_BASE_SHAPE: return "BaseShape"; - case JSTRACE_LAZY_SCRIPT: return "LazyScript"; - case JSTRACE_JITCODE: return "JitCode"; - case JSTRACE_OBJECT_GROUP: return "ObjectGroup"; +#define MAP_NAME(name, _0, _1) case JS::TraceKind::name: return #name; +FOR_EACH_GC_LAYOUT(MAP_NAME); +#undef MAP_NAME default: return "Invalid"; } } @@ -6985,19 +6979,19 @@ JS::GCCellPtr::GCCellPtr(const Value& v) : ptr(0) { if (v.isString()) - ptr = checkedCast(v.toString(), JSTRACE_STRING); + ptr = checkedCast(v.toString(), JS::TraceKind::String); else if (v.isObject()) - ptr = checkedCast(&v.toObject(), JSTRACE_OBJECT); + ptr = checkedCast(&v.toObject(), JS::TraceKind::Object); else if (v.isSymbol()) - ptr = checkedCast(v.toSymbol(), JSTRACE_SYMBOL); + ptr = checkedCast(v.toSymbol(), JS::TraceKind::Symbol); else - ptr = checkedCast(nullptr, JSTRACE_NULL); + ptr = checkedCast(nullptr, JS::TraceKind::Null); } -JSGCTraceKind +JS::TraceKind JS::GCCellPtr::outOfLineKind() const { - MOZ_ASSERT(JSGCTraceKind(ptr & JSTRACE_OUTOFLINE) == JSTRACE_OUTOFLINE); + MOZ_ASSERT((ptr & OutOfLineTraceKindMask) == OutOfLineTraceKindMask); MOZ_ASSERT(asCell()->isTenured()); return MapAllocToTraceKind(asCell()->asTenured().getAllocKind()); } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index ceb9c4b9fc..f1e682c5d0 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -25,7 +25,6 @@ #define FOR_EACH_GC_LAYOUT(D) \ /* PrettyName TypeName AddToCCKind */ \ - D(AccessorShape, js::AccessorShape, true) \ D(BaseShape, js::BaseShape, true) \ D(JitCode, js::jit::JitCode, true) \ D(LazyScript, js::LazyScript, true) \ @@ -71,6 +70,15 @@ enum State { COMPACT }; +// Map from base trace type to the trace kind. +template struct MapTypeToTraceKind {}; +#define EXPAND_DEF(name, type, _) \ + template <> struct MapTypeToTraceKind { \ + static const JS::TraceKind kind = JS::TraceKind::name; \ + }; +FOR_EACH_GC_LAYOUT(EXPAND_DEF); +#undef EXPAND_DEF + /* Map from C++ type to alloc kind. JSObject does not have a 1:1 mapping, so must use Arena::thingSize. */ template struct MapTypeToFinalizeKind {}; template <> struct MapTypeToFinalizeKind { static const AllocKind kind = AllocKind::SCRIPT; }; @@ -176,9 +184,6 @@ CanBeFinalizedInBackground(AllocKind kind, const Class* clasp) (!clasp->finalize || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE))); } -inline JSGCTraceKind -GetGCThingTraceKind(const void* thing); - // Fortunately, few places in the system need to deal with fully abstract // cells. In those places that do, we generally want to move to a layout // templated function as soon as possible. This template wraps the upcast @@ -196,28 +201,15 @@ GetGCThingTraceKind(const void* thing); #endif template auto -CallTyped(F f, JSGCTraceKind traceKind, Args&&... args) +CallTyped(F f, JS::TraceKind traceKind, Args&&... args) -> decltype(f. DEPENDENT_TEMPLATE_HINT operator()(mozilla::Forward(args)...)) { switch (traceKind) { - case JSTRACE_OBJECT: - return f. DEPENDENT_TEMPLATE_HINT operator()(mozilla::Forward(args)...); - case JSTRACE_SCRIPT: - return f. DEPENDENT_TEMPLATE_HINT operator()(mozilla::Forward(args)...); - case JSTRACE_STRING: - return f. DEPENDENT_TEMPLATE_HINT operator()(mozilla::Forward(args)...); - case JSTRACE_SYMBOL: - return f. DEPENDENT_TEMPLATE_HINT operator()(mozilla::Forward(args)...); - case JSTRACE_BASE_SHAPE: - return f. DEPENDENT_TEMPLATE_HINT operator()(mozilla::Forward(args)...); - case JSTRACE_JITCODE: - return f. DEPENDENT_TEMPLATE_HINT operator()(mozilla::Forward(args)...); - case JSTRACE_LAZY_SCRIPT: - return f. DEPENDENT_TEMPLATE_HINT operator()(mozilla::Forward(args)...); - case JSTRACE_SHAPE: - return f. DEPENDENT_TEMPLATE_HINT operator()(mozilla::Forward(args)...); - case JSTRACE_OBJECT_GROUP: - return f. DEPENDENT_TEMPLATE_HINT operator()(mozilla::Forward(args)...); +#define EXPAND_DEF(name, type, _) \ + case JS::TraceKind::name: \ + return f. DEPENDENT_TEMPLATE_HINT operator()(mozilla::Forward(args)...); + FOR_EACH_GC_LAYOUT(EXPAND_DEF); +#undef EXPAND_DEF default: MOZ_CRASH("Invalid trace kind in CallTyped."); } @@ -226,28 +218,15 @@ CallTyped(F f, JSGCTraceKind traceKind, Args&&... args) template auto -CallTyped(F f, void* thing, JSGCTraceKind traceKind, Args&&... args) +CallTyped(F f, void* thing, JS::TraceKind traceKind, Args&&... args) -> decltype(f(reinterpret_cast(0), mozilla::Forward(args)...)) { switch (traceKind) { - case JSTRACE_OBJECT: - return f(static_cast(thing), mozilla::Forward(args)...); - case JSTRACE_SCRIPT: - return f(static_cast(thing), mozilla::Forward(args)...); - case JSTRACE_STRING: - return f(static_cast(thing), mozilla::Forward(args)...); - case JSTRACE_SYMBOL: - return f(static_cast(thing), mozilla::Forward(args)...); - case JSTRACE_BASE_SHAPE: - return f(static_cast(thing), mozilla::Forward(args)...); - case JSTRACE_JITCODE: - return f(static_cast(thing), mozilla::Forward(args)...); - case JSTRACE_LAZY_SCRIPT: - return f(static_cast(thing), mozilla::Forward(args)...); - case JSTRACE_SHAPE: - return f(static_cast(thing), mozilla::Forward(args)...); - case JSTRACE_OBJECT_GROUP: - return f(static_cast(thing), mozilla::Forward(args)...); +#define EXPAND_DEF(name, type, _) \ + case JS::TraceKind::name: \ + return f(static_cast(thing), mozilla::Forward(args)...); + FOR_EACH_GC_LAYOUT(EXPAND_DEF); +#undef EXPAND_DEF default: MOZ_CRASH("Invalid trace kind in CallTyped."); } @@ -1152,9 +1131,9 @@ typedef HashSet GCChunkSet; typedef void (*IterateChunkCallback)(JSRuntime* rt, void* data, gc::Chunk* chunk); typedef void (*IterateZoneCallback)(JSRuntime* rt, void* data, JS::Zone* zone); typedef void (*IterateArenaCallback)(JSRuntime* rt, void* data, gc::Arena* arena, - JSGCTraceKind traceKind, size_t thingSize); + JS::TraceKind traceKind, size_t thingSize); typedef void (*IterateCellCallback)(JSRuntime* rt, void* data, void* thing, - JSGCTraceKind traceKind, size_t thingSize); + JS::TraceKind traceKind, size_t thingSize); /* * This function calls |zoneCallback| on every zone, |compartmentCallback| on @@ -1446,12 +1425,6 @@ class AutoEnterOOMUnsafeRegion class AutoEnterOOMUnsafeRegion {}; #endif /* DEBUG */ -// This tests whether something is inside the GGC's nursery only; -// use sparingly, mostly testing for any nursery, using IsInsideNursery, -// is appropriate. -bool -IsInsideGGCNursery(const gc::Cell* cell); - // A singly linked list of zones. class ZoneList { diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index d659db90b4..1fb2107783 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -26,16 +26,6 @@ GetGCObjectKind(const Class* clasp) return GetGCObjectKind(nslots); } -inline JSGCTraceKind -GetGCThingTraceKind(const void* thing) -{ - MOZ_ASSERT(thing); - const Cell* cell = static_cast(thing); - if (IsInsideNursery(cell)) - return JSTRACE_OBJECT; - return MapAllocToTraceKind(cell->asTenured().getAllocKind()); -} - inline void GCRuntime::poke() { diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index f0cddeae17..d5c7820d81 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -336,24 +336,20 @@ Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props) do { if (JSNewEnumerateOp enumerate = pobj->getOps()->enumerate) { - // This hook has the full control over what gets enumerated. AutoIdVector properties(cx); - if (!enumerate(cx, pobj, properties)) + bool enumerableOnly = !(flags & JSITER_HIDDEN); + if (!enumerate(cx, pobj, properties, enumerableOnly)) return false; RootedId id(cx); for (size_t n = 0; n < properties.length(); n++) { id = properties[n]; - bool enumerable = true; // The enumerate hook does not indicate whether the properties - // it returns are enumerable or not. There is no non-effectful - // way to determine this from the object, so carve out - // exceptions here for places where the property is not - // enumerable. - if (pobj->is() && id == NameToId(cx->names().length)) - enumerable = false; - + // it returns are enumerable or not. Since we already passed + // `enumerableOnly` to the hook to filter out non-enumerable + // properties, it doesn't really matter what we pass here. + bool enumerable = true; if (!Enumerate(cx, pobj, id, enumerable, flags, ht, props)) return false; } diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index 6c43f5fc11..5330364039 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -765,10 +765,6 @@ random_generateSeed() return seed.u64; } -static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL; -static const uint64_t RNG_ADDEND = 0xBLL; -static const uint64_t RNG_MASK = (1LL << 48) - 1; - /* * Math.random() support, lifted from java.util.Random.java. */ @@ -785,7 +781,7 @@ uint64_t js::random_next(uint64_t* rngState, int bits) { MOZ_ASSERT((*rngState & 0xffff000000000000ULL) == 0, "Bad rngState"); - MOZ_ASSERT(bits > 0 && bits <= 48, "bits is out of range"); + MOZ_ASSERT(bits > 0 && bits <= RNG_STATE_WIDTH, "bits is out of range"); if (*rngState == 0) { random_initState(rngState); @@ -795,7 +791,7 @@ js::random_next(uint64_t* rngState, int bits) nextstate += RNG_ADDEND; nextstate &= RNG_MASK; *rngState = nextstate; - return nextstate >> (48 - bits); + return nextstate >> (RNG_STATE_WIDTH - bits); } double @@ -1622,8 +1618,11 @@ js::InitMathClass(JSContext* cx, HandleObject obj) if (!Math) return nullptr; - if (!JS_DefineProperty(cx, obj, js_Math_str, Math, 0, JS_STUBGETTER, JS_STUBSETTER)) + if (!JS_DefineProperty(cx, obj, js_Math_str, Math, JSPROP_RESOLVING, + JS_STUBGETTER, JS_STUBSETTER)) + { return nullptr; + } if (!JS_DefineFunctions(cx, Math, math_static_methods)) return nullptr; if (!JS_DefineConstDoubles(cx, Math, math_constants)) diff --git a/js/src/jsmath.h b/js/src/jsmath.h index 5f63df5675..90a262af65 100644 --- a/js/src/jsmath.h +++ b/js/src/jsmath.h @@ -97,11 +97,18 @@ extern uint64_t random_next(uint64_t* rngState, int bits); static const double RNG_DSCALE = double(1LL << 53); +static const int RNG_STATE_WIDTH = 48; +static const int RNG_HIGH_BITS = 26; +static const int RNG_LOW_BITS = 27; +static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL; +static const uint64_t RNG_ADDEND = 0xBLL; +static const uint64_t RNG_MASK = (1LL << RNG_STATE_WIDTH) - 1; inline double random_nextDouble(uint64_t* rng) { - return double((random_next(rng, 26) << 27) + random_next(rng, 27)) / RNG_DSCALE; + return double((random_next(rng, RNG_HIGH_BITS) << RNG_LOW_BITS) + + random_next(rng, RNG_LOW_BITS)) / RNG_DSCALE; } extern double diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 71b7bb8be6..7ee8d4183d 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -457,8 +457,8 @@ js::num_parseInt(JSContext* cx, unsigned argc, Value* vp) } static const JSFunctionSpec number_functions[] = { - JS_SELF_HOSTED_FN(js_isNaN_str, "Global_isNaN", 1,0), - JS_SELF_HOSTED_FN(js_isFinite_str, "Global_isFinite", 1,0), + JS_SELF_HOSTED_FN(js_isNaN_str, "Global_isNaN", 1, JSPROP_RESOLVING), + JS_SELF_HOSTED_FN(js_isFinite_str, "Global_isFinite", 1, JSPROP_RESOLVING), JS_FS_END }; @@ -1201,20 +1201,22 @@ js::InitNumberClass(JSContext* cx, HandleObject obj) /* Number.parseInt should be the same function object as global parseInt. */ RootedId parseIntId(cx, NameToId(cx->names().parseInt)); - JSFunction *parseInt = DefineFunction(cx, global, parseIntId, num_parseInt, 2, 0); - if(!parseInt) + JSFunction* parseInt = DefineFunction(cx, global, parseIntId, num_parseInt, 2, + JSPROP_RESOLVING); + if (!parseInt) return nullptr; RootedValue parseIntValue(cx, ObjectValue(*parseInt)); - if(!DefineProperty(cx, ctor, parseIntId, parseIntValue, nullptr, nullptr, 0)) + if (!DefineProperty(cx, ctor, parseIntId, parseIntValue, nullptr, nullptr, 0)) return nullptr; /* Number.parseFloat should be the same function object as global parseFloat. */ RootedId parseFloatId(cx, NameToId(cx->names().parseFloat)); - JSFunction *parseFloat = DefineFunction(cx, global, parseFloatId, num_parseFloat, 1, 0); - if(!parseFloat) + JSFunction* parseFloat = DefineFunction(cx, global, parseFloatId, num_parseFloat, 1, + JSPROP_RESOLVING); + if (!parseFloat) return nullptr; RootedValue parseFloatValue(cx, ObjectValue(*parseFloat)); - if(!DefineProperty(cx, ctor, parseFloatId, parseFloatValue, nullptr, nullptr, 0)) + if (!DefineProperty(cx, ctor, parseFloatId, parseFloatValue, nullptr, nullptr, 0)) return nullptr; RootedValue valueNaN(cx, cx->runtime()->NaNValue); @@ -1222,9 +1224,9 @@ js::InitNumberClass(JSContext* cx, HandleObject obj) /* ES5 15.1.1.1, 15.1.1.2 */ if (!NativeDefineProperty(cx, global, cx->names().NaN, valueNaN, nullptr, nullptr, - JSPROP_PERMANENT | JSPROP_READONLY) || + JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING) || !NativeDefineProperty(cx, global, cx->names().Infinity, valueInfinity, nullptr, nullptr, - JSPROP_PERMANENT | JSPROP_READONLY)) + JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING)) { return nullptr; } @@ -1276,7 +1278,7 @@ js::NumberToCString(JSContext* cx, ToCStringBuf* cbuf, double d, int base/* = 10 } template -static JSString * +static JSString* NumberToStringWithBase(ExclusiveContext* cx, double d, int base) { ToCStringBuf cbuf; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 7daf4fed3f..12979f0667 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -275,418 +275,7 @@ js::Throw(JSContext* cx, JSObject* obj, unsigned errorNumber) -/*** Standard-compliant property definition (used by Object.defineProperty) **********************/ - -static bool -DefinePropertyOnObject(JSContext* cx, HandleNativeObject obj, HandleId id, - Handle desc, ObjectOpResult& result) -{ - /* 8.12.9 step 1. */ - RootedShape shape(cx); - MOZ_ASSERT(!obj->getOps()->lookupProperty); - if (!NativeLookupOwnProperty(cx, obj, id, &shape)) - return false; - - MOZ_ASSERT(!obj->getOps()->defineProperty); - - /* 8.12.9 steps 2-4. */ - if (!shape) { - bool extensible; - if (!IsExtensible(cx, obj, &extensible)) - return false; - if (!extensible) - return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE); - - if (desc.isGenericDescriptor() || desc.isDataDescriptor()) { - MOZ_ASSERT(!obj->getOps()->defineProperty); - RootedValue v(cx, desc.hasValue() ? desc.value() : UndefinedValue()); - unsigned attrs = desc.attributes() & (JSPROP_PERMANENT | JSPROP_ENUMERATE | JSPROP_READONLY); - - if (!desc.hasConfigurable()) - attrs |= JSPROP_PERMANENT; - if (!desc.hasWritable()) - attrs |= JSPROP_READONLY; - return NativeDefineProperty(cx, obj, id, v, nullptr, nullptr, attrs, result); - } - - MOZ_ASSERT(desc.isAccessorDescriptor()); - - unsigned attrs = desc.attributes() & (JSPROP_PERMANENT | JSPROP_ENUMERATE | - JSPROP_SHARED | JSPROP_GETTER | JSPROP_SETTER); - if (!desc.hasConfigurable()) - attrs |= JSPROP_PERMANENT; - return NativeDefineProperty(cx, obj, id, UndefinedHandleValue, - desc.getter(), desc.setter(), attrs, result); - } - - /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */ - RootedValue v(cx); - - bool shapeDataDescriptor = true, - shapeAccessorDescriptor = false, - shapeWritable = true, - shapeConfigurable = true, - shapeEnumerable = true, - shapeHasDefaultGetter = true, - shapeHasDefaultSetter = true, - shapeHasGetterValue = false, - shapeHasSetterValue = false; - uint8_t shapeAttributes = GetShapeAttributes(obj, shape); - if (!IsImplicitDenseOrTypedArrayElement(shape)) { - shapeDataDescriptor = shape->isDataDescriptor(); - shapeAccessorDescriptor = shape->isAccessorDescriptor(); - shapeWritable = shape->writable(); - shapeConfigurable = shape->configurable(); - shapeEnumerable = shape->enumerable(); - shapeHasDefaultGetter = shape->hasDefaultGetter(); - shapeHasDefaultSetter = shape->hasDefaultSetter(); - shapeHasGetterValue = shape->hasGetterValue(); - shapeHasSetterValue = shape->hasSetterValue(); - shapeAttributes = shape->attributes(); - } - - do { - if (desc.isAccessorDescriptor()) { - if (!shapeAccessorDescriptor) - break; - - if (desc.hasGetterObject()) { - if (!shape->hasGetterValue() || desc.getterObject() != shape->getterObject()) - break; - } - - if (desc.hasSetterObject()) { - if (!shape->hasSetterValue() || desc.setterObject() != shape->setterObject()) - break; - } - } else { - /* - * Determine the current value of the property once, if the current - * value might actually need to be used or preserved later. NB: we - * guard on whether the current property is a data descriptor to - * avoid calling a getter; we won't need the value if it's not a - * data descriptor. - */ - if (IsImplicitDenseOrTypedArrayElement(shape)) { - v = obj->getDenseOrTypedArrayElement(JSID_TO_INT(id)); - } else if (shape->isDataDescriptor()) { - /* - * We must rule out a non-configurable js::SetterOp-guarded - * property becoming a writable unguarded data property, since - * such a property can have its value changed to one the getter - * and setter preclude. - * - * A desc lacking writable but with value is a data descriptor - * and we must reject it as if it had writable: true if current - * is writable. - */ - if (!shape->configurable() && - (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()) && - desc.isDataDescriptor() && - (desc.hasWritable() ? desc.writable() : shape->writable())) - { - return result.fail(JSMSG_CANT_REDEFINE_PROP); - } - - if (!NativeGetExistingProperty(cx, obj, obj, shape, &v)) - return false; - } - - if (desc.isDataDescriptor()) { - if (!shapeDataDescriptor) - break; - - bool same; - if (desc.hasValue()) { - if (!SameValue(cx, desc.value(), v, &same)) - return false; - if (!same) { - /* - * Insist that a non-configurable js::GetterOp data - * property is frozen at exactly the last-got value. - * - * Duplicate the first part of the big conjunction that - * we tested above, rather than add a local bool flag. - * Likewise, don't try to keep shape->writable() in a - * flag we veto from true to false for non-configurable - * GetterOp-based data properties and test before the - * SameValue check later on in order to re-use that "if - * (!SameValue) return false" logic. - * - * This function is large and complex enough that it - * seems best to repeat a small bit of code and return - * result.fail() ASAP, instead of being clever. - */ - if (!shapeConfigurable && - (!shape->hasDefaultGetter() || !shape->hasDefaultSetter())) - { - return result.fail(JSMSG_CANT_REDEFINE_PROP); - } - break; - } - } - if (desc.hasWritable() && desc.writable() != shapeWritable) - break; - } else { - /* The only fields in desc will be handled below. */ - MOZ_ASSERT(desc.isGenericDescriptor()); - } - } - - if (desc.hasConfigurable() && desc.configurable() != shapeConfigurable) - break; - if (desc.hasEnumerable() && desc.enumerable() != shapeEnumerable) - break; - - /* The conditions imposed by step 5 or step 6 apply. */ - return result.succeed(); - } while (0); - - /* 8.12.9 step 7. */ - if (!shapeConfigurable) { - if ((desc.hasConfigurable() && desc.configurable()) || - (desc.hasEnumerable() && desc.enumerable() != shape->enumerable())) { - return result.fail(JSMSG_CANT_REDEFINE_PROP); - } - } - - bool callDelProperty = false; - - if (desc.isGenericDescriptor()) { - /* 8.12.9 step 8, no validation required */ - } else if (desc.isDataDescriptor() != shapeDataDescriptor) { - /* 8.12.9 step 9. */ - if (!shapeConfigurable) - return result.fail(JSMSG_CANT_REDEFINE_PROP); - } else if (desc.isDataDescriptor()) { - /* 8.12.9 step 10. */ - MOZ_ASSERT(shapeDataDescriptor); - if (!shapeConfigurable && !shape->writable()) { - if (desc.hasWritable() && desc.writable()) - return result.fail(JSMSG_CANT_REDEFINE_PROP); - if (desc.hasValue()) { - bool same; - if (!SameValue(cx, desc.value(), v, &same)) - return false; - if (!same) - return result.fail(JSMSG_CANT_REDEFINE_PROP); - } - } - - callDelProperty = !shapeHasDefaultGetter || !shapeHasDefaultSetter; - } else { - /* 8.12.9 step 11. */ - MOZ_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor()); - if (!shape->configurable()) { - // The hasSetterValue() and hasGetterValue() calls below ought to - // be redundant here, because accessor shapes should always have - // both JSPROP_GETTER and JSPROP_SETTER. But this is not the case - // currently; in particular Object.defineProperty(obj, key, {get: fn}) - // creates a property without JSPROP_SETTER (bug 1133315). - if (desc.hasSetterObject() && - desc.setterObject() != (shape->hasSetterValue() ? shape->setterObject() : nullptr)) - { - return result.fail(JSMSG_CANT_REDEFINE_PROP); - } - - if (desc.hasGetterObject() && - desc.getterObject() != (shape->hasGetterValue() ? shape->getterObject() : nullptr)) - { - return result.fail(JSMSG_CANT_REDEFINE_PROP); - } - } - } - - /* 8.12.9 step 12. */ - unsigned attrs; - GetterOp getter; - SetterOp setter; - if (desc.isGenericDescriptor()) { - unsigned changed = 0; - if (desc.hasConfigurable()) - changed |= JSPROP_PERMANENT; - if (desc.hasEnumerable()) - changed |= JSPROP_ENUMERATE; - - attrs = (shapeAttributes & ~changed) | (desc.attributes() & changed); - getter = IsImplicitDenseOrTypedArrayElement(shape) ? nullptr : shape->getter(); - setter = IsImplicitDenseOrTypedArrayElement(shape) ? nullptr : shape->setter(); - } else if (desc.isDataDescriptor()) { - /* Watch out for accessor -> data transformations here. */ - unsigned changed = JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED; - unsigned descAttrs = desc.attributes(); - if (desc.hasConfigurable()) - changed |= JSPROP_PERMANENT; - if (desc.hasEnumerable()) - changed |= JSPROP_ENUMERATE; - - if (desc.hasWritable()) { - changed |= JSPROP_READONLY; - } else if (!shapeDataDescriptor) { - changed |= JSPROP_READONLY; - descAttrs |= JSPROP_READONLY; - } - - if (desc.hasValue()) - v = desc.value(); - attrs = (descAttrs & changed) | (shapeAttributes & ~changed); - getter = nullptr; - setter = nullptr; - } else { - MOZ_ASSERT(desc.isAccessorDescriptor()); - - /* 8.12.9 step 12. */ - unsigned changed = 0; - if (desc.hasConfigurable()) - changed |= JSPROP_PERMANENT; - if (desc.hasEnumerable()) - changed |= JSPROP_ENUMERATE; - if (desc.hasGetterObject()) - changed |= JSPROP_GETTER | JSPROP_SHARED | JSPROP_READONLY | JSPROP_SHADOWABLE; - if (desc.hasSetterObject()) - changed |= JSPROP_SETTER | JSPROP_SHARED | JSPROP_READONLY | JSPROP_SHADOWABLE; - - attrs = (desc.attributes() & changed) | (shapeAttributes & ~changed); - if (desc.hasGetterObject()) - getter = desc.getter(); - else - getter = shapeHasGetterValue ? shape->getter() : nullptr; - if (desc.hasSetterObject()) - setter = desc.setter(); - else - setter = shapeHasSetterValue ? shape->setter() : nullptr; - } - - /* - * Since "data" properties implemented using native C functions may rely on - * side effects during setting, we must make them aware that they have been - * "assigned"; deleting the property before redefining it does the trick. - * See bug 539766, where we ran into problems when we redefined - * arguments.length without making the property aware that its value had - * been changed (which would have happened if we had deleted it before - * redefining it or we had invoked its setter to change its value). - */ - if (callDelProperty) { - ObjectOpResult ignored; - if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, ignored)) - return false; - } - - return NativeDefineProperty(cx, obj, id, v, getter, setter, attrs, result); -} - -/* ES6 20130308 draft 8.4.2.1 [[DefineOwnProperty]] */ -static bool -DefinePropertyOnArray(JSContext* cx, Handle arr, HandleId id, - Handle desc, ObjectOpResult& result) -{ - /* Step 2. */ - if (id == NameToId(cx->names().length)) { - // Canonicalize value, if necessary, before proceeding any further. It - // would be better if this were always/only done by ArraySetLength. - // But canonicalization may throw a RangeError (or other exception, if - // the value is an object with user-defined conversion semantics) - // before other attributes are checked. So as long as our internal - // defineProperty hook doesn't match the ECMA one, this duplicate - // checking can't be helped. - RootedValue v(cx); - if (desc.hasValue()) { - uint32_t newLen; - if (!CanonicalizeArrayLengthValue(cx, desc.value(), &newLen)) - return false; - v.setNumber(newLen); - } else { - v.setNumber(arr->length()); - } - - if (desc.hasConfigurable() && desc.configurable()) - return result.fail(JSMSG_CANT_REDEFINE_PROP); - if (desc.hasEnumerable() && desc.enumerable()) - return result.fail(JSMSG_CANT_REDEFINE_PROP); - - if (desc.isAccessorDescriptor()) - return result.fail(JSMSG_CANT_REDEFINE_PROP); - - unsigned attrs = arr->lookup(cx, id)->attributes(); - if (!arr->lengthIsWritable()) { - if (desc.hasWritable() && desc.writable()) - return result.fail(JSMSG_CANT_REDEFINE_PROP); - } else { - if (desc.hasWritable() && !desc.writable()) - attrs = attrs | JSPROP_READONLY; - } - - return ArraySetLength(cx, arr, id, attrs, v, result); - } - - /* Step 3. */ - uint32_t index; - if (IdIsIndex(id, &index)) { - /* Step 3b. */ - uint32_t oldLen = arr->length(); - - /* Steps 3a, 3e. */ - if (index >= oldLen && !arr->lengthIsWritable()) - return result.fail(JSMSG_CANT_APPEND_TO_ARRAY); - - /* Steps 3f-j. */ - return DefinePropertyOnObject(cx, arr, id, desc, result); - } - - /* Step 4. */ - return DefinePropertyOnObject(cx, arr, id, desc, result); -} - -// ES6 draft rev31 9.4.5.3 [[DefineOwnProperty]] -static bool -DefinePropertyOnTypedArray(JSContext* cx, HandleObject obj, HandleId id, - Handle desc, ObjectOpResult& result) -{ - MOZ_ASSERT(IsAnyTypedArray(obj)); - // Steps 3.a-c. - uint64_t index; - if (IsTypedArrayIndex(id, &index)) - return DefineTypedArrayElement(cx, obj, index, desc, result); - - // Step 4. - return DefinePropertyOnObject(cx, obj.as(), id, desc, result); -} - -bool -js::StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle desc, ObjectOpResult& result) -{ - if (obj->is()) { - Rooted arr(cx, &obj->as()); - return DefinePropertyOnArray(cx, arr, id, desc, result); - } - - if (IsAnyTypedArray(obj)) - return DefinePropertyOnTypedArray(cx, obj, id, desc, result); - - if (!MaybeConvertUnboxedObjectToNative(cx, obj)) - return false; - - if (obj->getOps()->lookupProperty) { - if (obj->is()) { - Rooted pd(cx, desc); - pd.object().set(obj); - return Proxy::defineProperty(cx, obj, id, pd, result); - } - return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE); - } - - return DefinePropertyOnObject(cx, obj.as(), id, desc, result); -} - -bool -js::StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle desc) -{ - ObjectOpResult success; - return StandardDefineProperty(cx, obj, id, desc, success) && - success.checkStrict(cx, obj, id); -} +/*** PropertyDescriptor operations and DefineProperties ******************************************/ bool CheckCallable(JSContext* cx, JSObject* obj, const char* fieldName) @@ -883,13 +472,14 @@ js::DefineProperties(JSContext* cx, HandleObject obj, HandleObject props) return false; for (size_t i = 0, len = ids.length(); i < len; i++) { - if (!StandardDefineProperty(cx, obj, ids[i], descs[i])) + if (!DefineProperty(cx, obj, ids[i], descs[i])) return false; } return true; } + /*** Seal and freeze *****************************************************************************/ static unsigned @@ -992,15 +582,15 @@ js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level) } // 8.a.i-ii. / 9.a.iii.3-4 - if (!StandardDefineProperty(cx, obj, id, desc)) + if (!DefineProperty(cx, obj, id, desc)) return false; } } // Ordinarily ArraySetLength handles this, but we're going behind its back // right now, so we must do this manually. Neither the custom property - // tree mutations nor the StandardDefineProperty call in the above code will - // do this for us. + // tree mutations nor the DefineProperty call in the above code will do + // this for us. // // ArraySetLength also implements the capacity <= length invariant for // arrays with non-writable length. We don't need to do anything special @@ -1507,7 +1097,7 @@ JS_CopyPropertyFrom(JSContext* cx, HandleId id, HandleObject target, if (!cx->compartment()->wrap(cx, &desc)) return false; - return StandardDefineProperty(cx, target, wrappedId, desc); + return DefineProperty(cx, target, wrappedId, desc); } JS_FRIEND_API(bool) @@ -1594,23 +1184,26 @@ js::CloneObject(JSContext* cx, HandleObject obj, Handle proto) } static bool -GetScriptArrayObjectElements(JSContext* cx, HandleArrayObject obj, AutoValueVector &values) +GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, AutoValueVector& values) { MOZ_ASSERT(!obj->isSingleton()); + MOZ_ASSERT(obj->is() || obj->is()); + + size_t length = GetAnyBoxedOrUnboxedArrayLength(obj); + if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), length)) + return false; if (obj->nonProxyIsExtensible()) { - MOZ_ASSERT(obj->slotSpan() == 0); + MOZ_ASSERT_IF(obj->is(), obj->as().slotSpan() == 0); - if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), obj->getDenseInitializedLength())) - return false; - - for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) - values[i].set(obj->getDenseElement(i)); + size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj); + for (size_t i = 0; i < initlen; i++) + values[i].set(GetAnyBoxedOrUnboxedDenseElement(obj, i)); } else { // Call site objects are frozen before they escape to script, which // converts their dense elements into data properties. - - for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) { + ArrayObject* aobj = &obj->as(); + for (Shape::Range r(aobj->lastProperty()); !r.empty(); r.popFront()) { Shape& shape = r.front(); if (shape.propid() == NameToId(cx->names().length)) continue; @@ -1624,12 +1217,7 @@ GetScriptArrayObjectElements(JSContext* cx, HandleArrayObject obj, AutoValueVect continue; uint32_t index = JSID_TO_INT(shape.propid()); - while (index >= values.length()) { - if (!values.append(MagicValue(JS_ELEMENTS_HOLE))) - return false; - } - - values[index].set(obj->getSlot(shape.slot())); + values[index].set(aobj->getSlot(shape.slot())); } } @@ -1700,40 +1288,27 @@ js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKin /* NB: Keep this in sync with XDRObjectLiteral. */ MOZ_ASSERT_IF(obj->isSingleton(), JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates()); - MOZ_ASSERT(obj->is() || obj->is() || obj->is()); - MOZ_ASSERT(cx->isInsideCurrentCompartment(obj)); + MOZ_ASSERT(obj->is() || obj->is() || + obj->is() || obj->is()); MOZ_ASSERT(newKind != SingletonObject); - if (obj->is()) { - HandleArrayObject aobj = obj.as(); - + if (obj->is() || obj->is()) { AutoValueVector values(cx); - if (!GetScriptArrayObjectElements(cx, aobj, values)) + if (!GetScriptArrayObjectElements(cx, obj, values)) return nullptr; // Deep clone any elements. - uint32_t initialized = aobj->getDenseInitializedLength(); - for (uint32_t i = 0; i < initialized; ++i) { + for (uint32_t i = 0; i < values.length(); ++i) { if (!DeepCloneValue(cx, values[i].address(), newKind)) return nullptr; } - RootedArrayObject clone(cx, NewDenseUnallocatedArray(cx, aobj->length(), - nullptr, newKind)); - if (!clone || !clone->ensureElements(cx, values.length())) - return nullptr; + ObjectGroup::NewArrayKind arrayKind = ObjectGroup::NewArrayKind::Normal; + if (obj->is() && obj->as().denseElementsAreCopyOnWrite()) + arrayKind = ObjectGroup::NewArrayKind::CopyOnWrite; - clone->setDenseInitializedLength(values.length()); - clone->initDenseElements(0, values.begin(), values.length()); - - if (aobj->denseElementsAreCopyOnWrite()) { - if (!ObjectElements::MakeElementsCopyOnWrite(cx, clone)) - return nullptr; - } else { - ObjectGroup::fixArrayGroup(cx, &clone->as()); - } - - return clone; + return ObjectGroup::newArrayObject(cx, values.begin(), values.length(), newKind, + arrayKind); } AutoIdValueVector properties(cx); @@ -1835,8 +1410,9 @@ js::XDRObjectLiteral(XDRState* xdr, MutableHandleObject obj) if (mode == XDR_ENCODE) { MOZ_ASSERT(obj->is() || obj->is() || - obj->is()); - isArray = obj->is() ? 1 : 0; + obj->is() || + obj->is()); + isArray = (obj->is() || obj->is()) ? 1 : 0; } if (!xdr->codeUint32(&isArray)) @@ -1847,69 +1423,39 @@ js::XDRObjectLiteral(XDRState* xdr, MutableHandleObject obj) RootedId tmpId(cx); if (isArray) { - uint32_t length; - RootedArrayObject aobj(cx); - - if (mode == XDR_ENCODE) { - aobj = &obj->as(); - length = aobj->length(); - } - - if (!xdr->codeUint32(&length)) - return false; - - if (mode == XDR_DECODE) { - obj.set(NewDenseUnallocatedArray(cx, length, nullptr, TenuredObject)); - if (!obj) - return false; - aobj = &obj->as(); - } - AutoValueVector values(cx); - if (mode == XDR_ENCODE && !GetScriptArrayObjectElements(cx, aobj, values)) + if (mode == XDR_ENCODE && !GetScriptArrayObjectElements(cx, obj, values)) return false; uint32_t initialized; - { - if (mode == XDR_ENCODE) - initialized = values.length(); - if (!xdr->codeUint32(&initialized)) - return false; - if (mode == XDR_DECODE) { - if (initialized) { - if (!aobj->ensureElements(cx, initialized)) - return false; - } - } - } + if (mode == XDR_ENCODE) + initialized = values.length(); + if (!xdr->codeUint32(&initialized)) + return false; + if (mode == XDR_DECODE && !values.appendN(MagicValue(JS_ELEMENTS_HOLE), initialized)) + return false; // Recursively copy dense elements. for (unsigned i = 0; i < initialized; i++) { - if (mode == XDR_ENCODE) - tmpValue = values[i]; - - if (!xdr->codeConstValue(&tmpValue)) + if (!xdr->codeConstValue(values[i])) return false; - - if (mode == XDR_DECODE) { - aobj->setDenseInitializedLength(i + 1); - aobj->initDenseElement(i, tmpValue); - } } uint32_t copyOnWrite; if (mode == XDR_ENCODE) - copyOnWrite = aobj->denseElementsAreCopyOnWrite(); + copyOnWrite = obj->is() && + obj->as().denseElementsAreCopyOnWrite(); if (!xdr->codeUint32(©OnWrite)) return false; if (mode == XDR_DECODE) { - if (copyOnWrite) { - if (!ObjectElements::MakeElementsCopyOnWrite(cx, aobj)) - return false; - } else { - ObjectGroup::fixArrayGroup(cx, aobj); - } + ObjectGroup::NewArrayKind arrayKind = copyOnWrite + ? ObjectGroup::NewArrayKind::CopyOnWrite + : ObjectGroup::NewArrayKind::Normal; + obj.set(ObjectGroup::newArrayObject(cx, values.begin(), values.length(), + TenuredObject, arrayKind)); + if (!obj) + return false; } return true; @@ -1967,65 +1513,6 @@ js::XDRObjectLiteral(XDRState* xdr, MutableHandleObject obj); template bool js::XDRObjectLiteral(XDRState* xdr, MutableHandleObject obj); -JSObject* -js::CloneObjectLiteral(JSContext* cx, HandleObject srcObj) -{ - if (srcObj->is()) { - AllocKind kind = GetBackgroundAllocKind(gc::GetGCObjectKind(srcObj->as().numFixedSlots())); - MOZ_ASSERT_IF(srcObj->isTenured(), kind == srcObj->asTenured().getAllocKind()); - - RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx)); - if (!proto) - return nullptr; - RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PlainObject::class_, - TaggedProto(proto))); - if (!group) - return nullptr; - - RootedPlainObject res(cx, NewObjectWithGroup(cx, group, kind, TenuredObject)); - if (!res) - return nullptr; - - // XXXbz Do we still need the reshape here? We got "kind" off the - // srcObj, no? See bug 1143270. - RootedShape newShape(cx, ReshapeForAllocKind(cx, srcObj->as().lastProperty(), - TaggedProto(proto), kind)); - if (!newShape || !res->setLastProperty(cx, newShape)) - return nullptr; - - return res; - } - - RootedArrayObject srcArray(cx, &srcObj->as()); - MOZ_ASSERT(srcArray->denseElementsAreCopyOnWrite()); - MOZ_ASSERT(srcArray->getElementsHeader()->ownerObject() == srcObj); - - size_t length = srcArray->as().length(); - RootedArrayObject res(cx, NewDenseFullyAllocatedArray(cx, length, nullptr, TenuredObject)); - if (!res) - return nullptr; - - RootedId id(cx); - RootedValue value(cx); - for (size_t i = 0; i < length; i++) { - // The only markable values in copy on write arrays are atoms, which - // can be freely copied between compartments. - value = srcArray->getDenseElement(i); - MOZ_ASSERT_IF(value.isMarkable(), - value.toGCThing()->isTenured() && - value.toGCThing()->asTenured().zoneFromAnyThread()->isAtomsZone()); - - id = INT_TO_JSID(i); - if (!DefineProperty(cx, res, id, value, nullptr, nullptr, JSPROP_ENUMERATE)) - return nullptr; - } - - if (!ObjectElements::MakeElementsCopyOnWrite(cx, res)) - return nullptr; - - return res; -} - void NativeObject::fillInAfterSwap(JSContext* cx, const Vector& values, void* priv) { @@ -3088,6 +2575,14 @@ js::GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, return true; } +bool +js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle desc) +{ + ObjectOpResult result; + return DefineProperty(cx, obj, id, desc, result) && + result.checkStrict(cx, obj, id); +} + bool js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle desc, ObjectOpResult& result) @@ -4126,7 +3621,11 @@ JSObject::traceChildren(JSTracer* trc) { GetObjectSlotNameFunctor func(nobj); JS::AutoTracingDetails ctx(trc, func); - TraceObjectSlots(trc, nobj, 0, nobj->slotSpan()); + JS::AutoTracingIndex index(trc); + for (uint32_t i = 0; i < nobj->slotSpan(); ++i) { + TraceManuallyBarrieredEdge(trc, nobj->getSlotRef(i).unsafeGet(), "object slot"); + ++index; + } } do { diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 4db0a37ef5..fce1c823c2 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -155,9 +155,8 @@ class JSObject : public js::gc::Cell return group_->lazy(); } - JSCompartment* compartment() const { - return group_->compartment(); - } + JSCompartment* compartment() const { return group_->compartment(); } + JSCompartment* maybeCompartment() const { return compartment(); } inline js::Shape* maybeShape() const; inline js::Shape* ensureShape(js::ExclusiveContext* cx); @@ -748,29 +747,7 @@ extern bool GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, MutableHandle desc); -/* - * ES6 [[DefineOwnProperty]]. Define a property on obj. - * - * If obj is an array, this follows ES5 15.4.5.1. - * If obj is any other native object, this follows ES5 8.12.9. - * If obj is a proxy, this calls the proxy handler's defineProperty method. - * Otherwise, this reports an error and returns false. - * - * Both StandardDefineProperty functions hew close to the ES5 spec. Note that - * the DefineProperty functions do not enforce some invariants mandated by ES6. - */ -extern bool -StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle descriptor, ObjectOpResult& result); - -/* - * Same as above except without the ObjectOpResult out-parameter. Throws a - * TypeError on failure. - */ -extern bool -StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle desc); - +/* ES6 [[DefineOwnProperty]]. Define a property on obj. */ extern bool DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle desc, ObjectOpResult& result); @@ -791,6 +768,9 @@ DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, HandleValu * When the 'result' out-param is omitted, the behavior is the same as above, except * that any failure results in a TypeError. */ +extern bool +DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle desc); + extern bool DefineProperty(ExclusiveContext* cx, HandleObject obj, HandleId id, HandleValue value, JSGetterOp getter = nullptr, @@ -1282,9 +1262,6 @@ template bool XDRObjectLiteral(XDRState* xdr, MutableHandleObject obj); -extern JSObject* -CloneObjectLiteral(JSContext* cx, HandleObject srcObj); - extern bool ReportGetterOnlyAssignment(JSContext* cx, bool strict); diff --git a/js/src/json.cpp b/js/src/json.cpp index 5ff47ee0dc..c718ce4785 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -710,16 +710,16 @@ Walk(JSContext* cx, HandleObject holder, HandleId name, HandleValue reviver, Mut if (!Walk(cx, obj, id, reviver, &newElement)) return false; + ObjectOpResult ignored; if (newElement.isUndefined()) { - /* Step 2a(iii)(2). */ - ObjectOpResult ignored; + /* Step 2a(iii)(2). The spec deliberately ignores strict failure. */ if (!DeleteProperty(cx, obj, id, ignored)) return false; } else { - /* Step 2a(iii)(3). */ - // XXX This definition should ignore success/failure, when - // our property-definition APIs indicate that. - if (!DefineProperty(cx, obj, id, newElement)) + /* Step 2a(iii)(3). The spec deliberately ignores strict failure. */ + Rooted desc(cx); + desc.setDataDescriptor(newElement, JSPROP_ENUMERATE); + if (!DefineProperty(cx, obj, id, desc, ignored)) return false; } } @@ -738,16 +738,16 @@ Walk(JSContext* cx, HandleObject holder, HandleId name, HandleValue reviver, Mut if (!Walk(cx, obj, id, reviver, &newElement)) return false; + ObjectOpResult ignored; if (newElement.isUndefined()) { - /* Step 2b(ii)(2). */ - ObjectOpResult ignored; + /* Step 2b(ii)(2). The spec deliberately ignores strict failure. */ if (!DeleteProperty(cx, obj, id, ignored)) return false; } else { - /* Step 2b(ii)(3). */ - // XXX This definition should ignore success/failure, when - // our property-definition APIs indicate that. - if (!DefineProperty(cx, obj, id, newElement)) + /* Step 2b(ii)(3). The spec deliberately ignores strict failure. */ + Rooted desc(cx); + desc.setDataDescriptor(newElement, JSPROP_ENUMERATE); + if (!DefineProperty(cx, obj, id, desc, ignored)) return false; } } @@ -900,9 +900,11 @@ js::InitJSONClass(JSContext* cx, HandleObject obj) if (!JSON) return nullptr; - if (!JS_DefineProperty(cx, global, js_JSON_str, JSON, 0, + if (!JS_DefineProperty(cx, global, js_JSON_str, JSON, JSPROP_RESOLVING, JS_STUBGETTER, JS_STUBSETTER)) + { return nullptr; + } if (!JS_DefineFunctions(cx, JSON, json_static_methods)) return nullptr; diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index a410400e51..35e5de10b8 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -3071,13 +3071,7 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, clone = CloneFunctionAndScript(cx, enclosingScope, innerFun, polluted); } } else { - /* - * Clone object literals emitted for the JSOP_NEWOBJECT opcode. We only emit that - * instead of the less-optimized JSOP_NEWINIT for self-hosted code or code compiled - * with JSOPTION_COMPILE_N_GO set. As we don't clone the latter type of code, this - * case should only ever be hit when cloning objects from self-hosted code. - */ - clone = CloneObjectLiteral(cx, obj); + clone = DeepCloneObjectLiteral(cx, obj, TenuredObject); } if (!clone || !objects.append(clone)) return nullptr; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index eb66b4bf19..1bf8482d5b 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -1060,6 +1060,7 @@ class JSScript : public js::gc::TenuredCell inline JSPrincipals* principals(); JSCompartment* compartment() const { return compartment_; } + JSCompartment* maybeCompartment() const { return compartment(); } void setVersion(JSVersion v) { version = v; } diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index e8c5c88048..424be77608 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -363,15 +363,15 @@ str_uneval(JSContext* cx, unsigned argc, Value* vp) #endif static const JSFunctionSpec string_functions[] = { - JS_FN(js_escape_str, str_escape, 1,0), - JS_FN(js_unescape_str, str_unescape, 1,0), + JS_FN(js_escape_str, str_escape, 1, JSPROP_RESOLVING), + JS_FN(js_unescape_str, str_unescape, 1, JSPROP_RESOLVING), #if JS_HAS_UNEVAL - JS_FN(js_uneval_str, str_uneval, 1,0), + JS_FN(js_uneval_str, str_uneval, 1, JSPROP_RESOLVING), #endif - JS_FN(js_decodeURI_str, str_decodeURI, 1,0), - JS_FN(js_encodeURI_str, str_encodeURI, 1,0), - JS_FN(js_decodeURIComponent_str, str_decodeURI_Component, 1,0), - JS_FN(js_encodeURIComponent_str, str_encodeURI_Component, 1,0), + JS_FN(js_decodeURI_str, str_decodeURI, 1, JSPROP_RESOLVING), + JS_FN(js_encodeURI_str, str_encodeURI, 1, JSPROP_RESOLVING), + JS_FN(js_decodeURIComponent_str, str_decodeURI_Component, 1, JSPROP_RESOLVING), + JS_FN(js_encodeURIComponent_str, str_encodeURI_Component, 1, JSPROP_RESOLVING), JS_FS_END }; @@ -388,8 +388,11 @@ str_enumerate(JSContext* cx, HandleObject obj) if (!str1) return false; value.setString(str1); - if (!DefineElement(cx, obj, i, value, nullptr, nullptr, STRING_ELEMENT_ATTRS)) + if (!DefineElement(cx, obj, i, value, nullptr, nullptr, + STRING_ELEMENT_ATTRS | JSPROP_RESOLVING)) + { return false; + } } return true; @@ -416,8 +419,11 @@ str_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) if (!str1) return false; RootedValue value(cx, StringValue(str1)); - if (!DefineElement(cx, obj, uint32_t(slot), value, nullptr, nullptr, STRING_ELEMENT_ATTRS)) + if (!DefineElement(cx, obj, uint32_t(slot), value, nullptr, nullptr, + STRING_ELEMENT_ATTRS | JSPROP_RESOLVING)) + { return false; + } *resolvedp = true; } return true; @@ -3604,7 +3610,7 @@ class SplitMatchResult { } /* anonymous namespace */ template -static ArrayObject* +static JSObject* SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher& splitMatch, HandleObjectGroup group) { @@ -3627,10 +3633,10 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher * assertEq(b.length, 0); */ if (!result.isFailure()) - return NewDenseEmptyArray(cx); + return NewFullyAllocatedArrayTryUseGroup(cx, group, 0); RootedValue v(cx, StringValue(str)); - return NewDenseCopiedArray(cx, 1, v.address()); + return NewCopiedArrayTryUseGroup(cx, group, v.address(), 1); } /* Step 12. */ @@ -3688,7 +3694,7 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher /* Step 13(c)(iii)(4). */ if (splits.length() == limit) - return NewDenseCopiedArray(cx, splits.length(), splits.begin()); + return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length()); /* Step 13(c)(iii)(5). */ lastEndIndex = endIndex; @@ -3709,15 +3715,13 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher if (!sub || !splits.append(StringValue(sub))) return nullptr; } else { - /* Only string entries have been accounted for so far. */ - AddTypePropertyId(cx, group, nullptr, JSID_VOID, UndefinedValue()); if (!splits.append(UndefinedValue())) return nullptr; } /* Step 13(c)(iii)(7)(d). */ if (splits.length() == limit) - return NewDenseCopiedArray(cx, splits.length(), splits.begin()); + return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length()); } } @@ -3731,12 +3735,12 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher return nullptr; /* Step 16. */ - return NewDenseCopiedArray(cx, splits.length(), splits.begin()); + return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length()); } // Fast-path for splitting a string into a character array via split(""). -static ArrayObject* -CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit) +static JSObject* +CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleObjectGroup group) { size_t strLength = str->length(); if (strLength == 0) @@ -3756,7 +3760,7 @@ CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit) splits.infallibleAppend(StringValue(sub)); } - return NewDenseCopiedArray(cx, splits.length(), splits.begin()); + return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length()); } namespace { @@ -3841,7 +3845,6 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp) RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); if (!group) return false; - AddTypePropertyId(cx, group, nullptr, JSID_VOID, TypeSet::StringType()); /* Step 5: Use the second argument as the split limit, if given. */ uint32_t limit; @@ -3872,10 +3875,9 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp) /* Step 9. */ if (limit == 0) { - JSObject* aobj = NewDenseEmptyArray(cx); + JSObject* aobj = NewFullyAllocatedArrayTryUseGroup(cx, group, 0); if (!aobj) return false; - aobj->setGroup(group); args.rval().setObject(*aobj); return true; } @@ -3883,10 +3885,9 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp) /* Step 10. */ if (!sepDefined) { RootedValue v(cx, StringValue(str)); - JSObject* aobj = NewDenseCopiedArray(cx, 1, v.address()); + JSObject* aobj = NewCopiedArrayTryUseGroup(cx, group, v.address(), 1); if (!aobj) return false; - aobj->setGroup(group); args.rval().setObject(*aobj); return true; } @@ -3898,7 +3899,7 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp) RootedObject aobj(cx); if (!re.initialized()) { if (sepstr->length() == 0) { - aobj = CharSplitHelper(cx, linearStr, limit); + aobj = CharSplitHelper(cx, linearStr, limit, group); } else { SplitStringMatcher matcher(cx, sepstr); aobj = SplitHelper(cx, linearStr, limit, matcher, group); @@ -3914,7 +3915,6 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp) return false; /* Step 16. */ - aobj->setGroup(group); args.rval().setObject(*aobj); return true; } @@ -3932,19 +3932,11 @@ js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, H uint32_t limit = UINT32_MAX; - RootedObject aobj(cx); - if (linearSep->length() == 0) { - aobj = CharSplitHelper(cx, linearStr, limit); - } else { - SplitStringMatcher matcher(cx, linearSep); - aobj = SplitHelper(cx, linearStr, limit, matcher, group); - } + if (linearSep->length() == 0) + return CharSplitHelper(cx, linearStr, limit, group); - if (!aobj) - return nullptr; - - aobj->setGroup(group); - return aobj; + SplitStringMatcher matcher(cx, linearSep); + return SplitHelper(cx, linearStr, limit, matcher, group); } /* diff --git a/js/src/jsweakmap.cpp b/js/src/jsweakmap.cpp index 46acb775d1..965c87bc6b 100644 --- a/js/src/jsweakmap.cpp +++ b/js/src/jsweakmap.cpp @@ -39,7 +39,10 @@ WeakMapBase::WeakMapBase(JSObject* memOf, JSCompartment* c) WeakMapBase::~WeakMapBase() { - MOZ_ASSERT(!isInList()); + MOZ_ASSERT(CurrentThreadIsGCSweeping() || CurrentThreadIsHandlingInitFailure()); + MOZ_ASSERT_IF(CurrentThreadIsGCSweeping(), !isInList()); + if (isInList()) + removeWeakMapFromList(this); } void diff --git a/js/src/jsweakmap.h b/js/src/jsweakmap.h index 0b3923ffe4..cf00158a74 100644 --- a/js/src/jsweakmap.h +++ b/js/src/jsweakmap.h @@ -92,7 +92,7 @@ class WeakMapBase { virtual void finish() = 0; // Object that this weak map is part of, if any. - RelocatablePtrObject memberOf; + HeapPtrObject memberOf; // Compartment that this weak map is part of. JSCompartment* compartment; diff --git a/js/src/proxy/DirectProxyHandler.cpp b/js/src/proxy/DirectProxyHandler.cpp index f67e1a852c..ca94f598da 100644 --- a/js/src/proxy/DirectProxyHandler.cpp +++ b/js/src/proxy/DirectProxyHandler.cpp @@ -39,7 +39,7 @@ DirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId i { assertEnteredPolicy(cx, proxy, id, SET); RootedObject target(cx, proxy->as().target()); - return StandardDefineProperty(cx, target, id, desc, result); + return DefineProperty(cx, target, id, desc, result); } bool diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index 25ec8f971d..43dcd986d3 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -774,10 +774,8 @@ js::InitProxyClass(JSContext* cx, HandleObject obj) if (!JS_DefineFunctions(cx, ctor, static_methods)) return nullptr; - if (!JS_DefineProperty(cx, obj, "Proxy", ctor, 0, - JS_STUBGETTER, JS_STUBSETTER)) { + if (!JS_DefineProperty(cx, obj, "Proxy", ctor, JSPROP_RESOLVING, JS_STUBGETTER, JS_STUBSETTER)) return nullptr; - } global->setConstructor(JSProto_Proxy, ObjectValue(*ctor)); return ctor; diff --git a/js/src/proxy/ScriptedDirectProxyHandler.cpp b/js/src/proxy/ScriptedDirectProxyHandler.cpp index 96901a8681..cb08c4c797 100644 --- a/js/src/proxy/ScriptedDirectProxyHandler.cpp +++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp @@ -570,7 +570,7 @@ ScriptedDirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, Ha // step 8 if (trap.isUndefined()) - return StandardDefineProperty(cx, target, id, desc, result); + return DefineProperty(cx, target, id, desc, result); // step 9 RootedValue descObj(cx); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index b4085fff40..1e95703dd6 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -131,7 +131,6 @@ static bool enableBaseline = false; static bool enableIon = false; static bool enableAsmJS = false; static bool enableNativeRegExp = false; -static bool enableUnboxedObjects = false; static bool enableUnboxedArrays = false; #ifdef JS_GC_ZEAL static char gZealStr[128]; @@ -987,7 +986,7 @@ CacheEntry_setBytecode(JSContext* cx, HandleObject cache, uint8_t* buffer, uint3 if (!arrayBuffer) return false; - SetReservedSlot(cache, CacheEntry_BYTECODE, OBJECT_TO_JSVAL(arrayBuffer)); + SetReservedSlot(cache, CacheEntry_BYTECODE, ObjectValue(*arrayBuffer)); return true; } @@ -5289,7 +5288,7 @@ static void InitDOMObject(HandleObject obj) { /* Fow now just initialize to a constant we can check. */ - SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL((void*)0x1234)); + SetReservedSlot(obj, DOM_OBJECT_SLOT, PrivateValue((void*)0x1234)); } static bool @@ -5758,16 +5757,17 @@ SetRuntimeOptions(JSRuntime* rt, const OptionParser& op) enableIon = !op.getBoolOption("no-ion"); enableAsmJS = !op.getBoolOption("no-asmjs"); enableNativeRegExp = !op.getBoolOption("no-native-regexp"); - enableUnboxedObjects = op.getBoolOption("unboxed-objects"); enableUnboxedArrays = op.getBoolOption("unboxed-arrays"); JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline) .setIon(enableIon) .setAsmJS(enableAsmJS) .setNativeRegExp(enableNativeRegExp) - .setUnboxedObjects(enableUnboxedObjects) .setUnboxedArrays(enableUnboxedArrays); + if (op.getBoolOption("no-unboxed-objects")) + jit::js_JitOptions.disableUnboxedObjects = true; + if (const char* str = op.getStringOption("ion-scalar-replacement")) { if (strcmp(str, "on") == 0) jit::js_JitOptions.disableScalarReplacement = false; @@ -5971,7 +5971,6 @@ SetWorkerRuntimeOptions(JSRuntime* rt) .setIon(enableIon) .setAsmJS(enableAsmJS) .setNativeRegExp(enableNativeRegExp) - .setUnboxedObjects(enableUnboxedObjects) .setUnboxedArrays(enableUnboxedArrays); rt->setOffthreadIonCompilationEnabled(offthreadCompilation); rt->profilingScripts = enableDisassemblyDumps; @@ -6148,7 +6147,7 @@ main(int argc, char** argv, char** envp) || !op.addBoolOption('\0', "no-ion", "Disable IonMonkey") || !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation") || !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation") - || !op.addBoolOption('\0', "unboxed-objects", "Allow creating unboxed plain objects") + || !op.addBoolOption('\0', "no-unboxed-objects", "Disable creating unboxed plain objects") || !op.addBoolOption('\0', "unboxed-arrays", "Allow creating unboxed arrays") || !op.addStringOption('\0', "ion-scalar-replacement", "on/off", "Scalar Replacement (default: on, off to disable)") diff --git a/js/src/tests/ecma_6/Class/methDefn.js b/js/src/tests/ecma_6/Class/methDefn.js index cfaaa78ca5..3da1dbbb8e 100644 --- a/js/src/tests/ecma_6/Class/methDefn.js +++ b/js/src/tests/ecma_6/Class/methDefn.js @@ -115,6 +115,10 @@ assertEq(b.enumerable, true); assertEq(b.writable, true); assertEq(b.value(), 4); +// prototype property +assertEq(a.b.prototype, undefined); +assertEq(a.b.hasOwnProperty("prototype"), false); + // Defining several methods using eval. var code = "({"; for (i = 0; i < 1000; i++) diff --git a/js/src/tests/ecma_6/Class/methDefnGen.js b/js/src/tests/ecma_6/Class/methDefnGen.js index 596d55477f..8afd94198a 100644 --- a/js/src/tests/ecma_6/Class/methDefnGen.js +++ b/js/src/tests/ecma_6/Class/methDefnGen.js @@ -66,6 +66,9 @@ assertEq(next.done, true); assertEq(next.value.hello, 2); assertEq(next.value.world, 3); +// prototype property +assertEq(b.g.hasOwnProperty("prototype"), true); + // Strict mode a = {*b(c){"use strict";yield c;}}; assertEq(a.b(1).next().value, 1); diff --git a/js/src/tests/ecma_6/Class/methodsPrototype.js b/js/src/tests/ecma_6/Class/methodsPrototype.js new file mode 100644 index 0000000000..65d5f465d7 --- /dev/null +++ b/js/src/tests/ecma_6/Class/methodsPrototype.js @@ -0,0 +1,45 @@ +var test = ` +class TestClass { + constructor() { } + method() { } + get getter() { } + set setter(x) { } + *generator() { } + static staticMethod() { } + static get staticGetter() { } + static set staticSetter(x) { } + static *staticGenerator() { } +} + +var test = new TestClass(); + +var hasPrototype = [ + test.constructor, + test.generator, + TestClass.staticGenerator +] + +for (var fun of hasPrototype) { + assertEq(fun.hasOwnProperty('prototype'), true); +} + +var hasNoPrototype = [ + test.method, + Object.getOwnPropertyDescriptor(test.__proto__, 'getter').get, + Object.getOwnPropertyDescriptor(test.__proto__, 'setter').set, + TestClass.staticMethod, + Object.getOwnPropertyDescriptor(TestClass, 'staticGetter').get, + Object.getOwnPropertyDescriptor(TestClass, 'staticSetter').set, +] + +for (var fun of hasNoPrototype) { + assertEq(fun.hasOwnProperty('prototype'), false); +} + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "OK"); diff --git a/js/src/tests/lib/jittests.py b/js/src/tests/lib/jittests.py index 7fb9dce3cf..180234153c 100755 --- a/js/src/tests/lib/jittests.py +++ b/js/src/tests/lib/jittests.py @@ -116,6 +116,8 @@ class Test: self.tz_pacific = False # True means force Pacific time for the test self.test_also_noasmjs = False # True means run with and without asm.js # enabled. + self.test_also = [] # List of other configurations to test with. + self.test_join = [] # List of other configurations to test with all existing variants. self.expect_error = '' # Errors to expect and consider passing self.expect_status = 0 # Exit status to expect from shell @@ -129,6 +131,8 @@ class Test: t.valgrind = self.valgrind t.tz_pacific = self.tz_pacific t.test_also_noasmjs = self.test_also_noasmjs + t.test_also = self.test_also + t.test_join = self.test_join t.expect_error = self.expect_error t.expect_status = self.expect_status return t @@ -139,12 +143,14 @@ class Test: return t def copy_variants(self, variants): - # If the tests are flagged with the |jit-test| test-also-noasmjs flags, - # then we duplicate the variants such that the test can be used both - # with the interpreter and asmjs. This is a simple way to check for - # differential behaviour. - if self.test_also_noasmjs: - variants = variants + [['--no-asmjs']] + # Append variants to be tested in addition to the current set of tests. + variants = variants + self.test_also + + # For each existing variant, duplicates it for each list of options in + # test_join. This will multiply the number of variants by 2 for set of + # options. + for join_opts in self.test_join: + variants = variants + [ opts + join_opts for opts in variants ]; # For each list of jit flags, make a copy of the test. return [self.copy_and_extend_jitflags(v) for v in variants] @@ -201,7 +207,12 @@ class Test: elif name == 'tz-pacific': test.tz_pacific = True elif name == 'test-also-noasmjs': - test.test_also_noasmjs = options.can_test_also_noasmjs + if options.can_test_also_noasmjs: + test.test_also.append(['--no-asmjs']) + elif name.startswith('test-also='): + test.test_also.append([name[len('test-also='):]]) + elif name.startswith('test-join='): + test.test_join.append([name[len('test-join='):]]) elif name == 'ion-eager': test.jitflags.append('--ion-eager') elif name == 'dump-bytecode': diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index eda09dff5e..8c67b7ea43 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -355,7 +355,7 @@ args_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) { Rooted argsobj(cx, &obj->as()); - unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; + unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE | JSPROP_RESOLVING; if (JSID_IS_INT(id)) { uint32_t arg = uint32_t(JSID_TO_INT(id)); if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg)) @@ -490,6 +490,7 @@ strictargs_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp setter = CastAsSetterOp(argsobj->global().getThrowTypeError()); } + attrs |= JSPROP_RESOLVING; if (!NativeDefineProperty(cx, argsobj, id, UndefinedHandleValue, getter, setter, attrs)) return false; diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index c307c5b9c6..0cdc080579 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -42,6 +42,7 @@ #include "js/MemoryMetrics.h" #include "vm/GlobalObject.h" #include "vm/Interpreter.h" +#include "vm/SharedArrayObject.h" #include "vm/WrapperObject.h" #include "jsatominlines.h" @@ -1288,6 +1289,14 @@ js::UnwrapArrayBufferView(JSObject* obj) return nullptr; } +JS_FRIEND_API(JSObject*) +js::UnwrapSharedArrayBufferView(JSObject* obj) +{ + if (JSObject* unwrapped = CheckedUnwrap(obj)) + return unwrapped->is() ? unwrapped : nullptr; + return nullptr; +} + JS_FRIEND_API(uint32_t) JS_GetArrayBufferByteLength(JSObject* obj) { @@ -1349,6 +1358,13 @@ JS_NewArrayBuffer(JSContext* cx, uint32_t nbytes) return ArrayBufferObject::create(cx, nbytes); } +JS_FRIEND_API(JSObject*) +JS_NewSharedArrayBuffer(JSContext* cx, uint32_t nbytes) +{ + MOZ_ASSERT(nbytes <= INT32_MAX); + return SharedArrayBufferObject::New(cx, nbytes); +} + JS_PUBLIC_API(JSObject*) JS_NewArrayBufferWithContents(JSContext* cx, size_t nbytes, void* data) { @@ -1379,6 +1395,14 @@ js::UnwrapArrayBuffer(JSObject* obj) return nullptr; } +JS_FRIEND_API(JSObject*) +js::UnwrapSharedArrayBuffer(JSObject* obj) +{ + if (JSObject* unwrapped = CheckedUnwrap(obj)) + return unwrapped->is() ? unwrapped : nullptr; + return nullptr; +} + JS_PUBLIC_API(void*) JS_StealArrayBufferContents(JSContext* cx, HandleObject objArg) { diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 8b91a0e800..f75ee5a079 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -3188,7 +3188,7 @@ Debugger::construct(JSContext* cx, unsigned argc, Value* vp) Debugger* debugger; { /* Construct the underlying C++ object. */ - auto dbg = cx->make_unique(cx, obj.get()); + AutoInitGCManagedObject dbg(cx->make_unique(cx, obj.get())); if (!dbg || !dbg->init(cx)) return false; @@ -7002,7 +7002,7 @@ DebuggerObject_defineProperty(JSContext* cx, unsigned argc, Value* vp) return false; ErrorCopier ec(ac); - if (!StandardDefineProperty(cx, obj, id, desc)) + if (!DefineProperty(cx, obj, id, desc)) return false; } @@ -7045,7 +7045,7 @@ DebuggerObject_defineProperties(JSContext* cx, unsigned argc, Value* vp) ErrorCopier ec(ac); for (size_t i = 0; i < n; i++) { - if (!StandardDefineProperty(cx, obj, ids[i], descs[i])) + if (!DefineProperty(cx, obj, ids[i], descs[i])) return false; } } diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index bb3db740a5..c06100621f 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -329,7 +329,7 @@ GlobalObject::initStandardClasses(JSContext* cx, Handle global) { /* Define a top-level property 'undefined' with the undefined value. */ if (!DefineProperty(cx, global, cx->names().undefined, UndefinedHandleValue, - nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY)) + nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING)) { return false; } diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h index 510dfcc194..eed6339b56 100644 --- a/js/src/vm/Interpreter-inl.h +++ b/js/src/vm/Interpreter-inl.h @@ -22,6 +22,7 @@ #include "vm/ScopeObject-inl.h" #include "vm/Stack-inl.h" #include "vm/String-inl.h" +#include "vm/UnboxedObject-inl.h" namespace js { diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 38f6c82a61..b9ddcc95e9 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -4677,8 +4677,11 @@ js::NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc, group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Object); if (!group) return nullptr; - if (group->maybePreliminaryObjects()) + if (group->maybePreliminaryObjects()) { group->maybePreliminaryObjects()->maybeAnalyze(cx, group); + if (group->maybeUnboxedLayout()) + group->maybeUnboxedLayout()->setAllocationSite(script, pc); + } if (group->shouldPreTenure() || group->maybePreliminaryObjects()) newKind = TenuredObject; diff --git a/js/src/vm/JSONParser.cpp b/js/src/vm/JSONParser.cpp index 05f8d48ca5..2c90c9ab9c 100644 --- a/js/src/vm/JSONParser.cpp +++ b/js/src/vm/JSONParser.cpp @@ -598,13 +598,11 @@ JSONParserBase::finishArray(MutableHandleValue vp, ElementVector& elements) { MOZ_ASSERT(&elements == &stack.back().elements()); - ArrayObject* obj = NewDenseCopiedArray(cx, elements.length(), elements.begin()); + JSObject* obj = ObjectGroup::newArrayObject(cx, elements.begin(), elements.length(), + GenericObject); if (!obj) return false; - /* Try to assign a new group to the array according to its elements. */ - ObjectGroup::fixArrayGroup(cx, obj); - vp.setObject(*obj); if (!freeElements.append(&elements)) return false; diff --git a/js/src/vm/MemoryMetrics.cpp b/js/src/vm/MemoryMetrics.cpp index fb891aac4c..fe5af0fc1a 100644 --- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -346,7 +346,7 @@ StatsCompartmentCallback(JSRuntime* rt, void* data, JSCompartment* compartment) static void StatsArenaCallback(JSRuntime* rt, void* data, gc::Arena* arena, - JSGCTraceKind traceKind, size_t thingSize) + JS::TraceKind traceKind, size_t thingSize) { RuntimeStats* rtStats = static_cast(data)->rtStats; @@ -401,14 +401,14 @@ AddClassInfo(Granularity granularity, CompartmentStats* cStats, const char* clas // profile speed for complex pages such as gmail.com. template static void -StatsCellCallback(JSRuntime* rt, void* data, void* thing, JSGCTraceKind traceKind, +StatsCellCallback(JSRuntime* rt, void* data, void* thing, JS::TraceKind traceKind, size_t thingSize) { StatsClosure* closure = static_cast(data); RuntimeStats* rtStats = closure->rtStats; ZoneStats* zStats = rtStats->currZoneStats; switch (traceKind) { - case JSTRACE_OBJECT: { + case JS::TraceKind::Object: { JSObject* obj = static_cast(thing); CompartmentStats* cStats = GetCompartmentStats(obj->compartment()); JS::ClassInfo info; // This zeroes all the sizes. @@ -429,7 +429,7 @@ StatsCellCallback(JSRuntime* rt, void* data, void* thing, JSGCTraceKind traceKin break; } - case JSTRACE_SCRIPT: { + case JS::TraceKind::Script: { JSScript* script = static_cast(thing); CompartmentStats* cStats = GetCompartmentStats(script->compartment()); cStats->scriptsGCHeap += thingSize; @@ -469,7 +469,7 @@ StatsCellCallback(JSRuntime* rt, void* data, void* thing, JSGCTraceKind traceKin break; } - case JSTRACE_STRING: { + case JS::TraceKind::String: { JSString* str = static_cast(thing); JS::StringInfo info; @@ -499,11 +499,11 @@ StatsCellCallback(JSRuntime* rt, void* data, void* thing, JSGCTraceKind traceKin break; } - case JSTRACE_SYMBOL: + case JS::TraceKind::Symbol: zStats->symbolsGCHeap += thingSize; break; - case JSTRACE_BASE_SHAPE: { + case JS::TraceKind::BaseShape: { BaseShape* base = static_cast(thing); CompartmentStats* cStats = GetCompartmentStats(base->compartment()); @@ -519,20 +519,20 @@ StatsCellCallback(JSRuntime* rt, void* data, void* thing, JSGCTraceKind traceKin break; } - case JSTRACE_JITCODE: { + case JS::TraceKind::JitCode: { zStats->jitCodesGCHeap += thingSize; // The code for a script is counted in ExecutableAllocator::sizeOfCode(). break; } - case JSTRACE_LAZY_SCRIPT: { + case JS::TraceKind::LazyScript: { LazyScript* lazy = static_cast(thing); zStats->lazyScriptsGCHeap += thingSize; zStats->lazyScriptsMallocHeap += lazy->sizeOfExcludingThis(rtStats->mallocSizeOf_); break; } - case JSTRACE_SHAPE: { + case JS::TraceKind::Shape: { Shape* shape = static_cast(thing); CompartmentStats* cStats = GetCompartmentStats(shape->compartment()); JS::ClassInfo info; // This zeroes all the sizes. @@ -550,7 +550,7 @@ StatsCellCallback(JSRuntime* rt, void* data, void* thing, JSGCTraceKind traceKin break; } - case JSTRACE_OBJECT_GROUP: { + case JS::TraceKind::ObjectGroup: { ObjectGroup* group = static_cast(thing); zStats->objectGroupsGCHeap += thingSize; zStats->objectGroupsMallocHeap += group->sizeOfExcludingThis(rtStats->mallocSizeOf_); diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h index 88bb01e3fc..323c07fcfa 100644 --- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -240,29 +240,6 @@ NativeObject::getDenseOrTypedArrayElement(uint32_t idx) return getDenseElement(idx); } -inline void -NativeObject::initDenseElementsUnbarriered(uint32_t dstStart, const Value* src, uint32_t count) { - /* - * For use by parallel threads, which since they cannot see nursery - * things do not require a barrier. - */ - MOZ_ASSERT(dstStart + count <= getDenseCapacity()); - MOZ_ASSERT(!denseElementsAreCopyOnWrite()); -#ifdef DEBUG - /* - * This asserts a global invariant: parallel code does not - * observe objects inside the generational GC's nursery. - */ - MOZ_ASSERT(!gc::IsInsideGGCNursery(this)); - for (uint32_t index = 0; index < count; ++index) { - const Value& value = src[index]; - if (value.isMarkable()) - MOZ_ASSERT(!gc::IsInsideGGCNursery(static_cast(value.toGCThing()))); - } -#endif - memcpy(&elements_[dstStart], src, count * sizeof(HeapSlot)); -} - /* static */ inline NativeObject* NativeObject::copy(ExclusiveContext* cx, gc::AllocKind kind, gc::InitialHeap heap, HandleNativeObject templateObject) diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index aa0b303d4a..ab65d86ae9 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -357,6 +357,8 @@ NativeObject::setLastPropertyMakeNative(ExclusiveContext* cx, Shape* shape) size_t oldSpan = shape->numFixedSlots(); size_t newSpan = shape->slotSpan(); + initializeSlotRange(0, oldSpan); + // A failure at this point will leave the object as a mutant, and we // can't recover. if (oldSpan != newSpan && !updateSlotsForSpan(cx, oldSpan, newSpan)) @@ -1206,9 +1208,80 @@ GetExistingPropertyValue(ExclusiveContext* cx, HandleNativeObject obj, HandleId } if (!cx->shouldBeJSContext()) return false; + + MOZ_ASSERT(shape->propid() == id); + MOZ_ASSERT(obj->contains(cx, shape)); + return GetExistingProperty(cx->asJSContext(), obj, obj, shape, vp); } +/* + * If ES6 draft rev 37 9.1.6.3 ValidateAndApplyPropertyDescriptor step 4 would + * return early, because desc is redundant with an existing own property obj[id], + * then set *redundant = true and return true. + */ +static bool +DefinePropertyIsRedundant(ExclusiveContext* cx, HandleNativeObject obj, HandleId id, + HandleShape shape, unsigned shapeAttrs, + Handle desc, bool *redundant) +{ + *redundant = false; + + if (desc.hasConfigurable() && desc.configurable() != ((shapeAttrs & JSPROP_PERMANENT) == 0)) + return true; + if (desc.hasEnumerable() && desc.enumerable() != ((shapeAttrs & JSPROP_ENUMERATE) != 0)) + return true; + if (desc.isDataDescriptor()) { + if ((shapeAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0) + return true; + if (desc.hasWritable() && desc.writable() != ((shapeAttrs & JSPROP_READONLY) == 0)) + return true; + if (desc.hasValue()) { + // Get the current value of the existing property. + RootedValue currentValue(cx); + if (!IsImplicitDenseOrTypedArrayElement(shape) && + shape->hasSlot() && + shape->hasDefaultGetter()) + { + // Inline GetExistingPropertyValue in order to omit a type + // correctness assertion that's too strict for this particular + // call site. For details, see bug 1125624 comments 13-16. + currentValue.set(obj->getSlot(shape->slot())); + } else { + if (!GetExistingPropertyValue(cx, obj, id, shape, ¤tValue)) + return false; + } + + // The specification calls for SameValue here, but it seems to be a + // bug. See . + if (desc.value() != currentValue) + return true; + } + + GetterOp existingGetterOp = + IsImplicitDenseOrTypedArrayElement(shape) ? nullptr : shape->getter(); + if (desc.getter() != existingGetterOp) + return true; + + SetterOp existingSetterOp = + IsImplicitDenseOrTypedArrayElement(shape) ? nullptr : shape->setter(); + if (desc.setter() != existingSetterOp) + return true; + } else { + if (desc.hasGetterObject()) { + if (!(shapeAttrs & JSPROP_GETTER) || desc.getterObject() != shape->getterObject()) + return true; + } + if (desc.hasSetterObject()) { + if (!(shapeAttrs & JSPROP_SETTER) || desc.setterObject() != shape->setterObject()) + return true; + } + } + + *redundant = true; + return true; +} + bool js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id, Handle desc_, @@ -1255,26 +1328,17 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId // distinguish the two cases, we note that when resolving, the // property won't already exist; whereas the first time it is // redefined, it will. - if (obj->containsPure(id)) + if ((desc_.attributes() & JSPROP_RESOLVING) == 0) obj->as().markLengthOverridden(); } } // 9.1.6.1 OrdinaryDefineOwnProperty steps 1-2. RootedShape shape(cx); - if (desc_.hasValue()) { - // If we did a normal lookup here, it would cause resolve hook recursion in - // the following case. Suppose the first script we run in a lazy global is - // |parseInt()|. - // - js::InitNumberClass is called to resolve parseInt. - // - js::InitNumberClass tries to define the Number constructor on the - // global. - // - We end up here. - // - This lookup for 'Number' triggers the global resolve hook. - // - js::InitNumberClass is called again, this time to resolve Number. - // - It creates a second Number constructor, which trips an assertion. - // - // Therefore we do a special lookup that does not call the resolve hook. + if (desc_.attributes() & JSPROP_RESOLVING) { + // We are being called from a resolve or enumerate hook to reify a + // lazily-resolved property. To avoid reentering the resolve hook and + // recursing forever, skip the resolve hook when doing this lookup. NativeLookupOwnPropertyNoResolve(cx, obj, id, &shape); } else { if (!NativeLookupOwnProperty(cx, obj, id, &shape)) @@ -1306,6 +1370,25 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId MOZ_ASSERT(shape); + // Steps 3-4. (Step 3 is a special case of step 4.) We use shapeAttrs as a + // stand-in for shape in many places below, since shape might not be a + // pointer to a real Shape (see IsImplicitDenseOrTypedArrayElement). + unsigned shapeAttrs = GetShapeAttributes(obj, shape); + bool redundant; + if (!DefinePropertyIsRedundant(cx, obj, id, shape, shapeAttrs, desc, &redundant)) + return false; + if (redundant) { + // In cases involving JSOP_NEWOBJECT and JSOP_INITPROP, obj can have a + // type for this property that doesn't match the value in the slot. + // Update the type here, even though this DefineProperty call is + // otherwise a no-op. (See bug 1125624 comment 13.) + if (!IsImplicitDenseOrTypedArrayElement(shape) && desc.hasValue()) { + if (!UpdateShapeTypeAndValue(cx, obj, shape, desc.value())) + return false; + } + return result.succeed(); + } + // Non-standard hack: Allow redefining non-configurable properties if // JSPROP_REDEFINE_NONCONFIGURABLE is set _and_ the object is a non-DOM // global. The idea is that a DOM object can never have such a thing on @@ -1316,12 +1399,7 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId obj->is() && !obj->getClass()->isDOMClass(); - // Steps 3-4 are redundant. - - // Step 5. We use shapeAttrs as a stand-in for shape in many places below - // since shape might not be a pointer to a real Shape (see - // IsImplicitDenseOrTypedArrayElement). - unsigned shapeAttrs = GetShapeAttributes(obj, shape); + // Step 5. if (!IsConfigurable(shapeAttrs) && !skipRedefineChecks) { if (desc.hasConfigurable() && desc.configurable()) return result.fail(JSMSG_CANT_REDEFINE_PROP); diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h index f7f325b0b7..553840f5b8 100644 --- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -291,21 +291,6 @@ static inline bool IsObjectValueInCompartment(Value v, JSCompartment* comp); #endif -/* - * NOTE: This is a placeholder for bug 619558. - * - * Run a post write barrier that encompasses multiple contiguous slots in a - * single step. - */ -inline void -DenseRangeWriteBarrierPost(JSRuntime* rt, NativeObject* obj, uint32_t start, uint32_t count) -{ - if (count > 0) { - JS::shadow::Runtime* shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt); - shadowRuntime->gcStoreBufferPtr()->putSlotFromAnyThread(obj, HeapSlot::Element, start, count); - } -} - // Operations which change an object's dense elements can either succeed, fail, // or be unable to complete. For native objects, the latter is used when the // object's elements must become sparse instead. The enum below is used for @@ -938,6 +923,20 @@ class NativeObject : public JSObject inline void ensureDenseInitializedLengthNoPackedCheck(ExclusiveContext* cx, uint32_t index, uint32_t extra); + // Run a post write barrier that encompasses multiple contiguous elements in a + // single step. + inline void elementsRangeWriteBarrierPost(uint32_t start, uint32_t count) { + for (size_t i = 0; i < count; i++) { + const Value& v = elements_[start + i]; + if (v.isObject() && IsInsideNursery(&v.toObject())) { + JS::shadow::Runtime* shadowRuntime = shadowRuntimeFromMainThread(); + shadowRuntime->gcStoreBufferPtr()->putSlotFromAnyThread(this, HeapSlot::Element, + start + i, count - i); + return; + } + } + } + public: void setDenseInitializedLength(uint32_t length) { MOZ_ASSERT(length <= getDenseCapacity()); @@ -985,7 +984,7 @@ class NativeObject : public JSObject elements_[dstStart + i].set(this, HeapSlot::Element, dstStart + i, src[i]); } else { memcpy(&elements_[dstStart], src, count * sizeof(HeapSlot)); - DenseRangeWriteBarrierPost(runtimeFromMainThread(), this, dstStart, count); + elementsRangeWriteBarrierPost(dstStart, count); } } @@ -993,11 +992,9 @@ class NativeObject : public JSObject MOZ_ASSERT(dstStart + count <= getDenseCapacity()); MOZ_ASSERT(!denseElementsAreCopyOnWrite()); memcpy(&elements_[dstStart], src, count * sizeof(HeapSlot)); - DenseRangeWriteBarrierPost(runtimeFromMainThread(), this, dstStart, count); + elementsRangeWriteBarrierPost(dstStart, count); } - void initDenseElementsUnbarriered(uint32_t dstStart, const Value* src, uint32_t count); - void moveDenseElements(uint32_t dstStart, uint32_t srcStart, uint32_t count) { MOZ_ASSERT(dstStart + count <= getDenseCapacity()); MOZ_ASSERT(srcStart + count <= getDenseInitializedLength()); @@ -1029,7 +1026,7 @@ class NativeObject : public JSObject } } else { memmove(elements_ + dstStart, elements_ + srcStart, count * sizeof(HeapSlot)); - DenseRangeWriteBarrierPost(runtimeFromMainThread(), this, dstStart, count); + elementsRangeWriteBarrierPost(dstStart, count); } } @@ -1041,7 +1038,7 @@ class NativeObject : public JSObject MOZ_ASSERT(!denseElementsAreCopyOnWrite()); memmove(elements_ + dstStart, elements_ + srcStart, count * sizeof(Value)); - DenseRangeWriteBarrierPost(runtimeFromMainThread(), this, dstStart, count); + elementsRangeWriteBarrierPost(dstStart, count); } bool shouldConvertDoubleElements() { diff --git a/js/src/vm/ObjectGroup-inl.h b/js/src/vm/ObjectGroup-inl.h new file mode 100644 index 0000000000..9074f4d974 --- /dev/null +++ b/js/src/vm/ObjectGroup-inl.h @@ -0,0 +1,127 @@ +/* -*- 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 vm_ObjectGroup_inl_h +#define vm_ObjectGroup_inl_h + +#include "vm/ObjectGroup.h" + +namespace js { + +inline bool +ObjectGroup::needsSweep() +{ + // Note: this can be called off thread during compacting GCs, in which case + // nothing will be running on the main thread. + return generation() != zoneFromAnyThread()->types.generation; +} + +inline void +ObjectGroup::maybeSweep(AutoClearTypeInferenceStateOnOOM* oom) +{ + if (needsSweep()) + sweep(oom); +} + +inline ObjectGroupFlags +ObjectGroup::flags() +{ + maybeSweep(nullptr); + return flagsDontCheckGeneration(); +} + +inline void +ObjectGroup::addFlags(ObjectGroupFlags flags) +{ + maybeSweep(nullptr); + flags_ |= flags; +} + +inline void +ObjectGroup::clearFlags(ObjectGroupFlags flags) +{ + maybeSweep(nullptr); + flags_ &= ~flags; +} + +inline bool +ObjectGroup::hasAnyFlags(ObjectGroupFlags flags) +{ + MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); + return !!(this->flags() & flags); +} + +inline bool +ObjectGroup::hasAllFlags(ObjectGroupFlags flags) +{ + MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); + return (this->flags() & flags) == flags; +} + +inline bool +ObjectGroup::unknownProperties() +{ + MOZ_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES, + hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK)); + return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES); +} + +inline bool +ObjectGroup::shouldPreTenure() +{ + return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties(); +} + +inline bool +ObjectGroup::canPreTenure() +{ + return !unknownProperties(); +} + +inline bool +ObjectGroup::fromAllocationSite() +{ + return flags() & OBJECT_FLAG_FROM_ALLOCATION_SITE; +} + +inline void +ObjectGroup::setShouldPreTenure(ExclusiveContext* cx) +{ + MOZ_ASSERT(canPreTenure()); + setFlags(cx, OBJECT_FLAG_PRE_TENURE); +} + +inline TypeNewScript* +ObjectGroup::newScript() +{ + maybeSweep(nullptr); + return newScriptDontCheckGeneration(); +} + +inline PreliminaryObjectArrayWithTemplate* +ObjectGroup::maybePreliminaryObjects() +{ + maybeSweep(nullptr); + return maybePreliminaryObjectsDontCheckGeneration(); +} + +inline UnboxedLayout* +ObjectGroup::maybeUnboxedLayout() +{ + maybeSweep(nullptr); + return maybeUnboxedLayoutDontCheckGeneration(); +} + +inline UnboxedLayout& +ObjectGroup::unboxedLayout() +{ + maybeSweep(nullptr); + return unboxedLayoutDontCheckGeneration(); +} + +} // namespace js + +#endif /* vm_ObjectGroup_inl_h */ diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index 33b7ef6437..2684a32b18 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -16,8 +16,11 @@ #include "jsobjinlines.h" +#include "vm/UnboxedObject-inl.h" + using namespace js; +using mozilla::DebugOnly; using mozilla::PodZero; ///////////////////////////////////////////////////////////////////// @@ -43,8 +46,12 @@ ObjectGroup::ObjectGroup(const Class* clasp, TaggedProto proto, JSCompartment* c void ObjectGroup::finalize(FreeOp* fop) { + if (newScriptDontCheckGeneration()) + newScriptDontCheckGeneration()->clear(); fop->delete_(newScriptDontCheckGeneration()); fop->delete_(maybeUnboxedLayoutDontCheckGeneration()); + if (maybePreliminaryObjectsDontCheckGeneration()) + maybePreliminaryObjectsDontCheckGeneration()->clear(); fop->delete_(maybePreliminaryObjectsDontCheckGeneration()); } @@ -707,26 +714,25 @@ ObjectGroup::defaultNewGroup(JSContext* cx, JSProtoKey key) struct ObjectGroupCompartment::ArrayObjectKey : public DefaultHasher { TypeSet::Type type; - JSObject* proto; ArrayObjectKey() - : type(TypeSet::UndefinedType()), proto(nullptr) + : type(TypeSet::UndefinedType()) {} - ArrayObjectKey(TypeSet::Type type, JSObject* proto) - : type(type), proto(proto) + explicit ArrayObjectKey(TypeSet::Type type) + : type(type) {} static inline uint32_t hash(const ArrayObjectKey& v) { - return (uint32_t) (v.type.raw() ^ ((uint32_t)(size_t)v.proto >> 2)); + return v.type.raw(); } static inline bool match(const ArrayObjectKey& v1, const ArrayObjectKey& v2) { - return v1.type == v2.type && v1.proto == v2.proto; + return v1.type == v2.type; } bool operator==(const ArrayObjectKey& other) { - return type == other.type && proto == other.proto; + return type == other.type; } bool operator!=(const ArrayObjectKey& other) { @@ -754,52 +760,39 @@ GetValueTypeForTable(const Value& v) return type; } -/* static */ void -ObjectGroup::fixArrayGroup(ExclusiveContext* cx, ArrayObject* obj) +/* static */ JSObject* +ObjectGroup::newArrayObject(ExclusiveContext* cx, + const Value* vp, size_t length, + NewObjectKind newKind, NewArrayKind arrayKind) { - AutoEnterAnalysis enter(cx); + MOZ_ASSERT(newKind != SingletonObject); - /* - * If the array is of homogenous type, pick a group which will be - * shared with all other singleton/JSON arrays of the same type. - * If the array is heterogenous, keep the existing group, which has - * unknown properties. - */ - - unsigned len = obj->getDenseInitializedLength(); - if (len == 0) - return; - - TypeSet::Type type = GetValueTypeForTable(obj->getDenseElement(0)); - - for (unsigned i = 1; i < len; i++) { - TypeSet::Type ntype = GetValueTypeForTable(obj->getDenseElement(i)); - if (ntype != type) { - if (NumberTypes(type, ntype)) - type = TypeSet::DoubleType(); - else - return; - } + // If we are making a copy on write array, don't try to adjust the group as + // getOrFixupCopyOnWriteObject will do this before any objects are copied + // from this one. + if (arrayKind == NewArrayKind::CopyOnWrite) { + ArrayObject* obj = NewDenseCopiedArray(cx, length, vp, nullptr, newKind); + if (!obj || !ObjectElements::MakeElementsCopyOnWrite(cx, obj)) + return nullptr; + return obj; } - setGroupToHomogenousArray(cx, obj, type); -} - -/* static */ void -ObjectGroup::fixRestArgumentsGroup(ExclusiveContext* cx, ArrayObject* obj) -{ - AutoEnterAnalysis enter(cx); - - // Tracking element types for rest argument arrays is not worth it, but we - // still want it to be known that it's a dense array. - setGroupToHomogenousArray(cx, obj, TypeSet::UnknownType()); -} - -/* static */ void -ObjectGroup::setGroupToHomogenousArray(ExclusiveContext* cx, JSObject* obj, - TypeSet::Type elementType) -{ - MOZ_ASSERT(cx->zone()->types.activeAnalysis); + // Get a type which captures all the elements in the array to be created. + TypeSet::Type elementType = TypeSet::UnknownType(); + if (arrayKind != NewArrayKind::UnknownIndex && length != 0) { + elementType = GetValueTypeForTable(vp[0]); + for (unsigned i = 1; i < length; i++) { + TypeSet::Type ntype = GetValueTypeForTable(vp[i]); + if (ntype != elementType) { + if (NumberTypes(elementType, ntype)) { + elementType = TypeSet::DoubleType(); + } else { + elementType = TypeSet::UnknownType(); + break; + } + } + } + } ObjectGroupCompartment::ArrayObjectTable*& table = cx->compartment()->objectGroups.arrayObjectTable; @@ -809,28 +802,50 @@ ObjectGroup::setGroupToHomogenousArray(ExclusiveContext* cx, JSObject* obj, if (!table || !table->init()) { js_delete(table); table = nullptr; - return; + return nullptr; } } - ObjectGroupCompartment::ArrayObjectKey key(elementType, obj->getProto()); + ObjectGroupCompartment::ArrayObjectKey key(elementType); DependentAddPtr p(cx, *table, key); - if (p) { - obj->setGroup(p->value()); - } else { - // Make a new group to use for future arrays with the same elements. - RootedObject objProto(cx, obj->getProto()); - Rooted taggedProto(cx, TaggedProto(objProto)); - ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, &ArrayObject::class_, taggedProto); + + if (!p) { + RootedArrayObject obj(cx, NewDenseCopiedArray(cx, length, vp, nullptr, TenuredObject)); + if (!obj) + return nullptr; + + Rooted proto(cx, TaggedProto(obj->getProto())); + RootedObjectGroup group(cx, ObjectGroupCompartment::makeGroup(cx, &ArrayObject::class_, + proto)); if (!group) - return; - obj->setGroup(group); + return nullptr; AddTypePropertyId(cx, group, nullptr, JSID_VOID, elementType); - key.proto = objProto; - (void) p.add(cx, *table, key, group); + obj->setGroup(group); + + if (elementType != TypeSet::UnknownType()) { + // Keep track of the initial objects we create with this type. + // If the initial ones have a consistent shape and property types, we + // will try to use an unboxed layout for the group. + PreliminaryObjectArrayWithTemplate* preliminaryObjects = + cx->new_(nullptr); + if (!preliminaryObjects) + return nullptr; + group->setPreliminaryObjects(preliminaryObjects); + preliminaryObjects->registerNewObject(obj); + } + + + if (!p.add(cx, *table, key, group)) + return nullptr; + + return obj; } + + RootedObjectGroup group(cx, p->value()); + return NewCopiedArrayTryUseGroup(cx, group, vp, length, newKind, + ShouldUpdateTypes::DontUpdate); } ///////////////////////////////////////////////////////////////////// @@ -1153,7 +1168,10 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc } } - if (JSOp(*pc) == JSOP_NEWARRAY && cx->runtime()->options().unboxedArrays()) { + if (kind == JSProto_Array && + (JSOp(*pc) == JSOP_NEWARRAY || IsCallPC(pc)) && + cx->runtime()->options().unboxedArrays()) + { PreliminaryObjectArrayWithTemplate* preliminaryObjects = cx->new_(nullptr); if (preliminaryObjects) @@ -1168,6 +1186,21 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc return res; } +void +ObjectGroupCompartment::replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc, + JSProtoKey kind, ObjectGroup* group) +{ + AllocationSiteKey key; + key.script = script; + key.offset = script->pcToOffset(pc); + key.kind = kind; + + AllocationSiteTable::Ptr p = allocationSiteTable->lookup(key); + MOZ_ASSERT(p); + allocationSiteTable->remove(p); + allocationSiteTable->putNew(key, group); +} + /* static */ ObjectGroup* ObjectGroup::callingAllocationSiteGroup(JSContext* cx, JSProtoKey key) { @@ -1411,11 +1444,6 @@ ObjectGroupCompartment::sweep(FreeOp* fop) else key.type = TypeSet::ObjectType(group); } - if (key.proto && key.proto != TaggedProto::LazyProto && - IsAboutToBeFinalizedUnbarriered(&key.proto)) - { - remove = true; - } if (IsAboutToBeFinalized(&e.front().value())) remove = true; diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h index e9f9d3f91b..928d804a8f 100644 --- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -216,9 +216,8 @@ class ObjectGroup : public gc::TenuredCell return res; } - JSCompartment* compartment() const { - return compartment_; - } + JSCompartment* compartment() const { return compartment_; } + JSCompartment* maybeCompartment() const { return compartment(); } private: /* Flags for this group. */ @@ -245,8 +244,8 @@ class ObjectGroup : public gc::TenuredCell Addendum_UnboxedLayout, // If this group is used by objects that have been converted from an - // unboxed representation, the addendum points to the original unboxed - // group. + // unboxed representation and/or have the same allocation kind as such + // objects, the addendum points to that unboxed group. Addendum_OriginalUnboxedGroup, // When used by typed objects, the addendum stores a TypeDescr. @@ -270,12 +269,6 @@ class ObjectGroup : public gc::TenuredCell return nullptr; } - UnboxedLayout* maybeUnboxedLayoutDontCheckGeneration() const { - if (addendumKind() == Addendum_UnboxedLayout) - return reinterpret_cast(addendum_); - return nullptr; - } - TypeNewScript* anyNewScript(); void detachNewScript(bool writeBarrier, ObjectGroup* replacement); @@ -285,34 +278,16 @@ class ObjectGroup : public gc::TenuredCell public: - ObjectGroupFlags flags() { - maybeSweep(nullptr); - return flagsDontCheckGeneration(); - } - - void addFlags(ObjectGroupFlags flags) { - maybeSweep(nullptr); - flags_ |= flags; - } - - void clearFlags(ObjectGroupFlags flags) { - maybeSweep(nullptr); - flags_ &= ~flags; - } - - TypeNewScript* newScript() { - maybeSweep(nullptr); - return newScriptDontCheckGeneration(); - } + inline ObjectGroupFlags flags(); + inline void addFlags(ObjectGroupFlags flags); + inline void clearFlags(ObjectGroupFlags flags); + inline TypeNewScript* newScript(); void setNewScript(TypeNewScript* newScript) { setAddendum(Addendum_NewScript, newScript); } - PreliminaryObjectArrayWithTemplate* maybePreliminaryObjects() { - maybeSweep(nullptr); - return maybePreliminaryObjectsDontCheckGeneration(); - } + inline PreliminaryObjectArrayWithTemplate* maybePreliminaryObjects(); PreliminaryObjectArrayWithTemplate* maybePreliminaryObjectsDontCheckGeneration() { if (addendumKind() == Addendum_PreliminaryObjects) @@ -325,7 +300,7 @@ class ObjectGroup : public gc::TenuredCell } void detachPreliminaryObjects() { - MOZ_ASSERT(maybePreliminaryObjects()); + MOZ_ASSERT(maybePreliminaryObjectsDontCheckGeneration()); setAddendum(Addendum_None, nullptr); } @@ -334,9 +309,13 @@ class ObjectGroup : public gc::TenuredCell maybePreliminaryObjectsDontCheckGeneration(); } - UnboxedLayout* maybeUnboxedLayout() { - maybeSweep(nullptr); - return maybeUnboxedLayoutDontCheckGeneration(); + inline UnboxedLayout* maybeUnboxedLayout(); + inline UnboxedLayout& unboxedLayout(); + + UnboxedLayout* maybeUnboxedLayoutDontCheckGeneration() const { + if (addendumKind() == Addendum_UnboxedLayout) + return reinterpret_cast(addendum_); + return nullptr; } UnboxedLayout &unboxedLayoutDontCheckGeneration() const { @@ -344,11 +323,6 @@ class ObjectGroup : public gc::TenuredCell return *maybeUnboxedLayoutDontCheckGeneration(); } - UnboxedLayout& unboxedLayout() { - maybeSweep(nullptr); - return unboxedLayoutDontCheckGeneration(); - } - void setUnboxedLayout(UnboxedLayout* layout) { setAddendum(Addendum_UnboxedLayout, layout); } @@ -463,26 +437,15 @@ class ObjectGroup : public gc::TenuredCell inline ObjectGroup(const Class* clasp, TaggedProto proto, JSCompartment* comp, ObjectGroupFlags initialFlags); - inline bool hasAnyFlags(ObjectGroupFlags flags) { - MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); - return !!(this->flags() & flags); - } - - bool hasAllFlags(ObjectGroupFlags flags) { - MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); - return (this->flags() & flags) == flags; - } + inline bool hasAnyFlags(ObjectGroupFlags flags); + inline bool hasAllFlags(ObjectGroupFlags flags); bool hasAllFlagsDontCheckGeneration(ObjectGroupFlags flags) { MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); return (this->flagsDontCheckGeneration() & flags) == flags; } - bool unknownProperties() { - MOZ_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES, - hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK)); - return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES); - } + inline bool unknownProperties(); bool unknownPropertiesDontCheckGeneration() { MOZ_ASSERT_IF(flagsDontCheckGeneration() & OBJECT_FLAG_UNKNOWN_PROPERTIES, @@ -490,24 +453,13 @@ class ObjectGroup : public gc::TenuredCell return !!(flagsDontCheckGeneration() & OBJECT_FLAG_UNKNOWN_PROPERTIES); } - bool shouldPreTenure() { - return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties(); - } + inline bool shouldPreTenure(); gc::InitialHeap initialHeap(CompilerConstraintList* constraints); - bool canPreTenure() { - return !unknownProperties(); - } - - bool fromAllocationSite() { - return flags() & OBJECT_FLAG_FROM_ALLOCATION_SITE; - } - - void setShouldPreTenure(ExclusiveContext* cx) { - MOZ_ASSERT(canPreTenure()); - setFlags(cx, OBJECT_FLAG_PRE_TENURE); - } + inline bool canPreTenure(); + inline bool fromAllocationSite(); + inline void setShouldPreTenure(ExclusiveContext* cx); /* * Get or create a property of this object. Only call this for properties which @@ -529,7 +481,7 @@ class ObjectGroup : public gc::TenuredCell /* Helpers */ void updateNewPropertyTypes(ExclusiveContext* cx, JSObject* obj, jsid id, HeapTypeSet* types); - bool addDefiniteProperties(ExclusiveContext* cx, Shape* shape); + void addDefiniteProperties(ExclusiveContext* cx, Shape* shape); bool matchDefiniteProperties(HandleObject obj); void markPropertyNonData(ExclusiveContext* cx, JSObject* obj, jsid id); void markPropertyNonWritable(ExclusiveContext* cx, JSObject* obj, jsid id); @@ -544,13 +496,13 @@ class ObjectGroup : public gc::TenuredCell void print(); inline void clearProperties(); - void maybeSweep(AutoClearTypeInferenceStateOnOOM* oom); void traceChildren(JSTracer* trc); + inline bool needsSweep(); + inline void maybeSweep(AutoClearTypeInferenceStateOnOOM* oom); + private: -#ifdef DEBUG - bool needsSweep(); -#endif + void sweep(AutoClearTypeInferenceStateOnOOM* oom); uint32_t generation() { return (flags_ & OBJECT_FLAG_GENERATION_MASK) >> OBJECT_FLAG_GENERATION_SHIFT; @@ -634,14 +586,20 @@ class ObjectGroup : public gc::TenuredCell // Static accessors for ObjectGroupCompartment ArrayObjectTable and PlainObjectTable. - // Update the group of a freshly created array according to - // the object's current contents. - static void fixArrayGroup(ExclusiveContext* cx, ArrayObject* obj); + enum class NewArrayKind { + Normal, // Specialize array group based on its element type. + CopyOnWrite, // Make an array with copy-on-write elements. + UnknownIndex // Make an array with an unknown element type. + }; - // Update the group of a freshly created 'rest' arguments object. - static void fixRestArgumentsGroup(ExclusiveContext* cx, ArrayObject* obj); + // Create an ArrayObject or UnboxedArrayObject with the specified elements + // and a group specialized for the elements. + static JSObject* newArrayObject(ExclusiveContext* cx, const Value* vp, size_t length, + NewObjectKind newKind, + NewArrayKind arrayKind = NewArrayKind::Normal); - // Create a PlainObject or UnboxedPlainObject with the specified properties. + // Create a PlainObject or UnboxedPlainObject with the specified properties + // and a group specialized for those properties. static JSObject* newPlainObject(ExclusiveContext* cx, IdValuePair* properties, size_t nproperties, NewObjectKind newKind); @@ -671,8 +629,6 @@ class ObjectGroup : public gc::TenuredCell private: static ObjectGroup* defaultNewGroup(JSContext* cx, JSProtoKey key); - static void setGroupToHomogenousArray(ExclusiveContext* cx, JSObject* obj, - TypeSet::Type type); }; // Structure used to manage the groups in a compartment. @@ -726,6 +682,9 @@ class ObjectGroupCompartment ObjectGroupCompartment(); ~ObjectGroupCompartment(); + void replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc, + JSProtoKey kind, ObjectGroup* group); + void removeDefaultNewGroup(const Class* clasp, TaggedProto proto, JSObject* associated); void replaceDefaultNewGroup(const Class* clasp, TaggedProto proto, JSObject* associated, ObjectGroup* group); diff --git a/js/src/vm/ReceiverGuard.cpp b/js/src/vm/ReceiverGuard.cpp index ea746f0c41..81b36ce387 100644 --- a/js/src/vm/ReceiverGuard.cpp +++ b/js/src/vm/ReceiverGuard.cpp @@ -62,6 +62,6 @@ HeapReceiverGuard::trace(JSTracer* trc) { if (shape_) TraceEdge(trc, &shape_, "receiver_guard_shape"); - else + if (group_) TraceEdge(trc, &group_, "receiver_guard_group"); } diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index a35b7f0e8d..76314ac4b3 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -171,6 +171,7 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) profilingScripts(false), suppressProfilerSampling(false), hadOutOfMemory(false), + handlingInitFailure(false), haveCreatedContext(false), allowRelazificationForTesting(false), data(nullptr), diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 96733efe40..f3fd3a3a62 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -1058,6 +1058,9 @@ struct JSRuntime : public JS::shadow::Runtime, /* Had an out-of-memory error which did not populate an exception. */ bool hadOutOfMemory; + /* We are curently deleting an object due to an initialization failure. */ + mozilla::DebugOnly handlingInitFailure; + /* A context has been created on this runtime. */ bool haveCreatedContext; @@ -1945,6 +1948,71 @@ class AutoEnterIonCompilation MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; +/* + * AutoInitGCManagedObject is a wrapper for use when initializing a object whose + * lifetime is managed by the GC. It ensures that the object is destroyed if + * initialization fails but also allows us to assert the invariant that such + * objects are only destroyed in this way or by the GC. + * + * It has a limited interface but is a drop-in replacement for UniquePtr is + * this situation. For example: + * + * AutoInitGCManagedObject ptr(cx->make_unique()); + * if (!ptr) { + * ReportOutOfMemory(cx); + * return nullptr; + * } + * + * if (!ptr->init(cx)) + * return nullptr; // Object destroyed here if init() failed. + * + * object->setPrivate(ptr.release()); + * // Initialization successful, ptr is now owned through another object. + */ +template +class MOZ_STACK_CLASS AutoInitGCManagedObject +{ + typedef mozilla::UniquePtr> UniquePtrT; + + UniquePtrT ptr_; + + public: + explicit AutoInitGCManagedObject(UniquePtrT&& ptr) + : ptr_(mozilla::Move(ptr)) + {} + + ~AutoInitGCManagedObject() { +#ifdef DEBUG + if (ptr_) { + JSRuntime* rt = TlsPerThreadData.get()->runtimeFromMainThread(); + MOZ_ASSERT(!rt->handlingInitFailure); + rt->handlingInitFailure = true; + ptr_.reset(nullptr); + rt->handlingInitFailure = false; + } +#endif + } + + T& operator*() const { + return *ptr_.get(); + } + + T* operator->() const { + return ptr_.get(); + } + + explicit operator bool() const { + return ptr_.get() != nullptr; + } + + T* release() { + return ptr_.release(); + } + + AutoInitGCManagedObject(const AutoInitGCManagedObject& other) = delete; + AutoInitGCManagedObject& operator=(const AutoInitGCManagedObject& other) = delete; +}; + } /* namespace js */ #ifdef _MSC_VER diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index cef0bba13f..fa26915a4e 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -337,81 +337,11 @@ js::intrinsic_NewDenseArray(JSContext* cx, unsigned argc, Value* vp) uint32_t length = args[0].toInt32(); // Make a new buffer and initialize it up to length. - RootedArrayObject buffer(cx, NewDenseFullyAllocatedArray(cx, length)); + RootedObject buffer(cx, NewFullyAllocatedArrayForCallingAllocationSite(cx, length)); if (!buffer) return false; - ObjectGroup* newgroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array); - if (!newgroup) - return false; - buffer->setGroup(newgroup); - - DenseElementResult edr = buffer->ensureDenseElements(cx, length, 0); - switch (edr) { - case DenseElementResult::Success: - args.rval().setObject(*buffer); - return true; - - case DenseElementResult::Incomplete: // shouldn't happen! - MOZ_ASSERT(!"%EnsureDenseArrayElements() would yield sparse array"); - JS_ReportError(cx, "%EnsureDenseArrayElements() would yield sparse array"); - break; - - case DenseElementResult::Failure: - break; - } - return false; -} - -/* - * UnsafePutElements(arr0, idx0, elem0, ..., arrN, idxN, elemN): For each set of - * (arr, idx, elem) arguments that are passed, performs the assignment - * |arr[idx] = elem|. |arr| must be either a dense array or a typed array. - * - * If |arr| is a dense array, the index must be an int32 less than the - * initialized length of |arr|. Use |%EnsureDenseResultArrayElements| - * to ensure that the initialized length is long enough. - * - * If |arr| is a typed array, the index must be an int32 less than the - * length of |arr|. - */ -bool -js::intrinsic_UnsafePutElements(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - if ((args.length() % 3) != 0) { - JS_ReportError(cx, "Incorrect number of arguments, not divisible by 3"); - return false; - } - - for (uint32_t base = 0; base < args.length(); base += 3) { - uint32_t arri = base; - uint32_t idxi = base+1; - uint32_t elemi = base+2; - - MOZ_ASSERT(args[arri].isObject()); - MOZ_ASSERT(args[arri].toObject().isNative() || IsTypedObjectArray(args[arri].toObject())); - MOZ_ASSERT(args[idxi].isInt32()); - - RootedObject arrobj(cx, &args[arri].toObject()); - uint32_t idx = args[idxi].toInt32(); - - if (IsAnyTypedArray(arrobj.get()) || arrobj->is()) { - MOZ_ASSERT_IF(IsAnyTypedArray(arrobj.get()), idx < AnyTypedArrayLength(arrobj.get())); - MOZ_ASSERT_IF(arrobj->is(), idx < uint32_t(arrobj->as().length())); - // XXX: Always non-strict. - ObjectOpResult ignored; - RootedValue receiver(cx, ObjectValue(*arrobj)); - if (!SetElement(cx, arrobj, idx, args[elemi], receiver, ignored)) - return false; - } else { - MOZ_ASSERT(idx < arrobj->as().getDenseInitializedLength()); - arrobj->as().setDenseElementWithType(cx, idx, args[elemi]); - } - } - - args.rval().setUndefined(); + args.rval().setObject(*buffer); return true; } @@ -420,38 +350,47 @@ js::intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - MOZ_ASSERT(args.length() == 4); + MOZ_ASSERT(args.length() >= 3); MOZ_ASSERT(args[0].isObject()); - MOZ_ASSERT(args[3].isInt32()); RootedObject obj(cx, &args[0].toObject()); RootedId id(cx); if (!ValueToId(cx, args[1], &id)) return false; RootedValue value(cx, args[2]); - unsigned attributes = args[3].toInt32(); + + unsigned attrs = 0; + if (args.length() >= 4) { + unsigned attributes = args[3].toInt32(); + + MOZ_ASSERT(bool(attributes & ATTR_ENUMERABLE) != bool(attributes & ATTR_NONENUMERABLE), + "_DefineDataProperty must receive either ATTR_ENUMERABLE xor ATTR_NONENUMERABLE"); + if (attributes & ATTR_ENUMERABLE) + attrs |= JSPROP_ENUMERATE; + + MOZ_ASSERT(bool(attributes & ATTR_CONFIGURABLE) != bool(attributes & ATTR_NONCONFIGURABLE), + "_DefineDataProperty must receive either ATTR_CONFIGURABLE xor " + "ATTR_NONCONFIGURABLE"); + if (attributes & ATTR_NONCONFIGURABLE) + attrs |= JSPROP_PERMANENT; + + MOZ_ASSERT(bool(attributes & ATTR_WRITABLE) != bool(attributes & ATTR_NONWRITABLE), + "_DefineDataProperty must receive either ATTR_WRITABLE xor ATTR_NONWRITABLE"); + if (attributes & ATTR_NONWRITABLE) + attrs |= JSPROP_READONLY; + } else { + // If the fourth argument is unspecified, the attributes are for a + // plain data property. + attrs = JSPROP_ENUMERATE; + } Rooted desc(cx); - unsigned attrs = 0; - - MOZ_ASSERT(bool(attributes & ATTR_ENUMERABLE) != bool(attributes & ATTR_NONENUMERABLE), - "_DefineDataProperty must receive either ATTR_ENUMERABLE xor ATTR_NONENUMERABLE"); - if (attributes & ATTR_ENUMERABLE) - attrs |= JSPROP_ENUMERATE; - - MOZ_ASSERT(bool(attributes & ATTR_CONFIGURABLE) != bool(attributes & ATTR_NONCONFIGURABLE), - "_DefineDataProperty must receive either ATTR_CONFIGURABLE xor " - "ATTR_NONCONFIGURABLE"); - if (attributes & ATTR_NONCONFIGURABLE) - attrs |= JSPROP_PERMANENT; - - MOZ_ASSERT(bool(attributes & ATTR_WRITABLE) != bool(attributes & ATTR_NONWRITABLE), - "_DefineDataProperty must receive either ATTR_WRITABLE xor ATTR_NONWRITABLE"); - if (attributes & ATTR_NONWRITABLE) - attrs |= JSPROP_READONLY; - desc.setDataDescriptor(value, attrs); - return StandardDefineProperty(cx, obj, id, desc); + if (!DefineProperty(cx, obj, id, desc)) + return false; + + args.rval().setUndefined(); + return true; } bool @@ -1432,7 +1371,6 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0), JS_FN("SubstringKernel", intrinsic_SubstringKernel, 3,0), - JS_FN("UnsafePutElements", intrinsic_UnsafePutElements, 3,0), JS_FN("_DefineDataProperty", intrinsic_DefineDataProperty, 4,0), JS_FN("UnsafeSetReservedSlot", intrinsic_UnsafeSetReservedSlot, 3,0), JS_FN("UnsafeGetReservedSlot", intrinsic_UnsafeGetReservedSlot, 2,0), diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index 971708c66e..5d43e9a5fe 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -431,7 +431,8 @@ class BaseShape : public gc::TenuredCell uint32_t slotSpan() const { MOZ_ASSERT(isOwned()); return slotSpan_; } void setSlotSpan(uint32_t slotSpan) { MOZ_ASSERT(isOwned()); slotSpan_ = slotSpan; } - JSCompartment *compartment() const { return compartment_; } + JSCompartment* compartment() const { return compartment_; } + JSCompartment* maybeCompartment() const { return compartment(); } /* * Lookup base shapes from the compartment's baseShapes table, adding if @@ -686,6 +687,7 @@ class Shape : public gc::TenuredCell const HeapPtrShape& previous() const { return parent; } JSCompartment* compartment() const { return base()->compartment(); } + JSCompartment* maybeCompartment() const { return compartment(); } template class Range { @@ -1406,6 +1408,12 @@ IsImplicitDenseOrTypedArrayElement(Shape* prop) return prop == reinterpret_cast(1); } +static inline bool +IsImplicitNonNativeProperty(Shape *prop) +{ + return prop == reinterpret_cast(1); +} + Shape * ReshapeForAllocKind(JSContext *cx, Shape *shape, TaggedProto proto, gc::AllocKind allocKind); diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp index e0aa09fd9d..9932db2787 100644 --- a/js/src/vm/SharedArrayObject.cpp +++ b/js/src/vm/SharedArrayObject.cpp @@ -383,9 +383,36 @@ js::AsSharedArrayBuffer(HandleObject obj) return obj->as(); } +JS_FRIEND_API(void) +js::GetSharedArrayBufferViewLengthAndData(JSObject* obj, uint32_t* length, uint8_t** data) +{ + MOZ_ASSERT(obj->is()); + + *length = obj->as().byteLength(); + + *data = static_cast(obj->as().viewData()); +} + +JS_FRIEND_API(void) +js::GetSharedArrayBufferLengthAndData(JSObject* obj, uint32_t* length, uint8_t** data) +{ + MOZ_ASSERT(obj->is()); + *length = obj->as().byteLength(); + *data = obj->as().dataPointer(); +} + JS_FRIEND_API(bool) JS_IsSharedArrayBufferObject(JSObject* obj) { obj = CheckedUnwrap(obj); - return obj ? obj->is() : false; + return obj ? obj->is() : false; +} + +JS_FRIEND_API(uint8_t*) +JS_GetSharedArrayBufferData(JSObject* obj, const JS::AutoCheckCannotGC&) +{ + obj = CheckedUnwrap(obj); + if (!obj) + return nullptr; + return obj->as().dataPointer(); } diff --git a/js/src/vm/SharedTypedArrayObject.cpp b/js/src/vm/SharedTypedArrayObject.cpp index 4b05b5d97a..5f5818b7f4 100644 --- a/js/src/vm/SharedTypedArrayObject.cpp +++ b/js/src/vm/SharedTypedArrayObject.cpp @@ -999,6 +999,105 @@ SharedTypedArrayObject::setElement(SharedTypedArrayObject& obj, uint32_t index, } } +JS_FRIEND_API(int8_t*) +JS_GetSharedInt8ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&) +{ + obj = CheckedUnwrap(obj); + if (!obj) + return nullptr; + SharedTypedArrayObject* tarr = &obj->as(); + MOZ_ASSERT((int32_t) tarr->type() == Scalar::Int8); + return static_cast(tarr->viewData()); +} + +JS_FRIEND_API(uint8_t*) +JS_GetSharedUint8ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&) +{ + obj = CheckedUnwrap(obj); + if (!obj) + return nullptr; + SharedTypedArrayObject* tarr = &obj->as(); + MOZ_ASSERT((int32_t) tarr->type() == Scalar::Uint8); + return static_cast(tarr->viewData()); +} + +JS_FRIEND_API(uint8_t*) +JS_GetSharedUint8ClampedArrayData(JSObject* obj, const JS::AutoCheckCannotGC&) +{ + obj = CheckedUnwrap(obj); + if (!obj) + return nullptr; + SharedTypedArrayObject* tarr = &obj->as(); + MOZ_ASSERT((int32_t) tarr->type() == Scalar::Uint8Clamped); + return static_cast(tarr->viewData()); +} + +JS_FRIEND_API(int16_t*) +JS_GetSharedInt16ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&) +{ + obj = CheckedUnwrap(obj); + if (!obj) + return nullptr; + SharedTypedArrayObject* tarr = &obj->as(); + MOZ_ASSERT((int32_t) tarr->type() == Scalar::Int16); + return static_cast(tarr->viewData()); +} + +JS_FRIEND_API(uint16_t*) +JS_GetSharedUint16ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&) +{ + obj = CheckedUnwrap(obj); + if (!obj) + return nullptr; + SharedTypedArrayObject* tarr = &obj->as(); + MOZ_ASSERT((int32_t) tarr->type() == Scalar::Uint16); + return static_cast(tarr->viewData()); +} + +JS_FRIEND_API(int32_t*) +JS_GetSharedInt32ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&) +{ + obj = CheckedUnwrap(obj); + if (!obj) + return nullptr; + SharedTypedArrayObject* tarr = &obj->as(); + MOZ_ASSERT((int32_t) tarr->type() == Scalar::Int32); + return static_cast(tarr->viewData()); +} + +JS_FRIEND_API(uint32_t*) +JS_GetSharedUint32ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&) +{ + obj = CheckedUnwrap(obj); + if (!obj) + return nullptr; + SharedTypedArrayObject* tarr = &obj->as(); + MOZ_ASSERT((int32_t) tarr->type() == Scalar::Uint32); + return static_cast(tarr->viewData()); +} + +JS_FRIEND_API(float*) +JS_GetSharedFloat32ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&) +{ + obj = CheckedUnwrap(obj); + if (!obj) + return nullptr; + SharedTypedArrayObject* tarr = &obj->as(); + MOZ_ASSERT((int32_t) tarr->type() == Scalar::Float32); + return static_cast(tarr->viewData()); +} + +JS_FRIEND_API(double*) +JS_GetSharedFloat64ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&) +{ + obj = CheckedUnwrap(obj); + if (!obj) + return nullptr; + SharedTypedArrayObject* tarr = &obj->as(); + MOZ_ASSERT((int32_t) tarr->type() == Scalar::Float64); + return static_cast(tarr->viewData()); +} + #undef IMPL_SHARED_TYPED_ARRAY_STATICS #undef IMPL_SHARED_TYPED_ARRAY_JSAPI_CONSTRUCTORS #undef IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index cb800dfeb3..21ac4f790a 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -143,11 +143,8 @@ InterpreterFrame::createRestParameter(JSContext* cx) unsigned nformal = fun()->nargs() - 1, nactual = numActualArgs(); unsigned nrest = (nactual > nformal) ? nactual - nformal : 0; Value* restvp = argv() + nformal; - ArrayObject* obj = NewDenseCopiedArray(cx, nrest, restvp, nullptr); - if (!obj) - return nullptr; - ObjectGroup::fixRestArgumentsGroup(cx, obj); - return obj; + return ObjectGroup::newArrayObject(cx, restvp, nrest, GenericObject, + ObjectGroup::NewArrayKind::UnknownIndex); } static inline void diff --git a/js/src/vm/TypeInference-inl.h b/js/src/vm/TypeInference-inl.h index fe793aa05b..5c595c44e0 100644 --- a/js/src/vm/TypeInference-inl.h +++ b/js/src/vm/TypeInference-inl.h @@ -26,6 +26,8 @@ #include "jscntxtinlines.h" +#include "vm/ObjectGroup-inl.h" + namespace js { ///////////////////////////////////////////////////////////////////// @@ -353,25 +355,8 @@ TrackPropertyTypes(ExclusiveContext* cx, JSObject* obj, jsid id) return true; } -inline void -EnsureTrackPropertyTypes(JSContext* cx, JSObject* obj, jsid id) -{ - id = IdToTypeId(id); - - if (obj->isSingleton()) { - AutoEnterAnalysis enter(cx); - if (obj->hasLazyGroup() && !obj->getGroup(cx)) { - CrashAtUnhandlableOOM("Could not allocate ObjectGroup in EnsureTrackPropertyTypes"); - return; - } - if (!obj->group()->unknownProperties() && !obj->group()->getProperty(cx, obj, id)) { - MOZ_ASSERT(obj->group()->unknownProperties()); - return; - } - } - - MOZ_ASSERT(obj->group()->unknownProperties() || TrackPropertyTypes(cx, obj, id)); -} +void +EnsureTrackPropertyTypes(JSContext* cx, JSObject* obj, jsid id); inline bool CanHaveEmptyPropertyTypesForOwnProperty(JSObject* obj) @@ -445,7 +430,7 @@ MarkObjectGroupFlags(ExclusiveContext *cx, JSObject *obj, ObjectGroupFlags flags } inline void -MarkObjectGroupUnknownProperties(JSContext* cx, ObjectGroup* obj) +MarkObjectGroupUnknownProperties(ExclusiveContext* cx, ObjectGroup* obj) { if (!obj->unknownProperties()) obj->markUnknown(cx); diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index cfa012370a..27f30c44a5 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -1228,6 +1228,26 @@ TypeSet::ObjectKey::ensureTrackedProperty(JSContext* cx, jsid id) } } +void +js::EnsureTrackPropertyTypes(JSContext* cx, JSObject* obj, jsid id) +{ + id = IdToTypeId(id); + + if (obj->isSingleton()) { + AutoEnterAnalysis enter(cx); + if (obj->hasLazyGroup() && !obj->getGroup(cx)) { + CrashAtUnhandlableOOM("Could not allocate ObjectGroup in EnsureTrackPropertyTypes"); + return; + } + if (!obj->group()->unknownProperties() && !obj->group()->getProperty(cx, obj, id)) { + MOZ_ASSERT(obj->group()->unknownProperties()); + return; + } + } + + MOZ_ASSERT(obj->group()->unknownProperties() || TrackPropertyTypes(cx, obj, id)); +} + bool HeapTypeSetKey::instantiate(JSContext* cx) { @@ -2621,11 +2641,11 @@ ObjectGroup::updateNewPropertyTypes(ExclusiveContext* cx, JSObject* objArg, jsid } } -bool +void ObjectGroup::addDefiniteProperties(ExclusiveContext* cx, Shape* shape) { if (unknownProperties()) - return true; + return; // Mark all properties of shape as definite properties of this group. AutoEnterAnalysis enter(cx); @@ -2636,16 +2656,12 @@ ObjectGroup::addDefiniteProperties(ExclusiveContext* cx, Shape* shape) MOZ_ASSERT_IF(shape->slot() >= shape->numFixedSlots(), shape->numFixedSlots() == NativeObject::MAX_FIXED_SLOTS); TypeSet* types = getProperty(cx, nullptr, id); - if (!types) - return false; - if (types->canSetDefinite(shape->slot())) + if (types && types->canSetDefinite(shape->slot())) types->setDefinite(shape->slot()); } shape = shape->previous(); } - - return true; } bool @@ -2853,6 +2869,11 @@ ObjectGroup::markUnknown(ExclusiveContext* cx) prop->types.setNonDataProperty(cx); } } + + if (maybeUnboxedLayout() && maybeUnboxedLayout()->nativeGroup()) + MarkObjectGroupUnknownProperties(cx, maybeUnboxedLayout()->nativeGroup()); + if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup()) + MarkObjectGroupUnknownProperties(cx, unboxedGroup); } TypeNewScript* @@ -3429,9 +3450,9 @@ PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext* cx, ObjectGro if (shape()) { // We weren't able to use an unboxed layout, but since the preliminary - // still reflect the template object's properties, and all objects in the - // future will be created with those properties, the properties can be - // marked as definite for objects in the group. + // objects still reflect the template object's properties, and all + // objects in the future will be created with those properties, the + // properties can be marked as definite for objects in the group. group->addDefiniteProperties(cx, shape()); } } @@ -3735,8 +3756,7 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, // The definite properties analysis found exactly the properties that // are held in common by the preliminary objects. No further analysis // is needed. - if (!group->addDefiniteProperties(cx, templateObject()->lastProperty())) - return false; + group->addDefiniteProperties(cx, templateObject()->lastProperty()); destroyNewScript.group = nullptr; return true; @@ -3757,10 +3777,8 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, if (!initialGroup) return false; - if (!initialGroup->addDefiniteProperties(cx, templateObject()->lastProperty())) - return false; - if (!group->addDefiniteProperties(cx, prefixShape)) - return false; + initialGroup->addDefiniteProperties(cx, templateObject()->lastProperty()); + group->addDefiniteProperties(cx, prefixShape); cx->compartment()->objectGroups.replaceDefaultNewGroup(nullptr, group->proto(), function(), initialGroup); @@ -4024,16 +4042,6 @@ ObjectGroup::clearProperties() propertySet = nullptr; } -#ifdef DEBUG -bool -ObjectGroup::needsSweep() -{ - // Note: this can be called off thread during compacting GCs, in which case - // nothing will be running on the main thread. - return generation() != zoneFromAnyThread()->types.generation; -} -#endif - static void EnsureHasAutoClearTypeInferenceStateOnOOM(AutoClearTypeInferenceStateOnOOM*& oom, Zone* zone, Maybe& fallback) @@ -4056,12 +4064,9 @@ EnsureHasAutoClearTypeInferenceStateOnOOM(AutoClearTypeInferenceStateOnOOM*& oom * objects are accessed before their contents have been swept. */ void -ObjectGroup::maybeSweep(AutoClearTypeInferenceStateOnOOM* oom) +ObjectGroup::sweep(AutoClearTypeInferenceStateOnOOM* oom) { - if (generation() == zoneFromAnyThread()->types.generation) { - // No sweeping required. - return; - } + MOZ_ASSERT(generation() != zoneFromAnyThread()->types.generation); setGeneration(zone()->types.generation); diff --git a/js/src/vm/TypeInference.h b/js/src/vm/TypeInference.h index fa7bca125d..7c5da134da 100644 --- a/js/src/vm/TypeInference.h +++ b/js/src/vm/TypeInference.h @@ -809,6 +809,10 @@ class PreliminaryObjectArrayWithTemplate : public PreliminaryObjectArray : shape_(shape) {} + void clear() { + shape_.init(nullptr); + } + Shape* shape() { return shape_; } @@ -917,6 +921,13 @@ class TypeNewScript js_free(initializerList); } + void clear() { + function_.init(nullptr); + templateObject_.init(nullptr); + initializedShape_.init(nullptr); + initializedGroup_.init(nullptr); + } + static void writeBarrierPre(TypeNewScript* newScript); bool analyzed() const { diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 5efe6556e3..fd5bce9262 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -2214,6 +2214,18 @@ JS_GetArrayBufferViewType(JSObject* obj) MOZ_CRASH("invalid ArrayBufferView type"); } +JS_FRIEND_API(js::Scalar::Type) +JS_GetSharedArrayBufferViewType(JSObject* obj) +{ + obj = CheckedUnwrap(obj); + if (!obj) + return Scalar::MaxTypedArrayViewType; + + if (obj->is()) + return obj->as().type(); + MOZ_CRASH("invalid SharedArrayBufferView type"); +} + JS_FRIEND_API(int8_t*) JS_GetInt8ArrayData(JSObject* obj, const JS::AutoCheckCannotGC&) { diff --git a/js/src/vm/UbiNode.cpp b/js/src/vm/UbiNode.cpp index fe3e378a57..1b54b24d87 100644 --- a/js/src/vm/UbiNode.cpp +++ b/js/src/vm/UbiNode.cpp @@ -58,11 +58,11 @@ Concrete::size(mozilla::MallocSizeOf mallocSizeof) const MOZ_CRASH("null ubi::Node"); } -struct Node::ConstructFunctor : public BoolDefaultAdaptor { +struct Node::ConstructFunctor : public js::BoolDefaultAdaptor { template bool operator()(T* t, Node* node) { node->construct(t); return true; } }; -Node::Node(JSGCTraceKind kind, void* ptr) +Node::Node(JS::TraceKind kind, void* ptr) { CallTyped(ConstructFunctor(), ptr, kind, this); } @@ -108,11 +108,11 @@ class SimpleEdgeVectorTracer : public JS::CallbackTracer { // True if we should populate the edge's names. bool wantNames; - static void staticCallback(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind) { + static void staticCallback(JS::CallbackTracer* trc, void** thingp, JS::TraceKind kind) { static_cast(trc)->callback(thingp, kind); } - void callback(void** thingp, JSGCTraceKind kind) { + void callback(void** thingp, JS::TraceKind kind) { if (!okay) return; @@ -172,7 +172,7 @@ class SimpleEdgeRange : public EdgeRange { public: explicit SimpleEdgeRange(JSContext* cx) : edges(cx), i(0) { } - bool init(JSContext* cx, void* thing, JSGCTraceKind kind, bool wantNames = true) { + bool init(JSContext* cx, void* thing, JS::TraceKind kind, bool wantNames = true) { SimpleEdgeVectorTracer tracer(cx, &edges, wantNames); JS_TraceChildren(&tracer, thing, kind); settle(); @@ -273,7 +273,6 @@ template class TracerConcrete; } // namespace ubi } // namespace JS - namespace JS { namespace ubi { diff --git a/js/src/vm/UnboxedObject-inl.h b/js/src/vm/UnboxedObject-inl.h index 5c45516a86..78157142cf 100644 --- a/js/src/vm/UnboxedObject-inl.h +++ b/js/src/vm/UnboxedObject-inl.h @@ -161,10 +161,26 @@ SetUnboxedValue(ExclusiveContext* cx, JSObject* unboxedObject, jsid id, } } +///////////////////////////////////////////////////////////////////// +// UnboxedPlainObject +///////////////////////////////////////////////////////////////////// + +inline const UnboxedLayout& +UnboxedPlainObject::layout() const +{ + return group()->unboxedLayout(); +} + ///////////////////////////////////////////////////////////////////// // UnboxedArrayObject ///////////////////////////////////////////////////////////////////// +inline const UnboxedLayout& +UnboxedArrayObject::layout() const +{ + return group()->unboxedLayout(); +} + inline void UnboxedArrayObject::setLength(ExclusiveContext* cx, uint32_t length) { @@ -304,10 +320,28 @@ SetAnyBoxedOrUnboxedArrayLength(JSContext* cx, JSObject* obj, size_t length) } } +static inline bool +SetAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value) +{ + if (obj->isNative()) { + obj->as().setDenseElementWithType(cx, index, value); + return true; + } + return obj->as().setElement(cx, index, value); +} + ///////////////////////////////////////////////////////////////////// // Template methods for NativeObject and UnboxedArrayObject accesses. ///////////////////////////////////////////////////////////////////// +static inline JSValueType +GetBoxedOrUnboxedType(JSObject* obj) +{ + if (obj->isNative()) + return JSVAL_TYPE_MAGIC; + return obj->as().elementType(); +} + template static inline bool HasBoxedOrUnboxedDenseElements(JSObject* obj) @@ -371,6 +405,17 @@ SetBoxedOrUnboxedDenseElementNoTypeChange(JSObject* obj, size_t index, const Val obj->as().setElementNoTypeChangeSpecific(index, value); } +template +static inline bool +SetBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value) +{ + if (Type == JSVAL_TYPE_MAGIC) { + obj->as().setDenseElementWithType(cx, index, value); + return true; + } + return obj->as().setElementSpecific(cx, index, value); +} + template static inline bool EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count) @@ -387,17 +432,11 @@ EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count) return true; } -enum ShouldUpdateTypes -{ - UpdateTypes = true, - DontUpdateTypes = false -}; - template static inline DenseElementResult -SetOrExtendBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, +SetOrExtendBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj, uint32_t start, const Value* vp, uint32_t count, - ShouldUpdateTypes updateTypes) + ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update) { if (Type == JSVAL_TYPE_MAGIC) { NativeObject* nobj = &obj->as(); @@ -416,7 +455,7 @@ SetOrExtendBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, if (obj->is() && start + count >= obj->as().length()) obj->as().setLengthInt32(start + count); - if (updateTypes == DontUpdateTypes && !nobj->shouldConvertDoubleElements()) { + if (updateTypes == ShouldUpdateTypes::DontUpdate && !nobj->shouldConvertDoubleElements()) { nobj->copyDenseElements(start, vp, count); } else { for (size_t i = 0; i < count; i++) @@ -446,7 +485,7 @@ SetOrExtendBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, // which will overwrite the already-modified elements as well as the ones // that were left alone. size_t i = 0; - if (updateTypes == DontUpdateTypes) { + if (updateTypes == ShouldUpdateTypes::DontUpdate) { for (size_t j = start; i < count && j < oldInitlen; i++) nobj->setElementNoTypeChangeSpecific(j, vp[i]); } else { @@ -458,7 +497,7 @@ SetOrExtendBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, if (i != count) { obj->as().setInitializedLength(start + count); - if (updateTypes == DontUpdateTypes) { + if (updateTypes == ShouldUpdateTypes::DontUpdate) { for (; i < count; i++) nobj->initElementNoTypeChangeSpecific(start + i, vp[i]); } else { @@ -635,9 +674,9 @@ struct Signature ## Functor { \ } DenseElementResult -SetOrExtendAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, +SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj, uint32_t start, const Value* vp, uint32_t count, - ShouldUpdateTypes updateTypes); + ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update); DenseElementResult MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, @@ -650,6 +689,9 @@ CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src, void SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen); +bool +EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count); + } // namespace js #endif // vm_UnboxedObject_inl_h diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index cfb9c445fd..8e0f9b0f17 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -6,6 +6,7 @@ #include "vm/UnboxedObject-inl.h" +#include "jit/BaselineIC.h" #include "jit/JitCommon.h" #include "jit/Linker.h" @@ -40,8 +41,11 @@ UnboxedLayout::trace(JSTracer* trc) if (nativeShape_) TraceEdge(trc, &nativeShape_, "unboxed_layout_nativeShape"); - if (replacementNewGroup_) - TraceEdge(trc, &replacementNewGroup_, "unboxed_layout_replacementNewGroup"); + if (allocationScript_) + TraceEdge(trc, &allocationScript_, "unboxed_layout_allocationScript"); + + if (replacementGroup_) + TraceEdge(trc, &replacementGroup_, "unboxed_layout_replacementGroup"); if (constructorCode_) TraceEdge(trc, &constructorCode_, "unboxed_layout_constructorCode"); @@ -381,6 +385,25 @@ PropagatePropertyTypes(JSContext* cx, jsid id, ObjectGroup* oldGroup, ObjectGrou return true; } +static PlainObject* +MakeReplacementTemplateObject(JSContext* cx, HandleObjectGroup group, const UnboxedLayout &layout) +{ + PlainObject* obj = NewObjectWithGroup(cx, group, layout.getAllocKind(), + TenuredObject); + if (!obj) + return nullptr; + + for (size_t i = 0; i < layout.properties().length(); i++) { + const UnboxedLayout::Property& property = layout.properties()[i]; + if (!obj->addDataProperty(cx, NameToId(property.name), i, JSPROP_ENUMERATE)) + return nullptr; + MOZ_ASSERT(obj->slotSpan() == i + 1); + MOZ_ASSERT(!obj->inDictionaryMode()); + } + + return obj; +} + /* static */ bool UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group) { @@ -391,46 +414,68 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group) MOZ_ASSERT(!layout.nativeGroup()); + RootedObjectGroup replacementGroup(cx); + + const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_; + // Immediately clear any new script on the group. This is done by replacing // the existing new script with one for a replacement default new group. // This is done so that the size of the replacment group's objects is the // same as that for the unboxed group, so that we do not see polymorphic // slot accesses later on for sites that see converted objects from this // group and objects that were allocated using the replacement new group. - RootedObjectGroup replacementNewGroup(cx); if (layout.newScript()) { MOZ_ASSERT(!layout.isArray()); - replacementNewGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto); - if (!replacementNewGroup) + replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto); + if (!replacementGroup) return false; - PlainObject* templateObject = NewObjectWithGroup(cx, replacementNewGroup, - layout.getAllocKind(), - TenuredObject); + PlainObject* templateObject = MakeReplacementTemplateObject(cx, replacementGroup, layout); if (!templateObject) return false; - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - if (!templateObject->addDataProperty(cx, NameToId(property.name), i, JSPROP_ENUMERATE)) - return false; - MOZ_ASSERT(templateObject->slotSpan() == i + 1); - MOZ_ASSERT(!templateObject->inDictionaryMode()); - } - TypeNewScript* replacementNewScript = TypeNewScript::makeNativeVersion(cx, layout.newScript(), templateObject); if (!replacementNewScript) return false; - replacementNewGroup->setNewScript(replacementNewScript); - gc::TraceTypeNewScript(replacementNewGroup); + replacementGroup->setNewScript(replacementNewScript); + gc::TraceTypeNewScript(replacementGroup); - group->clearNewScript(cx, replacementNewGroup); + group->clearNewScript(cx, replacementGroup); + } + + // Similarly, if this group is keyed to an allocation site, replace its + // entry with a new group that has no unboxed layout. + if (layout.allocationScript()) { + RootedScript script(cx, layout.allocationScript()); + jsbytecode* pc = layout.allocationPc(); + + replacementGroup = ObjectGroupCompartment::makeGroup(cx, clasp, proto); + if (!replacementGroup) + return false; + + PlainObject* templateObject = &script->getObject(pc)->as(); + replacementGroup->addDefiniteProperties(cx, templateObject->lastProperty()); + + JSProtoKey key = layout.isArray() ? JSProto_Array : JSProto_Object; + cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc, key, + replacementGroup); + + // Clear any baseline information at this opcode which might use the old group. + if (script->hasBaselineScript()) { + jit::ICEntry& entry = script->baselineScript()->icEntryFromPCOffset(script->pcToOffset(pc)); + jit::ICFallbackStub* fallback = entry.fallbackStub(); + for (jit::ICStubIterator iter = fallback->beginChain(); !iter.atEnd(); iter++) + iter.unlink(cx); + if (fallback->isNewObject_Fallback()) + fallback->toNewObject_Fallback()->setTemplateObject(nullptr); + else if (fallback->isNewArray_Fallback()) + fallback->toNewArray_Fallback()->setTemplateGroup(replacementGroup); + } } - const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_; size_t nfixed = layout.isArray() ? 0 : gc::GetGCKindSlots(layout.getAllocKind()); if (layout.isArray()) { @@ -477,14 +522,14 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group) return false; HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id); - if (nativeProperty->canSetDefinite(i)) + if (nativeProperty && nativeProperty->canSetDefinite(i)) nativeProperty->setDefinite(i); } } layout.nativeGroup_ = nativeGroup; layout.nativeShape_ = shape; - layout.replacementNewGroup_ = replacementNewGroup; + layout.replacementGroup_ = replacementGroup; nativeGroup->setOriginalUnboxedGroup(group); @@ -563,7 +608,7 @@ UnboxedPlainObject::convertToNative(JSContext* cx, JSObject* obj) if (!GetOwnPropertyDescriptor(cx, nexpando, id, &desc)) return false; ObjectOpResult result; - if (!StandardDefineProperty(cx, nobj, id, desc, result)) + if (!DefineProperty(cx, nobj, id, desc, result)) return false; MOZ_ASSERT(result.ok()); } @@ -691,7 +736,7 @@ UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId if (!desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) { // This define is equivalent to setting an existing property. if (obj->as().setValue(cx, *property, desc.value())) - return true; + return result.succeed(); } // Trying to incompatibly redefine an existing property requires the @@ -699,8 +744,7 @@ UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId if (!convertToNative(cx, obj)) return false; - return DefineProperty(cx, obj, id, desc, result) && - result.checkStrict(cx, obj, id); + return DefineProperty(cx, obj, id, desc, result); } // Define the property on the expando object. @@ -840,7 +884,8 @@ UnboxedPlainObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, Hand } /* static */ bool -UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties) +UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, + bool enumerableOnly) { UnboxedExpandoObject* expando = obj->as().maybeExpando(); @@ -863,6 +908,8 @@ UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& if (expando) { Vector ids(cx); for (Shape::Range r(expando->lastProperty()); !r.empty(); r.popFront()) { + if (enumerableOnly && !r.front().enumerable()) + continue; if (!ids.append(r.front().propid())) return false; } @@ -958,13 +1005,14 @@ UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj) /* static */ UnboxedArrayObject* UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32_t length, - NewObjectKind newKind) + NewObjectKind newKind, uint32_t maxLength) { MOZ_ASSERT(length <= MaximumCapacity); MOZ_ASSERT(group->clasp() == &class_); uint32_t elementSize = UnboxedTypeSize(group->unboxedLayout().elementType()); - uint32_t nbytes = offsetOfInlineElements() + elementSize * length; + uint32_t capacity = Min(length, maxLength); + uint32_t nbytes = offsetOfInlineElements() + elementSize * capacity; UnboxedArrayObject* res; if (nbytes <= JSObject::MAX_BYTE_SIZE) { @@ -972,7 +1020,7 @@ UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32 // If there was no provided length information, pick an allocation kind // to accommodate small arrays (as is done for normal native arrays). - if (length == 0) + if (capacity == 0) allocKind = gc::AllocKind::OBJECT8; res = NewObjectWithGroup(cx, group, allocKind, newKind); @@ -980,14 +1028,20 @@ UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32 return nullptr; res->setInlineElements(); - size_t capacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize; - res->setCapacityIndex(exactCapacityIndex(capacity)); + size_t actualCapacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize; + MOZ_ASSERT(actualCapacity >= capacity); + res->setCapacityIndex(exactCapacityIndex(actualCapacity)); } else { res = NewObjectWithGroup(cx, group, gc::AllocKind::OBJECT0, newKind); if (!res) return nullptr; - res->elements_ = AllocateObjectBuffer(cx, res, length * elementSize); + uint32_t capacityIndex = (capacity == length) + ? CapacityMatchesLengthIndex + : chooseCapacityIndex(capacity, length); + uint32_t actualCapacity = computeCapacity(capacityIndex, length); + + res->elements_ = AllocateObjectBuffer(cx, res, actualCapacity * elementSize); if (!res->elements_) { // Make the object safe for GC. res->setInlineElements(); @@ -995,7 +1049,7 @@ UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32 return nullptr; } - res->setCapacityIndex(CapacityMatchesLengthIndex); + res->setCapacityIndex(capacityIndex); } res->setLength(cx, length); @@ -1471,13 +1525,18 @@ UnboxedArrayObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, Hand } /* static */ bool -UnboxedArrayObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties) +UnboxedArrayObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, + bool enumerableOnly) { for (size_t i = 0; i < obj->as().initializedLength(); i++) { if (!properties.append(INT_TO_JSID(i))) return false; } - return properties.append(NameToId(cx->names().length)); + + if (!enumerableOnly && !properties.append(NameToId(cx->names().length))) + return false; + + return true; } const Class UnboxedArrayObject::class_ = { @@ -1798,26 +1857,26 @@ bool js::TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape, ObjectGroup* group, PreliminaryObjectArray* objects) { - // Unboxed objects are nightly only for now. The getenv() call will be + bool isArray = !templateShape; + + // Unboxed arrays are nightly only for now. The getenv() call will be // removed when they are on by default. See bug 1153266. + if (isArray) { #ifdef NIGHTLY_BUILD - if (templateShape) { - if (!getenv("JS_OPTION_USE_UNBOXED_OBJECTS")) { - if (!group->runtimeFromAnyThread()->options().unboxedObjects()) + if (!getenv("JS_OPTION_USE_UNBOXED_ARRAYS")) { + if (!group->runtimeFromAnyThread()->options().unboxedArrays()) return true; } +#else + return true; +#endif } else { - if (!group->runtimeFromAnyThread()->options().unboxedArrays()) + if (jit::js_JitOptions.disableUnboxedObjects) return true; } -#else - return true; -#endif MOZ_ASSERT_IF(templateShape, !templateShape->getObjectFlags()); - bool isArray = !templateShape; - if (group->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global())) return true; @@ -1955,10 +2014,11 @@ js::TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape, } DefineBoxedOrUnboxedFunctor6(SetOrExtendBoxedOrUnboxedDenseElements, - JSContext*, JSObject*, uint32_t, const Value*, uint32_t, ShouldUpdateTypes); + ExclusiveContext*, JSObject*, uint32_t, const Value*, uint32_t, + ShouldUpdateTypes); DenseElementResult -js::SetOrExtendAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, +js::SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj, uint32_t start, const Value* vp, uint32_t count, ShouldUpdateTypes updateTypes) { diff --git a/js/src/vm/UnboxedObject.h b/js/src/vm/UnboxedObject.h index b1954a6056..04be43646f 100644 --- a/js/src/vm/UnboxedObject.h +++ b/js/src/vm/UnboxedObject.h @@ -64,6 +64,17 @@ class UnboxedLayout : public mozilla::LinkedListElement HeapPtrObjectGroup nativeGroup_; HeapPtrShape nativeShape_; + // Any script/pc which the associated group is created for. + HeapPtrScript allocationScript_; + jsbytecode* allocationPc_; + + // If nativeGroup is set and this object originally had a TypeNewScript or + // was keyed to an allocation site, this points to the group which replaced + // this one. This link is only needed to keep the replacement group from + // being GC'ed. If it were GC'ed and a new one regenerated later, that new + // group might have a different allocation kind from this group. + HeapPtrObjectGroup replacementGroup_; + // The following members are only used for unboxed plain objects. // All properties on objects with this layout, in enumeration order. @@ -79,14 +90,6 @@ class UnboxedLayout : public mozilla::LinkedListElement // structure as the trace list on a TypeDescr. int32_t* traceList_; - // If nativeGroup is set and this object originally had a TypeNewScript, - // this points to the default 'new' group which replaced this one (and - // which might itself have been cleared since). This link is only needed to - // keep the replacement group from being GC'ed. If it were GC'ed and a new - // one regenerated later, that new group might have a different allocation - // kind from this group. - HeapPtrObjectGroup replacementNewGroup_; - // If this layout has been used to construct script or JSON constant // objects, this code might be filled in to more quickly fill in objects // from an array of values. @@ -99,8 +102,9 @@ class UnboxedLayout : public mozilla::LinkedListElement public: UnboxedLayout() - : nativeGroup_(nullptr), nativeShape_(nullptr), size_(0), newScript_(nullptr), - traceList_(nullptr), replacementNewGroup_(nullptr), constructorCode_(nullptr), + : nativeGroup_(nullptr), nativeShape_(nullptr), + allocationScript_(nullptr), allocationPc_(nullptr), replacementGroup_(nullptr), + size_(0), newScript_(nullptr), traceList_(nullptr), constructorCode_(nullptr), elementType_(JSVAL_TYPE_MAGIC) {} @@ -114,11 +118,18 @@ class UnboxedLayout : public mozilla::LinkedListElement } ~UnboxedLayout() { + if (newScript_) + newScript_->clear(); js_delete(newScript_); js_free(traceList_); + + nativeGroup_.init(nullptr); + nativeShape_.init(nullptr); + replacementGroup_.init(nullptr); + constructorCode_.init(nullptr); } - bool isArray() { + bool isArray() const { return elementType_ != JSVAL_TYPE_MAGIC; } @@ -134,6 +145,19 @@ class UnboxedLayout : public mozilla::LinkedListElement void setNewScript(TypeNewScript* newScript, bool writeBarrier = true); + JSScript* allocationScript() const { + return allocationScript_; + } + + jsbytecode* allocationPc() const { + return allocationPc_; + } + + void setAllocationSite(JSScript* script, jsbytecode* pc) { + allocationScript_ = script; + allocationPc_ = pc; + } + const int32_t* traceList() const { return traceList_; } @@ -238,12 +262,11 @@ class UnboxedPlainObject : public JSObject static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result); - static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties); + static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, + bool enumerableOnly); static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable); - const UnboxedLayout& layout() const { - return group()->unboxedLayout(); - } + inline const UnboxedLayout& layout() const; const UnboxedLayout& layoutDontCheckGeneration() const { return group()->unboxedLayoutDontCheckGeneration(); @@ -375,12 +398,11 @@ class UnboxedArrayObject : public JSObject static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result); - static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties); + static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, + bool enumerableOnly); static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable); - const UnboxedLayout& layout() const { - return group()->unboxedLayout(); - } + inline const UnboxedLayout& layout() const; const UnboxedLayout& layoutDontCheckGeneration() const { return group()->unboxedLayoutDontCheckGeneration(); @@ -396,7 +418,8 @@ class UnboxedArrayObject : public JSObject static bool convertToNative(JSContext* cx, JSObject* obj); static UnboxedArrayObject* create(ExclusiveContext* cx, HandleObjectGroup group, - uint32_t length, NewObjectKind newKind); + uint32_t length, NewObjectKind newKind, + uint32_t maxLength = MaximumCapacity); void fillAfterConvert(ExclusiveContext* cx, const AutoValueVector& values, size_t* valueCursor); diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index d9e303d89c..ecf6ed833d 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,7 +29,7 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 283; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 284; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); diff --git a/js/xpconnect/idl/nsIAddonInterposition.idl b/js/xpconnect/idl/nsIAddonInterposition.idl index e9bf8b58f3..9ed0f9b7a3 100644 --- a/js/xpconnect/idl/nsIAddonInterposition.idl +++ b/js/xpconnect/idl/nsIAddonInterposition.idl @@ -20,7 +20,7 @@ * property, it should return a replacement property descriptor for it. If not, * it should return null. */ -[scriptable,uuid(e5453950-d95a-11e3-9c1a-0800200c9a66)] +[scriptable,uuid(215353cb-6e77-462f-a791-6891f42e593f)] interface nsIAddonInterposition : nsISupports { /** @@ -35,8 +35,23 @@ interface nsIAddonInterposition : nsISupports * @param prop The name of the property being accessed. * @return A property descriptor or null. */ - jsval interpose(in jsval addonId, - in jsval target, - in nsIIDPtr iface, - in jsval prop); + jsval interposeProperty(in jsval addonId, + in jsval target, + in nsIIDPtr iface, + in jsval prop); + /** + * We're intercepting calls from add-ons scopes into non-addon scopes. + * + * @param addonId The ID of the add-on accessing the property. + * @param originalFunc The function object being intercepted. + * @param originalThis The |this| value for the intercepted call. + * @param args The arguments of the original call in an array. + * @return The result of the call. NOTE: after the call interception, + * the original function will not be called automatically, so the + * implementer has to do that. + */ + jsval interposeCall(in jsval addonId, + in jsval originalFunc, + in jsval originalThis, + in jsval args); }; diff --git a/js/xpconnect/idl/xpccomponents.idl b/js/xpconnect/idl/xpccomponents.idl index 5de1d8180c..8365359cca 100644 --- a/js/xpconnect/idl/xpccomponents.idl +++ b/js/xpconnect/idl/xpccomponents.idl @@ -123,7 +123,7 @@ interface ScheduledGCCallback : nsISupports /** * interface of Components.utils */ -[scriptable, uuid(1a9bcb85-67a9-4a22-89a2-40eaf7fc31ec)] +[scriptable, uuid(e04a4a58-2b5e-4a74-941a-0d344b057c5a)] interface nsIXPCComponents_Utils : nsISupports { @@ -686,6 +686,12 @@ interface nsIXPCComponents_Utils : nsISupports [implicit_jscontext] void setAddonInterposition(in ACString addonId, in nsIAddonInterposition interposition); + /* + * Enables call interpositions from addon scopes to any functions in the scope of |target|. + */ + [implicit_jscontext] + void setAddonCallInterposition(in jsval target); + /* * Return a fractional number of milliseconds from process * startup, measured with a monotonic clock. diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index b0067849a8..ba6bbd4379 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -444,7 +444,7 @@ static bool sandbox_convert(JSContext* cx, HandleObject obj, JSType type, MutableHandleValue vp) { if (type == JSTYPE_OBJECT) { - vp.set(OBJECT_TO_JSVAL(obj)); + vp.setObject(*obj); return true; } diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index be0257b2cf..4d9ef2f5a1 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -328,7 +328,8 @@ nsXPCComponents_Interfaces::Resolve(nsIXPConnectWrappedNative* wrapper, *_retval = JS_DefinePropertyById(cx, obj, id, idobj, JSPROP_ENUMERATE | JSPROP_READONLY | - JSPROP_PERMANENT); + JSPROP_PERMANENT | + JSPROP_RESOLVING); } } } @@ -575,7 +576,8 @@ nsXPCComponents_InterfacesByID::Resolve(nsIXPConnectWrappedNative* wrapper, JS_DefinePropertyById(cx, obj, id, idobj, JSPROP_ENUMERATE | JSPROP_READONLY | - JSPROP_PERMANENT); + JSPROP_PERMANENT | + JSPROP_RESOLVING); } } } @@ -802,7 +804,8 @@ nsXPCComponents_Classes::Resolve(nsIXPConnectWrappedNative* wrapper, *_retval = JS_DefinePropertyById(cx, obj, id, idobj, JSPROP_ENUMERATE | JSPROP_READONLY | - JSPROP_PERMANENT); + JSPROP_PERMANENT | + JSPROP_RESOLVING); } } } @@ -1049,7 +1052,8 @@ nsXPCComponents_ClassesByID::Resolve(nsIXPConnectWrappedNative* wrapper, *_retval = JS_DefinePropertyById(cx, obj, id, idobj, JSPROP_ENUMERATE | JSPROP_READONLY | - JSPROP_PERMANENT); + JSPROP_PERMANENT | + JSPROP_RESOLVING); } } } @@ -1253,7 +1257,8 @@ nsXPCComponents_Results::Resolve(nsIXPConnectWrappedNative* wrapper, if (!JS_DefinePropertyById(cx, obj, id, (uint32_t)rv, JSPROP_ENUMERATE | JSPROP_READONLY | - JSPROP_PERMANENT)) { + JSPROP_PERMANENT | + JSPROP_RESOLVING)) { return NS_ERROR_UNEXPECTED; } } @@ -3546,6 +3551,21 @@ nsXPCComponents_Utils::SetAddonInterposition(const nsACString& addonIdStr, return NS_OK; } +NS_IMETHODIMP +nsXPCComponents_Utils::SetAddonCallInterposition(HandleValue target, + JSContext* cx) +{ + NS_ENSURE_TRUE(target.isObject(), NS_ERROR_INVALID_ARG); + RootedObject targetObj(cx, &target.toObject()); + targetObj = js::CheckedUnwrap(targetObj); + NS_ENSURE_TRUE(targetObj, NS_ERROR_INVALID_ARG); + XPCWrappedNativeScope* xpcScope = ObjectScope(targetObj); + NS_ENSURE_TRUE(xpcScope, NS_ERROR_INVALID_ARG); + + xpcScope->SetAddonCallInterposition(); + return NS_OK; +} + NS_IMETHODIMP nsXPCComponents_Utils::Now(double* aRetval) { diff --git a/js/xpconnect/src/XPCJSID.cpp b/js/xpconnect/src/XPCJSID.cpp index 2394fb8d5e..97f43dbc33 100644 --- a/js/xpconnect/src/XPCJSID.cpp +++ b/js/xpconnect/src/XPCJSID.cpp @@ -406,7 +406,7 @@ nsJSIID::Resolve(nsIXPConnectWrappedNative* wrapper, *resolvedp = true; *_retval = JS_DefinePropertyById(cx, obj, id, val, JSPROP_ENUMERATE | JSPROP_READONLY | - JSPROP_PERMANENT); + JSPROP_PERMANENT | JSPROP_RESOLVING); } return NS_OK; diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 3361acc3aa..07994227f1 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -3487,7 +3487,7 @@ XPCJSRuntime::OnJSContextNew(JSContext* cx) return false; } mStrIDs[i] = INTERNED_STRING_TO_JSID(cx, str); - mStrJSVals[i] = STRING_TO_JSVAL(str); + mStrJSVals[i].setString(str); } if (!mozilla::dom::DefineStaticJSVals(cx)) { diff --git a/js/xpconnect/src/XPCLocale.cpp b/js/xpconnect/src/XPCLocale.cpp index 2179554e43..37b504f20b 100644 --- a/js/xpconnect/src/XPCLocale.cpp +++ b/js/xpconnect/src/XPCLocale.cpp @@ -115,7 +115,7 @@ private: return false; } - rval.set(STRING_TO_JSVAL(ucstr)); + rval.setString(ucstr); return true; } diff --git a/js/xpconnect/src/XPCShellImpl.cpp b/js/xpconnect/src/XPCShellImpl.cpp index 71288d362f..afc55be7a4 100644 --- a/js/xpconnect/src/XPCShellImpl.cpp +++ b/js/xpconnect/src/XPCShellImpl.cpp @@ -700,7 +700,7 @@ env_setProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue JS_ReportError(cx, "can't set envariable %s to %s", name.ptr(), value.ptr()); return false; } - vp.set(STRING_TO_JSVAL(valstr)); + vp.setString(valstr); #endif /* !defined SOLARIS */ return result.succeed(); } diff --git a/js/xpconnect/src/XPCWrappedJSClass.cpp b/js/xpconnect/src/XPCWrappedJSClass.cpp index 3c67954a10..3c67667229 100644 --- a/js/xpconnect/src/XPCWrappedJSClass.cpp +++ b/js/xpconnect/src/XPCWrappedJSClass.cpp @@ -1139,7 +1139,7 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex, goto pre_call_clean_up; } } - *sp++ = OBJECT_TO_JSVAL(out_obj); + *sp++ = JS::ObjectValue(*out_obj); } else *sp++ = val; } diff --git a/js/xpconnect/src/XPCWrappedNativeInfo.cpp b/js/xpconnect/src/XPCWrappedNativeInfo.cpp index 729475e407..69afc59fe4 100644 --- a/js/xpconnect/src/XPCWrappedNativeInfo.cpp +++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp @@ -98,7 +98,7 @@ XPCNativeMember::Resolve(XPCCallContext& ccx, XPCNativeInterface* iface, js::SetFunctionNativeReserved(funobj, XPC_FUNCTION_PARENT_OBJECT_SLOT, ObjectValue(*parent)); - *vp = OBJECT_TO_JSVAL(funobj); + vp->setObject(*funobj); return true; } diff --git a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp index 586c5c1180..f5dfe5fe34 100644 --- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp +++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp @@ -61,7 +61,7 @@ ToStringGuts(XPCCallContext& ccx) if (!str) return false; - ccx.SetRetVal(STRING_TO_JSVAL(str)); + ccx.SetRetVal(JS::StringValue(str)); return true; } @@ -199,6 +199,8 @@ DefinePropertyIfFound(XPCCallContext& ccx, bool found; const char* name; + propFlags |= JSPROP_RESOLVING; + if (set) { if (iface) found = true; @@ -357,13 +359,14 @@ DefinePropertyIfFound(XPCCallContext& ccx, if (scope->HasInterposition()) { Rooted desc(ccx); - if (!xpc::Interpose(ccx, obj, iface->GetIID(), id, &desc)) + if (!xpc::InterposeProperty(ccx, obj, iface->GetIID(), id, &desc)) return false; if (desc.object()) { AutoResolveName arn(ccx, id); if (resolved) *resolved = true; + desc.attributesRef() |= JSPROP_RESOLVING; return JS_DefinePropertyById(ccx, obj, id, desc); } } @@ -451,7 +454,7 @@ static bool XPC_WN_Shared_Convert(JSContext* cx, HandleObject obj, JSType type, MutableHandleValue vp) { if (type == JSTYPE_OBJECT) { - vp.set(OBJECT_TO_JSVAL(obj)); + vp.setObject(*obj); return true; } @@ -466,7 +469,7 @@ XPC_WN_Shared_Convert(JSContext* cx, HandleObject obj, JSType type, MutableHandl XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo(); if (si && (si->GetFlags().WantCall() || si->GetFlags().WantConstruct())) { - vp.set(OBJECT_TO_JSVAL(obj)); + vp.setObject(*obj); return true; } } @@ -921,7 +924,8 @@ XPC_WN_Helper_Enumerate(JSContext* cx, HandleObject obj) /***************************************************************************/ static bool -XPC_WN_JSOp_Enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties) +XPC_WN_JSOp_Enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, + bool enumerableOnly) { XPCCallContext ccx(JS_CALLER, cx, obj); XPCWrappedNative* wrapper = ccx.GetWrapper(); diff --git a/js/xpconnect/src/XPCWrappedNativeScope.cpp b/js/xpconnect/src/XPCWrappedNativeScope.cpp index fe221bcb8b..088ec7f4f8 100644 --- a/js/xpconnect/src/XPCWrappedNativeScope.cpp +++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp @@ -83,6 +83,7 @@ XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext* cx, mComponents(nullptr), mNext(nullptr), mGlobalJSObject(aGlobal), + mHasCallInterpositions(false), mIsContentXBLScope(false), mIsAddonScope(false) { @@ -131,10 +132,19 @@ XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext* cx, JSAddonId* addonId = JS::AddonIdOfObject(aGlobal); if (gInterpositionMap) { + bool isSystem = nsContentUtils::IsSystemPrincipal(principal); if (InterpositionMap::Ptr p = gInterpositionMap->lookup(addonId)) { - MOZ_RELEASE_ASSERT(nsContentUtils::IsSystemPrincipal(principal)); + MOZ_RELEASE_ASSERT(isSystem); mInterposition = p->value(); } + // We also want multiprocessCompatible add-ons to have a default interposition. + if (!mInterposition && addonId && isSystem) { + bool interpositionEnabled = mozilla::Preferences::GetBool( + "extensions.interposition.enabled", false); + if (interpositionEnabled) { + mInterposition = do_GetService("@mozilla.org/addons/default-addon-shims;1"); + } + } } } @@ -200,7 +210,7 @@ XPCWrappedNativeScope::AttachComponentsObject(JSContext* aCx) // The global Components property is non-configurable if it's a full // nsXPCComponents object. That way, if it's an nsXPCComponentsBase, // enableUniversalXPConnect can upgrade it later. - unsigned attrs = JSPROP_READONLY; + unsigned attrs = JSPROP_READONLY | JSPROP_RESOLVING; nsCOMPtr c = do_QueryInterface(mComponents); if (c) attrs |= JSPROP_PERMANENT; diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 15a9e13d63..50a36145b6 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -376,7 +376,7 @@ CreateGlobalObject(JSContext* cx, const JSClass* clasp, nsIPrincipal* principal, if (!((const js::Class*)clasp)->ext.isWrappedNative) { VerifyTraceProtoAndIfaceCacheCalledTracer trc(JS_GetRuntime(cx)); - JS_TraceChildren(&trc, global, JSTRACE_OBJECT); + JS_TraceChildren(&trc, global, JS::TraceKind::Object); MOZ_ASSERT(trc.ok, "Trace hook on global needs to call TraceXPCGlobal for XPConnect compartments."); } #endif diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 8ea17d03c6..155925f13f 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -1168,9 +1168,6 @@ public: js::PointerHasher, js::SystemAllocPolicy> InterpositionMap; - static bool SetAddonInterposition(JSAddonId* addonId, - nsIAddonInterposition* interp); - // Gets the appropriate scope object for XBL in this scope. The context // must be same-compartment with the global upon entering, and the scope // object is wrapped into the compartment of the global. @@ -1192,6 +1189,12 @@ public: bool HasInterposition() { return mInterposition; } nsCOMPtr GetInterposition(); + static bool SetAddonInterposition(JSAddonId* addonId, + nsIAddonInterposition* interp); + + void SetAddonCallInterposition() { mHasCallInterpositions = true; } + bool HasCallInterposition() { return mHasCallInterpositions; }; + protected: virtual ~XPCWrappedNativeScope(); @@ -1230,10 +1233,14 @@ private: // Lazily created sandboxes for addon code. nsTArray mAddonScopes; - // This is a service that will be use to interpose on all calls out of this - // scope. If it's null, no interposition is done. + // This is a service that will be use to interpose on some property accesses on + // objects from other scope, for add-on compatibility reasons. nsCOMPtr mInterposition; + // If this flag is set, we intercept function calls on vanilla JS function objects + // from this scope if the caller scope has mInterposition set. + bool mHasCallInterpositions; + nsAutoPtr mDOMExpandoSet; JS::WeakMapPtr mXrayExpandos; diff --git a/js/xpconnect/tests/unit/test_interposition.js b/js/xpconnect/tests/unit/test_interposition.js index 9711c46ea0..2590114b24 100644 --- a/js/xpconnect/tests/unit/test_interposition.js +++ b/js/xpconnect/tests/unit/test_interposition.js @@ -34,7 +34,7 @@ let TestInterposition = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonInterposition, Ci.nsISupportsWeakReference]), - interpose: function(addonId, target, iid, prop) { + interposeProperty: function(addonId, target, iid, prop) { do_check_eq(addonId, ADDONID); do_check_eq(gExpectedProp, prop); gExpectedProp = undefined; @@ -56,6 +56,12 @@ let TestInterposition = { } return null; + }, + + interposeCall: function(addonId, originalFunc, originalThis, args) { + do_check_eq(addonId, ADDONID); + args.splice(0, 0, addonId); + return originalFunc.apply(originalThis, args); } }; @@ -63,8 +69,10 @@ function run_test() { Cu.setAddonInterposition(ADDONID, TestInterposition); + Cu.importGlobalProperties(["XMLHttpRequest"]); + let sandbox = Cu.Sandbox(this, {addonId: ADDONID}); - sandbox.outerObj = {}; + sandbox.outerObj = new XMLHttpRequest(); expectAccess("abcxyz", () => { Cu.evalInSandbox("outerObj.abcxyz = 12;", sandbox); @@ -136,5 +144,17 @@ function run_test() do_check_eq(desc.configurable, false); }); + let moduleScope = Cu.Sandbox(this); + moduleScope.ADDONID = ADDONID; + moduleScope.do_check_eq = do_check_eq; + function funToIntercept(addonId) { + do_check_eq(addonId, ADDONID); + counter++; + } + sandbox.moduleFunction = Cu.evalInSandbox(funToIntercept.toSource() + "; funToIntercept", moduleScope); + Cu.evalInSandbox("var counter = 0;", moduleScope); + Cu.evalInSandbox("Components.utils.setAddonCallInterposition(this);", moduleScope); + Cu.evalInSandbox("moduleFunction()", sandbox); + do_check_eq(Cu.evalInSandbox("counter", moduleScope), 1); Cu.setAddonInterposition(ADDONID, null); } diff --git a/js/xpconnect/wrappers/AddonWrapper.cpp b/js/xpconnect/wrappers/AddonWrapper.cpp index 96a37fdc41..2b66a48b22 100644 --- a/js/xpconnect/wrappers/AddonWrapper.cpp +++ b/js/xpconnect/wrappers/AddonWrapper.cpp @@ -11,6 +11,8 @@ #include "jsfriendapi.h" #include "nsIAddonInterposition.h" #include "xpcprivate.h" +#include "mozilla/dom/BindingUtils.h" +#include "nsGlobalWindow.h" #include "nsID.h" @@ -20,9 +22,20 @@ using namespace JS; namespace xpc { bool -Interpose(JSContext* cx, HandleObject target, const nsIID* iid, HandleId id, - MutableHandle descriptor) +InterposeProperty(JSContext* cx, HandleObject target, const nsIID* iid, HandleId id, + MutableHandle descriptor) { + // We only want to do interpostion on DOM instances and + // wrapped natives. + RootedObject unwrapped(cx, UncheckedUnwrap(target)); + const js::Class* clasp = js::GetObjectClass(unwrapped); + if (!mozilla::dom::IsDOMClass(clasp) && + !IS_WN_CLASS(clasp) && + !IS_PROTO_CLASS(clasp) && + clasp != &OuterWindowProxyClass) { + return true; + } + XPCWrappedNativeScope* scope = ObjectScope(CurrentGlobalOrNull(cx)); MOZ_ASSERT(scope->HasInterposition()); @@ -32,8 +45,8 @@ Interpose(JSContext* cx, HandleObject target, const nsIID* iid, HandleId id, RootedValue prop(cx, IdToValue(id)); RootedValue targetValue(cx, ObjectValue(*target)); RootedValue descriptorVal(cx); - nsresult rv = interp->Interpose(addonIdValue, targetValue, - iid, prop, &descriptorVal); + nsresult rv = interp->InterposeProperty(addonIdValue, targetValue, + iid, prop, &descriptorVal); if (NS_FAILED(rv)) { xpc::Throw(cx, rv); return false; @@ -66,12 +79,64 @@ Interpose(JSContext* cx, HandleObject target, const nsIID* iid, HandleId id, return true; } +bool +InterposeCall(JSContext* cx, JS::HandleObject target, const JS::CallArgs& args, bool* done) +{ + *done = false; + XPCWrappedNativeScope* scope = ObjectScope(CurrentGlobalOrNull(cx)); + MOZ_ASSERT(scope->HasInterposition()); + + nsCOMPtr interp = scope->GetInterposition(); + + RootedObject unwrappedTarget(cx, UncheckedUnwrap(target)); + XPCWrappedNativeScope* targetScope = ObjectScope(unwrappedTarget); + bool hasInterpostion = targetScope->HasCallInterposition(); + + if (!hasInterpostion) + return true; + + // If there is a call interpostion, we don't want to propogate the + // call to Base: + *done = true; + + JSAddonId* addonId = AddonIdOfObject(target); + RootedValue addonIdValue(cx, StringValue(StringOfAddonId(addonId))); + RootedValue targetValue(cx, ObjectValue(*target)); + RootedValue thisValue(cx, args.thisv()); + RootedObject argsArray(cx, ConvertArgsToArray(cx, args)); + if (!argsArray) + return false; + + RootedValue argsVal(cx, ObjectValue(*argsArray)); + RootedValue returnVal(cx); + + nsresult rv = interp->InterposeCall(addonIdValue, targetValue, + thisValue, argsVal, args.rval()); + if (NS_FAILED(rv)) { + xpc::Throw(cx, rv); + return false; + } + + return true; +} + +template +bool AddonWrapper::call(JSContext* cx, JS::Handle wrapper, + const JS::CallArgs& args) const +{ + bool done = false; + if (!InterposeCall(cx, wrapper, args, &done)) + return false; + + return done || Base::call(cx, wrapper, args); +} + template bool AddonWrapper::getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, MutableHandle desc) const { - if (!Interpose(cx, wrapper, nullptr, id, desc)) + if (!InterposeProperty(cx, wrapper, nullptr, id, desc)) return false; if (desc.object()) @@ -85,7 +150,7 @@ bool AddonWrapper::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, MutableHandle desc) const { - if (!Interpose(cx, wrapper, nullptr, id, desc)) + if (!InterposeProperty(cx, wrapper, nullptr, id, desc)) return false; if (desc.object()) @@ -100,7 +165,7 @@ AddonWrapper::get(JSContext* cx, JS::Handle wrapper, JS::Handle JS::Handle id, JS::MutableHandle vp) const { Rooted desc(cx); - if (!Interpose(cx, wrapper, nullptr, id, &desc)) + if (!InterposeProperty(cx, wrapper, nullptr, id, &desc)) return false; if (!desc.object()) @@ -119,11 +184,11 @@ AddonWrapper::get(JSContext* cx, JS::Handle wrapper, JS::Handle template bool -AddonWrapper::set(JSContext *cx, JS::HandleObject wrapper, JS::HandleId id, JS::HandleValue v, - JS::HandleValue receiver, JS::ObjectOpResult &result) const +AddonWrapper::set(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::HandleValue v, + JS::HandleValue receiver, JS::ObjectOpResult& result) const { Rooted desc(cx); - if (!Interpose(cx, wrapper, nullptr, id, &desc)) + if (!InterposeProperty(cx, wrapper, nullptr, id, &desc)) return false; if (!desc.object()) @@ -146,12 +211,12 @@ AddonWrapper::set(JSContext *cx, JS::HandleObject wrapper, JS::HandleId id template bool -AddonWrapper::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, +AddonWrapper::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, Handle desc, - ObjectOpResult &result) const + ObjectOpResult& result) const { Rooted interpDesc(cx); - if (!Interpose(cx, wrapper, nullptr, id, &interpDesc)) + if (!InterposeProperty(cx, wrapper, nullptr, id, &interpDesc)) return false; if (!interpDesc.object()) @@ -163,11 +228,11 @@ AddonWrapper::defineProperty(JSContext *cx, HandleObject wrapper, HandleId template bool -AddonWrapper::delete_(JSContext *cx, HandleObject wrapper, HandleId id, - ObjectOpResult &result) const +AddonWrapper::delete_(JSContext* cx, HandleObject wrapper, HandleId id, + ObjectOpResult& result) const { Rooted desc(cx); - if (!Interpose(cx, wrapper, nullptr, id, &desc)) + if (!InterposeProperty(cx, wrapper, nullptr, id, &desc)) return false; if (!desc.object()) diff --git a/js/xpconnect/wrappers/AddonWrapper.h b/js/xpconnect/wrappers/AddonWrapper.h index d02725a33a..8515f71ad1 100644 --- a/js/xpconnect/wrappers/AddonWrapper.h +++ b/js/xpconnect/wrappers/AddonWrapper.h @@ -16,28 +16,34 @@ namespace xpc { bool -Interpose(JSContext* cx, JS::HandleObject target, const nsIID* iid, JS::HandleId id, - JS::MutableHandle descriptor); +InterposeProperty(JSContext* cx, JS::HandleObject target, const nsIID* iid, JS::HandleId id, + JS::MutableHandle descriptor); + +bool +InterposeCall(JSContext* cx, JS::HandleObject wrapper, + const JS::CallArgs& args, bool& done); template class AddonWrapper : public Base { public: explicit MOZ_CONSTEXPR AddonWrapper(unsigned flags) : Base(flags) { } - virtual bool getOwnPropertyDescriptor(JSContext *cx, JS::Handle wrapper, + virtual bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle wrapper, JS::Handle id, JS::MutableHandle desc) const override; - virtual bool defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, + virtual bool defineProperty(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::Handle desc, - JS::ObjectOpResult &result) const override; - virtual bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, - JS::ObjectOpResult &result) const override; - virtual bool get(JSContext *cx, JS::Handle wrapper, JS::Handle receiver, + JS::ObjectOpResult& result) const override; + virtual bool delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, + JS::ObjectOpResult& result) const override; + virtual bool get(JSContext* cx, JS::Handle wrapper, JS::Handle receiver, JS::Handle id, JS::MutableHandle vp) const override; - virtual bool set(JSContext *cx, JS::HandleObject wrapper, JS::HandleId id, JS::HandleValue v, - JS::HandleValue receiver, JS::ObjectOpResult &result) const override; + virtual bool set(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::HandleValue v, + JS::HandleValue receiver, JS::ObjectOpResult& result) const override; + virtual bool call(JSContext* cx, JS::Handle wrapper, + const JS::CallArgs& args) const override; - virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle wrapper, + virtual bool getPropertyDescriptor(JSContext* cx, JS::Handle wrapper, JS::Handle id, JS::MutableHandle desc) const override; diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index 8f116934b9..a1d14dccb1 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -983,11 +983,11 @@ XrayTraits::attachExpandoObject(JSContext* cx, HandleObject target, // AddRef and store the principal. NS_ADDREF(origin); - JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN, PRIVATE_TO_JSVAL(origin)); + JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN, JS::PrivateValue(origin)); // Note the exclusive global, if any. JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL, - OBJECT_TO_JSVAL(exclusiveGlobal)); + ObjectOrNullValue(exclusiveGlobal)); // If this is our first expando object, take the opportunity to preserve // the wrapper. This keeps our expandos alive even if the Xray wrapper gets @@ -997,7 +997,7 @@ XrayTraits::attachExpandoObject(JSContext* cx, HandleObject target, preserveWrapper(target); // Insert it at the front of the chain. - JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_NEXT, OBJECT_TO_JSVAL(chain)); + JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_NEXT, ObjectOrNullValue(chain)); setExpandoChain(cx, target, expandoObject); return expandoObject; diff --git a/media/mtransport/dtlsidentity.cpp b/media/mtransport/dtlsidentity.cpp index b0a0b21907..63be7a3d6b 100644 --- a/media/mtransport/dtlsidentity.cpp +++ b/media/mtransport/dtlsidentity.cpp @@ -4,30 +4,26 @@ * 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 -#include "logging.h" -#include "nspr.h" -#include "cryptohi.h" -#include "ssl.h" -#include "keyhi.h" -#include "pk11pub.h" -#include "sechash.h" -#include "nsError.h" #include "dtlsidentity.h" +#include "cert.h" +#include "cryptohi.h" +#include "keyhi.h" +#include "nsError.h" +#include "pk11pub.h" +#include "prprf.h" +#include "sechash.h" +#include "ssl.h" + namespace mozilla { DtlsIdentity::~DtlsIdentity() { - // XXX: make cert_ a smart pointer to avoid this, after we figure - // out the linking problem. - if (cert_) + if (cert_) { CERT_DestroyCertificate(cert_); + } } -const std::string DtlsIdentity::DEFAULT_HASH_ALGORITHM = "sha-256"; - -already_AddRefed DtlsIdentity::Generate() { - +RefPtr DtlsIdentity::Generate() { ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); if (!slot) { return nullptr; @@ -53,10 +49,16 @@ already_AddRefed DtlsIdentity::Generate() { return nullptr; } - PK11RSAGenParams rsaparams; - rsaparams.keySizeInBits = 1024; // TODO: make this stronger when we - // pre-generate. - rsaparams.pe = 65537; // We are too paranoid to use 3 as the exponent. + unsigned char paramBuf[12]; // OIDs are small + SECItem ecdsaParams = { siBuffer, paramBuf, sizeof(paramBuf) }; + SECOidData* oidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); + if (!oidData || (oidData->oid.len > (sizeof(paramBuf) - 2))) { + return nullptr; + } + ecdsaParams.data[0] = SEC_ASN1_OBJECT_ID; + ecdsaParams.data[1] = oidData->oid.len; + memcpy(ecdsaParams.data + 2, oidData->oid.data, oidData->oid.len); + ecdsaParams.len = oidData->oid.len + 2; ScopedSECKEYPrivateKey private_key; ScopedSECKEYPublicKey public_key; @@ -64,7 +66,7 @@ already_AddRefed DtlsIdentity::Generate() { private_key = PK11_GenerateKeyPair(slot, - CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaparams, &pubkey, + CKM_EC_KEY_PAIR_GEN, &ecdsaParams, &pubkey, PR_FALSE, PR_TRUE, nullptr); if (private_key == nullptr) return nullptr; @@ -120,7 +122,7 @@ already_AddRefed DtlsIdentity::Generate() { PLArenaPool *arena = certificate->arena; rv = SECOID_SetAlgorithmID(arena, &certificate->signature, - SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, 0); + SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, 0); if (rv != SECSuccess) return nullptr; @@ -144,32 +146,34 @@ already_AddRefed DtlsIdentity::Generate() { rv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len, private_key, - SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION); + SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); if (rv != SECSuccess) { return nullptr; } certificate->derCert = *signedCert; RefPtr identity = - new DtlsIdentity(private_key.forget(), certificate.forget()); + new DtlsIdentity(private_key.forget(), certificate.forget(), ssl_kea_ecdh); return identity.forget(); } +const std::string DtlsIdentity::DEFAULT_HASH_ALGORITHM = "sha-256"; nsresult DtlsIdentity::ComputeFingerprint(const std::string algorithm, - unsigned char *digest, - std::size_t size, - std::size_t *digest_length) { - MOZ_ASSERT(cert_); + uint8_t *digest, + size_t size, + size_t *digest_length) const { + const CERTCertificate* c = cert(); + MOZ_ASSERT(c); - return ComputeFingerprint(cert_, algorithm, digest, size, digest_length); + return ComputeFingerprint(c, algorithm, digest, size, digest_length); } nsresult DtlsIdentity::ComputeFingerprint(const CERTCertificate *cert, const std::string algorithm, - unsigned char *digest, - std::size_t size, - std::size_t *digest_length) { + uint8_t *digest, + size_t size, + size_t *digest_length) { MOZ_ASSERT(cert); HASH_HashType ht; @@ -190,19 +194,22 @@ nsresult DtlsIdentity::ComputeFingerprint(const CERTCertificate *cert, const SECHashObject *ho = HASH_GetHashObject(ht); MOZ_ASSERT(ho); - if (!ho) + if (!ho) { return NS_ERROR_INVALID_ARG; + } MOZ_ASSERT(ho->length >= 20); // Double check - if (size < ho->length) + if (size < ho->length) { return NS_ERROR_INVALID_ARG; + } SECStatus rv = HASH_HashBuf(ho->type, digest, cert->derCert.data, cert->derCert.len); - if (rv != SECSuccess) + if (rv != SECSuccess) { return NS_ERROR_FAILURE; + } *digest_length = ho->length; diff --git a/media/mtransport/dtlsidentity.h b/media/mtransport/dtlsidentity.h index ab2c160302..e29f9afc2d 100644 --- a/media/mtransport/dtlsidentity.h +++ b/media/mtransport/dtlsidentity.h @@ -11,6 +11,7 @@ #include "m_cpp_utils.h" #include "mozilla/RefPtr.h" #include "nsISupportsImpl.h" +#include "sslt.h" #include "ScopedNSSTypes.h" // All code in this module requires NSS to be live. @@ -18,31 +19,38 @@ // protocol. namespace mozilla { -class DtlsIdentity { - private: - ~DtlsIdentity(); - +class DtlsIdentity final { public: - // Generate an identity with a random name. - static already_AddRefed Generate(); + // This constructor takes ownership of privkey and cert. + DtlsIdentity(SECKEYPrivateKey *privkey, + CERTCertificate *cert, + SSLKEAType authType) + : private_key_(privkey), cert_(cert), auth_type_(authType) {} - // Note: the following two functions just provide access. They - // do not transfer ownership. If you want a pointer that lasts - // past the lifetime of the DtlsIdentity, you must make - // a copy yourself. - CERTCertificate *cert() { return cert_; } - SECKEYPrivateKey *privkey() { return privkey_; } + // This is only for use in tests, or for external linkage. It makes a (bad) + // instance of this class. + static RefPtr Generate(); + + // These don't create copies or transfer ownership. If you want these to live + // on, make a copy. + CERTCertificate *cert() const { return cert_; } + SECKEYPrivateKey *privkey() const { return private_key_; } + // Note: this uses SSLKEAType because that is what the libssl API requires. + // This is a giant confusing mess, but libssl indexes certificates based on a + // key exchange type, not authentication type (as you might have reasonably + // expected). + SSLKEAType auth_type() const { return auth_type_; } nsresult ComputeFingerprint(const std::string algorithm, - unsigned char *digest, - std::size_t size, - std::size_t *digest_length); - + uint8_t *digest, + size_t size, + size_t *digest_length) const; static nsresult ComputeFingerprint(const CERTCertificate *cert, const std::string algorithm, - unsigned char *digest, - std::size_t size, - std::size_t *digest_length); + uint8_t *digest, + size_t size, + size_t *digest_length); + static const std::string DEFAULT_HASH_ALGORITHM; enum { HASH_ALGORITHM_MAX_LENGTH = 64 @@ -50,14 +58,14 @@ class DtlsIdentity { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DtlsIdentity) -private: - DtlsIdentity(SECKEYPrivateKey *privkey, CERTCertificate *cert) - : privkey_(privkey), cert_(cert) {} + private: + ~DtlsIdentity(); DISALLOW_COPY_ASSIGN(DtlsIdentity); - ScopedSECKEYPrivateKey privkey_; + ScopedSECKEYPrivateKey private_key_; CERTCertificate *cert_; // TODO: Using a smart pointer here causes link // errors. + SSLKEAType auth_type_; }; } // close namespace #endif diff --git a/media/mtransport/transportlayerdtls.cpp b/media/mtransport/transportlayerdtls.cpp index 5d4f1a342d..68592b7abe 100644 --- a/media/mtransport/transportlayerdtls.cpp +++ b/media/mtransport/transportlayerdtls.cpp @@ -506,7 +506,7 @@ bool TransportLayerDtls::Setup() { // Server side rv = SSL_ConfigSecureServer(ssl_fd, identity_->cert(), identity_->privkey(), - kt_rsa); + identity_->auth_type()); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't set identity"); return false; diff --git a/media/webrtc/moz.build b/media/webrtc/moz.build index 5ecaa5a23d..f6ff3c15be 100644 --- a/media/webrtc/moz.build +++ b/media/webrtc/moz.build @@ -105,4 +105,3 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk': build_for_standalone=1 ) GYP_DIRS['signalingstandalone'].non_unified_sources += signaling_non_unified_sources - diff --git a/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp b/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp index 85e632e01e..5e1b00c2b6 100644 --- a/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp +++ b/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp @@ -167,7 +167,7 @@ MediaPipelineFactory::CreateOrGetTransportFlow( ? TransportLayerDtls::CLIENT : TransportLayerDtls::SERVER); - RefPtr pcid = mPC->GetIdentity(); + RefPtr pcid = mPC->Identity(); if (!pcid) { MOZ_MTLOG(ML_ERROR, "Failed to get DTLS identity."); return NS_ERROR_FAILURE; diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index 113007b96e..fba7a73fb0 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -74,6 +74,7 @@ #include "nsIDOMLocation.h" #include "nsNullPrincipal.h" #include "mozilla/PeerIdentity.h" +#include "mozilla/dom/RTCCertificate.h" #include "mozilla/dom/RTCConfigurationBinding.h" #include "mozilla/dom/RTCStatsReportBinding.h" #include "mozilla/dom/RTCPeerConnectionBinding.h" @@ -372,7 +373,11 @@ PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal) , mIceGatheringState(PCImplIceGatheringState::New) , mDtlsConnected(false) , mWindow(nullptr) +#if !defined(MOZILLA_EXTERNAL_LINKAGE) + , mCertificate(nullptr) +#else , mIdentity(nullptr) +#endif , mPrivacyRequested(false) , mSTSThread(nullptr) , mAllowIceLoopback(false) @@ -421,17 +426,6 @@ PeerConnectionImpl::~PeerConnectionImpl() Close(); -#if !defined(MOZILLA_EXTERNAL_LINKAGE) - { - // Deregister as an NSS Shutdown Object - nsNSSShutDownPreventionLock locker; - if (!isAlreadyShutDown()) { - destructorSafeDestroyNSSReference(); - shutdown(calledFromObject); - } - } -#endif - // Since this and Initialize() occur on MainThread, they can't both be // running at once @@ -756,16 +750,6 @@ PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver, PeerConnectionCtx::GetInstance()->mPeerConnections[mHandle] = this; - STAMP_TIMECARD(mTimeCard, "Generating DTLS Identity"); - // Create the DTLS Identity - mIdentity = DtlsIdentity::Generate(); - STAMP_TIMECARD(mTimeCard, "Done Generating DTLS Identity"); - - if (!mIdentity) { - CSFLogError(logTag, "%s: Generate returned NULL", __FUNCTION__); - return NS_ERROR_FAILURE; - } - mJsepSession = MakeUnique(mName, MakeUnique()); @@ -786,21 +770,74 @@ PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver, return res; } - const std::string& fpAlg = DtlsIdentity::DEFAULT_HASH_ALGORITHM; - std::vector fingerprint; - res = CalculateFingerprint(fpAlg, fingerprint); - NS_ENSURE_SUCCESS(res, res); - res = mJsepSession->AddDtlsFingerprint(fpAlg, fingerprint); - if (NS_FAILED(res)) { - CSFLogError(logTag, "%s: Couldn't set DTLS credentials, res=%u", - __FUNCTION__, - static_cast(res)); - return res; +#if defined(MOZILLA_EXTERNAL_LINKAGE) + { + mIdentity = DtlsIdentity::Generate(); + if (!mIdentity) { + return NS_ERROR_FAILURE; + } + + std::vector fingerprint; + res = CalculateFingerprint(DtlsIdentity::DEFAULT_HASH_ALGORITHM, + &fingerprint); + NS_ENSURE_SUCCESS(res, res); + + res = mJsepSession->AddDtlsFingerprint(DtlsIdentity::DEFAULT_HASH_ALGORITHM, + fingerprint); + NS_ENSURE_SUCCESS(res, res); } +#endif return NS_OK; } +#if !defined(MOZILLA_EXTERNAL_LINKAGE) +void +PeerConnectionImpl::SetCertificate(mozilla::dom::RTCCertificate& aCertificate) +{ + PC_AUTO_ENTER_API_CALL_NO_CHECK(); + MOZ_ASSERT(!mCertificate, "This can only be called once"); + mCertificate = &aCertificate; + + std::vector fingerprint; + nsresult rv = CalculateFingerprint(DtlsIdentity::DEFAULT_HASH_ALGORITHM, + &fingerprint); + if (NS_FAILED(rv)) { + CSFLogError(logTag, "%s: Couldn't calculate fingerprint, rv=%u", + __FUNCTION__, static_cast(rv)); + mCertificate = nullptr; + return; + } + rv = mJsepSession->AddDtlsFingerprint(DtlsIdentity::DEFAULT_HASH_ALGORITHM, + fingerprint); + if (NS_FAILED(rv)) { + CSFLogError(logTag, "%s: Couldn't set DTLS credentials, rv=%u", + __FUNCTION__, static_cast(rv)); + mCertificate = nullptr; + } +} + +const nsRefPtr& +PeerConnectionImpl::Certificate() const +{ + PC_AUTO_ENTER_API_CALL_NO_CHECK(); + return mCertificate; +} +#endif + +mozilla::RefPtr +PeerConnectionImpl::Identity() const +{ + PC_AUTO_ENTER_API_CALL_NO_CHECK(); +#if !defined(MOZILLA_EXTERNAL_LINKAGE) + MOZ_ASSERT(mCertificate); + return mCertificate->CreateDtlsIdentity(); +#else + mozilla::RefPtr id = mIdentity; + return id; +#endif +} + class CompareCodecPriority { public: void SetPreferredCodec(int32_t preferredCodec) { @@ -989,13 +1026,6 @@ PeerConnectionImpl::ConfigureJsepSessionCodecs() { return NS_OK; } -RefPtr const -PeerConnectionImpl::GetIdentity() const -{ - PC_AUTO_ENTER_API_CALL_NO_CHECK(); - return mIdentity; -} - // Data channels won't work without a window, so in order for the C++ unit // tests to work (it doesn't have a window available) we ifdef the following // two implementations. @@ -2164,18 +2194,27 @@ PeerConnectionImpl::ReplaceTrack(MediaStreamTrack& aThisTrack, nsresult PeerConnectionImpl::CalculateFingerprint( const std::string& algorithm, - std::vector& fingerprint) const { + std::vector* fingerprint) const { uint8_t buf[DtlsIdentity::HASH_ALGORITHM_MAX_LENGTH]; size_t len = 0; - nsresult rv = mIdentity->ComputeFingerprint(algorithm, &buf[0], sizeof(buf), - &len); + CERTCertificate* cert; + + MOZ_ASSERT(fingerprint); +#if !defined(MOZILLA_EXTERNAL_LINKAGE) + cert = mCertificate->Certificate(); +#else + cert = mIdentity->cert(); +#endif + nsresult rv = DtlsIdentity::ComputeFingerprint(cert, algorithm, + &buf[0], sizeof(buf), + &len); if (NS_FAILED(rv)) { CSFLogError(logTag, "Unable to calculate certificate fingerprint, rv=%u", static_cast(rv)); return rv; } MOZ_ASSERT(len > 0 && len <= DtlsIdentity::HASH_ALGORITHM_MAX_LENGTH); - fingerprint.assign(buf, buf + len); + fingerprint->assign(buf, buf + len); return NS_OK; } @@ -2183,9 +2222,11 @@ NS_IMETHODIMP PeerConnectionImpl::GetFingerprint(char** fingerprint) { MOZ_ASSERT(fingerprint); - MOZ_ASSERT(mIdentity); +#if !defined(MOZILLA_EXTERNAL_LINKAGE) + MOZ_ASSERT(mCertificate); +#endif std::vector fp; - nsresult rv = CalculateFingerprint(DtlsIdentity::DEFAULT_HASH_ALGORITHM, fp); + nsresult rv = CalculateFingerprint(DtlsIdentity::DEFAULT_HASH_ALGORITHM, &fp); NS_ENSURE_SUCCESS(rv, rv); std::ostringstream os; os << DtlsIdentity::DEFAULT_HASH_ALGORITHM << ' ' @@ -2385,25 +2426,6 @@ PeerConnectionImpl::ShutdownMedia() mMedia.forget().take()->SelfDestruct(); } -#if !defined(MOZILLA_EXTERNAL_LINKAGE) -// If NSS is shutting down, then we need to get rid of the DTLS -// identity right now; otherwise, we'll cause wreckage when we do -// finally deallocate it in our destructor. -void -PeerConnectionImpl::virtualDestroyNSSReference() -{ - destructorSafeDestroyNSSReference(); -} - -void -PeerConnectionImpl::destructorSafeDestroyNSSReference() -{ - MOZ_ASSERT(NS_IsMainThread()); - CSFLogDebug(logTag, "%s: NSS shutting down; freeing our DtlsIdentity.", __FUNCTION__); - mIdentity = nullptr; -} -#endif - void PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState) { diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h index d3611abbe0..e285a1197b 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -38,7 +38,6 @@ #include "mozilla/net/DataChannel.h" #include "VideoUtils.h" #include "VideoSegment.h" -#include "nsNSSShutDown.h" #include "mozilla/dom/RTCStatsReportBinding.h" #include "nsIPrincipal.h" #include "mozilla/PeerIdentity.h" @@ -78,6 +77,7 @@ class DOMMediaStream; #endif namespace dom { +class RTCCertificate; struct RTCConfiguration; struct RTCIceServer; struct RTCOfferOptions; @@ -230,7 +230,6 @@ class RTCStatsQuery { class PeerConnectionImpl final : public nsISupports, #if !defined(MOZILLA_EXTERNAL_LINKAGE) public mozilla::DataChannelConnection::DataConnectionListener, - public nsNSSShutDownObject, public DOMMediaStream::PrincipalChangeObserver, #endif public sigslot::has_slots<> @@ -319,9 +318,6 @@ public: return mSTSThread; } - // Get the DTLS identity (local side) - mozilla::RefPtr const GetIdentity() const; - nsPIDOMWindow* GetWindow() const { PC_AUTO_ENTER_API_CALL_NO_CHECK(); return mWindow; @@ -348,6 +344,13 @@ public: } } +#if !defined(MOZILLA_EXTERNAL_LINKAGE) + void SetCertificate(mozilla::dom::RTCCertificate& aCertificate); + const nsRefPtr& Certificate() const; +#endif + // This is a hack to support external linkage. + mozilla::RefPtr Identity() const; + NS_IMETHODIMP_TO_ERRORRESULT(CreateOffer, ErrorResult &rv, const RTCOfferOptions& aOptions) { @@ -614,7 +617,7 @@ private: const RTCConfiguration* aRTCConfiguration, nsISupports* aThread); nsresult CalculateFingerprint(const std::string& algorithm, - std::vector& fingerprint) const; + std::vector* fingerprint) const; nsresult ConfigureJsepSessionCodecs(); NS_IMETHODIMP EnsureDataConnection(uint16_t aNumstreams); @@ -639,8 +642,6 @@ private: } #if !defined(MOZILLA_EXTERNAL_LINKAGE) - void virtualDestroyNSSReference() final; - void destructorSafeDestroyNSSReference(); nsresult GetTimeSinceEpoch(DOMHighResTimeStamp *result); #endif @@ -707,11 +708,14 @@ private: std::string mRemoteFingerprint; // identity-related fields - mozilla::RefPtr mIdentity; #if !defined(MOZILLA_EXTERNAL_LINKAGE) // The entity on the other end of the peer-to-peer connection; // void if they are not yet identified, and no identity setting has been set nsAutoPtr mPeerIdentity; + // The certificate we are using. + nsRefPtr mCertificate; +#else + mozilla::RefPtr mIdentity; #endif // Whether an app should be prevented from accessing media produced by the PC // If this is true, then media will not be sent until mPeerIdentity matches diff --git a/storage/mozStorageAsyncStatementJSHelper.cpp b/storage/mozStorageAsyncStatementJSHelper.cpp index f8efa16283..1eac10796d 100644 --- a/storage/mozStorageAsyncStatementJSHelper.cpp +++ b/storage/mozStorageAsyncStatementJSHelper.cpp @@ -66,7 +66,7 @@ AsyncStatementJSHelper::getParams(AsyncStatement *aStatement, obj = aStatement->mStatementParamsHolder->GetJSObject(); NS_ENSURE_STATE(obj); - *_params = OBJECT_TO_JSVAL(obj); + _params->setObject(*obj); return NS_OK; } diff --git a/storage/mozStorageAsyncStatementParams.cpp b/storage/mozStorageAsyncStatementParams.cpp index e28e5a7ecd..b41cf296f0 100644 --- a/storage/mozStorageAsyncStatementParams.cpp +++ b/storage/mozStorageAsyncStatementParams.cpp @@ -106,7 +106,8 @@ AsyncStatementParams::Resolve(nsIXPConnectWrappedNative *aWrapper, uint32_t idx = JSID_TO_INT(aId); // All indexes are good because we don't know how many parameters there // really are. - ok = ::JS_DefineElement(aCtx, scopeObj, idx, JS::UndefinedHandleValue, 0); + ok = ::JS_DefineElement(aCtx, scopeObj, idx, JS::UndefinedHandleValue, + JSPROP_RESOLVING); resolved = true; } else if (JSID_IS_STRING(aId)) { @@ -114,7 +115,8 @@ AsyncStatementParams::Resolve(nsIXPConnectWrappedNative *aWrapper, // we must assume that there is. This screws the rest of the prototype // chain, but people really shouldn't be depending on this anyways. JS::Rooted id(aCtx, aId); - ok = ::JS_DefinePropertyById(aCtx, scopeObj, id, JS::UndefinedHandleValue, 0); + ok = ::JS_DefinePropertyById(aCtx, scopeObj, id, JS::UndefinedHandleValue, + JSPROP_RESOLVING); resolved = true; } diff --git a/storage/mozStorageStatementJSHelper.cpp b/storage/mozStorageStatementJSHelper.cpp index 9d5c8b2c30..7799a0e34d 100644 --- a/storage/mozStorageStatementJSHelper.cpp +++ b/storage/mozStorageStatementJSHelper.cpp @@ -71,7 +71,7 @@ stepFunc(JSContext *aCtx, return false; } - *_vp = BOOLEAN_TO_JSVAL(hasMore); + _vp->setBoolean(hasMore); return true; } @@ -118,7 +118,7 @@ StatementJSHelper::getRow(Statement *aStatement, obj = aStatement->mStatementRowHolder->GetJSObject(); NS_ENSURE_STATE(obj); - *_row = OBJECT_TO_JSVAL(obj); + _row->setObject(*obj); return NS_OK; } @@ -164,7 +164,7 @@ StatementJSHelper::getParams(Statement *aStatement, obj = aStatement->mStatementParamsHolder->GetJSObject(); NS_ENSURE_STATE(obj); - *_params = OBJECT_TO_JSVAL(obj); + _params->setObject(*obj); return NS_OK; } @@ -234,7 +234,7 @@ StatementJSHelper::Resolve(nsIXPConnectWrappedNative *aWrapper, JS::RootedObject scope(aCtx, aScopeObj); if (::JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(aId), "step")) { *_retval = ::JS_DefineFunction(aCtx, scope, "step", stepFunc, - 0, 0) != nullptr; + 0, JSPROP_RESOLVING) != nullptr; *aResolvedp = true; return NS_OK; } diff --git a/storage/mozStorageStatementParams.cpp b/storage/mozStorageStatementParams.cpp index 7d01167b55..ede4e731ba 100644 --- a/storage/mozStorageStatementParams.cpp +++ b/storage/mozStorageStatementParams.cpp @@ -149,7 +149,8 @@ StatementParams::Resolve(nsIXPConnectWrappedNative *aWrapper, if (idx >= mParamCount) return NS_ERROR_INVALID_ARG; - ok = ::JS_DefineElement(aCtx, scope, idx, JS::UndefinedHandleValue, JSPROP_ENUMERATE); + ok = ::JS_DefineElement(aCtx, scope, idx, JS::UndefinedHandleValue, + JSPROP_ENUMERATE | JSPROP_RESOLVING); resolved = true; } else if (JSID_IS_STRING(id)) { @@ -165,7 +166,8 @@ StatementParams::Resolve(nsIXPConnectWrappedNative *aWrapper, uint32_t idx; nsresult rv = mStatement->GetParameterIndex(name, &idx); if (NS_SUCCEEDED(rv)) { - ok = ::JS_DefinePropertyById(aCtx, scope, id, JS::UndefinedHandleValue, JSPROP_ENUMERATE); + ok = ::JS_DefinePropertyById(aCtx, scope, id, JS::UndefinedHandleValue, + JSPROP_ENUMERATE | JSPROP_RESOLVING); resolved = true; } } diff --git a/storage/mozStorageStatementRow.cpp b/storage/mozStorageStatementRow.cpp index 24cb503763..18f9a21765 100644 --- a/storage/mozStorageStatementRow.cpp +++ b/storage/mozStorageStatementRow.cpp @@ -80,7 +80,7 @@ StatementRow::GetProperty(nsIXPConnectWrappedNative *aWrapper, *_retval = false; return NS_OK; } - *_vp = STRING_TO_JSVAL(str); + _vp->setString(str); } else if (type == mozIStorageValueArray::VALUE_TYPE_BLOB) { uint32_t length; @@ -91,7 +91,7 @@ StatementRow::GetProperty(nsIXPConnectWrappedNative *aWrapper, *_retval = false; return NS_OK; } - *_vp = OBJECT_TO_JSVAL(obj); + _vp->setObject(*obj); // Copy the blob over to the JS array. for (uint32_t i = 0; i < length; i++) { @@ -142,7 +142,8 @@ StatementRow::Resolve(nsIXPConnectWrappedNative *aWrapper, } JS::Rooted id(aCtx, aId); - *_retval = ::JS_DefinePropertyById(aCtx, scopeObj, id, JS::UndefinedHandleValue, 0); + *_retval = ::JS_DefinePropertyById(aCtx, scopeObj, id, JS::UndefinedHandleValue, + JSPROP_RESOLVING); *aResolvedp = true; return NS_OK; } diff --git a/toolkit/components/addoncompat/addoncompat.manifest b/toolkit/components/addoncompat/addoncompat.manifest index 4273bf246c..4228c8a1a0 100644 --- a/toolkit/components/addoncompat/addoncompat.manifest +++ b/toolkit/components/addoncompat/addoncompat.manifest @@ -1,4 +1,6 @@ component {1363d5f0-d95e-11e3-9c1a-0800200c9a66} multiprocessShims.js contract @mozilla.org/addons/multiprocess-shims;1 {1363d5f0-d95e-11e3-9c1a-0800200c9a66} +component {50bc93ce-602a-4bef-bf3a-61fc749c4caf} defaultShims.js +contract @mozilla.org/addons/default-addon-shims;1 {50bc93ce-602a-4bef-bf3a-61fc749c4caf} component {dfd07380-6083-11e4-9803-0800200c9a66} remoteTagService.js contract @mozilla.org/addons/remote-tag-service;1 {dfd07380-6083-11e4-9803-0800200c9a66} diff --git a/toolkit/components/addoncompat/defaultShims.js b/toolkit/components/addoncompat/defaultShims.js new file mode 100644 index 0000000000..107d6ee8fd --- /dev/null +++ b/toolkit/components/addoncompat/defaultShims.js @@ -0,0 +1,35 @@ +/* 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/. */ + +"use strict"; + +const Cu = Components.utils; +const Ci = Components.interfaces; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +/** + * Using multiprocessShims is optional, and if an add-on is e10s compatible it should not + * use it. But in some cases we still want to use the interposition service for various + * features so we have a default shim service. + */ + +function DefaultInterpositionService() { +} + +DefaultInterpositionService.prototype = { + classID: Components.ID("{50bc93ce-602a-4bef-bf3a-61fc749c4caf}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonInterposition, Ci.nsISupportsWeakReference]), + + interposeProperty: function(addon, target, iid, prop) { + return null; + }, + + interposeCall: function(addonId, originalFunc, originalThis, args) { + args.splice(0, 0, addonId); + return originalFunc.apply(originalThis, args); + }, +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DefaultInterpositionService]); diff --git a/toolkit/components/addoncompat/multiprocessShims.js b/toolkit/components/addoncompat/multiprocessShims.js index 96e02d0c71..0da79015cc 100644 --- a/toolkit/components/addoncompat/multiprocessShims.js +++ b/toolkit/components/addoncompat/multiprocessShims.js @@ -105,7 +105,7 @@ AddonInterpositionService.prototype = { return "generic"; }, - interpose: function(addon, target, iid, prop) { + interposeProperty: function(addon, target, iid, prop) { let interp; if (iid) { interp = this._interfaceInterpositions[iid]; @@ -143,6 +143,11 @@ AddonInterpositionService.prototype = { return Prefetcher.lookupInCache(addon, target, prop); }, + + interposeCall: function(addonId, originalFunc, originalThis, args) { + args.splice(0, 0, addonId); + return originalFunc.apply(originalThis, args); + }, }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AddonInterpositionService]); diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index f06a490bc3..a9c7ff0f6b 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -116,7 +116,7 @@ public: static void TraceWeakMappingChild(JS::CallbackTracer* aTrc, void** aThingp, - JSGCTraceKind aKind); + JS::TraceKind aKind); struct NoteWeakMapChildrenTracer : public JS::CallbackTracer { @@ -136,7 +136,7 @@ struct NoteWeakMapChildrenTracer : public JS::CallbackTracer static void TraceWeakMappingChild(JS::CallbackTracer* aTrc, void** aThingp, - JSGCTraceKind aKind) + JS::TraceKind aKind) { MOZ_ASSERT(aTrc->hasCallback(TraceWeakMappingChild)); NoteWeakMapChildrenTracer* tracer = @@ -255,7 +255,7 @@ private: // If nothing that could be held alive by this entry is marked gray, return. bool delegateMightNeedMarking = aKey && JS::GCThingIsMarkedGray(aKey); bool valueMightNeedMarking = aValue && JS::GCThingIsMarkedGray(aValue) && - aValue.kind() != JSTRACE_STRING; + aValue.kind() != JS::TraceKind::String; if (!delegateMightNeedMarking && !valueMightNeedMarking) { return; } @@ -276,7 +276,7 @@ private: if (aValue && JS::GCThingIsMarkedGray(aValue) && (!aKey || !JS::GCThingIsMarkedGray(aKey)) && (!aMap || !JS::ObjectIsMarkedGray(aMap)) && - aValue.kind() != JSTRACE_SHAPE) { + aValue.kind() != JS::TraceKind::Shape) { if (JS::UnmarkGrayGCThingRecursively(aValue)) { tracer->mAnyMarked = true; } @@ -368,7 +368,7 @@ JSZoneParticipant::Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb) static void NoteJSChildTracerShim(JS::CallbackTracer* aTrc, void** aThingp, - JSGCTraceKind aTraceKind); + JS::TraceKind aTraceKind); struct TraversalTracer : public JS::CallbackTracer { @@ -424,7 +424,7 @@ NoteJSChild(JS::CallbackTracer* aTrc, JS::GCCellPtr aThing) static void NoteJSChildTracerShim(JS::CallbackTracer* aTrc, void** aThingp, - JSGCTraceKind aTraceKind) + JS::TraceKind aTraceKind) { JS::GCCellPtr thing(*aThingp, aTraceKind); NoteJSChild(aTrc, thing); diff --git a/xpcom/base/CycleCollectedJSRuntime.h b/xpcom/base/CycleCollectedJSRuntime.h index 6d4ab20ce6..a07b3522dd 100644 --- a/xpcom/base/CycleCollectedJSRuntime.h +++ b/xpcom/base/CycleCollectedJSRuntime.h @@ -330,10 +330,10 @@ private: void TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer); -// Returns true if the JSGCTraceKind is one the cycle collector cares about. -inline bool AddToCCKind(JSGCTraceKind aKind) +// Returns true if the JS::TraceKind is one the cycle collector cares about. +inline bool AddToCCKind(JS::TraceKind aKind) { - return aKind == JSTRACE_OBJECT || aKind == JSTRACE_SCRIPT; + return aKind == JS::TraceKind::Object || aKind == JS::TraceKind::Script; } } // namespace mozilla diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index 2f5bc872e9..ff56ffa8b7 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -2016,7 +2016,7 @@ GCThingIsGrayCCThing(JS::GCCellPtr thing) static bool ValueIsGrayCCThing(const JS::Value& value) { - return AddToCCKind(value.gcKind()) && + return AddToCCKind(value.traceKind()) && JS::GCThingIsMarkedGray(value.toGCCellPtr()); } diff --git a/xpfe/components/directory/nsDirectoryViewer.cpp b/xpfe/components/directory/nsDirectoryViewer.cpp index 78d90f26c4..6a69fd61cb 100644 --- a/xpfe/components/directory/nsDirectoryViewer.cpp +++ b/xpfe/components/directory/nsDirectoryViewer.cpp @@ -254,7 +254,7 @@ nsHTTPIndex::OnStartRequest(nsIRequest *request, nsISupports* aContext) "unable to get jsobj from xpconnect wrapper"); if (!jsobj) return NS_ERROR_UNEXPECTED; - JS::Rooted jslistener(cx, OBJECT_TO_JSVAL(jsobj)); + JS::Rooted jslistener(cx, JS::ObjectValue(*jsobj)); // ...and stuff it into the global context bool ok = JS_SetProperty(cx, global, "HTTPIndex", jslistener);