• 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/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) { // LCOV_EXCL_START
66         if (mayThrow) {
67             THROW_TYPE_ERROR_AND_RETURN(thread, "Not all array elements is configurable", false);
68         }
69         return false;
70     } // LCOV_EXCL_STOP
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()) { // LCOV_EXCL_START
219         THROW_TYPE_ERROR(thread, "SendableArray don't support dictionary mode.");
220     } // LCOV_EXCL_STOP
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         JSObject::FillElementsWithHoles(thread, array, 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) { // LCOV_EXCL_START
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     } // LCOV_EXCL_STOP
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 
337     // 3. Else if P is an array index, then
338     // already do in step 4.
339     // 4. Return OrdinaryDefineOwnProperty(A, P, Desc).
340     return JSObject::OrdinaryDefineOwnProperty(thread, array, key, desc);
341 }
342 
IsLengthString(JSThread * thread,const JSHandle<JSTaggedValue> & key)343 bool JSSharedArray::IsLengthString(JSThread *thread, const JSHandle<JSTaggedValue> &key)
344 {
345     return key.GetTaggedValue() == thread->GlobalConstants()->GetLengthString();
346 }
347 
CreateArrayFromList(JSThread * thread,const JSHandle<TaggedArray> & elements)348 JSHandle<JSSharedArray> JSSharedArray::CreateArrayFromList(JSThread *thread, const JSHandle<TaggedArray> &elements)
349 {
350     // Assert: elements is a List whose elements are all ECMAScript language values.
351     // 2. Let array be ArrayCreate(0).
352     uint32_t length = elements->GetLength();
353 
354     // 4. For each element e of elements
355     auto env = thread->GetEcmaVM()->GetGlobalEnv();
356     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
357     JSHandle<JSFunction> arrayFunc(env->GetSharedArrayFunction());
358     JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(arrayFunc);
359     JSSharedArray::Cast(*obj)->SetArrayLength(thread, length);
360     obj->SetElements(thread, elements);
361     obj->GetJSHClass()->SetExtensible(false);
362     JSHandle<JSSharedArray> arr(obj);
363 
364     return arr;
365 }
366 
FastGetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t index)367 JSHandle<JSTaggedValue> JSSharedArray::FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
368                                                               uint32_t index)
369 {
370     auto result = ObjectFastOperator::FastGetPropertyByIndex(thread, obj.GetTaggedValue(), index);
371     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
372     return JSHandle<JSTaggedValue>(thread, result);
373 }
374 
FastGetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,SCheckMode sCheckMode)375 JSHandle<JSTaggedValue> JSSharedArray::FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
376                                                               const JSHandle<JSTaggedValue> &key, SCheckMode sCheckMode)
377 {
378     auto result =
379         ObjectFastOperator::FastGetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(), sCheckMode);
380     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
381     return JSHandle<JSTaggedValue>(thread, result);
382 }
383 
FastSetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t index,const JSHandle<JSTaggedValue> & value)384 bool JSSharedArray::FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, uint32_t index,
385                                            const JSHandle<JSTaggedValue> &value)
386 {
387     return ObjectFastOperator::FastSetPropertyByIndex(thread, obj.GetTaggedValue(), index, value.GetTaggedValue());
388 }
389 
FastSetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)390 bool JSSharedArray::FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
391                                            const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
392 {
393     return ObjectFastOperator::FastSetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(),
394                                                       value.GetTaggedValue(), SCheckMode::SKIP);
395 }
396 
GetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,SCheckMode sCheckMode)397 OperationResult JSSharedArray::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
398                                            const JSHandle<JSTaggedValue> &key, SCheckMode sCheckMode)
399 {
400     // Add Concurrent check for shared array
401     [[maybe_unused]] ConcurrentApiScope<JSSharedArray> scope(thread, obj,
402                                                              sCheckMode);
403     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
404                                       OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
405 
406     ObjectOperator op(thread, obj, key);
407     // Out of bounds check for shared array
408     if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) {
409         return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(false));
410     }
411     return OperationResult(thread, JSObject::GetProperty(thread, &op), PropertyMetaData(op.IsFound()));
412 }
413 
SetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,bool mayThrow,SCheckMode sCheckMode)414 bool JSSharedArray::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
415                                 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value, bool mayThrow,
416                                 SCheckMode sCheckMode)
417 {
418     // Concurrent check for shared array
419     [[maybe_unused]] ConcurrentApiScope<JSSharedArray, ModType::WRITE> scope(
420         thread, obj, sCheckMode);
421     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
422     // 2 ~ 4 findProperty in Receiver, Obj and its parents
423     ObjectOperator op(thread, obj, key);
424     // Out of bounds check for shared array
425     if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) {
426         auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::RANGE_ERROR,
427                                                                "The value of index is out of range.");
428         THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
429     }
430     return JSObject::SetProperty(&op, value, mayThrow);
431 }
432 
SetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t index,const JSHandle<JSTaggedValue> & value,bool mayThrow,SCheckMode sCheckMode)433 bool JSSharedArray::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
434                                 uint32_t index, const JSHandle<JSTaggedValue> &value, bool mayThrow,
435                                 SCheckMode sCheckMode)
436 {
437     // Concurrent check for shared array
438     [[maybe_unused]] ConcurrentApiScope<JSSharedArray, ModType::WRITE> scope(
439         thread, obj, sCheckMode);
440     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
441     // 2 ~ 4 findProperty in Receiver, Obj and its parents
442     ObjectOperator op(thread, obj, index);
443     // Out of bounds check for shared array
444     if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) {
445         auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::RANGE_ERROR,
446                                                                "The value of index is out of range.");
447         THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
448     }
449     return JSObject::SetProperty(&op, value, mayThrow);
450 }
451 
452 // ecma2024 23.1.3.20 Array.prototype.sort(comparefn)
Sort(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & fn)453 JSTaggedValue JSSharedArray::Sort(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
454                                   const JSHandle<JSTaggedValue> &fn)
455 {
456     ASSERT(fn->IsUndefined() || fn->IsCallable());
457     // 3. Let len be ?LengthOfArrayLike(obj).
458     int64_t len = ArrayHelper::GetArrayLength(thread, obj);
459     // ReturnIfAbrupt(len).
460     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
461     // If len is 0 or 1, no need to sort
462     if (len == 0 || len == 1) {
463         return obj.GetTaggedValue();
464     }
465 
466     // 4. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs
467     // the following steps when called:
468     //    a. Return ? CompareArrayElements(x, y, comparefn).
469     // 5. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, SKIP-HOLES).
470     JSHandle<TaggedArray> sortedList =
471         ArrayHelper::SortIndexedProperties(thread, obj, len, fn, base::HolesType::SKIP_HOLES);
472     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
473     // 6. Let itemCount be the number of elements in sortedList.
474     uint32_t itemCount = sortedList->GetLength();
475 
476     // 7. Let j be 0.
477     uint32_t j = 0;
478     // 8. Repeat, while j < itemCount,
479     //     a. Perform ! Set(obj, ! ToString((j)), sortedList[j], true).
480     //     b. Set j to j + 1.
481     JSMutableHandle<JSTaggedValue> item(thread, JSTaggedValue::Undefined());
482     while (j < itemCount) {
483         item.Update(sortedList->Get(j));
484         JSSharedArray::FastSetPropertyByValue(thread, obj, j, item);
485         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
486         ++j;
487     }
488     // 9. NOTE: The call to SortIndexedProperties in step 5 uses SKIP-HOLES.The remaining indices are deleted to
489     // preserve the number of holes that were detected and excluded from the sort.
490     // 10. Repeat, while j < len,
491     //       a. Perform ? DeletePropertyOrThrow(obj, ! ToString((j))).
492     //       b. Set j to j + 1.
493     while (j < len) {
494         item.Update(JSTaggedValue(j));
495         JSTaggedValue::DeletePropertyOrThrow(thread, obj, item);
496         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
497         ++j;
498     }
499 
500     return obj.GetTaggedValue();
501 }
502 
IncludeInSortedValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & value)503 bool JSSharedArray::IncludeInSortedValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
504                                          const JSHandle<JSTaggedValue> &value)
505 {
506     ASSERT(obj->IsJSSharedArray());
507     JSHandle<JSSharedArray> arrayObj = JSHandle<JSSharedArray>::Cast(obj);
508     int32_t length = static_cast<int32_t>(arrayObj->GetArrayLength());
509     if (length == 0) {
510         return false;
511     }
512     int32_t left = 0;
513     int32_t right = length - 1;
514     while (left <= right) {
515         int32_t middle = (left + right) / 2;
516         JSHandle<JSTaggedValue> vv = JSSharedArray::FastGetPropertyByValue(thread, obj, middle);
517         ComparisonResult res = JSTaggedValue::Compare(thread, vv, value);
518         if (res == ComparisonResult::EQUAL) {
519             return true;
520         } else if (res == ComparisonResult::LESS) {
521             left = middle + 1;
522         } else {
523             right = middle - 1;
524         }
525     }
526     return false;
527 }
528 
CheckAndCopyArray(const JSThread * thread,JSHandle<JSSharedArray> obj)529 void JSSharedArray::CheckAndCopyArray(const JSThread *thread, JSHandle<JSSharedArray> obj)
530 {
531     JSHandle<TaggedArray> arr(thread, obj->GetElements());
532     // Check whether array is shared in the nonmovable space before set properties and elements.
533     // If true, then really copy array in the semi space.
534     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
535     if (arr.GetTaggedValue().IsCOWArray()) {
536         auto newArray = factory->CopyArray(arr, arr->GetLength(), arr->GetLength(),
537             JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE);
538         obj->SetElements(thread, newArray.GetTaggedValue());
539     }
540     JSHandle<TaggedArray> prop(thread, obj->GetProperties());
541     if (prop.GetTaggedValue().IsCOWArray()) {
542         auto newProps = factory->CopyArray(prop, prop->GetLength(), prop->GetLength(),
543             JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE);
544         obj->SetProperties(thread, newProps.GetTaggedValue());
545     }
546 }
547 
DeleteInElementMode(const JSThread * thread,JSHandle<JSSharedArray> & obj)548 void JSSharedArray::DeleteInElementMode(const JSThread *thread, JSHandle<JSSharedArray> &obj)
549 {
550     JSHandle<TaggedArray> elements(thread, obj->GetElements());
551     ASSERT(!obj->GetJSHClass()->IsDictionaryElement());
552     uint32_t length = elements->GetLength();
553     // fixme(hzzhouzebin) Optimize Delete later.
554     uint32_t size = 0;
555     for (uint32_t i = 0; i < length; i++) {
556         JSTaggedValue value = ElementAccessor::Get(thread, JSHandle<JSObject>(obj), i);
557         if (value.IsHole()) {
558             continue;
559         }
560         ++size;
561     }
562     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
563     JSHandle<TaggedArray> newElements(
564         factory->NewTaggedArray(length, JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE));
565     uint32_t newCurr = 0;
566     for (uint32_t i = 0; i < length; i++) {
567         JSTaggedValue value = ElementAccessor::Get(thread, JSHandle<JSObject>(obj), i);
568         if (value.IsHole()) {
569             continue;
570         }
571         newElements->Set(thread, newCurr, value);
572         ++newCurr;
573     }
574     obj->SetElements(thread, newElements);
575 }
576 }  // namespace panda::ecmascript
577