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/enum_cache.h"
20 #include "ecmascript/ic/proto_change_details.h"
21 #include "ecmascript/js_object-inl.h"
22 #include "ecmascript/js_tagged_value.h"
23
24 namespace panda::ecmascript {
25 using BuiltinsBase = base::BuiltinsBase;
IsEnumCacheValid(const JSThread * thread,JSTaggedValue receiver,JSTaggedValue cachedHClass,EnumCacheKind kind)26 bool JSForInIterator::IsEnumCacheValid(const JSThread *thread, JSTaggedValue receiver, JSTaggedValue cachedHClass,
27 EnumCacheKind kind)
28 {
29 DISALLOW_GARBAGE_COLLECTION;
30 JSHClass *hclass = receiver.GetTaggedObject()->GetClass();
31 if (JSTaggedValue(hclass) != cachedHClass) {
32 return false;
33 }
34 if (kind == EnumCacheKind::SIMPLE) {
35 return true;
36 }
37 if (kind == EnumCacheKind::NONE) {
38 return false;
39 }
40 ASSERT(kind == EnumCacheKind::PROTOCHAIN);
41 JSTaggedValue proto = hclass->GetPrototype(thread);
42 if (!proto.IsHeapObject()) {
43 return true;
44 }
45 JSTaggedValue enumCache = proto.GetTaggedObject()->GetClass()->GetEnumCache(thread);
46 if (enumCache.IsEnumCache()) {
47 JSTaggedValue enumCacheAll = EnumCache::Cast(enumCache.GetTaggedObject())->GetEnumCacheAll(thread);
48 if (enumCacheAll != JSTaggedValue::Null()) {
49 return true;
50 }
51 }
52 return false;
53 }
54
NeedCheckProperty(const JSThread * thread,JSTaggedValue receiver)55 bool JSForInIterator::NeedCheckProperty(const JSThread *thread, JSTaggedValue receiver)
56 {
57 DISALLOW_GARBAGE_COLLECTION;
58 JSTaggedValue current = receiver;
59 while (current.IsHeapObject()) {
60 if (!current.IsJSObject() || current.GetTaggedObject()->GetClass()->HasDeleteProperty()) {
61 return true;
62 }
63 current = JSObject::GetPrototype(thread, current);
64 }
65 return false;
66 }
67
HasProperty(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> key)68 bool JSForInIterator::HasProperty(JSThread *thread, JSHandle<JSTaggedValue> receiver, JSHandle<JSTaggedValue> key)
69 {
70 JSMutableHandle<JSTaggedValue> current(thread, receiver.GetTaggedValue());
71 while (current->IsHeapObject()) {
72 PropertyDescriptor desc(thread);
73 bool has = JSTaggedValue::GetOwnProperty(thread, current, key, desc);
74 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
75 if (has && desc.IsEnumerable()) {
76 return true;
77 }
78 current.Update(JSTaggedValue::GetPrototype(thread, current));
79 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
80 }
81 return false;
82 }
83
NextInternal(JSThread * thread,const JSHandle<JSForInIterator> & it)84 JSTaggedValue JSForInIterator::NextInternal(JSThread *thread, const JSHandle<JSForInIterator> &it)
85 {
86 uint32_t length = it->GetLength();
87 uint32_t index = it->GetIndex();
88 if (index >= length) {
89 return JSTaggedValue::Undefined();
90 }
91 JSTaggedValue taggedKeys = it->GetKeys(thread);
92 JSTaggedValue receiver = it->GetObject(thread);
93 EnumCacheKind kind = static_cast<EnumCacheKind>(it->GetCacheKind());
94 TaggedArray *keys = TaggedArray::Cast(taggedKeys.GetTaggedObject());
95 if (IsEnumCacheValid(thread, receiver, it->GetCachedHClass(thread), kind)) {
96 JSTaggedValue key = keys->Get(thread, index);
97 index++;
98 it->SetIndex(index);
99 return key;
100 }
101
102 if (!NeedCheckProperty(thread, receiver)) {
103 JSTaggedValue key = keys->Get(thread, index);
104 index++;
105 it->SetIndex(index);
106 return key;
107 }
108 // slow path
109 return NextInternalSlowpath(thread, it);
110 }
111
NextInternalSlowpath(JSThread * thread,const JSHandle<JSForInIterator> & it)112 JSTaggedValue JSForInIterator::NextInternalSlowpath(JSThread *thread, const JSHandle<JSForInIterator> &it)
113 {
114 uint32_t length = it->GetLength();
115 uint32_t index = it->GetIndex();
116 if (index >= length) {
117 return JSTaggedValue::Undefined();
118 }
119 JSHandle<TaggedArray> keysHandle(thread, it->GetKeys(thread));
120 JSHandle<JSTaggedValue> receiverHandle(thread, it->GetObject(thread));
121 JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
122 bool has = false;
123 while (index < length) {
124 keyHandle.Update(keysHandle->Get(thread, index));
125 if (keyHandle->IsUndefined()) {
126 has = false;
127 break;
128 }
129 has = HasProperty(thread, receiverHandle, keyHandle);
130 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
131 if (has) {
132 break;
133 }
134 index++;
135 }
136 if (!has) {
137 it->SetIndex(index);
138 return JSTaggedValue::Undefined();
139 }
140
141 JSTaggedValue key = keysHandle->Get(thread, index);
142 index++;
143 it->SetIndex(index);
144 return key;
145 }
146
147 // 13.7.5.16.2.1 %ForInIteratorPrototype%.next ( )
Next(EcmaRuntimeCallInfo * msg)148 JSTaggedValue JSForInIterator::Next(EcmaRuntimeCallInfo *msg)
149 {
150 ASSERT(msg);
151 JSThread *thread = msg->GetThread();
152 [[maybe_unused]] EcmaHandleScope handleScope(thread);
153 // 1. Let O be the this value.
154 JSHandle<JSForInIterator> it(BuiltinsBase::GetThis(msg));
155 ASSERT(JSHandle<JSTaggedValue>(it)->IsForinIterator());
156 JSTaggedValue res = NextInternal(thread, it);
157 return res;
158 }
159 } // namespace panda::ecmascript
160