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