diff options
Diffstat (limited to 'js/src/vm')
51 files changed, 1906 insertions, 647 deletions
diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h index eeaca41a97..9ac3989885 100644 --- a/js/src/vm/ArgumentsObject.h +++ b/js/src/vm/ArgumentsObject.h @@ -275,12 +275,14 @@ class ArgumentsObject : public NativeObject { return argc; } - // True iff arguments.length has been assigned or deleted. - bool hasOverriddenLength() const { + bool hasFlags(uint32_t flags) const { const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT); - return v.toInt32() & LENGTH_OVERRIDDEN_BIT; + return v.toInt32() & flags; } + // True iff arguments.length has been assigned or deleted. + bool hasOverriddenLength() const { return hasFlags(LENGTH_OVERRIDDEN_BIT); } + void markLengthOverridden() { uint32_t v = getFixedSlot(INITIAL_LENGTH_SLOT).toInt32() | LENGTH_OVERRIDDEN_BIT; @@ -292,8 +294,7 @@ class ArgumentsObject : public NativeObject { // True iff arguments[@@iterator] has been assigned or deleted. bool hasOverriddenIterator() const { - const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT); - return v.toInt32() & ITERATOR_OVERRIDDEN_BIT; + return hasFlags(ITERATOR_OVERRIDDEN_BIT); } void markIteratorOverridden() { @@ -311,10 +312,7 @@ class ArgumentsObject : public NativeObject { static bool getArgumentsIterator(JSContext* cx, MutableHandleValue val); // True iff any element has been assigned or deleted. - bool hasOverriddenElement() const { - const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT); - return v.toInt32() & ELEMENT_OVERRIDDEN_BIT; - } + bool hasOverriddenElement() const { return hasFlags(ELEMENT_OVERRIDDEN_BIT); } void markElementOverridden() { uint32_t v = @@ -409,10 +407,7 @@ class ArgumentsObject : public NativeObject { return IsMagicScopeSlotValue(v); } - bool anyArgIsForwarded() const { - const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT); - return v.toInt32() & FORWARDED_ARGUMENTS_BIT; - } + bool anyArgIsForwarded() const { return hasFlags(FORWARDED_ARGUMENTS_BIT); } void markArgumentForwarded() { uint32_t v = @@ -504,10 +499,7 @@ class MappedArgumentsObject : public ArgumentsObject { return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>(); } - bool hasOverriddenCallee() const { - const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT); - return v.toInt32() & CALLEE_OVERRIDDEN_BIT; - } + bool hasOverriddenCallee() const { return hasFlags(CALLEE_OVERRIDDEN_BIT); } void markCalleeOverridden() { uint32_t v = diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index 72c9ebeb18..2fe4f01f8d 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -536,12 +536,7 @@ bool ArrayBufferObject::maxByteLengthGetterImpl(JSContext* cx, auto* buffer = &args.thisv().toObject().as<ArrayBufferObject>(); // Steps 4-6. - size_t maxByteLength; - if (buffer->isResizable()) { - maxByteLength = buffer->as<ResizableArrayBufferObject>().maxByteLength(); - } else { - maxByteLength = buffer->byteLength(); - } + size_t maxByteLength = buffer->maxByteLength(); MOZ_ASSERT_IF(buffer->isDetached(), maxByteLength == 0); // Step 7. @@ -914,8 +909,6 @@ void ArrayBufferObject::detach(JSContext* cx, // Update all views of the buffer to account for the buffer having been // detached, and clear the buffer's data and list of views. - // - // Typed object buffers are not exposed and cannot be detached. auto& innerViews = ObjectRealm::get(buffer).innerViews.get(); if (InnerViewTable::ViewVector* views = @@ -962,6 +955,20 @@ void ResizableArrayBufferObject::resize(size_t newByteLength) { } setByteLength(newByteLength); + + // Update all views of the buffer to account for the buffer having been + // resized. + + auto& innerViews = ObjectRealm::get(this).innerViews.get(); + if (InnerViewTable::ViewVector* views = + innerViews.maybeViewsUnbarriered(this)) { + for (auto& view : *views) { + view->notifyBufferResized(); + } + } + if (auto* view = firstView()) { + view->as<ArrayBufferViewObject>().notifyBufferResized(); + } } /* clang-format off */ @@ -1490,10 +1497,7 @@ size_t ArrayBufferObject::byteLength() const { inline size_t ArrayBufferObject::associatedBytes() const { if (isMalloced()) { - if (isResizable()) { - return as<ResizableArrayBufferObject>().maxByteLength(); - } - return byteLength(); + return maxByteLength(); } if (isMapped()) { return RoundUp(byteLength(), js::gc::SystemPageSize()); @@ -2472,7 +2476,7 @@ bool ArrayBufferObject::ensureNonInline(JSContext* cx, return true; } - size_t nbytes = buffer->byteLength(); + size_t nbytes = buffer->maxByteLength(); ArrayBufferContents copy = NewCopiedBufferContents(cx, buffer); if (!copy) { return false; diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index 17faf7682e..5aa96bf887 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -566,6 +566,12 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared { void setDataPointer(BufferContents contents); void setByteLength(size_t length); + /** + * Return the byte length for fixed-length buffers or the maximum byte length + * for resizable buffers. + */ + inline size_t maxByteLength() const; + size_t associatedBytes() const; uint32_t flags() const; @@ -703,6 +709,13 @@ class ResizableArrayBufferObject : public ArrayBufferObject { JS::Handle<ResizableArrayBufferObject*> source); }; +size_t ArrayBufferObject::maxByteLength() const { + if (isResizable()) { + return as<ResizableArrayBufferObject>().maxByteLength(); + } + return byteLength(); +} + // Create a buffer for a wasm memory, whose type is determined by // memory.indexType(). ArrayBufferObjectMaybeShared* CreateWasmBuffer(JSContext* cx, @@ -775,6 +788,7 @@ class InnerViewTable { private: friend class ArrayBufferObject; + friend class ResizableArrayBufferObject; bool addView(JSContext* cx, ArrayBufferObject* buffer, ArrayBufferViewObject* view); ViewVector* maybeViewsUnbarriered(ArrayBufferObject* buffer); diff --git a/js/src/vm/ArrayBufferViewObject.cpp b/js/src/vm/ArrayBufferViewObject.cpp index 4bcd7890c1..27004f3e2a 100644 --- a/js/src/vm/ArrayBufferViewObject.cpp +++ b/js/src/vm/ArrayBufferViewObject.cpp @@ -41,7 +41,7 @@ void ArrayBufferViewObject::trace(JSTracer* trc, JSObject* obj) { &gc::MaybeForwardedObjectAs<ResizableArrayBufferObject>(bufferObj); } if (buffer) { - size_t offset = view->byteOffset(); + size_t offset = view->dataPointerOffset(); MOZ_ASSERT_IF(!buffer->dataPointer(), offset == 0); // The data may or may not be inline with the buffer. The buffer can only @@ -69,13 +69,22 @@ void ArrayBufferViewObject::notifyBufferDetached() { setFixedSlot(DATA_SLOT, UndefinedValue()); } +void ArrayBufferViewObject::notifyBufferResized() { + MOZ_ASSERT(!isSharedMemory()); + MOZ_ASSERT(hasBuffer()); + MOZ_ASSERT(!bufferUnshared()->isLengthPinned()); + MOZ_ASSERT(bufferUnshared()->isResizable()); + + computeResizableLengthAndByteOffset(bytesPerElement()); +} + void ArrayBufferViewObject::notifyBufferMoved(uint8_t* srcBufStart, uint8_t* dstBufStart) { MOZ_ASSERT(!isSharedMemory()); MOZ_ASSERT(hasBuffer()); if (srcBufStart != dstBufStart) { - void* data = dstBufStart + byteOffset(); + void* data = dstBufStart + dataPointerOffset(); getFixedSlotRef(DATA_SLOT).unbarrieredSet(PrivateValue(data)); } } @@ -183,6 +192,76 @@ bool ArrayBufferViewObject::init(JSContext* cx, return true; } +bool ArrayBufferViewObject::initResizable(JSContext* cx, + ArrayBufferObjectMaybeShared* buffer, + size_t byteOffset, size_t length, + uint32_t bytesPerElement, + AutoLength autoLength) { + MOZ_ASSERT(buffer->isResizable()); + + if (!init(cx, buffer, byteOffset, length, bytesPerElement)) { + return false; + } + + initFixedSlot(AUTO_LENGTH_SLOT, BooleanValue(static_cast<bool>(autoLength))); + initFixedSlot(INITIAL_LENGTH_SLOT, PrivateValue(length)); + initFixedSlot(INITIAL_BYTE_OFFSET_SLOT, PrivateValue(byteOffset)); + + // Compute the actual byteLength and byteOffset for non-shared buffers. + if (!isSharedMemory()) { + computeResizableLengthAndByteOffset(bytesPerElement); + } + + MOZ_ASSERT(!isOutOfBounds(), "can't create out-of-bounds views"); + + return true; +} + +void ArrayBufferViewObject::computeResizableLengthAndByteOffset( + size_t bytesPerElement) { + MOZ_ASSERT(!isSharedMemory()); + MOZ_ASSERT(hasBuffer()); + MOZ_ASSERT(!bufferUnshared()->isLengthPinned()); + MOZ_ASSERT(bufferUnshared()->isResizable()); + + size_t byteOffsetStart = initialByteOffset(); + size_t bufferByteLength = bufferUnshared()->byteLength(); + + // Out-of-bounds if the byteOffset exceeds the buffer length. + if (byteOffsetStart > bufferByteLength) { + setFixedSlot(LENGTH_SLOT, PrivateValue(size_t(0))); + setFixedSlot(BYTEOFFSET_SLOT, PrivateValue(size_t(0))); + return; + } + + size_t length; + if (isAutoLength()) { + length = (bufferByteLength - byteOffsetStart) / bytesPerElement; + } else { + length = initialLength(); + + // Out-of-bounds if the byteOffset end index exceeds the buffer length. + size_t byteOffsetEnd = byteOffsetStart + length * bytesPerElement; + if (byteOffsetEnd > bufferByteLength) { + setFixedSlot(LENGTH_SLOT, PrivateValue(size_t(0))); + setFixedSlot(BYTEOFFSET_SLOT, PrivateValue(size_t(0))); + return; + } + } + + setFixedSlot(LENGTH_SLOT, PrivateValue(length)); + setFixedSlot(BYTEOFFSET_SLOT, PrivateValue(byteOffsetStart)); +} + +size_t ArrayBufferViewObject::bytesPerElement() const { + if (is<TypedArrayObject>()) { + return as<TypedArrayObject>().bytesPerElement(); + } + + MOZ_ASSERT(is<DataViewObject>()); + return 1; +} + bool ArrayBufferViewObject::hasResizableBuffer() const { if (auto* buffer = bufferEither()) { return buffer->isResizable(); @@ -190,6 +269,98 @@ bool ArrayBufferViewObject::hasResizableBuffer() const { return false; } +size_t ArrayBufferViewObject::dataPointerOffset() const { + // Views without a buffer have a zero offset. + if (!hasBuffer()) { + MOZ_ASSERT(byteOffsetSlotValue() == 0); + return 0; + } + + // Views on shared buffers store the offset in |byteOffset|. + if (isSharedMemory()) { + return byteOffsetSlotValue(); + } + + // Can be called during tracing, so the buffer is possibly forwarded. + const auto* bufferObj = gc::MaybeForwarded(&bufferValue().toObject()); + + // Two distinct classes are used for non-shared buffers. + MOZ_ASSERT( + gc::MaybeForwardedObjectIs<FixedLengthArrayBufferObject>(bufferObj) || + gc::MaybeForwardedObjectIs<ResizableArrayBufferObject>(bufferObj)); + + // Ensure these two classes can be casted to ArrayBufferObject. + static_assert( + std::is_base_of_v<ArrayBufferObject, FixedLengthArrayBufferObject>); + static_assert( + std::is_base_of_v<ArrayBufferObject, ResizableArrayBufferObject>); + + // Manual cast necessary because the buffer is possibly forwarded. + const auto* buffer = static_cast<const ArrayBufferObject*>(bufferObj); + + // Views on resizable buffers store the offset in |initialByteOffset|. + if (buffer->isResizable() && !buffer->isDetached()) { + return initialByteOffsetValue(); + } + + // Callers expect that this method returns zero for detached buffers. + MOZ_ASSERT_IF(buffer->isDetached(), byteOffsetSlotValue() == 0); + + // Views on fixed-length buffers store the offset in |byteOffset|. + return byteOffsetSlotValue(); +} + +mozilla::Maybe<size_t> ArrayBufferViewObject::byteOffset() const { + // |byteOffset| is set to zero for detached or out-of-bounds views, so a + // non-zero value indicates the view is in-bounds. + size_t byteOffset = byteOffsetSlotValue(); + if (byteOffset > 0) { + MOZ_ASSERT(!hasDetachedBuffer()); + MOZ_ASSERT_IF(hasResizableBuffer(), !isOutOfBounds()); + return mozilla::Some(byteOffset); + } + if (hasDetachedBufferOrIsOutOfBounds()) { + return mozilla::Nothing{}; + } + return mozilla::Some(0); +} + +mozilla::Maybe<size_t> ArrayBufferViewObject::length() const { + // |length| is set to zero for detached or out-of-bounds views, so a non-zero + // value indicates the view is in-bounds. + size_t length = lengthSlotValue(); + if (MOZ_LIKELY(length > 0)) { + MOZ_ASSERT(!hasDetachedBuffer()); + MOZ_ASSERT_IF(hasResizableBuffer(), !isOutOfBounds()); + MOZ_ASSERT(!isSharedMemory() || !hasResizableBuffer() || !isAutoLength(), + "length is zero for auto-length growable shared buffers"); + return mozilla::Some(length); + } + + if (hasDetachedBufferOrIsOutOfBounds()) { + return mozilla::Nothing{}; + } + + if (isSharedMemory()) { + auto* buffer = bufferShared(); + MOZ_ASSERT(buffer, "shared memory doesn't use inline data"); + + // Views backed by a growable SharedArrayBuffer can never get out-of-bounds, + // but we have to dynamically compute the length when the auto-length flag + // is set. + if (buffer->isGrowable() && isAutoLength()) { + size_t bufferByteLength = buffer->byteLength(); + size_t byteOffset = byteOffsetSlotValue(); + MOZ_ASSERT(byteOffset <= bufferByteLength); + MOZ_ASSERT(byteOffset == initialByteOffset(), + "views on growable shared buffers can't get out-of-bounds"); + + return mozilla::Some((bufferByteLength - byteOffset) / bytesPerElement()); + } + } + return mozilla::Some(0); +} + #if defined(DEBUG) || defined(JS_JITSPEW) void ArrayBufferViewObject::dumpOwnFields(js::JSONPrinter& json) const { json.formatProperty("length", "%zu", diff --git a/js/src/vm/ArrayBufferViewObject.h b/js/src/vm/ArrayBufferViewObject.h index c1c1ccac88..babba93f9e 100644 --- a/js/src/vm/ArrayBufferViewObject.h +++ b/js/src/vm/ArrayBufferViewObject.h @@ -7,6 +7,8 @@ #ifndef vm_ArrayBufferViewObject_h #define vm_ArrayBufferViewObject_h +#include "mozilla/Maybe.h" + #include "builtin/TypedArrayConstants.h" #include "vm/ArrayBufferObject.h" #include "vm/NativeObject.h" @@ -47,6 +49,14 @@ class ArrayBufferViewObject : public NativeObject { static constexpr size_t RESERVED_SLOTS = 4; + // Additional slots for views on resizable/growable (Shared)ArrayBufferObject. + + static const uint8_t AUTO_LENGTH_SLOT = 4; + static const uint8_t INITIAL_LENGTH_SLOT = 5; + static const uint8_t INITIAL_BYTE_OFFSET_SLOT = 6; + + static constexpr size_t RESIZABLE_RESERVED_SLOTS = 7; + #ifdef DEBUG static const uint8_t ZeroLengthArrayData = 0x4A; #endif @@ -63,6 +73,15 @@ class ArrayBufferViewObject : public NativeObject { static constexpr int dataOffset() { return NativeObject::getFixedSlotOffset(DATA_SLOT); } + static constexpr int autoLengthOffset() { + return NativeObject::getFixedSlotOffset(AUTO_LENGTH_SLOT); + } + static constexpr int initialLengthOffset() { + return NativeObject::getFixedSlotOffset(INITIAL_LENGTH_SLOT); + } + static constexpr int initialByteOffsetOffset() { + return NativeObject::getFixedSlotOffset(INITIAL_BYTE_OFFSET_SLOT); + } private: void* dataPointerEither_() const { @@ -76,10 +95,19 @@ class ArrayBufferViewObject : public NativeObject { size_t byteOffset, size_t length, uint32_t bytesPerElement); + enum class AutoLength : bool { No, Yes }; + + [[nodiscard]] bool initResizable(JSContext* cx, + ArrayBufferObjectMaybeShared* buffer, + size_t byteOffset, size_t length, + uint32_t bytesPerElement, + AutoLength autoLength); + static ArrayBufferObjectMaybeShared* ensureBufferObject( JSContext* cx, Handle<ArrayBufferViewObject*> obj); void notifyBufferDetached(); + void notifyBufferResized(); void notifyBufferMoved(uint8_t* srcBufStart, uint8_t* dstBufStart); void initDataPointer(SharedMem<uint8_t*> viewData) { @@ -156,6 +184,24 @@ class ArrayBufferViewObject : public NativeObject { bool hasResizableBuffer() const; + private: + bool hasDetachedBufferOrIsOutOfBounds() const { + // Shared buffers can't be detached or get out-of-bounds. + if (isSharedMemory()) { + return false; + } + + // A view with a null buffer has never had its buffer exposed to become + // detached or get out-of-bounds. + auto* buffer = bufferUnshared(); + if (!buffer) { + return false; + } + + return buffer->isDetached() || (buffer->isResizable() && isOutOfBounds()); + } + + public: bool isLengthPinned() const { Value buffer = bufferValue(); if (buffer.isBoolean()) { @@ -193,11 +239,75 @@ class ArrayBufferViewObject : public NativeObject { static bool ensureNonInline(JSContext* cx, JS::Handle<ArrayBufferViewObject*> view); + private: + void computeResizableLengthAndByteOffset(size_t bytesPerElement); + + size_t bytesPerElement() const; + protected: - size_t byteOffset() const { + size_t lengthSlotValue() const { + return size_t(getFixedSlot(LENGTH_SLOT).toPrivate()); + } + + size_t byteOffsetSlotValue() const { return size_t(getFixedSlot(BYTEOFFSET_SLOT).toPrivate()); } + /** + * Offset into the buffer's data-pointer. Different from |byteOffset| for + * views on non-detached resizable buffers which are currently out-of-bounds. + */ + size_t dataPointerOffset() const; + + /** + * Return the current length, or |Nothing| if the view is detached or + * out-of-bounds. + */ + mozilla::Maybe<size_t> length() const; + + public: + /** + * Return the current byteOffset, or |Nothing| if the view is detached or + * out-of-bounds. + */ + mozilla::Maybe<size_t> byteOffset() const; + + private: + size_t initialByteOffsetValue() const { + // No assertion for resizable buffers here, because this method is called + // from dataPointerOffset(), which can be called during tracing. + return size_t(getFixedSlot(INITIAL_BYTE_OFFSET_SLOT).toPrivate()); + } + + public: + // The following methods can only be called on views for resizable buffers. + + bool isAutoLength() const { + MOZ_ASSERT(hasResizableBuffer()); + return getFixedSlot(AUTO_LENGTH_SLOT).toBoolean(); + } + + size_t initialLength() const { + MOZ_ASSERT(hasResizableBuffer()); + return size_t(getFixedSlot(INITIAL_LENGTH_SLOT).toPrivate()); + } + + size_t initialByteOffset() const { + MOZ_ASSERT(hasResizableBuffer()); + return initialByteOffsetValue(); + } + + bool isOutOfBounds() const { + MOZ_ASSERT(hasResizableBuffer()); + + // The view is out-of-bounds if the length and byteOffset slots are both set + // to zero and the initial length or initial byteOffset are non-zero. If the + // initial length and initial byteOffset are both zero, the view can never + // get out-of-bounds. + return lengthSlotValue() == 0 && byteOffsetSlotValue() == 0 && + (initialLength() > 0 || initialByteOffset() > 0); + } + public: static void trace(JSTracer* trc, JSObject* obj); diff --git a/js/src/vm/BigIntType.h b/js/src/vm/BigIntType.h index 24f544cd81..fb9f4085e6 100644 --- a/js/src/vm/BigIntType.h +++ b/js/src/vm/BigIntType.h @@ -419,6 +419,7 @@ class BigInt final : public js::gc::CellWithLengthAndFlags { static JSLinearString* toStringGeneric(JSContext* cx, Handle<BigInt*>, unsigned radix); + friend struct ::JSStructuredCloneReader; // So it can call the following: static BigInt* destructivelyTrimHighZeroDigits(JSContext* cx, BigInt* x); bool absFitsInUint64() const { return digitLength() <= 64 / DigitBits; } diff --git a/js/src/vm/CharacterEncoding.cpp b/js/src/vm/CharacterEncoding.cpp index 79d28ab719..3d05275e2d 100644 --- a/js/src/vm/CharacterEncoding.cpp +++ b/js/src/vm/CharacterEncoding.cpp @@ -286,11 +286,6 @@ static bool InflateUTF8ToUTF16(JSContext* cx, const UTF8Chars& src, break; } } else { - // Non-ASCII code unit. Determine its length in bytes (n). - uint32_t n = 1; - while (v & (0x80 >> n)) { - n++; - } #define INVALID(report, arg, n2) \ do { \ @@ -315,6 +310,14 @@ static bool InflateUTF8ToUTF16(JSContext* cx, const UTF8Chars& src, } \ } while (0) + // Non-ASCII code unit. Determine its length in bytes (n). + // + // Avoid undefined behavior from passing in 0 + // (https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fclz) + // by turning on the low bit so that 0xff will set n=31-24=7, which will + // be detected as an invalid character. + uint32_t n = mozilla::CountLeadingZeroes32(~int8_t(src[i]) | 0x1) - 24; + // Check the leading byte. if (n < 2 || n > 4) { INVALID(ReportInvalidCharacter, i, 1); diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 7e81f4cb8a..d4376ec6a4 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -332,6 +332,7 @@ MACRO_(join, "join") \ MACRO2(js, "js") \ MACRO_(jsStringModule, "js-string") \ + MACRO_(json, "json") \ MACRO_(keys, "keys") \ IF_DECORATORS(MACRO_(kind, "kind")) \ MACRO_(label, "label") \ diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp index cbb14f93f2..008cfca260 100644 --- a/js/src/vm/EnvironmentObject.cpp +++ b/js/src/vm/EnvironmentObject.cpp @@ -416,6 +416,40 @@ ModuleEnvironmentObject* ModuleEnvironmentObject::create( return env; } +/* static */ +ModuleEnvironmentObject* ModuleEnvironmentObject::createSynthetic( + JSContext* cx, Handle<ModuleObject*> module) { + Rooted<SharedShape*> shape(cx, + CreateEnvironmentShapeForSyntheticModule( + cx, &class_, JSSLOT_FREE(&class_), module)); + MOZ_ASSERT(shape->getObjectClass() == &class_); + + Rooted<ModuleEnvironmentObject*> env( + cx, CreateEnvironmentObject<ModuleEnvironmentObject>(cx, shape, + TenuredObject)); + if (!env) { + return nullptr; + } + + env->initReservedSlot(MODULE_SLOT, ObjectValue(*module)); + + // Initialize this early so that we can manipulate the env object without + // causing assertions. + env->initEnclosingEnvironment(&cx->global()->lexicalEnvironment()); + + // It is not be possible to add or remove bindings from a module environment + // after this point as module code is always strict. +#ifdef DEBUG + for (ShapePropertyIter<NoGC> iter(env->shape()); !iter.done(); iter++) { + MOZ_ASSERT(!iter->configurable()); + } + MOZ_ASSERT(env->hasFlag(ObjectFlag::NotExtensible)); + MOZ_ASSERT(!env->inDictionaryMode()); +#endif + + return env; +} + ModuleObject& ModuleEnvironmentObject::module() const { return getReservedSlot(MODULE_SLOT).toObject().as<ModuleObject>(); } diff --git a/js/src/vm/EnvironmentObject.h b/js/src/vm/EnvironmentObject.h index fd60128e1f..192d8d2ce7 100644 --- a/js/src/vm/EnvironmentObject.h +++ b/js/src/vm/EnvironmentObject.h @@ -622,6 +622,8 @@ class ModuleEnvironmentObject : public EnvironmentObject { static const JSClassOps classOps_; public: + using EnvironmentObject::setAliasedBinding; + static const JSClass class_; static constexpr uint32_t RESERVED_SLOTS = 2; @@ -630,6 +632,9 @@ class ModuleEnvironmentObject : public EnvironmentObject { static ModuleEnvironmentObject* create(JSContext* cx, Handle<ModuleObject*> module); + static ModuleEnvironmentObject* createSynthetic(JSContext* cx, + Handle<ModuleObject*> module); + ModuleObject& module() const; IndirectBindingMap& importBindings() const; @@ -648,6 +653,8 @@ class ModuleEnvironmentObject : public EnvironmentObject { // `env` may be a DebugEnvironmentProxy, but not a hollow environment. static ModuleEnvironmentObject* find(JSObject* env); + uint32_t firstSyntheticValueSlot() { return RESERVED_SLOTS; } + private: static bool lookupProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleObject objp, PropertyResult* propp); diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 9c9003a2ba..6782433fd3 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -202,7 +202,7 @@ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) { return false; case JSProto_Segmenter: -# if defined(MOZ_ICU4X) && defined(NIGHTLY_BUILD) +# if defined(MOZ_ICU4X) return false; # else return true; diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 92dec6698f..a296336385 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -129,8 +129,8 @@ class GlobalObjectData { HeapPtr<JSObject*> constructor; HeapPtr<JSObject*> prototype; }; - using CtorArray = - mozilla::EnumeratedArray<JSProtoKey, JSProto_LIMIT, ConstructorWithProto>; + using CtorArray = mozilla::EnumeratedArray<JSProtoKey, ConstructorWithProto, + size_t(JSProto_LIMIT)>; CtorArray builtinConstructors; // Built-in prototypes for this global. Note that this is different from the @@ -154,8 +154,8 @@ class GlobalObjectData { Limit }; - using ProtoArray = - mozilla::EnumeratedArray<ProtoKind, ProtoKind::Limit, HeapPtr<JSObject*>>; + using ProtoArray = mozilla::EnumeratedArray<ProtoKind, HeapPtr<JSObject*>, + size_t(ProtoKind::Limit)>; ProtoArray builtinProtos; HeapPtr<GlobalScope*> emptyGlobalScope; @@ -195,8 +195,9 @@ class GlobalObjectData { // Shape for PlainObject with %Object.prototype% as proto, for each object // AllocKind. - using PlainObjectShapeArray = mozilla::EnumeratedArray< - PlainObjectSlotsKind, PlainObjectSlotsKind::Limit, HeapPtr<SharedShape*>>; + using PlainObjectShapeArray = + mozilla::EnumeratedArray<PlainObjectSlotsKind, HeapPtr<SharedShape*>, + size_t(PlainObjectSlotsKind::Limit)>; PlainObjectShapeArray plainObjectShapesWithDefaultProto; // Shape for JSFunction with %Function.prototype% as proto, for both diff --git a/js/src/vm/HelperThreadState.h b/js/src/vm/HelperThreadState.h index 8e601a385c..a43efef7af 100644 --- a/js/src/vm/HelperThreadState.h +++ b/js/src/vm/HelperThreadState.h @@ -26,13 +26,12 @@ #include <stdint.h> // uint32_t, uint64_t #include <utility> // std::move -#include "ds/Fifo.h" // Fifo -#include "frontend/CompilationStencil.h" // frontend::CompilationStencil -#include "gc/GCRuntime.h" // gc::GCRuntime -#include "js/AllocPolicy.h" // SystemAllocPolicy -#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions -#include "js/experimental/CompileScript.h" // JS::CompilationStorage -#include "js/experimental/JSStencil.h" // JS::InstantiationStorage +#include "ds/Fifo.h" // Fifo +#include "frontend/CompilationStencil.h" // frontend::CompilationStencil +#include "gc/GCRuntime.h" // gc::GCRuntime +#include "js/AllocPolicy.h" // SystemAllocPolicy +#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions +#include "js/experimental/JSStencil.h" // JS::InstantiationStorage #include "js/HelperThreadAPI.h" // JS::HelperThreadTaskCallback, JS::DispatchReason #include "js/MemoryMetrics.h" // JS::GlobalStats #include "js/ProfilingStack.h" // JS::RegisterThreadCallback, JS::UnregisterThreadCallback @@ -115,13 +114,19 @@ class GlobalHelperThreadState { PromiseHelperTaskVector; // Count of running task by each threadType. - mozilla::EnumeratedArray<ThreadType, ThreadType::THREAD_TYPE_MAX, size_t> + mozilla::EnumeratedArray<ThreadType, size_t, + size_t(ThreadType::THREAD_TYPE_MAX)> runningTaskCount; size_t totalCountRunningTasks; WriteOnceData<JS::RegisterThreadCallback> registerThread; WriteOnceData<JS::UnregisterThreadCallback> unregisterThread; + // Count of helper threads 'reserved' for parallel marking. This is used to + // prevent too many runtimes trying to mark in parallel at once. Does not stop + // threads from being used for other kinds of task, including GC tasks. + HelperThreadLockData<size_t> gcParallelMarkingThreads; + private: // The lists below are all protected by |lock|. diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index 36792b1d09..da8231c1dc 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -859,6 +859,8 @@ void GlobalHelperThreadState::finish(AutoLockHelperThreadState& lock) { return; } + MOZ_ASSERT_IF(!JSRuntime::hasLiveRuntimes(), gcParallelMarkingThreads == 0); + finishThreads(lock); // Make sure there are no Ion free tasks left. We check this here because, diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 255ab9aa94..9eec4b81dd 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -31,7 +31,6 @@ #include "builtin/Object.h" #include "builtin/Promise.h" #include "gc/GC.h" -#include "jit/AtomicOperations.h" #include "jit/BaselineJIT.h" #include "jit/Jit.h" #include "jit/JitRuntime.h" @@ -4738,15 +4737,6 @@ bool js::GreaterThanOrEqual(JSContext* cx, MutableHandleValue lhs, return GreaterThanOrEqualOperation(cx, lhs, rhs, res); } -bool js::AtomicIsLockFree(JSContext* cx, HandleValue in, int* out) { - int i; - if (!ToInt32(cx, in, &i)) { - return false; - } - *out = js::jit::AtomicOperations::isLockfreeJS(i); - return true; -} - bool js::DeleteNameOperation(JSContext* cx, Handle<PropertyName*> name, HandleObject scopeObj, MutableHandleValue res) { RootedObject scope(cx), pobj(cx); diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index d962e7f344..8ac7db7004 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -589,8 +589,6 @@ bool GreaterThan(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool GreaterThanOrEqual(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res); -bool AtomicIsLockFree(JSContext* cx, HandleValue in, int* out); - template <bool strict> bool DelPropOperation(JSContext* cx, HandleValue val, Handle<PropertyName*> name, bool* res); diff --git a/js/src/vm/Iteration.cpp b/js/src/vm/Iteration.cpp index 304054e0ec..d02f9de8cf 100644 --- a/js/src/vm/Iteration.cpp +++ b/js/src/vm/Iteration.cpp @@ -829,7 +829,7 @@ static PropertyIteratorObject* CreatePropertyIterator( bool supportsIndices, PropertyIndexVector* indices, uint32_t cacheableProtoChainLength) { MOZ_ASSERT_IF(indices, supportsIndices); - if (props.length() > NativeIterator::PropCountLimit) { + if (props.length() >= NativeIterator::PropCountLimit) { ReportAllocationOverflow(cx); return nullptr; } diff --git a/js/src/vm/JSContext-inl.h b/js/src/vm/JSContext-inl.h index f20c4b3c7d..252fefd88d 100644 --- a/js/src/vm/JSContext-inl.h +++ b/js/src/vm/JSContext-inl.h @@ -352,52 +352,6 @@ inline void JSContext::setRealmForJitExceptionHandler(JS::Realm* realm) { realm_ = realm; } -inline JSScript* JSContext::currentScript( - jsbytecode** ppc, AllowCrossRealm allowCrossRealm) const { - if (ppc) { - *ppc = nullptr; - } - - js::Activation* act = activation(); - if (!act) { - return nullptr; - } - - MOZ_ASSERT(act->cx() == this); - - // Cross-compartment implies cross-realm. - if (allowCrossRealm == AllowCrossRealm::DontAllow && - act->compartment() != compartment()) { - return nullptr; - } - - JSScript* script = nullptr; - jsbytecode* pc = nullptr; - if (act->isJit()) { - if (act->hasWasmExitFP()) { - return nullptr; - } - js::jit::GetPcScript(const_cast<JSContext*>(this), &script, &pc); - } else { - js::InterpreterFrame* fp = act->asInterpreter()->current(); - MOZ_ASSERT(!fp->runningInJit()); - script = fp->script(); - pc = act->asInterpreter()->regs().pc; - } - - MOZ_ASSERT(script->containsPC(pc)); - - if (allowCrossRealm == AllowCrossRealm::DontAllow && - script->realm() != realm()) { - return nullptr; - } - - if (ppc) { - *ppc = pc; - } - return script; -} - inline js::RuntimeCaches& JSContext::caches() { return runtime()->caches(); } template <typename T, js::AllowGC allowGC, typename... Args> diff --git a/js/src/vm/JSContext.cpp b/js/src/vm/JSContext.cpp index 4d355dc828..5a4bfa86cd 100644 --- a/js/src/vm/JSContext.cpp +++ b/js/src/vm/JSContext.cpp @@ -55,6 +55,7 @@ #include "vm/BytecodeUtil.h" // JSDVG_IGNORE_STACK #include "vm/ErrorObject.h" #include "vm/ErrorReporting.h" +#include "vm/FrameIter.h" #include "vm/JSFunction.h" #include "vm/JSObject.h" #include "vm/PlainObject.h" // js::PlainObject @@ -997,7 +998,6 @@ JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options) suppressProfilerSampling(false), tempLifoAlloc_(this, (size_t)TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), debuggerMutations(this, 0), - ionPcScriptCache(this, nullptr), status(this, JS::ExceptionStatus::None), unwrappedException_(this), unwrappedExceptionStack_(this), @@ -1263,6 +1263,34 @@ void JSContext::resetJitStackLimit() { void JSContext::initJitStackLimit() { resetJitStackLimit(); } +JSScript* JSContext::currentScript(jsbytecode** ppc, + AllowCrossRealm allowCrossRealm) { + if (ppc) { + *ppc = nullptr; + } + + // Fast path: there are no JS frames on the stack if there's no activation. + if (!activation()) { + return nullptr; + } + + FrameIter iter(this); + if (iter.done() || !iter.hasScript()) { + return nullptr; + } + + JSScript* script = iter.script(); + if (allowCrossRealm == AllowCrossRealm::DontAllow && + script->realm() != realm()) { + return nullptr; + } + + if (ppc) { + *ppc = iter.pc(); + } + return script; +} + #ifdef JS_CRASH_DIAGNOSTICS void ContextChecks::check(AbstractFramePtr frame, int argIndex) { if (frame) { diff --git a/js/src/vm/JSContext.h b/js/src/vm/JSContext.h index 1801816fa0..57aa236801 100644 --- a/js/src/vm/JSContext.h +++ b/js/src/vm/JSContext.h @@ -20,7 +20,6 @@ #include "gc/GCEnum.h" #include "gc/Memory.h" #include "irregexp/RegExpTypes.h" -#include "jit/PcScriptCache.h" #include "js/ContextOptions.h" // JS::ContextOptions #include "js/Exception.h" #include "js/GCVector.h" @@ -563,9 +562,6 @@ struct JS_PUBLIC_API JSContext : public JS::RootingContext, js::ContextData<uint32_t> debuggerMutations; - // Cache for jit::GetPcScript(). - js::ContextData<js::UniquePtr<js::jit::PcScriptCache>> ionPcScriptCache; - private: // Indicates if an exception is pending and the reason for it. js::ContextData<JS::ExceptionStatus> status; @@ -693,9 +689,9 @@ struct JS_PUBLIC_API JSContext : public JS::RootingContext, * overridden by passing AllowCrossRealm::Allow. */ enum class AllowCrossRealm { DontAllow = false, Allow = true }; - inline JSScript* currentScript( - jsbytecode** pc = nullptr, - AllowCrossRealm allowCrossRealm = AllowCrossRealm::DontAllow) const; + JSScript* currentScript( + jsbytecode** ppc = nullptr, + AllowCrossRealm allowCrossRealm = AllowCrossRealm::DontAllow); inline void minorGC(JS::GCReason reason); diff --git a/js/src/vm/JSONParser.cpp b/js/src/vm/JSONParser.cpp index addd9aa263..f151f261b5 100644 --- a/js/src/vm/JSONParser.cpp +++ b/js/src/vm/JSONParser.cpp @@ -692,8 +692,9 @@ inline bool JSONFullParseHandlerAnyChar::finishObject( if (gcHeap == gc::Heap::Tenured) { newKind = TenuredObject; } + // properties is traced in the parser; see JSONParser<CharT>::trace() JSObject* obj = NewPlainObjectWithMaybeDuplicateKeys( - cx, properties->begin(), properties->length(), newKind); + cx, Handle<IdValueVector>::fromMarkedLocation(properties), newKind); if (!obj) { return false; } diff --git a/js/src/vm/JSONParser.h b/js/src/vm/JSONParser.h index 3a3da721f6..91e33c02b3 100644 --- a/js/src/vm/JSONParser.h +++ b/js/src/vm/JSONParser.h @@ -167,7 +167,7 @@ class MOZ_STACK_CLASS JSONFullParseHandlerAnyChar { // State for an object that is currently being parsed. This includes all // the key/value pairs that have been seen so far. - using PropertyVector = JS::GCVector<IdValuePair, 10>; + using PropertyVector = IdValueVector; enum class ParseType { // Parsing a string as if by JSON.parse. diff --git a/js/src/vm/JSObject.cpp b/js/src/vm/JSObject.cpp index bafc7a4437..ea4dfeb6f7 100644 --- a/js/src/vm/JSObject.cpp +++ b/js/src/vm/JSObject.cpp @@ -3147,38 +3147,44 @@ js::gc::AllocKind JSObject::allocKindForTenure( MOZ_ASSERT(IsInsideNursery(this)); - if (canHaveFixedElements()) { - const NativeObject& nobj = as<NativeObject>(); - MOZ_ASSERT(nobj.numFixedSlots() == 0); + if (is<NativeObject>()) { + if (canHaveFixedElements()) { + const NativeObject& nobj = as<NativeObject>(); + MOZ_ASSERT(nobj.numFixedSlots() == 0); - /* Use minimal size object if we are just going to copy the pointer. */ - if (!nursery.isInside(nobj.getUnshiftedElementsHeader())) { - return gc::AllocKind::OBJECT0_BACKGROUND; - } + /* Use minimal size object if we are just going to copy the pointer. */ + if (!nursery.isInside(nobj.getUnshiftedElementsHeader())) { + return gc::AllocKind::OBJECT0_BACKGROUND; + } - size_t nelements = nobj.getDenseCapacity(); - return ForegroundToBackgroundAllocKind(GetGCArrayKind(nelements)); - } + size_t nelements = nobj.getDenseCapacity(); + return ForegroundToBackgroundAllocKind(GetGCArrayKind(nelements)); + } - if (is<JSFunction>()) { - return as<JSFunction>().getAllocKind(); - } + if (is<JSFunction>()) { + return as<JSFunction>().getAllocKind(); + } - // Fixed length typed arrays in the nursery may have a lazily allocated - // buffer, make sure there is room for the array's fixed data when moving the - // array. - if (is<FixedLengthTypedArrayObject>() && - !as<FixedLengthTypedArrayObject>().hasBuffer()) { - gc::AllocKind allocKind; - if (as<FixedLengthTypedArrayObject>().hasInlineElements()) { - size_t nbytes = as<FixedLengthTypedArrayObject>().byteLength(); - allocKind = FixedLengthTypedArrayObject::AllocKindForLazyBuffer(nbytes); - } else { - allocKind = GetGCObjectKind(getClass()); + // Fixed length typed arrays in the nursery may have a lazily allocated + // buffer, make sure there is room for the array's fixed data when moving + // the array. + if (is<FixedLengthTypedArrayObject>() && + !as<FixedLengthTypedArrayObject>().hasBuffer()) { + gc::AllocKind allocKind; + if (as<FixedLengthTypedArrayObject>().hasInlineElements()) { + size_t nbytes = as<FixedLengthTypedArrayObject>().byteLength(); + allocKind = FixedLengthTypedArrayObject::AllocKindForLazyBuffer(nbytes); + } else { + allocKind = GetGCObjectKind(getClass()); + } + return ForegroundToBackgroundAllocKind(allocKind); } - return ForegroundToBackgroundAllocKind(allocKind); + + return as<NativeObject>().allocKindForTenure(); } + // Handle all non-native objects. + // Proxies that are CrossCompartmentWrappers may be nursery allocated. if (is<ProxyObject>()) { return as<ProxyObject>().allocKindForTenure(); @@ -3194,13 +3200,9 @@ js::gc::AllocKind JSObject::allocKindForTenure( // WasmArrayObjects sometimes have a variable-length tail which contains the // data for small arrays. Make sure we copy it all over to the new object. - if (is<WasmArrayObject>()) { - gc::AllocKind allocKind = as<WasmArrayObject>().allocKind(); - return allocKind; - } - - // All nursery allocatable non-native objects are handled above. - return as<NativeObject>().allocKindForTenure(); + MOZ_ASSERT(is<WasmArrayObject>()); + gc::AllocKind allocKind = as<WasmArrayObject>().allocKind(); + return allocKind; } void JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp index 6ea871ad42..7c3ed975a0 100644 --- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -3245,8 +3245,10 @@ void JSScript::resetWarmUpCounterToDelayIonCompilation() { #if defined(DEBUG) || defined(JS_JITSPEW) void BaseScript::dumpStringContent(js::GenericPrinter& out) const { - out.printf("%s:%u:%u @ 0x%p", filename() ? filename() : "<null>", lineno(), - column().oneOriginValue(), this); + StringEscape esc('"'); + EscapePrinter ep(out, esc); + ep.printf("%s:%u:%u @ 0x%p", filename() ? filename() : "<null>", lineno(), + column().oneOriginValue(), this); } void JSScript::dump(JSContext* cx) { diff --git a/js/src/vm/MemoryMetrics.cpp b/js/src/vm/MemoryMetrics.cpp index 3a852cf9ea..c9c43fceed 100644 --- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -211,11 +211,12 @@ static void StatsZoneCallback(JSRuntime* rt, void* data, Zone* zone, rtStats->currZoneStats = &zStats; zone->addSizeOfIncludingThis( - rtStats->mallocSizeOf_, &zStats.code, &zStats.regexpZone, &zStats.jitZone, - &zStats.cacheIRStubs, &zStats.uniqueIdMap, &zStats.initialPropMapTable, - &zStats.shapeTables, &rtStats->runtime.atomsMarkBitmaps, - &zStats.compartmentObjects, &zStats.crossCompartmentWrappersTables, - &zStats.compartmentsPrivateData, &zStats.scriptCountsMap); + rtStats->mallocSizeOf_, &zStats.zoneObject, &zStats.code, + &zStats.regexpZone, &zStats.jitZone, &zStats.cacheIRStubs, + &zStats.uniqueIdMap, &zStats.initialPropMapTable, &zStats.shapeTables, + &rtStats->runtime.atomsMarkBitmaps, &zStats.compartmentObjects, + &zStats.crossCompartmentWrappersTables, &zStats.compartmentsPrivateData, + &zStats.scriptCountsMap); } static void StatsRealmCallback(JSContext* cx, void* data, Realm* realm, diff --git a/js/src/vm/Modules.cpp b/js/src/vm/Modules.cpp index 87546cf280..f461e7bec1 100644 --- a/js/src/vm/Modules.cpp +++ b/js/src/vm/Modules.cpp @@ -15,7 +15,9 @@ #include "jstypes.h" // JS_PUBLIC_API +#include "builtin/JSON.h" // js::ParseJSONWithReviver #include "builtin/ModuleObject.h" // js::FinishDynamicModuleImport, js::{,Requested}ModuleObject +#include "builtin/Promise.h" // js::CreatePromiseObjectForAsync, js::AsyncFunctionReturned #include "ds/Sort.h" #include "frontend/BytecodeCompiler.h" // js::frontend::CompileModule #include "frontend/FrontendContext.h" // js::AutoReportFrontendContext @@ -33,6 +35,8 @@ #include "vm/JSAtomUtils-inl.h" // AtomToId #include "vm/JSContext-inl.h" // JSContext::{c,releaseC}heck +#include "vm/JSObject-inl.h" +#include "vm/NativeObject-inl.h" using namespace js; @@ -120,6 +124,46 @@ JS_PUBLIC_API JSObject* JS::CompileModule(JSContext* cx, return CompileModuleHelper(cx, options, srcBuf); } +JS_PUBLIC_API JSObject* JS::CompileJsonModule( + JSContext* cx, const ReadOnlyCompileOptions& options, + SourceText<char16_t>& srcBuf) { + MOZ_ASSERT(!cx->zone()->isAtomsZone()); + AssertHeapIsIdle(); + CHECK_THREAD(cx); + + JS::RootedValue jsonValue(cx); + auto charRange = + mozilla::Range<const char16_t>(srcBuf.get(), srcBuf.length()); + if (!js::ParseJSONWithReviver(cx, charRange, NullHandleValue, &jsonValue)) { + return nullptr; + } + + Rooted<ExportNameVector> exportNames(cx); + if (!exportNames.reserve(1)) { + return nullptr; + } + exportNames.infallibleAppend(cx->names().default_); + + Rooted<ModuleObject*> moduleObject( + cx, ModuleObject::createSynthetic(cx, &exportNames)); + if (!moduleObject) { + return nullptr; + } + + Rooted<GCVector<Value>> exportValues(cx, GCVector<Value>(cx)); + if (!exportValues.reserve(1)) { + return nullptr; + } + exportValues.infallibleAppend(jsonValue); + + if (!ModuleObject::createSyntheticEnvironment(cx, moduleObject, + exportValues)) { + return nullptr; + } + + return moduleObject; +} + JS_PUBLIC_API void JS::SetModulePrivate(JSObject* module, const Value& value) { JSRuntime* rt = module->zone()->runtimeFromMainThread(); module->as<ModuleObject>().scriptSourceObject()->setPrivate(rt, value); @@ -150,6 +194,10 @@ JS_PUBLIC_API bool JS::ModuleEvaluate(JSContext* cx, CHECK_THREAD(cx); cx->releaseCheck(moduleRecord); + if (moduleRecord.as<ModuleObject>()->hasSyntheticModuleFields()) { + return SyntheticModuleEvaluate(cx, moduleRecord.as<ModuleObject>(), rval); + } + return js::ModuleEvaluate(cx, moduleRecord.as<ModuleObject>(), rval); } @@ -312,6 +360,10 @@ static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module, Handle<JSAtom*> exportName, MutableHandle<ResolveSet> resolveSet, MutableHandle<Value> result); +static bool SyntheticModuleResolveExport(JSContext* cx, + Handle<ModuleObject*> module, + Handle<JSAtom*> exportName, + MutableHandle<Value> result); static ModuleNamespaceObject* ModuleNamespaceCreate( JSContext* cx, Handle<ModuleObject*> module, MutableHandle<UniquePtr<ExportNameVector>> exports); @@ -345,7 +397,7 @@ static const char* ModuleStatusName(ModuleStatus status) { } } -static bool ContainsElement(Handle<ExportNameVector> list, JSAtom* atom) { +static bool ContainsElement(const ExportNameVector& list, JSAtom* atom) { for (JSAtom* a : list) { if (a == atom) { return true; @@ -378,6 +430,20 @@ static size_t CountElements(Handle<ModuleVector> stack, ModuleObject* module) { } #endif +// https://tc39.es/proposal-json-modules/#sec-smr-getexportednames +static bool SyntheticModuleGetExportedNames( + JSContext* cx, Handle<ModuleObject*> module, + MutableHandle<ExportNameVector> exportedNames) { + MOZ_ASSERT(exportedNames.empty()); + + if (!exportedNames.appendAll(module->syntheticExportNames())) { + ReportOutOfMemory(cx); + return false; + } + + return true; +} + // https://tc39.es/ecma262/#sec-getexportednames // ES2023 16.2.1.6.2 GetExportedNames static bool ModuleGetExportedNames( @@ -387,6 +453,10 @@ static bool ModuleGetExportedNames( // Step 4. Let exportedNames be a new empty List. MOZ_ASSERT(exportedNames.empty()); + if (module->hasSyntheticModuleFields()) { + return SyntheticModuleGetExportedNames(cx, module, exportedNames); + } + // Step 2. If exportStarSet contains module, then: if (exportStarSet.has(module)) { // Step 2.a. We've reached the starting point of an export * circularity. @@ -482,12 +552,10 @@ static ModuleObject* HostResolveImportedModule( if (!requestedModule) { return nullptr; } - if (requestedModule->status() < expectedMinimumStatus) { ThrowUnexpectedModuleStatus(cx, requestedModule->status()); return nullptr; } - return requestedModule; } @@ -510,6 +578,10 @@ static ModuleObject* HostResolveImportedModule( bool js::ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module, Handle<JSAtom*> exportName, MutableHandle<Value> result) { + if (module->hasSyntheticModuleFields()) { + return ::SyntheticModuleResolveExport(cx, module, exportName, result); + } + // Step 1. If resolveSet is not present, set resolveSet to a new empty List. Rooted<ResolveSet> resolveSet(cx); @@ -683,6 +755,22 @@ static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module, return true; } +// https://tc39.es/proposal-json-modules/#sec-smr-resolveexport +static bool SyntheticModuleResolveExport(JSContext* cx, + Handle<ModuleObject*> module, + Handle<JSAtom*> exportName, + MutableHandle<Value> result) { + // Step 2. If module.[[ExportNames]] does not contain exportName, return null. + if (!ContainsElement(module->syntheticExportNames(), exportName)) { + result.setNull(); + return true; + } + + // Step 3. Return ResolvedBinding Record { [[Module]]: module, + // [[BindingName]]: exportName }. + return CreateResolvedBindingObject(cx, module, exportName, result); +} + // https://tc39.es/ecma262/#sec-getmodulenamespace // ES2023 16.2.1.10 GetModuleNamespace ModuleNamespaceObject* js::GetOrCreateModuleNamespace( @@ -1097,6 +1185,14 @@ bool js::ModuleLink(JSContext* cx, Handle<ModuleObject*> module) { static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module, MutableHandle<ModuleVector> stack, size_t index, size_t* indexOut) { + // Step 1. If module is not a Cyclic Module Record, then + if (!module->hasCyclicModuleFields()) { + // Step 1.a. Perform ? module.Link(). (Skipped) + // Step 2.b. Return index. + *indexOut = index; + return true; + } + // Step 2. If module.[[Status]] is linking, linked, evaluating-async, or // evaluated, then: if (module->status() == ModuleStatus::Linking || @@ -1155,25 +1251,29 @@ static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module, } // Step 9.c. If requiredModule is a Cyclic Module Record, then: - // Step 9.c.i. Assert: requiredModule.[[Status]] is either linking, linked, - // evaluating-async, or evaluated. - MOZ_ASSERT(requiredModule->status() == ModuleStatus::Linking || - requiredModule->status() == ModuleStatus::Linked || - requiredModule->status() == ModuleStatus::EvaluatingAsync || - requiredModule->status() == ModuleStatus::Evaluated); - - // Step 9.c.ii. Assert: requiredModule.[[Status]] is linking if and only if - // requiredModule is in stack. - MOZ_ASSERT((requiredModule->status() == ModuleStatus::Linking) == - ContainsElement(stack, requiredModule)); - - // Step 9.c.iii. If requiredModule.[[Status]] is linking, then: - if (requiredModule->status() == ModuleStatus::Linking) { - // Step 9.c.iii.1. Set module.[[DFSAncestorIndex]] to - // min(module.[[DFSAncestorIndex]], - // requiredModule.[[DFSAncestorIndex]]). - module->setDfsAncestorIndex(std::min(module->dfsAncestorIndex(), - requiredModule->dfsAncestorIndex())); + if (requiredModule->hasCyclicModuleFields()) { + // Step 9.c.i. Assert: requiredModule.[[Status]] is either linking, + // linked, + // evaluating-async, or evaluated. + MOZ_ASSERT(requiredModule->status() == ModuleStatus::Linking || + requiredModule->status() == ModuleStatus::Linked || + requiredModule->status() == ModuleStatus::EvaluatingAsync || + requiredModule->status() == ModuleStatus::Evaluated); + + // Step 9.c.ii. Assert: requiredModule.[[Status]] is linking if and only + // if + // requiredModule is in stack. + MOZ_ASSERT((requiredModule->status() == ModuleStatus::Linking) == + ContainsElement(stack, requiredModule)); + + // Step 9.c.iii. If requiredModule.[[Status]] is linking, then: + if (requiredModule->status() == ModuleStatus::Linking) { + // Step 9.c.iii.1. Set module.[[DFSAncestorIndex]] to + // min(module.[[DFSAncestorIndex]], + // requiredModule.[[DFSAncestorIndex]]). + module->setDfsAncestorIndex(std::min( + module->dfsAncestorIndex(), requiredModule->dfsAncestorIndex())); + } } } @@ -1213,6 +1313,28 @@ static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module, return true; } +bool js::SyntheticModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg, + MutableHandle<Value> result) { + // Steps 1-12 happens elsewhere in the engine. + + // Step 13. Let pc be ! NewPromiseCapability(%Promise%). + Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectForAsync(cx)); + if (!resultPromise) { + return false; + } + + // Step 14. IfAbruptRejectPromise(result, pc) (Skipped) + + // 15. Perform ! pc.[[Resolve]](result). + if (!AsyncFunctionReturned(cx, resultPromise, result)) { + return false; + } + + // 16. Return pc.[[Promise]]. + result.set(ObjectValue(*resultPromise)); + return true; +} + // https://tc39.es/ecma262/#sec-moduleevaluation // ES2023 16.2.1.5.2 Evaluate bool js::ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg, @@ -1349,6 +1471,17 @@ bool js::ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg, static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module, MutableHandle<ModuleVector> stack, size_t index, size_t* indexOut) { + // Step 1: If module is not a Cyclic Module Record, then + if (!module->hasCyclicModuleFields()) { + // Step 1.a. Let promise be ! module.Evaluate(). (Skipped) + // Step 1.b. Assert: promise.[[PromiseState]] is not pending. (Skipped) + // Step 1.c. If promise.[[PromiseState]] is rejected, then (Skipped) + // Step 1.c.i Return ThrowCompletion(promise.[[PromiseResult]]). (Skipped) + // Step 1.d. Return index. + *indexOut = index; + return true; + } + // Step 2. If module.[[Status]] is evaluating-async or evaluated, then: if (module->status() == ModuleStatus::EvaluatingAsync || module->status() == ModuleStatus::Evaluated) { @@ -1419,55 +1552,59 @@ static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module, } // Step 11.d. If requiredModule is a Cyclic Module Record, then: - // Step 11.d.i. Assert: requiredModule.[[Status]] is either evaluating, - // evaluating-async, or evaluated. - MOZ_ASSERT(requiredModule->status() == ModuleStatus::Evaluating || - requiredModule->status() == ModuleStatus::EvaluatingAsync || - requiredModule->status() == ModuleStatus::Evaluated); - - // Step 11.d.ii. Assert: requiredModule.[[Status]] is evaluating if and only - // if requiredModule is in stack. - MOZ_ASSERT((requiredModule->status() == ModuleStatus::Evaluating) == - ContainsElement(stack, requiredModule)); - - // Step 11.d.iii. If requiredModule.[[Status]] is evaluating, then: - if (requiredModule->status() == ModuleStatus::Evaluating) { - // Step 11.d.iii.1. Set module.[[DFSAncestorIndex]] to - // min(module.[[DFSAncestorIndex]], - // requiredModule.[[DFSAncestorIndex]]). - module->setDfsAncestorIndex(std::min(module->dfsAncestorIndex(), - requiredModule->dfsAncestorIndex())); - } else { - // Step 11.d.iv. Else: - // Step 11.d.iv.1. Set requiredModule to requiredModule.[[CycleRoot]]. - requiredModule = requiredModule->getCycleRoot(); - - // Step 11.d.iv.2. Assert: requiredModule.[[Status]] is evaluating-async - // or evaluated. - MOZ_ASSERT(requiredModule->status() >= ModuleStatus::EvaluatingAsync || + if (requiredModule->hasCyclicModuleFields()) { + // Step 11.d.i. Assert: requiredModule.[[Status]] is either evaluating, + // evaluating-async, or evaluated. + MOZ_ASSERT(requiredModule->status() == ModuleStatus::Evaluating || + requiredModule->status() == ModuleStatus::EvaluatingAsync || requiredModule->status() == ModuleStatus::Evaluated); - // Step 11.d.iv.3. If requiredModule.[[EvaluationError]] is not empty, - // return ? requiredModule.[[EvaluationError]]. - if (requiredModule->hadEvaluationError()) { - Rooted<Value> error(cx, requiredModule->evaluationError()); - cx->setPendingException(error, ShouldCaptureStack::Maybe); - return false; + // Step 11.d.ii. Assert: requiredModule.[[Status]] is evaluating if and + // only if requiredModule is in stack. + MOZ_ASSERT((requiredModule->status() == ModuleStatus::Evaluating) == + ContainsElement(stack, requiredModule)); + + // Step 11.d.iii. If requiredModule.[[Status]] is evaluating, then: + if (requiredModule->status() == ModuleStatus::Evaluating) { + // Step 11.d.iii.1. Set module.[[DFSAncestorIndex]] to + // min(module.[[DFSAncestorIndex]], + // requiredModule.[[DFSAncestorIndex]]). + module->setDfsAncestorIndex(std::min( + module->dfsAncestorIndex(), requiredModule->dfsAncestorIndex())); + } else { + // Step 11.d.iv. Else: + // Step 11.d.iv.1. Set requiredModule to requiredModule.[[CycleRoot]]. + requiredModule = requiredModule->getCycleRoot(); + + // Step 11.d.iv.2. Assert: requiredModule.[[Status]] is evaluating-async + // or evaluated. + MOZ_ASSERT(requiredModule->status() >= ModuleStatus::EvaluatingAsync || + requiredModule->status() == ModuleStatus::Evaluated); + + // Step 11.d.iv.3. If requiredModule.[[EvaluationError]] is not empty, + // return ? requiredModule.[[EvaluationError]]. + if (requiredModule->hadEvaluationError()) { + Rooted<Value> error(cx, requiredModule->evaluationError()); + cx->setPendingException(error, ShouldCaptureStack::Maybe); + return false; + } } - } - // Step 11.d.v. If requiredModule.[[AsyncEvaluation]] is true, then: - if (requiredModule->isAsyncEvaluating() && - requiredModule->status() != ModuleStatus::Evaluated) { - // Step 11.d.v.2. Append module to requiredModule.[[AsyncParentModules]]. - if (!ModuleObject::appendAsyncParentModule(cx, requiredModule, module)) { - return false; - } + // Step 11.d.v. If requiredModule.[[AsyncEvaluation]] is true, then: + if (requiredModule->isAsyncEvaluating() && + requiredModule->status() != ModuleStatus::Evaluated) { + // Step 11.d.v.2. Append module to + // requiredModule.[[AsyncParentModules]]. + if (!ModuleObject::appendAsyncParentModule(cx, requiredModule, + module)) { + return false; + } - // Step 11.d.v.1. Set module.[[PendingAsyncDependencies]] to - // module.[[PendingAsyncDependencies]] + 1. - module->setPendingAsyncDependencies(module->pendingAsyncDependencies() + - 1); + // Step 11.d.v.1. Set module.[[PendingAsyncDependencies]] to + // module.[[PendingAsyncDependencies]] + 1. + module->setPendingAsyncDependencies(module->pendingAsyncDependencies() + + 1); + } } } diff --git a/js/src/vm/Modules.h b/js/src/vm/Modules.h index 18b97ac3d7..21aff46b90 100644 --- a/js/src/vm/Modules.h +++ b/js/src/vm/Modules.h @@ -34,6 +34,8 @@ bool ModuleLink(JSContext* cx, Handle<ModuleObject*> module); // Start evaluating the module. If TLA is enabled, result will be a promise. bool ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> module, MutableHandle<Value> result); +bool SyntheticModuleEvaluate(JSContext* cx, Handle<ModuleObject*> module, + MutableHandle<Value> result); void AsyncModuleExecutionFulfilled(JSContext* cx, Handle<ModuleObject*> module); diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index c952e1b40a..640a185981 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -943,8 +943,8 @@ bool NativeObject::growElements(JSContext* cx, uint32_t reqCapacity) { // For arrays with writable length, and all non-Array objects, call // `NativeObject::goodElementsAllocationAmount()` to determine the // amount to allocate from the the requested capacity and existing length. - if (!goodElementsAllocationAmount(cx, reqCapacity + numShifted, - getElementsHeader()->length, + uint32_t length = is<ArrayObject>() ? as<ArrayObject>().length() : 0; + if (!goodElementsAllocationAmount(cx, reqCapacity + numShifted, length, &newAllocated)) { return false; } diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index d71ca98fd0..438f361bcf 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -2937,7 +2937,8 @@ /* * Push the number of actual arguments as Int32Value. * - * This is emitted for the ArgumentsLength() intrinsic in self-hosted code. + * This is emitted for the ArgumentsLength() intrinsic in self-hosted code, + * and if the script uses only arguments.length. * * Category: Variables and scopes * Type: Getting binding values diff --git a/js/src/vm/PlainObject.cpp b/js/src/vm/PlainObject.cpp index 8341636796..0eef3acbce 100644 --- a/js/src/vm/PlainObject.cpp +++ b/js/src/vm/PlainObject.cpp @@ -207,16 +207,15 @@ void js::NewPlainObjectWithPropsCache::add(SharedShape* shape) { entries_[0] = shape; } -static bool ShapeMatches(IdValuePair* properties, size_t nproperties, - SharedShape* shape) { - if (shape->slotSpan() != nproperties) { +static bool ShapeMatches(Handle<IdValueVector> properties, SharedShape* shape) { + if (shape->slotSpan() != properties.length()) { return false; } SharedShapePropertyIter<NoGC> iter(shape); - for (size_t i = nproperties; i > 0; i--) { + for (size_t i = properties.length(); i > 0; i--) { MOZ_ASSERT(iter->isDataProperty()); MOZ_ASSERT(iter->flags() == PropertyFlags::defaultDataPropFlags); - if (properties[i - 1].id != iter->key()) { + if (properties[i - 1].get().id != iter->key()) { return false; } iter++; @@ -226,10 +225,10 @@ static bool ShapeMatches(IdValuePair* properties, size_t nproperties, } SharedShape* js::NewPlainObjectWithPropsCache::lookup( - IdValuePair* properties, size_t nproperties) const { + Handle<IdValueVector> properties) const { for (size_t i = 0; i < NumEntries; i++) { SharedShape* shape = entries_[i]; - if (shape && ShapeMatches(properties, nproperties, shape)) { + if (shape && ShapeMatches(properties, shape)) { return shape; } } @@ -239,35 +238,33 @@ SharedShape* js::NewPlainObjectWithPropsCache::lookup( enum class KeysKind { UniqueNames, Unknown }; template <KeysKind Kind> -static PlainObject* NewPlainObjectWithProperties(JSContext* cx, - IdValuePair* properties, - size_t nproperties, - NewObjectKind newKind) { +static PlainObject* NewPlainObjectWithProperties( + JSContext* cx, Handle<IdValueVector> properties, NewObjectKind newKind) { auto& cache = cx->realm()->newPlainObjectWithPropsCache; // If we recently created an object with these properties, we can use that // Shape directly. - if (SharedShape* shape = cache.lookup(properties, nproperties)) { + if (SharedShape* shape = cache.lookup(properties)) { Rooted<SharedShape*> shapeRoot(cx, shape); PlainObject* obj = PlainObject::createWithShape(cx, shapeRoot, newKind); if (!obj) { return nullptr; } - MOZ_ASSERT(obj->slotSpan() == nproperties); - for (size_t i = 0; i < nproperties; i++) { - obj->initSlot(i, properties[i].value); + MOZ_ASSERT(obj->slotSpan() == properties.length()); + for (size_t i = 0; i < properties.length(); i++) { + obj->initSlot(i, properties[i].get().value); } return obj; } - gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties); + gc::AllocKind allocKind = gc::GetGCObjectKind(properties.length()); Rooted<PlainObject*> obj(cx, NewPlainObjectWithAllocKind(cx, allocKind, newKind)); if (!obj) { return nullptr; } - if (nproperties == 0) { + if (properties.empty()) { return obj; } @@ -275,9 +272,9 @@ static PlainObject* NewPlainObjectWithProperties(JSContext* cx, Rooted<Value> value(cx); bool canCache = true; - for (size_t i = 0; i < nproperties; i++) { - key = properties[i].id; - value = properties[i].value; + for (const auto& prop : properties) { + key = prop.id; + value = prop.value; // Integer keys may need to be stored in dense elements. This is uncommon so // just fall back to NativeDefineDataProperty. @@ -314,7 +311,7 @@ static PlainObject* NewPlainObjectWithProperties(JSContext* cx, if (canCache && !obj->inDictionaryMode()) { MOZ_ASSERT(obj->getDenseInitializedLength() == 0); - MOZ_ASSERT(obj->slotSpan() == nproperties); + MOZ_ASSERT(obj->slotSpan() == properties.length()); cache.add(obj->sharedShape()); } @@ -322,17 +319,14 @@ static PlainObject* NewPlainObjectWithProperties(JSContext* cx, } PlainObject* js::NewPlainObjectWithUniqueNames(JSContext* cx, - IdValuePair* properties, - size_t nproperties, + Handle<IdValueVector> properties, NewObjectKind newKind) { - return NewPlainObjectWithProperties<KeysKind::UniqueNames>( - cx, properties, nproperties, newKind); + return NewPlainObjectWithProperties<KeysKind::UniqueNames>(cx, properties, + newKind); } -PlainObject* js::NewPlainObjectWithMaybeDuplicateKeys(JSContext* cx, - IdValuePair* properties, - size_t nproperties, - NewObjectKind newKind) { +PlainObject* js::NewPlainObjectWithMaybeDuplicateKeys( + JSContext* cx, Handle<IdValueVector> properties, NewObjectKind newKind) { return NewPlainObjectWithProperties<KeysKind::Unknown>(cx, properties, - nproperties, newKind); + newKind); } diff --git a/js/src/vm/PlainObject.h b/js/src/vm/PlainObject.h index 5dcbe29a46..aa2a202fa0 100644 --- a/js/src/vm/PlainObject.h +++ b/js/src/vm/PlainObject.h @@ -7,6 +7,7 @@ #ifndef vm_PlainObject_h #define vm_PlainObject_h +#include "ds/IdValuePair.h" #include "gc/AllocKind.h" // js::gc::AllocKind #include "js/Class.h" // JSClass #include "js/RootingAPI.h" // JS::Handle @@ -98,13 +99,13 @@ extern PlainObject* NewPlainObjectWithProtoAndAllocKind( // Create a plain object with the given properties. The list must not contain // duplicate keys or integer keys. extern PlainObject* NewPlainObjectWithUniqueNames( - JSContext* cx, IdValuePair* properties, size_t nproperties, + JSContext* cx, Handle<IdValueVector> properties, NewObjectKind newKind = GenericObject); // Create a plain object with the given properties. The list may contain integer // keys or duplicate keys. extern PlainObject* NewPlainObjectWithMaybeDuplicateKeys( - JSContext* cx, IdValuePair* properties, size_t nproperties, + JSContext* cx, Handle<IdValueVector> properties, NewObjectKind newKind = GenericObject); } // namespace js diff --git a/js/src/vm/PortableBaselineInterpret.cpp b/js/src/vm/PortableBaselineInterpret.cpp index e2acaf2d7b..2990942dc6 100644 --- a/js/src/vm/PortableBaselineInterpret.cpp +++ b/js/src/vm/PortableBaselineInterpret.cpp @@ -33,6 +33,8 @@ #include "jit/JitScript.h" #include "jit/JSJitFrameIter.h" #include "jit/VMFunctions.h" +#include "proxy/DeadObjectProxy.h" +#include "proxy/DOMProxy.h" #include "vm/AsyncFunction.h" #include "vm/AsyncIteration.h" #include "vm/EnvironmentObject.h" @@ -448,6 +450,12 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, goto cacheop_##name; \ } +#define PREDICT_RETURN() \ + if (icregs.cacheIRReader.peekOp() == CacheOp::ReturnFromIC) { \ + TRACE_PRINTF("stub successful, predicted return\n"); \ + return ICInterpretOpResult::Return; \ + } + CacheOp cacheop; DISPATCH_CACHEOP(); @@ -498,6 +506,15 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(GuardIsNotUninitializedLexical) { + ValOperandId valId = icregs.cacheIRReader.valOperandId(); + Value val = Value::fromRawBits(icregs.icVals[valId.id()]); + if (val == MagicValue(JS_UNINITIALIZED_LEXICAL)) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(GuardToBoolean) { ValOperandId inputId = icregs.cacheIRReader.valOperandId(); Value v = Value::fromRawBits(icregs.icVals[inputId.id()]); @@ -565,6 +582,15 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(GuardToNonGCThing) { + ValOperandId inputId = icregs.cacheIRReader.valOperandId(); + Value input = Value::fromRawBits(icregs.icVals[inputId.id()]); + if (input.isGCThing()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(GuardBooleanToInt32) { ValOperandId inputId = icregs.cacheIRReader.valOperandId(); Int32OperandId resultId = icregs.cacheIRReader.int32OperandId(); @@ -595,6 +621,36 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } + CACHEOP_CASE(Int32ToIntPtr) { + Int32OperandId inputId = icregs.cacheIRReader.int32OperandId(); + IntPtrOperandId resultId = icregs.cacheIRReader.intPtrOperandId(); + BOUNDSCHECK(resultId); + int32_t input = int32_t(icregs.icVals[inputId.id()]); + // Note that this must sign-extend to pointer width: + icregs.icVals[resultId.id()] = intptr_t(input); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardToInt32ModUint32) { + ValOperandId inputId = icregs.cacheIRReader.valOperandId(); + Int32OperandId resultId = icregs.cacheIRReader.int32OperandId(); + BOUNDSCHECK(resultId); + Value input = Value::fromRawBits(icregs.icVals[inputId.id()]); + if (input.isInt32()) { + icregs.icVals[resultId.id()] = Int32Value(input.toInt32()).asRawBits(); + DISPATCH_CACHEOP(); + } else if (input.isDouble()) { + double doubleVal = input.toDouble(); + // Accept any double that fits in an int64_t but truncate the top 32 bits. + if (doubleVal >= double(INT64_MIN) && doubleVal <= double(INT64_MAX)) { + icregs.icVals[resultId.id()] = + Int32Value(int64_t(doubleVal)).asRawBits(); + DISPATCH_CACHEOP(); + } + } + return ICInterpretOpResult::NextIC; + } + CACHEOP_CASE(GuardNonDoubleType) { ValOperandId inputId = icregs.cacheIRReader.valOperandId(); ValueType type = icregs.cacheIRReader.valueType(); @@ -668,15 +724,24 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, ObjOperandId objId = icregs.cacheIRReader.objOperandId(); uint32_t protoOffset = icregs.cacheIRReader.stubOffset(); JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); - uintptr_t expectedProto = - cstub->stubInfo()->getStubRawWord(cstub, protoOffset); - if (reinterpret_cast<uintptr_t>(obj->staticPrototype()) != expectedProto) { + JSObject* proto = reinterpret_cast<JSObject*>( + cstub->stubInfo()->getStubRawWord(cstub, protoOffset)); + if (obj->staticPrototype() != proto) { return ICInterpretOpResult::NextIC; } PREDICT_NEXT(LoadProto); DISPATCH_CACHEOP(); } + CACHEOP_CASE(GuardNullProto) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (obj->taggedProto().raw()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(GuardClass) { ObjOperandId objId = icregs.cacheIRReader.objOperandId(); GuardClassKind kind = icregs.cacheIRReader.guardClassKind(); @@ -697,16 +762,31 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } break; + case GuardClassKind::ResizableArrayBuffer: + if (object->getClass() != &ResizableArrayBufferObject::class_) { + return ICInterpretOpResult::NextIC; + } + break; case GuardClassKind::FixedLengthSharedArrayBuffer: if (object->getClass() != &FixedLengthSharedArrayBufferObject::class_) { return ICInterpretOpResult::NextIC; } break; + case GuardClassKind::GrowableSharedArrayBuffer: + if (object->getClass() != &GrowableSharedArrayBufferObject::class_) { + return ICInterpretOpResult::NextIC; + } + break; case GuardClassKind::FixedLengthDataView: if (object->getClass() != &FixedLengthDataViewObject::class_) { return ICInterpretOpResult::NextIC; } break; + case GuardClassKind::ResizableDataView: + if (object->getClass() != &ResizableDataViewObject::class_) { + return ICInterpretOpResult::NextIC; + } + break; case GuardClassKind::MappedArguments: if (object->getClass() != &MappedArgumentsObject::class_) { return ICInterpretOpResult::NextIC; @@ -747,6 +827,18 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(GuardAnyClass) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t claspOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + JSClass* clasp = reinterpret_cast<JSClass*>( + cstub->stubInfo()->getStubRawWord(cstub, claspOffset)); + if (obj->getClass() != clasp) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(GuardGlobalGeneration) { uint32_t expectedOffset = icregs.cacheIRReader.stubOffset(); uint32_t generationAddrOffset = icregs.cacheIRReader.stubOffset(); @@ -760,12 +852,131 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(HasClassResult) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t claspOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + JSClass* clasp = reinterpret_cast<JSClass*>( + cstub->stubInfo()->getStubRawWord(cstub, claspOffset)); + icregs.icResult = BooleanValue(obj->getClass() == clasp).asRawBits(); + PREDICT_RETURN(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardCompartment) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t globalOffset = icregs.cacheIRReader.stubOffset(); + uint32_t compartmentOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + JSObject* global = reinterpret_cast<JSObject*>( + cstub->stubInfo()->getStubRawWord(cstub, globalOffset)); + JS::Compartment* compartment = reinterpret_cast<JS::Compartment*>( + cstub->stubInfo()->getStubRawWord(cstub, compartmentOffset)); + if (IsDeadProxyObject(global)) { + return ICInterpretOpResult::NextIC; + } + if (obj->compartment() != compartment) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardIsExtensible) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (obj->nonProxyIsExtensible()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardIsNativeObject) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (!obj->is<NativeObject>()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardIsProxy) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (!obj->is<ProxyObject>()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardIsNotProxy) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (obj->is<ProxyObject>()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardIsNotArrayBufferMaybeShared) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + const JSClass* clasp = obj->getClass(); + if (clasp == &ArrayBufferObject::protoClass_ || + clasp == &SharedArrayBufferObject::protoClass_) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardIsTypedArray) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (!IsTypedArrayClass(obj->getClass())) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardHasProxyHandler) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t handlerOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + BaseProxyHandler* handler = reinterpret_cast<BaseProxyHandler*>( + cstub->stubInfo()->getStubRawWord(cstub, handlerOffset)); + if (obj->as<ProxyObject>().handler() != handler) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardIsNotDOMProxy) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (obj->as<ProxyObject>().handler()->family() == + GetDOMProxyHandlerFamily()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(GuardSpecificObject) { ObjOperandId objId = icregs.cacheIRReader.objOperandId(); uint32_t expectedOffset = icregs.cacheIRReader.stubOffset(); - uintptr_t expected = - cstub->stubInfo()->getStubRawWord(cstub, expectedOffset); - if (expected != icregs.icVals[objId.id()]) { + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + JSObject* expected = reinterpret_cast<JSObject*>( + cstub->stubInfo()->getStubRawWord(cstub, expectedOffset)); + if (obj != expected) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardObjectIdentity) { + ObjOperandId obj1Id = icregs.cacheIRReader.objOperandId(); + ObjOperandId obj2Id = icregs.cacheIRReader.objOperandId(); + JSObject* obj1 = reinterpret_cast<JSObject*>(icregs.icVals[obj1Id.id()]); + JSObject* obj2 = reinterpret_cast<JSObject*>(icregs.icVals[obj2Id.id()]); + if (obj1 != obj2) { return ICInterpretOpResult::NextIC; } DISPATCH_CACHEOP(); @@ -808,6 +1019,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, uintptr_t expected = cstub->stubInfo()->getStubRawWord(cstub, expectedOffset); if (expected != icregs.icVals[strId.id()]) { + // TODO: BaselineCacheIRCompiler also checks for equal strings return ICInterpretOpResult::NextIC; } DISPATCH_CACHEOP(); @@ -833,23 +1045,211 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(GuardNoDenseElements) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (obj->as<NativeObject>().getDenseInitializedLength() != 0) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardStringToIndex) { + StringOperandId strId = icregs.cacheIRReader.stringOperandId(); + Int32OperandId resultId = icregs.cacheIRReader.int32OperandId(); + BOUNDSCHECK(resultId); + JSString* str = reinterpret_cast<JSString*>(icregs.icVals[strId.id()]); + int32_t result; + if (str->hasIndexValue()) { + uint32_t index = str->getIndexValue(); + MOZ_ASSERT(index <= INT32_MAX); + result = index; + } else { + result = GetIndexFromString(str); + if (result < 0) { + return ICInterpretOpResult::NextIC; + } + } + icregs.icVals[resultId.id()] = result; + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardStringToInt32) { + StringOperandId strId = icregs.cacheIRReader.stringOperandId(); + Int32OperandId resultId = icregs.cacheIRReader.int32OperandId(); + BOUNDSCHECK(resultId); + JSString* str = reinterpret_cast<JSString*>(icregs.icVals[strId.id()]); + int32_t result; + // Use indexed value as fast path if possible. + if (str->hasIndexValue()) { + uint32_t index = str->getIndexValue(); + MOZ_ASSERT(index <= INT32_MAX); + result = index; + } else { + if (!GetInt32FromStringPure(frameMgr.cxForLocalUseOnly(), str, &result)) { + return ICInterpretOpResult::NextIC; + } + } + icregs.icVals[resultId.id()] = result; + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardStringToNumber) { + StringOperandId strId = icregs.cacheIRReader.stringOperandId(); + NumberOperandId resultId = icregs.cacheIRReader.numberOperandId(); + BOUNDSCHECK(resultId); + JSString* str = reinterpret_cast<JSString*>(icregs.icVals[strId.id()]); + Value result; + // Use indexed value as fast path if possible. + if (str->hasIndexValue()) { + uint32_t index = str->getIndexValue(); + MOZ_ASSERT(index <= INT32_MAX); + result = Int32Value(index); + } else { + double value; + if (!StringToNumberPure(frameMgr.cxForLocalUseOnly(), str, &value)) { + return ICInterpretOpResult::NextIC; + } + result = DoubleValue(value); + } + icregs.icVals[resultId.id()] = result.asRawBits(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(BooleanToNumber) { + BooleanOperandId booleanId = icregs.cacheIRReader.booleanOperandId(); + NumberOperandId resultId = icregs.cacheIRReader.numberOperandId(); + BOUNDSCHECK(resultId); + uint64_t boolean = icregs.icVals[booleanId.id()]; + MOZ_ASSERT((boolean & ~1) == 0); + icregs.icVals[resultId.id()] = Int32Value(boolean).asRawBits(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardHasGetterSetter) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t idOffset = icregs.cacheIRReader.stubOffset(); + uint32_t getterSetterOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + jsid id = + jsid::fromRawBits(cstub->stubInfo()->getStubRawWord(cstub, idOffset)); + GetterSetter* getterSetter = reinterpret_cast<GetterSetter*>( + cstub->stubInfo()->getStubRawWord(cstub, getterSetterOffset)); + if (!ObjectHasGetterSetterPure(frameMgr.cxForLocalUseOnly(), obj, id, + getterSetter)) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardInt32IsNonNegative) { + Int32OperandId indexId = icregs.cacheIRReader.int32OperandId(); + int32_t index = int32_t(icregs.icVals[indexId.id()]); + if (index < 0) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(GuardDynamicSlotIsSpecificObject) { ObjOperandId objId = icregs.cacheIRReader.objOperandId(); ObjOperandId expectedId = icregs.cacheIRReader.objOperandId(); uint32_t slotOffset = icregs.cacheIRReader.stubOffset(); JSObject* expected = reinterpret_cast<JSObject*>(icregs.icVals[expectedId.id()]); - uintptr_t offset = cstub->stubInfo()->getStubRawInt32(cstub, slotOffset); + uintptr_t slot = cstub->stubInfo()->getStubRawInt32(cstub, slotOffset); NativeObject* nobj = reinterpret_cast<NativeObject*>(icregs.icVals[objId.id()]); HeapSlot* slots = nobj->getSlotsUnchecked(); - Value actual = slots[offset / sizeof(Value)]; + // Note that unlike similar opcodes, GuardDynamicSlotIsSpecificObject takes + // a slot index rather than a byte offset. + Value actual = slots[slot]; if (actual != ObjectValue(*expected)) { return ICInterpretOpResult::NextIC; } DISPATCH_CACHEOP(); } + CACHEOP_CASE(GuardDynamicSlotIsNotObject) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t slotOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uint32_t slot = cstub->stubInfo()->getStubRawInt32(cstub, slotOffset); + NativeObject* nobj = &obj->as<NativeObject>(); + HeapSlot* slots = nobj->getSlotsUnchecked(); + // Note that unlike similar opcodes, GuardDynamicSlotIsNotObject takes a + // slot index rather than a byte offset. + Value actual = slots[slot]; + if (actual.isObject()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardFixedSlotValue) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t offsetOffset = icregs.cacheIRReader.stubOffset(); + uint32_t valOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uint32_t offset = cstub->stubInfo()->getStubRawInt32(cstub, offsetOffset); + Value val = Value::fromRawBits( + cstub->stubInfo()->getStubRawInt64(cstub, valOffset)); + GCPtr<Value>* slot = reinterpret_cast<GCPtr<Value>*>( + reinterpret_cast<uintptr_t>(obj) + offset); + Value actual = slot->get(); + if (actual != val) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardDynamicSlotValue) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t offsetOffset = icregs.cacheIRReader.stubOffset(); + uint32_t valOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uint32_t offset = cstub->stubInfo()->getStubRawInt32(cstub, offsetOffset); + Value val = Value::fromRawBits( + cstub->stubInfo()->getStubRawInt64(cstub, valOffset)); + NativeObject* nobj = &obj->as<NativeObject>(); + HeapSlot* slots = nobj->getSlotsUnchecked(); + Value actual = slots[offset / sizeof(Value)]; + if (actual != val) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(LoadFixedSlot) { + ValOperandId resultId = icregs.cacheIRReader.valOperandId(); + BOUNDSCHECK(resultId); + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t offsetOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uint32_t offset = cstub->stubInfo()->getStubRawInt32(cstub, offsetOffset); + GCPtr<Value>* slot = reinterpret_cast<GCPtr<Value>*>( + reinterpret_cast<uintptr_t>(obj) + offset); + Value actual = slot->get(); + icregs.icVals[resultId.id()] = actual.asRawBits(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(LoadDynamicSlot) { + ValOperandId resultId = icregs.cacheIRReader.valOperandId(); + BOUNDSCHECK(resultId); + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t slotOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uint32_t slot = cstub->stubInfo()->getStubRawInt32(cstub, slotOffset); + NativeObject* nobj = &obj->as<NativeObject>(); + HeapSlot* slots = nobj->getSlotsUnchecked(); + // Note that unlike similar opcodes, LoadDynamicSlot takes a slot index + // rather than a byte offset. + Value actual = slots[slot]; + icregs.icVals[resultId.id()] = actual.asRawBits(); + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(GuardNoAllocationMetadataBuilder) { uint32_t builderAddrOffset = icregs.cacheIRReader.stubOffset(); uintptr_t builderAddr = @@ -860,6 +1260,73 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(GuardFunctionHasJitEntry) { + ObjOperandId funId = icregs.cacheIRReader.objOperandId(); + bool constructing = icregs.cacheIRReader.readBool(); + JSObject* fun = reinterpret_cast<JSObject*>(icregs.icVals[funId.id()]); + uint16_t flags = FunctionFlags::HasJitEntryFlags(constructing); + if (!fun->as<JSFunction>().flags().hasFlags(flags)) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardFunctionHasNoJitEntry) { + ObjOperandId funId = icregs.cacheIRReader.objOperandId(); + JSObject* fun = reinterpret_cast<JSObject*>(icregs.icVals[funId.id()]); + uint16_t flags = FunctionFlags::HasJitEntryFlags(/*constructing =*/false); + if (fun->as<JSFunction>().flags().hasFlags(flags)) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardFunctionIsNonBuiltinCtor) { + ObjOperandId funId = icregs.cacheIRReader.objOperandId(); + JSObject* fun = reinterpret_cast<JSObject*>(icregs.icVals[funId.id()]); + if (!fun->as<JSFunction>().isNonBuiltinConstructor()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardFunctionIsConstructor) { + ObjOperandId funId = icregs.cacheIRReader.objOperandId(); + JSObject* fun = reinterpret_cast<JSObject*>(icregs.icVals[funId.id()]); + if (!fun->as<JSFunction>().isConstructor()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardNotClassConstructor) { + ObjOperandId funId = icregs.cacheIRReader.objOperandId(); + JSObject* fun = reinterpret_cast<JSObject*>(icregs.icVals[funId.id()]); + if (fun->as<JSFunction>().isClassConstructor()) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardArrayIsPacked) { + ObjOperandId arrayId = icregs.cacheIRReader.objOperandId(); + JSObject* array = reinterpret_cast<JSObject*>(icregs.icVals[arrayId.id()]); + if (!IsPackedArray(array)) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(GuardArgumentsObjectFlags) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint8_t flags = icregs.cacheIRReader.readByte(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + if (obj->as<ArgumentsObject>().hasFlags(flags)) { + return ICInterpretOpResult::NextIC; + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(LoadObject) { ObjOperandId resultId = icregs.cacheIRReader.objOperandId(); BOUNDSCHECK(resultId); @@ -893,6 +1360,35 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(LoadEnclosingEnvironment) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + ObjOperandId resultId = icregs.cacheIRReader.objOperandId(); + BOUNDSCHECK(resultId); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + JSObject* env = &obj->as<EnvironmentObject>().enclosingEnvironment(); + icregs.icVals[resultId.id()] = reinterpret_cast<uintptr_t>(env); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(LoadWrapperTarget) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + ObjOperandId resultId = icregs.cacheIRReader.objOperandId(); + BOUNDSCHECK(resultId); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + JSObject* target = &obj->as<ProxyObject>().private_().toObject(); + icregs.icVals[resultId.id()] = reinterpret_cast<uintptr_t>(target); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(LoadValueTag) { + ValOperandId valId = icregs.cacheIRReader.valOperandId(); + ValueTagOperandId resultId = icregs.cacheIRReader.valueTagOperandId(); + BOUNDSCHECK(resultId); + Value val = Value::fromRawBits(icregs.icVals[valId.id()]); + icregs.icVals[resultId.id()] = val.extractNonDoubleType(); + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(LoadArgumentFixedSlot) { ValOperandId resultId = icregs.cacheIRReader.valOperandId(); BOUNDSCHECK(resultId); @@ -915,6 +1411,70 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(TruncateDoubleToUInt32) { + NumberOperandId inputId = icregs.cacheIRReader.numberOperandId(); + Int32OperandId resultId = icregs.cacheIRReader.int32OperandId(); + BOUNDSCHECK(resultId); + Value input = Value::fromRawBits(icregs.icVals[inputId.id()]); + icregs.icVals[resultId.id()] = JS::ToInt32(input.toNumber()); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(MegamorphicLoadSlotResult) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t nameOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + jsid name = + jsid::fromRawBits(cstub->stubInfo()->getStubRawWord(cstub, nameOffset)); + if (!obj->shape()->isNative()) { + return ICInterpretOpResult::NextIC; + } + Value result; + if (!GetNativeDataPropertyPureWithCacheLookup( + frameMgr.cxForLocalUseOnly(), obj, name, nullptr, &result)) { + return ICInterpretOpResult::NextIC; + } + icregs.icResult = result.asRawBits(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(MegamorphicLoadSlotByValueResult) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + ValOperandId idId = icregs.cacheIRReader.valOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + Value id = Value::fromRawBits(icregs.icVals[idId.id()]); + if (!obj->shape()->isNative()) { + return ICInterpretOpResult::NextIC; + } + Value values[2] = {id}; + if (!GetNativeDataPropertyByValuePure(frameMgr.cxForLocalUseOnly(), obj, + nullptr, values)) { + return ICInterpretOpResult::NextIC; + } + icregs.icResult = values[1].asRawBits(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(MegamorphicSetElement) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + ValOperandId idId = icregs.cacheIRReader.valOperandId(); + ValOperandId rhsId = icregs.cacheIRReader.valOperandId(); + bool strict = icregs.cacheIRReader.readBool(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + Value id = Value::fromRawBits(icregs.icVals[idId.id()]); + Value rhs = Value::fromRawBits(icregs.icVals[rhsId.id()]); + { + PUSH_IC_FRAME(); + ReservedRooted<JSObject*> obj0(&state.obj0, obj); + ReservedRooted<Value> value0(&state.value0, id); + ReservedRooted<Value> value1(&state.value1, rhs); + if (!SetElementMegamorphic<false>(cx, obj0, value0, value1, strict)) { + return ICInterpretOpResult::Error; + } + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(StoreFixedSlot) { ObjOperandId objId = icregs.cacheIRReader.objOperandId(); uint32_t offsetOffset = icregs.cacheIRReader.stubOffset(); @@ -926,7 +1486,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, reinterpret_cast<uintptr_t>(nobj) + offset); Value val = Value::fromRawBits(icregs.icVals[rhsId.id()]); slot->set(val); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -942,7 +1502,75 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, size_t dynSlot = offset / sizeof(Value); size_t slot = dynSlot + nobj->numFixedSlots(); slots[dynSlot].set(nobj, HeapSlot::Slot, slot, val); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(AddAndStoreFixedSlot) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t offsetOffset = icregs.cacheIRReader.stubOffset(); + ValOperandId rhsId = icregs.cacheIRReader.valOperandId(); + uint32_t newShapeOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + int32_t offset = cstub->stubInfo()->getStubRawInt32(cstub, offsetOffset); + Value rhs = Value::fromRawBits(icregs.icVals[rhsId.id()]); + Shape* newShape = reinterpret_cast<Shape*>( + cstub->stubInfo()->getStubRawWord(cstub, newShapeOffset)); + obj->setShape(newShape); + GCPtr<Value>* slot = reinterpret_cast<GCPtr<Value>*>( + reinterpret_cast<uintptr_t>(obj) + offset); + slot->init(rhs); + PREDICT_RETURN(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(AddAndStoreDynamicSlot) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t offsetOffset = icregs.cacheIRReader.stubOffset(); + ValOperandId rhsId = icregs.cacheIRReader.valOperandId(); + uint32_t newShapeOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + int32_t offset = cstub->stubInfo()->getStubRawInt32(cstub, offsetOffset); + Value rhs = Value::fromRawBits(icregs.icVals[rhsId.id()]); + Shape* newShape = reinterpret_cast<Shape*>( + cstub->stubInfo()->getStubRawWord(cstub, newShapeOffset)); + NativeObject* nobj = &obj->as<NativeObject>(); + obj->setShape(newShape); + HeapSlot* slots = nobj->getSlotsUnchecked(); + size_t dynSlot = offset / sizeof(Value); + size_t slot = dynSlot + nobj->numFixedSlots(); + slots[dynSlot].init(nobj, HeapSlot::Slot, slot, rhs); + PREDICT_RETURN(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(AllocateAndStoreDynamicSlot) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + uint32_t offsetOffset = icregs.cacheIRReader.stubOffset(); + ValOperandId rhsId = icregs.cacheIRReader.valOperandId(); + uint32_t newShapeOffset = icregs.cacheIRReader.stubOffset(); + uint32_t numNewSlotsOffset = icregs.cacheIRReader.stubOffset(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + int32_t offset = cstub->stubInfo()->getStubRawInt32(cstub, offsetOffset); + Value rhs = Value::fromRawBits(icregs.icVals[rhsId.id()]); + Shape* newShape = reinterpret_cast<Shape*>( + cstub->stubInfo()->getStubRawWord(cstub, newShapeOffset)); + int32_t numNewSlots = + cstub->stubInfo()->getStubRawInt32(cstub, numNewSlotsOffset); + NativeObject* nobj = &obj->as<NativeObject>(); + // We have to (re)allocate dynamic slots. Do this first, as it's the + // only fallible operation here. Note that growSlotsPure is fallible but + // does not GC. Otherwise this is the same as AddAndStoreDynamicSlot above. + if (!NativeObject::growSlotsPure(frameMgr.cxForLocalUseOnly(), nobj, + numNewSlots)) { + return ICInterpretOpResult::NextIC; + } + obj->setShape(newShape); + HeapSlot* slots = nobj->getSlotsUnchecked(); + size_t dynSlot = offset / sizeof(Value); + size_t slot = dynSlot + nobj->numFixedSlots(); + slots[dynSlot].init(nobj, HeapSlot::Slot, slot, rhs); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -964,7 +1592,71 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, Value val = Value::fromRawBits(icregs.icVals[rhsId.id()]); slot->set(nobj, HeapSlot::Element, index + elems->numShiftedElements(), val); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(StoreDenseElementHole) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + Int32OperandId indexId = icregs.cacheIRReader.int32OperandId(); + ValOperandId rhsId = icregs.cacheIRReader.valOperandId(); + bool handleAdd = icregs.cacheIRReader.readBool(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uint32_t index = uint32_t(icregs.icVals[indexId.id()]); + Value rhs = Value::fromRawBits(icregs.icVals[rhsId.id()]); + NativeObject* nobj = &obj->as<NativeObject>(); + uint32_t initLength = nobj->getDenseInitializedLength(); + if (index < initLength) { + nobj->setDenseElement(index, rhs); + } else if (!handleAdd || index > initLength) { + return ICInterpretOpResult::NextIC; + } else { + if (index >= nobj->getDenseCapacity()) { + if (!NativeObject::addDenseElementPure(frameMgr.cxForLocalUseOnly(), + nobj)) { + return ICInterpretOpResult::NextIC; + } + } + nobj->setDenseInitializedLength(initLength + 1); + + // Baseline always updates the length field by directly accessing its + // offset in ObjectElements. If the object is not an ArrayObject then this + // field is never read, so it's okay to skip the update here in that case. + if (nobj->is<ArrayObject>()) { + ArrayObject* aobj = &nobj->as<ArrayObject>(); + uint32_t len = aobj->length(); + if (len <= index) { + aobj->setLength(len + 1); + } + } + + nobj->initDenseElement(index, rhs); + } + PREDICT_RETURN(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(ArrayPush) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + ValOperandId rhsId = icregs.cacheIRReader.valOperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + Value rhs = Value::fromRawBits(icregs.icVals[rhsId.id()]); + ArrayObject* aobj = &obj->as<ArrayObject>(); + uint32_t initLength = aobj->getDenseInitializedLength(); + if (aobj->length() != initLength) { + return ICInterpretOpResult::NextIC; + } + if (initLength >= aobj->getDenseCapacity()) { + if (!NativeObject::addDenseElementPure(frameMgr.cxForLocalUseOnly(), + aobj)) { + return ICInterpretOpResult::NextIC; + } + } + aobj->setDenseInitializedLength(initLength + 1); + aobj->setLength(initLength + 1); + aobj->initDenseElement(initLength, rhs); + icregs.icResult = Int32Value(initLength + 1).asRawBits(); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -972,7 +1664,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, ValOperandId inputId = icregs.cacheIRReader.valOperandId(); Value val = Value::fromRawBits(icregs.icVals[inputId.id()]); icregs.icResult = BooleanValue(val.isObject()).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -989,6 +1681,66 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(StoreTypedArrayElement) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + Scalar::Type elementType = icregs.cacheIRReader.scalarType(); + IntPtrOperandId indexId = icregs.cacheIRReader.intPtrOperandId(); + uint32_t rhsId = icregs.cacheIRReader.rawOperandId(); + bool handleOOB = icregs.cacheIRReader.readBool(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uintptr_t index = uintptr_t(icregs.icVals[indexId.id()]); + uint64_t rhs = icregs.icVals[rhsId]; + if (obj->as<TypedArrayObject>().length().isNothing()) { + return ICInterpretOpResult::NextIC; + } + if (index >= obj->as<TypedArrayObject>().length().value()) { + if (!handleOOB) { + return ICInterpretOpResult::NextIC; + } + } else { + Value v; + switch (elementType) { + case Scalar::Int8: + case Scalar::Uint8: + case Scalar::Int16: + case Scalar::Uint16: + case Scalar::Int32: + case Scalar::Uint32: + case Scalar::Uint8Clamped: + v = Int32Value(rhs); + break; + + case Scalar::Float32: + case Scalar::Float64: + v = Value::fromRawBits(rhs); + MOZ_ASSERT(v.isNumber()); + break; + + case Scalar::BigInt64: + case Scalar::BigUint64: + v = BigIntValue(reinterpret_cast<JS::BigInt*>(rhs)); + break; + + case Scalar::MaxTypedArrayViewType: + case Scalar::Int64: + case Scalar::Simd128: + MOZ_CRASH("Unsupported TypedArray type"); + } + + // SetTypedArrayElement doesn't do anything that can actually GC or need a + // new context when the value can only be Int32, Double, or BigInt, as the + // above switch statement enforces. + FakeRooted<TypedArrayObject*> obj0(nullptr, &obj->as<TypedArrayObject>()); + FakeRooted<Value> value0(nullptr, v); + ObjectOpResult result; + MOZ_ASSERT(elementType == obj0->type()); + MOZ_ALWAYS_TRUE(SetTypedArrayElement(frameMgr.cxForLocalUseOnly(), obj0, + index, value0, result)); + MOZ_ALWAYS_TRUE(result.ok()); + } + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(CallInt32ToString) { Int32OperandId inputId = icregs.cacheIRReader.int32OperandId(); StringOperandId resultId = icregs.cacheIRReader.stringOperandId(); @@ -1031,8 +1783,12 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, } // For now, fail any constructing or different-realm cases. - if (flags.isConstructing() || !flags.isSameRealm()) { - TRACE_PRINTF("failing: constructing or not same realm\n"); + if (flags.isConstructing()) { + TRACE_PRINTF("failing: constructing\n"); + return ICInterpretOpResult::NextIC; + } + if (!flags.isSameRealm()) { + TRACE_PRINTF("failing: not same realm\n"); return ICInterpretOpResult::NextIC; } // And support only "standard" arg formats. @@ -1123,6 +1879,15 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, } } + PREDICT_RETURN(); + DISPATCH_CACHEOP(); + } + + CACHEOP_CASE(MetaScriptedThisShape) { + uint32_t thisShapeOffset = icregs.cacheIRReader.stubOffset(); + // This op is only metadata for the Warp Transpiler and should be ignored. + (void)thisShapeOffset; + PREDICT_NEXT(CallScriptedFunction); DISPATCH_CACHEOP(); } @@ -1139,7 +1904,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, "slot %" PRIx64 "\n", nobj, int(offsetOffset), int(offset), slot, slot->asRawBits()); icregs.icResult = slot->asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1151,7 +1916,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, reinterpret_cast<NativeObject*>(icregs.icVals[objId.id()]); HeapSlot* slots = nobj->getSlotsUnchecked(); icregs.icResult = slots[offset / sizeof(Value)].get().asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1171,7 +1936,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } icregs.icResult = val.asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1184,7 +1949,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } icregs.icResult = Int32Value(length).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1202,6 +1967,22 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } + CACHEOP_CASE(LoadArgumentsObjectArgResult) { + ObjOperandId objId = icregs.cacheIRReader.objOperandId(); + Int32OperandId indexId = icregs.cacheIRReader.int32OperandId(); + JSObject* obj = reinterpret_cast<JSObject*>(icregs.icVals[objId.id()]); + uint32_t index = uint32_t(icregs.icVals[indexId.id()]); + ArgumentsObject* args = &obj->as<ArgumentsObject>(); + if (index >= args->initialLength() || args->hasOverriddenElement()) { + return ICInterpretOpResult::NextIC; + } + if (args->argIsForwarded(index)) { + return ICInterpretOpResult::NextIC; + } + icregs.icResult = args->arg(index).asRawBits(); + DISPATCH_CACHEOP(); + } + CACHEOP_CASE(LinearizeForCharAccess) { StringOperandId strId = icregs.cacheIRReader.stringOperandId(); Int32OperandId indexId = icregs.cacheIRReader.int32OperandId(); @@ -1258,7 +2039,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, } } icregs.icResult = StringValue(result).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1286,7 +2067,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, result = Int32Value(c); } icregs.icResult = result.asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1298,7 +2079,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } icregs.icResult = Int32Value(length).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1307,7 +2088,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, icregs.icResult = ObjectValue(*reinterpret_cast<JSObject*>(icregs.icVals[objId.id()])) .asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1316,6 +2097,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, icregs.icResult = StringValue(reinterpret_cast<JSString*>(icregs.icVals[strId.id()])) .asRawBits(); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1324,14 +2106,14 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, icregs.icResult = SymbolValue(reinterpret_cast<JS::Symbol*>(icregs.icVals[symId.id()])) .asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } CACHEOP_CASE(LoadInt32Result) { Int32OperandId valId = icregs.cacheIRReader.int32OperandId(); icregs.icResult = Int32Value(icregs.icVals[valId.id()]).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1342,7 +2124,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, val = DoubleValue(val.toInt32()); } icregs.icResult = val.asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1351,14 +2133,14 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, icregs.icResult = BigIntValue(reinterpret_cast<JS::BigInt*>(icregs.icVals[valId.id()])) .asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } CACHEOP_CASE(LoadBooleanResult) { bool val = icregs.cacheIRReader.readBool(); icregs.icResult = BooleanValue(val).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1376,7 +2158,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, JSString* str = reinterpret_cast<JSString*>( cstub->stubInfo()->getStubRawWord(cstub, strOffset)); icregs.icResult = StringValue(str).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1392,12 +2174,14 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; \ } \ icregs.icResult = Int32Value(int32_t(result)).asRawBits(); \ - PREDICT_NEXT(ReturnFromIC); \ + PREDICT_RETURN(); \ DISPATCH_CACHEOP(); \ } + // clang-format off INT32_OP(Add, +, {}); INT32_OP(Sub, -, {}); + // clang-format on INT32_OP(Mul, *, { if (rhs * lhs == 0 && ((rhs < 0) ^ (lhs < 0))) { return ICInterpretOpResult::NextIC; @@ -1422,8 +2206,11 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } }); + // clang-format off INT32_OP(BitOr, |, {}); + INT32_OP(BitXor, ^, {}); INT32_OP(BitAnd, &, {}); + // clang-format on CACHEOP_CASE(Int32PowResult) { Int32OperandId lhsId = icregs.cacheIRReader.int32OperandId(); @@ -1458,7 +2245,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, } icregs.icResult = Int32Value(int32_t(result)).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1470,7 +2257,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } icregs.icResult = Int32Value(int32_t(value)).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1478,7 +2265,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, ValOperandId inputId = icregs.cacheIRReader.valOperandId(); int32_t val = int32_t(icregs.icVals[inputId.id()]); icregs.icResult = BooleanValue(val != 0).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1487,7 +2274,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, JSString* str = reinterpret_cast<JSLinearString*>(icregs.icVals[strId.id()]); icregs.icResult = BooleanValue(str->length() > 0).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1499,21 +2286,21 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, return ICInterpretOpResult::NextIC; } icregs.icResult = BooleanValue(!cls->emulatesUndefined()).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } CACHEOP_CASE(LoadValueResult) { uint32_t valOffset = icregs.cacheIRReader.stubOffset(); icregs.icResult = cstub->stubInfo()->getStubRawInt64(cstub, valOffset); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } CACHEOP_CASE(LoadOperandResult) { ValOperandId inputId = icregs.cacheIRReader.valOperandId(); icregs.icResult = icregs.icVals[inputId.id()]; - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1533,7 +2320,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, } else { return ICInterpretOpResult::NextIC; } - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1548,57 +2335,72 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, ReservedRooted<JSString*> rhs( &state.str1, reinterpret_cast<JSString*>(icregs.icVals[rhsId.id()])); bool result; - switch (op) { - case JSOp::Eq: - case JSOp::StrictEq: - if (lhs->length() != rhs->length()) { - result = false; + if (lhs == rhs) { + // If operands point to the same instance, the strings are trivially + // equal. + result = op == JSOp::Eq || op == JSOp::StrictEq || op == JSOp::Le || + op == JSOp::Ge; + } else { + switch (op) { + case JSOp::Eq: + case JSOp::StrictEq: + if (lhs->isAtom() && rhs->isAtom()) { + result = false; + break; + } + if (lhs->length() != rhs->length()) { + result = false; + break; + } + if (!StringsEqual<EqualityKind::Equal>(cx, lhs, rhs, &result)) { + return ICInterpretOpResult::Error; + } break; - } - if (!StringsEqual<EqualityKind::Equal>(cx, lhs, rhs, &result)) { - return ICInterpretOpResult::Error; - } - break; - case JSOp::Ne: - case JSOp::StrictNe: - if (lhs->length() != rhs->length()) { - result = true; + case JSOp::Ne: + case JSOp::StrictNe: + if (lhs->isAtom() && rhs->isAtom()) { + result = true; + break; + } + if (lhs->length() != rhs->length()) { + result = true; + break; + } + if (!StringsEqual<EqualityKind::NotEqual>(cx, lhs, rhs, &result)) { + return ICInterpretOpResult::Error; + } break; - } - if (!StringsEqual<EqualityKind::NotEqual>(cx, lhs, rhs, &result)) { - return ICInterpretOpResult::Error; - } - break; - case JSOp::Lt: - if (!StringsCompare<ComparisonKind::LessThan>(cx, lhs, rhs, - &result)) { - return ICInterpretOpResult::Error; - } - break; - case JSOp::Ge: - if (!StringsCompare<ComparisonKind::GreaterThanOrEqual>(cx, lhs, rhs, - &result)) { - return ICInterpretOpResult::Error; - } - break; - case JSOp::Le: - if (!StringsCompare<ComparisonKind::GreaterThanOrEqual>( - cx, /* N.B. swapped order */ rhs, lhs, &result)) { - return ICInterpretOpResult::Error; - } - break; - case JSOp::Gt: - if (!StringsCompare<ComparisonKind::LessThan>( - cx, /* N.B. swapped order */ rhs, lhs, &result)) { - return ICInterpretOpResult::Error; - } - break; - default: - MOZ_CRASH("bad opcode"); + case JSOp::Lt: + if (!StringsCompare<ComparisonKind::LessThan>(cx, lhs, rhs, + &result)) { + return ICInterpretOpResult::Error; + } + break; + case JSOp::Ge: + if (!StringsCompare<ComparisonKind::GreaterThanOrEqual>( + cx, lhs, rhs, &result)) { + return ICInterpretOpResult::Error; + } + break; + case JSOp::Le: + if (!StringsCompare<ComparisonKind::GreaterThanOrEqual>( + cx, /* N.B. swapped order */ rhs, lhs, &result)) { + return ICInterpretOpResult::Error; + } + break; + case JSOp::Gt: + if (!StringsCompare<ComparisonKind::LessThan>( + cx, /* N.B. swapped order */ rhs, lhs, &result)) { + return ICInterpretOpResult::Error; + } + break; + default: + MOZ_CRASH("bad opcode"); + } } icregs.icResult = BooleanValue(result).asRawBits(); } - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1636,7 +2438,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, MOZ_CRASH("Unexpected opcode"); } icregs.icResult = BooleanValue(result).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1671,7 +2473,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, MOZ_CRASH("bad opcode"); } icregs.icResult = BooleanValue(result).asRawBits(); - PREDICT_NEXT(ReturnFromIC); + PREDICT_RETURN(); DISPATCH_CACHEOP(); } @@ -1686,15 +2488,9 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, DISPATCH_CACHEOP(); } - CACHEOP_CASE_UNIMPL(GuardToNonGCThing) - CACHEOP_CASE_UNIMPL(Int32ToIntPtr) CACHEOP_CASE_UNIMPL(GuardNumberToIntPtrIndex) - CACHEOP_CASE_UNIMPL(GuardToInt32ModUint32) CACHEOP_CASE_UNIMPL(GuardToUint8Clamped) CACHEOP_CASE_UNIMPL(GuardMultipleShapes) - CACHEOP_CASE_UNIMPL(GuardNullProto) - CACHEOP_CASE_UNIMPL(GuardAnyClass) - CACHEOP_CASE_UNIMPL(HasClassResult) CACHEOP_CASE_UNIMPL(CallRegExpMatcherResult) CACHEOP_CASE_UNIMPL(CallRegExpSearcherResult) CACHEOP_CASE_UNIMPL(RegExpSearcherLastLimitResult) @@ -1708,53 +2504,19 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(RegExpPrototypeOptimizableResult) CACHEOP_CASE_UNIMPL(RegExpInstanceOptimizableResult) CACHEOP_CASE_UNIMPL(GetFirstDollarIndexResult) - CACHEOP_CASE_UNIMPL(GuardCompartment) - CACHEOP_CASE_UNIMPL(GuardIsExtensible) - CACHEOP_CASE_UNIMPL(GuardIsNativeObject) - CACHEOP_CASE_UNIMPL(GuardIsProxy) - CACHEOP_CASE_UNIMPL(GuardIsNotProxy) - CACHEOP_CASE_UNIMPL(GuardIsNotArrayBufferMaybeShared) - CACHEOP_CASE_UNIMPL(GuardIsTypedArray) CACHEOP_CASE_UNIMPL(GuardIsFixedLengthTypedArray) - CACHEOP_CASE_UNIMPL(GuardHasProxyHandler) - CACHEOP_CASE_UNIMPL(GuardIsNotDOMProxy) - CACHEOP_CASE_UNIMPL(GuardObjectIdentity) - CACHEOP_CASE_UNIMPL(GuardNoDenseElements) - CACHEOP_CASE_UNIMPL(GuardStringToIndex) - CACHEOP_CASE_UNIMPL(GuardStringToInt32) - CACHEOP_CASE_UNIMPL(GuardStringToNumber) + CACHEOP_CASE_UNIMPL(GuardIsResizableTypedArray) CACHEOP_CASE_UNIMPL(StringToAtom) - CACHEOP_CASE_UNIMPL(BooleanToNumber) - CACHEOP_CASE_UNIMPL(GuardHasGetterSetter) - CACHEOP_CASE_UNIMPL(GuardInt32IsNonNegative) CACHEOP_CASE_UNIMPL(GuardIndexIsValidUpdateOrAdd) CACHEOP_CASE_UNIMPL(GuardIndexIsNotDenseElement) CACHEOP_CASE_UNIMPL(GuardTagNotEqual) CACHEOP_CASE_UNIMPL(GuardXrayExpandoShapeAndDefaultProto) CACHEOP_CASE_UNIMPL(GuardXrayNoExpando) - CACHEOP_CASE_UNIMPL(GuardDynamicSlotIsNotObject) - CACHEOP_CASE_UNIMPL(GuardFixedSlotValue) - CACHEOP_CASE_UNIMPL(GuardDynamicSlotValue) + CACHEOP_CASE_UNIMPL(GuardEitherClass) CACHEOP_CASE_UNIMPL(LoadScriptedProxyHandler) CACHEOP_CASE_UNIMPL(IdToStringOrSymbol) - CACHEOP_CASE_UNIMPL(LoadFixedSlot) - CACHEOP_CASE_UNIMPL(LoadDynamicSlot) - CACHEOP_CASE_UNIMPL(GuardFunctionHasJitEntry) - CACHEOP_CASE_UNIMPL(GuardFunctionHasNoJitEntry) - CACHEOP_CASE_UNIMPL(GuardFunctionIsNonBuiltinCtor) - CACHEOP_CASE_UNIMPL(GuardFunctionIsConstructor) - CACHEOP_CASE_UNIMPL(GuardNotClassConstructor) - CACHEOP_CASE_UNIMPL(GuardArrayIsPacked) - CACHEOP_CASE_UNIMPL(GuardArgumentsObjectFlags) - CACHEOP_CASE_UNIMPL(LoadEnclosingEnvironment) - CACHEOP_CASE_UNIMPL(LoadWrapperTarget) - CACHEOP_CASE_UNIMPL(LoadValueTag) - CACHEOP_CASE_UNIMPL(TruncateDoubleToUInt32) CACHEOP_CASE_UNIMPL(DoubleToUint8Clamped) - CACHEOP_CASE_UNIMPL(MegamorphicLoadSlotResult) - CACHEOP_CASE_UNIMPL(MegamorphicLoadSlotByValueResult) CACHEOP_CASE_UNIMPL(MegamorphicStoreSlot) - CACHEOP_CASE_UNIMPL(MegamorphicSetElement) CACHEOP_CASE_UNIMPL(MegamorphicHasPropResult) CACHEOP_CASE_UNIMPL(SmallObjectVariableKeyHasOwnResult) CACHEOP_CASE_UNIMPL(ObjectToIteratorResult) @@ -1763,12 +2525,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(LoadDOMExpandoValueGuardGeneration) CACHEOP_CASE_UNIMPL(LoadDOMExpandoValueIgnoreGeneration) CACHEOP_CASE_UNIMPL(GuardDOMExpandoMissingOrGuardShape) - CACHEOP_CASE_UNIMPL(AddAndStoreFixedSlot) - CACHEOP_CASE_UNIMPL(AddAndStoreDynamicSlot) - CACHEOP_CASE_UNIMPL(AllocateAndStoreDynamicSlot) CACHEOP_CASE_UNIMPL(AddSlotAndCallAddPropHook) - CACHEOP_CASE_UNIMPL(StoreDenseElementHole) - CACHEOP_CASE_UNIMPL(ArrayPush) CACHEOP_CASE_UNIMPL(ArrayJoinResult) CACHEOP_CASE_UNIMPL(ObjectKeysResult) CACHEOP_CASE_UNIMPL(PackedArrayPopResult) @@ -1785,10 +2542,22 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(IsTypedArrayConstructorResult) CACHEOP_CASE_UNIMPL(ArrayBufferViewByteOffsetInt32Result) CACHEOP_CASE_UNIMPL(ArrayBufferViewByteOffsetDoubleResult) + CACHEOP_CASE_UNIMPL(ResizableTypedArrayByteOffsetMaybeOutOfBoundsInt32Result) + CACHEOP_CASE_UNIMPL(ResizableTypedArrayByteOffsetMaybeOutOfBoundsDoubleResult) CACHEOP_CASE_UNIMPL(TypedArrayByteLengthInt32Result) CACHEOP_CASE_UNIMPL(TypedArrayByteLengthDoubleResult) + CACHEOP_CASE_UNIMPL(ResizableTypedArrayByteLengthInt32Result) + CACHEOP_CASE_UNIMPL(ResizableTypedArrayByteLengthDoubleResult) + CACHEOP_CASE_UNIMPL(ResizableTypedArrayLengthInt32Result) + CACHEOP_CASE_UNIMPL(ResizableTypedArrayLengthDoubleResult) CACHEOP_CASE_UNIMPL(TypedArrayElementSizeResult) + CACHEOP_CASE_UNIMPL(ResizableDataViewByteLengthInt32Result) + CACHEOP_CASE_UNIMPL(ResizableDataViewByteLengthDoubleResult) + CACHEOP_CASE_UNIMPL(GrowableSharedArrayBufferByteLengthInt32Result) + CACHEOP_CASE_UNIMPL(GrowableSharedArrayBufferByteLengthDoubleResult) CACHEOP_CASE_UNIMPL(GuardHasAttachedArrayBuffer) + CACHEOP_CASE_UNIMPL(GuardResizableArrayBufferViewInBounds) + CACHEOP_CASE_UNIMPL(GuardResizableArrayBufferViewInBoundsOrDetached) CACHEOP_CASE_UNIMPL(NewArrayIteratorResult) CACHEOP_CASE_UNIMPL(NewStringIteratorResult) CACHEOP_CASE_UNIMPL(NewRegExpStringIteratorResult) @@ -1843,7 +2612,6 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(DoubleParseIntResult) CACHEOP_CASE_UNIMPL(ObjectToStringResult) CACHEOP_CASE_UNIMPL(ReflectGetPrototypeOfResult) - CACHEOP_CASE_UNIMPL(StoreTypedArrayElement) CACHEOP_CASE_UNIMPL(AtomicsCompareExchangeResult) CACHEOP_CASE_UNIMPL(AtomicsExchangeResult) CACHEOP_CASE_UNIMPL(AtomicsAddResult) @@ -1875,7 +2643,6 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(CallScriptedProxyGetResult) CACHEOP_CASE_UNIMPL(CallScriptedProxyGetByValueResult) #endif - CACHEOP_CASE_UNIMPL(MetaScriptedThisShape) CACHEOP_CASE_UNIMPL(BindFunctionResult) CACHEOP_CASE_UNIMPL(SpecializedBindFunctionResult) CACHEOP_CASE_UNIMPL(LoadFixedSlotTypedResult) @@ -1887,7 +2654,6 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(LoadTypedArrayElementResult) CACHEOP_CASE_UNIMPL(LoadDataViewValueResult) CACHEOP_CASE_UNIMPL(StoreDataViewValueResult) - CACHEOP_CASE_UNIMPL(LoadArgumentsObjectArgResult) CACHEOP_CASE_UNIMPL(LoadArgumentsObjectArgHoleResult) CACHEOP_CASE_UNIMPL(LoadArgumentsObjectArgExistsResult) CACHEOP_CASE_UNIMPL(LoadArgumentsObjectLengthResult) @@ -1932,7 +2698,6 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(BigIntDivResult) CACHEOP_CASE_UNIMPL(BigIntModResult) CACHEOP_CASE_UNIMPL(BigIntPowResult) - CACHEOP_CASE_UNIMPL(Int32BitXorResult) CACHEOP_CASE_UNIMPL(Int32LeftShiftResult) CACHEOP_CASE_UNIMPL(Int32RightShiftResult) CACHEOP_CASE_UNIMPL(Int32URightShiftResult) @@ -1996,8 +2761,7 @@ ICInterpretOps(BaselineFrame* frame, VMFrameManager& frameMgr, State& state, CACHEOP_CASE_UNIMPL(Breakpoint) CACHEOP_CASE_UNIMPL(WrapResult) CACHEOP_CASE_UNIMPL(Bailout) - CACHEOP_CASE_UNIMPL(AssertRecoveredOnBailoutResult) - CACHEOP_CASE_UNIMPL(GuardIsNotUninitializedLexical) { + CACHEOP_CASE_UNIMPL(AssertRecoveredOnBailoutResult) { TRACE_PRINTF("unknown CacheOp: %s\n", CacheIROpNames[int(cacheop)]); return ICInterpretOpResult::NextIC; } @@ -2498,6 +3262,13 @@ PBIResult PortableBaselineInterpret(JSContext* cx_, State& state, Stack& stack, } ret->setUndefined(); + // Check if we are being debugged, and set a flag in the frame if so. This + // flag must be set before calling InitFunctionEnvironmentObjects. + if (script->isDebuggee()) { + TRACE_PRINTF("Script is debuggee\n"); + frame->setIsDebuggee(); + } + if (CalleeTokenIsFunction(frame->calleeToken())) { JSFunction* func = CalleeTokenToFunction(frame->calleeToken()); frame->setEnvironmentChain(func->environment()); @@ -2511,12 +3282,8 @@ PBIResult PortableBaselineInterpret(JSContext* cx_, State& state, Stack& stack, } } - // Check if we are being debugged, and set a flag in the frame if - // so. + // The debug prologue can't run until the function environment is set up. if (script->isDebuggee()) { - TRACE_PRINTF("Script is debuggee\n"); - frame->setIsDebuggee(); - PUSH_EXIT_FRAME(); if (!DebugPrologue(cx, frame)) { goto error; diff --git a/js/src/vm/Realm.cpp b/js/src/vm/Realm.cpp index d2ad39f3db..4e3eba5677 100644 --- a/js/src/vm/Realm.cpp +++ b/js/src/vm/Realm.cpp @@ -237,11 +237,17 @@ void Realm::traceRoots(JSTracer* trc, // The global is never nursery allocated, so we don't need to // trace it when doing a minor collection. // - // If a realm is on-stack, we mark its global so that - // JSContext::global() remains valid. + // If a realm is on-stack, we mark its global so that JSContext::global() + // remains valid. if (shouldTraceGlobal() && global_) { TraceRoot(trc, global_.unbarrieredAddress(), "on-stack realm global"); } + + // If the realm is still being initialized we set a flag so that it doesn't + // get deleted, since there may be GC things that contain pointers to it. + if (shouldTraceGlobal() && initializingGlobal_) { + allocatedDuringIncrementalGC_ = true; + } } // Nothing below here needs to be treated as a root if we aren't marking diff --git a/js/src/vm/Realm.h b/js/src/vm/Realm.h index 2e6d56aa5e..4518b4ced4 100644 --- a/js/src/vm/Realm.h +++ b/js/src/vm/Realm.h @@ -17,6 +17,7 @@ #include <stddef.h> #include "builtin/Array.h" +#include "ds/IdValuePair.h" #include "gc/Barrier.h" #include "js/GCVariant.h" #include "js/RealmOptions.h" @@ -129,7 +130,7 @@ class NewPlainObjectWithPropsCache { public: NewPlainObjectWithPropsCache() { purge(); } - SharedShape* lookup(IdValuePair* properties, size_t nproperties) const; + SharedShape* lookup(Handle<IdValueVector> properties) const; void add(SharedShape* shape); void purge() { @@ -436,9 +437,6 @@ class JS::Realm : public JS::shadow::Realm { // features are required. bool isUnlimitedStacksCapturingEnabled = false; - // Whether or not the deprecation warning for bug 1873186 has been shown. - bool warnedAboutDateLateWeekday = false; - private: void updateDebuggerObservesFlag(unsigned flag); diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 256aade5f8..29806c21d4 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -298,7 +298,6 @@ void RegExpObject::initAndZeroLastIndex(JSAtom* source, RegExpFlags flags, zeroLastIndex(cx); } -#if defined(DEBUG) || defined(JS_JITSPEW) template <typename KnownF, typename UnknownF> void ForEachRegExpFlag(JS::RegExpFlags flags, KnownF known, UnknownF unknown) { uint8_t raw = flags.value(); @@ -336,6 +335,14 @@ void ForEachRegExpFlag(JS::RegExpFlags flags, KnownF known, UnknownF unknown) { } } +std::ostream& JS::operator<<(std::ostream& os, RegExpFlags flags) { + ForEachRegExpFlag( + flags, [&](const char* name, const char* c) { os << c; }, + [&](uint8_t value) { os << '?'; }); + return os; +} + +#if defined(DEBUG) || defined(JS_JITSPEW) void RegExpObject::dumpOwnFields(js::JSONPrinter& json) const { { js::GenericPrinter& out = json.beginStringProperty("source"); @@ -1120,36 +1127,7 @@ static bool ParseRegExpFlags(const CharT* chars, size_t length, for (size_t i = 0; i < length; i++) { uint8_t flag; - switch (chars[i]) { - case 'd': - flag = RegExpFlag::HasIndices; - break; - case 'g': - flag = RegExpFlag::Global; - break; - case 'i': - flag = RegExpFlag::IgnoreCase; - break; - case 'm': - flag = RegExpFlag::Multiline; - break; - case 's': - flag = RegExpFlag::DotAll; - break; - case 'u': - flag = RegExpFlag::Unicode; - break; - case 'v': - flag = RegExpFlag::UnicodeSets; - break; - case 'y': - flag = RegExpFlag::Sticky; - break; - default: - *invalidFlag = chars[i]; - return false; - } - if (*flagsOut & flag) { + if (!JS::MaybeParseRegExpFlag(chars[i], &flag) || *flagsOut & flag) { *invalidFlag = chars[i]; return false; } diff --git a/js/src/vm/RegExpShared.h b/js/src/vm/RegExpShared.h index 4ff68e9ee1..07f57d1e7a 100644 --- a/js/src/vm/RegExpShared.h +++ b/js/src/vm/RegExpShared.h @@ -103,7 +103,7 @@ class RegExpShared size_t byteCodeLength() const { MOZ_ASSERT(byteCode); - return byteCode->length; + return byteCode->length(); } }; diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 934a534185..57d4fb1411 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -526,9 +526,9 @@ struct JSRuntime { js::GeckoProfilerRuntime& geckoProfiler() { return geckoProfiler_.ref(); } // Heap GC roots for PersistentRooted pointers. - js::MainThreadData< - mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit, - mozilla::LinkedList<js::PersistentRootedBase>>> + js::MainThreadData<mozilla::EnumeratedArray< + JS::RootKind, mozilla::LinkedList<js::PersistentRootedBase>, + size_t(JS::RootKind::Limit)>> heapRoots; void tracePersistentRoots(JSTracer* trc); diff --git a/js/src/vm/Scope.cpp b/js/src/vm/Scope.cpp index c48d0976d8..45cbeb6419 100644 --- a/js/src/vm/Scope.cpp +++ b/js/src/vm/Scope.cpp @@ -148,6 +148,33 @@ SharedShape* js::CreateEnvironmentShape(JSContext* cx, BindingIter& bi, map, mapLength, objectFlags); } +SharedShape* js::CreateEnvironmentShapeForSyntheticModule( + JSContext* cx, const JSClass* cls, uint32_t numSlots, + Handle<ModuleObject*> module) { + Rooted<SharedPropMap*> map(cx); + uint32_t mapLength = 0; + + PropertyFlags propFlags = {PropertyFlag::Enumerable}; + ObjectFlags objectFlags = ModuleEnvironmentObject::OBJECT_FLAGS; + + RootedId id(cx); + uint32_t slotIndex = numSlots; + for (JSAtom* exportName : module->syntheticExportNames()) { + id = NameToId(exportName->asPropertyName()); + if (!SharedPropMap::addPropertyWithKnownSlot(cx, cls, &map, &mapLength, id, + propFlags, slotIndex, + &objectFlags)) { + return nullptr; + } + slotIndex++; + } + + uint32_t numFixed = gc::GetGCKindSlots(gc::GetGCObjectKind(numSlots)); + return SharedShape::getInitialOrPropMapShape(cx, cls, cx->realm(), + TaggedProto(nullptr), numFixed, + map, mapLength, objectFlags); +} + template <class DataT> inline size_t SizeOfAllocatedData(DataT* data) { return SizeOfScopeData<DataT>(data->length); diff --git a/js/src/vm/Scope.h b/js/src/vm/Scope.h index a914a14f28..22777f100a 100644 --- a/js/src/vm/Scope.h +++ b/js/src/vm/Scope.h @@ -1765,6 +1765,10 @@ SharedShape* CreateEnvironmentShape(JSContext* cx, BindingIter& bi, const JSClass* cls, uint32_t numSlots, ObjectFlags objectFlags); +SharedShape* CreateEnvironmentShapeForSyntheticModule( + JSContext* cx, const JSClass* cls, uint32_t numSlots, + Handle<ModuleObject*> module); + SharedShape* EmptyEnvironmentShape(JSContext* cx, const JSClass* cls, uint32_t numSlots, ObjectFlags objectFlags); diff --git a/js/src/vm/SharedArrayObject.h b/js/src/vm/SharedArrayObject.h index 572fe2e6fb..525ee78451 100644 --- a/js/src/vm/SharedArrayObject.h +++ b/js/src/vm/SharedArrayObject.h @@ -116,7 +116,9 @@ class SharedArrayRawBuffer { // this method merely sets the number of user accessible bytes of this buffer. bool grow(size_t newByteLength); - static int32_t liveBuffers(); + static size_t offsetOfByteLength() { + return offsetof(SharedArrayRawBuffer, length_); + } }; class WasmSharedArrayRawBuffer : public SharedArrayRawBuffer { @@ -364,6 +366,10 @@ class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared { return rawBufferObject()->dataPointerShared(); } + static constexpr int rawBufferOffset() { + return NativeObject::getFixedSlotOffset(RAWBUF_SLOT); + } + // WebAssembly support: // Create a SharedArrayBufferObject using the provided buffer and size. diff --git a/js/src/vm/SharedStencil.h b/js/src/vm/SharedStencil.h index 58666919dc..a402895bd6 100644 --- a/js/src/vm/SharedStencil.h +++ b/js/src/vm/SharedStencil.h @@ -789,7 +789,11 @@ using SharedImmutableScriptDataTable = SharedImmutableScriptData::Hasher, SystemAllocPolicy>; struct MemberInitializers { +#ifdef ENABLE_DECORATORS + static constexpr size_t NumBits = 30; +#else static constexpr size_t NumBits = 31; +#endif static constexpr uint32_t MaxInitializers = BitMask(NumBits); #ifdef DEBUG @@ -798,20 +802,37 @@ struct MemberInitializers { bool hasPrivateBrand : 1; +#ifdef ENABLE_DECORATORS + bool hasDecorators : 1; +#endif + // This struct will eventually have a vector of constant values for optimizing // field initializers. uint32_t numMemberInitializers : NumBits; - MemberInitializers(bool hasPrivateBrand, uint32_t numMemberInitializers) + MemberInitializers(bool hasPrivateBrand, +#ifdef ENABLE_DECORATORS + bool hasDecorators, +#endif + uint32_t numMemberInitializers) : #ifdef DEBUG valid(true), #endif hasPrivateBrand(hasPrivateBrand), +#ifdef ENABLE_DECORATORS + hasDecorators(hasDecorators), +#endif numMemberInitializers(numMemberInitializers) { +#ifdef ENABLE_DECORATORS + MOZ_ASSERT( + this->numMemberInitializers == numMemberInitializers, + "numMemberInitializers should easily fit in the 30-bit bitfield"); +#else MOZ_ASSERT( this->numMemberInitializers == numMemberInitializers, "numMemberInitializers should easily fit in the 31-bit bitfield"); +#endif } static MemberInitializers Invalid() { return MemberInitializers(); } @@ -820,17 +841,33 @@ struct MemberInitializers { // fields. This is used when we elide the trivial data but still need a valid // set to stop scope walking. static const MemberInitializers& Empty() { - static const MemberInitializers zeroInitializers(false, 0); + static const MemberInitializers zeroInitializers(false, +#ifdef ENABLE_DECORATORS + false, +#endif + 0); return zeroInitializers; } uint32_t serialize() const { +#ifdef ENABLE_DECORATORS + auto serialised = (hasPrivateBrand << (NumBits + 1)) | + hasDecorators << NumBits | numMemberInitializers; + return serialised; +#else return (hasPrivateBrand << NumBits) | numMemberInitializers; +#endif } static MemberInitializers deserialize(uint32_t bits) { +#ifdef ENABLE_DECORATORS + return MemberInitializers((bits & Bit(NumBits + 1)) != 0, + (bits & Bit(NumBits)) != 0, + bits & BitMask(NumBits)); +#else return MemberInitializers((bits & Bit(NumBits)) != 0, bits & BitMask(NumBits)); +#endif } private: @@ -840,6 +877,9 @@ struct MemberInitializers { valid(false), #endif hasPrivateBrand(false), +#ifdef ENABLE_DECORATORS + hasDecorators(false), +#endif numMemberInitializers(0) { } }; diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index ffbe88147d..d222ddc51c 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -642,7 +642,20 @@ JS::ProfilingFrameIterator::getPhysicalFrameAndEntry( if (isWasm()) { Frame frame; - frame.kind = Frame_Wasm; + switch (wasmIter().category()) { + case wasm::ProfilingFrameIterator::Baseline: { + frame.kind = FrameKind::Frame_WasmBaseline; + break; + } + case wasm::ProfilingFrameIterator::Ion: { + frame.kind = FrameKind::Frame_WasmIon; + break; + } + default: { + frame.kind = FrameKind::Frame_WasmOther; + break; + } + } frame.stackAddress = stackAddr; frame.returnAddress_ = nullptr; frame.activation = activation_; diff --git a/js/src/vm/StringType-inl.h b/js/src/vm/StringType-inl.h index b0424b868c..8954a46aac 100644 --- a/js/src/vm/StringType-inl.h +++ b/js/src/vm/StringType-inl.h @@ -321,6 +321,10 @@ inline JSRope::JSRope(JSString* left, JSString* right, size_t length) { // |length| must be the sum of the length of both child nodes. MOZ_ASSERT(left->length() + right->length() == length); + // |isLatin1| is set when both children are guaranteed to contain only Latin-1 + // characters. Note that flattening either rope child can clear the Latin-1 + // flag of that child, so it's possible that a Latin-1 rope can end up with + // both children being two-byte (dependent) strings. bool isLatin1 = left->hasLatin1Chars() && right->hasLatin1Chars(); // Do not try to make a rope that could fit inline. diff --git a/js/src/vm/StringType.cpp b/js/src/vm/StringType.cpp index 03f6a7e1ac..63afd8864b 100644 --- a/js/src/vm/StringType.cpp +++ b/js/src/vm/StringType.cpp @@ -2224,10 +2224,12 @@ void JSInlineString::dumpOwnRepresentationFields(js::JSONPrinter& json) const {} void JSLinearString::dumpOwnRepresentationFields(js::JSONPrinter& json) const { if (!isInline()) { - js::gc::StoreBuffer* sb = storeBuffer(); - bool inNursery = sb && sb->nursery().isInside(nonInlineCharsRaw()); - - json.boolProperty("inNursery", inNursery); + // Include whether the chars are in the nursery even for tenured + // strings, which should always be false. For investigating bugs, it's + // better to not assume that. + js::Nursery& nursery = runtimeFromMainThread()->gc.nursery(); + bool inNursery = nursery.isInside(nonInlineCharsRaw()); + json.boolProperty("charsInNursery", inNursery); } } #endif diff --git a/js/src/vm/StringType.h b/js/src/vm/StringType.h index ea2174be42..f2850c33a4 100644 --- a/js/src/vm/StringType.h +++ b/js/src/vm/StringType.h @@ -297,7 +297,10 @@ class JSString : public js::gc::CellWithLengthAndFlags { * If LATIN1_CHARS_BIT is set, the string's characters are stored as Latin1 * instead of TwoByte. This flag can also be set for ropes, if both the * left and right nodes are Latin1. Flattening will result in a Latin1 - * string in this case. + * string in this case. When we flatten a TwoByte rope, we turn child ropes + * (including Latin1 ropes) into TwoByte dependent strings. If one of these + * strings is also part of another Latin1 rope tree, we can have a Latin1 rope + * with a TwoByte descendent. * * The other flags store the string's type. Instead of using a dense index * to represent the most-derived type, string types are encoded to allow @@ -385,6 +388,15 @@ class JSString : public js::gc::CellWithLengthAndFlags { static_assert((TYPE_FLAGS_MASK & js::gc::HeaderWord::RESERVED_MASK) == 0, "GC reserved bits must not be used for Strings"); + // Linear strings: + // - Content and representation are Latin-1 characters. + // - Unmodifiable after construction. + // + // Ropes: + // - Content are Latin-1 characters. + // - Flag may be cleared when the rope is changed into a dependent string. + // + // Also see LATIN1_CHARS_BIT description under "Flag Encoding". static const uint32_t LATIN1_CHARS_BIT = js::Bit(9); // Whether this atom's characters store an uint32 index value less than or diff --git a/js/src/vm/StructuredClone.cpp b/js/src/vm/StructuredClone.cpp index 8f1e131021..e2e67a2ee3 100644 --- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -2568,7 +2568,7 @@ BigInt* JSStructuredCloneReader::readBigInt(uint32_t data) { if (!in.readArray(result->digits().data(), length)) { return nullptr; } - return result; + return JS::BigInt::destructivelyTrimHighZeroDigits(context(), result); } static uint32_t TagToV1ArrayType(uint32_t tag) { diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 0264b481b3..35a2237cd5 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -382,6 +382,7 @@ class TypedArrayObjectTemplate { using FixedLengthTypedArray = FixedLengthTypedArrayObjectTemplate<NativeType>; using ResizableTypedArray = ResizableTypedArrayObjectTemplate<NativeType>; + using AutoLength = ArrayBufferViewObject::AutoLength; static constexpr auto ByteLengthLimit = TypedArrayObject::ByteLengthLimit; static constexpr auto INLINE_BUFFER_LIMIT = @@ -574,7 +575,7 @@ class TypedArrayObjectTemplate { static bool computeAndCheckLength( JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> bufferMaybeUnwrapped, uint64_t byteOffset, uint64_t lengthIndex, size_t* length, - bool* autoLength) { + AutoLength* autoLength) { MOZ_ASSERT(byteOffset % BYTES_PER_ELEMENT == 0); MOZ_ASSERT(byteOffset < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT)); MOZ_ASSERT_IF(lengthIndex != UINT64_MAX, @@ -605,7 +606,7 @@ class TypedArrayObjectTemplate { // Resizable buffers without an explicit length are auto-length. if (bufferMaybeUnwrapped->isResizable()) { *length = 0; - *autoLength = true; + *autoLength = AutoLength::Yes; return true; } @@ -642,7 +643,7 @@ class TypedArrayObjectTemplate { MOZ_ASSERT(len <= ByteLengthLimit / BYTES_PER_ELEMENT); *length = len; - *autoLength = false; + *autoLength = AutoLength::No; return true; } @@ -654,7 +655,7 @@ class TypedArrayObjectTemplate { uint64_t byteOffset, uint64_t lengthIndex, HandleObject proto) { // Steps 5-8. size_t length = 0; - bool autoLength = false; + auto autoLength = AutoLength::No; if (!computeAndCheckLength(cx, buffer, byteOffset, lengthIndex, &length, &autoLength)) { return nullptr; @@ -703,7 +704,7 @@ class TypedArrayObjectTemplate { unwrappedBuffer = &unwrapped->as<ArrayBufferObjectMaybeShared>(); size_t length = 0; - bool autoLength = false; + auto autoLength = AutoLength::No; if (!computeAndCheckLength(cx, unwrappedBuffer, byteOffset, lengthIndex, &length, &autoLength)) { return nullptr; @@ -1025,13 +1026,13 @@ class ResizableTypedArrayObjectTemplate } static ResizableTypedArrayObject* newBuiltinClassInstance( - JSContext* cx, gc::AllocKind allocKind) { + JSContext* cx, gc::AllocKind allocKind, gc::Heap heap) { RootedObject proto(cx, GlobalObject::getOrCreatePrototype(cx, protoKey())); if (!proto) { return nullptr; } return NewTypedArrayObject<ResizableTypedArrayObject>( - cx, instanceClass(), proto, allocKind, gc::Heap::Default); + cx, instanceClass(), proto, allocKind, heap); } static ResizableTypedArrayObject* makeProtoInstance(JSContext* cx, @@ -1044,11 +1045,12 @@ class ResizableTypedArrayObjectTemplate static ResizableTypedArrayObject* makeInstance( JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, - size_t byteOffset, size_t len, bool autoLength, HandleObject proto) { + size_t byteOffset, size_t len, AutoLength autoLength, + HandleObject proto) { MOZ_ASSERT(buffer); MOZ_ASSERT(buffer->isResizable()); MOZ_ASSERT(!buffer->isDetached()); - MOZ_ASSERT(!autoLength || len == 0, + MOZ_ASSERT(autoLength == AutoLength::No || len == 0, "length is zero for 'auto' length views"); MOZ_ASSERT(len <= ByteLengthLimit / BYTES_PER_ELEMENT); @@ -1059,16 +1061,43 @@ class ResizableTypedArrayObjectTemplate if (proto) { obj = makeProtoInstance(cx, proto, allocKind); } else { - obj = newBuiltinClassInstance(cx, allocKind); + obj = newBuiltinClassInstance(cx, allocKind, gc::Heap::Default); } - if (!obj || !obj->init(cx, buffer, byteOffset, len, BYTES_PER_ELEMENT)) { + if (!obj || !obj->initResizable(cx, buffer, byteOffset, len, + BYTES_PER_ELEMENT, autoLength)) { return nullptr; } - obj->setFixedSlot(AUTO_LENGTH_SLOT, BooleanValue(autoLength)); - return obj; } + + static ResizableTypedArrayObject* makeTemplateObject(JSContext* cx) { + gc::AllocKind allocKind = gc::GetGCObjectKind(instanceClass()); + + AutoSetNewObjectMetadata metadata(cx); + + auto* tarray = newBuiltinClassInstance(cx, allocKind, gc::Heap::Tenured); + if (!tarray) { + return nullptr; + } + + tarray->initFixedSlot(TypedArrayObject::BUFFER_SLOT, JS::FalseValue()); + tarray->initFixedSlot(TypedArrayObject::LENGTH_SLOT, + PrivateValue(size_t(0))); + tarray->initFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, + PrivateValue(size_t(0))); + tarray->initFixedSlot(AUTO_LENGTH_SLOT, BooleanValue(false)); + tarray->initFixedSlot(ResizableTypedArrayObject::INITIAL_LENGTH_SLOT, + PrivateValue(size_t(0))); + tarray->initFixedSlot(ResizableTypedArrayObject::INITIAL_BYTE_OFFSET_SLOT, + PrivateValue(size_t(0))); + + // Template objects don't need memory for their elements, since there + // won't be any elements to store. + MOZ_ASSERT(tarray->getReservedSlot(DATA_SLOT).isUndefined()); + + return tarray; + } }; template <typename NativeType> @@ -1499,18 +1528,29 @@ static bool GetTemplateObjectForNative(JSContext* cx, return !!res; } + if (!arg.isObject()) { + return true; + } + auto* obj = &arg.toObject(); + // We don't support wrappers, because of the complicated interaction between // wrapped ArrayBuffers and TypedArrays, see |fromBufferWrapped()|. - if (arg.isObject() && !IsWrapper(&arg.toObject())) { - // We don't use the template's length in the object case, so we can create - // the template typed array with an initial length of zero. - uint32_t len = 0; + if (IsWrapper(obj)) { + return true; + } + + // We don't use the template's length in the object case, so we can create + // the template typed array with an initial length of zero. + uint32_t len = 0; + + if (!obj->is<ArrayBufferObjectMaybeShared>() || + !obj->as<ArrayBufferObjectMaybeShared>().isResizable()) { res.set( FixedLengthTypedArrayObjectTemplate<T>::makeTemplateObject(cx, len)); - return !!res; + } else { + res.set(ResizableTypedArrayObjectTemplate<T>::makeTemplateObject(cx)); } - - return true; + return !!res; } /* static */ bool TypedArrayObject::GetTemplateObjectForNative( @@ -2199,131 +2239,6 @@ bool TypedArrayObjectTemplate<uint64_t>::getElement(JSContext* cx, } } /* anonymous namespace */ -/** - * IsIntegerIndexedObjectOutOfBounds ( iieoRecord ) - * - * IsIntegerIndexedObjectOutOfBounds can be rewritten into the following spec - * steps when inlining the call to - * MakeIntegerIndexedObjectWithBufferWitnessRecord. - * - * 1. Let buffer be O.[[ViewedArrayBuffer]]. - * 2. If IsDetachedBuffer(buffer) is true, then - * a. Return true. - * 3. If IsFixedLengthArrayBuffer(buffer) is true, then - * a. Return false. - * 4. Let bufferByteLength be ArrayBufferByteLength(buffer, order). - * 5. Let byteOffsetStart be O.[[ByteOffset]]. - * 6. If byteOffsetStart > bufferByteLength, then - * a. Return true. - * 7. If O.[[ArrayLength]] is auto, then - * a. Return false. - * 8. Let elementSize be TypedArrayElementSize(O). - * 9. Let byteOffsetEnd be byteOffsetStart + O.[[ArrayLength]] × elementSize. - * 10. If byteOffsetEnd > bufferByteLength, then - * a. Return true. - * 11. Return false. - * - * The additional call to IsFixedLengthArrayBuffer is an optimization to skip - * unnecessary validation which don't apply for fixed length typed arrays. - * - * https://tc39.es/ecma262/#sec-isintegerindexedobjectoutofbounds - * https://tc39.es/ecma262/#sec-makeintegerindexedobjectwithbufferwitnessrecord - */ -mozilla::Maybe<size_t> TypedArrayObject::byteOffset() const { - if (MOZ_UNLIKELY(hasDetachedBuffer())) { - return mozilla::Nothing{}; - } - - size_t byteOffsetStart = ArrayBufferViewObject::byteOffset(); - - if (MOZ_LIKELY(is<FixedLengthTypedArrayObject>())) { - return mozilla::Some(byteOffsetStart); - } - - auto* buffer = bufferEither(); - MOZ_ASSERT(buffer->isResizable()); - - size_t bufferByteLength = buffer->byteLength(); - if (byteOffsetStart > bufferByteLength) { - return mozilla::Nothing{}; - } - - if (as<ResizableTypedArrayObject>().isAutoLength()) { - return mozilla::Some(byteOffsetStart); - } - - size_t viewByteLength = rawByteLength(); - size_t byteOffsetEnd = byteOffsetStart + viewByteLength; - if (byteOffsetEnd > bufferByteLength) { - return mozilla::Nothing{}; - } - return mozilla::Some(byteOffsetStart); -} - -/** - * IntegerIndexedObjectLength ( iieoRecord ) - * - * IntegerIndexedObjectLength can be rewritten into the following spec - * steps when inlining the calls to IsIntegerIndexedObjectOutOfBounds and - * MakeIntegerIndexedObjectWithBufferWitnessRecord. - * - * 1. Let buffer be O.[[ViewedArrayBuffer]]. - * 2. If IsDetachedBuffer(buffer) is true, then - * a. Return out-of-bounds. - * 3. If IsFixedLengthArrayBuffer(buffer) is true, then - * a. Return O.[[ArrayLength]]. - * 4. Let bufferByteLength be ArrayBufferByteLength(buffer, order). - * 5. Let byteOffsetStart be O.[[ByteOffset]]. - * 6. If byteOffsetStart > bufferByteLength, then - * a. Return out-of-bounds. - * 7. If O.[[ArrayLength]] is auto, then - * a. Let elementSize be TypedArrayElementSize(O). - * b. Return floor((bufferByteLength - byteOffsetStart) / elementSize). - * 8. Let elementSize be TypedArrayElementSize(O). - * 9. Let byteOffsetEnd be byteOffsetStart + O.[[ArrayLength]] × elementSize. - * 10. If byteOffsetEnd > bufferByteLength, then - * a. Return out-of-bounds. - * 11. Return O.[[ArrayLength]]. - * - * The additional call to IsFixedLengthArrayBuffer is an optimization to skip - * unnecessary validation which don't apply for fixed length typed arrays. - * - * https://tc39.es/ecma262/#sec-integerindexedobjectlength - * https://tc39.es/ecma262/#sec-isintegerindexedobjectoutofbounds - * https://tc39.es/ecma262/#sec-makeintegerindexedobjectwithbufferwitnessrecord - */ -mozilla::Maybe<size_t> TypedArrayObject::length() const { - if (MOZ_UNLIKELY(hasDetachedBuffer())) { - return mozilla::Nothing{}; - } - - if (MOZ_LIKELY(is<FixedLengthTypedArrayObject>())) { - size_t arrayLength = rawLength(); - return mozilla::Some(arrayLength); - } - - auto* buffer = bufferEither(); - MOZ_ASSERT(buffer->isResizable()); - - size_t bufferByteLength = buffer->byteLength(); - size_t byteOffsetStart = ArrayBufferViewObject::byteOffset(); - if (byteOffsetStart > bufferByteLength) { - return mozilla::Nothing{}; - } - - if (as<ResizableTypedArrayObject>().isAutoLength()) { - size_t bytes = bufferByteLength - byteOffsetStart; - return mozilla::Some(bytes / bytesPerElement()); - } - - size_t arrayLength = rawLength(); - size_t byteOffsetEnd = byteOffsetStart + arrayLength * bytesPerElement(); - if (byteOffsetEnd > bufferByteLength) { - return mozilla::Nothing{}; - } - return mozilla::Some(arrayLength); -} - namespace js { template <> diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index b6b1f00e72..46531ec4ee 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -69,27 +69,30 @@ class TypedArrayObject : public ArrayBufferViewObject { static bool ensureHasBuffer(JSContext* cx, Handle<TypedArrayObject*> typedArray); - protected: - size_t rawByteLength() const { return rawLength() * bytesPerElement(); } - - size_t rawLength() const { - return size_t(getFixedSlot(LENGTH_SLOT).toPrivate()); - } - public: - mozilla::Maybe<size_t> byteOffset() const; + /** + * Return the current length, or |Nothing| if the TypedArray is detached or + * out-of-bounds. + */ + mozilla::Maybe<size_t> length() const { + return ArrayBufferViewObject::length(); + } + /** + * Return the current byteLength, or |Nothing| if the TypedArray is detached + * or out-of-bounds. + */ mozilla::Maybe<size_t> byteLength() const { return length().map( [this](size_t value) { return value * bytesPerElement(); }); } - mozilla::Maybe<size_t> length() const; - // Self-hosted TypedArraySubarray function needs to read [[ByteOffset]], even // when it's currently out-of-bounds. size_t byteOffsetMaybeOutOfBounds() const { - return ArrayBufferViewObject::byteOffset(); + // dataPointerOffset() returns the [[ByteOffset]] spec value, except when + // the buffer is detached. (bug 1840991) + return ArrayBufferViewObject::dataPointerOffset(); } template <AllowGC allowGC> @@ -148,11 +151,13 @@ class FixedLengthTypedArrayObject : public TypedArrayObject { static inline gc::AllocKind AllocKindForLazyBuffer(size_t nbytes); - size_t byteOffset() const { return ArrayBufferViewObject::byteOffset(); } + size_t byteOffset() const { + return ArrayBufferViewObject::byteOffsetSlotValue(); + } - size_t byteLength() const { return rawByteLength(); } + size_t byteLength() const { return length() * bytesPerElement(); } - size_t length() const { return rawLength(); } + size_t length() const { return ArrayBufferViewObject::lengthSlotValue(); } bool hasInlineElements() const; void setInlineElements(); @@ -176,13 +181,7 @@ class FixedLengthTypedArrayObject : public TypedArrayObject { class ResizableTypedArrayObject : public TypedArrayObject { public: - static const uint8_t AUTO_LENGTH_SLOT = TypedArrayObject::RESERVED_SLOTS; - - static const uint8_t RESERVED_SLOTS = TypedArrayObject::RESERVED_SLOTS + 1; - - bool isAutoLength() const { - return getFixedSlot(AUTO_LENGTH_SLOT).toBoolean(); - } + static const uint8_t RESERVED_SLOTS = RESIZABLE_RESERVED_SLOTS; }; extern TypedArrayObject* NewTypedArrayWithTemplateAndLength( diff --git a/js/src/vm/UbiNodeCensus.cpp b/js/src/vm/UbiNodeCensus.cpp index ba3ccd0898..7e33341d30 100644 --- a/js/src/vm/UbiNodeCensus.cpp +++ b/js/src/vm/UbiNodeCensus.cpp @@ -6,6 +6,8 @@ #include "js/UbiNodeCensus.h" +#include "mozilla/ScopeExit.h" + #include "builtin/MapObject.h" #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* #include "js/Printer.h" @@ -1062,17 +1064,19 @@ JS_PUBLIC_API bool CensusHandler::operator()( /*** Parsing Breakdowns *****************************************************/ -static CountTypePtr ParseChildBreakdown(JSContext* cx, HandleObject breakdown, - PropertyName* prop) { +static CountTypePtr ParseChildBreakdown( + JSContext* cx, HandleObject breakdown, PropertyName* prop, + MutableHandle<GCVector<JSLinearString*>> seen) { RootedValue v(cx); if (!GetProperty(cx, breakdown, breakdown, prop, &v)) { return nullptr; } - return ParseBreakdown(cx, v); + return ParseBreakdown(cx, v, seen); } -JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, - HandleValue breakdownValue) { +JS_PUBLIC_API CountTypePtr +ParseBreakdown(JSContext* cx, HandleValue breakdownValue, + MutableHandle<GCVector<JSLinearString*>> seen) { if (breakdownValue.isUndefined()) { // Construct the default type, { by: 'count' } CountTypePtr simple(cx->new_<SimpleCount>()); @@ -1097,6 +1101,24 @@ JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, return nullptr; } + for (auto candidate : seen.get()) { + if (EqualStrings(by, candidate)) { + UniqueChars byBytes = QuoteString(cx, by, '"'); + if (!byBytes) { + return nullptr; + } + + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_DEBUG_CENSUS_BREAKDOWN_NESTED, + byBytes.get()); + return nullptr; + } + } + if (!seen.append(by)) { + return nullptr; + } + auto popper = mozilla::MakeScopeExit([&]() { seen.popBack(); }); + if (StringEqualsLiteral(by, "count")) { RootedValue countValue(cx), bytesValue(cx); if (!GetProperty(cx, breakdown, breakdown, cx->names().count, @@ -1140,13 +1162,14 @@ JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, } if (StringEqualsLiteral(by, "objectClass")) { - CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then)); + CountTypePtr thenType( + ParseChildBreakdown(cx, breakdown, cx->names().then, seen)); if (!thenType) { return nullptr; } CountTypePtr otherType( - ParseChildBreakdown(cx, breakdown, cx->names().other)); + ParseChildBreakdown(cx, breakdown, cx->names().other, seen)); if (!otherType) { return nullptr; } @@ -1156,27 +1179,27 @@ JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, if (StringEqualsLiteral(by, "coarseType")) { CountTypePtr objectsType( - ParseChildBreakdown(cx, breakdown, cx->names().objects)); + ParseChildBreakdown(cx, breakdown, cx->names().objects, seen)); if (!objectsType) { return nullptr; } CountTypePtr scriptsType( - ParseChildBreakdown(cx, breakdown, cx->names().scripts)); + ParseChildBreakdown(cx, breakdown, cx->names().scripts, seen)); if (!scriptsType) { return nullptr; } CountTypePtr stringsType( - ParseChildBreakdown(cx, breakdown, cx->names().strings)); + ParseChildBreakdown(cx, breakdown, cx->names().strings, seen)); if (!stringsType) { return nullptr; } CountTypePtr otherType( - ParseChildBreakdown(cx, breakdown, cx->names().other)); + ParseChildBreakdown(cx, breakdown, cx->names().other, seen)); if (!otherType) { return nullptr; } CountTypePtr domNodeType( - ParseChildBreakdown(cx, breakdown, cx->names().domNode)); + ParseChildBreakdown(cx, breakdown, cx->names().domNode, seen)); if (!domNodeType) { return nullptr; } @@ -1186,7 +1209,8 @@ JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, } if (StringEqualsLiteral(by, "internalType")) { - CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then)); + CountTypePtr thenType( + ParseChildBreakdown(cx, breakdown, cx->names().then, seen)); if (!thenType) { return nullptr; } @@ -1195,7 +1219,8 @@ JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, } if (StringEqualsLiteral(by, "descriptiveType")) { - CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then)); + CountTypePtr thenType( + ParseChildBreakdown(cx, breakdown, cx->names().then, seen)); if (!thenType) { return nullptr; } @@ -1203,12 +1228,13 @@ JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, } if (StringEqualsLiteral(by, "allocationStack")) { - CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then)); + CountTypePtr thenType( + ParseChildBreakdown(cx, breakdown, cx->names().then, seen)); if (!thenType) { return nullptr; } CountTypePtr noStackType( - ParseChildBreakdown(cx, breakdown, cx->names().noStack)); + ParseChildBreakdown(cx, breakdown, cx->names().noStack, seen)); if (!noStackType) { return nullptr; } @@ -1217,13 +1243,14 @@ JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx, } if (StringEqualsLiteral(by, "filename")) { - CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then)); + CountTypePtr thenType( + ParseChildBreakdown(cx, breakdown, cx->names().then, seen)); if (!thenType) { return nullptr; } CountTypePtr noFilenameType( - ParseChildBreakdown(cx, breakdown, cx->names().noFilename)); + ParseChildBreakdown(cx, breakdown, cx->names().noFilename, seen)); if (!noFilenameType) { return nullptr; } @@ -1307,8 +1334,9 @@ JS_PUBLIC_API bool ParseCensusOptions(JSContext* cx, Census& census, return false; } + Rooted<GCVector<JSLinearString*>> seen(cx, cx); outResult = breakdown.isUndefined() ? GetDefaultBreakdown(cx) - : ParseBreakdown(cx, breakdown); + : ParseBreakdown(cx, breakdown, &seen); return !!outResult; } diff --git a/js/src/vm/Value.cpp b/js/src/vm/Value.cpp index 8fcad7ee83..0da89a41c2 100644 --- a/js/src/vm/Value.cpp +++ b/js/src/vm/Value.cpp @@ -10,6 +10,7 @@ #include <inttypes.h> +#include "gc/Cell.h" // js::gc::Cell #include "js/Conversions.h" // JS::NumberToString, JS::MaximumNumberToStringLength #include "js/Printer.h" // js::GenericPrinter, js::Fprinter #include "vm/BigIntType.h" // JS::BigInt @@ -41,6 +42,12 @@ const HandleValue FalseHandleValue = const Handle<mozilla::Maybe<Value>> NothingHandleValue = Handle<mozilla::Maybe<Value>>::fromMarkedLocation(&JSVAL_NOTHING); +#ifdef DEBUG +void JS::Value::assertTraceKindMatches(js::gc::Cell* cell) const { + MOZ_ASSERT(traceKind() == cell->getTraceKind()); +} +#endif + } // namespace JS void js::ReportBadValueTypeAndCrash(const JS::Value& value) { diff --git a/js/src/vm/Watchtower.cpp b/js/src/vm/Watchtower.cpp index 80023d7e81..86c748285d 100644 --- a/js/src/vm/Watchtower.cpp +++ b/js/src/vm/Watchtower.cpp @@ -102,23 +102,24 @@ static void InvalidateMegamorphicCache(JSContext* cx, } void MaybePopReturnFuses(JSContext* cx, Handle<NativeObject*> nobj) { - JSObject* objectProto = &cx->global()->getObjectPrototype(); + GlobalObject* global = &nobj->global(); + JSObject* objectProto = &global->getObjectPrototype(); if (nobj == objectProto) { nobj->realm()->realmFuses.objectPrototypeHasNoReturnProperty.popFuse( cx, nobj->realm()->realmFuses); return; } - JSObject* iteratorProto = cx->global()->maybeGetIteratorPrototype(); + JSObject* iteratorProto = global->maybeGetIteratorPrototype(); if (nobj == iteratorProto) { nobj->realm()->realmFuses.iteratorPrototypeHasNoReturnProperty.popFuse( cx, nobj->realm()->realmFuses); return; } - JSObject* arrayIterProto = cx->global()->maybeGetArrayIteratorPrototype(); + JSObject* arrayIterProto = global->maybeGetArrayIteratorPrototype(); if (nobj == arrayIterProto) { - cx->realm()->realmFuses.arrayIteratorPrototypeHasNoReturnProperty.popFuse( + nobj->realm()->realmFuses.arrayIteratorPrototypeHasNoReturnProperty.popFuse( cx, nobj->realm()->realmFuses); return; } @@ -208,12 +209,12 @@ static bool WatchProtoChangeImpl(JSContext* cx, HandleObject obj) { InvalidateMegamorphicCache(cx, obj.as<NativeObject>()); NativeObject* nobj = &obj->as<NativeObject>(); - if (nobj == cx->global()->maybeGetArrayIteratorPrototype()) { + if (nobj == nobj->global().maybeGetArrayIteratorPrototype()) { nobj->realm()->realmFuses.arrayIteratorPrototypeHasIteratorProto.popFuse( cx, nobj->realm()->realmFuses); } - if (nobj == cx->global()->maybeGetIteratorPrototype()) { + if (nobj == nobj->global().maybeGetIteratorPrototype()) { nobj->realm()->realmFuses.iteratorPrototypeHasObjectProto.popFuse( cx, nobj->realm()->realmFuses); } |