diff options
author | wolfbeast <mcwerewolf@wolfbeast.com> | 2019-11-10 11:39:27 +0100 |
---|---|---|
committer | wolfbeast <mcwerewolf@wolfbeast.com> | 2019-11-10 11:39:27 +0100 |
commit | 974a481d12bf430891725bd3662876358e57e11a (patch) | |
tree | cad011151456251fef2f1b8d02ef4b4e45fad61a /js | |
parent | 6bd66b1728eeddb058066edda740aaeb2ceaec23 (diff) | |
parent | 736d25cbec4541186ed46c935c117ce4d1c7f3bb (diff) | |
download | uxp-974a481d12bf430891725bd3662876358e57e11a.tar.gz |
Merge branch 'master' into js-modules
# Conflicts:
# modules/libpref/init/all.js
Diffstat (limited to 'js')
503 files changed, 11332 insertions, 18058 deletions
diff --git a/js/ipc/JavaScriptParent.cpp b/js/ipc/JavaScriptParent.cpp index ca0a0bd214..aafd7f565f 100644 --- a/js/ipc/JavaScriptParent.cpp +++ b/js/ipc/JavaScriptParent.cpp @@ -9,13 +9,13 @@ #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/ScriptSettings.h" #include "nsJSUtils.h" +#include "nsIScriptError.h" #include "jsfriendapi.h" #include "jswrapper.h" #include "js/Proxy.h" #include "js/HeapAPI.h" #include "xpcprivate.h" #include "mozilla/Casting.h" -#include "mozilla/Telemetry.h" #include "mozilla/Unused.h" #include "nsAutoPtr.h" @@ -110,7 +110,6 @@ JavaScriptParent::allowMessage(JSContext* cx) if (!xpc::CompartmentPrivate::Get(jsGlobal)->allowCPOWs) { if (!addonId && ForbidUnsafeBrowserCPOWs() && !isSafe) { - Telemetry::Accumulate(Telemetry::BROWSER_SHIM_USAGE_BLOCKED, 1); JS_ReportErrorASCII(cx, "unsafe CPOW usage forbidden"); return false; } @@ -120,7 +119,6 @@ JavaScriptParent::allowMessage(JSContext* cx) nsString addonIdString; AssignJSFlatString(addonIdString, flat); NS_ConvertUTF16toUTF8 addonIdCString(addonIdString); - Telemetry::Accumulate(Telemetry::ADDON_FORBIDDEN_CPOW_USAGE, addonIdCString); if (ForbidCPOWsInCompatibleAddon(addonIdCString)) { JS_ReportErrorASCII(cx, "CPOW usage forbidden in this add-on"); diff --git a/js/ipc/JavaScriptShared.cpp b/js/ipc/JavaScriptShared.cpp index 9786243f21..961a3f9106 100644 --- a/js/ipc/JavaScriptShared.cpp +++ b/js/ipc/JavaScriptShared.cpp @@ -61,6 +61,15 @@ IdToObjectMap::find(ObjectId id) return p->value(); } +JSObject* +IdToObjectMap::findPreserveColor(ObjectId id) +{ + Table::Ptr p = table_.lookup(id); + if (!p) + return nullptr; + return p->value().unbarrieredGet(); +} + bool IdToObjectMap::add(ObjectId id, JSObject* obj) { diff --git a/js/ipc/JavaScriptShared.h b/js/ipc/JavaScriptShared.h index 4de1538265..5ecec74296 100644 --- a/js/ipc/JavaScriptShared.h +++ b/js/ipc/JavaScriptShared.h @@ -96,6 +96,7 @@ class IdToObjectMap bool add(ObjectId id, JSObject* obj); JSObject* find(ObjectId id); + JSObject* findPreserveColor(ObjectId id); void remove(ObjectId id); void clear(); diff --git a/js/ipc/WrapperAnswer.cpp b/js/ipc/WrapperAnswer.cpp index fc342bbb68..563f8f90d3 100644 --- a/js/ipc/WrapperAnswer.cpp +++ b/js/ipc/WrapperAnswer.cpp @@ -789,7 +789,7 @@ WrapperAnswer::RecvDOMInstanceOf(const ObjectId& objId, const int& prototypeID, bool WrapperAnswer::RecvDropObject(const ObjectId& objId) { - JSObject* obj = objects_.find(objId); + JSObject* obj = objects_.findPreserveColor(objId); if (obj) { objectIdMap(objId.hasXrayWaiver()).remove(obj); objects_.remove(objId); diff --git a/js/public/CallArgs.h b/js/public/CallArgs.h index a7774309aa..aae7121ad2 100644 --- a/js/public/CallArgs.h +++ b/js/public/CallArgs.h @@ -128,7 +128,10 @@ class MOZ_STACK_CLASS CallArgsBase : public WantUsedRval protected: Value* argv_; unsigned argc_; - bool constructing_; + bool constructing_:1; + + // True if the caller does not use the return value. + bool ignoresReturnValue_:1; public: // CALLEE ACCESS @@ -164,6 +167,10 @@ class MOZ_STACK_CLASS CallArgsBase : public WantUsedRval return true; } + bool ignoresReturnValue() const { + return ignoresReturnValue_; + } + MutableHandleValue newTarget() const { MOZ_ASSERT(constructing_); return MutableHandleValue::fromMarkedLocation(&this->argv_[argc_]); @@ -280,14 +287,17 @@ class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsed { private: friend CallArgs CallArgsFromVp(unsigned argc, Value* vp); - friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing); + friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing, + bool ignoresReturnValue); - static CallArgs create(unsigned argc, Value* argv, bool constructing) { + static CallArgs create(unsigned argc, Value* argv, bool constructing, + bool ignoresReturnValue = false) { CallArgs args; args.clearUsedRval(); args.argv_ = argv; args.argc_ = argc; args.constructing_ = constructing; + args.ignoresReturnValue_ = ignoresReturnValue; #ifdef DEBUG for (unsigned i = 0; i < argc; ++i) MOZ_ASSERT_IF(argv[i].isGCThing(), !GCThingIsMarkedGray(GCCellPtr(argv[i]))); @@ -314,9 +324,11 @@ CallArgsFromVp(unsigned argc, Value* vp) // eventually move it to an internal header. Embedders should use // JS::CallArgsFromVp! MOZ_ALWAYS_INLINE CallArgs -CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing = false) +CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing = false, + bool ignoresReturnValue = false) { - return CallArgs::create(stackSlots - constructing, sp - stackSlots, constructing); + return CallArgs::create(stackSlots - constructing, sp - stackSlots, constructing, + ignoresReturnValue); } } // namespace JS diff --git a/js/public/Class.h b/js/public/Class.h index 3b5023875e..f7533654bb 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -425,12 +425,6 @@ typedef bool (* DeletePropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::ObjectOpResult& result); -typedef bool -(* WatchOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable); - -typedef bool -(* UnwatchOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id); - class JS_FRIEND_API(ElementAdder) { public: @@ -670,8 +664,6 @@ struct ObjectOps SetPropertyOp setProperty; GetOwnPropertyOp getOwnPropertyDescriptor; DeletePropertyOp deleteProperty; - WatchOp watch; - UnwatchOp unwatch; GetElementsOp getElements; JSNewEnumerateOp enumerate; JSFunToStringOp funToString; @@ -822,8 +814,8 @@ struct Class * Objects of this class aren't native objects. They don't have Shapes that * describe their properties and layout. Classes using this flag must * provide their own property behavior, either by being proxy classes (do - * this) or by overriding all the ObjectOps except getElements, watch and - * unwatch (don't do this). + * this) or by overriding all the ObjectOps except getElements + * (don't do this). */ static const uint32_t NON_NATIVE = JSCLASS_INTERNAL_FLAG2; @@ -900,8 +892,6 @@ struct Class const { return oOps ? oOps->getOwnPropertyDescriptor : nullptr; } DeletePropertyOp getOpsDeleteProperty() const { return oOps ? oOps->deleteProperty : nullptr; } - WatchOp getOpsWatch() const { return oOps ? oOps->watch : nullptr; } - UnwatchOp getOpsUnwatch() const { return oOps ? oOps->unwatch : nullptr; } GetElementsOp getOpsGetElements() const { return oOps ? oOps->getElements : nullptr; } JSNewEnumerateOp getOpsEnumerate() const { return oOps ? oOps->enumerate : nullptr; } JSFunToStringOp getOpsFunToString() const { return oOps ? oOps->funToString : nullptr; } diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index 7a6675ca72..4ef2a8370a 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -119,14 +119,6 @@ enum Reason { #undef MAKE_REASON NO_REASON, NUM_REASONS, - - /* - * For telemetry, we want to keep a fixed max bucket size over time so we - * don't have to switch histograms. 100 is conservative; as of this writing - * there are 52. But the cost of extra buckets seems to be low while the - * cost of switching histograms is high. - */ - NUM_TELEMETRY_REASONS = 100 }; /** diff --git a/js/public/HashTable.h b/js/public/HashTable.h index 5d4c0665d7..8a2493b559 100644 --- a/js/public/HashTable.h +++ b/js/public/HashTable.h @@ -12,6 +12,7 @@ #include "mozilla/Attributes.h" #include "mozilla/Casting.h" #include "mozilla/HashFunctions.h" +#include "mozilla/MemoryChecking.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Move.h" #include "mozilla/Opaque.h" @@ -805,17 +806,22 @@ class HashTableEntry void operator=(const HashTableEntry&) = delete; ~HashTableEntry() = delete; + void destroyStoredT() { + mem.addr()->~T(); + MOZ_MAKE_MEM_UNDEFINED(mem.addr(), sizeof(*mem.addr())); + } + public: // NB: HashTableEntry is treated as a POD: no constructor or destructor calls. void destroyIfLive() { if (isLive()) - mem.addr()->~T(); + destroyStoredT(); } void destroy() { MOZ_ASSERT(isLive()); - mem.addr()->~T(); + destroyStoredT(); } void swap(HashTableEntry* other) { @@ -835,10 +841,28 @@ class HashTableEntry NonConstT& getMutable() { MOZ_ASSERT(isLive()); return *mem.addr(); } bool isFree() const { return keyHash == sFreeKey; } - void clearLive() { MOZ_ASSERT(isLive()); keyHash = sFreeKey; mem.addr()->~T(); } - void clear() { if (isLive()) mem.addr()->~T(); keyHash = sFreeKey; } + void clearLive() { + MOZ_ASSERT(isLive()); + keyHash = sFreeKey; + destroyStoredT(); + } + + void clear() { + if (isLive()) + destroyStoredT(); + + MOZ_MAKE_MEM_UNDEFINED(this, sizeof(*this)); + keyHash = sFreeKey; + } + bool isRemoved() const { return keyHash == sRemovedKey; } - void removeLive() { MOZ_ASSERT(isLive()); keyHash = sRemovedKey; mem.addr()->~T(); } + + void removeLive() { + MOZ_ASSERT(isLive()); + keyHash = sRemovedKey; + destroyStoredT(); + } + bool isLive() const { return isLiveHash(keyHash); } void setCollision() { MOZ_ASSERT(isLive()); keyHash |= sCollisionBit; } void unsetCollision() { keyHash &= ~sCollisionBit; } @@ -1654,14 +1678,10 @@ class HashTable : private AllocPolicy public: void clear() { - if (mozilla::IsPod<Entry>::value) { - memset(table, 0, sizeof(*table) * capacity()); - } else { - uint32_t tableCapacity = capacity(); - Entry* end = table + tableCapacity; - for (Entry* e = table; e < end; ++e) - e->clear(); - } + Entry* end = table + capacity(); + for (Entry* e = table; e < end; ++e) + e->clear(); + removedCount = 0; entryCount = 0; #ifdef JS_DEBUG diff --git a/js/public/LegacyIntTypes.h b/js/public/LegacyIntTypes.h index 2c8498c89e..cdfd98726d 100644 --- a/js/public/LegacyIntTypes.h +++ b/js/public/LegacyIntTypes.h @@ -31,20 +31,10 @@ typedef uint16_t uint16; typedef uint32_t uint32; typedef uint64_t uint64; -/* - * On AIX 4.3, sys/inttypes.h (which is included by sys/types.h, a very - * common header file) defines the types int8, int16, int32, and int64. - * So we don't define these four types here to avoid conflicts in case - * the code also includes sys/types.h. - */ -#if defined(AIX) && defined(HAVE_SYS_INTTYPES_H) -#include <sys/inttypes.h> -#else typedef int8_t int8; typedef int16_t int16; typedef int32_t int32; typedef int64_t int64; -#endif /* AIX && HAVE_SYS_INTTYPES_H */ typedef uint8_t JSUint8; typedef uint16_t JSUint16; diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index 9b5caa24b7..dcc8862174 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -11,7 +11,6 @@ // at your own risk. #include "mozilla/MemoryReporting.h" -#include "mozilla/PodOperations.h" #include "mozilla/TypeTraits.h" #include <string.h> @@ -37,7 +36,13 @@ struct TabSizes Other }; - TabSizes() { mozilla::PodZero(this); } + TabSizes() + : objects(0) + , strings(0) + , private_(0) + , other(0) + { + } void add(Kind kind, size_t n) { switch (kind) { @@ -68,7 +73,7 @@ struct ServoSizes Ignore }; - ServoSizes() { mozilla::PodZero(this); } + ServoSizes() = default; void add(Kind kind, size_t n) { switch (kind) { @@ -83,12 +88,12 @@ struct ServoSizes } } - size_t gcHeapUsed; - size_t gcHeapUnused; - size_t gcHeapAdmin; - size_t gcHeapDecommitted; - size_t mallocHeap; - size_t nonHeap; + size_t gcHeapUsed = 0; + size_t gcHeapUnused = 0; + size_t gcHeapAdmin = 0; + size_t gcHeapDecommitted = 0; + size_t mallocHeap = 0; + size_t nonHeap = 0; }; } // namespace JS diff --git a/js/public/Proxy.h b/js/public/Proxy.h index 5acb91b26e..f40772fb09 100644 --- a/js/public/Proxy.h +++ b/js/public/Proxy.h @@ -341,12 +341,6 @@ class JS_FRIEND_API(BaseProxyHandler) virtual bool isCallable(JSObject* obj) const; virtual bool isConstructor(JSObject* obj) const; - // These two hooks must be overridden, or not overridden, in tandem -- no - // overriding just one! - virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, - JS::HandleObject callable) const; - virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const; - virtual bool getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end, ElementAdder* adder) const; diff --git a/js/public/Utility.h b/js/public/Utility.h index 68de3004ab..99712faa8b 100644 --- a/js/public/Utility.h +++ b/js/public/Utility.h @@ -391,7 +391,7 @@ js_delete_poison(const T* p) { if (p) { p->~T(); - memset(const_cast<T*>(p), 0x3B, sizeof(T)); + memset(static_cast<void*>(const_cast<T*>(p)), 0x3B, sizeof(T)); js_free(const_cast<T*>(p)); } } diff --git a/js/public/Value.h b/js/public/Value.h index 01666ed4e1..7c4f833e3d 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -567,8 +567,11 @@ class MOZ_NON_PARAM alignas(8) Value } bool isMagic(JSWhyMagic why) const { - MOZ_ASSERT_IF(isMagic(), data.s.payload.why == why); - return isMagic(); + if (!isMagic()) { + return false; + } + MOZ_RELEASE_ASSERT(data.s.payload.why == why); + return true; } JS::TraceKind traceKind() const { diff --git a/js/src/Makefile.in b/js/src/Makefile.in index b007954b1f..bc99e62b5d 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -138,23 +138,13 @@ distclean:: CFLAGS += $(MOZ_ZLIB_CFLAGS) -# Silence warnings on AIX/HP-UX from non-GNU compilers -ifndef GNU_CC -ifeq ($(OS_ARCH),AIX) -# Suppress warnings from xlC -# 1540-1281: offsetof() on null non-POD types -# 1540-1608: anonymous unions using static data members -CFLAGS += -qsuppress=1540-1281 -qsuppress=1540-1608 -CXXFLAGS += -qsuppress=1540-1281 -qsuppress=1540-1608 -endif -endif ifeq ($(OS_ARCH),SunOS) ifeq ($(TARGET_CPU),sparc) ifdef GNU_CC CFLAGS += -mcpu=v9 CXXFLAGS += -mcpu=v9 -endif # GNU_CC +endif #GNU_CC endif endif diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index 30e6fb35fb..05fc41bc14 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -1073,6 +1073,114 @@ function ArrayConcat(arg1) { return A; } +// https://tc39.github.io/proposal-flatMap/ +// January 4, 2019 +function ArrayFlatMap(mapperFunction/*, thisArg*/) { + // Step 1. + var O = ToObject(this); + + // Step 2. + var sourceLen = ToLength(O.length); + + // Step 3. + if (!IsCallable(mapperFunction)) + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, mapperFunction)); + + // Step 4. + var T = arguments.length > 1 ? arguments[1] : undefined; + + // Step 5. + var A = ArraySpeciesCreate(O, 0); + + // Step 6. + FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T); + + // Step 7. + return A; +} + +// https://tc39.github.io/proposal-flatMap/ +// January 4, 2019 +function ArrayFlat(/* depth */) { + // Step 1. + var O = ToObject(this); + + // Step 2. + var sourceLen = ToLength(O.length); + + // Step 3. + var depthNum = 1; + + // Step 4. + if (arguments.length > 0 && arguments[0] !== undefined) + depthNum = ToInteger(arguments[0]); + + // Step 5. + var A = ArraySpeciesCreate(O, 0); + + // Step 6. + FlattenIntoArray(A, O, sourceLen, 0, depthNum); + + // Step 7. + return A; +} + +// https://tc39.github.io/proposal-flatMap/ +// January 4, 2019 +function FlattenIntoArray(target, source, sourceLen, start, depth, mapperFunction, thisArg) { + // Step 1. + var targetIndex = start; + + // Steps 2-3. + for (var sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { + // Steps 3.a-c. + if (sourceIndex in source) { + // Step 3.c.i. + var element = source[sourceIndex]; + + if (mapperFunction) { + // Step 3.c.ii.1. + assert(arguments.length === 7, "thisArg is present"); + + // Step 3.c.ii.2. + element = callContentFunction(mapperFunction, thisArg, element, sourceIndex, source); + } + + // Step 3.c.iii. + var shouldFlatten = false; + + // Step 3.c.iv. + if (depth > 0) { + // Step 3.c.iv.1. + shouldFlatten = IsArray(element); + } + + // Step 3.c.v. + if (shouldFlatten) { + // Step 3.c.v.1. + var elementLen = ToLength(element.length); + + // Step 3.c.v.2. + // Recursive call to walk the depth. + targetIndex = FlattenIntoArray(target, element, elementLen, targetIndex, depth - 1); + } else { + // Step 3.c.vi.1. + if (targetIndex >= MAX_NUMERIC_INDEX) + ThrowTypeError(JSMSG_TOO_LONG_ARRAY); + + // Step 3.c.vi.2. + _DefineDataProperty(target, targetIndex, element); + + // Step 3.c.vi.3. + targetIndex++; + } + } + } + + // Step 4. + return targetIndex; +} + function ArrayStaticConcat(arr, arg1) { if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.concat'); diff --git a/js/src/builtin/AtomicsObject.cpp b/js/src/builtin/AtomicsObject.cpp index 08777fd512..ceee83349a 100644 --- a/js/src/builtin/AtomicsObject.cpp +++ b/js/src/builtin/AtomicsObject.cpp @@ -789,7 +789,7 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp) // and it provides the necessary memory fence. AutoLockFutexAPI lock; - SharedMem<int32_t*>(addr) = view->viewDataShared().cast<int32_t*>() + offset; + SharedMem<int32_t*> addr = view->viewDataShared().cast<int32_t*>() + offset; if (jit::AtomicOperations::loadSafeWhenRacy(addr) != value) { r.setString(cx->names().futexNotEqual); return true; @@ -834,7 +834,7 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp) } bool -js::atomics_wake(JSContext* cx, unsigned argc, Value* vp) +js::atomics_notify(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); HandleValue objv = args.get(0); @@ -874,7 +874,7 @@ js::atomics_wake(JSContext* cx, unsigned argc, Value* vp) iter = iter->lower_pri; if (c->offset != offset || !c->rt->fx.isWaiting()) continue; - c->rt->fx.wake(FutexRuntime::WakeExplicit); + c->rt->fx.notify(FutexRuntime::NotifyExplicit); ++woken; --count; } while (count > 0 && iter != waiters); @@ -950,7 +950,7 @@ js::FutexRuntime::isWaiting() // When a worker is awoken for an interrupt it goes into state // WaitingNotifiedForInterrupt for a short time before it actually // wakes up and goes into WaitingInterrupted. In those states the - // worker is still waiting, and if an explicit wake arrives the + // worker is still waiting, and if an explicit notify arrives the // worker transitions to Woken. See further comments in // FutexRuntime::wait(). return state_ == Waiting || state_ == WaitingInterrupted || state_ == WaitingNotifiedForInterrupt; @@ -1029,14 +1029,14 @@ js::FutexRuntime::wait(JSContext* cx, js::UniqueLock<js::Mutex>& locked, // should be woken when the interrupt handler returns. // To that end, we flag the thread as interrupted around // the interrupt and check state_ when the interrupt - // handler returns. A wake() call that reaches the + // handler returns. A notify() call that reaches the // runtime during the interrupt sets state_ to Woken. // // - It is in principle possible for wait() to be // reentered on the same thread/runtime and waiting on the // same location and to yet again be interrupted and enter // the interrupt handler. In this case, it is important - // that when another agent wakes waiters, all waiters using + // that when another agent notifies waiters, all waiters using // the same runtime on the same location are woken in LIFO // order; FIFO may be the required order, but FIFO would // fail to wake up the innermost call. Interrupts are @@ -1073,25 +1073,25 @@ finished: } void -js::FutexRuntime::wake(WakeReason reason) +js::FutexRuntime::notify(NotifyReason reason) { MOZ_ASSERT(isWaiting()); - if ((state_ == WaitingInterrupted || state_ == WaitingNotifiedForInterrupt) && reason == WakeExplicit) { + if ((state_ == WaitingInterrupted || state_ == WaitingNotifiedForInterrupt) && reason == NotifyExplicit) { state_ = Woken; return; } switch (reason) { - case WakeExplicit: + case NotifyExplicit: state_ = Woken; break; - case WakeForJSInterrupt: + case NotifyForJSInterrupt: if (state_ == WaitingNotifiedForInterrupt) return; state_ = WaitingNotifiedForInterrupt; break; default: - MOZ_CRASH("bad WakeReason in FutexRuntime::wake()"); + MOZ_CRASH("bad NotifyReason in FutexRuntime::notify()"); } cond_->notify_all(); } @@ -1108,7 +1108,8 @@ const JSFunctionSpec AtomicsMethods[] = { JS_INLINABLE_FN("xor", atomics_xor, 3,0, AtomicsXor), JS_INLINABLE_FN("isLockFree", atomics_isLockFree, 1,0, AtomicsIsLockFree), JS_FN("wait", atomics_wait, 4,0), - JS_FN("wake", atomics_wake, 3,0), + JS_FN("notify", atomics_notify, 3,0), + JS_FN("wake", atomics_notify, 3,0), //Legacy name JS_FS_END }; @@ -1116,7 +1117,7 @@ JSObject* AtomicsObject::initClass(JSContext* cx, Handle<GlobalObject*> global) { // Create Atomics Object. - RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); + RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); if (!objProto) return nullptr; RootedObject Atomics(cx, NewObjectWithGivenProto(cx, &AtomicsObject::class_, objProto, diff --git a/js/src/builtin/AtomicsObject.h b/js/src/builtin/AtomicsObject.h index adb6fb986a..6511dc8bf4 100644 --- a/js/src/builtin/AtomicsObject.h +++ b/js/src/builtin/AtomicsObject.h @@ -36,7 +36,7 @@ MOZ_MUST_USE bool atomics_or(JSContext* cx, unsigned argc, Value* vp); MOZ_MUST_USE bool atomics_xor(JSContext* cx, unsigned argc, Value* vp); MOZ_MUST_USE bool atomics_isLockFree(JSContext* cx, unsigned argc, Value* vp); MOZ_MUST_USE bool atomics_wait(JSContext* cx, unsigned argc, Value* vp); -MOZ_MUST_USE bool atomics_wake(JSContext* cx, unsigned argc, Value* vp); +MOZ_MUST_USE bool atomics_notify(JSContext* cx, unsigned argc, Value* vp); /* asm.js callouts */ namespace wasm { class Instance; } @@ -63,10 +63,10 @@ public: MOZ_MUST_USE bool initInstance(); void destroyInstance(); - // Parameters to wake(). - enum WakeReason { - WakeExplicit, // Being asked to wake up by another thread - WakeForJSInterrupt // Interrupt requested + // Parameters to notify(). + enum NotifyReason { + NotifyExplicit, // Being asked to wake up by another thread + NotifyForJSInterrupt // Interrupt requested }; // Result code from wait(). @@ -83,29 +83,27 @@ public: // times allowed; specify mozilla::Nothing() for an indefinite // wait. // - // wait() will not wake up spuriously. It will return true and - // set *result to a return code appropriate for - // Atomics.wait() on success, and return false on error. + // wait() will not wake up spuriously. MOZ_MUST_USE bool wait(JSContext* cx, js::UniqueLock<js::Mutex>& locked, mozilla::Maybe<mozilla::TimeDuration>& timeout, WaitResult* result); - // Wake the thread represented by this Runtime. + // Notify the thread represented by this Runtime. // // The futex lock must be held around this call. (The sleeping - // thread will not wake up until the caller of Atomics.wake() + // thread will not wake up until the caller of Atomics.notify() // releases the lock.) // // If the thread is not waiting then this method does nothing. // // If the thread is waiting in a call to wait() and the - // reason is WakeExplicit then the wait() call will return + // reason is NotifyExplicit then the wait() call will return // with Woken. // // If the thread is waiting in a call to wait() and the - // reason is WakeForJSInterrupt then the wait() will return + // reason is NotifyForJSInterrupt then the wait() will return // with WaitingNotifiedForInterrupt; in the latter case the caller // of wait() must handle the interrupt. - void wake(WakeReason reason); + void notify(NotifyReason reason); bool isWaiting(); @@ -128,7 +126,7 @@ public: // interrupt handler WaitingInterrupted, // We are waiting, but have been interrupted // and are running the interrupt handler - Woken // Woken by a script call to Atomics.wake + Woken // Woken by a script call to Atomics.notify }; // Condition variable that this runtime will wait on. diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp index 71f7b58d45..0cd0c62dd1 100644 --- a/js/src/builtin/Intl.cpp +++ b/js/src/builtin/Intl.cpp @@ -270,7 +270,7 @@ Collator(JSContext* cx, const CallArgs& args, bool construct) // See https://github.com/tc39/ecma402/issues/57 if (!construct) { // ES Intl 1st ed., 10.1.2.1 step 3 - JSObject* intl = cx->global()->getOrCreateIntlObject(cx); + JSObject* intl = GlobalObject::getOrCreateIntlObject(cx, cx->global()); if (!intl) return false; RootedValue self(cx, args.thisv()); @@ -298,7 +298,7 @@ Collator(JSContext* cx, const CallArgs& args, bool construct) return false; if (!proto) { - proto = cx->global()->getOrCreateCollatorPrototype(cx); + proto = GlobalObject::getOrCreateCollatorPrototype(cx, cx->global()); if (!proto) return false; } @@ -358,11 +358,12 @@ collator_finalize(FreeOp* fop, JSObject* obj) static JSObject* CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global) { - RootedFunction ctor(cx, global->createConstructor(cx, &Collator, cx->names().Collator, 0)); + RootedFunction ctor(cx, GlobalObject::createConstructor(cx, &Collator, cx->names().Collator, + 0)); if (!ctor) return nullptr; - RootedNativeObject proto(cx, global->createBlankPrototype(cx, &CollatorClass)); + RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &CollatorClass)); if (!proto) return nullptr; proto->setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr)); @@ -772,7 +773,7 @@ NumberFormat(JSContext* cx, const CallArgs& args, bool construct) // See https://github.com/tc39/ecma402/issues/57 if (!construct) { // ES Intl 1st ed., 11.1.2.1 step 3 - JSObject* intl = cx->global()->getOrCreateIntlObject(cx); + JSObject* intl = GlobalObject::getOrCreateIntlObject(cx, cx->global()); if (!intl) return false; RootedValue self(cx, args.thisv()); @@ -800,7 +801,7 @@ NumberFormat(JSContext* cx, const CallArgs& args, bool construct) return false; if (!proto) { - proto = cx->global()->getOrCreateNumberFormatPrototype(cx); + proto = GlobalObject::getOrCreateNumberFormatPrototype(cx, cx->global()); if (!proto) return false; } @@ -862,11 +863,12 @@ static JSObject* CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global) { RootedFunction ctor(cx); - ctor = global->createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0); + ctor = GlobalObject::createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0); if (!ctor) return nullptr; - RootedNativeObject proto(cx, global->createBlankPrototype(cx, &NumberFormatClass)); + RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, + &NumberFormatClass)); if (!proto) return nullptr; proto->setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr)); @@ -1250,7 +1252,7 @@ DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct) // See https://github.com/tc39/ecma402/issues/57 if (!construct) { // ES Intl 1st ed., 12.1.2.1 step 3 - JSObject* intl = cx->global()->getOrCreateIntlObject(cx); + JSObject* intl = GlobalObject::getOrCreateIntlObject(cx, cx->global()); if (!intl) return false; RootedValue self(cx, args.thisv()); @@ -1278,7 +1280,7 @@ DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct) return false; if (!proto) { - proto = cx->global()->getOrCreateDateTimeFormatPrototype(cx); + proto = GlobalObject::getOrCreateDateTimeFormatPrototype(cx, cx->global()); if (!proto) return false; } @@ -1340,11 +1342,12 @@ static JSObject* CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global) { RootedFunction ctor(cx); - ctor = global->createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0); + ctor = GlobalObject::createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0); if (!ctor) return nullptr; - RootedNativeObject proto(cx, global->createBlankPrototype(cx, &DateTimeFormatClass)); + RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, + &DateTimeFormatClass)); if (!proto) return nullptr; proto->setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr)); @@ -2731,10 +2734,10 @@ static const JSFunctionSpec intl_static_methods[] = { * Initializes the Intl Object and its standard built-in properties. * Spec: ECMAScript Internationalization API Specification, 8.0, 8.1 */ -bool +/* static */ bool GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global) { - RootedObject proto(cx, global->getOrCreateObjectPrototype(cx)); + RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); if (!proto) return false; diff --git a/js/src/builtin/IntlTimeZoneData.h b/js/src/builtin/IntlTimeZoneData.h index fa808c0b94..1612f0f6be 100644 --- a/js/src/builtin/IntlTimeZoneData.h +++ b/js/src/builtin/IntlTimeZoneData.h @@ -1,5 +1,5 @@ // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2018e +// tzdata version = 2019c #ifndef builtin_IntlTimeZoneData_h #define builtin_IntlTimeZoneData_h diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp index c496cfb778..34e2e566d9 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -164,7 +164,7 @@ MapIteratorObject::kind() const return MapObject::IteratorKind(i); } -bool +/* static */ bool GlobalObject::initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global) { Rooted<JSObject*> base(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global)); @@ -924,7 +924,7 @@ SetIteratorObject::kind() const return SetObject::IteratorKind(i); } -bool +/* static */ bool GlobalObject::initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global) { Rooted<JSObject*> base(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global)); diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp index 333cb3e11b..575bab0b0c 100644 --- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -103,7 +103,7 @@ GlobalObject::initImportEntryProto(JSContext* cx, Handle<GlobalObject*> global) JS_PS_END }; - RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx)); + RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global)); if (!proto) return false; @@ -169,7 +169,7 @@ GlobalObject::initExportEntryProto(JSContext* cx, Handle<GlobalObject*> global) JS_PS_END }; - RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx)); + RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global)); if (!proto) return false; @@ -768,7 +768,7 @@ AssertModuleScopesMatch(ModuleObject* module) } void -ModuleObject::fixEnvironmentsAfterCompartmentMerge(JSContext* cx) +ModuleObject::fixEnvironmentsAfterCompartmentMerge() { AssertModuleScopesMatch(this); initialEnvironment().fixEnclosingEnvironmentAfterCompartmentMerge(script()->global()); @@ -927,7 +927,7 @@ ModuleObject::evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValu ModuleObject::createNamespace(JSContext* cx, HandleModuleObject self, HandleObject exports) { MOZ_ASSERT(!self->namespace_()); - MOZ_ASSERT(exports->is<ArrayObject>() || exports->is<UnboxedArrayObject>()); + MOZ_ASSERT(exports->is<ArrayObject>()); RootedModuleNamespaceObject ns(cx, ModuleNamespaceObject::create(cx, self)); if (!ns) @@ -1000,7 +1000,7 @@ GlobalObject::initModuleProto(JSContext* cx, Handle<GlobalObject*> global) JS_FS_END }; - RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx)); + RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global)); if (!proto) return false; diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h index 51a428271d..22db762ac6 100644 --- a/js/src/builtin/ModuleObject.h +++ b/js/src/builtin/ModuleObject.h @@ -237,7 +237,7 @@ class ModuleObject : public NativeObject #ifdef DEBUG static bool IsFrozen(JSContext* cx, HandleModuleObject self); #endif - void fixEnvironmentsAfterCompartmentMerge(JSContext* cx); + void fixEnvironmentsAfterCompartmentMerge(); JSScript* script() const; Scope* enclosingScope() const; diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index cd4ac122c9..bfcc8d20ef 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -9,11 +9,13 @@ #include "mozilla/ArrayUtils.h" #include "jscntxt.h" +#include "jsstr.h" #include "builtin/Eval.h" #include "frontend/BytecodeCompiler.h" #include "jit/InlinableNatives.h" #include "js/UniquePtr.h" +#include "vm/AsyncFunction.h" #include "vm/StringBuffer.h" #include "jsobjinlines.h" @@ -124,6 +126,27 @@ obj_toSource(JSContext* cx, unsigned argc, Value* vp) return true; } +template <typename CharT> +static bool +Consume(const CharT*& s, const CharT* e, const char *chars) +{ + size_t len = strlen(chars); + if (s + len >= e) + return false; + if (!EqualChars(s, chars, len)) + return false; + s += len; + return true; +} + +template <typename CharT> +static void +ConsumeSpaces(const CharT*& s, const CharT* e) +{ + while (*s == ' ' && s < e) + s++; +} + /* * Given a function source string, return the offset and length of the part * between '(function $name' and ')'. @@ -133,37 +156,53 @@ static bool ArgsAndBodySubstring(mozilla::Range<const CharT> chars, size_t* outOffset, size_t* outLen) { const CharT* const start = chars.begin().get(); - const CharT* const end = chars.end().get(); const CharT* s = start; + const CharT* e = chars.end().get(); - uint8_t parenChomp = 0; - if (s[0] == '(') { - s++; - parenChomp = 1; - } - - /* Try to jump "function" keyword. */ - s = js_strchr_limit(s, ' ', end); - if (!s) + if (s == e) return false; - /* - * Jump over the function's name: it can't be encoded as part - * of an ECMA getter or setter. - */ - s = js_strchr_limit(s, '(', end); - if (!s) - return false; + // Remove enclosing parentheses. + if (*s == '(' && *(e - 1) == ')') { + s++; + e--; + } - if (*s == ' ') + (void) Consume(s, e, "async"); + ConsumeSpaces(s, e); + (void) (Consume(s, e, "function") || Consume(s, e, "get") || Consume(s, e, "set")); + ConsumeSpaces(s, e); + (void) Consume(s, e, "*"); + ConsumeSpaces(s, e); + + // Jump over the function's name. + if (Consume(s, e, "[")) { + s = js_strchr_limit(s, ']', e); + if (!s) + return false; s++; + ConsumeSpaces(s, e); + if (*s != '(') + return false; + } else { + s = js_strchr_limit(s, '(', e); + if (!s) + return false; + } *outOffset = s - start; - *outLen = end - s - parenChomp; + *outLen = e - s; MOZ_ASSERT(*outOffset + *outLen <= chars.length()); return true; } +enum class PropertyKind { + Getter, + Setter, + Method, + Normal +}; + JSString* js::ObjectToSource(JSContext* cx, HandleObject obj) { @@ -182,59 +221,28 @@ js::ObjectToSource(JSContext* cx, HandleObject obj) if (!buf.append('{')) return nullptr; - RootedValue v0(cx), v1(cx); - MutableHandleValue val[2] = {&v0, &v1}; - - RootedString str0(cx), str1(cx); - MutableHandleString gsop[2] = {&str0, &str1}; - AutoIdVector idv(cx); if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_SYMBOLS, &idv)) return nullptr; bool comma = false; - for (size_t i = 0; i < idv.length(); ++i) { - RootedId id(cx, idv[i]); - Rooted<PropertyDescriptor> desc(cx); - if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) - return nullptr; - - int valcnt = 0; - if (desc.object()) { - if (desc.isAccessorDescriptor()) { - if (desc.hasGetterObject() && desc.getterObject()) { - val[valcnt].setObject(*desc.getterObject()); - gsop[valcnt].set(cx->names().get); - valcnt++; - } - if (desc.hasSetterObject() && desc.setterObject()) { - val[valcnt].setObject(*desc.setterObject()); - gsop[valcnt].set(cx->names().set); - valcnt++; - } - } else { - valcnt = 1; - val[0].set(desc.value()); - gsop[0].set(nullptr); - } - } - + auto AddProperty = [cx, &comma, &buf](HandleId id, HandleValue val, PropertyKind kind) -> bool { /* Convert id to a string. */ RootedString idstr(cx); if (JSID_IS_SYMBOL(id)) { RootedValue v(cx, SymbolValue(JSID_TO_SYMBOL(id))); idstr = ValueToSource(cx, v); if (!idstr) - return nullptr; + return false; } else { RootedValue idv(cx, IdToValue(id)); idstr = ToString<CanGC>(cx, idv); if (!idstr) - return nullptr; + return false; /* - * If id is a string that's not an identifier, or if it's a negative - * integer, then it must be quoted. + * If id is a string that's not an identifier, or if it's a + * negative integer, then it must be quoted. */ if (JSID_IS_ATOM(id) ? !IsIdentifier(JSID_TO_ATOM(id)) @@ -242,28 +250,65 @@ js::ObjectToSource(JSContext* cx, HandleObject obj) { idstr = QuoteString(cx, idstr, char16_t('\'')); if (!idstr) - return nullptr; + return false; } } - for (int j = 0; j < valcnt; j++) { - /* Convert val[j] to its canonical source form. */ - JSString* valsource = ValueToSource(cx, val[j]); - if (!valsource) - return nullptr; + RootedString valsource(cx, ValueToSource(cx, val)); + if (!valsource) + return false; - RootedLinearString valstr(cx, valsource->ensureLinear(cx)); - if (!valstr) - return nullptr; + RootedLinearString valstr(cx, valsource->ensureLinear(cx)); + if (!valstr) + return false; - size_t voffset = 0; - size_t vlength = valstr->length(); + if (comma && !buf.append(", ")) + return false; + comma = true; + + size_t voffset, vlength; + + // Methods and accessors can return exact syntax of source, that fits + // into property without adding property name or "get"/"set" prefix. + // Use the exact syntax when the following conditions are met: + // + // * It's a function object + // (exclude proxies) + // * Function's kind and property's kind are same + // (this can be false for dynamically defined properties) + // * Function has explicit name + // (this can be false for computed property and dynamically defined + // properties) + // * Function's name and property's name are same + // (this can be false for dynamically defined properties) + if (kind == PropertyKind::Getter || kind == PropertyKind::Setter || + kind == PropertyKind::Method) + { + RootedFunction fun(cx); + if (val.toObject().is<JSFunction>()) { + fun = &val.toObject().as<JSFunction>(); + // Method's case should be checked on caller. + if (((fun->isGetter() && kind == PropertyKind::Getter) || + (fun->isSetter() && kind == PropertyKind::Setter) || + kind == PropertyKind::Method) && + fun->explicitName()) + { + bool result; + if (!EqualStrings(cx, fun->explicitName(), idstr, &result)) + return false; - /* - * Remove '(function ' from the beginning of valstr and ')' from the - * end so that we can put "get" in front of the function definition. - */ - if (gsop[j] && IsFunctionObject(val[j])) { + if (result) { + if (!buf.append(valstr)) + return false; + return true; + } + } + } + + { + // When falling back try to generate a better string + // representation by skipping the prelude, and also removing + // the enclosing parentheses. bool success; JS::AutoCheckCannotGC nogc; if (valstr->hasLatin1Chars()) @@ -271,29 +316,90 @@ js::ObjectToSource(JSContext* cx, HandleObject obj) else success = ArgsAndBodySubstring(valstr->twoByteRange(nogc), &voffset, &vlength); if (!success) - gsop[j].set(nullptr); + kind = PropertyKind::Normal; } - if (comma && !buf.append(", ")) - return nullptr; - comma = true; + if (kind == PropertyKind::Getter) { + if (!buf.append("get ")) + return false; + } else if (kind == PropertyKind::Setter) { + if (!buf.append("set ")) + return false; + } else if (kind == PropertyKind::Method && fun) { + if (IsWrappedAsyncFunction(fun)) { + if (!buf.append("async ")) + return false; + } - if (gsop[j]) { - if (!buf.append(gsop[j]) || !buf.append(' ')) - return nullptr; + if (fun->isStarGenerator()) { + if (!buf.append('*')) + return false; + } } - if (JSID_IS_SYMBOL(id) && !buf.append('[')) - return nullptr; - if (!buf.append(idstr)) - return nullptr; - if (JSID_IS_SYMBOL(id) && !buf.append(']')) - return nullptr; - if (!buf.append(gsop[j] ? ' ' : ':')) - return nullptr; + } + bool needsBracket = JSID_IS_SYMBOL(id); + if (needsBracket && !buf.append('[')) + return false; + if (!buf.append(idstr)) + return false; + if (needsBracket && !buf.append(']')) + return false; + + if (kind == PropertyKind::Getter || kind == PropertyKind::Setter || + kind == PropertyKind::Method) + { if (!buf.appendSubstring(valstr, voffset, vlength)) - return nullptr; + return false; + } else { + if (!buf.append(':')) + return false; + if (!buf.append(valstr)) + return false; + } + return true; + }; + + RootedId id(cx); + Rooted<PropertyDescriptor> desc(cx); + RootedValue val(cx); + RootedFunction fun(cx); + for (size_t i = 0; i < idv.length(); ++i) { + id = idv[i]; + if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) + return nullptr; + + if (!desc.object()) + continue; + + if (desc.isAccessorDescriptor()) { + if (desc.hasGetterObject() && desc.getterObject()) { + val.setObject(*desc.getterObject()); + if (!AddProperty(id, val, PropertyKind::Getter)) + return nullptr; + } + if (desc.hasSetterObject() && desc.setterObject()) { + val.setObject(*desc.setterObject()); + if (!AddProperty(id, val, PropertyKind::Setter)) + return nullptr; + } + continue; + } + + val.set(desc.value()); + if (IsFunctionObject(val, fun.address())) { + if (IsWrappedAsyncFunction(fun)) + fun = GetUnwrappedAsyncFunction(fun); + + if (fun->isMethod()) { + if (!AddProperty(id, val, PropertyKind::Method)) + return nullptr; + continue; + } } + + if (!AddProperty(id, val, PropertyKind::Normal)) + return nullptr; } if (!buf.append('}')) @@ -419,18 +525,6 @@ js::obj_toString(JSContext* cx, unsigned argc, Value* vp) return true; } - -bool -js::obj_valueOf(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - RootedObject obj(cx, ToObject(cx, args.thisv())); - if (!obj) - return false; - args.rval().setObject(*obj); - return true; -} - static bool obj_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp) { @@ -474,97 +568,6 @@ obj_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp) return true; } -#if JS_HAS_OBJ_WATCHPOINT - -bool -js::WatchHandler(JSContext* cx, JSObject* obj_, jsid id_, const JS::Value& old, - JS::Value* nvp, void* closure) -{ - RootedObject obj(cx, obj_); - RootedId id(cx, id_); - - /* Avoid recursion on (obj, id) already being watched on cx. */ - AutoResolving resolving(cx, obj, id, AutoResolving::WATCH); - if (resolving.alreadyStarted()) - return true; - - FixedInvokeArgs<3> args(cx); - - args[0].set(IdToValue(id)); - args[1].set(old); - args[2].set(*nvp); - - RootedValue callable(cx, ObjectValue(*static_cast<JSObject*>(closure))); - RootedValue thisv(cx, ObjectValue(*obj)); - RootedValue rv(cx); - if (!Call(cx, callable, thisv, args, &rv)) - return false; - - *nvp = rv; - return true; -} - -static bool -obj_watch(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - RootedObject obj(cx, ToObject(cx, args.thisv())); - if (!obj) - return false; - - if (!GlobalObject::warnOnceAboutWatch(cx, obj)) - return false; - - if (args.length() <= 1) { - ReportMissingArg(cx, args.calleev(), 1); - return false; - } - - RootedObject callable(cx, ValueToCallable(cx, args[1], args.length() - 2)); - if (!callable) - return false; - - RootedId propid(cx); - if (!ValueToId<CanGC>(cx, args[0], &propid)) - return false; - - if (!WatchProperty(cx, obj, propid, callable)) - return false; - - args.rval().setUndefined(); - return true; -} - -static bool -obj_unwatch(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - RootedObject obj(cx, ToObject(cx, args.thisv())); - if (!obj) - return false; - - if (!GlobalObject::warnOnceAboutWatch(cx, obj)) - return false; - - RootedId id(cx); - if (args.length() != 0) { - if (!ValueToId<CanGC>(cx, args[0], &id)) - return false; - } else { - id = JSID_VOID; - } - - if (!UnwatchProperty(cx, obj, id)) - return false; - - args.rval().setUndefined(); - return true; -} - -#endif /* JS_HAS_OBJ_WATCHPOINT */ - /* ECMA 15.2.4.5. */ bool js::obj_hasOwnProperty(JSContext* cx, unsigned argc, Value* vp) @@ -1195,11 +1198,7 @@ static const JSFunctionSpec object_methods[] = { #endif JS_FN(js_toString_str, obj_toString, 0,0), JS_SELF_HOSTED_FN(js_toLocaleString_str, "Object_toLocaleString", 0, 0), - JS_FN(js_valueOf_str, obj_valueOf, 0,0), -#if JS_HAS_OBJ_WATCHPOINT - JS_FN(js_watch_str, obj_watch, 2,0), - JS_FN(js_unwatch_str, obj_unwatch, 1,0), -#endif + JS_SELF_HOSTED_FN(js_valueOf_str, "Object_valueOf", 0,0), JS_FN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0), JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0), JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0), @@ -1314,8 +1313,8 @@ FinishObjectClassInit(JSContext* cx, JS::HandleObject ctor, JS::HandleObject pro * only set the [[Prototype]] if it hasn't already been set. */ Rooted<TaggedProto> tagged(cx, TaggedProto(proto)); - if (global->shouldSplicePrototype(cx)) { - if (!global->splicePrototype(cx, global->getClass(), tagged)) + if (global->shouldSplicePrototype()) { + if (!JSObject::splicePrototype(cx, global, global->getClass(), tagged)) return false; } return true; diff --git a/js/src/builtin/Object.h b/js/src/builtin/Object.h index 09512be369..8231888b1d 100644 --- a/js/src/builtin/Object.h +++ b/js/src/builtin/Object.h @@ -25,9 +25,6 @@ obj_construct(JSContext* cx, unsigned argc, JS::Value* vp); MOZ_MUST_USE bool obj_propertyIsEnumerable(JSContext* cx, unsigned argc, Value* vp); -MOZ_MUST_USE bool -obj_valueOf(JSContext* cx, unsigned argc, JS::Value* vp); - PlainObject* ObjectCreateImpl(JSContext* cx, HandleObject proto, NewObjectKind newKind = GenericObject, HandleObjectGroup group = nullptr); diff --git a/js/src/builtin/Object.js b/js/src/builtin/Object.js index a7440aec72..9ed1be0e12 100644 --- a/js/src/builtin/Object.js +++ b/js/src/builtin/Object.js @@ -87,6 +87,12 @@ function Object_toLocaleString() { return callContentFunction(O.toString, O); } +// ES 2017 draft bb96899bb0d9ef9be08164a26efae2ee5f25e875 19.1.3.7 +function Object_valueOf() { + // Step 1. + return ToObject(this); +} + // ES7 draft (2016 March 8) B.2.2.3 function ObjectDefineSetter(name, setter) { // Step 1. diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index c781a336de..ec7845e89d 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -603,7 +603,7 @@ ResolvePromise(JSContext* cx, Handle<PromiseObject*> promise, HandleValue valueO // Now that everything else is done, do the things the debugger needs. // Step 7 of RejectPromise implemented in onSettled. - promise->onSettled(cx); + PromiseObject::onSettled(cx, promise); // Step 7 of FulfillPromise. // Step 8 of RejectPromise. @@ -1947,26 +1947,23 @@ PerformPromiseRace(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, } // ES2016, Sub-steps of 25.4.4.4 and 25.4.4.5. -static MOZ_MUST_USE bool -CommonStaticResolveRejectImpl(JSContext* cx, unsigned argc, Value* vp, ResolutionMode mode) +static MOZ_MUST_USE JSObject* +CommonStaticResolveRejectImpl(JSContext* cx, HandleValue thisVal, HandleValue argVal, + ResolutionMode mode) { - CallArgs args = CallArgsFromVp(argc, vp); - RootedValue x(cx, args.get(0)); - // Steps 1-2. - if (!args.thisv().isObject()) { + if (!thisVal.isObject()) { const char* msg = mode == ResolveMode ? "Receiver of Promise.resolve call" : "Receiver of Promise.reject call"; JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, msg); - return false; + return nullptr; } - RootedValue cVal(cx, args.thisv()); - RootedObject C(cx, &cVal.toObject()); + RootedObject C(cx, &thisVal.toObject()); // Step 3 of Resolve. - if (mode == ResolveMode && x.isObject()) { - RootedObject xObj(cx, &x.toObject()); + if (mode == ResolveMode && argVal.isObject()) { + RootedObject xObj(cx, &argVal.toObject()); bool isPromise = false; if (xObj->is<PromiseObject>()) { isPromise = true; @@ -1985,11 +1982,9 @@ CommonStaticResolveRejectImpl(JSContext* cx, unsigned argc, Value* vp, Resolutio if (isPromise) { RootedValue ctorVal(cx); if (!GetProperty(cx, xObj, xObj, cx->names().constructor, &ctorVal)) - return false; - if (ctorVal == cVal) { - args.rval().set(x); - return true; - } + return nullptr; + if (ctorVal == thisVal) + return xObj; } } @@ -1998,15 +1993,17 @@ CommonStaticResolveRejectImpl(JSContext* cx, unsigned argc, Value* vp, Resolutio RootedObject resolveFun(cx); RootedObject rejectFun(cx); if (!NewPromiseCapability(cx, C, &promise, &resolveFun, &rejectFun, true)) - return false; + return nullptr; // Step 5 of Resolve, 4 of Reject. - if (!RunResolutionFunction(cx, mode == ResolveMode ? resolveFun : rejectFun, x, mode, promise)) - return false; + if (!RunResolutionFunction(cx, mode == ResolveMode ? resolveFun : rejectFun, argVal, mode, + promise)) + { + return nullptr; + } // Step 6 of Resolve, 4 of Reject. - args.rval().setObject(*promise); - return true; + return promise; } /** @@ -2015,7 +2012,14 @@ CommonStaticResolveRejectImpl(JSContext* cx, unsigned argc, Value* vp, Resolutio bool js::Promise_reject(JSContext* cx, unsigned argc, Value* vp) { - return CommonStaticResolveRejectImpl(cx, argc, vp, RejectMode); + CallArgs args = CallArgsFromVp(argc, vp); + RootedValue thisVal(cx, args.thisv()); + RootedValue argVal(cx, args.get(0)); + JSObject* result = CommonStaticResolveRejectImpl(cx, thisVal, argVal, RejectMode); + if (!result) + return false; + args.rval().setObject(*result); + return true; } /** @@ -2024,19 +2028,11 @@ js::Promise_reject(JSContext* cx, unsigned argc, Value* vp) /* static */ JSObject* PromiseObject::unforgeableReject(JSContext* cx, HandleValue value) { - // Steps 1-2 (omitted). - - // Roughly step 3. - Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx)); - if (!promise) + RootedObject promiseCtor(cx, JS::GetPromiseConstructor(cx)); + if (!promiseCtor) return nullptr; - - // Roughly step 4. - if (!ResolvePromise(cx, promise, value, JS::PromiseState::Rejected)) - return nullptr; - - // Step 5. - return promise; + RootedValue cVal(cx, ObjectValue(*promiseCtor)); + return CommonStaticResolveRejectImpl(cx, cVal, value, RejectMode); } /** @@ -2045,7 +2041,14 @@ PromiseObject::unforgeableReject(JSContext* cx, HandleValue value) bool js::Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp) { - return CommonStaticResolveRejectImpl(cx, argc, vp, ResolveMode); + CallArgs args = CallArgsFromVp(argc, vp); + RootedValue thisVal(cx, args.thisv()); + RootedValue argVal(cx, args.get(0)); + JSObject* result = CommonStaticResolveRejectImpl(cx, thisVal, argVal, ResolveMode); + if (!result) + return false; + args.rval().setObject(*result); + return true; } /** @@ -2054,30 +2057,11 @@ js::Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp) /* static */ JSObject* PromiseObject::unforgeableResolve(JSContext* cx, HandleValue value) { - // Steps 1-2 (omitted). - - // Step 3. - if (value.isObject()) { - JSObject* obj = &value.toObject(); - if (IsWrapper(obj)) - obj = CheckedUnwrap(obj); - // Instead of getting the `constructor` property, do an unforgeable - // check. - if (obj && obj->is<PromiseObject>()) - return obj; - } - - // Step 4. - Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx)); - if (!promise) + RootedObject promiseCtor(cx, JS::GetPromiseConstructor(cx)); + if (!promiseCtor) return nullptr; - - // Steps 5. - if (!ResolvePromiseInternal(cx, promise, value)) - return nullptr; - - // Step 6. - return promise; + RootedValue cVal(cx, ObjectValue(*promiseCtor)); + return CommonStaticResolveRejectImpl(cx, cVal, value, ResolveMode); } // ES2016, 25.4.4.6, implemented in Promise.js. @@ -2647,14 +2631,14 @@ PromiseObject::dependentPromises(JSContext* cx, MutableHandle<GCVector<Value>> v return true; } -bool -PromiseObject::resolve(JSContext* cx, HandleValue resolutionValue) +/* static */ bool +PromiseObject::resolve(JSContext* cx, Handle<PromiseObject*> promise, HandleValue resolutionValue) { - MOZ_ASSERT(!PromiseHasAnyFlag(*this, PROMISE_FLAG_ASYNC)); - if (state() != JS::PromiseState::Pending) + MOZ_ASSERT(!PromiseHasAnyFlag(*promise, PROMISE_FLAG_ASYNC)); + if (promise->state() != JS::PromiseState::Pending) return true; - RootedObject resolveFun(cx, GetResolveFunctionFromPromise(this)); + RootedObject resolveFun(cx, GetResolveFunctionFromPromise(promise)); RootedValue funVal(cx, ObjectValue(*resolveFun)); // For xray'd Promises, the resolve fun may have been created in another @@ -2670,14 +2654,14 @@ PromiseObject::resolve(JSContext* cx, HandleValue resolutionValue) return Call(cx, funVal, UndefinedHandleValue, args, &dummy); } -bool -PromiseObject::reject(JSContext* cx, HandleValue rejectionValue) +/* static */ bool +PromiseObject::reject(JSContext* cx, Handle<PromiseObject*> promise, HandleValue rejectionValue) { - MOZ_ASSERT(!PromiseHasAnyFlag(*this, PROMISE_FLAG_ASYNC)); - if (state() != JS::PromiseState::Pending) + MOZ_ASSERT(!PromiseHasAnyFlag(*promise, PROMISE_FLAG_ASYNC)); + if (promise->state() != JS::PromiseState::Pending) return true; - RootedValue funVal(cx, this->getFixedSlot(PromiseSlot_RejectFunction)); + RootedValue funVal(cx, promise->getFixedSlot(PromiseSlot_RejectFunction)); MOZ_ASSERT(IsCallable(funVal)); FixedInvokeArgs<1> args(cx); @@ -2687,10 +2671,9 @@ PromiseObject::reject(JSContext* cx, HandleValue rejectionValue) return Call(cx, funVal, UndefinedHandleValue, args, &dummy); } -void -PromiseObject::onSettled(JSContext* cx) +/* static */ void +PromiseObject::onSettled(JSContext* cx, Handle<PromiseObject*> promise) { - Rooted<PromiseObject*> promise(cx, this); RootedObject stack(cx); if (cx->options().asyncStack() || cx->compartment()->isDebuggee()) { if (!JS::CaptureCurrentStack(cx, &stack, JS::StackCapture(JS::AllFrames()))) { @@ -2750,7 +2733,7 @@ PromiseTask::executeAndFinish(JSContext* cx) static JSObject* CreatePromisePrototype(JSContext* cx, JSProtoKey key) { - return cx->global()->createBlankPrototype(cx, &PromiseObject::protoClass_); + return GlobalObject::createBlankPrototype(cx, cx->global(), &PromiseObject::protoClass_); } static const JSFunctionSpec promise_methods[] = { diff --git a/js/src/builtin/Promise.h b/js/src/builtin/Promise.h index bb47786316..c76dc358c7 100644 --- a/js/src/builtin/Promise.h +++ b/js/src/builtin/Promise.h @@ -66,10 +66,12 @@ class PromiseObject : public NativeObject return getFixedSlot(PromiseSlot_ReactionsOrResult); } - MOZ_MUST_USE bool resolve(JSContext* cx, HandleValue resolutionValue); - MOZ_MUST_USE bool reject(JSContext* cx, HandleValue rejectionValue); + static MOZ_MUST_USE bool resolve(JSContext* cx, Handle<PromiseObject*> promise, + HandleValue resolutionValue); + static MOZ_MUST_USE bool reject(JSContext* cx, Handle<PromiseObject*> promise, + HandleValue rejectionValue); - void onSettled(JSContext* cx); + static void onSettled(JSContext* cx, Handle<PromiseObject*> promise); double allocationTime() { return getFixedSlot(PromiseSlot_AllocationTime).toNumber(); } double resolutionTime() { return getFixedSlot(PromiseSlot_ResolutionTime).toNumber(); } diff --git a/js/src/builtin/Reflect.cpp b/js/src/builtin/Reflect.cpp index 2f509a2268..4e7fae78c3 100644 --- a/js/src/builtin/Reflect.cpp +++ b/js/src/builtin/Reflect.cpp @@ -268,7 +268,8 @@ static const JSFunctionSpec methods[] = { JSObject* js::InitReflect(JSContext* cx, HandleObject obj) { - RootedObject proto(cx, obj->as<GlobalObject>().getOrCreateObjectPrototype(cx)); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); + RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); if (!proto) return nullptr; diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index beff58e133..8e8bb24174 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -3044,7 +3044,12 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos)); RootedValue expr(cx); - expr.setString(next->pn_atom); + if (next->isKind(PNK_RAW_UNDEFINED)) { + expr.setUndefined(); + } else { + MOZ_ASSERT(next->isKind(PNK_TEMPLATE_STRING)); + expr.setString(next->pn_atom); + } cooked.infallibleAppend(expr); } @@ -3136,6 +3141,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case PNK_TRUE: case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: return literal(pn, dst); case PNK_YIELD_STAR: @@ -3216,6 +3222,8 @@ ASTSerializer::property(ParseNode* pn, MutableHandleValue dst) return expression(pn->pn_kid, &val) && builder.prototypeMutation(val, &pn->pn_pos, dst); } + if (pn->isKind(PNK_SPREAD)) + return expression(pn, dst); PropKind kind; switch (pn->getOp()) { @@ -3276,6 +3284,10 @@ ASTSerializer::literal(ParseNode* pn, MutableHandleValue dst) val.setNull(); break; + case PNK_RAW_UNDEFINED: + val.setUndefined(); + break; + case PNK_TRUE: val.setBoolean(true); break; @@ -3332,6 +3344,16 @@ ASTSerializer::objectPattern(ParseNode* pn, MutableHandleValue dst) return false; for (ParseNode* propdef = pn->pn_head; propdef; propdef = propdef->pn_next) { + if (propdef->isKind(PNK_SPREAD)) { + RootedValue target(cx); + RootedValue spread(cx); + if (!pattern(propdef->pn_kid, &target)) + return false; + if(!builder.spreadExpression(target, &propdef->pn_pos, &spread)) + return false; + elts.infallibleAppend(spread); + continue; + } LOCAL_ASSERT(propdef->isKind(PNK_MUTATEPROTO) != propdef->isOp(JSOP_INITPROP)); RootedValue key(cx); @@ -3407,12 +3429,7 @@ ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst) : GeneratorStyle::None; bool isAsync = pn->pn_funbox->isAsync(); - bool isExpression = -#if JS_HAS_EXPR_CLOSURES - func->isExprBody(); -#else - false; -#endif + bool isExpression = pn->pn_funbox->isExprBody(); RootedValue id(cx); RootedAtom funcAtom(cx, func->explicitName()); diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index b20f41c53e..7cf20d23c5 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -140,12 +140,12 @@ ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, RegExpShared& re, HandleLin /* Legacy ExecuteRegExp behavior is baked into the JSAPI. */ bool -js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, RegExpObject& reobj, +js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*> reobj, HandleLinearString input, size_t* lastIndex, bool test, MutableHandleValue rval) { RegExpGuard shared(cx); - if (!reobj.getShared(cx, &shared)) + if (!RegExpObject::getShared(cx, reobj, &shared)) return false; ScopedMatchPairs matches(&cx->tempLifoAlloc()); @@ -801,7 +801,7 @@ const JSFunctionSpec js::regexp_methods[] = { name(JSContext* cx, unsigned argc, Value* vp) \ { \ CallArgs args = CallArgsFromVp(argc, vp); \ - RegExpStatics* res = cx->global()->getRegExpStatics(cx); \ + RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global()); \ if (!res) \ return false; \ code; \ @@ -827,7 +827,7 @@ DEFINE_STATIC_GETTER(static_paren9_getter, STATIC_PAREN_GETTER_CODE(9)) static bool \ name(JSContext* cx, unsigned argc, Value* vp) \ { \ - RegExpStatics* res = cx->global()->getRegExpStatics(cx); \ + RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global()); \ if (!res) \ return false; \ code; \ @@ -838,7 +838,7 @@ static bool static_input_setter(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - RegExpStatics* res = cx->global()->getRegExpStatics(cx); + RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global()); if (!res) return false; @@ -918,12 +918,12 @@ ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string, Rooted<RegExpObject*> reobj(cx, ®exp->as<RegExpObject>()); RegExpGuard re(cx); - if (!reobj->getShared(cx, &re)) + if (!RegExpObject::getShared(cx, reobj, &re)) return RegExpRunStatus_Error; RegExpStatics* res; if (staticsUpdate == UpdateRegExpStatics) { - res = cx->global()->getRegExpStatics(cx); + res = GlobalObject::getRegExpStatics(cx, cx->global()); if (!res) return RegExpRunStatus_Error; } else { @@ -1725,7 +1725,7 @@ js::intrinsic_GetElemBaseForLambda(JSContext* cx, unsigned argc, Value* vp) if (!fun->isInterpreted() || fun->isClassConstructor()) return true; - JSScript* script = fun->getOrCreateScript(cx); + JSScript* script = JSFunction::getOrCreateScript(cx, fun); if (!script) return false; @@ -1800,7 +1800,7 @@ js::intrinsic_GetStringDataProperty(JSContext* cx, unsigned argc, Value* vp) return false; RootedValue v(cx); - if (HasDataProperty(cx, nobj, AtomToId(atom), v.address()) && v.isString()) + if (GetPropertyPure(cx, nobj, AtomToId(atom), v.address()) && v.isString()) args.rval().set(v); else args.rval().setUndefined(); diff --git a/js/src/builtin/RegExp.h b/js/src/builtin/RegExp.h index 715656f402..4e0ff69484 100644 --- a/js/src/builtin/RegExp.h +++ b/js/src/builtin/RegExp.h @@ -31,7 +31,7 @@ enum RegExpStaticsUpdate { UpdateRegExpStatics, DontUpdateRegExpStatics }; * |chars| and |length|. */ MOZ_MUST_USE bool -ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, RegExpObject& reobj, +ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*> reobj, HandleLinearString input, size_t* lastIndex, bool test, MutableHandleValue rval); diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index 2383922db8..5fe6911521 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -475,7 +475,7 @@ const Class SimdObject::class_ = { &SimdObjectClassOps }; -bool +/* static */ bool GlobalObject::initSimdObject(JSContext* cx, Handle<GlobalObject*> global) { // SIMD relies on the TypedObject module being initialized. @@ -483,11 +483,11 @@ GlobalObject::initSimdObject(JSContext* cx, Handle<GlobalObject*> global) // to be able to call GetTypedObjectModule(). It is NOT necessary // to install the TypedObjectModule global, but at the moment // those two things are not separable. - if (!global->getOrCreateTypedObjectModule(cx)) + if (!GlobalObject::getOrCreateTypedObjectModule(cx, global)) return false; RootedObject globalSimdObject(cx); - RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); + RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); if (!objProto) return false; @@ -510,7 +510,7 @@ static bool CreateSimdType(JSContext* cx, Handle<GlobalObject*> global, HandlePropertyName stringRepr, SimdType simdType, const JSFunctionSpec* methods) { - RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx)); + RootedObject funcProto(cx, GlobalObject::getOrCreateFunctionPrototype(cx, global)); if (!funcProto) return false; @@ -531,7 +531,7 @@ CreateSimdType(JSContext* cx, Handle<GlobalObject*> global, HandlePropertyName s return false; // Create prototype property, which inherits from Object.prototype. - RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); + RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); if (!objProto) return false; Rooted<TypedProto*> proto(cx); @@ -551,7 +551,7 @@ CreateSimdType(JSContext* cx, Handle<GlobalObject*> global, HandlePropertyName s } // Bind type descriptor to the global SIMD object - RootedObject globalSimdObject(cx, global->getOrCreateSimdGlobalObject(cx)); + RootedObject globalSimdObject(cx, GlobalObject::getOrCreateSimdGlobalObject(cx, global)); MOZ_ASSERT(globalSimdObject); RootedValue typeValue(cx, ObjectValue(*typeDescr)); @@ -568,7 +568,7 @@ CreateSimdType(JSContext* cx, Handle<GlobalObject*> global, HandlePropertyName s return !!typeDescr; } -bool +/* static */ bool GlobalObject::initSimdType(JSContext* cx, Handle<GlobalObject*> global, SimdType simdType) { #define CREATE_(Type) \ @@ -584,13 +584,13 @@ GlobalObject::initSimdType(JSContext* cx, Handle<GlobalObject*> global, SimdType #undef CREATE_ } -SimdTypeDescr* +/* static */ SimdTypeDescr* GlobalObject::getOrCreateSimdTypeDescr(JSContext* cx, Handle<GlobalObject*> global, SimdType simdType) { MOZ_ASSERT(unsigned(simdType) < unsigned(SimdType::Count), "Invalid SIMD type"); - RootedObject globalSimdObject(cx, global->getOrCreateSimdGlobalObject(cx)); + RootedObject globalSimdObject(cx, GlobalObject::getOrCreateSimdGlobalObject(cx, global)); if (!globalSimdObject) return nullptr; @@ -628,8 +628,8 @@ SimdObject::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* JSObject* js::InitSimdClass(JSContext* cx, HandleObject obj) { - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); - return global->getOrCreateSimdGlobalObject(cx); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); + return GlobalObject::getOrCreateSimdGlobalObject(cx, global); } template<typename V> diff --git a/js/src/builtin/String.js b/js/src/builtin/String.js index e5b2ad5525..f830b1aa24 100644 --- a/js/src/builtin/String.js +++ b/js/src/builtin/String.js @@ -828,16 +828,16 @@ function String_static_trim(string) { return callFunction(std_String_trim, string); } -function String_static_trimLeft(string) { +function String_static_trimStart(string) { if (arguments.length < 1) - ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimLeft'); - return callFunction(std_String_trimLeft, string); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimStart'); + return callFunction(std_String_trimStart, string); } -function String_static_trimRight(string) { +function String_static_trimEnd(string) { if (arguments.length < 1) - ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimRight'); - return callFunction(std_String_trimRight, string); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimEnd'); + return callFunction(std_String_trimEnd, string); } function String_static_toLocaleLowerCase(string) { diff --git a/js/src/builtin/SymbolObject.cpp b/js/src/builtin/SymbolObject.cpp index cf48402d69..8fa860ef3d 100644 --- a/js/src/builtin/SymbolObject.cpp +++ b/js/src/builtin/SymbolObject.cpp @@ -33,6 +33,7 @@ SymbolObject::create(JSContext* cx, JS::HandleSymbol symbol) } const JSPropertySpec SymbolObject::properties[] = { + JS_PSG("description", descriptionGetter, 0), JS_PS_END }; @@ -52,17 +53,17 @@ const JSFunctionSpec SymbolObject::staticMethods[] = { JSObject* SymbolObject::initClass(JSContext* cx, HandleObject obj) { - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); // This uses &JSObject::class_ because: "The Symbol prototype object is an // ordinary object. It is not a Symbol instance and does not have a // [[SymbolData]] internal slot." (ES6 rev 24, 19.4.3) - RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx)); + RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global)); if (!proto) return nullptr; - RootedFunction ctor(cx, global->createConstructor(cx, construct, - ClassName(JSProto_Symbol, cx), 0)); + RootedFunction ctor(cx, GlobalObject::createConstructor(cx, construct, + ClassName(JSProto_Symbol, cx), 0)); if (!ctor) return nullptr; @@ -227,6 +228,34 @@ SymbolObject::toPrimitive(JSContext* cx, unsigned argc, Value* vp) return CallNonGenericMethod<IsSymbol, valueOf_impl>(cx, args); } +// ES2019 Stage 4 Draft / November 28, 2018 +// Symbol description accessor +// See: https://tc39.github.io/proposal-Symbol-description/ +bool +SymbolObject::descriptionGetter_impl(JSContext* cx, const CallArgs& args) +{ + // Get symbol object pointer. + HandleValue thisv = args.thisv(); + MOZ_ASSERT(IsSymbol(thisv)); + Rooted<Symbol*> sym(cx, thisv.isSymbol() + ? thisv.toSymbol() + : thisv.toObject().as<SymbolObject>().unbox()); + + // Return the symbol's description if present, otherwise return undefined. + if (JSString* str = sym->description()) + args.rval().setString(str); + else + args.rval().setUndefined(); + return true; +} + +bool +SymbolObject::descriptionGetter(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod<IsSymbol, descriptionGetter_impl>(cx, args); +} + JSObject* js::InitSymbolClass(JSContext* cx, HandleObject obj) { diff --git a/js/src/builtin/SymbolObject.h b/js/src/builtin/SymbolObject.h index 0f204b494a..e10b42d532 100644 --- a/js/src/builtin/SymbolObject.h +++ b/js/src/builtin/SymbolObject.h @@ -52,6 +52,10 @@ class SymbolObject : public NativeObject static MOZ_MUST_USE bool valueOf(JSContext* cx, unsigned argc, Value* vp); static MOZ_MUST_USE bool toPrimitive(JSContext* cx, unsigned argc, Value* vp); + // Properties defined on Symbol.prototype. + static MOZ_MUST_USE bool descriptionGetter_impl(JSContext* cx, const CallArgs& args); + static MOZ_MUST_USE bool descriptionGetter(JSContext* cx, unsigned argc, Value *vp); + static const JSPropertySpec properties[]; static const JSFunctionSpec methods[]; static const JSFunctionSpec staticMethods[]; diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 373b6c9edc..4363c7aed8 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -239,12 +239,11 @@ GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) value = BooleanValue(true); if (!JS_SetProperty(cx, info, "intl-api", value)) return false; - -#if defined(SOLARIS) +#ifdef XP_SOLARIS value = BooleanValue(false); #else value = BooleanValue(true); -#endif +#endif if (!JS_SetProperty(cx, info, "mapped-array-buffer", value)) return false; @@ -3222,13 +3221,13 @@ ByteSizeOfScript(JSContext*cx, unsigned argc, Value* vp) return false; } - JSFunction* fun = &args[0].toObject().as<JSFunction>(); + RootedFunction fun(cx, &args[0].toObject().as<JSFunction>()); if (fun->isNative()) { JS_ReportErrorASCII(cx, "Argument must be a scripted function"); return false; } - RootedScript script(cx, fun->getOrCreateScript(cx)); + RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun)); if (!script) return false; @@ -3323,7 +3322,8 @@ GetConstructorName(JSContext* cx, unsigned argc, Value* vp) } RootedAtom name(cx); - if (!args[0].toObject().constructorDisplayAtom(cx, &name)) + RootedObject obj(cx, &args[0].toObject()); + if (!JSObject::constructorDisplayAtom(cx, obj, &name)) return false; if (name) { @@ -4027,7 +4027,7 @@ DisRegExp(JSContext* cx, unsigned argc, Value* vp) return false; } - if (!reobj->dumpBytecode(cx, match_only, input)) + if (!RegExpObject::dumpBytecode(cx, reobj, match_only, input)) return false; args.rval().setUndefined(); @@ -4046,6 +4046,32 @@ IsConstructor(JSContext* cx, unsigned argc, Value* vp) return true; } +static bool +GetErrorNotes(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (!args.requireAtLeast(cx, "getErrorNotes", 1)) + return false; + + if (!args[0].isObject() || !args[0].toObject().is<ErrorObject>()) { + args.rval().setNull(); + return true; + } + + JSErrorReport* report = args[0].toObject().as<ErrorObject>().getErrorReport(); + if (!report) { + args.rval().setNull(); + return true; + } + + RootedObject notesArray(cx, CreateErrorNotesArray(cx, report)); + if (!notesArray) + return false; + + args.rval().setObject(*notesArray); + return true; +} + static const JSFunctionSpecWithHelp TestingFunctions[] = { JS_FN_HELP("gc", ::GC, 0, 0, "gc([obj] | 'zone' [, 'shrinking'])", @@ -4580,6 +4606,10 @@ static const JSFunctionSpecWithHelp FuzzingUnsafeTestingFunctions[] = { " Dumps RegExp bytecode."), #endif + JS_FN_HELP("getErrorNotes", GetErrorNotes, 1, 0, +"getErrorNotes(error)", +" Returns an array of error notes."), + JS_FS_HELP_END }; diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index ae74f01bf6..50bf0b8360 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -652,7 +652,7 @@ ArrayMetaTypeDescr::create(JSContext* cx, if (!CreateTraceList(cx, obj)) return nullptr; - if (!cx->zone()->typeDescrObjects.put(obj)) { + if (!cx->zone()->addTypeDescrObject(cx, obj)) { ReportOutOfMemory(cx); return nullptr; } @@ -993,8 +993,8 @@ StructMetaTypeDescr::create(JSContext* cx, if (!CreateTraceList(cx, descr)) return nullptr; - if (!cx->zone()->typeDescrObjects.put(descr) || - !cx->zone()->typeDescrObjects.put(fieldTypeVec)) + if (!cx->zone()->addTypeDescrObject(cx, descr) || + !cx->zone()->addTypeDescrObject(cx, fieldTypeVec)) { ReportOutOfMemory(cx); return nullptr; @@ -1124,11 +1124,11 @@ DefineSimpleTypeDescr(JSContext* cx, typename T::Type type, HandlePropertyName className) { - RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); + RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); if (!objProto) return false; - RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx)); + RootedObject funcProto(cx, GlobalObject::getOrCreateFunctionPrototype(cx, global)); if (!funcProto) return false; @@ -1165,10 +1165,8 @@ DefineSimpleTypeDescr(JSContext* cx, if (!CreateTraceList(cx, descr)) return false; - if (!cx->zone()->typeDescrObjects.put(descr)) { - ReportOutOfMemory(cx); + if (!cx->zone()->addTypeDescrObject(cx, descr)) return false; - } return true; } @@ -1187,7 +1185,7 @@ DefineMetaTypeDescr(JSContext* cx, if (!className) return nullptr; - RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx)); + RootedObject funcProto(cx, GlobalObject::getOrCreateFunctionPrototype(cx, global)); if (!funcProto) return nullptr; @@ -1199,7 +1197,7 @@ DefineMetaTypeDescr(JSContext* cx, // Create ctor.prototype.prototype, which inherits from Object.__proto__ - RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); + RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); if (!objProto) return nullptr; RootedObject protoProto(cx); @@ -1218,7 +1216,7 @@ DefineMetaTypeDescr(JSContext* cx, const int constructorLength = 2; RootedFunction ctor(cx); - ctor = global->createConstructor(cx, T::construct, className, constructorLength); + ctor = GlobalObject::createConstructor(cx, T::construct, className, constructorLength); if (!ctor || !LinkConstructorAndPrototype(cx, ctor, proto) || !DefinePropertiesAndFunctions(cx, proto, @@ -1242,10 +1240,10 @@ DefineMetaTypeDescr(JSContext* cx, * initializer for the `TypedObject` class populate the * `TypedObject` global (which is referred to as "module" herein). */ -bool +/* static */ bool GlobalObject::initTypedObjectModule(JSContext* cx, Handle<GlobalObject*> global) { - RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); + RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); if (!objProto) return false; @@ -1319,9 +1317,8 @@ GlobalObject::initTypedObjectModule(JSContext* cx, Handle<GlobalObject*> global) JSObject* js::InitTypedObjectModuleObject(JSContext* cx, HandleObject obj) { - MOZ_ASSERT(obj->is<GlobalObject>()); - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); - return global->getOrCreateTypedObjectModule(cx); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); + return GlobalObject::getOrCreateTypedObjectModule(cx, global); } /****************************************************************************** @@ -2218,7 +2215,6 @@ const ObjectOps TypedObject::objectOps_ = { TypedObject::obj_setProperty, TypedObject::obj_getOwnPropertyDescriptor, TypedObject::obj_deleteProperty, - nullptr, nullptr, /* watch/unwatch */ nullptr, /* getElements */ TypedObject::obj_enumerate, nullptr, /* thisValue */ diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js index c73bc5e7f7..d5f233d050 100644 --- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -229,6 +229,69 @@ function GetInternalError(msg) { // To be used when a function is required but calling it shouldn't do anything. function NullFunction() {} +// Object Rest/Spread Properties proposal +// Abstract operation: CopyDataProperties (target, source, excluded) +function CopyDataProperties(target, source, excluded) { + // Step 1. + assert(IsObject(target), "target is an object"); + + // Step 2. + assert(IsObject(excluded), "excluded is an object"); + + // Steps 3, 6. + if (source === undefined || source === null) + return; + + // Step 4.a. + source = ToObject(source); + + // Step 4.b. + var keys = OwnPropertyKeys(source, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS); + + // Step 5. + for (var index = 0; index < keys.length; index++) { + var key = keys[index]; + + // We abbreviate this by calling propertyIsEnumerable which is faster + // and returns false for not defined properties. + if (!callFunction(std_Object_hasOwnProperty, excluded, key) && callFunction(std_Object_propertyIsEnumerable, source, key)) + _DefineDataProperty(target, key, source[key]); + } + + // Step 6 (Return). +} + +// Object Rest/Spread Properties proposal +// Abstract operation: CopyDataProperties (target, source, excluded) +function CopyDataPropertiesUnfiltered(target, source) { + // Step 1. + assert(IsObject(target), "target is an object"); + + // Step 2 (Not applicable). + + // Steps 3, 6. + if (source === undefined || source === null) + return; + + // Step 4.a. + source = ToObject(source); + + // Step 4.b. + var keys = OwnPropertyKeys(source, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS); + + // Step 5. + for (var index = 0; index < keys.length; index++) { + var key = keys[index]; + + // We abbreviate this by calling propertyIsEnumerable which is faster + // and returns false for not defined properties. + if (callFunction(std_Object_propertyIsEnumerable, source, key)) + _DefineDataProperty(target, key, source[key]); + } + + // Step 6 (Return). +} + /*************************************** Testing functions ***************************************/ function outer() { return function inner() { diff --git a/js/src/builtin/WeakMapObject.cpp b/js/src/builtin/WeakMapObject.cpp index 555b9e03a7..dcfa19776c 100644 --- a/js/src/builtin/WeakMapObject.cpp +++ b/js/src/builtin/WeakMapObject.cpp @@ -350,14 +350,14 @@ InitWeakMapClass(JSContext* cx, HandleObject obj, bool defineMembers) { MOZ_ASSERT(obj->isNative()); - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx)); if (!proto) return nullptr; - RootedFunction ctor(cx, global->createConstructor(cx, WeakMap_construct, - cx->names().WeakMap, 0)); + RootedFunction ctor(cx, GlobalObject::createConstructor(cx, WeakMap_construct, + cx->names().WeakMap, 0)); if (!ctor) return nullptr; diff --git a/js/src/builtin/WeakSetObject.cpp b/js/src/builtin/WeakSetObject.cpp index 7ea3f2fef1..fbe5e418c5 100644 --- a/js/src/builtin/WeakSetObject.cpp +++ b/js/src/builtin/WeakSetObject.cpp @@ -41,14 +41,15 @@ const JSFunctionSpec WeakSetObject::methods[] = { }; JSObject* -WeakSetObject::initClass(JSContext* cx, JSObject* obj) +WeakSetObject::initClass(JSContext* cx, HandleObject obj) { - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx)); if (!proto) return nullptr; - Rooted<JSFunction*> ctor(cx, global->createConstructor(cx, construct, ClassName(JSProto_WeakSet, cx), 0)); + Rooted<JSFunction*> ctor(cx, GlobalObject::createConstructor(cx, construct, + ClassName(JSProto_WeakSet, cx), 0)); if (!ctor || !LinkConstructorAndPrototype(cx, ctor, proto) || !DefinePropertiesAndFunctions(cx, proto, properties, methods) || diff --git a/js/src/builtin/WeakSetObject.h b/js/src/builtin/WeakSetObject.h index 0a6ff33f36..50e54c182b 100644 --- a/js/src/builtin/WeakSetObject.h +++ b/js/src/builtin/WeakSetObject.h @@ -16,7 +16,7 @@ class WeakSetObject : public NativeObject public: static const unsigned RESERVED_SLOTS = 1; - static JSObject* initClass(JSContext* cx, JSObject* obj); + static JSObject* initClass(JSContext* cx, HandleObject obj); static const Class class_; private: diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 0facd0009e..aed1114bdb 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -20,7 +20,7 @@ #include <float.h> #endif -#if defined(SOLARIS) +#if defined(XP_SOLARIS) #include <ieeefp.h> #endif diff --git a/js/src/ctypes/libffi/src/x86/win32.S b/js/src/ctypes/libffi/src/x86/win32.S index daf0e799ca..4f702e8b1b 100644 --- a/js/src/ctypes/libffi/src/x86/win32.S +++ b/js/src/ctypes/libffi/src/x86/win32.S @@ -1158,8 +1158,24 @@ L_ffi_closure_SYSV_inner$stub: .byte 0x7c /* .sleb128 -4; CIE Data Alignment Factor */ .byte 0x8 /* CIE RA Column */ #ifdef __PIC__ - .byte 0x1 /* .uleb128 0x1; Augmentation size */ - .byte 0x1b /* FDE Encoding (pcrel sdata4) */ +# if defined __sun__ && defined __svr4__ +/* 32-bit Solaris 2/x86 uses datarel encoding for PIC. GNU ld before 2.22 + doesn't correctly sort .eh_frame_hdr with mixed encodings, so match this. */ +# define FDE_ENCODING 0x30 /* datarel */ +# define FDE_ENCODE(X) X@GOTOFF +# else +# define FDE_ENCODING 0x1b /* pcrel sdata4 */ +# if defined HAVE_AS_X86_PCREL +# define FDE_ENCODE(X) X-. +# else +# define FDE_ENCODE(X) X@rel +# endif +# endif +#else +# define FDE_ENCODING 0 /* absolute */ +# define FDE_ENCODE(X) X +.byte 0x1 /* .uleb128 0x1; Augmentation size */ +.byte FDE_ENCODING #endif .byte 0xc /* DW_CFA_def_cfa CFA = r4 + 4 = 4(%esp) */ .byte 0x4 /* .uleb128 0x4 */ @@ -1176,7 +1192,7 @@ L_ffi_closure_SYSV_inner$stub: #if defined __PIC__ && defined HAVE_AS_X86_PCREL .long .LFB1-. /* FDE initial location */ #else - .long .LFB1 + .long FDE_ENCODE(.LFB1) #endif .long .LFE1-.LFB1 /* FDE address range */ #ifdef __PIC__ @@ -1207,7 +1223,7 @@ L_ffi_closure_SYSV_inner$stub: #if defined __PIC__ && defined HAVE_AS_X86_PCREL .long .LFB3-. /* FDE initial location */ #else - .long .LFB3 + .long FDE_ENCODE(.LFB3) #endif .long .LFE3-.LFB3 /* FDE address range */ #ifdef __PIC__ @@ -1240,7 +1256,7 @@ L_ffi_closure_SYSV_inner$stub: #if defined __PIC__ && defined HAVE_AS_X86_PCREL .long .LFB4-. /* FDE initial location */ #else - .long .LFB4 + .long FDE_ENCODE(.LFB4) #endif .long .LFE4-.LFB4 /* FDE address range */ #ifdef __PIC__ @@ -1278,7 +1294,7 @@ L_ffi_closure_SYSV_inner$stub: #if defined __PIC__ && defined HAVE_AS_X86_PCREL .long .LFB5-. /* FDE initial location */ #else - .long .LFB5 + .long FDE_ENCODE(.LFB5) #endif .long .LFE5-.LFB5 /* FDE address range */ #ifdef __PIC__ diff --git a/js/src/ctypes/libffi/testsuite/libffi.call/float2.c b/js/src/ctypes/libffi/testsuite/libffi.call/float2.c index a0b296cf4b..dfdef0598a 100644 --- a/js/src/ctypes/libffi/testsuite/libffi.call/float2.c +++ b/js/src/ctypes/libffi/testsuite/libffi.call/float2.c @@ -32,21 +32,11 @@ int main (void) f = 3.14159; -#if 1 - /* This is ifdef'd out for now. long double support under SunOS/gcc - is pretty much non-existent. You'll get the odd bus error in library - routines like printf(). */ printf ("%Lf\n", ldblit(f)); -#endif ld = 666; ffi_call(&cif, FFI_FN(ldblit), &ld, values); -#if 1 - /* This is ifdef'd out for now. long double support under SunOS/gcc - is pretty much non-existent. You'll get the odd bus error in library - routines like printf(). */ printf ("%Lf, %Lf, %Lf, %Lf\n", ld, ldblit(f), ld - ldblit(f), LDBL_EPSILON); -#endif /* These are not always the same!! Check for a reasonable delta */ if (ld - ldblit(f) < LDBL_EPSILON) diff --git a/js/src/ds/LifoAlloc.h b/js/src/ds/LifoAlloc.h index f349cd4769..b4e9c34185 100644 --- a/js/src/ds/LifoAlloc.h +++ b/js/src/ds/LifoAlloc.h @@ -15,6 +15,8 @@ #include "mozilla/TemplateLib.h" #include "mozilla/TypeTraits.h" +#include <new> + // This data structure supports stacky LIFO allocation (mark/release and // LifoAllocScope). It does not maintain one contiguous segment; instead, it // maintains a bunch of linked memory segments. In order to prevent malloc/free @@ -285,6 +287,20 @@ class LifoAlloc return allocImpl(n); } + template<typename T, typename... Args> + MOZ_ALWAYS_INLINE T* + allocInSize(size_t n, Args&&... args) + { + MOZ_ASSERT(n >= sizeof(T), "must request enough space to store a T"); + static_assert(alignof(T) <= detail::LIFO_ALLOC_ALIGN, + "LifoAlloc must provide enough alignment to store T"); + void* ptr = alloc(n); + if (!ptr) + return nullptr; + + return new (ptr) T(mozilla::Forward<Args>(args)...); + } + MOZ_ALWAYS_INLINE void* allocInfallible(size_t n) { AutoEnterOOMUnsafeRegion oomUnsafe; diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index 76afe80b13..a1abbfeda2 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -77,7 +77,13 @@ class MOZ_STACK_CLASS BytecodeCompiler bool canLazilyParse(); bool createParser(); bool createSourceAndParser(Maybe<uint32_t> parameterListEnd = Nothing()); + + // If toString{Start,End} are not explicitly passed, assume the script's + // offsets in the source used to parse it are the same as what should be + // used to compute its Function.prototype.toString() value. bool createScript(); + bool createScript(uint32_t toStringStart, uint32_t toStringEnd); + bool emplaceEmitter(Maybe<BytecodeEmitter>& emitter, SharedContext* sharedContext); bool handleParseFailure(const Directives& newDirectives); bool deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment); @@ -244,8 +250,15 @@ BytecodeCompiler::createSourceAndParser(Maybe<uint32_t> parameterListEnd /* = No bool BytecodeCompiler::createScript() { + return createScript(0, sourceBuffer.length()); +} + +bool +BytecodeCompiler::createScript(uint32_t toStringStart, uint32_t toStringEnd) +{ script = JSScript::Create(cx, options, - sourceObject, /* sourceStart = */ 0, sourceBuffer.length()); + sourceObject, /* sourceStart = */ 0, sourceBuffer.length(), + toStringStart, toStringEnd); return script != nullptr; } @@ -286,7 +299,8 @@ BytecodeCompiler::deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObj RootedObject env(cx, environment); while (env->is<EnvironmentObject>() || env->is<DebugEnvironmentProxy>()) { if (env->is<CallObject>()) { - RootedScript script(cx, env->as<CallObject>().callee().getOrCreateScript(cx)); + RootedFunction fun(cx, &env->as<CallObject>().callee()); + RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun)); if (!script) return false; if (script->argumentsHasVarBinding()) { @@ -456,7 +470,7 @@ BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun, if (fn->pn_funbox->function()->isInterpreted()) { MOZ_ASSERT(fun == fn->pn_funbox->function()); - if (!createScript()) + if (!createScript(fn->pn_funbox->toStringStart, fn->pn_funbox->toStringEnd)) return false; Maybe<BytecodeEmitter> emitter; @@ -650,7 +664,8 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha MOZ_ASSERT(sourceObject); Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject, - lazy->begin(), lazy->end())); + lazy->begin(), lazy->end(), + lazy->toStringStart(), lazy->toStringEnd())); if (!script) return false; diff --git a/js/src/frontend/BytecodeCompiler.h b/js/src/frontend/BytecodeCompiler.h index 72e9676395..0bc1ab2abf 100644 --- a/js/src/frontend/BytecodeCompiler.h +++ b/js/src/frontend/BytecodeCompiler.h @@ -109,6 +109,8 @@ IsIdentifier(JSLinearString* str); * As above, but taking chars + length. */ bool +IsIdentifier(const char* chars, size_t length); +bool IsIdentifier(const char16_t* chars, size_t length); /* True if str is a keyword. Defined in TokenStream.cpp. */ diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index c7c615ccfe..309d6c290d 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -319,7 +319,7 @@ ScopeKindIsInBody(ScopeKind kind) static inline void MarkAllBindingsClosedOver(LexicalScope::Data& data) { - BindingName* names = data.names; + TrailingNamesArray& names = data.trailingNames; for (uint32_t i = 0; i < data.length; i++) names[i] = BindingName(names[i].name(), true); } @@ -2260,12 +2260,14 @@ BytecodeEmitter::locationOfNameBoundInFunctionScope(JSAtom* name, EmitterScope* bool BytecodeEmitter::emitCheck(ptrdiff_t delta, ptrdiff_t* offset) { - *offset = code().length(); + size_t oldLength = code().length(); + *offset = ptrdiff_t(oldLength); - // Start it off moderately large to avoid repeated resizings early on. - // ~98% of cases fit within 1024 bytes. - if (code().capacity() == 0 && !code().reserve(1024)) - return false; + size_t newLength = oldLength + size_t(delta); + if (MOZ_UNLIKELY(newLength > MaxBytecodeLength)) { + ReportAllocationOverflow(cx); + return false; + } if (!code().growBy(delta)) { ReportOutOfMemory(cx); @@ -3067,6 +3069,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case PNK_TRUE: case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: case PNK_ELISION: case PNK_GENERATOR: case PNK_NUMBER: @@ -3536,9 +3539,10 @@ BytecodeEmitter::needsImplicitThis() bool BytecodeEmitter::maybeSetDisplayURL() { - if (tokenStream()->hasDisplayURL()) { - if (!parser->ss->setDisplayURL(cx, tokenStream()->displayURL())) + if (tokenStream().hasDisplayURL()) { + if (!parser->ss->setDisplayURL(cx, tokenStream().displayURL())) { return false; + } } return true; } @@ -3546,10 +3550,11 @@ BytecodeEmitter::maybeSetDisplayURL() bool BytecodeEmitter::maybeSetSourceMap() { - if (tokenStream()->hasSourceMapURL()) { + if (tokenStream().hasSourceMapURL()) { MOZ_ASSERT(!parser->ss->hasSourceMapURL()); - if (!parser->ss->setSourceMapURL(cx, tokenStream()->sourceMapURL())) + if (!parser->ss->setSourceMapURL(cx, tokenStream().sourceMapURL())) { return false; + } } /* @@ -3559,9 +3564,11 @@ BytecodeEmitter::maybeSetSourceMap() if (parser->options().sourceMapURL()) { // Warn about the replacement, but use the new one. if (parser->ss->hasSourceMapURL()) { - if(!parser->report(ParseWarning, false, nullptr, JSMSG_ALREADY_HAS_PRAGMA, - parser->ss->filename(), "//# sourceMappingURL")) + if (!parser->reportNoOffset(ParseWarning, false, JSMSG_ALREADY_HAS_PRAGMA, + parser->ss->filename(), "//# sourceMappingURL")) + { return false; + } } if (!parser->ss->setSourceMapURL(cx, parser->options().sourceMapURL())) @@ -3586,33 +3593,34 @@ BytecodeEmitter::tellDebuggerAboutCompiledScript(ExclusiveContext* cx) } } -inline TokenStream* +inline TokenStream& BytecodeEmitter::tokenStream() { - return &parser->tokenStream; + return parser->tokenStream; } bool BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...) { - TokenPos pos = pn ? pn->pn_pos : tokenStream()->currentToken().pos; + TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos; va_list args; va_start(args, errorNumber); - bool result = tokenStream()->reportCompileErrorNumberVA(pos.begin, JSREPORT_ERROR, - errorNumber, args); + bool result = tokenStream().reportCompileErrorNumberVA(nullptr, pos.begin, JSREPORT_ERROR, + errorNumber, args); va_end(args); return result; } bool -BytecodeEmitter::reportStrictWarning(ParseNode* pn, unsigned errorNumber, ...) +BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...) { - TokenPos pos = pn ? pn->pn_pos : tokenStream()->currentToken().pos; + TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos; va_list args; va_start(args, errorNumber); - bool result = tokenStream()->reportStrictWarningErrorNumberVA(pos.begin, errorNumber, args); + bool result = tokenStream().reportExtraWarningErrorNumberVA(nullptr, pos.begin, + errorNumber, args); va_end(args); return result; } @@ -3620,12 +3628,12 @@ BytecodeEmitter::reportStrictWarning(ParseNode* pn, unsigned errorNumber, ...) bool BytecodeEmitter::reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...) { - TokenPos pos = pn ? pn->pn_pos : tokenStream()->currentToken().pos; + TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos; va_list args; va_start(args, errorNumber); - bool result = tokenStream()->reportStrictModeErrorNumberVA(pos.begin, sc->strict(), - errorNumber, args); + bool result = tokenStream().reportStrictModeErrorNumberVA(nullptr, pos.begin, sc->strict(), + errorNumber, args); va_end(args); return result; } @@ -4595,7 +4603,7 @@ BytecodeEmitter::emitSwitch(ParseNode* pn) // If the expression is a literal, suppress line number emission so // that debugging works more naturally. if (caseValue) { - if (!emitTree(caseValue, + if (!emitTree(caseValue, ValueUsage::WantValue, caseValue->isLiteral() ? SUPPRESS_LINENOTE : EMIT_LINENOTE)) { return false; @@ -5806,36 +5814,78 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla if (!emitRequireObjectCoercible()) // ... RHS return false; + bool needsRestPropertyExcludedSet = pattern->pn_count > 1 && + pattern->last()->isKind(PNK_SPREAD); + if (needsRestPropertyExcludedSet) { + if (!emitDestructuringObjRestExclusionSet(pattern)) // ... RHS SET + return false; + + if (!emit1(JSOP_SWAP)) // ... SET RHS + return false; + } + for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) { ParseNode* subpattern; - if (member->isKind(PNK_MUTATEPROTO)) + if (member->isKind(PNK_MUTATEPROTO) || member->isKind(PNK_SPREAD)) subpattern = member->pn_kid; else subpattern = member->pn_right; + ParseNode* lhs = subpattern; + MOZ_ASSERT_IF(member->isKind(PNK_SPREAD), !lhs->isKind(PNK_ASSIGN)); if (lhs->isKind(PNK_ASSIGN)) lhs = lhs->pn_left; size_t emitted; - if (!emitDestructuringLHSRef(lhs, &emitted)) // ... RHS *LREF + if (!emitDestructuringLHSRef(lhs, &emitted)) // ... *SET RHS *LREF return false; // Duplicate the value being destructured to use as a reference base. if (emitted) { - if (!emitDupAt(emitted)) // ... RHS *LREF RHS + if (!emitDupAt(emitted)) // ... *SET RHS *LREF RHS return false; } else { - if (!emit1(JSOP_DUP)) // ... RHS RHS + if (!emit1(JSOP_DUP)) // ... *SET RHS RHS return false; } + if (member->isKind(PNK_SPREAD)) { + if (!updateSourceCoordNotes(member->pn_pos.begin)) + return false; + + if (!emitNewInit(JSProto_Object)) // ... *SET RHS *LREF RHS TARGET + return false; + if (!emit1(JSOP_DUP)) // ... *SET RHS *LREF RHS TARGET TARGET + return false; + if (!emit2(JSOP_PICK, 2)) // ... *SET RHS *LREF TARGET TARGET RHS + return false; + + if (needsRestPropertyExcludedSet) { + if (!emit2(JSOP_PICK, emitted + 4)) // ... RHS *LREF TARGET TARGET RHS SET + return false; + } + + CopyOption option = needsRestPropertyExcludedSet + ? CopyOption::Filtered + : CopyOption::Unfiltered; + if (!emitCopyDataProperties(option)) // ... RHS *LREF TARGET + return false; + + // Destructure TARGET per this member's lhs. + if (!emitSetOrInitializeDestructuring(lhs, flav)) // ... RHS + return false; + + MOZ_ASSERT(member == pattern->last(), "Rest property is always last"); + break; + } + // Now push the property name currently being matched, which is the // current property name "label" on the left of a colon in the object // initialiser. bool needsGetElem = true; if (member->isKind(PNK_MUTATEPROTO)) { - if (!emitAtomOp(cx->names().proto, JSOP_GETPROP)) // ... RHS *LREF PROP + if (!emitAtomOp(cx->names().proto, JSOP_GETPROP)) // ... *SET RHS *LREF PROP return false; needsGetElem = false; } else { @@ -5843,40 +5893,131 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla ParseNode* key = member->pn_left; if (key->isKind(PNK_NUMBER)) { - if (!emitNumberOp(key->pn_dval)) // ... RHS *LREF RHS KEY + if (!emitNumberOp(key->pn_dval)) // ... *SET RHS *LREF RHS KEY return false; } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) { - PropertyName* name = key->pn_atom->asPropertyName(); - - // The parser already checked for atoms representing indexes and - // used PNK_NUMBER instead, but also watch for ids which TI treats - // as indexes for simplification of downstream analysis. - jsid id = NameToId(name); - if (id != IdToTypeId(id)) { - if (!emitTree(key)) // ... RHS *LREF RHS KEY + if (!emitAtomOp(key->pn_atom, JSOP_GETPROP)) // ... *SET RHS *LREF PROP + return false; + needsGetElem = false; + } else { + if (!emitComputedPropertyName(key)) // ... *SET RHS *LREF RHS KEY + return false; + + // Add the computed property key to the exclusion set. + if (needsRestPropertyExcludedSet) { + if (!emitDupAt(emitted + 3)) // ... SET RHS *LREF RHS KEY SET return false; - } else { - if (!emitAtomOp(name, JSOP_GETPROP)) // ... RHS *LREF PROP + if (!emitDupAt(1)) // ... SET RHS *LREF RHS KEY SET KEY + return false; + if (!emit1(JSOP_UNDEFINED)) // ... SET RHS *LREF RHS KEY SET KEY UNDEFINED + return false; + if (!emit1(JSOP_INITELEM)) // ... SET RHS *LREF RHS KEY SET + return false; + if (!emit1(JSOP_POP)) // ... SET RHS *LREF RHS KEY return false; - needsGetElem = false; } - } else { - if (!emitComputedPropertyName(key)) // ... RHS *LREF RHS KEY - return false; } } // Get the property value if not done already. - if (needsGetElem && !emitElemOpBase(JSOP_GETELEM)) // ... RHS *LREF PROP + if (needsGetElem && !emitElemOpBase(JSOP_GETELEM)) // ... *SET RHS *LREF PROP return false; if (subpattern->isKind(PNK_ASSIGN)) { - if (!emitDefault(subpattern->pn_right, lhs)) // ... RHS *LREF VALUE + if (!emitDefault(subpattern->pn_right, lhs)) // ... *SET RHS *LREF VALUE return false; } // Destructure PROP per this member's lhs. - if (!emitSetOrInitializeDestructuring(subpattern, flav)) // ... RHS + if (!emitSetOrInitializeDestructuring(subpattern, flav)) // ... *SET RHS + return false; + } + + return true; +} + +bool +BytecodeEmitter::emitDestructuringObjRestExclusionSet(ParseNode* pattern) +{ + MOZ_ASSERT(pattern->isKind(PNK_OBJECT)); + MOZ_ASSERT(pattern->isArity(PN_LIST)); + MOZ_ASSERT(pattern->last()->isKind(PNK_SPREAD)); + + ptrdiff_t offset = this->offset(); + if (!emitNewInit(JSProto_Object)) + return false; + + // Try to construct the shape of the object as we go, so we can emit a + // JSOP_NEWOBJECT with the final shape instead. + // In the case of computed property names and indices, we cannot fix the + // shape at bytecode compile time. When the shape cannot be determined, + // |obj| is nulled out. + + // No need to do any guessing for the object kind, since we know the upper + // bound of how many properties we plan to have. + gc::AllocKind kind = gc::GetGCObjectKind(pattern->pn_count - 1); + RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject)); + if (!obj) + return false; + + RootedAtom pnatom(cx); + for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) { + if (member->isKind(PNK_SPREAD)) + break; + + bool isIndex = false; + if (member->isKind(PNK_MUTATEPROTO)) { + pnatom.set(cx->names().proto); + } else { + ParseNode* key = member->pn_left; + if (key->isKind(PNK_NUMBER)) { + if (!emitNumberOp(key->pn_dval)) + return false; + isIndex = true; + } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) { + pnatom.set(key->pn_atom); + } else { + // Otherwise this is a computed property name which needs to + // be added dynamically. + obj.set(nullptr); + continue; + } + } + + // Initialize elements with |undefined|. + if (!emit1(JSOP_UNDEFINED)) + return false; + + if (isIndex) { + obj.set(nullptr); + if (!emit1(JSOP_INITELEM)) + return false; + } else { + uint32_t index; + if (!makeAtomIndex(pnatom, &index)) + return false; + + if (obj) { + MOZ_ASSERT(!obj->inDictionaryMode()); + Rooted<jsid> id(cx, AtomToId(pnatom)); + if (!NativeDefineProperty(cx, obj, id, UndefinedHandleValue, nullptr, nullptr, + JSPROP_ENUMERATE)) + { + return false; + } + if (obj->inDictionaryMode()) + obj.set(nullptr); + } + + if (!emitIndex32(JSOP_INITPROP, index)) + return false; + } + } + + if (obj) { + // The object survived and has a predictable shape: update the + // original bytecode. + if (!replaceNewInitWithNewObject(obj, offset)) return false; } @@ -6238,6 +6379,9 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje case PNK_NULL: vp.setNull(); return true; + case PNK_RAW_UNDEFINED: + vp.setUndefined(); + return true; case PNK_CALLSITEOBJ: case PNK_ARRAY: { unsigned count; @@ -6277,8 +6421,8 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje } MOZ_ASSERT(idx == count); - JSObject* obj = ObjectGroup::newArrayObject(cx, values.begin(), values.length(), - newKind, arrayKind); + ArrayObject* obj = ObjectGroup::newArrayObject(cx, values.begin(), values.length(), + newKind, arrayKind); if (!obj) return false; @@ -6641,7 +6785,7 @@ BytecodeEmitter::emitLexicalScopeBody(ParseNode* body, EmitLineNumberNote emitLi } // Line notes were updated by emitLexicalScope. - return emitTree(body, emitLineNote); + return emitTree(body, ValueUsage::WantValue, emitLineNote); } // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See @@ -6743,9 +6887,9 @@ BytecodeEmitter::emitRequireObjectCoercible() return false; if (!emit2(JSOP_PICK, 2)) // VAL REQUIREOBJECTCOERCIBLE UNDEFINED VAL return false; - if (!emitCall(JSOP_CALL, 1)) // VAL IGNORED + if (!emitCall(JSOP_CALL_IGNORES_RV, 1))// VAL IGNORED return false; - checkTypeSet(JSOP_CALL); + checkTypeSet(JSOP_CALL_IGNORES_RV); if (!emit1(JSOP_POP)) // VAL return false; @@ -6755,6 +6899,53 @@ BytecodeEmitter::emitRequireObjectCoercible() } bool +BytecodeEmitter::emitCopyDataProperties(CopyOption option) +{ + DebugOnly<int32_t> depth = this->stackDepth; + + uint32_t argc; + if (option == CopyOption::Filtered) { + MOZ_ASSERT(depth > 2); // TARGET SOURCE SET + argc = 3; + + if (!emitAtomOp(cx->names().CopyDataProperties, + JSOP_GETINTRINSIC)) // TARGET SOURCE SET COPYDATAPROPERTIES + { + return false; + } + } else { + MOZ_ASSERT(depth > 1); // TARGET SOURCE + argc = 2; + + if (!emitAtomOp(cx->names().CopyDataPropertiesUnfiltered, + JSOP_GETINTRINSIC)) // TARGET SOURCE COPYDATAPROPERTIES + { + return false; + } + } + + if (!emit1(JSOP_UNDEFINED)) // TARGET SOURCE *SET COPYDATAPROPERTIES UNDEFINED + return false; + if (!emit2(JSOP_PICK, argc + 1)) // SOURCE *SET COPYDATAPROPERTIES UNDEFINED TARGET + return false; + if (!emit2(JSOP_PICK, argc + 1)) // *SET COPYDATAPROPERTIES UNDEFINED TARGET SOURCE + return false; + if (option == CopyOption::Filtered) { + if (!emit2(JSOP_PICK, argc + 1)) // COPYDATAPROPERTIES UNDEFINED TARGET SOURCE SET + return false; + } + if (!emitCall(JSOP_CALL_IGNORES_RV, argc)) // IGNORED + return false; + checkTypeSet(JSOP_CALL_IGNORES_RV); + + if (!emit1(JSOP_POP)) // - + return false; + + MOZ_ASSERT(depth - int(argc) == this->stackDepth); + return true; +} + +bool BytecodeEmitter::emitIterator() { // Convert iterable to iterator. @@ -7265,12 +7456,14 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterSc // scope, but we still need to emit code for the initializers.) if (!updateSourceCoordNotes(init->pn_pos.begin)) return false; - if (!emitTree(init)) - return false; - - if (!init->isForLoopDeclaration()) { + if (init->isForLoopDeclaration()) { + if (!emitTree(init)) + return false; + } else { // 'init' is an expression, not a declaration. emitTree left its // value on the stack. + if (!emitTree(init, ValueUsage::IgnoreValue)) + return false; if (!emit1(JSOP_POP)) return false; } @@ -7352,7 +7545,7 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterSc if (!updateSourceCoordNotes(update->pn_pos.begin)) return false; - if (!emitTree(update)) + if (!emitTree(update, ValueUsage::IgnoreValue)) return false; if (!emit1(JSOP_POP)) return false; @@ -7834,7 +8027,9 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) Rooted<JSObject*> sourceObject(cx, script->sourceObject()); Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject, - funbox->bufStart, funbox->bufEnd)); + funbox->bufStart, funbox->bufEnd, + funbox->toStringStart, + funbox->toStringEnd)); if (!script) return false; @@ -8677,8 +8872,9 @@ BytecodeEmitter::emitStatement(ParseNode* pn) if (useful) { JSOp op = wantval ? JSOP_SETRVAL : JSOP_POP; + ValueUsage valueUsage = wantval ? ValueUsage::WantValue : ValueUsage::IgnoreValue; MOZ_ASSERT_IF(pn2->isKind(PNK_ASSIGN), pn2->isOp(JSOP_NOP)); - if (!emitTree(pn2)) + if (!emitTree(pn2, valueUsage)) return false; if (!emit1(op)) return false; @@ -8704,13 +8900,13 @@ BytecodeEmitter::emitStatement(ParseNode* pn) } if (directive) { - if (!reportStrictWarning(pn2, JSMSG_CONTRARY_NONDIRECTIVE, directive)) + if (!reportExtraWarning(pn2, JSMSG_CONTRARY_NONDIRECTIVE, directive)) return false; } } else { current->currentLine = parser->tokenStream.srcCoords.lineNum(pn2->pn_pos.begin); current->lastColumn = 0; - if (!reportStrictWarning(pn2, JSMSG_USELESS_EXPR)) + if (!reportExtraWarning(pn2, JSMSG_USELESS_EXPR)) return false; } } @@ -8978,7 +9174,8 @@ BytecodeEmitter::isRestParameter(ParseNode* pn, bool* result) if (bindings->nonPositionalFormalStart > 0) { // |paramName| can be nullptr when the rest destructuring syntax is // used: `function f(...[]) {}`. - JSAtom* paramName = bindings->names[bindings->nonPositionalFormalStart - 1].name(); + JSAtom* paramName = + bindings->trailingNames[bindings->nonPositionalFormalStart - 1].name(); *result = paramName && name == paramName; return true; } @@ -9027,7 +9224,7 @@ BytecodeEmitter::emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitte } bool -BytecodeEmitter::emitCallOrNew(ParseNode* pn) +BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */) { bool callop = pn->isKind(PNK_CALL) || pn->isKind(PNK_TAGGED_TEMPLATE); /* @@ -9186,7 +9383,7 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn) return false; } - if (!emitArray(args, argc, JSOP_SPREADCALLARRAY)) + if (!emitArray(args, argc)) return false; if (optCodeEmitted) { @@ -9206,13 +9403,20 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn) } if (!spread) { - if (!emitCall(pn->getOp(), argc, pn)) - return false; + if (pn->getOp() == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) { + if (!emitCall(JSOP_CALL_IGNORES_RV, argc, pn)) + return false; + checkTypeSet(JSOP_CALL_IGNORES_RV); + } else { + if (!emitCall(pn->getOp(), argc, pn)) + return false; + checkTypeSet(pn->getOp()); + } } else { if (!emit1(pn->getOp())) return false; + checkTypeSet(pn->getOp()); } - checkTypeSet(pn->getOp()); if (pn->isOp(JSOP_EVAL) || pn->isOp(JSOP_STRICTEVAL) || pn->isOp(JSOP_SPREADEVAL) || @@ -9310,12 +9514,13 @@ BytecodeEmitter::emitLogical(ParseNode* pn) } bool -BytecodeEmitter::emitSequenceExpr(ParseNode* pn) +BytecodeEmitter::emitSequenceExpr(ParseNode* pn, + ValueUsage valueUsage /* = ValueUsage::WantValue */) { for (ParseNode* child = pn->pn_head; ; child = child->pn_next) { if (!updateSourceCoordNotes(child->pn_pos.begin)) return false; - if (!emitTree(child)) + if (!emitTree(child, child->pn_next ? ValueUsage::IgnoreValue : valueUsage)) return false; if (!child->pn_next) break; @@ -9378,7 +9583,8 @@ BytecodeEmitter::emitLabeledStatement(const LabeledStatement* pn) } bool -BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional) +BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional, + ValueUsage valueUsage /* = ValueUsage::WantValue */) { /* Emit the condition, then branch if false to the else part. */ if (!emitTree(&conditional.condition())) @@ -9388,13 +9594,13 @@ BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional) if (!ifThenElse.emitCond()) return false; - if (!emitTreeInBranch(&conditional.thenExpression())) + if (!emitTreeInBranch(&conditional.thenExpression(), valueUsage)) return false; if (!ifThenElse.emitElse()) return false; - if (!emitTreeInBranch(&conditional.elseExpression())) + if (!emitTreeInBranch(&conditional.elseExpression(), valueUsage)) return false; if (!ifThenElse.emitEnd()) @@ -9423,6 +9629,22 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, continue; } + if (propdef->isKind(PNK_SPREAD)) { + MOZ_ASSERT(type == ObjectLiteral); + + if (!emit1(JSOP_DUP)) + return false; + + if (!emitTree(propdef->pn_kid)) + return false; + + if (!emitCopyDataProperties(CopyOption::Unfiltered)) + return false; + + objp.set(nullptr); + continue; + } + bool extraPop = false; if (type == ClassBody && propdef->as<ClassMethod>().isStatic()) { extraPop = true; @@ -9446,16 +9668,6 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, { continue; } - - // The parser already checked for atoms representing indexes and - // used PNK_NUMBER instead, but also watch for ids which TI treats - // as indexes for simpliciation of downstream analysis. - jsid id = NameToId(key->pn_atom->asPropertyName()); - if (id != IdToTypeId(id)) { - if (!emitTree(key)) - return false; - isIndex = true; - } } else { if (!emitComputedPropertyName(key)) return false; @@ -9536,8 +9748,7 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, MOZ_ASSERT(!IsHiddenInitOp(op)); MOZ_ASSERT(!objp->inDictionaryMode()); Rooted<jsid> id(cx, AtomToId(key->pn_atom)); - RootedValue undefinedValue(cx, UndefinedValue()); - if (!NativeDefineProperty(cx, objp, id, undefinedValue, nullptr, nullptr, + if (!NativeDefineProperty(cx, objp, id, UndefinedHandleValue, nullptr, nullptr, JSPROP_ENUMERATE)) { return false; @@ -9580,15 +9791,16 @@ BytecodeEmitter::emitObject(ParseNode* pn) if (!emitNewInit(JSProto_Object)) return false; - /* - * Try to construct the shape of the object as we go, so we can emit a - * JSOP_NEWOBJECT with the final shape instead. - */ - RootedPlainObject obj(cx); - // No need to do any guessing for the object kind, since we know exactly - // how many properties we plan to have. + // Try to construct the shape of the object as we go, so we can emit a + // JSOP_NEWOBJECT with the final shape instead. + // In the case of computed property names and indices, we cannot fix the + // shape at bytecode compile time. When the shape cannot be determined, + // |obj| is nulled out. + + // No need to do any guessing for the object kind, since we know the upper + // bound of how many properties we plan to have. gc::AllocKind kind = gc::GetGCObjectKind(pn->pn_count); - obj = NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject); + RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject)); if (!obj) return false; @@ -9596,25 +9808,34 @@ BytecodeEmitter::emitObject(ParseNode* pn) return false; if (obj) { - /* - * The object survived and has a predictable shape: update the original - * bytecode. - */ - ObjectBox* objbox = parser->newObjectBox(obj); - if (!objbox) + // The object survived and has a predictable shape: update the original + // bytecode. + if (!replaceNewInitWithNewObject(obj, offset)) return false; + } - static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH, - "newinit and newobject must have equal length to edit in-place"); + return true; +} - uint32_t index = objectList.add(objbox); - jsbytecode* code = this->code(offset); - code[0] = JSOP_NEWOBJECT; - code[1] = jsbytecode(index >> 24); - code[2] = jsbytecode(index >> 16); - code[3] = jsbytecode(index >> 8); - code[4] = jsbytecode(index); - } +bool +BytecodeEmitter::replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset) +{ + ObjectBox* objbox = parser->newObjectBox(obj); + if (!objbox) + return false; + + static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH, + "newinit and newobject must have equal length to edit in-place"); + + uint32_t index = objectList.add(objbox); + jsbytecode* code = this->code(offset); + + MOZ_ASSERT(code[0] == JSOP_NEWINIT); + code[0] = JSOP_NEWOBJECT; + code[1] = jsbytecode(index >> 24); + code[2] = jsbytecode(index >> 16); + code[3] = jsbytecode(index >> 8); + code[4] = jsbytecode(index); return true; } @@ -9677,11 +9898,11 @@ BytecodeEmitter::emitArrayLiteral(ParseNode* pn) } } - return emitArray(pn->pn_head, pn->pn_count, JSOP_NEWARRAY); + return emitArray(pn->pn_head, pn->pn_count); } bool -BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op) +BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count) { /* @@ -9692,7 +9913,6 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op) * to avoid dup'ing and popping the array as each element is added, as * JSOP_SETELEM/JSOP_SETPROP would do. */ - MOZ_ASSERT(op == JSOP_NEWARRAY || op == JSOP_SPREADCALLARRAY); uint32_t nspread = 0; for (ParseNode* elt = pn; elt; elt = elt->pn_next) { @@ -9713,7 +9933,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op) // For arrays with spread, this is a very pessimistic allocation, the // minimum possible final size. - if (!emitUint32Operand(op, count - nspread)) // ARRAY + if (!emitUint32Operand(JSOP_NEWARRAY, count - nspread)) // ARRAY return false; ParseNode* pn2 = pn; @@ -10214,6 +10434,13 @@ BytecodeEmitter::emitClass(ParseNode* pn) return false; } } else { + // In the case of default class constructors, emit the start and end + // offsets in the source buffer as source notes so that when we + // actually make the constructor during execution, we can give it the + // correct toString output. + if (!newSrcNote3(SRC_CLASS_SPAN, ptrdiff_t(pn->pn_pos.begin), ptrdiff_t(pn->pn_pos.end))) + return false; + JSAtom *name = names ? names->innerBinding()->pn_atom : cx->names().empty; if (heritageExpression) { if (!emitAtomOp(name, JSOP_DERIVEDCONSTRUCTOR)) @@ -10268,7 +10495,8 @@ BytecodeEmitter::emitClass(ParseNode* pn) } bool -BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) +BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */, + EmitLineNumberNote emitLineNote /* = EMIT_LINENOTE */) { JS_CHECK_RECURSION(cx, return false); @@ -10390,7 +10618,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) break; case PNK_COMMA: - if (!emitSequenceExpr(pn)) + if (!emitSequenceExpr(pn, valueUsage)) return false; break; @@ -10412,7 +10640,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) break; case PNK_CONDITIONAL: - if (!emitConditionalExpression(pn->as<ConditionalExpression>())) + if (!emitConditionalExpression(pn->as<ConditionalExpression>(), valueUsage)) return false; break; @@ -10525,7 +10753,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) case PNK_CALL: case PNK_GENEXP: case PNK_SUPERCALL: - if (!emitCallOrNew(pn)) + if (!emitCallOrNew(pn, valueUsage)) return false; break; @@ -10631,6 +10859,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) case PNK_TRUE: case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: if (!emit1(pn->getOp())) return false; break; @@ -10682,28 +10911,31 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) } bool -BytecodeEmitter::emitTreeInBranch(ParseNode* pn) +BytecodeEmitter::emitTreeInBranch(ParseNode* pn, + ValueUsage valueUsage /* = ValueUsage::WantValue */) { // Code that may be conditionally executed always need their own TDZ // cache. TDZCheckCache tdzCache(this); - return emitTree(pn); + return emitTree(pn, valueUsage); } static bool AllocSrcNote(ExclusiveContext* cx, SrcNotesVector& notes, unsigned* index) { - // Start it off moderately large to avoid repeated resizings early on. - // ~99% of cases fit within 256 bytes. - if (notes.capacity() == 0 && !notes.reserve(256)) - return false; + size_t oldLength = notes.length(); + if (MOZ_UNLIKELY(oldLength + 1 > MaxSrcNotesLength)) { + ReportAllocationOverflow(cx); + return false; + } + if (!notes.growBy(1)) { ReportOutOfMemory(cx); return false; } - *index = notes.length() - 1; + *index = oldLength; return true; } @@ -10829,6 +11061,10 @@ BytecodeEmitter::setSrcNoteOffset(unsigned index, unsigned which, ptrdiff_t offs /* Maybe this offset was already set to a four-byte value. */ if (!(*sn & SN_4BYTE_OFFSET_FLAG)) { /* Insert three dummy bytes that will be overwritten shortly. */ + if (MOZ_UNLIKELY(notes.length() + 3 > MaxSrcNotesLength)) {
+ ReportAllocationOverflow(cx);
+ return false; + } jssrcnote dummy = 0; if (!(sn = notes.insert(sn, dummy)) || !(sn = notes.insert(sn, dummy)) || diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 04307c8c19..595ee64055 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -109,10 +109,12 @@ struct CGYieldOffsetList { void finish(YieldOffsetArray& array, uint32_t prologueLength); }; -// Use zero inline elements because these go on the stack and affect how many -// nested functions are possible. -typedef Vector<jsbytecode, 0> BytecodeVector; -typedef Vector<jssrcnote, 0> SrcNotesVector; +static size_t MaxBytecodeLength = INT32_MAX; +static size_t MaxSrcNotesLength = INT32_MAX; + +// Have a few inline elements to avoid heap allocation for tiny sequences. +typedef Vector<jsbytecode, 256> BytecodeVector; +typedef Vector<jssrcnote, 64> SrcNotesVector; // Linked list of jump instructions that need to be patched. The linked list is // stored in the bytes of the incomplete bytecode that will be patched, so no @@ -169,6 +171,11 @@ struct JumpList { void patchAll(jsbytecode* code, JumpTarget target); }; +enum class ValueUsage { + WantValue, + IgnoreValue +}; + struct MOZ_STACK_CLASS BytecodeEmitter { class TDZCheckCache; @@ -354,7 +361,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool maybeSetSourceMap(); void tellDebuggerAboutCompiledScript(ExclusiveContext* cx); - inline TokenStream* tokenStream(); + inline TokenStream& tokenStream(); BytecodeVector& code() const { return current->code; } jsbytecode* code(ptrdiff_t offset) const { return current->code.begin() + offset; } @@ -388,7 +395,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter } bool reportError(ParseNode* pn, unsigned errorNumber, ...); - bool reportStrictWarning(ParseNode* pn, unsigned errorNumber, ...); + bool reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...); bool reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...); // If pn contains a useful expression, return true with *answer set to true. @@ -432,10 +439,12 @@ struct MOZ_STACK_CLASS BytecodeEmitter }; // Emit code for the tree rooted at pn. - MOZ_MUST_USE bool emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote = EMIT_LINENOTE); + MOZ_MUST_USE bool emitTree(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue, + EmitLineNumberNote emitLineNote = EMIT_LINENOTE); // Emit code for the tree rooted at pn with its own TDZ cache. - MOZ_MUST_USE bool emitTreeInBranch(ParseNode* pn); + MOZ_MUST_USE bool emitTreeInBranch(ParseNode* pn, + ValueUsage valueUsage = ValueUsage::WantValue); // Emit global, eval, or module code for tree rooted at body. Always // encompasses the entire source. @@ -519,7 +528,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitAtomOp(ParseNode* pn, JSOp op); MOZ_MUST_USE bool emitArrayLiteral(ParseNode* pn); - MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count, JSOp op); + MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count); MOZ_MUST_USE bool emitArrayComp(ParseNode* pn); MOZ_MUST_USE bool emitInternedScopeOp(uint32_t index, JSOp op); @@ -531,6 +540,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(ParseNode* pn, bool needsProto = false); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ParseNode* pn); + MOZ_MUST_USE bool replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset); + MOZ_MUST_USE bool emitHoistedFunctionsInList(ParseNode* pn); MOZ_MUST_USE bool emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, @@ -658,6 +669,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter // []/{} expression). MOZ_MUST_USE bool emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav); + // emitDestructuringObjRestExclusionSet emits the property exclusion set + // for the rest-property in an object pattern. + MOZ_MUST_USE bool emitDestructuringObjRestExclusionSet(ParseNode* pattern); + // emitDestructuringOps assumes the to-be-destructured value has been // pushed on the stack and emits code to destructure each part of a [] or // {} lhs expression. @@ -675,6 +690,15 @@ struct MOZ_STACK_CLASS BytecodeEmitter // object, with no overall effect on the stack. MOZ_MUST_USE bool emitRequireObjectCoercible(); + enum class CopyOption { + Filtered, Unfiltered + }; + + // Calls either the |CopyDataProperties| or the + // |CopyDataPropertiesUnfiltered| intrinsic function, consumes three (or + // two in the latter case) elements from the stack. + MOZ_MUST_USE bool emitCopyDataProperties(CopyOption option); + // emitIterator expects the iterable to already be on the stack. // It will replace that stack value with the corresponding iterator MOZ_MUST_USE bool emitIterator(); @@ -722,16 +746,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitRightAssociative(ParseNode* pn); MOZ_MUST_USE bool emitLeftAssociative(ParseNode* pn); MOZ_MUST_USE bool emitLogical(ParseNode* pn); - MOZ_MUST_USE bool emitSequenceExpr(ParseNode* pn); + MOZ_MUST_USE bool emitSequenceExpr(ParseNode* pn, + ValueUsage valueUsage = ValueUsage::WantValue); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitIncOrDec(ParseNode* pn); - MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional); + MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional, + ValueUsage valueUsage = ValueUsage::WantValue); MOZ_MUST_USE bool isRestParameter(ParseNode* pn, bool* result); MOZ_MUST_USE bool emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitted); - MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn); + MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue); MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn); MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn); MOZ_MUST_USE bool emitSelfHostedForceInterpreter(ParseNode* pn); diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 6f62ffac68..689fa02b4b 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -378,6 +378,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) case PNK_TRUE: case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: case PNK_THIS: case PNK_ELISION: case PNK_NUMBER: @@ -468,6 +469,7 @@ IsEffectless(ParseNode* node) node->isKind(PNK_TEMPLATE_STRING) || node->isKind(PNK_NUMBER) || node->isKind(PNK_NULL) || + node->isKind(PNK_RAW_UNDEFINED) || node->isKind(PNK_FUNCTION) || node->isKind(PNK_GENEXP); } @@ -492,6 +494,7 @@ Boolish(ParseNode* pn) case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: return Falsy; case PNK_VOID: { @@ -1342,15 +1345,8 @@ FoldElement(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& if (!name) return true; - // Also don't optimize if the name doesn't map directly to its id for TI's - // purposes. - if (NameToId(name) != IdToTypeId(NameToId(name))) - return true; - // Optimization 3: We have expr["foo"] where foo is not an index. Convert // to a property access (like expr.foo) that optimizes better downstream. - // Don't bother with this for names that TI considers to be indexes, to - // simplify downstream analysis. ParseNode* dottedAccess = parser.handler.newPropertyAccess(expr, name, node->pn_pos.end); if (!dottedAccess) return false; @@ -1643,6 +1639,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo case PNK_TRUE: case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: case PNK_ELISION: case PNK_NUMBER: case PNK_DEBUGGER: diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 0fd1377962..2d7f57e1ea 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -10,6 +10,8 @@ #include "mozilla/Attributes.h" #include "mozilla/PodOperations.h" +#include <string.h> + #include "frontend/ParseNode.h" #include "frontend/SharedContext.h" @@ -181,6 +183,10 @@ class FullParseHandler return new_<NullLiteral>(pos); } + ParseNode* newRawUndefinedLiteral(const TokenPos& pos) { + return new_<RawUndefinedLiteral>(pos); + } + // The Boxer object here is any object that can allocate ObjectBoxes. // Specifically, a Boxer has a .newObjectBox(T) method that accepts a // Rooted<RegExpObject*> argument and returns an ObjectBox*. @@ -296,10 +302,9 @@ class FullParseHandler } MOZ_MUST_USE bool addSpreadElement(ParseNode* literal, uint32_t begin, ParseNode* inner) { - TokenPos pos(begin, inner->pn_pos.end); - ParseNode* spread = new_<UnaryNode>(PNK_SPREAD, JSOP_NOP, pos, inner); + ParseNode* spread = newSpread(begin, inner); if (!spread) - return null(); + return false; literal->append(spread); literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST; return true; @@ -327,8 +332,10 @@ class FullParseHandler return literal; } - ParseNode* newClass(ParseNode* name, ParseNode* heritage, ParseNode* methodBlock) { - return new_<ClassNode>(name, heritage, methodBlock); + ParseNode* newClass(ParseNode* name, ParseNode* heritage, ParseNode* methodBlock, + const TokenPos& pos) + { + return new_<ClassNode>(name, heritage, methodBlock, pos); } ParseNode* newClassMethodList(uint32_t begin) { return new_<ListNode>(PNK_CLASSMETHODLIST, TokenPos(begin, begin + 1)); @@ -388,6 +395,18 @@ class FullParseHandler return true; } + MOZ_MUST_USE bool addSpreadProperty(ParseNode* literal, uint32_t begin, ParseNode* inner) { + MOZ_ASSERT(literal->isKind(PNK_OBJECT)); + MOZ_ASSERT(literal->isArity(PN_LIST)); + + setListFlag(literal, PNX_NONCONST); + ParseNode* spread = newSpread(begin, inner); + if (!spread) + return false; + literal->append(spread); + return true; + } + MOZ_MUST_USE bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn, JSOp op) { @@ -670,9 +689,18 @@ class FullParseHandler pn->setDirectRHSAnonFunction(true); } - ParseNode* newFunctionDefinition() { - return new_<CodeNode>(PNK_FUNCTION, pos()); + ParseNode* newFunctionStatement() { + return new_<CodeNode>(PNK_FUNCTION, JSOP_NOP, pos()); + } + + ParseNode* newFunctionExpression() { + return new_<CodeNode>(PNK_FUNCTION, JSOP_LAMBDA, pos()); } + + ParseNode* newArrowFunction() { + return new_<CodeNode>(PNK_FUNCTION, JSOP_LAMBDA_ARROW, pos()); + } + bool setComprehensionLambdaBody(ParseNode* pn, ParseNode* body) { MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST)); ParseNode* paramsBody = newList(PNK_PARAMSBODY, body); @@ -699,7 +727,7 @@ class FullParseHandler } ParseNode* newModule() { - return new_<CodeNode>(PNK_MODULE, pos()); + return new_<CodeNode>(PNK_MODULE, JSOP_NOP, pos()); } ParseNode* newLexicalScope(LexicalScope::Data* bindings, ParseNode* body) { @@ -845,7 +873,7 @@ class FullParseHandler MOZ_MUST_USE ParseNode* setLikelyIIFE(ParseNode* pn) { return parenthesize(pn); } - void setPrologue(ParseNode* pn) { + void setInDirectivePrologue(ParseNode* pn) { pn->pn_prologue = true; } @@ -861,29 +889,29 @@ class FullParseHandler return node->isKind(PNK_NAME); } - bool nameIsEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) { - MOZ_ASSERT(isNameAnyParentheses(node), - "must only call this function on known names"); + bool isArgumentsAnyParentheses(ParseNode* node, ExclusiveContext* cx) { + return node->isKind(PNK_NAME) && node->pn_atom == cx->names().arguments; + } - return node->pn_atom == cx->names().eval; + bool isEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) { + return node->isKind(PNK_NAME) && node->pn_atom == cx->names().eval; } const char* nameIsArgumentsEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) { MOZ_ASSERT(isNameAnyParentheses(node), "must only call this function on known names"); - if (nameIsEvalAnyParentheses(node, cx)) + if (isEvalAnyParentheses(node, cx)) return js_eval_str; - if (node->pn_atom == cx->names().arguments) + if (isArgumentsAnyParentheses(node, cx)) return js_arguments_str; return nullptr; } - bool nameIsUnparenthesizedAsync(ParseNode* node, ExclusiveContext* cx) { - MOZ_ASSERT(isNameAnyParentheses(node), - "must only call this function on known names"); - - return node->pn_atom == cx->names().async; + bool isAsyncKeyword(ParseNode* node, ExclusiveContext* cx) { + return node->isKind(PNK_NAME) && + node->pn_pos.begin + strlen("async") == node->pn_pos.end && + node->pn_atom == cx->names().async; } bool isCall(ParseNode* pn) { diff --git a/js/src/frontend/GenerateReservedWords.py b/js/src/frontend/GenerateReservedWords.py new file mode 100644 index 0000000000..bd698cc5f0 --- /dev/null +++ b/js/src/frontend/GenerateReservedWords.py @@ -0,0 +1,213 @@ +# 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/. + +import re +import sys + +def read_reserved_word_list(filename): + macro_pat = re.compile(r"^\s*macro\(([^,]+), *[^,]+, *[^\)]+\)\s*\\?$") + + reserved_word_list = [] + index = 0 + with open(filename, 'r') as f: + for line in f: + m = macro_pat.search(line) + if m: + reserved_word_list.append((index, m.group(1))) + index += 1 + + assert(len(reserved_word_list) != 0) + + return reserved_word_list + +def line(opt, s): + opt['output'].write('{}{}\n'.format(' ' * opt['indent_level'], s)) + +def indent(opt): + opt['indent_level'] += 1 + +def dedent(opt): + opt['indent_level'] -= 1 + +def span_and_count_at(reserved_word_list, column): + assert(len(reserved_word_list) != 0); + + chars_dict = {} + for index, word in reserved_word_list: + chars_dict[ord(word[column])] = True + + chars = sorted(chars_dict.keys()) + return chars[-1] - chars[0] + 1, len(chars) + +def optimal_switch_column(opt, reserved_word_list, columns, unprocessed_columns): + assert(len(reserved_word_list) != 0); + assert(unprocessed_columns != 0); + + min_count = 0 + min_span = 0 + min_count_index = 0 + min_span_index = 0 + + for index in range(0, unprocessed_columns): + span, count = span_and_count_at(reserved_word_list, columns[index]) + assert(span != 0) + + if span == 1: + assert(count == 1) + return 1, True + + assert(count != 1) + if index == 0 or min_span > span: + min_span = span + min_span_index = index + + if index == 0 or min_count > count: + min_count = count + min_count_index = index + + if min_count <= opt['use_if_threshold']: + return min_count_index, True + + return min_span_index, False + +def split_list_per_column(reserved_word_list, column): + assert(len(reserved_word_list) != 0); + + column_dict = {} + for item in reserved_word_list: + index, word = item + per_column = column_dict.setdefault(word[column], []) + per_column.append(item) + + return sorted(column_dict.items(), key=lambda (char, word): ord(char)) + +def generate_letter_switch(opt, unprocessed_columns, reserved_word_list, + columns=None): + assert(len(reserved_word_list) != 0); + + if not columns: + columns = range(0, unprocessed_columns) + + if len(reserved_word_list) == 1: + index, word = reserved_word_list[0] + + if unprocessed_columns == 0: + line(opt, 'JSRW_GOT_MATCH({}) /* {} */'.format(index, word)) + return + + if unprocessed_columns > opt['char_tail_test_threshold']: + line(opt, 'JSRW_TEST_GUESS({}) /* {} */'.format(index, word)) + return + + conds = [] + for column in columns[0:unprocessed_columns]: + quoted = repr(word[column]) + conds.append('JSRW_AT({})=={}'.format(column, quoted)) + + line(opt, 'if ({}) {{'.format(' && '.join(conds))) + + indent(opt) + line(opt, 'JSRW_GOT_MATCH({}) /* {} */'.format(index, word)) + dedent(opt) + + line(opt, '}') + line(opt, 'JSRW_NO_MATCH()') + return + + assert(unprocessed_columns != 0); + + optimal_column_index, use_if = optimal_switch_column(opt, reserved_word_list, + columns, + unprocessed_columns) + optimal_column = columns[optimal_column_index] + + # Make a copy to avoid breaking passed list. + columns = columns[:] + columns[optimal_column_index] = columns[unprocessed_columns - 1] + + list_per_column = split_list_per_column(reserved_word_list, optimal_column) + + if not use_if: + line(opt, 'switch (JSRW_AT({})) {{'.format(optimal_column)) + + for char, reserved_word_list_per_column in list_per_column: + quoted = repr(char) + if use_if: + line(opt, 'if (JSRW_AT({}) == {}) {{'.format(optimal_column, + quoted)) + else: + line(opt, ' case {}:'.format(quoted)) + + indent(opt) + generate_letter_switch(opt, unprocessed_columns - 1, + reserved_word_list_per_column, columns) + dedent(opt) + + if use_if: + line(opt, '}') + + if not use_if: + line(opt, '}') + + line(opt, 'JSRW_NO_MATCH()') + +def split_list_per_length(reserved_word_list): + assert(len(reserved_word_list) != 0); + + length_dict = {} + for item in reserved_word_list: + index, word = item + per_length = length_dict.setdefault(len(word), []) + per_length.append(item) + + return sorted(length_dict.items(), key=lambda (length, word): length) + +def generate_switch(opt, reserved_word_list): + assert(len(reserved_word_list) != 0); + + line(opt, '/*') + line(opt, ' * Generating switch for the list of {} entries:'.format(len(reserved_word_list))) + for index, word in reserved_word_list: + line(opt, ' * {}'.format(word)) + line(opt, ' */') + + list_per_length = split_list_per_length(reserved_word_list) + + use_if = False + if len(list_per_length) < opt['use_if_threshold']: + use_if = True + + if not use_if: + line(opt, 'switch (JSRW_LENGTH()) {') + + for length, reserved_word_list_per_length in list_per_length: + if use_if: + line(opt, 'if (JSRW_LENGTH() == {}) {{'.format(length)) + else: + line(opt, ' case {}:'.format(length)) + + indent(opt) + generate_letter_switch(opt, length, reserved_word_list_per_length) + dedent(opt) + + if use_if: + line(opt, '}') + + if not use_if: + line(opt, '}') + line(opt, 'JSRW_NO_MATCH()') + +def main(output, reserved_words_h): + reserved_word_list = read_reserved_word_list(reserved_words_h) + + opt = { + 'indent_level': 1, + 'use_if_threshold': 3, + 'char_tail_test_threshold': 4, + 'output': output + } + generate_switch(opt, reserved_word_list) + +if __name__ == '__main__': + main(sys.stdout, *sys.argv[1:]) diff --git a/js/src/frontend/NameAnalysisTypes.h b/js/src/frontend/NameAnalysisTypes.h index d39e177fba..2d327c8272 100644 --- a/js/src/frontend/NameAnalysisTypes.h +++ b/js/src/frontend/NameAnalysisTypes.h @@ -78,6 +78,7 @@ enum class DeclarationKind : uint8_t Const, Import, BodyLevelFunction, + ModuleBodyLevelFunction, LexicalFunction, VarForAnnexBLexicalFunction, SimpleCatchParameter, @@ -95,6 +96,7 @@ DeclarationKindToBindingKind(DeclarationKind kind) case DeclarationKind::Var: case DeclarationKind::BodyLevelFunction: + case DeclarationKind::ModuleBodyLevelFunction: case DeclarationKind::VarForAnnexBLexicalFunction: case DeclarationKind::ForOfVar: return BindingKind::Var; @@ -124,6 +126,7 @@ DeclarationKindIsLexical(DeclarationKind kind) // Used in Parser to track declared names. class DeclaredNameInfo { + uint32_t pos_; DeclarationKind kind_; // If the declared name is a binding, whether the binding is closed @@ -132,8 +135,9 @@ class DeclaredNameInfo bool closedOver_; public: - explicit DeclaredNameInfo(DeclarationKind kind) - : kind_(kind), + explicit DeclaredNameInfo(DeclarationKind kind, uint32_t pos) + : pos_(pos), + kind_(kind), closedOver_(false) { } @@ -144,6 +148,12 @@ class DeclaredNameInfo return kind_; } + static const uint32_t npos = uint32_t(-1); + + uint32_t pos() const { + return pos_; + } + void alterKind(DeclarationKind kind) { kind_ = kind; } diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index ce1318f0b1..dc54d0a88f 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -316,7 +316,8 @@ class NameResolver return false; // Next is the callsite object node. This node only contains - // internal strings and an array -- no user-controlled expressions. + // internal strings or undefined and an array -- no user-controlled + // expressions. element = element->pn_next; #ifdef DEBUG { @@ -326,7 +327,7 @@ class NameResolver for (ParseNode* kid = array->pn_head; kid; kid = kid->pn_next) MOZ_ASSERT(kid->isKind(PNK_TEMPLATE_STRING)); for (ParseNode* next = array->pn_next; next; next = next->pn_next) - MOZ_ASSERT(next->isKind(PNK_TEMPLATE_STRING)); + MOZ_ASSERT(next->isKind(PNK_TEMPLATE_STRING) || next->isKind(PNK_RAW_UNDEFINED)); } #endif @@ -382,6 +383,7 @@ class NameResolver case PNK_TRUE: case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: case PNK_ELISION: case PNK_GENERATOR: case PNK_NUMBER: diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index ece3a45dfb..5fe64e3d36 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -190,6 +190,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_TRUE: case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: case PNK_ELISION: case PNK_GENERATOR: case PNK_NUMBER: @@ -685,6 +686,7 @@ NullaryNode::dump() case PNK_TRUE: fprintf(stderr, "#true"); break; case PNK_FALSE: fprintf(stderr, "#false"); break; case PNK_NULL: fprintf(stderr, "#null"); break; + case PNK_RAW_UNDEFINED: fprintf(stderr, "#undefined"); break; case PNK_NUMBER: { ToCStringBuf cbuf; @@ -838,7 +840,7 @@ LexicalScopeNode::dump(int indent) if (!isEmptyScope()) { LexicalScope::Data* bindings = scopeBindings(); for (uint32_t i = 0; i < bindings->length; i++) { - JSAtom* name = bindings->names[i].name(); + JSAtom* name = bindings->trailingNames[i].name(); JS::AutoCheckCannotGC nogc; if (name->hasLatin1Chars()) DumpName(name->latin1Chars(nogc), name->length()); diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index c58dab431b..1f20f39887 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -54,6 +54,7 @@ class ObjectBox; F(TRUE) \ F(FALSE) \ F(NULL) \ + F(RAW_UNDEFINED) \ F(THIS) \ F(FUNCTION) \ F(MODULE) \ @@ -406,7 +407,8 @@ IsTypeofKind(ParseNodeKind kind) * PNK_NUMBER dval pn_dval: double value of numeric literal * PNK_TRUE, nullary pn_op: JSOp bytecode * PNK_FALSE, - * PNK_NULL + * PNK_NULL, + * PNK_RAW_UNDEFINED * * PNK_THIS, unary pn_kid: '.this' Name if function `this`, else nullptr * PNK_SUPERBASE unary pn_kid: '.this' Name @@ -686,7 +688,8 @@ class ParseNode isKind(PNK_STRING) || isKind(PNK_TRUE) || isKind(PNK_FALSE) || - isKind(PNK_NULL); + isKind(PNK_NULL) || + isKind(PNK_RAW_UNDEFINED); } /* Return true if this node appears in a Directive Prologue. */ @@ -926,10 +929,14 @@ struct ListNode : public ParseNode struct CodeNode : public ParseNode { - CodeNode(ParseNodeKind kind, const TokenPos& pos) - : ParseNode(kind, JSOP_NOP, PN_CODE, pos) + CodeNode(ParseNodeKind kind, JSOp op, const TokenPos& pos) + : ParseNode(kind, op, PN_CODE, pos) { MOZ_ASSERT(kind == PNK_FUNCTION || kind == PNK_MODULE); + MOZ_ASSERT_IF(kind == PNK_MODULE, op == JSOP_NOP); + MOZ_ASSERT(op == JSOP_NOP || // statement, module + op == JSOP_LAMBDA_ARROW || // arrow function + op == JSOP_LAMBDA); // expression, method, comprehension, accessor, &c. MOZ_ASSERT(!pn_body); MOZ_ASSERT(!pn_objbox); } @@ -1137,6 +1144,16 @@ class NullLiteral : public ParseNode explicit NullLiteral(const TokenPos& pos) : ParseNode(PNK_NULL, JSOP_NULL, PN_NULLARY, pos) { } }; +// This is only used internally, currently just for tagged templates. +// It represents the value 'undefined' (aka `void 0`), like NullLiteral +// represents the value 'null'. +class RawUndefinedLiteral : public ParseNode +{ + public: + explicit RawUndefinedLiteral(const TokenPos& pos) + : ParseNode(PNK_RAW_UNDEFINED, JSOP_UNDEFINED, PN_NULLARY, pos) { } +}; + class BooleanLiteral : public ParseNode { public: @@ -1292,8 +1309,9 @@ struct ClassNames : public BinaryNode { }; struct ClassNode : public TernaryNode { - ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock) - : TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodsOrBlock) + ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock, + const TokenPos& pos) + : TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodsOrBlock, pos) { MOZ_ASSERT_IF(names, names->is<ClassNames>()); MOZ_ASSERT(methodsOrBlock->is<LexicalScopeNode>() || @@ -1357,6 +1375,7 @@ ParseNode::isConstant() case PNK_STRING: case PNK_TEMPLATE_STRING: case PNK_NULL: + case PNK_RAW_UNDEFINED: case PNK_FALSE: case PNK_TRUE: return true; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 623379f61f..0c279591f9 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -19,6 +19,10 @@ #include "frontend/Parser.h" +#include "mozilla/Sprintf.h" + +#include <new> + #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" @@ -60,19 +64,33 @@ using AddDeclaredNamePtr = ParseContext::Scope::AddDeclaredNamePtr; using BindingIter = ParseContext::Scope::BindingIter; using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr; -/* Read a token. Report an error and return null() if that token isn't of type tt. */ -#define MUST_MATCH_TOKEN_MOD(tt, modifier, errno) \ +// Read a token. Report an error and return null() if that token doesn't match +// to the condition. Do not use MUST_MATCH_TOKEN_INTERNAL directly. +#define MUST_MATCH_TOKEN_INTERNAL(cond, modifier, errorReport) \ JS_BEGIN_MACRO \ TokenKind token; \ if (!tokenStream.getToken(&token, modifier)) \ return null(); \ - if (token != tt) { \ - report(ParseError, false, null(), errno); \ + if (!(cond)) { \ + errorReport; \ return null(); \ } \ JS_END_MACRO -#define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_MOD(tt, TokenStream::None, errno) +#define MUST_MATCH_TOKEN_MOD(tt, modifier, errorNumber) \ + MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, error(errorNumber)) + +#define MUST_MATCH_TOKEN(tt, errorNumber) \ + MUST_MATCH_TOKEN_MOD(tt, TokenStream::None, errorNumber) + +#define MUST_MATCH_TOKEN_FUNC_MOD(func, modifier, errorNumber) \ + MUST_MATCH_TOKEN_INTERNAL((func)(token), modifier, error(errorNumber)) + +#define MUST_MATCH_TOKEN_FUNC(func, errorNumber) \ + MUST_MATCH_TOKEN_FUNC_MOD(func, TokenStream::None, errorNumber) + +#define MUST_MATCH_TOKEN_MOD_WITH_REPORT(tt, modifier, errorReport) \ + MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, errorReport) template <class T, class U> static inline void @@ -104,6 +122,7 @@ DeclarationKindString(DeclarationKind kind) case DeclarationKind::Import: return "import"; case DeclarationKind::BodyLevelFunction: + case DeclarationKind::ModuleBodyLevelFunction: case DeclarationKind::LexicalFunction: return "function"; case DeclarationKind::VarForAnnexBLexicalFunction: @@ -125,7 +144,8 @@ StatementKindIsBraced(StatementKind kind) kind == StatementKind::Switch || kind == StatementKind::Try || kind == StatementKind::Catch || - kind == StatementKind::Finally; + kind == StatementKind::Finally || + kind == StatementKind::Class; } void @@ -187,11 +207,12 @@ ParseContext::Scope::addCatchParameters(ParseContext* pc, Scope& catchParamScope for (DeclaredNameMap::Range r = catchParamScope.declared_->all(); !r.empty(); r.popFront()) { DeclarationKind kind = r.front().value()->kind(); + uint32_t pos = r.front().value()->pos(); MOZ_ASSERT(DeclarationKindIsCatchParameter(kind)); JSAtom* name = r.front().key(); AddDeclaredNamePtr p = lookupDeclaredNameForAdd(name); MOZ_ASSERT(!p); - if (!addDeclaredName(pc, p, name, kind)) + if (!addDeclaredName(pc, p, name, kind, pos)) return false; } @@ -330,7 +351,8 @@ ParseContext::init() namedLambdaScope_->lookupDeclaredNameForAdd(fun->explicitName()); MOZ_ASSERT(!p); if (!namedLambdaScope_->addDeclaredName(this, p, fun->explicitName(), - DeclarationKind::Const)) + DeclarationKind::Const, + DeclaredNameInfo::npos)) { return false; } @@ -439,7 +461,8 @@ UsedNameTracker::rewind(RewindToken token) } FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead, - JSFunction* fun, Directives directives, bool extraWarnings, + JSFunction* fun, uint32_t toStringStart, + Directives directives, bool extraWarnings, GeneratorKind generatorKind, FunctionAsyncKind asyncKind) : ObjectBox(fun, traceListHead), SharedContext(cx, Kind::ObjectBox, directives, extraWarnings), @@ -452,6 +475,8 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* trac bufEnd(0), startLine(1), startColumn(0), + toStringStart(toStringStart), + toStringEnd(0), length(0), generatorKindBits_(GeneratorKindAsBits(generatorKind)), asyncKindBits_(AsyncKindAsBits(asyncKind)), @@ -470,6 +495,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* trac usesThis(false), usesReturn(false), hasRest_(false), + isExprBody_(false), funCxFlags() { // Functions created at parse time may be set singleton after parsing and @@ -521,10 +547,16 @@ FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, FunctionSynt allowNewTarget_ = true; allowSuperProperty_ = fun->allowSuperProperty(); - if (kind == DerivedClassConstructor) { - setDerivedClassConstructor(); - allowSuperCall_ = true; - needsThisTDZChecks_ = true; + if (kind == ClassConstructor || kind == DerivedClassConstructor) { + auto stmt = enclosing->findInnermostStatement<ParseContext::ClassStatement>(); + MOZ_ASSERT(stmt); + stmt->constructorBox = this; + + if (kind == DerivedClassConstructor) { + setDerivedClassConstructor(); + allowSuperCall_ = true; + needsThisTDZChecks_ = true; + } } if (isGenexpLambda) @@ -566,68 +598,166 @@ FunctionBox::initWithEnclosingScope(Scope* enclosingScope) computeInWith(enclosingScope); } -template <typename ParseHandler> +void +ParserBase::error(unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); +#ifdef DEBUG + bool result = +#endif + tokenStream.reportCompileErrorNumberVA(nullptr, pos().begin, JSREPORT_ERROR, + errorNumber, args); + MOZ_ASSERT(!result, "reporting an error returned true?"); + va_end(args); +} + +void +ParserBase::errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); +#ifdef DEBUG + bool result = +#endif + tokenStream.reportCompileErrorNumberVA(Move(notes), pos().begin, JSREPORT_ERROR, + errorNumber, args); + MOZ_ASSERT(!result, "reporting an error returned true?"); + va_end(args); +} + +void +ParserBase::errorAt(uint32_t offset, unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); +#ifdef DEBUG + bool result = +#endif + tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR, errorNumber, args); + MOZ_ASSERT(!result, "reporting an error returned true?"); + va_end(args); +} + +void +ParserBase::errorWithNotesAt(UniquePtr<JSErrorNotes> notes, uint32_t offset, + unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); +#ifdef DEBUG + bool result = +#endif + tokenStream.reportCompileErrorNumberVA(Move(notes), offset, JSREPORT_ERROR, + errorNumber, args); + MOZ_ASSERT(!result, "reporting an error returned true?"); + va_end(args); +} + bool -Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset, - unsigned errorNumber, va_list args) +ParserBase::warning(unsigned errorNumber, ...) { - bool result = false; - switch (kind) { - case ParseError: - result = tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args); - break; - case ParseWarning: - result = - tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args); - break; - case ParseExtraWarning: - result = tokenStream.reportStrictWarningErrorNumberVA(offset, errorNumber, args); - break; - case ParseStrictError: - result = tokenStream.reportStrictModeErrorNumberVA(offset, strict, errorNumber, args); - break; - } + va_list args; + va_start(args, errorNumber); + bool result = + tokenStream.reportCompileErrorNumberVA(nullptr, pos().begin, JSREPORT_WARNING, + errorNumber, args); + va_end(args); return result; } -template <typename ParseHandler> bool -Parser<ParseHandler>::report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...) +ParserBase::warningAt(uint32_t offset, unsigned errorNumber, ...) { - uint32_t offset = (pn ? handler.getPosition(pn) : pos()).begin; + va_list args; + va_start(args, errorNumber); + bool result = + tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_WARNING, + errorNumber, args); + va_end(args); + return result; +} +bool +ParserBase::extraWarning(unsigned errorNumber, ...) +{ va_list args; va_start(args, errorNumber); - bool result = reportHelper(kind, strict, offset, errorNumber, args); + bool result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, pos().begin, + errorNumber, args); va_end(args); return result; } -template <typename ParseHandler> bool -Parser<ParseHandler>::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...) +ParserBase::extraWarningAt(uint32_t offset, unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = reportHelper(kind, strict, TokenStream::NoOffset, errorNumber, args); + + bool result = + tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset, errorNumber, args); + va_end(args); return result; } -template <typename ParseHandler> bool -Parser<ParseHandler>::reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset, - unsigned errorNumber, ...) +ParserBase::strictModeError(unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); + bool res = + tokenStream.reportStrictModeErrorNumberVA(nullptr, pos().begin, pc->sc()->strict(), + errorNumber, args); + va_end(args); + return res; +} + +bool +ParserBase::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); + bool res = + tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, pc->sc()->strict(), + errorNumber, args); + va_end(args); + return res; +} + +bool +ParserBase::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = reportHelper(kind, strict, offset, errorNumber, args); + bool result = false; + uint32_t offset = TokenStream::NoOffset; + switch (kind) { + case ParseError: + result = tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR, + errorNumber, args); + break; + case ParseWarning: + result = + tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_WARNING, + errorNumber, args); + break; + case ParseExtraWarning: + result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset, + errorNumber, args); + break; + case ParseStrictError: + result = tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, strict, + errorNumber, args); + break; + } va_end(args); return result; } template <> -bool +inline bool Parser<FullParseHandler>::abortIfSyntaxParser() { handler.disableSyntaxParser(); @@ -635,23 +765,21 @@ Parser<FullParseHandler>::abortIfSyntaxParser() } template <> -bool +inline bool Parser<SyntaxParseHandler>::abortIfSyntaxParser() { abortedSyntaxParse = true; return false; } -template <typename ParseHandler> -Parser<ParseHandler>::Parser(ExclusiveContext* cx, LifoAlloc& alloc, - const ReadOnlyCompileOptions& options, - const char16_t* chars, size_t length, - bool foldConstants, - UsedNameTracker& usedNames, - Parser<SyntaxParseHandler>* syntaxParser, - LazyScript* lazyOuterFunction) - : AutoGCRooter(cx, PARSER), - context(cx), +ParserBase::ParserBase(ExclusiveContext* cx, LifoAlloc& alloc, + const ReadOnlyCompileOptions& options, + const char16_t* chars, size_t length, + bool foldConstants, + UsedNameTracker& usedNames, + Parser<SyntaxParseHandler>* syntaxParser, + LazyScript* lazyOuterFunction) + : context(cx), alloc(alloc), tokenStream(cx, options, chars, length, thisForCtor()), traceListHead(nullptr), @@ -666,17 +794,44 @@ Parser<ParseHandler>::Parser(ExclusiveContext* cx, LifoAlloc& alloc, #endif abortedSyntaxParse(false), isUnexpectedEOF_(false), - handler(cx, alloc, tokenStream, syntaxParser, lazyOuterFunction) + awaitIsKeyword_(false) { cx->perThreadData->frontendCollectionPool.addActiveCompilation(); + tempPoolMark = alloc.mark(); +} + +ParserBase::~ParserBase() +{ + alloc.release(tempPoolMark); + + /* + * The parser can allocate enormous amounts of memory for large functions. + * Eagerly free the memory now (which otherwise won't be freed until the + * next GC) to avoid unnecessary OOMs. + */ + alloc.freeAllIfHugeAndUnused(); + + context->perThreadData->frontendCollectionPool.removeActiveCompilation(); +} +template <typename ParseHandler> +Parser<ParseHandler>::Parser(ExclusiveContext* cx, LifoAlloc& alloc, + const ReadOnlyCompileOptions& options, + const char16_t* chars, size_t length, + bool foldConstants, + UsedNameTracker& usedNames, + Parser<SyntaxParseHandler>* syntaxParser, + LazyScript* lazyOuterFunction) + : ParserBase(cx, alloc, options, chars, length, foldConstants, usedNames, syntaxParser, + lazyOuterFunction), + AutoGCRooter(cx, PARSER), + handler(cx, alloc, tokenStream, syntaxParser, lazyOuterFunction) +{ // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings // which are not generated if functions are parsed lazily. Note that the // standard "use strict" does not inhibit lazy parsing. if (options.extraWarningsOption) handler.disableSyntaxParser(); - - tempPoolMark = alloc.mark(); } template<typename ParseHandler> @@ -687,26 +842,29 @@ Parser<ParseHandler>::checkOptions() checkOptionsCalled = true; #endif - if (!tokenStream.checkOptions()) - return false; - - return true; + return tokenStream.checkOptions(); } template <typename ParseHandler> Parser<ParseHandler>::~Parser() { MOZ_ASSERT(checkOptionsCalled); - alloc.release(tempPoolMark); +} - /* - * The parser can allocate enormous amounts of memory for large functions. - * Eagerly free the memory now (which otherwise won't be freed until the - * next GC) to avoid unnecessary OOMs. - */ - alloc.freeAllIfHugeAndUnused(); +template <> +void +Parser<SyntaxParseHandler>::setAwaitIsKeyword(bool isKeyword) +{ + awaitIsKeyword_ = isKeyword; +} - context->perThreadData->frontendCollectionPool.removeActiveCompilation(); +template <> +void +Parser<FullParseHandler>::setAwaitIsKeyword(bool isKeyword) +{ + awaitIsKeyword_ = isKeyword; + if (Parser<SyntaxParseHandler>* parser = handler.syntaxParser) + parser->setAwaitIsKeyword(isKeyword); } template <typename ParseHandler> @@ -736,7 +894,8 @@ Parser<ParseHandler>::newObjectBox(JSObject* obj) template <typename ParseHandler> FunctionBox* -Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, Directives inheritedDirectives, +Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart, + Directives inheritedDirectives, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB) { @@ -751,8 +910,9 @@ Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, Directives inheri * function. */ FunctionBox* funbox = - alloc.new_<FunctionBox>(context, alloc, traceListHead, fun, inheritedDirectives, - options().extraWarningsOption, generatorKind, asyncKind); + alloc.new_<FunctionBox>(context, alloc, traceListHead, fun, toStringStart, + inheritedDirectives, options().extraWarningsOption, + generatorKind, asyncKind); if (!funbox) { ReportOutOfMemory(context); return nullptr; @@ -820,8 +980,7 @@ Parser<ParseHandler>::parse() if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); if (tt != TOK_EOF) { - report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT, - "script", TokenKindToDesc(tt)); + error(JSMSG_GARBAGE_AFTER_INPUT, "script", TokenKindToDesc(tt)); return null(); } if (foldConstants) { @@ -832,56 +991,19 @@ Parser<ParseHandler>::parse() return pn; } -template <typename ParseHandler> -bool -Parser<ParseHandler>::reportBadReturn(Node pn, ParseReportKind kind, - unsigned errnum, unsigned anonerrnum) -{ - JSAutoByteString name; - if (JSAtom* atom = pc->functionBox()->function()->explicitName()) { - if (!AtomToPrintableString(context, atom, &name)) - return false; - } else { - errnum = anonerrnum; - } - return report(kind, pc->sc()->strict(), pn, errnum, name.ptr()); -} - /* * Strict mode forbids introducing new definitions for 'eval', 'arguments', or - * for any strict mode reserved keyword. + * for any strict mode reserved word. */ -template <typename ParseHandler> bool -Parser<ParseHandler>::isValidStrictBinding(PropertyName* name) +ParserBase::isValidStrictBinding(PropertyName* name) { return name != context->names().eval && name != context->names().arguments && name != context->names().let && name != context->names().static_ && - !(IsKeyword(name) && name != context->names().await); -} - -/* - * Check that it is permitted to introduce a binding for |name|. Use |pos| for - * reporting error locations. - */ -template <typename ParseHandler> -bool -Parser<ParseHandler>::checkStrictBinding(PropertyName* name, TokenPos pos) -{ - if (!pc->sc()->needStrictChecks()) - return true; - - if (!isValidStrictBinding(name)) { - JSAutoByteString bytes; - if (!AtomToPrintableString(context, name, &bytes)) - return false; - return reportWithOffset(ParseStrictError, pc->sc()->strict(), pos.begin, - JSMSG_BAD_BINDING, bytes.ptr()); - } - - return true; + name != context->names().yield && + !IsStrictReservedWord(name); } /* @@ -908,14 +1030,71 @@ Parser<ParseHandler>::hasValidSimpleStrictParameterNames() template <typename ParseHandler> void -Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKind kind, - TokenPos pos) +Parser<ParseHandler>::reportMissingClosing(unsigned errorNumber, unsigned noteNumber, + uint32_t openedPos) +{ + auto notes = MakeUnique<JSErrorNotes>(); + if (!notes) + return; + + uint32_t line, column; + tokenStream.srcCoords.lineNumAndColumnIndex(openedPos, &line, &column); + + const size_t MaxWidth = sizeof("4294967295"); + char columnNumber[MaxWidth]; + SprintfLiteral(columnNumber, "%" PRIu32, column); + char lineNumber[MaxWidth]; + SprintfLiteral(lineNumber, "%" PRIu32, line); + + if (!notes->addNoteASCII(pc->sc()->context, + getFilename(), line, column, + GetErrorMessage, nullptr, + noteNumber, lineNumber, columnNumber)) + { + return; + } + + errorWithNotes(Move(notes), errorNumber); +} + +template <typename ParseHandler> +void +Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, + TokenPos pos, uint32_t prevPos) { JSAutoByteString bytes; if (!AtomToPrintableString(context, name, &bytes)) return; - reportWithOffset(ParseError, false, pos.begin, JSMSG_REDECLARED_VAR, - DeclarationKindString(kind), bytes.ptr()); + + if (prevPos == DeclaredNameInfo::npos) { + errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.ptr()); + return; + } + + auto notes = MakeUnique<JSErrorNotes>(); + if (!notes) + return; + + uint32_t line, column; + tokenStream.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column); + + const size_t MaxWidth = sizeof("4294967295"); + char columnNumber[MaxWidth]; + SprintfLiteral(columnNumber, "%" PRIu32, column); + char lineNumber[MaxWidth]; + SprintfLiteral(lineNumber, "%" PRIu32, line); + + if (!notes->addNoteASCII(pc->sc()->context, + getFilename(), line, column, + GetErrorMessage, nullptr, + JSMSG_REDECLARED_PREV, + lineNumber, columnNumber)) + { + return; + } + + errorWithNotesAt(Move(notes), pos.begin, JSMSG_REDECLARED_VAR, + DeclarationKindString(prevKind), bytes.ptr()); } // notePositionalFormalParameter is called for both the arguments of a regular @@ -930,12 +1109,13 @@ Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKi template <typename ParseHandler> bool Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName name, + uint32_t beginPos, bool disallowDuplicateParams, bool* duplicatedParam) { if (AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name)) { if (disallowDuplicateParams) { - report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); + error(JSMSG_BAD_DUP_ARGS); return false; } @@ -947,17 +1127,14 @@ Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName JSAutoByteString bytes; if (!AtomToPrintableString(context, name, &bytes)) return false; - if (!report(ParseStrictError, pc->sc()->strict(), null(), - JSMSG_DUPLICATE_FORMAL, bytes.ptr())) - { + if (!strictModeError(JSMSG_DUPLICATE_FORMAL, bytes.ptr())) return false; - } } *duplicatedParam = true; } else { DeclarationKind kind = DeclarationKind::PositionalFormalParameter; - if (!pc->functionScope().addDeclaredName(pc, p, name, kind)) + if (!pc->functionScope().addDeclaredName(pc, p, name, kind, beginPos)) return false; } @@ -970,9 +1147,6 @@ Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName if (!paramNode) return false; - if (!checkStrictBinding(name, pos())) - return false; - handler.addFunctionFormalParameter(fn, paramNode); return true; } @@ -1064,8 +1238,8 @@ DeclarationKindIsParameter(DeclarationKind kind) template <typename ParseHandler> bool -Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kind, - Maybe<DeclarationKind>* redeclaredKind) +Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos, + Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos) { MOZ_ASSERT(DeclarationKindIsVar(kind)); @@ -1138,17 +1312,29 @@ Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kin if (!annexB35Allowance && !annexB33Allowance) { *redeclaredKind = Some(declaredKind); + *prevPos = p->value()->pos(); return true; } + } else if (kind == DeclarationKind::VarForAnnexBLexicalFunction) { + MOZ_ASSERT(DeclarationKindIsParameter(declaredKind)); + + // Annex B.3.3.1 disallows redeclaring parameter names. + // We don't need to set *prevPos here since this case is not + // an error. + *redeclaredKind = Some(declaredKind); + return true; } } else { - if (!scope->addDeclaredName(pc, p, name, kind)) + if (!scope->addDeclaredName(pc, p, name, kind, beginPos)) return false; } } - if (!pc->sc()->strict() && pc->sc()->isEvalContext()) + if (!pc->sc()->strict() && pc->sc()->isEvalContext()) { *redeclaredKind = isVarRedeclaredInEval(name, kind); + // We don't have position information at runtime. + *prevPos = DeclaredNameInfo::npos; + } return true; } @@ -1156,11 +1342,34 @@ Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kin template <typename ParseHandler> bool Parser<ParseHandler>::tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name, - bool* tryAnnexB) + uint32_t beginPos, bool* tryAnnexB) { Maybe<DeclarationKind> redeclaredKind; - if (!tryDeclareVar(name, DeclarationKind::VarForAnnexBLexicalFunction, &redeclaredKind)) + uint32_t unused; + if (!tryDeclareVar(name, DeclarationKind::VarForAnnexBLexicalFunction, beginPos, + &redeclaredKind, &unused)) + { return false; + } + + if (!redeclaredKind && pc->isFunctionBox()) { + ParseContext::Scope& funScope = pc->functionScope(); + ParseContext::Scope& varScope = pc->varScope(); + if (&funScope != &varScope) { + // Annex B.3.3.1 disallows redeclaring parameter names. In the + // presence of parameter expressions, parameter names are on the + // function scope, which encloses the var scope. This means + // tryDeclareVar call above would not catch this case, so test it + // manually. + if (AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(name)) { + DeclarationKind declaredKind = p->value()->kind(); + if (DeclarationKindIsParameter(declaredKind)) + redeclaredKind = Some(declaredKind); + else + MOZ_ASSERT(FunctionScope::isSpecialName(context, name)); + } + } + } if (redeclaredKind) { // If an early error would have occurred, undo all the @@ -1187,11 +1396,11 @@ Parser<ParseHandler>::checkLexicalDeclarationDirectlyWithinBlock(ParseContext::S if (!StatementKindIsBraced(stmt.kind()) && stmt.kind() != StatementKind::ForLoopLexicalHead) { - reportWithOffset(ParseError, false, pos.begin, - stmt.kind() == StatementKind::Label - ? JSMSG_LEXICAL_DECL_LABEL - : JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, - DeclarationKindString(kind)); + errorAt(pos.begin, + stmt.kind() == StatementKind::Label + ? JSMSG_LEXICAL_DECL_LABEL + : JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, + DeclarationKindString(kind)); return false; } @@ -1208,36 +1417,52 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind if (pc->useAsmOrInsideUseAsm()) return true; - if (!checkStrictBinding(name, pos)) - return false; - switch (kind) { case DeclarationKind::Var: case DeclarationKind::BodyLevelFunction: case DeclarationKind::ForOfVar: { Maybe<DeclarationKind> redeclaredKind; - if (!tryDeclareVar(name, kind, &redeclaredKind)) + uint32_t prevPos; + if (!tryDeclareVar(name, kind, pos.begin, &redeclaredKind, &prevPos)) return false; if (redeclaredKind) { - reportRedeclaration(name, *redeclaredKind, pos); + reportRedeclaration(name, *redeclaredKind, pos, prevPos); return false; } break; } + case DeclarationKind::ModuleBodyLevelFunction: { + MOZ_ASSERT(pc->atModuleLevel()); + + AddDeclaredNamePtr p = pc->varScope().lookupDeclaredNameForAdd(name); + if (p) { + reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos()); + return false; + } + + if (!pc->varScope().addDeclaredName(pc, p, name, kind, pos.begin)) + return false; + + // Body-level functions in modules are always closed over. + pc->varScope().lookupDeclaredName(name)->value()->setClosedOver(); + + break; + } + case DeclarationKind::FormalParameter: { // It is an early error if any non-positional formal parameter name // (e.g., destructuring formal parameter) is duplicated. AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name); if (p) { - report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); + error(JSMSG_BAD_DUP_ARGS); return false; } - if (!pc->functionScope().addDeclaredName(pc, p, name, kind)) + if (!pc->functionScope().addDeclaredName(pc, p, name, kind, pos.begin)) return false; break; @@ -1247,7 +1472,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind // Functions in block have complex allowances in sloppy mode for being // labelled that other lexical declarations do not have. Those checks // are more complex than calling checkLexicalDeclarationDirectlyWithin- - // Block and are done in checkFunctionDefinition. + // Block and are done inline in callers. ParseContext::Scope* scope = pc->innermostScope(); if (AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name)) { @@ -1260,7 +1485,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind (p->value()->kind() != DeclarationKind::LexicalFunction && p->value()->kind() != DeclarationKind::VarForAnnexBLexicalFunction)) { - reportRedeclaration(name, p->value()->kind(), pos); + reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos()); return false; } @@ -1268,7 +1493,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind // declaration that shadows the VarForAnnexBLexicalFunction. p->value()->alterKind(kind); } else { - if (!scope->addDeclaredName(pc, p, name, kind)) + if (!scope->addDeclaredName(pc, p, name, kind, pos.begin)) return false; } @@ -1281,7 +1506,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind // contain 'let'. (CatchParameter is the only lexical binding form // without this restriction.) if (name == context->names().let) { - reportWithOffset(ParseError, false, pos.begin, JSMSG_LEXICAL_DECL_DEFINES_LET); + errorAt(pos.begin, JSMSG_LEXICAL_DECL_DEFINES_LET); return false; } @@ -1308,7 +1533,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind if (pc->isFunctionExtraBodyVarScopeInnermost()) { DeclaredNamePtr p = pc->functionScope().lookupDeclaredName(name); if (p && DeclarationKindIsParameter(p->value()->kind())) { - reportRedeclaration(name, p->value()->kind(), pos); + reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos()); return false; } } @@ -1325,12 +1550,12 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind p = scope->lookupDeclaredNameForAdd(name); MOZ_ASSERT(!p); } else { - reportRedeclaration(name, p->value()->kind(), pos); + reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos()); return false; } } - if (!p && !scope->addDeclaredName(pc, p, name, kind)) + if (!p && !scope->addDeclaredName(pc, p, name, kind, pos.begin)) return false; break; @@ -1440,8 +1665,7 @@ Parser<FullParseHandler>::checkStatementsEOF() if (!tokenStream.peekToken(&tt, TokenStream::Operand)) return false; if (tt != TOK_EOF) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "expression", TokenKindToDesc(tt)); + error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt)); return false; } return true; @@ -1451,16 +1675,26 @@ template <typename Scope> static typename Scope::Data* NewEmptyBindingData(ExclusiveContext* cx, LifoAlloc& alloc, uint32_t numBindings) { + using Data = typename Scope::Data; size_t allocSize = Scope::sizeOfData(numBindings); - typename Scope::Data* bindings = static_cast<typename Scope::Data*>(alloc.alloc(allocSize)); - if (!bindings) { + auto* bindings = alloc.allocInSize<Data>(allocSize, numBindings); + if (!bindings) ReportOutOfMemory(cx); - return nullptr; - } - PodZero(bindings); return bindings; } +/** + * Copy-construct |BindingName|s from |bindings| into |cursor|, then return + * the location one past the newly-constructed |BindingName|s. + */ +static MOZ_MUST_USE BindingName* +FreshlyInitializeBindings(BindingName* cursor, const Vector<BindingName>& bindings) +{ + for (const BindingName& binding : bindings) + new (cursor++) BindingName(binding); + return cursor; +} + template <> Maybe<GlobalScope::Data*> Parser<FullParseHandler>::newGlobalScopeData(ParseContext::Scope& scope) @@ -1505,22 +1739,20 @@ Parser<FullParseHandler>::newGlobalScopeData(ParseContext::Scope& scope) return Nothing(); // The ordering here is important. See comments in GlobalScope. - BindingName* start = bindings->names; + BindingName* start = bindings->trailingNames.start(); BindingName* cursor = start; - PodCopy(cursor, funs.begin(), funs.length()); - cursor += funs.length(); + cursor = FreshlyInitializeBindings(cursor, funs); bindings->varStart = cursor - start; - PodCopy(cursor, vars.begin(), vars.length()); - cursor += vars.length(); + cursor = FreshlyInitializeBindings(cursor, vars); bindings->letStart = cursor - start; - PodCopy(cursor, lets.begin(), lets.length()); - cursor += lets.length(); + cursor = FreshlyInitializeBindings(cursor, lets); bindings->constStart = cursor - start; - PodCopy(cursor, consts.begin(), consts.length()); + cursor = FreshlyInitializeBindings(cursor, consts); + bindings->length = numBindings; } @@ -1572,22 +1804,20 @@ Parser<FullParseHandler>::newModuleScopeData(ParseContext::Scope& scope) return Nothing(); // The ordering here is important. See comments in ModuleScope. - BindingName* start = bindings->names; + BindingName* start = bindings->trailingNames.start(); BindingName* cursor = start; - PodCopy(cursor, imports.begin(), imports.length()); - cursor += imports.length(); + cursor = FreshlyInitializeBindings(cursor, imports); bindings->varStart = cursor - start; - PodCopy(cursor, vars.begin(), vars.length()); - cursor += vars.length(); + cursor = FreshlyInitializeBindings(cursor, vars); bindings->letStart = cursor - start; - PodCopy(cursor, lets.begin(), lets.length()); - cursor += lets.length(); + cursor = FreshlyInitializeBindings(cursor, lets); bindings->constStart = cursor - start; - PodCopy(cursor, consts.begin(), consts.length()); + cursor = FreshlyInitializeBindings(cursor, consts); + bindings->length = numBindings; } @@ -1623,16 +1853,16 @@ Parser<FullParseHandler>::newEvalScopeData(ParseContext::Scope& scope) if (!bindings) return Nothing(); - BindingName* start = bindings->names; + BindingName* start = bindings->trailingNames.start(); BindingName* cursor = start; // Keep track of what vars are functions. This is only used in BCE to omit // superfluous DEFVARs. - PodCopy(cursor, funs.begin(), funs.length()); - cursor += funs.length(); + cursor = FreshlyInitializeBindings(cursor, funs); bindings->varStart = cursor - start; - PodCopy(cursor, vars.begin(), vars.length()); + cursor = FreshlyInitializeBindings(cursor, vars); + bindings->length = numBindings; } @@ -1697,11 +1927,8 @@ Parser<FullParseHandler>::newFunctionScopeData(ParseContext::Scope& scope, bool case BindingKind::Var: // The only vars in the function scope when there are parameter // exprs, which induces a separate var environment, should be the - // special internal bindings. - MOZ_ASSERT_IF(hasParameterExprs, - bi.name() == context->names().arguments || - bi.name() == context->names().dotThis || - bi.name() == context->names().dotGenerator); + // special bindings. + MOZ_ASSERT_IF(hasParameterExprs, FunctionScope::isSpecialName(context, bi.name())); if (!vars.append(binding)) return Nothing(); break; @@ -1719,18 +1946,17 @@ Parser<FullParseHandler>::newFunctionScopeData(ParseContext::Scope& scope, bool return Nothing(); // The ordering here is important. See comments in FunctionScope. - BindingName* start = bindings->names; + BindingName* start = bindings->trailingNames.start(); BindingName* cursor = start; - PodCopy(cursor, positionalFormals.begin(), positionalFormals.length()); - cursor += positionalFormals.length(); + cursor = FreshlyInitializeBindings(cursor, positionalFormals); bindings->nonPositionalFormalStart = cursor - start; - PodCopy(cursor, formals.begin(), formals.length()); - cursor += formals.length(); + cursor = FreshlyInitializeBindings(cursor, formals); bindings->varStart = cursor - start; - PodCopy(cursor, vars.begin(), vars.length()); + cursor = FreshlyInitializeBindings(cursor, vars); + bindings->length = numBindings; } @@ -1760,10 +1986,11 @@ Parser<FullParseHandler>::newVarScopeData(ParseContext::Scope& scope) return Nothing(); // The ordering here is important. See comments in FunctionScope. - BindingName* start = bindings->names; + BindingName* start = bindings->trailingNames.start(); BindingName* cursor = start; - PodCopy(cursor, vars.begin(), vars.length()); + cursor = FreshlyInitializeBindings(cursor, vars); + bindings->length = numBindings; } @@ -1808,14 +2035,14 @@ Parser<FullParseHandler>::newLexicalScopeData(ParseContext::Scope& scope) return Nothing(); // The ordering here is important. See comments in LexicalScope. - BindingName* cursor = bindings->names; + BindingName* cursor = bindings->trailingNames.start(); BindingName* start = cursor; - PodCopy(cursor, lets.begin(), lets.length()); - cursor += lets.length(); + cursor = FreshlyInitializeBindings(cursor, lets); bindings->constStart = cursor - start; - PodCopy(cursor, consts.begin(), consts.length()); + cursor = FreshlyInitializeBindings(cursor, consts); + bindings->length = numBindings; } @@ -1900,7 +2127,7 @@ Parser<FullParseHandler>::evalBody(EvalSharedContext* evalsc) // script. if (hasUsedName(context->names().arguments)) { if (IsArgumentsUsedInLegacyGenerator(context, pc->sc()->compilationEnclosingScope())) { - report(ParseError, false, nullptr, JSMSG_BAD_GENEXP_BODY, js_arguments_str); + error(JSMSG_BAD_GENEXP_BODY, js_arguments_str); return nullptr; } } @@ -1986,7 +2213,7 @@ Parser<FullParseHandler>::moduleBody(ModuleSharedContext* modulesc) if (!mn) return null(); - AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, true); + AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, true); ParseNode* pn = statementList(YieldIsKeyword); if (!pn) return null(); @@ -1998,7 +2225,7 @@ Parser<FullParseHandler>::moduleBody(ModuleSharedContext* modulesc) if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); if (tt != TOK_EOF) { - report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT, "module", TokenKindToDesc(tt)); + error(JSMSG_GARBAGE_AFTER_INPUT, "module", TokenKindToDesc(tt)); return null(); } @@ -2078,8 +2305,11 @@ Parser<ParseHandler>::declareFunctionThis() ParseContext::Scope& funScope = pc->functionScope(); AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotThis); MOZ_ASSERT(!p); - if (!funScope.addDeclaredName(pc, p, dotThis, DeclarationKind::Var)) + if (!funScope.addDeclaredName(pc, p, dotThis, DeclarationKind::Var, + DeclaredNameInfo::npos)) + { return false; + } funbox->setHasThisBinding(); } @@ -2121,14 +2351,17 @@ Parser<ParseHandler>::declareDotGeneratorName() ParseContext::Scope& funScope = pc->functionScope(); HandlePropertyName dotGenerator = context->names().dotGenerator; AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotGenerator); - if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var)) + if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var, + DeclaredNameInfo::npos)) + { return false; + } return true; } template <typename ParseHandler> bool -Parser<ParseHandler>::finishFunctionScopes() +Parser<ParseHandler>::finishFunctionScopes(bool isStandaloneFunction) { FunctionBox* funbox = pc->functionBox(); @@ -2137,7 +2370,7 @@ Parser<ParseHandler>::finishFunctionScopes() return false; } - if (funbox->function()->isNamedLambda()) { + if (funbox->function()->isNamedLambda() && !isStandaloneFunction) { if (!propagateFreeNamesAndMarkClosedOverBindings(pc->namedLambdaScope())) return false; } @@ -2147,9 +2380,9 @@ Parser<ParseHandler>::finishFunctionScopes() template <> bool -Parser<FullParseHandler>::finishFunction() +Parser<FullParseHandler>::finishFunction(bool isStandaloneFunction /* = false */) { - if (!finishFunctionScopes()) + if (!finishFunctionScopes(isStandaloneFunction)) return false; FunctionBox* funbox = pc->functionBox(); @@ -2170,7 +2403,7 @@ Parser<FullParseHandler>::finishFunction() funbox->functionScopeBindings().set(*bindings); } - if (funbox->function()->isNamedLambda()) { + if (funbox->function()->isNamedLambda() && !isStandaloneFunction) { Maybe<LexicalScope::Data*> bindings = newLexicalScopeData(pc->namedLambdaScope()); if (!bindings) return false; @@ -2182,14 +2415,14 @@ Parser<FullParseHandler>::finishFunction() template <> bool -Parser<SyntaxParseHandler>::finishFunction() +Parser<SyntaxParseHandler>::finishFunction(bool isStandaloneFunction /* = false */) { // The LazyScript for a lazily parsed function needs to know its set of // free variables and inner functions so that when it is fully parsed, we // can skip over any already syntax parsed inner functions and still // retain correct scope information. - if (!finishFunctionScopes()) + if (!finishFunctionScopes(isStandaloneFunction)) return false; // There are too many bindings or inner functions to be saved into the @@ -2206,6 +2439,7 @@ Parser<SyntaxParseHandler>::finishFunction() LazyScript* lazy = LazyScript::Create(context, fun, pc->closedOverBindingsForLazy(), pc->innerFunctionsForLazy, versionNumber(), funbox->bufStart, funbox->bufEnd, + funbox->toStringStart, funbox->startLine, funbox->startColumn); if (!lazy) return false; @@ -2218,6 +2452,8 @@ Parser<SyntaxParseHandler>::finishFunction() lazy->setAsyncKind(funbox->asyncKind()); if (funbox->hasRest()) lazy->setHasRest(); + if (funbox->isExprBody()) + lazy->setIsExprBody(); if (funbox->isLikelyConstructorWrapper()) lazy->setLikelyConstructorWrapper(); if (funbox->isDerivedClassConstructor()) @@ -2259,7 +2495,33 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun, { MOZ_ASSERT(checkOptionsCalled); - Node fn = handler.newFunctionDefinition(); + // Skip prelude. + TokenKind tt; + if (!tokenStream.getToken(&tt)) + return null(); + if (asyncKind == AsyncFunction) { + if (!tokenStream.getToken(&tt)) + return null(); + } + MOZ_ASSERT(tt == TOK_FUNCTION); + + if (!tokenStream.getToken(&tt)) + return null(); + if (generatorKind == StarGenerator && asyncKind == SyncFunction) { + MOZ_ASSERT(tt == TOK_MUL); + if (!tokenStream.getToken(&tt)) + return null(); + } + + // Skip function name, if present. + if (tt == TOK_NAME || tt == TOK_YIELD) { + MOZ_ASSERT(tokenStream.currentName() == fun->explicitName()); + } else { + MOZ_ASSERT(fun->explicitName() == nullptr); + tokenStream.ungetToken(); + } + + Node fn = handler.newFunctionStatement(); if (!fn) return null(); @@ -2268,8 +2530,8 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun, return null(); fn->pn_body = argsbody; - FunctionBox* funbox = newFunctionBox(fn, fun, inheritedDirectives, generatorKind, - asyncKind, /* tryAnnexB = */ false); + FunctionBox* funbox = newFunctionBox(fn, fun, /* toStringStart = */ 0, inheritedDirectives, + generatorKind, asyncKind, /* tryAnnexB = */ false); if (!funbox) return null(); funbox->initStandaloneFunction(enclosingScope); @@ -2280,19 +2542,17 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun, funpc.setIsStandaloneFunctionBody(); YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); - AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction); + AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction); if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement, - parameterListEnd)) + parameterListEnd, /* isStandaloneFunction = */ true)) { return null(); } - TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); if (tt != TOK_EOF) { - report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT, - "function body", TokenKindToDesc(tt)); + error(JSMSG_GARBAGE_AFTER_INPUT, "function body", TokenKindToDesc(tt)); return null(); } @@ -2344,8 +2604,11 @@ Parser<ParseHandler>::declareFunctionArgumentsObject() if (tryDeclareArguments) { AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(argumentsName); if (!p) { - if (!funScope.addDeclaredName(pc, p, argumentsName, DeclarationKind::Var)) + if (!funScope.addDeclaredName(pc, p, argumentsName, DeclarationKind::Var, + DeclaredNameInfo::npos)) + { return false; + } funbox->declaredArguments = true; funbox->usesArguments = true; } else if (hasExtraBodyVarScope) { @@ -2561,39 +2824,59 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind, /* * WARNING: Do not call this function directly. - * Call either MatchOrInsertSemicolonAfterExpression or - * MatchOrInsertSemicolonAfterNonExpression instead, depending on context. + * Call either matchOrInsertSemicolonAfterExpression or + * matchOrInsertSemicolonAfterNonExpression instead, depending on context. */ -static bool -MatchOrInsertSemicolonHelper(TokenStream& ts, TokenStream::Modifier modifier) +template <typename ParseHandler> +bool +Parser<ParseHandler>::matchOrInsertSemicolonHelper(TokenStream::Modifier modifier) { TokenKind tt = TOK_EOF; - if (!ts.peekTokenSameLine(&tt, modifier)) + if (!tokenStream.peekTokenSameLine(&tt, modifier)) return false; if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) { + /* + * When current token is `await` and it's outside of async function, + * it's possibly intended to be an await expression. + * + * await f(); + * ^ + * | + * tried to insert semicolon here + * + * Detect this situation and throw an understandable error. Otherwise + * we'd throw a confusing "missing ; before statement" error. + */ + if (!pc->isAsync() && tokenStream.currentToken().type == TOK_AWAIT) { + error(JSMSG_AWAIT_OUTSIDE_ASYNC); + return false; + } + /* Advance the scanner for proper error location reporting. */ - ts.consumeKnownToken(tt, modifier); - ts.reportError(JSMSG_SEMI_BEFORE_STMNT); + tokenStream.consumeKnownToken(tt, modifier); + error(JSMSG_SEMI_BEFORE_STMNT); return false; } bool matched; - if (!ts.matchToken(&matched, TOK_SEMI, modifier)) + if (!tokenStream.matchToken(&matched, TOK_SEMI, modifier)) return false; if (!matched && modifier == TokenStream::None) - ts.addModifierException(TokenStream::OperandIsNone); + tokenStream.addModifierException(TokenStream::OperandIsNone); return true; } -static bool -MatchOrInsertSemicolonAfterExpression(TokenStream& ts) +template <typename ParseHandler> +bool +Parser<ParseHandler>::matchOrInsertSemicolonAfterExpression() { - return MatchOrInsertSemicolonHelper(ts, TokenStream::None); + return matchOrInsertSemicolonHelper(TokenStream::None); } -static bool -MatchOrInsertSemicolonAfterNonExpression(TokenStream& ts) +template <typename ParseHandler> +bool +Parser<ParseHandler>::matchOrInsertSemicolonAfterNonExpression() { - return MatchOrInsertSemicolonHelper(ts, TokenStream::Operand); + return matchOrInsertSemicolonHelper(TokenStream::Operand); } template <typename ParseHandler> @@ -2687,7 +2970,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn firstTokenModifier = funbox->isAsync() ? TokenStream::None : TokenStream::Operand; if (!tokenStream.peekToken(&tt, firstTokenModifier)) return false; - if (tt == TOK_NAME || tt == TOK_YIELD) { + if (TokenKindIsPossibleIdentifier(tt)) { parenFreeArrow = true; argModifier = firstTokenModifier; } @@ -2697,8 +2980,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn if (!tokenStream.getToken(&tt, firstTokenModifier)) return false; if (tt != TOK_LP) { - report(ParseError, false, null(), - kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL); + error(kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL); return false; } @@ -2730,13 +3012,13 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn AtomVector& positionalFormals = pc->positionalFormalParameterNames(); if (IsGetterKind(kind)) { - report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s"); + error(JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s"); return false; } while (true) { if (hasRest) { - report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST); + error(JSMSG_PARAMETER_AFTER_REST); return false; } @@ -2744,19 +3026,18 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn if (!tokenStream.getToken(&tt, argModifier)) return false; argModifier = TokenStream::Operand; - MOZ_ASSERT_IF(parenFreeArrow, tt == TOK_NAME || tt == TOK_YIELD); + MOZ_ASSERT_IF(parenFreeArrow, TokenKindIsPossibleIdentifier(tt)); if (tt == TOK_TRIPLEDOT) { if (IsSetterKind(kind)) { - report(ParseError, false, null(), - JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); + error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); return false; } disallowDuplicateParams = true; if (duplicatedParam) { // Has duplicated args before the rest parameter. - report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); + error(JSMSG_BAD_DUP_ARGS); return false; } @@ -2766,8 +3047,8 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn if (!tokenStream.getToken(&tt)) return false; - if (tt != TOK_NAME && tt != TOK_YIELD && tt != TOK_LB && tt != TOK_LC) { - report(ParseError, false, null(), JSMSG_NO_REST_NAME); + if (!TokenKindIsPossibleIdentifier(tt) && tt != TOK_LB && tt != TOK_LC) { + error(JSMSG_NO_REST_NAME); return false; } } @@ -2778,7 +3059,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn disallowDuplicateParams = true; if (duplicatedParam) { // Has duplicated args before the destructuring parameter. - report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); + error(JSMSG_BAD_DUP_ARGS); return false; } @@ -2796,26 +3077,21 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn break; } - case TOK_NAME: - case TOK_YIELD: { - if (parenFreeArrow) - funbox->setStart(tokenStream); - - if (funbox->isAsync() && tokenStream.currentName() == context->names().await) { - // `await` is already gotten as TOK_NAME for the following - // case: - // - // async await => 1 - report(ParseError, false, null(), JSMSG_RESERVED_ID, "await"); + default: { + if (!TokenKindIsPossibleIdentifier(tt)) { + error(JSMSG_MISSING_FORMAL); return false; } + if (parenFreeArrow) + funbox->setStart(tokenStream); + RootedPropertyName name(context, bindingIdentifier(yieldHandling)); if (!name) return false; - if (!notePositionalFormalParameter(funcpn, name, disallowDuplicateParams, - &duplicatedParam)) + if (!notePositionalFormalParameter(funcpn, name, pos().begin, + disallowDuplicateParams, &duplicatedParam)) { return false; } @@ -2824,14 +3100,10 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn break; } - - default: - report(ParseError, false, null(), JSMSG_MISSING_FORMAL); - return false; } if (positionalFormals.length() >= ARGNO_LIMIT) { - report(ParseError, false, null(), JSMSG_TOO_MANY_FUN_ARGS); + error(JSMSG_TOO_MANY_FUN_ARGS); return false; } @@ -2846,12 +3118,12 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn MOZ_ASSERT(!parenFreeArrow); if (hasRest) { - report(ParseError, false, null(), JSMSG_REST_WITH_DEFAULT); + error(JSMSG_REST_WITH_DEFAULT); return false; } disallowDuplicateParams = true; if (duplicatedParam) { - report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); + error(JSMSG_BAD_DUP_ARGS); return false; } @@ -2895,12 +3167,11 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn return false; if (tt != TOK_RP) { if (IsSetterKind(kind)) { - report(ParseError, false, null(), - JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); + error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); return false; } - report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL); + error(JSMSG_PAREN_AFTER_FORMAL); return false; } } @@ -2913,78 +3184,17 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn funbox->function()->setArgCount(positionalFormals.length()); } else if (IsSetterKind(kind)) { - report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); + error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); return false; } return true; } -template <typename ParseHandler> -bool -Parser<ParseHandler>::checkFunctionDefinition(HandleAtom funAtom, Node pn, FunctionSyntaxKind kind, - GeneratorKind generatorKind, bool* tryAnnexB) -{ - if (kind == Statement) { - TokenPos pos = handler.getPosition(pn); - RootedPropertyName funName(context, funAtom->asPropertyName()); - - // In sloppy mode, Annex B.3.2 allows labelled function - // declarations. Otherwise it is a parse error. - ParseContext::Statement* declaredInStmt = pc->innermostStatement(); - if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) { - MOZ_ASSERT(!pc->sc()->strict(), - "labeled functions shouldn't be parsed in strict mode"); - - // Find the innermost non-label statement. Report an error if it's - // unbraced: functions can't appear in it. Otherwise the statement - // (or its absence) determines the scope the function's bound in. - while (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) - declaredInStmt = declaredInStmt->enclosing(); - - if (declaredInStmt && !StatementKindIsBraced(declaredInStmt->kind())) { - reportWithOffset(ParseError, false, pos.begin, JSMSG_SLOPPY_FUNCTION_LABEL); - return false; - } - } - - if (declaredInStmt) { - MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label); - MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind())); - - if (!pc->sc()->strict() && generatorKind == NotGenerator) { - // Under sloppy mode, try Annex B.3.3 semantics. If making an - // additional 'var' binding of the same name does not throw an - // early error, do so. This 'var' binding would be assigned - // the function object when its declaration is reached, not at - // the start of the block. - - if (!tryDeclareVarForAnnexBLexicalFunction(funName, tryAnnexB)) - return false; - } - - if (!noteDeclaredName(funName, DeclarationKind::LexicalFunction, pos)) - return false; - } else { - if (!noteDeclaredName(funName, DeclarationKind::BodyLevelFunction, pos)) - return false; - - // Body-level functions in modules are always closed over. - if (pc->atModuleLevel()) - pc->varScope().lookupDeclaredName(funName)->value()->setClosedOver(); - } - } else { - // A function expression does not introduce any binding. - handler.setOp(pn, kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA); - } - - return true; -} - template <> bool -Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKind kind, - bool tryAnnexB) +Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, uint32_t toStringStart, + FunctionSyntaxKind kind, bool tryAnnexB) { // When a lazily-parsed function is called, we only fully parse (and emit) // that function, not any of its nested children. The initial syntax-only @@ -2993,7 +3203,7 @@ Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKin RootedFunction fun(context, handler.nextLazyInnerFunction()); MOZ_ASSERT(!fun->isLegacyGenerator()); - FunctionBox* funbox = newFunctionBox(pn, fun, Directives(/* strict = */ false), + FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, Directives(/* strict = */ false), fun->generatorKind(), fun->asyncKind(), tryAnnexB); if (!funbox) return false; @@ -3001,6 +3211,8 @@ Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKin LazyScript* lazy = fun->lazyScript(); if (lazy->needsHomeObject()) funbox->setNeedsHomeObject(); + if (lazy->isExprBody()) + funbox->setIsExprBody(); PropagateTransitiveParseFlags(lazy, pc->sc()); @@ -3013,18 +3225,23 @@ Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKin if (!tokenStream.advance(fun->lazyScript()->end() - userbufBase)) return false; - if (kind == Statement && fun->isExprBody()) { - if (!MatchOrInsertSemicolonAfterExpression(tokenStream)) +#if JS_HAS_EXPR_CLOSURES + // Only expression closure can be Statement kind. + // If we remove expression closure, we can remove isExprBody flag from + // LazyScript and JSScript. + if (kind == Statement && funbox->isExprBody()) { + if (!matchOrInsertSemicolonAfterExpression()) return false; } +#endif return true; } template <> bool -Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind, - bool tryAnnexB) +Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, uint32_t toStringStart, + FunctionSyntaxKind kind, bool tryAnnexB) { MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing"); } @@ -3043,7 +3260,7 @@ Parser<ParseHandler>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling if (!tokenStream.getToken(&tt)) return false; if (tt != TOK_RC) { - report(ParseError, false, null(), JSMSG_TEMPLSTR_UNTERM_EXPR); + error(JSMSG_TEMPLSTR_UNTERM_EXPR); return false; } @@ -3076,7 +3293,7 @@ template <typename ParseHandler> typename ParseHandler::Node Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling) { - Node pn = noSubstitutionTemplate(); + Node pn = noSubstitutionUntaggedTemplate(); if (!pn) return null(); @@ -3089,7 +3306,7 @@ Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling) if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt)) return null(); - pn = noSubstitutionTemplate(); + pn = noSubstitutionUntaggedTemplate(); if (!pn) return null(); @@ -3100,31 +3317,20 @@ Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling) template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yieldHandling, +Parser<ParseHandler>::functionDefinition(uint32_t toStringStart, Node pn, InHandling inHandling, + YieldHandling yieldHandling, HandleAtom funName, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, - InvokedPrediction invoked) + bool tryAnnexB /* = false */) { MOZ_ASSERT_IF(kind == Statement, funName); MOZ_ASSERT_IF(asyncKind == AsyncFunction, generatorKind == StarGenerator); - Node pn = handler.newFunctionDefinition(); - if (!pn) - return null(); - - if (invoked) - pn = handler.setLikelyIIFE(pn); - - // Note the declared name and check for early errors. - bool tryAnnexB = false; - if (!checkFunctionDefinition(funName, pn, kind, generatorKind, &tryAnnexB)) - return null(); - // When fully parsing a LazyScript, we do not fully reparse its inner // functions, which are also lazy. Instead, their free variables and // source extents are recorded and may be skipped. if (handler.canSkipLazyInnerFunctions()) { - if (!skipLazyInnerFunction(pn, kind, tryAnnexB)) + if (!skipLazyInnerFunction(pn, toStringStart, kind, tryAnnexB)) return null(); return pn; } @@ -3157,8 +3363,9 @@ Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yi // reparse a function due to failed syntax parsing and encountering new // "use foo" directives. while (true) { - if (trySyntaxParseInnerFunction(pn, fun, inHandling, yieldHandling, kind, generatorKind, - asyncKind, tryAnnexB, directives, &newDirectives)) + if (trySyntaxParseInnerFunction(pn, fun, toStringStart, inHandling, yieldHandling, kind, + generatorKind, asyncKind, tryAnnexB, directives, + &newDirectives)) { break; } @@ -3185,6 +3392,7 @@ Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yi template <> bool Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunction fun, + uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, @@ -3218,14 +3426,15 @@ Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct // Make a FunctionBox before we enter the syntax parser, because |pn| // still expects a FunctionBox to be attached to it during BCE, and // the syntax parser cannot attach one to it. - FunctionBox* funbox = newFunctionBox(pn, fun, inheritedDirectives, generatorKind, - asyncKind, tryAnnexB); + FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives, + generatorKind, asyncKind, tryAnnexB); if (!funbox) return false; funbox->initWithEnclosingParseContext(pc, kind); - if (!parser->innerFunction(SyntaxParseHandler::NodeGeneric, pc, funbox, inHandling, - yieldHandling, kind, inheritedDirectives, newDirectives)) + if (!parser->innerFunction(SyntaxParseHandler::NodeGeneric, pc, funbox, toStringStart, + inHandling, yieldHandling, kind, + inheritedDirectives, newDirectives)) { if (parser->hadAbortedSyntaxParse()) { // Try again with a full parse. UsedNameTracker needs to be @@ -3251,13 +3460,14 @@ Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct } while (false); // We failed to do a syntax parse above, so do the full parse. - return innerFunction(pn, pc, fun, inHandling, yieldHandling, kind, generatorKind, asyncKind, - tryAnnexB, inheritedDirectives, newDirectives); + return innerFunction(pn, pc, fun, toStringStart, inHandling, yieldHandling, kind, + generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives); } template <> bool Parser<SyntaxParseHandler>::trySyntaxParseInnerFunction(Node pn, HandleFunction fun, + uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, @@ -3268,13 +3478,14 @@ Parser<SyntaxParseHandler>::trySyntaxParseInnerFunction(Node pn, HandleFunction Directives* newDirectives) { // This is already a syntax parser, so just parse the inner function. - return innerFunction(pn, pc, fun, inHandling, yieldHandling, kind, generatorKind, asyncKind, - tryAnnexB, inheritedDirectives, newDirectives); + return innerFunction(pn, pc, fun, toStringStart, inHandling, yieldHandling, kind, + generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives); } template <typename ParseHandler> bool Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, + uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, Directives inheritedDirectives, Directives* newDirectives) @@ -3298,6 +3509,7 @@ Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, FunctionBox* template <typename ParseHandler> bool Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, + uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, @@ -3309,21 +3521,21 @@ Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, HandleFuncti // parser. In that case, outerpc is a ParseContext from the full parser // instead of the current top of the stack of the syntax parser. - FunctionBox* funbox = newFunctionBox(pn, fun, inheritedDirectives, generatorKind, - asyncKind, tryAnnexB); + FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives, + generatorKind, asyncKind, tryAnnexB); if (!funbox) return false; funbox->initWithEnclosingParseContext(outerpc, kind); - return innerFunction(pn, outerpc, funbox, inHandling, yieldHandling, kind, inheritedDirectives, - newDirectives); + return innerFunction(pn, outerpc, funbox, toStringStart, inHandling, yieldHandling, kind, + inheritedDirectives, newDirectives); } template <typename ParseHandler> bool Parser<ParseHandler>::appendToCallSiteObj(Node callSiteObj) { - Node cookedNode = noSubstitutionTemplate(); + Node cookedNode = noSubstitutionTaggedTemplate(); if (!cookedNode) return false; @@ -3346,13 +3558,13 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict { MOZ_ASSERT(checkOptionsCalled); - Node pn = handler.newFunctionDefinition(); + Node pn = handler.newFunctionStatement(); if (!pn) return null(); Directives directives(strict); - FunctionBox* funbox = newFunctionBox(pn, fun, directives, generatorKind, asyncKind, - /* tryAnnexB = */ false); + FunctionBox* funbox = newFunctionBox(pn, fun, /* toStringStart = */ 0, directives, + generatorKind, asyncKind, /* tryAnnexB = */ false); if (!funbox) return null(); funbox->initFromLazyFunction(); @@ -3401,7 +3613,8 @@ bool Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling, Node pn, FunctionSyntaxKind kind, - Maybe<uint32_t> parameterListEnd /* = Nothing() */) + Maybe<uint32_t> parameterListEnd /* = Nothing() */, + bool isStandaloneFunction /* = false */) { // Given a properly initialized parse context, try to parse an actual // function without concern for conversion to strict mode, use of lazy @@ -3410,9 +3623,14 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, FunctionBox* funbox = pc->functionBox(); RootedFunction fun(context, funbox->function()); - AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, funbox->isAsync()); - if (!functionArguments(yieldHandling, kind, pn)) - return false; + // See below for an explanation why arrow function parameters and arrow + // function bodies are parsed with different yield/await settings. + { + bool asyncOrArrowInAsync = funbox->isAsync() || (kind == Arrow && awaitIsKeyword()); + AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncOrArrowInAsync); + if (!functionArguments(yieldHandling, kind, pn)) + return false; + } Maybe<ParseContext::VarScope> varScope; if (funbox->hasParameterExprs) { @@ -3428,7 +3646,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, if (!tokenStream.matchToken(&matched, TOK_ARROW)) return false; if (!matched) { - report(ParseError, false, null(), JSMSG_BAD_ARROW_ARGS); + error(JSMSG_BAD_ARROW_ARGS); return false; } } @@ -3436,7 +3654,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, // When parsing something for new Function() we have to make sure to // only treat a certain part of the source as a parameter list. if (parameterListEnd.isSome() && parameterListEnd.value() != pos().begin) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_PARAMLIST_END); + error(JSMSG_UNEXPECTED_PARAMLIST_END); return false; } @@ -3445,30 +3663,30 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::Operand)) return false; + uint32_t openedPos = 0; if (tt != TOK_LC) { if ((funbox->isStarGenerator() && !funbox->isAsync()) || kind == Method || kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure || IsConstructorKind(kind)) { - report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY); + error(JSMSG_CURLY_BEFORE_BODY); return false; } if (kind != Arrow) { #if JS_HAS_EXPR_CLOSURES - addTelemetry(JSCompartment::DeprecatedExpressionClosure); if (!warnOnceAboutExprClosure()) return false; #else - report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY); + error(JSMSG_CURLY_BEFORE_BODY); return false; #endif } tokenStream.ungetToken(); bodyType = ExpressionBody; -#if JS_HAS_EXPR_CLOSURES - fun->setIsExprBody(); -#endif + funbox->setIsExprBody(); + } else { + openedPos = pos().begin; } // Arrow function parameters inherit yieldHandling from the enclosing @@ -3476,41 +3694,59 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, // |yield| in the parameters is either a name or keyword, depending on // whether the arrow function is enclosed in a generator function or not. // Whereas the |yield| in the function body is always parsed as a name. + // The same goes when parsing |await| in arrow functions. YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind(), pc->asyncKind()); - Node body = functionBody(inHandling, bodyYieldHandling, kind, bodyType); - if (!body) - return false; + Node body; + { + AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, funbox->isAsync()); + body = functionBody(inHandling, bodyYieldHandling, kind, bodyType); + if (!body) + return false; + } - if ((kind != Method && !IsConstructorKind(kind)) && fun->explicitName()) { + if ((kind == Statement || kind == Expression) && fun->explicitName()) { RootedPropertyName propertyName(context, fun->explicitName()->asPropertyName()); - if (!checkStrictBinding(propertyName, handler.getPosition(pn))) + YieldHandling nameYieldHandling; + if (kind == Expression) { + // Named lambda has binding inside it. + nameYieldHandling = bodyYieldHandling; + } else { + // Otherwise YieldHandling cannot be checked at this point + // because of different context. + // It should already be checked before this point. + nameYieldHandling = YieldIsName; + } + + // We already use the correct await-handling at this point, therefore + // we don't need call AutoAwaitIsKeyword here. + + if (!checkBindingIdentifier(propertyName, handler.getPosition(pn).begin, + nameYieldHandling)) + { return false; + } } if (bodyType == StatementListBody) { - bool matched; - if (!tokenStream.matchToken(&matched, TOK_RC, TokenStream::Operand)) - return false; - if (!matched) { - report(ParseError, false, null(), JSMSG_CURLY_AFTER_BODY); - return false; - } - funbox->bufEnd = pos().end; + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand, + reportMissingClosing(JSMSG_CURLY_AFTER_BODY, + JSMSG_CURLY_OPENED, openedPos)); + funbox->setEnd(pos().end); } else { #if !JS_HAS_EXPR_CLOSURES MOZ_ASSERT(kind == Arrow); #endif if (tokenStream.hadError()) return false; - funbox->bufEnd = pos().end; - if (kind == Statement && !MatchOrInsertSemicolonAfterExpression(tokenStream)) + funbox->setEnd(pos().end); + if (kind == Statement && !matchOrInsertSemicolonAfterExpression()) return false; } if (IsMethodDefinitionKind(kind) && pc->superScopeNeedsHomeObject()) funbox->setNeedsHomeObject(); - if (!finishFunction()) + if (!finishFunction(isStandaloneFunction)) return false; handler.setEndPosition(body, pos().begin); @@ -3522,35 +3758,38 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling, - FunctionAsyncKind asyncKind) +Parser<ParseHandler>::functionStmt(uint32_t toStringStart, YieldHandling yieldHandling, + DefaultHandling defaultHandling, FunctionAsyncKind asyncKind) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); - // Annex B.3.4 says we can parse function declarations unbraced under if - // or else as if it were braced. That is, |if (x) function f() {}| is - // parsed as |if (x) { function f() {} }|. - Maybe<ParseContext::Statement> synthesizedStmtForAnnexB; - Maybe<ParseContext::Scope> synthesizedScopeForAnnexB; - if (!pc->sc()->strict()) { - ParseContext::Statement* stmt = pc->innermostStatement(); - if (stmt && stmt->kind() == StatementKind::If) { - synthesizedStmtForAnnexB.emplace(pc, StatementKind::Block); - synthesizedScopeForAnnexB.emplace(this); - if (!synthesizedScopeForAnnexB->init(pc)) - return null(); + // In sloppy mode, Annex B.3.2 allows labelled function declarations. + // Otherwise it's a parse error. + ParseContext::Statement* declaredInStmt = pc->innermostStatement(); + if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) { + MOZ_ASSERT(!pc->sc()->strict(), + "labeled functions shouldn't be parsed in strict mode"); + + // Find the innermost non-label statement. Report an error if it's + // unbraced: functions can't appear in it. Otherwise the statement + // (or its absence) determines the scope the function's bound in. + while (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) + declaredInStmt = declaredInStmt->enclosing(); + + if (declaredInStmt && !StatementKindIsBraced(declaredInStmt->kind())) { + error(JSMSG_SLOPPY_FUNCTION_LABEL); + return null(); } } - RootedPropertyName name(context); - GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator; TokenKind tt; if (!tokenStream.getToken(&tt)) return null(); + GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator; if (tt == TOK_MUL) { if (asyncKind != SyncFunction) { - report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR); + error(JSMSG_ASYNC_GENERATOR); return null(); } generatorKind = StarGenerator; @@ -3558,7 +3797,8 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling return null(); } - if (tt == TOK_NAME || tt == TOK_YIELD) { + RootedPropertyName name(context); + if (TokenKindIsPossibleIdentifier(tt)) { name = bindingIdentifier(yieldHandling); if (!name) return null(); @@ -3567,34 +3807,53 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling tokenStream.ungetToken(); } else { /* Unnamed function expressions are forbidden in statement context. */ - report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT); + error(JSMSG_UNNAMED_FUNCTION_STMT); return null(); } - YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind); - Node fun = functionDefinition(InAllowed, newYieldHandling, name, Statement, generatorKind, - asyncKind, PredictUninvoked); - if (!fun) - return null(); + // Note the declared name and check for early errors. + bool tryAnnexB = false; + if (declaredInStmt) { + MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label); + MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind())); + + if (!pc->sc()->strict() && generatorKind == NotGenerator) { + // In sloppy mode, try Annex B.3.3 semantics. If making an + // additional 'var' binding of the same name does not throw an + // early error, do so. This 'var' binding would be assigned + // the function object when its declaration is reached, not at + // the start of the block. + if (!tryDeclareVarForAnnexBLexicalFunction(name, pos().begin, &tryAnnexB)) + return null(); + } - if (synthesizedStmtForAnnexB) { - Node synthesizedStmtList = handler.newStatementList(handler.getPosition(fun)); - if (!synthesizedStmtList) + if (!noteDeclaredName(name, DeclarationKind::LexicalFunction, pos())) + return null(); + } else { + DeclarationKind kind = pc->atModuleLevel() + ? DeclarationKind::ModuleBodyLevelFunction + : DeclarationKind::BodyLevelFunction; + if (!noteDeclaredName(name, kind, pos())) return null(); - handler.addStatementToList(synthesizedStmtList, fun); - return finishLexicalScope(*synthesizedScopeForAnnexB, synthesizedStmtList); } - return fun; + Node pn = handler.newFunctionStatement(); + if (!pn) + return null(); + + YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind); + return functionDefinition(toStringStart, pn, InAllowed, newYieldHandling, + name, Statement, generatorKind, asyncKind, tryAnnexB); } template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind asyncKind) +Parser<ParseHandler>::functionExpr(uint32_t toStringStart, InvokedPrediction invoked, + FunctionAsyncKind asyncKind) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); - AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction); + AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction); GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator; TokenKind tt; if (!tokenStream.getToken(&tt)) @@ -3602,7 +3861,7 @@ Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind if (tt == TOK_MUL) { if (asyncKind != SyncFunction) { - report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR); + error(JSMSG_ASYNC_GENERATOR); return null(); } generatorKind = StarGenerator; @@ -3613,7 +3872,7 @@ Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); RootedPropertyName name(context); - if (tt == TOK_NAME || tt == TOK_YIELD) { + if (TokenKindIsPossibleIdentifier(tt)) { name = bindingIdentifier(yieldHandling); if (!name) return null(); @@ -3621,8 +3880,15 @@ Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind tokenStream.ungetToken(); } - return functionDefinition(InAllowed, yieldHandling, name, Expression, generatorKind, - asyncKind, invoked); + Node pn = handler.newFunctionExpression(); + if (!pn) + return null(); + + if (invoked) + pn = handler.setLikelyIIFE(pn); + + return functionDefinition(toStringStart, pn, InAllowed, yieldHandling, name, Expression, + generatorKind, asyncKind); } /* @@ -3643,17 +3909,6 @@ IsEscapeFreeStringLiteral(const TokenPos& pos, JSAtom* str) return pos.begin + str->length() + 2 == pos.end; } -template <typename ParseHandler> -bool -Parser<ParseHandler>::checkUnescapedName() -{ - if (!tokenStream.currentToken().nameContainsEscape()) - return true; - - report(ParseError, false, null(), JSMSG_ESCAPED_KEYWORD); - return false; -} - template <> bool Parser<SyntaxParseHandler>::asmJS(Node list) @@ -3726,10 +3981,10 @@ Parser<FullParseHandler>::asmJS(Node list) */ template <typename ParseHandler> bool -Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont) +Parser<ParseHandler>::maybeParseDirective(Node list, Node possibleDirective, bool* cont) { TokenPos directivePos; - JSAtom* directive = handler.isStringExprStatement(pn, &directivePos); + JSAtom* directive = handler.isStringExprStatement(possibleDirective, &directivePos); *cont = !!directive; if (!*cont) @@ -3746,7 +4001,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont) // directive in the future. We don't want to interfere with people // taking advantage of directive-prologue-enabled features that appear // in other browsers first. - handler.setPrologue(pn); + handler.setInDirectivePrologue(possibleDirective); if (directive == context->names().useStrict) { // Functions with non-simple parameter lists (destructuring, @@ -3760,8 +4015,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont) : funbox->hasParameterExprs ? "default" : "rest"; - reportWithOffset(ParseError, false, directivePos.begin, - JSMSG_STRICT_NON_SIMPLE_PARAMS, parameterKind); + errorAt(directivePos.begin, JSMSG_STRICT_NON_SIMPLE_PARAMS, parameterKind); return false; } } @@ -3774,7 +4028,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont) // occur in the directive prologue -- octal escapes -- and // complain now. if (tokenStream.sawOctalEscape()) { - report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL); + error(JSMSG_DEPRECATED_OCTAL); return false; } pc->sc()->strictScript = true; @@ -3782,7 +4036,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont) } else if (directive == context->names().useAsm) { if (pc->isFunctionBox()) return asmJS(list); - return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL); + return warningAt(directivePos.begin, JSMSG_USE_ASM_DIRECTIVE_FAIL); } } return true; @@ -3814,10 +4068,8 @@ Parser<ParseHandler>::statementList(YieldHandling yieldHandling) if (tt == TOK_EOF || tt == TOK_RC) break; if (afterReturn) { - TokenPos pos(0, 0); - if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand)) + if (!tokenStream.peekOffset(&statementBegin, TokenStream::Operand)) return null(); - statementBegin = pos.begin; } Node next = statementListItem(yieldHandling, canHaveDirectives); if (!next) { @@ -3828,11 +4080,9 @@ Parser<ParseHandler>::statementList(YieldHandling yieldHandling) if (!warnedAboutStatementsAfterReturn) { if (afterReturn) { if (!handler.isStatementPermittedAfterReturnStatement(next)) { - if (!reportWithOffset(ParseWarning, false, statementBegin, - JSMSG_STMT_AFTER_RETURN)) - { + if (!warningAt(statementBegin, JSMSG_STMT_AFTER_RETURN)) return null(); - } + warnedAboutStatementsAfterReturn = true; } } else if (handler.isReturnStatement(next)) { @@ -3863,7 +4113,7 @@ Parser<ParseHandler>::condition(InHandling inHandling, YieldHandling yieldHandli /* Check for (a = b) and warn about possible (a == b) mistype. */ if (handler.isUnparenthesizedAssignment(pn)) { - if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN)) + if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN)) return null(); } return pn; @@ -3877,7 +4127,7 @@ Parser<ParseHandler>::matchLabel(YieldHandling yieldHandling, MutableHandle<Prop if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand)) return false; - if (tt == TOK_NAME || tt == TOK_YIELD) { + if (TokenKindIsPossibleIdentifier(tt)) { tokenStream.consumeKnownToken(tt, TokenStream::Operand); label.set(labelIdentifier(yieldHandling)); @@ -3900,8 +4150,17 @@ Parser<ParseHandler>::PossibleError::error(ErrorKind kind) { if (kind == ErrorKind::Expression) return exprError_; - MOZ_ASSERT(kind == ErrorKind::Destructuring); - return destructuringError_; + if (kind == ErrorKind::Destructuring) + return destructuringError_; + MOZ_ASSERT(kind == ErrorKind::DestructuringWarning); + return destructuringWarning_; +} + +template <typename ParseHandler> +bool +Parser<ParseHandler>::PossibleError::hasPendingDestructuringError() +{ + return hasError(ErrorKind::Destructuring); } template <typename ParseHandler> @@ -3920,7 +4179,8 @@ Parser<ParseHandler>::PossibleError::hasError(ErrorKind kind) template <typename ParseHandler> void -Parser<ParseHandler>::PossibleError::setPending(ErrorKind kind, Node pn, unsigned errorNumber) +Parser<ParseHandler>::PossibleError::setPending(ErrorKind kind, const TokenPos& pos, + unsigned errorNumber) { // Don't overwrite a previously recorded error. if (hasError(kind)) @@ -3929,23 +4189,33 @@ Parser<ParseHandler>::PossibleError::setPending(ErrorKind kind, Node pn, unsigne // If we report an error later, we'll do it from the position where we set // the state to pending. Error& err = error(kind); - err.offset_ = (pn ? parser_.handler.getPosition(pn) : parser_.pos()).begin; + err.offset_ = pos.begin; err.errorNumber_ = errorNumber; err.state_ = ErrorState::Pending; } template <typename ParseHandler> void -Parser<ParseHandler>::PossibleError::setPendingDestructuringError(Node pn, unsigned errorNumber) +Parser<ParseHandler>::PossibleError::setPendingDestructuringErrorAt(const TokenPos& pos, + unsigned errorNumber) +{ + setPending(ErrorKind::Destructuring, pos, errorNumber); +} + +template <typename ParseHandler> +void +Parser<ParseHandler>::PossibleError::setPendingDestructuringWarningAt(const TokenPos& pos, + unsigned errorNumber) { - setPending(ErrorKind::Destructuring, pn, errorNumber); + setPending(ErrorKind::DestructuringWarning, pos, errorNumber); } template <typename ParseHandler> void -Parser<ParseHandler>::PossibleError::setPendingExpressionError(Node pn, unsigned errorNumber) +Parser<ParseHandler>::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos, + unsigned errorNumber) { - setPending(ErrorKind::Expression, pn, errorNumber); + setPending(ErrorKind::Expression, pos, errorNumber); } template <typename ParseHandler> @@ -3956,29 +4226,42 @@ Parser<ParseHandler>::PossibleError::checkForError(ErrorKind kind) return true; Error& err = error(kind); - parser_.reportWithOffset(ParseError, false, err.offset_, err.errorNumber_); + parser_.errorAt(err.offset_, err.errorNumber_); return false; } template <typename ParseHandler> bool -Parser<ParseHandler>::PossibleError::checkForDestructuringError() +Parser<ParseHandler>::PossibleError::checkForWarning(ErrorKind kind) +{ + if (!hasError(kind)) + return true; + + Error& err = error(kind); + return parser_.extraWarningAt(err.offset_, err.errorNumber_); +} + +template <typename ParseHandler> +bool +Parser<ParseHandler>::PossibleError::checkForDestructuringErrorOrWarning() { // Clear pending expression error, because we're definitely not in an // expression context. setResolved(ErrorKind::Expression); - // Report any pending destructuring error. - return checkForError(ErrorKind::Destructuring); + // Report any pending destructuring error or warning. + return checkForError(ErrorKind::Destructuring) && + checkForWarning(ErrorKind::DestructuringWarning); } template <typename ParseHandler> bool Parser<ParseHandler>::PossibleError::checkForExpressionError() { - // Clear pending destructuring error, because we're definitely not in a - // destructuring context. + // Clear pending destructuring error or warning, because we're definitely + // not in a destructuring context. setResolved(ErrorKind::Destructuring); + setResolved(ErrorKind::DestructuringWarning); // Report any pending expression error. return checkForError(ErrorKind::Expression); @@ -4011,205 +4294,271 @@ Parser<ParseHandler>::PossibleError::transferErrorsTo(PossibleError* other) } template <typename ParseHandler> -bool -Parser<ParseHandler>::checkAssignmentToCall(Node target, unsigned msg) +typename ParseHandler::Node +Parser<ParseHandler>::bindingInitializer(Node lhs, DeclarationKind kind, + YieldHandling yieldHandling) { - MOZ_ASSERT(handler.isFunctionCall(target)); + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN)); + + if (kind == DeclarationKind::FormalParameter) + pc->functionBox()->hasParameterExprs = true; - // Assignment to function calls is forbidden in ES6. We're still somewhat - // concerned about sites using this in dead code, so forbid it only in - // strict mode code (or if the werror option has been set), and otherwise - // warn. - return report(ParseStrictError, pc->sc()->strict(), target, msg); + Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); + if (!rhs) + return null(); + + handler.checkAndSetIsDirectRHSAnonFunction(rhs); + + Node assign = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP); + if (!assign) + return null(); + + if (foldConstants && !FoldConstants(context, &assign, this)) + return null(); + + return assign; } -template <> -bool -Parser<FullParseHandler>::checkDestructuringName(ParseNode* expr, Maybe<DeclarationKind> maybeDecl) +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling) { - MOZ_ASSERT(!handler.isUnparenthesizedDestructuringPattern(expr)); - - // Parentheses are forbidden around destructuring *patterns* (but allowed - // around names). Use our nicer error message for parenthesized, nested - // patterns. - if (handler.isParenthesizedDestructuringPattern(expr)) { - report(ParseError, false, expr, JSMSG_BAD_DESTRUCT_PARENS); - return false; - } + RootedPropertyName name(context, bindingIdentifier(yieldHandling)); + if (!name) + return null(); - // This expression might be in a variable-binding pattern where only plain, - // unparenthesized names are permitted. - if (maybeDecl) { - // Destructuring patterns in declarations must only contain - // unparenthesized names. - if (!handler.isUnparenthesizedName(expr)) { - report(ParseError, false, expr, JSMSG_NO_VARIABLE_NAME); - return false; - } + Node binding = newName(name); + if (!binding || !noteDeclaredName(name, kind, pos())) + return null(); - RootedPropertyName name(context, expr->name()); - return noteDeclaredName(name, *maybeDecl, handler.getPosition(expr)); - } + return binding; +} - // Otherwise this is an expression in destructuring outside a declaration. - if (!reportIfNotValidSimpleAssignmentTarget(expr, KeyedDestructuringAssignment)) - return false; +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::bindingIdentifierOrPattern(DeclarationKind kind, YieldHandling yieldHandling, + TokenKind tt) +{ + if (tt == TOK_LB) + return arrayBindingPattern(kind, yieldHandling); - MOZ_ASSERT(!handler.isFunctionCall(expr), - "function calls shouldn't be considered valid targets in " - "destructuring patterns"); + if (tt == TOK_LC) + return objectBindingPattern(kind, yieldHandling); - if (handler.isNameAnyParentheses(expr)) { - // The arguments/eval identifiers are simple in non-strict mode code. - // Warn to discourage their use nonetheless. - return reportIfArgumentsEvalTarget(expr); + if (!TokenKindIsPossibleIdentifierName(tt)) { + error(JSMSG_NO_VARIABLE_NAME); + return null(); } - // Nothing further to do for property accesses. - MOZ_ASSERT(handler.isPropertyAccess(expr)); - return true; + return bindingIdentifier(kind, yieldHandling); } -template <> -bool -Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern, - Maybe<DeclarationKind> maybeDecl, - PossibleError* possibleError /* = nullptr */); - -template <> -bool -Parser<FullParseHandler>::checkDestructuringObject(ParseNode* objectPattern, - Maybe<DeclarationKind> maybeDecl) +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling) { - MOZ_ASSERT(objectPattern->isKind(PNK_OBJECT)); + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); - for (ParseNode* member = objectPattern->pn_head; member; member = member->pn_next) { - ParseNode* target; - if (member->isKind(PNK_MUTATEPROTO)) { - target = member->pn_kid; - } else { - MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND)); - MOZ_ASSERT_IF(member->isKind(PNK_SHORTHAND), - member->pn_left->isKind(PNK_OBJECT_PROPERTY_NAME) && - member->pn_right->isKind(PNK_NAME) && - member->pn_left->pn_atom == member->pn_right->pn_atom); + JS_CHECK_RECURSION(context, return null()); - target = member->pn_right; - } - if (handler.isUnparenthesizedAssignment(target)) - target = target->pn_left; + uint32_t begin = pos().begin; + Node literal = handler.newObjectLiteral(begin); + if (!literal) + return null(); - if (handler.isUnparenthesizedDestructuringPattern(target)) { - if (!checkDestructuringPattern(target, maybeDecl)) - return false; + Maybe<DeclarationKind> declKind = Some(kind); + RootedAtom propAtom(context); + for (;;) { + TokenKind tt; + if (!tokenStream.peekToken(&tt)) + return null(); + if (tt == TOK_RC) + break; + + if (tt == TOK_TRIPLEDOT) { + // rest-binding property + tokenStream.consumeKnownToken(TOK_TRIPLEDOT); + uint32_t begin = pos().begin; + + TokenKind tt; + if (!tokenStream.getToken(&tt)) + return null(); + + Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt); + if (!inner) + return null(); + + if (!handler.addSpreadProperty(literal, begin, inner)) + return null(); } else { - if (!checkDestructuringName(target, maybeDecl)) - return false; - } - } + TokenPos namePos = tokenStream.nextToken().pos; - return true; -} + PropertyType propType; + Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom); + if (!propName) + return null(); + if (propType == PropertyType::Normal) { + // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|. -template <> -bool -Parser<FullParseHandler>::checkDestructuringArray(ParseNode* arrayPattern, - Maybe<DeclarationKind> maybeDecl) -{ - MOZ_ASSERT(arrayPattern->isKind(PNK_ARRAY)); + if (!tokenStream.getToken(&tt, TokenStream::Operand)) + return null(); - for (ParseNode* element = arrayPattern->pn_head; element; element = element->pn_next) { - if (element->isKind(PNK_ELISION)) - continue; + Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt); + if (!binding) + return null(); + + bool hasInitializer; + if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN)) + return null(); + + Node bindingExpr = hasInitializer + ? bindingInitializer(binding, kind, yieldHandling) + : binding; + if (!bindingExpr) + return null(); + + if (!handler.addPropertyDefinition(literal, propName, bindingExpr)) + return null(); + } else if (propType == PropertyType::Shorthand) { + // Handle e.g., |var {x, y} = o| as destructuring shorthand + // for |var {x: x, y: y} = o|. + MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt)); + + Node binding = bindingIdentifier(kind, yieldHandling); + if (!binding) + return null(); + + if (!handler.addShorthand(literal, propName, binding)) + return null(); + } else if (propType == PropertyType::CoverInitializedName) { + // Handle e.g., |var {x=1, y=2} = o| as destructuring + // shorthand with default values. + MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt)); + + Node binding = bindingIdentifier(kind, yieldHandling); + if (!binding) + return null(); + + tokenStream.consumeKnownToken(TOK_ASSIGN); + + Node bindingExpr = bindingInitializer(binding, kind, yieldHandling); + if (!bindingExpr) + return null(); + + if (!handler.addPropertyDefinition(literal, propName, bindingExpr)) + return null(); + } else { + errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME); + return null(); - ParseNode* target; - if (element->isKind(PNK_SPREAD)) { - if (element->pn_next) { - report(ParseError, false, element->pn_next, JSMSG_PARAMETER_AFTER_REST); - return false; } - target = element->pn_kid; - } else if (handler.isUnparenthesizedAssignment(element)) { - target = element->pn_left; - } else { - target = element; } - if (handler.isUnparenthesizedDestructuringPattern(target)) { - if (!checkDestructuringPattern(target, maybeDecl)) - return false; - } else { - if (!checkDestructuringName(target, maybeDecl)) - return false; + bool matched; + if (!tokenStream.matchToken(&matched, TOK_COMMA)) + return null(); + if (!matched) + break; + if (tt == TOK_TRIPLEDOT) { + error(JSMSG_REST_WITH_COMMA); + return null(); } } - return true; + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None, + reportMissingClosing(JSMSG_CURLY_AFTER_LIST, + JSMSG_CURLY_OPENED, begin)); + + handler.setEndPosition(literal, pos().end); + return literal; } -/* - * Destructuring patterns can appear in two kinds of contexts: - * - * - assignment-like: assignment expressions and |for| loop heads. In - * these cases, the patterns' property value positions can be - * arbitrary lvalue expressions; the destructuring is just a fancy - * assignment. - * - * - binding-like: |var| and |let| declarations, functions' formal - * parameter lists, |catch| clauses, and comprehension tails. In - * these cases, the patterns' property value positions must be - * simple names; the destructuring defines them as new variables. - * - * In both cases, other code parses the pattern as an arbitrary - * primaryExpr, and then, here in checkDestructuringPattern, verify - * that the tree is a valid AssignmentPattern or BindingPattern. - * - * In assignment-like contexts, we parse the pattern with - * pc->inDestructuringDecl clear, so the lvalue expressions in the - * pattern are parsed normally. primaryExpr links variable references - * into the appropriate use chains; creates placeholder definitions; - * and so on. checkDestructuringPattern won't bind any new names and - * we specialize lvalues as appropriate. - * - * In declaration-like contexts, the normal variable reference - * processing would just be an obstruction, because we're going to - * define the names that appear in the property value positions as new - * variables anyway. In this case, we parse the pattern with - * pc->inDestructuringDecl set, which directs primaryExpr to leave - * whatever name nodes it creates unconnected. Then, here in - * checkDestructuringPattern, we require the pattern's property value - * positions to be simple names, and define them as appropriate to the - * context. - */ -template <> -bool -Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern, - Maybe<DeclarationKind> maybeDecl, - PossibleError* possibleError /* = nullptr */) +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling) { - if (pattern->isKind(PNK_ARRAYCOMP)) { - report(ParseError, false, pattern, JSMSG_ARRAY_COMP_LEFTSIDE); - return false; - } + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB)); - bool isDestructuring = pattern->isKind(PNK_ARRAY) - ? checkDestructuringArray(pattern, maybeDecl) - : checkDestructuringObject(pattern, maybeDecl); + JS_CHECK_RECURSION(context, return null()); - // Report any pending destructuring error. - if (isDestructuring && possibleError && !possibleError->checkForDestructuringError()) - return false; + uint32_t begin = pos().begin; + Node literal = handler.newArrayLiteral(begin); + if (!literal) + return null(); - return isDestructuring; -} + uint32_t index = 0; + TokenStream::Modifier modifier = TokenStream::Operand; + for (; ; index++) { + if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) { + error(JSMSG_ARRAY_INIT_TOO_BIG); + return null(); + } + + TokenKind tt; + if (!tokenStream.getToken(&tt, TokenStream::Operand)) + return null(); + + if (tt == TOK_RB) { + tokenStream.ungetToken(); + break; + } + + if (tt == TOK_COMMA) { + if (!handler.addElision(literal, pos())) + return null(); + } else if (tt == TOK_TRIPLEDOT) { + uint32_t begin = pos().begin; + + TokenKind tt; + if (!tokenStream.getToken(&tt, TokenStream::Operand)) + return null(); + + Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt); + if (!inner) + return null(); + + if (!handler.addSpreadElement(literal, begin, inner)) + return null(); + } else { + Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt); + if (!binding) + return null(); + + bool hasInitializer; + if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN)) + return null(); + + Node element = hasInitializer + ? bindingInitializer(binding, kind, yieldHandling) + : binding; + if (!element) + return null(); + + handler.addArrayElement(literal, element); + } + + if (tt != TOK_COMMA) { + // If we didn't already match TOK_COMMA in above case. + bool matched; + if (!tokenStream.matchToken(&matched, TOK_COMMA)) + return null(); + if (!matched) { + modifier = TokenStream::None; + break; + } + if (tt == TOK_TRIPLEDOT) { + error(JSMSG_REST_WITH_COMMA); + return null(); + } + } + } + + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier, + reportMissingClosing(JSMSG_BRACKET_AFTER_LIST, + JSMSG_BRACKET_OPENED, begin)); -template <> -bool -Parser<SyntaxParseHandler>::checkDestructuringPattern(Node pattern, - Maybe<DeclarationKind> maybeDecl, - PossibleError* possibleError /* = nullptr */) -{ - return abortIfSyntaxParser(); + handler.setEndPosition(literal, pos().end); + return literal; } template <typename ParseHandler> @@ -4220,18 +4569,9 @@ Parser<ParseHandler>::destructuringDeclaration(DeclarationKind kind, YieldHandli MOZ_ASSERT(tokenStream.isCurrentTokenType(tt)); MOZ_ASSERT(tt == TOK_LB || tt == TOK_LC); - PossibleError possibleError(*this); - Node pattern; - { - pc->inDestructuringDecl = Some(kind); - pattern = primaryExpr(yieldHandling, TripledotProhibited, tt, &possibleError); - pc->inDestructuringDecl = Nothing(); - } - - if (!pattern || !checkDestructuringPattern(pattern, Some(kind), &possibleError)) - return null(); - - return pattern; + return tt == TOK_LB + ? arrayBindingPattern(kind, yieldHandling) + : objectBindingPattern(kind, yieldHandling); } template <typename ParseHandler> @@ -4245,11 +4585,11 @@ Parser<ParseHandler>::destructuringDeclarationWithoutYieldOrAwait(DeclarationKin Node res = destructuringDeclaration(kind, yieldHandling, tt); if (res) { if (pc->lastYieldOffset != startYieldOffset) { - reportWithOffset(ParseError, false, pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT); + errorAt(pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT); return null(); } if (pc->lastAwaitOffset != startAwaitOffset) { - reportWithOffset(ParseError, false, pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT); + errorAt(pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT); return null(); } } @@ -4261,6 +4601,7 @@ typename ParseHandler::Node Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling, unsigned errorNumber) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); + uint32_t openedPos = pos().begin; ParseContext::Statement stmt(pc, StatementKind::Block); ParseContext::Scope scope(this); @@ -4271,7 +4612,9 @@ Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling, unsigned error if (!list) return null(); - MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, errorNumber); + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand, + reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED, + openedPos)); return finishLexicalScope(scope, list); } @@ -4327,14 +4670,7 @@ Parser<ParseHandler>::declarationPattern(Node decl, DeclarationKind declKind, To } } - TokenKind token; - if (!tokenStream.getToken(&token, TokenStream::None)) - return null(); - - if (token != TOK_ASSIGN) { - report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_DECL); - return null(); - } + MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL); Node init = assignExpr(forHeadKind ? InProhibited : InAllowed, yieldHandling, TripledotProhibited); @@ -4349,6 +4685,12 @@ Parser<ParseHandler>::declarationPattern(Node decl, DeclarationKind declKind, To // binary operator (examined with modifier None) terminated |init|. // For all other declarations, through ASI's infinite majesty, a next // token on a new line would begin an expression. + // Similar to the case in initializerInNameDeclaration(), we need to + // peek at the next token when assignExpr() is a lazily parsed arrow + // function. + TokenKind ignored; + if (!tokenStream.peekToken(&ignored)) + return null(); tokenStream.addModifierException(TokenStream::OperandIsNone); } @@ -4367,6 +4709,10 @@ Parser<ParseHandler>::initializerInNameDeclaration(Node decl, Node binding, { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN)); + uint32_t initializerOffset; + if (!tokenStream.peekOffset(&initializerOffset, TokenStream::Operand)) + return false; + Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed, yieldHandling, TripledotProhibited); if (!initializer) @@ -4384,7 +4730,7 @@ Parser<ParseHandler>::initializerInNameDeclaration(Node decl, Node binding, // // for (var/let/const x = ... of ...); // BAD if (isForOf) { - report(ParseError, false, binding, JSMSG_BAD_FOR_LEFTSIDE); + errorAt(initializerOffset, JSMSG_OF_AFTER_FOR_LOOP_DECL); return false; } @@ -4393,18 +4739,15 @@ Parser<ParseHandler>::initializerInNameDeclaration(Node decl, Node binding, // // for (let/const x = ... in ...); // BAD if (DeclarationKindIsLexical(declKind)) { - report(ParseError, false, binding, JSMSG_BAD_FOR_LEFTSIDE); + errorAt(initializerOffset, JSMSG_IN_AFTER_LEXICAL_FOR_DECL); return false; } // This leaves only initialized for-in |var| declarations. ES6 // forbids these; later ES un-forbids in non-strict mode code. *forHeadKind = PNK_FORIN; - if (!report(ParseStrictError, pc->sc()->strict(), initializer, - JSMSG_INVALID_FOR_IN_DECL_WITH_INIT)) - { + if (!strictModeErrorAt(initializerOffset, JSMSG_INVALID_FOR_IN_DECL_WITH_INIT)) return false; - } *forInOrOfExpression = expressionAfterForInOrOf(PNK_FORIN, yieldHandling); if (!*forInOrOfExpression) @@ -4448,8 +4791,9 @@ Parser<ParseHandler>::declarationName(Node decl, DeclarationKind declKind, Token ParseNodeKind* forHeadKind, Node* forInOrOfExpression) { // Anything other than TOK_YIELD or TOK_NAME is an error. - if (tt != TOK_NAME && tt != TOK_YIELD) { - report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME); + // Anything other than possible identifier is an error. + if (!TokenKindIsPossibleIdentifier(tt)) { + error(JSMSG_NO_VARIABLE_NAME); return null(); } @@ -4509,7 +4853,7 @@ Parser<ParseHandler>::declarationName(Node decl, DeclarationKind declKind, Token // Normal const declarations, and const declarations in for(;;) // heads, must be initialized. if (declKind == DeclarationKind::Const) { - report(ParseError, false, binding, JSMSG_BAD_CONST_DECL); + errorAt(namePos.begin, JSMSG_BAD_CONST_DECL); return null(); } } @@ -4589,8 +4933,10 @@ Parser<ParseHandler>::declarationList(YieldHandling yieldHandling, template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::lexicalDeclaration(YieldHandling yieldHandling, bool isConst) +Parser<ParseHandler>::lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind) { + MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let); + /* * Parse body-level lets without a new block object. ES6 specs * that an execution environment's initial lexical environment @@ -4602,8 +4948,9 @@ Parser<ParseHandler>::lexicalDeclaration(YieldHandling yieldHandling, bool isCon * * See 8.1.1.1.6 and the note in 13.2.1. */ - Node decl = declarationList(yieldHandling, isConst ? PNK_CONST : PNK_LET); - if (!decl || !MatchOrInsertSemicolonAfterExpression(tokenStream)) + Node decl = declarationList(yieldHandling, + kind == DeclarationKind::Const ? PNK_CONST : PNK_LET); + if (!decl || !matchOrInsertSemicolonAfterExpression()) return null(); return decl; @@ -4614,42 +4961,35 @@ bool Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet) { if (tt == TOK_LC) { - TokenStream::Modifier modifier = TokenStream::KeywordIsName; while (true) { // Handle the forms |import {} from 'a'| and // |import { ..., } from 'a'| (where ... is non empty), by // escaping the loop early if the next token is }. - if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName)) + if (!tokenStream.getToken(&tt)) return false; if (tt == TOK_RC) break; - // If the next token is a keyword, the previous call to - // peekToken matched it as a TOK_NAME, and put it in the - // lookahead buffer, so this call will match keywords as well. - MUST_MATCH_TOKEN_MOD(TOK_NAME, TokenStream::KeywordIsName, JSMSG_NO_IMPORT_NAME); + if (!TokenKindIsPossibleIdentifierName(tt)) { + error(JSMSG_NO_IMPORT_NAME); + return false; + } + Rooted<PropertyName*> importName(context, tokenStream.currentName()); TokenPos importNamePos = pos(); - TokenKind maybeAs; - if (!tokenStream.peekToken(&maybeAs)) + bool matched; + if (!tokenStream.matchToken(&matched, TOK_AS)) return null(); - if (maybeAs == TOK_NAME && - tokenStream.nextName() == context->names().as) - { - tokenStream.consumeKnownToken(TOK_NAME); - - if (!checkUnescapedName()) - return false; - + if (matched) { TokenKind afterAs; if (!tokenStream.getToken(&afterAs)) return false; - if (afterAs != TOK_NAME && afterAs != TOK_YIELD) { - report(ParseError, false, null(), JSMSG_NO_BINDING_NAME); + if (!TokenKindIsPossibleIdentifierName(afterAs)) { + error(JSMSG_NO_BINDING_NAME); return false; } } else { @@ -4658,10 +4998,7 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor // by the keyword 'as'. // See the ImportSpecifier production in ES6 section 15.2.2. if (IsKeyword(importName)) { - JSAutoByteString bytes; - if (!AtomToPrintableString(context, importName, &bytes)) - return false; - report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr()); + error(JSMSG_AS_AFTER_RESERVED_WORD, ReservedWordToCharZ(importName)); return false; } } @@ -4686,31 +5023,24 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor handler.addList(importSpecSet, importSpec); - bool matched; - if (!tokenStream.matchToken(&matched, TOK_COMMA)) + TokenKind next; + if (!tokenStream.getToken(&next)) return false; - if (!matched) { - modifier = TokenStream::None; + if (next == TOK_RC) break; + + if (next != TOK_COMMA) { + error(JSMSG_RC_AFTER_IMPORT_SPEC_LIST); + return false; } } - - MUST_MATCH_TOKEN_MOD(TOK_RC, modifier, JSMSG_RC_AFTER_IMPORT_SPEC_LIST); } else { MOZ_ASSERT(tt == TOK_MUL); - if (!tokenStream.getToken(&tt)) - return false; - if (tt != TOK_NAME || tokenStream.currentName() != context->names().as) { - report(ParseError, false, null(), JSMSG_AS_AFTER_IMPORT_STAR); - return false; - } + MUST_MATCH_TOKEN(TOK_AS, JSMSG_AS_AFTER_IMPORT_STAR); - if (!checkUnescapedName()) - return false; - - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME); + MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_BINDING_NAME); Node importName = newName(context->names().star); if (!importName) @@ -4744,21 +5074,13 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor } template<> -bool -Parser<SyntaxParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet) -{ - MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); - return false; -} - -template<> ParseNode* Parser<FullParseHandler>::importDeclaration() { MOZ_ASSERT(tokenStream.currentToken().type == TOK_IMPORT); if (!pc->atModuleLevel()) { - report(ParseError, false, null(), JSMSG_IMPORT_DECL_AT_TOP_LEVEL); + error(JSMSG_IMPORT_DECL_AT_TOP_LEVEL); return null(); } @@ -4771,8 +5093,15 @@ Parser<FullParseHandler>::importDeclaration() if (!importSpecSet) return null(); - if (tt == TOK_NAME || tt == TOK_LC || tt == TOK_MUL) { - if (tt == TOK_NAME) { + if (tt == TOK_STRING) { + // Handle the form |import 'a'| by leaving the list empty. This is + // equivalent to |import {} from 'a'|. + importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin; + } else { + if (tt == TOK_LC || tt == TOK_MUL) { + if (!namedImportsOrNamespaceImport(tt, importSpecSet)) + return null(); + } else if (TokenKindIsPossibleIdentifierName(tt)) { // Handle the form |import a from 'b'|, by adding a single import // specifier to the list, with 'default' as the import name and // 'a' as the binding name. This is equivalent to @@ -4807,7 +5136,7 @@ Parser<FullParseHandler>::importDeclaration() return null(); if (tt != TOK_LC && tt != TOK_MUL) { - report(ParseError, false, null(), JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT); + error(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT); return null(); } @@ -4815,36 +5144,20 @@ Parser<FullParseHandler>::importDeclaration() return null(); } } else { - if (!namedImportsOrNamespaceImport(tt, importSpecSet)) - return null(); - } - - if (!tokenStream.getToken(&tt)) - return null(); - - if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) { - report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_CLAUSE); + error(JSMSG_DECLARATION_AFTER_IMPORT); return null(); } - if (!checkUnescapedName()) - return null(); + MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_IMPORT_CLAUSE); MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); - } else if (tt == TOK_STRING) { - // Handle the form |import 'a'| by leaving the list empty. This is - // equivalent to |import {} from 'a'|. - importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin; - } else { - report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT); - return null(); } Node moduleSpec = stringLiteral(); if (!moduleSpec) return null(); - if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterNonExpression()) return null(); ParseNode* node = @@ -4874,7 +5187,7 @@ Parser<FullParseHandler>::checkExportedName(JSAtom* exportName) if (!AtomToPrintableString(context, exportName, &str)) return false; - report(ParseError, false, null(), JSMSG_DUPLICATE_EXPORT_NAME, str.ptr()); + error(JSMSG_DUPLICATE_EXPORT_NAME, str.ptr()); return false; } @@ -4911,290 +5224,539 @@ Parser<SyntaxParseHandler>::checkExportedNamesForDeclaration(Node node) } template<> -ParseNode* -Parser<FullParseHandler>::exportDeclaration() +bool +Parser<FullParseHandler>::checkExportedNameForClause(ParseNode* node) { - MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT); + return checkExportedName(node->pn_atom); +} - if (!pc->atModuleLevel()) { - report(ParseError, false, null(), JSMSG_EXPORT_DECL_AT_TOP_LEVEL); +template<> +bool +Parser<SyntaxParseHandler>::checkExportedNameForClause(Node node) +{ + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); + return false; +} + +template<> +bool +Parser<FullParseHandler>::checkExportedNameForFunction(ParseNode* node) +{ + return checkExportedName(node->pn_funbox->function()->explicitName()); +} + +template<> +bool +Parser<SyntaxParseHandler>::checkExportedNameForFunction(Node node) +{ + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); + return false; +} + +template<> +bool +Parser<FullParseHandler>::checkExportedNameForClass(ParseNode* node) +{ + const ClassNode& cls = node->as<ClassNode>(); + MOZ_ASSERT(cls.names()); + return checkExportedName(cls.names()->innerBinding()->pn_atom); +} + +template<> +bool +Parser<SyntaxParseHandler>::checkExportedNameForClass(Node node) +{ + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); + return false; +} + +template<> +bool +Parser<FullParseHandler>::processExport(ParseNode* node) +{ + return pc->sc()->asModuleContext()->builder.processExport(node); +} + +template<> +bool +Parser<SyntaxParseHandler>::processExport(Node node) +{ + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); + return false; +} + +template<> +bool +Parser<FullParseHandler>::processExportFrom(ParseNode* node) +{ + return pc->sc()->asModuleContext()->builder.processExportFrom(node); +} + +template<> +bool +Parser<SyntaxParseHandler>::processExportFrom(Node node) +{ + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); + return false; +} + +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::exportFrom(uint32_t begin, Node specList) +{ + if (!abortIfSyntaxParser()) return null(); - } - uint32_t begin = pos().begin; + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FROM)); - Node kid; - TokenKind tt; - if (!tokenStream.getToken(&tt)) + if (!abortIfSyntaxParser()) return null(); - switch (tt) { - case TOK_LC: { - kid = handler.newList(PNK_EXPORT_SPEC_LIST); - if (!kid) - return null(); - while (true) { - // Handle the forms |export {}| and |export { ..., }| (where ... - // is non empty), by escaping the loop early if the next token - // is }. - if (!tokenStream.peekToken(&tt)) - return null(); - if (tt == TOK_RC) - break; + MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME); - Node bindingName = newName(tokenStream.currentName()); - if (!bindingName) - return null(); + Node moduleSpec = stringLiteral(); + if (!moduleSpec) + return null(); - bool foundAs; - if (!tokenStream.matchContextualKeyword(&foundAs, context->names().as)) - return null(); - if (foundAs) { - if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) - return null(); - if (tt != TOK_NAME) { - report(ParseError, false, null(), JSMSG_NO_EXPORT_NAME); - return null(); - } - } + if (!matchOrInsertSemicolonAfterNonExpression()) + return null(); - Node exportName = newName(tokenStream.currentName()); - if (!exportName) - return null(); + Node node = handler.newExportFromDeclaration(begin, specList, moduleSpec); + if (!node) + return null(); - if (!checkExportedName(exportName->pn_atom)) - return null(); + if (!processExportFrom(node)) + return null(); - Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName); - if (!exportSpec) - return null(); + return node; +} - handler.addList(kid, exportSpec); +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::exportBatch(uint32_t begin) +{ + if (!abortIfSyntaxParser()) + return null(); - bool matched; - if (!tokenStream.matchToken(&matched, TOK_COMMA)) - return null(); - if (!matched) - break; - } + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_MUL)); - MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_EXPORT_SPEC_LIST); + Node kid = handler.newList(PNK_EXPORT_SPEC_LIST); + if (!kid) + return null(); - // Careful! If |from| follows, even on a new line, it must start a - // FromClause: - // - // export { x } - // from "foo"; // a single ExportDeclaration - // - // But if it doesn't, we might have an ASI opportunity in Operand - // context, so simply matching a contextual keyword won't work: - // - // export { x } // ExportDeclaration, terminated by ASI - // fro\u006D // ExpressionStatement, the name "from" - // - // In that case let MatchOrInsertSemicolonAfterNonExpression sort out - // ASI or any necessary error. - TokenKind tt; - if (!tokenStream.getToken(&tt, TokenStream::Operand)) - return null(); + // Handle the form |export *| by adding a special export batch + // specifier to the list. + Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos()); + if (!exportSpec) + return null(); - if (tt == TOK_NAME && - tokenStream.currentToken().name() == context->names().from && - !tokenStream.currentToken().nameContainsEscape()) - { - MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); + handler.addList(kid, exportSpec); - Node moduleSpec = stringLiteral(); - if (!moduleSpec) - return null(); + MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_EXPORT_STAR); - if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream)) - return null(); + return exportFrom(begin, kid); +} - ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec); - if (!node || !pc->sc()->asModuleContext()->builder.processExportFrom(node)) - return null(); +template<> +bool +Parser<FullParseHandler>::checkLocalExportNames(ParseNode* node) +{ + // ES 2017 draft 15.2.3.1. + for (ParseNode* next = node->pn_head; next; next = next->pn_next) { + ParseNode* name = next->pn_left; + MOZ_ASSERT(name->isKind(PNK_NAME)); - return node; - } + RootedPropertyName ident(context, name->pn_atom->asPropertyName()); + if (!checkLocalExportName(ident, name->pn_pos.begin)) + return false; + } - tokenStream.ungetToken(); + return true; +} - if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream)) - return null(); - break; - } +template<> +bool +Parser<SyntaxParseHandler>::checkLocalExportNames(Node node) +{ + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); + return false; +} - case TOK_MUL: { - kid = handler.newList(PNK_EXPORT_SPEC_LIST); - if (!kid) - return null(); +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::exportClause(uint32_t begin) +{ + if (!abortIfSyntaxParser()) + return null(); - // Handle the form |export *| by adding a special export batch - // specifier to the list. - Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos()); - if (!exportSpec) - return null(); + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); - handler.addList(kid, exportSpec); + Node kid = handler.newList(PNK_EXPORT_SPEC_LIST); + if (!kid) + return null(); + TokenKind tt; + while (true) { + // Handle the forms |export {}| and |export { ..., }| (where ... is non + // empty), by escaping the loop early if the next token is }. if (!tokenStream.getToken(&tt)) return null(); - if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) { - report(ParseError, false, null(), JSMSG_FROM_AFTER_EXPORT_STAR); + + if (tt == TOK_RC) + break; + + if (!TokenKindIsPossibleIdentifierName(tt)) { + error(JSMSG_NO_BINDING_NAME); return null(); } - if (!checkUnescapedName()) + Node bindingName = newName(tokenStream.currentName()); + if (!bindingName) return null(); - MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); - - Node moduleSpec = stringLiteral(); - if (!moduleSpec) + bool foundAs; + if (!tokenStream.matchToken(&foundAs, TOK_AS)) return null(); + if (foundAs) + MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME); - if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream)) + Node exportName = newName(tokenStream.currentName()); + if (!exportName) return null(); - ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec); - if (!node || !pc->sc()->asModuleContext()->builder.processExportFrom(node)) + if (!checkExportedNameForClause(exportName)) return null(); - return node; + Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName); + if (!exportSpec) + return null(); - } + handler.addList(kid, exportSpec); - case TOK_FUNCTION: - kid = functionStmt(YieldIsKeyword, NameRequired); - if (!kid) + TokenKind next; + if (!tokenStream.getToken(&next)) return null(); - if (!checkExportedName(kid->pn_funbox->function()->explicitName())) - return null(); - break; + if (next == TOK_RC) + break; - case TOK_CLASS: { - kid = classDefinition(YieldIsKeyword, ClassStatement, NameRequired); - if (!kid) + if (next != TOK_COMMA) { + error(JSMSG_RC_AFTER_EXPORT_SPEC_LIST); return null(); + } + } - const ClassNode& cls = kid->as<ClassNode>(); - MOZ_ASSERT(cls.names()); - if (!checkExportedName(cls.names()->innerBinding()->pn_atom)) - return null(); - break; - } + // Careful! If |from| follows, even on a new line, it must start a + // FromClause: + // + // export { x } + // from "foo"; // a single ExportDeclaration + // + // But if it doesn't, we might have an ASI opportunity in Operand context: + // + // export { x } // ExportDeclaration, terminated by ASI + // fro\u006D // ExpressionStatement, the name "from" + // + // In that case let matchOrInsertSemicolonAfterNonExpression sort out ASI + // or any necessary error. + bool matched; + if (!tokenStream.matchToken(&matched, TOK_FROM, TokenStream::Operand)) + return null(); - case TOK_VAR: - kid = declarationList(YieldIsName, PNK_VAR); - if (!kid) - return null(); - if (!MatchOrInsertSemicolonAfterExpression(tokenStream)) - return null(); - if (!checkExportedNamesForDeclaration(kid)) - return null(); - break; + if (matched) + return exportFrom(begin, kid); - case TOK_DEFAULT: { - if (!tokenStream.getToken(&tt, TokenStream::Operand)) - return null(); + if (!matchOrInsertSemicolonAfterNonExpression()) + return null(); - if (!checkExportedName(context->names().default_)) - return null(); + if (!checkLocalExportNames(kid)) + return null(); - ParseNode* nameNode = nullptr; - switch (tt) { - case TOK_FUNCTION: - kid = functionStmt(YieldIsKeyword, AllowDefaultName); - if (!kid) - return null(); - break; - case TOK_CLASS: - kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName); - if (!kid) - return null(); - break; - default: { - if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) { - TokenKind nextSameLine = TOK_EOF; - if (!tokenStream.peekTokenSameLine(&nextSameLine)) - return null(); + Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); + if (!node) + return null(); - if (nextSameLine == TOK_FUNCTION) { - tokenStream.consumeKnownToken(nextSameLine); - kid = functionStmt(YieldIsName, AllowDefaultName, AsyncFunction); - if (!kid) - return null(); - break; - } - } + if (!processExport(node)) + return null(); - tokenStream.ungetToken(); - RootedPropertyName name(context, context->names().starDefaultStar); - nameNode = newName(name); - if (!nameNode) - return null(); - if (!noteDeclaredName(name, DeclarationKind::Const, pos())) - return null(); - kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited); - if (!kid) - return null(); - if (!MatchOrInsertSemicolonAfterExpression(tokenStream)) - return null(); - break; - } - } + return node; +} - ParseNode* node = handler.newExportDefaultDeclaration(kid, nameNode, - TokenPos(begin, pos().end)); - if (!node || !pc->sc()->asModuleContext()->builder.processExport(node)) - return null(); +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::exportVariableStatement(uint32_t begin) +{ + if (!abortIfSyntaxParser()) + return null(); - return node; - } + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_VAR)); - case TOK_CONST: - kid = lexicalDeclaration(YieldIsName, /* isConst = */ true); - if (!kid) - return null(); - if (!checkExportedNamesForDeclaration(kid)) - return null(); - break; + Node kid = declarationList(YieldIsName, PNK_VAR); + if (!kid) + return null(); + if (!matchOrInsertSemicolonAfterExpression()) + return null(); + if (!checkExportedNamesForDeclaration(kid)) + return null(); - case TOK_NAME: - if (tokenStream.currentName() == context->names().let) { - if (!checkUnescapedName()) - return null(); + Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); + if (!node) + return null(); - kid = lexicalDeclaration(YieldIsName, /* isConst = */ false); - if (!kid) - return null(); - if (!checkExportedNamesForDeclaration(kid)) - return null(); - break; - } - MOZ_FALLTHROUGH; + if (!processExport(node)) + return null(); - default: - report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_EXPORT); + return node; +} + +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::exportFunctionDeclaration(uint32_t begin) +{ + if (!abortIfSyntaxParser()) return null(); - } - ParseNode* node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); - if (!node || !pc->sc()->asModuleContext()->builder.processExport(node)) + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); + + Node kid = functionStmt(pos().begin, YieldIsKeyword, NameRequired); + if (!kid) + return null(); + + if (!checkExportedNameForFunction(kid)) + return null(); + + Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); + if (!node) + return null(); + + if (!processExport(node)) return null(); return node; } -template<> -SyntaxParseHandler::Node -Parser<SyntaxParseHandler>::exportDeclaration() +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::exportClassDeclaration(uint32_t begin) { - JS_ALWAYS_FALSE(abortIfSyntaxParser()); - return SyntaxParseHandler::NodeFailure; + if (!abortIfSyntaxParser()) + return null(); + + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS)); + + Node kid = classDefinition(YieldIsKeyword, ClassStatement, NameRequired); + if (!kid) + return null(); + + if (!checkExportedNameForClass(kid)) + return null(); + + Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); + if (!node) + return null(); + + if (!processExport(node)) + return null(); + + return node; +} + +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::exportLexicalDeclaration(uint32_t begin, DeclarationKind kind) +{ + if (!abortIfSyntaxParser()) + return null(); + + MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let); + MOZ_ASSERT_IF(kind == DeclarationKind::Const, tokenStream.isCurrentTokenType(TOK_CONST)); + MOZ_ASSERT_IF(kind == DeclarationKind::Let, tokenStream.isCurrentTokenType(TOK_LET)); + + Node kid = lexicalDeclaration(YieldIsName, kind); + if (!kid) + return null(); + if (!checkExportedNamesForDeclaration(kid)) + return null(); + + Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); + if (!node) + return null(); + + if (!processExport(node)) + return null(); + + return node; +} + +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::exportDefaultFunctionDeclaration(uint32_t begin, + FunctionAsyncKind asyncKind + /* = SyncFunction */) +{ + if (!abortIfSyntaxParser()) + return null(); + + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); + + Node kid = functionStmt(pos().begin, YieldIsKeyword, AllowDefaultName, asyncKind); + if (!kid) + return null(); + + Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end)); + if (!node) + return null(); + + if (!processExport(node)) + return null(); + + return node; +} + +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::exportDefaultClassDeclaration(uint32_t begin) +{ + if (!abortIfSyntaxParser()) + return null(); + + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS)); + + Node kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName); + if (!kid) + return null(); + + Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end)); + if (!node) + return null(); + + if (!processExport(node)) + return null(); + + return node; +} + +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::exportDefaultAssignExpr(uint32_t begin) +{ + if (!abortIfSyntaxParser()) + return null(); + + RootedPropertyName name(context, context->names().starDefaultStar); + Node nameNode = newName(name); + if (!nameNode) + return null(); + if (!noteDeclaredName(name, DeclarationKind::Const, pos())) + return null(); + + Node kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited); + if (!kid) + return null(); + if (!matchOrInsertSemicolonAfterExpression()) + return null(); + + Node node = handler.newExportDefaultDeclaration(kid, nameNode, TokenPos(begin, pos().end)); + if (!node) + return null(); + + if (!processExport(node)) + return null(); + + return node; +} + +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::exportDefault(uint32_t begin) +{ + if (!abortIfSyntaxParser()) + return null(); + + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_DEFAULT)); + + TokenKind tt; + if (!tokenStream.getToken(&tt, TokenStream::Operand)) + return null(); + + if (!checkExportedName(context->names().default_)) + return null(); + + switch (tt) { + case TOK_FUNCTION: + return exportDefaultFunctionDeclaration(begin); + + case TOK_ASYNC: { + TokenKind nextSameLine = TOK_EOF; + if (!tokenStream.peekTokenSameLine(&nextSameLine)) + return null(); + + if (nextSameLine == TOK_FUNCTION) { + tokenStream.consumeKnownToken(TOK_FUNCTION); + return exportDefaultFunctionDeclaration(begin, AsyncFunction); + } + + tokenStream.ungetToken(); + return exportDefaultAssignExpr(begin); + } + + case TOK_CLASS: + return exportDefaultClassDeclaration(begin); + + default: + tokenStream.ungetToken(); + return exportDefaultAssignExpr(begin); + } +} + +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::exportDeclaration() +{ + if (!abortIfSyntaxParser()) + return null(); + + MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT); + + if (!pc->atModuleLevel()) { + error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL); + return null(); + } + + uint32_t begin = pos().begin; + + TokenKind tt; + if (!tokenStream.getToken(&tt)) + return null(); + switch (tt) { + case TOK_MUL: + return exportBatch(begin); + + case TOK_LC: + return exportClause(begin); + + case TOK_VAR: + return exportVariableStatement(begin); + + case TOK_FUNCTION: + return exportFunctionDeclaration(begin); + + case TOK_CLASS: + return exportClassDeclaration(begin); + + case TOK_CONST: + return exportLexicalDeclaration(begin, DeclarationKind::Const); + + case TOK_LET: + return exportLexicalDeclaration(begin, DeclarationKind::Let); + + case TOK_DEFAULT: + return exportDefault(begin); + + default: + error(JSMSG_DECLARATION_AFTER_EXPORT); + return null(); + } } template <typename ParseHandler> @@ -5206,7 +5768,7 @@ Parser<ParseHandler>::expressionStatement(YieldHandling yieldHandling, InvokedPr /* possibleError = */ nullptr, invoked); if (!pnexpr) return null(); - if (!MatchOrInsertSemicolonAfterExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterExpression()) return null(); return handler.newExprStatement(pnexpr, pos().end); } @@ -5219,14 +5781,71 @@ Parser<ParseHandler>::consequentOrAlternative(YieldHandling yieldHandling) if (!tokenStream.peekToken(&next, TokenStream::Operand)) return null(); - if (next == TOK_FUNCTION) { - // Apply Annex B.3.4 in non-strict code to allow FunctionDeclaration as - // the consequent/alternative of an |if| or |else|. Parser::statement - // will report the strict mode error. - if (!pc->sc()->strict()) { - tokenStream.consumeKnownToken(next, TokenStream::Operand); - return functionStmt(yieldHandling, NameRequired); + // Annex B.3.4 says that unbraced FunctionDeclarations under if/else in + // non-strict code act as if they were braced: |if (x) function f() {}| + // parses as |if (x) { function f() {} }|. + // + // Careful! FunctionDeclaration doesn't include generators or async + // functions. + if (next == TOK_ASYNC) { + tokenStream.consumeKnownToken(next, TokenStream::Operand); + + // Peek only on the same line: ExpressionStatement's lookahead + // restriction is phrased as + // + // [lookahead ∉ { {, function, async [no LineTerminator here] function, class, let [ }] + // + // meaning that code like this is valid: + // + // if (true) + // async // ASI opportunity + // function clownshoes() {} + TokenKind maybeFunction; + if (!tokenStream.peekTokenSameLine(&maybeFunction)) + return null(); + + if (maybeFunction == TOK_FUNCTION) { + error(JSMSG_FORBIDDEN_AS_STATEMENT, "async function declarations"); + return null(); + } + + // Otherwise this |async| begins an ExpressionStatement. + tokenStream.ungetToken(); + } else if (next == TOK_FUNCTION) { + tokenStream.consumeKnownToken(next, TokenStream::Operand); + + // Parser::statement would handle this, but as this function handles + // every other error case, it seems best to handle this. + if (pc->sc()->strict()) { + error(JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations"); + return null(); + } + + TokenKind maybeStar; + if (!tokenStream.peekToken(&maybeStar)) + return null(); + + if (maybeStar == TOK_MUL) { + error(JSMSG_FORBIDDEN_AS_STATEMENT, "generator declarations"); + return null(); } + + ParseContext::Statement stmt(pc, StatementKind::Block); + ParseContext::Scope scope(this); + if (!scope.init(pc)) + return null(); + + TokenPos funcPos = pos(); + Node fun = functionStmt(pos().begin, yieldHandling, NameRequired); + if (!fun) + return null(); + + Node block = handler.newStatementList(funcPos); + if (!block) + return null(); + + handler.addStatementToList(block, fun); + return finishLexicalScope(scope, block); } return statement(yieldHandling); @@ -5254,7 +5873,7 @@ Parser<ParseHandler>::ifStatement(YieldHandling yieldHandling) if (!tokenStream.peekToken(&tt, TokenStream::Operand)) return null(); if (tt == TOK_SEMI) { - if (!report(ParseExtraWarning, false, null(), JSMSG_EMPTY_CONSEQUENT)) + if (!extraWarning(JSMSG_EMPTY_CONSEQUENT)) return null(); } @@ -5341,13 +5960,9 @@ Parser<ParseHandler>::matchInOrOf(bool* isForInp, bool* isForOfp) return false; *isForInp = tt == TOK_IN; - *isForOfp = tt == TOK_NAME && tokenStream.currentToken().name() == context->names().of; - if (!*isForInp && !*isForOfp) { + *isForOfp = tt == TOK_OF; + if (!*isForInp && !*isForOfp) tokenStream.ungetToken(); - } else { - if (tt == TOK_NAME && !checkUnescapedName()) - return false; - } MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp); return true; @@ -5355,37 +5970,6 @@ Parser<ParseHandler>::matchInOrOf(bool* isForInp, bool* isForOfp) template <class ParseHandler> bool -Parser<ParseHandler>::validateForInOrOfLHSExpression(Node target, PossibleError* possibleError) -{ - if (handler.isUnparenthesizedDestructuringPattern(target)) - return checkDestructuringPattern(target, Nothing(), possibleError); - - // All other permitted targets are simple. - if (!reportIfNotValidSimpleAssignmentTarget(target, ForInOrOfTarget)) - return false; - - if (handler.isPropertyAccess(target)) - return true; - - if (handler.isNameAnyParentheses(target)) { - // The arguments/eval identifiers are simple in non-strict mode code, - // but warn to discourage use nonetheless. - if (!reportIfArgumentsEvalTarget(target)) - return false; - - handler.adjustGetToSet(target); - return true; - } - - if (handler.isFunctionCall(target)) - return checkAssignmentToCall(target, JSMSG_BAD_FOR_LEFTSIDE); - - report(ParseError, false, target, JSMSG_BAD_FOR_LEFTSIDE); - return false; -} - -template <class ParseHandler> -bool Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling, ParseNodeKind* forHeadKind, Node* forInitialPart, @@ -5428,15 +6012,12 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling, if (tt == TOK_CONST) { parsingLexicalDeclaration = true; tokenStream.consumeKnownToken(tt, TokenStream::Operand); - } else if (tt == TOK_NAME && - tokenStream.nextName() == context->names().let && - !tokenStream.nextNameContainsEscape()) - { + } else if (tt == TOK_LET) { // We could have a {For,Lexical}Declaration, or we could have a // LeftHandSideExpression with lookahead restrictions so it's not // ambiguous with the former. Check for a continuation of the former // to decide which we have. - tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand); + tokenStream.consumeKnownToken(TOK_LET, TokenStream::Operand); TokenKind next; if (!tokenStream.peekToken(&next)) @@ -5464,6 +6045,10 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling, return *forInitialPart != null(); } + uint32_t exprOffset; + if (!tokenStream.peekOffset(&exprOffset, TokenStream::Operand)) + return false; + // Finally, handle for-loops that start with expressions. Pass // |InProhibited| so that |in| isn't parsed in a RelationalExpression as a // binary operator. |in| makes it a for-in loop, *not* an |in| expression. @@ -5501,14 +6086,35 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling, // // See ES6 13.7. if (isForOf && letIsIdentifier) { - report(ParseError, false, *forInitialPart, JSMSG_LET_STARTING_FOROF_LHS); + errorAt(exprOffset, JSMSG_LET_STARTING_FOROF_LHS); return false; } *forHeadKind = isForIn ? PNK_FORIN : PNK_FOROF; - if (!validateForInOrOfLHSExpression(*forInitialPart, &possibleError)) + // Verify the left-hand side expression doesn't have a forbidden form. + if (handler.isUnparenthesizedDestructuringPattern(*forInitialPart)) { + if (!possibleError.checkForDestructuringErrorOrWarning()) + return false; + } else if (handler.isNameAnyParentheses(*forInitialPart)) { + const char* chars = handler.nameIsArgumentsEvalAnyParentheses(*forInitialPart, context); + if (chars) { + // |chars| is "arguments" or "eval" here. + if (!strictModeErrorAt(exprOffset, JSMSG_BAD_STRICT_ASSIGN, chars)) + return false; + } + + handler.adjustGetToSet(*forInitialPart); + } else if (handler.isPropertyAccess(*forInitialPart)) { + // Permitted: no additional testing/fixup needed. + } else if (handler.isFunctionCall(*forInitialPart)) { + if (!strictModeErrorAt(exprOffset, JSMSG_BAD_FOR_LEFTSIDE)) + return false; + } else { + errorAt(exprOffset, JSMSG_BAD_FOR_LEFTSIDE); return false; + } + if (!possibleError.checkForExpressionError()) return false; @@ -5532,12 +6138,11 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling) if (allowsForEachIn()) { bool matched; - if (!tokenStream.matchContextualKeyword(&matched, context->names().each)) + if (!tokenStream.matchToken(&matched, TOK_EACH)) return null(); if (matched) { iflags = JSITER_FOREACH; isForEach = true; - addTelemetry(JSCompartment::DeprecatedForEach); if (!warnOnceAboutForEach()) return null(); } @@ -5593,7 +6198,7 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling) Node init = startNode; if (isForEach) { - reportWithOffset(ParseError, false, begin, JSMSG_BAD_FOR_EACH_LOOP); + errorAt(begin, JSMSG_BAD_FOR_EACH_LOOP); return null(); } @@ -5653,19 +6258,13 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling) iflags |= JSITER_ENUMERATE; } else { if (isForEach) { - report(ParseError, false, startNode, JSMSG_BAD_FOR_EACH_LOOP); + errorAt(begin, JSMSG_BAD_FOR_EACH_LOOP); return null(); } stmt.refineForKind(StatementKind::ForOfLoop); } - if (!handler.isDeclarationList(target)) { - MOZ_ASSERT(!forLoopLexicalScope); - if (!checkAndMarkAsAssignmentLhs(target, PlainAssignment)) - return null(); - } - // Parser::declaration consumed everything up to the closing ')'. That // token follows an {Assignment,}Expression, so the next token must be // consumed as if an operator continued the expression, i.e. as None. @@ -5729,7 +6328,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling) switch (tt) { case TOK_DEFAULT: if (seenDefault) { - report(ParseError, false, null(), JSMSG_TOO_MANY_DEFAULTS); + error(JSMSG_TOO_MANY_DEFAULTS); return null(); } seenDefault = true; @@ -5743,7 +6342,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling) break; default: - report(ParseError, false, null(), JSMSG_BAD_SWITCH); + error(JSMSG_BAD_SWITCH); return null(); } @@ -5762,10 +6361,8 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling) if (tt == TOK_RC || tt == TOK_CASE || tt == TOK_DEFAULT) break; if (afterReturn) { - TokenPos pos(0, 0); - if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand)) + if (!tokenStream.peekOffset(&statementBegin, TokenStream::Operand)) return null(); - statementBegin = pos.begin; } Node stmt = statementListItem(yieldHandling); if (!stmt) @@ -5773,11 +6370,9 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling) if (!warnedAboutStatementsAfterReturn) { if (afterReturn) { if (!handler.isStatementPermittedAfterReturnStatement(stmt)) { - if (!reportWithOffset(ParseWarning, false, statementBegin, - JSMSG_STMT_AFTER_RETURN)) - { + if (!warningAt(statementBegin, JSMSG_STMT_AFTER_RETURN)) return null(); - } + warnedAboutStatementsAfterReturn = true; } } else if (handler.isReturnStatement(stmt)) { @@ -5827,8 +6422,10 @@ Parser<ParseHandler>::continueStatement(YieldHandling yieldHandling) for (;;) { stmt = ParseContext::Statement::findNearest(stmt, isLoop); if (!stmt) { - report(ParseError, false, null(), - foundLoop ? JSMSG_LABEL_NOT_FOUND : JSMSG_BAD_CONTINUE); + if (foundLoop) + error(JSMSG_LABEL_NOT_FOUND); + else + errorAt(begin, JSMSG_BAD_CONTINUE); return null(); } @@ -5848,11 +6445,11 @@ Parser<ParseHandler>::continueStatement(YieldHandling yieldHandling) break; } } else if (!pc->findInnermostStatement(isLoop)) { - report(ParseError, false, null(), JSMSG_BAD_CONTINUE); + error(JSMSG_BAD_CONTINUE); return null(); } - if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterNonExpression()) return null(); return handler.newContinueStatement(label, TokenPos(begin, pos().end)); @@ -5878,7 +6475,7 @@ Parser<ParseHandler>::breakStatement(YieldHandling yieldHandling) }; if (!pc->findInnermostStatement<ParseContext::LabelStatement>(hasSameLabel)) { - report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND); + error(JSMSG_LABEL_NOT_FOUND); return null(); } } else { @@ -5887,12 +6484,12 @@ Parser<ParseHandler>::breakStatement(YieldHandling yieldHandling) }; if (!pc->findInnermostStatement(isBreakTarget)) { - report(ParseError, false, null(), JSMSG_TOUGH_BREAK); + errorAt(begin, JSMSG_TOUGH_BREAK); return null(); } } - if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterNonExpression()) return null(); return handler.newBreakStatement(label, TokenPos(begin, pos().end)); @@ -5932,10 +6529,10 @@ Parser<ParseHandler>::returnStatement(YieldHandling yieldHandling) } if (exprNode) { - if (!MatchOrInsertSemicolonAfterExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterExpression()) return null(); } else { - if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterNonExpression()) return null(); } @@ -5943,10 +6540,9 @@ Parser<ParseHandler>::returnStatement(YieldHandling yieldHandling) if (!pn) return null(); + /* Disallow "return v;" in legacy generators. */ if (pc->isLegacyGenerator() && exprNode) { - /* Disallow "return v;" in legacy generators. */ - reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN, - JSMSG_BAD_ANON_GENERATOR_RETURN); + errorAt(begin, JSMSG_BAD_GENERATOR_RETURN); return null(); } @@ -6037,13 +6633,12 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling) return null(); if (!pc->isFunctionBox()) { - report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str); + error(JSMSG_BAD_RETURN_OR_YIELD, js_yield_str); return null(); } if (pc->functionBox()->isArrow()) { - reportWithOffset(ParseError, false, begin, - JSMSG_YIELD_IN_ARROW, js_yield_str); + errorAt(begin, JSMSG_YIELD_IN_ARROW, js_yield_str); return null(); } @@ -6051,25 +6646,22 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling) pc->functionBox()->function()->isGetter() || pc->functionBox()->function()->isSetter()) { - reportWithOffset(ParseError, false, begin, - JSMSG_YIELD_IN_METHOD, js_yield_str); + errorAt(begin, JSMSG_YIELD_IN_METHOD, js_yield_str); return null(); } if (pc->funHasReturnExpr #if JS_HAS_EXPR_CLOSURES - || pc->functionBox()->function()->isExprBody() + || pc->functionBox()->isExprBody() #endif ) { /* As in Python (see PEP-255), disallow return v; in generators. */ - reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN, - JSMSG_BAD_ANON_GENERATOR_RETURN); + errorAt(begin, JSMSG_BAD_GENERATOR_RETURN); return null(); } pc->functionBox()->setGeneratorKind(LegacyGenerator); - addTelemetry(JSCompartment::DeprecatedLegacyGenerator); MOZ_FALLTHROUGH; @@ -6119,14 +6711,13 @@ Parser<ParseHandler>::withStatement(YieldHandling yieldHandling) MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH)); uint32_t begin = pos().begin; - // In most cases, we want the constructs forbidden in strict mode code to be - // a subset of those that JSOPTION_EXTRA_WARNINGS warns about, and we should - // use reportStrictModeError. However, 'with' is the sole instance of a - // construct that is forbidden in strict mode code, but doesn't even merit a - // warning under JSOPTION_EXTRA_WARNINGS. See + // Usually we want the constructs forbidden in strict mode code to be a + // subset of those that ContextOptions::extraWarnings() warns about, and we + // use strictModeError directly. But while 'with' is forbidden in strict + // mode code, it doesn't even merit a warning in non-strict code. See // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1. if (pc->sc()->strict()) { - if (!report(ParseStrictError, true, null(), JSMSG_STRICT_CODE_WITH)) + if (!strictModeError(JSMSG_STRICT_CODE_WITH)) return null(); } @@ -6165,7 +6756,7 @@ Parser<ParseHandler>::labeledItem(YieldHandling yieldHandling) // GeneratorDeclaration is only matched by HoistableDeclaration in // StatementListItem, so generators can't be inside labels. if (next == TOK_MUL) { - report(ParseError, false, null(), JSMSG_GENERATOR_LABEL); + error(JSMSG_GENERATOR_LABEL); return null(); } @@ -6173,11 +6764,11 @@ Parser<ParseHandler>::labeledItem(YieldHandling yieldHandling) // is ever matched. Per Annex B.3.2 that modifies this text, this // applies only to strict mode code. if (pc->sc()->strict()) { - report(ParseError, false, null(), JSMSG_FUNCTION_LABEL); + error(JSMSG_FUNCTION_LABEL); return null(); } - return functionStmt(yieldHandling, NameRequired); + return functionStmt(pos().begin, yieldHandling, NameRequired); } tokenStream.ungetToken(); @@ -6196,13 +6787,13 @@ Parser<ParseHandler>::labeledStatement(YieldHandling yieldHandling) return stmt->label() == label; }; + uint32_t begin = pos().begin; + if (pc->findInnermostStatement<ParseContext::LabelStatement>(hasSameLabel)) { - report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL); + errorAt(begin, JSMSG_DUPLICATE_LABEL); return null(); } - uint32_t begin = pos().begin; - tokenStream.consumeKnownToken(TOK_COLON); /* Push a label struct and parse the statement. */ @@ -6226,11 +6817,11 @@ Parser<ParseHandler>::throwStatement(YieldHandling yieldHandling) if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand)) return null(); if (tt == TOK_EOF || tt == TOK_SEMI || tt == TOK_RC) { - report(ParseError, false, null(), JSMSG_MISSING_EXPR_AFTER_THROW); + error(JSMSG_MISSING_EXPR_AFTER_THROW); return null(); } if (tt == TOK_EOL) { - report(ParseError, false, null(), JSMSG_LINE_BREAK_AFTER_THROW); + error(JSMSG_LINE_BREAK_AFTER_THROW); return null(); } @@ -6238,7 +6829,7 @@ Parser<ParseHandler>::throwStatement(YieldHandling yieldHandling) if (!throwExpr) return null(); - if (!MatchOrInsertSemicolonAfterExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterExpression()) return null(); return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end)); @@ -6258,12 +6849,12 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling) * kid3 is the finally statement * * catch nodes are ternary. - * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC) + * kid1 is the lvalue (possible identifier, TOK_LB, or TOK_LC) * kid2 is the catch guard or null if no guard * kid3 is the catch block * * catch lvalue nodes are either: - * TOK_NAME for a single identifier + * a single identifier * TOK_RB or TOK_RC for a destructuring left-hand side * * finally nodes are TOK_LC statement lists. @@ -6273,6 +6864,8 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling) { MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY); + uint32_t openedPos = pos().begin; + ParseContext::Statement stmt(pc, StatementKind::Try); ParseContext::Scope scope(this); if (!scope.init(pc)) @@ -6286,7 +6879,9 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling) if (!innerBlock) return null(); - MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_TRY); + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand, + reportMissingClosing(JSMSG_CURLY_AFTER_TRY, + JSMSG_CURLY_OPENED, openedPos)); } bool hasUnconditionalCatch = false; @@ -6304,7 +6899,7 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling) /* Check for another catch after unconditional catch. */ if (hasUnconditionalCatch) { - report(ParseError, false, null(), JSMSG_CATCH_AFTER_GENERAL); + error(JSMSG_CATCH_AFTER_GENERAL); return null(); } @@ -6338,22 +6933,18 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling) return null(); break; - case TOK_NAME: - case TOK_YIELD: { - RootedPropertyName param(context, bindingIdentifier(yieldHandling)); - if (!param) + default: { + if (!TokenKindIsPossibleIdentifierName(tt)) { + error(JSMSG_CATCH_IDENTIFIER); return null(); - catchName = newName(param); + } + + catchName = bindingIdentifier(DeclarationKind::SimpleCatchParameter, + yieldHandling); if (!catchName) return null(); - if (!noteDeclaredName(param, DeclarationKind::SimpleCatchParameter, pos())) - return null(); break; } - - default: - report(ParseError, false, null(), JSMSG_CATCH_IDENTIFIER); - return null(); } Node catchGuard = null(); @@ -6402,6 +6993,8 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling) if (tt == TOK_FINALLY) { MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY); + uint32_t openedPos = pos().begin; + ParseContext::Statement stmt(pc, StatementKind::Finally); ParseContext::Scope scope(this); if (!scope.init(pc)) @@ -6415,12 +7008,14 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling) if (!finallyBlock) return null(); - MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_FINALLY); + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand, + reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY, + JSMSG_CURLY_OPENED, openedPos)); } else { tokenStream.ungetToken(); } if (!catchList && !finallyBlock) { - report(ParseError, false, null(), JSMSG_CATCH_OR_FINALLY); + error(JSMSG_CATCH_OR_FINALLY); return null(); } @@ -6432,6 +7027,8 @@ typename ParseHandler::Node Parser<ParseHandler>::catchBlockStatement(YieldHandling yieldHandling, ParseContext::Scope& catchParamScope) { + uint32_t openedPos = pos().begin; + ParseContext::Statement stmt(pc, StatementKind::Block); // ES 13.15.7 CatchClauseEvaluation @@ -6451,7 +7048,9 @@ Parser<ParseHandler>::catchBlockStatement(YieldHandling yieldHandling, if (!list) return null(); - MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_CATCH); + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand, + reportMissingClosing(JSMSG_CURLY_AFTER_CATCH, + JSMSG_CURLY_OPENED, openedPos)); // The catch parameter names are not bound in the body scope, so remove // them before generating bindings. @@ -6465,7 +7064,7 @@ Parser<ParseHandler>::debuggerStatement() { TokenPos p; p.begin = pos().begin; - if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterNonExpression()) return null(); p.end = pos().end; @@ -6497,49 +7096,6 @@ JSOpFromPropertyType(PropertyType propType) } } -static FunctionSyntaxKind -FunctionSyntaxKindFromPropertyType(PropertyType propType) -{ - switch (propType) { - case PropertyType::Getter: - return Getter; - case PropertyType::GetterNoExpressionClosure: - return GetterNoExpressionClosure; - case PropertyType::Setter: - return Setter; - case PropertyType::SetterNoExpressionClosure: - return SetterNoExpressionClosure; - case PropertyType::Method: - case PropertyType::GeneratorMethod: - case PropertyType::AsyncMethod: - return Method; - case PropertyType::Constructor: - return ClassConstructor; - case PropertyType::DerivedConstructor: - return DerivedClassConstructor; - default: - MOZ_CRASH("unexpected property type"); - } -} - -static GeneratorKind -GeneratorKindFromPropertyType(PropertyType propType) -{ - if (propType == PropertyType::GeneratorMethod) - return StarGenerator; - if (propType == PropertyType::AsyncMethod) - return StarGenerator; - return NotGenerator; -} - -static FunctionAsyncKind -AsyncKindFromPropertyType(PropertyType propType) -{ - if (propType == PropertyType::AsyncMethod) - return AsyncFunction; - return SyncFunction; -} - template <typename ParseHandler> typename ParseHandler::Node Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, @@ -6548,6 +7104,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS)); + uint32_t classStartOffset = pos().begin; bool savedStrictness = setLocalStrictMode(true); TokenKind tt; @@ -6555,7 +7112,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, return null(); RootedPropertyName name(context); - if (tt == TOK_NAME || tt == TOK_YIELD) { + if (TokenKindIsPossibleIdentifier(tt)) { name = bindingIdentifier(yieldHandling); if (!name) return null(); @@ -6565,7 +7122,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, tokenStream.ungetToken(); } else { // Class statements must have a bound name - report(ParseError, false, null(), JSMSG_UNNAMED_CLASS_STMT); + error(JSMSG_UNNAMED_CLASS_STMT); return null(); } } else { @@ -6573,16 +7130,20 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, tokenStream.ungetToken(); } + // Push a ParseContext::ClassStatement to keep track of the constructor + // funbox. + ParseContext::ClassStatement classStmt(pc); + RootedAtom propAtom(context); // A named class creates a new lexical scope with a const binding of the - // class name. - Maybe<ParseContext::Statement> classStmt; - Maybe<ParseContext::Scope> classScope; + // class name for the "inner name". + Maybe<ParseContext::Statement> innerScopeStmt; + Maybe<ParseContext::Scope> innerScope; if (name) { - classStmt.emplace(pc, StatementKind::Block); - classScope.emplace(this); - if (!classScope->init(pc)) + innerScopeStmt.emplace(pc, StatementKind::Block); + innerScope.emplace(this); + if (!innerScope->init(pc)) return null(); } @@ -6609,10 +7170,10 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, if (!classMethods) return null(); - bool seenConstructor = false; + Maybe<DeclarationKind> declKind = Nothing(); for (;;) { TokenKind tt; - if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) + if (!tokenStream.getToken(&tt)) return null(); if (tt == TOK_RC) break; @@ -6621,31 +7182,30 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, continue; bool isStatic = false; - if (tt == TOK_NAME && tokenStream.currentName() == context->names().static_) { - if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName)) + if (tt == TOK_STATIC) { + if (!tokenStream.peekToken(&tt)) return null(); if (tt == TOK_RC) { - tokenStream.consumeKnownToken(tt, TokenStream::KeywordIsName); - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "property name", TokenKindToDesc(tt)); + tokenStream.consumeKnownToken(tt); + error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt)); return null(); } if (tt != TOK_LP) { - if (!checkUnescapedName()) - return null(); - isStatic = true; } else { - tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName); tokenStream.ungetToken(); } } else { tokenStream.ungetToken(); } + uint32_t nameOffset; + if (!tokenStream.peekOffset(&nameOffset)) + return null(); + PropertyType propType; - Node propName = propertyName(yieldHandling, classMethods, &propType, &propAtom); + Node propName = propertyName(yieldHandling, declKind, classMethods, &propType, &propAtom); if (!propName) return null(); @@ -6654,7 +7214,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, propType != PropertyType::AsyncMethod && propType != PropertyType::Constructor && propType != PropertyType::DerivedConstructor) { - report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF); + errorAt(nameOffset, JSMSG_BAD_METHOD_DEF); return null(); } @@ -6662,19 +7222,20 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, propType = PropertyType::GetterNoExpressionClosure; if (propType == PropertyType::Setter) propType = PropertyType::SetterNoExpressionClosure; - if (!isStatic && propAtom == context->names().constructor) { + + bool isConstructor = !isStatic && propAtom == context->names().constructor; + if (isConstructor) { if (propType != PropertyType::Method) { - report(ParseError, false, propName, JSMSG_BAD_METHOD_DEF); + errorAt(nameOffset, JSMSG_BAD_METHOD_DEF); return null(); } - if (seenConstructor) { - report(ParseError, false, propName, JSMSG_DUPLICATE_PROPERTY, "constructor"); + if (classStmt.constructorBox) { + errorAt(nameOffset, JSMSG_DUPLICATE_PROPERTY, "constructor"); return null(); } - seenConstructor = true; propType = hasHeritage ? PropertyType::DerivedConstructor : PropertyType::Constructor; } else if (isStatic && propAtom == context->names().prototype) { - report(ParseError, false, propName, JSMSG_BAD_METHOD_DEF); + errorAt(nameOffset, JSMSG_BAD_METHOD_DEF); return null(); } @@ -6696,7 +7257,12 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, if (!tokenStream.isCurrentTokenType(TOK_RB)) funName = propAtom; } - Node fn = methodDefinition(propType, funName); + + // Calling toString on constructors need to return the source text for + // the entire class. The end offset is unknown at this point in + // parsing and will be amended when class parsing finishes below. + Node fn = methodDefinition(isConstructor ? classStartOffset : nameOffset, + propType, funName); if (!fn) return null(); @@ -6707,6 +7273,15 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, return null(); } + // Amend the toStringEnd offset for the constructor now that we've + // finished parsing the class. + uint32_t classEndOffset = pos().end; + if (FunctionBox* ctorbox = classStmt.constructorBox) { + if (ctorbox->function()->isInterpretedLazy()) + ctorbox->function()->lazyScript()->setToStringEnd(classEndOffset); + ctorbox->toStringEnd = classEndOffset; + } + Node nameNode = null(); Node methodsOrBlock = classMethods; if (name) { @@ -6718,15 +7293,15 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, if (!innerName) return null(); - Node classBlock = finishLexicalScope(*classScope, classMethods); + Node classBlock = finishLexicalScope(*innerScope, classMethods); if (!classBlock) return null(); methodsOrBlock = classBlock; // Pop the inner scope. - classScope.reset(); - classStmt.reset(); + innerScope.reset(); + innerScopeStmt.reset(); Node outerName = null(); if (classContext == ClassStatement) { @@ -6746,16 +7321,15 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling, MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness)); - return handler.newClass(nameNode, classHeritage, methodsOrBlock); + return handler.newClass(nameNode, classHeritage, methodsOrBlock, + TokenPos(classStartOffset, classEndOffset)); } template <class ParseHandler> bool Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling) { - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME)); - MOZ_ASSERT(tokenStream.currentName() == context->names().let); - MOZ_ASSERT(!tokenStream.currentToken().nameContainsEscape()); + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LET)); #ifdef DEBUG TokenKind verify; @@ -6767,18 +7341,19 @@ Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHand if (next == TOK_LB || next == TOK_LC) return true; - // Otherwise a let declaration must have a name. - if (next == TOK_NAME) { - if (tokenStream.nextName() == context->names().yield) { - MOZ_ASSERT(tokenStream.nextNameContainsEscape(), - "token stream should interpret unescaped 'yield' as TOK_YIELD"); - - // Same as |next == TOK_YIELD|. - return yieldHandling == YieldIsName; - } + // If we have the name "yield", the grammar parameter exactly states + // whether this is okay. (This wasn't true for SpiderMonkey's ancient + // legacy generator syntax, but that's dead now.) If YieldIsName, + // declaration-parsing code will (if necessary) enforce a strict mode + // restriction on defining "yield". If YieldIsKeyword, consider this the + // end of the declaration, in case ASI induces a semicolon that makes the + // "yield" valid. + if (next == TOK_YIELD) + return yieldHandling == YieldIsName; - // One non-"yield" TOK_NAME edge case deserves special comment. - // Consider this: + // Otherwise a let declaration must have a name. + if (TokenKindIsPossibleIdentifier(next)) { + // A "let" edge case deserves special comment. Consider this: // // let // not an ASI opportunity // let; @@ -6791,16 +7366,6 @@ Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHand return true; } - // If we have the name "yield", the grammar parameter exactly states - // whether this is okay. (This wasn't true for SpiderMonkey's ancient - // legacy generator syntax, but that's dead now.) If YieldIsName, - // declaration-parsing code will (if necessary) enforce a strict mode - // restriction on defining "yield". If YieldIsKeyword, consider this the - // end of the declaration, in case ASI induces a semicolon that makes the - // "yield" valid. - if (next == TOK_YIELD) - return yieldHandling == YieldIsName; - // Otherwise not a let declaration. return false; } @@ -6812,7 +7377,7 @@ Parser<ParseHandler>::variableStatement(YieldHandling yieldHandling) Node vars = declarationList(yieldHandling, PNK_VAR); if (!vars) return null(); - if (!MatchOrInsertSemicolonAfterExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterExpression()) return null(); return vars; } @@ -6863,16 +7428,21 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling) return expressionStatement(yieldHandling); } - case TOK_NAME: { + default: { + // Avoid getting next token with None. + if (tt == TOK_AWAIT && pc->isAsync()) + return expressionStatement(yieldHandling); + + if (!TokenKindIsPossibleIdentifier(tt)) + return expressionStatement(yieldHandling); + TokenKind next; if (!tokenStream.peekToken(&next)) return null(); // |let| here can only be an Identifier, not a declaration. Give nicer // errors for declaration-looking typos. - if (!tokenStream.currentToken().nameContainsEscape() && - tokenStream.currentName() == context->names().let) - { + if (tt == TOK_LET) { bool forbiddenLetDeclaration = false; if (pc->sc()->strict() || versionNumber() >= JSVERSION_1_7) { @@ -6882,7 +7452,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling) } else if (next == TOK_LB) { // Enforce ExpressionStatement's 'let [' lookahead restriction. forbiddenLetDeclaration = true; - } else if (next == TOK_LC || next == TOK_NAME) { + } else if (next == TOK_LC || TokenKindIsPossibleIdentifier(next)) { // 'let {' and 'let foo' aren't completely forbidden, if ASI // causes 'let' to be the entire Statement. But if they're // same-line, we can aggressively give a better error message. @@ -6893,7 +7463,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling) if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); - MOZ_ASSERT(nextSameLine == TOK_NAME || + MOZ_ASSERT(TokenKindIsPossibleIdentifier(nextSameLine) || nextSameLine == TOK_LC || nextSameLine == TOK_EOL); @@ -6901,8 +7471,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling) } if (forbiddenLetDeclaration) { - report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, - "lexical declarations"); + error(JSMSG_FORBIDDEN_AS_STATEMENT, "lexical declarations"); return null(); } } @@ -6918,9 +7487,6 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling) case TOK_NEW: return expressionStatement(yieldHandling, PredictInvoked); - default: - return expressionStatement(yieldHandling); - // IfStatement[?Yield, ?Return] case TOK_IF: return ifStatement(yieldHandling); @@ -6956,7 +7522,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling) // detected this way, so don't bother passing around an extra parameter // everywhere. if (!pc->isFunctionBox()) { - report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str); + error(JSMSG_BAD_RETURN_OR_YIELD, js_return_str); return null(); } return returnStatement(yieldHandling); @@ -6966,7 +7532,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling) return withStatement(yieldHandling); // LabelledStatement[?Yield, ?Return] - // This is really handled by TOK_NAME and TOK_YIELD cases above. + // This is really handled by default and TOK_YIELD cases above. // ThrowStatement[?Yield] case TOK_THROW: @@ -6984,12 +7550,12 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling) // statement of |if| or |else|, but Parser::consequentOrAlternative // handles that). case TOK_FUNCTION: - report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations"); + error(JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations"); return null(); // |class| is also forbidden by lookahead restriction. case TOK_CLASS: - report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "classes"); + error(JSMSG_FORBIDDEN_AS_STATEMENT, "classes"); return null(); // ImportDeclaration (only inside modules) @@ -7003,11 +7569,11 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling) // Miscellaneous error cases arguably better caught here than elsewhere. case TOK_CATCH: - report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY); + error(JSMSG_CATCH_WITHOUT_TRY); return null(); case TOK_FINALLY: - report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY); + error(JSMSG_FINALLY_WITHOUT_TRY); return null(); // NOTE: default case handled in the ExpressionStatement section. @@ -7048,7 +7614,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling, if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) { if (!abortIfSyntaxParser()) return null(); - if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL)) + if (!warning(JSMSG_USE_ASM_DIRECTIVE_FAIL)) return null(); } return expressionStatement(yieldHandling); @@ -7072,25 +7638,29 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling, return expressionStatement(yieldHandling); } - case TOK_NAME: { + default: { + // Avoid getting next token with None. + if (tt == TOK_AWAIT && pc->isAsync()) + return expressionStatement(yieldHandling); + + if (!TokenKindIsPossibleIdentifier(tt)) + return expressionStatement(yieldHandling); + TokenKind next; if (!tokenStream.peekToken(&next)) return null(); - if (!tokenStream.currentToken().nameContainsEscape() && - tokenStream.currentName() == context->names().let && - nextTokenContinuesLetDeclaration(next, yieldHandling)) - { - return lexicalDeclaration(yieldHandling, /* isConst = */ false); - } + if (tt == TOK_LET && nextTokenContinuesLetDeclaration(next, yieldHandling)) + return lexicalDeclaration(yieldHandling, DeclarationKind::Let); - if (tokenStream.currentName() == context->names().async) { + if (tt == TOK_ASYNC) { TokenKind nextSameLine = TOK_EOF; if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); if (nextSameLine == TOK_FUNCTION) { + uint32_t toStringStart = pos().begin; tokenStream.consumeKnownToken(TOK_FUNCTION); - return functionStmt(yieldHandling, NameRequired, AsyncFunction); + return functionStmt(toStringStart, yieldHandling, NameRequired, AsyncFunction); } } @@ -7103,9 +7673,6 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling, case TOK_NEW: return expressionStatement(yieldHandling, PredictInvoked); - default: - return expressionStatement(yieldHandling); - // IfStatement[?Yield, ?Return] case TOK_IF: return ifStatement(yieldHandling); @@ -7141,7 +7708,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling, // detected this way, so don't bother passing around an extra parameter // everywhere. if (!pc->isFunctionBox()) { - report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str); + error(JSMSG_BAD_RETURN_OR_YIELD, js_return_str); return null(); } return returnStatement(yieldHandling); @@ -7151,7 +7718,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling, return withStatement(yieldHandling); // LabelledStatement[?Yield, ?Return] - // This is really handled by TOK_NAME and TOK_YIELD cases above. + // This is really handled by default and TOK_YIELD cases above. // ThrowStatement[?Yield] case TOK_THROW: @@ -7169,7 +7736,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling, // HoistableDeclaration[?Yield, ~Default] case TOK_FUNCTION: - return functionStmt(yieldHandling, NameRequired); + return functionStmt(pos().begin, yieldHandling, NameRequired); // ClassDeclaration[?Yield, ~Default] case TOK_CLASS: @@ -7180,7 +7747,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling, case TOK_CONST: // [In] is the default behavior, because for-loops specially parse // their heads to handle |in| in this situation. - return lexicalDeclaration(yieldHandling, /* isConst = */ true); + return lexicalDeclaration(yieldHandling, DeclarationKind::Const); // ImportDeclaration (only inside modules) case TOK_IMPORT: @@ -7193,11 +7760,11 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling, // Miscellaneous error cases arguably better caught here than elsewhere. case TOK_CATCH: - report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY); + error(JSMSG_CATCH_WITHOUT_TRY); return null(); case TOK_FINALLY: - report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY); + error(JSMSG_FINALLY_WITHOUT_TRY); return null(); // NOTE: default case handled in the ExpressionStatement section. @@ -7242,8 +7809,7 @@ Parser<ParseHandler>::expr(InHandling inHandling, YieldHandling yieldHandling, if (!tokenStream.peekToken(&tt)) return null(); if (tt != TOK_ARROW) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "expression", TokenKindToDesc(TOK_RP)); + error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TOK_RP)); return null(); } @@ -7400,7 +7966,7 @@ Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling return null(); // Report an error for unary expressions on the LHS of **. if (tok == TOK_POW && handler.isUnparenthesizedUnaryExpression(pn)) { - report(ParseError, false, null(), JSMSG_BAD_POW_LEFTSIDE); + error(JSMSG_BAD_POW_LEFTSIDE); return null(); } pnk = BinaryOpTokenKindToParseNodeKind(tok); @@ -7471,65 +8037,6 @@ Parser<ParseHandler>::condExpr1(InHandling inHandling, YieldHandling yieldHandli } template <typename ParseHandler> -bool -Parser<ParseHandler>::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor, - PossibleError* possibleError) -{ - MOZ_ASSERT(flavor != KeyedDestructuringAssignment, - "destructuring must use special checking/marking code, not " - "this method"); - - if (handler.isUnparenthesizedDestructuringPattern(target)) { - if (flavor == CompoundAssignment) { - report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS); - return false; - } - - return checkDestructuringPattern(target, Nothing(), possibleError); - } - - // All other permitted targets are simple. - if (!reportIfNotValidSimpleAssignmentTarget(target, flavor)) - return false; - - if (handler.isPropertyAccess(target)) - return true; - - if (handler.isNameAnyParentheses(target)) { - // The arguments/eval identifiers are simple in non-strict mode code, - // but warn to discourage use nonetheless. - if (!reportIfArgumentsEvalTarget(target)) - return false; - - handler.adjustGetToSet(target); - return true; - } - - MOZ_ASSERT(handler.isFunctionCall(target)); - return checkAssignmentToCall(target, JSMSG_BAD_LEFTSIDE_OF_ASS); -} - -class AutoClearInDestructuringDecl -{ - ParseContext* pc_; - Maybe<DeclarationKind> saved_; - - public: - explicit AutoClearInDestructuringDecl(ParseContext* pc) - : pc_(pc), - saved_(pc->inDestructuringDecl) - { - pc->inDestructuringDecl = Nothing(); - if (saved_ && *saved_ == DeclarationKind::FormalParameter) - pc->functionBox()->hasParameterExprs = true; - } - - ~AutoClearInDestructuringDecl() { - pc_->inDestructuringDecl = saved_; - } -}; - -template <typename ParseHandler> typename ParseHandler::Node Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, @@ -7553,8 +8060,13 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); + TokenPos exprPos = pos(); + bool endsExpr; + // This only handles identifiers that *never* have special meaning anywhere + // in the language. Contextual keywords, reserved words in strict mode, + // and other hard cases are handled outside this fast path. if (tt == TOK_NAME) { if (!tokenStream.nextTokenEndsExpr(&endsExpr)) return null(); @@ -7585,12 +8097,12 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl return yieldExpression(inHandling); bool maybeAsyncArrow = false; - if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) { + if (tt == TOK_ASYNC) { TokenKind nextSameLine = TOK_EOF; if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); - if (nextSameLine == TOK_NAME || nextSameLine == TOK_YIELD) + if (TokenKindIsPossibleIdentifier(nextSameLine)) maybeAsyncArrow = true; } @@ -7604,13 +8116,12 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl PossibleError possibleErrorInner(*this); Node lhs; if (maybeAsyncArrow) { - tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand); - MOZ_ASSERT(tokenStream.currentName() == context->names().async); + tokenStream.consumeKnownToken(TOK_ASYNC, TokenStream::Operand); TokenKind tt; if (!tokenStream.getToken(&tt)) return null(); - MOZ_ASSERT(tt == TOK_NAME || tt == TOK_YIELD); + MOZ_ASSERT(TokenKindIsPossibleIdentifier(tt)); // Check yield validity here. RootedPropertyName name(context, bindingIdentifier(yieldHandling)); @@ -7620,8 +8131,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl if (!tokenStream.getToken(&tt)) return null(); if (tt != TOK_ARROW) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "'=>' after argument list", TokenKindToDesc(tt)); + error(JSMSG_UNEXPECTED_TOKEN, "'=>' after argument list", TokenKindToDesc(tt)); return null(); } @@ -7659,7 +8169,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl MOZ_ASSERT(next == TOK_ARROW || next == TOK_EOL); if (next != TOK_ARROW) { - report(ParseError, false, null(), JSMSG_LINE_BREAK_BEFORE_ARROW); + error(JSMSG_LINE_BREAK_BEFORE_ARROW); return null(); } tokenStream.consumeKnownToken(TOK_ARROW); @@ -7672,32 +8182,34 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl tokenStream.seek(start); - if (!tokenStream.peekToken(&next, TokenStream::Operand)) + if (!tokenStream.getToken(&next, TokenStream::Operand)) return null(); + uint32_t toStringStart = pos().begin; + tokenStream.ungetToken(); GeneratorKind generatorKind = NotGenerator; FunctionAsyncKind asyncKind = SyncFunction; - if (next == TOK_NAME) { + if (next == TOK_ASYNC) { tokenStream.consumeKnownToken(next, TokenStream::Operand); - if (tokenStream.currentName() == context->names().async) { - TokenKind nextSameLine = TOK_EOF; - if (!tokenStream.peekTokenSameLine(&nextSameLine)) - return null(); + TokenKind nextSameLine = TOK_EOF; + if (!tokenStream.peekTokenSameLine(&nextSameLine)) + return null(); - if (nextSameLine == TOK_ARROW) { - tokenStream.ungetToken(); - } else { - generatorKind = StarGenerator; - asyncKind = AsyncFunction; - } - } else { + if (nextSameLine == TOK_ARROW) { tokenStream.ungetToken(); + } else { + generatorKind = StarGenerator; + asyncKind = AsyncFunction; } } - Node arrowFunc = functionDefinition(inHandling, yieldHandling, nullptr, + Node pn = handler.newArrowFunction(); + if (!pn) + return null(); + + Node arrowFunc = functionDefinition(toStringStart, pn, inHandling, yieldHandling, nullptr, Arrow, generatorKind, asyncKind); if (!arrowFunc) return null(); @@ -7747,19 +8259,42 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl return lhs; } - AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment; - if (!checkAndMarkAsAssignmentLhs(lhs, flavor, &possibleErrorInner)) + // Verify the left-hand side expression doesn't have a forbidden form. + if (handler.isUnparenthesizedDestructuringPattern(lhs)) { + if (kind != PNK_ASSIGN) { + error(JSMSG_BAD_DESTRUCT_ASS); + return null(); + } + + if (!possibleErrorInner.checkForDestructuringErrorOrWarning()) + return null(); + } else if (handler.isNameAnyParentheses(lhs)) { + if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) { + // |chars| is "arguments" or "eval" here. + if (!strictModeErrorAt(exprPos.begin, JSMSG_BAD_STRICT_ASSIGN, chars)) + return null(); + } + + handler.adjustGetToSet(lhs); + } else if (handler.isPropertyAccess(lhs)) { + // Permitted: no additional testing/fixup needed. + } else if (handler.isFunctionCall(lhs)) { + if (!strictModeErrorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS)) + return null(); + + if (possibleError) + possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET); + } else { + errorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS); return null(); + } + if (!possibleErrorInner.checkForExpressionError()) return null(); - Node rhs; - { - AutoClearInDestructuringDecl autoClear(pc); - rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited); - if (!rhs) - return null(); - } + Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited); + if (!rhs) + return null(); if (kind == PNK_ASSIGN) handler.checkAndSetIsDirectRHSAnonFunction(rhs); @@ -7796,91 +8331,29 @@ Parser<ParseHandler>::isValidSimpleAssignmentTarget(Node node, template <typename ParseHandler> bool -Parser<ParseHandler>::reportIfArgumentsEvalTarget(Node nameNode) -{ - const char* chars = handler.nameIsArgumentsEvalAnyParentheses(nameNode, context); - if (!chars) - return true; - - if (!report(ParseStrictError, pc->sc()->strict(), nameNode, JSMSG_BAD_STRICT_ASSIGN, chars)) - return false; - - MOZ_ASSERT(!pc->sc()->strict(), - "an error should have been reported if this was strict mode " - "code"); - return true; -} - -template <typename ParseHandler> -bool -Parser<ParseHandler>::reportIfNotValidSimpleAssignmentTarget(Node target, AssignmentFlavor flavor) +Parser<ParseHandler>::checkIncDecOperand(Node operand, uint32_t operandOffset) { - FunctionCallBehavior behavior = flavor == KeyedDestructuringAssignment - ? ForbidAssignmentToFunctionCalls - : PermitAssignmentToFunctionCalls; - if (isValidSimpleAssignmentTarget(target, behavior)) - return true; - - if (handler.isNameAnyParentheses(target)) { - // Use a special error if the target is arguments/eval. This ensures - // targeting these names is consistently a SyntaxError (which error numbers - // below don't guarantee) while giving us a nicer error message. - if (!reportIfArgumentsEvalTarget(target)) + if (handler.isNameAnyParentheses(operand)) { + if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(operand, context)) { + if (!strictModeErrorAt(operandOffset, JSMSG_BAD_STRICT_ASSIGN, chars)) + return false; + } + } else if (handler.isPropertyAccess(operand)) { + // Permitted: no additional testing/fixup needed. + } else if (handler.isFunctionCall(operand)) { + // Assignment to function calls is forbidden in ES6. We're still + // somewhat concerned about sites using this in dead code, so forbid it + // only in strict mode code (or if the werror option has been set), and + // otherwise warn. + if (!strictModeErrorAt(operandOffset, JSMSG_BAD_INCOP_OPERAND)) return false; - } - - unsigned errnum = 0; - const char* extra = nullptr; - - switch (flavor) { - case IncrementAssignment: - errnum = JSMSG_BAD_OPERAND; - extra = "increment"; - break; - - case DecrementAssignment: - errnum = JSMSG_BAD_OPERAND; - extra = "decrement"; - break; - - case KeyedDestructuringAssignment: - errnum = JSMSG_BAD_DESTRUCT_TARGET; - break; - - case PlainAssignment: - case CompoundAssignment: - errnum = JSMSG_BAD_LEFTSIDE_OF_ASS; - break; - - case ForInOrOfTarget: - errnum = JSMSG_BAD_FOR_LEFTSIDE; - break; - } - - report(ParseError, pc->sc()->strict(), target, errnum, extra); - return false; -} - -template <typename ParseHandler> -bool -Parser<ParseHandler>::checkAndMarkAsIncOperand(Node target, AssignmentFlavor flavor) -{ - MOZ_ASSERT(flavor == IncrementAssignment || flavor == DecrementAssignment); - - // Check. - if (!reportIfNotValidSimpleAssignmentTarget(target, flavor)) + } else { + errorAt(operandOffset, JSMSG_BAD_INCOP_OPERAND); return false; - - // Mark. - if (handler.isNameAnyParentheses(target)) { - // Assignment to arguments/eval is allowed outside strict mode code, - // but it's dodgy. Report a strict warning (error, if werror was set). - if (!reportIfArgumentsEvalTarget(target)) - return false; - } else if (handler.isFunctionCall(target)) { - if (!checkAssignmentToCall(target, JSMSG_BAD_INCOP_OPERAND)) - return false; } + + MOZ_ASSERT(isValidSimpleAssignmentTarget(operand, PermitAssignmentToFunctionCalls), + "inconsistent increment/decrement operand validation"); return true; } @@ -7944,18 +8417,21 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t TokenKind tt2; if (!tokenStream.getToken(&tt2, TokenStream::Operand)) return null(); - Node pn2 = memberExpr(yieldHandling, TripledotProhibited, tt2); - if (!pn2) - return null(); - AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment; - if (!checkAndMarkAsIncOperand(pn2, flavor)) + + uint32_t operandOffset = pos().begin; + Node operand = memberExpr(yieldHandling, TripledotProhibited, tt2); + if (!operand || !checkIncDecOperand(operand, operandOffset)) return null(); + return handler.newUpdate((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT, - begin, - pn2); + begin, operand); } case TOK_DELETE: { + uint32_t exprOffset; + if (!tokenStream.peekOffset(&exprOffset, TokenStream::Operand)) + return null(); + Node expr = unaryExpr(yieldHandling, TripledotProhibited); if (!expr) return null(); @@ -7963,8 +8439,9 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t // Per spec, deleting any unary expression is valid -- it simply // returns true -- except for one case that is illegal in strict mode. if (handler.isNameAnyParentheses(expr)) { - if (!report(ParseStrictError, pc->sc()->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND)) + if (!strictModeErrorAt(exprOffset, JSMSG_DEPRECATED_DELETE_OPERAND)) return null(); + pc->sc()->setBindingsAccessedDynamically(); } @@ -7972,39 +8449,35 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t } case TOK_AWAIT: { - if (!pc->isAsync()) { - // TOK_AWAIT can be returned in module, even if it's not inside - // async function. - report(ParseError, false, null(), JSMSG_RESERVED_ID, "await"); - return null(); + if (pc->isAsync()) { + Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked); + if (!kid) + return null(); + pc->lastAwaitOffset = begin; + return newAwaitExpression(begin, kid); } - - Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked); - if (!kid) - return null(); - pc->lastAwaitOffset = begin; - return newAwaitExpression(begin, kid); } + MOZ_FALLTHROUGH; + default: { - Node pn = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true, + Node expr = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true, possibleError, invoked); - if (!pn) + if (!expr) return null(); /* Don't look across a newline boundary for a postfix incop. */ if (!tokenStream.peekTokenSameLine(&tt)) return null(); - if (tt == TOK_INC || tt == TOK_DEC) { - tokenStream.consumeKnownToken(tt); - AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment; - if (!checkAndMarkAsIncOperand(pn, flavor)) - return null(); - return handler.newUpdate((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT, - begin, - pn); - } - return pn; + + if (tt != TOK_INC && tt != TOK_DEC) + return expr; + + tokenStream.consumeKnownToken(tt); + if (!checkIncDecOperand(expr, begin)) + return null(); + return handler.newUpdate((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT, + begin, expr); } } } @@ -8026,10 +8499,9 @@ template <typename ParseHandler> typename ParseHandler::Node Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin) { - Node genfn = handler.newFunctionDefinition(); + Node genfn = handler.newFunctionExpression(); if (!genfn) return null(); - handler.setOp(genfn, JSOP_LAMBDA); ParseContext* outerpc = pc; @@ -8049,8 +8521,8 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin) // Create box for fun->object early to root it. Directives directives(/* strict = */ outerpc->sc()->strict()); - FunctionBox* genFunbox = newFunctionBox(genfn, fun, directives, StarGenerator, SyncFunction, - /* tryAnnexB = */ false); + FunctionBox* genFunbox = newFunctionBox(genfn, fun, /* toStringStart = */ 0, directives, + StarGenerator, SyncFunction, /* tryAnnexB = */ false); if (!genFunbox) return null(); genFunbox->isGenexpLambda = true; @@ -8082,12 +8554,14 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin) MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); + uint32_t end = pos().end; handler.setBeginPosition(comp, begin); - handler.setEndPosition(comp, pos().end); + handler.setEndPosition(comp, end); + genFunbox->setEnd(end); handler.addStatementToList(body, comp); - handler.setEndPosition(body, pos().end); + handler.setEndPosition(body, end); handler.setBeginPosition(genfn, begin); - handler.setEndPosition(genfn, pos().end); + handler.setEndPosition(genfn, end); Node generator = newDotGeneratorName(); if (!generator) @@ -8123,10 +8597,12 @@ Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind) // FIXME: Destructuring binding (bug 980828). - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME); - RootedPropertyName name(context, tokenStream.currentName()); + MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifier, JSMSG_NO_VARIABLE_NAME); + RootedPropertyName name(context, bindingIdentifier(YieldIsKeyword)); + if (!name) + return null(); if (name == context->names().let) { - report(ParseError, false, null(), JSMSG_LET_COMP_BINDING); + error(JSMSG_LET_COMP_BINDING); return null(); } TokenPos namePos = pos(); @@ -8134,10 +8610,10 @@ Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind) if (!lhs) return null(); bool matched; - if (!tokenStream.matchContextualKeyword(&matched, context->names().of)) + if (!tokenStream.matchToken(&matched, TOK_OF)) return null(); if (!matched) { - report(ParseError, false, null(), JSMSG_OF_AFTER_FOR_NAME); + error(JSMSG_OF_AFTER_FOR_NAME); return null(); } @@ -8198,7 +8674,7 @@ Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind) /* Check for (a = b) and warn about possible (a == b) mistype. */ if (handler.isUnparenthesizedAssignment(cond)) { - if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN)) + if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN)) return null(); } @@ -8259,8 +8735,7 @@ Parser<ParseHandler>::comprehension(GeneratorKind comprehensionKind) return null(); if (comprehensionKind != NotGenerator && pc->lastYieldOffset != startYieldOffset) { - reportWithOffset(ParseError, false, pc->lastYieldOffset, - JSMSG_BAD_GENEXP_BODY, js_yield_str); + errorAt(pc->lastYieldOffset, JSMSG_BAD_GENEXP_BODY, js_yield_str); return null(); } @@ -8322,11 +8797,11 @@ Parser<ParseHandler>::assignExprWithoutYieldOrAwait(YieldHandling yieldHandling) Node res = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (res) { if (pc->lastYieldOffset != startYieldOffset) { - reportWithOffset(ParseError, false, pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT); + errorAt(pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT); return null(); } if (pc->lastAwaitOffset != startAwaitOffset) { - reportWithOffset(ParseError, false, pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT); + errorAt(pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT); return null(); } } @@ -8383,13 +8858,8 @@ Parser<ParseHandler>::argumentList(YieldHandling yieldHandling, Node listNode, b } } - TokenKind tt; - if (!tokenStream.getToken(&tt)) - return false; - if (tt != TOK_RP) { - report(ParseError, false, null(), JSMSG_PAREN_AFTER_ARGS); - return false; - } + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_ARGS); + handler.setEndPosition(listNode, pos().end); return true; } @@ -8475,19 +8945,19 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling Node nextMember; if (tt == TOK_DOT) { - if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) + if (!tokenStream.getToken(&tt)) return null(); - if (tt == TOK_NAME) { + if (TokenKindIsPossibleIdentifierName(tt)) { PropertyName* field = tokenStream.currentName(); if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) { - report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "property"); + error(JSMSG_BAD_SUPERPROP, "property"); return null(); } nextMember = handler.newPropertyAccess(lhs, field, pos().end); if (!nextMember) return null(); } else { - report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT); + error(JSMSG_NAME_AFTER_DOT); return null(); } } else if (tt == TOK_LB) { @@ -8498,7 +8968,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX); if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) { - report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "member"); + error(JSMSG_BAD_SUPERPROP, "member"); return null(); } nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end); @@ -8510,12 +8980,12 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling { if (handler.isSuperBase(lhs)) { if (!pc->sc()->allowSuperCall()) { - report(ParseError, false, null(), JSMSG_BAD_SUPERCALL); + error(JSMSG_BAD_SUPERCALL); return null(); } if (tt != TOK_LP) { - report(ParseError, false, null(), JSMSG_BAD_SUPER); + error(JSMSG_BAD_SUPER); return null(); } @@ -8542,7 +9012,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling return null(); } else { if (options().selfHostingMode && handler.isPropertyAccess(lhs)) { - report(ParseError, false, null(), JSMSG_SELFHOSTED_METHOD_CALL); + error(JSMSG_SELFHOSTED_METHOD_CALL); return null(); } @@ -8552,8 +9022,27 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling JSOp op = JSOP_CALL; bool maybeAsyncArrow = false; - if (tt == TOK_LP && handler.isNameAnyParentheses(lhs)) { - if (handler.nameIsEvalAnyParentheses(lhs, context)) { + if (PropertyName* prop = handler.maybeDottedProperty(lhs)) { + // Use the JSOP_FUN{APPLY,CALL} optimizations given the + // right syntax. + if (prop == context->names().apply) { + op = JSOP_FUNAPPLY; + if (pc->isFunctionBox()) { + pc->functionBox()->usesApply = true; + } + } else if (prop == context->names().call) { + op = JSOP_FUNCALL; + } + } else if (tt == TOK_LP) { + if (handler.isAsyncKeyword(lhs, context)) { + // |async (| can be the start of an async arrow + // function, so we need to defer reporting possible + // errors from destructuring syntax. To give better + // error messages, we only allow the AsyncArrowHead + // part of the CoverCallExpressionAndAsyncArrowHead + // syntax when the initial name is "async". + maybeAsyncArrow = true; + } else if (handler.isEvalAnyParentheses(lhs, context)) { // Select the right EVAL op and flag pc as having a // direct eval. op = pc->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL; @@ -8570,24 +9059,6 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling // it. (If we're not in a method, that's fine, so // ignore the return value.) checkAndMarkSuperScope(); - } else if (handler.nameIsUnparenthesizedAsync(lhs, context)) { - // |async (| can be the start of an async arrow - // function, so we need to defer reporting possible - // errors from destructuring syntax. To give better - // error messages, we only allow the AsyncArrowHead - // part of the CoverCallExpressionAndAsyncArrowHead - // syntax when the initial name is "async". - maybeAsyncArrow = true; - } - } else if (PropertyName* prop = handler.maybeDottedProperty(lhs)) { - // Use the JSOP_FUN{APPLY,CALL} optimizations given the - // right syntax. - if (prop == context->names().apply) { - op = JSOP_FUNAPPLY; - if (pc->isFunctionBox()) - pc->functionBox()->usesApply = true; - } else if (prop == context->names().call) { - op = JSOP_FUNCALL; } } @@ -8624,7 +9095,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling } if (handler.isSuperBase(lhs)) { - report(ParseError, false, null(), JSMSG_BAD_SUPER); + error(JSMSG_BAD_SUPER); return null(); } @@ -8646,106 +9117,117 @@ Parser<ParseHandler>::newName(PropertyName* name, TokenPos pos) } template <typename ParseHandler> -PropertyName* -Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling, - bool yieldTokenizedAsName) -{ - PropertyName* ident; - bool isYield; - const Token& tok = tokenStream.currentToken(); - if (tok.type == TOK_NAME) { - MOZ_ASSERT(tok.name() != context->names().yield || - tok.nameContainsEscape() || - yieldTokenizedAsName, - "tokenizer should have treated unescaped 'yield' as TOK_YIELD"); - MOZ_ASSERT_IF(yieldTokenizedAsName, tok.name() == context->names().yield); - - ident = tok.name(); - isYield = ident == context->names().yield; - } else { - MOZ_ASSERT(tok.type == TOK_YIELD && !yieldTokenizedAsName); +bool +Parser<ParseHandler>::checkLabelOrIdentifierReference(HandlePropertyName ident, + uint32_t offset, + YieldHandling yieldHandling) +{ + if (ident == context->names().yield) { + if (yieldHandling == YieldIsKeyword || + versionNumber() >= JSVERSION_1_7) + { + errorAt(offset, JSMSG_RESERVED_ID, "yield"); + return false; + } + if (pc->sc()->needStrictChecks()) { + if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "yield")) + return false; + } - ident = context->names().yield; - isYield = true; + return true; } - if (!isYield) { - if (pc->sc()->strict()) { - const char* badName = ident == context->names().let - ? "let" - : ident == context->names().static_ - ? "static" - : nullptr; - if (badName) { - report(ParseError, false, null(), JSMSG_RESERVED_ID, badName); - return nullptr; - } + if (ident == context->names().await) { + if (awaitIsKeyword()) { + errorAt(offset, JSMSG_RESERVED_ID, "await"); + return false; } - } else { - if (yieldHandling == YieldIsKeyword || - pc->sc()->strict() || - versionNumber() >= JSVERSION_1_7) - { - report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield"); - return nullptr; + return true; + } + + if (IsKeyword(ident) || IsReservedWordLiteral(ident)) { + errorAt(offset, JSMSG_INVALID_ID, ReservedWordToCharZ(ident)); + return false; + } + + if (IsFutureReservedWord(ident)) { + errorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(ident)); + return false; + } + + if (pc->sc()->needStrictChecks()) { + if (IsStrictReservedWord(ident)) { + if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(ident))) + return false; + return true; + } + + if (ident == context->names().let) { + if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "let")) + return false; + return true; + } + + if (ident == context->names().static_) { + if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "static")) + return false; + return true; } } - return ident; + return true; } template <typename ParseHandler> -PropertyName* -Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling) +bool +Parser<ParseHandler>::checkBindingIdentifier(HandlePropertyName ident, + uint32_t offset, + YieldHandling yieldHandling) { - PropertyName* ident; - bool isYield; - const Token& tok = tokenStream.currentToken(); - if (tok.type == TOK_NAME) { - MOZ_ASSERT(tok.name() != context->names().yield || tok.nameContainsEscape(), - "tokenizer should have treated unescaped 'yield' as TOK_YIELD"); + if (!checkLabelOrIdentifierReference(ident, offset, yieldHandling)) + return false; - ident = tok.name(); - isYield = ident == context->names().yield; - } else { - MOZ_ASSERT(tok.type == TOK_YIELD); + if (pc->sc()->needStrictChecks()) { + if (ident == context->names().arguments) { + if (!strictModeErrorAt(offset, JSMSG_BAD_STRICT_ASSIGN, "arguments")) + return false; + return true; + } - ident = context->names().yield; - isYield = true; + if (ident == context->names().eval) { + if (!strictModeErrorAt(offset, JSMSG_BAD_STRICT_ASSIGN, "eval")) + return false; + return true; + } } - if (!isYield) { - if (pc->sc()->strict()) { - const char* badName = ident == context->names().arguments - ? "arguments" - : ident == context->names().eval - ? "eval" - : nullptr; - if (badName) { - report(ParseError, false, null(), JSMSG_BAD_STRICT_ASSIGN, badName); - return nullptr; - } + return true; +} - badName = ident == context->names().let - ? "let" - : ident == context->names().static_ - ? "static" - : nullptr; - if (badName) { - report(ParseError, false, null(), JSMSG_RESERVED_ID, badName); - return nullptr; - } - } - } else { - if (yieldHandling == YieldIsKeyword || - pc->sc()->strict() || - versionNumber() >= JSVERSION_1_7) - { - report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield"); - return nullptr; - } - } +template <typename ParseHandler> +PropertyName* +Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling) +{ + // ES 2017 draft 12.1.1. + // StringValue of IdentifierName normalizes any Unicode escape sequences + // in IdentifierName hence such escapes cannot be used to write an + // Identifier whose code point sequence is the same as a ReservedWord. + // + // Use PropertyName* instead of TokenKind to reflect the normalization. + RootedPropertyName ident(context, tokenStream.currentName()); + if (!checkLabelOrIdentifierReference(ident, pos().begin, yieldHandling)) + return nullptr; + return ident; +} + +template <typename ParseHandler> +PropertyName* +Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling) +{ + RootedPropertyName ident(context, tokenStream.currentName()); + if (!checkBindingIdentifier(ident, pos().begin, yieldHandling)) + return nullptr; return ident; } @@ -8757,7 +9239,7 @@ Parser<ParseHandler>::identifierReference(Handle<PropertyName*> name) if (!pn) return null(); - if (!pc->inDestructuringDecl && !noteUsedName(name)) + if (!noteUsedName(name)) return null(); return pn; @@ -8772,8 +9254,23 @@ Parser<ParseHandler>::stringLiteral() template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::noSubstitutionTemplate() +Parser<ParseHandler>::noSubstitutionTaggedTemplate() { + if (tokenStream.hasInvalidTemplateEscape()) { + tokenStream.clearInvalidTemplateEscape(); + return handler.newRawUndefinedLiteral(pos()); + } + + return handler.newTemplateStringLiteral(stopStringCompression(), pos()); +} + +template <typename ParseHandler> +typename ParseHandler::Node +Parser<ParseHandler>::noSubstitutionUntaggedTemplate() +{ + if (!tokenStream.checkForInvalidTemplateEscapeError()) + return null(); + return handler.newTemplateStringLiteral(stopStringCompression(), pos()); } @@ -8809,6 +9306,74 @@ Parser<ParseHandler>::newRegExp() } template <typename ParseHandler> +void +Parser<ParseHandler>::checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos, + PossibleError* possibleError) +{ + // Return early if a pending destructuring error is already present. + if (possibleError->hasPendingDestructuringError()) + return; + + if (pc->sc()->needStrictChecks()) { + if (handler.isArgumentsAnyParentheses(expr, context)) { + if (pc->sc()->strict()) { + possibleError->setPendingDestructuringErrorAt(exprPos, + JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS); + } else { + possibleError->setPendingDestructuringWarningAt(exprPos, + JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS); + } + return; + } + + if (handler.isEvalAnyParentheses(expr, context)) { + if (pc->sc()->strict()) { + possibleError->setPendingDestructuringErrorAt(exprPos, + JSMSG_BAD_STRICT_ASSIGN_EVAL); + } else { + possibleError->setPendingDestructuringWarningAt(exprPos, + JSMSG_BAD_STRICT_ASSIGN_EVAL); + } + return; + } + } + + // The expression must be either a simple assignment target, i.e. a name + // or a property accessor, or a nested destructuring pattern. + if (!handler.isUnparenthesizedDestructuringPattern(expr) && + !handler.isNameAnyParentheses(expr) && + !handler.isPropertyAccess(expr)) + { + // Parentheses are forbidden around destructuring *patterns* (but + // allowed around names). Use our nicer error message for + // parenthesized, nested patterns. + if (handler.isParenthesizedDestructuringPattern(expr)) + possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_PARENS); + else + possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET); + } +} + +template <typename ParseHandler> +void +Parser<ParseHandler>::checkDestructuringAssignmentElement(Node expr, TokenPos exprPos, + PossibleError* possibleError) +{ + // ES2018 draft rev 0719f44aab93215ed9a626b2f45bd34f36916834 + // 12.15.5 Destructuring Assignment + // + // AssignmentElement[Yield, Await]: + // DestructuringAssignmentTarget[?Yield, ?Await] + // DestructuringAssignmentTarget[?Yield, ?Await] Initializer[+In, ?Yield, ?Await] + + // If |expr| is an assignment element with an initializer expression, its + // destructuring assignment target was already validated in assignExpr(). + // Otherwise we need to check that |expr| is a valid destructuring target. + if (!handler.isUnparenthesizedAssignment(expr)) + checkDestructuringAssignmentTarget(expr, exprPos, possibleError); +} + +template <typename ParseHandler> typename ParseHandler::Node Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError) { @@ -8840,7 +9405,7 @@ Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleErro TokenStream::Modifier modifier = TokenStream::Operand; for (; ; index++) { if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) { - report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG); + error(JSMSG_ARRAY_INIT_TOO_BIG); return null(); } @@ -8857,17 +9422,29 @@ Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleErro } else if (tt == TOK_TRIPLEDOT) { tokenStream.consumeKnownToken(TOK_TRIPLEDOT, TokenStream::Operand); uint32_t begin = pos().begin; + + TokenPos innerPos; + if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand)) + return null(); + Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError); if (!inner) return null(); + if (possibleError) + checkDestructuringAssignmentTarget(inner, innerPos, possibleError); if (!handler.addSpreadElement(literal, begin, inner)) return null(); } else { + TokenPos elementPos; + if (!tokenStream.peekTokenPos(&elementPos, TokenStream::Operand)) + return null(); Node element = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError); if (!element) return null(); + if (possibleError) + checkDestructuringAssignmentElement(element, elementPos, possibleError); if (foldConstants && !FoldConstants(context, &element, this)) return null(); handler.addArrayElement(literal, element); @@ -8883,11 +9460,13 @@ Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleErro break; } if (tt == TOK_TRIPLEDOT && possibleError) - possibleError->setPendingDestructuringError(null(), JSMSG_REST_WITH_COMMA); + possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA); } } - MUST_MATCH_TOKEN_MOD(TOK_RB, modifier, JSMSG_BRACKET_AFTER_LIST); + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier, + reportMissingClosing(JSMSG_BRACKET_AFTER_LIST, + JSMSG_BRACKET_OPENED, begin)); } handler.setEndPosition(literal, pos().end); return literal; @@ -8903,11 +9482,12 @@ DoubleToAtom(ExclusiveContext* cx, double value) template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList, +Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, + const Maybe<DeclarationKind>& maybeDecl, Node propList, PropertyType* propType, MutableHandleAtom propAtom) { TokenKind ltok; - if (!tokenStream.getToken(<ok, TokenStream::KeywordIsName)) + if (!tokenStream.getToken(<ok)) return null(); MOZ_ASSERT(ltok != TOK_RC, "caller should have handled TOK_RC"); @@ -8916,11 +9496,11 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList, bool isAsync = false; if (ltok == TOK_MUL) { isGenerator = true; - if (!tokenStream.getToken(<ok, TokenStream::KeywordIsName)) + if (!tokenStream.getToken(<ok)) return null(); } - if (ltok == TOK_NAME && tokenStream.currentName() == context->names().async) { + if (ltok == TOK_ASYNC) { // AsyncMethod[Yield, Await]: // async [no LineTerminator here] PropertyName[?Yield, ?Await] ... // @@ -8936,21 +9516,18 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList, // ComputedPropertyName[Yield, Await]: // [ ... TokenKind tt = TOK_EOF; - if (!tokenStream.peekTokenSameLine(&tt, TokenStream::KeywordIsName)) + if (!tokenStream.getToken(&tt)) return null(); - if (tt == TOK_STRING || tt == TOK_NUMBER || tt == TOK_LB || - tt == TOK_NAME || tt == TOK_YIELD) - { + if (tt != TOK_LP && tt != TOK_COLON && tt != TOK_RC && tt != TOK_ASSIGN) { isAsync = true; - tokenStream.consumeKnownToken(tt, TokenStream::KeywordIsName); ltok = tt; } else { - tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName); + tokenStream.ungetToken(); } } if (isAsync && isGenerator) { - report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR); + error(JSMSG_ASYNC_GENERATOR); return null(); } @@ -8967,46 +9544,41 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList, break; case TOK_LB: - propName = computedPropertyName(yieldHandling, propList); + propName = computedPropertyName(yieldHandling, maybeDecl, propList); if (!propName) return null(); break; - case TOK_NAME: { + default: { + if (!TokenKindIsPossibleIdentifierName(ltok)) { + error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(ltok)); + return null(); + } + propAtom.set(tokenStream.currentName()); // Do not look for accessor syntax on generators - if (isGenerator || isAsync || - !(propAtom.get() == context->names().get || - propAtom.get() == context->names().set)) - { + if (isGenerator || isAsync || !(ltok == TOK_GET || ltok == TOK_SET)) { propName = handler.newObjectLiteralPropertyName(propAtom, pos()); if (!propName) return null(); break; } - *propType = propAtom.get() == context->names().get ? PropertyType::Getter - : PropertyType::Setter; + *propType = ltok == TOK_GET ? PropertyType::Getter : PropertyType::Setter; // We have parsed |get| or |set|. Look for an accessor property // name next. TokenKind tt; - if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName)) + if (!tokenStream.peekToken(&tt)) return null(); - if (tt == TOK_NAME) { - if (!checkUnescapedName()) - return null(); - - tokenStream.consumeKnownToken(TOK_NAME, TokenStream::KeywordIsName); + if (TokenKindIsPossibleIdentifierName(tt)) { + tokenStream.consumeKnownToken(tt); propAtom.set(tokenStream.currentName()); return handler.newObjectLiteralPropertyName(propAtom, pos()); } if (tt == TOK_STRING) { - if (!checkUnescapedName()) - return null(); - - tokenStream.consumeKnownToken(TOK_STRING, TokenStream::KeywordIsName); + tokenStream.consumeKnownToken(TOK_STRING); propAtom.set(tokenStream.currentToken().atom()); @@ -9020,10 +9592,7 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList, return stringLiteral(); } if (tt == TOK_NUMBER) { - if (!checkUnescapedName()) - return null(); - - tokenStream.consumeKnownToken(TOK_NUMBER, TokenStream::KeywordIsName); + tokenStream.consumeKnownToken(TOK_NUMBER); propAtom.set(DoubleToAtom(context, tokenStream.currentToken().number())); if (!propAtom.get()) @@ -9031,19 +9600,15 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList, return newNumber(tokenStream.currentToken()); } if (tt == TOK_LB) { - if (!checkUnescapedName()) - return null(); - - tokenStream.consumeKnownToken(TOK_LB, TokenStream::KeywordIsName); + tokenStream.consumeKnownToken(TOK_LB); - return computedPropertyName(yieldHandling, propList); + return computedPropertyName(yieldHandling, maybeDecl, propList); } // Not an accessor property after all. propName = handler.newObjectLiteralPropertyName(propAtom.get(), pos()); if (!propName) return null(); - tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName); break; } @@ -9061,10 +9626,6 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList, return null(); break; } - - default: - report(ParseError, false, null(), JSMSG_BAD_PROP_ID); - return null(); } TokenKind tt; @@ -9073,16 +9634,18 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList, if (tt == TOK_COLON) { if (isGenerator) { - report(ParseError, false, null(), JSMSG_BAD_PROP_ID); + error(JSMSG_BAD_PROP_ID); return null(); } *propType = PropertyType::Normal; return propName; } - if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN)) { + if (TokenKindIsPossibleIdentifierName(ltok) && + (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN)) + { if (isGenerator) { - report(ParseError, false, null(), JSMSG_BAD_PROP_ID); + error(JSMSG_BAD_PROP_ID); return null(); } tokenStream.ungetToken(); @@ -9103,34 +9666,31 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList, return propName; } - report(ParseError, false, null(), JSMSG_COLON_AFTER_ID); + error(JSMSG_COLON_AFTER_ID); return null(); } template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::computedPropertyName(YieldHandling yieldHandling, Node literal) +Parser<ParseHandler>::computedPropertyName(YieldHandling yieldHandling, + const Maybe<DeclarationKind>& maybeDecl, + Node literal) { uint32_t begin = pos().begin; - Node assignNode; - { - // Turn off the inDestructuringDecl flag when parsing computed property - // names. In short, when parsing 'let {[x + y]: z} = obj;', noteUsedName() - // should be called on x and y, but not on z. See the comment on - // Parser<>::checkDestructuringPattern() for details. - AutoClearInDestructuringDecl autoClear(pc); - assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited); - if (!assignNode) - return null(); + if (maybeDecl) { + if (*maybeDecl == DeclarationKind::FormalParameter) + pc->functionBox()->hasParameterExprs = true; + } else { + handler.setListFlag(literal, PNX_NONCONST); } - MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR); - Node propname = handler.newComputedName(assignNode, begin, pos().end); - if (!propname) + Node assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited); + if (!assignNode) return null(); - handler.setListFlag(literal, PNX_NONCONST); - return propname; + + MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR); + return handler.newComputedName(assignNode, begin, pos().end); } template <typename ParseHandler> @@ -9139,205 +9699,274 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); + uint32_t openedPos = pos().begin; + Node literal = handler.newObjectLiteral(pos().begin); if (!literal) return null(); bool seenPrototypeMutation = false; bool seenCoverInitializedName = false; + Maybe<DeclarationKind> declKind = Nothing(); RootedAtom propAtom(context); for (;;) { TokenKind tt; - if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) + if (!tokenStream.peekToken(&tt)) return null(); if (tt == TOK_RC) break; - tokenStream.ungetToken(); + if (tt == TOK_TRIPLEDOT) { + // object spread + tokenStream.consumeKnownToken(TOK_TRIPLEDOT); + uint32_t begin = pos().begin; - PropertyType propType; - Node propName = propertyName(yieldHandling, literal, &propType, &propAtom); - if (!propName) - return null(); + TokenPos innerPos; + if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand)) + return null(); - if (propType == PropertyType::Normal) { - Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited, - possibleError); - if (!propExpr) + Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited, + possibleError); + if (!inner) return null(); + if (possibleError) + checkDestructuringAssignmentTarget(inner, innerPos, possibleError); + if (!handler.addSpreadProperty(literal, begin, inner)) + return null(); + } else { + TokenPos namePos = tokenStream.nextToken().pos; - handler.checkAndSetIsDirectRHSAnonFunction(propExpr); + PropertyType propType; + Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom); + if (!propName) - if (foldConstants && !FoldConstants(context, &propExpr, this)) return null(); - if (propAtom == context->names().proto) { - if (seenPrototypeMutation) { - // Directly report the error when we're not in a - // destructuring context. - if (!possibleError) { - report(ParseError, false, propName, JSMSG_DUPLICATE_PROTO_PROPERTY); - return null(); - } - - // Otherwise delay error reporting until we've determined - // whether or not we're destructuring. - possibleError->setPendingExpressionError(propName, - JSMSG_DUPLICATE_PROTO_PROPERTY); - } - seenPrototypeMutation = true; - - // Note: this occurs *only* if we observe TOK_COLON! Only - // __proto__: v mutates [[Prototype]]. Getters, setters, - // method/generator definitions, computed property name - // versions of all of these, and shorthands do not. - uint32_t begin = handler.getPosition(propName).begin; - if (!handler.addPrototypeMutation(literal, begin, propExpr)) + if (propType == PropertyType::Normal) { + TokenPos exprPos; + if (!tokenStream.peekTokenPos(&exprPos, TokenStream::Operand)) return null(); - } else { - if (!handler.isConstant(propExpr)) - handler.setListFlag(literal, PNX_NONCONST); - if (!handler.addPropertyDefinition(literal, propName, propExpr)) + Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited, + possibleError); + if (!propExpr) return null(); - } - } else if (propType == PropertyType::Shorthand) { - /* - * Support, e.g., |var {x, y} = o| as destructuring shorthand - * for |var {x: x, y: y} = o|, and |var o = {x, y}| as initializer - * shorthand for |var o = {x: x, y: y}|. - */ - TokenKind propToken = TOK_NAME; - if (!tokenStream.checkForKeyword(propAtom, &propToken)) - return null(); - if (propToken != TOK_NAME && propToken != TOK_YIELD) { - report(ParseError, false, null(), JSMSG_RESERVED_ID, TokenKindToDesc(propToken)); - return null(); - } + handler.checkAndSetIsDirectRHSAnonFunction(propExpr); - Rooted<PropertyName*> name(context, - identifierReference(yieldHandling, propToken == TOK_YIELD)); - if (!name) - return null(); + if (possibleError) + checkDestructuringAssignmentElement(propExpr, exprPos, possibleError); - Node nameExpr = identifierReference(name); - if (!nameExpr) - return null(); + if (foldConstants && !FoldConstants(context, &propExpr, this)) + return null(); - if (!handler.addShorthand(literal, propName, nameExpr)) - return null(); - } else if (propType == PropertyType::CoverInitializedName) { - /* - * Support, e.g., |var {x=1, y=2} = o| as destructuring shorthand - * with default values, as per ES6 12.14.5 - */ - TokenKind propToken = TOK_NAME; - if (!tokenStream.checkForKeyword(propAtom, &propToken)) - return null(); + if (propAtom == context->names().proto) { + if (seenPrototypeMutation) { + // Directly report the error when we're not in a + // destructuring context. + if (!possibleError) { + errorAt(namePos.begin, JSMSG_DUPLICATE_PROTO_PROPERTY); + return null(); + } - if (propToken != TOK_NAME && propToken != TOK_YIELD) { - report(ParseError, false, null(), JSMSG_RESERVED_ID, TokenKindToDesc(propToken)); - return null(); - } + // Otherwise delay error reporting until we've + // determined whether or not we're destructuring. + possibleError->setPendingExpressionErrorAt(namePos, + JSMSG_DUPLICATE_PROTO_PROPERTY); + } + seenPrototypeMutation = true; - Rooted<PropertyName*> name(context, - identifierReference(yieldHandling, propToken == TOK_YIELD)); - if (!name) - return null(); + // Note: this occurs *only* if we observe TOK_COLON! Only + // __proto__: v mutates [[Prototype]]. Getters, setters, + // method/generator definitions, computed property name + // versions of all of these, and shorthands do not. + if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr)) + return null(); + } else { + if (!handler.isConstant(propExpr)) + handler.setListFlag(literal, PNX_NONCONST); - Node lhs = identifierReference(name); - if (!lhs) - return null(); + if (!handler.addPropertyDefinition(literal, propName, propExpr)) + return null(); + } + } else if (propType == PropertyType::Shorthand) { + /* + * Support, e.g., |({x, y} = o)| as destructuring shorthand + * for |({x: x, y: y} = o)|, and |var o = {x, y}| as + * initializer shorthand for |var o = {x: x, y: y}|. + */ + Rooted<PropertyName*> name(context, identifierReference(yieldHandling)); + if (!name) + return null(); + + Node nameExpr = identifierReference(name); + if (!nameExpr) + return null(); - tokenStream.consumeKnownToken(TOK_ASSIGN); + if (possibleError) + checkDestructuringAssignmentTarget(nameExpr, namePos, possibleError); - if (!seenCoverInitializedName) { - // "shorthand default" or "CoverInitializedName" syntax is only - // valid in the case of destructuring. - seenCoverInitializedName = true; + if (!handler.addShorthand(literal, propName, nameExpr)) + return null(); + } else if (propType == PropertyType::CoverInitializedName) { + /* + * Support, e.g., |({x=1, y=2} = o)| as destructuring + * shorthand with default values, as per ES6 12.14.5 + */ + Rooted<PropertyName*> name(context, identifierReference(yieldHandling)); + if (!name) + return null(); - if (!possibleError) { - // Destructuring defaults are definitely not allowed in this object literal, - // because of something the caller knows about the preceding code. - // For example, maybe the preceding token is an operator: `x + {y=z}`. - report(ParseError, false, null(), JSMSG_COLON_AFTER_ID); + Node lhs = identifierReference(name); + if (!lhs) return null(); + + tokenStream.consumeKnownToken(TOK_ASSIGN); + + if (!seenCoverInitializedName) { + // "shorthand default" or "CoverInitializedName" syntax is + // only valid in the case of destructuring. + seenCoverInitializedName = true; + + if (!possibleError) { + // Destructuring defaults are definitely not allowed + // in this object literal, because of something the + // caller knows about the preceding code. For example, + // maybe the preceding token is an operator: + // |x + {y=z}|. + error(JSMSG_COLON_AFTER_ID); + return null(); + } + + // Here we set a pending error so that later in the parse, + // once we've determined whether or not we're + // destructuring, the error can be reported or ignored + // appropriately. + possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID); } - // Here we set a pending error so that later in the parse, once we've - // determined whether or not we're destructuring, the error can be - // reported or ignored appropriately. - possibleError->setPendingExpressionError(null(), JSMSG_COLON_AFTER_ID); - } + if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) { + // |chars| is "arguments" or "eval" here. + if (!strictModeErrorAt(namePos.begin, JSMSG_BAD_STRICT_ASSIGN, chars)) + return null(); + } - Node rhs; - { - // Clearing `inDestructuringDecl` allows name use to be noted - // in Parser::identifierReference. See bug 1255167. - AutoClearInDestructuringDecl autoClear(pc); - rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); + Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (!rhs) return null(); - } - handler.checkAndSetIsDirectRHSAnonFunction(rhs); + handler.checkAndSetIsDirectRHSAnonFunction(rhs); - Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP); - if (!propExpr) - return null(); - - if (!handler.addPropertyDefinition(literal, propName, propExpr)) - return null(); + Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP); + if (!propExpr) + return null(); - if (!abortIfSyntaxParser()) - return null(); - } else { - RootedAtom funName(context); - if (!tokenStream.isCurrentTokenType(TOK_RB)) { - funName = propAtom; + if (!handler.addPropertyDefinition(literal, propName, propExpr)) + return null(); + } else { + RootedAtom funName(context); + if (!tokenStream.isCurrentTokenType(TOK_RB)) { + funName = propAtom; - if (propType == PropertyType::Getter || propType == PropertyType::Setter) { - funName = prefixAccessorName(propType, propAtom); - if (!funName) - return null(); + if (propType == PropertyType::Getter || propType == PropertyType::Setter) { + funName = prefixAccessorName(propType, propAtom); + if (!funName) + return null(); + } } - } - Node fn = methodDefinition(propType, funName); - if (!fn) - return null(); + Node fn = methodDefinition(namePos.begin, propType, funName); + if (!fn) + return null(); - handler.checkAndSetIsDirectRHSAnonFunction(fn); + handler.checkAndSetIsDirectRHSAnonFunction(fn); - JSOp op = JSOpFromPropertyType(propType); - if (!handler.addObjectMethodDefinition(literal, propName, fn, op)) - return null(); + JSOp op = JSOpFromPropertyType(propType); + if (!handler.addObjectMethodDefinition(literal, propName, fn, op)) + return null(); + + if (possibleError) { + possibleError->setPendingDestructuringErrorAt(namePos, + JSMSG_BAD_DESTRUCT_TARGET); + } + } } - if (!tokenStream.getToken(&tt)) + bool matched; + if (!tokenStream.matchToken(&matched, TOK_COMMA)) return null(); - if (tt == TOK_RC) + if (!matched) break; - if (tt != TOK_COMMA) { - report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST); - return null(); - } + if (tt == TOK_TRIPLEDOT && possibleError) + possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA); } + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None, + reportMissingClosing(JSMSG_CURLY_AFTER_LIST, + JSMSG_CURLY_OPENED, openedPos)); + handler.setEndPosition(literal, pos().end); return literal; } template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::methodDefinition(PropertyType propType, HandleAtom funName) +Parser<ParseHandler>::methodDefinition(uint32_t toStringStart, PropertyType propType, + HandleAtom funName) { - FunctionSyntaxKind kind = FunctionSyntaxKindFromPropertyType(propType); - GeneratorKind generatorKind = GeneratorKindFromPropertyType(propType); - FunctionAsyncKind asyncKind = AsyncKindFromPropertyType(propType); + FunctionSyntaxKind kind; + switch (propType) { + case PropertyType::Getter: + kind = Getter; + break; + + case PropertyType::GetterNoExpressionClosure: + kind = GetterNoExpressionClosure; + break; + + case PropertyType::Setter: + kind = Setter; + break; + + case PropertyType::SetterNoExpressionClosure: + kind = SetterNoExpressionClosure; + break; + + case PropertyType::Method: + case PropertyType::GeneratorMethod: + case PropertyType::AsyncMethod: + kind = Method; + break; + + case PropertyType::Constructor: + kind = ClassConstructor; + break; + + case PropertyType::DerivedConstructor: + kind = DerivedClassConstructor; + break; + + default: + MOZ_CRASH("Parser: methodDefinition: unexpected property type"); + } + + GeneratorKind generatorKind = (propType == PropertyType::GeneratorMethod || + propType == PropertyType::AsyncMethod) + ? StarGenerator + : NotGenerator; + + FunctionAsyncKind asyncKind = (propType == PropertyType::AsyncMethod) + ? AsyncFunction + : SyncFunction; + YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); - return functionDefinition(InAllowed, yieldHandling, funName, kind, generatorKind, asyncKind); + + Node pn = handler.newFunctionExpression(); + if (!pn) + return null(); + + return functionDefinition(toStringStart, pn, InAllowed, yieldHandling, funName, + kind, generatorKind, asyncKind); } template <typename ParseHandler> @@ -9366,17 +9995,13 @@ Parser<ParseHandler>::tryNewTarget(Node &newTarget) if (!tokenStream.getToken(&next)) return false; - if (next != TOK_NAME || tokenStream.currentName() != context->names().target) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "target", TokenKindToDesc(next)); + if (next != TOK_TARGET) { + error(JSMSG_UNEXPECTED_TOKEN, "target", TokenKindToDesc(next)); return false; } - if (!checkUnescapedName()) - return false; - if (!pc->sc()->allowNewTarget()) { - reportWithOffset(ParseError, false, begin, JSMSG_BAD_NEWTARGET); + errorAt(begin, JSMSG_BAD_NEWTARGET); return false; } @@ -9399,7 +10024,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling switch (tt) { case TOK_FUNCTION: - return functionExpr(invoked); + return functionExpr(pos().begin, invoked); case TOK_CLASS: return classDefinition(yieldHandling, ClassExpression, NameRequired); @@ -9423,8 +10048,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling if (!tokenStream.peekToken(&next)) return null(); if (next != TOK_ARROW) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "expression", TokenKindToDesc(TOK_RP)); + error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TOK_RP)); return null(); } @@ -9445,7 +10069,6 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling if (!expr) return null(); MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); - handler.setEndPosition(expr, pos().end); return handler.parenthesize(expr); } @@ -9453,21 +10076,26 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling return templateLiteral(yieldHandling); case TOK_NO_SUBS_TEMPLATE: - return noSubstitutionTemplate(); + return noSubstitutionUntaggedTemplate(); case TOK_STRING: return stringLiteral(); - case TOK_YIELD: - case TOK_NAME: { - if (tokenStream.currentName() == context->names().async) { + default: { + if (!TokenKindIsPossibleIdentifier(tt)) { + error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt)); + return null(); + } + + if (tt == TOK_ASYNC) { TokenKind nextSameLine = TOK_EOF; if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); if (nextSameLine == TOK_FUNCTION) { + uint32_t toStringStart = pos().begin; tokenStream.consumeKnownToken(TOK_FUNCTION); - return functionExpr(PredictUninvoked, AsyncFunction); + return functionExpr(toStringStart, PredictUninvoked, AsyncFunction); } } @@ -9510,8 +10138,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling // name, closing parenthesis, and arrow, and allow it only if all are // present. if (tripledotHandling != TripledotAllowed) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "expression", TokenKindToDesc(tt)); + error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt)); return null(); } @@ -9532,9 +10159,8 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling // the enclosing code is strict mode code, any of "let", "yield", // or "arguments" should be prohibited. Argument-parsing code // handles that. - if (next != TOK_NAME && next != TOK_YIELD) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "rest argument name", TokenKindToDesc(next)); + if (!TokenKindIsPossibleIdentifier(next)) { + error(JSMSG_UNEXPECTED_TOKEN, "rest argument name", TokenKindToDesc(next)); return null(); } } @@ -9542,8 +10168,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling if (!tokenStream.getToken(&next)) return null(); if (next != TOK_RP) { - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "closing parenthesis", TokenKindToDesc(next)); + error(JSMSG_UNEXPECTED_TOKEN, "closing parenthesis", TokenKindToDesc(next)); return null(); } @@ -9552,8 +10177,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling if (next != TOK_ARROW) { // Advance the scanner for proper error location reporting. tokenStream.consumeKnownToken(next); - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "'=>' after argument list", TokenKindToDesc(next)); + error(JSMSG_UNEXPECTED_TOKEN, "'=>' after argument list", TokenKindToDesc(next)); return null(); } @@ -9562,11 +10186,6 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling // Return an arbitrary expression node. See case TOK_RP above. return handler.newNullLiteral(pos()); } - - default: - report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, - "expression", TokenKindToDesc(tt)); - return null(); } } @@ -9580,19 +10199,8 @@ Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHan return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked); } -template <typename ParseHandler> -void -Parser<ParseHandler>::addTelemetry(JSCompartment::DeprecatedLanguageExtension e) -{ - JSContext* cx = context->maybeJSContext(); - if (!cx) - return; - cx->compartment()->addTelemetry(getFilename(), e); -} - -template <typename ParseHandler> bool -Parser<ParseHandler>::warnOnceAboutExprClosure() +ParserBase::warnOnceAboutExprClosure() { #ifndef RELEASE_OR_BETA JSContext* cx = context->maybeJSContext(); @@ -9600,7 +10208,7 @@ Parser<ParseHandler>::warnOnceAboutExprClosure() return true; if (!cx->compartment()->warnedAboutExprClosure) { - if (!report(ParseWarning, false, null(), JSMSG_DEPRECATED_EXPR_CLOSURE)) + if (!warning(JSMSG_DEPRECATED_EXPR_CLOSURE)) return false; cx->compartment()->warnedAboutExprClosure = true; } @@ -9608,9 +10216,8 @@ Parser<ParseHandler>::warnOnceAboutExprClosure() return true; } -template <typename ParseHandler> bool -Parser<ParseHandler>::warnOnceAboutForEach() +ParserBase::warnOnceAboutForEach() { JSContext* cx = context->maybeJSContext(); if (!cx) @@ -9618,7 +10225,7 @@ Parser<ParseHandler>::warnOnceAboutForEach() if (!cx->compartment()->warnedAboutForEach) { // Disabled warning spew. - // if (!report(ParseWarning, false, null(), JSMSG_DEPRECATED_FOR_EACH)) + // if (!warning(JSMSG_DEPRECATED_FOR_EACH)) // return false; cx->compartment()->warnedAboutForEach = true; } diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index b58b021cd7..88d2dad189 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -85,6 +85,16 @@ class ParseContext : public Nestable<ParseContext> } }; + struct ClassStatement : public Statement + { + FunctionBox* constructorBox; + + explicit ClassStatement(ParseContext* pc) + : Statement(pc, StatementKind::Class), + constructorBox(nullptr) + { } + }; + // The intra-function scope stack. // // Tracks declared and used names within a scope. @@ -146,9 +156,9 @@ class ParseContext : public Nestable<ParseContext> } MOZ_MUST_USE bool addDeclaredName(ParseContext* pc, AddDeclaredNamePtr& p, JSAtom* name, - DeclarationKind kind) + DeclarationKind kind, uint32_t pos) { - return maybeReportOOM(pc, declared_->add(p, name, DeclaredNameInfo(kind))); + return maybeReportOOM(pc, declared_->add(p, name, DeclaredNameInfo(kind, pos))); } // Remove all VarForAnnexBLexicalFunction declarations of a certain @@ -330,17 +340,6 @@ class ParseContext : public Nestable<ParseContext> // pointer may be nullptr. Directives* newDirectives; - // Set when parsing a declaration-like destructuring pattern. This flag - // causes PrimaryExpr to create PN_NAME parse nodes for variable references - // which are not hooked into any definition's use chain, added to any tree - // context's AtomList, etc. etc. checkDestructuring will do that work - // later. - // - // The comments atop checkDestructuring explain the distinction between - // assignment-like and declaration-like destructuring patterns, and why - // they need to be treated differently. - mozilla::Maybe<DeclarationKind> inDestructuringDecl; - // Set when parsing a function and it has 'return <expr>;' bool funHasReturnExpr; @@ -432,6 +431,11 @@ class ParseContext : public Nestable<ParseContext> return Statement::findNearest<T>(innermostStatement_, predicate); } + template <typename T> + T* findInnermostStatement() { + return Statement::findNearest<T>(innermostStatement_); + } + AtomVector& positionalFormalParameterNames() { return *positionalFormalParameterNames_; } @@ -532,6 +536,13 @@ ParseContext::Statement::is<ParseContext::LabelStatement>() const return kind_ == StatementKind::Label; } +template <> +inline bool +ParseContext::Statement::is<ParseContext::ClassStatement>() const +{ + return kind_ == StatementKind::Class; +} + template <typename T> inline T& ParseContext::Statement::as() @@ -735,7 +746,155 @@ class UsedNameTracker }; template <typename ParseHandler> -class Parser final : private JS::AutoGCRooter, public StrictModeGetter +class AutoAwaitIsKeyword; + +class ParserBase : public StrictModeGetter +{ + private: + ParserBase* thisForCtor() { return this; } + + public: + ExclusiveContext* const context; + + LifoAlloc& alloc; + + TokenStream tokenStream; + LifoAlloc::Mark tempPoolMark; + + /* list of parsed objects for GC tracing */ + ObjectBox* traceListHead; + + /* innermost parse context (stack-allocated) */ + ParseContext* pc; + + // For tracking used names in this parsing session. + UsedNameTracker& usedNames; + + /* Compression token for aborting. */ + SourceCompressionTask* sct; + + ScriptSource* ss; + + /* Root atoms and objects allocated for the parsed tree. */ + AutoKeepAtoms keepAtoms; + + /* Perform constant-folding; must be true when interfacing with the emitter. */ + const bool foldConstants:1; + + protected: +#if DEBUG + /* Our fallible 'checkOptions' member function has been called. */ + bool checkOptionsCalled:1; +#endif + + /* + * Not all language constructs can be handled during syntax parsing. If it + * is not known whether the parse succeeds or fails, this bit is set and + * the parse will return false. + */ + bool abortedSyntaxParse:1; + + /* Unexpected end of input, i.e. TOK_EOF not at top-level. */ + bool isUnexpectedEOF_:1; + + bool awaitIsKeyword_:1; + + public: + bool awaitIsKeyword() const { + return awaitIsKeyword_; + } + + ParserBase(ExclusiveContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options, + const char16_t* chars, size_t length, bool foldConstants, + UsedNameTracker& usedNames, Parser<SyntaxParseHandler>* syntaxParser, + LazyScript* lazyOuterFunction); + ~ParserBase(); + + const char* getFilename() const { return tokenStream.getFilename(); } + JSVersion versionNumber() const { return tokenStream.versionNumber(); } + TokenPos pos() const { return tokenStream.currentToken().pos; } + + // Determine whether |yield| is a valid name in the current context, or + // whether it's prohibited due to strictness, JS version, or occurrence + // inside a star generator. + bool yieldExpressionsSupported() { + return (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()) && !pc->isAsync(); + } + + virtual bool strictMode() { return pc->sc()->strict(); } + bool setLocalStrictMode(bool strict) { + MOZ_ASSERT(tokenStream.debugHasNoLookahead()); + return pc->sc()->setLocalStrictMode(strict); + } + + const ReadOnlyCompileOptions& options() const { + return tokenStream.options(); + } + + bool hadAbortedSyntaxParse() { + return abortedSyntaxParse; + } + void clearAbortedSyntaxParse() { + abortedSyntaxParse = false; + } + + bool isUnexpectedEOF() const { return isUnexpectedEOF_; } + + bool reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...); + + /* Report the given error at the current offset. */ + void error(unsigned errorNumber, ...); + void errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber, ...); + + /* Report the given error at the given offset. */ + void errorAt(uint32_t offset, unsigned errorNumber, ...); + void errorWithNotesAt(UniquePtr<JSErrorNotes> notes, uint32_t offset, + unsigned errorNumber, ...); + + /* + * Handle a strict mode error at the current offset. Report an error if in + * strict mode code, or warn if not, using the given error number and + * arguments. + */ + MOZ_MUST_USE bool strictModeError(unsigned errorNumber, ...); + + /* + * Handle a strict mode error at the given offset. Report an error if in + * strict mode code, or warn if not, using the given error number and + * arguments. + */ + MOZ_MUST_USE bool strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...); + + /* Report the given warning at the current offset. */ + MOZ_MUST_USE bool warning(unsigned errorNumber, ...); + + /* Report the given warning at the given offset. */ + MOZ_MUST_USE bool warningAt(uint32_t offset, unsigned errorNumber, ...); + + /* + * If extra warnings are enabled, report the given warning at the current + * offset. + */ + MOZ_MUST_USE bool extraWarning(unsigned errorNumber, ...); + + /* + * If extra warnings are enabled, report the given warning at the given + * offset. + */ + MOZ_MUST_USE bool extraWarningAt(uint32_t offset, unsigned errorNumber, ...); + + bool isValidStrictBinding(PropertyName* name); + + bool warnOnceAboutExprClosure(); + bool warnOnceAboutForEach(); + + protected: + enum InvokedPrediction { PredictUninvoked = false, PredictInvoked = true }; + enum ForInitLocation { InForInit, NotInForInit }; +}; + +template <typename ParseHandler> +class Parser final : public ParserBase, private JS::AutoGCRooter { private: using Node = typename ParseHandler::Node; @@ -769,13 +928,13 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter * * Ex: * PossibleError possibleError(*this); - * possibleError.setPendingExpressionError(pn, JSMSG_BAD_PROP_ID); + * possibleError.setPendingExpressionErrorAt(pos, JSMSG_BAD_PROP_ID); * // A JSMSG_BAD_PROP_ID ParseError is reported, returns false. * if (!possibleError.checkForExpressionError()) * return false; // we reach this point with a pending exception * * PossibleError possibleError(*this); - * possibleError.setPendingExpressionError(pn, JSMSG_BAD_PROP_ID); + * possibleError.setPendingExpressionErrorAt(pos, JSMSG_BAD_PROP_ID); * // Returns true, no error is reported. * if (!possibleError.checkForDestructuringError()) * return false; // not reached, no pending exception @@ -788,7 +947,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter class MOZ_STACK_CLASS PossibleError { private: - enum class ErrorKind { Expression, Destructuring }; + enum class ErrorKind { Expression, Destructuring, DestructuringWarning }; enum class ErrorState { None, Pending }; @@ -803,11 +962,12 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter Parser<ParseHandler>& parser_; Error exprError_; Error destructuringError_; + Error destructuringWarning_; // Returns the error report. Error& error(ErrorKind kind); - // Return true if an error is pending without reporting + // Return true if an error is pending without reporting. bool hasError(ErrorKind kind); // Resolve any pending error. @@ -815,11 +975,15 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter // Set a pending error. Only a single error may be set per instance and // error kind. - void setPending(ErrorKind kind, Node pn, unsigned errorNumber); + void setPending(ErrorKind kind, const TokenPos& pos, unsigned errorNumber); // If there is a pending error, report it and return false, otherwise // return true. - bool checkForError(ErrorKind kind); + MOZ_MUST_USE bool checkForError(ErrorKind kind); + + // If there is a pending warning, report it and return either false or + // true depending on the werror option, otherwise return true. + MOZ_MUST_USE bool checkForWarning(ErrorKind kind); // Transfer an existing error to another instance. void transferErrorTo(ErrorKind kind, PossibleError* other); @@ -827,23 +991,33 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter public: explicit PossibleError(Parser<ParseHandler>& parser); + // Return true if a pending destructuring error is present. + bool hasPendingDestructuringError(); + // Set a pending destructuring error. Only a single error may be set // per instance, i.e. subsequent calls to this method are ignored and // won't overwrite the existing pending error. - void setPendingDestructuringError(Node pn, unsigned errorNumber); + void setPendingDestructuringErrorAt(const TokenPos& pos, unsigned errorNumber); + + // Set a pending destructuring warning. Only a single warning may be + // set per instance, i.e. subsequent calls to this method are ignored + // and won't overwrite the existing pending warning. + void setPendingDestructuringWarningAt(const TokenPos& pos, unsigned errorNumber); // Set a pending expression error. Only a single error may be set per // instance, i.e. subsequent calls to this method are ignored and won't // overwrite the existing pending error. - void setPendingExpressionError(Node pn, unsigned errorNumber); + void setPendingExpressionErrorAt(const TokenPos& pos, unsigned errorNumber); - // If there is a pending destructuring error, report it and return - // false, otherwise return true. Clears any pending expression error. - bool checkForDestructuringError(); + // If there is a pending destructuring error or warning, report it and + // return false, otherwise return true. Clears any pending expression + // error. + MOZ_MUST_USE bool checkForDestructuringErrorOrWarning(); // If there is a pending expression error, report it and return false, - // otherwise return true. Clears any pending destructuring error. - bool checkForExpressionError(); + // otherwise return true. Clears any pending destructuring error or + // warning. + MOZ_MUST_USE bool checkForExpressionError(); // Pass pending errors between possible error instances. This is useful // for extending the lifetime of a pending error beyond the scope of @@ -853,70 +1027,21 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter }; public: - ExclusiveContext* const context; - - LifoAlloc& alloc; - - TokenStream tokenStream; - LifoAlloc::Mark tempPoolMark; - - /* list of parsed objects for GC tracing */ - ObjectBox* traceListHead; - - /* innermost parse context (stack-allocated) */ - ParseContext* pc; - - // For tracking used names in this parsing session. - UsedNameTracker& usedNames; - - /* Compression token for aborting. */ - SourceCompressionTask* sct; - - ScriptSource* ss; - - /* Root atoms and objects allocated for the parsed tree. */ - AutoKeepAtoms keepAtoms; - - /* Perform constant-folding; must be true when interfacing with the emitter. */ - const bool foldConstants:1; - - private: -#if DEBUG - /* Our fallible 'checkOptions' member function has been called. */ - bool checkOptionsCalled:1; -#endif - - /* - * Not all language constructs can be handled during syntax parsing. If it - * is not known whether the parse succeeds or fails, this bit is set and - * the parse will return false. - */ - bool abortedSyntaxParse:1; - - /* Unexpected end of input, i.e. TOK_EOF not at top-level. */ - bool isUnexpectedEOF_:1; - - public: /* State specific to the kind of parse being performed. */ ParseHandler handler; void prepareNodeForMutation(Node node) { handler.prepareNodeForMutation(node); } void freeTree(Node node) { handler.freeTree(node); } - private: - bool reportHelper(ParseReportKind kind, bool strict, uint32_t offset, - unsigned errorNumber, va_list args); public: - bool report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...); - bool reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...); - bool reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset, unsigned errorNumber, - ...); - Parser(ExclusiveContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options, const char16_t* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames, Parser<SyntaxParseHandler>* syntaxParser, LazyScript* lazyOuterFunction); ~Parser(); + friend class AutoAwaitIsKeyword<ParseHandler>; + void setAwaitIsKeyword(bool isKeyword); + bool checkOptions(); // A Parser::Mark is the extension of the LifoAlloc::Mark to the entire @@ -941,9 +1066,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter friend void js::frontend::MarkParser(JSTracer* trc, JS::AutoGCRooter* parser); - const char* getFilename() const { return tokenStream.getFilename(); } - JSVersion versionNumber() const { return tokenStream.versionNumber(); } - /* * Parse a top-level JS script. */ @@ -954,7 +1076,8 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter * cx->tempLifoAlloc. */ ObjectBox* newObjectBox(JSObject* obj); - FunctionBox* newFunctionBox(Node fn, JSFunction* fun, Directives directives, + FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart, + Directives directives, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB); @@ -968,24 +1091,14 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter void trace(JSTracer* trc); - bool hadAbortedSyntaxParse() { - return abortedSyntaxParse; - } - void clearAbortedSyntaxParse() { - abortedSyntaxParse = false; - } - - bool isUnexpectedEOF() const { return isUnexpectedEOF_; } - - bool checkUnescapedName(); - private: Parser* thisForCtor() { return this; } JSAtom* stopStringCompression(); Node stringLiteral(); - Node noSubstitutionTemplate(); + Node noSubstitutionTaggedTemplate(); + Node noSubstitutionUntaggedTemplate(); Node templateLiteral(YieldHandling yieldHandling); bool taggedTemplate(YieldHandling yieldHandling, Node nodeList, TokenKind tt); bool appendToCallSiteObj(Node callSiteObj); @@ -1034,43 +1147,23 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter // Parse an inner function given an enclosing ParseContext and a // FunctionBox for the inner function. - bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, InHandling inHandling, - YieldHandling yieldHandling, FunctionSyntaxKind kind, + bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, uint32_t toStringStart, + InHandling inHandling, YieldHandling yieldHandling, + FunctionSyntaxKind kind, Directives inheritedDirectives, Directives* newDirectives); // Parse a function's formal parameters and its body assuming its function // ParseContext is already on the stack. bool functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling, Node pn, FunctionSyntaxKind kind, - mozilla::Maybe<uint32_t> parameterListEnd = mozilla::Nothing()); - - - // Determine whether |yield| is a valid name in the current context, or - // whether it's prohibited due to strictness, JS version, or occurrence - // inside a star generator. - bool yieldExpressionsSupported() { - return (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()) && !pc->isAsync(); - } + mozilla::Maybe<uint32_t> parameterListEnd = mozilla::Nothing(), + bool isStandaloneFunction = false); // Match the current token against the BindingIdentifier production with // the given Yield parameter. If there is no match, report a syntax // error. PropertyName* bindingIdentifier(YieldHandling yieldHandling); - virtual bool strictMode() { return pc->sc()->strict(); } - bool setLocalStrictMode(bool strict) { - MOZ_ASSERT(tokenStream.debugHasNoLookahead()); - return pc->sc()->setLocalStrictMode(strict); - } - - const ReadOnlyCompileOptions& options() const { - return tokenStream.options(); - } - - private: - enum InvokedPrediction { PredictUninvoked = false, PredictInvoked = true }; - enum ForInitLocation { InForInit, NotInForInit }; - private: /* * JS parsers, from lowest to highest precedence. @@ -1088,9 +1181,10 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter * Some parsers have two versions: an always-inlined version (with an 'i' * suffix) and a never-inlined version (with an 'n' suffix). */ - Node functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling, + Node functionStmt(uint32_t toStringStart, + YieldHandling yieldHandling, DefaultHandling defaultHandling, FunctionAsyncKind asyncKind = SyncFunction); - Node functionExpr(InvokedPrediction invoked = PredictUninvoked, + Node functionExpr(uint32_t toStringStart, InvokedPrediction invoked = PredictUninvoked, FunctionAsyncKind asyncKind = SyncFunction); Node statementList(YieldHandling yieldHandling); @@ -1106,7 +1200,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter Node* forInitialPart, mozilla::Maybe<ParseContext::Scope>& forLetImpliedScope, Node* forInOrOfExpression); - bool validateForInOrOfLHSExpression(Node target, PossibleError* possibleError); Node expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling); Node switchStatement(YieldHandling yieldHandling); @@ -1132,9 +1225,27 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter // continues a LexicalDeclaration. bool nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling); - Node lexicalDeclaration(YieldHandling yieldHandling, bool isConst); + Node lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind); Node importDeclaration(); + + bool processExport(Node node); + bool processExportFrom(Node node); + + Node exportFrom(uint32_t begin, Node specList); + Node exportBatch(uint32_t begin); + bool checkLocalExportNames(Node node); + Node exportClause(uint32_t begin); + Node exportFunctionDeclaration(uint32_t begin); + Node exportVariableStatement(uint32_t begin); + Node exportClassDeclaration(uint32_t begin); + Node exportLexicalDeclaration(uint32_t begin, DeclarationKind kind); + Node exportDefaultFunctionDeclaration(uint32_t begin, + FunctionAsyncKind asyncKind = SyncFunction); + Node exportDefaultClassDeclaration(uint32_t begin); + Node exportDefaultAssignExpr(uint32_t begin); + Node exportDefault(uint32_t begin); + Node exportDeclaration(); Node expressionStatement(YieldHandling yieldHandling, InvokedPrediction invoked = PredictUninvoked); @@ -1222,7 +1333,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter bool tryNewTarget(Node& newTarget); bool checkAndMarkSuperScope(); - Node methodDefinition(PropertyType propType, HandleAtom funName); + Node methodDefinition(uint32_t toStringStart, PropertyType propType, HandleAtom funName); /* * Additional JS parsers. @@ -1230,10 +1341,11 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind, Node funcpn); - Node functionDefinition(InHandling inHandling, YieldHandling yieldHandling, HandleAtom name, + Node functionDefinition(uint32_t toStringStart, Node pn, + InHandling inHandling, YieldHandling yieldHandling, HandleAtom name, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, - InvokedPrediction invoked = PredictUninvoked); + bool tryAnnexB = false); // Parse a function body. Pass StatementListBody if the body is a list of // statements; pass ExpressionBody if the body is a single expression. @@ -1265,21 +1377,34 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter bool checkExportedName(JSAtom* exportName); bool checkExportedNamesForDeclaration(Node node); + bool checkExportedNameForClause(Node node); + bool checkExportedNameForFunction(Node node); + bool checkExportedNameForClass(Node node); + enum ClassContext { ClassStatement, ClassExpression }; Node classDefinition(YieldHandling yieldHandling, ClassContext classContext, DefaultHandling defaultHandling); - PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling, - bool yieldTokenizedAsName); + bool checkLabelOrIdentifierReference(HandlePropertyName ident, + uint32_t offset, + YieldHandling yieldHandling); + + bool checkLocalExportName(HandlePropertyName ident, uint32_t offset) { + return checkLabelOrIdentifierReference(ident, offset, YieldIsName); + } + + bool checkBindingIdentifier(HandlePropertyName ident, + uint32_t offset, + YieldHandling yieldHandling); + + PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling); PropertyName* labelIdentifier(YieldHandling yieldHandling) { - return labelOrIdentifierReference(yieldHandling, false); + return labelOrIdentifierReference(yieldHandling); } - PropertyName* identifierReference(YieldHandling yieldHandling, - bool yieldTokenizedAsName = false) - { - return labelOrIdentifierReference(yieldHandling, yieldTokenizedAsName); + PropertyName* identifierReference(YieldHandling yieldHandling) { + return labelOrIdentifierReference(yieldHandling); } PropertyName* importedBinding() { @@ -1298,17 +1423,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter #endif } - enum AssignmentFlavor { - PlainAssignment, - CompoundAssignment, - KeyedDestructuringAssignment, - IncrementAssignment, - DecrementAssignment, - ForInOrOfTarget - }; - - bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor, - PossibleError* possibleError=nullptr); bool matchInOrOf(bool* isForInp, bool* isForOfp); bool hasUsedFunctionSpecialName(HandlePropertyName name); @@ -1319,23 +1433,27 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter Node newDotGeneratorName(); bool declareDotGeneratorName(); - bool checkFunctionDefinition(HandleAtom funAtom, Node pn, FunctionSyntaxKind kind, - GeneratorKind generatorKind, bool* tryAnnexB); - bool skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind, bool tryAnnexB); - bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, + bool skipLazyInnerFunction(Node pn, uint32_t toStringStart, FunctionSyntaxKind kind, + bool tryAnnexB); + bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives, Directives* newDirectives); - bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, InHandling inHandling, - YieldHandling yieldHandling, FunctionSyntaxKind kind, + bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, uint32_t toStringStart, + InHandling inHandling, YieldHandling yieldHandling, + FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives, Directives* newDirectives); - bool finishFunctionScopes(); - bool finishFunction(); + bool finishFunctionScopes(bool isStandaloneFunction); + bool finishFunction(bool isStandaloneFunction = false); bool leaveInnerFunction(ParseContext* outerpc); + bool matchOrInsertSemicolonHelper(TokenStream::Modifier modifier); + bool matchOrInsertSemicolonAfterExpression(); + bool matchOrInsertSemicolonAfterNonExpression(); + public: enum FunctionCallBehavior { PermitAssignmentToFunctionCalls, @@ -1346,26 +1464,24 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls); private: - bool reportIfArgumentsEvalTarget(Node nameNode); - bool reportIfNotValidSimpleAssignmentTarget(Node target, AssignmentFlavor flavor); - - bool checkAndMarkAsIncOperand(Node kid, AssignmentFlavor flavor); + bool checkIncDecOperand(Node operand, uint32_t operandOffset); bool checkStrictAssignment(Node lhs); - bool checkStrictBinding(PropertyName* name, TokenPos pos); bool hasValidSimpleStrictParameterNames(); - bool isValidStrictBinding(PropertyName* name); + void reportMissingClosing(unsigned errorNumber, unsigned noteNumber, uint32_t openedPos); - void reportRedeclaration(HandlePropertyName name, DeclarationKind kind, TokenPos pos); - bool notePositionalFormalParameter(Node fn, HandlePropertyName name, + void reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, TokenPos pos, + uint32_t prevPos); + bool notePositionalFormalParameter(Node fn, HandlePropertyName name, uint32_t beginPos, bool disallowDuplicateParams, bool* duplicatedParam); bool noteDestructuredPositionalFormalParameter(Node fn, Node destruct); mozilla::Maybe<DeclarationKind> isVarRedeclaredInEval(HandlePropertyName name, DeclarationKind kind); - bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind, - mozilla::Maybe<DeclarationKind>* redeclaredKind); - bool tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name, bool* tryAnnexB); + bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos, + mozilla::Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos); + bool tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name, uint32_t beginPos, + bool* tryAnnexB); bool checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt, DeclarationKind kind, TokenPos pos); bool noteDeclaredName(HandlePropertyName name, DeclarationKind kind, TokenPos pos); @@ -1384,27 +1500,27 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter mozilla::Maybe<LexicalScope::Data*> newLexicalScopeData(ParseContext::Scope& scope); Node finishLexicalScope(ParseContext::Scope& scope, Node body); - Node propertyName(YieldHandling yieldHandling, Node propList, + Node propertyName(YieldHandling yieldHandling, + const mozilla::Maybe<DeclarationKind>& maybeDecl, Node propList, PropertyType* propType, MutableHandleAtom propAtom); - Node computedPropertyName(YieldHandling yieldHandling, Node literal); + Node computedPropertyName(YieldHandling yieldHandling, + const mozilla::Maybe<DeclarationKind>& maybeDecl, Node literal); Node arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError); Node newRegExp(); Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError); - // Top-level entrypoint into destructuring pattern checking/name-analyzing. - bool checkDestructuringPattern(Node pattern, mozilla::Maybe<DeclarationKind> maybeDecl, - PossibleError* possibleError = nullptr); + Node bindingInitializer(Node lhs, DeclarationKind kind, YieldHandling yieldHandling); + Node bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling); + Node bindingIdentifierOrPattern(DeclarationKind kind, YieldHandling yieldHandling, + TokenKind tt); + Node objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling); + Node arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling); - // Recursive methods for checking/name-analyzing subcomponents of a - // destructuring pattern. The array/object methods *must* be passed arrays - // or objects. The name method may be passed anything but will report an - // error if not passed a name. - bool checkDestructuringArray(Node arrayPattern, mozilla::Maybe<DeclarationKind> maybeDecl); - bool checkDestructuringObject(Node objectPattern, mozilla::Maybe<DeclarationKind> maybeDecl); - bool checkDestructuringName(Node expr, mozilla::Maybe<DeclarationKind> maybeDecl); - - bool checkAssignmentToCall(Node node, unsigned errnum); + void checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos, + PossibleError* possibleError); + void checkDestructuringAssignmentElement(Node expr, TokenPos exprPos, + PossibleError* possibleError); Node newNumber(const Token& tok) { return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos); @@ -1412,18 +1528,28 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter static Node null() { return ParseHandler::null(); } - bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum); - JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom); - TokenPos pos() const { return tokenStream.currentToken().pos; } - bool asmJS(Node list); +}; + +template <typename ParseHandler> +class MOZ_STACK_CLASS AutoAwaitIsKeyword +{ + private: + Parser<ParseHandler>* parser_; + bool oldAwaitIsKeyword_; - void addTelemetry(JSCompartment::DeprecatedLanguageExtension e); + public: + AutoAwaitIsKeyword(Parser<ParseHandler>* parser, bool awaitIsKeyword) { + parser_ = parser; + oldAwaitIsKeyword_ = parser_->awaitIsKeyword_; + parser_->setAwaitIsKeyword(awaitIsKeyword); + } - bool warnOnceAboutExprClosure(); - bool warnOnceAboutForEach(); + ~AutoAwaitIsKeyword() { + parser_->setAwaitIsKeyword(oldAwaitIsKeyword_); + } }; } /* namespace frontend */ diff --git a/js/src/vm/Keywords.h b/js/src/frontend/ReservedWords.h index ef37c44198..27f5b11c1e 100644 --- a/js/src/vm/Keywords.h +++ b/js/src/frontend/ReservedWords.h @@ -4,15 +4,16 @@ * 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/. */ -/* A higher-order macro for enumerating keyword tokens. */ +/* A higher-order macro for enumerating reserved word tokens. */ -#ifndef vm_Keywords_h -#define vm_Keywords_h +#ifndef vm_ReservedWords_h +#define vm_ReservedWords_h -#define FOR_EACH_JAVASCRIPT_KEYWORD(macro) \ +#define FOR_EACH_JAVASCRIPT_RESERVED_WORD(macro) \ macro(false, false_, TOK_FALSE) \ macro(true, true_, TOK_TRUE) \ macro(null, null, TOK_NULL) \ + \ /* Keywords. */ \ macro(break, break_, TOK_BREAK) \ macro(case, case_, TOK_CASE) \ @@ -36,31 +37,45 @@ macro(this, this_, TOK_THIS) \ macro(throw, throw_, TOK_THROW) \ macro(try, try_, TOK_TRY) \ - macro(typeof, typeof, TOK_TYPEOF) \ + macro(typeof, typeof_, TOK_TYPEOF) \ macro(var, var, TOK_VAR) \ macro(void, void_, TOK_VOID) \ macro(while, while_, TOK_WHILE) \ macro(with, with, TOK_WITH) \ macro(import, import, TOK_IMPORT) \ - macro(export, export, TOK_EXPORT) \ + macro(export, export_, TOK_EXPORT) \ macro(class, class_, TOK_CLASS) \ macro(extends, extends, TOK_EXTENDS) \ macro(super, super, TOK_SUPER) \ - /* Reserved keywords. */ \ - macro(enum, enum_, TOK_RESERVED) \ - /* Future reserved keywords, but only in strict mode. */ \ - macro(implements, implements, TOK_STRICT_RESERVED) \ - macro(interface, interface, TOK_STRICT_RESERVED) \ - macro(package, package, TOK_STRICT_RESERVED) \ - macro(private, private_, TOK_STRICT_RESERVED) \ - macro(protected, protected_, TOK_STRICT_RESERVED) \ - macro(public, public_, TOK_STRICT_RESERVED) \ + \ + /* Future reserved words. */ \ + macro(enum, enum_, TOK_ENUM) \ + \ + /* Future reserved words, but only in strict mode. */ \ + macro(implements, implements, TOK_IMPLEMENTS) \ + macro(interface, interface, TOK_INTERFACE) \ + macro(package, package, TOK_PACKAGE) \ + macro(private, private_, TOK_PRIVATE) \ + macro(protected, protected_, TOK_PROTECTED) \ + macro(public, public_, TOK_PUBLIC) \ + \ + /* Contextual keywords. */ \ + macro(as, as, TOK_AS) \ + macro(async, async, TOK_ASYNC) \ macro(await, await, TOK_AWAIT) \ + macro(each, each, TOK_EACH) \ + macro(from, from, TOK_FROM) \ + macro(get, get, TOK_GET) \ + macro(let, let, TOK_LET) \ + macro(of, of, TOK_OF) \ + macro(set, set, TOK_SET) \ + macro(static, static_, TOK_STATIC) \ + macro(target, target, TOK_TARGET) \ /* \ * Yield is a token inside function*. Outside of a function*, it is a \ - * future reserved keyword in strict mode, but a keyword in JS1.7 even \ + * future reserved word in strict mode, but a keyword in JS1.7 even \ * when strict. Punt logic to parser. \ */ \ macro(yield, yield, TOK_YIELD) -#endif /* vm_Keywords_h */ +#endif /* vm_ReservedWords_h */ diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index a6ac542f67..013444690c 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -38,6 +38,7 @@ enum class StatementKind : uint8_t ForOfLoop, DoLoop, WhileLoop, + Class, // Used only by BytecodeEmitter. Spread @@ -450,6 +451,8 @@ class FunctionBox : public ObjectBox, public SharedContext uint32_t bufEnd; uint32_t startLine; uint32_t startColumn; + uint32_t toStringStart; + uint32_t toStringEnd; uint16_t length; uint8_t generatorKindBits_; /* The GeneratorKind of this function. */ @@ -472,12 +475,15 @@ class FunctionBox : public ObjectBox, public SharedContext bool usesThis:1; /* contains 'this' */ bool usesReturn:1; /* contains a 'return' statement */ bool hasRest_:1; /* has rest parameter */ + bool isExprBody_:1; /* arrow function with expression + * body or expression closure: + * function(x) x*x */ FunctionContextFlags funCxFlags; FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead, JSFunction* fun, - Directives directives, bool extraWarnings, GeneratorKind generatorKind, - FunctionAsyncKind asyncKind); + uint32_t toStringStart, Directives directives, bool extraWarnings, + GeneratorKind generatorKind, FunctionAsyncKind asyncKind); MutableHandle<LexicalScope::Data*> namedLambdaBindings() { MOZ_ASSERT(context->compartment()->runtimeFromAnyThread()->keepAtoms()); @@ -545,6 +551,11 @@ class FunctionBox : public ObjectBox, public SharedContext hasRest_ = true; } + bool isExprBody() const { return isExprBody_; } + void setIsExprBody() { + isExprBody_ = true; + } + void setGeneratorKind(GeneratorKind kind) { // A generator kind can be set at initialization, or when "yield" is // first seen. In both cases the transition can only happen from @@ -594,6 +605,14 @@ class FunctionBox : public ObjectBox, public SharedContext tokenStream.srcCoords.lineNumAndColumnIndex(bufStart, &startLine, &startColumn); } + void setEnd(uint32_t end) { + // For all functions except class constructors, the buffer and + // toString ending positions are the same. Class constructors override + // the toString ending position with the end of the class definition. + bufEnd = end; + toStringEnd = end; + } + void trace(JSTracer* trc) override; }; diff --git a/js/src/frontend/SourceNotes.h b/js/src/frontend/SourceNotes.h index dd2a95ad1e..6ae184ae4b 100644 --- a/js/src/frontend/SourceNotes.h +++ b/js/src/frontend/SourceNotes.h @@ -56,13 +56,14 @@ namespace js { M(SRC_NEXTCASE, "nextcase", 1) /* Distance forward from one CASE in a CONDSWITCH to \ the next. */ \ M(SRC_ASSIGNOP, "assignop", 0) /* += or another assign-op follows. */ \ + M(SRC_CLASS_SPAN, "class", 2) /* The starting and ending offsets for the class, used \ + for toString correctness for default ctors. */ \ M(SRC_TRY, "try", 1) /* JSOP_TRY, offset points to goto at the end of the \ try block. */ \ /* All notes above here are "gettable". See SN_IS_GETTABLE below. */ \ M(SRC_COLSPAN, "colspan", 1) /* Number of columns this opcode spans. */ \ M(SRC_NEWLINE, "newline", 0) /* Bytecode follows a source newline. */ \ M(SRC_SETLINE, "setline", 1) /* A file-absolute source line number note. */ \ - M(SRC_UNUSED20, "unused20", 0) /* Unused. */ \ M(SRC_UNUSED21, "unused21", 0) /* Unused. */ \ M(SRC_UNUSED22, "unused22", 0) /* Unused. */ \ M(SRC_UNUSED23, "unused23", 0) /* Unused. */ \ diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index b7f00605be..a604b599fb 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -9,6 +9,8 @@ #include "mozilla/Attributes.h" +#include <string.h> + #include "frontend/ParseNode.h" #include "frontend/TokenStream.h" @@ -94,10 +96,13 @@ class SyntaxParseHandler // Nodes representing unparenthesized names. NodeUnparenthesizedArgumentsName, - NodeUnparenthesizedAsyncName, NodeUnparenthesizedEvalName, NodeUnparenthesizedName, + // Node representing the "async" name, which may actually be a + // contextual keyword. + NodePotentialAsyncKeyword, + // Valuable for recognizing potential destructuring patterns. NodeUnparenthesizedArray, NodeUnparenthesizedObject, @@ -183,8 +188,8 @@ class SyntaxParseHandler lastAtom = name; if (name == cx->names().arguments) return NodeUnparenthesizedArgumentsName; - if (name == cx->names().async) - return NodeUnparenthesizedAsyncName; + if (pos.begin + strlen("async") == pos.end && name == cx->names().async) + return NodePotentialAsyncKeyword; if (name == cx->names().eval) return NodeUnparenthesizedEvalName; return NodeUnparenthesizedName; @@ -219,6 +224,7 @@ class SyntaxParseHandler Node newThisLiteral(const TokenPos& pos, Node thisName) { return NodeGeneric; } Node newNullLiteral(const TokenPos& pos) { return NodeGeneric; } + Node newRawUndefinedLiteral(const TokenPos& pos) { return NodeGeneric; } template <class Boxer> Node newRegExp(RegExpObject* reobj, const TokenPos& pos, Boxer& boxer) { return NodeGeneric; } @@ -235,6 +241,10 @@ class SyntaxParseHandler return NodeUnparenthesizedUnary; } + Node newNullary(ParseNodeKind kind, JSOp op, const TokenPos& pos) { + return NodeGeneric; + } + Node newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, Node kid) { return NodeUnparenthesizedUnary; } @@ -279,7 +289,7 @@ class SyntaxParseHandler Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; } Node newClassMethodList(uint32_t begin) { return NodeGeneric; } Node newClassNames(Node outer, Node inner, const TokenPos& pos) { return NodeGeneric; } - Node newClass(Node name, Node heritage, Node methodBlock) { return NodeGeneric; } + Node newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; } Node newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; } Node newPosHolder(const TokenPos& pos) { return NodeGeneric; } @@ -288,6 +298,7 @@ class SyntaxParseHandler MOZ_MUST_USE bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; } MOZ_MUST_USE bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; } MOZ_MUST_USE bool addShorthand(Node literal, Node name, Node expr) { return true; } + MOZ_MUST_USE bool addSpreadProperty(Node literal, uint32_t begin, Node inner) { return true; } MOZ_MUST_USE bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; } MOZ_MUST_USE bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; } Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } @@ -302,6 +313,16 @@ class SyntaxParseHandler MOZ_MUST_USE bool prependInitialYield(Node stmtList, Node gen) { return true; } Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; } + Node newExportDeclaration(Node kid, const TokenPos& pos) { + return NodeGeneric; + } + Node newExportFromDeclaration(uint32_t begin, Node exportSpecSet, Node moduleSpec) { + return NodeGeneric; + } + Node newExportDefaultDeclaration(Node kid, Node maybeBinding, const TokenPos& pos) { + return NodeGeneric; + } + Node newSetThis(Node thisName, Node value) { return value; } Node newExprStatement(Node expr, uint32_t end) { @@ -342,7 +363,10 @@ class SyntaxParseHandler void checkAndSetIsDirectRHSAnonFunction(Node pn) {} - Node newFunctionDefinition() { return NodeFunctionDefinition; } + Node newFunctionStatement() { return NodeFunctionDefinition; } + Node newFunctionExpression() { return NodeFunctionDefinition; } + Node newArrowFunction() { return NodeFunctionDefinition; } + bool setComprehensionLambdaBody(Node pn, Node body) { return true; } void setFunctionFormalParametersAndBody(Node pn, Node kid) {} void setFunctionBody(Node pn, Node kid) {} @@ -494,7 +518,7 @@ class SyntaxParseHandler return NodeParenthesizedArgumentsName; if (node == NodeUnparenthesizedEvalName) return NodeParenthesizedEvalName; - if (node == NodeUnparenthesizedName || node == NodeUnparenthesizedAsyncName) + if (node == NodeUnparenthesizedName || node == NodePotentialAsyncKeyword) return NodeParenthesizedName; if (node == NodeUnparenthesizedArray) @@ -519,15 +543,15 @@ class SyntaxParseHandler MOZ_MUST_USE Node setLikelyIIFE(Node pn) { return pn; // Remain in syntax-parse mode. } - void setPrologue(Node pn) {} + void setInDirectivePrologue(Node pn) {} bool isConstant(Node pn) { return false; } bool isUnparenthesizedName(Node node) { return node == NodeUnparenthesizedArgumentsName || - node == NodeUnparenthesizedAsyncName || node == NodeUnparenthesizedEvalName || - node == NodeUnparenthesizedName; + node == NodeUnparenthesizedName || + node == NodePotentialAsyncKeyword; } bool isNameAnyParentheses(Node node) { @@ -538,9 +562,11 @@ class SyntaxParseHandler node == NodeParenthesizedName; } - bool nameIsEvalAnyParentheses(Node node, ExclusiveContext* cx) { - MOZ_ASSERT(isNameAnyParentheses(node), - "must only call this function on known names"); + bool isArgumentsAnyParentheses(Node node, ExclusiveContext* cx) { + return node == NodeUnparenthesizedArgumentsName || node == NodeParenthesizedArgumentsName; + } + + bool isEvalAnyParentheses(Node node, ExclusiveContext* cx) { return node == NodeUnparenthesizedEvalName || node == NodeParenthesizedEvalName; } @@ -548,17 +574,15 @@ class SyntaxParseHandler MOZ_ASSERT(isNameAnyParentheses(node), "must only call this method on known names"); - if (nameIsEvalAnyParentheses(node, cx)) + if (isEvalAnyParentheses(node, cx)) return js_eval_str; - if (node == NodeUnparenthesizedArgumentsName || node == NodeParenthesizedArgumentsName) + if (isArgumentsAnyParentheses(node, cx)) return js_arguments_str; return nullptr; } - bool nameIsUnparenthesizedAsync(Node node, ExclusiveContext* cx) { - MOZ_ASSERT(isNameAnyParentheses(node), - "must only call this function on known names"); - return node == NodeUnparenthesizedAsyncName; + bool isAsyncKeyword(Node node, ExclusiveContext* cx) { + return node == NodePotentialAsyncKeyword; } PropertyName* maybeDottedProperty(Node node) { diff --git a/js/src/frontend/TokenKind.h b/js/src/frontend/TokenKind.h index 6f22d78e5f..98f23fec8d 100644 --- a/js/src/frontend/TokenKind.h +++ b/js/src/frontend/TokenKind.h @@ -81,9 +81,12 @@ \ macro(REGEXP, "regular expression literal") \ macro(TRUE, "boolean literal 'true'") \ + range(RESERVED_WORD_LITERAL_FIRST, TRUE) \ macro(FALSE, "boolean literal 'false'") \ macro(NULL, "null literal") \ + range(RESERVED_WORD_LITERAL_LAST, NULL) \ macro(THIS, "keyword 'this'") \ + range(KEYWORD_FIRST, THIS) \ macro(FUNCTION, "keyword 'function'") \ macro(IF, "keyword 'if'") \ macro(ELSE, "keyword 'else'") \ @@ -106,16 +109,43 @@ macro(FINALLY, "keyword 'finally'") \ macro(THROW, "keyword 'throw'") \ macro(DEBUGGER, "keyword 'debugger'") \ - macro(YIELD, "keyword 'yield'") \ - macro(AWAIT, "keyword 'await'") \ macro(EXPORT, "keyword 'export'") \ macro(IMPORT, "keyword 'import'") \ macro(CLASS, "keyword 'class'") \ macro(EXTENDS, "keyword 'extends'") \ macro(SUPER, "keyword 'super'") \ - macro(RESERVED, "reserved keyword") \ - /* reserved keywords in strict mode */ \ - macro(STRICT_RESERVED, "reserved keyword") \ + range(KEYWORD_LAST, SUPER) \ + \ + /* contextual keywords */ \ + macro(AS, "'as'") \ + range(CONTEXTUAL_KEYWORD_FIRST, AS) \ + macro(ASYNC, "'async'") \ + macro(AWAIT, "'await'") \ + macro(EACH, "'each'") \ + macro(FROM, "'from'") \ + macro(GET, "'get'") \ + macro(LET, "'let'") \ + macro(OF, "'of'") \ + macro(SET, "'set'") \ + macro(STATIC, "'static'") \ + macro(TARGET, "'target'") \ + macro(YIELD, "'yield'") \ + range(CONTEXTUAL_KEYWORD_LAST, YIELD) \ + \ + /* future reserved words */ \ + macro(ENUM, "reserved word 'enum'") \ + range(FUTURE_RESERVED_KEYWORD_FIRST, ENUM) \ + range(FUTURE_RESERVED_KEYWORD_LAST, ENUM) \ + \ + /* reserved words in strict mode */ \ + macro(IMPLEMENTS, "reserved word 'implements'") \ + range(STRICT_RESERVED_KEYWORD_FIRST, IMPLEMENTS) \ + macro(INTERFACE, "reserved word 'interface'") \ + macro(PACKAGE, "reserved word 'package'") \ + macro(PRIVATE, "reserved word 'private'") \ + macro(PROTECTED, "reserved word 'protected'") \ + macro(PUBLIC, "reserved word 'public'") \ + range(STRICT_RESERVED_KEYWORD_LAST, PUBLIC) \ \ /* \ * The following token types occupy contiguous ranges to enable easy \ @@ -149,7 +179,9 @@ range(RELOP_LAST, GE) \ \ macro(INSTANCEOF, "keyword 'instanceof'") \ + range(KEYWORD_BINOP_FIRST, INSTANCEOF) \ macro(IN, "keyword 'in'") \ + range(KEYWORD_BINOP_LAST, IN) \ \ /* Shift ops, per TokenKindIsShift. */ \ macro(LSH, "'<<'") \ @@ -168,7 +200,9 @@ \ /* Unary operation tokens. */ \ macro(TYPEOF, "keyword 'typeof'") \ + range(KEYWORD_UNOP_FIRST, TYPEOF) \ macro(VOID, "keyword 'void'") \ + range(KEYWORD_UNOP_LAST, VOID) \ macro(NOT, "'!'") \ macro(BITNOT, "'~'") \ \ @@ -239,6 +273,61 @@ TokenKindIsAssignment(TokenKind tt) return TOK_ASSIGNMENT_START <= tt && tt <= TOK_ASSIGNMENT_LAST; } +inline MOZ_MUST_USE bool +TokenKindIsKeyword(TokenKind tt) +{ + return (TOK_KEYWORD_FIRST <= tt && tt <= TOK_KEYWORD_LAST) || + (TOK_KEYWORD_BINOP_FIRST <= tt && tt <= TOK_KEYWORD_BINOP_LAST) || + (TOK_KEYWORD_UNOP_FIRST <= tt && tt <= TOK_KEYWORD_UNOP_LAST); +} + +inline MOZ_MUST_USE bool +TokenKindIsContextualKeyword(TokenKind tt) +{ + return TOK_CONTEXTUAL_KEYWORD_FIRST <= tt && tt <= TOK_CONTEXTUAL_KEYWORD_LAST; +} + +inline MOZ_MUST_USE bool +TokenKindIsFutureReservedWord(TokenKind tt) +{ + return TOK_FUTURE_RESERVED_KEYWORD_FIRST <= tt && tt <= TOK_FUTURE_RESERVED_KEYWORD_LAST; +} + +inline MOZ_MUST_USE bool +TokenKindIsStrictReservedWord(TokenKind tt) +{ + return TOK_STRICT_RESERVED_KEYWORD_FIRST <= tt && tt <= TOK_STRICT_RESERVED_KEYWORD_LAST; +} + +inline MOZ_MUST_USE bool +TokenKindIsReservedWordLiteral(TokenKind tt) +{ + return TOK_RESERVED_WORD_LITERAL_FIRST <= tt && tt <= TOK_RESERVED_WORD_LITERAL_LAST; +} + +inline MOZ_MUST_USE bool +TokenKindIsReservedWord(TokenKind tt) +{ + return TokenKindIsKeyword(tt) || + TokenKindIsFutureReservedWord(tt) || + TokenKindIsReservedWordLiteral(tt); +} + +inline MOZ_MUST_USE bool +TokenKindIsPossibleIdentifier(TokenKind tt) +{ + return tt == TOK_NAME || + TokenKindIsContextualKeyword(tt) || + TokenKindIsStrictReservedWord(tt); +} + +inline MOZ_MUST_USE bool +TokenKindIsPossibleIdentifierName(TokenKind tt) +{ + return TokenKindIsPossibleIdentifier(tt) || + TokenKindIsReservedWord(tt); +} + } // namespace frontend } // namespace js diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 179a7c2447..b8623d545e 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -8,6 +8,7 @@ #include "frontend/TokenStream.h" +#include "mozilla/ArrayUtils.h" #include "mozilla/IntegerTypeTraits.h" #include "mozilla/PodOperations.h" @@ -23,80 +24,81 @@ #include "jsnum.h" #include "frontend/BytecodeCompiler.h" +#include "frontend/ReservedWords.h" #include "js/CharacterEncoding.h" #include "js/UniquePtr.h" #include "vm/HelperThreads.h" -#include "vm/Keywords.h" #include "vm/StringBuffer.h" #include "vm/Unicode.h" using namespace js; using namespace js::frontend; +using mozilla::ArrayLength; using mozilla::Maybe; using mozilla::PodAssign; using mozilla::PodCopy; using mozilla::PodZero; -struct KeywordInfo { - const char* chars; // C string with keyword text +struct ReservedWordInfo { + const char* chars; // C string with reserved word text TokenKind tokentype; }; -static const KeywordInfo keywords[] = { -#define KEYWORD_INFO(keyword, name, type) \ - {js_##keyword##_str, type}, - FOR_EACH_JAVASCRIPT_KEYWORD(KEYWORD_INFO) -#undef KEYWORD_INFO +static const ReservedWordInfo reservedWords[] = { +#define RESERVED_WORD_INFO(word, name, type) \ + {js_##word##_str, type}, + FOR_EACH_JAVASCRIPT_RESERVED_WORD(RESERVED_WORD_INFO) +#undef RESERVED_WORD_INFO }; -// Returns a KeywordInfo for the specified characters, or nullptr if the string -// is not a keyword. +// Returns a ReservedWordInfo for the specified characters, or nullptr if the +// string is not a reserved word. template <typename CharT> -static const KeywordInfo* -FindKeyword(const CharT* s, size_t length) +static const ReservedWordInfo* +FindReservedWord(const CharT* s, size_t length) { MOZ_ASSERT(length != 0); size_t i; - const KeywordInfo* kw; + const ReservedWordInfo* rw; const char* chars; -#define JSKW_LENGTH() length -#define JSKW_AT(column) s[column] -#define JSKW_GOT_MATCH(index) i = (index); goto got_match; -#define JSKW_TEST_GUESS(index) i = (index); goto test_guess; -#define JSKW_NO_MATCH() goto no_match; -#include "jsautokw.h" -#undef JSKW_NO_MATCH -#undef JSKW_TEST_GUESS -#undef JSKW_GOT_MATCH -#undef JSKW_AT -#undef JSKW_LENGTH +#define JSRW_LENGTH() length +#define JSRW_AT(column) s[column] +#define JSRW_GOT_MATCH(index) i = (index); goto got_match; +#define JSRW_TEST_GUESS(index) i = (index); goto test_guess; +#define JSRW_NO_MATCH() goto no_match; +#include "frontend/ReservedWordsGenerated.h" +#undef JSRW_NO_MATCH +#undef JSRW_TEST_GUESS +#undef JSRW_GOT_MATCH +#undef JSRW_AT +#undef JSRW_LENGTH got_match: - return &keywords[i]; + return &reservedWords[i]; test_guess: - kw = &keywords[i]; - chars = kw->chars; + rw = &reservedWords[i]; + chars = rw->chars; do { if (*s++ != (unsigned char)(*chars++)) goto no_match; } while (--length != 0); - return kw; + return rw; no_match: return nullptr; } -static const KeywordInfo* -FindKeyword(JSLinearString* str) +static const ReservedWordInfo* +FindReservedWord(JSLinearString* str) { JS::AutoCheckCannotGC nogc; return str->hasLatin1Chars() - ? FindKeyword(str->latin1Chars(nogc), str->length()) - : FindKeyword(str->twoByteChars(nogc), str->length()); + ? FindReservedWord(str->latin1Chars(nogc), str->length()) + : FindReservedWord(str->twoByteChars(nogc), str->length()); } template <typename CharT> @@ -172,6 +174,12 @@ frontend::IsIdentifier(JSLinearString* str) } bool +frontend::IsIdentifier(const char* chars, size_t length) +{ + return ::IsIdentifier(chars, length); +} + +bool frontend::IsIdentifier(const char16_t* chars, size_t length) { return ::IsIdentifier(chars, length); @@ -180,7 +188,68 @@ frontend::IsIdentifier(const char16_t* chars, size_t length) bool frontend::IsKeyword(JSLinearString* str) { - return FindKeyword(str) != nullptr; + if (const ReservedWordInfo* rw = FindReservedWord(str)) + return TokenKindIsKeyword(rw->tokentype); + + return false; +} + +bool +frontend::IsFutureReservedWord(JSLinearString* str) +{ + if (const ReservedWordInfo* rw = FindReservedWord(str)) + return TokenKindIsFutureReservedWord(rw->tokentype); + + return false; +} + +bool +frontend::IsStrictReservedWord(JSLinearString* str) +{ + if (const ReservedWordInfo* rw = FindReservedWord(str)) + return TokenKindIsStrictReservedWord(rw->tokentype); + + return false; +} + +bool +frontend::IsReservedWordLiteral(JSLinearString* str) +{ + if (const ReservedWordInfo* rw = FindReservedWord(str)) + return TokenKindIsReservedWordLiteral(rw->tokentype); + + return false; +} + +const char* +frontend::ReservedWordToCharZ(PropertyName* str) +{ + const ReservedWordInfo* rw = FindReservedWord(str); + if (rw == nullptr) + return nullptr; + + switch (rw->tokentype) { +#define EMIT_CASE(word, name, type) case type: return js_##word##_str; + FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE) +#undef EMIT_CASE + default: + MOZ_ASSERT_UNREACHABLE("Not a reserved word PropertyName."); + } + return nullptr; +} + +PropertyName* +TokenStream::reservedWordToPropertyName(TokenKind tt) const +{ + MOZ_ASSERT(tt != TOK_NAME); + switch (tt) { +#define EMIT_CASE(word, name, type) case type: return cx->names().name; + FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE) +#undef EMIT_CASE + default: + MOZ_ASSERT_UNREACHABLE("Not a reserved word TokenKind."); + } + return nullptr; } TokenStream::SourceCoords::SourceCoords(ExclusiveContext* cx, uint32_t ln) @@ -217,8 +286,13 @@ TokenStream::SourceCoords::add(uint32_t lineNum, uint32_t lineStartOffset) // only if lineStartOffsets_.append succeeds, to keep sentinel. // Otherwise return false to tell TokenStream about OOM. uint32_t maxPtr = MAX_PTR; - if (!lineStartOffsets_.append(maxPtr)) + if (!lineStartOffsets_.append(maxPtr)) { + static_assert(mozilla::IsSame<decltype(lineStartOffsets_.allocPolicy()), + TempAllocPolicy&>::value, + "this function's caller depends on it reporting an " + "error on failure, as TempAllocPolicy ensures"); return false; + } lineStartOffsets_[lineIndex] = lineStartOffset; } else { @@ -548,8 +622,9 @@ TokenStream::advance(size_t position) MOZ_MAKE_MEM_UNDEFINED(&cur->type, sizeof(cur->type)); lookahead = 0; - if (flags.hitOOM) - return reportError(JSMSG_OUT_OF_MEMORY); + if (flags.hitOOM) { + return false; + } return true; } @@ -593,8 +668,8 @@ TokenStream::seek(const Position& pos, const TokenStream& other) } bool -TokenStream::reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, unsigned errorNumber, - va_list args) +TokenStream::reportStrictModeErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset, + bool strictMode, unsigned errorNumber, va_list args) { // In strict mode code, this is an error, not merely a warning. unsigned flags; @@ -605,7 +680,7 @@ TokenStream::reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, uns else return true; - return reportCompileErrorNumberVA(offset, flags, errorNumber, args); + return reportCompileErrorNumberVA(Move(notes), offset, flags, errorNumber, args); } void @@ -631,8 +706,8 @@ CompileError::throwError(JSContext* cx) } bool -TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigned errorNumber, - va_list args) +TokenStream::reportCompileErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset, + unsigned flags, unsigned errorNumber, va_list args) { bool warning = JSREPORT_IS_WARNING(flags); @@ -649,6 +724,7 @@ TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigne return false; CompileError& err = *tempErrPtr; + err.notes = Move(notes); err.flags = flags; err.errorNumber = errorNumber; err.filename = filename; @@ -740,7 +816,7 @@ TokenStream::reportStrictModeError(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = reportStrictModeErrorNumberVA(currentToken().pos.begin, strictMode(), + bool result = reportStrictModeErrorNumberVA(nullptr, currentToken().pos.begin, strictMode(), errorNumber, args); va_end(args); return result; @@ -751,8 +827,8 @@ TokenStream::reportError(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = reportCompileErrorNumberVA(currentToken().pos.begin, JSREPORT_ERROR, errorNumber, - args); + bool result = reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_ERROR, + errorNumber, args); va_end(args); return result; } @@ -762,30 +838,32 @@ TokenStream::reportErrorNoOffset(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = reportCompileErrorNumberVA(NoOffset, JSREPORT_ERROR, errorNumber, - args); + bool result = reportCompileErrorNumberVA(nullptr, NoOffset, JSREPORT_ERROR, + errorNumber, args); va_end(args); return result; } bool -TokenStream::reportWarning(unsigned errorNumber, ...) +TokenStream::warning(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = reportCompileErrorNumberVA(currentToken().pos.begin, JSREPORT_WARNING, + bool result = reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_WARNING, errorNumber, args); va_end(args); return result; } bool -TokenStream::reportStrictWarningErrorNumberVA(uint32_t offset, unsigned errorNumber, va_list args) +TokenStream::reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset, + unsigned errorNumber, va_list args) { if (!options().extraWarningsOption) return true; - return reportCompileErrorNumberVA(offset, JSREPORT_STRICT|JSREPORT_WARNING, errorNumber, args); + return reportCompileErrorNumberVA(Move(notes), offset, JSREPORT_STRICT|JSREPORT_WARNING, + errorNumber, args); } void @@ -796,7 +874,34 @@ TokenStream::reportAsmJSError(uint32_t offset, unsigned errorNumber, ...) unsigned flags = options().throwOnAsmJSValidationFailureOption ? JSREPORT_ERROR : JSREPORT_WARNING; - reportCompileErrorNumberVA(offset, flags, errorNumber, args); + reportCompileErrorNumberVA(nullptr, offset, flags, errorNumber, args); + va_end(args); +} + +void +TokenStream::error(unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); +#ifdef DEBUG + bool result = +#endif + reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_ERROR, + errorNumber, args); + MOZ_ASSERT(!result, "reporting an error returned true?"); + va_end(args); +} + +void +TokenStream::errorAt(uint32_t offset, unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); +#ifdef DEBUG + bool result = +#endif + reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR, errorNumber, args); + MOZ_ASSERT(!result, "reporting an error returned true?"); va_end(args); } @@ -928,34 +1033,49 @@ TokenStream::getDirectives(bool isMultiline, bool shouldWarnDeprecated) bool TokenStream::getDirective(bool isMultiline, bool shouldWarnDeprecated, - const char* directive, int directiveLength, + const char* directive, uint8_t directiveLength, const char* errorMsgPragma, UniqueTwoByteChars* destination) { MOZ_ASSERT(directiveLength <= 18); char16_t peeked[18]; - int32_t c; if (peekChars(directiveLength, peeked) && CharsMatch(peeked, directive)) { - if (shouldWarnDeprecated && - !reportWarning(JSMSG_DEPRECATED_PRAGMA, errorMsgPragma)) - return false; + if (shouldWarnDeprecated) { + if (!warning(JSMSG_DEPRECATED_PRAGMA, errorMsgPragma)) + return false; + } skipChars(directiveLength); tokenbuf.clear(); - while ((c = peekChar()) && c != EOF && !unicode::IsSpaceOrBOM2(c)) { - getChar(); + do { + int32_t c; + if (!peekChar(&c)) + return false; + + if (c == EOF || unicode::IsSpaceOrBOM2(c)) + break; + + consumeKnownChar(c); + // Debugging directives can occur in both single- and multi-line // comments. If we're currently inside a multi-line comment, we also // need to recognize multi-line comment terminators. - if (isMultiline && c == '*' && peekChar() == '/') { - ungetChar('*'); - break; + if (isMultiline && c == '*') { + int32_t c2; + if (!peekChar(&c2)) + return false; + + if (c2 == '/') { + ungetChar('*'); + break; + } } + if (!tokenbuf.append(c)) return false; - } + } while (true); if (tokenbuf.empty()) { // The directive's URL was missing, but this is not quite an @@ -987,7 +1107,10 @@ TokenStream::getDisplayURL(bool isMultiline, bool shouldWarnDeprecated) // developer would like to refer to the source as from the source's actual // URL. - return getDirective(isMultiline, shouldWarnDeprecated, " sourceURL=", 11, + static const char sourceURLDirective[] = " sourceURL="; + constexpr uint8_t sourceURLDirectiveLength = ArrayLength(sourceURLDirective) - 1; + return getDirective(isMultiline, shouldWarnDeprecated, + sourceURLDirective, sourceURLDirectiveLength, "sourceURL", &displayURL_); } @@ -997,7 +1120,10 @@ TokenStream::getSourceMappingURL(bool isMultiline, bool shouldWarnDeprecated) // Match comments of the form "//# sourceMappingURL=<url>" or // "/\* //# sourceMappingURL=<url> *\/" - return getDirective(isMultiline, shouldWarnDeprecated, " sourceMappingURL=", 18, + static const char sourceMappingURLDirective[] = " sourceMappingURL="; + constexpr uint8_t sourceMappingURLDirectiveLength = ArrayLength(sourceMappingURLDirective) - 1; + return getDirective(isMultiline, shouldWarnDeprecated, + sourceMappingURLDirective, sourceMappingURLDirectiveLength, "sourceMappingURL", &sourceMapURL_); } @@ -1104,36 +1230,6 @@ TokenStream::putIdentInTokenbuf(const char16_t* identStart) return true; } -bool -TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp) -{ - if (!awaitIsKeyword && kw->tokentype == TOK_AWAIT) { - if (ttp) - *ttp = TOK_NAME; - return true; - } - - if (kw->tokentype == TOK_RESERVED) - return reportError(JSMSG_RESERVED_ID, kw->chars); - - if (kw->tokentype == TOK_STRICT_RESERVED) - return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars); - - // Working keyword. - *ttp = kw->tokentype; - return true; -} - -bool -TokenStream::checkForKeyword(JSAtom* atom, TokenKind* ttp) -{ - const KeywordInfo* kw = FindKeyword(atom); - if (!kw) - return true; - - return checkForKeyword(kw, ttp); -} - enum FirstCharKind { // A char16_t has the 'OneChar' kind if it, by itself, constitutes a valid // token that cannot also be a prefix of a longer token. E.g. ';' has the @@ -1357,36 +1453,18 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) length = userbuf.addressOfNextRawChar() - identStart; } - // Represent keywords as keyword tokens unless told otherwise. - if (modifier != KeywordIsName) { - if (const KeywordInfo* kw = FindKeyword(chars, length)) { - // That said, keywords can't contain escapes. (Contexts where - // keywords are treated as names, that also sometimes treat - // keywords as keywords, must manually check this requirement.) - // There are two exceptions - // 1) StrictReservedWords: These keywords need to be treated as - // names in non-strict mode. - // 2) yield is also treated as a name if it contains an escape - // sequence. The parser must handle this case separately. - if (hadUnicodeEscape && !( - (kw->tokentype == TOK_STRICT_RESERVED && !strictMode()) || - kw->tokentype == TOK_YIELD)) - { - reportError(JSMSG_ESCAPED_KEYWORD); - goto error; - } - - tp->type = TOK_NAME; - if (!checkForKeyword(kw, &tp->type)) - goto error; - if (tp->type != TOK_NAME && !hadUnicodeEscape) - goto out; + // Represent reserved words as reserved word tokens. + if (!hadUnicodeEscape) { + if (const ReservedWordInfo* rw = FindReservedWord(chars, length)) { + tp->type = rw->tokentype; + goto out; } } JSAtom* atom = AtomizeChars(cx, chars, length); - if (!atom) + if (!atom) { goto error; + } tp->type = TOK_NAME; tp->setName(atom->asPropertyName()); goto out; @@ -1532,10 +1610,11 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) // grammar. We might not always be so permissive, so we warn // about it. if (c >= '8') { - if (!reportWarning(JSMSG_BAD_OCTAL, c == '8' ? "08" : "09")) { + if (!warning(JSMSG_BAD_OCTAL, c == '8' ? "08" : "09")) goto error; - } - goto decimal; // use the decimal scanner for the rest of the number + + // Use the decimal scanner for the rest of the number. + goto decimal; } c = getCharIgnoreEOL(); } @@ -1684,7 +1763,8 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) case '/': // Look for a single-line comment. if (matchChar('/')) { - c = peekChar(); + if (!peekChar(&c)) + goto error; if (c == '@' || c == '#') { bool shouldWarn = getChar() == '@'; if (!getDirectives(false, shouldWarn)) @@ -1751,7 +1831,8 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) RegExpFlag reflags = NoFlags; unsigned length = tokenbuf.length() + 1; while (true) { - c = peekChar(); + if (!peekChar(&c)) + goto error; if (c == 'g' && !(reflags & GlobalFlag)) reflags = RegExpFlag(reflags | GlobalFlag); else if (c == 'i' && !(reflags & IgnoreCaseFlag)) @@ -1768,7 +1849,8 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) length++; } - c = peekChar(); + if (!peekChar(&c)) + goto error; if (JS7_ISLET(c)) { char buf[2] = { '\0', '\0' }; tp->pos.begin += length + 1; @@ -1791,8 +1873,13 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) case '-': if (matchChar('-')) { - if (peekChar() == '>' && !flags.isDirtyLine) + int32_t c2; + if (!peekChar(&c2)) + goto error; + + if (c2 == '>' && !flags.isDirtyLine) goto skipline; + tp->type = TOK_DEC; } else { tp->type = matchChar('=') ? TOK_SUBASSIGN : TOK_SUB; @@ -1808,8 +1895,9 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) MOZ_CRASH("should have jumped to |out| or |error|"); out: - if (flags.hitOOM) - return reportError(JSMSG_OUT_OF_MEMORY); + if (flags.hitOOM) { + return false; + } flags.isDirtyLine = true; tp->pos.end = userbuf.offset(); @@ -1825,8 +1913,9 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) return true; error: - if (flags.hitOOM) - return reportError(JSMSG_OUT_OF_MEMORY); + if (flags.hitOOM) { + return false; + } flags.isDirtyLine = true; tp->pos.end = userbuf.offset(); @@ -1844,37 +1933,6 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) } bool -TokenStream::getBracedUnicode(uint32_t* cp) -{ - consumeKnownChar('{'); - - bool first = true; - int32_t c; - uint32_t code = 0; - while (true) { - c = getCharIgnoreEOL(); - if (c == EOF) - return false; - if (c == '}') { - if (first) - return false; - break; - } - - if (!JS7_ISHEX(c)) - return false; - - code = (code << 4) | JS7_UNHEX(c); - if (code > unicode::NonBMPMax) - return false; - first = false; - } - - *cp = code; - return true; -} - -bool TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) { int c; @@ -1891,11 +1949,15 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) while ((c = getCharIgnoreEOL()) != untilChar) { if (c == EOF) { ungetCharIgnoreEOL(c); - reportError(JSMSG_UNTERMINATED_STRING); + error(JSMSG_UNTERMINATED_STRING); return false; } if (c == '\\') { + // When parsing templates, we don't immediately report errors for + // invalid escapes; these are handled by the parser. + // In those cases we don't append to tokenbuf, since it won't be + // read. switch (c = getChar()) { case 'b': c = '\b'; break; case 'f': c = '\f'; break; @@ -1911,12 +1973,73 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) // Unicode character specification. case 'u': { - if (peekChar() == '{') { - uint32_t code; - if (!getBracedUnicode(&code)) { - reportError(JSMSG_MALFORMED_ESCAPE, "Unicode"); - return false; - } + uint32_t code = 0; + + int32_t c2; + if (!peekChar(&c2)) + return false; + + uint32_t start = userbuf.offset() - 2; + + if (c2 == '{') { + consumeKnownChar('{'); + + bool first = true; + bool valid = true; + do { + int32_t c = getCharIgnoreEOL(); + if (c == EOF) { + if (parsingTemplate) { + setInvalidTemplateEscape(start, InvalidEscapeType::Unicode); + valid = false; + break; + } + reportInvalidEscapeError(start, InvalidEscapeType::Unicode); + return false; + } + if (c == '}') { + if (first) { + if (parsingTemplate) { + setInvalidTemplateEscape(start, InvalidEscapeType::Unicode); + valid = false; + break; + } + reportInvalidEscapeError(start, InvalidEscapeType::Unicode); + return false; + } + break; + } + + if (!JS7_ISHEX(c)) { + if (parsingTemplate) { + // We put the character back so that we read + // it on the next pass, which matters if it + // was '`' or '\'. + ungetCharIgnoreEOL(c); + setInvalidTemplateEscape(start, InvalidEscapeType::Unicode); + valid = false; + break; + } + reportInvalidEscapeError(start, InvalidEscapeType::Unicode); + return false; + } + + code = (code << 4) | JS7_UNHEX(c); + if (code > unicode::NonBMPMax) { + if (parsingTemplate) { + setInvalidTemplateEscape(start + 3, InvalidEscapeType::UnicodeOverflow); + valid = false; + break; + } + reportInvalidEscapeError(start + 3, InvalidEscapeType::UnicodeOverflow); + return false; + } + + first = false; + } while (true); + + if (!valid) + continue; MOZ_ASSERT(code <= unicode::NonBMPMax); if (code < unicode::NonBMPMin) { @@ -1939,7 +2062,11 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) c = (c << 4) + JS7_UNHEX(cp[3]); skipChars(4); } else { - reportError(JSMSG_MALFORMED_ESCAPE, "Unicode"); + if (parsingTemplate) { + setInvalidTemplateEscape(start, InvalidEscapeType::Unicode); + continue; + } + reportInvalidEscapeError(start, InvalidEscapeType::Unicode); return false; } break; @@ -1952,7 +2079,12 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]); skipChars(2); } else { - reportError(JSMSG_MALFORMED_ESCAPE, "hexadecimal"); + uint32_t start = userbuf.offset() - 2; + if (parsingTemplate) { + setInvalidTemplateEscape(start, InvalidEscapeType::Hexadecimal); + continue; + } + reportInvalidEscapeError(start, InvalidEscapeType::Hexadecimal); return false; } break; @@ -1963,13 +2095,14 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) if (JS7_ISOCT(c)) { int32_t val = JS7_UNOCT(c); - c = peekChar(); + if (!peekChar(&c)) + return false; // Strict mode code allows only \0, then a non-digit. if (val != 0 || JS7_ISDEC(c)) { if (parsingTemplate) { - reportError(JSMSG_DEPRECATED_OCTAL); - return false; + setInvalidTemplateEscape(userbuf.offset() - 2, InvalidEscapeType::Octal); + continue; } if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL)) return false; @@ -1979,7 +2112,8 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) if (JS7_ISOCT(c)) { val = 8 * val + JS7_UNOCT(c); getChar(); - c = peekChar(); + if (!peekChar(&c)) + return false; if (JS7_ISOCT(c)) { int32_t save = val; val = 8 * val + JS7_UNOCT(c); @@ -1997,7 +2131,7 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) } else if (TokenBuf::isRawEOLChar(c)) { if (!parsingTemplate) { ungetCharIgnoreEOL(c); - reportError(JSMSG_UNTERMINATED_STRING); + error(JSMSG_UNTERMINATED_STRING); return false; } if (c == '\r') { diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 5d6b4b795a..2744fd144a 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -26,14 +26,13 @@ #include "js/UniquePtr.h" #include "js/Vector.h" #include "vm/RegExpObject.h" +#include "vm/String.h" struct KeywordInfo; namespace js { namespace frontend { -class AutoAwaitIsKeyword; - struct TokenPos { uint32_t begin; // Offset of the token's first char. uint32_t end; // Offset of 1 past the token's last char. @@ -80,6 +79,20 @@ struct TokenPos { enum DecimalPoint { NoDecimal = false, HasDecimal = true }; +enum class InvalidEscapeType { + // No invalid character escapes. + None, + // A malformed \x escape. + Hexadecimal, + // A malformed \u escape. + Unicode, + // An otherwise well-formed \u escape which represents a + // codepoint > 10FFFF. + UnicodeOverflow, + // An octal escape in a template token. + Octal +}; + class TokenStream; struct Token @@ -106,9 +119,6 @@ struct Token // TOK_DIV. Operand, - // Treat keywords as names by returning TOK_NAME. - KeywordIsName, - // Treat subsequent characters as the tail of a template literal, after // a template substitution, beginning with a "}", continuing with zero // or more template literal characters, and ending with either "${" or @@ -150,10 +160,6 @@ struct Token // If a semicolon is inserted automatically, the next token is already // gotten with None, but we expect Operand. OperandIsNone, - - // If name of method definition is `get` or `set`, the next token is - // already gotten with KeywordIsName, but we expect None. - NoneIsKeywordIsName, }; friend class TokenStream; @@ -210,11 +216,6 @@ struct Token return u.name->JSAtom::asPropertyName(); // poor-man's type verification } - bool nameContainsEscape() const { - PropertyName* n = name(); - return pos.begin + n->length() != pos.end; - } - JSAtom* atom() const { MOZ_ASSERT(type == TOK_STRING || type == TOK_TEMPLATE_HEAD || @@ -240,10 +241,22 @@ struct Token }; class CompileError : public JSErrorReport { -public: + public: void throwError(JSContext* cx); }; +extern const char* +ReservedWordToCharZ(PropertyName* str); + +extern MOZ_MUST_USE bool +IsFutureReservedWord(JSLinearString* str); + +extern MOZ_MUST_USE bool +IsReservedWordLiteral(JSLinearString* str); + +extern MOZ_MUST_USE bool +IsStrictReservedWord(JSLinearString* str); + // Ideally, tokenizing would be entirely independent of context. But the // strict mode flag, which is in SharedContext, affects tokenizing, and // TokenStream needs to see it. @@ -330,25 +343,26 @@ class MOZ_STACK_CLASS TokenStream JSVersion versionNumber() const { return VersionNumber(options().version); } JSVersion versionWithFlags() const { return options().version; } + private: + PropertyName* reservedWordToPropertyName(TokenKind tt) const; + + public: PropertyName* currentName() const { - if (isCurrentTokenType(TOK_YIELD)) - return cx->names().yield; - MOZ_ASSERT(isCurrentTokenType(TOK_NAME)); - return currentToken().name(); + if (isCurrentTokenType(TOK_NAME)) { + return currentToken().name(); + } + + MOZ_ASSERT(TokenKindIsPossibleIdentifierName(currentToken().type)); + return reservedWordToPropertyName(currentToken().type); } PropertyName* nextName() const { - if (nextToken().type == TOK_YIELD) - return cx->names().yield; - MOZ_ASSERT(nextToken().type == TOK_NAME); - return nextToken().name(); - } + if (nextToken().type != TOK_NAME) { + return nextToken().name(); + } - bool nextNameContainsEscape() const { - if (nextToken().type == TOK_YIELD) - return false; - MOZ_ASSERT(nextToken().type == TOK_NAME); - return nextToken().nameContainsEscape(); + MOZ_ASSERT(TokenKindIsPossibleIdentifierName(nextToken().type)); + return reservedWordToPropertyName(nextToken().type); } bool isCurrentTokenAssignment() const { @@ -361,22 +375,47 @@ class MOZ_STACK_CLASS TokenStream bool hadError() const { return flags.hadError; } void clearSawOctalEscape() { flags.sawOctalEscape = false; } + bool hasInvalidTemplateEscape() const { + return invalidTemplateEscapeType != InvalidEscapeType::None; + } + void clearInvalidTemplateEscape() { + invalidTemplateEscapeType = InvalidEscapeType::None; + } + + // If there is an invalid escape in a template, report it and return false, + // otherwise return true. + bool checkForInvalidTemplateEscapeError() { + if (invalidTemplateEscapeType == InvalidEscapeType::None) + return true; + + reportInvalidEscapeError(invalidTemplateEscapeOffset, invalidTemplateEscapeType); + return false; + } + // TokenStream-specific error reporters. bool reportError(unsigned errorNumber, ...); bool reportErrorNoOffset(unsigned errorNumber, ...); - bool reportWarning(unsigned errorNumber, ...); + + // Report the given error at the current offset. + void error(unsigned errorNumber, ...); + + // Report the given error at the given offset. + void errorAt(uint32_t offset, unsigned errorNumber, ...); + + // Warn at the current offset. + MOZ_MUST_USE bool warning(unsigned errorNumber, ...); static const uint32_t NoOffset = UINT32_MAX; // General-purpose error reporters. You should avoid calling these - // directly, and instead use the more succinct alternatives (e.g. - // reportError()) in TokenStream, Parser, and BytecodeEmitter. - bool reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigned errorNumber, - va_list args); - bool reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, unsigned errorNumber, - va_list args); - bool reportStrictWarningErrorNumberVA(uint32_t offset, unsigned errorNumber, - va_list args); + // directly, and instead use the more succinct alternatives (error(), + // warning(), &c.) in TokenStream, Parser, and BytecodeEmitter. + bool reportCompileErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset, unsigned flags, + unsigned errorNumber, va_list args); + bool reportStrictModeErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset, + bool strictMode, unsigned errorNumber, va_list args); + bool reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset, + unsigned errorNumber, va_list args); // asm.js reporter void reportAsmJSError(uint32_t offset, unsigned errorNumber, ...); @@ -415,6 +454,33 @@ class MOZ_STACK_CLASS TokenStream bool reportStrictModeError(unsigned errorNumber, ...); bool strictMode() const { return strictModeGetter && strictModeGetter->strictMode(); } + void setInvalidTemplateEscape(uint32_t offset, InvalidEscapeType type) { + MOZ_ASSERT(type != InvalidEscapeType::None); + if (invalidTemplateEscapeType != InvalidEscapeType::None) + return; + invalidTemplateEscapeOffset = offset; + invalidTemplateEscapeType = type; + } + void reportInvalidEscapeError(uint32_t offset, InvalidEscapeType type) { + switch (type) { + case InvalidEscapeType::None: + MOZ_ASSERT_UNREACHABLE("unexpected InvalidEscapeType"); + return; + case InvalidEscapeType::Hexadecimal: + errorAt(offset, JSMSG_MALFORMED_ESCAPE, "hexadecimal"); + return; + case InvalidEscapeType::Unicode: + errorAt(offset, JSMSG_MALFORMED_ESCAPE, "Unicode"); + return; + case InvalidEscapeType::UnicodeOverflow: + errorAt(offset, JSMSG_UNICODE_OVERFLOW, "escape sequence"); + return; + case InvalidEscapeType::Octal: + errorAt(offset, JSMSG_DEPRECATED_OCTAL); + return; + } + } + static JSAtom* atomize(ExclusiveContext* cx, CharBuffer& cb); MOZ_MUST_USE bool putIdentInTokenbuf(const char16_t* identStart); @@ -432,21 +498,19 @@ class MOZ_STACK_CLASS TokenStream {} }; - bool awaitIsKeyword = false; - friend class AutoAwaitIsKeyword; + uint32_t invalidTemplateEscapeOffset = 0; + InvalidEscapeType invalidTemplateEscapeType = InvalidEscapeType::None; public: typedef Token::Modifier Modifier; static constexpr Modifier None = Token::None; static constexpr Modifier Operand = Token::Operand; - static constexpr Modifier KeywordIsName = Token::KeywordIsName; static constexpr Modifier TemplateTail = Token::TemplateTail; typedef Token::ModifierException ModifierException; static constexpr ModifierException NoException = Token::NoException; static constexpr ModifierException NoneIsOperand = Token::NoneIsOperand; static constexpr ModifierException OperandIsNone = Token::OperandIsNone; - static constexpr ModifierException NoneIsKeywordIsName = Token::NoneIsKeywordIsName; void addModifierException(ModifierException modifierException) { #ifdef DEBUG @@ -475,10 +539,6 @@ class MOZ_STACK_CLASS TokenStream MOZ_ASSERT(next.type != TOK_DIV && next.type != TOK_REGEXP, "next token requires contextual specifier to be parsed unambiguously"); break; - case NoneIsKeywordIsName: - MOZ_ASSERT(next.modifier == KeywordIsName); - MOZ_ASSERT(next.type != TOK_NAME); - break; default: MOZ_CRASH("unexpected modifier exception"); } @@ -505,18 +565,17 @@ class MOZ_STACK_CLASS TokenStream return; } - if (lookaheadToken.modifierException == NoneIsKeywordIsName) { - // getToken() permissibly following getToken(KeywordIsName). - if (modifier == None && lookaheadToken.modifier == KeywordIsName) - return; - } - MOZ_ASSERT_UNREACHABLE("this token was previously looked up with a " "different modifier, potentially making " "tokenization non-deterministic"); #endif } + const Token& nextToken() const { + MOZ_ASSERT(hasLookahead()); + return tokens[(cursor + 1) & ntokensMask]; + } + // Advance to the next token. If the token stream encountered an error, // return false. Otherwise return true and store the token kind in |*ttp|. MOZ_MUST_USE bool getToken(TokenKind* ttp, Modifier modifier = None) { @@ -570,6 +629,14 @@ class MOZ_STACK_CLASS TokenStream return true; } + MOZ_MUST_USE bool peekOffset(uint32_t* offset, Modifier modifier = None) { + TokenPos pos; + if (!peekTokenPos(&pos, modifier)) + return false; + *offset = pos.begin; + return true; + } + // This is like peekToken(), with one exception: if there is an EOL // between the end of the current token and the start of the next token, it // return true and store TOK_EOL in |*ttp|. In that case, no token with @@ -637,36 +704,6 @@ class MOZ_STACK_CLASS TokenStream MOZ_ALWAYS_TRUE(matched); } - // Like matchToken(..., TOK_NAME) but further matching the name token only - // if it has the given characters, without containing escape sequences. - // If the name token has the given characters yet *does* contain an escape, - // a syntax error will be reported. - // - // This latter behavior makes this method unsuitable for use in any context - // where ASI might occur. In such places, an escaped "contextual keyword" - // on a new line is the start of an ExpressionStatement, not a continuation - // of a StatementListItem (or ImportDeclaration or ExportDeclaration, in - // modules). - MOZ_MUST_USE bool matchContextualKeyword(bool* matchedp, Handle<PropertyName*> keyword, - Modifier modifier = None) - { - TokenKind token; - if (!getToken(&token, modifier)) - return false; - if (token == TOK_NAME && currentToken().name() == keyword) { - if (currentToken().nameContainsEscape()) { - reportError(JSMSG_ESCAPED_KEYWORD); - return false; - } - - *matchedp = true; - } else { - *matchedp = false; - ungetToken(); - } - return true; - } - MOZ_MUST_USE bool nextTokenEndsExpr(bool* endsExpr) { TokenKind tt; if (!peekToken(&tt)) @@ -732,19 +769,6 @@ class MOZ_STACK_CLASS TokenStream return sourceMapURL_.get(); } - // If |atom| is not a keyword in this version, return true with *ttp - // unchanged. - // - // If it is a reserved word in this version and strictness mode, and thus - // can't be present in correct code, report a SyntaxError and return false. - // - // If it is a keyword, like "if", return true with the keyword's TokenKind - // in *ttp. - MOZ_MUST_USE bool checkForKeyword(JSAtom* atom, TokenKind* ttp); - - // Same semantics as above, but for the provided keyword. - MOZ_MUST_USE bool checkForKeyword(const KeywordInfo* kw, TokenKind* ttp); - // This class maps a userbuf offset (which is 0-indexed) to a line number // (which is 1-indexed) and a column index (which is 0-indexed). class SourceCoords @@ -940,7 +964,6 @@ class MOZ_STACK_CLASS TokenStream MOZ_MUST_USE bool getTokenInternal(TokenKind* ttp, Modifier modifier); - MOZ_MUST_USE bool getBracedUnicode(uint32_t* code); MOZ_MUST_USE bool getStringOrTemplateToken(int untilChar, Token** tp); int32_t getChar(); @@ -957,7 +980,7 @@ class MOZ_STACK_CLASS TokenStream MOZ_MUST_USE bool getDirectives(bool isMultiline, bool shouldWarnDeprecated); MOZ_MUST_USE bool getDirective(bool isMultiline, bool shouldWarnDeprecated, - const char* directive, int directiveLength, + const char* directive, uint8_t directiveLength, const char* errorMsgPragma, UniquePtr<char16_t[], JS::FreePolicy>* destination); MOZ_MUST_USE bool getDisplayURL(bool isMultiline, bool shouldWarnDeprecated); @@ -975,29 +998,30 @@ class MOZ_STACK_CLASS TokenStream MOZ_ASSERT(c == expect); } - int32_t peekChar() { - int32_t c = getChar(); - ungetChar(c); - return c; + MOZ_MUST_USE bool peekChar(int32_t* c) { + *c = getChar(); + ungetChar(*c); + return true; } - void skipChars(int n) { - while (--n >= 0) - getChar(); + void skipChars(uint8_t n) { + while (n-- > 0) { + MOZ_ASSERT(userbuf.hasRawChars()); + mozilla::DebugOnly<int32_t> c = getCharIgnoreEOL(); + MOZ_ASSERT(c != '\n'); + } } - void skipCharsIgnoreEOL(int n) { - while (--n >= 0) + void skipCharsIgnoreEOL(uint8_t n) { + while (n-- > 0) { + MOZ_ASSERT(userbuf.hasRawChars()); getCharIgnoreEOL(); + } } void updateLineInfoForEOL(); void updateFlagsForEOL(); - const Token& nextToken() const { - MOZ_ASSERT(hasLookahead()); - return tokens[(cursor + 1) & ntokensMask]; - } bool hasLookahead() const { return lookahead > 0; } @@ -1022,25 +1046,6 @@ class MOZ_STACK_CLASS TokenStream StrictModeGetter* strictModeGetter; // used to test for strict mode }; -class MOZ_STACK_CLASS AutoAwaitIsKeyword -{ -private: - TokenStream* ts_; - bool oldAwaitIsKeyword_; - -public: - AutoAwaitIsKeyword(TokenStream* ts, bool awaitIsKeyword) { - ts_ = ts; - oldAwaitIsKeyword_ = ts_->awaitIsKeyword; - ts_->awaitIsKeyword = awaitIsKeyword; - } - - ~AutoAwaitIsKeyword() { - ts_->awaitIsKeyword = oldAwaitIsKeyword_; - ts_ = nullptr; - } -}; - extern const char* TokenKindToDesc(TokenKind tt); diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index effc9233ea..dce3b2a203 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -667,29 +667,15 @@ class HeapSlot : public WriteBarrieredBase<Value> Element = 1 }; - explicit HeapSlot() = delete; - - explicit HeapSlot(NativeObject* obj, Kind kind, uint32_t slot, const Value& v) - : WriteBarrieredBase<Value>(v) - { - post(obj, kind, slot, v); - } - - explicit HeapSlot(NativeObject* obj, Kind kind, uint32_t slot, const HeapSlot& s) - : WriteBarrieredBase<Value>(s.value) - { - post(obj, kind, slot, s); - } - - ~HeapSlot() { - pre(); - } - void init(NativeObject* owner, Kind kind, uint32_t slot, const Value& v) { value = v; post(owner, kind, slot, v); } + void destroy() { + pre(); + } + #ifdef DEBUG bool preconditionForSet(NativeObject* owner, Kind kind, uint32_t slot) const; bool preconditionForWriteBarrierPost(NativeObject* obj, Kind kind, uint32_t slot, @@ -703,11 +689,6 @@ class HeapSlot : public WriteBarrieredBase<Value> post(owner, kind, slot, v); } - /* For users who need to manually barrier the raw types. */ - static void writeBarrierPost(NativeObject* owner, Kind kind, uint32_t slot, const Value& target) { - reinterpret_cast<HeapSlot*>(const_cast<Value*>(&target))->post(owner, kind, slot, target); - } - private: void post(NativeObject* owner, Kind kind, uint32_t slot, const Value& target) { MOZ_ASSERT(preconditionForWriteBarrierPost(owner, kind, slot, target)); diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h index 4919b87a54..e8df0bb709 100644 --- a/js/src/gc/GCInternals.h +++ b/js/src/gc/GCInternals.h @@ -9,7 +9,6 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/Maybe.h" -#include "mozilla/PodOperations.h" #include "jscntxt.h" @@ -102,9 +101,9 @@ struct TenureCountCache static const size_t EntryShift = 4; static const size_t EntryCount = 1 << EntryShift; - TenureCount entries[EntryCount]; + TenureCount entries[EntryCount] = {}; // zeroes - TenureCountCache() { mozilla::PodZero(this); } + TenureCountCache() = default; HashNumber hash(ObjectGroup* group) { #if JS_BITS_PER_WORD == 32 diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 5c2576efde..f102e9ef03 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -900,7 +900,8 @@ class GCRuntime void requestMajorGC(JS::gcreason::Reason reason); SliceBudget defaultBudget(JS::gcreason::Reason reason, int64_t millis); - void budgetIncrementalGC(SliceBudget& budget, AutoLockForExclusiveAccess& lock); + void budgetIncrementalGC(JS::gcreason::Reason reason, SliceBudget& budget, + AutoLockForExclusiveAccess& lock); void resetIncrementalGC(AbortReason reason, AutoLockForExclusiveAccess& lock); // Assert if the system state is such that we should never @@ -915,6 +916,7 @@ class GCRuntime void collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::Reason reason) JS_HAZ_GC_CALL; MOZ_MUST_USE bool gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason::Reason reason); + bool shouldRepeatForDeadZone(JS::gcreason::Reason reason); void incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock); diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index b2c105999a..262fc8cbcc 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -18,6 +18,7 @@ #include "builtin/ModuleObject.h" #include "gc/GCInternals.h" #include "gc/Policy.h" +#include "gc/StoreBuffer-inl.h" #include "jit/IonCode.h" #include "js/SliceBudget.h" #include "vm/ArgumentsObject.h" @@ -28,7 +29,6 @@ #include "vm/Shape.h" #include "vm/Symbol.h" #include "vm/TypedArrayObject.h" -#include "vm/UnboxedObject.h" #include "wasm/WasmJS.h" #include "jscompartmentinlines.h" @@ -37,7 +37,6 @@ #include "gc/Nursery-inl.h" #include "vm/String-inl.h" -#include "vm/UnboxedObject-inl.h" using namespace js; using namespace js::gc; @@ -1231,34 +1230,34 @@ BindingIter::trace(JSTracer* trc) void LexicalScope::Data::trace(JSTracer* trc) { - TraceBindingNames(trc, names, length); + TraceBindingNames(trc, trailingNames.start(), length); } void FunctionScope::Data::trace(JSTracer* trc) { TraceNullableEdge(trc, &canonicalFunction, "scope canonical function"); - TraceNullableBindingNames(trc, names, length); + TraceNullableBindingNames(trc, trailingNames.start(), length); } void VarScope::Data::trace(JSTracer* trc) { - TraceBindingNames(trc, names, length); + TraceBindingNames(trc, trailingNames.start(), length); } void GlobalScope::Data::trace(JSTracer* trc) { - TraceBindingNames(trc, names, length); + TraceBindingNames(trc, trailingNames.start(), length); } void EvalScope::Data::trace(JSTracer* trc) { - TraceBindingNames(trc, names, length); + TraceBindingNames(trc, trailingNames.start(), length); } void ModuleScope::Data::trace(JSTracer* trc) { TraceNullableEdge(trc, &module, "scope module"); - TraceBindingNames(trc, names, length); + TraceBindingNames(trc, trailingNames.start(), length); } void Scope::traceChildren(JSTracer* trc) @@ -1302,13 +1301,13 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope) traverseEdge(scope, static_cast<Scope*>(scope->enclosing_)); if (scope->environmentShape_) traverseEdge(scope, static_cast<Shape*>(scope->environmentShape_)); - BindingName* names = nullptr; + TrailingNamesArray* names = nullptr; uint32_t length = 0; switch (scope->kind_) { case ScopeKind::Function: { FunctionScope::Data* data = reinterpret_cast<FunctionScope::Data*>(scope->data_); traverseEdge(scope, static_cast<JSObject*>(data->canonicalFunction)); - names = data->names; + names = &data->trailingNames; length = data->length; break; } @@ -1316,7 +1315,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope) case ScopeKind::FunctionBodyVar: case ScopeKind::ParameterExpressionVar: { VarScope::Data* data = reinterpret_cast<VarScope::Data*>(scope->data_); - names = data->names; + names = &data->trailingNames; length = data->length; break; } @@ -1327,7 +1326,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope) case ScopeKind::NamedLambda: case ScopeKind::StrictNamedLambda: { LexicalScope::Data* data = reinterpret_cast<LexicalScope::Data*>(scope->data_); - names = data->names; + names = &data->trailingNames; length = data->length; break; } @@ -1335,7 +1334,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope) case ScopeKind::Global: case ScopeKind::NonSyntactic: { GlobalScope::Data* data = reinterpret_cast<GlobalScope::Data*>(scope->data_); - names = data->names; + names = &data->trailingNames; length = data->length; break; } @@ -1343,7 +1342,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope) case ScopeKind::Eval: case ScopeKind::StrictEval: { EvalScope::Data* data = reinterpret_cast<EvalScope::Data*>(scope->data_); - names = data->names; + names = &data->trailingNames; length = data->length; break; } @@ -1351,7 +1350,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope) case ScopeKind::Module: { ModuleScope::Data* data = reinterpret_cast<ModuleScope::Data*>(scope->data_); traverseEdge(scope, static_cast<JSObject*>(data->module)); - names = data->names; + names = &data->trailingNames; length = data->length; break; } @@ -1361,12 +1360,12 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope) } if (scope->kind_ == ScopeKind::Function) { for (uint32_t i = 0; i < length; i++) { - if (JSAtom* name = names[i].name()) + if (JSAtom* name = names->operator[](i).name()) traverseEdge(scope, static_cast<JSString*>(name)); } } else { for (uint32_t i = 0; i < length; i++) - traverseEdge(scope, static_cast<JSString*>(names[i].name())); + traverseEdge(scope, static_cast<JSString*>(names->operator[](i).name())); } } @@ -1395,14 +1394,6 @@ js::ObjectGroup::traceChildren(JSTracer* trc) if (maybePreliminaryObjects()) maybePreliminaryObjects()->trace(trc); - if (maybeUnboxedLayout()) - unboxedLayout().trace(trc); - - if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup()) { - TraceManuallyBarrieredEdge(trc, &unboxedGroup, "group_original_unboxed_group"); - setOriginalUnboxedGroup(unboxedGroup); - } - if (JSObject* descr = maybeTypeDescr()) { TraceManuallyBarrieredEdge(trc, &descr, "group_type_descr"); setTypeDescr(&descr->as<TypeDescr>()); @@ -1436,12 +1427,6 @@ js::GCMarker::lazilyMarkChildren(ObjectGroup* group) if (group->maybePreliminaryObjects()) group->maybePreliminaryObjects()->trace(this); - if (group->maybeUnboxedLayout()) - group->unboxedLayout().trace(this); - - if (ObjectGroup* unboxedGroup = group->maybeOriginalUnboxedGroup()) - traverseEdge(group, unboxedGroup); - if (TypeDescr* descr = group->maybeTypeDescr()) traverseEdge(group, static_cast<JSObject*>(descr)); @@ -1484,23 +1469,6 @@ CallTraceHook(Functor f, JSTracer* trc, JSObject* obj, CheckGeneration check, Ar return nullptr; } - if (clasp == &UnboxedPlainObject::class_) { - JSObject** pexpando = obj->as<UnboxedPlainObject>().addressOfExpando(); - if (*pexpando) - f(pexpando, mozilla::Forward<Args>(args)...); - - UnboxedPlainObject& unboxed = obj->as<UnboxedPlainObject>(); - const UnboxedLayout& layout = check == CheckGeneration::DoChecks - ? unboxed.layout() - : unboxed.layoutDontCheckGeneration(); - if (layout.traceList()) { - VisitTraceList(f, layout.traceList(), unboxed.data(), - mozilla::Forward<Args>(args)...); - } - - return nullptr; - } - clasp->doTrace(trc, obj); if (!clasp->isNative()) @@ -2293,18 +2261,6 @@ static inline void TraceWholeCell(TenuringTracer& mover, JSObject* object) { mover.traceObject(object); - - // Additionally trace the expando object attached to any unboxed plain - // objects. Baseline and Ion can write properties to the expando while - // only adding a post barrier to the owning unboxed object. Note that - // it isn't possible for a nursery unboxed object to have a tenured - // expando, so that adding a post barrier on the original object will - // capture any tenured->nursery edges in the expando as well. - - if (object->is<UnboxedPlainObject>()) { - if (UnboxedExpandoObject* expando = object->as<UnboxedPlainObject>().maybeExpando()) - expando->traceChildren(&mover); - } } static inline void @@ -2548,8 +2504,6 @@ js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind InlineTypedObject::objectMovedDuringMinorGC(this, dst, src); } else if (src->is<TypedArrayObject>()) { tenuredSize += TypedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind); - } else if (src->is<UnboxedArrayObject>()) { - tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind); } else if (src->is<ArgumentsObject>()) { tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src); } else if (src->is<ProxyObject>()) { @@ -2892,10 +2846,9 @@ struct UnmarkGrayTracer : public JS::CallbackTracer * * There is an additional complication for certain kinds of edges that are not * contained explicitly in the source object itself, such as from a weakmap key - * to its value, and from an object being watched by a watchpoint to the - * watchpoint's closure. These "implicit edges" are represented in some other - * container object, such as the weakmap or the watchpoint itself. In these - * cases, calling unmark gray on an object won't find all of its children. + * to its value. These "implicit edges" are represented in some other + * container object, such as the weakmap itself. In these cases, calling unmark + * gray on an object won't find all of its children. * * Handling these implicit edges has two parts: * - A special pass enumerating all of the containers that know about the diff --git a/js/src/gc/Memory.cpp b/js/src/gc/Memory.cpp index 26da754692..418984057a 100644 --- a/js/src/gc/Memory.cpp +++ b/js/src/gc/Memory.cpp @@ -17,11 +17,6 @@ #include "jswin.h" #include <psapi.h> -#elif defined(SOLARIS) - -#include <sys/mman.h> -#include <unistd.h> - #elif defined(XP_UNIX) #include <algorithm> @@ -408,86 +403,6 @@ DeallocateMappedContent(void* p, size_t length) # endif -#elif defined(SOLARIS) - -#ifndef MAP_NOSYNC -# define MAP_NOSYNC 0 -#endif - -void -InitMemorySubsystem() -{ - if (pageSize == 0) - pageSize = allocGranularity = size_t(sysconf(_SC_PAGESIZE)); -} - -void* -MapAlignedPages(size_t size, size_t alignment) -{ - MOZ_ASSERT(size >= alignment); - MOZ_ASSERT(size >= allocGranularity); - MOZ_ASSERT(size % alignment == 0); - MOZ_ASSERT(size % pageSize == 0); - MOZ_ASSERT_IF(alignment < allocGranularity, allocGranularity % alignment == 0); - MOZ_ASSERT_IF(alignment > allocGranularity, alignment % allocGranularity == 0); - - int prot = PROT_READ | PROT_WRITE; - int flags = MAP_PRIVATE | MAP_ANON | MAP_ALIGN | MAP_NOSYNC; - - void* p = mmap((caddr_t)alignment, size, prot, flags, -1, 0); - if (p == MAP_FAILED) - return nullptr; - return p; -} - -static void* -MapAlignedPagesLastDitch(size_t size, size_t alignment) -{ - return nullptr; -} - -void -UnmapPages(void* p, size_t size) -{ - MOZ_ALWAYS_TRUE(0 == munmap((caddr_t)p, size)); -} - -bool -MarkPagesUnused(void* p, size_t size) -{ - MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0); - return true; -} - -bool -MarkPagesInUse(void* p, size_t size) -{ - if (!DecommitEnabled()) - return; - - MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0); -} - -size_t -GetPageFaultCount() -{ - return 0; -} - -void* -AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment) -{ - // Not implemented. - return nullptr; -} - -// Deallocate mapped memory for object. -void -DeallocateMappedContent(void* p, size_t length) -{ - // Not implemented. -} - #elif defined(XP_UNIX) void @@ -500,8 +415,15 @@ InitMemorySubsystem() static inline void* MapMemoryAt(void* desired, size_t length, int prot = PROT_READ | PROT_WRITE, int flags = MAP_PRIVATE | MAP_ANON, int fd = -1, off_t offset = 0) + +// Solaris manages 64-bit address space in a different manner from every other +// AMD64 operating system, but fortunately the fix is the same one +// required for every operating system on 64-bit SPARC, Itanium, and ARM. +// Most people's intuition failed them here and they thought this couldn't +// possibly be correct on AMD64, but for Solaris/illumos it is. + { -#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) || defined(__aarch64__) +#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) || defined(__aarch64__) || (defined(__sun) && defined(__x86_64__)) MOZ_ASSERT((0xffff800000000000ULL & (uintptr_t(desired) + length - 1)) == 0); #endif void* region = mmap(desired, length, prot, flags, fd, offset); @@ -551,7 +473,7 @@ MapMemory(size_t length, int prot = PROT_READ | PROT_WRITE, return nullptr; } return region; -#elif defined(__aarch64__) +#elif defined(__aarch64__) || (defined(__sun) && defined(__x86_64__)) /* * There might be similar virtual address issue on arm64 which depends on * hardware and kernel configurations. But the work around is slightly @@ -763,7 +685,11 @@ MarkPagesUnused(void* p, size_t size) return false; MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0); - int result = madvise(p, size, MADV_DONTNEED); +#ifdef XP_SOLARIS + int result = posix_madvise(p, size, POSIX_MADV_DONTNEED); +#else + int result = madvise(p, size, MADV_DONTNEED); +#endif return result != -1; } diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 55ca5a059d..93a0eb6a8b 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -210,6 +210,7 @@ js::Nursery::disable() return; updateNumChunks(0); currentEnd_ = 0; + position_ = 0; runtime()->gc.storeBuffer.disable(); } @@ -530,7 +531,6 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason) // the nursery is full, look for object groups that are getting promoted // excessively and try to pretenure them. maybeStartProfile(ProfileKey::Pretenure); - uint32_t pretenureCount = 0; if (promotionRate > 0.8 || reason == JS::gcreason::FULL_STORE_BUFFER) { JSContext* cx = rt->contextFromMainThread(); for (auto& entry : tenureCounts.entries) { @@ -539,7 +539,6 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason) if (group->canPreTenure()) { AutoCompartment ac(cx, group->compartment()); group->setShouldPreTenure(cx); - pretenureCount++; } } } @@ -556,12 +555,6 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason) minorGcCount_++; int64_t totalTime = profileTimes_[ProfileKey::Total]; - rt->addTelemetry(JS_TELEMETRY_GC_MINOR_US, totalTime); - rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON, reason); - if (totalTime > 1000) - rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON_LONG, reason); - rt->addTelemetry(JS_TELEMETRY_GC_NURSERY_BYTES, sizeOfHeapCommitted()); - rt->addTelemetry(JS_TELEMETRY_GC_PRETENURE_COUNT, pretenureCount); rt->gc.stats.endNurseryCollection(reason); TraceMinorGCEnd(); diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index 0d215d9975..a839a49790 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -245,6 +245,7 @@ class Nursery // Free space remaining, not counting chunk trailers. MOZ_ALWAYS_INLINE size_t freeSpace() const { + MOZ_ASSERT(isEnabled()); MOZ_ASSERT(currentEnd_ - position_ <= NurseryChunkUsableSize); return (currentEnd_ - position_) + (numChunks() - currentChunk_ - 1) * NurseryChunkUsableSize; diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 93264084bb..7d665e8ebc 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -14,7 +14,6 @@ #include "jsgc.h" #include "jsprf.h" #include "jstypes.h" -#include "jswatchpoint.h" #include "builtin/MapObject.h" #include "frontend/BytecodeCompiler.h" @@ -478,6 +477,7 @@ js::gc::GCRuntime::bufferGrayRoots() for (GCZonesIter zone(rt); !zone.done(); zone.next()) MOZ_ASSERT(zone->gcGrayRoots.empty()); + gcstats::AutoPhase ap(stats, gcstats::PHASE_BUFFER_GRAY_ROOTS); BufferGrayRootsTracer grayBufferer(rt); if (JSTraceDataOp op = grayRootTracer.op) @@ -540,4 +540,3 @@ GCRuntime::resetBufferedGrayRoots() const for (GCZonesIter zone(rt); !zone.done(); zone.next()) zone->gcGrayRoots.clearAndFree(); } - diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 19f9986dd2..8a9f4e1350 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -34,13 +34,6 @@ using mozilla::MakeRange; using mozilla::PodArrayZero; using mozilla::PodZero; -/* - * If this fails, then you can either delete this assertion and allow all - * larger-numbered reasons to pile up in the last telemetry bucket, or switch - * to GC_REASON_3 and bump the max value. - */ -JS_STATIC_ASSERT(JS::gcreason::NUM_TELEMETRY_REASONS >= JS::gcreason::NUM_REASONS); - const char* js::gcstats::ExplainInvocationKind(JSGCInvocationKind gckind) { @@ -92,7 +85,6 @@ struct PhaseInfo Phase index; const char* name; Phase parent; - const uint8_t telemetryBucket; }; // The zeroth entry in the timing arrays is used for phases that have a @@ -134,78 +126,74 @@ struct DagChildEdge { */ static const PhaseInfo phases[] = { - { PHASE_MUTATOR, "Mutator Running", PHASE_NO_PARENT, 0 }, - { PHASE_GC_BEGIN, "Begin Callback", PHASE_NO_PARENT, 1 }, - { PHASE_WAIT_BACKGROUND_THREAD, "Wait Background Thread", PHASE_NO_PARENT, 2 }, - { PHASE_MARK_DISCARD_CODE, "Mark Discard Code", PHASE_NO_PARENT, 3 }, - { PHASE_RELAZIFY_FUNCTIONS, "Relazify Functions", PHASE_NO_PARENT, 4 }, - { PHASE_PURGE, "Purge", PHASE_NO_PARENT, 5 }, - { PHASE_MARK, "Mark", PHASE_NO_PARENT, 6 }, - { PHASE_UNMARK, "Unmark", PHASE_MARK, 7 }, + { PHASE_MUTATOR, "Mutator Running", PHASE_NO_PARENT }, + { PHASE_GC_BEGIN, "Begin Callback", PHASE_NO_PARENT }, + { PHASE_WAIT_BACKGROUND_THREAD, "Wait Background Thread", PHASE_NO_PARENT }, + { PHASE_MARK_DISCARD_CODE, "Mark Discard Code", PHASE_NO_PARENT }, + { PHASE_RELAZIFY_FUNCTIONS, "Relazify Functions", PHASE_NO_PARENT }, + { PHASE_PURGE, "Purge", PHASE_NO_PARENT }, + { PHASE_MARK, "Mark", PHASE_NO_PARENT }, + { PHASE_UNMARK, "Unmark", PHASE_MARK }, /* PHASE_MARK_ROOTS */ - { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK, 8 }, - { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT, 9 }, - { PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP, 10 }, - { PHASE_SWEEP_MARK_TYPES, "Mark Types During Sweeping", PHASE_SWEEP_MARK, 11 }, - { PHASE_SWEEP_MARK_INCOMING_BLACK, "Mark Incoming Black Pointers", PHASE_SWEEP_MARK, 12 }, - { PHASE_SWEEP_MARK_WEAK, "Mark Weak", PHASE_SWEEP_MARK, 13 }, - { PHASE_SWEEP_MARK_INCOMING_GRAY, "Mark Incoming Gray Pointers", PHASE_SWEEP_MARK, 14 }, - { PHASE_SWEEP_MARK_GRAY, "Mark Gray", PHASE_SWEEP_MARK, 15 }, - { PHASE_SWEEP_MARK_GRAY_WEAK, "Mark Gray and Weak", PHASE_SWEEP_MARK, 16 }, - { PHASE_FINALIZE_START, "Finalize Start Callbacks", PHASE_SWEEP, 17 }, - { PHASE_WEAK_ZONEGROUP_CALLBACK, "Per-Slice Weak Callback", PHASE_FINALIZE_START, 57 }, - { PHASE_WEAK_COMPARTMENT_CALLBACK, "Per-Compartment Weak Callback", PHASE_FINALIZE_START, 58 }, - { PHASE_SWEEP_ATOMS, "Sweep Atoms", PHASE_SWEEP, 18 }, - { PHASE_SWEEP_SYMBOL_REGISTRY, "Sweep Symbol Registry", PHASE_SWEEP, 19 }, - { PHASE_SWEEP_COMPARTMENTS, "Sweep Compartments", PHASE_SWEEP, 20 }, - { PHASE_SWEEP_DISCARD_CODE, "Sweep Discard Code", PHASE_SWEEP_COMPARTMENTS, 21 }, - { PHASE_SWEEP_INNER_VIEWS, "Sweep Inner Views", PHASE_SWEEP_COMPARTMENTS, 22 }, - { PHASE_SWEEP_CC_WRAPPER, "Sweep Cross Compartment Wrappers", PHASE_SWEEP_COMPARTMENTS, 23 }, - { PHASE_SWEEP_BASE_SHAPE, "Sweep Base Shapes", PHASE_SWEEP_COMPARTMENTS, 24 }, - { PHASE_SWEEP_INITIAL_SHAPE, "Sweep Initial Shapes", PHASE_SWEEP_COMPARTMENTS, 25 }, - { PHASE_SWEEP_TYPE_OBJECT, "Sweep Type Objects", PHASE_SWEEP_COMPARTMENTS, 26 }, - { PHASE_SWEEP_BREAKPOINT, "Sweep Breakpoints", PHASE_SWEEP_COMPARTMENTS, 27 }, - { PHASE_SWEEP_REGEXP, "Sweep Regexps", PHASE_SWEEP_COMPARTMENTS, 28 }, - { PHASE_SWEEP_MISC, "Sweep Miscellaneous", PHASE_SWEEP_COMPARTMENTS, 29 }, - { PHASE_SWEEP_TYPES, "Sweep type information", PHASE_SWEEP_COMPARTMENTS, 30 }, - { PHASE_SWEEP_TYPES_BEGIN, "Sweep type tables and compilations", PHASE_SWEEP_TYPES, 31 }, - { PHASE_SWEEP_TYPES_END, "Free type arena", PHASE_SWEEP_TYPES, 32 }, - { PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP, 33 }, - { PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP, 34 }, - { PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP, 35 }, - { PHASE_SWEEP_SCOPE, "Sweep Scope", PHASE_SWEEP, 59 }, - { PHASE_SWEEP_SHAPE, "Sweep Shape", PHASE_SWEEP, 36 }, - { PHASE_SWEEP_JITCODE, "Sweep JIT code", PHASE_SWEEP, 37 }, - { PHASE_FINALIZE_END, "Finalize End Callback", PHASE_SWEEP, 38 }, - { PHASE_DESTROY, "Deallocate", PHASE_SWEEP, 39 }, - { PHASE_COMPACT, "Compact", PHASE_NO_PARENT, 40 }, - { PHASE_COMPACT_MOVE, "Compact Move", PHASE_COMPACT, 41 }, - { PHASE_COMPACT_UPDATE, "Compact Update", PHASE_COMPACT, 42 }, + { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK }, + { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT }, + { PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP }, + { PHASE_SWEEP_MARK_TYPES, "Mark Types During Sweeping", PHASE_SWEEP_MARK }, + { PHASE_SWEEP_MARK_INCOMING_BLACK, "Mark Incoming Black Pointers", PHASE_SWEEP_MARK }, + { PHASE_SWEEP_MARK_WEAK, "Mark Weak", PHASE_SWEEP_MARK }, + { PHASE_SWEEP_MARK_INCOMING_GRAY, "Mark Incoming Gray Pointers", PHASE_SWEEP_MARK }, + { PHASE_SWEEP_MARK_GRAY, "Mark Gray", PHASE_SWEEP_MARK }, + { PHASE_SWEEP_MARK_GRAY_WEAK, "Mark Gray and Weak", PHASE_SWEEP_MARK }, + { PHASE_FINALIZE_START, "Finalize Start Callbacks", PHASE_SWEEP }, + { PHASE_WEAK_ZONEGROUP_CALLBACK, "Per-Slice Weak Callback", PHASE_FINALIZE_START }, + { PHASE_WEAK_COMPARTMENT_CALLBACK, "Per-Compartment Weak Callback", PHASE_FINALIZE_START }, + { PHASE_SWEEP_ATOMS, "Sweep Atoms", PHASE_SWEEP }, + { PHASE_SWEEP_SYMBOL_REGISTRY, "Sweep Symbol Registry", PHASE_SWEEP }, + { PHASE_SWEEP_COMPARTMENTS, "Sweep Compartments", PHASE_SWEEP }, + { PHASE_SWEEP_DISCARD_CODE, "Sweep Discard Code", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_INNER_VIEWS, "Sweep Inner Views", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_CC_WRAPPER, "Sweep Cross Compartment Wrappers", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_BASE_SHAPE, "Sweep Base Shapes", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_INITIAL_SHAPE, "Sweep Initial Shapes", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_TYPE_OBJECT, "Sweep Type Objects", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_BREAKPOINT, "Sweep Breakpoints", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_REGEXP, "Sweep Regexps", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_MISC, "Sweep Miscellaneous", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_TYPES, "Sweep type information", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_TYPES_BEGIN, "Sweep type tables and compilations", PHASE_SWEEP_TYPES }, + { PHASE_SWEEP_TYPES_END, "Free type arena", PHASE_SWEEP_TYPES }, + { PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP }, + { PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP }, + { PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP }, + { PHASE_SWEEP_SCOPE, "Sweep Scope", PHASE_SWEEP }, + { PHASE_SWEEP_SHAPE, "Sweep Shape", PHASE_SWEEP }, + { PHASE_SWEEP_JITCODE, "Sweep JIT code", PHASE_SWEEP }, + { PHASE_FINALIZE_END, "Finalize End Callback", PHASE_SWEEP }, + { PHASE_DESTROY, "Deallocate", PHASE_SWEEP }, + { PHASE_COMPACT, "Compact", PHASE_NO_PARENT }, + { PHASE_COMPACT_MOVE, "Compact Move", PHASE_COMPACT }, + { PHASE_COMPACT_UPDATE, "Compact Update", PHASE_COMPACT }, /* PHASE_MARK_ROOTS */ - { PHASE_COMPACT_UPDATE_CELLS, "Compact Update Cells", PHASE_COMPACT_UPDATE, 43 }, - { PHASE_GC_END, "End Callback", PHASE_NO_PARENT, 44 }, - { PHASE_MINOR_GC, "All Minor GCs", PHASE_NO_PARENT, 45 }, + { PHASE_COMPACT_UPDATE_CELLS, "Compact Update Cells", PHASE_COMPACT_UPDATE }, + { PHASE_GC_END, "End Callback", PHASE_NO_PARENT }, + { PHASE_MINOR_GC, "All Minor GCs", PHASE_NO_PARENT }, /* PHASE_MARK_ROOTS */ - { PHASE_EVICT_NURSERY, "Minor GCs to Evict Nursery", PHASE_NO_PARENT, 46 }, + { PHASE_EVICT_NURSERY, "Minor GCs to Evict Nursery", PHASE_NO_PARENT }, /* PHASE_MARK_ROOTS */ - { PHASE_TRACE_HEAP, "Trace Heap", PHASE_NO_PARENT, 47 }, + { PHASE_TRACE_HEAP, "Trace Heap", PHASE_NO_PARENT }, /* PHASE_MARK_ROOTS */ - { PHASE_BARRIER, "Barriers", PHASE_NO_PARENT, 55 }, - { PHASE_UNMARK_GRAY, "Unmark gray", PHASE_BARRIER, 56 }, - { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MULTI_PARENTS, 48 }, - { PHASE_BUFFER_GRAY_ROOTS, "Buffer Gray Roots", PHASE_MARK_ROOTS, 49 }, - { PHASE_MARK_CCWS, "Mark Cross Compartment Wrappers", PHASE_MARK_ROOTS, 50 }, - { PHASE_MARK_STACK, "Mark C and JS stacks", PHASE_MARK_ROOTS, 51 }, - { PHASE_MARK_RUNTIME_DATA, "Mark Runtime-wide Data", PHASE_MARK_ROOTS, 52 }, - { PHASE_MARK_EMBEDDING, "Mark Embedding", PHASE_MARK_ROOTS, 53 }, - { PHASE_MARK_COMPARTMENTS, "Mark Compartments", PHASE_MARK_ROOTS, 54 }, - { PHASE_PURGE_SHAPE_TABLES, "Purge ShapeTables", PHASE_NO_PARENT, 60 }, - - { PHASE_LIMIT, nullptr, PHASE_NO_PARENT, 60 } - - // Current number of telemetryBuckets is 60. If you insert new phases - // somewhere, start at that number and count up. Do not change any existing - // numbers. + { PHASE_BARRIER, "Barriers", PHASE_NO_PARENT }, + { PHASE_UNMARK_GRAY, "Unmark gray", PHASE_BARRIER }, + { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MULTI_PARENTS }, + { PHASE_BUFFER_GRAY_ROOTS, "Buffer Gray Roots", PHASE_MARK_ROOTS }, + { PHASE_MARK_CCWS, "Mark Cross Compartment Wrappers", PHASE_MARK_ROOTS }, + { PHASE_MARK_STACK, "Mark C and JS stacks", PHASE_MARK_ROOTS }, + { PHASE_MARK_RUNTIME_DATA, "Mark Runtime-wide Data", PHASE_MARK_ROOTS }, + { PHASE_MARK_EMBEDDING, "Mark Embedding", PHASE_MARK_ROOTS }, + { PHASE_MARK_COMPARTMENTS, "Mark Compartments", PHASE_MARK_ROOTS }, + { PHASE_PURGE_SHAPE_TABLES, "Purge ShapeTables", PHASE_NO_PARENT }, + + { PHASE_LIMIT, nullptr, PHASE_NO_PARENT } }; static ExtraPhaseInfo phaseExtra[PHASE_LIMIT] = { { 0, 0 } }; @@ -845,12 +833,6 @@ Statistics::~Statistics() /* static */ bool Statistics::initialize() { - for (size_t i = 0; i < PHASE_LIMIT; i++) { - MOZ_ASSERT(phases[i].index == i); - for (size_t j = 0; j < PHASE_LIMIT; j++) - MOZ_ASSERT_IF(i != j, phases[i].telemetryBucket != phases[j].telemetryBucket); - } - // Create a static table of descendants for every phase with multiple // children. This assumes that all descendants come linearly in the // list, which is reasonable since full dags are not supported; any @@ -925,32 +907,6 @@ Statistics::getMaxGCPauseSinceClear() return maxPauseInInterval; } -// Sum up the time for a phase, including instances of the phase with different -// parents. -static int64_t -SumPhase(Phase phase, const Statistics::PhaseTimeTable times) -{ - int64_t sum = 0; - for (auto i : MakeRange(Statistics::NumTimingArrays)) - sum += times[i][phase]; - return sum; -} - -static Phase -LongestPhase(const Statistics::PhaseTimeTable times) -{ - int64_t longestTime = 0; - Phase longestPhase = PHASE_NONE; - for (size_t i = 0; i < PHASE_LIMIT; ++i) { - int64_t phaseTime = SumPhase(Phase(i), times); - if (phaseTime > longestTime) { - longestTime = phaseTime; - longestPhase = Phase(i); - } - } - return longestPhase; -} - void Statistics::printStats() { @@ -985,34 +941,6 @@ Statistics::endGC() int64_t total, longest; gcDuration(&total, &longest); - int64_t sccTotal, sccLongest; - sccDurations(&sccTotal, &sccLongest); - - runtime->addTelemetry(JS_TELEMETRY_GC_IS_ZONE_GC, !zoneStats.isCollectingAllZones()); - runtime->addTelemetry(JS_TELEMETRY_GC_MS, t(total)); - runtime->addTelemetry(JS_TELEMETRY_GC_MAX_PAUSE_MS, t(longest)); - int64_t markTotal = SumPhase(PHASE_MARK, phaseTimes); - int64_t markRootsTotal = SumPhase(PHASE_MARK_ROOTS, phaseTimes); - runtime->addTelemetry(JS_TELEMETRY_GC_MARK_MS, t(markTotal)); - runtime->addTelemetry(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_DAG_NONE][PHASE_SWEEP])); - if (runtime->gc.isCompactingGc()) { - runtime->addTelemetry(JS_TELEMETRY_GC_COMPACT_MS, - t(phaseTimes[PHASE_DAG_NONE][PHASE_COMPACT])); - } - runtime->addTelemetry(JS_TELEMETRY_GC_MARK_ROOTS_MS, t(markRootsTotal)); - runtime->addTelemetry(JS_TELEMETRY_GC_MARK_GRAY_MS, t(phaseTimes[PHASE_DAG_NONE][PHASE_SWEEP_MARK_GRAY])); - runtime->addTelemetry(JS_TELEMETRY_GC_NON_INCREMENTAL, nonincremental()); - if (nonincremental()) - runtime->addTelemetry(JS_TELEMETRY_GC_NON_INCREMENTAL_REASON, uint32_t(nonincrementalReason_)); - runtime->addTelemetry(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gc.isIncrementalGCAllowed()); - runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, t(sccTotal)); - runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, t(sccLongest)); - - if (!aborted) { - double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC); - runtime->addTelemetry(JS_TELEMETRY_GC_MMU_50, mmu50 * 100); - } - if (fp) printStats(); @@ -1061,8 +989,6 @@ Statistics::beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind, return; } - runtime->addTelemetry(JS_TELEMETRY_GC_REASON, reason); - // Slice callbacks should only fire for the outermost level. if (gcDepth == 1) { bool wasFullGC = zoneStats.isCollectingAllZones(); @@ -1082,25 +1008,6 @@ Statistics::endSlice() slices.back().endFaults = GetPageFaultCount(); slices.back().finalState = runtime->gc.state(); - int64_t sliceTime = slices.back().end - slices.back().start; - runtime->addTelemetry(JS_TELEMETRY_GC_SLICE_MS, t(sliceTime)); - runtime->addTelemetry(JS_TELEMETRY_GC_RESET, slices.back().wasReset()); - if (slices.back().wasReset()) - runtime->addTelemetry(JS_TELEMETRY_GC_RESET_REASON, uint32_t(slices.back().resetReason)); - - if (slices.back().budget.isTimeBudget()) { - int64_t budget_ms = slices.back().budget.timeBudget.budget; - runtime->addTelemetry(JS_TELEMETRY_GC_BUDGET_MS, budget_ms); - if (budget_ms == runtime->gc.defaultSliceBudget()) - runtime->addTelemetry(JS_TELEMETRY_GC_ANIMATION_MS, t(sliceTime)); - - // Record any phase that goes more than 2x over its budget. - if (sliceTime > 2 * budget_ms * 1000) { - Phase longest = LongestPhase(slices.back().phaseTimes); - runtime->addTelemetry(JS_TELEMETRY_GC_SLOW_PHASE, phases[longest].telemetryBucket); - } - } - sliceCount_++; } diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index ca1969b2ce..08a2810cfb 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -10,7 +10,6 @@ #include "mozilla/EnumeratedArray.h" #include "mozilla/IntegerRange.h" #include "mozilla/Maybe.h" -#include "mozilla/PodOperations.h" #include "jsalloc.h" #include "jsgc.h" @@ -112,29 +111,26 @@ enum Stat { struct ZoneGCStats { /* Number of zones collected in this GC. */ - int collectedZoneCount; + int collectedZoneCount = 0; /* Total number of zones in the Runtime at the start of this GC. */ - int zoneCount; + int zoneCount = 0; /* Number of zones swept in this GC. */ - int sweptZoneCount; + int sweptZoneCount = 0; /* Total number of compartments in all zones collected. */ - int collectedCompartmentCount; + int collectedCompartmentCount = 0; /* Total number of compartments in the Runtime at the start of this GC. */ - int compartmentCount; + int compartmentCount = 0; /* Total number of compartments swept by this GC. */ - int sweptCompartmentCount; + int sweptCompartmentCount = 0; bool isCollectingAllZones() const { return collectedZoneCount == zoneCount; } - ZoneGCStats() - : collectedZoneCount(0), zoneCount(0), sweptZoneCount(0), - collectedCompartmentCount(0), compartmentCount(0), sweptCompartmentCount(0) - {} + ZoneGCStats() = default; }; #define FOR_EACH_GC_PROFILE_TIME(_) \ diff --git a/js/src/gc/Tracer.cpp b/js/src/gc/Tracer.cpp index 63cd9b08a4..3416464ddf 100644 --- a/js/src/gc/Tracer.cpp +++ b/js/src/gc/Tracer.cpp @@ -201,80 +201,15 @@ gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, Shape* shape) } while (shape); } -// Object groups can point to other object groups via an UnboxedLayout or the -// the original unboxed group link. There can potentially be deep or cyclic -// chains of such groups to trace through without going through a thing that -// participates in cycle collection. These need to be handled iteratively to -// avoid blowing the stack when running the cycle collector's callback tracer. -struct ObjectGroupCycleCollectorTracer : public JS::CallbackTracer -{ - explicit ObjectGroupCycleCollectorTracer(JS::CallbackTracer* innerTracer) - : JS::CallbackTracer(innerTracer->runtime(), DoNotTraceWeakMaps), - innerTracer(innerTracer) - {} - - void onChild(const JS::GCCellPtr& thing) override; - - JS::CallbackTracer* innerTracer; - Vector<ObjectGroup*, 4, SystemAllocPolicy> seen, worklist; -}; - -void -ObjectGroupCycleCollectorTracer::onChild(const JS::GCCellPtr& thing) -{ - if (thing.is<BaseShape>()) { - // The CC does not care about BaseShapes, and no additional GC things - // will be reached by following this edge. - return; - } - - if (thing.is<JSObject>() || thing.is<JSScript>()) { - // Invoke the inner cycle collector callback on this child. It will not - // recurse back into TraceChildren. - innerTracer->onChild(thing); - return; - } - - if (thing.is<ObjectGroup>()) { - // If this group is required to be in an ObjectGroup chain, trace it - // via the provided worklist rather than continuing to recurse. - ObjectGroup& group = thing.as<ObjectGroup>(); - if (group.maybeUnboxedLayout()) { - for (size_t i = 0; i < seen.length(); i++) { - if (seen[i] == &group) - return; - } - if (seen.append(&group) && worklist.append(&group)) { - return; - } else { - // If append fails, keep tracing normally. The worst that will - // happen is we end up overrecursing. - } - } - } - - TraceChildren(this, thing.asCell(), thing.kind()); -} - void gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, ObjectGroup* group) { MOZ_ASSERT(trc->isCallbackTracer()); - // Early return if this group is not required to be in an ObjectGroup chain. - if (!group->maybeUnboxedLayout()) - return group->traceChildren(trc); - - ObjectGroupCycleCollectorTracer groupTracer(trc->asCallbackTracer()); - group->traceChildren(&groupTracer); - - while (!groupTracer.worklist.empty()) { - ObjectGroup* innerGroup = groupTracer.worklist.popCopy(); - innerGroup->traceChildren(&groupTracer); - } + group->traceChildren(trc); } - + /*** Traced Edge Printer *************************************************************************/ static size_t diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index ed099341c5..f0cdde0121 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -370,6 +370,21 @@ Zone::fixupAfterMovingGC() fixupInitialShapeTable(); } +bool +Zone::addTypeDescrObject(JSContext* cx, HandleObject obj) +{ + // Type descriptor objects are always tenured so we don't need post barriers + // on the set. + MOZ_ASSERT(!IsInsideNursery(obj)); + + if (!typeDescrObjects.put(obj)) { + ReportOutOfMemory(cx); + return false; + } + + return true; +} + ZoneList::ZoneList() : head(nullptr), tail(nullptr) {} diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 50d06319db..c8520ed55b 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -349,10 +349,17 @@ struct Zone : public JS::shadow::Zone, // Keep track of all TypeDescr and related objects in this compartment. // This is used by the GC to trace them all first when compacting, since the // TypedObject trace hook may access these objects. - using TypeDescrObjectSet = js::GCHashSet<js::HeapPtr<JSObject*>, - js::MovableCellHasher<js::HeapPtr<JSObject*>>, + + // + // There are no barriers here - the set contains only tenured objects so no + // post-barrier is required, and these are weak references so no pre-barrier + // is required. + using TypeDescrObjectSet = js::GCHashSet<JSObject*, + js::MovableCellHasher<JSObject*>, js::SystemAllocPolicy>; JS::WeakCache<TypeDescrObjectSet> typeDescrObjects; + + bool addTypeDescrObject(JSContext* cx, HandleObject obj); // Malloc counter to measure memory pressure for GC scheduling. It runs from diff --git a/js/src/irregexp/RegExpParser.cpp b/js/src/irregexp/RegExpParser.cpp index ccc6ae3ebb..8bd88047af 100644 --- a/js/src/irregexp/RegExpParser.cpp +++ b/js/src/irregexp/RegExpParser.cpp @@ -243,10 +243,10 @@ RegExpParser<CharT>::RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc, template <typename CharT> RegExpTree* -RegExpParser<CharT>::ReportError(unsigned errorNumber) +RegExpParser<CharT>::ReportError(unsigned errorNumber, const char* param /* = nullptr */) { gc::AutoSuppressGC suppressGC(ts.context()); - ts.reportError(errorNumber); + ts.reportError(errorNumber, param); return nullptr; } @@ -350,7 +350,7 @@ RegExpParser<CharT>::ParseBracedHexEscape(widechar* value) } code = (code << 4) | d; if (code > unicode::NonBMPMax) { - ReportError(JSMSG_UNICODE_OVERFLOW); + ReportError(JSMSG_UNICODE_OVERFLOW, "regular expression"); return false; } Advance(); diff --git a/js/src/irregexp/RegExpParser.h b/js/src/irregexp/RegExpParser.h index b5228a86f9..0a7e618589 100644 --- a/js/src/irregexp/RegExpParser.h +++ b/js/src/irregexp/RegExpParser.h @@ -211,7 +211,7 @@ class RegExpParser bool ParseBackReferenceIndex(int* index_out); bool ParseClassAtom(char16_t* char_class, widechar *value); - RegExpTree* ReportError(unsigned errorNumber); + RegExpTree* ReportError(unsigned errorNumber, const char* param = nullptr); void Advance(); void Advance(int dist) { next_pos_ += dist - 1; diff --git a/js/src/jit-test/modules/export-default-async-asi.js b/js/src/jit-test/modules/export-default-async-asi.js new file mode 100644 index 0000000000..a69a7aa3dc --- /dev/null +++ b/js/src/jit-test/modules/export-default-async-asi.js @@ -0,0 +1,2 @@ +export default async // ASI occurs here due to the [no LineTerminator here] restriction on default-exporting an async function +function async() { return 17; } diff --git a/js/src/jit-test/tests/asm.js/import-function-toPrimitive.js b/js/src/jit-test/tests/asm.js/import-function-toPrimitive.js new file mode 100644 index 0000000000..aa529b465d --- /dev/null +++ b/js/src/jit-test/tests/asm.js/import-function-toPrimitive.js @@ -0,0 +1,26 @@ +var counter = 0; + +function f(stdlib, foreign) +{ + "use asm"; + var a = +foreign.a; + var b = +foreign.b; + function g() {} + return g; +} + +var foreign = + { + a: function() {}, + b: /value that doesn't coerce purely/, + }; + +foreign.a[Symbol.toPrimitive] = + function() + { + counter++; + return 0; + }; + +f(null, foreign); +assertEq(counter, 1); diff --git a/js/src/jit-test/tests/asm.js/testSource.js b/js/src/jit-test/tests/asm.js/testSource.js index b44c52a6bc..d7ad428640 100644 --- a/js/src/jit-test/tests/asm.js/testSource.js +++ b/js/src/jit-test/tests/asm.js/testSource.js @@ -32,7 +32,7 @@ var f0 = function() { } -funcBody1 = funcBody.replace('function f0','function '); +funcBody1 = funcBody.replace('function f0','function'); assertEq(f0.toString(), funcBody1); assertEq(f0.toSource(), '(' + funcBody1 + ')'); @@ -48,14 +48,14 @@ assertEq(g.toString(), funcBody2); assertEq(g.toSource(), '(' + funcBody2 + ')'); f0 = new Function(bodyOnly); -assertEq(f0.toString(), "function anonymous() {\n" + bodyOnly + "\n}"); -assertEq(f0.toSource(), "(function anonymous() {\n" + bodyOnly + "\n})"); +assertEq(f0.toString(), "function anonymous(\n) {\n" + bodyOnly + "\n}"); +assertEq(f0.toSource(), "(function anonymous(\n) {\n" + bodyOnly + "\n})"); if (isAsmJSCompilationAvailable() && isCachingEnabled()) { var m = new Function(bodyOnly); assertEq(isAsmJSModuleLoadedFromCache(m), true); - assertEq(m.toString(), "function anonymous() {\n" + bodyOnly + "\n}"); - assertEq(m.toSource(), "(function anonymous() {\n" + bodyOnly + "\n})"); + assertEq(m.toString(), "function anonymous(\n) {\n" + bodyOnly + "\n}"); + assertEq(m.toSource(), "(function anonymous(\n) {\n" + bodyOnly + "\n})"); } })(); @@ -91,7 +91,7 @@ f1 = function(glob) { } -funcBody1 = funcBody.replace('function f1', 'function '); +funcBody1 = funcBody.replace('function f1', 'function'); assertEq(f1.toString(), funcBody1); assertEq(f1.toSource(), '(' + funcBody1 + ')'); @@ -107,14 +107,14 @@ assertEq(g.toString(), funcBody2); assertEq(g.toSource(), '(' + funcBody2 + ')'); f1 = new Function('glob', bodyOnly); -assertEq(f1.toString(), "function anonymous(glob) {\n" + bodyOnly + "\n}"); -assertEq(f1.toSource(), "(function anonymous(glob) {\n" + bodyOnly + "\n})"); +assertEq(f1.toString(), "function anonymous(glob\n) {\n" + bodyOnly + "\n}"); +assertEq(f1.toSource(), "(function anonymous(glob\n) {\n" + bodyOnly + "\n})"); if (isAsmJSCompilationAvailable() && isCachingEnabled()) { var m = new Function('glob', bodyOnly); assertEq(isAsmJSModuleLoadedFromCache(m), true); - assertEq(m.toString(), "function anonymous(glob) {\n" + bodyOnly + "\n}"); - assertEq(m.toSource(), "(function anonymous(glob) {\n" + bodyOnly + "\n})"); + assertEq(m.toString(), "function anonymous(glob\n) {\n" + bodyOnly + "\n}"); + assertEq(m.toSource(), "(function anonymous(glob\n) {\n" + bodyOnly + "\n})"); } })(); @@ -144,14 +144,14 @@ var funcBody = 'function f2(glob, ffi) {\n\ assertEq(f2.toString(), funcBody); assertEq(f2.toSource(), funcBody); -f2 = function (glob, ffi) { +f2 = function(glob, ffi) { "use asm"; function g() {} return g; } -funcBody1 = funcBody.replace('function f2', 'function '); +funcBody1 = funcBody.replace('function f2', 'function'); assertEq(f2.toString(), funcBody1); assertEq(f2.toSource(), '(' + funcBody1 + ')'); @@ -167,14 +167,14 @@ assertEq(g.toString(), funcBody2); assertEq(g.toSource(), '(' + funcBody2 + ')'); f2 = new Function('glob', 'ffi', bodyOnly); -assertEq(f2.toString(), "function anonymous(glob, ffi) {\n" + bodyOnly + "\n}"); -assertEq(f2.toSource(), "(function anonymous(glob, ffi) {\n" + bodyOnly + "\n})"); +assertEq(f2.toString(), "function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n}"); +assertEq(f2.toSource(), "(function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n})"); if (isAsmJSCompilationAvailable() && isCachingEnabled()) { var m = new Function('glob', 'ffi', bodyOnly); assertEq(isAsmJSModuleLoadedFromCache(m), true); - assertEq(m.toString(), "function anonymous(glob, ffi) {\n" + bodyOnly + "\n}"); - assertEq(m.toSource(), "(function anonymous(glob, ffi) {\n" + bodyOnly + "\n})"); + assertEq(m.toString(), "function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n}"); + assertEq(m.toSource(), "(function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n})"); } })(); @@ -204,14 +204,14 @@ var funcBody = 'function f3(glob, ffi, heap) {\n\ assertEq(f3.toString(), funcBody); assertEq(f3.toSource(), funcBody); -f3 = function (glob, ffi, heap) { +f3 = function(glob, ffi, heap) { "use asm"; function g() {} return g; } -funcBody1 = funcBody.replace('function f3', 'function '); +funcBody1 = funcBody.replace('function f3', 'function'); assertEq(f3.toString(), funcBody1); assertEq(f3.toSource(), '(' + funcBody1 + ')'); @@ -227,14 +227,14 @@ assertEq(g.toString(), funcBody2); assertEq(g.toSource(), '(' + funcBody2 + ')'); f3 = new Function('glob', 'ffi', 'heap', bodyOnly); -assertEq(f3.toString(), "function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n}"); -assertEq(f3.toSource(), "(function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n})"); +assertEq(f3.toString(), "function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n}"); +assertEq(f3.toSource(), "(function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n})"); if (isAsmJSCompilationAvailable() && isCachingEnabled()) { var m = new Function('glob', 'ffi', 'heap', bodyOnly); assertEq(isAsmJSModuleLoadedFromCache(m), true); - assertEq(m.toString(), "function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n}"); - assertEq(m.toSource(), "(function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n})"); + assertEq(m.toString(), "function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n}"); + assertEq(m.toSource(), "(function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n})"); } })(); @@ -243,7 +243,7 @@ if (isAsmJSCompilationAvailable() && isCachingEnabled()) { (function() { var funcSource = - `function (glob, ffi, heap) { + `function(glob, ffi, heap) { "use asm"; function g() {} return g; @@ -252,7 +252,7 @@ var funcSource = var f4 = eval("\"use strict\";\n(" + funcSource + ")"); var expectedToString = funcSource; -var expectedToSource = '(' + expectedToString + ')' +var expectedToSource = '(' + expectedToString + ')'; assertEq(f4.toString(), expectedToString); assertEq(f4.toSource(), expectedToSource); diff --git a/js/src/jit-test/tests/auto-regress/bug466654.js b/js/src/jit-test/tests/auto-regress/bug466654.js deleted file mode 100644 index 6c82c425bf..0000000000 --- a/js/src/jit-test/tests/auto-regress/bug466654.js +++ /dev/null @@ -1,8 +0,0 @@ -// |jit-test| error:TypeError - -// Binary: cache/js-dbg-32-29add08d84ae-linux -// Flags: -j -// -this.watch('y', /x/g ); -for each (y in ['q', 'q', 'q']) continue; -gc(); diff --git a/js/src/jit-test/tests/auto-regress/bug516897.js b/js/src/jit-test/tests/auto-regress/bug516897.js deleted file mode 100644 index e3caf4e6e3..0000000000 --- a/js/src/jit-test/tests/auto-regress/bug516897.js +++ /dev/null @@ -1,6 +0,0 @@ -// Binary: cache/js-dbg-64-38754465ffde-linux -// Flags: -// -this.__defineSetter__("x", gc); -this.watch("x",function(){return}); -x = 3; diff --git a/js/src/jit-test/tests/auto-regress/bug537854.js b/js/src/jit-test/tests/auto-regress/bug537854.js deleted file mode 100644 index 80fb3c14a8..0000000000 --- a/js/src/jit-test/tests/auto-regress/bug537854.js +++ /dev/null @@ -1,4 +0,0 @@ -// Binary: cache/js-dbg-64-9d51f2a931f7-linux -// Flags: -// -({x:function(){}}).watch('x',function(){}); diff --git a/js/src/jit-test/tests/auto-regress/bug560796.js b/js/src/jit-test/tests/auto-regress/bug560796.js deleted file mode 100644 index 4ab93567e4..0000000000 --- a/js/src/jit-test/tests/auto-regress/bug560796.js +++ /dev/null @@ -1,9 +0,0 @@ -// Binary: cache/js-dbg-64-a6d7a5677b4c-linux -// Flags: -// -this.__defineSetter__("x", function(){}) -this.watch("x", "".localeCompare) -window = x -Object.defineProperty(this, "x", ({ - set: window -})) diff --git a/js/src/jit-test/tests/auto-regress/bug638735.js b/js/src/jit-test/tests/auto-regress/bug638735.js index 63071aa7c4..c941f53695 100644 --- a/js/src/jit-test/tests/auto-regress/bug638735.js +++ b/js/src/jit-test/tests/auto-regress/bug638735.js @@ -4,7 +4,6 @@ var o9 = Function.prototype; var o13 = Array; function f5(o) { -o.watch('p3', function() {}); ox1 = new Proxy(o, {}); } f5(o9); diff --git a/js/src/jit-test/tests/auto-regress/bug639413.js b/js/src/jit-test/tests/auto-regress/bug639413.js deleted file mode 100644 index d8dd58eaf9..0000000000 --- a/js/src/jit-test/tests/auto-regress/bug639413.js +++ /dev/null @@ -1,9 +0,0 @@ -// |jit-test| error:TypeError - -// Binary: cache/js-dbg-32-1c8e91b2e3a4-linux -// Flags: -// -a = evalcx("lazy"); -a.watch("x", function() {}); -({}).watch("x", function() {}); -a.__defineGetter__("y", {}); diff --git a/js/src/jit-test/tests/auto-regress/bug698899.js b/js/src/jit-test/tests/auto-regress/bug698899.js deleted file mode 100644 index 644f45ec2c..0000000000 --- a/js/src/jit-test/tests/auto-regress/bug698899.js +++ /dev/null @@ -1,9 +0,0 @@ -// Binary: cache/js-dbg-32-f951e9151626-linux -// Flags: -m -n -// -o = evalcx("lazy").__proto__ -gc() -try { - o.watch() -} catch (e) {} -o.constructor() diff --git a/js/src/jit-test/tests/auto-regress/bug746397.js b/js/src/jit-test/tests/auto-regress/bug746397.js deleted file mode 100644 index d915ca7bbd..0000000000 --- a/js/src/jit-test/tests/auto-regress/bug746397.js +++ /dev/null @@ -1,10 +0,0 @@ -// |jit-test| error:ReferenceError - -// Binary: cache/js-dbg-64-67bf9a4a1f77-linux -// Flags: --ion-eager -// - -(function () { - var a = ['x', 'y']; - obj.watch(a[+("0")], counter); -})(); diff --git a/js/src/jit-test/tests/auto-regress/bug769192.js b/js/src/jit-test/tests/auto-regress/bug769192.js deleted file mode 100644 index 531e379120..0000000000 --- a/js/src/jit-test/tests/auto-regress/bug769192.js +++ /dev/null @@ -1,6 +0,0 @@ -// |jit-test| error:TypeError - -// Binary: cache/js-dbg-64-bf8f2961d0cc-linux -// Flags: -// -Object.watch.call(new Uint8ClampedArray, "length", function() {}); diff --git a/js/src/jit-test/tests/baseline/bug1344334.js b/js/src/jit-test/tests/baseline/bug1344334.js new file mode 100644 index 0000000000..66994338af --- /dev/null +++ b/js/src/jit-test/tests/baseline/bug1344334.js @@ -0,0 +1,14 @@ +if (!('oomTest' in this)) + quit(); + +function f(s) { + s + "x"; + s.indexOf("y") === 0; + oomTest(new Function(s)); +} +var s = ` + class TestClass { constructor() {} } + for (var fun of hasPrototype) {} +`; +if (s.length) + f(s); diff --git a/js/src/jit-test/tests/baseline/bug843444.js b/js/src/jit-test/tests/baseline/bug843444.js deleted file mode 100644 index 3a77402ac7..0000000000 --- a/js/src/jit-test/tests/baseline/bug843444.js +++ /dev/null @@ -1,8 +0,0 @@ -gczeal(8, 1) -function recurse(x) { - recurse; - if (x < 20) - recurse(x + 1); -}; -this.watch(5, (function () {})) -recurse(0) diff --git a/js/src/jit-test/tests/basic/bug510437.js b/js/src/jit-test/tests/basic/bug510437.js deleted file mode 100644 index 2418b9b117..0000000000 --- a/js/src/jit-test/tests/basic/bug510437.js +++ /dev/null @@ -1,13 +0,0 @@ -// Don't crash or assert. - -var d; -this.watch("d", eval); -(function () { - (eval("\ - (function () {\ - for (let x = 0; x < 2; ++x) {\ - d = x\ - }\ - })\ -"))() -})() diff --git a/js/src/jit-test/tests/basic/bug605015.js b/js/src/jit-test/tests/basic/bug605015.js deleted file mode 100644 index a35f7b6c7d..0000000000 --- a/js/src/jit-test/tests/basic/bug605015.js +++ /dev/null @@ -1,9 +0,0 @@ -// |jit-test| error: TypeError
-// don't assert
-
-print(this.watch("x",
-function() {
- Object.defineProperty(this, "x", ({
- get: (Int8Array)
- }))
-}))(x = /x/)
diff --git a/js/src/jit-test/tests/basic/bug631305.js b/js/src/jit-test/tests/basic/bug631305.js deleted file mode 100644 index b0cbbbb248..0000000000 --- a/js/src/jit-test/tests/basic/bug631305.js +++ /dev/null @@ -1,9 +0,0 @@ -var n = 0; -var a = []; -for (var i = 0; i < 20; i++) - a[i] = {}; -a[18].watch("p", function () { n++; }); -delete a[18].p; -for (var i = 0; i < 20; i++) - a[i].p = 0; -assertEq(n, 1); diff --git a/js/src/jit-test/tests/basic/bug662562.js b/js/src/jit-test/tests/basic/bug662562.js deleted file mode 100644 index 45b48589a9..0000000000 --- a/js/src/jit-test/tests/basic/bug662562.js +++ /dev/null @@ -1,6 +0,0 @@ -// |jit-test| error: TypeError -function f(o) { - o.watch("x", this); -} -var c = evalcx(""); -f(c); diff --git a/js/src/jit-test/tests/basic/bug690292.js b/js/src/jit-test/tests/basic/bug690292.js deleted file mode 100644 index 43ab56dd77..0000000000 --- a/js/src/jit-test/tests/basic/bug690292.js +++ /dev/null @@ -1,12 +0,0 @@ - -done = false; -try { - function x() {} - print(this.watch("d", Object.create)) - var d = {} -} catch (e) {} -try { - eval("d = ''") - done = true; -} catch (e) {} -assertEq(done, false); diff --git a/js/src/jit-test/tests/basic/bug696748.js b/js/src/jit-test/tests/basic/bug696748.js index fe171f976d..33fb4d52f9 100644 --- a/js/src/jit-test/tests/basic/bug696748.js +++ b/js/src/jit-test/tests/basic/bug696748.js @@ -1,6 +1,3 @@ -try { -this.watch("b", "".substring); -} catch(exc1) {} eval("\ var URI = '';\ test();\ diff --git a/js/src/jit-test/tests/basic/bug713226.js b/js/src/jit-test/tests/basic/bug713226.js index 3ae991f43f..36858b86c5 100644 --- a/js/src/jit-test/tests/basic/bug713226.js +++ b/js/src/jit-test/tests/basic/bug713226.js @@ -8,7 +8,7 @@ function addDebug(g, id) {\ var debuggerGlobal = newGlobal();\ debuggerGlobal.debuggee = g;\ debuggerGlobal.id = id;\ - debuggerGlobal.print = function (s) { (g) += s; };\ + debuggerGlobal.print = function (s) { print(s); };\ debuggerGlobal.eval('var dbg = new Debugger(debuggee);dbg.onDebuggerStatement = function () { print(id); debugger; };');\ return debuggerGlobal;\ }\ diff --git a/js/src/jit-test/tests/basic/bug831846.js b/js/src/jit-test/tests/basic/bug831846.js deleted file mode 100644 index 30bb3aa863..0000000000 --- a/js/src/jit-test/tests/basic/bug831846.js +++ /dev/null @@ -1,3 +0,0 @@ -// |jit-test| error:TypeError - -evalcx('').watch("", /()/); diff --git a/js/src/jit-test/tests/basic/function-tosource-bug779694.js b/js/src/jit-test/tests/basic/function-tosource-bug779694.js index 3893d3ff2c..782b2594bc 100644 --- a/js/src/jit-test/tests/basic/function-tosource-bug779694.js +++ b/js/src/jit-test/tests/basic/function-tosource-bug779694.js @@ -5,4 +5,4 @@ for (var i=0; i<400; ++i) { x += String.fromCharCode(i * 289); } var s = "'" + x + "'"; -assertEq(Function("evt", s).toString(), "function anonymous(evt) {\n" + s + "\n}"); +assertEq(Function("evt", s).toString(), "function anonymous(evt\n) {\n" + s + "\n}"); diff --git a/js/src/jit-test/tests/basic/function-tosource-constructor.js b/js/src/jit-test/tests/basic/function-tosource-constructor.js index e1d1443640..9a8961fe2c 100644 --- a/js/src/jit-test/tests/basic/function-tosource-constructor.js +++ b/js/src/jit-test/tests/basic/function-tosource-constructor.js @@ -1,14 +1,14 @@ var f = Function("a", "b", "return a + b;"); -assertEq(f.toString(), "function anonymous(a, b) {\nreturn a + b;\n}"); -assertEq(f.toSource(), "(function anonymous(a, b) {\nreturn a + b;\n})"); +assertEq(f.toString(), "function anonymous(a,b\n) {\nreturn a + b;\n}"); +assertEq(f.toSource(), "(function anonymous(a,b\n) {\nreturn a + b;\n})"); assertEq(decompileFunction(f), f.toString()); f = Function("a", "...rest", "return rest[42] + b;"); -assertEq(f.toString(), "function anonymous(a, ...rest) {\nreturn rest[42] + b;\n}"); -assertEq(f.toSource(), "(function anonymous(a, ...rest) {\nreturn rest[42] + b;\n})") +assertEq(f.toString(), "function anonymous(a,...rest\n) {\nreturn rest[42] + b;\n}"); +assertEq(f.toSource(), "(function anonymous(a,...rest\n) {\nreturn rest[42] + b;\n})") assertEq(decompileFunction(f), f.toString()); f = Function(""); -assertEq(f.toString(), "function anonymous() {\n\n}"); +assertEq(f.toString(), "function anonymous(\n) {\n\n}"); f = Function("", "(abc)"); -assertEq(f.toString(), "function anonymous() {\n(abc)\n}"); -f = Function("", "return function (a, b) a + b;")(); -assertEq(f.toString(), "function (a, b) a + b"); +assertEq(f.toString(), "function anonymous(\n) {\n(abc)\n}"); +f = Function("", "return function (a,b) a + b;")(); +assertEq(f.toString(), "function (a,b) a + b"); diff --git a/js/src/jit-test/tests/basic/function-tosource-getset.js b/js/src/jit-test/tests/basic/function-tosource-getset.js index 36c6d010e7..1804d38f2f 100644 --- a/js/src/jit-test/tests/basic/function-tosource-getset.js +++ b/js/src/jit-test/tests/basic/function-tosource-getset.js @@ -1,7 +1,7 @@ var o = {get prop() a + b, set prop(x) a + b}; var prop = Object.getOwnPropertyDescriptor(o, "prop"); -assertEq(prop.get.toString(), "function get prop() a + b"); -assertEq(prop.get.toSource(), "(function get prop() a + b)"); -assertEq(prop.set.toString(), "function set prop(x) a + b"); -assertEq(prop.set.toSource(), "(function set prop(x) a + b)"); -assertEq(o.toSource(), "({get prop () a + b, set prop (x) a + b})"); +assertEq(prop.get.toString(), "get prop() { a + b; }"); +assertEq(prop.get.toSource(), "get prop() { a + b; }"); +assertEq(prop.set.toString(), "set prop(x) { a + b; }"); +assertEq(prop.set.toSource(), "set prop(x) { a + b; }"); +assertEq(o.toSource(), "({get prop() { a + b; }, set prop(x) { a + b; }})"); diff --git a/js/src/jit-test/tests/basic/hasnativemethodpure-optimization.js b/js/src/jit-test/tests/basic/hasnativemethodpure-optimization.js new file mode 100644 index 0000000000..2f5e991864 --- /dev/null +++ b/js/src/jit-test/tests/basic/hasnativemethodpure-optimization.js @@ -0,0 +1,21 @@ +load(libdir + "asserts.js"); + +let string = Object.defineProperty(new String("123"), "valueOf", { + get: function() { throw "get-valueOf"; } +}); +assertThrowsValue(() => "" + string, "get-valueOf"); + +string = Object.defineProperty(new String("123"), "toString", { + get: function() { throw "get-toString"; } +}); +assertThrowsValue(() => string.toLowerCase(), "get-toString"); + +string = Object.defineProperty(new String("123"), Symbol.toPrimitive, { + get: function() { throw "get-toPrimitive"; } +}); +assertThrowsValue(() => string.toLowerCase(), "get-toPrimitive"); + +let number = Object.defineProperty(new Number(123), "valueOf", { + get: function() { throw "get-valueOf"; } +}); +assertThrowsValue(() => +number, "get-valueOf");
\ No newline at end of file diff --git a/js/src/jit-test/tests/basic/testAssigningWatchedDeletedProperty.js b/js/src/jit-test/tests/basic/testAssigningWatchedDeletedProperty.js deleted file mode 100644 index c22eabed08..0000000000 --- a/js/src/jit-test/tests/basic/testAssigningWatchedDeletedProperty.js +++ /dev/null @@ -1,7 +0,0 @@ -var o = {}; -o.watch("p", function() { }); - -for (var i = 0; i < 10; i++) { - o.p = 123; - delete o.p; -} diff --git a/js/src/jit-test/tests/basic/testBug566556.js b/js/src/jit-test/tests/basic/testBug566556.js deleted file mode 100644 index 244be57d22..0000000000 --- a/js/src/jit-test/tests/basic/testBug566556.js +++ /dev/null @@ -1,9 +0,0 @@ -var msg = ""; -try { - this.__defineSetter__('x', Object.create); - this.watch('x', function() {}); - x = 3; -} catch (e) { - msg = e.toString(); -} -assertEq(msg, "TypeError: undefined is not an object or null"); diff --git a/js/src/jit-test/tests/basic/testBug578044.js b/js/src/jit-test/tests/basic/testBug578044.js deleted file mode 100644 index c5b811dc9b..0000000000 --- a/js/src/jit-test/tests/basic/testBug578044.js +++ /dev/null @@ -1,13 +0,0 @@ -this.watch("x", Object.create) -try { - (function() { - this.__defineGetter__("x", - function() { - return this - }) - })() -} catch(e) {} -Object.defineProperty(x, "x", ({ - set: Uint16Array -})) - diff --git a/js/src/jit-test/tests/basic/testBug584650.js b/js/src/jit-test/tests/basic/testBug584650.js deleted file mode 100644 index b6c9d8ab7f..0000000000 --- a/js/src/jit-test/tests/basic/testBug584650.js +++ /dev/null @@ -1,9 +0,0 @@ -if (typeof gczeal != "function") - gczeal = function() {} - -// don't crash -x = (evalcx('lazy')) -x.watch("", function () {}) -gczeal(1) -for (w in x) {} - diff --git a/js/src/jit-test/tests/basic/testBug780288-1.js b/js/src/jit-test/tests/basic/testBug780288-1.js deleted file mode 100644 index 90746a04a3..0000000000 --- a/js/src/jit-test/tests/basic/testBug780288-1.js +++ /dev/null @@ -1,20 +0,0 @@ -s = newGlobal() -try { - evalcx("\ - Object.defineProperty(this,\"i\",{enumerable:true,get:function(){t}});\ - for each(y in this)true\ - ", s) -} catch (e) {} -try { - evalcx("\ - for(z=0,(7).watch(\"\",eval);;g){\ - if(z=1){({t:function(){}})\ - }\ - ", s) -} catch (e) {} -try { - evalcx("\ - Object.defineProperty(this,\"g2\",{get:function(){return this}});\ - g2.y()\ - ", s) -} catch (e) {} diff --git a/js/src/jit-test/tests/basic/testBug780288-2.js b/js/src/jit-test/tests/basic/testBug780288-2.js deleted file mode 100644 index 8c4c1737c8..0000000000 --- a/js/src/jit-test/tests/basic/testBug780288-2.js +++ /dev/null @@ -1,20 +0,0 @@ -s = newGlobal() -try { - evalcx("\ - Object.defineProperty(this,\"i\",{enumerable:true,get:function(){t}});\ - for each(y in this)true\ - ", s) -} catch (e) {} -try { - evalcx("\ - for(z=0,(7).watch(\"\",eval);;g){\ - if(z=1){({t:function(){}})\ - }\ - ", s) -} catch (e) {} -try { - evalcx("\ - Object.defineProperty(this,\"g2\",{get:function(){return this}});\ - g2.y(\"\")\ - ", s) -} catch (e) {} diff --git a/js/src/jit-test/tests/basic/testEvalCalledFromWatchOverSetter.js b/js/src/jit-test/tests/basic/testEvalCalledFromWatchOverSetter.js deleted file mode 100644 index bcd60fd807..0000000000 --- a/js/src/jit-test/tests/basic/testEvalCalledFromWatchOverSetter.js +++ /dev/null @@ -1,3 +0,0 @@ -this.__defineSetter__("x", function(){}); -this.watch("x", eval); -x = 0; diff --git a/js/src/jit-test/tests/basic/testFunctionStatementAliasLocals.js b/js/src/jit-test/tests/basic/testFunctionStatementAliasLocals.js index 7be49b7f39..be7f528b95 100644 --- a/js/src/jit-test/tests/basic/testFunctionStatementAliasLocals.js +++ b/js/src/jit-test/tests/basic/testFunctionStatementAliasLocals.js @@ -8,9 +8,11 @@ assertEq(typeof f1(true), "function"); assertEq(f1(false), 3); function f2(b, w) { + // Annex B doesn't apply to functions in blocks with the same name as a + // parameter. if (b) function w() {} return w; } -assertEq(typeof f2(true, 3), "function"); +assertEq(typeof f2(true, 3), "number"); assertEq(f2(false, 3), 3); diff --git a/js/src/jit-test/tests/basic/testLet.js b/js/src/jit-test/tests/basic/testLet.js index 263c3eb8a3..9a2f391973 100644 --- a/js/src/jit-test/tests/basic/testLet.js +++ b/js/src/jit-test/tests/basic/testLet.js @@ -9,7 +9,7 @@ function test(str, arg, result) var fun = new Function('x', str); var got = fun.toSource(); - var expect = '(function anonymous(x) {\n' + str + '\n})'; + var expect = '(function anonymous(x\n) {\n' + str + '\n})'; if (got !== expect) { print("GOT: " + got); print("EXPECT: " + expect); diff --git a/js/src/jit-test/tests/basic/testNonStubGetter.js b/js/src/jit-test/tests/basic/testNonStubGetter.js deleted file mode 100644 index 58a698eb4f..0000000000 --- a/js/src/jit-test/tests/basic/testNonStubGetter.js +++ /dev/null @@ -1,7 +0,0 @@ -function testNonStubGetter() { - { let [] = []; (this.watch("x", function(p, o, n) { return /a/g.exec(p, o, n); })); }; - (function () { (eval("(function(){for each (x in [1, 2, 2]);});"))(); })(); - this.unwatch("x"); - return "ok"; -} -assertEq(testNonStubGetter(), "ok"); diff --git a/js/src/jit-test/tests/basic/testSettingWatchPointOnReadOnlyProp.js b/js/src/jit-test/tests/basic/testSettingWatchPointOnReadOnlyProp.js deleted file mode 100644 index 78c281f05b..0000000000 --- a/js/src/jit-test/tests/basic/testSettingWatchPointOnReadOnlyProp.js +++ /dev/null @@ -1,7 +0,0 @@ -for (var i = 0; i < 5; ++i) { - var o = {} - Object.defineProperty(o, 'x', { value:"cow", writable:false }); - var r = o.watch('x', function() {}); - assertEq(r, undefined); - o.x = 4; -} diff --git a/js/src/jit-test/tests/basic/testTrueShiftTrue.js b/js/src/jit-test/tests/basic/testTrueShiftTrue.js deleted file mode 100644 index 44c1290d84..0000000000 --- a/js/src/jit-test/tests/basic/testTrueShiftTrue.js +++ /dev/null @@ -1,16 +0,0 @@ -// Test no assert or crash from outer recorders (bug 465145) -function testBug465145() { - this.__defineSetter__("x", function(){}); - this.watch("x", function(){}); - y = this; - for (var z = 0; z < 2; ++z) { x = y }; - this.__defineSetter__("x", function(){}); - for (var z = 0; z < 2; ++z) { x = y }; -} - -function testTrueShiftTrue() { - var a = new Array(5); - for (var i=0;i<5;++i) a[i] = "" + (true << true); - return a.join(","); -} -assertEq(testTrueShiftTrue(), "2,2,2,2,2"); diff --git a/js/src/jit-test/tests/basic/testWatchRecursion.js b/js/src/jit-test/tests/basic/testWatchRecursion.js deleted file mode 100644 index e5d5877df9..0000000000 --- a/js/src/jit-test/tests/basic/testWatchRecursion.js +++ /dev/null @@ -1,63 +0,0 @@ -// Test that the watch handler is not called recursively for the same object -// and property. -(function() { - var obj1 = {}, obj2 = {}; - var handler_entry_count = 0; - var handler_exit_count = 0; - - obj1.watch('x', handler); - obj1.watch('y', handler); - obj2.watch('x', handler); - obj1.x = 1; - assertEq(handler_entry_count, 3); - assertEq(handler_exit_count, 3); - - function handler(id) { - handler_entry_count++; - assertEq(handler_exit_count, 0); - switch (true) { - case this === obj1 && id === "x": - assertEq(handler_entry_count, 1); - obj2.x = 3; - assertEq(handler_exit_count, 2); - break; - case this === obj2 && id === "x": - assertEq(handler_entry_count, 2); - obj1.y = 4; - assertEq(handler_exit_count, 1); - break; - default: - assertEq(this, obj1); - assertEq(id, "y"); - assertEq(handler_entry_count, 3); - - // We expect no more watch handler invocations - obj1.x = 5; - obj1.y = 6; - obj2.x = 7; - assertEq(handler_exit_count, 0); - break; - } - ++handler_exit_count; - assertEq(handler_entry_count, 3); - } -})(); - - -// Test that run-away recursion in watch handlers is properly handled. -(function() { - var obj = {}; - var i = 0; - try { - handler(); - throw new Error("Unreachable"); - } catch(e) { - assertEq(e instanceof InternalError, true); - } - - function handler() { - var prop = "a" + ++i; - obj.watch(prop, handler); - obj[prop] = 2; - } -})(); diff --git a/js/src/jit-test/tests/basic/unboxed-object-clear-new-script.js b/js/src/jit-test/tests/basic/unboxed-object-clear-new-script.js deleted file mode 100644 index f55456222d..0000000000 --- a/js/src/jit-test/tests/basic/unboxed-object-clear-new-script.js +++ /dev/null @@ -1,49 +0,0 @@ - -function Foo(a, b) { - this.a = a; - this.b = b; -} - -function invalidate_foo() { - var a = []; - var counter = 0; - for (var i = 0; i < 50; i++) - a.push(new Foo(i, i + 1)); - Object.defineProperty(Foo.prototype, "a", {configurable: true, set: function() { counter++; }}); - for (var i = 0; i < 50; i++) - a.push(new Foo(i, i + 1)); - delete Foo.prototype.a; - var total = 0; - for (var i = 0; i < a.length; i++) { - assertEq('a' in a[i], i < 50); - total += a[i].b; - } - assertEq(total, 2550); - assertEq(counter, 50); -} -invalidate_foo(); - -function Bar(a, b, fn) { - this.a = a; - if (b == 30) - Object.defineProperty(Bar.prototype, "b", {configurable: true, set: fn}); - this.b = b; -} - -function invalidate_bar() { - var a = []; - var counter = 0; - function fn() { counter++; } - for (var i = 0; i < 50; i++) - a.push(new Bar(i, i + 1, fn)); - delete Bar.prototype.b; - var total = 0; - for (var i = 0; i < a.length; i++) { - assertEq('a' in a[i], true); - assertEq('b' in a[i], i < 29); - total += a[i].a; - } - assertEq(total, 1225); - assertEq(counter, 21); -} -invalidate_bar(); diff --git a/js/src/jit-test/tests/basic/unboxed-object-convert-to-native.js b/js/src/jit-test/tests/basic/unboxed-object-convert-to-native.js deleted file mode 100644 index 691fe166c3..0000000000 --- a/js/src/jit-test/tests/basic/unboxed-object-convert-to-native.js +++ /dev/null @@ -1,47 +0,0 @@ - -// Test various ways of converting an unboxed object to native. - -function Foo(a, b) { - this.a = a; - this.b = b; -} - -var proxyObj = { - get: function(recipient, name) { - return recipient[name] + 2; - } -}; - -function f() { - var a = []; - for (var i = 0; i < 50; i++) - a.push(new Foo(i, i + 1)); - - var prop = "a"; - - i = 0; - for (; i < 5; i++) - a[i].c = i; - for (; i < 10; i++) - Object.defineProperty(a[i], 'c', {value: i}); - for (; i < 15; i++) - a[i] = new Proxy(a[i], proxyObj); - for (; i < 20; i++) - a[i].a = 3.5; - for (; i < 25; i++) - delete a[i].b; - for (; i < 30; i++) - a[prop] = 4; - - var total = 0; - for (i = 0; i < a.length; i++) { - if ('a' in a[i]) - total += a[i].a; - if ('b' in a[i]) - total += a[i].b; - if ('c' in a[i]) - total += a[i].c; - } - assertEq(total, 2382.5); -} -f(); diff --git a/js/src/jit-test/tests/basic/unboxed-object-getelem.js b/js/src/jit-test/tests/basic/unboxed-object-getelem.js deleted file mode 100644 index b30b8325a0..0000000000 --- a/js/src/jit-test/tests/basic/unboxed-object-getelem.js +++ /dev/null @@ -1,20 +0,0 @@ - -function f() { - var propNames = ["a","b","c","d","e","f","g","h","i","j","x","y"]; - var arr = []; - for (var i=0; i<64; i++) - arr.push({x:1, y:2}); - for (var i=0; i<64; i++) { - // Make sure there are expandos with dynamic slots for each object. - for (var j = 0; j < propNames.length; j++) - arr[i][propNames[j]] = j; - } - var res = 0; - for (var i=0; i<100000; i++) { - var o = arr[i % 64]; - var p = propNames[i % propNames.length]; - res += o[p]; - } - assertEq(res, 549984); -} -f(); diff --git a/js/src/jit-test/tests/basic/unboxed-object-set-property.js b/js/src/jit-test/tests/basic/unboxed-object-set-property.js deleted file mode 100644 index fdcfcf6b22..0000000000 --- a/js/src/jit-test/tests/basic/unboxed-object-set-property.js +++ /dev/null @@ -1,31 +0,0 @@ - -// Use the correct receiver when non-native objects are prototypes of other objects. - -function Thing(a, b) { - this.a = a; - this.b = b; -} - -function foo() { - var array = []; - for (var i = 0; i < 10000; i++) - array.push(new Thing(i, i + 1)); - - var proto = new Thing(1, 2); - var obj = Object.create(proto); - - Object.defineProperty(Thing.prototype, "c", {set:function() { this.d = 0; }}); - obj.c = 3; - assertEq(obj.c, undefined); - assertEq(obj.d, 0); - assertEq(obj.hasOwnProperty("d"), true); - assertEq(proto.d, undefined); - assertEq(proto.hasOwnProperty("d"), false); - - obj.a = 3; - assertEq(obj.a, 3); - assertEq(proto.a, 1); - assertEq(obj.hasOwnProperty("a"), true); -} - -foo(); diff --git a/js/src/jit-test/tests/basic/unboxed-property-enumeration.js b/js/src/jit-test/tests/basic/unboxed-property-enumeration.js deleted file mode 100644 index 142d932dd3..0000000000 --- a/js/src/jit-test/tests/basic/unboxed-property-enumeration.js +++ /dev/null @@ -1,24 +0,0 @@ -function O() { - this.x = 1; - this.y = 2; -} -function testUnboxed() { - var arr = []; - for (var i=0; i<100; i++) - arr.push(new O); - - var o = arr[arr.length-1]; - o[0] = 0; - o[2] = 2; - var sym = Symbol(); - o[sym] = 1; - o.z = 3; - Object.defineProperty(o, '3', {value:1,enumerable:false,configurable:false,writable:false}); - o[4] = 4; - - var props = Reflect.ownKeys(o); - assertEq(props[props.length-1], sym); - - assertEq(Object.getOwnPropertyNames(o).join(""), "0234xyz"); -} -testUnboxed(); diff --git a/js/src/jit-test/tests/class/bug1357506.js b/js/src/jit-test/tests/class/bug1357506.js new file mode 100644 index 0000000000..52a5643e62 --- /dev/null +++ b/js/src/jit-test/tests/class/bug1357506.js @@ -0,0 +1,8 @@ +// Test that constructors that abort due to asm.js do not assert due to the +// parser keeping track of the FunctionBox corresponding to the constructor. + +class a { + constructor() { + "use asm"; + } +} diff --git a/js/src/jit-test/tests/class/bug1359622.js b/js/src/jit-test/tests/class/bug1359622.js new file mode 100644 index 0000000000..b4a0df7490 --- /dev/null +++ b/js/src/jit-test/tests/class/bug1359622.js @@ -0,0 +1,4 @@ +setDiscardSource(true) +evaluate(` + unescape(class get { static staticMethod() {} }); +`); diff --git a/js/src/jit-test/tests/ctypes/function-definition.js b/js/src/jit-test/tests/ctypes/function-definition.js index 4df317a09d..5882ba8891 100644 --- a/js/src/jit-test/tests/ctypes/function-definition.js +++ b/js/src/jit-test/tests/ctypes/function-definition.js @@ -27,7 +27,7 @@ function test() { let lib; try { - lib = ctypes.open(ctypes.libraryName("c")); + lib = ctypes.open(ctypes.libraryName("m")); } catch (e) { } if (!lib) diff --git a/js/src/jit-test/tests/debug/Script-gc-02.js b/js/src/jit-test/tests/debug/Script-gc-02.js index 04dd4b220f..9689a6ebe4 100644 --- a/js/src/jit-test/tests/debug/Script-gc-02.js +++ b/js/src/jit-test/tests/debug/Script-gc-02.js @@ -10,5 +10,5 @@ assertEq(arr.length, 10); gc(); for (var i = 0; i < arr.length; i++) - assertEq(arr[i].lineCount, 3); + assertEq(arr[i].lineCount, 4); diff --git a/js/src/jit-test/tests/debug/Script-gc-03.js b/js/src/jit-test/tests/debug/Script-gc-03.js index 30c3e8dbca..5ecb4556f2 100644 --- a/js/src/jit-test/tests/debug/Script-gc-03.js +++ b/js/src/jit-test/tests/debug/Script-gc-03.js @@ -10,6 +10,6 @@ assertEq(arr.length, 100); gc(g); for (var i = 0; i < arr.length; i++) - assertEq(arr[i].lineCount, 3); + assertEq(arr[i].lineCount, 4); gc(); diff --git a/js/src/jit-test/tests/debug/Script-sourceStart-04.js b/js/src/jit-test/tests/debug/Script-sourceStart-04.js index 2aa382b7b4..4546818e47 100644 --- a/js/src/jit-test/tests/debug/Script-sourceStart-04.js +++ b/js/src/jit-test/tests/debug/Script-sourceStart-04.js @@ -20,6 +20,6 @@ function test(string, range) { } test("eval('2 * 3')", [0, 5]); -test("new Function('2 * 3')", [0, 12]); -test("new Function('x', 'x * x')", [0, 13]); +test("new Function('2 * 3')", [0, 31]); +test("new Function('x', 'x * x')", [0, 32]); assertEq(count, 6); diff --git a/js/src/jit-test/tests/debug/Source-text-02.js b/js/src/jit-test/tests/debug/Source-text-02.js index 64cfce92a4..46e76015ec 100644 --- a/js/src/jit-test/tests/debug/Source-text-02.js +++ b/js/src/jit-test/tests/debug/Source-text-02.js @@ -3,6 +3,7 @@ let g = newGlobal(); let dbg = new Debugger(g); +var text; var count = 0; dbg.onNewScript = function (script) { ++count; diff --git a/js/src/jit-test/tests/debug/wasm-12.js b/js/src/jit-test/tests/debug/wasm-12.js new file mode 100644 index 0000000000..18d3b574d8 --- /dev/null +++ b/js/src/jit-test/tests/debug/wasm-12.js @@ -0,0 +1,26 @@ +// Tests that wasm module scripts have special URLs. + +if (!wasmIsSupported()) + quit(); + +var g = newGlobal(); +g.eval(` +function initWasm(s) { return new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(s))); } +o = initWasm('(module (func) (export "" 0))'); +o2 = initWasm('(module (func) (func) (export "" 1))'); +`); + +function isWasm(script) { return script.format === "wasm"; } + +function isValidWasmURL(url) { + // The URLs will have the following format: + // wasm: [<uri-econded-filename-of-host> ":"] <64-bit-hash> + return /^wasm:(?:[^:]*:)*?[0-9a-f]{16}$/.test(url); +} + +var dbg = new Debugger(g); +var foundScripts = dbg.findScripts().filter(isWasm); +assertEq(foundScripts.length, 2); +assertEq(isValidWasmURL(foundScripts[0].source.url), true); +assertEq(isValidWasmURL(foundScripts[1].source.url), true); +assertEq(foundScripts[0].source.url != foundScripts[1].source.url, true); diff --git a/js/src/jit-test/tests/gc/bug-900405.js b/js/src/jit-test/tests/gc/bug-900405.js deleted file mode 100644 index eeec6f25f1..0000000000 --- a/js/src/jit-test/tests/gc/bug-900405.js +++ /dev/null @@ -1,3 +0,0 @@ -(function() { - [{ "9": [] }.watch([], function(){})] -})() diff --git a/js/src/jit-test/tests/gc/bug-913261.js b/js/src/jit-test/tests/gc/bug-913261.js deleted file mode 100644 index 43066053ff..0000000000 --- a/js/src/jit-test/tests/gc/bug-913261.js +++ /dev/null @@ -1,5 +0,0 @@ -// |jit-test| error: InternalError: too much recursion -(function f() { - "".watch(2, function() {}); - f(); -})() diff --git a/js/src/jit-test/tests/gc/bug-986864.js b/js/src/jit-test/tests/gc/bug-986864.js deleted file mode 100644 index abb8de6b2b..0000000000 --- a/js/src/jit-test/tests/gc/bug-986864.js +++ /dev/null @@ -1,8 +0,0 @@ -// |jit-test| slow -function x() {} -for (var j = 0; j < 9999; ++j) { - (function() { - x += x.watch("endsWith", ArrayBuffer); - return 0 >> Function(x) - })() -} diff --git a/js/src/jit-test/tests/ion/bug1063182.js b/js/src/jit-test/tests/ion/bug1063182.js deleted file mode 100644 index 9cda480995..0000000000 --- a/js/src/jit-test/tests/ion/bug1063182.js +++ /dev/null @@ -1,8 +0,0 @@ -// |jit-test| error: ReferenceError - -eval("(function() { " + "\ -var o = {};\ -o.watch('p', function() { });\ -for (var i = 0; i < 10; \u5ede ++)\ - o.p = 123;\ -" + " })();"); diff --git a/js/src/jit-test/tests/ion/bug1493900-1.js b/js/src/jit-test/tests/ion/bug1493900-1.js new file mode 100644 index 0000000000..643c1943d6 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1493900-1.js @@ -0,0 +1,17 @@ +function f() { + var objs = []; + for (var i = 0; i < 100; i++) { + objs[i] = {}; + } + var o = objs[0]; + var a = new Float64Array(1024); + function g(a, b) { + let p = b; + for (; p.x < 0; p = p.x) { + while (p === p) {} + } + for (var i = 0; i < 10000; ++i) {} + } + g(a, o); +} +f(); diff --git a/js/src/jit-test/tests/ion/bug1493900-2.js b/js/src/jit-test/tests/ion/bug1493900-2.js new file mode 100644 index 0000000000..7e7f5fdecd --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1493900-2.js @@ -0,0 +1,7 @@ +function f(a, b) { + for (; b.x < 0; b = b.x) { + while (b === b) {}; + } + for (var i = 0; i < 99999; ++i) {} +} +f(0, 0); diff --git a/js/src/jit-test/tests/ion/bug772901.js b/js/src/jit-test/tests/ion/bug772901.js index eb71f6afb8..164afd1517 100644 --- a/js/src/jit-test/tests/ion/bug772901.js +++ b/js/src/jit-test/tests/ion/bug772901.js @@ -4,4 +4,4 @@ function f(x) { delete ((x)++); arguments[0] !== undefined; } -f(1, x = [f.ArrayBuffer,unwatch.Int32Array], this, this, this) ; +f(1, x = [f.ArrayBuffer, undefined], this, this, this) ; diff --git a/js/src/jit-test/tests/ion/bug774257-1.js b/js/src/jit-test/tests/ion/bug774257-1.js deleted file mode 100644 index 9c998a028e..0000000000 --- a/js/src/jit-test/tests/ion/bug774257-1.js +++ /dev/null @@ -1,8 +0,0 @@ -Object.defineProperty(Object.prototype, 'x', { - set: function() { evalcx('lazy'); } -}); -var obj = {}; -obj.watch("x", function (id, oldval, newval) {}); -for (var str in 'A') { - obj.x = 1; -} diff --git a/js/src/jit-test/tests/ion/bug774257-2.js b/js/src/jit-test/tests/ion/bug774257-2.js deleted file mode 100644 index b31043b089..0000000000 --- a/js/src/jit-test/tests/ion/bug774257-2.js +++ /dev/null @@ -1,10 +0,0 @@ -Object.defineProperty(Object.prototype, 'x', { - set: function() { evalcx('lazy'); } -}); -var obj = {}; -var prot = {}; -obj.__proto__ = prot; -obj.watch("x", function (id, oldval, newval) {}); -for (var str in 'A') { - obj.x = 1; -} diff --git a/js/src/jit-test/tests/ion/bug779631.js b/js/src/jit-test/tests/ion/bug779631.js deleted file mode 100644 index 087aa01ac2..0000000000 --- a/js/src/jit-test/tests/ion/bug779631.js +++ /dev/null @@ -1,9 +0,0 @@ -var flag = 0; -var a = {}; -Object.defineProperty(a, "value", {set: function(x) {}}); -a.watch("value", function(){flag++;}); - -for(var i = 0; i < 100; i++) { - a.value = i; - assertEq(flag, i+1); -} diff --git a/js/src/jit-test/tests/ion/bug783590.js b/js/src/jit-test/tests/ion/bug783590.js index d48cb609eb..9d277e02c9 100644 --- a/js/src/jit-test/tests/ion/bug783590.js +++ b/js/src/jit-test/tests/ion/bug783590.js @@ -7,7 +7,6 @@ Object.defineProperty(arr, 0, { glob.__proto__; }) }); -this.watch("s", function() {}); try { arr.pop(); } catch (e) {} diff --git a/js/src/jit-test/tests/ion/unboxed-objects-invalidate.js b/js/src/jit-test/tests/ion/unboxed-objects-invalidate.js deleted file mode 100644 index 02e27614fe..0000000000 --- a/js/src/jit-test/tests/ion/unboxed-objects-invalidate.js +++ /dev/null @@ -1,16 +0,0 @@ - -var a = []; -for (var i = 0; i < 2000; i++) - a.push({f:i}); - -function f() { - var total = 0; - for (var i = 0; i < a.length; i++) - total += a[i].f; - return total; -} -assertEq(f(), 1999000); - -var sub = Object.create(a[0]); - -assertEq(f(), 1999000); diff --git a/js/src/jit-test/tests/jaeger/bug550665.js b/js/src/jit-test/tests/jaeger/bug550665.js deleted file mode 100644 index 816888b5ef..0000000000 --- a/js/src/jit-test/tests/jaeger/bug550665.js +++ /dev/null @@ -1,8 +0,0 @@ -(function () { - var a; - eval("for(w in ((function(x,y){b:0})())) ;"); -})(); - -this.__defineSetter__("l", function() { gc() }); -this.watch("l", function(x) { yield {} }); -l = true; diff --git a/js/src/jit-test/tests/jaeger/bug557063.js b/js/src/jit-test/tests/jaeger/bug557063.js deleted file mode 100644 index ea77fd2c87..0000000000 --- a/js/src/jit-test/tests/jaeger/bug557063.js +++ /dev/null @@ -1,7 +0,0 @@ -(function() { - for (a = 0; a < 2; a++) - ''.watch("", function() {}) -})() - -/* Don't crash or assert. */ - diff --git a/js/src/jit-test/tests/jaeger/bug588338.js b/js/src/jit-test/tests/jaeger/bug588338.js index 457be238a1..c400d76875 100644 --- a/js/src/jit-test/tests/jaeger/bug588338.js +++ b/js/src/jit-test/tests/jaeger/bug588338.js @@ -9,7 +9,6 @@ function f() { } } })(/x/))) -for (z = 0; z < 100; x.unwatch(), z++) for (e in [0]) { gczeal(2) } ( [1,2,3])("") diff --git a/js/src/jit-test/tests/jaeger/bug625438.js b/js/src/jit-test/tests/jaeger/bug625438.js deleted file mode 100644 index 32586d8ab4..0000000000 --- a/js/src/jit-test/tests/jaeger/bug625438.js +++ /dev/null @@ -1,10 +0,0 @@ -// vim: set ts=8 sts=4 et sw=4 tw=99: - -var count = 0; -this.watch("x", function() { - count++; -}); -for(var i=0; i<10; i++) { - x = 2; -} -assertEq(count, 10); diff --git a/js/src/jit-test/tests/jaeger/bug630366.js b/js/src/jit-test/tests/jaeger/bug630366.js deleted file mode 100644 index acac8d3ef4..0000000000 --- a/js/src/jit-test/tests/jaeger/bug630366.js +++ /dev/null @@ -1,7 +0,0 @@ -var o = {}; -for(var i=0; i<5; i++) { - o.p = 2; - o.watch("p", function() { }); - o.p = 2; - delete o.p; -} diff --git a/js/src/jit-test/tests/jaeger/recompile/bug641225.js b/js/src/jit-test/tests/jaeger/recompile/bug641225.js index a6e3a86c78..7b79781972 100644 --- a/js/src/jit-test/tests/jaeger/recompile/bug641225.js +++ b/js/src/jit-test/tests/jaeger/recompile/bug641225.js @@ -118,7 +118,6 @@ for(var o2 in f5) { f2(o5); f2(o5); f0(o3); - o9.watch('p3', function() {}); o8[o8] = o8; f0(o5); f1(o6); diff --git a/js/src/jit-test/tests/latin1/assorted.js b/js/src/jit-test/tests/latin1/assorted.js index cef79cb96f..1389a1adae 100644 --- a/js/src/jit-test/tests/latin1/assorted.js +++ b/js/src/jit-test/tests/latin1/assorted.js @@ -12,18 +12,18 @@ var o = {}; Object.defineProperty(o, "prop", {get: function() { return 1; }, set: function() { return 2; }, enumerable: true, configurable: true}); -assertEq(o.toSource(), "({get prop () { return 1; }, set prop () { return 2; }})"); +assertEq(o.toSource(), "({get prop() { return 1; }, set prop() { return 2; }})"); // obj.toSource TwoByte Object.defineProperty(o, "prop", {get: function() { return "\u1200"; }, set: function() { return "\u1200"; }, enumerable: true}); -assertEq(o.toSource(), '({get prop () { return "\\u1200"; }, set prop () { return "\\u1200"; }})'); +assertEq(o.toSource(), '({get prop() { return "\\u1200"; }, set prop() { return "\\u1200"; }})'); var ff = function() { return 10; }; ff.toSource = function() { return "((11))"; } Object.defineProperty(o, "prop", {get: ff, set: ff, enumerable: true}); -assertEq(o.toSource(), "({prop:((11)), prop:((11))})"); +assertEq(o.toSource(), "({get prop(11), set prop(11)})"); // XDR load(libdir + 'bytecode-cache.js'); diff --git a/js/src/jit-test/tests/latin1/function.js b/js/src/jit-test/tests/latin1/function.js index a0dedf2516..07a76733a3 100644 --- a/js/src/jit-test/tests/latin1/function.js +++ b/js/src/jit-test/tests/latin1/function.js @@ -6,11 +6,11 @@ function test() { var f = Function(arg1TwoByte, arg2Latin1, bodyLatin1); assertEq(f(10, 20), 60); - assertEq(f.toSource().includes("arg1\u1200, arg2"), true); + assertEq(f.toSource().includes("arg1\u1200,arg2"), true); var bodyTwoByte = "return arg1\u1200 + arg2;"; f = Function(arg1TwoByte, arg2Latin1, bodyTwoByte); assertEq(f(30, 40), 70); - assertEq(f.toSource().includes("arg1\u1200, arg2"), true); + assertEq(f.toSource().includes("arg1\u1200,arg2"), true); } test(); diff --git a/js/src/jit-test/tests/modules/export-declaration.js b/js/src/jit-test/tests/modules/export-declaration.js index 3c4a9b7355..9925f2c68b 100644 --- a/js/src/jit-test/tests/modules/export-declaration.js +++ b/js/src/jit-test/tests/modules/export-declaration.js @@ -403,12 +403,52 @@ assertThrowsInstanceOf(function() { parseAsModule("export {,} from 'a'"); }, SyntaxError); +program([ + exportDeclaration( + null, + [ + exportSpecifier( + ident("true"), + ident("true") + ), + ], + lit("b"), + false + ) +]).assert(parseAsModule("export { true } from 'b'")); + +program([ + exportDeclaration( + null, + [ + exportSpecifier( + ident("true"), + ident("name") + ), + ], + lit("b"), + false + ) +]).assert(parseAsModule("export { true as name } from 'b'")); + +assertThrowsInstanceOf(function() { + parseAsModule("export { true }"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("export { true as name }"); +}, SyntaxError); + +assertThrowsInstanceOf(function() { + parseAsModule("export { static }"); +}, SyntaxError); + assertThrowsInstanceOf(function() { - parseAsModule("export { true as a } from 'b'"); + parseAsModule("export { static as name }"); }, SyntaxError); assertThrowsInstanceOf(function () { - parseAsModule("export { a } from 'b' f();"); + parseAsModule("export { name } from 'b' f();"); }, SyntaxError); assertThrowsInstanceOf(function () { diff --git a/js/src/jit-test/tests/modules/function-redeclaration.js b/js/src/jit-test/tests/modules/function-redeclaration.js new file mode 100644 index 0000000000..b84704641d --- /dev/null +++ b/js/src/jit-test/tests/modules/function-redeclaration.js @@ -0,0 +1,94 @@ +load(libdir + "asserts.js"); + +var functionDeclarations = [ + "function f(){}", + "function* f(){}", + "async function f(){}", +]; + +var varDeclarations = [ + "var f", + "{ var f; }", + "for (var f in null);", + "for (var f of null);", + "for (var f; ;);", +]; + +var lexicalDeclarations = [ + "let f;", + "const f = 0;", + "class f {};", +]; + +var imports = [ + "import f from '';", + "import f, {} from '';", + "import d, {f} from '';", + "import d, {f as f} from '';", + "import d, {foo as f} from '';", + "import f, * as d from '';", + "import d, * as f from '';", + "import {f} from '';", + "import {f as f} from '';", + "import {foo as f} from '';", + "import* as f from '';", +]; + +var exports = [ + "export var f;", + ...functionDeclarations.map(fn => `export ${fn};`), + ...lexicalDeclarations.map(ld => `export ${ld};`), + ...functionDeclarations.map(fn => `export default ${fn};`), + "export default class f {};", +]; + +var redeclarations = [ + ...functionDeclarations, + ...varDeclarations, + ...lexicalDeclarations, + ...imports, + ...exports, +]; + +var noredeclarations = [ + ...functionDeclarations.map(fn => `{ ${fn} }`), + ...lexicalDeclarations.map(ld => `{ ${ld} }`), + ...["let", "const"].map(ld => `for (${ld} f in null);`), + ...["let", "const"].map(ld => `for (${ld} f of null);`), + ...["let", "const"].map(ld => `for (${ld} f = 0; ;);`), + "export {f};", + "export {f as f};", + "export {foo as f}; var foo;", + "export {f} from '';", + "export {f as f} from '';", + "export {foo as f} from '';", +]; + +for (var decl of functionDeclarations) { + for (var redecl of redeclarations) { + assertThrowsInstanceOf(() => { + parseModule(` + ${decl} + ${redecl} + `); + }, SyntaxError); + + assertThrowsInstanceOf(() => { + parseModule(` + ${redecl} + ${decl} + `); + }, SyntaxError); + } + + for (var redecl of noredeclarations) { + parseModule(` + ${decl} + ${redecl} + `); + parseModule(` + ${redecl} + ${decl} + `); + } +} diff --git a/js/src/jit-test/tests/parser/arrow-rest.js b/js/src/jit-test/tests/parser/arrow-rest.js index 53750f25be..b1429066ef 100644 --- a/js/src/jit-test/tests/parser/arrow-rest.js +++ b/js/src/jit-test/tests/parser/arrow-rest.js @@ -39,7 +39,7 @@ testThrow(` testThrow(` ({...a)=> -`, 2); +`, 6); testThrow(` function f([... ...a)=> @@ -47,7 +47,7 @@ function f([... ...a)=> testThrow(` function f({...a)=> -`, 12); +`, 16); // arrow @@ -67,7 +67,7 @@ var [... ...a)=> testThrow(` var {...a)=> -`, 5); +`, 9); // initializer diff --git a/js/src/jit-test/tests/parser/missing-closing-brace.js b/js/src/jit-test/tests/parser/missing-closing-brace.js new file mode 100644 index 0000000000..6820954aeb --- /dev/null +++ b/js/src/jit-test/tests/parser/missing-closing-brace.js @@ -0,0 +1,90 @@ +function test(source, [lineNumber, columnNumber], openType = "{", closeType = "}") { + let caught = false; + try { + Reflect.parse(source, { source: "foo.js" }); + } catch (e) { + assertEq(e.message.includes("missing " + closeType + " "), true); + let notes = getErrorNotes(e); + assertEq(notes.length, 1); + let note = notes[0]; + assertEq(note.message, openType + " opened at line " + lineNumber + ", column " + columnNumber); + assertEq(note.fileName, "foo.js"); + assertEq(note.lineNumber, lineNumber); + assertEq(note.columnNumber, columnNumber); + caught = true; + } + assertEq(caught, true); +} + +// Function + +test(` +function test1() { +} +function test2() { + if (true) { + //} +} +function test3() { +} +`, [4, 17]); + +// Block statement. +test(` +{ + if (true) { +} +`, [2, 0]); +test(` +if (true) { + if (true) { +} +`, [2, 10]); +test(` +for (;;) { + if (true) { +} +`, [2, 9]); +test(` +while (true) { + if (true) { +} +`, [2, 13]); +test(` +do { + do { +} while(true); +`, [2, 3]); + +// try-catch-finally. +test(` +try { + if (true) { +} +`, [2, 4]); +test(` +try { +} catch (e) { + if (true) { +} +`, [3, 12]); +test(` +try { +} finally { + if (true) { +} +`, [3, 10]); + +// Object literal. +test(` +var x = { + foo: { +}; +`, [2, 8]); + +// Array literal. +test(` +var x = [ + [ +]; +`, [2, 8], "[", "]"); diff --git a/js/src/jit-test/tests/parser/redeclaration.js b/js/src/jit-test/tests/parser/redeclaration.js new file mode 100644 index 0000000000..f719021ac7 --- /dev/null +++ b/js/src/jit-test/tests/parser/redeclaration.js @@ -0,0 +1,230 @@ +// Error message for redeclaration should show the position where the variable +// was declared. + +const npos = -1; + +function test_one(fun, filename, name, + [prevLineNumber, prevColumnNumber], + [lineNumber, columnNumber]) { + let caught = false; + try { + fun(); + } catch (e) { + assertEq(e.message.includes("redeclaration"), true); + assertEq(e.lineNumber, lineNumber); + assertEq(e.columnNumber, columnNumber); + let notes = getErrorNotes(e); + if (prevLineNumber == npos) { + assertEq(notes.length, 0); + } else { + assertEq(notes.length, 1); + let note = notes[0]; + assertEq(note.message, + `Previously declared at line ${prevLineNumber}, column ${prevColumnNumber}`); + assertEq(note.lineNumber, prevLineNumber); + assertEq(note.columnNumber, prevColumnNumber); + if (filename) + assertEq(note.fileName, filename); + } + caught = true; + } + assertEq(caught, true); +} + +function test_parse(source, ...args) { + test_one(() => { + Reflect.parse(source, { source: "foo.js" }); + }, "foo.js", ...args); +} + +function test_eval(source, ...args) { + test_one(() => { + eval(source); + }, undefined, ...args); +} + +function test(...args) { + test_parse(...args); + test_eval(...args); +} + +// let + +test(` +let a, a; +`, "a", [2, 4], [2, 7]); + +test(` +let a; +let a; +`, "a", [2, 4], [3, 4]); + +test(` +let a; +const a = 1; +`, "a", [2, 4], [3, 6]); + +test(` +let a; +var a; +`, "a", [2, 4], [3, 4]); + +test(` +let a; +function a() { +} +`, "a", [2, 4], [3, 9]); + +test(` +{ + let a; + function a() { + } +} +`, "a", [3, 6], [4, 11]); + +// const + +test(` +const a = 1, a = 2; +`, "a", [2, 6], [2, 13]); + +test(` +const a = 1; +const a = 2; +`, "a", [2, 6], [3, 6]); + +test(` +const a = 1; +let a; +`, "a", [2, 6], [3, 4]); + +test(` +const a = 1; +var a; +`, "a", [2, 6], [3, 4]); + +test(` +const a = 1; +function a() { +} +`, "a", [2, 6], [3, 9]); + +test(` +{ + const a = 1; + function a() { + } +} +`, "a", [3, 8], [4, 11]); + +// var + +test(` +var a; +let a; +`, "a", [2, 4], [3, 4]); + +test(` +var a; +const a = 1; +`, "a", [2, 4], [3, 6]); + +// function + +test(` +function a() {}; +let a; +`, "a", [2, 9], [3, 4]); + +test(` +function a() {}; +const a = 1; +`, "a", [2, 9], [3, 6]); + +// Annex B lexical function + +test(` +{ + function a() {}; + let a; +} +`, "a", [3, 11], [4, 6]); + +test(` +{ + function a() {}; + const a = 1; +} +`, "a", [3, 11], [4, 8]); + +// catch parameter + +test(` +try { +} catch (a) { + let a; +} +`, "a", [3, 9], [4, 6]); + +test(` +try { +} catch (a) { + const a = 1; +} +`, "a", [3, 9], [4, 8]); + +test(` +try { +} catch (a) { + function a() { + } +} +`, "a", [3, 9], [4, 11]); + +// parameter + +test(` +function f(a) { + let a; +} +`, "a", [2, 11], [3, 6]); + +test(` +function f(a) { + const a = 1; +} +`, "a", [2, 11], [3, 8]); + +test(` +function f([a]) { + let a; +} +`, "a", [2, 12], [3, 6]); + +test(` +function f({a}) { + let a; +} +`, "a", [2, 12], [3, 6]); + +test(` +function f(...a) { + let a; +} +`, "a", [2, 14], [3, 6]); + +test(` +function f(a=1) { + let a; +} +`, "a", [2, 11], [3, 6]); + +// eval +// We don't have position information at runtime. +// No note should be shown. + +test_eval(` +let a; +eval("var a"); +`, "a", [npos, npos], [1, 4]); diff --git a/js/src/jit-test/tests/pic/fuzz1.js b/js/src/jit-test/tests/pic/fuzz1.js deleted file mode 100644 index 2481a13140..0000000000 --- a/js/src/jit-test/tests/pic/fuzz1.js +++ /dev/null @@ -1,4 +0,0 @@ -(function() {
- for (a = 0; a < 2; a++)
- ''.watch("", function() {})
-})()
diff --git a/js/src/jit-test/tests/pic/fuzz3.js b/js/src/jit-test/tests/pic/fuzz3.js deleted file mode 100644 index 17613b6f5b..0000000000 --- a/js/src/jit-test/tests/pic/fuzz3.js +++ /dev/null @@ -1,3 +0,0 @@ -for each(let w in [[], 0, [], 0]) {
- w.unwatch()
-}
diff --git a/js/src/jit-test/tests/pic/watch1.js b/js/src/jit-test/tests/pic/watch1.js deleted file mode 100644 index 09d6347bf9..0000000000 --- a/js/src/jit-test/tests/pic/watch1.js +++ /dev/null @@ -1,7 +0,0 @@ -// assignments to watched objects must not be cached -var obj = {x: 0}; -var hits = 0; -obj.watch("x", function (id, oldval, newval) { hits++; return newval; }); -for (var i = 0; i < 10; i++) - obj.x = i; -assertEq(hits, 10); diff --git a/js/src/jit-test/tests/pic/watch1a.js b/js/src/jit-test/tests/pic/watch1a.js deleted file mode 100644 index 4b404f507c..0000000000 --- a/js/src/jit-test/tests/pic/watch1a.js +++ /dev/null @@ -1,17 +0,0 @@ -// assignments to watched objects must not be traced -var hits = 0; -function counter(id, oldval, newval) { - hits++; - return newval; -} - -(function () { - var obj = {x: 0, y: 0}; - var a = ['x', 'y']; - obj.watch('z', counter); - for (var i = 0; i < 14; i++) { - obj.watch(a[+(i > 8)], counter); - obj.y = i; - } -})(); -assertEq(hits, 5); diff --git a/js/src/jit-test/tests/pic/watch2.js b/js/src/jit-test/tests/pic/watch2.js deleted file mode 100644 index fb3e296176..0000000000 --- a/js/src/jit-test/tests/pic/watch2.js +++ /dev/null @@ -1,8 +0,0 @@ -// assignments to watched properties via ++ must not be cached -var obj = {x: 0}; -var hits = 0; -obj.watch("x", function (id, oldval, newval) { hits++; return newval; }); -for (var i = 0; i < 10; i++) - obj.x++; -assertEq(hits, 10); - diff --git a/js/src/jit-test/tests/pic/watch2a.js b/js/src/jit-test/tests/pic/watch2a.js deleted file mode 100644 index ce3294ba3f..0000000000 --- a/js/src/jit-test/tests/pic/watch2a.js +++ /dev/null @@ -1,18 +0,0 @@ -// assignments to watched properties via ++ must not be traced -var hits = 0; -function counter(id, oldval, newval) { - hits++; - return newval; -} - -(function () { - var obj = {x: 0, y: 0}; - var a = ['x', 'y']; - obj.watch('z', counter); - for (var i = 0; i < 14; i++) { - obj.watch(a[+(i > 8)], counter); - obj.y++; - } -})(); -assertEq(hits, 5); - diff --git a/js/src/jit-test/tests/pic/watch3.js b/js/src/jit-test/tests/pic/watch3.js deleted file mode 100644 index 4c5c93d8bb..0000000000 --- a/js/src/jit-test/tests/pic/watch3.js +++ /dev/null @@ -1,7 +0,0 @@ -// assignment to watched global properties must not be cached -x = 0; -var hits = 0; -this.watch("x", function (id, oldval, newval) { hits++; return newval; }); -for (var i = 0; i < 10; i++) - x = i; -assertEq(hits, 10); diff --git a/js/src/jit-test/tests/pic/watch3a.js b/js/src/jit-test/tests/pic/watch3a.js deleted file mode 100644 index 700daf6af9..0000000000 --- a/js/src/jit-test/tests/pic/watch3a.js +++ /dev/null @@ -1,19 +0,0 @@ -// assignment to watched global properties must not be traced -var hits = 0; -function counter(id, oldval, newval) { - hits++; - return newval; -} - -var x = 0; -var y = 0; -(function () { - var a = ['x', 'y']; - this.watch('z', counter); - for (var i = 0; i < 14; i++) { - this.watch(a[+(i > 8)], counter); - y = 1; - } -})(); -assertEq(hits, 5); - diff --git a/js/src/jit-test/tests/pic/watch3b.js b/js/src/jit-test/tests/pic/watch3b.js deleted file mode 100644 index 9b0dc8cc95..0000000000 --- a/js/src/jit-test/tests/pic/watch3b.js +++ /dev/null @@ -1,20 +0,0 @@ -// assignment to watched global properties must not be traced -var hits = 0; -function counter(id, oldval, newval) { - hits++; - return newval; -} - -var x = 0; -var y = 0; -function f() { - var a = [{}, this]; - for (var i = 0; i < 14; i++) { - print(shapeOf(this)); - Object.prototype.watch.call(a[+(i > 8)], "y", counter); - y++; - } -} -f(); -assertEq(hits, 5); - diff --git a/js/src/jit-test/tests/pic/watch4.js b/js/src/jit-test/tests/pic/watch4.js deleted file mode 100644 index 4b640c4dfe..0000000000 --- a/js/src/jit-test/tests/pic/watch4.js +++ /dev/null @@ -1,9 +0,0 @@ -// adding assignment + watchpoint vs. caching -var hits = 0; -var obj = {}; -obj.watch("x", function (id, oldval, newval) { hits++; return newval; }); -for (var i = 0; i < 10; i++) { - obj.x = 1; - delete obj.x; -} -assertEq(hits, 10); diff --git a/js/src/jit-test/tests/pic/watch5.js b/js/src/jit-test/tests/pic/watch5.js deleted file mode 100644 index 6b22951a4a..0000000000 --- a/js/src/jit-test/tests/pic/watch5.js +++ /dev/null @@ -1,27 +0,0 @@ -// test against future pic support for symbols - -// assignments to watched objects must not be cached -var obj = {}; -var x = Symbol.for("x"); -obj[x] = 0; -var hits = 0; -obj.watch(x, function (id, oldval, newval) { hits++; return newval; }); -for (var i = 0; i < 10; i++) - obj[x] = i; -assertEq(hits, 10); - -// assignments to watched properties via ++ must not be cached -hits = 0; -for (var i = 0; i < 10; i++) - obj[x]++; -assertEq(hits, 10); - -// adding assignment + watchpoint vs. caching -hits = 0; -obj = {}; -obj.watch(x, function (id, oldval, newval) { hits++; return newval; }); -for (var i = 0; i < 10; i++) { - obj[x] = 1; - delete obj[x]; -} -assertEq(hits, 10); diff --git a/js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js b/js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js index d665486803..e9dd5d5267 100644 --- a/js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js +++ b/js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js @@ -31,7 +31,7 @@ cold_and_warm(Object.prototype.toString, { ToString: {} }, []); var toS = { toString: function myToString() { return "string"; } }; cold_and_warm(toS.toString, { ToString: toS }, [ "myToString" ]); -cold_and_warm(undefined, { ToNumber: {} }, []); +cold_and_warm(undefined, { ToNumber: 5 }, []); var vOf = { valueOf: function myValueOf() { return 42; } }; cold_and_warm(vOf.valueOf, { ToNumber: vOf }, [ "myValueOf" ]); diff --git a/js/src/jit-test/tests/profiler/bug1140643.js b/js/src/jit-test/tests/profiler/bug1140643.js deleted file mode 100644 index 1b171aea29..0000000000 --- a/js/src/jit-test/tests/profiler/bug1140643.js +++ /dev/null @@ -1,14 +0,0 @@ -// |jit-test| allow-oom -enableSPSProfiling(); -loadFile('\ -for (var i = 0; i < 2; i++) {\ - obj = { m: function () {} };\ - obj.watch("m", function () { float32 = 0 + obj.foo; });\ - obj.m = 0;\ -}\ -'); -gcparam("maxBytes", gcparam("gcBytes") + (1)*1024); -newGlobal("same-compartment"); -function loadFile(lfVarx) { - evaluate(lfVarx, { noScriptRval : true, isRunOnce : true }); -} diff --git a/js/src/jit/AliasAnalysisShared.cpp b/js/src/jit/AliasAnalysisShared.cpp index ae28327cad..0f0d4a66a8 100644 --- a/js/src/jit/AliasAnalysisShared.cpp +++ b/js/src/jit/AliasAnalysisShared.cpp @@ -93,16 +93,11 @@ GetObject(const MDefinition* ins) case MDefinition::Op_Elements: case MDefinition::Op_MaybeCopyElementsForWrite: case MDefinition::Op_MaybeToDoubleElement: - case MDefinition::Op_UnboxedArrayLength: - case MDefinition::Op_UnboxedArrayInitializedLength: - case MDefinition::Op_IncrementUnboxedArrayInitializedLength: - case MDefinition::Op_SetUnboxedArrayInitializedLength: case MDefinition::Op_TypedArrayLength: case MDefinition::Op_SetTypedObjectOffset: case MDefinition::Op_SetDisjointTypedElements: case MDefinition::Op_ArrayPopShift: case MDefinition::Op_ArrayPush: - case MDefinition::Op_ArraySlice: case MDefinition::Op_LoadTypedArrayElementHole: case MDefinition::Op_StoreTypedArrayElementHole: case MDefinition::Op_LoadFixedSlot: @@ -115,8 +110,6 @@ GetObject(const MDefinition* ins) case MDefinition::Op_GuardObjectGroup: case MDefinition::Op_GuardObjectIdentity: case MDefinition::Op_GuardClass: - case MDefinition::Op_GuardUnboxedExpando: - case MDefinition::Op_LoadUnboxedExpando: case MDefinition::Op_LoadSlot: case MDefinition::Op_StoreSlot: case MDefinition::Op_InArray: @@ -126,6 +119,7 @@ GetObject(const MDefinition* ins) object = ins->getOperand(0); break; case MDefinition::Op_GetPropertyCache: + case MDefinition::Op_CallGetProperty: case MDefinition::Op_LoadTypedArrayElementStatic: case MDefinition::Op_StoreTypedArrayElementStatic: case MDefinition::Op_GetDOMProperty: @@ -148,6 +142,7 @@ GetObject(const MDefinition* ins) case MDefinition::Op_WasmLoadGlobalVar: case MDefinition::Op_WasmStoreGlobalVar: case MDefinition::Op_ArrayJoin: + case MDefinition::Op_ArraySlice: return nullptr; default: #ifdef DEBUG diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp index 94ef25785b..645aefc4f7 100644 --- a/js/src/jit/BacktrackingAllocator.cpp +++ b/js/src/jit/BacktrackingAllocator.cpp @@ -378,7 +378,6 @@ BacktrackingAllocator::init() size_t numVregs = graph.numVirtualRegisters(); if (!vregs.init(mir->alloc(), numVregs)) return false; - memset(&vregs[0], 0, sizeof(VirtualRegister) * numVregs); for (uint32_t i = 0; i < numVregs; i++) new(&vregs[i]) VirtualRegister(); @@ -1101,9 +1100,9 @@ BacktrackingAllocator::mergeAndQueueRegisters() if (iter->isParameter()) { for (size_t i = 0; i < iter->numDefs(); i++) { DebugOnly<bool> found = false; - VirtualRegister ¶mVreg = vreg(iter->getDef(i)); + VirtualRegister& paramVreg = vreg(iter->getDef(i)); for (; original < paramVreg.vreg(); original++) { - VirtualRegister &originalVreg = vregs[original]; + VirtualRegister& originalVreg = vregs[original]; if (*originalVreg.def()->output() == *iter->getDef(i)->output()) { MOZ_ASSERT(originalVreg.ins()->isParameter()); if (!tryMergeBundles(originalVreg.firstBundle(), paramVreg.firstBundle())) @@ -1136,7 +1135,7 @@ BacktrackingAllocator::mergeAndQueueRegisters() LBlock* block = graph.getBlock(i); for (size_t j = 0; j < block->numPhis(); j++) { LPhi* phi = block->getPhi(j); - VirtualRegister &outputVreg = vreg(phi->getDef(0)); + VirtualRegister& outputVreg = vreg(phi->getDef(0)); for (size_t k = 0, kend = phi->numOperands(); k < kend; k++) { VirtualRegister& inputVreg = vreg(phi->getOperand(k)->toUse()); if (!tryMergeBundles(inputVreg.firstBundle(), outputVreg.firstBundle())) @@ -1334,7 +1333,7 @@ BacktrackingAllocator::computeRequirement(LiveBundle* bundle, for (LiveRange::BundleLinkIterator iter = bundle->rangesBegin(); iter; iter++) { LiveRange* range = LiveRange::get(*iter); - VirtualRegister ® = vregs[range->vreg()]; + VirtualRegister& reg = vregs[range->vreg()]; if (range->hasDefinition()) { // Deal with any definition constraints/hints. @@ -1396,7 +1395,7 @@ BacktrackingAllocator::tryAllocateRegister(PhysicalRegister& r, LiveBundle* bund for (LiveRange::BundleLinkIterator iter = bundle->rangesBegin(); iter; iter++) { LiveRange* range = LiveRange::get(*iter); - VirtualRegister ® = vregs[range->vreg()]; + VirtualRegister& reg = vregs[range->vreg()]; if (!reg.isCompatible(r.reg)) return true; @@ -1737,6 +1736,18 @@ BacktrackingAllocator::deadRange(LiveRange* range) } bool +BacktrackingAllocator::moveAtEdge(LBlock* predecessor, LBlock* successor, LiveRange* from, + LiveRange* to, LDefinition::Type type) +{ + if (successor->mir()->numPredecessors() > 1) { + MOZ_ASSERT(predecessor->mir()->numSuccessors() == 1); + return moveAtExit(predecessor, from, to, type); + } + + return moveAtEntry(successor, from, to, type); +} + +bool BacktrackingAllocator::resolveControlFlow() { // Add moves to handle changing assignments for vregs over their lifetime. @@ -1844,10 +1855,15 @@ BacktrackingAllocator::resolveControlFlow() LiveRange* from = vreg(input).rangeFor(exitOf(predecessor), /* preferRegister = */ true); MOZ_ASSERT(from); - if (!alloc().ensureBallast()) + if (!alloc().ensureBallast()) { return false; - if (!moveAtExit(predecessor, from, to, def->type())) + } + + // Note: we have to use moveAtEdge both here and below (for edge + // resolution) to avoid conflicting moves. See bug 1493900. + if (!moveAtEdge(predecessor, successor, from, to, def->type())) { return false; + } } } } @@ -1876,16 +1892,12 @@ BacktrackingAllocator::resolveControlFlow() if (targetRange->covers(exitOf(predecessor))) continue; - if (!alloc().ensureBallast()) + if (!alloc().ensureBallast()) { return false; + } LiveRange* from = reg.rangeFor(exitOf(predecessor), true); - if (successor->mir()->numPredecessors() > 1) { - MOZ_ASSERT(predecessor->mir()->numSuccessors() == 1); - if (!moveAtExit(predecessor, from, targetRange, reg.type())) - return false; - } else { - if (!moveAtEntry(successor, from, targetRange, reg.type())) - return false; + if (!moveAtEdge(predecessor, successor, from, targetRange, reg.type())) { + return false; } } } diff --git a/js/src/jit/BacktrackingAllocator.h b/js/src/jit/BacktrackingAllocator.h index 6d14ffacd7..c6cf26695b 100644 --- a/js/src/jit/BacktrackingAllocator.h +++ b/js/src/jit/BacktrackingAllocator.h @@ -108,8 +108,9 @@ class Requirement } MOZ_ASSERT(newRequirement.kind() == Requirement::REGISTER); - if (kind() == Requirement::FIXED) + if (kind() == Requirement::FIXED) { return allocation().isRegister(); + } *this = newRequirement; return true; @@ -353,10 +354,12 @@ class LiveRange : public TempObject // Comparator for use in range splay trees. static int compare(LiveRange* v0, LiveRange* v1) { // LiveRange includes 'from' but excludes 'to'. - if (v0->to() <= v1->from()) + if (v0->to() <= v1->from()) { return -1; - if (v0->from() >= v1->to()) + } + if (v0->from() >= v1->to()) { return 1; + } return 0; } }; @@ -478,34 +481,31 @@ class LiveBundle : public TempObject class VirtualRegister { // Instruction which defines this register. - LNode* ins_; + LNode* ins_ = nullptr; // Definition in the instruction for this register. - LDefinition* def_; + LDefinition* def_ = nullptr; // All live ranges for this register. These may overlap each other, and are // ordered by their start position. InlineForwardList<LiveRange::RegisterLink> ranges_; // Whether def_ is a temp or an output. - bool isTemp_; + bool isTemp_ = false; // Whether this vreg is an input for some phi. This use is not reflected in // any range on the vreg. - bool usedByPhi_; + bool usedByPhi_ = false; // If this register's definition is MUST_REUSE_INPUT, whether a copy must // be introduced before the definition that relaxes the policy. - bool mustCopyInput_; + bool mustCopyInput_ = false; void operator=(const VirtualRegister&) = delete; VirtualRegister(const VirtualRegister&) = delete; public: - explicit VirtualRegister() - { - // Note: This class is zeroed before it is constructed. - } + VirtualRegister() = default; void init(LNode* ins, LDefinition* def, bool isTemp) { MOZ_ASSERT(!ins_); @@ -645,10 +645,12 @@ class BacktrackingAllocator : protected RegisterAllocator // Comparator for use in splay tree. static int compare(CallRange* v0, CallRange* v1) { - if (v0->range.to <= v1->range.from) + if (v0->range.to <= v1->range.from) { return -1; - if (v0->range.from >= v1->range.to) + } + if (v0->range.from >= v1->range.to) { return 1; + } return 0; } }; @@ -747,36 +749,43 @@ class BacktrackingAllocator : protected RegisterAllocator MOZ_MUST_USE bool moveInput(LInstruction* ins, LiveRange* from, LiveRange* to, LDefinition::Type type) { - if (from->bundle()->allocation() == to->bundle()->allocation()) + if (from->bundle()->allocation() == to->bundle()->allocation()) { return true; + } LMoveGroup* moves = getInputMoveGroup(ins); return addMove(moves, from, to, type); } MOZ_MUST_USE bool moveAfter(LInstruction* ins, LiveRange* from, LiveRange* to, LDefinition::Type type) { - if (from->bundle()->allocation() == to->bundle()->allocation()) + if (from->bundle()->allocation() == to->bundle()->allocation()) { return true; + } LMoveGroup* moves = getMoveGroupAfter(ins); return addMove(moves, from, to, type); } MOZ_MUST_USE bool moveAtExit(LBlock* block, LiveRange* from, LiveRange* to, LDefinition::Type type) { - if (from->bundle()->allocation() == to->bundle()->allocation()) + if (from->bundle()->allocation() == to->bundle()->allocation()) { return true; + } LMoveGroup* moves = block->getExitMoveGroup(alloc()); return addMove(moves, from, to, type); } MOZ_MUST_USE bool moveAtEntry(LBlock* block, LiveRange* from, LiveRange* to, LDefinition::Type type) { - if (from->bundle()->allocation() == to->bundle()->allocation()) + if (from->bundle()->allocation() == to->bundle()->allocation()) { return true; + } LMoveGroup* moves = block->getEntryMoveGroup(alloc()); return addMove(moves, from, to, type); } + MOZ_MUST_USE bool moveAtEdge(LBlock* predecessor, LBlock* successor, LiveRange* from, + LiveRange* to, LDefinition::Type type); + // Debugging methods. void dumpAllocations(); diff --git a/js/src/jit/BaselineCacheIR.cpp b/js/src/jit/BaselineCacheIR.cpp index bf96932d1e..67c80473b4 100644 --- a/js/src/jit/BaselineCacheIR.cpp +++ b/js/src/jit/BaselineCacheIR.cpp @@ -16,7 +16,7 @@ using namespace js; using namespace js::jit; // OperandLocation represents the location of an OperandId. The operand is -// either in a register or on the stack, and is either boxed or unboxed. +// either in a register or on the stack. class OperandLocation { public: @@ -787,9 +787,6 @@ BaselineCacheIRCompiler::emitGuardClass() case GuardClassKind::Array: clasp = &ArrayObject::class_; break; - case GuardClassKind::UnboxedArray: - clasp = &UnboxedArrayObject::class_; - break; case GuardClassKind::MappedArguments: clasp = &MappedArgumentsObject::class_; break; @@ -818,36 +815,6 @@ BaselineCacheIRCompiler::emitGuardSpecificObject() } bool -BaselineCacheIRCompiler::emitGuardNoUnboxedExpando() -{ - Register obj = allocator.useRegister(masm, reader.objOperandId()); - - FailurePath* failure; - if (!addFailurePath(&failure)) - return false; - - Address expandoAddr(obj, UnboxedPlainObject::offsetOfExpando()); - masm.branchPtr(Assembler::NotEqual, expandoAddr, ImmWord(0), failure->label()); - return true; -} - -bool -BaselineCacheIRCompiler::emitGuardAndLoadUnboxedExpando() -{ - Register obj = allocator.useRegister(masm, reader.objOperandId()); - Register output = allocator.defineRegister(masm, reader.objOperandId()); - - FailurePath* failure; - if (!addFailurePath(&failure)) - return false; - - Address expandoAddr(obj, UnboxedPlainObject::offsetOfExpando()); - masm.loadPtr(expandoAddr, output); - masm.branchTestPtr(Assembler::Zero, output, output, failure->label()); - return true; -} - -bool BaselineCacheIRCompiler::emitLoadFixedSlotResult() { Register obj = allocator.useRegister(masm, reader.objOperandId()); @@ -874,26 +841,6 @@ BaselineCacheIRCompiler::emitLoadDynamicSlotResult() } bool -BaselineCacheIRCompiler::emitLoadUnboxedPropertyResult() -{ - Register obj = allocator.useRegister(masm, reader.objOperandId()); - AutoScratchRegister scratch(allocator, masm); - - JSValueType fieldType = reader.valueType(); - - Address fieldOffset(stubAddress(reader.stubOffset())); - masm.load32(fieldOffset, scratch); - masm.loadUnboxedProperty(BaseIndex(obj, scratch, TimesOne), fieldType, R0); - - if (fieldType == JSVAL_TYPE_OBJECT) - emitEnterTypeMonitorIC(); - else - emitReturnFromIC(); - - return true; -} - -bool BaselineCacheIRCompiler::emitGuardNoDetachedTypedObjects() { FailurePath* failure; @@ -1004,19 +951,6 @@ BaselineCacheIRCompiler::emitLoadInt32ArrayLengthResult() } bool -BaselineCacheIRCompiler::emitLoadUnboxedArrayLengthResult() -{ - Register obj = allocator.useRegister(masm, reader.objOperandId()); - masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), R0.scratchReg()); - masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0); - - // The int32 type was monitored when attaching the stub, so we can - // just return. - emitReturnFromIC(); - return true; -} - -bool BaselineCacheIRCompiler::emitLoadArgumentsObjectLengthResult() { Register obj = allocator.useRegister(masm, reader.objOperandId()); diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 3fa5a80ed6..b2f9d3b230 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -2049,13 +2049,7 @@ BaselineCompiler::emit_JSOP_NEWARRAY() return true; } -bool -BaselineCompiler::emit_JSOP_SPREADCALLARRAY() -{ - return emit_JSOP_NEWARRAY(); -} - -typedef JSObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject, gc::InitialHeap); +typedef ArrayObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject, gc::InitialHeap); const VMFunction jit::NewArrayCopyOnWriteInfo = FunctionInfo<NewArrayCopyOnWriteFn>(js::NewDenseCopyOnWriteArray, "NewDenseCopyOnWriteArray"); @@ -3298,6 +3292,12 @@ BaselineCompiler::emit_JSOP_CALL() } bool +BaselineCompiler::emit_JSOP_CALL_IGNORES_RV() +{ + return emitCall(); +} + +bool BaselineCompiler::emit_JSOP_CALLITER() { return emitCall(); @@ -4136,14 +4136,14 @@ BaselineCompiler::emit_JSOP_REST() { frame.syncStack(0); - JSObject* templateObject = + ArrayObject* templateObject = ObjectGroup::newArrayObject(cx, nullptr, 0, TenuredObject, ObjectGroup::NewArrayKind::UnknownIndex); if (!templateObject) return false; // Call IC. - ICRest_Fallback::Compiler compiler(cx, &templateObject->as<ArrayObject>()); + ICRest_Fallback::Compiler compiler(cx, templateObject); if (!emitOpIC(compiler.getStub(&stubSpace_))) return false; diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 6b5bf009ed..95e0c77ad4 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -100,7 +100,6 @@ namespace jit { _(JSOP_BITNOT) \ _(JSOP_NEG) \ _(JSOP_NEWARRAY) \ - _(JSOP_SPREADCALLARRAY) \ _(JSOP_NEWARRAY_COPYONWRITE) \ _(JSOP_INITELEM_ARRAY) \ _(JSOP_NEWOBJECT) \ @@ -160,6 +159,7 @@ namespace jit { _(JSOP_INITALIASEDLEXICAL) \ _(JSOP_UNINITIALIZED) \ _(JSOP_CALL) \ + _(JSOP_CALL_IGNORES_RV) \ _(JSOP_CALLITER) \ _(JSOP_FUNCALL) \ _(JSOP_FUNAPPLY) \ diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 863c611610..9530f65fad 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -10,6 +10,8 @@ #include "mozilla/SizePrintfMacros.h" #include "mozilla/TemplateLib.h" +#include "jsfriendapi.h" +#include "jsfun.h" #include "jslibmath.h" #include "jstypes.h" @@ -42,8 +44,8 @@ #include "jit/shared/Lowering-shared-inl.h" #include "vm/EnvironmentObject-inl.h" #include "vm/Interpreter-inl.h" +#include "vm/NativeObject-inl.h" #include "vm/StringObject-inl.h" -#include "vm/UnboxedObject-inl.h" using mozilla::DebugOnly; @@ -289,7 +291,7 @@ DoTypeUpdateFallback(JSContext* cx, BaselineFrame* frame, ICUpdatedStub* stub, H case ICStub::SetProp_Native: case ICStub::SetProp_NativeAdd: case ICStub::SetProp_Unboxed: { - MOZ_ASSERT(obj->isNative() || obj->is<UnboxedPlainObject>()); + MOZ_ASSERT(obj->isNative()); jsbytecode* pc = stub->getChainFallback()->icEntry()->pc(script); if (*pc == JSOP_SETALIASEDVAR || *pc == JSOP_INITALIASEDLEXICAL) id = NameToId(EnvironmentCoordinateName(cx->caches.envCoordinateNameCache, script, pc)); @@ -321,7 +323,14 @@ DoTypeUpdateFallback(JSContext* cx, BaselineFrame* frame, ICUpdatedStub* stub, H MOZ_CRASH("Invalid stub"); } - return stub->addUpdateStubForValue(cx, script /* = outerScript */, obj, id, value); + if (!stub->addUpdateStubForValue(cx, script /* = outerScript */, obj, id, value)) { + // The calling JIT code assumes this function is infallible (for + // instance we may reallocate dynamic slots before calling this), + // so ignore OOMs if we failed to attach a stub. + cx->recoverFromOutOfMemory(); + } + + return true; } typedef bool (*DoTypeUpdateFallbackFn)(JSContext*, BaselineFrame*, ICUpdatedStub*, HandleValue, @@ -732,11 +741,6 @@ LastPropertyForSetProp(JSObject* obj) if (obj->isNative()) return obj->as<NativeObject>().lastProperty(); - if (obj->is<UnboxedPlainObject>()) { - UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando(); - return expando ? expando->lastProperty() : nullptr; - } - return nullptr; } @@ -1153,56 +1157,6 @@ TryAttachNativeOrUnboxedGetValueElemStub(JSContext* cx, HandleScript script, jsb ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); - if (obj->is<UnboxedPlainObject>() && holder == obj) { - const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id); - - // Once unboxed objects support symbol-keys, we need to change the following accordingly - MOZ_ASSERT_IF(!keyVal.isString(), !property); - - if (property) { - if (!cx->runtime()->jitSupportsFloatingPoint) - return true; - - RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName()); - ICGetElemNativeCompiler<PropertyName*> compiler(cx, ICStub::GetElem_UnboxedPropertyName, - monitorStub, obj, holder, - name, - ICGetElemNativeStub::UnboxedProperty, - needsAtomize, property->offset + - UnboxedPlainObject::offsetOfData(), - property->type); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - stub->addNewStub(newStub); - *attached = true; - return true; - } - - Shape* shape = obj->as<UnboxedPlainObject>().maybeExpando()->lookup(cx, id); - if (!shape->hasDefaultGetter() || !shape->hasSlot()) - return true; - - bool isFixedSlot; - uint32_t offset; - GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); - - ICGetElemNativeStub::AccessType acctype = - isFixedSlot ? ICGetElemNativeStub::FixedSlot - : ICGetElemNativeStub::DynamicSlot; - ICGetElemNativeCompiler<T> compiler(cx, getGetElemStubKind<T>(ICStub::GetElem_NativeSlotName), - monitorStub, obj, holder, key, - acctype, needsAtomize, offset); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - stub->addNewStub(newStub); - *attached = true; - return true; - } - if (!holder->isNative()) return true; @@ -1366,7 +1320,7 @@ IsNativeDenseElementAccess(HandleObject obj, HandleValue key) static bool IsNativeOrUnboxedDenseElementAccess(HandleObject obj, HandleValue key) { - if (!obj->isNative() && !obj->is<UnboxedArrayObject>()) + if (!obj->isNative()) return false; if (key.isInt32() && key.toInt32() >= 0 && !obj->is<TypedArrayObject>()) return true; @@ -1450,7 +1404,7 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_ } // Check for NativeObject[id] and UnboxedPlainObject[id] shape-optimizable accesses. - if (obj->isNative() || obj->is<UnboxedPlainObject>()) { + if (obj->isNative()) { RootedScript rootedScript(cx, script); if (rhs.isString()) { if (!TryAttachNativeOrUnboxedGetValueElemStub<PropertyName*>(cx, rootedScript, pc, stub, @@ -1470,20 +1424,6 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_ script = rootedScript; } - // Check for UnboxedArray[int] accesses. - if (obj->is<UnboxedArrayObject>() && rhs.isInt32() && rhs.toInt32() >= 0) { - JitSpew(JitSpew_BaselineIC, " Generating GetElem(UnboxedArray[Int32]) stub"); - ICGetElem_UnboxedArray::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(), - obj->group()); - ICStub* unboxedStub = compiler.getStub(compiler.getStubSpace(script)); - if (!unboxedStub) - return false; - - stub->addNewStub(unboxedStub); - *attached = true; - return true; - } - // Check for TypedArray[int] => Number and TypedObject[int] => Number accesses. if ((obj->is<TypedArrayObject>() || IsPrimitiveArrayTypedObject(obj)) && rhs.isNumber() && @@ -1876,14 +1816,6 @@ ICGetElemNativeCompiler<T>::generateStubCode(MacroAssembler& masm) Register holderReg; if (obj_ == holder_) { holderReg = objReg; - - if (obj_->is<UnboxedPlainObject>() && acctype_ != ICGetElemNativeStub::UnboxedProperty) { - // The property will be loaded off the unboxed expando. - masm.push(R1.scratchReg()); - popR1 = true; - holderReg = R1.scratchReg(); - masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg); - } } else { // Shape guard holder. if (regs.empty()) { @@ -1934,13 +1866,6 @@ ICGetElemNativeCompiler<T>::generateStubCode(MacroAssembler& masm) if (popR1) masm.addToStackPtr(ImmWord(sizeof(size_t))); - } else if (acctype_ == ICGetElemNativeStub::UnboxedProperty) { - masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub<T>::offsetOfOffset()), - scratchReg); - masm.loadUnboxedProperty(BaseIndex(objReg, scratchReg, TimesOne), unboxedType_, - TypedOrValueRegister(R0)); - if (popR1) - masm.addToStackPtr(ImmWord(sizeof(size_t))); } else { MOZ_ASSERT(acctype_ == ICGetElemNativeStub::NativeGetter || acctype_ == ICGetElemNativeStub::ScriptedGetter); @@ -2090,56 +2015,6 @@ ICGetElem_Dense::Compiler::generateStubCode(MacroAssembler& masm) } // -// GetElem_UnboxedArray -// - -bool -ICGetElem_UnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - masm.branchTestInt32(Assembler::NotEqual, R1, &failure); - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); - Register scratchReg = regs.takeAny(); - - // Unbox R0 and group guard. - Register obj = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(ICStubReg, ICGetElem_UnboxedArray::offsetOfGroup()), scratchReg); - masm.branchTestObjGroup(Assembler::NotEqual, obj, scratchReg, &failure); - - // Unbox key. - Register key = masm.extractInt32(R1, ExtractTemp1); - - // Bounds check. - masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), - scratchReg); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg); - masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure); - - // Load obj->elements. - masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg); - - // Load value. - size_t width = UnboxedTypeSize(elementType_); - BaseIndex addr(scratchReg, key, ScaleFromElemWidth(width)); - masm.loadUnboxedProperty(addr, elementType_, R0); - - // Only monitor the result if its type might change. - if (elementType_ == JSVAL_TYPE_OBJECT) - EmitEnterTypeMonitorIC(masm); - else - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -// // GetElem_TypedArray // @@ -2387,14 +2262,20 @@ DenseOrUnboxedArraySetElemStubExists(JSContext* cx, ICStub::Kind kind, for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) { if (kind == ICStub::SetElem_DenseOrUnboxedArray && iter->isSetElem_DenseOrUnboxedArray()) { ICSetElem_DenseOrUnboxedArray* nstub = iter->toSetElem_DenseOrUnboxedArray(); - if (obj->maybeShape() == nstub->shape() && obj->getGroup(cx) == nstub->group()) + if (obj->maybeShape() == nstub->shape() && + JSObject::getGroup(cx, obj) == nstub->group()) + { return true; + } } if (kind == ICStub::SetElem_DenseOrUnboxedArrayAdd && iter->isSetElem_DenseOrUnboxedArrayAdd()) { ICSetElem_DenseOrUnboxedArrayAdd* nstub = iter->toSetElem_DenseOrUnboxedArrayAdd(); - if (obj->getGroup(cx) == nstub->group() && SetElemAddHasSameShapes(nstub, obj)) + if (JSObject::getGroup(cx, obj) == nstub->group() && + SetElemAddHasSameShapes(nstub, obj)) + { return true; + } } } return false; @@ -2437,8 +2318,8 @@ CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index, Shape* oldShape, uint32_t oldCapacity, uint32_t oldInitLength, bool* isAddingCaseOut, size_t* protoDepthOut) { - uint32_t initLength = GetAnyBoxedOrUnboxedInitializedLength(obj); - uint32_t capacity = GetAnyBoxedOrUnboxedCapacity(obj); + uint32_t initLength = obj->as<NativeObject>().getDenseInitializedLength(); + uint32_t capacity = obj->as<NativeObject>().getDenseCapacity(); *isAddingCaseOut = false; *protoDepthOut = 0; @@ -2447,10 +2328,6 @@ CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index, if (initLength < oldInitLength || capacity < oldCapacity) return false; - // Unboxed arrays need to be able to emit floating point code. - if (obj->is<UnboxedArrayObject>() && !obj->runtimeFromMainThread()->jitSupportsFloatingPoint) - return false; - Shape* shape = obj->maybeShape(); // Cannot optimize if the shape changed. @@ -2532,8 +2409,8 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_ uint32_t oldCapacity = 0; uint32_t oldInitLength = 0; if (index.isInt32() && index.toInt32() >= 0) { - oldCapacity = GetAnyBoxedOrUnboxedCapacity(obj); - oldInitLength = GetAnyBoxedOrUnboxedInitializedLength(obj); + oldCapacity = obj->as<NativeObject>().getDenseCapacity(); + oldInitLength = obj->as<NativeObject>().getDenseInitializedLength(); } if (op == JSOP_INITELEM || op == JSOP_INITHIDDENELEM) { @@ -2584,7 +2461,7 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_ &addingCase, &protoDepth)) { RootedShape shape(cx, obj->maybeShape()); - RootedObjectGroup group(cx, obj->getGroup(cx)); + RootedObjectGroup group(cx, JSObject::getGroup(cx, obj)); if (!group) return false; @@ -2741,18 +2618,6 @@ BaselineScript::noteArrayWriteHole(uint32_t pcOffset) // SetElem_DenseOrUnboxedArray // -template <typename T> -void -EmitUnboxedPreBarrierForBaseline(MacroAssembler &masm, T address, JSValueType type) -{ - if (type == JSVAL_TYPE_OBJECT) - EmitPreBarrier(masm, address, MIRType::Object); - else if (type == JSVAL_TYPE_STRING) - EmitPreBarrier(masm, address, MIRType::String); - else - MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(type)); -} - bool ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) { @@ -2871,29 +2736,6 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) masm.loadValue(valueAddr, tmpVal); EmitPreBarrier(masm, element, MIRType::Value); masm.storeValue(tmpVal, element); - } else { - // Set element on an unboxed array. - - // Bounds check. - Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLength, scratchReg); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg); - masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure); - - // Load obj->elements. - masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg); - - // Compute the address being written to. - BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_))); - - EmitUnboxedPreBarrierForBaseline(masm, address, unboxedType_); - - Address valueAddr(masm.getStackPointer(), ICStackValueOffset + sizeof(Value)); - masm.Push(R0); - masm.loadValue(valueAddr, R0); - masm.storeUnboxedProperty(address, unboxedType_, - ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0); - masm.Pop(R0); } EmitReturnFromIC(masm); @@ -3087,40 +2929,6 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm) BaseIndex element(scratchReg, key, TimesEight); masm.loadValue(valueAddr, tmpVal); masm.storeValue(tmpVal, element); - } else { - // Adding element to an unboxed array. - - // Bounds check (key == initLength) - Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLengthAddr, scratchReg); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg); - masm.branch32(Assembler::NotEqual, scratchReg, key, &failure); - - // Capacity check. - masm.checkUnboxedArrayCapacity(obj, RegisterOrInt32Constant(key), scratchReg, &failure); - - // Load obj->elements. - masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg); - - // Write the value first, since this can fail. No need for pre-barrier - // since we're not overwriting an old value. - masm.Push(R0); - Address valueAddr(masm.getStackPointer(), ICStackValueOffset + sizeof(Value)); - masm.loadValue(valueAddr, R0); - BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_))); - masm.storeUnboxedProperty(address, unboxedType_, - ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0); - masm.Pop(R0); - - // Increment initialized length. - masm.add32(Imm32(1), initLengthAddr); - - // If length is now <= key, increment length. - Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength()); - Label skipIncrementLength; - masm.branch32(Assembler::Above, lengthAddr, key, &skipIncrementLength); - masm.add32(Imm32(1), lengthAddr); - masm.bind(&skipIncrementLength); } EmitReturnFromIC(masm); @@ -4245,9 +4053,6 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC { MOZ_ASSERT(!*attached); - if (obj->watched()) - return true; - RootedShape shape(cx); RootedObject holder(cx); if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape)) @@ -4256,18 +4061,7 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC return true; if (!obj->isNative()) { - if (obj->is<UnboxedPlainObject>()) { - UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando(); - if (expando) { - shape = expando->lookup(cx, name); - if (!shape) - return true; - } else { - return true; - } - } else { - return true; - } + return true; } size_t chainDepth; @@ -4354,9 +4148,6 @@ TryAttachSetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, MOZ_ASSERT(!*attached); MOZ_ASSERT(!*isTemporarilyUnoptimizable); - if (obj->watched()) - return true; - RootedShape shape(cx); RootedObject holder(cx); if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape)) @@ -4418,40 +4209,6 @@ TryAttachSetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, } static bool -TryAttachUnboxedSetPropStub(JSContext* cx, HandleScript script, - ICSetProp_Fallback* stub, HandleId id, - HandleObject obj, HandleValue rhs, bool* attached) -{ - MOZ_ASSERT(!*attached); - - if (!cx->runtime()->jitSupportsFloatingPoint) - return true; - - if (!obj->is<UnboxedPlainObject>()) - return true; - - const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id); - if (!property) - return true; - - ICSetProp_Unboxed::Compiler compiler(cx, obj->group(), - property->offset + UnboxedPlainObject::offsetOfData(), - property->type); - ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - if (compiler.needsUpdateStubs() && !newStub->addUpdateStubForValue(cx, script, obj, id, rhs)) - return false; - - stub->addNewStub(newStub); - - StripPreliminaryObjectStubs(cx, stub); - - *attached = true; - return true; -} - -static bool TryAttachTypedObjectSetPropStub(JSContext* cx, HandleScript script, ICSetProp_Fallback* stub, HandleId id, HandleObject obj, HandleValue rhs, bool* attached) @@ -4529,17 +4286,11 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_ if (!obj) return false; RootedShape oldShape(cx, obj->maybeShape()); - RootedObjectGroup oldGroup(cx, obj->getGroup(cx)); + RootedObjectGroup oldGroup(cx, JSObject::getGroup(cx, obj)); if (!oldGroup) return false; RootedReceiverGuard oldGuard(cx, ReceiverGuard(obj)); - if (obj->is<UnboxedPlainObject>()) { - MOZ_ASSERT(!oldShape); - if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) - oldShape = expando->lastProperty(); - } - bool attached = false; // There are some reasons we can fail to attach a stub that are temporary. // We want to avoid calling noteUnoptimizableAccess() if the reason we @@ -4612,15 +4363,6 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_ if (!attached && lhs.isObject() && - !TryAttachUnboxedSetPropStub(cx, script, stub, id, obj, rhs, &attached)) - { - return false; - } - if (attached) - return true; - - if (!attached && - lhs.isObject() && !TryAttachTypedObjectSetPropStub(cx, script, stub, id, obj, rhs, &attached)) { return false; @@ -4703,20 +4445,7 @@ GuardGroupAndShapeMaybeUnboxedExpando(MacroAssembler& masm, JSObject* obj, // Guard against shape or expando shape. masm.loadPtr(Address(ICStubReg, offsetOfShape), scratch); - if (obj->is<UnboxedPlainObject>()) { - Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); - masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure); - Label done; - masm.push(object); - masm.loadPtr(expandoAddress, object); - masm.branchTestObjShape(Assembler::Equal, object, scratch, &done); - masm.pop(object); - masm.jump(failure); - masm.bind(&done); - masm.pop(object); - } else { - masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure); - } + masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure); } bool @@ -4755,13 +4484,7 @@ ICSetProp_Native::Compiler::generateStubCode(MacroAssembler& masm) regs.takeUnchecked(objReg); Register holderReg; - if (obj_->is<UnboxedPlainObject>()) { - // We are loading off the expando object, so use that for the holder. - holderReg = regs.takeAny(); - masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg); - if (!isFixedSlot_) - masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg); - } else if (isFixedSlot_) { + if (isFixedSlot_) { holderReg = objReg; } else { holderReg = regs.takeAny(); @@ -4898,31 +4621,17 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm) regs.add(R0); regs.takeUnchecked(objReg); - if (obj_->is<UnboxedPlainObject>()) { - holderReg = regs.takeAny(); - masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg); - - // Write the expando object's new shape. - Address shapeAddr(holderReg, ShapedObject::offsetOfShape()); - EmitPreBarrier(masm, shapeAddr, MIRType::Shape); - masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch); - masm.storePtr(scratch, shapeAddr); + // Write the object's new shape. + Address shapeAddr(objReg, ShapedObject::offsetOfShape()); + EmitPreBarrier(masm, shapeAddr, MIRType::Shape); + masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch); + masm.storePtr(scratch, shapeAddr); - if (!isFixedSlot_) - masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg); + if (isFixedSlot_) { + holderReg = objReg; } else { - // Write the object's new shape. - Address shapeAddr(objReg, ShapedObject::offsetOfShape()); - EmitPreBarrier(masm, shapeAddr, MIRType::Shape); - masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch); - masm.storePtr(scratch, shapeAddr); - - if (isFixedSlot_) { - holderReg = objReg; - } else { - holderReg = regs.takeAny(); - masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg); - } + holderReg = regs.takeAny(); + masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg); } // Perform the store. No write barrier required since this is a new @@ -4954,70 +4663,6 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm) } bool -ICSetProp_Unboxed::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - - // Guard input is an object. - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); - Register scratch = regs.takeAny(); - - // Unbox and group guard. - Register object = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(ICStubReg, ICSetProp_Unboxed::offsetOfGroup()), scratch); - masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch, - &failure); - - if (needsUpdateStubs()) { - // Stow both R0 and R1 (object and value). - EmitStowICValues(masm, 2); - - // Move RHS into R0 for TypeUpdate check. - masm.moveValue(R1, R0); - - // Call the type update stub. - if (!callTypeUpdateIC(masm, sizeof(Value))) - return false; - - // Unstow R0 and R1 (object and key) - EmitUnstowICValues(masm, 2); - - // The TypeUpdate IC may have smashed object. Rederive it. - masm.unboxObject(R0, object); - - // Trigger post barriers here on the values being written. Fields which - // objects can be written to also need update stubs. - LiveGeneralRegisterSet saveRegs; - saveRegs.add(R0); - saveRegs.add(R1); - saveRegs.addUnchecked(object); - saveRegs.add(ICStubReg); - emitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs); - } - - // Compute the address being written to. - masm.load32(Address(ICStubReg, ICSetProp_Unboxed::offsetOfFieldOffset()), scratch); - BaseIndex address(object, scratch, TimesOne); - - EmitUnboxedPreBarrierForBaseline(masm, address, fieldType_); - masm.storeUnboxedProperty(address, fieldType_, - ConstantOrRegister(TypedOrValueRegister(R1)), &failure); - - // The RHS has to be in R0. - masm.moveValue(R1, R0); - - EmitReturnFromIC(masm); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -bool ICSetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm) { MOZ_ASSERT(engine_ == Engine::Baseline); @@ -5490,13 +5135,6 @@ GetTemplateObjectForSimd(JSContext* cx, JSFunction* target, MutableHandleObject return true; } -static void -EnsureArrayGroupAnalyzed(JSContext* cx, JSObject* obj) -{ - if (PreliminaryObjectArrayWithTemplate* objects = obj->group()->maybePreliminaryObjects()) - objects->maybeAnalyze(cx, obj->group(), /* forceAnalyze = */ true); -} - static bool GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs& args, MutableHandleObject res, bool* skipAttach) @@ -5528,10 +5166,7 @@ GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs& // With this and other array templates, analyze the group so that // we don't end up with a template whose structure might change later. res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject)); - if (!res) - return false; - EnsureArrayGroupAnalyzed(cx, res); - return true; + return !!res; } } @@ -5549,18 +5184,14 @@ GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs& if (native == js::array_slice) { if (args.thisv().isObject()) { - JSObject* obj = &args.thisv().toObject(); + RootedObject obj(cx, &args.thisv().toObject()); if (!obj->isSingleton()) { if (obj->group()->maybePreliminaryObjects()) { *skipAttach = true; return true; } - res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0, - TenuredObject)); - if (!res) - return false; - EnsureArrayGroupAnalyzed(cx, res); - return true; + res.set(NewFullyAllocatedArrayTryReuseGroup(cx, obj, 0, TenuredObject)); + return !!res; } } } @@ -5577,10 +5208,7 @@ GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs& } res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, 0, TenuredObject)); - if (!res) - return false; - EnsureArrayGroupAnalyzed(cx, res); - return true; + return !!res; } if (native == StringConstructor) { @@ -5793,7 +5421,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb if (!thisObject) return false; - if (thisObject->is<PlainObject>() || thisObject->is<UnboxedPlainObject>()) + if (thisObject->is<PlainObject>()) templateObject = thisObject; } @@ -5869,11 +5497,17 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb MOZ_ASSERT_IF(templateObject, !templateObject->group()->maybePreliminaryObjects()); } + bool ignoresReturnValue = false; + if (op == JSOP_CALL_IGNORES_RV && fun->isNative()) { + const JSJitInfo* jitInfo = fun->jitInfo(); + ignoresReturnValue = jitInfo && jitInfo->type() == JSJitInfo::IgnoresReturnValueNative; + } + JitSpew(JitSpew_BaselineIC, " Generating Call_Native stub (fun=%p, cons=%s, spread=%s)", fun.get(), constructing ? "yes" : "no", isSpread ? "yes" : "no"); ICCall_Native::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(), - fun, templateObject, constructing, isSpread, - script->pcToOffset(pc)); + fun, templateObject, constructing, ignoresReturnValue, + isSpread, script->pcToOffset(pc)); ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); if (!newStub) return false; @@ -5887,15 +5521,24 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb } static bool -CopyArray(JSContext* cx, HandleObject obj, MutableHandleValue result) +CopyArray(JSContext* cx, HandleArrayObject arr, MutableHandleValue result) { - uint32_t length = GetAnyBoxedOrUnboxedArrayLength(obj); - JSObject* nobj = NewFullyAllocatedArrayTryReuseGroup(cx, obj, length, TenuredObject); + uint32_t length = arr->length(); + ArrayObject* nobj = NewFullyAllocatedArrayTryReuseGroup(cx, arr, length, TenuredObject); if (!nobj) return false; - EnsureArrayGroupAnalyzed(cx, nobj); - CopyAnyBoxedOrUnboxedDenseElements(cx, nobj, obj, 0, 0, length); - + + MOZ_ASSERT(arr->isNative()); + MOZ_ASSERT(nobj->isNative()); + MOZ_ASSERT(nobj->as<NativeObject>().getDenseInitializedLength() == 0); + MOZ_ASSERT(arr->as<NativeObject>().getDenseInitializedLength() >= length); + MOZ_ASSERT(nobj->as<NativeObject>().getDenseCapacity() >= length); + + nobj->as<NativeObject>().setDenseInitializedLength(length); + + const Value* vp = arr->as<NativeObject>().getDenseElements(); + nobj->as<NativeObject>().initDenseElements(0, vp, length); + result.setObject(*nobj); return true; } @@ -5926,26 +5569,22 @@ TryAttachStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript script, RootedValue arr(cx); // Copy the array before storing in stub. - if (!CopyArray(cx, obj, &arr)) + if (!CopyArray(cx, obj.as<ArrayObject>(), &arr)) return false; // Atomize all elements of the array. - RootedObject arrObj(cx, &arr.toObject()); - uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(arrObj); + RootedArrayObject arrObj(cx, &arr.toObject().as<ArrayObject>()); + uint32_t initLength = arrObj->length(); for (uint32_t i = 0; i < initLength; i++) { - JSAtom* str = js::AtomizeString(cx, GetAnyBoxedOrUnboxedDenseElement(arrObj, i).toString()); + JSAtom* str = js::AtomizeString(cx, arrObj->getDenseElement(i).toString()); if (!str) return false; - if (!SetAnyBoxedOrUnboxedDenseElement(cx, arrObj, i, StringValue(str))) { - // The value could not be stored to an unboxed dense element. - return true; - } + arrObj->setDenseElementWithType(cx, i, StringValue(str)); } ICCall_StringSplit::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(), - script->pcToOffset(pc), str, sep, - arr); + script->pcToOffset(pc), str, sep, arrObj); ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); if (!newStub) return false; @@ -5971,12 +5610,14 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint MOZ_ASSERT(argc == GET_ARGC(pc)); bool constructing = (op == JSOP_NEW); + bool ignoresReturnValue = (op == JSOP_CALL_IGNORES_RV); // Ensure vp array is rooted - we may GC in here. size_t numValues = argc + 2 + constructing; AutoArrayRooter vpRoot(cx, numValues, vp); - CallArgs callArgs = CallArgsFromSp(argc + constructing, vp + numValues, constructing); + CallArgs callArgs = CallArgsFromSp(argc + constructing, vp + numValues, constructing, + ignoresReturnValue); RootedValue callee(cx, vp[0]); // Handle funapply with JSOP_ARGUMENTS @@ -6006,6 +5647,7 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint return false; } else { MOZ_ASSERT(op == JSOP_CALL || + op == JSOP_CALL_IGNORES_RV || op == JSOP_CALLITER || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY || @@ -6830,7 +6472,7 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm) return true; } -typedef bool (*CopyArrayFn)(JSContext*, HandleObject, MutableHandleValue); +typedef bool (*CopyArrayFn)(JSContext*, HandleArrayObject, MutableHandleValue); static const VMFunction CopyArrayInfo = FunctionInfo<CopyArrayFn>(CopyArray, "CopyArray"); bool @@ -7056,7 +6698,12 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm) // stub and use that instead of the original one. masm.callWithABI(Address(ICStubReg, ICCall_Native::offsetOfNative())); #else - masm.callWithABI(Address(callee, JSFunction::offsetOfNativeOrScript())); + if (ignoresReturnValue_) { + masm.loadPtr(Address(callee, JSFunction::offsetOfJitInfo()), callee); + masm.callWithABI(Address(callee, JSJitInfo::offsetOfIgnoresReturnValueNative())); + } else { + masm.callWithABI(Address(callee, JSFunction::offsetOfNativeOrScript())); + } #endif // Test for failure. @@ -8301,19 +7948,6 @@ ICGetElem_Dense::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorSt return New<ICGetElem_Dense>(cx, space, other.jitCode(), firstMonitorStub, other.shape_); } -ICGetElem_UnboxedArray::ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub, - ObjectGroup *group) - : ICMonitoredStub(GetElem_UnboxedArray, stubCode, firstMonitorStub), - group_(group) -{ } - -/* static */ ICGetElem_UnboxedArray* -ICGetElem_UnboxedArray::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetElem_UnboxedArray& other) -{ - return New<ICGetElem_UnboxedArray>(cx, space, other.jitCode(), firstMonitorStub, other.group_); -} - ICGetElem_TypedArray::ICGetElem_TypedArray(JitCode* stubCode, Shape* shape, Scalar::Type type) : ICStub(GetElem_TypedArray, stubCode), shape_(shape) @@ -8349,7 +7983,7 @@ ICUpdatedStub* ICSetElemDenseOrUnboxedArrayAddCompiler::getStubSpecific(ICStubSpace* space, Handle<ShapeVector> shapes) { - RootedObjectGroup group(cx, obj_->getGroup(cx)); + RootedObjectGroup group(cx, JSObject::getGroup(cx, obj_)); if (!group) return nullptr; Rooted<JitCode*> stubCode(cx, getStubCode()); @@ -8486,7 +8120,7 @@ ICSetProp_Native::ICSetProp_Native(JitCode* stubCode, ObjectGroup* group, Shape* ICSetProp_Native* ICSetProp_Native::Compiler::getStub(ICStubSpace* space) { - RootedObjectGroup group(cx, obj_->getGroup(cx)); + RootedObjectGroup group(cx, JSObject::getGroup(cx, obj_)); if (!group) return nullptr; @@ -8689,8 +8323,8 @@ static bool DoRestFallback(JSContext* cx, BaselineFrame* frame, ICRest_Fallback* unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0; Value* rest = frame->argv() + numFormals; - JSObject* obj = ObjectGroup::newArrayObject(cx, rest, numRest, GenericObject, - ObjectGroup::NewArrayKind::UnknownIndex); + ArrayObject* obj = ObjectGroup::newArrayObject(cx, rest, numRest, GenericObject, + ObjectGroup::NewArrayKind::UnknownIndex); if (!obj) return false; res.setObject(*obj); diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index a57556d993..e1ad12559f 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -22,7 +22,6 @@ #include "jit/SharedICRegisters.h" #include "js/GCVector.h" #include "vm/ArrayObject.h" -#include "vm/UnboxedObject.h" namespace js { namespace jit { @@ -892,54 +891,6 @@ class ICGetElem_Dense : public ICMonitoredStub }; }; -class ICGetElem_UnboxedArray : public ICMonitoredStub -{ - friend class ICStubSpace; - - GCPtrObjectGroup group_; - - ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub, ObjectGroup* group); - - public: - static ICGetElem_UnboxedArray* Clone(JSContext* cx, ICStubSpace* space, - ICStub* firstMonitorStub, ICGetElem_UnboxedArray& other); - - static size_t offsetOfGroup() { - return offsetof(ICGetElem_UnboxedArray, group_); - } - - GCPtrObjectGroup& group() { - return group_; - } - - class Compiler : public ICStubCompiler { - ICStub* firstMonitorStub_; - RootedObjectGroup group_; - JSValueType elementType_; - - protected: - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm); - - virtual int32_t getKey() const { - return static_cast<int32_t>(engine_) | - (static_cast<int32_t>(kind) << 1) | - (static_cast<int32_t>(elementType_) << 17); - } - - public: - Compiler(JSContext* cx, ICStub* firstMonitorStub, ObjectGroup* group) - : ICStubCompiler(cx, ICStub::GetElem_UnboxedArray, Engine::Baseline), - firstMonitorStub_(firstMonitorStub), - group_(cx, group), - elementType_(group->unboxedLayoutDontCheckGeneration().elementType()) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub<ICGetElem_UnboxedArray>(space, getStubCode(), firstMonitorStub_, group_); - } - }; -}; - // Accesses scalar elements of a typed array or typed object. class ICGetElem_TypedArray : public ICStub { @@ -1115,9 +1066,7 @@ class ICSetElem_DenseOrUnboxedArray : public ICUpdatedStub : ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArray, Engine::Baseline), shape_(cx, shape), group_(cx, group), - unboxedType_(shape - ? JSVAL_TYPE_MAGIC - : group->unboxedLayoutDontCheckGeneration().elementType()) + unboxedType_(JSVAL_TYPE_MAGIC) {} ICUpdatedStub* getStub(ICStubSpace* space) { @@ -1225,9 +1174,7 @@ class ICSetElemDenseOrUnboxedArrayAddCompiler : public ICStubCompiler { : ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd, Engine::Baseline), obj_(cx, obj), protoChainDepth_(protoChainDepth), - unboxedType_(obj->is<UnboxedArrayObject>() - ? obj->as<UnboxedArrayObject>().elementType() - : JSVAL_TYPE_MAGIC) + unboxedType_(JSVAL_TYPE_MAGIC) {} template <size_t ProtoChainDepth> @@ -1875,8 +1822,7 @@ class ICSetProp_Native : public ICUpdatedStub virtual int32_t getKey() const { return static_cast<int32_t>(engine_) | (static_cast<int32_t>(kind) << 1) | - (static_cast<int32_t>(isFixedSlot_) << 17) | - (static_cast<int32_t>(obj_->is<UnboxedPlainObject>()) << 18); + (static_cast<int32_t>(isFixedSlot_) << 17); } MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm); @@ -1981,7 +1927,6 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler return static_cast<int32_t>(engine_) | (static_cast<int32_t>(kind) << 1) | (static_cast<int32_t>(isFixedSlot_) << 17) | - (static_cast<int32_t>(obj_->is<UnboxedPlainObject>()) << 18) | (static_cast<int32_t>(protoChainDepth_) << 19); } @@ -1995,7 +1940,7 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler template <size_t ProtoChainDepth> ICUpdatedStub* getStubSpecific(ICStubSpace* space, Handle<ShapeVector> shapes) { - RootedObjectGroup newGroup(cx, obj_->getGroup(cx)); + RootedObjectGroup newGroup(cx, JSObject::getGroup(cx, obj_)); if (!newGroup) return nullptr; @@ -2006,10 +1951,7 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler newGroup = nullptr; RootedShape newShape(cx); - if (obj_->isNative()) - newShape = obj_->as<NativeObject>().lastProperty(); - else - newShape = obj_->as<UnboxedPlainObject>().maybeExpando()->lastProperty(); + newShape = obj_->as<NativeObject>().lastProperty(); return newStub<ICSetProp_NativeAddImpl<ProtoChainDepth>>( space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_); @@ -2335,6 +2277,7 @@ class ICSetProp_CallNative : public ICSetPropCallSetter // Call // JSOP_CALL +// JSOP_CALL_IGNORES_RV // JSOP_FUNAPPLY // JSOP_FUNCALL // JSOP_NEW @@ -2605,6 +2548,7 @@ class ICCall_Native : public ICMonitoredStub protected: ICStub* firstMonitorStub_; bool isConstructing_; + bool ignoresReturnValue_; bool isSpread_; RootedFunction callee_; RootedObject templateObject_; @@ -2614,17 +2558,19 @@ class ICCall_Native : public ICMonitoredStub virtual int32_t getKey() const { return static_cast<int32_t>(engine_) | (static_cast<int32_t>(kind) << 1) | - (static_cast<int32_t>(isConstructing_) << 17) | - (static_cast<int32_t>(isSpread_) << 18); + (static_cast<int32_t>(isSpread_) << 17) | + (static_cast<int32_t>(isConstructing_) << 18) | + (static_cast<int32_t>(ignoresReturnValue_) << 19); } public: Compiler(JSContext* cx, ICStub* firstMonitorStub, HandleFunction callee, HandleObject templateObject, - bool isConstructing, bool isSpread, uint32_t pcOffset) + bool isConstructing, bool ignoresReturnValue, bool isSpread, uint32_t pcOffset) : ICCallStubCompiler(cx, ICStub::Call_Native), firstMonitorStub_(firstMonitorStub), isConstructing_(isConstructing), + ignoresReturnValue_(ignoresReturnValue), isSpread_(isSpread), callee_(cx, callee), templateObject_(cx, templateObject), @@ -2870,10 +2816,10 @@ class ICCall_StringSplit : public ICMonitoredStub uint32_t pcOffset_; GCPtrString expectedStr_; GCPtrString expectedSep_; - GCPtrObject templateObject_; + GCPtrArrayObject templateObject_; ICCall_StringSplit(JitCode* stubCode, ICStub* firstMonitorStub, uint32_t pcOffset, JSString* str, - JSString* sep, JSObject* templateObject) + JSString* sep, ArrayObject* templateObject) : ICMonitoredStub(ICStub::Call_StringSplit, stubCode, firstMonitorStub), pcOffset_(pcOffset), expectedStr_(str), expectedSep_(sep), templateObject_(templateObject) @@ -2900,7 +2846,7 @@ class ICCall_StringSplit : public ICMonitoredStub return expectedSep_; } - GCPtrObject& templateObject() { + GCPtrArrayObject& templateObject() { return templateObject_; } @@ -2910,7 +2856,7 @@ class ICCall_StringSplit : public ICMonitoredStub uint32_t pcOffset_; RootedString expectedStr_; RootedString expectedSep_; - RootedObject templateObject_; + RootedArrayObject templateObject_; MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm); @@ -2921,13 +2867,13 @@ class ICCall_StringSplit : public ICMonitoredStub public: Compiler(JSContext* cx, ICStub* firstMonitorStub, uint32_t pcOffset, HandleString str, - HandleString sep, HandleValue templateObject) + HandleString sep, HandleArrayObject templateObject) : ICCallStubCompiler(cx, ICStub::Call_StringSplit), firstMonitorStub_(firstMonitorStub), pcOffset_(pcOffset), expectedStr_(cx, str), expectedSep_(cx, sep), - templateObject_(cx, &templateObject.toObject()) + templateObject_(cx, templateObject) { } ICStub* getStub(ICStubSpace* space) { diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index c9e09bed7a..3b852debf3 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -96,32 +96,19 @@ VectorAppendNoDuplicate(S& list, T value) static bool AddReceiver(const ReceiverGuard& receiver, - BaselineInspector::ReceiverVector& receivers, - BaselineInspector::ObjectGroupVector& convertUnboxedGroups) + BaselineInspector::ReceiverVector& receivers) { - if (receiver.group && receiver.group->maybeUnboxedLayout()) { - if (receiver.group->unboxedLayout().nativeGroup()) - return VectorAppendNoDuplicate(convertUnboxedGroups, receiver.group); - } return VectorAppendNoDuplicate(receivers, receiver); } static bool GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub, ReceiverGuard* receiver) { - // We match either: + // We match: // // GuardIsObject 0 // GuardShape 0 // LoadFixedSlotResult 0 or LoadDynamicSlotResult 0 - // - // or - // - // GuardIsObject 0 - // GuardGroup 0 - // 1: GuardAndLoadUnboxedExpando 0 - // GuardShape 1 - // LoadFixedSlotResult 1 or LoadDynamicSlotResult 1 *receiver = ReceiverGuard(); CacheIRReader reader(stub->stubInfo()); @@ -130,14 +117,6 @@ GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub, ReceiverGuard* re if (!reader.matchOp(CacheOp::GuardIsObject, objId)) return false; - if (reader.matchOp(CacheOp::GuardGroup, objId)) { - receiver->group = stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset()); - - if (!reader.matchOp(CacheOp::GuardAndLoadUnboxedExpando, objId)) - return false; - objId = reader.objOperandId(); - } - if (reader.matchOp(CacheOp::GuardShape, objId)) { receiver->shape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset()); return reader.matchOpEither(CacheOp::LoadFixedSlotResult, CacheOp::LoadDynamicSlotResult); @@ -146,40 +125,13 @@ GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub, ReceiverGuard* re return false; } -static bool -GetCacheIRReceiverForUnboxedProperty(ICCacheIR_Monitored* stub, ReceiverGuard* receiver) -{ - // We match: - // - // GuardIsObject 0 - // GuardGroup 0 - // LoadUnboxedPropertyResult 0 .. - - *receiver = ReceiverGuard(); - CacheIRReader reader(stub->stubInfo()); - - ObjOperandId objId = ObjOperandId(0); - if (!reader.matchOp(CacheOp::GuardIsObject, objId)) - return false; - - if (!reader.matchOp(CacheOp::GuardGroup, objId)) - return false; - receiver->group = stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset()); - - return reader.matchOp(CacheOp::LoadUnboxedPropertyResult, objId); -} - bool -BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers, - ObjectGroupVector& convertUnboxedGroups) +BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers) { // Return a list of the receivers seen by the baseline IC for the current // op. Empty lists indicate no receivers are known, or there was an - // uncacheable access. convertUnboxedGroups is used for unboxed object - // groups which have been seen, but have had instances converted to native - // objects and should be eagerly converted by Ion. + // uncacheable access. MOZ_ASSERT(receivers.empty()); - MOZ_ASSERT(convertUnboxedGroups.empty()); if (!hasBaselineScript()) return true; @@ -191,8 +143,7 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receiv while (stub->next()) { ReceiverGuard receiver; if (stub->isCacheIR_Monitored()) { - if (!GetCacheIRReceiverForNativeReadSlot(stub->toCacheIR_Monitored(), &receiver) && - !GetCacheIRReceiverForUnboxedProperty(stub->toCacheIR_Monitored(), &receiver)) + if (!GetCacheIRReceiverForNativeReadSlot(stub->toCacheIR_Monitored(), &receiver)) { receivers.clear(); return true; @@ -200,14 +151,12 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receiv } else if (stub->isSetProp_Native()) { receiver = ReceiverGuard(stub->toSetProp_Native()->group(), stub->toSetProp_Native()->shape()); - } else if (stub->isSetProp_Unboxed()) { - receiver = ReceiverGuard(stub->toSetProp_Unboxed()->group(), nullptr); } else { receivers.clear(); return true; } - if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) + if (!AddReceiver(receiver, receivers)) return false; stub = stub->next(); @@ -589,7 +538,7 @@ BaselineInspector::getTemplateObjectForNative(jsbytecode* pc, Native native) bool BaselineInspector::isOptimizableCallStringSplit(jsbytecode* pc, JSString** strOut, JSString** sepOut, - JSObject** objOut) + ArrayObject** objOut) { if (!hasBaselineScript()) return false; @@ -700,14 +649,12 @@ bool BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonGetter, Shape** globalShape, bool* isOwnProperty, - ReceiverVector& receivers, - ObjectGroupVector& convertUnboxedGroups) + ReceiverVector& receivers) { if (!hasBaselineScript()) return false; MOZ_ASSERT(receivers.empty()); - MOZ_ASSERT(convertUnboxedGroups.empty()); *holder = nullptr; const ICEntry& entry = icEntryFromPC(pc); @@ -719,7 +666,7 @@ BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shap { ICGetPropCallGetter* nstub = static_cast<ICGetPropCallGetter*>(stub); bool isOwn = nstub->isOwnGetter(); - if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups)) + if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers)) return false; if (!*holder) { @@ -751,21 +698,19 @@ BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shap if (!*holder) return false; - MOZ_ASSERT(*isOwnProperty == (receivers.empty() && convertUnboxedGroups.empty())); + MOZ_ASSERT(*isOwnProperty == (receivers.empty())); return true; } bool BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonSetter, bool* isOwnProperty, - ReceiverVector& receivers, - ObjectGroupVector& convertUnboxedGroups) + ReceiverVector& receivers) { if (!hasBaselineScript()) return false; MOZ_ASSERT(receivers.empty()); - MOZ_ASSERT(convertUnboxedGroups.empty()); *holder = nullptr; const ICEntry& entry = icEntryFromPC(pc); @@ -774,7 +719,7 @@ BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shap if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) { ICSetPropCallSetter* nstub = static_cast<ICSetPropCallSetter*>(stub); bool isOwn = nstub->isOwnSetter(); - if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups)) + if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers)) return false; if (!*holder) { diff --git a/js/src/jit/BaselineInspector.h b/js/src/jit/BaselineInspector.h index 961df18aa2..1ed4b55475 100644 --- a/js/src/jit/BaselineInspector.h +++ b/js/src/jit/BaselineInspector.h @@ -95,8 +95,7 @@ class BaselineInspector public: typedef Vector<ReceiverGuard, 4, JitAllocPolicy> ReceiverVector; typedef Vector<ObjectGroup*, 4, JitAllocPolicy> ObjectGroupVector; - MOZ_MUST_USE bool maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers, - ObjectGroupVector& convertUnboxedGroups); + MOZ_MUST_USE bool maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers); SetElemICInspector setElemICInspector(jsbytecode* pc) { return makeICInspector<SetElemICInspector>(pc, ICStub::SetElem_Fallback); @@ -114,7 +113,7 @@ class BaselineInspector bool hasSeenNonStringIterMore(jsbytecode* pc); MOZ_MUST_USE bool isOptimizableCallStringSplit(jsbytecode* pc, JSString** strOut, - JSString** sepOut, JSObject** objOut); + JSString** sepOut, ArrayObject** objOut); JSObject* getTemplateObject(jsbytecode* pc); JSObject* getTemplateObjectForNative(jsbytecode* pc, Native native); JSObject* getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp); @@ -131,12 +130,10 @@ class BaselineInspector MOZ_MUST_USE bool commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonGetter, Shape** globalShape, - bool* isOwnProperty, ReceiverVector& receivers, - ObjectGroupVector& convertUnboxedGroups); + bool* isOwnProperty, ReceiverVector& receivers); MOZ_MUST_USE bool commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonSetter, bool* isOwnProperty, - ReceiverVector& receivers, - ObjectGroupVector& convertUnboxedGroups); + ReceiverVector& receivers); MOZ_MUST_USE bool instanceOfData(jsbytecode* pc, Shape** shape, uint32_t* slot, JSObject** prototypeObject); diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index d0e297c2d7..5c21926b53 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -273,7 +273,7 @@ jit::BaselineCompile(JSContext* cx, JSScript* script, bool forceDebugInstrumenta MOZ_ASSERT(script->canBaselineCompile()); MOZ_ASSERT(IsBaselineEnabled(cx)); - script->ensureNonLazyCanonicalFunction(cx); + script->ensureNonLazyCanonicalFunction(); LifoAlloc alloc(TempAllocator::PreferredLifoChunkSize); TempAllocator* temp = alloc.new_<TempAllocator>(&alloc); diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index f1061af701..d184ea40c5 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -10,8 +10,7 @@ #include "jit/IonCaches.h" #include "jsobjinlines.h" - -#include "vm/UnboxedObject-inl.h" +#include "vm/NativeObject-inl.h" using namespace js; using namespace js::jit; @@ -60,10 +59,6 @@ GetPropIRGenerator::tryAttachStub(Maybe<CacheIRWriter>& writer) return false; if (!emitted_ && !tryAttachNative(*writer, obj, objId)) return false; - if (!emitted_ && !tryAttachUnboxed(*writer, obj, objId)) - return false; - if (!emitted_ && !tryAttachUnboxedExpando(*writer, obj, objId)) - return false; if (!emitted_ && !tryAttachTypedObject(*writer, obj, objId)) return false; if (!emitted_ && !tryAttachModuleNamespace(*writer, obj, objId)) @@ -163,19 +158,9 @@ GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, } static void -TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, Shape* shape, ObjOperandId objId, - Maybe<ObjOperandId>* expandoId) +TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, Shape* shape, ObjOperandId objId) { - if (obj->is<UnboxedPlainObject>()) { - writer.guardGroup(objId, obj->group()); - - if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) { - expandoId->emplace(writer.guardAndLoadUnboxedExpando(objId)); - writer.guardShape(expandoId->ref(), expando->lastProperty()); - } else { - writer.guardNoUnboxedExpando(objId); - } - } else if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) { + if (obj->is<TypedObject>()) { writer.guardGroup(objId, obj->group()); } else { Shape* shape = obj->maybeShape(); @@ -188,8 +173,7 @@ static void EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder, Shape* shape, ObjOperandId objId) { - Maybe<ObjOperandId> expandoId; - TestMatchingReceiver(writer, obj, shape, objId, &expandoId); + TestMatchingReceiver(writer, obj, shape, objId); ObjOperandId holderId; if (obj != holder) { @@ -212,9 +196,6 @@ EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder, lastObjId = protoId; } } - } else if (obj->is<UnboxedPlainObject>()) { - holder = obj->as<UnboxedPlainObject>().maybeExpando(); - holderId = *expandoId; } else { holderId = objId; } @@ -266,51 +247,6 @@ GetPropIRGenerator::tryAttachNative(CacheIRWriter& writer, HandleObject obj, Obj } bool -GetPropIRGenerator::tryAttachUnboxed(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId) -{ - MOZ_ASSERT(!emitted_); - - if (!obj->is<UnboxedPlainObject>()) - return true; - - const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(name_); - if (!property) - return true; - - if (!cx_->runtime()->jitSupportsFloatingPoint) - return true; - - writer.guardGroup(objId, obj->group()); - writer.loadUnboxedPropertyResult(objId, property->type, - UnboxedPlainObject::offsetOfData() + property->offset); - emitted_ = true; - preliminaryObjectAction_ = PreliminaryObjectAction::Unlink; - return true; -} - -bool -GetPropIRGenerator::tryAttachUnboxedExpando(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId) -{ - MOZ_ASSERT(!emitted_); - - if (!obj->is<UnboxedPlainObject>()) - return true; - - UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando(); - if (!expando) - return true; - - Shape* shape = expando->lookup(cx_, NameToId(name_)); - if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) - return true; - - emitted_ = true; - - EmitReadSlotResult(writer, obj, obj, shape, objId); - return true; -} - -bool GetPropIRGenerator::tryAttachTypedObject(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId) { MOZ_ASSERT(!emitted_); @@ -368,13 +304,6 @@ GetPropIRGenerator::tryAttachObjectLength(CacheIRWriter& writer, HandleObject ob return true; } - if (obj->is<UnboxedArrayObject>()) { - writer.guardClass(objId, GuardClassKind::UnboxedArray); - writer.loadUnboxedArrayLengthResult(objId); - emitted_ = true; - return true; - } - if (obj->is<ArgumentsObject>() && !obj->as<ArgumentsObject>().hasOverriddenLength()) { if (obj->is<MappedArgumentsObject>()) { writer.guardClass(objId, GuardClassKind::MappedArguments); diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index 51e55f48b8..ae55cfebb4 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -87,16 +87,12 @@ class ObjOperandId : public OperandId _(GuardClass) \ _(GuardSpecificObject) \ _(GuardNoDetachedTypedObjects) \ - _(GuardNoUnboxedExpando) \ - _(GuardAndLoadUnboxedExpando) \ _(LoadObject) \ _(LoadProto) \ _(LoadFixedSlotResult) \ _(LoadDynamicSlotResult) \ - _(LoadUnboxedPropertyResult) \ _(LoadTypedObjectResult) \ _(LoadInt32ArrayLengthResult) \ - _(LoadUnboxedArrayLengthResult) \ _(LoadArgumentsObjectLengthResult) \ _(LoadUndefinedResult) @@ -128,7 +124,6 @@ struct StubField { enum class GuardClassKind { Array, - UnboxedArray, MappedArguments, UnmappedArguments, }; @@ -276,15 +271,6 @@ class MOZ_RAII CacheIRWriter void guardNoDetachedTypedObjects() { writeOp(CacheOp::GuardNoDetachedTypedObjects); } - void guardNoUnboxedExpando(ObjOperandId obj) { - writeOpWithOperandId(CacheOp::GuardNoUnboxedExpando, obj); - } - ObjOperandId guardAndLoadUnboxedExpando(ObjOperandId obj) { - ObjOperandId res(nextOperandId_++); - writeOpWithOperandId(CacheOp::GuardAndLoadUnboxedExpando, obj); - writeOperandId(res); - return res; - } ObjOperandId loadObject(JSObject* obj) { ObjOperandId res(nextOperandId_++); @@ -310,11 +296,6 @@ class MOZ_RAII CacheIRWriter writeOpWithOperandId(CacheOp::LoadDynamicSlotResult, obj); addStubWord(offset, StubField::GCType::NoGCThing); } - void loadUnboxedPropertyResult(ObjOperandId obj, JSValueType type, size_t offset) { - writeOpWithOperandId(CacheOp::LoadUnboxedPropertyResult, obj); - buffer_.writeByte(uint32_t(type)); - addStubWord(offset, StubField::GCType::NoGCThing); - } void loadTypedObjectResult(ObjOperandId obj, uint32_t offset, TypedThingLayout layout, uint32_t typeDescr) { MOZ_ASSERT(uint32_t(layout) <= UINT8_MAX); @@ -327,9 +308,6 @@ class MOZ_RAII CacheIRWriter void loadInt32ArrayLengthResult(ObjOperandId obj) { writeOpWithOperandId(CacheOp::LoadInt32ArrayLengthResult, obj); } - void loadUnboxedArrayLengthResult(ObjOperandId obj) { - writeOpWithOperandId(CacheOp::LoadUnboxedArrayLengthResult, obj); - } void loadArgumentsObjectLengthResult(ObjOperandId obj) { writeOpWithOperandId(CacheOp::LoadArgumentsObjectLengthResult, obj); } @@ -411,9 +389,6 @@ class MOZ_RAII GetPropIRGenerator PreliminaryObjectAction preliminaryObjectAction_; MOZ_MUST_USE bool tryAttachNative(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId); - MOZ_MUST_USE bool tryAttachUnboxed(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId); - MOZ_MUST_USE bool tryAttachUnboxedExpando(CacheIRWriter& writer, HandleObject obj, - ObjOperandId objId); MOZ_MUST_USE bool tryAttachTypedObject(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId); MOZ_MUST_USE bool tryAttachObjectLength(CacheIRWriter& writer, HandleObject obj, diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 16d0260929..205812942a 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -25,6 +25,7 @@ #include "builtin/Eval.h" #include "builtin/TypedObject.h" #include "gc/Nursery.h" +#include "gc/StoreBuffer-inl.h" #include "irregexp/NativeRegExpMacroAssembler.h" #include "jit/AtomicOperations.h" #include "jit/BaselineCompiler.h" @@ -1052,7 +1053,7 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset); - RegExpStatics* res = cx->global()->getRegExpStatics(cx); + RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global()); if (!res) return false; #ifdef JS_USE_LINK_REGISTER @@ -3027,19 +3028,10 @@ CodeGenerator::visitStoreSlotV(LStoreSlotV* lir) static void GuardReceiver(MacroAssembler& masm, const ReceiverGuard& guard, - Register obj, Register scratch, Label* miss, bool checkNullExpando) + Register obj, Register scratch, Label* miss) { if (guard.group) { masm.branchTestObjGroup(Assembler::NotEqual, obj, guard.group, miss); - - Address expandoAddress(obj, UnboxedPlainObject::offsetOfExpando()); - if (guard.shape) { - masm.loadPtr(expandoAddress, scratch); - masm.branchPtr(Assembler::Equal, scratch, ImmWord(0), miss); - masm.branchTestObjShape(Assembler::NotEqual, scratch, guard.shape, miss); - } else if (checkNullExpando) { - masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), miss); - } } else { masm.branchTestObjShape(Assembler::NotEqual, obj, guard.shape, miss); } @@ -3058,13 +3050,11 @@ CodeGenerator::emitGetPropertyPolymorphic(LInstruction* ins, Register obj, Regis Label next; masm.comment("GuardReceiver"); - GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false); + GuardReceiver(masm, receiver, obj, scratch, &next); if (receiver.shape) { masm.comment("loadTypedOrValue"); - // If this is an unboxed expando access, GuardReceiver loaded the - // expando object into scratch. - Register target = receiver.group ? scratch : obj; + Register target = obj; Shape* shape = mir->shape(i); if (shape->slot() < shape->numFixedSlots()) { @@ -3077,13 +3067,6 @@ CodeGenerator::emitGetPropertyPolymorphic(LInstruction* ins, Register obj, Regis masm.loadPtr(Address(target, NativeObject::offsetOfSlots()), scratch); masm.loadTypedOrValue(Address(scratch, offset), output); } - } else { - masm.comment("loadUnboxedProperty"); - const UnboxedLayout::Property* property = - receiver.group->unboxedLayout().lookup(mir->name()); - Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset); - - masm.loadUnboxedProperty(propertyAddr, property->type, output); } if (i == mir->numReceivers() - 1) { @@ -3124,8 +3107,6 @@ EmitUnboxedPreBarrier(MacroAssembler &masm, T address, JSValueType type) masm.patchableCallPreBarrier(address, MIRType::Object); else if (type == JSVAL_TYPE_STRING) masm.patchableCallPreBarrier(address, MIRType::String); - else - MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(type)); } void @@ -3139,12 +3120,10 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Regis ReceiverGuard receiver = mir->receiver(i); Label next; - GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false); + GuardReceiver(masm, receiver, obj, scratch, &next); if (receiver.shape) { - // If this is an unboxed expando access, GuardReceiver loaded the - // expando object into scratch. - Register target = receiver.group ? scratch : obj; + Register target = obj; Shape* shape = mir->shape(i); if (shape->slot() < shape->numFixedSlots()) { @@ -3161,13 +3140,6 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Regis emitPreBarrier(addr); masm.storeConstantOrRegister(value, addr); } - } else { - const UnboxedLayout::Property* property = - receiver.group->unboxedLayout().lookup(mir->name()); - Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset); - - EmitUnboxedPreBarrier(masm, propertyAddr, property->type); - masm.storeUnboxedProperty(propertyAddr, property->type, value, nullptr); } if (i == mir->numReceivers() - 1) { @@ -3208,9 +3180,7 @@ CodeGenerator::visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins) void CodeGenerator::visitElements(LElements* lir) { - Address elements(ToRegister(lir->object()), - lir->mir()->unboxed() ? UnboxedArrayObject::offsetOfElements() - : NativeObject::offsetOfElements()); + Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements()); masm.loadPtr(elements, ToRegister(lir->output())); } @@ -3319,7 +3289,7 @@ CodeGenerator::visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir) const ReceiverGuard& receiver = mir->receiver(i); Label next; - GuardReceiver(masm, receiver, obj, temp, &next, /* checkNullExpando = */ true); + GuardReceiver(masm, receiver, obj, temp, &next); if (i == mir->numReceivers() - 1) { bailoutFrom(&next, lir->snapshot()); @@ -3333,27 +3303,6 @@ CodeGenerator::visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir) } void -CodeGenerator::visitGuardUnboxedExpando(LGuardUnboxedExpando* lir) -{ - Label miss; - - Register obj = ToRegister(lir->object()); - masm.branchPtr(lir->mir()->requireExpando() ? Assembler::Equal : Assembler::NotEqual, - Address(obj, UnboxedPlainObject::offsetOfExpando()), ImmWord(0), &miss); - - bailoutFrom(&miss, lir->snapshot()); -} - -void -CodeGenerator::visitLoadUnboxedExpando(LLoadUnboxedExpando* lir) -{ - Register obj = ToRegister(lir->object()); - Register result = ToRegister(lir->getDef(0)); - - masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), result); -} - -void CodeGenerator::visitTypeBarrierV(LTypeBarrierV* lir) { ValueOperand operand = ToValue(lir, LTypeBarrierV::Input); @@ -3739,7 +3688,13 @@ CodeGenerator::visitCallNative(LCallNative* call) masm.passABIArg(argContextReg); masm.passABIArg(argUintNReg); masm.passABIArg(argVpReg); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native())); + JSNative native = target->native(); + if (call->ignoresReturnValue()) { + const JSJitInfo* jitInfo = target->jitInfo(); + if (jitInfo && jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) + native = jitInfo->ignoresReturnValueMethod; + } + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, native)); emitTracelogStopEvent(TraceLogger_Call); @@ -3896,13 +3851,15 @@ CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir) callVM(GetIntrinsicValueInfo, lir); } -typedef bool (*InvokeFunctionFn)(JSContext*, HandleObject, bool, uint32_t, Value*, MutableHandleValue); +typedef bool (*InvokeFunctionFn)(JSContext*, HandleObject, bool, bool, uint32_t, Value*, + MutableHandleValue); static const VMFunction InvokeFunctionInfo = FunctionInfo<InvokeFunctionFn>(InvokeFunction, "InvokeFunction"); void CodeGenerator::emitCallInvokeFunction(LInstruction* call, Register calleereg, - bool constructing, uint32_t argc, uint32_t unusedStack) + bool constructing, bool ignoresReturnValue, + uint32_t argc, uint32_t unusedStack) { // Nestle %esp up to the argument vector. // Each path must account for framePushed_ separately, for callVM to be valid. @@ -3910,6 +3867,7 @@ CodeGenerator::emitCallInvokeFunction(LInstruction* call, Register calleereg, pushArg(masm.getStackPointer()); // argv. pushArg(Imm32(argc)); // argc. + pushArg(Imm32(ignoresReturnValue)); pushArg(Imm32(constructing)); // constructing. pushArg(calleereg); // JSFunction*. @@ -3996,8 +3954,8 @@ CodeGenerator::visitCallGeneric(LCallGeneric* call) // Handle uncompiled or native functions. masm.bind(&invoke); - emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->numActualArgs(), - unusedStack); + emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->ignoresReturnValue(), + call->numActualArgs(), unusedStack); masm.bind(&end); @@ -4052,7 +4010,8 @@ CodeGenerator::visitCallKnown(LCallKnown* call) masm.checkStackAlignment(); if (target->isClassConstructor() && !call->isConstructing()) { - emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->numActualArgs(), unusedStack); + emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->ignoresReturnValue(), + call->numActualArgs(), unusedStack); return; } @@ -4096,7 +4055,8 @@ CodeGenerator::visitCallKnown(LCallKnown* call) if (call->isConstructing() && target->nargs() > call->numActualArgs()) emitCallInvokeFunctionShuffleNewTarget(call, calleereg, target->nargs(), unusedStack); else - emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->numActualArgs(), unusedStack); + emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->ignoresReturnValue(), + call->numActualArgs(), unusedStack); masm.bind(&end); @@ -4123,6 +4083,7 @@ CodeGenerator::emitCallInvokeFunction(T* apply, Register extraStackSize) pushArg(objreg); // argv. pushArg(ToRegister(apply->getArgc())); // argc. + pushArg(Imm32(false)); // ignoresReturnValue. pushArg(Imm32(false)); // isConstrucing. pushArg(ToRegister(apply->getFunction())); // JSFunction*. @@ -4479,19 +4440,6 @@ CodeGenerator::visitApplyArrayGeneric(LApplyArrayGeneric* apply) emitApplyGeneric(apply); } -typedef bool (*ArraySpliceDenseFn)(JSContext*, HandleObject, uint32_t, uint32_t); -static const VMFunction ArraySpliceDenseInfo = - FunctionInfo<ArraySpliceDenseFn>(ArraySpliceDense, "ArraySpliceDense"); - -void -CodeGenerator::visitArraySplice(LArraySplice* lir) -{ - pushArg(ToRegister(lir->getDeleteCount())); - pushArg(ToRegister(lir->getStart())); - pushArg(ToRegister(lir->getObject())); - callVM(ArraySpliceDenseInfo, lir); -} - void CodeGenerator::visitBail(LBail* lir) { @@ -5217,11 +5165,11 @@ static JSObject* NewArrayWithGroup(JSContext* cx, uint32_t length, HandleObjectGroup group, bool convertDoubleElements) { - JSObject* res = NewFullyAllocatedArrayTryUseGroup(cx, group, length); + ArrayObject* res = NewFullyAllocatedArrayTryUseGroup(cx, group, length); if (!res) return nullptr; if (convertDoubleElements) - res->as<ArrayObject>().setShouldConvertDoubleElements(); + res->setShouldConvertDoubleElements(); return res; } @@ -5367,7 +5315,7 @@ CodeGenerator::visitNewArrayCopyOnWrite(LNewArrayCopyOnWrite* lir) masm.bind(ool->rejoin()); } -typedef JSObject* (*ArrayConstructorOneArgFn)(JSContext*, HandleObjectGroup, int32_t length); +typedef ArrayObject* (*ArrayConstructorOneArgFn)(JSContext*, HandleObjectGroup, int32_t length); static const VMFunction ArrayConstructorOneArgInfo = FunctionInfo<ArrayConstructorOneArgFn>(ArrayConstructorOneArg, "ArrayConstructorOneArg"); @@ -5387,21 +5335,11 @@ CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir) bool canInline = true; size_t inlineLength = 0; - if (templateObject->is<ArrayObject>()) { - if (templateObject->as<ArrayObject>().hasFixedElements()) { - size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()); - inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER; - } else { - canInline = false; - } + if (templateObject->as<ArrayObject>().hasFixedElements()) { + size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()); + inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER; } else { - if (templateObject->as<UnboxedArrayObject>().hasInlineElements()) { - size_t nbytes = - templateObject->tenuredSizeOfThis() - UnboxedArrayObject::offsetOfInlineElements(); - inlineLength = nbytes / templateObject->as<UnboxedArrayObject>().elementSize(); - } else { - canInline = false; - } + canInline = false; } if (canInline) { @@ -7823,7 +7761,7 @@ CodeGenerator::visitSinCos(LSinCos *lir) masm.freeStack(sizeof(double) * 2); } -typedef JSObject* (*StringSplitFn)(JSContext*, HandleObjectGroup, HandleString, HandleString, uint32_t); +typedef ArrayObject* (*StringSplitFn)(JSContext*, HandleObjectGroup, HandleString, HandleString, uint32_t); static const VMFunction StringSplitInfo = FunctionInfo<StringSplitFn>(js::str_split_string, "str_split_string"); @@ -7858,49 +7796,6 @@ CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir) } void -CodeGenerator::visitUnboxedArrayLength(LUnboxedArrayLength* lir) -{ - Register obj = ToRegister(lir->object()); - Register result = ToRegister(lir->output()); - masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), result); -} - -void -CodeGenerator::visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir) -{ - Register obj = ToRegister(lir->object()); - Register result = ToRegister(lir->output()); - masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), result); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), result); -} - -void -CodeGenerator::visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir) -{ - Register obj = ToRegister(lir->object()); - masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); -} - -void -CodeGenerator::visitSetUnboxedArrayInitializedLength(LSetUnboxedArrayInitializedLength* lir) -{ - Register obj = ToRegister(lir->object()); - RegisterOrInt32Constant key = ToRegisterOrInt32Constant(lir->length()); - Register temp = ToRegister(lir->temp()); - - Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLengthAddr, temp); - masm.and32(Imm32(UnboxedArrayObject::CapacityMask), temp); - - if (key.isRegister()) - masm.or32(key.reg(), temp); - else - masm.or32(Imm32(key.constant()), temp); - - masm.store32(temp, initLengthAddr); -} - -void CodeGenerator::visitNotO(LNotO* lir) { MOZ_ASSERT(lir->mir()->operandMightEmulateUndefined(), @@ -8196,46 +8091,19 @@ CodeGenerator::emitStoreElementHoleT(T* lir) OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir); addOutOfLineCode(ool, lir->mir()); - Register obj = ToRegister(lir->object()); Register elements = ToRegister(lir->elements()); const LAllocation* index = lir->index(); RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index); - JSValueType unboxedType = lir->mir()->unboxedType(); - if (unboxedType == JSVAL_TYPE_MAGIC) { - Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry()); - - if (lir->mir()->needsBarrier()) - emitPreBarrier(elements, index, 0); - - masm.bind(ool->rejoinStore()); - emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(), - elements, index, 0); - } else { - Register temp = ToRegister(lir->getTemp(0)); - Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLength, temp); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp); - masm.branch32(Assembler::BelowOrEqual, temp, key, ool->entry()); - - ConstantOrRegister v = ToConstantOrRegister(lir->value(), lir->mir()->value()->type()); + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry()); - if (index->isConstant()) { - Address address(elements, ToInt32(index) * UnboxedTypeSize(unboxedType)); - EmitUnboxedPreBarrier(masm, address, unboxedType); - - masm.bind(ool->rejoinStore()); - masm.storeUnboxedProperty(address, unboxedType, v, nullptr); - } else { - BaseIndex address(elements, ToRegister(index), - ScaleFromElemWidth(UnboxedTypeSize(unboxedType))); - EmitUnboxedPreBarrier(masm, address, unboxedType); + if (lir->mir()->needsBarrier()) + emitPreBarrier(elements, index, 0); - masm.bind(ool->rejoinStore()); - masm.storeUnboxedProperty(address, unboxedType, v, nullptr); - } - } + masm.bind(ool->rejoinStore()); + emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(), + elements, index, 0); masm.bind(ool->rejoin()); } @@ -8255,47 +8123,22 @@ CodeGenerator::emitStoreElementHoleV(T* lir) OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir); addOutOfLineCode(ool, lir->mir()); - Register obj = ToRegister(lir->object()); Register elements = ToRegister(lir->elements()); const LAllocation* index = lir->index(); const ValueOperand value = ToValue(lir, T::Value); RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index); - JSValueType unboxedType = lir->mir()->unboxedType(); - if (unboxedType == JSVAL_TYPE_MAGIC) { - Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry()); + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry()); - if (lir->mir()->needsBarrier()) - emitPreBarrier(elements, index, 0); - - masm.bind(ool->rejoinStore()); - if (index->isConstant()) - masm.storeValue(value, Address(elements, ToInt32(index) * sizeof(js::Value))); - else - masm.storeValue(value, BaseIndex(elements, ToRegister(index), TimesEight)); - } else { - Register temp = ToRegister(lir->getTemp(0)); - Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLength, temp); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp); - masm.branch32(Assembler::BelowOrEqual, temp, key, ool->entry()); - - if (index->isConstant()) { - Address address(elements, ToInt32(index) * UnboxedTypeSize(unboxedType)); - EmitUnboxedPreBarrier(masm, address, unboxedType); - - masm.bind(ool->rejoinStore()); - masm.storeUnboxedProperty(address, unboxedType, ConstantOrRegister(value), nullptr); - } else { - BaseIndex address(elements, ToRegister(index), - ScaleFromElemWidth(UnboxedTypeSize(unboxedType))); - EmitUnboxedPreBarrier(masm, address, unboxedType); + if (lir->mir()->needsBarrier()) + emitPreBarrier(elements, index, 0); - masm.bind(ool->rejoinStore()); - masm.storeUnboxedProperty(address, unboxedType, ConstantOrRegister(value), nullptr); - } - } + masm.bind(ool->rejoinStore()); + if (index->isConstant()) + masm.storeValue(value, Address(elements, ToInt32(index) * sizeof(js::Value))); + else + masm.storeValue(value, BaseIndex(elements, ToRegister(index), TimesEight)); masm.bind(ool->rejoin()); } @@ -8366,11 +8209,10 @@ CodeGenerator::visitFallibleStoreElementV(LFallibleStoreElementV* lir) masm.bind(&isFrozen); } -typedef bool (*SetDenseOrUnboxedArrayElementFn)(JSContext*, HandleObject, int32_t, - HandleValue, bool strict); -static const VMFunction SetDenseOrUnboxedArrayElementInfo = - FunctionInfo<SetDenseOrUnboxedArrayElementFn>(SetDenseOrUnboxedArrayElement, - "SetDenseOrUnboxedArrayElement"); +typedef bool (*SetDenseElementFn)(JSContext*, HandleNativeObject, int32_t, HandleValue, + bool strict); +static const VMFunction SetDenseElementInfo = + FunctionInfo<SetDenseElementFn>(jit::SetDenseElement, "SetDenseElement"); void CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) @@ -8380,8 +8222,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) const LAllocation* index; MIRType valueType; ConstantOrRegister value; - JSValueType unboxedType; - LDefinition *temp = nullptr; if (ins->isStoreElementHoleV()) { LStoreElementHoleV* store = ins->toStoreElementHoleV(); @@ -8390,8 +8230,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) index = store->index(); valueType = store->mir()->value()->type(); value = TypedOrValueRegister(ToValue(store, LStoreElementHoleV::Value)); - unboxedType = store->mir()->unboxedType(); - temp = store->getTemp(0); } else if (ins->isFallibleStoreElementV()) { LFallibleStoreElementV* store = ins->toFallibleStoreElementV(); object = ToRegister(store->object()); @@ -8399,8 +8237,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) index = store->index(); valueType = store->mir()->value()->type(); value = TypedOrValueRegister(ToValue(store, LFallibleStoreElementV::Value)); - unboxedType = store->mir()->unboxedType(); - temp = store->getTemp(0); } else if (ins->isStoreElementHoleT()) { LStoreElementHoleT* store = ins->toStoreElementHoleT(); object = ToRegister(store->object()); @@ -8411,8 +8247,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) value = ConstantOrRegister(store->value()->toConstant()->toJSValue()); else value = TypedOrValueRegister(valueType, ToAnyRegister(store->value())); - unboxedType = store->mir()->unboxedType(); - temp = store->getTemp(0); } else { // ins->isFallibleStoreElementT() LFallibleStoreElementT* store = ins->toFallibleStoreElementT(); object = ToRegister(store->object()); @@ -8423,8 +8257,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) value = ConstantOrRegister(store->value()->toConstant()->toJSValue()); else value = TypedOrValueRegister(valueType, ToAnyRegister(store->value())); - unboxedType = store->mir()->unboxedType(); - temp = store->getTemp(0); } RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index); @@ -8435,54 +8267,32 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) Label callStub; #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) // Had to reimplement for MIPS because there are no flags. - if (unboxedType == JSVAL_TYPE_MAGIC) { - Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::NotEqual, initLength, key, &callStub); - } else { - Address initLength(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLength, ToRegister(temp)); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), ToRegister(temp)); - masm.branch32(Assembler::NotEqual, ToRegister(temp), key, &callStub); - } + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::NotEqual, initLength, key, &callStub); #else masm.j(Assembler::NotEqual, &callStub); #endif - if (unboxedType == JSVAL_TYPE_MAGIC) { - // Check array capacity. - masm.branch32(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()), - key, &callStub); - - // Update initialized length. The capacity guard above ensures this won't overflow, - // due to MAX_DENSE_ELEMENTS_COUNT. - masm.inc32(&key); - masm.store32(key, Address(elements, ObjectElements::offsetOfInitializedLength())); - - // Update length if length < initializedLength. - Label dontUpdate; - masm.branch32(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()), - key, &dontUpdate); - masm.store32(key, Address(elements, ObjectElements::offsetOfLength())); - masm.bind(&dontUpdate); + // Check array capacity. + masm.branch32(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()), + key, &callStub); - masm.dec32(&key); - } else { - // Check array capacity. - masm.checkUnboxedArrayCapacity(object, key, ToRegister(temp), &callStub); + // Update initialized length. The capacity guard above ensures this won't overflow, + // due to MAX_DENSE_ELEMENTS_COUNT. + masm.inc32(&key); + masm.store32(key, Address(elements, ObjectElements::offsetOfInitializedLength())); - // Update initialized length. - masm.add32(Imm32(1), Address(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); + // Update length if length < initializedLength. + Label dontUpdate; + masm.branch32(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()), + key, &dontUpdate); + masm.store32(key, Address(elements, ObjectElements::offsetOfLength())); + masm.bind(&dontUpdate); - // Update length if length < initializedLength. - Address lengthAddr(object, UnboxedArrayObject::offsetOfLength()); - Label dontUpdate; - masm.branch32(Assembler::Above, lengthAddr, key, &dontUpdate); - masm.add32(Imm32(1), lengthAddr); - masm.bind(&dontUpdate); - } + masm.dec32(&key); if ((ins->isStoreElementHoleT() || ins->isFallibleStoreElementT()) && - unboxedType == JSVAL_TYPE_MAGIC && valueType != MIRType::Double) + valueType != MIRType::Double) { // The inline path for StoreElementHoleT and FallibleStoreElementT does not always store // the type tag, so we do the store on the OOL path. We use MIRType::None for the element @@ -8511,7 +8321,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) else pushArg(ToRegister(index)); pushArg(object); - callVM(SetDenseOrUnboxedArrayElementInfo, ins); + callVM(SetDenseElementInfo, ins); restoreLive(ins); masm.jump(ool->rejoin()); @@ -8568,29 +8378,6 @@ CodeGenerator::visitStoreUnboxedPointer(LStoreUnboxedPointer* lir) } } -typedef bool (*ConvertUnboxedObjectToNativeFn)(JSContext*, JSObject*); -static const VMFunction ConvertUnboxedPlainObjectToNativeInfo = - FunctionInfo<ConvertUnboxedObjectToNativeFn>(UnboxedPlainObject::convertToNative, - "UnboxedPlainObject::convertToNative"); -static const VMFunction ConvertUnboxedArrayObjectToNativeInfo = - FunctionInfo<ConvertUnboxedObjectToNativeFn>(UnboxedArrayObject::convertToNative, - "UnboxedArrayObject::convertToNative"); - -void -CodeGenerator::visitConvertUnboxedObjectToNative(LConvertUnboxedObjectToNative* lir) -{ - Register object = ToRegister(lir->getOperand(0)); - - OutOfLineCode* ool = oolCallVM(lir->mir()->group()->unboxedLayoutDontCheckGeneration().isArray() - ? ConvertUnboxedArrayObjectToNativeInfo - : ConvertUnboxedPlainObjectToNativeInfo, - lir, ArgList(object), StoreNothing()); - - masm.branchPtr(Assembler::Equal, Address(object, JSObject::offsetOfGroup()), - ImmGCPtr(lir->mir()->group()), ool->entry()); - masm.bind(ool->rejoin()); -} - typedef bool (*ArrayPopShiftFn)(JSContext*, HandleObject, MutableHandleValue); static const VMFunction ArrayPopDenseInfo = FunctionInfo<ArrayPopShiftFn>(jit::ArrayPopDense, "ArrayPopDense"); @@ -8615,20 +8402,11 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R // Load elements and length, and VM call if length != initializedLength. RegisterOrInt32Constant key = RegisterOrInt32Constant(lengthTemp); - if (mir->unboxedType() == JSVAL_TYPE_MAGIC) { - masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp); - masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp); - - Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::NotEqual, initLength, key, ool->entry()); - } else { - masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), elementsTemp); - masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), lengthTemp); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), lengthTemp); + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp); + masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp); - Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength()); - masm.branch32(Assembler::NotEqual, lengthAddr, key, ool->entry()); - } + Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::NotEqual, initLength, key, ool->entry()); // Test for length != 0. On zero length either take a VM call or generate // an undefined value, depending on whether the call is known to produce @@ -8640,13 +8418,10 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R // According to the spec we need to set the length 0 (which is already 0). // This is observable when the array length is made non-writable. - // Handle this case in the OOL. When freezing an unboxed array it is converted - // to an normal array. - if (mir->unboxedType() == JSVAL_TYPE_MAGIC) { - Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags()); - Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH); - masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry()); - } + // Handle this case in the OOL. + Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags()); + Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH); + masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry()); masm.moveValue(UndefinedValue(), out.valueReg()); masm.jump(&done); @@ -8658,41 +8433,25 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R masm.dec32(&key); if (mir->mode() == MArrayPopShift::Pop) { - if (mir->unboxedType() == JSVAL_TYPE_MAGIC) { - BaseIndex addr(elementsTemp, lengthTemp, TimesEight); - masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry()); - } else { - size_t elemSize = UnboxedTypeSize(mir->unboxedType()); - BaseIndex addr(elementsTemp, lengthTemp, ScaleFromElemWidth(elemSize)); - masm.loadUnboxedProperty(addr, mir->unboxedType(), out); - } + BaseIndex addr(elementsTemp, lengthTemp, TimesEight); + masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry()); } else { MOZ_ASSERT(mir->mode() == MArrayPopShift::Shift); Address addr(elementsTemp, 0); - if (mir->unboxedType() == JSVAL_TYPE_MAGIC) - masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry()); - else - masm.loadUnboxedProperty(addr, mir->unboxedType(), out); + masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry()); } - if (mir->unboxedType() == JSVAL_TYPE_MAGIC) { - // Handle the failure case when the array length is non-writable in the - // OOL path. (Unlike in the adding-an-element cases, we can't rely on the - // capacity <= length invariant for such arrays to avoid an explicit - // check.) - Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags()); - Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH); - masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry()); + // Handle the failure case when the array length is non-writable in the + // OOL path. (Unlike in the adding-an-element cases, we can't rely on the + // capacity <= length invariant for such arrays to avoid an explicit + // check.) + Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags()); + Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH); + masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry()); - // Now adjust length and initializedLength. - masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength())); - masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength())); - } else { - // Unboxed arrays always have writable lengths. Adjust length and - // initializedLength. - masm.store32(lengthTemp, Address(obj, UnboxedArrayObject::offsetOfLength())); - masm.add32(Imm32(-1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); - } + // Now adjust length and initializedLength. + masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength())); + masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength())); if (mir->mode() == MArrayPopShift::Shift) { // Don't save the temp registers. @@ -8731,7 +8490,7 @@ CodeGenerator::visitArrayPopShiftT(LArrayPopShiftT* lir) emitArrayPopShift(lir, lir->mir(), obj, elements, length, out); } -typedef bool (*ArrayPushDenseFn)(JSContext*, HandleObject, HandleValue, uint32_t*); +typedef bool (*ArrayPushDenseFn)(JSContext*, HandleArrayObject, HandleValue, uint32_t*); static const VMFunction ArrayPushDenseInfo = FunctionInfo<ArrayPushDenseFn>(jit::ArrayPushDense, "ArrayPushDense"); @@ -8742,50 +8501,27 @@ CodeGenerator::emitArrayPush(LInstruction* lir, const MArrayPush* mir, Register OutOfLineCode* ool = oolCallVM(ArrayPushDenseInfo, lir, ArgList(obj, value), StoreRegisterTo(length)); RegisterOrInt32Constant key = RegisterOrInt32Constant(length); - if (mir->unboxedType() == JSVAL_TYPE_MAGIC) { - // Load elements and length. - masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp); - masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length); - // Guard length == initializedLength. - Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::NotEqual, initLength, key, ool->entry()); + // Load elements and length. + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp); + masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length); - // Guard length < capacity. - Address capacity(elementsTemp, ObjectElements::offsetOfCapacity()); - masm.branch32(Assembler::BelowOrEqual, capacity, key, ool->entry()); + // Guard length == initializedLength. + Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::NotEqual, initLength, key, ool->entry()); - // Do the store. - masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight)); - } else { - // Load initialized length. - masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), length); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), length); - - // Guard length == initializedLength. - Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength()); - masm.branch32(Assembler::NotEqual, lengthAddr, key, ool->entry()); + // Guard length < capacity. + Address capacity(elementsTemp, ObjectElements::offsetOfCapacity()); + masm.branch32(Assembler::BelowOrEqual, capacity, key, ool->entry()); - // Guard length < capacity. - masm.checkUnboxedArrayCapacity(obj, key, elementsTemp, ool->entry()); - - // Load elements and do the store. - masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), elementsTemp); - size_t elemSize = UnboxedTypeSize(mir->unboxedType()); - BaseIndex addr(elementsTemp, length, ScaleFromElemWidth(elemSize)); - masm.storeUnboxedProperty(addr, mir->unboxedType(), value, nullptr); - } + // Do the store. + masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight)); masm.inc32(&key); // Update length and initialized length. - if (mir->unboxedType() == JSVAL_TYPE_MAGIC) { - masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength())); - masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength())); - } else { - masm.store32(length, Address(obj, UnboxedArrayObject::offsetOfLength())); - masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); - } + masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength())); + masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength())); masm.bind(ool->rejoin()); } @@ -8934,11 +8670,11 @@ CodeGenerator::visitIteratorStartO(LIteratorStartO* lir) masm.loadPtr(Address(niTemp, offsetof(NativeIterator, guard_array)), temp2); // Compare object with the first receiver guard. The last iterator can only - // match for native objects and unboxed objects. + // match for native objects. { Address groupAddr(temp2, offsetof(ReceiverGuard, group)); Address shapeAddr(temp2, offsetof(ReceiverGuard, shape)); - Label guardDone, shapeMismatch, noExpando; + Label guardDone, shapeMismatch; masm.loadObjShape(obj, temp1); masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, &shapeMismatch); @@ -8950,12 +8686,6 @@ CodeGenerator::visitIteratorStartO(LIteratorStartO* lir) masm.bind(&shapeMismatch); masm.loadObjGroup(obj, temp1); masm.branchPtr(Assembler::NotEqual, groupAddr, temp1, ool->entry()); - masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), temp1); - masm.branchTestPtr(Assembler::Zero, temp1, temp1, &noExpando); - branchIfNotEmptyObjectElements(temp1, ool->entry()); - masm.loadObjShape(temp1, temp1); - masm.bind(&noExpando); - masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, ool->entry()); masm.bind(&guardDone); } @@ -10590,22 +10320,11 @@ CodeGenerator::visitLoadElementHole(LLoadElementHole* lir) else masm.branch32(Assembler::BelowOrEqual, initLength, ToRegister(lir->index()), &undefined); - if (mir->unboxedType() != JSVAL_TYPE_MAGIC) { - size_t width = UnboxedTypeSize(mir->unboxedType()); - if (lir->index()->isConstant()) { - Address addr(elements, ToInt32(lir->index()) * width); - masm.loadUnboxedProperty(addr, mir->unboxedType(), out); - } else { - BaseIndex addr(elements, ToRegister(lir->index()), ScaleFromElemWidth(width)); - masm.loadUnboxedProperty(addr, mir->unboxedType(), out); - } + if (lir->index()->isConstant()) { + NativeObject::elementsSizeMustNotOverflow(); + masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out); } else { - if (lir->index()->isConstant()) { - NativeObject::elementsSizeMustNotOverflow(); - masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out); - } else { - masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out); - } + masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out); } // If a hole check is needed, and the value wasn't a hole, we're done. @@ -10983,7 +10702,7 @@ CodeGenerator::visitInArray(LInArray* lir) } masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index), failedInitLength); - if (mir->needsHoleCheck() && mir->unboxedType() == JSVAL_TYPE_MAGIC) { + if (mir->needsHoleCheck()) { NativeObject::elementsSizeMustNotOverflow(); Address address = Address(elements, index * sizeof(Value)); masm.branchTestMagic(Assembler::Equal, address, &falseBranch); @@ -10996,7 +10715,7 @@ CodeGenerator::visitInArray(LInArray* lir) failedInitLength = &negativeIntCheck; masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength); - if (mir->needsHoleCheck() && mir->unboxedType() == JSVAL_TYPE_MAGIC) { + if (mir->needsHoleCheck()) { BaseIndex address = BaseIndex(elements, ToRegister(lir->index()), TimesEight); masm.branchTestMagic(Assembler::Equal, address, &falseBranch); } diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index b226f6cc91..12f1238ef3 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -148,8 +148,6 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite* lir); void visitGuardObjectIdentity(LGuardObjectIdentity* guard); void visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir); - void visitGuardUnboxedExpando(LGuardUnboxedExpando* lir); - void visitLoadUnboxedExpando(LLoadUnboxedExpando* lir); void visitTypeBarrierV(LTypeBarrierV* lir); void visitTypeBarrierO(LTypeBarrierO* lir); void visitMonitorTypes(LMonitorTypes* lir); @@ -167,8 +165,8 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitOutOfLineCallPostWriteElementBarrier(OutOfLineCallPostWriteElementBarrier* ool); void visitCallNative(LCallNative* call); void emitCallInvokeFunction(LInstruction* call, Register callereg, - bool isConstructing, uint32_t argc, - uint32_t unusedStack); + bool isConstructing, bool ignoresReturnValue, + uint32_t argc, uint32_t unusedStack); void visitCallGeneric(LCallGeneric* call); void emitCallInvokeFunctionShuffleNewTarget(LCallKnown *call, Register calleeReg, @@ -236,10 +234,6 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitSubstr(LSubstr* lir); void visitInitializedLength(LInitializedLength* lir); void visitSetInitializedLength(LSetInitializedLength* lir); - void visitUnboxedArrayLength(LUnboxedArrayLength* lir); - void visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir); - void visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir); - void visitSetUnboxedArrayInitializedLength(LSetUnboxedArrayInitializedLength* lir); void visitNotO(LNotO* ins); void visitNotV(LNotV* ins); void visitBoundsCheck(LBoundsCheck* lir); @@ -257,7 +251,6 @@ class CodeGenerator final : public CodeGeneratorSpecific void emitSetPropertyPolymorphic(LInstruction* lir, Register obj, Register scratch, const ConstantOrRegister& value); void visitSetPropertyPolymorphicV(LSetPropertyPolymorphicV* ins); - void visitArraySplice(LArraySplice* splice); void visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins); void visitAbsI(LAbsI* lir); void visitAtan2D(LAtan2D* lir); @@ -310,7 +303,6 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitFallibleStoreElementV(LFallibleStoreElementV* lir); void visitFallibleStoreElementT(LFallibleStoreElementT* lir); void visitStoreUnboxedPointer(LStoreUnboxedPointer* lir); - void visitConvertUnboxedObjectToNative(LConvertUnboxedObjectToNative* lir); void emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, Register obj, Register elementsTemp, Register lengthTemp, TypedOrValueRegister out); void visitArrayPopShiftV(LArrayPopShiftV* lir); diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index 23d3143a78..9c864515d0 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -15,7 +15,6 @@ _(ArrayShift) \ _(ArrayPush) \ _(ArraySlice) \ - _(ArraySplice) \ \ _(AtomicsCompareExchange) \ _(AtomicsExchange) \ diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index c61b414e07..b8a2d2fba5 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2153,7 +2153,7 @@ IonCompile(JSContext* cx, JSScript* script, // Make sure the script's canonical function isn't lazy. We can't de-lazify // it in a helper thread. - script->ensureNonLazyCanonicalFunction(cx); + script->ensureNonLazyCanonicalFunction(); TrackPropertiesForSingletonScopes(cx, script, baselineFrame); diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index 2c9ffb607d..ace6cd81e4 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -2306,7 +2306,7 @@ jit::RemoveUnmarkedBlocks(MIRGenerator* mir, MIRGraph& graph, uint32_t numMarked // bailout. for (PostorderIterator it(graph.poBegin()); it != graph.poEnd();) { MBasicBlock* block = *it++; - if (!block->isMarked()) + if (block->isMarked()) continue; FlagAllOperandsAsHavingRemovedUses(mir, block); @@ -3127,6 +3127,15 @@ ExtractMathSpace(MDefinition* ins) MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown TruncateKind"); } +static bool MonotoneAdd(int32_t lhs, int32_t rhs) { + return (lhs >= 0 && rhs >= 0) || (lhs <= 0 && rhs <= 0); +} + +static bool MonotoneSub(int32_t lhs, int32_t rhs) { + return (lhs >= 0 && rhs <= 0) || (lhs <= 0 && rhs >= 0); +} + + // Extract a linear sum from ins, if possible (otherwise giving the sum 'ins + 0'). SimpleLinearSum jit::ExtractLinearSum(MDefinition* ins, MathSpace space) @@ -3168,10 +3177,12 @@ jit::ExtractLinearSum(MDefinition* ins, MathSpace space) // Check if this is of the form <SUM> + n or n + <SUM>. if (ins->isAdd()) { int32_t constant; - if (space == MathSpace::Modulo) + if (space == MathSpace::Modulo) { constant = lsum.constant + rsum.constant; - else if (!SafeAdd(lsum.constant, rsum.constant, &constant)) + } else if (!SafeAdd(lsum.constant, rsum.constant, &constant) || + !MonotoneAdd(lsum.constant, rsum.constant)) { return SimpleLinearSum(ins, 0); + } return SimpleLinearSum(lsum.term ? lsum.term : rsum.term, constant); } @@ -3179,10 +3190,12 @@ jit::ExtractLinearSum(MDefinition* ins, MathSpace space) // Check if this is of the form <SUM> - n. if (lsum.term) { int32_t constant; - if (space == MathSpace::Modulo) + if (space == MathSpace::Modulo) { constant = lsum.constant - rsum.constant; - else if (!SafeSub(lsum.constant, rsum.constant, &constant)) + } else if (!SafeSub(lsum.constant, rsum.constant, &constant) || + !MonotoneSub(lsum.constant, rsum.constant)) { return SimpleLinearSum(ins, 0); + } return SimpleLinearSum(lsum.term, constant); } @@ -3502,8 +3515,6 @@ PassthroughOperand(MDefinition* def) return def->toConvertElementsToDoubles()->elements(); if (def->isMaybeCopyElementsForWrite()) return def->toMaybeCopyElementsForWrite()->object(); - if (def->isConvertUnboxedObjectToNative()) - return def->toConvertUnboxedObjectToNative()->object(); return nullptr; } @@ -3994,7 +4005,7 @@ jit::ConvertLinearInequality(TempAllocator& alloc, MBasicBlock* block, const Lin } static bool -AnalyzePoppedThis(JSContext* cx, ObjectGroup* group, +AnalyzePoppedThis(JSContext* cx, DPAConstraintInfo& constraintInfo, ObjectGroup* group, MDefinition* thisValue, MInstruction* ins, bool definitelyExecuted, HandlePlainObject baseobj, Vector<TypeNewScript::Initializer>* initializerList, @@ -4035,7 +4046,12 @@ AnalyzePoppedThis(JSContext* cx, ObjectGroup* group, return true; RootedId id(cx, NameToId(setprop->name())); - if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, group, id)) { + bool added = false; + if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, constraintInfo, + group, id, &added)) { + return false; + } + if (!added) { // The prototype chain already contains a getter/setter for this // property, or type information is too imprecise. return true; @@ -4044,7 +4060,7 @@ AnalyzePoppedThis(JSContext* cx, ObjectGroup* group, // Add the property to the object, being careful not to update type information. DebugOnly<unsigned> slotSpan = baseobj->slotSpan(); MOZ_ASSERT(!baseobj->containsPure(id)); - if (!baseobj->addDataProperty(cx, id, baseobj->slotSpan(), JSPROP_ENUMERATE)) + if (!NativeObject::addDataProperty(cx, baseobj, id, baseobj->slotSpan(), JSPROP_ENUMERATE)) return false; MOZ_ASSERT(baseobj->slotSpan() != slotSpan); MOZ_ASSERT(!baseobj->inDictionaryMode()); @@ -4095,7 +4111,12 @@ AnalyzePoppedThis(JSContext* cx, ObjectGroup* group, if (!baseobj->lookup(cx, id) && !accessedProperties->append(get->name())) return false; - if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, group, id)) { + bool added = false; + if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, constraintInfo, + group, id, &added)) { + return false; + } + if (!added) { // The |this| value can escape if any property reads it does go // through a getter. return true; @@ -4121,8 +4142,11 @@ CmpInstructions(const void* a, const void* b) } bool -jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun, - ObjectGroup* group, HandlePlainObject baseobj, +jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, + DPAConstraintInfo& constraintInfo, + HandleFunction fun, + ObjectGroup* group, + HandlePlainObject baseobj, Vector<TypeNewScript::Initializer>* initializerList) { MOZ_ASSERT(cx->zone()->types.activeAnalysis); @@ -4131,7 +4155,7 @@ jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun, // which will definitely be added to the created object before it has a // chance to escape and be accessed elsewhere. - RootedScript script(cx, fun->getOrCreateScript(cx)); + RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun)); if (!script) return false; @@ -4282,7 +4306,7 @@ jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun, bool handled = false; size_t slotSpan = baseobj->slotSpan(); - if (!AnalyzePoppedThis(cx, group, thisValue, ins, definitelyExecuted, + if (!AnalyzePoppedThis(cx, constraintInfo, group, thisValue, ins, definitelyExecuted, baseobj, initializerList, &accessedProperties, &handled)) { return false; @@ -4301,7 +4325,6 @@ jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun, // contingent on the correct frames being inlined. Add constraints to // invalidate the definite properties if additional functions could be // called at the inline frame sites. - Vector<MBasicBlock*> exitBlocks(cx); for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) { // Inlining decisions made after the last new property was added to // the object don't need to be frozen. @@ -4309,9 +4332,11 @@ jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun, break; if (MResumePoint* rp = block->callerResumePoint()) { if (block->numPredecessors() == 1 && block->getPredecessor(0) == rp->block()) { - JSScript* script = rp->block()->info().script(); - if (!AddClearDefiniteFunctionUsesInScript(cx, group, script, block->info().script())) + JSScript* caller = rp->block()->info().script(); + JSScript* callee = block->info().script(); + if (!constraintInfo.addInliningConstraint(caller, callee)) { return false; + } } } } diff --git a/js/src/jit/IonAnalysis.h b/js/src/jit/IonAnalysis.h index 1ce8edc804..49bc0b5919 100644 --- a/js/src/jit/IonAnalysis.h +++ b/js/src/jit/IonAnalysis.h @@ -196,8 +196,11 @@ MCompare* ConvertLinearInequality(TempAllocator& alloc, MBasicBlock* block, const LinearSum& sum); MOZ_MUST_USE bool -AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun, - ObjectGroup* group, HandlePlainObject baseobj, +AnalyzeNewScriptDefiniteProperties(JSContext* cx, + DPAConstraintInfo& constraintInfo, + HandleFunction fun, + ObjectGroup* group, + HandlePlainObject baseobj, Vector<TypeNewScript::Initializer>* initializerList); MOZ_MUST_USE bool diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 54d05cac4e..0c69729a4c 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -32,7 +32,6 @@ #include "vm/EnvironmentObject-inl.h" #include "vm/NativeObject-inl.h" #include "vm/ObjectGroup-inl.h" -#include "vm/UnboxedObject-inl.h" using namespace js; using namespace js::jit; @@ -467,7 +466,8 @@ IonBuilder::canInlineTarget(JSFunction* target, CallInfo& callInfo) // Allow constructing lazy scripts when performing the definite properties // analysis, as baseline has not been used to warm the caller up yet. if (target->isInterpreted() && info().analysisMode() == Analysis_DefiniteProperties) { - RootedScript script(analysisContext, target->getOrCreateScript(analysisContext)); + RootedFunction fun(analysisContext, target); + RootedScript script(analysisContext, JSFunction::getOrCreateScript(analysisContext, fun)); if (!script) return InliningDecision_Error; @@ -1939,6 +1939,7 @@ IonBuilder::inspectOpcode(JSOp op) return jsop_funapply(GET_ARGC(pc)); case JSOP_CALL: + case JSOP_CALL_IGNORES_RV: case JSOP_CALLITER: case JSOP_NEW: case JSOP_SUPERCALL: @@ -1946,7 +1947,8 @@ IonBuilder::inspectOpcode(JSOp op) if (!outermostBuilder()->iterators_.append(current->peek(-1))) return false; } - return jsop_call(GET_ARGC(pc), (JSOp)*pc == JSOP_NEW || (JSOp)*pc == JSOP_SUPERCALL); + return jsop_call(GET_ARGC(pc), (JSOp)*pc == JSOP_NEW || (JSOp)*pc == JSOP_SUPERCALL, + (JSOp)*pc == JSOP_CALL_IGNORES_RV); case JSOP_EVAL: case JSOP_STRICTEVAL: @@ -2219,6 +2221,8 @@ IonBuilder::inspectOpcode(JSOp op) // update that stale value. #endif default: + // Any unused opcodes and JSOP_LIMIT will end up here without having + // to explicitly specify break; } @@ -5872,7 +5876,7 @@ IonBuilder::inlineGenericFallback(JSFunction* target, CallInfo& callInfo, MBasic return false; // Create a new CallInfo to track modified state within this block. - CallInfo fallbackInfo(alloc(), callInfo.constructing()); + CallInfo fallbackInfo(alloc(), callInfo.constructing(), callInfo.ignoresReturnValue()); if (!fallbackInfo.init(callInfo)) return false; fallbackInfo.popFormals(fallbackBlock); @@ -5911,7 +5915,7 @@ IonBuilder::inlineObjectGroupFallback(CallInfo& callInfo, MBasicBlock* dispatchB MOZ_ASSERT(cache->idempotent()); // Create a new CallInfo to track modified state within the fallback path. - CallInfo fallbackInfo(alloc(), callInfo.constructing()); + CallInfo fallbackInfo(alloc(), callInfo.constructing(), callInfo.ignoresReturnValue()); if (!fallbackInfo.init(callInfo)) return false; @@ -6087,7 +6091,7 @@ IonBuilder::inlineCalls(CallInfo& callInfo, const ObjectVector& targets, BoolVec inlineBlock->rewriteSlot(funIndex, funcDef); // Create a new CallInfo to track modified state within the inline block. - CallInfo inlineInfo(alloc(), callInfo.constructing()); + CallInfo inlineInfo(alloc(), callInfo.constructing(), callInfo.ignoresReturnValue()); if (!inlineInfo.init(callInfo)) return false; inlineInfo.popFormals(inlineBlock); @@ -6392,7 +6396,7 @@ IonBuilder::createThisScriptedSingleton(JSFunction* target, MDefinition* callee) JSObject* templateObject = inspector->getTemplateObject(pc); if (!templateObject) return nullptr; - if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>()) + if (!templateObject->is<PlainObject>()) return nullptr; if (templateObject->staticPrototype() != proto) return nullptr; @@ -6429,7 +6433,7 @@ IonBuilder::createThisScriptedBaseline(MDefinition* callee) JSObject* templateObject = inspector->getTemplateObject(pc); if (!templateObject) return nullptr; - if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>()) + if (!templateObject->is<PlainObject>()) return nullptr; Shape* shape = target->lookupPure(compartment->runtime()->names().prototype); @@ -6536,7 +6540,8 @@ IonBuilder::jsop_funcall(uint32_t argc) TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet(); JSFunction* native = getSingleCallTarget(calleeTypes); if (!native || !native->isNative() || native->native() != &fun_call) { - CallInfo callInfo(alloc(), false); + CallInfo callInfo(alloc(), /* constructing = */ false, + /* ignoresReturnValue = */ BytecodeIsPopped(pc)); if (!callInfo.init(current, argc)) return false; return makeCall(native, callInfo); @@ -6561,7 +6566,8 @@ IonBuilder::jsop_funcall(uint32_t argc) argc -= 1; } - CallInfo callInfo(alloc(), false); + CallInfo callInfo(alloc(), /* constructing = */ false, + /* ignoresReturnValue = */ BytecodeIsPopped(pc)); if (!callInfo.init(current, argc)) return false; @@ -6598,7 +6604,8 @@ IonBuilder::jsop_funapply(uint32_t argc) TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet(); JSFunction* native = getSingleCallTarget(calleeTypes); if (argc != 2 || info().analysisMode() == Analysis_ArgumentsUsage) { - CallInfo callInfo(alloc(), false); + CallInfo callInfo(alloc(), /* constructing = */ false, + /* ignoresReturnValue = */ BytecodeIsPopped(pc)); if (!callInfo.init(current, argc)) return false; return makeCall(native, callInfo); @@ -6627,7 +6634,8 @@ IonBuilder::jsop_funapply(uint32_t argc) return jsop_funapplyarray(argc); } - CallInfo callInfo(alloc(), false); + CallInfo callInfo(alloc(), /* constructing = */ false, + /* ignoresReturnValue = */ BytecodeIsPopped(pc)); if (!callInfo.init(current, argc)) return false; return makeCall(native, callInfo); @@ -6736,7 +6744,8 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc) // can inline the apply() target and don't care about the actual arguments // that were passed in. - CallInfo callInfo(alloc(), false); + CallInfo callInfo(alloc(), /* constructing = */ false, + /* ignoresReturnValue = */ BytecodeIsPopped(pc)); // Vp MDefinition* vp = current->pop(); @@ -6782,7 +6791,7 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc) } bool -IonBuilder::jsop_call(uint32_t argc, bool constructing) +IonBuilder::jsop_call(uint32_t argc, bool constructing, bool ignoresReturnValue) { startTrackingOptimizations(); @@ -6808,7 +6817,7 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing) if (calleeTypes && !getPolyCallTargets(calleeTypes, constructing, targets, 4)) return false; - CallInfo callInfo(alloc(), constructing); + CallInfo callInfo(alloc(), constructing, ignoresReturnValue); if (!callInfo.init(current, argc)) return false; @@ -6944,7 +6953,8 @@ IonBuilder::makeCallHelper(JSFunction* target, CallInfo& callInfo) } MCall* call = MCall::New(alloc(), target, targetArgs + 1 + callInfo.constructing(), - callInfo.argc(), callInfo.constructing(), isDOMCall); + callInfo.argc(), callInfo.constructing(), + callInfo.ignoresReturnValue(), isDOMCall); if (!call) return nullptr; @@ -7045,7 +7055,7 @@ IonBuilder::jsop_eval(uint32_t argc) // Emit a normal call if the eval has never executed. This keeps us from // disabling compilation for the script when testing with --ion-eager. if (calleeTypes && calleeTypes->empty()) - return jsop_call(argc, /* constructing = */ false); + return jsop_call(argc, /* constructing = */ false, false); JSFunction* singleton = getSingleCallTarget(calleeTypes); if (!singleton) @@ -7061,7 +7071,8 @@ IonBuilder::jsop_eval(uint32_t argc) if (info().funMaybeLazy()->isArrow()) return abort("Direct eval from arrow function"); - CallInfo callInfo(alloc(), /* constructing = */ false); + CallInfo callInfo(alloc(), /* constructing = */ false, + /* ignoresReturnValue = */ BytecodeIsPopped(pc)); if (!callInfo.init(current, argc)) return false; callInfo.setImplicitlyUsedUnchecked(); @@ -7100,7 +7111,8 @@ IonBuilder::jsop_eval(uint32_t argc) current->push(dynamicName); current->push(constant(UndefinedValue())); // thisv - CallInfo evalCallInfo(alloc(), /* constructing = */ false); + CallInfo evalCallInfo(alloc(), /* constructing = */ false, + /* ignoresReturnValue = */ BytecodeIsPopped(pc)); if (!evalCallInfo.init(current, /* argc = */ 0)) return false; @@ -7117,7 +7129,7 @@ IonBuilder::jsop_eval(uint32_t argc) return resumeAfter(ins) && pushTypeBarrier(ins, types, BarrierKind::TypeSet); } - return jsop_call(argc, /* constructing = */ false); + return jsop_call(argc, /* constructing = */ false, false); } bool @@ -7337,12 +7349,6 @@ IonBuilder::newArrayTryTemplateObject(bool* emitted, JSObject* templateObject, u if (!templateObject) return true; - if (templateObject->is<UnboxedArrayObject>()) { - MOZ_ASSERT(templateObject->as<UnboxedArrayObject>().capacity() >= length); - if (!templateObject->as<UnboxedArrayObject>().hasInlineElements()) - return true; - } - MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT); size_t arraySlots = @@ -7598,7 +7604,6 @@ IonBuilder::jsop_initelem_array() // intializer, and that arrays are marked as non-packed when writing holes // to them during initialization. bool needStub = false; - JSValueType unboxedType = JSVAL_TYPE_MAGIC; if (shouldAbortOnPreliminaryGroups(obj)) { needStub = true; } else if (!obj->resultTypeSet() || @@ -7609,12 +7614,6 @@ IonBuilder::jsop_initelem_array() } else { MOZ_ASSERT(obj->resultTypeSet()->getObjectCount() == 1); TypeSet::ObjectKey* initializer = obj->resultTypeSet()->getObject(0); - if (initializer->clasp() == &UnboxedArrayObject::class_) { - if (initializer->group()->unboxedLayout().nativeGroup()) - needStub = true; - else - unboxedType = initializer->group()->unboxedLayout().elementType(); - } if (value->type() == MIRType::MagicHole) { if (!initializer->hasFlags(constraints(), OBJECT_FLAG_NON_PACKED)) needStub = true; @@ -7634,60 +7633,46 @@ IonBuilder::jsop_initelem_array() return resumeAfter(store); } - return initializeArrayElement(obj, index, value, unboxedType, /* addResumePoint = */ true); + return initializeArrayElement(obj, index, value, /* addResumePoint = */ true); } bool IonBuilder::initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value, - JSValueType unboxedType, bool addResumePointAndIncrementInitializedLength) { MConstant* id = MConstant::New(alloc(), Int32Value(index)); current->add(id); // Get the elements vector. - MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); + MElements* elements = MElements::New(alloc(), obj); current->add(elements); - if (unboxedType != JSVAL_TYPE_MAGIC) { - // Note: storeUnboxedValue takes care of any post barriers on the value. - storeUnboxedValue(obj, elements, 0, id, unboxedType, value, /* preBarrier = */ false); - - if (addResumePointAndIncrementInitializedLength) { - MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj); - current->add(increment); - - if (!resumeAfter(increment)) - return false; - } - } else { - if (NeedsPostBarrier(value)) - current->add(MPostWriteBarrier::New(alloc(), obj, value)); + if (NeedsPostBarrier(value)) + current->add(MPostWriteBarrier::New(alloc(), obj, value)); - if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) || - (obj->isNullarySharedStub() && - obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles)) - { - MInstruction* valueDouble = MToDouble::New(alloc(), value); - current->add(valueDouble); - value = valueDouble; - } + if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) || + (obj->isNullarySharedStub() && + obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles)) + { + MInstruction* valueDouble = MToDouble::New(alloc(), value); + current->add(valueDouble); + value = valueDouble; + } - // Store the value. - MStoreElement* store = MStoreElement::New(alloc(), elements, id, value, + // Store the value. + MStoreElement* store = MStoreElement::New(alloc(), elements, id, value, /* needsHoleCheck = */ false); - current->add(store); + current->add(store); - if (addResumePointAndIncrementInitializedLength) { - // Update the initialized length. (The template object for this - // array has the array's ultimate length, so the length field is - // already correct: no updating needed.) - MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id); - current->add(initLength); + if (addResumePointAndIncrementInitializedLength) { + // Update the initialized length. (The template object for this + // array has the array's ultimate length, so the length field is + // already correct: no updating needed.) + MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id); + current->add(initLength); - if (!resumeAfter(initLength)) - return false; - } + if (!resumeAfter(initLength)) + return false; } return true; @@ -7718,8 +7703,6 @@ IonBuilder::jsop_initprop(PropertyName* name) if (templateObject->is<PlainObject>()) { if (!templateObject->as<PlainObject>().containsPure(name)) useSlowPath = true; - } else { - MOZ_ASSERT(templateObject->as<UnboxedPlainObject>().layout().lookup(name)); } } else { useSlowPath = true; @@ -8178,9 +8161,7 @@ IonBuilder::maybeMarkEmpty(MDefinition* ins) static bool ClassHasEffectlessLookup(const Class* clasp) { - return (clasp == &UnboxedPlainObject::class_) || - (clasp == &UnboxedArrayObject::class_) || - IsTypedObjectClass(clasp) || + return IsTypedObjectClass(clasp) || (clasp->isNative() && !clasp->getOpsLookupProperty()); } @@ -8914,10 +8895,8 @@ IonBuilder::jsop_getimport(PropertyName* name) if (!emitted) { // This can happen if we don't have type information. - TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(targetEnv); TemporaryTypeSet* types = bytecodeTypes(pc); - BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticKey, - name, types, /* updateObserved = */ true); + BarrierKind barrier = BarrierKind::TypeSet; if (!loadStaticSlot(targetEnv, barrier, types, shape->slot())) return false; @@ -9012,8 +8991,6 @@ IonBuilder::jsop_getelem() } obj = maybeUnboxForPropertyAccess(obj); - if (obj->type() == MIRType::Object) - obj = convertUnboxedObjects(obj); bool emitted = false; @@ -9458,12 +9435,9 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index) { MOZ_ASSERT(*emitted == false); - JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, index); - if (unboxedType == JSVAL_TYPE_MAGIC) { - if (!ElementAccessIsDenseNative(constraints(), obj, index)) { - trackOptimizationOutcome(TrackedOutcome::AccessNotDense); - return true; - } + if (!ElementAccessIsDenseNative(constraints(), obj, index)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotDense); + return true; } // Don't generate a fast path if there have been bounds check failures @@ -9480,7 +9454,7 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index) return true; } - if (!jsop_getelem_dense(obj, index, unboxedType)) + if (!jsop_getelem_dense(obj, index)) return false; trackOptimizationSuccess(); @@ -9832,7 +9806,7 @@ IonBuilder::computeHeapType(const TemporaryTypeSet* objTypes, const jsid id) } bool -IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType) +IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index) { TemporaryTypeSet* types = bytecodeTypes(pc); @@ -9856,7 +9830,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType !ElementAccessHasExtraIndexedProperty(this, obj); MIRType knownType = MIRType::Value; - if (unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier) + if (barrier == BarrierKind::NoBarrier) knownType = GetElemKnownType(needsHoleCheck, types); // Ensure index is an integer. @@ -9865,13 +9839,13 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType index = idInt32; // Get the elements vector. - MInstruction* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); + MInstruction* elements = MElements::New(alloc(), obj); current->add(elements); // Note: to help GVN, use the original MElements instruction and not // MConvertElementsToDoubles as operand. This is fine because converting // elements to double does not change the initialized length. - MInstruction* initLength = initializedLength(obj, elements, unboxedType); + MInstruction* initLength = initializedLength(obj, elements); // If we can load the element as a definite double, make sure to check that // the array has been converted to homogenous doubles first. @@ -9887,7 +9861,6 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType } bool loadDouble = - unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier && loopDepth_ && inBounds && @@ -9906,18 +9879,13 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType // hoisting. index = addBoundsCheck(index, initLength); - if (unboxedType != JSVAL_TYPE_MAGIC) { - load = loadUnboxedValue(elements, 0, index, unboxedType, barrier, types); - } else { - load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble); - current->add(load); - } + load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble); + current->add(load); } else { // This load may return undefined, so assume that we *can* read holes, // or that we can read out-of-bounds accesses. In this case, the bounds // check is part of the opcode. - load = MLoadElementHole::New(alloc(), elements, index, initLength, - unboxedType, needsHoleCheck); + load = MLoadElementHole::New(alloc(), elements, index, initLength, needsHoleCheck); current->add(load); // If maybeUndefined was true, the typeset must have undefined, and @@ -9927,8 +9895,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType } if (knownType != MIRType::Value) { - if (unboxedType == JSVAL_TYPE_MAGIC) - load->setResultType(knownType); + load->setResultType(knownType); load->setResultTypeSet(types); } @@ -10137,7 +10104,7 @@ IonBuilder::jsop_setelem() MDefinition* value = current->pop(); MDefinition* index = current->pop(); - MDefinition* object = convertUnboxedObjects(current->pop()); + MDefinition* object = current->pop(); trackTypeInfo(TrackedTypeSite::Receiver, object->type(), object->resultTypeSet()); trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet()); @@ -10375,12 +10342,9 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object, { MOZ_ASSERT(*emitted == false); - JSValueType unboxedType = UnboxedArrayElementType(constraints(), object, index); - if (unboxedType == JSVAL_TYPE_MAGIC) { - if (!ElementAccessIsDenseNative(constraints(), object, index)) { - trackOptimizationOutcome(TrackedOutcome::AccessNotDense); - return true; - } + if (!ElementAccessIsDenseNative(constraints(), object, index)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotDense); + return true; } if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, @@ -10414,7 +10378,7 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object, } // Emit dense setelem variant. - if (!jsop_setelem_dense(conversion, object, index, value, unboxedType, writeHole, emitted)) + if (!jsop_setelem_dense(conversion, object, index, value, writeHole, emitted)) return false; if (!*emitted) { @@ -10504,13 +10468,11 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object, bool IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, MDefinition* obj, MDefinition* id, MDefinition* value, - JSValueType unboxedType, bool writeHole, bool* emitted) + bool writeHole, bool* emitted) { MOZ_ASSERT(*emitted == false); - MIRType elementType = MIRType::None; - if (unboxedType == JSVAL_TYPE_MAGIC) - elementType = DenseNativeElementType(constraints(), obj); + MIRType elementType = DenseNativeElementType(constraints(), obj); bool packed = ElementAccessIsPacked(constraints(), obj); // Writes which are on holes in the object do not have to bail out if they @@ -10540,7 +10502,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false); // Get the elements vector. - MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); + MElements* elements = MElements::New(alloc(), obj); current->add(elements); // Ensure the value is a double, if double conversion might be needed. @@ -10577,7 +10539,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, MInstruction* store; MStoreElementCommon* common = nullptr; if (writeHole && hasNoExtraIndexedProperty && !mayBeFrozen) { - MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType); + MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue); store = ins; common = ins; @@ -10589,27 +10551,23 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, bool strict = IsStrictSetPC(pc); MFallibleStoreElement* ins = MFallibleStoreElement::New(alloc(), obj, elements, id, - newValue, unboxedType, strict); + newValue, strict); store = ins; common = ins; current->add(ins); current->push(value); } else { - MInstruction* initLength = initializedLength(obj, elements, unboxedType); + MInstruction* initLength = initializedLength(obj, elements); id = addBoundsCheck(id, initLength); bool needsHoleCheck = !packed && !hasNoExtraIndexedProperty; - if (unboxedType != JSVAL_TYPE_MAGIC) { - store = storeUnboxedValue(obj, elements, 0, id, unboxedType, newValue); - } else { - MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck); - store = ins; - common = ins; + MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck); + store = ins; + common = ins; - current->add(store); - } + current->add(store); current->push(value); } @@ -10727,18 +10685,6 @@ IonBuilder::jsop_length_fastPath() return true; } - // Compute the length for unboxed array objects. - if (UnboxedArrayElementType(constraints(), obj, nullptr) != JSVAL_TYPE_MAGIC && - !objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW)) - { - current->pop(); - - MUnboxedArrayLength* length = MUnboxedArrayLength::New(alloc(), obj); - current->add(length); - current->push(length); - return true; - } - // Compute the length for array typed objects. TypedObjectPrediction prediction = typedObjectPrediction(obj); if (!prediction.isUseless()) { @@ -10972,11 +10918,8 @@ IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_ } // Definite slots will always be fixed slots when they are in the - // allowable range for fixed slots, except for objects which were - // converted from unboxed objects and have a smaller allocation size. + // allowable range for fixed slots. size_t nfixed = NativeObject::MAX_FIXED_SLOTS; - if (ObjectGroup* group = key->group()->maybeOriginalUnboxedGroup()) - nfixed = gc::GetGCKindSlots(group->unboxedLayout().getAllocKind()); uint32_t propertySlot = property.maybeTypes()->definiteSlot(); if (slot == UINT32_MAX) { @@ -10991,65 +10934,6 @@ IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_ return slot; } -uint32_t -IonBuilder::getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name, JSValueType* punboxedType) -{ - if (!types || types->unknownObject() || !types->objectOrSentinel()) { - trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); - return UINT32_MAX; - } - - uint32_t offset = UINT32_MAX; - - for (size_t i = 0; i < types->getObjectCount(); i++) { - TypeSet::ObjectKey* key = types->getObject(i); - if (!key) - continue; - - if (key->unknownProperties()) { - trackOptimizationOutcome(TrackedOutcome::UnknownProperties); - return UINT32_MAX; - } - - if (key->isSingleton()) { - trackOptimizationOutcome(TrackedOutcome::Singleton); - return UINT32_MAX; - } - - UnboxedLayout* layout = key->group()->maybeUnboxedLayout(); - if (!layout) { - trackOptimizationOutcome(TrackedOutcome::NotUnboxed); - return UINT32_MAX; - } - - const UnboxedLayout::Property* property = layout->lookup(name); - if (!property) { - trackOptimizationOutcome(TrackedOutcome::StructNoField); - return UINT32_MAX; - } - - if (layout->nativeGroup()) { - trackOptimizationOutcome(TrackedOutcome::UnboxedConvertedToNative); - return UINT32_MAX; - } - - key->watchStateChangeForUnboxedConvertedToNative(constraints()); - - if (offset == UINT32_MAX) { - offset = property->offset; - *punboxedType = property->type; - } else if (offset != property->offset) { - trackOptimizationOutcome(TrackedOutcome::InconsistentFieldOffset); - return UINT32_MAX; - } else if (*punboxedType != property->type) { - trackOptimizationOutcome(TrackedOutcome::InconsistentFieldType); - return UINT32_MAX; - } - } - - return offset; -} - bool IonBuilder::jsop_runonce() { @@ -11496,8 +11380,6 @@ IonBuilder::jsop_getprop(PropertyName* name) } obj = maybeUnboxForPropertyAccess(obj); - if (obj->type() == MIRType::Object) - obj = convertUnboxedObjects(obj); BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj, name, types); @@ -11569,11 +11451,6 @@ IonBuilder::jsop_getprop(PropertyName* name) if (!getPropTryDefiniteSlot(&emitted, obj, name, barrier, types) || emitted) return emitted; - // Try to emit loads from unboxed objects. - trackOptimizationAttempt(TrackedStrategy::GetProp_Unboxed); - if (!getPropTryUnboxed(&emitted, obj, name, barrier, types) || emitted) - return emitted; - // Try to inline a common property getter, or make a call. trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter); if (!getPropTryCommonGetter(&emitted, obj, name, types) || emitted) @@ -11939,49 +11816,6 @@ IonBuilder::getPropTryComplexPropOfTypedObject(bool* emitted, fieldPrediction, fieldTypeObj); } -MDefinition* -IonBuilder::convertUnboxedObjects(MDefinition* obj) -{ - // If obj might be in any particular unboxed group which should be - // converted to a native representation, perform that conversion. This does - // not guarantee the object will not have such a group afterwards, if the - // object's possible groups are not precisely known. - TemporaryTypeSet* types = obj->resultTypeSet(); - if (!types || types->unknownObject() || !types->objectOrSentinel()) - return obj; - - BaselineInspector::ObjectGroupVector list(alloc()); - for (size_t i = 0; i < types->getObjectCount(); i++) { - TypeSet::ObjectKey* key = obj->resultTypeSet()->getObject(i); - if (!key || !key->isGroup()) - continue; - - if (UnboxedLayout* layout = key->group()->maybeUnboxedLayout()) { - AutoEnterOOMUnsafeRegion oomUnsafe; - if (layout->nativeGroup() && !list.append(key->group())) - oomUnsafe.crash("IonBuilder::convertUnboxedObjects"); - } - } - - return convertUnboxedObjects(obj, list); -} - -MDefinition* -IonBuilder::convertUnboxedObjects(MDefinition* obj, - const BaselineInspector::ObjectGroupVector& list) -{ - for (size_t i = 0; i < list.length(); i++) { - ObjectGroup* group = list[i]; - if (TemporaryTypeSet* types = obj->resultTypeSet()) { - if (!types->hasType(TypeSet::ObjectType(group))) - continue; - } - obj = MConvertUnboxedObjectToNative::New(alloc(), obj, group); - current->add(obj->toInstruction()); - } - return obj; -} - bool IonBuilder::getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name, BarrierKind barrier, TemporaryTypeSet* types) @@ -12066,111 +11900,14 @@ IonBuilder::getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyN return true; } -MInstruction* -IonBuilder::loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, - BarrierKind barrier, TemporaryTypeSet* types) -{ - // loadUnboxedValue is designed to load any value as if it were contained in - // an array. Thus a property offset is converted to an index, when the - // object is reinterpreted as an array of properties of the same size. - size_t index = offset / UnboxedTypeSize(unboxedType); - MInstruction* indexConstant = MConstant::New(alloc(), Int32Value(index)); - current->add(indexConstant); - - return loadUnboxedValue(obj, UnboxedPlainObject::offsetOfData(), - indexConstant, unboxedType, barrier, types); -} - -MInstruction* -IonBuilder::loadUnboxedValue(MDefinition* elements, size_t elementsOffset, - MDefinition* index, JSValueType unboxedType, - BarrierKind barrier, TemporaryTypeSet* types) -{ - MInstruction* load; - switch (unboxedType) { - case JSVAL_TYPE_BOOLEAN: - load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Uint8, - DoesNotRequireMemoryBarrier, elementsOffset); - load->setResultType(MIRType::Boolean); - break; - - case JSVAL_TYPE_INT32: - load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Int32, - DoesNotRequireMemoryBarrier, elementsOffset); - load->setResultType(MIRType::Int32); - break; - - case JSVAL_TYPE_DOUBLE: - load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Float64, - DoesNotRequireMemoryBarrier, elementsOffset, - /* canonicalizeDoubles = */ false); - load->setResultType(MIRType::Double); - break; - - case JSVAL_TYPE_STRING: - load = MLoadUnboxedString::New(alloc(), elements, index, elementsOffset); - break; - - case JSVAL_TYPE_OBJECT: { - MLoadUnboxedObjectOrNull::NullBehavior nullBehavior; - if (types->hasType(TypeSet::NullType())) - nullBehavior = MLoadUnboxedObjectOrNull::HandleNull; - else if (barrier != BarrierKind::NoBarrier) - nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull; - else - nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible; - load = MLoadUnboxedObjectOrNull::New(alloc(), elements, index, nullBehavior, - elementsOffset); - break; - } - - default: - MOZ_CRASH(); - } - - current->add(load); - return load; -} - -bool -IonBuilder::getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name, - BarrierKind barrier, TemporaryTypeSet* types) -{ - MOZ_ASSERT(*emitted == false); - - JSValueType unboxedType; - uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType); - if (offset == UINT32_MAX) - return true; - - if (obj->type() != MIRType::Object) { - MGuardObject* guard = MGuardObject::New(alloc(), obj); - current->add(guard); - obj = guard; - } - - MInstruction* load = loadUnboxedProperty(obj, offset, unboxedType, barrier, types); - current->push(load); - - if (!pushTypeBarrier(load, types, barrier)) - return false; - - trackOptimizationSuccess(); - *emitted = true; - return true; -} - MDefinition* IonBuilder::addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape, const BaselineInspector::ReceiverVector& receivers, - const BaselineInspector::ObjectGroupVector& convertUnboxedGroups, bool isOwnProperty) { MOZ_ASSERT(holder); MOZ_ASSERT(holderShape); - obj = convertUnboxedObjects(obj, convertUnboxedGroups); - if (isOwnProperty) { MOZ_ASSERT(receivers.empty()); return addShapeGuard(obj, holderShape, Bailout_ShapeGuard); @@ -12194,10 +11931,8 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName JSObject* foundProto = nullptr; bool isOwnProperty = false; BaselineInspector::ReceiverVector receivers(alloc()); - BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); if (!inspector->commonGetPropFunction(pc, &foundProto, &lastProperty, &commonGetter, - &globalShape, &isOwnProperty, - receivers, convertUnboxedGroups)) + &globalShape, &isOwnProperty, receivers)) { return true; } @@ -12213,8 +11948,7 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName // If type information is bad, we can still optimize the getter if we // shape guard. obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty, - receivers, convertUnboxedGroups, - isOwnProperty); + receivers, isOwnProperty); if (!obj) return false; } @@ -12275,7 +12009,8 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName current->push(obj); - CallInfo callInfo(alloc(), false); + CallInfo callInfo(alloc(), /* constructing = */ false, + /* ignoresReturnValue = */ BytecodeIsPopped(pc)); if (!callInfo.init(current, 0)) return false; @@ -12381,15 +12116,12 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName MOZ_ASSERT(*emitted == false); BaselineInspector::ReceiverVector receivers(alloc()); - BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); - if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups)) + if (!inspector->maybeInfoForPropertyOp(pc, receivers)) return false; if (!canInlinePropertyOpShapes(receivers)) return true; - obj = convertUnboxedObjects(obj, convertUnboxedGroups); - MIRType rvalType = types->getKnownMIRType(); if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType)) rvalType = MIRType::Value; @@ -12412,45 +12144,6 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName return true; } - if (receivers[0].shape) { - // Monomorphic load from an unboxed object expando. - spew("Inlining monomorphic unboxed expando GETPROP"); - - obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard); - obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard); - - MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj); - current->add(expando); - - expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard); - - Shape* shape = receivers[0].shape->searchLinear(NameToId(name)); - MOZ_ASSERT(shape); - - if (!loadSlot(expando, shape, rvalType, barrier, types)) - return false; - - trackOptimizationOutcome(TrackedOutcome::Monomorphic); - *emitted = true; - return true; - } - - // Monomorphic load from an unboxed object. - ObjectGroup* group = receivers[0].group; - if (obj->resultTypeSet() && !obj->resultTypeSet()->hasType(TypeSet::ObjectType(group))) - return true; - - obj = addGroupGuard(obj, group, Bailout_ShapeGuard); - - const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name); - MInstruction* load = loadUnboxedProperty(obj, property->offset, property->type, barrier, types); - current->push(load); - - if (!pushTypeBarrier(load, types, barrier)) - return false; - - trackOptimizationOutcome(TrackedOutcome::Monomorphic); - *emitted = true; return true; } @@ -12692,7 +12385,7 @@ bool IonBuilder::jsop_setprop(PropertyName* name) { MDefinition* value = current->pop(); - MDefinition* obj = convertUnboxedObjects(current->pop()); + MDefinition* obj = current->pop(); bool emitted = false; startTrackingOptimizations(); @@ -12725,13 +12418,6 @@ IonBuilder::jsop_setprop(PropertyName* name) bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value, /* canModify = */ true); - if (!forceInlineCaches()) { - // Try to emit stores to unboxed objects. - trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed); - if (!setPropTryUnboxed(&emitted, obj, name, value, barrier, objTypes) || emitted) - return emitted; - } - // Add post barrier if needed. The instructions above manage any post // barriers they need directly. if (NeedsPostBarrier(value)) @@ -12765,10 +12451,8 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj, JSObject* foundProto = nullptr; bool isOwnProperty; BaselineInspector::ReceiverVector receivers(alloc()); - BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); if (!inspector->commonSetPropFunction(pc, &foundProto, &lastProperty, &commonSetter, - &isOwnProperty, - receivers, convertUnboxedGroups)) + &isOwnProperty, receivers)) { trackOptimizationOutcome(TrackedOutcome::NoProtoFound); return true; @@ -12783,8 +12467,7 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj, // If type information is bad, we can still optimize the setter if we // shape guard. obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty, - receivers, convertUnboxedGroups, - isOwnProperty); + receivers, isOwnProperty); if (!obj) return false; } @@ -12822,7 +12505,8 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj, // Call the setter. Note that we have to push the original value, not // the setter's return value. - CallInfo callInfo(alloc(), false); + CallInfo callInfo(alloc(), /* constructing = */ false, + /* ignoresReturnValue = */ BytecodeIsPopped(pc)); if (!callInfo.init(current, 1)) return false; @@ -13039,100 +12723,6 @@ IonBuilder::setPropTryDefiniteSlot(bool* emitted, MDefinition* obj, return true; } -MInstruction* -IonBuilder::storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, - MDefinition* value) -{ - size_t scaledOffsetConstant = offset / UnboxedTypeSize(unboxedType); - MInstruction* scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant)); - current->add(scaledOffset); - - return storeUnboxedValue(obj, obj, UnboxedPlainObject::offsetOfData(), - scaledOffset, unboxedType, value); -} - -MInstruction* -IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t elementsOffset, - MDefinition* scaledOffset, JSValueType unboxedType, - MDefinition* value, bool preBarrier /* = true */) -{ - MInstruction* store; - switch (unboxedType) { - case JSVAL_TYPE_BOOLEAN: - store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Uint8, - MStoreUnboxedScalar::DontTruncateInput, - DoesNotRequireMemoryBarrier, elementsOffset); - break; - - case JSVAL_TYPE_INT32: - store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Int32, - MStoreUnboxedScalar::DontTruncateInput, - DoesNotRequireMemoryBarrier, elementsOffset); - break; - - case JSVAL_TYPE_DOUBLE: - store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Float64, - MStoreUnboxedScalar::DontTruncateInput, - DoesNotRequireMemoryBarrier, elementsOffset); - break; - - case JSVAL_TYPE_STRING: - store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, - elementsOffset, preBarrier); - break; - - case JSVAL_TYPE_OBJECT: - MOZ_ASSERT(value->type() == MIRType::Object || - value->type() == MIRType::Null || - value->type() == MIRType::Value); - MOZ_ASSERT(!value->mightBeType(MIRType::Undefined), - "MToObjectOrNull slow path is invalid for unboxed objects"); - store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, obj, - elementsOffset, preBarrier); - break; - - default: - MOZ_CRASH(); - } - - current->add(store); - return store; -} - -bool -IonBuilder::setPropTryUnboxed(bool* emitted, MDefinition* obj, - PropertyName* name, MDefinition* value, - bool barrier, TemporaryTypeSet* objTypes) -{ - MOZ_ASSERT(*emitted == false); - - if (barrier) { - trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); - return true; - } - - JSValueType unboxedType; - uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType); - if (offset == UINT32_MAX) - return true; - - if (obj->type() != MIRType::Object) { - MGuardObject* guard = MGuardObject::New(alloc(), obj); - current->add(guard); - obj = guard; - } - - MInstruction* store = storeUnboxedProperty(obj, offset, unboxedType, value); - - current->push(value); - - if (!resumeAfter(store)) - return false; - - *emitted = true; - return true; -} - bool IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name, MDefinition* value, @@ -13146,15 +12736,12 @@ IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj, } BaselineInspector::ReceiverVector receivers(alloc()); - BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); - if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups)) + if (!inspector->maybeInfoForPropertyOp(pc, receivers)) return false; if (!canInlinePropertyOpShapes(receivers)) return true; - obj = convertUnboxedObjects(obj, convertUnboxedGroups); - if (receivers.length() == 1) { if (!receivers[0].group) { // Monomorphic store to a native object. @@ -13174,46 +12761,6 @@ IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj, return true; } - if (receivers[0].shape) { - // Monomorphic store to an unboxed object expando. - spew("Inlining monomorphic unboxed expando SETPROP"); - - obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard); - obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard); - - MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj); - current->add(expando); - - expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard); - - Shape* shape = receivers[0].shape->searchLinear(NameToId(name)); - MOZ_ASSERT(shape); - - bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name)); - if (!storeSlot(expando, shape, value, needsBarrier)) - return false; - - trackOptimizationOutcome(TrackedOutcome::Monomorphic); - *emitted = true; - return true; - } - - // Monomorphic store to an unboxed object. - spew("Inlining monomorphic unboxed SETPROP"); - - ObjectGroup* group = receivers[0].group; - if (!objTypes->hasType(TypeSet::ObjectType(group))) - return true; - - obj = addGroupGuard(obj, group, Bailout_ShapeGuard); - - const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name); - storeUnboxedProperty(obj, property->offset, property->type, value); - - current->push(value); - - trackOptimizationOutcome(TrackedOutcome::Monomorphic); - *emitted = true; return true; } @@ -13884,7 +13431,7 @@ IonBuilder::jsop_setaliasedvar(EnvironmentCoordinate ec) bool IonBuilder::jsop_in() { - MDefinition* obj = convertUnboxedObjects(current->pop()); + MDefinition* obj = current->pop(); MDefinition* id = current->pop(); bool emitted = false; @@ -13911,11 +13458,8 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id) if (shouldAbortOnPreliminaryGroups(obj)) return true; - JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, id); - if (unboxedType == JSVAL_TYPE_MAGIC) { - if (!ElementAccessIsDenseNative(constraints(), obj, id)) - return true; - } + if (!ElementAccessIsDenseNative(constraints(), obj, id)) + return true; if (ElementAccessHasExtraIndexedProperty(this, obj)) return true; @@ -13930,10 +13474,10 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id) id = idInt32; // Get the elements vector. - MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); + MElements* elements = MElements::New(alloc(), obj); current->add(elements); - MInstruction* initLength = initializedLength(obj, elements, unboxedType); + MInstruction* initLength = initializedLength(obj, elements); // If there are no holes, speculate the InArray check will not fail. if (!needsHoleCheck && !failedBoundsCheck_) { @@ -13943,8 +13487,7 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id) } // Check if id < initLength and elem[id] not a hole. - MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck, - unboxedType); + MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck); current->add(ins); current->push(ins); @@ -14241,19 +13784,6 @@ IonBuilder::addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bail } MInstruction* -IonBuilder::addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind) -{ - MGuardUnboxedExpando* guard = MGuardUnboxedExpando::New(alloc(), obj, hasExpando, bailoutKind); - current->add(guard); - - // If a shape guard failed in the past, don't optimize group guards. - if (failedShapeGuard_) - guard->setNotMovable(); - - return guard; -} - -MInstruction* IonBuilder::addGuardReceiverPolymorphic(MDefinition* obj, const BaselineInspector::ReceiverVector& receivers) { @@ -14262,15 +13792,6 @@ IonBuilder::addGuardReceiverPolymorphic(MDefinition* obj, // Monomorphic guard on a native object. return addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard); } - - if (!receivers[0].shape) { - // Guard on an unboxed object that does not have an expando. - obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard); - return addUnboxedExpandoGuard(obj, /* hasExpando = */ false, Bailout_ShapeGuard); - } - - // Monomorphic receiver guards are not yet supported when the receiver - // is an unboxed object with an expando. } MGuardReceiverPolymorphic* guard = MGuardReceiverPolymorphic::New(alloc(), obj); @@ -14644,32 +14165,24 @@ IonBuilder::constantInt(int32_t i) } MInstruction* -IonBuilder::initializedLength(MDefinition* obj, MDefinition* elements, JSValueType unboxedType) +IonBuilder::initializedLength(MDefinition* obj, MDefinition* elements) { - MInstruction* res; - if (unboxedType != JSVAL_TYPE_MAGIC) - res = MUnboxedArrayInitializedLength::New(alloc(), obj); - else - res = MInitializedLength::New(alloc(), elements); + MInstruction* res = MInitializedLength::New(alloc(), elements); current->add(res); return res; } MInstruction* -IonBuilder::setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count) +IonBuilder::setInitializedLength(MDefinition* obj, size_t count) { MOZ_ASSERT(count); - MInstruction* res; - if (unboxedType != JSVAL_TYPE_MAGIC) { - res = MSetUnboxedArrayInitializedLength::New(alloc(), obj, constant(Int32Value(count))); - } else { - // MSetInitializedLength takes the index of the last element, rather - // than the count itself. - MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false); - current->add(elements); - res = MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1))); - } + // MSetInitializedLength takes the index of the last element, rather + // than the count itself. + MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false); + current->add(elements); + MInstruction* res = + MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1))); current->add(res); return res; } diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index f24ef30c84..1f763a4f2c 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -346,9 +346,8 @@ class IonBuilder MConstant* constant(const Value& v); MConstant* constantInt(int32_t i); - MInstruction* initializedLength(MDefinition* obj, MDefinition* elements, - JSValueType unboxedType); - MInstruction* setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count); + MInstruction* initializedLength(MDefinition* obj, MDefinition* elements); + MInstruction* setInitializedLength(MDefinition* obj, size_t count); // Improve the type information at tests MOZ_MUST_USE bool improveTypesAtTest(MDefinition* ins, bool trueBranch, MTest* test); @@ -401,7 +400,6 @@ class IonBuilder MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length); MInstruction* addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind); MInstruction* addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind); - MInstruction* addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind); MInstruction* addSharedTypedArrayGuard(MDefinition* obj); MInstruction* @@ -441,8 +439,6 @@ class IonBuilder BarrierKind barrier, TemporaryTypeSet* types); MOZ_MUST_USE bool getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyName* name, BarrierKind barrier, TemporaryTypeSet* types); - MOZ_MUST_USE bool getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name, - BarrierKind barrier, TemporaryTypeSet* types); MOZ_MUST_USE bool getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name, TemporaryTypeSet* types); MOZ_MUST_USE bool getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name, @@ -475,9 +471,6 @@ class IonBuilder MOZ_MUST_USE bool setPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name, MDefinition* value, bool barrier, TemporaryTypeSet* objTypes); - MOZ_MUST_USE bool setPropTryUnboxed(bool* emitted, MDefinition* obj, - PropertyName* name, MDefinition* value, - bool barrier, TemporaryTypeSet* objTypes); MOZ_MUST_USE bool setPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name, MDefinition* value, bool barrier, TemporaryTypeSet* objTypes); @@ -617,7 +610,6 @@ class IonBuilder TypedObjectPrediction elemTypeReprs, uint32_t elemSize); MOZ_MUST_USE bool initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value, - JSValueType unboxedType, bool addResumePointAndIncrementInitializedLength); // jsop_getelem() helpers. @@ -707,6 +699,7 @@ class IonBuilder MOZ_MUST_USE bool jsop_funapplyarguments(uint32_t argc); MOZ_MUST_USE bool jsop_funapplyarray(uint32_t argc); MOZ_MUST_USE bool jsop_call(uint32_t argc, bool constructing); + MOZ_MUST_USE bool jsop_call(uint32_t argc, bool constructing, bool ignoresReturnValue); MOZ_MUST_USE bool jsop_eval(uint32_t argc); MOZ_MUST_USE bool jsop_ifeq(JSOp op); MOZ_MUST_USE bool jsop_try(); @@ -729,15 +722,13 @@ class IonBuilder MOZ_MUST_USE bool jsop_bindname(PropertyName* name); MOZ_MUST_USE bool jsop_bindvar(); MOZ_MUST_USE bool jsop_getelem(); - MOZ_MUST_USE bool jsop_getelem_dense(MDefinition* obj, MDefinition* index, - JSValueType unboxedType); + MOZ_MUST_USE bool jsop_getelem_dense(MDefinition* obj, MDefinition* index); MOZ_MUST_USE bool jsop_getelem_typed(MDefinition* obj, MDefinition* index, ScalarTypeDescr::Type arrayType); MOZ_MUST_USE bool jsop_setelem(); MOZ_MUST_USE bool jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, MDefinition* object, MDefinition* index, - MDefinition* value, JSValueType unboxedType, - bool writeHole, bool* emitted); + MDefinition* value, bool writeHole, bool* emitted); MOZ_MUST_USE bool jsop_setelem_typed(ScalarTypeDescr::Type arrayType, MDefinition* object, MDefinition* index, MDefinition* value); @@ -830,7 +821,6 @@ class IonBuilder InliningStatus inlineArrayPush(CallInfo& callInfo); InliningStatus inlineArraySlice(CallInfo& callInfo); InliningStatus inlineArrayJoin(CallInfo& callInfo); - InliningStatus inlineArraySplice(CallInfo& callInfo); // Math natives. InliningStatus inlineMathAbs(CallInfo& callInfo); @@ -1041,7 +1031,6 @@ class IonBuilder MDefinition* addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape, const BaselineInspector::ReceiverVector& receivers, - const BaselineInspector::ObjectGroupVector& convertUnboxedGroups, bool isOwnProperty); MOZ_MUST_USE bool annotateGetPropertyCache(MDefinition* obj, PropertyName* name, @@ -1059,22 +1048,6 @@ class IonBuilder ResultWithOOM<bool> testNotDefinedProperty(MDefinition* obj, jsid id); uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed); - MDefinition* convertUnboxedObjects(MDefinition* obj); - MDefinition* convertUnboxedObjects(MDefinition* obj, - const BaselineInspector::ObjectGroupVector& list); - uint32_t getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name, - JSValueType* punboxedType); - MInstruction* loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, - BarrierKind barrier, TemporaryTypeSet* types); - MInstruction* loadUnboxedValue(MDefinition* elements, size_t elementsOffset, - MDefinition* scaledOffset, JSValueType unboxedType, - BarrierKind barrier, TemporaryTypeSet* types); - MInstruction* storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, - MDefinition* value); - MInstruction* storeUnboxedValue(MDefinition* obj, - MDefinition* elements, int32_t elementsOffset, - MDefinition* scaledOffset, JSValueType unboxedType, - MDefinition* value, bool preBarrier = true); MOZ_MUST_USE bool checkPreliminaryGroups(MDefinition *obj); MOZ_MUST_USE bool freezePropTypeSets(TemporaryTypeSet* types, JSObject* foundProto, PropertyName* name); @@ -1379,16 +1352,21 @@ class CallInfo MDefinition* newTargetArg_; MDefinitionVector args_; - bool constructing_; - bool setter_; + bool constructing_:1; + + // True if the caller does not use the return value. + bool ignoresReturnValue_:1; + + bool setter_:1; public: - CallInfo(TempAllocator& alloc, bool constructing) + CallInfo(TempAllocator& alloc, bool constructing, bool ignoresReturnValue) : fun_(nullptr), thisArg_(nullptr), newTargetArg_(nullptr), args_(alloc), constructing_(constructing), + ignoresReturnValue_(ignoresReturnValue), setter_(false) { } @@ -1397,6 +1375,7 @@ class CallInfo fun_ = callInfo.fun(); thisArg_ = callInfo.thisArg(); + ignoresReturnValue_ = callInfo.ignoresReturnValue(); if (constructing()) newTargetArg_ = callInfo.getNewTarget(); @@ -1493,6 +1472,10 @@ class CallInfo return constructing_; } + bool ignoresReturnValue() const { + return ignoresReturnValue_; + } + void setNewTarget(MDefinition* newTarget) { MOZ_ASSERT(constructing()); newTargetArg_ = newTarget; diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index 96e488ea8c..fb42911886 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -619,29 +619,7 @@ TestMatchingReceiver(MacroAssembler& masm, IonCache::StubAttacher& attacher, Register object, JSObject* obj, Label* failure, bool alwaysCheckGroup = false) { - if (obj->is<UnboxedPlainObject>()) { - MOZ_ASSERT(failure); - - masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure); - Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); - if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) { - masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure); - Label success; - masm.push(object); - masm.loadPtr(expandoAddress, object); - masm.branchTestObjShape(Assembler::Equal, object, expando->lastProperty(), - &success); - masm.pop(object); - masm.jump(failure); - masm.bind(&success); - masm.pop(object); - } else { - masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure); - } - } else if (obj->is<UnboxedArrayObject>()) { - MOZ_ASSERT(failure); - masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure); - } else if (obj->is<TypedObject>()) { + if (obj->is<TypedObject>()) { attacher.branchNextStubOrLabel(masm, Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), ImmGCPtr(obj->group()), failure); @@ -758,7 +736,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm, // jump directly. Otherwise, jump to the end of the stub, so there's a // common point to patch. bool multipleFailureJumps = (obj != holder) - || obj->is<UnboxedPlainObject>() || (checkTDZ && output.hasValue()) || (failures != nullptr && failures->used()); @@ -777,7 +754,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm, Register scratchReg = Register::FromCode(0); // Quell compiler warning. if (obj != holder || - obj->is<UnboxedPlainObject>() || !holder->as<NativeObject>().isFixedSlot(shape->slot())) { if (output.hasValue()) { @@ -838,10 +814,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm, holderReg = InvalidReg; } - } else if (obj->is<UnboxedPlainObject>()) { - holder = obj->as<UnboxedPlainObject>().maybeExpando(); - holderReg = scratchReg; - masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), holderReg); } else { holderReg = object; } @@ -869,30 +841,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm, attacher.jumpNextStub(masm); } -static void -GenerateReadUnboxed(JSContext* cx, IonScript* ion, MacroAssembler& masm, - IonCache::StubAttacher& attacher, JSObject* obj, - const UnboxedLayout::Property* property, - Register object, TypedOrValueRegister output, - Label* failures = nullptr) -{ - // Guard on the group of the object. - attacher.branchNextStubOrLabel(masm, Assembler::NotEqual, - Address(object, JSObject::offsetOfGroup()), - ImmGCPtr(obj->group()), failures); - - Address address(object, UnboxedPlainObject::offsetOfData() + property->offset); - - masm.loadUnboxedProperty(address, property->type, output); - - attacher.jumpRejoin(masm); - - if (failures) { - masm.bind(failures); - attacher.jumpNextStub(masm); - } -} - static bool EmitGetterCall(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher, JSObject* obj, @@ -1187,39 +1135,6 @@ GenerateArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& return true; } -static void -GenerateUnboxedArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher, - JSObject* array, Register object, TypedOrValueRegister output, - Label* failures) -{ - Register outReg; - if (output.hasValue()) { - outReg = output.valueReg().scratchReg(); - } else { - MOZ_ASSERT(output.type() == MIRType::Int32); - outReg = output.typedReg().gpr(); - } - MOZ_ASSERT(object != outReg); - - TestMatchingReceiver(masm, attacher, object, array, failures); - - // Load length. - masm.load32(Address(object, UnboxedArrayObject::offsetOfLength()), outReg); - - // Check for a length that fits in an int32. - masm.branchTest32(Assembler::Signed, outReg, outReg, failures); - - if (output.hasValue()) - masm.tagValue(JSVAL_TYPE_INT32, outReg, output.valueReg()); - - // Success. - attacher.jumpRejoin(masm); - - // Failure. - masm.bind(failures); - attacher.jumpNextStub(masm); -} - // In this case, the code for TypedArray and SharedTypedArray is not the same, // because the code embeds pointers to the respective class arrays. Code that // caches the stub code must distinguish between the two cases. @@ -1533,101 +1448,6 @@ GetPropertyIC::tryAttachNative(JSContext* cx, HandleScript outerScript, IonScrip } bool -GetPropertyIC::tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion, - HandleObject obj, HandleId id, void* returnAddr, bool* emitted) -{ - MOZ_ASSERT(canAttachStub()); - MOZ_ASSERT(!*emitted); - MOZ_ASSERT(outerScript->ionScript() == ion); - - if (!obj->is<UnboxedPlainObject>()) - return true; - const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id); - if (!property) - return true; - - *emitted = true; - - MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_); - - Label failures; - emitIdGuard(masm, id, &failures); - Label* maybeFailures = failures.used() ? &failures : nullptr; - - StubAttacher attacher(*this); - GenerateReadUnboxed(cx, ion, masm, attacher, obj, property, object(), output(), maybeFailures); - return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed", - JS::TrackedOutcome::ICGetPropStub_UnboxedRead); -} - -bool -GetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion, - HandleObject obj, HandleId id, void* returnAddr, bool* emitted) -{ - MOZ_ASSERT(canAttachStub()); - MOZ_ASSERT(!*emitted); - MOZ_ASSERT(outerScript->ionScript() == ion); - - if (!obj->is<UnboxedPlainObject>()) - return true; - Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando()); - if (!expando) - return true; - - Shape* shape = expando->lookup(cx, id); - if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) - return true; - - *emitted = true; - - MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_); - - Label failures; - emitIdGuard(masm, id, &failures); - Label* maybeFailures = failures.used() ? &failures : nullptr; - - StubAttacher attacher(*this); - GenerateReadSlot(cx, ion, masm, attacher, DontCheckTDZ, obj, obj, - shape, object(), output(), maybeFailures); - return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed expando", - JS::TrackedOutcome::ICGetPropStub_UnboxedReadExpando); -} - -bool -GetPropertyIC::tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion, - HandleObject obj, HandleId id, void* returnAddr, - bool* emitted) -{ - MOZ_ASSERT(canAttachStub()); - MOZ_ASSERT(!*emitted); - MOZ_ASSERT(outerScript->ionScript() == ion); - - if (!obj->is<UnboxedArrayObject>()) - return true; - - if (!JSID_IS_ATOM(id, cx->names().length)) - return true; - - if (obj->as<UnboxedArrayObject>().length() > INT32_MAX) - return true; - - if (!allowArrayLength(cx)) - return true; - - *emitted = true; - - MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_); - - Label failures; - emitIdGuard(masm, id, &failures); - - StubAttacher attacher(*this); - GenerateUnboxedArrayLength(cx, masm, attacher, obj, object(), output(), &failures); - return linkAndAttachStub(cx, masm, attacher, ion, "unboxed array length", - JS::TrackedOutcome::ICGetPropStub_UnboxedArrayLength); -} - -bool GetPropertyIC::tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool* emitted) { @@ -2196,15 +2016,6 @@ GetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, returnAddr, emitted)) return false; - if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, id, returnAddr, emitted)) - return false; - - if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, id, returnAddr, emitted)) - return false; - - if (!*emitted && !tryAttachUnboxedArrayLength(cx, outerScript, ion, obj, id, returnAddr, emitted)) - return false; - if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, id, emitted)) return false; } @@ -2383,12 +2194,6 @@ GenerateSetSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att NativeObject::slotsSizeMustNotOverflow(); - if (obj->is<UnboxedPlainObject>()) { - obj = obj->as<UnboxedPlainObject>().maybeExpando(); - masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), tempReg); - object = tempReg; - } - if (obj->as<NativeObject>().isFixedSlot(shape->slot())) { Address addr(object, NativeObject::getFixedSlotOffset(shape->slot())); @@ -3026,23 +2831,13 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att masm.branchTestObjGroup(Assembler::NotEqual, object, oldGroup, failures); if (obj->maybeShape()) { masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, failures); - } else { - MOZ_ASSERT(obj->is<UnboxedPlainObject>()); - - Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); - masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failures); - - masm.loadPtr(expandoAddress, tempReg); - masm.branchTestObjShape(Assembler::NotEqual, tempReg, oldShape, failures); } Shape* newShape = obj->maybeShape(); - if (!newShape) - newShape = obj->as<UnboxedPlainObject>().maybeExpando()->lastProperty(); // Guard that the incoming value is in the type set for the property // if a type barrier is required. - if (checkTypeset) + if (newShape && checkTypeset) CheckTypeSetForWrite(masm, obj, newShape->propid(), tempReg, value, failures); // Guard shapes along prototype chain. @@ -3063,9 +2858,7 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att } // Call a stub to (re)allocate dynamic slots, if necessary. - uint32_t newNumDynamicSlots = obj->is<UnboxedPlainObject>() - ? obj->as<UnboxedPlainObject>().maybeExpando()->numDynamicSlots() - : obj->as<NativeObject>().numDynamicSlots(); + uint32_t newNumDynamicSlots = obj->as<NativeObject>().numDynamicSlots(); if (NativeObject::dynamicSlotsCount(oldShape) != newNumDynamicSlots) { AllocatableRegisterSet regs(RegisterSet::Volatile()); LiveRegisterSet save(regs.asLiveSet()); @@ -3076,12 +2869,6 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att Register temp1 = regs.takeAnyGeneral(); Register temp2 = regs.takeAnyGeneral(); - if (obj->is<UnboxedPlainObject>()) { - // Pass the expando object to the stub. - masm.Push(object); - masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object); - } - masm.setupUnalignedABICall(temp1); masm.loadJSContext(temp1); masm.passABIArg(temp1); @@ -3098,27 +2885,16 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att masm.jump(&allocDone); masm.bind(&allocFailed); - if (obj->is<UnboxedPlainObject>()) - masm.Pop(object); masm.PopRegsInMask(save); masm.jump(failures); masm.bind(&allocDone); masm.setFramePushed(framePushedAfterCall); - if (obj->is<UnboxedPlainObject>()) - masm.Pop(object); masm.PopRegsInMask(save); } bool popObject = false; - if (obj->is<UnboxedPlainObject>()) { - masm.push(object); - popObject = true; - obj = obj->as<UnboxedPlainObject>().maybeExpando(); - masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object); - } - // Write the object or expando object's new shape. Address shapeAddr(object, ShapedObject::offsetOfShape()); if (cx->zone()->needsIncrementalBarrier()) @@ -3126,8 +2902,6 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att masm.storePtr(ImmGCPtr(newShape), shapeAddr); if (oldGroup != obj->group()) { - MOZ_ASSERT(!obj->is<UnboxedPlainObject>()); - // Changing object's group from a partially to fully initialized group, // per the acquired properties analysis. Only change the group if the // old group still has a newScript. @@ -3370,141 +3144,6 @@ CanAttachNativeSetProp(JSContext* cx, HandleObject obj, HandleId id, const Const return SetPropertyIC::CanAttachNone; } -static void -GenerateSetUnboxed(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher, - JSObject* obj, jsid id, uint32_t unboxedOffset, JSValueType unboxedType, - Register object, Register tempReg, const ConstantOrRegister& value, - bool checkTypeset, Label* failures) -{ - // Guard on the type of the object. - masm.branchPtr(Assembler::NotEqual, - Address(object, JSObject::offsetOfGroup()), - ImmGCPtr(obj->group()), failures); - - if (checkTypeset) - CheckTypeSetForWrite(masm, obj, id, tempReg, value, failures); - - Address address(object, UnboxedPlainObject::offsetOfData() + unboxedOffset); - - if (cx->zone()->needsIncrementalBarrier()) { - if (unboxedType == JSVAL_TYPE_OBJECT) - masm.callPreBarrier(address, MIRType::Object); - else if (unboxedType == JSVAL_TYPE_STRING) - masm.callPreBarrier(address, MIRType::String); - else - MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(unboxedType)); - } - - masm.storeUnboxedProperty(address, unboxedType, value, failures); - - attacher.jumpRejoin(masm); - - masm.bind(failures); - attacher.jumpNextStub(masm); -} - -static bool -CanAttachSetUnboxed(JSContext* cx, HandleObject obj, HandleId id, const ConstantOrRegister& val, - bool needsTypeBarrier, bool* checkTypeset, - uint32_t* unboxedOffset, JSValueType* unboxedType) -{ - if (!obj->is<UnboxedPlainObject>()) - return false; - - const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id); - if (property) { - *checkTypeset = false; - if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset)) - return false; - *unboxedOffset = property->offset; - *unboxedType = property->type; - return true; - } - - return false; -} - -static bool -CanAttachSetUnboxedExpando(JSContext* cx, HandleObject obj, HandleId id, - const ConstantOrRegister& val, - bool needsTypeBarrier, bool* checkTypeset, Shape** pshape) -{ - if (!obj->is<UnboxedPlainObject>()) - return false; - - Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando()); - if (!expando) - return false; - - Shape* shape = expando->lookupPure(id); - if (!shape || !shape->hasDefaultSetter() || !shape->hasSlot() || !shape->writable()) - return false; - - *checkTypeset = false; - if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset)) - return false; - - *pshape = shape; - return true; -} - -static bool -CanAttachAddUnboxedExpando(JSContext* cx, HandleObject obj, HandleShape oldShape, - HandleId id, const ConstantOrRegister& val, - bool needsTypeBarrier, bool* checkTypeset) -{ - if (!obj->is<UnboxedPlainObject>()) - return false; - - Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando()); - if (!expando || expando->inDictionaryMode()) - return false; - - Shape* newShape = expando->lastProperty(); - if (newShape->isEmptyShape() || newShape->propid() != id || newShape->previous() != oldShape) - return false; - - MOZ_ASSERT(newShape->hasDefaultSetter() && newShape->hasSlot() && newShape->writable()); - - if (PrototypeChainShadowsPropertyAdd(cx, obj, id)) - return false; - - *checkTypeset = false; - if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset)) - return false; - - return true; -} - -bool -SetPropertyIC::tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion, - HandleObject obj, HandleId id, bool* emitted) -{ - MOZ_ASSERT(!*emitted); - - bool checkTypeset = false; - uint32_t unboxedOffset; - JSValueType unboxedType; - if (!CanAttachSetUnboxed(cx, obj, id, value(), needsTypeBarrier(), &checkTypeset, - &unboxedOffset, &unboxedType)) - { - return true; - } - - *emitted = true; - - MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_); - StubAttacher attacher(*this); - - Label failures; - emitIdGuard(masm, id, &failures); - - GenerateSetUnboxed(cx, masm, attacher, obj, id, unboxedOffset, unboxedType, - object(), temp(), value(), checkTypeset, &failures); - return linkAndAttachStub(cx, masm, attacher, ion, "set_unboxed", - JS::TrackedOutcome::ICSetPropStub_SetUnboxed); -} - bool SetPropertyIC::tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool* emitted) @@ -3586,26 +3225,6 @@ SetPropertyIC::tryAttachNative(JSContext* cx, HandleScript outerScript, IonScrip } bool -SetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion, - HandleObject obj, HandleId id, bool* emitted) -{ - MOZ_ASSERT(!*emitted); - - RootedShape shape(cx); - bool checkTypeset = false; - if (!CanAttachSetUnboxedExpando(cx, obj, id, value(), needsTypeBarrier(), - &checkTypeset, shape.address())) - { - return true; - } - - if (!attachSetSlot(cx, outerScript, ion, obj, shape, checkTypeset)) - return false; - *emitted = true; - return true; -} - -bool SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleValue idval, HandleValue value, MutableHandleId id, bool* emitted, bool* tryNativeAddSlot) @@ -3613,7 +3232,7 @@ SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* MOZ_ASSERT(!*emitted); MOZ_ASSERT(!*tryNativeAddSlot); - if (!canAttachStub() || obj->watched()) + if (!canAttachStub()) return true; // Fail cache emission if the object is frozen @@ -3630,12 +3249,6 @@ SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, emitted, tryNativeAddSlot)) return false; - - if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, id, emitted)) - return false; - - if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, id, emitted)) - return false; } if (idval.isInt32()) { @@ -3687,16 +3300,6 @@ SetPropertyIC::tryAttachAddSlot(JSContext* cx, HandleScript outerScript, IonScri return true; } - checkTypeset = false; - if (CanAttachAddUnboxedExpando(cx, obj, oldShape, id, value(), needsTypeBarrier(), - &checkTypeset)) - { - if (!attachAddSlot(cx, outerScript, ion, obj, id, oldShape, oldGroup, checkTypeset)) - return false; - *emitted = true; - return true; - } - return true; } @@ -3713,16 +3316,11 @@ SetPropertyIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex RootedObjectGroup oldGroup(cx); RootedShape oldShape(cx); if (cache.canAttachStub()) { - oldGroup = obj->getGroup(cx); + oldGroup = JSObject::getGroup(cx, obj); if (!oldGroup) return false; oldShape = obj->maybeShape(); - if (obj->is<UnboxedPlainObject>()) { - MOZ_ASSERT(!oldShape); - if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) - oldShape = expando->lastProperty(); - } } RootedId id(cx); @@ -4025,7 +3623,7 @@ GetPropertyIC::tryAttachDenseElementHole(JSContext* cx, HandleScript outerScript GetPropertyIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval, TypedOrValueRegister output) { - if (!obj->is<TypedArrayObject>() && !obj->is<UnboxedArrayObject>()) + if (!obj->is<TypedArrayObject>()) return false; MOZ_ASSERT(idval.isInt32() || idval.isString()); @@ -4056,13 +3654,6 @@ GetPropertyIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& i return output.hasValue() || !output.typedReg().isFloat(); } - if (index >= obj->as<UnboxedArrayObject>().initializedLength()) - return false; - - JSValueType elementType = obj->as<UnboxedArrayObject>().elementType(); - if (elementType == JSVAL_TYPE_DOUBLE) - return output.hasValue(); - return output.hasValue() || !output.typedReg().isFloat(); } @@ -4139,46 +3730,27 @@ GenerateGetTypedOrUnboxedArrayElement(JSContext* cx, MacroAssembler& masm, Label popObjectAndFail; - if (array->is<TypedArrayObject>()) { - // Guard on the initialized length. - Address length(object, TypedArrayObject::lengthOffset()); - masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures); + // Guard on the initialized length. + Address length(object, TypedArrayObject::lengthOffset()); + masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures); - // Save the object register on the stack in case of failure. - Register elementReg = object; - masm.push(object); + // Save the object register on the stack in case of failure. + Register elementReg = object; + masm.push(object); - // Load elements vector. - masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elementReg); + // Load elements vector. + masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elementReg); - // Load the value. We use an invalid register because the destination - // register is necessary a non double register. - Scalar::Type arrayType = array->as<TypedArrayObject>().type(); - int width = Scalar::byteSize(arrayType); - BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width)); - if (output.hasValue()) { - masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult, - elementReg, &popObjectAndFail); - } else { - masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popObjectAndFail); - } + // Load the value. We use an invalid register because the destination + // register is necessary a non double register. + Scalar::Type arrayType = array->as<TypedArrayObject>().type(); + int width = Scalar::byteSize(arrayType); + BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width)); + if (output.hasValue()) { + masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult, + elementReg, &popObjectAndFail); } else { - // Save the object register on the stack in case of failure. - masm.push(object); - - // Guard on the initialized length. - masm.load32(Address(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), object); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), object); - masm.branch32(Assembler::BelowOrEqual, object, indexReg, &popObjectAndFail); - - // Load elements vector. - Register elementReg = object; - masm.loadPtr(Address(masm.getStackPointer(), 0), object); - masm.loadPtr(Address(object, UnboxedArrayObject::offsetOfElements()), elementReg); - - JSValueType elementType = array->as<UnboxedArrayObject>().elementType(); - BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(UnboxedTypeSize(elementType))); - masm.loadUnboxedProperty(source, elementType, output); + masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popObjectAndFail); } masm.pop(object); @@ -4325,9 +3897,6 @@ IsDenseElementSetInlineable(JSObject* obj, const Value& idval, const ConstantOrR if (!obj->is<ArrayObject>()) return false; - if (obj->watched()) - return false; - if (!idval.isInt32()) return false; diff --git a/js/src/jit/IonCaches.h b/js/src/jit/IonCaches.h index 173e06c6ba..b006465383 100644 --- a/js/src/jit/IonCaches.h +++ b/js/src/jit/IonCaches.h @@ -529,18 +529,6 @@ class GetPropertyIC : public IonCache HandleObject obj, HandleId id, void* returnAddr, bool* emitted); - MOZ_MUST_USE bool tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion, - HandleObject obj, HandleId id, void* returnAddr, - bool* emitted); - - MOZ_MUST_USE bool tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, - IonScript* ion, HandleObject obj, HandleId id, - void* returnAddr, bool* emitted); - - MOZ_MUST_USE bool tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, - IonScript* ion, HandleObject obj, HandleId id, - void* returnAddr, bool* emitted); - MOZ_MUST_USE bool tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool* emitted); diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h index c581aa62ed..55c3d4dadf 100644 --- a/js/src/jit/IonCode.h +++ b/js/src/jit/IonCode.h @@ -9,7 +9,6 @@ #include "mozilla/Atomics.h" #include "mozilla/MemoryReporting.h" -#include "mozilla/PodOperations.h" #include "jstypes.h" @@ -692,17 +691,15 @@ struct IonScriptCounts { private: // Any previous invalidated compilation(s) for the script. - IonScriptCounts* previous_; + IonScriptCounts* previous_ = nullptr; // Information about basic blocks in this script. - size_t numBlocks_; - IonBlockCounts* blocks_; + size_t numBlocks_ = 0; + IonBlockCounts* blocks_ = nullptr; public: - IonScriptCounts() { - mozilla::PodZero(this); - } + IonScriptCounts() = default; ~IonScriptCounts() { for (size_t i = 0; i < numBlocks_; i++) diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp index eb5a6c1c22..3f9d9db888 100644 --- a/js/src/jit/JitOptions.cpp +++ b/js/src/jit/JitOptions.cpp @@ -221,9 +221,6 @@ DefaultJitOptions::DefaultJitOptions() Warn(forcedRegisterAllocatorEnv, env); } - // Toggles whether unboxed plain objects can be created by the VM. - SET_DEFAULT(disableUnboxedObjects, false); - // Test whether Atomics are allowed in asm.js code. SET_DEFAULT(asmJSAtomicsEnable, false); diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h index 076980b4e5..719ee14d95 100644 --- a/js/src/jit/JitOptions.h +++ b/js/src/jit/JitOptions.h @@ -91,9 +91,6 @@ struct DefaultJitOptions mozilla::Maybe<uint32_t> forcedDefaultIonSmallFunctionWarmUpThreshold; mozilla::Maybe<IonRegisterAllocator> forcedRegisterAllocator; - // The options below affect the rest of the VM, and not just the JIT. - bool disableUnboxedObjects; - DefaultJitOptions(); bool isSmallFunction(JSScript* script) const; void setEagerCompilation(); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 709de99873..22f1d5f70f 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -657,16 +657,6 @@ LIRGenerator::visitAssertRecoveredOnBailout(MAssertRecoveredOnBailout* assertion } void -LIRGenerator::visitArraySplice(MArraySplice* ins) -{ - LArraySplice* lir = new(alloc()) LArraySplice(useRegisterAtStart(ins->object()), - useRegisterAtStart(ins->start()), - useRegisterAtStart(ins->deleteCount())); - add(lir, ins); - assignSafepoint(lir, ins); -} - -void LIRGenerator::visitGetDynamicName(MGetDynamicName* ins) { MDefinition* envChain = ins->getEnvironmentChain(); @@ -2889,32 +2879,6 @@ LIRGenerator::visitSetInitializedLength(MSetInitializedLength* ins) } void -LIRGenerator::visitUnboxedArrayLength(MUnboxedArrayLength* ins) -{ - define(new(alloc()) LUnboxedArrayLength(useRegisterAtStart(ins->object())), ins); -} - -void -LIRGenerator::visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins) -{ - define(new(alloc()) LUnboxedArrayInitializedLength(useRegisterAtStart(ins->object())), ins); -} - -void -LIRGenerator::visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins) -{ - add(new(alloc()) LIncrementUnboxedArrayInitializedLength(useRegister(ins->object())), ins); -} - -void -LIRGenerator::visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins) -{ - add(new(alloc()) LSetUnboxedArrayInitializedLength(useRegister(ins->object()), - useRegisterOrConstant(ins->length()), - temp()), ins); -} - -void LIRGenerator::visitNot(MNot* ins) { MDefinition* op = ins->input(); @@ -3163,22 +3127,16 @@ LIRGenerator::visitStoreElementHole(MStoreElementHole* ins) const LUse elements = useRegister(ins->elements()); const LAllocation index = useRegisterOrConstant(ins->index()); - // Use a temp register when adding new elements to unboxed arrays. - LDefinition tempDef = LDefinition::BogusTemp(); - if (ins->unboxedType() != JSVAL_TYPE_MAGIC) - tempDef = temp(); - LInstruction* lir; switch (ins->value()->type()) { case MIRType::Value: - lir = new(alloc()) LStoreElementHoleV(object, elements, index, useBox(ins->value()), - tempDef); + lir = new(alloc()) LStoreElementHoleV(object, elements, index, useBox(ins->value())); break; default: { const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); - lir = new(alloc()) LStoreElementHoleT(object, elements, index, value, tempDef); + lir = new(alloc()) LStoreElementHoleT(object, elements, index, value); break; } } @@ -3197,20 +3155,14 @@ LIRGenerator::visitFallibleStoreElement(MFallibleStoreElement* ins) const LUse elements = useRegister(ins->elements()); const LAllocation index = useRegisterOrConstant(ins->index()); - // Use a temp register when adding new elements to unboxed arrays. - LDefinition tempDef = LDefinition::BogusTemp(); - if (ins->unboxedType() != JSVAL_TYPE_MAGIC) - tempDef = temp(); - LInstruction* lir; switch (ins->value()->type()) { case MIRType::Value: - lir = new(alloc()) LFallibleStoreElementV(object, elements, index, useBox(ins->value()), - tempDef); + lir = new(alloc()) LFallibleStoreElementV(object, elements, index, useBox(ins->value())); break; default: const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); - lir = new(alloc()) LFallibleStoreElementT(object, elements, index, value, tempDef); + lir = new(alloc()) LFallibleStoreElementT(object, elements, index, value); break; } @@ -3252,14 +3204,6 @@ LIRGenerator::visitStoreUnboxedString(MStoreUnboxedString* ins) } void -LIRGenerator::visitConvertUnboxedObjectToNative(MConvertUnboxedObjectToNative* ins) -{ - LInstruction* check = new(alloc()) LConvertUnboxedObjectToNative(useRegister(ins->object())); - add(check, ins); - assignSafepoint(check, ins); -} - -void LIRGenerator::visitEffectiveAddress(MEffectiveAddress* ins) { define(new(alloc()) LEffectiveAddress(useRegister(ins->base()), useRegister(ins->index())), ins); @@ -3777,24 +3721,6 @@ LIRGenerator::visitGuardReceiverPolymorphic(MGuardReceiverPolymorphic* ins) } void -LIRGenerator::visitGuardUnboxedExpando(MGuardUnboxedExpando* ins) -{ - LGuardUnboxedExpando* guard = - new(alloc()) LGuardUnboxedExpando(useRegister(ins->object())); - assignSnapshot(guard, ins->bailoutKind()); - add(guard, ins); - redefine(ins, ins->object()); -} - -void -LIRGenerator::visitLoadUnboxedExpando(MLoadUnboxedExpando* ins) -{ - LLoadUnboxedExpando* lir = - new(alloc()) LLoadUnboxedExpando(useRegisterAtStart(ins->object())); - define(lir, ins); -} - -void LIRGenerator::visitAssertRange(MAssertRange* ins) { MDefinition* input = ins->input(); diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 9b4095aec0..9342ef471b 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -107,7 +107,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitCall(MCall* call); void visitApplyArgs(MApplyArgs* apply); void visitApplyArray(MApplyArray* apply); - void visitArraySplice(MArraySplice* splice); void visitBail(MBail* bail); void visitUnreachable(MUnreachable* unreachable); void visitEncodeSnapshot(MEncodeSnapshot* ins); @@ -216,10 +215,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitTypedObjectDescr(MTypedObjectDescr* ins); void visitInitializedLength(MInitializedLength* ins); void visitSetInitializedLength(MSetInitializedLength* ins); - void visitUnboxedArrayLength(MUnboxedArrayLength* ins); - void visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins); - void visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins); - void visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins); void visitNot(MNot* ins); void visitBoundsCheck(MBoundsCheck* ins); void visitBoundsCheckLower(MBoundsCheckLower* ins); @@ -232,7 +227,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitFallibleStoreElement(MFallibleStoreElement* ins); void visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins); void visitStoreUnboxedString(MStoreUnboxedString* ins); - void visitConvertUnboxedObjectToNative(MConvertUnboxedObjectToNative* ins); void visitEffectiveAddress(MEffectiveAddress* ins); void visitArrayPopShift(MArrayPopShift* ins); void visitArrayPush(MArrayPush* ins); @@ -256,8 +250,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitGuardObject(MGuardObject* ins); void visitGuardString(MGuardString* ins); void visitGuardReceiverPolymorphic(MGuardReceiverPolymorphic* ins); - void visitGuardUnboxedExpando(MGuardUnboxedExpando* ins); - void visitLoadUnboxedExpando(MLoadUnboxedExpando* ins); void visitPolyInlineGuard(MPolyInlineGuard* ins); void visitAssertRange(MAssertRange* ins); void visitCallGetProperty(MCallGetProperty* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 736c6c8920..2363545300 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -30,7 +30,6 @@ #include "jit/shared/Lowering-shared-inl.h" #include "vm/NativeObject-inl.h" #include "vm/StringObject-inl.h" -#include "vm/UnboxedObject-inl.h" using mozilla::ArrayLength; using mozilla::AssertedCast; @@ -86,8 +85,6 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineArrayPush(callInfo); case InlinableNative::ArraySlice: return inlineArraySlice(callInfo); - case InlinableNative::ArraySplice: - return inlineArraySplice(callInfo); // Atomic natives. case InlinableNative::AtomicsCompareExchange: @@ -473,11 +470,6 @@ IonBuilder::inlineArray(CallInfo& callInfo) return InliningStatus_NotInlined; } - if (templateObject->is<UnboxedArrayObject>()) { - if (templateObject->group()->unboxedLayout().nativeGroup()) - return InliningStatus_NotInlined; - } - // Multiple arguments imply array initialization, not just construction. if (callInfo.argc() >= 2) { initLength = callInfo.argc(); @@ -525,7 +517,7 @@ IonBuilder::inlineArray(CallInfo& callInfo) // Make sure initLength matches the template object's length. This is // not guaranteed to be the case, for instance if we're inlining the // MConstant may come from an outer script. - if (initLength != GetAnyBoxedOrUnboxedArrayLength(templateObject)) + if (initLength != templateObject->as<ArrayObject>().length()) return InliningStatus_NotInlined; // Don't inline large allocations. @@ -540,16 +532,15 @@ IonBuilder::inlineArray(CallInfo& callInfo) MDefinition* array = current->peek(-1); if (callInfo.argc() >= 2) { - JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject); for (uint32_t i = 0; i < initLength; i++) { if (!alloc().ensureBallast()) return InliningStatus_Error; MDefinition* value = callInfo.getArg(i); - if (!initializeArrayElement(array, i, value, unboxedType, /* addResumePoint = */ false)) + if (!initializeArrayElement(array, i, value, /* addResumePoint = */ false)) return InliningStatus_Error; } - MInstruction* setLength = setInitializedLength(array, unboxedType, initLength); + MInstruction* setLength = setInitializedLength(array, initLength); if (!resumeAfter(setLength)) return InliningStatus_Error; } @@ -582,7 +573,7 @@ IonBuilder::inlineArrayIsArray(CallInfo& callInfo) if (!clasp || clasp->isProxy()) return InliningStatus_NotInlined; - isArray = (clasp == &ArrayObject::class_ || clasp == &UnboxedArrayObject::class_); + isArray = (clasp == &ArrayObject::class_); } pushConstant(BooleanValue(isArray)); @@ -613,34 +604,27 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode) OBJECT_FLAG_LENGTH_OVERFLOW | OBJECT_FLAG_ITERATED; - MDefinition* obj = convertUnboxedObjects(callInfo.thisArg()); + MDefinition* obj = callInfo.thisArg(); TemporaryTypeSet* thisTypes = obj->resultTypeSet(); if (!thisTypes) return InliningStatus_NotInlined; const Class* clasp = thisTypes->getKnownClass(constraints()); - if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_) + if (clasp != &ArrayObject::class_) return InliningStatus_NotInlined; if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) { trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; } - if (ArrayPrototypeHasIndexedProperty(this, script())) { + // Watch out for extra indexed properties on the object or its prototype. + if (ElementAccessHasExtraIndexedProperty(this, obj)) { trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return InliningStatus_NotInlined; } - JSValueType unboxedType = JSVAL_TYPE_MAGIC; - if (clasp == &UnboxedArrayObject::class_) { - unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr); - if (unboxedType == JSVAL_TYPE_MAGIC) - return InliningStatus_NotInlined; - } - callInfo.setImplicitlyUsedUnchecked(); - if (clasp == &ArrayObject::class_) - obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false); + obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false); TemporaryTypeSet* returnTypes = getInlineReturnTypeSet(); bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED); @@ -651,8 +635,7 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode) if (barrier != BarrierKind::NoBarrier) returnType = MIRType::Value; - MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode, - unboxedType, needsHoleCheck, maybeUndefined); + MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode, needsHoleCheck, maybeUndefined); current->add(ins); current->push(ins); ins->setResultType(returnType); @@ -667,46 +650,6 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode) } IonBuilder::InliningStatus -IonBuilder::inlineArraySplice(CallInfo& callInfo) -{ - if (callInfo.argc() != 2 || callInfo.constructing()) { - trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); - return InliningStatus_NotInlined; - } - - // Ensure |this|, argument and result are objects. - if (getInlineReturnType() != MIRType::Object) - return InliningStatus_NotInlined; - if (callInfo.thisArg()->type() != MIRType::Object) - return InliningStatus_NotInlined; - if (callInfo.getArg(0)->type() != MIRType::Int32) - return InliningStatus_NotInlined; - if (callInfo.getArg(1)->type() != MIRType::Int32) - return InliningStatus_NotInlined; - - callInfo.setImplicitlyUsedUnchecked(); - - // Specialize arr.splice(start, deleteCount) with unused return value and - // avoid creating the result array in this case. - if (!BytecodeIsPopped(pc)) { - trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric); - return InliningStatus_NotInlined; - } - - MArraySplice* ins = MArraySplice::New(alloc(), - callInfo.thisArg(), - callInfo.getArg(0), - callInfo.getArg(1)); - - current->add(ins); - pushConstant(UndefinedValue()); - - if (!resumeAfter(ins)) - return InliningStatus_Error; - return InliningStatus_Inlined; -} - -IonBuilder::InliningStatus IonBuilder::inlineArrayJoin(CallInfo& callInfo) { if (callInfo.argc() != 1 || callInfo.constructing()) { @@ -741,7 +684,7 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo) return InliningStatus_NotInlined; } - MDefinition* obj = convertUnboxedObjects(callInfo.thisArg()); + MDefinition* obj = callInfo.thisArg(); MDefinition* value = callInfo.getArg(0); if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, nullptr, &value, /* canModify = */ false)) @@ -759,16 +702,10 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo) if (!thisTypes) return InliningStatus_NotInlined; const Class* clasp = thisTypes->getKnownClass(constraints()); - if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_) + if (clasp != &ArrayObject::class_) return InliningStatus_NotInlined; - if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | - OBJECT_FLAG_LENGTH_OVERFLOW)) - { - trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); - return InliningStatus_NotInlined; - } - if (ArrayPrototypeHasIndexedProperty(this, script())) { + if (ElementAccessHasExtraIndexedProperty(this, obj)) { trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return InliningStatus_NotInlined; } @@ -780,13 +717,6 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo) return InliningStatus_NotInlined; } - JSValueType unboxedType = JSVAL_TYPE_MAGIC; - if (clasp == &UnboxedArrayObject::class_) { - unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr); - if (unboxedType == JSVAL_TYPE_MAGIC) - return InliningStatus_NotInlined; - } - callInfo.setImplicitlyUsedUnchecked(); if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles || @@ -797,13 +727,12 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo) value = valueDouble; } - if (unboxedType == JSVAL_TYPE_MAGIC) - obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false); + obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false); if (NeedsPostBarrier(value)) current->add(MPostWriteBarrier::New(alloc(), obj, value)); - MArrayPush* ins = MArrayPush::New(alloc(), obj, value, unboxedType); + MArrayPush* ins = MArrayPush::New(alloc(), obj, value); current->add(ins); current->push(ins); @@ -820,7 +749,7 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo) return InliningStatus_NotInlined; } - MDefinition* obj = convertUnboxedObjects(callInfo.thisArg()); + MDefinition* obj = callInfo.thisArg(); // Ensure |this| and result are objects. if (getInlineReturnType() != MIRType::Object) @@ -844,24 +773,11 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo) return InliningStatus_NotInlined; const Class* clasp = thisTypes->getKnownClass(constraints()); - if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_) - return InliningStatus_NotInlined; - if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | - OBJECT_FLAG_LENGTH_OVERFLOW)) - { - trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); + if (clasp != &ArrayObject::class_) return InliningStatus_NotInlined; - } - - JSValueType unboxedType = JSVAL_TYPE_MAGIC; - if (clasp == &UnboxedArrayObject::class_) { - unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr); - if (unboxedType == JSVAL_TYPE_MAGIC) - return InliningStatus_NotInlined; - } - // Watch out for indexed properties on the prototype. - if (ArrayPrototypeHasIndexedProperty(this, script())) { + // Watch out for indexed properties on the object or its prototype. + if (ElementAccessHasExtraIndexedProperty(this, obj)) { trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return InliningStatus_NotInlined; } @@ -880,15 +796,8 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo) if (!templateObj) return InliningStatus_NotInlined; - if (unboxedType == JSVAL_TYPE_MAGIC) { - if (!templateObj->is<ArrayObject>()) - return InliningStatus_NotInlined; - } else { - if (!templateObj->is<UnboxedArrayObject>()) - return InliningStatus_NotInlined; - if (templateObj->as<UnboxedArrayObject>().elementType() != unboxedType) - return InliningStatus_NotInlined; - } + if (!templateObj->is<ArrayObject>()) + return InliningStatus_NotInlined; callInfo.setImplicitlyUsedUnchecked(); @@ -907,16 +816,12 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo) end = MArrayLength::New(alloc(), elements); current->add(end->toInstruction()); - } else { - end = MUnboxedArrayLength::New(alloc(), obj); - current->add(end->toInstruction()); } MArraySlice* ins = MArraySlice::New(alloc(), constraints(), obj, begin, end, templateObj, - templateObj->group()->initialHeap(constraints()), - unboxedType); + templateObj->group()->initialHeap(constraints())); current->add(ins); current->push(ins); @@ -1435,7 +1340,7 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo) // Check if exist a template object in stub. JSString* stringStr = nullptr; JSString* stringSep = nullptr; - JSObject* templateObject = nullptr; + ArrayObject* templateObject = nullptr; if (!inspector->isOptimizableCallStringSplit(pc, &stringStr, &stringSep, &templateObject)) return InliningStatus_NotInlined; @@ -1461,13 +1366,13 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo) if (!key.maybeTypes()->hasType(TypeSet::StringType())) return InliningStatus_NotInlined; - uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(templateObject); - if (GetAnyBoxedOrUnboxedInitializedLength(templateObject) != initLength) + uint32_t initLength = templateObject->length(); + if (templateObject->getDenseInitializedLength() != initLength) return InliningStatus_NotInlined; Vector<MConstant*, 0, SystemAllocPolicy> arrayValues; for (uint32_t i = 0; i < initLength; i++) { - Value str = GetAnyBoxedOrUnboxedDenseElement(templateObject, i); + Value str = templateObject->getDenseElement(i); MOZ_ASSERT(str.toString()->isAtom()); MConstant* value = MConstant::New(alloc().fallible(), str, constraints()); if (!value) @@ -1498,8 +1403,6 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo) return InliningStatus_Inlined; } - JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject); - // Store all values, no need to initialize the length after each as // jsop_initelem_array is doing because we do not expect to bailout // because the memory is supposed to be allocated by now. @@ -1510,11 +1413,11 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo) MConstant* value = arrayValues[i]; current->add(value); - if (!initializeArrayElement(array, i, value, unboxedType, /* addResumePoint = */ false)) + if (!initializeArrayElement(array, i, value, /* addResumePoint = */ false)) return InliningStatus_Error; } - MInstruction* setLength = setInitializedLength(array, unboxedType, initLength); + MInstruction* setLength = setInitializedLength(array, initLength); if (!resumeAfter(setLength)) return InliningStatus_Error; @@ -2150,7 +2053,7 @@ IonBuilder::inlineDefineDataProperty(CallInfo& callInfo) if (callInfo.argc() != 3) return InliningStatus_NotInlined; - MDefinition* obj = convertUnboxedObjects(callInfo.getArg(0)); + MDefinition* obj = callInfo.getArg(0); MDefinition* id = callInfo.getArg(1); MDefinition* value = callInfo.getArg(2); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 287b875824..0cf31adb33 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -1970,7 +1970,7 @@ WrappedFunction::WrappedFunction(JSFunction* fun) MCall* MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numActualArgs, - bool construct, bool isDOMCall) + bool construct, bool ignoresReturnValue, bool isDOMCall) { WrappedFunction* wrappedTarget = target ? new(alloc) WrappedFunction(target) : nullptr; MOZ_ASSERT(maxArgc >= numActualArgs); @@ -1979,7 +1979,7 @@ MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numA MOZ_ASSERT(!construct); ins = new(alloc) MCallDOMNative(wrappedTarget, numActualArgs); } else { - ins = new(alloc) MCall(wrappedTarget, numActualArgs, construct); + ins = new(alloc) MCall(wrappedTarget, numActualArgs, construct, ignoresReturnValue); } if (!ins->init(alloc, maxArgc + NumNonArgumentOperands)) return nullptr; @@ -2630,40 +2630,6 @@ jit::EqualTypes(MIRType type1, TemporaryTypeSet* typeset1, return typeset1->equals(typeset2); } -// Tests whether input/inputTypes can always be stored to an unboxed -// object/array property with the given unboxed type. -bool -jit::CanStoreUnboxedType(TempAllocator& alloc, - JSValueType unboxedType, MIRType input, TypeSet* inputTypes) -{ - TemporaryTypeSet types; - - switch (unboxedType) { - case JSVAL_TYPE_BOOLEAN: - case JSVAL_TYPE_INT32: - case JSVAL_TYPE_DOUBLE: - case JSVAL_TYPE_STRING: - types.addType(TypeSet::PrimitiveType(unboxedType), alloc.lifoAlloc()); - break; - - case JSVAL_TYPE_OBJECT: - types.addType(TypeSet::AnyObjectType(), alloc.lifoAlloc()); - types.addType(TypeSet::NullType(), alloc.lifoAlloc()); - break; - - default: - MOZ_CRASH("Bad unboxed type"); - } - - return TypeSetIncludes(&types, input, inputTypes); -} - -static bool -CanStoreUnboxedType(TempAllocator& alloc, JSValueType unboxedType, MDefinition* value) -{ - return CanStoreUnboxedType(alloc, unboxedType, value->type(), value->resultTypeSet()); -} - bool MPhi::specializeType(TempAllocator& alloc) { @@ -4810,67 +4776,31 @@ MBeta::printOpcode(GenericPrinter& out) const bool MCreateThisWithTemplate::canRecoverOnBailout() const { - MOZ_ASSERT(templateObject()->is<PlainObject>() || templateObject()->is<UnboxedPlainObject>()); - MOZ_ASSERT_IF(templateObject()->is<PlainObject>(), - !templateObject()->as<PlainObject>().denseElementsAreCopyOnWrite()); - return true; -} - -bool -OperandIndexMap::init(TempAllocator& alloc, JSObject* templateObject) -{ - const UnboxedLayout& layout = - templateObject->as<UnboxedPlainObject>().layoutDontCheckGeneration(); - - const UnboxedLayout::PropertyVector& properties = layout.properties(); - MOZ_ASSERT(properties.length() < 255); - - // Allocate an array of indexes, where the top of each field correspond to - // the index of the operand in the MObjectState instance. - if (!map.init(alloc, layout.size())) - return false; - - // Reset all indexes to 0, which is an error code. - for (size_t i = 0; i < map.length(); i++) - map[i] = 0; - - // Map the property offsets to the indexes of MObjectState operands. - uint8_t index = 1; - for (size_t i = 0; i < properties.length(); i++, index++) - map[properties[i].offset] = index; - + MOZ_ASSERT(templateObject()->is<PlainObject>()); + MOZ_ASSERT(!templateObject()->as<PlainObject>().denseElementsAreCopyOnWrite()); return true; } MObjectState::MObjectState(MObjectState* state) : numSlots_(state->numSlots_), - numFixedSlots_(state->numFixedSlots_), - operandIndex_(state->operandIndex_) + numFixedSlots_(state->numFixedSlots_) { // This instruction is only used as a summary for bailout paths. setResultType(MIRType::Object); setRecoveredOnBailout(); } -MObjectState::MObjectState(JSObject *templateObject, OperandIndexMap* operandIndex) +MObjectState::MObjectState(JSObject* templateObject) { // This instruction is only used as a summary for bailout paths. setResultType(MIRType::Object); setRecoveredOnBailout(); - if (templateObject->is<NativeObject>()) { - NativeObject* nativeObject = &templateObject->as<NativeObject>(); - numSlots_ = nativeObject->slotSpan(); - numFixedSlots_ = nativeObject->numFixedSlots(); - } else { - const UnboxedLayout& layout = - templateObject->as<UnboxedPlainObject>().layoutDontCheckGeneration(); - // Same as UnboxedLayout::makeNativeGroup - numSlots_ = layout.properties().length(); - numFixedSlots_ = gc::GetGCKindSlots(layout.getAllocKind()); - } + MOZ_ASSERT(templateObject->is<NativeObject>()); - operandIndex_ = operandIndex; + NativeObject* nativeObject = &templateObject->as<NativeObject>(); + numSlots_ = nativeObject->slotSpan(); + numFixedSlots_ = nativeObject->numFixedSlots(); } JSObject* @@ -4905,39 +4835,21 @@ MObjectState::initFromTemplateObject(TempAllocator& alloc, MDefinition* undefine // the template object. This is needed to account values which are baked in // the template objects and not visible in IonMonkey, such as the // uninitialized-lexical magic value of call objects. - if (templateObject->is<UnboxedPlainObject>()) { - UnboxedPlainObject& unboxedObject = templateObject->as<UnboxedPlainObject>(); - const UnboxedLayout& layout = unboxedObject.layoutDontCheckGeneration(); - const UnboxedLayout::PropertyVector& properties = layout.properties(); - - for (size_t i = 0; i < properties.length(); i++) { - Value val = unboxedObject.getValue(properties[i], /* maybeUninitialized = */ true); - MDefinition *def = undefinedVal; - if (!val.isUndefined()) { - MConstant* ins = val.isObject() ? - MConstant::NewConstraintlessObject(alloc, &val.toObject()) : - MConstant::New(alloc, val); - block()->insertBefore(this, ins); - def = ins; - } - initSlot(i, def); - } - } else { - NativeObject& nativeObject = templateObject->as<NativeObject>(); - MOZ_ASSERT(nativeObject.slotSpan() == numSlots()); - - for (size_t i = 0; i < numSlots(); i++) { - Value val = nativeObject.getSlot(i); - MDefinition *def = undefinedVal; - if (!val.isUndefined()) { - MConstant* ins = val.isObject() ? - MConstant::NewConstraintlessObject(alloc, &val.toObject()) : - MConstant::New(alloc, val); - block()->insertBefore(this, ins); - def = ins; - } - initSlot(i, def); + NativeObject& nativeObject = templateObject->as<NativeObject>(); + MOZ_ASSERT(nativeObject.slotSpan() == numSlots()); + + MOZ_ASSERT(templateObject->is<NativeObject>()); + for (size_t i = 0; i < numSlots(); i++) { + Value val = nativeObject.getSlot(i); + MDefinition *def = undefinedVal; + if (!val.isUndefined()) { + MConstant* ins = val.isObject() ? + MConstant::NewConstraintlessObject(alloc, &val.toObject()) : + MConstant::New(alloc, val); + block()->insertBefore(this, ins); + def = ins; } + initSlot(i, def); } return true; } @@ -4948,14 +4860,7 @@ MObjectState::New(TempAllocator& alloc, MDefinition* obj) JSObject* templateObject = templateObjectOf(obj); MOZ_ASSERT(templateObject, "Unexpected object creation."); - OperandIndexMap* operandIndex = nullptr; - if (templateObject->is<UnboxedPlainObject>()) { - operandIndex = new(alloc) OperandIndexMap; - if (!operandIndex || !operandIndex->init(alloc, templateObject)) - return nullptr; - } - - MObjectState* res = new(alloc) MObjectState(templateObject, operandIndex); + MObjectState* res = new(alloc) MObjectState(templateObject); if (!res || !res->init(alloc, obj)) return nullptr; return res; @@ -5862,35 +5767,6 @@ MGetFirstDollarIndex::foldsTo(TempAllocator& alloc) return MConstant::New(alloc, Int32Value(index)); } -MConvertUnboxedObjectToNative* -MConvertUnboxedObjectToNative::New(TempAllocator& alloc, MDefinition* obj, ObjectGroup* group) -{ - MConvertUnboxedObjectToNative* res = new(alloc) MConvertUnboxedObjectToNative(obj, group); - - ObjectGroup* nativeGroup = group->unboxedLayout().nativeGroup(); - - // Make a new type set for the result of this instruction which replaces - // the input group with the native group we will convert it to. - TemporaryTypeSet* types = obj->resultTypeSet(); - if (types && !types->unknownObject()) { - TemporaryTypeSet* newTypes = types->cloneWithoutObjects(alloc.lifoAlloc()); - if (newTypes) { - for (size_t i = 0; i < types->getObjectCount(); i++) { - TypeSet::ObjectKey* key = types->getObject(i); - if (!key) - continue; - if (key->unknownProperties() || !key->isGroup() || key->group() != group) - newTypes->addType(TypeSet::ObjectType(key), alloc.lifoAlloc()); - else - newTypes->addType(TypeSet::ObjectType(nativeGroup), alloc.lifoAlloc()); - } - res->setResultTypeSet(newTypes); - } - } - - return res; -} - bool jit::ElementAccessIsDenseNative(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id) @@ -5910,48 +5786,6 @@ jit::ElementAccessIsDenseNative(CompilerConstraintList* constraints, return clasp && clasp->isNative() && !IsTypedArrayClass(clasp); } -JSValueType -jit::UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* obj, - MDefinition* id) -{ - if (obj->mightBeType(MIRType::String)) - return JSVAL_TYPE_MAGIC; - - if (id && id->type() != MIRType::Int32 && id->type() != MIRType::Double) - return JSVAL_TYPE_MAGIC; - - TemporaryTypeSet* types = obj->resultTypeSet(); - if (!types || types->unknownObject()) - return JSVAL_TYPE_MAGIC; - - JSValueType elementType = JSVAL_TYPE_MAGIC; - for (unsigned i = 0; i < types->getObjectCount(); i++) { - TypeSet::ObjectKey* key = types->getObject(i); - if (!key) - continue; - - if (key->unknownProperties() || !key->isGroup()) - return JSVAL_TYPE_MAGIC; - - if (key->clasp() != &UnboxedArrayObject::class_) - return JSVAL_TYPE_MAGIC; - - const UnboxedLayout &layout = key->group()->unboxedLayout(); - - if (layout.nativeGroup()) - return JSVAL_TYPE_MAGIC; - - if (elementType == layout.elementType() || elementType == JSVAL_TYPE_MAGIC) - elementType = layout.elementType(); - else - return JSVAL_TYPE_MAGIC; - - key->watchStateChangeForUnboxedConvertedToNative(constraints); - } - - return elementType; -} - bool jit::ElementAccessIsTypedArray(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id, @@ -6111,11 +5945,6 @@ ObjectSubsumes(TypeSet::ObjectKey* first, TypeSet::ObjectKey* second) firstElements.maybeTypes()->equals(secondElements.maybeTypes()); } - if (first->clasp() == &UnboxedArrayObject::class_) { - return first->group()->unboxedLayout().elementType() == - second->group()->unboxedLayout().elementType(); - } - return false; } @@ -6355,15 +6184,6 @@ PrototypeHasIndexedProperty(IonBuilder* builder, JSObject* obj) return false; } -// Whether Array.prototype, or an object on its proto chain, has an indexed property. -bool -jit::ArrayPrototypeHasIndexedProperty(IonBuilder* builder, JSScript* script) -{ - if (JSObject* proto = script->global().maybeGetArrayPrototype()) - return PrototypeHasIndexedProperty(builder, proto); - return true; -} - // Whether obj or any of its prototypes have an indexed property. bool jit::TypeCanHaveExtraIndexedProperties(IonBuilder* builder, TemporaryTypeSet* types) @@ -6579,23 +6399,6 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* } } - // Perform additional filtering to make sure that any unboxed property - // being written can accommodate the value. - for (size_t i = 0; i < types->getObjectCount(); i++) { - TypeSet::ObjectKey* key = types->getObject(i); - if (key && key->isGroup() && key->group()->maybeUnboxedLayout()) { - const UnboxedLayout& layout = key->group()->unboxedLayout(); - if (name) { - const UnboxedLayout::Property* property = layout.lookup(name); - if (property && !CanStoreUnboxedType(alloc, property->type, *pvalue)) - return true; - } else { - if (layout.isArray() && !CanStoreUnboxedType(alloc, layout.elementType(), *pvalue)) - return true; - } - } - } - if (success) return false; @@ -6626,17 +6429,6 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* MOZ_ASSERT(excluded); - // If the excluded object is a group with an unboxed layout, make sure it - // does not have a corresponding native group. Objects with the native - // group might appear even though they are not in the type set. - if (excluded->isGroup()) { - if (UnboxedLayout* layout = excluded->group()->maybeUnboxedLayout()) { - if (layout->nativeGroup()) - return true; - excluded->watchStateChangeForUnboxedConvertedToNative(constraints); - } - } - *pobj = AddGroupGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true); return false; } diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 6ec05af764..6c376d5283 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -30,7 +30,6 @@ #include "vm/EnvironmentObject.h" #include "vm/SharedMem.h" #include "vm/TypedArrayCommon.h" -#include "vm/UnboxedObject.h" // Undo windows.h damage on Win64 #undef MemoryBarrier @@ -376,8 +375,7 @@ class AliasSet { Element = 1 << 1, // A Value member of obj->elements or // a typed object. UnboxedElement = 1 << 2, // An unboxed scalar or reference member of - // a typed array, typed object, or unboxed - // object. + // typed object. DynamicSlot = 1 << 3, // A Value member of obj->slots. FixedSlot = 1 << 4, // A Value member of obj->fixedSlots(). DOMProperty = 1 << 5, // A DOM property @@ -434,9 +432,6 @@ class AliasSet { MOZ_ASSERT(flags && !(flags & Store_)); return AliasSet(flags | Store_); } - static uint32_t BoxedOrUnboxedElements(JSValueType type) { - return (type == JSVAL_TYPE_MAGIC) ? Element : UnboxedElement; - } }; typedef Vector<MDefinition*, 6, JitAllocPolicy> MDefinitionVector; @@ -3763,14 +3758,9 @@ class MObjectState { private: uint32_t numSlots_; - uint32_t numFixedSlots_; // valid if isUnboxed() == false. - OperandIndexMap* operandIndex_; // valid if isUnboxed() == true. - - bool isUnboxed() const { - return operandIndex_ != nullptr; - } + uint32_t numFixedSlots_; - MObjectState(JSObject *templateObject, OperandIndexMap* operandIndex); + MObjectState(JSObject *templateObject); explicit MObjectState(MObjectState* state); MOZ_MUST_USE bool init(TempAllocator& alloc, MDefinition* obj); @@ -3795,7 +3785,6 @@ class MObjectState MOZ_MUST_USE bool initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal); size_t numFixedSlots() const { - MOZ_ASSERT(!isUnboxed()); return numFixedSlots_; } size_t numSlots() const { @@ -3831,18 +3820,6 @@ class MObjectState setSlot(slot + numFixedSlots(), def); } - // Interface reserved for unboxed objects. - bool hasOffset(uint32_t offset) const { - MOZ_ASSERT(isUnboxed()); - return offset < operandIndex_->map.length() && operandIndex_->map[offset] != 0; - } - MDefinition* getOffset(uint32_t offset) const { - return getOperand(operandIndex_->map[offset]); - } - void setOffset(uint32_t offset, MDefinition* def) { - replaceOperand(operandIndex_->map[offset], def); - } - MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override; bool canRecoverOnBailout() const override { return true; @@ -4066,14 +4043,18 @@ class MCall uint32_t numActualArgs_; // True if the call is for JSOP_NEW. - bool construct_; + bool construct_:1; - bool needsArgCheck_; + // True if the caller does not use the return value. + bool ignoresReturnValue_:1; - MCall(WrappedFunction* target, uint32_t numActualArgs, bool construct) + bool needsArgCheck_:1; + + MCall(WrappedFunction* target, uint32_t numActualArgs, bool construct, bool ignoresReturnValue) : target_(target), numActualArgs_(numActualArgs), construct_(construct), + ignoresReturnValue_(ignoresReturnValue), needsArgCheck_(true) { setResultType(MIRType::Value); @@ -4082,7 +4063,7 @@ class MCall public: INSTRUCTION_HEADER(Call) static MCall* New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numActualArgs, - bool construct, bool isDOMCall); + bool construct, bool ignoresReturnValue, bool isDOMCall); void initFunction(MDefinition* func) { initOperand(FunctionOperandIndex, func); @@ -4127,6 +4108,10 @@ class MCall return construct_; } + bool ignoresReturnValue() const { + return ignoresReturnValue_; + } + // The number of stack arguments is the max between the number of formal // arguments and the number of actual arguments. The number of stack // argument includes the |undefined| padding added in case of underflow. @@ -4170,7 +4155,7 @@ class MCallDOMNative : public MCall // virtual things from MCall. protected: MCallDOMNative(WrappedFunction* target, uint32_t numActualArgs) - : MCall(target, numActualArgs, false) + : MCall(target, numActualArgs, false, false) { MOZ_ASSERT(getJitInfo()->type() != JSJitInfo::InlinableNative); @@ -4185,7 +4170,8 @@ class MCallDOMNative : public MCall } friend MCall* MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, - size_t numActualArgs, bool construct, bool isDOMCall); + size_t numActualArgs, bool construct, bool ignoresReturnValue, + bool isDOMCall); const JSJitInfo* getJitInfo() const; public: @@ -4200,27 +4186,6 @@ class MCallDOMNative : public MCall virtual void computeMovable() override; }; -// arr.splice(start, deleteCount) with unused return value. -class MArraySplice - : public MTernaryInstruction, - public Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >::Data -{ - private: - - MArraySplice(MDefinition* object, MDefinition* start, MDefinition* deleteCount) - : MTernaryInstruction(object, start, deleteCount) - { } - - public: - INSTRUCTION_HEADER(ArraySplice) - TRIVIAL_NEW_WRAPPERS - NAMED_OPERANDS((0, object), (1, start), (2, deleteCount)) - - bool possiblyCalls() const override { - return true; - } -}; - // fun.apply(self, arguments) class MApplyArgs : public MAryInstruction<3>, @@ -8272,7 +8237,10 @@ class MGetFirstDollarIndex : MUnaryInstruction(str) { setResultType(MIRType::Int32); - setMovable(); + + // Codegen assumes string length > 0 but that's not guaranteed in RegExp. + // Don't allow LICM to move this. + MOZ_ASSERT(!isMovable()); } public: @@ -8743,102 +8711,6 @@ class MSetInitializedLength ALLOW_CLONE(MSetInitializedLength) }; -// Load the length from an unboxed array. -class MUnboxedArrayLength - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - explicit MUnboxedArrayLength(MDefinition* object) - : MUnaryInstruction(object) - { - setResultType(MIRType::Int32); - setMovable(); - } - - public: - INSTRUCTION_HEADER(UnboxedArrayLength) - TRIVIAL_NEW_WRAPPERS - NAMED_OPERANDS((0, object)) - - bool congruentTo(const MDefinition* ins) const override { - return congruentIfOperandsEqual(ins); - } - AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::ObjectFields); - } - - ALLOW_CLONE(MUnboxedArrayLength) -}; - -// Load the initialized length from an unboxed array. -class MUnboxedArrayInitializedLength - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - explicit MUnboxedArrayInitializedLength(MDefinition* object) - : MUnaryInstruction(object) - { - setResultType(MIRType::Int32); - setMovable(); - } - - public: - INSTRUCTION_HEADER(UnboxedArrayInitializedLength) - TRIVIAL_NEW_WRAPPERS - NAMED_OPERANDS((0, object)) - - bool congruentTo(const MDefinition* ins) const override { - return congruentIfOperandsEqual(ins); - } - AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::ObjectFields); - } - - ALLOW_CLONE(MUnboxedArrayInitializedLength) -}; - -// Increment the initialized length of an unboxed array object. -class MIncrementUnboxedArrayInitializedLength - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - explicit MIncrementUnboxedArrayInitializedLength(MDefinition* obj) - : MUnaryInstruction(obj) - {} - - public: - INSTRUCTION_HEADER(IncrementUnboxedArrayInitializedLength) - TRIVIAL_NEW_WRAPPERS - NAMED_OPERANDS((0, object)) - - AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::ObjectFields); - } - - ALLOW_CLONE(MIncrementUnboxedArrayInitializedLength) -}; - -// Set the initialized length of an unboxed array object. -class MSetUnboxedArrayInitializedLength - : public MBinaryInstruction, - public SingleObjectPolicy::Data -{ - explicit MSetUnboxedArrayInitializedLength(MDefinition* obj, MDefinition* length) - : MBinaryInstruction(obj, length) - {} - - public: - INSTRUCTION_HEADER(SetUnboxedArrayInitializedLength) - TRIVIAL_NEW_WRAPPERS - NAMED_OPERANDS((0, object), (1, length)) - - AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::ObjectFields); - } - - ALLOW_CLONE(MSetUnboxedArrayInitializedLength) -}; - // Load the array length from an elements header. class MArrayLength : public MUnaryInstruction, @@ -9332,23 +9204,19 @@ class MLoadElement ALLOW_CLONE(MLoadElement) }; -// Load a value from the elements vector for a dense native or unboxed array. +// Load a value from the elements vector of a native object. // If the index is out-of-bounds, or the indexed slot has a hole, undefined is // returned instead. class MLoadElementHole : public MTernaryInstruction, public SingleObjectPolicy::Data { - // Unboxed element type, JSVAL_TYPE_MAGIC for dense native elements. - JSValueType unboxedType_; - bool needsNegativeIntCheck_; bool needsHoleCheck_; MLoadElementHole(MDefinition* elements, MDefinition* index, MDefinition* initLength, - JSValueType unboxedType, bool needsHoleCheck) + bool needsHoleCheck) : MTernaryInstruction(elements, index, initLength), - unboxedType_(unboxedType), needsNegativeIntCheck_(true), needsHoleCheck_(needsHoleCheck) { @@ -9370,9 +9238,6 @@ class MLoadElementHole TRIVIAL_NEW_WRAPPERS NAMED_OPERANDS((0, elements), (1, index), (2, initLength)) - JSValueType unboxedType() const { - return unboxedType_; - } bool needsNegativeIntCheck() const { return needsNegativeIntCheck_; } @@ -9383,8 +9248,6 @@ class MLoadElementHole if (!ins->isLoadElementHole()) return false; const MLoadElementHole* other = ins->toLoadElementHole(); - if (unboxedType() != other->unboxedType()) - return false; if (needsHoleCheck() != other->needsHoleCheck()) return false; if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) @@ -9392,7 +9255,7 @@ class MLoadElementHole return congruentIfOperandsEqual(other); } AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::BoxedOrUnboxedElements(unboxedType())); + return AliasSet::Load(AliasSet::Element); } void collectRangeInfoPreTrunc() override; @@ -9572,20 +9435,17 @@ class MStoreElement ALLOW_CLONE(MStoreElement) }; -// Like MStoreElement, but supports indexes >= initialized length, and can -// handle unboxed arrays. The downside is that we cannot hoist the elements -// vector and bounds check, since this instruction may update the (initialized) -// length and reallocate the elements vector. +// Like MStoreElement, but supports indexes >= initialized length. The downside +// is that we cannot hoist the elements vector and bounds check, since this +// instruction may update the (initialized) length and reallocate the elements +// vector. class MStoreElementHole : public MAryInstruction<4>, public MStoreElementCommon, public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >::Data { - JSValueType unboxedType_; - MStoreElementHole(MDefinition* object, MDefinition* elements, - MDefinition* index, MDefinition* value, JSValueType unboxedType) - : unboxedType_(unboxedType) + MDefinition* index, MDefinition* value) { initOperand(0, object); initOperand(1, elements); @@ -9600,14 +9460,10 @@ class MStoreElementHole TRIVIAL_NEW_WRAPPERS NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value)) - JSValueType unboxedType() const { - return unboxedType_; - } AliasSet getAliasSet() const override { // StoreElementHole can update the initialized length, the array length // or reallocate obj->elements. - return AliasSet::Store(AliasSet::ObjectFields | - AliasSet::BoxedOrUnboxedElements(unboxedType())); + return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element); } ALLOW_CLONE(MStoreElementHole) @@ -9620,13 +9476,11 @@ class MFallibleStoreElement public MStoreElementCommon, public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >::Data { - JSValueType unboxedType_; bool strict_; MFallibleStoreElement(MDefinition* object, MDefinition* elements, MDefinition* index, MDefinition* value, - JSValueType unboxedType, bool strict) - : unboxedType_(unboxedType) + bool strict) { initOperand(0, object); initOperand(1, elements); @@ -9642,12 +9496,8 @@ class MFallibleStoreElement TRIVIAL_NEW_WRAPPERS NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value)) - JSValueType unboxedType() const { - return unboxedType_; - } AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::ObjectFields | - AliasSet::BoxedOrUnboxedElements(unboxedType())); + return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element); } bool strict() const { return strict_; @@ -9739,59 +9589,6 @@ class MStoreUnboxedString ALLOW_CLONE(MStoreUnboxedString) }; -// Passes through an object, after ensuring it is converted from an unboxed -// object to a native representation. -class MConvertUnboxedObjectToNative - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - CompilerObjectGroup group_; - - explicit MConvertUnboxedObjectToNative(MDefinition* obj, ObjectGroup* group) - : MUnaryInstruction(obj), - group_(group) - { - setGuard(); - setMovable(); - setResultType(MIRType::Object); - } - - public: - INSTRUCTION_HEADER(ConvertUnboxedObjectToNative) - NAMED_OPERANDS((0, object)) - - static MConvertUnboxedObjectToNative* New(TempAllocator& alloc, MDefinition* obj, - ObjectGroup* group); - - ObjectGroup* group() const { - return group_; - } - bool congruentTo(const MDefinition* ins) const override { - if (!congruentIfOperandsEqual(ins)) - return false; - return ins->toConvertUnboxedObjectToNative()->group() == group(); - } - AliasSet getAliasSet() const override { - // This instruction can read and write to all parts of the object, but - // is marked as non-effectful so it can be consolidated by LICM and GVN - // and avoid inhibiting other optimizations. - // - // This is valid to do because when unboxed objects might have a native - // group they can be converted to, we do not optimize accesses to the - // unboxed objects and do not guard on their group or shape (other than - // in this opcode). - // - // Later accesses can assume the object has a native representation - // and optimize accordingly. Those accesses cannot be reordered before - // this instruction, however. This is prevented by chaining this - // instruction with the object itself, in the same way as MBoundsCheck. - return AliasSet::None(); - } - bool appendRoots(MRootList& roots) const override { - return roots.append(group_); - } -}; - // Array.prototype.pop or Array.prototype.shift on a dense array. class MArrayPopShift : public MUnaryInstruction, @@ -9805,13 +9602,12 @@ class MArrayPopShift private: Mode mode_; - JSValueType unboxedType_; bool needsHoleCheck_; bool maybeUndefined_; - MArrayPopShift(MDefinition* object, Mode mode, JSValueType unboxedType, + MArrayPopShift(MDefinition* object, Mode mode, bool needsHoleCheck, bool maybeUndefined) - : MUnaryInstruction(object), mode_(mode), unboxedType_(unboxedType), + : MUnaryInstruction(object), mode_(mode), needsHoleCheck_(needsHoleCheck), maybeUndefined_(maybeUndefined) { } @@ -9829,12 +9625,8 @@ class MArrayPopShift bool mode() const { return mode_; } - JSValueType unboxedType() const { - return unboxedType_; - } AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::ObjectFields | - AliasSet::BoxedOrUnboxedElements(unboxedType())); + return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element); } ALLOW_CLONE(MArrayPopShift) @@ -9845,10 +9637,8 @@ class MArrayPush : public MBinaryInstruction, public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data { - JSValueType unboxedType_; - - MArrayPush(MDefinition* object, MDefinition* value, JSValueType unboxedType) - : MBinaryInstruction(object, value), unboxedType_(unboxedType) + MArrayPush(MDefinition* object, MDefinition* value) + : MBinaryInstruction(object, value) { setResultType(MIRType::Int32); } @@ -9858,12 +9648,8 @@ class MArrayPush TRIVIAL_NEW_WRAPPERS NAMED_OPERANDS((0, object), (1, value)) - JSValueType unboxedType() const { - return unboxedType_; - } AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::ObjectFields | - AliasSet::BoxedOrUnboxedElements(unboxedType())); + return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element); } void computeRange(TempAllocator& alloc) override; @@ -9877,15 +9663,13 @@ class MArraySlice { CompilerObject templateObj_; gc::InitialHeap initialHeap_; - JSValueType unboxedType_; MArraySlice(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* begin, MDefinition* end, - JSObject* templateObj, gc::InitialHeap initialHeap, JSValueType unboxedType) + JSObject* templateObj, gc::InitialHeap initialHeap) : MTernaryInstruction(obj, begin, end), templateObj_(templateObj), - initialHeap_(initialHeap), - unboxedType_(unboxedType) + initialHeap_(initialHeap) { setResultType(MIRType::Object); } @@ -9903,14 +9687,6 @@ class MArraySlice return initialHeap_; } - JSValueType unboxedType() const { - return unboxedType_; - } - - AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedType()) | - AliasSet::ObjectFields); - } bool possiblyCalls() const override { return true; } @@ -11175,11 +10951,6 @@ class MGuardShape setMovable(); setResultType(MIRType::Object); setResultTypeSet(obj->resultTypeSet()); - - // Disallow guarding on unboxed object shapes. The group is better to - // guard on, and guarding on the shape can interact badly with - // MConvertUnboxedObjectToNative. - MOZ_ASSERT(shape->getObjectClass() != &UnboxedPlainObject::class_); } public: @@ -11274,11 +11045,6 @@ class MGuardObjectGroup setGuard(); setMovable(); setResultType(MIRType::Object); - - // Unboxed groups which might be converted to natives can't be guarded - // on, due to MConvertUnboxedObjectToNative. - MOZ_ASSERT_IF(group->maybeUnboxedLayoutDontCheckGeneration(), - !group->unboxedLayoutDontCheckGeneration().nativeGroup()); } public: @@ -11387,73 +11153,6 @@ class MGuardClass ALLOW_CLONE(MGuardClass) }; -// Guard on the presence or absence of an unboxed object's expando. -class MGuardUnboxedExpando - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - bool requireExpando_; - BailoutKind bailoutKind_; - - MGuardUnboxedExpando(MDefinition* obj, bool requireExpando, BailoutKind bailoutKind) - : MUnaryInstruction(obj), - requireExpando_(requireExpando), - bailoutKind_(bailoutKind) - { - setGuard(); - setMovable(); - setResultType(MIRType::Object); - } - - public: - INSTRUCTION_HEADER(GuardUnboxedExpando) - TRIVIAL_NEW_WRAPPERS - NAMED_OPERANDS((0, object)) - - bool requireExpando() const { - return requireExpando_; - } - BailoutKind bailoutKind() const { - return bailoutKind_; - } - bool congruentTo(const MDefinition* ins) const override { - if (!congruentIfOperandsEqual(ins)) - return false; - if (requireExpando() != ins->toGuardUnboxedExpando()->requireExpando()) - return false; - return true; - } - AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::ObjectFields); - } -}; - -// Load an unboxed plain object's expando. -class MLoadUnboxedExpando - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - private: - explicit MLoadUnboxedExpando(MDefinition* object) - : MUnaryInstruction(object) - { - setResultType(MIRType::Object); - setMovable(); - } - - public: - INSTRUCTION_HEADER(LoadUnboxedExpando) - TRIVIAL_NEW_WRAPPERS - NAMED_OPERANDS((0, object)) - - bool congruentTo(const MDefinition* ins) const override { - return congruentIfOperandsEqual(ins); - } - AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::ObjectFields); - } -}; - // Load from vp[slot] (slots that are not inline in an object). class MLoadSlot : public MUnaryInstruction, @@ -11834,7 +11533,8 @@ class MCallGetProperty AliasSet getAliasSet() const override { if (!idempotent_) return AliasSet::Store(AliasSet::Any); - return AliasSet::None(); + return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | + AliasSet::DynamicSlot); } bool possiblyCalls() const override { return true; @@ -12355,15 +12055,13 @@ class MInArray { bool needsHoleCheck_; bool needsNegativeIntCheck_; - JSValueType unboxedType_; MInArray(MDefinition* elements, MDefinition* index, MDefinition* initLength, MDefinition* object, - bool needsHoleCheck, JSValueType unboxedType) + bool needsHoleCheck) : MQuaternaryInstruction(elements, index, initLength, object), needsHoleCheck_(needsHoleCheck), - needsNegativeIntCheck_(true), - unboxedType_(unboxedType) + needsNegativeIntCheck_(true) { setResultType(MIRType::Boolean); setMovable(); @@ -12383,9 +12081,6 @@ class MInArray bool needsNegativeIntCheck() const { return needsNegativeIntCheck_; } - JSValueType unboxedType() const { - return unboxedType_; - } void collectRangeInfoPreTrunc() override; AliasSet getAliasSet() const override { return AliasSet::Load(AliasSet::Element); @@ -12398,8 +12093,6 @@ class MInArray return false; if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) return false; - if (unboxedType() != other->unboxedType()) - return false; return congruentIfOperandsEqual(other); } }; @@ -14300,8 +13993,6 @@ MDefinition::maybeConstantValue() bool ElementAccessIsDenseNative(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id); -JSValueType UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* obj, - MDefinition* id); bool ElementAccessIsTypedArray(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id, Scalar::Type* arrayType); @@ -14333,7 +14024,6 @@ bool PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* MBasicBlock* current, MDefinition** pobj, PropertyName* name, MDefinition** pvalue, bool canModify, MIRType implicitType = MIRType::None); -bool ArrayPrototypeHasIndexedProperty(IonBuilder* builder, JSScript* script); bool TypeCanHaveExtraIndexedProperties(IonBuilder* builder, TemporaryTypeSet* types); inline MIRType diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index fddc1e637d..9e460a2ba6 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -69,7 +69,6 @@ namespace jit { _(Call) \ _(ApplyArgs) \ _(ApplyArray) \ - _(ArraySplice) \ _(Bail) \ _(Unreachable) \ _(EncodeSnapshot) \ @@ -187,8 +186,6 @@ namespace jit { _(GuardObjectGroup) \ _(GuardObjectIdentity) \ _(GuardClass) \ - _(GuardUnboxedExpando) \ - _(LoadUnboxedExpando) \ _(ArrayLength) \ _(SetArrayLength) \ _(GetNextEntryForIterator) \ @@ -200,10 +197,6 @@ namespace jit { _(SetTypedObjectOffset) \ _(InitializedLength) \ _(SetInitializedLength) \ - _(UnboxedArrayLength) \ - _(UnboxedArrayInitializedLength) \ - _(IncrementUnboxedArrayInitializedLength) \ - _(SetUnboxedArrayInitializedLength) \ _(Not) \ _(BoundsCheck) \ _(BoundsCheckLower) \ @@ -219,7 +212,6 @@ namespace jit { _(StoreUnboxedScalar) \ _(StoreUnboxedObjectOrNull) \ _(StoreUnboxedString) \ - _(ConvertUnboxedObjectToNative) \ _(ArrayPopShift) \ _(ArrayPush) \ _(ArraySlice) \ diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 9dbbe76242..a739b93256 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -126,20 +126,14 @@ MacroAssembler::guardTypeSetMightBeIncomplete(TypeSet* types, Register obj, Regi { // Type set guards might miss when an object's group changes. In this case // either its old group's properties will become unknown, or it will change - // to a native object with an original unboxed group. Jump to label if this - // might have happened for the input object. + // to a native object. Jump to label if this might have happened for the + // input object. if (types->unknownObject()) { jump(label); return; } - loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch); - load32(Address(scratch, ObjectGroup::offsetOfFlags()), scratch); - and32(Imm32(OBJECT_FLAG_ADDENDUM_MASK), scratch); - branch32(Assembler::Equal, - scratch, Imm32(ObjectGroup::addendumOriginalUnboxedGroupValue()), label); - for (size_t i = 0; i < types->getObjectCount(); i++) { if (JSObject* singleton = types->getSingletonNoBarrier(i)) { movePtr(ImmGCPtr(singleton), scratch); @@ -468,268 +462,6 @@ template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const A template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const BaseIndex& src, const ValueOperand& dest, bool allowDouble, Register temp, Label* fail); -template <typename T> -void -MacroAssembler::loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output) -{ - switch (type) { - case JSVAL_TYPE_INT32: { - // Handle loading an int32 into a double reg. - if (output.type() == MIRType::Double) { - convertInt32ToDouble(address, output.typedReg().fpu()); - break; - } - MOZ_FALLTHROUGH; - } - - case JSVAL_TYPE_BOOLEAN: - case JSVAL_TYPE_STRING: { - Register outReg; - if (output.hasValue()) { - outReg = output.valueReg().scratchReg(); - } else { - MOZ_ASSERT(output.type() == MIRTypeFromValueType(type)); - outReg = output.typedReg().gpr(); - } - - switch (type) { - case JSVAL_TYPE_BOOLEAN: - load8ZeroExtend(address, outReg); - break; - case JSVAL_TYPE_INT32: - load32(address, outReg); - break; - case JSVAL_TYPE_STRING: - loadPtr(address, outReg); - break; - default: - MOZ_CRASH(); - } - - if (output.hasValue()) - tagValue(type, outReg, output.valueReg()); - break; - } - - case JSVAL_TYPE_OBJECT: - if (output.hasValue()) { - Register scratch = output.valueReg().scratchReg(); - loadPtr(address, scratch); - - Label notNull, done; - branchPtr(Assembler::NotEqual, scratch, ImmWord(0), ¬Null); - - moveValue(NullValue(), output.valueReg()); - jump(&done); - - bind(¬Null); - tagValue(JSVAL_TYPE_OBJECT, scratch, output.valueReg()); - - bind(&done); - } else { - // Reading null can't be possible here, as otherwise the result - // would be a value (either because null has been read before or - // because there is a barrier). - Register reg = output.typedReg().gpr(); - loadPtr(address, reg); -#ifdef DEBUG - Label ok; - branchTestPtr(Assembler::NonZero, reg, reg, &ok); - assumeUnreachable("Null not possible"); - bind(&ok); -#endif - } - break; - - case JSVAL_TYPE_DOUBLE: - // Note: doubles in unboxed objects are not accessed through other - // views and do not need canonicalization. - if (output.hasValue()) - loadValue(address, output.valueReg()); - else - loadDouble(address, output.typedReg().fpu()); - break; - - default: - MOZ_CRASH(); - } -} - -template void -MacroAssembler::loadUnboxedProperty(Address address, JSValueType type, - TypedOrValueRegister output); - -template void -MacroAssembler::loadUnboxedProperty(BaseIndex address, JSValueType type, - TypedOrValueRegister output); - -static void -StoreUnboxedFailure(MacroAssembler& masm, Label* failure) -{ - // Storing a value to an unboxed property is a fallible operation and - // the caller must provide a failure label if a particular unboxed store - // might fail. Sometimes, however, a store that cannot succeed (such as - // storing a string to an int32 property) will be marked as infallible. - // This can only happen if the code involved is unreachable. - if (failure) - masm.jump(failure); - else - masm.assumeUnreachable("Incompatible write to unboxed property"); -} - -template <typename T> -void -MacroAssembler::storeUnboxedProperty(T address, JSValueType type, - const ConstantOrRegister& value, Label* failure) -{ - switch (type) { - case JSVAL_TYPE_BOOLEAN: - if (value.constant()) { - if (value.value().isBoolean()) - store8(Imm32(value.value().toBoolean()), address); - else - StoreUnboxedFailure(*this, failure); - } else if (value.reg().hasTyped()) { - if (value.reg().type() == MIRType::Boolean) - store8(value.reg().typedReg().gpr(), address); - else - StoreUnboxedFailure(*this, failure); - } else { - if (failure) - branchTestBoolean(Assembler::NotEqual, value.reg().valueReg(), failure); - storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 1); - } - break; - - case JSVAL_TYPE_INT32: - if (value.constant()) { - if (value.value().isInt32()) - store32(Imm32(value.value().toInt32()), address); - else - StoreUnboxedFailure(*this, failure); - } else if (value.reg().hasTyped()) { - if (value.reg().type() == MIRType::Int32) - store32(value.reg().typedReg().gpr(), address); - else - StoreUnboxedFailure(*this, failure); - } else { - if (failure) - branchTestInt32(Assembler::NotEqual, value.reg().valueReg(), failure); - storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 4); - } - break; - - case JSVAL_TYPE_DOUBLE: - if (value.constant()) { - if (value.value().isNumber()) { - loadConstantDouble(value.value().toNumber(), ScratchDoubleReg); - storeDouble(ScratchDoubleReg, address); - } else { - StoreUnboxedFailure(*this, failure); - } - } else if (value.reg().hasTyped()) { - if (value.reg().type() == MIRType::Int32) { - convertInt32ToDouble(value.reg().typedReg().gpr(), ScratchDoubleReg); - storeDouble(ScratchDoubleReg, address); - } else if (value.reg().type() == MIRType::Double) { - storeDouble(value.reg().typedReg().fpu(), address); - } else { - StoreUnboxedFailure(*this, failure); - } - } else { - ValueOperand reg = value.reg().valueReg(); - Label notInt32, end; - branchTestInt32(Assembler::NotEqual, reg, ¬Int32); - int32ValueToDouble(reg, ScratchDoubleReg); - storeDouble(ScratchDoubleReg, address); - jump(&end); - bind(¬Int32); - if (failure) - branchTestDouble(Assembler::NotEqual, reg, failure); - storeValue(reg, address); - bind(&end); - } - break; - - case JSVAL_TYPE_OBJECT: - if (value.constant()) { - if (value.value().isObjectOrNull()) - storePtr(ImmGCPtr(value.value().toObjectOrNull()), address); - else - StoreUnboxedFailure(*this, failure); - } else if (value.reg().hasTyped()) { - MOZ_ASSERT(value.reg().type() != MIRType::Null); - if (value.reg().type() == MIRType::Object) - storePtr(value.reg().typedReg().gpr(), address); - else - StoreUnboxedFailure(*this, failure); - } else { - if (failure) { - Label ok; - branchTestNull(Assembler::Equal, value.reg().valueReg(), &ok); - branchTestObject(Assembler::NotEqual, value.reg().valueReg(), failure); - bind(&ok); - } - storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ sizeof(uintptr_t)); - } - break; - - case JSVAL_TYPE_STRING: - if (value.constant()) { - if (value.value().isString()) - storePtr(ImmGCPtr(value.value().toString()), address); - else - StoreUnboxedFailure(*this, failure); - } else if (value.reg().hasTyped()) { - if (value.reg().type() == MIRType::String) - storePtr(value.reg().typedReg().gpr(), address); - else - StoreUnboxedFailure(*this, failure); - } else { - if (failure) - branchTestString(Assembler::NotEqual, value.reg().valueReg(), failure); - storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ sizeof(uintptr_t)); - } - break; - - default: - MOZ_CRASH(); - } -} - -template void -MacroAssembler::storeUnboxedProperty(Address address, JSValueType type, - const ConstantOrRegister& value, Label* failure); - -template void -MacroAssembler::storeUnboxedProperty(BaseIndex address, JSValueType type, - const ConstantOrRegister& value, Label* failure); - -void -MacroAssembler::checkUnboxedArrayCapacity(Register obj, const RegisterOrInt32Constant& index, - Register temp, Label* failure) -{ - Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength()); - - Label capacityIsIndex, done; - load32(initLengthAddr, temp); - branchTest32(Assembler::NonZero, temp, Imm32(UnboxedArrayObject::CapacityMask), &capacityIsIndex); - branch32(Assembler::BelowOrEqual, lengthAddr, index, failure); - jump(&done); - bind(&capacityIsIndex); - - // Do a partial shift so that we can get an absolute offset from the base - // of CapacityArray to use. - JS_STATIC_ASSERT(sizeof(UnboxedArrayObject::CapacityArray[0]) == 4); - rshiftPtr(Imm32(UnboxedArrayObject::CapacityShift - 2), temp); - and32(Imm32(~0x3), temp); - - addPtr(ImmPtr(&UnboxedArrayObject::CapacityArray), temp); - branch32(Assembler::BelowOrEqual, Address(temp, 0), index, failure); - bind(&done); -} - // Inlined version of gc::CheckAllocatorState that checks the bare essentials // and bails for anything that cannot be handled with our jit allocators. void @@ -1277,20 +1009,6 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj, nbytes = (nbytes < sizeof(uintptr_t)) ? 0 : nbytes - sizeof(uintptr_t); offset += sizeof(uintptr_t); } - } else if (templateObj->is<UnboxedPlainObject>()) { - storePtr(ImmWord(0), Address(obj, UnboxedPlainObject::offsetOfExpando())); - if (initContents) - initUnboxedObjectContents(obj, &templateObj->as<UnboxedPlainObject>()); - } else if (templateObj->is<UnboxedArrayObject>()) { - MOZ_ASSERT(templateObj->as<UnboxedArrayObject>().hasInlineElements()); - int elementsOffset = UnboxedArrayObject::offsetOfInlineElements(); - computeEffectiveAddress(Address(obj, elementsOffset), temp); - storePtr(temp, Address(obj, UnboxedArrayObject::offsetOfElements())); - store32(Imm32(templateObj->as<UnboxedArrayObject>().length()), - Address(obj, UnboxedArrayObject::offsetOfLength())); - uint32_t capacityIndex = templateObj->as<UnboxedArrayObject>().capacityIndex(); - store32(Imm32(capacityIndex << UnboxedArrayObject::CapacityShift), - Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); } else { MOZ_CRASH("Unknown object"); } @@ -1312,29 +1030,6 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj, } void -MacroAssembler::initUnboxedObjectContents(Register object, UnboxedPlainObject* templateObject) -{ - const UnboxedLayout& layout = templateObject->layoutDontCheckGeneration(); - - // Initialize reference fields of the object, per UnboxedPlainObject::create. - if (const int32_t* list = layout.traceList()) { - while (*list != -1) { - storePtr(ImmGCPtr(GetJitContext()->runtime->names().empty), - Address(object, UnboxedPlainObject::offsetOfData() + *list)); - list++; - } - list++; - while (*list != -1) { - storePtr(ImmWord(0), - Address(object, UnboxedPlainObject::offsetOfData() + *list)); - list++; - } - // Unboxed objects don't have Values to initialize. - MOZ_ASSERT(*(list + 1) == -1); - } -} - -void MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result, Label* fail) { @@ -2214,12 +1909,6 @@ MacroAssembler::finish() } MacroAssemblerSpecific::finish(); - - MOZ_RELEASE_ASSERT(size() <= MaxCodeBytesPerProcess, - "AssemblerBuffer should ensure we don't exceed MaxCodeBytesPerProcess"); - - if (bytesNeeded() > MaxCodeBytesPerProcess) - setOOM(); } void diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index b6616321c3..d5cc95839d 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -36,7 +36,6 @@ #include "vm/ProxyObject.h" #include "vm/Shape.h" #include "vm/TypedArrayObject.h" -#include "vm/UnboxedObject.h" using mozilla::FloatingPoint; @@ -1626,20 +1625,6 @@ class MacroAssembler : public MacroAssemblerSpecific void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address& dest, unsigned numElems = 0); - // Load a property from an UnboxedPlainObject or UnboxedArrayObject. - template <typename T> - void loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output); - - // Store a property to an UnboxedPlainObject, without triggering barriers. - // If failure is null, the value definitely has a type suitable for storing - // in the property. - template <typename T> - void storeUnboxedProperty(T address, JSValueType type, - const ConstantOrRegister& value, Label* failure); - - void checkUnboxedArrayCapacity(Register obj, const RegisterOrInt32Constant& index, - Register temp, Label* failure); - Register extractString(const Address& address, Register scratch) { return extractObject(address, scratch); } @@ -1716,8 +1701,6 @@ class MacroAssembler : public MacroAssemblerSpecific LiveRegisterSet liveRegs, Label* fail, TypedArrayObject* templateObj, TypedArrayLength lengthKind); - void initUnboxedObjectContents(Register object, UnboxedPlainObject* templateObject); - void newGCString(Register result, Register temp, Label* fail); void newGCFatInlineString(Register result, Register temp, Label* fail); diff --git a/js/src/jit/OptimizationTracking.cpp b/js/src/jit/OptimizationTracking.cpp index 308def0411..7d72795a01 100644 --- a/js/src/jit/OptimizationTracking.cpp +++ b/js/src/jit/OptimizationTracking.cpp @@ -844,8 +844,6 @@ MaybeConstructorFromType(TypeSet::Type ty) return nullptr; ObjectGroup* obj = ty.group(); TypeNewScript* newScript = obj->newScript(); - if (!newScript && obj->maybeUnboxedLayout()) - newScript = obj->unboxedLayout().newScript(); return newScript ? newScript->function() : nullptr; } diff --git a/js/src/jit/ProcessExecutableMemory.cpp b/js/src/jit/ProcessExecutableMemory.cpp index 301541541e..71c2ab0dce 100644 --- a/js/src/jit/ProcessExecutableMemory.cpp +++ b/js/src/jit/ProcessExecutableMemory.cpp @@ -385,6 +385,14 @@ class PageBitSet #endif }; +// Limit on the number of bytes of executable memory to prevent JIT spraying +// attacks. +#if JS_BITS_PER_WORD == 32 +static const size_t MaxCodeBytesPerProcess = 128 * 1024 * 1024; +#else +static const size_t MaxCodeBytesPerProcess = 1 * 1024 * 1024 * 1024; +#endif + // Per-process executable memory allocator. It reserves a block of memory of // MaxCodeBytesPerProcess bytes, then allocates/deallocates pages from that. // diff --git a/js/src/jit/ProcessExecutableMemory.h b/js/src/jit/ProcessExecutableMemory.h index a0e2fab985..078ce7cb75 100644 --- a/js/src/jit/ProcessExecutableMemory.h +++ b/js/src/jit/ProcessExecutableMemory.h @@ -17,14 +17,6 @@ namespace jit { // alignment though. static const size_t ExecutableCodePageSize = 64 * 1024; -// Limit on the number of bytes of executable memory to prevent JIT spraying -// attacks. -#if JS_BITS_PER_WORD == 32 -static const size_t MaxCodeBytesPerProcess = 128 * 1024 * 1024; -#else -static const size_t MaxCodeBytesPerProcess = 1 * 1024 * 1024 * 1024; -#endif - enum class ProtectionSetting { Protected, // Not readable, writable, or executable. Writable, diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 95484c2494..d64f9b8ca4 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -2167,7 +2167,7 @@ RangeAnalysis::analyzeLoopPhi(MBasicBlock* header, LoopIterationBound* loopBound if (initial->block()->isMarked()) return; - SimpleLinearSum modified = ExtractLinearSum(phi->getLoopBackedgeOperand()); + SimpleLinearSum modified = ExtractLinearSum(phi->getLoopBackedgeOperand(), MathSpace::Infinite); if (modified.term != phi || modified.constant == 0) return; diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp index 13bf9224b3..793b631df0 100644 --- a/js/src/jit/Recover.cpp +++ b/js/src/jit/Recover.cpp @@ -1354,7 +1354,7 @@ RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const RootedValue result(cx); RootedObjectGroup group(cx, templateObject->group()); - JSObject* resultObject = NewFullyAllocatedArrayTryUseGroup(cx, group, count_); + ArrayObject* resultObject = NewFullyAllocatedArrayTryUseGroup(cx, group, count_); if (!resultObject) return false; @@ -1539,37 +1539,12 @@ RObjectState::recover(JSContext* cx, SnapshotIterator& iter) const RootedObject object(cx, &iter.read().toObject()); RootedValue val(cx); - if (object->is<UnboxedPlainObject>()) { - const UnboxedLayout& layout = object->as<UnboxedPlainObject>().layout(); + RootedNativeObject nativeObject(cx, &object->as<NativeObject>()); + MOZ_ASSERT(nativeObject->slotSpan() == numSlots()); - RootedId id(cx); - RootedValue receiver(cx, ObjectValue(*object)); - const UnboxedLayout::PropertyVector& properties = layout.properties(); - for (size_t i = 0; i < properties.length(); i++) { - val = iter.read(); - - // This is the default placeholder value of MObjectState, when no - // properties are defined yet. - if (val.isUndefined()) - continue; - - id = NameToId(properties[i].name); - ObjectOpResult result; - - // SetProperty can only fail due to OOM. - if (!SetProperty(cx, object, id, val, receiver, result)) - return false; - if (!result) - return result.reportError(cx, object, id); - } - } else { - RootedNativeObject nativeObject(cx, &object->as<NativeObject>()); - MOZ_ASSERT(nativeObject->slotSpan() == numSlots()); - - for (size_t i = 0; i < numSlots(); i++) { - val = iter.read(); - nativeObject->setSlot(i, val); - } + for (size_t i = 0; i < numSlots(); i++) { + val = iter.read(); + nativeObject->setSlot(i, val); } val.setObject(*object); diff --git a/js/src/jit/ScalarReplacement.cpp b/js/src/jit/ScalarReplacement.cpp index 4614b21627..97ba52349f 100644 --- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -13,7 +13,6 @@ #include "jit/MIR.h" #include "jit/MIRGenerator.h" #include "jit/MIRGraph.h" -#include "vm/UnboxedObject.h" #include "jsobjinlines.h" @@ -183,25 +182,6 @@ IsObjectEscaped(MInstruction* ins, JSObject* objDefault) JitSpewDef(JitSpew_Escape, "is escaped by\n", def); return true; - case MDefinition::Op_LoadUnboxedScalar: - case MDefinition::Op_StoreUnboxedScalar: - case MDefinition::Op_LoadUnboxedObjectOrNull: - case MDefinition::Op_StoreUnboxedObjectOrNull: - case MDefinition::Op_LoadUnboxedString: - case MDefinition::Op_StoreUnboxedString: - // Not escaped if it is the first argument. - if (def->indexOf(*i) != 0) { - JitSpewDef(JitSpew_Escape, "is escaped by\n", def); - return true; - } - - if (!def->getOperand(1)->isConstant()) { - JitSpewDef(JitSpew_Escape, "is addressed with unknown index\n", def); - return true; - } - - break; - case MDefinition::Op_PostWriteBarrier: break; @@ -305,16 +285,6 @@ class ObjectMemoryView : public MDefinitionVisitorDefaultNoop void visitGuardShape(MGuardShape* ins); void visitFunctionEnvironment(MFunctionEnvironment* ins); void visitLambda(MLambda* ins); - void visitStoreUnboxedScalar(MStoreUnboxedScalar* ins); - void visitLoadUnboxedScalar(MLoadUnboxedScalar* ins); - void visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins); - void visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull* ins); - void visitStoreUnboxedString(MStoreUnboxedString* ins); - void visitLoadUnboxedString(MLoadUnboxedString* ins); - - private: - void storeOffset(MInstruction* ins, size_t offset, MDefinition* value); - void loadOffset(MInstruction* ins, size_t offset); }; const char* ObjectMemoryView::phaseName = "Scalar Replacement of Object"; @@ -656,121 +626,6 @@ ObjectMemoryView::visitLambda(MLambda* ins) ins->setIncompleteObject(); } -static size_t -GetOffsetOf(MDefinition* index, size_t width, int32_t baseOffset) -{ - int32_t idx = index->toConstant()->toInt32(); - MOZ_ASSERT(idx >= 0); - MOZ_ASSERT(baseOffset >= 0 && size_t(baseOffset) >= UnboxedPlainObject::offsetOfData()); - return idx * width + baseOffset - UnboxedPlainObject::offsetOfData(); -} - -static size_t -GetOffsetOf(MDefinition* index, Scalar::Type type, int32_t baseOffset) -{ - return GetOffsetOf(index, Scalar::byteSize(type), baseOffset); -} - -void -ObjectMemoryView::storeOffset(MInstruction* ins, size_t offset, MDefinition* value) -{ - // Clone the state and update the slot value. - MOZ_ASSERT(state_->hasOffset(offset)); - state_ = BlockState::Copy(alloc_, state_); - if (!state_) { - oom_ = true; - return; - } - - state_->setOffset(offset, value); - ins->block()->insertBefore(ins, state_); - - // Remove original instruction. - ins->block()->discard(ins); -} - -void -ObjectMemoryView::loadOffset(MInstruction* ins, size_t offset) -{ - // Replace load by the slot value. - MOZ_ASSERT(state_->hasOffset(offset)); - ins->replaceAllUsesWith(state_->getOffset(offset)); - - // Remove original instruction. - ins->block()->discard(ins); -} - -void -ObjectMemoryView::visitStoreUnboxedScalar(MStoreUnboxedScalar* ins) -{ - // Skip stores made on other objects. - if (ins->elements() != obj_) - return; - - size_t offset = GetOffsetOf(ins->index(), ins->storageType(), ins->offsetAdjustment()); - storeOffset(ins, offset, ins->value()); -} - -void -ObjectMemoryView::visitLoadUnboxedScalar(MLoadUnboxedScalar* ins) -{ - // Skip loads made on other objects. - if (ins->elements() != obj_) - return; - - // Replace load by the slot value. - size_t offset = GetOffsetOf(ins->index(), ins->storageType(), ins->offsetAdjustment()); - loadOffset(ins, offset); -} - -void -ObjectMemoryView::visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins) -{ - // Skip stores made on other objects. - if (ins->elements() != obj_) - return; - - // Clone the state and update the slot value. - size_t offset = GetOffsetOf(ins->index(), sizeof(uintptr_t), ins->offsetAdjustment()); - storeOffset(ins, offset, ins->value()); -} - -void -ObjectMemoryView::visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull* ins) -{ - // Skip loads made on other objects. - if (ins->elements() != obj_) - return; - - // Replace load by the slot value. - size_t offset = GetOffsetOf(ins->index(), sizeof(uintptr_t), ins->offsetAdjustment()); - loadOffset(ins, offset); -} - -void -ObjectMemoryView::visitStoreUnboxedString(MStoreUnboxedString* ins) -{ - // Skip stores made on other objects. - if (ins->elements() != obj_) - return; - - // Clone the state and update the slot value. - size_t offset = GetOffsetOf(ins->index(), sizeof(uintptr_t), ins->offsetAdjustment()); - storeOffset(ins, offset, ins->value()); -} - -void -ObjectMemoryView::visitLoadUnboxedString(MLoadUnboxedString* ins) -{ - // Skip loads made on other objects. - if (ins->elements() != obj_) - return; - - // Replace load by the slot value. - size_t offset = GetOffsetOf(ins->index(), sizeof(uintptr_t), ins->offsetAdjustment()); - loadOffset(ins, offset); -} - static bool IndexOf(MDefinition* ins, int32_t* res) { @@ -907,11 +762,6 @@ IsArrayEscaped(MInstruction* ins) return true; } - if (obj->is<UnboxedArrayObject>()) { - JitSpew(JitSpew_Escape, "Template object is an unboxed plain object."); - return true; - } - if (length >= 16) { JitSpew(JitSpew_Escape, "Array has too many elements"); return true; diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index 767cff661a..05a95824fb 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -27,6 +27,7 @@ #endif #include "jit/VMFunctions.h" #include "vm/Interpreter.h" +#include "vm/NativeObject-inl.h" #include "jit/MacroAssembler-inl.h" #include "vm/Interpreter-inl.h" @@ -285,11 +286,6 @@ ICStub::trace(JSTracer* trc) TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-dense-shape"); break; } - case ICStub::GetElem_UnboxedArray: { - ICGetElem_UnboxedArray* getElemStub = toGetElem_UnboxedArray(); - TraceEdge(trc, &getElemStub->group(), "baseline-getelem-unboxed-array-group"); - break; - } case ICStub::GetElem_TypedArray: { ICGetElem_TypedArray* getElemStub = toGetElem_TypedArray(); TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-typedarray-shape"); @@ -2248,9 +2244,7 @@ IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy) if (!isDOMProxy && !obj->isNative()) { if (obj == holder) return false; - if (!obj->is<UnboxedPlainObject>() && - !obj->is<UnboxedArrayObject>() && - !obj->is<TypedObject>()) + if (!obj->is<TypedObject>()) { return false; } @@ -2578,12 +2572,6 @@ CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, PropertyName* name, } else if (curObj != obj) { // Non-native objects are only handled as the original receiver. return false; - } else if (curObj->is<UnboxedPlainObject>()) { - if (curObj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, NameToId(name))) - return false; - } else if (curObj->is<UnboxedArrayObject>()) { - if (name == cx->names().length) - return false; } else if (curObj->is<TypedObject>()) { if (curObj->as<TypedObject>().typeDescr().hasProperty(cx->names(), NameToId(name))) return false; @@ -2848,34 +2836,15 @@ GuardReceiverObject(MacroAssembler& masm, ReceiverGuard guard, { Address groupAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfGroup()); Address shapeAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfShape()); - Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); if (guard.group) { masm.loadPtr(groupAddress, scratch); masm.branchTestObjGroup(Assembler::NotEqual, object, scratch, failure); - - if (guard.group->clasp() == &UnboxedPlainObject::class_ && !guard.shape) { - // Guard the unboxed object has no expando object. - masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure); - } } if (guard.shape) { masm.loadPtr(shapeAddress, scratch); - if (guard.group && guard.group->clasp() == &UnboxedPlainObject::class_) { - // Guard the unboxed object has a matching expando object. - masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure); - Label done; - masm.push(object); - masm.loadPtr(expandoAddress, object); - masm.branchTestObjShape(Assembler::Equal, object, scratch, &done); - masm.pop(object); - masm.jump(failure); - masm.bind(&done); - masm.pop(object); - } else { - masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure); - } + masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure); } } @@ -4259,8 +4228,7 @@ DoNewObject(JSContext* cx, void* payload, ICNewObject_Fallback* stub, MutableHan return false; if (!stub->invalid() && - (templateObject->is<UnboxedPlainObject>() || - !templateObject->as<PlainObject>().hasDynamicSlots())) + !templateObject->as<PlainObject>().hasDynamicSlots()) { JitCode* code = GenerateNewObjectWithTemplateCode(cx, templateObject); if (!code) diff --git a/js/src/jit/StupidAllocator.cpp b/js/src/jit/StupidAllocator.cpp index 8e3ea62865..55431e8e0c 100644 --- a/js/src/jit/StupidAllocator.cpp +++ b/js/src/jit/StupidAllocator.cpp @@ -407,7 +407,6 @@ StupidAllocator::allocateForDefinition(LInstruction* ins, LDefinition* def) { uint32_t vreg = def->virtualRegister(); - CodePosition from; if ((def->output()->isRegister() && def->policy() == LDefinition::FIXED) || def->policy() == LDefinition::MUST_REUSE_INPUT) { diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 77b9e36470..1ff7adfd16 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -28,7 +28,7 @@ #include "vm/NativeObject-inl.h" #include "vm/StringObject-inl.h" #include "vm/TypeInference-inl.h" -#include "vm/UnboxedObject-inl.h" +#include "gc/StoreBuffer-inl.h" using namespace js; using namespace js::jit; @@ -54,8 +54,8 @@ VMFunction::addToFunctions() } bool -InvokeFunction(JSContext* cx, HandleObject obj, bool constructing, uint32_t argc, Value* argv, - MutableHandleValue rval) +InvokeFunction(JSContext* cx, HandleObject obj, bool constructing, bool ignoresReturnValue, + uint32_t argc, Value* argv, MutableHandleValue rval) { TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime()); TraceLogStartEvent(logger, TraceLogger_Call); @@ -104,7 +104,7 @@ InvokeFunction(JSContext* cx, HandleObject obj, bool constructing, uint32_t argc return InternalConstructWithProvidedThis(cx, fval, thisv, cargs, newTarget, rval); } - InvokeArgs args(cx); + InvokeArgsMaybeIgnoresReturnValue args(cx, ignoresReturnValue); if (!args.init(cx, argc)) return false; @@ -120,7 +120,7 @@ InvokeFunctionShuffleNewTarget(JSContext* cx, HandleObject obj, uint32_t numActu { MOZ_ASSERT(numFormalArgs > numActualArgs); argv[1 + numActualArgs] = argv[1 + numFormalArgs]; - return InvokeFunction(cx, obj, true, numActualArgs, argv, rval); + return InvokeFunction(cx, obj, true, false, numActualArgs, argv, rval); } bool @@ -304,21 +304,9 @@ template bool StringsEqual<true>(JSContext* cx, HandleString lhs, HandleString r template bool StringsEqual<false>(JSContext* cx, HandleString lhs, HandleString rhs, bool* res); bool -ArraySpliceDense(JSContext* cx, HandleObject obj, uint32_t start, uint32_t deleteCount) -{ - JS::AutoValueArray<4> argv(cx); - argv[0].setUndefined(); - argv[1].setObject(*obj); - argv[2].set(Int32Value(start)); - argv[3].set(Int32Value(deleteCount)); - - return js::array_splice_impl(cx, 2, argv.begin(), false); -} - -bool ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval) { - MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()); + MOZ_ASSERT(obj->is<ArrayObject>()); AutoDetectInvalidation adi(cx, rval); @@ -337,12 +325,11 @@ ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval) } bool -ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length) +ArrayPushDense(JSContext* cx, HandleArrayObject arr, HandleValue v, uint32_t* length) { - *length = GetAnyBoxedOrUnboxedArrayLength(obj); - DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, *length, v.address(), 1, - ShouldUpdateTypes::DontUpdate); + *length = arr->length(); + DenseElementResult result = arr->setOrExtendDenseElements(cx, *length, v.address(), 1, + ShouldUpdateTypes::DontUpdate); if (result != DenseElementResult::Incomplete) { (*length)++; return result == DenseElementResult::Success; @@ -350,7 +337,7 @@ ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length) JS::AutoValueArray<3> argv(cx); argv[0].setUndefined(); - argv[1].setObject(*obj); + argv[1].setObject(*arr); argv[2].set(v); if (!js::array_push(cx, 1, argv.begin())) return false; @@ -362,7 +349,7 @@ ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length) bool ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval) { - MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()); + MOZ_ASSERT(obj->is<ArrayObject>()); AutoDetectInvalidation adi(cx, rval); @@ -556,7 +543,7 @@ CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHa if (callee->is<JSFunction>()) { RootedFunction fun(cx, &callee->as<JSFunction>()); if (fun->isInterpreted() && fun->isConstructor()) { - JSScript* script = fun->getOrCreateScript(cx); + JSScript* script = JSFunction::getOrCreateScript(cx, fun); if (!script || !script->ensureHasTypes(cx)) return false; if (fun->isBoundFunction() || script->isDerivedClassConstructor()) { @@ -1143,16 +1130,14 @@ Recompile(JSContext* cx) } bool -SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index, - HandleValue value, bool strict) +SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, HandleValue value, bool strict) { // This function is called from Ion code for StoreElementHole's OOL path. - // In this case we know the object is native or an unboxed array and that - // no type changes are needed. + // In this case we know the object is native and that no type changes are + // needed. - DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, index, value.address(), 1, - ShouldUpdateTypes::DontUpdate); + DenseElementResult result = obj->setOrExtendDenseElements(cx, index, value.address(), 1, + ShouldUpdateTypes::DontUpdate); if (result != DenseElementResult::Incomplete) return result == DenseElementResult::Success; diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index 572f053732..94f7413971 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -584,8 +584,8 @@ class AutoDetectInvalidation }; MOZ_MUST_USE bool -InvokeFunction(JSContext* cx, HandleObject obj0, bool constructing, uint32_t argc, Value* argv, - MutableHandleValue rval); +InvokeFunction(JSContext* cx, HandleObject obj0, bool constructing, bool ignoresReturnValue, + uint32_t argc, Value* argv, MutableHandleValue rval); MOZ_MUST_USE bool InvokeFunctionShuffleNewTarget(JSContext* cx, HandleObject obj, uint32_t numActualArgs, uint32_t numFormalArgs, Value* argv, MutableHandleValue rval); @@ -622,7 +622,7 @@ template<bool Equal> bool StringsEqual(JSContext* cx, HandleString left, HandleString right, bool* res); MOZ_MUST_USE bool ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval); -MOZ_MUST_USE bool ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length); +MOZ_MUST_USE bool ArrayPushDense(JSContext* cx, HandleArrayObject obj, HandleValue v, uint32_t* length); MOZ_MUST_USE bool ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval); JSString* ArrayJoin(JSContext* cx, HandleObject array, HandleString sep); @@ -739,17 +739,14 @@ JSObject* CreateDerivedTypedObj(JSContext* cx, HandleObject descr, HandleObject owner, int32_t offset); MOZ_MUST_USE bool -ArraySpliceDense(JSContext* cx, HandleObject obj, uint32_t start, uint32_t deleteCount); - -MOZ_MUST_USE bool Recompile(JSContext* cx); MOZ_MUST_USE bool ForcedRecompile(JSContext* cx); JSString* StringReplace(JSContext* cx, HandleString string, HandleString pattern, HandleString repl); -MOZ_MUST_USE bool SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index, - HandleValue value, bool strict); +MOZ_MUST_USE bool SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, + HandleValue value, bool strict); void AssertValidObjectPtr(JSContext* cx, JSObject* obj); void AssertValidObjectOrNullPtr(JSContext* cx, JSObject* obj); diff --git a/js/src/jit/arm/Assembler-arm.cpp b/js/src/jit/arm/Assembler-arm.cpp index 2830f06955..1e20da1c8c 100644 --- a/js/src/jit/arm/Assembler-arm.cpp +++ b/js/src/jit/arm/Assembler-arm.cpp @@ -2401,7 +2401,12 @@ Assembler::as_b(Label* l, Condition c) if (oom()) return BufferOffset(); - as_b(BufferOffset(l).diffB<BOffImm>(ret), c, ret); + BOffImm off = BufferOffset(l).diffB<BOffImm>(ret); + if (off.isInvalid()) { + m_buffer.fail_bail(); + return BufferOffset(); + } + as_b(off, c, ret); #ifdef JS_DISASM_ARM spewBranch(m_buffer.getInstOrNull(ret), l); #endif diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index d405785144..3421001f7a 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -3462,8 +3462,8 @@ MacroAssemblerARMCompat::storePayload(const Value& val, const Address& dest) ScratchRegisterScope scratch(asMasm()); SecondScratchRegisterScope scratch2(asMasm()); - if (val.isMarkable()) - ma_mov(ImmGCPtr(val.toMarkablePointer()), scratch); + if (val.isGCThing()) + ma_mov(ImmGCPtr(val.toGCThing()), scratch); else ma_mov(Imm32(val.toNunboxPayload()), scratch); ma_str(scratch, ToPayload(dest), scratch2); @@ -5012,7 +5012,10 @@ void MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset) { BufferOffset inst(callerOffset - 4); - as_bl(BufferOffset(calleeOffset).diffB<BOffImm>(inst), Always, inst); + BOffImm off = BufferOffset(calleeOffset).diffB<BOffImm>(inst); + MOZ_RELEASE_ASSERT(!off.isInvalid(), + "Failed to insert necessary far jump islands"); + as_bl(off, Always, inst); } CodeOffset diff --git a/js/src/jit/arm64/Architecture-arm64.h b/js/src/jit/arm64/Architecture-arm64.h index e74340f130..bee212db79 100644 --- a/js/src/jit/arm64/Architecture-arm64.h +++ b/js/src/jit/arm64/Architecture-arm64.h @@ -299,10 +299,12 @@ static const uint32_t ION_FRAME_SLACK_SIZE = 24; static const uint32_t ShadowStackSpace = 0; -// TODO: -// This constant needs to be updated to account for whatever near/far branching -// strategy is used by ARM64. -static const uint32_t JumpImmediateRange = UINT32_MAX; +// When our only strategy for far jumps is to encode the offset directly, and +// not insert any jump islands during assembly for even further jumps, then the +// architecture restricts us to -2^27 .. 2^27-4, to fit into a signed 28-bit +// value. We further reduce this range to allow the far-jump inserting code to +// have some breathing room. +static const uint32_t JumpImmediateRange = ((1 << 27) - (20 * 1024 * 1024)); static const uint32_t ABIStackAlignment = 16; static const uint32_t CodeAlignment = 16; diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index aac9687b80..8044e75cb4 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -7,8 +7,6 @@ #ifndef jit_shared_Assembler_shared_h #define jit_shared_Assembler_shared_h -#include "mozilla/PodOperations.h" - #include <limits.h> #include "jit/AtomicOp.h" @@ -491,10 +489,10 @@ class CodeLabel class CodeOffsetJump { - size_t offset_; + size_t offset_ = 0; #ifdef JS_SMALL_BRANCH - size_t jumpTableIndex_; + size_t jumpTableIndex_ = 0; #endif public: @@ -510,9 +508,7 @@ class CodeOffsetJump explicit CodeOffsetJump(size_t offset) : offset_(offset) {} #endif - CodeOffsetJump() { - mozilla::PodZero(this); - } + CodeOffsetJump() = default; size_t offset() const { return offset_; diff --git a/js/src/jit/shared/IonAssemblerBuffer.h b/js/src/jit/shared/IonAssemblerBuffer.h index 3a65526968..cc20e26d21 100644 --- a/js/src/jit/shared/IonAssemblerBuffer.h +++ b/js/src/jit/shared/IonAssemblerBuffer.h @@ -181,10 +181,6 @@ class AssemblerBuffer protected: virtual Slice* newSlice(LifoAlloc& a) { - if (size() > MaxCodeBytesPerProcess - sizeof(Slice)) { - fail_oom(); - return nullptr; - } Slice* tmp = static_cast<Slice*>(a.alloc(sizeof(Slice))); if (!tmp) { fail_oom(); diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index f4adcc63c0..ecf02816ed 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -1898,6 +1898,9 @@ class LJSCallInstructionHelper : public LCallInstructionHelper<Defs, Operands, T bool isConstructing() const { return mir()->isConstructing(); } + bool ignoresReturnValue() const { + return mir()->ignoresReturnValue(); + } }; // Generates a polymorphic callsite, wherein the function being called is @@ -2218,34 +2221,6 @@ class LApplyArrayGeneric : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES } }; -class LArraySplice : public LCallInstructionHelper<0, 3, 0> -{ - public: - LIR_HEADER(ArraySplice) - - LArraySplice(const LAllocation& object, const LAllocation& start, - const LAllocation& deleteCount) - { - setOperand(0, object); - setOperand(1, start); - setOperand(2, deleteCount); - } - - MArraySplice* mir() const { - return mir_->toArraySplice(); - } - - const LAllocation* getObject() { - return getOperand(0); - } - const LAllocation* getStart() { - return getOperand(1); - } - const LAllocation* getDeleteCount() { - return getOperand(2); - } -}; - class LGetDynamicName : public LCallInstructionHelper<BOX_PIECES, 2, 3> { public: @@ -5165,72 +5140,6 @@ class LSetInitializedLength : public LInstructionHelper<0, 2, 0> } }; -class LUnboxedArrayLength : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(UnboxedArrayLength) - - explicit LUnboxedArrayLength(const LAllocation& object) { - setOperand(0, object); - } - - const LAllocation* object() { - return getOperand(0); - } -}; - -class LUnboxedArrayInitializedLength : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(UnboxedArrayInitializedLength) - - explicit LUnboxedArrayInitializedLength(const LAllocation& object) { - setOperand(0, object); - } - - const LAllocation* object() { - return getOperand(0); - } -}; - -class LIncrementUnboxedArrayInitializedLength : public LInstructionHelper<0, 1, 0> -{ - public: - LIR_HEADER(IncrementUnboxedArrayInitializedLength) - - explicit LIncrementUnboxedArrayInitializedLength(const LAllocation& object) { - setOperand(0, object); - } - - const LAllocation* object() { - return getOperand(0); - } -}; - -class LSetUnboxedArrayInitializedLength : public LInstructionHelper<0, 2, 1> -{ - public: - LIR_HEADER(SetUnboxedArrayInitializedLength) - - explicit LSetUnboxedArrayInitializedLength(const LAllocation& object, - const LAllocation& length, - const LDefinition& temp) { - setOperand(0, object); - setOperand(1, length); - setTemp(0, temp); - } - - const LAllocation* object() { - return getOperand(0); - } - const LAllocation* length() { - return getOperand(1); - } - const LDefinition* temp() { - return getTemp(0); - } -}; - // Load the length from an elements header. class LArrayLength : public LInstructionHelper<1, 1, 0> { @@ -5735,19 +5644,17 @@ class LStoreElementT : public LInstructionHelper<0, 3, 0> }; // Like LStoreElementV, but supports indexes >= initialized length. -class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 1> +class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 0> { public: LIR_HEADER(StoreElementHoleV) LStoreElementHoleV(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LBoxAllocation& value, - const LDefinition& temp) { + const LAllocation& index, const LBoxAllocation& value) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); setBoxOperand(Value, value); - setTemp(0, temp); } static const size_t Value = 3; @@ -5767,19 +5674,17 @@ class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 1> }; // Like LStoreElementT, but supports indexes >= initialized length. -class LStoreElementHoleT : public LInstructionHelper<0, 4, 1> +class LStoreElementHoleT : public LInstructionHelper<0, 4, 0> { public: LIR_HEADER(StoreElementHoleT) LStoreElementHoleT(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LAllocation& value, - const LDefinition& temp) { + const LAllocation& index, const LAllocation& value) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); setOperand(3, value); - setTemp(0, temp); } const MStoreElementHole* mir() const { @@ -5800,19 +5705,17 @@ class LStoreElementHoleT : public LInstructionHelper<0, 4, 1> }; // Like LStoreElementV, but can just ignore assignment (for eg. frozen objects) -class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 1> +class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 0> { public: LIR_HEADER(FallibleStoreElementV) LFallibleStoreElementV(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LBoxAllocation& value, - const LDefinition& temp) { + const LAllocation& index, const LBoxAllocation& value) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); setBoxOperand(Value, value); - setTemp(0, temp); } static const size_t Value = 3; @@ -5832,19 +5735,17 @@ class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 1> }; // Like LStoreElementT, but can just ignore assignment (for eg. frozen objects) -class LFallibleStoreElementT : public LInstructionHelper<0, 4, 1> +class LFallibleStoreElementT : public LInstructionHelper<0, 4, 0> { public: LIR_HEADER(FallibleStoreElementT) LFallibleStoreElementT(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LAllocation& value, - const LDefinition& temp) { + const LAllocation& index, const LAllocation& value) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); setOperand(3, value); - setTemp(0, temp); } const MFallibleStoreElement* mir() const { @@ -5891,22 +5792,6 @@ class LStoreUnboxedPointer : public LInstructionHelper<0, 3, 0> } }; -// If necessary, convert an unboxed object in a particular group to its native -// representation. -class LConvertUnboxedObjectToNative : public LInstructionHelper<0, 1, 0> -{ - public: - LIR_HEADER(ConvertUnboxedObjectToNative) - - explicit LConvertUnboxedObjectToNative(const LAllocation& object) { - setOperand(0, object); - } - - MConvertUnboxedObjectToNative* mir() { - return mir_->toConvertUnboxedObjectToNative(); - } -}; - class LArrayPopShiftV : public LInstructionHelper<BOX_PIECES, 1, 2> { public: @@ -7429,38 +7314,6 @@ class LGuardReceiverPolymorphic : public LInstructionHelper<0, 1, 1> } }; -class LGuardUnboxedExpando : public LInstructionHelper<0, 1, 0> -{ - public: - LIR_HEADER(GuardUnboxedExpando) - - explicit LGuardUnboxedExpando(const LAllocation& in) { - setOperand(0, in); - } - const LAllocation* object() { - return getOperand(0); - } - const MGuardUnboxedExpando* mir() const { - return mir_->toGuardUnboxedExpando(); - } -}; - -class LLoadUnboxedExpando : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(LoadUnboxedExpando) - - explicit LLoadUnboxedExpando(const LAllocation& in) { - setOperand(0, in); - } - const LAllocation* object() { - return getOperand(0); - } - const MLoadUnboxedExpando* mir() const { - return mir_->toLoadUnboxedExpando(); - } -}; - // Guard that a value is in a TypeSet. class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 1> { diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index fe2ab5ea35..e260d7e943 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -69,7 +69,6 @@ _(NewArrayDynamicLength) \ _(NewTypedArray) \ _(NewTypedArrayDynamicLength) \ - _(ArraySplice) \ _(NewObject) \ _(NewTypedObject) \ _(NewNamedLambdaObject) \ @@ -258,8 +257,6 @@ _(GuardObjectGroup) \ _(GuardObjectIdentity) \ _(GuardClass) \ - _(GuardUnboxedExpando) \ - _(LoadUnboxedExpando) \ _(TypeBarrierV) \ _(TypeBarrierO) \ _(MonitorTypes) \ @@ -269,10 +266,6 @@ _(PostWriteElementBarrierV) \ _(InitializedLength) \ _(SetInitializedLength) \ - _(UnboxedArrayLength) \ - _(UnboxedArrayInitializedLength) \ - _(IncrementUnboxedArrayInitializedLength) \ - _(SetUnboxedArrayInitializedLength) \ _(BoundsCheck) \ _(BoundsCheckRange) \ _(BoundsCheckLower) \ @@ -287,7 +280,6 @@ _(StoreElementT) \ _(StoreUnboxedScalar) \ _(StoreUnboxedPointer) \ - _(ConvertUnboxedObjectToNative) \ _(ArrayPopShiftV) \ _(ArrayPopShiftT) \ _(ArrayPushV) \ diff --git a/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h b/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h index fe678fc7db..8343579c81 100644 --- a/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h +++ b/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h @@ -68,33 +68,6 @@ namespace js { namespace jit { - // AllocPolicy for AssemblerBuffer. OOMs when trying to allocate more than - // MaxCodeBytesPerProcess bytes. Use private inheritance to make sure we - // explicitly have to expose SystemAllocPolicy methods. - class AssemblerBufferAllocPolicy : private SystemAllocPolicy - { - public: - using SystemAllocPolicy::checkSimulatedOOM; - using SystemAllocPolicy::reportAllocOverflow; - using SystemAllocPolicy::free_; - - template <typename T> T* pod_realloc(T* p, size_t oldSize, size_t newSize) { - static_assert(sizeof(T) == 1, - "AssemblerBufferAllocPolicy should only be used with byte vectors"); - MOZ_ASSERT(oldSize <= MaxCodeBytesPerProcess); - if (MOZ_UNLIKELY(newSize > MaxCodeBytesPerProcess)) - return nullptr; - return SystemAllocPolicy::pod_realloc<T>(p, oldSize, newSize); - } - template <typename T> T* pod_malloc(size_t numElems) { - static_assert(sizeof(T) == 1, - "AssemblerBufferAllocPolicy should only be used with byte vectors"); - if (MOZ_UNLIKELY(numElems > MaxCodeBytesPerProcess)) - return nullptr; - return SystemAllocPolicy::pod_malloc<T>(numElems); - } - }; - class AssemblerBuffer { template<size_t size, typename T> @@ -120,10 +93,8 @@ namespace jit { void ensureSpace(size_t space) { - // This should only be called with small |space| values to ensure - // we don't overflow below. - MOZ_ASSERT(space <= 16); - if (MOZ_UNLIKELY(!m_buffer.reserve(m_buffer.length() + space))) + if (MOZ_UNLIKELY(m_buffer.length() > (SIZE_MAX - space) || + !m_buffer.reserve(m_buffer.length() + space))) oomDetected(); } @@ -198,7 +169,7 @@ namespace jit { m_buffer.clear(); } - PageProtectingVector<unsigned char, 256, AssemblerBufferAllocPolicy> m_buffer; + PageProtectingVector<unsigned char, 256, SystemAllocPolicy> m_buffer; bool m_oom; }; diff --git a/js/src/jit/x86/Assembler-x86.h b/js/src/jit/x86/Assembler-x86.h index 3fb5efaffd..5939583d90 100644 --- a/js/src/jit/x86/Assembler-x86.h +++ b/js/src/jit/x86/Assembler-x86.h @@ -421,20 +421,11 @@ class Assembler : public AssemblerX86Shared MOZ_ASSERT(dest.size() == 16); masm.vhaddpd_rr(src.encoding(), dest.encoding()); } - void vsubpd(const Operand& src1, FloatRegister src0, FloatRegister dest) { + void vsubpd(FloatRegister src1, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); MOZ_ASSERT(src0.size() == 16); MOZ_ASSERT(dest.size() == 16); - switch (src1.kind()) { - case Operand::MEM_REG_DISP: - masm.vsubpd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); - break; - case Operand::MEM_ADDRESS32: - masm.vsubpd_mr(src1.address(), src0.encoding(), dest.encoding()); - break; - default: - MOZ_CRASH("unexpected operand kind"); - } + masm.vsubpd_rr(src1.encoding(), src0.encoding(), dest.encoding()); } void vpunpckldq(FloatRegister src1, FloatRegister src0, FloatRegister dest) { diff --git a/js/src/jit/x86/BaseAssembler-x86.h b/js/src/jit/x86/BaseAssembler-x86.h index 5b16311d03..caaef3f823 100644 --- a/js/src/jit/x86/BaseAssembler-x86.h +++ b/js/src/jit/x86/BaseAssembler-x86.h @@ -152,14 +152,6 @@ class BaseAssemblerX86 : public BaseAssembler { twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, src1, src0, dst); } - void vsubpd_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) - { - twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, offset, base, src0, dst); - } - void vsubpd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) - { - twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, address, src0, dst); - } void vpunpckldq_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vpunpckldq", VEX_PD, OP2_PUNPCKLDQ, src1, src0, dst); diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index dc97b5b5bd..429a71fa94 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -21,15 +21,6 @@ using namespace js; using namespace js::jit; -// vpunpckldq requires 16-byte boundary for memory operand. -// See convertUInt64ToDouble for the details. -MOZ_ALIGNED_DECL(static const uint64_t, 16) TO_DOUBLE[4] = { - 0x4530000043300000LL, - 0x0LL, - 0x4330000000000000LL, - 0x4530000000000000LL -}; - static const double TO_DOUBLE_HIGH_SCALE = 0x100000000; bool @@ -90,8 +81,16 @@ MacroAssemblerX86::convertUInt64ToDouble(Register64 src, FloatRegister dest, Reg // here, each 64-bit part of dest represents following double: // HI(dest) = 0x 1.00000HHHHHHHH * 2**84 == 2**84 + 0x HHHHHHHH 00000000 // LO(dest) = 0x 1.00000LLLLLLLL * 2**52 == 2**52 + 0x 00000000 LLLLLLLL - movePtr(ImmWord((uintptr_t)TO_DOUBLE), temp); - vpunpckldq(Operand(temp, 0), dest128, dest128); + // See convertUInt64ToDouble for the details. + static const int32_t CST1[4] = { + 0x43300000, + 0x45300000, + 0x0, + 0x0, + }; + + loadConstantSimd128Int(SimdConstant::CreateX4(CST1), ScratchSimd128Reg); + vpunpckldq(ScratchSimd128Reg, dest128, dest128); // Subtract a constant C2 from dest, for each 64-bit part: // C2 = 0x 45300000 00000000 43300000 00000000 @@ -101,7 +100,15 @@ MacroAssemblerX86::convertUInt64ToDouble(Register64 src, FloatRegister dest, Reg // after the operation each 64-bit part of dest represents following: // HI(dest) = double(0x HHHHHHHH 00000000) // LO(dest) = double(0x 00000000 LLLLLLLL) - vsubpd(Operand(temp, sizeof(uint64_t) * 2), dest128, dest128); + static const int32_t CST2[4] = { + 0x0, + 0x43300000, + 0x0, + 0x45300000, + }; + + loadConstantSimd128Int(SimdConstant::CreateX4(CST2), ScratchSimd128Reg); + vsubpd(ScratchSimd128Reg, dest128, dest128); // Add HI(dest) and LO(dest) in double and store it into LO(dest), // LO(dest) = double(0x HHHHHHHH 00000000) + double(0x 00000000 LLLLLLLL) diff --git a/js/src/js.msg b/js/src/js.msg index a276dab944..1612c831d7 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -47,7 +47,6 @@ MSG_DEF(JSMSG_MORE_ARGS_NEEDED, 3, JSEXN_TYPEERR, "{0} requires more than MSG_DEF(JSMSG_INCOMPATIBLE_PROTO, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}") MSG_DEF(JSMSG_NO_CONSTRUCTOR, 1, JSEXN_TYPEERR, "{0} has no constructor") MSG_DEF(JSMSG_BAD_SORT_ARG, 0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument") -MSG_DEF(JSMSG_CANT_WATCH, 1, JSEXN_TYPEERR, "can't watch non-native objects of class {0}") MSG_DEF(JSMSG_READ_ONLY, 1, JSEXN_TYPEERR, "{0} is read-only") MSG_DEF(JSMSG_CANT_DELETE, 1, JSEXN_TYPEERR, "property {0} is non-configurable and can't be deleted") MSG_DEF(JSMSG_CANT_TRUNCATE_ARRAY, 0, JSEXN_TYPEERR, "can't delete non-configurable array element") @@ -63,6 +62,7 @@ MSG_DEF(JSMSG_SPREAD_TOO_LARGE, 0, JSEXN_RANGEERR, "array too large due t MSG_DEF(JSMSG_BAD_WEAKMAP_KEY, 0, JSEXN_TYPEERR, "cannot use the given object as a weak map key") MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 1, JSEXN_TYPEERR, "invalid {0} usage") MSG_DEF(JSMSG_BAD_ARRAY_LENGTH, 0, JSEXN_RANGEERR, "invalid array length") +MSG_DEF(JSMSG_REDECLARED_PREV, 2, JSEXN_NOTE, "Previously declared at line {0}, column {1}") MSG_DEF(JSMSG_REDECLARED_VAR, 2, JSEXN_SYNTAXERR, "redeclaration of {0} {1}") MSG_DEF(JSMSG_UNDECLARED_VAR, 1, JSEXN_REFERENCEERR, "assignment to undeclared variable {0}") MSG_DEF(JSMSG_GETTER_ONLY, 1, JSEXN_TYPEERR, "setting getter-only property {0}") @@ -71,7 +71,6 @@ MSG_DEF(JSMSG_UNDEFINED_PROP, 1, JSEXN_REFERENCEERR, "reference to unde MSG_DEF(JSMSG_INVALID_MAP_ITERABLE, 1, JSEXN_TYPEERR, "iterable for {0} should have array-like objects") MSG_DEF(JSMSG_NESTING_GENERATOR, 0, JSEXN_TYPEERR, "already executing generator") MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}") -MSG_DEF(JSMSG_OBJECT_WATCH_DEPRECATED, 0, JSEXN_WARN, "Object.prototype.watch and unwatch are very slow, non-standard, and deprecated; use a getter/setter instead") MSG_DEF(JSMSG_ARRAYBUFFER_SLICE_DEPRECATED, 0, JSEXN_WARN, "ArrayBuffer.slice is deprecated; use ArrayBuffer.prototype.slice instead") MSG_DEF(JSMSG_BAD_SURROGATE_CHAR, 1, JSEXN_TYPEERR, "bad surrogate character {0}") MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large") @@ -186,7 +185,7 @@ MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as' MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'") MSG_DEF(JSMSG_ASYNC_GENERATOR, 0, JSEXN_SYNTAXERR, "generator function or method can't be async") MSG_DEF(JSMSG_AWAIT_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "await can't be used in default expression") -MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "anonymous generator function returns a value") +MSG_DEF(JSMSG_AWAIT_OUTSIDE_ASYNC, 0, JSEXN_SYNTAXERR, "await is only valid in async functions") MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)") MSG_DEF(JSMSG_BAD_BINDING, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated") MSG_DEF(JSMSG_BAD_CONST_DECL, 0, JSEXN_SYNTAXERR, "missing = in const declaration") @@ -200,16 +199,18 @@ MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 0, JSEXN_SYNTAXERR, "invalid for each loo MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid for-in/of left-hand side") MSG_DEF(JSMSG_LEXICAL_DECL_DEFINES_LET,0, JSEXN_SYNTAXERR, "a lexical declaration can't define a 'let' binding") MSG_DEF(JSMSG_LET_STARTING_FOROF_LHS, 0, JSEXN_SYNTAXERR, "an expression X in 'for (X of Y)' must not start with 'let'") -MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 1, JSEXN_TYPEERR, "generator function {0} returns a value") +MSG_DEF(JSMSG_BAD_FUNCTION_YIELD, 0, JSEXN_TYPEERR, "can't use 'yield' in a function that can return a value") +MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "generator function can't return a value") MSG_DEF(JSMSG_BAD_GENEXP_BODY, 1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression") -MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 0, JSEXN_REFERENCEERR, "invalid increment/decrement operand") +MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 0, JSEXN_SYNTAXERR, "invalid increment/decrement operand") MSG_DEF(JSMSG_BAD_METHOD_DEF, 0, JSEXN_SYNTAXERR, "bad method definition") MSG_DEF(JSMSG_BAD_OCTAL, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant") -MSG_DEF(JSMSG_BAD_OPERAND, 1, JSEXN_SYNTAXERR, "invalid {0} operand") MSG_DEF(JSMSG_BAD_POW_LEFTSIDE, 0, JSEXN_SYNTAXERR, "unparenthesized unary expression can't appear on the left-hand side of '**'") MSG_DEF(JSMSG_BAD_PROP_ID, 0, JSEXN_SYNTAXERR, "invalid property id") MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 1, JSEXN_SYNTAXERR, "{0} not in function") MSG_DEF(JSMSG_BAD_STRICT_ASSIGN, 1, JSEXN_SYNTAXERR, "'{0}' can't be defined or assigned to in strict mode code") +MSG_DEF(JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS, 0, JSEXN_SYNTAXERR, "'arguments' can't be defined or assigned to in strict mode code") +MSG_DEF(JSMSG_BAD_STRICT_ASSIGN_EVAL, 0, JSEXN_SYNTAXERR, "'eval' can't be defined or assigned to in strict mode code") MSG_DEF(JSMSG_BAD_SWITCH, 0, JSEXN_SYNTAXERR, "invalid switch statement") MSG_DEF(JSMSG_BAD_SUPER, 0, JSEXN_SYNTAXERR, "invalid use of keyword 'super'") MSG_DEF(JSMSG_BAD_SUPERPROP, 1, JSEXN_SYNTAXERR, "use of super {0} accesses only valid within methods or eval code within methods") @@ -217,6 +218,7 @@ MSG_DEF(JSMSG_BAD_SUPERCALL, 0, JSEXN_SYNTAXERR, "super() is only vali MSG_DEF(JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION, 0, JSEXN_SYNTAXERR, "missing ] after array comprehension") MSG_DEF(JSMSG_BRACKET_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing ] after element list") MSG_DEF(JSMSG_BRACKET_IN_INDEX, 0, JSEXN_SYNTAXERR, "missing ] in index expression") +MSG_DEF(JSMSG_BRACKET_OPENED, 2, JSEXN_NOTE, "[ opened at line {0}, column {1}") MSG_DEF(JSMSG_CATCH_AFTER_GENERAL, 0, JSEXN_SYNTAXERR, "catch after unconditional catch") MSG_DEF(JSMSG_CATCH_IDENTIFIER, 0, JSEXN_SYNTAXERR, "missing identifier in catch") MSG_DEF(JSMSG_CATCH_OR_FINALLY, 0, JSEXN_SYNTAXERR, "missing catch or finally after try") @@ -227,6 +229,7 @@ MSG_DEF(JSMSG_COLON_IN_COND, 0, JSEXN_SYNTAXERR, "missing : in conditi MSG_DEF(JSMSG_COMP_PROP_UNTERM_EXPR, 0, JSEXN_SYNTAXERR, "missing ] in computed property name") MSG_DEF(JSMSG_CONTRARY_NONDIRECTIVE, 1, JSEXN_SYNTAXERR, "'{0}' statement won't be enforced as a directive because it isn't in directive prologue position") MSG_DEF(JSMSG_CURLY_AFTER_BODY, 0, JSEXN_SYNTAXERR, "missing } after function body") +MSG_DEF(JSMSG_CURLY_OPENED, 2, JSEXN_NOTE, "{ opened at line {0}, column {1}") MSG_DEF(JSMSG_CURLY_AFTER_CATCH, 0, JSEXN_SYNTAXERR, "missing } after catch block") MSG_DEF(JSMSG_CURLY_AFTER_FINALLY, 0, JSEXN_SYNTAXERR, "missing } after finally block") MSG_DEF(JSMSG_CURLY_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing } after property list") @@ -262,7 +265,10 @@ MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT, 2, JSEXN_SYNTAXERR, "unexpected garbage a MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal") MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 0, JSEXN_SYNTAXERR, "illegal character") MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module") +MSG_DEF(JSMSG_OF_AFTER_FOR_LOOP_DECL, 0, JSEXN_SYNTAXERR, "a declaration in the head of a for-of loop can't have an initializer") +MSG_DEF(JSMSG_IN_AFTER_LEXICAL_FOR_DECL,0,JSEXN_SYNTAXERR, "a lexical declaration in the head of a for-in loop can't have an initializer") MSG_DEF(JSMSG_INVALID_FOR_IN_DECL_WITH_INIT,0,JSEXN_SYNTAXERR,"for-in loop head declarations may not have initializers") +MSG_DEF(JSMSG_INVALID_ID, 1, JSEXN_SYNTAXERR, "{0} is an invalid identifier") MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found") MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable") MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, 1, JSEXN_SYNTAXERR, "{0} declaration not directly within block") @@ -500,7 +506,7 @@ MSG_DEF(JSMSG_RANGE_WITH_CLASS_ESCAPE, 0, JSEXN_SYNTAXERR, "character class esca MSG_DEF(JSMSG_RAW_BRACE_IN_REGEP, 0, JSEXN_SYNTAXERR, "raw brace is not allowed in regular expression with unicode flag") MSG_DEF(JSMSG_RAW_BRACKET_IN_REGEP, 0, JSEXN_SYNTAXERR, "raw bracket is not allowed in regular expression with unicode flag") MSG_DEF(JSMSG_TOO_MANY_PARENS, 0, JSEXN_INTERNALERR, "too many parentheses in regular expression") -MSG_DEF(JSMSG_UNICODE_OVERFLOW, 0, JSEXN_SYNTAXERR, "unicode codepoint should not be greater than 0x10FFFF in regular expression") +MSG_DEF(JSMSG_UNICODE_OVERFLOW, 1, JSEXN_SYNTAXERR, "Unicode codepoint must not be greater than 0x10FFFF in {0}") MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression") MSG_DEF(JSMSG_UNTERM_CLASS, 0, JSEXN_SYNTAXERR, "unterminated character class") diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index 277a145b09..c176fbf0ad 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -37,6 +37,7 @@ UNIFIED_SOURCES += [ 'testForOfIterator.cpp', 'testForwardSetProperty.cpp', 'testFreshGlobalEvalRedefinition.cpp', + 'testFunctionBinding.cpp', 'testFunctionProperties.cpp', 'testGCAllocator.cpp', 'testGCCellPtr.cpp', diff --git a/js/src/jsapi-tests/testFunctionBinding.cpp b/js/src/jsapi-tests/testFunctionBinding.cpp new file mode 100644 index 0000000000..33632db14b --- /dev/null +++ b/js/src/jsapi-tests/testFunctionBinding.cpp @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * Test function name binding. + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jsfriendapi.h" + +#include "jsapi-tests/tests.h" + +using namespace js; + +BEGIN_TEST(test_functionBinding) +{ + RootedFunction fun(cx); + + JS::CompileOptions options(cx); + options.setFileAndLine(__FILE__, __LINE__); + + // Named function shouldn't have it's binding. + const char s1chars[] = "return (typeof s1) == 'undefined';"; + JS::AutoObjectVector emptyScopeChain(cx); + CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "s1", 0, nullptr, s1chars, + strlen(s1chars), &fun)); + CHECK(fun); + + JS::AutoValueVector args(cx); + RootedValue rval(cx); + CHECK(JS::Call(cx, UndefinedHandleValue, fun, args, &rval)); + CHECK(rval.isBoolean()); + CHECK(rval.toBoolean()); + + // Named function shouldn't have `anonymous` binding. + const char s2chars[] = "return (typeof anonymous) == 'undefined';"; + CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "s2", 0, nullptr, s2chars, + strlen(s2chars), &fun)); + CHECK(fun); + + CHECK(JS::Call(cx, UndefinedHandleValue, fun, args, &rval)); + CHECK(rval.isBoolean()); + CHECK(rval.toBoolean()); + + // Anonymous function shouldn't have `anonymous` binding. + const char s3chars[] = "return (typeof anonymous) == 'undefined';"; + CHECK(JS::CompileFunction(cx, emptyScopeChain, options, nullptr, 0, nullptr, s3chars, + strlen(s3chars), &fun)); + CHECK(fun); + + CHECK(JS::Call(cx, UndefinedHandleValue, fun, args, &rval)); + CHECK(rval.isBoolean()); + CHECK(rval.toBoolean()); + + return true; +} +END_TEST(test_functionBinding) diff --git a/js/src/jsapi-tests/testGCAllocator.cpp b/js/src/jsapi-tests/testGCAllocator.cpp index 2c5c58a296..d203019ecb 100644 --- a/js/src/jsapi-tests/testGCAllocator.cpp +++ b/js/src/jsapi-tests/testGCAllocator.cpp @@ -14,8 +14,6 @@ #if defined(XP_WIN) #include "jswin.h" #include <psapi.h> -#elif defined(SOLARIS) -// This test doesn't apply to Solaris. #elif defined(XP_UNIX) #include <algorithm> #include <errno.h> @@ -39,8 +37,6 @@ BEGIN_TEST(testGCAllocator) # else // Various APIs are unavailable. This test is disabled. return true; # endif -#elif defined(SOLARIS) - return true; #elif defined(XP_UNIX) PageSize = size_t(sysconf(_SC_PAGESIZE)); #else @@ -301,12 +297,6 @@ void* mapMemory(size_t length) { return nullptr; } void unmapPages(void* p, size_t size) { } # endif -#elif defined(SOLARIS) // This test doesn't apply to Solaris. - -void* mapMemoryAt(void* desired, size_t length) { return nullptr; } -void* mapMemory(size_t length) { return nullptr; } -void unmapPages(void* p, size_t size) { } - #elif defined(XP_UNIX) void* @@ -377,7 +367,7 @@ unmapPages(void* p, size_t size) MOZ_RELEASE_ASSERT(errno == ENOMEM); } -#else // !defined(XP_WIN) && !defined(SOLARIS) && !defined(XP_UNIX) +#else // !defined(XP_WIN) && !defined(XP_UNIX) #error "Memory mapping functions are not defined for your OS." #endif END_TEST(testGCAllocator) diff --git a/js/src/jsapi-tests/testUbiNode.cpp b/js/src/jsapi-tests/testUbiNode.cpp index 075e777d67..00b1253e79 100644 --- a/js/src/jsapi-tests/testUbiNode.cpp +++ b/js/src/jsapi-tests/testUbiNode.cpp @@ -646,7 +646,7 @@ BEGIN_TEST(test_JS_ubi_Node_scriptFilename) CHECK(obj->is<JSFunction>()); JS::RootedFunction func(cx, &obj->as<JSFunction>()); - JS::RootedScript script(cx, func->getOrCreateScript(cx)); + JS::RootedScript script(cx, JSFunction::getOrCreateScript(cx, func)); CHECK(script); CHECK(script->filename()); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 85a38bba42..f9a0c6a6bc 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -39,7 +39,6 @@ #include "jsstr.h" #include "jstypes.h" #include "jsutil.h" -#include "jswatchpoint.h" #include "jsweakmap.h" #include "jswrapper.h" @@ -69,7 +68,6 @@ #include "js/Proxy.h" #include "js/SliceBudget.h" #include "js/StructuredClone.h" -#include "js/UniquePtr.h" #include "js/Utility.h" #include "vm/AsyncFunction.h" #include "vm/DateObject.h" @@ -1022,7 +1020,7 @@ JS_ResolveStandardClass(JSContext* cx, HandleObject obj, HandleId id, bool* reso CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); *resolved = false; if (!JSID_IS_ATOM(id)) @@ -1066,10 +1064,7 @@ JS_ResolveStandardClass(JSContext* cx, HandleObject obj, HandleId id, bool* reso // more way: its prototype chain is lazily initialized. That is, // global->getProto() might be null right now because we haven't created // Object.prototype yet. Force it now. - if (!global->getOrCreateObjectPrototype(cx)) - return false; - - return true; + return GlobalObject::getOrCreateObjectPrototype(cx, global); } JS_PUBLIC_API(bool) @@ -1102,8 +1097,7 @@ JS_EnumerateStandardClasses(JSContext* cx, HandleObject obj) AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); - MOZ_ASSERT(obj->is<GlobalObject>()); - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); return GlobalObject::initStandardClasses(cx, global); } @@ -1159,7 +1153,8 @@ JS_GetObjectPrototype(JSContext* cx, HandleObject forObj) { CHECK_REQUEST(cx); assertSameCompartment(cx, forObj); - return forObj->global().getOrCreateObjectPrototype(cx); + Rooted<GlobalObject*> global(cx, &forObj->global()); + return GlobalObject::getOrCreateObjectPrototype(cx, global); } JS_PUBLIC_API(JSObject*) @@ -1167,7 +1162,8 @@ JS_GetFunctionPrototype(JSContext* cx, HandleObject forObj) { CHECK_REQUEST(cx); assertSameCompartment(cx, forObj); - return forObj->global().getOrCreateFunctionPrototype(cx); + Rooted<GlobalObject*> global(cx, &forObj->global()); + return GlobalObject::getOrCreateFunctionPrototype(cx, global); } JS_PUBLIC_API(JSObject*) @@ -2003,10 +1999,10 @@ JS_GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name, } JS_PUBLIC_API(bool) -JS_GetOwnUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* name, +JS_GetOwnUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, MutableHandle<PropertyDescriptor> desc) { - JSAtom* atom = AtomizeChars(cx, name, js_strlen(name)); + JSAtom* atom = AtomizeChars(cx, name, namelen); if (!atom) return false; RootedId id(cx, AtomToId(atom)); @@ -2014,6 +2010,28 @@ JS_GetOwnUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* n } JS_PUBLIC_API(bool) +JS_GetOwnElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleValue vp) +{ + RootedId id(cx); + if (!IndexToId(cx, index, &id)) { + return false; + } + + Rooted<PropertyDescriptor> desc(cx); + if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc)) { + return false; + } + + if (desc.object() && desc.isDataDescriptor()) { + vp.set(desc.value()); + } else { + vp.setUndefined(); + } + + return true; +} + +JS_PUBLIC_API(bool) JS_GetPropertyDescriptorById(JSContext* cx, HandleObject obj, HandleId id, MutableHandle<PropertyDescriptor> desc) { @@ -2028,7 +2046,19 @@ JS_GetPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name, if (!atom) return false; RootedId id(cx, AtomToId(atom)); - return atom && JS_GetPropertyDescriptorById(cx, obj, id, desc); + return JS_GetPropertyDescriptorById(cx, obj, id, desc); +} + +JS_PUBLIC_API(bool) +JS_GetUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, + MutableHandle<PropertyDescriptor> desc) +{ + JSAtom* atom = AtomizeChars(cx, name, namelen); + if (!atom) { + return false; + } + RootedId id(cx, AtomToId(atom)); + return JS_GetPropertyDescriptorById(cx, obj, id, desc); } static bool @@ -3477,7 +3507,7 @@ CreateNonSyntacticEnvironmentChain(JSContext* cx, AutoObjectVector& envChain, // declaration was qualified by "var". There is only sadness. // // See JSObject::isQualifiedVarObj. - if (!env->setQualifiedVarObj(cx)) + if (!JSObject::setQualifiedVarObj(cx, env)) return false; // Also get a non-syntactic lexical environment to capture 'let' and @@ -3537,7 +3567,7 @@ CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject env, Handle RootedFunction fun(cx, &funobj->as<JSFunction>()); if (fun->isInterpretedLazy()) { AutoCompartment ac(cx, funobj); - if (!fun->getOrCreateScript(cx)) + if (!JSFunction::getOrCreateScript(cx, fun)) return nullptr; } @@ -3569,7 +3599,7 @@ CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject env, Handle // Fail here if we OOM during debug asserting. // CloneFunctionReuseScript will delazify the script anyways, so we // are not creating an extra failure condition for DEBUG builds. - if (!fun->getOrCreateScript(cx)) + if (!JSFunction::getOrCreateScript(cx, fun)) return nullptr; MOZ_ASSERT(scope->as<GlobalScope>().isSyntactic() || fun->nonLazyScript()->hasNonSyntacticScope()); @@ -4222,7 +4252,7 @@ JS_GetFunctionScript(JSContext* cx, HandleFunction fun) return nullptr; if (fun->isInterpretedLazy()) { AutoCompartment funCompartment(cx, fun); - JSScript* script = fun->getOrCreateScript(cx); + JSScript* script = JSFunction::getOrCreateScript(cx, fun); if (!script) MOZ_CRASH(); return script; @@ -4238,7 +4268,7 @@ JS_GetFunctionScript(JSContext* cx, HandleFunction fun) */ static bool CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, - const char* name, + HandleAtom name, bool isInvalidName, SourceBufferHolder& srcBuf, uint32_t parameterListEnd, HandleObject enclosingEnv, HandleScope enclosingScope, MutableHandleFunction fun) @@ -4249,13 +4279,8 @@ CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, assertSameCompartment(cx, enclosingEnv); RootedAtom funAtom(cx); - if (name) { - funAtom = Atomize(cx, name, strlen(name)); - if (!funAtom) - return false; - } - - fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL, funAtom, + fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL, + isInvalidName ? nullptr : name, /* proto = */ nullptr, gc::AllocKind::FUNCTION, TenuredObject, enclosingEnv)); @@ -4273,11 +4298,17 @@ CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, return false; } + // When function name is not a valid identifier, the generated function + // source in srcBuf doesn't have a function name. Set it here. + if (isInvalidName) + fun->setAtom(name); + return true; } static MOZ_MUST_USE bool -BuildFunctionString(unsigned nargs, const char* const* argnames, +BuildFunctionString(const char* name, size_t nameLen, + unsigned nargs, const char* const* argnames, const SourceBufferHolder& srcBuf, StringBuffer* out, uint32_t* parameterListEnd) { @@ -4286,6 +4317,12 @@ BuildFunctionString(unsigned nargs, const char* const* argnames, if (!out->ensureTwoByteChars()) return false; + if (!out->append("function ")) + return false; + if (name) { + if (!out->append(name, nameLen)) + return false; + } if (!out->append("(")) return false; for (unsigned i = 0; i < nargs; i++) { @@ -4322,15 +4359,32 @@ JS::CompileFunction(JSContext* cx, AutoObjectVector& envChain, if (!CreateNonSyntacticEnvironmentChain(cx, envChain, &env, &scope)) return false; + size_t nameLen = 0; + bool isInvalidName = false; + RootedAtom nameAtom(cx); + if (name) { + nameLen = strlen(name); + nameAtom = Atomize(cx, name, nameLen); + if (!nameAtom) + return false; + + // If name is not valid identifier + if (!js::frontend::IsIdentifier(name, nameLen)) + isInvalidName = true; + } + uint32_t parameterListEnd; StringBuffer funStr(cx); - if (!BuildFunctionString(nargs, argnames, srcBuf, &funStr, ¶meterListEnd)) + if (!BuildFunctionString(isInvalidName ? nullptr : name, nameLen, nargs, argnames, srcBuf, + &funStr, ¶meterListEnd)) { return false; + } size_t newLen = funStr.length(); SourceBufferHolder newSrcBuf(funStr.stealChars(), newLen, SourceBufferHolder::GiveOwnership); - return CompileFunction(cx, options, name, newSrcBuf, parameterListEnd, env, scope, fun); + return CompileFunction(cx, options, nameAtom, isInvalidName, newSrcBuf, parameterListEnd, env, + scope, fun); } JS_PUBLIC_API(bool) @@ -4369,14 +4423,15 @@ JS_DecompileScript(JSContext* cx, HandleScript script, const char* name, unsigne AssertHeapIsIdle(cx); CHECK_REQUEST(cx); - script->ensureNonLazyCanonicalFunction(cx); + script->ensureNonLazyCanonicalFunction(); RootedFunction fun(cx, script->functionNonDelazifying()); if (fun) return JS_DecompileFunction(cx, fun, indent); bool haveSource = script->scriptSource()->hasSourceData(); if (!haveSource && !JSScript::loadSource(cx, script->scriptSource(), &haveSource)) return nullptr; - return haveSource ? script->sourceData(cx) : NewStringCopyZ<CanGC>(cx, "[no source]"); + return haveSource ? JSScript::sourceData(cx, script) + : NewStringCopyZ<CanGC>(cx, "[no source]"); } JS_PUBLIC_API(JSString*) @@ -4848,7 +4903,7 @@ JS::CallOriginalPromiseResolve(JSContext* cx, JS::HandleValue resolutionValue) assertSameCompartment(cx, resolutionValue); RootedObject promise(cx, PromiseObject::unforgeableResolve(cx, resolutionValue)); - MOZ_ASSERT_IF(promise, promise->is<PromiseObject>()); + MOZ_ASSERT_IF(promise, CheckedUnwrap(promise)->is<PromiseObject>()); return promise; } @@ -4860,7 +4915,7 @@ JS::CallOriginalPromiseReject(JSContext* cx, JS::HandleValue rejectionValue) assertSameCompartment(cx, rejectionValue); RootedObject promise(cx, PromiseObject::unforgeableReject(cx, rejectionValue)); - MOZ_ASSERT_IF(promise, promise->is<PromiseObject>()); + MOZ_ASSERT_IF(promise, CheckedUnwrap(promise)->is<PromiseObject>()); return promise; } @@ -4890,8 +4945,8 @@ ResolveOrRejectPromise(JSContext* cx, JS::HandleObject promiseObj, JS::HandleVal } return reject - ? promise->reject(cx, resultOrReason) - : promise->resolve(cx, resultOrReason); + ? PromiseObject::reject(cx, promise, resultOrReason) + : PromiseObject::resolve(cx, promise, resultOrReason); } JS_PUBLIC_API(bool) @@ -5978,7 +6033,8 @@ JS_SetRegExpInput(JSContext* cx, HandleObject obj, HandleString input) CHECK_REQUEST(cx); assertSameCompartment(cx, input); - RegExpStatics* res = obj->as<GlobalObject>().getRegExpStatics(cx); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); + RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global); if (!res) return false; @@ -5993,7 +6049,8 @@ JS_ClearRegExpStatics(JSContext* cx, HandleObject obj) CHECK_REQUEST(cx); MOZ_ASSERT(obj); - RegExpStatics* res = obj->as<GlobalObject>().getRegExpStatics(cx); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); + RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global); if (!res) return false; @@ -6008,7 +6065,8 @@ JS_ExecuteRegExp(JSContext* cx, HandleObject obj, HandleObject reobj, char16_t* AssertHeapIsIdle(cx); CHECK_REQUEST(cx); - RegExpStatics* res = obj->as<GlobalObject>().getRegExpStatics(cx); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); + RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global); if (!res) return false; @@ -6016,7 +6074,7 @@ JS_ExecuteRegExp(JSContext* cx, HandleObject obj, HandleObject reobj, char16_t* if (!input) return false; - return ExecuteRegExpLegacy(cx, res, reobj->as<RegExpObject>(), input, indexp, test, rval); + return ExecuteRegExpLegacy(cx, res, reobj.as<RegExpObject>(), input, indexp, test, rval); } JS_PUBLIC_API(bool) @@ -6030,7 +6088,7 @@ JS_ExecuteRegExpNoStatics(JSContext* cx, HandleObject obj, char16_t* chars, size if (!input) return false; - return ExecuteRegExpLegacy(cx, nullptr, obj->as<RegExpObject>(), input, indexp, test, + return ExecuteRegExpLegacy(cx, nullptr, obj.as<RegExpObject>(), input, indexp, test, rval); } @@ -6262,7 +6320,7 @@ JSErrorReport::freeLinebuf() } JSString* -JSErrorReport::newMessageString(JSContext* cx) +JSErrorBase::newMessageString(JSContext* cx) { if (!message_) return cx->runtime()->emptyString; @@ -6271,7 +6329,7 @@ JSErrorReport::newMessageString(JSContext* cx) } void -JSErrorReport::freeMessage() +JSErrorBase::freeMessage() { if (ownsMessage_) { js_free((void*)message_.get()); @@ -6280,6 +6338,132 @@ JSErrorReport::freeMessage() message_ = JS::ConstUTF8CharsZ(); } +JSErrorNotes::JSErrorNotes() + : notes_() +{} + +JSErrorNotes::~JSErrorNotes() +{ +} + +static UniquePtr<JSErrorNotes::Note> +CreateErrorNoteVA(ExclusiveContext* cx, + const char* filename, unsigned lineno, unsigned column, + JSErrorCallback errorCallback, void* userRef, + const unsigned errorNumber, + ErrorArgumentsType argumentsType, va_list ap) +{ + auto note = MakeUnique<JSErrorNotes::Note>(); + if (!note) + return nullptr; + + note->errorNumber = errorNumber; + note->filename = filename; + note->lineno = lineno; + note->column = column; + + if (!ExpandErrorArgumentsVA(cx, errorCallback, userRef, errorNumber, + nullptr, argumentsType, note.get(), ap)) { + return nullptr; + } + + return note; +} + +bool +JSErrorNotes::addNoteASCII(ExclusiveContext* cx, + const char* filename, unsigned lineno, unsigned column, + JSErrorCallback errorCallback, void* userRef, + const unsigned errorNumber, ...) +{ + va_list ap; + va_start(ap, errorNumber); + auto note = CreateErrorNoteVA(cx, filename, lineno, column, errorCallback, userRef, + errorNumber, ArgumentsAreASCII, ap); + va_end(ap); + + if (!note) + return false; + if (!notes_.append(Move(note))) + return false; + return true; +} + +bool +JSErrorNotes::addNoteLatin1(ExclusiveContext* cx, + const char* filename, unsigned lineno, unsigned column, + JSErrorCallback errorCallback, void* userRef, + const unsigned errorNumber, ...) +{ + va_list ap; + va_start(ap, errorNumber); + auto note = CreateErrorNoteVA(cx, filename, lineno, column, errorCallback, userRef, + errorNumber, ArgumentsAreLatin1, ap); + va_end(ap); + + if (!note) + return false; + if (!notes_.append(Move(note))) + return false; + return true; +} + +bool +JSErrorNotes::addNoteUTF8(ExclusiveContext* cx, + const char* filename, unsigned lineno, unsigned column, + JSErrorCallback errorCallback, void* userRef, + const unsigned errorNumber, ...) +{ + va_list ap; + va_start(ap, errorNumber); + auto note = CreateErrorNoteVA(cx, filename, lineno, column, errorCallback, userRef, + errorNumber, ArgumentsAreUTF8, ap); + va_end(ap); + + if (!note) + return false; + if (!notes_.append(Move(note))) + return false; + return true; +} + +size_t +JSErrorNotes::length() +{ + return notes_.length(); +} + +UniquePtr<JSErrorNotes> +JSErrorNotes::copy(JSContext* cx) +{ + auto copiedNotes = MakeUnique<JSErrorNotes>(); + if (!copiedNotes) + return nullptr; + + for (auto&& note : *this) { + js::UniquePtr<JSErrorNotes::Note> copied(CopyErrorNote(cx, note.get())); + if (!copied) + return nullptr; + + if (!copiedNotes->notes_.append(Move(copied))) + return nullptr; + } + + return copiedNotes; +} + +JSErrorNotes::iterator +JSErrorNotes::begin() +{ + return iterator(notes_.begin()); +} + +JSErrorNotes::iterator +JSErrorNotes::end() +{ + return iterator(notes_.end()); +} + JS_PUBLIC_API(bool) JS_ThrowStopIteration(JSContext* cx) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index c1195cc007..dc00c650d4 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -18,6 +18,7 @@ #include "mozilla/RefPtr.h" #include "mozilla/Variant.h" +#include <iterator> #include <stdarg.h> #include <stddef.h> #include <stdint.h> @@ -36,6 +37,7 @@ #include "js/Realm.h" #include "js/RootingAPI.h" #include "js/TracingAPI.h" +#include "js/UniquePtr.h" #include "js/Utility.h" #include "js/Value.h" #include "js/Vector.h" @@ -652,6 +654,7 @@ typedef enum JSExnType { JSEXN_WASMRUNTIMEERROR, JSEXN_ERROR_LIMIT, JSEXN_WARN = JSEXN_ERROR_LIMIT, + JSEXN_NOTE, JSEXN_LIMIT } JSExnType; @@ -1093,7 +1096,6 @@ class JS_PUBLIC_API(ContextOptions) { wasmAlwaysBaseline_(false), throwOnAsmJSValidationFailure_(false), nativeRegExp_(true), - unboxedArrays_(false), asyncStack_(true), throwOnDebuggeeWouldRun_(true), dumpStackOnDebuggeeWouldRun_(false), @@ -1170,12 +1172,6 @@ class JS_PUBLIC_API(ContextOptions) { return *this; } - bool unboxedArrays() const { return unboxedArrays_; } - ContextOptions& setUnboxedArrays(bool flag) { - unboxedArrays_ = flag; - return *this; - } - bool asyncStack() const { return asyncStack_; } ContextOptions& setAsyncStack(bool flag) { asyncStack_ = flag; @@ -1238,7 +1234,6 @@ class JS_PUBLIC_API(ContextOptions) { bool wasmAlwaysBaseline_ : 1; bool throwOnAsmJSValidationFailure_ : 1; bool nativeRegExp_ : 1; - bool unboxedArrays_ : 1; bool asyncStack_ : 1; bool throwOnDebuggeeWouldRun_ : 1; bool dumpStackOnDebuggeeWouldRun_ : 1; @@ -2154,6 +2149,13 @@ namespace JS { extern JS_PUBLIC_API(bool) OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool* bp); +// Implementation of +// https://www.ecma-international.org/ecma-262/6.0/#sec-instanceofoperator +// This is almost identical to JS_HasInstance, except the latter may call a +// custom hasInstance class op instead of InstanceofOperator. +extern JS_PUBLIC_API(bool) +InstanceofOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp); + } // namespace JS extern JS_PUBLIC_API(void*) @@ -2917,9 +2919,12 @@ JS_GetOwnPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char* nam JS::MutableHandle<JS::PropertyDescriptor> desc); extern JS_PUBLIC_API(bool) -JS_GetOwnUCPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char16_t* name, +JS_GetOwnUCPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen, JS::MutableHandle<JS::PropertyDescriptor> desc); +extern JS_PUBLIC_API(bool) +JS_GetOwnElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleValue vp); + /** * Like JS_GetOwnPropertyDescriptorById, but also searches the prototype chain * if no own property is found directly on obj. The object on which the @@ -2934,6 +2939,10 @@ extern JS_PUBLIC_API(bool) JS_GetPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char* name, JS::MutableHandle<JS::PropertyDescriptor> desc); +extern JS_PUBLIC_API(bool) +JS_GetUCPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen, + JS::MutableHandle<JS::PropertyDescriptor> desc); + /** * Define a property on obj. * @@ -5359,14 +5368,130 @@ JS_ReportOutOfMemory(JSContext* cx); extern JS_PUBLIC_API(void) JS_ReportAllocationOverflow(JSContext* cx); -class JSErrorReport +/** + * Base class that implements parts shared by JSErrorReport and + * JSErrorNotes::Note. + */ +class JSErrorBase { // The (default) error message. // If ownsMessage_ is true, the it is freed in destructor. JS::ConstUTF8CharsZ message_; + public: + JSErrorBase() + : filename(nullptr), lineno(0), column(0), + errorNumber(0), + ownsMessage_(false) + {} + + ~JSErrorBase() { + freeMessage(); + } + + // Source file name, URL, etc., or null. + const char* filename; + + // Source line number. + unsigned lineno; + + // Zero-based column index in line. + unsigned column; + + // the error number, e.g. see js.msg. + unsigned errorNumber; + + private: + bool ownsMessage_ : 1; + + public: + const JS::ConstUTF8CharsZ message() const { + return message_; + } + + void initOwnedMessage(const char* messageArg) { + initBorrowedMessage(messageArg); + ownsMessage_ = true; + } + void initBorrowedMessage(const char* messageArg) { + MOZ_ASSERT(!message_); + message_ = JS::ConstUTF8CharsZ(messageArg, strlen(messageArg)); + } + + JSString* newMessageString(JSContext* cx); + + private: + void freeMessage(); +}; + +/** + * Notes associated with JSErrorReport. + */ +class JSErrorNotes +{ + public: + class Note : public JSErrorBase + {}; + + private: + // Stores pointers to each note. + js::Vector<js::UniquePtr<Note>, 1, js::SystemAllocPolicy> notes_; + + public: + JSErrorNotes(); + ~JSErrorNotes(); + + // Add an note to the given position. + bool addNoteASCII(js::ExclusiveContext* cx, + const char* filename, unsigned lineno, unsigned column, + JSErrorCallback errorCallback, void* userRef, + const unsigned errorNumber, ...); + bool addNoteLatin1(js::ExclusiveContext* cx, + const char* filename, unsigned lineno, unsigned column, + JSErrorCallback errorCallback, void* userRef, + const unsigned errorNumber, ...); + bool addNoteUTF8(js::ExclusiveContext* cx, + const char* filename, unsigned lineno, unsigned column, + JSErrorCallback errorCallback, void* userRef, + const unsigned errorNumber, ...); + + size_t length(); + + // Create a deep copy of notes. + js::UniquePtr<JSErrorNotes> copy(JSContext* cx); + + class iterator : public std::iterator<std::input_iterator_tag, js::UniquePtr<Note>> + { + js::UniquePtr<Note>* note_; + public: + explicit iterator(js::UniquePtr<Note>* note = nullptr) : note_(note) + {} + + bool operator==(iterator other) const { + return note_ == other.note_; + } + bool operator!=(iterator other) const { + return !(*this == other); + } + iterator& operator++() { + note_++; + return *this; + } + reference operator*() { + return *note_; + } + }; + iterator begin(); + iterator end(); +}; + +/** + * Describes a single error or warning that occurs in the execution of script. + */ +class JSErrorReport : public JSErrorBase +{ // Offending source line without final '\n'. - // If ownsLinebuf__ is true, the buffer is freed in destructor. + // If ownsLinebuf_ is true, the buffer is freed in destructor. const char16_t* linebuf_; // Number of chars in linebuf_. Does not include trailing '\0'. @@ -5378,28 +5503,29 @@ class JSErrorReport public: JSErrorReport() : linebuf_(nullptr), linebufLength_(0), tokenOffset_(0), - filename(nullptr), lineno(0), column(0), - flags(0), errorNumber(0), - exnType(0), isMuted(false), - ownsLinebuf_(false), ownsMessage_(false) + notes(nullptr), + flags(0), exnType(0), isMuted(false), + ownsLinebuf_(false) {} ~JSErrorReport() { freeLinebuf(); - freeMessage(); } - const char* filename; /* source file name, URL, etc., or null */ - unsigned lineno; /* source line number */ - unsigned column; /* zero-based column index in line */ - unsigned flags; /* error/warning, etc. */ - unsigned errorNumber; /* the error number, e.g. see js.msg */ - int16_t exnType; /* One of the JSExnType constants */ - bool isMuted : 1; /* See the comment in ReadOnlyCompileOptions. */ + // Associated notes, or nullptr if there's no note. + js::UniquePtr<JSErrorNotes> notes; + + // error/warning, etc. + unsigned flags; + + // One of the JSExnType constants. + int16_t exnType; + + // See the comment in ReadOnlyCompileOptions. + bool isMuted : 1; private: bool ownsLinebuf_ : 1; - bool ownsMessage_ : 1; public: const char16_t* linebuf() const { @@ -5411,29 +5537,16 @@ class JSErrorReport size_t tokenOffset() const { return tokenOffset_; } - void initOwnedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, size_t tokenOffsetArg) { + void initOwnedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, + size_t tokenOffsetArg) { initBorrowedLinebuf(linebufArg, linebufLengthArg, tokenOffsetArg); ownsLinebuf_ = true; } - void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, size_t tokenOffsetArg); - void freeLinebuf(); - - const JS::ConstUTF8CharsZ message() const { - return message_; - } + void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, + size_t tokenOffsetArg); - void initOwnedMessage(const char* messageArg) { - initBorrowedMessage(messageArg); - ownsMessage_ = true; - } - void initBorrowedMessage(const char* messageArg) { - MOZ_ASSERT(!message_); - message_ = JS::ConstUTF8CharsZ(messageArg, strlen(messageArg)); - } - - JSString* newMessageString(JSContext* cx); - - void freeMessage(); + private: + void freeLinebuf(); }; /* @@ -6562,7 +6675,7 @@ struct JS_PUBLIC_API(PerformanceGroup) { uint64_t refCount_; }; -using PerformanceGroupVector = mozilla::Vector<RefPtr<js::PerformanceGroup>, 0, SystemAllocPolicy>; +using PerformanceGroupVector = mozilla::Vector<RefPtr<js::PerformanceGroup>, 8, SystemAllocPolicy>; /** * Commit any Performance Monitoring data. @@ -6601,10 +6714,6 @@ SetStopwatchIsMonitoringJank(JSContext*, bool); extern JS_PUBLIC_API(bool) GetStopwatchIsMonitoringJank(JSContext*); -// Extract the CPU rescheduling data. -extern JS_PUBLIC_API(void) -GetPerfMonitoringTestCpuRescheduling(JSContext*, uint64_t* stayed, uint64_t* moved); - /** * Add a number of microseconds to the time spent waiting on CPOWs diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 7a67c00954..e618c319fd 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -45,7 +45,6 @@ #include "vm/Caches-inl.h" #include "vm/Interpreter-inl.h" #include "vm/NativeObject-inl.h" -#include "vm/UnboxedObject-inl.h" using namespace js; using namespace js::gc; @@ -64,7 +63,7 @@ using JS::ToUint32; bool JS::IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer) { - if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) { + if (obj->is<ArrayObject>()) { *answer = IsArrayAnswer::Array; return true; } @@ -100,11 +99,6 @@ js::GetLengthProperty(JSContext* cx, HandleObject obj, uint32_t* lengthp) return true; } - if (obj->is<UnboxedArrayObject>()) { - *lengthp = obj->as<UnboxedArrayObject>().length(); - return true; - } - if (obj->is<ArgumentsObject>()) { ArgumentsObject& argsobj = obj->as<ArgumentsObject>(); if (!argsobj.hasOverriddenLength()) { @@ -253,18 +247,20 @@ static bool GetElement(JSContext* cx, HandleObject obj, HandleObject receiver, uint32_t index, bool* hole, MutableHandleValue vp) { - AssertGreaterThanZero(index); - if (index < GetAnyBoxedOrUnboxedInitializedLength(obj)) { - vp.set(GetAnyBoxedOrUnboxedDenseElement(obj, uint32_t(index))); - if (!vp.isMagic(JS_ELEMENTS_HOLE)) { - *hole = false; - return true; + if (obj->isNative()) { + NativeObject* nobj = &obj->as<NativeObject>(); + if (index < nobj->getDenseInitializedLength()) { + vp.set(nobj->getDenseElement(size_t(index))); + if (!vp.isMagic(JS_ELEMENTS_HOLE)) { + *hole = false; + return true; + } } - } - if (obj->is<ArgumentsObject>()) { - if (obj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) { - *hole = false; - return true; + if (nobj->is<ArgumentsObject>() && index <= UINT32_MAX) { + if (nobj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) { + *hole = false; + return true; + } } } @@ -283,8 +279,8 @@ ElementAdder::append(JSContext* cx, HandleValue v) { MOZ_ASSERT(index_ < length_); if (resObj_) { - DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_, v.address(), 1); + NativeObject* resObj = &resObj_->as<NativeObject>(); + DenseElementResult result = resObj->setOrExtendDenseElements(cx, index_, v.address(), 1); if (result == DenseElementResult::Failure) return false; if (result == DenseElementResult::Incomplete) { @@ -336,37 +332,31 @@ js::GetElementsWithAdder(JSContext* cx, HandleObject obj, HandleObject receiver, return true; } -template <JSValueType Type> -DenseElementResult -GetBoxedOrUnboxedDenseElements(JSObject* aobj, uint32_t length, Value* vp) +static bool +GetDenseElements(NativeObject* aobj, uint32_t length, Value* vp) { MOZ_ASSERT(!ObjectMayHaveExtraIndexedProperties(aobj)); - if (length > GetBoxedOrUnboxedInitializedLength<Type>(aobj)) - return DenseElementResult::Incomplete; + if (length > aobj->getDenseInitializedLength()) + return false; for (size_t i = 0; i < length; i++) { - vp[i] = GetBoxedOrUnboxedDenseElement<Type>(aobj, i); + vp[i] = aobj->getDenseElement(i); // No other indexed properties so hole => undefined. if (vp[i].isMagic(JS_ELEMENTS_HOLE)) vp[i] = UndefinedValue(); } - return DenseElementResult::Success; + return true; } -DefineBoxedOrUnboxedFunctor3(GetBoxedOrUnboxedDenseElements, - JSObject*, uint32_t, Value*); - bool js::GetElements(JSContext* cx, HandleObject aobj, uint32_t length, Value* vp) { if (!ObjectMayHaveExtraIndexedProperties(aobj)) { - GetBoxedOrUnboxedDenseElementsFunctor functor(aobj, length, vp); - DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, aobj); - if (result != DenseElementResult::Incomplete) - return result == DenseElementResult::Success; + if (GetDenseElements(&aobj->as<NativeObject>(), length, vp)) + return true; } if (aobj->is<ArgumentsObject>()) { @@ -398,9 +388,9 @@ SetArrayElement(JSContext* cx, HandleObject obj, double index, HandleValue v) { MOZ_ASSERT(index >= 0); - if ((obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) && !obj->isIndexed() && index <= UINT32_MAX) { - DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(index), v.address(), 1); + if (obj->is<ArrayObject>() && !obj->isIndexed() && index <= UINT32_MAX) { + NativeObject* nobj = &obj->as<NativeObject>(); + DenseElementResult result = nobj->setOrExtendDenseElements(cx, uint32_t(index), v.address(), 1); if (result != DenseElementResult::Incomplete) return result == DenseElementResult::Success; } @@ -520,24 +510,6 @@ struct ReverseIndexComparator } }; -bool -js::CanonicalizeArrayLengthValue(JSContext* cx, HandleValue v, uint32_t* newLen) -{ - double d; - - if (!ToUint32(cx, v, newLen)) - return false; - - if (!ToNumber(cx, v, &d)) - return false; - - if (d == *newLen) - return true; - - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); - return false; -} - /* ES6 draft rev 34 (2015 Feb 20) 9.4.2.4 ArraySetLength */ bool js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id, @@ -559,12 +531,22 @@ js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id, } else { // Step 2 is irrelevant in our implementation. - // Steps 3-7. - MOZ_ASSERT_IF(attrs & JSPROP_IGNORE_VALUE, value.isUndefined()); - if (!CanonicalizeArrayLengthValue(cx, value, &newLen)) + // Step 3. + if (!ToUint32(cx, value, &newLen)) return false; - // Step 8 is irrelevant in our implementation. + // Step 4. + double d; + if (!ToNumber(cx, value, &d)) + return false; + + // Step 5. + if (d != newLen) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); + return false; + } + + // Steps 6-8 are irrelevant in our implementation. } // Steps 9-11. @@ -620,7 +602,7 @@ js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id, // for..in iteration over the array. Keys deleted before being reached // during the iteration must not be visited, and suppressing them here // would be too costly. - ObjectGroup* arrGroup = arr->getGroup(cx); + ObjectGroup* arrGroup = JSObject::getGroup(cx, arr); if (MOZ_UNLIKELY(!arrGroup)) return false; if (!arr->isIndexed() && !MOZ_UNLIKELY(arrGroup->hasAllFlags(OBJECT_FLAG_ITERATED))) { @@ -823,7 +805,7 @@ array_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v) static inline bool ObjectMayHaveExtraIndexedOwnProperties(JSObject* obj) { - return (!obj->isNative() && !obj->is<UnboxedArrayObject>()) || + return !obj->isNative() || obj->isIndexed() || obj->is<TypedArrayObject>() || ClassMayResolveId(*obj->runtimeFromAnyThread()->commonNames, @@ -854,7 +836,7 @@ js::ObjectMayHaveExtraIndexedProperties(JSObject* obj) if (ObjectMayHaveExtraIndexedOwnProperties(obj)) return true; - if (GetAnyBoxedOrUnboxedInitializedLength(obj) != 0) + if (obj->as<NativeObject>().getDenseInitializedLength() != 0) return true; } while (true); } @@ -1064,31 +1046,32 @@ struct StringSeparatorOp } }; -template <typename SeparatorOp, JSValueType Type> -static DenseElementResult -ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t length, +template <typename SeparatorOp> +static bool +ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleNativeObject obj, uint32_t length, StringBuffer& sb, uint32_t* numProcessed) { // This loop handles all elements up to initializedLength. If // length > initLength we rely on the second loop to add the // other elements. MOZ_ASSERT(*numProcessed == 0); - uint32_t initLength = Min<uint32_t>(GetBoxedOrUnboxedInitializedLength<Type>(obj), length); + uint32_t initLength = Min<uint32_t>(obj->getDenseInitializedLength(), + length); while (*numProcessed < initLength) { if (!CheckForInterrupt(cx)) - return DenseElementResult::Failure; + return false; - Value elem = GetBoxedOrUnboxedDenseElement<Type>(obj, *numProcessed); + Value elem = obj->as<NativeObject>().getDenseElement(*numProcessed); if (elem.isString()) { if (!sb.append(elem.toString())) - return DenseElementResult::Failure; + return false; } else if (elem.isNumber()) { if (!NumberValueToStringBuffer(cx, elem, sb)) - return DenseElementResult::Failure; + return false; } else if (elem.isBoolean()) { if (!BooleanToStringBuffer(elem.toBoolean(), sb)) - return DenseElementResult::Failure; + return false; } else if (elem.isObject() || elem.isSymbol()) { /* * Object stringifying could modify the initialized length or make @@ -1104,33 +1087,13 @@ ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_ } if (++(*numProcessed) != length && !sepOp(cx, sb)) - return DenseElementResult::Failure; + return false; } - return DenseElementResult::Incomplete; + return true; } template <typename SeparatorOp> -struct ArrayJoinDenseKernelFunctor { - JSContext* cx; - SeparatorOp sepOp; - HandleObject obj; - uint32_t length; - StringBuffer& sb; - uint32_t* numProcessed; - - ArrayJoinDenseKernelFunctor(JSContext* cx, SeparatorOp sepOp, HandleObject obj, - uint32_t length, StringBuffer& sb, uint32_t* numProcessed) - : cx(cx), sepOp(sepOp), obj(obj), length(length), sb(sb), numProcessed(numProcessed) - {} - - template <JSValueType Type> - DenseElementResult operator()() { - return ArrayJoinDenseKernel<SeparatorOp, Type>(cx, sepOp, obj, length, sb, numProcessed); - } -}; - -template <typename SeparatorOp> static bool ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t length, StringBuffer& sb) @@ -1138,10 +1101,10 @@ ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t len uint32_t i = 0; if (!ObjectMayHaveExtraIndexedProperties(obj)) { - ArrayJoinDenseKernelFunctor<SeparatorOp> functor(cx, sepOp, obj, length, sb, &i); - DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj); - if (result == DenseElementResult::Failure) + if (!ArrayJoinDenseKernel<SeparatorOp>(cx, sepOp, obj.as<NativeObject>(), length, sb, &i)) + { return false; + } } if (i != length) { @@ -1212,11 +1175,14 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp) // An optimized version of a special case of steps 7-11: when length==1 and // the 0th element is a string, ToString() of that element is a no-op and // so it can be immediately returned as the result. - if (length == 1 && GetAnyBoxedOrUnboxedInitializedLength(obj) == 1) { - Value elem0 = GetAnyBoxedOrUnboxedDenseElement(obj, 0); - if (elem0.isString()) { - args.rval().set(elem0); - return true; + if (length == 1 && obj->isNative()) { + NativeObject* nobj = &obj->as<NativeObject>(); + if (nobj->getDenseInitializedLength() == 1) { + Value elem0 = nobj->getDenseElement(0); + if (elem0.isString()) { + args.rval().set(elem0); + return true; + } } } @@ -1259,7 +1225,7 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp) } // Step 11 - JSString *str = sb.finishString(); + JSString* str = sb.finishString(); if (!str) return false; @@ -1288,10 +1254,6 @@ array_toLocaleString(JSContext* cx, unsigned argc, Value* vp) args.rval().setString(cx->names().empty); return true; } - if (obj->is<UnboxedArrayObject>() && obj->as<UnboxedArrayObject>().length() == 0) { - args.rval().setString(cx->names().empty); - return true; - } AutoCycleDetector detector(cx, obj); if (!detector.init()) @@ -1323,13 +1285,14 @@ InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start, if (count == 0) return true; - ObjectGroup* group = obj->getGroup(cx); + ObjectGroup* group = JSObject::getGroup(cx, obj); if (!group) return false; if (!ObjectMayHaveExtraIndexedProperties(obj)) { - DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, start, vector, count, updateTypes); + NativeObject* nobj = &obj->as<NativeObject>(); + DenseElementResult result = nobj->setOrExtendDenseElements(cx, uint32_t(start), vector, + count, updateTypes); if (result != DenseElementResult::Incomplete) return result == DenseElementResult::Success; } @@ -1363,54 +1326,45 @@ InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start, return true; } -template <JSValueType Type> -DenseElementResult -ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length) +static DenseElementResult +ArrayReverseDenseKernel(JSContext* cx, HandleNativeObject obj, uint32_t length) { /* An empty array or an array with no elements is already reversed. */ - if (length == 0 || GetBoxedOrUnboxedInitializedLength<Type>(obj) == 0) + if (length == 0 || obj->getDenseInitializedLength() == 0) return DenseElementResult::Success; - if (Type == JSVAL_TYPE_MAGIC) { - if (obj->as<NativeObject>().denseElementsAreFrozen()) - return DenseElementResult::Incomplete; + if (obj->denseElementsAreFrozen()) + return DenseElementResult::Incomplete; - /* - * It's actually surprisingly complicated to reverse an array due to the - * orthogonality of array length and array capacity while handling - * leading and trailing holes correctly. Reversing seems less likely to - * be a common operation than other array mass-mutation methods, so for - * now just take a probably-small memory hit (in the absence of too many - * holes in the array at its start) and ensure that the capacity is - * sufficient to hold all the elements in the array if it were full. - */ - DenseElementResult result = obj->as<NativeObject>().ensureDenseElements(cx, length, 0); - if (result != DenseElementResult::Success) - return result; + /* + * It's actually surprisingly complicated to reverse an array due to the + * orthogonality of array length and array capacity while handling + * leading and trailing holes correctly. Reversing seems less likely to + * be a common operation than other array mass-mutation methods, so for + * now just take a probably-small memory hit (in the absence of too many + * holes in the array at its start) and ensure that the capacity is + * sufficient to hold all the elements in the array if it were full. + */ + DenseElementResult result = obj->ensureDenseElements(cx, length, 0); + if (result != DenseElementResult::Success) + return result; - /* Fill out the array's initialized length to its proper length. */ - obj->as<NativeObject>().ensureDenseInitializedLength(cx, length, 0); - } else { - // Unboxed arrays can only be reversed here if their initialized length - // matches their actual length. Otherwise the reversal will place holes - // at the beginning of the array, which we don't support. - if (length != obj->as<UnboxedArrayObject>().initializedLength()) - return DenseElementResult::Incomplete; - } + /* Fill out the array's initialized length to its proper length. */ + obj->ensureDenseInitializedLength(cx, length, 0); RootedValue origlo(cx), orighi(cx); uint32_t lo = 0, hi = length - 1; for (; lo < hi; lo++, hi--) { - origlo = GetBoxedOrUnboxedDenseElement<Type>(obj, lo); - orighi = GetBoxedOrUnboxedDenseElement<Type>(obj, hi); - SetBoxedOrUnboxedDenseElementNoTypeChange<Type>(obj, lo, orighi); + origlo = obj->getDenseElement(lo); + orighi = obj->getDenseElement(hi); + obj->setDenseElement(lo, orighi); if (orighi.isMagic(JS_ELEMENTS_HOLE) && !SuppressDeletedProperty(cx, obj, INT_TO_JSID(lo))) { return DenseElementResult::Failure; } - SetBoxedOrUnboxedDenseElementNoTypeChange<Type>(obj, hi, origlo); + obj->setDenseElement(hi, origlo); if (origlo.isMagic(JS_ELEMENTS_HOLE) && !SuppressDeletedProperty(cx, obj, INT_TO_JSID(hi))) { @@ -1421,9 +1375,6 @@ ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length) return DenseElementResult::Success; } -DefineBoxedOrUnboxedFunctor3(ArrayReverseDenseKernel, - JSContext*, HandleObject, uint32_t); - bool js::array_reverse(JSContext* cx, unsigned argc, Value* vp) { @@ -1438,8 +1389,8 @@ js::array_reverse(JSContext* cx, unsigned argc, Value* vp) return false; if (!ObjectMayHaveExtraIndexedProperties(obj)) { - ArrayReverseDenseKernelFunctor functor(cx, obj, len); - DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj); + DenseElementResult result = + ArrayReverseDenseKernel(cx, obj.as<NativeObject>(), uint32_t(len)); if (result != DenseElementResult::Incomplete) { /* * Per ECMA-262, don't update the length of the array, even if the new @@ -1711,11 +1662,11 @@ MatchNumericComparator(JSContext* cx, const Value& v) if (!obj.is<JSFunction>()) return Match_None; - JSFunction* fun = &obj.as<JSFunction>(); + RootedFunction fun(cx, &obj.as<JSFunction>()); if (!fun->isInterpreted() || fun->isClassConstructor()) return Match_None; - JSScript* script = fun->getOrCreateScript(cx); + JSScript* script = JSFunction::getOrCreateScript(cx, fun); if (!script) return Match_Failure; @@ -2082,8 +2033,8 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp) if (!ObjectMayHaveExtraIndexedProperties(obj)) { DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, length, - args.array(), args.length()); + obj->as<NativeObject>().setOrExtendDenseElements(cx, uint32_t(length), + args.array(), args.length()); if (result != DenseElementResult::Incomplete) { if (result == DenseElementResult::Failure) return false; @@ -2091,14 +2042,8 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp) uint32_t newlength = length + args.length(); args.rval().setNumber(newlength); - // SetOrExtendAnyBoxedOrUnboxedDenseElements takes care of updating the - // length for boxed and unboxed arrays. Handle updates to the length of - // non-arrays here. - bool isArray; - if (!IsArray(cx, obj, &isArray)) - return false; - - if (!isArray) + // Handle updates to the length of non-arrays here. + if (!obj->is<ArrayObject>()) return SetLengthProperty(cx, obj, newlength); return true; @@ -2154,73 +2099,74 @@ js::array_pop(JSContext* cx, unsigned argc, Value* vp) return SetLengthProperty(cx, obj, index); } -template <JSValueType Type> -static inline DenseElementResult -ShiftMoveBoxedOrUnboxedDenseElements(JSObject* obj) +void +js::ArrayShiftMoveElements(NativeObject* obj) { - MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj)); + MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable()); + + size_t initlen = obj->getDenseInitializedLength(); + MOZ_ASSERT(initlen > 0); /* * At this point the length and initialized length have already been * decremented and the result fetched, so just shift the array elements * themselves. */ - size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj); - if (Type == JSVAL_TYPE_MAGIC) { - obj->as<NativeObject>().moveDenseElementsNoPreBarrier(0, 1, initlen); - } else { - uint8_t* data = obj->as<UnboxedArrayObject>().elements(); - size_t elementSize = UnboxedTypeSize(Type); - memmove(data, data + elementSize, initlen * elementSize); - } - - return DenseElementResult::Success; + obj->moveDenseElementsNoPreBarrier(0, 1, initlen); } -DefineBoxedOrUnboxedFunctor1(ShiftMoveBoxedOrUnboxedDenseElements, JSObject*); +static inline void +SetInitializedLength(JSContext* cx, NativeObject* obj, size_t initlen) +{ + size_t oldInitlen = obj->getDenseInitializedLength(); + obj->setDenseInitializedLength(initlen); + if (initlen < oldInitlen) + obj->shrinkElements(cx, initlen); +} -void -js::ArrayShiftMoveElements(JSObject* obj) +static DenseElementResult +MoveDenseElements(JSContext* cx, NativeObject* obj, uint32_t dstStart, uint32_t srcStart, + uint32_t length) { - MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable()); + if (obj->denseElementsAreFrozen()) + return DenseElementResult::Incomplete; + + if (!obj->maybeCopyElementsForWrite(cx)) + return DenseElementResult::Failure; + obj->moveDenseElements(dstStart, srcStart, length); - ShiftMoveBoxedOrUnboxedDenseElementsFunctor functor(obj); - JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success); + return DenseElementResult::Success; } -template <JSValueType Type> -DenseElementResult +static DenseElementResult ArrayShiftDenseKernel(JSContext* cx, HandleObject obj, MutableHandleValue rval) { if (ObjectMayHaveExtraIndexedProperties(obj)) return DenseElementResult::Incomplete; - RootedObjectGroup group(cx, obj->getGroup(cx)); + RootedObjectGroup group(cx, JSObject::getGroup(cx, obj)); if (MOZ_UNLIKELY(!group)) return DenseElementResult::Failure; if (MOZ_UNLIKELY(group->hasAllFlags(OBJECT_FLAG_ITERATED))) return DenseElementResult::Incomplete; - size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj); + size_t initlen = obj->as<NativeObject>().getDenseInitializedLength(); if (initlen == 0) return DenseElementResult::Incomplete; - rval.set(GetBoxedOrUnboxedDenseElement<Type>(obj, 0)); + rval.set(obj->as<NativeObject>().getDenseElement(0)); if (rval.isMagic(JS_ELEMENTS_HOLE)) rval.setUndefined(); - DenseElementResult result = MoveBoxedOrUnboxedDenseElements<Type>(cx, obj, 0, 1, initlen - 1); + DenseElementResult result = MoveDenseElements(cx, &obj->as<NativeObject>(), 0, 1, initlen - 1); if (result != DenseElementResult::Success) return result; - SetBoxedOrUnboxedInitializedLength<Type>(cx, obj, initlen - 1); + SetInitializedLength(cx, obj.as<NativeObject>(), initlen - 1); return DenseElementResult::Success; } -DefineBoxedOrUnboxedFunctor3(ArrayShiftDenseKernel, - JSContext*, HandleObject, MutableHandleValue); - /* ES5 15.4.4.9 */ bool js::array_shift(JSContext* cx, unsigned argc, Value* vp) @@ -2252,8 +2198,7 @@ js::array_shift(JSContext* cx, unsigned argc, Value* vp) uint32_t newlen = len - 1; /* Fast paths. */ - ArrayShiftDenseKernelFunctor functor(cx, obj, args.rval()); - DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj); + DenseElementResult result = ArrayShiftDenseKernel(cx, obj, args.rval()); if (result != DenseElementResult::Incomplete) { if (result == DenseElementResult::Failure) return false; @@ -2307,9 +2252,6 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp) if (args.length() > 0) { /* Slide up the array to make room for all args at the bottom. */ if (length > 0) { - // Only include a fast path for boxed arrays. Unboxed arrays can'nt - // be optimized here because unshifting temporarily places holes at - // the start of the array. bool optimized = false; do { if (!obj->is<ArrayObject>()) @@ -2369,10 +2311,10 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp) } /* - * Returns true if this is a dense or unboxed array whose |count| properties - * starting from |startingIndex| may be accessed (get, set, delete) directly - * through its contiguous vector of elements without fear of getters, setters, - * etc. along the prototype chain, or of enumerators requiring notification of + * Returns true if this is a dense array whose properties ending at |endIndex| + * (exclusive) may be accessed (get, set, delete) directly through its + * contiguous vector of elements without fear of getters, setters, etc. along + * the prototype chain, or of enumerators requiring notification of * modifications. */ static inline bool @@ -2383,11 +2325,11 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co return false; /* There's no optimizing possible if it's not an array. */ - if (!arr->is<ArrayObject>() && !arr->is<UnboxedArrayObject>()) + if (!arr->is<ArrayObject>()) return false; /* If it's a frozen array, always pick the slow path */ - if (arr->is<ArrayObject>() && arr->as<ArrayObject>().denseElementsAreFrozen()) + if (arr->as<ArrayObject>().denseElementsAreFrozen()) return false; /* @@ -2398,7 +2340,7 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co * deleted if a hole is moved from one location to another location not yet * visited. See bug 690622. */ - ObjectGroup* arrGroup = arr->getGroup(cx); + ObjectGroup* arrGroup = JSObject::getGroup(cx, arr); if (!arrGroup) { cx->recoverFromOutOfMemory(); return false; @@ -2419,14 +2361,23 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co * is subsumed by the initializedLength comparison.) */ return !ObjectMayHaveExtraIndexedProperties(arr) && - startingIndex + count <= GetAnyBoxedOrUnboxedInitializedLength(arr); + startingIndex + count <= arr->as<NativeObject>().getDenseInitializedLength(); } -/* ES 2016 draft Mar 25, 2016 22.1.3.26. */ -bool -js::array_splice(JSContext* cx, unsigned argc, Value* vp) +static inline DenseElementResult +CopyDenseElements(JSContext* cx, NativeObject* dst, NativeObject* src, + uint32_t dstStart, uint32_t srcStart, uint32_t length) { - return array_splice_impl(cx, argc, vp, true); + MOZ_ASSERT(dst->getDenseInitializedLength() == dstStart); + MOZ_ASSERT(src->getDenseInitializedLength() >= srcStart + length); + MOZ_ASSERT(dst->getDenseCapacity() >= dstStart + length); + + dst->setDenseInitializedLength(dstStart + length); + + const Value* vp = src->getDenseElements() + srcStart; + dst->initDenseElements(dstStart, vp, length); + + return DenseElementResult::Success; } static inline bool @@ -2458,8 +2409,8 @@ ArraySpliceCopy(JSContext* cx, HandleObject arr, HandleObject obj, return SetLengthProperty(cx, arr, actualDeleteCount); } -bool -js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUsed) +static bool +array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUsed) { AutoSPSEntry pseudoFrame(cx->runtime(), "Array.prototype.splice"); CallArgs args = CallArgsFromVp(argc, vp); @@ -2520,7 +2471,9 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI /* Steps 10-11. */ DebugOnly<DenseElementResult> result = - CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, 0, actualStart, actualDeleteCount); + CopyDenseElements(cx, &arr->as<NativeObject>(), + &obj->as<NativeObject>(), 0, + actualStart, actualDeleteCount); MOZ_ASSERT(result.value == DenseElementResult::Success); /* Step 12 (implicit). */ @@ -2557,14 +2510,13 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI if (CanOptimizeForDenseStorage(obj, 0, len, cx)) { /* Steps 15.a-b. */ DenseElementResult result = - MoveAnyBoxedOrUnboxedDenseElements(cx, obj, targetIndex, sourceIndex, - len - sourceIndex); + MoveDenseElements(cx, &obj->as<NativeObject>(), targetIndex, sourceIndex, len - sourceIndex); MOZ_ASSERT(result != DenseElementResult::Incomplete); if (result == DenseElementResult::Failure) return false; /* Steps 15.c-d. */ - SetAnyBoxedOrUnboxedInitializedLength(cx, obj, finalLength); + SetInitializedLength(cx, obj.as<NativeObject>(), finalLength); } else { /* * This is all very slow if the length is very large. We don't yet @@ -2644,15 +2596,15 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) { DenseElementResult result = - MoveAnyBoxedOrUnboxedDenseElements(cx, obj, actualStart + itemCount, - actualStart + actualDeleteCount, - len - (actualStart + actualDeleteCount)); + MoveDenseElements(cx, &obj->as<NativeObject>(), actualStart + itemCount, + actualStart + actualDeleteCount, + len - (actualStart + actualDeleteCount)); MOZ_ASSERT(result != DenseElementResult::Incomplete); if (result == DenseElementResult::Failure) return false; /* Steps 16.a-b. */ - SetAnyBoxedOrUnboxedInitializedLength(cx, obj, len + itemCount - actualDeleteCount); + SetInitializedLength(cx, obj.as<NativeObject>(), len + itemCount - actualDeleteCount); } else { RootedValue fromValue(cx); for (double k = len - actualDeleteCount; k > actualStart; k--) { @@ -2708,6 +2660,19 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI return true; } +/* ES 2016 draft Mar 25, 2016 22.1.3.26. */ +bool +js::array_splice(JSContext* cx, unsigned argc, Value* vp) +{ + return array_splice_impl(cx, argc, vp, true); +} + +static bool +array_splice_noRetVal(JSContext* cx, unsigned argc, Value* vp) +{ + return array_splice_impl(cx, argc, vp, false); +} + struct SortComparatorIndexes { bool operator()(uint32_t a, uint32_t b, bool* lessOrEqualp) { @@ -2824,7 +2789,7 @@ SliceSlowly(JSContext* cx, HandleObject obj, HandleObject receiver, } static bool -SliceSparse(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end, HandleObject result) +SliceSparse(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end, HandleArrayObject result) { MOZ_ASSERT(begin <= end); @@ -2874,26 +2839,28 @@ ArraySliceOrdinary(JSContext* cx, HandleObject obj, uint32_t length, uint32_t be begin = end; if (!ObjectMayHaveExtraIndexedProperties(obj)) { - size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj); + size_t initlen = obj->as<NativeObject>().getDenseInitializedLength(); size_t count = 0; if (initlen > begin) count = Min<size_t>(initlen - begin, end - begin); - RootedObject narr(cx, NewFullyAllocatedArrayTryReuseGroup(cx, obj, count)); + RootedArrayObject narr(cx, NewFullyAllocatedArrayTryReuseGroup(cx, obj, count)); if (!narr) return false; - SetAnyBoxedOrUnboxedArrayLength(cx, narr, end - begin); + + MOZ_ASSERT(count >= narr->as<ArrayObject>().length()); + narr->as<ArrayObject>().setLength(cx, count); if (count) { DebugOnly<DenseElementResult> result = - CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, count); + CopyDenseElements(cx, &narr->as<NativeObject>(), &obj->as<NativeObject>(), 0, begin, count); MOZ_ASSERT(result.value == DenseElementResult::Success); } arr.set(narr); return true; } - RootedObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin)); + RootedArrayObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin)); if (!narr) return false; @@ -3010,11 +2977,10 @@ js::array_slice(JSContext* cx, unsigned argc, Value* vp) return true; } -template <JSValueType Type> -DenseElementResult -ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t endArg, JSObject* result) +static bool +ArraySliceDenseKernel(JSContext* cx, ArrayObject* arr, int32_t beginArg, int32_t endArg, ArrayObject* result) { - int32_t length = GetAnyBoxedOrUnboxedArrayLength(obj); + int32_t length = arr->length(); uint32_t begin = NormalizeSliceTerm(beginArg, length); uint32_t end = NormalizeSliceTerm(endArg, length); @@ -3022,33 +2988,33 @@ ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t en if (begin > end) begin = end; - size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj); + size_t initlen = arr->getDenseInitializedLength(); + size_t count = Min<size_t>(initlen - begin, end - begin); if (initlen > begin) { - size_t count = Min<size_t>(initlen - begin, end - begin); if (count) { - DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements<Type>(cx, result, count); - if (rv != DenseElementResult::Success) - return rv; - CopyBoxedOrUnboxedDenseElements<Type, Type>(cx, result, obj, 0, begin, count); + if (!result->ensureElements(cx, count)) + return false; + CopyDenseElements(cx, &result->as<NativeObject>(), &arr->as<NativeObject>(), 0, begin, count); } } - SetAnyBoxedOrUnboxedArrayLength(cx, result, end - begin); - return DenseElementResult::Success; -} + MOZ_ASSERT(count >= result->length()); + result->setLength(cx, count); -DefineBoxedOrUnboxedFunctor5(ArraySliceDenseKernel, - JSContext*, JSObject*, int32_t, int32_t, JSObject*); + return true; +} JSObject* js::array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end, HandleObject result) { if (result && IsArraySpecies(cx, obj)) { - ArraySliceDenseKernelFunctor functor(cx, obj, begin, end, result); - DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result); - MOZ_ASSERT(rv != DenseElementResult::Incomplete); - return rv == DenseElementResult::Success ? result : nullptr; + if (!ArraySliceDenseKernel(cx, &obj->as<ArrayObject>(), begin, end, + &result->as<ArrayObject>())) + { + return nullptr; + } + return result; } // Slower path if the JIT wasn't able to allocate an object inline. @@ -3079,7 +3045,7 @@ array_isArray(JSContext* cx, unsigned argc, Value* vp) static bool ArrayFromCallArgs(JSContext* cx, CallArgs& args, HandleObject proto = nullptr) { - JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto); + ArrayObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto); if (!obj) return false; @@ -3124,6 +3090,15 @@ array_of(JSContext* cx, unsigned argc, Value* vp) return true; } +const JSJitInfo js::array_splice_info = { + { (JSJitGetterOp)array_splice_noRetVal }, + { 0 }, /* unused */ + { 0 }, /* unused */ + JSJitInfo::IgnoresReturnValueNative, + JSJitInfo::AliasEverything, + JSVAL_TYPE_UNDEFINED, +}; + static const JSFunctionSpec array_methods[] = { #if JS_HAS_TOSOURCE JS_FN(js_toSource_str, array_toSource, 0,0), @@ -3139,7 +3114,7 @@ static const JSFunctionSpec array_methods[] = { JS_INLINABLE_FN("pop", array_pop, 0,0, ArrayPop), JS_INLINABLE_FN("shift", array_shift, 0,0, ArrayShift), JS_FN("unshift", array_unshift, 1,0), - JS_INLINABLE_FN("splice", array_splice, 2,0, ArraySplice), + JS_FNINFO("splice", array_splice, &array_splice_info, 2,0), /* Pythonic sequence methods. */ JS_SELF_HOSTED_FN("concat", "ArrayConcat", 1,0), @@ -3169,6 +3144,11 @@ static const JSFunctionSpec array_methods[] = { /* ES7 additions */ JS_SELF_HOSTED_FN("includes", "ArrayIncludes", 2,0), + + /* ES2019 additions */ + JS_SELF_HOSTED_FN("flat", "ArrayFlat", 0,0), + JS_SELF_HOSTED_FN("flatMap", "ArrayFlatMap", 1,0), + JS_FS_END }; @@ -3239,7 +3219,7 @@ ArrayConstructorImpl(JSContext* cx, CallArgs& args, bool isConstructor) } } - JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto); + ArrayObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto); if (!obj) return false; @@ -3265,7 +3245,7 @@ js::array_construct(JSContext* cx, unsigned argc, Value* vp) return ArrayConstructorImpl(cx, args, /* isConstructor = */ false); } -JSObject* +ArrayObject* js::ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt) { if (lengthInt < 0) { @@ -3281,7 +3261,7 @@ static JSObject* CreateArrayPrototype(JSContext* cx, JSProtoKey key) { MOZ_ASSERT(key == JSProto_Array); - RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx)); + RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, cx->global())); if (!proto) return nullptr; @@ -3301,7 +3281,7 @@ CreateArrayPrototype(JSContext* cx, JSProtoKey key) metadata)); if (!arrayProto || !JSObject::setSingleton(cx, arrayProto) || - !arrayProto->setDelegate(cx) || + !JSObject::setDelegate(cx, arrayProto) || !AddLengthProperty(cx, arrayProto)) { return nullptr; @@ -3333,6 +3313,8 @@ array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto) !DefineProperty(cx, unscopables, cx->names().fill, value) || !DefineProperty(cx, unscopables, cx->names().find, value) || !DefineProperty(cx, unscopables, cx->names().findIndex, value) || + !DefineProperty(cx, unscopables, cx->names().flat, value) || + !DefineProperty(cx, unscopables, cx->names().flatMap, value) || !DefineProperty(cx, unscopables, cx->names().includes, value) || !DefineProperty(cx, unscopables, cx->names().keys, value) || !DefineProperty(cx, unscopables, cx->names().values, value)) @@ -3558,7 +3540,7 @@ js::NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSOb return arr; } -JSObject* +ArrayObject* js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap) { MOZ_ASSERT(!gc::IsInsideNursery(templateObject)); @@ -3571,30 +3553,21 @@ js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc return arr; } -// Return a new boxed or unboxed array with the specified length and allocated -// capacity (up to maxLength), using the specified group if possible. If the -// specified group cannot be used, ensure that the created array at least has -// the given [[Prototype]]. +// Return a new array with the specified length and allocated capacity (up to +// maxLength), using the specified group if possible. If the specified group +// cannot be used, ensure that the created array at least has the given +// [[Prototype]]. template <uint32_t maxLength> -static inline JSObject* +static inline ArrayObject* NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length, NewObjectKind newKind = GenericObject) { MOZ_ASSERT(newKind != SingletonObject); - if (group->maybePreliminaryObjects()) - group->maybePreliminaryObjects()->maybeAnalyze(cx, group); - - if (group->shouldPreTenure() || group->maybePreliminaryObjects()) + if (group->shouldPreTenure()) newKind = TenuredObject; RootedObject proto(cx, group->proto().toObject()); - if (group->maybeUnboxedLayout()) { - if (length > UnboxedArrayObject::MaximumCapacity) - return NewArray<maxLength>(cx, length, proto, newKind); - return UnboxedArrayObject::create(cx, group, length, newKind, maxLength); - } - ArrayObject* res = NewArray<maxLength>(cx, length, proto, newKind); if (!res) return nullptr; @@ -3606,20 +3579,17 @@ NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length if (res->length() > INT32_MAX) res->setLength(cx, res->length()); - if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects()) - preliminaryObjects->registerNewObject(res); - return res; } -JSObject* +ArrayObject* js::NewFullyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length, NewObjectKind newKind) { return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind); } -JSObject* +ArrayObject* js::NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length) { return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length); @@ -3628,42 +3598,39 @@ js::NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup g // Return a new array with the default prototype and specified allocated // capacity and length. If possible, try to reuse the group of the input // object. The resulting array will either reuse the input object's group or -// will have unknown property types. Additionally, the result will have the -// same boxed/unboxed elements representation as the input object, unless -// |length| is larger than the input object's initialized length (in which case -// UnboxedArrayObject::MaximumCapacity might be exceeded). +// will have unknown property types. template <uint32_t maxLength> -static inline JSObject* -NewArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length, +static inline ArrayObject* +NewArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length, NewObjectKind newKind = GenericObject) { - if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>()) + if (!obj->is<ArrayObject>()) return NewArray<maxLength>(cx, length, nullptr, newKind); if (obj->staticPrototype() != cx->global()->maybeGetArrayPrototype()) return NewArray<maxLength>(cx, length, nullptr, newKind); - RootedObjectGroup group(cx, obj->getGroup(cx)); + RootedObjectGroup group(cx, JSObject::getGroup(cx, obj)); if (!group) return nullptr; return NewArrayTryUseGroup<maxLength>(cx, group, length, newKind); } -JSObject* -js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length, +ArrayObject* +js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length, NewObjectKind newKind) { return NewArrayTryReuseGroup<UINT32_MAX>(cx, obj, length, newKind); } -JSObject* -js::NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length) +ArrayObject* +js::NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length) { return NewArrayTryReuseGroup<ArrayObject::EagerAllocationMaxLength>(cx, obj, length); } -JSObject* +ArrayObject* js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, NewObjectKind newKind) { @@ -3673,7 +3640,7 @@ js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind); } -JSObject* +ArrayObject* js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto) { RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto)); @@ -3682,68 +3649,23 @@ js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length); } -bool -js::MaybeAnalyzeBeforeCreatingLargeArray(ExclusiveContext* cx, HandleObjectGroup group, - const Value* vp, size_t length) -{ - static const size_t EagerPreliminaryObjectAnalysisThreshold = 800; - - // Force analysis to see if an unboxed array can be used when making a - // sufficiently large array, to avoid excessive analysis and copying later - // on. If this is the first array of its group that is being created, first - // make a dummy array with the initial elements of the array we are about - // to make, so there is some basis for the unboxed array analysis. - if (length > EagerPreliminaryObjectAnalysisThreshold) { - if (PreliminaryObjectArrayWithTemplate* objects = group->maybePreliminaryObjects()) { - if (objects->empty()) { - size_t nlength = Min<size_t>(length, 100); - JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, nlength); - if (!obj) - return false; - DebugOnly<DenseElementResult> result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, nlength, - ShouldUpdateTypes::Update); - MOZ_ASSERT(result.value == DenseElementResult::Success); - } - objects->maybeAnalyze(cx, group, /* forceAnalyze = */ true); - } - } - return true; -} - -JSObject* +ArrayObject* js::NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, const Value* vp, size_t length, NewObjectKind newKind, ShouldUpdateTypes updateTypes) { - if (!MaybeAnalyzeBeforeCreatingLargeArray(cx, group, vp, length)) - return nullptr; - - JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length, newKind); + ArrayObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length, newKind); if (!obj) return nullptr; - DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, updateTypes); + DenseElementResult result = obj->setOrExtendDenseElements(cx, 0, vp, length, updateTypes); if (result == DenseElementResult::Failure) return nullptr; - if (result == DenseElementResult::Success) - return obj; - - MOZ_ASSERT(obj->is<UnboxedArrayObject>()); - if (!UnboxedArrayObject::convertToNative(cx->asJSContext(), obj)) - return nullptr; - - result = SetOrExtendBoxedOrUnboxedDenseElements<JSVAL_TYPE_MAGIC>(cx, obj, 0, vp, length, - updateTypes); - MOZ_ASSERT(result != DenseElementResult::Incomplete); - if (result == DenseElementResult::Failure) - return nullptr; - + MOZ_ASSERT(result == DenseElementResult::Success); return obj; } -JSObject* +ArrayObject* js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length, HandleObject proto /* = nullptr */) { diff --git a/js/src/jsarray.h b/js/src/jsarray.h index e22cde8810..d0084731f9 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -72,49 +72,37 @@ extern ArrayObject* NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSObject* templateObject); /* Create a dense array with the same copy-on-write elements as another object. */ -extern JSObject* +extern ArrayObject* NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap); -// The methods below can create either boxed or unboxed arrays. - -extern JSObject* +extern ArrayObject* NewFullyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length, NewObjectKind newKind = GenericObject); -extern JSObject* +extern ArrayObject* NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length); -extern JSObject* -NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length, +extern ArrayObject* +NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length, NewObjectKind newKind = GenericObject); -extern JSObject* -NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length); +extern ArrayObject* +NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length); -extern JSObject* +extern ArrayObject* NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, NewObjectKind newKind = GenericObject); -extern JSObject* +extern ArrayObject* NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto); -enum class ShouldUpdateTypes -{ - Update, - DontUpdate -}; - -extern bool -MaybeAnalyzeBeforeCreatingLargeArray(ExclusiveContext* cx, HandleObjectGroup group, - const Value* vp, size_t length); - -extern JSObject* +extern ArrayObject* NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, const Value* vp, size_t length, NewObjectKind newKind = GenericObject, ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update); -extern JSObject* +extern ArrayObject* NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length, HandleObject proto = nullptr); @@ -129,13 +117,6 @@ NewValuePair(JSContext* cx, const Value& val1, const Value& val2, MutableHandleV extern bool WouldDefinePastNonwritableLength(HandleNativeObject obj, uint32_t index); -/* - * Canonicalize |vp| to a uint32_t value potentially suitable for use as an - * array length. - */ -extern bool -CanonicalizeArrayLengthValue(JSContext* cx, HandleValue v, uint32_t* canonicalized); - extern bool GetLengthProperty(JSContext* cx, HandleObject obj, uint32_t* lengthp); @@ -166,13 +147,10 @@ extern bool array_pop(JSContext* cx, unsigned argc, js::Value* vp); extern bool -array_splice_impl(JSContext* cx, unsigned argc, js::Value* vp, bool pop); - -extern bool array_join(JSContext* cx, unsigned argc, js::Value* vp); extern void -ArrayShiftMoveElements(JSObject* obj); +ArrayShiftMoveElements(NativeObject* obj); extern bool array_shift(JSContext* cx, unsigned argc, js::Value* vp); @@ -192,6 +170,8 @@ array_reverse(JSContext* cx, unsigned argc, js::Value* vp); extern bool array_splice(JSContext* cx, unsigned argc, js::Value* vp); +extern const JSJitInfo array_splice_info; + /* * Append the given (non-hole) value to the end of an array. The array must be * a newborn array -- that is, one which has not been exposed to script for @@ -202,7 +182,7 @@ array_splice(JSContext* cx, unsigned argc, js::Value* vp); extern bool NewbornArrayPush(JSContext* cx, HandleObject obj, const Value& v); -extern JSObject* +extern ArrayObject* ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt); #ifdef DEBUG diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index 2a3c58638a..280ec3e9c0 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -54,41 +54,9 @@ FOR_EACH_COMMON_PROPERTYNAME(CONST_CHAR_STR) #undef CONST_CHAR_STR /* Constant strings that are not atomized. */ -const char js_break_str[] = "break"; -const char js_case_str[] = "case"; -const char js_catch_str[] = "catch"; -const char js_class_str[] = "class"; -const char js_const_str[] = "const"; -const char js_continue_str[] = "continue"; -const char js_debugger_str[] = "debugger"; -const char js_default_str[] = "default"; -const char js_do_str[] = "do"; -const char js_else_str[] = "else"; -const char js_enum_str[] = "enum"; -const char js_export_str[] = "export"; -const char js_extends_str[] = "extends"; -const char js_finally_str[] = "finally"; -const char js_for_str[] = "for"; const char js_getter_str[] = "getter"; -const char js_if_str[] = "if"; -const char js_implements_str[] = "implements"; -const char js_import_str[] = "import"; -const char js_in_str[] = "in"; -const char js_instanceof_str[] = "instanceof"; -const char js_interface_str[] = "interface"; -const char js_package_str[] = "package"; -const char js_private_str[] = "private"; -const char js_protected_str[] = "protected"; -const char js_public_str[] = "public"; const char js_send_str[] = "send"; const char js_setter_str[] = "setter"; -const char js_switch_str[] = "switch"; -const char js_this_str[] = "this"; -const char js_try_str[] = "try"; -const char js_typeof_str[] = "typeof"; -const char js_void_str[] = "void"; -const char js_while_str[] = "while"; -const char js_with_str[] = "with"; // Use a low initial capacity for atom hash tables to avoid penalizing runtimes // which create a small number of atoms. diff --git a/js/src/jsatom.h b/js/src/jsatom.h index 496dcbb4c2..0a5fd3c148 100644 --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -142,44 +142,9 @@ FOR_EACH_COMMON_PROPERTYNAME(DECLARE_CONST_CHAR_STR) #undef DECLARE_CONST_CHAR_STR /* Constant strings that are not atomized. */ -extern const char js_break_str[]; -extern const char js_case_str[]; -extern const char js_catch_str[]; -extern const char js_class_str[]; -extern const char js_close_str[]; -extern const char js_const_str[]; -extern const char js_continue_str[]; -extern const char js_debugger_str[]; -extern const char js_default_str[]; -extern const char js_do_str[]; -extern const char js_else_str[]; -extern const char js_enum_str[]; -extern const char js_export_str[]; -extern const char js_extends_str[]; -extern const char js_finally_str[]; -extern const char js_for_str[]; extern const char js_getter_str[]; -extern const char js_if_str[]; -extern const char js_implements_str[]; -extern const char js_import_str[]; -extern const char js_in_str[]; -extern const char js_instanceof_str[]; -extern const char js_interface_str[]; -extern const char js_package_str[]; -extern const char js_private_str[]; -extern const char js_protected_str[]; -extern const char js_public_str[]; extern const char js_send_str[]; extern const char js_setter_str[]; -extern const char js_static_str[]; -extern const char js_super_str[]; -extern const char js_switch_str[]; -extern const char js_this_str[]; -extern const char js_try_str[]; -extern const char js_typeof_str[]; -extern const char js_void_str[]; -extern const char js_while_str[]; -extern const char js_with_str[]; namespace js { diff --git a/js/src/jsbool.cpp b/js/src/jsbool.cpp index c8109b02c2..b2d07c6283 100644 --- a/js/src/jsbool.cpp +++ b/js/src/jsbool.cpp @@ -137,14 +137,14 @@ js::InitBooleanClass(JSContext* cx, HandleObject obj) { MOZ_ASSERT(obj->isNative()); - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); - Rooted<BooleanObject*> booleanProto(cx, global->createBlankPrototype<BooleanObject>(cx)); + Rooted<BooleanObject*> booleanProto(cx, GlobalObject::createBlankPrototype<BooleanObject>(cx, global)); if (!booleanProto) return nullptr; booleanProto->setFixedSlot(BooleanObject::PRIMITIVE_VALUE_SLOT, BooleanValue(false)); - RootedFunction ctor(cx, global->createConstructor(cx, Boolean, cx->names().Boolean, 1)); + RootedFunction ctor(cx, GlobalObject::createConstructor(cx, Boolean, cx->names().Boolean, 1)); if (!ctor) return nullptr; diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 31d62332d8..23b9d27ae1 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -37,7 +37,6 @@ #include "jsscript.h" #include "jsstr.h" #include "jstypes.h" -#include "jswatchpoint.h" #include "gc/Marking.h" #include "jit/Ion.h" @@ -381,49 +380,16 @@ js::ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg) } } -bool -js::PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult, - JSErrorReport* report, bool reportWarnings) -{ - MOZ_ASSERT(report); - - /* Conditionally ignore reported warnings. */ - if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings) - return false; - - char* prefix = nullptr; - if (report->filename) - prefix = JS_smprintf("%s:", report->filename); - if (report->lineno) { - char* tmp = prefix; - prefix = JS_smprintf("%s%u:%u ", tmp ? tmp : "", report->lineno, report->column); - JS_free(cx, tmp); - } - if (JSREPORT_IS_WARNING(report->flags)) { - char* tmp = prefix; - prefix = JS_smprintf("%s%swarning: ", - tmp ? tmp : "", - JSREPORT_IS_STRICT(report->flags) ? "strict " : ""); - JS_free(cx, tmp); - } - - const char* message = toStringResult ? toStringResult.c_str() : report->message().c_str(); - - /* embedded newlines -- argh! */ - const char* ctmp; - while ((ctmp = strchr(message, '\n')) != 0) { - ctmp++; - if (prefix) - fputs(prefix, file); - fwrite(message, 1, ctmp - message, file); - message = ctmp; - } - - /* If there were no filename or lineno, the prefix might be empty */ - if (prefix) - fputs(prefix, file); - fputs(message, file); +enum class PrintErrorKind { + Error, + Warning, + StrictWarning, + Note +}; +static void +PrintErrorLine(JSContext* cx, FILE* file, const char* prefix, JSErrorReport* report) +{ if (const char16_t* linebuf = report->linebuf()) { size_t n = report->linebufLength(); @@ -453,9 +419,96 @@ js::PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult, } fputc('^', file); } +} + +static void +PrintErrorLine(JSContext* cx, FILE* file, const char* prefix, JSErrorNotes::Note* note) +{ +} + +template <typename T> +static bool +PrintSingleError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult, + T* report, PrintErrorKind kind) +{ + UniquePtr<char> prefix; + if (report->filename) + prefix.reset(JS_smprintf("%s:", report->filename)); + + if (report->lineno) { + UniquePtr<char> tmp(JS_smprintf("%s%u:%u ", prefix ? prefix.get() : "", report->lineno, + report->column)); + prefix = Move(tmp); + } + + if (kind != PrintErrorKind::Error) { + const char* kindPrefix = nullptr; + switch (kind) { + case PrintErrorKind::Error: + break; + case PrintErrorKind::Warning: + kindPrefix = "warning"; + break; + case PrintErrorKind::StrictWarning: + kindPrefix = "strict warning"; + break; + case PrintErrorKind::Note: + kindPrefix = "note"; + break; + } + + UniquePtr<char> tmp(JS_smprintf("%s%s: ", prefix ? prefix.get() : "", kindPrefix)); + prefix = Move(tmp); + } + + const char* message = toStringResult ? toStringResult.c_str() : report->message().c_str(); + + /* embedded newlines -- argh! */ + const char* ctmp; + while ((ctmp = strchr(message, '\n')) != 0) { + ctmp++; + if (prefix) + fputs(prefix.get(), file); + fwrite(message, 1, ctmp - message, file); + message = ctmp; + } + + /* If there were no filename or lineno, the prefix might be empty */ + if (prefix) + fputs(prefix.get(), file); + fputs(message, file); + + PrintErrorLine(cx, file, prefix.get(), report); fputc('\n', file); + fflush(file); - JS_free(cx, prefix); + return true; +} + +bool +js::PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult, + JSErrorReport* report, bool reportWarnings) +{ + MOZ_ASSERT(report); + + /* Conditionally ignore reported warnings. */ + if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings) + return false; + + PrintErrorKind kind = PrintErrorKind::Error; + if (JSREPORT_IS_WARNING(report->flags)) { + if (JSREPORT_IS_STRICT(report->flags)) + kind = PrintErrorKind::StrictWarning; + else + kind = PrintErrorKind::Warning; + } + PrintSingleError(cx, file, toStringResult, report, kind); + + if (report->notes) { + for (auto&& note : *report->notes) + PrintSingleError(cx, file, JS::ConstUTF8CharsZ(), note.get(), PrintErrorKind::Note); + } + return true; } @@ -557,6 +610,18 @@ class MOZ_RAII AutoMessageArgs } }; +static void +SetExnType(JSErrorReport* reportp, int16_t exnType) +{ + reportp->exnType = exnType; +} + +static void +SetExnType(JSErrorNotes::Note* notep, int16_t exnType) +{ + // Do nothing for JSErrorNotes::Note. +} + /* * The arguments from ap need to be packaged up into an array and stored * into the report struct. @@ -568,12 +633,13 @@ class MOZ_RAII AutoMessageArgs * * Returns true if the expansion succeeds (can fail if out of memory). */ +template <typename T> bool -js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, +ExpandErrorArgumentsHelper(ExclusiveContext* cx, JSErrorCallback callback, void* userRef, const unsigned errorNumber, const char16_t** messageArgs, ErrorArgumentsType argumentsType, - JSErrorReport* reportp, va_list ap) + T* reportp, va_list ap) { const JSErrorFormatString* efs; @@ -586,7 +652,7 @@ js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, } if (efs) { - reportp->exnType = efs->exnType; + SetExnType(reportp, efs->exnType); MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII, JS::StringIsASCII(efs->format)); @@ -670,6 +736,28 @@ js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, } bool +js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, + void* userRef, const unsigned errorNumber, + const char16_t** messageArgs, + ErrorArgumentsType argumentsType, + JSErrorReport* reportp, va_list ap) +{ + return ExpandErrorArgumentsHelper(cx, callback, userRef, errorNumber, + messageArgs, argumentsType, reportp, ap); +} + +bool +js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, + void* userRef, const unsigned errorNumber, + const char16_t** messageArgs, + ErrorArgumentsType argumentsType, + JSErrorNotes::Note* notep, va_list ap) +{ + return ExpandErrorArgumentsHelper(cx, callback, userRef, errorNumber, + messageArgs, argumentsType, notep, ap); +} + +bool js::ReportErrorNumberVA(JSContext* cx, unsigned flags, JSErrorCallback callback, void* userRef, const unsigned errorNumber, ErrorArgumentsType argumentsType, va_list ap) @@ -832,6 +920,52 @@ js::ReportValueErrorFlags(JSContext* cx, unsigned flags, const unsigned errorNum return ok; } +JSObject* +js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report) +{ + RootedArrayObject notesArray(cx, NewDenseEmptyArray(cx)); + if (!notesArray) + return nullptr; + + if (!report->notes) + return notesArray; + + for (auto&& note : *report->notes) { + RootedPlainObject noteObj(cx, NewBuiltinClassInstance<PlainObject>(cx)); + if (!noteObj) + return nullptr; + + RootedString messageStr(cx, note->newMessageString(cx)); + if (!messageStr) + return nullptr; + RootedValue messageVal(cx, StringValue(messageStr)); + if (!DefineProperty(cx, noteObj, cx->names().message, messageVal)) + return nullptr; + + RootedValue filenameVal(cx); + if (note->filename) { + RootedString filenameStr(cx, NewStringCopyZ<CanGC>(cx, note->filename)); + if (!filenameStr) + return nullptr; + filenameVal = StringValue(filenameStr); + } + if (!DefineProperty(cx, noteObj, cx->names().fileName, filenameVal)) + return nullptr; + + RootedValue linenoVal(cx, Int32Value(note->lineno)); + if (!DefineProperty(cx, noteObj, cx->names().lineNumber, linenoVal)) + return nullptr; + RootedValue columnVal(cx, Int32Value(note->column)); + if (!DefineProperty(cx, noteObj, cx->names().columnNumber, columnVal)) + return nullptr; + + if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj))) + return nullptr; + } + + return notesArray; +} + const JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = { #define MSG_DEF(name, count, exception, format) \ { #name, format, count, exception } , diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 0a28412425..8be3376cf0 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -621,6 +621,13 @@ ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, ErrorArgumentsType argumentsType, JSErrorReport* reportp, va_list ap); +extern bool +ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, + void* userRef, const unsigned errorNumber, + const char16_t** messageArgs, + ErrorArgumentsType argumentsType, + JSErrorNotes::Note* notep, va_list ap); + /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */ extern void ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg); @@ -678,6 +685,9 @@ ReportValueErrorFlags(JSContext* cx, unsigned flags, const unsigned errorNumber, ((void)ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ spindex, v, fallback, arg1, arg2)) +JSObject* +CreateErrorNotesArray(JSContext* cx, JSErrorReport* report); + } /* namespace js */ extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 4e4ccdf2a3..8ba186b086 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -13,7 +13,6 @@ #include "jsfriendapi.h" #include "jsgc.h" #include "jsiter.h" -#include "jswatchpoint.h" #include "jswrapper.h" #include "gc/Marking.h" @@ -41,7 +40,6 @@ using namespace js::gc; using namespace js::jit; using mozilla::DebugOnly; -using mozilla::PodArrayZero; JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = JS::CompartmentOptions()) : creationOptions_(options.creationOptions()), @@ -77,7 +75,6 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = gcIncomingGrayPointers(nullptr), debugModeBits(0), randomKeyGenerator_(runtime_->forkRandomKeyGenerator()), - watchpointMap(nullptr), scriptCountsMap(nullptr), debugScriptMap(nullptr), debugEnvs(nullptr), @@ -91,7 +88,6 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = unmappedArgumentsTemplate_(nullptr), lcovOutput() { - PodArrayZero(sawDeprecatedLanguageExtension); runtime_->numCompartments++; MOZ_ASSERT_IF(creationOptions_.mergeable(), creationOptions_.invisibleToDebugger()); @@ -99,15 +95,12 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = JSCompartment::~JSCompartment() { - reportTelemetry(); - // Write the code coverage information in a file. JSRuntime* rt = runtimeFromMainThread(); if (rt->lcovOutput.isEnabled()) rt->lcovOutput.writeLCovResult(lcovOutput); js_delete(jitCompartment_); - js_delete(watchpointMap); js_delete(scriptCountsMap); js_delete(debugScriptMap); js_delete(debugEnvs); @@ -116,13 +109,6 @@ JSCompartment::~JSCompartment() js_delete(nonSyntacticLexicalEnvironments_), js_free(enumerators); -#ifdef DEBUG - // Avoid assertion destroying the unboxed layouts list if the embedding - // leaked GC things. - if (!rt->gc.shutdownCollectedEverything()) - unboxedLayouts.clear(); -#endif - runtime_->numCompartments--; } @@ -673,12 +659,6 @@ JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime t if (traceOrMark == js::gc::GCRuntime::MarkRuntime && !zone()->isCollecting()) return; - // During a GC, these are treated as weak pointers. - if (traceOrMark == js::gc::GCRuntime::TraceRuntime) { - if (watchpointMap) - watchpointMap->markAll(trc); - } - /* Mark debug scopes, if present */ if (debugEnvs) debugEnvs->mark(trc); @@ -723,9 +703,6 @@ JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime t void JSCompartment::finishRoots() { - if (watchpointMap) - watchpointMap->clear(); - if (debugEnvs) debugEnvs->finish(); @@ -1086,18 +1063,18 @@ CreateLazyScriptsForCompartment(JSContext* cx) // Create scripts for each lazy function, updating the list of functions to // process with any newly exposed inner functions in created scripts. // A function cannot be delazified until its outer script exists. + RootedFunction fun(cx); for (size_t i = 0; i < lazyFunctions.length(); i++) { - JSFunction* fun = &lazyFunctions[i]->as<JSFunction>(); + fun = &lazyFunctions[i]->as<JSFunction>(); // lazyFunctions may have been populated with multiple functions for // a lazy script. if (!fun->isInterpretedLazy()) continue; - LazyScript* lazy = fun->lazyScript(); - bool lazyScriptHadNoScript = !lazy->maybeScript(); + bool lazyScriptHadNoScript = !fun->lazyScript()->maybeScript(); - JSScript* script = fun->getOrCreateScript(cx); + JSScript* script = JSFunction::getOrCreateScript(cx, fun); if (!script) return false; if (lazyScriptHadNoScript && !AddInnerLazyFunctionsFromScript(script, lazyFunctions)) @@ -1268,39 +1245,6 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, *privateData += callback(mallocSizeOf, this); } -void -JSCompartment::reportTelemetry() -{ - // Only report telemetry for web content and add-ons, not chrome JS. - if (isSystem_) - return; - - // Hazard analysis can't tell that the telemetry callbacks don't GC. - JS::AutoSuppressGCAnalysis nogc; - - int id = creationOptions_.addonIdOrNull() - ? JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS - : JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT; - - // Call back into Firefox's Telemetry reporter. - for (size_t i = 0; i < DeprecatedLanguageExtensionCount; i++) { - if (sawDeprecatedLanguageExtension[i]) - runtime_->addTelemetry(id, i); - } -} - -void -JSCompartment::addTelemetry(const char* filename, DeprecatedLanguageExtension e) -{ - // Only report telemetry for web content and add-ons, not chrome JS. - if (isSystem_) - return; - if (!creationOptions_.addonIdOrNull() && (!filename || strncmp(filename, "http", 4) != 0)) - return; - - sawDeprecatedLanguageExtension[e] = true; -} - HashNumber JSCompartment::randomHashCode() { diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 7bfeee1f68..05ff40b439 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -282,7 +282,6 @@ class MOZ_RAII AutoSetNewObjectMetadata : private JS::CustomAutoRooter namespace js { class DebugEnvironments; class ObjectWeakMap; -class WatchpointMap; class WeakMapBase; } // namespace js @@ -344,13 +343,6 @@ struct JSCompartment isAtomsCompartment_ = true; } - // Used to approximate non-content code when reporting telemetry. - inline bool isProbablySystemOrAddonCode() const { - if (creationOptions_.addonIdOrNull()) - return true; - - return isSystem_; - } private: JSPrincipals* principals_; bool isSystem_; @@ -536,9 +528,6 @@ struct JSCompartment // table manages references from such typed objects to their buffers. js::ObjectWeakMap* lazyArrayBuffers; - // All unboxed layouts in the compartment. - mozilla::LinkedList<js::UnboxedLayout> unboxedLayouts; - // WebAssembly state for the compartment. js::wasm::Compartment wasm; @@ -821,8 +810,6 @@ struct JSCompartment void sweepBreakpoints(js::FreeOp* fop); public: - js::WatchpointMap* watchpointMap; - js::ScriptCountsMap* scriptCountsMap; js::DebugScriptMap* debugScriptMap; @@ -879,34 +866,10 @@ struct JSCompartment return jitCompartment_; } - enum DeprecatedLanguageExtension { - DeprecatedForEach = 0, // JS 1.6+ - // NO LONGER USING 1 - DeprecatedLegacyGenerator = 2, // JS 1.7+ - DeprecatedExpressionClosure = 3, // Added in JS 1.8 - // NO LONGER USING 4 - // NO LONGER USING 5 - // NO LONGER USING 6 - // NO LONGER USING 7 - // NO LONGER USING 8 - // NO LONGER USING 9 - DeprecatedBlockScopeFunRedecl = 10, - DeprecatedLanguageExtensionCount - }; - js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped); js::ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const; - private: - // Used for collecting telemetry on SpiderMonkey's deprecated language extensions. - bool sawDeprecatedLanguageExtension[DeprecatedLanguageExtensionCount]; - - void reportTelemetry(); - - public: - void addTelemetry(const char* filename, DeprecatedLanguageExtension e); - public: // Aggregated output used to collect JSScript hit counts when code coverage // is enabled. diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 52294a5df1..c6a369e2db 100755 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -3163,7 +3163,7 @@ js::DateConstructor(JSContext* cx, unsigned argc, Value* vp) static JSObject* CreateDatePrototype(JSContext* cx, JSProtoKey key) { - return cx->global()->createBlankPrototype(cx, &DateObject::protoClass_); + return GlobalObject::createBlankPrototype(cx, cx->global(), &DateObject::protoClass_); } static bool diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 9a8e364edd..3fc9200c1d 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -201,28 +201,92 @@ ErrorObject::classes[JSEXN_ERROR_LIMIT] = { IMPLEMENT_ERROR_CLASS(RuntimeError) }; -JSErrorReport* -js::CopyErrorReport(JSContext* cx, JSErrorReport* report) +size_t +ExtraMallocSize(JSErrorReport* report) +{ + if (report->linebuf()) + /* + * Mozilla bug 1352449. Count with null + * terminator and alignment. See CopyExtraData for + * the details about alignment. + */ + return (report->linebufLength() + 1) * sizeof(char16_t) + 1; + + return 0; +} + +size_t +ExtraMallocSize(JSErrorNotes::Note* note) +{ + return 0; +} + +bool +CopyExtraData(JSContext* cx, uint8_t** cursor, JSErrorReport* copy, JSErrorReport* report) +{ + if (report->linebuf()) { + /* + * Make sure cursor is properly aligned for char16_t for platforms + * which need it and it's at the end of the buffer on exit. + */ + size_t alignment_backlog = 0; + if (size_t(*cursor) % 2) + (*cursor)++; + else + alignment_backlog = 1; + + size_t linebufSize = (report->linebufLength() + 1) * sizeof(char16_t); + const char16_t* linebufCopy = (const char16_t*)(*cursor); + js_memcpy(*cursor, report->linebuf(), linebufSize); + *cursor += linebufSize + alignment_backlog; + copy->initBorrowedLinebuf(linebufCopy, report->linebufLength(), report->tokenOffset()); + } + + /* Copy non-pointer members. */ + copy->isMuted = report->isMuted; + copy->exnType = report->exnType; + + /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */ + copy->flags = report->flags; + + /* Deep copy notes. */ + if (report->notes) { + auto copiedNotes = report->notes->copy(cx); + if (!copiedNotes) + return false; + copy->notes = Move(copiedNotes); + } else { + copy->notes.reset(nullptr); + } + + return true; +} + +bool +CopyExtraData(JSContext* cx, uint8_t** cursor, JSErrorNotes::Note* copy, JSErrorNotes::Note* report) +{ + return true; +} + +template <typename T> +static T* +CopyErrorHelper(JSContext* cx, T* report) { /* - * We use a single malloc block to make a deep copy of JSErrorReport with + * We use a single malloc block to make a deep copy of JSErrorReport or + * JSErrorNotes::Note, except JSErrorNotes linked from JSErrorReport with * the following layout: - * JSErrorReport + * JSErrorReport or JSErrorNotes::Note * char array with characters for message_ - * char16_t array with characters for linebuf * char array with characters for filename + * char16_t array with characters for linebuf (only for JSErrorReport) * Such layout together with the properties enforced by the following * asserts does not need any extra alignment padding. */ - JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char*) == 0); + JS_STATIC_ASSERT(sizeof(T) % sizeof(const char*) == 0); JS_STATIC_ASSERT(sizeof(const char*) % sizeof(char16_t) == 0); -#define JS_CHARS_SIZE(chars) ((js_strlen(chars) + 1) * sizeof(char16_t)) - size_t filenameSize = report->filename ? strlen(report->filename) + 1 : 0; - size_t linebufSize = 0; - if (report->linebuf()) - linebufSize = (report->linebufLength() + 1) * sizeof(char16_t); size_t messageSize = 0; if (report->message()) messageSize = strlen(report->message().c_str()) + 1; @@ -231,13 +295,13 @@ js::CopyErrorReport(JSContext* cx, JSErrorReport* report) * The mallocSize can not overflow since it represents the sum of the * sizes of already allocated objects. */ - size_t mallocSize = sizeof(JSErrorReport) + messageSize + linebufSize + filenameSize; + size_t mallocSize = sizeof(T) + messageSize + filenameSize + ExtraMallocSize(report); uint8_t* cursor = cx->pod_calloc<uint8_t>(mallocSize); if (!cursor) return nullptr; - JSErrorReport* copy = (JSErrorReport*)cursor; - cursor += sizeof(JSErrorReport); + T* copy = new (cursor) T(); + cursor += sizeof(T); if (report->message()) { copy->initBorrowedMessage((const char*)cursor); @@ -245,33 +309,40 @@ js::CopyErrorReport(JSContext* cx, JSErrorReport* report) cursor += messageSize; } - if (report->linebuf()) { - const char16_t* linebufCopy = (const char16_t*)cursor; - js_memcpy(cursor, report->linebuf(), linebufSize); - cursor += linebufSize; - copy->initBorrowedLinebuf(linebufCopy, report->linebufLength(), report->tokenOffset()); - } - if (report->filename) { copy->filename = (const char*)cursor; js_memcpy(cursor, report->filename, filenameSize); + cursor += filenameSize; + } + + if (!CopyExtraData(cx, &cursor, copy, report)) { + /* js_delete calls destructor for T and js_free for pod_calloc. */ + js_delete(copy); + return nullptr; } - MOZ_ASSERT(cursor + filenameSize == (uint8_t*)copy + mallocSize); + + MOZ_ASSERT(cursor == (uint8_t*)copy + mallocSize); /* Copy non-pointer members. */ - copy->isMuted = report->isMuted; copy->lineno = report->lineno; copy->column = report->column; copy->errorNumber = report->errorNumber; - copy->exnType = report->exnType; - /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */ - copy->flags = report->flags; - -#undef JS_CHARS_SIZE return copy; } +JSErrorNotes::Note* +js::CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note) +{ + return CopyErrorHelper(cx, note); +} + +JSErrorReport* +js::CopyErrorReport(JSContext* cx, JSErrorReport* report) +{ + return CopyErrorHelper(cx, report); +} + struct SuppressErrorsGuard { JSContext* cx; @@ -322,7 +393,7 @@ exn_finalize(FreeOp* fop, JSObject* obj) { MOZ_ASSERT(fop->maybeOffMainThread()); if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport()) - fop->free_(report); + fop->delete_(report); } JSErrorReport* @@ -512,14 +583,17 @@ ErrorObject::createProto(JSContext* cx, JSProtoKey key) { JSExnType type = ExnTypeFromProtoKey(key); - if (type == JSEXN_ERR) - return cx->global()->createBlankPrototype(cx, &ErrorObject::protoClasses[JSEXN_ERR]); + if (type == JSEXN_ERR) { + return GlobalObject::createBlankPrototype(cx, cx->global(), + &ErrorObject::protoClasses[JSEXN_ERR]); + } RootedObject protoProto(cx, GlobalObject::getOrCreateErrorPrototype(cx, cx->global())); if (!protoProto) return nullptr; - return cx->global()->createBlankPrototypeInheriting(cx, &ErrorObject::protoClasses[type], + return GlobalObject::createBlankPrototypeInheriting(cx, cx->global(), + &ErrorObject::protoClasses[type], protoProto); } @@ -586,6 +660,7 @@ js::ErrorToException(JSContext* cx, JSErrorReport* reportp, const JSErrorFormatString* errorString = callback(userRef, errorNumber); JSExnType exnType = errorString ? static_cast<JSExnType>(errorString->exnType) : JSEXN_ERR; MOZ_ASSERT(exnType < JSEXN_LIMIT); + MOZ_ASSERT(exnType != JSEXN_NOTE); if (exnType == JSEXN_WARN) { // werror must be enabled, so we use JSEXN_ERR. @@ -669,7 +744,7 @@ ErrorReportToString(JSContext* cx, JSErrorReport* reportp) */ JSExnType type = static_cast<JSExnType>(reportp->exnType); RootedString str(cx); - if (type != JSEXN_WARN) + if (type != JSEXN_WARN && type != JSEXN_NOTE) str = ClassName(GetExceptionProtoKey(type), cx); /* @@ -707,67 +782,6 @@ ErrorReport::~ErrorReport() { } -void -ErrorReport::ReportAddonExceptionToTelementry(JSContext* cx) -{ - MOZ_ASSERT(exnObject); - RootedObject unwrapped(cx, UncheckedUnwrap(exnObject)); - MOZ_ASSERT(unwrapped, "UncheckedUnwrap failed?"); - - // There is not much we can report if the exception is not an ErrorObject, let's ignore those. - if (!unwrapped->is<ErrorObject>()) - return; - - Rooted<ErrorObject*> errObj(cx, &unwrapped->as<ErrorObject>()); - RootedObject stack(cx, errObj->stack()); - - // Let's ignore TOP level exceptions. For regular add-ons those will not be reported anyway, - // for SDK based once it should not be a valid case either. - // At this point the frame stack is unwound but the exception object stored the stack so let's - // use that for getting the function name. - if (!stack) - return; - - JSCompartment* comp = stack->compartment(); - JSAddonId* addonId = comp->creationOptions().addonIdOrNull(); - - // We only want to send the report if the scope that just have thrown belongs to an add-on. - // Let's check the compartment of the youngest function on the stack, to determine that. - if (!addonId) - return; - - RootedString funnameString(cx); - JS::SavedFrameResult result = GetSavedFrameFunctionDisplayName(cx, stack, &funnameString); - // AccessDenied should never be the case here for add-ons but let's not risk it. - JSAutoByteString bytes; - const char* funname = nullptr; - bool denied = result == JS::SavedFrameResult::AccessDenied; - funname = denied ? "unknown" - : funnameString ? AtomToPrintableString(cx, - &funnameString->asAtom(), - &bytes) - : "anonymous"; - - UniqueChars addonIdChars(JS_EncodeString(cx, addonId)); - - const char* filename = nullptr; - if (reportp && reportp->filename) { - filename = strrchr(reportp->filename, '/'); - if (filename) - filename++; - } - if (!filename) { - filename = "FILE_NOT_FOUND"; - } - char histogramKey[64]; - SprintfLiteral(histogramKey, "%s %s %s %u", - addonIdChars.get(), - funname, - filename, - (reportp ? reportp->lineno : 0) ); - cx->runtime()->addTelemetry(JS_TELEMETRY_ADDON_EXCEPTIONS, 1, histogramKey); -} - bool ErrorReport::init(JSContext* cx, HandleValue exn, SniffingBehavior sniffingBehavior) @@ -786,10 +800,6 @@ ErrorReport::init(JSContext* cx, HandleValue exn, JSMSG_ERR_DURING_THROW); return false; } - - // Let's see if the exception is from add-on code, if so, it should be reported - // to telementry. - ReportAddonExceptionToTelementry(cx); } diff --git a/js/src/jsexn.h b/js/src/jsexn.h index ae6335209b..00120d89c8 100644 --- a/js/src/jsexn.h +++ b/js/src/jsexn.h @@ -18,6 +18,9 @@ namespace js { class ErrorObject; +JSErrorNotes::Note* +CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note); + JSErrorReport* CopyErrorReport(JSContext* cx, JSErrorReport* report); @@ -67,7 +70,8 @@ static_assert(JSEXN_ERR == 0 && JSProto_Error + JSEXN_WASMCOMPILEERROR == JSProto_CompileError && JSProto_Error + JSEXN_WASMRUNTIMEERROR == JSProto_RuntimeError && JSEXN_WASMRUNTIMEERROR + 1 == JSEXN_WARN && - JSEXN_WARN + 1 == JSEXN_LIMIT, + JSEXN_WARN + 1 == JSEXN_NOTE && + JSEXN_NOTE + 1 == JSEXN_LIMIT, "GetExceptionProtoKey and ExnTypeFromProtoKey require that " "each corresponding JSExnType and JSProtoKey value be separated " "by the same constant value"); diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 595a214102..bdb3c0a4da 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -15,7 +15,6 @@ #include "jsgc.h" #include "jsobj.h" #include "jsprf.h" -#include "jswatchpoint.h" #include "jsweakmap.h" #include "jswrapper.h" @@ -113,7 +112,7 @@ JS_SplicePrototype(JSContext* cx, HandleObject obj, HandleObject proto) } Rooted<TaggedProto> tagged(cx, TaggedProto(proto)); - return obj->splicePrototype(cx, obj->getClass(), tagged); + return JSObject::splicePrototype(cx, obj, obj->getClass(), tagged); } JS_FRIEND_API(JSObject*) @@ -269,9 +268,9 @@ js::GetBuiltinClass(JSContext* cx, HandleObject obj, ESClass* cls) if (MOZ_UNLIKELY(obj->is<ProxyObject>())) return Proxy::getBuiltinClass(cx, obj, cls); - if (obj->is<PlainObject>() || obj->is<UnboxedPlainObject>()) + if (obj->is<PlainObject>()) *cls = ESClass::Object; - else if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) + else if (obj->is<ArrayObject>()) *cls = ESClass::Array; else if (obj->is<NumberObject>()) *cls = ESClass::Number; @@ -543,11 +542,6 @@ js::SetPreserveWrapperCallback(JSContext* cx, PreserveWrapperCallback callback) cx->preserveWrapperCallback = callback; } -/* - * The below code is for temporary telemetry use. It can be removed when - * sufficient data has been harvested. - */ - namespace js { // Defined in vm/GlobalObject.cpp. extern size_t sSetProtoCalled; @@ -584,7 +578,6 @@ void js::TraceWeakMaps(WeakMapTracer* trc) { WeakMapBase::traceAllMappings(trc); - WatchpointMap::traceAll(trc); } extern JS_FRIEND_API(bool) @@ -643,12 +636,6 @@ js::StringToLinearStringSlow(JSContext* cx, JSString* str) return str->ensureLinear(cx); } -JS_FRIEND_API(void) -JS_SetAccumulateTelemetryCallback(JSContext* cx, JSAccumulateTelemetryDataCallback callback) -{ - cx->setTelemetryCallback(cx, callback); -} - JS_FRIEND_API(JSObject*) JS_CloneObject(JSContext* cx, HandleObject obj, HandleObject protoArg) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 7220855491..4912154565 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -105,46 +105,6 @@ JS_TraceShapeCycleCollectorChildren(JS::CallbackTracer* trc, JS::GCCellPtr shape extern JS_FRIEND_API(void) JS_TraceObjectGroupCycleCollectorChildren(JS::CallbackTracer* trc, JS::GCCellPtr group); -enum { - JS_TELEMETRY_GC_REASON, - JS_TELEMETRY_GC_IS_ZONE_GC, - JS_TELEMETRY_GC_MS, - JS_TELEMETRY_GC_BUDGET_MS, - JS_TELEMETRY_GC_ANIMATION_MS, - JS_TELEMETRY_GC_MAX_PAUSE_MS, - JS_TELEMETRY_GC_MARK_MS, - JS_TELEMETRY_GC_SWEEP_MS, - JS_TELEMETRY_GC_COMPACT_MS, - JS_TELEMETRY_GC_MARK_ROOTS_MS, - JS_TELEMETRY_GC_MARK_GRAY_MS, - JS_TELEMETRY_GC_SLICE_MS, - JS_TELEMETRY_GC_SLOW_PHASE, - JS_TELEMETRY_GC_MMU_50, - JS_TELEMETRY_GC_RESET, - JS_TELEMETRY_GC_RESET_REASON, - JS_TELEMETRY_GC_INCREMENTAL_DISABLED, - JS_TELEMETRY_GC_NON_INCREMENTAL, - JS_TELEMETRY_GC_NON_INCREMENTAL_REASON, - JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, - JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, - JS_TELEMETRY_GC_MINOR_REASON, - JS_TELEMETRY_GC_MINOR_REASON_LONG, - JS_TELEMETRY_GC_MINOR_US, - JS_TELEMETRY_GC_NURSERY_BYTES, - JS_TELEMETRY_GC_PRETENURE_COUNT, - JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, - JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS, - JS_TELEMETRY_ADDON_EXCEPTIONS, - JS_TELEMETRY_AOT_USAGE, - JS_TELEMETRY_END -}; - -typedef void -(*JSAccumulateTelemetryDataCallback)(int id, uint32_t sample, const char* key); - -extern JS_FRIEND_API(void) -JS_SetAccumulateTelemetryCallback(JSContext* cx, JSAccumulateTelemetryDataCallback callback); - extern JS_FRIEND_API(bool) JS_GetIsSecureContext(JSCompartment* compartment); @@ -1456,9 +1416,6 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(ErrorReport) bool populateUncaughtExceptionReportUTF8(JSContext* cx, ...); bool populateUncaughtExceptionReportUTF8VA(JSContext* cx, va_list ap); - // Reports exceptions from add-on scopes to telementry. - void ReportAddonExceptionToTelementry(JSContext* cx); - // We may have a provided JSErrorReport, so need a way to represent that. JSErrorReport* reportp; @@ -2153,30 +2110,6 @@ JS_FRIEND_API(void*) JS_GetDataViewData(JSObject* obj, const JS::AutoCheckCannotGC&); namespace js { - -/** - * Add a watchpoint -- in the Object.prototype.watch sense -- to |obj| for the - * property |id|, using the callable object |callable| as the function to be - * called for notifications. - * - * This is an internal function exposed -- temporarily -- only so that DOM - * proxies can be watchable. Don't use it! We'll soon kill off the - * Object.prototype.{,un}watch functions, at which point this will go too. - */ -extern JS_FRIEND_API(bool) -WatchGuts(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable); - -/** - * Remove a watchpoint -- in the Object.prototype.watch sense -- from |obj| for - * the property |id|. - * - * This is an internal function exposed -- temporarily -- only so that DOM - * proxies can be watchable. Don't use it! We'll soon kill off the - * Object.prototype.{,un}watch functions, at which point this will go too. - */ -extern JS_FRIEND_API(bool) -UnwatchGuts(JSContext* cx, JS::HandleObject obj, JS::HandleId id); - namespace jit { enum class InlinableNative : uint16_t; @@ -2297,6 +2230,7 @@ struct JSJitInfo { Method, StaticMethod, InlinableNative, + IgnoresReturnValueNative, // Must be last OpTypeCount }; @@ -2388,8 +2322,13 @@ struct JSJitInfo { JSJitMethodOp method; /** A DOM static method, used for Promise wrappers */ JSNative staticMethod; + JSNative ignoresReturnValueMethod; }; + static unsigned offsetOfIgnoresReturnValueNative() { + return offsetof(JSJitInfo, ignoresReturnValueMethod); + } + union { uint16_t protoID; js::jit::InlinableNative inlinableNative; diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index bcb0da80b2..9edf238efd 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -129,6 +129,11 @@ IsFunctionInStrictMode(JSFunction* fun) return IsAsmJSStrictModeModuleOrFunction(fun); } +static bool +IsNewerTypeFunction(JSFunction* fun) { + return fun->isArrow() || fun->isGenerator() || fun->isAsync() || fun->isMethod(); +} + // Beware: this function can be invoked on *any* function! That includes // natives, strict mode functions, bound functions, arrow functions, // self-hosted functions and constructors, asm.js functions, functions with @@ -142,7 +147,9 @@ ArgumentsRestrictions(JSContext* cx, HandleFunction fun) // a strict mode function, or a bound function. // TODO (bug 1057208): ensure semantics are correct for all possible // pairings of callee/caller. - if (fun->isBuiltin() || IsFunctionInStrictMode(fun) || fun->isBoundFunction()) { + if (fun->isBuiltin() || IsFunctionInStrictMode(fun) || + fun->isBoundFunction() || IsNewerTypeFunction(fun)) + { ThrowTypeErrorBehavior(cx); return false; } @@ -229,7 +236,9 @@ CallerRestrictions(JSContext* cx, HandleFunction fun) // a strict mode function, or a bound function. // TODO (bug 1057208): ensure semantics are correct for all possible // pairings of callee/caller. - if (fun->isBuiltin() || IsFunctionInStrictMode(fun) || fun->isBoundFunction()) { + if (fun->isBuiltin() || IsFunctionInStrictMode(fun) || + fun->isBoundFunction() || IsNewerTypeFunction(fun)) + { ThrowTypeErrorBehavior(cx); return false; } @@ -275,6 +284,8 @@ CallerGetterImpl(JSContext* cx, const CallArgs& args) } RootedObject caller(cx, iter.callee(cx)); + if (caller->is<JSFunction>() && caller->as<JSFunction>().isAsync()) + caller = GetWrappedAsyncFunction(&caller->as<JSFunction>()); if (!cx->compartment()->wrap(cx, &caller)) return false; @@ -288,7 +299,15 @@ CallerGetterImpl(JSContext* cx, const CallArgs& args) return true; } + if (JS_IsDeadWrapper(callerObj)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_DEAD_OBJECT); + return false; + } + JSFunction* callerFun = &callerObj->as<JSFunction>(); + if (IsWrappedAsyncFunction(callerFun)) + callerFun = GetUnwrappedAsyncFunction(callerFun); MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?"); if (callerFun->strict()) { @@ -314,54 +333,14 @@ CallerSetterImpl(JSContext* cx, const CallArgs& args) { MOZ_ASSERT(IsFunction(args.thisv())); - // Beware! This function can be invoked on *any* function! It can't - // assume it'll never be invoked on natives, strict mode functions, bound - // functions, or anything else that ordinarily has immutable .caller - // defined with [[ThrowTypeError]]. - RootedFunction fun(cx, &args.thisv().toObject().as<JSFunction>()); - if (!CallerRestrictions(cx, fun)) - return false; - - // Return |undefined| unless an error must be thrown. - args.rval().setUndefined(); - - // We can almost just return |undefined| here -- but if the caller function - // was strict mode code, we still have to throw a TypeError. This requires - // computing the caller, checking that no security boundaries are crossed, - // and throwing a TypeError if the resulting caller is strict. - - NonBuiltinScriptFrameIter iter(cx); - if (!AdvanceToActiveCallLinear(cx, iter, fun)) - return true; - - ++iter; - while (!iter.done() && iter.isEvalFrame()) - ++iter; - - if (iter.done() || !iter.isFunctionFrame()) - return true; - - RootedObject caller(cx, iter.callee(cx)); - if (!cx->compartment()->wrap(cx, &caller)) { - cx->clearPendingException(); - return true; - } - - // If we don't have full access to the caller, or the caller is not strict, - // return undefined. Otherwise throw a TypeError. - JSObject* callerObj = CheckedUnwrap(caller); - if (!callerObj) - return true; - - JSFunction* callerFun = &callerObj->as<JSFunction>(); - MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?"); - - if (callerFun->strict()) { - JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_ERROR, GetErrorMessage, nullptr, - JSMSG_CALLER_IS_STRICT); - return false; + // We just have to return |undefined|, but first we call CallerGetterImpl
+ // because we need the same strict-mode and security checks.
+
+ if (!CallerGetterImpl(cx, args)) { + return false;
} + args.rval().setUndefined(); return true; } @@ -400,7 +379,7 @@ ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId if (isStarGenerator) objProto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global); else - objProto = fun->global().getOrCreateObjectPrototype(cx); + objProto = GlobalObject::getOrCreateObjectPrototype(cx, global); if (!objProto) return false; @@ -497,7 +476,7 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) if (fun->hasResolvedLength()) return true; - if (!fun->getUnresolvedLength(cx, &v)) + if (!JSFunction::getUnresolvedLength(cx, fun, &v)) return false; } else { if (fun->hasResolvedName()) @@ -690,7 +669,7 @@ js::fun_symbolHasInstance(JSContext* cx, unsigned argc, Value* vp) } /* - * ES6 (4-25-16) 7.3.19 OrdinaryHasInstance + * ES6 7.3.19 OrdinaryHasInstance */ bool JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool* bp) @@ -707,7 +686,7 @@ JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool* if (obj->is<JSFunction>() && obj->isBoundFunction()) { /* Steps 2a-b. */ obj = obj->as<JSFunction>().getBoundFunctionTarget(); - return InstanceOfOperator(cx, obj, v, bp); + return InstanceofOperator(cx, obj, v, bp); } /* Step 3. */ @@ -716,12 +695,12 @@ JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool* return true; } - /* Step 4. */ + /* Step 4-5. */ RootedValue pval(cx); if (!GetProperty(cx, obj, obj, cx->names().prototype, &pval)) return false; - /* Step 5. */ + /* Step 6. */ if (pval.isPrimitive()) { /* * Throw a runtime error if instanceof is called on a function that @@ -732,7 +711,7 @@ JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool* return false; } - /* Step 6. */ + /* Step 7. */ RootedObject pobj(cx, &pval.toObject()); bool isDelegate; if (!IsDelegate(cx, pobj, v, &isDelegate)) @@ -815,8 +794,10 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key) RootedFunction functionProto(cx, &functionProto_->as<JSFunction>()); - const char* rawSource = "() {\n}"; + const char* rawSource = "function () {\n}"; size_t sourceLen = strlen(rawSource); + size_t begin = 9; + MOZ_ASSERT(rawSource[begin] == '('); mozilla::UniquePtr<char16_t[], JS::FreePolicy> source(InflateString(cx, rawSource, &sourceLen)); if (!source) return nullptr; @@ -838,13 +819,15 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key) RootedScript script(cx, JSScript::Create(cx, options, sourceObject, + begin, + ss->length(), 0, ss->length())); if (!script || !JSScript::initFunctionPrototype(cx, script, functionProto)) return nullptr; functionProto->initScript(script); - ObjectGroup* protoGroup = functionProto->getGroup(cx); + ObjectGroup* protoGroup = JSObject::getGroup(cx, functionProto); if (!protoGroup) return nullptr; @@ -879,6 +862,28 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key) if (!throwTypeError || !PreventExtensions(cx, throwTypeError)) return nullptr; + // The "length" property of %ThrowTypeError% is non-configurable, adjust + // the default property attributes accordingly. + Rooted<PropertyDescriptor> nonConfigurableDesc(cx); + nonConfigurableDesc.setAttributes(JSPROP_PERMANENT | JSPROP_IGNORE_READONLY | + JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_VALUE); + + RootedId lengthId(cx, NameToId(cx->names().length)); + ObjectOpResult lengthResult; + if (!NativeDefineProperty(cx, throwTypeError, lengthId, nonConfigurableDesc, lengthResult)) + return nullptr; + MOZ_ASSERT(lengthResult); + + // Non-standard: Also change "name" to non-configurable. ECMAScript defines + // %ThrowTypeError% as an anonymous function, i.e. it shouldn't actually + // get an own "name" property. To be consistent with other built-in, + // anonymous functions, we don't delete %ThrowTypeError%'s "name" property. + RootedId nameId(cx, NameToId(cx->names().name)); + ObjectOpResult nameResult; + if (!NativeDefineProperty(cx, throwTypeError, nameId, nonConfigurableDesc, nameResult)) + return nullptr; + MOZ_ASSERT(nameResult); + self->setThrowTypeError(throwTypeError); return functionProto; @@ -917,80 +922,10 @@ const Class JSFunction::class_ = { const Class* const js::FunctionClassPtr = &JSFunction::class_; -/* Find the body of a function (not including braces). */ -bool -js::FindBody(JSContext* cx, HandleFunction fun, HandleLinearString src, size_t* bodyStart, - size_t* bodyEnd) -{ - // We don't need principals, since those are only used for error reporting. - CompileOptions options(cx); - options.setFileAndLine("internal-findBody", 0); - - // For asm.js/wasm modules, there's no script. - if (fun->hasScript()) - options.setVersion(fun->nonLazyScript()->getVersion()); - - AutoKeepAtoms keepAtoms(cx->perThreadData); - - AutoStableStringChars stableChars(cx); - if (!stableChars.initTwoByte(cx, src)) - return false; - - const mozilla::Range<const char16_t> srcChars = stableChars.twoByteRange(); - TokenStream ts(cx, options, srcChars.begin().get(), srcChars.length(), nullptr); - int nest = 0; - bool onward = true; - // Skip arguments list. - do { - TokenKind tt; - if (!ts.getToken(&tt)) - return false; - switch (tt) { - case TOK_NAME: - case TOK_YIELD: - if (nest == 0) - onward = false; - break; - case TOK_LP: - nest++; - break; - case TOK_RP: - if (--nest == 0) - onward = false; - break; - default: - break; - } - } while (onward); - TokenKind tt; - if (!ts.getToken(&tt)) - return false; - if (tt == TOK_ARROW) { - if (!ts.getToken(&tt)) - return false; - } - bool braced = tt == TOK_LC; - MOZ_ASSERT_IF(fun->isExprBody(), !braced); - *bodyStart = ts.currentToken().pos.begin; - if (braced) - *bodyStart += 1; - mozilla::RangedPtr<const char16_t> end = srcChars.end(); - if (end[-1] == '}') { - end--; - } else { - MOZ_ASSERT(!braced); - for (; unicode::IsSpaceOrBOM2(end[-1]); end--) - ; - } - *bodyEnd = end - srcChars.begin(); - MOZ_ASSERT(*bodyStart <= *bodyEnd); - return true; -} - JSString* js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint) { - if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx)) + if (fun->isInterpretedLazy() && !JSFunction::getOrCreateScript(cx, fun)) return nullptr; if (IsAsmJSModule(fun)) @@ -1019,80 +954,97 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint) } } - if (fun->isAsync()) { - if (!out.append("async ")) - return nullptr; - } + bool funIsNonArrowLambda = fun->isLambda() && !fun->isArrow(); - bool funIsMethodOrNonArrowLambda = (fun->isLambda() && !fun->isArrow()) || fun->isMethod() || - fun->isGetter() || fun->isSetter(); - bool haveSource = fun->isInterpreted() && !fun->isSelfHostedBuiltin(); + // Default class constructors are self-hosted, but have their source + // objects overridden to refer to the span of the class statement or + // expression. Non-default class constructors are never self-hosted. So, + // all class constructors always have source. + bool haveSource = fun->isInterpreted() && (fun->isClassConstructor() || + !fun->isSelfHostedBuiltin()); - // If we're not in pretty mode, put parentheses around lambda functions and methods. - if (haveSource && !prettyPrint && funIsMethodOrNonArrowLambda) { + // If we're not in pretty mode, put parentheses around lambda functions + // so that eval returns lambda, not function statement. + if (haveSource && !prettyPrint && funIsNonArrowLambda) { if (!out.append("(")) return nullptr; } - if (!fun->isArrow()) { - bool ok; - if (fun->isStarGenerator() && !fun->isAsync()) - ok = out.append("function* "); - else - ok = out.append("function "); - if (!ok) - return nullptr; - } - if (fun->explicitName()) { - if (!out.append(fun->explicitName())) - return nullptr; - } if (haveSource && !script->scriptSource()->hasSourceData() && !JSScript::loadSource(cx, script->scriptSource(), &haveSource)) { return nullptr; } + + auto AppendPrelude = [&out, &fun]() { + if (fun->isAsync()) { + if (!out.append("async ")) + return false; + } + + if (!fun->isArrow()) { + if (!out.append("function")) + return false; + + if (fun->isStarGenerator()) { + if (!out.append('*')) + return false; + } + } + + if (fun->explicitName()) { + if (!out.append(' ')) + return false; + if (!out.append(fun->explicitName())) + return false; + } + return true; + }; + if (haveSource) { - Rooted<JSFlatString*> src(cx, script->sourceData(cx)); + Rooted<JSFlatString*> src(cx, JSScript::sourceDataForToString(cx, script)); if (!src) return nullptr; if (!out.append(src)) return nullptr; - if (!prettyPrint && funIsMethodOrNonArrowLambda) { + if (!prettyPrint && funIsNonArrowLambda) { if (!out.append(")")) return nullptr; } - } else if (fun->isInterpreted() && !fun->isSelfHostedBuiltin()) { - if (!out.append("() {\n ") || + } else if (fun->isInterpreted() && + (!fun->isSelfHostedBuiltin() || + fun->infallibleIsDefaultClassConstructor(cx))) + { + // Default class constructors should always haveSource except; + // + // 1. Source has been discarded for the whole compartment. + // + // 2. The source is marked as "lazy", i.e., retrieved on demand, and + // the embedding has not provided a hook to retrieve sources. + MOZ_ASSERT_IF(fun->infallibleIsDefaultClassConstructor(cx), + !cx->runtime()->sourceHook || + !script->scriptSource()->sourceRetrievable() || + fun->compartment()->behaviors().discardSource()); + if (!AppendPrelude() || + !out.append("() {\n ") || !out.append("[sourceless code]") || !out.append("\n}")) { return nullptr; } } else { - MOZ_ASSERT(!fun->isExprBody()); - bool derived = fun->infallibleIsDefaultClassConstructor(cx); - if (derived && fun->isDerivedClassConstructor()) { - if (!out.append("(...args) {\n ") || - !out.append("super(...args);\n}")) - { - return nullptr; - } - } else { - if (!out.append("() {\n ")) - return nullptr; + if (!AppendPrelude() || + !out.append("() {\n ")) + return nullptr; - if (!derived) { - if (!out.append("[native code]")) - return nullptr; - } + if (!out.append("[native code]")) + return nullptr; - if (!out.append("\n}")) - return nullptr; - } + if (!out.append("\n}")) + return nullptr; } return out.finishString(); } @@ -1321,34 +1273,33 @@ JSFunction::isDerivedClassConstructor() return derived; } -bool -JSFunction::getLength(JSContext* cx, uint16_t* length) +/* static */ bool +JSFunction::getLength(JSContext* cx, HandleFunction fun, uint16_t* length) { - JS::RootedFunction self(cx, this); - MOZ_ASSERT(!self->isBoundFunction()); - if (self->isInterpretedLazy() && !self->getOrCreateScript(cx)) + MOZ_ASSERT(!fun->isBoundFunction()); + if (fun->isInterpretedLazy() && !getOrCreateScript(cx, fun)) return false; - *length = self->isNative() ? self->nargs() : self->nonLazyScript()->funLength(); + *length = fun->isNative() ? fun->nargs() : fun->nonLazyScript()->funLength(); return true; } -bool -JSFunction::getUnresolvedLength(JSContext* cx, MutableHandleValue v) +/* static */ bool +JSFunction::getUnresolvedLength(JSContext* cx, HandleFunction fun, MutableHandleValue v) { - MOZ_ASSERT(!IsInternalFunctionObject(*this)); - MOZ_ASSERT(!hasResolvedLength()); + MOZ_ASSERT(!IsInternalFunctionObject(*fun)); + MOZ_ASSERT(!fun->hasResolvedLength()); // Bound functions' length can have values up to MAX_SAFE_INTEGER, so // they're handled differently from other functions. - if (isBoundFunction()) { - MOZ_ASSERT(getExtendedSlot(BOUND_FUN_LENGTH_SLOT).isNumber()); - v.set(getExtendedSlot(BOUND_FUN_LENGTH_SLOT)); + if (fun->isBoundFunction()) { + MOZ_ASSERT(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT).isNumber()); + v.set(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT)); return true; } uint16_t length; - if (!getLength(cx, &length)) + if (!JSFunction::getLength(cx, fun, &length)) return false; v.setInt32(length); @@ -1405,13 +1356,11 @@ GetBoundFunctionArguments(const JSFunction* boundFun) } const js::Value& -JSFunction::getBoundFunctionArgument(JSContext* cx, unsigned which) const +JSFunction::getBoundFunctionArgument(unsigned which) const { MOZ_ASSERT(which < getBoundFunctionArgumentCount()); - RootedArrayObject boundArgs(cx, GetBoundFunctionArguments(this)); - RootedValue res(cx); - return boundArgs->getDenseElement(which); + return GetBoundFunctionArguments(this)->getDenseElement(which); } size_t @@ -1448,7 +1397,7 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti } if (fun != lazy->functionNonDelazifying()) { - if (!lazy->functionDelazifying(cx)) + if (!LazyScript::functionDelazifying(cx, lazy)) return false; script = lazy->functionNonDelazifying()->nonLazyScript(); if (!script) @@ -1669,7 +1618,18 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator StringBuffer sb(cx); - if (!sb.append('(')) + if (isAsync) { + if (!sb.append("async ")) + return false; + } + if (!sb.append("function")) + return false; + if (isStarGenerator && !isAsync) { + if (!sb.append('*')) + return false; + } + + if (!sb.append(" anonymous(")) return false; if (args.length() > 1) { @@ -1690,12 +1650,15 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator if (i < args.length() - 2) { // Step 9.d.iii. - if (!sb.append(", ")) + if (!sb.append(",")) return false; } } } + if (!sb.append('\n')) + return false; + // Remember the position of ")". Maybe<uint32_t> parameterListEnd = Some(uint32_t(sb.length())); MOZ_ASSERT(FunctionConstructorMedialSigils[0] == ')'); diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 7da831aa2b..234169507d 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -58,8 +58,6 @@ class JSFunction : public js::NativeObject CONSTRUCTOR = 0x0002, /* function that can be called as a constructor */ EXTENDED = 0x0004, /* structure is FunctionExtended */ 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 name was guessed for it anyway */ LAMBDA = 0x0040, /* function comes from a FunctionExpression, ArrowFunction, or @@ -102,7 +100,7 @@ class JSFunction : public js::NativeObject INTERPRETED_GENERATOR = INTERPRETED, NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME, - STABLE_ACROSS_CLONES = CONSTRUCTOR | EXPR_BODY | HAS_GUESSED_ATOM | LAMBDA | + STABLE_ACROSS_CLONES = CONSTRUCTOR | HAS_GUESSED_ATOM | LAMBDA | SELF_HOSTED | HAS_COMPILE_TIME_NAME | FUNCTION_KIND_MASK }; @@ -187,7 +185,6 @@ class JSFunction : public js::NativeObject bool isAsmJSNative() const { return kind() == AsmJS; } /* Possible attributes of an interpreted function: */ - bool isExprBody() const { return flags() & EXPR_BODY; } bool hasCompileTimeName() const { return flags() & HAS_COMPILE_TIME_NAME; } bool hasGuessedAtom() const { return flags() & HAS_GUESSED_ATOM; } bool isLambda() const { return flags() & LAMBDA; } @@ -290,11 +287,6 @@ class JSFunction : public js::NativeObject flags_ |= SELF_HOSTED; } - // Can be called multiple times by the parser. - void setIsExprBody() { - flags_ |= EXPR_BODY; - } - void setArrow() { setKind(Arrow); } @@ -314,7 +306,8 @@ class JSFunction : public js::NativeObject nonLazyScript()->setAsyncKind(asyncKind); } - bool getUnresolvedLength(JSContext* cx, js::MutableHandleValue v); + static bool getUnresolvedLength(JSContext* cx, js::HandleFunction fun, + js::MutableHandleValue v); JSAtom* getUnresolvedName(JSContext* cx); @@ -419,16 +412,15 @@ class JSFunction : public js::NativeObject // // - For functions known to have a JSScript, nonLazyScript() will get it. - JSScript* getOrCreateScript(JSContext* cx) { - MOZ_ASSERT(isInterpreted()); + static JSScript* getOrCreateScript(JSContext* cx, js::HandleFunction fun) { + MOZ_ASSERT(fun->isInterpreted()); MOZ_ASSERT(cx); - if (isInterpretedLazy()) { - JS::RootedFunction self(cx, this); - if (!createScriptForLazilyInterpretedFunction(cx, self)) + if (fun->isInterpretedLazy()) { + if (!createScriptForLazilyInterpretedFunction(cx, fun)) return nullptr; - return self->nonLazyScript(); + return fun->nonLazyScript(); } - return nonLazyScript(); + return fun->nonLazyScript(); } JSScript* existingScriptNonDelazifying() const { @@ -460,6 +452,19 @@ class JSFunction : public js::NativeObject return nonLazyScript(); } + // If this is a scripted function, returns its canonical function (the + // original function allocated by the frontend). Note that lazy self-hosted + // builtins don't have a lazy script so in that case we also return nullptr. + JSFunction* maybeCanonicalFunction() const { + if (hasScript()) { + return nonLazyScript()->functionNonDelazifying(); + } + if (isInterpretedLazy() && !isSelfHostedBuiltin()) { + return lazyScript()->functionNonDelazifying(); + } + return nullptr; + } + // The state of a JSFunction whose script errored out during bytecode // compilation. Such JSFunctions are only reachable via GC iteration and // not from script. @@ -473,7 +478,7 @@ class JSFunction : public js::NativeObject return u.i.s.script_; } - bool getLength(JSContext* cx, uint16_t* length); + static bool getLength(JSContext* cx, js::HandleFunction fun, uint16_t* length); js::LazyScript* lazyScript() const { MOZ_ASSERT(isInterpretedLazy() && u.i.s.lazy_); @@ -580,13 +585,17 @@ class JSFunction : public js::NativeObject return offsetof(JSFunction, u.nativeOrScript); } + static unsigned offsetOfJitInfo() { + return offsetof(JSFunction, u.n.jitinfo); + } + inline void trace(JSTracer* trc); /* Bound function accessors. */ JSObject* getBoundFunctionTarget() const; const js::Value& getBoundFunctionThis() const; - const js::Value& getBoundFunctionArgument(JSContext* cx, unsigned which) const; + const js::Value& getBoundFunctionArgument(unsigned which) const; size_t getBoundFunctionArgumentCount() const; private: @@ -789,10 +798,6 @@ CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent, gc::AllocKind kind = gc::AllocKind::FUNCTION, HandleObject proto = nullptr); -extern bool -FindBody(JSContext* cx, HandleFunction fun, HandleLinearString src, size_t* bodyStart, - size_t* bodyEnd); - } // namespace js inline js::FunctionExtended* diff --git a/js/src/jsfuninlines.h b/js/src/jsfuninlines.h index e134def617..13fe51e26b 100644 --- a/js/src/jsfuninlines.h +++ b/js/src/jsfuninlines.h @@ -88,7 +88,7 @@ CloneFunctionObjectIfNotSingleton(JSContext* cx, HandleFunction fun, HandleObjec if (CanReuseScriptForClone(cx->compartment(), fun, parent)) return CloneFunctionReuseScript(cx, fun, parent, kind, newKind, proto); - RootedScript script(cx, fun->getOrCreateScript(cx)); + RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun)); if (!script) return nullptr; RootedScope enclosingScope(cx, script->enclosingScope()); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 3d4dae9bb9..5a9d732b65 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -207,7 +207,6 @@ #include "jsscript.h" #include "jstypes.h" #include "jsutil.h" -#include "jswatchpoint.h" #include "jsweakmap.h" #ifdef XP_WIN # include "jswin.h" @@ -1524,19 +1523,11 @@ GCMarker::delayMarkingChildren(const void* thing) } inline void -ArenaLists::prepareForIncrementalGC(JSRuntime* rt) +ArenaLists::prepareForIncrementalGC() { + purge(); for (auto i : AllAllocKinds()) { - FreeSpan* span = freeLists[i]; - if (span != &placeholder) { - if (!span->isEmpty()) { - Arena* arena = span->getArena(); - arena->allocatedDuringIncremental = true; - rt->gc.marker.delayMarkingArena(arena); - } else { - freeLists[i] = &placeholder; - } - } + arenaLists[i].moveCursorToEnd(); } } @@ -2251,7 +2242,7 @@ GCRuntime::updateTypeDescrObjects(MovingTracer* trc, Zone* zone) { zone->typeDescrObjects.sweep(); for (auto r = zone->typeDescrObjects.all(); !r.empty(); r.popFront()) - UpdateCellPointers(trc, r.front().get()); + UpdateCellPointers(trc, r.front()); } void @@ -2310,22 +2301,27 @@ GCRuntime::updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, s // 2) typed object type descriptor objects // 3) all other objects // +// Also, there can be data races calling IsForwarded() on the new location of a +// cell that is being updated in parallel on another thread. This can be avoided +// by updating some kinds of cells in different phases. This is done for JSScripts +// and LazyScripts, and JSScripts and Scopes. +// // Since we want to minimize the number of phases, we put everything else into // the first phase and label it the 'misc' phase. static const AllocKinds UpdatePhaseMisc { AllocKind::SCRIPT, - AllocKind::LAZY_SCRIPT, AllocKind::BASE_SHAPE, AllocKind::SHAPE, AllocKind::ACCESSOR_SHAPE, AllocKind::OBJECT_GROUP, AllocKind::STRING, - AllocKind::JITCODE, - AllocKind::SCOPE + AllocKind::JITCODE }; static const AllocKinds UpdatePhaseObjects { + AllocKind::LAZY_SCRIPT, + AllocKind::SCOPE, AllocKind::FUNCTION, AllocKind::FUNCTION_EXTENDED, AllocKind::OBJECT0, @@ -2395,11 +2391,6 @@ GCRuntime::updatePointersToRelocatedCells(Zone* zone, AutoLockForExclusiveAccess Debugger::markIncomingCrossCompartmentEdges(&trc); WeakMapBase::markAll(zone, &trc); - for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) { - c->trace(&trc); - if (c->watchpointMap) - c->watchpointMap->markAll(&trc); - } // Mark all gray roots, making sure we call the trace callback to get the // current set. @@ -2408,7 +2399,6 @@ GCRuntime::updatePointersToRelocatedCells(Zone* zone, AutoLockForExclusiveAccess } // Sweep everything to fix up weak pointers - WatchpointMap::sweepAll(rt); Debugger::sweepAll(rt->defaultFreeOp()); jit::JitRuntime::SweepJitcodeGlobalTable(rt); rt->gc.sweepZoneAfterCompacting(zone); @@ -3574,6 +3564,23 @@ RelazifyFunctions(Zone* zone, AllocKind kind) } } +static bool +ShouldCollectZone(Zone* zone, JS::gcreason::Reason reason) +{ + // Normally we collect all scheduled zones. + if (reason != JS::gcreason::COMPARTMENT_REVIVED) + return zone->isGCScheduled(); + + // If we are repeating a GC because we noticed dead compartments haven't + // been collected, then only collect zones containing those compartments. + for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) { + if (comp->scheduledForDestruction) + return true; + } + + return false; +} + bool GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock) { @@ -3597,7 +3604,7 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces #endif /* Set up which zones will be collected. */ - if (zone->isGCScheduled()) { + if (ShouldCollectZone(zone, reason)) { if (!zone->isAtomsZone()) { any = true; zone->setGCState(Zone::Mark); @@ -3616,7 +3623,7 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) { c->marked = false; c->scheduledForDestruction = false; - c->maybeAlive = false; + c->maybeAlive = c->hasBeenEntered() || !c->zone()->isGCScheduled(); if (shouldPreserveJITCode(c, currentTime, reason, canAllocateMoreCode)) c->zone()->setPreservingCode(true); } @@ -3636,6 +3643,7 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces * on. If the value of keepAtoms() changes between GC slices, then we'll * cancel the incremental GC. See IsIncrementalGCSafe. */ + if (isFull && !rt->keepAtoms()) { Zone* atomsZone = rt->atomsCompartment(lock)->zone(); if (atomsZone->isGCScheduled()) { @@ -3650,15 +3658,12 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces return false; /* - * At the end of each incremental slice, we call prepareForIncrementalGC, - * which marks objects in all arenas that we're currently allocating - * into. This can cause leaks if unreachable objects are in these - * arenas. This purge call ensures that we only mark arenas that have had - * allocations after the incremental GC started. + * Ensure that after the start of a collection we don't allocate into any + * existing arenas, as this can cause unreachable things to be marked. */ if (isIncremental) { for (GCZonesIter zone(rt); !zone.done(); zone.next()) - zone->arenas.purge(); + zone->arenas.prepareForIncrementalGC(); } MemProfiler::MarkTenuredStart(rt); @@ -3743,12 +3748,10 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces gcstats::AutoPhase ap2(stats, gcstats::PHASE_MARK_ROOTS); if (isIncremental) { - gcstats::AutoPhase ap3(stats, gcstats::PHASE_BUFFER_GRAY_ROOTS); bufferGrayRoots(); + markCompartments(); } - - markCompartments(); - + return true; } @@ -3761,9 +3764,14 @@ GCRuntime::markCompartments() * This code ensures that if a compartment is "dead", then it will be * collected in this GC. A compartment is considered dead if its maybeAlive * flag is false. The maybeAlive flag is set if: - * (1) the compartment has incoming cross-compartment edges, or - * (2) an object in the compartment was marked during root marking, either - * as a black root or a gray root. + * (1) the compartment has been entered (set in beginMarkPhase() above) + * (2) the compartment is not being collected (set in beginMarkPhase() + * above) + * (3) an object in the compartment was marked during root marking, either + * as a black root or a gray root (set in RootMarking.cpp), or + * (4) the compartment has incoming cross-compartment edges from another + * compartment that has maybeAlive set (set by this method). + * * If the maybeAlive is false, then we set the scheduledForDestruction flag. * At the end of the GC, we look for compartments where * scheduledForDestruction is true. These are compartments that were somehow @@ -3781,26 +3789,37 @@ GCRuntime::markCompartments() * allocation and read barriers during JS_TransplantObject and the like. */ - /* Set the maybeAlive flag based on cross-compartment edges. */ - for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { - for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { + /* Propagate the maybeAlive flag via cross-compartment edges. */ + + Vector<JSCompartment*, 0, js::SystemAllocPolicy> workList; + + for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) { + if (comp->maybeAlive) { + if (!workList.append(comp)) + return; + } + } + while (!workList.empty()) { + JSCompartment* comp = workList.popCopy(); + for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront()) { if (e.front().key().is<JSString*>()) continue; JSCompartment* dest = e.front().mutableKey().compartment(); - if (dest) + if (dest && !dest->maybeAlive) { dest->maybeAlive = true; + if (!workList.append(dest)) + return; + } } } - /* - * For black roots, code in gc/Marking.cpp will already have set maybeAlive - * during MarkRuntime. - */ - - /* Propogate maybeAlive to scheduleForDestruction. */ - for (GCCompartmentsIter c(rt); !c.done(); c.next()) { - if (!c->maybeAlive && !rt->isAtomsCompartment(c)) - c->scheduledForDestruction = true; + + /* Set scheduleForDestruction based on maybeAlive. */ + + for (GCCompartmentsIter comp(rt); !comp.done(); comp.next()) { + MOZ_ASSERT(!comp->scheduledForDestruction); + if (!comp->maybeAlive && !rt->isAtomsCompartment(comp)) + comp->scheduledForDestruction = true; } } @@ -3824,10 +3843,6 @@ GCRuntime::markWeakReferences(gcstats::Phase phase) for (ZoneIterT zone(rt); !zone.done(); zone.next()) markedAny |= WeakMapBase::markZoneIteratively(zone, &marker); } - for (CompartmentsIterT<ZoneIterT> c(rt); !c.done(); c.next()) { - if (c->watchpointMap) - markedAny |= c->watchpointMap->markIteratively(&marker); - } markedAny |= Debugger::markAllIteratively(&marker); markedAny |= jit::JitRuntime::MarkJitcodeGlobalTableIteratively(&marker); @@ -4599,9 +4614,6 @@ GCRuntime::beginSweepingZoneGroup(AutoLockForExclusiveAccess& lock) // Bug 1071218: the following two methods have not yet been // refactored to work on a single zone-group at once. - // Collect watch points associated with unreachable objects. - WatchpointMap::sweepAll(rt); - // Detach unreachable debuggers and global objects from each other. Debugger::sweepAll(&fop); @@ -5301,7 +5313,7 @@ AutoGCSlice::~AutoGCSlice() for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) { if (zone->isGCMarking()) { zone->setNeedsIncrementalBarrier(true, Zone::UpdateJit); - zone->arenas.prepareForIncrementalGC(runtime); + zone->arenas.purge(); } else { zone->setNeedsIncrementalBarrier(false, Zone::UpdateJit); } @@ -5482,7 +5494,7 @@ gc::AbortReason gc::IsIncrementalGCUnsafe(JSRuntime* rt) { MOZ_ASSERT(!rt->mainThread.suppressGC); - + if (rt->keepAtoms()) return gc::AbortReason::KeepAtomsSet; @@ -5493,9 +5505,17 @@ gc::IsIncrementalGCUnsafe(JSRuntime* rt) } void -GCRuntime::budgetIncrementalGC(SliceBudget& budget, AutoLockForExclusiveAccess& lock) +GCRuntime::budgetIncrementalGC(JS::gcreason::Reason reason, SliceBudget& budget, + AutoLockForExclusiveAccess& lock) { AbortReason unsafeReason = IsIncrementalGCUnsafe(rt); + if (unsafeReason == AbortReason::None) { + if (reason == JS::gcreason::COMPARTMENT_REVIVED) + unsafeReason = gc::AbortReason::CompartmentRevived; + else if (mode != JSGC_MODE_INCREMENTAL) + unsafeReason = gc::AbortReason::ModeChange; + } + if (unsafeReason != AbortReason::None) { resetIncrementalGC(unsafeReason, lock); budget.makeUnlimited(); @@ -5503,12 +5523,7 @@ GCRuntime::budgetIncrementalGC(SliceBudget& budget, AutoLockForExclusiveAccess& return; } - if (mode != JSGC_MODE_INCREMENTAL) { - resetIncrementalGC(AbortReason::ModeChange, lock); - budget.makeUnlimited(); - stats.nonincremental(AbortReason::ModeChange); - return; - } + if (isTooMuchMalloc()) { budget.makeUnlimited(); @@ -5667,7 +5682,7 @@ GCRuntime::gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason:: stats.nonincremental(gc::AbortReason::NonIncrementalRequested); budget.makeUnlimited(); } else { - budgetIncrementalGC(budget, session.lock); + budgetIncrementalGC(reason, budget, session.lock); } /* The GC was reset, so we need a do-over. */ @@ -5759,6 +5774,22 @@ GCRuntime::checkIfGCAllowedInCurrentState(JS::gcreason::Reason reason) return true; } +bool +GCRuntime::shouldRepeatForDeadZone(JS::gcreason::Reason reason) +{ + MOZ_ASSERT_IF(reason == JS::gcreason::COMPARTMENT_REVIVED, !isIncremental); + + if (!isIncremental || isIncrementalGCInProgress()) + return false; + + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { + if (c->scheduledForDestruction) + return true; + } + + return false; +} + void GCRuntime::collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::Reason reason) { @@ -5777,27 +5808,23 @@ GCRuntime::collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::R do { poked = false; bool wasReset = gcCycle(nonincrementalByAPI, budget, reason); - - /* Need to re-schedule all zones for GC. */ - if (poked && cleanUpEverything) + + bool repeatForDeadZone = false; + if (poked && cleanUpEverything) { + /* Need to re-schedule all zones for GC. */ JS::PrepareForFullGC(rt->contextFromMainThread()); - /* - * This code makes an extra effort to collect compartments that we - * thought were dead at the start of the GC. See the large comment in - * beginMarkPhase. - */ - bool repeatForDeadZone = false; - if (!nonincrementalByAPI && !isIncrementalGCInProgress()) { - for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { - if (c->scheduledForDestruction) { - nonincrementalByAPI = true; - repeatForDeadZone = true; - reason = JS::gcreason::COMPARTMENT_REVIVED; - c->zone()->scheduleGC(); - } - } + + } else if (shouldRepeatForDeadZone(reason) && !wasReset) { + /* + * This code makes an extra effort to collect compartments that we + * thought were dead at the start of the GC. See the large comment + * in beginMarkPhase. + */ + repeatForDeadZone = true; + reason = JS::gcreason::COMPARTMENT_REVIVED; } + /* * If we reset an existing GC, we need to start a new one. Also, we @@ -6149,12 +6176,6 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) for (auto group = source->zone()->cellIter<ObjectGroup>(); !group.done(); group.next()) { group->setGeneration(target->zone()->types.generation); group->compartment_ = target; - - // Remove any unboxed layouts from the list in the off thread - // compartment. These do not need to be reinserted in the target - // compartment's list, as the list is not required to be complete. - if (UnboxedLayout* layout = group->maybeUnboxedLayoutDontCheckGeneration()) - layout->detachFromCompartment(); } // Fixup zone pointers in source's zone to refer to target's zone. diff --git a/js/src/jsgc.h b/js/src/jsgc.h index d3cf31fe77..952fd6bae5 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -61,7 +61,8 @@ enum class State { D(ModeChange) \ D(MallocBytesTrigger) \ D(GCBytesTrigger) \ - D(ZoneChange) + D(ZoneChange) \ + D(CompartmentRevived) enum class AbortReason { #define MAKE_REASON(name) name, GC_ABORT_REASONS(MAKE_REASON) @@ -453,6 +454,12 @@ class ArenaList { check(); return !*cursorp_; } + + void moveCursorToEnd() { + while (!isCursorAtEnd()) { + cursorp_ = &(*cursorp_)->next; + } + } // This can return nullptr. Arena* arenaAfterCursor() const { @@ -739,7 +746,7 @@ class ArenaLists freeLists[i] = &placeholder; } - inline void prepareForIncrementalGC(JSRuntime* rt); + inline void prepareForIncrementalGC(); /* Check if this arena is in use. */ bool arenaIsInUse(Arena* arena, AllocKind kind) const { diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 59893fa989..3e222ca6fb 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -157,8 +157,11 @@ SortComparatorIntegerIds(jsid a, jsid b, bool* lessOrEqualp) } static bool -EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags, Maybe<IdSet>& ht, - AutoIdVector* props, Handle<UnboxedPlainObject*> unboxed = nullptr) +EnumerateNativeProperties(JSContext* cx, + HandleNativeObject pobj, + unsigned flags, + Maybe<IdSet>& ht, + AutoIdVector* props) { bool enumerateSymbols; if (flags & JSITER_SYMBOLSONLY) { @@ -220,16 +223,6 @@ EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags return false; } - if (unboxed) { - // If |unboxed| is set then |pobj| is the expando for an unboxed - // plain object we are enumerating. Add the unboxed properties - // themselves here since they are all property names that were - // given to the object before any of the expando's properties. - MOZ_ASSERT(pobj->is<UnboxedExpandoObject>()); - if (!EnumerateExtraProperties(cx, unboxed, flags, ht, props)) - return false; - } - size_t initialLength = props->length(); /* Collect all unique property names from this object's shape. */ @@ -355,22 +348,12 @@ Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props) do { if (pobj->getOpsEnumerate()) { - if (pobj->is<UnboxedPlainObject>() && pobj->as<UnboxedPlainObject>().maybeExpando()) { - // Special case unboxed objects with an expando object. - RootedNativeObject expando(cx, pobj->as<UnboxedPlainObject>().maybeExpando()); - if (!EnumerateNativeProperties(cx, expando, flags, ht, props, - pobj.as<UnboxedPlainObject>())) - { - return false; - } - } else { - if (!EnumerateExtraProperties(cx, pobj, flags, ht, props)) - return false; + if (!EnumerateExtraProperties(cx, pobj, flags, ht, props)) + return false; - if (pobj->isNative()) { - if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props)) - return false; - } + if (pobj->isNative()) { + if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props)) + return false; } } else if (pobj->isNative()) { // Give the object a chance to resolve all lazy properties @@ -671,7 +654,7 @@ VectorToKeyIterator(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVecto { MOZ_ASSERT(!(flags & JSITER_FOREACH)); - if (obj->isSingleton() && !obj->setIteratedSingleton(cx)) + if (obj->isSingleton() && !JSObject::setIteratedSingleton(cx, obj)) return false; MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED); @@ -715,7 +698,7 @@ VectorToValueIterator(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVec { MOZ_ASSERT(flags & JSITER_FOREACH); - if (obj->isSingleton() && !obj->setIteratedSingleton(cx)) + if (obj->isSingleton() && !JSObject::setIteratedSingleton(cx, obj)) return false; MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED); @@ -785,11 +768,6 @@ CanCompareIterableObjectToCache(JSObject* obj) { if (obj->isNative()) return obj->as<NativeObject>().hasEmptyElements(); - if (obj->is<UnboxedPlainObject>()) { - if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) - return expando->hasEmptyElements(); - return true; - } return false; } @@ -943,7 +921,7 @@ js::CreateItrResultObject(JSContext* cx, HandleValue value, bool done) // FIXME: We can cache the iterator result object shape somewhere. AssertHeapIsIdle(cx); - RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx)); + RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, cx->global())); if (!proto) return nullptr; @@ -1507,7 +1485,7 @@ GlobalObject::initIteratorProto(JSContext* cx, Handle<GlobalObject*> global) if (global->getReservedSlot(ITERATOR_PROTO).isObject()) return true; - RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx)); + RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global)); if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, iterator_proto_methods)) return false; @@ -1526,7 +1504,8 @@ GlobalObject::initArrayIteratorProto(JSContext* cx, Handle<GlobalObject*> global return false; const Class* cls = &ArrayIteratorPrototypeClass; - RootedObject proto(cx, global->createBlankPrototypeInheriting(cx, cls, iteratorProto)); + RootedObject proto(cx, GlobalObject::createBlankPrototypeInheriting(cx, global, cls, + iteratorProto)); if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, array_iterator_methods) || !DefineToStringTag(cx, proto, cx->names().ArrayIterator)) @@ -1549,7 +1528,8 @@ GlobalObject::initStringIteratorProto(JSContext* cx, Handle<GlobalObject*> globa return false; const Class* cls = &StringIteratorPrototypeClass; - RootedObject proto(cx, global->createBlankPrototypeInheriting(cx, cls, iteratorProto)); + RootedObject proto(cx, GlobalObject::createBlankPrototypeInheriting(cx, global, cls, + iteratorProto)); if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, string_iterator_methods) || !DefineToStringTag(cx, proto, cx->names().StringIterator)) @@ -1570,7 +1550,8 @@ js::InitLegacyIteratorClass(JSContext* cx, HandleObject obj) return &global->getPrototype(JSProto_Iterator).toObject(); RootedObject iteratorProto(cx); - iteratorProto = global->createBlankPrototype(cx, &PropertyIteratorObject::class_); + iteratorProto = GlobalObject::createBlankPrototype(cx, global, + &PropertyIteratorObject::class_); if (!iteratorProto) return nullptr; @@ -1582,7 +1563,7 @@ js::InitLegacyIteratorClass(JSContext* cx, HandleObject obj) ni->init(nullptr, nullptr, 0 /* flags */, 0, 0); Rooted<JSFunction*> ctor(cx); - ctor = global->createConstructor(cx, IteratorConstructor, cx->names().Iterator, 2); + ctor = GlobalObject::createConstructor(cx, IteratorConstructor, cx->names().Iterator, 2); if (!ctor) return nullptr; if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto)) @@ -1603,7 +1584,8 @@ js::InitStopIterationClass(JSContext* cx, HandleObject obj) { Handle<GlobalObject*> global = obj.as<GlobalObject>(); if (!global->getPrototype(JSProto_StopIteration).isObject()) { - RootedObject proto(cx, global->createBlankPrototype(cx, &StopIterationObject::class_)); + RootedObject proto(cx, GlobalObject::createBlankPrototype(cx, global, + &StopIterationObject::class_)); if (!proto || !FreezeObject(cx, proto)) return nullptr; diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index 08fbe048c0..78a2310030 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -1417,7 +1417,8 @@ static const JSFunctionSpec math_static_methods[] = { JSObject* js::InitMathClass(JSContext* cx, HandleObject obj) { - RootedObject proto(cx, obj->as<GlobalObject>().getOrCreateObjectPrototype(cx)); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); + RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); if (!proto) return nullptr; RootedObject Math(cx, NewObjectWithGivenProto(cx, &MathClass, proto, SingletonObject)); diff --git a/js/src/jsnativestack.cpp b/js/src/jsnativestack.cpp index 166a5a4f77..4e96e01e87 100644 --- a/js/src/jsnativestack.cpp +++ b/js/src/jsnativestack.cpp @@ -26,11 +26,7 @@ # include <sys/syscall.h> # include <sys/types.h> # include <unistd.h> -static pid_t -gettid() -{ - return syscall(__NR_gettid); -} +# define gettid() static_cast<pid_t>(syscall(SYS_gettid)) # endif #else @@ -71,33 +67,18 @@ js::GetNativeStackBaseImpl() # endif } -#elif defined(SOLARIS) +#elif defined(XP_SOLARIS) #include <ucontext.h> JS_STATIC_ASSERT(JS_STACK_GROWTH_DIRECTION < 0); -void* -js::GetNativeStackBaseImpl() -{ - stack_t st; - stack_getbounds(&st); - return static_cast<char*>(st.ss_sp) + st.ss_size; -} - -#elif defined(AIX) - -#include <ucontext.h> - -JS_STATIC_ASSERT(JS_STACK_GROWTH_DIRECTION < 0); - -void* +void* js::GetNativeStackBaseImpl() { - ucontext_t context; - getcontext(&context); - return static_cast<char*>(context.uc_stack.ss_sp) + - context.uc_stack.ss_size; + stack_t st; + stack_getbounds(&st); + return static_cast<char*>(st.ss_sp) + st.ss_size; } #elif defined(XP_LINUX) && !defined(ANDROID) && defined(__GLIBC__) diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 28ed151598..bde1f918e2 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -1005,15 +1005,16 @@ js::InitNumberClass(JSContext* cx, HandleObject obj) /* XXX must do at least once per new thread, so do it per JSContext... */ FIX_FPU(); - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); - RootedObject numberProto(cx, global->createBlankPrototype(cx, &NumberObject::class_)); + RootedObject numberProto(cx, GlobalObject::createBlankPrototype(cx, global, + &NumberObject::class_)); if (!numberProto) return nullptr; numberProto->as<NumberObject>().setPrimitiveValue(0); RootedFunction ctor(cx); - ctor = global->createConstructor(cx, Number, cx->names().Number, 1); + ctor = GlobalObject::createConstructor(cx, Number, cx->names().Number, 1); if (!ctor) return nullptr; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index b17c845bb7..ef12910797 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -33,7 +33,6 @@ #include "jsstr.h" #include "jstypes.h" #include "jsutil.h" -#include "jswatchpoint.h" #include "jswin.h" #include "jswrapper.h" @@ -43,6 +42,7 @@ #include "frontend/BytecodeCompiler.h" #include "gc/Marking.h" #include "gc/Policy.h" +#include "gc/StoreBuffer-inl.h" #include "jit/BaselineJIT.h" #include "js/MemoryMetrics.h" #include "js/Proxy.h" @@ -470,7 +470,7 @@ js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level) // Steps 8-9, loosely interpreted. if (obj->isNative() && !obj->as<NativeObject>().inDictionaryMode() && - !obj->is<TypedArrayObject>()) + !obj->is<TypedArrayObject>() && !obj->is<MappedArgumentsObject>()) { HandleNativeObject nobj = obj.as<NativeObject>(); @@ -869,9 +869,6 @@ static inline JSObject* CreateThisForFunctionWithGroup(JSContext* cx, HandleObjectGroup group, NewObjectKind newKind) { - if (group->maybeUnboxedLayout() && newKind != SingletonObject) - return UnboxedPlainObject::create(cx, group, newKind); - if (TypeNewScript* newScript = group->newScript()) { if (newScript->analyzed()) { // The definite properties analysis has been performed for this @@ -886,7 +883,7 @@ CreateThisForFunctionWithGroup(JSContext* cx, HandleObjectGroup group, if (newKind == SingletonObject) { Rooted<TaggedProto> proto(cx, TaggedProto(templateObject->staticPrototype())); - if (!res->splicePrototype(cx, &PlainObject::class_, proto)) + if (!JSObject::splicePrototype(cx, res, &PlainObject::class_, proto)) return nullptr; } else { res->setGroup(group); @@ -954,7 +951,7 @@ js::CreateThisForFunctionWithProto(JSContext* cx, HandleObject callee, HandleObj } if (res) { - JSScript* script = callee->as<JSFunction>().getOrCreateScript(cx); + JSScript* script = JSFunction::getOrCreateScript(cx, callee.as<JSFunction>()); if (!script) return nullptr; TypeScript::SetThis(cx, script, TypeSet::ObjectType(res)); @@ -1013,13 +1010,7 @@ js::CreateThisForFunction(JSContext* cx, HandleObject callee, HandleObject newTa JSObject::nonNativeSetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, HandleValue receiver, ObjectOpResult& result) { - RootedValue value(cx, v); - if (MOZ_UNLIKELY(obj->watched())) { - WatchpointMap* wpmap = cx->compartment()->watchpointMap; - if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, &value)) - return false; - } - return obj->getOpsSetProperty()(cx, obj, id, value, receiver, result); + return obj->getOpsSetProperty()(cx, obj, id, v, receiver, result); } /* static */ bool @@ -1145,19 +1136,18 @@ js::CloneObject(JSContext* cx, HandleObject obj, Handle<js::TaggedProto> proto) } static bool -GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, MutableHandle<GCVector<Value>> values) +GetScriptArrayObjectElements(JSContext* cx, HandleArrayObject arr, MutableHandle<GCVector<Value>> values) { - MOZ_ASSERT(!obj->isSingleton()); - MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()); - MOZ_ASSERT(!obj->isIndexed()); + MOZ_ASSERT(!arr->isSingleton()); + MOZ_ASSERT(!arr->isIndexed()); - size_t length = GetAnyBoxedOrUnboxedArrayLength(obj); + size_t length = arr->length(); if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), length)) return false; - size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj); + size_t initlen = arr->getDenseInitializedLength(); for (size_t i = 0; i < initlen; i++) - values[i].set(GetAnyBoxedOrUnboxedDenseElement(obj, i)); + values[i].set(arr->getDenseElement(i)); return true; } @@ -1166,46 +1156,27 @@ static bool GetScriptPlainObjectProperties(JSContext* cx, HandleObject obj, MutableHandle<IdValueVector> properties) { - if (obj->is<PlainObject>()) { - PlainObject* nobj = &obj->as<PlainObject>(); - - if (!properties.appendN(IdValuePair(), nobj->slotSpan())) - return false; + MOZ_ASSERT(obj->is<PlainObject>()); + PlainObject* nobj = &obj->as<PlainObject>(); - for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) { - Shape& shape = r.front(); - MOZ_ASSERT(shape.isDataDescriptor()); - uint32_t slot = shape.slot(); - properties[slot].get().id = shape.propid(); - properties[slot].get().value = nobj->getSlot(slot); - } - - for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) { - Value v = nobj->getDenseElement(i); - if (!v.isMagic(JS_ELEMENTS_HOLE) && !properties.append(IdValuePair(INT_TO_JSID(i), v))) - return false; - } + if (!properties.appendN(IdValuePair(), nobj->slotSpan())) + return false; - return true; + for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) { + Shape& shape = r.front(); + MOZ_ASSERT(shape.isDataDescriptor()); + uint32_t slot = shape.slot(); + properties[slot].get().id = shape.propid(); + properties[slot].get().value = nobj->getSlot(slot); } - if (obj->is<UnboxedPlainObject>()) { - UnboxedPlainObject* nobj = &obj->as<UnboxedPlainObject>(); - - const UnboxedLayout& layout = nobj->layout(); - if (!properties.appendN(IdValuePair(), layout.properties().length())) + for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) { + Value v = nobj->getDenseElement(i); + if (!v.isMagic(JS_ELEMENTS_HOLE) && !properties.append(IdValuePair(INT_TO_JSID(i), v))) return false; - - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - properties[i].get().id = NameToId(property.name); - properties[i].get().value = nobj->getValue(property); - } - - return true; } - MOZ_CRASH("Bad object kind"); + return true; } static bool @@ -1227,13 +1198,13 @@ js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKin /* NB: Keep this in sync with XDRObjectLiteral. */ MOZ_ASSERT_IF(obj->isSingleton(), cx->compartment()->behaviors().getSingletonsAsTemplates()); - MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() || - obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()); + MOZ_ASSERT(obj->is<PlainObject>() || + obj->is<ArrayObject>()); MOZ_ASSERT(newKind != SingletonObject); - if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) { + if (obj->is<ArrayObject>()) { Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx)); - if (!GetScriptArrayObjectElements(cx, obj, &values)) + if (!GetScriptArrayObjectElements(cx, obj.as<ArrayObject>(), &values)) return nullptr; // Deep clone any elements. @@ -1347,10 +1318,8 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj) { if (mode == XDR_ENCODE) { MOZ_ASSERT(obj->is<PlainObject>() || - obj->is<UnboxedPlainObject>() || - obj->is<ArrayObject>() || - obj->is<UnboxedArrayObject>()); - isArray = (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) ? 1 : 0; + obj->is<ArrayObject>()); + isArray = obj->is<ArrayObject>() ? 1 : 0; } if (!xdr->codeUint32(&isArray)) @@ -1362,8 +1331,11 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj) if (isArray) { Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx)); - if (mode == XDR_ENCODE && !GetScriptArrayObjectElements(cx, obj, &values)) - return false; + if (mode == XDR_ENCODE) { + RootedArrayObject arr(cx, &obj->as<ArrayObject>()); + if (!GetScriptArrayObjectElements(cx, arr, &values)) + return false; + } uint32_t initialized; if (mode == XDR_ENCODE) @@ -1451,40 +1423,41 @@ js::XDRObjectLiteral(XDRState<XDR_ENCODE>* xdr, MutableHandleObject obj); template bool js::XDRObjectLiteral(XDRState<XDR_DECODE>* xdr, MutableHandleObject obj); -bool -NativeObject::fillInAfterSwap(JSContext* cx, const Vector<Value>& values, void* priv) +/* static */ bool +NativeObject::fillInAfterSwap(JSContext* cx, HandleNativeObject obj, + const Vector<Value>& values, void* priv) { // This object has just been swapped with some other object, and its shape // no longer reflects its allocated size. Correct this information and // fill the slots in with the specified values. - MOZ_ASSERT(slotSpan() == values.length()); + MOZ_ASSERT(obj->slotSpan() == values.length()); // Make sure the shape's numFixedSlots() is correct. - size_t nfixed = gc::GetGCKindSlots(asTenured().getAllocKind(), getClass()); - if (nfixed != shape_->numFixedSlots()) { - if (!generateOwnShape(cx)) + size_t nfixed = gc::GetGCKindSlots(obj->asTenured().getAllocKind(), obj->getClass()); + if (nfixed != obj->shape_->numFixedSlots()) { + if (!NativeObject::generateOwnShape(cx, obj)) return false; - shape_->setNumFixedSlots(nfixed); + obj->shape_->setNumFixedSlots(nfixed); } - if (hasPrivate()) - setPrivate(priv); + if (obj->hasPrivate()) + obj->setPrivate(priv); else MOZ_ASSERT(!priv); - if (slots_) { - js_free(slots_); - slots_ = nullptr; + if (obj->slots_) { + js_free(obj->slots_); + obj->slots_ = nullptr; } - if (size_t ndynamic = dynamicSlotsCount(nfixed, values.length(), getClass())) { - slots_ = cx->zone()->pod_malloc<HeapSlot>(ndynamic); - if (!slots_) + if (size_t ndynamic = dynamicSlotsCount(nfixed, values.length(), obj->getClass())) { + obj->slots_ = cx->zone()->pod_malloc<HeapSlot>(ndynamic); + if (!obj->slots_) return false; - Debug_SetSlotRangeToCrashOnTouch(slots_, ndynamic); + Debug_SetSlotRangeToCrashOnTouch(obj->slots_, ndynamic); } - initSlotRange(0, values.begin(), values.length()); + obj->initSlotRange(0, values.begin(), values.length()); return true; } @@ -1510,9 +1483,9 @@ JSObject::swap(JSContext* cx, HandleObject a, HandleObject b) AutoCompartment ac(cx, a); - if (!a->getGroup(cx)) + if (!JSObject::getGroup(cx, a)) oomUnsafe.crash("JSObject::swap"); - if (!b->getGroup(cx)) + if (!JSObject::getGroup(cx, b)) oomUnsafe.crash("JSObject::swap"); /* @@ -1594,10 +1567,14 @@ JSObject::swap(JSContext* cx, HandleObject a, HandleObject b) a->fixDictionaryShapeAfterSwap(); b->fixDictionaryShapeAfterSwap(); - if (na && !b->as<NativeObject>().fillInAfterSwap(cx, avals, apriv)) - oomUnsafe.crash("fillInAfterSwap"); - if (nb && !a->as<NativeObject>().fillInAfterSwap(cx, bvals, bpriv)) - oomUnsafe.crash("fillInAfterSwap"); + if (na) { + if (!NativeObject::fillInAfterSwap(cx, b.as<NativeObject>(), avals, apriv)) + oomUnsafe.crash("fillInAfterSwap"); + } + if (nb) { + if (!NativeObject::fillInAfterSwap(cx, a.as<NativeObject>(), bvals, bpriv)) + oomUnsafe.crash("fillInAfterSwap"); + } } // Swapping the contents of two objects invalidates type sets which contain @@ -1743,7 +1720,7 @@ DefineConstructorAndPrototype(JSContext* cx, HandleObject obj, JSProtoKey key, H /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */ Rooted<TaggedProto> tagged(cx, TaggedProto(proto)); - if (ctor->getClass() == clasp && !ctor->splicePrototype(cx, clasp, tagged)) + if (ctor->getClass() == clasp && !JSObject::splicePrototype(cx, ctor, clasp, tagged)) goto bad; } @@ -1860,10 +1837,10 @@ js::SetClassAndProto(JSContext* cx, HandleObject obj, // We always generate a new shape if the object is a singleton, // regardless of the uncacheable-proto flag. ICs may rely on // this. - if (!oldproto->as<NativeObject>().generateOwnShape(cx)) + if (!NativeObject::generateOwnShape(cx, oldproto.as<NativeObject>())) return false; } else { - if (!oldproto->setUncacheableProto(cx)) + if (!JSObject::setUncacheableProto(cx, oldproto)) return false; } if (!obj->isDelegate()) { @@ -1875,15 +1852,18 @@ js::SetClassAndProto(JSContext* cx, HandleObject obj, oldproto = oldproto->staticPrototype(); } - if (proto.isObject() && !proto.toObject()->setDelegate(cx)) - return false; + if (proto.isObject()) { + RootedObject protoObj(cx, proto.toObject()); + if (!JSObject::setDelegate(cx, protoObj)) + return false; + } if (obj->isSingleton()) { /* * Just splice the prototype, but mark the properties as unknown for * consistent behavior. */ - if (!obj->splicePrototype(cx, clasp, proto)) + if (!JSObject::splicePrototype(cx, obj, clasp, proto)) return false; MarkObjectGroupUnknownProperties(cx, obj->group()); return true; @@ -2003,7 +1983,8 @@ js::GetObjectFromIncumbentGlobal(JSContext* cx, MutableHandleObject obj) { AutoCompartment ac(cx, globalObj); - obj.set(globalObj->as<GlobalObject>().getOrCreateObjectPrototype(cx)); + Handle<GlobalObject*> global = globalObj.as<GlobalObject>(); + obj.set(GlobalObject::getOrCreateObjectPrototype(cx, global)); if (!obj) return false; } @@ -2216,7 +2197,8 @@ js::LookupNameUnqualified(JSContext* cx, HandlePropertyName name, HandleObject e // environments. if (env->is<DebugEnvironmentProxy>()) { RootedValue v(cx); - if (!env->as<DebugEnvironmentProxy>().getMaybeSentinelValue(cx, id, &v)) + Rooted<DebugEnvironmentProxy*> envProxy(cx, &env->as<DebugEnvironmentProxy>()); + if (!DebugEnvironmentProxy::getMaybeSentinelValue(cx, envProxy, id, &v)) return false; isTDZ = IsUninitializedLexical(v); } else { @@ -2333,16 +2315,6 @@ js::LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Shape** // us the resolve hook won't define a property with this id. if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj)) return false; - } else if (obj->is<UnboxedPlainObject>()) { - if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) { - MarkNonNativePropertyFound<NoGC>(propp); - return true; - } - } else if (obj->is<UnboxedArrayObject>()) { - if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) { - MarkNonNativePropertyFound<NoGC>(propp); - return true; - } } else if (obj->is<TypedObject>()) { if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) { MarkNonNativePropertyFound<NoGC>(propp); @@ -2357,9 +2329,18 @@ js::LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Shape** } static inline bool -NativeGetPureInline(NativeObject* pobj, Shape* shape, Value* vp) +NativeGetPureInline(NativeObject* pobj, jsid id, Shape* shape, Value* vp) { - /* Fail if we have a custom getter. */ + if (IsImplicitDenseOrTypedArrayElement(shape)) { + // For simplicity we ignore the TypedArray with string index case. + if (!JSID_IS_INT(id)) + return false; + + *vp = pobj->getDenseOrTypedArrayElement(JSID_TO_INT(id)); + return true; + } + + // Fail if we have a custom getter. if (!shape->hasDefaultGetter()) return false; @@ -2386,13 +2367,13 @@ js::GetPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Value* vp) return true; } - return pobj->isNative() && NativeGetPureInline(&pobj->as<NativeObject>(), shape, vp); + return pobj->isNative() && NativeGetPureInline(&pobj->as<NativeObject>(), id, shape, vp); } static inline bool NativeGetGetterPureInline(Shape* shape, JSFunction** fp) { - if (shape->hasGetterObject()) { + if (!IsImplicitDenseOrTypedArrayElement(shape) && shape->hasGetterObject()) { if (shape->getterObject()->is<JSFunction>()) { *fp = &shape->getterObject()->as<JSFunction>(); return true; @@ -2473,7 +2454,7 @@ js::HasOwnDataPropertyPure(JSContext* cx, JSObject* obj, jsid id, bool* result) return true; } -bool +/* static */ bool JSObject::reportReadOnly(JSContext* cx, jsid id, unsigned report) { RootedValue val(cx, IdToValue(id)); @@ -2482,7 +2463,7 @@ JSObject::reportReadOnly(JSContext* cx, jsid id, unsigned report) nullptr, nullptr); } -bool +/* static */ bool JSObject::reportNotConfigurable(JSContext* cx, jsid id, unsigned report) { RootedValue val(cx, IdToValue(id)); @@ -2491,10 +2472,10 @@ JSObject::reportNotConfigurable(JSContext* cx, jsid id, unsigned report) nullptr, nullptr); } -bool -JSObject::reportNotExtensible(JSContext* cx, unsigned report) +/* static */ bool +JSObject::reportNotExtensible(JSContext* cx, HandleObject obj, unsigned report) { - RootedValue val(cx, ObjectValue(*this)); + RootedValue val(cx, ObjectValue(*obj)); return ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE, JSDVG_IGNORE_STACK, val, nullptr, nullptr, nullptr); @@ -2566,7 +2547,7 @@ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::Object // [[Prototype]] chain is always properly immutable, even in the presence // of lazy standard classes. if (obj->is<GlobalObject>()) { - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); if (!GlobalObject::ensureConstructor(cx, global, JSProto_Object)) return false; } @@ -2590,11 +2571,6 @@ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::Object break; } - // Convert unboxed objects to their native representations before changing - // their prototype/group, as they depend on the group for their layout. - if (!MaybeConvertUnboxedObjectToNative(cx, obj)) - return false; - Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto)); if (!SetClassAndProto(cx, obj, obj->getClass(), taggedProto)) return false; @@ -2618,9 +2594,6 @@ js::PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result, I if (!obj->nonProxyIsExtensible()) return result.succeed(); - if (!MaybeConvertUnboxedObjectToNative(cx, obj)) - return false; - // Force lazy properties to be resolved. AutoIdVector props(cx); if (!js::GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props)) @@ -2641,7 +2614,7 @@ js::PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result, I } } - if (!obj->setFlags(cx, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE)) { + if (!JSObject::setFlags(cx, obj, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE)) { // We failed to mark the object non-extensible, so reset the frozen // flag on the elements. MOZ_ASSERT(obj->nonProxyIsExtensible()); @@ -2781,7 +2754,7 @@ js::SetImmutablePrototype(ExclusiveContext* cx, HandleObject obj, bool* succeede return Proxy::setImmutablePrototype(cx->asJSContext(), obj, succeeded); } - if (!obj->setFlags(cx, BaseShape::IMMUTABLE_PROTOTYPE)) + if (!JSObject::setFlags(cx, obj, BaseShape::IMMUTABLE_PROTOTYPE)) return false; *succeeded = true; return true; @@ -2815,68 +2788,6 @@ js::GetPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, return true; } -bool -js::WatchGuts(JSContext* cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable) -{ - RootedObject obj(cx, ToWindowIfWindowProxy(origObj)); - if (obj->isNative()) { - // Use sparse indexes for watched objects, as dense elements can be - // written to without checking the watchpoint map. - if (!NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>())) - return false; - - MarkTypePropertyNonData(cx, obj, id); - } - - WatchpointMap* wpmap = cx->compartment()->watchpointMap; - if (!wpmap) { - wpmap = cx->runtime()->new_<WatchpointMap>(); - if (!wpmap || !wpmap->init()) { - ReportOutOfMemory(cx); - js_delete(wpmap); - return false; - } - cx->compartment()->watchpointMap = wpmap; - } - - return wpmap->watch(cx, obj, id, js::WatchHandler, callable); -} - -bool -js::UnwatchGuts(JSContext* cx, JS::HandleObject origObj, JS::HandleId id) -{ - // Looking in the map for an unsupported object will never hit, so we don't - // need to check for nativeness or watchable-ness here. - RootedObject obj(cx, ToWindowIfWindowProxy(origObj)); - if (WatchpointMap* wpmap = cx->compartment()->watchpointMap) - wpmap->unwatch(obj, id, nullptr, nullptr); - return true; -} - -bool -js::WatchProperty(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable) -{ - if (WatchOp op = obj->getOpsWatch()) - return op(cx, obj, id, callable); - - if (!obj->isNative() || obj->is<TypedArrayObject>()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_WATCH, - obj->getClass()->name); - return false; - } - - return WatchGuts(cx, obj, id, callable); -} - -bool -js::UnwatchProperty(JSContext* cx, HandleObject obj, HandleId id) -{ - if (UnwatchOp op = obj->getOpsUnwatch()) - return op(cx, obj, id); - - return UnwatchGuts(cx, obj, id); -} - const char* js::GetObjectClassName(JSContext* cx, HandleObject obj) { @@ -2891,24 +2802,6 @@ js::GetObjectClassName(JSContext* cx, HandleObject obj) /* * */ -bool -js::HasDataProperty(JSContext* cx, NativeObject* obj, jsid id, Value* vp) -{ - if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) { - *vp = obj->getDenseElement(JSID_TO_INT(id)); - return true; - } - - if (Shape* shape = obj->lookup(cx, id)) { - if (shape->hasDefaultGetter() && shape->hasSlot()) { - *vp = obj->getSlot(shape->slot()); - return true; - } - } - - return false; -} - extern bool PropertySpecNameToId(JSContext* cx, const char* name, MutableHandleId id, js::PinningBehavior pin = js::DoNotPinAtom); @@ -3024,7 +2917,7 @@ JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHan /* Optimize (new String(...)).toString(). */ if (clasp == &StringObject::class_) { StringObject* nobj = &obj->as<StringObject>(); - if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString)) { + if (HasNativeMethodPure(nobj, cx->names().toString, str_toString, cx)) { vp.setString(nobj->unbox()); return true; } @@ -3046,7 +2939,7 @@ JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHan /* Optimize new String(...).valueOf(). */ if (clasp == &StringObject::class_) { StringObject* nobj = &obj->as<StringObject>(); - if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString)) { + if (HasNativeMethodPure(nobj, cx->names().valueOf, str_toString, cx)) { vp.setString(nobj->unbox()); return true; } @@ -3055,7 +2948,7 @@ JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHan /* Optimize new Number(...).valueOf(). */ if (clasp == &NumberObject::class_) { NumberObject* nobj = &obj->as<NumberObject>(); - if (ClassMethodIsNative(cx, nobj, &NumberObject::class_, id, num_valueOf)) { + if (HasNativeMethodPure(nobj, cx->names().valueOf, num_valueOf, cx)) { vp.setNumber(nobj->unbox()); return true; } @@ -3454,7 +3347,6 @@ JSObject::dump(FILE* fp) const if (obj->isBoundFunction()) fprintf(fp, " bound_function"); if (obj->isQualifiedVarObj()) fprintf(fp, " varobj"); if (obj->isUnqualifiedVarObj()) fprintf(fp, " unqualified_varobj"); - if (obj->watched()) fprintf(fp, " watched"); if (obj->isIteratedSingleton()) fprintf(fp, " iterated_singleton"); if (obj->isNewGroupUnknown()) fprintf(fp, " new_type_unknown"); if (obj->hasUncacheableProto()) fprintf(fp, " has_uncacheable_proto"); @@ -3714,22 +3606,6 @@ JSObject::allocKindForTenure(const js::Nursery& nursery) const if (IsProxy(this)) return as<ProxyObject>().allocKindForTenure(); - // Unboxed plain objects are sized according to the data they store. - if (is<UnboxedPlainObject>()) { - size_t nbytes = as<UnboxedPlainObject>().layoutDontCheckGeneration().size(); - return GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + nbytes); - } - - // Unboxed arrays use inline data if their size is small enough. - if (is<UnboxedArrayObject>()) { - const UnboxedArrayObject* nobj = &as<UnboxedArrayObject>(); - size_t nbytes = UnboxedArrayObject::offsetOfInlineElements() + - nobj->capacity() * nobj->elementSize(); - if (nbytes <= JSObject::MAX_BYTE_SIZE) - return GetGCObjectKindForBytes(nbytes); - return AllocKind::OBJECT0; - } - // Inlined typed objects are followed by their data, so make sure we copy // it all over to the new object. if (is<InlineTypedObject>()) { @@ -3913,10 +3789,10 @@ displayAtomFromObjectGroup(ObjectGroup& group) return script->function()->displayAtom(); } -bool -JSObject::constructorDisplayAtom(JSContext* cx, js::MutableHandleAtom name) +/* static */ bool +JSObject::constructorDisplayAtom(JSContext* cx, js::HandleObject obj, js::MutableHandleAtom name) { - ObjectGroup *g = getGroup(cx); + ObjectGroup *g = JSObject::getGroup(cx, obj); if (!g) return false; diff --git a/js/src/jsobj.h b/js/src/jsobj.h index af79131af7..01845d7e6c 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -141,8 +141,6 @@ class JSObject : public js::gc::Cell js::GetOwnPropertyOp getOpsGetOwnPropertyDescriptor() const { return getClass()->getOpsGetOwnPropertyDescriptor(); } js::DeletePropertyOp getOpsDeleteProperty() const { return getClass()->getOpsDeleteProperty(); } - js::WatchOp getOpsWatch() const { return getClass()->getOpsWatch(); } - js::UnwatchOp getOpsUnwatch() const { return getClass()->getOpsUnwatch(); } js::GetElementsOp getOpsGetElements() const { return getClass()->getOpsGetElements(); } JSNewEnumerateOp getOpsEnumerate() const { return getClass()->getOpsEnumerate(); } JSFunToStringOp getOpsFunToString() const { return getClass()->getOpsFunToString(); } @@ -200,8 +198,8 @@ class JSObject : public js::gc::Cell GENERATE_SHAPE }; - bool setFlags(js::ExclusiveContext* cx, js::BaseShape::Flag flags, - GenerateShape generateShape = GENERATE_NONE); + static bool setFlags(js::ExclusiveContext* cx, JS::HandleObject obj, js::BaseShape::Flag flags, + GenerateShape generateShape = GENERATE_NONE); inline bool hasAllFlags(js::BaseShape::Flag flags) const; /* @@ -214,18 +212,13 @@ class JSObject : public js::gc::Cell * (see Purge{Scope,Proto}Chain in jsobj.cpp). */ inline bool isDelegate() const; - bool setDelegate(js::ExclusiveContext* cx) { - return setFlags(cx, js::BaseShape::DELEGATE, GENERATE_SHAPE); + static bool setDelegate(js::ExclusiveContext* cx, JS::HandleObject obj) { + return setFlags(cx, obj, js::BaseShape::DELEGATE, GENERATE_SHAPE); } inline bool isBoundFunction() const; inline bool hasSpecialEquality() const; - inline bool watched() const; - bool setWatched(js::ExclusiveContext* cx) { - return setFlags(cx, js::BaseShape::WATCHED, GENERATE_SHAPE); - } - // A "qualified" varobj is the object on which "qualified" variable // declarations (i.e., those defined with "var") are kept. // @@ -247,8 +240,8 @@ class JSObject : public js::gc::Cell // (e.g., Gecko and XPConnect), as they often wish to run scripts under a // scope that captures var bindings. inline bool isQualifiedVarObj() const; - bool setQualifiedVarObj(js::ExclusiveContext* cx) { - return setFlags(cx, js::BaseShape::QUALIFIED_VAROBJ); + static bool setQualifiedVarObj(js::ExclusiveContext* cx, JS::HandleObject obj) { + return setFlags(cx, obj, js::BaseShape::QUALIFIED_VAROBJ); } // An "unqualified" varobj is the object on which "unqualified" @@ -262,11 +255,11 @@ class JSObject : public js::gc::Cell // generate a new shape when their prototype changes, regardless of this // hasUncacheableProto flag. inline bool hasUncacheableProto() const; - bool setUncacheableProto(js::ExclusiveContext* cx) { - MOZ_ASSERT(hasStaticPrototype(), + static bool setUncacheableProto(js::ExclusiveContext* cx, JS::HandleObject obj) { + MOZ_ASSERT(obj->hasStaticPrototype(), "uncacheability as a concept is only applicable to static " "(not dynamically-computed) prototypes"); - return setFlags(cx, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE); + return setFlags(cx, obj, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE); } /* @@ -274,8 +267,8 @@ class JSObject : public js::gc::Cell * PropertyTree::MAX_HEIGHT. */ inline bool hadElementsAccess() const; - bool setHadElementsAccess(js::ExclusiveContext* cx) { - return setFlags(cx, js::BaseShape::HAD_ELEMENTS_ACCESS); + static bool setHadElementsAccess(js::ExclusiveContext* cx, JS::HandleObject obj) { + return setFlags(cx, obj, js::BaseShape::HAD_ELEMENTS_ACCESS); } /* @@ -288,7 +281,8 @@ class JSObject : public js::gc::Cell * If this object was instantiated with `new Ctor`, return the constructor's * display atom. Otherwise, return nullptr. */ - bool constructorDisplayAtom(JSContext* cx, js::MutableHandleAtom name); + static bool constructorDisplayAtom(JSContext* cx, js::HandleObject obj, + js::MutableHandleAtom name); /* * The same as constructorDisplayAtom above, however if this object has a @@ -348,7 +342,7 @@ class JSObject : public js::gc::Cell // Change an existing object to have a singleton group. static bool changeToSingleton(JSContext* cx, js::HandleObject obj); - inline js::ObjectGroup* getGroup(JSContext* cx); + static inline js::ObjectGroup* getGroup(JSContext* cx, js::HandleObject obj); const js::GCPtrObjectGroup& groupFromGC() const { /* Direct field access for use by GC. */ @@ -420,8 +414,8 @@ class JSObject : public js::gc::Cell * is purged on GC. */ inline bool isIteratedSingleton() const; - bool setIteratedSingleton(js::ExclusiveContext* cx) { - return setFlags(cx, js::BaseShape::ITERATED_SINGLETON); + static bool setIteratedSingleton(js::ExclusiveContext* cx, JS::HandleObject obj) { + return setFlags(cx, obj, js::BaseShape::ITERATED_SINGLETON); } /* @@ -433,18 +427,19 @@ class JSObject : public js::gc::Cell // Mark an object as having its 'new' script information cleared. inline bool wasNewScriptCleared() const; - bool setNewScriptCleared(js::ExclusiveContext* cx) { - return setFlags(cx, js::BaseShape::NEW_SCRIPT_CLEARED); + static bool setNewScriptCleared(js::ExclusiveContext* cx, JS::HandleObject obj) { + return setFlags(cx, obj, js::BaseShape::NEW_SCRIPT_CLEARED); } /* Set a new prototype for an object with a singleton type. */ - bool splicePrototype(JSContext* cx, const js::Class* clasp, js::Handle<js::TaggedProto> proto); + static bool splicePrototype(JSContext* cx, js::HandleObject obj, const js::Class* clasp, + js::Handle<js::TaggedProto> proto); /* * For bootstrapping, whether to splice a prototype for Function.prototype * or the global object. */ - bool shouldSplicePrototype(JSContext* cx); + bool shouldSplicePrototype(); /* * Environment chains. @@ -518,8 +513,9 @@ class JSObject : public js::gc::Cell public: static bool reportReadOnly(JSContext* cx, jsid id, unsigned report = JSREPORT_ERROR); - bool reportNotConfigurable(JSContext* cx, jsid id, unsigned report = JSREPORT_ERROR); - bool reportNotExtensible(JSContext* cx, unsigned report = JSREPORT_ERROR); + static bool reportNotConfigurable(JSContext* cx, jsid id, unsigned report = JSREPORT_ERROR); + static bool reportNotExtensible(JSContext* cx, js::HandleObject obj, + unsigned report = JSREPORT_ERROR); static bool nonNativeSetProperty(JSContext* cx, js::HandleObject obj, js::HandleId id, js::HandleValue v, js::HandleValue receiver, @@ -1029,21 +1025,6 @@ extern bool DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs, DefineAsIntrinsic intrinsic); -/* - * Set a watchpoint: a synchronous callback when the given property of the - * given object is set. - * - * Watchpoints are nonstandard and do not fit in well with the way ES6 - * specifies [[Set]]. They are also insufficient for implementing - * Object.observe. - */ -extern bool -WatchProperty(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable); - -/* Clear a watchpoint. */ -extern bool -UnwatchProperty(JSContext* cx, HandleObject obj, HandleId id); - /* ES6 draft rev 36 (2015 March 17) 7.1.1 ToPrimitive(vp[, preferredType]) */ extern bool ToPrimitiveSlow(JSContext* cx, JSType hint, MutableHandleValue vp); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 6be4d0d288..98e7401427 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -32,21 +32,6 @@ #include "vm/ShapedObject-inl.h" #include "vm/TypeInference-inl.h" -namespace js { - -// This is needed here for ensureShape() below. -inline bool -MaybeConvertUnboxedObjectToNative(ExclusiveContext* cx, JSObject* obj) -{ - if (obj->is<UnboxedPlainObject>()) - return UnboxedPlainObject::convertToNative(cx->asJSContext(), obj); - if (obj->is<UnboxedArrayObject>()) - return UnboxedArrayObject::convertToNative(cx->asJSContext(), obj); - return true; -} - -} // namespace js - inline js::Shape* JSObject::maybeShape() const { @@ -59,8 +44,6 @@ JSObject::maybeShape() const inline js::Shape* JSObject::ensureShape(js::ExclusiveContext* cx) { - if (!js::MaybeConvertUnboxedObjectToNative(cx, this)) - return nullptr; js::Shape* shape = maybeShape(); MOZ_ASSERT(shape); return shape; @@ -134,17 +117,16 @@ JSObject::setSingleton(js::ExclusiveContext* cx, js::HandleObject obj) return true; } -inline js::ObjectGroup* -JSObject::getGroup(JSContext* cx) +/* static */ inline js::ObjectGroup* +JSObject::getGroup(JSContext* cx, js::HandleObject obj) { - MOZ_ASSERT(cx->compartment() == compartment()); - if (hasLazyGroup()) { - JS::RootedObject self(cx, this); - if (cx->compartment() != compartment()) + MOZ_ASSERT(cx->compartment() == obj->compartment()); + if (obj->hasLazyGroup()) { + if (cx->compartment() != obj->compartment()) MOZ_CRASH(); - return makeLazyGroup(cx, self); + return makeLazyGroup(cx, obj); } - return group_; + return obj->group_; } inline void @@ -482,12 +464,6 @@ JSObject::isBoundFunction() const } inline bool -JSObject::watched() const -{ - return hasAllFlags(js::BaseShape::WATCHED); -} - -inline bool JSObject::isDelegate() const { return hasAllFlags(js::BaseShape::DELEGATE); @@ -574,48 +550,30 @@ IsNativeFunction(const js::Value& v, JSNative native) return IsFunctionObject(v, &fun) && fun->maybeNative() == native; } -/* - * When we have an object of a builtin class, we don't quite know what its - * valueOf/toString methods are, since these methods may have been overwritten - * or shadowed. However, we can still do better than the general case by - * hard-coding the necessary properties for us to find the native we expect. - * - * TODO: a per-thread shape-based cache would be faster and simpler. - */ +// Return whether looking up a method on 'obj' definitely resolves to the +// original specified native function. The method may conservatively return +// 'false' in the case of proxies or other non-native objects. static MOZ_ALWAYS_INLINE bool -ClassMethodIsNative(JSContext* cx, NativeObject* obj, const Class* clasp, jsid methodid, JSNative native) +HasNativeMethodPure(JSObject* obj, PropertyName* name, JSNative native, JSContext* cx) { - MOZ_ASSERT(obj->getClass() == clasp); - Value v; - if (!HasDataProperty(cx, obj, methodid, &v)) { - JSObject* proto = obj->staticPrototype(); - if (!proto || proto->getClass() != clasp || !HasDataProperty(cx, &proto->as<NativeObject>(), methodid, &v)) - return false; - } + if (!GetPropertyPure(cx, obj, NameToId(name), &v)) + return false; return IsNativeFunction(v, native); } -// Return whether looking up 'valueOf' on 'obj' definitely resolves to the -// original Object.prototype.valueOf. The method may conservatively return -// 'false' in the case of proxies or other non-native objects. +// Return whether 'obj' definitely has no @@toPrimitive method. static MOZ_ALWAYS_INLINE bool -HasObjectValueOf(JSObject* obj, JSContext* cx) +HasNoToPrimitiveMethodPure(JSObject* obj, JSContext* cx) { - if (obj->is<ProxyObject>() || !obj->isNative()) + jsid id = SYMBOL_TO_JSID(cx->wellKnownSymbols().toPrimitive); + JSObject* pobj; + Shape* shape; + if (!LookupPropertyPure(cx, obj, id, &pobj, &shape)) return false; - jsid valueOf = NameToId(cx->names().valueOf); - - Value v; - while (!HasDataProperty(cx, &obj->as<NativeObject>(), valueOf, &v)) { - obj = obj->staticPrototype(); - if (!obj || obj->is<ProxyObject>() || !obj->isNative()) - return false; - } - - return IsNativeFunction(v, obj_valueOf); + return !shape; } /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */ diff --git a/js/src/json.cpp b/js/src/json.cpp index 425a2f1179..08382b97bd 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -971,9 +971,9 @@ static const JSFunctionSpec json_static_methods[] = { JSObject* js::InitJSONClass(JSContext* cx, HandleObject obj) { - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); - RootedObject proto(cx, global->getOrCreateObjectPrototype(cx)); + RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); if (!proto) return nullptr; RootedObject JSON(cx, NewObjectWithGivenProto(cx, &JSONClass, proto, SingletonObject)); diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 6adb5401ea..d1ae3cd5ef 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -132,7 +132,8 @@ js::StackUses(JSScript* script, jsbytecode* pc) return 2 + GET_ARGC(pc) + 1; default: /* stack: fun, this, [argc arguments] */ - MOZ_ASSERT(op == JSOP_CALL || op == JSOP_EVAL || op == JSOP_CALLITER || + MOZ_ASSERT(op == JSOP_CALL || op == JSOP_CALL_IGNORES_RV || op == JSOP_EVAL || + op == JSOP_CALLITER || op == JSOP_STRICTEVAL || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY); return 2 + GET_ARGC(pc); } @@ -1363,6 +1364,7 @@ ExpressionDecompiler::decompilePC(jsbytecode* pc) case JSOP_NEWTARGET: return write("new.target"); case JSOP_CALL: + case JSOP_CALL_IGNORES_RV: case JSOP_CALLITER: case JSOP_FUNCALL: return decompilePCForStackOperand(pc, -int32_t(GET_ARGC(pc) + 2)) && @@ -1662,7 +1664,7 @@ DecompileArgumentFromStack(JSContext* cx, int formalIndex, char** res) /* Don't handle getters, setters or calls from fun.call/fun.apply. */ JSOp op = JSOp(*current); - if (op != JSOP_CALL && op != JSOP_NEW) + if (op != JSOP_CALL && op != JSOP_CALL_IGNORES_RV && op != JSOP_NEW) return true; if (static_cast<unsigned>(formalIndex) >= GET_ARGC(current)) @@ -1725,6 +1727,8 @@ js::CallResultEscapes(jsbytecode* pc) if (*pc == JSOP_CALL) pc += JSOP_CALL_LENGTH; + else if (*pc == JSOP_CALL_IGNORES_RV) + pc += JSOP_CALL_IGNORES_RV_LENGTH; else if (*pc == JSOP_SPREADCALL) pc += JSOP_SPREADCALL_LENGTH; else @@ -2214,6 +2218,7 @@ GenerateLcovInfo(JSContext* cx, JSCompartment* comp, GenericPrinter& out) return false; RootedScript script(cx); + RootedFunction fun(cx); do { script = queue.popCopy(); compCover.collectCodeCoverageInfo(comp, script->sourceObject(), script); @@ -2231,15 +2236,15 @@ GenerateLcovInfo(JSContext* cx, JSCompartment* comp, GenericPrinter& out) // Only continue on JSFunction objects. if (!obj->is<JSFunction>()) continue; - JSFunction& fun = obj->as<JSFunction>(); + fun = &obj->as<JSFunction>(); // Let's skip wasm for now. - if (!fun.isInterpreted()) + if (!fun->isInterpreted()) continue; // Queue the script in the list of script associated to the // current source. - JSScript* childScript = fun.getOrCreateScript(cx); + JSScript* childScript = JSFunction::getOrCreateScript(cx, fun); if (!childScript || !queue.append(childScript)) return false; } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 9f914943ec..fc7438e3b9 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -235,6 +235,8 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri { uint32_t begin = script->sourceStart(); uint32_t end = script->sourceEnd(); + uint32_t toStringStart = script->toStringStart(); + uint32_t toStringEnd = script->toStringEnd(); uint32_t lineno = script->lineno(); uint32_t column = script->column(); @@ -242,6 +244,8 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri packedFields = lazy->packedFields(); MOZ_ASSERT(begin == lazy->begin()); MOZ_ASSERT(end == lazy->end()); + MOZ_ASSERT(toStringStart == lazy->toStringStart()); + MOZ_ASSERT(toStringEnd == lazy->toStringEnd()); MOZ_ASSERT(lineno == lazy->lineno()); MOZ_ASSERT(column == lazy->column()); // We can assert we have no inner functions because we don't @@ -255,7 +259,12 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri if (mode == XDR_DECODE) { lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, script, - packedFields, begin, end, lineno, column)); + packedFields, begin, end, toStringStart, lineno, column)); + + if (!lazy) + return false; + + lazy->setToStringEnd(toStringEnd); // As opposed to XDRLazyScript, we need to restore the runtime bits // of the script, as we are trying to match the fact this function @@ -317,6 +326,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip IsStarGenerator, IsAsync, HasRest, + IsExprBody, OwnSource, ExplicitUseStrict, SelfHosted, @@ -434,6 +444,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip scriptBits |= (1 << IsAsync); if (script->hasRest()) scriptBits |= (1 << HasRest); + if (script->isExprBody()) + scriptBits |= (1 << IsExprBody); if (script->hasSingletons()) scriptBits |= (1 << HasSingleton); if (script->treatAsRunOnce()) @@ -517,7 +529,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>(); } - script = JSScript::Create(cx, options, sourceObject, 0, 0); + script = JSScript::Create(cx, options, sourceObject, 0, 0, 0, 0); if (!script) return false; @@ -587,6 +599,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip script->setAsyncKind(AsyncFunction); if (scriptBits & (1 << HasRest)) script->setHasRest(); + if (scriptBits & (1 << IsExprBody)) + script->setIsExprBody(); } JS_STATIC_ASSERT(sizeof(jsbytecode) == 1); @@ -600,6 +614,10 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip return false; if (!xdr->codeUint32(&script->sourceEnd_)) return false; + if (!xdr->codeUint32(&script->toStringStart_)) + return false; + if (!xdr->codeUint32(&script->toStringEnd_)) + return false; if (!xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) || @@ -930,6 +948,8 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript { uint32_t begin; uint32_t end; + uint32_t toStringStart; + uint32_t toStringEnd; uint32_t lineno; uint32_t column; uint64_t packedFields; @@ -943,12 +963,16 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript begin = lazy->begin(); end = lazy->end(); + toStringStart = lazy->toStringStart(); + toStringEnd = lazy->toStringEnd(); lineno = lazy->lineno(); column = lazy->column(); packedFields = lazy->packedFields(); } if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) || + !xdr->codeUint32(&toStringStart) || + !xdr->codeUint32(&toStringEnd) || !xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) || !xdr->codeUint64(&packedFields)) { @@ -957,9 +981,10 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript if (mode == XDR_DECODE) { lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, enclosingScript, - packedFields, begin, end, lineno, column)); + packedFields, begin, end, toStringStart, lineno, column)); if (!lazy) return false; + lazy->setToStringEnd(toStringEnd); fun->initLazyScript(lazy); } } @@ -1003,6 +1028,15 @@ JSScript::setSourceObject(JSObject* object) sourceObject_ = object; } +void +JSScript::setDefaultClassConstructorSpan(JSObject* sourceObject, uint32_t start, uint32_t end) +{ + MOZ_ASSERT(isDefaultClassConstructor()); + setSourceObject(sourceObject); + toStringStart_ = start; + toStringEnd_ = end; +} + js::ScriptSourceObject& JSScript::scriptSourceUnwrap() const { return UncheckedUnwrap(sourceObject())->as<ScriptSourceObject>(); @@ -1423,11 +1457,18 @@ JSScript::loadSource(JSContext* cx, ScriptSource* ss, bool* worked) return true; } -JSFlatString* -JSScript::sourceData(JSContext* cx) +/* static */ JSFlatString* +JSScript::sourceData(JSContext* cx, HandleScript script) +{ + MOZ_ASSERT(script->scriptSource()->hasSourceData()); + return script->scriptSource()->substring(cx, script->sourceStart(), script->sourceEnd()); +} + +/* static */ JSFlatString* +JSScript::sourceDataForToString(JSContext* cx, HandleScript script) { - MOZ_ASSERT(scriptSource()->hasSourceData()); - return scriptSource()->substring(cx, sourceStart(), sourceEnd()); + MOZ_ASSERT(script->scriptSource()->hasSourceData()); + return script->scriptSource()->substring(cx, script->toStringStart(), script->toStringEnd()); } UncompressedSourceCache::AutoHoldEntry::AutoHoldEntry() @@ -2428,9 +2469,16 @@ JSScript::initCompartment(ExclusiveContext* cx) /* static */ JSScript* JSScript::Create(ExclusiveContext* cx, const ReadOnlyCompileOptions& options, - HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd) + HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd, + uint32_t toStringStart, uint32_t toStringEnd) { + // bufStart and bufEnd specify the range of characters parsed by the + // Parser to produce this script. toStringStart and toStringEnd specify + // the range of characters to be returned for Function.prototype.toString. MOZ_ASSERT(bufStart <= bufEnd); + MOZ_ASSERT(toStringStart <= toStringEnd); + MOZ_ASSERT(toStringStart <= bufStart); + MOZ_ASSERT(toStringEnd >= bufEnd); RootedScript script(cx, Allocate<JSScript>(cx)); if (!script) @@ -2450,6 +2498,8 @@ JSScript::Create(ExclusiveContext* cx, const ReadOnlyCompileOptions& options, script->setSourceObject(sourceObject); script->sourceStart_ = bufStart; script->sourceEnd_ = bufEnd; + script->toStringStart_ = toStringStart; + script->toStringEnd_ = toStringEnd; return script; } @@ -2644,6 +2694,8 @@ JSScript::initFromFunctionBox(ExclusiveContext* cx, HandleScript script, script->setAsyncKind(funbox->asyncKind()); if (funbox->hasRest()) script->setHasRest(); + if (funbox->isExprBody()) + script->setIsExprBody(); PositionalFormalParameterIter fi(script); while (fi && !fi.closedOver()) @@ -3245,7 +3297,7 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst, } else { if (innerFun->isInterpretedLazy()) { AutoCompartment ac(cx, innerFun); - if (!innerFun->getOrCreateScript(cx)) + if (!JSFunction::getOrCreateScript(cx, innerFun)) return false; } @@ -3304,6 +3356,7 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst, dst->isDefaultClassConstructor_ = src->isDefaultClassConstructor(); dst->isAsync_ = src->asyncKind() == AsyncFunction; dst->hasRest_ = src->hasRest_; + dst->isExprBody_ = src->isExprBody_; if (nconsts != 0) { GCPtrValue* vector = Rebase<GCPtrValue>(dst, src, src->consts()->vector); @@ -3382,7 +3435,8 @@ CreateEmptyScriptForClone(JSContext* cx, HandleScript src) .setNoScriptRval(src->noScriptRval()) .setVersion(src->getVersion()); - return JSScript::Create(cx, options, sourceObject, src->sourceStart(), src->sourceEnd()); + return JSScript::Create(cx, options, sourceObject, src->sourceStart(), src->sourceEnd(), + src->toStringStart(), src->toStringEnd()); } JSScript* @@ -3932,7 +3986,8 @@ JSScript::formalLivesInArgumentsObject(unsigned argSlot) } LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields, - uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) + uint32_t begin, uint32_t end, + uint32_t toStringStart, uint32_t lineno, uint32_t column) : script_(nullptr), function_(fun), enclosingScope_(nullptr), @@ -3941,10 +3996,13 @@ LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields, packedFields_(packedFields), begin_(begin), end_(end), + toStringStart_(toStringStart), + toStringEnd_(end), lineno_(lineno), column_(column) { MOZ_ASSERT(begin <= end); + MOZ_ASSERT(toStringStart <= begin); } void @@ -3990,7 +4048,7 @@ LazyScript::maybeForwardedScriptSource() const /* static */ LazyScript* LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun, uint64_t packedFields, uint32_t begin, uint32_t end, - uint32_t lineno, uint32_t column) + uint32_t toStringStart, uint32_t lineno, uint32_t column) { union { PackedView p; @@ -4018,7 +4076,8 @@ LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun, cx->compartment()->scheduleDelazificationForDebugger(); - return new (res) LazyScript(fun, table.forget(), packed, begin, end, lineno, column); + return new (res) LazyScript(fun, table.forget(), packed, begin, end, + toStringStart, lineno, column); } /* static */ LazyScript* @@ -4026,7 +4085,8 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun, const frontend::AtomVector& closedOverBindings, Handle<GCVector<JSFunction*, 8>> innerFunctions, JSVersion version, - uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) + uint32_t begin, uint32_t end, + uint32_t toStringStart, uint32_t lineno, uint32_t column) { union { PackedView p; @@ -4038,6 +4098,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun, p.hasThisBinding = false; p.isAsync = false; p.hasRest = false; + p.isExprBody = false; p.numClosedOverBindings = closedOverBindings.length(); p.numInnerFunctions = innerFunctions.length(); p.generatorKindBits = GeneratorKindAsBits(NotGenerator); @@ -4049,7 +4110,8 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun, p.isDerivedClassConstructor = false; p.needsHomeObject = false; - LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column); + LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, toStringStart, + lineno, column); if (!res) return nullptr; @@ -4070,7 +4132,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun, HandleScript script, HandleScope enclosingScope, HandleScript enclosingScript, uint64_t packedFields, uint32_t begin, uint32_t end, - uint32_t lineno, uint32_t column) + uint32_t toStringStart, uint32_t lineno, uint32_t column) { // Dummy atom which is not a valid property name. RootedAtom dummyAtom(cx, cx->names().comma); @@ -4079,7 +4141,8 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun, // holding this lazy script. HandleFunction dummyFun = fun; - LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column); + LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, toStringStart, + lineno, column); if (!res) return nullptr; @@ -4264,7 +4327,7 @@ JSScript::AutoDelazify::holdScript(JS::HandleFunction fun) script_ = fun->nonLazyScript(); } else { JSAutoCompartment ac(cx_, fun); - script_ = fun->getOrCreateScript(cx_); + script_ = JSFunction::getOrCreateScript(cx_, fun); if (script_) { oldDoNotRelazify_ = script_->doNotRelazify_; script_->setDoNotRelazify(true); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 87da79901d..85eb2938d3 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -575,10 +575,6 @@ class ScriptSource introductionOffset_ = offset; hasIntroductionOffset_ = true; } - - uint32_t parameterListEnd() const { - return parameterListEnd_; - } }; class ScriptSourceHolder @@ -857,9 +853,36 @@ class JSScript : public js::gc::TenuredCell uint32_t bodyScopeIndex_; /* index into the scopes array of the body scope */ - /* Range of characters in scriptSource which contains this script's source. */ + // Range of characters in scriptSource which contains this script's + // source, that is, the range used by the Parser to produce this script. + // + // Most scripted functions have sourceStart_ == toStringStart_ and + // sourceEnd_ == toStringEnd_. However, for functions with extra + // qualifiers (e.g. generators, async) and for class constructors (which + // need to return the entire class source), their values differ. + // + // Each field points the following locations. + // + // function * f(a, b) { return a + b; } + // ^ ^ ^ + // | | | + // | sourceStart_ sourceEnd_ + // | | + // toStringStart_ toStringEnd_ + // + // And, in the case of class constructors, an additional toStringEnd + // offset is used. + // + // class C { constructor() { this.field = 42; } } + // ^ ^ ^ ^ + // | | | `---------` + // | sourceStart_ sourceEnd_ | + // | | + // toStringStart_ toStringEnd_ uint32_t sourceStart_; uint32_t sourceEnd_; + uint32_t toStringStart_; + uint32_t toStringEnd_; // Number of times the script has been called or has had backedges taken. // When running in ion, also increased for any inlined scripts. Reset if @@ -1015,6 +1038,7 @@ class JSScript : public js::gc::TenuredCell bool isAsync_:1; bool hasRest_:1; + bool isExprBody_:1; // Add padding so JSScript is gc::Cell aligned. Make padding protected // instead of private to suppress -Wunused-private-field compiler warnings. @@ -1030,8 +1054,9 @@ class JSScript : public js::gc::TenuredCell public: static JSScript* Create(js::ExclusiveContext* cx, const JS::ReadOnlyCompileOptions& options, - js::HandleObject sourceObject, uint32_t sourceStart, - uint32_t sourceEnd); + js::HandleObject sourceObject, + uint32_t sourceStart, uint32_t sourceEnd, + uint32_t toStringStart, uint32_t toStringEnd); void initCompartment(js::ExclusiveContext* cx); @@ -1178,6 +1203,14 @@ class JSScript : public js::gc::TenuredCell return sourceEnd_; } + uint32_t toStringStart() const { + return toStringStart_; + } + + uint32_t toStringEnd() const { + return toStringEnd_; + } + bool noScriptRval() const { return noScriptRval_; } @@ -1319,6 +1352,13 @@ class JSScript : public js::gc::TenuredCell hasRest_ = true; } + bool isExprBody() const { + return isExprBody_; + } + void setIsExprBody() { + isExprBody_ = true; + } + void setNeedsHomeObject() { needsHomeObject_ = true; } @@ -1454,6 +1494,7 @@ class JSScript : public js::gc::TenuredCell bool isRelazifiable() const { return (selfHosted() || lazyScript) && !hasInnerFunctions_ && !types_ && !isGenerator() && !hasBaselineScript() && !hasAnyIonScript() && + !isDefaultClassConstructor() && !doNotRelazify_; } void setLazyScript(js::LazyScript* lazy) { @@ -1481,7 +1522,7 @@ class JSScript : public js::gc::TenuredCell * De-lazifies the canonical function. Must be called before entering code * that expects the function to be non-lazy. */ - inline void ensureNonLazyCanonicalFunction(JSContext* cx); + inline void ensureNonLazyCanonicalFunction(); js::ModuleObject* module() const { if (bodyScope()->is<js::ModuleScope>()) @@ -1500,8 +1541,9 @@ class JSScript : public js::gc::TenuredCell // directly, via lazy arguments or a rest parameter. bool mayReadFrameArgsDirectly(); - JSFlatString* sourceData(JSContext* cx); - + static JSFlatString* sourceData(JSContext* cx, JS::HandleScript script); + static JSFlatString* sourceDataForToString(JSContext* cx, JS::HandleScript script); + static bool loadSource(JSContext* cx, js::ScriptSource* ss, bool* worked); void setSourceObject(JSObject* object); @@ -1515,6 +1557,8 @@ class JSScript : public js::gc::TenuredCell const char* filename() const { return scriptSource()->filename(); } const char* maybeForwardedFilename() const { return maybeForwardedScriptSource()->filename(); } + void setDefaultClassConstructorSpan(JSObject* sourceObject, uint32_t start, uint32_t end); + public: /* Return whether this script was compiled for 'eval' */ @@ -1924,7 +1968,7 @@ class LazyScript : public gc::TenuredCell #endif private: - static const uint32_t NumClosedOverBindingsBits = 21; + static const uint32_t NumClosedOverBindingsBits = 20; static const uint32_t NumInnerFunctionsBits = 20; struct PackedView { @@ -1934,7 +1978,12 @@ class LazyScript : public gc::TenuredCell uint32_t shouldDeclareArguments : 1; uint32_t hasThisBinding : 1; uint32_t isAsync : 1; + uint32_t isExprBody : 1; + uint32_t numClosedOverBindings : NumClosedOverBindingsBits; + + // -- 32bit boundary -- + uint32_t numInnerFunctions : NumInnerFunctionsBits; uint32_t generatorKindBits : 2; @@ -1960,20 +2009,26 @@ class LazyScript : public gc::TenuredCell }; // Source location for the script. + // See the comment in JSScript for the details. uint32_t begin_; uint32_t end_; + uint32_t toStringStart_; + uint32_t toStringEnd_; + // Line and column of |begin_| position, that is the position where we + // start parsing. uint32_t lineno_; uint32_t column_; LazyScript(JSFunction* fun, void* table, uint64_t packedFields, - uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column); + uint32_t begin, uint32_t end, uint32_t toStringStart, + uint32_t lineno, uint32_t column); // Create a LazyScript without initializing the closedOverBindings and the // innerFunctions. To be GC-safe, the caller must initialize both vectors // with valid atoms and functions. static LazyScript* CreateRaw(ExclusiveContext* cx, HandleFunction fun, uint64_t packedData, uint32_t begin, uint32_t end, - uint32_t lineno, uint32_t column); + uint32_t toStringStart, uint32_t lineno, uint32_t column); public: static const uint32_t NumClosedOverBindingsLimit = 1 << NumClosedOverBindingsBits; @@ -1985,7 +2040,7 @@ class LazyScript : public gc::TenuredCell const frontend::AtomVector& closedOverBindings, Handle<GCVector<JSFunction*, 8>> innerFunctions, JSVersion version, uint32_t begin, uint32_t end, - uint32_t lineno, uint32_t column); + uint32_t toStringStart, uint32_t lineno, uint32_t column); // Create a LazyScript and initialize the closedOverBindings and the // innerFunctions with dummy values to be replaced in a later initialization @@ -2000,11 +2055,11 @@ class LazyScript : public gc::TenuredCell HandleScript script, HandleScope enclosingScope, HandleScript enclosingScript, uint64_t packedData, uint32_t begin, uint32_t end, - uint32_t lineno, uint32_t column); + uint32_t toStringStart, uint32_t lineno, uint32_t column); void initRuntimeFields(uint64_t packedFields); - inline JSFunction* functionDelazifying(JSContext* cx) const; + static inline JSFunction* functionDelazifying(JSContext* cx, Handle<LazyScript*>); JSFunction* functionNonDelazifying() const { return function_; } @@ -2087,6 +2142,13 @@ class LazyScript : public gc::TenuredCell p_.hasRest = true; } + bool isExprBody() const { + return p_.isExprBody; + } + void setIsExprBody() { + p_.isExprBody = true; + } + bool strict() const { return p_.strict; } @@ -2173,6 +2235,12 @@ class LazyScript : public gc::TenuredCell uint32_t end() const { return end_; } + uint32_t toStringStart() const { + return toStringStart_; + } + uint32_t toStringEnd() const { + return toStringEnd_; + } uint32_t lineno() const { return lineno_; } @@ -2180,6 +2248,12 @@ class LazyScript : public gc::TenuredCell return column_; } + void setToStringEnd(uint32_t toStringEnd) { + MOZ_ASSERT(toStringStart_ <= toStringEnd); + MOZ_ASSERT(toStringEnd_ >= end_); + toStringEnd_ = toStringEnd; + } + bool hasUncompiledEnclosingScript() const; friend class GCMarker; @@ -2199,7 +2273,8 @@ class LazyScript : public gc::TenuredCell }; /* If this fails, add/remove padding within LazyScript. */ -JS_STATIC_ASSERT(sizeof(LazyScript) % js::gc::CellSize == 0); +static_assert(sizeof(LazyScript) % js::gc::CellSize == 0, + "Size of LazyScript must be an integral multiple of js::gc::CellSize"); struct ScriptAndCounts { diff --git a/js/src/jsscriptinlines.h b/js/src/jsscriptinlines.h index da23804aca..e1052111bb 100644 --- a/js/src/jsscriptinlines.h +++ b/js/src/jsscriptinlines.h @@ -74,13 +74,13 @@ void SetFrameArgumentsObject(JSContext* cx, AbstractFramePtr frame, HandleScript script, JSObject* argsobj); -inline JSFunction* -LazyScript::functionDelazifying(JSContext* cx) const +/* static */ inline JSFunction* +LazyScript::functionDelazifying(JSContext* cx, Handle<LazyScript*> script) { - Rooted<const LazyScript*> self(cx, this); - if (self->function_ && !self->function_->getOrCreateScript(cx)) + RootedFunction fun(cx, script->function_); + if (script->function_ && !JSFunction::getOrCreateScript(cx, fun)) return nullptr; - return self->function_; + return script->function_; } } // namespace js @@ -100,7 +100,7 @@ JSScript::functionDelazifying() const } inline void -JSScript::ensureNonLazyCanonicalFunction(JSContext* cx) +JSScript::ensureNonLazyCanonicalFunction() { // Infallibly delazify the canonical script. JSFunction* fun = function(); diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 4151d012b6..74f61b87dc 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -461,9 +461,13 @@ ToStringForStringFunction(JSContext* cx, HandleValue thisv) RootedObject obj(cx, &thisv.toObject()); if (obj->is<StringObject>()) { StringObject* nobj = &obj->as<StringObject>(); - Rooted<jsid> id(cx, NameToId(cx->names().toString)); - if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString)) + // We have to make sure that the ToPrimitive call from ToString + // would be unobservable. + if (HasNoToPrimitiveMethodPure(nobj, cx) && + HasNativeMethodPure(nobj, cx->names().toString, str_toString, cx)) + { return nobj->unbox(); + } } } else if (thisv.isNullOrUndefined()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO, @@ -1970,14 +1974,14 @@ js::str_trim(JSContext* cx, unsigned argc, Value* vp) } bool -js::str_trimLeft(JSContext* cx, unsigned argc, Value* vp) +js::str_trimStart(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); return TrimString(cx, args, true, false); } bool -js::str_trimRight(JSContext* cx, unsigned argc, Value* vp) +js::str_trimEnd(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); return TrimString(cx, args, false, true); @@ -2369,7 +2373,7 @@ js::str_replace_string_raw(JSContext* cx, HandleString string, HandleString patt } // ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18. -static JSObject* +static ArrayObject* SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleLinearString sep, HandleObjectGroup group) { @@ -2466,7 +2470,7 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleLinearS } // Fast-path for splitting a string into a character array via split(""). -static JSObject* +static ArrayObject* CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleObjectGroup group) { size_t strLength = str->length(); @@ -2491,7 +2495,7 @@ CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleObj } // ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18. -JSObject* +ArrayObject* js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep, uint32_t limit) { @@ -2568,8 +2572,10 @@ static const JSFunctionSpec string_methods[] = { JS_FN("startsWith", str_startsWith, 1,0), JS_FN("endsWith", str_endsWith, 1,0), JS_FN("trim", str_trim, 0,0), - JS_FN("trimLeft", str_trimLeft, 0,0), - JS_FN("trimRight", str_trimRight, 0,0), + JS_FN("trimLeft", str_trimStart, 0,0), + JS_FN("trimStart", str_trimStart, 0,0), + JS_FN("trimRight", str_trimEnd, 0,0), + JS_FN("trimEnd", str_trimEnd, 0,0), JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,0), JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,0), JS_SELF_HOSTED_FN("localeCompare", "String_localeCompare", 1,0), @@ -2881,8 +2887,10 @@ static const JSFunctionSpec string_static_methods[] = { JS_SELF_HOSTED_FN("startsWith", "String_static_startsWith", 2,0), JS_SELF_HOSTED_FN("endsWith", "String_static_endsWith", 2,0), JS_SELF_HOSTED_FN("trim", "String_static_trim", 1,0), - JS_SELF_HOSTED_FN("trimLeft", "String_static_trimLeft", 1,0), - JS_SELF_HOSTED_FN("trimRight", "String_static_trimRight", 1,0), + JS_SELF_HOSTED_FN("trimLeft", "String_static_trimStart", 1,0), + JS_SELF_HOSTED_FN("trimStart", "String_static_trimStart", 1,0), + JS_SELF_HOSTED_FN("trimRight", "String_static_trimEnd", 1,0), + JS_SELF_HOSTED_FN("trimEnd", "String_static_trimEnd", 1,0), JS_SELF_HOSTED_FN("toLocaleLowerCase","String_static_toLocaleLowerCase",1,0), JS_SELF_HOSTED_FN("toLocaleUpperCase","String_static_toLocaleUpperCase",1,0), JS_SELF_HOSTED_FN("normalize", "String_static_normalize", 1,0), @@ -2897,8 +2905,8 @@ StringObject::assignInitialShape(ExclusiveContext* cx, Handle<StringObject*> obj { MOZ_ASSERT(obj->empty()); - return obj->addDataProperty(cx, cx->names().length, LENGTH_SLOT, - JSPROP_PERMANENT | JSPROP_READONLY); + return NativeObject::addDataProperty(cx, obj, cx->names().length, LENGTH_SLOT, + JSPROP_PERMANENT | JSPROP_READONLY); } JSObject* @@ -2906,17 +2914,20 @@ js::InitStringClass(JSContext* cx, HandleObject obj) { MOZ_ASSERT(obj->isNative()); - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); Rooted<JSString*> empty(cx, cx->runtime()->emptyString); - RootedObject proto(cx, global->createBlankPrototype(cx, &StringObject::class_)); - if (!proto || !proto->as<StringObject>().init(cx, empty)) + RootedObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &StringObject::class_)); + if (!proto) + return nullptr; + Handle<StringObject*> protoObj = proto.as<StringObject>(); + if (!StringObject::init(cx, protoObj, empty)) return nullptr; /* Now create the String function. */ RootedFunction ctor(cx); - ctor = global->createConstructor(cx, StringConstructor, cx->names().String, 1, - AllocKind::FUNCTION, &jit::JitInfo_String); + ctor = GlobalObject::createConstructor(cx, StringConstructor, cx->names().String, 1, + AllocKind::FUNCTION, &jit::JitInfo_String); if (!ctor) return nullptr; @@ -3070,8 +3081,11 @@ js::ValueToSource(JSContext* cx, HandleValue v) return ToString<CanGC>(cx, v); } - +#if JS_HAS_TOSOURCE return ObjectToSource(cx, obj); +#else + return ToString<CanGC>(cx, v); +#endif } JSString* diff --git a/js/src/jsstr.h b/js/src/jsstr.h index 118118839c..68175c8268 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -367,10 +367,10 @@ extern bool str_trim(JSContext* cx, unsigned argc, Value* vp); extern bool -str_trimLeft(JSContext* cx, unsigned argc, Value* vp); +str_trimStart(JSContext* cx, unsigned argc, Value* vp); extern bool -str_trimRight(JSContext* cx, unsigned argc, Value* vp); +str_trimEnd(JSContext* cx, unsigned argc, Value* vp); extern bool str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp); @@ -465,7 +465,7 @@ FileEscapedString(FILE* fp, const char* chars, size_t length, uint32_t quote) return res; } -JSObject* +ArrayObject* str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep, uint32_t limit); diff --git a/js/src/jstypes.h b/js/src/jstypes.h index 75774e5b82..04ffbe00d3 100644 --- a/js/src/jstypes.h +++ b/js/src/jstypes.h @@ -147,13 +147,7 @@ # define JS_64BIT # endif #elif defined(__GNUC__) -/* Additional GCC defines are when running on Solaris, AIX, and HPUX */ -# if defined(__x86_64__) || defined(__sparcv9) || \ - defined(__64BIT__) || defined(__LP64__) -# define JS_64BIT -# endif -#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) /* Sun Studio C/C++ */ -# if defined(__x86_64) || defined(__sparcv9) +# if defined(__x86_64__) || defined(__LP64__) # define JS_64BIT # endif #elif defined(__xlc__) || defined(__xlC__) /* IBM XL C/C++ */ diff --git a/js/src/jsversion.h b/js/src/jsversion.h index 8bdfe47b64..cf4c6e73a2 100644 --- a/js/src/jsversion.h +++ b/js/src/jsversion.h @@ -12,7 +12,6 @@ */ #define JS_HAS_STR_HTML_HELPERS 1 /* (no longer used) */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ -#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ #define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ #define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ diff --git a/js/src/jswatchpoint.cpp b/js/src/jswatchpoint.cpp deleted file mode 100644 index 3cf43e2197..0000000000 --- a/js/src/jswatchpoint.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "jswatchpoint.h" - -#include "jsatom.h" -#include "jscompartment.h" -#include "jsfriendapi.h" - -#include "gc/Marking.h" -#include "vm/Shape.h" - -#include "jsgcinlines.h" - -using namespace js; -using namespace js::gc; - -inline HashNumber -WatchKeyHasher::hash(const Lookup& key) -{ - return MovableCellHasher<PreBarrieredObject>::hash(key.object) ^ HashId(key.id); -} - -namespace { - -class AutoEntryHolder { - typedef WatchpointMap::Map Map; - Generation gen; - Map& map; - Map::Ptr p; - RootedObject obj; - RootedId id; - - public: - AutoEntryHolder(JSContext* cx, Map& map, Map::Ptr p) - : gen(map.generation()), map(map), p(p), obj(cx, p->key().object), id(cx, p->key().id) - { - MOZ_ASSERT(!p->value().held); - p->value().held = true; - } - - ~AutoEntryHolder() { - if (gen != map.generation()) - p = map.lookup(WatchKey(obj, id)); - if (p) - p->value().held = false; - } -}; - -} /* anonymous namespace */ - -bool -WatchpointMap::init() -{ - return map.init(); -} - -bool -WatchpointMap::watch(JSContext* cx, HandleObject obj, HandleId id, - JSWatchPointHandler handler, HandleObject closure) -{ - MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id) || JSID_IS_SYMBOL(id)); - - if (!obj->setWatched(cx)) - return false; - - Watchpoint w(handler, closure, false); - if (!map.put(WatchKey(obj, id), w)) { - ReportOutOfMemory(cx); - return false; - } - /* - * For generational GC, we don't need to post-barrier writes to the - * hashtable here because we mark all watchpoints as part of root marking in - * markAll(). - */ - return true; -} - -void -WatchpointMap::unwatch(JSObject* obj, jsid id, - JSWatchPointHandler* handlerp, JSObject** closurep) -{ - if (Map::Ptr p = map.lookup(WatchKey(obj, id))) { - if (handlerp) - *handlerp = p->value().handler; - if (closurep) { - // Read barrier to prevent an incorrectly gray closure from escaping the - // watchpoint. See the comment before UnmarkGrayChildren in gc/Marking.cpp - JS::ExposeObjectToActiveJS(p->value().closure); - *closurep = p->value().closure; - } - map.remove(p); - } -} - -void -WatchpointMap::unwatchObject(JSObject* obj) -{ - for (Map::Enum e(map); !e.empty(); e.popFront()) { - Map::Entry& entry = e.front(); - if (entry.key().object == obj) - e.removeFront(); - } -} - -void -WatchpointMap::clear() -{ - map.clear(); -} - -bool -WatchpointMap::triggerWatchpoint(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) -{ - Map::Ptr p = map.lookup(WatchKey(obj, id)); - if (!p || p->value().held) - return true; - - AutoEntryHolder holder(cx, map, p); - - /* Copy the entry, since GC would invalidate p. */ - JSWatchPointHandler handler = p->value().handler; - RootedObject closure(cx, p->value().closure); - - /* Determine the property's old value. */ - Value old; - old.setUndefined(); - if (obj->isNative()) { - NativeObject* nobj = &obj->as<NativeObject>(); - if (Shape* shape = nobj->lookup(cx, id)) { - if (shape->hasSlot()) - old = nobj->getSlot(shape->slot()); - } - } - - // Read barrier to prevent an incorrectly gray closure from escaping the - // watchpoint. See the comment before UnmarkGrayChildren in gc/Marking.cpp - JS::ExposeObjectToActiveJS(closure); - - /* Call the handler. */ - return handler(cx, obj, id, old, vp.address(), closure); -} - -bool -WatchpointMap::markIteratively(JSTracer* trc) -{ - bool marked = false; - for (Map::Enum e(map); !e.empty(); e.popFront()) { - Map::Entry& entry = e.front(); - JSObject* priorKeyObj = entry.key().object; - jsid priorKeyId(entry.key().id.get()); - bool objectIsLive = - IsMarked(trc->runtime(), const_cast<PreBarrieredObject*>(&entry.key().object)); - if (objectIsLive || entry.value().held) { - if (!objectIsLive) { - TraceEdge(trc, const_cast<PreBarrieredObject*>(&entry.key().object), - "held Watchpoint object"); - marked = true; - } - - MOZ_ASSERT(JSID_IS_STRING(priorKeyId) || - JSID_IS_INT(priorKeyId) || - JSID_IS_SYMBOL(priorKeyId)); - TraceEdge(trc, const_cast<PreBarrieredId*>(&entry.key().id), "WatchKey::id"); - - if (entry.value().closure && !IsMarked(trc->runtime(), &entry.value().closure)) { - TraceEdge(trc, &entry.value().closure, "Watchpoint::closure"); - marked = true; - } - - /* We will sweep this entry in sweepAll if !objectIsLive. */ - if (priorKeyObj != entry.key().object || priorKeyId != entry.key().id) - e.rekeyFront(WatchKey(entry.key().object, entry.key().id)); - } - } - return marked; -} - -void -WatchpointMap::markAll(JSTracer* trc) -{ - for (Map::Enum e(map); !e.empty(); e.popFront()) { - Map::Entry& entry = e.front(); - WatchKey key = entry.key(); - WatchKey prior = key; - MOZ_ASSERT(JSID_IS_STRING(prior.id) || JSID_IS_INT(prior.id) || JSID_IS_SYMBOL(prior.id)); - - TraceEdge(trc, const_cast<PreBarrieredObject*>(&key.object), - "held Watchpoint object"); - TraceEdge(trc, const_cast<PreBarrieredId*>(&key.id), "WatchKey::id"); - TraceEdge(trc, &entry.value().closure, "Watchpoint::closure"); - - if (prior.object != key.object || prior.id != key.id) - e.rekeyFront(key); - } -} - -void -WatchpointMap::sweepAll(JSRuntime* rt) -{ - for (GCCompartmentsIter c(rt); !c.done(); c.next()) { - if (WatchpointMap* wpmap = c->watchpointMap) - wpmap->sweep(); - } -} - -void -WatchpointMap::sweep() -{ - for (Map::Enum e(map); !e.empty(); e.popFront()) { - Map::Entry& entry = e.front(); - JSObject* obj(entry.key().object); - if (IsAboutToBeFinalizedUnbarriered(&obj)) { - MOZ_ASSERT(!entry.value().held); - e.removeFront(); - } else if (obj != entry.key().object) { - e.rekeyFront(WatchKey(obj, entry.key().id)); - } - } -} - -void -WatchpointMap::traceAll(WeakMapTracer* trc) -{ - JSRuntime* rt = trc->context; - for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) { - if (WatchpointMap* wpmap = comp->watchpointMap) - wpmap->trace(trc); - } -} - -void -WatchpointMap::trace(WeakMapTracer* trc) -{ - for (Map::Range r = map.all(); !r.empty(); r.popFront()) { - Map::Entry& entry = r.front(); - trc->trace(nullptr, - JS::GCCellPtr(entry.key().object.get()), - JS::GCCellPtr(entry.value().closure.get())); - } -} diff --git a/js/src/jswatchpoint.h b/js/src/jswatchpoint.h deleted file mode 100644 index bba6c38ce7..0000000000 --- a/js/src/jswatchpoint.h +++ /dev/null @@ -1,90 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jswatchpoint_h -#define jswatchpoint_h - -#include "jsalloc.h" - -#include "gc/Barrier.h" -#include "js/HashTable.h" - -namespace js { - -struct WeakMapTracer; - -struct WatchKey { - WatchKey() {} - WatchKey(JSObject* obj, jsid id) : object(obj), id(id) {} - WatchKey(const WatchKey& key) : object(key.object.get()), id(key.id.get()) {} - - // These are traced unconditionally during minor GC, so do not require - // post-barriers. - PreBarrieredObject object; - PreBarrieredId id; - - bool operator!=(const WatchKey& other) const { - return object != other.object || id != other.id; - } -}; - -typedef bool -(* JSWatchPointHandler)(JSContext* cx, JSObject* obj, jsid id, const JS::Value& old, - JS::Value* newp, void* closure); - -struct Watchpoint { - JSWatchPointHandler handler; - PreBarrieredObject closure; /* This is always marked in minor GCs and so doesn't require a postbarrier. */ - bool held; /* true if currently running handler */ - Watchpoint(JSWatchPointHandler handler, JSObject* closure, bool held) - : handler(handler), closure(closure), held(held) {} -}; - -struct WatchKeyHasher -{ - typedef WatchKey Lookup; - static inline js::HashNumber hash(const Lookup& key); - - static bool match(const WatchKey& k, const Lookup& l) { - return MovableCellHasher<PreBarrieredObject>::match(k.object, l.object) && - DefaultHasher<PreBarrieredId>::match(k.id, l.id); - } - - static void rekey(WatchKey& k, const WatchKey& newKey) { - k.object.unsafeSet(newKey.object); - k.id.unsafeSet(newKey.id); - } -}; - -class WatchpointMap { - public: - typedef HashMap<WatchKey, Watchpoint, WatchKeyHasher, SystemAllocPolicy> Map; - - bool init(); - bool watch(JSContext* cx, HandleObject obj, HandleId id, - JSWatchPointHandler handler, HandleObject closure); - void unwatch(JSObject* obj, jsid id, - JSWatchPointHandler* handlerp, JSObject** closurep); - void unwatchObject(JSObject* obj); - void clear(); - - bool triggerWatchpoint(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp); - - bool markIteratively(JSTracer* trc); - void markAll(JSTracer* trc); - static void sweepAll(JSRuntime* rt); - void sweep(); - - static void traceAll(WeakMapTracer* trc); - void trace(WeakMapTracer* trc); - - private: - Map map; -}; - -} // namespace js - -#endif /* jswatchpoint_h */ diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 3c73979f88..32336c68b2 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -136,7 +136,7 @@ class JS_FRIEND_API(Wrapper) : public BaseProxyHandler static JSObject* New(JSContext* cx, JSObject* obj, const Wrapper* handler, const WrapperOptions& options = WrapperOptions()); - static JSObject* Renew(JSContext* cx, JSObject* existing, JSObject* obj, const Wrapper* handler); + static JSObject* Renew(JSObject* existing, JSObject* obj, const Wrapper* handler); static const Wrapper* wrapperHandler(JSObject* wrapper); @@ -270,6 +270,8 @@ class JS_FRIEND_API(OpaqueCrossCompartmentWrapper) : public CrossCompartmentWrap virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper, ESClass* cls) const override; virtual bool isArray(JSContext* cx, HandleObject obj, JS::IsArrayAnswer* answer) const override; + virtual bool hasInstance(JSContext* cx, HandleObject wrapper, + MutableHandleValue v, bool* bp) const override; virtual const char* className(JSContext* cx, HandleObject wrapper) const override; virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override; @@ -313,13 +315,6 @@ class JS_FRIEND_API(SecurityWrapper) : public Base virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override; virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override; - // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded - // against. - - virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, - JS::HandleObject callable) const override; - virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const override; - /* * Allow our subclasses to select the superclass behavior they want without * needing to specify an exact superclass. diff --git a/js/src/moz.build b/js/src/moz.build index a3283b5d68..d1e80b4ce8 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -291,7 +291,6 @@ UNIFIED_SOURCES += [ 'jspropertytree.cpp', 'jsscript.cpp', 'jsstr.cpp', - 'jswatchpoint.cpp', 'jsweakmap.cpp', 'perf/jsperf.cpp', 'proxy/BaseProxyHandler.cpp', @@ -355,7 +354,6 @@ UNIFIED_SOURCES += [ 'vm/UbiNode.cpp', 'vm/UbiNodeCensus.cpp', 'vm/UbiNodeShortestPaths.cpp', - 'vm/UnboxedObject.cpp', 'vm/Unicode.cpp', 'vm/Value.cpp', 'vm/WeakMapPtr.cpp', @@ -620,11 +618,11 @@ else: 'perf/pm_stub.cpp' ] -GENERATED_FILES += ['jsautokw.h'] -jsautokw = GENERATED_FILES['jsautokw.h'] -jsautokw.script = 'jsautokw.py' -jsautokw.inputs += [ - 'vm/Keywords.h' +GENERATED_FILES += ['frontend/ReservedWordsGenerated.h'] +ReservedWordsGenerated = GENERATED_FILES['frontend/ReservedWordsGenerated.h'] +ReservedWordsGenerated.script = 'frontend/GenerateReservedWords.py' +ReservedWordsGenerated.inputs += [ + 'frontend/ReservedWords.h' ] # JavaScript must be built shared, even for static builds, as it is used by diff --git a/js/src/old-configure.in b/js/src/old-configure.in index 8abea59564..6566ce05ef 100644 --- a/js/src/old-configure.in +++ b/js/src/old-configure.in @@ -206,10 +206,6 @@ case "$target" in # -Zc:sizedDealloc- disables C++14 global sized deallocation (see bug 1160146) CXXFLAGS="$CXXFLAGS -Zc:sizedDealloc-" - - # Disable C++11 thread-safe statics due to crashes on XP (bug 1204752) - # See https://connect.microsoft.com/VisualStudio/feedback/details/1789709/visual-c-2015-runtime-broken-on-windows-server-2003-c-11-magic-statics - CXXFLAGS="$CXXFLAGS -Zc:threadSafeInit-" ;; esac AC_SUBST(MSVS_VERSION) @@ -927,6 +923,14 @@ case "$target" in fi ;; +i*86-*-solaris*) + MOZ_FIX_LINK_PATHS="-L${DIST}/bin -R'\$\$ORIGIN':/usr/gcc/7/lib" + ;; + +x86_64-*-solaris*) + MOZ_FIX_LINK_PATHS="-L${DIST}/bin -R'\$\$ORIGIN':/usr/gcc/7/lib/amd64" + ;; + esac dnl Only one oddball right now (QNX), but this gives us flexibility diff --git a/js/src/proxy/BaseProxyHandler.cpp b/js/src/proxy/BaseProxyHandler.cpp index 8d5f30a198..423aa8671f 100644 --- a/js/src/proxy/BaseProxyHandler.cpp +++ b/js/src/proxy/BaseProxyHandler.cpp @@ -419,20 +419,6 @@ BaseProxyHandler::setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* } bool -BaseProxyHandler::watch(JSContext* cx, HandleObject proxy, HandleId id, HandleObject callable) const -{ - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_WATCH, - proxy->getClass()->name); - return false; -} - -bool -BaseProxyHandler::unwatch(JSContext* cx, HandleObject proxy, HandleId id) const -{ - return true; -} - -bool BaseProxyHandler::getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end, ElementAdder* adder) const { diff --git a/js/src/proxy/CrossCompartmentWrapper.cpp b/js/src/proxy/CrossCompartmentWrapper.cpp index 2a6cb54002..246639956b 100644 --- a/js/src/proxy/CrossCompartmentWrapper.cpp +++ b/js/src/proxy/CrossCompartmentWrapper.cpp @@ -89,7 +89,7 @@ CrossCompartmentWrapper::getPrototype(JSContext* cx, HandleObject wrapper, if (!GetPrototype(cx, wrapped, protop)) return false; if (protop) { - if (!protop->setDelegate(cx)) + if (!JSObject::setDelegate(cx, protop)) return false; } } @@ -122,7 +122,7 @@ CrossCompartmentWrapper::getPrototypeIfOrdinary(JSContext* cx, HandleObject wrap return true; if (protop) { - if (!protop->setDelegate(cx)) + if (!JSObject::setDelegate(cx, protop)) return false; } } diff --git a/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp b/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp index ff3f4145cc..02bf237ff2 100644 --- a/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp +++ b/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp @@ -175,6 +175,14 @@ OpaqueCrossCompartmentWrapper::isArray(JSContext* cx, HandleObject obj, return true; } +bool OpaqueCrossCompartmentWrapper::hasInstance(JSContext* cx, + HandleObject wrapper, + MutableHandleValue v, + bool* bp) const { + *bp = false; + return true; +} + const char* OpaqueCrossCompartmentWrapper::className(JSContext* cx, HandleObject proxy) const diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index b43fd02d21..6b3a3d1e99 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -505,20 +505,6 @@ Proxy::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp JSObject * const TaggedProto::LazyProto = reinterpret_cast<JSObject*>(0x1); /* static */ bool -Proxy::watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable) -{ - JS_CHECK_RECURSION(cx, return false); - return proxy->as<ProxyObject>().handler()->watch(cx, proxy, id, callable); -} - -/* static */ bool -Proxy::unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) -{ - JS_CHECK_RECURSION(cx, return false); - return proxy->as<ProxyObject>().handler()->unwatch(cx, proxy, id); -} - -/* static */ bool Proxy::getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end, ElementAdder* adder) { @@ -699,18 +685,6 @@ js::proxy_Construct(JSContext* cx, unsigned argc, Value* vp) } bool -js::proxy_Watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable) -{ - return Proxy::watch(cx, obj, id, callable); -} - -bool -js::proxy_Unwatch(JSContext* cx, HandleObject obj, HandleId id) -{ - return Proxy::unwatch(cx, obj, id); -} - -bool js::proxy_GetElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end, ElementAdder* adder) { @@ -750,7 +724,6 @@ const ObjectOps js::ProxyObjectOps = { js::proxy_SetProperty, js::proxy_GetOwnPropertyDescriptor, js::proxy_DeleteProperty, - js::proxy_Watch, js::proxy_Unwatch, js::proxy_GetElements, nullptr, /* enumerate */ js::proxy_FunToString @@ -774,7 +747,7 @@ js::NewProxyObject(JSContext* cx, const BaseProxyHandler* handler, HandleValue p } void -ProxyObject::renew(JSContext* cx, const BaseProxyHandler* handler, const Value& priv) +ProxyObject::renew(const BaseProxyHandler* handler, const Value& priv) { MOZ_ASSERT(!IsInsideNursery(this)); MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this)); @@ -796,9 +769,9 @@ js::InitProxyClass(JSContext* cx, HandleObject obj) JS_FS_END }; - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); RootedFunction ctor(cx); - ctor = global->createConstructor(cx, proxy, cx->names().Proxy, 2); + ctor = GlobalObject::createConstructor(cx, proxy, cx->names().Proxy, 2); if (!ctor) return nullptr; diff --git a/js/src/proxy/Proxy.h b/js/src/proxy/Proxy.h index 89909a085e..4a8ecf8ab4 100644 --- a/js/src/proxy/Proxy.h +++ b/js/src/proxy/Proxy.h @@ -65,9 +65,6 @@ class Proxy static bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g); static bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp); - static bool watch(JSContext* cx, HandleObject proxy, HandleId id, HandleObject callable); - static bool unwatch(JSContext* cx, HandleObject proxy, HandleId id); - static bool getElements(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end, ElementAdder* adder); diff --git a/js/src/proxy/ScriptedProxyHandler.cpp b/js/src/proxy/ScriptedProxyHandler.cpp index 7765473371..0e25f470c0 100644 --- a/js/src/proxy/ScriptedProxyHandler.cpp +++ b/js/src/proxy/ScriptedProxyHandler.cpp @@ -8,8 +8,6 @@ #include "jsapi.h" -#include "vm/Interpreter.h" // For InstanceOfOperator - #include "jsobjinlines.h" #include "vm/NativeObject-inl.h" @@ -1230,7 +1228,7 @@ bool ScriptedProxyHandler::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const { - return InstanceOfOperator(cx, proxy, v, bp); + return InstanceofOperator(cx, proxy, v, bp); } bool diff --git a/js/src/proxy/SecurityWrapper.cpp b/js/src/proxy/SecurityWrapper.cpp index 710faf9b0e..71d22fca67 100644 --- a/js/src/proxy/SecurityWrapper.cpp +++ b/js/src/proxy/SecurityWrapper.cpp @@ -130,24 +130,5 @@ SecurityWrapper<Base>::defineProperty(JSContext* cx, HandleObject wrapper, Handl return Base::defineProperty(cx, wrapper, id, desc, result); } -template <class Base> -bool -SecurityWrapper<Base>::watch(JSContext* cx, HandleObject proxy, - HandleId id, HandleObject callable) const -{ - ReportUnwrapDenied(cx); - return false; -} - -template <class Base> -bool -SecurityWrapper<Base>::unwatch(JSContext* cx, HandleObject proxy, - HandleId id) const -{ - ReportUnwrapDenied(cx); - return false; -} - - template class js::SecurityWrapper<Wrapper>; template class js::SecurityWrapper<CrossCompartmentWrapper>; diff --git a/js/src/proxy/Wrapper.cpp b/js/src/proxy/Wrapper.cpp index 43d559ff30..67f437262f 100644 --- a/js/src/proxy/Wrapper.cpp +++ b/js/src/proxy/Wrapper.cpp @@ -312,9 +312,9 @@ Wrapper::New(JSContext* cx, JSObject* obj, const Wrapper* handler, } JSObject* -Wrapper::Renew(JSContext* cx, JSObject* existing, JSObject* obj, const Wrapper* handler) +Wrapper::Renew(JSObject* existing, JSObject* obj, const Wrapper* handler) { - existing->as<ProxyObject>().renew(cx, handler, ObjectValue(*obj)); + existing->as<ProxyObject>().renew(handler, ObjectValue(*obj)); return existing; } diff --git a/js/src/shell/OSObject.cpp b/js/src/shell/OSObject.cpp index 846ec7b156..4fb3d4e77d 100644 --- a/js/src/shell/OSObject.cpp +++ b/js/src/shell/OSObject.cpp @@ -184,6 +184,11 @@ FileAsTypedArray(JSContext* cx, JS::HandleString pathnameStr) return nullptr; JS_ReportErrorUTF8(cx, "can't seek start of %s", pathname.ptr()); } else { + if (len > INT32_MAX) { + JS_ReportErrorUTF8(cx, "file %s is too large for a Uint8Array", + pathname.ptr()); + return nullptr; + } obj = JS_NewUint8Array(cx, len); if (!obj) return nullptr; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 8d144417a4..51cd11fe83 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -320,7 +320,6 @@ static bool enableIon = false; static bool enableAsmJS = false; static bool enableWasm = false; static bool enableNativeRegExp = false; -static bool enableUnboxedArrays = false; static bool enableSharedMemory = SHARED_MEMORY_DEFAULT; static bool enableWasmAlwaysBaseline = false; static bool enableArrayProtoValues = true; @@ -2311,7 +2310,7 @@ ValueToScript(JSContext* cx, HandleValue v, JSFunction** funp = nullptr) return nullptr; } - JSScript* script = fun->getOrCreateScript(cx); + JSScript* script = JSFunction::getOrCreateScript(cx, fun); if (!script) return nullptr; @@ -2551,6 +2550,14 @@ SrcNotes(JSContext* cx, HandleScript script, Sprinter* sp) return false; break; + case SRC_CLASS_SPAN: { + unsigned startOffset = GetSrcNoteOffset(sn, 0); + unsigned endOffset = GetSrcNoteOffset(sn, 1); + if (!sp->jsprintf(" %u %u", startOffset, endOffset)) + return false; + break; + } + default: MOZ_ASSERT_UNREACHABLE("unrecognized srcnote"); } @@ -2690,7 +2697,7 @@ DisassembleScript(JSContext* cx, HandleScript script, HandleFunction fun, if (sp->put(" CONSTRUCTOR") < 0) return false; } - if (fun->isExprBody()) { + if (script->isExprBody()) { if (sp->put(" EXPRESSION_CLOSURE") < 0) return false; } @@ -2727,7 +2734,7 @@ DisassembleScript(JSContext* cx, HandleScript script, HandleFunction fun, RootedFunction fun(cx, &obj->as<JSFunction>()); if (fun->isInterpreted()) { - RootedScript script(cx, fun->getOrCreateScript(cx)); + RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun)); if (script) { if (!DisassembleScript(cx, script, fun, lines, recursive, sourceNotes, sp)) return false; @@ -3489,8 +3496,8 @@ GroupOf(JSContext* cx, unsigned argc, JS::Value* vp) JS_ReportErrorASCII(cx, "groupOf: object expected"); return false; } - JSObject* obj = &args[0].toObject(); - ObjectGroup* group = obj->getGroup(cx); + RootedObject obj(cx, &args[0].toObject()); + ObjectGroup* group = JSObject::getGroup(cx, obj); if (!group) return false; args.rval().set(JS_NumberValue(double(uintptr_t(group) >> 3))); @@ -5404,7 +5411,7 @@ DumpScopeChain(JSContext* cx, unsigned argc, Value* vp) ReportUsageErrorASCII(cx, callee, "Argument must be an interpreted function"); return false; } - script = fun->getOrCreateScript(cx); + script = JSFunction::getOrCreateScript(cx, fun); } else { script = obj->as<ModuleObject>().script(); } @@ -6420,6 +6427,14 @@ CreateLastWarningObject(JSContext* cx, JSErrorReport* report) if (!DefineProperty(cx, warningObj, cx->names().columnNumber, columnVal)) return false; + RootedObject notesArray(cx, CreateErrorNotesArray(cx, report)); + if (!notesArray) + return false; + + RootedValue notesArrayVal(cx, ObjectValue(*notesArray)); + if (!DefineProperty(cx, warningObj, cx->names().notes, notesArrayVal)) + return false; + GetShellContext(cx)->lastWarning.setObject(*warningObj); return true; } @@ -7260,7 +7275,6 @@ SetContextOptions(JSContext* cx, const OptionParser& op) enableAsmJS = !op.getBoolOption("no-asmjs"); enableWasm = !op.getBoolOption("no-wasm"); enableNativeRegExp = !op.getBoolOption("no-native-regexp"); - enableUnboxedArrays = op.getBoolOption("unboxed-arrays"); enableWasmAlwaysBaseline = op.getBoolOption("wasm-always-baseline"); enableArrayProtoValues = !op.getBoolOption("no-array-proto-values"); @@ -7270,15 +7284,11 @@ SetContextOptions(JSContext* cx, const OptionParser& op) .setWasm(enableWasm) .setWasmAlwaysBaseline(enableWasmAlwaysBaseline) .setNativeRegExp(enableNativeRegExp) - .setUnboxedArrays(enableUnboxedArrays) .setArrayProtoValues(enableArrayProtoValues); if (op.getBoolOption("wasm-check-bce")) jit::JitOptions.wasmAlwaysCheckBounds = true; - if (op.getBoolOption("no-unboxed-objects")) - jit::JitOptions.disableUnboxedObjects = true; - if (const char* str = op.getStringOption("cache-ir-stubs")) { if (strcmp(str, "on") == 0) jit::JitOptions.disableCacheIR = false; @@ -7542,7 +7552,6 @@ SetWorkerContextOptions(JSContext* cx) .setWasm(enableWasm) .setWasmAlwaysBaseline(enableWasmAlwaysBaseline) .setNativeRegExp(enableNativeRegExp) - .setUnboxedArrays(enableUnboxedArrays) .setArrayProtoValues(enableArrayProtoValues); cx->setOffthreadIonCompilationEnabled(offthreadCompilation); cx->profilingScripts = enableCodeCoverage || enableDisassemblyDumps; @@ -7712,8 +7721,6 @@ main(int argc, char** argv, char** envp) || !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation") || !op.addBoolOption('\0', "no-wasm", "Disable WebAssembly compilation") || !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation") - || !op.addBoolOption('\0', "no-unboxed-objects", "Disable creating unboxed plain objects") - || !op.addBoolOption('\0', "unboxed-arrays", "Allow creating unboxed arrays") || !op.addBoolOption('\0', "wasm-always-baseline", "Enable wasm baseline compiler when possible") || !op.addBoolOption('\0', "wasm-check-bce", "Always generate wasm bounds check, even redundant ones.") || !op.addBoolOption('\0', "no-array-proto-values", "Remove Array.prototype.values") diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js index d87abd7be4..8bd0512c53 100644 --- a/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js +++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js @@ -1,7 +1,7 @@ // |reftest| skip-if(!this.hasOwnProperty("Intl")) // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2018e +// tzdata version = 2019c const tzMapper = [ x => x, @@ -67,6 +67,7 @@ const links = { "Cuba": "America/Havana", "Egypt": "Africa/Cairo", "Eire": "Europe/Dublin", + "Etc/UCT": "Etc/UTC", "GB": "Europe/London", "GB-Eire": "Europe/London", "GMT+0": "Etc/GMT", @@ -98,7 +99,7 @@ const links = { "ROK": "Asia/Seoul", "Singapore": "Asia/Singapore", "Turkey": "Europe/Istanbul", - "UCT": "Etc/UCT", + "UCT": "Etc/UTC", "US/Alaska": "America/Anchorage", "US/Aleutian": "America/Adak", "US/Arizona": "America/Phoenix", diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js index b96dac96f8..c760fd85e9 100644 --- a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js +++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js @@ -1,7 +1,7 @@ // |reftest| skip-if(!this.hasOwnProperty("Intl")) // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2018e +// tzdata version = 2019c const tzMapper = [ x => x, diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js index 66ef3075dd..38db5e77dd 100644 --- a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js +++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js @@ -1,7 +1,7 @@ // |reftest| skip-if(!this.hasOwnProperty("Intl")) // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2018e +// tzdata version = 2019c const tzMapper = [ x => x, diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js index 8d44204bcf..64a25c2414 100644 --- a/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js +++ b/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js @@ -1,7 +1,7 @@ // |reftest| skip-if(!this.hasOwnProperty("Intl")) // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2018e +// tzdata version = 2019c const tzMapper = [ x => x, diff --git a/js/src/tests/ecma_2017/AsyncFunctions/await-error.js b/js/src/tests/ecma_2017/AsyncFunctions/await-error.js new file mode 100644 index 0000000000..1f40ea8a0b --- /dev/null +++ b/js/src/tests/ecma_2017/AsyncFunctions/await-error.js @@ -0,0 +1,16 @@ +var BUGNUMBER = 1317153; +var summary = "await outside of async function should provide better error"; + +print(BUGNUMBER + ": " + summary); + +let caught = false; +try { + eval("await 10"); +} catch(e) { + assertEq(e.message, "await is only valid in async functions"); + caught = true; +} +assertEq(caught, true); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_2017/AsyncFunctions/await-in-arrow-parameters.js b/js/src/tests/ecma_2017/AsyncFunctions/await-in-arrow-parameters.js new file mode 100644 index 0000000000..ebb4ea9dac --- /dev/null +++ b/js/src/tests/ecma_2017/AsyncFunctions/await-in-arrow-parameters.js @@ -0,0 +1,94 @@ +var ieval = eval; +var AsyncFunction = async function(){}.constructor; + +var functionContext = { + Function: { + constructor: Function, + toSourceBody: code => `function f() { ${code} }`, + toSourceParameter: code => `function f(x = ${code}) { }`, + }, + AsyncFunction: { + constructor: AsyncFunction, + toSourceBody: code => `async function f() { ${code} }`, + toSourceParameter: code => `async function f(x = ${code}) { }`, + }, +}; + +function assertSyntaxError(kind, code) { + var {constructor, toSourceBody, toSourceParameter} = functionContext[kind]; + var body = toSourceBody(code); + var parameter = toSourceParameter(code); + + assertThrowsInstanceOf(() => { constructor(code); }, SyntaxError, constructor.name + ":" + code); + assertThrowsInstanceOf(() => { constructor(`x = ${code}`, ""); }, SyntaxError, constructor.name + ":" + code); + + assertThrowsInstanceOf(() => { eval(body); }, SyntaxError, "eval:" + body); + assertThrowsInstanceOf(() => { ieval(body); }, SyntaxError, "indirect eval:" + body); + + assertThrowsInstanceOf(() => { eval(parameter); }, SyntaxError, "eval:" + parameter); + assertThrowsInstanceOf(() => { ieval(parameter); }, SyntaxError, "indirect eval:" + parameter); +} + +function assertNoSyntaxError(kind, code) { + var {constructor, toSourceBody, toSourceParameter} = functionContext[kind]; + var body = toSourceBody(code); + var parameter = toSourceParameter(code); + + constructor(code); + constructor(`x = ${code}`, ""); + + eval(body); + ieval(body); + + eval(parameter); + ieval(parameter); +} + +function assertSyntaxErrorAsync(code) { + assertNoSyntaxError("Function", code); + assertSyntaxError("AsyncFunction", code); +} + +function assertSyntaxErrorBoth(code) { + assertSyntaxError("Function", code); + assertSyntaxError("AsyncFunction", code); +} + + +// Bug 1353691 +// |await| expression is invalid in arrow functions in async-context. +// |await/r/g| first parses as |AwaitExpression RegularExpressionLiteral|, when reparsing the +// arrow function, it is parsed as |IdentRef DIV IdentRef DIV IdentRef|. We need to ensure in this +// case, that we still treat |await| as a keyword and hence throw a SyntaxError. +assertSyntaxErrorAsync("(a = await/r/g) => {}"); +assertSyntaxErrorBoth("async(a = await/r/g) => {}"); + +// Also applies when nesting arrow functions. +assertSyntaxErrorAsync("(a = (b = await/r/g) => {}) => {}"); +assertSyntaxErrorBoth("async(a = (b = await/r/g) => {}) => {}"); +assertSyntaxErrorBoth("(a = async(b = await/r/g) => {}) => {}"); +assertSyntaxErrorBoth("async(a = async(b = await/r/g) => {}) => {}"); + + +// Bug 1355860 +// |await| cannot be used as rest-binding parameter in arrow functions in async-context. +assertSyntaxErrorAsync("(...await) => {}"); +assertSyntaxErrorBoth("async(...await) => {}"); + +assertSyntaxErrorAsync("(a, ...await) => {}"); +assertSyntaxErrorBoth("async(a, ...await) => {}"); + +// Also test nested arrow functions. +assertSyntaxErrorAsync("(a = (...await) => {}) => {}"); +assertSyntaxErrorBoth("(a = async(...await) => {}) => {}"); +assertSyntaxErrorBoth("async(a = (...await) => {}) => {}"); +assertSyntaxErrorBoth("async(a = async(...await) => {}) => {}"); + +assertSyntaxErrorAsync("(a = (b, ...await) => {}) => {}"); +assertSyntaxErrorBoth("(a = async(b, ...await) => {}) => {}"); +assertSyntaxErrorBoth("async(a = (b, ...await) => {}) => {}"); +assertSyntaxErrorBoth("async(a = async(b, ...await) => {}) => {}"); + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_2017/AsyncFunctions/forbidden-as-consequent.js b/js/src/tests/ecma_2017/AsyncFunctions/forbidden-as-consequent.js new file mode 100644 index 0000000000..656ed46deb --- /dev/null +++ b/js/src/tests/ecma_2017/AsyncFunctions/forbidden-as-consequent.js @@ -0,0 +1,14 @@ +/* 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/. */ + +assertThrowsInstanceOf(() => eval("if (1) async function foo() {}"), + SyntaxError); +assertThrowsInstanceOf(() => eval("'use strict'; if (1) async function foo() {}"), + SyntaxError); + +var async = 42; +assertEq(eval("if (1) async \n function foo() {}"), 42); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_2017/AsyncFunctions/inner-caller.js b/js/src/tests/ecma_2017/AsyncFunctions/inner-caller.js new file mode 100644 index 0000000000..523eb79ead --- /dev/null +++ b/js/src/tests/ecma_2017/AsyncFunctions/inner-caller.js @@ -0,0 +1,26 @@ +var BUGNUMBER = 1185106; +var summary = "caller property of function inside async function should return wrapped async function"; + +print(BUGNUMBER + ": " + summary); + +(async function f() { + var inner = (function g() { + return g.caller; + })(); + assertEq(inner, f); +})(); + +(async function f() { + "use strict"; + try { + (function g() { + return g.caller; + })(); + assertEq(true, false); + } catch (e) { + assertEq(e instanceof TypeError, true); + } +})(); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_2017/Function/Object-toSource.js b/js/src/tests/ecma_2017/Function/Object-toSource.js new file mode 100644 index 0000000000..33b9e588ee --- /dev/null +++ b/js/src/tests/ecma_2017/Function/Object-toSource.js @@ -0,0 +1,370 @@ +var BUGNUMBER = 1317400;
+var summary = "Function string representation in Object.prototype.toSource";
+
+print(BUGNUMBER + ": " + summary);
+
+// Methods.
+
+assertEq(({ foo(){} }).toSource(),
+ "({foo(){}})");
+assertEq(({ *foo(){} }).toSource(),
+ "({*foo(){}})");
+assertEq(({ async foo(){} }).toSource(),
+ "({async foo(){}})");
+
+assertEq(({ 1(){} }).toSource(),
+ "({1(){}})");
+
+// Methods with more spacing.
+// Spacing is kept.
+
+assertEq(({ foo (){} }).toSource(),
+ "({foo (){}})");
+assertEq(({ foo () {} }).toSource(),
+ "({foo () {}})");
+
+// Methods with computed name.
+// Method syntax is composed.
+
+let name = "foo";
+assertEq(({ [name](){} }).toSource(),
+ "({foo(){}})");
+assertEq(({ *[name](){} }).toSource(),
+ "({*foo(){}})");
+assertEq(({ async [name](){} }).toSource(),
+ "({async foo(){}})");
+
+assertEq(({ [ Symbol.iterator ](){} }).toSource(),
+ "({[Symbol.iterator](){}})");
+
+// Accessors.
+
+assertEq(({ get foo(){} }).toSource(),
+ "({get foo(){}})");
+assertEq(({ set foo(v){} }).toSource(),
+ "({set foo(v){}})");
+
+// Accessors with computed name.
+// Method syntax is composed.
+
+assertEq(({ get [name](){} }).toSource(),
+ "({get foo(){}})");
+assertEq(({ set [name](v){} }).toSource(),
+ "({set foo(v){}})");
+
+assertEq(({ get [ Symbol.iterator ](){} }).toSource(),
+ "({get [Symbol.iterator](){}})");
+assertEq(({ set [ Symbol.iterator ](v){} }).toSource(),
+ "({set [Symbol.iterator](v){}})");
+
+// Getter and setter with same name.
+// Getter always comes before setter.
+
+assertEq(({ get foo(){}, set foo(v){} }).toSource(),
+ "({get foo(){}, set foo(v){}})");
+assertEq(({ set foo(v){}, get foo(){} }).toSource(),
+ "({get foo(){}, set foo(v){}})");
+
+// Normal properties.
+
+assertEq(({ foo: function(){} }).toSource(),
+ "({foo:(function(){})})");
+assertEq(({ foo: function bar(){} }).toSource(),
+ "({foo:(function bar(){})})");
+assertEq(({ foo: function*(){} }).toSource(),
+ "({foo:(function*(){})})");
+assertEq(({ foo: async function(){} }).toSource(),
+ "({foo:(async function(){})})");
+
+// Normal properties with computed name.
+
+assertEq(({ [ Symbol.iterator ]: function(){} }).toSource(),
+ "({[Symbol.iterator]:(function(){})})");
+
+// Dynamically defined properties with function expression.
+// Never become a method syntax.
+
+let obj = {};
+obj.foo = function() {};
+assertEq(obj.toSource(),
+ "({foo:(function() {})})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function() {}});
+assertEq(obj.toSource(),
+ "({})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo:(function() {})})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function bar() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo:(function bar() {})})");
+
+obj = {};
+Object.defineProperty(obj, Symbol.iterator, {value: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({[Symbol.iterator]:(function() {})})");
+
+// Dynamically defined property with other object's method.
+// Method syntax is composed.
+
+let method = ({foo() {}}).foo;
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, "bar", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({bar() {}})");
+
+method = ({*foo() {}}).foo;
+
+obj = {};
+Object.defineProperty(obj, "bar", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({*bar() {}})");
+
+method = ({async foo() {}}).foo;
+
+obj = {};
+Object.defineProperty(obj, "bar", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({async bar() {}})");
+
+// Dynamically defined accessors.
+// Accessor syntax is composed.
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, Symbol.iterator, {get: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({get [Symbol.iterator]() {}})");
+
+obj = {};
+Object.defineProperty(obj, Symbol.iterator, {set: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({set [Symbol.iterator]() {}})");
+
+// Dynamically defined accessors with other object's accessors.
+// Accessor syntax is composed.
+
+let accessor = Object.getOwnPropertyDescriptor({ get foo() {} }, "foo").get;
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ get bar() {} }, "bar").get;
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ set foo(v) {} }, "foo").set;
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo(v) {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ set bar(v) {} }, "bar").set;
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo(v) {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ get foo() {} }, "foo").get;
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ get bar() {} }, "bar").get;
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ set foo(v) {} }, "foo").set;
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo(v) {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ set bar(v) {} }, "bar").set;
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo(v) {}})");
+
+// Methods with proxy.
+// Treated as normal property.
+
+method = ({foo() {}}).foo;
+let handler = {
+ get(that, name) {
+ if (name == "toSource") {
+ return function() {
+ return that.toSource();
+ };
+ }
+ return that[name];
+ }
+};
+let proxy = new Proxy(method, handler);
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: proxy, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo:foo() {}})");
+
+// Accessors with proxy.
+// Accessor syntax is composed.
+
+accessor = Object.getOwnPropertyDescriptor({ get foo() {} }, "foo").get;
+proxy = new Proxy(accessor, handler);
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: proxy, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: proxy, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+// Methods from other global.
+// Treated as normal property.
+
+let g = newGlobal();
+
+method = g.eval("({ foo() {} }).foo");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo:foo() {}})");
+
+// Accessors from other global.
+// Accessor syntax is composed.
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ get foo() {} }, 'foo').get");
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ get bar() {} }, 'bar').get");
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ set foo(v) {} }, 'foo').set");
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo(v) {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ set bar(v) {} }, 'bar').set");
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo(v) {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ get foo() {} }, 'foo').get");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ get bar() {} }, 'bar').get");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ set foo(v) {} }, 'foo').set");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo(v) {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ set bar(v) {} }, 'bar').set");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo(v) {}})");
+
+// **** Some weird cases ****
+
+// Accessors with generator or async.
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: function*() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: async function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+// Modified toSource.
+
+obj = { foo() {} };
+obj.foo.toSource = () => "hello";
+assertEq(obj.toSource(),
+ "({hello})");
+
+obj = { foo() {} };
+obj.foo.toSource = () => "bar() {}";
+assertEq(obj.toSource(),
+ "({bar() {}})");
+
+// Modified toSource with different method name.
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function bar() {}, enumerable: true});
+obj.foo.toSource = () => "hello";
+assertEq(obj.toSource(),
+ "({foo:hello})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function* bar() {}, enumerable: true});
+obj.foo.toSource = () => "hello";
+assertEq(obj.toSource(),
+ "({foo:hello})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: async function bar() {}, enumerable: true});
+obj.foo.toSource = () => "hello";
+assertEq(obj.toSource(),
+ "({foo:hello})");
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_2017/Function/browser.js b/js/src/tests/ecma_2017/Function/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/ecma_2017/Function/browser.js diff --git a/js/src/tests/ecma_2017/Function/shell.js b/js/src/tests/ecma_2017/Function/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/ecma_2017/Function/shell.js diff --git a/js/src/tests/ecma_5/Array/frozen-dense-array.js b/js/src/tests/ecma_5/Array/frozen-dense-array.js index 9db63036f3..cdb86daa3b 100644 --- a/js/src/tests/ecma_5/Array/frozen-dense-array.js +++ b/js/src/tests/ecma_5/Array/frozen-dense-array.js @@ -38,24 +38,5 @@ assertEq(delete a[0], false); assertArrayIsExpected(); -var watchpointCalled = false; -// NOTE: Be careful with the position of this test, since this sparsifies the -// elements and you might not test what you think you're testing otherwise. -a.watch(2, function(prop, oldValue, newValue) { - watchpointCalled = true; - assertEq(prop, 2); - assertEq(oldValue, 1); - assertEq(newValue, "foo"); -}); - -assertArrayIsExpected(); - -a.length = 5; -a[2] = "foo"; -assertEq(watchpointCalled, true); -assertEq(delete a[0], false); - -assertArrayIsExpected(); - if (typeof reportCompare === "function") reportCompare(true, true); diff --git a/js/src/tests/ecma_5/extensions/watch-array-length.js b/js/src/tests/ecma_5/extensions/watch-array-length.js deleted file mode 100644 index e9b356efa5..0000000000 --- a/js/src/tests/ecma_5/extensions/watch-array-length.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -var hitCount; -function watcher(p,o,n) { hitCount++; return n; } - -var a = [1]; -a.watch('length', watcher); -hitCount = 0; -a.length = 0; -reportCompare(1, hitCount, "lenient; configurable: watchpoint hit"); - -var b = Object.defineProperty([1],'0',{configurable:false}); -b.watch('length', watcher); -hitCount = 0; -var result; -try { - b.length = 0; - result = "no error"; -} catch (x) { - result = x.toString(); -} -reportCompare(1, hitCount, "lenient; non-configurable: watchpoint hit"); -reportCompare(1, b.length, "lenient; non-configurable: length unchanged"); -reportCompare("no error", result, "lenient; non-configurable: no error thrown"); - -var c = Object.defineProperty([1],'0',{configurable:false}); -c.watch('length', watcher); -hitCount = 0; -var threwTypeError; -try { - (function(){'use strict'; c.length = 0;})(); - threwTypeError = false; -} catch (x) { - threwTypeError = x instanceof TypeError; -} -reportCompare(1, hitCount, "strict; non-configurable: watchpoint hit"); -reportCompare(1, c.length, "strict; non-configurable: length unchanged"); -reportCompare(true, threwTypeError, "strict; non-configurable: TypeError thrown"); diff --git a/js/src/tests/ecma_5/extensions/watch-inherited-property.js b/js/src/tests/ecma_5/extensions/watch-inherited-property.js deleted file mode 100644 index 1a0ad566bc..0000000000 --- a/js/src/tests/ecma_5/extensions/watch-inherited-property.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -/* Create a prototype object with a setter property. */ -var protoSetterCount; -var proto = ({ set x(v) { protoSetterCount++; } }); - -/* Put a watchpoint on that setter. */ -var protoWatchCount; -proto.watch('x', function() { protoWatchCount++; }); - -/* Make an object with the above as its prototype. */ -function C() { } -C.prototype = proto; -var o = new C(); - -/* - * Set a watchpoint on the property in the inheriting object. We have - * defined this to mean "duplicate the property, setter and all, in the - * inheriting object." I don't think debugging observation mechanisms - * should mutate the program being run, but that's what we've got. - */ -var oWatchCount; -o.watch('x', function() { oWatchCount++; }); - -/* - * Assign to the property. This should trip the watchpoint on the inheriting object and - * the setter. - */ -protoSetterCount = protoWatchCount = oWatchCount = 0; -o.x = 1; -assertEq(protoWatchCount, 0); -assertEq(oWatchCount, 1); -assertEq(protoSetterCount, 1); - -reportCompare(true, true); diff --git a/js/src/tests/ecma_5/extensions/watch-replaced-setter.js b/js/src/tests/ecma_5/extensions/watch-replaced-setter.js deleted file mode 100644 index 05cf60aff3..0000000000 --- a/js/src/tests/ecma_5/extensions/watch-replaced-setter.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -/* A stock watcher function. */ -var watcherCount; -function watcher(id, oldval, newval) { watcherCount++; return newval; } - -/* Create an object with a JavaScript setter. */ -var setterCount; -var o = { w:2, set x(v) { setterCount++; } }; - -/* - * Put the object in dictionary mode, so that JSObject::putProperty will - * mutate its shapes instead of creating new ones. - */ -delete o.w; - -/* - * Place a watchpoint on the property. The watchpoint structure holds the - * original JavaScript setter, and a pointer to the shape. - */ -o.watch('x', watcher); - -/* - * Replace the accessor property with a value property. The shape's setter - * should become a non-JS setter, js_watch_set, and the watchpoint - * structure's saved setter should be updated (in this case, cleared). - */ -Object.defineProperty(o, 'x', { value:3, - writable:true, - enumerable:true, - configurable:true }); - -/* - * Assign to the property. This should trigger js_watch_set, which should - * call the handler, and then see that there is no JS-level setter to pass - * control on to, and return. - */ -watcherCount = setterCount = 0; -o.x = 3; -assertEq(watcherCount, 1); -assertEq(setterCount, 0); - -reportCompare(true, true); diff --git a/js/src/tests/ecma_5/extensions/watch-setter-become-setter.js b/js/src/tests/ecma_5/extensions/watch-setter-become-setter.js deleted file mode 100644 index f5eff98b8a..0000000000 --- a/js/src/tests/ecma_5/extensions/watch-setter-become-setter.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -/* Create an object with a JavaScript setter. */ -var firstSetterCount; -var o = { w:2, set x(v) { firstSetterCount++; } }; - -/* - * Put the object in dictionary mode, so that JSObject::putProperty will - * mutate its shapes instead of creating new ones. - */ -delete o.w; - -/* A stock watcher function. */ -var watcherCount; -function watcher(id, oldval, newval) { watcherCount++; return newval; } - -/* - * Place a watchpoint on the property. The property's shape now has the - * watchpoint setter, with the original setter saved in the watchpoint - * structure. - */ -o.watch('x', watcher); - -/* - * Replace the setter with a new setter. The shape should get updated to - * refer to the new setter, and then the watchpoint setter should be - * re-established. - */ -var secondSetterCount; -Object.defineProperty(o, 'x', { set: function () { secondSetterCount++ } }); - -/* - * Assign to the property. This should trigger the watchpoint and the new setter. - */ -watcherCount = firstSetterCount = secondSetterCount = 0; -o.x = 3; -assertEq(watcherCount, 1); -assertEq(firstSetterCount, 0); -assertEq(secondSetterCount, 1); - -reportCompare(true, true); diff --git a/js/src/tests/ecma_5/extensions/watch-value-prop-becoming-setter.js b/js/src/tests/ecma_5/extensions/watch-value-prop-becoming-setter.js deleted file mode 100644 index 03cdd788e6..0000000000 --- a/js/src/tests/ecma_5/extensions/watch-value-prop-becoming-setter.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -/* A stock watcher function. */ -var watcherCount; -function watcher(id, old, newval) { - watcherCount++; - return newval; -} - -/* Create an object with a value property. */ -var o = { w:2, x:3 }; - -/* - * Place a watchpoint on the value property. The watchpoint structure holds - * the original JavaScript setter, and a pointer to the shape. - */ -o.watch('x', watcher); - -/* - * Put the object in dictionary mode, so that JSObject::putProperty will - * mutate its shapes instead of creating new ones. - */ -delete o.w; - -/* - * Replace the value property with a setter. - */ -var setterCount; -o.__defineSetter__('x', function() { setterCount++; }); - -/* - * Trigger the watchpoint. The watchpoint handler should run, and then the - * setter should run. - */ -watcherCount = setterCount = 0; -o.x = 4; -assertEq(watcherCount, 1); -assertEq(setterCount, 1); - -reportCompare(true, true); diff --git a/js/src/tests/ecma_5/extensions/watchpoint-deletes-JSPropertyOp-setter.js b/js/src/tests/ecma_5/extensions/watchpoint-deletes-JSPropertyOp-setter.js deleted file mode 100644 index 85410bbd4b..0000000000 --- a/js/src/tests/ecma_5/extensions/watchpoint-deletes-JSPropertyOp-setter.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -function make_watcher(name) { - return function (id, oldv, newv) { - print("watched " + name + "[0]"); - }; -} - -var o, p; -function f(flag) { - if (flag) { - o = arguments; - } else { - p = arguments; - o.watch(0, make_watcher('o')); - p.watch(0, make_watcher('p')); - - /* - * Previously, the watchpoint implementation actually substituted its magic setter - * functions for the setters of shared shapes, and then 1) carefully ignored calls - * to its magic setter from unrelated objects, and 2) avoided restoring the - * original setter until all watchpoints on that shape had been removed. - * - * However, when the watchpoint code began using JSObject::changeProperty and - * js_ChangeNativePropertyAttrs to change shapes' setters, the shape tree code - * became conscious of the presence of watchpoints, and shared shapes between - * objects only when their watchpoint nature coincided. Clearing the magic setter - * from one object's shape would not affect other objects, because the - * watchpointed and non-watchpointed shapes were distinct if they were shared. - * - * Thus, the first unwatch call must go ahead and fix p's shape, even though a - * watchpoint exists on the same shape in o. o's watchpoint's presence shouldn't - * cause 'unwatch' to leave p's magic setter in place. - */ - - /* DropWatchPointAndUnlock would see o's watchpoint, and not change p's property. */ - p.unwatch(0); - - /* DropWatchPointAndUnlock would fix o's property, but not p's; p's setter would be gone. */ - o.unwatch(0); - - /* This would fail to invoke the arguments object's setter. */ - p[0] = 4; - - /* And the formal parameter would not get updated. */ - assertEq(flag, 4); - } -} - -f(true); -f(false); - -reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Array/unscopables.js b/js/src/tests/ecma_6/Array/unscopables.js index 6685309a06..56b3aa520a 100644 --- a/js/src/tests/ecma_6/Array/unscopables.js +++ b/js/src/tests/ecma_6/Array/unscopables.js @@ -26,6 +26,8 @@ assertDeepEq(keys, [ "fill", "find", "findIndex", + "flat", + "flatMap", "includes", "keys", "values" diff --git a/js/src/tests/ecma_6/Class/parenExprToString.js b/js/src/tests/ecma_6/Class/parenExprToString.js new file mode 100644 index 0000000000..a93972ce92 --- /dev/null +++ b/js/src/tests/ecma_6/Class/parenExprToString.js @@ -0,0 +1,8 @@ +// Test that parenthesized class expressions don't get their toString offsets +// messed up. + +assertEq((class {}).toString(), "class {}"); +assertEq(((class {})).toString(), "class {}"); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "OK"); diff --git a/js/src/tests/ecma_6/Comprehensions/for-reserved-word.js b/js/src/tests/ecma_6/Comprehensions/for-reserved-word.js new file mode 100644 index 0000000000..9b320fc91e --- /dev/null +++ b/js/src/tests/ecma_6/Comprehensions/for-reserved-word.js @@ -0,0 +1,107 @@ +var BUGNUMBER = 1340089; +var summary = "Comprehension should check the binding names"; + +print(BUGNUMBER + ": " + summary); + +// Non strict mode. +// Keywords, literals, 'let', and 'yield' are not allowed. + +assertThrowsInstanceOf(function () { + eval("[for (true of [1]) 2]"); +}, SyntaxError); +assertThrowsInstanceOf(function () { + eval("(for (true of [1]) 2)"); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + eval("[for (throw of [1]) 2]"); +}, SyntaxError); +assertThrowsInstanceOf(function () { + eval("(for (throw of [1]) 2)"); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + eval("[for (let of [1]) 2]"); +}, SyntaxError); +assertThrowsInstanceOf(function () { + eval("(for (let of [1]) 2)"); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + eval("[for (yield of [1]) 2]"); +}, SyntaxError); +assertThrowsInstanceOf(function () { + eval("(for (yield of [1]) 2)"); +}, SyntaxError); + +eval("[for (public of [1]) 2]"); +eval("(for (public of [1]) 2)"); + +eval("[for (static of [1]) 2]"); +eval("(for (static of [1]) 2)"); + +// Strict mode. +// All reserved words are not allowed. + +assertThrowsInstanceOf(function () { + "use strict"; + eval("[for (true of [1]) 2]"); +}, SyntaxError); +assertThrowsInstanceOf(function () { + "use strict"; + eval("(for (true of [1]) 2)"); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + "use strict"; + eval("[for (throw of [1]) 2]"); +}, SyntaxError); +assertThrowsInstanceOf(function () { + "use strict"; + eval("(for (throw of [1]) 2)"); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + "use strict"; + eval("[for (let of [1]) 2]"); +}, SyntaxError); +assertThrowsInstanceOf(function () { + "use strict"; + eval("(for (let of [1]) 2)"); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + "use strict"; + eval("[for (yield of [1]) 2]"); +}, SyntaxError); +assertThrowsInstanceOf(function () { + "use strict"; + eval("(for (yield of [1]) 2)"); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + "use strict"; + eval("[for (public of [1]) 2]"); +}, SyntaxError); +assertThrowsInstanceOf(function () { + "use strict"; + eval("(for (public of [1]) 2)"); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + "use strict"; + eval("[for (static of [1]) 2]"); +}, SyntaxError); +assertThrowsInstanceOf(function () { + "use strict"; + eval("(for (static of [1]) 2)"); +}, SyntaxError); + +(function () { + "use strict"; + eval("[for (await of [1]) 2]"); + eval("(for (await of [1]) 2)"); +})(); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Function/constructor-binding.js b/js/src/tests/ecma_6/Function/constructor-binding.js new file mode 100644 index 0000000000..e82274d279 --- /dev/null +++ b/js/src/tests/ecma_6/Function/constructor-binding.js @@ -0,0 +1,11 @@ +var BUGNUMBER = 636635; +var summary = "A function created by Function constructor shouldn't have anonymous binding"; + +print(BUGNUMBER + ": " + summary); + +assertEq(new Function("return typeof anonymous")(), "undefined"); +assertEq(new Function("return function() { return typeof anonymous; }")()(), "undefined"); +assertEq(new Function("return function() { eval(''); return typeof anonymous; }")()(), "undefined"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Function/throw-type-error.js b/js/src/tests/ecma_6/Function/throw-type-error.js new file mode 100644 index 0000000000..68dd6e1d07 --- /dev/null +++ b/js/src/tests/ecma_6/Function/throw-type-error.js @@ -0,0 +1,16 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ + +const ThrowTypeError = function(){ + "use strict"; + return Object.getOwnPropertyDescriptor(arguments, "callee").get; +}(); + +assertDeepEq(Object.getOwnPropertyDescriptor(ThrowTypeError, "length"), { + value: 0, writable: false, enumerable: false, configurable: false +}); + +assertEq(Object.isFrozen(ThrowTypeError), true); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/forbidden-as-consequent.js b/js/src/tests/ecma_6/Generators/forbidden-as-consequent.js new file mode 100644 index 0000000000..13647e154b --- /dev/null +++ b/js/src/tests/ecma_6/Generators/forbidden-as-consequent.js @@ -0,0 +1,11 @@ +/* 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/. */ + +assertThrowsInstanceOf(() => eval("if (1) function* foo() {}"), + SyntaxError); +assertThrowsInstanceOf(() => eval("'use strict'; if (1) function* foo() {}"), + SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/runtime.js b/js/src/tests/ecma_6/Generators/runtime.js index c4d3bb6a66..7146eef9ff 100644 --- a/js/src/tests/ecma_6/Generators/runtime.js +++ b/js/src/tests/ecma_6/Generators/runtime.js @@ -109,7 +109,7 @@ function TestGeneratorFunction() { // Doesn't matter particularly what string gets serialized, as long // as it contains "function*" and "yield 10". assertEq(GeneratorFunction('yield 10').toString(), - "function* anonymous() {\nyield 10\n}"); + "function* anonymous(\n) {\nyield 10\n}"); } TestGeneratorFunction(); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-parameter.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-parameter.js new file mode 100644 index 0000000000..ae7fbe879c --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-parameter.js @@ -0,0 +1,21 @@ +// Annex B.3.3.1 disallows Annex B lexical function behavior when redeclaring a +// parameter. + +(function(f) { + if (true) function f() { } + assertEq(f, 123); +}(123)); + +(function(f) { + { function f() { } } + assertEq(f, 123); +}(123)); + +(function(f = 123) { + assertEq(f, 123); + { function f() { } } + assertEq(f, 123); +}()); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/TemplateStrings/tagTempl.js b/js/src/tests/ecma_6/TemplateStrings/tagTempl.js index 1e3f52bfb7..99c2098dc7 100644 --- a/js/src/tests/ecma_6/TemplateStrings/tagTempl.js +++ b/js/src/tests/ecma_6/TemplateStrings/tagTempl.js @@ -287,5 +287,176 @@ assertEq(String.raw`h\r\ney${4}there\n`, "h\\r\\ney4there\\n"); assertEq(String.raw`hey`, "hey"); assertEq(String.raw``, ""); +// Invalid escape sequences +check(raw`\01`, ["\\01"]); +check(raw`\01${0}right`, ["\\01","right"]); +check(raw`left${0}\01`, ["left","\\01"]); +check(raw`left${0}\01${1}right`, ["left","\\01","right"]); +check(raw`\1`, ["\\1"]); +check(raw`\1${0}right`, ["\\1","right"]); +check(raw`left${0}\1`, ["left","\\1"]); +check(raw`left${0}\1${1}right`, ["left","\\1","right"]); +check(raw`\xg`, ["\\xg"]); +check(raw`\xg${0}right`, ["\\xg","right"]); +check(raw`left${0}\xg`, ["left","\\xg"]); +check(raw`left${0}\xg${1}right`, ["left","\\xg","right"]); +check(raw`\xAg`, ["\\xAg"]); +check(raw`\xAg${0}right`, ["\\xAg","right"]); +check(raw`left${0}\xAg`, ["left","\\xAg"]); +check(raw`left${0}\xAg${1}right`, ["left","\\xAg","right"]); +check(raw`\u0`, ["\\u0"]); +check(raw`\u0${0}right`, ["\\u0","right"]); +check(raw`left${0}\u0`, ["left","\\u0"]); +check(raw`left${0}\u0${1}right`, ["left","\\u0","right"]); +check(raw`\u0g`, ["\\u0g"]); +check(raw`\u0g${0}right`, ["\\u0g","right"]); +check(raw`left${0}\u0g`, ["left","\\u0g"]); +check(raw`left${0}\u0g${1}right`, ["left","\\u0g","right"]); +check(raw`\u00g`, ["\\u00g"]); +check(raw`\u00g${0}right`, ["\\u00g","right"]); +check(raw`left${0}\u00g`, ["left","\\u00g"]); +check(raw`left${0}\u00g${1}right`, ["left","\\u00g","right"]); +check(raw`\u000g`, ["\\u000g"]); +check(raw`\u000g${0}right`, ["\\u000g","right"]); +check(raw`left${0}\u000g`, ["left","\\u000g"]); +check(raw`left${0}\u000g${1}right`, ["left","\\u000g","right"]); +check(raw`\u{}`, ["\\u{}"]); +check(raw`\u{}${0}right`, ["\\u{}","right"]); +check(raw`left${0}\u{}`, ["left","\\u{}"]); +check(raw`left${0}\u{}${1}right`, ["left","\\u{}","right"]); +check(raw`\u{-0}`, ["\\u{-0}"]); +check(raw`\u{-0}${0}right`, ["\\u{-0}","right"]); +check(raw`left${0}\u{-0}`, ["left","\\u{-0}"]); +check(raw`left${0}\u{-0}${1}right`, ["left","\\u{-0}","right"]); +check(raw`\u{g}`, ["\\u{g}"]); +check(raw`\u{g}${0}right`, ["\\u{g}","right"]); +check(raw`left${0}\u{g}`, ["left","\\u{g}"]); +check(raw`left${0}\u{g}${1}right`, ["left","\\u{g}","right"]); +check(raw`\u{0`, ["\\u{0"]); +check(raw`\u{0${0}right`, ["\\u{0","right"]); +check(raw`left${0}\u{0`, ["left","\\u{0"]); +check(raw`left${0}\u{0${1}right`, ["left","\\u{0","right"]); +check(raw`\u{\u{0}`, ["\\u{\\u{0}"]); +check(raw`\u{\u{0}${0}right`, ["\\u{\\u{0}","right"]); +check(raw`left${0}\u{\u{0}`, ["left","\\u{\\u{0}"]); +check(raw`left${0}\u{\u{0}${1}right`, ["left","\\u{\\u{0}","right"]); +check(raw`\u{110000}`, ["\\u{110000}"]); +check(raw`\u{110000}${0}right`, ["\\u{110000}","right"]); +check(raw`left${0}\u{110000}`, ["left","\\u{110000}"]); +check(raw`left${0}\u{110000}${1}right`, ["left","\\u{110000}","right"]); + +check(cooked`\01`, [void 0]); +check(cooked`\01${0}right`, [void 0,"right"]); +check(cooked`left${0}\01`, ["left",void 0]); +check(cooked`left${0}\01${1}right`, ["left",void 0,"right"]); +check(cooked`\1`, [void 0]); +check(cooked`\1${0}right`, [void 0,"right"]); +check(cooked`left${0}\1`, ["left",void 0]); +check(cooked`left${0}\1${1}right`, ["left",void 0,"right"]); +check(cooked`\xg`, [void 0]); +check(cooked`\xg${0}right`, [void 0,"right"]); +check(cooked`left${0}\xg`, ["left",void 0]); +check(cooked`left${0}\xg${1}right`, ["left",void 0,"right"]); +check(cooked`\xAg`, [void 0]); +check(cooked`\xAg${0}right`, [void 0,"right"]); +check(cooked`left${0}\xAg`, ["left",void 0]); +check(cooked`left${0}\xAg${1}right`, ["left",void 0,"right"]); +check(cooked`\u0`, [void 0]); +check(cooked`\u0${0}right`, [void 0,"right"]); +check(cooked`left${0}\u0`, ["left",void 0]); +check(cooked`left${0}\u0${1}right`, ["left",void 0,"right"]); +check(cooked`\u0g`, [void 0]); +check(cooked`\u0g${0}right`, [void 0,"right"]); +check(cooked`left${0}\u0g`, ["left",void 0]); +check(cooked`left${0}\u0g${1}right`, ["left",void 0,"right"]); +check(cooked`\u00g`, [void 0]); +check(cooked`\u00g${0}right`, [void 0,"right"]); +check(cooked`left${0}\u00g`, ["left",void 0]); +check(cooked`left${0}\u00g${1}right`, ["left",void 0,"right"]); +check(cooked`\u000g`, [void 0]); +check(cooked`\u000g${0}right`, [void 0,"right"]); +check(cooked`left${0}\u000g`, ["left",void 0]); +check(cooked`left${0}\u000g${1}right`, ["left",void 0,"right"]); +check(cooked`\u{}`, [void 0]); +check(cooked`\u{}${0}right`, [void 0,"right"]); +check(cooked`left${0}\u{}`, ["left",void 0]); +check(cooked`left${0}\u{}${1}right`, ["left",void 0,"right"]); +check(cooked`\u{-0}`, [void 0]); +check(cooked`\u{-0}${0}right`, [void 0,"right"]); +check(cooked`left${0}\u{-0}`, ["left",void 0]); +check(cooked`left${0}\u{-0}${1}right`, ["left",void 0,"right"]); +check(cooked`\u{g}`, [void 0]); +check(cooked`\u{g}${0}right`, [void 0,"right"]); +check(cooked`left${0}\u{g}`, ["left",void 0]); +check(cooked`left${0}\u{g}${1}right`, ["left",void 0,"right"]); +check(cooked`\u{0`, [void 0]); +check(cooked`\u{0${0}right`, [void 0,"right"]); +check(cooked`left${0}\u{0`, ["left",void 0]); +check(cooked`left${0}\u{0${1}right`, ["left",void 0,"right"]); +check(cooked`\u{\u{0}`, [void 0]); +check(cooked`\u{\u{0}${0}right`, [void 0,"right"]); +check(cooked`left${0}\u{\u{0}`, ["left",void 0]); +check(cooked`left${0}\u{\u{0}${1}right`, ["left",void 0,"right"]); +check(cooked`\u{110000}`, [void 0]); +check(cooked`\u{110000}${0}right`, [void 0,"right"]); +check(cooked`left${0}\u{110000}`, ["left",void 0]); +check(cooked`left${0}\u{110000}${1}right`, ["left",void 0,"right"]); + +syntaxError("`\\01`"); +syntaxError("`\\01${0}right`"); +syntaxError("`left${0}\\01`"); +syntaxError("`left${0}\\01${1}right`"); +syntaxError("`\\1`"); +syntaxError("`\\1${0}right`"); +syntaxError("`left${0}\\1`"); +syntaxError("`left${0}\\1${1}right`"); +syntaxError("`\\xg`"); +syntaxError("`\\xg${0}right`"); +syntaxError("`left${0}\\xg`"); +syntaxError("`left${0}\\xg${1}right`"); +syntaxError("`\\xAg`"); +syntaxError("`\\xAg${0}right`"); +syntaxError("`left${0}\\xAg`"); +syntaxError("`left${0}\\xAg${1}right`"); +syntaxError("`\\u0`"); +syntaxError("`\\u0${0}right`"); +syntaxError("`left${0}\\u0`"); +syntaxError("`left${0}\\u0${1}right`"); +syntaxError("`\\u0g`"); +syntaxError("`\\u0g${0}right`"); +syntaxError("`left${0}\\u0g`"); +syntaxError("`left${0}\\u0g${1}right`"); +syntaxError("`\\u00g`"); +syntaxError("`\\u00g${0}right`"); +syntaxError("`left${0}\\u00g`"); +syntaxError("`left${0}\\u00g${1}right`"); +syntaxError("`\\u000g`"); +syntaxError("`\\u000g${0}right`"); +syntaxError("`left${0}\\u000g`"); +syntaxError("`left${0}\\u000g${1}right`"); +syntaxError("`\\u{}`"); +syntaxError("`\\u{}${0}right`"); +syntaxError("`left${0}\\u{}`"); +syntaxError("`left${0}\\u{}${1}right`"); +syntaxError("`\\u{-0}`"); +syntaxError("`\\u{-0}${0}right`"); +syntaxError("`left${0}\\u{-0}`"); +syntaxError("`left${0}\\u{-0}${1}right`"); +syntaxError("`\\u{g}`"); +syntaxError("`\\u{g}${0}right`"); +syntaxError("`left${0}\\u{g}`"); +syntaxError("`left${0}\\u{g}${1}right`"); +syntaxError("`\\u{0`"); +syntaxError("`\\u{0${0}right`"); +syntaxError("`left${0}\\u{0`"); +syntaxError("`left${0}\\u{0${1}right`"); +syntaxError("`\\u{\\u{0}`"); +syntaxError("`\\u{\\u{0}${0}right`"); +syntaxError("`left${0}\\u{\\u{0}`"); +syntaxError("`left${0}\\u{\\u{0}${1}right`"); +syntaxError("`\\u{110000}`"); +syntaxError("`\\u{110000}${0}right`"); +syntaxError("`left${0}\\u{110000}`"); +syntaxError("`left${0}\\u{110000}${1}right`"); reportCompare(0, 0, "ok"); diff --git a/js/src/tests/ecma_6/extensions/newer-type-functions-caller-arguments.js b/js/src/tests/ecma_6/extensions/newer-type-functions-caller-arguments.js new file mode 100644 index 0000000000..7fed18037c --- /dev/null +++ b/js/src/tests/ecma_6/extensions/newer-type-functions-caller-arguments.js @@ -0,0 +1,43 @@ +// Tests that newer-type functions (i.e. anything not defined by regular function declarations and +// expressions) throw when accessing their 'arguments' and 'caller' properties. + +// 9.2.7 (AddRestrictedFunctionProperties) defines accessors on Function.prototype which throw on +// every 'get' and 'set' of 'caller' and 'arguments'. +// Additionally, 16.2 (Forbidden Extensions) forbids adding properties to all non-"legacy" function +// declarations and expressions. This creates the effect that all newer-style functions act like +// strict-mode functions when accessing their 'caller' and 'arguments' properties. + +const container = { + async asyncMethod() {}, + *genMethod() {}, + method() {} +}; + +[ + async function(){}, + function*(){}, + () => {}, + async () => {}, + + container.asyncMethod, + container.genMethod, + container.method +].forEach(f => { + checkArgumentsAccess(f); + checkCallerAccess(f); +}); + +function checkArgumentsAccess(f) { + assertThrowsInstanceOf(() => f.arguments, TypeError, + `Expected 'arguments' property access to throw on ${f}`); +} + +function checkCallerAccess(f) { + assertThrowsInstanceOf(() => f.caller, TypeError, + `Expected 'caller' property access to throw on ${f}`); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/ecma_7/AsyncFunctions/async-contains-unicode-escape.js b/js/src/tests/ecma_7/AsyncFunctions/async-contains-unicode-escape.js new file mode 100644 index 0000000000..d53dff696c --- /dev/null +++ b/js/src/tests/ecma_7/AsyncFunctions/async-contains-unicode-escape.js @@ -0,0 +1,54 @@ +var BUGNUMBER = 1315815; +var summary = "async/await containing escapes"; + +print(BUGNUMBER + ": " + summary); + +// Using "eval" as the argument name is fugly, but it means evals below are +// *direct* evals, and so their effects in the unescaped case won't extend +// past each separate |test| call (as would happen if we used a different name +// that made each eval into an indirect eval, affecting code in the global +// scope). +function test(code, eval) +{ + var unescaped = code.replace("###", "async"); + var escaped = code.replace("###", "\\u0061"); + + assertThrowsInstanceOf(() => eval(escaped), SyntaxError); + eval(unescaped); +} + +test("### function f() {}", eval); +test("var x = ### function f() {}", eval); +test("### x => {};", eval); +test("var x = ### x => {}", eval); +test("### () => {};", eval); +test("var x = ### () => {}", eval); +test("### (y) => {};", eval); +test("var x = ### (y) => {}", eval); +test("({ ### x() {} })", eval); +test("var x = ### function f() {}", eval); + +if (typeof parseModule === "function") + test("export default ### function f() {}", parseModule); + +assertThrowsInstanceOf(() => eval("async await => 1;"), + SyntaxError); +assertThrowsInstanceOf(() => eval("async aw\\u0061it => 1;"), + SyntaxError); + +var async = 0; +assertEq(\u0061sync, 0); + +var obj = { \u0061sync() { return 1; } }; +assertEq(obj.async(), 1); + +async = function() { return 42; }; + +var z = async(obj); +assertEq(z, 42); + +var w = async(obj)=>{}; +assertEq(typeof w, "function"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/js1_5/Object/regress-362872-01.js b/js/src/tests/js1_5/Object/regress-362872-01.js deleted file mode 100644 index 0808ee82ba..0000000000 --- a/js/src/tests/js1_5/Object/regress-362872-01.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 362872; -var summary = 'script should not drop watchpoint that is in use'; -var actual = 'No Crash'; -var expect = 'No Crash'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - function exploit() { - var rooter = {}, object = {}, filler1 = "", filler2 = "\u5555"; - for(var i = 0; i < 32/2-2; i++) { filler1 += "\u5050"; } - object.watch("foo", function(){ - object.unwatch("foo"); - object.unwatch("foo"); - for(var i = 0; i < 8 * 1024; i++) { - rooter[i] = filler1 + filler2; - } - }); - object.foo = "bar"; - } - exploit(); - - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/Object/regress-362872-02.js b/js/src/tests/js1_5/Object/regress-362872-02.js deleted file mode 100644 index edee43a4ab..0000000000 --- a/js/src/tests/js1_5/Object/regress-362872-02.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Blake Kaplan - */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 362872; -var summary = 'script should not drop watchpoint that is in use'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -this.watch('x', function f() { - print("before"); - x = 3; - print("after"); - }); -x = 3; - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/Regress/regress-127243.js b/js/src/tests/js1_5/Regress/regress-127243.js deleted file mode 100644 index 11779f8037..0000000000 --- a/js/src/tests/js1_5/Regress/regress-127243.js +++ /dev/null @@ -1,75 +0,0 @@ -// |reftest| skip-if(xulRuntime.OS=="WINNT"&&isDebugBuild) slow -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 127243; -var summary = 'Do not crash on watch'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -if (typeof window != 'undefined' && typeof document != 'undefined') -{ - // delay test driver end - gDelayTestDriverEnd = true; - window.addEventListener('load', handleLoad, false); -} -else -{ - printStatus('This test must be run in the browser'); - reportCompare(expect, actual, summary); - -} - -var div; - -function handleLoad() -{ - div = document.createElement('div'); - document.body.appendChild(div); - div.setAttribute('id', 'id1'); - div.style.width = '50px'; - div.style.height = '100px'; - div.style.overflow = 'auto'; - - for (var i = 0; i < 5; i++) - { - var p = document.createElement('p'); - var t = document.createTextNode('blah'); - p.appendChild(t); - div.appendChild(p); - } - - div.watch('scrollTop', wee); - - setTimeout('setScrollTop()', 1000); -} - -function wee(id, oldval, newval) -{ - var t = document.createTextNode('setting ' + id + - ' value ' + div.scrollTop + - ' oldval ' + oldval + - ' newval ' + newval); - var p = document.createElement('p'); - p.appendChild(t); - document.body.appendChild(p); - - return newval; -} - -function setScrollTop() -{ - div.scrollTop = 42; - - reportCompare(expect, actual, summary); - - gDelayTestDriverEnd = false; - jsTestDriverEnd(); - -} diff --git a/js/src/tests/js1_5/Regress/regress-213482.js b/js/src/tests/js1_5/Regress/regress-213482.js deleted file mode 100644 index 26ed7e894d..0000000000 --- a/js/src/tests/js1_5/Regress/regress-213482.js +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 213482; -var summary = 'Do not crash watching property when watcher sets property'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -var testobj = {value: 'foo'}; - -function watched (a, b, c) { - testobj.value = (new Date()).getTime(); -} - -function setTest() { - testobj.value = 'b'; -} - -testobj.watch("value", watched); - -setTest(); - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/Regress/regress-240577.js b/js/src/tests/js1_5/Regress/regress-240577.js deleted file mode 100644 index ba8330aa0b..0000000000 --- a/js/src/tests/js1_5/Regress/regress-240577.js +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Bob Clary - */ - -//----------------------------------------------------------------------------- -// originally reported by Jens Thiele <karme@unforgettable.com> in -var BUGNUMBER = 240577; -var summary = 'object.watch execution context'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -var createWatcher = function ( watchlabel ) -{ - var watcher = function (property, oldvalue, newvalue) - { - actual += watchlabel; return newvalue; - }; - return watcher; -}; - -var watcher1 = createWatcher('watcher1'); - -var object = {property: 'value'}; - -object.watch('property', watcher1); - -object.property = 'newvalue'; - -expect = 'watcher1'; - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/Regress/regress-355341.js b/js/src/tests/js1_5/Regress/regress-355341.js deleted file mode 100644 index ab2a4b884a..0000000000 --- a/js/src/tests/js1_5/Regress/regress-355341.js +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 355341; -var summary = 'Do not crash with watch and setter'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - Object.defineProperty(this, "x", { set: Function, enumerable: true, configurable: true }); - this.watch('x', function () { }); x = 3; - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/Regress/regress-355344.js b/js/src/tests/js1_5/Regress/regress-355344.js deleted file mode 100644 index 00bd391475..0000000000 --- a/js/src/tests/js1_5/Regress/regress-355344.js +++ /dev/null @@ -1,49 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 355344; -var summary = 'Exceptions thrown by watch point'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - var o = {}; - - expect = 'setter: yikes'; - - o.watch('x', function(){throw 'yikes'}); - try - { - o.x = 3; - } - catch(ex) - { - actual = "setter: " + ex; - } - - try - { - eval("") ; - } - catch(e) - { - actual = "eval: " + e; - } - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/Regress/regress-361467.js b/js/src/tests/js1_5/Regress/regress-361467.js deleted file mode 100644 index 371c0a8b5a..0000000000 --- a/js/src/tests/js1_5/Regress/regress-361467.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361467; -var summary = 'Do not crash with certain watchers'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - expect = actual = 'No Crash'; - - var x; - this.watch('x', print); - x = 5; - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/Regress/regress-361617.js b/js/src/tests/js1_5/Regress/regress-361617.js deleted file mode 100644 index 5d20fd78f8..0000000000 --- a/js/src/tests/js1_5/Regress/regress-361617.js +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361617; -var summary = 'Do not crash with getter, watch and gc'; -var actual = 'No Crash'; -var expect = 'No Crash'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - (function() { - Object.defineProperty(this, "x", { get: function(){}, enumerable: true, configurable: true }); - })(); - this.watch('x', print); - Object.defineProperty(this, "x", { get: function(){}, enumerable: true, configurable: true }); - gc(); - this.unwatch('x'); - x; - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/Regress/regress-385393-06.js b/js/src/tests/js1_5/Regress/regress-385393-06.js deleted file mode 100644 index 327103f395..0000000000 --- a/js/src/tests/js1_5/Regress/regress-385393-06.js +++ /dev/null @@ -1,28 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - - -//----------------------------------------------------------------------------- -var BUGNUMBER = 385393; -var summary = 'Regression test for bug 385393'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - reportCompare(expect, actual, summary); - - true.watch("x", function(){}); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/Regress/regress-506567.js b/js/src/tests/js1_5/Regress/regress-506567.js deleted file mode 100644 index e78dfe1db5..0000000000 --- a/js/src/tests/js1_5/Regress/regress-506567.js +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 506567; -var summary = 'Do not crash with watched variables'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - if (typeof clearInterval == 'undefined') - { - clearInterval = (function () {}); - } - - var obj = new Object(); - obj.test = null; - obj.watch("test", (function(prop, oldval, newval) - { - if(false) - { - var test = newval % oldval; - var func = (function(){clearInterval(myInterval);}); - } - })); - - obj.test = 'null'; - print(obj.test); - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/Scope/regress-185485.js b/js/src/tests/js1_5/Scope/regress-185485.js index 19d190eeaa..a75bf885a9 100644 --- a/js/src/tests/js1_5/Scope/regress-185485.js +++ b/js/src/tests/js1_5/Scope/regress-185485.js @@ -94,7 +94,7 @@ with (x) } status = inSection(5); actual = x.g.toString(); -expect = (function () {}).toString(); +expect = (function() {}).toString(); addThis(); diff --git a/js/src/tests/js1_5/extensions/regress-303277.js b/js/src/tests/js1_5/extensions/regress-303277.js deleted file mode 100644 index 319d9a2ed4..0000000000 --- a/js/src/tests/js1_5/extensions/regress-303277.js +++ /dev/null @@ -1,19 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 303277; -var summary = 'Do not crash with crash with a watchpoint for __proto__ property '; -var actual = 'No Crash'; -var expect = 'No Crash'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -var o = {}; -o.watch("__proto__", function(){return null;}); -o.__proto__ = null; - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-355339.js b/js/src/tests/js1_5/extensions/regress-355339.js deleted file mode 100644 index 9b15bd7421..0000000000 --- a/js/src/tests/js1_5/extensions/regress-355339.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 355339; -var summary = 'Do not assert: sprop->setter != js_watch_set'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - expect = actual = 'No Crash'; - o = {}; - o.watch("j", function(a,b,c) { print("*",a,b,c) }); - o.unwatch("j"); - o.watch("j", function(a,b,c) { print("*",a,b,c) }); - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-361346.js b/js/src/tests/js1_5/extensions/regress-361346.js deleted file mode 100644 index 297c3b1f22..0000000000 --- a/js/src/tests/js1_5/extensions/regress-361346.js +++ /dev/null @@ -1,22 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361346; -var summary = 'Crash with setter, watch, GC'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -expect = actual = 'No Crash'; - -Object.defineProperty(this, "x", { set: new Function, enumerable: true, configurable: true }); -this.watch('x', function(){}); -gc(); -x = {}; - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-361360.js b/js/src/tests/js1_5/extensions/regress-361360.js deleted file mode 100644 index 98e6575d9a..0000000000 --- a/js/src/tests/js1_5/extensions/regress-361360.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361360; -var summary = 'Do not assert: !caller || caller->pc involving setter and watch'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - expect = actual = 'No Crash'; - - this.__defineSetter__('x', eval); - this.watch('x', function(){}); - x = 3; - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-361552.js b/js/src/tests/js1_5/extensions/regress-361552.js deleted file mode 100644 index eed54e6dd0..0000000000 --- a/js/src/tests/js1_5/extensions/regress-361552.js +++ /dev/null @@ -1,27 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361552; -var summary = 'Crash with setter, watch, Script'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -expect = actual = 'No Crash'; - -if (typeof Script == 'undefined') -{ - print('Test skipped. Script not defined.'); -} -else -{ - this.__defineSetter__('x', gc); - this.watch('x', new Script('')); - x = 3; -} -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-361558.js b/js/src/tests/js1_5/extensions/regress-361558.js deleted file mode 100644 index a9a3ae7258..0000000000 --- a/js/src/tests/js1_5/extensions/regress-361558.js +++ /dev/null @@ -1,19 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361558; -var summary = 'Do not assert: sprop->setter != js_watch_set'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -expect = actual = 'No Crash'; - -({}.__proto__.watch('x', print)); ({}.watch('x', print)); - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-361571.js b/js/src/tests/js1_5/extensions/regress-361571.js deleted file mode 100644 index bf89d794b6..0000000000 --- a/js/src/tests/js1_5/extensions/regress-361571.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361571; -var summary = 'Do not assert: fp->scopeChain == parent'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - try - { - o = {}; - o.__defineSetter__('y', eval); - o.watch('y', function () { return "";}); - o.y = 1; - } - catch(ex) - { - printStatus('Note eval can no longer be called directly'); - expect = 'EvalError: function eval must be called directly, and not by way of a function of another name'; - actual = ex + ''; - } - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-361856.js b/js/src/tests/js1_5/extensions/regress-361856.js deleted file mode 100644 index e7e2f675bd..0000000000 --- a/js/src/tests/js1_5/extensions/regress-361856.js +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361856; -var summary = 'Do not assert: overwriting @ js_AddScopeProperty'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - function testit() { - var obj = {}; - obj.watch("foo", function(){}); - delete obj.foo; - obj = null; - gc(); - } - testit(); - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-361964.js b/js/src/tests/js1_5/extensions/regress-361964.js deleted file mode 100644 index fcb8bba013..0000000000 --- a/js/src/tests/js1_5/extensions/regress-361964.js +++ /dev/null @@ -1,54 +0,0 @@ -// |reftest| skip -- slow, alert not dismissed, now busted by harness -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361964; -var summary = 'Crash [@ MarkGCThingChildren] involving watch and setter'; -var actual = 'No Crash'; -var expect = 'No Crash'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - var doc; - if (typeof document == 'undefined') - { - doc = {}; - } - else - { - doc = document; - } - - if (typeof alert == 'undefined') - { - alert = print; - } - -// Crash: - doc.watch("title", function(a,b,c,d) { - return { toString : function() { alert(1); } }; - }); - doc.title = "xxx"; - -// No crash: - doc.watch("title", function() { - return { toString : function() { alert(1); } }; - }); - doc.title = "xxx"; - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-385134.js b/js/src/tests/js1_5/extensions/regress-385134.js deleted file mode 100644 index 041f4d6e7a..0000000000 --- a/js/src/tests/js1_5/extensions/regress-385134.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 385134; -var summary = 'Do not crash with setter, watch, uneval'; -var actual = 'No Crash'; -var expect = 'No Crash'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - if (typeof this.__defineSetter__ != 'undefined' && - typeof this.watch != 'undefined' && - typeof uneval != 'undefined') - { - try { - this.__defineSetter__(0, function(){}); - } catch (exc) { - // In the browser, this fails. Ignore the error. - } - this.watch(0, function(){}); - uneval(this); - } - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-385393-09.js b/js/src/tests/js1_5/extensions/regress-385393-09.js deleted file mode 100644 index 42834824af..0000000000 --- a/js/src/tests/js1_5/extensions/regress-385393-09.js +++ /dev/null @@ -1,18 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - - -//----------------------------------------------------------------------------- -var BUGNUMBER = 385393; -var summary = 'Regression test for bug 385393'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -eval("this.__defineSetter__('x', gc); this.watch('x', [].slice); x = 1;"); - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-390597.js b/js/src/tests/js1_5/extensions/regress-390597.js deleted file mode 100644 index 9f8596adc9..0000000000 --- a/js/src/tests/js1_5/extensions/regress-390597.js +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 390597; -var summary = 'watch point + eval-as-setter allows access to dead JSStackFrame'; -var actual = 'No Crash'; -var expect = 'No Crash'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - function exploit() { - try - { - var obj = this, args = null; - obj.__defineSetter__("evil", eval); - obj.watch("evil", function() { return "args = arguments;"; }); - obj.evil = null; - eval("print(args[0]);"); - } - catch(ex) - { - print('Caught ' + ex); - } - } - exploit(); - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-420612.js b/js/src/tests/js1_5/extensions/regress-420612.js deleted file mode 100644 index a4491095e7..0000000000 --- a/js/src/tests/js1_5/extensions/regress-420612.js +++ /dev/null @@ -1,21 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 420612; -var summary = 'Do not assert: obj == pobj'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -var obj = Object.create([]); -obj.unwatch("x"); - -if (typeof reportCompare === "function") - reportCompare(true, true); - -print("Tests complete"); diff --git a/js/src/tests/js1_5/extensions/regress-435345-01.js b/js/src/tests/js1_5/extensions/regress-435345-01.js deleted file mode 100644 index 28beab473f..0000000000 --- a/js/src/tests/js1_5/extensions/regress-435345-01.js +++ /dev/null @@ -1,100 +0,0 @@ -// |reftest| fails -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 435345; -var summary = 'Watch the length property of arrays'; -var actual = ''; -var expect = ''; - -// see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Object:watch - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - var arr; - - try - { - expect = 'watcher: propname=length, oldval=0, newval=1; '; - actual = ''; - arr = []; - arr.watch('length', watcher); - arr[0] = '0'; - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': 1'); - - try - { - expect = 'watcher: propname=length, oldval=1, newval=2; ' + - 'watcher: propname=length, oldval=2, newval=2; '; - actual = ''; - arr.push(5); - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': 2'); - - try - { - expect = 'watcher: propname=length, oldval=2, newval=1; '; - actual = ''; - arr.pop(); - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': 3'); - - try - { - expect = 'watcher: propname=length, oldval=1, newval=2; '; - actual = ''; - arr.length++; - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': 4'); - - try - { - expect = 'watcher: propname=length, oldval=2, newval=5; '; - actual = ''; - arr.length = 5; - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': 5'); - - exitFunc ('test'); -} - -function watcher(propname, oldval, newval) -{ - actual += 'watcher: propname=' + propname + ', oldval=' + oldval + - ', newval=' + newval + '; '; - - return newval; -} - diff --git a/js/src/tests/js1_5/extensions/regress-454040.js b/js/src/tests/js1_5/extensions/regress-454040.js deleted file mode 100644 index 63b5e14889..0000000000 --- a/js/src/tests/js1_5/extensions/regress-454040.js +++ /dev/null @@ -1,25 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 454040; -var summary = 'Do not crash @ js_ComputeFilename'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -try -{ - this.__defineGetter__("x", Function); - this.__defineSetter__("x", Function); - this.watch("x", x.__proto__); - x = 1; -} -catch(ex) -{ -} -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-454142.js b/js/src/tests/js1_5/extensions/regress-454142.js deleted file mode 100644 index 05f3312786..0000000000 --- a/js/src/tests/js1_5/extensions/regress-454142.js +++ /dev/null @@ -1,30 +0,0 @@ -// |reftest| skip-if(Android) -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 454142; -var summary = 'Do not crash with gczeal, setter, watch'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -this.watch("x", function(){}); -delete x; -if (typeof gczeal == 'function') -{ - gczeal(2); -} - -this.__defineSetter__("x", function(){}); - -if (typeof gczeal == 'function') -{ - gczeal(0); -} - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-455413.js b/js/src/tests/js1_5/extensions/regress-455413.js deleted file mode 100644 index d00ab135b0..0000000000 --- a/js/src/tests/js1_5/extensions/regress-455413.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 455413; -var summary = 'Do not assert with JIT: (m != JSVAL_INT) || isInt32(*vp)'; -var actual = 'No Crash'; -var expect = 'No Crash'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -printBugNumber(BUGNUMBER); -printStatus (summary); - - -this.watch('x', Math.pow); (function() { for(var j=0;j<4;++j){x=1;} })(); - - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-465145.js b/js/src/tests/js1_5/extensions/regress-465145.js deleted file mode 100644 index 84f81703be..0000000000 --- a/js/src/tests/js1_5/extensions/regress-465145.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 465145; -var summary = 'Do not crash with watched defined setter'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - - -this.__defineSetter__("x", function(){}); -this.watch("x", function(){}); -y = this; -for (var z = 0; z < 2; ++z) { x = y }; -this.__defineSetter__("x", function(){}); -for (var z = 0; z < 2; ++z) { x = y }; - - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-472787.js b/js/src/tests/js1_5/extensions/regress-472787.js deleted file mode 100755 index d3196f05f1..0000000000 --- a/js/src/tests/js1_5/extensions/regress-472787.js +++ /dev/null @@ -1,31 +0,0 @@ -// |reftest| skip-if(Android) -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 472787; -var summary = 'Do not hang with gczeal, watch and concat'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -this.__defineSetter__("x", Math.sin); -this.watch("x", '' .concat); - -if (typeof gczeal == 'function') -{ - gczeal(2); -} - -x = 1; - -if (typeof gczeal == 'function') -{ - gczeal(0); -} - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-488995.js b/js/src/tests/js1_5/extensions/regress-488995.js deleted file mode 100644 index f7abbe4397..0000000000 --- a/js/src/tests/js1_5/extensions/regress-488995.js +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 488995; -var summary = 'Do not crash with watch, __defineSetter__ on svg'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - if (typeof document == 'undefined') - { - print('Test skipped: requires browser.'); - } - else - { - try - { - var o=document.createElementNS("http://www.w3.org/2000/svg", "svg"); - var p=o.y; - p.watch('animVal', function(id, oldvar, newval) {} ); - p.type='xxx'; - p.__defineSetter__('animVal', function() {}); - p.animVal=0; - } - catch(ex) - { - } - } - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_6/Regress/regress-476655.js b/js/src/tests/js1_6/Regress/regress-476655.js deleted file mode 100644 index 9077ba3a07..0000000000 --- a/js/src/tests/js1_6/Regress/regress-476655.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 476655; -var summary = 'TM: Do not assert: count <= (size_t) (fp->regs->sp - StackBase(fp) - depth)'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - - try - { - eval( - "(function (){ for (var y in this) {} })();" + - "[''.watch(\"\", function(){}) for each (x in ['', '', eval, '', '']) if " + - "(x)].map(Function)" - ); - } - catch(ex) - { - } - - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_6/extensions/regress-457521.js b/js/src/tests/js1_6/extensions/regress-457521.js deleted file mode 100644 index 6f4fbda50e..0000000000 --- a/js/src/tests/js1_6/extensions/regress-457521.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 457521; -var summary = 'Do not crash @ js_DecompileValueGenerator'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -try -{ - this.__defineSetter__("x", [,,,].map); - this.watch("x", (new Function("var y, eval"))); - x = true; -} -catch(ex) -{ -} -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_6/extensions/regress-479567.js b/js/src/tests/js1_6/extensions/regress-479567.js deleted file mode 100644 index 6f35251302..0000000000 --- a/js/src/tests/js1_6/extensions/regress-479567.js +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 479567; -var summary = 'Do not assert: thing'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - - -function f() -{ - (eval("(function(){for each (y in [false, false, false]);});"))(); -} - -try -{ - this.__defineGetter__("x", gc); - uneval(this.watch("y", this.toSource)) - f(); - throw x; -} -catch(ex) -{ -} - - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_7/extensions/regress-354945-01.js b/js/src/tests/js1_7/extensions/regress-354945-01.js index 76f1a3c82f..1c57db0f7f 100644 --- a/js/src/tests/js1_7/extensions/regress-354945-01.js +++ b/js/src/tests/js1_7/extensions/regress-354945-01.js @@ -6,7 +6,7 @@ //----------------------------------------------------------------------------- var BUGNUMBER = 354945; var summary = 'Do not crash with new Iterator'; -var expect = 'TypeError: trap __iterator__ for ({__iterator__:(function (){ })}) returned a primitive value'; +var expect = 'TypeError: trap __iterator__ for ({__iterator__:(function(){ })}) returned a primitive value'; var actual; diff --git a/js/src/tests/js1_7/extensions/regress-354945-02.js b/js/src/tests/js1_7/extensions/regress-354945-02.js index 261bf7de17..abef90f77f 100644 --- a/js/src/tests/js1_7/extensions/regress-354945-02.js +++ b/js/src/tests/js1_7/extensions/regress-354945-02.js @@ -20,7 +20,7 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - expect = 'TypeError: trap __iterator__ for ({__iterator__:(function (){ })}) returned a primitive value'; + expect = 'TypeError: trap __iterator__ for ({__iterator__:(function(){ })}) returned a primitive value'; var obj = {}; obj.__iterator__ = function(){ }; try diff --git a/js/src/tests/js1_7/extensions/regress-453955.js b/js/src/tests/js1_7/extensions/regress-453955.js deleted file mode 100644 index 454f712f3d..0000000000 --- a/js/src/tests/js1_7/extensions/regress-453955.js +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 453955; -var summary = 'Do not assert: sprop->setter != js_watch_set || pobj != obj'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - for (var z = 0; z < 2; ++z) - { - [].filter.watch("9", function(y) { yield y; }); - } - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_7/extensions/regress-473282.js b/js/src/tests/js1_7/extensions/regress-473282.js deleted file mode 100644 index c50ac92348..0000000000 --- a/js/src/tests/js1_7/extensions/regress-473282.js +++ /dev/null @@ -1,20 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 473282; -var summary = 'Do not assert: thing'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -this.watch("b", "".substring); -this.__defineGetter__("a", gc); -for each (b in [this, null, null]); -a; - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_7/geniter/regress-352197.js b/js/src/tests/js1_7/geniter/regress-352197.js index 7982e12ee0..495717af70 100644 --- a/js/src/tests/js1_7/geniter/regress-352197.js +++ b/js/src/tests/js1_7/geniter/regress-352197.js @@ -20,7 +20,7 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - expect = /TypeError: anonymous generator function returns a value/; + expect = /TypeError: can't use 'yield' in a function that can return a value/; try { var gen = eval('(function() { { return 5; } yield 3; })'); diff --git a/js/src/tests/js1_7/regress/regress-385133-01.js b/js/src/tests/js1_7/regress/regress-385133-01.js deleted file mode 100644 index 03b09f5356..0000000000 --- a/js/src/tests/js1_7/regress/regress-385133-01.js +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 385133; -var summary = 'Do not crash due to recursion with watch, setter, delete, generator'; -var actual = 'No Crash'; -var expect = 'No Crash'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - try - { - Object.defineProperty(this, "x", { set: {}.watch, enumerable: true, configurable: true }); - this.watch('x', 'foo'.split); - delete x; - function g(){ x = 1; yield; } - for (i in g()) { } - } - catch(ex) - { - } - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8/extensions/regress-394709.js b/js/src/tests/js1_8/extensions/regress-394709.js deleted file mode 100644 index d04d8832fb..0000000000 --- a/js/src/tests/js1_8/extensions/regress-394709.js +++ /dev/null @@ -1,51 +0,0 @@ -// |reftest| skip-if(!xulRuntime.shell) -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 394709; -var summary = 'Do not leak with object.watch and closure'; -var actual = 'No Leak'; -var expect = 'No Leak'; - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - assertEq(finalizeCount(), 0, "invalid initial state"); - - runtest(); - gc(); - assertEq(finalizeCount(), 1, "leaked"); - - runtest(); - gc(); - assertEq(finalizeCount(), 2, "leaked"); - - runtest(); - gc(); - assertEq(finalizeCount(), 3, "leaked"); - - - function runtest () { - var obj = { b: makeFinalizeObserver() }; - obj.watch('b', watcher); - - function watcher(id, old, value) { - ++obj.n; - return value; - } - } - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8/extensions/regress-481989.js b/js/src/tests/js1_8/extensions/regress-481989.js deleted file mode 100644 index 36efb7567f..0000000000 --- a/js/src/tests/js1_8/extensions/regress-481989.js +++ /dev/null @@ -1,19 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 481989; -var summary = 'TM: Do not assert: SPROP_HAS_STUB_SETTER(sprop)'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - - -y = this.watch("x", function(){}); for each (let y in ['', '']) x = y; - - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_8/genexps/regress-683738.js b/js/src/tests/js1_8/genexps/regress-683738.js index de563645da..b0309a90ea 100644 --- a/js/src/tests/js1_8/genexps/regress-683738.js +++ b/js/src/tests/js1_8/genexps/regress-683738.js @@ -20,7 +20,7 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - expect = "generator function foo returns a value"; + expect = "can't use 'yield' in a function that can return a value"; try { actual = 'No Error'; @@ -32,7 +32,7 @@ function test() } reportCompare(expect, actual, summary + ": 1"); - expect = "generator function foo returns a value"; + expect = "generator function can't return a value"; try { actual = 'No Error'; @@ -44,7 +44,7 @@ function test() } reportCompare(expect, actual, summary + ": 2"); - expect = "generator function foo returns a value"; + expect = "can't use 'yield' in a function that can return a value"; try { actual = 'No Error'; @@ -56,7 +56,7 @@ function test() } reportCompare(expect, actual, summary + ": 3"); - expect = "generator function foo returns a value"; + expect = "generator function can't return a value"; try { actual = 'No Error'; diff --git a/js/src/tests/js1_8/regress/regress-467495-05.js b/js/src/tests/js1_8/regress/regress-467495-05.js index 505fb6bd66..ecf47b8fe3 100644 --- a/js/src/tests/js1_8/regress/regress-467495-05.js +++ b/js/src/tests/js1_8/regress/regress-467495-05.js @@ -20,7 +20,7 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - expect = 'function x() {}'; + expect = '1'; function g(x) { if (1) function x() {} return x; } print(actual = g(1) + ''); diff --git a/js/src/tests/js1_8/regress/regress-467495-06.js b/js/src/tests/js1_8/regress/regress-467495-06.js index d8bc81c831..72adeee9ec 100644 --- a/js/src/tests/js1_8/regress/regress-467495-06.js +++ b/js/src/tests/js1_8/regress/regress-467495-06.js @@ -32,7 +32,7 @@ function test() var r = f(0); - if (typeof(r[0]) != "function") + if (typeof(r[0]) != "number") actual += "Bad r[0]"; if (typeof(r[1]) != "function") diff --git a/js/src/tests/js1_8_1/extensions/regress-452498-193.js b/js/src/tests/js1_8_1/extensions/regress-452498-193.js deleted file mode 100644 index 1397bf4bb2..0000000000 --- a/js/src/tests/js1_8_1/extensions/regress-452498-193.js +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 452498; -var summary = 'TM: upvar2 regression tests'; -var actual = ''; -var expect = ''; - -//------- Comment #193 From Gary Kwong - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - -// Assertion failure: afunbox->parent, at ../jsparse.cpp:1912 - - this.x = undefined; - this.watch("x", Function); - NaN = uneval({ get \u3056 (){ return undefined } }); - x+=NaN; - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8_1/extensions/regress-452498-196.js b/js/src/tests/js1_8_1/extensions/regress-452498-196.js index 69d5a3586a..5b91911994 100644 --- a/js/src/tests/js1_8_1/extensions/regress-452498-196.js +++ b/js/src/tests/js1_8_1/extensions/regress-452498-196.js @@ -21,15 +21,6 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); -// Assertion failure: localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST, at ../jsfun.cpp:916 - - this.x = undefined; - this.watch("x", Function); - NaN = uneval({ get \u3056 (){ return undefined } }); - x+=NaN; - - reportCompare(expect, actual, summary + ': 1'); - // Assertion failure: lexdep->isLet(), at ../jsparse.cpp:1900 (function (){ diff --git a/js/src/tests/js1_8_1/extensions/regress-520572.js b/js/src/tests/js1_8_1/extensions/regress-520572.js deleted file mode 100644 index 97f00029af..0000000000 --- a/js/src/tests/js1_8_1/extensions/regress-520572.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Blake Kaplan - */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 520572; -var summary = 'watch should innerize the object being watched'; -var actual = 0; -var expect = 2; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - if ("evalcx" in this) { - // shell - let s = evalcx("lazy"); - s.n = 0; - evalcx('this.watch("x", function(){ n++; }); this.x = 4; x = 6', s); - actual = s.n; - reportCompare(expect, actual, summary); - } else { - // browser - this.watch('x', function(){ actual++; }); - this.x = 4; - x = 6; - reportCompare(expect, actual, summary); - } - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8_1/regress/regress-452498-160.js b/js/src/tests/js1_8_1/regress/regress-452498-160.js index 6498a0b5aa..305b41795a 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-160.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-160.js @@ -21,11 +21,6 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); -// crash [@ js_Interpret] - (eval("(function(){ this.watch(\"x\", function () { new function ()y } ); const y = undefined });"))(); - x = NaN; - reportCompare(expect, actual, summary + ': 2'); - // Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:5916 ({ set z(v){}, set y(v)--x, set w(v)--w }); reportCompare(expect, actual, summary + ': 3'); diff --git a/js/src/tests/js1_8_5/extensions/regress-604781-1.js b/js/src/tests/js1_8_5/extensions/regress-604781-1.js deleted file mode 100644 index a7c43f95d2..0000000000 --- a/js/src/tests/js1_8_5/extensions/regress-604781-1.js +++ /dev/null @@ -1,24 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var watcherCount, setterCount; -function watcher(id, oldval, newval) { watcherCount++; return newval; } -function setter(newval) { setterCount++; } - -var p = { set x(v) { setter(v); } }; -p.watch('x', watcher); - -watcherCount = setterCount = 0; -p.x = 2; -assertEq(setterCount, 1); -assertEq(watcherCount, 1); - -var o = Object.defineProperty({}, 'x', { set:setter, enumerable:true, configurable:true }); -o.watch('x', watcher); - -watcherCount = setterCount = 0; -o.x = 2; -assertEq(setterCount, 1); -assertEq(watcherCount, 1); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-604781-2.js b/js/src/tests/js1_8_5/extensions/regress-604781-2.js deleted file mode 100644 index 7aba4a274d..0000000000 --- a/js/src/tests/js1_8_5/extensions/regress-604781-2.js +++ /dev/null @@ -1,13 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var log; -function watcher(id, old, newval) { log += 'watcher'; return newval; } -var o = { set x(v) { log += 'setter'; } }; -o.watch('x', watcher); -Object.defineProperty(o, 'x', {value: 3, writable: true}); -log = ''; -o.x = 3; -assertEq(log, 'watcher'); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-1.js b/js/src/tests/js1_8_5/extensions/regress-627984-1.js deleted file mode 100644 index a3726630a3..0000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627984-1.js +++ /dev/null @@ -1,16 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -// See bug 627984, comment 17, item 1. -var obj; -var methods = []; -for (var i = 0; i < 2; i++) { - obj = {m: function () { return this.x; }}; - obj.watch("m", function (id, oldval, newval) { methods[i] = oldval; }); - obj.m = 0; -} -assertEq(typeof methods[0], "function"); -assertEq(typeof methods[1], "function"); -assertEq(methods[0] !== methods[1], true); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-2.js b/js/src/tests/js1_8_5/extensions/regress-627984-2.js deleted file mode 100644 index c4f1b508c2..0000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627984-2.js +++ /dev/null @@ -1,15 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -// See bug 627984, comment 17, item 2. -var obj = {}; -var x; -obj.watch("m", function (id, oldval, newval) { - x = this.m; - return newval; - }); -delete obj.m; -obj.m = function () { return this.method; }; -obj.m = 2; - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-3.js b/js/src/tests/js1_8_5/extensions/regress-627984-3.js deleted file mode 100644 index cbe4e10fa4..0000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627984-3.js +++ /dev/null @@ -1,14 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -// Don't write string value to method slot. -// See bug 627984, comment 17, item 2. -var obj = {}; -obj.watch("m", function (id, oldval, newval) { - return 'ok'; - }); -delete obj.m; -obj.m = function () { return this.x; }; -assertEq(obj.m, 'ok'); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-4.js b/js/src/tests/js1_8_5/extensions/regress-627984-4.js deleted file mode 100644 index bbc017ffb0..0000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627984-4.js +++ /dev/null @@ -1,15 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -// See bug 627984, comment 17, item 3. -var obj = {}; -obj.watch("m", function (id, oldval, newval) { - delete obj.m; - obj.m = function () {}; - return newval; - }); -delete obj.m; -obj.m = 1; -assertEq(obj.m, 1); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-5.js b/js/src/tests/js1_8_5/extensions/regress-627984-5.js deleted file mode 100644 index 704d8421cf..0000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627984-5.js +++ /dev/null @@ -1,13 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -// Bug 627984 comment 11. -var o = ({}); -o.p = function() {}; -o.watch('p', function() { }); -o.q = function() {} -delete o.p; -o.p = function() {}; -assertEq(o.p, void 0); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-6.js b/js/src/tests/js1_8_5/extensions/regress-627984-6.js deleted file mode 100644 index cb1a0fca95..0000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627984-6.js +++ /dev/null @@ -1,15 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -// Bug 627984 description. -var o = Array; -o.p = function() {}; -o.watch('p', function() { }); -for(var x in o) { - o[x]; -} -delete o.p; -o.p = function() {}; -assertEq(o.p, void 0); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-7.js b/js/src/tests/js1_8_5/extensions/regress-627984-7.js deleted file mode 100644 index b13a0e9125..0000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627984-7.js +++ /dev/null @@ -1,9 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -// See bug 627984 comment 20. -var obj = {m: function () {}}; -obj.watch("m", function () { throw 'FAIL'; }); -var f = obj.m; // don't call the watchpoint - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-631723.js b/js/src/tests/js1_8_5/extensions/regress-631723.js deleted file mode 100644 index f7c755603c..0000000000 --- a/js/src/tests/js1_8_5/extensions/regress-631723.js +++ /dev/null @@ -1,10 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var o = {a:1, b:2}; -o.watch("p", function() { return 13; }); -delete o.p; -o.p = 0; -assertEq(o.p, 13); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-636697.js b/js/src/tests/js1_8_5/extensions/regress-636697.js deleted file mode 100644 index 6b3b1de370..0000000000 --- a/js/src/tests/js1_8_5/extensions/regress-636697.js +++ /dev/null @@ -1,11 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var a = {set p(x) {}}; -a.watch('p', function () {}); -var b = Object.create(a); -b.watch('p', function () {}); -delete b.p; -b.p = 0; - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-637985.js b/js/src/tests/js1_8_5/extensions/regress-637985.js deleted file mode 100644 index 305bfc820e..0000000000 --- a/js/src/tests/js1_8_5/extensions/regress-637985.js +++ /dev/null @@ -1,8 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var obj = {}; -obj.watch(-1, function(){}); -obj.unwatch("-1"); // don't assert - -reportCompare(0, 0, 'ok');
\ No newline at end of file diff --git a/js/src/tests/js1_8_5/extensions/regress-691746.js b/js/src/tests/js1_8_5/extensions/regress-691746.js deleted file mode 100644 index 26f732d07d..0000000000 --- a/js/src/tests/js1_8_5/extensions/regress-691746.js +++ /dev/null @@ -1,11 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var obj = {}; -try { - obj.watch(QName(), function () {}); -} catch (exc) { -} -gc(); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/watch-undefined-setter.js b/js/src/tests/js1_8_5/extensions/watch-undefined-setter.js deleted file mode 100644 index 92608de0ef..0000000000 --- a/js/src/tests/js1_8_5/extensions/watch-undefined-setter.js +++ /dev/null @@ -1,19 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: - * Gary Kwong - */ - -var gTestfile = 'watch-undefined-setter.js'; -//----------------------------------------------------------------------------- -var BUGNUMBER = 560277; -var summary = - 'Crash [@ JSObject::getParent] or [@ js_WrapWatchedSetter] or ' + - '[@ js_GetClassPrototype]'; - -this.watch("x", function() { }); -Object.defineProperty(this, "x", { set: undefined, configurable: true }); - -reportCompare(true, true); diff --git a/js/src/tests/js1_8_5/reflect-parse/object-rest.js b/js/src/tests/js1_8_5/reflect-parse/object-rest.js new file mode 100644 index 0000000000..5af06909bc --- /dev/null +++ b/js/src/tests/js1_8_5/reflect-parse/object-rest.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!xulRuntime.shell) + +function property(key, value = key, shorthand = key === value) { + return { key, value, shorthand }; +} + +function assertDestrAssign(src, pattern) { + assertExpr(`(${src} = 0)`, aExpr("=", pattern, lit(0))); +} + +function assertDestrBinding(src, pattern) { + assertDecl(`var ${src} = 0`, varDecl([{id: pattern, init: lit(0)}])); +} + +function test() { + // Target expression must be a simple assignment target or a nested pattern + // in object assignment patterns. + assertDestrAssign("{...x}", objPatt([spread(ident("x"))])); + assertDestrAssign("{...(x)}", objPatt([spread(ident("x"))])); + assertDestrAssign("{...obj.p}", objPatt([spread(dotExpr(ident("obj"), ident("p")))])); + assertDestrAssign("{...{}}", objPatt([spread(objPatt([]))])); + assertDestrAssign("{...[]}", objPatt([spread(arrPatt([]))])); + + // Object binding patterns only allow binding identifiers or nested patterns. + assertDestrBinding("{...x}", objPatt([spread(ident("x"))])); + assertDestrBinding("{...{}}", objPatt([spread(objPatt([]))])); + assertDestrBinding("{...[]}", objPatt([spread(arrPatt([]))])); + + // The rest-property can be preceded by other properties. + for (var assertDestr of [assertDestrAssign, assertDestrBinding]) { + assertDestr("{a, ...x}", objPatt([property(ident("a")), spread(ident("x"))])); + assertDestr("{a: b, ...x}", objPatt([property(ident("a"), ident("b")), spread(ident("x"))])); + assertDestr("{[a]: b, ...x}", objPatt([property(comp(ident("a")), ident("b")), spread(ident("x"))])); + } + + // Tests when __proto__ is used in the object pattern. + for (var assertDestr of [assertDestrAssign, assertDestrBinding]) { + assertDestr("{...__proto__}", objPatt([spread(ident("__proto__"))])); + assertDestr("{__proto__, ...x}", objPatt([property(ident("__proto__")), spread(ident("x"))])); + } + assertDestrAssign("{__proto__: a, ...x}", objPatt([property(lit("__proto__"), ident("a")), spread(ident("x"))])); + assertDestrBinding("{__proto__: a, ...x}", objPatt([property(ident("__proto__"), ident("a")), spread(ident("x"))])); +} + +runtest(test); diff --git a/js/src/tests/js1_8_5/reflect-parse/object-spread.js b/js/src/tests/js1_8_5/reflect-parse/object-spread.js new file mode 100644 index 0000000000..a4b269c409 --- /dev/null +++ b/js/src/tests/js1_8_5/reflect-parse/object-spread.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!xulRuntime.shell) + +function property(key, value = key, shorthand = key === value) { + return { key, value, shorthand }; +} + +function test() { + // Any expression can be spreaded. + assertExpr("({...x})", objExpr([spread(ident("x"))])); + assertExpr("({...f()})", objExpr([spread(callExpr(ident("f"), []))])); + assertExpr("({...123})", objExpr([spread(lit(123))])); + + // Multiple spread expression are allowed. + assertExpr("({...x, ...obj.p})", objExpr([spread(ident("x")), spread(dotExpr(ident("obj"), ident("p")))])); + + // Spread expression can appear anywhere in an object literal. + assertExpr("({p, ...x})", objExpr([property(ident("p")), spread(ident("x"))])); + assertExpr("({p: a, ...x})", objExpr([property(ident("p"), ident("a")), spread(ident("x"))])); + assertExpr("({...x, p: a})", objExpr([spread(ident("x")), property(ident("p"), ident("a"))])); + + // Trailing comma after spread expression is allowed. + assertExpr("({...x,})", objExpr([spread(ident("x"))])); + + // __proto__ is not special in spread expressions. + assertExpr("({...__proto__})", objExpr([spread(ident("__proto__"))])); + assertExpr("({...__proto__, ...__proto__})", objExpr([spread(ident("__proto__")), spread(ident("__proto__"))])); +} + +runtest(test); diff --git a/js/src/tests/js1_8_5/reflect-parse/templateStrings.js b/js/src/tests/js1_8_5/reflect-parse/templateStrings.js index c87ba96b84..fb12afd00f 100644 --- a/js/src/tests/js1_8_5/reflect-parse/templateStrings.js +++ b/js/src/tests/js1_8_5/reflect-parse/templateStrings.js @@ -7,6 +7,8 @@ assertStringExpr("`hey\nthere`", literal("hey\nthere")); assertExpr("`hey${\"there\"}`", templateLit([lit("hey"), lit("there"), lit("")])); assertExpr("`hey${\"there\"}mine`", templateLit([lit("hey"), lit("there"), lit("mine")])); assertExpr("`hey${a == 5}mine`", templateLit([lit("hey"), binExpr("==", ident("a"), lit(5)), lit("mine")])); +assertExpr("func`hey\\x`", taggedTemplate(ident("func"), template(["hey\\x"], [void 0]))); +assertExpr("func`hey${4}\\x`", taggedTemplate(ident("func"), template(["hey","\\x"], ["hey",void 0], lit(4)))); assertExpr("`hey${`there${\"how\"}`}mine`", templateLit([lit("hey"), templateLit([lit("there"), lit("how"), lit("")]), lit("mine")])); assertExpr("func`hey`", taggedTemplate(ident("func"), template(["hey"], ["hey"]))); diff --git a/js/src/tests/js1_8_5/regress/regress-533876.js b/js/src/tests/js1_8_5/regress/regress-533876.js deleted file mode 100644 index e44bc8a4f2..0000000000 --- a/js/src/tests/js1_8_5/regress/regress-533876.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributors: Gary Kwong and Jason Orendorff - */ - -var savedEval = eval; -var x = [0]; -eval(); - -x.__proto__ = this; // x has non-dictionary scope -try { - DIE; -} catch(e) { -} - -delete eval; // force dictionary scope for global -gc(); -eval = savedEval; -var f = eval("(function () { return /x/; })"); -x.watch('x', f); // clone property from global to x, including SPROP_IN_DICTIONARY flag - -reportCompare("ok", "ok", "bug 533876"); diff --git a/js/src/tests/js1_8_5/regress/regress-548276.js b/js/src/tests/js1_8_5/regress/regress-548276.js deleted file mode 100644 index 5e306eba1d..0000000000 --- a/js/src/tests/js1_8_5/regress/regress-548276.js +++ /dev/null @@ -1,10 +0,0 @@ -// |reftest| skip -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributors: Gary Kwong and Jason Orendorff - */ -var obj = {}; -obj.__defineSetter__("x", function() {}); -obj.watch("x", function() {}); -obj.__defineSetter__("x", /a/); diff --git a/js/src/tests/js1_8_5/regress/regress-584355.js b/js/src/tests/js1_8_5/regress/regress-584355.js index 4ddfe65d35..7d1b81a2ec 100644 --- a/js/src/tests/js1_8_5/regress/regress-584355.js +++ b/js/src/tests/js1_8_5/regress/regress-584355.js @@ -1,5 +1,5 @@ var actual; -var expect = "function f() { ff (); }"; +var expect = "function f () { ff (); }"; function fun() { (new Function ("function ff () { actual = '' + ff. caller; } function f () { ff (); } f ();")) (); } diff --git a/js/src/tests/js1_8_5/regress/regress-584648.js b/js/src/tests/js1_8_5/regress/regress-584648.js deleted file mode 100644 index a1635ea518..0000000000 --- a/js/src/tests/js1_8_5/regress/regress-584648.js +++ /dev/null @@ -1,16 +0,0 @@ -// |reftest| skip -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ -// Contributors: Gary Kwong <gary@rumblingedge.com> -// Jason Orendorff <jorendorff@mozilla.com> - -// on a non-global object -var x = {}; -x.watch("p", function () { evalcx(''); }); -x.p = 0; - -// on the global -watch("e", (function () { evalcx(''); })); -e = function () {}; - -reportCompare(0, 0, "ok"); diff --git a/js/src/tests/js1_8_5/regress/regress-635195.js b/js/src/tests/js1_8_5/regress/regress-635195.js deleted file mode 100644 index 89980b05a8..0000000000 --- a/js/src/tests/js1_8_5/regress/regress-635195.js +++ /dev/null @@ -1,8 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var obj = {set x(v) {}}; -obj.watch("x", function() { delete obj.x; }); -obj.x = "hi"; // don't assert - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/regress/regress-636394.js b/js/src/tests/js1_8_5/regress/regress-636394.js deleted file mode 100644 index d1a249786b..0000000000 --- a/js/src/tests/js1_8_5/regress/regress-636394.js +++ /dev/null @@ -1,10 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var a = {p0: function () {}}; -var b = /f/; -b.__proto__ = a; -b.watch("p0", function () {}); -b.p0; - -reportCompare(0, 0, "ok"); diff --git a/js/src/tests/shell/futex.js b/js/src/tests/shell/futex.js index b0951f12ec..8ba61d71c4 100644 --- a/js/src/tests/shell/futex.js +++ b/js/src/tests/shell/futex.js @@ -67,6 +67,8 @@ if (helperThreadCount() === 0) { quit(); } +var mem = new Int32Array(new SharedArrayBuffer(1024)); + //////////////////////////////////////////////////////////// // wait() returns "not-equal" if the value is not the expected one. @@ -102,7 +104,7 @@ dprint("Sleeping for 2 seconds"); sleep(2); dprint("Waking the main thread now"); setSharedArrayBuffer(null); -assertEq(Atomics.wake(mem, 0, 1), 1); // Can fail spuriously but very unlikely +assertEq(Atomics.notify(mem, 0, 1), 1); // Can fail spuriously but very unlikely `); var then = Date.now(); @@ -113,14 +115,14 @@ assertEq(getSharedArrayBuffer(), null); // The worker's clearing of the mbx is v //////////////////////////////////////////////////////////// -// Test the default argument to atomics.wake() +// Test the default argument to atomics.notify() setSharedArrayBuffer(mem.buffer); evalInWorker(` var mem = new Int32Array(getSharedArrayBuffer()); sleep(2); // Probably long enough to avoid a spurious error next -assertEq(Atomics.wake(mem, 0), 1); // Last argument to wake should default to +Infinity +assertEq(Atomics.notify(mem, 0), 1); // Last argument to wake should default to +Infinity `); var then = Date.now(); diff --git a/js/src/tests/test262/built-ins/Object/getOwnPropertyDescriptors/shell.js b/js/src/tests/test262/built-ins/Object/getOwnPropertyDescriptors/shell.js index 6ed766e941..e69de29bb2 100644 --- a/js/src/tests/test262/built-ins/Object/getOwnPropertyDescriptors/shell.js +++ b/js/src/tests/test262/built-ins/Object/getOwnPropertyDescriptors/shell.js @@ -1,27 +0,0 @@ -var assert = { - sameValue: assertEq, - notSameValue(a, b, msg) { - try { - assertEq(a, b); - throw "equal" - } catch (e) { - if (e === "equal") - throw new Error("Assertion failed: expected different values, got " + a); - } - }, - throws(ctor, f) { - var fullmsg; - try { - f(); - } catch (exc) { - if (exc instanceof ctor) - return; - fullmsg = "Assertion failed: expected exception " + ctor.name + ", got " + exc; - } - if (fullmsg === undefined) - fullmsg = "Assertion failed: expected exception " + ctor.name + ", no exception thrown"; - if (msg !== undefined) - fullmsg += " - " + msg; - throw new Error(fullmsg); - } -} diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-1.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-1.js new file mode 100755 index 0000000000..08925e079b --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-1.js @@ -0,0 +1,17 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapped value is not changed when property was made non-configurable. +flags: [noStrict] +---*/ + +function argumentsNonConfigurable(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + + assert.sameValue(a, 1); + assert.sameValue(arguments[0], 1); +} +argumentsNonConfigurable(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-2.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-2.js new file mode 100755 index 0000000000..265481e015 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-2.js @@ -0,0 +1,19 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapping works when property is non-configurable, variable is + changed with SetMutableBinding. +flags: [noStrict] +---*/ + +function argumentsAndSetMutableBinding(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + + a = 2; + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 2); +} +argumentsAndSetMutableBinding(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-3.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-3.js new file mode 100755 index 0000000000..6877a0c9d9 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-3.js @@ -0,0 +1,19 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapping works when property is non-configurable, arguments property + is changed with [[DefineOwnProperty]]. +flags: [noStrict] +---*/ + +function argumentsAndDefineOwnProperty(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + + Object.defineProperty(arguments, "0", {value: 2}); + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 2); +} +argumentsAndDefineOwnProperty(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-4.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-4.js new file mode 100755 index 0000000000..d2ac4d376c --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-4.js @@ -0,0 +1,19 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapping works when property is non-configurable, arguments property + is changed with [[Set]]. +flags: [noStrict] +---*/ + +function argumentsAndSet(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + + arguments[0] = 2; + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 2); +} +argumentsAndSet(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-1.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-1.js new file mode 100755 index 0000000000..67f9713691 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-1.js @@ -0,0 +1,19 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapping works when property is non-configurable, arguments property + was not deleted. [[Delete]] operation returns false. +flags: [noStrict] +---*/ + +function argumentsAndDelete(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + + assert.sameValue(delete arguments[0], false); + assert.sameValue(a, 1); + assert.sameValue(arguments[0], 1); +} +argumentsAndDelete(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-2.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-2.js new file mode 100755 index 0000000000..9a089eaacc --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-2.js @@ -0,0 +1,24 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapping works when property is non-configurable, arguments property + was not deleted. Variable is changed with SetMutableBinding. +flags: [noStrict] +---*/ + +function argumentsAndDeleteSetMutableBinding(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + + // Precondition: Delete is unsuccessful and doesn't affect mapping. + assert.sameValue(delete arguments[0], false); + assert.sameValue(a, 1); + assert.sameValue(arguments[0], 1); + + a = 2; + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 2); +} +argumentsAndDeleteSetMutableBinding(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-3.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-3.js new file mode 100755 index 0000000000..26676a4eee --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-3.js @@ -0,0 +1,25 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapping works when property is non-configurable, arguments property + was not deleted. Arguments property is changed with + [[DefineOwnProperty]]. +flags: [noStrict] +---*/ + +function argumentsAndDeleteDefineOwnProperty(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + + // Precondition: Delete is unsuccessful and doesn't affect mapping. + assert.sameValue(delete arguments[0], false); + assert.sameValue(a, 1); + assert.sameValue(arguments[0], 1); + + Object.defineProperty(arguments, "0", {value: 2}); + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 2); +} +argumentsAndDeleteDefineOwnProperty(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-4.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-4.js new file mode 100755 index 0000000000..ed96a0e67a --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-4.js @@ -0,0 +1,25 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapping works when property is non-configurable, arguments property + was not deleted. Arguments property is changed with + [[Set]]. +flags: [noStrict] +---*/ + +function argumentsAndDeleteSet(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + + // Precondition: Delete is unsuccessful and doesn't affect mapping. + assert.sameValue(delete arguments[0], false); + assert.sameValue(a, 1); + assert.sameValue(arguments[0], 1); + + arguments[0] = 2; + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 2); +} +argumentsAndDeleteSet(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-1.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-1.js new file mode 100755 index 0000000000..cfeba05c48 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-1.js @@ -0,0 +1,24 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapped arguments property is changed to non-configurable and + non-writable. Perform property attribute changes with a single + [[DefineOwnProperty]] call. Mapped values are unchanged, mapping + itself is removed. +flags: [noStrict] +---*/ + +function argumentsNonConfigurableAndNonWritable(a) { + Object.defineProperty(arguments, "0", {configurable: false, writable: false}); + assert.sameValue(a, 1); + assert.sameValue(arguments[0], 1); + + // Postcondition: Arguments mapping is removed. + a = 2; + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 1); +} +argumentsNonConfigurableAndNonWritable(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-2.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-2.js new file mode 100755 index 0000000000..69c0e125ba --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-2.js @@ -0,0 +1,25 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapped arguments property is changed to non-configurable and + non-writable. Perform property attribute changes with two + consecutive [[DefineOwnProperty]] calls. Mapped values are + unchanged, mapping itself is removed. +flags: [noStrict] +---*/ + +function argumentsNonConfigurableThenNonWritable(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + Object.defineProperty(arguments, "0", {writable: false}); + assert.sameValue(a, 1); + assert.sameValue(arguments[0], 1); + + // Postcondition: Arguments mapping is removed. + a = 2; + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 1); +} +argumentsNonConfigurableThenNonWritable(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-3.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-3.js new file mode 100755 index 0000000000..dca0adcd23 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-3.js @@ -0,0 +1,28 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapped arguments property is changed to non-configurable and + non-writable. Perform property attribute changes with two + [[DefineOwnProperty]] calls. Add intervening call to + SetMutableBinding. +flags: [noStrict] +---*/ + +function argumentsNonConfigurableThenNonWritableWithInterveningSetMutableBinding(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + a = 2; + Object.defineProperty(arguments, "0", {writable: false}); + assert.sameValue(a, 2); + // `arguments[0] === 1` per ES2015, Rev 38, April 14, 2015 Final Draft. + // Specification bug: https://bugs.ecmascript.org/show_bug.cgi?id=4371 + assert.sameValue(arguments[0], 2); + + // Postcondition: Arguments mapping is removed. + a = 3; + assert.sameValue(a, 3); + assert.sameValue(arguments[0], 2); +} +argumentsNonConfigurableThenNonWritableWithInterveningSetMutableBinding(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-4.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-4.js new file mode 100755 index 0000000000..80d56fe1c5 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-4.js @@ -0,0 +1,25 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapped arguments property is changed to non-configurable and + non-writable. Perform property attribute changes with two + [[DefineOwnProperty]] calls. Add intervening call to [[Set]]. +flags: [noStrict] +---*/ + +function argumentsNonConfigurableThenNonWritableWithInterveningSet(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + arguments[0] = 2; + Object.defineProperty(arguments, "0", {writable: false}); + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 2); + + // Postcondition: Arguments mapping is removed. + a = 3; + assert.sameValue(a, 3); + assert.sameValue(arguments[0], 2); +} +argumentsNonConfigurableThenNonWritableWithInterveningSet(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-5.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-5.js new file mode 100755 index 0000000000..bbb951502d --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-5.js @@ -0,0 +1,26 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapped arguments property is changed to non-configurable and + non-writable. Perform property attribute changes with two + [[DefineOwnProperty]] calls. Add intervening call to + [[DefineOwnProperty]]. +flags: [noStrict] +---*/ + +function argumentsNonConfigurableThenNonWritableWithDefineOwnProperty(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + Object.defineProperty(arguments, "0", {value: 2}); + Object.defineProperty(arguments, "0", {writable: false}); + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 2); + + // Postcondition: Arguments mapping is removed. + a = 3; + assert.sameValue(a, 3); + assert.sameValue(arguments[0], 2); +} +argumentsNonConfigurableThenNonWritableWithDefineOwnProperty(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-1.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-1.js new file mode 100755 index 0000000000..b918f75a10 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-1.js @@ -0,0 +1,21 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapping works when property is non-configurable, arguments property + was not deleted. [[Delete]] operations throws TypeError if called + from strict-mode code. +flags: [noStrict] +---*/ + +function argumentsAndStrictDelete(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + + var args = arguments; + assert.throws(TypeError, function() { "use strict"; delete args[0]; }); + assert.sameValue(a, 1); + assert.sameValue(arguments[0], 1); +} +argumentsAndStrictDelete(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-2.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-2.js new file mode 100755 index 0000000000..01afbe4de3 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-2.js @@ -0,0 +1,26 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapping works when property is non-configurable, arguments property + was not deleted. [[Delete]] operations throws TypeError if called + from strict-mode code. Variable is changed with SetMutableBinding. +flags: [noStrict] +---*/ + +function argumentsAndStrictDeleteSetMutableBinding(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + + // Precondition: Delete is unsuccessful and doesn't affect mapping. + var args = arguments; + assert.throws(TypeError, function() { "use strict"; delete args[0]; }); + assert.sameValue(a, 1); + assert.sameValue(arguments[0], 1); + + a = 2; + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 2); +} +argumentsAndStrictDeleteSetMutableBinding(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-3.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-3.js new file mode 100755 index 0000000000..9aa2a2ed23 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-3.js @@ -0,0 +1,27 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapping works when property is non-configurable, arguments property + was not deleted. [[Delete]] operations throws TypeError if called + from strict-mode code. Arguments property is changed with + [[DefineOwnProperty]]. +flags: [noStrict] +---*/ + +function argumentsAndStrictDeleteDefineOwnProperty(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + + // Precondition: Delete is unsuccessful and doesn't affect mapping. + var args = arguments; + assert.throws(TypeError, function() { "use strict"; delete args[0]; }); + assert.sameValue(a, 1); + assert.sameValue(arguments[0], 1); + + Object.defineProperty(arguments, "0", {value: 2}); + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 2); +} +argumentsAndStrictDeleteDefineOwnProperty(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-4.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-4.js new file mode 100755 index 0000000000..b480188436 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-4.js @@ -0,0 +1,26 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapping works when property is non-configurable, arguments property + was not deleted. [[Delete]] operations throws TypeError if called + from strict-mode code. Arguments property is changed with [[Set]]. +flags: [noStrict] +---*/ + +function argumentsAndStrictDeleteSet(a) { + Object.defineProperty(arguments, "0", {configurable: false}); + + // Precondition: Delete is unsuccessful and doesn't affect mapping. + var args = arguments; + assert.throws(TypeError, function() { "use strict"; delete args[0]; }); + assert.sameValue(a, 1); + assert.sameValue(arguments[0], 1); + + arguments[0] = 2; + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 2); +} +argumentsAndStrictDeleteSet(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-1.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-1.js new file mode 100755 index 0000000000..2dae076788 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-1.js @@ -0,0 +1,25 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapped arguments property is changed to non-writable and + non-configurable. Perform property attribute changes with two + consecutive [[DefineOwnProperty]] calls. Mapped values are + unchanged, mapping itself is removed. +flags: [noStrict] +---*/ + +function argumentsNonWritableThenNonConfigurable(a) { + Object.defineProperty(arguments, "0", {writable: false}); + Object.defineProperty(arguments, "0", {configurable: false}); + assert.sameValue(a, 1); + assert.sameValue(arguments[0], 1); + + // Postcondition: Arguments mapping is removed. + a = 2; + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 1); +} +argumentsNonWritableThenNonConfigurable(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-2.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-2.js new file mode 100755 index 0000000000..63585b437e --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-2.js @@ -0,0 +1,26 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapped arguments property is changed to non-writable and + non-configurable. Perform property attribute changes with two + [[DefineOwnProperty]] calls. Add intervening call to + SetMutableBinding. +flags: [noStrict] +---*/ + +function argumentsNonWritableThenNonConfigurableWithInterveningSetMutableBinding(a) { + Object.defineProperty(arguments, "0", {writable: false}); + a = 2; + Object.defineProperty(arguments, "0", {configurable: false}); + assert.sameValue(a, 2); + assert.sameValue(arguments[0], 1); + + // Postcondition: Arguments mapping is removed. + a = 3; + assert.sameValue(a, 3); + assert.sameValue(arguments[0], 1); +} +argumentsNonWritableThenNonConfigurableWithInterveningSetMutableBinding(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-3.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-3.js new file mode 100755 index 0000000000..2bd1bc9c12 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-3.js @@ -0,0 +1,25 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapped arguments property is changed to non-writable and + non-configurable. Perform property attribute changes with two + [[DefineOwnProperty]] calls. Add intervening call to [[Set]]. +flags: [noStrict] +---*/ + +function argumentsNonWritableThenNonConfigurableWithInterveningSet(a) { + Object.defineProperty(arguments, "0", {writable: false}); + arguments[0] = 2; + Object.defineProperty(arguments, "0", {configurable: false}); + assert.sameValue(a, 1); + assert.sameValue(arguments[0], 1); + + // Postcondition: Arguments mapping is removed. + a = 3; + assert.sameValue(a, 3); + assert.sameValue(arguments[0], 1); +} +argumentsNonWritableThenNonConfigurableWithInterveningSet(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-4.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-4.js new file mode 100755 index 0000000000..ea40a49a82 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-4.js @@ -0,0 +1,26 @@ +// Copyright (C) 2015 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: Mapped arguments object with non-configurable property +description: > + Mapped arguments property is changed to non-writable and + non-configurable. Perform property attribute changes with two + [[DefineOwnProperty]] calls. Add intervening call to + [[DefineOwnProperty]]. +flags: [noStrict] +---*/ + +function argumentsNonWritableThenNonConfigurableWithInterveningDefineOwnProperty(a) { + Object.defineProperty(arguments, "0", {writable: false}); + Object.defineProperty(arguments, "0", {value: 2}); + Object.defineProperty(arguments, "0", {configurable: false}); + assert.sameValue(a, 1); + assert.sameValue(arguments[0], 2); + + // Postcondition: Arguments mapping is removed. + a = 3; + assert.sameValue(a, 3); + assert.sameValue(arguments[0], 2); +} +argumentsNonWritableThenNonConfigurableWithInterveningDefineOwnProperty(1); diff --git a/js/src/tests/test262/language/arguments-object/mapped/shell.js b/js/src/tests/test262/language/arguments-object/mapped/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/mapped/shell.js diff --git a/js/src/tests/test262/language/arguments-object/shell.js b/js/src/tests/test262/language/arguments-object/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/language/arguments-object/shell.js diff --git a/js/src/tests/test262/language/shell.js b/js/src/tests/test262/language/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/language/shell.js diff --git a/js/src/tests/test262/shell.js b/js/src/tests/test262/shell.js index b70bb5dbba..462ec9cf8b 100644 --- a/js/src/tests/test262/shell.js +++ b/js/src/tests/test262/shell.js @@ -938,3 +938,31 @@ var fnGlobalObject = (function() var global = Function("return this")(); return function fnGlobalObject() { return global; }; })(); + +var assert = { + sameValue: assertEq, + notSameValue(a, b, msg) { + try { + assertEq(a, b); + throw "equal" + } catch (e) { + if (e === "equal") + throw new Error("Assertion failed: expected different values, got " + a); + } + }, + throws(ctor, f) { + var fullmsg; + try { + f(); + } catch (exc) { + if (exc instanceof ctor) + return; + fullmsg = "Assertion failed: expected exception " + ctor.name + ", got " + exc; + } + if (fullmsg === undefined) + fullmsg = "Assertion failed: expected exception " + ctor.name + ", no exception thrown"; + if (msg !== undefined) + fullmsg += " - " + msg; + throw new Error(fullmsg); + } +} diff --git a/js/src/tests/user.js b/js/src/tests/user.js index 732bbbd1a2..e75593ab18 100755 --- a/js/src/tests/user.js +++ b/js/src/tests/user.js @@ -22,7 +22,6 @@ user_pref("javascript.options.strict", false); user_pref("javascript.options.werror", false); user_pref("toolkit.startup.max_resumed_crashes", -1); user_pref("security.turn_off_all_security_so_that_viruses_can_take_over_this_computer", true); -user_pref("toolkit.telemetry.enabled", false); user_pref("browser.safebrowsing.phishing.enabled", false); user_pref("browser.safebrowsing.malware.enabled", false); user_pref("browser.safebrowsing.forbiddenURIs.enabled", false); diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index 717aa10508..66e0f40a27 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -214,7 +214,7 @@ ArgumentsObject::createTemplateObject(JSContext* cx, bool mapped) ? &MappedArgumentsObject::class_ : &UnmappedArgumentsObject::class_; - RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx)); + RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, cx->global())); if (!proto) return nullptr; @@ -475,7 +475,7 @@ MappedArgSetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue attrs &= (JSPROP_ENUMERATE | JSPROP_PERMANENT); /* only valid attributes */ RootedFunction callee(cx, &argsobj->callee()); - RootedScript script(cx, callee->getOrCreateScript(cx)); + RootedScript script(cx, JSFunction::getOrCreateScript(cx, callee)); if (!script) return false; @@ -590,6 +590,64 @@ MappedArgumentsObject::obj_enumerate(JSContext* cx, HandleObject obj) return true; } +// ES 2017 draft 9.4.4.2 +/* static */ bool +MappedArgumentsObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, + Handle<PropertyDescriptor> desc, ObjectOpResult& result) +{ + // Step 1. + Rooted<MappedArgumentsObject*> argsobj(cx, &obj->as<MappedArgumentsObject>()); + + // Steps 2-3. + bool isMapped = false; + if (JSID_IS_INT(id)) { + unsigned arg = unsigned(JSID_TO_INT(id)); + isMapped = arg < argsobj->initialLength() && !argsobj->isElementDeleted(arg); + } + + // Step 4. + Rooted<PropertyDescriptor> newArgDesc(cx, desc); + if (!desc.isAccessorDescriptor() && isMapped) { + // In this case the live mapping is supposed to keep working, + // we have to pass along the Getter/Setter otherwise they are overwritten. + newArgDesc.setGetter(MappedArgGetter); + newArgDesc.setSetter(MappedArgSetter); + } + + // Steps 5-6. NativeDefineProperty will lookup [[Value]] for us. + if (!NativeDefineProperty(cx, obj.as<NativeObject>(), id, newArgDesc, result)) + return false; + // Step 7. + if (!result.ok()) + return true; + + // Step 8. + if (isMapped) { + unsigned arg = unsigned(JSID_TO_INT(id)); + if (desc.isAccessorDescriptor()) { + if (!argsobj->markElementDeleted(cx, arg)) + return false; + } else { + if (desc.hasValue()) { + RootedFunction callee(cx, &argsobj->callee()); + RootedScript script(cx, JSFunction::getOrCreateScript(cx, callee)); + if (!script) + return false; + argsobj->setElement(cx, arg, desc.value()); + if (arg < script->functionNonDelazifying()->nargs()) + TypeScript::SetArgument(cx, script, arg, desc.value()); + } + if (desc.hasWritable() && !desc.writable()) { + if (!argsobj->markElementDeleted(cx, arg)) + return false; + } + } + } + + // Step 9. + return result.succeed(); +} + static bool UnmappedArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) { @@ -804,6 +862,11 @@ const ClassOps MappedArgumentsObject::classOps_ = { ArgumentsObject::trace }; +const ObjectOps MappedArgumentsObject::objectOps_ = { + nullptr, /* lookupProperty */ + MappedArgumentsObject::obj_defineProperty +}; + const Class MappedArgumentsObject::class_ = { "Arguments", JSCLASS_DELAY_METADATA_BUILDER | @@ -811,7 +874,10 @@ const Class MappedArgumentsObject::class_ = { JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_SKIP_NURSERY_FINALIZE | JSCLASS_BACKGROUND_FINALIZE, - &MappedArgumentsObject::classOps_ + &MappedArgumentsObject::classOps_, + nullptr, + nullptr, + &MappedArgumentsObject::objectOps_ }; /* diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h index 247c7cd943..988e41951b 100644 --- a/js/src/vm/ArgumentsObject.h +++ b/js/src/vm/ArgumentsObject.h @@ -389,6 +389,7 @@ class ArgumentsObject : public NativeObject class MappedArgumentsObject : public ArgumentsObject { static const ClassOps classOps_; + static const ObjectOps objectOps_; public: static const Class class_; @@ -410,6 +411,8 @@ class MappedArgumentsObject : public ArgumentsObject private: static bool obj_enumerate(JSContext* cx, HandleObject obj); static bool obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp); + static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, + Handle<JS::PropertyDescriptor> desc, ObjectOpResult& result); }; class UnmappedArgumentsObject : public ArgumentsObject diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index 1053fa99d7..392724b218 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -140,7 +140,7 @@ static const Class ArrayBufferObjectProtoClass = { static JSObject* CreateArrayBufferPrototype(JSContext* cx, JSProtoKey key) { - return cx->global()->createBlankPrototype(cx, &ArrayBufferObjectProtoClass); + return GlobalObject::createBlankPrototype(cx, cx->global(), &ArrayBufferObjectProtoClass); } static const ClassOps ArrayBufferObjectClassOps = { @@ -344,7 +344,7 @@ ArrayBufferObject::detach(JSContext* cx, Handle<ArrayBufferObject*> buffer, // Make sure the global object's group has been instantiated, so the // flag change will be observed. AutoEnterOOMUnsafeRegion oomUnsafe; - if (!cx->global()->getGroup(cx)) + if (!JSObject::getGroup(cx, cx->global())) oomUnsafe.crash("ArrayBufferObject::detach"); MarkObjectGroupFlags(cx, cx->global(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER); cx->compartment()->detachedTypedObjects = 1; diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index 6614f5220a..e9c9bc0e06 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -457,8 +457,8 @@ ClampDoubleToUint8(const double x); struct uint8_clamped { uint8_t val; - uint8_clamped() { } - uint8_clamped(const uint8_clamped& other) : val(other.val) { } + uint8_clamped() = default; + uint8_clamped(const uint8_clamped& other) = default; // invoke our assignment helpers for constructor conversion explicit uint8_clamped(uint8_t x) { *this = x; } @@ -469,10 +469,7 @@ struct uint8_clamped { explicit uint8_clamped(int32_t x) { *this = x; } explicit uint8_clamped(double x) { *this = x; } - uint8_clamped& operator=(const uint8_clamped& x) { - val = x.val; - return *this; - } + uint8_clamped& operator=(const uint8_clamped& x) = default; uint8_clamped& operator=(uint8_t x) { val = x; diff --git a/js/src/vm/AsyncFunction.cpp b/js/src/vm/AsyncFunction.cpp index f50c871141..e14b77424a 100644 --- a/js/src/vm/AsyncFunction.cpp +++ b/js/src/vm/AsyncFunction.cpp @@ -118,7 +118,7 @@ js::WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleOb RootedAtom funName(cx, unwrapped->explicitName()); uint16_t length; - if (!unwrapped->getLength(cx, &length)) + if (!JSFunction::getLength(cx, unwrapped, &length)) return nullptr; // Steps 3 (partially). diff --git a/js/src/vm/Caches.h b/js/src/vm/Caches.h index 91a78bdc82..b11dd9dcbd 100644 --- a/js/src/vm/Caches.h +++ b/js/src/vm/Caches.h @@ -7,6 +7,8 @@ #ifndef vm_Caches_h #define vm_Caches_h +#include <new> + #include "jsatom.h" #include "jsbytecode.h" #include "jsobj.h" @@ -191,14 +193,20 @@ class NewObjectCache char templateObject[MAX_OBJ_SIZE]; }; - Entry entries[41]; // TODO: reconsider size + using EntryArray = Entry[41]; // TODO: reconsider size; + EntryArray entries; public: - typedef int EntryIndex; + using EntryIndex = int; + + NewObjectCache() + : entries{} // zeroes out the array + {} - NewObjectCache() { mozilla::PodZero(this); } - void purge() { mozilla::PodZero(this); } + void purge() { + new (&entries) EntryArray{}; // zeroes out the array + } /* Remove any cached items keyed on moved objects. */ void clearNurseryObjects(JSRuntime* rt); diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index e971dc8443..fd1c9f5e63 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -38,6 +38,7 @@ macro(Bool32x4, Bool32x4, "Bool32x4") \ macro(Bool64x2, Bool64x2, "Bool64x2") \ macro(boundWithSpace, boundWithSpace, "bound ") \ + macro(break, break_, "break") \ macro(breakdown, breakdown, "breakdown") \ macro(buffer, buffer, "buffer") \ macro(builder, builder, "builder") \ @@ -52,8 +53,10 @@ macro(callee, callee, "callee") \ macro(caller, caller, "caller") \ macro(callFunction, callFunction, "callFunction") \ + macro(case, case_, "case") \ macro(caseFirst, caseFirst, "caseFirst") \ - macro(class_, class_, "class") \ + macro(catch, catch_, "catch") \ + macro(class, class_, "class") \ macro(close, close, "close") \ macro(Collator, Collator, "Collator") \ macro(CollatorCompareGet, CollatorCompareGet, "Intl_Collator_compare_get") \ @@ -62,10 +65,14 @@ macro(comma, comma, ",") \ macro(compare, compare, "compare") \ macro(configurable, configurable, "configurable") \ + macro(const, const_, "const") \ macro(construct, construct, "construct") \ macro(constructContentFunction, constructContentFunction, "constructContentFunction") \ macro(constructor, constructor, "constructor") \ + macro(continue, continue_, "continue") \ macro(ConvertAndCopyTo, ConvertAndCopyTo, "ConvertAndCopyTo") \ + macro(CopyDataProperties, CopyDataProperties, "CopyDataProperties") \ + macro(CopyDataPropertiesUnfiltered, CopyDataPropertiesUnfiltered, "CopyDataPropertiesUnfiltered") \ macro(copyWithin, copyWithin, "copyWithin") \ macro(count, count, "count") \ macro(CreateResolvingFunctions, CreateResolvingFunctions, "CreateResolvingFunctions") \ @@ -76,28 +83,32 @@ macro(DateTimeFormatFormatToParts, DateTimeFormatFormatToParts, "Intl_DateTimeFormat_formatToParts") \ macro(day, day, "day") \ macro(dayPeriod, dayPeriod, "dayPeriod") \ + macro(debugger, debugger, "debugger") \ macro(decodeURI, decodeURI, "decodeURI") \ macro(decodeURIComponent, decodeURIComponent, "decodeURIComponent") \ macro(DefaultBaseClassConstructor, DefaultBaseClassConstructor, "DefaultBaseClassConstructor") \ macro(DefaultDerivedClassConstructor, DefaultDerivedClassConstructor, "DefaultDerivedClassConstructor") \ - macro(default_, default_, "default") \ + macro(default, default_, "default") \ macro(defineGetter, defineGetter, "__defineGetter__") \ macro(defineProperty, defineProperty, "defineProperty") \ macro(defineSetter, defineSetter, "__defineSetter__") \ macro(delete, delete_, "delete") \ macro(deleteProperty, deleteProperty, "deleteProperty") \ macro(displayURL, displayURL, "displayURL") \ + macro(do, do_, "do") \ macro(done, done, "done") \ macro(dotGenerator, dotGenerator, ".generator") \ macro(dotThis, dotThis, ".this") \ macro(each, each, "each") \ macro(elementType, elementType, "elementType") \ + macro(else, else_, "else") \ macro(empty, empty, "") \ macro(emptyRegExp, emptyRegExp, "(?:)") \ macro(encodeURI, encodeURI, "encodeURI") \ macro(encodeURIComponent, encodeURIComponent, "encodeURIComponent") \ macro(endTimestamp, endTimestamp, "endTimestamp") \ macro(entries, entries, "entries") \ + macro(enum, enum_, "enum") \ macro(enumerable, enumerable, "enumerable") \ macro(enumerate, enumerate, "enumerate") \ macro(era, era, "era") \ @@ -105,20 +116,26 @@ macro(escape, escape, "escape") \ macro(eval, eval, "eval") \ macro(exec, exec, "exec") \ + macro(export, export_, "export") \ + macro(extends, extends, "extends") \ macro(false, false_, "false") \ macro(fieldOffsets, fieldOffsets, "fieldOffsets") \ macro(fieldTypes, fieldTypes, "fieldTypes") \ macro(fileName, fileName, "fileName") \ macro(fill, fill, "fill") \ + macro(finally, finally_, "finally") \ macro(find, find, "find") \ macro(findIndex, findIndex, "findIndex") \ macro(firstDayOfWeek, firstDayOfWeek, "firstDayOfWeek") \ macro(fix, fix, "fix") \ macro(flags, flags, "flags") \ + macro(flat, flat, "flat") \ + macro(flatMap, flatMap, "flatMap") \ macro(float32, float32, "float32") \ macro(Float32x4, Float32x4, "Float32x4") \ macro(float64, float64, "float64") \ macro(Float64x2, Float64x2, "Float64x2") \ + macro(for, for_, "for") \ macro(forceInterpreter, forceInterpreter, "forceInterpreter") \ macro(forEach, forEach, "forEach") \ macro(format, format, "format") \ @@ -144,8 +161,12 @@ macro(hasOwn, hasOwn, "hasOwn") \ macro(hasOwnProperty, hasOwnProperty, "hasOwnProperty") \ macro(hour, hour, "hour") \ + macro(if, if_, "if") \ macro(ignoreCase, ignoreCase, "ignoreCase") \ macro(ignorePunctuation, ignorePunctuation, "ignorePunctuation") \ + macro(implements, implements, "implements") \ + macro(import, import, "import") \ + macro(in, in, "in") \ macro(includes, includes, "includes") \ macro(incumbentGlobal, incumbentGlobal, "incumbentGlobal") \ macro(index, index, "index") \ @@ -156,12 +177,14 @@ macro(innermost, innermost, "innermost") \ macro(inNursery, inNursery, "inNursery") \ macro(input, input, "input") \ + macro(instanceof, instanceof, "instanceof") \ macro(int8, int8, "int8") \ macro(int16, int16, "int16") \ macro(int32, int32, "int32") \ macro(Int8x16, Int8x16, "Int8x16") \ macro(Int16x8, Int16x8, "Int16x8") \ macro(Int32x4, Int32x4, "Int32x4") \ + macro(interface, interface, "interface") \ macro(InterpretGeneratorResume, InterpretGeneratorResume, "InterpretGeneratorResume") \ macro(isEntryPoint, isEntryPoint, "isEntryPoint") \ macro(isExtensible, isExtensible, "isExtensible") \ @@ -215,6 +238,7 @@ macro(noFilename, noFilename, "noFilename") \ macro(nonincrementalReason, nonincrementalReason, "nonincrementalReason") \ macro(noStack, noStack, "noStack") \ + macro(notes, notes, "notes") \ macro(NumberFormat, NumberFormat, "NumberFormat") \ macro(NumberFormatFormatGet, NumberFormatFormatGet, "Intl_NumberFormat_format_get") \ macro(numeric, numeric, "numeric") \ @@ -236,13 +260,18 @@ macro(other, other, "other") \ macro(outOfMemory, outOfMemory, "out of memory") \ macro(ownKeys, ownKeys, "ownKeys") \ + macro(Object_valueOf, Object_valueOf, "Object_valueOf") \ + macro(package, package, "package") \ macro(parseFloat, parseFloat, "parseFloat") \ macro(parseInt, parseInt, "parseInt") \ macro(pattern, pattern, "pattern") \ macro(pending, pending, "pending") \ + macro(public, public_, "public") \ macro(preventExtensions, preventExtensions, "preventExtensions") \ + macro(private, private_, "private") \ macro(promise, promise, "promise") \ macro(propertyIsEnumerable, propertyIsEnumerable, "propertyIsEnumerable") \ + macro(protected, protected_, "protected") \ macro(proto, proto, "__proto__") \ macro(prototype, prototype, "prototype") \ macro(proxy, proxy, "proxy") \ @@ -291,10 +320,12 @@ macro(StructType, StructType, "StructType") \ macro(style, style, "style") \ macro(super, super, "super") \ + macro(switch, switch_, "switch") \ macro(Symbol_iterator_fun, Symbol_iterator_fun, "[Symbol.iterator]") \ macro(target, target, "target") \ macro(test, test, "test") \ macro(then, then, "then") \ + macro(this, this_, "this") \ macro(throw, throw_, "throw") \ macro(timestamp, timestamp, "timestamp") \ macro(timeZone, timeZone, "timeZone") \ @@ -307,7 +338,9 @@ macro(toString, toString, "toString") \ macro(toUTCString, toUTCString, "toUTCString") \ macro(true, true_, "true") \ + macro(try, try_, "try") \ macro(type, type, "type") \ + macro(typeof, typeof_, "typeof") \ macro(uint8, uint8, "uint8") \ macro(uint8Clamped, uint8Clamped, "uint8Clamped") \ macro(uint16, uint16, "uint16") \ @@ -327,6 +360,7 @@ macro(useAsm, useAsm, "use asm") \ macro(useGrouping, useGrouping, "useGrouping") \ macro(useStrict, useStrict, "use strict") \ + macro(void, void_, "void") \ macro(value, value, "value") \ macro(valueOf, valueOf, "valueOf") \ macro(values, values, "values") \ @@ -341,6 +375,8 @@ macro(weekday, weekday, "weekday") \ macro(weekendEnd, weekendEnd, "weekendEnd") \ macro(weekendStart, weekendStart, "weekendStart") \ + macro(while, while_, "while") \ + macro(with, with, "with") \ macro(writable, writable, "writable") \ macro(year, year, "year") \ macro(yield, yield, "yield") \ diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index d16781326d..d68d1b75eb 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -224,7 +224,7 @@ EnsureFunctionHasScript(JSContext* cx, HandleFunction fun) { if (fun->isInterpretedLazy()) { AutoCompartment ac(cx, fun); - return !!fun->getOrCreateScript(cx); + return !!JSFunction::getOrCreateScript(cx, fun); } return true; } @@ -2234,7 +2234,7 @@ Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame RootedAtom ctorName(cx); { AutoCompartment ac(cx, obj); - if (!obj->constructorDisplayAtom(cx, &ctorName)) + if (!JSObject::constructorDisplayAtom(cx, obj, &ctorName)) return false; } @@ -7227,8 +7227,8 @@ static const JSFunctionSpec DebuggerSource_methods[] = { /* static */ NativeObject* DebuggerFrame::initClass(JSContext* cx, HandleObject dbgCtor, HandleObject obj) { - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); - RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); + RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); return InitClass(cx, dbgCtor, objProto, &class_, construct, 0, properties_, methods_, nullptr, nullptr); @@ -8666,6 +8666,14 @@ DebuggerObject::errorMessageNameGetter(JSContext *cx, unsigned argc, Value* vp) } /* static */ bool +DebuggerObject::errorNotesGetter(JSContext *cx, unsigned argc, Value* vp) +{ + THIS_DEBUGOBJECT(cx, argc, vp, "get errorNotes", args, object) + + return DebuggerObject::getErrorNotes(cx, object, args.rval()); +} + +/* static */ bool DebuggerObject::errorLineNumberGetter(JSContext *cx, unsigned argc, Value* vp) { THIS_DEBUGOBJECT(cx, argc, vp, "get errorLineNumber", args, object) @@ -9324,6 +9332,7 @@ const JSPropertySpec DebuggerObject::properties_[] = { JS_PSG("global", DebuggerObject::globalGetter, 0), JS_PSG("allocationSite", DebuggerObject::allocationSiteGetter, 0), JS_PSG("errorMessageName", DebuggerObject::errorMessageNameGetter, 0), + JS_PSG("errorNotes", DebuggerObject::errorNotesGetter, 0), JS_PSG("errorLineNumber", DebuggerObject::errorLineNumberGetter, 0), JS_PSG("errorColumnNumber", DebuggerObject::errorColumnNumberGetter, 0), JS_PSG("isProxy", DebuggerObject::isProxyGetter, 0), @@ -9376,8 +9385,8 @@ const JSFunctionSpec DebuggerObject::methods_[] = { /* static */ NativeObject* DebuggerObject::initClass(JSContext* cx, HandleObject obj, HandleObject debugCtor) { - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); - RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); + RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); RootedNativeObject objectProto(cx, InitClass(cx, debugCtor, objProto, &class_, construct, 0, properties_, @@ -9611,7 +9620,7 @@ DebuggerObject::getBoundArguments(JSContext* cx, HandleDebuggerObject object, if (!result.resize(length)) return false; for (size_t i = 0; i < length; i++) { - result[i].set(referent->getBoundFunctionArgument(cx, i)); + result[i].set(referent->getBoundFunctionArgument(i)); if (!dbg->wrapDebuggeeValue(cx, result[i])) return false; } @@ -9695,6 +9704,30 @@ DebuggerObject::getErrorMessageName(JSContext* cx, HandleDebuggerObject object, } /* static */ bool +DebuggerObject::getErrorNotes(JSContext* cx, HandleDebuggerObject object, + MutableHandleValue result) +{ + RootedObject referent(cx, object->referent()); + JSErrorReport* report; + if (!getErrorReport(cx, referent, report)) + return false; + + if (!report) { + result.setUndefined(); + return true; + } + + RootedObject errorNotesArray(cx, CreateErrorNotesArray(cx, report)); + if (!errorNotesArray) + return false; + + if (!cx->compartment()->wrap(cx, &errorNotesArray)) + return false; + result.setObject(*errorNotesArray); + return true; +} + +/* static */ bool DebuggerObject::getErrorLineNumber(JSContext* cx, HandleDebuggerObject object, MutableHandleValue result) { @@ -10577,8 +10610,8 @@ const JSFunctionSpec DebuggerEnvironment::methods_[] = { /* static */ NativeObject* DebuggerEnvironment::initClass(JSContext* cx, HandleObject dbgCtor, HandleObject obj) { - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); - RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); + RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); return InitClass(cx, dbgCtor, objProto, &DebuggerEnvironment::class_, construct, 0, properties_, methods_, nullptr, nullptr); @@ -10774,7 +10807,8 @@ DebuggerEnvironment::getVariable(JSContext* cx, HandleDebuggerEnvironment enviro // // See wrapDebuggeeValue for how the sentinel values are wrapped. if (referent->is<DebugEnvironmentProxy>()) { - if (!referent->as<DebugEnvironmentProxy>().getMaybeSentinelValue(cx, id, result)) + Rooted<DebugEnvironmentProxy*> env(cx, &referent->as<DebugEnvironmentProxy>()); + if (!DebugEnvironmentProxy::getMaybeSentinelValue(cx, env, id, result)) return false; } else { if (!GetProperty(cx, referent, referent, id, result)) @@ -10942,9 +10976,9 @@ JS_DefineDebuggerObject(JSContext* cx, HandleObject obj) memoryProto(cx); RootedObject debuggeeWouldRunProto(cx); RootedValue debuggeeWouldRunCtor(cx); - Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); + Handle<GlobalObject*> global = obj.as<GlobalObject>(); - objProto = global->getOrCreateObjectPrototype(cx); + objProto = GlobalObject::getOrCreateObjectPrototype(cx, global); if (!objProto) return false; debugProto = InitClass(cx, obj, diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index 3239ade6dc..cdcf2d67fb 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -1246,6 +1246,8 @@ class DebuggerObject : public NativeObject MutableHandleObject result); static MOZ_MUST_USE bool getErrorMessageName(JSContext* cx, HandleDebuggerObject object, MutableHandleString result); + static MOZ_MUST_USE bool getErrorNotes(JSContext* cx, HandleDebuggerObject object, + MutableHandleValue result); static MOZ_MUST_USE bool getErrorLineNumber(JSContext* cx, HandleDebuggerObject object, MutableHandleValue result); static MOZ_MUST_USE bool getErrorColumnNumber(JSContext* cx, HandleDebuggerObject object, @@ -1371,6 +1373,7 @@ class DebuggerObject : public NativeObject static MOZ_MUST_USE bool globalGetter(JSContext* cx, unsigned argc, Value* vp); static MOZ_MUST_USE bool allocationSiteGetter(JSContext* cx, unsigned argc, Value* vp); static MOZ_MUST_USE bool errorMessageNameGetter(JSContext* cx, unsigned argc, Value* vp); + static MOZ_MUST_USE bool errorNotesGetter(JSContext* cx, unsigned argc, Value* vp); static MOZ_MUST_USE bool errorLineNumberGetter(JSContext* cx, unsigned argc, Value* vp); static MOZ_MUST_USE bool errorColumnNumberGetter(JSContext* cx, unsigned argc, Value* vp); static MOZ_MUST_USE bool isProxyGetter(JSContext* cx, unsigned argc, Value* vp); diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp index 9b20c2b9c8..c95bb0597a 100644 --- a/js/src/vm/EnvironmentObject.cpp +++ b/js/src/vm/EnvironmentObject.cpp @@ -408,7 +408,6 @@ const ObjectOps ModuleEnvironmentObject::objectOps_ = { ModuleEnvironmentObject::setProperty, ModuleEnvironmentObject::getOwnPropertyDescriptor, ModuleEnvironmentObject::deleteProperty, - nullptr, nullptr, /* watch/unwatch */ nullptr, /* getElements */ ModuleEnvironmentObject::enumerate, nullptr @@ -790,7 +789,6 @@ static const ObjectOps WithEnvironmentObjectOps = { with_SetProperty, with_GetOwnPropertyDescriptor, with_DeleteProperty, - nullptr, nullptr, /* watch/unwatch */ nullptr, /* getElements */ nullptr, /* enumerate (native enumeration of target doesn't work) */ nullptr, @@ -816,7 +814,7 @@ NonSyntacticVariablesObject::create(JSContext* cx) return nullptr; MOZ_ASSERT(obj->isUnqualifiedVarObj()); - if (!obj->setQualifiedVarObj(cx)) + if (!JSObject::setQualifiedVarObj(cx, obj)) return nullptr; obj->initEnclosingEnvironment(&cx->global()->lexicalEnvironment()); @@ -957,7 +955,7 @@ LexicalEnvironmentObject::createHollowForDebug(JSContext* cx, Handle<LexicalScop return nullptr; } - if (!env->setFlags(cx, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE)) + if (!JSObject::setFlags(cx, env, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE)) return nullptr; env->initScopeUnchecked(scope); @@ -1159,7 +1157,6 @@ static const ObjectOps RuntimeLexicalErrorObjectObjectOps = { lexicalError_SetProperty, lexicalError_GetOwnPropertyDescriptor, lexicalError_DeleteProperty, - nullptr, nullptr, /* watch/unwatch */ nullptr, /* getElements */ nullptr, /* enumerate (native enumeration of target doesn't work) */ nullptr, /* this */ @@ -1425,7 +1422,8 @@ class DebugEnvironmentProxyHandler : public BaseProxyHandler /* Handle unaliased formals, vars, lets, and consts at function scope. */ if (env->is<CallObject>()) { CallObject& callobj = env->as<CallObject>(); - RootedScript script(cx, callobj.callee().getOrCreateScript(cx)); + RootedFunction fun(cx, &callobj.callee()); + RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun)); if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx)) return false; @@ -2233,11 +2231,11 @@ DebugEnvironmentProxy::isForDeclarative() const e.is<LexicalEnvironmentObject>(); } -bool -DebugEnvironmentProxy::getMaybeSentinelValue(JSContext* cx, HandleId id, MutableHandleValue vp) +/* static */ bool +DebugEnvironmentProxy::getMaybeSentinelValue(JSContext* cx, Handle<DebugEnvironmentProxy*> env, + HandleId id, MutableHandleValue vp) { - Rooted<DebugEnvironmentProxy*> self(cx, this); - return DebugEnvironmentProxyHandler::singleton.getMaybeSentinelValue(cx, self, id, vp); + return DebugEnvironmentProxyHandler::singleton.getMaybeSentinelValue(cx, env, id, vp); } bool @@ -2960,7 +2958,7 @@ js::GetDebugEnvironmentForFunction(JSContext* cx, HandleFunction fun) MOZ_ASSERT(CanUseDebugEnvironmentMaps(cx)); if (!DebugEnvironments::updateLiveEnvironments(cx)) return nullptr; - JSScript* script = fun->getOrCreateScript(cx); + JSScript* script = JSFunction::getOrCreateScript(cx, fun); if (!script) return nullptr; EnvironmentIter ei(cx, fun->environment(), script->enclosingScope()); @@ -3468,11 +3466,13 @@ RemoveReferencedNames(JSContext* cx, HandleScript script, PropertyNameSet& remai if (script->hasObjects()) { ObjectArray* objects = script->objects(); + RootedFunction fun(cx); + RootedScript innerScript(cx); for (size_t i = 0; i < objects->length; i++) { JSObject* obj = objects->vector[i]; if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) { - JSFunction* fun = &obj->as<JSFunction>(); - RootedScript innerScript(cx, fun->getOrCreateScript(cx)); + fun = &obj->as<JSFunction>(); + innerScript = JSFunction::getOrCreateScript(cx, fun); if (!innerScript) return false; @@ -3535,11 +3535,13 @@ AnalyzeEntrainedVariablesInScript(JSContext* cx, HandleScript script, HandleScri if (innerScript->hasObjects()) { ObjectArray* objects = innerScript->objects(); + RootedFunction fun(cx); + RootedScript innerInnerScript(cx); for (size_t i = 0; i < objects->length; i++) { JSObject* obj = objects->vector[i]; if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) { - JSFunction* fun = &obj->as<JSFunction>(); - RootedScript innerInnerScript(cx, fun->getOrCreateScript(cx)); + fun = &obj->as<JSFunction>(); + innerInnerScript = JSFunction::getOrCreateScript(cx, fun); if (!innerInnerScript || !AnalyzeEntrainedVariablesInScript(cx, script, innerInnerScript)) { @@ -3570,11 +3572,13 @@ js::AnalyzeEntrainedVariables(JSContext* cx, HandleScript script) return true; ObjectArray* objects = script->objects(); + RootedFunction fun(cx); + RootedScript innerScript(cx); for (size_t i = 0; i < objects->length; i++) { JSObject* obj = objects->vector[i]; if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) { - JSFunction* fun = &obj->as<JSFunction>(); - RootedScript innerScript(cx, fun->getOrCreateScript(cx)); + fun = &obj->as<JSFunction>(); + innerScript = JSFunction::getOrCreateScript(cx, fun); if (!innerScript) return false; diff --git a/js/src/vm/EnvironmentObject.h b/js/src/vm/EnvironmentObject.h index 0322861164..c527cd1b0d 100644 --- a/js/src/vm/EnvironmentObject.h +++ b/js/src/vm/EnvironmentObject.h @@ -872,7 +872,8 @@ class DebugEnvironmentProxy : public ProxyObject // Get a property by 'id', but returns sentinel values instead of throwing // on exceptional cases. - bool getMaybeSentinelValue(JSContext* cx, HandleId id, MutableHandleValue vp); + static bool getMaybeSentinelValue(JSContext* cx, Handle<DebugEnvironmentProxy*> env, + HandleId id, MutableHandleValue vp); // Returns true iff this is a function environment with its own this-binding // (all functions except arrow functions and generator expression lambdas). diff --git a/js/src/vm/ErrorObject.cpp b/js/src/vm/ErrorObject.cpp index d8d29830b1..271132801c 100644 --- a/js/src/vm/ErrorObject.cpp +++ b/js/src/vm/ErrorObject.cpp @@ -29,11 +29,11 @@ js::ErrorObject::assignInitialShape(ExclusiveContext* cx, Handle<ErrorObject*> o { MOZ_ASSERT(obj->empty()); - if (!obj->addDataProperty(cx, cx->names().fileName, FILENAME_SLOT, 0)) + if (!NativeObject::addDataProperty(cx, obj, cx->names().fileName, FILENAME_SLOT, 0)) return nullptr; - if (!obj->addDataProperty(cx, cx->names().lineNumber, LINENUMBER_SLOT, 0)) + if (!NativeObject::addDataProperty(cx, obj, cx->names().lineNumber, LINENUMBER_SLOT, 0)) return nullptr; - return obj->addDataProperty(cx, cx->names().columnNumber, COLUMNNUMBER_SLOT, 0); + return NativeObject::addDataProperty(cx, obj, cx->names().columnNumber, COLUMNNUMBER_SLOT, 0); } /* static */ bool @@ -57,7 +57,7 @@ js::ErrorObject::init(JSContext* cx, Handle<ErrorObject*> obj, JSExnType type, // |new Error()|. RootedShape messageShape(cx); if (message) { - messageShape = obj->addDataProperty(cx, cx->names().message, MESSAGE_SLOT, 0); + messageShape = NativeObject::addDataProperty(cx, obj, cx->names().message, MESSAGE_SLOT, 0); if (!messageShape) return false; MOZ_ASSERT(messageShape->slot() == MESSAGE_SLOT); diff --git a/js/src/vm/ErrorReporting.cpp b/js/src/vm/ErrorReporting.cpp new file mode 100644 index 0000000000..5877f3a4b4 --- /dev/null +++ b/js/src/vm/ErrorReporting.cpp @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "vm/ErrorReporting.h" + +#include "mozilla/Move.h" + +#include <stdarg.h> + +#include "jscntxt.h" +#include "jsexn.h" + +using mozilla::Move; + +using JS::UniqueTwoByteChars; + +void +CallWarningReporter(JSContext* cx, JSErrorReport* reportp) +{ + MOZ_ASSERT(reportp); + MOZ_ASSERT(JSREPORT_IS_WARNING(reportp->flags)); + + if (JS::WarningReporter warningReporter = cx->runtime()->warningReporter) + warningReporter(cx, reportp); +} + +void +CompileError::throwError(JSContext* cx) +{ + if (JSREPORT_IS_WARNING(flags)) { + CallWarningReporter(cx, this); + return; + } + + // If there's a runtime exception type associated with this error + // number, set that as the pending exception. For errors occuring at + // compile time, this is very likely to be a JSEXN_SYNTAXERR. + // + // If an exception is thrown but not caught, the JSREPORT_EXCEPTION + // flag will be set in report.flags. Proper behavior for an error + // reporter is to ignore a report with this flag for all but top-level + // compilation errors. The exception will remain pending, and so long + // as the non-top-level "load", "eval", or "compile" native function + // returns false, the top-level reporter will eventually receive the + // uncaught exception report. + ErrorToException(cx, this, nullptr, nullptr); +} + +bool +ReportCompileWarning(JSContext* cx, ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes, + unsigned flags, unsigned errorNumber, va_list args) +{ + // On the main thread, report the error immediately. When compiling off + // thread, save the error so that the thread finishing the parse can report + // it later. + CompileError tempErr; + CompileError* err = &tempErr; + if (!cx->isJSContext() && !cx->addPendingCompileError(&err)) { + return false; + } + + err->notes = Move(notes); + err->flags = flags; + err->errorNumber = errorNumber; + + err->filename = metadata.filename; + err->lineno = metadata.lineNumber; + err->column = metadata.columnNumber; + err->isMuted = metadata.isMuted; + + if (UniqueTwoByteChars lineOfContext = Move(metadata.lineOfContext)) + err->initOwnedLinebuf(lineOfContext.release(), metadata.lineLength, metadata.tokenOffset); + + if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, errorNumber, + nullptr, ArgumentsAreLatin1, err, args)) + { + return false; + } + + if (cx->isJSContext()) { + err->throwError(cx->asJSContext()); + } + + return true; +} + +void +ReportCompileError(JSContext* cx, ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes, + unsigned flags, unsigned errorNumber, va_list args) +{ + // On the main thread, report the error immediately. When compiling off + // thread, save the error so that the thread finishing the parse can report + // it later. + CompileError tempErr; + CompileError* err = &tempErr; + if (!cx->isJSContext() && !cx->addPendingCompileError(&err)) { + return; + } + + err->notes = Move(notes); + err->flags = flags; + err->errorNumber = errorNumber; + + err->filename = metadata.filename; + err->lineno = metadata.lineNumber; + err->column = metadata.columnNumber; + err->isMuted = metadata.isMuted; + + if (UniqueTwoByteChars lineOfContext = Move(metadata.lineOfContext)) + err->initOwnedLinebuf(lineOfContext.release(), metadata.lineLength, metadata.tokenOffset); + + if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, errorNumber, + nullptr, ArgumentsAreLatin1, err, args)) + { + return; + } + + if (cx->isJSContext()) { + err->throwError(cx->asJSContext()); + } +} diff --git a/js/src/vm/ErrorReporting.h b/js/src/vm/ErrorReporting.h new file mode 100644 index 0000000000..02bbe2c636 --- /dev/null +++ b/js/src/vm/ErrorReporting.h @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef vm_ErrorReporting_h +#define vm_ErrorReporting_h + +#include "mozilla/Move.h" + +#include <stdarg.h> + +#include "jsapi.h" // for JSErrorNotes, JSErrorReport + +#include "js/UniquePtr.h" // for UniquePtr +#include "js/Utility.h" // for UniqueTwoByteChars + +struct JSContext; + +namespace js { + +/** + * Metadata for a compilation error (or warning) at a particular offset, or at + * no offset (i.e. with respect to a script overall). + */ +struct ErrorMetadata +{ + // The file/URL where the error occurred. + const char* filename; + + // The line and column numbers where the error occurred. If the error + // is with respect to the entire script and not with respect to a + // particular location, these will both be zero. + uint32_t lineNumber; + uint32_t columnNumber; + + // If the error occurs at a particular location, context surrounding the + // location of the error: the line that contained the error, or a small + // portion of it if the line is long. + // + // This information is provided on a best-effort basis: code populating + // ErrorMetadata instances isn't obligated to supply this. + JS::UniqueTwoByteChars lineOfContext; + + // If |lineOfContext| is non-null, its length. + size_t lineLength; + + // If |lineOfContext| is non-null, the offset within it of the token that + // triggered the error. + size_t tokenOffset; + + // Whether the error is "muted" because it derives from a cross-origin + // load. See the comment in TransitiveCompileOptions in jsapi.h for + // details. + bool isMuted; +}; + +class CompileError : public JSErrorReport +{ + public: + void throwError(JSContext* cx); +}; + +/** Send a JSErrorReport to the warningReporter callback. */ +extern void +CallWarningReporter(JSContext* cx, JSErrorReport* report); + +/** + * Report a compile error during script processing prior to execution of the + * script. + */ +extern void +ReportCompileError(ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes, + unsigned flags, unsigned errorNumber, va_list args); + +/** + * Report a compile warning during script processing prior to execution of the + * script. Returns true if the warning was successfully reported, false if an + * error occurred. + * + * This function DOES NOT respect an existing werror option. If the caller + * wishes such option to be respected, it must do so itself. + */ +extern MOZ_MUST_USE bool +ReportCompileWarning(JSContext* cx, ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes, + unsigned flags, unsigned errorNumber, va_list args); + +} // namespace js + +#endif /* vm_ErrorReporting_h */ diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index 690c0bf485..ba28501e61 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -256,7 +256,7 @@ static const JSFunctionSpec legacy_generator_methods[] = { static JSObject* NewSingletonObjectWithObjectPrototype(JSContext* cx, Handle<GlobalObject*> global) { - RootedObject proto(cx, global->getOrCreateObjectPrototype(cx)); + RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); if (!proto) return nullptr; return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject); @@ -265,7 +265,7 @@ NewSingletonObjectWithObjectPrototype(JSContext* cx, Handle<GlobalObject*> globa JSObject* js::NewSingletonObjectWithFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global) { - RootedObject proto(cx, global->getOrCreateFunctionPrototype(cx)); + RootedObject proto(cx, GlobalObject::getOrCreateFunctionPrototype(cx, global)); if (!proto) return nullptr; return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject); @@ -278,7 +278,7 @@ GlobalObject::initLegacyGeneratorProto(JSContext* cx, Handle<GlobalObject*> glob return true; RootedObject proto(cx, NewSingletonObjectWithObjectPrototype(cx, global)); - if (!proto || !proto->setDelegate(cx)) + if (!proto || !JSObject::setDelegate(cx, proto)) return false; if (!DefinePropertiesAndFunctions(cx, proto, nullptr, legacy_generator_methods)) return false; @@ -297,9 +297,9 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global) if (!iteratorProto) return false; - RootedObject genObjectProto(cx, global->createBlankPrototypeInheriting(cx, - &PlainObject::class_, - iteratorProto)); + RootedObject genObjectProto(cx, GlobalObject::createBlankPrototypeInheriting(cx, global, + &PlainObject::class_, + iteratorProto)); if (!genObjectProto) return false; if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr, star_generator_methods) || @@ -309,7 +309,7 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global) } RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global)); - if (!genFunctionProto || !genFunctionProto->setDelegate(cx)) + if (!genFunctionProto || !JSObject::setDelegate(cx, genFunctionProto)) return false; if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto) || !DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction)) diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index c90b6b85fa..85707e1c60 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -329,15 +329,15 @@ GlobalObject::createInternal(JSContext* cx, const Class* clasp) cx->compartment()->initGlobal(*global); - if (!global->setQualifiedVarObj(cx)) + if (!JSObject::setQualifiedVarObj(cx, global)) return nullptr; - if (!global->setDelegate(cx)) + if (!JSObject::setDelegate(cx, global)) return nullptr; return global; } -GlobalObject* +/* static */ GlobalObject* GlobalObject::new_(JSContext* cx, const Class* clasp, JSPrincipals* principals, JS::OnNewGlobalHookOption hookOption, const JS::CompartmentOptions& options) @@ -398,7 +398,7 @@ GlobalObject::emptyGlobalScope() const GlobalObject::getOrCreateEval(JSContext* cx, Handle<GlobalObject*> global, MutableHandleObject eval) { - if (!global->getOrCreateObjectPrototype(cx)) + if (!getOrCreateObjectPrototype(cx, global)) return false; eval.set(&global->getSlot(EVAL).toObject()); return true; @@ -573,7 +573,7 @@ GlobalObject::warnOnceAbout(JSContext* cx, HandleObject obj, WarnOnceFlag flag, return true; } -JSFunction* +/* static */ JSFunction* GlobalObject::createConstructor(JSContext* cx, Native ctor, JSAtom* nameArg, unsigned length, gc::AllocKind kind, const JSJitInfo* jitInfo) { @@ -595,28 +595,27 @@ CreateBlankProto(JSContext* cx, const Class* clasp, HandleObject proto, HandleOb RootedNativeObject blankProto(cx, NewNativeObjectWithGivenProto(cx, clasp, proto, SingletonObject)); - if (!blankProto || !blankProto->setDelegate(cx)) + if (!blankProto || !JSObject::setDelegate(cx, blankProto)) return nullptr; return blankProto; } -NativeObject* -GlobalObject::createBlankPrototype(JSContext* cx, const Class* clasp) +/* static */ NativeObject* +GlobalObject::createBlankPrototype(JSContext* cx, Handle<GlobalObject*> global, const Class* clasp) { - Rooted<GlobalObject*> self(cx, this); - RootedObject objectProto(cx, getOrCreateObjectPrototype(cx)); + RootedObject objectProto(cx, getOrCreateObjectPrototype(cx, global)); if (!objectProto) return nullptr; - return CreateBlankProto(cx, clasp, objectProto, self); + return CreateBlankProto(cx, clasp, objectProto, global); } -NativeObject* -GlobalObject::createBlankPrototypeInheriting(JSContext* cx, const Class* clasp, HandleObject proto) +/* static */ NativeObject* +GlobalObject::createBlankPrototypeInheriting(JSContext* cx, Handle<GlobalObject*> global, + const Class* clasp, HandleObject proto) { - Rooted<GlobalObject*> self(cx, this); - return CreateBlankProto(cx, clasp, proto, self); + return CreateBlankProto(cx, clasp, proto, global); } bool @@ -729,21 +728,20 @@ GlobalObject::hasRegExpStatics() const return !getSlot(REGEXP_STATICS).isUndefined(); } -RegExpStatics* -GlobalObject::getRegExpStatics(ExclusiveContext* cx) const +/* static */ RegExpStatics* +GlobalObject::getRegExpStatics(ExclusiveContext* cx, Handle<GlobalObject*> global) { MOZ_ASSERT(cx); - Rooted<GlobalObject*> self(cx, const_cast<GlobalObject*>(this)); RegExpStaticsObject* resObj = nullptr; - const Value& val = this->getSlot(REGEXP_STATICS); + const Value& val = global->getSlot(REGEXP_STATICS); if (!val.isObject()) { MOZ_ASSERT(val.isUndefined()); - resObj = RegExpStatics::create(cx, self); + resObj = RegExpStatics::create(cx, global); if (!resObj) return nullptr; - self->initSlot(REGEXP_STATICS, ObjectValue(*resObj)); + global->initSlot(REGEXP_STATICS, ObjectValue(*resObj)); } else { resObj = &val.toObject().as<RegExpStaticsObject>(); } @@ -866,7 +864,7 @@ GlobalObject::addIntrinsicValue(JSContext* cx, Handle<GlobalObject*> global, /* static */ bool GlobalObject::ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global) { - return global->getOrCreateObject(cx, MODULE_PROTO, initModuleProto) && - global->getOrCreateObject(cx, IMPORT_ENTRY_PROTO, initImportEntryProto) && - global->getOrCreateObject(cx, EXPORT_ENTRY_PROTO, initExportEntryProto); + return getOrCreateObject(cx, global, MODULE_PROTO, initModuleProto) && + getOrCreateObject(cx, global, IMPORT_ENTRY_PROTO, initImportEntryProto) && + getOrCreateObject(cx, global, EXPORT_ENTRY_PROTO, initExportEntryProto); } diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 3534ef2f6c..5aacfc5dcd 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -290,8 +290,8 @@ class GlobalObject : public NativeObject * Create a constructor function with the specified name and length using * ctor, a method which creates objects with the given class. */ - JSFunction* - createConstructor(JSContext* cx, JSNative ctor, JSAtom* name, unsigned length, + static JSFunction* + createConstructor(JSContext* cx, JSNative ctor, JSAtom* name, unsigned length, gc::AllocKind kind = gc::AllocKind::FUNCTION, const JSJitInfo* jitInfo = nullptr); @@ -303,48 +303,44 @@ class GlobalObject : public NativeObject * complete the minimal initialization to make the returned object safe to * touch. */ - NativeObject* createBlankPrototype(JSContext* cx, const js::Class* clasp); + static NativeObject* + createBlankPrototype(JSContext* cx, Handle<GlobalObject*> global, const js::Class* clasp); /* * Identical to createBlankPrototype, but uses proto as the [[Prototype]] * of the returned blank prototype. */ - NativeObject* createBlankPrototypeInheriting(JSContext* cx, const js::Class* clasp, - HandleObject proto); + static NativeObject* + createBlankPrototypeInheriting(JSContext* cx, Handle<GlobalObject*> global, + const js::Class* clasp, HandleObject proto); template <typename T> - T* createBlankPrototype(JSContext* cx) { - NativeObject* res = createBlankPrototype(cx, &T::class_); + static T* + createBlankPrototype(JSContext* cx, Handle<GlobalObject*> global) { + NativeObject* res = createBlankPrototype(cx, global, &T::class_); return res ? &res->template as<T>() : nullptr; } - NativeObject* getOrCreateObjectPrototype(JSContext* cx) { - if (functionObjectClassesInitialized()) - return &getPrototype(JSProto_Object).toObject().as<NativeObject>(); - RootedGlobalObject self(cx, this); - if (!ensureConstructor(cx, self, JSProto_Object)) + static NativeObject* + getOrCreateObjectPrototype(JSContext* cx, Handle<GlobalObject*> global) { + if (global->functionObjectClassesInitialized()) + return &global->getPrototype(JSProto_Object).toObject().as<NativeObject>(); + if (!ensureConstructor(cx, global, JSProto_Object)) return nullptr; - return &self->getPrototype(JSProto_Object).toObject().as<NativeObject>(); - } - - static NativeObject* getOrCreateObjectPrototype(JSContext* cx, Handle<GlobalObject*> global) { - return global->getOrCreateObjectPrototype(cx); + return &global->getPrototype(JSProto_Object).toObject().as<NativeObject>(); } - NativeObject* getOrCreateFunctionPrototype(JSContext* cx) { - if (functionObjectClassesInitialized()) - return &getPrototype(JSProto_Function).toObject().as<NativeObject>(); - RootedGlobalObject self(cx, this); - if (!ensureConstructor(cx, self, JSProto_Object)) + static NativeObject* + getOrCreateFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global) { + if (global->functionObjectClassesInitialized()) + return &global->getPrototype(JSProto_Function).toObject().as<NativeObject>(); + if (!ensureConstructor(cx, global, JSProto_Object)) return nullptr; - return &self->getPrototype(JSProto_Function).toObject().as<NativeObject>(); - } - - static NativeObject* getOrCreateFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global) { - return global->getOrCreateFunctionPrototype(cx); + return &global->getPrototype(JSProto_Function).toObject().as<NativeObject>(); } - static NativeObject* getOrCreateArrayPrototype(JSContext* cx, Handle<GlobalObject*> global) { + static NativeObject* + getOrCreateArrayPrototype(JSContext* cx, Handle<GlobalObject*> global) { if (!ensureConstructor(cx, global, JSProto_Array)) return nullptr; return &global->getPrototype(JSProto_Array).toObject().as<NativeObject>(); @@ -356,37 +352,43 @@ class GlobalObject : public NativeObject return nullptr; } - static NativeObject* getOrCreateBooleanPrototype(JSContext* cx, Handle<GlobalObject*> global) { + static NativeObject* + getOrCreateBooleanPrototype(JSContext* cx, Handle<GlobalObject*> global) { if (!ensureConstructor(cx, global, JSProto_Boolean)) return nullptr; return &global->getPrototype(JSProto_Boolean).toObject().as<NativeObject>(); } - static NativeObject* getOrCreateNumberPrototype(JSContext* cx, Handle<GlobalObject*> global) { + static NativeObject* + getOrCreateNumberPrototype(JSContext* cx, Handle<GlobalObject*> global) { if (!ensureConstructor(cx, global, JSProto_Number)) return nullptr; return &global->getPrototype(JSProto_Number).toObject().as<NativeObject>(); } - static NativeObject* getOrCreateStringPrototype(JSContext* cx, Handle<GlobalObject*> global) { + static NativeObject* + getOrCreateStringPrototype(JSContext* cx, Handle<GlobalObject*> global) { if (!ensureConstructor(cx, global, JSProto_String)) return nullptr; return &global->getPrototype(JSProto_String).toObject().as<NativeObject>(); } - static NativeObject* getOrCreateSymbolPrototype(JSContext* cx, Handle<GlobalObject*> global) { + static NativeObject* + getOrCreateSymbolPrototype(JSContext* cx, Handle<GlobalObject*> global) { if (!ensureConstructor(cx, global, JSProto_Symbol)) return nullptr; return &global->getPrototype(JSProto_Symbol).toObject().as<NativeObject>(); } - static NativeObject* getOrCreatePromisePrototype(JSContext* cx, Handle<GlobalObject*> global) { + static NativeObject* + getOrCreatePromisePrototype(JSContext* cx, Handle<GlobalObject*> global) { if (!ensureConstructor(cx, global, JSProto_Promise)) return nullptr; return &global->getPrototype(JSProto_Promise).toObject().as<NativeObject>(); } - static NativeObject* getOrCreateRegExpPrototype(JSContext* cx, Handle<GlobalObject*> global) { + static NativeObject* + getOrCreateRegExpPrototype(JSContext* cx, Handle<GlobalObject*> global) { if (!ensureConstructor(cx, global, JSProto_RegExp)) return nullptr; return &global->getPrototype(JSProto_RegExp).toObject().as<NativeObject>(); @@ -398,28 +400,30 @@ class GlobalObject : public NativeObject return nullptr; } - static NativeObject* getOrCreateSavedFramePrototype(JSContext* cx, - Handle<GlobalObject*> global) { + static NativeObject* + getOrCreateSavedFramePrototype(JSContext* cx, Handle<GlobalObject*> global) { if (!ensureConstructor(cx, global, JSProto_SavedFrame)) return nullptr; return &global->getPrototype(JSProto_SavedFrame).toObject().as<NativeObject>(); } - static JSObject* getOrCreateArrayBufferPrototype(JSContext* cx, Handle<GlobalObject*> global) { + static JSObject* + getOrCreateArrayBufferPrototype(JSContext* cx, Handle<GlobalObject*> global) { if (!ensureConstructor(cx, global, JSProto_ArrayBuffer)) return nullptr; return &global->getPrototype(JSProto_ArrayBuffer).toObject(); } - JSObject* getOrCreateSharedArrayBufferPrototype(JSContext* cx, Handle<GlobalObject*> global) { + static JSObject* + getOrCreateSharedArrayBufferPrototype(JSContext* cx, Handle<GlobalObject*> global) { if (!ensureConstructor(cx, global, JSProto_SharedArrayBuffer)) return nullptr; return &global->getPrototype(JSProto_SharedArrayBuffer).toObject(); } - static JSObject* getOrCreateCustomErrorPrototype(JSContext* cx, - Handle<GlobalObject*> global, - JSExnType exnType) + static JSObject* + getOrCreateCustomErrorPrototype(JSContext* cx, Handle<GlobalObject*> global, + JSExnType exnType) { JSProtoKey key = GetExceptionProtoKey(exnType); if (!ensureConstructor(cx, global, key)) @@ -439,35 +443,41 @@ class GlobalObject : public NativeObject return getOrCreateCustomErrorPrototype(cx, global, JSEXN_ERR); } - static NativeObject* getOrCreateSetPrototype(JSContext* cx, Handle<GlobalObject*> global) { + static NativeObject* + getOrCreateSetPrototype(JSContext* cx, Handle<GlobalObject*> global) { if (!ensureConstructor(cx, global, JSProto_Set)) return nullptr; return &global->getPrototype(JSProto_Set).toObject().as<NativeObject>(); } - static NativeObject* getOrCreateWeakSetPrototype(JSContext* cx, Handle<GlobalObject*> global) { + static NativeObject* + getOrCreateWeakSetPrototype(JSContext* cx, Handle<GlobalObject*> global) { if (!ensureConstructor(cx, global, JSProto_WeakSet)) return nullptr; return &global->getPrototype(JSProto_WeakSet).toObject().as<NativeObject>(); } - JSObject* getOrCreateIntlObject(JSContext* cx) { - return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_Intl, initIntlObject); + static JSObject* + getOrCreateIntlObject(JSContext* cx, Handle<GlobalObject*> global) { + return getOrCreateObject(cx, global, APPLICATION_SLOTS + JSProto_Intl, initIntlObject); } - JSObject* getOrCreateTypedObjectModule(JSContext* cx) { - return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_TypedObject, initTypedObjectModule); + static JSObject* + getOrCreateTypedObjectModule(JSContext* cx, Handle<GlobalObject*> global) { + return getOrCreateObject(cx, global, APPLICATION_SLOTS + JSProto_TypedObject, + initTypedObjectModule); } - JSObject* getOrCreateSimdGlobalObject(JSContext* cx) { - return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_SIMD, initSimdObject); + static JSObject* + getOrCreateSimdGlobalObject(JSContext* cx, Handle<GlobalObject*> global) { + return getOrCreateObject(cx, global, APPLICATION_SLOTS + JSProto_SIMD, initSimdObject); } // Get the type descriptor for one of the SIMD types. // simdType is one of the JS_SIMDTYPEREPR_* constants. // Implemented in builtin/SIMD.cpp. - static SimdTypeDescr* getOrCreateSimdTypeDescr(JSContext* cx, Handle<GlobalObject*> global, - SimdType simdType); + static SimdTypeDescr* + getOrCreateSimdTypeDescr(JSContext* cx, Handle<GlobalObject*> global, SimdType simdType); TypedObjectModuleObject& getTypedObjectModule() const; @@ -475,16 +485,19 @@ class GlobalObject : public NativeObject return &getPrototype(JSProto_Iterator).toObject(); } - JSObject* getOrCreateCollatorPrototype(JSContext* cx) { - return getOrCreateObject(cx, COLLATOR_PROTO, initIntlObject); + static JSObject* + getOrCreateCollatorPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return getOrCreateObject(cx, global, COLLATOR_PROTO, initIntlObject); } - JSObject* getOrCreateNumberFormatPrototype(JSContext* cx) { - return getOrCreateObject(cx, NUMBER_FORMAT_PROTO, initIntlObject); + static JSObject* + getOrCreateNumberFormatPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return getOrCreateObject(cx, global, NUMBER_FORMAT_PROTO, initIntlObject); } - JSObject* getOrCreateDateTimeFormatPrototype(JSContext* cx) { - return getOrCreateObject(cx, DATE_TIME_FORMAT_PROTO, initIntlObject); + static JSObject* + getOrCreateDateTimeFormatPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return getOrCreateObject(cx, global, DATE_TIME_FORMAT_PROTO, initIntlObject); } static bool ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global); @@ -539,88 +552,86 @@ class GlobalObject : public NativeObject private: typedef bool (*ObjectInitOp)(JSContext* cx, Handle<GlobalObject*> global); - JSObject* getOrCreateObject(JSContext* cx, unsigned slot, ObjectInitOp init) { - Value v = getSlotRef(slot); + static JSObject* + getOrCreateObject(JSContext* cx, Handle<GlobalObject*> global, unsigned slot, + ObjectInitOp init) + { + Value v = global->getSlotRef(slot); if (v.isObject()) return &v.toObject(); - RootedGlobalObject self(cx, this); - if (!init(cx, self)) + if (!init(cx, global)) return nullptr; - return &self->getSlot(slot).toObject(); + return &global->getSlot(slot).toObject(); } public: - static NativeObject* getOrCreateIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) - { - return MaybeNativeObject(global->getOrCreateObject(cx, ITERATOR_PROTO, initIteratorProto)); + static NativeObject* + getOrCreateIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return MaybeNativeObject(getOrCreateObject(cx, global, ITERATOR_PROTO, initIteratorProto)); } - static NativeObject* getOrCreateArrayIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) - { - return MaybeNativeObject(global->getOrCreateObject(cx, ARRAY_ITERATOR_PROTO, initArrayIteratorProto)); + static NativeObject* + getOrCreateArrayIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return MaybeNativeObject(getOrCreateObject(cx, global, ARRAY_ITERATOR_PROTO, + initArrayIteratorProto)); } - static NativeObject* getOrCreateStringIteratorPrototype(JSContext* cx, - Handle<GlobalObject*> global) - { - return MaybeNativeObject(global->getOrCreateObject(cx, STRING_ITERATOR_PROTO, initStringIteratorProto)); + static NativeObject* + getOrCreateStringIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return MaybeNativeObject(getOrCreateObject(cx, global, STRING_ITERATOR_PROTO, + initStringIteratorProto)); } - static NativeObject* getOrCreateLegacyGeneratorObjectPrototype(JSContext* cx, - Handle<GlobalObject*> global) - { - return MaybeNativeObject(global->getOrCreateObject(cx, LEGACY_GENERATOR_OBJECT_PROTO, - initLegacyGeneratorProto)); + static NativeObject* + getOrCreateLegacyGeneratorObjectPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return MaybeNativeObject(getOrCreateObject(cx, global, LEGACY_GENERATOR_OBJECT_PROTO, + initLegacyGeneratorProto)); } - static NativeObject* getOrCreateStarGeneratorObjectPrototype(JSContext* cx, - Handle<GlobalObject*> global) + static NativeObject* + getOrCreateStarGeneratorObjectPrototype(JSContext* cx, Handle<GlobalObject*> global) { - return MaybeNativeObject(global->getOrCreateObject(cx, STAR_GENERATOR_OBJECT_PROTO, initStarGenerators)); + return MaybeNativeObject(getOrCreateObject(cx, global, STAR_GENERATOR_OBJECT_PROTO, + initStarGenerators)); } - static NativeObject* getOrCreateStarGeneratorFunctionPrototype(JSContext* cx, - Handle<GlobalObject*> global) - { - return MaybeNativeObject(global->getOrCreateObject(cx, STAR_GENERATOR_FUNCTION_PROTO, initStarGenerators)); + static NativeObject* + getOrCreateStarGeneratorFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return MaybeNativeObject(getOrCreateObject(cx, global, STAR_GENERATOR_FUNCTION_PROTO, + initStarGenerators)); } - static JSObject* getOrCreateStarGeneratorFunction(JSContext* cx, - Handle<GlobalObject*> global) - { - return global->getOrCreateObject(cx, STAR_GENERATOR_FUNCTION, initStarGenerators); + static JSObject* + getOrCreateStarGeneratorFunction(JSContext* cx, Handle<GlobalObject*> global) { + return getOrCreateObject(cx, global, STAR_GENERATOR_FUNCTION, initStarGenerators); } - static NativeObject* getOrCreateAsyncFunctionPrototype(JSContext* cx, - Handle<GlobalObject*> global) - { - return MaybeNativeObject(global->getOrCreateObject(cx, ASYNC_FUNCTION_PROTO, - initAsyncFunction)); + static NativeObject* + getOrCreateAsyncFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_FUNCTION_PROTO, + initAsyncFunction)); } - static JSObject* getOrCreateAsyncFunction(JSContext* cx, - Handle<GlobalObject*> global) - { - return global->getOrCreateObject(cx, ASYNC_FUNCTION, initAsyncFunction); + static JSObject* + getOrCreateAsyncFunction(JSContext* cx, Handle<GlobalObject*> global) { + return getOrCreateObject(cx, global, ASYNC_FUNCTION, initAsyncFunction); } - static JSObject* getOrCreateMapIteratorPrototype(JSContext* cx, - Handle<GlobalObject*> global) - { - return global->getOrCreateObject(cx, MAP_ITERATOR_PROTO, initMapIteratorProto); + static JSObject* + getOrCreateMapIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return getOrCreateObject(cx, global, MAP_ITERATOR_PROTO, initMapIteratorProto); } - static JSObject* getOrCreateSetIteratorPrototype(JSContext* cx, - Handle<GlobalObject*> global) - { - return global->getOrCreateObject(cx, SET_ITERATOR_PROTO, initSetIteratorProto); + static JSObject* + getOrCreateSetIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return getOrCreateObject(cx, global, SET_ITERATOR_PROTO, initSetIteratorProto); } - JSObject* getOrCreateDataViewPrototype(JSContext* cx) { - RootedGlobalObject self(cx, this); - if (!ensureConstructor(cx, self, JSProto_DataView)) + static JSObject* + getOrCreateDataViewPrototype(JSContext* cx, Handle<GlobalObject*> global) { + if (!ensureConstructor(cx, global, JSProto_DataView)) return nullptr; - return &self->getPrototype(JSProto_DataView).toObject(); + return &global->getPrototype(JSProto_DataView).toObject(); } static JSFunction* @@ -678,8 +689,9 @@ class GlobalObject : public NativeObject return true; } - static bool getIntrinsicValue(JSContext* cx, Handle<GlobalObject*> global, - HandlePropertyName name, MutableHandleValue value) + static bool + getIntrinsicValue(JSContext* cx, Handle<GlobalObject*> global, + HandlePropertyName name, MutableHandleValue value) { bool exists = false; if (!GlobalObject::maybeGetIntrinsicValue(cx, global, name, value, &exists)) @@ -709,7 +721,8 @@ class GlobalObject : public NativeObject unsigned nargs, MutableHandleValue funVal); bool hasRegExpStatics() const; - RegExpStatics* getRegExpStatics(ExclusiveContext* cx) const; + static RegExpStatics* getRegExpStatics(ExclusiveContext* cx, + Handle<GlobalObject*> global); RegExpStatics* getAlreadyCreatedRegExpStatics() const; JSObject* getThrowTypeError() const { @@ -996,7 +1009,7 @@ GenericCreateConstructor(JSContext* cx, JSProtoKey key) // Note - We duplicate the trick from ClassName() so that we don't need to // include jsatominlines.h here. PropertyName* name = (&cx->names().Null)[key]; - return cx->global()->createConstructor(cx, ctor, name, length, kind, jitInfo); + return GlobalObject::createConstructor(cx, ctor, name, length, kind, jitInfo); } inline JSObject* @@ -1009,7 +1022,7 @@ GenericCreatePrototype(JSContext* cx, JSProtoKey key) if (!GlobalObject::ensureConstructor(cx, cx->global(), protoKey)) return nullptr; RootedObject parentProto(cx, &cx->global()->getPrototype(protoKey).toObject()); - return cx->global()->createBlankPrototypeInheriting(cx, clasp, parentProto); + return GlobalObject::createBlankPrototypeInheriting(cx, cx->global(), clasp, parentProto); } inline JSProtoKey diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index bd29d0c796..44915521fd 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -1291,7 +1291,7 @@ GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, void* token) MOZ_ASSERT(script->module()); RootedModuleObject module(cx, script->module()); - module->fixEnvironmentsAfterCompartmentMerge(cx); + module->fixEnvironmentsAfterCompartmentMerge(); if (!ModuleObject::Freeze(cx, module)) return nullptr; diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h index 5f476c4ffc..acfa8f74bc 100644 --- a/js/src/vm/Interpreter-inl.h +++ b/js/src/vm/Interpreter-inl.h @@ -22,7 +22,6 @@ #include "vm/EnvironmentObject-inl.h" #include "vm/Stack-inl.h" #include "vm/String-inl.h" -#include "vm/UnboxedObject-inl.h" namespace js { @@ -337,14 +336,10 @@ InitGlobalLexicalOperation(JSContext* cx, LexicalEnvironmentObject* lexicalEnvAr inline bool InitPropertyOperation(JSContext* cx, JSOp op, HandleObject obj, HandleId id, HandleValue rhs) { - if (obj->is<PlainObject>() || obj->is<JSFunction>()) { - unsigned propAttrs = GetInitDataPropAttrs(op); - return NativeDefineProperty(cx, obj.as<NativeObject>(), id, rhs, nullptr, nullptr, - propAttrs); - } - - MOZ_ASSERT(obj->as<UnboxedPlainObject>().layout().lookup(id)); - return PutProperty(cx, obj, id, rhs, false); + MOZ_ASSERT(obj->is<PlainObject>() || obj->is<JSFunction>()); + unsigned propAttrs = GetInitDataPropAttrs(op); + return NativeDefineProperty(cx, obj.as<NativeObject>(), id, rhs, + nullptr, nullptr, propAttrs); } inline bool @@ -598,7 +593,7 @@ InitArrayElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, uint32_t JSOp op = JSOp(*pc); MOZ_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC); - MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()); + MOZ_ASSERT(obj->is<ArrayObject>()); if (op == JSOP_INITELEM_INC && index == INT32_MAX) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SPREAD_TOO_LARGE); @@ -835,7 +830,7 @@ class FastCallGuard if (useIon_ && fun_) { if (!script_) { - script_ = fun_->getOrCreateScript(cx); + script_ = JSFunction::getOrCreateScript(cx, fun_); if (!script_) return false; } diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index b747e4d7a0..030f0f3b6c 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -261,11 +261,16 @@ SetPropertyOperation(JSContext* cx, JSOp op, HandleValue lval, HandleId id, Hand } static JSFunction* -MakeDefaultConstructor(JSContext* cx, JSOp op, JSAtom* atom, HandleObject proto) +MakeDefaultConstructor(JSContext* cx, HandleScript script, jsbytecode* pc, HandleObject proto) { + JSOp op = JSOp(*pc); + JSAtom* atom = script->getAtom(pc); bool derived = op == JSOP_DERIVEDCONSTRUCTOR; MOZ_ASSERT(derived == !!proto); + jssrcnote* classNote = GetSrcNote(cx, script, pc); + MOZ_ASSERT(classNote && SN_TYPE(classNote) == SRC_CLASS_SPAN); + PropertyName* lookup = derived ? cx->names().DefaultDerivedClassConstructor : cx->names().DefaultBaseClassConstructor; @@ -285,6 +290,17 @@ MakeDefaultConstructor(JSContext* cx, JSOp op, JSAtom* atom, HandleObject proto) MOZ_ASSERT(ctor->infallibleIsDefaultClassConstructor(cx)); + // Create the script now, as the source span needs to be overridden for + // toString. Calling toString on a class constructor must not return the + // source for just the constructor function. + JSScript *ctorScript = JSFunction::getOrCreateScript(cx, ctor); + if (!ctorScript) + return nullptr; + uint32_t classStartOffset = GetSrcNoteOffset(classNote, 0); + uint32_t classEndOffset = GetSrcNoteOffset(classNote, 1); + ctorScript->setDefaultClassConstructorSpan(script->sourceObject(), classStartOffset, + classEndOffset); + return ctor; } @@ -373,7 +389,7 @@ js::RunScript(JSContext* cx, RunState& state) SPSEntryMarker marker(cx->runtime(), state.script()); - state.script()->ensureNonLazyCanonicalFunction(cx); + state.script()->ensureNonLazyCanonicalFunction(); if (jit::IsIonEnabled(cx)) { jit::MethodStatus status = jit::CanEnter(cx, state); @@ -446,7 +462,7 @@ js::InternalCallOrConstruct(JSContext* cx, const CallArgs& args, MaybeConstruct } /* Invoke native functions. */ - JSFunction* fun = &args.callee().as<JSFunction>(); + RootedFunction fun(cx, &args.callee().as<JSFunction>()); if (construct != CONSTRUCT && fun->isClassConstructor()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CALL_CLASS_CONSTRUCTOR); return false; @@ -454,10 +470,16 @@ js::InternalCallOrConstruct(JSContext* cx, const CallArgs& args, MaybeConstruct if (fun->isNative()) { MOZ_ASSERT_IF(construct, !fun->isConstructor()); - return CallJSNative(cx, fun->native(), args); + JSNative native = fun->native(); + if (!construct && args.ignoresReturnValue()) { + const JSJitInfo* jitInfo = fun->jitInfo(); + if (jitInfo && jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) + native = jitInfo->ignoresReturnValueMethod; + } + return CallJSNative(cx, native, args); } - if (!fun->getOrCreateScript(cx)) + if (!JSFunction::getOrCreateScript(cx, fun)) return false; /* Run function until JSOP_RETRVAL, JSOP_RETURN or error. */ @@ -718,14 +740,14 @@ js::Execute(JSContext* cx, HandleScript script, JSObject& envChainArg, Value* rv } /* - * ES6 (4-25-16) 12.10.4 InstanceofOperator + * ES6 12.9.4 InstanceofOperator */ extern bool -js::InstanceOfOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp) +JS::InstanceofOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp) { /* Step 1. is handled by caller. */ - /* Step 2. */ + /* Step 2-3. */ RootedValue hasInstance(cx); RootedId id(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().hasInstance)); if (!GetProperty(cx, obj, obj, id, &hasInstance)) @@ -735,7 +757,7 @@ js::InstanceOfOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp) if (!IsCallable(hasInstance)) return ReportIsNotFunction(cx, hasInstance); - /* Step 3. */ + /* Step 4. */ RootedValue rval(cx); if (!Call(cx, hasInstance, obj, v, &rval)) return false; @@ -743,13 +765,13 @@ js::InstanceOfOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp) return true; } - /* Step 4. */ + /* Step 5. */ if (!obj->isCallable()) { RootedValue val(cx, ObjectValue(*obj)); return ReportIsNotFunction(cx, val); } - /* Step 5. */ + /* Step 6. */ return OrdinaryHasInstance(cx, obj, v, bp); } @@ -760,7 +782,7 @@ js::HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp) RootedValue local(cx, v); if (JSHasInstanceOp hasInstance = clasp->getHasInstance()) return hasInstance(cx, obj, &local, bp); - return js::InstanceOfOperator(cx, obj, local, bp); + return JS::InstanceofOperator(cx, obj, local, bp); } static inline bool @@ -1543,7 +1565,7 @@ SetObjectElementOperation(JSContext* cx, HandleObject obj, HandleId id, HandleVa } } - if (obj->isNative() && !JSID_IS_INT(id) && !obj->setHadElementsAccess(cx)) + if (obj->isNative() && !JSID_IS_INT(id) && !JSObject::setHadElementsAccess(cx, obj)) return false; ObjectOpResult result; @@ -1916,6 +1938,7 @@ CASE(EnableInterruptsPseudoOpcode) /* Various 1-byte no-ops. */ CASE(JSOP_NOP) CASE(JSOP_NOP_DESTRUCTURING) +CASE(JSOP_UNUSED126) CASE(JSOP_UNUSED192) CASE(JSOP_UNUSED209) CASE(JSOP_UNUSED210) @@ -2958,6 +2981,7 @@ CASE(JSOP_FUNAPPLY) CASE(JSOP_NEW) CASE(JSOP_CALL) +CASE(JSOP_CALL_IGNORES_RV) CASE(JSOP_CALLITER) CASE(JSOP_SUPERCALL) CASE(JSOP_FUNCALL) @@ -2966,10 +2990,11 @@ CASE(JSOP_FUNCALL) cx->runtime()->spsProfiler.updatePC(script, REGS.pc); MaybeConstruct construct = MaybeConstruct(*REGS.pc == JSOP_NEW || *REGS.pc == JSOP_SUPERCALL); + bool ignoresReturnValue = *REGS.pc == JSOP_CALL_IGNORES_RV; unsigned argStackSlots = GET_ARGC(REGS.pc) + construct; MOZ_ASSERT(REGS.stackDepth() >= 2u + GET_ARGC(REGS.pc)); - CallArgs args = CallArgsFromSp(argStackSlots, REGS.sp, construct); + CallArgs args = CallArgsFromSp(argStackSlots, REGS.sp, construct, ignoresReturnValue); JSFunction* maybeFun; bool isFunction = IsFunctionObject(args.calleev(), &maybeFun); @@ -2999,7 +3024,7 @@ CASE(JSOP_FUNCALL) { MOZ_ASSERT(maybeFun); ReservedRooted<JSFunction*> fun(&rootFunction0, maybeFun); - ReservedRooted<JSScript*> funScript(&rootScript0, fun->getOrCreateScript(cx)); + ReservedRooted<JSScript*> funScript(&rootScript0, JSFunction::getOrCreateScript(cx, fun)); if (!funScript) goto error; @@ -3636,7 +3661,6 @@ CASE(JSOP_NEWINIT) END_CASE(JSOP_NEWINIT) CASE(JSOP_NEWARRAY) -CASE(JSOP_SPREADCALLARRAY) { uint32_t length = GET_UINT32(REGS.pc); JSObject* obj = NewArrayOperation(cx, script, REGS.pc, length); @@ -4111,7 +4135,7 @@ CASE(JSOP_INITHOMEOBJECT) /* Load the home object */ ReservedRooted<JSObject*> obj(&rootObject0); obj = ®S.sp[int(-2 - skipOver)].toObject(); - MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() || obj->is<JSFunction>()); + MOZ_ASSERT(obj->is<PlainObject>() || obj->is<JSFunction>()); func->setExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT, ObjectValue(*obj)); } @@ -4174,8 +4198,8 @@ CASE(JSOP_DERIVEDCONSTRUCTOR) MOZ_ASSERT(REGS.sp[-1].isObject()); ReservedRooted<JSObject*> proto(&rootObject0, ®S.sp[-1].toObject()); - JSFunction* constructor = MakeDefaultConstructor(cx, JSOp(*REGS.pc), script->getAtom(REGS.pc), - proto); + JSFunction* constructor = MakeDefaultConstructor(cx, script, REGS.pc, proto); + if (!constructor) goto error; @@ -4185,8 +4209,7 @@ END_CASE(JSOP_DERIVEDCONSTRUCTOR) CASE(JSOP_CLASSCONSTRUCTOR) { - JSFunction* constructor = MakeDefaultConstructor(cx, JSOp(*REGS.pc), script->getAtom(REGS.pc), - nullptr); + JSFunction* constructor = MakeDefaultConstructor(cx, script, REGS.pc, nullptr); if (!constructor) goto error; PUSH_OBJECT(*constructor); @@ -4725,7 +4748,8 @@ js::RunOnceScriptPrologue(JSContext* cx, HandleScript script) // Force instantiation of the script's function's group to ensure the flag // is preserved in type information. - if (!script->functionNonDelazifying()->getGroup(cx)) + RootedFunction fun(cx, script->functionNonDelazifying()); + if (!JSObject::getGroup(cx, fun)) return false; MarkObjectGroupFlags(cx, script->functionNonDelazifying(), OBJECT_FLAG_RUNONCE_INVALIDATED); @@ -4927,18 +4951,13 @@ js::NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc, return nullptr; if (group->maybePreliminaryObjects()) { group->maybePreliminaryObjects()->maybeAnalyze(cx, group); - if (group->maybeUnboxedLayout()) - group->maybeUnboxedLayout()->setAllocationSite(script, pc); } if (group->shouldPreTenure() || group->maybePreliminaryObjects()) newKind = TenuredObject; - - if (group->maybeUnboxedLayout()) - return UnboxedPlainObject::create(cx, group, newKind); } - RootedObject obj(cx); + RootedPlainObject obj(cx); if (*pc == JSOP_NEWOBJECT) { RootedPlainObject baseObject(cx, &script->getObject(pc)->as<PlainObject>()); @@ -4975,11 +4994,6 @@ js::NewObjectOperationWithTemplate(JSContext* cx, HandleObject templateObject) NewObjectKind newKind = templateObject->group()->shouldPreTenure() ? TenuredObject : GenericObject; - if (templateObject->group()->maybeUnboxedLayout()) { - RootedObjectGroup group(cx, templateObject->group()); - return UnboxedPlainObject::create(cx, group, newKind); - } - JSObject* obj = CopyInitializerObject(cx, templateObject.as<PlainObject>(), newKind); if (!obj) return nullptr; @@ -5006,9 +5020,6 @@ js::NewArrayOperation(JSContext* cx, HandleScript script, jsbytecode* pc, uint32 if (group->shouldPreTenure() || group->maybePreliminaryObjects()) newKind = TenuredObject; - - if (group->maybeUnboxedLayout()) - return UnboxedArrayObject::create(cx, group, length, newKind); } ArrayObject* obj = NewDenseFullyAllocatedArray(cx, length, nullptr, newKind); @@ -5019,9 +5030,6 @@ js::NewArrayOperation(JSContext* cx, HandleScript script, jsbytecode* pc, uint32 MOZ_ASSERT(obj->isSingleton()); } else { obj->setGroup(group); - - if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects()) - preliminaryObjects->registerNewObject(obj); } return obj; @@ -5034,12 +5042,6 @@ js::NewArrayOperationWithTemplate(JSContext* cx, HandleObject templateObject) NewObjectKind newKind = templateObject->group()->shouldPreTenure() ? TenuredObject : GenericObject; - if (templateObject->is<UnboxedArrayObject>()) { - uint32_t length = templateObject->as<UnboxedArrayObject>().length(); - RootedObjectGroup group(cx, templateObject->group()); - return UnboxedArrayObject::create(cx, group, length, newKind); - } - ArrayObject* obj = NewDenseFullyAllocatedArray(cx, templateObject->as<ArrayObject>().length(), nullptr, newKind); if (!obj) diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index 330dbef5f5..9fefd75ccd 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -323,9 +323,6 @@ extern JSType TypeOfValue(const Value& v); extern bool -InstanceOfOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp); - -extern bool HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp); // Unwind environment chain and iterator to match the scope corresponding to diff --git a/js/src/vm/JSONParser.cpp b/js/src/vm/JSONParser.cpp index 01883bb155..e50da3bc46 100644 --- a/js/src/vm/JSONParser.cpp +++ b/js/src/vm/JSONParser.cpp @@ -606,8 +606,8 @@ JSONParserBase::finishArray(MutableHandleValue vp, ElementVector& elements) { MOZ_ASSERT(&elements == &stack.back().elements()); - JSObject* obj = ObjectGroup::newArrayObject(cx, elements.begin(), elements.length(), - GenericObject); + ArrayObject* obj = ObjectGroup::newArrayObject(cx, elements.begin(), elements.length(), + GenericObject); if (!obj) return false; diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h index 48a42a8db8..030d92c127 100644 --- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -158,11 +158,11 @@ NativeObject::extendDenseElements(ExclusiveContext* cx, MOZ_ASSERT(!denseElementsAreFrozen()); /* - * Don't grow elements for non-extensible objects or watched objects. Dense - * elements can be added/written with no extensible or watchpoint checks as - * long as there is capacity for them. + * Don't grow elements for non-extensible objects. Dense elements can be + * added/written with no extensible checks as long as there is capacity + * for them. */ - if (!nonProxyIsExtensible() || watched()) { + if (!nonProxyIsExtensible()) { MOZ_ASSERT(getDenseCapacity() == 0); return DenseElementResult::Incomplete; } @@ -235,6 +235,38 @@ NativeObject::ensureDenseElements(ExclusiveContext* cx, uint32_t index, uint32_t return DenseElementResult::Success; } +inline DenseElementResult +NativeObject::setOrExtendDenseElements(ExclusiveContext* cx, uint32_t start, const Value* vp, + uint32_t count, + ShouldUpdateTypes updateTypes) +{ + if (denseElementsAreFrozen()) + return DenseElementResult::Incomplete; + + if (is<ArrayObject>() && + !as<ArrayObject>().lengthIsWritable() && + start + count >= as<ArrayObject>().length()) + { + return DenseElementResult::Incomplete; + } + + DenseElementResult result = ensureDenseElements(cx, start, count); + if (result != DenseElementResult::Success) + return result; + + if (is<ArrayObject>() && start + count >= as<ArrayObject>().length()) + as<ArrayObject>().setLengthInt32(start + count); + + if (updateTypes == ShouldUpdateTypes::DontUpdate && !shouldConvertDoubleElements()) { + copyDenseElements(start, vp, count); + } else { + for (size_t i = 0; i < count; i++) + setDenseElementWithType(cx, start + i, vp[i]); + } + + return DenseElementResult::Success; +} + inline Value NativeObject::getDenseOrTypedArrayElement(uint32_t idx) { diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index 21f73f4a91..d801fad06e 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -9,8 +9,6 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/Casting.h" -#include "jswatchpoint.h" - #include "gc/Marking.h" #include "js/Value.h" #include "vm/Debugger.h" @@ -390,33 +388,6 @@ NativeObject::setLastPropertyMakeNonNative(Shape* shape) shape_ = shape; } -void -NativeObject::setLastPropertyMakeNative(ExclusiveContext* cx, Shape* shape) -{ - MOZ_ASSERT(getClass()->isNative()); - MOZ_ASSERT(shape->getObjectClass()->isNative()); - MOZ_ASSERT(!shape->inDictionary()); - - // This method is used to convert unboxed objects into native objects. In - // this case, the shape_ field was previously used to store other data and - // this should be treated as an initialization. - shape_.init(shape); - - slots_ = nullptr; - elements_ = emptyObjectElements; - - size_t oldSpan = shape->numFixedSlots(); - size_t newSpan = shape->slotSpan(); - - initializeSlotRange(0, oldSpan); - - // A failure at this point will leave the object as a mutant, and we - // can't recover. - AutoEnterOOMUnsafeRegion oomUnsafe; - if (oldSpan != newSpan && !updateSlotsForSpan(cx, oldSpan, newSpan)) - oomUnsafe.crash("NativeObject::setLastPropertyMakeNative"); -} - bool NativeObject::setSlotSpan(ExclusiveContext* cx, uint32_t span) { @@ -629,7 +600,7 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO return DenseElementResult::Incomplete; /* Watch for conditions under which an object's elements cannot be dense. */ - if (!obj->nonProxyIsExtensible() || obj->watched()) + if (!obj->nonProxyIsExtensible()) return DenseElementResult::Incomplete; /* @@ -699,10 +670,10 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO */ if (shape != obj->lastProperty()) { shape = shape->previous(); - if (!obj->removeProperty(cx, id)) + if (!NativeObject::removeProperty(cx, obj, id)) return DenseElementResult::Failure; } else { - if (!obj->removeProperty(cx, id)) + if (!NativeObject::removeProperty(cx, obj, id)) return DenseElementResult::Failure; shape = obj->lastProperty(); } @@ -718,7 +689,7 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO * flag so that we will not start using sparse indexes again if we need * to grow the object. */ - if (!obj->clearFlag(cx, BaseShape::INDEXED)) + if (!NativeObject::clearFlag(cx, obj, BaseShape::INDEXED)) return DenseElementResult::Failure; return DenseElementResult::Success; @@ -1023,23 +994,22 @@ NativeObject::freeSlot(ExclusiveContext* cx, uint32_t slot) setSlot(slot, UndefinedValue()); } -Shape* -NativeObject::addDataProperty(ExclusiveContext* cx, jsid idArg, uint32_t slot, unsigned attrs) +/* static */ Shape* +NativeObject::addDataProperty(ExclusiveContext* cx, HandleNativeObject obj, + jsid idArg, uint32_t slot, unsigned attrs) { MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); - RootedNativeObject self(cx, this); RootedId id(cx, idArg); - return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0); + return addProperty(cx, obj, id, nullptr, nullptr, slot, attrs, 0); } -Shape* -NativeObject::addDataProperty(ExclusiveContext* cx, HandlePropertyName name, - uint32_t slot, unsigned attrs) +/* static */ Shape* +NativeObject::addDataProperty(ExclusiveContext* cx, HandleNativeObject obj, + HandlePropertyName name, uint32_t slot, unsigned attrs) { MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); - RootedNativeObject self(cx, this); RootedId id(cx, NameToId(name)); - return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0); + return addProperty(cx, obj, id, nullptr, nullptr, slot, attrs, 0); } template <AllowGC allowGC> @@ -1073,7 +1043,7 @@ CallAddPropertyHook(ExclusiveContext* cx, HandleNativeObject obj, HandleShape sh RootedId id(cx, shape->propid()); if (!CallJSAddPropertyOp(cx->asJSContext(), addProperty, obj, id, value)) { - obj->removeProperty(cx, shape->propid()); + NativeObject::removeProperty(cx, obj, shape->propid()); return false; } } @@ -1145,7 +1115,7 @@ PurgeProtoChain(ExclusiveContext* cx, JSObject* objArg, HandleId id) shape = obj->as<NativeObject>().lookup(cx, id); if (shape) - return obj->as<NativeObject>().shadowingShapeChange(cx, *shape); + return NativeObject::shadowingShapeChange(cx, obj.as<NativeObject>(), *shape); obj = obj->staticPrototype(); } @@ -2438,17 +2408,9 @@ SetExistingProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleVa } bool -js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value, +js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v, HandleValue receiver, QualifiedBool qualified, ObjectOpResult& result) { - // Fire watchpoints, if any. - RootedValue v(cx, value); - if (MOZ_UNLIKELY(obj->watched())) { - WatchpointMap* wpmap = cx->compartment()->watchpointMap; - if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, &v)) - return false; - } - // Step numbers below reference ES6 rev 27 9.1.9, the [[Set]] internal // method for ordinary objects. We substitute our own names for these names // used in the spec: O -> pobj, P -> id, ownDesc -> shape. @@ -2556,7 +2518,7 @@ js::NativeDeleteProperty(JSContext* cx, HandleNativeObject obj, HandleId id, obj->setDenseElementHole(cx, JSID_TO_INT(id)); } else { - if (!obj->removeProperty(cx, id)) + if (!NativeObject::removeProperty(cx, obj, id)) return false; } diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h index d2c06eabc1..9cc6d54366 100644 --- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -339,16 +339,19 @@ IsObjectValueInCompartment(const Value& v, JSCompartment* comp); #endif // Operations which change an object's dense elements can either succeed, fail, -// or be unable to complete. For native objects, the latter is used when the -// object's elements must become sparse instead. The enum below is used for -// such operations, and for similar operations on unboxed arrays and methods -// that work on both kinds of objects. +// or be unable to complete. The latter is used when the object's elements must +// become sparse instead. The enum below is used for such operations. enum class DenseElementResult { Failure, Success, Incomplete }; +enum class ShouldUpdateTypes { + Update, + DontUpdate +}; + /* * NativeObject specifies the internal implementation of a native object. * @@ -467,11 +470,6 @@ class NativeObject : public ShapedObject // that are (temporarily) inconsistent. void setLastPropertyMakeNonNative(Shape* shape); - // As for setLastProperty(), but changes the class associated with the - // object to a native one. The object's type has already been changed, and - // this brings the shape into sync with it. - void setLastPropertyMakeNative(ExclusiveContext* cx, Shape* shape); - // Newly-created TypedArrays that map a SharedArrayBuffer are // marked as shared by giving them an ObjectElements that has the // ObjectElements::SHARED_MEMORY flag set. @@ -493,8 +491,8 @@ class NativeObject : public ShapedObject void checkShapeConsistency() { } #endif - Shape* - replaceWithNewEquivalentShape(ExclusiveContext* cx, + static Shape* + replaceWithNewEquivalentShape(ExclusiveContext* cx, HandleNativeObject obj, Shape* existingShape, Shape* newShape = nullptr, bool accessorShape = false); @@ -512,7 +510,7 @@ class NativeObject : public ShapedObject */ bool setSlotSpan(ExclusiveContext* cx, uint32_t span); - bool toDictionaryMode(ExclusiveContext* cx); + static MOZ_MUST_USE bool toDictionaryMode(ExclusiveContext* cx, HandleNativeObject obj); private: friend class TenuringTracer; @@ -611,12 +609,15 @@ class NativeObject : public ShapedObject } public: - bool generateOwnShape(ExclusiveContext* cx, Shape* newShape = nullptr) { - return replaceWithNewEquivalentShape(cx, lastProperty(), newShape); + static MOZ_MUST_USE bool generateOwnShape(ExclusiveContext* cx, HandleNativeObject obj, + Shape* newShape = nullptr) + { + return replaceWithNewEquivalentShape(cx, obj, obj->lastProperty(), newShape); } - bool shadowingShapeChange(ExclusiveContext* cx, const Shape& shape); - bool clearFlag(ExclusiveContext* cx, BaseShape::Flag flag); + static MOZ_MUST_USE bool shadowingShapeChange(ExclusiveContext* cx, HandleNativeObject obj, + const Shape& shape); + static bool clearFlag(ExclusiveContext* cx, HandleNativeObject obj, BaseShape::Flag flag); // The maximum number of slots in an object. // |MAX_SLOTS_COUNT * sizeof(JS::Value)| shouldn't overflow @@ -743,10 +744,10 @@ class NativeObject : public ShapedObject bool allowDictionary = true); /* Add a data property whose id is not yet in this scope. */ - Shape* addDataProperty(ExclusiveContext* cx, - jsid id_, uint32_t slot, unsigned attrs); - Shape* addDataProperty(ExclusiveContext* cx, HandlePropertyName name, - uint32_t slot, unsigned attrs); + static Shape* addDataProperty(ExclusiveContext* cx, HandleNativeObject obj, + jsid id_, uint32_t slot, unsigned attrs); + static Shape* addDataProperty(ExclusiveContext* cx, HandleNativeObject obj, + HandlePropertyName name, uint32_t slot, unsigned attrs); /* Add or overwrite a property for id in this scope. */ static Shape* @@ -766,7 +767,7 @@ class NativeObject : public ShapedObject unsigned attrs, JSGetterOp getter, JSSetterOp setter); /* Remove the property named by id from this object. */ - bool removeProperty(ExclusiveContext* cx, jsid id); + static bool removeProperty(ExclusiveContext* cx, HandleNativeObject obj, jsid id); /* Clear the scope, making it empty. */ static void clear(ExclusiveContext* cx, HandleNativeObject obj); @@ -785,7 +786,8 @@ class NativeObject : public ShapedObject unsigned flags, ShapeTable::Entry* entry, bool allowDictionary, const AutoKeepShapeTables& keep); - bool fillInAfterSwap(JSContext* cx, const Vector<Value>& values, void* priv); + static MOZ_MUST_USE bool fillInAfterSwap(JSContext* cx, HandleNativeObject obj, + const Vector<Value>& values, void* priv); public: // Return true if this object has been converted from shared-immutable @@ -876,7 +878,7 @@ class NativeObject : public ShapedObject MOZ_ASSERT(end <= getDenseInitializedLength()); MOZ_ASSERT(!denseElementsAreCopyOnWrite()); for (size_t i = start; i < end; i++) - elements_[i].HeapSlot::~HeapSlot(); + elements_[i].destroy(); } /* @@ -885,7 +887,7 @@ class NativeObject : public ShapedObject */ void prepareSlotRangeForOverwrite(size_t start, size_t end) { for (size_t i = start; i < end; i++) - getSlotAddressUnchecked(i)->HeapSlot::~HeapSlot(); + getSlotAddressUnchecked(i)->destroy(); } public: @@ -1085,7 +1087,8 @@ class NativeObject : public ShapedObject for (uint32_t i = 0; i < count; ++i) elements_[dstStart + i].set(this, HeapSlot::Element, dstStart + i, src[i]); } else { - memcpy(&elements_[dstStart], src, count * sizeof(HeapSlot)); + memcpy(reinterpret_cast<Value*>(&elements_[dstStart]), src, + count * sizeof(Value)); elementsRangeWriteBarrierPost(dstStart, count); } } @@ -1094,7 +1097,7 @@ class NativeObject : public ShapedObject MOZ_ASSERT(dstStart + count <= getDenseCapacity()); MOZ_ASSERT(!denseElementsAreCopyOnWrite()); MOZ_ASSERT(!denseElementsAreFrozen()); - memcpy(&elements_[dstStart], src, count * sizeof(HeapSlot)); + memcpy(reinterpret_cast<Value*>(&elements_[dstStart]), src, count * sizeof(Value)); elementsRangeWriteBarrierPost(dstStart, count); } @@ -1142,10 +1145,14 @@ class NativeObject : public ShapedObject MOZ_ASSERT(!denseElementsAreCopyOnWrite()); MOZ_ASSERT(!denseElementsAreFrozen()); - memmove(elements_ + dstStart, elements_ + srcStart, count * sizeof(Value)); + memmove(elements_ + dstStart, elements_ + srcStart, count * sizeof(HeapSlot)); elementsRangeWriteBarrierPost(dstStart, count); } + inline DenseElementResult + setOrExtendDenseElements(ExclusiveContext* cx, uint32_t start, const Value* vp, uint32_t count, + ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update); + bool shouldConvertDoubleElements() { return getElementsHeader()->shouldConvertDoubleElements(); } @@ -1467,19 +1474,6 @@ NativeGetExistingProperty(JSContext* cx, HandleObject receiver, HandleNativeObje /* * */ -/* - * If obj has an already-resolved data property for id, return true and - * store the property value in *vp. - */ -extern bool -HasDataProperty(JSContext* cx, NativeObject* obj, jsid id, Value* vp); - -inline bool -HasDataProperty(JSContext* cx, NativeObject* obj, PropertyName* name, Value* vp) -{ - return HasDataProperty(cx, obj, NameToId(name), vp); -} - extern bool GetPropertyForNameLookup(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp); diff --git a/js/src/vm/ObjectGroup-inl.h b/js/src/vm/ObjectGroup-inl.h index 9074f4d974..d41343be64 100644 --- a/js/src/vm/ObjectGroup-inl.h +++ b/js/src/vm/ObjectGroup-inl.h @@ -108,20 +108,6 @@ ObjectGroup::maybePreliminaryObjects() return maybePreliminaryObjectsDontCheckGeneration(); } -inline UnboxedLayout* -ObjectGroup::maybeUnboxedLayout() -{ - maybeSweep(nullptr); - return maybeUnboxedLayoutDontCheckGeneration(); -} - -inline UnboxedLayout& -ObjectGroup::unboxedLayout() -{ - maybeSweep(nullptr); - return unboxedLayoutDontCheckGeneration(); -} - } // namespace js #endif /* vm_ObjectGroup_inl_h */ diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index d6a8fcaa46..ec0a7aec19 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -18,11 +18,10 @@ #include "vm/ArrayObject.h" #include "vm/Shape.h" #include "vm/TaggedProto.h" -#include "vm/UnboxedObject.h" #include "jsobjinlines.h" -#include "vm/UnboxedObject-inl.h" +#include "vm/NativeObject-inl.h" using namespace js; @@ -56,7 +55,6 @@ ObjectGroup::finalize(FreeOp* fop) if (newScriptDontCheckGeneration()) newScriptDontCheckGeneration()->clear(); fop->delete_(newScriptDontCheckGeneration()); - fop->delete_(maybeUnboxedLayoutDontCheckGeneration()); if (maybePreliminaryObjectsDontCheckGeneration()) maybePreliminaryObjectsDontCheckGeneration()->clear(); fop->delete_(maybePreliminaryObjectsDontCheckGeneration()); @@ -83,8 +81,6 @@ ObjectGroup::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const size_t n = 0; if (TypeNewScript* newScript = newScriptDontCheckGeneration()) n += newScript->sizeOfIncludingThis(mallocSizeOf); - if (UnboxedLayout* layout = maybeUnboxedLayoutDontCheckGeneration()) - n += layout->sizeOfIncludingThis(mallocSizeOf); return n; } @@ -253,7 +249,7 @@ ObjectGroup::useSingletonForAllocationSite(JSScript* script, jsbytecode* pc, con ///////////////////////////////////////////////////////////////////// bool -JSObject::shouldSplicePrototype(JSContext* cx) +JSObject::shouldSplicePrototype() { /* * During bootstrapping, if inference is enabled we need to make sure not @@ -266,33 +262,36 @@ JSObject::shouldSplicePrototype(JSContext* cx) return isSingleton(); } -bool -JSObject::splicePrototype(JSContext* cx, const Class* clasp, Handle<TaggedProto> proto) +/* static */ bool +JSObject::splicePrototype(JSContext* cx, HandleObject obj, const Class* clasp, + Handle<TaggedProto> proto) { - MOZ_ASSERT(cx->compartment() == compartment()); - - RootedObject self(cx, this); + MOZ_ASSERT(cx->compartment() == obj->compartment()); /* * For singleton groups representing only a single JSObject, the proto * can be rearranged as needed without destroying type information for * the old or new types. */ - MOZ_ASSERT(self->isSingleton()); + MOZ_ASSERT(obj->isSingleton()); // Windows may not appear on prototype chains. MOZ_ASSERT_IF(proto.isObject(), !IsWindow(proto.toObject())); - if (proto.isObject() && !proto.toObject()->setDelegate(cx)) - return false; + if (proto.isObject()) { + RootedObject protoObj(cx, proto.toObject()); + if (!JSObject::setDelegate(cx, protoObj)) + return false; + } // Force type instantiation when splicing lazy group. - RootedObjectGroup group(cx, self->getGroup(cx)); + RootedObjectGroup group(cx, JSObject::getGroup(cx, obj)); if (!group) return false; RootedObjectGroup protoGroup(cx, nullptr); if (proto.isObject()) { - protoGroup = proto.toObject()->getGroup(cx); + RootedObject protoObj(cx, proto.toObject()); + protoGroup = JSObject::getGroup(cx, protoObj); if (!protoGroup) return false; } @@ -311,7 +310,7 @@ JSObject::makeLazyGroup(JSContext* cx, HandleObject obj) /* De-lazification of functions can GC, so we need to do it up here. */ if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) { RootedFunction fun(cx, &obj->as<JSFunction>()); - if (!fun->getOrCreateScript(cx)) + if (!JSFunction::getOrCreateScript(cx, fun)) return nullptr; } @@ -350,7 +349,7 @@ JSObject::makeLazyGroup(JSContext* cx, HandleObject obj) JSObject::setNewGroupUnknown(JSContext* cx, const js::Class* clasp, JS::HandleObject obj) { ObjectGroup::setDefaultNewGroupUnknown(cx, clasp, obj); - return obj->setFlags(cx, BaseShape::NEW_GROUP_UNKNOWN); + return JSObject::setFlags(cx, obj, BaseShape::NEW_GROUP_UNKNOWN); } ///////////////////////////////////////////////////////////////////// @@ -495,13 +494,7 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp, if (associated->is<JSFunction>()) { // Canonicalize new functions to use the original one associated with its script. - JSFunction* fun = &associated->as<JSFunction>(); - if (fun->hasScript()) - associated = fun->nonLazyScript()->functionNonDelazifying(); - else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin()) - associated = fun->lazyScript()->functionNonDelazifying(); - else - associated = nullptr; + associated = associated->as<JSFunction>().maybeCanonicalFunction(); // If we have previously cleared the 'new' script information for this // function, don't try to construct another one. @@ -518,7 +511,7 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp, if (proto.isObject() && !proto.toObject()->isDelegate()) { RootedObject protoObj(cx, proto.toObject()); - if (!protoObj->setDelegate(cx)) + if (!JSObject::setDelegate(cx, protoObj)) return nullptr; // Objects which are prototypes of one another should be singletons, so @@ -536,8 +529,7 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp, if (p) { ObjectGroup* group = p->group; MOZ_ASSERT_IF(clasp, group->clasp() == clasp); - MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ || - group->clasp() == &UnboxedPlainObject::class_); + MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_); MOZ_ASSERT(group->proto() == proto); return group; } @@ -780,7 +772,7 @@ GetValueTypeForTable(const Value& v) return type; } -/* static */ JSObject* +/* static */ ArrayObject* ObjectGroup::newArrayObject(ExclusiveContext* cx, const Value* vp, size_t length, NewObjectKind newKind, NewArrayKind arrayKind) @@ -844,56 +836,13 @@ ObjectGroup::newArrayObject(ExclusiveContext* cx, AddTypePropertyId(cx, group, nullptr, JSID_VOID, elementType); - if (elementType != TypeSet::UnknownType()) { - // Keep track of the initial objects we create with this type. - // If the initial ones have a consistent shape and property types, we - // will try to use an unboxed layout for the group. - PreliminaryObjectArrayWithTemplate* preliminaryObjects = - cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr); - if (!preliminaryObjects) - return nullptr; - group->setPreliminaryObjects(preliminaryObjects); - } - if (!p.add(cx, *table, ObjectGroupCompartment::ArrayObjectKey(elementType), group)) return nullptr; } // The type of the elements being added will already be reflected in type - // information, but make sure when creating an unboxed array that the - // common element type is suitable for the unboxed representation. + // information. ShouldUpdateTypes updateTypes = ShouldUpdateTypes::DontUpdate; - if (!MaybeAnalyzeBeforeCreatingLargeArray(cx, group, vp, length)) - return nullptr; - if (group->maybePreliminaryObjects()) - group->maybePreliminaryObjects()->maybeAnalyze(cx, group); - if (group->maybeUnboxedLayout()) { - switch (group->unboxedLayout().elementType()) { - case JSVAL_TYPE_BOOLEAN: - if (elementType != TypeSet::BooleanType()) - updateTypes = ShouldUpdateTypes::Update; - break; - case JSVAL_TYPE_INT32: - if (elementType != TypeSet::Int32Type()) - updateTypes = ShouldUpdateTypes::Update; - break; - case JSVAL_TYPE_DOUBLE: - if (elementType != TypeSet::Int32Type() && elementType != TypeSet::DoubleType()) - updateTypes = ShouldUpdateTypes::Update; - break; - case JSVAL_TYPE_STRING: - if (elementType != TypeSet::StringType()) - updateTypes = ShouldUpdateTypes::Update; - break; - case JSVAL_TYPE_OBJECT: - if (elementType != TypeSet::NullType() && !elementType.get().isObjectUnchecked()) - updateTypes = ShouldUpdateTypes::Update; - break; - default: - MOZ_CRASH(); - } - } - return NewCopiedArrayTryUseGroup(cx, group, vp, length, newKind, updateTypes); } @@ -903,49 +852,15 @@ GiveObjectGroup(ExclusiveContext* cx, JSObject* source, JSObject* target) { MOZ_ASSERT(source->group() != target->group()); - if (!target->is<ArrayObject>() && !target->is<UnboxedArrayObject>()) - return true; - - if (target->group()->maybePreliminaryObjects()) { - bool force = IsInsideNursery(source); - target->group()->maybePreliminaryObjects()->maybeAnalyze(cx, target->group(), force); - } - - if (target->is<ArrayObject>()) { - ObjectGroup* sourceGroup = source->group(); - - if (source->is<UnboxedArrayObject>()) { - Shape* shape = target->as<ArrayObject>().lastProperty(); - if (!UnboxedArrayObject::convertToNativeWithGroup(cx, source, target->group(), shape)) - return false; - } else if (source->is<ArrayObject>()) { - source->setGroup(target->group()); - } else { - return true; - } - - if (sourceGroup->maybePreliminaryObjects()) - sourceGroup->maybePreliminaryObjects()->unregisterObject(source); - if (target->group()->maybePreliminaryObjects()) - target->group()->maybePreliminaryObjects()->registerNewObject(source); - - for (size_t i = 0; i < source->as<ArrayObject>().getDenseInitializedLength(); i++) { - Value v = source->as<ArrayObject>().getDenseElement(i); - AddTypePropertyId(cx, source->group(), source, JSID_VOID, v); - } - + if (!target->is<ArrayObject>() || !source->is<ArrayObject>()) { return true; } - if (target->is<UnboxedArrayObject>()) { - if (!source->is<UnboxedArrayObject>()) - return true; - if (source->as<UnboxedArrayObject>().elementType() != JSVAL_TYPE_INT32) - return true; - if (target->as<UnboxedArrayObject>().elementType() != JSVAL_TYPE_DOUBLE) - return true; + source->setGroup(target->group()); - return source->as<UnboxedArrayObject>().convertInt32ToDouble(cx, target->group()); + for (size_t i = 0; i < source->as<ArrayObject>().getDenseInitializedLength(); i++) { + Value v = source->as<ArrayObject>().getDenseElement(i); + AddTypePropertyId(cx, source->group(), source, JSID_VOID, v); } return true; @@ -1054,46 +969,6 @@ js::CombinePlainObjectPropertyTypes(ExclusiveContext* cx, JSObject* newObj, } } } - } else if (newObj->is<UnboxedPlainObject>()) { - const UnboxedLayout& layout = newObj->as<UnboxedPlainObject>().layout(); - const int32_t* traceList = layout.traceList(); - if (!traceList) - return true; - - uint8_t* newData = newObj->as<UnboxedPlainObject>().data(); - uint8_t* oldData = oldObj->as<UnboxedPlainObject>().data(); - - for (; *traceList != -1; traceList++) {} - traceList++; - for (; *traceList != -1; traceList++) { - JSObject* newInnerObj = *reinterpret_cast<JSObject**>(newData + *traceList); - JSObject* oldInnerObj = *reinterpret_cast<JSObject**>(oldData + *traceList); - - if (!newInnerObj || !oldInnerObj || SameGroup(oldInnerObj, newInnerObj)) - continue; - - if (!GiveObjectGroup(cx, newInnerObj, oldInnerObj)) - return false; - - if (SameGroup(oldInnerObj, newInnerObj)) - continue; - - if (!GiveObjectGroup(cx, oldInnerObj, newInnerObj)) - return false; - - if (SameGroup(oldInnerObj, newInnerObj)) { - for (size_t i = 1; i < ncompare; i++) { - if (compare[i].isObject() && SameGroup(&compare[i].toObject(), newObj)) { - uint8_t* otherData = compare[i].toObject().as<UnboxedPlainObject>().data(); - JSObject* otherInnerObj = *reinterpret_cast<JSObject**>(otherData + *traceList); - if (otherInnerObj && !SameGroup(otherInnerObj, newInnerObj)) { - if (!GiveObjectGroup(cx, otherInnerObj, newInnerObj)) - return false; - } - } - } - } - } } return true; @@ -1317,12 +1192,6 @@ ObjectGroup::newPlainObject(ExclusiveContext* cx, IdValuePair* properties, size_ RootedObjectGroup group(cx, p->value().group); - // Watch for existing groups which now use an unboxed layout. - if (group->maybeUnboxedLayout()) { - MOZ_ASSERT(group->unboxedLayout().properties().length() == nproperties); - return UnboxedPlainObject::createWithProperties(cx, group, newKind, properties); - } - // Update property types according to the properties we are about to add. // Do this before we do anything which can GC, which might move or remove // this table entry. @@ -1509,18 +1378,6 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode* } } - if (kind == JSProto_Array && - (JSOp(*pc) == JSOP_NEWARRAY || IsCallPC(pc)) && - cx->options().unboxedArrays()) - { - PreliminaryObjectArrayWithTemplate* preliminaryObjects = - cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr); - if (preliminaryObjects) - res->setPreliminaryObjects(preliminaryObjects); - else - cx->recoverFromOutOfMemory(); - } - if (!table->add(p, key, res)) { ReportOutOfMemory(cx); return nullptr; diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h index 4e24de9f14..0b6eaee51b 100644 --- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -20,7 +20,6 @@ namespace js { class TypeDescr; -class UnboxedLayout; class PreliminaryObjectArrayWithTemplate; class TypeNewScript; @@ -154,16 +153,6 @@ class ObjectGroup : public gc::TenuredCell // For some plain objects, the addendum stores a PreliminaryObjectArrayWithTemplate. Addendum_PreliminaryObjects, - // When objects in this group have an unboxed representation, the - // addendum stores an UnboxedLayout (which might have a TypeNewScript - // as well, if the group is also constructed using 'new'). - Addendum_UnboxedLayout, - - // If this group is used by objects that have been converted from an - // unboxed representation and/or have the same allocation kind as such - // objects, the addendum points to that unboxed group. - Addendum_OriginalUnboxedGroup, - // When used by typed objects, the addendum stores a TypeDescr. Addendum_TypeDescr }; @@ -185,7 +174,6 @@ class ObjectGroup : public gc::TenuredCell return nullptr; } - TypeNewScript* anyNewScript(); void detachNewScript(bool writeBarrier, ObjectGroup* replacement); ObjectGroupFlags flagsDontCheckGeneration() const { @@ -225,34 +213,6 @@ class ObjectGroup : public gc::TenuredCell maybePreliminaryObjectsDontCheckGeneration(); } - inline UnboxedLayout* maybeUnboxedLayout(); - inline UnboxedLayout& unboxedLayout(); - - UnboxedLayout* maybeUnboxedLayoutDontCheckGeneration() const { - if (addendumKind() == Addendum_UnboxedLayout) - return reinterpret_cast<UnboxedLayout*>(addendum_); - return nullptr; - } - - UnboxedLayout& unboxedLayoutDontCheckGeneration() const { - MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout); - return *maybeUnboxedLayoutDontCheckGeneration(); - } - - void setUnboxedLayout(UnboxedLayout* layout) { - setAddendum(Addendum_UnboxedLayout, layout); - } - - ObjectGroup* maybeOriginalUnboxedGroup() const { - if (addendumKind() == Addendum_OriginalUnboxedGroup) - return reinterpret_cast<ObjectGroup*>(addendum_); - return nullptr; - } - - void setOriginalUnboxedGroup(ObjectGroup* group) { - setAddendum(Addendum_OriginalUnboxedGroup, group); - } - TypeDescr* maybeTypeDescr() { // Note: there is no need to sweep when accessing the type descriptor // of an object, as it is strongly held and immutable. @@ -313,9 +273,8 @@ class ObjectGroup : public gc::TenuredCell * that can be read out of that property in actual JS objects. In native * objects, property types account for plain data properties (those with a * slot and no getter or setter hook) and dense elements. In typed objects - * and unboxed objects, property types account for object and value - * properties and elements in the object, and expando properties in unboxed - * objects. + * property types account for object and value properties and elements in + * the object. * * For accesses on these properties, the correspondence is as follows: * @@ -338,10 +297,9 @@ class ObjectGroup : public gc::TenuredCell * 2. Array lengths are special cased by the compiler and VM and are not * reflected in property types. * - * 3. In typed objects (but not unboxed objects), the initial values of - * properties (null pointers and undefined values) are not reflected in - * the property types. These values are always possible when reading the - * property. + * 3. In typed objects, the initial values of properties (null pointers and + * undefined values) are not reflected in the property types. These + * values are always possible when reading the property. * * We establish these by using write barriers on calls to setProperty and * defineProperty which are on native properties, and on any jitcode which @@ -455,12 +413,6 @@ class ObjectGroup : public gc::TenuredCell return &flags_; } - // Get the bit pattern stored in an object's addendum when it has an - // original unboxed group. - static inline int32_t addendumOriginalUnboxedGroupValue() { - return Addendum_OriginalUnboxedGroup << OBJECT_FLAG_ADDENDUM_SHIFT; - } - inline uint32_t basePropertyCount(); private: @@ -505,14 +457,14 @@ class ObjectGroup : public gc::TenuredCell UnknownIndex // Make an array with an unknown element type. }; - // Create an ArrayObject or UnboxedArrayObject with the specified elements - // and a group specialized for the elements. - static JSObject* newArrayObject(ExclusiveContext* cx, const Value* vp, size_t length, - NewObjectKind newKind, - NewArrayKind arrayKind = NewArrayKind::Normal); + // Create an ArrayObject with the specified elements and a group specialized + // for the elements. + static ArrayObject* newArrayObject(ExclusiveContext* cx, const Value* vp, size_t length, + NewObjectKind newKind, + NewArrayKind arrayKind = NewArrayKind::Normal); - // Create a PlainObject or UnboxedPlainObject with the specified properties - // and a group specialized for those properties. + // Create a PlainObject with the specified properties and a group specialized + // for those properties. static JSObject* newPlainObject(ExclusiveContext* cx, IdValuePair* properties, size_t nproperties, NewObjectKind newKind); diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 4b044c8d8f..3c4d61a673 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1281,17 +1281,7 @@ * Stack: receiver, obj, propval => obj[propval] */ \ macro(JSOP_GETELEM_SUPER, 125, "getelem-super", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) \ - /* - * Pushes newly created array for a spread call onto the stack. This has - * the same semantics as JSOP_NEWARRAY, but is distinguished to avoid - * using unboxed arrays in spread calls, which would make compiling spread - * calls in baseline more complex. - * Category: Literals - * Type: Array - * Operands: uint32_t length - * Stack: => obj - */ \ - macro(JSOP_SPREADCALLARRAY, 126, "spreadcallarray", NULL, 5, 0, 1, JOF_UINT32) \ + macro(JSOP_UNUSED126, 126, "unused126", NULL, 5, 0, 1, JOF_UINT32) \ \ /* * Defines the given function on the current scope. @@ -2292,14 +2282,23 @@ * Operands: * Stack: => */ \ - macro(JSOP_JUMPTARGET, 230, "jumptarget", NULL, 1, 0, 0, JOF_BYTE) + macro(JSOP_JUMPTARGET, 230, "jumptarget", NULL, 1, 0, 0, JOF_BYTE)\ + /* + * Like JSOP_CALL, but tells the function that the return value is ignored. + * stack. + * Category: Statements + * Type: Function + * Operands: uint16_t argc + * Stack: callee, this, args[0], ..., args[argc-1] => rval + * nuses: (argc+2) + */ \ + macro(JSOP_CALL_IGNORES_RV, 231, "call-ignores-rv", NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) /* * In certain circumstances it may be useful to "pad out" the opcode space to * a power of two. Use this macro to do so. */ #define FOR_EACH_TRAILING_UNUSED_OPCODE(macro) \ - macro(231) \ macro(232) \ macro(233) \ macro(234) \ diff --git a/js/src/vm/ProxyObject.h b/js/src/vm/ProxyObject.h index a0a929b20a..d86d72cc98 100644 --- a/js/src/vm/ProxyObject.h +++ b/js/src/vm/ProxyObject.h @@ -104,7 +104,7 @@ class ProxyObject : public ShapedObject public: static unsigned grayLinkExtraSlot(JSObject* obj); - void renew(JSContext* cx, const BaseProxyHandler* handler, const Value& priv); + void renew(const BaseProxyHandler* handler, const Value& priv); static void trace(JSTracer* trc, JSObject* obj); diff --git a/js/src/vm/ReceiverGuard.cpp b/js/src/vm/ReceiverGuard.cpp index 97df908c3b..e95e8a208a 100644 --- a/js/src/vm/ReceiverGuard.cpp +++ b/js/src/vm/ReceiverGuard.cpp @@ -7,7 +7,6 @@ #include "vm/ReceiverGuard.h" #include "builtin/TypedObject.h" -#include "vm/UnboxedObject.h" #include "jsobjinlines.h" using namespace js; @@ -16,11 +15,7 @@ ReceiverGuard::ReceiverGuard(JSObject* obj) : group(nullptr), shape(nullptr) { if (obj) { - if (obj->is<UnboxedPlainObject>()) { - group = obj->group(); - if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) - shape = expando->lastProperty(); - } else if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) { + if (obj->is<TypedObject>()) { group = obj->group(); } else { shape = obj->maybeShape(); @@ -33,9 +28,7 @@ ReceiverGuard::ReceiverGuard(ObjectGroup* group, Shape* shape) { if (group) { const Class* clasp = group->clasp(); - if (clasp == &UnboxedPlainObject::class_) { - // Keep both group and shape. - } else if (clasp == &UnboxedArrayObject::class_ || IsTypedObjectClass(clasp)) { + if (IsTypedObjectClass(clasp)) { this->shape = nullptr; } else { this->group = nullptr; @@ -46,12 +39,8 @@ ReceiverGuard::ReceiverGuard(ObjectGroup* group, Shape* shape) /* static */ int32_t HeapReceiverGuard::keyBits(JSObject* obj) { - if (obj->is<UnboxedPlainObject>()) { - // Both the group and shape need to be guarded for unboxed plain objects. - return obj->as<UnboxedPlainObject>().maybeExpando() ? 0 : 1; - } - if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) { - // Only the group needs to be guarded for unboxed arrays and typed objects. + if (obj->is<TypedObject>()) { + // Only the group needs to be guarded for typed objects. return 2; } // Other objects only need the shape to be guarded. diff --git a/js/src/vm/ReceiverGuard.h b/js/src/vm/ReceiverGuard.h index 459cc0012d..c14f0d83b5 100644 --- a/js/src/vm/ReceiverGuard.h +++ b/js/src/vm/ReceiverGuard.h @@ -28,11 +28,6 @@ namespace js { // TypedObject: The structure of a typed object is determined by its group. // All typed objects with the same group have the same class, prototype, and // own properties. -// -// UnboxedPlainObject: The structure of an unboxed plain object is determined -// by its group and its expando object's shape, if there is one. All unboxed -// plain objects with the same group and expando shape have the same -// properties except those stored in the expando's dense elements. class HeapReceiverGuard; class RootedReceiverGuard; diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index e0b44e1eb7..ef97ed8165 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -129,10 +129,10 @@ RegExpSharedReadBarrier(JSContext* cx, RegExpShared* shared) shared->unmarkGray(); } -bool -RegExpObject::getShared(JSContext* cx, RegExpGuard* g) +/* static */ bool +RegExpObject::getShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard* g) { - if (RegExpShared* shared = maybeShared()) { + if (RegExpShared* shared = regexp->maybeShared()) { // Fetching a RegExpShared from an object requires a read // barrier, as the shared pointer might be weak. RegExpSharedReadBarrier(cx, shared); @@ -141,7 +141,7 @@ RegExpObject::getShared(JSContext* cx, RegExpGuard* g) return true; } - return createShared(cx, g); + return createShared(cx, regexp, g); } /* static */ bool @@ -199,7 +199,7 @@ RegExpObject::trace(JSTracer* trc, JSObject* obj) static JSObject* CreateRegExpPrototype(JSContext* cx, JSProtoKey key) { - return cx->global()->createBlankPrototype(cx, &RegExpObject::protoClass_); + return GlobalObject::createBlankPrototype(cx, cx->global(), &RegExpObject::protoClass_); } static const ClassOps RegExpObjectClassOps = { @@ -279,16 +279,14 @@ RegExpObject::create(ExclusiveContext* cx, HandleAtom source, RegExpFlag flags, return regexp; } -bool -RegExpObject::createShared(JSContext* cx, RegExpGuard* g) +/* static */ bool +RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard* g) { - Rooted<RegExpObject*> self(cx, this); - - MOZ_ASSERT(!maybeShared()); - if (!cx->compartment()->regExps.get(cx, getSource(), getFlags(), g)) + MOZ_ASSERT(!regexp->maybeShared()); + if (!cx->compartment()->regExps.get(cx, regexp->getSource(), regexp->getFlags(), g)) return false; - self->setShared(**g); + regexp->setShared(**g); return true; } @@ -300,7 +298,8 @@ RegExpObject::assignInitialShape(ExclusiveContext* cx, Handle<RegExpObject*> sel JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0); /* The lastIndex property alone is writable but non-configurable. */ - return self->addDataProperty(cx, cx->names().lastIndex, LAST_INDEX_SLOT, JSPROP_PERMANENT); + return NativeObject::addDataProperty(cx, self, cx->names().lastIndex, LAST_INDEX_SLOT, + JSPROP_PERMANENT); } void @@ -891,11 +890,12 @@ RegExpShared::dumpBytecode(JSContext* cx, bool match_only, HandleLinearString in return true; } -bool -RegExpObject::dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input) +/* static */ bool +RegExpObject::dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp, + bool match_only, HandleLinearString input) { RegExpGuard g(cx); - if (!getShared(cx, &g)) + if (!getShared(cx, regexp, &g)) return false; return g.re()->dumpBytecode(cx, match_only, input); @@ -1430,7 +1430,7 @@ js::CloneRegExpObject(JSContext* cx, JSObject* obj_) Rooted<JSAtom*> source(cx, regex->getSource()); RegExpGuard g(cx); - if (!regex->getShared(cx, &g)) + if (!RegExpObject::getShared(cx, regex, &g)) return nullptr; clone->initAndZeroLastIndex(source, g->getFlags(), cx); diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index dc428a9735..f1ea101ed5 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -483,7 +483,8 @@ class RegExpObject : public NativeObject static bool isOriginalFlagGetter(JSNative native, RegExpFlag* mask); - bool getShared(JSContext* cx, RegExpGuard* g); + static MOZ_MUST_USE bool getShared(JSContext* cx, Handle<RegExpObject*> regexp, + RegExpGuard* g); void setShared(RegExpShared& shared) { MOZ_ASSERT(!maybeShared()); @@ -500,7 +501,8 @@ class RegExpObject : public NativeObject void initAndZeroLastIndex(HandleAtom source, RegExpFlag flags, ExclusiveContext* cx); #ifdef DEBUG - bool dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input); + static MOZ_MUST_USE bool dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp, + bool match_only, HandleLinearString input); #endif private: @@ -508,7 +510,8 @@ class RegExpObject : public NativeObject * Precondition: the syntax for |source| has already been validated. * Side effect: sets the private field. */ - bool createShared(JSContext* cx, RegExpGuard* g); + static MOZ_MUST_USE bool createShared(JSContext* cx, Handle<RegExpObject*> regexp, + RegExpGuard* g); RegExpShared* maybeShared() const { return static_cast<RegExpShared*>(NativeObject::getPrivate(PRIVATE_SLOT)); } @@ -531,7 +534,7 @@ inline bool RegExpToShared(JSContext* cx, HandleObject obj, RegExpGuard* g) { if (obj->is<RegExpObject>()) - return obj->as<RegExpObject>().getShared(cx, g); + return RegExpObject::getShared(cx, obj.as<RegExpObject>(), g); return Proxy::regexp_toShared(cx, obj, g); } diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 174e235942..284a4f3d7c 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -34,7 +34,6 @@ #include "jsnativestack.h" #include "jsobj.h" #include "jsscript.h" -#include "jswatchpoint.h" #include "jswin.h" #include "jswrapper.h" @@ -147,7 +146,6 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) updateChildRuntimeCount(parentRuntime), #endif interrupt_(false), - telemetryCallback(nullptr), handlingSegFault(false), handlingJitInterrupt_(false), interruptCallbackDisabled(false), @@ -452,19 +450,6 @@ JSRuntime::destroyRuntime() } void -JSRuntime::addTelemetry(int id, uint32_t sample, const char* key) -{ - if (telemetryCallback) - (*telemetryCallback)(id, sample, key); -} - -void -JSRuntime::setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback callback) -{ - rt->telemetryCallback = callback; -} - -void JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes* rtSizes) { // Several tables in the runtime enumerated below can be used off thread. @@ -603,7 +588,7 @@ JSRuntime::requestInterrupt(InterruptMode mode) // Atomics.wait(). fx.lock(); if (fx.isWaiting()) - fx.wake(FutexRuntime::WakeForJSInterrupt); + fx.notify(FutexRuntime::NotifyForJSInterrupt); fx.unlock(); InterruptRunningJitCode(this); } diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 735adadf2f..e60371e38c 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -11,11 +11,11 @@ #include "mozilla/Attributes.h" #include "mozilla/LinkedList.h" #include "mozilla/MemoryReporting.h" -#include "mozilla/PodOperations.h" #include "mozilla/Scoped.h" #include "mozilla/ThreadLocal.h" #include "mozilla/Vector.h" +#include <algorithm> #include <setjmp.h> #include "jsatom.h" @@ -577,17 +577,7 @@ struct JSRuntime : public JS::shadow::Runtime, #endif mozilla::Atomic<uint32_t, mozilla::Relaxed> interrupt_; - - /* Call this to accumulate telemetry data. */ - JSAccumulateTelemetryDataCallback telemetryCallback; public: - // Accumulates data for Firefox telemetry. |id| is the ID of a JS_TELEMETRY_* - // histogram. |key| provides an additional key to identify the histogram. - // |sample| is the data to add to the histogram. - void addTelemetry(int id, uint32_t sample, const char* key = nullptr); - - void setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback callback); - enum InterruptMode { RequestInterruptUrgent, RequestInterruptCanWait @@ -1504,20 +1494,21 @@ PerThreadData::exclusiveThreadsPresent() static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(Value* vec, size_t len) { - mozilla::PodZero(vec, len); + // Don't PodZero here because JS::Value is non-trivial. + for (size_t i = 0; i < len; i++) + vec[i].setDouble(+0.0); } static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(Value* beg, Value* end) { - mozilla::PodZero(beg, end - beg); + MakeRangeGCSafe(beg, end - beg); } static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(jsid* beg, jsid* end) { - for (jsid* id = beg; id != end; ++id) - *id = INT_TO_JSID(0); + std::fill(beg, end, INT_TO_JSID(0)); } static MOZ_ALWAYS_INLINE void @@ -1529,13 +1520,13 @@ MakeRangeGCSafe(jsid* vec, size_t len) static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(Shape** beg, Shape** end) { - mozilla::PodZero(beg, end - beg); + std::fill(beg, end, nullptr); } static MOZ_ALWAYS_INLINE void MakeRangeGCSafe(Shape** vec, size_t len) { - mozilla::PodZero(vec, len); + MakeRangeGCSafe(vec, vec + len); } static MOZ_ALWAYS_INLINE void diff --git a/js/src/vm/Scope.cpp b/js/src/vm/Scope.cpp index 112b345862..0f80d7b691 100644 --- a/js/src/vm/Scope.cpp +++ b/js/src/vm/Scope.cpp @@ -191,12 +191,12 @@ template <typename ConcreteScope> static UniquePtr<typename ConcreteScope::Data> NewEmptyScopeData(ExclusiveContext* cx, uint32_t length = 0) { - uint8_t* bytes = cx->zone()->pod_calloc<uint8_t>(ConcreteScope::sizeOfData(length)); + uint8_t* bytes = cx->zone()->pod_malloc<uint8_t>(ConcreteScope::sizeOfData(length)); if (!bytes) ReportOutOfMemory(cx); auto data = reinterpret_cast<typename ConcreteScope::Data*>(bytes); if (data) - new (data) typename ConcreteScope::Data(); + new (data) typename ConcreteScope::Data(length); return UniquePtr<typename ConcreteScope::Data>(data); } @@ -273,7 +273,7 @@ Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope, } for (uint32_t i = 0; i < length; i++) { - if (!XDRBindingName(xdr, &data->names[i])) { + if (!XDRBindingName(xdr, &data->trailingNames[i])) { if (mode == XDR_DECODE) { DeleteScopeData(data.get()); data.set(nullptr); @@ -669,6 +669,14 @@ FunctionScope::script() const return canonicalFunction()->nonLazyScript(); } +/* static */ bool +FunctionScope::isSpecialName(ExclusiveContext* cx, JSAtom* name) +{ + return name == cx->names().arguments || + name == cx->names().dotThis || + name == cx->names().dotGenerator; +} + /* static */ Shape* FunctionScope::getEmptyEnvironmentShape(ExclusiveContext* cx, bool hasParameterExprs) { @@ -1250,7 +1258,7 @@ BindingIter::init(LexicalScope::Data& data, uint32_t firstFrameSlot, uint8_t fla init(0, 0, 0, 0, 0, 0, CanHaveEnvironmentSlots | flags, firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_), - data.names, data.length); + data.trailingNames.start(), data.length); } else { // imports - [0, 0) // positional formals - [0, 0) @@ -1262,7 +1270,7 @@ BindingIter::init(LexicalScope::Data& data, uint32_t firstFrameSlot, uint8_t fla init(0, 0, 0, 0, 0, data.constStart, CanHaveFrameSlots | CanHaveEnvironmentSlots | flags, firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_), - data.names, data.length); + data.trailingNames.start(), data.length); } } @@ -1283,7 +1291,7 @@ BindingIter::init(FunctionScope::Data& data, uint8_t flags) init(0, data.nonPositionalFormalStart, data.varStart, data.varStart, data.length, data.length, flags, 0, JSSLOT_FREE(&CallObject::class_), - data.names, data.length); + data.trailingNames.start(), data.length); } void @@ -1299,7 +1307,7 @@ BindingIter::init(VarScope::Data& data, uint32_t firstFrameSlot) init(0, 0, 0, 0, data.length, data.length, CanHaveFrameSlots | CanHaveEnvironmentSlots, firstFrameSlot, JSSLOT_FREE(&VarEnvironmentObject::class_), - data.names, data.length); + data.trailingNames.start(), data.length); } void @@ -1315,7 +1323,7 @@ BindingIter::init(GlobalScope::Data& data) init(0, 0, 0, data.varStart, data.letStart, data.constStart, CannotHaveSlots, UINT32_MAX, UINT32_MAX, - data.names, data.length); + data.trailingNames.start(), data.length); } void @@ -1343,7 +1351,7 @@ BindingIter::init(EvalScope::Data& data, bool strict) // consts - [data.length, data.length) init(0, 0, 0, data.varStart, data.length, data.length, flags, firstFrameSlot, firstEnvironmentSlot, - data.names, data.length); + data.trailingNames.start(), data.length); } void @@ -1359,7 +1367,7 @@ BindingIter::init(ModuleScope::Data& data) init(data.varStart, data.varStart, data.varStart, data.varStart, data.letStart, data.constStart, CanHaveFrameSlots | CanHaveEnvironmentSlots, 0, JSSLOT_FREE(&ModuleEnvironmentObject::class_), - data.names, data.length); + data.trailingNames.start(), data.length); } PositionalFormalParameterIter::PositionalFormalParameterIter(JSScript* script) diff --git a/js/src/vm/Scope.h b/js/src/vm/Scope.h index 5304d6713e..4a4ae80908 100644 --- a/js/src/vm/Scope.h +++ b/js/src/vm/Scope.h @@ -12,6 +12,7 @@ #include "jsobj.h" #include "jsopcode.h" +#include "jsutil.h" #include "gc/Heap.h" #include "gc/Policy.h" @@ -111,6 +112,47 @@ class BindingName void trace(JSTracer* trc); }; +/** + * The various {Global,Module,...}Scope::Data classes consist of always-present + * bits, then a trailing array of BindingNames. The various Data classes all + * end in a TrailingNamesArray that contains sized/aligned space for *one* + * BindingName. Data instances that contain N BindingNames, are then allocated + * in sizeof(Data) + (space for (N - 1) BindingNames). Because this class's + * |data_| field is properly sized/aligned, the N-BindingName array can start + * at |data_|. + * + * This is concededly a very low-level representation, but we want to only + * allocate once for data+bindings both, and this does so approximately as + * elegantly as C++ allows. + */ +class TrailingNamesArray +{ + private: + alignas(BindingName) unsigned char data_[sizeof(BindingName)]; + + private: + // Some versions of GCC treat it as a -Wstrict-aliasing violation (ergo a + // -Werror compile error) to reinterpret_cast<> |data_| to |T*|, even + // through |void*|. Placing the latter cast in these separate functions + // breaks the chain such that affected GCC versions no longer warn/error. + void* ptr() { + return data_; + } + + public: + // Explicitly ensure no one accidentally allocates scope data without + // poisoning its trailing names. + TrailingNamesArray() = delete; + + explicit TrailingNamesArray(size_t nameCount) { + if (nameCount) + JS_POISON(&data_, 0xCC, sizeof(BindingName) * nameCount); + } + BindingName* start() { return reinterpret_cast<BindingName*>(ptr()); } + + BindingName& operator[](size_t i) { return start()[i]; } +}; + class BindingLocation { public: @@ -337,16 +379,19 @@ class LexicalScope : public Scope // // lets - [0, constStart) // consts - [constStart, length) - uint32_t constStart; - uint32_t length; + uint32_t constStart = 0; + uint32_t length = 0; // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. - uint32_t nextFrameSlot; + uint32_t nextFrameSlot = 0; // The array of tagged JSAtom* names, allocated beyond the end of the // struct. - BindingName names[1]; + TrailingNamesArray trailingNames; + + explicit Data(size_t nameCount) : trailingNames(nameCount) {} + Data() = delete; void trace(JSTracer* trc); }; @@ -401,10 +446,11 @@ Scope::is<LexicalScope>() const } // -// Scope corresponding to a function. Holds formal parameter names and, if the -// function parameters contain no expressions that might possibly be -// evaluated, the function's var bindings. For example, in these functions, -// the FunctionScope will store a/b/c bindings but not d/e/f bindings: +// Scope corresponding to a function. Holds formal parameter names, special +// internal names (see FunctionScope::isSpecialName), and, if the function +// parameters contain no expressions that might possibly be evaluated, the +// function's var bindings. For example, in these functions, the FunctionScope +// will store a/b/c bindings but not d/e/f bindings: // // function f1(a, b) { // var c; @@ -433,11 +479,11 @@ class FunctionScope : public Scope // The canonical function of the scope, as during a scope walk we // often query properties of the JSFunction (e.g., is the function an // arrow). - GCPtrFunction canonicalFunction; + GCPtrFunction canonicalFunction = {}; // If parameter expressions are present, parameters act like lexical // bindings. - bool hasParameterExprs; + bool hasParameterExprs = false; // Bindings are sorted by kind in both frames and environments. // @@ -452,17 +498,20 @@ class FunctionScope : public Scope // positional formals - [0, nonPositionalFormalStart) // other formals - [nonPositionalParamStart, varStart) // vars - [varStart, length) - uint16_t nonPositionalFormalStart; - uint16_t varStart; - uint32_t length; + uint16_t nonPositionalFormalStart = 0; + uint16_t varStart = 0; + uint32_t length = 0; // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. - uint32_t nextFrameSlot; + uint32_t nextFrameSlot = 0; // The array of tagged JSAtom* names, allocated beyond the end of the // struct. - BindingName names[1]; + TrailingNamesArray trailingNames; + + explicit Data(size_t nameCount) : trailingNames(nameCount) {} + Data() = delete; void trace(JSTracer* trc); }; @@ -514,6 +563,8 @@ class FunctionScope : public Scope return data().nonPositionalFormalStart; } + static bool isSpecialName(ExclusiveContext* cx, JSAtom* name); + static Shape* getEmptyEnvironmentShape(ExclusiveContext* cx, bool hasParameterExprs); }; @@ -548,15 +599,18 @@ class VarScope : public Scope struct Data { // All bindings are vars. - uint32_t length; + uint32_t length = 0; // Frame slots [firstFrameSlot(), nextFrameSlot) are live when this is // the innermost scope. - uint32_t nextFrameSlot; + uint32_t nextFrameSlot = 0; // The array of tagged JSAtom* names, allocated beyond the end of the // struct. - BindingName names[1]; + TrailingNamesArray trailingNames; + + explicit Data(size_t nameCount) : trailingNames(nameCount) {} + Data() = delete; void trace(JSTracer* trc); }; @@ -638,14 +692,17 @@ class GlobalScope : public Scope // vars - [varStart, letStart) // lets - [letStart, constStart) // consts - [constStart, length) - uint32_t varStart; - uint32_t letStart; - uint32_t constStart; - uint32_t length; + uint32_t varStart = 0; + uint32_t letStart = 0; + uint32_t constStart = 0; + uint32_t length = 0; // The array of tagged JSAtom* names, allocated beyond the end of the // struct. - BindingName names[1]; + TrailingNamesArray trailingNames; + + explicit Data(size_t nameCount) : trailingNames(nameCount) {} + Data() = delete; void trace(JSTracer* trc); }; @@ -736,16 +793,19 @@ class EvalScope : public Scope // // top-level funcs - [0, varStart) // vars - [varStart, length) - uint32_t varStart; - uint32_t length; + uint32_t varStart = 0; + uint32_t length = 0; // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. - uint32_t nextFrameSlot; + uint32_t nextFrameSlot = 0; // The array of tagged JSAtom* names, allocated beyond the end of the // struct. - BindingName names[1]; + TrailingNamesArray trailingNames; + + explicit Data(size_t nameCount) : trailingNames(nameCount) {} + Data() = delete; void trace(JSTracer* trc); }; @@ -827,7 +887,7 @@ class ModuleScope : public Scope struct Data { // The module of the scope. - GCPtr<ModuleObject*> module; + GCPtr<ModuleObject*> module = {}; // Bindings are sorted by kind. // @@ -835,18 +895,21 @@ class ModuleScope : public Scope // vars - [varStart, letStart) // lets - [letStart, constStart) // consts - [constStart, length) - uint32_t varStart; - uint32_t letStart; - uint32_t constStart; - uint32_t length; + uint32_t varStart = 0; + uint32_t letStart = 0; + uint32_t constStart = 0; + uint32_t length = 0; // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. - uint32_t nextFrameSlot; + uint32_t nextFrameSlot = 0; // The array of tagged JSAtom* names, allocated beyond the end of the // struct. - BindingName names[1]; + TrailingNamesArray trailingNames; + + explicit Data(size_t nameCount) : trailingNames(nameCount) {} + Data() = delete; void trace(JSTracer* trc); }; diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 3e7baccade..82d2cde642 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -477,7 +477,7 @@ intrinsic_FinishBoundFunctionInit(JSContext* cx, unsigned argc, Value* vp) // Try to avoid invoking the resolve hook. if (targetObj->is<JSFunction>() && !targetObj->as<JSFunction>().hasResolvedLength()) { RootedValue targetLength(cx); - if (!targetObj->as<JSFunction>().getUnresolvedLength(cx, &targetLength)) + if (!JSFunction::getUnresolvedLength(cx, targetObj.as<JSFunction>(), &targetLength)) return false; length = Max(0.0, targetLength.toNumber() - argCount); @@ -1873,23 +1873,6 @@ intrinsic_RuntimeDefaultLocale(JSContext* cx, unsigned argc, Value* vp) } static bool -intrinsic_AddContentTelemetry(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - MOZ_ASSERT(args.length() == 2); - - int id = args[0].toInt32(); - MOZ_ASSERT(id < JS_TELEMETRY_END); - MOZ_ASSERT(id >= 0); - - if (!cx->compartment()->isProbablySystemOrAddonCode()) - cx->runtime()->addTelemetry(id, args[1].toInt32()); - - args.rval().setUndefined(); - return true; -} - -static bool intrinsic_ConstructFunction(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -2140,7 +2123,7 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_INLINABLE_FN("std_Array_slice", array_slice, 2,0, ArraySlice), JS_FN("std_Array_sort", array_sort, 1,0), JS_FN("std_Array_reverse", array_reverse, 0,0), - JS_INLINABLE_FN("std_Array_splice", array_splice, 2,0, ArraySplice), + JS_FNINFO("std_Array_splice", array_splice, &array_splice_info, 2,0), JS_FN("std_Date_now", date_now, 0,0), JS_FN("std_Date_valueOf", date_valueOf, 0,0), @@ -2186,8 +2169,10 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_INLINABLE_FN("std_String_charAt", str_charAt, 1,0, StringCharAt), JS_FN("std_String_endsWith", str_endsWith, 1,0), JS_FN("std_String_trim", str_trim, 0,0), - JS_FN("std_String_trimLeft", str_trimLeft, 0,0), - JS_FN("std_String_trimRight", str_trimRight, 0,0), + JS_FN("std_String_trimLeft", str_trimStart, 0,0), + JS_FN("std_String_trimStart", str_trimStart, 0,0), + JS_FN("std_String_trimRight", str_trimEnd, 0,0), + JS_FN("std_String_trimEnd", str_trimEnd, 0,0), JS_FN("std_String_toLocaleLowerCase", str_toLocaleLowerCase, 0,0), JS_FN("std_String_toLocaleUpperCase", str_toLocaleUpperCase, 0,0), JS_FN("std_String_normalize", str_normalize, 0,0), @@ -2242,7 +2227,6 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0), JS_FN("_FinishBoundFunctionInit", intrinsic_FinishBoundFunctionInit, 3,0), JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0), - JS_FN("AddContentTelemetry", intrinsic_AddContentTelemetry, 2,0), JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing, 0,0, IntrinsicIsConstructing), @@ -2984,7 +2968,7 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name, MOZ_ASSERT(targetFun->isInterpretedLazy()); MOZ_ASSERT(targetFun->isSelfHostedBuiltin()); - RootedScript sourceScript(cx, sourceFun->getOrCreateScript(cx)); + RootedScript sourceScript(cx, JSFunction::getOrCreateScript(cx, sourceFun)); if (!sourceScript) return false; diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index 306a2c5400..8fe2145e58 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -460,15 +460,13 @@ NativeObject::getChildProperty(ExclusiveContext* cx, return shape; } -bool -js::NativeObject::toDictionaryMode(ExclusiveContext* cx) +/* static */ bool +js::NativeObject::toDictionaryMode(ExclusiveContext* cx, HandleNativeObject obj) { - MOZ_ASSERT(!inDictionaryMode()); - MOZ_ASSERT(cx->isInsideCurrentCompartment(this)); - - uint32_t span = slotSpan(); + MOZ_ASSERT(!obj->inDictionaryMode()); + MOZ_ASSERT(cx->isInsideCurrentCompartment(obj)); - Rooted<NativeObject*> self(cx, this); + uint32_t span = obj->slotSpan(); // Clone the shapes into a new dictionary list. Don't update the last // property of this object until done, otherwise a GC triggered while @@ -476,7 +474,7 @@ js::NativeObject::toDictionaryMode(ExclusiveContext* cx) RootedShape root(cx); RootedShape dictionaryShape(cx); - RootedShape shape(cx, lastProperty()); + RootedShape shape(cx, obj->lastProperty()); while (shape) { MOZ_ASSERT(!shape->inDictionary()); @@ -488,7 +486,7 @@ js::NativeObject::toDictionaryMode(ExclusiveContext* cx) GCPtrShape* listp = dictionaryShape ? &dictionaryShape->parent : nullptr; StackShape child(shape); - dprop->initDictionaryShape(child, self->numFixedSlots(), listp); + dprop->initDictionaryShape(child, obj->numFixedSlots(), listp); if (!dictionaryShape) root = dprop; @@ -503,18 +501,18 @@ js::NativeObject::toDictionaryMode(ExclusiveContext* cx) return false; } - if (IsInsideNursery(self) && - !cx->asJSContext()->gc.nursery.queueDictionaryModeObjectToSweep(self)) + if (IsInsideNursery(obj) && + !cx->asJSContext()->gc.nursery.queueDictionaryModeObjectToSweep(obj)) { ReportOutOfMemory(cx); return false; } MOZ_ASSERT(root->listp == nullptr); - root->listp = &self->shape_; - self->shape_ = root; + root->listp = &obj->shape_; + obj->shape_ = root; - MOZ_ASSERT(self->inDictionaryMode()); + MOZ_ASSERT(obj->inDictionaryMode()); root->base()->setSlotSpan(span); return true; @@ -534,7 +532,7 @@ NativeObject::addProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId return nullptr; if (!extensible) { if (cx->isJSContext()) - obj->reportNotExtensible(cx->asJSContext()); + JSObject::reportNotExtensible(cx->asJSContext(), obj); return nullptr; } @@ -592,7 +590,7 @@ NativeObject::addPropertyInternal(ExclusiveContext* cx, if (allowDictionary && (!stableSlot || ShouldConvertToDictionary(obj))) { - if (!obj->toDictionaryMode(cx)) + if (!toDictionaryMode(cx, obj)) return nullptr; table = obj->lastProperty()->maybeTable(keep); entry = &table->search<MaybeAdding::Adding>(id, keep); @@ -727,7 +725,7 @@ CheckCanChangeAttrs(ExclusiveContext* cx, JSObject* obj, Shape* shape, unsigned* (*attrsp & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED))) { if (cx->isJSContext()) - obj->reportNotConfigurable(cx->asJSContext(), shape->propid()); + JSObject::reportNotConfigurable(cx->asJSContext(), shape->propid()); return false; } @@ -785,7 +783,7 @@ NativeObject::putProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId if (!extensible) { if (cx->isJSContext()) - obj->reportNotExtensible(cx->asJSContext()); + JSObject::reportNotExtensible(cx->asJSContext(), obj); return nullptr; } @@ -834,7 +832,7 @@ NativeObject::putProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId * addPropertyInternal because a failure under add would lose data. */ if (shape != obj->lastProperty() && !obj->inDictionaryMode()) { - if (!obj->toDictionaryMode(cx)) + if (!toDictionaryMode(cx, obj)) return nullptr; ShapeTable* table = obj->lastProperty()->maybeTable(keep); MOZ_ASSERT(table); @@ -853,10 +851,11 @@ NativeObject::putProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId */ bool updateLast = (shape == obj->lastProperty()); bool accessorShape = getter || setter || (attrs & (JSPROP_GETTER | JSPROP_SETTER)); - shape = obj->replaceWithNewEquivalentShape(cx, shape, nullptr, accessorShape); + shape = NativeObject::replaceWithNewEquivalentShape(cx, obj, shape, nullptr, + accessorShape); if (!shape) return nullptr; - if (!updateLast && !obj->generateOwnShape(cx)) + if (!updateLast && !NativeObject::generateOwnShape(cx, obj)) return nullptr; /* @@ -968,16 +967,15 @@ NativeObject::changeProperty(ExclusiveContext* cx, HandleNativeObject obj, Handl return newShape; } -bool -NativeObject::removeProperty(ExclusiveContext* cx, jsid id_) +/* static */ bool +NativeObject::removeProperty(ExclusiveContext* cx, HandleNativeObject obj, jsid id_) { RootedId id(cx, id_); - RootedNativeObject self(cx, this); AutoKeepShapeTables keep(cx); ShapeTable::Entry* entry; RootedShape shape(cx); - if (!Shape::search(cx, lastProperty(), id, keep, shape.address(), &entry)) + if (!Shape::search(cx, obj->lastProperty(), id, keep, shape.address(), &entry)) return false; if (!shape) @@ -987,10 +985,10 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_) * If shape is not the last property added, or the last property cannot * be removed, switch to dictionary mode. */ - if (!self->inDictionaryMode() && (shape != self->lastProperty() || !self->canRemoveLastProperty())) { - if (!self->toDictionaryMode(cx)) + if (!obj->inDictionaryMode() && (shape != obj->lastProperty() || !obj->canRemoveLastProperty())) { + if (!toDictionaryMode(cx, obj)) return false; - ShapeTable* table = self->lastProperty()->maybeTable(keep); + ShapeTable* table = obj->lastProperty()->maybeTable(keep); MOZ_ASSERT(table); entry = &table->search<MaybeAdding::NotAdding>(shape->propid(), keep); shape = entry->shape(); @@ -1004,21 +1002,21 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_) * the object or table, so the remaining removal is infallible. */ RootedShape spare(cx); - if (self->inDictionaryMode()) { + if (obj->inDictionaryMode()) { /* For simplicity, always allocate an accessor shape for now. */ spare = Allocate<AccessorShape>(cx); if (!spare) return false; new (spare) Shape(shape->base()->unowned(), 0); - if (shape == self->lastProperty()) { + if (shape == obj->lastProperty()) { /* * Get an up to date unowned base shape for the new last property * when removing the dictionary's last property. Information in * base shapes for non-last properties may be out of sync with the * object's state. */ - RootedShape previous(cx, self->lastProperty()->parent); - StackBaseShape base(self->lastProperty()->base()); + RootedShape previous(cx, obj->lastProperty()->parent); + StackBaseShape base(obj->lastProperty()->base()); BaseShape* nbase = BaseShape::getUnowned(cx, base); if (!nbase) return false; @@ -1028,7 +1026,7 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_) /* If shape has a slot, free its slot number. */ if (shape->hasSlot()) { - self->freeSlot(cx, shape->slot()); + obj->freeSlot(cx, shape->slot()); if (cx->isJSContext()) ++cx->asJSContext()->runtime()->propertyRemovals; } @@ -1038,8 +1036,8 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_) * doubly linked list, hashed by lastProperty()->table. So we can edit the * list and hash in place. */ - if (self->inDictionaryMode()) { - ShapeTable* table = self->lastProperty()->maybeTable(keep); + if (obj->inDictionaryMode()) { + ShapeTable* table = obj->lastProperty()->maybeTable(keep); MOZ_ASSERT(table); if (entry->hadCollision()) { @@ -1056,23 +1054,23 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_) * checks not to alter significantly the complexity of the * delete in debug builds, see bug 534493. */ - Shape* aprop = self->lastProperty(); + Shape* aprop = obj->lastProperty(); for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent) - MOZ_ASSERT_IF(aprop != shape, self->contains(cx, aprop)); + MOZ_ASSERT_IF(aprop != shape, obj->contains(cx, aprop)); #endif } { /* Remove shape from its non-circular doubly linked list. */ - Shape* oldLastProp = self->lastProperty(); - shape->removeFromDictionary(self); + Shape* oldLastProp = obj->lastProperty(); + shape->removeFromDictionary(obj); /* Hand off table from the old to new last property. */ - oldLastProp->handoffTableTo(self->lastProperty()); + oldLastProp->handoffTableTo(obj->lastProperty()); } /* Generate a new shape for the object, infallibly. */ - JS_ALWAYS_TRUE(self->generateOwnShape(cx, spare)); + JS_ALWAYS_TRUE(NativeObject::generateOwnShape(cx, obj, spare)); /* Consider shrinking table if its load factor is <= .25. */ uint32_t size = table->capacity(); @@ -1085,11 +1083,11 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_) * lazily make via a later hashify the exact table for the new property * lineage. */ - MOZ_ASSERT(shape == self->lastProperty()); - self->removeLastProperty(cx); + MOZ_ASSERT(shape == obj->lastProperty()); + obj->removeLastProperty(cx); } - self->checkShapeConsistency(); + obj->checkShapeConsistency(); return true; } @@ -1133,35 +1131,30 @@ NativeObject::rollbackProperties(ExclusiveContext* cx, HandleNativeObject obj, u if (slot < slotSpan) break; } - if (!obj->removeProperty(cx, obj->lastProperty()->propid())) + if (!NativeObject::removeProperty(cx, obj, obj->lastProperty()->propid())) return false; } return true; } -Shape* -NativeObject::replaceWithNewEquivalentShape(ExclusiveContext* cx, Shape* oldShape, Shape* newShape, - bool accessorShape) +/* static */ Shape* +NativeObject::replaceWithNewEquivalentShape(ExclusiveContext* cx, HandleNativeObject obj, + Shape* oldShape, Shape* newShape, bool accessorShape) { MOZ_ASSERT(cx->isInsideCurrentZone(oldShape)); - MOZ_ASSERT_IF(oldShape != lastProperty(), - inDictionaryMode() && lookup(cx, oldShape->propidRef()) == oldShape); - - NativeObject* self = this; + MOZ_ASSERT_IF(oldShape != obj->lastProperty(), + obj->inDictionaryMode() && obj->lookup(cx, oldShape->propidRef()) == oldShape); - if (!inDictionaryMode()) { - RootedNativeObject selfRoot(cx, self); + if (!obj->inDictionaryMode()) { RootedShape newRoot(cx, newShape); - if (!toDictionaryMode(cx)) + if (!toDictionaryMode(cx, obj)) return nullptr; - oldShape = selfRoot->lastProperty(); - self = selfRoot; + oldShape = obj->lastProperty(); newShape = newRoot; } if (!newShape) { - RootedNativeObject selfRoot(cx, self); RootedShape oldRoot(cx, oldShape); newShape = (oldShape->isAccessorShape() || accessorShape) ? Allocate<AccessorShape>(cx) @@ -1169,12 +1162,11 @@ NativeObject::replaceWithNewEquivalentShape(ExclusiveContext* cx, Shape* oldShap if (!newShape) return nullptr; new (newShape) Shape(oldRoot->base()->unowned(), 0); - self = selfRoot; oldShape = oldRoot; } AutoCheckCannotGC nogc; - ShapeTable* table = self->lastProperty()->ensureTableForDictionary(cx, nogc); + ShapeTable* table = obj->lastProperty()->ensureTableForDictionary(cx, nogc); if (!table) return nullptr; @@ -1187,12 +1179,12 @@ NativeObject::replaceWithNewEquivalentShape(ExclusiveContext* cx, Shape* oldShap * enumeration order (see bug 601399). */ StackShape nshape(oldShape); - newShape->initDictionaryShape(nshape, self->numFixedSlots(), oldShape->listp); + newShape->initDictionaryShape(nshape, obj->numFixedSlots(), oldShape->listp); MOZ_ASSERT(newShape->parent == oldShape); - oldShape->removeFromDictionary(self); + oldShape->removeFromDictionary(obj); - if (newShape == self->lastProperty()) + if (newShape == obj->lastProperty()) oldShape->handoffTableTo(newShape); if (entry) @@ -1200,63 +1192,63 @@ NativeObject::replaceWithNewEquivalentShape(ExclusiveContext* cx, Shape* oldShap return newShape; } -bool -NativeObject::shadowingShapeChange(ExclusiveContext* cx, const Shape& shape) +/* static */ bool +NativeObject::shadowingShapeChange(ExclusiveContext* cx, HandleNativeObject obj, const Shape& shape) { - return generateOwnShape(cx); + return generateOwnShape(cx, obj); } -bool -JSObject::setFlags(ExclusiveContext* cx, BaseShape::Flag flags, GenerateShape generateShape) +/* static */ bool +JSObject::setFlags(ExclusiveContext* cx, HandleObject obj, BaseShape::Flag flags, + GenerateShape generateShape) { - if (hasAllFlags(flags)) + if (obj->hasAllFlags(flags)) return true; - RootedObject self(cx, this); - - Shape* existingShape = self->ensureShape(cx); + Shape* existingShape = obj->ensureShape(cx); if (!existingShape) return false; - if (isNative() && as<NativeObject>().inDictionaryMode()) { - if (generateShape == GENERATE_SHAPE && !as<NativeObject>().generateOwnShape(cx)) - return false; - StackBaseShape base(self->as<NativeObject>().lastProperty()); + if (obj->isNative() && obj->as<NativeObject>().inDictionaryMode()) { + if (generateShape == GENERATE_SHAPE) { + if (!NativeObject::generateOwnShape(cx, obj.as<NativeObject>())) + return false; + } + StackBaseShape base(obj->as<NativeObject>().lastProperty()); base.flags |= flags; UnownedBaseShape* nbase = BaseShape::getUnowned(cx, base); if (!nbase) return false; - self->as<NativeObject>().lastProperty()->base()->adoptUnowned(nbase); + obj->as<NativeObject>().lastProperty()->base()->adoptUnowned(nbase); return true; } - Shape* newShape = Shape::setObjectFlags(cx, flags, self->taggedProto(), existingShape); + Shape* newShape = Shape::setObjectFlags(cx, flags, obj->taggedProto(), existingShape); if (!newShape) return false; - // The success of the |JSObject::ensureShape| call above means that |self| + // The success of the |JSObject::ensureShape| call above means that |obj| // can be assumed to have a shape. - self->as<ShapedObject>().setShape(newShape); + obj->as<ShapedObject>().setShape(newShape); return true; } -bool -NativeObject::clearFlag(ExclusiveContext* cx, BaseShape::Flag flag) +/* static */ bool +NativeObject::clearFlag(ExclusiveContext* cx, HandleNativeObject obj, BaseShape::Flag flag) { - MOZ_ASSERT(inDictionaryMode()); + MOZ_ASSERT(obj->inDictionaryMode()); - RootedNativeObject self(cx, &as<NativeObject>()); - MOZ_ASSERT(self->lastProperty()->getObjectFlags() & flag); + MOZ_ASSERT(obj->lastProperty()->getObjectFlags() & flag); - StackBaseShape base(self->lastProperty()); + StackBaseShape base(obj->lastProperty()); base.flags &= ~flag; UnownedBaseShape* nbase = BaseShape::getUnowned(cx, base); if (!nbase) return false; - self->lastProperty()->base()->adoptUnowned(nbase); + obj->lastProperty()->base()->adoptUnowned(nbase); return true; } diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index 978798aaa6..85bc044a55 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -387,7 +387,7 @@ class BaseShape : public gc::TenuredCell INDEXED = 0x20, /* (0x40 is unused) */ HAD_ELEMENTS_ACCESS = 0x80, - WATCHED = 0x100, + /* (0x100 is unused) */ ITERATED_SINGLETON = 0x200, NEW_GROUP_UNKNOWN = 0x400, UNCACHEABLE_PROTO = 0x800, @@ -905,9 +905,6 @@ class Shape : public gc::TenuredCell setter() == rawSetter; } - bool set(JSContext* cx, HandleNativeObject obj, HandleObject receiver, MutableHandleValue vp, - ObjectOpResult& result); - BaseShape* base() const { return base_.get(); } bool hasSlot() const { diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp index c69306aaca..0dff412017 100644 --- a/js/src/vm/SharedArrayObject.cpp +++ b/js/src/vm/SharedArrayObject.cpp @@ -366,7 +366,8 @@ static const Class SharedArrayBufferObjectProtoClass = { static JSObject* CreateSharedArrayBufferPrototype(JSContext* cx, JSProtoKey key) { - return cx->global()->createBlankPrototype(cx, &SharedArrayBufferObjectProtoClass); + return GlobalObject::createBlankPrototype(cx, cx->global(), + &SharedArrayBufferObjectProtoClass); } static const ClassOps SharedArrayBufferObjectClassOps = { diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index a51c0aa146..11a19d1751 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -306,7 +306,7 @@ InterpreterStack::pushInlineFrame(JSContext* cx, InterpreterRegs& regs, const Ca MOZ_ASSERT(regs.sp == args.end()); MOZ_ASSERT(callee->nonLazyScript() == script); - script->ensureNonLazyCanonicalFunction(cx); + script->ensureNonLazyCanonicalFunction(); InterpreterFrame* prev = regs.fp(); jsbytecode* prevpc = regs.pc; @@ -336,13 +336,13 @@ InterpreterStack::resumeGeneratorCallFrame(JSContext* cx, InterpreterRegs& regs, HandleObject envChain) { MOZ_ASSERT(callee->isGenerator()); - RootedScript script(cx, callee->getOrCreateScript(cx)); + RootedScript script(cx, JSFunction::getOrCreateScript(cx, callee)); InterpreterFrame* prev = regs.fp(); jsbytecode* prevpc = regs.pc; Value* prevsp = regs.sp; MOZ_ASSERT(prev); - script->ensureNonLazyCanonicalFunction(cx); + script->ensureNonLazyCanonicalFunction(); LifoAlloc::Mark mark = allocator_.mark(); diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 87e95c8936..c5f2cf5f34 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -82,7 +82,7 @@ InterpreterFrame::isNonGlobalEvalFrame() const return isEvalFrame() && script()->bodyScope()->as<EvalScope>().isNonGlobal(); } -JSObject* +ArrayObject* InterpreterFrame::createRestParameter(JSContext* cx) { MOZ_ASSERT(script()->hasRest()); diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 552738d898..23e6213444 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -523,7 +523,7 @@ class InterpreterFrame ArgumentsObject& argsObj() const; void initArgsObj(ArgumentsObject& argsobj); - JSObject* createRestParameter(JSContext* cx); + ArrayObject* createRestParameter(JSContext* cx); /* * Environment chain @@ -1006,6 +1006,17 @@ class InvokeArgs : public detail::GenericArgsBase<NO_CONSTRUCT> explicit InvokeArgs(JSContext* cx) : Base(cx) {} }; +/** Function call args of statically-unknown count. */ +class InvokeArgsMaybeIgnoresReturnValue : public detail::GenericArgsBase<NO_CONSTRUCT> +{ + using Base = detail::GenericArgsBase<NO_CONSTRUCT>; + + public: + explicit InvokeArgsMaybeIgnoresReturnValue(JSContext* cx, bool ignoresReturnValue) : Base(cx) { + this->ignoresReturnValue_ = ignoresReturnValue; + } +}; + /** Function call args of statically-known count. */ template <size_t N> class FixedInvokeArgs : public detail::FixedArgsBase<NO_CONSTRUCT, N> diff --git a/js/src/vm/Stopwatch.cpp b/js/src/vm/Stopwatch.cpp index 28632c2a15..684846f000 100644 --- a/js/src/vm/Stopwatch.cpp +++ b/js/src/vm/Stopwatch.cpp @@ -20,6 +20,7 @@ #include "gc/Zone.h" #include "vm/Runtime.h" + namespace js { bool @@ -136,6 +137,9 @@ PerformanceMonitoring::start() bool PerformanceMonitoring::commit() { + // Maximal initialization size, in elements for the vector of groups. + static const size_t MAX_GROUPS_INIT_CAPACITY = 1024; + #if !defined(MOZ_HAVE_RDTSC) // The AutoStopwatch is only executed if `MOZ_HAVE_RDTSC`. return false; @@ -152,13 +156,24 @@ PerformanceMonitoring::commit() return true; } - PerformanceGroupVector recentGroups; - recentGroups_.swap(recentGroups); + // The move operation is generally constant time, unless + // `recentGroups_.length()` is very small, in which case + // it's fast just because it's small. + PerformanceGroupVector recentGroups(Move(recentGroups_)); + recentGroups_ = PerformanceGroupVector(); // Reconstruct after `Move`. bool success = true; if (stopwatchCommitCallback) success = stopwatchCommitCallback(iteration_, recentGroups, stopwatchCommitClosure); + // Heuristic: we expect to have roughly the same number of groups as in + // the previous iteration. + const size_t capacity = recentGroups.capacity() < MAX_GROUPS_INIT_CAPACITY ? + recentGroups.capacity() : + MAX_GROUPS_INIT_CAPACITY; + success = recentGroups_.reserve(capacity) + && success; + // Reset immediately, to make sure that we're not hit by the end // of a nested event loop (which would cause `commit` to be called // twice in succession). @@ -227,7 +242,7 @@ AutoStopwatch::AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IM MOZ_GUARD_OBJECT_NOTIFIER_INIT; JSCompartment* compartment = cx_->compartment(); - if (compartment->scheduledForDestruction) + if (MOZ_UNLIKELY(compartment->scheduledForDestruction)) return; JSRuntime* runtime = cx_->runtime(); @@ -266,11 +281,11 @@ AutoStopwatch::~AutoStopwatch() } JSCompartment* compartment = cx_->compartment(); - if (compartment->scheduledForDestruction) + if (MOZ_UNLIKELY(compartment->scheduledForDestruction)) return; JSRuntime* runtime = cx_->runtime(); - if (iteration_ != runtime->performanceMonitoring.iteration()) { + if (MOZ_UNLIKELY(iteration_ != runtime->performanceMonitoring.iteration())) { // We have entered a nested event loop at some point. // Any information we may have is obsolete. return; @@ -319,11 +334,6 @@ AutoStopwatch::exit() const uint64_t cyclesEnd = getCycles(runtime); cyclesDelta = cyclesEnd - cyclesStart_; // Always >= 0 by definition of `getCycles`. } -#if WINVER >= 0x600 - updateTelemetry(cpuStart_, cpuEnd); -#elif defined(__linux__) - updateTelemetry(cpuStart_, cpuEnd); -#endif // WINVER >= 0x600 || _linux__ } uint64_t CPOWTimeDelta = 0; @@ -335,17 +345,6 @@ AutoStopwatch::exit() return addToGroups(cyclesDelta, CPOWTimeDelta); } -void -AutoStopwatch::updateTelemetry(const cpuid_t& cpuStart_, const cpuid_t& cpuEnd) -{ - JSRuntime* runtime = cx_->runtime(); - - if (isSameCPU(cpuStart_, cpuEnd)) - runtime->performanceMonitoring.testCpuRescheduling.stayed += 1; - else - runtime->performanceMonitoring.testCpuRescheduling.moved += 1; -} - PerformanceGroup* AutoStopwatch::acquireGroup(PerformanceGroup* group) { @@ -638,13 +637,6 @@ GetStopwatchIsMonitoringCPOW(JSContext* cx) } JS_PUBLIC_API(void) -GetPerfMonitoringTestCpuRescheduling(JSContext* cx, uint64_t* stayed, uint64_t* moved) -{ - *stayed = cx->performanceMonitoring.testCpuRescheduling.stayed; - *moved = cx->performanceMonitoring.testCpuRescheduling.moved; -} - -JS_PUBLIC_API(void) AddCPOWPerformanceDelta(JSContext* cx, uint64_t delta) { cx->performanceMonitoring.totalCPOWTime += delta; diff --git a/js/src/vm/Stopwatch.h b/js/src/vm/Stopwatch.h index 38a3eb801c..d7f299594b 100644 --- a/js/src/vm/Stopwatch.h +++ b/js/src/vm/Stopwatch.h @@ -217,33 +217,6 @@ struct PerformanceMonitoring { */ uint64_t monotonicReadTimestampCounter(); - /** - * Data extracted by the AutoStopwatch to determine how often - * we reschedule the process to a different CPU during the - * execution of JS. - * - * Warning: These values are incremented *only* on platforms - * that offer a syscall/libcall to check on which CPU a - * process is currently executed. - */ - struct TestCpuRescheduling - { - // Incremented once we have finished executing code - // in a group, if the CPU on which we started - // execution is the same as the CPU on which - // we finished. - uint64_t stayed; - // Incremented once we have finished executing code - // in a group, if the CPU on which we started - // execution is different from the CPU on which - // we finished. - uint64_t moved; - TestCpuRescheduling() - : stayed(0), - moved(0) - { } - }; - TestCpuRescheduling testCpuRescheduling; private: PerformanceMonitoring(const PerformanceMonitoring&) = delete; PerformanceMonitoring& operator=(const PerformanceMonitoring&) = delete; @@ -375,9 +348,6 @@ class AutoStopwatch final { // Add recent changes to a single group. Mark the group as changed recently. bool addToGroup(JSRuntime* runtime, uint64_t cyclesDelta, uint64_t CPOWTimeDelta, PerformanceGroup* group); - // Update telemetry statistics. - void updateTelemetry(const cpuid_t& a, const cpuid_t& b); - // Perform a subtraction for a quantity that should be monotonic // but is not guaranteed to be so. // diff --git a/js/src/vm/String.h b/js/src/vm/String.h index 1a0c58575d..514e2c2050 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -8,7 +8,6 @@ #define vm_String_h #include "mozilla/MemoryReporting.h" -#include "mozilla/PodOperations.h" #include "mozilla/Range.h" #include "jsapi.h" @@ -1087,19 +1086,17 @@ class StaticStrings static const size_t SMALL_CHAR_LIMIT = 128U; static const size_t NUM_SMALL_CHARS = 64U; - JSAtom* length2StaticTable[NUM_SMALL_CHARS * NUM_SMALL_CHARS]; + JSAtom* length2StaticTable[NUM_SMALL_CHARS * NUM_SMALL_CHARS] = {}; // zeroes public: /* We keep these public for the JITs. */ static const size_t UNIT_STATIC_LIMIT = 256U; - JSAtom* unitStaticTable[UNIT_STATIC_LIMIT]; + JSAtom* unitStaticTable[UNIT_STATIC_LIMIT] = {}; // zeroes static const size_t INT_STATIC_LIMIT = 256U; - JSAtom* intStaticTable[INT_STATIC_LIMIT]; + JSAtom* intStaticTable[INT_STATIC_LIMIT] = {}; // zeroes - StaticStrings() { - mozilla::PodZero(this); - } + StaticStrings() = default; bool init(JSContext* cx); void trace(JSTracer* trc); diff --git a/js/src/vm/StringObject-inl.h b/js/src/vm/StringObject-inl.h index 5fc1656f69..38191fc7a6 100644 --- a/js/src/vm/StringObject-inl.h +++ b/js/src/vm/StringObject-inl.h @@ -15,31 +15,29 @@ namespace js { -inline bool -StringObject::init(JSContext* cx, HandleString str) +/* static */ inline bool +StringObject::init(JSContext* cx, Handle<StringObject*> obj, HandleString str) { - MOZ_ASSERT(numFixedSlots() == 2); + MOZ_ASSERT(obj->numFixedSlots() == 2); - Rooted<StringObject*> self(cx, this); - - if (!EmptyShape::ensureInitialCustomShape<StringObject>(cx, self)) + if (!EmptyShape::ensureInitialCustomShape<StringObject>(cx, obj)) return false; - MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().length))->slot() == LENGTH_SLOT); + MOZ_ASSERT(obj->lookup(cx, NameToId(cx->names().length))->slot() == LENGTH_SLOT); - self->setStringThis(str); + obj->setStringThis(str); return true; } -inline StringObject* +/* static */ inline StringObject* StringObject::create(JSContext* cx, HandleString str, HandleObject proto, NewObjectKind newKind) { JSObject* obj = NewObjectWithClassProto(cx, &class_, proto, newKind); if (!obj) return nullptr; Rooted<StringObject*> strobj(cx, &obj->as<StringObject>()); - if (!strobj->init(cx, str)) + if (!StringObject::init(cx, strobj, str)) return nullptr; return strobj; } diff --git a/js/src/vm/StringObject.h b/js/src/vm/StringObject.h index 119e3d9fa6..561e0478a8 100644 --- a/js/src/vm/StringObject.h +++ b/js/src/vm/StringObject.h @@ -56,7 +56,7 @@ class StringObject : public NativeObject } private: - inline bool init(JSContext* cx, HandleString str); + static inline bool init(JSContext* cx, Handle<StringObject*> obj, HandleString str); void setStringThis(JSString* str) { MOZ_ASSERT(getReservedSlot(PRIMITIVE_VALUE_SLOT).isUndefined()); diff --git a/js/src/vm/Time.cpp b/js/src/vm/Time.cpp index 69e2cc41d2..a9a5b7f0fc 100644 --- a/js/src/vm/Time.cpp +++ b/js/src/vm/Time.cpp @@ -11,9 +11,10 @@ #include "mozilla/DebugOnly.h" #include "mozilla/MathAlgorithms.h" -#ifdef SOLARIS +#ifdef XP_SOLARIS #define _REENTRANT 1 #endif + #include <string.h> #include <time.h> @@ -33,7 +34,7 @@ #ifdef XP_UNIX -#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */ +#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */ extern int gettimeofday(struct timeval* tv); #endif @@ -49,11 +50,11 @@ PRMJ_Now() { struct timeval tv; -#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */ +#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */ gettimeofday(&tv); #else gettimeofday(&tv, 0); -#endif /* _SVID_GETTOD */ +#endif /* _SVID_GETTOD */ return int64_t(tv.tv_sec) * PRMJ_USEC_PER_SEC + int64_t(tv.tv_usec); } diff --git a/js/src/vm/TypeInference-inl.h b/js/src/vm/TypeInference-inl.h index da47fa898b..2af252cea1 100644 --- a/js/src/vm/TypeInference-inl.h +++ b/js/src/vm/TypeInference-inl.h @@ -23,7 +23,6 @@ #include "vm/SharedArrayObject.h" #include "vm/StringObject.h" #include "vm/TypedArrayObject.h" -#include "vm/UnboxedObject.h" #include "jscntxtinlines.h" @@ -285,10 +284,6 @@ TypeIdString(jsid id) */ struct AutoEnterAnalysis { - // For use when initializing an UnboxedLayout. The UniquePtr's destructor - // must run when GC is not suppressed. - UniquePtr<UnboxedLayout> unboxedLayoutToCleanUp; - // Prevent GC activity in the middle of analysis. gc::AutoSuppressGC suppressGC; diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index 3d09c74641..2b1fa0e3bb 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -12,6 +12,8 @@ #include "mozilla/SizePrintfMacros.h" #include "mozilla/Sprintf.h" +#include <new> + #include "jsapi.h" #include "jscntxt.h" #include "jsgc.h" @@ -33,7 +35,6 @@ #include "vm/Opcodes.h" #include "vm/Shape.h" #include "vm/Time.h" -#include "vm/UnboxedObject.h" #include "jsatominlines.h" #include "jsscriptinlines.h" @@ -295,9 +296,6 @@ js::ObjectGroupHasProperty(JSContext* cx, ObjectGroup* group, jsid id, const Val return true; } } - JSObject* obj = &value.toObject(); - if (!obj->hasLazyGroup() && obj->group()->maybeOriginalUnboxedGroup()) - return true; } if (!types->hasType(type)) { @@ -859,10 +857,8 @@ TypeSet::IsTypeAboutToBeFinalized(TypeSet::Type* v) } bool -TypeSet::clone(LifoAlloc* alloc, TemporaryTypeSet* result) const +TypeSet::cloneIntoUninitialized(LifoAlloc* alloc, TemporaryTypeSet* result) const { - MOZ_ASSERT(result->empty()); - unsigned objectCount = baseObjectCount(); unsigned capacity = (objectCount >= 2) ? TypeHashSet::Capacity(objectCount) : 0; @@ -874,15 +870,15 @@ TypeSet::clone(LifoAlloc* alloc, TemporaryTypeSet* result) const PodCopy(newSet, objectSet, capacity); } - new(result) TemporaryTypeSet(flags, capacity ? newSet : objectSet); + new (result) TemporaryTypeSet(flags, capacity ? newSet : objectSet); return true; } TemporaryTypeSet* TypeSet::clone(LifoAlloc* alloc) const { - TemporaryTypeSet* res = alloc->new_<TemporaryTypeSet>(); - if (!res || !clone(alloc, res)) + TemporaryTypeSet* res = alloc->pod_malloc<TemporaryTypeSet>(); + if (!res || !cloneIntoUninitialized(alloc, res)) return nullptr; return res; } @@ -1150,10 +1146,9 @@ TypeScript::FreezeTypeSets(CompilerConstraintList* constraints, JSScript* script TemporaryTypeSet* types = alloc->newArrayUninitialized<TemporaryTypeSet>(count); if (!types) return false; - PodZero(types, count); for (size_t i = 0; i < count; i++) { - if (!existing[i].clone(alloc, &types[i])) + if (!existing[i].cloneIntoUninitialized(alloc, &types[i])) return false; } @@ -1324,7 +1319,8 @@ js::EnsureTrackPropertyTypes(JSContext* cx, JSObject* obj, jsid id) AutoEnterAnalysis enter(cx); if (obj->hasLazyGroup()) { AutoEnterOOMUnsafeRegion oomUnsafe; - if (!obj->getGroup(cx)) { + RootedObject objRoot(cx, obj); + if (!JSObject::getGroup(cx, objRoot)) { oomUnsafe.crash("Could not allocate ObjectGroup in EnsureTrackPropertyTypes"); return; } @@ -1343,9 +1339,12 @@ HeapTypeSetKey::instantiate(JSContext* cx) { if (maybeTypes()) return true; - if (object()->isSingleton() && !object()->singleton()->getGroup(cx)) { - cx->clearPendingException(); - return false; + if (object()->isSingleton()) { + RootedObject obj(cx, object()->singleton()); + if (!JSObject::getGroup(cx, obj)) { + cx->clearPendingException(); + return false; + } } JSObject* obj = object()->isSingleton() ? object()->singleton() : nullptr; maybeTypes_ = object()->maybeGroup()->getProperty(cx, obj, id()); @@ -1945,33 +1944,6 @@ class ConstraintDataFreezeObjectForTypedArrayData } }; -// Constraint which triggers recompilation if an unboxed object in some group -// is converted to a native object. -class ConstraintDataFreezeObjectForUnboxedConvertedToNative -{ - public: - ConstraintDataFreezeObjectForUnboxedConvertedToNative() - {} - - const char* kind() { return "freezeObjectForUnboxedConvertedToNative"; } - - bool invalidateOnNewType(TypeSet::Type type) { return false; } - bool invalidateOnNewPropertyState(TypeSet* property) { return false; } - bool invalidateOnNewObjectState(ObjectGroup* group) { - return group->unboxedLayout().nativeGroup() != nullptr; - } - - bool constraintHolds(JSContext* cx, - const HeapTypeSetKey& property, TemporaryTypeSet* expected) - { - return !invalidateOnNewObjectState(property.object()->maybeGroup()); - } - - bool shouldSweep() { return false; } - - JSCompartment* maybeCompartment() { return nullptr; } -}; - } /* anonymous namespace */ void @@ -1996,17 +1968,6 @@ TypeSet::ObjectKey::watchStateChangeForTypedArrayData(CompilerConstraintList* co ConstraintDataFreezeObjectForTypedArrayData(tarray))); } -void -TypeSet::ObjectKey::watchStateChangeForUnboxedConvertedToNative(CompilerConstraintList* constraints) -{ - HeapTypeSetKey objectProperty = property(JSID_EMPTY); - LifoAlloc* alloc = constraints->alloc(); - - typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForUnboxedConvertedToNative> T; - constraints->add(alloc->new_<T>(alloc, objectProperty, - ConstraintDataFreezeObjectForUnboxedConvertedToNative())); -} - static void ObjectStateChange(ExclusiveContext* cxArg, ObjectGroup* group, bool markingUnknown) { @@ -2517,8 +2478,6 @@ TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList* constraints, jsid bool js::ClassCanHaveExtraProperties(const Class* clasp) { - if (clasp == &UnboxedPlainObject::class_ || clasp == &UnboxedArrayObject::class_) - return false; return clasp->getResolve() || clasp->getOpsLookupProperty() || clasp->getOpsGetProperty() @@ -2711,14 +2670,6 @@ ObjectGroup::updateNewPropertyTypes(ExclusiveContext* cx, JSObject* objArg, jsid if (shape) UpdatePropertyType(cx, types, obj, shape, false); } - - if (obj->watched()) { - /* - * Mark the property as non-data, to inhibit optimizations on it - * and avoid bypassing the watchpoint handler. - */ - types->setNonDataProperty(cx); - } } void @@ -2817,15 +2768,6 @@ js::AddTypePropertyId(ExclusiveContext* cx, ObjectGroup* group, JSObject* obj, j // from acquiring the fully initialized group. if (group->newScript() && group->newScript()->initializedGroup()) AddTypePropertyId(cx, group->newScript()->initializedGroup(), nullptr, id, type); - - // Maintain equivalent type information for unboxed object groups and their - // corresponding native group. Since type sets might contain the unboxed - // group but not the native group, this ensures optimizations based on the - // unboxed group are valid for the native group. - if (group->maybeUnboxedLayout() && group->maybeUnboxedLayout()->nativeGroup()) - AddTypePropertyId(cx, group->maybeUnboxedLayout()->nativeGroup(), nullptr, id, type); - if (ObjectGroup* unboxedGroup = group->maybeOriginalUnboxedGroup()) - AddTypePropertyId(cx, unboxedGroup, nullptr, id, type); } void @@ -2897,12 +2839,6 @@ ObjectGroup::setFlags(ExclusiveContext* cx, ObjectGroupFlags flags) // acquired properties analysis. if (newScript() && newScript()->initializedGroup()) newScript()->initializedGroup()->setFlags(cx, flags); - - // Propagate flag changes between unboxed and corresponding native groups. - if (maybeUnboxedLayout() && maybeUnboxedLayout()->nativeGroup()) - maybeUnboxedLayout()->nativeGroup()->setFlags(cx, flags); - if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup()) - unboxedGroup->setFlags(cx, flags); } void @@ -2935,23 +2871,6 @@ ObjectGroup::markUnknown(ExclusiveContext* cx) prop->types.setNonDataProperty(cx); } } - - if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup()) - MarkObjectGroupUnknownProperties(cx, unboxedGroup); - if (maybeUnboxedLayout() && maybeUnboxedLayout()->nativeGroup()) - MarkObjectGroupUnknownProperties(cx, maybeUnboxedLayout()->nativeGroup()); - if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup()) - MarkObjectGroupUnknownProperties(cx, unboxedGroup); -} - -TypeNewScript* -ObjectGroup::anyNewScript() -{ - if (newScript()) - return newScript(); - if (maybeUnboxedLayout()) - return unboxedLayout().newScript(); - return nullptr; } void @@ -2961,7 +2880,7 @@ ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup* replacement) // analyzed, remove it from the newObjectGroups table so that it will not be // produced by calling 'new' on the associated function anymore. // The TypeNewScript is not actually destroyed. - TypeNewScript* newScript = anyNewScript(); + TypeNewScript* newScript = this->newScript(); MOZ_ASSERT(newScript); if (newScript->analyzed()) { @@ -2980,10 +2899,7 @@ ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup* replacement) MOZ_ASSERT(!replacement); } - if (this->newScript()) - setAddendum(Addendum_None, nullptr, writeBarrier); - else - unboxedLayout().setNewScript(nullptr, writeBarrier); + setAddendum(Addendum_None, nullptr, writeBarrier); } void @@ -2994,7 +2910,7 @@ ObjectGroup::maybeClearNewScriptOnOOM() if (!isMarked()) return; - TypeNewScript* newScript = anyNewScript(); + TypeNewScript* newScript = this->newScript(); if (!newScript) return; @@ -3009,7 +2925,7 @@ ObjectGroup::maybeClearNewScriptOnOOM() void ObjectGroup::clearNewScript(ExclusiveContext* cx, ObjectGroup* replacement /* = nullptr*/) { - TypeNewScript* newScript = anyNewScript(); + TypeNewScript* newScript = this->newScript(); if (!newScript) return; @@ -3021,7 +2937,8 @@ ObjectGroup::clearNewScript(ExclusiveContext* cx, ObjectGroup* replacement /* = // Mark the constructing function as having its 'new' script cleared, so we // will not try to construct another one later. - if (!newScript->function()->setNewScriptCleared(cx)) + RootedFunction fun(cx, newScript->function()); + if (!JSObject::setNewScriptCleared(cx, fun)) cx->recoverFromOutOfMemory(); } @@ -3159,29 +3076,39 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint }; bool -js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* group, HandleId id) +js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, + DPAConstraintInfo& constraintInfo, + ObjectGroup* group, + HandleId id, + bool* added) { /* * Ensure that if the properties named here could have a getter, setter or * a permanent property in any transitive prototype, the definite * properties get cleared from the group. */ + + *added = false; + RootedObject proto(cx, group->proto().toObjectOrNull()); while (proto) { - ObjectGroup* protoGroup = proto->getGroup(cx); + ObjectGroup* protoGroup = JSObject::getGroup(cx, proto); if (!protoGroup) { - cx->recoverFromOutOfMemory(); return false; } if (protoGroup->unknownProperties()) - return false; + return true; HeapTypeSet* protoTypes = protoGroup->getProperty(cx, proto, id); - if (!protoTypes || protoTypes->nonDataProperty() || protoTypes->nonWritableProperty()) + if (!protoTypes) return false; - if (!protoTypes->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(group))) + if (protoTypes->nonDataProperty() || protoTypes->nonWritableProperty()) + return true; + if (!constraintInfo.addProtoConstraint(proto, id)) return false; proto = proto->staticPrototype(); } + + *added = true; return true; } @@ -3405,7 +3332,7 @@ JSFunction::setTypeForScriptedFunction(ExclusiveContext* cx, HandleFunction fun, ///////////////////////////////////////////////////////////////////// void -PreliminaryObjectArray::registerNewObject(JSObject* res) +PreliminaryObjectArray::registerNewObject(PlainObject* res) { // The preliminary object pointers are weak, and won't be swept properly // during nursery collections, so the preliminary objects need to be @@ -3423,7 +3350,7 @@ PreliminaryObjectArray::registerNewObject(JSObject* res) } void -PreliminaryObjectArray::unregisterObject(JSObject* obj) +PreliminaryObjectArray::unregisterObject(PlainObject* obj) { for (size_t i = 0; i < COUNT; i++) { if (objects[i] == obj) { @@ -3463,22 +3390,6 @@ PreliminaryObjectArray::sweep() for (size_t i = 0; i < COUNT; i++) { JSObject** ptr = &objects[i]; if (*ptr && IsAboutToBeFinalizedUnbarriered(ptr)) { - // Before we clear this reference, change the object's group to the - // Object.prototype group. This is done to ensure JSObject::finalize - // sees a NativeObject Class even if we change the current group's - // Class to one of the unboxed object classes in the meantime. If - // the compartment's global is dead, we don't do anything as the - // group's Class is not going to change in that case. - JSObject* obj = *ptr; - GlobalObject* global = obj->compartment()->unsafeUnbarrieredMaybeGlobal(); - if (global && !obj->isSingleton()) { - JSObject* objectProto = GetBuiltinPrototypePure(global, JSProto_Object); - obj->setGroup(objectProto->groupRaw()); - MOZ_ASSERT(obj->is<NativeObject>()); - MOZ_ASSERT(obj->getClass() == objectProto->getClass()); - MOZ_ASSERT(!obj->getClass()->hasFinalize()); - } - *ptr = nullptr; } } @@ -3578,17 +3489,11 @@ PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext* cx, ObjectGro } } - TryConvertToUnboxedLayout(cx, enter, shape(), group, preliminaryObjects); - if (group->maybeUnboxedLayout()) - return; - - if (shape()) { - // We weren't able to use an unboxed layout, but since the preliminary - // objects still reflect the template object's properties, and all - // objects in the future will be created with those properties, the - // properties can be marked as definite for objects in the group. - group->addDefiniteProperties(cx, shape()); - } + // Since the preliminary objects still reflect the template object's + // properties, and all objects in the future will be created with those + // properties, the properties can be marked as definitive for objects in + // the group. + group->addDefiniteProperties(cx, shape()); } ///////////////////////////////////////////////////////////////////// @@ -3602,7 +3507,10 @@ TypeNewScript::make(JSContext* cx, ObjectGroup* group, JSFunction* fun) { MOZ_ASSERT(cx->zone()->types.activeAnalysis); MOZ_ASSERT(!group->newScript()); - MOZ_ASSERT(!group->maybeUnboxedLayout()); + + // rollbackPartiallyInitializedObjects expects function_ to be + // canonicalized. + MOZ_ASSERT(fun->maybeCanonicalFunction() == fun); if (group->unknownProperties()) return true; @@ -3706,6 +3614,43 @@ struct DestroyTypeNewScript } // namespace +bool DPAConstraintInfo::finishConstraints(JSContext* cx, ObjectGroup* group) { + for (const ProtoConstraint& constraint : protoConstraints_) { + ObjectGroup* protoGroup = constraint.proto->group(); + + // Note: we rely on the group's type information being unchanged since + // AddClearDefiniteGetterSetterForPrototypeChain. + + bool unknownProperties = protoGroup->unknownProperties(); + MOZ_RELEASE_ASSERT(!unknownProperties); + + HeapTypeSet* protoTypes = + protoGroup->getProperty(cx, constraint.proto, constraint.id); + MOZ_RELEASE_ASSERT(protoTypes); + + MOZ_ASSERT(!protoTypes->nonDataProperty()); + MOZ_ASSERT(!protoTypes->nonWritableProperty()); + + if (!protoTypes->addConstraint( + cx, + cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>( + group))) { + ReportOutOfMemory(cx); + return false; + } + } + + for (const InliningConstraint& constraint : inliningConstraints_) { + if (!AddClearDefiniteFunctionUsesInScript(cx, group, constraint.caller, + constraint.callee)) { + ReportOutOfMemory(cx); + return false; + } + } + + return true; +} + bool TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, bool force) { @@ -3809,9 +3754,17 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, return false; Vector<Initializer> initializerVector(cx); + + DPAConstraintInfo constraintInfo(cx); RootedPlainObject templateRoot(cx, templateObject()); - if (!jit::AnalyzeNewScriptDefiniteProperties(cx, function(), group, templateRoot, &initializerVector)) + RootedFunction fun(cx, function()); + if (!jit::AnalyzeNewScriptDefiniteProperties(cx, + constraintInfo, + fun, + group, + templateRoot, + &initializerVector)) return false; if (!group->newScript()) @@ -3858,38 +3811,21 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, PodCopy(initializerList, initializerVector.begin(), initializerVector.length()); } - // Try to use an unboxed representation for the group. - if (!TryConvertToUnboxedLayout(cx, enter, templateObject()->lastProperty(), group, preliminaryObjects)) - return false; - js_delete(preliminaryObjects); preliminaryObjects = nullptr; - if (group->maybeUnboxedLayout()) { - // An unboxed layout was constructed for the group, and this has already - // been hooked into it. - MOZ_ASSERT(group->unboxedLayout().newScript() == this); - destroyNewScript.group = nullptr; - - // Clear out the template object, which is not used for TypeNewScripts - // with an unboxed layout. Currently it is a mutant object with a - // non-native group and native shape, so make it safe for GC by changing - // its group to the default for its prototype. - AutoEnterOOMUnsafeRegion oomUnsafe; - ObjectGroup* plainGroup = ObjectGroup::defaultNewGroup(cx, &PlainObject::class_, - group->proto()); - if (!plainGroup) - oomUnsafe.crash("TypeNewScript::maybeAnalyze"); - templateObject_->setGroup(plainGroup); - templateObject_ = nullptr; - - return true; - } - if (prefixShape->slotSpan() == templateObject()->slotSpan()) { // The definite properties analysis found exactly the properties that // are held in common by the preliminary objects. No further analysis // is needed. + + if (!constraintInfo.finishConstraints(cx, group)) { + return false; + } + if (!group->newScript()) { + return true; + } + group->addDefiniteProperties(cx, templateObject()->lastProperty()); destroyNewScript.group = nullptr; @@ -3911,6 +3847,16 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, if (!initialGroup) return false; + // Add the constraints. Use the initialGroup as group referenced by the + // constraints because that's the group that will have the TypeNewScript + // associated with it. See the detachNewScript and setNewScript calls below. + if (!constraintInfo.finishConstraints(cx, initialGroup)) { + return false; + } + if (!group->newScript()) { + return true; + } + initialGroup->addDefiniteProperties(cx, templateObject()->lastProperty()); group->addDefiniteProperties(cx, prefixShape); @@ -3959,8 +3905,15 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* g oomUnsafe.crash("rollbackPartiallyInitializedObjects"); } - if (!iter.isConstructing() || !iter.matchCallee(cx, function)) + if (!iter.isConstructing()) { continue; + } + + MOZ_ASSERT(iter.calleeTemplate()->maybeCanonicalFunction()); + + if (iter.calleeTemplate()->maybeCanonicalFunction() != function) { + continue; + } // Derived class constructors initialize their this-binding later and // we shouldn't run the definite properties analysis on them. @@ -3974,12 +3927,6 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* g continue; } - if (thisv.toObject().is<UnboxedPlainObject>()) { - AutoEnterOOMUnsafeRegion oomUnsafe; - if (!UnboxedPlainObject::convertToNative(cx, &thisv.toObject())) - oomUnsafe.crash("rollbackPartiallyInitializedObjects"); - } - // Found a matching frame. RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>()); @@ -4173,12 +4120,6 @@ ConstraintTypeSet::sweep(Zone* zone, AutoClearTypeInferenceStateOnOOM& oom) // Object sets containing objects with unknown properties might // not be complete. Mark the type set as unknown, which it will // be treated as during Ion compilation. - // - // Note that we don't have to do this when the type set might - // be missing the native group corresponding to an unboxed - // object group. In this case, the native group points to the - // unboxed object group via its addendum, so as long as objects - // with either group exist, neither group will be finalized. flags |= TYPE_FLAG_ANYOBJECT; clearObjects(); objectCount = 0; @@ -4262,21 +4203,6 @@ ObjectGroup::sweep(AutoClearTypeInferenceStateOnOOM* oom) Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM; EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM); - if (maybeUnboxedLayout()) { - // Remove unboxed layouts that are about to be finalized from the - // compartment wide list while we are still on the main thread. - ObjectGroup* group = this; - if (IsAboutToBeFinalizedUnbarriered(&group)) - unboxedLayout().detachFromCompartment(); - - if (unboxedLayout().newScript()) - unboxedLayout().newScript()->sweep(); - - // Discard constructor code to avoid holding onto ExecutablePools. - if (zone()->isGCCompacting()) - unboxedLayout().setConstructorCode(nullptr); - } - if (maybePreliminaryObjects()) maybePreliminaryObjects()->sweep(); diff --git a/js/src/vm/TypeInference.h b/js/src/vm/TypeInference.h index 9ba1c3cc82..fd021fc96c 100644 --- a/js/src/vm/TypeInference.h +++ b/js/src/vm/TypeInference.h @@ -262,7 +262,6 @@ class TypeSet bool hasStableClassAndProto(CompilerConstraintList* constraints); void watchStateChangeForInlinedCall(CompilerConstraintList* constraints); void watchStateChangeForTypedArrayData(CompilerConstraintList* constraints); - void watchStateChangeForUnboxedConvertedToNative(CompilerConstraintList* constraints); HeapTypeSetKey property(jsid id); void ensureTrackedProperty(JSContext* cx, jsid id); @@ -498,7 +497,10 @@ class TypeSet // Clone a type set into an arbitrary allocator. TemporaryTypeSet* clone(LifoAlloc* alloc) const; - bool clone(LifoAlloc* alloc, TemporaryTypeSet* result) const; + + // |*result| is not even partly initialized when this function is called: + // this function placement-new's its contents into existence. + bool cloneIntoUninitialized(LifoAlloc* alloc, TemporaryTypeSet* result) const; // Create a new TemporaryTypeSet where undefined and/or null has been filtered out. TemporaryTypeSet* filter(LifoAlloc* alloc, bool filterUndefined, bool filterNull) const; @@ -787,8 +789,65 @@ class TemporaryTypeSet : public TypeSet TypedArraySharedness* sharedness); }; +// Stack class to record information about constraints that need to be added +// after finishing the Definite Properties Analysis. When the analysis succeeds, +// the |finishConstraints| method must be called to add the constraints to the +// TypeSets. +// +// There are two constraint types managed here: +// +// 1. Proto constraints for HeapTypeSets, to guard against things like getters +// and setters on the proto chain. +// +// 2. Inlining constraints for StackTypeSets, to invalidate when additional +// functions could be called at call sites where we inlined a function. +// +// This class uses bare GC-thing pointers because GC is suppressed when the +// analysis runs. +class MOZ_RAII DPAConstraintInfo { + struct ProtoConstraint { + JSObject* proto; + jsid id; + ProtoConstraint(JSObject* proto, jsid id) : proto(proto), id(id) {} + }; + struct InliningConstraint { + JSScript* caller; + JSScript* callee; + InliningConstraint(JSScript* caller, JSScript* callee) + : caller(caller), callee(callee) {} + }; + + JS::AutoCheckCannotGC nogc_; + Vector<ProtoConstraint, 8> protoConstraints_; + Vector<InliningConstraint, 4> inliningConstraints_; + +public: + explicit DPAConstraintInfo(JSContext* cx) + : nogc_(cx) + , protoConstraints_(cx) + , inliningConstraints_(cx) + { + } + + DPAConstraintInfo(const DPAConstraintInfo&) = delete; + void operator=(const DPAConstraintInfo&) = delete; + + MOZ_MUST_USE bool addProtoConstraint(JSObject* proto, jsid id) { + return protoConstraints_.emplaceBack(proto, id); + } + MOZ_MUST_USE bool addInliningConstraint(JSScript* caller, JSScript* callee) { + return inliningConstraints_.emplaceBack(caller, callee); + } + + MOZ_MUST_USE bool finishConstraints(JSContext* cx, ObjectGroup* group); +}; + bool -AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* group, HandleId id); +AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, + DPAConstraintInfo& constraintInfo, + ObjectGroup* group, + HandleId id, + bool* added); bool AddClearDefiniteFunctionUsesInScript(JSContext* cx, ObjectGroup* group, @@ -807,15 +866,13 @@ class PreliminaryObjectArray private: // All objects with the type which have been allocated. The pointers in // this array are weak. - JSObject* objects[COUNT]; + JSObject* objects[COUNT] = {}; // zeroes public: - PreliminaryObjectArray() { - mozilla::PodZero(this); - } + PreliminaryObjectArray() = default; - void registerNewObject(JSObject* res); - void unregisterObject(JSObject* obj); + void registerNewObject(PlainObject* res); + void unregisterObject(PlainObject* obj); JSObject* get(size_t i) const { MOZ_ASSERT(i < COUNT); @@ -906,11 +963,11 @@ class TypeNewScript private: // Scripted function which this information was computed for. - HeapPtr<JSFunction*> function_; + HeapPtr<JSFunction*> function_ = {}; // Any preliminary objects with the type. The analyses are not performed // until this array is cleared. - PreliminaryObjectArray* preliminaryObjects; + PreliminaryObjectArray* preliminaryObjects = nullptr; // After the new script properties analyses have been performed, a template // object to use for newly constructed objects. The shape of this object @@ -918,7 +975,7 @@ class TypeNewScript // allocation kind to use. This is null if the new objects have an unboxed // layout, in which case the UnboxedLayout provides the initial structure // of the object. - HeapPtr<PlainObject*> templateObject_; + HeapPtr<PlainObject*> templateObject_ = {}; // Order in which definite properties become initialized. We need this in // case the definite properties are invalidated (such as by adding a setter @@ -928,21 +985,21 @@ class TypeNewScript // shape. Property assignments in inner frames are preceded by a series of // SETPROP_FRAME entries specifying the stack down to the frame containing // the write. - Initializer* initializerList; + Initializer* initializerList = nullptr; // If there are additional properties found by the acquired properties // analysis which were not found by the definite properties analysis, this // shape contains all such additional properties (plus the definite // properties). When an object of this group acquires this shape, it is // fully initialized and its group can be changed to initializedGroup. - HeapPtr<Shape*> initializedShape_; + HeapPtr<Shape*> initializedShape_ = {}; // Group with definite properties set for all properties found by // both the definite and acquired properties analyses. - HeapPtr<ObjectGroup*> initializedGroup_; + HeapPtr<ObjectGroup*> initializedGroup_ = {}; public: - TypeNewScript() { mozilla::PodZero(this); } + TypeNewScript() = default; ~TypeNewScript() { js_delete(preliminaryObjects); js_free(initializerList); diff --git a/js/src/vm/TypedArrayCommon.h b/js/src/vm/TypedArrayCommon.h index d29c93a653..f59419b283 100644 --- a/js/src/vm/TypedArrayCommon.h +++ b/js/src/vm/TypedArrayCommon.h @@ -11,7 +11,8 @@ #include "mozilla/Assertions.h" #include "mozilla/FloatingPoint.h" -#include "mozilla/PodOperations.h" + +#include <algorithm> #include "jsarray.h" #include "jscntxt.h" @@ -245,12 +246,24 @@ class UnsharedOps template<typename T> static void podCopy(SharedMem<T*> dest, SharedMem<T*> src, size_t nelem) { - mozilla::PodCopy(dest.unwrapUnshared(), src.unwrapUnshared(), nelem); + // std::copy_n better matches the argument values/types of this + // function, but as noted below it allows the input/output ranges to + // overlap. std::copy does not, so use it so the compiler has extra + // ability to optimize. + const auto* first = src.unwrapUnshared(); + const auto* last = first + nelem; + auto* result = dest.unwrapUnshared(); + std::copy(first, last, result); } template<typename T> - static void podMove(SharedMem<T*> dest, SharedMem<T*> src, size_t nelem) { - mozilla::PodMove(dest.unwrapUnshared(), src.unwrapUnshared(), nelem); + static void podMove(SharedMem<T*> dest, SharedMem<T*> src, size_t n) { + // std::copy_n copies from |src| to |dest| starting from |src|, so + // input/output ranges *may* permissibly overlap, as this function + // allows. + const auto* start = src.unwrapUnshared(); + auto* result = dest.unwrapUnshared(); + std::copy_n(start, n, result); } static SharedMem<void*> extract(TypedArrayObject* obj) { diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index ae97be0de0..8b03029174 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -361,7 +361,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject return nullptr; const Class* clasp = TypedArrayObject::protoClassForType(ArrayTypeID()); - return global->createBlankPrototypeInheriting(cx, clasp, typedArrayProto); + return GlobalObject::createBlankPrototypeInheriting(cx, global, clasp, typedArrayProto); } static JSObject* @@ -1892,7 +1892,7 @@ DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallA Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal()); if (!proto) { - proto = global->getOrCreateDataViewPrototype(cx); + proto = GlobalObject::getOrCreateDataViewPrototype(cx, global); if (!proto) return false; } @@ -2892,12 +2892,13 @@ DataViewObject::initClass(JSContext* cx) if (global->isStandardClassResolved(JSProto_DataView)) return true; - RootedNativeObject proto(cx, global->createBlankPrototype(cx, &DataViewObject::protoClass)); + RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, + &DataViewObject::protoClass)); if (!proto) return false; - RootedFunction ctor(cx, global->createConstructor(cx, DataViewObject::class_constructor, - cx->names().DataView, 3)); + RootedFunction ctor(cx, GlobalObject::createConstructor(cx, DataViewObject::class_constructor, + cx->names().DataView, 3)); if (!ctor) return false; diff --git a/js/src/vm/UnboxedObject-inl.h b/js/src/vm/UnboxedObject-inl.h deleted file mode 100644 index 93ad7bf28a..0000000000 --- a/js/src/vm/UnboxedObject-inl.h +++ /dev/null @@ -1,840 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef vm_UnboxedObject_inl_h -#define vm_UnboxedObject_inl_h - -#include "vm/UnboxedObject.h" - -#include "gc/StoreBuffer-inl.h" -#include "vm/ArrayObject-inl.h" -#include "vm/NativeObject-inl.h" - -namespace js { - -static inline Value -GetUnboxedValue(uint8_t* p, JSValueType type, bool maybeUninitialized) -{ - switch (type) { - case JSVAL_TYPE_BOOLEAN: - return BooleanValue(*p != 0); - - case JSVAL_TYPE_INT32: - return Int32Value(*reinterpret_cast<int32_t*>(p)); - - case JSVAL_TYPE_DOUBLE: { - // During unboxed plain object creation, non-GC thing properties are - // left uninitialized. This is normally fine, since the properties will - // be filled in shortly, but if they are read before that happens we - // need to make sure that doubles are canonical. - double d = *reinterpret_cast<double*>(p); - if (maybeUninitialized) - return DoubleValue(JS::CanonicalizeNaN(d)); - return DoubleValue(d); - } - - case JSVAL_TYPE_STRING: - return StringValue(*reinterpret_cast<JSString**>(p)); - - case JSVAL_TYPE_OBJECT: - return ObjectOrNullValue(*reinterpret_cast<JSObject**>(p)); - - default: - MOZ_CRASH("Invalid type for unboxed value"); - } -} - -static inline void -SetUnboxedValueNoTypeChange(JSObject* unboxedObject, - uint8_t* p, JSValueType type, const Value& v, - bool preBarrier) -{ - switch (type) { - case JSVAL_TYPE_BOOLEAN: - *p = v.toBoolean(); - return; - - case JSVAL_TYPE_INT32: - *reinterpret_cast<int32_t*>(p) = v.toInt32(); - return; - - case JSVAL_TYPE_DOUBLE: - *reinterpret_cast<double*>(p) = v.toNumber(); - return; - - case JSVAL_TYPE_STRING: { - MOZ_ASSERT(!IsInsideNursery(v.toString())); - JSString** np = reinterpret_cast<JSString**>(p); - if (preBarrier) - JSString::writeBarrierPre(*np); - *np = v.toString(); - return; - } - - case JSVAL_TYPE_OBJECT: { - JSObject** np = reinterpret_cast<JSObject**>(p); - - // Manually trigger post barriers on the whole object. If we treat - // the pointer as a HeapPtrObject we will get confused later if the - // object is converted to its native representation. - JSObject* obj = v.toObjectOrNull(); - if (IsInsideNursery(obj) && !IsInsideNursery(unboxedObject)) { - JSRuntime* rt = unboxedObject->runtimeFromMainThread(); - rt->gc.storeBuffer.putWholeCell(unboxedObject); - } - - if (preBarrier) - JSObject::writeBarrierPre(*np); - *np = obj; - return; - } - - default: - MOZ_CRASH("Invalid type for unboxed value"); - } -} - -static inline bool -SetUnboxedValue(ExclusiveContext* cx, JSObject* unboxedObject, jsid id, - uint8_t* p, JSValueType type, const Value& v, bool preBarrier) -{ - switch (type) { - case JSVAL_TYPE_BOOLEAN: - if (v.isBoolean()) { - *p = v.toBoolean(); - return true; - } - return false; - - case JSVAL_TYPE_INT32: - if (v.isInt32()) { - *reinterpret_cast<int32_t*>(p) = v.toInt32(); - return true; - } - return false; - - case JSVAL_TYPE_DOUBLE: - if (v.isNumber()) { - *reinterpret_cast<double*>(p) = v.toNumber(); - return true; - } - return false; - - case JSVAL_TYPE_STRING: - if (v.isString()) { - MOZ_ASSERT(!IsInsideNursery(v.toString())); - JSString** np = reinterpret_cast<JSString**>(p); - if (preBarrier) - JSString::writeBarrierPre(*np); - *np = v.toString(); - return true; - } - return false; - - case JSVAL_TYPE_OBJECT: - if (v.isObjectOrNull()) { - JSObject** np = reinterpret_cast<JSObject**>(p); - - // Update property types when writing object properties. Types for - // other properties were captured when the unboxed layout was - // created. - AddTypePropertyId(cx, unboxedObject, id, v); - - // As above, trigger post barriers on the whole object. - JSObject* obj = v.toObjectOrNull(); - if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(unboxedObject)) { - JSRuntime* rt = unboxedObject->runtimeFromMainThread(); - rt->gc.storeBuffer.putWholeCell(unboxedObject); - } - - if (preBarrier) - JSObject::writeBarrierPre(*np); - *np = obj; - return true; - } - return false; - - default: - MOZ_CRASH("Invalid type for unboxed value"); - } -} - -///////////////////////////////////////////////////////////////////// -// UnboxedPlainObject -///////////////////////////////////////////////////////////////////// - -inline const UnboxedLayout& -UnboxedPlainObject::layout() const -{ - return group()->unboxedLayout(); -} - -///////////////////////////////////////////////////////////////////// -// UnboxedArrayObject -///////////////////////////////////////////////////////////////////// - -inline const UnboxedLayout& -UnboxedArrayObject::layout() const -{ - return group()->unboxedLayout(); -} - -inline void -UnboxedArrayObject::setLength(ExclusiveContext* cx, uint32_t length) -{ - if (length > INT32_MAX) { - // Track objects with overflowing lengths in type information. - MarkObjectGroupFlags(cx, this, OBJECT_FLAG_LENGTH_OVERFLOW); - } - - length_ = length; -} - -inline void -UnboxedArrayObject::setInitializedLength(uint32_t initlen) -{ - if (initlen < initializedLength()) { - switch (elementType()) { - case JSVAL_TYPE_STRING: - for (size_t i = initlen; i < initializedLength(); i++) - triggerPreBarrier<JSVAL_TYPE_STRING>(i); - break; - case JSVAL_TYPE_OBJECT: - for (size_t i = initlen; i < initializedLength(); i++) - triggerPreBarrier<JSVAL_TYPE_OBJECT>(i); - break; - default: - MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(elementType())); - } - } - setInitializedLengthNoBarrier(initlen); -} - -template <JSValueType Type> -inline bool -UnboxedArrayObject::setElementSpecific(ExclusiveContext* cx, size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - MOZ_ASSERT(Type == elementType()); - uint8_t* p = elements() + index * UnboxedTypeSize(Type); - return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ true); -} - -template <JSValueType Type> -inline void -UnboxedArrayObject::setElementNoTypeChangeSpecific(size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - MOZ_ASSERT(Type == elementType()); - uint8_t* p = elements() + index * UnboxedTypeSize(Type); - return SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ true); -} - -template <JSValueType Type> -inline bool -UnboxedArrayObject::initElementSpecific(ExclusiveContext* cx, size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - MOZ_ASSERT(Type == elementType()); - uint8_t* p = elements() + index * UnboxedTypeSize(Type); - return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ false); -} - -template <JSValueType Type> -inline void -UnboxedArrayObject::initElementNoTypeChangeSpecific(size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - MOZ_ASSERT(Type == elementType()); - uint8_t* p = elements() + index * UnboxedTypeSize(Type); - return SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ false); -} - -template <JSValueType Type> -inline Value -UnboxedArrayObject::getElementSpecific(size_t index) -{ - MOZ_ASSERT(index < initializedLength()); - MOZ_ASSERT(Type == elementType()); - uint8_t* p = elements() + index * UnboxedTypeSize(Type); - return GetUnboxedValue(p, Type, /* maybeUninitialized = */ false); -} - -template <JSValueType Type> -inline void -UnboxedArrayObject::triggerPreBarrier(size_t index) -{ - MOZ_ASSERT(UnboxedTypeNeedsPreBarrier(Type)); - - uint8_t* p = elements() + index * UnboxedTypeSize(Type); - - switch (Type) { - case JSVAL_TYPE_STRING: { - JSString** np = reinterpret_cast<JSString**>(p); - JSString::writeBarrierPre(*np); - break; - } - - case JSVAL_TYPE_OBJECT: { - JSObject** np = reinterpret_cast<JSObject**>(p); - JSObject::writeBarrierPre(*np); - break; - } - - default: - MOZ_CRASH("Bad type"); - } -} - -///////////////////////////////////////////////////////////////////// -// Combined methods for NativeObject and UnboxedArrayObject accesses. -///////////////////////////////////////////////////////////////////// - -static inline bool -HasAnyBoxedOrUnboxedDenseElements(JSObject* obj) -{ - return obj->isNative() || obj->is<UnboxedArrayObject>(); -} - -static inline size_t -GetAnyBoxedOrUnboxedInitializedLength(JSObject* obj) -{ - if (obj->isNative()) - return obj->as<NativeObject>().getDenseInitializedLength(); - if (obj->is<UnboxedArrayObject>()) - return obj->as<UnboxedArrayObject>().initializedLength(); - return 0; -} - -static inline size_t -GetAnyBoxedOrUnboxedCapacity(JSObject* obj) -{ - if (obj->isNative()) - return obj->as<NativeObject>().getDenseCapacity(); - if (obj->is<UnboxedArrayObject>()) - return obj->as<UnboxedArrayObject>().capacity(); - return 0; -} - -static inline Value -GetAnyBoxedOrUnboxedDenseElement(JSObject* obj, size_t index) -{ - if (obj->isNative()) - return obj->as<NativeObject>().getDenseElement(index); - return obj->as<UnboxedArrayObject>().getElement(index); -} - -static inline size_t -GetAnyBoxedOrUnboxedArrayLength(JSObject* obj) -{ - if (obj->is<ArrayObject>()) - return obj->as<ArrayObject>().length(); - return obj->as<UnboxedArrayObject>().length(); -} - -static inline void -SetAnyBoxedOrUnboxedArrayLength(JSContext* cx, JSObject* obj, size_t length) -{ - if (obj->is<ArrayObject>()) { - MOZ_ASSERT(length >= obj->as<ArrayObject>().length()); - obj->as<ArrayObject>().setLength(cx, length); - } else { - MOZ_ASSERT(length >= obj->as<UnboxedArrayObject>().length()); - obj->as<UnboxedArrayObject>().setLength(cx, length); - } -} - -static inline bool -SetAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value) -{ - if (obj->isNative()) { - obj->as<NativeObject>().setDenseElementWithType(cx, index, value); - return true; - } - return obj->as<UnboxedArrayObject>().setElement(cx, index, value); -} - -static inline bool -InitAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value) -{ - if (obj->isNative()) { - obj->as<NativeObject>().initDenseElementWithType(cx, index, value); - return true; - } - return obj->as<UnboxedArrayObject>().initElement(cx, index, value); -} - -///////////////////////////////////////////////////////////////////// -// Template methods for NativeObject and UnboxedArrayObject accesses. -///////////////////////////////////////////////////////////////////// - -static inline JSValueType -GetBoxedOrUnboxedType(JSObject* obj) -{ - if (obj->isNative()) - return JSVAL_TYPE_MAGIC; - return obj->as<UnboxedArrayObject>().elementType(); -} - -template <JSValueType Type> -static inline bool -HasBoxedOrUnboxedDenseElements(JSObject* obj) -{ - if (Type == JSVAL_TYPE_MAGIC) - return obj->isNative(); - return obj->is<UnboxedArrayObject>() && obj->as<UnboxedArrayObject>().elementType() == Type; -} - -template <JSValueType Type> -static inline size_t -GetBoxedOrUnboxedInitializedLength(JSObject* obj) -{ - if (Type == JSVAL_TYPE_MAGIC) - return obj->as<NativeObject>().getDenseInitializedLength(); - return obj->as<UnboxedArrayObject>().initializedLength(); -} - -template <JSValueType Type> -static inline DenseElementResult -SetBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen) -{ - size_t oldInitlen = GetBoxedOrUnboxedInitializedLength<Type>(obj); - if (Type == JSVAL_TYPE_MAGIC) { - obj->as<NativeObject>().setDenseInitializedLength(initlen); - if (initlen < oldInitlen) - obj->as<NativeObject>().shrinkElements(cx, initlen); - } else { - obj->as<UnboxedArrayObject>().setInitializedLength(initlen); - if (initlen < oldInitlen) - obj->as<UnboxedArrayObject>().shrinkElements(cx, initlen); - } - return DenseElementResult::Success; -} - -template <JSValueType Type> -static inline size_t -GetBoxedOrUnboxedCapacity(JSObject* obj) -{ - if (Type == JSVAL_TYPE_MAGIC) - return obj->as<NativeObject>().getDenseCapacity(); - return obj->as<UnboxedArrayObject>().capacity(); -} - -template <JSValueType Type> -static inline Value -GetBoxedOrUnboxedDenseElement(JSObject* obj, size_t index) -{ - if (Type == JSVAL_TYPE_MAGIC) - return obj->as<NativeObject>().getDenseElement(index); - return obj->as<UnboxedArrayObject>().getElementSpecific<Type>(index); -} - -template <JSValueType Type> -static inline void -SetBoxedOrUnboxedDenseElementNoTypeChange(JSObject* obj, size_t index, const Value& value) -{ - if (Type == JSVAL_TYPE_MAGIC) - obj->as<NativeObject>().setDenseElement(index, value); - else - obj->as<UnboxedArrayObject>().setElementNoTypeChangeSpecific<Type>(index, value); -} - -template <JSValueType Type> -static inline bool -SetBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value) -{ - if (Type == JSVAL_TYPE_MAGIC) { - obj->as<NativeObject>().setDenseElementWithType(cx, index, value); - return true; - } - return obj->as<UnboxedArrayObject>().setElementSpecific<Type>(cx, index, value); -} - -template <JSValueType Type> -static inline DenseElementResult -EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count) -{ - if (Type == JSVAL_TYPE_MAGIC) { - if (!obj->as<ArrayObject>().ensureElements(cx, count)) - return DenseElementResult::Failure; - } else { - if (obj->as<UnboxedArrayObject>().capacity() < count) { - if (!obj->as<UnboxedArrayObject>().growElements(cx, count)) - return DenseElementResult::Failure; - } - } - return DenseElementResult::Success; -} - -template <JSValueType Type> -static inline DenseElementResult -SetOrExtendBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj, - uint32_t start, const Value* vp, uint32_t count, - ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update) -{ - if (Type == JSVAL_TYPE_MAGIC) { - NativeObject* nobj = &obj->as<NativeObject>(); - - if (nobj->denseElementsAreFrozen()) - return DenseElementResult::Incomplete; - - if (obj->is<ArrayObject>() && - !obj->as<ArrayObject>().lengthIsWritable() && - start + count >= obj->as<ArrayObject>().length()) - { - return DenseElementResult::Incomplete; - } - - DenseElementResult result = nobj->ensureDenseElements(cx, start, count); - if (result != DenseElementResult::Success) - return result; - - if (obj->is<ArrayObject>() && start + count >= obj->as<ArrayObject>().length()) - obj->as<ArrayObject>().setLengthInt32(start + count); - - if (updateTypes == ShouldUpdateTypes::DontUpdate && !nobj->shouldConvertDoubleElements()) { - nobj->copyDenseElements(start, vp, count); - } else { - for (size_t i = 0; i < count; i++) - nobj->setDenseElementWithType(cx, start + i, vp[i]); - } - - return DenseElementResult::Success; - } - - UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>(); - - if (start > nobj->initializedLength()) - return DenseElementResult::Incomplete; - - if (start + count >= UnboxedArrayObject::MaximumCapacity) - return DenseElementResult::Incomplete; - - if (start + count > nobj->capacity() && !nobj->growElements(cx, start + count)) - return DenseElementResult::Failure; - - size_t oldInitlen = nobj->initializedLength(); - - // Overwrite any existing elements covered by the new range. If we fail - // after this point due to some incompatible type being written to the - // object's elements, afterwards the contents will be different from when - // we started. The caller must retry the operation using a generic path, - // which will overwrite the already-modified elements as well as the ones - // that were left alone. - size_t i = 0; - if (updateTypes == ShouldUpdateTypes::DontUpdate) { - for (size_t j = start; i < count && j < oldInitlen; i++, j++) - nobj->setElementNoTypeChangeSpecific<Type>(j, vp[i]); - } else { - for (size_t j = start; i < count && j < oldInitlen; i++, j++) { - if (!nobj->setElementSpecific<Type>(cx, j, vp[i])) - return DenseElementResult::Incomplete; - } - } - - if (i != count) { - obj->as<UnboxedArrayObject>().setInitializedLength(start + count); - if (updateTypes == ShouldUpdateTypes::DontUpdate) { - for (; i < count; i++) - nobj->initElementNoTypeChangeSpecific<Type>(start + i, vp[i]); - } else { - for (; i < count; i++) { - if (!nobj->initElementSpecific<Type>(cx, start + i, vp[i])) { - nobj->setInitializedLengthNoBarrier(oldInitlen); - return DenseElementResult::Incomplete; - } - } - } - } - - if (start + count >= nobj->length()) - nobj->setLength(cx, start + count); - - return DenseElementResult::Success; -} - -template <JSValueType Type> -static inline DenseElementResult -MoveBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, uint32_t dstStart, uint32_t srcStart, - uint32_t length) -{ - MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj)); - - if (Type == JSVAL_TYPE_MAGIC) { - if (obj->as<NativeObject>().denseElementsAreFrozen()) - return DenseElementResult::Incomplete; - - if (!obj->as<NativeObject>().maybeCopyElementsForWrite(cx)) - return DenseElementResult::Failure; - obj->as<NativeObject>().moveDenseElements(dstStart, srcStart, length); - } else { - uint8_t* data = obj->as<UnboxedArrayObject>().elements(); - size_t elementSize = UnboxedTypeSize(Type); - - if (UnboxedTypeNeedsPreBarrier(Type) && - JS::shadow::Zone::asShadowZone(obj->zone())->needsIncrementalBarrier()) - { - // Trigger pre barriers on any elements we are overwriting. See - // NativeObject::moveDenseElements. No post barrier is needed as - // only whole cell post barriers are used with unboxed objects. - for (size_t i = 0; i < length; i++) - obj->as<UnboxedArrayObject>().triggerPreBarrier<Type>(dstStart + i); - } - - memmove(data + dstStart * elementSize, - data + srcStart * elementSize, - length * elementSize); - } - - return DenseElementResult::Success; -} - -template <JSValueType DstType, JSValueType SrcType> -static inline DenseElementResult -CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src, - uint32_t dstStart, uint32_t srcStart, uint32_t length) -{ - MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<SrcType>(src)); - MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<DstType>(dst)); - MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<DstType>(dst) == dstStart); - MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<SrcType>(src) >= srcStart + length); - MOZ_ASSERT(GetBoxedOrUnboxedCapacity<DstType>(dst) >= dstStart + length); - - SetBoxedOrUnboxedInitializedLength<DstType>(cx, dst, dstStart + length); - - if (DstType == JSVAL_TYPE_MAGIC) { - if (SrcType == JSVAL_TYPE_MAGIC) { - const Value* vp = src->as<NativeObject>().getDenseElements() + srcStart; - dst->as<NativeObject>().initDenseElements(dstStart, vp, length); - } else { - for (size_t i = 0; i < length; i++) { - Value v = GetBoxedOrUnboxedDenseElement<SrcType>(src, srcStart + i); - dst->as<NativeObject>().initDenseElement(dstStart + i, v); - } - } - } else if (DstType == SrcType) { - uint8_t* dstData = dst->as<UnboxedArrayObject>().elements(); - uint8_t* srcData = src->as<UnboxedArrayObject>().elements(); - size_t elementSize = UnboxedTypeSize(DstType); - - memcpy(dstData + dstStart * elementSize, - srcData + srcStart * elementSize, - length * elementSize); - - // Add a store buffer entry if we might have copied a nursery pointer to dst. - if (UnboxedTypeNeedsPostBarrier(DstType) && !IsInsideNursery(dst)) - dst->runtimeFromMainThread()->gc.storeBuffer.putWholeCell(dst); - } else if (DstType == JSVAL_TYPE_DOUBLE && SrcType == JSVAL_TYPE_INT32) { - uint8_t* dstData = dst->as<UnboxedArrayObject>().elements(); - uint8_t* srcData = src->as<UnboxedArrayObject>().elements(); - - for (size_t i = 0; i < length; i++) { - int32_t v = *reinterpret_cast<int32_t*>(srcData + (srcStart + i) * sizeof(int32_t)); - *reinterpret_cast<double*>(dstData + (dstStart + i) * sizeof(double)) = v; - } - } else { - for (size_t i = 0; i < length; i++) { - Value v = GetBoxedOrUnboxedDenseElement<SrcType>(src, srcStart + i); - dst->as<UnboxedArrayObject>().initElementNoTypeChangeSpecific<DstType>(dstStart + i, v); - } - } - - return DenseElementResult::Success; -} - -///////////////////////////////////////////////////////////////////// -// Dispatch to specialized methods based on the type of an object. -///////////////////////////////////////////////////////////////////// - -// Goop to fix MSVC. See DispatchTraceKindTyped in TraceKind.h. -// The clang-cl front end defines _MSC_VER, but still requires the explicit -// template declaration, so we must test for __clang__ here as well. -#if defined(_MSC_VER) && !defined(__clang__) -# define DEPENDENT_TEMPLATE_HINT -#else -# define DEPENDENT_TEMPLATE_HINT template -#endif - -// Function to dispatch a method specialized to whatever boxed or unboxed dense -// elements which an input object has. -template <typename F> -DenseElementResult -CallBoxedOrUnboxedSpecialization(F f, JSObject* obj) -{ - if (!HasAnyBoxedOrUnboxedDenseElements(obj)) - return DenseElementResult::Incomplete; - switch (GetBoxedOrUnboxedType(obj)) { - case JSVAL_TYPE_MAGIC: - return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_MAGIC>(); - case JSVAL_TYPE_BOOLEAN: - return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_BOOLEAN>(); - case JSVAL_TYPE_INT32: - return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_INT32>(); - case JSVAL_TYPE_DOUBLE: - return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_DOUBLE>(); - case JSVAL_TYPE_STRING: - return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_STRING>(); - case JSVAL_TYPE_OBJECT: - return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_OBJECT>(); - default: - MOZ_CRASH(); - } -} - -// As above, except the specialization can reflect the unboxed type of two objects. -template <typename F> -DenseElementResult -CallBoxedOrUnboxedSpecialization(F f, JSObject* obj1, JSObject* obj2) -{ - if (!HasAnyBoxedOrUnboxedDenseElements(obj1) || !HasAnyBoxedOrUnboxedDenseElements(obj2)) - return DenseElementResult::Incomplete; - -#define SPECIALIZE_OBJ2(TYPE) \ - switch (GetBoxedOrUnboxedType(obj2)) { \ - case JSVAL_TYPE_MAGIC: \ - return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_MAGIC>(); \ - case JSVAL_TYPE_BOOLEAN: \ - return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_BOOLEAN>(); \ - case JSVAL_TYPE_INT32: \ - return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_INT32>(); \ - case JSVAL_TYPE_DOUBLE: \ - return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_DOUBLE>(); \ - case JSVAL_TYPE_STRING: \ - return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_STRING>(); \ - case JSVAL_TYPE_OBJECT: \ - return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_OBJECT>(); \ - default: \ - MOZ_CRASH(); \ - } - - switch (GetBoxedOrUnboxedType(obj1)) { - case JSVAL_TYPE_MAGIC: - SPECIALIZE_OBJ2(JSVAL_TYPE_MAGIC) - case JSVAL_TYPE_BOOLEAN: - SPECIALIZE_OBJ2(JSVAL_TYPE_BOOLEAN) - case JSVAL_TYPE_INT32: - SPECIALIZE_OBJ2(JSVAL_TYPE_INT32) - case JSVAL_TYPE_DOUBLE: - SPECIALIZE_OBJ2(JSVAL_TYPE_DOUBLE) - case JSVAL_TYPE_STRING: - SPECIALIZE_OBJ2(JSVAL_TYPE_STRING) - case JSVAL_TYPE_OBJECT: - SPECIALIZE_OBJ2(JSVAL_TYPE_OBJECT) - default: - MOZ_CRASH(); - } - -#undef SPECIALIZE_OBJ2 -} - -#undef DEPENDENT_TEMPLATE_HINT - -#define DefineBoxedOrUnboxedFunctor1(Signature, A) \ -struct Signature ## Functor { \ - A a; \ - explicit Signature ## Functor(A a) \ - : a(a) \ - {} \ - template <JSValueType Type> \ - DenseElementResult operator()() { \ - return Signature<Type>(a); \ - } \ -} - -#define DefineBoxedOrUnboxedFunctor3(Signature, A, B, C) \ -struct Signature ## Functor { \ - A a; B b; C c; \ - Signature ## Functor(A a, B b, C c) \ - : a(a), b(b), c(c) \ - {} \ - template <JSValueType Type> \ - DenseElementResult operator()() { \ - return Signature<Type>(a, b, c); \ - } \ -} - -#define DefineBoxedOrUnboxedFunctor4(Signature, A, B, C, D) \ -struct Signature ## Functor { \ - A a; B b; C c; D d; \ - Signature ## Functor(A a, B b, C c, D d) \ - : a(a), b(b), c(c), d(d) \ - {} \ - template <JSValueType Type> \ - DenseElementResult operator()() { \ - return Signature<Type>(a, b, c, d); \ - } \ -} - -#define DefineBoxedOrUnboxedFunctorPair4(Signature, A, B, C, D) \ -struct Signature ## Functor { \ - A a; B b; C c; D d; \ - Signature ## Functor(A a, B b, C c, D d) \ - : a(a), b(b), c(c), d(d) \ - {} \ - template <JSValueType TypeOne, JSValueType TypeTwo> \ - DenseElementResult operator()() { \ - return Signature<TypeOne, TypeTwo>(a, b, c, d); \ - } \ -} - -#define DefineBoxedOrUnboxedFunctor5(Signature, A, B, C, D, E) \ -struct Signature ## Functor { \ - A a; B b; C c; D d; E e; \ - Signature ## Functor(A a, B b, C c, D d, E e) \ - : a(a), b(b), c(c), d(d), e(e) \ - {} \ - template <JSValueType Type> \ - DenseElementResult operator()() { \ - return Signature<Type>(a, b, c, d, e); \ - } \ -} - -#define DefineBoxedOrUnboxedFunctor6(Signature, A, B, C, D, E, F) \ -struct Signature ## Functor { \ - A a; B b; C c; D d; E e; F f; \ - Signature ## Functor(A a, B b, C c, D d, E e, F f) \ - : a(a), b(b), c(c), d(d), e(e), f(f) \ - {} \ - template <JSValueType Type> \ - DenseElementResult operator()() { \ - return Signature<Type>(a, b, c, d, e, f); \ - } \ -} - -#define DefineBoxedOrUnboxedFunctorPair6(Signature, A, B, C, D, E, F) \ -struct Signature ## Functor { \ - A a; B b; C c; D d; E e; F f; \ - Signature ## Functor(A a, B b, C c, D d, E e, F f) \ - : a(a), b(b), c(c), d(d), e(e), f(f) \ - {} \ - template <JSValueType TypeOne, JSValueType TypeTwo> \ - DenseElementResult operator()() { \ - return Signature<TypeOne, TypeTwo>(a, b, c, d, e, f); \ - } \ -} - -DenseElementResult -SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj, - uint32_t start, const Value* vp, uint32_t count, - ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update); - -DenseElementResult -MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, - uint32_t dstStart, uint32_t srcStart, uint32_t length); - -DenseElementResult -CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src, - uint32_t dstStart, uint32_t srcStart, uint32_t length); - -void -SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen); - -DenseElementResult -EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count); - -} // namespace js - -#endif // vm_UnboxedObject_inl_h diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp deleted file mode 100644 index 3018ace677..0000000000 --- a/js/src/vm/UnboxedObject.cpp +++ /dev/null @@ -1,2152 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "vm/UnboxedObject-inl.h" - -#include "jit/BaselineIC.h" -#include "jit/ExecutableAllocator.h" -#include "jit/JitCommon.h" -#include "jit/Linker.h" - -#include "jsobjinlines.h" - -#include "gc/Nursery-inl.h" -#include "jit/MacroAssembler-inl.h" -#include "vm/Shape-inl.h" - -using mozilla::ArrayLength; -using mozilla::DebugOnly; -using mozilla::PodCopy; - -using namespace js; - -///////////////////////////////////////////////////////////////////// -// UnboxedLayout -///////////////////////////////////////////////////////////////////// - -void -UnboxedLayout::trace(JSTracer* trc) -{ - for (size_t i = 0; i < properties_.length(); i++) - TraceManuallyBarrieredEdge(trc, &properties_[i].name, "unboxed_layout_name"); - - if (newScript()) - newScript()->trace(trc); - - TraceNullableEdge(trc, &nativeGroup_, "unboxed_layout_nativeGroup"); - TraceNullableEdge(trc, &nativeShape_, "unboxed_layout_nativeShape"); - TraceNullableEdge(trc, &allocationScript_, "unboxed_layout_allocationScript"); - TraceNullableEdge(trc, &replacementGroup_, "unboxed_layout_replacementGroup"); - TraceNullableEdge(trc, &constructorCode_, "unboxed_layout_constructorCode"); -} - -size_t -UnboxedLayout::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) -{ - return mallocSizeOf(this) - + properties_.sizeOfExcludingThis(mallocSizeOf) - + (newScript() ? newScript()->sizeOfIncludingThis(mallocSizeOf) : 0) - + mallocSizeOf(traceList()); -} - -void -UnboxedLayout::setNewScript(TypeNewScript* newScript, bool writeBarrier /* = true */) -{ - if (newScript_ && writeBarrier) - TypeNewScript::writeBarrierPre(newScript_); - newScript_ = newScript; -} - -// Constructor code returns a 0x1 value to indicate the constructor code should -// be cleared. -static const uintptr_t CLEAR_CONSTRUCTOR_CODE_TOKEN = 0x1; - -/* static */ bool -UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group) -{ - gc::AutoSuppressGC suppress(cx); - - using namespace jit; - - if (!cx->compartment()->ensureJitCompartmentExists(cx)) - return false; - - UnboxedLayout& layout = group->unboxedLayout(); - MOZ_ASSERT(!layout.constructorCode()); - - UnboxedPlainObject* templateObject = UnboxedPlainObject::create(cx, group, TenuredObject); - if (!templateObject) - return false; - - JitContext jitContext(cx, nullptr); - - MacroAssembler masm; - - Register propertiesReg, newKindReg; -#ifdef JS_CODEGEN_X86 - propertiesReg = eax; - newKindReg = ecx; - masm.loadPtr(Address(masm.getStackPointer(), sizeof(void*)), propertiesReg); - masm.loadPtr(Address(masm.getStackPointer(), 2 * sizeof(void*)), newKindReg); -#else - propertiesReg = IntArgReg0; - newKindReg = IntArgReg1; -#endif - -#ifdef JS_CODEGEN_ARM64 - // ARM64 communicates stack address via sp, but uses a pseudo-sp for addressing. - masm.initStackPtr(); -#endif - - MOZ_ASSERT(propertiesReg.volatile_()); - MOZ_ASSERT(newKindReg.volatile_()); - - AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); - regs.take(propertiesReg); - regs.take(newKindReg); - Register object = regs.takeAny(), scratch1 = regs.takeAny(), scratch2 = regs.takeAny(); - - LiveGeneralRegisterSet savedNonVolatileRegisters = SavedNonVolatileRegisters(regs); - masm.PushRegsInMask(savedNonVolatileRegisters); - - // The scratch double register might be used by MacroAssembler methods. - if (ScratchDoubleReg.volatile_()) - masm.push(ScratchDoubleReg); - - Label failure, tenuredObject, allocated; - masm.branch32(Assembler::NotEqual, newKindReg, Imm32(GenericObject), &tenuredObject); - masm.branchTest32(Assembler::NonZero, AbsoluteAddress(group->addressOfFlags()), - Imm32(OBJECT_FLAG_PRE_TENURE), &tenuredObject); - - // Allocate an object in the nursery - masm.createGCObject(object, scratch1, templateObject, gc::DefaultHeap, &failure, - /* initFixedSlots = */ false); - - masm.jump(&allocated); - masm.bind(&tenuredObject); - - // Allocate an object in the tenured heap. - masm.createGCObject(object, scratch1, templateObject, gc::TenuredHeap, &failure, - /* initFixedSlots = */ false); - - // If any of the properties being stored are in the nursery, add a store - // buffer entry for the new object. - Label postBarrier; - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - if (property.type == JSVAL_TYPE_OBJECT) { - Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value)); - Label notObject; - masm.branchTestObject(Assembler::NotEqual, valueAddress, ¬Object); - Register valueObject = masm.extractObject(valueAddress, scratch1); - masm.branchPtrInNurseryChunk(Assembler::Equal, valueObject, scratch2, &postBarrier); - masm.bind(¬Object); - } - } - - masm.jump(&allocated); - masm.bind(&postBarrier); - - LiveGeneralRegisterSet liveVolatileRegisters; - liveVolatileRegisters.add(propertiesReg); - if (object.volatile_()) - liveVolatileRegisters.add(object); - masm.PushRegsInMask(liveVolatileRegisters); - - masm.mov(ImmPtr(cx->runtime()), scratch1); - masm.setupUnalignedABICall(scratch2); - masm.passABIArg(scratch1); - masm.passABIArg(object); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier)); - - masm.PopRegsInMask(liveVolatileRegisters); - - masm.bind(&allocated); - - ValueOperand valueOperand; -#ifdef JS_NUNBOX32 - valueOperand = ValueOperand(scratch1, scratch2); -#else - valueOperand = ValueOperand(scratch1); -#endif - - Label failureStoreOther, failureStoreObject; - - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value)); - Address targetAddress(object, UnboxedPlainObject::offsetOfData() + property.offset); - - masm.loadValue(valueAddress, valueOperand); - - if (property.type == JSVAL_TYPE_OBJECT) { - HeapTypeSet* types = group->maybeGetProperty(IdToTypeId(NameToId(property.name))); - - Label notObject; - masm.branchTestObject(Assembler::NotEqual, valueOperand, - types->mightBeMIRType(MIRType::Null) ? ¬Object : &failureStoreObject); - - Register payloadReg = masm.extractObject(valueOperand, scratch1); - - if (!types->hasType(TypeSet::AnyObjectType())) { - Register scratch = (payloadReg == scratch1) ? scratch2 : scratch1; - masm.guardObjectType(payloadReg, types, scratch, &failureStoreObject); - } - - masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT, - TypedOrValueRegister(MIRType::Object, - AnyRegister(payloadReg)), nullptr); - - if (notObject.used()) { - Label done; - masm.jump(&done); - masm.bind(¬Object); - masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther); - masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT, NullValue(), nullptr); - masm.bind(&done); - } - } else { - masm.storeUnboxedProperty(targetAddress, property.type, - ConstantOrRegister(valueOperand), &failureStoreOther); - } - } - - Label done; - masm.bind(&done); - - if (object != ReturnReg) - masm.movePtr(object, ReturnReg); - - // Restore non-volatile registers which were saved on entry. - if (ScratchDoubleReg.volatile_()) - masm.pop(ScratchDoubleReg); - masm.PopRegsInMask(savedNonVolatileRegisters); - - masm.abiret(); - - masm.bind(&failureStoreOther); - - // There was a failure while storing a value which cannot be stored at all - // in the unboxed object. Initialize the object so it is safe for GC and - // return null. - masm.initUnboxedObjectContents(object, templateObject); - - masm.bind(&failure); - - masm.movePtr(ImmWord(0), object); - masm.jump(&done); - - masm.bind(&failureStoreObject); - - // There was a failure while storing a value to an object slot of the - // unboxed object. If the value is storable, the failure occurred due to - // incomplete type information in the object, so return a token to trigger - // regeneration of the jitcode after a new object is created in the VM. - { - Label isObject; - masm.branchTestObject(Assembler::Equal, valueOperand, &isObject); - masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther); - masm.bind(&isObject); - } - - // Initialize the object so it is safe for GC. - masm.initUnboxedObjectContents(object, templateObject); - - masm.movePtr(ImmWord(CLEAR_CONSTRUCTOR_CODE_TOKEN), object); - masm.jump(&done); - - Linker linker(masm); - AutoFlushICache afc("UnboxedObject"); - JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE); - if (!code) - return false; - - layout.setConstructorCode(code); - return true; -} - -void -UnboxedLayout::detachFromCompartment() -{ - if (isInList()) - remove(); -} - -///////////////////////////////////////////////////////////////////// -// UnboxedPlainObject -///////////////////////////////////////////////////////////////////// - -bool -UnboxedPlainObject::setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property, - const Value& v) -{ - uint8_t* p = &data_[property.offset]; - return SetUnboxedValue(cx, this, NameToId(property.name), p, property.type, v, - /* preBarrier = */ true); -} - -Value -UnboxedPlainObject::getValue(const UnboxedLayout::Property& property, - bool maybeUninitialized /* = false */) -{ - uint8_t* p = &data_[property.offset]; - return GetUnboxedValue(p, property.type, maybeUninitialized); -} - -void -UnboxedPlainObject::trace(JSTracer* trc, JSObject* obj) -{ - if (obj->as<UnboxedPlainObject>().expando_) { - TraceManuallyBarrieredEdge(trc, - reinterpret_cast<NativeObject**>(&obj->as<UnboxedPlainObject>().expando_), - "unboxed_expando"); - } - - const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layoutDontCheckGeneration(); - const int32_t* list = layout.traceList(); - if (!list) - return; - - uint8_t* data = obj->as<UnboxedPlainObject>().data(); - while (*list != -1) { - GCPtrString* heap = reinterpret_cast<GCPtrString*>(data + *list); - TraceEdge(trc, heap, "unboxed_string"); - list++; - } - list++; - while (*list != -1) { - GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(data + *list); - TraceNullableEdge(trc, heap, "unboxed_object"); - list++; - } - - // Unboxed objects don't have Values to trace. - MOZ_ASSERT(*(list + 1) == -1); -} - -/* static */ UnboxedExpandoObject* -UnboxedPlainObject::ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj) -{ - if (obj->expando_) - return obj->expando_; - - UnboxedExpandoObject* expando = - NewObjectWithGivenProto<UnboxedExpandoObject>(cx, nullptr, gc::AllocKind::OBJECT4); - if (!expando) - return nullptr; - - // Don't track property types for expando objects. This allows Baseline - // and Ion AddSlot ICs to guard on the unboxed group without guarding on - // the expando group. - MarkObjectGroupUnknownProperties(cx, expando->group()); - - // If the expando is tenured then the original object must also be tenured. - // Otherwise barriers triggered on the original object for writes to the - // expando (as can happen in the JIT) won't see the tenured->nursery edge. - // See WholeCellEdges::mark. - MOZ_ASSERT_IF(!IsInsideNursery(expando), !IsInsideNursery(obj)); - - // As with setValue(), we need to manually trigger post barriers on the - // whole object. If we treat the field as a GCPtrObject and later - // convert the object to its native representation, we will end up with a - // corrupted store buffer entry. - if (IsInsideNursery(expando) && !IsInsideNursery(obj)) - cx->runtime()->gc.storeBuffer.putWholeCell(obj); - - obj->expando_ = expando; - return expando; -} - -bool -UnboxedPlainObject::containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid id) const -{ - if (layout().lookup(id)) - return true; - - if (maybeExpando() && maybeExpando()->containsShapeOrElement(cx, id)) - return true; - - return false; -} - -static bool -PropagatePropertyTypes(JSContext* cx, jsid id, ObjectGroup* oldGroup, ObjectGroup* newGroup) -{ - HeapTypeSet* typeProperty = oldGroup->maybeGetProperty(id); - TypeSet::TypeList types; - if (!typeProperty->enumerateTypes(&types)) { - ReportOutOfMemory(cx); - return false; - } - for (size_t j = 0; j < types.length(); j++) - AddTypePropertyId(cx, newGroup, nullptr, id, types[j]); - return true; -} - -static PlainObject* -MakeReplacementTemplateObject(JSContext* cx, HandleObjectGroup group, const UnboxedLayout &layout) -{ - PlainObject* obj = NewObjectWithGroup<PlainObject>(cx, group, layout.getAllocKind(), - TenuredObject); - if (!obj) - return nullptr; - - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - if (!obj->addDataProperty(cx, NameToId(property.name), i, JSPROP_ENUMERATE)) - return nullptr; - MOZ_ASSERT(obj->slotSpan() == i + 1); - MOZ_ASSERT(!obj->inDictionaryMode()); - } - - return obj; -} - -/* static */ bool -UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group) -{ - AutoEnterAnalysis enter(cx); - - UnboxedLayout& layout = group->unboxedLayout(); - Rooted<TaggedProto> proto(cx, group->proto()); - - MOZ_ASSERT(!layout.nativeGroup()); - - RootedObjectGroup replacementGroup(cx); - - const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_; - - // Immediately clear any new script on the group. This is done by replacing - // the existing new script with one for a replacement default new group. - // This is done so that the size of the replacment group's objects is the - // same as that for the unboxed group, so that we do not see polymorphic - // slot accesses later on for sites that see converted objects from this - // group and objects that were allocated using the replacement new group. - if (layout.newScript()) { - MOZ_ASSERT(!layout.isArray()); - - replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto); - if (!replacementGroup) - return false; - - PlainObject* templateObject = MakeReplacementTemplateObject(cx, replacementGroup, layout); - if (!templateObject) - return false; - - TypeNewScript* replacementNewScript = - TypeNewScript::makeNativeVersion(cx, layout.newScript(), templateObject); - if (!replacementNewScript) - return false; - - replacementGroup->setNewScript(replacementNewScript); - gc::TraceTypeNewScript(replacementGroup); - - group->clearNewScript(cx, replacementGroup); - } - - // Similarly, if this group is keyed to an allocation site, replace its - // entry with a new group that has no unboxed layout. - if (layout.allocationScript()) { - RootedScript script(cx, layout.allocationScript()); - jsbytecode* pc = layout.allocationPc(); - - replacementGroup = ObjectGroupCompartment::makeGroup(cx, clasp, proto); - if (!replacementGroup) - return false; - - PlainObject* templateObject = &script->getObject(pc)->as<PlainObject>(); - replacementGroup->addDefiniteProperties(cx, templateObject->lastProperty()); - - JSProtoKey key = layout.isArray() ? JSProto_Array : JSProto_Object; - cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc, key, - replacementGroup); - - // Clear any baseline information at this opcode which might use the old group. - if (script->hasBaselineScript()) { - jit::ICEntry& entry = script->baselineScript()->icEntryFromPCOffset(script->pcToOffset(pc)); - jit::ICFallbackStub* fallback = entry.fallbackStub(); - for (jit::ICStubIterator iter = fallback->beginChain(); !iter.atEnd(); iter++) - iter.unlink(cx); - if (fallback->isNewObject_Fallback()) - fallback->toNewObject_Fallback()->setTemplateObject(nullptr); - else if (fallback->isNewArray_Fallback()) - fallback->toNewArray_Fallback()->setTemplateGroup(replacementGroup); - } - } - - size_t nfixed = layout.isArray() ? 0 : gc::GetGCKindSlots(layout.getAllocKind()); - - if (layout.isArray()) { - // The length shape to use for arrays is cached via a modified initial - // shape for array objects. Create an array now to make sure this entry - // is instantiated. - if (!NewDenseEmptyArray(cx)) - return false; - } - - RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, proto, nfixed, 0)); - if (!shape) - return false; - - MOZ_ASSERT_IF(layout.isArray(), !shape->isEmptyShape() && shape->slotSpan() == 0); - - // Add shapes for each property, if this is for a plain object. - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - - Rooted<StackShape> child(cx, StackShape(shape->base()->unowned(), NameToId(property.name), - i, JSPROP_ENUMERATE, 0)); - shape = cx->zone()->propertyTree.getChild(cx, shape, child); - if (!shape) - return false; - } - - ObjectGroup* nativeGroup = - ObjectGroupCompartment::makeGroup(cx, clasp, proto, - group->flags() & OBJECT_FLAG_DYNAMIC_MASK); - if (!nativeGroup) - return false; - - // No sense propagating if we don't know what we started with. - if (!group->unknownProperties()) { - // Propagate all property types from the old group to the new group. - if (layout.isArray()) { - if (!PropagatePropertyTypes(cx, JSID_VOID, group, nativeGroup)) - return false; - } else { - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - jsid id = NameToId(property.name); - if (!PropagatePropertyTypes(cx, id, group, nativeGroup)) - return false; - - // If we are OOM we may not be able to propagate properties. - if (nativeGroup->unknownProperties()) - break; - - HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id); - if (nativeProperty && nativeProperty->canSetDefinite(i)) - nativeProperty->setDefinite(i); - } - } - } else { - // If we skip, though, the new group had better agree. - MOZ_ASSERT(nativeGroup->unknownProperties()); - } - - layout.nativeGroup_ = nativeGroup; - layout.nativeShape_ = shape; - layout.replacementGroup_ = replacementGroup; - - nativeGroup->setOriginalUnboxedGroup(group); - - group->markStateChange(cx); - - return true; -} - -/* static */ bool -UnboxedPlainObject::convertToNative(JSContext* cx, JSObject* obj) -{ - const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout(); - UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando(); - - if (!layout.nativeGroup()) { - if (!UnboxedLayout::makeNativeGroup(cx, obj->group())) - return false; - - // makeNativeGroup can reentrantly invoke this method. - if (obj->is<PlainObject>()) - return true; - } - - AutoValueVector values(cx); - for (size_t i = 0; i < layout.properties().length(); i++) { - // We might be reading properties off the object which have not been - // initialized yet. Make sure any double values we read here are - // canonicalized. - if (!values.append(obj->as<UnboxedPlainObject>().getValue(layout.properties()[i], true))) - return false; - } - - // We are eliminating the expando edge with the conversion, so trigger a - // pre barrier. - JSObject::writeBarrierPre(expando); - - // Additionally trigger a post barrier on the expando itself. Whole cell - // store buffer entries can be added on the original unboxed object for - // writes to the expando (see WholeCellEdges::trace), so after conversion - // we need to make sure the expando itself will still be traced. - if (expando && !IsInsideNursery(expando)) - cx->runtime()->gc.storeBuffer.putWholeCell(expando); - - obj->setGroup(layout.nativeGroup()); - obj->as<PlainObject>().setLastPropertyMakeNative(cx, layout.nativeShape()); - - for (size_t i = 0; i < values.length(); i++) - obj->as<PlainObject>().initSlotUnchecked(i, values[i]); - - if (expando) { - // Add properties from the expando object to the object, in order. - // Suppress GC here, so that callers don't need to worry about this - // method collecting. The stuff below can only fail due to OOM, in - // which case the object will not have been completely filled back in. - gc::AutoSuppressGC suppress(cx); - - Vector<jsid> ids(cx); - for (Shape::Range<NoGC> r(expando->lastProperty()); !r.empty(); r.popFront()) { - if (!ids.append(r.front().propid())) - return false; - } - for (size_t i = 0; i < expando->getDenseInitializedLength(); i++) { - if (!expando->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) { - if (!ids.append(INT_TO_JSID(i))) - return false; - } - } - ::Reverse(ids.begin(), ids.end()); - - RootedPlainObject nobj(cx, &obj->as<PlainObject>()); - Rooted<UnboxedExpandoObject*> nexpando(cx, expando); - RootedId id(cx); - Rooted<PropertyDescriptor> desc(cx); - for (size_t i = 0; i < ids.length(); i++) { - id = ids[i]; - if (!GetOwnPropertyDescriptor(cx, nexpando, id, &desc)) - return false; - ObjectOpResult result; - if (!DefineProperty(cx, nobj, id, desc, result)) - return false; - MOZ_ASSERT(result.ok()); - } - } - - return true; -} - -/* static */ -UnboxedPlainObject* -UnboxedPlainObject::create(ExclusiveContext* cx, HandleObjectGroup group, NewObjectKind newKind) -{ - AutoSetNewObjectMetadata metadata(cx); - - MOZ_ASSERT(group->clasp() == &class_); - gc::AllocKind allocKind = group->unboxedLayout().getAllocKind(); - - UnboxedPlainObject* res = - NewObjectWithGroup<UnboxedPlainObject>(cx, group, allocKind, newKind); - if (!res) - return nullptr; - - // Overwrite the dummy shape which was written to the object's expando field. - res->initExpando(); - - // Initialize reference fields of the object. All fields in the object will - // be overwritten shortly, but references need to be safe for the GC. - const int32_t* list = res->layout().traceList(); - if (list) { - uint8_t* data = res->data(); - while (*list != -1) { - GCPtrString* heap = reinterpret_cast<GCPtrString*>(data + *list); - heap->init(cx->names().empty); - list++; - } - list++; - while (*list != -1) { - GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(data + *list); - heap->init(nullptr); - list++; - } - // Unboxed objects don't have Values to initialize. - MOZ_ASSERT(*(list + 1) == -1); - } - - return res; -} - -/* static */ JSObject* -UnboxedPlainObject::createWithProperties(ExclusiveContext* cx, HandleObjectGroup group, - NewObjectKind newKind, IdValuePair* properties) -{ - MOZ_ASSERT(newKind == GenericObject || newKind == TenuredObject); - - UnboxedLayout& layout = group->unboxedLayout(); - - if (layout.constructorCode()) { - MOZ_ASSERT(cx->isJSContext()); - - typedef JSObject* (*ConstructorCodeSignature)(IdValuePair*, NewObjectKind); - ConstructorCodeSignature function = - reinterpret_cast<ConstructorCodeSignature>(layout.constructorCode()->raw()); - - JSObject* obj; - { - JS::AutoSuppressGCAnalysis nogc; - obj = reinterpret_cast<JSObject*>(CALL_GENERATED_2(function, properties, newKind)); - } - if (obj > reinterpret_cast<JSObject*>(CLEAR_CONSTRUCTOR_CODE_TOKEN)) - return obj; - - if (obj == reinterpret_cast<JSObject*>(CLEAR_CONSTRUCTOR_CODE_TOKEN)) - layout.setConstructorCode(nullptr); - } - - UnboxedPlainObject* obj = UnboxedPlainObject::create(cx, group, newKind); - if (!obj) - return nullptr; - - for (size_t i = 0; i < layout.properties().length(); i++) { - if (!obj->setValue(cx, layout.properties()[i], properties[i].value)) - return NewPlainObjectWithProperties(cx, properties, layout.properties().length(), newKind); - } - -#ifndef JS_CODEGEN_NONE - if (cx->isJSContext() && - !group->unknownProperties() && - !layout.constructorCode() && - cx->asJSContext()->runtime()->jitSupportsFloatingPoint && - jit::CanLikelyAllocateMoreExecutableMemory()) - { - if (!UnboxedLayout::makeConstructorCode(cx->asJSContext(), group)) - return nullptr; - } -#endif - - return obj; -} - -/* static */ bool -UnboxedPlainObject::obj_lookupProperty(JSContext* cx, HandleObject obj, - HandleId id, MutableHandleObject objp, - MutableHandleShape propp) -{ - if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) { - MarkNonNativePropertyFound<CanGC>(propp); - objp.set(obj); - return true; - } - - RootedObject proto(cx, obj->staticPrototype()); - if (!proto) { - objp.set(nullptr); - propp.set(nullptr); - return true; - } - - return LookupProperty(cx, proto, id, objp, propp); -} - -/* static */ bool -UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle<PropertyDescriptor> desc, - ObjectOpResult& result) -{ - const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout(); - - if (const UnboxedLayout::Property* property = layout.lookup(id)) { - if (!desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) { - // This define is equivalent to setting an existing property. - if (obj->as<UnboxedPlainObject>().setValue(cx, *property, desc.value())) - return result.succeed(); - } - - // Trying to incompatibly redefine an existing property requires the - // object to be converted to a native object. - if (!convertToNative(cx, obj)) - return false; - - return DefineProperty(cx, obj, id, desc, result); - } - - // Define the property on the expando object. - Rooted<UnboxedExpandoObject*> expando(cx, ensureExpando(cx, obj.as<UnboxedPlainObject>())); - if (!expando) - return false; - - // Update property types on the unboxed object as well. - AddTypePropertyId(cx, obj, id, desc.value()); - - return DefineProperty(cx, expando, id, desc, result); -} - -/* static */ bool -UnboxedPlainObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp) -{ - if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) { - *foundp = true; - return true; - } - - RootedObject proto(cx, obj->staticPrototype()); - if (!proto) { - *foundp = false; - return true; - } - - return HasProperty(cx, proto, id, foundp); -} - -/* static */ bool -UnboxedPlainObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver, - HandleId id, MutableHandleValue vp) -{ - const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout(); - - if (const UnboxedLayout::Property* property = layout.lookup(id)) { - vp.set(obj->as<UnboxedPlainObject>().getValue(*property)); - return true; - } - - if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) { - if (expando->containsShapeOrElement(cx, id)) { - RootedObject nexpando(cx, expando); - return GetProperty(cx, nexpando, receiver, id, vp); - } - } - - RootedObject proto(cx, obj->staticPrototype()); - if (!proto) { - vp.setUndefined(); - return true; - } - - return GetProperty(cx, proto, receiver, id, vp); -} - -/* static */ bool -UnboxedPlainObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult& result) -{ - const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout(); - - if (const UnboxedLayout::Property* property = layout.lookup(id)) { - if (receiver.isObject() && obj == &receiver.toObject()) { - if (obj->as<UnboxedPlainObject>().setValue(cx, *property, v)) - return result.succeed(); - - if (!convertToNative(cx, obj)) - return false; - return SetProperty(cx, obj, id, v, receiver, result); - } - - return SetPropertyByDefining(cx, id, v, receiver, result); - } - - if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) { - if (expando->containsShapeOrElement(cx, id)) { - // Update property types on the unboxed object as well. - AddTypePropertyId(cx, obj, id, v); - - RootedObject nexpando(cx, expando); - return SetProperty(cx, nexpando, id, v, receiver, result); - } - } - - return SetPropertyOnProto(cx, obj, id, v, receiver, result); -} - -/* static */ bool -UnboxedPlainObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, - MutableHandle<PropertyDescriptor> desc) -{ - const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout(); - - if (const UnboxedLayout::Property* property = layout.lookup(id)) { - desc.value().set(obj->as<UnboxedPlainObject>().getValue(*property)); - desc.setAttributes(JSPROP_ENUMERATE); - desc.object().set(obj); - return true; - } - - if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) { - if (expando->containsShapeOrElement(cx, id)) { - RootedObject nexpando(cx, expando); - if (!GetOwnPropertyDescriptor(cx, nexpando, id, desc)) - return false; - if (desc.object() == nexpando) - desc.object().set(obj); - return true; - } - } - - desc.object().set(nullptr); - return true; -} - -/* static */ bool -UnboxedPlainObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, - ObjectOpResult& result) -{ - if (!convertToNative(cx, obj)) - return false; - return DeleteProperty(cx, obj, id, result); -} - -/* static */ bool -UnboxedPlainObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable) -{ - if (!convertToNative(cx, obj)) - return false; - return WatchProperty(cx, obj, id, callable); -} - -/* static */ bool -UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, - bool enumerableOnly) -{ - // Ignore expando properties here, they are special-cased by the property - // enumeration code. - - const UnboxedLayout::PropertyVector& unboxed = obj->as<UnboxedPlainObject>().layout().properties(); - for (size_t i = 0; i < unboxed.length(); i++) { - if (!properties.append(NameToId(unboxed[i].name))) - return false; - } - - return true; -} - -const Class UnboxedExpandoObject::class_ = { - "UnboxedExpandoObject", - 0 -}; - -static const ClassOps UnboxedPlainObjectClassOps = { - nullptr, /* addProperty */ - nullptr, /* delProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ - nullptr, /* resolve */ - nullptr, /* mayResolve */ - nullptr, /* finalize */ - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - UnboxedPlainObject::trace, -}; - -static const ObjectOps UnboxedPlainObjectObjectOps = { - UnboxedPlainObject::obj_lookupProperty, - UnboxedPlainObject::obj_defineProperty, - UnboxedPlainObject::obj_hasProperty, - UnboxedPlainObject::obj_getProperty, - UnboxedPlainObject::obj_setProperty, - UnboxedPlainObject::obj_getOwnPropertyDescriptor, - UnboxedPlainObject::obj_deleteProperty, - UnboxedPlainObject::obj_watch, - nullptr, /* No unwatch needed, as watch() converts the object to native */ - nullptr, /* getElements */ - UnboxedPlainObject::obj_enumerate, - nullptr /* funToString */ -}; - -const Class UnboxedPlainObject::class_ = { - js_Object_str, - Class::NON_NATIVE | - JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | - JSCLASS_DELAY_METADATA_BUILDER, - &UnboxedPlainObjectClassOps, - JS_NULL_CLASS_SPEC, - JS_NULL_CLASS_EXT, - &UnboxedPlainObjectObjectOps -}; - -///////////////////////////////////////////////////////////////////// -// UnboxedArrayObject -///////////////////////////////////////////////////////////////////// - -template <JSValueType Type> -DenseElementResult -AppendUnboxedDenseElements(UnboxedArrayObject* obj, uint32_t initlen, - MutableHandle<GCVector<Value>> values) -{ - for (size_t i = 0; i < initlen; i++) - values.infallibleAppend(obj->getElementSpecific<Type>(i)); - return DenseElementResult::Success; -} - -DefineBoxedOrUnboxedFunctor3(AppendUnboxedDenseElements, - UnboxedArrayObject*, uint32_t, MutableHandle<GCVector<Value>>); - -/* static */ bool -UnboxedArrayObject::convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj, - ObjectGroup* group, Shape* shape) -{ - size_t length = obj->as<UnboxedArrayObject>().length(); - size_t initlen = obj->as<UnboxedArrayObject>().initializedLength(); - - Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx)); - if (!values.reserve(initlen)) - return false; - - AppendUnboxedDenseElementsFunctor functor(&obj->as<UnboxedArrayObject>(), initlen, &values); - DebugOnly<DenseElementResult> result = CallBoxedOrUnboxedSpecialization(functor, obj); - MOZ_ASSERT(result.value == DenseElementResult::Success); - - obj->setGroup(group); - - ArrayObject* aobj = &obj->as<ArrayObject>(); - aobj->setLastPropertyMakeNative(cx, shape); - - // Make sure there is at least one element, so that this array does not - // use emptyObjectElements / emptyObjectElementsShared. - if (!aobj->ensureElements(cx, Max<size_t>(initlen, 1))) - return false; - - MOZ_ASSERT(!aobj->getDenseInitializedLength()); - aobj->setDenseInitializedLength(initlen); - aobj->initDenseElements(0, values.begin(), initlen); - aobj->setLengthInt32(length); - - return true; -} - -/* static */ bool -UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj) -{ - const UnboxedLayout& layout = obj->as<UnboxedArrayObject>().layout(); - - if (!layout.nativeGroup()) { - if (!UnboxedLayout::makeNativeGroup(cx, obj->group())) - return false; - } - - return convertToNativeWithGroup(cx, obj, layout.nativeGroup(), layout.nativeShape()); -} - -bool -UnboxedArrayObject::convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group) -{ - MOZ_ASSERT(elementType() == JSVAL_TYPE_INT32); - MOZ_ASSERT(group->unboxedLayout().elementType() == JSVAL_TYPE_DOUBLE); - - Vector<int32_t> values(cx); - if (!values.reserve(initializedLength())) - return false; - for (size_t i = 0; i < initializedLength(); i++) - values.infallibleAppend(getElementSpecific<JSVAL_TYPE_INT32>(i).toInt32()); - - uint8_t* newElements; - if (hasInlineElements()) { - newElements = AllocateObjectBuffer<uint8_t>(cx, this, capacity() * sizeof(double)); - } else { - newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(), - capacity() * sizeof(int32_t), - capacity() * sizeof(double)); - } - if (!newElements) - return false; - - setGroup(group); - elements_ = newElements; - - for (size_t i = 0; i < initializedLength(); i++) - setElementNoTypeChangeSpecific<JSVAL_TYPE_DOUBLE>(i, DoubleValue(values[i])); - - return true; -} - -/* static */ UnboxedArrayObject* -UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32_t length, - NewObjectKind newKind, uint32_t maxLength) -{ - MOZ_ASSERT(length <= MaximumCapacity); - - MOZ_ASSERT(group->clasp() == &class_); - uint32_t elementSize = UnboxedTypeSize(group->unboxedLayout().elementType()); - uint32_t capacity = Min(length, maxLength); - uint32_t nbytes = offsetOfInlineElements() + elementSize * capacity; - - UnboxedArrayObject* res; - if (nbytes <= JSObject::MAX_BYTE_SIZE) { - gc::AllocKind allocKind = gc::GetGCObjectKindForBytes(nbytes); - - // If there was no provided length information, pick an allocation kind - // to accommodate small arrays (as is done for normal native arrays). - if (capacity == 0) - allocKind = gc::AllocKind::OBJECT8; - - res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, allocKind, newKind); - if (!res) - return nullptr; - res->setInitializedLengthNoBarrier(0); - res->setInlineElements(); - - size_t actualCapacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize; - MOZ_ASSERT(actualCapacity >= capacity); - res->setCapacityIndex(exactCapacityIndex(actualCapacity)); - } else { - res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, gc::AllocKind::OBJECT0, newKind); - if (!res) - return nullptr; - res->setInitializedLengthNoBarrier(0); - - uint32_t capacityIndex = (capacity == length) - ? CapacityMatchesLengthIndex - : chooseCapacityIndex(capacity, length); - uint32_t actualCapacity = computeCapacity(capacityIndex, length); - - res->elements_ = AllocateObjectBuffer<uint8_t>(cx, res, actualCapacity * elementSize); - if (!res->elements_) { - // Make the object safe for GC. - res->setInlineElements(); - return nullptr; - } - - res->setCapacityIndex(capacityIndex); - } - - res->setLength(cx, length); - return res; -} - -bool -UnboxedArrayObject::setElement(ExclusiveContext* cx, size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - uint8_t* p = elements() + index * elementSize(); - return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ true); -} - -bool -UnboxedArrayObject::initElement(ExclusiveContext* cx, size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - uint8_t* p = elements() + index * elementSize(); - return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ false); -} - -void -UnboxedArrayObject::initElementNoTypeChange(size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - uint8_t* p = elements() + index * elementSize(); - if (UnboxedTypeNeedsPreBarrier(elementType())) - *reinterpret_cast<void**>(p) = nullptr; - SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ false); -} - -Value -UnboxedArrayObject::getElement(size_t index) -{ - MOZ_ASSERT(index < initializedLength()); - uint8_t* p = elements() + index * elementSize(); - return GetUnboxedValue(p, elementType(), /* maybeUninitialized = */ false); -} - -/* static */ void -UnboxedArrayObject::trace(JSTracer* trc, JSObject* obj) -{ - JSValueType type = obj->as<UnboxedArrayObject>().elementType(); - if (!UnboxedTypeNeedsPreBarrier(type)) - return; - - MOZ_ASSERT(obj->as<UnboxedArrayObject>().elementSize() == sizeof(uintptr_t)); - size_t initlen = obj->as<UnboxedArrayObject>().initializedLength(); - void** elements = reinterpret_cast<void**>(obj->as<UnboxedArrayObject>().elements()); - - switch (type) { - case JSVAL_TYPE_OBJECT: - for (size_t i = 0; i < initlen; i++) { - GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(elements + i); - TraceNullableEdge(trc, heap, "unboxed_object"); - } - break; - - case JSVAL_TYPE_STRING: - for (size_t i = 0; i < initlen; i++) { - GCPtrString* heap = reinterpret_cast<GCPtrString*>(elements + i); - TraceEdge(trc, heap, "unboxed_string"); - } - break; - - default: - MOZ_CRASH(); - } -} - -/* static */ void -UnboxedArrayObject::objectMoved(JSObject* obj, const JSObject* old) -{ - UnboxedArrayObject& dst = obj->as<UnboxedArrayObject>(); - const UnboxedArrayObject& src = old->as<UnboxedArrayObject>(); - - // Fix up possible inline data pointer. - if (src.hasInlineElements()) - dst.setInlineElements(); -} - -/* static */ void -UnboxedArrayObject::finalize(FreeOp* fop, JSObject* obj) -{ - MOZ_ASSERT(!IsInsideNursery(obj)); - if (!obj->as<UnboxedArrayObject>().hasInlineElements()) - js_free(obj->as<UnboxedArrayObject>().elements()); -} - -/* static */ size_t -UnboxedArrayObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src, - gc::AllocKind allocKind) -{ - UnboxedArrayObject* ndst = &dst->as<UnboxedArrayObject>(); - UnboxedArrayObject* nsrc = &src->as<UnboxedArrayObject>(); - MOZ_ASSERT(ndst->elements() == nsrc->elements()); - - Nursery& nursery = trc->runtime()->gc.nursery; - - if (!nursery.isInside(nsrc->elements())) { - nursery.removeMallocedBuffer(nsrc->elements()); - return 0; - } - - // Determine if we can use inline data for the target array. If this is - // possible, the nursery will have picked an allocation size that is large - // enough. - size_t nbytes = nsrc->capacity() * nsrc->elementSize(); - if (offsetOfInlineElements() + nbytes <= GetGCKindBytes(allocKind)) { - ndst->setInlineElements(); - } else { - MOZ_ASSERT(allocKind == gc::AllocKind::OBJECT0); - - AutoEnterOOMUnsafeRegion oomUnsafe; - uint8_t* data = nsrc->zone()->pod_malloc<uint8_t>(nbytes); - if (!data) - oomUnsafe.crash("Failed to allocate unboxed array elements while tenuring."); - ndst->elements_ = data; - } - - PodCopy(ndst->elements(), nsrc->elements(), nsrc->initializedLength() * nsrc->elementSize()); - - // Set a forwarding pointer for the element buffers in case they were - // preserved on the stack by Ion. - bool direct = nsrc->capacity() * nsrc->elementSize() >= sizeof(uintptr_t); - nursery.maybeSetForwardingPointer(trc, nsrc->elements(), ndst->elements(), direct); - - return ndst->hasInlineElements() ? 0 : nbytes; -} - -// Possible capacities for unboxed arrays. Some of these capacities might seem -// a little weird, but were chosen to allow the inline data of objects of each -// size to be fully utilized for arrays of the various types on both 32 bit and -// 64 bit platforms. -// -// To find the possible inline capacities, the following script was used: -// -// var fixedSlotCapacities = [0, 2, 4, 8, 12, 16]; -// var dataSizes = [1, 4, 8]; -// var header32 = 4 * 2 + 4 * 2; -// var header64 = 8 * 2 + 4 * 2; -// -// for (var i = 0; i < fixedSlotCapacities.length; i++) { -// var nfixed = fixedSlotCapacities[i]; -// var size32 = 4 * 4 + 8 * nfixed - header32; -// var size64 = 8 * 4 + 8 * nfixed - header64; -// for (var j = 0; j < dataSizes.length; j++) { -// print(size32 / dataSizes[j]); -// print(size64 / dataSizes[j]); -// } -// } -// -/* static */ const uint32_t -UnboxedArrayObject::CapacityArray[] = { - UINT32_MAX, // For CapacityMatchesLengthIndex. - 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 13, 16, 17, 18, 24, 26, 32, 34, 40, 64, 72, 96, 104, 128, 136, - 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, - 1048576, 2097152, 3145728, 4194304, 5242880, 6291456, 7340032, 8388608, 9437184, 11534336, - 13631488, 15728640, 17825792, 20971520, 24117248, 27262976, 31457280, 35651584, 40894464, - 46137344, 52428800, 59768832, MaximumCapacity -}; - -static const uint32_t -Pow2CapacityIndexes[] = { - 2, // 1 - 3, // 2 - 5, // 4 - 8, // 8 - 13, // 16 - 18, // 32 - 21, // 64 - 25, // 128 - 27, // 256 - 28, // 512 - 29, // 1024 - 30, // 2048 - 31, // 4096 - 32, // 8192 - 33, // 16384 - 34, // 32768 - 35, // 65536 - 36, // 131072 - 37, // 262144 - 38, // 524288 - 39 // 1048576 -}; - -static const uint32_t MebiCapacityIndex = 39; - -/* static */ uint32_t -UnboxedArrayObject::chooseCapacityIndex(uint32_t capacity, uint32_t length) -{ - // Note: the structure and behavior of this method follow along with - // NativeObject::goodAllocated. Changes to the allocation strategy in one - // should generally be matched by the other. - - // Make sure we have enough space to store all possible values for the capacity index. - // This ought to be a static_assert, but MSVC doesn't like that. - MOZ_ASSERT(mozilla::ArrayLength(CapacityArray) - 1 <= (CapacityMask >> CapacityShift)); - - // The caller should have ensured the capacity is possible for an unboxed array. - MOZ_ASSERT(capacity <= MaximumCapacity); - - static const uint32_t Mebi = 1024 * 1024; - - if (capacity <= Mebi) { - capacity = mozilla::RoundUpPow2(capacity); - - // When the required capacity is close to the array length, then round - // up to the array length itself, as for NativeObject. - if (length >= capacity && capacity > (length / 3) * 2) - return CapacityMatchesLengthIndex; - - if (capacity < MinimumDynamicCapacity) - capacity = MinimumDynamicCapacity; - - uint32_t bit = mozilla::FloorLog2Size(capacity); - MOZ_ASSERT(capacity == uint32_t(1 << bit)); - MOZ_ASSERT(bit <= 20); - MOZ_ASSERT(mozilla::ArrayLength(Pow2CapacityIndexes) == 21); - - uint32_t index = Pow2CapacityIndexes[bit]; - MOZ_ASSERT(CapacityArray[index] == capacity); - - return index; - } - - MOZ_ASSERT(CapacityArray[MebiCapacityIndex] == Mebi); - - for (uint32_t i = MebiCapacityIndex + 1;; i++) { - if (CapacityArray[i] >= capacity) - return i; - } - - MOZ_CRASH("Invalid capacity"); -} - -/* static */ uint32_t -UnboxedArrayObject::exactCapacityIndex(uint32_t capacity) -{ - for (size_t i = CapacityMatchesLengthIndex + 1; i < ArrayLength(CapacityArray); i++) { - if (CapacityArray[i] == capacity) - return i; - } - MOZ_CRASH(); -} - -bool -UnboxedArrayObject::growElements(ExclusiveContext* cx, size_t cap) -{ - // The caller should have checked if this capacity is possible for an - // unboxed array, so the only way this call can fail is from OOM. - MOZ_ASSERT(cap <= MaximumCapacity); - - uint32_t oldCapacity = capacity(); - uint32_t newCapacityIndex = chooseCapacityIndex(cap, length()); - uint32_t newCapacity = computeCapacity(newCapacityIndex, length()); - - MOZ_ASSERT(oldCapacity < cap); - MOZ_ASSERT(cap <= newCapacity); - - // The allocation size computation below cannot have integer overflows. - JS_STATIC_ASSERT(MaximumCapacity < UINT32_MAX / sizeof(double)); - - uint8_t* newElements; - if (hasInlineElements()) { - newElements = AllocateObjectBuffer<uint8_t>(cx, this, newCapacity * elementSize()); - if (!newElements) - return false; - js_memcpy(newElements, elements(), initializedLength() * elementSize()); - } else { - newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(), - oldCapacity * elementSize(), - newCapacity * elementSize()); - if (!newElements) - return false; - } - - elements_ = newElements; - setCapacityIndex(newCapacityIndex); - - return true; -} - -void -UnboxedArrayObject::shrinkElements(ExclusiveContext* cx, size_t cap) -{ - if (hasInlineElements()) - return; - - uint32_t oldCapacity = capacity(); - uint32_t newCapacityIndex = chooseCapacityIndex(cap, 0); - uint32_t newCapacity = computeCapacity(newCapacityIndex, 0); - - MOZ_ASSERT(cap < oldCapacity); - MOZ_ASSERT(cap <= newCapacity); - - if (newCapacity >= oldCapacity) - return; - - uint8_t* newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(), - oldCapacity * elementSize(), - newCapacity * elementSize()); - if (!newElements) - return; - - elements_ = newElements; - setCapacityIndex(newCapacityIndex); -} - -bool -UnboxedArrayObject::containsProperty(ExclusiveContext* cx, jsid id) -{ - if (JSID_IS_INT(id) && uint32_t(JSID_TO_INT(id)) < initializedLength()) - return true; - if (JSID_IS_ATOM(id) && JSID_TO_ATOM(id) == cx->names().length) - return true; - return false; -} - -/* static */ bool -UnboxedArrayObject::obj_lookupProperty(JSContext* cx, HandleObject obj, - HandleId id, MutableHandleObject objp, - MutableHandleShape propp) -{ - if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) { - MarkNonNativePropertyFound<CanGC>(propp); - objp.set(obj); - return true; - } - - RootedObject proto(cx, obj->staticPrototype()); - if (!proto) { - objp.set(nullptr); - propp.set(nullptr); - return true; - } - - return LookupProperty(cx, proto, id, objp, propp); -} - -/* static */ bool -UnboxedArrayObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle<PropertyDescriptor> desc, - ObjectOpResult& result) -{ - if (JSID_IS_INT(id) && !desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) { - UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>(); - - uint32_t index = JSID_TO_INT(id); - if (index < nobj->initializedLength()) { - if (nobj->setElement(cx, index, desc.value())) - return result.succeed(); - } else if (index == nobj->initializedLength() && index < MaximumCapacity) { - if (nobj->initializedLength() == nobj->capacity()) { - if (!nobj->growElements(cx, index + 1)) - return false; - } - nobj->setInitializedLength(index + 1); - if (nobj->initElement(cx, index, desc.value())) { - if (nobj->length() <= index) - nobj->setLengthInt32(index + 1); - return result.succeed(); - } - nobj->setInitializedLengthNoBarrier(index); - } - } - - if (!convertToNative(cx, obj)) - return false; - - return DefineProperty(cx, obj, id, desc, result); -} - -/* static */ bool -UnboxedArrayObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp) -{ - if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) { - *foundp = true; - return true; - } - - RootedObject proto(cx, obj->staticPrototype()); - if (!proto) { - *foundp = false; - return true; - } - - return HasProperty(cx, proto, id, foundp); -} - -/* static */ bool -UnboxedArrayObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver, - HandleId id, MutableHandleValue vp) -{ - if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) { - if (JSID_IS_INT(id)) - vp.set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id))); - else - vp.set(Int32Value(obj->as<UnboxedArrayObject>().length())); - return true; - } - - RootedObject proto(cx, obj->staticPrototype()); - if (!proto) { - vp.setUndefined(); - return true; - } - - return GetProperty(cx, proto, receiver, id, vp); -} - -/* static */ bool -UnboxedArrayObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult& result) -{ - if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) { - if (receiver.isObject() && obj == &receiver.toObject()) { - if (JSID_IS_INT(id)) { - if (obj->as<UnboxedArrayObject>().setElement(cx, JSID_TO_INT(id), v)) - return result.succeed(); - } else { - uint32_t len; - if (!CanonicalizeArrayLengthValue(cx, v, &len)) - return false; - UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>(); - if (len < nobj->initializedLength()) { - nobj->setInitializedLength(len); - nobj->shrinkElements(cx, len); - } - nobj->setLength(cx, len); - return result.succeed(); - } - - if (!convertToNative(cx, obj)) - return false; - return SetProperty(cx, obj, id, v, receiver, result); - } - - return SetPropertyByDefining(cx, id, v, receiver, result); - } - - return SetPropertyOnProto(cx, obj, id, v, receiver, result); -} - -/* static */ bool -UnboxedArrayObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, - MutableHandle<PropertyDescriptor> desc) -{ - if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) { - if (JSID_IS_INT(id)) { - desc.value().set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id))); - desc.setAttributes(JSPROP_ENUMERATE); - } else { - desc.value().set(Int32Value(obj->as<UnboxedArrayObject>().length())); - desc.setAttributes(JSPROP_PERMANENT); - } - desc.object().set(obj); - return true; - } - - desc.object().set(nullptr); - return true; -} - -/* static */ bool -UnboxedArrayObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, - ObjectOpResult& result) -{ - if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) { - size_t initlen = obj->as<UnboxedArrayObject>().initializedLength(); - if (JSID_IS_INT(id) && JSID_TO_INT(id) == int32_t(initlen - 1)) { - obj->as<UnboxedArrayObject>().setInitializedLength(initlen - 1); - obj->as<UnboxedArrayObject>().shrinkElements(cx, initlen - 1); - return result.succeed(); - } - } - - if (!convertToNative(cx, obj)) - return false; - return DeleteProperty(cx, obj, id, result); -} - -/* static */ bool -UnboxedArrayObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable) -{ - if (!convertToNative(cx, obj)) - return false; - return WatchProperty(cx, obj, id, callable); -} - -/* static */ bool -UnboxedArrayObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, - bool enumerableOnly) -{ - for (size_t i = 0; i < obj->as<UnboxedArrayObject>().initializedLength(); i++) { - if (!properties.append(INT_TO_JSID(i))) - return false; - } - - if (!enumerableOnly && !properties.append(NameToId(cx->names().length))) - return false; - - return true; -} - -static const ClassOps UnboxedArrayObjectClassOps = { - nullptr, /* addProperty */ - nullptr, /* delProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ - nullptr, /* resolve */ - nullptr, /* mayResolve */ - UnboxedArrayObject::finalize, - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - UnboxedArrayObject::trace, -}; - -static const ClassExtension UnboxedArrayObjectClassExtension = { - nullptr, /* weakmapKeyDelegateOp */ - UnboxedArrayObject::objectMoved -}; - -static const ObjectOps UnboxedArrayObjectObjectOps = { - UnboxedArrayObject::obj_lookupProperty, - UnboxedArrayObject::obj_defineProperty, - UnboxedArrayObject::obj_hasProperty, - UnboxedArrayObject::obj_getProperty, - UnboxedArrayObject::obj_setProperty, - UnboxedArrayObject::obj_getOwnPropertyDescriptor, - UnboxedArrayObject::obj_deleteProperty, - UnboxedArrayObject::obj_watch, - nullptr, /* No unwatch needed, as watch() converts the object to native */ - nullptr, /* getElements */ - UnboxedArrayObject::obj_enumerate, - nullptr /* funToString */ -}; - -const Class UnboxedArrayObject::class_ = { - "Array", - Class::NON_NATIVE | - JSCLASS_SKIP_NURSERY_FINALIZE | - JSCLASS_BACKGROUND_FINALIZE, - &UnboxedArrayObjectClassOps, - JS_NULL_CLASS_SPEC, - &UnboxedArrayObjectClassExtension, - &UnboxedArrayObjectObjectOps -}; - -///////////////////////////////////////////////////////////////////// -// API -///////////////////////////////////////////////////////////////////// - -static bool -UnboxedTypeIncludes(JSValueType supertype, JSValueType subtype) -{ - if (supertype == JSVAL_TYPE_DOUBLE && subtype == JSVAL_TYPE_INT32) - return true; - if (supertype == JSVAL_TYPE_OBJECT && subtype == JSVAL_TYPE_NULL) - return true; - return false; -} - -static bool -CombineUnboxedTypes(const Value& value, JSValueType* existing) -{ - JSValueType type = value.isDouble() ? JSVAL_TYPE_DOUBLE : value.extractNonDoubleType(); - - if (*existing == JSVAL_TYPE_MAGIC || *existing == type || UnboxedTypeIncludes(type, *existing)) { - *existing = type; - return true; - } - if (UnboxedTypeIncludes(*existing, type)) - return true; - return false; -} - -// Return whether the property names and types in layout are a subset of the -// specified vector. -static bool -PropertiesAreSuperset(const UnboxedLayout::PropertyVector& properties, UnboxedLayout* layout) -{ - for (size_t i = 0; i < layout->properties().length(); i++) { - const UnboxedLayout::Property& layoutProperty = layout->properties()[i]; - bool found = false; - for (size_t j = 0; j < properties.length(); j++) { - if (layoutProperty.name == properties[j].name) { - found = (layoutProperty.type == properties[j].type); - break; - } - } - if (!found) - return false; - } - return true; -} - -static bool -CombinePlainObjectProperties(PlainObject* obj, Shape* templateShape, - UnboxedLayout::PropertyVector& properties) -{ - // All preliminary objects must have been created with enough space to - // fill in their unboxed data inline. This is ensured either by using - // the largest allocation kind (which limits the maximum size of an - // unboxed object), or by using an allocation kind that covers all - // properties in the template, as the space used by unboxed properties - // is less than or equal to that used by boxed properties. - MOZ_ASSERT(gc::GetGCKindSlots(obj->asTenured().getAllocKind()) >= - Min(NativeObject::MAX_FIXED_SLOTS, templateShape->slotSpan())); - - if (obj->lastProperty() != templateShape || obj->hasDynamicElements()) { - // Only use an unboxed representation if all created objects match - // the template shape exactly. - return false; - } - - for (size_t i = 0; i < templateShape->slotSpan(); i++) { - Value val = obj->getSlot(i); - - JSValueType& existing = properties[i].type; - if (!CombineUnboxedTypes(val, &existing)) - return false; - } - - return true; -} - -static bool -CombineArrayObjectElements(ExclusiveContext* cx, ArrayObject* obj, JSValueType* elementType) -{ - if (obj->inDictionaryMode() || - obj->lastProperty()->propid() != AtomToId(cx->names().length) || - !obj->lastProperty()->previous()->isEmptyShape()) - { - // Only use an unboxed representation if the object has no properties. - return false; - } - - for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) { - Value val = obj->getDenseElement(i); - - // For now, unboxed arrays cannot have holes. - if (val.isMagic(JS_ELEMENTS_HOLE)) - return false; - - if (!CombineUnboxedTypes(val, elementType)) - return false; - } - - return true; -} - -static size_t -ComputePlainObjectLayout(ExclusiveContext* cx, Shape* templateShape, - UnboxedLayout::PropertyVector& properties) -{ - // Fill in the names for all the object's properties. - for (Shape::Range<NoGC> r(templateShape); !r.empty(); r.popFront()) { - size_t slot = r.front().slot(); - MOZ_ASSERT(!properties[slot].name); - properties[slot].name = JSID_TO_ATOM(r.front().propid())->asPropertyName(); - } - - // Fill in all the unboxed object's property offsets. - uint32_t offset = 0; - - // Search for an existing unboxed layout which is a subset of this one. - // If there are multiple such layouts, use the largest one. If we're able - // to find such a layout, use the same property offsets for the shared - // properties, which will allow us to generate better code if the objects - // have a subtype/supertype relation and are accessed at common sites. - UnboxedLayout* bestExisting = nullptr; - for (UnboxedLayout* existing : cx->compartment()->unboxedLayouts) { - if (PropertiesAreSuperset(properties, existing)) { - if (!bestExisting || - existing->properties().length() > bestExisting->properties().length()) - { - bestExisting = existing; - } - } - } - if (bestExisting) { - for (size_t i = 0; i < bestExisting->properties().length(); i++) { - const UnboxedLayout::Property& existingProperty = bestExisting->properties()[i]; - for (size_t j = 0; j < templateShape->slotSpan(); j++) { - if (existingProperty.name == properties[j].name) { - MOZ_ASSERT(existingProperty.type == properties[j].type); - properties[j].offset = existingProperty.offset; - } - } - } - offset = bestExisting->size(); - } - - // Order remaining properties from the largest down for the best space - // utilization. - static const size_t typeSizes[] = { 8, 4, 1 }; - - for (size_t i = 0; i < ArrayLength(typeSizes); i++) { - size_t size = typeSizes[i]; - for (size_t j = 0; j < templateShape->slotSpan(); j++) { - if (properties[j].offset != UINT32_MAX) - continue; - JSValueType type = properties[j].type; - if (UnboxedTypeSize(type) == size) { - offset = JS_ROUNDUP(offset, size); - properties[j].offset = offset; - offset += size; - } - } - } - - // The final offset is the amount of data needed by the object. - return offset; -} - -static bool -SetLayoutTraceList(ExclusiveContext* cx, UnboxedLayout* layout) -{ - // Figure out the offsets of any objects or string properties. - Vector<int32_t, 8, SystemAllocPolicy> objectOffsets, stringOffsets; - for (size_t i = 0; i < layout->properties().length(); i++) { - const UnboxedLayout::Property& property = layout->properties()[i]; - MOZ_ASSERT(property.offset != UINT32_MAX); - if (property.type == JSVAL_TYPE_OBJECT) { - if (!objectOffsets.append(property.offset)) - return false; - } else if (property.type == JSVAL_TYPE_STRING) { - if (!stringOffsets.append(property.offset)) - return false; - } - } - - // Construct the layout's trace list. - if (!objectOffsets.empty() || !stringOffsets.empty()) { - Vector<int32_t, 8, SystemAllocPolicy> entries; - if (!entries.appendAll(stringOffsets) || - !entries.append(-1) || - !entries.appendAll(objectOffsets) || - !entries.append(-1) || - !entries.append(-1)) - { - return false; - } - int32_t* traceList = cx->zone()->pod_malloc<int32_t>(entries.length()); - if (!traceList) - return false; - PodCopy(traceList, entries.begin(), entries.length()); - layout->setTraceList(traceList); - } - - return true; -} - -static inline Value -NextValue(Handle<GCVector<Value>> values, size_t* valueCursor) -{ - return values[(*valueCursor)++]; -} - -static bool -GetValuesFromPreliminaryArrayObject(ArrayObject* obj, MutableHandle<GCVector<Value>> values) -{ - if (!values.append(Int32Value(obj->length()))) - return false; - if (!values.append(Int32Value(obj->getDenseInitializedLength()))) - return false; - for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) { - if (!values.append(obj->getDenseElement(i))) - return false; - } - return true; -} - -void -UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx, - Handle<GCVector<Value>> values, size_t* valueCursor) -{ - MOZ_ASSERT(CapacityArray[1] == 0); - setCapacityIndex(1); - setInitializedLengthNoBarrier(0); - setInlineElements(); - - setLength(cx, NextValue(values, valueCursor).toInt32()); - - int32_t initlen = NextValue(values, valueCursor).toInt32(); - if (!initlen) - return; - - AutoEnterOOMUnsafeRegion oomUnsafe; - if (!growElements(cx, initlen)) - oomUnsafe.crash("UnboxedArrayObject::fillAfterConvert"); - - setInitializedLength(initlen); - - for (size_t i = 0; i < size_t(initlen); i++) - JS_ALWAYS_TRUE(initElement(cx, i, NextValue(values, valueCursor))); -} - -static bool -GetValuesFromPreliminaryPlainObject(PlainObject* obj, MutableHandle<GCVector<Value>> values) -{ - for (size_t i = 0; i < obj->slotSpan(); i++) { - if (!values.append(obj->getSlot(i))) - return false; - } - return true; -} - -void -UnboxedPlainObject::fillAfterConvert(ExclusiveContext* cx, - Handle<GCVector<Value>> values, size_t* valueCursor) -{ - initExpando(); - memset(data(), 0, layout().size()); - for (size_t i = 0; i < layout().properties().length(); i++) - JS_ALWAYS_TRUE(setValue(cx, layout().properties()[i], NextValue(values, valueCursor))); -} - -bool -js::TryConvertToUnboxedLayout(ExclusiveContext* cx, AutoEnterAnalysis& enter, Shape* templateShape, - ObjectGroup* group, PreliminaryObjectArray* objects) -{ - bool isArray = !templateShape; - - // Unboxed arrays are nightly only for now. The getenv() call will be - // removed when they are on by default. See bug 1153266. - if (isArray) { -#ifdef NIGHTLY_BUILD - if (!getenv("JS_OPTION_USE_UNBOXED_ARRAYS")) { - if (!cx->options().unboxedArrays()) - return true; - } -#else - return true; -#endif - } else { - if (jit::JitOptions.disableUnboxedObjects) - return true; - } - - MOZ_ASSERT_IF(templateShape, !templateShape->getObjectFlags()); - - if (group->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global())) - return true; - - if (!isArray && templateShape->slotSpan() == 0) - return true; - - UnboxedLayout::PropertyVector properties; - if (!isArray) { - if (!properties.appendN(UnboxedLayout::Property(), templateShape->slotSpan())) - return false; - } - JSValueType elementType = JSVAL_TYPE_MAGIC; - - size_t objectCount = 0; - for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - JSObject* obj = objects->get(i); - if (!obj) - continue; - - if (obj->isSingleton() || obj->group() != group) - return true; - - objectCount++; - - if (isArray) { - if (!CombineArrayObjectElements(cx, &obj->as<ArrayObject>(), &elementType)) - return true; - } else { - if (!CombinePlainObjectProperties(&obj->as<PlainObject>(), templateShape, properties)) - return true; - } - } - - size_t layoutSize = 0; - if (isArray) { - // Don't use an unboxed representation if we couldn't determine an - // element type for the objects. - if (UnboxedTypeSize(elementType) == 0) - return true; - } else { - if (objectCount <= 1) { - // If only one of the objects has been created, it is more likely - // to have new properties added later. This heuristic is not used - // for array objects, where we might want an unboxed representation - // even if there is only one large array. - return true; - } - - for (size_t i = 0; i < templateShape->slotSpan(); i++) { - // We can't use an unboxed representation if e.g. all the objects have - // a null value for one of the properties, as we can't decide what type - // it is supposed to have. - if (UnboxedTypeSize(properties[i].type) == 0) - return true; - } - - // Make sure that all properties on the template shape are property - // names, and not indexes. - for (Shape::Range<NoGC> r(templateShape); !r.empty(); r.popFront()) { - jsid id = r.front().propid(); - uint32_t dummy; - if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&dummy)) - return true; - } - - layoutSize = ComputePlainObjectLayout(cx, templateShape, properties); - - // The entire object must be allocatable inline. - if (UnboxedPlainObject::offsetOfData() + layoutSize > JSObject::MAX_BYTE_SIZE) - return true; - } - - UniquePtr<UnboxedLayout>& layout = enter.unboxedLayoutToCleanUp; - MOZ_ASSERT(!layout); - layout = group->zone()->make_unique<UnboxedLayout>(); - if (!layout) - return false; - - if (isArray) { - layout->initArray(elementType); - } else { - if (!layout->initProperties(properties, layoutSize)) - return false; - - // The unboxedLayouts list only tracks layouts for plain objects. - cx->compartment()->unboxedLayouts.insertFront(layout.get()); - - if (!SetLayoutTraceList(cx, layout.get())) - return false; - } - - // We've determined that all the preliminary objects can use the new layout - // just constructed, so convert the existing group to use the unboxed class, - // and update the preliminary objects to use the new layout. Do the - // fallible stuff first before modifying any objects. - - // Get an empty shape which we can use for the preliminary objects. - const Class* clasp = isArray ? &UnboxedArrayObject::class_ : &UnboxedPlainObject::class_; - Shape* newShape = EmptyShape::getInitialShape(cx, clasp, group->proto(), 0); - if (!newShape) { - cx->recoverFromOutOfMemory(); - return false; - } - - // Accumulate a list of all the values in each preliminary object, and - // update their shapes. - Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx)); - for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - JSObject* obj = objects->get(i); - if (!obj) - continue; - - bool ok; - if (isArray) - ok = GetValuesFromPreliminaryArrayObject(&obj->as<ArrayObject>(), &values); - else - ok = GetValuesFromPreliminaryPlainObject(&obj->as<PlainObject>(), &values); - - if (!ok) { - cx->recoverFromOutOfMemory(); - return false; - } - } - - if (TypeNewScript* newScript = group->newScript()) - layout->setNewScript(newScript); - - for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - if (JSObject* obj = objects->get(i)) - obj->as<NativeObject>().setLastPropertyMakeNonNative(newShape); - } - - group->setClasp(clasp); - group->setUnboxedLayout(layout.release()); - - size_t valueCursor = 0; - for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - JSObject* obj = objects->get(i); - if (!obj) - continue; - - if (isArray) - obj->as<UnboxedArrayObject>().fillAfterConvert(cx, values, &valueCursor); - else - obj->as<UnboxedPlainObject>().fillAfterConvert(cx, values, &valueCursor); - } - - MOZ_ASSERT(valueCursor == values.length()); - return true; -} - -DefineBoxedOrUnboxedFunctor6(SetOrExtendBoxedOrUnboxedDenseElements, - ExclusiveContext*, JSObject*, uint32_t, const Value*, uint32_t, - ShouldUpdateTypes); - -DenseElementResult -js::SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj, - uint32_t start, const Value* vp, uint32_t count, - ShouldUpdateTypes updateTypes) -{ - SetOrExtendBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, start, vp, count, updateTypes); - return CallBoxedOrUnboxedSpecialization(functor, obj); -}; - -DefineBoxedOrUnboxedFunctor5(MoveBoxedOrUnboxedDenseElements, - JSContext*, JSObject*, uint32_t, uint32_t, uint32_t); - -DenseElementResult -js::MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, - uint32_t dstStart, uint32_t srcStart, uint32_t length) -{ - MoveBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, dstStart, srcStart, length); - return CallBoxedOrUnboxedSpecialization(functor, obj); -} - -DefineBoxedOrUnboxedFunctorPair6(CopyBoxedOrUnboxedDenseElements, - JSContext*, JSObject*, JSObject*, uint32_t, uint32_t, uint32_t); - -DenseElementResult -js::CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src, - uint32_t dstStart, uint32_t srcStart, uint32_t length) -{ - CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, dstStart, srcStart, length); - return CallBoxedOrUnboxedSpecialization(functor, dst, src); -} - -DefineBoxedOrUnboxedFunctor3(SetBoxedOrUnboxedInitializedLength, - JSContext*, JSObject*, size_t); - -void -js::SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen) -{ - SetBoxedOrUnboxedInitializedLengthFunctor functor(cx, obj, initlen); - JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success); -} - -DefineBoxedOrUnboxedFunctor3(EnsureBoxedOrUnboxedDenseElements, - JSContext*, JSObject*, size_t); - -DenseElementResult -js::EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t initlen) -{ - EnsureBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, initlen); - return CallBoxedOrUnboxedSpecialization(functor, obj); -} diff --git a/js/src/vm/UnboxedObject.h b/js/src/vm/UnboxedObject.h deleted file mode 100644 index ecff8be5b1..0000000000 --- a/js/src/vm/UnboxedObject.h +++ /dev/null @@ -1,531 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef vm_UnboxedObject_h -#define vm_UnboxedObject_h - -#include "jsgc.h" -#include "jsobj.h" - -#include "vm/Runtime.h" -#include "vm/TypeInference.h" - -namespace js { - -// Memory required for an unboxed value of a given type. Returns zero for types -// which can't be used for unboxed objects. -static inline size_t -UnboxedTypeSize(JSValueType type) -{ - switch (type) { - case JSVAL_TYPE_BOOLEAN: return 1; - case JSVAL_TYPE_INT32: return 4; - case JSVAL_TYPE_DOUBLE: return 8; - case JSVAL_TYPE_STRING: return sizeof(void*); - case JSVAL_TYPE_OBJECT: return sizeof(void*); - default: return 0; - } -} - -static inline bool -UnboxedTypeNeedsPreBarrier(JSValueType type) -{ - return type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_OBJECT; -} - -static inline bool -UnboxedTypeNeedsPostBarrier(JSValueType type) -{ - return type == JSVAL_TYPE_OBJECT; -} - -// Class tracking information specific to unboxed objects. -class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout> -{ - public: - struct Property { - PropertyName* name; - uint32_t offset; - JSValueType type; - - Property() - : name(nullptr), offset(UINT32_MAX), type(JSVAL_TYPE_MAGIC) - {} - }; - - typedef Vector<Property, 0, SystemAllocPolicy> PropertyVector; - - private: - // If objects in this group have ever been converted to native objects, - // these store the corresponding native group and initial shape for such - // objects. Type information for this object is reflected in nativeGroup. - GCPtrObjectGroup nativeGroup_; - GCPtrShape nativeShape_; - - // Any script/pc which the associated group is created for. - GCPtrScript allocationScript_; - jsbytecode* allocationPc_; - - // If nativeGroup is set and this object originally had a TypeNewScript or - // was keyed to an allocation site, this points to the group which replaced - // this one. This link is only needed to keep the replacement group from - // being GC'ed. If it were GC'ed and a new one regenerated later, that new - // group might have a different allocation kind from this group. - GCPtrObjectGroup replacementGroup_; - - // The following members are only used for unboxed plain objects. - - // All properties on objects with this layout, in enumeration order. - PropertyVector properties_; - - // Byte size of the data for objects with this layout. - size_t size_; - - // Any 'new' script information associated with this layout. - TypeNewScript* newScript_; - - // List for use in tracing objects with this layout. This has the same - // structure as the trace list on a TypeDescr. - int32_t* traceList_; - - // If this layout has been used to construct script or JSON constant - // objects, this code might be filled in to more quickly fill in objects - // from an array of values. - GCPtrJitCode constructorCode_; - - // The following members are only used for unboxed arrays. - - // The type of array elements. - JSValueType elementType_; - - public: - UnboxedLayout() - : nativeGroup_(nullptr), nativeShape_(nullptr), - allocationScript_(nullptr), allocationPc_(nullptr), replacementGroup_(nullptr), - size_(0), newScript_(nullptr), traceList_(nullptr), constructorCode_(nullptr), - elementType_(JSVAL_TYPE_MAGIC) - {} - - bool initProperties(const PropertyVector& properties, size_t size) { - size_ = size; - return properties_.appendAll(properties); - } - - void initArray(JSValueType elementType) { - elementType_ = elementType; - } - - ~UnboxedLayout() { - if (newScript_) - newScript_->clear(); - js_delete(newScript_); - js_free(traceList_); - - nativeGroup_.init(nullptr); - nativeShape_.init(nullptr); - replacementGroup_.init(nullptr); - constructorCode_.init(nullptr); - } - - bool isArray() const { - return elementType_ != JSVAL_TYPE_MAGIC; - } - - void detachFromCompartment(); - - const PropertyVector& properties() const { - return properties_; - } - - TypeNewScript* newScript() const { - return newScript_; - } - - void setNewScript(TypeNewScript* newScript, bool writeBarrier = true); - - JSScript* allocationScript() const { - return allocationScript_; - } - - jsbytecode* allocationPc() const { - return allocationPc_; - } - - void setAllocationSite(JSScript* script, jsbytecode* pc) { - allocationScript_ = script; - allocationPc_ = pc; - } - - const int32_t* traceList() const { - return traceList_; - } - - void setTraceList(int32_t* traceList) { - traceList_ = traceList; - } - - const Property* lookup(JSAtom* atom) const { - for (size_t i = 0; i < properties_.length(); i++) { - if (properties_[i].name == atom) - return &properties_[i]; - } - return nullptr; - } - - const Property* lookup(jsid id) const { - if (JSID_IS_STRING(id)) - return lookup(JSID_TO_ATOM(id)); - return nullptr; - } - - size_t size() const { - return size_; - } - - ObjectGroup* nativeGroup() const { - return nativeGroup_; - } - - Shape* nativeShape() const { - return nativeShape_; - } - - jit::JitCode* constructorCode() const { - return constructorCode_; - } - - void setConstructorCode(jit::JitCode* code) { - constructorCode_ = code; - } - - JSValueType elementType() const { - return elementType_; - } - - inline gc::AllocKind getAllocKind() const; - - void trace(JSTracer* trc); - - size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); - - static bool makeNativeGroup(JSContext* cx, ObjectGroup* group); - static bool makeConstructorCode(JSContext* cx, HandleObjectGroup group); -}; - -// Class for expando objects holding extra properties given to an unboxed plain -// object. These objects behave identically to normal native plain objects, and -// have a separate Class to distinguish them for memory usage reporting. -class UnboxedExpandoObject : public NativeObject -{ - public: - static const Class class_; -}; - -// Class for a plain object using an unboxed representation. The physical -// layout of these objects is identical to that of an InlineTypedObject, though -// these objects use an UnboxedLayout instead of a TypeDescr to keep track of -// how their properties are stored. -class UnboxedPlainObject : public JSObject -{ - // Optional object which stores extra properties on this object. This is - // not automatically barriered to avoid problems if the object is converted - // to a native. See ensureExpando(). - UnboxedExpandoObject* expando_; - - // Start of the inline data, which immediately follows the group and extra properties. - uint8_t data_[1]; - - public: - static const Class class_; - - static bool obj_lookupProperty(JSContext* cx, HandleObject obj, - HandleId id, MutableHandleObject objp, - MutableHandleShape propp); - - static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle<PropertyDescriptor> desc, - ObjectOpResult& result); - - static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp); - - static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver, - HandleId id, MutableHandleValue vp); - - static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult& result); - - static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, - MutableHandle<PropertyDescriptor> desc); - - static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, - ObjectOpResult& result); - - static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, - bool enumerableOnly); - static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable); - - inline const UnboxedLayout& layout() const; - - const UnboxedLayout& layoutDontCheckGeneration() const { - return group()->unboxedLayoutDontCheckGeneration(); - } - - uint8_t* data() { - return &data_[0]; - } - - UnboxedExpandoObject* maybeExpando() const { - return expando_; - } - - void initExpando() { - expando_ = nullptr; - } - - // For use during GC. - JSObject** addressOfExpando() { - return reinterpret_cast<JSObject**>(&expando_); - } - - bool containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid id) const; - - static UnboxedExpandoObject* ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj); - - bool setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property, const Value& v); - Value getValue(const UnboxedLayout::Property& property, bool maybeUninitialized = false); - - static bool convertToNative(JSContext* cx, JSObject* obj); - static UnboxedPlainObject* create(ExclusiveContext* cx, HandleObjectGroup group, - NewObjectKind newKind); - static JSObject* createWithProperties(ExclusiveContext* cx, HandleObjectGroup group, - NewObjectKind newKind, IdValuePair* properties); - - void fillAfterConvert(ExclusiveContext* cx, - Handle<GCVector<Value>> values, size_t* valueCursor); - - static void trace(JSTracer* trc, JSObject* object); - - static size_t offsetOfExpando() { - return offsetof(UnboxedPlainObject, expando_); - } - - static size_t offsetOfData() { - return offsetof(UnboxedPlainObject, data_[0]); - } -}; - -// Try to construct an UnboxedLayout for each of the preliminary objects, -// provided they all match the template shape. If successful, converts the -// preliminary objects and their group to the new unboxed representation. -bool -TryConvertToUnboxedLayout(ExclusiveContext* cx, AutoEnterAnalysis& enter, Shape* templateShape, - ObjectGroup* group, PreliminaryObjectArray* objects); - -inline gc::AllocKind -UnboxedLayout::getAllocKind() const -{ - MOZ_ASSERT(size()); - return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size()); -} - -// Class for an array object using an unboxed representation. -class UnboxedArrayObject : public JSObject -{ - // Elements pointer for the object. - uint8_t* elements_; - - // The nominal array length. This always fits in an int32_t. - uint32_t length_; - - // Value indicating the allocated capacity and initialized length of the - // array. The top CapacityBits bits are an index into CapacityArray, which - // indicates the elements capacity. The low InitializedLengthBits store the - // initialized length of the array. - uint32_t capacityIndexAndInitializedLength_; - - // If the elements are inline, they will point here. - uint8_t inlineElements_[1]; - - public: - static const uint32_t CapacityBits = 6; - static const uint32_t CapacityShift = 26; - - static const uint32_t CapacityMask = uint32_t(-1) << CapacityShift; - static const uint32_t InitializedLengthMask = (1 << CapacityShift) - 1; - - static const uint32_t MaximumCapacity = InitializedLengthMask; - static const uint32_t MinimumDynamicCapacity = 8; - - static const uint32_t CapacityArray[]; - - // Capacity index which indicates the array's length is also its capacity. - static const uint32_t CapacityMatchesLengthIndex = 0; - - private: - static inline uint32_t computeCapacity(uint32_t index, uint32_t length) { - if (index == CapacityMatchesLengthIndex) - return length; - return CapacityArray[index]; - } - - static uint32_t chooseCapacityIndex(uint32_t capacity, uint32_t length); - static uint32_t exactCapacityIndex(uint32_t capacity); - - public: - static const Class class_; - - static bool obj_lookupProperty(JSContext* cx, HandleObject obj, - HandleId id, MutableHandleObject objp, - MutableHandleShape propp); - - static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle<PropertyDescriptor> desc, - ObjectOpResult& result); - - static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp); - - static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver, - HandleId id, MutableHandleValue vp); - - static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult& result); - - static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, - MutableHandle<PropertyDescriptor> desc); - - static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, - ObjectOpResult& result); - - static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, - bool enumerableOnly); - static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable); - - inline const UnboxedLayout& layout() const; - - const UnboxedLayout& layoutDontCheckGeneration() const { - return group()->unboxedLayoutDontCheckGeneration(); - } - - JSValueType elementType() const { - return layoutDontCheckGeneration().elementType(); - } - - uint32_t elementSize() const { - return UnboxedTypeSize(elementType()); - } - - static bool convertToNative(JSContext* cx, JSObject* obj); - static UnboxedArrayObject* create(ExclusiveContext* cx, HandleObjectGroup group, - uint32_t length, NewObjectKind newKind, - uint32_t maxLength = MaximumCapacity); - - static bool convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj, - ObjectGroup* group, Shape* shape); - bool convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group); - - void fillAfterConvert(ExclusiveContext* cx, - Handle<GCVector<Value>> values, size_t* valueCursor); - - static void trace(JSTracer* trc, JSObject* object); - static void objectMoved(JSObject* obj, const JSObject* old); - static void finalize(FreeOp* fop, JSObject* obj); - - static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src, - gc::AllocKind allocKind); - - uint8_t* elements() { - return elements_; - } - - bool hasInlineElements() const { - return elements_ == &inlineElements_[0]; - } - - uint32_t length() const { - return length_; - } - - uint32_t initializedLength() const { - return capacityIndexAndInitializedLength_ & InitializedLengthMask; - } - - uint32_t capacityIndex() const { - return (capacityIndexAndInitializedLength_ & CapacityMask) >> CapacityShift; - } - - uint32_t capacity() const { - return computeCapacity(capacityIndex(), length()); - } - - bool containsProperty(ExclusiveContext* cx, jsid id); - - bool setElement(ExclusiveContext* cx, size_t index, const Value& v); - bool initElement(ExclusiveContext* cx, size_t index, const Value& v); - void initElementNoTypeChange(size_t index, const Value& v); - Value getElement(size_t index); - - template <JSValueType Type> inline bool setElementSpecific(ExclusiveContext* cx, size_t index, - const Value& v); - template <JSValueType Type> inline void setElementNoTypeChangeSpecific(size_t index, const Value& v); - template <JSValueType Type> inline bool initElementSpecific(ExclusiveContext* cx, size_t index, - const Value& v); - template <JSValueType Type> inline void initElementNoTypeChangeSpecific(size_t index, const Value& v); - template <JSValueType Type> inline Value getElementSpecific(size_t index); - template <JSValueType Type> inline void triggerPreBarrier(size_t index); - - bool growElements(ExclusiveContext* cx, size_t cap); - void shrinkElements(ExclusiveContext* cx, size_t cap); - - static uint32_t offsetOfElements() { - return offsetof(UnboxedArrayObject, elements_); - } - static uint32_t offsetOfLength() { - return offsetof(UnboxedArrayObject, length_); - } - static uint32_t offsetOfCapacityIndexAndInitializedLength() { - return offsetof(UnboxedArrayObject, capacityIndexAndInitializedLength_); - } - static uint32_t offsetOfInlineElements() { - return offsetof(UnboxedArrayObject, inlineElements_); - } - - void setLengthInt32(uint32_t length) { - MOZ_ASSERT(length <= INT32_MAX); - length_ = length; - } - - inline void setLength(ExclusiveContext* cx, uint32_t len); - inline void setInitializedLength(uint32_t initlen); - - inline void setInitializedLengthNoBarrier(uint32_t initlen) { - MOZ_ASSERT(initlen <= InitializedLengthMask); - capacityIndexAndInitializedLength_ = - (capacityIndexAndInitializedLength_ & CapacityMask) | initlen; - } - - private: - void setInlineElements() { - elements_ = &inlineElements_[0]; - } - - void setCapacityIndex(uint32_t index) { - MOZ_ASSERT(index <= (CapacityMask >> CapacityShift)); - capacityIndexAndInitializedLength_ = - (index << CapacityShift) | initializedLength(); - } -}; - -} // namespace js - -namespace JS { - -template <> -struct DeletePolicy<js::UnboxedLayout> : public js::GCManagedDeletePolicy<js::UnboxedLayout> -{}; - -} /* namespace JS */ - -#endif /* vm_UnboxedObject_h */ diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index 7fade24fb5..52b8eeed18 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -34,6 +34,7 @@ #include "frontend/Parser.h" #include "gc/Policy.h" #include "js/MemoryMetrics.h" +#include "vm/SelfHosting.h" #include "vm/StringBuffer.h" #include "vm/Time.h" #include "vm/TypedArrayObject.h" @@ -249,14 +250,14 @@ typedef Vector<AsmJSImport, 0, SystemAllocPolicy> AsmJSImportVector; // case the function is toString()ed. class AsmJSExport { - uint32_t funcIndex_; + uint32_t funcIndex_ = 0; // All fields are treated as cacheable POD: - uint32_t startOffsetInModule_; // Store module-start-relative offsets - uint32_t endOffsetInModule_; // so preserved by serialization. + uint32_t startOffsetInModule_ = 0; // Store module-start-relative offsets + uint32_t endOffsetInModule_ = 0; // so preserved by serialization. public: - AsmJSExport() { PodZero(this); } + AsmJSExport() = default; AsmJSExport(uint32_t funcIndex, uint32_t startOffsetInModule, uint32_t endOffsetInModule) : funcIndex_(funcIndex), startOffsetInModule_(startOffsetInModule), @@ -288,12 +289,12 @@ enum class CacheResult struct AsmJSMetadataCacheablePod { - uint32_t numFFIs; - uint32_t srcLength; - uint32_t srcLengthWithRightBrace; - bool usesSimd; + uint32_t numFFIs = 0; + uint32_t srcLength = 0; + uint32_t srcLengthWithRightBrace = 0; + bool usesSimd = false; - AsmJSMetadataCacheablePod() { PodZero(this); } + AsmJSMetadataCacheablePod() = default; }; struct js::AsmJSMetadata : Metadata, AsmJSMetadataCacheablePod @@ -318,6 +319,7 @@ struct js::AsmJSMetadata : Metadata, AsmJSMetadataCacheablePod // Function constructor, this will be the first character in the function // source. Otherwise, it will be the opening parenthesis of the arguments // list. + uint32_t toStringStart; uint32_t srcStart; uint32_t srcBodyStart; bool strict; @@ -1758,6 +1760,7 @@ class MOZ_STACK_CLASS ModuleValidator if (!asmJSMetadata_) return false; + asmJSMetadata_->toStringStart = moduleFunctionNode_->pn_funbox->toStringStart; asmJSMetadata_->srcStart = moduleFunctionNode_->pn_body->pn_pos.begin; asmJSMetadata_->srcBodyStart = parser_.tokenStream.currentToken().pos.end; asmJSMetadata_->strict = parser_.pc->sc()->strict() && @@ -3248,10 +3251,9 @@ CheckModuleLevelName(ModuleValidator& m, ParseNode* usepn, PropertyName* name) static bool CheckFunctionHead(ModuleValidator& m, ParseNode* fn) { - JSFunction* fun = FunctionObject(fn); if (fn->pn_funbox->hasRest()) return m.fail(fn, "rest args not allowed"); - if (fun->isExprBody()) + if (fn->pn_funbox->isExprBody()) return m.fail(fn, "expression closures not allowed"); if (fn->pn_funbox->hasDestructuringArgs) return m.fail(fn, "destructuring args not allowed"); @@ -7049,19 +7051,20 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line) TokenStream& tokenStream = m.tokenStream(); tokenStream.consumeKnownToken(TOK_FUNCTION, TokenStream::Operand); + uint32_t toStringStart = tokenStream.currentToken().pos.begin; *line = tokenStream.srcCoords.lineNum(tokenStream.currentToken().pos.end); TokenKind tk; if (!tokenStream.getToken(&tk, TokenStream::Operand)) return false; - if (tk != TOK_NAME && tk != TOK_YIELD) + if (!TokenKindIsPossibleIdentifier(tk)) return false; // The regular parser will throw a SyntaxError, no need to m.fail. RootedPropertyName name(m.cx(), m.parser().bindingIdentifier(YieldIsName)); if (!name) return false; - ParseNode* fn = m.parser().handler.newFunctionDefinition(); + ParseNode* fn = m.parser().handler.newFunctionStatement(); if (!fn) return false; @@ -7071,7 +7074,7 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line) ParseContext* outerpc = m.parser().pc; Directives directives(outerpc); - FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, directives, NotGenerator, + FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, toStringStart, directives, NotGenerator, SyncFunction, /* tryAnnexB = */ false); if (!funbox) return false; @@ -7463,6 +7466,20 @@ GetDataProperty(JSContext* cx, HandleValue objVal, ImmutablePropertyNamePtr fiel } static bool +HasObjectValueOfMethodPure(JSObject* obj, JSContext* cx) +{ + Value v; + if (!GetPropertyPure(cx, obj, NameToId(cx->names().valueOf), &v)) + return false; + + JSFunction* fun; + if (!IsFunctionObject(v, &fun)) + return false; + + return IsSelfHostedFunctionWithName(fun, cx->names().Object_valueOf); +} + +static bool HasPureCoercion(JSContext* cx, HandleValue v) { // Unsigned SIMD types are not allowed in function signatures. @@ -7476,10 +7493,10 @@ HasPureCoercion(JSContext* cx, HandleValue v) // coercions are not observable and coercion via ToNumber/ToInt32 // definitely produces NaN/0. We should remove this special case later once // most apps have been built with newer Emscripten. - jsid toString = NameToId(cx->names().toString); if (v.toObject().is<JSFunction>() && - HasObjectValueOf(&v.toObject(), cx) && - ClassMethodIsNative(cx, &v.toObject().as<JSFunction>(), &JSFunction::class_, toString, fun_toString)) + HasNoToPrimitiveMethodPure(&v.toObject(), cx) && + HasObjectValueOfMethodPure(&v.toObject(), cx) && + HasNativeMethodPure(&v.toObject(), cx->names().toString, fun_toString, cx)) { return true; } @@ -8054,7 +8071,7 @@ HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& me return false; } - uint32_t begin = metadata.srcStart; + uint32_t begin = metadata.toStringStart; uint32_t end = metadata.srcEndAfterCurly(); Rooted<JSFlatString*> src(cx, source->substringDontDeflate(cx, begin, end)); if (!src) @@ -8085,7 +8102,7 @@ HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& me SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller() ? SourceBufferHolder::GiveOwnership : SourceBufferHolder::NoOwnership; - SourceBufferHolder srcBuf(chars, stableChars.twoByteRange().length(), ownership); + SourceBufferHolder srcBuf(chars, end - begin, ownership); if (!frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, Nothing())) return false; @@ -8537,6 +8554,7 @@ LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, bool* loaded return true; // See AsmJSMetadata comment as well as ModuleValidator::init(). + asmJSMetadata->toStringStart = parser.pc->functionBox()->toStringStart; asmJSMetadata->srcStart = parser.pc->functionBox()->functionNode->pn_body->pn_pos.begin; asmJSMetadata->srcBodyStart = parser.tokenStream.currentToken().pos.end; asmJSMetadata->strict = parser.pc->sc()->strict() && !parser.pc->sc()->hasExplicitUseStrict(); @@ -8834,7 +8852,7 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda MOZ_ASSERT(IsAsmJSModule(fun)); const AsmJSMetadata& metadata = AsmJSModuleFunctionToModule(fun).metadata().asAsmJS(); - uint32_t begin = metadata.srcStart; + uint32_t begin = metadata.toStringStart; uint32_t end = metadata.srcEndAfterCurly(); ScriptSource* source = metadata.scriptSource.get(); @@ -8843,17 +8861,15 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda if (addParenToLambda && fun->isLambda() && !out.append("(")) return nullptr; - if (!out.append("function ")) - return nullptr; - - if (fun->explicitName() && !out.append(fun->explicitName())) - return nullptr; - bool haveSource = source->hasSourceData(); if (!haveSource && !JSScript::loadSource(cx, source, &haveSource)) return nullptr; if (!haveSource) { + if (!out.append("function ")) + return nullptr; + if (fun->explicitName() && !out.append(fun->explicitName())) + return nullptr; if (!out.append("() {\n [sourceless code]\n}")) return nullptr; } else { diff --git a/js/src/wasm/WasmBinaryConstants.h b/js/src/wasm/WasmBinaryConstants.h index fd3bd1264a..9aa5091f65 100644 --- a/js/src/wasm/WasmBinaryConstants.h +++ b/js/src/wasm/WasmBinaryConstants.h @@ -434,15 +434,6 @@ enum class Op Limit }; -// Telemetry sample values for the JS_AOT_USAGE key, indicating whether asm.js -// or WebAssembly is used. - -enum class Telemetry -{ - ASMJS = 0, - WASM = 1 -}; - } // namespace wasm } // namespace js diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index 0b030c8449..8d4f575b23 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -1659,7 +1659,7 @@ Reject(JSContext* cx, const CompileArgs& args, UniqueChars error, Handle<Promise if (!cx->getPendingException(&rejectionValue)) return false; - return promise->reject(cx, rejectionValue); + return PromiseObject::reject(cx, promise, rejectionValue); } RootedObject stack(cx, promise->allocationSite()); @@ -1687,7 +1687,7 @@ Reject(JSContext* cx, const CompileArgs& args, UniqueChars error, Handle<Promise return false; RootedValue rejectionValue(cx, ObjectValue(*errorObj)); - return promise->reject(cx, rejectionValue); + return PromiseObject::reject(cx, promise, rejectionValue); } static bool @@ -1699,7 +1699,7 @@ ResolveCompilation(JSContext* cx, Module& module, Handle<PromiseObject*> promise return false; RootedValue resolutionValue(cx, ObjectValue(*moduleObj)); - return promise->resolve(cx, resolutionValue); + return PromiseObject::resolve(cx, promise, resolutionValue); } struct CompileTask : PromiseTask @@ -1734,7 +1734,7 @@ RejectWithPendingException(JSContext* cx, Handle<PromiseObject*> promise) if (!GetAndClearException(cx, &rejectionValue)) return false; - return promise->reject(cx, rejectionValue); + return PromiseObject::reject(cx, promise, rejectionValue); } static bool @@ -1822,7 +1822,7 @@ ResolveInstantiation(JSContext* cx, Module& module, HandleObject importObj, return false; val = ObjectValue(*resultObj); - return promise->resolve(cx, val); + return PromiseObject::resolve(cx, promise, val); } struct InstantiateTask : CompileTask @@ -1894,7 +1894,7 @@ WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp) return RejectWithPendingException(cx, promise, callArgs); RootedValue resolutionValue(cx, ObjectValue(*instanceObj)); - if (!promise->resolve(cx, resolutionValue)) + if (!PromiseObject::resolve(cx, promise, resolutionValue)) return false; } else { auto task = cx->make_unique<InstantiateTask>(cx, promise, importObj); @@ -2018,7 +2018,7 @@ js::InitWebAssemblyClass(JSContext* cx, HandleObject obj) Handle<GlobalObject*> global = obj.as<GlobalObject>(); MOZ_ASSERT(!global->isStandardClassResolved(JSProto_WebAssembly)); - RootedObject proto(cx, global->getOrCreateObjectPrototype(cx)); + RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global)); if (!proto) return nullptr; diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp index b24e01a400..f1ecd86203 100644 --- a/js/src/wasm/WasmModule.cpp +++ b/js/src/wasm/WasmModule.cpp @@ -1066,8 +1066,5 @@ Module::instantiate(JSContext* cx, return false; } - uint32_t mode = uint32_t(metadata().isAsmJS() ? Telemetry::ASMJS : Telemetry::WASM); - cx->runtime()->addTelemetry(JS_TELEMETRY_AOT_USAGE, mode); - return true; } diff --git a/js/src/wasm/WasmSignalHandlers.cpp b/js/src/wasm/WasmSignalHandlers.cpp index 78d21369d7..21093ca9ae 100644 --- a/js/src/wasm/WasmSignalHandlers.cpp +++ b/js/src/wasm/WasmSignalHandlers.cpp @@ -130,11 +130,16 @@ class AutoSetHandlingSegFault # define EPC_sig(p) ((p)->sc_pc) # define RFP_sig(p) ((p)->sc_regs[30]) # endif -#elif defined(__linux__) || defined(SOLARIS) +#elif defined(__linux__) || defined(__sun) # if defined(__linux__) # define XMM_sig(p,i) ((p)->uc_mcontext.fpregs->_xmm[i]) # define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_EIP]) -# else +# else // defined(__sun) +/* See https://www.illumos.org/issues/5876. They keep arguing over whether + * <ucontext.h> should provide the register index defines in regset.h or + * require applications to request them specifically, and we need them here. */ +#include <ucontext.h> +#include <sys/regset.h> # define XMM_sig(p,i) ((p)->uc_mcontext.fpregs.fp_reg_set.fpchip_state.xmm[i]) # define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_PC]) # endif diff --git a/js/xpconnect/idl/moz.build b/js/xpconnect/idl/moz.build index 2438b1a5a8..0808d34506 100644 --- a/js/xpconnect/idl/moz.build +++ b/js/xpconnect/idl/moz.build @@ -7,7 +7,6 @@ XPIDL_SOURCES += [ 'mozIJSSubScriptLoader.idl', 'nsIAddonInterposition.idl', - 'nsIScriptError.idl', 'nsIXPConnect.idl', 'nsIXPCScriptable.idl', 'xpccomponents.idl', diff --git a/js/xpconnect/idl/nsIScriptError.idl b/js/xpconnect/idl/nsIScriptError.idl deleted file mode 100644 index 468ca682fa..0000000000 --- a/js/xpconnect/idl/nsIScriptError.idl +++ /dev/null @@ -1,122 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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/. */ - -/* - * nsIConsoleMessage subclass for representing JavaScript errors and warnings. - */ - - -#include "nsISupports.idl" -#include "nsIConsoleMessage.idl" - -%{C++ -#include "nsStringGlue.h" // for nsDependentCString -%} - -[scriptable, uuid(361be358-76f0-47aa-b37b-6ad833599e8d)] -interface nsIScriptError : nsIConsoleMessage -{ - /** pseudo-flag for default case */ - const unsigned long errorFlag = 0x0; - - /** message is warning */ - const unsigned long warningFlag = 0x1; - - /** exception was thrown for this case - exception-aware hosts can ignore */ - const unsigned long exceptionFlag = 0x2; - - // XXX check how strict is implemented these days. - /** error or warning is due to strict option */ - const unsigned long strictFlag = 0x4; - - /** just a log message */ - const unsigned long infoFlag = 0x8; - - /** - * The error message without any context/line number information. - * - * @note nsIConsoleMessage.message will return the error formatted - * with file/line information. - */ - readonly attribute AString errorMessage; - - readonly attribute AString sourceName; - readonly attribute AString sourceLine; - readonly attribute uint32_t lineNumber; - readonly attribute uint32_t columnNumber; - readonly attribute uint32_t flags; - - /** - * Categories I know about - - * XUL javascript - * content javascript (both of these from nsDocShell, currently) - * system javascript (errors in JS components and other system JS) - */ - readonly attribute string category; - - /* Get the window id this was initialized with. Zero will be - returned if init() was used instead of initWithWindowID(). */ - readonly attribute unsigned long long outerWindowID; - - /* Get the inner window id this was initialized with. Zero will be - returned if init() was used instead of initWithWindowID(). */ - readonly attribute unsigned long long innerWindowID; - - readonly attribute boolean isFromPrivateWindow; - - attribute jsval stack; - - /** - * The name of a template string, as found in js.msg, associated with the - * error message. - */ - attribute AString errorMessageName; - - - void init(in AString message, - in AString sourceName, - in AString sourceLine, - in uint32_t lineNumber, - in uint32_t columnNumber, - in uint32_t flags, - in string category); - - /* This should be called instead of nsIScriptError.init to - initialize with a window id. The window id should be for the - inner window associated with this error. */ - void initWithWindowID(in AString message, - in AString sourceName, - in AString sourceLine, - in uint32_t lineNumber, - in uint32_t columnNumber, - in uint32_t flags, - in ACString category, - in unsigned long long innerWindowID); -%{C++ - // This overload allows passing a literal string for category. - template<uint32_t N> - nsresult InitWithWindowID(const nsAString& message, - const nsAString& sourceName, - const nsAString& sourceLine, - uint32_t lineNumber, - uint32_t columnNumber, - uint32_t flags, - const char (&c)[N], - uint64_t aInnerWindowID) - { - nsDependentCString category(c, N - 1); - return InitWithWindowID(message, sourceName, sourceLine, lineNumber, - columnNumber, flags, category, aInnerWindowID); - } -%} - -}; - -%{ C++ -#define NS_SCRIPTERROR_CID \ -{ 0x1950539a, 0x90f0, 0x4d22, { 0xb5, 0xaf, 0x71, 0x32, 0x9c, 0x68, 0xfa, 0x35 }} - -#define NS_SCRIPTERROR_CONTRACTID "@mozilla.org/scripterror;1" -%} diff --git a/js/xpconnect/loader/mozJSSubScriptLoader.cpp b/js/xpconnect/loader/mozJSSubScriptLoader.cpp index 9c8908ea41..f23e5833a0 100644 --- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp +++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp @@ -295,7 +295,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AsyncScriptLoader) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AsyncScriptLoader) diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index 120772ed2f..a516cf73fb 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -66,7 +66,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SandboxPrivate) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SandboxPrivate) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS tmp->TraverseHostObjectURIs(cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index dbb63092ed..07ce7460b9 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -34,9 +34,11 @@ #include "nsDOMClassInfo.h" #include "ShimInterfaceInfo.h" #include "nsIAddonInterposition.h" +#include "nsIScriptError.h" #include "nsISimpleEnumerator.h" #include "nsPIDOMWindow.h" #include "nsGlobalWindow.h" +#include "nsScriptError.h" using namespace mozilla; using namespace JS; diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp index 37932b452c..77f09f4a54 100644 --- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -11,9 +11,11 @@ #include "xpcprivate.h" #include "nsIAtom.h" +#include "nsIScriptError.h" #include "nsWrapperCache.h" #include "nsJSUtils.h" #include "nsQueryObject.h" +#include "nsScriptError.h" #include "WrapperFactory.h" #include "nsWrapperCacheInlines.h" diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp index f352607d4c..0243d80e36 100644 --- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -28,7 +28,6 @@ #include "nsPIDOMWindow.h" #include "nsPrintfCString.h" #include "mozilla/Preferences.h" -#include "mozilla/Telemetry.h" #include "mozilla/Services.h" #include "mozilla/dom/ScriptSettings.h" @@ -133,10 +132,7 @@ class AsyncFreeSnowWhite : public Runnable public: NS_IMETHOD Run() override { - TimeStamp start = TimeStamp::Now(); bool hadSnowWhiteObjects = nsCycleCollector_doDeferredDeletion(); - Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_ASYNC_SNOW_WHITE_FREEING, - uint32_t((TimeStamp::Now() - start).ToMilliseconds())); if (hadSnowWhiteObjects && !mContinuation) { mContinuation = true; if (NS_FAILED(NS_DispatchToCurrentThread(this))) { @@ -1238,8 +1234,6 @@ XPCJSContext::InterruptCallback(JSContext* cx) if (self->mSlowScriptCheckpoint.IsNull()) { self->mSlowScriptCheckpoint = TimeStamp::NowLoRes(); self->mSlowScriptSecondHalf = false; - self->mSlowScriptActualWait = mozilla::TimeDuration(); - self->mTimeoutAccumulated = false; return true; } @@ -1261,8 +1255,6 @@ XPCJSContext::InterruptCallback(JSContext* cx) if (limit == 0 || duration.ToSeconds() < limit / 2.0) return true; - self->mSlowScriptActualWait += duration; - // In order to guard against time changes or laptops going to sleep, we // don't trigger the slow script warning until (limit/2) seconds have // elapsed twice. @@ -1314,13 +1306,6 @@ XPCJSContext::InterruptCallback(JSContext* cx) return false; } - // Accumulate slow script invokation delay. - if (!chrome && !self->mTimeoutAccumulated) { - uint32_t delay = uint32_t(self->mSlowScriptActualWait.ToMilliseconds() - (limit * 1000.0)); - Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_NOTIFY_DELAY, delay); - self->mTimeoutAccumulated = true; - } - // Show the prompt to the user, and kill if requested. nsGlobalWindow::SlowScriptResponse response = win->ShowSlowScriptDialog(); if (response == nsGlobalWindow::KillSlowScript) { @@ -2953,105 +2938,6 @@ JSSizeOfTab(JSObject* objArg, size_t* jsObjectsSize, size_t* jsStringsSize, } // namespace xpc static void -AccumulateTelemetryCallback(int id, uint32_t sample, const char* key) -{ - switch (id) { - case JS_TELEMETRY_GC_REASON: - Telemetry::Accumulate(Telemetry::GC_REASON_2, sample); - break; - case JS_TELEMETRY_GC_IS_ZONE_GC: - Telemetry::Accumulate(Telemetry::GC_IS_COMPARTMENTAL, sample); - break; - case JS_TELEMETRY_GC_MS: - Telemetry::Accumulate(Telemetry::GC_MS, sample); - break; - case JS_TELEMETRY_GC_BUDGET_MS: - Telemetry::Accumulate(Telemetry::GC_BUDGET_MS, sample); - break; - case JS_TELEMETRY_GC_ANIMATION_MS: - Telemetry::Accumulate(Telemetry::GC_ANIMATION_MS, sample); - break; - case JS_TELEMETRY_GC_MAX_PAUSE_MS: - Telemetry::Accumulate(Telemetry::GC_MAX_PAUSE_MS, sample); - break; - case JS_TELEMETRY_GC_MARK_MS: - Telemetry::Accumulate(Telemetry::GC_MARK_MS, sample); - break; - case JS_TELEMETRY_GC_SWEEP_MS: - Telemetry::Accumulate(Telemetry::GC_SWEEP_MS, sample); - break; - case JS_TELEMETRY_GC_COMPACT_MS: - Telemetry::Accumulate(Telemetry::GC_COMPACT_MS, sample); - break; - case JS_TELEMETRY_GC_MARK_ROOTS_MS: - Telemetry::Accumulate(Telemetry::GC_MARK_ROOTS_MS, sample); - break; - case JS_TELEMETRY_GC_MARK_GRAY_MS: - Telemetry::Accumulate(Telemetry::GC_MARK_GRAY_MS, sample); - break; - case JS_TELEMETRY_GC_SLICE_MS: - Telemetry::Accumulate(Telemetry::GC_SLICE_MS, sample); - break; - case JS_TELEMETRY_GC_SLOW_PHASE: - Telemetry::Accumulate(Telemetry::GC_SLOW_PHASE, sample); - break; - case JS_TELEMETRY_GC_MMU_50: - Telemetry::Accumulate(Telemetry::GC_MMU_50, sample); - break; - case JS_TELEMETRY_GC_RESET: - Telemetry::Accumulate(Telemetry::GC_RESET, sample); - break; - case JS_TELEMETRY_GC_RESET_REASON: - Telemetry::Accumulate(Telemetry::GC_RESET_REASON, sample); - break; - case JS_TELEMETRY_GC_INCREMENTAL_DISABLED: - Telemetry::Accumulate(Telemetry::GC_INCREMENTAL_DISABLED, sample); - break; - case JS_TELEMETRY_GC_NON_INCREMENTAL: - Telemetry::Accumulate(Telemetry::GC_NON_INCREMENTAL, sample); - break; - case JS_TELEMETRY_GC_NON_INCREMENTAL_REASON: - Telemetry::Accumulate(Telemetry::GC_NON_INCREMENTAL_REASON, sample); - break; - case JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS: - Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_TOTAL_MS, sample); - break; - case JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS: - Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_MAX_PAUSE_MS, sample); - break; - case JS_TELEMETRY_GC_MINOR_REASON: - Telemetry::Accumulate(Telemetry::GC_MINOR_REASON, sample); - break; - case JS_TELEMETRY_GC_MINOR_REASON_LONG: - Telemetry::Accumulate(Telemetry::GC_MINOR_REASON_LONG, sample); - break; - case JS_TELEMETRY_GC_MINOR_US: - Telemetry::Accumulate(Telemetry::GC_MINOR_US, sample); - break; - case JS_TELEMETRY_GC_NURSERY_BYTES: - Telemetry::Accumulate(Telemetry::GC_NURSERY_BYTES, sample); - break; - case JS_TELEMETRY_GC_PRETENURE_COUNT: - Telemetry::Accumulate(Telemetry::GC_PRETENURE_COUNT, sample); - break; - case JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT: - Telemetry::Accumulate(Telemetry::JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, sample); - break; - case JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS: - Telemetry::Accumulate(Telemetry::JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS, sample); - break; - case JS_TELEMETRY_ADDON_EXCEPTIONS: - Telemetry::Accumulate(Telemetry::JS_TELEMETRY_ADDON_EXCEPTIONS, nsDependentCString(key), sample); - break; - case JS_TELEMETRY_AOT_USAGE: - Telemetry::Accumulate(Telemetry::JS_AOT_USAGE, sample); - break; - default: - MOZ_ASSERT_UNREACHABLE("Unexpected JS_TELEMETRY id"); - } -} - -static void CompartmentNameCallback(JSContext* cx, JSCompartment* comp, char* buf, size_t bufsize) { @@ -3210,7 +3096,6 @@ XPCJSContext::XPCJSContext() mWatchdogManager(new WatchdogManager(this)), mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite()), mSlowScriptSecondHalf(false), - mTimeoutAccumulated(false), mPendingResult(NS_OK) { } @@ -3376,7 +3261,6 @@ XPCJSContext::Initialize() JS_AddWeakPointerCompartmentCallback(cx, WeakPointerCompartmentCallback, this); JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks); js::SetPreserveWrapperCallback(cx, PreserveWrapper); - JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryCallback); js::SetActivityCallback(cx, ActivityCallback, this); JS_AddInterruptCallback(cx, InterruptCallback); js::SetWindowProxyClass(cx, &OuterWindowProxyClass); @@ -3541,8 +3425,6 @@ XPCJSContext::BeforeProcessTask(bool aMightBlock) // Start the slow script timer. mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes(); mSlowScriptSecondHalf = false; - mSlowScriptActualWait = mozilla::TimeDuration(); - mTimeoutAccumulated = false; // As we may be entering a nested event loop, we need to // cancel any ongoing performance measurement. diff --git a/js/xpconnect/src/XPCModule.h b/js/xpconnect/src/XPCModule.h index d627646254..506e8945a4 100644 --- a/js/xpconnect/src/XPCModule.h +++ b/js/xpconnect/src/XPCModule.h @@ -23,7 +23,6 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsJSID) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIXPConnect, nsXPConnect::GetSingleton) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsScriptError) NS_GENERIC_FACTORY_CONSTRUCTOR(mozJSComponentLoader) NS_GENERIC_FACTORY_CONSTRUCTOR(mozJSSubScriptLoader) @@ -31,14 +30,12 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(mozJSSubScriptLoader) NS_DEFINE_NAMED_CID(NS_JS_ID_CID); NS_DEFINE_NAMED_CID(NS_XPCONNECT_CID); NS_DEFINE_NAMED_CID(NS_XPCEXCEPTION_CID); -NS_DEFINE_NAMED_CID(NS_SCRIPTERROR_CID); NS_DEFINE_NAMED_CID(MOZJSCOMPONENTLOADER_CID); NS_DEFINE_NAMED_CID(MOZ_JSSUBSCRIPTLOADER_CID); #define XPCONNECT_CIDENTRIES \ { &kNS_JS_ID_CID, false, nullptr, nsJSIDConstructor }, \ { &kNS_XPCONNECT_CID, false, nullptr, nsIXPConnectConstructor }, \ - { &kNS_SCRIPTERROR_CID, false, nullptr, nsScriptErrorConstructor }, \ { &kMOZJSCOMPONENTLOADER_CID, false, nullptr, mozJSComponentLoaderConstructor },\ { &kMOZ_JSSUBSCRIPTLOADER_CID, false, nullptr, mozJSSubScriptLoaderConstructor }, @@ -46,7 +43,6 @@ NS_DEFINE_NAMED_CID(MOZ_JSSUBSCRIPTLOADER_CID); { XPC_ID_CONTRACTID, &kNS_JS_ID_CID }, \ { XPC_XPCONNECT_CONTRACTID, &kNS_XPCONNECT_CID }, \ { XPC_CONTEXT_STACK_CONTRACTID, &kNS_XPCONNECT_CID }, \ - { NS_SCRIPTERROR_CONTRACTID, &kNS_SCRIPTERROR_CID }, \ { MOZJSCOMPONENTLOADER_CONTRACTID, &kMOZJSCOMPONENTLOADER_CID }, \ { MOZJSSUBSCRIPTLOADER_CONTRACTID, &kMOZ_JSSUBSCRIPTLOADER_CID }, diff --git a/js/xpconnect/src/XPCShellImpl.cpp b/js/xpconnect/src/XPCShellImpl.cpp index a6432856df..abe50f4493 100644 --- a/js/xpconnect/src/XPCShellImpl.cpp +++ b/js/xpconnect/src/XPCShellImpl.cpp @@ -644,7 +644,7 @@ env_setProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue ObjectOpResult& result) { /* XXX porting may be easy, but these don't seem to supply setenv by default */ -#if !defined SOLARIS +#if !defined XP_SOLARIS RootedString valstr(cx); RootedString idstr(cx); int rv; @@ -663,7 +663,7 @@ env_setProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue JSAutoByteString value(cx, valstr); if (!value) return false; -#if defined XP_WIN || defined HPUX || defined OSF1 || defined SCO +#if defined XP_WIN || defined SCO { char* waste = JS_smprintf("%s=%s", name.ptr(), value.ptr()); if (!waste) { @@ -671,16 +671,7 @@ env_setProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue return false; } rv = putenv(waste); -#ifdef XP_WIN - /* - * HPUX9 at least still has the bad old non-copying putenv. - * - * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv - * that will crash if you pass it an auto char array (so it must place - * its argument directly in the char* environ[] array). - */ free(waste); -#endif } #else rv = setenv(name.ptr(), value.ptr(), 1); @@ -696,7 +687,7 @@ env_setProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue return false; } vp.setString(valstr); -#endif /* !defined SOLARIS */ +#endif /* !defined XP_SOLARIS */ return result.succeed(); } diff --git a/js/xpconnect/src/XPCWrappedJSClass.cpp b/js/xpconnect/src/XPCWrappedJSClass.cpp index 2c9fd66bc1..e90373e3d1 100644 --- a/js/xpconnect/src/XPCWrappedJSClass.cpp +++ b/js/xpconnect/src/XPCWrappedJSClass.cpp @@ -10,6 +10,7 @@ #include "jsprf.h" #include "nsArrayEnumerator.h" #include "nsContentUtils.h" +#include "nsIScriptError.h" #include "nsWrapperCache.h" #include "AccessCheck.h" #include "nsJSUtils.h" diff --git a/js/xpconnect/src/XPCWrappedNativeInfo.cpp b/js/xpconnect/src/XPCWrappedNativeInfo.cpp index 302454fb53..4b0330af62 100644 --- a/js/xpconnect/src/XPCWrappedNativeInfo.cpp +++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp @@ -11,6 +11,7 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/XPTInterfaceInfoManager.h" +#include "nsIScriptError.h" #include "nsPrintfCString.h" using namespace JS; diff --git a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp index 12b203b705..08ba8241a3 100644 --- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp +++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp @@ -924,8 +924,6 @@ const js::ObjectOps XPC_WN_ObjectOpsWithEnumerate = { nullptr, // setProperty nullptr, // getOwnPropertyDescriptor nullptr, // deleteProperty - nullptr, // watch - nullptr, // unwatch nullptr, // getElements XPC_WN_JSOp_Enumerate, nullptr, // funToString diff --git a/js/xpconnect/src/moz.build b/js/xpconnect/src/moz.build index 7e787bb566..29cfc47768 100644 --- a/js/xpconnect/src/moz.build +++ b/js/xpconnect/src/moz.build @@ -14,8 +14,6 @@ EXPORTS += [ UNIFIED_SOURCES += [ 'ExportHelpers.cpp', - 'nsScriptError.cpp', - 'nsScriptErrorWithStack.cpp', 'nsXPConnect.cpp', 'Sandbox.cpp', 'XPCCallContext.cpp', @@ -58,6 +56,7 @@ LOCAL_INCLUDES += [ '../wrappers', '/caps', '/dom/base', + '/dom/bindings', '/dom/html', '/dom/svg', '/dom/workers', @@ -67,4 +66,4 @@ LOCAL_INCLUDES += [ ] if CONFIG['GNU_CXX']: - CXXFLAGS += ['-Wno-shadow', '-Werror=format'] + CXXFLAGS += ['-Wno-shadow'] diff --git a/js/xpconnect/src/nsScriptError.cpp b/js/xpconnect/src/nsScriptError.cpp deleted file mode 100644 index ff687bc441..0000000000 --- a/js/xpconnect/src/nsScriptError.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* vim: set ts=8 sts=4 et sw=4 tw=99: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * nsIScriptError implementation. Defined here, lacking a JS-specific - * place to put XPCOM things. - */ - -#include "xpcprivate.h" -#include "jsprf.h" -#include "MainThreadUtils.h" -#include "mozilla/Assertions.h" -#include "nsGlobalWindow.h" -#include "nsPIDOMWindow.h" -#include "nsILoadContext.h" -#include "nsIDocShell.h" -#include "nsIScriptError.h" -#include "nsISensitiveInfoHiddenURI.h" - -static_assert(nsIScriptError::errorFlag == JSREPORT_ERROR && - nsIScriptError::warningFlag == JSREPORT_WARNING && - nsIScriptError::exceptionFlag == JSREPORT_EXCEPTION && - nsIScriptError::strictFlag == JSREPORT_STRICT && - nsIScriptError::infoFlag == JSREPORT_USER_1, - "flags should be consistent"); - -nsScriptErrorBase::nsScriptErrorBase() - : mMessage(), - mMessageName(), - mSourceName(), - mLineNumber(0), - mSourceLine(), - mColumnNumber(0), - mFlags(0), - mCategory(), - mOuterWindowID(0), - mInnerWindowID(0), - mTimeStamp(0), - mInitializedOnMainThread(false), - mIsFromPrivateWindow(false) -{ -} - -nsScriptErrorBase::~nsScriptErrorBase() {} - -void -nsScriptErrorBase::InitializeOnMainThread() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mInitializedOnMainThread); - - if (mInnerWindowID) { - nsGlobalWindow* window = - nsGlobalWindow::GetInnerWindowWithId(mInnerWindowID); - if (window) { - nsPIDOMWindowOuter* outer = window->GetOuterWindow(); - if (outer) - mOuterWindowID = outer->WindowID(); - - nsIDocShell* docShell = window->GetDocShell(); - nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell); - - if (loadContext) { - // Never mark exceptions from chrome windows as having come from - // private windows, since we always want them to be reported. - nsIPrincipal* winPrincipal = window->GetPrincipal(); - mIsFromPrivateWindow = loadContext->UsePrivateBrowsing() && - !nsContentUtils::IsSystemPrincipal(winPrincipal); - } - } - } - - mInitializedOnMainThread = true; -} - -// nsIConsoleMessage methods -NS_IMETHODIMP -nsScriptErrorBase::GetMessageMoz(char16_t** result) { - nsresult rv; - - nsAutoCString message; - rv = ToString(message); - if (NS_FAILED(rv)) - return rv; - - *result = UTF8ToNewUnicode(message); - if (!*result) - return NS_ERROR_OUT_OF_MEMORY; - - return NS_OK; -} - - -NS_IMETHODIMP -nsScriptErrorBase::GetLogLevel(uint32_t* aLogLevel) -{ - if (mFlags & (uint32_t)nsIScriptError::infoFlag) { - *aLogLevel = nsIConsoleMessage::info; - } else if (mFlags & (uint32_t)nsIScriptError::warningFlag) { - *aLogLevel = nsIConsoleMessage::warn; - } else { - *aLogLevel = nsIConsoleMessage::error; - } - return NS_OK; -} - -// nsIScriptError methods -NS_IMETHODIMP -nsScriptErrorBase::GetErrorMessage(nsAString& aResult) { - aResult.Assign(mMessage); - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::GetSourceName(nsAString& aResult) { - aResult.Assign(mSourceName); - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::GetSourceLine(nsAString& aResult) { - aResult.Assign(mSourceLine); - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::GetLineNumber(uint32_t* result) { - *result = mLineNumber; - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::GetColumnNumber(uint32_t* result) { - *result = mColumnNumber; - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::GetFlags(uint32_t* result) { - *result = mFlags; - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::GetCategory(char** result) { - *result = ToNewCString(mCategory); - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::GetStack(JS::MutableHandleValue aStack) { - aStack.setUndefined(); - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::SetStack(JS::HandleValue aStack) { - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::GetErrorMessageName(nsAString& aErrorMessageName) { - aErrorMessageName = mMessageName; - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::SetErrorMessageName(const nsAString& aErrorMessageName) { - mMessageName = aErrorMessageName; - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::Init(const nsAString& message, - const nsAString& sourceName, - const nsAString& sourceLine, - uint32_t lineNumber, - uint32_t columnNumber, - uint32_t flags, - const char* category) -{ - return InitWithWindowID(message, sourceName, sourceLine, lineNumber, - columnNumber, flags, - category ? nsDependentCString(category) - : EmptyCString(), - 0); -} - -NS_IMETHODIMP -nsScriptErrorBase::InitWithWindowID(const nsAString& message, - const nsAString& sourceName, - const nsAString& sourceLine, - uint32_t lineNumber, - uint32_t columnNumber, - uint32_t flags, - const nsACString& category, - uint64_t aInnerWindowID) -{ - mMessage.Assign(message); - - if (!sourceName.IsEmpty()) { - mSourceName.Assign(sourceName); - - nsCOMPtr<nsIURI> uri; - nsAutoCString pass; - if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), sourceName)) && - NS_SUCCEEDED(uri->GetPassword(pass)) && - !pass.IsEmpty()) { - nsCOMPtr<nsISensitiveInfoHiddenURI> safeUri = - do_QueryInterface(uri); - - nsAutoCString loc; - if (safeUri && - NS_SUCCEEDED(safeUri->GetSensitiveInfoHiddenSpec(loc))) { - mSourceName.Assign(NS_ConvertUTF8toUTF16(loc)); - } - } - } - - mLineNumber = lineNumber; - mSourceLine.Assign(sourceLine); - mColumnNumber = columnNumber; - mFlags = flags; - mCategory = category; - mTimeStamp = JS_Now() / 1000; - mInnerWindowID = aInnerWindowID; - - if (aInnerWindowID && NS_IsMainThread()) { - InitializeOnMainThread(); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::ToString(nsACString& /*UTF8*/ aResult) -{ - static const char format0[] = - "[%s: \"%s\" {file: \"%s\" line: %d column: %d source: \"%s\"}]"; - static const char format1[] = - "[%s: \"%s\" {file: \"%s\" line: %d}]"; - static const char format2[] = - "[%s: \"%s\"]"; - - static const char error[] = "JavaScript Error"; - static const char warning[] = "JavaScript Warning"; - - const char* severity = !(mFlags & JSREPORT_WARNING) ? error : warning; - - char* temp; - char* tempMessage = nullptr; - char* tempSourceName = nullptr; - char* tempSourceLine = nullptr; - - if (!mMessage.IsEmpty()) - tempMessage = ToNewUTF8String(mMessage); - if (!mSourceName.IsEmpty()) - // Use at most 512 characters from mSourceName. - tempSourceName = ToNewUTF8String(StringHead(mSourceName, 512)); - if (!mSourceLine.IsEmpty()) - // Use at most 512 characters from mSourceLine. - tempSourceLine = ToNewUTF8String(StringHead(mSourceLine, 512)); - - if (nullptr != tempSourceName && nullptr != tempSourceLine) - temp = JS_smprintf(format0, - severity, - tempMessage, - tempSourceName, - mLineNumber, - mColumnNumber, - tempSourceLine); - else if (!mSourceName.IsEmpty()) - temp = JS_smprintf(format1, - severity, - tempMessage, - tempSourceName, - mLineNumber); - else - temp = JS_smprintf(format2, - severity, - tempMessage); - - if (nullptr != tempMessage) - free(tempMessage); - if (nullptr != tempSourceName) - free(tempSourceName); - if (nullptr != tempSourceLine) - free(tempSourceLine); - - if (!temp) - return NS_ERROR_OUT_OF_MEMORY; - - aResult.Assign(temp); - JS_smprintf_free(temp); - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::GetOuterWindowID(uint64_t* aOuterWindowID) -{ - NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread, - "This can't be safely determined off the main thread, " - "returning an inaccurate value!"); - - if (!mInitializedOnMainThread && NS_IsMainThread()) { - InitializeOnMainThread(); - } - - *aOuterWindowID = mOuterWindowID; - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::GetInnerWindowID(uint64_t* aInnerWindowID) -{ - *aInnerWindowID = mInnerWindowID; - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::GetTimeStamp(int64_t* aTimeStamp) -{ - *aTimeStamp = mTimeStamp; - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorBase::GetIsFromPrivateWindow(bool* aIsFromPrivateWindow) -{ - NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread, - "This can't be safely determined off the main thread, " - "returning an inaccurate value!"); - - if (!mInitializedOnMainThread && NS_IsMainThread()) { - InitializeOnMainThread(); - } - - *aIsFromPrivateWindow = mIsFromPrivateWindow; - return NS_OK; -} - -NS_IMPL_ISUPPORTS(nsScriptError, nsIConsoleMessage, nsIScriptError) diff --git a/js/xpconnect/src/nsScriptErrorWithStack.cpp b/js/xpconnect/src/nsScriptErrorWithStack.cpp deleted file mode 100644 index edc12fa767..0000000000 --- a/js/xpconnect/src/nsScriptErrorWithStack.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* vim: set ts=8 sts=4 et sw=4 tw=99: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * nsScriptErrorWithStack implementation. - * a main-thread-only, cycle-collected subclass of nsScriptErrorBase - * that can store a SavedFrame stack trace object. - */ - -#include "xpcprivate.h" -#include "MainThreadUtils.h" -#include "mozilla/Assertions.h" -#include "nsGlobalWindow.h" -#include "nsCycleCollectionParticipant.h" - - -namespace { - -static nsCString -FormatStackString(JSContext* cx, HandleObject aStack) { - JS::RootedString formattedStack(cx); - - if (!JS::BuildStackString(cx, aStack, &formattedStack)) { - return nsCString(); - } - - nsAutoJSString stackJSString; - if (!stackJSString.init(cx, formattedStack)) { - return nsCString(); - } - - return NS_ConvertUTF16toUTF8(stackJSString.get()); -} - -} - - -NS_IMPL_CYCLE_COLLECTION_CLASS(nsScriptErrorWithStack) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsScriptErrorWithStack) - tmp->mStack = nullptr; -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsScriptErrorWithStack) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsScriptErrorWithStack) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStack) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_IMPL_CYCLE_COLLECTING_ADDREF(nsScriptErrorWithStack) -NS_IMPL_CYCLE_COLLECTING_RELEASE(nsScriptErrorWithStack) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsScriptErrorWithStack) - NS_INTERFACE_MAP_ENTRY(nsISupports) - NS_INTERFACE_MAP_ENTRY(nsIConsoleMessage) - NS_INTERFACE_MAP_ENTRY(nsIScriptError) -NS_INTERFACE_MAP_END - -nsScriptErrorWithStack::nsScriptErrorWithStack(JS::HandleObject aStack) - : mStack(aStack) -{ - MOZ_ASSERT(NS_IsMainThread(), "You can't use this class on workers."); - mozilla::HoldJSObjects(this); -} - -nsScriptErrorWithStack::~nsScriptErrorWithStack() { - mozilla::DropJSObjects(this); -} - -NS_IMETHODIMP -nsScriptErrorWithStack::Init(const nsAString& message, - const nsAString& sourceName, - const nsAString& sourceLine, - uint32_t lineNumber, - uint32_t columnNumber, - uint32_t flags, - const char* category) -{ - MOZ_CRASH("nsScriptErrorWithStack requires to be initialized with a document, by using InitWithWindowID"); -} - -NS_IMETHODIMP -nsScriptErrorWithStack::GetStack(JS::MutableHandleValue aStack) { - aStack.setObjectOrNull(mStack); - return NS_OK; -} - -NS_IMETHODIMP -nsScriptErrorWithStack::ToString(nsACString& /*UTF8*/ aResult) -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsCString message; - nsresult rv = nsScriptErrorBase::ToString(message); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mStack) { - aResult.Assign(message); - return NS_OK; - } - - AutoJSAPI jsapi; - if (!jsapi.Init(mStack)) { - return NS_ERROR_FAILURE; - } - - JSContext* cx = jsapi.cx(); - RootedObject stack(cx, mStack); - nsCString stackString = FormatStackString(cx, stack); - nsCString combined = message + NS_LITERAL_CSTRING("\n") + stackString; - aResult.Assign(combined); - - return NS_OK; -} diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 0466175b16..0d1a6be0a7 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -33,7 +33,9 @@ #include "nsIObjectOutputStream.h" #include "nsScriptSecurityManager.h" #include "nsIPermissionManager.h" +#include "nsIScriptError.h" #include "nsContentUtils.h" +#include "nsScriptError.h" #include "jsfriendapi.h" using namespace mozilla; @@ -170,9 +172,31 @@ nsXPConnect::IsISupportsDescendant(nsIInterfaceInfo* info) } void +xpc::ErrorBase::Init(JSErrorBase* aReport) +{ + if (!aReport->filename) { + mFileName.SetIsVoid(true); + } else { + mFileName.AssignWithConversion(aReport->filename); + } + + mLineNumber = aReport->lineno; + mColumn = aReport->column; +} + +void +xpc::ErrorNote::Init(JSErrorNotes::Note* aNote) +{ + xpc::ErrorBase::Init(aNote); + + ErrorNoteToMessageString(aNote, mErrorMsg); +} + +void xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aToStringResult, bool aIsChrome, uint64_t aWindowID) { + xpc::ErrorBase::Init(aReport); mCategory = aIsChrome ? NS_LITERAL_CSTRING("chrome javascript") : NS_LITERAL_CSTRING("content javascript"); mWindowID = aWindowID; @@ -182,12 +206,6 @@ xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aToStringResult, AppendUTF8toUTF16(aToStringResult, mErrorMsg); } - if (!aReport->filename) { - mFileName.SetIsVoid(true); - } else { - mFileName.AssignWithConversion(aReport->filename); - } - mSourceLine.Assign(aReport->linebuf(), aReport->linebufLength()); const JSErrorFormatString* efs = js::GetErrorMessage(nullptr, aReport->errorNumber); @@ -197,10 +215,20 @@ xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aToStringResult, mErrorMsgName.AssignASCII(efs->name); } - mLineNumber = aReport->lineno; - mColumn = aReport->column; mFlags = aReport->flags; mIsMuted = aReport->isMuted; + + if (aReport->notes) { + if (!mNotes.SetLength(aReport->notes->length(), fallible)) { + return; + } + + size_t i = 0; + for (auto&& note : *aReport->notes) { + mNotes.ElementAt(i).Init(note.get()); + i++; + } + } } void @@ -226,6 +254,59 @@ xpc::ErrorReport::Init(JSContext* aCx, mozilla::dom::Exception* aException, static LazyLogModule gJSDiagnostics("JSDiagnostics"); void +xpc::ErrorBase::AppendErrorDetailsTo(nsCString& error) +{ + error.Append(NS_LossyConvertUTF16toASCII(mFileName)); + error.AppendLiteral(", line "); + error.AppendInt(mLineNumber, 10); + error.AppendLiteral(": "); + error.Append(NS_LossyConvertUTF16toASCII(mErrorMsg)); +} + +void +xpc::ErrorNote::LogToStderr() +{ + if (!nsContentUtils::DOMWindowDumpEnabled()) { + return; + } + + nsAutoCString error; + error.AssignLiteral("JavaScript note: "); + AppendErrorDetailsTo(error); + + fprintf(stderr, "%s\n", error.get()); + fflush(stderr); +} + +void +xpc::ErrorReport::LogToStderr() +{ + if (!nsContentUtils::DOMWindowDumpEnabled()) { + return; + } + + nsAutoCString error; + error.AssignLiteral("JavaScript "); + if (JSREPORT_IS_STRICT(mFlags)) { + error.AppendLiteral("strict "); + } + if (JSREPORT_IS_WARNING(mFlags)) { + error.AppendLiteral("warning: "); + } else { + error.AppendLiteral("error: "); + } + AppendErrorDetailsTo(error); + + fprintf(stderr, "%s\n", error.get()); + fflush(stderr); + + for (size_t i = 0, len = mNotes.Length(); i < len; i++) { + ErrorNote& note = mNotes[i]; + note.LogToStderr(); + } +} + +void xpc::ErrorReport::LogToConsole() { LogToConsoleWithStack(nullptr); @@ -233,25 +314,7 @@ xpc::ErrorReport::LogToConsole() void xpc::ErrorReport::LogToConsoleWithStack(JS::HandleObject aStack) { - // Log to stdout. - if (nsContentUtils::DOMWindowDumpEnabled()) { - nsAutoCString error; - error.AssignLiteral("JavaScript "); - if (JSREPORT_IS_STRICT(mFlags)) - error.AppendLiteral("strict "); - if (JSREPORT_IS_WARNING(mFlags)) - error.AppendLiteral("warning: "); - else - error.AppendLiteral("error: "); - error.Append(NS_LossyConvertUTF16toASCII(mFileName)); - error.AppendLiteral(", line "); - error.AppendInt(mLineNumber, 10); - error.AppendLiteral(": "); - error.Append(NS_LossyConvertUTF16toASCII(mErrorMsg)); - - fprintf(stderr, "%s\n", error.get()); - fflush(stderr); - } + LogToStderr(); MOZ_LOG(gJSDiagnostics, JSREPORT_IS_WARNING(mFlags) ? LogLevel::Warning : LogLevel::Error, @@ -263,8 +326,9 @@ xpc::ErrorReport::LogToConsoleWithStack(JS::HandleObject aStack) // mechanisms. nsCOMPtr<nsIConsoleService> consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID); + NS_ENSURE_TRUE_VOID(consoleService); - nsCOMPtr<nsIScriptError> errorObject; + RefPtr<nsScriptErrorBase> errorObject; if (mWindowID && aStack) { // Only set stack on messages related to a document // As we cache messages in the console service, @@ -275,18 +339,38 @@ xpc::ErrorReport::LogToConsoleWithStack(JS::HandleObject aStack) errorObject = new nsScriptError(); } errorObject->SetErrorMessageName(mErrorMsgName); - NS_ENSURE_TRUE_VOID(consoleService); nsresult rv = errorObject->InitWithWindowID(mErrorMsg, mFileName, mSourceLine, mLineNumber, mColumn, mFlags, mCategory, mWindowID); NS_ENSURE_SUCCESS_VOID(rv); + + for (size_t i = 0, len = mNotes.Length(); i < len; i++) { + ErrorNote& note = mNotes[i]; + + nsScriptErrorNote* noteObject = new nsScriptErrorNote(); + noteObject->Init(note.mErrorMsg, note.mFileName, + note.mLineNumber, note.mColumn); + errorObject->AddNote(noteObject); + } + consoleService->LogMessage(errorObject); } /* static */ void +xpc::ErrorNote::ErrorNoteToMessageString(JSErrorNotes::Note* aNote, + nsAString& aString) +{ + aString.Truncate(); + if (aNote->message()) { + aString.Append(NS_ConvertUTF8toUTF16(aNote->message().c_str())); + } +} + +/* static */ +void xpc::ErrorReport::ErrorReportToMessageString(JSErrorReport* aReport, nsAString& aString) { diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index d7d5586b8f..e55cc06e0d 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -128,7 +128,6 @@ #include "MainThreadUtils.h" #include "nsIConsoleService.h" -#include "nsIScriptError.h" #include "nsIException.h" #include "nsVariant.h" @@ -634,9 +633,6 @@ private: // (whichever comes later). We use it to determine whether the interrupt // callback needs to do anything. mozilla::TimeStamp mSlowScriptCheckpoint; - // Accumulates total time we actually waited for telemetry - mozilla::TimeDuration mSlowScriptActualWait; - bool mTimeoutAccumulated; // mPendingResult is used to implement Components.returnCode. Only really // meaningful while calling through XPCWrappedJS. @@ -2555,77 +2551,6 @@ extern char* xpc_PrintJSStack(JSContext* cx, bool showArgs, bool showLocals, bool showThisProps); -/***************************************************************************/ - -// Definition of nsScriptError, defined here because we lack a place to put -// XPCOM objects associated with the JavaScript engine. -class nsScriptErrorBase : public nsIScriptError { -public: - nsScriptErrorBase(); - - // TODO - do something reasonable on getting null from these babies. - - NS_DECL_NSICONSOLEMESSAGE - NS_DECL_NSISCRIPTERROR - -protected: - virtual ~nsScriptErrorBase(); - - void - InitializeOnMainThread(); - - nsString mMessage; - nsString mMessageName; - nsString mSourceName; - uint32_t mLineNumber; - nsString mSourceLine; - uint32_t mColumnNumber; - uint32_t mFlags; - nsCString mCategory; - // mOuterWindowID is set on the main thread from InitializeOnMainThread(). - uint64_t mOuterWindowID; - uint64_t mInnerWindowID; - int64_t mTimeStamp; - // mInitializedOnMainThread and mIsFromPrivateWindow are set on the main - // thread from InitializeOnMainThread(). - mozilla::Atomic<bool> mInitializedOnMainThread; - bool mIsFromPrivateWindow; -}; - -class nsScriptError final : public nsScriptErrorBase { -public: - nsScriptError() {} - NS_DECL_THREADSAFE_ISUPPORTS - -private: - virtual ~nsScriptError() {} -}; - -class nsScriptErrorWithStack : public nsScriptErrorBase { -public: - explicit nsScriptErrorWithStack(JS::HandleObject); - - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsScriptErrorWithStack) - - NS_IMETHOD Init(const nsAString& message, - const nsAString& sourceName, - const nsAString& sourceLine, - uint32_t lineNumber, - uint32_t columnNumber, - uint32_t flags, - const char* category) override; - - NS_IMETHOD GetStack(JS::MutableHandleValue) override; - NS_IMETHOD ToString(nsACString& aResult) override; - -private: - virtual ~nsScriptErrorWithStack(); - // Complete stackframe where the error happened. - // Must be SavedFrame object. - JS::Heap<JSObject*> mStack; -}; - /****************************************************************************** * Handles pre/post script processing. */ diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index fc8670d460..399cd21815 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -515,13 +515,50 @@ AllowCPOWsInAddon(const nsACString& addonId, bool allow); bool ExtraWarningsForSystemJS(); -class ErrorReport { +class ErrorBase { + public: + nsString mErrorMsg; + nsString mFileName; + uint32_t mLineNumber; + uint32_t mColumn; + + ErrorBase() : mLineNumber(0) + , mColumn(0) + {} + + void Init(JSErrorBase* aReport); + + void AppendErrorDetailsTo(nsCString& error); +}; + +class ErrorNote : public ErrorBase { + public: + void Init(JSErrorNotes::Note* aNote); + + // Produce an error event message string from the given JSErrorNotes::Note. + // This may produce an empty string if aNote doesn't have a message + // attached. + static void ErrorNoteToMessageString(JSErrorNotes::Note* aNote, + nsAString& aString); + + // Log the error note to the stderr. + void LogToStderr(); +}; + +class ErrorReport : public ErrorBase { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ErrorReport); + nsTArray<ErrorNote> mNotes; + + nsCString mCategory; + nsString mSourceLine; + nsString mErrorMsgName; + uint64_t mWindowID; + uint32_t mFlags; + bool mIsMuted; + ErrorReport() : mWindowID(0) - , mLineNumber(0) - , mColumn(0) , mFlags(0) , mIsMuted(false) {} @@ -530,6 +567,7 @@ class ErrorReport { bool aIsChrome, uint64_t aWindowID); void Init(JSContext* aCx, mozilla::dom::Exception* aException, bool aIsChrome, uint64_t aWindowID); + // Log the error report to the console. Which console will depend on the // window id it was initialized with. void LogToConsole(); @@ -544,18 +582,8 @@ class ErrorReport { static void ErrorReportToMessageString(JSErrorReport* aReport, nsAString& aString); - public: - - nsCString mCategory; - nsString mErrorMsgName; - nsString mErrorMsg; - nsString mFileName; - nsString mSourceLine; - uint64_t mWindowID; - uint32_t mLineNumber; - uint32_t mColumn; - uint32_t mFlags; - bool mIsMuted; + // Log the error report to the stderr. + void LogToStderr(); private: ~ErrorReport() {} diff --git a/js/xpconnect/tests/chrome/chrome.ini b/js/xpconnect/tests/chrome/chrome.ini index 5a7b982149..d89c89b546 100644 --- a/js/xpconnect/tests/chrome/chrome.ini +++ b/js/xpconnect/tests/chrome/chrome.ini @@ -106,7 +106,6 @@ skip-if = os == 'win' || os == 'mac' # bug 1131110 [test_precisegc.xul] [test_sandboxImport.xul] [test_scriptSettings.xul] -[test_watchpoints.xul] [test_weakmap_keys_preserved.xul] [test_weakmap_keys_preserved2.xul] [test_weakmaps.xul] diff --git a/js/xpconnect/tests/chrome/test_bug1041626.xul b/js/xpconnect/tests/chrome/test_bug1041626.xul index c7c7b70248..11529fbe48 100644 --- a/js/xpconnect/tests/chrome/test_bug1041626.xul +++ b/js/xpconnect/tests/chrome/test_bug1041626.xul @@ -28,9 +28,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1041626 ok(Cu.isXrayWrapper(window[0].location), "Location is Xrayed"); let xrayOwnProperties = Object.getOwnPropertyNames(window[0].location); - todo(xrayOwnProperties.indexOf('toJSON') != -1, - "dummy toJSON on Location should show up in Xrayable properties"); - xrayOwnProperties.push('toJSON'); let realOwnProperties = Object.getOwnPropertyNames(window[0].wrappedJSObject.location); ok(realOwnProperties.length > 2); diff --git a/js/xpconnect/tests/chrome/test_watchpoints.xul b/js/xpconnect/tests/chrome/test_watchpoints.xul deleted file mode 100644 index 2262b1a902..0000000000 --- a/js/xpconnect/tests/chrome/test_watchpoints.xul +++ /dev/null @@ -1,75 +0,0 @@ -<?xml version="1.0"?> -<?xml-stylesheet type="text/css" href="chrome://global/skin"?> -<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=693527 ---> -<window title="Mozilla Bug " - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> - - <!-- test results are displayed in the html:body --> - <body xmlns="http://www.w3.org/1999/xhtml"> - <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=" - target="_blank">Mozilla Bug 693527</a> - </body> - - <!-- test code goes here --> - <script type="application/javascript"> - <![CDATA[ - /** Test for Bug 693527 **/ - - let Cu = Components.utils; - let Ci = Components.interfaces; - - /* Create a weak reference, with a single-element weak map. */ - let make_weak_ref = function (obj) { - let m = new WeakMap; - m.set(obj, {}); - return m; - }; - - /* Check to see if a weak reference is dead. */ - let weak_ref_dead = function (r) { - return ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(r).length == 0; - } - - - let make_cycle = function () { - var p = document.createElement("p"); - p.children.x = p; - var f = function() { }; - p.watch("y", f); - var d = document.createElement("div"); - d.appendChild(p); - f.loop = d; - f.bar = {}; // observing f directly makes the leak go away even without the CC somehow - return make_weak_ref(f.bar); - }; - - var cycle_ref = make_cycle(); - - - /* set up for running precise GC/CC then checking the results */ - - SimpleTest.waitForExplicitFinish(); - - Cu.schedulePreciseGC(function () { - window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .cycleCollect(); - window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .garbageCollect(); - window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .garbageCollect(); - - ok(weak_ref_dead(cycle_ref), "Garbage gray watchpoint cycle should be collected."); - - SimpleTest.finish(); - }); - - ]]> - </script> -</window> diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul index ed67a34fe7..495b996074 100644 --- a/js/xpconnect/tests/chrome/test_xrayToJS.xul +++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul @@ -182,8 +182,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 "toGMTString", Symbol.toPrimitive]; gConstructorProperties['Date'] = constructorProps(["UTC", "parse", "now"]); gPrototypeProperties['Object'] = - ["constructor", "toSource", "toString", "toLocaleString", "valueOf", "watch", - "unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", + ["constructor", "toSource", "toString", "toLocaleString", "valueOf", + "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "__proto__"]; gConstructorProperties['Object'] = @@ -198,7 +198,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf", "includes", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find", "findIndex", "copyWithin", "fill", Symbol.iterator, Symbol.unscopables, "entries", "keys", - "values", "constructor"]; + "values", "constructor", "flat", "flatMap"]; if (isNightlyBuild) { // ...nothing now } diff --git a/js/xpconnect/wrappers/WaiveXrayWrapper.cpp b/js/xpconnect/wrappers/WaiveXrayWrapper.cpp index 27c010d341..dca3daa58c 100644 --- a/js/xpconnect/wrappers/WaiveXrayWrapper.cpp +++ b/js/xpconnect/wrappers/WaiveXrayWrapper.cpp @@ -88,6 +88,37 @@ WaiveXrayWrapper::nativeCall(JSContext* cx, JS::IsAcceptableThis test, } bool +WaiveXrayWrapper::hasInstance(JSContext* cx, HandleObject wrapper, + MutableHandleValue v, bool* bp) const { + if (v.isObject() && WrapperFactory::IsXrayWrapper(&v.toObject())) { + // If |v| is an XrayWrapper and in the same compartment as the value + // wrapped by |wrapper|, then the Xrays of |v| would be waived upon + // calling CrossCompartmentWrapper::hasInstance. This may trigger + // getters and proxy traps of unwrapped |v|. To prevent that from + // happening, we exit early. + + // |wrapper| is the right operand of "instanceof", and must either be + // a function or an object with a @@hasInstance method. We are not going + // to call @@hasInstance, so only check whether it is a function. + // This check is here for consistency with usual "instanceof" behavior, + // which throws if the right operand is not a function. Without this + // check, the "instanceof" operator would return false and potentially + // hide errors in the code that uses the "instanceof" operator. + if (!JS::IsCallable(wrapper)) { + RootedValue wrapperv(cx, JS::ObjectValue(*wrapper)); + js::ReportIsNotFunction(cx, wrapperv); + return false; + } + + *bp = false; + return true; + } + + // Both |wrapper| and |v| have no Xrays here. + return CrossCompartmentWrapper::hasInstance(cx, wrapper, v, bp); +} + +bool WaiveXrayWrapper::getPrototype(JSContext* cx, HandleObject wrapper, MutableHandleObject protop) const { return CrossCompartmentWrapper::getPrototype(cx, wrapper, protop) && diff --git a/js/xpconnect/wrappers/WaiveXrayWrapper.h b/js/xpconnect/wrappers/WaiveXrayWrapper.h index b0b4477968..0f9675c174 100644 --- a/js/xpconnect/wrappers/WaiveXrayWrapper.h +++ b/js/xpconnect/wrappers/WaiveXrayWrapper.h @@ -36,6 +36,8 @@ class WaiveXrayWrapper : public js::CrossCompartmentWrapper { JS::MutableHandle<JSObject*> objp) const override; virtual bool nativeCall(JSContext* cx, JS::IsAcceptableThis test, JS::NativeImpl impl, const JS::CallArgs& args) const override; + virtual bool hasInstance(JSContext* cx, JS::HandleObject wrapper, + JS::MutableHandleValue v, bool* bp) const override; virtual bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id, JS::MutableHandle<JS::PropertyDescriptor> desc) const override; diff --git a/js/xpconnect/wrappers/WrapperFactory.cpp b/js/xpconnect/wrappers/WrapperFactory.cpp index 0031fb1272..8c9d387887 100644 --- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -536,7 +536,7 @@ WrapperFactory::Rewrap(JSContext* cx, HandleObject existing, HandleObject obj) DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target); if (existing) - return Wrapper::Renew(cx, existing, obj, wrapper); + return Wrapper::Renew(existing, obj, wrapper); return Wrapper::New(cx, obj, wrapper); } diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index 48a9fdc68e..6e5a2f5e59 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -2309,6 +2309,20 @@ XrayWrapper<Base, Traits>::getBuiltinClass(JSContext* cx, JS::HandleObject wrapp } template <typename Base, typename Traits> +bool +XrayWrapper<Base, Traits>::hasInstance(JSContext* cx, + JS::HandleObject wrapper, + JS::MutableHandleValue v, + bool* bp) const { + assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::GET); + + // CrossCompartmentWrapper::hasInstance unwraps |wrapper|'s Xrays and enters + // its compartment. Any present XrayWrappers should be preserved, so the + // standard "instanceof" implementation is called without unwrapping first. + return JS::InstanceofOperator(cx, wrapper, v, bp); +} + +template <typename Base, typename Traits> const char* XrayWrapper<Base, Traits>::className(JSContext* cx, HandleObject wrapper) const { diff --git a/js/xpconnect/wrappers/XrayWrapper.h b/js/xpconnect/wrappers/XrayWrapper.h index 5630982c28..038d823900 100644 --- a/js/xpconnect/wrappers/XrayWrapper.h +++ b/js/xpconnect/wrappers/XrayWrapper.h @@ -482,6 +482,8 @@ class XrayWrapper : public Base { JS::AutoIdVector& props) const override; virtual bool getBuiltinClass(JSContext* cx, JS::HandleObject wapper, js::ESClass* cls) const override; + virtual bool hasInstance(JSContext* cx, JS::HandleObject wrapper, + JS::MutableHandleValue v, bool* bp) const override; virtual const char* className(JSContext* cx, JS::HandleObject proxy) const override; static const XrayWrapper singleton; |