• 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/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