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