• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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/property_accessor.h"
17 
18 #include "ecmascript/js_object-inl.h"
19 #include "ecmascript/object_factory.h"
20 #include "ecmascript/tagged_array-inl.h"
21 
22 namespace panda::ecmascript {
PropertyAccessor(JSThread * thread,JSHandle<JSTaggedValue> object)23 PropertyAccessor::PropertyAccessor(JSThread *thread, JSHandle<JSTaggedValue> object)
24     : thread_(thread),
25       receiver_(thread, object.GetTaggedValue()),
26       fastKeysArray_(thread, JSTaggedValue::Undefined()),
27       cachedHclass_(thread, JSTaggedValue::Undefined()),
28       keyLength_(0),
29       shadowKeyLength_(0),
30       onlyHasSimpleProperties_(true),
31       canUseEnumCache_(true),
32       hasSlowProperties_(false),
33       slowKeysArray_(thread, JSTaggedValue::Undefined()),
34       acutalKeyLength_(0)
35 {
36     PreLoad();
37 }
38 
PreLoad()39 void PropertyAccessor::PreLoad()
40 {
41     if (receiver_->IsSlowKeysObject()) {
42         hasSlowProperties_ = true;
43         return;
44     }
45     JSHandle<JSObject> receiverObj(receiver_);
46     JSHClass *jshclass = receiverObj->GetJSHClass();
47     if (jshclass->IsDictionaryMode()) {
48         onlyHasSimpleProperties_ = false;
49         canUseEnumCache_ = false;
50     }
51     uint32_t numOfElements = receiverObj->GetNumberOfElements();
52     if (numOfElements > 0) {
53         AccumulateKeyLength(numOfElements);
54         onlyHasSimpleProperties_ = false;
55         canUseEnumCache_ = false;
56     }
57     std::pair<uint32_t, uint32_t> numOfKeys = receiverObj->GetNumberOfEnumKeys();
58     uint32_t numOfEnumKeys = numOfKeys.first;
59     if (numOfEnumKeys > 0) {
60         AccumulateKeyLength(numOfEnumKeys);
61     }
62     uint32_t numOfShadowKeys = numOfKeys.second;
63     if (numOfShadowKeys > 0) {
64         AccumulateShadowKeyLength(numOfShadowKeys);
65     }
66 
67     CollectPrototypeInfo();
68     if (hasSlowProperties_ || !onlyHasSimpleProperties_) {
69         return;
70     }
71     ASSERT(canUseEnumCache_);
72     // fast path
73     InitSimplePropertiesEnumCache();
74 }
75 
CollectPrototypeInfo()76 void PropertyAccessor::CollectPrototypeInfo()
77 {
78     DISALLOW_GARBAGE_COLLECTION;
79     JSTaggedValue current = JSTaggedValue::GetPrototype(thread_, receiver_);
80     RETURN_IF_ABRUPT_COMPLETION(thread_);
81     while (current.IsHeapObject()) {
82         if (current.IsSlowKeysObject()) {
83             hasSlowProperties_ = true;
84             break;
85         }
86         JSObject *currentObj = JSObject::Cast(current.GetTaggedObject());
87         uint32_t numOfCurrentElements = currentObj->GetNumberOfElements();
88         if (numOfCurrentElements > 0) {
89             AccumulateKeyLength(numOfCurrentElements);
90             onlyHasSimpleProperties_ = false;
91             canUseEnumCache_ = false;
92         }
93         std::pair<uint32_t, uint32_t> numOfKeys = currentObj->GetNumberOfEnumKeys();
94         uint32_t numOfEnumKeys = numOfKeys.first;
95         if (numOfEnumKeys > 0) {
96             AccumulateKeyLength(numOfEnumKeys);
97             onlyHasSimpleProperties_ = false;
98         }
99         uint32_t numOfShadowKeys = numOfKeys.second;
100         if (numOfShadowKeys > 0) {
101             AccumulateShadowKeyLength(numOfShadowKeys);
102         }
103         JSHClass *jshclass = currentObj->GetJSHClass();
104         if (jshclass->IsDictionaryMode()) {
105             onlyHasSimpleProperties_ = false;
106             canUseEnumCache_ = false;
107         }
108         if (onlyHasSimpleProperties_) {
109             // a fast path to check simple enum cache
110             jshclass->SetEnumCache(thread_, JSTaggedValue::Undefined());
111         }
112         current = JSObject::GetPrototype(current);
113     }
114 }
115 
InitSimplePropertiesEnumCache()116 void PropertyAccessor::InitSimplePropertiesEnumCache()
117 {
118     ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
119     JSHandle<JSObject> receiverObj(receiver_);
120     ASSERT(receiverObj->GetNumberOfElements() == 0);
121 
122     JSMutableHandle<TaggedArray> keyArray(thread_, JSTaggedValue::Undefined());
123     if (keyLength_ == 0) {
124         keyArray.Update(factory->EmptyArray());
125         SetActualKeyLength(0);
126     } else {
127         uint32_t arraySize = keyLength_ + EnumCache::ENUM_CACHE_HEADER_SIZE;
128         JSHandle<TaggedArray> newArray = thread_->GetEcmaVM()->GetFactory()->NewTaggedArray(arraySize);
129         uint32_t length = JSObject::GetAllEnumKeys(thread_, receiverObj, EnumCache::ENUM_CACHE_HEADER_SIZE, newArray);
130         SetActualKeyLength(length);
131         JSObject::SetEnumCacheKind(thread_, *newArray, EnumCacheKind::SIMPLE);
132         keyArray.Update(newArray);
133     }
134     JSObject::ClearHasDeleteProperty(receiver_);
135     JSHClass *jsHclass = receiverObj->GetJSHClass();
136     jsHclass->SetEnumCache(thread_, keyArray.GetTaggedValue());
137     fastKeysArray_.Update(keyArray.GetTaggedValue());
138     cachedHclass_.Update(JSTaggedValue(jsHclass));
139 }
140 
AccumulateKeyLength(uint32_t length)141 inline void PropertyAccessor::AccumulateKeyLength(uint32_t length)
142 {
143     keyLength_ += length;
144 }
145 
AccumulateShadowKeyLength(uint32_t length)146 inline void PropertyAccessor::AccumulateShadowKeyLength(uint32_t length)
147 {
148     shadowKeyLength_ += length;
149 }
150 
GetCachedHclass()151 JSHandle<JSTaggedValue> PropertyAccessor::GetCachedHclass()
152 {
153     return cachedHclass_;
154 }
155 
GetActualKeyLength() const156 uint32_t PropertyAccessor::GetActualKeyLength() const
157 {
158     return acutalKeyLength_;
159 }
160 
SetActualKeyLength(uint32_t length)161 inline void PropertyAccessor::SetActualKeyLength(uint32_t length)
162 {
163     acutalKeyLength_ = length;
164 }
165 
AddKeysEndIfNeeded(JSHandle<TaggedArray> keys)166 void PropertyAccessor::AddKeysEndIfNeeded(JSHandle<TaggedArray> keys)
167 {
168     // when has duplicated keys
169     if (acutalKeyLength_ < keyLength_) {
170         keys->Set(thread_, acutalKeyLength_ + EnumCache::ENUM_CACHE_HEADER_SIZE, JSTaggedValue::Undefined());
171     }
172 }
173 
TryInitEnumCacheWithProtoChainInfo()174 void PropertyAccessor::TryInitEnumCacheWithProtoChainInfo()
175 {
176 #if ECMASCRIPT_ENABLE_IC
177     if (!canUseEnumCache_) {
178         JSObject::SetEnumCacheKind(thread_, TaggedArray::Cast(fastKeysArray_->GetTaggedObject()), EnumCacheKind::NONE);
179         return;
180     }
181     ASSERT(!onlyHasSimpleProperties_);
182     JSHandle<JSObject> receiverObj(receiver_);
183     JSHandle<JSHClass> jsHclass(thread_, receiverObj->GetJSHClass());
184     jsHclass->SetEnumCache(thread_, fastKeysArray_.GetTaggedValue());
185     JSObject::SetEnumCacheKind(
186         thread_, TaggedArray::Cast(fastKeysArray_->GetTaggedObject()), EnumCacheKind::PROTOCHAIN);
187     cachedHclass_.Update(jsHclass);
188     JSHClass::EnableProtoChangeMarker(thread_, jsHclass);
189 #endif
190 }
191 
GetKeysFast()192 JSHandle<JSTaggedValue> PropertyAccessor::GetKeysFast()
193 {
194     if (!fastKeysArray_->IsUndefined()) {
195         AddKeysEndIfNeeded(JSHandle<TaggedArray>(thread_, fastKeysArray_.GetTaggedValue()));
196         return fastKeysArray_;
197     }
198     if (hasSlowProperties_) {
199         return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined());
200     }
201     ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
202     uint32_t arraySize = keyLength_ + EnumCache::ENUM_CACHE_HEADER_SIZE;
203     JSHandle<TaggedArray> keyArray = factory->NewTaggedArray(arraySize);
204     JSHandle<TaggedQueue> shadowQueue = factory->NewTaggedQueue(shadowKeyLength_);
205     uint32_t keysNum = EnumCache::ENUM_CACHE_HEADER_SIZE;
206     JSMutableHandle<JSTaggedValue> current(thread_, receiver_);
207     while (current->IsHeapObject()) {
208         JSObject::AppendOwnEnumPropertyKeys(thread_, JSHandle<JSObject>(current), keyArray, &keysNum, shadowQueue);
209         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
210         JSObject::ClearHasDeleteProperty(current);
211         current.Update(JSObject::GetPrototype(current.GetTaggedValue()));
212     }
213     SetActualKeyLength(keysNum - EnumCache::ENUM_CACHE_HEADER_SIZE);
214     AddKeysEndIfNeeded(keyArray);
215     fastKeysArray_.Update(keyArray.GetTaggedValue());
216     TryInitEnumCacheWithProtoChainInfo();
217     return fastKeysArray_;
218 }
219 
GetKeysSlow()220 JSHandle<JSTaggedValue> PropertyAccessor::GetKeysSlow()
221 {
222     std::vector<JSHandle<TaggedArray>> remainings;
223     std::vector<JSHandle<JSTaggedValue>> visited;
224     JSMutableHandle<JSTaggedValue> current(thread_, receiver_);
225     while (current->IsHeapObject()) {
226         PushRemainingKeys(JSHandle<JSObject>(current), remainings);
227         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
228         JSObject::ClearHasDeleteProperty(current);
229         visited.emplace_back(thread_, current.GetTaggedValue());
230         current.Update(JSTaggedValue::GetPrototype(thread_, current));
231         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
232     }
233     MergeRemainings(remainings, visited);
234     return JSHandle<JSTaggedValue>(thread_, slowKeysArray_.GetTaggedValue());
235 }
236 
PushRemainingKeys(JSHandle<JSObject> object,std::vector<JSHandle<TaggedArray>> & remainings)237 void PropertyAccessor::PushRemainingKeys(JSHandle<JSObject> object, std::vector<JSHandle<TaggedArray>> &remainings)
238 {
239     JSMutableHandle<JSTaggedValue> value(thread_, JSTaggedValue::Undefined());
240     uint32_t remainingIndex = 0;
241     if (object->IsJSProxy()) {
242         JSHandle<TaggedArray> proxyArr = JSProxy::OwnPropertyKeys(thread_, JSHandle<JSProxy>(object));
243         RETURN_IF_ABRUPT_COMPLETION(thread_);
244         uint32_t length = proxyArr->GetLength();
245         for (uint32_t i = 0; i < length; i++) {
246             value.Update(proxyArr->Get(i));
247             PropertyDescriptor desc(thread_);
248             JSProxy::GetOwnProperty(thread_, JSHandle<JSProxy>(object), value, desc);
249             RETURN_IF_ABRUPT_COMPLETION(thread_);
250             if (!desc.IsEnumerable()) {
251                 proxyArr->Set(thread_, i, JSTaggedValue::Hole());
252             } else {
253                 remainingIndex++;
254             }
255         }
256         remainings.push_back(proxyArr);
257         AccumulateKeyLength(remainingIndex);
258     } else {
259         JSHandle<TaggedArray> array = JSTaggedValue::GetOwnEnumPropertyKeys(thread_, JSHandle<JSTaggedValue>(object));
260         uint32_t length = array->GetLength();
261         for (uint32_t i = 0; i < length; i++) {
262             value.Update(array->Get(i));
263             if (!value->IsString()) {
264                 array->Set(thread_, i, JSTaggedValue::Hole());
265             } else {
266                 remainingIndex++;
267             }
268         }
269         remainings.push_back(array);
270         AccumulateKeyLength(remainingIndex);
271     }
272 }
273 
MergeRemainings(const std::vector<JSHandle<TaggedArray>> & remainings,const std::vector<JSHandle<JSTaggedValue>> & visited)274 void PropertyAccessor::MergeRemainings(const std::vector<JSHandle<TaggedArray>> &remainings,
275                                        const std::vector<JSHandle<JSTaggedValue>> &visited)
276 {
277     uint32_t arraySize = keyLength_ + EnumCache::ENUM_CACHE_HEADER_SIZE;
278     JSHandle<TaggedArray> keyArray = thread_->GetEcmaVM()->GetFactory()->NewTaggedArray(arraySize);
279 
280     JSMutableHandle<TaggedArray> remaining(thread_, JSTaggedValue::Undefined());
281     JSMutableHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::Undefined());
282     JSMutableHandle<JSTaggedValue> objHandle(thread_, JSTaggedValue::Undefined());
283     uint32_t index = EnumCache::ENUM_CACHE_HEADER_SIZE;
284     uint32_t numberOfRemaining = remainings.size();
285     for (uint32_t i = 0; i < numberOfRemaining; i++) {
286         remaining.Update(remainings[i]);
287         uint32_t remainingSize = remaining->GetLength();
288         for (uint32_t j = 0; j < remainingSize; j++) {
289             keyHandle.Update(remaining->Get(thread_, j));
290             if (keyHandle->IsHole()) {
291                 continue;
292             }
293             bool has = false;
294             for (uint32_t k = 0; k < i; k++) {
295                 objHandle.Update(visited[k]);
296                 PropertyDescriptor desc(thread_);
297                 has = JSTaggedValue::GetOwnProperty(thread_, objHandle, keyHandle, desc);
298                 RETURN_IF_ABRUPT_COMPLETION(thread_);
299                 if (has) {
300                     break;
301                 }
302             }
303             if (!has) {
304                 keyArray->Set(thread_, index, keyHandle);
305                 index++;
306             }
307         }
308     }
309     SetActualKeyLength(index - EnumCache::ENUM_CACHE_HEADER_SIZE);
310     AddKeysEndIfNeeded(keyArray);
311     slowKeysArray_.Update(keyArray.GetTaggedValue());
312     JSObject::SetEnumCacheKind(thread_, *keyArray, EnumCacheKind::NONE);
313 }
314 }  // namespace panda::ecmascript
315