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