1/* 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "ecmascript/builtins/builtins_sendable_arraybuffer.h" 17 18#include <typeinfo> 19 20#include "ecmascript/containers/containers_errors.h" 21#include "ecmascript/interpreter/interpreter.h" 22#include "ecmascript/js_function.h" 23#include "ecmascript/shared_objects/js_sendable_arraybuffer.h" 24#include "ecmascript/base/typed_array_helper-inl.h" 25#include "cstdio" 26#include "cstring" 27 28namespace panda::ecmascript::builtins { 29using TypedArrayHelper = base::TypedArrayHelper; 30using ContainerError = containers::ContainerError; 31// 24.1.2.1 ArrayBuffer(length) 32JSTaggedValue BuiltinsSendableArrayBuffer::ArrayBufferConstructor(EcmaRuntimeCallInfo *argv) 33{ 34 ASSERT(argv); 35 JSThread *thread = argv->GetThread(); 36 BUILTINS_API_TRACE(thread, SendableArrayBuffer, Constructor); 37 [[maybe_unused]] EcmaHandleScope handleScope(thread); 38 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv); 39 // 1. If NewTarget is undefined, throw a TypeError exception. 40 if (newTarget->IsUndefined()) { 41 JSTaggedValue error = ContainerError::BusinessError(thread, containers::ErrorFlag::IS_NULL_ERROR, 42 "The ArkTS ArrayBuffer's constructor cannot be directly invoked."); 43 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 44 } 45 JSHandle<JSTaggedValue> lengthHandle = GetCallArg(argv, 0); 46 JSTaggedNumber lenNum = JSTaggedValue::ToIndex(thread, lengthHandle); 47 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 48 uint64_t length = lenNum.GetNumber(); 49 return AllocateSendableArrayBuffer(thread, newTarget, length); 50} 51 52// 24.1.3.1 ArrayBuffer.isView(arg) 53JSTaggedValue BuiltinsSendableArrayBuffer::IsView(EcmaRuntimeCallInfo *argv) 54{ 55 ASSERT(argv); 56 JSThread *thread = argv->GetThread(); 57 BUILTINS_API_TRACE(thread, SendableArrayBuffer, IsView); 58 [[maybe_unused]] EcmaHandleScope handleScope(thread); 59 JSHandle<JSTaggedValue> arg = GetCallArg(argv, 0); 60 // 1. If Type(arg) is not Object, return false. 61 if (!arg->IsECMAObject()) { 62 return BuiltinsSendableArrayBuffer::GetTaggedBoolean(false); 63 } 64 // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true. 65 if (arg->IsDataView() || arg->IsSharedTypedArray()) { 66 return BuiltinsSendableArrayBuffer::GetTaggedBoolean(true); 67 } 68 // 3. Return false. 69 return BuiltinsSendableArrayBuffer::GetTaggedBoolean(false); 70} 71 72// 24.1.3.3 get ArrayBuffer [ @@species ] 73JSTaggedValue BuiltinsSendableArrayBuffer::Species(EcmaRuntimeCallInfo *argv) 74{ 75 ASSERT(argv); 76 BUILTINS_API_TRACE(argv->GetThread(), SendableArrayBuffer, Species); 77 return GetThis(argv).GetTaggedValue(); 78} 79 80// 24.1.4.1 get ArrayBuffer.prototype.byteLength 81JSTaggedValue BuiltinsSendableArrayBuffer::GetByteLength(EcmaRuntimeCallInfo *argv) 82{ 83 ASSERT(argv); 84 JSThread *thread = argv->GetThread(); 85 BUILTINS_API_TRACE(thread, SendableArrayBuffer, GetByteLength); 86 [[maybe_unused]] EcmaHandleScope handleScope(thread); 87 88 // 1. Let O be the this value. 89 JSHandle<JSTaggedValue> thisHandle = GetThis(argv); 90 // 2. If Type(O) is not Object, throw a TypeError exception. 91 if (!thisHandle->IsECMAObject()) { 92 THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception()); 93 } 94 // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. 95 if (!thisHandle->IsSendableArrayBuffer()) { 96 THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception()); 97 } 98 // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception. 99 if (IsDetachedBuffer(thisHandle.GetTaggedValue())) { 100 THROW_TYPE_ERROR_AND_RETURN(thread, "IsDetachedBuffer", JSTaggedValue::Exception()); 101 } 102 JSHandle<JSSendableArrayBuffer> arrBuf(thisHandle); 103 // 5. Let length be the value of O’s [[ArrayBufferByteLength]] internal slot. 104 uint32_t length = arrBuf->GetArrayBufferByteLength(); 105 // 6. Return length. 106 return JSTaggedValue(length); 107} 108 109// 24.1.4.3 ArrayBuffer.prototype.slice(start, end) 110JSTaggedValue BuiltinsSendableArrayBuffer::Slice(EcmaRuntimeCallInfo *argv) 111{ 112 ASSERT(argv); 113 JSThread *thread = argv->GetThread(); 114 BUILTINS_API_TRACE(thread, SendableArrayBuffer, Slice); 115 [[maybe_unused]] EcmaHandleScope handleScope(thread); 116 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv(); 117 // 1. Let O be the this value. 118 JSHandle<JSTaggedValue> thisHandle = GetThis(argv); 119 // 2. If Type(O) is not Object, throw a TypeError exception. 120 if (!thisHandle->IsHeapObject()) { 121 THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception()); 122 } 123 // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. 124 if (!thisHandle->IsSendableArrayBuffer()) { 125 auto error = ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR, 126 "The slice method cannot be bound."); 127 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 128 } 129 // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception. 130 if (IsDetachedBuffer(thisHandle.GetTaggedValue())) { 131 THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception()); 132 } 133 JSHandle<JSSendableArrayBuffer> arrBuf(thisHandle); 134 // 5. Let len be the value of O’s [[ArrayBufferByteLength]] internal slot. 135 int32_t len = static_cast<int32_t>(arrBuf->GetArrayBufferByteLength()); 136 JSHandle<JSTaggedValue> startHandle = GetCallArg(argv, 0); 137 // 6. Let relativeStart be ToInteger(start). 138 JSTaggedNumber relativeStart = JSTaggedValue::ToInteger(thread, startHandle); 139 // 7. ReturnIfAbrupt(relativeStart). 140 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 141 int32_t start = base::NumberHelper::DoubleInRangeInt32(relativeStart.GetNumber()); 142 int32_t end = 0; 143 int32_t first = 0; 144 int32_t last = 0; 145 // 8. If relativeStart < 0, let first be max((len + relativeStart),0); else let first be min(relativeStart, len). 146 if (start < 0) { 147 first = std::max((len + start), 0); 148 } else { 149 first = std::min(start, len); 150 } 151 // 9. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end). 152 JSHandle<JSTaggedValue> endHandle = GetCallArg(argv, 1); 153 if (endHandle->IsUndefined()) { 154 end = len; 155 } else { 156 JSTaggedNumber relativeEnd = JSTaggedValue::ToInteger(thread, endHandle); 157 // 10. ReturnIfAbrupt(relativeEnd). 158 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 159 end = base::NumberHelper::DoubleInRangeInt32(relativeEnd.GetNumber()); 160 } 161 // 11. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len). 162 if (end < 0) { 163 last = std::max((len + end), 0); 164 } else { 165 last = std::min(end, len); 166 } 167 // 12. Let newLen be max(final-first,0). 168 uint32_t newLen = std::max((last - first), 0); 169 // 13. Let ctor be SpeciesConstructor(O, %ArrayBuffer%). 170 JSHandle<JSTaggedValue> defaultConstructor = env->GetSBuiltininArrayBufferFunction(); 171 JSHandle<JSObject> objHandle(thisHandle); 172 JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor); 173 // 14. ReturnIfAbrupt(ctor). 174 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 175 // 15. Let new be Construct(ctor, «newLen»). 176 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 177 EcmaRuntimeCallInfo *info = 178 EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 1); 179 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 180 info->SetCallArg(JSTaggedValue(newLen)); 181 JSTaggedValue taggedNewArrBuf = JSFunction::Construct(info); 182 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 183 JSHandle<JSTaggedValue> newArrBuf(thread, taggedNewArrBuf); 184 // 16. ReturnIfAbrupt(new). 185 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 186 // 17. If new does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. 187 if (!newArrBuf->IsSendableArrayBuffer()) { 188 THROW_TYPE_ERROR_AND_RETURN(thread, "don't have bufferdata internal slot", JSTaggedValue::Exception()); 189 } 190 // 18. If IsDetachedBuffer(new) is true, throw a TypeError exception. 191 if (IsDetachedBuffer(newArrBuf.GetTaggedValue())) { 192 THROW_TYPE_ERROR_AND_RETURN(thread, "new arrayBuffer IsDetachedBuffer", JSTaggedValue::Exception()); 193 } 194 // 19. If SameValue(new, O) is true, throw a TypeError exception. 195 if (JSTaggedValue::SameValue(newArrBuf.GetTaggedValue(), thisHandle.GetTaggedValue())) { 196 THROW_TYPE_ERROR_AND_RETURN(thread, "value of new arraybuffer and this is same", JSTaggedValue::Exception()); 197 } 198 JSHandle<JSSendableArrayBuffer> newJsArrBuf(newArrBuf); 199 // 20. If the value of new’s [[ArrayBufferByteLength]] internal slot < newLen, throw a TypeError exception. 200 uint32_t newArrBufLen = newJsArrBuf->GetArrayBufferByteLength(); 201 if (newArrBufLen < newLen) { 202 THROW_TYPE_ERROR_AND_RETURN(thread, "new array buffer length smaller than newlen", JSTaggedValue::Exception()); 203 } 204 // 21. NOTE: Side-effects of the above steps may have detached O. 205 // 22. If IsDetachedBuffer(O) is true, throw a TypeError exception. 206 if (IsDetachedBuffer(thisHandle.GetTaggedValue())) { 207 THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception()); 208 } 209 if (newLen > 0) { 210 // 23. Let fromBuf be the value of O’s [[ArrayBufferData]] internal slot. 211 void *fromBuf = GetDataPointFromBuffer(arrBuf.GetTaggedValue()); 212 // 24. Let toBuf be the value of new’s [[ArrayBufferData]] internal slot. 213 void *toBuf = GetDataPointFromBuffer(newJsArrBuf.GetTaggedValue()); 214 // 25. Perform CopyDataBlockBytes(toBuf, fromBuf, first, newLen). 215 JSSendableArrayBuffer::CopyDataPointBytes(toBuf, fromBuf, first, newLen); 216 } 217 // Return new. 218 return newArrBuf.GetTaggedValue(); 219} 220 221// 24.1.1.1 AllocateArrayBuffer(constructor, byteLength) 222JSTaggedValue BuiltinsSendableArrayBuffer::AllocateSendableArrayBuffer( 223 JSThread *thread, const JSHandle<JSTaggedValue> &newTarget, uint64_t byteLength) 224{ 225 BUILTINS_API_TRACE(thread, SendableArrayBuffer, AllocateSendableArrayBuffer); 226 /** 227 * 1. Let obj be OrdinaryCreateFromConstructor(constructor, "%ArrayBufferPrototype%", 228 * «[[ArrayBufferData]], [[ArrayBufferByteLength]]» ). 229 * */ 230 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 231 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv(); 232 JSHandle<JSTaggedValue> arrBufFunc = env->GetSBuiltininArrayBufferFunction(); 233 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(arrBufFunc), newTarget); 234 // 2. ReturnIfAbrupt 235 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 236 ASSERT(obj.GetTaggedValue().IsInSharedHeap()); 237 // 4. Let block be CreateByteDataBlock(byteLength). 238 if (byteLength > INT_MAX) { 239 THROW_RANGE_ERROR_AND_RETURN(thread, "Out of range", JSTaggedValue::Exception()); 240 } 241 uint32_t arrayByteLength = static_cast<uint32_t>(byteLength); 242 JSHandle<JSSendableArrayBuffer> arrayBuffer(obj); 243 // 6. Set obj’s [[ArrayBufferData]] internal slot to block. 244 factory->NewJSSendableArrayBufferData(arrayBuffer, arrayByteLength); 245 // 7. Set obj’s [[ArrayBufferByteLength]] internal slot to byteLength. 246 arrayBuffer->SetArrayBufferByteLength(arrayByteLength); 247 // 8. Return obj. 248 return arrayBuffer.GetTaggedValue(); 249} 250 251// 24.1.1.2 IsDetachedBuffer() 252bool BuiltinsSendableArrayBuffer::IsDetachedBuffer(JSTaggedValue arrayBuffer) 253{ 254 if (arrayBuffer.IsByteArray()) { 255 return false; 256 } 257 // 1. Assert: Type(arrayBuffer) is Object and it has an [[ArrayBufferData]] internal slot. 258 ASSERT(arrayBuffer.IsSendableArrayBuffer()); 259 JSSendableArrayBuffer *buffer = JSSendableArrayBuffer::Cast(arrayBuffer.GetTaggedObject()); 260 JSTaggedValue dataSlot = buffer->GetArrayBufferData(); 261 // 2. If arrayBuffer’s [[ArrayBufferData]] internal slot is null, return true. 262 // 3. Return false. 263 return dataSlot.IsNull(); 264} 265 266// 24.1.1.4 267JSTaggedValue BuiltinsSendableArrayBuffer::CloneArrayBuffer(JSThread *thread, 268 const JSHandle<JSTaggedValue> &srcBuffer, 269 uint32_t srcByteOffset, 270 JSHandle<JSTaggedValue> constructor) 271{ 272 BUILTINS_API_TRACE(thread, SendableArrayBuffer, CloneArrayBuffer); 273 // 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot. 274 ASSERT(srcBuffer->IsSendableArrayBuffer()|| srcBuffer->IsByteArray()); 275 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv(); 276 // 2. If cloneConstructor is not present 277 if (constructor->IsUndefined()) { 278 // a. Let cloneConstructor be SpeciesConstructor(srcBuffer, %ArrayBuffer%). 279 JSHandle<JSTaggedValue> defaultConstructor = env->GetSBuiltininArrayBufferFunction(); 280 JSHandle<JSObject> objHandle(srcBuffer); 281 constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor); 282 // b. ReturnIfAbrupt(cloneConstructor). 283 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 284 // c. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. 285 if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) { 286 THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); 287 } else { 288 ASSERT(constructor->IsConstructor()); 289 } 290 } 291 // 4. Let srcLength be the value of srcBuffer’s [[ArrayBufferByteLength]] internal slot. 292 uint32_t srcLen = 0; 293 int32_t cloneLen = 0; 294 if (srcBuffer->IsByteArray()) { 295 JSHandle<ByteArray> byteArrayBuf(srcBuffer); 296 srcLen = byteArrayBuf->GetArrayLength(); 297 int32_t byteLen = static_cast<int32_t>(byteArrayBuf->GetByteLength()); 298 // 5. Assert: srcByteOffset ≤ srcLength. 299 ASSERT(srcByteOffset <= srcLen); 300 // 6. Let cloneLength be (srcLength – srcByteOffset) * byteLen. 301 cloneLen = static_cast<int32_t>(srcLen - srcByteOffset) * byteLen; 302 srcByteOffset *= static_cast<uint32_t>(byteLen); 303 } else { 304 JSHandle<JSSendableArrayBuffer> arrBuf(srcBuffer); 305 srcLen = arrBuf->GetArrayBufferByteLength(); 306 // 5. Assert: srcByteOffset ≤ srcLength. 307 ASSERT(srcByteOffset <= srcLen); 308 // 6. Let cloneLength be srcLength – srcByteOffset. 309 cloneLen = static_cast<int32_t>(srcLen - srcByteOffset); 310 } 311 312 // 8. Let targetBuffer be AllocateArrayBuffer(cloneConstructor, cloneLength). 313 JSTaggedValue taggedBuf = AllocateSendableArrayBuffer(thread, constructor, cloneLen); 314 // 9. ReturnIfAbrupt(targetBuffer). 315 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 316 // 10. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. 317 if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) { 318 THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); 319 } 320 // 11. Let targetBlock be the value of targetBuffer’s [[ArrayBufferData]] internal slot. 321 JSHandle<JSSendableArrayBuffer> newArrBuf(thread, taggedBuf); 322 // Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, cloneLength). 323 // 7. Let srcBlock be the value of srcBuffer’s [[ArrayBufferData]] internal slot. 324 void *fromBuf = GetDataPointFromBuffer(srcBuffer.GetTaggedValue()); 325 void *toBuf = GetDataPointFromBuffer(taggedBuf); 326 if (cloneLen > 0) { 327 JSSendableArrayBuffer::CopyDataPointBytes(toBuf, fromBuf, srcByteOffset, cloneLen); 328 } 329 return taggedBuf; 330} 331 332void *BuiltinsSendableArrayBuffer::GetDataPointFromBuffer(JSTaggedValue arrBuf, uint32_t byteOffset) 333{ 334 if (arrBuf.IsByteArray()) { 335 return reinterpret_cast<void *>(ToUintPtr(ByteArray::Cast(arrBuf.GetTaggedObject())->GetData()) + byteOffset); 336 } 337 338 JSSendableArrayBuffer *arrayBuffer = JSSendableArrayBuffer::Cast(arrBuf.GetTaggedObject()); 339 if (arrayBuffer->GetArrayBufferByteLength() == 0) { 340 return nullptr; 341 } 342 343 JSTaggedValue data = arrayBuffer->GetArrayBufferData(); 344 return reinterpret_cast<void *>(ToUintPtr(JSNativePointer::Cast(data.GetTaggedObject()) 345 ->GetExternalPointer()) + byteOffset); 346} 347} // namespace panda::ecmascript::builtins 348