• 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_for_in_iterator.h"
17 
18 #include "ecmascript/base/builtins_base.h"
19 #include "ecmascript/global_dictionary-inl.h"
20 #include "ecmascript/global_env.h"
21 #include "ecmascript/js_object-inl.h"
22 #include "ecmascript/js_primitive_ref.h"
23 #include "ecmascript/object_factory.h"
24 #include "ecmascript/tagged_array-inl.h"
25 #include "ecmascript/tagged_dictionary.h"
26 #include "ecmascript/tagged_queue-inl.h"
27 #include "ecmascript/tagged_queue.h"
28 
29 namespace panda::ecmascript {
30 using BuiltinsBase = base::BuiltinsBase;
31 
CheckObjProto(const JSThread * thread,const JSHandle<JSForInIterator> & it)32 bool JSForInIterator::CheckObjProto(const JSThread *thread, const JSHandle<JSForInIterator> &it)
33 {
34     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
35     JSHandle<JSTaggedValue> object(thread, it->GetObject());
36     if (!object->IsJSObject()) {
37         return false;
38     }
39     auto *hclass = object->GetTaggedObject()->GetClass();
40     JSType jsType = hclass->GetObjectType();
41     if (jsType != JSType::JS_OBJECT) {
42         return false;
43     }
44     JSTaggedValue proto = hclass->GetPrototype();
45     if (!proto.IsJSObject()) {
46         return false;
47     }
48     return hclass->GetPrototype().GetTaggedObject()->GetClass() ==
49            env->GetObjectFunctionPrototypeClass().GetTaggedValue().GetTaggedObject()->GetClass();
50 }
51 
FastGetAllEnumKeys(const JSThread * thread,const JSHandle<JSForInIterator> & it,const JSHandle<JSTaggedValue> & object)52 void JSForInIterator::FastGetAllEnumKeys(const JSThread *thread, const JSHandle<JSForInIterator> &it,
53                                          const JSHandle<JSTaggedValue> &object)
54 {
55     JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
56     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
57     JSHandle<JSObject> obj(object);
58     uint32_t numOfElements = obj->GetNumberOfElements();
59     uint32_t numOfKeys = obj->GetNumberOfKeys();
60     JSHandle<TaggedQueue> remaining = factory->NewTaggedQueue(numOfElements + numOfKeys + 1);
61     if (numOfElements > 0) {
62         uint32_t elementIndex = 0;
63         if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) {
64             elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength();
65             for (uint32_t i = 0; i < elementIndex; i++) {
66                 value.Update(factory->NewFromCanBeCompressString(ToCString(i)).GetTaggedValue());
67                 TaggedQueue::PushFixedQueue(thread, remaining, value);
68             }
69         } else {
70             JSHandle<TaggedArray> elements(thread, obj->GetElements());
71             if (!elements->IsDictionaryMode()) {
72                 uint32_t elementsLen = elements->GetLength();
73                 for (uint32_t i = 0; i < elementsLen; ++i) {
74                     if (!elements->Get(i).IsHole()) {
75                         value.Update(factory->NewFromCanBeCompressString(ToCString(i)).GetTaggedValue());
76                         TaggedQueue::PushFixedQueue(thread, remaining, value);
77                     }
78                 }
79             } else {
80                 JSHandle<NumberDictionary> numberDic(elements);
81                 int size = numberDic->Size();
82                 CVector<JSTaggedValue> sortArr;
83                 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
84                     JSTaggedValue key = numberDic->GetKey(hashIndex);
85                     if (!key.IsUndefined() && !key.IsHole()) {
86                         PropertyAttributes attr = numberDic->GetAttributes(hashIndex);
87                         if (attr.IsEnumerable()) {
88                             sortArr.push_back(JSTaggedValue(static_cast<uint32_t>(key.GetInt())));
89                         }
90                     }
91                 }
92                 std::sort(sortArr.begin(), sortArr.end(), NumberDictionary::CompKey);
93                 for (const auto &entry : sortArr) {
94                     value.Update(factory->NewFromCanBeCompressString(ToCString(entry.GetInt())).GetTaggedValue());
95                     TaggedQueue::PushFixedQueue(thread, remaining, value);
96                 }
97             }
98         }
99     }
100     if (numOfKeys > 0) {
101         if (obj->IsJSGlobalObject()) {
102             GlobalDictionary *dict = GlobalDictionary::Cast(obj->GetProperties().GetTaggedObject());
103             int size = dict->Size();
104             CVector<std::pair<JSTaggedValue, uint32_t>> sortArr;
105             for (int hashIndex = 0; hashIndex < size; hashIndex++) {
106                 JSTaggedValue key = dict->GetKey(hashIndex);
107                 if (!key.IsUndefined() && !key.IsHole()) {
108                     PropertyAttributes attr = dict->GetAttributes(hashIndex);
109                     if (attr.IsEnumerable()) {
110                         std::pair<JSTaggedValue, uint32_t> pair(key, attr.GetOffset());
111                         sortArr.emplace_back(pair);
112                     }
113                 }
114             }
115             std::sort(sortArr.begin(), sortArr.end(), GlobalDictionary::CompKey);
116             for (const auto &entry : sortArr) {
117                 JSTaggedValue nameKey = entry.first;
118                 if (nameKey.IsString()) {
119                     value.Update(nameKey);
120                     TaggedQueue::PushFixedQueue(thread, remaining, value);
121                 }
122             }
123         } else {
124             JSHandle<TaggedArray> propertiesArr(thread, obj->GetProperties());
125             if (!propertiesArr->IsDictionaryMode()) {
126                 JSHClass *jsHclass = obj->GetJSHClass();
127                 JSTaggedValue enumCache = jsHclass->GetEnumCache();
128                 if (!enumCache.IsNull()) {
129                     JSHandle<TaggedArray> cache(thread, enumCache);
130                     uint32_t length = cache->GetLength();
131                     if (length != numOfKeys) {
132                         JSHandle<LayoutInfo> layoutInfoHandle(thread, jsHclass->GetLayout());
133                         for (uint32_t i = 0; i < numOfKeys; i++) {
134                             JSTaggedValue key = layoutInfoHandle->GetKey(i);
135                             if (key.IsString()) {
136                                 value.Update(key);
137                                 if (layoutInfoHandle->GetAttr(i).IsEnumerable()) {
138                                     TaggedQueue::PushFixedQueue(thread, remaining, value);
139                                 }
140                             }
141                         }
142                     } else {
143                         for (uint32_t i = 0; i < length; i++) {
144                             JSTaggedValue key = cache->Get(i);
145                             if (key.IsString()) {
146                                 value.Update(key);
147                                 TaggedQueue::PushFixedQueue(thread, remaining, value);
148                             }
149                         }
150                     }
151                 } else {
152                     JSHandle<LayoutInfo> layoutInfoHandle(thread, jsHclass->GetLayout());
153                     for (uint32_t i = 0; i < numOfKeys; i++) {
154                         JSTaggedValue key = layoutInfoHandle->GetKey(i);
155                         if (key.IsString()) {
156                             value.Update(key);
157                             if (layoutInfoHandle->GetAttr(i).IsEnumerable()) {
158                                 TaggedQueue::PushFixedQueue(thread, remaining, value);
159                             }
160                         }
161                     }
162                 }
163             } else {
164                 JSHandle<NameDictionary> nameDic(propertiesArr);
165                 int size = nameDic->Size();
166                 CVector<std::pair<JSTaggedValue, PropertyAttributes>> sortArr;
167                 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
168                     JSTaggedValue key = nameDic->GetKey(hashIndex);
169                     if (key.IsString()) {
170                         PropertyAttributes attr = nameDic->GetAttributes(hashIndex);
171                         if (attr.IsEnumerable()) {
172                             std::pair<JSTaggedValue, PropertyAttributes> pair(key, attr);
173                             sortArr.emplace_back(pair);
174                         }
175                     }
176                 }
177                 std::sort(sortArr.begin(), sortArr.end(), NameDictionary::CompKey);
178                 for (const auto &entry : sortArr) {
179                     value.Update(entry.first);
180                     TaggedQueue::PushFixedQueue(thread, remaining, value);
181                 }
182             }
183         }
184     }
185     it->SetRemainingKeys(thread, remaining);
186     it->SetWasVisited(true);
187 }
188 
SlowGetAllEnumKeys(JSThread * thread,const JSHandle<JSForInIterator> & it,const JSHandle<JSTaggedValue> & object)189 void JSForInIterator::SlowGetAllEnumKeys(JSThread *thread, const JSHandle<JSForInIterator> &it,
190                                          const JSHandle<JSTaggedValue> &object)
191 {
192     JSMutableHandle<TaggedQueue> visited(thread, it->GetVisitedKeys());
193     JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
194     JSMutableHandle<TaggedQueue> remaining(thread, it->GetRemainingKeys());
195     JSHandle<TaggedArray> arr = JSTaggedValue::GetOwnPropertyKeys(thread, object);
196     uint32_t len = arr->GetLength();
197     for (uint32_t i = 0; i < len; i++) {
198         value.Update(arr->Get(i));
199         if (value->IsString()) {
200             TaggedQueue *newQueue = TaggedQueue::Push(thread, remaining, value);
201             remaining.Update(JSTaggedValue(newQueue));
202         }
203     }
204     it->SetRemainingKeys(thread, remaining);
205     it->SetVisitedKeys(thread, visited);
206     it->SetWasVisited(true);
207 }
208 
NextInternal(JSThread * thread,const JSHandle<JSForInIterator> & it)209 std::pair<JSTaggedValue, bool> JSForInIterator::NextInternal(JSThread *thread, const JSHandle<JSForInIterator> &it)
210 {
211     bool notModiObjProto = true;
212     notModiObjProto = CheckObjProto(thread, it);
213     while (true) {
214         JSHandle<JSTaggedValue> object(thread, it->GetObject());
215         if (object->IsNull() || object->IsUndefined()) {
216             return std::make_pair(JSTaggedValue::Undefined(), true);
217         }
218         if (!it->GetWasVisited()) {
219             if (object->IsJSObject() && notModiObjProto) {
220                 FastGetAllEnumKeys(thread, it, object);
221             } else {
222                 SlowGetAllEnumKeys(thread, it, object);
223             }
224         }
225 
226         JSHandle<TaggedQueue> remaining(thread, it->GetRemainingKeys());
227         JSMutableHandle<TaggedQueue> visited(thread, it->GetVisitedKeys());
228         while (!remaining->Empty()) {
229             JSTaggedValue r = remaining->Pop(thread);
230             if (object->IsJSObject() && notModiObjProto) {
231                 return std::make_pair(r, false);
232             }
233             JSHandle<JSTaggedValue> key(thread, r);
234             bool has_same = false;
235             uint32_t len = visited->Size();
236             for (uint32_t i = 0; i < len; i++) {
237                 if (JSTaggedValue::SameValue(r, visited->Get(i))) {
238                     has_same = true;
239                     break;
240                 }
241             }
242             if (has_same) {
243                 continue;
244             }
245             PropertyDescriptor desc(thread);
246             bool has = JSTaggedValue::GetOwnProperty(thread, object, key, desc);
247             if (has) {
248                 auto newQueue = JSTaggedValue(TaggedQueue::Push(thread, visited, key));
249                 visited.Update(newQueue);
250                 it->SetVisitedKeys(thread, newQueue);
251                 if (desc.IsEnumerable()) {
252                     return std::make_pair(key.GetTaggedValue(), false);
253                 }
254             }
255         }
256         if (notModiObjProto) {
257             return std::make_pair(JSTaggedValue::Undefined(), true);
258         }
259         JSTaggedValue proto = JSHandle<JSObject>::Cast(object)->GetPrototype(thread);
260         it->SetObject(thread, proto);
261         it->SetWasVisited(false);
262     }
263 }
264 
265 // 13.7.5.16.2.1 %ForInIteratorPrototype%.next ( )
Next(EcmaRuntimeCallInfo * msg)266 JSTaggedValue JSForInIterator::Next(EcmaRuntimeCallInfo *msg)
267 {
268     ASSERT(msg);
269     JSThread *thread = msg->GetThread();
270     [[maybe_unused]] EcmaHandleScope handleScope(thread);
271     // 1. Let O be the this value.
272     JSHandle<JSForInIterator> it(BuiltinsBase::GetThis(msg));
273     ASSERT(it->IsForinIterator());
274     std::pair<JSTaggedValue, bool> res = NextInternal(thread, it);
275     return res.first;
276 }
277 }  // namespace panda::ecmascript
278