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
28 namespace panda::ecmascript::builtins {
29 using TypedArrayHelper = base::TypedArrayHelper;
30 using ContainerError = containers::ContainerError;
31 // 24.1.2.1 ArrayBuffer(length)
ArrayBufferConstructor(EcmaRuntimeCallInfo * argv)32 JSTaggedValue 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)
IsView(EcmaRuntimeCallInfo * argv)53 JSTaggedValue 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 ]
Species(EcmaRuntimeCallInfo * argv)73 JSTaggedValue 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
GetByteLength(EcmaRuntimeCallInfo * argv)81 JSTaggedValue 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)
Slice(EcmaRuntimeCallInfo * argv)110 JSTaggedValue 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)
AllocateSendableArrayBuffer(JSThread * thread,const JSHandle<JSTaggedValue> & newTarget,uint64_t byteLength)222 JSTaggedValue 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 ASSERT(obj.GetTaggedValue().IsInSharedHeap());
235 // 2. ReturnIfAbrupt
236 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
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()
IsDetachedBuffer(JSTaggedValue arrayBuffer)252 bool 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
CloneArrayBuffer(JSThread * thread,const JSHandle<JSTaggedValue> & srcBuffer,uint32_t srcByteOffset,JSHandle<JSTaggedValue> constructor)267 JSTaggedValue 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
GetDataPointFromBuffer(JSTaggedValue arrBuf,uint32_t byteOffset)332 void *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