• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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/js_array.h"
17 #include "ecmascript/accessor_data.h"
18 #include "ecmascript/ecma_vm.h"
19 #include "ecmascript/global_env.h"
20 #include "ecmascript/internal_call_params.h"
21 #include "ecmascript/js_invoker.h"
22 #include "ecmascript/js_tagged_value-inl.h"
23 #include "ecmascript/object_factory.h"
24 #include "interpreter/fast_runtime_stub-inl.h"
25 
26 namespace panda::ecmascript {
LengthGetter(JSThread * thread,const JSHandle<JSObject> & self)27 JSTaggedValue JSArray::LengthGetter([[maybe_unused]] JSThread *thread, const JSHandle<JSObject> &self)
28 {
29     return JSArray::Cast(*self)->GetLength();
30 }
31 
LengthSetter(JSThread * thread,const JSHandle<JSObject> & self,const JSHandle<JSTaggedValue> & value,bool mayThrow)32 bool JSArray::LengthSetter(JSThread *thread, const JSHandle<JSObject> &self, const JSHandle<JSTaggedValue> &value,
33                            bool mayThrow)
34 {
35     uint32_t newLen = 0;
36     if (!JSTaggedValue::ToArrayLength(thread, value, &newLen) && mayThrow) {
37         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
38     }
39 
40     if (!IsArrayLengthWritable(thread, self)) {
41         if (mayThrow) {
42             THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot assign to read only property", false);
43         }
44         return false;
45     }
46 
47     uint32_t oldLen = JSArray::Cast(*self)->GetArrayLength();
48     JSArray::SetCapacity(thread, self, oldLen, newLen);
49     return true;
50 }
51 
ArrayCreate(JSThread * thread,JSTaggedNumber length)52 JSHandle<JSTaggedValue> JSArray::ArrayCreate(JSThread *thread, JSTaggedNumber length)
53 {
54     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
55     JSHandle<JSTaggedValue> arrayFunction = env->GetArrayFunction();
56     return JSArray::ArrayCreate(thread, length, arrayFunction);
57 }
58 
59 // 9.4.2.2 ArrayCreate(length, proto)
ArrayCreate(JSThread * thread,JSTaggedNumber length,const JSHandle<JSTaggedValue> & newTarget)60 JSHandle<JSTaggedValue> JSArray::ArrayCreate(JSThread *thread, JSTaggedNumber length,
61                                              const JSHandle<JSTaggedValue> &newTarget)
62 {
63     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
64     // Assert: length is an integer Number ≥ 0.
65     ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer");
66     // 2. If length is −0, let length be +0.
67     double arrayLength = JSTaggedValue::ToInteger(thread, JSHandle<JSTaggedValue>(thread, length)).GetDouble();
68     if (arrayLength > MAX_ARRAY_INDEX) {
69         JSHandle<JSTaggedValue> exception(thread, JSTaggedValue::Exception());
70         THROW_RANGE_ERROR_AND_RETURN(thread, "array length must less than 2^32 - 1", exception);
71     }
72     uint32_t normalArrayLength = length.ToUint32();
73 
74     // 8. Set the [[Prototype]] internal slot of A to proto.
75     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
76     JSHandle<JSTaggedValue> arrayFunc = env->GetArrayFunction();
77     JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(arrayFunc), newTarget);
78     // 9. Set the [[Extensible]] internal slot of A to true.
79     obj->GetJSHClass()->SetExtensible(true);
80 
81     // 10. Perform OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]:
82     // true, [[Enumerable]]: false, [[Configurable]]: false}).
83     JSArray::Cast(*obj)->SetArrayLength(thread, normalArrayLength);
84 
85     return JSHandle<JSTaggedValue>(obj);
86 }
87 
88 // 9.4.2.3 ArraySpeciesCreate(originalArray, length)
ArraySpeciesCreate(JSThread * thread,const JSHandle<JSObject> & originalArray,JSTaggedNumber length)89 JSTaggedValue JSArray::ArraySpeciesCreate(JSThread *thread, const JSHandle<JSObject> &originalArray,
90                                           JSTaggedNumber length)
91 {
92     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
93     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
94     // Assert: length is an integer Number ≥ 0.
95     ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer");
96     // If length is −0, let length be +0.
97     double arrayLength = JSTaggedValue::ToInteger(thread, JSHandle<JSTaggedValue>(thread, length)).GetDouble();
98     if (arrayLength == -0) {
99         arrayLength = +0;
100     }
101     // Let C be undefined.
102     // Let isArray be IsArray(originalArray).
103     JSHandle<JSTaggedValue> originalValue(originalArray);
104     bool isArray = originalValue->IsArray(thread);
105     // ReturnIfAbrupt(isArray).
106     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
107     // If isArray is true, then
108     JSHandle<JSTaggedValue> constructor(thread, JSTaggedValue::Undefined());
109     if (isArray) {
110         // Let C be Get(originalArray, "constructor").
111         auto *hclass = originalArray->GetJSHClass();
112         if (hclass->IsJSArray() && !hclass->HasConstructor()) {
113             return JSArray::ArrayCreate(thread, length).GetTaggedValue();
114         }
115         JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString();
116         constructor = JSTaggedValue::GetProperty(thread, originalValue, constructorKey).GetValue();
117         // ReturnIfAbrupt(C).
118         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
119         // If IsConstructor(C) is true, then
120         if (constructor->IsConstructor()) {
121             // Let thisRealm be the running execution context’s Realm.
122             // Let realmC be GetFunctionRealm(C).
123             JSHandle<GlobalEnv> realmC = JSObject::GetFunctionRealm(thread, constructor);
124             // ReturnIfAbrupt(realmC).
125             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
126             // If thisRealm and realmC are not the same Realm Record, then
127             if (*realmC != *env) {
128                 JSTaggedValue realmArrayConstructor = realmC->GetArrayFunction().GetTaggedValue();
129                 // If SameValue(C, realmC.[[intrinsics]].[[%Array%]]) is true, let C be undefined.
130                 if (JSTaggedValue::SameValue(constructor.GetTaggedValue(), realmArrayConstructor)) {
131                     return JSArray::ArrayCreate(thread, length).GetTaggedValue();
132                 }
133             }
134         }
135 
136         // If Type(C) is Object, then
137         if (constructor->IsECMAObject()) {
138             // Let C be Get(C, @@species).
139             JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
140             constructor = JSTaggedValue::GetProperty(thread, constructor, speciesSymbol).GetValue();
141             // ReturnIfAbrupt(C).
142             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
143             // If C is null, let C be undefined.
144             if (constructor->IsNull()) {
145                 return JSArray::ArrayCreate(thread, length).GetTaggedValue();
146             }
147         }
148     }
149 
150     // If C is undefined, return ArrayCreate(length).
151     if (constructor->IsUndefined()) {
152         return JSArray::ArrayCreate(thread, length).GetTaggedValue();
153     }
154     // If IsConstructor(C) is false, throw a TypeError exception.
155     if (!constructor->IsConstructor()) {
156         THROW_TYPE_ERROR_AND_RETURN(thread, "Not a constructor", JSTaggedValue::Exception());
157     }
158     // Return Construct(C, «length»).
159     JSHandle<JSTaggedValue> newTarget(thread, JSTaggedValue::Undefined());
160     InternalCallParams *arguments = thread->GetInternalCallParams();
161     arguments->MakeArgv(JSTaggedValue(arrayLength));
162     JSTaggedValue result = JSFunction::Construct(thread, constructor, 1, arguments->GetArgv(), newTarget);
163 
164     // NOTEIf originalArray was created using the standard built-in Array constructor for
165     // a Realm that is not the Realm of the running execution context, then a new Array is
166     // created using the Realm of the running execution context. This maintains compatibility
167     // with Web browsers that have historically had that behaviour for the Array.prototype methods
168     // that now are defined using ArraySpeciesCreate.
169     return result;
170 }
171 
SetCapacity(JSThread * thread,const JSHandle<JSObject> & array,uint32_t oldLen,uint32_t newLen)172 void JSArray::SetCapacity(JSThread *thread, const JSHandle<JSObject> &array, uint32_t oldLen, uint32_t newLen)
173 {
174     TaggedArray *element = TaggedArray::Cast(array->GetElements().GetTaggedObject());
175 
176     if (element->IsDictionaryMode()) {
177         ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
178         int32_t numOfElements = array->GetNumberOfElements();
179         uint32_t newNumOfElements = newLen;
180         if (newLen < oldLen && numOfElements != 0) {
181             JSHandle<NumberDictionary> dictHandle(thread, element);
182             JSHandle<TaggedArray> newArr = factory->NewTaggedArray(numOfElements);
183             GetAllElementKeys(thread, array, 0, newArr);
184             for (uint32_t i = numOfElements - 1; i >= newLen; i--) {
185                 JSTaggedValue value = newArr->Get(i);
186                 uint32_t output = 0;
187                 JSTaggedValue::StringToElementIndex(value, &output);
188                 JSTaggedValue key(static_cast<int>(output));
189                 int entry = dictHandle->FindEntry(key);
190                 uint32_t attr = dictHandle->GetAttributes(entry).GetValue();
191                 PropertyAttributes propAttr(attr);
192                 if (propAttr.IsConfigurable()) {
193                     JSHandle<NumberDictionary> newDict = NumberDictionary::Remove(thread, dictHandle, entry);
194                     array->SetElements(thread, newDict);
195                     if (i == 0) {
196                         newNumOfElements = i;
197                         break;
198                     }
199                 } else {
200                     newNumOfElements = i + 1;
201                     break;
202                 }
203             }
204         }
205         JSArray::Cast(*array)->SetArrayLength(thread, newNumOfElements);
206         return;
207     }
208     uint32_t capacity = element->GetLength();
209     if (newLen <= capacity) {
210         // judge if need to cut down the array size, else fill the unused tail with holes
211         array->FillElementsWithHoles(thread, newLen, oldLen < capacity ? oldLen : capacity);
212     }
213     if (JSObject::ShouldTransToDict(oldLen, newLen)) {
214         JSObject::ElementsToDictionary(thread, array);
215     } else if (newLen > capacity) {
216         JSObject::GrowElementsCapacity(thread, array, newLen);
217     }
218     JSArray::Cast(*array)->SetArrayLength(thread, newLen);
219 }
220 
ArraySetLength(JSThread * thread,const JSHandle<JSObject> & array,const PropertyDescriptor & desc)221 bool JSArray::ArraySetLength(JSThread *thread, const JSHandle<JSObject> &array, const PropertyDescriptor &desc)
222 {
223     JSHandle<JSTaggedValue> lengthKeyHandle(thread->GlobalConstants()->GetHandledLengthString());
224 
225     // 1. If the [[Value]] field of Desc is absent, then
226     if (!desc.HasValue()) {
227         // 1a. Return OrdinaryDefineOwnProperty(A, "length", Desc).
228         return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, desc);
229     }
230     // 2. Let newLenDesc be a copy of Desc.
231     // (Actual copying is not necessary.)
232     PropertyDescriptor newLenDesc = desc;
233     // 3. - 7. Convert Desc.[[Value]] to newLen.
234     uint32_t newLen = 0;
235     if (!JSTaggedValue::ToArrayLength(thread, desc.GetValue(), &newLen)) {
236         THROW_RANGE_ERROR_AND_RETURN(thread, "array length must less than 2^32 - 1", false);
237     }
238     // 8. Set newLenDesc.[[Value]] to newLen.
239     // (Done below, if needed.)
240     // 9. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
241     PropertyDescriptor oldLenDesc(thread);
242     [[maybe_unused]] bool success = GetOwnProperty(thread, array, lengthKeyHandle, oldLenDesc);
243     // 10. (Assert)
244     ASSERT(success);
245 
246     // 11. Let oldLen be oldLenDesc.[[Value]].
247     uint32_t oldLen = 0;
248     JSTaggedValue::ToArrayLength(thread, oldLenDesc.GetValue(), &oldLen);
249     // 12. If newLen >= oldLen, then
250     if (newLen >= oldLen) {
251         // 8. Set newLenDesc.[[Value]] to newLen.
252         // 12a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc).
253         newLenDesc.SetValue(JSHandle<JSTaggedValue>(thread, JSTaggedValue(newLen)));
254         return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, newLenDesc);
255     }
256     // 13. If oldLenDesc.[[Writable]] is false, return false.
257     if (!oldLenDesc.IsWritable() ||
258         // Also handle the {configurable: true} case since we later use
259         // JSArray::SetLength instead of OrdinaryDefineOwnProperty to change
260         // the length, and it doesn't have access to the descriptor anymore.
261         newLenDesc.IsConfigurable()) {
262         return false;
263     }
264     // 14. If newLenDesc.[[Writable]] is absent or has the value true,
265     // let newWritable be true.
266     bool newWritable = false;
267     if (!newLenDesc.HasWritable() || newLenDesc.IsWritable()) {
268         newWritable = true;
269     }
270     // 15. Else,
271     // 15a. Need to defer setting the [[Writable]] attribute to false in case
272     //      any elements cannot be deleted.
273     // 15b. Let newWritable be false. (It's initialized as "false" anyway.)
274     // 15c. Set newLenDesc.[[Writable]] to true.
275     // (Not needed.)
276 
277     // Most of steps 16 through 19 is implemented by JSArray::SetCapacity.
278     JSArray::SetCapacity(thread, array, oldLen, newLen);
279     // Steps 19d-ii, 20.
280     if (!newWritable) {
281         PropertyDescriptor readonly(thread);
282         readonly.SetWritable(false);
283         success = JSObject::DefineOwnProperty(thread, array, lengthKeyHandle, readonly);
284         ASSERT_PRINT(success, "DefineOwnProperty of length must be success here!");
285     }
286 
287     // Steps 19d-v, 21. Return false if there were non-deletable elements.
288     uint32_t arrayLength = JSArray::Cast(*array)->GetArrayLength();
289     return arrayLength == newLen;
290 }
291 
PropertyKeyToArrayIndex(JSThread * thread,const JSHandle<JSTaggedValue> & key,uint32_t * output)292 bool JSArray::PropertyKeyToArrayIndex(JSThread *thread, const JSHandle<JSTaggedValue> &key, uint32_t *output)
293 {
294     return JSTaggedValue::ToArrayLength(thread, key, output) && *output <= JSArray::MAX_ARRAY_INDEX;
295 }
296 
297 // 9.4.2.1 [[DefineOwnProperty]] ( P, Desc)
DefineOwnProperty(JSThread * thread,const JSHandle<JSObject> & array,const JSHandle<JSTaggedValue> & key,const PropertyDescriptor & desc)298 bool JSArray::DefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &array, const JSHandle<JSTaggedValue> &key,
299                                 const PropertyDescriptor &desc)
300 {
301     // 1. Assert: IsPropertyKey(P) is true.
302     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key!");
303     // 2. If P is "length", then
304     if (IsLengthString(thread, key)) {
305         // a. Return ArraySetLength(A, Desc).
306         return ArraySetLength(thread, array, desc);
307     }
308     // 3. Else if P is an array index, then
309     // already do in step 4.
310     // 4. Return OrdinaryDefineOwnProperty(A, P, Desc).
311     bool success = JSObject::OrdinaryDefineOwnProperty(thread, array, key, desc);
312     if (success) {
313         JSTaggedValue constructorKey = thread->GlobalConstants()->GetConstructorString();
314         if (key.GetTaggedValue() == constructorKey) {
315             array->GetJSHClass()->SetHasConstructor(true);
316             return true;
317         }
318     }
319     return success;
320 }
321 
DefineOwnProperty(JSThread * thread,const JSHandle<JSObject> & array,uint32_t index,const PropertyDescriptor & desc)322 bool JSArray::DefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &array, uint32_t index,
323                                 const PropertyDescriptor &desc)
324 {
325     return JSObject::OrdinaryDefineOwnProperty(thread, array, index, desc);
326 }
327 
IsLengthString(JSThread * thread,const JSHandle<JSTaggedValue> & key)328 bool JSArray::IsLengthString(JSThread *thread, const JSHandle<JSTaggedValue> &key)
329 {
330     return key.GetTaggedValue() == thread->GlobalConstants()->GetLengthString();
331 }
332 
333 // ecma6 7.3 Operations on Objects
CreateArrayFromList(JSThread * thread,const JSHandle<TaggedArray> & elements)334 JSHandle<JSArray> JSArray::CreateArrayFromList(JSThread *thread, const JSHandle<TaggedArray> &elements)
335 {
336     // Assert: elements is a List whose elements are all ECMAScript language values.
337     // 2. Let array be ArrayCreate(0) (see 9.4.2.2).
338     uint32_t length = elements->GetLength();
339 
340     // 4. For each element e of elements
341     auto env = thread->GetEcmaVM()->GetGlobalEnv();
342     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
343     JSHandle<JSTaggedValue> arrayFunc = env->GetArrayFunction();
344     JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(arrayFunc), arrayFunc);
345     obj->GetJSHClass()->SetExtensible(true);
346     JSArray::Cast(*obj)->SetArrayLength(thread, length);
347 
348     obj->SetElements(thread, elements);
349 
350     return JSHandle<JSArray>(obj);
351 }
352 
FastGetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t index)353 JSHandle<JSTaggedValue> JSArray::FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
354                                                         uint32_t index)
355 {
356     auto result = FastRuntimeStub::FastGetPropertyByIndex(thread, obj.GetTaggedValue(), index);
357     return JSHandle<JSTaggedValue>(thread, result);
358 }
359 
FastGetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)360 JSHandle<JSTaggedValue> JSArray::FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
361                                                         const JSHandle<JSTaggedValue> &key)
362 {
363     auto result = FastRuntimeStub::FastGetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue());
364     return JSHandle<JSTaggedValue>(thread, result);
365 }
366 
FastSetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t index,const JSHandle<JSTaggedValue> & value)367 bool JSArray::FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, uint32_t index,
368                                      const JSHandle<JSTaggedValue> &value)
369 {
370     return FastRuntimeStub::FastSetPropertyByIndex(thread, obj.GetTaggedValue(), index, value.GetTaggedValue());
371 }
372 
FastSetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)373 bool JSArray::FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
374                                      const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
375 {
376     return FastRuntimeStub::FastSetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(),
377                                                    value.GetTaggedValue());
378 }
379 }  // namespace panda::ecmascript
380