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