From c975928fffefc67846d5440cf4c9dbc9ccd78056 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Thu, 3 Aug 2023 15:16:47 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1235923 - Part 1: Add C++ APIs for unbarriered pointer tracing; r=sfink (dc22d4c486) - Bug 1235923 - Part 2: Use new Root tracing APIs in Gecko; r=smaug (f455edf9b2) - Bug 1225650 - Use stable hashing for JSObject2WrappedJSMap; r=jonco (bdefc44e0e) - Bug 1238786 - Part 1: Allow null pointers in public tracing APIs; r=sfink (0ad99d3854) - Bug 1238786 - Part 2: We no longer need to null check before using trace APIs; r=smaug (c1caf8f003) - Bug 1240264 - Annotate intentional switch fallthroughs in dom/media/. r=cpearce (85bf054d64) - Bug 1230692. Fix WebAudioDecodeJob to properly suppress exceptions from its callbacks. r=ehsan (f7ae8b0502) - Bug 1237557 - Suppress the exception if calling the callback fails when finishing a decodeAudioData operation, because there is not much we can do. r=ehsan (73775a3145) - Bug 1238779 - Remove the unused and nonsensical JS_TraceRuntime; r=fitzgen (e2d98419f9) - Bug 1234862 - Part 1: Rename GCMethods to BarrierMethods; r=sfink (1dab0dee58) - Bug 1233117 - Fix handling of eval in modules r=jandem (be635033a6) - Bug 1233109 - Alias fewer bindings at module toplevel r=shu (8d5fb08136) - Bug 1000780 - Part 1: Bake in already-cloned intrinsic functions even if the callsite doesn't have type information. r=jandem (ee6a2134e5) - Bug 1000780 - Part 2: Emit JSOP_FUNAPPLY when using std_Function_apply in self-hosted code. r=jandem (a71e470a12) - Bug 1000780 - Part 3: Free up JSFunction flag. r=jwalden+bmo (73db74b60a) - Bug 1000780 - Part 4: Remove Function#bind usage from async stack tests. r=fitzgen (707102b764) - Bug 1216150 - Implement ECMA 402 DateTimeFormat formatToParts (8b1b2974e5) - Bug 1234702 - Part 1: Allow opt-in calls to content invoking spread opts in self-hosted code. (r=till) (90e847bcad) - Bug 1234702 - Part 2: Fix up class constructor scripts to allow cloning. (r=Waldo) (08fc55eccf) - Bug 1234702 - Part 3: Self-host default derived class constructor. (r=till) (0702fe0790) - Bug 1234702 - Part 4: Self-host default base class constructor. (r=till) (c499d25dd7) - Bug 1235408: Lazily resolve SIMD types; r=jandem (59d116461c) - Bug 1000780 - Part 5: Self-host Function.prototype.bind. r=jandem (ee118512ad) - Bug 1000780 - Part 6: Fix nit in jsfun.h. r=jandem (61cb77f34c) - Bug 1234845 part 1 - Remove fun() method from frames and remove some dead code. r=luke (6c474eb5b0) - Bug 1234845 part 2 - Remove some dead code from InterpreterFrame. r=jonco (c4fe3cc77b) - Bug 1234845 part 3 - Remove some fields and unions from InterpreterFrame. r=luke (7efd5a7348) - Bug 1234845 part 4 - Eval frame refactoring, remove isFunctionFrame. r=luke (141df1a467) - Bug 1234845 part 5 - Rename isNonEvalFunctionFrame to isFunctionFrame and use the script instead of flags. r=luke (c3fb98a60c) - Bug 1234845 part 6 - Simplify isEvalFrame, make it use script->isForEval() instead of flags. r=luke (200d188811) - Bug 1234845 part 7 - Simplify isGlobalFrame and isModuleFrame. r=jonco (dcf7ce2d63) - Bug 1234845 part 8 - Remove JitProfilingFrameIterator special case for eval that's no longer needed. r=shu (57e416d498) - Bug 1234845 part 9 - Remove callee slot from non-function interpreter frames. r=luke (dfbf5309a1) - Bug 1234845 part 10 - Remove ExecuteType and InitialFrameFlags enums. r=luke (3c08ae26aa) - Bug 1234845 part 11 - Remove HAS_SCOPECHAIN InterpreterFrame flag, repack flags. r=luke (74e66ac19c) --- dom/base/nsWrapperCache.h | 2 +- dom/bindings/BindingUtils.h | 8 +- dom/bindings/Codegen.py | 6 +- dom/bindings/TypedArray.h | 8 +- dom/media/MediaManager.cpp | 3 +- dom/media/VideoUtils.cpp | 2 +- dom/media/webaudio/AudioEventTimeline.h | 4 +- dom/media/webaudio/MediaBufferDecoder.cpp | 13 +- dom/media/webm/WebMBufferedParser.cpp | 2 +- dom/plugins/base/nsJSNPRuntime.cpp | 10 +- dom/xbl/nsXBLMaybeCompiled.h | 22 +- dom/xul/nsXULElement.h | 2 +- js/public/Class.h | 4 +- js/public/Debug.h | 2 +- js/public/Id.h | 7 +- js/public/RootingAPI.h | 34 +- js/public/TracingAPI.h | 61 ++- js/public/Value.h | 7 +- js/src/asmjs/AsmJS.cpp | 16 +- js/src/builtin/Classes.js | 17 + js/src/builtin/Date.js | 6 +- js/src/builtin/Eval.cpp | 14 +- js/src/builtin/Function.js | 299 ++++++++++++++ js/src/builtin/Intl.cpp | 304 ++++++++++++++- js/src/builtin/Intl.js | 38 +- js/src/builtin/ModuleObject.cpp | 25 +- js/src/builtin/ModuleObject.h | 11 +- js/src/builtin/Object.cpp | 9 +- js/src/builtin/SIMD.cpp | 368 +++++++----------- js/src/builtin/SIMD.h | 1 + js/src/builtin/SelfHostingDefines.h | 8 + js/src/builtin/TypedObject.cpp | 26 +- js/src/ctypes/CTypes.cpp | 2 +- js/src/frontend/BytecodeEmitter.cpp | 53 ++- js/src/frontend/BytecodeEmitter.h | 7 +- js/src/frontend/Parser.cpp | 40 +- js/src/frontend/Parser.h | 5 - js/src/gc/Barrier.cpp | 8 +- js/src/gc/Barrier.h | 66 ++-- js/src/gc/Marking.cpp | 39 +- js/src/gc/Tracer.cpp | 42 -- js/src/jit-test/modules/defaultClass.js | 5 + js/src/jit-test/modules/defaultFunction.js | 3 + .../tests/heap-analysis/byteSize-of-object.js | 2 +- js/src/jit-test/tests/modules/bug-1233117.js | 2 + js/src/jit-test/tests/modules/bug-1233915.js | 10 + .../tests/modules/debugger-vars-function.js | 37 ++ .../tests/modules/debugger-vars-toplevel.js | 38 ++ .../tests/modules/export-declaration.js | 56 ++- .../tests/modules/import-default-class.js | 5 + .../tests/modules/import-default-function.js | 4 + .../tests/modules/import-namespace.js | 13 +- .../saved-stacks/async-max-frame-count.js | 2 +- .../tests/saved-stacks/async-principals.js | 5 +- js/src/jit/Bailouts.cpp | 5 +- js/src/jit/BaselineBailouts.cpp | 2 +- js/src/jit/BaselineCompiler.cpp | 8 +- js/src/jit/BaselineFrame-inl.h | 2 +- js/src/jit/BaselineFrame.cpp | 18 +- js/src/jit/BaselineFrame.h | 58 +-- js/src/jit/BaselineFrameInfo.h | 3 - js/src/jit/BaselineIC.cpp | 7 +- js/src/jit/BaselineJIT.cpp | 16 +- js/src/jit/CodeGenerator.cpp | 2 +- js/src/jit/InlinableNatives.h | 2 - js/src/jit/Ion.cpp | 28 +- js/src/jit/IonBuilder.cpp | 17 +- js/src/jit/JitFrames.cpp | 10 +- js/src/jit/MCallOptimize.cpp | 71 ---- js/src/jit/RematerializedFrame.cpp | 4 +- js/src/jit/RematerializedFrame.h | 21 +- js/src/jit/SharedIC.cpp | 2 +- js/src/jit/VMFunctions.cpp | 6 +- js/src/jsapi.cpp | 1 + js/src/jsapi.h | 22 +- js/src/jscntxtinlines.h | 1 - js/src/jsfriendapi.cpp | 7 +- js/src/jsfun.cpp | 329 ++++------------ js/src/jsfun.h | 56 ++- js/src/jsobj.cpp | 4 +- js/src/jsobjinlines.h | 2 +- js/src/jsopcode.cpp | 18 +- js/src/jsscript.cpp | 25 +- js/src/jsscript.h | 31 +- js/src/jsstr.cpp | 2 +- js/src/moz.build | 2 + js/src/shell/js.cpp | 7 +- .../Intl/DateTimeFormat/formatToParts.js | 176 +++++++++ .../Class/defaultConstructorDerivedSpread.js | 19 + .../keyword-unescaped-requirement-modules.js | 12 +- js/src/vm/ArgumentsObject.cpp | 2 +- js/src/vm/CommonPropertyNames.h | 42 +- js/src/vm/Debugger.cpp | 9 +- js/src/vm/GeneratorObject.cpp | 2 +- js/src/vm/GlobalObject.cpp | 8 +- js/src/vm/GlobalObject.h | 31 +- js/src/vm/Interpreter.cpp | 116 ++---- js/src/vm/Interpreter.h | 24 +- js/src/vm/Runtime.h | 2 + js/src/vm/SavedStacks.cpp | 2 +- js/src/vm/ScopeObject.cpp | 8 +- js/src/vm/SelfHosting.cpp | 128 +++++- js/src/vm/Shape.h | 2 +- js/src/vm/Stack-inl.h | 117 ++---- js/src/vm/Stack.cpp | 184 +++------ js/src/vm/Stack.h | 287 ++++---------- js/src/vm/TaggedProto.cpp | 11 +- js/src/vm/TaggedProto.h | 6 +- js/xpconnect/src/XPCMaps.cpp | 5 +- js/xpconnect/src/XPCMaps.h | 18 +- js/xpconnect/src/xpcprivate.h | 5 +- xpcom/base/CycleCollectedJSRuntime.cpp | 4 +- 112 files changed, 2154 insertions(+), 1638 deletions(-) create mode 100644 js/src/builtin/Classes.js create mode 100644 js/src/builtin/Function.js create mode 100644 js/src/jit-test/modules/defaultClass.js create mode 100644 js/src/jit-test/modules/defaultFunction.js create mode 100644 js/src/jit-test/tests/modules/bug-1233117.js create mode 100644 js/src/jit-test/tests/modules/bug-1233915.js create mode 100644 js/src/jit-test/tests/modules/debugger-vars-function.js create mode 100644 js/src/jit-test/tests/modules/debugger-vars-toplevel.js create mode 100644 js/src/jit-test/tests/modules/import-default-class.js create mode 100644 js/src/jit-test/tests/modules/import-default-function.js create mode 100644 js/src/tests/Intl/DateTimeFormat/formatToParts.js create mode 100644 js/src/tests/ecma_6/Class/defaultConstructorDerivedSpread.js diff --git a/dom/base/nsWrapperCache.h b/dom/base/nsWrapperCache.h index 0c8daaa35b..c837e1e534 100644 --- a/dom/base/nsWrapperCache.h +++ b/dom/base/nsWrapperCache.h @@ -258,7 +258,7 @@ protected: void TraceWrapper(JSTracer* aTrc, const char* name) { if (mWrapper) { - JS_CallUnbarrieredObjectTracer(aTrc, &mWrapper, name); + js::UnsafeTraceManuallyBarrieredEdge(aTrc, &mWrapper, name); } } diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 84364a733d..f2fd773878 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -334,7 +334,7 @@ class ProtoAndIfaceCache void Trace(JSTracer* aTracer) { for (size_t i = 0; i < ArrayLength(*this); ++i) { - JS::TraceNullableEdge(aTracer, &(*this)[i], "protoAndIfaceCache[i]"); + JS::TraceEdge(aTracer, &(*this)[i], "protoAndIfaceCache[i]"); } } @@ -393,7 +393,7 @@ class ProtoAndIfaceCache Page* p = mPages[i]; if (p) { for (size_t j = 0; j < ArrayLength(*p); ++j) { - JS::TraceNullableEdge(trc, &(*p)[j], "protoAndIfaceCache[i]"); + JS::TraceEdge(trc, &(*p)[j], "protoAndIfaceCache[i]"); } } } @@ -2118,7 +2118,7 @@ class SequenceTracer public: static void TraceSequence(JSTracer* trc, JSObject** objp, JSObject** end) { for (; objp != end; ++objp) { - JS_CallUnbarrieredObjectTracer(trc, objp, "sequence"); + JS::UnsafeTraceRoot(trc, objp, "sequence"); } } }; @@ -2132,7 +2132,7 @@ class SequenceTracer public: static void TraceSequence(JSTracer* trc, JS::Value* valp, JS::Value* end) { for (; valp != end; ++valp) { - JS_CallUnbarrieredValueTracer(trc, valp, "sequence"); + JS::UnsafeTraceRoot(trc, valp, "sequence"); } } }; diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 420bbefefc..0ce3d60424 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -9620,7 +9620,7 @@ class CGUnionStruct(CGThing): if t.isObject(): traceCases.append( CGCase("e" + vars["name"], - CGGeneric('JS_CallUnbarrieredObjectTracer(trc, %s, "%s");\n' % + CGGeneric('JS::UnsafeTraceRoot(trc, %s, "%s");\n' % ("&mValue.m" + vars["name"] + ".Value()", "mValue.m" + vars["name"])))) elif t.isDictionary(): @@ -12620,12 +12620,12 @@ class CGDictionary(CGThing): memberLoc) if type.isObject(): - trace = CGGeneric('JS_CallUnbarrieredObjectTracer(trc, %s, "%s");\n' % + trace = CGGeneric('JS::UnsafeTraceRoot(trc, %s, "%s");\n' % ("&"+memberData, memberName)) if type.nullable(): trace = CGIfWrapper(trace, memberData) elif type.isAny(): - trace = CGGeneric('JS_CallUnbarrieredValueTracer(trc, %s, "%s");\n' % + trace = CGGeneric('JS::UnsafeTraceRoot(trc, %s, "%s");\n' % ("&"+memberData, memberName)) elif (type.isSequence() or type.isDictionary() or type.isSpiderMonkeyInterface() or type.isUnion()): diff --git a/dom/bindings/TypedArray.h b/dom/bindings/TypedArray.h index ef0c074a7d..8305dff476 100644 --- a/dom/bindings/TypedArray.h +++ b/dom/bindings/TypedArray.h @@ -44,12 +44,8 @@ protected: public: inline void TraceSelf(JSTracer* trc) { - if (mTypedObj) { - JS_CallUnbarrieredObjectTracer(trc, &mTypedObj, "TypedArray.mTypedObj"); - } - if (mWrappedObj) { - JS_CallUnbarrieredObjectTracer(trc, &mWrappedObj, "TypedArray.mWrappedObj"); - } + JS::UnsafeTraceRoot(trc, &mTypedObj, "TypedArray.mTypedObj"); + JS::UnsafeTraceRoot(trc, &mTypedObj, "TypedArray.mWrappedObj"); } private: diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index 504e7d8cfe..ef29a87975 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -1855,8 +1855,7 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, nsPIDOMWindow *outer = aWindow->GetOuterWindow(); vc.mBrowserWindow.Construct(outer->WindowID()); } - // | Fall through - // V + MOZ_FALLTHROUGH; case dom::MediaSourceEnum::Screen: case dom::MediaSourceEnum::Application: case dom::MediaSourceEnum::Window: diff --git a/dom/media/VideoUtils.cpp b/dom/media/VideoUtils.cpp index 77c129da7d..bf27d38cf2 100644 --- a/dom/media/VideoUtils.cpp +++ b/dom/media/VideoUtils.cpp @@ -252,7 +252,7 @@ already_AddRefed GetMediaThreadPool(MediaThreadType aType) name = "MediaPDecoder"; break; default: - MOZ_ASSERT(false); + MOZ_FALLTHROUGH_ASSERT("Unexpected MediaThreadType"); case MediaThreadType::PLAYBACK: name = "MediaPlayback"; break; diff --git a/dom/media/webaudio/AudioEventTimeline.h b/dom/media/webaudio/AudioEventTimeline.h index 121637471a..2ca9310d01 100644 --- a/dom/media/webaudio/AudioEventTimeline.h +++ b/dom/media/webaudio/AudioEventTimeline.h @@ -558,7 +558,7 @@ public: aPrevious->mCurve, aPrevious->mCurveLength, aPrevious->mDuration, aTime); case AudioTimelineEvent::SetTarget: - MOZ_ASSERT(false, "unreached"); + MOZ_FALLTHROUGH_ASSERT("AudioTimelineEvent::SetTarget"); case AudioTimelineEvent::SetValue: case AudioTimelineEvent::Cancel: case AudioTimelineEvent::Stream: @@ -606,7 +606,7 @@ public: aPrevious->mCurve, aPrevious->mCurveLength, aPrevious->mDuration, aTime); case AudioTimelineEvent::SetTarget: - MOZ_ASSERT(false, "unreached"); + MOZ_FALLTHROUGH_ASSERT("AudioTimelineEvent::SetTarget"); case AudioTimelineEvent::SetValue: case AudioTimelineEvent::Cancel: case AudioTimelineEvent::Stream: diff --git a/dom/media/webaudio/MediaBufferDecoder.cpp b/dom/media/webaudio/MediaBufferDecoder.cpp index 8fda135a4c..6807dbc2b6 100644 --- a/dom/media/webaudio/MediaBufferDecoder.cpp +++ b/dom/media/webaudio/MediaBufferDecoder.cpp @@ -549,15 +549,17 @@ WebAudioDecodeJob::OnSuccess(ErrorCode aErrorCode) MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aErrorCode == NoError); - // Ignore errors in calling the callback, since there is not much that we can - // do about it here. - ErrorResult rv; if (mSuccessCallback) { + ErrorResult rv; mSuccessCallback->Call(*mOutput, rv); + // Ignore errors in calling the callback, since there is not much that we can + // do about it here. + rv.SuppressException(); } mPromise->MaybeResolve(mOutput); mContext->RemoveFromDecodeQueue(this); + } void @@ -568,7 +570,7 @@ WebAudioDecodeJob::OnFailure(ErrorCode aErrorCode) const char* errorMessage; switch (aErrorCode) { case NoError: - MOZ_ASSERT(false, "Who passed NoError to OnFailure?"); + MOZ_FALLTHROUGH_ASSERT("Who passed NoError to OnFailure?"); // Fall through to get some sort of a sane error message if this actually // happens at runtime. case UnknownError: @@ -599,8 +601,7 @@ WebAudioDecodeJob::OnFailure(ErrorCode aErrorCode) // Ignore errors in calling the callback, since there is not much that we can // do about it here. if (mFailureCallback) { - ErrorResult rv; - mFailureCallback->Call(rv); + mFailureCallback->Call(); } mPromise->MaybeReject(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR); diff --git a/dom/media/webm/WebMBufferedParser.cpp b/dom/media/webm/WebMBufferedParser.cpp index 9639482dc9..f9e0408969 100644 --- a/dom/media/webm/WebMBufferedParser.cpp +++ b/dom/media/webm/WebMBufferedParser.cpp @@ -129,7 +129,7 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength, case EBML_ID: mLastInitStartOffset = mCurrentOffset + (p - aBuffer) - (mElement.mID.mLength + mElement.mSize.mLength); - /* FALLTHROUGH */ + MOZ_FALLTHROUGH; default: mSkipBytes = mElement.mSize.mValue; mState = SKIP_DATA; diff --git a/dom/plugins/base/nsJSNPRuntime.cpp b/dom/plugins/base/nsJSNPRuntime.cpp index e53ed32fb6..e01ca7eb0a 100644 --- a/dom/plugins/base/nsJSNPRuntime.cpp +++ b/dom/plugins/base/nsJSNPRuntime.cpp @@ -287,9 +287,9 @@ TraceJSObjWrappers(JSTracer *trc, void *data) // any of them moved. for (JSObjWrapperTable::Enum e(sJSObjWrappers); !e.empty(); e.popFront()) { nsJSObjWrapperKey key = e.front().key(); - JS_CallUnbarrieredObjectTracer(trc, &key.mJSObj, "sJSObjWrappers key object"); + JS::UnsafeTraceRoot(trc, &key.mJSObj, "sJSObjWrappers key object"); nsJSObjWrapper *wrapper = e.front().value(); - JS::TraceNullableEdge(trc, &wrapper->mJSObj, "sJSObjWrappers wrapper object"); + JS::TraceEdge(trc, &wrapper->mJSObj, "sJSObjWrappers wrapper object"); if (key != e.front().key()) { e.rekeyFront(key); } @@ -1102,7 +1102,7 @@ JSObjWrapperKeyMarkCallback(JSTracer* trc, JSObject* obj, void* data) { if (!p) return; - JS_CallUnbarrieredObjectTracer(trc, &obj, "sJSObjWrappers key object"); + js::UnsafeTraceManuallyBarrieredEdge(trc, &obj, "sJSObjWrappers key object"); nsJSObjWrapperKey newKey(obj, npp); sJSObjWrappers.rekeyIfMoved(oldKey, newKey); } @@ -2274,8 +2274,8 @@ NPObjectMember_Trace(JSTracer* trc, JSObject* obj) // There's no strong reference from our private data to the // NPObject, so make sure to mark the NPObject wrapper to keep the // NPObject alive as long as this NPObjectMember is alive. - JS::TraceNullableEdge(trc, &memberPrivate->npobjWrapper, - "NPObject Member => npobjWrapper"); + JS::TraceEdge(trc, &memberPrivate->npobjWrapper, + "NPObject Member => npobjWrapper"); } static bool diff --git a/dom/xbl/nsXBLMaybeCompiled.h b/dom/xbl/nsXBLMaybeCompiled.h index e478a3a23f..63c1b24ce6 100644 --- a/dom/xbl/nsXBLMaybeCompiled.h +++ b/dom/xbl/nsXBLMaybeCompiled.h @@ -79,18 +79,22 @@ private: JSObject* mCompiled; }; - friend struct js::GCMethods>; + friend struct js::BarrierMethods>; }; /* Add support for JS::Heap. */ namespace js { template -struct GCMethods > +struct GCPolicy> { - typedef struct GCMethods Base; - static nsXBLMaybeCompiled initial() { return nsXBLMaybeCompiled(); } +}; + +template +struct BarrierMethods> +{ + typedef struct BarrierMethods Base; static void postBarrier(nsXBLMaybeCompiled* functionp, nsXBLMaybeCompiled prev, @@ -109,14 +113,14 @@ struct GCMethods > }; template -class HeapBase > +class HeapBase> { - const JS::Heap >& wrapper() const { - return *static_cast >*>(this); + const JS::Heap>& wrapper() const { + return *static_cast>*>(this); } - JS::Heap >& wrapper() { - return *static_cast >*>(this); + JS::Heap>& wrapper() { + return *static_cast>*>(this); } const nsXBLMaybeCompiled* extract() const { diff --git a/dom/xul/nsXULElement.h b/dom/xul/nsXULElement.h index 67665561d1..767184d210 100644 --- a/dom/xul/nsXULElement.h +++ b/dom/xul/nsXULElement.h @@ -249,7 +249,7 @@ public: void TraceScriptObject(JSTracer* aTrc) { - JS::TraceNullableEdge(aTrc, &mScriptObject, "active window XUL prototype script"); + JS::TraceEdge(aTrc, &mScriptObject, "active window XUL prototype script"); } void Trace(const TraceCallbacks& aCallbacks, void* aClosure) diff --git a/js/public/Class.h b/js/public/Class.h index 47180fd316..8745564de7 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -337,8 +337,8 @@ typedef JSString* * (e.g., the DOM attributes for a given node reflected as obj) on demand. * * JS looks for a property in an object, and if not found, tries to resolve - * the given id. *resolvedp should be set to true iff the property was - * was defined on |obj|. + * the given id. *resolvedp should be set to true iff the property was defined + * on |obj|. */ typedef bool (* JSResolveOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, diff --git a/js/public/Debug.h b/js/public/Debug.h index 976b329d0a..e1cf17c712 100644 --- a/js/public/Debug.h +++ b/js/public/Debug.h @@ -154,7 +154,7 @@ class Builder { // A rooted reference to our value. PersistentRooted value; - BuiltThing(JSContext* cx, Builder& owner_, T value_ = js::GCMethods::initial()) + BuiltThing(JSContext* cx, Builder& owner_, T value_ = js::GCPolicy::initial()) : owner(owner_), value(cx, value_) { owner.assertBuilt(value_); diff --git a/js/public/Id.h b/js/public/Id.h index d5ddef9bab..bb3c581244 100644 --- a/js/public/Id.h +++ b/js/public/Id.h @@ -169,9 +169,14 @@ extern JS_PUBLIC_DATA(const JS::HandleId) JSID_EMPTYHANDLE; namespace js { template <> -struct GCMethods +struct GCPolicy { static jsid initial() { return JSID_VOID; } +}; + +template <> +struct BarrierMethods +{ static void postBarrier(jsid* idp, jsid prev, jsid next) {} }; diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h index d30d8576a3..b6ba800491 100644 --- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -105,7 +105,11 @@ namespace js { template -struct GCMethods { +struct BarrierMethods { +}; + +template +struct GCPolicy { static T initial() { return T(); } }; @@ -221,7 +225,7 @@ class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase Heap() { static_assert(sizeof(T) == sizeof(Heap), "Heap must be binary compatible with T."); - init(js::GCMethods::initial()); + init(js::GCPolicy::initial()); } explicit Heap(T p) { init(p); } @@ -234,7 +238,7 @@ class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase explicit Heap(const Heap& p) { init(p.ptr); } ~Heap() { - post(ptr, js::GCMethods::initial()); + post(ptr, js::GCPolicy::initial()); } DECLARE_POINTER_CONSTREF_OPS(T); @@ -258,7 +262,7 @@ class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase private: void init(T newPtr) { ptr = newPtr; - post(js::GCMethods::initial(), ptr); + post(js::GCPolicy::initial(), ptr); } void set(T newPtr) { @@ -268,7 +272,7 @@ class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase } void post(const T& prev, const T& next) { - js::GCMethods::postBarrier(&ptr, prev, next); + js::BarrierMethods::postBarrier(&ptr, prev, next); } enum { @@ -527,7 +531,7 @@ struct RootKind }; template -struct GCMethods +struct BarrierMethods { static T* initial() { return nullptr; } static void postBarrier(T** vp, T* prev, T* next) { @@ -538,7 +542,7 @@ struct GCMethods }; template <> -struct GCMethods +struct BarrierMethods { static JSObject* initial() { return nullptr; } static gc::Cell* asGCThingOrNull(JSObject* v) { @@ -553,7 +557,7 @@ struct GCMethods }; template <> -struct GCMethods +struct BarrierMethods { static JSFunction* initial() { return nullptr; } static void postBarrier(JSFunction** vp, JSFunction* prev, JSFunction* next) { @@ -703,7 +707,7 @@ class MOZ_RAII Rooted : public js::RootedBase public: template explicit Rooted(const RootingContext& cx) - : ptr(js::GCMethods::initial()) + : ptr(js::GCPolicy::initial()) { registerWithRootLists(js::RootListsForRootingContext(cx)); } @@ -808,7 +812,7 @@ class MOZ_RAII FakeRooted : public RootedBase { public: template - explicit FakeRooted(CX* cx) : ptr(GCMethods::initial()) {} + explicit FakeRooted(CX* cx) : ptr(GCPolicy::initial()) {} template FakeRooted(CX* cx, T initial) : ptr(initial) {} @@ -1025,11 +1029,11 @@ class PersistentRooted : public js::PersistentRootedBase, } public: - PersistentRooted() : ptr(js::GCMethods::initial()) {} + PersistentRooted() : ptr(js::GCPolicy::initial()) {} template explicit PersistentRooted(const RootingContext& cx) - : ptr(js::GCMethods::initial()) + : ptr(js::GCPolicy::initial()) { registerWithRootLists(js::RootListsForRootingContext(cx)); } @@ -1062,7 +1066,7 @@ class PersistentRooted : public js::PersistentRootedBase, template void init(const RootingContext& cx) { - init(cx, js::GCMethods::initial()); + init(cx, js::GCPolicy::initial()); } template @@ -1073,7 +1077,7 @@ class PersistentRooted : public js::PersistentRootedBase, void reset() { if (initialized()) { - set(js::GCMethods::initial()); + set(js::GCPolicy::initial()); ListBase::remove(); } } @@ -1170,7 +1174,7 @@ CallTraceCallbackOnNonHeap(T* v, const TraceCallbacks& aCallbacks, const char* a { static_assert(sizeof(T) == sizeof(JS::Heap), "T and Heap must be compatible."); MOZ_ASSERT(v); - mozilla::DebugOnly cell = GCMethods::asGCThingOrNull(*v); + mozilla::DebugOnly cell = BarrierMethods::asGCThingOrNull(*v); MOZ_ASSERT(cell); MOZ_ASSERT(!IsInsideNursery(cell)); JS::Heap* asHeapT = reinterpret_cast*>(v); diff --git a/js/public/TracingAPI.h b/js/public/TracingAPI.h index 0806d43961..1b0bbac56a 100644 --- a/js/public/TracingAPI.h +++ b/js/public/TracingAPI.h @@ -294,47 +294,26 @@ namespace JS { // is dependent on the object's address. For example, if the object's address // is used as a key in a hashtable, then the object must be removed and // re-inserted with the correct hash. +// +// Note that while |edgep| must never be null, it is fine for |*edgep| to be +// nullptr. template extern JS_PUBLIC_API(void) TraceEdge(JSTracer* trc, JS::Heap* edgep, const char* name); -// As with JS::TraceEdge, but checks if *edgep is a nullptr before proceeding. -// Note that edgep itself must always be non-null. +extern JS_PUBLIC_API(void) +TraceEdge(JSTracer* trc, JS::TenuredHeap* edgep, const char* name); + +// Edges that are always traced as part of root marking do not require +// incremental barriers. This function allows for marking non-barriered +// pointers, but asserts that this happens during root marking. +// +// Note that while |edgep| must never be null, it is fine for |*edgep| to be +// nullptr. template extern JS_PUBLIC_API(void) -TraceNullableEdge(JSTracer* trc, JS::Heap* edgep, const char* name); +UnsafeTraceRoot(JSTracer* trc, T* edgep, const char* name); -} // namespace JS - -// The following JS_CallUnbarriered*Tracer functions should only be called where -// you know for sure that a heap post barrier is not required. Use with extreme -// caution! -extern JS_PUBLIC_API(void) -JS_CallUnbarrieredValueTracer(JSTracer* trc, JS::Value* valuep, const char* name); - -extern JS_PUBLIC_API(void) -JS_CallUnbarrieredIdTracer(JSTracer* trc, jsid* idp, const char* name); - -extern JS_PUBLIC_API(void) -JS_CallUnbarrieredObjectTracer(JSTracer* trc, JSObject** objp, const char* name); - -extern JS_PUBLIC_API(void) -JS_CallUnbarrieredStringTracer(JSTracer* trc, JSString** strp, const char* name); - -extern JS_PUBLIC_API(void) -JS_CallUnbarrieredScriptTracer(JSTracer* trc, JSScript** scriptp, const char* name); - -/** - * Trace an object that is known to always be tenured. No post barriers are - * required in this case. - */ -extern JS_PUBLIC_API(void) -JS_CallTenuredObjectTracer(JSTracer* trc, JS::TenuredHeap* objp, const char* name); - -extern JS_PUBLIC_API(void) -JS_TraceRuntime(JSTracer* trc); - -namespace JS { extern JS_PUBLIC_API(void) TraceChildren(JSTracer* trc, GCCellPtr thing); @@ -353,6 +332,16 @@ JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc, void* thing, JS::TraceKind kind, bool includeDetails); namespace js { + +// Trace an edge that is not a GC root and is not wrapped in a barriered +// wrapper for some reason. +// +// This method does not check if |*edgep| is non-null before tracing through +// it, so callers must check any nullable pointer before calling this method. +template +extern JS_PUBLIC_API(void) +UnsafeTraceManuallyBarrieredEdge(JSTracer* trc, T* edgep, const char* name); + namespace gc { template extern JS_PUBLIC_API(bool) @@ -396,8 +385,8 @@ struct DefaultGCPolicy : public StructGCPolicy {}; template <> struct DefaultGCPolicy { - static void trace(JSTracer* trc, jsid* id, const char* name) { - JS_CallUnbarrieredIdTracer(trc, id, name); + static void trace(JSTracer* trc, jsid* idp, const char* name) { + js::UnsafeTraceManuallyBarrieredEdge(trc, idp, name); } }; diff --git a/js/public/Value.h b/js/public/Value.h index 20a441ad32..15d4eb4b94 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -1699,14 +1699,15 @@ JS_PUBLIC_API(void) HeapValuePostBarrier(Value* valuep, const Value& prev, const namespace js { -template <> struct GCMethods +template <> +struct GCPolicy { static JS::Value initial() { return JS::UndefinedValue(); } }; -template <> struct GCMethods +template <> +struct BarrierMethods { - static JS::Value initial() { return JS::UndefinedValue(); } static gc::Cell* asGCThingOrNull(const JS::Value& v) { return v.isMarkable() ? v.toGCThing() : nullptr; } diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index f4fd9ed011..023da12003 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -3227,15 +3227,15 @@ CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr) static bool IsSimdTypeName(ModuleValidator& m, PropertyName* name, AsmJSSimdType* type) { - if (name == m.cx()->names().int32x4) { + if (name == m.cx()->names().Int32x4) { *type = AsmJSSimdType_int32x4; return true; } - if (name == m.cx()->names().float32x4) { + if (name == m.cx()->names().Float32x4) { *type = AsmJSSimdType_float32x4; return true; } - if (name == m.cx()->names().bool32x4) { + if (name == m.cx()->names().Bool32x4) { *type = AsmJSSimdType_bool32x4; return true; } @@ -6939,7 +6939,7 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line, unsigned* c // This flows into FunctionBox, so must be tenured. RootedFunction fun(m.cx(), NewScriptedFunction(m.cx(), 0, JSFunction::INTERPRETED, - name, gc::AllocKind::FUNCTION, + name, /* proto = */ nullptr, gc::AllocKind::FUNCTION, TenuredObject)); if (!fun) return false; @@ -7547,9 +7547,9 @@ static PropertyName* SimdTypeToName(JSContext* cx, AsmJSSimdType type) { switch (type) { - case AsmJSSimdType_int32x4: return cx->names().int32x4; - case AsmJSSimdType_float32x4: return cx->names().float32x4; - case AsmJSSimdType_bool32x4: return cx->names().bool32x4; + case AsmJSSimdType_int32x4: return cx->names().Int32x4; + case AsmJSSimdType_float32x4: return cx->names().Float32x4; + case AsmJSSimdType_bool32x4: return cx->names().Bool32x4; } MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type"); } @@ -7874,7 +7874,7 @@ HandleDynamicLinkFailure(JSContext* cx, const CallArgs& args, AsmJSModule& modul return false; RootedFunction fun(cx, NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL, - name, gc::AllocKind::FUNCTION, + name, /* proto = */ nullptr, gc::AllocKind::FUNCTION, TenuredObject)); if (!fun) return false; diff --git a/js/src/builtin/Classes.js b/js/src/builtin/Classes.js new file mode 100644 index 0000000000..d0f20b5fd7 --- /dev/null +++ b/js/src/builtin/Classes.js @@ -0,0 +1,17 @@ +// Give a builtin constructor that we can use as the default. When we give +// it to our newly made class, we will be sure to set it up with the correct name +// and .prototype, so that everything works properly. + +var DefaultDerivedClassConstructor = + class extends null { + constructor(...args) { + super(...allowContentSpread(args)); + } + }; +MakeDefaultConstructor(DefaultDerivedClassConstructor); + +var DefaultBaseClassConstructor = + class { + constructor() { } + }; +MakeDefaultConstructor(DefaultBaseClassConstructor); diff --git a/js/src/builtin/Date.js b/js/src/builtin/Date.js index 8e938b30da..bd2d5854cb 100644 --- a/js/src/builtin/Date.js +++ b/js/src/builtin/Date.js @@ -101,7 +101,7 @@ function Date_toLocaleString() { } // Step 7. - return intl_FormatDateTime(dateTimeFormat, x); + return intl_FormatDateTime(dateTimeFormat, x, false); } @@ -134,7 +134,7 @@ function Date_toLocaleDateString() { } // Step 7. - return intl_FormatDateTime(dateTimeFormat, x); + return intl_FormatDateTime(dateTimeFormat, x, false); } @@ -167,5 +167,5 @@ function Date_toLocaleTimeString() { } // Step 7. - return intl_FormatDateTime(dateTimeFormat, x); + return intl_FormatDateTime(dateTimeFormat, x, false); } diff --git a/js/src/builtin/Eval.cpp b/js/src/builtin/Eval.cpp index 49b5e4d04a..d0dca4605a 100644 --- a/js/src/builtin/Eval.cpp +++ b/js/src/builtin/Eval.cpp @@ -212,8 +212,7 @@ TryEvalJSON(JSContext* cx, JSLinearString* str, MutableHandleValue rval) : ParseEvalStringAsJSON(cx, linearChars.twoByteRange(), rval); } -// Define subset of ExecuteType so that casting performs the injection. -enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL }; +enum EvalType { DIRECT_EVAL, INDIRECT_EVAL }; // Common code implementing direct and indirect eval. // @@ -268,7 +267,7 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame EvalScriptGuard esg(cx); - if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame()) + if (evalType == DIRECT_EVAL && caller.isFunctionFrame()) esg.lookupInEvalCache(linearStr, callerScript, pc); if (!esg.foundScript()) { @@ -328,7 +327,7 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame // Look up the newTarget from the frame iterator. Value newTargetVal = NullValue(); - return ExecuteKernel(cx, esg.script(), *scopeobj, newTargetVal, ExecuteType(evalType), + return ExecuteKernel(cx, esg.script(), *scopeobj, newTargetVal, NullFramePtr() /* evalInFrame */, args.rval().address()); } @@ -409,7 +408,7 @@ js::DirectEvalStringFromIon(JSContext* cx, } return ExecuteKernel(cx, esg.script(), *scopeobj, newTargetValue, - ExecuteType(DIRECT_EVAL), NullFramePtr() /* evalInFrame */, vp.address()); + NullFramePtr() /* evalInFrame */, vp.address()); } bool @@ -433,8 +432,7 @@ js::DirectEval(JSContext* cx, const CallArgs& args) JSOp(*iter.pc()) == JSOP_STRICTEVAL || JSOp(*iter.pc()) == JSOP_SPREADEVAL || JSOp(*iter.pc()) == JSOP_STRICTSPREADEVAL); - MOZ_ASSERT_IF(caller.isFunctionFrame(), - caller.compartment() == caller.callee()->compartment()); + MOZ_ASSERT(caller.compartment() == caller.script()->compartment()); RootedObject scopeChain(cx, caller.scopeChain()); return EvalKernel(cx, args, DIRECT_EVAL, caller, scopeChain, iter.pc()); @@ -482,7 +480,7 @@ js::ExecuteInGlobalAndReturnScope(JSContext* cx, HandleObject global, HandleScri return false; RootedValue rval(cx); - if (!ExecuteKernel(cx, script, *scope, UndefinedValue(), EXECUTE_GLOBAL, + if (!ExecuteKernel(cx, script, *scope, UndefinedValue(), NullFramePtr() /* evalInFrame */, rval.address())) { return false; diff --git a/js/src/builtin/Function.js b/js/src/builtin/Function.js new file mode 100644 index 0000000000..3f0cf7a915 --- /dev/null +++ b/js/src/builtin/Function.js @@ -0,0 +1,299 @@ +/* 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/. */ + +// ES6 spec draft 29, 19.2.3.2 +function FunctionBind(thisArg, ...boundArgs) { + // Step 1. + var target = this; + // Step 2. + if (!IsCallable(target)) + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, 'Function', 'bind', target); + + // Step 3 (implicit). + // Steps 4-5. + var F; + var argCount = boundArgs.length; + switch (argCount) { + case 0: + F = bind_bindFunction0(target, thisArg, boundArgs); + break; + case 1: + F = bind_bindFunction1(target, thisArg, boundArgs); + break; + case 2: + F = bind_bindFunction2(target, thisArg, boundArgs); + break; + default: + F = bind_bindFunctionN(target, thisArg, boundArgs); + } + + // Steps 6-7. + var targetHasLength = callFunction(std_Object_hasOwnProperty, target, "length"); + + // Step 8. + var L; + if (targetHasLength) { + // Steps 8.a-b. + var targetLen = target.length; + // Step 8.c. + if (typeof targetLen !== 'number') { + L = 0; + } else { + // Steps d.i-ii. + L = std_Math_max(0, ToInteger(targetLen) - argCount); + } + } else { + // Step 9. + L = 0; + } + + // Steps 12-13. + var targetName = target.name; + + // Step 14. + if (typeof targetName !== "string") + targetName = ""; + + // Steps 10-11, 15-16. + _FinishBoundFunctionInit(F, target, L, targetName); + + // Ensure that the apply intrinsic has been cloned so it can be baked into + // JIT code. + var funApply = std_Function_apply; + + // Step 17. + return F; +} +/** + * bind_bindFunction{0,1,2} are special cases of the generic bind_bindFunctionN + * below. They avoid the need to merge the lists of bound arguments and call + * arguments to the bound function into a new list which is then used in a + * destructuring call of the bound function. + * + * All three of these functions again have special-cases for call argument + * counts between 0 and 5. For calls with 6+ arguments, all - bound and call - + * arguments are copied into an array before invoking the generic call and + * construct helper functions. This avoids having to use rest parameters and + * destructuring in the fast path. + * + * All bind_bindFunction{X} functions have the same signature to enable simple + * reading out of closed-over state by debugging functions. + */ +function bind_bindFunction0(fun, thisArg, boundArgs) { + return function bound() { + var a = arguments; + var constructing = _IsConstructing(); + if (constructing) { + switch (a.length) { + case 0: + return new fun(); + case 1: + return new fun(a[0]); + case 2: + return new fun(a[0], a[1]); + case 3: + return new fun(a[0], a[1], a[2]); + case 4: + return new fun(a[0], a[1], a[2], a[3]); + case 5: + return new fun(a[0], a[1], a[2], a[3], a[4]); + } + } else { + switch (a.length) { + case 0: + return callContentFunction(fun, thisArg); + case 1: + return callContentFunction(fun, thisArg, a[0]); + case 2: + return callContentFunction(fun, thisArg, a[0], a[1]); + case 3: + return callContentFunction(fun, thisArg, a[0], a[1], a[2]); + case 4: + return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3]); + case 5: + return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4]); + } + } + var callArgs = FUN_APPLY(bind_mapArguments, null, arguments); + return bind_invokeFunctionN(fun, thisArg, constructing, boundArgs, callArgs); + }; +} + +function bind_bindFunction1(fun, thisArg, boundArgs) { + var bound1 = boundArgs[0]; + return function bound() { + var a = arguments; + var constructing = _IsConstructing(); + if (constructing) { + switch (a.length) { + case 0: + return new fun(bound1); + case 1: + return new fun(bound1, a[0]); + case 2: + return new fun(bound1, a[0], a[1]); + case 3: + return new fun(bound1, a[0], a[1], a[2]); + case 4: + return new fun(bound1, a[0], a[1], a[2], a[3]); + case 5: + return new fun(bound1, a[0], a[1], a[2], a[3], a[4]); + } + } else { + switch (a.length) { + case 0: + return callContentFunction(fun, thisArg, bound1); + case 1: + return callContentFunction(fun, thisArg, bound1, a[0]); + case 2: + return callContentFunction(fun, thisArg, bound1, a[0], a[1]); + case 3: + return callContentFunction(fun, thisArg, bound1, a[0], a[1], a[2]); + case 4: + return callContentFunction(fun, thisArg, bound1, a[0], a[1], a[2], a[3]); + case 5: + return callContentFunction(fun, thisArg, bound1, a[0], a[1], a[2], a[3], a[4]); + } + } + var callArgs = FUN_APPLY(bind_mapArguments, null, arguments); + return bind_invokeFunctionN(fun, thisArg, constructing, boundArgs, callArgs); + }; +} + +function bind_bindFunction2(fun, thisArg, boundArgs) { + var bound1 = boundArgs[0]; + var bound2 = boundArgs[1]; + return function bound() { + var a = arguments; + var constructing = _IsConstructing(); + if (constructing) { + switch (a.length) { + case 0: + return new fun(bound1, bound2); + case 1: + return new fun(bound1, bound2, a[0]); + case 2: + return new fun(bound1, bound2, a[0], a[1]); + case 3: + return new fun(bound1, bound2, a[0], a[1], a[2]); + case 4: + return new fun(bound1, bound2, a[0], a[1], a[2], a[3]); + case 5: + return new fun(bound1, bound2, a[0], a[1], a[2], a[3], a[4]); + } + } else { + switch (a.length) { + case 0: + return callContentFunction(fun, thisArg, bound1, bound2); + case 1: + return callContentFunction(fun, thisArg, bound1, bound2, a[0]); + case 2: + return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1]); + case 3: + return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1], a[2]); + case 4: + return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1], a[2], a[3]); + case 5: + return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1], a[2], a[3], a[4]); + } + } + var callArgs = FUN_APPLY(bind_mapArguments, null, arguments); + return bind_invokeFunctionN(fun, thisArg, constructing, boundArgs, callArgs); + }; +} + +function bind_bindFunctionN(fun, thisArg, boundArgs) { + assert(boundArgs.length > 2, "Fast paths should be used for few-bound-args cases."); + return function bound() { + if (arguments.length === 0) { + if (_IsConstructing()) + return bind_constructFunctionN(fun, boundArgs); + else + return bind_applyFunctionN(fun, thisArg, boundArgs); + } + var callArgs = FUN_APPLY(bind_mapArguments, null, arguments); + return bind_invokeFunctionN(fun, thisArg, _IsConstructing(), boundArgs, callArgs); + }; +} + +function bind_mapArguments() { + var len = arguments.length; + var args = std_Array(len); + for (var i = 0; i < len; i++) + _DefineDataProperty(args, i, arguments[i]); + return args; +} + +function bind_invokeFunctionN(fun, thisArg, constructing, boundArgs, callArgs) { + var boundArgsCount = boundArgs.length; + var callArgsCount = callArgs.length; + var args = std_Array(boundArgsCount + callArgsCount); + for (var i = 0; i < boundArgsCount; i++) + _DefineDataProperty(args, i, boundArgs[i]); + for (var i = 0; i < callArgsCount; i++) + _DefineDataProperty(args, i + boundArgsCount, callArgs[i]); + if (constructing) + return bind_constructFunctionN(fun, args); + return bind_applyFunctionN(fun, thisArg, args); +} + +function bind_applyFunctionN(fun, thisArg, a) { + switch (a.length) { + case 0: + return callContentFunction(fun, thisArg); + case 1: + return callContentFunction(fun, thisArg, a[0]); + case 2: + return callContentFunction(fun, thisArg, a[0], a[1]); + case 3: + return callContentFunction(fun, thisArg, a[0], a[1], a[2]); + case 4: + return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3]); + case 5: + return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4]); + case 6: + return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5]); + case 7: + return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5], a[6]); + case 8: + return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); + case 9: + return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]); + default: + return FUN_APPLY(fun, thisArg, a); + } +} + +function bind_constructFunctionN(fun, a) { + switch (a.length) { + case 1: + return new fun(a[0]); + case 2: + return new fun(a[0], a[1]); + case 3: + return new fun(a[0], a[1], a[2]); + case 4: + return new fun(a[0], a[1], a[2], a[3]); + case 5: + return new fun(a[0], a[1], a[2], a[3], a[4]); + case 6: + return new fun(a[0], a[1], a[2], a[3], a[4], a[5]); + case 7: + return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6]); + case 8: + return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); + case 9: + return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]); + case 10: + return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]); + case 11: + return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10]); + case 12: + return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11]); + default: + assert(a.length !== 0, + "bound function construction without args should be handled by caller"); + return _ConstructFunction(fun, a); + } +} diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp index 89fe217868..c780d14347 100644 --- a/js/src/builtin/Intl.cpp +++ b/js/src/builtin/Intl.cpp @@ -12,6 +12,7 @@ #include "builtin/Intl.h" #include "mozilla/Range.h" +#include "mozilla/ScopeExit.h" #include @@ -45,6 +46,7 @@ using namespace js; using mozilla::IsFinite; using mozilla::IsNegativeZero; +using mozilla::MakeScopeExit; #if ENABLE_INTL_API using icu::Locale; @@ -176,6 +178,7 @@ ucol_getKeywordValuesForLocale(const char* key, const char* locale, UBool common struct UParseError; struct UFieldPosition; +struct UFieldPositionIterator; typedef void* UNumberFormat; enum UNumberFormatStyle { @@ -339,6 +342,46 @@ udatpg_close(UDateTimePatternGenerator* dtpg) typedef void* UCalendar; typedef void* UDateFormat; +enum UDateFormatField { + UDAT_ERA_FIELD = 0, + UDAT_YEAR_FIELD = 1, + UDAT_MONTH_FIELD = 2, + UDAT_DATE_FIELD = 3, + UDAT_HOUR_OF_DAY1_FIELD = 4, + UDAT_HOUR_OF_DAY0_FIELD = 5, + UDAT_MINUTE_FIELD = 6, + UDAT_SECOND_FIELD = 7, + UDAT_FRACTIONAL_SECOND_FIELD = 8, + UDAT_DAY_OF_WEEK_FIELD = 9, + UDAT_DAY_OF_YEAR_FIELD = 10, + UDAT_DAY_OF_WEEK_IN_MONTH_FIELD = 11, + UDAT_WEEK_OF_YEAR_FIELD = 12, + UDAT_WEEK_OF_MONTH_FIELD = 13, + UDAT_AM_PM_FIELD = 14, + UDAT_HOUR1_FIELD = 15, + UDAT_HOUR0_FIELD = 16, + UDAT_TIMEZONE_FIELD = 17, + UDAT_YEAR_WOY_FIELD = 18, + UDAT_DOW_LOCAL_FIELD = 19, + UDAT_EXTENDED_YEAR_FIELD = 20, + UDAT_JULIAN_DAY_FIELD = 21, + UDAT_MILLISECONDS_IN_DAY_FIELD = 22, + UDAT_TIMEZONE_RFC_FIELD = 23, + UDAT_TIMEZONE_GENERIC_FIELD = 24, + UDAT_STANDALONE_DAY_FIELD = 25, + UDAT_STANDALONE_MONTH_FIELD = 26, + UDAT_QUARTER_FIELD = 27, + UDAT_STANDALONE_QUARTER_FIELD = 28, + UDAT_TIMEZONE_SPECIAL_FIELD = 29, + UDAT_YEAR_NAME_FIELD = 30, + UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD = 31, + UDAT_TIMEZONE_ISO_FIELD = 32, + UDAT_TIMEZONE_ISO_LOCAL_FIELD = 33, + UDAT_RELATED_YEAR_FIELD = 34, + UDAT_TIME_SEPARATOR_FIELD = 35, + UDAT_FIELD_COUNT = 36 +}; + enum UDateFormatStyle { UDAT_PATTERN = -2, UDAT_IGNORE = UDAT_PATTERN @@ -383,6 +426,32 @@ udat_format(const UDateFormat* format, UDate dateToFormat, UChar* result, MOZ_CRASH("udat_format: Intl API disabled"); } +static int32_t +udat_formatForFields(const UDateFormat* format, UDate dateToFormat, + UChar* result, int32_t resultLength, UFieldPositionIterator* fpositer, + UErrorCode* status) +{ + MOZ_CRASH("udat_formatForFields: Intl API disabled"); +} + +static UFieldPositionIterator* +ufieldpositer_open(UErrorCode* status) +{ + MOZ_CRASH("ufieldpositer_open: Intl API disabled"); +} + +static void +ufieldpositer_close(UFieldPositionIterator* fpositer) +{ + MOZ_CRASH("ufieldpositer_close: Intl API disabled"); +} + +static int32_t +ufieldpositer_next(UFieldPositionIterator* fpositer, int32_t* beginIndex, int32_t* endIndex) +{ + MOZ_CRASH("ufieldpositer_next: Intl API disabled"); +} + static void udat_close(UDateFormat* format) { @@ -1669,11 +1738,9 @@ InitDateTimeFormatClass(JSContext* cx, HandleObject Intl, Handle if (!JS_DefineFunctions(cx, proto, dateTimeFormat_methods)) return nullptr; - /* - * Install the getter for DateTimeFormat.prototype.format, which returns a - * bound formatting function for the specified DateTimeFormat object - * (suitable for passing to methods like Array.prototype.map). - */ + // Install a getter for DateTimeFormat.prototype.format that returns a + // formatting function bound to a specified DateTimeFormat object (suitable + // for passing to methods like Array.prototype.map). RootedValue getter(cx); if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().DateTimeFormatFormatGet, &getter)) @@ -1687,6 +1754,22 @@ InitDateTimeFormatClass(JSContext* cx, HandleObject Intl, Handle return nullptr; } + // If the still-experimental DateTimeFormat.prototype.formatToParts method + // is enabled, also add its getter. + if (cx->compartment()->creationOptions().experimentalDateTimeFormatFormatToPartsEnabled()) { + if (!GlobalObject::getIntrinsicValue(cx, cx->global(), + cx->names().DateTimeFormatFormatToPartsGet, &getter)) + { + return nullptr; + } + if (!DefineProperty(cx, proto, cx->names().formatToParts, UndefinedHandleValue, + JS_DATA_TO_FUNC_PTR(JSGetterOp, &getter.toObject()), + nullptr, JSPROP_GETTER | JSPROP_SHARED)) + { + return nullptr; + } + } + RootedValue options(cx); if (!CreateDefaultOptions(cx, &options)) return nullptr; @@ -1986,6 +2069,210 @@ intl_FormatDateTime(JSContext* cx, UDateFormat* df, double x, MutableHandleValue return false; result.setString(str); + + return true; +} + +using FieldType = ImmutablePropertyNamePtr JSAtomState::*; + +static FieldType +GetFieldTypeForFormatField(UDateFormatField fieldName) +{ + // See intl/icu/source/i18n/unicode/udat.h for a detailed field list. This + // switch is deliberately exhaustive: cases might have to be added/removed + // if this code is compiled with a different ICU with more + // UDateFormatField enum initializers. Please guard such cases with + // appropriate ICU version-testing #ifdefs, should cross-version divergence + // occur. + switch (fieldName) { + case UDAT_ERA_FIELD: + return &JSAtomState::era; + case UDAT_YEAR_FIELD: + case UDAT_YEAR_WOY_FIELD: + case UDAT_EXTENDED_YEAR_FIELD: + case UDAT_YEAR_NAME_FIELD: + return &JSAtomState::year; + + case UDAT_MONTH_FIELD: + case UDAT_STANDALONE_MONTH_FIELD: + return &JSAtomState::month; + + case UDAT_DATE_FIELD: + case UDAT_JULIAN_DAY_FIELD: + return &JSAtomState::day; + + case UDAT_HOUR_OF_DAY1_FIELD: + case UDAT_HOUR_OF_DAY0_FIELD: + case UDAT_HOUR1_FIELD: + case UDAT_HOUR0_FIELD: + return &JSAtomState::hour; + + case UDAT_MINUTE_FIELD: + return &JSAtomState::minute; + + case UDAT_SECOND_FIELD: + return &JSAtomState::second; + + case UDAT_DAY_OF_WEEK_FIELD: + case UDAT_STANDALONE_DAY_FIELD: + case UDAT_DOW_LOCAL_FIELD: + case UDAT_DAY_OF_WEEK_IN_MONTH_FIELD: + return &JSAtomState::weekday; + + case UDAT_AM_PM_FIELD: + return &JSAtomState::dayperiod; + + case UDAT_TIMEZONE_FIELD: + return &JSAtomState::timeZoneName; + + case UDAT_FRACTIONAL_SECOND_FIELD: + case UDAT_DAY_OF_YEAR_FIELD: + case UDAT_WEEK_OF_YEAR_FIELD: + case UDAT_WEEK_OF_MONTH_FIELD: + case UDAT_MILLISECONDS_IN_DAY_FIELD: + case UDAT_TIMEZONE_RFC_FIELD: + case UDAT_TIMEZONE_GENERIC_FIELD: + case UDAT_QUARTER_FIELD: + case UDAT_STANDALONE_QUARTER_FIELD: + case UDAT_TIMEZONE_SPECIAL_FIELD: + case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: + case UDAT_TIMEZONE_ISO_FIELD: + case UDAT_TIMEZONE_ISO_LOCAL_FIELD: +#ifndef U_HIDE_INTERNAL_API + case UDAT_RELATED_YEAR_FIELD: +#endif +#ifndef U_HIDE_DRAFT_API + case UDAT_TIME_SEPARATOR_FIELD: +#endif + // These fields are all unsupported. + return nullptr; + + case UDAT_FIELD_COUNT: + MOZ_ASSERT_UNREACHABLE("format field sentinel value returned by " + "iterator!"); + } + + MOZ_ASSERT_UNREACHABLE("unenumerated, undocumented format field returned " + "by iterator"); + return nullptr; +} + +static bool +intl_FormatToPartsDateTime(JSContext* cx, UDateFormat* df, double x, MutableHandleValue result) +{ + if (!IsFinite(x)) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DATE_NOT_FINITE); + return false; + } + + Vector chars(cx); + if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE)) + return false; + + UErrorCode status = U_ZERO_ERROR; + UFieldPositionIterator* fpositer = ufieldpositer_open(&status); + if (U_FAILURE(status)) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR); + return false; + } + auto closeFieldPosIter = MakeScopeExit([&]() { ufieldpositer_close(fpositer); }); + + int resultSize = + udat_formatForFields(df, x, Char16ToUChar(chars.begin()), INITIAL_CHAR_BUFFER_SIZE, + fpositer, &status); + if (status == U_BUFFER_OVERFLOW_ERROR) { + if (!chars.resize(resultSize)) + return false; + status = U_ZERO_ERROR; + udat_formatForFields(df, x, Char16ToUChar(chars.begin()), resultSize, fpositer, &status); + } + if (U_FAILURE(status)) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR); + return false; + } + + RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx)); + if (!partsArray) + return false; + if (resultSize == 0) { + // An empty string contains no parts, so avoid extra work below. + result.setObject(*partsArray); + return true; + } + + RootedString overallResult(cx, NewStringCopyN(cx, chars.begin(), resultSize)); + if (!overallResult) + return false; + + size_t lastEndIndex = 0; + + uint32_t partIndex = 0; + RootedObject singlePart(cx); + RootedValue partType(cx); + RootedString partSubstr(cx); + RootedValue val(cx); + + auto AppendPart = [&](FieldType type, size_t beginIndex, size_t endIndex) { + singlePart = NewBuiltinClassInstance(cx); + if (!singlePart) + return false; + + partType = StringValue(cx->names().*type); + if (!DefineProperty(cx, singlePart, cx->names().type, partType)) + return false; + + partSubstr = SubstringKernel(cx, overallResult, beginIndex, endIndex - beginIndex); + if (!partSubstr) + return false; + + val = StringValue(partSubstr); + if (!DefineProperty(cx, singlePart, cx->names().value, val)) + return false; + + val = ObjectValue(*singlePart); + if (!DefineElement(cx, partsArray, partIndex, val)) + return false; + + lastEndIndex = endIndex; + partIndex++; + return true; + }; + + int32_t fieldInt, beginIndexInt, endIndexInt; + while ((fieldInt = ufieldpositer_next(fpositer, &beginIndexInt, &endIndexInt)) >= 0) { + MOZ_ASSERT(beginIndexInt >= 0); + MOZ_ASSERT(endIndexInt >= 0); + MOZ_ASSERT(beginIndexInt <= endIndexInt, + "field iterator returning invalid range"); + + size_t beginIndex(beginIndexInt); + size_t endIndex(endIndexInt); + + // Technically this isn't guaranteed. But it appears true in pratice, + // and http://bugs.icu-project.org/trac/ticket/12024 is expected to + // correct the documentation lapse. + MOZ_ASSERT(lastEndIndex <= beginIndex, + "field iteration didn't return fields in order start to " + "finish as expected"); + + if (FieldType type = GetFieldTypeForFormatField(static_cast(fieldInt))) { + if (lastEndIndex < beginIndex) { + if (!AppendPart(&JSAtomState::separator, lastEndIndex, beginIndex)) + return false; + } + + if (!AppendPart(type, beginIndex, endIndex)) + return false; + } + } + + // Append any final separator. + if (lastEndIndex < overallResult->length()) { + if (!AppendPart(&JSAtomState::separator, lastEndIndex, overallResult->length())) + return false; + } + + result.setObject(*partsArray); return true; } @@ -1993,9 +2280,10 @@ bool js::intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(args.length() == 3); MOZ_ASSERT(args[0].isObject()); MOZ_ASSERT(args[1].isNumber()); + MOZ_ASSERT(args[2].isBoolean()); RootedObject dateTimeFormat(cx, &args[0].toObject()); @@ -2024,7 +2312,9 @@ js::intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp) // Use the UDateFormat to actually format the time stamp. RootedValue result(cx); - bool success = intl_FormatDateTime(cx, df, args[1].toNumber(), &result); + bool success = args[2].toBoolean() + ? intl_FormatToPartsDateTime(cx, df, args[1].toNumber(), &result) + : intl_FormatDateTime(cx, df, args[1].toNumber(), &result); if (!isDateTimeFormatInstance) udat_close(df); diff --git a/js/src/builtin/Intl.js b/js/src/builtin/Intl.js index 892b9e905e..6fc89827f6 100644 --- a/js/src/builtin/Intl.js +++ b/js/src/builtin/Intl.js @@ -1596,7 +1596,7 @@ function Intl_Collator_compare_get() { var F = collatorCompareToBind; // Step 1.b-d. - var bc = callFunction(std_Function_bind, F, this); + var bc = callFunction(FunctionBind, F, this); internals.boundCompare = bc; } @@ -2037,7 +2037,7 @@ function Intl_NumberFormat_format_get() { var F = numberFormatFormatToBind; // Step 1.b-d. - var bf = callFunction(std_Function_bind, F, this); + var bf = callFunction(FunctionBind, F, this); internals.boundFormat = bf; } // Step 2. @@ -2713,10 +2713,9 @@ function dateTimeFormatFormatToBind() { var x = (date === undefined) ? std_Date_now() : ToNumber(date); // Step 1.a.iii. - return intl_FormatDateTime(this, x); + return intl_FormatDateTime(this, x, false); } - /** * Returns a function bound to this DateTimeFormat that returns a String value * representing the result of calling ToNumber(date) according to the @@ -2734,7 +2733,7 @@ function Intl_DateTimeFormat_format_get() { var F = dateTimeFormatFormatToBind; // Step 1.b-d. - var bf = callFunction(std_Function_bind, F, this); + var bf = callFunction(FunctionBind, F, this); internals.boundFormat = bf; } @@ -2743,6 +2742,35 @@ function Intl_DateTimeFormat_format_get() { } +function dateTimeFormatFormatToPartsToBind() { + // Steps 1.a.i-ii + var date = arguments.length > 0 ? arguments[0] : undefined; + var x = (date === undefined) ? std_Date_now() : ToNumber(date); + + // Step 1.a.iii. + return intl_FormatDateTime(this, x, true); +} + + +function Intl_DateTimeFormat_formatToParts_get() { + // Check "this DateTimeFormat object" per introduction of section 12.3. + var internals = getDateTimeFormatInternals(this, "formatToParts"); + + // Step 1. + if (internals.boundFormatToParts === undefined) { + // Step 1.a. + var F = dateTimeFormatFormatToPartsToBind; + + // Step 1.b-d. + var bf = callFunction(FunctionBind, F, this); + internals.boundFormatToParts = bf; + } + + // Step 2. + return internals.boundFormatToParts; +} + + /** * Returns the resolved options for a DateTimeFormat object. * diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp index e652c2aaa8..561152e2a6 100644 --- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -906,18 +906,18 @@ ModuleBuilder::ModuleBuilder(JSContext* cx, HandleModuleObject module) {} bool -ModuleBuilder::initModule() +ModuleBuilder::buildTables() { for (const auto& e : exportEntries_) { RootedExportEntryObject exp(cx_, e); if (!exp->moduleRequest()) { RootedImportEntryObject importEntry(cx_, importEntryFor(exp->localName())); if (!importEntry) { - if (!appendLocalExportEntry(exp)) + if (!localExportEntries_.append(exp)) return false; } else { if (importEntry->importName() == cx_->names().star) { - if (!appendLocalExportEntry(exp)) + if (!localExportEntries_.append(exp)) return false; } else { RootedAtom exportName(cx_, exp->exportName()); @@ -942,6 +942,12 @@ ModuleBuilder::initModule() } } + return true; +} + +bool +ModuleBuilder::initModule() +{ RootedArrayObject requestedModules(cx_, createArray(requestedModules_)); if (!requestedModules) return false; @@ -972,19 +978,6 @@ ModuleBuilder::initModule() return true; } -bool -ModuleBuilder::appendLocalExportEntry(HandleExportEntryObject exp) -{ - if (!module_->initialEnvironment().lookup(cx_, AtomToId(exp->localName()))) { - JSAutoByteString str; - str.encodeLatin1(cx_, exp->localName()); - JS_ReportErrorNumber(cx_, GetErrorMessage, nullptr, JSMSG_MISSING_EXPORT, str.ptr()); - return false; - } - - return localExportEntries_.append(exp); -} - bool ModuleBuilder::processImport(frontend::ParseNode* pn) { diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h index bab8e38601..f0352955b9 100644 --- a/js/src/builtin/ModuleObject.h +++ b/js/src/builtin/ModuleObject.h @@ -282,6 +282,12 @@ class MOZ_STACK_CLASS ModuleBuilder bool hasExportedName(JSAtom* name) const; + using ExportEntryVector = TraceableVector; + const ExportEntryVector& localExportEntries() const { + return localExportEntries_; + } + + bool buildTables(); bool initModule(); private: @@ -289,8 +295,7 @@ class MOZ_STACK_CLASS ModuleBuilder using RootedAtomVector = JS::Rooted; using ImportEntryVector = TraceableVector; using RootedImportEntryVector = JS::Rooted; - using ExportEntryVector = TraceableVector ; - using RootedExportEntryVector = JS::Rooted ; + using RootedExportEntryVector = JS::Rooted; JSContext* cx_; RootedModuleObject module_; @@ -310,8 +315,6 @@ class MOZ_STACK_CLASS ModuleBuilder bool maybeAppendRequestedModule(HandleAtom module); - bool appendLocalExportEntry(HandleExportEntryObject exp); - template ArrayObject* createArray(const TraceableVector& vector); }; diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 33431517d5..4c425f1990 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -1092,14 +1092,19 @@ FinishObjectClassInit(JSContext* cx, JS::HandleObject ctor, JS::HandleObject pro return false; /* - * Define self-hosted functions after setting the intrinsics holder - * (which is needed to define self-hosted functions) + * Define self-hosted functions on Object and Function after setting the + * intrinsics holder (which is needed to define self-hosted functions). */ if (!cx->runtime()->isSelfHostingGlobal(global)) { if (!JS_DefineFunctions(cx, ctor, object_static_methods, OnlyDefineLateProperties)) return false; if (!JS_DefineFunctions(cx, proto, object_methods, OnlyDefineLateProperties)) return false; + RootedObject funProto(cx, global->getOrCreateFunctionPrototype(cx)); + if (!funProto) + return false; + if (!JS_DefineFunctions(cx, funProto, function_methods, OnlyDefineLateProperties)) + return false; } /* diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index 22a590ed73..fac86e9f36 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -61,15 +61,24 @@ js::IsVectorObject(HandleValue v) return CheckVectorObject(v, V::type); } -template bool js::IsVectorObject(HandleValue v); -template bool js::IsVectorObject(HandleValue v); -template bool js::IsVectorObject(HandleValue v); -template bool js::IsVectorObject(HandleValue v); -template bool js::IsVectorObject(HandleValue v); -template bool js::IsVectorObject(HandleValue v); -template bool js::IsVectorObject(HandleValue v); -template bool js::IsVectorObject(HandleValue v); -template bool js::IsVectorObject(HandleValue v); +#define FOR_EACH_SIMD(macro) \ + macro(Int8x16) \ + macro(Int16x8) \ + macro(Int32x4) \ + macro(Uint8x16) \ + macro(Uint16x8) \ + macro(Uint32x4) \ + macro(Float32x4) \ + macro(Float64x2) \ + macro(Bool8x16) \ + macro(Bool16x8) \ + macro(Bool32x4) \ + macro(Bool64x2) + +#define InstantiateIsVectorObject_(T) \ + template bool js::IsVectorObject(HandleValue v); +FOR_EACH_SIMD(InstantiateIsVectorObject_) +#undef InstantiateIsVectorObject_ static inline bool ErrorBadArgs(JSContext* cx) @@ -100,7 +109,8 @@ template static SimdTypeDescr* GetTypeDescr(JSContext* cx) { - return cx->global()->getOrCreateSimdTypeDescr(cx); + RootedGlobalObject global(cx, cx->global()); + return GlobalObject::getOrCreateSimdTypeDescr(cx, global); } template @@ -147,67 +157,18 @@ const Class SimdTypeDescr::class_ = { namespace { -// These classes just exist to group together various properties and so on. -class Int8x16Defn { - public: - static const SimdTypeDescr::Type type = SimdTypeDescr::Int8x16; - static const JSFunctionSpec Methods[]; -}; -class Int16x8Defn { - public: - static const SimdTypeDescr::Type type = SimdTypeDescr::Int16x8; - static const JSFunctionSpec Methods[]; -}; -class Int32x4Defn { - public: - static const SimdTypeDescr::Type type = SimdTypeDescr::Int32x4; - static const JSFunctionSpec Methods[]; -}; -class Uint8x16Defn { - public: - static const SimdTypeDescr::Type type = SimdTypeDescr::Uint8x16; - static const JSFunctionSpec Methods[]; -}; -class Uint16x8Defn { - public: - static const SimdTypeDescr::Type type = SimdTypeDescr::Uint16x8; - static const JSFunctionSpec Methods[]; -}; -class Uint32x4Defn { - public: - static const SimdTypeDescr::Type type = SimdTypeDescr::Uint32x4; - static const JSFunctionSpec Methods[]; -}; -class Float32x4Defn { - public: - static const SimdTypeDescr::Type type = SimdTypeDescr::Float32x4; - static const JSFunctionSpec Methods[]; -}; -class Float64x2Defn { - public: - static const SimdTypeDescr::Type type = SimdTypeDescr::Float64x2; - static const JSFunctionSpec Methods[]; -}; -class Bool8x16Defn { - public: - static const SimdTypeDescr::Type type = SimdTypeDescr::Bool8x16; - static const JSFunctionSpec Methods[]; -}; -class Bool16x8Defn { - public: - static const SimdTypeDescr::Type type = SimdTypeDescr::Bool16x8; - static const JSFunctionSpec Methods[]; -}; -class Bool32x4Defn { - public: - static const SimdTypeDescr::Type type = SimdTypeDescr::Bool32x4; - static const JSFunctionSpec Methods[]; -}; -class Bool64x2Defn { - public: - static const SimdTypeDescr::Type type = SimdTypeDescr::Bool64x2; - static const JSFunctionSpec Methods[]; +// Define classes (Int8x16Defn, Int16x8Defn, etc.) to group together various +// properties and so on. +#define DEFINE_DEFN_(TypeName) \ +class TypeName##Defn { \ + public: \ + static const SimdTypeDescr::Type type = SimdTypeDescr::TypeName; \ + static const JSFunctionSpec Methods[]; \ }; + +FOR_EACH_SIMD(DEFINE_DEFN_) +#undef DEFINE_DEFN_ + } // namespace // Shared type descriptor methods for all SIMD types. @@ -320,68 +281,6 @@ const JSFunctionSpec Bool64x2Defn::Methods[] = { JS_FS_END }; -template -static JSObject* -CreateAndBindSimdClass(JSContext* cx, Handle global, HandleObject globalSimdObject, - HandlePropertyName stringRepr) -{ - const SimdTypeDescr::Type type = T::type; - - RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx)); - if (!funcProto) - return nullptr; - - // Create type constructor itself and initialize its reserved slots. - Rooted typeDescr(cx); - typeDescr = NewObjectWithGivenProto(cx, funcProto, SingletonObject); - if (!typeDescr) - return nullptr; - - typeDescr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Simd)); - typeDescr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr)); - typeDescr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(SimdTypeDescr::alignment(type))); - typeDescr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(SimdTypeDescr::size(type))); - typeDescr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(false)); - typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type)); - - if (!CreateUserSizeAndAlignmentProperties(cx, typeDescr)) - return nullptr; - - // Create prototype property, which inherits from Object.prototype. - RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); - if (!objProto) - return nullptr; - Rooted proto(cx); - proto = NewObjectWithGivenProto(cx, objProto, SingletonObject); - if (!proto) - return nullptr; - typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto)); - - // Link constructor to prototype and install properties. - if (!JS_DefineFunctions(cx, typeDescr, TypeDescriptorMethods)) - return nullptr; - - if (!LinkConstructorAndPrototype(cx, typeDescr, proto) || - !JS_DefineFunctions(cx, proto, SimdTypedObjectMethods)) - { - return nullptr; - } - - // Bind type descriptor to the global SIMD object - RootedValue typeValue(cx, ObjectValue(*typeDescr)); - if (!JS_DefineFunctions(cx, typeDescr, T::Methods) || - !DefineProperty(cx, globalSimdObject, stringRepr, typeValue, nullptr, nullptr, - JSPROP_READONLY | JSPROP_PERMANENT)) - { - return nullptr; - } - - uint32_t slot = uint32_t(typeDescr->type()); - globalSimdObject->as().setReservedSlot(slot, ObjectValue(*typeDescr)); - - return typeDescr; -} - template static bool FillLanes(JSContext* cx, Handle result, const CallArgs& args) @@ -410,21 +309,14 @@ SimdTypeDescr::call(JSContext* cx, unsigned argc, Value* vp) if (!result) return false; +#define CASE_CALL_(Type) \ + case SimdTypeDescr::Type: return FillLanes< ::Type>(cx, result, args); + switch (descr->type()) { - case SimdTypeDescr::Int8x16: return FillLanes< ::Int8x16>(cx, result, args); - case SimdTypeDescr::Int16x8: return FillLanes< ::Int16x8>(cx, result, args); - case SimdTypeDescr::Int32x4: return FillLanes< ::Int32x4>(cx, result, args); - case SimdTypeDescr::Uint8x16: return FillLanes< ::Uint8x16>(cx, result, args); - case SimdTypeDescr::Uint16x8: return FillLanes< ::Uint16x8>(cx, result, args); - case SimdTypeDescr::Uint32x4: return FillLanes< ::Uint32x4>(cx, result, args); - case SimdTypeDescr::Float32x4: return FillLanes< ::Float32x4>(cx, result, args); - case SimdTypeDescr::Float64x2: return FillLanes< ::Float64x2>(cx, result, args); - case SimdTypeDescr::Bool8x16: return FillLanes< ::Bool8x16>(cx, result, args); - case SimdTypeDescr::Bool16x8: return FillLanes< ::Bool16x8>(cx, result, args); - case SimdTypeDescr::Bool32x4: return FillLanes< ::Bool32x4>(cx, result, args); - case SimdTypeDescr::Bool64x2: return FillLanes< ::Bool64x2>(cx, result, args); + FOR_EACH_SIMD(CASE_CALL_) } +#undef CASE_CALL_ MOZ_CRASH("unexpected SIMD descriptor"); return false; } @@ -436,7 +328,13 @@ static const uint32_t SIMD_SLOTS_COUNT = SimdTypeDescr::LAST_TYPE + 1; const Class SIMDObject::class_ = { "SIMD", - JSCLASS_HAS_RESERVED_SLOTS(SIMD_SLOTS_COUNT) + JSCLASS_HAS_RESERVED_SLOTS(SIMD_SLOTS_COUNT), + nullptr, /* addProperty */ + nullptr, /* delProperty */ + nullptr, /* getProperty */ + nullptr, /* setProperty */ + nullptr, /* enumerate */ + resolve /* resolve */ }; bool @@ -470,77 +368,106 @@ GlobalObject::initSimdObject(JSContext* cx, Handle global) return true; } +template +static bool +CreateSimdType(JSContext* cx, Handle global, HandlePropertyName stringRepr) +{ + RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx)); + if (!funcProto) + return false; + + // Create type constructor itself and initialize its reserved slots. + Rooted typeDescr(cx); + typeDescr = NewObjectWithGivenProto(cx, funcProto, SingletonObject); + if (!typeDescr) + return false; + + const SimdTypeDescr::Type type = T::type; + typeDescr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Simd)); + typeDescr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr)); + typeDescr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(SimdTypeDescr::alignment(type))); + typeDescr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(SimdTypeDescr::size(type))); + typeDescr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(false)); + typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type)); + + if (!CreateUserSizeAndAlignmentProperties(cx, typeDescr)) + return false; + + // Create prototype property, which inherits from Object.prototype. + RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); + if (!objProto) + return false; + Rooted proto(cx); + proto = NewObjectWithGivenProto(cx, objProto, SingletonObject); + if (!proto) + return false; + typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto)); + + // Link constructor to prototype and install properties. + if (!JS_DefineFunctions(cx, typeDescr, TypeDescriptorMethods)) + return false; + + if (!LinkConstructorAndPrototype(cx, typeDescr, proto) || + !JS_DefineFunctions(cx, proto, SimdTypedObjectMethods)) + { + return false; + } + + // Bind type descriptor to the global SIMD object + RootedObject globalSimdObject(cx, global->getOrCreateSimdGlobalObject(cx)); + MOZ_ASSERT(globalSimdObject); + + RootedValue typeValue(cx, ObjectValue(*typeDescr)); + if (!JS_DefineFunctions(cx, typeDescr, T::Methods) || + !DefineProperty(cx, globalSimdObject, stringRepr, typeValue, nullptr, nullptr, + JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING)) + { + return false; + } + + uint32_t slot = uint32_t(typeDescr->type()); + MOZ_ASSERT(globalSimdObject->as().getReservedSlot(slot).isUndefined()); + globalSimdObject->as().setReservedSlot(slot, ObjectValue(*typeDescr)); + return !!typeDescr; +} + +bool +GlobalObject::initSimdType(JSContext* cx, Handle global, uint32_t simdTypeDescrType) +{ +#define CREATE_(Type) \ + case SimdTypeDescr::Type: return CreateSimdType(cx, global, cx->names().Type); + + switch (SimdTypeDescr::Type(simdTypeDescrType)) { + FOR_EACH_SIMD(CREATE_) + } + MOZ_CRASH("unexpected simd type"); + +#undef CREATE_ +} + +bool +SIMDObject::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolved) +{ + *resolved = false; + if (!JSID_IS_ATOM(id)) + return true; + JSAtom* str = JSID_TO_ATOM(id); + Rooted global(cx, cx->global()); +#define TRY_RESOLVE_(Type) \ + if (str == cx->names().Type) { \ + *resolved = CreateSimdType(cx, global, cx->names().Type); \ + return *resolved; \ + } + FOR_EACH_SIMD(TRY_RESOLVE_) +#undef TRY_RESOLVE_ + return true; +} + JSObject* js::InitSIMDClass(JSContext* cx, HandleObject obj) { - MOZ_ASSERT(obj->is()); Rooted global(cx, &obj->as()); - - RootedObject globalSimdObject(cx, global->getOrCreateSimdGlobalObject(cx)); - if (!globalSimdObject) - return nullptr; - - RootedObject i8x16(cx); - i8x16 = CreateAndBindSimdClass(cx, global, globalSimdObject, cx->names().int8x16); - if (!i8x16) - return nullptr; - - RootedObject i16x8(cx); - i16x8 = CreateAndBindSimdClass(cx, global, globalSimdObject, cx->names().int16x8); - if (!i16x8) - return nullptr; - - RootedObject i32x4(cx); - i32x4 = CreateAndBindSimdClass(cx, global, globalSimdObject, cx->names().int32x4); - if (!i32x4) - return nullptr; - - RootedObject u8x16(cx); - u8x16 = CreateAndBindSimdClass(cx, global, globalSimdObject, cx->names().uint8x16); - if (!u8x16) - return nullptr; - - RootedObject u16x8(cx); - u16x8 = CreateAndBindSimdClass(cx, global, globalSimdObject, cx->names().uint16x8); - if (!u16x8) - return nullptr; - - RootedObject u32x4(cx); - u32x4 = CreateAndBindSimdClass(cx, global, globalSimdObject, cx->names().uint32x4); - if (!u32x4) - return nullptr; - - RootedObject f32x4(cx); - f32x4 = CreateAndBindSimdClass(cx, global, globalSimdObject, cx->names().float32x4); - if (!f32x4) - return nullptr; - - RootedObject f64x2(cx); - f64x2 = CreateAndBindSimdClass(cx, global, globalSimdObject, cx->names().float64x2); - if (!f64x2) - return nullptr; - - RootedObject b8x16(cx); - b8x16 = CreateAndBindSimdClass(cx, global, globalSimdObject, cx->names().bool8x16); - if (!b8x16) - return nullptr; - - RootedObject b16x8(cx); - b16x8 = CreateAndBindSimdClass(cx, global, globalSimdObject, cx->names().bool16x8); - if (!b16x8) - return nullptr; - - RootedObject b32x4(cx); - b32x4 = CreateAndBindSimdClass(cx, global, globalSimdObject, cx->names().bool32x4); - if (!b32x4) - return nullptr; - - RootedObject b64x2(cx); - b64x2 = CreateAndBindSimdClass(cx, global, globalSimdObject, cx->names().bool64x2); - if (!b64x2) - return nullptr; - - return globalSimdObject; + return global->getOrCreateSimdGlobalObject(cx); } template @@ -561,15 +488,14 @@ js::CreateSimd(JSContext* cx, const typename V::Elem* data) return result; } -template JSObject* js::CreateSimd(JSContext* cx, const Int8x16::Elem* data); -template JSObject* js::CreateSimd(JSContext* cx, const Int16x8::Elem* data); -template JSObject* js::CreateSimd(JSContext* cx, const Int32x4::Elem* data); -template JSObject* js::CreateSimd(JSContext* cx, const Float32x4::Elem* data); -template JSObject* js::CreateSimd(JSContext* cx, const Float64x2::Elem* data); -template JSObject* js::CreateSimd(JSContext* cx, const Bool8x16::Elem* data); -template JSObject* js::CreateSimd(JSContext* cx, const Bool16x8::Elem* data); -template JSObject* js::CreateSimd(JSContext* cx, const Bool32x4::Elem* data); -template JSObject* js::CreateSimd(JSContext* cx, const Bool64x2::Elem* data); +#define InstantiateCreateSimd_(Type) \ + template JSObject* js::CreateSimd(JSContext* cx, const Type::Elem* data); + +FOR_EACH_SIMD(InstantiateCreateSimd_) + +#undef InstantiateCreateSimd_ + +#undef FOR_EACH_SIMD namespace js { // Unary SIMD operators diff --git a/js/src/builtin/SIMD.h b/js/src/builtin/SIMD.h index 0f7deaefdf..847c4f7b3f 100644 --- a/js/src/builtin/SIMD.h +++ b/js/src/builtin/SIMD.h @@ -784,6 +784,7 @@ class SIMDObject : public JSObject public: static const Class class_; static bool toString(JSContext* cx, unsigned int argc, Value* vp); + static bool resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId, bool* resolved); }; // These classes implement the concept containing the following constraints: diff --git a/js/src/builtin/SelfHostingDefines.h b/js/src/builtin/SelfHostingDefines.h index d3c4afb89f..db7ed70b79 100644 --- a/js/src/builtin/SelfHostingDefines.h +++ b/js/src/builtin/SelfHostingDefines.h @@ -15,6 +15,10 @@ #define IS_UINT32(x) ((x) >>> 0 === (x)) #define MAX_NUMERIC_INDEX 0x1fffffffffffff // == Math.pow(2, 53) - 1 +// Unforgeable version of Function.prototype.apply. +#define FUN_APPLY(FUN, RECEIVER, ARGS) \ + callFunction(std_Function_apply, FUN, RECEIVER, ARGS) + // Unforgeable versions of ARRAY.push(ELEMENT) and ARRAY.slice. #define ARRAY_PUSH(ARRAY, ELEMENT) \ callFunction(std_Array_push, ARRAY, ELEMENT); @@ -39,6 +43,10 @@ // global. This slot is used only in debug build. #define HAS_SELFHOSTED_CANONICAL_NAME_SLOT 0 +// Stores the length for bound functions, so the .length property doesn't need +// to be resolved eagerly. +#define BOUND_FUN_LENGTH_SLOT 1 + // Stores the private WeakMap slot used for WeakSets #define WEAKSET_MAP_SLOT 0 diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index e212cd4b6e..3cb45407f8 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -231,7 +231,7 @@ const Class js::ScalarTypeDescr::class_ = { const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = { JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0), - {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"}, + {"array", {nullptr, nullptr}, 0, 0, "ArrayShorthand"}, {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"}, JS_FS_END }; @@ -2595,7 +2595,7 @@ js::GetFloat32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); Rooted global(cx, cx->global()); MOZ_ASSERT(global); - args.rval().setObject(*global->getOrCreateSimdTypeDescr(cx)); + args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); return true; } @@ -2605,7 +2605,7 @@ js::GetFloat64x2TypeDescr(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); Rooted global(cx, cx->global()); MOZ_ASSERT(global); - args.rval().setObject(*global->getOrCreateSimdTypeDescr(cx)); + args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); return true; } @@ -2615,7 +2615,7 @@ js::GetInt8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); Rooted global(cx, cx->global()); MOZ_ASSERT(global); - args.rval().setObject(*global->getOrCreateSimdTypeDescr(cx)); + args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); return true; } @@ -2625,7 +2625,7 @@ js::GetInt16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); Rooted global(cx, cx->global()); MOZ_ASSERT(global); - args.rval().setObject(*global->getOrCreateSimdTypeDescr(cx)); + args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); return true; } @@ -2635,7 +2635,7 @@ js::GetInt32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); Rooted global(cx, cx->global()); MOZ_ASSERT(global); - args.rval().setObject(*global->getOrCreateSimdTypeDescr(cx)); + args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); return true; } @@ -2645,7 +2645,7 @@ js::GetUint8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); Rooted global(cx, cx->global()); MOZ_ASSERT(global); - args.rval().setObject(*global->getOrCreateSimdTypeDescr(cx)); + args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); return true; } @@ -2655,7 +2655,7 @@ js::GetUint16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); Rooted global(cx, cx->global()); MOZ_ASSERT(global); - args.rval().setObject(*global->getOrCreateSimdTypeDescr(cx)); + args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); return true; } @@ -2665,7 +2665,7 @@ js::GetUint32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); Rooted global(cx, cx->global()); MOZ_ASSERT(global); - args.rval().setObject(*global->getOrCreateSimdTypeDescr(cx)); + args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); return true; } @@ -2675,7 +2675,7 @@ js::GetBool8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); Rooted global(cx, cx->global()); MOZ_ASSERT(global); - args.rval().setObject(*global->getOrCreateSimdTypeDescr(cx)); + args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); return true; } @@ -2685,7 +2685,7 @@ js::GetBool16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); Rooted global(cx, cx->global()); MOZ_ASSERT(global); - args.rval().setObject(*global->getOrCreateSimdTypeDescr(cx)); + args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); return true; } @@ -2695,7 +2695,7 @@ js::GetBool32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); Rooted global(cx, cx->global()); MOZ_ASSERT(global); - args.rval().setObject(*global->getOrCreateSimdTypeDescr(cx)); + args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); return true; } @@ -2705,7 +2705,7 @@ js::GetBool64x2TypeDescr(JSContext* cx, unsigned argc, Value* vp) CallArgs args = CallArgsFromVp(argc, vp); Rooted global(cx, cx->global()); MOZ_ASSERT(global); - args.rval().setObject(*global->getOrCreateSimdTypeDescr(cx)); + args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); return true; } diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 80976b0978..e024da19be 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -5549,7 +5549,7 @@ PostBarrierCallback(JSTracer* trc, JSString* key, void* data) UnbarrieredFieldInfoHash* table = reinterpret_cast(data); JSString* prior = key; - JS_CallUnbarrieredStringTracer(trc, &key, "CType fieldName"); + js::UnsafeTraceManuallyBarrieredEdge(trc, &key, "CType fieldName"); table->rekeyIfMoved(JS_ASSERT_STRING_IS_FLAT(prior), JS_ASSERT_STRING_IS_FLAT(key)); } diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index e715e46498..ae93ad7171 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -3973,9 +3973,9 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, VarEmitOption emitOptio } bool -BytecodeEmitter::emitIteratorNext(ParseNode* pn) +BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted) { - MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting, + MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting, ".next() iteration is prohibited in self-hosted code because it " "can run user-modifiable iteration code"); @@ -5514,7 +5514,7 @@ BytecodeEmitter::emitForInOrOfVariables(ParseNode* pn) } bool -BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn) +BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn, bool allowSelfHosted) { MOZ_ASSERT(type == StmtType::FOR_OF_LOOP || type == StmtType::SPREAD); #ifdef DEBUG @@ -5623,7 +5623,7 @@ BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn) if (!emitDupAt(2)) // ITER ARR I ITER return false; } - if (!emitIteratorNext(forHead)) // ... RESULT + if (!emitIteratorNext(forHead, allowSelfHosted)) // ... RESULT return false; if (!emit1(JSOP_DUP)) // ... RESULT RESULT return false; @@ -7193,12 +7193,15 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) return false; } - if (pn->getOp() != JSOP_CALL) { + JSOp callOp = pn->getOp(); + if (callOp != JSOP_CALL) { reportError(pn, JSMSG_NOT_CONSTRUCTOR, errorName); return false; } ParseNode* funNode = pn2->pn_next; + if (funNode->getKind() == PNK_NAME && funNode->name() == cx->names().std_Function_apply) + callOp = JSOP_FUNAPPLY; if (!emitTree(funNode)) return false; @@ -7222,10 +7225,10 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) emittingForInit = oldEmittingForInit; uint32_t argc = pn->pn_count - 3; - if (!emitCall(pn->getOp(), argc)) + if (!emitCall(callOp, argc)) return false; - checkTypeSet(pn->getOp()); + checkTypeSet(callOp); return true; } @@ -7269,6 +7272,18 @@ BytecodeEmitter::emitSelfHostedForceInterpreter(ParseNode* pn) return true; } +bool +BytecodeEmitter::emitSelfHostedAllowContentSpread(ParseNode* pn) +{ + if (pn->pn_count != 2) { + reportError(pn, JSMSG_MORE_ARGS_NEEDED, "allowContentSpread", "1", ""); + return false; + } + + // We're just here as a sentinel. Pass the value through directly. + return emitTree(pn->pn_head->pn_next); +} + bool BytecodeEmitter::emitCallOrNew(ParseNode* pn) { @@ -7317,6 +7332,8 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn) return emitSelfHostedResumeGenerator(pn); if (pn2->name() == cx->names().forceInterpreter) return emitSelfHostedForceInterpreter(pn); + if (pn2->name() == cx->names().allowContentSpread) + return emitSelfHostedAllowContentSpread(pn); // Fall through. } if (!emitNameOp(pn2, callop)) @@ -7932,9 +7949,9 @@ BytecodeEmitter::emitArrayComp(ParseNode* pn) } bool -BytecodeEmitter::emitSpread() +BytecodeEmitter::emitSpread(bool allowSelfHosted) { - return emitForOf(StmtType::SPREAD, nullptr); + return emitForOf(StmtType::SPREAD, nullptr, allowSelfHosted); } bool @@ -8024,11 +8041,25 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op) } if (!updateSourceCoordNotes(pn2->pn_pos.begin)) return false; + + bool allowSelfHostedSpread = false; if (pn2->isKind(PNK_ELISION)) { if (!emit1(JSOP_HOLE)) return false; } else { - ParseNode* expr = pn2->isKind(PNK_SPREAD) ? pn2->pn_kid : pn2; + ParseNode* expr; + if (pn2->isKind(PNK_SPREAD)) { + expr = pn2->pn_kid; + + if (emitterMode == BytecodeEmitter::SelfHosting && + expr->isKind(PNK_CALL) && + expr->pn_head->name() == cx->names().allowContentSpread) + { + allowSelfHostedSpread = true; + } + } else { + expr = pn2; + } if (!emitTree(expr)) // ARRAY INDEX? VALUE return false; } @@ -8039,7 +8070,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op) return false; if (!emit2(JSOP_PICK, 2)) // ITER ARRAY INDEX return false; - if (!emitSpread()) // ARRAY INDEX + if (!emitSpread(allowSelfHostedSpread)) // ARRAY INDEX return false; } else if (afterSpread) { if (!emit1(JSOP_INITELEM_INC)) diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index bf9e9b89bb..c21d3c1337 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -575,7 +575,7 @@ struct BytecodeEmitter // Pops iterator from the top of the stack. Pushes the result of |.next()| // onto the stack. - bool emitIteratorNext(ParseNode* pn); + bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false); // Check if the value on top of the stack is "undefined". If so, replace // that value on the stack with the value defined by |defaultExpr|. @@ -612,6 +612,7 @@ struct BytecodeEmitter bool emitSelfHostedCallFunction(ParseNode* pn); bool emitSelfHostedResumeGenerator(ParseNode* pn); bool emitSelfHostedForceInterpreter(ParseNode* pn); + bool emitSelfHostedAllowContentSpread(ParseNode* pn); bool emitComprehensionFor(ParseNode* compFor); bool emitComprehensionForIn(ParseNode* pn); @@ -641,7 +642,7 @@ struct BytecodeEmitter // |.next()| and put the results into the I-th element of array with // incrementing I, then push the result I (it will be original I + // iteration count). The stack after iteration will look like |ARRAY INDEX|. - bool emitSpread(); + bool emitSpread(bool allowSelfHosted = false); // If type is StmtType::FOR_OF_LOOP, emit bytecode for a for-of loop. // pn should be PNK_FOR, and pn->pn_left should be PNK_FOROF. @@ -651,7 +652,7 @@ struct BytecodeEmitter // // Please refer the comment above emitSpread for additional information about // stack convention. - bool emitForOf(StmtType type, ParseNode* pn); + bool emitForOf(StmtType type, ParseNode* pn, bool allowSelfHosted = false); bool emitClass(ParseNode* pn); bool emitSuperPropLHS(ParseNode* superBase, bool isCall = false); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index f92d37ee97..dd50cc06b9 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -305,8 +305,6 @@ ParseContext::define(TokenStream& ts, if (!checkLocalsOverflow(ts)) return false; } - if (atModuleScope()) - dn->pn_dflags |= PND_CLOSED; if (!decls_.addUnique(name, dn)) return false; break; @@ -316,8 +314,6 @@ ParseContext::define(TokenStream& ts, // See FullParseHandler::setLexicalDeclarationOp. dn->setOp(dn->pn_scopecoord.isFree() ? JSOP_INITGLEXICAL : JSOP_INITLEXICAL); dn->pn_dflags |= (PND_LEXICAL | PND_BOUND); - if (atModuleLevel()) - dn->pn_dflags |= PND_CLOSED; if (atBodyLevel()) { if (!bodyLevelLexicals_.append(dn)) return false; @@ -335,7 +331,7 @@ ParseContext::define(TokenStream& ts, break; case Definition::IMPORT: - dn->pn_dflags |= PND_LEXICAL | PND_CLOSED; + dn->pn_dflags |= PND_LEXICAL; MOZ_ASSERT(atBodyLevel()); if (!decls_.addShadow(name, dn)) return false; @@ -948,9 +944,9 @@ Parser::checkStrictBinding(PropertyName* name, Node pn) return true; } -template -typename ParseHandler::Node -Parser::standaloneModule(HandleModuleObject module, ModuleBuilder& builder) +template <> +ParseNode* +Parser::standaloneModule(HandleModuleObject module, ModuleBuilder& builder) { MOZ_ASSERT(checkOptionsCalled); @@ -984,6 +980,28 @@ Parser::standaloneModule(HandleModuleObject module, ModuleBuilder& return null(); } + if (!builder.buildTables()) + return null(); + + // Check exported local bindings exist and mark them as closed over. + for (auto entry : modulebox->builder.localExportEntries()) { + JSAtom* name = entry->localName(); + MOZ_ASSERT(name); + + Definition* def = modulepc.decls().lookupFirst(name); + if (!def) { + JSAutoByteString str; + if (!str.encodeLatin1(context, name)) + return null(); + + JS_ReportErrorNumber(context->asJSContext(), GetErrorMessage, nullptr, + JSMSG_MISSING_EXPORT, str.ptr()); + return null(); + } + + def->pn_dflags |= PND_CLOSED; + } + if (!FoldConstants(context, &pn, this)) return null(); @@ -2825,6 +2843,10 @@ Parser::functionArgsAndBody(InHandling inHandling, ParseNode* { ParseContext* outerpc = pc; + // Close over top level function definitions in modules. + if (pc->sc->isModuleBox()) + pn->pn_dflags |= PND_CLOSED; + // Create box for fun->object early to protect against last-ditch GC. FunctionBox* funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind); if (!funbox) @@ -4898,7 +4920,7 @@ Parser::namedImportsOrNamespaceImport(TokenKind tt, Node impor // const variables which are initialized during the // ModuleDeclarationInstantiation step. Thus we don't set the PND_IMPORT // flag on the definition. - bindingName->pn_dflags |= PND_CONST; + bindingName->pn_dflags |= PND_CONST | PND_CLOSED; BindData data(context); data.initLexical(HoistVars, JSOP_DEFLET, nullptr, JSMSG_TOO_MANY_LOCALS); handler.setPosition(bindingName, pos()); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 207c93eab5..b6875cabd6 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -331,11 +331,6 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext return atBodyLevel() && sc->isModuleBox(); } - // True if the current lexical scope is the topmost level of a module. - bool atModuleScope() { - return sc->isModuleBox() && !innermostScopeStmt(); - } - // True if this is the ParseContext for the body of a function created by // the Function constructor. bool isFunctionConstructorBody() const { diff --git a/js/src/gc/Barrier.cpp b/js/src/gc/Barrier.cpp index 699bc3aec2..f2b007e65f 100644 --- a/js/src/gc/Barrier.cpp +++ b/js/src/gc/Barrier.cpp @@ -90,7 +90,7 @@ template void ReadBarrierFunctor::operator()(T* t) { - InternalGCMethods::readBarrier(t); + InternalBarrierMethods::readBarrier(t); } template void ReadBarrierFunctor::operator()(JS::Symbol*); template void ReadBarrierFunctor::operator()(JSObject*); @@ -101,7 +101,7 @@ template void PreBarrierFunctor::operator()(T* t) { - InternalGCMethods::preBarrier(t); + InternalBarrierMethods::preBarrier(t); } template void PreBarrierFunctor::operator()(JS::Symbol*); template void PreBarrierFunctor::operator()(JSObject*); @@ -170,12 +170,12 @@ JS_PUBLIC_API(void) JS::HeapObjectPostBarrier(JSObject** objp, JSObject* prev, JSObject* next) { MOZ_ASSERT(objp); - js::InternalGCMethods::postBarrier(objp, prev, next); + js::InternalBarrierMethods::postBarrier(objp, prev, next); } JS_PUBLIC_API(void) JS::HeapValuePostBarrier(JS::Value* valuep, const Value& prev, const Value& next) { MOZ_ASSERT(valuep); - js::InternalGCMethods::postBarrier(valuep, prev, next); + js::InternalBarrierMethods::postBarrier(valuep, prev, next); } diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index ce464218e7..1ee5c4bcfb 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -156,23 +156,23 @@ * via: * * WriteBarrieredBase::pre - * -> InternalGCMethods::preBarrier + * -> InternalBarrierMethods::preBarrier * -> T::writeBarrierPre - * -> InternalGCMethods::preBarrier - * -> InternalGCMethods::preBarrier - * -> InternalGCMethods::preBarrier + * -> InternalBarrierMethods::preBarrier + * -> InternalBarrierMethods::preBarrier + * -> InternalBarrierMethods::preBarrier * -> T::writeBarrierPre * * HeapPtr::post and RelocatablePtr::post - * -> InternalGCMethods::postBarrier + * -> InternalBarrierMethods::postBarrier * -> T::writeBarrierPost - * -> InternalGCMethods::postBarrier + * -> InternalBarrierMethods::postBarrier * -> StoreBuffer::put * * These classes are designed to be used by the internals of the JS engine. * Barriers designed to be used externally are provided in js/RootingAPI.h. * These external barriers call into the same post-barrier implementations at - * InternalGCMethods::post via an indirect call to Heap(.+)Barrier. + * InternalBarrierMethods::post via an indirect call to Heap(.+)Barrier. */ class JSAtom; @@ -240,10 +240,10 @@ void MarkIdForBarrier(JSTracer* trc, jsid* idp, const char* name); } // namespace gc template -struct InternalGCMethods {}; +struct InternalBarrierMethods {}; template -struct InternalGCMethods +struct InternalBarrierMethods { static bool isMarkable(T* v) { return v != nullptr; } @@ -265,7 +265,7 @@ template struct ReadBarrierFunctor : public VoidDefaultAdaptor { }; template <> -struct InternalGCMethods +struct InternalBarrierMethods { static bool isMarkable(Value v) { return v.isMarkable(); } static bool isMarkableTaggedPointer(Value v) { return isMarkable(v); } @@ -301,7 +301,7 @@ struct InternalGCMethods }; template <> -struct InternalGCMethods +struct InternalBarrierMethods { static bool isMarkable(jsid id) { return JSID_IS_STRING(id) || JSID_IS_SYMBOL(id); } static bool isMarkableTaggedPointer(jsid id) { return isMarkable(id); } @@ -368,11 +368,11 @@ class WriteBarrieredBase : public BarrieredBase void unsafeSet(T v) { this->value = v; } // For users who need to manually barrier the raw types. - static void writeBarrierPre(const T& v) { InternalGCMethods::preBarrier(v); } + static void writeBarrierPre(const T& v) { InternalBarrierMethods::preBarrier(v); } protected: - void pre() { InternalGCMethods::preBarrier(this->value); } - void post(T prev, T next) { InternalGCMethods::postBarrier(&this->value, prev, next); } + void pre() { InternalBarrierMethods::preBarrier(this->value); } + void post(T prev, T next) { InternalBarrierMethods::postBarrier(&this->value, prev, next); } }; /* @@ -385,7 +385,7 @@ template class PreBarriered : public WriteBarrieredBase { public: - PreBarriered() : WriteBarrieredBase(GCMethods::initial()) {} + PreBarriered() : WriteBarrieredBase(GCPolicy::initial()) {} /* * Allow implicit construction for use in generic contexts, such as DebuggerWeakMap::markKeys. */ @@ -430,12 +430,12 @@ template class HeapPtr : public WriteBarrieredBase { public: - HeapPtr() : WriteBarrieredBase(GCMethods::initial()) {} + HeapPtr() : WriteBarrieredBase(GCPolicy::initial()) {} explicit HeapPtr(T v) : WriteBarrieredBase(v) { - this->post(GCMethods::initial(), v); + this->post(GCPolicy::initial(), v); } explicit HeapPtr(const HeapPtr& v) : WriteBarrieredBase(v) { - this->post(GCMethods::initial(), v); + this->post(GCPolicy::initial(), v); } #ifdef DEBUG ~HeapPtr() { @@ -447,7 +447,7 @@ class HeapPtr : public WriteBarrieredBase void init(T v) { this->value = v; - this->post(GCMethods::initial(), v); + this->post(GCPolicy::initial(), v); } DECLARE_POINTER_ASSIGN_OPS(HeapPtr, T); @@ -482,11 +482,11 @@ template class RelocatablePtr : public WriteBarrieredBase { public: - RelocatablePtr() : WriteBarrieredBase(GCMethods::initial()) {} + RelocatablePtr() : WriteBarrieredBase(GCPolicy::initial()) {} // Implicitly adding barriers is a reasonable default. MOZ_IMPLICIT RelocatablePtr(const T& v) : WriteBarrieredBase(v) { - this->post(GCMethods::initial(), this->value); + this->post(GCPolicy::initial(), this->value); } /* @@ -496,17 +496,17 @@ class RelocatablePtr : public WriteBarrieredBase * simply omit the rvalue variant. */ MOZ_IMPLICIT RelocatablePtr(const RelocatablePtr& v) : WriteBarrieredBase(v) { - this->post(GCMethods::initial(), this->value); + this->post(GCPolicy::initial(), this->value); } ~RelocatablePtr() { this->pre(); - this->post(this->value, GCMethods::initial()); + this->post(this->value, GCPolicy::initial()); } void init(T v) { this->value = v; - this->post(GCMethods::initial(), this->value); + this->post(GCPolicy::initial(), this->value); } DECLARE_POINTER_ASSIGN_OPS(RelocatablePtr, T); @@ -540,8 +540,8 @@ class ReadBarrieredBase : public BarrieredBase explicit ReadBarrieredBase(T v) : BarrieredBase(v) {} protected: - void read() const { InternalGCMethods::readBarrier(this->value); } - void post(T prev, T next) { InternalGCMethods::postBarrier(&this->value, prev, next); } + void read() const { InternalBarrierMethods::readBarrier(this->value); } + void post(T prev, T next) { InternalBarrierMethods::postBarrier(&this->value, prev, next); } }; // Incremental GC requires that weak pointers have read barriers. This is mostly @@ -561,16 +561,16 @@ template class ReadBarriered : public ReadBarrieredBase { public: - ReadBarriered() : ReadBarrieredBase(GCMethods::initial()) {} + ReadBarriered() : ReadBarrieredBase(GCPolicy::initial()) {} // It is okay to add barriers implicitly. MOZ_IMPLICIT ReadBarriered(const T& v) : ReadBarrieredBase(v) { - this->post(GCMethods::initial(), v); + this->post(GCPolicy::initial(), v); } // Copy is creating a new edge, so we must read barrier the source edge. explicit ReadBarriered(const ReadBarriered& v) : ReadBarrieredBase(v) { - this->post(GCMethods::initial(), v.get()); + this->post(GCPolicy::initial(), v.get()); } // Move retains the lifetime status of the source edge, so does not fire @@ -578,11 +578,11 @@ class ReadBarriered : public ReadBarrieredBase ReadBarriered(ReadBarriered&& v) : ReadBarrieredBase(mozilla::Forward>(v)) { - this->post(GCMethods::initial(), v.value); + this->post(GCPolicy::initial(), v.value); } ~ReadBarriered() { - this->post(this->value, GCMethods::initial()); + this->post(this->value, GCPolicy::initial()); } ReadBarriered& operator=(const ReadBarriered& v) { @@ -593,8 +593,8 @@ class ReadBarriered : public ReadBarrieredBase } const T get() const { - if (!InternalGCMethods::isMarkable(this->value)) - return GCMethods::initial(); + if (!InternalBarrierMethods::isMarkable(this->value)) + return GCPolicy::initial(); this->read(); return this->value; } diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 3a6241c731..8b6bd4f761 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -413,16 +413,19 @@ template JS_PUBLIC_API(void) JS::TraceEdge(JSTracer* trc, JS::Heap* thingp, const char* name) { - DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name); + MOZ_ASSERT(thingp); + if (InternalBarrierMethods::isMarkable(thingp->get())) + DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name); } -template JS_PUBLIC_API(void) -JS::TraceNullableEdge(JSTracer* trc, JS::Heap* thingp, const char* name) +JS::TraceEdge(JSTracer* trc, JS::TenuredHeap* thingp, const char* name) { MOZ_ASSERT(thingp); - if (InternalGCMethods::isMarkable(thingp->get())) - DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name); + if (JSObject* ptr = thingp->getPtr()) { + DispatchToTracer(trc, &ptr, name); + thingp->setPtr(ptr); + } } template @@ -432,6 +435,13 @@ js::TraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name) DispatchToTracer(trc, ConvertToBase(thingp), name); } +template +JS_PUBLIC_API(void) +js::UnsafeTraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name) +{ + DispatchToTracer(trc, ConvertToBase(thingp), name); +} + template void js::TraceWeakEdge(JSTracer* trc, WeakRef* thingp, const char* name) @@ -464,7 +474,7 @@ void js::TraceNullableRoot(JSTracer* trc, T* thingp, const char* name) { AssertRootMarkingPhase(trc); - if (InternalGCMethods::isMarkableTaggedPointer(*thingp)) + if (InternalBarrierMethods::isMarkableTaggedPointer(*thingp)) DispatchToTracer(trc, ConvertToBase(thingp), name); } @@ -475,13 +485,21 @@ js::TraceNullableRoot(JSTracer* trc, ReadBarriered* thingp, const char* name) TraceNullableRoot(trc, thingp->unsafeGet(), name); } +template +JS_PUBLIC_API(void) +JS::UnsafeTraceRoot(JSTracer* trc, T* thingp, const char* name) +{ + MOZ_ASSERT(thingp); + js::TraceNullableRoot(trc, thingp, name); +} + template void js::TraceRange(JSTracer* trc, size_t len, WriteBarrieredBase* vec, const char* name) { JS::AutoTracingIndex index(trc); for (auto i : MakeRange(len)) { - if (InternalGCMethods::isMarkable(vec[i].get())) + if (InternalBarrierMethods::isMarkable(vec[i].get())) DispatchToTracer(trc, ConvertToBase(vec[i].unsafeUnbarrieredForTracing()), name); ++index; } @@ -494,7 +512,7 @@ js::TraceRootRange(JSTracer* trc, size_t len, T* vec, const char* name) AssertRootMarkingPhase(trc); JS::AutoTracingIndex index(trc); for (auto i : MakeRange(len)) { - if (InternalGCMethods::isMarkable(vec[i])) + if (InternalBarrierMethods::isMarkable(vec[i])) DispatchToTracer(trc, ConvertToBase(&vec[i]), name); ++index; } @@ -516,8 +534,9 @@ FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS) #define INSTANTIATE_PUBLIC_TRACE_FUNCTIONS(type) \ template JS_PUBLIC_API(void) JS::TraceEdge(JSTracer*, JS::Heap*, const char*); \ - template JS_PUBLIC_API(void) JS::TraceNullableEdge(JSTracer*, JS::Heap*, \ - const char*); + template JS_PUBLIC_API(void) JS::UnsafeTraceRoot(JSTracer*, type*, const char*); \ + template JS_PUBLIC_API(void) js::UnsafeTraceManuallyBarrieredEdge(JSTracer*, type*, \ + const char*); FOR_EACH_PUBLIC_GC_POINTER_TYPE(INSTANTIATE_PUBLIC_TRACE_FUNCTIONS) #undef INSTANTIATE_PUBLIC_TRACE_FUNCTIONS diff --git a/js/src/gc/Tracer.cpp b/js/src/gc/Tracer.cpp index d9236a7ec8..eef163f4f4 100644 --- a/js/src/gc/Tracer.cpp +++ b/js/src/gc/Tracer.cpp @@ -105,48 +105,6 @@ JS::CallbackTracer::getTracingEdgeName(char* buffer, size_t bufferSize) /*** Public Tracing API **************************************************************************/ -JS_PUBLIC_API(void) -JS_CallUnbarrieredValueTracer(JSTracer* trc, Value* valuep, const char* name) -{ - TraceManuallyBarrieredEdge(trc, valuep, name); -} - -JS_PUBLIC_API(void) -JS_CallUnbarrieredIdTracer(JSTracer* trc, jsid* idp, const char* name) -{ - TraceManuallyBarrieredEdge(trc, idp, name); -} - -JS_PUBLIC_API(void) -JS_CallUnbarrieredObjectTracer(JSTracer* trc, JSObject** objp, const char* name) -{ - TraceManuallyBarrieredEdge(trc, objp, name); -} - -JS_PUBLIC_API(void) -JS_CallUnbarrieredStringTracer(JSTracer* trc, JSString** strp, const char* name) -{ - TraceManuallyBarrieredEdge(trc, strp, name); -} - -JS_PUBLIC_API(void) -JS_CallUnbarrieredScriptTracer(JSTracer* trc, JSScript** scriptp, const char* name) -{ - TraceManuallyBarrieredEdge(trc, scriptp, name); -} - -JS_PUBLIC_API(void) -JS_CallTenuredObjectTracer(JSTracer* trc, JS::TenuredHeap* objp, const char* name) -{ - JSObject* obj = objp->getPtr(); - if (!obj) - return; - - TraceManuallyBarrieredEdge(trc, &obj, name); - - objp->setPtr(obj); -} - JS_PUBLIC_API(void) JS::TraceChildren(JSTracer* trc, GCCellPtr thing) { diff --git a/js/src/jit-test/modules/defaultClass.js b/js/src/jit-test/modules/defaultClass.js new file mode 100644 index 0000000000..c05d5ff5a0 --- /dev/null +++ b/js/src/jit-test/modules/defaultClass.js @@ -0,0 +1,5 @@ +export default class { + triple(x) { + return x * 3; + } +} diff --git a/js/src/jit-test/modules/defaultFunction.js b/js/src/jit-test/modules/defaultFunction.js new file mode 100644 index 0000000000..1c6e75d453 --- /dev/null +++ b/js/src/jit-test/modules/defaultFunction.js @@ -0,0 +1,3 @@ +export default function(x) { + return x * 2; +} diff --git a/js/src/jit-test/tests/heap-analysis/byteSize-of-object.js b/js/src/jit-test/tests/heap-analysis/byteSize-of-object.js index c9748fdcfd..3b667aa4ac 100644 --- a/js/src/jit-test/tests/heap-analysis/byteSize-of-object.js +++ b/js/src/jit-test/tests/heap-analysis/byteSize-of-object.js @@ -78,6 +78,6 @@ assertEq(tByteSize([1, 2, 3, 4, 5, 6, 7, 8]), s(112, 128)); // Various forms of functions. assertEq(tByteSize(function () {}), s(32, 64)); -assertEq(tByteSize(function () {}.bind()), s(96, 128)); +assertEq(tByteSize(function () {}.bind()), s(48, 80)); assertEq(tByteSize(() => 1), s(48, 80)); assertEq(tByteSize(Math.sin), s(32, 64)); diff --git a/js/src/jit-test/tests/modules/bug-1233117.js b/js/src/jit-test/tests/modules/bug-1233117.js new file mode 100644 index 0000000000..f313e1c4ac --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1233117.js @@ -0,0 +1,2 @@ +// |jit-test| module +eval("1"); diff --git a/js/src/jit-test/tests/modules/bug-1233915.js b/js/src/jit-test/tests/modules/bug-1233915.js new file mode 100644 index 0000000000..98370ab6f1 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1233915.js @@ -0,0 +1,10 @@ +g = newGlobal(); +g.parent = this; +g.eval("(" + function() { + Debugger(parent) + .onExceptionUnwind = function(frame) + frame.eval("") +} + ")()"); +m = parseModule(` s1 `); +m.declarationInstantiation(); +m.evaluation(); diff --git a/js/src/jit-test/tests/modules/debugger-vars-function.js b/js/src/jit-test/tests/modules/debugger-vars-function.js new file mode 100644 index 0000000000..75dc023749 --- /dev/null +++ b/js/src/jit-test/tests/modules/debugger-vars-function.js @@ -0,0 +1,37 @@ +// Test debugger access to aliased and unaliased bindings work correctly. + +load(libdir + "asserts.js"); + +var g = newGlobal(); +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + let env = frame.environment.parent; + assertEq(env.getVariable('a'), 1); + assertEq(env.getVariable('b'), 2); + assertEq(env.getVariable('c'), 3); + assertEq(env.getVariable('d'), 4); + assertEq(env.getVariable('e'), 5); +}; + +g.eval( +` + let moduleRepo = {}; + setModuleResolveHook(function(module, specifier) { + if (specifier in moduleRepo) + return moduleRepo[specifier]; + throw "Module '" + specifier + "' not found"; + }); + + let m = parseModule( + \` + var a = 1; + let b = 2; + export var c = 3; + export let d = 4; + let e = 5; + function f() { debugger; return e; } + \`); + m.declarationInstantiation(); + m.evaluation(); +`); + diff --git a/js/src/jit-test/tests/modules/debugger-vars-toplevel.js b/js/src/jit-test/tests/modules/debugger-vars-toplevel.js new file mode 100644 index 0000000000..8be8f4079a --- /dev/null +++ b/js/src/jit-test/tests/modules/debugger-vars-toplevel.js @@ -0,0 +1,38 @@ +// Test debugger access to aliased and unaliased bindings work correctly. + +load(libdir + "asserts.js"); + +var g = newGlobal(); +var dbg = Debugger(g); +dbg.onDebuggerStatement = function (frame) { + let env = frame.environment; + assertEq(env.getVariable('a'), 1); + assertEq(env.getVariable('b'), 2); + assertEq(env.getVariable('c'), 3); + assertEq(env.getVariable('d'), 4); + assertEq(env.getVariable('e'), 5); +}; + +g.eval( +` + let moduleRepo = {}; + setModuleResolveHook(function(module, specifier) { + if (specifier in moduleRepo) + return moduleRepo[specifier]; + throw "Module '" + specifier + "' not found"; + }); + + let m = parseModule( + \` + var a = 1; + let b = 2; + export var c = 3; + export let d = 4; + let e = 5; + function f() { return e; } + debugger; + \`); + m.declarationInstantiation(); + m.evaluation(); +`); + diff --git a/js/src/jit-test/tests/modules/export-declaration.js b/js/src/jit-test/tests/modules/export-declaration.js index a3c931eb6c..3c4a9b7355 100644 --- a/js/src/jit-test/tests/modules/export-declaration.js +++ b/js/src/jit-test/tests/modules/export-declaration.js @@ -78,6 +78,12 @@ program([ ]).assert(parseAsModule("export {}")); program([ + letDeclaration([ + { + id: ident("a"), + init: lit(1) + } + ]), exportDeclaration( null, [ @@ -89,9 +95,15 @@ program([ null, false ) -]).assert(parseAsModule("export { a }")); +]).assert(parseAsModule("let a = 1; export { a }")); program([ + letDeclaration([ + { + id: ident("a"), + init: lit(1) + } + ]), exportDeclaration( null, [ @@ -103,9 +115,15 @@ program([ null, false ) -]).assert(parseAsModule("export { a as b }")); +]).assert(parseAsModule("let a = 1; export { a as b }")); program([ + letDeclaration([ + { + id: ident("as"), + init: lit(1) + } + ]), exportDeclaration( null, [ @@ -117,9 +135,15 @@ program([ null, false ) -]).assert(parseAsModule("export { as as as }")); +]).assert(parseAsModule("let as = 1; export { as as as }")); program([ + letDeclaration([ + { + id: ident("a"), + init: lit(1) + } + ]), exportDeclaration( null, [ @@ -131,9 +155,19 @@ program([ null, false ) -]).assert(parseAsModule("export { a as true }")); +]).assert(parseAsModule("let a = 1; export { a as true }")); program([ + letDeclaration([ + { + id: ident("a"), + init: lit(1) + }, + { + id: ident("b"), + init: lit(2) + } + ]), exportDeclaration( null, [ @@ -149,9 +183,19 @@ program([ null, false ) -]).assert(parseAsModule("export { a, b }")); +]).assert(parseAsModule("let a = 1, b = 2; export { a, b }")); program([ + letDeclaration([ + { + id: ident("a"), + init: lit(1) + }, + { + id: ident("c"), + init: lit(2) + } + ]), exportDeclaration( null, [ @@ -167,7 +211,7 @@ program([ null, false ) -]).assert(parseAsModule("export { a as b, c as d }")); +]).assert(parseAsModule("let a = 1, c = 2; export { a as b, c as d }")); program([ exportDeclaration( diff --git a/js/src/jit-test/tests/modules/import-default-class.js b/js/src/jit-test/tests/modules/import-default-class.js new file mode 100644 index 0000000000..ff87649ac6 --- /dev/null +++ b/js/src/jit-test/tests/modules/import-default-class.js @@ -0,0 +1,5 @@ +// |jit-test| module + +import c from "defaultClass.js"; +let o = new c(); +assertEq(o.triple(14), 42); diff --git a/js/src/jit-test/tests/modules/import-default-function.js b/js/src/jit-test/tests/modules/import-default-function.js new file mode 100644 index 0000000000..95e962448f --- /dev/null +++ b/js/src/jit-test/tests/modules/import-default-function.js @@ -0,0 +1,4 @@ +// |jit-test| module + +import f from "defaultFunction.js"; +assertEq(f(21), 42); diff --git a/js/src/jit-test/tests/modules/import-namespace.js b/js/src/jit-test/tests/modules/import-namespace.js index b6410b060f..cc4eb9c86b 100644 --- a/js/src/jit-test/tests/modules/import-namespace.js +++ b/js/src/jit-test/tests/modules/import-namespace.js @@ -25,8 +25,17 @@ function testHasNames(names, expected) { }); } -let a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;"); -let b = moduleRepo['b'] = parseModule("import * as ns from 'a'; var x = ns.a + ns.b;"); +let a = moduleRepo['a'] = parseModule( + `export var a = 1; + export var b = 2;` +); + +let b = moduleRepo['b'] = parseModule( + `import * as ns from 'a'; + export { ns }; + export var x = ns.a + ns.b;` +); + b.declarationInstantiation(); b.evaluation(); testHasNames(getModuleEnvironmentNames(b), ["ns", "x"]); diff --git a/js/src/jit-test/tests/saved-stacks/async-max-frame-count.js b/js/src/jit-test/tests/saved-stacks/async-max-frame-count.js index db531f818e..ebcbe13d31 100644 --- a/js/src/jit-test/tests/saved-stacks/async-max-frame-count.js +++ b/js/src/jit-test/tests/saved-stacks/async-max-frame-count.js @@ -4,7 +4,7 @@ const defaultAsyncStackLimit = 60; function recur(n, limit) { if (n > 0) { - return callFunctionWithAsyncStack(recur.bind(undefined, n - 1, limit), + return callFunctionWithAsyncStack(function recur() {return recur(n - 1, limit)}, saveStack(limit), "Recurse"); } return saveStack(limit); diff --git a/js/src/jit-test/tests/saved-stacks/async-principals.js b/js/src/jit-test/tests/saved-stacks/async-principals.js index 75eced08de..09b8b5ce3d 100644 --- a/js/src/jit-test/tests/saved-stacks/async-principals.js +++ b/js/src/jit-test/tests/saved-stacks/async-principals.js @@ -141,13 +141,15 @@ function v() { { name: "u", asyncCause: "UtoV" }, ]); - let xToCall = x.bind(undefined, saveStack(0, low)); + let stack = saveStack(0, low); + function xToCall() { return x(stack);}; let stackX = callFunctionWithAsyncStack(xToCall, saveStack(), "VtoX"); print("high.checkVisibleStack(stackX)"); checkVisibleStack(stackX, [ { name: "x", asyncCause: null }, + { name: "xToCall", asyncCause: null }, { name: "v", asyncCause: "VtoX" }, { name: "u", asyncCause: "UtoV" }, ]); @@ -187,6 +189,7 @@ function x(stackV) { return saveStack(0, high); } + function y() { let stackY = saveStack(); diff --git a/js/src/jit/Bailouts.cpp b/js/src/jit/Bailouts.cpp index cf8f92446a..30c1bf6e8b 100644 --- a/js/src/jit/Bailouts.cpp +++ b/js/src/jit/Bailouts.cpp @@ -260,8 +260,11 @@ jit::ExceptionHandlerBailout(JSContext* cx, const InlineFrameIterator& frame, bool jit::EnsureHasScopeObjects(JSContext* cx, AbstractFramePtr fp) { + // Ion does not compile eval scripts. + MOZ_ASSERT(!fp.isEvalFrame()); + if (fp.isFunctionFrame() && - fp.fun()->needsCallObject() && + fp.callee()->needsCallObject() && !fp.hasCallObj()) { return fp.initFunctionScopeObjects(cx); diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp index c26d345530..040aca7dc7 100644 --- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -1721,7 +1721,7 @@ CopyFromRematerializedFrame(JSContext* cx, JitActivation* act, uint8_t* fp, size frame->setScopeChain(rematFrame->scopeChain()); - if (frame->isNonEvalFunctionFrame()) + if (frame->isFunctionFrame()) frame->thisArgument() = rematFrame->thisArgument(); for (unsigned i = 0; i < frame->numActualArgs(); i++) diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 4a2ad63990..2d56284054 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -356,13 +356,7 @@ BaselineCompiler::emitPrologue() // is passed in R1, so we have to be careful not to clobber it. // Initialize BaselineFrame::flags. - uint32_t flags = 0; - if (script->isForEval()) - flags |= BaselineFrame::EVAL; - masm.store32(Imm32(flags), frame.addressOfFlags()); - - if (script->isForEval()) - masm.storePtr(ImmGCPtr(script), frame.addressOfEvalScript()); + masm.store32(Imm32(0), frame.addressOfFlags()); // Handle scope chain pre-initialization (in case GC gets run // during stack check). For global and eval scripts, the scope diff --git a/js/src/jit/BaselineFrame-inl.h b/js/src/jit/BaselineFrame-inl.h index 34f812bf4e..3cb640f928 100644 --- a/js/src/jit/BaselineFrame-inl.h +++ b/js/src/jit/BaselineFrame-inl.h @@ -89,7 +89,7 @@ inline CallObject& BaselineFrame::callObj() const { MOZ_ASSERT(hasCallObj()); - MOZ_ASSERT(fun()->needsCallObject()); + MOZ_ASSERT(callee()->needsCallObject()); JSObject* obj = scopeChain(); while (!obj->is()) diff --git a/js/src/jit/BaselineFrame.cpp b/js/src/jit/BaselineFrame.cpp index 9dbbda0cdf..c2d8f6355b 100644 --- a/js/src/jit/BaselineFrame.cpp +++ b/js/src/jit/BaselineFrame.cpp @@ -33,7 +33,7 @@ BaselineFrame::trace(JSTracer* trc, JitFrameIterator& frameIterator) replaceCalleeToken(MarkCalleeToken(trc, calleeToken())); // Mark |this|, actual and formal args. - if (isNonEvalFunctionFrame()) { + if (isFunctionFrame()) { TraceRoot(trc, &thisArgument(), "baseline-this"); unsigned numArgs = js::Max(numActualArgs(), numFormalArgs()); @@ -48,11 +48,8 @@ BaselineFrame::trace(JSTracer* trc, JitFrameIterator& frameIterator) if (hasReturnValue()) TraceRoot(trc, returnValue().address(), "baseline-rval"); - if (isEvalFrame()) { - TraceRoot(trc, &evalScript_, "baseline-evalscript"); - if (isFunctionFrame()) - TraceRoot(trc, evalNewTargetAddress(), "baseline-evalNewTarget"); - } + if (isEvalFrame() && script()->isDirectEvalInFunction()) + TraceRoot(trc, evalNewTargetAddress(), "baseline-evalNewTarget"); if (hasArgsObj()) TraceRoot(trc, &argsObj_, "baseline-args-obj"); @@ -126,8 +123,8 @@ BaselineFrame::initStrictEvalScopeObjects(JSContext* cx) bool BaselineFrame::initFunctionScopeObjects(JSContext* cx) { - MOZ_ASSERT(isNonEvalFunctionFrame()); - MOZ_ASSERT(fun()->needsCallObject()); + MOZ_ASSERT(isFunctionFrame()); + MOZ_ASSERT(callee()->needsCallObject()); CallObject* callobj = CallObject::createForFunction(cx, this); if (!callobj) @@ -148,11 +145,6 @@ BaselineFrame::initForOsr(InterpreterFrame* fp, uint32_t numStackValues) if (fp->hasCallObjUnchecked()) flags_ |= BaselineFrame::HAS_CALL_OBJ; - if (fp->isEvalFrame()) { - flags_ |= BaselineFrame::EVAL; - evalScript_ = fp->script(); - } - if (fp->script()->needsArgsObj() && fp->hasArgsObj()) { flags_ |= BaselineFrame::HAS_ARGS_OBJ; argsObj_ = &fp->argsObj(); diff --git a/js/src/jit/BaselineFrame.h b/js/src/jit/BaselineFrame.h index ffaa29916d..ff5378a7b7 100644 --- a/js/src/jit/BaselineFrame.h +++ b/js/src/jit/BaselineFrame.h @@ -24,13 +24,6 @@ struct BaselineDebugModeOSRInfo; // locals // stack values -// Eval frames -// -// Like js::InterpreterFrame, every BaselineFrame is either a global frame -// or a function frame. Both global and function frames can optionally -// be "eval frames". The callee token for eval function frames is the -// enclosing function. BaselineFrame::evalScript_ stores the eval script -// itself. class BaselineFrame { public: @@ -53,8 +46,7 @@ class BaselineFrame // invariants of debuggee compartments, scripts, and frames. DEBUGGEE = 1 << 6, - // Eval frame, see the "eval frames" comment. - EVAL = 1 << 7, + // (1 << 7 and 1 << 8 are unused) // Frame has over-recursed on an early check. OVER_RECURSED = 1 << 9, @@ -100,9 +92,7 @@ class BaselineFrame uint32_t hiReturnValue_; uint32_t frameSize_; JSObject* scopeChain_; // Scope chain (always initialized). - JSScript* evalScript_; // If isEvalFrame(), the current eval script. ArgumentsObject* argsObj_; // If HAS_ARGS_OBJ, the arguments object. - void* unused; // See static assertion re: sizeof, below. uint32_t overrideOffset_; // If HAS_OVERRIDE_PC, the bytecode offset. uint32_t flags_; @@ -154,16 +144,8 @@ class BaselineFrame return CalleeTokenIsConstructing(calleeToken()); } JSScript* script() const { - if (isEvalFrame()) - return evalScript(); return ScriptFromCalleeToken(calleeToken()); } - JSFunction* fun() const { - return CalleeTokenToFunction(calleeToken()); - } - JSFunction* maybeFun() const { - return isFunctionFrame() ? fun() : nullptr; - } JSFunction* callee() const { return CalleeTokenToFunction(calleeToken()); } @@ -212,7 +194,7 @@ class BaselineFrame return script()->functionNonDelazifying()->nargs(); } Value& thisArgument() const { - MOZ_ASSERT(isNonEvalFunctionFrame()); + MOZ_ASSERT(isFunctionFrame()); return *(Value*)(reinterpret_cast(this) + BaselineFrame::Size() + offsetOfThis()); @@ -226,7 +208,7 @@ class BaselineFrame private: Value* evalNewTargetAddress() const { MOZ_ASSERT(isEvalFrame()); - MOZ_ASSERT(isFunctionFrame()); + MOZ_ASSERT(script()->isDirectEvalInFunction()); return (Value*)(reinterpret_cast(this) + BaselineFrame::Size() + offsetOfEvalNewTarget()); @@ -234,15 +216,16 @@ class BaselineFrame public: Value newTarget() const { - MOZ_ASSERT(isFunctionFrame()); if (isEvalFrame()) return *evalNewTargetAddress(); - if (fun()->isArrow()) - return fun()->getExtendedSlot(FunctionExtended::ARROW_NEWTARGET_SLOT); - if (isConstructing()) + MOZ_ASSERT(isFunctionFrame()); + if (callee()->isArrow()) + return callee()->getExtendedSlot(FunctionExtended::ARROW_NEWTARGET_SLOT); + if (isConstructing()) { return *(Value*)(reinterpret_cast(this) + BaselineFrame::Size() + offsetOfArg(Max(numFormalArgs(), numActualArgs()))); + } return UndefinedValue(); } @@ -336,11 +319,6 @@ class BaselineFrame flags_ |= HAS_CACHED_SAVED_FRAME; } - JSScript* evalScript() const { - MOZ_ASSERT(isEvalFrame()); - return evalScript_; - } - bool overRecursed() const { return flags_ & OVER_RECURSED; } @@ -394,17 +372,14 @@ class BaselineFrame void trace(JSTracer* trc, JitFrameIterator& frame); - bool isFunctionFrame() const { - return CalleeTokenIsFunction(calleeToken()); + bool isGlobalFrame() const { + return script()->isGlobalCode(); } bool isModuleFrame() const { - return CalleeTokenIsModuleScript(calleeToken()); + return script()->module(); } - bool isGlobalFrame() const { - return !isFunctionFrame() && !isModuleFrame(); - } - bool isEvalFrame() const { - return flags_ & EVAL; + bool isEvalFrame() const { + return script()->isForEval(); } bool isStrictEvalFrame() const { return isEvalFrame() && script()->strict(); @@ -416,8 +391,8 @@ class BaselineFrame bool isNonStrictDirectEvalFrame() const { return isNonStrictEvalFrame() && isNonGlobalEvalFrame(); } - bool isNonEvalFunctionFrame() const { - return isFunctionFrame() && !isEvalFrame(); + bool isFunctionFrame() const { + return CalleeTokenIsFunction(calleeToken()); } bool isDebuggerEvalFrame() const { return false; @@ -466,9 +441,6 @@ class BaselineFrame static int reverseOffsetOfFlags() { return -int(Size()) + offsetof(BaselineFrame, flags_); } - static int reverseOffsetOfEvalScript() { - return -int(Size()) + offsetof(BaselineFrame, evalScript_); - } static int reverseOffsetOfReturnValue() { return -int(Size()) + offsetof(BaselineFrame, loReturnValue_); } diff --git a/js/src/jit/BaselineFrameInfo.h b/js/src/jit/BaselineFrameInfo.h index 18af6d6ab0..c7f2c23312 100644 --- a/js/src/jit/BaselineFrameInfo.h +++ b/js/src/jit/BaselineFrameInfo.h @@ -280,9 +280,6 @@ class FrameInfo Address addressOfFlags() const { return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()); } - Address addressOfEvalScript() const { - return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfEvalScript()); - } Address addressOfReturnValue() const { return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfReturnValue()); } diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 90078e2a51..52d9e4f484 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -5701,6 +5701,7 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args, } if (JitSupportsSimd()) { + RootedGlobalObject global(cx, cx->global()); #define ADD_INT32X4_SIMD_OP_NAME_(OP) || native == js::simd_int32x4_##OP #define ADD_BOOL32X4_SIMD_OP_NAME_(OP) || native == js::simd_bool32x4_##OP #define ADD_FLOAT32X4_SIMD_OP_NAME_(OP) || native == js::simd_float32x4_##OP @@ -5713,7 +5714,7 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args, ADD_INT32X4_SIMD_OP_NAME_(fromFloat32x4) ADD_INT32X4_SIMD_OP_NAME_(fromFloat32x4Bits)) { - Rooted descr(cx, cx->global()->getOrCreateSimdTypeDescr(cx)); + Rooted descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global)); res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr)); return !!res; } @@ -5724,7 +5725,7 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args, FOREACH_COMP_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_) FOREACH_COMP_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_)) { - Rooted descr(cx, cx->global()->getOrCreateSimdTypeDescr(cx)); + Rooted descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global)); res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr)); return !!res; } @@ -5736,7 +5737,7 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args, ADD_FLOAT32X4_SIMD_OP_NAME_(fromInt32x4Bits) ION_COMMONX4_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_)) { - Rooted descr(cx, cx->global()->getOrCreateSimdTypeDescr(cx)); + Rooted descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global)); res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr)); return !!res; } diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index b2fb0183ad..44f3242b39 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -84,7 +84,7 @@ CheckFrame(InterpreterFrame* fp) return false; } - if (fp->isNonEvalFunctionFrame() && fp->numActualArgs() > BASELINE_MAX_ARGS_LENGTH) { + if (fp->isFunctionFrame() && fp->numActualArgs() > BASELINE_MAX_ARGS_LENGTH) { // Fall back to the interpreter to avoid running out of stack space. JitSpew(JitSpew_BaselineAbort, "Too many arguments (%u)", fp->numActualArgs()); return false; @@ -201,7 +201,7 @@ jit::EnterBaselineAtBranch(JSContext* cx, InterpreterFrame* fp, jsbytecode* pc) AutoValueVector vals(cx); RootedValue thisv(cx); - if (fp->isNonEvalFunctionFrame()) { + if (fp->isFunctionFrame()) { data.constructing = fp->isConstructing(); data.numActualArgs = fp->numActualArgs(); data.maxArgc = Max(fp->numActualArgs(), fp->numFormalArgs()) + 1; // +1 = include |this| @@ -216,11 +216,7 @@ jit::EnterBaselineAtBranch(JSContext* cx, InterpreterFrame* fp, jsbytecode* pc) data.maxArgv = thisv.address(); data.scopeChain = fp->scopeChain(); - // For eval function frames, set the callee token to the enclosing function. - if (fp->isFunctionFrame()) - data.calleeToken = CalleeToToken(&fp->callee(), /* constructing = */ false); - else - data.calleeToken = CalleeToToken(fp->script()); + data.calleeToken = CalleeToToken(fp->script()); if (fp->isEvalFrame()) { if (!vals.reserve(2)) @@ -228,7 +224,7 @@ jit::EnterBaselineAtBranch(JSContext* cx, InterpreterFrame* fp, jsbytecode* pc) vals.infallibleAppend(thisv); - if (fp->isFunctionFrame()) + if (fp->script()->isDirectEvalInFunction()) vals.infallibleAppend(fp->newTarget()); else vals.infallibleAppend(NullValue()); @@ -371,9 +367,7 @@ jit::CanEnterBaselineMethod(JSContext* cx, RunState& state) return Method_Error; } } else { - MOZ_ASSERT(state.isExecute()); - ExecuteType type = state.asExecute()->type(); - if (type == EXECUTE_DEBUG) { + if (state.asExecute()->isDebuggerEval()) { JitSpew(JitSpew_BaselineAbort, "debugger frame"); return Method_CantCompile; } diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index e94b51d982..46a8a92d04 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1985,7 +1985,7 @@ CodeGenerator::visitLambda(LLambda* lir) emitLambdaInit(output, scopeChain, info); if (info.flags & JSFunction::EXTENDED) { - MOZ_ASSERT(info.fun->allowSuperProperty()); + MOZ_ASSERT(info.fun->allowSuperProperty() || info.fun->isSelfHostedBuiltin()); static_assert(FunctionExtended::NUM_EXTENDED_SLOTS == 2, "All slots must be initialized"); masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(0))); masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1))); diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index 2dfbeb3664..46c1ecb670 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -78,8 +78,6 @@ \ _(ObjectCreate) \ \ - _(CallBoundFunction) \ - \ _(SimdInt32x4) \ _(SimdFloat32x4) \ _(SimdBool32x4) \ diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 53c4f62914..f37af41330 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2307,6 +2307,7 @@ CheckFrame(JSContext* cx, BaselineFrame* frame) { MOZ_ASSERT(!frame->script()->isGenerator()); MOZ_ASSERT(!frame->isDebuggerEvalFrame()); + MOZ_ASSERT(!frame->isEvalFrame()); // This check is to not overrun the stack. if (frame->isFunctionFrame()) { @@ -2639,9 +2640,9 @@ MethodStatus jit::CompileFunctionForBaseline(JSContext* cx, HandleScript script, BaselineFrame* frame) { MOZ_ASSERT(jit::IsIonEnabled(cx)); - MOZ_ASSERT(frame->fun()->nonLazyScript()->canIonCompile()); - MOZ_ASSERT(!frame->fun()->nonLazyScript()->isIonCompilingOffThread()); - MOZ_ASSERT(!frame->fun()->nonLazyScript()->hasIonScript()); + MOZ_ASSERT(frame->callee()->nonLazyScript()->canIonCompile()); + MOZ_ASSERT(!frame->callee()->nonLazyScript()->isIonCompilingOffThread()); + MOZ_ASSERT(!frame->callee()->nonLazyScript()->hasIonScript()); MOZ_ASSERT(frame->isFunctionFrame()); // Mark as forbidden if frame can't be handled. @@ -2791,27 +2792,18 @@ jit::SetEnterJitData(JSContext* cx, EnterJitData& data, RunState& state, AutoVal data.calleeToken = CalleeToToken(state.script()); - if (state.script()->isForEval() && - !(state.asExecute()->type() & InterpreterFrame::GLOBAL)) - { - ScriptFrameIter iter(cx); - if (iter.isFunctionFrame()) - data.calleeToken = CalleeToToken(iter.callee(cx), /* constructing = */ false); - + if (state.script()->isForEval() && state.script()->isDirectEvalInFunction()) { // Push newTarget onto the stack. if (!vals.reserve(1)) return false; + ScriptFrameIter iter(cx); data.maxArgc = 1; data.maxArgv = vals.begin(); - if (iter.isFunctionFrame()) { - if (state.asExecute()->newTarget().isNull()) - vals.infallibleAppend(iter.newTarget()); - else - vals.infallibleAppend(state.asExecute()->newTarget()); - } else { - vals.infallibleAppend(NullValue()); - } + if (state.asExecute()->newTarget().isNull()) + vals.infallibleAppend(iter.newTarget()); + else + vals.infallibleAppend(state.asExecute()->newTarget()); } } diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 4c4b6fd39f..25a4444fa2 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -75,7 +75,7 @@ jit::NewBaselineFrameInspector(TempAllocator* temp, BaselineFrame* frame, Compil // during compilation could capture nursery pointers, so the values' types // are recorded instead. - if (frame->isNonEvalFunctionFrame()) + if (frame->isFunctionFrame()) inspector->thisType = TypeSet::GetMaybeUntrackedValueType(frame->thisArgument()); if (frame->scopeChain()->isSingleton()) @@ -6278,6 +6278,9 @@ IonBuilder::createThis(JSFunction* target, MDefinition* callee, MDefinition* new return magic; } + if (target->isBoundFunction()) + return constant(MagicValue(JS_UNINITIALIZED_LEXICAL)); + if (target->isDerivedClassConstructor()) { MOZ_ASSERT(target->isClassConstructor()); return constant(MagicValue(JS_UNINITIALIZED_LEXICAL)); @@ -8381,9 +8384,11 @@ IonBuilder::jsop_intrinsic(PropertyName* name) { TemporaryTypeSet* types = bytecodeTypes(pc); - // If we haven't executed this opcode yet, we need to get the intrinsic - // value and monitor the result. - if (types->empty()) { + Value vp = script()->global().maybeExistingIntrinsicValue(name); + + // If the intrinsic value doesn't yet exist, we haven't executed this + // opcode yet, so we need to get it and monitor the result. + if (vp.isUndefined()) { MCallGetIntrinsicValue* ins = MCallGetIntrinsicValue::New(alloc(), name); current->add(ins); @@ -8395,10 +8400,12 @@ IonBuilder::jsop_intrinsic(PropertyName* name) return pushTypeBarrier(ins, types, BarrierKind::TypeSet); } + if (types->empty()) + types->addType(TypeSet::GetValueType(vp), alloc().lifoAlloc()); + // Bake in the intrinsic, guaranteed to exist because a non-empty typeset // means the intrinsic was successfully gotten in the VM call above. // Assert that TI agrees with us on the type. - Value vp = script()->global().existingIntrinsicValue(name); MOZ_ASSERT(types->hasType(TypeSet::GetValueType(vp))); pushConstant(vp); diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index 0533bb0669..7f45ecb362 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -2966,15 +2966,7 @@ JitProfilingFrameIterator::JitProfilingFrameIterator( return; } - // In some rare cases (e.g. baseline eval frame), the callee script may - // not have a baselineScript. Treat this is an empty frame-sequence and - // move on. - if (!frameScript()->hasBaselineScript()) { - type_ = JitFrame_Entry; - fp_ = nullptr; - returnAddressToFp_ = nullptr; - return; - } + MOZ_ASSERT(frameScript()->hasBaselineScript()); // If nothing matches, for now just assume we are at the start of the last frame's // baseline jit code. diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 63a21cc0b1..9629d54d9d 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -199,10 +199,6 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) case InlinableNative::ObjectCreate: return inlineObjectCreate(callInfo); - // Bound function. - case InlinableNative::CallBoundFunction: - return inlineBoundFunction(callInfo, target); - // SIMD natives. case InlinableNative::SimdInt32x4: return inlineSimdInt32x4(callInfo, target->native()); @@ -2667,73 +2663,6 @@ IonBuilder::inlineAssertRecoveredOnBailout(CallInfo& callInfo) return InliningStatus_Inlined; } -IonBuilder::InliningStatus -IonBuilder::inlineBoundFunction(CallInfo& nativeCallInfo, JSFunction* target) -{ - trackOptimizationOutcome(TrackedOutcome::CantInlineBound); - - if (!target->getBoundFunctionTarget()->is()) - return InliningStatus_NotInlined; - - JSFunction* scriptedTarget = &(target->getBoundFunctionTarget()->as()); - - // Don't optimize if we're constructing and the callee is not a - // constructor, so that CallKnown does not have to handle this case - // (it should always throw). - if (nativeCallInfo.constructing() && !scriptedTarget->isConstructor()) - return InliningStatus_NotInlined; - - if (nativeCallInfo.constructing() && nativeCallInfo.getNewTarget() != nativeCallInfo.fun()) - return InliningStatus_NotInlined; - - if (gc::IsInsideNursery(scriptedTarget)) - return InliningStatus_NotInlined; - - for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) { - const Value val = target->getBoundFunctionArgument(i); - if (val.isObject() && gc::IsInsideNursery(&val.toObject())) - return InliningStatus_NotInlined; - if (val.isString() && !val.toString()->isAtom()) - return InliningStatus_NotInlined; - } - - const Value thisVal = target->getBoundFunctionThis(); - if (thisVal.isObject() && gc::IsInsideNursery(&thisVal.toObject())) - return InliningStatus_NotInlined; - if (thisVal.isString() && !thisVal.toString()->isAtom()) - return InliningStatus_NotInlined; - - size_t argc = target->getBoundFunctionArgumentCount() + nativeCallInfo.argc(); - if (argc > ARGS_LENGTH_MAX) - return InliningStatus_NotInlined; - - nativeCallInfo.thisArg()->setImplicitlyUsedUnchecked(); - - CallInfo callInfo(alloc(), nativeCallInfo.constructing()); - callInfo.setFun(constant(ObjectValue(*scriptedTarget))); - callInfo.setThis(constant(thisVal)); - - if (!callInfo.argv().reserve(argc)) - return InliningStatus_Error; - - for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) { - MConstant* argConst = constant(target->getBoundFunctionArgument(i)); - callInfo.argv().infallibleAppend(argConst); - } - for (size_t i = 0; i < nativeCallInfo.argc(); i++) - callInfo.argv().infallibleAppend(nativeCallInfo.getArg(i)); - - // We only inline when it was not a super-call, so just set the newTarget - // to be the target function, per spec. - if (nativeCallInfo.constructing()) - callInfo.setNewTarget(callInfo.fun()); - - if (!makeCall(scriptedTarget, callInfo)) - return InliningStatus_Error; - - return InliningStatus_Inlined; -} - IonBuilder::InliningStatus IonBuilder::inlineAtomicsCompareExchange(CallInfo& callInfo) { diff --git a/js/src/jit/RematerializedFrame.cpp b/js/src/jit/RematerializedFrame.cpp index abf08c7dd6..22c08bb75f 100644 --- a/js/src/jit/RematerializedFrame.cpp +++ b/js/src/jit/RematerializedFrame.cpp @@ -142,8 +142,8 @@ RematerializedFrame::pushOnScopeChain(ScopeObject& scope) bool RematerializedFrame::initFunctionScopeObjects(JSContext* cx) { - MOZ_ASSERT(isNonEvalFunctionFrame()); - MOZ_ASSERT(fun()->needsCallObject()); + MOZ_ASSERT(isFunctionFrame()); + MOZ_ASSERT(callee()->needsCallObject()); CallObject* callobj = CallObject::createForFunction(cx, this); if (!callobj) return false; diff --git a/js/src/jit/RematerializedFrame.h b/js/src/jit/RematerializedFrame.h index d50e874a69..4d3cce19cf 100644 --- a/js/src/jit/RematerializedFrame.h +++ b/js/src/jit/RematerializedFrame.h @@ -124,7 +124,7 @@ class RematerializedFrame bool initFunctionScopeObjects(JSContext* cx); bool hasCallObj() const { - MOZ_ASSERT(fun()->needsCallObject()); + MOZ_ASSERT(callee()->needsCallObject()); return hasCallObj_; } CallObject& callObj() const; @@ -141,27 +141,16 @@ class RematerializedFrame bool isFunctionFrame() const { return !!script_->functionNonDelazifying(); } - bool isModuleFrame() const { - return !!script_->module(); - } bool isGlobalFrame() const { - return !isFunctionFrame() && !isModuleFrame(); + return script_->isGlobalCode(); } - bool isNonEvalFunctionFrame() const { - // Ion doesn't support eval frames. - return isFunctionFrame(); + bool isModuleFrame() const { + return script_->module(); } JSScript* script() const { return script_; } - JSFunction* fun() const { - MOZ_ASSERT(isFunctionFrame()); - return script_->functionNonDelazifying(); - } - JSFunction* maybeFun() const { - return isFunctionFrame() ? fun() : nullptr; - } JSFunction* callee() const { MOZ_ASSERT(isFunctionFrame()); return callee_; @@ -186,7 +175,7 @@ class RematerializedFrame } unsigned numFormalArgs() const { - return maybeFun() ? fun()->nargs() : 0; + return isFunctionFrame() ? callee()->nargs() : 0; } unsigned numActualArgs() const { return numActualArgs_; diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index fdb35e8789..fad6c380fa 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -4739,7 +4739,7 @@ DoTypeMonitorFallback(JSContext* cx, BaselineFrame* frame, ICTypeMonitor_Fallbac // In derived class constructors (including nested arrows/eval), the // |this| argument or GETALIASEDVAR can return the magic TDZ value. MOZ_ASSERT(value.isMagic(JS_UNINITIALIZED_LEXICAL)); - MOZ_ASSERT(frame->isFunctionFrame()); + MOZ_ASSERT(frame->isFunctionFrame() || frame->isEvalFrame()); MOZ_ASSERT(stub->monitorsThis() || *GetNextPc(pc) == JSOP_CHECKTHIS || *GetNextPc(pc) == JSOP_CHECKRETURN); diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 75b22954e0..6a6ec4423d 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -569,12 +569,12 @@ CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHa rval.set(MagicValue(JS_IS_CONSTRUCTING)); if (callee->is()) { - JSFunction* fun = &callee->as(); + RootedFunction fun(cx, &callee->as()); if (fun->isInterpreted() && fun->isConstructor()) { JSScript* script = fun->getOrCreateScript(cx); if (!script || !script->ensureHasTypes(cx)) return false; - if (script->isDerivedClassConstructor()) { + if (fun->isBoundFunction() || script->isDerivedClassConstructor()) { rval.set(MagicValue(JS_UNINITIALIZED_LEXICAL)); } else { JSObject* thisObj = CreateThisForFunction(cx, callee, newTarget, GenericObject); @@ -712,7 +712,7 @@ DebugEpilogue(JSContext* cx, BaselineFrame* frame, jsbytecode* pc, bool ok) JSScript* script = frame->script(); frame->setOverridePc(script->lastPC()); - if (frame->isNonEvalFunctionFrame()) { + if (frame->isFunctionFrame()) { MOZ_ASSERT_IF(ok, frame->hasReturnValue()); DebugScopes::onPopCall(frame, cx); } else if (frame->isStrictEvalFrame()) { diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index c5423697cb..0a01d66ff7 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4253,6 +4253,7 @@ CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, } fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL, funAtom, + /* proto = */ nullptr, gc::AllocKind::FUNCTION, TenuredObject, enclosingDynamicScope)); if (!fun) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 3c75f4fd56..3ab8cd507f 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2187,7 +2187,8 @@ class JS_PUBLIC_API(CompartmentCreationOptions) invisibleToDebugger_(false), mergeable_(false), preserveJitCode_(false), - cloneSingletons_(false) + cloneSingletons_(false), + experimentalDateTimeFormatFormatToPartsEnabled_(false) { zone_.spec = JS::FreshZone; } @@ -2250,6 +2251,24 @@ class JS_PUBLIC_API(CompartmentCreationOptions) return *this; } + // ECMA-402 is considering adding a "formatToParts" DateTimeFormat method, + // that exposes not just a formatted string but its ordered subcomponents. + // The method, its semantics, and its name are all well short of being + // finalized, so for now it's exposed *only* if requested. + // + // Until "formatToParts" is included in a final specification edition, it's + // subject to change or removal at any time. Do *not* rely on it in + // mission-critical code that can't be changed if ECMA-402 decides not to + // accept the method in its current form. + bool experimentalDateTimeFormatFormatToPartsEnabled() const { + return experimentalDateTimeFormatFormatToPartsEnabled_; + } + CompartmentCreationOptions& setExperimentalDateTimeFormatFormatToPartsEnabled(bool flag) { + experimentalDateTimeFormatFormatToPartsEnabled_ = flag; + return *this; + } + + private: JSAddonId* addonId_; JSTraceOp traceGlobal_; @@ -2261,6 +2280,7 @@ class JS_PUBLIC_API(CompartmentCreationOptions) bool mergeable_; bool preserveJitCode_; bool cloneSingletons_; + bool experimentalDateTimeFormatFormatToPartsEnabled_; }; /** diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index b85b8c10a8..da458330be 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -294,7 +294,6 @@ CallJSNativeConstructor(JSContext* cx, Native native, const CallArgs& args) * - (new Object(Object)) returns the callee. */ MOZ_ASSERT_IF(native != js::proxy_Construct && - native != js::CallOrConstructBoundFunction && native != js::IteratorConstructor && (!callee->is() || callee->as().native() != obj_construct), args.rval().isObject() && callee != &args.rval().toObject()); diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 70e3e2d619..a7234d63f7 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -394,6 +394,11 @@ JS_FRIEND_API(JSFunction*) js::GetOutermostEnclosingFunctionOfScriptedCaller(JSContext* cx) { ScriptFrameIter iter(cx); + + // Skip eval frames. + while (!iter.done() && iter.isEvalFrame()) + ++iter; + if (iter.done()) return nullptr; @@ -715,7 +720,7 @@ FormatFrame(JSContext* cx, const ScriptFrameIter& iter, char* buf, int num, RootedValue thisVal(cx); if (iter.hasUsableAbstractFramePtr() && - iter.isNonEvalFunctionFrame() && + iter.isFunctionFrame() && fun && !fun->isArrow() && !fun->isDerivedClassConstructor()) { if (!GetFunctionThis(cx, iter.abstractFramePtr(), &thisVal)) diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 57ed25db70..d5e232c39c 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -29,6 +29,7 @@ #include "builtin/Eval.h" #include "builtin/Object.h" +#include "builtin/SelfHostingDefines.h" #include "frontend/BytecodeCompiler.h" #include "frontend/TokenStream.h" #include "gc/Marking.h" @@ -93,10 +94,9 @@ static bool AdvanceToActiveCallLinear(JSContext* cx, NonBuiltinScriptFrameIter& iter, HandleFunction fun) { MOZ_ASSERT(!fun->isBuiltin()); - MOZ_ASSERT(!fun->isBoundFunction(), "all bound functions are currently native (ergo builtin)"); for (; !iter.done(); ++iter) { - if (!iter.isFunctionFrame() || iter.isEvalFrame()) + if (!iter.isFunctionFrame()) continue; if (iter.matchCallee(cx, fun)) return true; @@ -252,6 +252,9 @@ CallerGetterImpl(JSContext* cx, const CallArgs& args) } ++iter; + while (!iter.done() && iter.isEvalFrame()) + ++iter; + if (iter.done() || !iter.isFunctionFrame()) { args.rval().setNull(); return true; @@ -318,6 +321,9 @@ CallerSetterImpl(JSContext* cx, const CallArgs& args) return true; ++iter; + while (!iter.done() && iter.isEvalFrame()) + ++iter; + if (iter.done() || !iter.isFunctionFrame()) return true; @@ -362,7 +368,6 @@ static bool ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId id) { MOZ_ASSERT(fun->isInterpreted() || fun->isAsmJSNative()); - MOZ_ASSERT(!fun->isFunctionPrototype()); MOZ_ASSERT(id == NameToId(cx->names().prototype)); // Assert that fun is not a compiler-created function object, which @@ -478,11 +483,18 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) if (fun->hasResolvedLength()) return true; - uint16_t length; - if (!fun->getLength(cx, &length)) - return false; + // Bound functions' length can have values up to MAX_SAFE_INTEGER, + // so they're handled differently from other functions. + if (fun->isBoundFunction()) { + MOZ_ASSERT(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT).isNumber()); + v.set(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT)); + } else { + uint16_t length; + if (!fun->getLength(cx, &length)) + return false; - v.setInt32(length); + v.setInt32(length); + } } else { if (fun->hasResolvedName()) return true; @@ -839,7 +851,6 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key) return nullptr; RootedFunction functionProto(cx, &functionProto_->as()); - functionProto->setIsFunctionPrototype(); const char* rawSource = "() {\n}"; size_t sourceLen = strlen(rawSource); @@ -1169,7 +1180,8 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen) } else { MOZ_ASSERT(!fun->isExprBody()); - if (fun->isNative() && fun->native() == js::DefaultDerivedClassConstructor) { + bool derived = fun->infallibleIsDefaultClassConstructor(cx); + if (derived && fun->isDerivedClassConstructor()) { if (!out.append("(...args) {\n ") || !out.append("super(...args);\n}")) { @@ -1179,7 +1191,7 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen) if (!out.append("() {\n ")) return nullptr; - if (!fun->isNative() || fun->native() != js::DefaultClassConstructor) { + if (!derived) { if (!out.append("[native code]")) return nullptr; } @@ -1369,74 +1381,83 @@ js::fun_apply(JSContext* cx, unsigned argc, Value* vp) return true; } -static const uint32_t JSSLOT_BOUND_FUNCTION_TARGET = 0; -static const uint32_t JSSLOT_BOUND_FUNCTION_THIS = 1; -static const uint32_t JSSLOT_BOUND_FUNCTION_ARGS_COUNT = 2; - -static const uint32_t BOUND_FUNCTION_RESERVED_SLOTS = 3; - -inline bool -JSFunction::initBoundFunction(JSContext* cx, HandleObject target, HandleValue thisArg, - const Value* args, unsigned argslen) +bool +JSFunction::infallibleIsDefaultClassConstructor(JSContext* cx) const { - RootedFunction self(cx, this); - - /* - * Convert to a dictionary to set the BOUND_FUNCTION flag and increase - * the slot span to cover the arguments and additional slots for the 'this' - * value and arguments count. - */ - if (!self->toDictionaryMode(cx)) + if (!isSelfHostedBuiltin()) return false; - if (!self->JSObject::setFlags(cx, BaseShape::BOUND_FUNCTION)) + bool isDefault = false; + if (isInterpretedLazy()) { + JSAtom* name = &getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).toString()->asAtom(); + isDefault = name == cx->names().DefaultDerivedClassConstructor || + name == cx->names().DefaultBaseClassConstructor; + } else { + isDefault = nonLazyScript()->isDefaultClassConstructor(); + } + + MOZ_ASSERT_IF(isDefault, isConstructor()); + MOZ_ASSERT_IF(isDefault, isClassConstructor()); + return isDefault; +} + +bool +JSFunction::getLength(JSContext* cx, uint16_t* length) +{ + JS::RootedFunction self(cx, this); + MOZ_ASSERT(!self->isBoundFunction()); + if (self->isInterpretedLazy() && !self->getOrCreateScript(cx)) return false; - if (!self->setSlotSpan(cx, BOUND_FUNCTION_RESERVED_SLOTS + argslen)) - return false; - - self->setSlot(JSSLOT_BOUND_FUNCTION_TARGET, ObjectValue(*target)); - self->setSlot(JSSLOT_BOUND_FUNCTION_THIS, thisArg); - self->setSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT, PrivateUint32Value(argslen)); - - self->initSlotRange(BOUND_FUNCTION_RESERVED_SLOTS, args, argslen); - - self->setJitInfo(&jit::JitInfo_CallBoundFunction); - + *length = self->hasScript() ? self->nonLazyScript()->funLength() + : (self->nargs() - self->hasRest()); return true; } +static const js::Value& +BoundFunctionEnvironmentSlotValue(const JSFunction* fun, uint32_t slotIndex) +{ + MOZ_ASSERT(fun->isBoundFunction()); + MOZ_ASSERT(fun->environment()->is()); + CallObject* callObject = &fun->environment()->as(); + return callObject->getSlot(slotIndex); +} + JSObject* JSFunction::getBoundFunctionTarget() const { - MOZ_ASSERT(isBoundFunction()); - - return &getSlot(JSSLOT_BOUND_FUNCTION_TARGET).toObject(); + js::Value targetVal = BoundFunctionEnvironmentSlotValue(this, JSSLOT_BOUND_FUNCTION_TARGET); + MOZ_ASSERT(IsCallable(targetVal)); + return &targetVal.toObject(); } const js::Value& JSFunction::getBoundFunctionThis() const { - MOZ_ASSERT(isBoundFunction()); + return BoundFunctionEnvironmentSlotValue(this, JSSLOT_BOUND_FUNCTION_THIS); +} - return getSlot(JSSLOT_BOUND_FUNCTION_THIS); +static ArrayObject* +GetBoundFunctionArguments(const JSFunction* boundFun) +{ + js::Value argsVal = BoundFunctionEnvironmentSlotValue(boundFun, JSSLOT_BOUND_FUNCTION_ARGS); + return &argsVal.toObject().as(); } const js::Value& -JSFunction::getBoundFunctionArgument(unsigned which) const +JSFunction::getBoundFunctionArgument(JSContext* cx, unsigned which) const { - MOZ_ASSERT(isBoundFunction()); MOZ_ASSERT(which < getBoundFunctionArgumentCount()); - return getSlot(BOUND_FUNCTION_RESERVED_SLOTS + which); + RootedArrayObject boundArgs(cx, GetBoundFunctionArguments(this)); + RootedValue res(cx); + return boundArgs->getDenseElement(which); } size_t JSFunction::getBoundFunctionArgumentCount() const { - MOZ_ASSERT(isBoundFunction()); - - return getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32(); + return GetBoundFunctionArguments(this)->length(); } /* static */ bool @@ -1593,9 +1614,14 @@ JSFunction::maybeRelazify(JSRuntime* rt) return; // To delazify self-hosted builtins we need the name of the function - // to clone. This name is stored in the first extended slot. - if (isSelfHostedBuiltin() && !isExtended()) + // to clone. This name is stored in the first extended slot. Since + // that slot is sometimes also used for other purposes, make sure it + // contains a string. + if (isSelfHostedBuiltin() && + (!isExtended() || !getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).isString())) + { return; + } JSScript* script = nonLazyScript(); @@ -1612,73 +1638,6 @@ JSFunction::maybeRelazify(JSRuntime* rt) } } -/* ES5 15.3.4.5.1 and 15.3.4.5.2. */ -bool -js::CallOrConstructBoundFunction(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - RootedFunction fun(cx, &args.callee().as()); - MOZ_ASSERT(fun->isBoundFunction()); - - /* 15.3.4.5.1 step 1, 15.3.4.5.2 step 3. */ - unsigned boundArgsLen = fun->getBoundFunctionArgumentCount(); - - uint32_t argsLen = args.length(); - if (argsLen + boundArgsLen > ARGS_LENGTH_MAX) { - ReportAllocationOverflow(cx); - return false; - } - - /* 15.3.4.5.1 step 3, 15.3.4.5.2 step 1. */ - RootedObject target(cx, fun->getBoundFunctionTarget()); - - /* 15.3.4.5.1 step 2. */ - const Value& boundThis = fun->getBoundFunctionThis(); - - if (args.isConstructing()) { - ConstructArgs cargs(cx); - if (!cargs.init(argsLen + boundArgsLen)) - return false; - - /* 15.3.4.5.1, 15.3.4.5.2 step 4. */ - for (uint32_t i = 0; i < boundArgsLen; i++) - cargs[i].set(fun->getBoundFunctionArgument(i)); - for (uint32_t i = 0; i < argsLen; i++) - cargs[boundArgsLen + i].set(args[i]); - - RootedValue targetv(cx, ObjectValue(*target)); - - /* ES6 9.4.1.2 step 5 */ - RootedValue newTarget(cx); - if (&args.newTarget().toObject() == fun) - newTarget.set(targetv); - else - newTarget.set(args.newTarget()); - - return Construct(cx, targetv, cargs, newTarget, args.rval()); - } - - InvokeArgs invokeArgs(cx); - if (!invokeArgs.init(argsLen + boundArgsLen)) - return false; - - /* 15.3.4.5.1, 15.3.4.5.2 step 4. */ - for (uint32_t i = 0; i < boundArgsLen; i++) - invokeArgs[i].set(fun->getBoundFunctionArgument(i)); - for (uint32_t i = 0; i < argsLen; i++) - invokeArgs[boundArgsLen + i].set(args[i]); - - /* 15.3.4.5.1, 15.3.4.5.2 step 5. */ - invokeArgs.setCallee(ObjectValue(*target)); - invokeArgs.setThis(boundThis); - - if (!Invoke(cx, invokeArgs)) - return false; - - args.rval().set(invokeArgs.rval()); - return true; -} - static bool fun_isGenerator(JSContext* cx, unsigned argc, Value* vp) { @@ -1693,132 +1652,6 @@ fun_isGenerator(JSContext* cx, unsigned argc, Value* vp) return true; } -static JSFunction* -NewNativeFunctionWithGivenProto(JSContext* cx, Native native, unsigned nargs, - HandleAtom atom, HandleObject proto) -{ - return NewFunctionWithProto(cx, native, nargs, JSFunction::NATIVE_FUN, nullptr, atom, proto, - AllocKind::FUNCTION, GenericObject, NewFunctionGivenProto); -} - -static JSFunction* -NewNativeConstructorWithGivenProto(JSContext* cx, Native native, unsigned nargs, - HandleAtom atom, HandleObject proto) -{ - return NewFunctionWithProto(cx, native, nargs, JSFunction::NATIVE_CTOR, nullptr, atom, proto, - AllocKind::FUNCTION, GenericObject, NewFunctionGivenProto); -} - -// ES6 draft rev32 19.2.3.2 -bool -js::fun_bind(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - // Step 1. - RootedValue thisv(cx, args.thisv()); - - // Step 2. - if (!IsCallable(thisv)) { - ReportIncompatibleMethod(cx, args, &JSFunction::class_); - return false; - } - - // Step 3. - Value* boundArgs = nullptr; - unsigned argslen = 0; - if (args.length() > 1) { - boundArgs = args.array() + 1; - argslen = args.length() - 1; - } - - RootedValue thisArg(cx, args.length() >= 1 ? args[0] : UndefinedValue()); - RootedObject target(cx, &thisv.toObject()); - - // This is part of step 4, but we're delaying allocating the function object. - RootedObject proto(cx); - if (!GetPrototype(cx, target, &proto)) - return false; - - double length = 0.0; - // Try to avoid invoking the resolve hook. - if (target->is() && !target->as().hasResolvedLength()) { - uint16_t len; - if (!target->as().getLength(cx, &len)) - return false; - length = Max(0.0, double(len) - argslen); - } else { - // Steps 5-6. - RootedId id(cx, NameToId(cx->names().length)); - bool hasLength; - if (!HasOwnProperty(cx, target, id, &hasLength)) - return false; - - // Step 7-8. - if (hasLength) { - // a-b. - RootedValue targetLen(cx); - if (!GetProperty(cx, target, target, id, &targetLen)) - return false; - // d. - if (targetLen.isNumber()) - length = Max(0.0, JS::ToInteger(targetLen.toNumber()) - argslen); - } - } - - RootedString name(cx, cx->names().empty); - if (target->is() && !target->as().hasResolvedName()) { - if (target->as().atom()) - name = target->as().atom(); - } else { - // Steps 11-12. - RootedValue targetName(cx); - if (!GetProperty(cx, target, target, cx->names().name, &targetName)) - return false; - - // Step 13. - if (targetName.isString()) - name = targetName.toString(); - } - - // Step 14. Relevant bits from SetFunctionName. - StringBuffer sb(cx); - // Disabled for B2G failures. - // if (!sb.append("bound ") || !sb.append(name)) - // return false; - if (!sb.append(name)) - return false; - - RootedAtom nameAtom(cx, sb.finishAtom()); - if (!nameAtom) - return false; - - // Step 4. - RootedFunction fun(cx, target->isConstructor() ? - NewNativeConstructorWithGivenProto(cx, CallOrConstructBoundFunction, length, nameAtom, proto) : - NewNativeFunctionWithGivenProto(cx, CallOrConstructBoundFunction, length, nameAtom, proto)); - if (!fun) - return false; - - if (!fun->initBoundFunction(cx, target, thisArg, boundArgs, argslen)) - return false; - - // Steps 9-10. Set length again, because NewNativeFunction/NewNativeConstructor - // sometimes truncates. - if (length != fun->nargs()) { - RootedValue lengthVal(cx, NumberValue(length)); - if (!DefineProperty(cx, fun, cx->names().length, lengthVal, nullptr, nullptr, - JSPROP_READONLY)) - { - return false; - } - } - - // Step 15. - args.rval().setObject(*fun); - return true; -} - /* * Report "malformed formal parameter" iff no illegal char or similar scanner * error was already reported. @@ -1837,8 +1670,8 @@ const JSFunctionSpec js::function_methods[] = { JS_FN(js_toString_str, fun_toString, 0,0), JS_FN(js_apply_str, fun_apply, 2,0), JS_FN(js_call_str, fun_call, 1,0), - JS_FN("bind", fun_bind, 1,0), JS_FN("isGenerator", fun_isGenerator,0,0), + JS_SELF_HOSTED_FN("bind", "FunctionBind", 1,JSPROP_DEFINE_LATE), JS_FS_END }; @@ -2109,6 +1942,7 @@ js::NewNativeConstructor(ExclusiveContext* cx, Native native, unsigned nargs, Ha JSFunction* js::NewScriptedFunction(ExclusiveContext* cx, unsigned nargs, JSFunction::Flags flags, HandleAtom atom, + HandleObject proto /* = nullptr */, gc::AllocKind allocKind /* = AllocKind::FUNCTION */, NewObjectKind newKind /* = GenericObject */, HandleObject enclosingDynamicScopeArg /* = nullptr */) @@ -2117,7 +1951,7 @@ js::NewScriptedFunction(ExclusiveContext* cx, unsigned nargs, if (!enclosingDynamicScope) enclosingDynamicScope = &cx->global()->lexicalScope(); return NewFunctionWithProto(cx, nullptr, nargs, flags, enclosingDynamicScope, - atom, nullptr, allocKind, newKind); + atom, proto, allocKind, newKind); } #ifdef DEBUG @@ -2407,6 +2241,7 @@ js::DefineFunction(JSContext* cx, HandleObject obj, HandleId id, Native native, if (!native) fun = NewScriptedFunction(cx, nargs, JSFunction::INTERPRETED_LAZY, atom, + /* proto = */ nullptr, allocKind, GenericObject, obj); else if (flags & JSFUN_CONSTRUCTOR) fun = NewNativeConstructor(cx, native, nargs, atom, allocKind); diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 7b9a67eab2..e147824253 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -28,6 +28,10 @@ typedef JSNative Native; struct JSAtomState; +static const uint32_t JSSLOT_BOUND_FUNCTION_TARGET = 2; +static const uint32_t JSSLOT_BOUND_FUNCTION_THIS = 3; +static const uint32_t JSSLOT_BOUND_FUNCTION_ARGS = 4; + class JSFunction : public js::NativeObject { public: @@ -48,7 +52,7 @@ class JSFunction : public js::NativeObject INTERPRETED = 0x0001, /* function has a JSScript and environment. */ CONSTRUCTOR = 0x0002, /* function that can be called as a constructor */ EXTENDED = 0x0004, /* structure is FunctionExtended */ - IS_FUN_PROTO = 0x0008, /* function is Function.prototype for some global object */ + BOUND_FUN = 0x0008, /* function was created with Function.prototype.bind. */ EXPR_BODY = 0x0010, /* arrow function with expression body or * expression closure: function(x) x*x */ HAS_GUESSED_ATOM = 0x0020, /* function had no explicit name, but a @@ -93,8 +97,8 @@ class JSFunction : public js::NativeObject INTERPRETED_GENERATOR = INTERPRETED, NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME, - STABLE_ACROSS_CLONES = IS_FUN_PROTO | CONSTRUCTOR | EXPR_BODY | HAS_GUESSED_ATOM | - LAMBDA | SELF_HOSTED | HAS_REST | FUNCTION_KIND_MASK + STABLE_ACROSS_CLONES = CONSTRUCTOR | EXPR_BODY | HAS_GUESSED_ATOM | LAMBDA | + SELF_HOSTED | HAS_REST | FUNCTION_KIND_MASK }; static_assert((INTERPRETED | INTERPRETED_LAZY) == js::JS_FUNCTION_INTERPRETED_BITS, @@ -183,15 +187,17 @@ class JSFunction : public js::NativeObject bool isAsmJSNative() const { return kind() == AsmJS; } /* Possible attributes of an interpreted function: */ - bool isFunctionPrototype() const { return flags() & IS_FUN_PROTO; } bool isExprBody() const { return flags() & EXPR_BODY; } bool hasGuessedAtom() const { return flags() & HAS_GUESSED_ATOM; } bool isLambda() const { return flags() & LAMBDA; } + bool isBoundFunction() const { return flags() & BOUND_FUN; } bool hasRest() const { return flags() & HAS_REST; } bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; } bool hasScript() const { return flags() & INTERPRETED; } bool isBeingParsed() const { return flags() & BEING_PARSED; } + bool infallibleIsDefaultClassConstructor(JSContext* cx) const; + // Arrow functions store their lexical new.target in the first extended slot. bool isArrow() const { return kind() == Arrow; } // Every class-constructor is also a method. @@ -255,6 +261,13 @@ class JSFunction : public js::NativeObject flags_ |= CONSTRUCTOR; } + void setIsClassConstructor() { + MOZ_ASSERT(!isClassConstructor()); + MOZ_ASSERT(isConstructor()); + + setKind(ClassConstructor); + } + // Can be called multiple times by the parser. void setArgCount(uint16_t nargs) { this->nargs_ = nargs; @@ -265,6 +278,11 @@ class JSFunction : public js::NativeObject flags_ |= HAS_REST; } + void setIsBoundFunction() { + MOZ_ASSERT(!isBoundFunction()); + flags_ |= BOUND_FUN; + } + void setIsSelfHostedBuiltin() { MOZ_ASSERT(isInterpreted()); MOZ_ASSERT(!isSelfHostedBuiltin()); @@ -278,11 +296,6 @@ class JSFunction : public js::NativeObject flags_ |= SELF_HOSTED; } - void setIsFunctionPrototype() { - MOZ_ASSERT(!isFunctionPrototype()); - flags_ |= IS_FUN_PROTO; - } - // Can be called multiple times by the parser. void setIsExprBody() { flags_ |= EXPR_BODY; @@ -453,15 +466,7 @@ class JSFunction : public js::NativeObject return u.i.s.script_; } - bool getLength(JSContext* cx, uint16_t* length) { - JS::RootedFunction self(cx, this); - if (self->isInterpretedLazy() && !self->getOrCreateScript(cx)) - return false; - - *length = self->hasScript() ? self->nonLazyScript()->funLength() - : (self->nargs() - self->hasRest()); - return true; - } + bool getLength(JSContext* cx, uint16_t* length); js::LazyScript* lazyScript() const { MOZ_ASSERT(isInterpretedLazy() && u.i.s.lazy_); @@ -571,12 +576,9 @@ class JSFunction : public js::NativeObject /* Bound function accessors. */ - inline bool initBoundFunction(JSContext* cx, js::HandleObject target, js::HandleValue thisArg, - const js::Value* args, unsigned argslen); - JSObject* getBoundFunctionTarget() const; const js::Value& getBoundFunctionThis() const; - const js::Value& getBoundFunctionArgument(unsigned which) const; + const js::Value& getBoundFunctionArgument(JSContext* cx, unsigned which) const; size_t getBoundFunctionArgumentCount() const; private: @@ -656,7 +658,8 @@ NewNativeConstructor(ExclusiveContext* cx, JSNative native, unsigned nargs, Hand // the global. extern JSFunction* NewScriptedFunction(ExclusiveContext* cx, unsigned nargs, JSFunction::Flags flags, - HandleAtom atom, gc::AllocKind allocKind = gc::AllocKind::FUNCTION, + HandleAtom atom, HandleObject proto = nullptr, + gc::AllocKind allocKind = gc::AllocKind::FUNCTION, NewObjectKind newKind = GenericObject, HandleObject enclosingDynamicScope = nullptr); @@ -692,9 +695,6 @@ FunctionHasResolveHook(const JSAtomState& atomState, jsid id); extern bool fun_toString(JSContext* cx, unsigned argc, Value* vp); -extern bool -fun_bind(JSContext* cx, unsigned argc, Value* vp); - struct WellKnownSymbols; extern bool @@ -839,10 +839,8 @@ ReportIncompatibleMethod(JSContext* cx, CallReceiver call, const Class* clasp); extern void ReportIncompatible(JSContext* cx, CallReceiver call); -bool -CallOrConstructBoundFunction(JSContext*, unsigned, js::Value*); - extern const JSFunctionSpec function_methods[]; +extern const JSFunctionSpec function_selfhosted_methods[]; extern bool fun_apply(JSContext* cx, unsigned argc, Value* vp); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index d239b9b7e9..ebe3eecbe8 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3606,7 +3606,7 @@ js::DumpInterpreterFrame(JSContext* cx, InterpreterFrame* start) v.setObject(*fun); dumpValue(v); } else { - fprintf(stderr, "global frame, no callee"); + fprintf(stderr, "global or eval frame, no callee"); } fputc('\n', stderr); @@ -3618,7 +3618,7 @@ js::DumpInterpreterFrame(JSContext* cx, InterpreterFrame* start) fprintf(stderr, " current op: %s\n", CodeName[*pc]); MaybeDumpObject("staticScope", i.script()->getStaticBlockScope(pc)); } - if (i.isNonEvalFunctionFrame()) + if (i.isFunctionFrame()) MaybeDumpValue("this", i.thisArgument(cx)); if (!i.isJit()) { fprintf(stderr, " rval: "); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 0b352e6b3c..0f4598a528 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -428,7 +428,7 @@ JSObject::nonProxyIsExtensible() const inline bool JSObject::isBoundFunction() const { - return hasAllFlags(js::BaseShape::BOUND_FUNCTION); + return is() && as().isBoundFunction(); } inline bool diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 8e4873d2c5..39a413ec39 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -1020,14 +1020,12 @@ struct ExpressionDecompiler { JSContext* cx; RootedScript script; - RootedFunction fun; BytecodeParser parser; Sprinter sprinter; - ExpressionDecompiler(JSContext* cx, JSScript* script, JSFunction* fun) + ExpressionDecompiler(JSContext* cx, JSScript* script) : cx(cx), script(cx, script), - fun(cx, fun), parser(cx, script), sprinter(cx) {} @@ -1259,7 +1257,7 @@ ExpressionDecompiler::loadAtom(jsbytecode* pc) JSAtom* ExpressionDecompiler::getArg(unsigned slot) { - MOZ_ASSERT(fun); + MOZ_ASSERT(script->functionNonDelazifying()); MOZ_ASSERT(slot < script->bindings.numArgs()); for (BindingIter bi(script); bi; bi++) { @@ -1405,9 +1403,6 @@ DecompileExpressionFromStack(JSContext* cx, int spindex, int skipStackHits, Hand RootedScript script(cx, frameIter.script()); AutoCompartment ac(cx, &script->global()); jsbytecode* valuepc = frameIter.pc(); - RootedFunction fun(cx, frameIter.isFunctionFrame() - ? frameIter.calleeTemplate() - : nullptr); MOZ_ASSERT(script->containsPC(valuepc)); @@ -1420,7 +1415,7 @@ DecompileExpressionFromStack(JSContext* cx, int spindex, int skipStackHits, Hand if (!valuepc) return true; - ExpressionDecompiler ed(cx, script, fun); + ExpressionDecompiler ed(cx, script); if (!ed.init()) return false; if (!ed.decompilePC(valuepc)) @@ -1487,9 +1482,6 @@ DecompileArgumentFromStack(JSContext* cx, int formalIndex, char** res) RootedScript script(cx, frameIter.script()); AutoCompartment ac(cx, &script->global()); jsbytecode* current = frameIter.pc(); - RootedFunction fun(cx, frameIter.isFunctionFrame() - ? frameIter.calleeTemplate() - : nullptr); MOZ_ASSERT(script->containsPC(current)); @@ -1509,7 +1501,7 @@ DecompileArgumentFromStack(JSContext* cx, int formalIndex, char** res) if (uint32_t(formalStackIndex) >= parser.stackDepthAtPC(current)) return true; - ExpressionDecompiler ed(cx, script, fun); + ExpressionDecompiler ed(cx, script); if (!ed.init()) return false; if (!ed.decompilePCForStackOperand(current, formalStackIndex)) @@ -1833,7 +1825,7 @@ GetPCCountJSON(JSContext* cx, const ScriptAndCounts& sac, StringBuffer& buf) } { - ExpressionDecompiler ed(cx, script, script->functionDelazifying()); + ExpressionDecompiler ed(cx, script); if (!ed.init()) return false; if (!ed.decompilePC(pc)) diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index e91cec1b82..fd1fc5fcdd 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -176,7 +176,6 @@ Bindings::initWithTemporaryStorage(ExclusiveContext* cx, MutableHandle uint32_t slot = CallObject::RESERVED_SLOTS; for (BindingIter bi(self); bi; bi++) { - MOZ_ASSERT_IF(isModule, bi->aliased()); if (!bi->aliased()) continue; @@ -627,6 +626,7 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript HasInnerFunctions, NeedsHomeObject, IsDerivedClassConstructor, + IsDefaultClassConstructor, }; uint32_t length, lineno, column, nslots; @@ -772,6 +772,8 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript scriptBits |= (1 << NeedsHomeObject); if (script->isDerivedClassConstructor()) scriptBits |= (1 << IsDerivedClassConstructor); + if (script->isDefaultClassConstructor()) + scriptBits |= (1 << IsDefaultClassConstructor); } if (!xdr->codeUint32(&prologueLength)) @@ -916,6 +918,8 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript script->needsHomeObject_ = true; if (scriptBits & (1 << IsDerivedClassConstructor)) script->isDerivedClassConstructor_ = true; + if (scriptBits & (1 << IsDefaultClassConstructor)) + script->isDefaultClassConstructor_ = true; if (scriptBits & (1 << IsLegacyGenerator)) { MOZ_ASSERT(!(scriptBits & (1 << IsStarGenerator))); @@ -3392,8 +3396,17 @@ CloneInnerInterpretedFunction(JSContext* cx, HandleObject enclosingScope, Handle } gc::AllocKind allocKind = srcFun->getAllocKind(); - RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, 0, - JSFunction::INTERPRETED, nullptr, nullptr, + uint16_t flags = srcFun->flags(); + if (srcFun->isSelfHostedBuiltin()) { + // Functions in the self-hosting compartment are only extended in + // debug mode. For top-level functions, FUNCTION_EXTENDED gets used by + // the cloning algorithm. Do the same for inner functions here. + allocKind = gc::AllocKind::FUNCTION_EXTENDED; + flags |= JSFunction::Flags::EXTENDED; + } + RootedAtom atom(cx, srcFun->displayAtom()); + RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, srcFun->nargs(), + JSFunction::Flags(flags), nullptr, atom, cloneProto, allocKind, TenuredObject)); if (!clone) return nullptr; @@ -3405,9 +3418,6 @@ CloneInnerInterpretedFunction(JSContext* cx, HandleObject enclosingScope, Handle if (!cloneScript) return nullptr; - clone->setArgCount(srcFun->nargs()); - clone->setFlags(srcFun->flags()); - clone->initAtom(srcFun->displayAtom()); if (!JSFunction::setTypeForScriptedFunction(cx, clone)) return nullptr; @@ -3572,6 +3582,9 @@ js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScri dst->hasInnerFunctions_ = src->hasInnerFunctions(); dst->isGeneratorExp_ = src->isGeneratorExp(); dst->setGeneratorKind(src->generatorKind()); + dst->isDerivedClassConstructor_ = src->isDerivedClassConstructor(); + dst->needsHomeObject_ = src->needsHomeObject(); + dst->isDefaultClassConstructor_ = src->isDefaultClassConstructor(); if (nconsts != 0) { HeapValue* vector = Rebase(dst, src, src->consts()->vector); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 3bdfe93507..0847ef8881 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -1195,6 +1195,7 @@ class JSScript : public js::gc::TenuredCell bool needsHomeObject_:1; bool isDerivedClassConstructor_:1; + bool isDefaultClassConstructor_:1; // Add padding so JSScript is gc::Cell aligned. Make padding protected // instead of private to suppress -Wunused-private-field compiler warnings. @@ -1287,23 +1288,23 @@ class JSScript : public js::gc::TenuredCell void setColumn(size_t column) { column_ = column; } - // The fixed part of a stack frame is comprised of vars (in function code) - // and block-scoped locals (in all kinds of code). + // The fixed part of a stack frame is comprised of vars (in function and + // module code) and block-scoped locals (in all kinds of code). size_t nfixed() const { - return function_ ? bindings.numFixedLocals() : bindings.numBlockScoped(); + return isGlobalOrEvalCode() ? bindings.numBlockScoped() : bindings.numFixedLocals(); } // Number of fixed slots reserved for vars. Only nonzero for function - // code. + // or module code. size_t nfixedvars() const { - return function_ ? bindings.numUnaliasedVars() : 0; + return isGlobalOrEvalCode() ? 0 : bindings.numUnaliasedVars(); } // Number of fixed slots reserved for body-level lexicals and vars. This // value minus nfixedvars() is the number of body-level lexicals. Only - // nonzero for function code. + // nonzero for function or module code. size_t nbodyfixed() const { - return function_ ? bindings.numUnaliasedBodyLevelLocals() : 0; + return isGlobalOrEvalCode() ? 0 : bindings.numUnaliasedBodyLevelLocals(); } // Calculate the number of fixed slots that are live at a particular bytecode. @@ -1426,6 +1427,9 @@ class JSScript : public js::gc::TenuredCell bool failedLexicalCheck() const { return failedLexicalCheck_; } + bool isDefaultClassConstructor() const { + return isDefaultClassConstructor_; + } void setFailedBoundsCheck() { failedBoundsCheck_ = true; } void setFailedShapeGuard() { failedShapeGuard_ = true; } @@ -1434,6 +1438,7 @@ class JSScript : public js::gc::TenuredCell void setUninlineable() { uninlineable_ = true; } void setInvalidatedIdempotentCache() { invalidatedIdempotentCache_ = true; } void setFailedLexicalCheck() { failedLexicalCheck_ = true; } + void setIsDefaultClassConstructor() { isDefaultClassConstructor_ = true; } bool hasScriptCounts() const { return hasScriptCounts_; } @@ -1638,6 +1643,13 @@ class JSScript : public js::gc::TenuredCell } inline void setModule(js::ModuleObject* module); + bool isGlobalOrEvalCode() const { + return !function_ && !module_; + } + bool isGlobalCode() const { + return isGlobalOrEvalCode() && !isForEval(); + } + JSFlatString* sourceData(JSContext* cx); static bool loadSource(JSContext* cx, js::ScriptSource* ss, bool* worked); @@ -1656,7 +1668,10 @@ class JSScript : public js::gc::TenuredCell public: /* Return whether this script was compiled for 'eval' */ - bool isForEval() { return isCachedEval() || isActiveEval(); } + bool isForEval() const { return isCachedEval() || isActiveEval(); } + + /* Return whether this is a 'direct eval' script in a function scope. */ + bool isDirectEvalInFunction() const { return isForEval() && savedCallerFun(); } /* * Return whether this script is a top-level script. diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 88e5410f0f..78c65d6906 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -4244,7 +4244,7 @@ static const JSFunctionSpec string_static_methods[] = { JS_INLINABLE_FN("fromCharCode", js::str_fromCharCode, 1, 0, StringFromCharCode), JS_SELF_HOSTED_FN("fromCodePoint", "String_static_fromCodePoint", 1,0), - JS_SELF_HOSTED_FN("raw", "String_static_raw", 2,0), + JS_SELF_HOSTED_FN("raw", "String_static_raw", 1,0), JS_SELF_HOSTED_FN("substring", "String_static_substring", 3,0), JS_SELF_HOSTED_FN("substr", "String_static_substr", 3,0), JS_SELF_HOSTED_FN("slice", "String_static_slice", 3,0), diff --git a/js/src/moz.build b/js/src/moz.build index f327abfef2..92aec2e933 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -700,8 +700,10 @@ selfhosted.inputs = [ 'builtin/SelfHostingDefines.h', 'builtin/Utilities.js', 'builtin/Array.js', + 'builtin/Classes.js', 'builtin/Date.js', 'builtin/Error.js', + 'builtin/Function.js', 'builtin/Generator.js', 'builtin/Intl.js', 'builtin/IntlData.js', diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 91dd398f19..e1280ae5d7 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -2170,8 +2170,6 @@ DisassembleScript(JSContext* cx, HandleScript script, HandleFunction fun, Sprint(sp, " CONSTRUCTOR"); if (fun->isExprBody()) Sprint(sp, " EXPRESSION_CLOSURE"); - if (fun->isFunctionPrototype()) - Sprint(sp, " Function.prototype"); if (fun->isSelfHostedBuiltin()) Sprint(sp, " SELF_HOSTED"); if (fun->isArrow()) @@ -3975,6 +3973,11 @@ NewGlobal(JSContext* cx, unsigned argc, Value* vp) if (v.isBoolean()) creationOptions.setCloneSingletons(v.toBoolean()); + if (!JS_GetProperty(cx, opts, "experimentalDateTimeFormatFormatToPartsEnabled", &v)) + return true; + if (v.isBoolean()) + creationOptions.setExperimentalDateTimeFormatFormatToPartsEnabled(v.toBoolean()); + if (!JS_GetProperty(cx, opts, "sameZoneAs", &v)) return false; if (v.isObject()) diff --git a/js/src/tests/Intl/DateTimeFormat/formatToParts.js b/js/src/tests/Intl/DateTimeFormat/formatToParts.js new file mode 100644 index 0000000000..e88439cffd --- /dev/null +++ b/js/src/tests/Intl/DateTimeFormat/formatToParts.js @@ -0,0 +1,176 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.newGlobal||!newGlobal({experimentalDateTimeFormatFormatToPartsEnabled:true}).Intl.DateTimeFormat().formatToParts) +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +// Tests the format function with a diverse set of locales and options. +// Always use UTC to avoid dependencies on test environment. + +/* + * Return true if A is equal to B, where equality on arrays and objects + * means that they have the same set of enumerable properties, the values + * of each property are deep_equal, and their 'length' properties are + * equal. Equality on other types is ==. + */ +function deepEqual(a, b) { + if (typeof a !== typeof b) + return false; + + if (a === null) + return b === null; + + if (typeof a === 'object') { + // For every property of a, does b have that property with an equal value? + var props = {}; + for (var prop in a) { + if (!deepEqual(a[prop], b[prop])) + return false; + props[prop] = true; + } + + // Are all of b's properties present on a? + for (var prop in b) + if (!props[prop]) + return false; + + // length isn't enumerable, but we want to check it, too. + return a.length === b.length; + } + + return Object.is(a, b); +} + +function composeDate(parts) { + return parts.map(({value}) => value) + .reduce((string, part) => string + part); +} + +var format; +var date = Date.UTC(2012, 11, 17, 3, 0, 42); + +// The experimental formatToParts method is only exposed if specifically +// requested. Perform all tests using DateTimeFormat instances from a global +// object with this method enabled. +var DateTimeFormat = + newGlobal({experimentalDateTimeFormatFormatToPartsEnabled:true}).Intl.DateTimeFormat; + +// Locale en-US; default options. +format = new DateTimeFormat("en-us", {timeZone: "UTC"}); +assertEq(deepEqual(format.formatToParts(date), [ + { type: 'month', value: '12' }, + { type: 'separator', value: '/' }, + { type: 'day', value: '17' }, + { type: 'separator', value: '/' }, + { type: 'year', value: '2012' } +]), true); + +// Just date +format = new DateTimeFormat("en-us", { + year: 'numeric', + month: 'numeric', + day: 'numeric', + timeZone: "UTC"}); +assertEq(deepEqual(format.formatToParts(date), [ + { type: 'month', value: '12' }, + { type: 'separator', value: '/' }, + { type: 'day', value: '17' }, + { type: 'separator', value: '/' }, + { type: 'year', value: '2012' } +]), true); +assertEq(composeDate(format.formatToParts(date)), format.format(date)); + +// Just time in hour24 +format = new DateTimeFormat("en-us", { + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + hour12: false, + timeZone: "UTC"}); +assertEq(deepEqual(format.formatToParts(date), [ + { type: 'hour', value: '03' }, + { type: 'separator', value: ':' }, + { type: 'minute', value: '00' }, + { type: 'separator', value: ':' }, + { type: 'second', value: '42' } +]), true); +assertEq(composeDate(format.formatToParts(date)), format.format(date)); + +// Just time in hour12 +format = new DateTimeFormat("en-us", { + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + hour12: true, + timeZone: "UTC"}); +assertEq(deepEqual(format.formatToParts(date), [ + { type: 'hour', value: '3' }, + { type: 'separator', value: ':' }, + { type: 'minute', value: '00' }, + { type: 'separator', value: ':' }, + { type: 'second', value: '42' }, + { type: 'separator', value: ' ' }, + { type: 'dayperiod', value: 'AM' } +]), true); +assertEq(composeDate(format.formatToParts(date)), format.format(date)); + +// Just month. +format = new DateTimeFormat("en-us", { + month: "narrow", + timeZone: "UTC"}); +assertEq(deepEqual(format.formatToParts(date), [ + { type: 'month', value: 'D' } +]), true); +assertEq(composeDate(format.formatToParts(date)), format.format(date)); + +// Just weekday. +format = new DateTimeFormat("en-us", { + weekday: "narrow", + timeZone: "UTC"}); +assertEq(deepEqual(format.formatToParts(date), [ + { type: 'weekday', value: 'M' } +]), true); +assertEq(composeDate(format.formatToParts(date)), format.format(date)); + +// Year and era. +format = new DateTimeFormat("en-us", { + year: "numeric", + era: "short", + timeZone: "UTC"}); +assertEq(deepEqual(format.formatToParts(date), [ + { type: 'year', value: '2012' }, + { type: 'separator', value: ' ' }, + { type: 'era', value: 'AD' } +]), true); +assertEq(composeDate(format.formatToParts(date)), format.format(date)); + +// Time and date +format = new DateTimeFormat("en-us", { + weekday: 'long', + year: 'numeric', + month: 'numeric', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + hour12: true, + timeZone: "UTC"}); +assertEq(deepEqual(format.formatToParts(date), [ + { type: 'weekday', value: 'Monday' }, + { type: 'separator', value: ', ' }, + { type: 'month', value: '12' }, + { type: 'separator', value: '/' }, + { type: 'day', value: '17' }, + { type: 'separator', value: '/' }, + { type: 'year', value: '2012' }, + { type: 'separator', value: ', ' }, + { type: 'hour', value: '3' }, + { type: 'separator', value: ':' }, + { type: 'minute', value: '00' }, + { type: 'separator', value: ':' }, + { type: 'second', value: '42' }, + { type: 'separator', value: ' ' }, + { type: 'dayperiod', value: 'AM' } +]), true); +assertEq(composeDate(format.formatToParts(date)), format.format(date)); + +if (typeof reportCompare === "function") + reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/ecma_6/Class/defaultConstructorDerivedSpread.js b/js/src/tests/ecma_6/Class/defaultConstructorDerivedSpread.js new file mode 100644 index 0000000000..2b85e4d3d9 --- /dev/null +++ b/js/src/tests/ecma_6/Class/defaultConstructorDerivedSpread.js @@ -0,0 +1,19 @@ +/* Make sure that the default derived class constructor has the required spread semantics. + * + * Test credit André Bargull + */ + +Array.prototype[Symbol.iterator] = function*() { yield 1; yield 2; }; + +class Base { + constructor(a, b) { + assertEq(a, 1); + assertEq(b, 2); + } +}; +class Derived extends Base {}; + +new Derived(); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/extensions/keyword-unescaped-requirement-modules.js b/js/src/tests/ecma_6/extensions/keyword-unescaped-requirement-modules.js index 6d10114c13..260948cfed 100644 --- a/js/src/tests/ecma_6/extensions/keyword-unescaped-requirement-modules.js +++ b/js/src/tests/ecma_6/extensions/keyword-unescaped-requirement-modules.js @@ -43,16 +43,18 @@ if (typeof parseModule === "function") if (typeof Reflect.parse === "function") { var twoStatementAST = - Reflect.parse(`export { x } /* ASI should trigger here */ + Reflect.parse(`let x = 0; + export { x } /* ASI should trigger here */ fro\\u006D`, { target: "module" }); var statements = twoStatementAST.body; - assertEq(statements.length, 2, + assertEq(statements.length, 3, "should have two items in the module, not one ExportDeclaration"); - assertEq(statements[0].type, "ExportDeclaration"); - assertEq(statements[1].type, "ExpressionStatement"); - assertEq(statements[1].expression.name, "from"); + assertEq(statements[0].type, "VariableDeclaration"); + assertEq(statements[1].type, "ExportDeclaration"); + assertEq(statements[2].type, "ExpressionStatement"); + assertEq(statements[2].expression.name, "from"); var oneStatementAST = Reflect.parse(`export { x } /* no ASI here */ diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index 45b2c7cc2d..25becc6a5f 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -39,7 +39,7 @@ ArgumentsObject::MaybeForwardToCallObject(AbstractFramePtr frame, ArgumentsObjec ArgumentsData* data) { JSScript* script = frame.script(); - if (frame.fun()->needsCallObject() && script->argumentsAliasesFormals()) { + if (frame.callee()->needsCallObject() && script->argumentsAliasesFormals()) { obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(frame.callObj())); for (AliasedFormalIter fi(script); fi; fi++) data->args[fi.frameIndex()] = MagicScopeSlotValue(fi.scopeSlot()); diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index b866e681aa..4027ed06f3 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -13,6 +13,7 @@ #define FOR_EACH_COMMON_PROPERTYNAME(macro) \ macro(add, add, "add") \ + macro(allowContentSpread, allowContentSpread, "allowContentSpread") \ macro(anonymous, anonymous, "anonymous") \ macro(Any, Any, "Any") \ macro(apply, apply, "apply") \ @@ -23,10 +24,10 @@ macro(ArrayValues, ArrayValues, "ArrayValues") \ macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \ macro(Async, Async, "Async") \ - macro(bool8x16, bool8x16, "Bool8x16") \ - macro(bool16x8, bool16x8, "Bool16x8") \ - macro(bool32x4, bool32x4, "Bool32x4") \ - macro(bool64x2, bool64x2, "Bool64x2") \ + macro(Bool8x16, Bool8x16, "Bool8x16") \ + macro(Bool16x8, Bool16x8, "Bool16x8") \ + macro(Bool32x4, Bool32x4, "Bool32x4") \ + macro(Bool64x2, Bool64x2, "Bool64x2") \ macro(breakdown, breakdown, "breakdown") \ macro(buffer, buffer, "buffer") \ macro(builder, builder, "builder") \ @@ -38,6 +39,7 @@ macro(BYTES_PER_ELEMENT, BYTES_PER_ELEMENT, "BYTES_PER_ELEMENT") \ macro(call, call, "call") \ macro(callContentFunction, callContentFunction, "callContentFunction") \ + macro(std_Function_apply, std_Function_apply, "std_Function_apply") \ macro(callee, callee, "callee") \ macro(caller, caller, "caller") \ macro(callFunction, callFunction, "callFunction") \ @@ -60,9 +62,14 @@ macro(currencyDisplay, currencyDisplay, "currencyDisplay") \ macro(DateTimeFormat, DateTimeFormat, "DateTimeFormat") \ macro(DateTimeFormatFormatGet, DateTimeFormatFormatGet, "Intl_DateTimeFormat_format_get") \ + macro(DateTimeFormatFormatToPartsGet, DateTimeFormatFormatToPartsGet, "Intl_DateTimeFormat_formatToParts_get") \ + macro(day, day, "day") \ + macro(dayperiod, dayperiod, "dayperiod") \ macro(decodeURI, decodeURI, "decodeURI") \ macro(decodeURIComponent, decodeURIComponent, "decodeURIComponent") \ macro(default_, default_, "default") \ + macro(DefaultBaseClassConstructor, DefaultBaseClassConstructor, "DefaultBaseClassConstructor") \ + macro(DefaultDerivedClassConstructor, DefaultDerivedClassConstructor, "DefaultDerivedClassConstructor") \ macro(defineProperty, defineProperty, "defineProperty") \ macro(defineGetter, defineGetter, "__defineGetter__") \ macro(defineSetter, defineSetter, "__defineSetter__") \ @@ -82,6 +89,7 @@ macro(entries, entries, "entries") \ macro(enumerable, enumerable, "enumerable") \ macro(enumerate, enumerate, "enumerate") \ + macro(era, era, "era") \ macro(escape, escape, "escape") \ macro(eval, eval, "eval") \ macro(false, false_, "false") \ @@ -94,12 +102,13 @@ macro(fix, fix, "fix") \ macro(flags, flags, "flags") \ macro(float32, float32, "float32") \ - macro(float32x4, float32x4, "Float32x4") \ + macro(Float32x4, Float32x4, "Float32x4") \ macro(float64, float64, "float64") \ - macro(float64x2, float64x2, "Float64x2") \ + macro(Float64x2, Float64x2, "Float64x2") \ macro(forceInterpreter, forceInterpreter, "forceInterpreter") \ macro(forEach, forEach, "forEach") \ macro(format, format, "format") \ + macro(formatToParts, formatToParts, "formatToParts") \ macro(frame, frame, "frame") \ macro(from, from, "from") \ macro(gcCycleNumber, gcCycleNumber, "gcCycleNumber") \ @@ -114,6 +123,7 @@ macro(has, has, "has") \ macro(hasOwn, hasOwn, "hasOwn") \ macro(hasOwnProperty, hasOwnProperty, "hasOwnProperty") \ + macro(hour, hour, "hour") \ macro(ignoreCase, ignoreCase, "ignoreCase") \ macro(ignorePunctuation, ignorePunctuation, "ignorePunctuation") \ macro(includes, includes, "includes") \ @@ -124,9 +134,9 @@ macro(inNursery, inNursery, "inNursery") \ macro(innermost, innermost, "innermost") \ macro(input, input, "input") \ - macro(int8x16, int8x16, "Int8x16") \ - macro(int16x8, int16x8, "Int16x8") \ - macro(int32x4, int32x4, "Int32x4") \ + macro(Int8x16, Int8x16, "Int8x16") \ + macro(Int16x8, Int16x8, "Int16x8") \ + macro(Int32x4, Int32x4, "Int32x4") \ macro(isFinite, isFinite, "isFinite") \ macro(isNaN, isNaN, "isNaN") \ macro(isPrototypeOf, isPrototypeOf, "isPrototypeOf") \ @@ -158,8 +168,10 @@ macro(minimumFractionDigits, minimumFractionDigits, "minimumFractionDigits") \ macro(minimumIntegerDigits, minimumIntegerDigits, "minimumIntegerDigits") \ macro(minimumSignificantDigits, minimumSignificantDigits, "minimumSignificantDigits") \ + macro(minute, minute, "minute") \ macro(missingArguments, missingArguments, "missingArguments") \ macro(module, module, "module") \ + macro(month, month, "month") \ macro(multiline, multiline, "multiline") \ macro(name, name, "name") \ macro(NaN, NaN, "NaN") \ @@ -206,7 +218,9 @@ macro(revoke, revoke, "revoke") \ macro(script, script, "script") \ macro(scripts, scripts, "scripts") \ + macro(second, second, "second") \ macro(sensitivity, sensitivity, "sensitivity") \ + macro(separator, separator, "separator") \ macro(set, set, "set") \ macro(shape, shape, "shape") \ macro(size, size, "size") \ @@ -227,6 +241,7 @@ macro(throw, throw_, "throw") \ macro(timestamp, timestamp, "timestamp") \ macro(timeZone, timeZone, "timeZone") \ + macro(timeZoneName, timeZoneName, "timeZoneName") \ macro(toGMTString, toGMTString, "toGMTString") \ macro(toISOString, toISOString, "toISOString") \ macro(toJSON, toJSON, "toJSON") \ @@ -235,6 +250,7 @@ macro(toString, toString, "toString") \ macro(toUTCString, toUTCString, "toUTCString") \ macro(true, true_, "true") \ + macro(type, type, "type") \ macro(unescape, unescape, "unescape") \ macro(uneval, uneval, "uneval") \ macro(unicode, unicode, "unicode") \ @@ -243,9 +259,9 @@ macro(uint8Clamped, uint8Clamped, "uint8Clamped") \ macro(uint16, uint16, "uint16") \ macro(uint32, uint32, "uint32") \ - macro(uint8x16, uint8x16, "Uint8x16") \ - macro(uint16x8, uint16x8, "Uint16x8") \ - macro(uint32x4, uint32x4, "Uint32x4") \ + macro(Uint8x16, Uint8x16, "Uint8x16") \ + macro(Uint16x8, Uint16x8, "Uint16x8") \ + macro(Uint32x4, Uint32x4, "Uint32x4") \ macro(unsized, unsized, "unsized") \ macro(unwatch, unwatch, "unwatch") \ macro(url, url, "url") \ @@ -261,7 +277,9 @@ macro(void0, void0, "(void 0)") \ macro(watch, watch, "watch") \ macro(WeakSet_add, WeakSet_add, "WeakSet_add") \ + macro(weekday, weekday, "weekday") \ macro(writable, writable, "writable") \ + macro(year, year, "year") \ macro(yield, yield, "yield") \ macro(raw, raw, "raw") \ /* Type names must be contiguous and ordered; see js::TypeName. */ \ diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 7eee84f868..cb8f803b12 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -6320,7 +6320,7 @@ static bool DebuggerFrame_getCallee(JSContext* cx, unsigned argc, Value* vp) { THIS_FRAME(cx, argc, vp, "get callee", args, thisobj, frame); - RootedValue calleev(cx, frame.isNonEvalFunctionFrame() ? frame.calleev() : NullValue()); + RootedValue calleev(cx, frame.isFunctionFrame() ? frame.calleev() : NullValue()); if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, &calleev)) return false; args.rval().set(calleev); @@ -6509,7 +6509,7 @@ DebuggerFrame_getScript(JSContext* cx, unsigned argc, Value* vp) Debugger* debug = Debugger::fromChildJSObject(thisobj); RootedObject scriptObject(cx); - if (frame.isFunctionFrame() && !frame.isEvalFrame()) { + if (frame.isFunctionFrame()) { RootedFunction callee(cx, frame.callee()); if (callee->isInterpreted()) { RootedScript script(cx, callee->nonLazyScript()); @@ -6709,8 +6709,7 @@ EvaluateInEnv(JSContext* cx, Handle env, AbstractFramePtr frame, script->setActiveEval(); } - ExecuteType type = !frame ? EXECUTE_GLOBAL : EXECUTE_DEBUG; - return ExecuteKernel(cx, script, *env, NullValue(), type, frame, rval.address()); + return ExecuteKernel(cx, script, *env, NullValue(), frame, rval.address()); } enum EvalBindings { EvalHasExtraBindings = true, EvalWithDefaultBindings = false }; @@ -7244,7 +7243,7 @@ DebuggerObject_getBoundArguments(JSContext* cx, unsigned argc, Value* vp) if (!boundArgs.resize(length)) return false; for (size_t i = 0; i < length; i++) { - boundArgs[i].set(fun->getBoundFunctionArgument(i)); + boundArgs[i].set(fun->getBoundFunctionArgument(cx, i)); if (!dbg->wrapDebuggeeValue(cx, boundArgs[i])) return false; } diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index 5103859146..b436328c0e 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -24,7 +24,7 @@ GeneratorObject::create(JSContext* cx, AbstractFramePtr frame) RootedNativeObject obj(cx); if (frame.script()->isStarGenerator()) { RootedValue pval(cx); - RootedObject fun(cx, frame.fun()); + RootedObject fun(cx, frame.callee()); // FIXME: This would be faster if we could avoid doing a lookup to get // the prototype for the instance. Bug 906600. if (!GetProperty(cx, fun, fun, cx->names().prototype, &pval)) diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 6c56f11659..e24584be05 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -24,7 +24,7 @@ #include "builtin/ModuleObject.h" #include "builtin/Object.h" #include "builtin/RegExp.h" -#include "builtin/SIMD.h" +#include "builtin/SelfHostingDefines.h" #include "builtin/SymbolObject.h" #include "builtin/TypedObject.h" #include "builtin/WeakMapObject.h" @@ -690,8 +690,12 @@ GlobalObject::getSelfHostedFunction(JSContext* cx, Handle global, } RootedFunction fun(cx); - if (!cx->runtime()->createLazySelfHostedFunctionClone(cx, selfHostedName, name, nargs, &fun)) + if (!cx->runtime()->createLazySelfHostedFunctionClone(cx, selfHostedName, name, nargs, + /* proto = */ nullptr, + SingletonObject, &fun)) + { return false; + } funVal.setObject(*fun); return GlobalObject::addIntrinsicValue(cx, global, selfHostedName, funVal); diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 2e94ea97f4..95d077d75d 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -443,11 +443,18 @@ class GlobalObject : public NativeObject } template - SimdTypeDescr* getOrCreateSimdTypeDescr(JSContext* cx) { - RootedObject globalSimdObject(cx, cx->global()->getOrCreateSimdGlobalObject(cx)); + static SimdTypeDescr* + getOrCreateSimdTypeDescr(JSContext* cx, Handle global) { + RootedObject globalSimdObject(cx, global->getOrCreateSimdGlobalObject(cx)); if (!globalSimdObject) return nullptr; - const Value& slot = globalSimdObject->as().getReservedSlot(uint32_t(T::type)); + uint32_t typeSlotIndex(T::type); + if (globalSimdObject->as().getReservedSlot(typeSlotIndex).isUndefined() && + !GlobalObject::initSimdType(cx, global, typeSlotIndex)) + { + return nullptr; + } + const Value& slot = globalSimdObject->as().getReservedSlot(typeSlotIndex); MOZ_ASSERT(slot.isObject()); return &slot.toObject().as(); } @@ -573,18 +580,29 @@ class GlobalObject : public NativeObject static NativeObject* getIntrinsicsHolder(JSContext* cx, Handle global); - Value existingIntrinsicValue(PropertyName* name) { + Value maybeExistingIntrinsicValue(PropertyName* name) { Value slot = getReservedSlot(INTRINSICS); - MOZ_ASSERT(slot.isObject(), "intrinsics holder must already exist"); + // If we're in the self-hosting compartment itself, the + // intrinsics-holder isn't initialized at this point. + if (slot.isUndefined()) + return UndefinedValue(); NativeObject* holder = &slot.toObject().as(); Shape* shape = holder->lookupPure(name); - MOZ_ASSERT(shape, "intrinsic must already have been added to holder"); + if (!shape) + return UndefinedValue(); return holder->getSlot(shape->slot()); } + Value existingIntrinsicValue(PropertyName* name) { + Value val = maybeExistingIntrinsicValue(name); + MOZ_ASSERT(!val.isUndefined(), "intrinsic must already have been added to holder"); + + return val; + } + static bool maybeGetIntrinsicValue(JSContext* cx, Handle global, Handle name, MutableHandleValue vp) @@ -704,6 +722,7 @@ class GlobalObject : public NativeObject // Implemented in builtim/SIMD.cpp static bool initSimdObject(JSContext* cx, Handle global); + static bool initSimdType(JSContext* cx, Handle global, uint32_t simdTypeDescrType); static bool initStandardClasses(JSContext* cx, Handle global); static bool initSelfHostingBuiltins(JSContext* cx, Handle global, diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 61e7d6bc17..7999e52bce 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -138,12 +138,12 @@ js::BoxNonStrictThis(JSContext* cx, const CallReceiver& call) bool js::GetFunctionThis(JSContext* cx, AbstractFramePtr frame, MutableHandleValue res) { - MOZ_ASSERT(frame.isNonEvalFunctionFrame()); - MOZ_ASSERT(!frame.fun()->isArrow()); + MOZ_ASSERT(frame.isFunctionFrame()); + MOZ_ASSERT(!frame.callee()->isArrow()); if (frame.thisArgument().isObject() || - frame.fun()->strict() || - frame.fun()->isSelfHostedBuiltin()) + frame.callee()->strict() || + frame.callee()->isSelfHostedBuiltin()) { res.set(frame.thisArgument()); return true; @@ -293,9 +293,26 @@ MakeDefaultConstructor(JSContext* cx, JSOp op, JSAtom* atom, HandleObject proto) bool derived = op == JSOP_DERIVEDCONSTRUCTOR; MOZ_ASSERT(derived == !!proto); + PropertyName* lookup = derived ? cx->names().DefaultDerivedClassConstructor + : cx->names().DefaultBaseClassConstructor; + + RootedPropertyName selfHostedName(cx, lookup); RootedAtom name(cx, atom == cx->names().empty ? nullptr : atom); - JSNative native = derived ? DefaultDerivedClassConstructor : DefaultClassConstructor; - return NewFunctionWithProto(cx, native, 0, JSFunction::NATIVE_CLASS_CTOR, nullptr, name, proto); + + RootedFunction ctor(cx); + if (!cx->runtime()->createLazySelfHostedFunctionClone(cx, selfHostedName, name, + /* nargs = */ 0, + proto, TenuredObject, &ctor)) + { + return nullptr; + } + + ctor->setIsConstructor(); + ctor->setIsClassConstructor(); + + MOZ_ASSERT(ctor->infallibleIsDefaultClassConstructor(cx)); + + return ctor; } bool @@ -326,7 +343,9 @@ RunState::maybeCreateThisForConstructor(JSContext* cx) InvokeState& invoke = *asInvoke(); if (invoke.constructing() && invoke.args().thisv().isPrimitive()) { RootedObject callee(cx, &invoke.args().callee()); - if (script()->isDerivedClassConstructor()) { + if (callee->isBoundFunction()) { + invoke.args().setThis(MagicValue(JS_UNINITIALIZED_LEXICAL)); + } else if (script()->isDerivedClassConstructor()) { MOZ_ASSERT(callee->as().isClassConstructor()); invoke.args().setThis(MagicValue(JS_UNINITIALIZED_LEXICAL)); } else { @@ -348,14 +367,14 @@ Interpret(JSContext* cx, RunState& state); InterpreterFrame* InvokeState::pushInterpreterFrame(JSContext* cx) { - return cx->runtime()->interpreterStack().pushInvokeFrame(cx, args_, initial_); + return cx->runtime()->interpreterStack().pushInvokeFrame(cx, args_, construct_); } InterpreterFrame* ExecuteState::pushInterpreterFrame(JSContext* cx) { return cx->runtime()->interpreterStack().pushExecuteFrame(cx, script_, newTargetValue_, - scopeChain_, type_, evalInFrame_); + scopeChain_, evalInFrame_); } // MSVC with PGO inlines a lot of functions in RunScript, resulting in large // stack frames and stack overflow issues, see bug 1167883. Turn off PGO to @@ -429,9 +448,6 @@ js::Invoke(JSContext* cx, const CallArgs& args, MaybeConstruct construct) /* Perform GC if necessary on exit from the function. */ AutoGCIfRequested gcIfRequested(cx->runtime()); - /* MaybeConstruct is a subset of InitialFrameFlags */ - InitialFrameFlags initial = (InitialFrameFlags) construct; - unsigned skipForCallee = args.length() + 1 + (construct == CONSTRUCT); if (args.calleev().isPrimitive()) return ReportIsNotFunction(cx, args.calleev(), skipForCallee, construct); @@ -461,7 +477,7 @@ js::Invoke(JSContext* cx, const CallArgs& args, MaybeConstruct construct) return false; /* Run function until JSOP_RETRVAL, JSOP_RETURN or error. */ - InvokeState state(cx, args, initial); + InvokeState state(cx, args, construct); // Check to see if createSingleton flag should be set for this frame. if (construct) { @@ -629,12 +645,11 @@ js::InvokeSetter(JSContext* cx, const Value& thisv, Value fval, HandleValue v) bool js::ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChainArg, - const Value& newTargetValue, ExecuteType type, AbstractFramePtr evalInFrame, + const Value& newTargetValue, AbstractFramePtr evalInFrame, Value* result) { - MOZ_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG); - MOZ_ASSERT_IF(type == EXECUTE_GLOBAL, IsGlobalLexicalScope(&scopeChainArg) || - !IsSyntacticScope(&scopeChainArg)); + MOZ_ASSERT_IF(script->isGlobalCode(), + IsGlobalLexicalScope(&scopeChainArg) || !IsSyntacticScope(&scopeChainArg)); #ifdef DEBUG RootedObject terminatingScope(cx, &scopeChainArg); while (IsSyntacticScope(terminatingScope)) @@ -659,7 +674,7 @@ js::ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChainArg, } probes::StartExecution(script); - ExecuteState state(cx, script, newTargetValue, scopeChainArg, type, evalInFrame, result); + ExecuteState state(cx, script, newTargetValue, scopeChainArg, evalInFrame, result); bool ok = RunScript(cx, state); probes::StopExecution(script); @@ -692,9 +707,7 @@ js::Execute(JSContext* cx, HandleScript script, JSObject& scopeChainArg, Value* } while ((s = s->enclosingScope())); #endif - ExecuteType type = script->module() ? EXECUTE_MODULE : EXECUTE_GLOBAL; - - return ExecuteKernel(cx, script, *scopeChain, NullValue(), type, + return ExecuteKernel(cx, script, *scopeChain, NullValue(), NullFramePtr() /* evalInFrame */, rval); } @@ -1505,11 +1518,11 @@ class ReservedRooted : public ReservedRootedBase } explicit ReservedRooted(Rooted* root) : savedRoot(root) { - *root = js::GCMethods::initial(); + *root = js::GCPolicy::initial(); } ~ReservedRooted() { - *savedRoot = js::GCMethods::initial(); + *savedRoot = js::GCPolicy::initial(); } void set(const T& p) const { *savedRoot = p; } @@ -2798,7 +2811,7 @@ CASE(JSOP_FUNCALL) if (REGS.fp()->hasPushedSPSFrame()) cx->runtime()->spsProfiler.updatePC(script, REGS.pc); - bool construct = (*REGS.pc == JSOP_NEW || *REGS.pc == JSOP_SUPERCALL); + MaybeConstruct construct = MaybeConstruct(*REGS.pc == JSOP_NEW || *REGS.pc == JSOP_SUPERCALL); unsigned argStackSlots = GET_ARGC(REGS.pc) + construct; MOZ_ASSERT(REGS.stackDepth() >= 2u + GET_ARGC(REGS.pc)); @@ -2836,13 +2849,12 @@ CASE(JSOP_FUNCALL) if (!funScript) goto error; - InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE; bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, REGS.pc); TypeMonitorCall(cx, args, construct); mozilla::Maybe state; - state.emplace(cx, args, initial); + state.emplace(cx, args, construct); if (createSingleton) state->setCreateSingleton(); @@ -2876,7 +2888,7 @@ CASE(JSOP_FUNCALL) state.reset(); funScript = fun->nonLazyScript(); - if (!activation.pushInlineFrame(args, funScript, initial)) + if (!activation.pushInlineFrame(args, funScript, construct)) goto error; if (createSingleton) @@ -3361,7 +3373,7 @@ CASE(JSOP_LAMBDA_ARROW) END_CASE(JSOP_LAMBDA_ARROW) CASE(JSOP_CALLEE) - MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame()); + MOZ_ASSERT(REGS.fp()->isFunctionFrame()); PUSH_COPY(REGS.fp()->calleev()); END_CASE(JSOP_CALLEE) @@ -3713,7 +3725,7 @@ END_CASE(JSOP_GENERATOR) CASE(JSOP_INITIALYIELD) { MOZ_ASSERT(!cx->isExceptionPending()); - MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame()); + MOZ_ASSERT(REGS.fp()->isFunctionFrame()); ReservedRooted obj(&rootObject0, ®S.sp[-1].toObject()); POP_RETURN_VALUE(); MOZ_ASSERT(REGS.stackDepth() == 0); @@ -3725,7 +3737,7 @@ CASE(JSOP_INITIALYIELD) CASE(JSOP_YIELD) { MOZ_ASSERT(!cx->isExceptionPending()); - MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame()); + MOZ_ASSERT(REGS.fp()->isFunctionFrame()); ReservedRooted obj(&rootObject0, ®S.sp[-1].toObject()); if (!GeneratorObject::normalSuspend(cx, obj, REGS.fp(), REGS.pc, REGS.spForStackDepth(0), REGS.stackDepth() - 2)) @@ -4845,50 +4857,6 @@ js::ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber, ReportRuntimeLexicalError(cx, errorNumber, name); } -bool -js::DefaultClassConstructor(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - if (!args.isConstructing()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CALL_CLASS_CONSTRUCTOR); - return false; - } - - RootedObject newTarget(cx, &args.newTarget().toObject()); - JSObject* obj = CreateThis(cx, &PlainObject::class_, newTarget); - if (!obj) - return false; - - args.rval().set(ObjectValue(*obj)); - return true; -} - -bool -js::DefaultDerivedClassConstructor(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - if (!args.isConstructing()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CALL_CLASS_CONSTRUCTOR); - return false; - } - - RootedObject fun(cx, &args.callee()); - RootedObject superFun(cx); - if (!GetPrototype(cx, fun, &superFun)) - return false; - - RootedValue fval(cx, ObjectOrNullValue(superFun)); - if (!IsConstructor(fval)) { - ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval, nullptr); - return false; - } - - ConstructArgs constArgs(cx); - if (!FillArgumentsFromArraylike(cx, constArgs, args)) - return false; - return Construct(cx, fval, constArgs, args.newTarget(), args.rval()); -} - void js::ReportRuntimeRedeclaration(JSContext* cx, HandlePropertyName name, frontend::Definition::Kind declKind) diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index 92f68b6744..077f03e252 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -40,11 +40,6 @@ GetFunctionThis(JSContext* cx, AbstractFramePtr frame, MutableHandleValue res); extern bool GetNonSyntacticGlobalThis(JSContext* cx, HandleObject scopeChain, MutableHandleValue res); -enum MaybeConstruct { - NO_CONSTRUCT = INITIAL_NONE, - CONSTRUCT = INITIAL_CONSTRUCT -}; - /* * numToSkip is the number of stack values the expression decompiler should skip * before it reaches |v|. If it's -1, the decompiler will search the stack. @@ -113,8 +108,7 @@ InternalConstructWithProvidedThis(JSContext* cx, HandleValue fval, HandleValue t */ extern bool ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChain, - const Value& newTargetVal, ExecuteType type, AbstractFramePtr evalInFrame, - Value* result); + const Value& newTargetVal, AbstractFramePtr evalInFrame, Value* result); /* Execute a script with the given scopeChain as global code. */ extern bool @@ -169,8 +163,6 @@ class RunState // Eval or global script. class ExecuteState : public RunState { - ExecuteType type_; - RootedValue newTargetValue_; RootedObject scopeChain_; @@ -179,10 +171,8 @@ class ExecuteState : public RunState public: ExecuteState(JSContext* cx, JSScript* script, const Value& newTargetValue, - JSObject& scopeChain, ExecuteType type, AbstractFramePtr evalInFrame, - Value* result) + JSObject& scopeChain, AbstractFramePtr evalInFrame, Value* result) : RunState(cx, Execute, script), - type_(type), newTargetValue_(cx, newTargetValue), scopeChain_(cx, &scopeChain), evalInFrame_(evalInFrame), @@ -191,7 +181,7 @@ class ExecuteState : public RunState Value newTarget() { return newTargetValue_; } JSObject* scopeChain() const { return scopeChain_; } - ExecuteType type() const { return type_; } + bool isDebuggerEval() const { return !!evalInFrame_; } virtual InterpreterFrame* pushInterpreterFrame(JSContext* cx); @@ -205,21 +195,21 @@ class ExecuteState : public RunState class InvokeState : public RunState { const CallArgs& args_; - InitialFrameFlags initial_; + MaybeConstruct construct_; bool createSingleton_; public: - InvokeState(JSContext* cx, const CallArgs& args, InitialFrameFlags initial) + InvokeState(JSContext* cx, const CallArgs& args, MaybeConstruct construct) : RunState(cx, Invoke, args.callee().as().nonLazyScript()), args_(args), - initial_(initial), + construct_(construct), createSingleton_(false) { } bool createSingleton() const { return createSingleton_; } void setCreateSingleton() { createSingleton_ = true; } - bool constructing() const { return InitialFrameFlagsAreConstructing(initial_); } + bool constructing() const { return construct_; } const CallArgs& args() const { return args_; } virtual InterpreterFrame* pushInterpreterFrame(JSContext* cx); diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 02bd61bfd7..9fbcff4e5f 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -984,6 +984,8 @@ struct JSRuntime : public JS::shadow::Runtime, bool isSelfHostingZone(const JS::Zone* zone) const; bool createLazySelfHostedFunctionClone(JSContext* cx, js::HandlePropertyName selfHostedName, js::HandleAtom name, unsigned nargs, + js::HandleObject proto, + js::NewObjectKind newKind, js::MutableHandleFunction fun); bool cloneSelfHostedFunctionScript(JSContext* cx, js::Handle name, js::Handle targetFun); diff --git a/js/src/vm/SavedStacks.cpp b/js/src/vm/SavedStacks.cpp index e6a70da4d3..5888c34a55 100644 --- a/js/src/vm/SavedStacks.cpp +++ b/js/src/vm/SavedStacks.cpp @@ -1130,7 +1130,7 @@ SavedStacks::insertFrames(JSContext* cx, FrameIter& iter, MutableHandleSavedFram if (maxFrameCount == 0) parentIsInCache = iter.hasCachedSavedFrame(); - auto displayAtom = iter.isNonEvalFunctionFrame() ? iter.functionDisplayAtom() : nullptr; + auto displayAtom = iter.isFunctionFrame() ? iter.functionDisplayAtom() : nullptr; if (!stackChain->emplaceBack(location.source(), location.line(), location.column(), diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index fbda915bc6..7e2abebee3 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -251,7 +251,7 @@ CallObject::createForFunction(JSContext* cx, HandleObject enclosing, HandleFunct CallObject* CallObject::createForFunction(JSContext* cx, AbstractFramePtr frame) { - MOZ_ASSERT(frame.isNonEvalFunctionFrame()); + MOZ_ASSERT(frame.isFunctionFrame()); assertSameCompartment(cx, frame); RootedObject scopeChain(cx, frame.scopeChain()); @@ -1451,7 +1451,7 @@ ScopeIter::settle() { // Check for trying to iterate a function frame before the prologue has // created the CallObject, in which case we have to skip. - if (frame_ && frame_.isNonEvalFunctionFrame() && frame_.fun()->needsCallObject() && + if (frame_ && frame_.isFunctionFrame() && frame_.callee()->needsCallObject() && !frame_.hasCallObj()) { MOZ_ASSERT(ssi_.type() == StaticScopeIter::Function); @@ -2608,7 +2608,7 @@ DebugScopes::onPopCall(AbstractFramePtr frame, JSContext* cx) Rooted debugScope(cx, nullptr); - if (frame.fun()->needsCallObject()) { + if (frame.callee()->needsCallObject()) { /* * The frame may be observed before the prologue has created the * CallObject. See ScopeIter::settle. @@ -2616,7 +2616,7 @@ DebugScopes::onPopCall(AbstractFramePtr frame, JSContext* cx) if (!frame.hasCallObj()) return; - if (frame.fun()->isGenerator()) + if (frame.callee()->isGenerator()) return; CallObject& callobj = frame.scopeChain()->as(); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 2ebf0d775e..bb6340511b 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -310,7 +310,7 @@ intrinsic_MakeConstructible(JSContext* cx, unsigned argc, Value* vp) MOZ_ASSERT(args[0].isObject()); MOZ_ASSERT(args[0].toObject().is()); MOZ_ASSERT(args[0].toObject().as().isSelfHostedBuiltin()); - MOZ_ASSERT(args[1].isObject()); + MOZ_ASSERT(args[1].isObjectOrNull()); // Normal .prototype properties aren't enumerable. But for this to clone // correctly, it must be enumerable. @@ -327,6 +327,79 @@ intrinsic_MakeConstructible(JSContext* cx, unsigned argc, Value* vp) return true; } +static bool +intrinsic_MakeDefaultConstructor(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].toObject().as().isSelfHostedBuiltin()); + + RootedFunction ctor(cx, &args[0].toObject().as()); + + ctor->nonLazyScript()->setIsDefaultClassConstructor(); + + args.rval().setUndefined(); + return true; +} + +/* + * Used to mark bound functions as such and make them constructible if the + * target is. + * Also sets the name and correct length, both of which are more costly to + * do in JS. + */ +static bool +intrinsic_FinishBoundFunctionInit(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 4); + MOZ_ASSERT(IsCallable(args[1])); + MOZ_ASSERT(args[2].isNumber()); + MOZ_ASSERT(args[3].isString()); + + RootedFunction bound(cx, &args[0].toObject().as()); + bound->setIsBoundFunction(); + RootedObject targetObj(cx, &args[1].toObject()); + MOZ_ASSERT(bound->getBoundFunctionTarget() == targetObj); + if (targetObj->isConstructor()) + bound->setIsConstructor(); + + // 9.4.1.3 BoundFunctionCreate, steps 2-3,8. + RootedObject proto(cx); + GetPrototype(cx, targetObj, &proto); + if (bound->getProto() != proto) { + if (!SetPrototype(cx, bound, proto)) + return false; + } + + bound->setExtendedSlot(BOUND_FUN_LENGTH_SLOT, args[2]); + MOZ_ASSERT(!bound->hasGuessedAtom()); + RootedAtom name(cx, AtomizeString(cx, args[3].toString())); + if (!name) + return false; + bound->setAtom(name); + + args.rval().setUndefined(); + return true; +} + +static bool +intrinsic_SetPrototype(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(args[0].isObject()); + MOZ_ASSERT(args[1].isObjectOrNull()); + + RootedObject obj(cx, &args[0].toObject()); + RootedObject proto(cx, args[1].toObjectOrNull()); + if (!SetPrototype(cx, obj, proto)) + return false; + + args.rval().setUndefined(); + return true; +} + /* * Used to decompile values in the nearest non-builtin stack frame, falling * back to decompiling in the current frame. Helpful for printing higher-order @@ -1240,6 +1313,26 @@ intrinsic_LocalTZA(JSContext* cx, unsigned argc, Value* vp) return true; } +static bool +intrinsic_ConstructFunction(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(args[0].toObject().is()); + MOZ_ASSERT(args[1].toObject().is()); + + RootedArrayObject argsList(cx, &args[1].toObject().as()); + uint32_t len = argsList->length(); + ConstructArgs constructArgs(cx); + if (!constructArgs.init(len)) + return false; + for (uint32_t index = 0; index < len; index++) + constructArgs[index].set(argsList->getDenseElement(index)); + + return Construct(cx, args[0], constructArgs, args.rval()); +} + + static bool intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp) { @@ -1465,7 +1558,6 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("std_Date_now", date_now, 0,0), JS_FN("std_Date_valueOf", date_valueOf, 0,0), - JS_FN("std_Function_bind", fun_bind, 1,0), JS_FN("std_Function_apply", fun_apply, 2,0), JS_INLINABLE_FN("std_Math_floor", math_floor, 1,0, MathFloor), @@ -1486,6 +1578,7 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("std_Object_getOwnPropertyNames", obj_getOwnPropertyNames, 1,0), JS_FN("std_Object_getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor, 2,0), JS_FN("std_Object_hasOwnProperty", obj_hasOwnProperty, 1,0), + JS_FN("std_Object_setPrototypeOf", intrinsic_SetPrototype, 2,0), JS_FN("std_Object_toString", obj_toString, 0,0), JS_FN("std_Reflect_getPrototypeOf", Reflect_getPrototypeOf, 1,0), @@ -1532,15 +1625,18 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("ToPropertyKey", intrinsic_ToPropertyKey, 1,0), JS_INLINABLE_FN("IsCallable", intrinsic_IsCallable, 1,0, IntrinsicIsCallable), JS_FN("IsConstructor", intrinsic_IsConstructor, 1,0), - JS_FN("OwnPropertyKeys", intrinsic_OwnPropertyKeys, 1,0), + JS_FN("GetBuiltinConstructorImpl", intrinsic_GetBuiltinConstructor, 1,0), + JS_FN("MakeConstructible", intrinsic_MakeConstructible, 2,0), + JS_FN("_ConstructFunction", intrinsic_ConstructFunction, 2,0), JS_FN("ThrowRangeError", intrinsic_ThrowRangeError, 4,0), JS_FN("ThrowTypeError", intrinsic_ThrowTypeError, 4,0), JS_FN("ThrowSyntaxError", intrinsic_ThrowSyntaxError, 4,0), JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1,0), - JS_FN("GetBuiltinConstructorImpl", intrinsic_GetBuiltinConstructor, 1,0), - JS_FN("MakeConstructible", intrinsic_MakeConstructible, 2,0), + JS_FN("OwnPropertyKeys", intrinsic_OwnPropertyKeys, 1,0), + JS_FN("MakeDefaultConstructor", intrinsic_MakeDefaultConstructor, 2,0), JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0), JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0), + JS_FN("_FinishBoundFunctionInit", intrinsic_FinishBoundFunctionInit, 4,0), JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0), JS_FN("LocalTZA", intrinsic_LocalTZA, 0,0), @@ -2049,6 +2145,7 @@ CloneObject(JSContext* cx, HandleNativeObject selfHostedObject) if (selfHostedObject->is()) { RootedFunction selfHostedFunction(cx, &selfHostedObject->as()); bool hasName = selfHostedFunction->atom() != nullptr; + // Arrow functions use the first extended slot for their lexical |this| value. MOZ_ASSERT(!selfHostedFunction->isArrow()); js::gc::AllocKind kind = hasName @@ -2094,6 +2191,7 @@ CloneObject(JSContext* cx, HandleNativeObject selfHostedObject) } if (!clone) return nullptr; + if (!CloneProperties(cx, selfHostedObject, clone)) return nullptr; return clone; @@ -2134,8 +2232,11 @@ CloneValue(JSContext* cx, HandleValue selfHostedValue, MutableHandleValue vp) bool JSRuntime::createLazySelfHostedFunctionClone(JSContext* cx, HandlePropertyName selfHostedName, HandleAtom name, unsigned nargs, + HandleObject proto, NewObjectKind newKind, MutableHandleFunction fun) { + MOZ_ASSERT(newKind != GenericObject); + RootedAtom funName(cx, name); JSFunction* selfHostedFun = getUnclonedSelfHostedFunction(cx, selfHostedName); if (!selfHostedFun) @@ -2147,7 +2248,7 @@ JSRuntime::createLazySelfHostedFunctionClone(JSContext* cx, HandlePropertyName s } fun.set(NewScriptedFunction(cx, nargs, JSFunction::INTERPRETED_LAZY, - funName, gc::AllocKind::FUNCTION_EXTENDED, SingletonObject)); + funName, proto, gc::AllocKind::FUNCTION_EXTENDED, newKind)); if (!fun) return false; fun->setIsSelfHostedBuiltin(); @@ -2165,7 +2266,6 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name, // JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there // aren't any. MOZ_ASSERT(!sourceFun->isGenerator()); - MOZ_ASSERT(sourceFun->nargs() == targetFun->nargs()); MOZ_ASSERT(targetFun->isExtended()); MOZ_ASSERT(targetFun->isInterpretedLazy()); MOZ_ASSERT(targetFun->isSelfHostedBuiltin()); @@ -2185,6 +2285,20 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name, return false; MOZ_ASSERT(!targetFun->isInterpretedLazy()); + // ...rest args don't count as formal args, but are included in nargs. We don't, + // however, want to introduce a flag "has rest args" in the declaration of + // self-hosted functions, so we fix up the flag and the nargs value here. + // Since the target function might have been cloned and relazified before, + // this only happens if the target function isn't marked as having rest + // args. + MOZ_ASSERT(sourceFun->nargs() - sourceFun->hasRest() == + targetFun->nargs() - targetFun->hasRest()); + MOZ_ASSERT_IF(targetFun->hasRest(), sourceFun->hasRest()); + if (sourceFun->hasRest() && !targetFun->hasRest()) { + targetFun->setHasRest(); + targetFun->setArgCount(sourceFun->nargs()); + } + // The target function might have been relazified after its flags changed. targetFun->setFlags(targetFun->flags() | sourceFun->flags()); return true; diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index 51185898d8..5907f7e88f 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -345,7 +345,7 @@ class BaseShape : public gc::TenuredCell DELEGATE = 0x8, NOT_EXTENSIBLE = 0x10, INDEXED = 0x20, - BOUND_FUNCTION = 0x40, + /* (0x40 is unused) */ HAD_ELEMENTS_ACCESS = 0x80, WATCHED = 0x100, ITERATED_SINGLETON = 0x200, diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 55cc0aa7ca..6c3fd1963d 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -44,11 +44,6 @@ IsCacheableNonGlobalScope(JSObject* obj) inline HandleObject InterpreterFrame::scopeChain() const { - MOZ_ASSERT_IF(!(flags_ & HAS_SCOPECHAIN), isFunctionFrame()); - if (!(flags_ & HAS_SCOPECHAIN)) { - scopeChain_ = callee().environment(); - flags_ |= HAS_SCOPECHAIN; - } return HandleObject::fromMarkedLocation(&scopeChain_); } @@ -73,26 +68,20 @@ InterpreterFrame::extensibleLexicalScope() const return NearestEnclosingExtensibleLexicalScope(scopeChain()); } -inline JSCompartment* -InterpreterFrame::compartment() const -{ - MOZ_ASSERT(scopeChain()->compartment() == script()->compartment()); - return scopeChain()->compartment(); -} - inline void InterpreterFrame::initCallFrame(JSContext* cx, InterpreterFrame* prev, jsbytecode* prevpc, Value* prevsp, JSFunction& callee, JSScript* script, Value* argv, - uint32_t nactual, InterpreterFrame::Flags flagsArg) + uint32_t nactual, MaybeConstruct constructing) { - MOZ_ASSERT((flagsArg & ~CONSTRUCTING) == 0); MOZ_ASSERT(callee.nonLazyScript() == script); /* Initialize stack frame members. */ - flags_ = FUNCTION | HAS_SCOPECHAIN | flagsArg; + flags_ = 0; + if (constructing) + flags_ |= CONSTRUCTING; argv_ = argv; - exec.fun = &callee; - u.nactual = nactual; + script_ = script; + nactual_ = nactual; scopeChain_ = callee.environment(); prev_ = prev; prevpc_ = prevpc; @@ -204,20 +193,17 @@ InterpreterFrame::pushOnScopeChain(ScopeObject& scope) MOZ_ASSERT(*scopeChain() == scope.enclosingScope() || *scopeChain() == scope.as().enclosingScope().as().enclosingScope()); scopeChain_ = &scope; - flags_ |= HAS_SCOPECHAIN; } inline void InterpreterFrame::popOffScopeChain() { - MOZ_ASSERT(flags_ & HAS_SCOPECHAIN); scopeChain_ = &scopeChain_->as().enclosingScope(); } inline void InterpreterFrame::replaceInnermostScope(ScopeObject& scope) { - MOZ_ASSERT(flags_ & HAS_SCOPECHAIN); MOZ_ASSERT(scope.enclosingScope() == scopeChain_->as().enclosingScope()); scopeChain_ = &scope; } @@ -225,14 +211,14 @@ InterpreterFrame::replaceInnermostScope(ScopeObject& scope) bool InterpreterFrame::hasCallObj() const { - MOZ_ASSERT(isStrictEvalFrame() || fun()->needsCallObject()); + MOZ_ASSERT(isStrictEvalFrame() || callee().needsCallObject()); return flags_ & HAS_CALL_OBJ; } inline CallObject& InterpreterFrame::callObj() const { - MOZ_ASSERT(fun()->needsCallObject()); + MOZ_ASSERT(callee().needsCallObject()); JSObject* pobj = scopeChain(); while (MOZ_UNLIKELY(!pobj->is())) @@ -281,7 +267,7 @@ InterpreterStack::allocateFrame(JSContext* cx, size_t size) MOZ_ALWAYS_INLINE InterpreterFrame* InterpreterStack::getCallFrame(JSContext* cx, const CallArgs& args, HandleScript script, - InterpreterFrame::Flags* flags, Value** pargv) + MaybeConstruct constructing, Value** pargv) { JSFunction* fun = &args.callee().as(); @@ -298,8 +284,7 @@ InterpreterStack::getCallFrame(JSContext* cx, const CallArgs& args, HandleScript // Pad any missing arguments with |undefined|. MOZ_ASSERT(args.length() < nformal); - bool isConstructing = *flags & InterpreterFrame::CONSTRUCTING; - unsigned nfunctionState = 2 + isConstructing; // callee, |this|, |new.target| + unsigned nfunctionState = 2 + constructing; // callee, |this|, |new.target| nvals += nformal + nfunctionState; uint8_t* buffer = allocateFrame(cx, sizeof(InterpreterFrame) + nvals * sizeof(Value)); @@ -312,7 +297,7 @@ InterpreterStack::getCallFrame(JSContext* cx, const CallArgs& args, HandleScript mozilla::PodCopy(argv, args.base(), 2 + args.length()); SetValueRangeToUndefined(argv + 2 + args.length(), nmissing); - if (isConstructing) + if (constructing) argv[2 + nformal] = args.newTarget(); *pargv = argv + 2; @@ -321,7 +306,7 @@ InterpreterStack::getCallFrame(JSContext* cx, const CallArgs& args, HandleScript MOZ_ALWAYS_INLINE bool InterpreterStack::pushInlineFrame(JSContext* cx, InterpreterRegs& regs, const CallArgs& args, - HandleScript script, InitialFrameFlags initial) + HandleScript script, MaybeConstruct constructing) { RootedFunction callee(cx, &args.callee().as()); MOZ_ASSERT(regs.sp == args.end()); @@ -336,16 +321,16 @@ InterpreterStack::pushInlineFrame(JSContext* cx, InterpreterRegs& regs, const Ca LifoAlloc::Mark mark = allocator_.mark(); - InterpreterFrame::Flags flags = ToFrameFlags(initial); Value* argv; - InterpreterFrame* fp = getCallFrame(cx, args, script, &flags, &argv); + InterpreterFrame* fp = getCallFrame(cx, args, script, constructing, &argv); if (!fp) return false; fp->mark_ = mark; /* Initialize frame, locals, regs. */ - fp->initCallFrame(cx, prev, prevpc, prevsp, *callee, script, argv, args.length(), flags); + fp->initCallFrame(cx, prev, prevpc, prevsp, *callee, script, argv, args.length(), + constructing); regs.prepareToRun(*fp, script); return true; @@ -367,7 +352,7 @@ InterpreterStack::resumeGeneratorCallFrame(JSContext* cx, InterpreterRegs& regs, LifoAlloc::Mark mark = allocator_.mark(); - bool constructing = newTarget.isObject(); + MaybeConstruct constructing = MaybeConstruct(newTarget.isObject()); // Include callee, |this|, and maybe |new.target| unsigned nformal = callee->nargs(); @@ -385,10 +370,8 @@ InterpreterStack::resumeGeneratorCallFrame(JSContext* cx, InterpreterRegs& regs, argv[nformal] = newTarget; InterpreterFrame* fp = reinterpret_cast(argv + nformal + constructing); - InterpreterFrame::Flags flags = constructing ? ToFrameFlags(INITIAL_CONSTRUCT) - : ToFrameFlags(INITIAL_NONE); fp->mark_ = mark; - fp->initCallFrame(cx, prev, prevpc, prevsp, *callee, script, argv, 0, flags); + fp->initCallFrame(cx, prev, prevpc, prevsp, *callee, script, argv, 0, constructing); fp->resumeGeneratorFrame(scopeChain); regs.prepareToRun(*fp, script); @@ -571,13 +554,13 @@ AbstractFramePtr::createSingleton() const } inline bool -AbstractFramePtr::isFunctionFrame() const +AbstractFramePtr::isGlobalFrame() const { if (isInterpreterFrame()) - return asInterpreterFrame()->isFunctionFrame(); + return asInterpreterFrame()->isGlobalFrame(); if (isBaselineFrame()) - return asBaselineFrame()->isFunctionFrame(); - return asRematerializedFrame()->isFunctionFrame(); + return asBaselineFrame()->isGlobalFrame(); + return asRematerializedFrame()->isGlobalFrame(); } inline bool @@ -590,16 +573,6 @@ AbstractFramePtr::isModuleFrame() const return asRematerializedFrame()->isModuleFrame(); } -inline bool -AbstractFramePtr::isGlobalFrame() const -{ - if (isInterpreterFrame()) - return asInterpreterFrame()->isGlobalFrame(); - if (isBaselineFrame()) - return asBaselineFrame()->isGlobalFrame(); - return asRematerializedFrame()->isGlobalFrame(); -} - inline bool AbstractFramePtr::isEvalFrame() const { @@ -677,7 +650,7 @@ AbstractFramePtr::unsetIsDebuggee() inline bool AbstractFramePtr::hasArgs() const { - return isNonEvalFunctionFrame(); + return isFunctionFrame(); } inline JSScript* @@ -690,26 +663,6 @@ AbstractFramePtr::script() const return asRematerializedFrame()->script(); } -inline JSFunction* -AbstractFramePtr::fun() const -{ - if (isInterpreterFrame()) - return asInterpreterFrame()->fun(); - if (isBaselineFrame()) - return asBaselineFrame()->fun(); - return asRematerializedFrame()->fun(); -} - -inline JSFunction* -AbstractFramePtr::maybeFun() const -{ - if (isInterpreterFrame()) - return asInterpreterFrame()->maybeFun(); - if (isBaselineFrame()) - return asBaselineFrame()->maybeFun(); - return asRematerializedFrame()->maybeFun(); -} - inline JSFunction* AbstractFramePtr::callee() const { @@ -731,13 +684,13 @@ AbstractFramePtr::calleev() const } inline bool -AbstractFramePtr::isNonEvalFunctionFrame() const +AbstractFramePtr::isFunctionFrame() const { if (isInterpreterFrame()) - return asInterpreterFrame()->isNonEvalFunctionFrame(); + return asInterpreterFrame()->isFunctionFrame(); if (isBaselineFrame()) - return asBaselineFrame()->isNonEvalFunctionFrame(); - return asRematerializedFrame()->isNonEvalFunctionFrame(); + return asBaselineFrame()->isFunctionFrame(); + return asRematerializedFrame()->isFunctionFrame(); } inline bool @@ -982,20 +935,18 @@ InterpreterActivation::~InterpreterActivation() while (regs_.fp() != entryFrame_) popInlineFrame(regs_.fp()); - JSContext* cx = cx_->asJSContext(); - MOZ_ASSERT(oldFrameCount_ == cx->runtime()->interpreterStack().frameCount_); - MOZ_ASSERT_IF(oldFrameCount_ == 0, cx->runtime()->interpreterStack().allocator_.used() == 0); + MOZ_ASSERT(oldFrameCount_ == cx_->runtime()->interpreterStack().frameCount_); + MOZ_ASSERT_IF(oldFrameCount_ == 0, cx_->runtime()->interpreterStack().allocator_.used() == 0); if (entryFrame_) - cx->runtime()->interpreterStack().releaseFrame(entryFrame_); + cx_->runtime()->interpreterStack().releaseFrame(entryFrame_); } inline bool InterpreterActivation::pushInlineFrame(const CallArgs& args, HandleScript script, - InitialFrameFlags initial) + MaybeConstruct constructing) { - JSContext* cx = cx_->asJSContext(); - if (!cx->runtime()->interpreterStack().pushInlineFrame(cx, regs_, args, script, initial)) + if (!cx_->runtime()->interpreterStack().pushInlineFrame(cx_, regs_, args, script, constructing)) return false; MOZ_ASSERT(regs_.fp()->script()->compartment() == compartment()); return true; @@ -1008,15 +959,15 @@ InterpreterActivation::popInlineFrame(InterpreterFrame* frame) MOZ_ASSERT(regs_.fp() == frame); MOZ_ASSERT(regs_.fp() != entryFrame_); - cx_->asJSContext()->runtime()->interpreterStack().popInlineFrame(regs_); + cx_->runtime()->interpreterStack().popInlineFrame(regs_); } inline bool InterpreterActivation::resumeGeneratorFrame(HandleFunction callee, HandleValue newTarget, HandleObject scopeChain) { - InterpreterStack& stack = cx_->asJSContext()->runtime()->interpreterStack(); - if (!stack.resumeGeneratorCallFrame(cx_->asJSContext(), regs_, callee, newTarget, scopeChain)) + InterpreterStack& stack = cx_->runtime()->interpreterStack(); + if (!stack.resumeGeneratorCallFrame(cx_, regs_, callee, newTarget, scopeChain)) return false; MOZ_ASSERT(regs_.fp()->script()->compartment() == compartment_); diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 740984fbaf..10d86e0133 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -33,61 +33,27 @@ using mozilla::PodCopy; void InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script, AbstractFramePtr evalInFramePrev, - const Value& newTargetValue, HandleObject scopeChain, - ExecuteType type) + const Value& newTargetValue, HandleObject scopeChain) { - /* - * See encoding of ExecuteType. When GLOBAL isn't set, we are executing a - * script in the context of another frame and the frame type is determined - * by the context. - */ - flags_ = type | HAS_SCOPECHAIN; - - JSObject* callee = nullptr; + flags_ = 0; + script_ = script; // newTarget = NullValue is an initial sentinel for "please fill me in from the stack". // It should never be passed from Ion code. RootedValue newTarget(cx, newTargetValue); - if (!(flags_ & (GLOBAL | MODULE))) { + if (script->isDirectEvalInFunction()) { if (evalInFramePrev) { - MOZ_ASSERT(evalInFramePrev.isFunctionFrame() || evalInFramePrev.isGlobalFrame()); - if (evalInFramePrev.isFunctionFrame()) { - callee = evalInFramePrev.callee(); - if (newTarget.isNull()) - newTarget = evalInFramePrev.newTarget(); - flags_ |= FUNCTION; - } else { - flags_ |= GLOBAL; - } + if (newTarget.isNull() && evalInFramePrev.script()->functionOrCallerFunction()) + newTarget = evalInFramePrev.newTarget(); } else { FrameIter iter(cx); - MOZ_ASSERT(iter.isFunctionFrame() || iter.isGlobalFrame()); MOZ_ASSERT(!iter.isWasm()); - if (iter.isFunctionFrame()) { - if (newTarget.isNull()) - newTarget = iter.newTarget(); - callee = iter.callee(cx); - flags_ |= FUNCTION; - } else { - flags_ |= GLOBAL; - } + if (newTarget.isNull() && iter.script()->functionOrCallerFunction()) + newTarget = iter.newTarget(); } } - Value* dstvp = (Value*)this - 2; - - if (isFunctionFrame()) { - dstvp[1] = ObjectValue(*callee); - exec.fun = &callee->as(); - u.evalScript = script; - } else { - MOZ_ASSERT(isGlobalFrame() || isModuleFrame()); - dstvp[1] = NullValue(); - exec.script = script; -#ifdef DEBUG - u.evalScript = (JSScript*)0xbad; -#endif - } + Value* dstvp = (Value*)this - 1; dstvp[0] = newTarget; scopeChain_ = scopeChain.get(); @@ -126,8 +92,8 @@ InterpreterFrame::copyRawFrameSlots(AutoValueVector* vec) JSObject* InterpreterFrame::createRestParameter(JSContext* cx) { - MOZ_ASSERT(fun()->hasRest()); - unsigned nformal = fun()->nargs() - 1, nactual = numActualArgs(); + MOZ_ASSERT(callee().hasRest()); + unsigned nformal = callee().nargs() - 1, nactual = numActualArgs(); unsigned nrest = (nactual > nformal) ? nactual - nformal : 0; Value* restvp = argv() + nformal; return ObjectGroup::newArrayObject(cx, restvp, nrest, GenericObject, @@ -208,7 +174,6 @@ InterpreterFrame::prologue(JSContext* cx) { RootedScript script(cx, this->script()); - MOZ_ASSERT(isModuleFrame() == !!script->module()); MOZ_ASSERT(cx->interpreterRegs().pc == script->code()); if (isEvalFrame()) { @@ -248,12 +213,14 @@ InterpreterFrame::prologue(JSContext* cx) if (isModuleFrame()) return probes::EnterScript(cx, script, nullptr, this); - MOZ_ASSERT(isNonEvalFunctionFrame()); - if (fun()->needsCallObject() && !initFunctionScopeObjects(cx)) + MOZ_ASSERT(isFunctionFrame()); + if (callee().needsCallObject() && !initFunctionScopeObjects(cx)) return false; if (isConstructing()) { - if (script->isDerivedClassConstructor()) { + if (callee().isBoundFunction()) { + thisArgument() = MagicValue(JS_UNINITIALIZED_LEXICAL); + } else if (script->isDerivedClassConstructor()) { MOZ_ASSERT(callee().isClassConstructor()); thisArgument() = MagicValue(JS_UNINITIALIZED_LEXICAL); } else if (thisArgument().isPrimitive()) { @@ -300,10 +267,10 @@ InterpreterFrame::epilogue(JSContext* cx) if (isModuleFrame()) return; - MOZ_ASSERT(isNonEvalFunctionFrame()); + MOZ_ASSERT(isFunctionFrame()); - if (fun()->needsCallObject()) { - MOZ_ASSERT_IF(hasCallObj() && !fun()->isGenerator(), + if (callee().needsCallObject()) { + MOZ_ASSERT_IF(hasCallObj() && !callee().isGenerator(), scopeChain()->as().callee().nonLazyScript() == script); } else { AssertDynamicScopeMatchesStaticScope(cx, script, scopeChain()); @@ -312,7 +279,7 @@ InterpreterFrame::epilogue(JSContext* cx) if (MOZ_UNLIKELY(cx->compartment()->isDebuggee())) DebugScopes::onPopCall(this, cx); - if (!fun()->isGenerator() && + if (!callee().isGenerator() && isConstructing() && thisArgument().isObject() && returnValue().isPrimitive()) @@ -362,7 +329,6 @@ InterpreterFrame::pushBlock(JSContext* cx, StaticBlockObject& block) bool InterpreterFrame::freshenBlock(JSContext* cx) { - MOZ_ASSERT(flags_ & HAS_SCOPECHAIN); Rooted block(cx, &scopeChain_->as()); ClonedBlockObject* fresh = ClonedBlockObject::clone(cx, block); if (!fresh) @@ -397,17 +363,10 @@ InterpreterFrame::mark(JSTracer* trc) * this path. However, generators use a special write barrier when the stack * frame is copied to the floating frame. Therefore, no barrier is needed. */ - if (flags_ & HAS_SCOPECHAIN) - TraceManuallyBarrieredEdge(trc, &scopeChain_, "scope chain"); + TraceManuallyBarrieredEdge(trc, &scopeChain_, "scope chain"); if (flags_ & HAS_ARGS_OBJ) TraceManuallyBarrieredEdge(trc, &argsObj_, "arguments"); - if (isFunctionFrame()) { - TraceManuallyBarrieredEdge(trc, &exec.fun, "fun"); - if (isEvalFrame()) - TraceManuallyBarrieredEdge(trc, &u.evalScript, "eval script"); - } else { - TraceManuallyBarrieredEdge(trc, &exec.script, "script"); - } + TraceManuallyBarrieredEdge(trc, &script_, "script"); if (trc->isMarkingTracer()) script()->compartment()->zone()->active = true; if (hasReturnValue()) @@ -426,6 +385,20 @@ InterpreterFrame::markValues(JSTracer* trc, Value* sp, jsbytecode* pc) { MOZ_ASSERT(sp >= slots()); + if (hasArgs()) { + // Trace the callee and |this|. When we're doing a moving GC, we + // need to fix up the callee pointer before we use it below, under + // numFormalArgs() and script(). + TraceRootRange(trc, 2, argv_ - 2, "fp callee and this"); + + // Trace arguments. + unsigned argc = Max(numActualArgs(), numFormalArgs()); + TraceRootRange(trc, argc + isConstructing(), argv_, "fp argv"); + } else { + // Mark newTarget. + TraceRoot(trc, ((Value*)this) - 1, "stack newTarget"); + } + JSScript* script = this->script(); size_t nfixed = script->nfixed(); size_t nlivefixed = script->calculateLiveFixed(pc); @@ -444,15 +417,6 @@ InterpreterFrame::markValues(JSTracer* trc, Value* sp, jsbytecode* pc) // Mark live locals. markValues(trc, 0, nlivefixed); } - - if (hasArgs()) { - // Mark callee, |this| and arguments. - unsigned argc = Max(numActualArgs(), numFormalArgs()); - TraceRootRange(trc, argc + 2 + isConstructing(), argv_ - 2, "fp argv"); - } else { - // Mark callee and newTarget - TraceRootRange(trc, 2, ((Value*)this) - 2, "stack callee and newTarget"); - } } static void @@ -489,39 +453,38 @@ InterpreterRegs::setToEndOfScript() /*****************************************************************************/ InterpreterFrame* -InterpreterStack::pushInvokeFrame(JSContext* cx, const CallArgs& args, InitialFrameFlags initial) +InterpreterStack::pushInvokeFrame(JSContext* cx, const CallArgs& args, MaybeConstruct constructing) { LifoAlloc::Mark mark = allocator_.mark(); RootedFunction fun(cx, &args.callee().as()); RootedScript script(cx, fun->nonLazyScript()); - InterpreterFrame::Flags flags = ToFrameFlags(initial); Value* argv; - InterpreterFrame* fp = getCallFrame(cx, args, script, &flags, &argv); + InterpreterFrame* fp = getCallFrame(cx, args, script, constructing, &argv); if (!fp) return nullptr; fp->mark_ = mark; - fp->initCallFrame(cx, nullptr, nullptr, nullptr, *fun, script, argv, args.length(), flags); + fp->initCallFrame(cx, nullptr, nullptr, nullptr, *fun, script, argv, args.length(), + constructing); return fp; } InterpreterFrame* InterpreterStack::pushExecuteFrame(JSContext* cx, HandleScript script, const Value& newTargetValue, - HandleObject scopeChain, ExecuteType type, - AbstractFramePtr evalInFrame) + HandleObject scopeChain, AbstractFramePtr evalInFrame) { LifoAlloc::Mark mark = allocator_.mark(); - unsigned nvars = 2 /* callee, newTarget */ + script->nslots(); + unsigned nvars = 1 /* newTarget */ + script->nslots(); uint8_t* buffer = allocateFrame(cx, sizeof(InterpreterFrame) + nvars * sizeof(Value)); if (!buffer) return nullptr; - InterpreterFrame* fp = reinterpret_cast(buffer + 2 * sizeof(Value)); + InterpreterFrame* fp = reinterpret_cast(buffer + 1 * sizeof(Value)); fp->mark_ = mark; - fp->initExecuteFrame(cx, script, evalInFrame, newTargetValue, scopeChain, type); + fp->initExecuteFrame(cx, script, evalInFrame, newTargetValue, scopeChain); fp->initLocals(); return fp; @@ -578,7 +541,7 @@ FrameIter::settleOnActivation() // If the caller supplied principals, only show activations which are subsumed (of the same // origin or of an origin accessible) by these principals. if (data_.principals_) { - JSContext* cx = data_.cx_->asJSContext(); + JSContext* cx = data_.cx_; if (JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes) { if (!subsumes(data_.principals_, activation->compartment()->principals())) { ++data_.activations_; @@ -867,44 +830,6 @@ FrameIter::compartment() const MOZ_CRASH("Unexpected state"); } -bool -FrameIter::isFunctionFrame() const -{ - switch (data_.state_) { - case DONE: - break; - case INTERP: - return interpFrame()->isFunctionFrame(); - case JIT: - MOZ_ASSERT(data_.jitFrames_.isScripted()); - if (data_.jitFrames_.isBaselineJS()) - return data_.jitFrames_.isFunctionFrame(); - return ionInlineFrames_.isFunctionFrame(); - case WASM: - return true; - } - MOZ_CRASH("Unexpected state"); -} - -bool -FrameIter::isGlobalFrame() const -{ - switch (data_.state_) { - case DONE: - break; - case INTERP: - return interpFrame()->isGlobalFrame(); - case JIT: - if (data_.jitFrames_.isBaselineJS()) - return data_.jitFrames_.baselineFrame()->isGlobalFrame(); - MOZ_ASSERT(!script()->isForEval()); - return !script()->functionNonDelazifying(); - case WASM: - return false; - } - MOZ_CRASH("Unexpected state"); -} - bool FrameIter::isEvalFrame() const { @@ -925,16 +850,18 @@ FrameIter::isEvalFrame() const } bool -FrameIter::isNonEvalFunctionFrame() const +FrameIter::isFunctionFrame() const { MOZ_ASSERT(!done()); switch (data_.state_) { case DONE: break; case INTERP: - return interpFrame()->isNonEvalFunctionFrame(); + return interpFrame()->isFunctionFrame(); case JIT: - return !isEvalFrame() && isFunctionFrame(); + if (data_.jitFrames_.isBaselineJS()) + return data_.jitFrames_.baselineFrame()->isFunctionFrame(); + return script()->functionNonDelazifying(); case WASM: return true; } @@ -944,7 +871,7 @@ FrameIter::isNonEvalFunctionFrame() const JSAtom* FrameIter::functionDisplayAtom() const { - MOZ_ASSERT(isNonEvalFunctionFrame()); + MOZ_ASSERT(isFunctionFrame()); switch (data_.state_) { case DONE: @@ -1290,7 +1217,7 @@ FrameIter::argsObj() const Value FrameIter::thisArgument(JSContext* cx) const { - MOZ_ASSERT(isNonEvalFunctionFrame()); + MOZ_ASSERT(isFunctionFrame()); switch (data_.state_) { case DONE: @@ -1462,7 +1389,7 @@ ActivationEntryMonitor::ActivationEntryMonitor(JSContext* cx, InterpreterFrame* RootedValue stack(cx, asyncStack(cx)); RootedString asyncCause(cx, cx->runtime()->asyncCauseForNewActivations); if (entryFrame->isFunctionFrame()) - entryMonitor_->Entry(cx, entryFrame->fun(), stack, asyncCause); + entryMonitor_->Entry(cx, &entryFrame->callee(), stack, asyncCause); else entryMonitor_->Entry(cx, entryFrame->script(), stack, asyncCause); } @@ -1762,10 +1689,9 @@ WasmActivation::~WasmActivation() MOZ_ASSERT(module_.activation() == this); module_.activation() = prevWasmForModule_; - JSContext* cx = cx_->asJSContext(); - MOZ_ASSERT(cx->runtime()->wasmActivationStack_ == this); + MOZ_ASSERT(cx_->runtime()->wasmActivationStack_ == this); - cx->runtime()->wasmActivationStack_ = prevWasm_; + cx_->runtime()->wasmActivationStack_ = prevWasm_; } InterpreterFrameIterator& diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 62ec7b760a..a03f67e7c8 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -202,24 +202,21 @@ class AbstractFramePtr inline JSCompartment* compartment() const; inline bool hasCallObj() const; - inline bool isFunctionFrame() const; - inline bool isModuleFrame() const; inline bool isGlobalFrame() const; + inline bool isModuleFrame() const; inline bool isEvalFrame() const; inline bool isDebuggerEvalFrame() const; inline bool hasCachedSavedFrame() const; inline void setHasCachedSavedFrame(); inline JSScript* script() const; - inline JSFunction* fun() const; - inline JSFunction* maybeFun() const; inline JSFunction* callee() const; inline Value calleev() const; inline Value& thisArgument() const; inline Value newTarget() const; - inline bool isNonEvalFunctionFrame() const; + inline bool isFunctionFrame() const; inline bool isNonStrictDirectEvalFrame() const; inline bool isStrictEvalFrame() const; @@ -271,109 +268,59 @@ class NullFramePtr : public AbstractFramePtr { } }; -/*****************************************************************************/ - -/* Flags specified for a frame as it is constructed. */ -enum InitialFrameFlags { - INITIAL_NONE = 0, - INITIAL_CONSTRUCT = 0x20, /* == InterpreterFrame::CONSTRUCTING, asserted below */ -}; - -enum ExecuteType { - EXECUTE_GLOBAL = 0x1, /* == InterpreterFrame::GLOBAL */ - EXECUTE_MODULE = 0x4, /* == InterpreterFrame::GLOBAL */ - EXECUTE_DIRECT_EVAL = 0x8, /* == InterpreterFrame::EVAL */ - EXECUTE_INDIRECT_EVAL = 0x9, /* == InterpreterFrame::GLOBAL | EVAL */ - EXECUTE_DEBUG = 0x18, /* == InterpreterFrame::EVAL | DEBUGGER_EVAL */ -}; +enum MaybeConstruct { NO_CONSTRUCT = false, CONSTRUCT = true }; /*****************************************************************************/ class InterpreterFrame { - public: enum Flags : uint32_t { - /* Primary frame type */ - GLOBAL = 0x1, /* frame pushed for a global script */ - FUNCTION = 0x2, /* frame pushed for a scripted call */ - MODULE = 0x4, /* frame pushed for a module */ + CONSTRUCTING = 0x1, /* frame is for a constructor invocation */ - /* Frame subtypes */ - EVAL = 0x8, /* frame pushed for eval() or debugger eval */ - - - /* - * Frame pushed for debugger eval. - * - Don't bother to JIT it, because it's probably short-lived. - * - It is required to have a scope chain object outside the - * js::ScopeObject hierarchy: either a global object, or a - * DebugScopeObject (not a ScopeObject, despite the name) - * - If evalInFramePrev_ is set, then this frame was created for an - * "eval in frame" call, which can push a successor to any live - * frame; so its logical "prev" frame is not necessarily the - * previous frame in memory. Iteration should treat - * evalInFramePrev_ as this frame's previous frame. - */ - DEBUGGER_EVAL = 0x10, - - CONSTRUCTING = 0x20, /* frame is for a constructor invocation */ - - RESUMED_GENERATOR = 0x40, /* frame is for a resumed generator invocation */ - - /* (0x80 is unused) */ + RESUMED_GENERATOR = 0x2, /* frame is for a resumed generator invocation */ /* Function prologue state */ - HAS_CALL_OBJ = 0x100, /* CallObject created for needsCallObject function */ - HAS_ARGS_OBJ = 0x200, /* ArgumentsObject created for needsArgsObj script */ + HAS_CALL_OBJ = 0x4, /* CallObject created for needsCallObject function */ + HAS_ARGS_OBJ = 0x8, /* ArgumentsObject created for needsArgsObj script */ /* Lazy frame initialization */ - HAS_RVAL = 0x800, /* frame has rval_ set */ - HAS_SCOPECHAIN = 0x1000, /* frame has scopeChain_ set */ + HAS_RVAL = 0x10, /* frame has rval_ set */ /* Debugger state */ - PREV_UP_TO_DATE = 0x4000, /* see DebugScopes::updateLiveScopes */ + PREV_UP_TO_DATE = 0x20, /* see DebugScopes::updateLiveScopes */ /* * See comment above 'isDebuggee' in jscompartment.h for explanation of * invariants of debuggee compartments, scripts, and frames. */ - DEBUGGEE = 0x8000, /* Execution is being observed by Debugger */ + DEBUGGEE = 0x40, /* Execution is being observed by Debugger */ /* Used in tracking calls and profiling (see vm/SPSProfiler.cpp) */ - HAS_PUSHED_SPS_FRAME = 0x10000, /* SPS was notified of enty */ - + HAS_PUSHED_SPS_FRAME = 0x80, /* SPS was notified of enty */ /* * If set, we entered one of the JITs and ScriptFrameIter should skip * this frame. */ - RUNNING_IN_JIT = 0x20000, + RUNNING_IN_JIT = 0x100, /* Miscellaneous state. */ - CREATE_SINGLETON = 0x40000, /* Constructed |this| object should be singleton. */ + CREATE_SINGLETON = 0x200, /* Constructed |this| object should be singleton. */ /* * If set, this frame has been on the stack when * |js::SavedStacks::saveCurrentStack| was called, and so there is a * |js::SavedFrame| object cached for this frame. */ - HAS_CACHED_SAVED_FRAME = 0x80000, + HAS_CACHED_SAVED_FRAME = 0x400, }; - private: mutable uint32_t flags_; /* bits described by Flags */ - union { /* describes what code is executing in a */ - JSScript* script; /* global frame */ - JSFunction* fun; /* function frame, pre GetScopeChain */ - ModuleObject* module; /* module frame */ - } exec; - union { /* describes the arguments of a function */ - unsigned nactual; /* for non-eval frames */ - JSScript* evalScript; /* the script of an eval-in-function */ - } u; - mutable JSObject* scopeChain_; /* if HAS_SCOPECHAIN, current scope chain */ + uint32_t nactual_; /* number of actual arguments, for function frames */ + JSScript* script_; /* the script we're executing */ + JSObject* scopeChain_; /* current scope chain */ Value rval_; /* if HAS_RVAL, return value of the frame */ - ArgumentsObject* argsObj_; /* if HAS_ARGS_OBJ, the call's arguments object */ + ArgumentsObject* argsObj_; /* if HAS_ARGS_OBJ, the call's arguments object */ /* * Previous frame and its pc and sp. Always nullptr for @@ -422,11 +369,11 @@ class InterpreterFrame /* Used for Invoke and Interpret. */ void initCallFrame(JSContext* cx, InterpreterFrame* prev, jsbytecode* prevpc, Value* prevsp, JSFunction& callee, JSScript* script, Value* argv, uint32_t nactual, - InterpreterFrame::Flags flags); + MaybeConstruct constructing); /* Used for global and eval frames. */ void initExecuteFrame(JSContext* cx, HandleScript script, AbstractFramePtr prev, - const Value& newTargetValue, HandleObject scopeChain, ExecuteType type); + const Value& newTargetValue, HandleObject scopeChain); public: /* @@ -465,48 +412,29 @@ class InterpreterFrame /* * Stack frame type * - * A stack frame may have one of three types, which determines which + * A stack frame may have one of four types, which determines which * members of the frame may be accessed and other invariants: * - * global frame: execution of global code or an eval in global code - * function frame: execution of function code or an eval in a function - * module frame: execution of a module + * global frame: execution of global code + * function frame: execution of function code + * module frame: execution of a module + * eval frame: execution of eval code */ - bool isFunctionFrame() const { - return !!(flags_ & FUNCTION); - } - bool isGlobalFrame() const { - return !!(flags_ & GLOBAL); + return script_->isGlobalCode(); } bool isModuleFrame() const { - return !!(flags_ & MODULE); + return script_->module(); } - /* - * Eval frames - * - * As noted above, global and function frames may optionally be 'eval - * frames'. Eval code shares its parent's arguments which means that the - * arg-access members of InterpreterFrame may not be used for eval frames. - * Search for 'hasArgs' below for more details. - * - * A further sub-classification of eval frames is whether the frame was - * pushed for an ES5 strict-mode eval(). - */ - bool isEvalFrame() const { - return flags_ & EVAL; + return script_->isForEval(); } - bool isEvalInFunction() const { - return (flags_ & (EVAL | FUNCTION)) == (EVAL | FUNCTION); - } - - bool isNonEvalFunctionFrame() const { - return (flags_ & (FUNCTION | EVAL)) == FUNCTION; + bool isFunctionFrame() const { + return script_->functionNonDelazifying(); } inline bool isStrictEvalFrame() const { @@ -565,15 +493,15 @@ class InterpreterFrame inline Value& unaliasedLocal(uint32_t i); - bool hasArgs() const { return isNonEvalFunctionFrame(); } + bool hasArgs() const { return isFunctionFrame(); } inline Value& unaliasedFormal(unsigned i, MaybeCheckAliasing = CHECK_ALIASING); inline Value& unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING); template inline void unaliasedForEachActual(Op op); bool copyRawFrameSlots(AutoValueVector* v); - unsigned numFormalArgs() const { MOZ_ASSERT(hasArgs()); return fun()->nargs(); } - unsigned numActualArgs() const { MOZ_ASSERT(hasArgs()); return u.nactual; } + unsigned numFormalArgs() const { MOZ_ASSERT(hasArgs()); return callee().nargs(); } + unsigned numActualArgs() const { MOZ_ASSERT(hasArgs()); return nactual_; } /* Watch out, this exposes a pointer to the unaliased formal arg array. */ Value* argv() const { MOZ_ASSERT(hasArgs()); return argv_; } @@ -648,23 +576,12 @@ class InterpreterFrame /* * Script * - * All function and global frames have an associated JSScript which holds - * the bytecode being executed for the frame. This script/bytecode does - * not reflect any inlining that has been performed by the method JIT. - * If other frames were inlined into this one, the script/pc reflect the - * point of the outermost call. Inlined frame invariants: - * - * - Inlined frames have the same scope chain as the outer frame. - * - Inlined frames have the same strictness as the outer frame. - * - Inlined frames can only make calls to other JIT frames associated with - * the same VMFrame. Other calls force expansion of the inlined frames. + * All frames have an associated JSScript which holds the bytecode being + * executed for the frame. */ JSScript* script() const { - if (isFunctionFrame()) - return isEvalFrame() ? u.evalScript : fun()->nonLazyScript(); - MOZ_ASSERT(isGlobalFrame() || isModuleFrame()); - return exec.script; + return script_; } /* Return the previous frame's pc. */ @@ -679,35 +596,6 @@ class InterpreterFrame return prevsp_; } - /* - * Function - * - * All function frames have an associated interpreted JSFunction. The - * function returned by fun() and maybeFun() is not necessarily the - * original canonical function which the frame's script was compiled - * against. - */ - - JSFunction* fun() const { - MOZ_ASSERT(isFunctionFrame()); - return exec.fun; - } - - JSFunction* maybeFun() const { - return isFunctionFrame() ? fun() : nullptr; - } - - /* Module */ - - ModuleObject* module() const { - MOZ_ASSERT(isModuleFrame()); - return exec.module; - } - - ModuleObject* maybeModule() const { - return isModuleFrame() ? module() : nullptr; - } - /* * Return the 'this' argument passed to a non-eval function frame. This is * not necessarily the frame's this-binding, for instance non-strict @@ -715,7 +603,7 @@ class InterpreterFrame * return the original, unboxed Value. */ Value& thisArgument() const { - MOZ_ASSERT(isNonEvalFunctionFrame()); + MOZ_ASSERT(isFunctionFrame()); return argv()[-1]; } @@ -723,9 +611,7 @@ class InterpreterFrame * Callee * * Only function frames have a callee. An eval frame in a function has the - * same callee as its containing function frame. maybeCalleev can be used - * to return a value that is either the callee object (for function frames) or - * null (for global frames). + * same callee as its containing function frame. */ JSFunction& callee() const { @@ -735,21 +621,6 @@ class InterpreterFrame const Value& calleev() const { MOZ_ASSERT(isFunctionFrame()); - return mutableCalleev(); - } - - const Value& maybeCalleev() const { - Value& calleev = flags_ & (EVAL | GLOBAL) - ? ((Value*)this)[-1] - : argv()[-2]; - MOZ_ASSERT(calleev.isObjectOrNull()); - return calleev; - } - - Value& mutableCalleev() const { - MOZ_ASSERT(isFunctionFrame()); - if (isEvalFrame()) - return ((Value*)this)[-1]; return argv()[-2]; } @@ -761,9 +632,10 @@ class InterpreterFrame * frame. */ Value newTarget() const { - MOZ_ASSERT(isFunctionFrame()); if (isEvalFrame()) - return ((Value*)this)[-2]; + return ((Value*)this)[-1]; + + MOZ_ASSERT(isFunctionFrame()); if (callee().isArrow()) return callee().getExtendedSlot(FunctionExtended::ARROW_NEWTARGET_SLOT); @@ -775,15 +647,6 @@ class InterpreterFrame return UndefinedValue(); } - /* - * Frame compartment - * - * A stack frame's compartment is the frame's containing context's - * compartment when the frame was pushed. - */ - - inline JSCompartment* compartment() const; - /* Profiler flags */ bool hasPushedSPSFrame() { @@ -826,8 +689,8 @@ class InterpreterFrame void resumeGeneratorFrame(JSObject* scopeChain) { MOZ_ASSERT(script()->isGenerator()); - MOZ_ASSERT(isNonEvalFunctionFrame()); - flags_ |= HAS_CALL_OBJ | HAS_SCOPECHAIN; + MOZ_ASSERT(isFunctionFrame()); + flags_ |= HAS_CALL_OBJ; scopeChain_ = scopeChain; } @@ -835,18 +698,6 @@ class InterpreterFrame * Other flags */ - InitialFrameFlags initialFlags() const { - JS_STATIC_ASSERT((int)INITIAL_NONE == 0); - JS_STATIC_ASSERT((int)INITIAL_CONSTRUCT == (int)CONSTRUCTING); - uint32_t mask = CONSTRUCTING; - MOZ_ASSERT((flags_ & mask) != mask); - return InitialFrameFlags(flags_ & mask); - } - - void setConstructing() { - flags_ |= CONSTRUCTING; - } - bool isConstructing() const { return !!(flags_ & CONSTRUCTING); } @@ -886,8 +737,22 @@ class InterpreterFrame return flags_ & CREATE_SINGLETON; } + /* + * Debugger eval frames. + * + * - If evalInFramePrev_ is non-null, frame was created for an "eval in + * frame" call, which can push a successor to any live frame; so its + * logical "prev" frame is not necessarily the previous frame in memory. + * Iteration should treat evalInFramePrev_ as this frame's previous frame. + * + * - Don't bother to JIT it, because it's probably short-lived. + * + * - It is required to have a scope chain object outside the + * js::ScopeObject hierarchy: either a global object, or a + * DebugScopeObject (not a ScopeObject, despite the name) + */ bool isDebuggerEvalFrame() const { - return !!(flags_ & DEBUGGER_EVAL); + return isEvalFrame() && !!evalInFramePrev_; } bool prevUpToDate() const { @@ -936,26 +801,6 @@ class InterpreterFrame } }; -static const size_t VALUES_PER_STACK_FRAME = sizeof(InterpreterFrame) / sizeof(Value); - -static inline InterpreterFrame::Flags -ToFrameFlags(InitialFrameFlags initial) -{ - return InterpreterFrame::Flags(initial); -} - -static inline InitialFrameFlags -InitialFrameFlagsFromConstructing(bool b) -{ - return b ? INITIAL_CONSTRUCT : INITIAL_NONE; -} - -static inline bool -InitialFrameFlagsAreConstructing(InitialFrameFlags initial) -{ - return !!(initial & INITIAL_CONSTRUCT); -} - /*****************************************************************************/ class InterpreterRegs @@ -1031,7 +876,7 @@ class InterpreterStack inline InterpreterFrame* getCallFrame(JSContext* cx, const CallArgs& args, HandleScript script, - InterpreterFrame::Flags* pflags, Value** pargv); + MaybeConstruct constructing, Value** pargv); void releaseFrame(InterpreterFrame* fp) { frameCount_--; @@ -1051,16 +896,16 @@ class InterpreterStack // For execution of eval or global code. InterpreterFrame* pushExecuteFrame(JSContext* cx, HandleScript script, const Value& newTargetValue, HandleObject scopeChain, - ExecuteType type, AbstractFramePtr evalInFrame); + AbstractFramePtr evalInFrame); // Called to invoke a function. InterpreterFrame* pushInvokeFrame(JSContext* cx, const CallArgs& args, - InitialFrameFlags initial); + MaybeConstruct constructing); // The interpreter can push light-weight, "inline" frames without entering a // new InterpreterActivation or recursively calling Interpret. bool pushInlineFrame(JSContext* cx, InterpreterRegs& regs, const CallArgs& args, - HandleScript script, InitialFrameFlags initial); + HandleScript script, MaybeConstruct constructing); void popInlineFrame(InterpreterRegs& regs); @@ -1464,7 +1309,7 @@ class InterpreterActivation : public Activation inline ~InterpreterActivation(); inline bool pushInlineFrame(const CallArgs& args, HandleScript script, - InitialFrameFlags initial); + MaybeConstruct constructing); inline void popInlineFrame(InterpreterFrame* frame); inline bool resumeGeneratorFrame(HandleFunction callee, HandleValue newTarget, @@ -1920,11 +1765,9 @@ class FrameIter inline bool isBaseline() const; inline bool isPhysicalIonFrame() const; - bool isFunctionFrame() const; - bool isGlobalFrame() const; bool isEvalFrame() const; - bool isNonEvalFunctionFrame() const; - bool hasArgs() const { return isNonEvalFunctionFrame(); } + bool isFunctionFrame() const; + bool hasArgs() const { return isFunctionFrame(); } // These two methods may not be called with asm frames. inline bool hasCachedSavedFrame() const; diff --git a/js/src/vm/TaggedProto.cpp b/js/src/vm/TaggedProto.cpp index f00f07d31c..725c3bc64e 100644 --- a/js/src/vm/TaggedProto.cpp +++ b/js/src/vm/TaggedProto.cpp @@ -14,18 +14,19 @@ namespace js { /* static */ void -InternalGCMethods::preBarrier(TaggedProto& proto) +InternalBarrierMethods::preBarrier(TaggedProto& proto) { - InternalGCMethods::preBarrier(proto.toObjectOrNull()); + InternalBarrierMethods::preBarrier(proto.toObjectOrNull()); } /* static */ void -InternalGCMethods::postBarrier(TaggedProto* vp, TaggedProto prev, TaggedProto next) +InternalBarrierMethods::postBarrier(TaggedProto* vp, TaggedProto prev, + TaggedProto next) { JSObject* prevObj = prev.isObject() ? prev.toObject() : nullptr; JSObject* nextObj = next.isObject() ? next.toObject() : nullptr; - InternalGCMethods::postBarrier(reinterpret_cast(vp), prevObj, - nextObj); + InternalBarrierMethods::postBarrier(reinterpret_cast(vp), prevObj, + nextObj); } } // namespace js diff --git a/js/src/vm/TaggedProto.h b/js/src/vm/TaggedProto.h index 7253880253..412961a08b 100644 --- a/js/src/vm/TaggedProto.h +++ b/js/src/vm/TaggedProto.h @@ -52,12 +52,14 @@ class TaggedProto : public JS::Traceable JSObject* proto; }; -template <> struct GCMethods +template <> +struct GCPolicy { static TaggedProto initial() { return TaggedProto(); } }; -template <> struct InternalGCMethods +template <> +struct InternalBarrierMethods { static void preBarrier(TaggedProto& proto); diff --git a/js/xpconnect/src/XPCMaps.cpp b/js/xpconnect/src/XPCMaps.cpp index 297e9776ce..45876a9138 100644 --- a/js/xpconnect/src/XPCMaps.cpp +++ b/js/xpconnect/src/XPCMaps.cpp @@ -119,12 +119,11 @@ JSObject2WrappedJSMap::UpdateWeakPointersAfterGC(XPCJSRuntime* runtime) // Remove or update the JSObject key in the table if necessary. JSObject* obj = e.front().key(); - JSObject* prior = obj; JS_UpdateWeakPointerAfterGCUnbarriered(&obj); if (!obj) e.removeFront(); - else if (obj != prior) - e.rekeyFront(obj); + else + e.front().mutableKey() = obj; } } diff --git a/js/xpconnect/src/XPCMaps.h b/js/xpconnect/src/XPCMaps.h index 15f10b6a36..7d585e93ea 100644 --- a/js/xpconnect/src/XPCMaps.h +++ b/js/xpconnect/src/XPCMaps.h @@ -27,8 +27,10 @@ class JSObject2WrappedJSMap { - typedef js::HashMap, - js::SystemAllocPolicy> Map; + using Map = js::HashMap, + nsXPCWrappedJS*, + js::MovableCellHasher>, + js::SystemAllocPolicy>; public: static JSObject2WrappedJSMap* newMap(int length) { @@ -66,7 +68,6 @@ public: return p->value(); if (!mTable.add(p, obj, wrapper)) return nullptr; - JS_StoreObjectPostBarrierCallback(cx, KeyMarkCallback, obj, this); return wrapper; } @@ -95,17 +96,6 @@ public: private: JSObject2WrappedJSMap() {} - /* - * This function is called during minor GCs for each key in the HashMap that - * has been moved. - */ - static void KeyMarkCallback(JSTracer* trc, JSObject* key, void* data) { - JSObject2WrappedJSMap* self = static_cast(data); - JSObject* prior = key; - JS_CallUnbarrieredObjectTracer(trc, &key, "XPCJSRuntime::mWrappedJSMap key"); - self->mTable.rekeyIfMoved(prior, key); - } - Map mTable; }; diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index b2e26c95a5..34bf9b9123 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -2202,10 +2202,7 @@ public: // This is the only time we should be tracing our mFlatJSObject, // normally somebody else is doing that. Be careful not to trace the // bogus INVALID_OBJECT value we can have during init, though. - if (mFlatJSObject) { - JS_CallTenuredObjectTracer(trc, &mFlatJSObject, - "XPCWrappedNative::mFlatJSObject"); - } + JS::TraceEdge(trc, &mFlatJSObject, "XPCWrappedNative::mFlatJSObject"); } static void Trace(JSTracer* trc, JSObject* obj); diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index 5e585a683e..078f8173bb 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -796,12 +796,12 @@ struct JsGcTracer : public TraceCallbacks virtual void Trace(JSObject** aPtr, const char* aName, void* aClosure) const override { - JS_CallUnbarrieredObjectTracer(static_cast(aClosure), aPtr, aName); + js::UnsafeTraceManuallyBarrieredEdge(static_cast(aClosure), aPtr, aName); } virtual void Trace(JS::TenuredHeap* aPtr, const char* aName, void* aClosure) const override { - JS_CallTenuredObjectTracer(static_cast(aClosure), aPtr, aName); + JS::TraceEdge(static_cast(aClosure), aPtr, aName); } virtual void Trace(JS::Heap* aPtr, const char* aName, void* aClosure) const override