* 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_dataview.h"
#include "ecmascript/builtins/builtins_arraybuffer.h"
#include "ecmascript/js_arraybuffer.h"
#include "ecmascript/js_tagged_value-inl.h"
namespace panda::ecmascript::builtins {
JSTaggedValue BuiltinsDataView::DataViewConstructor(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, DataView, Constructor);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> ctor = GetConstructor(argv);
JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
if (newTarget->IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "newtarget is undefined", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> bufferHandle = GetCallArg(argv, 0);
if (!bufferHandle->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "buffer is not Object", JSTaggedValue::Exception());
}
if (!bufferHandle->IsArrayBuffer() && !bufferHandle->IsSharedArrayBuffer()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "buffer is not ArrayBuffer", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> offsetHandle = GetCallArg(argv, 1);
JSTaggedNumber offsetNumber = JSTaggedValue::ToIndex(thread, offsetHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
uint64_t offset = base::NumberHelper::DoubleToUInt64(offsetNumber.GetNumber());
if (BuiltinsArrayBuffer::IsDetachedBuffer(thread, bufferHandle.GetTaggedValue())) {
THROW_TYPE_ERROR_AND_RETURN(thread, "buffer is Detached Buffer", JSTaggedValue::Exception());
}
JSHandle<JSArrayBuffer> arrBufHandle(bufferHandle);
uint32_t bufByteLen = arrBufHandle->GetArrayBufferByteLength();
if (offset > bufByteLen) {
THROW_RANGE_ERROR_AND_RETURN(thread, "offset > bufferByteLength", JSTaggedValue::Exception());
}
uint64_t viewByteLen = 0;
JSHandle<JSTaggedValue> byteLenHandle = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
if (byteLenHandle->IsUndefined()) {
viewByteLen = bufByteLen - offset;
} else {
JSTaggedNumber byteLen = JSTaggedValue::ToIndex(thread, byteLenHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
viewByteLen = base::NumberHelper::DoubleToUInt64(byteLen.GetNumber());
if (offset + viewByteLen > bufByteLen) {
THROW_RANGE_ERROR_AND_RETURN(thread, "offset + viewByteLen > bufByteLen", JSTaggedValue::Exception());
}
}
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), newTarget);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSDataView> dataView(obj);
dataView->SetDataView(thread, JSTaggedValue::True());
dataView->SetViewedArrayBuffer(thread, bufferHandle.GetTaggedValue());
dataView->SetByteLength(static_cast<uint32_t>(viewByteLen));
dataView->SetByteOffset(static_cast<uint32_t>(offset));
return JSTaggedValue(dataView.GetTaggedValue());
}
JSTaggedValue BuiltinsDataView::GetBuffer(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, DataView, GetBuffer);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
if (!thisHandle->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception());
}
if (!thisHandle->IsDataView()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "O does not have a [[ViewedArrayBuffer]]", JSTaggedValue::Exception());
}
JSHandle<JSDataView> dataView(thisHandle);
JSTaggedValue buffer = dataView->GetViewedArrayBuffer(thread);
return JSTaggedValue(buffer);
}
JSTaggedValue BuiltinsDataView::GetByteLength(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, DataView, GetByteLength);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
if (!thisHandle->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception());
}
if (!thisHandle->IsDataView()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "O does not have a [[ViewedArrayBuffer]]", JSTaggedValue::Exception());
}
JSHandle<JSDataView> dataView(thisHandle);
JSTaggedValue buffer = dataView->GetViewedArrayBuffer(thread);
if (BuiltinsArrayBuffer::IsDetachedBuffer(thread, buffer)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
}
uint32_t size = dataView->GetByteLength();
return JSTaggedValue(size);
}
JSTaggedValue BuiltinsDataView::GetOffset(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, DataView, GetOffset);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
if (!thisHandle->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception());
}
if (!thisHandle->IsDataView()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "O does not have a [[ViewedArrayBuffer]]", JSTaggedValue::Exception());
}
JSHandle<JSDataView> dataView(thisHandle);
JSTaggedValue buffer = dataView->GetViewedArrayBuffer(thread);
if (BuiltinsArrayBuffer::IsDetachedBuffer(thread, buffer)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
}
uint32_t offset = dataView->GetByteOffset();
return JSTaggedValue(offset);
}
JSTaggedValue BuiltinsDataView::GetFloat32(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, GetFloat32);
return GetTypedValue(argv, DataViewType::FLOAT32);
}
JSTaggedValue BuiltinsDataView::GetFloat64(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, GetFloat64);
return GetTypedValue(argv, DataViewType::FLOAT64);
}
JSTaggedValue BuiltinsDataView::GetInt8(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, GetInt8);
return GetTypedValue(argv, DataViewType::INT8);
}
JSTaggedValue BuiltinsDataView::GetInt16(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, GetInt16);
return GetTypedValue(argv, DataViewType::INT16);
}
JSTaggedValue BuiltinsDataView::GetInt32(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, GetInt32);
return GetTypedValue(argv, DataViewType::INT32);
}
JSTaggedValue BuiltinsDataView::GetUint8(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, GetUint8);
return GetTypedValue(argv, DataViewType::UINT8);
}
JSTaggedValue BuiltinsDataView::GetUint16(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, GetUint16);
return GetTypedValue(argv, DataViewType::UINT16);
}
JSTaggedValue BuiltinsDataView::GetUint32(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, GetUint32);
return GetTypedValue(argv, DataViewType::UINT32);
}
JSTaggedValue BuiltinsDataView::GetBigInt64(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, GetBigInt64);
return GetTypedValue(argv, DataViewType::BIGINT64);
}
JSTaggedValue BuiltinsDataView::GetBigUint64(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, GetBigUint64);
return GetTypedValue(argv, DataViewType::BIGUINT64);
}
JSTaggedValue BuiltinsDataView::SetFloat32(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, SetFloat32);
return SetTypedValue(argv, DataViewType::FLOAT32);
}
JSTaggedValue BuiltinsDataView::SetFloat64(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, SetFloat64);
return SetTypedValue(argv, DataViewType::FLOAT64);
}
JSTaggedValue BuiltinsDataView::SetInt8(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, SetInt8);
return SetTypedValue(argv, DataViewType::INT8);
}
JSTaggedValue BuiltinsDataView::SetInt16(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, SetInt16);
return SetTypedValue(argv, DataViewType::INT16);
}
JSTaggedValue BuiltinsDataView::SetInt32(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, SetInt32);
return SetTypedValue(argv, DataViewType::INT32);
}
JSTaggedValue BuiltinsDataView::SetUint8(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, SetUint8);
return SetTypedValue(argv, DataViewType::UINT8);
}
JSTaggedValue BuiltinsDataView::SetUint16(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, SetUint16);
return SetTypedValue(argv, DataViewType::UINT16);
}
JSTaggedValue BuiltinsDataView::SetUint32(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, SetUint32);
return SetTypedValue(argv, DataViewType::UINT32);
}
JSTaggedValue BuiltinsDataView::SetBigInt64(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, SetBigInt64);
return SetTypedValue(argv, DataViewType::BIGINT64);
}
JSTaggedValue BuiltinsDataView::SetBigUint64(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), DataView, SetBigUint64);
return SetTypedValue(argv, DataViewType::BIGUINT64);
}
JSTaggedValue BuiltinsDataView::GetViewValue(JSThread *thread, const JSHandle<JSTaggedValue> &view,
const JSHandle<JSTaggedValue> &requestIndex,
const JSHandle<JSTaggedValue> &littleEndian,
DataViewType type)
{
BUILTINS_API_TRACE(thread, DataView, GetViewValue);
if (!view->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception());
}
if (!view->IsDataView()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "view is not dataview", JSTaggedValue::Exception());
}
uint64_t index = 0;
if (requestIndex->IsInt()) {
auto indexInt = requestIndex->GetInt();
if (indexInt < 0) {
THROW_RANGE_ERROR_AND_RETURN(thread, "getIndex < 0", JSTaggedValue::Exception());
}
index = static_cast<uint64_t>(indexInt);
} else {
JSTaggedNumber numberIndex = JSTaggedValue::ToIndex(thread, requestIndex);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
index = base::NumberHelper::DoubleToUInt64(numberIndex.GetNumber());
}
bool isLittleEndian = false;
if (littleEndian->IsUndefined()) {
isLittleEndian = false;
} else {
isLittleEndian = littleEndian->ToBoolean();
}
JSHandle<JSDataView> dataView(view);
JSTaggedValue buffer = dataView->GetViewedArrayBuffer(thread);
if (BuiltinsArrayBuffer::IsDetachedBuffer(thread, buffer)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
}
uint32_t offset = dataView->GetByteOffset();
uint32_t size = dataView->GetByteLength();
uint32_t elementSize = JSDataView::GetElementSize(type);
if (index + elementSize > size) {
THROW_RANGE_ERROR_AND_RETURN(thread, "getIndex +elementSize > viewSize", JSTaggedValue::Exception());
}
uint32_t bufferIndex = static_cast<uint32_t>(index + offset);
return BuiltinsArrayBuffer::GetValueFromBuffer(thread, buffer, bufferIndex, type, isLittleEndian);
}
JSTaggedValue BuiltinsDataView::SetViewValue(JSThread *thread, const JSHandle<JSTaggedValue> &view,
const JSHandle<JSTaggedValue> &requestIndex,
const JSHandle<JSTaggedValue> &littleEndian,
DataViewType type, const JSHandle<JSTaggedValue> &value)
{
BUILTINS_API_TRACE(thread, DataView, SetViewValue);
if (!view->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception());
}
if (!view->IsDataView()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "view is not dataview", JSTaggedValue::Exception());
}
uint64_t index = 0;
if (requestIndex->IsInt()) {
auto indexInt = requestIndex->GetInt();
if (indexInt < 0) {
THROW_RANGE_ERROR_AND_RETURN(thread, "getIndex < 0", JSTaggedValue::Exception());
}
index = static_cast<uint64_t>(indexInt);
} else {
JSTaggedNumber numberIndex = JSTaggedValue::ToIndex(thread, requestIndex);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
index = base::NumberHelper::DoubleToUInt64(numberIndex.GetNumber());
}
JSMutableHandle<JSTaggedValue> numValueHandle = JSMutableHandle<JSTaggedValue>(thread, value);
if (!value->IsNumber()) {
numValueHandle.Update(JSTaggedValue::ToNumeric(thread, value));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
bool isLittleEndian = false;
if (littleEndian->IsUndefined()) {
isLittleEndian = false;
} else {
isLittleEndian = littleEndian->ToBoolean();
}
JSHandle<JSDataView> dataView(view);
JSTaggedValue buffer = dataView->GetViewedArrayBuffer(thread);
if (BuiltinsArrayBuffer::IsDetachedBuffer(thread, buffer)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
}
uint32_t offset = dataView->GetByteOffset();
uint32_t size = dataView->GetByteLength();
uint32_t elementSize = JSDataView::GetElementSize(type);
if (index + elementSize > size) {
THROW_RANGE_ERROR_AND_RETURN(thread, "getIndex +elementSize > viewSize", JSTaggedValue::Exception());
}
uint32_t bufferIndex = static_cast<uint32_t>(index + offset);
return BuiltinsArrayBuffer::SetValueInBuffer(thread, buffer, bufferIndex, type, numValueHandle, isLittleEndian);
}
JSTaggedValue BuiltinsDataView::GetTypedValue(EcmaRuntimeCallInfo *argv, DataViewType type)
{
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, DataView, GetTypedValue);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSTaggedValue> offsetHandle = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> trueHandle(thread, JSTaggedValue::True());
if (type == DataViewType::UINT8 || type == DataViewType::INT8) {
return GetViewValue(thread, thisHandle, offsetHandle, trueHandle, type);
}
JSHandle<JSTaggedValue> littleEndianHandle = GetCallArg(argv, 1);
return GetViewValue(thread, thisHandle, offsetHandle, littleEndianHandle, type);
}
JSTaggedValue BuiltinsDataView::SetTypedValue(EcmaRuntimeCallInfo *argv, DataViewType type)
{
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, DataView, SetTypedValue);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSTaggedValue> offsetHandle = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> value = GetCallArg(argv, 1);
JSHandle<JSTaggedValue> trueHandle(thread, JSTaggedValue::True());
if (type == DataViewType::UINT8 || type == DataViewType::INT8) {
return SetViewValue(thread, thisHandle, offsetHandle, trueHandle, type, value);
}
JSHandle<JSTaggedValue> littleEndianHandle = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
return SetViewValue(thread, thisHandle, offsetHandle, littleEndianHandle, type, value);
}
}