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