Revert "Bug 1964692 - Part 8: Add fast-path when (Shared)ArrayBuffer species fuses are intact. r=jandem"

This reverts commit f206dc3f94.

Revert "Bug 1964692 - Part 7: Add fuses for ArrayBuffer and SharedArrayBuffer species. r=jandem"

This reverts commit 48314b5b5f.

Revert "Bug 1964692 - Part 6: Remove self-hosting functions for SharedArrayBufferSlice. r=jandem"

This reverts commit e854beadd7.

Revert "Bug 1964692 - Part 5: Implement SharedArrayBuffer.prototype.slice in C++. r=jandem"

This reverts commit c5fc89b3f3.

Revert "Bug 1964692 - Part 4: Remove unused built-in object kinds. r=jandem"

This reverts commit 85491af690.

Revert "Bug 1964692 - Part 3: Remove self-hosting functions for ArrayBufferSlice. r=jandem"

This reverts commit 6cfc2c0ecf.

Revert "Bug 1964692 - Part 2: Implement ArrayBuffer.prototype.slice in C++. r=jandem"

This reverts commit 792883453d.

Revert "Bug 1964692 - Part 1: Move ToIntegerIndex to jsnum. r=jandem"

This reverts commit fd25a9d5fa.
This commit is contained in:
agoloman
2025-05-09 08:52:17 +03:00
committed by agoloman@mozilla.com
parent f206dc3f94
commit 2a61527c1a
27 changed files with 523 additions and 611 deletions

View File

@@ -1322,6 +1322,123 @@ function IterableToList(items, method) {
return values; return values;
} }
// ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9
// 24.1.4.3 ArrayBuffer.prototype.slice ( start, end )
function ArrayBufferSlice(start, end) {
// Step 1.
var O = this;
// Steps 2-3,
// This function is not generic.
if (!IsObject(O) || (O = GuardToArrayBuffer(O)) === null) {
return callFunction(
CallArrayBufferMethodIfWrapped,
this,
start,
end,
"ArrayBufferSlice"
);
}
// Step 4.
if (IsDetachedBuffer(O)) {
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
}
// Step 5.
var len = ArrayBufferByteLength(O);
// Step 6.
var relativeStart = ToInteger(start);
// Step 7.
var first =
relativeStart < 0
? std_Math_max(len + relativeStart, 0)
: std_Math_min(relativeStart, len);
// Step 8.
var relativeEnd = end === undefined ? len : ToInteger(end);
// Step 9.
var final =
relativeEnd < 0
? std_Math_max(len + relativeEnd, 0)
: std_Math_min(relativeEnd, len);
// Step 10.
var newLen = std_Math_max(final - first, 0);
// Step 11
var ctor = SpeciesConstructor(O, GetBuiltinConstructor("ArrayBuffer"));
// Step 12.
var new_ = constructContentFunction(ctor, ctor, newLen);
// Steps 13-15.
var isWrapped = false;
var newBuffer;
if ((newBuffer = GuardToArrayBuffer(new_)) !== null) {
// Step 15.
if (IsDetachedBuffer(newBuffer)) {
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
}
} else {
newBuffer = new_;
// Steps 13-14.
if (!IsWrappedArrayBuffer(newBuffer)) {
ThrowTypeError(JSMSG_NON_ARRAY_BUFFER_RETURNED);
}
isWrapped = true;
// Step 15.
if (
callFunction(
CallArrayBufferMethodIfWrapped,
newBuffer,
"IsDetachedBufferThis"
)
) {
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
}
}
// Step 16.
if (newBuffer === O) {
ThrowTypeError(JSMSG_SAME_ARRAY_BUFFER_RETURNED);
}
// Step 17.
var actualLen = PossiblyWrappedArrayBufferByteLength(newBuffer);
if (actualLen < newLen) {
ThrowTypeError(JSMSG_SHORT_ARRAY_BUFFER_RETURNED, newLen, actualLen);
}
// Steps 18-19.
if (IsDetachedBuffer(O)) {
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
}
// Steps 20-22.
//
// Reacquire the length in case the buffer has been resized.
var currentLen = ArrayBufferByteLength(O);
if (first < currentLen) {
var count = std_Math_min(newLen, currentLen - first);
ArrayBufferCopyData(newBuffer, 0, O, first, count, isWrapped);
}
// Step 23.
return newBuffer;
}
function IsDetachedBufferThis() {
return IsDetachedBuffer(this);
}
// ES 2016 draft Mar 25, 2016 24.1.3.3. // ES 2016 draft Mar 25, 2016 24.1.3.3.
function $ArrayBufferSpecies() { function $ArrayBufferSpecies() {
// Step 1. // Step 1.
@@ -1336,6 +1453,83 @@ function $SharedArrayBufferSpecies() {
} }
SetCanonicalName($SharedArrayBufferSpecies, "get [Symbol.species]"); SetCanonicalName($SharedArrayBufferSpecies, "get [Symbol.species]");
// ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9
// 24.2.4.3 SharedArrayBuffer.prototype.slice ( start, end )
function SharedArrayBufferSlice(start, end) {
// Step 1.
var O = this;
// Steps 2-3.
// This function is not generic.
if (!IsObject(O) || (O = GuardToSharedArrayBuffer(O)) === null) {
return callFunction(
CallSharedArrayBufferMethodIfWrapped,
this,
start,
end,
"SharedArrayBufferSlice"
);
}
// Step 4.
var len = SharedArrayBufferByteLength(O);
// Step 5.
var relativeStart = ToInteger(start);
// Step 6.
var first =
relativeStart < 0
? std_Math_max(len + relativeStart, 0)
: std_Math_min(relativeStart, len);
// Step 7.
var relativeEnd = end === undefined ? len : ToInteger(end);
// Step 8.
var final =
relativeEnd < 0
? std_Math_max(len + relativeEnd, 0)
: std_Math_min(relativeEnd, len);
// Step 9.
var newLen = std_Math_max(final - first, 0);
// Step 10
var ctor = SpeciesConstructor(O, GetBuiltinConstructor("SharedArrayBuffer"));
// Step 11.
var new_ = constructContentFunction(ctor, ctor, newLen);
// Steps 12-13.
var isWrapped = false;
var newObj;
if ((newObj = GuardToSharedArrayBuffer(new_)) === null) {
if (!IsWrappedSharedArrayBuffer(new_)) {
ThrowTypeError(JSMSG_NON_SHARED_ARRAY_BUFFER_RETURNED);
}
isWrapped = true;
newObj = new_;
}
// Step 14.
if (newObj === O || SharedArrayBuffersMemorySame(newObj, O)) {
ThrowTypeError(JSMSG_SAME_SHARED_ARRAY_BUFFER_RETURNED);
}
// Step 15.
var actualLen = PossiblyWrappedSharedArrayBufferByteLength(newObj);
if (actualLen < newLen) {
ThrowTypeError(JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED, newLen, actualLen);
}
// Steps 16-18.
SharedArrayBufferCopyData(newObj, 0, O, first, newLen, isWrapped);
// Step 19.
return newObj;
}
// https://github.com/tc39/proposal-change-array-by-copy // https://github.com/tc39/proposal-change-array-by-copy
function TypedArrayCreateSameType(exemplar, length) { function TypedArrayCreateSameType(exemplar, length) {
// Step 1. Assert: exemplar is an Object that has [[TypedArrayName]] and [[ContentType]] internal slots. // Step 1. Assert: exemplar is an Object that has [[TypedArrayName]] and [[ContentType]] internal slots.

View File

@@ -737,8 +737,7 @@ bool ParserAtomsTable::isExtendedUnclonedSelfHostedFunctionName(
case WellKnownAtomId::dollar_ArraySpecies_: case WellKnownAtomId::dollar_ArraySpecies_:
case WellKnownAtomId::dollar_ArrayValues_: case WellKnownAtomId::dollar_ArrayValues_:
case WellKnownAtomId::dollar_RegExpFlagsGetter_: case WellKnownAtomId::dollar_RegExpFlagsGetter_:
case WellKnownAtomId::dollar_RegExpToString_: case WellKnownAtomId::dollar_RegExpToString_: {
case WellKnownAtomId::dollar_SharedArrayBufferSpecies_: {
#ifdef DEBUG #ifdef DEBUG
const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId()); const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
MOZ_ASSERT(info.content[0] == MOZ_ASSERT(info.content[0] ==

View File

@@ -1,19 +0,0 @@
function test() {
// Mutating ArrayBuffer.prototype.constructor pops the fuse. A no-op change is fine.
newGlobal().evaluate(`
assertEq(getFuseState().OptimizeArrayBufferSpeciesFuse.intact, true);
let p = ArrayBuffer.prototype.constructor;
ArrayBuffer.prototype.constructor = p;
assertEq(getFuseState().OptimizeArrayBufferSpeciesFuse.intact, true);
ArrayBuffer.prototype.constructor = Object;
assertEq(getFuseState().OptimizeArrayBufferSpeciesFuse.intact, false);
`);
// Mutating ArrayBuffer[Symbol.species] pops the fuse.
newGlobal().evaluate(`
assertEq(getFuseState().OptimizeArrayBufferSpeciesFuse.intact, true);
delete ArrayBuffer[Symbol.species];
assertEq(getFuseState().OptimizeArrayBufferSpeciesFuse.intact, false);
`);
}
test();

View File

@@ -1,17 +0,0 @@
// Test for array buffer species fuse with multiple realms.
function test() {
var g = newGlobal();
var arr = g.evaluate(`new ArrayBuffer(4)`);
var count = 0;
Object.defineProperty(g.ArrayBuffer.prototype, "constructor", {get: function() {
count++;
return ArrayBuffer;
}});
for (var i = 0; i < 20; i++) {
assertEq(ArrayBuffer.prototype.slice.call(arr).byteLength, 4);
}
assertEq(count, 20);
assertEq(getFuseState().OptimizeArrayBufferSpeciesFuse.intact, true);
assertEq(g.getFuseState().OptimizeArrayBufferSpeciesFuse.intact, false);
}
test();

View File

@@ -1,19 +0,0 @@
function test() {
// Mutating SharedArrayBuffer.prototype.constructor pops the fuse. A no-op change is fine.
newGlobal().evaluate(`
assertEq(getFuseState().OptimizeSharedArrayBufferSpeciesFuse.intact, true);
let p = SharedArrayBuffer.prototype.constructor;
SharedArrayBuffer.prototype.constructor = p;
assertEq(getFuseState().OptimizeSharedArrayBufferSpeciesFuse.intact, true);
SharedArrayBuffer.prototype.constructor = Object;
assertEq(getFuseState().OptimizeSharedArrayBufferSpeciesFuse.intact, false);
`);
// Mutating SharedArrayBuffer[Symbol.species] pops the fuse.
newGlobal().evaluate(`
assertEq(getFuseState().OptimizeSharedArrayBufferSpeciesFuse.intact, true);
delete SharedArrayBuffer[Symbol.species];
assertEq(getFuseState().OptimizeSharedArrayBufferSpeciesFuse.intact, false);
`);
}
test();

View File

@@ -1,17 +0,0 @@
// Test for shared array buffer species fuse with multiple realms.
function test() {
var g = newGlobal();
var arr = g.evaluate(`new SharedArrayBuffer(4)`);
var count = 0;
Object.defineProperty(g.SharedArrayBuffer.prototype, "constructor", {get: function() {
count++;
return SharedArrayBuffer;
}});
for (var i = 0; i < 20; i++) {
assertEq(SharedArrayBuffer.prototype.slice.call(arr).byteLength, 4);
}
assertEq(count, 20);
assertEq(getFuseState().OptimizeSharedArrayBufferSpeciesFuse.intact, true);
assertEq(g.getFuseState().OptimizeSharedArrayBufferSpeciesFuse.intact, false);
}
test();

View File

@@ -0,0 +1,20 @@
// |jit-test| --ion-warmup-threshold=50
var ArrayBufferByteLength = getSelfHostedValue("ArrayBufferByteLength");
function testBasic() {
var arr = [1, 2, 3];
var tarr = new Int32Array(arr);
var abuf = tarr.buffer;
var arrLength = arr.length;
var bytesPerElement = Int32Array.BYTES_PER_ELEMENT;
var f = function() {
assertEq(ArrayBufferByteLength(abuf), arrLength * bytesPerElement);
};
do {
f();
} while (!inIon());
}
testBasic();

View File

@@ -0,0 +1,41 @@
// |jit-test| --ion-warmup-threshold=50
var PossiblyWrappedArrayBufferByteLength = getSelfHostedValue("PossiblyWrappedArrayBufferByteLength");
function testBasic() {
var arr = [1, 2, 3];
var tarr = new Int32Array(arr);
var abuf = tarr.buffer;
var arrLength = arr.length;
var bytesPerElement = Int32Array.BYTES_PER_ELEMENT;
var f = function() {
assertEq(PossiblyWrappedArrayBufferByteLength(abuf), arrLength * bytesPerElement);
};
do {
f();
} while (!inIon());
}
testBasic();
function testWrapped() {
var g = newGlobal();
g.eval(`
var arr = [1, 2, 3];
var tarr = new Int32Array(arr);
var abuf = tarr.buffer;
`);
var abuf = g.abuf;
var arrLength = g.arr.length;
var bytesPerElement = g.Int32Array.BYTES_PER_ELEMENT;
var f = function() {
assertEq(PossiblyWrappedArrayBufferByteLength(abuf), arrLength * bytesPerElement);
};
do {
f();
} while (!inIon());
}
testWrapped();

View File

@@ -11,6 +11,7 @@ const dvSmall = new DataView(bufferSmall);
const dvLargeOffset = new DataView(bufferLarge, 5 * gb); const dvLargeOffset = new DataView(bufferLarge, 5 * gb);
const dvLargeLength = new DataView(bufferLarge); const dvLargeLength = new DataView(bufferLarge);
const ArrayBufferByteLength = getSelfHostedValue("ArrayBufferByteLength");
const TypedArrayByteOffset = getSelfHostedValue("TypedArrayByteOffset"); const TypedArrayByteOffset = getSelfHostedValue("TypedArrayByteOffset");
const TypedArrayLength = getSelfHostedValue("TypedArrayLength"); const TypedArrayLength = getSelfHostedValue("TypedArrayLength");
@@ -18,6 +19,7 @@ function testBufferByteLengthInt32() {
var arr = [bufferLarge, bufferSmall]; var arr = [bufferLarge, bufferSmall];
for (var i = 0; i < 2000; i++) { for (var i = 0; i < 2000; i++) {
var idx = +(i < 1900); // First 1 then 0. var idx = +(i < 1900); // First 1 then 0.
assertEq(ArrayBufferByteLength(arr[idx]), idx === 0 ? 6 * gb : 8);
assertEq(arr[idx].byteLength, idx === 0 ? 6 * gb : 8); assertEq(arr[idx].byteLength, idx === 0 ? 6 * gb : 8);
} }
} }

View File

@@ -11122,6 +11122,45 @@ AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayLength(
return AttachDecision::Attach; return AttachDecision::Attach;
} }
AttachDecision InlinableNativeIRGenerator::tryAttachArrayBufferByteLength(
bool isPossiblyWrapped) {
// Self-hosted code calls this with a single, possibly wrapped,
// ArrayBufferObject argument.
MOZ_ASSERT(args_.length() == 1);
MOZ_ASSERT(args_[0].isObject());
// Only optimize when the object isn't a wrapper.
if (isPossiblyWrapped && IsWrapper(&args_[0].toObject())) {
return AttachDecision::NoAction;
}
MOZ_ASSERT(args_[0].toObject().is<ArrayBufferObject>());
auto* buffer = &args_[0].toObject().as<ArrayBufferObject>();
// Initialize the input operand.
initializeInputOperand();
// Note: we don't need to call emitNativeCalleeGuard for intrinsics.
ValOperandId argId = loadArgumentIntrinsic(ArgumentKind::Arg0);
ObjOperandId objArgId = writer.guardToObject(argId);
if (isPossiblyWrapped) {
writer.guardIsNotProxy(objArgId);
}
if (buffer->byteLength() <= INT32_MAX) {
writer.loadArrayBufferByteLengthInt32Result(objArgId);
} else {
writer.loadArrayBufferByteLengthDoubleResult(objArgId);
}
writer.returnFromIC();
trackAttached("ArrayBufferByteLength");
return AttachDecision::Attach;
}
AttachDecision InlinableNativeIRGenerator::tryAttachIsConstructing() { AttachDecision InlinableNativeIRGenerator::tryAttachIsConstructing() {
// Self-hosted code calls this with no arguments in function scripts. // Self-hosted code calls this with no arguments in function scripts.
MOZ_ASSERT(args_.length() == 0); MOZ_ASSERT(args_.length() == 0);
@@ -12467,6 +12506,10 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() {
// ArrayBuffer intrinsics. // ArrayBuffer intrinsics.
case InlinableNative::IntrinsicGuardToArrayBuffer: case InlinableNative::IntrinsicGuardToArrayBuffer:
return tryAttachGuardToArrayBuffer(); return tryAttachGuardToArrayBuffer();
case InlinableNative::IntrinsicArrayBufferByteLength:
return tryAttachArrayBufferByteLength(/* isPossiblyWrapped = */ false);
case InlinableNative::IntrinsicPossiblyWrappedArrayBufferByteLength:
return tryAttachArrayBufferByteLength(/* isPossiblyWrapped = */ true);
// SharedArrayBuffer intrinsics. // SharedArrayBuffer intrinsics.
case InlinableNative::IntrinsicGuardToSharedArrayBuffer: case InlinableNative::IntrinsicGuardToSharedArrayBuffer:

View File

@@ -756,6 +756,7 @@ class MOZ_RAII InlinableNativeIRGenerator {
AttachDecision tryAttachTypedArrayElementSize(); AttachDecision tryAttachTypedArrayElementSize();
AttachDecision tryAttachTypedArrayLength(bool isPossiblyWrapped, AttachDecision tryAttachTypedArrayLength(bool isPossiblyWrapped,
bool allowOutOfBounds); bool allowOutOfBounds);
AttachDecision tryAttachArrayBufferByteLength(bool isPossiblyWrapped);
AttachDecision tryAttachIsConstructing(); AttachDecision tryAttachIsConstructing();
AttachDecision tryAttachGetNextMapSetEntryForIterator(bool isMap); AttachDecision tryAttachGetNextMapSetEntryForIterator(bool isMap);
AttachDecision tryAttachNewArrayIterator(); AttachDecision tryAttachNewArrayIterator();

View File

@@ -238,6 +238,8 @@ bool js::jit::CanInlineNativeCrossRealm(InlinableNative native) {
case InlinableNative::IntrinsicGuardToSetObject: case InlinableNative::IntrinsicGuardToSetObject:
case InlinableNative::IntrinsicGetNextSetEntryForIterator: case InlinableNative::IntrinsicGetNextSetEntryForIterator:
case InlinableNative::IntrinsicGuardToArrayBuffer: case InlinableNative::IntrinsicGuardToArrayBuffer:
case InlinableNative::IntrinsicArrayBufferByteLength:
case InlinableNative::IntrinsicPossiblyWrappedArrayBufferByteLength:
case InlinableNative::IntrinsicGuardToSharedArrayBuffer: case InlinableNative::IntrinsicGuardToSharedArrayBuffer:
case InlinableNative::IntrinsicIsTypedArrayConstructor: case InlinableNative::IntrinsicIsTypedArrayConstructor:
case InlinableNative::IntrinsicIsTypedArray: case InlinableNative::IntrinsicIsTypedArray:

View File

@@ -243,6 +243,8 @@
_(IntrinsicArrayIteratorPrototypeOptimizable) \ _(IntrinsicArrayIteratorPrototypeOptimizable) \
\ \
_(IntrinsicGuardToArrayBuffer) \ _(IntrinsicGuardToArrayBuffer) \
_(IntrinsicArrayBufferByteLength) \
_(IntrinsicPossiblyWrappedArrayBufferByteLength) \
\ \
_(IntrinsicGuardToSharedArrayBuffer) \ _(IntrinsicGuardToSharedArrayBuffer) \
\ \

View File

@@ -2205,39 +2205,6 @@ bool js::ToIndexSlow(JSContext* cx, JS::HandleValue v,
return true; return true;
} }
/**
* Convert |value| to an integer and clamp it to a valid integer index within
* the range `[0..length]`.
*/
template <typename ArrayLength>
bool js::ToIntegerIndexSlow(JSContext* cx, Handle<Value> value,
ArrayLength length, ArrayLength* result) {
MOZ_ASSERT(!value.isInt32());
double relative;
if (!ToInteger(cx, value, &relative)) {
return false;
}
if (relative >= 0) {
*result = ArrayLength(std::min(relative, double(length)));
} else {
*result = ArrayLength(std::max(relative + double(length), 0.0));
}
return true;
}
static_assert(std::is_same_v<size_t, uint32_t> ||
std::is_same_v<size_t, uint64_t>,
"If this assertion fails, add the corresponding unsigned int "
"type for size_t to the explicit instantiations below");
template bool js::ToIntegerIndexSlow<uint32_t>(JSContext*, Handle<Value>,
uint32_t, uint32_t*);
template bool js::ToIntegerIndexSlow<uint64_t>(JSContext*, Handle<Value>,
uint64_t, uint64_t*);
template <typename CharT> template <typename CharT>
double js_strtod(const CharT* begin, const CharT* end, const CharT** dEnd) { double js_strtod(const CharT* begin, const CharT* end, const CharT** dEnd) {
const CharT* s = SkipSpace(begin, end); const CharT* s = SkipSpace(begin, end);

View File

@@ -404,40 +404,6 @@ static MOZ_ALWAYS_INLINE bool IsDefinitelyIndex(const Value& v,
return ToIndex(cx, v, JSMSG_BAD_INDEX, index); return ToIndex(cx, v, JSMSG_BAD_INDEX, index);
} }
/**
* Convert |value| to an integer and clamp it to a valid integer index within
* the range `[0..length]`.
*/
template <typename ArrayLength>
[[nodiscard]] extern bool ToIntegerIndexSlow(JSContext* cx, Handle<Value> value,
ArrayLength length,
ArrayLength* result);
template <typename ArrayLength>
[[nodiscard]] static inline bool ToIntegerIndex(JSContext* cx,
Handle<Value> value,
ArrayLength length,
ArrayLength* result) {
static_assert(std::is_unsigned_v<ArrayLength>);
// Optimize for the common case when |value| is an int32 to avoid unnecessary
// floating point computations.
if (value.isInt32()) {
int32_t relative = value.toInt32();
if (relative >= 0) {
*result = std::min(ArrayLength(relative), length);
} else if (mozilla::Abs(relative) <= length) {
*result = length - mozilla::Abs(relative);
} else {
*result = 0;
}
return true;
}
return ToIntegerIndexSlow(cx, value, length, result);
}
} /* namespace js */ } /* namespace js */
#endif /* jsnum_h */ #endif /* jsnum_h */

View File

@@ -43,10 +43,8 @@
#include "js/Wrapper.h" #include "js/Wrapper.h"
#include "util/WindowsWrapper.h" #include "util/WindowsWrapper.h"
#include "vm/GlobalObject.h" #include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/JSContext.h" #include "vm/JSContext.h"
#include "vm/JSObject.h" #include "vm/JSObject.h"
#include "vm/SelfHosting.h"
#include "vm/SharedArrayObject.h" #include "vm/SharedArrayObject.h"
#include "vm/Warnings.h" // js::WarnNumberASCII #include "vm/Warnings.h" // js::WarnNumberASCII
#include "wasm/WasmConstants.h" #include "wasm/WasmConstants.h"
@@ -331,7 +329,7 @@ static const JSPropertySpec arraybuffer_properties[] = {
}; };
static const JSFunctionSpec arraybuffer_proto_functions[] = { static const JSFunctionSpec arraybuffer_proto_functions[] = {
JS_FN("slice", ArrayBufferObject::slice, 2, 0), JS_SELF_HOSTED_FN("slice", "ArrayBufferSlice", 2, 0),
JS_FN("resize", ArrayBufferObject::resize, 1, 0), JS_FN("resize", ArrayBufferObject::resize, 1, 0),
JS_FN("transfer", ArrayBufferObject::transfer, 0, 0), JS_FN("transfer", ArrayBufferObject::transfer, 0, 0),
JS_FN("transferToFixedLength", ArrayBufferObject::transferToFixedLength, 0, JS_FN("transferToFixedLength", ArrayBufferObject::transferToFixedLength, 0,
@@ -361,7 +359,6 @@ static const ClassSpec ArrayBufferObjectClassSpec = {
arraybuffer_properties, arraybuffer_properties,
arraybuffer_proto_functions, arraybuffer_proto_functions,
arraybuffer_proto_properties, arraybuffer_proto_properties,
GenericFinishInit<WhichHasFuseProperty::ProtoAndCtor>,
}; };
static const ClassExtension FixedLengthArrayBufferObjectClassExtension = { static const ClassExtension FixedLengthArrayBufferObjectClassExtension = {
@@ -729,190 +726,6 @@ bool ArrayBufferObject::resize(JSContext* cx, unsigned argc, Value* vp) {
return CallNonGenericMethod<IsResizableArrayBuffer, resizeImpl>(cx, args); return CallNonGenericMethod<IsResizableArrayBuffer, resizeImpl>(cx, args);
} }
static bool IsArrayBufferSpecies(JSContext* cx, JSFunction* species) {
return IsSelfHostedFunctionWithName(species,
cx->names().dollar_ArrayBufferSpecies_);
}
static bool HasBuiltinArrayBufferSpecies(ArrayBufferObject* obj,
JSContext* cx) {
// Ensure `ArrayBuffer.prototype.constructor` and `ArrayBuffer[@@species]`
// haven't been mutated.
if (!cx->realm()->realmFuses.optimizeArrayBufferSpeciesFuse.intact()) {
return false;
}
// Ensure |obj|'s prototype is the actual ArrayBuffer.prototype.
auto* proto = cx->global()->maybeGetPrototype(JSProto_ArrayBuffer);
if (!proto || obj->staticPrototype() != proto) {
return false;
}
// Fail if |obj| has an own `constructor` property.
if (obj->containsPure(NameToId(cx->names().constructor))) {
return false;
}
return true;
}
/**
* ArrayBuffer.prototype.slice ( start, end )
*
* https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice
*/
bool ArrayBufferObject::sliceImpl(JSContext* cx, const CallArgs& args) {
MOZ_ASSERT(IsArrayBuffer(args.thisv()));
Rooted<ArrayBufferObject*> obj(
cx, &args.thisv().toObject().as<ArrayBufferObject>());
// Step 4.
if (obj->isDetached()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_DETACHED);
return false;
}
// Step 5.
size_t len = obj->byteLength();
// Steps 6-9.
size_t first = 0;
if (args.hasDefined(0)) {
if (!ToIntegerIndex(cx, args[0], len, &first)) {
return false;
}
}
// Steps 10-13.
size_t final_ = len;
if (args.hasDefined(1)) {
if (!ToIntegerIndex(cx, args[1], len, &final_)) {
return false;
}
}
// Step 14.
size_t newLen = final_ >= first ? final_ - first : 0;
MOZ_ASSERT(newLen <= ArrayBufferObject::ByteLengthLimit);
// Steps 15-21.
Rooted<JSObject*> resultObj(cx);
ArrayBufferObject* unwrappedResult = nullptr;
if (HasBuiltinArrayBufferSpecies(obj, cx)) {
// Steps 15-16.
unwrappedResult = createZeroed(cx, newLen);
if (!unwrappedResult) {
return false;
}
resultObj.set(unwrappedResult);
// Steps 17-18. (Not applicable)
// Step 19.
MOZ_ASSERT(!unwrappedResult->isDetached());
// Step 20.
MOZ_ASSERT(unwrappedResult != obj);
// Step 21.
MOZ_ASSERT(unwrappedResult->byteLength() == newLen);
} else {
// Step 15.
Rooted<JSObject*> ctor(cx, SpeciesConstructor(cx, obj, JSProto_ArrayBuffer,
IsArrayBufferSpecies));
if (!ctor) {
return false;
}
// Step 16.
{
FixedConstructArgs<1> cargs(cx);
cargs[0].setNumber(newLen);
Rooted<Value> ctorVal(cx, ObjectValue(*ctor));
if (!Construct(cx, ctorVal, cargs, ctorVal, &resultObj)) {
return false;
}
}
// Steps 17-18.
unwrappedResult = resultObj->maybeUnwrapIf<ArrayBufferObject>();
if (!unwrappedResult) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_NON_ARRAY_BUFFER_RETURNED);
return false;
}
// Step 19.
if (unwrappedResult->isDetached()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_DETACHED);
return false;
}
// Step 20.
if (unwrappedResult == obj) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_SAME_ARRAY_BUFFER_RETURNED);
return false;
}
// Step 21.
size_t resultByteLength = unwrappedResult->byteLength();
if (resultByteLength < newLen) {
ToCStringBuf resultLenCbuf;
const char* resultLenStr =
NumberToCString(&resultLenCbuf, double(resultByteLength));
ToCStringBuf newLenCbuf;
const char* newLenStr = NumberToCString(&newLenCbuf, double(newLen));
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_SHORT_ARRAY_BUFFER_RETURNED, newLenStr,
resultLenStr);
return false;
}
}
// Steps 22-23.
if (obj->isDetached()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_DETACHED);
return false;
}
// Step 26.
//
// Reacquire the length in case the buffer has been resized.
size_t currentLen = obj->byteLength();
// Steps 24-25 and 27.
if (first < currentLen) {
// Step 27.a.
size_t count = std::min(newLen, currentLen - first);
// Steps 24-25 and 27.b.
ArrayBufferObject::copyData(unwrappedResult, 0, obj, first, count);
}
// Step 28.
args.rval().setObject(*resultObj);
return true;
}
/**
* ArrayBuffer.prototype.slice ( start, end )
*
* https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice
*/
bool ArrayBufferObject::slice(JSContext* cx, unsigned argc, Value* vp) {
// Steps 1-3.
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<IsArrayBuffer, sliceImpl>(cx, args);
}
/* /*
* ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1 * ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1
*/ */

View File

@@ -183,7 +183,6 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared {
static bool maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args); static bool maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args);
static bool resizableGetterImpl(JSContext* cx, const CallArgs& args); static bool resizableGetterImpl(JSContext* cx, const CallArgs& args);
static bool detachedGetterImpl(JSContext* cx, const CallArgs& args); static bool detachedGetterImpl(JSContext* cx, const CallArgs& args);
static bool sliceImpl(JSContext* cx, const CallArgs& args);
static bool resizeImpl(JSContext* cx, const CallArgs& args); static bool resizeImpl(JSContext* cx, const CallArgs& args);
static bool transferImpl(JSContext* cx, const CallArgs& args); static bool transferImpl(JSContext* cx, const CallArgs& args);
static bool transferToFixedLengthImpl(JSContext* cx, const CallArgs& args); static bool transferToFixedLengthImpl(JSContext* cx, const CallArgs& args);
@@ -423,8 +422,6 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared {
static bool fun_isView(JSContext* cx, unsigned argc, Value* vp); static bool fun_isView(JSContext* cx, unsigned argc, Value* vp);
static bool slice(JSContext* cx, unsigned argc, Value* vp);
static bool resize(JSContext* cx, unsigned argc, Value* vp); static bool resize(JSContext* cx, unsigned argc, Value* vp);
static bool transfer(JSContext* cx, unsigned argc, Value* vp); static bool transfer(JSContext* cx, unsigned argc, Value* vp);

View File

@@ -17,6 +17,10 @@ static JSProtoKey ToProtoKey(BuiltinObjectKind kind) {
switch (kind) { switch (kind) {
case BuiltinObjectKind::Array: case BuiltinObjectKind::Array:
return JSProto_Array; return JSProto_Array;
case BuiltinObjectKind::ArrayBuffer:
return JSProto_ArrayBuffer;
case BuiltinObjectKind::Int32Array:
return JSProto_Int32Array;
case BuiltinObjectKind::ListFormat: case BuiltinObjectKind::ListFormat:
return JSProto_ListFormat; return JSProto_ListFormat;
case BuiltinObjectKind::Map: case BuiltinObjectKind::Map:
@@ -27,6 +31,8 @@ static JSProtoKey ToProtoKey(BuiltinObjectKind kind) {
return JSProto_RegExp; return JSProto_RegExp;
case BuiltinObjectKind::Set: case BuiltinObjectKind::Set:
return JSProto_Set; return JSProto_Set;
case BuiltinObjectKind::SharedArrayBuffer:
return JSProto_SharedArrayBuffer;
case BuiltinObjectKind::Symbol: case BuiltinObjectKind::Symbol:
return JSProto_Symbol; return JSProto_Symbol;
@@ -34,6 +40,12 @@ static JSProtoKey ToProtoKey(BuiltinObjectKind kind) {
return JSProto_Function; return JSProto_Function;
case BuiltinObjectKind::IteratorPrototype: case BuiltinObjectKind::IteratorPrototype:
return JSProto_Iterator; return JSProto_Iterator;
case BuiltinObjectKind::ObjectPrototype:
return JSProto_Object;
case BuiltinObjectKind::RegExpPrototype:
return JSProto_RegExp;
case BuiltinObjectKind::StringPrototype:
return JSProto_String;
case BuiltinObjectKind::DateTimeFormatPrototype: case BuiltinObjectKind::DateTimeFormatPrototype:
return JSProto_DateTimeFormat; return JSProto_DateTimeFormat;
@@ -49,16 +61,22 @@ static JSProtoKey ToProtoKey(BuiltinObjectKind kind) {
static bool IsPrototype(BuiltinObjectKind kind) { static bool IsPrototype(BuiltinObjectKind kind) {
switch (kind) { switch (kind) {
case BuiltinObjectKind::Array: case BuiltinObjectKind::Array:
case BuiltinObjectKind::ArrayBuffer:
case BuiltinObjectKind::Int32Array:
case BuiltinObjectKind::ListFormat: case BuiltinObjectKind::ListFormat:
case BuiltinObjectKind::Map: case BuiltinObjectKind::Map:
case BuiltinObjectKind::Promise: case BuiltinObjectKind::Promise:
case BuiltinObjectKind::RegExp: case BuiltinObjectKind::RegExp:
case BuiltinObjectKind::Set: case BuiltinObjectKind::Set:
case BuiltinObjectKind::SharedArrayBuffer:
case BuiltinObjectKind::Symbol: case BuiltinObjectKind::Symbol:
return false; return false;
case BuiltinObjectKind::FunctionPrototype: case BuiltinObjectKind::FunctionPrototype:
case BuiltinObjectKind::IteratorPrototype: case BuiltinObjectKind::IteratorPrototype:
case BuiltinObjectKind::ObjectPrototype:
case BuiltinObjectKind::RegExpPrototype:
case BuiltinObjectKind::StringPrototype:
return true; return true;
case BuiltinObjectKind::DateTimeFormatPrototype: case BuiltinObjectKind::DateTimeFormatPrototype:
@@ -76,6 +94,12 @@ BuiltinObjectKind js::BuiltinConstructorForName(
if (name == frontend::TaggedParserAtomIndex::WellKnown::Array()) { if (name == frontend::TaggedParserAtomIndex::WellKnown::Array()) {
return BuiltinObjectKind::Array; return BuiltinObjectKind::Array;
} }
if (name == frontend::TaggedParserAtomIndex::WellKnown::ArrayBuffer()) {
return BuiltinObjectKind::ArrayBuffer;
}
if (name == frontend::TaggedParserAtomIndex::WellKnown::Int32Array()) {
return BuiltinObjectKind::Int32Array;
}
if (name == frontend::TaggedParserAtomIndex::WellKnown::ListFormat()) { if (name == frontend::TaggedParserAtomIndex::WellKnown::ListFormat()) {
return BuiltinObjectKind::ListFormat; return BuiltinObjectKind::ListFormat;
} }
@@ -91,6 +115,9 @@ BuiltinObjectKind js::BuiltinConstructorForName(
if (name == frontend::TaggedParserAtomIndex::WellKnown::Set()) { if (name == frontend::TaggedParserAtomIndex::WellKnown::Set()) {
return BuiltinObjectKind::Set; return BuiltinObjectKind::Set;
} }
if (name == frontend::TaggedParserAtomIndex::WellKnown::SharedArrayBuffer()) {
return BuiltinObjectKind::SharedArrayBuffer;
}
if (name == frontend::TaggedParserAtomIndex::WellKnown::Symbol()) { if (name == frontend::TaggedParserAtomIndex::WellKnown::Symbol()) {
return BuiltinObjectKind::Symbol; return BuiltinObjectKind::Symbol;
} }
@@ -105,6 +132,15 @@ BuiltinObjectKind js::BuiltinPrototypeForName(
if (name == frontend::TaggedParserAtomIndex::WellKnown::Iterator()) { if (name == frontend::TaggedParserAtomIndex::WellKnown::Iterator()) {
return BuiltinObjectKind::IteratorPrototype; return BuiltinObjectKind::IteratorPrototype;
} }
if (name == frontend::TaggedParserAtomIndex::WellKnown::Object()) {
return BuiltinObjectKind::ObjectPrototype;
}
if (name == frontend::TaggedParserAtomIndex::WellKnown::RegExp()) {
return BuiltinObjectKind::RegExpPrototype;
}
if (name == frontend::TaggedParserAtomIndex::WellKnown::String()) {
return BuiltinObjectKind::StringPrototype;
}
if (name == frontend::TaggedParserAtomIndex::WellKnown::DateTimeFormat()) { if (name == frontend::TaggedParserAtomIndex::WellKnown::DateTimeFormat()) {
return BuiltinObjectKind::DateTimeFormatPrototype; return BuiltinObjectKind::DateTimeFormatPrototype;
} }
@@ -135,6 +171,10 @@ const char* js::BuiltinObjectName(BuiltinObjectKind kind) {
switch (kind) { switch (kind) {
case BuiltinObjectKind::Array: case BuiltinObjectKind::Array:
return "Array"; return "Array";
case BuiltinObjectKind::ArrayBuffer:
return "ArrayBuffer";
case BuiltinObjectKind::Int32Array:
return "Int32Array";
case BuiltinObjectKind::ListFormat: case BuiltinObjectKind::ListFormat:
return "ListFormat"; return "ListFormat";
case BuiltinObjectKind::Map: case BuiltinObjectKind::Map:
@@ -143,6 +183,8 @@ const char* js::BuiltinObjectName(BuiltinObjectKind kind) {
return "Promise"; return "Promise";
case BuiltinObjectKind::RegExp: case BuiltinObjectKind::RegExp:
return "RegExp"; return "RegExp";
case BuiltinObjectKind::SharedArrayBuffer:
return "SharedArrayBuffer";
case BuiltinObjectKind::Set: case BuiltinObjectKind::Set:
return "Set"; return "Set";
case BuiltinObjectKind::Symbol: case BuiltinObjectKind::Symbol:
@@ -152,6 +194,12 @@ const char* js::BuiltinObjectName(BuiltinObjectKind kind) {
return "Function.prototype"; return "Function.prototype";
case BuiltinObjectKind::IteratorPrototype: case BuiltinObjectKind::IteratorPrototype:
return "Iterator.prototype"; return "Iterator.prototype";
case BuiltinObjectKind::ObjectPrototype:
return "Object.prototype";
case BuiltinObjectKind::RegExpPrototype:
return "RegExp.prototype";
case BuiltinObjectKind::StringPrototype:
return "String.prototype";
case BuiltinObjectKind::DateTimeFormatPrototype: case BuiltinObjectKind::DateTimeFormatPrototype:
return "DateTimeFormat.prototype"; return "DateTimeFormat.prototype";

View File

@@ -29,16 +29,22 @@ class GlobalObject;
enum class BuiltinObjectKind : uint8_t { enum class BuiltinObjectKind : uint8_t {
// Built-in constructors. // Built-in constructors.
Array, Array,
ArrayBuffer,
Int32Array,
ListFormat, ListFormat,
Map, Map,
Promise, Promise,
RegExp, RegExp,
Set, Set,
SharedArrayBuffer,
Symbol, Symbol,
// Built-in prototypes. // Built-in prototypes.
FunctionPrototype, FunctionPrototype,
IteratorPrototype, IteratorPrototype,
ObjectPrototype,
RegExpPrototype,
StringPrototype,
// Built-in Intl prototypes. // Built-in Intl prototypes.
DateTimeFormatPrototype, DateTimeFormatPrototype,

View File

@@ -156,7 +156,6 @@
MACRO_(dollar_ArrayValues_, "$ArrayValues") \ MACRO_(dollar_ArrayValues_, "$ArrayValues") \
MACRO_(dollar_RegExpFlagsGetter_, "$RegExpFlagsGetter") \ MACRO_(dollar_RegExpFlagsGetter_, "$RegExpFlagsGetter") \
MACRO_(dollar_RegExpToString_, "$RegExpToString") \ MACRO_(dollar_RegExpToString_, "$RegExpToString") \
MACRO_(dollar_SharedArrayBufferSpecies_, "$SharedArrayBufferSpecies") \
MACRO_(domNode, "domNode") \ MACRO_(domNode, "domNode") \
MACRO_(done, "done") \ MACRO_(done, "done") \
MACRO_(dotAll, "dotAll") \ MACRO_(dotAll, "dotAll") \

View File

@@ -336,43 +336,27 @@ void js::OptimizeArraySpeciesFuse::popFuse(JSContext* cx,
JSUseCounter::OPTIMIZE_ARRAY_SPECIES_FUSE); JSUseCounter::OPTIMIZE_ARRAY_SPECIES_FUSE);
} }
static bool SpeciesFuseCheckInvariant(JSContext* cx, JSProtoKey protoKey, bool js::OptimizeArraySpeciesFuse::checkInvariant(JSContext* cx) {
PropertyName* selfHostedSpeciesAccessor) { // Prototype must be Array.prototype.
// Prototype must be initialized. auto* proto = cx->global()->maybeGetArrayPrototype();
auto* proto = cx->global()->maybeGetPrototype<NativeObject>(protoKey);
if (!proto) { if (!proto) {
// No proto, invariant still holds // No proto, invariant still holds
return true; return true;
} }
auto* ctor = cx->global()->maybeGetConstructor<NativeObject>(protoKey); auto* ctor = cx->global()->maybeGetConstructor<NativeObject>(JSProto_Array);
MOZ_ASSERT(ctor); MOZ_ASSERT(ctor);
// Ensure the prototype's `constructor` slot is the original constructor. // Ensure Array.prototype's `constructor` slot is the `Array` constructor.
if (!ObjectHasDataPropertyValue(proto, NameToId(cx->names().constructor), if (!ObjectHasDataPropertyValue(proto, NameToId(cx->names().constructor),
ObjectValue(*ctor))) { ObjectValue(*ctor))) {
return false; return false;
} }
// Ensure constructor's `@@species` slot is the original species getter. // Ensure Array's `@@species` slot is the $ArraySpecies getter.
PropertyKey speciesKey = PropertyKey::Symbol(cx->wellKnownSymbols().species); PropertyKey speciesKey = PropertyKey::Symbol(cx->wellKnownSymbols().species);
return ObjectHasGetterFunction(ctor, speciesKey, selfHostedSpeciesAccessor); return ObjectHasGetterFunction(ctor, speciesKey,
} cx->names().dollar_ArraySpecies_);
bool js::OptimizeArraySpeciesFuse::checkInvariant(JSContext* cx) {
return SpeciesFuseCheckInvariant(cx, JSProto_Array,
cx->names().dollar_ArraySpecies_);
}
bool js::OptimizeArrayBufferSpeciesFuse::checkInvariant(JSContext* cx) {
return SpeciesFuseCheckInvariant(cx, JSProto_ArrayBuffer,
cx->names().dollar_ArrayBufferSpecies_);
}
bool js::OptimizeSharedArrayBufferSpeciesFuse::checkInvariant(JSContext* cx) {
return SpeciesFuseCheckInvariant(
cx, JSProto_SharedArrayBuffer,
cx->names().dollar_SharedArrayBufferSpecies_);
} }
void js::OptimizePromiseLookupFuse::popFuse(JSContext* cx, void js::OptimizePromiseLookupFuse::popFuse(JSContext* cx,

View File

@@ -150,34 +150,6 @@ struct OptimizeArraySpeciesFuse final : public InvalidatingRealmFuse {
virtual void popFuse(JSContext* cx, RealmFuses& realmFuses) override; virtual void popFuse(JSContext* cx, RealmFuses& realmFuses) override;
}; };
// Fuse used to optimize @@species lookups for ArrayBuffers. If this fuse is
// intact, the following invariants must hold:
//
// - The builtin `ArrayBuffer.prototype` object has a `constructor` property
// that's the builtin `ArrayBuffer` constructor.
// - This `ArrayBuffer` constructor has a `Symbol.species` property that's the
// original accessor.
struct OptimizeArrayBufferSpeciesFuse final : public RealmFuse {
virtual const char* name() override {
return "OptimizeArrayBufferSpeciesFuse";
}
virtual bool checkInvariant(JSContext* cx) override;
};
// Fuse used to optimize @@species lookups for SharedArrayBuffers. If this fuse
// is intact, the following invariants must hold:
//
// - The builtin `SharedArrayBuffer.prototype` object has a `constructor`
// property that's the builtin `SharedArrayBuffer` constructor.
// - This `SharedArrayBuffer` constructor has a `Symbol.species` property that's
// the original accessor.
struct OptimizeSharedArrayBufferSpeciesFuse final : public RealmFuse {
virtual const char* name() override {
return "OptimizeSharedArrayBufferSpeciesFuse";
}
virtual bool checkInvariant(JSContext* cx) override;
};
// Fuse used to optimize various property lookups for promises. If this fuse is // Fuse used to optimize various property lookups for promises. If this fuse is
// intact, the following invariants must hold: // intact, the following invariants must hold:
// //
@@ -309,9 +281,6 @@ struct OptimizeWeakSetPrototypeAddFuse final : public RealmFuse {
FUSE(IteratorPrototypeHasObjectProto, iteratorPrototypeHasObjectProto) \ FUSE(IteratorPrototypeHasObjectProto, iteratorPrototypeHasObjectProto) \
FUSE(ObjectPrototypeHasNoReturnProperty, objectPrototypeHasNoReturnProperty) \ FUSE(ObjectPrototypeHasNoReturnProperty, objectPrototypeHasNoReturnProperty) \
FUSE(OptimizeArraySpeciesFuse, optimizeArraySpeciesFuse) \ FUSE(OptimizeArraySpeciesFuse, optimizeArraySpeciesFuse) \
FUSE(OptimizeArrayBufferSpeciesFuse, optimizeArrayBufferSpeciesFuse) \
FUSE(OptimizeSharedArrayBufferSpeciesFuse, \
optimizeSharedArrayBufferSpeciesFuse) \
FUSE(OptimizePromiseLookupFuse, optimizePromiseLookupFuse) \ FUSE(OptimizePromiseLookupFuse, optimizePromiseLookupFuse) \
FUSE(OptimizeRegExpPrototypeFuse, optimizeRegExpPrototypeFuse) \ FUSE(OptimizeRegExpPrototypeFuse, optimizeRegExpPrototypeFuse) \
FUSE(OptimizeStringPrototypeSymbolsFuse, optimizeStringPrototypeSymbolsFuse) \ FUSE(OptimizeStringPrototypeSymbolsFuse, optimizeStringPrototypeSymbolsFuse) \

View File

@@ -876,6 +876,37 @@ static bool intrinsic_GeneratorSetClosed(JSContext* cx, unsigned argc,
return true; return true;
} }
template <typename T>
static bool intrinsic_ArrayBufferByteLength(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
MOZ_ASSERT(args[0].toObject().is<T>());
size_t byteLength = args[0].toObject().as<T>().byteLength();
args.rval().setNumber(byteLength);
return true;
}
template <typename T>
static bool intrinsic_PossiblyWrappedArrayBufferByteLength(JSContext* cx,
unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
T* obj = args[0].toObject().maybeUnwrapAs<T>();
if (!obj) {
ReportAccessDenied(cx);
return false;
}
size_t byteLength = obj->byteLength();
args.rval().setNumber(byteLength);
return true;
}
static void AssertNonNegativeInteger(const Value& v) { static void AssertNonNegativeInteger(const Value& v) {
MOZ_ASSERT(v.isNumber()); MOZ_ASSERT(v.isNumber());
MOZ_ASSERT(v.toNumber() >= 0); MOZ_ASSERT(v.toNumber() >= 0);
@@ -883,6 +914,60 @@ static void AssertNonNegativeInteger(const Value& v) {
MOZ_ASSERT(JS::ToInteger(v.toNumber()) == v.toNumber()); MOZ_ASSERT(JS::ToInteger(v.toNumber()) == v.toNumber());
} }
template <typename T>
static bool intrinsic_ArrayBufferCopyData(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 6);
AssertNonNegativeInteger(args[1]);
AssertNonNegativeInteger(args[3]);
AssertNonNegativeInteger(args[4]);
bool isWrapped = args[5].toBoolean();
Rooted<T*> toBuffer(cx);
if (!isWrapped) {
toBuffer = &args[0].toObject().as<T>();
} else {
JSObject* wrapped = &args[0].toObject();
MOZ_ASSERT(wrapped->is<WrapperObject>());
toBuffer = wrapped->maybeUnwrapAs<T>();
if (!toBuffer) {
ReportAccessDenied(cx);
return false;
}
}
size_t toIndex = size_t(args[1].toNumber());
Rooted<T*> fromBuffer(cx, &args[2].toObject().as<T>());
size_t fromIndex = size_t(args[3].toNumber());
size_t count = size_t(args[4].toNumber());
T::copyData(toBuffer, toIndex, fromBuffer, fromIndex, count);
args.rval().setUndefined();
return true;
}
// Arguments must both be SharedArrayBuffer or wrapped SharedArrayBuffer.
static bool intrinsic_SharedArrayBuffersMemorySame(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
auto* lhs = args[0].toObject().maybeUnwrapAs<SharedArrayBufferObject>();
if (!lhs) {
ReportAccessDenied(cx);
return false;
}
auto* rhs = args[1].toObject().maybeUnwrapAs<SharedArrayBufferObject>();
if (!rhs) {
ReportAccessDenied(cx);
return false;
}
args.rval().setBoolean(lhs->rawBufferObject() == rhs->rawBufferObject());
return true;
}
static bool intrinsic_IsTypedArrayConstructor(JSContext* cx, unsigned argc, static bool intrinsic_IsTypedArrayConstructor(JSContext* cx, unsigned argc,
Value* vp) { Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
@@ -1903,11 +1988,18 @@ static bool intrinsic_ToTemporalDuration(JSContext* cx, unsigned argc,
static const JSFunctionSpec intrinsic_functions[] = { static const JSFunctionSpec intrinsic_functions[] = {
// Intrinsic helper functions // Intrinsic helper functions
JS_INLINABLE_FN("ArrayBufferByteLength",
intrinsic_ArrayBufferByteLength<ArrayBufferObject>, 1, 0,
IntrinsicArrayBufferByteLength),
JS_FN("ArrayBufferCopyData",
intrinsic_ArrayBufferCopyData<ArrayBufferObject>, 6, 0),
JS_INLINABLE_FN("ArrayIteratorPrototypeOptimizable", JS_INLINABLE_FN("ArrayIteratorPrototypeOptimizable",
intrinsic_ArrayIteratorPrototypeOptimizable, 0, 0, intrinsic_ArrayIteratorPrototypeOptimizable, 0, 0,
IntrinsicArrayIteratorPrototypeOptimizable), IntrinsicArrayIteratorPrototypeOptimizable),
JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1, 0), JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1, 0),
JS_FN("BigIntToNumber", intrinsic_BigIntToNumber, 1, 0), JS_FN("BigIntToNumber", intrinsic_BigIntToNumber, 1, 0),
JS_FN("CallArrayBufferMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<ArrayBufferObject>>, 2, 0),
JS_FN("CallArrayIteratorMethodIfWrapped", JS_FN("CallArrayIteratorMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<ArrayIteratorObject>>, 2, 0), CallNonGenericSelfhostedMethod<Is<ArrayIteratorObject>>, 2, 0),
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
@@ -1940,6 +2032,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
CallNonGenericSelfhostedMethod<Is<SetIteratorObject>>, 2, 0), CallNonGenericSelfhostedMethod<Is<SetIteratorObject>>, 2, 0),
JS_FN("CallSetMethodIfWrapped", JS_FN("CallSetMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<SetObject>>, 2, 0), CallNonGenericSelfhostedMethod<Is<SetObject>>, 2, 0),
JS_FN("CallSharedArrayBufferMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<SharedArrayBufferObject>>, 2, 0),
JS_FN("CallStringIteratorMethodIfWrapped", JS_FN("CallStringIteratorMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<StringIteratorObject>>, 2, 0), CallNonGenericSelfhostedMethod<Is<StringIteratorObject>>, 2, 0),
JS_FN("CallTypedArrayMethodIfWrapped", JS_FN("CallTypedArrayMethodIfWrapped",
@@ -2083,6 +2177,10 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("IsTypedArrayConstructor", JS_INLINABLE_FN("IsTypedArrayConstructor",
intrinsic_IsTypedArrayConstructor, 1, 0, intrinsic_IsTypedArrayConstructor, 1, 0,
IntrinsicIsTypedArrayConstructor), IntrinsicIsTypedArrayConstructor),
JS_FN("IsWrappedArrayBuffer",
intrinsic_IsWrappedInstanceOfBuiltin<ArrayBufferObject>, 1, 0),
JS_FN("IsWrappedSharedArrayBuffer",
intrinsic_IsWrappedInstanceOfBuiltin<SharedArrayBufferObject>, 1, 0),
JS_INLINABLE_FN("NewArrayIterator", intrinsic_NewArrayIterator, 0, 0, JS_INLINABLE_FN("NewArrayIterator", intrinsic_NewArrayIterator, 0, 0,
IntrinsicNewArrayIterator), IntrinsicNewArrayIterator),
JS_FN("NewAsyncIteratorHelper", intrinsic_NewAsyncIteratorHelper, 0, 0), JS_FN("NewAsyncIteratorHelper", intrinsic_NewAsyncIteratorHelper, 0, 0),
@@ -2098,6 +2196,14 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("NewWrapForValidIterator", intrinsic_NewWrapForValidIterator, 0, 0), JS_FN("NewWrapForValidIterator", intrinsic_NewWrapForValidIterator, 0, 0),
JS_FN("NoPrivateGetter", intrinsic_NoPrivateGetter, 1, 0), JS_FN("NoPrivateGetter", intrinsic_NoPrivateGetter, 1, 0),
JS_FN("NumberToBigInt", intrinsic_NumberToBigInt, 1, 0), JS_FN("NumberToBigInt", intrinsic_NumberToBigInt, 1, 0),
JS_INLINABLE_FN(
"PossiblyWrappedArrayBufferByteLength",
intrinsic_PossiblyWrappedArrayBufferByteLength<ArrayBufferObject>, 1, 0,
IntrinsicPossiblyWrappedArrayBufferByteLength),
JS_FN(
"PossiblyWrappedSharedArrayBufferByteLength",
intrinsic_PossiblyWrappedArrayBufferByteLength<SharedArrayBufferObject>,
1, 0),
JS_FN("PossiblyWrappedTypedArrayHasDetachedBuffer", JS_FN("PossiblyWrappedTypedArrayHasDetachedBuffer",
intrinsic_PossiblyWrappedTypedArrayHasDetachedBuffer, 1, 0), intrinsic_PossiblyWrappedTypedArrayHasDetachedBuffer, 1, 0),
JS_INLINABLE_FN("PossiblyWrappedTypedArrayLength", JS_INLINABLE_FN("PossiblyWrappedTypedArrayLength",
@@ -2126,6 +2232,12 @@ static const JSFunctionSpec intrinsic_functions[] = {
intrinsic_RegExpSymbolProtocolOnPrimitiveCounter, 0, 0), intrinsic_RegExpSymbolProtocolOnPrimitiveCounter, 0, 0),
JS_INLINABLE_FN("SameValue", js::obj_is, 2, 0, ObjectIs), JS_INLINABLE_FN("SameValue", js::obj_is, 2, 0, ObjectIs),
JS_FN("SetCopy", SetObject::copy, 1, 0), JS_FN("SetCopy", SetObject::copy, 1, 0),
JS_FN("SharedArrayBufferByteLength",
intrinsic_ArrayBufferByteLength<SharedArrayBufferObject>, 1, 0),
JS_FN("SharedArrayBufferCopyData",
intrinsic_ArrayBufferCopyData<SharedArrayBufferObject>, 6, 0),
JS_FN("SharedArrayBuffersMemorySame",
intrinsic_SharedArrayBuffersMemorySame, 2, 0),
JS_FN("StringReplaceAllString", intrinsic_StringReplaceAllString, 3, 0), JS_FN("StringReplaceAllString", intrinsic_StringReplaceAllString, 3, 0),
JS_INLINABLE_FN("StringReplaceString", intrinsic_StringReplaceString, 3, 0, JS_INLINABLE_FN("StringReplaceString", intrinsic_StringReplaceString, 3, 0,
IntrinsicStringReplaceString), IntrinsicStringReplaceString),

View File

@@ -10,8 +10,6 @@
#include "mozilla/DebugOnly.h" #include "mozilla/DebugOnly.h"
#include "mozilla/TaggedAnonymousMemory.h" #include "mozilla/TaggedAnonymousMemory.h"
#include "jsnum.h"
#include "gc/GCContext.h" #include "gc/GCContext.h"
#include "gc/Memory.h" #include "gc/Memory.h"
#include "jit/AtomicOperations.h" #include "jit/AtomicOperations.h"
@@ -21,8 +19,6 @@
#include "js/SharedArrayBuffer.h" #include "js/SharedArrayBuffer.h"
#include "util/Memory.h" #include "util/Memory.h"
#include "util/WindowsWrapper.h" #include "util/WindowsWrapper.h"
#include "vm/Interpreter.h"
#include "vm/SelfHosting.h"
#include "vm/SharedMem.h" #include "vm/SharedMem.h"
#include "wasm/WasmConstants.h" #include "wasm/WasmConstants.h"
#include "wasm/WasmMemory.h" #include "wasm/WasmMemory.h"
@@ -434,156 +430,6 @@ bool SharedArrayBufferObject::grow(JSContext* cx, unsigned argc, Value* vp) {
return CallNonGenericMethod<IsGrowableSharedArrayBuffer, growImpl>(cx, args); return CallNonGenericMethod<IsGrowableSharedArrayBuffer, growImpl>(cx, args);
} }
static bool IsSharedArrayBufferSpecies(JSContext* cx, JSFunction* species) {
return IsSelfHostedFunctionWithName(
species, cx->names().dollar_SharedArrayBufferSpecies_);
}
static bool HasBuiltinSharedArrayBufferSpecies(SharedArrayBufferObject* obj,
JSContext* cx) {
// Ensure `SharedArrayBuffer.prototype.constructor` and
// `SharedArrayBuffer[@@species]` haven't been mutated.
if (!cx->realm()->realmFuses.optimizeSharedArrayBufferSpeciesFuse.intact()) {
return false;
}
// Ensure |obj|'s prototype is the actual SharedArrayBuffer.prototype.
auto* proto = cx->global()->maybeGetPrototype(JSProto_SharedArrayBuffer);
if (!proto || obj->staticPrototype() != proto) {
return false;
}
// Fail if |obj| has an own `constructor` property.
if (obj->containsPure(NameToId(cx->names().constructor))) {
return false;
}
return true;
}
/**
* SharedArrayBuffer.prototype.slice ( start, end )
*
* https://tc39.es/ecma262/#sec-sharedarraybuffer.prototype.slice
*/
bool SharedArrayBufferObject::sliceImpl(JSContext* cx, const CallArgs& args) {
MOZ_ASSERT(IsSharedArrayBuffer(args.thisv()));
Rooted<SharedArrayBufferObject*> obj(
cx, &args.thisv().toObject().as<SharedArrayBufferObject>());
// Step 4.
size_t len = obj->byteLength();
// Steps 5-8.
size_t first = 0;
if (args.hasDefined(0)) {
if (!ToIntegerIndex(cx, args[0], len, &first)) {
return false;
}
}
// Steps 9-12.
size_t final_ = len;
if (args.hasDefined(1)) {
if (!ToIntegerIndex(cx, args[1], len, &final_)) {
return false;
}
}
// Step 13.
size_t newLen = final_ >= first ? final_ - first : 0;
MOZ_ASSERT(newLen <= ArrayBufferObject::ByteLengthLimit);
// Steps 14-19.
Rooted<JSObject*> resultObj(cx);
SharedArrayBufferObject* unwrappedResult = nullptr;
if (HasBuiltinSharedArrayBufferSpecies(obj, cx)) {
// Steps 14-15.
unwrappedResult = New(cx, newLen);
if (!unwrappedResult) {
return false;
}
resultObj.set(unwrappedResult);
// Steps 16-17. (Not applicable)
// Step 18.
MOZ_ASSERT(obj->rawBufferObject() != unwrappedResult->rawBufferObject());
// Step 19.
MOZ_ASSERT(unwrappedResult->byteLength() == newLen);
} else {
// Step 14.
Rooted<JSObject*> ctor(
cx, SpeciesConstructor(cx, obj, JSProto_SharedArrayBuffer,
IsSharedArrayBufferSpecies));
if (!ctor) {
return false;
}
// Step 15.
{
FixedConstructArgs<1> cargs(cx);
cargs[0].setNumber(newLen);
Rooted<Value> ctorVal(cx, ObjectValue(*ctor));
if (!Construct(cx, ctorVal, cargs, ctorVal, &resultObj)) {
return false;
}
}
// Steps 16-17.
unwrappedResult = resultObj->maybeUnwrapIf<SharedArrayBufferObject>();
if (!unwrappedResult) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_NON_SHARED_ARRAY_BUFFER_RETURNED);
return false;
}
// Step 18.
if (obj->rawBufferObject() == unwrappedResult->rawBufferObject()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_SAME_SHARED_ARRAY_BUFFER_RETURNED);
return false;
}
// Step 19.
size_t resultByteLength = unwrappedResult->byteLength();
if (resultByteLength < newLen) {
ToCStringBuf resultLenCbuf;
const char* resultLenStr =
NumberToCString(&resultLenCbuf, double(resultByteLength));
ToCStringBuf newLenCbuf;
const char* newLenStr = NumberToCString(&newLenCbuf, double(newLen));
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED,
newLenStr, resultLenStr);
return false;
}
}
// Steps 20-22.
SharedArrayBufferObject::copyData(unwrappedResult, 0, obj, first, newLen);
// Step 23.
args.rval().setObject(*resultObj);
return true;
}
/**
* SharedArrayBuffer.prototype.slice ( start, end )
*
* https://tc39.es/ecma262/#sec-sharedarraybuffer.prototype.slice
*/
bool SharedArrayBufferObject::slice(JSContext* cx, unsigned argc, Value* vp) {
// Steps 1-3.
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<IsSharedArrayBuffer, sliceImpl>(cx, args);
}
// ES2024 draft rev 3a773fc9fae58be023228b13dbbd402ac18eeb6b // ES2024 draft rev 3a773fc9fae58be023228b13dbbd402ac18eeb6b
// 25.2.3.1 SharedArrayBuffer ( length [ , options ] ) // 25.2.3.1 SharedArrayBuffer ( length [ , options ] )
bool SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, bool SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc,
@@ -829,10 +675,10 @@ void SharedArrayBufferObject::addSizeOfExcludingThis(
} }
/* static */ /* static */
void SharedArrayBufferObject::copyData(ArrayBufferObjectMaybeShared* toBuffer, void SharedArrayBufferObject::copyData(
size_t toIndex, Handle<ArrayBufferObjectMaybeShared*> toBuffer, size_t toIndex,
ArrayBufferObjectMaybeShared* fromBuffer, Handle<ArrayBufferObjectMaybeShared*> fromBuffer, size_t fromIndex,
size_t fromIndex, size_t count) { size_t count) {
MOZ_ASSERT(toBuffer->byteLength() >= count); MOZ_ASSERT(toBuffer->byteLength() >= count);
MOZ_ASSERT(toBuffer->byteLength() >= toIndex + count); MOZ_ASSERT(toBuffer->byteLength() >= toIndex + count);
MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex); MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex);
@@ -936,7 +782,7 @@ static const JSPropertySpec sharedarray_properties[] = {
}; };
static const JSFunctionSpec sharedarray_proto_functions[] = { static const JSFunctionSpec sharedarray_proto_functions[] = {
JS_FN("slice", SharedArrayBufferObject::slice, 2, 0), JS_SELF_HOSTED_FN("slice", "SharedArrayBufferSlice", 2, 0),
JS_FN("grow", SharedArrayBufferObject::grow, 1, 0), JS_FN("grow", SharedArrayBufferObject::grow, 1, 0),
JS_FS_END, JS_FS_END,
}; };
@@ -963,7 +809,6 @@ static const ClassSpec SharedArrayBufferObjectClassSpec = {
sharedarray_properties, sharedarray_properties,
sharedarray_proto_functions, sharedarray_proto_functions,
sharedarray_proto_properties, sharedarray_proto_properties,
GenericFinishInit<WhichHasFuseProperty::ProtoAndCtor>,
}; };
const JSClass SharedArrayBufferObject::protoClass_ = { const JSClass SharedArrayBufferObject::protoClass_ = {

View File

@@ -255,7 +255,6 @@ class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared {
static bool maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args); static bool maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args);
static bool growableGetterImpl(JSContext* cx, const CallArgs& args); static bool growableGetterImpl(JSContext* cx, const CallArgs& args);
static bool growImpl(JSContext* cx, const CallArgs& args); static bool growImpl(JSContext* cx, const CallArgs& args);
static bool sliceImpl(JSContext* cx, const CallArgs& args);
public: public:
// RAWBUF_SLOT holds a pointer (as "private" data) to the // RAWBUF_SLOT holds a pointer (as "private" data) to the
@@ -285,8 +284,6 @@ class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared {
static bool grow(JSContext* cx, unsigned argc, Value* vp); static bool grow(JSContext* cx, unsigned argc, Value* vp);
static bool slice(JSContext* cx, unsigned argc, Value* vp);
static bool isOriginalByteLengthGetter(Native native) { static bool isOriginalByteLengthGetter(Native native) {
return native == byteLengthGetter; return native == byteLengthGetter;
} }
@@ -328,8 +325,9 @@ class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared {
JS::ClassInfo* info, JS::ClassInfo* info,
JS::RuntimeSizes* runtimeSizes); JS::RuntimeSizes* runtimeSizes);
static void copyData(ArrayBufferObjectMaybeShared* toBuffer, size_t toIndex, static void copyData(Handle<ArrayBufferObjectMaybeShared*> toBuffer,
ArrayBufferObjectMaybeShared* fromBuffer, size_t toIndex,
Handle<ArrayBufferObjectMaybeShared*> fromBuffer,
size_t fromIndex, size_t count); size_t fromIndex, size_t count);
SharedArrayRawBuffer* rawBufferObject() const; SharedArrayRawBuffer* rawBufferObject() const;

View File

@@ -1844,6 +1844,40 @@ static bool TypedArray_set(JSContext* cx, unsigned argc, Value* vp) {
return CallNonGenericMethod<IsTypedArrayObject, TypedArray_set>(cx, args); return CallNonGenericMethod<IsTypedArrayObject, TypedArray_set>(cx, args);
} }
/**
* Convert |value| to an integer and clamp it to a valid integer index within
* the range `[0..length]`.
*/
static bool ToIntegerIndex(JSContext* cx, Handle<Value> value, size_t length,
size_t* result) {
// Optimize for the common case when |value| is an int32 to avoid unnecessary
// floating point computations.
if (value.isInt32()) {
int32_t relative = value.toInt32();
if (relative >= 0) {
*result = std::min(size_t(relative), length);
} else if (mozilla::Abs(relative) <= length) {
*result = length - mozilla::Abs(relative);
} else {
*result = 0;
}
return true;
}
double relative;
if (!ToInteger(cx, value, &relative)) {
return false;
}
if (relative >= 0) {
*result = size_t(std::min(relative, double(length)));
} else {
*result = size_t(std::max(relative + double(length), 0.0));
}
return true;
}
// ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9 // ES2020 draft rev dc1e21c454bd316810be1c0e7af0131a2d7f38e9
// 22.2.3.5 %TypedArray%.prototype.copyWithin ( target, start [ , end ] ) // 22.2.3.5 %TypedArray%.prototype.copyWithin ( target, start [ , end ] )
static bool TypedArray_copyWithin(JSContext* cx, const CallArgs& args) { static bool TypedArray_copyWithin(JSContext* cx, const CallArgs& args) {

View File

@@ -452,52 +452,6 @@ static void MaybePopRegExpPrototypeFuses(JSContext* cx, NativeObject* obj,
} }
} }
static void MaybePopArrayBufferConstructorFuses(JSContext* cx,
NativeObject* obj, jsid id) {
if (obj != obj->global().maybeGetConstructor(JSProto_ArrayBuffer)) {
return;
}
if (id.isWellKnownSymbol(JS::SymbolCode::species)) {
obj->realm()->realmFuses.optimizeArrayBufferSpeciesFuse.popFuse(
cx, obj->realm()->realmFuses);
}
}
static void MaybePopArrayBufferPrototypeFuses(JSContext* cx, NativeObject* obj,
jsid id) {
if (obj != obj->global().maybeGetPrototype(JSProto_ArrayBuffer)) {
return;
}
if (id.isAtom(cx->names().constructor)) {
obj->realm()->realmFuses.optimizeArrayBufferSpeciesFuse.popFuse(
cx, obj->realm()->realmFuses);
}
}
static void MaybePopSharedArrayBufferConstructorFuses(JSContext* cx,
NativeObject* obj,
jsid id) {
if (obj != obj->global().maybeGetConstructor(JSProto_SharedArrayBuffer)) {
return;
}
if (id.isWellKnownSymbol(JS::SymbolCode::species)) {
obj->realm()->realmFuses.optimizeSharedArrayBufferSpeciesFuse.popFuse(
cx, obj->realm()->realmFuses);
}
}
static void MaybePopSharedArrayBufferPrototypeFuses(JSContext* cx,
NativeObject* obj,
jsid id) {
if (obj != obj->global().maybeGetPrototype(JSProto_SharedArrayBuffer)) {
return;
}
if (id.isAtom(cx->names().constructor)) {
obj->realm()->realmFuses.optimizeSharedArrayBufferSpeciesFuse.popFuse(
cx, obj->realm()->realmFuses);
}
}
static void MaybePopFuses(JSContext* cx, NativeObject* obj, jsid id) { static void MaybePopFuses(JSContext* cx, NativeObject* obj, jsid id) {
// Handle writes to Array constructor fuse properties. // Handle writes to Array constructor fuse properties.
MaybePopArrayConstructorFuses(cx, obj, id); MaybePopArrayConstructorFuses(cx, obj, id);
@@ -534,18 +488,6 @@ static void MaybePopFuses(JSContext* cx, NativeObject* obj, jsid id) {
// Handle writes to RegExp.prototype fuse properties. // Handle writes to RegExp.prototype fuse properties.
MaybePopRegExpPrototypeFuses(cx, obj, id); MaybePopRegExpPrototypeFuses(cx, obj, id);
// Handle writes to ArrayBuffer constructor fuse properties.
MaybePopArrayBufferConstructorFuses(cx, obj, id);
// Handle writes to ArrayBuffer.prototype fuse properties.
MaybePopArrayBufferPrototypeFuses(cx, obj, id);
// Handle writes to SharedArrayBuffer constructor fuse properties.
MaybePopSharedArrayBufferConstructorFuses(cx, obj, id);
// Handle writes to SharedArrayBuffer.prototype fuse properties.
MaybePopSharedArrayBufferPrototypeFuses(cx, obj, id);
} }
// static // static