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