From 0d8a96051b7cc45d3a66c68cdded5d1c4d52787e Mon Sep 17 00:00:00 2001 From: lijiamin2019 Date: Sun, 7 Apr 2024 22:06:42 +0800 Subject: [PATCH] support sendable array buffer Signed-off-by: lijiamin2019 Change-Id: I6abfec5cc725e3ec8e2b44804acd9b5b6b43b08a --- BUILD.gn | 2 + ecmascript/base/typed_array_helper.cpp | 176 +++- ecmascript/base/typed_array_helper.h | 5 + ecmascript/builtins/builtins.cpp | 5 + ecmascript/builtins/builtins.h | 4 + .../builtins_sendable_arraybuffer.cpp | 956 ++++++++++++++++++ .../builtins/builtins_sendable_arraybuffer.h | 197 ++++ ecmascript/builtins/builtins_shared_array.cpp | 2 + .../builtins/builtins_shared_typedarray.cpp | 22 +- ecmascript/builtins/shared_builtins.cpp | 125 +++ ecmascript/dfx/hprof/heap_snapshot.cpp | 2 + ecmascript/dump.cpp | 25 + ecmascript/global_env.h | 4 +- ecmascript/global_env_fields.h | 4 +- ecmascript/js_hclass.cpp | 1 + ecmascript/js_hclass.h | 6 + ecmascript/js_serializer.cpp | 1 + ecmascript/js_tagged_value-inl.h | 5 + ecmascript/js_tagged_value.h | 1 + ecmascript/js_typed_array.cpp | 82 +- ecmascript/mem/object_xray.h | 4 + ecmascript/object_factory.cpp | 40 + ecmascript/object_factory.h | 2 + ecmascript/runtime_call_id.h | 9 + .../js_sendable_arraybuffer.cpp | 82 ++ .../shared_objects/js_sendable_arraybuffer.h | 58 ++ ecmascript/tests/dump_test.cpp | 6 + 27 files changed, 1785 insertions(+), 41 deletions(-) create mode 100644 ecmascript/builtins/builtins_sendable_arraybuffer.cpp create mode 100644 ecmascript/builtins/builtins_sendable_arraybuffer.h create mode 100644 ecmascript/shared_objects/js_sendable_arraybuffer.cpp create mode 100644 ecmascript/shared_objects/js_sendable_arraybuffer.h diff --git a/BUILD.gn b/BUILD.gn index cc4e128e59..2e643f4d34 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -574,6 +574,7 @@ ecma_source = [ "ecmascript/builtins/builtins_ark_tools.cpp", "ecmascript/builtins/builtins_array.cpp", "ecmascript/builtins/builtins_shared_array.cpp", + "ecmascript/builtins/builtins_sendable_arraybuffer.cpp", "ecmascript/builtins/builtins_arraybuffer.cpp", "ecmascript/builtins/builtins_async_from_sync_iterator.cpp", "ecmascript/builtins/builtins_async_function.cpp", @@ -845,6 +846,7 @@ ecma_source = [ "ecmascript/shared_objects/js_shared_map_iterator.cpp", "ecmascript/shared_objects/js_shared_set.cpp", "ecmascript/shared_objects/js_shared_set_iterator.cpp", + "ecmascript/shared_objects/js_sendable_arraybuffer.cpp", "ecmascript/stackmap/ark_stackmap_builder.cpp", "ecmascript/stackmap/ark_stackmap_parser.cpp", "ecmascript/stackmap/litecg/litecg_stackmap_type.cpp", diff --git a/ecmascript/base/typed_array_helper.cpp b/ecmascript/base/typed_array_helper.cpp index c75dfe2329..d427d849c2 100644 --- a/ecmascript/base/typed_array_helper.cpp +++ b/ecmascript/base/typed_array_helper.cpp @@ -20,6 +20,7 @@ #include "ecmascript/base/error_type.h" #include "ecmascript/base/typed_array_helper-inl.h" #include "ecmascript/builtins/builtins_arraybuffer.h" +#include "ecmascript/builtins/builtins_sendable_arraybuffer.h" #include "ecmascript/ecma_macros.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/global_env.h" @@ -35,6 +36,7 @@ #include "ecmascript/object_factory.h" #include "ecmascript/property_detector-inl.h" #include "ecmascript/shared_objects/js_shared_typed_array.h" +#include "ecmascript/shared_objects/js_sendable_arraybuffer.h" namespace panda::ecmascript::base { using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer; @@ -122,13 +124,13 @@ JSTaggedValue TypedArrayHelper::SharedTypedArrayConstructor(EcmaRuntimeCallInfo if (firstArg->IsTypedArray() || firstArg->IsSharedTypedArray()) { return TypedArrayHelper::CreateFromTypedArray(argv, obj, arrayType); } - if (firstArg->IsArrayBuffer() || firstArg->IsSharedArrayBuffer()) { - return TypedArrayHelper::CreateFromArrayBuffer(argv, obj, arrayType); + if (firstArg->IsArrayBuffer() || firstArg->IsSharedArrayBuffer() || firstArg->IsSendableArrayBuffer()) { + return TypedArrayHelper::CreateFromSendableArrayBuffer(argv, obj, arrayType); } if (firstArg->IsStableJSArray(thread)) { return TypedArrayHelper::FastCopyElementFromArray(argv, obj, arrayType); } - return TypedArrayHelper::CreateFromOrdinaryObject(argv, obj, arrayType); + return TypedArrayHelper::CreateFromSendableOrdinaryObject(argv, obj, arrayType); } JSTaggedValue TypedArrayHelper::FastCopyElementFromArray(EcmaRuntimeCallInfo *argv, const JSHandle &obj, @@ -248,6 +250,100 @@ JSTaggedValue TypedArrayHelper::CreateFromOrdinaryObject(EcmaRuntimeCallInfo *ar return obj.GetTaggedValue(); } +// es11 22.2.4.4 TypedArray ( object ) +JSTaggedValue TypedArrayHelper::CreateFromSendableOrdinaryObject(EcmaRuntimeCallInfo *argv, + const JSHandle &obj, + const DataViewType arrayType) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + JSHandle objectArg = BuiltinsBase::GetCallArg(argv, 0); + JSHandle object(objectArg); + // 5. Let usingIterator be ? GetMethod(object, @@iterator). + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle usingIterator = + JSObject::GetMethod(thread, JSHandle::Cast(object), iteratorSymbol); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. If usingIterator is not undefined, then + if (!usingIterator->IsUndefined()) { + CVector> vec; + // a. Let values be ? IterableToList(object, usingIterator). + // b. Let len be the number of elements in values. + // c. Perform ? AllocateTypedArrayBuffer(O, len). + JSHandle iterator = JSIterator::GetIterator(thread, objectArg, usingIterator); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle next(thread, JSTaggedValue::True()); + while (!next->IsFalse()) { + next = JSIterator::IteratorStep(thread, iterator); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!next->IsFalse()) { + JSHandle nextValue = JSIterator::IteratorValue(thread, next); + vec.push_back(nextValue); + } + } + uint32_t len = static_cast(vec.size()); + TypedArrayHelper::AllocateSharedTypedArrayBuffer(thread, obj, len, arrayType); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // d. Let k be 0. + // e. Repeat, while k < len + // i. Let Pk be ! ToString(k). + // ii. Let kValue be the first element of values and remove that element from values. + // iii. Perform ? Set(O, Pk, kValue, true). + // iv. Set k to k + 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + uint32_t k = 0; + while (k < len) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle kValue = vec[k]; + JSTaggedValue::SetProperty(thread, JSHandle::Cast(obj), kKey, kValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // f. Assert: values is now an empty List. + // g. Return O. + return obj.GetTaggedValue(); + } + + // 7. NOTE: object is not an Iterable so assume it is already an array-like object. + // 8. Let arrayLike be object. + // 9. Let len be ? LengthOfArrayLike(arrayLike). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSTaggedNumber lenTemp = + JSTaggedValue::ToLength(thread, JSTaggedValue::GetProperty(thread, objectArg, lengthKey).GetValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + uint64_t rawLen = lenTemp.GetNumber(); + // 10. Perform ? AllocateTypedArrayBuffer(O, len). + TypedArrayHelper::AllocateSharedTypedArrayBuffer(thread, obj, rawLen, arrayType); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 11. Let k be 0. + // 12. Repeat, while k < len + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(arrayLike, Pk). + // c. Perform ? Set(O, Pk, kValue, true). + // d. Set k to k + 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + uint32_t len = static_cast(rawLen); + uint32_t k = 0; + while (k < len) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle kValue = JSTaggedValue::GetProperty(thread, objectArg, kKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(obj), kKey, kValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // 13. Return O. + return obj.GetTaggedValue(); +} + // es11 22.2.4.3 TypedArray ( typedArray ) JSTaggedValue TypedArrayHelper::CreateFromTypedArray(EcmaRuntimeCallInfo *argv, const JSHandle &obj, const DataViewType arrayType) @@ -423,6 +519,78 @@ JSTaggedValue TypedArrayHelper::CreateFromArrayBuffer(EcmaRuntimeCallInfo *argv, return obj.GetTaggedValue(); } +// es11 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] ) +JSTaggedValue TypedArrayHelper::CreateFromSendableArrayBuffer(EcmaRuntimeCallInfo *argv, + const JSHandle &obj, + const DataViewType arrayType) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 5. Let elementSize be the Element Size value specified in Table 61 for constructorName. + // 6. Let offset be ? ToIndex(byteOffset). + uint32_t elementSize = TypedArrayHelper::GetSizeFromType(arrayType); + JSHandle byteOffset = BuiltinsBase::GetCallArg(argv, 1); + JSTaggedNumber index = JSTaggedValue::ToIndex(thread, byteOffset); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + auto offset = static_cast(index.GetNumber()); + // 7. If offset modulo elementSize ≠ 0, throw a RangeError exception. + if (offset % elementSize != 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The offset cannot be an integral multiple of elementSize.", + JSTaggedValue::Exception()); + } + // 8. If length is not undefined, then + // a. Let newLength be ? ToIndex(length). + JSHandle length = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + uint64_t newLength = 0; + if (!length->IsUndefined()) { + index = JSTaggedValue::ToIndex(thread, length); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + newLength = static_cast(index.GetNumber()); + } + // 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + JSHandle buffer = BuiltinsBase::GetCallArg(argv, 0); + if (builtins::BuiltinsSendableArrayBuffer::IsDetachedBuffer(buffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The srcData is detached buffer.", JSTaggedValue::Exception()); + } + // 10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. + uint32_t bufferByteLength = JSHandle(buffer)->GetArrayBufferByteLength(); + // 11. If length is undefined, then + // a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception. + // b. Let newByteLength be bufferByteLength - offset. + // c. If newByteLength < 0, throw a RangeError exception. + uint64_t newByteLength = 0; + if (length->IsUndefined()) { + if (bufferByteLength % elementSize != 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The bufferByteLength cannot be an integral multiple of elementSize.", + JSTaggedValue::Exception()); + } + if (bufferByteLength < offset) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The newByteLength is less than 0.", JSTaggedValue::Exception()); + } + newByteLength = bufferByteLength - offset; + } else { + // 12. Else, + // a. Let newByteLength be newLength × elementSize. + // b. If offset + newByteLength > bufferByteLength, throw a RangeError exception. + newByteLength = newLength * elementSize; + if (offset + newByteLength > bufferByteLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The newByteLength is out of range.", JSTaggedValue::Exception()); + } + } + // 13. Set O.[[ViewedArrayBuffer]] to buffer. + // 14. Set O.[[ByteLength]] to newByteLength. + // 15. Set O.[[ByteOffset]] to offset. + // 16. Set O.[[ArrayLength]] to newByteLength / elementSize. + JSSharedTypedArray *jsTypedArray = JSSharedTypedArray::Cast(*obj); + jsTypedArray->SetViewedArrayBufferOrByteArray(thread, buffer); + jsTypedArray->SetByteLength(newByteLength); + jsTypedArray->SetByteOffset(offset); + jsTypedArray->SetArrayLength(newByteLength / elementSize); + // 17. Return O. + return obj.GetTaggedValue(); +} + // es11 22.2.4.2.1 Runtime Semantics: AllocateTypedArray ( constructorName, newTarget, defaultProto ) JSHandle TypedArrayHelper::AllocateTypedArray(JSThread *thread, const JSHandle &constructorName, @@ -621,7 +789,7 @@ JSHandle TypedArrayHelper::AllocateSharedTypedArrayBuffer(JSThread *th if (byteLength > JSTypedArray::MAX_ONHEAP_LENGTH) { JSHandle constructor = thread->GetEcmaVM()->GetGlobalEnv()->GetArrayBufferFunction(); data = JSHandle(thread, - BuiltinsArrayBuffer::AllocateArrayBuffer(thread, constructor, byteLength)); + builtins::BuiltinsSendableArrayBuffer::AllocateSendableArrayBuffer(thread, constructor, byteLength)); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); ASSERT_PRINT(!JSHandle(obj)->GetClass()->IsOnHeapFromBitField(), "must be not on heap"); } else { diff --git a/ecmascript/base/typed_array_helper.h b/ecmascript/base/typed_array_helper.h index 1d04b7147c..d3b645a805 100644 --- a/ecmascript/base/typed_array_helper.h +++ b/ecmascript/base/typed_array_helper.h @@ -95,10 +95,15 @@ public: private: static JSTaggedValue CreateFromOrdinaryObject(EcmaRuntimeCallInfo *argv, const JSHandle &obj, const DataViewType arrayType); + static JSTaggedValue CreateFromSendableOrdinaryObject(EcmaRuntimeCallInfo *argv, const JSHandle &obj, + const DataViewType arrayType); static JSTaggedValue CreateFromTypedArray(EcmaRuntimeCallInfo *argv, const JSHandle &obj, const DataViewType arrayType); static JSTaggedValue CreateFromArrayBuffer(EcmaRuntimeCallInfo *argv, const JSHandle &obj, const DataViewType arrayType); + static JSTaggedValue CreateFromSendableArrayBuffer(EcmaRuntimeCallInfo *argv, + const JSHandle &obj, + const DataViewType arrayType); static JSHandle AllocateTypedArrayBuffer(JSThread *thread, const JSHandle &obj, uint64_t length, const DataViewType arrayType); static JSHandle AllocateSharedTypedArrayBuffer(JSThread *thread, const JSHandle &obj, diff --git a/ecmascript/builtins/builtins.cpp b/ecmascript/builtins/builtins.cpp index 1139d0e306..23bd6ed137 100644 --- a/ecmascript/builtins/builtins.cpp +++ b/ecmascript/builtins/builtins.cpp @@ -3837,6 +3837,11 @@ void Builtins::RegisterSendableContainers(const JSHandle &env) const PropertyDescriptor desc(thread_, env->GetSharedArrayFunction(), true, false, true); JSObject::DefineOwnProperty(thread_, globalObject, nameString, desc); } + { + JSHandle nameString(factory_->NewFromUtf8("SendableArrayBuffer")); + PropertyDescriptor desc(thread_, env->GetSBuiltininArrayBufferFunction(), true, false, true); + JSObject::DefineOwnProperty(thread_, globalObject, nameString, desc); + } #define REGISTER_BUILTIN_SHARED_TYPED_ARRAY(Type, ctorName, TYPE, bytesPerElement) \ { \ JSHandle nameString(factory_->NewFromUtf8(#ctorName)); \ diff --git a/ecmascript/builtins/builtins.h b/ecmascript/builtins/builtins.h index 6b7ba180a2..b31d1fc8c4 100644 --- a/ecmascript/builtins/builtins.h +++ b/ecmascript/builtins/builtins.h @@ -363,6 +363,8 @@ private: const JSHandle &sFuncPrototype) const; void InitializeSFunction(const JSHandle &env, const JSHandle &sFuncPrototype) const; + void InitializeSArrayBuffer(const JSHandle &env, const JSHandle &objFuncClass, + const JSHandle &sFuncPrototype) const; void InitializeSSet(const JSHandle &env, const JSHandle &sObjPrototype, const JSHandle &sFuncPrototype) const; void InitializeSMap(const JSHandle &env, const JSHandle &sObjPrototype, @@ -394,6 +396,8 @@ private: JSHandle CreateSTypedArrayPrototypeHClass(const JSHandle &sObjPrototype) const; JSHandle CreateSTypedArrayFunctionHClass(const JSHandle &sFuncPrototype) const; JSHandle CreateSSpecificTypedArrayFuncHClass(const JSHandle &sFuncPrototype) const; + JSHandle CreateSArrayBufferPrototypeHClass(const JSHandle &sObjPrototype) const; + JSHandle CreateSArrayBufferFunctionHClass(const JSHandle &sFuncPrototype) const; void InitializeSCtor(const JSHandle &protoHClass, const JSHandle &ctor, std::string_view name, int length) const; diff --git a/ecmascript/builtins/builtins_sendable_arraybuffer.cpp b/ecmascript/builtins/builtins_sendable_arraybuffer.cpp new file mode 100644 index 0000000000..6074ac306c --- /dev/null +++ b/ecmascript/builtins/builtins_sendable_arraybuffer.cpp @@ -0,0 +1,956 @@ +/* + * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtins/builtins_sendable_arraybuffer.h" + +#include + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/base/number_helper.h" +#include "ecmascript/builtins/builtins_bigint.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/interpreter/interpreter.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_object-inl.h" +#include "ecmascript/js_tagged_number.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/shared_objects/js_sendable_arraybuffer.h" +#include "ecmascript/base/typed_array_helper-inl.h" +#include "ecmascript/base/typed_array_helper.h" + +#include "securec.h" +#include "cstdio" +#include "cstring" + +namespace panda::ecmascript::builtins { +using TypedArrayHelper = base::TypedArrayHelper; +// 24.1.2.1 ArrayBuffer(length) +JSTaggedValue BuiltinsSendableArrayBuffer::ArrayBufferConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, SendableArrayBuffer, Constructor); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle newTarget = GetNewTarget(argv); + // 1. If NewTarget is undefined, throw a TypeError exception. + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "newtarget is undefined", JSTaggedValue::Exception()); + } + JSHandle lengthHandle = GetCallArg(argv, 0); + JSTaggedNumber lenNum = JSTaggedValue::ToIndex(thread, lengthHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + uint64_t length = lenNum.GetNumber(); + return AllocateSendableArrayBuffer(thread, newTarget, length); +} + +// 24.1.3.1 ArrayBuffer.isView(arg) +JSTaggedValue BuiltinsSendableArrayBuffer::IsView(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, SendableArrayBuffer, IsView); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle arg = GetCallArg(argv, 0); + // 1. If Type(arg) is not Object, return false. + if (!arg->IsECMAObject()) { + return BuiltinsSendableArrayBuffer::GetTaggedBoolean(false); + } + // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true. + if (arg->IsDataView() || arg->IsTypedArray()) { + return BuiltinsSendableArrayBuffer::GetTaggedBoolean(true); + } + // 3. Return false. + return BuiltinsSendableArrayBuffer::GetTaggedBoolean(false); +} + +// 24.1.3.3 get ArrayBuffer [ @@species ] +JSTaggedValue BuiltinsSendableArrayBuffer::Species(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), SendableArrayBuffer, Species); + return GetThis(argv).GetTaggedValue(); +} + +// 24.1.4.1 get ArrayBuffer.prototype.byteLength +JSTaggedValue BuiltinsSendableArrayBuffer::GetByteLength(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, SendableArrayBuffer, GetByteLength); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception()); + } + // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. + if (!thisHandle->IsSendableArrayBuffer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception()); + } + // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception. + if (IsDetachedBuffer(thisHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "IsDetachedBuffer", JSTaggedValue::Exception()); + } + JSHandle arrBuf(thisHandle); + // 5. Let length be the value of O’s [[ArrayBufferByteLength]] internal slot. + uint32_t length = arrBuf->GetArrayBufferByteLength(); + // 6. Return length. + return JSTaggedValue(length); +} + +// 24.1.4.3 ArrayBuffer.prototype.slice(start, end) +JSTaggedValue BuiltinsSendableArrayBuffer::Slice(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, SendableArrayBuffer, Slice); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsHeapObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception()); + } + JSHandle arrBuf(thisHandle); + // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. + if (!thisHandle->IsSendableArrayBuffer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception()); + } + // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception. + if (IsDetachedBuffer(thisHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception()); + } + // 5. Let len be the value of O’s [[ArrayBufferByteLength]] internal slot. + int32_t len = static_cast(arrBuf->GetArrayBufferByteLength()); + JSHandle startHandle = GetCallArg(argv, 0); + // 6. Let relativeStart be ToInteger(start). + JSTaggedNumber relativeStart = JSTaggedValue::ToInteger(thread, startHandle); + // 7. ReturnIfAbrupt(relativeStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t start = base::NumberHelper::DoubleInRangeInt32(relativeStart.GetNumber()); + int32_t end = 0; + int32_t first = 0; + int32_t last = 0; + // 8. If relativeStart < 0, let first be max((len + relativeStart),0); else let first be min(relativeStart, len). + if (start < 0) { + first = std::max((len + start), 0); + } else { + first = std::min(start, len); + } + // 9. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end). + JSHandle endHandle = GetCallArg(argv, 1); + if (endHandle->IsUndefined()) { + end = len; + } else { + JSTaggedNumber relativeEnd = JSTaggedValue::ToInteger(thread, endHandle); + // 10. ReturnIfAbrupt(relativeEnd). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + end = base::NumberHelper::DoubleInRangeInt32(relativeEnd.GetNumber()); + } + // 11. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len). + if (end < 0) { + last = std::max((len + end), 0); + } else { + last = std::min(end, len); + } + // 12. Let newLen be max(final-first,0). + uint32_t newLen = std::max((last - first), 0); + // 13. Let ctor be SpeciesConstructor(O, %ArrayBuffer%). + JSHandle defaultConstructor = env->GetSBuiltininArrayBufferFunction(); + JSHandle objHandle(thisHandle); + JSHandle constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor); + // 14. ReturnIfAbrupt(ctor). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 15. Let new be Construct(ctor, «newLen»). + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + EcmaRuntimeCallInfo *info = + EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + info->SetCallArg(JSTaggedValue(newLen)); + JSTaggedValue taggedNewArrBuf = JSFunction::Construct(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrBuf(thread, taggedNewArrBuf); + // 16. ReturnIfAbrupt(new). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 17. If new does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. + if (!newArrBuf->IsSendableArrayBuffer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "don't have bufferdata internal slot", JSTaggedValue::Exception()); + } + // 18. If IsDetachedBuffer(new) is true, throw a TypeError exception. + if (IsDetachedBuffer(newArrBuf.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new arrayBuffer IsDetachedBuffer", JSTaggedValue::Exception()); + } + // 19. If SameValue(new, O) is true, throw a TypeError exception. + if (JSTaggedValue::SameValue(newArrBuf.GetTaggedValue(), thisHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "value of new arraybuffer and this is same", JSTaggedValue::Exception()); + } + JSHandle newJsArrBuf(newArrBuf); + // 20. If the value of new’s [[ArrayBufferByteLength]] internal slot < newLen, throw a TypeError exception. + uint32_t newArrBufLen = newJsArrBuf->GetArrayBufferByteLength(); + if (newArrBufLen < newLen) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new array buffer length smaller than newlen", JSTaggedValue::Exception()); + } + // 21. NOTE: Side-effects of the above steps may have detached O. + // 22. If IsDetachedBuffer(O) is true, throw a TypeError exception. + if (IsDetachedBuffer(thisHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception()); + } + if (newLen > 0) { + // 23. Let fromBuf be the value of O’s [[ArrayBufferData]] internal slot. + void *fromBuf = GetDataPointFromBuffer(arrBuf.GetTaggedValue()); + // 24. Let toBuf be the value of new’s [[ArrayBufferData]] internal slot. + void *toBuf = GetDataPointFromBuffer(newJsArrBuf.GetTaggedValue()); + // 25. Perform CopyDataBlockBytes(toBuf, fromBuf, first, newLen). + JSArrayBuffer::CopyDataPointBytes(toBuf, fromBuf, first, newLen); + } + // Return new. + return newArrBuf.GetTaggedValue(); +} + +// 24.1.1.1 AllocateArrayBuffer(constructor, byteLength) +JSTaggedValue BuiltinsSendableArrayBuffer::AllocateSendableArrayBuffer( + JSThread *thread, const JSHandle &newTarget, uint64_t byteLength) +{ + BUILTINS_API_TRACE(thread, SendableArrayBuffer, AllocateSendableArrayBuffer); + /** + * 1. Let obj be OrdinaryCreateFromConstructor(constructor, "%ArrayBufferPrototype%", + * «[[ArrayBufferData]], [[ArrayBufferByteLength]]» ). + * */ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle arrBufFunc = env->GetSBuiltininArrayBufferFunction(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(arrBufFunc), newTarget); + ASSERT(obj.GetTaggedValue().IsInSharedHeap()); + // 2. ReturnIfAbrupt + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 4. Let block be CreateByteDataBlock(byteLength). + if (byteLength > INT_MAX) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Out of range", JSTaggedValue::Exception()); + } + uint64_t totalNativeSize = static_cast(thread->GetNativeAreaAllocator()->GetArrayBufferNativeSize()); + if (UNLIKELY(totalNativeSize > MAX_NATIVE_SIZE_LIMIT)) { + THROW_RANGE_ERROR_AND_RETURN(thread, NATIVE_SIZE_OUT_OF_LIMIT_MESSAGE, JSTaggedValue::Exception()); + } + uint32_t arrayByteLength = static_cast(byteLength); + JSHandle arrayBuffer(obj); + // 6. Set obj’s [[ArrayBufferData]] internal slot to block. + factory->NewJSSendableArrayBufferData(arrayBuffer, arrayByteLength); + // 7. Set obj’s [[ArrayBufferByteLength]] internal slot to byteLength. + arrayBuffer->SetArrayBufferByteLength(arrayByteLength); + // 8. Return obj. + return arrayBuffer.GetTaggedValue(); +} + +// 24.1.1.2 IsDetachedBuffer() +bool BuiltinsSendableArrayBuffer::IsDetachedBuffer(JSTaggedValue arrayBuffer) +{ + if (arrayBuffer.IsByteArray()) { + return false; + } + // 1. Assert: Type(arrayBuffer) is Object and it has an [[ArrayBufferData]] internal slot. + ASSERT(arrayBuffer.IsSendableArrayBuffer()); + JSSendableArrayBuffer *buffer = JSSendableArrayBuffer::Cast(arrayBuffer.GetTaggedObject()); + JSTaggedValue dataSlot = buffer->GetArrayBufferData(); + // 2. If arrayBuffer’s [[ArrayBufferData]] internal slot is null, return true. + // 3. Return false. + return dataSlot.IsNull(); +} + +// 24.1.1.4 +JSTaggedValue BuiltinsSendableArrayBuffer::CloneArrayBuffer(JSThread *thread, + const JSHandle &srcBuffer, + uint32_t srcByteOffset, + JSHandle constructor) +{ + BUILTINS_API_TRACE(thread, SendableArrayBuffer, CloneArrayBuffer); + // 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot. + ASSERT(srcBuffer->IsSendableArrayBuffer() || srcBuffer->IsSharedArrayBuffer() || srcBuffer->IsByteArray()); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + // 2. If cloneConstructor is not present + if (constructor->IsUndefined()) { + // a. Let cloneConstructor be SpeciesConstructor(srcBuffer, %ArrayBuffer%). + JSHandle defaultConstructor = env->GetSBuiltininArrayBufferFunction(); + JSHandle objHandle(srcBuffer); + constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor); + // b. ReturnIfAbrupt(cloneConstructor). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); + } else { + ASSERT(constructor->IsConstructor()); + } + } + // 4. Let srcLength be the value of srcBuffer’s [[ArrayBufferByteLength]] internal slot. + JSHandle arrBuf(srcBuffer); + uint32_t srcLen = arrBuf->GetArrayBufferByteLength(); + // 5. Assert: srcByteOffset ≤ srcLength. + ASSERT(srcByteOffset <= srcLen); + // 6. Let cloneLength be srcLength – srcByteOffset. + int32_t cloneLen = static_cast(srcLen - srcByteOffset); + // 8. Let targetBuffer be AllocateArrayBuffer(cloneConstructor, cloneLength). + JSTaggedValue taggedBuf = AllocateSendableArrayBuffer(thread, constructor, cloneLen); + // 9. ReturnIfAbrupt(targetBuffer). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 10. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); + } + // 11. Let targetBlock be the value of targetBuffer’s [[ArrayBufferData]] internal slot. + JSHandle newArrBuf(thread, taggedBuf); + // Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, cloneLength). + // 7. Let srcBlock be the value of srcBuffer’s [[ArrayBufferData]] internal slot. + void *fromBuf = GetDataPointFromBuffer(arrBuf.GetTaggedValue()); + void *toBuf = GetDataPointFromBuffer(taggedBuf); + if (cloneLen > 0) { + JSArrayBuffer::CopyDataPointBytes(toBuf, fromBuf, srcByteOffset, cloneLen); + } + return taggedBuf; +} + +// 24.1.1.5 +// NOLINTNEXTLINE(readability-function-size) +JSTaggedValue BuiltinsSendableArrayBuffer::GetValueFromBuffer( + JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex, DataViewType type, bool littleEndian) +{ + void *pointer = GetDataPointFromBuffer(arrBuf); + uint8_t *block = reinterpret_cast(pointer); + return GetValueFromBuffer(thread, byteIndex, block, type, littleEndian); +} + +JSTaggedValue BuiltinsSendableArrayBuffer::GetValueFromBuffer(JSThread *thread, uint32_t byteIndex, uint8_t *block, + DataViewType type, bool littleEndian) +{ + switch (type) { + case DataViewType::UINT8: + case DataViewType::UINT8_CLAMPED: { + uint8_t res = block[byteIndex]; // NOLINT + return GetTaggedInt(res); + } + case DataViewType::INT8: { + uint8_t res = block[byteIndex]; // NOLINT + auto int8Res = static_cast(res); + return GetTaggedInt(int8Res); + } + case DataViewType::UINT16: + return GetValueFromBufferForInteger(block, byteIndex, littleEndian); + case DataViewType::INT16: + return GetValueFromBufferForInteger(block, byteIndex, littleEndian); + case DataViewType::UINT32: + return GetValueFromBufferForInteger(block, byteIndex, littleEndian); + case DataViewType::INT32: + return GetValueFromBufferForInteger(block, byteIndex, littleEndian); + case DataViewType::FLOAT32: + return GetValueFromBufferForFloat(block, byteIndex, littleEndian); + case DataViewType::FLOAT64: + return GetValueFromBufferForFloat(block, byteIndex, littleEndian); + case DataViewType::BIGINT64: + return GetValueFromBufferForBigInt(thread, block, byteIndex, littleEndian); + case DataViewType::BIGUINT64: + return GetValueFromBufferForBigInt(thread, block, byteIndex, littleEndian); + default: + break; + } + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); +} + +// 24.1.1.6 +JSTaggedValue BuiltinsSendableArrayBuffer::SetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex, + DataViewType type, const JSHandle &value, + bool littleEndian) +{ + if (UNLIKELY(IsBigIntElementType(type))) { + JSHandle arrBufHandle(thread, arrBuf); + switch (type) { + case DataViewType::BIGINT64: + SetValueInBufferForBigInt(thread, value, arrBufHandle, byteIndex, littleEndian); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + break; + case DataViewType::BIGUINT64: + SetValueInBufferForBigInt(thread, value, arrBufHandle, byteIndex, littleEndian); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + break; + default: + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } + return JSTaggedValue::Undefined(); + } + void *pointer = GetDataPointFromBuffer(arrBuf); + uint8_t *block = reinterpret_cast(pointer); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, value.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double val = numberValue.GetNumber(); + return SetValueInBuffer(thread, byteIndex, block, type, val, littleEndian); +} + +// es12 25.1.2.7 IsBigIntElementType ( type ) +bool BuiltinsSendableArrayBuffer::IsBigIntElementType(DataViewType type) +{ + if (type == DataViewType::BIGINT64 || type == DataViewType::BIGUINT64) { + return true; + } + return false; +} + +// es12 25.1.2.6 IsUnclampedIntegerElementType ( type ) +bool BuiltinsSendableArrayBuffer::IsUnclampedIntegerElementType(DataViewType type) +{ + switch (type) { + case DataViewType::INT8: + case DataViewType::INT16: + case DataViewType::INT32: + case DataViewType::UINT8: + case DataViewType::UINT16: + case DataViewType::UINT32: + return true; + default: + return false; + } +} + +template +void BuiltinsSendableArrayBuffer::SetTypeData(uint8_t *block, T value, uint32_t index) +{ + uint32_t sizeCount = sizeof(T); + uint8_t *res = reinterpret_cast(&value); + for (uint32_t i = 0; i < sizeCount; i++) { + *(block + index + i) = *(res + i); // NOLINT + } +} + +template +void BuiltinsSendableArrayBuffer::FastSetTypeData(uint8_t *byteBeginOffset, uint8_t *byteEndOffset, T value) +{ + uint32_t sizeCount = sizeof(T); + if (sizeCount == 1) { + memset_s(byteBeginOffset, byteEndOffset-byteBeginOffset, value, byteEndOffset-byteBeginOffset); + } else { + uint8_t *resAddr = reinterpret_cast(&value); + for (uint8_t *addr = byteBeginOffset; addr < byteEndOffset; addr += sizeCount) { + for (uint32_t i = 0; i < sizeCount; ++i) { + *(addr + i) = *(resAddr + i); + } + } + } +} + +template +T BuiltinsSendableArrayBuffer::LittleEndianToBigEndian(T liValue) +{ + uint8_t sizeCount = sizeof(T); + T biValue; + switch (sizeCount) { + case NumberSize::UINT16: + biValue = ((liValue & 0x00FF) << BITS_EIGHT) // NOLINT + | ((liValue & 0xFF00) >> BITS_EIGHT); // NOLINT + break; + case NumberSize::UINT32: + biValue = ((liValue & 0x000000FF) << BITS_TWENTY_FOUR) // NOLINT + | ((liValue & 0x0000FF00) << BITS_EIGHT) // NOLINT + | ((liValue & 0x00FF0000) >> BITS_EIGHT) // NOLINT + | ((liValue & 0xFF000000) >> BITS_TWENTY_FOUR); // NOLINT + break; + default: + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + break; + } + return biValue; +} +template +T BuiltinsSendableArrayBuffer::LittleEndianToBigEndian64Bit(T liValue) +{ + return ((liValue & 0x00000000000000FF) << BITS_FIFTY_SIX) // NOLINT + | ((liValue & 0x000000000000FF00) << BITS_FORTY) // NOLINT + | ((liValue & 0x0000000000FF0000) << BITS_TWENTY_FOUR) // NOLINT + | ((liValue & 0x00000000FF000000) << BITS_EIGHT) // NOLINT + | ((liValue & 0x000000FF00000000) >> BITS_EIGHT) // NOLINT + | ((liValue & 0x0000FF0000000000) >> BITS_TWENTY_FOUR) // NOLINT + | ((liValue & 0x00FF000000000000) >> BITS_FORTY) // NOLINT + | ((liValue & 0xFF00000000000000) >> BITS_FIFTY_SIX); // NOLINT +} + +template +JSTaggedValue BuiltinsSendableArrayBuffer::GetValueFromBufferForInteger( + uint8_t *block, uint32_t byteIndex, bool littleEndian) +{ + ASSERT_PRINT(std::is_integral_v, "T must be integral"); + ASSERT_PRINT(sizeof(T) == size, "Invalid number size"); + ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8"); + + ASSERT(size >= NumberSize::UINT16 || size <= NumberSize::FLOAT64); + T res = *reinterpret_cast(block + byteIndex); + if (!littleEndian) { + res = LittleEndianToBigEndian(res); + } + + // uint32_t maybe overflow with TaggedInt + // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon) + if constexpr (std::is_same_v) { + // NOLINTNEXTLINE(clang-diagnostic-sign-compare) + if (res > static_cast(std::numeric_limits::max())) { + return GetTaggedDouble(static_cast(res)); + } + } + return GetTaggedInt(res); +} + +template +JSTaggedValue BuiltinsSendableArrayBuffer::GetValueFromBufferForFloat( + uint8_t *block, uint32_t byteIndex, bool littleEndian) +{ + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be correct type"); + ASSERT_PRINT(sizeof(T) == size, "Invalid number size"); + + UnionType unionValue = {0}; + // NOLINTNEXTLINE(readability-braces-around-statements) + if constexpr (std::is_same_v) { + unionValue.uValue = *reinterpret_cast(block + byteIndex); + uint32_t res = LittleEndianToBigEndian(unionValue.uValue); + return CommonConvert(unionValue.value, res, littleEndian); + } else if constexpr (std::is_same_v) { // NOLINTNEXTLINE(readability-braces-around-statements) + unionValue.uValue = *reinterpret_cast(block + byteIndex); + uint64_t res = LittleEndianToBigEndian64Bit(unionValue.uValue); + return CommonConvert(unionValue.value, res, littleEndian); + } + + return GetTaggedDouble(unionValue.value); +} + +template +JSTaggedValue BuiltinsSendableArrayBuffer::CommonConvert(T1 &value, T2 &res, bool littleEndian) +{ + if (std::isnan(value) && !JSTaggedValue::IsImpureNaN(value)) { + return GetTaggedDouble(value); + } + if (!littleEndian) { + T1 d = base::bit_cast(res); + if (JSTaggedValue::IsImpureNaN(d)) { + return GetTaggedDouble(base::NAN_VALUE); + } + return GetTaggedDouble(d); + } else { + if (JSTaggedValue::IsImpureNaN(value)) { + return GetTaggedDouble(base::NAN_VALUE); + } + } + return GetTaggedDouble(value); +} + + +template +JSTaggedValue BuiltinsSendableArrayBuffer::GetValueFromBufferForBigInt(JSThread *thread, uint8_t *block, + uint32_t byteIndex, bool littleEndian) +{ + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be uint64_t/int64_t"); + auto pTmp = *reinterpret_cast(block + byteIndex); + if (!littleEndian) { + pTmp = LittleEndianToBigEndian64Bit(pTmp); + } + if constexpr (std::is_same_v) { + return BigInt::Uint64ToBigInt(thread, pTmp).GetTaggedValue(); + } + return BigInt::Int64ToBigInt(thread, pTmp).GetTaggedValue(); +} + + +template +void BuiltinsSendableArrayBuffer::SetValueInBufferForByte(double val, uint8_t *block, uint32_t byteIndex) +{ + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be int8/uint8"); + T res; + if (std::isnan(val) || std::isinf(val)) { + res = 0; + SetTypeData(block, res, byteIndex); + return; + } + auto int64Val = static_cast(val); + auto *resArr = reinterpret_cast(&int64Val); + res = *resArr; + SetTypeData(block, res, byteIndex); +} + +void BuiltinsSendableArrayBuffer::SetValueInBufferForUint8Clamped(double val, uint8_t *block, uint32_t byteIndex) +{ + uint8_t res; + if (std::isnan(val) || val <= 0) { + res = 0; + } else if (val > UINT8_MAX) { + res = UINT8_MAX; + } else { + // same as ToUint8Clamp + res = std::lrint(val); + } + SetTypeData(block, res, byteIndex); +} + +template +void BuiltinsSendableArrayBuffer::SetValueInBufferForInteger( + double val, uint8_t *block, uint32_t byteIndex, bool littleEndian) +{ + ASSERT_PRINT(std::is_integral_v, "T must be integral"); + ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8"); + T res; + if (std::isnan(val) || std::isinf(val)) { + res = 0; + SetTypeData(block, res, byteIndex); + return; + } + auto int64Val = static_cast(val); + // NOLINTNEXTLINE(readability-braces-around-statements) + if constexpr (std::is_same_v) { + auto *pTmp = reinterpret_cast(&int64Val); + int16_t tmp = *pTmp; + res = static_cast(tmp); + } else { // NOLINTNEXTLINE(readability-braces-around-statements) + auto *pTmp = reinterpret_cast(&int64Val); + res = *pTmp; + } + + if (!littleEndian) { + res = LittleEndianToBigEndian(res); + } + SetTypeData(block, res, byteIndex); +} + +template +void BuiltinsSendableArrayBuffer::SetValueInBufferForFloat( + double val, uint8_t *block, uint32_t byteIndex, bool littleEndian) +{ + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be float type"); + auto data = static_cast(val); + if (std::isnan(val)) { + SetTypeData(block, data, byteIndex); + return; + } + if (!littleEndian) { + if constexpr (std::is_same_v) { + uint32_t res = base::bit_cast(data); + data = base::bit_cast(LittleEndianToBigEndian(res)); + } else if constexpr (std::is_same_v) { + uint64_t res = base::bit_cast(data); + data = base::bit_cast(LittleEndianToBigEndian64Bit(res)); + } + } + SetTypeData(block, data, byteIndex); +} + +template +void BuiltinsSendableArrayBuffer::SetValueInBufferForBigInt(JSThread *thread, + const JSHandle &val, + JSHandle &arrBuf, + uint32_t byteIndex, bool littleEndian) +{ + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be int64_t/uint64_t"); + T value = 0; + bool lossless = true; + if constexpr(std::is_same_v) { + BigInt::BigIntToUint64(thread, val, reinterpret_cast(&value), &lossless); + } else { + BigInt::BigIntToInt64(thread, val, reinterpret_cast(&value), &lossless); + } + RETURN_IF_ABRUPT_COMPLETION(thread); + if (!littleEndian) { + value = LittleEndianToBigEndian64Bit(value); + } + void *pointer = GetDataPointFromBuffer(arrBuf.GetTaggedValue()); + uint8_t *block = reinterpret_cast(pointer); + SetTypeData(block, value, byteIndex); +} + +template +void BuiltinsSendableArrayBuffer::SetValueInBufferForBigInt(JSThread *thread, + double val, uint8_t *block, + uint32_t byteIndex, bool littleEndian) +{ + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be int64_t/uint64_t"); + T value = 0; + bool lossless = true; + + JSHandle valHandle(thread, GetTaggedDouble(val)); + if constexpr(std::is_same_v) { + BigInt::BigIntToUint64(thread, valHandle, reinterpret_cast(&value), &lossless); + } else { + BigInt::BigIntToInt64(thread, valHandle, reinterpret_cast(&value), &lossless); + } + RETURN_IF_ABRUPT_COMPLETION(thread); + if (!littleEndian) { + value = LittleEndianToBigEndian64Bit(value); + } + SetTypeData(block, value, byteIndex); +} + +JSTaggedValue BuiltinsSendableArrayBuffer::FastSetValueInBuffer( + JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex, DataViewType type, double val, bool littleEndian) +{ + void *pointer = GetDataPointFromBuffer(arrBuf); + uint8_t *block = reinterpret_cast(pointer); + return SetValueInBuffer(thread, byteIndex, block, type, val, littleEndian); +} + +JSTaggedValue BuiltinsSendableArrayBuffer::SetValueInBuffer(JSThread* thread, uint32_t byteIndex, uint8_t *block, + DataViewType type, double val, bool littleEndian) +{ + switch (type) { + case DataViewType::UINT8: + SetValueInBufferForByte(val, block, byteIndex); + break; + case DataViewType::UINT8_CLAMPED: + SetValueInBufferForUint8Clamped(val, block, byteIndex); + break; + case DataViewType::INT8: + SetValueInBufferForByte(val, block, byteIndex); + break; + case DataViewType::UINT16: + SetValueInBufferForInteger(val, block, byteIndex, littleEndian); + break; + case DataViewType::INT16: + SetValueInBufferForInteger(val, block, byteIndex, littleEndian); + break; + case DataViewType::UINT32: + SetValueInBufferForInteger(val, block, byteIndex, littleEndian); + break; + case DataViewType::INT32: + SetValueInBufferForInteger(val, block, byteIndex, littleEndian); + break; + case DataViewType::FLOAT32: + SetValueInBufferForFloat(val, block, byteIndex, littleEndian); + break; + case DataViewType::FLOAT64: + SetValueInBufferForFloat(val, block, byteIndex, littleEndian); + break; + case DataViewType::BIGINT64: + SetValueInBufferForBigInt(thread, val, block, byteIndex, littleEndian); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + break; + case DataViewType::BIGUINT64: + SetValueInBufferForBigInt(thread, val, block, byteIndex, littleEndian); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + break; + default: + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } + return JSTaggedValue::Undefined(); +} + +void *BuiltinsSendableArrayBuffer::GetDataPointFromBuffer(JSTaggedValue arrBuf, uint32_t byteOffset) +{ + if (arrBuf.IsByteArray()) { + return reinterpret_cast(ToUintPtr(ByteArray::Cast(arrBuf.GetTaggedObject())->GetData()) + byteOffset); + } + + JSSendableArrayBuffer *arrayBuffer = JSSendableArrayBuffer::Cast(arrBuf.GetTaggedObject()); + if (arrayBuffer->GetArrayBufferByteLength() == 0) { + return nullptr; + } + + JSTaggedValue data = arrayBuffer->GetArrayBufferData(); + return reinterpret_cast(ToUintPtr(JSNativePointer::Cast(data.GetTaggedObject()) + ->GetExternalPointer()) + byteOffset); +} + +JSTaggedValue BuiltinsSendableArrayBuffer::TypedArrayToList(JSThread *thread, JSHandle& items) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle bufferHandle(thread, items->GetViewedArrayBufferOrByteArray()); + uint32_t arrayLen = items->GetArrayLength(); + JSHandle newArrayHandle(thread, JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle oldElements(thread, newArrayHandle->GetElements()); + JSHandle elements = (oldElements->GetLength() < arrayLen) ? + factory->ExtendArray(oldElements, arrayLen) : oldElements; + newArrayHandle->SetElements(thread, elements); + uint32_t offset = items->GetByteOffset(); + uint32_t elementSize = TypedArrayHelper::GetElementSize(items); + DataViewType elementType = TypedArrayHelper::GetType(items); + uint32_t index = 0; + while (index < arrayLen) { + uint32_t byteIndex = index * elementSize + offset; + JSHandle result(thread, GetValueFromBuffer(thread, bufferHandle.GetTaggedValue(), + byteIndex, elementType, true)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ElementAccessor::Set(thread, newArrayHandle, index, result, true); + index++; + } + JSHandle(newArrayHandle)->SetArrayLength(thread, arrayLen); + return newArrayHandle.GetTaggedValue(); +} + +template +void BuiltinsSendableArrayBuffer::FastSetValueInBufferForByte(uint8_t *byteBeginOffset, + uint8_t *byteEndOffset, + double val) +{ + ASSERT_PRINT(sizeof(T) == 1, "sizeof(T) must be one"); + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be int8/uint8"); + T res; + if (std::isnan(val) || std::isinf(val)) { + res = 0; + } else { + auto int64Val = static_cast(val); + auto *resArr = reinterpret_cast(&int64Val); + res = *resArr; + } + FastSetTypeData(byteBeginOffset, byteEndOffset, res); +} + +void BuiltinsSendableArrayBuffer::FastSetValueInBufferForUint8Clamped(uint8_t *byteBeginOffset, + uint8_t *byteEndOffset, + double val) +{ + uint8_t res; + if (std::isnan(val) || val <= 0) { + res = 0; + } else { + val = val >= UINT8_MAX ? UINT8_MAX : val; + constexpr double HALF = 0.5; + val = val == HALF ? 0 : std::round(val); + res = static_cast(val); + } + FastSetTypeData(byteBeginOffset, byteEndOffset, res); +} + +template +void BuiltinsSendableArrayBuffer::FastSetValueInBufferForInteger(uint8_t *byteBeginOffset, + uint8_t *byteEndOffset, + double val, bool littleEndian) +{ + ASSERT_PRINT(std::is_integral_v, "T must be integral"); + ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8"); + T res; + if (std::isnan(val) || std::isinf(val)) { + res = 0; + } else { + auto int64Val = static_cast(val); + // NOLINTNEXTLINE(readability-braces-around-statements) + if constexpr (std::is_same_v) { + auto *pTmp = reinterpret_cast(&int64Val); + int16_t tmp = *pTmp; + res = static_cast(tmp); + } else { // NOLINTNEXTLINE(readability-braces-around-statements) + auto *pTmp = reinterpret_cast(&int64Val); + res = *pTmp; + } + if (!littleEndian) { + res = LittleEndianToBigEndian(res); + } + } + FastSetTypeData(byteBeginOffset, byteEndOffset, res); +} + +template +void BuiltinsSendableArrayBuffer::FastSetValueInBufferForFloat(uint8_t *byteBeginOffset, + uint8_t *byteEndOffset, + double val, bool littleEndian) +{ + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be float type"); + auto data = static_cast(val); + if (!std::isnan(val)) { + if (!littleEndian) { + if constexpr (std::is_same_v) { + uint32_t res = base::bit_cast(data); + data = base::bit_cast(LittleEndianToBigEndian(res)); + } else if constexpr (std::is_same_v) { + uint64_t res = base::bit_cast(data); + data = base::bit_cast(LittleEndianToBigEndian64Bit(res)); + } + } + } + FastSetTypeData(byteBeginOffset, byteEndOffset, data); +} + +template +void BuiltinsSendableArrayBuffer::FastSetValueInBufferForBigInt(JSThread *thread, + uint8_t *byteBeginOffset, + uint8_t *byteEndOffset, + double val, bool littleEndian) +{ + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be int64_t/uint64_t"); + T value = 0; + bool lossless = true; + + JSHandle valHandle(thread, GetTaggedDouble(val)); + if constexpr(std::is_same_v) { + BigInt::BigIntToUint64(thread, valHandle, reinterpret_cast(&value), &lossless); + } else { + BigInt::BigIntToInt64(thread, valHandle, reinterpret_cast(&value), &lossless); + } + RETURN_IF_ABRUPT_COMPLETION(thread); + if (!littleEndian) { + value = LittleEndianToBigEndian64Bit(value); + } + FastSetTypeData(byteBeginOffset, byteEndOffset, value); +} + +JSTaggedValue BuiltinsSendableArrayBuffer::TryFastSetValueInBuffer([[maybe_unused]] JSThread *thread, + JSTaggedValue arrBuf, + uint32_t byteBeginOffset, uint32_t byteEndOffset, + DataViewType type, double val, bool littleEndian) +{ + uint8_t *beginPointer = reinterpret_cast(GetDataPointFromBuffer(arrBuf, byteBeginOffset)); + uint8_t *endPointer = reinterpret_cast(GetDataPointFromBuffer(arrBuf, byteEndOffset)); + switch (type) { + case DataViewType::UINT8: + FastSetValueInBufferForByte(beginPointer, endPointer, val); + break; + case DataViewType::UINT8_CLAMPED: + FastSetValueInBufferForUint8Clamped(beginPointer, endPointer, val); + break; + case DataViewType::INT8: + FastSetValueInBufferForByte(beginPointer, endPointer, val); + break; + case DataViewType::UINT16: + FastSetValueInBufferForInteger(beginPointer, endPointer, val, littleEndian); + break; + case DataViewType::INT16: + FastSetValueInBufferForInteger(beginPointer, endPointer, val, littleEndian); + break; + case DataViewType::UINT32: + FastSetValueInBufferForInteger(beginPointer, endPointer, val, littleEndian); + break; + case DataViewType::INT32: + FastSetValueInBufferForInteger(beginPointer, endPointer, val, littleEndian); + break; + case DataViewType::FLOAT32: + FastSetValueInBufferForFloat(beginPointer, endPointer, val, littleEndian); + break; + case DataViewType::FLOAT64: + FastSetValueInBufferForFloat(beginPointer, endPointer, val, littleEndian); + break; + case DataViewType::BIGINT64: + FastSetValueInBufferForBigInt(thread, beginPointer, endPointer, val, littleEndian); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + break; + case DataViewType::BIGUINT64: + FastSetValueInBufferForBigInt(thread, beginPointer, endPointer, val, littleEndian); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + break; + default: + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } + return JSTaggedValue::Undefined(); +} +} // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_sendable_arraybuffer.h b/ecmascript/builtins/builtins_sendable_arraybuffer.h new file mode 100644 index 0000000000..e06c8dd313 --- /dev/null +++ b/ecmascript/builtins/builtins_sendable_arraybuffer.h @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ECMASCRIPT_BUILTINS_BUILTINS_SENDABLE_ARRAYBUFFER_H +#define ECMASCRIPT_BUILTINS_BUILTINS_SENDABLE_ARRAYBUFFER_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/base/number_helper.h" +#include "ecmascript/js_dataview.h" +#include "ecmascript/js_typed_array.h" + +// List of functions in ArrayBuffer, excluding the '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsArrayBuffer::func refers to the native implementation of ArrayBuffer[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +#define BUILTIN_ARRAY_BUFFER_FUNCTIONS(V) \ + /* ArrayBuffer.isView ( arg ) */ \ + V("isView", IsView, 1, ArrayBufferIsView) + +namespace panda::ecmascript::builtins { +using DataViewType = ecmascript::DataViewType; +using BuiltinFunctionEntry = base::BuiltinFunctionEntry; + +class BuiltinsSendableArrayBuffer : public base::BuiltinsBase { +public: + enum NumberSize : uint8_t { + UINT16 = 2, INT16 = 2, UINT32 = 4, INT32 = 4, FLOAT32 = 4, FLOAT64 = 8, BIGINT64 = 8, BIGUINT64 = 8 + }; + + // 24.1.2.1 ArrayBuffer(length) + static JSTaggedValue ArrayBufferConstructor(EcmaRuntimeCallInfo *argv); + // 24.1.3.1 ArrayBuffer.isView(arg) + static JSTaggedValue IsView(EcmaRuntimeCallInfo *argv); + // 24.1.3.3 get ArrayBuffer[@@species] + static JSTaggedValue Species(EcmaRuntimeCallInfo *argv); + // 24.1.4.1 get ArrayBuffer.prototype.byteLength + static JSTaggedValue GetByteLength(EcmaRuntimeCallInfo *argv); + // 24.1.4.3 ArrayBuffer.prototype.slice() + static JSTaggedValue Slice(EcmaRuntimeCallInfo *argv); + // 24.1.1.2 IsDetachedBuffer(arrayBuffer) + static bool IsDetachedBuffer(JSTaggedValue arrayBuffer); + // 24.1.1.5 GetValueFromBuffer ( arrayBuffer, byteIndex, type, isLittleEndian ) + static JSTaggedValue GetValueFromBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex, + DataViewType type, bool littleEndian); + // 24.1.1.6 SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isLittleEndian ) + static JSTaggedValue SetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex, + DataViewType type, const JSHandle &value, bool littleEndian); + // 24.1.1.4 CloneArrayBuffer( srcBuffer, srcByteOffset [, cloneConstructor] ) + static JSTaggedValue CloneArrayBuffer(JSThread *thread, const JSHandle &srcBuffer, + uint32_t srcByteOffset, JSHandle constructor); + // 24.1.1.1 AllocateArrayBuffer(constructor, byteLength) + static JSTaggedValue AllocateSendableArrayBuffer( + JSThread *thread, const JSHandle &newTarget, uint64_t byteLength); + // es12 25.1.2.6 IsUnclampedIntegerElementType ( type ) + static bool IsUnclampedIntegerElementType(DataViewType type); + // es12 25.1.2.7 IsBigIntElementType ( type ) + static bool IsBigIntElementType(DataViewType type); + + // Excluding the '@@' internal properties + static Span GetArrayBufferFunctions() + { + return Span(ARRAY_BUFFER_FUNCTIONS); + } + + static JSTaggedValue FastSetValueInBuffer(JSThread* thread, JSTaggedValue arrBuf, uint32_t byteIndex, + DataViewType type, double val, bool littleEndian); + static JSTaggedValue TryFastSetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteBeginOffset, + uint32_t byteEndOffset, DataViewType type, + double val, bool littleEndian); + template + static void FastSetValueInBufferForByte(uint8_t *byteBeginOffset, uint8_t *byteEndOffset, + double val); + static void FastSetValueInBufferForUint8Clamped(uint8_t *byteBeginOffset, uint8_t *byteEndOffset, + double val); + template + static void FastSetValueInBufferForInteger(uint8_t *byteBeginOffset, uint8_t *byteEndOffset, + double val, bool littleEndian); + template + static void FastSetValueInBufferForFloat(uint8_t *byteBeginOffset, uint8_t *byteEndOffset, + double val, bool littleEndian); + template + static void FastSetValueInBufferForBigInt(JSThread *thread, uint8_t *byteBeginOffset, uint8_t *byteEndOffset, + double val, bool littleEndian); + static JSTaggedValue SetValueInBuffer(JSThread *thread, uint32_t byteIndex, uint8_t *block, + DataViewType type, double val, bool littleEndian); + static JSTaggedValue GetValueFromBuffer(JSThread *thread, uint32_t byteIndex, uint8_t *block, + DataViewType type, bool littleEndian); + static void *GetDataPointFromBuffer(JSTaggedValue arrBuf, uint32_t byteOffset = 0); + + static size_t GetNumPrototypeInlinedProperties() + { + // 3 : 3 more inline properties in Set.prototype + // (1) Set.prototype.slice + // (2) Set.prototype [ @@toStringTag ] + // (3) get Set.prototype.size + return 3; + } + + static Span> GetPrototypeProperties() + { + return Span>(ARRAYBUFFER_PROTOTYPE_PROPERTIES); + } + + static Span> GetFunctionProperties() + { + return Span>(ARRAYBUFFER_FUNCTION_PROPERTIES); + } + +private: +#define BUILTIN_ARRAY_BUFFER_ENTRY(name, func, length, id) \ + BuiltinFunctionEntry::Create(name, BuiltinsSendableArrayBuffer::func, length, kungfu::BuiltinsStubCSigns::id), + + static constexpr std::array ARRAY_BUFFER_FUNCTIONS = {BUILTIN_ARRAY_BUFFER_FUNCTIONS(BUILTIN_ARRAY_BUFFER_ENTRY)}; +#undef BUILTIN_ARRAY_BUFFER_ENTRY + +#define ARRAYBUFFER_PROPERTIES_PAIR(name, func, length, id) \ + std::pair(name, false), + + static constexpr std::array ARRAYBUFFER_PROTOTYPE_PROPERTIES = { + std::pair("slice", false), + std::pair("byteLength", true), + std::pair("[Symbol.toStringTag]", false), + }; + + static constexpr std::array ARRAYBUFFER_FUNCTION_PROPERTIES = { + std::pair("length", false), + std::pair("name", false), + std::pair("prototype", false), + BUILTIN_ARRAY_BUFFER_FUNCTIONS(ARRAYBUFFER_PROPERTIES_PAIR) + std::pair("[Symbol.species]", true), + }; +#undef SET_PROPERTIES_PAIR + + template + static T LittleEndianToBigEndian(T liValue); + template + static T LittleEndianToBigEndian64Bit(T liValue); + + template + static void SetTypeData(uint8_t *block, T value, uint32_t index); + + template + static void FastSetTypeData(uint8_t *byteBeginOffset, uint8_t *byteEndOffset, T value); + + template + static JSTaggedValue GetValueFromBufferForInteger(uint8_t *block, uint32_t byteIndex, bool littleEndian); + + template + static JSTaggedValue GetValueFromBufferForFloat(uint8_t *block, uint32_t byteIndex, bool littleEndian); + template + static JSTaggedValue CommonConvert(T1 &value, T2 &res, bool littleEndian); + template + static JSTaggedValue GetValueFromBufferForBigInt(JSThread *thread, uint8_t *block, + uint32_t byteIndex, bool littleEndian); + + template + static void SetValueInBufferForByte(double val, uint8_t *block, uint32_t byteIndex); + + static void SetValueInBufferForUint8Clamped(double val, uint8_t *block, uint32_t byteIndex); + + template + static void SetValueInBufferForInteger(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian); + + template + static void SetValueInBufferForFloat(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian); + + template + static void SetValueInBufferForBigInt(JSThread *thread, const JSHandle &val, + JSHandle &arrBuf, uint32_t byteIndex, bool littleEndian); + + template + static void SetValueInBufferForBigInt(JSThread *thread, double val, + uint8_t *block, uint32_t byteIndex, bool littleEndian); + + static JSTaggedValue TypedArrayToList(JSThread *thread, JSHandle& items); + + static constexpr uint64_t MAX_NATIVE_SIZE_LIMIT = 4_GB; + static constexpr char const *NATIVE_SIZE_OUT_OF_LIMIT_MESSAGE = "total array buffer size out of limit(4_GB)"; + + friend class BuiltinsArray; + friend class BuiltinsSharedArray; +}; +} // namespace panda::ecmascript::builtins + +#endif // ECMASCRIPT_BUILTINS_BUILTINS_SENDABLE_ARRAYBUFFER_H diff --git a/ecmascript/builtins/builtins_shared_array.cpp b/ecmascript/builtins/builtins_shared_array.cpp index f4a90b97a5..0b14484cb4 100644 --- a/ecmascript/builtins/builtins_shared_array.cpp +++ b/ecmascript/builtins/builtins_shared_array.cpp @@ -1795,6 +1795,8 @@ JSTaggedValue BuiltinsSharedArray::ToString(EcmaRuntimeCallInfo *argv) thread, thisHandle.GetTaggedValue().GetTaggedObject()); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return ToStringImpl(argv, thread, thisHandle); + } else if (thisHandle->IsTypedArray()) { + return ToStringImpl(argv, thread, thisHandle); } else { auto error = ContainerError::BindError(thread, "The toString method cannot be bound."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); diff --git a/ecmascript/builtins/builtins_shared_typedarray.cpp b/ecmascript/builtins/builtins_shared_typedarray.cpp index e44be99445..e250ecc0d7 100755 --- a/ecmascript/builtins/builtins_shared_typedarray.cpp +++ b/ecmascript/builtins/builtins_shared_typedarray.cpp @@ -386,11 +386,6 @@ JSTaggedValue BuiltinsSharedTypedArray::GetBuffer(EcmaRuntimeCallInfo *argv) if (!thisHandle->IsECMAObject()) { THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); } - // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. - if (!thisHandle->IsSharedTypedArray()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[ViewedArrayBuffer]] internal slot.", - JSTaggedValue::Exception()); - } // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. JSHandle typedArray = JSHandle::Cast(thisHandle); JSTaggedValue buffer = JSTypedArray::GetOffHeapBuffer(thread, typedArray); @@ -411,11 +406,6 @@ JSTaggedValue BuiltinsSharedTypedArray::GetByteLength(EcmaRuntimeCallInfo *argv) if (!thisHandle->IsECMAObject()) { THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); } - // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. - if (!thisHandle->IsSharedTypedArray()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[ViewedArrayBuffer]] internal slot.", - JSTaggedValue::Exception()); - } // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. JSHandle typeArrayObj = JSHandle::Cast(thisHandle); JSTaggedValue buffer = typeArrayObj->GetViewedArrayBufferOrByteArray(); @@ -441,11 +431,6 @@ JSTaggedValue BuiltinsSharedTypedArray::GetByteOffset(EcmaRuntimeCallInfo *argv) if (!thisHandle->IsECMAObject()) { THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); } - // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. - if (!thisHandle->IsSharedTypedArray()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[ViewedArrayBuffer]] internal slot.", - JSTaggedValue::Exception()); - } // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. JSHandle typeArrayObj = JSHandle::Cast(thisHandle); JSTaggedValue buffer = typeArrayObj->GetViewedArrayBufferOrByteArray(); @@ -763,7 +748,7 @@ JSTaggedValue BuiltinsSharedTypedArray::Join(EcmaRuntimeCallInfo *argv) JSHandle thisHandle = GetThis(argv); - if (!thisHandle->IsSharedTypedArray()) { + if (!thisHandle->IsSharedTypedArray() && !thisHandle->IsTypedArray()) { THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); } @@ -882,11 +867,6 @@ JSTaggedValue BuiltinsSharedTypedArray::GetLength(EcmaRuntimeCallInfo *argv) if (!thisHandle->IsECMAObject()) { THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); } - // 3. If O does not have a [[TypedArrayName]] internal slot, throw a TypeError exception. - if (!thisHandle->IsSharedTypedArray()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[TypedArrayName]] internal slot.", - JSTaggedValue::Exception()); - } // 4. Assert: O has [[ViewedArrayBuffer]] and [[ArrayLength]] internal slots. // 5. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. JSTaggedValue buffer = JSHandle::Cast(thisHandle)->GetViewedArrayBufferOrByteArray(); diff --git a/ecmascript/builtins/shared_builtins.cpp b/ecmascript/builtins/shared_builtins.cpp index c49ed37a1f..ada596b86a 100644 --- a/ecmascript/builtins/shared_builtins.cpp +++ b/ecmascript/builtins/shared_builtins.cpp @@ -18,12 +18,14 @@ #include "ecmascript/builtins/builtins_function.h" #include "ecmascript/builtins/builtins_object.h" #include "ecmascript/builtins/builtins_symbol.h" +#include "ecmascript/builtins/builtins_sendable_arraybuffer.h" #include "ecmascript/builtins/builtins_shared_function.h" #include "ecmascript/builtins/builtins_shared_object.h" #include "ecmascript/builtins/builtins_shared_map.h" #include "ecmascript/builtins/builtins_shared_set.h" #include "ecmascript/builtins/builtins_shared_typedarray.h" #include "ecmascript/shared_objects/js_shared_array.h" +#include "ecmascript/shared_objects/js_sendable_arraybuffer.h" #include "ecmascript/shared_objects/js_shared_map.h" #include "ecmascript/shared_objects/js_shared_object.h" #include "ecmascript/shared_objects/js_shared_set.h" @@ -42,6 +44,7 @@ using BuiltinsSharedSet = builtins::BuiltinsSharedSet; using BuiltinsSharedMap = builtins::BuiltinsSharedMap; using BuiltinsSharedArray = builtins::BuiltinsSharedArray; using BuiltinsSharedTypedArray = builtins::BuiltinsSharedTypedArray; +using BuiltinsSendableArrayBuffer = builtins::BuiltinsSendableArrayBuffer; void Builtins::InitializeSObjectAndSFunction(const JSHandle &env) const { @@ -69,6 +72,7 @@ void Builtins::InitializeSObjectAndSFunction(const JSHandle &env) con InitializeSMap(env, sObjPrototype, sFuncPrototype); InitializeSharedArray(env, sObjPrototype, sFuncPrototype); InitializeSTypedArray(env, sObjPrototype, sFuncPrototype); + InitializeSArrayBuffer(env, sObjPrototype, sFuncPrototype); env->SetSObjectFunctionPrototype(thread_, sObjPrototype); } @@ -117,6 +121,69 @@ void Builtins::InitializeSObject(const JSHandle &env, const JSHandle< SetSAccessor(sObjPrototype, fieldIndex, protoGetter, protoSetter); } +void Builtins::InitializeSArrayBuffer(const JSHandle &env, const JSHandle &sObjPrototype, + const JSHandle &sFuncPrototype) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + // SendableArrayBuffer.prototype + JSHandle arrayBufferPrototypeHClass = CreateSArrayBufferPrototypeHClass(sObjPrototype); + JSHandle arrayBufferPrototype = + factory_->NewSharedOldSpaceJSObjectWithInit(arrayBufferPrototypeHClass); + + JSHandle arrayBufferPrototypeValue(arrayBufferPrototype); + + // SendableArrayBuffer.prototype_or_hclass + auto emptySLayout = globalConst->GetHandledEmptySLayoutInfo(); + JSHandle arrayBufferIHClass = factory_->NewSEcmaHClass( + JSSendableArrayBuffer::SIZE, 0, JSType::JS_SENDABLE_ARRAY_BUFFER, arrayBufferPrototypeValue, emptySLayout); + + // SendableArrayBuffer = new Function() + JSHandle arrayBufferFuncHClass = CreateSArrayBufferFunctionHClass(sFuncPrototype); + + JSHandle arrayBufferFunction = + factory_->NewSFunctionByHClass(reinterpret_cast(BuiltinsSendableArrayBuffer::ArrayBufferConstructor), + arrayBufferFuncHClass, FunctionKind::BUILTIN_CONSTRUCTOR); + + InitializeSCtor(arrayBufferIHClass, arrayBufferFunction, "SendableArrayBuffer", FunctionLength::ONE); + JSHandle globalObject(thread_, env->GetGlobalObject()); + JSHandle nameString(factory_->NewFromUtf8("SendableArrayBuffer")); + PropertyDescriptor desc(thread_, JSHandle::Cast(arrayBufferFunction), true, false, true); + JSObject::DefineOwnProperty(thread_, globalObject, nameString, desc); + RETURN_IF_ABRUPT_COMPLETION(thread_); + + // SendableArrayBuffer prototype method + uint32_t fieldIndex = 0; + SetSFunction(env, arrayBufferPrototype, "slice", + BuiltinsSendableArrayBuffer::Slice, fieldIndex++, FunctionLength::TWO); + + // 24.1.4.1 get SendableArrayBuffer.prototype.byteLength + JSHandle lengthGetter = + CreateSGetterSetter(env, BuiltinsSendableArrayBuffer::GetByteLength, "byteLength", FunctionLength::ZERO); + SetSAccessor( + JSHandle(arrayBufferPrototype), fieldIndex++, lengthGetter, globalConst->GetHandledUndefined()); + + // 24.1.4.4 SendableArrayBuffer.prototype[@@toStringTag] + JSHandle strTag(factory_->NewFromUtf8("SendableArrayBuffer")); + arrayBufferPrototype->SetPropertyInlinedProps(thread_, fieldIndex++, strTag.GetTaggedValue()); + + // 24.1.3.3 get SendableArrayBuffer[@@species] + fieldIndex = JSFunction::PROTOTYPE_INLINE_PROPERTY_INDEX + 1; + JSHandle speciesGetter = + CreateSGetterSetter(env, BuiltinsSendableArrayBuffer::Species, "[Symbol.species]", FunctionLength::ZERO); + SetSAccessor( + JSHandle(arrayBufferFunction), fieldIndex++, speciesGetter, globalConst->GetHandledUndefined()); + + // SendableArrayBuffer method + for (const base::BuiltinFunctionEntry& entry: BuiltinsSendableArrayBuffer::GetArrayBufferFunctions()) { + SetSFunction(env, JSHandle(arrayBufferFunction), entry.GetName(), entry.GetEntrypoint(), fieldIndex++, + entry.GetLength(), entry.GetBuiltinStubId()); + } + + env->SetSendableArrayBufferPrototype(thread_, arrayBufferPrototype); + env->SetSBuiltininArrayBufferFunction(thread_, arrayBufferFunction); +} + void Builtins::InitializeSSet(const JSHandle &env, const JSHandle &sObjPrototype, const JSHandle &sFuncPrototype) const { @@ -368,6 +435,35 @@ JSHandle Builtins::CreateSFunctionHClass(const JSHandle &s return sobjPrototypeHClass; } +JSHandle Builtins::CreateSArrayBufferFunctionHClass(const JSHandle &sFuncPrototype) const +{ + uint32_t index = 0; + auto env = vm_->GetGlobalEnv(); + PropertyAttributes attributes = PropertyAttributes::Default(false, false, false); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::TAGGED); + auto properties = BuiltinsSendableArrayBuffer::GetFunctionProperties(); + uint32_t length = properties.size(); + JSHandle keyString; + JSHandle layout = factory_->CreateSLayoutInfo(length); + for (const auto &[key, isAccessor] : properties) { + attributes.SetOffset(index); + attributes.SetIsAccessor(isAccessor); + if (key == "[Symbol.species]") { + keyString = env->GetSpeciesSymbol(); + } else { + keyString = JSHandle(factory_->NewFromUtf8(key)); + } + layout->AddKey(thread_, index++, keyString.GetTaggedValue(), attributes); + } + JSHandle sobjPrototypeHClass = + factory_->NewSEcmaHClass(JSSharedFunction::SIZE, length, JSType::JS_SHARED_FUNCTION, + JSHandle(sFuncPrototype), JSHandle(layout)); + sobjPrototypeHClass->SetConstructor(true); + sobjPrototypeHClass->SetCallable(true); + return sobjPrototypeHClass; +} + JSHandle Builtins::CreateSSetFunctionHClass(const JSHandle &sFuncPrototype) const { uint32_t index = 0; @@ -454,6 +550,35 @@ JSHandle Builtins::CreateSFunctionPrototypeHClass(const JSHandle Builtins::CreateSArrayBufferPrototypeHClass(const JSHandle &sObjPrototype) const +{ + uint32_t index = 0; + auto env = vm_->GetGlobalEnv(); + PropertyAttributes attributes = PropertyAttributes::Default(false, false, false); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::TAGGED); + auto properties = BuiltinsSendableArrayBuffer::GetPrototypeProperties(); + uint32_t length = properties.size(); + ASSERT(length == BuiltinsSendableArrayBuffer::GetNumPrototypeInlinedProperties()); + JSHandle layout = factory_->CreateSLayoutInfo(length); + JSHandle keyString; + for (const auto &[key, isAccessor] : properties) { + attributes.SetOffset(index); + attributes.SetIsAccessor(isAccessor); + if (key == "[Symbol.toStringTag]") { + keyString = env->GetToStringTagSymbol(); + } else { + keyString = JSHandle(factory_->NewFromUtf8(key)); + } + layout->AddKey(thread_, index++, keyString.GetTaggedValue(), attributes); + } + JSHandle sArrayBufferPrototypeHClass = + factory_->NewSEcmaHClass(JSSharedObject::SIZE, length, JSType::JS_SHARED_OBJECT, + JSHandle(sObjPrototype), + JSHandle(layout)); + return sArrayBufferPrototypeHClass; +} + JSHandle Builtins::CreateSSetPrototypeHClass(const JSHandle &sObjPrototype) const { uint32_t index = 0; diff --git a/ecmascript/dfx/hprof/heap_snapshot.cpp b/ecmascript/dfx/hprof/heap_snapshot.cpp index 6cb24d985c..bbe0aa0048 100644 --- a/ecmascript/dfx/hprof/heap_snapshot.cpp +++ b/ecmascript/dfx/hprof/heap_snapshot.cpp @@ -382,6 +382,8 @@ CString *HeapSnapshot::GenerateNodeName(TaggedObject *entry) return GetString("StringIterator"); case JSType::JS_ARRAY_BUFFER: return GetString("ArrayBuffer"); + case JSType::JS_SENDABLE_ARRAY_BUFFER: + return GetString("SendableArrayBuffer"); case JSType::JS_SHARED_ARRAY_BUFFER: return GetString("SharedArrayBuffer"); case JSType::JS_PROXY_REVOC_FUNCTION: diff --git a/ecmascript/dump.cpp b/ecmascript/dump.cpp index f7781ef442..74cc128ff9 100644 --- a/ecmascript/dump.cpp +++ b/ecmascript/dump.cpp @@ -110,6 +110,7 @@ #include "ecmascript/require/js_cjs_require.h" #include "ecmascript/require/js_cjs_exports.h" #include "ecmascript/shared_objects/js_shared_array.h" +#include "ecmascript/shared_objects/js_sendable_arraybuffer.h" #include "ecmascript/shared_objects/js_shared_array_iterator.h" #include "ecmascript/shared_objects/js_shared_map.h" #include "ecmascript/shared_objects/js_shared_map_iterator.h" @@ -314,6 +315,8 @@ CString JSHClass::DumpJSType(JSType type) return "RegExpIterator"; case JSType::JS_ARRAY_BUFFER: return "ArrayBuffer"; + case JSType::JS_SENDABLE_ARRAY_BUFFER: + return "SendableArrayBuffer"; case JSType::JS_SHARED_ARRAY_BUFFER: return "SharedArrayBuffer"; case JSType::JS_PROXY_REVOC_FUNCTION: @@ -915,6 +918,9 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) case JSType::JS_ARRAY_BUFFER: JSArrayBuffer::Cast(obj)->Dump(os); break; + case JSType::JS_SENDABLE_ARRAY_BUFFER: + JSSendableArrayBuffer::Cast(obj)->Dump(os); + break; case JSType::JS_SHARED_ARRAY_BUFFER: JSArrayBuffer::Cast(obj)->Dump(os); break; @@ -2914,6 +2920,14 @@ void JSArrayBuffer::Dump(std::ostream &os) const os << " - Shared: " << GetShared(); } +void JSSendableArrayBuffer::Dump(std::ostream &os) const +{ + os << " - byte-length: " << GetArrayBufferByteLength(); + os << " - buffer-data: "; + GetArrayBufferData().Dump(os); + os << " - Shared: " << GetShared(); +} + void PromiseReaction::Dump(std::ostream &os) const { os << " - promise-capability: "; @@ -4399,6 +4413,9 @@ static void DumpObject(TaggedObject *obj, std::vector &vec, bool isVm case JSType::JS_SHARED_ARRAY_BUFFER: JSArrayBuffer::Cast(obj)->DumpForSnapshot(vec); return; + case JSType::JS_SENDABLE_ARRAY_BUFFER: + JSSendableArrayBuffer::Cast(obj)->DumpForSnapshot(vec); + return; case JSType::JS_PROXY_REVOC_FUNCTION: JSProxyRevocFunction::Cast(obj)->DumpForSnapshot(vec); return; @@ -5682,6 +5699,14 @@ void JSArrayBuffer::DumpForSnapshot(std::vector &vec) const JSObject::DumpForSnapshot(vec); } +void JSSendableArrayBuffer::DumpForSnapshot(std::vector &vec) const +{ + vec.emplace_back(CString("buffer-data"), GetArrayBufferData()); + vec.emplace_back(CString("byte-length"), JSTaggedValue(GetArrayBufferByteLength())); + vec.emplace_back(CString("shared"), JSTaggedValue(GetShared())); + JSObject::DumpForSnapshot(vec); +} + void PromiseReaction::DumpForSnapshot(std::vector &vec) const { vec.emplace_back(CString("promise-capability"), GetPromiseCapability()); diff --git a/ecmascript/global_env.h b/ecmascript/global_env.h index 1109eb7537..f35f0bb92d 100644 --- a/ecmascript/global_env.h +++ b/ecmascript/global_env.h @@ -36,9 +36,9 @@ public: GLOBAL_ENV_FIELDS(GLOBAL_ENV_SLOT) static constexpr uint8_t FIRST_DETECTOR_SYMBOL_INDEX = static_cast(Field::REPLACE_SYMBOL_INDEX); static constexpr uint8_t LAST_DETECTOR_SYMBOL_INDEX = static_cast(Field::SPECIES_SYMBOL_INDEX); - static constexpr uint8_t FINAL_INDEX = static_cast(GlobalEnvField::FINAL_INDEX); + static constexpr uint16_t FINAL_INDEX = static_cast(GlobalEnvField::FINAL_INDEX); static constexpr uint8_t RESERVED_LENGTH = 1; // divide the gc area - static constexpr uint8_t JSTHREAD_INDEX = FINAL_INDEX; // not need gc + static constexpr uint16_t JSTHREAD_INDEX = FINAL_INDEX; // not need gc #undef GLOBAL_ENV_SLOT JSTaggedValue GetGlobalObject() const diff --git a/ecmascript/global_env_fields.h b/ecmascript/global_env_fields.h index e377ba9a61..39a31d99e1 100644 --- a/ecmascript/global_env_fields.h +++ b/ecmascript/global_env_fields.h @@ -226,6 +226,7 @@ V(JSTaggedValue, SObjectFunctionPrototype, SHARED_OBJECT_FUNCTION_PROTOTYPE_INDEX) \ V(JSTaggedValue, SFunctionFunction, SHARED_FUNCTION_FUNCTION_INDEX) \ V(JSTaggedValue, SFunctionPrototype, SHARED_FUNCTION_PROTOTYPE_INDEX) \ + V(JSTaggedValue, SBuiltininArrayBufferFunction, SENDABLE_BUILTIN_ARRAY_BUFFER_FUNCTION_INDEX) \ V(JSTaggedValue, SBuiltininSetFunction, SHARED_BUILTIN_SET_FUNCTION_INDEX) \ V(JSTaggedValue, SBuiltininMapFunction, SHARED_BUILTIN_MAP_FUNCTION_INDEX) \ V(JSTaggedValue, SConstructorClass, SHARED_CONSTRUCTOR_CLASS_INDEX) \ @@ -248,6 +249,7 @@ V(JSTaggedValue, UnscopablesSymbol, UNSCOPABLES_SYMBOL_INDEX) \ V(JSTaggedValue, NativeBindingSymbol, NATIVE_BINDING_SYMBOL_INDEX) \ V(JSTaggedValue, HasInstanceSymbol, HASINSTANCE_SYMBOL_INDEX) \ + V(JSTaggedValue, SendableArrayBufferPrototype, SENDABLE_ARRAY_BUFFER_PROTOTYPE_INDEX) \ V(JSTaggedValue, SharedArrayFunction, SHARED_ARRAY_FUNCTION_INDEX) \ V(JSTaggedValue, SharedArrayPrototype, SHARED_ARRAY_PROTOTYPE_INDEX) \ V(JSTaggedValue, SharedMapPrototype, SHARED_MAP_PROTOTYPE_INDEX) \ @@ -284,7 +286,7 @@ namespace panda::ecmascript { #define GLOBAL_ENV_FIELD_ENUM_ITEM(Type, Name, INDEX) INDEX, - enum class GlobalEnvField: uint8_t { + enum class GlobalEnvField: uint16_t { GLOBAL_ENV_FIELDS(GLOBAL_ENV_FIELD_ENUM_ITEM) FINAL_INDEX }; diff --git a/ecmascript/js_hclass.cpp b/ecmascript/js_hclass.cpp index 6f7c6f9eaf..f3ac335862 100644 --- a/ecmascript/js_hclass.cpp +++ b/ecmascript/js_hclass.cpp @@ -198,6 +198,7 @@ void JSHClass::Initialize(const JSThread *thread, uint32_t size, JSType type, case JSType::JS_SHARED_FLOAT64_ARRAY: case JSType::JS_SHARED_BIGINT64_ARRAY: case JSType::JS_SHARED_BIGUINT64_ARRAY: + case JSType::JS_SENDABLE_ARRAY_BUFFER: case JSType::BIGINT: case JSType::LINE_STRING: case JSType::CONSTANT_STRING: diff --git a/ecmascript/js_hclass.h b/ecmascript/js_hclass.h index 0402ef1abe..b8121d21bd 100644 --- a/ecmascript/js_hclass.h +++ b/ecmascript/js_hclass.h @@ -165,6 +165,7 @@ struct Reference; \ JS_ARRAY_BUFFER, /* ///////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_SHARED_ARRAY_BUFFER, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_SENDABLE_ARRAY_BUFFER, /* //////////////////////////////////////////////////////////////////////-PADDING */ \ JS_PROMISE, /* ///////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_DATA_VIEW, /* /////////////////////////////////////////////////////////////////////////////////////// */ \ JS_ARGUMENTS, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -1281,6 +1282,11 @@ public: return GetObjectType() == JSType::JS_SHARED_ARRAY_BUFFER; } + inline bool IsSendableArrayBuffer() const + { + return GetObjectType() == JSType::JS_SENDABLE_ARRAY_BUFFER; + } + inline bool IsDataView() const { return GetObjectType() == JSType::JS_DATA_VIEW; diff --git a/ecmascript/js_serializer.cpp b/ecmascript/js_serializer.cpp index 598becf278..fb66420c04 100644 --- a/ecmascript/js_serializer.cpp +++ b/ecmascript/js_serializer.cpp @@ -329,6 +329,7 @@ bool JSSerializer::WriteTaggedObject(const JSHandle &value) return WriteJSTypedArray(value, SerializationUID::JS_BIGUINT64_ARRAY); case JSType::JS_ARRAY_BUFFER: case JSType::JS_SHARED_ARRAY_BUFFER: + case JSType::JS_SENDABLE_ARRAY_BUFFER: return WriteJSArrayBuffer(value); case JSType::LINE_STRING: case JSType::CONSTANT_STRING: diff --git a/ecmascript/js_tagged_value-inl.h b/ecmascript/js_tagged_value-inl.h index 3e17a7988a..448634abd3 100644 --- a/ecmascript/js_tagged_value-inl.h +++ b/ecmascript/js_tagged_value-inl.h @@ -1422,6 +1422,11 @@ inline bool JSTaggedValue::IsSharedArrayBuffer() const return IsHeapObject() && GetTaggedObject()->GetClass()->IsSharedArrayBuffer(); } +inline bool JSTaggedValue::IsSendableArrayBuffer() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsSendableArrayBuffer(); +} + inline bool JSTaggedValue::IsDataView() const { return IsHeapObject() && GetTaggedObject()->GetClass()->IsDataView(); diff --git a/ecmascript/js_tagged_value.h b/ecmascript/js_tagged_value.h index 2759c60496..c76c31bba7 100644 --- a/ecmascript/js_tagged_value.h +++ b/ecmascript/js_tagged_value.h @@ -555,6 +555,7 @@ public: bool IsStringIterator() const; bool IsArrayBuffer() const; bool IsSharedArrayBuffer() const; + bool IsSendableArrayBuffer() const; bool IsJSSetIterator() const; bool IsJSSharedSetIterator() const; diff --git a/ecmascript/js_typed_array.cpp b/ecmascript/js_typed_array.cpp index 844328c750..bf71f08760 100644 --- a/ecmascript/js_typed_array.cpp +++ b/ecmascript/js_typed_array.cpp @@ -18,10 +18,12 @@ #include "ecmascript/accessor_data.h" #include "ecmascript/base/typed_array_helper-inl.h" #include "ecmascript/builtins/builtins_arraybuffer.h" +#include "ecmascript/builtins/builtins_sendable_arraybuffer.h" namespace panda::ecmascript { using TypedArrayHelper = base::TypedArrayHelper; using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer; +using BuiltinsSendableArrayBuffer = builtins::BuiltinsSendableArrayBuffer; JSHandle JSTypedArray::ToPropKey(JSThread *thread, const JSHandle &key) { @@ -91,7 +93,10 @@ bool JSTypedArray::HasProperty(JSThread *thread, const JSHandle & RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); if (!numericIndex.IsUndefined()) { JSTaggedValue buffer = typedarrayObj->GetViewedArrayBufferOrByteArray(); - if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + if (!buffer.IsSendableArrayBuffer() && BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", false); + } + if (buffer.IsSendableArrayBuffer() && BuiltinsSendableArrayBuffer::IsDetachedBuffer(buffer)) { THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", false); } if (!numericIndex.IsInteger()) { @@ -385,7 +390,11 @@ OperationResult JSTypedArray::IntegerIndexedElementGet(JSThread *thread, const J JSHandle typedarrayObj(typedarray); JSTaggedValue buffer = typedarrayObj->GetViewedArrayBufferOrByteArray(); // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. - if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + if (!buffer.IsSendableArrayBuffer() && BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + if (buffer.IsSendableArrayBuffer() && BuiltinsSendableArrayBuffer::IsDetachedBuffer(buffer)) { THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); } @@ -424,7 +433,12 @@ OperationResult JSTypedArray::IntegerIndexedElementGet(JSThread *thread, const J // 13. Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName. DataViewType elementType = TypedArrayHelper::GetType(typedarrayObj); // 14. Return GetValueFromBuffer(buffer, indexedPosition, elementType). - JSTaggedValue result = BuiltinsArrayBuffer::GetValueFromBuffer(thread, buffer, byteIndex, elementType, true); + JSTaggedValue result; + if (buffer.IsSendableArrayBuffer()) { + result = BuiltinsSendableArrayBuffer::GetValueFromBuffer(thread, buffer, byteIndex, elementType, true); + } else { + result = BuiltinsArrayBuffer::GetValueFromBuffer(thread, buffer, byteIndex, elementType, true); + } return OperationResult(thread, result, PropertyMetaData(true)); } @@ -435,7 +449,10 @@ bool JSTypedArray::IsValidIntegerIndex(const JSHandle &typedArray // 2. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return false. JSHandle typedarrayObj(typedArray); JSTaggedValue buffer = typedarrayObj->GetViewedArrayBufferOrByteArray(); - if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + if (!buffer.IsSendableArrayBuffer() && BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + return false; + } + if (buffer.IsSendableArrayBuffer() && BuiltinsSendableArrayBuffer::IsDetachedBuffer(buffer)) { return false; } // 3. If ! IsIntegralNumber(index) is false, return false. @@ -505,7 +522,12 @@ bool JSTypedArray::FastCopyElementToArray(JSThread *thread, const JSHandle bufferHandle = JSHandle(thread, typedarrayObj->GetViewedArrayBufferOrByteArray()); // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. - if (BuiltinsArrayBuffer::IsDetachedBuffer(bufferHandle.GetTaggedValue())) { + if (!bufferHandle.GetTaggedValue().IsSendableArrayBuffer() && + BuiltinsArrayBuffer::IsDetachedBuffer(bufferHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", false); + } + if (bufferHandle.GetTaggedValue().IsSendableArrayBuffer() && + BuiltinsSendableArrayBuffer::IsDetachedBuffer(bufferHandle.GetTaggedValue())) { THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", false); } @@ -523,8 +545,14 @@ bool JSTypedArray::FastCopyElementToArray(JSThread *thread, const JSHandleSet(thread, index, result); } return true; @@ -542,7 +570,10 @@ OperationResult JSTypedArray::FastElementGet(JSThread *thread, const JSHandleGetViewedArrayBufferOrByteArray(); // 10.4.5.15 TypedArrayGetElement ( O, index ) // 1. If IsValidIntegerIndex(O, index) is false, return undefined. - if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + if (!buffer.IsSendableArrayBuffer() && BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true)); + } + if (buffer.IsSendableArrayBuffer() && BuiltinsSendableArrayBuffer::IsDetachedBuffer(buffer)) { return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true)); } @@ -561,7 +592,12 @@ OperationResult JSTypedArray::FastElementGet(JSThread *thread, const JSHandleGetViewedArrayBufferOrByteArray(); - if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + if (!buffer.IsSendableArrayBuffer() && BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + return JSTaggedValue::Undefined(); + } + if (buffer.IsSendableArrayBuffer() && BuiltinsSendableArrayBuffer::IsDetachedBuffer(buffer)) { return JSTaggedValue::Undefined(); } @@ -672,6 +716,9 @@ JSTaggedValue JSTypedArray::FastGetPropertyByIndex(JSThread *thread, const JSTag // Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName. DataViewType elementType = TypedArrayHelper::GetType(jsType); // Return GetValueFromBuffer(buffer, indexedPosition, elementType). + if (buffer.IsSendableArrayBuffer()) { + return BuiltinsSendableArrayBuffer::GetValueFromBuffer(thread, buffer, byteIndex, elementType, true); + } return BuiltinsArrayBuffer::GetValueFromBuffer(thread, buffer, byteIndex, elementType, true); } @@ -709,6 +756,10 @@ JSTaggedValue JSTypedArray::FastSetPropertyByIndex(JSThread *thread, const JSTag // Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName. DataViewType elementType = TypedArrayHelper::GetType(jsType); // Perform SetValueInBuffer(buffer, indexedPosition, elementType, numValue). + if (buffer.IsSendableArrayBuffer()) { + return BuiltinsSendableArrayBuffer::FastSetValueInBuffer(thread, + buffer, byteIndex, elementType, numValue.GetNumber(), true); + } return BuiltinsArrayBuffer::FastSetValueInBuffer(thread, buffer, byteIndex, elementType, numValue.GetNumber(), true); } @@ -770,8 +821,13 @@ bool JSTypedArray::FastTypedArrayFill(JSThread *thread, const JSHandleVisitRangeSlot(visitor); break; + case JSType::JS_SENDABLE_ARRAY_BUFFER: + JSSendableArrayBuffer::Cast(object)->VisitRangeSlot(visitor); + break; case JSType::JS_SHARED_ARRAY_BUFFER: JSArrayBuffer::Cast(object)->VisitRangeSlot(visitor); break; diff --git a/ecmascript/object_factory.cpp b/ecmascript/object_factory.cpp index e8b881173e..cffe9ee9eb 100644 --- a/ecmascript/object_factory.cpp +++ b/ecmascript/object_factory.cpp @@ -125,6 +125,7 @@ #include "ecmascript/require/js_cjs_require.h" #include "ecmascript/shared_mm/shared_mm.h" #include "ecmascript/shared_objects/js_shared_array.h" +#include "ecmascript/shared_objects/js_sendable_arraybuffer.h" #include "ecmascript/shared_objects/js_shared_array_iterator.h" #include "ecmascript/shared_objects/js_shared_map.h" #include "ecmascript/shared_objects/js_shared_map_iterator.h" @@ -279,6 +280,41 @@ void ObjectFactory::NewJSArrayBufferData(const JSHandle &array, i vm_->GetNativeAreaAllocator()->IncreaseNativeSizeStats(length, NativeFlag::ARRAY_BUFFER); } +void ObjectFactory::NewJSSendableArrayBufferData(const JSHandle &array, int32_t length) +{ + if (length == 0) { + return; + } + + JSTaggedValue data = array->GetArrayBufferData(); + size_t size = static_cast(length) * sizeof(uint8_t); + NativeAreaAllocator *nativeAreaAllocator = sHeap_->GetNativeAreaAllocator(); + if (!data.IsUndefined()) { + auto *pointer = JSNativePointer::Cast(data.GetTaggedObject()); + auto newData = nativeAreaAllocator->AllocateBuffer(size); + if (memset_s(newData, length, 0, length) != EOK) { + LOG_FULL(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + pointer->ResetExternalPointer(newData); + nativeAreaAllocator->ModifyNativeSizeStats(pointer->GetBindingSize(), size, + NativeFlag::ARRAY_BUFFER); + return; + } + + auto newData = nativeAreaAllocator->AllocateBuffer(size); + if (memset_s(newData, length, 0, length) != EOK) { + LOG_FULL(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + JSHandle pointer = NewSJSNativePointer(newData, NativeAreaAllocator::FreeBufferFunc, + nativeAreaAllocator, false, size, + NativeFlag::ARRAY_BUFFER); + array->SetArrayBufferData(thread_, pointer); + array->SetWithNativeAreaAllocator(true); + nativeAreaAllocator->IncreaseNativeSizeStats(length, NativeFlag::ARRAY_BUFFER); +} + void ObjectFactory::NewJSSharedArrayBufferData(const JSHandle &array, int32_t length) { if (length == 0) { @@ -1324,6 +1360,10 @@ void ObjectFactory::InitializeJSObject(const JSHandle &obj, const JSHa JSArrayBuffer::Cast(*obj)->SetArrayBufferByteLength(0); JSArrayBuffer::Cast(*obj)->SetShared(true); break; + case JSType::JS_SENDABLE_ARRAY_BUFFER: + JSSendableArrayBuffer::Cast(*obj)->SetArrayBufferData(thread_, JSTaggedValue::Undefined()); + JSSendableArrayBuffer::Cast(*obj)->SetArrayBufferByteLength(0); + break; case JSType::JS_PROMISE: JSPromise::Cast(*obj)->SetPromiseState(PromiseState::PENDING); JSPromise::Cast(*obj)->SetPromiseResult(thread_, JSTaggedValue::Undefined()); diff --git a/ecmascript/object_factory.h b/ecmascript/object_factory.h index 7d05dffeb6..5219ec7fbf 100644 --- a/ecmascript/object_factory.h +++ b/ecmascript/object_factory.h @@ -74,6 +74,7 @@ class JSGeneratorObject; class CompletionRecord; class GeneratorContext; class JSArrayBuffer; +class JSSendableArrayBuffer; class JSDataView; class JSPromise; class JSPromiseReactionsFunction; @@ -497,6 +498,7 @@ public: bool canShareHClass); void NewJSArrayBufferData(const JSHandle &array, int32_t length); + void NewJSSendableArrayBufferData(const JSHandle &array, int32_t length); JSHandle NewJSArrayBuffer(int32_t length); diff --git a/ecmascript/runtime_call_id.h b/ecmascript/runtime_call_id.h index 4be21747c9..fd4626367a 100644 --- a/ecmascript/runtime_call_id.h +++ b/ecmascript/runtime_call_id.h @@ -345,6 +345,15 @@ namespace panda::ecmascript { V(SharedArrayBuffer, IsView) \ V(SharedArrayBuffer, Species) \ V(SharedArrayBuffer, GetByteLength) \ + V(SendableArrayBuffer, Constructor) \ + V(SendableArrayBuffer, Slice) \ + V(SendableArrayBuffer, Species) \ + V(SendableArrayBuffer, GetByteLength) \ + V(SendableArrayBuffer, GetValueFromBuffer) \ + V(SendableArrayBuffer, SetValueInBuffer) \ + V(SendableArrayBuffer, CloneArrayBuffer) \ + V(SendableArrayBuffer, AllocateSendableArrayBuffer) \ + V(SendableArrayBuffer, IsView) \ V(AsyncFromSyncIterator, Next) \ V(AsyncFromSyncIterator, Throw) \ V(AsyncFromSyncIterator, Return) \ diff --git a/ecmascript/shared_objects/js_sendable_arraybuffer.cpp b/ecmascript/shared_objects/js_sendable_arraybuffer.cpp new file mode 100644 index 0000000000..47a1578fad --- /dev/null +++ b/ecmascript/shared_objects/js_sendable_arraybuffer.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/shared_objects/js_sendable_arraybuffer.h" + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/mem/barriers-inl.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/platform/os.h" +#include "ecmascript/tagged_array.h" + +#include "securec.h" + +namespace panda::ecmascript { +void JSSendableArrayBuffer::CopyDataBlockBytes( + JSTaggedValue toBlock, JSTaggedValue fromBlock, int32_t fromIndex, int32_t count) +{ + void *fromBuf = JSNativePointer::Cast(fromBlock.GetTaggedObject())->GetExternalPointer(); + void *toBuf = JSNativePointer::Cast(toBlock.GetTaggedObject())->GetExternalPointer(); + CopyDataPointBytes(toBuf, fromBuf, fromIndex, count); +} + +void JSSendableArrayBuffer::CopyDataPointBytes(void *toBuf, void *fromBuf, int32_t fromIndex, int32_t count) +{ + auto *from = static_cast(fromBuf); + auto *to = static_cast(toBuf); + if (memcpy_s(to, count, from + fromIndex, count) != EOK) { // NOLINT + LOG_FULL(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } +} + +void JSSendableArrayBuffer::Attach(JSThread *thread, uint32_t arrayBufferByteLength, + JSTaggedValue arrayBufferData, bool transferWithNativeAreaAllocator) +{ + ASSERT(arrayBufferData.IsJSNativePointer()); + // only in transition, should the JSSendableArrayBuffer with NativeAreaAllocator increase mem usage + if (transferWithNativeAreaAllocator) { + LOG_FULL(DEBUG) << "attaching for transfer"; + JSHandle np(thread, arrayBufferData.GetTaggedObject()); + NativeAreaAllocator *allocator = thread->GetEcmaVM()->GetNativeAreaAllocator(); + allocator->IncreaseNativeMemoryUsage(MallocUsableSize(np->GetExternalPointer())); + np->SetData(allocator); + } + SetArrayBufferByteLength(arrayBufferByteLength); + SetArrayBufferData(thread, arrayBufferData); +} + +void JSSendableArrayBuffer::Detach(JSThread *thread, bool transferWithNativeAreaAllocator) +{ + JSTaggedValue arrayBufferData = GetArrayBufferData(); + // already detached. + if (arrayBufferData.IsNull()) { + return; + } + + // only in transition, should the JSSendableArrayBuffer with NativeAreaAllocator decrease mem usage + if (transferWithNativeAreaAllocator) { + LOG_FULL(DEBUG) << "detaching for transfer"; + JSHandle np(thread, arrayBufferData.GetTaggedObject()); + NativeAreaAllocator *allocator = thread->GetEcmaVM()->GetNativeAreaAllocator(); + allocator->DecreaseNativeMemoryUsage(MallocUsableSize(np->GetExternalPointer())); + np->SetData(nullptr); + } + SetArrayBufferData(thread, JSTaggedValue::Null()); + SetArrayBufferByteLength(0); +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/shared_objects/js_sendable_arraybuffer.h b/ecmascript/shared_objects/js_sendable_arraybuffer.h new file mode 100644 index 0000000000..cd828c69dd --- /dev/null +++ b/ecmascript/shared_objects/js_sendable_arraybuffer.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ECMASCRIPT_JS_SENDABLE_ARRAYBUFFER_H +#define ECMASCRIPT_JS_SENDABLE_ARRAYBUFFER_H + +#include "ecmascript/js_object.h" + +namespace panda::ecmascript { +class JSSendableArrayBuffer final : public JSObject { +public: + CAST_NO_CHECK(JSSendableArrayBuffer); + + // 6.2.6.2 + static void CopyDataBlockBytes(JSTaggedValue toBlock, JSTaggedValue fromBlock, int32_t fromIndex, int32_t count); + static void CopyDataPointBytes(void *toBuf, void *fromBuf, int32_t fromIndex, int32_t count); + + void Attach(JSThread *thread, uint32_t arrayBufferByteLength, JSTaggedValue arrayBufferData, + bool transferWithNativeAreaAllocator = false); + void Detach(JSThread *thread, bool transferWithNativeAreaAllocator = false); + + bool IsDetach() + { + JSTaggedValue arrayBufferData = GetArrayBufferData(); + return arrayBufferData.IsNull(); + } + + static constexpr size_t DATA_OFFSET = JSObject::SIZE; + ACCESSORS(ArrayBufferData, DATA_OFFSET, BYTE_LENGTH_OFFSET) + ACCESSORS_PRIMITIVE_FIELD(ArrayBufferByteLength, uint32_t, BYTE_LENGTH_OFFSET, BIT_FIELD_OFFSET) + ACCESSORS_BIT_FIELD(BitField, BIT_FIELD_OFFSET, LAST_OFFSET) + DEFINE_ALIGN_SIZE(LAST_OFFSET); + + // define BitField + static constexpr size_t SHARED_BITS = 1; + static constexpr size_t WITH_NATIVE_AREA_ALLOCATOR_BITS = 1; + + FIRST_BIT_FIELD(BitField, Shared, bool, SHARED_BITS) + NEXT_BIT_FIELD(BitField, WithNativeAreaAllocator, bool, WITH_NATIVE_AREA_ALLOCATOR_BITS, Shared) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, DATA_OFFSET, BYTE_LENGTH_OFFSET) + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_SENDABLE_ARRAYBUFFER_H \ No newline at end of file diff --git a/ecmascript/tests/dump_test.cpp b/ecmascript/tests/dump_test.cpp index 49942b67a1..04a6baba8c 100644 --- a/ecmascript/tests/dump_test.cpp +++ b/ecmascript/tests/dump_test.cpp @@ -113,6 +113,7 @@ #include "ecmascript/module/js_module_source_text.h" #include "ecmascript/object_factory.h" #include "ecmascript/shared_objects/js_shared_array.h" +#include "ecmascript/shared_objects/js_sendable_arraybuffer.h" #include "ecmascript/shared_objects/js_shared_array_iterator.h" #include "ecmascript/shared_objects/js_shared_map.h" #include "ecmascript/shared_objects/js_shared_map_iterator.h" @@ -829,6 +830,11 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) NEW_OBJECT_AND_DUMP(JSArrayBuffer, JS_ARRAY_BUFFER); break; } + case JSType::JS_SENDABLE_ARRAY_BUFFER: { + CHECK_DUMP_FIELDS(JSObject::SIZE, JSSendableArrayBuffer::SIZE, 2U); + NEW_OBJECT_AND_DUMP(JSSendableArrayBuffer, JS_SENDABLE_ARRAY_BUFFER); + break; + } case JSType::JS_PROMISE: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSPromise::SIZE, 4U); NEW_OBJECT_AND_DUMP(JSPromise, JS_PROMISE);