* Copyright (c) 2025 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 "ecmascript/compiler/builtins/builtins_arraybuffer_stub_builder.h"
#include "ecmascript/compiler/stub_builder-inl.h"
namespace panda::ecmascript::kungfu {
GateRef BuiltinsArrayBufferStubBuilder::GetValueFromBuffer(GateRef glue, GateRef pointer,
GateRef byteIndex, GateRef isLittleEndian, DataViewType type)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
DEFVARIABLE(result, VariableType::JS_ANY(), Undefined());
switch (type) {
case DataViewType::UINT8: {
GateRef byte = LoadPrimitive(VariableType::INT8(), pointer, byteIndex);
result = IntToTaggedPtr(ZExtInt8ToInt32(byte));
Jump(&exit);
break;
}
case DataViewType::INT8: {
GateRef byte = LoadPrimitive(VariableType::INT8(), pointer, byteIndex);
result = IntToTaggedPtr(SExtInt8ToInt32(byte));
Jump(&exit);
break;
}
case DataViewType::UINT16:
case DataViewType::INT16:
case DataViewType::UINT32:
case DataViewType::INT32:
result = GetValueFromBufferForInteger(glue, pointer, byteIndex, isLittleEndian, type);
Jump(&exit);
break;
case DataViewType::FLOAT32:
case DataViewType::FLOAT64:
result = GetValueFromBufferForFloat(glue, pointer, byteIndex, isLittleEndian, type);
Jump(&exit);
break;
default:
LOG_ECMA(FATAL) << "Unsupported DataViewType";
UNREACHABLE();
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayBufferStubBuilder::GetValueFromBufferForInteger(GateRef glue, GateRef pointer,
GateRef offset, GateRef littleEndianHandle, DataViewType type)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
DEFVARIABLE(result, VariableType::JS_ANY(), Undefined());
uint32_t byteSize = 0;
switch (type) {
case DataViewType::INT16:
case DataViewType::UINT16:
byteSize = 2;
break;
case DataViewType::INT32:
case DataViewType::UINT32:
byteSize = 4;
break;
default:
LOG_ECMA(FATAL) << "Invalid integer type";
UNREACHABLE();
}
GateRef value32 = ReadAndAssembleBytes(glue, pointer, offset, littleEndianHandle, byteSize);
switch (type) {
case DataViewType::INT16: {
GateRef value16 = TruncInt32ToInt16(value32);
GateRef signExtended = SExtInt16ToInt32(value16);
result = IntToTaggedPtr(signExtended);
Jump(&exit);
break;
}
case DataViewType::UINT16: {
GateRef value16 = TruncInt32ToInt16(value32);
GateRef zeroExtended = ZExtInt16ToInt32(value16);
result = IntToTaggedPtr(zeroExtended);
Jump(&exit);
break;
}
case DataViewType::INT32: {
result = IntToTaggedPtr(value32);
Jump(&exit);
break;
}
case DataViewType::UINT32: {
Label overflow(env);
Label notOverflow(env);
BRANCH(Int32UnsignedGreaterThan(value32, Int32(INT32_MAX)), &overflow, ¬Overflow);
Bind(&overflow);
{
result = DoubleToTaggedDoublePtr(ChangeUInt32ToFloat64(value32));
Jump(&exit);
}
Bind(¬Overflow);
{
result = IntToTaggedPtr(value32);
Jump(&exit);
}
break;
}
default:
LOG_ECMA(FATAL) << "Unreachable";
UNREACHABLE();
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayBufferStubBuilder::GetValueFromBufferForFloat(GateRef glue, GateRef pointer,
GateRef offset, GateRef littleEndianHandle, DataViewType type)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
DEFVARIABLE(result, VariableType::JS_ANY(), Undefined());
switch (type) {
case DataViewType::FLOAT32: {
uint32_t byteSize = 4;
GateRef rawBits = ReadAndAssembleBytes(glue, pointer, offset, littleEndianHandle, byteSize);
GateRef floatValue = CastInt32ToFloat32(rawBits);
GateRef doubleValue = ExtFloat32ToDouble(floatValue);
Label isNaN(env);
Label notNaN(env);
BRANCH_UNLIKELY(env->GetBuilder()->DoubleIsNAN(doubleValue), &isNaN, ¬NaN);
Bind(&isNaN);
{
result = DoubleToTaggedDoublePtr(Double(base::NAN_VALUE));
Jump(&exit);
}
Bind(¬NaN);
{
result = DoubleToTaggedDoublePtr(doubleValue);
Jump(&exit);
}
break;
}
case DataViewType::FLOAT64: {
GateRef b0 = ZExtInt8ToInt64(LoadPrimitive(VariableType::INT8(), pointer, offset));
GateRef b1 = ZExtInt8ToInt64(LoadPrimitive(VariableType::INT8(), pointer, Int32Add(offset, Int32(1))));
GateRef b2 = ZExtInt8ToInt64(LoadPrimitive(VariableType::INT8(), pointer, Int32Add(offset, Int32(2))));
GateRef b3 = ZExtInt8ToInt64(LoadPrimitive(VariableType::INT8(), pointer, Int32Add(offset, Int32(3))));
GateRef b4 = ZExtInt8ToInt64(LoadPrimitive(VariableType::INT8(), pointer, Int32Add(offset, Int32(4))));
GateRef b5 = ZExtInt8ToInt64(LoadPrimitive(VariableType::INT8(), pointer, Int32Add(offset, Int32(5))));
GateRef b6 = ZExtInt8ToInt64(LoadPrimitive(VariableType::INT8(), pointer, Int32Add(offset, Int32(6))));
GateRef b7 = ZExtInt8ToInt64(LoadPrimitive(VariableType::INT8(), pointer, Int32Add(offset, Int32(7))));
Label littleEnd(env);
Label bigEnd(env);
Label assembleExit(env);
DEFVARIABLE(rawBits64, VariableType::INT64(), Int64(0));
BRANCH(TaggedIsTrue(littleEndianHandle), &littleEnd, &bigEnd);
Bind(&littleEnd);
{
GateRef lower = Int64Or(Int64Or(b0, Int64LSL(b1, Int64(builtins::BITS_EIGHT))),
Int64Or(Int64LSL(b2, Int64(2 * builtins::BITS_EIGHT)),
Int64LSL(b3, Int64(builtins::BITS_TWENTY_FOUR))));
GateRef higher = Int64Or(Int64Or(b4, Int64LSL(b5, Int64(builtins::BITS_EIGHT))),
Int64Or(Int64LSL(b6, Int64(2 * builtins::BITS_EIGHT)),
Int64LSL(b7, Int64(builtins::BITS_TWENTY_FOUR))));
rawBits64 = Int64Or(lower, Int64LSL(higher, Int64(32)));
Jump(&assembleExit);
}
Bind(&bigEnd);
{
GateRef higher = Int64Or(Int64Or(Int64LSL(b0, Int64(builtins::BITS_TWENTY_FOUR)),
Int64LSL(b1, Int64(2 * builtins::BITS_EIGHT))),
Int64Or(Int64LSL(b2, Int64(builtins::BITS_EIGHT)), b3));
GateRef lower = Int64Or(Int64Or(Int64LSL(b4, Int64(builtins::BITS_TWENTY_FOUR)),
Int64LSL(b5, Int64(2 * builtins::BITS_EIGHT))),
Int64Or(Int64LSL(b6, Int64(builtins::BITS_EIGHT)), b7));
rawBits64 = Int64Or(Int64LSL(higher, Int64(32)), lower);
Jump(&assembleExit);
}
Bind(&assembleExit);
GateRef doubleValue = CastInt64ToFloat64(*rawBits64);
Label isNaN(env);
Label notNaN(env);
BRANCH_UNLIKELY(env->GetBuilder()->DoubleIsNAN(doubleValue), &isNaN, ¬NaN);
Bind(&isNaN);
{
result = DoubleToTaggedDoublePtr(Double(base::NAN_VALUE));
Jump(&exit);
}
Bind(¬NaN);
{
result = DoubleToTaggedDoublePtr(doubleValue);
Jump(&exit);
}
break;
}
default:
LOG_ECMA(FATAL) << "Invalid float type";
UNREACHABLE();
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayBufferStubBuilder::ReadAndAssembleBytes(GateRef glue, GateRef pointer,
GateRef offset, GateRef littleEndianHandle, uint32_t byteSize)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label littleEnd(env);
Label bigEnd(env);
Label exit(env);
DEFVARIABLE(result, VariableType::INT32(), Int32(0));
if (byteSize == 2) {
GateRef b0 = ZExtInt8ToInt32(LoadPrimitive(VariableType::INT8(), pointer, offset));
GateRef b1 = ZExtInt8ToInt32(LoadPrimitive(VariableType::INT8(), pointer, Int32Add(offset, Int32(1))));
BRANCH(TaggedIsTrue(littleEndianHandle), &littleEnd, &bigEnd);
Bind(&littleEnd);
{
result = Int32Or(b0, Int32LSL(b1, Int32(builtins::BITS_EIGHT)));
Jump(&exit);
}
Bind(&bigEnd);
{
result = Int32Or(Int32LSL(b0, Int32(builtins::BITS_EIGHT)), b1);
Jump(&exit);
}
} else if (byteSize == 4) {
GateRef b0 = ZExtInt8ToInt32(LoadPrimitive(VariableType::INT8(), pointer, offset));
GateRef b1 = ZExtInt8ToInt32(LoadPrimitive(VariableType::INT8(), pointer, Int32Add(offset, Int32(1))));
GateRef b2 = ZExtInt8ToInt32(LoadPrimitive(VariableType::INT8(), pointer, Int32Add(offset, Int32(2))));
GateRef b3 = ZExtInt8ToInt32(LoadPrimitive(VariableType::INT8(), pointer, Int32Add(offset, Int32(3))));
BRANCH(TaggedIsTrue(littleEndianHandle), &littleEnd, &bigEnd);
Bind(&littleEnd);
{
result = Int32Or(Int32Or(b0, Int32LSL(b1, Int32(builtins::BITS_EIGHT))),
Int32Or(Int32LSL(b2, Int32(2 * builtins::BITS_EIGHT)),
Int32LSL(b3, Int32(builtins::BITS_TWENTY_FOUR))));
Jump(&exit);
}
Bind(&bigEnd);
{
result = Int32Or(Int32Or(Int32LSL(b0, Int32(builtins::BITS_TWENTY_FOUR)),
Int32LSL(b1, Int32(2 * builtins::BITS_EIGHT))),
Int32Or(Int32LSL(b2, Int32(builtins::BITS_EIGHT)), b3));
Jump(&exit);
}
} else {
LOG_ECMA(FATAL) << "Invalid byte size";
UNREACHABLE();
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef BuiltinsArrayBufferStubBuilder::GetDataPointFromBuffer(GateRef glue, GateRef arrBuf)
{
auto env = GetEnvironment();
Label entryPass(env);
env->SubCfgEntry(&entryPass);
Label isNull(env);
Label exit(env);
Label isByteArray(env);
Label notByteArray(env);
DEFVARIABLE(result, VariableType::NATIVE_POINTER(), IntPtr(0));
BRANCH(IsByteArray(glue, arrBuf), &isByteArray, ¬ByteArray);
Bind(&isByteArray);
{
result = ChangeTaggedPointerToInt64(PtrAdd(arrBuf, IntPtr(ByteArray::DATA_OFFSET)));
Jump(&exit);
}
Bind(¬ByteArray);
{
GateRef data = GetArrayBufferData(glue, arrBuf);
result = GetExternalPointer(data);
Jump(&exit);
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
}