• 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 
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