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