diff options
author | Brian Smith <brian@dbsoft.org> | 2023-07-24 23:30:27 -0500 |
---|---|---|
committer | Brian Smith <brian@dbsoft.org> | 2023-07-24 23:30:27 -0500 |
commit | edacbbd96538a60495dd50010b5549defc1ea35c (patch) | |
tree | cce9a88046cf1ddccc37bebc7cb11fb0ec1e0e71 | |
parent | b4ddadd8b98a83e833eb01223dbb3858955e868d (diff) | |
download | uxp-edacbbd96538a60495dd50010b5549defc1ea35c.tar.gz |
Issue #2026 - Part 1 - Implement BigInt64 and BigUint64Array.
https://bugzilla.mozilla.org/show_bug.cgi?id=1456569
36 files changed, 788 insertions, 232 deletions
diff --git a/js/public/Class.h b/js/public/Class.h index 8aeacdc541..40885e6082 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -913,7 +913,7 @@ struct JSClass { // application. #define JSCLASS_GLOBAL_APPLICATION_SLOTS 5 #define JSCLASS_GLOBAL_SLOT_COUNT \ - (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 50) + (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 52) #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \ (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n))) #define JSCLASS_GLOBAL_FLAGS \ diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 388af28f26..f4353480ba 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -842,7 +842,9 @@ EnumerableOwnProperties(JSContext* cx, const JS::CallArgs& args, EnumerableOwnPr if (obj->is<NativeObject>()) { HandleNativeObject nobj = obj.as<NativeObject>(); if (JSID_IS_INT(id) && nobj->containsDenseElement(JSID_TO_INT(id))) { - value = nobj->getDenseOrTypedArrayElement(JSID_TO_INT(id)); + if(!nobj->getDenseOrTypedArrayElement<CanGC>(cx, JSID_TO_INT(id), &value)) { + return false; + } } else { shape = nobj->lookup(cx, id); if (!shape || !(shape->attributes() & JSPROP_ENUMERATE)) diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js index 22023aa7ca..57f6d738ca 100644 --- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -879,7 +879,7 @@ function SetFromNonTypedArray(target, array, targetOffset, targetLength, targetB // Steps 12-15, 21, 23-24. while (targetOffset < limitOffset) { // Steps 24a-c. - var kNumber = ToNumber(src[k]); + var kNumber = ToNumeric(src[k]); // Step 24d. This explicit check will be unnecessary when we implement // throw-on-getting/setting-element-in-detached-buffer semantics. @@ -1098,6 +1098,30 @@ function TypedArrayCompare(x, y) { return Number_isNaN(y) ? -1 : 0; } +// https://tc39.github.io/proposal-bigint/#sec-%typedarray%.prototype.sort +// TypedArray SortCompare specialization for BigInt values. +function TypedArrayCompareBigInt(x, y) { + // Step 1. + // eslint-disable-next-line valid-typeof + assert(typeof x === "bigint" && typeof y === "bigint", + "x and y are not BigInts."); + + // Step 2 (Implemented in TypedArraySort). + + // Step 6. + if (x < y) + return -1; + + // Step 7. + if (x > y) + return 1; + + // Steps 3-5, 8-9 (Not applicable when sorting BigInt values). + + // Step 10. + return 0; +} + // TypedArray SortCompare specialization for integer values. function TypedArrayCompareInt(x, y) { // Step 1. diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h index 83700001d4..9318a0f795 100644 --- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -245,6 +245,10 @@ class ScalarTypeDescr : public SimpleTypeDescr "TypedObjectConstants.h must be consistent with Scalar::Type"); static_assert(Scalar::Uint32 == JS_SCALARTYPEREPR_UINT32, "TypedObjectConstants.h must be consistent with Scalar::Type"); + static_assert(Scalar::BigInt64 == JS_SCALARTYPEREPR_BIGINT64, + "TypedObjectConstants.h must be consistent with Scalar::Type"); + static_assert(Scalar::BigUint64 == JS_SCALARTYPEREPR_BIGUINT64, + "TypedObjectConstants.h must be consistent with Scalar::Type"); static_assert(Scalar::Float32 == JS_SCALARTYPEREPR_FLOAT32, "TypedObjectConstants.h must be consistent with Scalar::Type"); static_assert(Scalar::Float64 == JS_SCALARTYPEREPR_FLOAT64, @@ -270,14 +274,16 @@ class ScalarTypeDescr : public SimpleTypeDescr // unique C representation. In particular, omits Uint8Clamped since it // is just a Uint8. #define JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_) \ - macro_(Scalar::Int8, int8_t, int8) \ - macro_(Scalar::Uint8, uint8_t, uint8) \ - macro_(Scalar::Int16, int16_t, int16) \ - macro_(Scalar::Uint16, uint16_t, uint16) \ - macro_(Scalar::Int32, int32_t, int32) \ - macro_(Scalar::Uint32, uint32_t, uint32) \ - macro_(Scalar::Float32, float, float32) \ - macro_(Scalar::Float64, double, float64) + macro_(Scalar::Int8, int8_t, int8) \ + macro_(Scalar::Uint8, uint8_t, uint8) \ + macro_(Scalar::Int16, int16_t, int16) \ + macro_(Scalar::Uint16, uint16_t, uint16) \ + macro_(Scalar::Int32, int32_t, int32) \ + macro_(Scalar::Uint32, uint32_t, uint32) \ + macro_(Scalar::Float32, float, float32) \ + macro_(Scalar::Float64, double, float64) \ + macro_(Scalar::BigInt64, int64_t, bigint64) \ + macro_(Scalar::BigUint64, uint64_t, biguint64) // Must be in same order as the enum ScalarTypeDescr::Type: #define JS_FOR_EACH_SCALAR_TYPE_REPR(macro_) \ diff --git a/js/src/builtin/TypedObjectConstants.h b/js/src/builtin/TypedObjectConstants.h index a28c9159a5..aa930d29bb 100644 --- a/js/src/builtin/TypedObjectConstants.h +++ b/js/src/builtin/TypedObjectConstants.h @@ -88,10 +88,12 @@ #define JS_SCALARTYPEREPR_FLOAT32 6 #define JS_SCALARTYPEREPR_FLOAT64 7 #define JS_SCALARTYPEREPR_UINT8_CLAMPED 8 -#define JS_SCALARTYPEREPR_FLOAT32X4 11 -#define JS_SCALARTYPEREPR_INT8X16 12 -#define JS_SCALARTYPEREPR_INT16X8 13 -#define JS_SCALARTYPEREPR_INT32X4 14 +#define JS_SCALARTYPEREPR_BIGINT64 9 +#define JS_SCALARTYPEREPR_BIGUINT64 10 +#define JS_SCALARTYPEREPR_FLOAT32X4 13 +#define JS_SCALARTYPEREPR_INT8X16 14 +#define JS_SCALARTYPEREPR_INT16X8 15 +#define JS_SCALARTYPEREPR_INT32X4 16 // These constants are for use exclusively in JS code. In C++ code, // prefer ReferenceTypeRepresentation::TYPE_ANY etc, which allows diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 2827f4b1d5..600b56d096 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -3204,6 +3204,9 @@ StoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type, Addres } else { masm.jump(failure); } + } else if (type == Scalar::BigInt64 || type == Scalar::BigUint64) { + // FIXME: https://bugzil.la/1536703 + masm.jump(failure); } else { Label notInt32; masm.branchTestInt32(Assembler::NotEqual, value, ¬Int32); diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h index c9714343dc..50b09cc30e 100644 --- a/js/src/jit/IonTypes.h +++ b/js/src/jit/IonTypes.h @@ -724,34 +724,9 @@ ScalarTypeToMIRType(Scalar::Type type) return MIRType::Int16x8; case Scalar::Int32x4: return MIRType::Int32x4; - case Scalar::MaxTypedArrayViewType: - break; - } - MOZ_CRASH("unexpected SIMD kind"); -} - -static inline unsigned -ScalarTypeToLength(Scalar::Type type) -{ - switch (type) { - case Scalar::Int8: - case Scalar::Uint8: - case Scalar::Int16: - case Scalar::Uint16: - case Scalar::Int32: - case Scalar::Uint32: - case Scalar::Int64: - case Scalar::Float32: - case Scalar::Float64: - case Scalar::Uint8Clamped: - return 1; - case Scalar::Float32x4: - case Scalar::Int32x4: - return 4; - case Scalar::Int16x8: - return 8; - case Scalar::Int8x16: - return 16; + case Scalar::BigInt64: + case Scalar::BigUint64: + MOZ_CRASH("NYI"); case Scalar::MaxTypedArrayViewType: break; } diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 63ff8f7201..2264bed4f2 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -6005,7 +6005,14 @@ jit::ElementAccessIsTypedArray(CompilerConstraintList* constraints, return false; *arrayType = types->getTypedArrayType(constraints); - return *arrayType != Scalar::MaxTypedArrayViewType; + + // FIXME: https://bugzil.la/1536699 + if (*arrayType == Scalar::MaxTypedArrayViewType || + Scalar::isBigIntType(*arrayType)) { + return false; + } + + return true; } bool diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 6a2adc9622..72c5214845 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -14443,6 +14443,9 @@ MIRTypeForTypedArrayRead(Scalar::Type arrayType, bool observedDouble) return MIRType::Float32; case Scalar::Float64: return MIRType::Double; + case Scalar::BigInt64: + case Scalar::BigUint64: + return MIRType::BigInt; default: break; } diff --git a/js/src/jit/MacroAssembler-inl.h b/js/src/jit/MacroAssembler-inl.h index 336b9e2df8..98316eb643 100644 --- a/js/src/jit/MacroAssembler-inl.h +++ b/js/src/jit/MacroAssembler-inl.h @@ -644,7 +644,7 @@ void MacroAssembler::canonicalizeFloatIfDeterministic(FloatRegister reg) { #ifdef JS_MORE_DETERMINISTIC - // See the comment in TypedArrayObjectTemplate::getIndexValue. + // See the comment in TypedArrayObjectTemplate::getElement. canonicalizeFloat(reg); #endif // JS_MORE_DETERMINISTIC } @@ -662,7 +662,7 @@ void MacroAssembler::canonicalizeDoubleIfDeterministic(FloatRegister reg) { #ifdef JS_MORE_DETERMINISTIC - // See the comment in TypedArrayObjectTemplate::getIndexValue. + // See the comment in TypedArrayObjectTemplate::getElement. canonicalizeDouble(reg); #endif // JS_MORE_DETERMINISTIC } diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index fdbcc9f23c..07f7b6fdcd 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -345,6 +345,11 @@ MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const T& src, AnyRegi branchTest32(Assembler::Signed, dest.gpr(), dest.gpr(), fail); } break; + case Scalar::BigInt64: + case Scalar::BigUint64: + // FIXME: https://bugzil.la/1536702 + jump(fail); + break; case Scalar::Float32: loadFloat32(src, dest.fpu()); canonicalizeFloat(dest.fpu()); @@ -447,17 +452,25 @@ MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const T& src, const V tagValue(JSVAL_TYPE_INT32, temp, dest); } break; - case Scalar::Float32: + case Scalar::Float32: { loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloat32Reg), dest.scratchReg(), nullptr); convertFloat32ToDouble(ScratchFloat32Reg, ScratchDoubleReg); boxDouble(ScratchDoubleReg, dest); break; - case Scalar::Float64: + } + case Scalar::Float64: { loadFromTypedArray(arrayType, src, AnyRegister(ScratchDoubleReg), dest.scratchReg(), nullptr); boxDouble(ScratchDoubleReg, dest); break; + } + // FIXME: https://bugzil.la/1536702 + case Scalar::BigInt64: + case Scalar::BigUint64: { + jump(fail); + break; + } default: MOZ_CRASH("Invalid typed array type"); } diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index b5f617ce32..52c737e677 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -1780,6 +1780,8 @@ GetTypedArrayRange(TempAllocator& alloc, Scalar::Type type) case Scalar::Int32: return Range::NewInt32Range(alloc, INT32_MIN, INT32_MAX); + case Scalar::BigInt64: + case Scalar::BigUint64: case Scalar::Int64: case Scalar::Float32: case Scalar::Float64: diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index 9dea005abb..3f4052f51d 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -445,6 +445,8 @@ CodeGeneratorX64::wasmStore(const wasm::MemoryAccessDesc& access, const LAllocat case Scalar::Int16x8: case Scalar::Int32x4: case Scalar::Uint8Clamped: + case Scalar::BigInt64: + case Scalar::BigUint64: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected array type"); } diff --git a/js/src/jit/x64/Lowering-x64.cpp b/js/src/jit/x64/Lowering-x64.cpp index db92d8dac5..4aebe05af2 100644 --- a/js/src/jit/x64/Lowering-x64.cpp +++ b/js/src/jit/x64/Lowering-x64.cpp @@ -239,6 +239,8 @@ LIRGeneratorX64::visitWasmStore(MWasmStore* ins) case Scalar::Int32x4: valueAlloc = useRegisterAtStart(value); break; + case Scalar::BigInt64: + case Scalar::BigUint64: case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected array type"); diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index 759977bac1..1fab669d2b 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -713,6 +713,8 @@ MacroAssembler::wasmLoad(const wasm::MemoryAccessDesc& access, Operand srcAddr, break; case Scalar::Int64: MOZ_CRASH("int64 loads must use load64"); + case Scalar::BigInt64: + case Scalar::BigUint64: case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected array type"); @@ -759,6 +761,8 @@ MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access, Operand srcAdd case Scalar::Int16x8: case Scalar::Int32x4: MOZ_CRASH("non-int64 loads should use load()"); + case Scalar::BigInt64: + case Scalar::BigUint64: case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected array type"); @@ -822,6 +826,8 @@ MacroAssembler::wasmStore(const wasm::MemoryAccessDesc& access, AnyRegister valu MOZ_ASSERT(access.numSimdElems() == 8, "unexpected partial store"); storeUnalignedSimd128Int(value.fpu(), dstAddr); break; + case Scalar::BigInt64: + case Scalar::BigUint64: case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected array type"); diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 3111ae4c92..5ec00da849 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -402,6 +402,8 @@ CodeGeneratorX86Shared::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTyp case Scalar::Int8x16: case Scalar::Int16x8: case Scalar::Int32x4: + case Scalar::BigInt64: + case Scalar::BigUint64: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected array type"); case Scalar::Float32: diff --git a/js/src/jit/x86/Lowering-x86.cpp b/js/src/jit/x86/Lowering-x86.cpp index ad3dd8afcb..27859c0772 100644 --- a/js/src/jit/x86/Lowering-x86.cpp +++ b/js/src/jit/x86/Lowering-x86.cpp @@ -326,6 +326,8 @@ LIRGeneratorX86::visitWasmStore(MWasmStore* ins) add(lir, ins); return; } + case Scalar::BigInt64: + case Scalar::BigUint64: case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected array type"); diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index aef460e2f4..9962b9c594 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -616,6 +616,8 @@ MacroAssembler::wasmLoad(const wasm::MemoryAccessDesc& access, Operand srcAddr, vmovdquWithPatch(srcAddr, out.fpu()); break; case Scalar::Int64: + case Scalar::BigInt64: + case Scalar::BigUint64: case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected type"); @@ -727,6 +729,8 @@ MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access, Operand srcAdd case Scalar::Int16x8: case Scalar::Int32x4: MOZ_CRASH("non-int64 loads should use load()"); + case Scalar::BigInt64: + case Scalar::BigUint64: case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected array type"); @@ -790,6 +794,8 @@ MacroAssembler::wasmStore(const wasm::MemoryAccessDesc& access, AnyRegister valu case Scalar::Int64: MOZ_CRASH("Should be handled in storeI64."); case Scalar::MaxTypedArrayViewType: + case Scalar::BigInt64: + case Scalar::BigUint64: MOZ_CRASH("unexpected type"); } append(wasm::MemoryPatch(size())); diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index cc0fcbebd9..4f5c717be5 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -376,6 +376,13 @@ js::GetElements(JSContext* cx, HandleObject aobj, uint32_t length, Value* vp) } } + if (aobj->is<TypedArrayObject>()) { + Handle<TypedArrayObject*> typedArray = aobj.as<TypedArrayObject>(); + if (typedArray->length() == length) { + return TypedArrayObject::getElements(cx, typedArray, vp); + } + } + if (js::GetElementsOp op = aobj->getOpsGetElements()) { ElementAdder adder(cx, vp, length, ElementAdder::GetElement); return op(cx, aobj, 0, length, &adder); diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 6a13d4343c..6a873e15da 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1467,12 +1467,15 @@ GetSCOffset(JSStructuredCloneWriter* writer); namespace Scalar { -/** - * Scalar types that can appear in typed arrays and typed objects. The enum - * values must to be kept in sync with the JS_SCALARTYPEREPR_ constants, as - * well as the TypedArrayObject::classes and TypedArrayObject::protoClasses - * definitions. - */ +// Scalar types that can appear in typed arrays and typed objects. +// The enum values must be kept in sync with: +// * the JS_SCALARTYPEREPR constants +// * the TYPEDARRAY_KIND constants +// * the SCTAG_TYPED_ARRAY constants +// * JS_FOR_EACH_TYPEDARRAY +// * JS_FOR_PROTOTYPES_ +// * JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE +// * JIT compilation enum Type { Int8 = 0, Uint8, @@ -1489,6 +1492,9 @@ enum Type { */ Uint8Clamped, + BigInt64, + BigUint64, + /** * Types that don't have their own TypedArray equivalent, for now. */ @@ -1518,6 +1524,8 @@ byteSize(Type atype) return 4; case Int64: case Float64: + case BigInt64: + case BigUint64: return 8; case Int8x16: case Int16x8: @@ -1539,6 +1547,7 @@ isSignedIntType(Type atype) { case Int8x16: case Int16x8: case Int32x4: + case BigInt64: return true; case Uint8: case Uint8Clamped: @@ -1547,12 +1556,39 @@ isSignedIntType(Type atype) { case Float32: case Float64: case Float32x4: + case BigUint64: return false; default: MOZ_CRASH("invalid scalar type"); } } +static inline bool isBigIntType(Type atype) { + switch (atype) { + case BigInt64: + case BigUint64: + return true; + case Int8: + case Int16: + case Int32: + case Int64: + case Uint8: + case Uint8Clamped: + case Uint16: + case Uint32: + case Float32: + case Float64: + case Int8x16: + case Int16x8: + case Int32x4: + case Float32x4: + return false; + case MaxTypedArrayViewType: + break; + } + MOZ_CRASH("invalid scalar type"); +} + static inline bool isSimdType(Type atype) { switch (atype) { @@ -1566,6 +1602,8 @@ isSimdType(Type atype) { case Int64: case Float32: case Float64: + case BigInt64: + case BigUint64: return false; case Int8x16: case Int16x8: @@ -1598,6 +1636,8 @@ scalarByteSize(Type atype) { case Int64: case Float32: case Float64: + case BigInt64: + case BigUint64: case MaxTypedArrayViewType: break; } @@ -1628,6 +1668,10 @@ JS_NewInt32Array(JSContext* cx, uint32_t nelements); extern JS_FRIEND_API(JSObject*) JS_NewUint32Array(JSContext* cx, uint32_t nelements); extern JS_FRIEND_API(JSObject*) +JS_NewBigInt64Array(JSContext* cx, int32_t nelements); +extern JS_FRIEND_API(JSObject*) +JS_NewBigUint64Array(JSContext* cx, int32_t nelements); +extern JS_FRIEND_API(JSObject*) JS_NewFloat32Array(JSContext* cx, uint32_t nelements); extern JS_FRIEND_API(JSObject*) JS_NewFloat64Array(JSContext* cx, uint32_t nelements); @@ -1655,6 +1699,10 @@ JS_NewInt32ArrayFromArray(JSContext* cx, JS::HandleObject array); extern JS_FRIEND_API(JSObject*) JS_NewUint32ArrayFromArray(JSContext* cx, JS::HandleObject array); extern JS_FRIEND_API(JSObject*) +JS_NewBigInt64ArrayWithBuffer(JSContext* cx, JS::HandleObject array); +extern JS_FRIEND_API(JSObject*) +JS_NewBigUint64ArrayWithBuffer(JSContext* cx, JS::HandleObject array); +extern JS_FRIEND_API(JSObject*) JS_NewFloat32ArrayFromArray(JSContext* cx, JS::HandleObject array); extern JS_FRIEND_API(JSObject*) JS_NewFloat64ArrayFromArray(JSContext* cx, JS::HandleObject array); @@ -1688,6 +1736,12 @@ extern JS_FRIEND_API(JSObject*) JS_NewUint32ArrayWithBuffer(JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset, int32_t length); extern JS_FRIEND_API(JSObject*) +JS_NewBigInt64ArrayWithBuffer(JSContext* cx, JS::HandleObject arrayBuffer, + uint32_t byteOffset, int32_t length); +extern JS_FRIEND_API(JSObject*) +JS_NewBigUint64ArrayWithBuffer(JSContext* cx, JS::HandleObject arrayBuffer, + uint32_t byteOffset, int32_t length); +extern JS_FRIEND_API(JSObject*) JS_NewFloat32ArrayWithBuffer(JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset, int32_t length); extern JS_FRIEND_API(JSObject*) @@ -1747,6 +1801,10 @@ JS_IsInt32Array(JSObject* obj); extern JS_FRIEND_API(bool) JS_IsUint32Array(JSObject* obj); extern JS_FRIEND_API(bool) +JS_IsBigInt64Array(JSObject* obj); +extern JS_FRIEND_API(bool) +JS_IsBigUint64Array(JSObject* obj); +extern JS_FRIEND_API(bool) JS_IsFloat32Array(JSObject* obj); extern JS_FRIEND_API(bool) JS_IsFloat64Array(JSObject* obj); @@ -1784,6 +1842,10 @@ UnwrapInt32Array(JSObject* obj); extern JS_FRIEND_API(JSObject*) UnwrapUint32Array(JSObject* obj); extern JS_FRIEND_API(JSObject*) +UnwrapBigInt64Array(JSObject* obj); +extern JS_FRIEND_API(JSObject*) +UnwrapBigUint64Array(JSObject* obj); +extern JS_FRIEND_API(JSObject*) UnwrapFloat32Array(JSObject* obj); extern JS_FRIEND_API(JSObject*) UnwrapFloat64Array(JSObject* obj); @@ -1807,6 +1869,8 @@ extern JS_FRIEND_DATA(const Class* const) Int16ArrayClassPtr; extern JS_FRIEND_DATA(const Class* const) Uint16ArrayClassPtr; extern JS_FRIEND_DATA(const Class* const) Int32ArrayClassPtr; extern JS_FRIEND_DATA(const Class* const) Uint32ArrayClassPtr; +extern JS_FRIEND_DATA(const Class* const) BigInt64ArrayClassPtr; +extern JS_FRIEND_DATA(const Class* const) BigUint64ArrayClassPtr; extern JS_FRIEND_DATA(const Class* const) Float32ArrayClassPtr; extern JS_FRIEND_DATA(const Class* const) Float64ArrayClassPtr; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index d0f9430bc8..730805e038 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2356,15 +2356,15 @@ js::LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Property } static inline bool -NativeGetPureInline(NativeObject* pobj, jsid id, PropertyResult prop, Value* vp) +NativeGetPureInline(NativeObject* pobj, jsid id, PropertyResult prop, Value* vp, + ExclusiveContext* cx) { if (prop.isDenseOrTypedArrayElement()) { // For simplicity we ignore the TypedArray with string index case. if (!JSID_IS_INT(id)) return false; - *vp = pobj->getDenseOrTypedArrayElement(JSID_TO_INT(id)); - return true; + return pobj->getDenseOrTypedArrayElement<NoGC>(cx, JSID_TO_INT(id), vp); } // Fail if we have a custom getter. @@ -2395,7 +2395,7 @@ js::GetPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Value* vp) return true; } - return pobj->isNative() && NativeGetPureInline(&pobj->as<NativeObject>(), id, prop, vp); + return pobj->isNative() && NativeGetPureInline(&pobj->as<NativeObject>(), id, prop, vp, cx); } static inline bool diff --git a/js/src/jsprototypes.h b/js/src/jsprototypes.h index 75168d37ef..34f835bc1d 100644 --- a/js/src/jsprototypes.h +++ b/js/src/jsprototypes.h @@ -91,6 +91,8 @@ real(Float32Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Float32)) \ real(Float64Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Float64)) \ real(Uint8ClampedArray, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint8Clamped)) \ + real(BigInt64Array, InitViaClassSpec, TYPED_ARRAY_CLASP(BigInt64)) \ + real(BigUint64Array, InitViaClassSpec, TYPED_ARRAY_CLASP(BigUint64)) \ real(BigInt, InitViaClassSpec, OCLASP(BigInt)) \ real(Proxy, InitProxyClass, js::ProxyClassPtr) \ real(WeakMap, InitWeakMapClass, OCLASP(WeakMap)) \ diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index 87dce34ba1..4ff7962cfb 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -463,9 +463,11 @@ struct uint8_clamped { explicit uint8_clamped(uint8_t x) { *this = x; } explicit uint8_clamped(uint16_t x) { *this = x; } explicit uint8_clamped(uint32_t x) { *this = x; } + explicit uint8_clamped(uint64_t x) { *this = x; } explicit uint8_clamped(int8_t x) { *this = x; } explicit uint8_clamped(int16_t x) { *this = x; } explicit uint8_clamped(int32_t x) { *this = x; } + explicit uint8_clamped(int64_t x) { *this = x; } explicit uint8_clamped(double x) { *this = x; } uint8_clamped& operator=(const uint8_clamped& x) = default; @@ -485,6 +487,11 @@ struct uint8_clamped { return *this; } + uint8_clamped& operator=(uint64_t x) { + val = (x > 255) ? 255 : uint8_t(x); + return *this; + } + uint8_clamped& operator=(int8_t x) { val = (x >= 0) ? uint8_t(x) : 0; return *this; @@ -508,6 +515,15 @@ struct uint8_clamped { return *this; } + uint8_clamped& operator=(int64_t x) { + val = (x >= 0) + ? ((x < 255) + ? uint8_t(x) + : 255) + : 0; + return *this; + } + uint8_clamped& operator=(const double x) { val = uint8_t(ClampDoubleToUint8(x)); return *this; diff --git a/js/src/vm/BigIntType.cpp b/js/src/vm/BigIntType.cpp index 7b8375526f..44bb1923f7 100644 --- a/js/src/vm/BigIntType.cpp +++ b/js/src/vm/BigIntType.cpp @@ -2626,6 +2626,22 @@ BigInt* js::ToBigInt(ExclusiveContext* cx, HandleValue val) { return nullptr; } +JS::Result<int64_t> js::ToBigInt64(JSContext* cx, HandleValue v) { + BigInt* bi = ToBigInt(cx, v); + if (!bi) { + return cx->alreadyReportedError(); + } + return BigInt::toInt64(bi); +} + +JS::Result<uint64_t> js::ToBigUint64(JSContext* cx, HandleValue v) { + BigInt* bi = ToBigInt(cx, v); + if (!bi) { + return cx->alreadyReportedError(); + } + return BigInt::toUint64(bi); +} + double BigInt::numberValue(BigInt* x) { if (x->isZero()) { return 0.0; diff --git a/js/src/vm/BigIntType.h b/js/src/vm/BigIntType.h index 0ccba99634..7cdd757994 100644 --- a/js/src/vm/BigIntType.h +++ b/js/src/vm/BigIntType.h @@ -358,6 +358,8 @@ namespace js { extern JSAtom* BigIntToAtom(js::ExclusiveContext* cx, JS::HandleBigInt bi); extern JS::BigInt* NumberToBigInt(js::ExclusiveContext* cx, double d); +extern JS::Result<int64_t> ToBigInt64(JSContext* cx, JS::Handle<JS::Value> v); +extern JS::Result<uint64_t> ToBigUint64(JSContext* cx, JS::Handle<JS::Value> v); // Parse a BigInt from a string, using the method specified for StringToBigInt. // Used by the BigInt constructor among other places. diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index efbd8a5323..f05a4db9be 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -36,6 +36,8 @@ macro(AsyncWrapped, AsyncWrapped, "AsyncWrapped") \ macro(async, async, "async") \ macro(await, await, "await") \ + macro(bigint64, bigint64, "bigint64") \ + macro(biguint64, biguint64, "biguint64") \ macro(Bool8x16, Bool8x16, "Bool8x16") \ macro(Bool16x8, Bool16x8, "Bool16x8") \ macro(Bool32x4, Bool32x4, "Bool32x4") \ diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 1e10fe5da3..c4d9cf7287 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -82,6 +82,8 @@ class GlobalObject : public NativeObject FROM_BUFFER_INT16, FROM_BUFFER_UINT32, FROM_BUFFER_INT32, + FROM_BUFFER_UINT64, + FROM_BUFFER_INT64, FROM_BUFFER_FLOAT32, FROM_BUFFER_FLOAT64, FROM_BUFFER_UINT8CLAMPED, @@ -959,6 +961,20 @@ GlobalObject::setCreateArrayFromBuffer<int32_t>(Handle<JSFunction*> fun) template<> inline void +GlobalObject::setCreateArrayFromBuffer<uint64_t>(Handle<JSFunction*> fun) +{ + setCreateArrayFromBufferHelper(FROM_BUFFER_UINT64, fun); +} + +template<> +inline void +GlobalObject::setCreateArrayFromBuffer<int64_t>(Handle<JSFunction*> fun) +{ + setCreateArrayFromBufferHelper(FROM_BUFFER_INT64, fun); +} + +template<> +inline void GlobalObject::setCreateArrayFromBuffer<float>(Handle<JSFunction*> fun) { setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT32, fun); @@ -1022,6 +1038,20 @@ GlobalObject::createArrayFromBuffer<int32_t>() const template<> inline Value +GlobalObject::createArrayFromBuffer<uint64_t>() const +{ + return createArrayFromBufferHelper(FROM_BUFFER_UINT64); +} + +template<> +inline Value +GlobalObject::createArrayFromBuffer<int64_t>() const +{ + return createArrayFromBufferHelper(FROM_BUFFER_INT64); +} + +template<> +inline Value GlobalObject::createArrayFromBuffer<float>() const { return createArrayFromBufferHelper(FROM_BUFFER_FLOAT32); diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h index 69976bc462..1fa5cbd10e 100644 --- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -239,12 +239,15 @@ NativeObject::ensureDenseElements(ExclusiveContext* cx, uint32_t index, uint32_t return DenseElementResult::Success; } -inline Value -NativeObject::getDenseOrTypedArrayElement(uint32_t idx) +template <AllowGC allowGC> +inline bool +NativeObject::getDenseOrTypedArrayElement(ExclusiveContext* cx, uint32_t idx, + typename MaybeRooted<Value, allowGC>::MutableHandleType val) { if (is<TypedArrayObject>()) - return as<TypedArrayObject>().getElement(idx); - return getDenseElement(idx); + return as<TypedArrayObject>().getElement<allowGC>(cx, idx, val); + val.set(getDenseElement(idx)); + return true; } /* static */ inline NativeObject* diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index a6bb9826ee..d21f88b15e 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -20,6 +20,7 @@ #include "vm/ArrayObject-inl.h" #include "vm/EnvironmentObject-inl.h" #include "vm/Shape-inl.h" +#include "vm/TypedArrayObject.h" using namespace js; @@ -1283,8 +1284,7 @@ GetExistingPropertyValue(ExclusiveContext* cx, HandleNativeObject obj, HandleId Handle<PropertyResult> prop, MutableHandleValue vp) { if (prop.isDenseOrTypedArrayElement()) { - vp.set(obj->getDenseOrTypedArrayElement(JSID_TO_INT(id))); - return true; + return obj->getDenseOrTypedArrayElement<CanGC>(cx, JSID_TO_INT(id), vp); } if (!cx->shouldBeJSContext()) return false; @@ -1814,7 +1814,9 @@ js::NativeGetOwnPropertyDescriptor(JSContext* cx, HandleNativeObject obj, Handle desc.attributesRef() &= ~JSPROP_SHARED; if (prop.isDenseOrTypedArrayElement()) { - desc.value().set(obj->getDenseOrTypedArrayElement(JSID_TO_INT(id))); + if (!obj->getDenseOrTypedArrayElement<CanGC>(cx, JSID_TO_INT(id), desc.value())) { + return false; + } } else { RootedShape shape(cx, prop.shape()); if (!NativeGetExistingProperty(cx, obj, obj, shape, desc.value())) @@ -2110,8 +2112,7 @@ NativeGetPropertyInline(JSContext* cx, // Steps 5-8. Special case for dense elements because // GetExistingProperty doesn't support those. if (prop.isDenseOrTypedArrayElement()) { - vp.set(pobj->getDenseOrTypedArrayElement(JSID_TO_INT(id))); - return true; + return pobj->template getDenseOrTypedArrayElement<allowGC>(cx, JSID_TO_INT(id), vp); } typename MaybeRooted<Shape*, allowGC>::RootType shape(cx, prop.shape()); @@ -2365,38 +2366,6 @@ SetNonexistentProperty(JSContext* cx, HandleId id, HandleValue v, HandleValue re } /* - * Set an existing own property obj[index] that's a dense element or typed - * array element. - */ -static bool -SetDenseOrTypedArrayElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue v, - ObjectOpResult& result) -{ - if (obj->is<TypedArrayObject>()) { - double d; - if (!ToNumber(cx, v, &d)) - return false; - - // Silently do nothing for out-of-bounds sets, for consistency with - // current behavior. (ES6 currently says to throw for this in - // strict mode code, so we may eventually need to change.) - uint32_t len = obj->as<TypedArrayObject>().length(); - if (index < len) - TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, d); - return result.succeed(); - } - - if (WouldDefinePastNonwritableLength(obj, index)) - return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); - - if (!obj->maybeCopyElementsForWrite(cx)) - return false; - - obj->setDenseElementWithType(cx, index, v); - return result.succeed(); -} - -/* * Finish the assignment `receiver[id] = v` when an existing property (shape) * has been found on a native object (pobj). This implements ES6 draft rev 32 * (2015 Feb 2) 9.1.9 steps 5 and 6. @@ -2416,8 +2385,14 @@ SetExistingProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleVa return result.fail(JSMSG_READ_ONLY); // Pure optimization for the common case: - if (receiver.isObject() && pobj == &receiver.toObject()) - return SetDenseOrTypedArrayElement(cx, pobj, JSID_TO_INT(id), v, result); + if (receiver.isObject() && pobj == &receiver.toObject()) { + uint32_t index = JSID_TO_INT(id); + + if (pobj->is<TypedArrayObject>()) { + Rooted<TypedArrayObject*> tobj(cx, &pobj->as<TypedArrayObject>()); + return SetTypedArrayElement(cx, tobj, index, v, result); + } + } // Steps 5.b-f. return SetPropertyByDefining(cx, id, v, receiver, result); diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h index c5865caa03..86977d109f 100644 --- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -18,6 +18,7 @@ #include "gc/Barrier.h" #include "gc/Heap.h" #include "gc/Marking.h" +#include "js/RootingAPI.h" #include "js/Value.h" #include "vm/Shape.h" #include "vm/ShapedObject.h" @@ -1088,7 +1089,9 @@ class NativeObject : public ShapedObject static inline void removeDenseElementForSparseIndex(ExclusiveContext* cx, HandleNativeObject obj, uint32_t index); - inline Value getDenseOrTypedArrayElement(uint32_t idx); + template <AllowGC allowGC> inline bool + getDenseOrTypedArrayElement(ExclusiveContext* cx, uint32_t idx, + typename MaybeRooted<Value, allowGC>::MutableHandleType val); void copyDenseElements(uint32_t dstStart, const Value* src, uint32_t count) { MOZ_ASSERT(dstStart + count <= getDenseCapacity()); diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index 741531f015..408e346608 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -686,6 +686,8 @@ GetClassForProtoKey(JSProtoKey key) case JSProto_Float32Array: case JSProto_Float64Array: case JSProto_Uint8ClampedArray: + case JSProto_BigInt64Array: + case JSProto_BigUint64Array: return &TypedArrayObject::classes[key - JSProto_Int8Array]; case JSProto_ArrayBuffer: diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index de497d02e1..e73e55d9a0 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -44,6 +44,7 @@ #include "jit/InlinableNatives.h" #include "js/CharacterEncoding.h" #include "js/Date.h" +#include "vm/BigIntType.h" #include "vm/Compression.h" #include "vm/GeneratorObject.h" #include "vm/Interpreter.h" @@ -1182,6 +1183,18 @@ intrinsic_IsFloat32TypedArray(JSContext* cx, unsigned argc, Value* vp) } static bool +intrinsic_IsBigInt64TypedArray(JSContext* cx, unsigned argc, Value* vp) +{ + return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::BigInt64); +} + +static bool +intrinsic_IsBigUint64TypedArray(JSContext* cx, unsigned argc, Value* vp) +{ + return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::BigUint64); +} + +static bool intrinsic_TypedArrayBuffer(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -1521,6 +1534,14 @@ struct DisjointElements CopyValues(dest, src.cast<double*>(), count); return; + case Scalar::BigInt64: + CopyValues(dest, src.cast<int64_t*>(), count); + return; + + case Scalar::BigUint64: + CopyValues(dest, src.cast<uint64_t*>(), count); + return; + case Scalar::Uint8Clamped: CopyValues(dest, src.cast<uint8_clamped*>(), count); return; @@ -1579,6 +1600,16 @@ CopyToDisjointArray(TypedArrayObject* target, uint32_t targetOffset, SharedMem<v break; } + case Scalar::BigInt64: { + DisjointElements::copy(dest.cast<int64_t*>(), src, srcType, count); + break; + } + + case Scalar::BigUint64: { + DisjointElements::copy(dest.cast<uint64_t*>(), src, srcType, count); + break; + } + case Scalar::Uint8Clamped: { DisjointElements::copy(dest.cast<uint8_clamped*>(), src, srcType, count); break; @@ -2164,6 +2195,18 @@ intrinsic_PromiseResolve(JSContext* cx, unsigned argc, Value* vp) return true; } +static bool intrinsic_ToBigInt(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + BigInt* res = ToBigInt(cx, args[0]); + if (!res) { + return false; + } + args.rval().setBigInt(res); + return true; +} + // The self-hosting global isn't initialized with the normal set of builtins. // Instead, individual C++-implemented functions that're required by // self-hosted code are defined as global functions. Accessing these @@ -2399,6 +2442,8 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("IsUint32TypedArray", intrinsic_IsUint32TypedArray, 1,0), JS_FN("IsInt32TypedArray", intrinsic_IsInt32TypedArray, 1,0), JS_FN("IsFloat32TypedArray", intrinsic_IsFloat32TypedArray, 1,0), + JS_FN("IsBigInt64TypedArray", intrinsic_IsBigInt64TypedArray, 1,0), + JS_FN("IsBigUint64TypedArray", intrinsic_IsBigUint64TypedArray, 1,0), JS_INLINABLE_FN("IsTypedArray", intrinsic_IsInstanceOfBuiltin<TypedArrayObject>, 1,0, IntrinsicIsTypedArray), @@ -2591,6 +2636,8 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("CallPromiseMethodIfWrapped", CallNonGenericSelfhostedMethod<Is<PromiseObject>>, 2, 0), JS_FN("PromiseResolve", intrinsic_PromiseResolve, 2, 0), + JS_FN("ToBigInt", intrinsic_ToBigInt, 1, 0), + JS_FS_END }; diff --git a/js/src/vm/StructuredClone.cpp b/js/src/vm/StructuredClone.cpp index ac73df30e6..e99cfe8f71 100644 --- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -121,7 +121,8 @@ enum StructuredDataType : uint32_t { SCTAG_TYPED_ARRAY_V1_FLOAT32 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Float32, SCTAG_TYPED_ARRAY_V1_FLOAT64 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Float64, SCTAG_TYPED_ARRAY_V1_UINT8_CLAMPED = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint8Clamped, - SCTAG_TYPED_ARRAY_V1_MAX = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::MaxTypedArrayViewType - 1, + // BigInt64 and BigUint64 are not supported in the v1 format. + SCTAG_TYPED_ARRAY_V1_MAX = SCTAG_TYPED_ARRAY_V1_UINT8_CLAMPED, /* * Define a separate range of numbers for Transferable-only tags, since @@ -1808,7 +1809,7 @@ bool JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp, bool v1Read) { - if (arrayType > Scalar::Uint8Clamped) { + if (arrayType > (v1Read ? Scalar::Uint8Clamped : Scalar::BigUint64)) { JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA, "unhandled typed array element type"); return false; @@ -1872,6 +1873,12 @@ JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, Mut case Scalar::Uint8Clamped: obj = JS_NewUint8ClampedArrayWithBuffer(context(), buffer, byteOffset, nelems); break; + case Scalar::BigInt64: + obj = JS_NewBigInt64ArrayWithBuffer(context(), buffer, byteOffset, nelems); + break; + case Scalar::BigUint64: + obj = JS_NewBigUint64ArrayWithBuffer(context(), buffer, byteOffset, nelems); + break; default: MOZ_CRASH("Can't happen: arrayType range checked above"); } @@ -2007,6 +2014,8 @@ JSStructuredCloneReader::readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, case Scalar::Float32: return in.readArray((uint32_t*) buffer.dataPointer(), nelems); case Scalar::Float64: + case Scalar::BigInt64: + case Scalar::BigUint64: return in.readArray((uint64_t*) buffer.dataPointer(), nelems); default: MOZ_CRASH("Can't happen: arrayType range checked by caller"); diff --git a/js/src/vm/TypedArrayCommon.h b/js/src/vm/TypedArrayCommon.h index eb7b94f109..8e66587a1e 100644 --- a/js/src/vm/TypedArrayCommon.h +++ b/js/src/vm/TypedArrayCommon.h @@ -112,6 +112,20 @@ ConvertNumber<uint32_t, float>(float src) return JS::ToUint32(src); } +template <> +inline int64_t +ConvertNumber<int64_t, float>(float src) +{ + return JS::ToInt64(src); +} + +template <> +inline uint64_t +ConvertNumber<uint64_t, float>(float src) +{ + return JS::ToUint64(src); +} + template<> inline int8_t ConvertNumber<int8_t, double>(double src) { @@ -160,6 +174,20 @@ ConvertNumber<uint32_t, double>(double src) return JS::ToUint32(src); } +template <> +inline int64_t +ConvertNumber<int64_t, double>(double src) +{ + return JS::ToInt64(src); +} + +template <> +inline uint64_t +ConvertNumber<uint64_t, double>(double src) +{ + return JS::ToUint64(src); +} + template<typename To, typename From> inline To ConvertNumber(From src) @@ -178,6 +206,8 @@ template<> struct TypeIDOfType<int16_t> { static const Scalar::Type id = Scalar: template<> struct TypeIDOfType<uint16_t> { static const Scalar::Type id = Scalar::Uint16; }; template<> struct TypeIDOfType<int32_t> { static const Scalar::Type id = Scalar::Int32; }; template<> struct TypeIDOfType<uint32_t> { static const Scalar::Type id = Scalar::Uint32; }; +template<> struct TypeIDOfType<int64_t> { static const Scalar::Type id = Scalar::BigInt64; }; +template<> struct TypeIDOfType<uint64_t> { static const Scalar::Type id = Scalar::BigUint64; }; template<> struct TypeIDOfType<float> { static const Scalar::Type id = Scalar::Float32; }; template<> struct TypeIDOfType<double> { static const Scalar::Type id = Scalar::Float64; }; template<> struct TypeIDOfType<uint8_clamped> { static const Scalar::Type id = Scalar::Uint8Clamped; }; @@ -355,6 +385,18 @@ class ElementSpecific Ops::store(dest++, ConvertNumber<T>(Ops::load(src++))); break; } + case Scalar::BigInt64: { + SharedMem<int64_t*> src = data.cast<int64_t*>(); + for (uint32_t i = 0; i < count; ++i) + Ops::store(dest++, ConvertNumber<T>(Ops::load(src++))); + break; + } + case Scalar::BigUint64: { + SharedMem<uint64_t*> src = data.cast<uint64_t*>(); + for (uint32_t i = 0; i < count; ++i) + Ops::store(dest++, ConvertNumber<T>(Ops::load(src++))); + break; + } case Scalar::Float32: { SharedMem<JS_VOLATILE_ARM float*> src = data.cast<JS_VOLATILE_ARM float*>(); for (uint32_t i = 0; i < count; ++i) @@ -399,12 +441,12 @@ class ElementSpecific SharedMem<T*> dest = target->template as<TypedArrayObject>().viewDataEither().template cast<T*>() + offset; - MOZ_ASSERT(!canConvertInfallibly(MagicValue(JS_ELEMENTS_HOLE)), + MOZ_ASSERT(!canConvertInfallibly(MagicValue(JS_ELEMENTS_HOLE), target->type()), "the following loop must abort on holes"); const Value* srcValues = source->as<NativeObject>().getDenseElements(); for (; i < bound; i++) { - if (!canConvertInfallibly(srcValues[i])) + if (!canConvertInfallibly(srcValues[i], target->type())) break; Ops::store(dest + i, infallibleValueToNative(srcValues[i])); } @@ -459,7 +501,7 @@ class ElementSpecific const Value* srcValues = source->getDenseElements(); for (; i < len; i++) { - if (!canConvertInfallibly(srcValues[i])) + if (!canConvertInfallibly(srcValues[i], target->type())) break; Ops::store(dest + i, infallibleValueToNative(srcValues[i])); } @@ -568,6 +610,18 @@ class ElementSpecific Ops::store(dest++, ConvertNumber<T>(*src++)); break; } + case Scalar::BigInt64: { + int64_t* src = static_cast<int64_t*>(data); + for (uint32_t i = 0; i < len; ++i) + Ops::store(dest++, ConvertNumber<T>(*src++)); + break; + } + case Scalar::BigUint64: { + uint64_t* src = static_cast<uint64_t*>(data); + for (uint32_t i = 0; i < len; ++i) + Ops::store(dest++, ConvertNumber<T>(*src++)); + break; + } case Scalar::Float32: { float* src = static_cast<float*>(data); for (uint32_t i = 0; i < len; ++i) @@ -589,8 +643,11 @@ class ElementSpecific } static bool - canConvertInfallibly(const Value& v) + canConvertInfallibly(const Value& v, Scalar::Type type) { + if (type == Scalar::BigInt64 || type == Scalar::BigUint64) { + return false; + } return v.isNumber() || v.isBoolean() || v.isNull() || v.isUndefined(); } @@ -615,11 +672,21 @@ class ElementSpecific { MOZ_ASSERT(!v.isMagic()); - if (MOZ_LIKELY(canConvertInfallibly(v))) { + if (MOZ_LIKELY(canConvertInfallibly(v, TypeIDOfType<T>::id))) { *result = infallibleValueToNative(v); return true; } + if (std::is_same<T, int64_t>::value) { + JS_TRY_VAR_OR_RETURN_FALSE(cx, *result, ToBigInt64(cx, v)); + return true; + } + + if (std::is_same<T, uint64_t>::value) { + JS_TRY_VAR_OR_RETURN_FALSE(cx, *result, ToBigUint64(cx, v)); + return true; + } + double d; MOZ_ASSERT(v.isString() || v.isObject() || v.isSymbol()); if (!(v.isString() ? StringToNumber(cx, v.toString(), &d) : ToNumber(cx, v, &d))) @@ -668,6 +735,8 @@ class TypedArrayMethods typedef typename SomeTypedArray::template OfType<uint16_t>::Type Uint16ArrayType; typedef typename SomeTypedArray::template OfType<int32_t>::Type Int32ArrayType; typedef typename SomeTypedArray::template OfType<uint32_t>::Type Uint32ArrayType; + typedef typename SomeTypedArray::template OfType<int64_t>::Type BigInt64ArrayType; + typedef typename SomeTypedArray::template OfType<uint64_t>::Type BigUint64ArrayType; typedef typename SomeTypedArray::template OfType<float>::Type Float32ArrayType; typedef typename SomeTypedArray::template OfType<double>::Type Float64ArrayType; typedef typename SomeTypedArray::template OfType<uint8_clamped>::Type Uint8ClampedArrayType; @@ -759,6 +828,14 @@ class TypedArrayMethods if (isShared) return ElementSpecific<Uint32ArrayType, SharedOps>::setFromTypedArray(cx, target, source, offset); return ElementSpecific<Uint32ArrayType, UnsharedOps>::setFromTypedArray(cx, target, source, offset); + case Scalar::BigInt64: + if (isShared) + return ElementSpecific<BigInt64ArrayType, SharedOps>::setFromTypedArray(cx, target, source, offset); + return ElementSpecific<BigInt64ArrayType, UnsharedOps>::setFromTypedArray(cx, target, source, offset); + case Scalar::BigUint64: + if (isShared) + return ElementSpecific<BigUint64ArrayType, SharedOps>::setFromTypedArray(cx, target, source, offset); + return ElementSpecific<BigUint64ArrayType, UnsharedOps>::setFromTypedArray(cx, target, source, offset); case Scalar::Float32: if (isShared) return ElementSpecific<Float32ArrayType, SharedOps>::setFromTypedArray(cx, target, source, offset); @@ -816,6 +893,14 @@ class TypedArrayMethods if (isShared) return ElementSpecific<Uint32ArrayType, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset); return ElementSpecific<Uint32ArrayType, UnsharedOps>::setFromNonTypedArray(cx, target, source, len, offset); + case Scalar::BigInt64: + if (isShared) + return ElementSpecific<BigInt64ArrayType, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset); + return ElementSpecific<BigInt64ArrayType, UnsharedOps>::setFromNonTypedArray(cx, target, source, len, offset); + case Scalar::BigUint64: + if (isShared) + return ElementSpecific<BigUint64ArrayType, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset); + return ElementSpecific<BigUint64ArrayType, UnsharedOps>::setFromNonTypedArray(cx, target, source, len, offset); case Scalar::Float32: if (isShared) return ElementSpecific<Float32ArrayType, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset); @@ -870,6 +955,14 @@ class TypedArrayMethods if (isShared) return ElementSpecific<Uint32ArrayType, SharedOps>::initFromIterablePackedArray(cx, target, source); return ElementSpecific<Uint32ArrayType, UnsharedOps>::initFromIterablePackedArray(cx, target, source); + case Scalar::BigInt64: + if (isShared) + return ElementSpecific<BigInt64ArrayType, SharedOps>::initFromIterablePackedArray(cx, target, source); + return ElementSpecific<BigInt64ArrayType, UnsharedOps>::initFromIterablePackedArray(cx, target, source); + case Scalar::BigUint64: + if (isShared) + return ElementSpecific<BigUint64ArrayType, SharedOps>::initFromIterablePackedArray(cx, target, source); + return ElementSpecific<BigUint64ArrayType, UnsharedOps>::initFromIterablePackedArray(cx, target, source); case Scalar::Float32: if (isShared) return ElementSpecific<Float32ArrayType, SharedOps>::initFromIterablePackedArray(cx, target, source); diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index ac93ec9b14..28e4090eb8 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -65,6 +65,33 @@ using JS::ToUint32; * the subclasses. */ +bool TypedArrayObject::convertForSideEffect(JSContext* cx, HandleValue v) const +{ + switch (type()) { + case Scalar::BigInt64: + case Scalar::BigUint64: { + return ToBigInt(cx, v) != nullptr; + } + case Scalar::Int8: + case Scalar::Uint8: + case Scalar::Int16: + case Scalar::Uint16: + case Scalar::Int32: + case Scalar::Uint32: + case Scalar::Float32: + case Scalar::Float64: + case Scalar::Uint8Clamped: { + double ignore; + return ToNumber(cx, v, &ignore); + } + case Scalar::MaxTypedArrayViewType: + case Scalar::Int64: + MOZ_CRASH("Unsupported TypedArray type"); + } + MOZ_ASSERT_UNREACHABLE("Invalid scalar type"); + return false; +} + /* static */ int TypedArrayObject::lengthOffset() { @@ -414,30 +441,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject return v.isObject() && v.toObject().hasClass(instanceClass()); } - static void - setIndexValue(TypedArrayObject& tarray, uint32_t index, double d) - { - // If the array is an integer array, we only handle up to - // 32-bit ints from this point on. if we want to handle - // 64-bit ints, we'll need some changes. - - // Assign based on characteristics of the destination type - if (ArrayTypeIsFloatingPoint()) { - setIndex(tarray, index, NativeType(d)); - } else if (ArrayTypeIsUnsigned()) { - MOZ_ASSERT(sizeof(NativeType) <= 4); - uint32_t n = ToUint32(d); - setIndex(tarray, index, NativeType(n)); - } else if (ArrayTypeID() == Scalar::Uint8Clamped) { - // The uint8_clamped type has a special rounding converter - // for doubles. - setIndex(tarray, index, NativeType(d)); - } else { - MOZ_ASSERT(sizeof(NativeType) <= 4); - int32_t n = ToInt32(d); - setIndex(tarray, index, NativeType(n)); - } - } + static bool convertValue(JSContext* cx, HandleValue v, NativeType* result); static TypedArrayObject* makeProtoInstance(JSContext* cx, HandleObject proto, AllocKind allocKind) @@ -1001,9 +1005,128 @@ class TypedArrayObjectTemplate : public TypedArrayObject jit::AtomicOperations::storeSafeWhenRacy(tarray.viewDataEither().cast<NativeType*>() + index, val); } - static Value getIndexValue(JSObject* tarray, uint32_t index); + static bool getElement(ExclusiveContext* cx, TypedArrayObject* tarray, uint32_t index, MutableHandleValue val); + static bool getElementPure(TypedArrayObject* tarray, uint32_t index, Value* vp); + + static bool setElement(JSContext* cx, Handle<TypedArrayObject*> obj, uint64_t index, HandleValue v, + ObjectOpResult& result); + static bool defineElement(JSContext* cx, HandleObject obj, uint64_t index, HandleValue v, + ObjectOpResult& result); }; +template <typename NativeType> +bool TypedArrayObjectTemplate<NativeType>::convertValue(JSContext* cx, HandleValue v, NativeType* result) +{ + double d; + if (!ToNumber(cx, v, &d)) { + return false; + } + +#ifdef JS_MORE_DETERMINISTIC + // See the comment in ElementSpecific::doubleToNative. + d = JS::CanonicalizeNaN(d); +#endif + + // Assign based on characteristics of the destination type + if (ArrayTypeIsFloatingPoint()) { + *result = NativeType(d); + } else if (ArrayTypeIsUnsigned()) { + MOZ_ASSERT(sizeof(NativeType) <= 4); + uint32_t n = ToUint32(d); + *result = NativeType(n); + } else if (ArrayTypeID() == Scalar::Uint8Clamped) { + // The uint8_clamped type has a special rounding converter + // for doubles. + *result = NativeType(d); + } else { + MOZ_ASSERT(sizeof(NativeType) <= 4); + int32_t n = ToInt32(d); + *result = NativeType(n); + } + return true; +} + +template <> +bool TypedArrayObjectTemplate<int64_t>::convertValue(JSContext* cx, HandleValue v, int64_t* result) +{ + JS_TRY_VAR_OR_RETURN_FALSE(cx, *result, ToBigInt64(cx, v)); + return true; +} + +template <> +bool TypedArrayObjectTemplate<uint64_t>::convertValue(JSContext* cx, HandleValue v, uint64_t* result) +{ + JS_TRY_VAR_OR_RETURN_FALSE(cx, *result, ToBigUint64(cx, v)); + return true; +} + +// https://tc39.github.io/proposal-bigint/#sec-integerindexedelementset +// 7.8 IntegerIndexedElementSet ( O, index, value ) +template <typename NativeType> +/* static */ bool TypedArrayObjectTemplate<NativeType>::setElement( + JSContext* cx, Handle<TypedArrayObject*> obj, uint64_t index, HandleValue v, + ObjectOpResult& result) +{ + // Steps 1-2 are enforced by the caller. + + // Steps 3-6. + NativeType nativeValue; + if (!convertValue(cx, v, &nativeValue)) { + return false; + } + + // Step 8. + if (obj->hasDetachedBuffer()) { + return result.failSoft(JSMSG_TYPED_ARRAY_DETACHED); + } + + // Steps 9-10 are enforced by the caller. + + // Step 11. + uint32_t length = obj->length(); + + // Step 12. + if (index >= length) { + return result.failSoft(JSMSG_BAD_INDEX); + } + + // Steps 7, 13-16. + TypedArrayObjectTemplate<NativeType>::setIndex(*obj, index, nativeValue); + + // Step 17. + return result.succeed(); +} + +// Version of IntegerIndexedElementSet with no length check, used in +// [[DefineOwnProperty]] +template <typename NativeType> +/* static */ bool TypedArrayObjectTemplate<NativeType>::defineElement( + JSContext* cx, HandleObject obj, uint64_t index, HandleValue v, + ObjectOpResult& result) +{ + // Steps 1-2 are enforced by the caller. + + // Steps 3-6. + NativeType nativeValue; + if (!convertValue(cx, v, &nativeValue)) { + return false; + } + + // Step 8. + if (obj->as<TypedArrayObject>().hasDetachedBuffer()) { + return result.fail(JSMSG_TYPED_ARRAY_DETACHED); + } + + // Steps 9-12 are enforced by the caller. + + // Steps 7, 13-16. + TypedArrayObjectTemplate<NativeType>::setIndex(obj->as<TypedArrayObject>(), + index, nativeValue); + + // Step 17. + return result.succeed(); +} + #define CREATE_TYPE_FOR_TYPED_ARRAY(T, N) \ typedef TypedArrayObjectTemplate<T> N##Array; JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPE_FOR_TYPED_ARRAY) @@ -1240,6 +1363,13 @@ TypedArrayObjectTemplate<T>::fromTypedArray(JSContext* cx, HandleObject other, b } } + // BigInt proposal 7.24, step 19.c. + if (Scalar::isBigIntType(ArrayTypeID()) != + Scalar::isBigIntType(srcArray->type())) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_BIGINT); + return nullptr; + } + // Steps 3-4 (remaining part), 18-21. Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, elementLength, proto)); if (!obj) @@ -1584,40 +1714,43 @@ ArrayBufferObject::createTypedArrayFromBuffer(JSContext* cx, unsigned argc, Valu return CallNonGenericMethod<IsAnyArrayBuffer, createTypedArrayFromBufferImpl<T> >(cx, args); } -// this default implementation is only valid for integer types -// less than 32-bits in size. +// This default implementation is only valid for integer types less +// than 32-bits in size. template<typename NativeType> -Value -TypedArrayObjectTemplate<NativeType>::getIndexValue(JSObject* tarray, uint32_t index) +bool +TypedArrayObjectTemplate<NativeType>::getElementPure(TypedArrayObject* tarray, uint32_t index, Value* vp) { static_assert(sizeof(NativeType) < 4, "this method must only handle NativeType values that are " "always exact int32_t values"); - return Int32Value(getIndex(tarray, index)); + *vp = Int32Value(getIndex(tarray, index)); + return true; } namespace { -// and we need to specialize for 32-bit integers and floats +// We need to specialize for floats and other integer types. template<> -Value -TypedArrayObjectTemplate<int32_t>::getIndexValue(JSObject* tarray, uint32_t index) +bool +TypedArrayObjectTemplate<int32_t>::getElementPure(TypedArrayObject* tarray, uint32_t index, Value* vp) { - return Int32Value(getIndex(tarray, index)); + *vp = Int32Value(getIndex(tarray, index)); + return true; } template<> -Value -TypedArrayObjectTemplate<uint32_t>::getIndexValue(JSObject* tarray, uint32_t index) +bool +TypedArrayObjectTemplate<uint32_t>::getElementPure(TypedArrayObject* tarray, uint32_t index, Value* vp) { uint32_t val = getIndex(tarray, index); - return NumberValue(val); + *vp = NumberValue(val); + return true; } template<> -Value -TypedArrayObjectTemplate<float>::getIndexValue(JSObject* tarray, uint32_t index) +bool +TypedArrayObjectTemplate<float>::getElementPure(TypedArrayObject* tarray, uint32_t index, Value* vp) { float val = getIndex(tarray, index); double dval = val; @@ -1632,12 +1765,13 @@ TypedArrayObjectTemplate<float>::getIndexValue(JSObject* tarray, uint32_t index) * This could be removed for platforms/compilers known to convert a 32-bit * non-canonical nan to a 64-bit canonical nan. */ - return DoubleValue(CanonicalizeNaN(dval)); + *vp = DoubleValue(CanonicalizeNaN(dval)); + return true; } template<> -Value -TypedArrayObjectTemplate<double>::getIndexValue(JSObject* tarray, uint32_t index) +bool +TypedArrayObjectTemplate<double>::getElementPure(TypedArrayObject* tarray, uint32_t index, Value* vp) { double val = getIndex(tarray, index); @@ -1648,9 +1782,60 @@ TypedArrayObjectTemplate<double>::getIndexValue(JSObject* tarray, uint32_t index * confuse the engine into interpreting a double-typed jsval as an * object-typed jsval. */ - return DoubleValue(CanonicalizeNaN(val)); + *vp = DoubleValue(CanonicalizeNaN(val)); + return true; +} + +template <> +bool +TypedArrayObjectTemplate<int64_t>::getElementPure(TypedArrayObject* tarray, uint32_t index, Value* vp) +{ + return false; } +template <> +bool +TypedArrayObjectTemplate<uint64_t>::getElementPure(TypedArrayObject* tarray, uint32_t index, Value* vp) +{ + return false; +} +} /* anonymous namespace */ + +namespace { + +template <typename NativeType> +bool TypedArrayObjectTemplate<NativeType>::getElement(ExclusiveContext* cx, TypedArrayObject* tarray, uint32_t index, + MutableHandleValue val) +{ + MOZ_ALWAYS_TRUE(getElementPure(tarray, index, val.address())); + return true; +} + +template <> +bool TypedArrayObjectTemplate<int64_t>::getElement(ExclusiveContext* cx, TypedArrayObject* tarray, uint32_t index, + MutableHandleValue val) +{ + int64_t n = getIndex(tarray, index); + BigInt* res = BigInt::createFromInt64(cx, n); + if (!res) { + return false; + } + val.setBigInt(res); + return true; +} + +template <> +bool TypedArrayObjectTemplate<uint64_t>::getElement(ExclusiveContext* cx, TypedArrayObject* tarray, uint32_t index, + MutableHandleValue val) +{ + uint64_t n = getIndex(tarray, index); + BigInt* res = BigInt::createFromUint64(cx, n); + if (!res) { + return false; + } + val.setBigInt(res); + return true; +} } /* anonymous namespace */ static NewObjectKind @@ -2544,28 +2729,17 @@ DataViewObject::fun_setFloat64(JSContext* cx, unsigned argc, Value* vp) return CallNonGenericMethod<is, setFloat64Impl>(cx, args); } -Value -TypedArrayObject::getElement(uint32_t index) +namespace js { + +template <> +bool TypedArrayObject::getElement<CanGC>(ExclusiveContext* cx, uint32_t index, MutableHandleValue val) { switch (type()) { - case Scalar::Int8: - return Int8Array::getIndexValue(this, index); - case Scalar::Uint8: - return Uint8Array::getIndexValue(this, index); - case Scalar::Int16: - return Int16Array::getIndexValue(this, index); - case Scalar::Uint16: - return Uint16Array::getIndexValue(this, index); - case Scalar::Int32: - return Int32Array::getIndexValue(this, index); - case Scalar::Uint32: - return Uint32Array::getIndexValue(this, index); - case Scalar::Float32: - return Float32Array::getIndexValue(this, index); - case Scalar::Float64: - return Float64Array::getIndexValue(this, index); - case Scalar::Uint8Clamped: - return Uint8ClampedArray::getIndexValue(this, index); +#define GET_ELEMENT(T, N) \ + case Scalar::N: \ + return N##Array::getElement(cx, this, index, val); + JS_FOR_EACH_TYPED_ARRAY(GET_ELEMENT) +#undef GET_ELEMENT case Scalar::Int64: case Scalar::Float32x4: case Scalar::Int8x16: @@ -2578,44 +2752,23 @@ TypedArrayObject::getElement(uint32_t index) MOZ_CRASH("Unknown TypedArray type"); } -void -TypedArrayObject::setElement(TypedArrayObject& obj, uint32_t index, double d) -{ - MOZ_ASSERT(index < obj.length()); +template <> +bool TypedArrayObject::getElement<NoGC>( + ExclusiveContext* cx, uint32_t index, + typename MaybeRooted<Value, NoGC>::MutableHandleType vp) { + return getElementPure(index, vp.address()); +} -#ifdef JS_MORE_DETERMINISTIC - // See the comment in ElementSpecific::doubleToNative. - d = JS::CanonicalizeNaN(d); -#endif +} // namespace js - switch (obj.type()) { - case Scalar::Int8: - Int8Array::setIndexValue(obj, index, d); - return; - case Scalar::Uint8: - Uint8Array::setIndexValue(obj, index, d); - return; - case Scalar::Uint8Clamped: - Uint8ClampedArray::setIndexValue(obj, index, d); - return; - case Scalar::Int16: - Int16Array::setIndexValue(obj, index, d); - return; - case Scalar::Uint16: - Uint16Array::setIndexValue(obj, index, d); - return; - case Scalar::Int32: - Int32Array::setIndexValue(obj, index, d); - return; - case Scalar::Uint32: - Uint32Array::setIndexValue(obj, index, d); - return; - case Scalar::Float32: - Float32Array::setIndexValue(obj, index, d); - return; - case Scalar::Float64: - Float64Array::setIndexValue(obj, index, d); - return; +bool TypedArrayObject::getElementPure(uint32_t index, Value* vp) +{ + switch (type()) { +#define GET_ELEMENT_PURE(T, N) \ + case Scalar::N: \ + return N##Array::getElementPure(this, index, vp); + JS_FOR_EACH_TYPED_ARRAY(GET_ELEMENT_PURE) +#undef GET_ELEMENT case Scalar::Int64: case Scalar::Float32x4: case Scalar::Int8x16: @@ -2628,6 +2781,38 @@ TypedArrayObject::setElement(TypedArrayObject& obj, uint32_t index, double d) MOZ_CRASH("Unknown TypedArray type"); } +/* static */ +bool TypedArrayObject::getElements(JSContext* cx, Handle<TypedArrayObject*> tarray, Value* vp) +{ + uint32_t length = tarray->length(); + MOZ_ASSERT_IF(length > 0, !tarray->hasDetachedBuffer()); + + switch (tarray->type()) { +#define GET_ELEMENTS(T, N) \ + case Scalar::N: \ + for (uint32_t i = 0; i < length; ++i, ++vp) { \ + if (!N##Array::getElement(cx, tarray, i, \ + MutableHandleValue::fromMarkedLocation(vp))) { \ + return false; \ + } \ + } \ + return true; + JS_FOR_EACH_TYPED_ARRAY(GET_ELEMENTS) +#undef GET_ELEMENTS + default: + MOZ_CRASH("Unknown TypedArray type"); + case Scalar::MaxTypedArrayViewType: + case Scalar::Int64: + case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: + case Scalar::Int32x4: + break; + } + + MOZ_CRASH("Unknown TypedArray type"); +} + /*** *** JS impl ***/ @@ -2680,6 +2865,8 @@ IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int32, int32_t) IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint32, uint32_t) IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float32, float) IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double) +IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(BigInt64, int64_t) +IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(BigUint64, uint64_t) #define IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Name, ExternalType, InternalType) \ JS_FRIEND_API(JSObject*) JS_GetObjectAs ## Name ## Array(JSObject* obj, \ @@ -2711,6 +2898,8 @@ IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int32, int32_t, int32_t) IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint32, uint32_t, uint32_t) IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float32, float, float) IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double) +IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(BigInt64, int64_t, int64_t) +IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(BigUint64, uint32_t, uint64_t) static const ClassOps TypedArrayClassOps = { nullptr, /* addProperty */ @@ -2748,7 +2937,9 @@ static const JSPropertySpec static_prototype_properties[Scalar::MaxTypedArrayVie IMPL_TYPED_ARRAY_PROPERTIES(Uint32), IMPL_TYPED_ARRAY_PROPERTIES(Float32), IMPL_TYPED_ARRAY_PROPERTIES(Float64), - IMPL_TYPED_ARRAY_PROPERTIES(Uint8Clamped) + IMPL_TYPED_ARRAY_PROPERTIES(Uint8Clamped), + IMPL_TYPED_ARRAY_PROPERTIES(BigInt64), + IMPL_TYPED_ARRAY_PROPERTIES(BigUint64) }; #define IMPL_TYPED_ARRAY_CLASS_SPEC(_type) \ @@ -2772,7 +2963,9 @@ static const ClassSpec TypedArrayObjectClassSpecs[Scalar::MaxTypedArrayViewType] IMPL_TYPED_ARRAY_CLASS_SPEC(Uint32), IMPL_TYPED_ARRAY_CLASS_SPEC(Float32), IMPL_TYPED_ARRAY_CLASS_SPEC(Float64), - IMPL_TYPED_ARRAY_CLASS_SPEC(Uint8Clamped) + IMPL_TYPED_ARRAY_CLASS_SPEC(Uint8Clamped), + IMPL_TYPED_ARRAY_CLASS_SPEC(BigInt64), + IMPL_TYPED_ARRAY_CLASS_SPEC(BigUint64) }; #define IMPL_TYPED_ARRAY_CLASS(_type) \ @@ -2798,7 +2991,9 @@ const Class TypedArrayObject::classes[Scalar::MaxTypedArrayViewType] = { IMPL_TYPED_ARRAY_CLASS(Uint32), IMPL_TYPED_ARRAY_CLASS(Float32), IMPL_TYPED_ARRAY_CLASS(Float64), - IMPL_TYPED_ARRAY_CLASS(Uint8Clamped) + IMPL_TYPED_ARRAY_CLASS(Uint8Clamped), + IMPL_TYPED_ARRAY_CLASS(BigInt64), + IMPL_TYPED_ARRAY_CLASS(BigUint64) }; #define IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(_type) \ @@ -2822,7 +3017,9 @@ static const ClassSpec TypedArrayObjectProtoClassSpecs[Scalar::MaxTypedArrayView IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint32), IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Float32), IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Float64), - IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint8Clamped) + IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint8Clamped), + IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(BigInt64), + IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(BigUint64) }; // The various typed array prototypes are supposed to 1) be normal objects, @@ -2856,7 +3053,9 @@ const Class TypedArrayObject::protoClasses[Scalar::MaxTypedArrayViewType] = { IMPL_TYPED_ARRAY_PROTO_CLASS(Uint32), IMPL_TYPED_ARRAY_PROTO_CLASS(Float32), IMPL_TYPED_ARRAY_PROTO_CLASS(Float64), - IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8Clamped) + IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8Clamped), + IMPL_TYPED_ARRAY_PROTO_CLASS(BigInt64), + IMPL_TYPED_ARRAY_PROTO_CLASS(BigUint64) }; /* static */ bool @@ -3112,6 +3311,25 @@ js::StringIsTypedArrayIndex(const char16_t* s, size_t length, uint64_t* indexp); template bool js::StringIsTypedArrayIndex(const Latin1Char* s, size_t length, uint64_t* indexp); +bool js::SetTypedArrayElement(JSContext* cx, Handle<TypedArrayObject*> obj, + uint64_t index, HandleValue v, ObjectOpResult& result) +{ + TypedArrayObject* tobj = &obj->as<TypedArrayObject>(); + + switch (tobj->type()) { +#define SET_TYPED_ARRAY_ELEMENT(T, N) \ + case Scalar::N: \ + return TypedArrayObjectTemplate<T>::setElement(cx, obj, index, v, result); + JS_FOR_EACH_TYPED_ARRAY(SET_TYPED_ARRAY_ELEMENT) +#undef SET_TYPED_ARRAY_ELEMENT + case Scalar::MaxTypedArrayViewType: + case Scalar::Int64: + break; + } + + MOZ_CRASH("Unsupported TypedArray type"); +} + /* ES6 draft rev 34 (2015 Feb 20) 9.4.5.3 [[DefineOwnProperty]] step 3.c. */ bool js::DefineTypedArrayElement(JSContext* cx, HandleObject obj, uint64_t index, @@ -3147,22 +3365,18 @@ js::DefineTypedArrayElement(JSContext* cx, HandleObject obj, uint64_t index, // Step x. if (desc.hasValue()) { - // The following step numbers refer to 9.4.5.9 - // IntegerIndexedElementSet. - - // Steps 1-2 are enforced by the caller. - - // Step 3. - double numValue; - if (!ToNumber(cx, desc.value(), &numValue)) - return false; - - // Steps 4-5, 8-9. - if (obj->as<TypedArrayObject>().hasDetachedBuffer()) - return result.fail(JSMSG_TYPED_ARRAY_DETACHED); - - // Steps 10-16. - TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, numValue); + TypedArrayObject* tobj = &obj->as<TypedArrayObject>(); + switch (tobj->type()) { +#define DEFINE_TYPED_ARRAY_ELEMENT(T, N) \ + case Scalar::N: \ + return TypedArrayObjectTemplate<T>::defineElement(cx, obj, index, \ + desc.value(), result); + JS_FOR_EACH_TYPED_ARRAY(DEFINE_TYPED_ARRAY_ELEMENT) +#undef DEFINE_TYPED_ARRAY_ELEMENT + case Scalar::MaxTypedArrayViewType: + case Scalar::Int64: + break; + } } // Step xii. diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index 196d347075..06aaea0617 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -24,7 +24,9 @@ macro(uint32_t, Uint32) \ macro(float, Float32) \ macro(double, Float64) \ - macro(uint8_clamped, Uint8Clamped) + macro(uint8_clamped, Uint8Clamped) \ + macro(int64_t, BigInt64) \ + macro(uint64_t, BigUint64) typedef struct JSProperty JSProperty; @@ -183,8 +185,12 @@ class TypedArrayObject : public NativeObject void assertZeroLengthArrayData() const {}; #endif - Value getElement(uint32_t index); - static void setElement(TypedArrayObject& obj, uint32_t index, double d); + template <AllowGC allowGC> + bool getElement(ExclusiveContext* cx, uint32_t index, + typename MaybeRooted<Value, allowGC>::MutableHandleType val); + bool getElementPure(uint32_t index, Value* vp); + + static bool getElements(JSContext* cx, Handle<TypedArrayObject*> tarray, Value* vp); void notifyBufferDetached(JSContext* cx, void* newData); @@ -306,6 +312,8 @@ class TypedArrayObject : public NativeObject static bool is(HandleValue v); static bool set(JSContext* cx, unsigned argc, Value* vp); + + bool convertForSideEffect(JSContext* cx, HandleValue v) const; }; MOZ_MUST_USE bool TypedArray_bufferGetter(JSContext* cx, unsigned argc, Value* vp); @@ -373,6 +381,10 @@ IsTypedArrayIndex(jsid id, uint64_t* indexp) return StringIsTypedArrayIndex(s, length, indexp); } +bool SetTypedArrayElement(JSContext* cx, Handle<TypedArrayObject*> obj, + uint64_t index, HandleValue v, + ObjectOpResult& result); + /* * Implements [[DefineOwnProperty]] for TypedArrays when the property * key is a TypedArray index. @@ -396,6 +408,8 @@ TypedArrayShift(Scalar::Type viewType) case Scalar::Uint32: case Scalar::Float32: return 2; + case Scalar::BigInt64: + case Scalar::BigUint64: case Scalar::Int64: case Scalar::Float64: return 3; |