• 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 #include "ecmascript/tagged_array-inl.h"
22 
23 namespace panda::ecmascript {
24 using BuiltinsBase = base::BuiltinsBase;
IsEnumCacheValid(JSTaggedValue receiver,JSTaggedValue cachedHclass,EnumCacheKind kind)25 bool JSForInIterator::IsEnumCacheValid(JSTaggedValue receiver, JSTaggedValue cachedHclass, EnumCacheKind kind)
26 {
27     DISALLOW_GARBAGE_COLLECTION;
28     JSHClass *hclass = receiver.GetTaggedObject()->GetClass();
29     if (JSTaggedValue(hclass) != cachedHclass) {
30         return false;
31     }
32     if (kind == EnumCacheKind::SIMPLE) {
33         return true;
34     }
35     ASSERT(kind == EnumCacheKind::PROTOCHAIN);
36     JSTaggedValue proto = hclass->GetPrototype();
37     if (!proto.IsECMAObject()) {
38         return false;
39     }
40     JSTaggedValue protoChangeMarker = proto.GetTaggedObject()->GetClass()->GetProtoChangeMarker();
41     if (protoChangeMarker.IsProtoChangeMarker()) {
42         if (!ProtoChangeMarker::Cast(protoChangeMarker.GetTaggedObject())->GetHasChanged()) {
43             return true;
44         }
45     }
46     return false;
47 }
48 
NeedCheckProperty(JSTaggedValue receiver)49 bool JSForInIterator::NeedCheckProperty(JSTaggedValue receiver)
50 {
51     DISALLOW_GARBAGE_COLLECTION;
52     JSTaggedValue current = receiver;
53     while (current.IsHeapObject()) {
54         if (!current.IsJSObject() || current.GetTaggedObject()->GetClass()->HasDeleteProperty()) {
55             return true;
56         }
57         current = JSObject::GetPrototype(current);
58     }
59     return false;
60 }
61 
HasProperty(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> key)62 bool JSForInIterator::HasProperty(JSThread *thread, JSHandle<JSTaggedValue> receiver, JSHandle<JSTaggedValue> key)
63 {
64     JSMutableHandle<JSTaggedValue> current(thread, receiver.GetTaggedValue());
65     while (current->IsHeapObject()) {
66         PropertyDescriptor desc(thread);
67         bool has = JSTaggedValue::GetOwnProperty(thread, current, key, desc);
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         if (has) {
124             break;
125         }
126         index++;
127     }
128     if (!has) {
129         it->SetIndex(index);
130         return JSTaggedValue::Undefined();
131     }
132 
133     JSTaggedValue key = keysHandle->Get(index);
134     index++;
135     it->SetIndex(index);
136     return key;
137 }
138 
139 // 13.7.5.16.2.1 %ForInIteratorPrototype%.next ( )
Next(EcmaRuntimeCallInfo * msg)140 JSTaggedValue JSForInIterator::Next(EcmaRuntimeCallInfo *msg)
141 {
142     ASSERT(msg);
143     JSThread *thread = msg->GetThread();
144     [[maybe_unused]] EcmaHandleScope handleScope(thread);
145     // 1. Let O be the this value.
146     JSHandle<JSForInIterator> it(BuiltinsBase::GetThis(msg));
147     ASSERT(JSHandle<JSTaggedValue>(it)->IsForinIterator());
148     JSTaggedValue res = NextInternal(thread, it);
149     return res;
150 }
151 }  // namespace panda::ecmascript
152