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