summaryrefslogtreecommitdiffstats
path: root/js/src/vm
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm')
-rw-r--r--js/src/vm/ArgumentsObject.h26
-rw-r--r--js/src/vm/ArrayBufferObject.cpp30
-rw-r--r--js/src/vm/ArrayBufferObject.h14
-rw-r--r--js/src/vm/ArrayBufferViewObject.cpp175
-rw-r--r--js/src/vm/ArrayBufferViewObject.h112
-rw-r--r--js/src/vm/BigIntType.h1
-rw-r--r--js/src/vm/CharacterEncoding.cpp13
-rw-r--r--js/src/vm/CommonPropertyNames.h1
-rw-r--r--js/src/vm/EnvironmentObject.cpp34
-rw-r--r--js/src/vm/EnvironmentObject.h7
-rw-r--r--js/src/vm/GlobalObject.cpp2
-rw-r--r--js/src/vm/GlobalObject.h13
-rw-r--r--js/src/vm/HelperThreadState.h21
-rw-r--r--js/src/vm/HelperThreads.cpp2
-rw-r--r--js/src/vm/Interpreter.cpp10
-rw-r--r--js/src/vm/Interpreter.h2
-rw-r--r--js/src/vm/Iteration.cpp2
-rw-r--r--js/src/vm/JSContext-inl.h46
-rw-r--r--js/src/vm/JSContext.cpp30
-rw-r--r--js/src/vm/JSContext.h10
-rw-r--r--js/src/vm/JSONParser.cpp3
-rw-r--r--js/src/vm/JSONParser.h2
-rw-r--r--js/src/vm/JSObject.cpp66
-rw-r--r--js/src/vm/JSScript.cpp6
-rw-r--r--js/src/vm/MemoryMetrics.cpp11
-rw-r--r--js/src/vm/Modules.cpp269
-rw-r--r--js/src/vm/Modules.h2
-rw-r--r--js/src/vm/NativeObject.cpp4
-rw-r--r--js/src/vm/Opcodes.h3
-rw-r--r--js/src/vm/PlainObject.cpp54
-rw-r--r--js/src/vm/PlainObject.h5
-rw-r--r--js/src/vm/PortableBaselineInterpret.cpp1053
-rw-r--r--js/src/vm/Realm.cpp10
-rw-r--r--js/src/vm/Realm.h6
-rw-r--r--js/src/vm/RegExpObject.cpp40
-rw-r--r--js/src/vm/RegExpShared.h2
-rw-r--r--js/src/vm/Runtime.h6
-rw-r--r--js/src/vm/Scope.cpp27
-rw-r--r--js/src/vm/Scope.h4
-rw-r--r--js/src/vm/SharedArrayObject.h8
-rw-r--r--js/src/vm/SharedStencil.h44
-rw-r--r--js/src/vm/Stack.cpp15
-rw-r--r--js/src/vm/StringType-inl.h4
-rw-r--r--js/src/vm/StringType.cpp10
-rw-r--r--js/src/vm/StringType.h14
-rw-r--r--js/src/vm/StructuredClone.cpp2
-rw-r--r--js/src/vm/TypedArrayObject.cpp205
-rw-r--r--js/src/vm/TypedArrayObject.h41
-rw-r--r--js/src/vm/UbiNodeCensus.cpp66
-rw-r--r--js/src/vm/Value.cpp7
-rw-r--r--js/src/vm/Watchtower.cpp13
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);
}