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/shared_objects/js_shared_array.h"
17
18 #include "ecmascript/interpreter/interpreter.h"
19 #include "ecmascript/object_fast_operator-inl.h"
20
21 namespace panda::ecmascript {
22 using base::ArrayHelper;
23
LengthGetter(JSThread * thread,const JSHandle<JSObject> & self,SCheckMode checkMode)24 JSTaggedValue JSSharedArray::LengthGetter([[maybe_unused]] JSThread *thread, const JSHandle<JSObject> &self,
25 SCheckMode checkMode)
26 {
27 [[maybe_unused]] ConcurrentApiScope<JSSharedArray> scope(thread, JSHandle<JSTaggedValue>::Cast(self),
28 checkMode);
29 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
30 return JSTaggedValue(JSSharedArray::Cast(*self)->GetLength());
31 }
32
DummyLengthSetter(JSThread * thread,const JSHandle<JSObject> & self,const JSHandle<JSTaggedValue> & value,bool mayThrow)33 bool JSSharedArray::DummyLengthSetter([[maybe_unused]] JSThread *thread,
34 [[maybe_unused]] const JSHandle<JSObject> &self,
35 [[maybe_unused]] const JSHandle<JSTaggedValue> &value,
36 [[maybe_unused]] bool mayThrow)
37 {
38 // length is the read only property for Shared Array
39 return true;
40 }
41
LengthSetter(JSThread * thread,const JSHandle<JSObject> & self,const JSHandle<JSTaggedValue> & value,bool mayThrow)42 bool JSSharedArray::LengthSetter(JSThread *thread, const JSHandle<JSObject> &self, const JSHandle<JSTaggedValue> &value,
43 bool mayThrow)
44 {
45 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
46 uint32_t newLen = 0;
47 if (!JSTaggedValue::ToArrayLength(thread, value, &newLen) && mayThrow) {
48 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
49 }
50
51 uint32_t oldLen = JSSharedArray::Cast(*self)->GetArrayLength();
52 if (oldLen == newLen) {
53 return true;
54 }
55
56 if (!IsArrayLengthWritable(thread, self)) {
57 if (mayThrow) {
58 THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty), false);
59 }
60 return false;
61 }
62
63 JSSharedArray::SetCapacity(thread, self, oldLen, newLen);
64 uint32_t actualLen = JSSharedArray::Cast(*self)->GetArrayLength();
65 if (actualLen != newLen) {
66 if (mayThrow) {
67 THROW_TYPE_ERROR_AND_RETURN(thread, "Not all array elements is configurable", false);
68 }
69 return false;
70 }
71
72 return true;
73 }
74
ArrayCreate(JSThread * thread,JSTaggedNumber length,ArrayMode mode)75 JSHandle<JSTaggedValue> JSSharedArray::ArrayCreate(JSThread *thread, JSTaggedNumber length, ArrayMode mode)
76 {
77 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
78 JSHandle<JSTaggedValue> sharedArrayFunction = env->GetSharedArrayFunction();
79 return JSSharedArray::ArrayCreate(thread, length, sharedArrayFunction, mode);
80 }
81
82 // 9.4.2.2 ArrayCreate(length, proto)
ArrayCreate(JSThread * thread,JSTaggedNumber length,const JSHandle<JSTaggedValue> & newTarget,ArrayMode mode)83 JSHandle<JSTaggedValue> JSSharedArray::ArrayCreate(JSThread *thread, JSTaggedNumber length,
84 const JSHandle<JSTaggedValue> &newTarget, ArrayMode mode)
85 {
86 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
87 // Assert: length is an integer Number ≥ 0.
88 ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer");
89 // 2. If length is −0, let length be +0.
90 double arrayLength = length.GetNumber();
91 if (arrayLength > MAX_ARRAY_INDEX) {
92 JSHandle<JSTaggedValue> exception(thread, JSTaggedValue::Exception());
93 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::TYPE_ERROR,
94 "Parameter error.Array length must less than 2^32.");
95 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, exception);
96 }
97 uint32_t normalArrayLength = length.ToUint32();
98
99 // 8. Set the [[Prototype]] internal slot of A to proto.
100 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
101 JSHandle<JSFunction> arrayFunc(env->GetSharedArrayFunction());
102 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(arrayFunc, newTarget);
103 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
104 // 9. Set the [[Extensible]] internal slot of A to true.
105
106 // 10. Perform OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]:
107 // true, [[Enumerable]]: false, [[Configurable]]: false}).
108 if (mode == ArrayMode::LITERAL) {
109 JSSharedArray::Cast(*obj)->SetArrayLength(thread, normalArrayLength);
110 } else {
111 JSSharedArray::SetCapacity(thread, obj, 0, normalArrayLength, true);
112 }
113 return JSHandle<JSTaggedValue>(obj);
114 }
115
116 // 9.4.2.3 ArraySpeciesCreate(originalArray, length)
ArraySpeciesCreate(JSThread * thread,const JSHandle<JSObject> & originalArray,JSTaggedNumber length)117 JSTaggedValue JSSharedArray::ArraySpeciesCreate(JSThread *thread, const JSHandle<JSObject> &originalArray,
118 JSTaggedNumber length)
119 {
120 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
121 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
122 // Assert: length is an integer Number ≥ 0.
123 ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer");
124 // If length is −0, let length be +0.
125 int64_t arrayLength = length.GetNumber();
126 if (arrayLength == -0) {
127 arrayLength = +0;
128 }
129 // Let C be undefined.
130 // Let isArray be IsArray(originalArray).
131 JSHandle<JSTaggedValue> originalValue(originalArray);
132 bool isSArray = originalValue->IsJSSharedArray();
133 // ReturnIfAbrupt(isArray).
134 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
135 // If isArray is true, then
136 JSHandle<JSTaggedValue> constructor(thread, JSTaggedValue::Undefined());
137 if (isSArray) {
138 // Let C be Get(originalArray, "constructor").
139 auto *hclass = originalArray->GetJSHClass();
140 JSTaggedValue proto = hclass->GetPrototype();
141 if (hclass->IsJSSharedArray() && !hclass->HasConstructor() && proto.IsJSSharedArray()) {
142 return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue();
143 }
144 JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString();
145 constructor = JSTaggedValue::GetProperty(thread, originalValue, constructorKey).GetValue();
146 // ReturnIfAbrupt(C).
147 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
148 // If IsConstructor(C) is true, then
149 if (constructor->IsConstructor()) {
150 // Let thisRealm be the running execution context’s Realm.
151 // Let realmC be GetFunctionRealm(C).
152 JSHandle<GlobalEnv> realmC = JSObject::GetFunctionRealm(thread, constructor);
153 // ReturnIfAbrupt(realmC).
154 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
155 // If thisRealm and realmC are not the same Realm Record, then
156 if (*realmC != *env) {
157 JSTaggedValue realmArrayConstructor = realmC->GetSharedArrayFunction().GetTaggedValue();
158 // If SameValue(C, realmC.[[intrinsics]].[[%Array%]]) is true, let C be undefined.
159 if (JSTaggedValue::SameValue(constructor.GetTaggedValue(), realmArrayConstructor)) {
160 return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue();
161 }
162 }
163 }
164
165 // If Type(C) is Object, then
166 if (constructor->IsECMAObject()) {
167 // Let C be Get(C, @@species).
168 JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
169 constructor = JSTaggedValue::GetProperty(thread, constructor, speciesSymbol).GetValue();
170 // ReturnIfAbrupt(C).
171 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
172 // If C is null, let C be undefined.
173 if (constructor->IsNull()) {
174 return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue();
175 }
176 }
177 }
178
179 // If C is undefined, return ArrayCreate(length).
180 if (constructor->IsUndefined()) {
181 return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue();
182 }
183 // If IsConstructor(C) is false, throw a TypeError exception.
184 if (!constructor->IsConstructor()) {
185 THROW_TYPE_ERROR_AND_RETURN(thread, "Not a constructor", JSTaggedValue::Exception());
186 }
187 // Return Construct(C, «length»).
188 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
189 EcmaRuntimeCallInfo *info =
190 EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 1);
191 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
192 info->SetCallArg(JSTaggedValue(arrayLength));
193 JSTaggedValue result = JSFunction::Construct(info);
194 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
195
196 // NOTEIf originalArray was created using the standard built-in Array constructor for
197 // a Realm that is not the Realm of the running execution context, then a new Array is
198 // created using the Realm of the running execution context. This maintains compatibility
199 // with Web browsers that have historically had that behaviour for the Array.prototype methods
200 // that now are defined using ArraySpeciesCreate.
201 return result;
202 }
203
SetCapacity(const JSThread * thread,const JSHandle<TaggedArray> & array,uint32_t capa)204 JSHandle<TaggedArray> JSSharedArray::SetCapacity(const JSThread *thread, const JSHandle<TaggedArray> &array,
205 uint32_t capa)
206 {
207 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
208 uint32_t oldLength = array->GetLength();
209 JSHandle<TaggedArray> newArray = factory->CopySArray(array, oldLength, capa);
210 return newArray;
211 }
212
SetCapacity(JSThread * thread,const JSHandle<JSObject> & array,uint32_t oldLen,uint32_t newLen,bool isNew)213 void JSSharedArray::SetCapacity(JSThread *thread, const JSHandle<JSObject> &array, uint32_t oldLen, uint32_t newLen,
214 bool isNew)
215 {
216 TaggedArray *element = TaggedArray::Cast(array->GetElements().GetTaggedObject());
217
218 if (element->IsDictionaryMode()) {
219 THROW_TYPE_ERROR(thread, "SendableArray don't support dictionary mode.");
220 }
221 uint32_t capacity = element->GetLength();
222 if (newLen <= capacity) {
223 // judge if need to cut down the array size, else fill the unused tail with holes
224 CheckAndCopyArray(thread, JSHandle<JSSharedArray>(array));
225 array->FillElementsWithHoles(thread, newLen, oldLen < capacity ? oldLen : capacity);
226 }
227 if (newLen > capacity) {
228 JSObject::GrowElementsCapacity(thread, array, newLen, isNew);
229 }
230 JSSharedArray::Cast(*array)->SetArrayLength(thread, newLen);
231 }
232
ArraySetLength(JSThread * thread,const JSHandle<JSObject> & array,const PropertyDescriptor & desc)233 bool JSSharedArray::ArraySetLength(JSThread *thread, const JSHandle<JSObject> &array, const PropertyDescriptor &desc)
234 {
235 JSHandle<JSTaggedValue> lengthKeyHandle(thread->GlobalConstants()->GetHandledLengthString());
236
237 // 1. If the [[Value]] field of Desc is absent, then
238 if (!desc.HasValue()) {
239 // 1a. Return OrdinaryDefineOwnProperty(A, "length", Desc).
240 return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, desc);
241 }
242 // 2. Let newLenDesc be a copy of Desc.
243 // (Actual copying is not necessary.)
244 PropertyDescriptor newLenDesc = desc;
245 // 3. - 7. Convert Desc.[[Value]] to newLen.
246 uint32_t newLen = 0;
247 if (!JSTaggedValue::ToArrayLength(thread, desc.GetValue(), &newLen)) {
248 THROW_RANGE_ERROR_AND_RETURN(thread, "array length must equal or less than 2^32.", false);
249 }
250 // 8. Set newLenDesc.[[Value]] to newLen.
251 // (Done below, if needed.)
252 // 9. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
253 PropertyDescriptor oldLenDesc(thread);
254 [[maybe_unused]] bool success = GetOwnProperty(thread, array, lengthKeyHandle, oldLenDesc);
255 // 10. (Assert)
256 ASSERT(success);
257
258 // 11. Let oldLen be oldLenDesc.[[Value]].
259 uint32_t oldLen = 0;
260 JSTaggedValue::ToArrayLength(thread, oldLenDesc.GetValue(), &oldLen);
261 // 12. If newLen >= oldLen, then
262 if (newLen >= oldLen) {
263 // 8. Set newLenDesc.[[Value]] to newLen.
264 // 12a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc).
265 newLenDesc.SetValue(JSHandle<JSTaggedValue>(thread, JSTaggedValue(newLen)));
266 return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, newLenDesc);
267 }
268 // 13. If oldLenDesc.[[Writable]] is false, return false.
269 if (!oldLenDesc.IsWritable() ||
270 // Also handle the {configurable: true} case since we later use
271 // JSSharedArray::SetLength instead of OrdinaryDefineOwnProperty to change
272 // the length, and it doesn't have access to the descriptor anymore.
273 newLenDesc.IsConfigurable() ||
274 (newLenDesc.HasEnumerable() && (newLenDesc.IsEnumerable() != oldLenDesc.IsEnumerable()))) {
275 return false;
276 }
277 // 14. If newLenDesc.[[Writable]] is absent or has the value true,
278 // let newWritable be true.
279 bool newWritable = false;
280 if (!newLenDesc.HasWritable() || newLenDesc.IsWritable()) {
281 newWritable = true;
282 } else {
283 // 15. Else,
284 // 15a. Need to defer setting the [[Writable]] attribute to false in case
285 // any elements cannot be deleted.
286 // 15b. Let newWritable be false. (It's initialized as "false" anyway.)
287 // 15c. Set newLenDesc.[[Writable]] to true.
288 // (Not needed.)
289 }
290
291 // Most of steps 16 through 19 is implemented by JSSharedArray::SetCapacity.
292 JSSharedArray::SetCapacity(thread, array, oldLen, newLen);
293 // Steps 19d-ii, 20.
294 if (!newWritable) {
295 PropertyDescriptor readonly(thread);
296 readonly.SetWritable(false);
297 success = JSObject::DefineOwnProperty(thread, array, lengthKeyHandle, readonly);
298 ASSERT_PRINT(success, "DefineOwnProperty of length must be success here!");
299 }
300
301 // Steps 19d-v, 21. Return false if there were non-deletable elements.
302 uint32_t arrayLength = JSSharedArray::Cast(*array)->GetArrayLength();
303 return arrayLength == newLen;
304 }
305
PropertyKeyToArrayIndex(JSThread * thread,const JSHandle<JSTaggedValue> & key,uint32_t * output)306 bool JSSharedArray::PropertyKeyToArrayIndex(JSThread *thread, const JSHandle<JSTaggedValue> &key, uint32_t *output)
307 {
308 return JSTaggedValue::ToArrayLength(thread, key, output) && *output <= JSSharedArray::MAX_ARRAY_INDEX;
309 }
310
311 // 9.4.2.1 [[DefineOwnProperty]] ( P, Desc)
DefineOwnProperty(JSThread * thread,const JSHandle<JSObject> & array,const JSHandle<JSTaggedValue> & key,const PropertyDescriptor & desc,SCheckMode sCheckMode)312 bool JSSharedArray::DefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &array,
313 const JSHandle<JSTaggedValue> &key, const PropertyDescriptor &desc,
314 SCheckMode sCheckMode)
315 {
316 if (!desc.GetValue()->IsSharedType() || (desc.HasGetter() && !desc.GetGetter()->IsSharedType()) ||
317 (desc.HasSetter() && !desc.GetSetter()->IsSharedType())) {
318 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::TYPE_ERROR,
319 "Parameter error. Only accept sendable value.");
320 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
321 }
322
323 if (sCheckMode == SCheckMode::CHECK && !(JSSharedArray::Cast(*array)->IsKeyInRange(key))) {
324 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::RANGE_ERROR,
325 "Key out of length.");
326 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
327 }
328
329 // 1. Assert: IsPropertyKey(P) is true.
330 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key!");
331 // 2. If P is "length", then
332 if (IsLengthString(thread, key)) {
333 // a. Return ArraySetLength(A, Desc).
334 return ArraySetLength(thread, array, desc);
335 }
336 // 3. Else if P is an array index, then
337 // already do in step 4.
338 // 4. Return OrdinaryDefineOwnProperty(A, P, Desc).
339 bool success = JSObject::OrdinaryDefineOwnProperty(thread, array, key, desc);
340 if (success) {
341 JSTaggedValue constructorKey = thread->GlobalConstants()->GetConstructorString();
342 if (key.GetTaggedValue() == constructorKey) {
343 array->GetJSHClass()->SetHasConstructor(true);
344 return true;
345 }
346 }
347 return success;
348 }
349
IsLengthString(JSThread * thread,const JSHandle<JSTaggedValue> & key)350 bool JSSharedArray::IsLengthString(JSThread *thread, const JSHandle<JSTaggedValue> &key)
351 {
352 return key.GetTaggedValue() == thread->GlobalConstants()->GetLengthString();
353 }
354
CreateArrayFromList(JSThread * thread,const JSHandle<TaggedArray> & elements)355 JSHandle<JSSharedArray> JSSharedArray::CreateArrayFromList(JSThread *thread, const JSHandle<TaggedArray> &elements)
356 {
357 // Assert: elements is a List whose elements are all ECMAScript language values.
358 // 2. Let array be ArrayCreate(0).
359 uint32_t length = elements->GetLength();
360
361 // 4. For each element e of elements
362 auto env = thread->GetEcmaVM()->GetGlobalEnv();
363 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
364 JSHandle<JSFunction> arrayFunc(env->GetSharedArrayFunction());
365 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(arrayFunc);
366 JSSharedArray::Cast(*obj)->SetArrayLength(thread, length);
367 obj->SetElements(thread, elements);
368 obj->GetJSHClass()->SetExtensible(false);
369 JSHandle<JSSharedArray> arr(obj);
370
371 return arr;
372 }
373
FastGetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t index)374 JSHandle<JSTaggedValue> JSSharedArray::FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
375 uint32_t index)
376 {
377 auto result = ObjectFastOperator::FastGetPropertyByIndex(thread, obj.GetTaggedValue(), index);
378 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
379 return JSHandle<JSTaggedValue>(thread, result);
380 }
381
FastGetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,SCheckMode sCheckMode)382 JSHandle<JSTaggedValue> JSSharedArray::FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
383 const JSHandle<JSTaggedValue> &key, SCheckMode sCheckMode)
384 {
385 auto result =
386 ObjectFastOperator::FastGetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(), sCheckMode);
387 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
388 return JSHandle<JSTaggedValue>(thread, result);
389 }
390
FastSetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t index,const JSHandle<JSTaggedValue> & value)391 bool JSSharedArray::FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, uint32_t index,
392 const JSHandle<JSTaggedValue> &value)
393 {
394 return ObjectFastOperator::FastSetPropertyByIndex(thread, obj.GetTaggedValue(), index, value.GetTaggedValue());
395 }
396
FastSetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)397 bool JSSharedArray::FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
398 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
399 {
400 return ObjectFastOperator::FastSetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(),
401 value.GetTaggedValue(), SCheckMode::SKIP);
402 }
403
GetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,SCheckMode sCheckMode)404 OperationResult JSSharedArray::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
405 const JSHandle<JSTaggedValue> &key, SCheckMode sCheckMode)
406 {
407 // Add Concurrent check for shared array
408 [[maybe_unused]] ConcurrentApiScope<JSSharedArray> scope(thread, obj,
409 sCheckMode);
410 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
411 OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
412
413 ObjectOperator op(thread, obj, key);
414 // Out of bounds check for shared array
415 if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) {
416 return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(false));
417 }
418 return OperationResult(thread, JSObject::GetProperty(thread, &op), PropertyMetaData(op.IsFound()));
419 }
420
SetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,bool mayThrow,SCheckMode sCheckMode)421 bool JSSharedArray::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
422 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value, bool mayThrow,
423 SCheckMode sCheckMode)
424 {
425 // Concurrent check for shared array
426 [[maybe_unused]] ConcurrentApiScope<JSSharedArray, ModType::WRITE> scope(
427 thread, obj, sCheckMode);
428 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
429 // 2 ~ 4 findProperty in Receiver, Obj and its parents
430 ObjectOperator op(thread, obj, key);
431 // Out of bounds check for shared array
432 if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) {
433 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::RANGE_ERROR,
434 "The value of index is out of range.");
435 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
436 }
437 return JSObject::SetProperty(&op, value, mayThrow);
438 }
439
SetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t index,const JSHandle<JSTaggedValue> & value,bool mayThrow,SCheckMode sCheckMode)440 bool JSSharedArray::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
441 uint32_t index, const JSHandle<JSTaggedValue> &value, bool mayThrow,
442 SCheckMode sCheckMode)
443 {
444 // Concurrent check for shared array
445 [[maybe_unused]] ConcurrentApiScope<JSSharedArray, ModType::WRITE> scope(
446 thread, obj, sCheckMode);
447 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
448 // 2 ~ 4 findProperty in Receiver, Obj and its parents
449 ObjectOperator op(thread, obj, index);
450 // Out of bounds check for shared array
451 if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) {
452 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::RANGE_ERROR,
453 "The value of index is out of range.");
454 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
455 }
456 return JSObject::SetProperty(&op, value, mayThrow);
457 }
458
459 // ecma2024 23.1.3.20 Array.prototype.sort(comparefn)
Sort(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & fn)460 JSTaggedValue JSSharedArray::Sort(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
461 const JSHandle<JSTaggedValue> &fn)
462 {
463 ASSERT(fn->IsUndefined() || fn->IsCallable());
464 // 3. Let len be ?LengthOfArrayLike(obj).
465 int64_t len = ArrayHelper::GetArrayLength(thread, obj);
466 // ReturnIfAbrupt(len).
467 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
468 // If len is 0 or 1, no need to sort
469 if (len == 0 || len == 1) {
470 return obj.GetTaggedValue();
471 }
472
473 // 4. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs
474 // the following steps when called:
475 // a. Return ? CompareArrayElements(x, y, comparefn).
476 // 5. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, SKIP-HOLES).
477 JSHandle<TaggedArray> sortedList =
478 ArrayHelper::SortIndexedProperties(thread, obj, len, fn, base::HolesType::SKIP_HOLES);
479 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
480 // 6. Let itemCount be the number of elements in sortedList.
481 uint32_t itemCount = sortedList->GetLength();
482
483 // 7. Let j be 0.
484 uint32_t j = 0;
485 // 8. Repeat, while j < itemCount,
486 // a. Perform ! Set(obj, ! ToString((j)), sortedList[j], true).
487 // b. Set j to j + 1.
488 JSMutableHandle<JSTaggedValue> item(thread, JSTaggedValue::Undefined());
489 while (j < itemCount) {
490 item.Update(sortedList->Get(j));
491 JSSharedArray::FastSetPropertyByValue(thread, obj, j, item);
492 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
493 ++j;
494 }
495 // 9. NOTE: The call to SortIndexedProperties in step 5 uses SKIP-HOLES.The remaining indices are deleted to
496 // preserve the number of holes that were detected and excluded from the sort.
497 // 10. Repeat, while j < len,
498 // a. Perform ? DeletePropertyOrThrow(obj, ! ToString((j))).
499 // b. Set j to j + 1.
500 while (j < len) {
501 item.Update(JSTaggedValue(j));
502 JSTaggedValue::DeletePropertyOrThrow(thread, obj, item);
503 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
504 ++j;
505 }
506
507 return obj.GetTaggedValue();
508 }
509
IncludeInSortedValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & value)510 bool JSSharedArray::IncludeInSortedValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
511 const JSHandle<JSTaggedValue> &value)
512 {
513 ASSERT(obj->IsJSSharedArray());
514 JSHandle<JSSharedArray> arrayObj = JSHandle<JSSharedArray>::Cast(obj);
515 int32_t length = static_cast<int32_t>(arrayObj->GetArrayLength());
516 if (length == 0) {
517 return false;
518 }
519 int32_t left = 0;
520 int32_t right = length - 1;
521 while (left <= right) {
522 int32_t middle = (left + right) / 2;
523 JSHandle<JSTaggedValue> vv = JSSharedArray::FastGetPropertyByValue(thread, obj, middle);
524 ComparisonResult res = JSTaggedValue::Compare(thread, vv, value);
525 if (res == ComparisonResult::EQUAL) {
526 return true;
527 } else if (res == ComparisonResult::LESS) {
528 left = middle + 1;
529 } else {
530 right = middle - 1;
531 }
532 }
533 return false;
534 }
535
CheckAndCopyArray(const JSThread * thread,JSHandle<JSSharedArray> obj)536 void JSSharedArray::CheckAndCopyArray(const JSThread *thread, JSHandle<JSSharedArray> obj)
537 {
538 JSHandle<TaggedArray> arr(thread, obj->GetElements());
539 // Check whether array is shared in the nonmovable space before set properties and elements.
540 // If true, then really copy array in the semi space.
541 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
542 if (arr.GetTaggedValue().IsCOWArray()) {
543 auto newArray = factory->CopyArray(arr, arr->GetLength(), arr->GetLength(),
544 JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE);
545 obj->SetElements(thread, newArray.GetTaggedValue());
546 }
547 JSHandle<TaggedArray> prop(thread, obj->GetProperties());
548 if (prop.GetTaggedValue().IsCOWArray()) {
549 auto newProps = factory->CopyArray(prop, prop->GetLength(), prop->GetLength(),
550 JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE);
551 obj->SetProperties(thread, newProps.GetTaggedValue());
552 }
553 }
554
DeleteInElementMode(const JSThread * thread,JSHandle<JSSharedArray> & obj)555 void JSSharedArray::DeleteInElementMode(const JSThread *thread, JSHandle<JSSharedArray> &obj)
556 {
557 JSHandle<TaggedArray> elements(thread, obj->GetElements());
558 ASSERT(!obj->GetJSHClass()->IsDictionaryElement());
559 uint32_t length = elements->GetLength();
560 // fixme(hzzhouzebin) Optimize Delete later.
561 uint32_t size = 0;
562 for (uint32_t i = 0; i < length; i++) {
563 JSTaggedValue value = ElementAccessor::Get(JSHandle<JSObject>(obj), i);
564 if (value.IsHole()) {
565 continue;
566 }
567 ++size;
568 }
569 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
570 JSHandle<TaggedArray> newElements(
571 factory->NewTaggedArray(length, JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE));
572 uint32_t newCurr = 0;
573 for (uint32_t i = 0; i < length; i++) {
574 JSTaggedValue value = ElementAccessor::Get(JSHandle<JSObject>(obj), i);
575 if (value.IsHole()) {
576 continue;
577 }
578 newElements->Set(thread, newCurr, value);
579 ++newCurr;
580 }
581 obj->SetElements(thread, newElements);
582 }
583 } // namespace panda::ecmascript
584