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