• 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/ic/proto_change_details.h"
19 #include "ecmascript/js_handle.h"
20 #include "ecmascript/js_object-inl.h"
21 #include "ecmascript/js_tagged_value.h"
22 
23 namespace panda::ecmascript {
PropertyAccessor(JSThread * thread,JSHandle<JSTaggedValue> object)24 PropertyAccessor::PropertyAccessor(JSThread *thread, JSHandle<JSTaggedValue> object)
25     : thread_(thread),
26       receiver_(thread, object.GetTaggedValue()),
27       fastKeysArray_(thread, JSTaggedValue::Undefined()),
28       cachedHClass_(thread, JSTaggedValue::Undefined()),
29       enumCache_(thread, JSTaggedValue::Undefined()),
30       keyLength_(0),
31       shadowKeyLength_(0),
32       onlyHasSimpleProperties_(true),
33       hasSlowProperties_(false),
34       hasPrototypeChainEnumCache_(false),
35       slowKeysArray_(thread, JSTaggedValue::Undefined()),
36       acutalKeyLength_(0)
37 {
38     PreLoad();
39 }
40 
PreLoad()41 void PropertyAccessor::PreLoad()
42 {
43     JSHandle<JSObject> receiverObj(receiver_);
44     JSHandle<JSHClass> jsHClass(thread_, receiverObj->GetJSHClass());
45     if (receiver_->IsSlowKeysObject()) {
46         hasSlowProperties_ = true;
47         return;
48     }
49     enumCache_ = JSHandle<JSTaggedValue>::Cast(
50         JSObject::GetOrCreateEnumCache(thread_, jsHClass));
51     cachedHClass_.Update(jsHClass);
52     if (jsHClass->IsDictionaryMode()) {
53         onlyHasSimpleProperties_ = false;
54     }
55     uint32_t numOfElements = receiverObj->GetNumberOfElements(thread_);
56     if (numOfElements > 0) {
57         AccumulateKeyLength(numOfElements);
58         onlyHasSimpleProperties_ = false;
59         tryPrototypeChainEnumCache_ = false;
60     }
61     std::pair<uint32_t, uint32_t> numOfKeys = receiverObj->GetNumberOfEnumKeys(thread_);
62     uint32_t numOfEnumKeys = numOfKeys.first;
63     if (numOfEnumKeys > 0) {
64         AccumulateKeyLength(numOfEnumKeys);
65     }
66     uint32_t numOfShadowKeys = numOfKeys.second;
67     if (numOfShadowKeys > 0) {
68         AccumulateShadowKeyLength(numOfShadowKeys);
69     }
70     CopyKeyLengthToSelf();
71     hasPrototypeChainEnumCache_ = HasPrototypeChainEnumCache();
72     CollectPrototypeInfo();
73     if (hasSlowProperties_ || !onlyHasSimpleProperties_) {
74         return;
75     }
76     ASSERT(!hasSlowProperties_ && onlyHasSimpleProperties_);
77     // Fath path for simple properties.
78     InitSimplePropertiesEnumCache();
79 }
80 
HasPrototypeChainEnumCache()81 bool PropertyAccessor::HasPrototypeChainEnumCache()
82 {
83     JSTaggedValue current = JSTaggedValue::GetPrototype(thread_, receiver_);
84     if (!current.IsHeapObject()) {
85         tryPrototypeChainEnumCache_ = false;
86         return false;
87     }
88     JSTaggedValue enumCache = current.GetTaggedObject()->GetClass()->GetEnumCache(thread_);
89     return enumCache.IsEnumCacheAllValid(thread_);
90 }
91 
CollectPrototypeInfo()92 void PropertyAccessor::CollectPrototypeInfo()
93 {
94     DISALLOW_GARBAGE_COLLECTION;
95     JSTaggedValue current = JSTaggedValue::GetPrototype(thread_, receiver_);
96     RETURN_IF_ABRUPT_COMPLETION(thread_);
97     while (current.IsHeapObject()) {
98         if (current.IsSlowKeysObject()) {
99             hasSlowProperties_ = true;
100             break;
101         }
102         JSObject *currentObj = JSObject::Cast(current.GetTaggedObject());
103         uint32_t numOfCurrentElements = currentObj->GetNumberOfElements(thread_);
104         if (numOfCurrentElements > 0) {
105             AccumulateKeyLength(numOfCurrentElements);
106             onlyHasSimpleProperties_ = false;
107             hasPrototypeChainEnumCache_ = false;
108             tryPrototypeChainEnumCache_ = false;
109         }
110         std::pair<uint32_t, uint32_t> numOfKeys = currentObj->GetNumberOfEnumKeys(thread_);
111         uint32_t numOfEnumKeys = numOfKeys.first;
112         if (numOfEnumKeys > 0) {
113             AccumulateKeyLength(numOfEnumKeys);
114             onlyHasSimpleProperties_ = false;
115         }
116         uint32_t numOfShadowKeys = numOfKeys.second;
117         if (numOfShadowKeys > 0) {
118             AccumulateShadowKeyLength(numOfShadowKeys);
119         }
120         JSHClass *jshclass = currentObj->GetJSHClass();
121         if (jshclass->IsDictionaryMode()) {
122             onlyHasSimpleProperties_ = false;
123             hasPrototypeChainEnumCache_ = false;
124             tryPrototypeChainEnumCache_ = false;
125         }
126         current = JSObject::GetPrototype(thread_, current);
127     }
128 }
129 
130 // For simple enumCache, set the ProtoChainInfoEnumCache of
131 // the receiver's prototype to JSTaggedValue::UNDEFINED.
132 // Additionally, enable the proto change marker to ensure that if the receiver's prototype chain changes,
133 // the ProtoChainInfoEnumCache of the receiver's prototype becomes JSTaggedValue::NULL.
InitSimplePropertiesEnumCache()134 void PropertyAccessor::InitSimplePropertiesEnumCache()
135 {
136     ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
137     JSHandle<JSObject> receiverObj(receiver_);
138     JSHClass *jsHClass = receiverObj->GetJSHClass();
139 
140     ASSERT(receiverObj->GetNumberOfElements(thread_) == 0);
141     ASSERT(!receiver_->IsInSharedHeap());
142 
143     JSMutableHandle<TaggedArray> keyArray(thread_, JSTaggedValue::Undefined());
144     if (keyLength_ == 0) {
145         keyArray.Update(factory->EmptyArray());
146         SetActualKeyLength(0);
147     } else {
148         uint32_t arraySize = keyLength_;
149         JSHandle<TaggedArray> newArray = thread_->GetEcmaVM()->GetFactory()->NewTaggedArray(arraySize);
150         uint32_t length = JSObject::GetAllEnumKeys(thread_, receiverObj, 0, newArray);
151         SetActualKeyLength(length);
152         keyArray.Update(newArray);
153     }
154 
155     // Set proto's ProtoChainInfoEnumCache to JSTaggedValue::NULL and enable proto's ChangeMarker.
156     JSTaggedValue proto = JSObject::GetPrototype(thread_, receiver_.GetTaggedValue());
157     if (proto.IsHeapObject()) {
158         JSHandle<EnumCache> enumCache = JSObject::GetOrCreateEnumCache(thread_,
159             JSHandle<JSHClass>(thread_, proto.GetTaggedObject()->GetClass()));
160         enumCache->SetProtoChainInfoEnumCache(thread_, JSTaggedValue::Undefined());
161         JSHClass::EnableProtoChangeMarker(thread_, JSHandle<JSHClass>(thread_, jsHClass));
162     }
163 
164     JSObject::SetEnumCacheKind(thread_, JSHandle<EnumCache>::Cast(enumCache_), EnumCacheKind::SIMPLE);
165     JSObject::ClearHasDeleteProperty(receiver_);
166     fastKeysArray_.Update(keyArray.GetTaggedValue());
167 }
168 
CopyKeyLengthToSelf()169 inline void PropertyAccessor::CopyKeyLengthToSelf()
170 {
171     keyLengthSelf_ = keyLength_;
172     shadowKeyLengthSelf_ = shadowKeyLength_;
173 }
174 
AccumulateKeyLength(uint32_t length)175 inline void PropertyAccessor::AccumulateKeyLength(uint32_t length)
176 {
177     keyLength_ += length;
178 }
179 
AccumulateShadowKeyLength(uint32_t length)180 inline void PropertyAccessor::AccumulateShadowKeyLength(uint32_t length)
181 {
182     shadowKeyLength_ += length;
183 }
184 
GetCachedHClass() const185 JSHandle<JSTaggedValue> PropertyAccessor::GetCachedHClass() const
186 {
187     return cachedHClass_;
188 }
189 
GetEnumCache() const190 JSHandle<JSTaggedValue> PropertyAccessor::GetEnumCache() const
191 {
192     return enumCache_;
193 }
194 
GetActualKeyLength() const195 uint32_t PropertyAccessor::GetActualKeyLength() const
196 {
197     return acutalKeyLength_;
198 }
199 
SetActualKeyLength(uint32_t length)200 inline void PropertyAccessor::SetActualKeyLength(uint32_t length)
201 {
202     acutalKeyLength_ = length;
203 }
204 
AddUndefinedEndIfNeeded(JSHandle<TaggedArray> keys)205 void PropertyAccessor::AddUndefinedEndIfNeeded(JSHandle<TaggedArray> keys)
206 {
207     // when has duplicated keys
208     if (acutalKeyLength_ < keyLength_) {
209         keys->Set(thread_, acutalKeyLength_, JSTaggedValue::Undefined());
210     }
211 }
212 
AddUndefinedEndIfNeeded(JSHandle<TaggedArray> keys,const uint32_t keyLength,const uint32_t acutalKeyLength)213 void PropertyAccessor::AddUndefinedEndIfNeeded(JSHandle<TaggedArray> keys,
214                                                const uint32_t keyLength, const uint32_t acutalKeyLength)
215 {
216     if (acutalKeyLength < keyLength) {
217         keys->Set(thread_, acutalKeyLength, JSTaggedValue::Undefined());
218     }
219 }
220 
GetChainKeys(const JSHandle<JSTaggedValue> & receiver,const uint32_t keyLength,const uint32_t shadowKeyLength)221 JSHandle<TaggedArray> PropertyAccessor::GetChainKeys(const JSHandle<JSTaggedValue> &receiver,
222                                                      const uint32_t keyLength,
223                                                      const uint32_t shadowKeyLength)
224 {
225     ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
226     uint32_t arraySize = keyLength;
227     JSHandle<TaggedArray> keyArray = factory->NewTaggedArray(arraySize);
228     JSHandle<TaggedQueue> shadowQueue = factory->NewTaggedQueue(shadowKeyLength + 1);
229     uint32_t keysNum = 0;
230     JSMutableHandle<JSTaggedValue> current(thread_, receiver);
231     while (current->IsHeapObject()) {
232         JSObject::AppendOwnEnumPropertyKeys(thread_, JSHandle<JSObject>(current), keyArray, &keysNum, shadowQueue);
233         RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread_);
234         JSObject::ClearHasDeleteProperty(current);
235         current.Update(JSObject::GetPrototype(thread_, current.GetTaggedValue()));
236     }
237     AddUndefinedEndIfNeeded(keyArray, keyLength, keysNum);
238     return keyArray;
239 }
240 
GetKeysFastWithoutCache()241 JSHandle<JSTaggedValue> PropertyAccessor::GetKeysFastWithoutCache()
242 {
243     JSHandle<TaggedArray> keyArray = GetChainKeys(receiver_, keyLength_, shadowKeyLength_);
244     return JSHandle<JSTaggedValue>::Cast(keyArray);
245 }
246 
GetOwnKeysWithoutCache()247 std::pair<JSHandle<TaggedArray>, JSHandle<TaggedQueue>> PropertyAccessor::GetOwnKeysWithoutCache()
248 {
249     ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
250     uint32_t arraySize = keyLengthSelf_;
251     JSHandle<TaggedArray> keyArrayOnReceiver = factory->NewTaggedArray(arraySize);
252     JSHandle<TaggedQueue> shadowQueueOnReceiver = factory->NewTaggedQueue(shadowKeyLengthSelf_ + 1);
253     uint32_t keysNum = 0;
254     JSObject::AppendOwnEnumPropertyKeys(thread_, JSHandle<JSObject>(receiver_),
255                                         keyArrayOnReceiver, &keysNum, shadowQueueOnReceiver);
256     AddUndefinedEndIfNeeded(keyArrayOnReceiver, arraySize, keysNum);
257     return std::make_pair(keyArrayOnReceiver, shadowQueueOnReceiver);
258 }
259 
IsObjectWithoutKey() const260 bool PropertyAccessor::IsObjectWithoutKey() const
261 {
262     return keyLengthSelf_ == 0 && shadowKeyLengthSelf_ == 0;
263 }
264 
GetOwnKeys()265 std::pair<JSHandle<TaggedArray>, JSHandle<TaggedQueue>> PropertyAccessor::GetOwnKeys()
266 {
267     if (IsObjectWithoutKey()) {
268         ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
269         JSHandle<TaggedArray> keyArrayOnReceiver = factory->NewTaggedArray(0);
270         JSHandle<TaggedQueue> shadowQueueOnReceiver = factory->NewTaggedQueue(0);
271         return std::make_pair(keyArrayOnReceiver, shadowQueueOnReceiver);
272     }
273 
274     return GetOwnKeysWithoutCache();
275 }
276 
GetAndSetChainKeys(const JSHandle<JSTaggedValue> & proto,const uint32_t keyLength,const uint32_t shadowKeyLength)277 JSHandle<TaggedArray> PropertyAccessor::GetAndSetChainKeys(const JSHandle<JSTaggedValue> &proto,
278                                                            const uint32_t keyLength,
279                                                            const uint32_t shadowKeyLength)
280 {
281     JSHandle<TaggedArray> keyArrayOnPrototypeChain = GetChainKeys(proto, keyLength, shadowKeyLength);
282     ASSERT(!receiver_->IsInSharedHeap());
283     ASSERT(!onlyHasSimpleProperties_);
284     JSHandle<JSObject> protoObj(proto);
285     JSHandle<JSHClass> jsHClass(thread_, protoObj->GetJSHClass());
286     JSHandle<EnumCache> enumCacheProto = JSObject::GetOrCreateEnumCache(thread_, jsHClass);
287 
288     // receiver's ProtoChainInfoEnumCache == proto's EnumCacheAll.
289     JSHandle<EnumCache>::Cast(enumCache_)->
290         SetProtoChainInfoEnumCache(thread_, keyArrayOnPrototypeChain.GetTaggedValue());
291     enumCacheProto->SetEnumCacheAll(thread_, keyArrayOnPrototypeChain.GetTaggedValue());
292     JSHClass::EnablePHCProtoChangeMarker(thread_, jsHClass);
293     return keyArrayOnPrototypeChain;
294 }
295 
AddKey(const JSHandle<JSTaggedValue> & value,JSHandle<TaggedArray> & allKeys,uint32_t & allKeysLength,const JSHandle<TaggedArray> & keyArrayOnReceiver,const JSHandle<TaggedQueue> & shadowQueueOnReceiver)296 void PropertyAccessor::AddKey(const JSHandle<JSTaggedValue> &value,
297                               JSHandle<TaggedArray>& allKeys, uint32_t& allKeysLength,
298                               const JSHandle<TaggedArray> &keyArrayOnReceiver,
299                               const JSHandle<TaggedQueue> &shadowQueueOnReceiver)
300 {
301     uint32_t receiverKeysLength = keyArrayOnReceiver->GetLength();
302     JSMutableHandle<JSTaggedValue> key(thread_, JSTaggedValue::Undefined());
303     for (uint32_t i = 0; i < receiverKeysLength; i++) {
304         key.Update(keyArrayOnReceiver->Get(thread_, i));
305         if (JSTaggedValue::Equal(thread_, key, value)) {
306             return;
307         }
308     }
309 
310     uint32_t shadowKeysSize = shadowQueueOnReceiver->Size(thread_);
311     for (uint32_t i = 0; i < shadowKeysSize; i++) {
312         key.Update(shadowQueueOnReceiver->Get(thread_, i));
313         if (JSTaggedValue::Equal(thread_, key, value)) {
314             return;
315         }
316     }
317     allKeys->Set(thread_, allKeysLength++, value);
318 }
319 
CombineKeys(const JSHandle<TaggedArray> & keyArrayOnReceiver,const JSHandle<TaggedArray> & keyArrayOnPrototypeChain,const JSHandle<TaggedQueue> & shadowQueueOnReceiver)320 JSHandle<TaggedArray> PropertyAccessor::CombineKeys(const JSHandle<TaggedArray> &keyArrayOnReceiver,
321                                                     const JSHandle<TaggedArray> &keyArrayOnPrototypeChain,
322                                                     const JSHandle<TaggedQueue> &shadowQueueOnReceiver)
323 {
324     uint32_t receiverKeysLength = keyArrayOnReceiver->GetLength();
325     uint32_t PrototypeChainKeysLength = keyArrayOnPrototypeChain->GetLength();
326     uint32_t allKeysLength = receiverKeysLength;
327     ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
328     uint32_t arraySize = receiverKeysLength + PrototypeChainKeysLength;
329     JSHandle<TaggedArray> allKeys = factory->NewTaggedArray(arraySize);
330     allKeys->Copy(thread_, 0, 0, keyArrayOnReceiver.GetObject<TaggedArray>(), receiverKeysLength);
331     JSMutableHandle<JSTaggedValue> value(thread_, JSTaggedValue::Undefined());
332     for (uint32_t i = 0; i < PrototypeChainKeysLength; i++) {
333         value.Update(keyArrayOnPrototypeChain->Get(thread_, i));
334         AddKey(value, allKeys, allKeysLength, keyArrayOnReceiver, shadowQueueOnReceiver);
335     }
336     AddUndefinedEndIfNeeded(allKeys, arraySize, allKeysLength);
337     return allKeys;
338 }
339 
GetKeysFastWithPrototypeChainEnumCache()340 JSHandle<JSTaggedValue> PropertyAccessor::GetKeysFastWithPrototypeChainEnumCache()
341 {
342     auto [keyArrayOnReceiver, shadowQueueOnReceiver] = GetOwnKeys();
343     JSHandle<TaggedArray> keyArrayOnPrototypeChain;
344     auto proto = JSTaggedValue::GetPrototype(thread_, receiver_);
345     JSHandle<JSTaggedValue> protoHandle(thread_, proto);
346     if (hasPrototypeChainEnumCache_) {
347         EnumCache* enumCacheProto = EnumCache::Cast(proto.GetTaggedObject()->GetClass()->GetEnumCache(thread_));
348         JSHandle<JSTaggedValue> keyValueOnPrototypeChain(thread_, enumCacheProto->GetEnumCacheAll(thread_));
349         keyArrayOnPrototypeChain = JSHandle<TaggedArray>::Cast(keyValueOnPrototypeChain);
350         JSHandle<EnumCache>::Cast(enumCache_)->
351             SetProtoChainInfoEnumCache(thread_, keyArrayOnPrototypeChain.GetTaggedValue());
352     } else {
353         keyArrayOnPrototypeChain = GetAndSetChainKeys(protoHandle,
354             keyLength_ - keyLengthSelf_, shadowKeyLength_ - shadowKeyLengthSelf_);
355     }
356 
357     JSHandle<TaggedArray> allKeys = CombineKeys(keyArrayOnReceiver, keyArrayOnPrototypeChain, shadowQueueOnReceiver);
358     return JSHandle<JSTaggedValue>::Cast(allKeys);
359 }
360 
GetKeysFast()361 JSHandle<JSTaggedValue> PropertyAccessor::GetKeysFast()
362 {
363     // Fath path for simple properties.
364     if (!fastKeysArray_->IsUndefined()) {
365         AddUndefinedEndIfNeeded(JSHandle<TaggedArray>(thread_, fastKeysArray_.GetTaggedValue()));
366         JSHandle<EnumCache>::Cast(enumCache_)->SetEnumCacheAll(thread_, fastKeysArray_.GetTaggedValue());
367         return fastKeysArray_;
368     }
369 
370     if (hasSlowProperties_) {
371         return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined());
372     }
373 
374     ASSERT(!receiver_->IsInSharedHeap());
375     ASSERT(!onlyHasSimpleProperties_);
376 
377     if (tryPrototypeChainEnumCache_) {
378         // This handles the case where prototype chain and self have no elements or slow keys.
379         fastKeysArray_.Update(GetKeysFastWithPrototypeChainEnumCache());
380         JSHandle<EnumCache>::Cast(enumCache_)->SetEnumCacheAll(thread_, fastKeysArray_.GetTaggedValue());
381     } else {
382         fastKeysArray_.Update(GetKeysFastWithoutCache());
383     }
384     JSObject::SetEnumCacheKind(thread_, JSHandle<EnumCache>::Cast(enumCache_), EnumCacheKind::PROTOCHAIN);
385     return fastKeysArray_;
386 }
387 
GetKeysSlow()388 JSHandle<JSTaggedValue> PropertyAccessor::GetKeysSlow()
389 {
390     std::vector<JSHandle<TaggedArray>> remainings;
391     std::vector<JSHandle<JSTaggedValue>> visited;
392     JSMutableHandle<JSTaggedValue> current(thread_, receiver_);
393     while (current->IsHeapObject()) {
394         PushRemainingKeys(JSHandle<JSObject>(current), remainings);
395         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
396         JSObject::ClearHasDeleteProperty(current);
397         visited.emplace_back(thread_, current.GetTaggedValue());
398         current.Update(JSTaggedValue::GetPrototype(thread_, current));
399         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
400     }
401     MergeRemainings(remainings, visited);
402     return JSHandle<JSTaggedValue>(thread_, slowKeysArray_.GetTaggedValue());
403 }
404 
PushRemainingKeys(JSHandle<JSObject> object,std::vector<JSHandle<TaggedArray>> & remainings)405 void PropertyAccessor::PushRemainingKeys(JSHandle<JSObject> object, std::vector<JSHandle<TaggedArray>> &remainings)
406 {
407     JSMutableHandle<JSTaggedValue> value(thread_, JSTaggedValue::Undefined());
408     uint32_t remainingIndex = 0;
409     if (object->IsJSProxy()) {
410         JSHandle<TaggedArray> proxyArr = JSProxy::OwnPropertyKeys(thread_, JSHandle<JSProxy>(object));
411         RETURN_IF_ABRUPT_COMPLETION(thread_);
412         uint32_t length = proxyArr->GetLength();
413         for (uint32_t i = 0; i < length; i++) {
414             value.Update(proxyArr->Get(thread_, i));
415             PropertyDescriptor desc(thread_);
416             JSProxy::GetOwnProperty(thread_, JSHandle<JSProxy>(object), value, desc);
417             RETURN_IF_ABRUPT_COMPLETION(thread_);
418             if (!desc.IsEnumerable()) {
419                 proxyArr->Set(thread_, i, JSTaggedValue::Hole());
420             } else {
421                 remainingIndex++;
422             }
423         }
424         remainings.push_back(proxyArr);
425         AccumulateKeyLength(remainingIndex);
426     } else {
427         JSHandle<TaggedArray> array = JSTaggedValue::GetOwnEnumPropertyKeys(thread_, JSHandle<JSTaggedValue>(object));
428         uint32_t length = array->GetLength();
429         for (uint32_t i = 0; i < length; i++) {
430             value.Update(array->Get(thread_, i));
431             if (!value->IsString()) {
432                 array->Set(thread_, i, JSTaggedValue::Hole());
433             } else {
434                 remainingIndex++;
435             }
436         }
437         remainings.push_back(array);
438         AccumulateKeyLength(remainingIndex);
439     }
440 }
441 
MergeRemainings(const std::vector<JSHandle<TaggedArray>> & remainings,const std::vector<JSHandle<JSTaggedValue>> & visited)442 void PropertyAccessor::MergeRemainings(const std::vector<JSHandle<TaggedArray>> &remainings,
443                                        const std::vector<JSHandle<JSTaggedValue>> &visited)
444 {
445     uint32_t arraySize = keyLength_;
446     JSHandle<TaggedArray> keyArray = thread_->GetEcmaVM()->GetFactory()->NewTaggedArray(arraySize);
447 
448     JSMutableHandle<TaggedArray> remaining(thread_, JSTaggedValue::Undefined());
449     JSMutableHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::Undefined());
450     JSMutableHandle<JSTaggedValue> objHandle(thread_, JSTaggedValue::Undefined());
451     uint32_t index = 0;
452     uint32_t numberOfRemaining = remainings.size();
453     for (uint32_t i = 0; i < numberOfRemaining; i++) {
454         remaining.Update(remainings[i]);
455         uint32_t remainingSize = remaining->GetLength();
456         for (uint32_t j = 0; j < remainingSize; j++) {
457             keyHandle.Update(remaining->Get(thread_, j));
458             if (keyHandle->IsHole()) {
459                 continue;
460             }
461             bool has = false;
462             for (uint32_t k = 0; k < i; k++) {
463                 objHandle.Update(visited[k]);
464                 PropertyDescriptor desc(thread_);
465                 has = JSTaggedValue::GetOwnProperty(thread_, objHandle, keyHandle, desc);
466                 RETURN_IF_ABRUPT_COMPLETION(thread_);
467                 if (has) {
468                     break;
469                 }
470             }
471             if (!has) {
472                 keyArray->Set(thread_, index, keyHandle);
473                 index++;
474             }
475         }
476     }
477     SetActualKeyLength(index);
478     AddUndefinedEndIfNeeded(keyArray);
479     slowKeysArray_.Update(keyArray.GetTaggedValue());
480 }
481 }  // namespace panda::ecmascript
482