Files
ark_js_runtime/ecmascript/builtins/builtins_arraybuffer.cpp
T
wanyanglan 86cfe52253 add ark js_runtime
Signed-off-by: wanyanglan <wanyanglan1@huawei.com>
Change-Id: Iac7851492f10c0ef1303c621febb65e5fdd22e41
2021-09-05 16:05:06 +08:00

593 lines
26 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (c) 2021 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_arraybuffer.h"
#include <typeinfo>
#include "ecmascript/base/builtins_base.h"
#include "ecmascript/base/number_helper.h"
#include "ecmascript/ecma_macros.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/global_env.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 "securec.h"
namespace panda::ecmascript::builtins {
// 24.1.2.1 ArrayBuffer(length)
JSTaggedValue BuiltinsArrayBuffer::ArrayBufferConstructor(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), ArrayBuffer, Constructor);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> 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<JSTaggedValue> lengthHandle = GetCallArg(argv, 0);
JSTaggedNumber lenNum = JSTaggedValue::ToIndex(thread, lengthHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
double length = lenNum.GetNumber();
return AllocateArrayBuffer(thread, newTarget, length);
}
// 24.1.3.1 ArrayBuffer.isView(arg)
JSTaggedValue BuiltinsArrayBuffer::IsView(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
[[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
JSHandle<JSTaggedValue> arg = GetCallArg(argv, 0);
// 1. If Type(arg) is not Object, return false.
if (!arg->IsECMAObject()) {
return BuiltinsArrayBuffer::GetTaggedBoolean(false);
}
// 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true.
if (arg->IsDataView() || arg->IsTypedArray()) {
return BuiltinsArrayBuffer::GetTaggedBoolean(true);
}
// 3. Return false.
return BuiltinsArrayBuffer::GetTaggedBoolean(false);
}
// 24.1.3.3 get ArrayBuffer [ @@species ]
JSTaggedValue BuiltinsArrayBuffer::Species(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
return GetThis(argv).GetTaggedValue();
}
// 24.1.4.1 get ArrayBuffer.prototype.byteLength
JSTaggedValue BuiltinsArrayBuffer::GetByteLength(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
[[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
JSThread *thread = argv->GetThread();
// 1. Let O be the this value.
JSHandle<JSTaggedValue> 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->IsArrayBuffer()) {
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<JSArrayBuffer> arrBuf(thisHandle);
// 5. Let length be the value of Os [[ArrayBufferByteLength]] internal slot.
JSTaggedValue length = arrBuf->GetArrayBufferByteLength();
// 6. Return length.
return JSTaggedValue(length);
}
// 24.1.4.3 ArrayBuffer.prototype.slice(start, end)
JSTaggedValue BuiltinsArrayBuffer::Slice(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), ArrayBuffer, Slice);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
// 1. Let O be the this value.
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
// 2. If Type(O) is not Object, throw a TypeError exception.
if (!thisHandle->IsObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception());
}
JSHandle<JSArrayBuffer> arrBuf(thisHandle);
// 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
if (!thisHandle->IsArrayBuffer()) {
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 Os [[ArrayBufferByteLength]] internal slot.
JSTaggedNumber lengthNum = JSTaggedNumber::FromIntOrDouble(thread, arrBuf->GetArrayBufferByteLength());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> 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 len = lengthNum.ToInt32();
int32_t start = base::NumberHelper::DoubleInRangeInt32(relativeStart.GetNumber());
int32_t end;
int32_t first;
int32_t last;
// 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<JSTaggedValue> 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).
int32_t newLen = std::max((last - first), 0);
// 13. Let ctor be SpeciesConstructor(O, %ArrayBuffer%).
JSHandle<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
JSHandle<JSObject> objHandle(thisHandle);
JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
// 14. ReturnIfAbrupt(ctor).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 15. Let new be Construct(ctor, «newLen»).
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
JSHandle<TaggedArray> arguments = factory->NewTaggedArray(1);
arguments->Set(thread, 0, JSTaggedValue(newLen));
JSTaggedValue taggedNewArrBuf = JSFunction::Construct(thread, constructor, arguments, undefined);
JSHandle<JSTaggedValue> 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->IsArrayBuffer()) {
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<JSArrayBuffer> newJsArrBuf(newArrBuf);
// 20. If the value of news [[ArrayBufferByteLength]] internal slot < newLen, throw a TypeError exception.
JSTaggedNumber newLengthNum = JSTaggedNumber::FromIntOrDouble(thread, newJsArrBuf->GetArrayBufferByteLength());
int32_t newArrBufLen = newLengthNum.ToInt32();
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 Os [[ArrayBufferData]] internal slot.
JSTaggedValue from = arrBuf->GetArrayBufferData();
// 24. Let toBuf be the value of news [[ArrayBufferData]] internal slot.
JSTaggedValue to = newJsArrBuf->GetArrayBufferData();
// 25. Perform CopyDataBlockBytes(toBuf, fromBuf, first, newLen).
JSArrayBuffer::CopyDataBlockBytes(to, from, first, newLen);
}
// Return new.
return newArrBuf.GetTaggedValue();
}
// 24.1.1.1 AllocateArrayBuffer(constructor, byteLength)
JSTaggedValue BuiltinsArrayBuffer::AllocateArrayBuffer(JSThread *thread, const JSHandle<JSTaggedValue> &newTarget,
double byteLength)
{
BUILTINS_API_TRACE(thread, ArrayBuffer, AllocateArrayBuffer);
/**
* 1. Let obj be OrdinaryCreateFromConstructor(constructor, "%ArrayBufferPrototype%",
* «[[ArrayBufferData]], [[ArrayBufferByteLength]]» ).
* */
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> arrBufFunc = env->GetArrayBufferFunction();
JSHandle<JSObject> obj;
if (!newTarget->IsBoundFunction()) {
obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(arrBufFunc), newTarget);
// 2. ReturnIfAbrupt
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
JSHandle<JSTaggedValue> prototypeKey = thread->GlobalConstants()->GetHandledPrototypeString();
JSHandle<JSTaggedValue> constructTag(newTarget);
JSHandle<JSTaggedValue> constructProto =
JSTaggedValue::GetProperty(thread, constructTag, prototypeKey).GetValue();
obj = JSObject::ObjectCreate(thread, JSHandle<JSObject>(constructProto));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
// 3. Assert: byteLength is a positive integer.
ASSERT(JSTaggedValue(byteLength).IsInteger());
ASSERT(byteLength >= 0);
// 4. Let block be CreateByteDataBlock(byteLength).
if (byteLength > INT_MAX) {
THROW_RANGE_ERROR_AND_RETURN(thread, "Out of range", JSTaggedValue::Exception());
}
JSHandle<JSArrayBuffer> arrayBuffer(obj);
// 6. Set objs [[ArrayBufferData]] internal slot to block.
factory->NewJSArrayBufferData(arrayBuffer, byteLength);
// 7. Set objs [[ArrayBufferByteLength]] internal slot to byteLength.
arrayBuffer->SetArrayBufferByteLength(thread, JSTaggedValue(static_cast<int32_t>(byteLength)));
// 8. Return obj.
return arrayBuffer.GetTaggedValue();
}
// 24.1.1.2 IsDetachedBuffer()
bool BuiltinsArrayBuffer::IsDetachedBuffer(JSTaggedValue arrayBuffer)
{
// 1. Assert: Type(arrayBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
ASSERT(arrayBuffer.IsArrayBuffer());
JSArrayBuffer *buffer = JSArrayBuffer::Cast(arrayBuffer.GetTaggedObject());
JSTaggedValue dataSlot = buffer->GetArrayBufferData();
// 2. If arrayBuffers [[ArrayBufferData]] internal slot is null, return true.
// 3. Return false.
return dataSlot == JSTaggedValue::Null();
}
// 24.1.1.4
JSTaggedValue BuiltinsArrayBuffer::CloneArrayBuffer(JSThread *thread, const JSHandle<JSTaggedValue> &srcBuffer,
int32_t srcByteOffset, JSHandle<JSTaggedValue> constructor)
{
BUILTINS_API_TRACE(thread, ArrayBuffer, CloneArrayBuffer);
// 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
ASSERT(srcBuffer->IsArrayBuffer());
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
// 2. If cloneConstructor is not present
if (constructor->IsUndefined()) {
// a. Let cloneConstructor be SpeciesConstructor(srcBuffer, %ArrayBuffer%).
JSHandle<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
JSHandle<JSObject> 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 srcBuffers [[ArrayBufferByteLength]] internal slot.
JSHandle<JSArrayBuffer> arrBuf(srcBuffer);
JSTaggedNumber lengthNumber = JSTaggedNumber::FromIntOrDouble(thread, arrBuf->GetArrayBufferByteLength());
int32_t srcLen = lengthNumber.ToInt32();
// 5. Assert: srcByteOffset ≤ srcLength.
ASSERT(srcByteOffset <= srcLen);
// 6. Let cloneLength be srcLength srcByteOffset.
int32_t cloneLen = srcLen - srcByteOffset;
// 8. Let targetBuffer be AllocateArrayBuffer(cloneConstructor, cloneLength).
JSTaggedValue taggedBuf = AllocateArrayBuffer(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 targetBuffers [[ArrayBufferData]] internal slot.
JSHandle<JSArrayBuffer> newArrBuf(thread, taggedBuf);
// Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, cloneLength).
// 7. Let srcBlock be the value of srcBuffers [[ArrayBufferData]] internal slot.
JSTaggedValue srcBlock = arrBuf->GetArrayBufferData();
JSTaggedValue targetBlock = newArrBuf->GetArrayBufferData();
if (cloneLen > 0) {
JSArrayBuffer::CopyDataBlockBytes(targetBlock, srcBlock, srcByteOffset, cloneLen);
}
return taggedBuf;
}
// 24.1.1.5
// NOLINTNEXTLINE(readability-function-size)
JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSTaggedValue arrBuf, int32_t byteIndex, DataViewType type,
bool littleEndian)
{
JSArrayBuffer *jsArrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject());
JSTaggedValue data = jsArrayBuffer->GetArrayBufferData();
void *pointer = JSNativePointer::Cast(data.GetTaggedObject())->GetExternalPointer();
auto *block = reinterpret_cast<uint8_t *>(pointer);
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<int8_t>(res);
return GetTaggedInt(int8Res);
}
case DataViewType::UINT16:
return GetValueFromBufferForInteger<uint16_t, NumberSize::UINT16>(block, byteIndex, littleEndian);
case DataViewType::INT16:
return GetValueFromBufferForInteger<int16_t, NumberSize::INT16>(block, byteIndex, littleEndian);
case DataViewType::UINT32:
return GetValueFromBufferForInteger<uint32_t, NumberSize::UINT32>(block, byteIndex, littleEndian);
case DataViewType::INT32:
return GetValueFromBufferForInteger<int32_t, NumberSize::INT32>(block, byteIndex, littleEndian);
case DataViewType::FLOAT32:
return GetValueFromBufferForFloat<float, NumberSize::FLOAT32>(block, byteIndex, littleEndian);
case DataViewType::FLOAT64:
return GetValueFromBufferForFloat<double, NumberSize::FLOAT64>(block, byteIndex, littleEndian);
default:
break;
}
UNREACHABLE();
}
// 24.1.1.6
JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSTaggedValue arrBuf, int32_t byteIndex, DataViewType type,
JSTaggedNumber value, bool littleEndian)
{
JSArrayBuffer *jsArrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject());
JSTaggedValue data = jsArrayBuffer->GetArrayBufferData();
void *pointer = JSNativePointer::Cast(data.GetTaggedObject())->GetExternalPointer();
auto *block = reinterpret_cast<uint8_t *>(pointer);
double val = value.GetNumber();
switch (type) {
case DataViewType::UINT8:
SetValueInBufferForByte<uint8_t>(val, block, byteIndex);
break;
case DataViewType::UINT8_CLAMPED:
SetValueInBufferForUint8Clamped(val, block, byteIndex);
break;
case DataViewType::INT8:
SetValueInBufferForByte<int8_t>(val, block, byteIndex);
break;
case DataViewType::UINT16:
SetValueInBufferForInteger<uint16_t>(val, block, byteIndex, littleEndian);
break;
case DataViewType::INT16:
SetValueInBufferForInteger<int16_t>(val, block, byteIndex, littleEndian);
break;
case DataViewType::UINT32:
SetValueInBufferForInteger<uint32_t>(val, block, byteIndex, littleEndian);
break;
case DataViewType::INT32:
SetValueInBufferForInteger<int32_t>(val, block, byteIndex, littleEndian);
break;
case DataViewType::FLOAT32:
SetValueInBufferForFloat<float>(val, block, byteIndex, littleEndian);
break;
case DataViewType::FLOAT64:
SetValueInBufferForFloat<double>(val, block, byteIndex, littleEndian);
break;
default:
UNREACHABLE();
}
return JSTaggedValue::Undefined();
}
template <typename T>
void BuiltinsArrayBuffer::SetTypeData(uint8_t *block, T value, int32_t index)
{
int32_t sizeCount = sizeof(T);
auto *res = reinterpret_cast<uint8_t *>(&value);
for (int i = 0; i < sizeCount; i++) {
*(block + index + i) = *(res + i); // NOLINT
}
}
template <typename T>
T BuiltinsArrayBuffer::TransformIntToBigEndian(T liValue)
{
uint8_t sizeCount = sizeof(T);
T biValue;
if (sizeCount == 2) { // 2:2 means size of T
biValue = ((liValue & 0x00FF) << BITS_EIGHT) // NOLINT
| ((liValue & 0xFF00) >> BITS_EIGHT); // NOLINT
} else {
biValue = ((liValue & 0x000000FF) << BITS_TWENTY_FOUR) // NOLINT
| ((liValue & 0x0000FF00) << BITS_EIGHT) // NOLINT
| ((liValue & 0x00FF0000) >> BITS_EIGHT) // NOLINT
| ((liValue & 0xFF000000) >> BITS_TWENTY_FOUR); // NOLINT
}
return biValue;
}
template <typename T>
T BuiltinsArrayBuffer::TransformFloatToBigEndian(T fValue)
{
static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>, "T must be float type");
constexpr int32_t size = sizeof(T);
uint8_t s[size]; // NOLINT
uint8_t t[size]; // NOLINT
if (memcpy_s(s, size, &fValue, size) != EOK) {
LOG_ECMA(FATAL) << "memcpy_s failed";
UNREACHABLE();
}
for (int i = 0, j = size - 1; i < size && j >= 0; i++, j--) {
t[i] = s[j];
}
T res;
if (memcpy_s(&res, size, t, size) != EOK) {
LOG_ECMA(FATAL) << "memset_s failed";
UNREACHABLE();
}
return res;
}
template <typename T>
T BuiltinsArrayBuffer::TransformByteToNumber(uint8_t *block, int32_t index, uint8_t *tmpArr, int32_t size)
{
if (memcpy_s(tmpArr, size, block + index, size) != EOK) { // NOLINT
LOG_ECMA(FATAL) << "memcpy_s failed";
UNREACHABLE();
}
auto *arr = reinterpret_cast<T *>(tmpArr);
T res = *arr;
return res;
}
template <typename T, BuiltinsArrayBuffer::NumberSize size>
JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForInteger(uint8_t *block, int32_t byteIndex, bool littleEndian)
{
static_assert(std::is_integral_v<T>, "T must be integral");
static_assert(sizeof(T) == size, "Invalid number size");
static_assert(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
ASSERT(size >= NumberSize::UINT16 || size <= NumberSize::FLOAT64);
uint8_t tmpArr[size]; // NOLINT
int32_t capacity = sizeof(T);
auto res = TransformByteToNumber<T>(block, byteIndex, tmpArr, capacity);
if (!littleEndian) {
res = TransformIntToBigEndian(res);
}
// uint32_t maybe overflow with TaggedInt
// NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon)
if constexpr (std::is_same_v<T, uint32_t>) {
// NOLINTNEXTLINE(clang-diagnostic-sign-compare)
if (res > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
return GetTaggedDouble(static_cast<double>(res));
}
}
return GetTaggedInt(res);
}
template <typename T, BuiltinsArrayBuffer::NumberSize size>
JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForFloat(uint8_t *block, int32_t byteIndex, bool littleEndian)
{
static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>, "T must be float type");
static_assert(sizeof(T) == size, "Invalid number size");
uint8_t tmpArr[size]; // NOLINT
int32_t capacity = sizeof(T);
auto tmp = TransformByteToNumber<T>(block, byteIndex, tmpArr, capacity);
// NOLINTNEXTLINE(readability-braces-around-statements)
if constexpr (std::is_same_v<T, float>) {
if (std::isnan(tmp)) {
return GetTaggedDouble(tmp);
}
} else if constexpr (std::is_same_v<T, double>) { // NOLINTNEXTLINE(readability-braces-around-statements)
if (std::isnan(tmp) && !JSTaggedValue::IsImpureNaN(tmp)) {
return GetTaggedDouble(tmp);
}
}
if (!littleEndian) {
T res = TransformFloatToBigEndian(tmp);
return GetTaggedDouble(res);
}
return GetTaggedDouble(tmp);
}
template <typename T>
void BuiltinsArrayBuffer::SetValueInBufferForByte(double val, uint8_t *block, int32_t byteIndex)
{
static_assert(std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t>, "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<int64_t>(val);
auto *resArr = reinterpret_cast<T *>(&int64Val);
res = *resArr;
SetTypeData(block, res, byteIndex);
}
void BuiltinsArrayBuffer::SetValueInBufferForUint8Clamped(double val, uint8_t *block, int32_t byteIndex)
{
uint8_t res;
if (std::isnan(val) || val <= 0) {
res = 0;
SetTypeData(block, res, byteIndex);
return;
}
val = val >= UINT8_MAX ? UINT8_MAX : val;
constexpr double HALF = 0.5;
val = val == HALF ? 0 : std::round(val);
res = static_cast<int64_t>(val);
SetTypeData(block, res, byteIndex);
}
template <typename T>
void BuiltinsArrayBuffer::SetValueInBufferForInteger(double val, uint8_t *block, int32_t byteIndex, bool littleEndian)
{
static_assert(std::is_integral_v<T>, "T must be integral");
static_assert(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<int64_t>(val);
// NOLINTNEXTLINE(readability-braces-around-statements)
if constexpr (std::is_same_v<T, uint16_t>) {
auto *pTmp = reinterpret_cast<int16_t *>(&int64Val);
int16_t tmp = *pTmp;
res = static_cast<T>(tmp);
} else { // NOLINTNEXTLINE(readability-braces-around-statements)
auto *pTmp = reinterpret_cast<T *>(&int64Val);
res = *pTmp;
}
if (!littleEndian) {
res = TransformIntToBigEndian<T>(res);
}
SetTypeData(block, res, byteIndex);
}
template <typename T>
void BuiltinsArrayBuffer::SetValueInBufferForFloat(double val, uint8_t *block, int32_t byteIndex, bool littleEndian)
{
static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>, "T must be float type");
auto data = static_cast<T>(val);
if (std::isnan(val)) {
SetTypeData(block, data, byteIndex);
return;
}
if (!littleEndian) {
auto res = TransformFloatToBigEndian<T>(data);
SetTypeData(block, res, byteIndex);
return;
}
SetTypeData(block, data, byteIndex);
}
} // namespace panda::ecmascript::builtins