diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js index aaf72f3797ec..c95ee76250ef 100644 --- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -1322,6 +1322,123 @@ function IterableToList(items, method) { 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. function $ArrayBufferSpecies() { // Step 1. @@ -1336,6 +1453,83 @@ function $SharedArrayBufferSpecies() { } 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 function TypedArrayCreateSameType(exemplar, length) { // Step 1. Assert: exemplar is an Object that has [[TypedArrayName]] and [[ContentType]] internal slots. diff --git a/js/src/frontend/ParserAtom.cpp b/js/src/frontend/ParserAtom.cpp index 4bf2d528db5c..482b3e92c63a 100644 --- a/js/src/frontend/ParserAtom.cpp +++ b/js/src/frontend/ParserAtom.cpp @@ -737,8 +737,7 @@ bool ParserAtomsTable::isExtendedUnclonedSelfHostedFunctionName( case WellKnownAtomId::dollar_ArraySpecies_: case WellKnownAtomId::dollar_ArrayValues_: case WellKnownAtomId::dollar_RegExpFlagsGetter_: - case WellKnownAtomId::dollar_RegExpToString_: - case WellKnownAtomId::dollar_SharedArrayBufferSpecies_: { + case WellKnownAtomId::dollar_RegExpToString_: { #ifdef DEBUG const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId()); MOZ_ASSERT(info.content[0] == diff --git a/js/src/jit-test/tests/fuses/species-fuse-arraybuffer-1.js b/js/src/jit-test/tests/fuses/species-fuse-arraybuffer-1.js deleted file mode 100644 index 8ac80a2d86c0..000000000000 --- a/js/src/jit-test/tests/fuses/species-fuse-arraybuffer-1.js +++ /dev/null @@ -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(); diff --git a/js/src/jit-test/tests/fuses/species-fuse-arraybuffer-2.js b/js/src/jit-test/tests/fuses/species-fuse-arraybuffer-2.js deleted file mode 100644 index 422b021bdcfc..000000000000 --- a/js/src/jit-test/tests/fuses/species-fuse-arraybuffer-2.js +++ /dev/null @@ -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(); diff --git a/js/src/jit-test/tests/fuses/species-fuse-sharedarraybuffer-1.js b/js/src/jit-test/tests/fuses/species-fuse-sharedarraybuffer-1.js deleted file mode 100644 index 7e4672eac590..000000000000 --- a/js/src/jit-test/tests/fuses/species-fuse-sharedarraybuffer-1.js +++ /dev/null @@ -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(); diff --git a/js/src/jit-test/tests/fuses/species-fuse-sharedarraybuffer-2.js b/js/src/jit-test/tests/fuses/species-fuse-sharedarraybuffer-2.js deleted file mode 100644 index 1436859605f3..000000000000 --- a/js/src/jit-test/tests/fuses/species-fuse-sharedarraybuffer-2.js +++ /dev/null @@ -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(); diff --git a/js/src/jit-test/tests/ion/testArrayBufferByteLength.js b/js/src/jit-test/tests/ion/testArrayBufferByteLength.js new file mode 100644 index 000000000000..bf5fc4983344 --- /dev/null +++ b/js/src/jit-test/tests/ion/testArrayBufferByteLength.js @@ -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(); diff --git a/js/src/jit-test/tests/ion/testPossiblyWrappedArrayBufferByteLength.js b/js/src/jit-test/tests/ion/testPossiblyWrappedArrayBufferByteLength.js new file mode 100644 index 000000000000..abbff59a6f8f --- /dev/null +++ b/js/src/jit-test/tests/ion/testPossiblyWrappedArrayBufferByteLength.js @@ -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(); diff --git a/js/src/jit-test/tests/large-arraybuffers/large-lengths-offsets.js b/js/src/jit-test/tests/large-arraybuffers/large-lengths-offsets.js index 93e1bf838a30..4e2bff1d70f2 100644 --- a/js/src/jit-test/tests/large-arraybuffers/large-lengths-offsets.js +++ b/js/src/jit-test/tests/large-arraybuffers/large-lengths-offsets.js @@ -11,6 +11,7 @@ const dvSmall = new DataView(bufferSmall); const dvLargeOffset = new DataView(bufferLarge, 5 * gb); const dvLargeLength = new DataView(bufferLarge); +const ArrayBufferByteLength = getSelfHostedValue("ArrayBufferByteLength"); const TypedArrayByteOffset = getSelfHostedValue("TypedArrayByteOffset"); const TypedArrayLength = getSelfHostedValue("TypedArrayLength"); @@ -18,6 +19,7 @@ function testBufferByteLengthInt32() { var arr = [bufferLarge, bufferSmall]; for (var i = 0; i < 2000; i++) { 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); } } diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index 064f812bea02..29da8f942ba1 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -11122,6 +11122,45 @@ AttachDecision InlinableNativeIRGenerator::tryAttachTypedArrayLength( 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()); + + auto* buffer = &args_[0].toObject().as(); + + // 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() { // Self-hosted code calls this with no arguments in function scripts. MOZ_ASSERT(args_.length() == 0); @@ -12467,6 +12506,10 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() { // ArrayBuffer intrinsics. case InlinableNative::IntrinsicGuardToArrayBuffer: return tryAttachGuardToArrayBuffer(); + case InlinableNative::IntrinsicArrayBufferByteLength: + return tryAttachArrayBufferByteLength(/* isPossiblyWrapped = */ false); + case InlinableNative::IntrinsicPossiblyWrappedArrayBufferByteLength: + return tryAttachArrayBufferByteLength(/* isPossiblyWrapped = */ true); // SharedArrayBuffer intrinsics. case InlinableNative::IntrinsicGuardToSharedArrayBuffer: diff --git a/js/src/jit/CacheIRGenerator.h b/js/src/jit/CacheIRGenerator.h index ff0aadd171b0..bdf70c5e1fde 100644 --- a/js/src/jit/CacheIRGenerator.h +++ b/js/src/jit/CacheIRGenerator.h @@ -756,6 +756,7 @@ class MOZ_RAII InlinableNativeIRGenerator { AttachDecision tryAttachTypedArrayElementSize(); AttachDecision tryAttachTypedArrayLength(bool isPossiblyWrapped, bool allowOutOfBounds); + AttachDecision tryAttachArrayBufferByteLength(bool isPossiblyWrapped); AttachDecision tryAttachIsConstructing(); AttachDecision tryAttachGetNextMapSetEntryForIterator(bool isMap); AttachDecision tryAttachNewArrayIterator(); diff --git a/js/src/jit/InlinableNatives.cpp b/js/src/jit/InlinableNatives.cpp index 8cb4e55e9b20..ea775a398829 100644 --- a/js/src/jit/InlinableNatives.cpp +++ b/js/src/jit/InlinableNatives.cpp @@ -238,6 +238,8 @@ bool js::jit::CanInlineNativeCrossRealm(InlinableNative native) { case InlinableNative::IntrinsicGuardToSetObject: case InlinableNative::IntrinsicGetNextSetEntryForIterator: case InlinableNative::IntrinsicGuardToArrayBuffer: + case InlinableNative::IntrinsicArrayBufferByteLength: + case InlinableNative::IntrinsicPossiblyWrappedArrayBufferByteLength: case InlinableNative::IntrinsicGuardToSharedArrayBuffer: case InlinableNative::IntrinsicIsTypedArrayConstructor: case InlinableNative::IntrinsicIsTypedArray: diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index 2ec98ad0ba87..96eec66ae954 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -243,6 +243,8 @@ _(IntrinsicArrayIteratorPrototypeOptimizable) \ \ _(IntrinsicGuardToArrayBuffer) \ + _(IntrinsicArrayBufferByteLength) \ + _(IntrinsicPossiblyWrappedArrayBufferByteLength) \ \ _(IntrinsicGuardToSharedArrayBuffer) \ \ diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 8e242d97a7ad..7e11162e85c8 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -2205,39 +2205,6 @@ bool js::ToIndexSlow(JSContext* cx, JS::HandleValue v, return true; } -/** - * Convert |value| to an integer and clamp it to a valid integer index within - * the range `[0..length]`. - */ -template -bool js::ToIntegerIndexSlow(JSContext* cx, Handle 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 || - std::is_same_v, - "If this assertion fails, add the corresponding unsigned int " - "type for size_t to the explicit instantiations below"); - -template bool js::ToIntegerIndexSlow(JSContext*, Handle, - uint32_t, uint32_t*); - -template bool js::ToIntegerIndexSlow(JSContext*, Handle, - uint64_t, uint64_t*); - template double js_strtod(const CharT* begin, const CharT* end, const CharT** dEnd) { const CharT* s = SkipSpace(begin, end); diff --git a/js/src/jsnum.h b/js/src/jsnum.h index 8120a57ada28..9be7ea8bc39c 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -404,40 +404,6 @@ static MOZ_ALWAYS_INLINE bool IsDefinitelyIndex(const Value& v, 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 -[[nodiscard]] extern bool ToIntegerIndexSlow(JSContext* cx, Handle value, - ArrayLength length, - ArrayLength* result); - -template -[[nodiscard]] static inline bool ToIntegerIndex(JSContext* cx, - Handle value, - ArrayLength length, - ArrayLength* result) { - static_assert(std::is_unsigned_v); - - // 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 */ #endif /* jsnum_h */ diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index 09f105044b48..9b8f35bb5b45 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -43,10 +43,8 @@ #include "js/Wrapper.h" #include "util/WindowsWrapper.h" #include "vm/GlobalObject.h" -#include "vm/Interpreter.h" #include "vm/JSContext.h" #include "vm/JSObject.h" -#include "vm/SelfHosting.h" #include "vm/SharedArrayObject.h" #include "vm/Warnings.h" // js::WarnNumberASCII #include "wasm/WasmConstants.h" @@ -331,7 +329,7 @@ static const JSPropertySpec arraybuffer_properties[] = { }; 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("transfer", ArrayBufferObject::transfer, 0, 0), JS_FN("transferToFixedLength", ArrayBufferObject::transferToFixedLength, 0, @@ -361,7 +359,6 @@ static const ClassSpec ArrayBufferObjectClassSpec = { arraybuffer_properties, arraybuffer_proto_functions, arraybuffer_proto_properties, - GenericFinishInit, }; static const ClassExtension FixedLengthArrayBufferObjectClassExtension = { @@ -729,190 +726,6 @@ bool ArrayBufferObject::resize(JSContext* cx, unsigned argc, Value* vp) { return CallNonGenericMethod(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 obj( - cx, &args.thisv().toObject().as()); - - // 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 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 ctor(cx, SpeciesConstructor(cx, obj, JSProto_ArrayBuffer, - IsArrayBufferSpecies)); - if (!ctor) { - return false; - } - - // Step 16. - { - FixedConstructArgs<1> cargs(cx); - cargs[0].setNumber(newLen); - - Rooted ctorVal(cx, ObjectValue(*ctor)); - if (!Construct(cx, ctorVal, cargs, ctorVal, &resultObj)) { - return false; - } - } - - // Steps 17-18. - unwrappedResult = resultObj->maybeUnwrapIf(); - 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(cx, args); -} - /* * ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1 */ diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index 3ac94cc10915..c8b40a51f41c 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -183,7 +183,6 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared { static bool maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args); static bool resizableGetterImpl(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 transferImpl(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 slice(JSContext* cx, unsigned argc, Value* vp); - static bool resize(JSContext* cx, unsigned argc, Value* vp); static bool transfer(JSContext* cx, unsigned argc, Value* vp); diff --git a/js/src/vm/BuiltinObjectKind.cpp b/js/src/vm/BuiltinObjectKind.cpp index 4670e7a38169..a770bd58f0f2 100644 --- a/js/src/vm/BuiltinObjectKind.cpp +++ b/js/src/vm/BuiltinObjectKind.cpp @@ -17,6 +17,10 @@ static JSProtoKey ToProtoKey(BuiltinObjectKind kind) { switch (kind) { case BuiltinObjectKind::Array: return JSProto_Array; + case BuiltinObjectKind::ArrayBuffer: + return JSProto_ArrayBuffer; + case BuiltinObjectKind::Int32Array: + return JSProto_Int32Array; case BuiltinObjectKind::ListFormat: return JSProto_ListFormat; case BuiltinObjectKind::Map: @@ -27,6 +31,8 @@ static JSProtoKey ToProtoKey(BuiltinObjectKind kind) { return JSProto_RegExp; case BuiltinObjectKind::Set: return JSProto_Set; + case BuiltinObjectKind::SharedArrayBuffer: + return JSProto_SharedArrayBuffer; case BuiltinObjectKind::Symbol: return JSProto_Symbol; @@ -34,6 +40,12 @@ static JSProtoKey ToProtoKey(BuiltinObjectKind kind) { return JSProto_Function; case BuiltinObjectKind::IteratorPrototype: return JSProto_Iterator; + case BuiltinObjectKind::ObjectPrototype: + return JSProto_Object; + case BuiltinObjectKind::RegExpPrototype: + return JSProto_RegExp; + case BuiltinObjectKind::StringPrototype: + return JSProto_String; case BuiltinObjectKind::DateTimeFormatPrototype: return JSProto_DateTimeFormat; @@ -49,16 +61,22 @@ static JSProtoKey ToProtoKey(BuiltinObjectKind kind) { static bool IsPrototype(BuiltinObjectKind kind) { switch (kind) { case BuiltinObjectKind::Array: + case BuiltinObjectKind::ArrayBuffer: + case BuiltinObjectKind::Int32Array: case BuiltinObjectKind::ListFormat: case BuiltinObjectKind::Map: case BuiltinObjectKind::Promise: case BuiltinObjectKind::RegExp: case BuiltinObjectKind::Set: + case BuiltinObjectKind::SharedArrayBuffer: case BuiltinObjectKind::Symbol: return false; case BuiltinObjectKind::FunctionPrototype: case BuiltinObjectKind::IteratorPrototype: + case BuiltinObjectKind::ObjectPrototype: + case BuiltinObjectKind::RegExpPrototype: + case BuiltinObjectKind::StringPrototype: return true; case BuiltinObjectKind::DateTimeFormatPrototype: @@ -76,6 +94,12 @@ BuiltinObjectKind js::BuiltinConstructorForName( if (name == frontend::TaggedParserAtomIndex::WellKnown::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()) { return BuiltinObjectKind::ListFormat; } @@ -91,6 +115,9 @@ BuiltinObjectKind js::BuiltinConstructorForName( if (name == frontend::TaggedParserAtomIndex::WellKnown::Set()) { return BuiltinObjectKind::Set; } + if (name == frontend::TaggedParserAtomIndex::WellKnown::SharedArrayBuffer()) { + return BuiltinObjectKind::SharedArrayBuffer; + } if (name == frontend::TaggedParserAtomIndex::WellKnown::Symbol()) { return BuiltinObjectKind::Symbol; } @@ -105,6 +132,15 @@ BuiltinObjectKind js::BuiltinPrototypeForName( if (name == frontend::TaggedParserAtomIndex::WellKnown::Iterator()) { 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()) { return BuiltinObjectKind::DateTimeFormatPrototype; } @@ -135,6 +171,10 @@ const char* js::BuiltinObjectName(BuiltinObjectKind kind) { switch (kind) { case BuiltinObjectKind::Array: return "Array"; + case BuiltinObjectKind::ArrayBuffer: + return "ArrayBuffer"; + case BuiltinObjectKind::Int32Array: + return "Int32Array"; case BuiltinObjectKind::ListFormat: return "ListFormat"; case BuiltinObjectKind::Map: @@ -143,6 +183,8 @@ const char* js::BuiltinObjectName(BuiltinObjectKind kind) { return "Promise"; case BuiltinObjectKind::RegExp: return "RegExp"; + case BuiltinObjectKind::SharedArrayBuffer: + return "SharedArrayBuffer"; case BuiltinObjectKind::Set: return "Set"; case BuiltinObjectKind::Symbol: @@ -152,6 +194,12 @@ const char* js::BuiltinObjectName(BuiltinObjectKind kind) { return "Function.prototype"; case BuiltinObjectKind::IteratorPrototype: return "Iterator.prototype"; + case BuiltinObjectKind::ObjectPrototype: + return "Object.prototype"; + case BuiltinObjectKind::RegExpPrototype: + return "RegExp.prototype"; + case BuiltinObjectKind::StringPrototype: + return "String.prototype"; case BuiltinObjectKind::DateTimeFormatPrototype: return "DateTimeFormat.prototype"; diff --git a/js/src/vm/BuiltinObjectKind.h b/js/src/vm/BuiltinObjectKind.h index fd5770199c3f..138aca1eface 100644 --- a/js/src/vm/BuiltinObjectKind.h +++ b/js/src/vm/BuiltinObjectKind.h @@ -29,16 +29,22 @@ class GlobalObject; enum class BuiltinObjectKind : uint8_t { // Built-in constructors. Array, + ArrayBuffer, + Int32Array, ListFormat, Map, Promise, RegExp, Set, + SharedArrayBuffer, Symbol, // Built-in prototypes. FunctionPrototype, IteratorPrototype, + ObjectPrototype, + RegExpPrototype, + StringPrototype, // Built-in Intl prototypes. DateTimeFormatPrototype, diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 1472244a038d..a529d45f4ef1 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -156,7 +156,6 @@ MACRO_(dollar_ArrayValues_, "$ArrayValues") \ MACRO_(dollar_RegExpFlagsGetter_, "$RegExpFlagsGetter") \ MACRO_(dollar_RegExpToString_, "$RegExpToString") \ - MACRO_(dollar_SharedArrayBufferSpecies_, "$SharedArrayBufferSpecies") \ MACRO_(domNode, "domNode") \ MACRO_(done, "done") \ MACRO_(dotAll, "dotAll") \ diff --git a/js/src/vm/RealmFuses.cpp b/js/src/vm/RealmFuses.cpp index 39c83fe7386a..afa641b82854 100644 --- a/js/src/vm/RealmFuses.cpp +++ b/js/src/vm/RealmFuses.cpp @@ -336,43 +336,27 @@ void js::OptimizeArraySpeciesFuse::popFuse(JSContext* cx, JSUseCounter::OPTIMIZE_ARRAY_SPECIES_FUSE); } -static bool SpeciesFuseCheckInvariant(JSContext* cx, JSProtoKey protoKey, - PropertyName* selfHostedSpeciesAccessor) { - // Prototype must be initialized. - auto* proto = cx->global()->maybeGetPrototype(protoKey); +bool js::OptimizeArraySpeciesFuse::checkInvariant(JSContext* cx) { + // Prototype must be Array.prototype. + auto* proto = cx->global()->maybeGetArrayPrototype(); if (!proto) { // No proto, invariant still holds return true; } - auto* ctor = cx->global()->maybeGetConstructor(protoKey); + auto* ctor = cx->global()->maybeGetConstructor(JSProto_Array); 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), ObjectValue(*ctor))) { 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); - return ObjectHasGetterFunction(ctor, speciesKey, selfHostedSpeciesAccessor); -} - -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_); + return ObjectHasGetterFunction(ctor, speciesKey, + cx->names().dollar_ArraySpecies_); } void js::OptimizePromiseLookupFuse::popFuse(JSContext* cx, diff --git a/js/src/vm/RealmFuses.h b/js/src/vm/RealmFuses.h index 03bf13e08c86..6ad2b5042fed 100644 --- a/js/src/vm/RealmFuses.h +++ b/js/src/vm/RealmFuses.h @@ -150,34 +150,6 @@ struct OptimizeArraySpeciesFuse final : public InvalidatingRealmFuse { 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 // intact, the following invariants must hold: // @@ -309,9 +281,6 @@ struct OptimizeWeakSetPrototypeAddFuse final : public RealmFuse { FUSE(IteratorPrototypeHasObjectProto, iteratorPrototypeHasObjectProto) \ FUSE(ObjectPrototypeHasNoReturnProperty, objectPrototypeHasNoReturnProperty) \ FUSE(OptimizeArraySpeciesFuse, optimizeArraySpeciesFuse) \ - FUSE(OptimizeArrayBufferSpeciesFuse, optimizeArrayBufferSpeciesFuse) \ - FUSE(OptimizeSharedArrayBufferSpeciesFuse, \ - optimizeSharedArrayBufferSpeciesFuse) \ FUSE(OptimizePromiseLookupFuse, optimizePromiseLookupFuse) \ FUSE(OptimizeRegExpPrototypeFuse, optimizeRegExpPrototypeFuse) \ FUSE(OptimizeStringPrototypeSymbolsFuse, optimizeStringPrototypeSymbolsFuse) \ diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 9f733f74f5e4..dcfa94f1ebcf 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -876,6 +876,37 @@ static bool intrinsic_GeneratorSetClosed(JSContext* cx, unsigned argc, return true; } +template +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()); + + size_t byteLength = args[0].toObject().as().byteLength(); + args.rval().setNumber(byteLength); + return true; +} + +template +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(); + if (!obj) { + ReportAccessDenied(cx); + return false; + } + + size_t byteLength = obj->byteLength(); + args.rval().setNumber(byteLength); + return true; +} + static void AssertNonNegativeInteger(const Value& v) { MOZ_ASSERT(v.isNumber()); MOZ_ASSERT(v.toNumber() >= 0); @@ -883,6 +914,60 @@ static void AssertNonNegativeInteger(const Value& v) { MOZ_ASSERT(JS::ToInteger(v.toNumber()) == v.toNumber()); } +template +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 toBuffer(cx); + if (!isWrapped) { + toBuffer = &args[0].toObject().as(); + } else { + JSObject* wrapped = &args[0].toObject(); + MOZ_ASSERT(wrapped->is()); + toBuffer = wrapped->maybeUnwrapAs(); + if (!toBuffer) { + ReportAccessDenied(cx); + return false; + } + } + size_t toIndex = size_t(args[1].toNumber()); + Rooted fromBuffer(cx, &args[2].toObject().as()); + 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(); + if (!lhs) { + ReportAccessDenied(cx); + return false; + } + auto* rhs = args[1].toObject().maybeUnwrapAs(); + if (!rhs) { + ReportAccessDenied(cx); + return false; + } + + args.rval().setBoolean(lhs->rawBufferObject() == rhs->rawBufferObject()); + return true; +} + static bool intrinsic_IsTypedArrayConstructor(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -1903,11 +1988,18 @@ static bool intrinsic_ToTemporalDuration(JSContext* cx, unsigned argc, static const JSFunctionSpec intrinsic_functions[] = { // Intrinsic helper functions + JS_INLINABLE_FN("ArrayBufferByteLength", + intrinsic_ArrayBufferByteLength, 1, 0, + IntrinsicArrayBufferByteLength), + JS_FN("ArrayBufferCopyData", + intrinsic_ArrayBufferCopyData, 6, 0), JS_INLINABLE_FN("ArrayIteratorPrototypeOptimizable", intrinsic_ArrayIteratorPrototypeOptimizable, 0, 0, IntrinsicArrayIteratorPrototypeOptimizable), JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1, 0), JS_FN("BigIntToNumber", intrinsic_BigIntToNumber, 1, 0), + JS_FN("CallArrayBufferMethodIfWrapped", + CallNonGenericSelfhostedMethod>, 2, 0), JS_FN("CallArrayIteratorMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2, 0), #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT @@ -1940,6 +2032,8 @@ static const JSFunctionSpec intrinsic_functions[] = { CallNonGenericSelfhostedMethod>, 2, 0), JS_FN("CallSetMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2, 0), + JS_FN("CallSharedArrayBufferMethodIfWrapped", + CallNonGenericSelfhostedMethod>, 2, 0), JS_FN("CallStringIteratorMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2, 0), JS_FN("CallTypedArrayMethodIfWrapped", @@ -2083,6 +2177,10 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_INLINABLE_FN("IsTypedArrayConstructor", intrinsic_IsTypedArrayConstructor, 1, 0, IntrinsicIsTypedArrayConstructor), + JS_FN("IsWrappedArrayBuffer", + intrinsic_IsWrappedInstanceOfBuiltin, 1, 0), + JS_FN("IsWrappedSharedArrayBuffer", + intrinsic_IsWrappedInstanceOfBuiltin, 1, 0), JS_INLINABLE_FN("NewArrayIterator", intrinsic_NewArrayIterator, 0, 0, IntrinsicNewArrayIterator), 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("NoPrivateGetter", intrinsic_NoPrivateGetter, 1, 0), JS_FN("NumberToBigInt", intrinsic_NumberToBigInt, 1, 0), + JS_INLINABLE_FN( + "PossiblyWrappedArrayBufferByteLength", + intrinsic_PossiblyWrappedArrayBufferByteLength, 1, 0, + IntrinsicPossiblyWrappedArrayBufferByteLength), + JS_FN( + "PossiblyWrappedSharedArrayBufferByteLength", + intrinsic_PossiblyWrappedArrayBufferByteLength, + 1, 0), JS_FN("PossiblyWrappedTypedArrayHasDetachedBuffer", intrinsic_PossiblyWrappedTypedArrayHasDetachedBuffer, 1, 0), JS_INLINABLE_FN("PossiblyWrappedTypedArrayLength", @@ -2126,6 +2232,12 @@ static const JSFunctionSpec intrinsic_functions[] = { intrinsic_RegExpSymbolProtocolOnPrimitiveCounter, 0, 0), JS_INLINABLE_FN("SameValue", js::obj_is, 2, 0, ObjectIs), JS_FN("SetCopy", SetObject::copy, 1, 0), + JS_FN("SharedArrayBufferByteLength", + intrinsic_ArrayBufferByteLength, 1, 0), + JS_FN("SharedArrayBufferCopyData", + intrinsic_ArrayBufferCopyData, 6, 0), + JS_FN("SharedArrayBuffersMemorySame", + intrinsic_SharedArrayBuffersMemorySame, 2, 0), JS_FN("StringReplaceAllString", intrinsic_StringReplaceAllString, 3, 0), JS_INLINABLE_FN("StringReplaceString", intrinsic_StringReplaceString, 3, 0, IntrinsicStringReplaceString), diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp index 6ea1e789f5d3..8b010fb595a1 100644 --- a/js/src/vm/SharedArrayObject.cpp +++ b/js/src/vm/SharedArrayObject.cpp @@ -10,8 +10,6 @@ #include "mozilla/DebugOnly.h" #include "mozilla/TaggedAnonymousMemory.h" -#include "jsnum.h" - #include "gc/GCContext.h" #include "gc/Memory.h" #include "jit/AtomicOperations.h" @@ -21,8 +19,6 @@ #include "js/SharedArrayBuffer.h" #include "util/Memory.h" #include "util/WindowsWrapper.h" -#include "vm/Interpreter.h" -#include "vm/SelfHosting.h" #include "vm/SharedMem.h" #include "wasm/WasmConstants.h" #include "wasm/WasmMemory.h" @@ -434,156 +430,6 @@ bool SharedArrayBufferObject::grow(JSContext* cx, unsigned argc, Value* vp) { return CallNonGenericMethod(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 obj( - cx, &args.thisv().toObject().as()); - - // 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 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 ctor( - cx, SpeciesConstructor(cx, obj, JSProto_SharedArrayBuffer, - IsSharedArrayBufferSpecies)); - if (!ctor) { - return false; - } - - // Step 15. - { - FixedConstructArgs<1> cargs(cx); - cargs[0].setNumber(newLen); - - Rooted ctorVal(cx, ObjectValue(*ctor)); - if (!Construct(cx, ctorVal, cargs, ctorVal, &resultObj)) { - return false; - } - } - - // Steps 16-17. - unwrappedResult = resultObj->maybeUnwrapIf(); - 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(cx, args); -} - // ES2024 draft rev 3a773fc9fae58be023228b13dbbd402ac18eeb6b // 25.2.3.1 SharedArrayBuffer ( length [ , options ] ) bool SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, @@ -829,10 +675,10 @@ void SharedArrayBufferObject::addSizeOfExcludingThis( } /* static */ -void SharedArrayBufferObject::copyData(ArrayBufferObjectMaybeShared* toBuffer, - size_t toIndex, - ArrayBufferObjectMaybeShared* fromBuffer, - size_t fromIndex, size_t count) { +void SharedArrayBufferObject::copyData( + Handle toBuffer, size_t toIndex, + Handle fromBuffer, size_t fromIndex, + size_t count) { MOZ_ASSERT(toBuffer->byteLength() >= count); MOZ_ASSERT(toBuffer->byteLength() >= toIndex + count); MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex); @@ -936,7 +782,7 @@ static const JSPropertySpec sharedarray_properties[] = { }; 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_FS_END, }; @@ -963,7 +809,6 @@ static const ClassSpec SharedArrayBufferObjectClassSpec = { sharedarray_properties, sharedarray_proto_functions, sharedarray_proto_properties, - GenericFinishInit, }; const JSClass SharedArrayBufferObject::protoClass_ = { diff --git a/js/src/vm/SharedArrayObject.h b/js/src/vm/SharedArrayObject.h index dfb26fe2f1a6..c78f0f1ec269 100644 --- a/js/src/vm/SharedArrayObject.h +++ b/js/src/vm/SharedArrayObject.h @@ -255,7 +255,6 @@ class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared { static bool maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args); static bool growableGetterImpl(JSContext* cx, const CallArgs& args); static bool growImpl(JSContext* cx, const CallArgs& args); - static bool sliceImpl(JSContext* cx, const CallArgs& args); public: // 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 slice(JSContext* cx, unsigned argc, Value* vp); - static bool isOriginalByteLengthGetter(Native native) { return native == byteLengthGetter; } @@ -328,8 +325,9 @@ class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared { JS::ClassInfo* info, JS::RuntimeSizes* runtimeSizes); - static void copyData(ArrayBufferObjectMaybeShared* toBuffer, size_t toIndex, - ArrayBufferObjectMaybeShared* fromBuffer, + static void copyData(Handle toBuffer, + size_t toIndex, + Handle fromBuffer, size_t fromIndex, size_t count); SharedArrayRawBuffer* rawBufferObject() const; diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 41caa6b6dcab..7ef471bba8ba 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -1844,6 +1844,40 @@ static bool TypedArray_set(JSContext* cx, unsigned argc, Value* vp) { return CallNonGenericMethod(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, 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 // 22.2.3.5 %TypedArray%.prototype.copyWithin ( target, start [ , end ] ) static bool TypedArray_copyWithin(JSContext* cx, const CallArgs& args) { diff --git a/js/src/vm/Watchtower.cpp b/js/src/vm/Watchtower.cpp index 4ea6ae040d3e..cbf3680fd521 100644 --- a/js/src/vm/Watchtower.cpp +++ b/js/src/vm/Watchtower.cpp @@ -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) { // Handle writes to Array constructor fuse properties. MaybePopArrayConstructorFuses(cx, obj, id); @@ -534,18 +488,6 @@ static void MaybePopFuses(JSContext* cx, NativeObject* obj, jsid id) { // Handle writes to RegExp.prototype fuse properties. 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