• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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