1 /*
2 * Copyright (c) 2021-2022 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_arraybuffer.h"
17
18 #include <typeinfo>
19
20 #include "ecmascript/base/builtins_base.h"
21 #include "ecmascript/base/number_helper.h"
22 #include "ecmascript/builtins/builtins_bigint.h"
23 #include "ecmascript/ecma_macros.h"
24 #include "ecmascript/ecma_vm.h"
25 #include "ecmascript/global_env.h"
26 #include "ecmascript/interpreter/interpreter.h"
27 #include "ecmascript/js_arraybuffer.h"
28 #include "ecmascript/js_object-inl.h"
29 #include "ecmascript/js_tagged_number.h"
30 #include "ecmascript/js_tagged_value-inl.h"
31 #include "ecmascript/js_tagged_value.h"
32 #include "ecmascript/object_factory.h"
33 #include "securec.h"
34
35 namespace panda::ecmascript::builtins {
36 // 24.1.2.1 ArrayBuffer(length)
ArrayBufferConstructor(EcmaRuntimeCallInfo * argv)37 JSTaggedValue BuiltinsArrayBuffer::ArrayBufferConstructor(EcmaRuntimeCallInfo *argv)
38 {
39 ASSERT(argv);
40 JSThread *thread = argv->GetThread();
41 BUILTINS_API_TRACE(thread, ArrayBuffer, Constructor);
42 [[maybe_unused]] EcmaHandleScope handleScope(thread);
43 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
44 // 1. If NewTarget is undefined, throw a TypeError exception.
45 if (newTarget->IsUndefined()) {
46 THROW_TYPE_ERROR_AND_RETURN(thread, "newtarget is undefined", JSTaggedValue::Exception());
47 }
48 JSHandle<JSTaggedValue> lengthHandle = GetCallArg(argv, 0);
49 JSTaggedNumber lenNum = JSTaggedValue::ToIndex(thread, lengthHandle);
50 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
51 uint64_t length = lenNum.GetNumber();
52 return AllocateArrayBuffer(thread, newTarget, length);
53 }
54
55 // 24.1.3.1 ArrayBuffer.isView(arg)
IsView(EcmaRuntimeCallInfo * argv)56 JSTaggedValue BuiltinsArrayBuffer::IsView(EcmaRuntimeCallInfo *argv)
57 {
58 ASSERT(argv);
59 JSThread *thread = argv->GetThread();
60 BUILTINS_API_TRACE(thread, ArrayBuffer, IsView);
61 [[maybe_unused]] EcmaHandleScope handleScope(thread);
62 JSHandle<JSTaggedValue> arg = GetCallArg(argv, 0);
63 // 1. If Type(arg) is not Object, return false.
64 if (!arg->IsECMAObject()) {
65 return BuiltinsArrayBuffer::GetTaggedBoolean(false);
66 }
67 // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true.
68 if (arg->IsDataView() || arg->IsTypedArray()) {
69 return BuiltinsArrayBuffer::GetTaggedBoolean(true);
70 }
71 // 3. Return false.
72 return BuiltinsArrayBuffer::GetTaggedBoolean(false);
73 }
74
75 // 24.1.3.3 get ArrayBuffer [ @@species ]
Species(EcmaRuntimeCallInfo * argv)76 JSTaggedValue BuiltinsArrayBuffer::Species(EcmaRuntimeCallInfo *argv)
77 {
78 ASSERT(argv);
79 BUILTINS_API_TRACE(argv->GetThread(), ArrayBuffer, Species);
80 return GetThis(argv).GetTaggedValue();
81 }
82
83 // 24.1.4.1 get ArrayBuffer.prototype.byteLength
GetByteLength(EcmaRuntimeCallInfo * argv)84 JSTaggedValue BuiltinsArrayBuffer::GetByteLength(EcmaRuntimeCallInfo *argv)
85 {
86 ASSERT(argv);
87 JSThread *thread = argv->GetThread();
88 BUILTINS_API_TRACE(thread, ArrayBuffer, GetByteLength);
89 [[maybe_unused]] EcmaHandleScope handleScope(thread);
90
91 // 1. Let O be the this value.
92 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
93 // 2. If Type(O) is not Object, throw a TypeError exception.
94 if (!thisHandle->IsECMAObject()) {
95 THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception());
96 }
97 // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
98 if (!thisHandle->IsArrayBuffer()) {
99 THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception());
100 }
101 // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
102 if (IsDetachedBuffer(thisHandle.GetTaggedValue())) {
103 THROW_TYPE_ERROR_AND_RETURN(thread, "IsDetachedBuffer", JSTaggedValue::Exception());
104 }
105 JSHandle<JSArrayBuffer> arrBuf(thisHandle);
106 // 5. Let length be the value of O’s [[ArrayBufferByteLength]] internal slot.
107 uint32_t length = arrBuf->GetArrayBufferByteLength();
108 // 6. Return length.
109 return JSTaggedValue(length);
110 }
111
112 // 24.1.4.3 ArrayBuffer.prototype.slice(start, end)
Slice(EcmaRuntimeCallInfo * argv)113 JSTaggedValue BuiltinsArrayBuffer::Slice(EcmaRuntimeCallInfo *argv)
114 {
115 ASSERT(argv);
116 JSThread *thread = argv->GetThread();
117 BUILTINS_API_TRACE(thread, ArrayBuffer, Slice);
118 [[maybe_unused]] EcmaHandleScope handleScope(thread);
119 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
120 // 1. Let O be the this value.
121 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
122 // 2. If Type(O) is not Object, throw a TypeError exception.
123 if (!thisHandle->IsHeapObject()) {
124 THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception());
125 }
126 JSHandle<JSArrayBuffer> arrBuf(thisHandle);
127 // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
128 if (!thisHandle->IsArrayBuffer()) {
129 THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception());
130 }
131 // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
132 if (IsDetachedBuffer(thisHandle.GetTaggedValue())) {
133 THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception());
134 }
135 // 5. Let len be the value of O’s [[ArrayBufferByteLength]] internal slot.
136 int32_t len = static_cast<int32_t>(arrBuf->GetArrayBufferByteLength());
137 JSHandle<JSTaggedValue> startHandle = GetCallArg(argv, 0);
138 // 6. Let relativeStart be ToInteger(start).
139 JSTaggedNumber relativeStart = JSTaggedValue::ToInteger(thread, startHandle);
140 // 7. ReturnIfAbrupt(relativeStart).
141 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
142 int32_t start = base::NumberHelper::DoubleInRangeInt32(relativeStart.GetNumber());
143 int32_t end = 0;
144 int32_t first = 0;
145 int32_t last = 0;
146 // 8. If relativeStart < 0, let first be max((len + relativeStart),0); else let first be min(relativeStart, len).
147 if (start < 0) {
148 first = std::max((len + start), 0);
149 } else {
150 first = std::min(start, len);
151 }
152 // 9. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end).
153 JSHandle<JSTaggedValue> endHandle = GetCallArg(argv, 1);
154 if (endHandle->IsUndefined()) {
155 end = len;
156 } else {
157 JSTaggedNumber relativeEnd = JSTaggedValue::ToInteger(thread, endHandle);
158 // 10. ReturnIfAbrupt(relativeEnd).
159 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
160 end = base::NumberHelper::DoubleInRangeInt32(relativeEnd.GetNumber());
161 }
162 // 11. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len).
163 if (end < 0) {
164 last = std::max((len + end), 0);
165 } else {
166 last = std::min(end, len);
167 }
168 // 12. Let newLen be max(final-first,0).
169 uint32_t newLen = std::max((last - first), 0);
170 // 13. Let ctor be SpeciesConstructor(O, %ArrayBuffer%).
171 JSHandle<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
172 JSHandle<JSObject> objHandle(thisHandle);
173 JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
174 // 14. ReturnIfAbrupt(ctor).
175 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
176 // 15. Let new be Construct(ctor, «newLen»).
177 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
178 EcmaRuntimeCallInfo *info =
179 EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 1);
180 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
181 info->SetCallArg(JSTaggedValue(newLen));
182 JSTaggedValue taggedNewArrBuf = JSFunction::Construct(info);
183 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
184 JSHandle<JSTaggedValue> newArrBuf(thread, taggedNewArrBuf);
185 // 16. ReturnIfAbrupt(new).
186 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
187 // 17. If new does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
188 if (!newArrBuf->IsArrayBuffer()) {
189 THROW_TYPE_ERROR_AND_RETURN(thread, "don't have bufferdata internal slot", JSTaggedValue::Exception());
190 }
191 // 18. If IsDetachedBuffer(new) is true, throw a TypeError exception.
192 if (IsDetachedBuffer(newArrBuf.GetTaggedValue())) {
193 THROW_TYPE_ERROR_AND_RETURN(thread, "new arrayBuffer IsDetachedBuffer", JSTaggedValue::Exception());
194 }
195 // 19. If SameValue(new, O) is true, throw a TypeError exception.
196 if (JSTaggedValue::SameValue(newArrBuf.GetTaggedValue(), thisHandle.GetTaggedValue())) {
197 THROW_TYPE_ERROR_AND_RETURN(thread, "value of new arraybuffer and this is same", JSTaggedValue::Exception());
198 }
199 JSHandle<JSArrayBuffer> newJsArrBuf(newArrBuf);
200 // 20. If the value of new’s [[ArrayBufferByteLength]] internal slot < newLen, throw a TypeError exception.
201 uint32_t newArrBufLen = newJsArrBuf->GetArrayBufferByteLength();
202 if (newArrBufLen < newLen) {
203 THROW_TYPE_ERROR_AND_RETURN(thread, "new array buffer length smaller than newlen", JSTaggedValue::Exception());
204 }
205 // 21. NOTE: Side-effects of the above steps may have detached O.
206 // 22. If IsDetachedBuffer(O) is true, throw a TypeError exception.
207 if (IsDetachedBuffer(thisHandle.GetTaggedValue())) {
208 THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception());
209 }
210 if (newLen > 0) {
211 // 23. Let fromBuf be the value of O’s [[ArrayBufferData]] internal slot.
212 void *fromBuf = GetDataPointFromBuffer(arrBuf.GetTaggedValue());
213 // 24. Let toBuf be the value of new’s [[ArrayBufferData]] internal slot.
214 void *toBuf = GetDataPointFromBuffer(newJsArrBuf.GetTaggedValue());
215 // 25. Perform CopyDataBlockBytes(toBuf, fromBuf, first, newLen).
216 JSArrayBuffer::CopyDataPointBytes(toBuf, fromBuf, first, newLen);
217 }
218 // Return new.
219 return newArrBuf.GetTaggedValue();
220 }
221
222 // 24.1.1.1 AllocateArrayBuffer(constructor, byteLength)
AllocateArrayBuffer(JSThread * thread,const JSHandle<JSTaggedValue> & newTarget,uint64_t byteLength)223 JSTaggedValue BuiltinsArrayBuffer::AllocateArrayBuffer(JSThread *thread, const JSHandle<JSTaggedValue> &newTarget,
224 uint64_t byteLength)
225 {
226 BUILTINS_API_TRACE(thread, ArrayBuffer, AllocateArrayBuffer);
227 /**
228 * 1. Let obj be OrdinaryCreateFromConstructor(constructor, "%ArrayBufferPrototype%",
229 * «[[ArrayBufferData]], [[ArrayBufferByteLength]]» ).
230 * */
231 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
232 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
233 JSHandle<JSTaggedValue> arrBufFunc = env->GetArrayBufferFunction();
234 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(arrBufFunc), newTarget);
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<JSArrayBuffer> arrayBuffer(obj);
243 // 6. Set obj’s [[ArrayBufferData]] internal slot to block.
244 factory->NewJSArrayBufferData(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 BuiltinsArrayBuffer::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.IsArrayBuffer() || arrayBuffer.IsSharedArrayBuffer());
259 JSArrayBuffer *buffer = JSArrayBuffer::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 BuiltinsArrayBuffer::CloneArrayBuffer(JSThread *thread, const JSHandle<JSTaggedValue> &srcBuffer,
268 uint32_t srcByteOffset, JSHandle<JSTaggedValue> constructor)
269 {
270 BUILTINS_API_TRACE(thread, ArrayBuffer, CloneArrayBuffer);
271 // 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
272 ASSERT(srcBuffer->IsArrayBuffer() || srcBuffer->IsSharedArrayBuffer());
273 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
274 // 2. If cloneConstructor is not present
275 if (constructor->IsUndefined()) {
276 // a. Let cloneConstructor be SpeciesConstructor(srcBuffer, %ArrayBuffer%).
277 JSHandle<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
278 JSHandle<JSObject> objHandle(srcBuffer);
279 constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
280 // b. ReturnIfAbrupt(cloneConstructor).
281 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
282 // c. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
283 if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) {
284 THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
285 } else {
286 ASSERT(constructor->IsConstructor());
287 }
288 }
289 // 4. Let srcLength be the value of srcBuffer’s [[ArrayBufferByteLength]] internal slot.
290 JSHandle<JSArrayBuffer> arrBuf(srcBuffer);
291 uint32_t srcLen = arrBuf->GetArrayBufferByteLength();
292 // 5. Assert: srcByteOffset ≤ srcLength.
293 ASSERT(srcByteOffset <= srcLen);
294 // 6. Let cloneLength be srcLength – srcByteOffset.
295 int32_t cloneLen = static_cast<int32_t>(srcLen - srcByteOffset);
296 // 8. Let targetBuffer be AllocateArrayBuffer(cloneConstructor, cloneLength).
297 JSTaggedValue taggedBuf = AllocateArrayBuffer(thread, constructor, cloneLen);
298 // 9. ReturnIfAbrupt(targetBuffer).
299 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
300 // 10. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
301 if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) {
302 THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
303 }
304 // 11. Let targetBlock be the value of targetBuffer’s [[ArrayBufferData]] internal slot.
305 JSHandle<JSArrayBuffer> newArrBuf(thread, taggedBuf);
306 // Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, cloneLength).
307 // 7. Let srcBlock be the value of srcBuffer’s [[ArrayBufferData]] internal slot.
308 void *fromBuf = GetDataPointFromBuffer(arrBuf.GetTaggedValue());
309 void *toBuf = GetDataPointFromBuffer(taggedBuf);
310 if (cloneLen > 0) {
311 JSArrayBuffer::CopyDataPointBytes(toBuf, fromBuf, srcByteOffset, cloneLen);
312 }
313 return taggedBuf;
314 }
315
316 // 24.1.1.5
317 // NOLINTNEXTLINE(readability-function-size)
GetValueFromBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,bool littleEndian)318 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
319 DataViewType type, bool littleEndian)
320 {
321 void *pointer = GetDataPointFromBuffer(arrBuf);
322 uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
323 return GetValueFromBuffer(thread, byteIndex, block, type, littleEndian);
324 }
325
GetValueFromBuffer(JSThread * thread,uint32_t byteIndex,uint8_t * block,DataViewType type,bool littleEndian)326 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSThread *thread, uint32_t byteIndex, uint8_t *block,
327 DataViewType type, bool littleEndian)
328 {
329 switch (type) {
330 case DataViewType::UINT8:
331 case DataViewType::UINT8_CLAMPED: {
332 uint8_t res = block[byteIndex]; // NOLINT
333 return GetTaggedInt(res);
334 }
335 case DataViewType::INT8: {
336 uint8_t res = block[byteIndex]; // NOLINT
337 auto int8Res = static_cast<int8_t>(res);
338 return GetTaggedInt(int8Res);
339 }
340 case DataViewType::UINT16:
341 return GetValueFromBufferForInteger<uint16_t, NumberSize::UINT16>(block, byteIndex, littleEndian);
342 case DataViewType::INT16:
343 return GetValueFromBufferForInteger<int16_t, NumberSize::INT16>(block, byteIndex, littleEndian);
344 case DataViewType::UINT32:
345 return GetValueFromBufferForInteger<uint32_t, NumberSize::UINT32>(block, byteIndex, littleEndian);
346 case DataViewType::INT32:
347 return GetValueFromBufferForInteger<int32_t, NumberSize::INT32>(block, byteIndex, littleEndian);
348 case DataViewType::FLOAT32:
349 return GetValueFromBufferForFloat<float, UnionType32, NumberSize::FLOAT32>(block, byteIndex, littleEndian);
350 case DataViewType::FLOAT64:
351 return GetValueFromBufferForFloat<double, UnionType64, NumberSize::FLOAT64>(block, byteIndex, littleEndian);
352 case DataViewType::BIGINT64:
353 return GetValueFromBufferForBigInt<int64_t, NumberSize::BIGINT64>(thread, block, byteIndex, littleEndian);
354 case DataViewType::BIGUINT64:
355 return GetValueFromBufferForBigInt<uint64_t, NumberSize::BIGUINT64>(thread, block, byteIndex, littleEndian);
356 default:
357 break;
358 }
359 LOG_ECMA(FATAL) << "this branch is unreachable";
360 UNREACHABLE();
361 }
362
363 // 24.1.1.6
SetValueInBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,const JSHandle<JSTaggedValue> & value,bool littleEndian)364 JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
365 DataViewType type, const JSHandle<JSTaggedValue> &value,
366 bool littleEndian)
367 {
368 if (UNLIKELY(IsBigIntElementType(type))) {
369 JSHandle<JSTaggedValue> arrBufHandle(thread, arrBuf);
370 switch (type) {
371 case DataViewType::BIGINT64:
372 SetValueInBufferForBigInt<int64_t>(thread, value, arrBufHandle, byteIndex, littleEndian);
373 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
374 break;
375 case DataViewType::BIGUINT64:
376 SetValueInBufferForBigInt<uint64_t>(thread, value, arrBufHandle, byteIndex, littleEndian);
377 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
378 break;
379 default:
380 LOG_ECMA(FATAL) << "this branch is unreachable";
381 UNREACHABLE();
382 }
383 return JSTaggedValue::Undefined();
384 }
385 void *pointer = GetDataPointFromBuffer(arrBuf);
386 uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
387 double val = value.GetTaggedValue().GetNumber();
388 return SetValueInBuffer(thread, byteIndex, block, type, val, littleEndian);
389 }
390
391 // es12 25.1.2.7 IsBigIntElementType ( type )
IsBigIntElementType(DataViewType type)392 bool BuiltinsArrayBuffer::IsBigIntElementType(DataViewType type)
393 {
394 if (type == DataViewType::BIGINT64 || type == DataViewType::BIGUINT64) {
395 return true;
396 }
397 return false;
398 }
399
400 // es12 25.1.2.6 IsUnclampedIntegerElementType ( type )
IsUnclampedIntegerElementType(DataViewType type)401 bool BuiltinsArrayBuffer::IsUnclampedIntegerElementType(DataViewType type)
402 {
403 switch (type) {
404 case DataViewType::INT8:
405 case DataViewType::INT16:
406 case DataViewType::INT32:
407 case DataViewType::UINT8:
408 case DataViewType::UINT16:
409 case DataViewType::UINT32:
410 return true;
411 default:
412 return false;
413 }
414 }
415
416 template<typename T>
SetTypeData(uint8_t * block,T value,uint32_t index)417 void BuiltinsArrayBuffer::SetTypeData(uint8_t *block, T value, uint32_t index)
418 {
419 uint32_t sizeCount = sizeof(T);
420 uint8_t *res = reinterpret_cast<uint8_t *>(&value);
421 for (uint32_t i = 0; i < sizeCount; i++) {
422 *(block + index + i) = *(res + i); // NOLINT
423 }
424 }
425
426 template <typename T>
LittleEndianToBigEndian(T liValue)427 T BuiltinsArrayBuffer::LittleEndianToBigEndian(T liValue)
428 {
429 uint8_t sizeCount = sizeof(T);
430 T biValue;
431 switch (sizeCount) {
432 case NumberSize::UINT16:
433 biValue = ((liValue & 0x00FF) << BITS_EIGHT) // NOLINT
434 | ((liValue & 0xFF00) >> BITS_EIGHT); // NOLINT
435 break;
436 case NumberSize::UINT32:
437 biValue = ((liValue & 0x000000FF) << BITS_TWENTY_FOUR) // NOLINT
438 | ((liValue & 0x0000FF00) << BITS_EIGHT) // NOLINT
439 | ((liValue & 0x00FF0000) >> BITS_EIGHT) // NOLINT
440 | ((liValue & 0xFF000000) >> BITS_TWENTY_FOUR); // NOLINT
441 break;
442 default:
443 LOG_ECMA(FATAL) << "this branch is unreachable";
444 UNREACHABLE();
445 break;
446 }
447 return biValue;
448 }
449 template <typename T>
LittleEndianToBigEndian64Bit(T liValue)450 T BuiltinsArrayBuffer::LittleEndianToBigEndian64Bit(T liValue)
451 {
452 return ((liValue & 0x00000000000000FF) << BITS_FIFTY_SIX) // NOLINT
453 | ((liValue & 0x000000000000FF00) << BITS_FORTY) // NOLINT
454 | ((liValue & 0x0000000000FF0000) << BITS_TWENTY_FOUR) // NOLINT
455 | ((liValue & 0x00000000FF000000) << BITS_EIGHT) // NOLINT
456 | ((liValue & 0x000000FF00000000) >> BITS_EIGHT) // NOLINT
457 | ((liValue & 0x0000FF0000000000) >> BITS_TWENTY_FOUR) // NOLINT
458 | ((liValue & 0x00FF000000000000) >> BITS_FORTY) // NOLINT
459 | ((liValue & 0xFF00000000000000) >> BITS_FIFTY_SIX); // NOLINT
460 }
461
462 template<typename T, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForInteger(uint8_t * block,uint32_t byteIndex,bool littleEndian)463 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForInteger(uint8_t *block, uint32_t byteIndex, bool littleEndian)
464 {
465 ASSERT_PRINT(std::is_integral_v<T>, "T must be integral");
466 ASSERT_PRINT(sizeof(T) == size, "Invalid number size");
467 ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
468
469 ASSERT(size >= NumberSize::UINT16 || size <= NumberSize::FLOAT64);
470 T res = *reinterpret_cast<T *>(block + byteIndex);
471 if (!littleEndian) {
472 res = LittleEndianToBigEndian(res);
473 }
474
475 // uint32_t maybe overflow with TaggedInt
476 // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon)
477 if constexpr (std::is_same_v<T, uint32_t>) {
478 // NOLINTNEXTLINE(clang-diagnostic-sign-compare)
479 if (res > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
480 return GetTaggedDouble(static_cast<double>(res));
481 }
482 }
483 return GetTaggedInt(res);
484 }
485
486 template<typename T, typename UnionType, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForFloat(uint8_t * block,uint32_t byteIndex,bool littleEndian)487 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForFloat(uint8_t *block, uint32_t byteIndex, bool littleEndian)
488 {
489 ASSERT_PRINT((std::is_same_v<T, float> || std::is_same_v<T, double>), "T must be correct type");
490 ASSERT_PRINT(sizeof(T) == size, "Invalid number size");
491
492 UnionType unionValue = {0};
493 // NOLINTNEXTLINE(readability-braces-around-statements)
494 if constexpr (std::is_same_v<T, float>) {
495 unionValue.uValue = *reinterpret_cast<uint32_t *>(block + byteIndex);
496 if (std::isnan(unionValue.value)) {
497 if (!JSTaggedValue::IsImpureNaN(unionValue.value)) {
498 return GetTaggedDouble(unionValue.value);
499 } else {
500 return GetTaggedDouble(base::NAN_VALUE);
501 }
502 }
503 if (!littleEndian) {
504 uint32_t res = LittleEndianToBigEndian(unionValue.uValue);
505 return GetTaggedDouble(base::bit_cast<T>(res));
506 }
507 } else if constexpr (std::is_same_v<T, double>) { // NOLINTNEXTLINE(readability-braces-around-statements)
508 unionValue.uValue = *reinterpret_cast<uint64_t *>(block + byteIndex);
509 if (std::isnan(unionValue.value) && !JSTaggedValue::IsImpureNaN(unionValue.value)) {
510 return GetTaggedDouble(unionValue.value);
511 }
512 if (!littleEndian) {
513 uint64_t res = LittleEndianToBigEndian64Bit(unionValue.uValue);
514 T d = base::bit_cast<T>(res);
515 if (JSTaggedValue::IsImpureNaN(d)) {
516 return GetTaggedDouble(base::bit_cast<T>(base::pureNaN));
517 }
518 return GetTaggedDouble(d);
519 } else {
520 if (JSTaggedValue::IsImpureNaN(unionValue.value)) {
521 return GetTaggedDouble(base::NAN_VALUE);
522 }
523 }
524 }
525
526 return GetTaggedDouble(unionValue.value);
527 }
528 template<typename T, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForBigInt(JSThread * thread,uint8_t * block,uint32_t byteIndex,bool littleEndian)529 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForBigInt(JSThread *thread, uint8_t *block,
530 uint32_t byteIndex, bool littleEndian)
531 {
532 ASSERT_PRINT((std::is_same_v<T, uint64_t> || std::is_same_v<T, int64_t>), "T must be uint64_t/int64_t");
533 auto pTmp = *reinterpret_cast<uint64_t *>(block + byteIndex);
534 if (!littleEndian) {
535 pTmp = LittleEndianToBigEndian64Bit(pTmp);
536 }
537 if constexpr (std::is_same_v<T, uint64_t>) {
538 return BigInt::Uint64ToBigInt(thread, pTmp).GetTaggedValue();
539 }
540 return BigInt::Int64ToBigInt(thread, pTmp).GetTaggedValue();
541 }
542
543
544 template<typename T>
SetValueInBufferForByte(double val,uint8_t * block,uint32_t byteIndex)545 void BuiltinsArrayBuffer::SetValueInBufferForByte(double val, uint8_t *block, uint32_t byteIndex)
546 {
547 ASSERT_PRINT((std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t>), "T must be int8/uint8");
548 T res;
549 if (std::isnan(val) || std::isinf(val)) {
550 res = 0;
551 SetTypeData(block, res, byteIndex);
552 return;
553 }
554 auto int64Val = static_cast<int64_t>(val);
555 auto *resArr = reinterpret_cast<T *>(&int64Val);
556 res = *resArr;
557 SetTypeData(block, res, byteIndex);
558 }
559
SetValueInBufferForUint8Clamped(double val,uint8_t * block,uint32_t byteIndex)560 void BuiltinsArrayBuffer::SetValueInBufferForUint8Clamped(double val, uint8_t *block, uint32_t byteIndex)
561 {
562 uint8_t res;
563 if (std::isnan(val) || val <= 0) {
564 res = 0;
565 SetTypeData(block, res, byteIndex);
566 return;
567 }
568 val = val >= UINT8_MAX ? UINT8_MAX : val;
569 constexpr double HALF = 0.5;
570 val = val == HALF ? 0 : std::round(val);
571 res = static_cast<uint64_t>(val);
572 SetTypeData(block, res, byteIndex);
573 }
574
575 template<typename T>
SetValueInBufferForInteger(double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)576 void BuiltinsArrayBuffer::SetValueInBufferForInteger(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian)
577 {
578 ASSERT_PRINT(std::is_integral_v<T>, "T must be integral");
579 ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
580 T res;
581 if (std::isnan(val) || std::isinf(val)) {
582 res = 0;
583 SetTypeData(block, res, byteIndex);
584 return;
585 }
586 auto int64Val = static_cast<int64_t>(val);
587 // NOLINTNEXTLINE(readability-braces-around-statements)
588 if constexpr (std::is_same_v<T, uint16_t>) {
589 auto *pTmp = reinterpret_cast<int16_t *>(&int64Val);
590 int16_t tmp = *pTmp;
591 res = static_cast<T>(tmp);
592 } else { // NOLINTNEXTLINE(readability-braces-around-statements)
593 auto *pTmp = reinterpret_cast<T *>(&int64Val);
594 res = *pTmp;
595 }
596
597 if (!littleEndian) {
598 res = LittleEndianToBigEndian<T>(res);
599 }
600 SetTypeData(block, res, byteIndex);
601 }
602
603 template<typename T>
SetValueInBufferForFloat(double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)604 void BuiltinsArrayBuffer::SetValueInBufferForFloat(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian)
605 {
606 ASSERT_PRINT((std::is_same_v<T, float> || std::is_same_v<T, double>), "T must be float type");
607 auto data = static_cast<T>(val);
608 if (std::isnan(val)) {
609 SetTypeData(block, data, byteIndex);
610 return;
611 }
612 if (!littleEndian) {
613 if constexpr (std::is_same_v<T, float>) {
614 uint32_t res = base::bit_cast<uint32_t>(data);
615 data = base::bit_cast<T>(LittleEndianToBigEndian(res));
616 } else if constexpr (std::is_same_v<T, double>) {
617 uint64_t res = base::bit_cast<uint64_t>(data);
618 data = base::bit_cast<T>(LittleEndianToBigEndian64Bit(res));
619 }
620 }
621 SetTypeData(block, data, byteIndex);
622 }
623
624 template<typename T>
SetValueInBufferForBigInt(JSThread * thread,const JSHandle<JSTaggedValue> & val,JSHandle<JSTaggedValue> & arrBuf,uint32_t byteIndex,bool littleEndian)625 void BuiltinsArrayBuffer::SetValueInBufferForBigInt(JSThread *thread,
626 const JSHandle<JSTaggedValue> &val,
627 JSHandle<JSTaggedValue> &arrBuf,
628 uint32_t byteIndex, bool littleEndian)
629 {
630 ASSERT_PRINT((std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>), "T must be int64_t/uint64_t");
631 T value = 0;
632 bool lossless = true;
633 if constexpr(std::is_same_v<T, uint64_t>) {
634 BigInt::BigIntToUint64(thread, val, reinterpret_cast<uint64_t *>(&value), &lossless);
635 } else {
636 BigInt::BigIntToInt64(thread, val, reinterpret_cast<int64_t *>(&value), &lossless);
637 }
638 RETURN_IF_ABRUPT_COMPLETION(thread);
639 if (!littleEndian) {
640 value = LittleEndianToBigEndian64Bit<T>(value);
641 }
642 void *pointer = GetDataPointFromBuffer(arrBuf.GetTaggedValue());
643 uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
644 SetTypeData(block, value, byteIndex);
645 }
646
647 template<typename T>
SetValueInBufferForBigInt(JSThread * thread,double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)648 void BuiltinsArrayBuffer::SetValueInBufferForBigInt(JSThread *thread,
649 double val, uint8_t *block,
650 uint32_t byteIndex, bool littleEndian)
651 {
652 ASSERT_PRINT((std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>), "T must be int64_t/uint64_t");
653 T value = 0;
654 bool lossless = true;
655
656 JSHandle<JSTaggedValue> valHandle(thread, GetTaggedDouble(val));
657 if constexpr(std::is_same_v<T, uint64_t>) {
658 BigInt::BigIntToUint64(thread, valHandle, reinterpret_cast<uint64_t *>(&value), &lossless);
659 } else {
660 BigInt::BigIntToInt64(thread, valHandle, reinterpret_cast<int64_t *>(&value), &lossless);
661 }
662 RETURN_IF_ABRUPT_COMPLETION(thread);
663 if (!littleEndian) {
664 value = LittleEndianToBigEndian64Bit<T>(value);
665 }
666 SetTypeData(block, value, byteIndex);
667 }
668
FastSetValueInBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,double val,bool littleEndian)669 JSTaggedValue BuiltinsArrayBuffer::FastSetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
670 DataViewType type, double val, bool littleEndian)
671 {
672 void *pointer = GetDataPointFromBuffer(arrBuf);
673 uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
674 return SetValueInBuffer(thread, byteIndex, block, type, val, littleEndian);
675 }
676
SetValueInBuffer(JSThread * thread,uint32_t byteIndex,uint8_t * block,DataViewType type,double val,bool littleEndian)677 JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSThread* thread, uint32_t byteIndex, uint8_t *block,
678 DataViewType type, double val, bool littleEndian)
679 {
680 switch (type) {
681 case DataViewType::UINT8:
682 SetValueInBufferForByte<uint8_t>(val, block, byteIndex);
683 break;
684 case DataViewType::UINT8_CLAMPED:
685 SetValueInBufferForUint8Clamped(val, block, byteIndex);
686 break;
687 case DataViewType::INT8:
688 SetValueInBufferForByte<int8_t>(val, block, byteIndex);
689 break;
690 case DataViewType::UINT16:
691 SetValueInBufferForInteger<uint16_t>(val, block, byteIndex, littleEndian);
692 break;
693 case DataViewType::INT16:
694 SetValueInBufferForInteger<int16_t>(val, block, byteIndex, littleEndian);
695 break;
696 case DataViewType::UINT32:
697 SetValueInBufferForInteger<uint32_t>(val, block, byteIndex, littleEndian);
698 break;
699 case DataViewType::INT32:
700 SetValueInBufferForInteger<int32_t>(val, block, byteIndex, littleEndian);
701 break;
702 case DataViewType::FLOAT32:
703 SetValueInBufferForFloat<float>(val, block, byteIndex, littleEndian);
704 break;
705 case DataViewType::FLOAT64:
706 SetValueInBufferForFloat<double>(val, block, byteIndex, littleEndian);
707 break;
708 case DataViewType::BIGINT64:
709 SetValueInBufferForBigInt<int64_t>(thread, val, block, byteIndex, littleEndian);
710 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
711 break;
712 case DataViewType::BIGUINT64:
713 SetValueInBufferForBigInt<uint64_t>(thread, val, block, byteIndex, littleEndian);
714 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
715 break;
716 default:
717 LOG_ECMA(FATAL) << "this branch is unreachable";
718 UNREACHABLE();
719 }
720 return JSTaggedValue::Undefined();
721 }
722
GetDataPointFromBuffer(JSTaggedValue arrBuf,uint32_t byteOffset)723 void *BuiltinsArrayBuffer::GetDataPointFromBuffer(JSTaggedValue arrBuf, uint32_t byteOffset)
724 {
725 if (arrBuf.IsByteArray()) {
726 return reinterpret_cast<void *>(ToUintPtr(ByteArray::Cast(arrBuf.GetTaggedObject())->GetData()) + byteOffset);
727 }
728
729 JSArrayBuffer *arrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject());
730 if (arrayBuffer->GetArrayBufferByteLength() == 0) {
731 return nullptr;
732 }
733
734 JSTaggedValue data = arrayBuffer->GetArrayBufferData();
735 return reinterpret_cast<void *>(ToUintPtr(JSNativePointer::Cast(data.GetTaggedObject())
736 ->GetExternalPointer()) + byteOffset);
737 }
738 } // namespace panda::ecmascript::builtins
739