• 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_hclass-inl.h"
17 
18 #include <algorithm>
19 
20 #include "ecmascript/base/config.h"
21 #include "ecmascript/global_env.h"
22 #include "ecmascript/ic/proto_change_details.h"
23 #include "ecmascript/js_object-inl.h"
24 #include "ecmascript/js_symbol.h"
25 #include "ecmascript/mem/c_containers.h"
26 #include "ecmascript/tagged_array-inl.h"
27 #include "ecmascript/weak_vector.h"
28 
29 namespace panda::ecmascript {
PutIfAbsent(const JSThread * thread,const JSHandle<TransitionsDictionary> & dictionary,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & metaData)30 JSHandle<TransitionsDictionary> TransitionsDictionary::PutIfAbsent(const JSThread *thread,
31                                                                    const JSHandle<TransitionsDictionary> &dictionary,
32                                                                    const JSHandle<JSTaggedValue> &key,
33                                                                    const JSHandle<JSTaggedValue> &value,
34                                                                    const JSHandle<JSTaggedValue> &metaData)
35 {
36     int hash = TransitionsDictionary::Hash(key.GetTaggedValue(), metaData.GetTaggedValue());
37 
38     /* no need to add key if exist */
39     int entry = dictionary->FindEntry(key.GetTaggedValue(), metaData.GetTaggedValue());
40     if (entry != -1) {
41         if (dictionary->GetValue(entry).IsUndefined()) {
42             JSTaggedValue weakValue = JSTaggedValue(value->CreateAndGetWeakRef());
43             dictionary->SetValue(thread, entry, weakValue);
44         }
45         return dictionary;
46     }
47 
48     // Check whether the dictionary should be extended.
49     JSHandle<TransitionsDictionary> newDictionary(HashTableT::GrowHashTable(thread, dictionary));
50     // Compute the key object.
51     entry = newDictionary->FindInsertIndex(hash);
52     JSTaggedValue val = value.GetTaggedValue();
53     newDictionary->SetEntry(thread, entry, key.GetTaggedValue(), val, metaData.GetTaggedValue());
54 
55     newDictionary->IncreaseEntries(thread);
56     return newDictionary;
57 }
58 
FindEntry(const JSTaggedValue & key,const JSTaggedValue & metaData)59 int TransitionsDictionary::FindEntry(const JSTaggedValue &key, const JSTaggedValue &metaData)
60 {
61     size_t size = static_cast<size_t>(Size());
62     uint32_t count = 1;
63     int32_t hash = TransitionsDictionary::Hash(key, metaData);
64     // GrowHashTable will guarantee the hash table is never full.
65     for (uint32_t entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) {
66         JSTaggedValue element = GetKey(entry);
67         if (element.IsHole()) {
68             continue;
69         }
70         if (element.IsUndefined()) {
71             return -1;
72         }
73 
74         if (TransitionsDictionary::IsMatch(key, metaData, element, GetAttributes(entry).GetWeakRawValue())) {
75             return static_cast<int>(entry);
76         }
77     }
78     return -1;
79 }
80 
Remove(const JSThread * thread,const JSHandle<TransitionsDictionary> & table,const JSHandle<JSTaggedValue> & key,const JSTaggedValue & metaData)81 JSHandle<TransitionsDictionary> TransitionsDictionary::Remove(const JSThread *thread,
82                                                               const JSHandle<TransitionsDictionary> &table,
83                                                               const JSHandle<JSTaggedValue> &key,
84                                                               const JSTaggedValue &metaData)
85 {
86     int entry = table->FindEntry(key.GetTaggedValue(), metaData);
87     if (entry == -1) {
88         return table;
89     }
90 
91     table->RemoveElement(thread, entry);
92     return TransitionsDictionary::Shrink(thread, table);
93 }
94 
Rehash(const JSThread * thread,TransitionsDictionary * newTable)95 void TransitionsDictionary::Rehash(const JSThread *thread, TransitionsDictionary *newTable)
96 {
97     DISALLOW_GARBAGE_COLLECTION;
98     if (newTable == nullptr) {
99         return;
100     }
101     int size = this->Size();
102     // Rehash elements to new table
103     int entryCount = 0;
104     for (int i = 0; i < size; i++) {
105         int fromIndex = GetEntryIndex(i);
106         JSTaggedValue k = this->GetKey(i);
107         JSTaggedValue v = this->GetValue(i);
108         if (IsKey(k) && TransitionsDictionary::CheckWeakExist(v)) {
109             int hash = TransitionsDictionary::Hash(k, this->GetAttributes(i));
110             int insertionIndex = GetEntryIndex(newTable->FindInsertIndex(hash));
111             JSTaggedValue tv = Get(fromIndex);
112             newTable->Set(thread, insertionIndex, tv);
113             for (int j = 1; j < TransitionsDictionary::ENTRY_SIZE; j++) {
114                 tv = Get(fromIndex + j);
115                 newTable->Set(thread, insertionIndex + j, tv);
116             }
117             entryCount++;
118         }
119     }
120     newTable->SetEntriesCount(thread, entryCount);
121     newTable->SetHoleEntriesCount(thread, 0);
122 }
123 
124 // class JSHClass
Initialize(const JSThread * thread,uint32_t size,JSType type,uint32_t inlinedProps)125 void JSHClass::Initialize(const JSThread *thread, uint32_t size, JSType type, uint32_t inlinedProps)
126 {
127     DISALLOW_GARBAGE_COLLECTION;
128     ClearBitField();
129     if (JSType::JS_OBJECT_FIRST <= type && type <= JSType::JS_OBJECT_LAST) {
130         SetObjectSize(size + inlinedProps * JSTaggedValue::TaggedTypeSize());
131         SetInlinedPropsStart(size);
132         SetLayout(thread, thread->GlobalConstants()->GetEmptyLayoutInfo());
133     } else {
134         SetObjectSize(size);
135         SetLayout(thread, JSTaggedValue::Null());
136     }
137     SetPrototype(thread, JSTaggedValue::Null());
138 
139     SetObjectType(type);
140     SetExtensible(true);
141     SetIsPrototype(false);
142     SetElementRepresentation(Representation::NONE);
143     SetTransitions(thread, JSTaggedValue::Undefined());
144     SetProtoChangeMarker(thread, JSTaggedValue::Null());
145     SetProtoChangeDetails(thread, JSTaggedValue::Null());
146     SetEnumCache(thread, JSTaggedValue::Null());
147 }
148 
Clone(const JSThread * thread,const JSHandle<JSHClass> & jshclass,bool withoutInlinedProperties)149 JSHandle<JSHClass> JSHClass::Clone(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
150                                    bool withoutInlinedProperties)
151 {
152     JSType type = jshclass->GetObjectType();
153     uint32_t size = jshclass->GetInlinedPropsStartSize();
154     uint32_t numInlinedProps = withoutInlinedProperties ? 0 : jshclass->GetInlinedProperties();
155     JSHandle<JSHClass> newJsHClass = thread->GetEcmaVM()->GetFactory()->NewEcmaHClass(size, type, numInlinedProps);
156     // Copy all
157     newJsHClass->Copy(thread, *jshclass);
158     newJsHClass->SetTransitions(thread, JSTaggedValue::Undefined());
159     newJsHClass->SetProtoChangeDetails(thread, JSTaggedValue::Null());
160     newJsHClass->SetEnumCache(thread, JSTaggedValue::Null());
161     // reuse Attributes first.
162     newJsHClass->SetLayout(thread, jshclass->GetLayout());
163 
164     if (jshclass->IsTS()) {
165         newJsHClass->SetTS(false);
166     }
167 
168     return newJsHClass;
169 }
170 
171 // use for transition to dictionary
CloneWithoutInlinedProperties(const JSThread * thread,const JSHandle<JSHClass> & jshclass)172 JSHandle<JSHClass> JSHClass::CloneWithoutInlinedProperties(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
173 {
174     return Clone(thread, jshclass, true);
175 }
176 
TransitionElementsToDictionary(const JSThread * thread,const JSHandle<JSObject> & obj)177 void JSHClass::TransitionElementsToDictionary(const JSThread *thread, const JSHandle<JSObject> &obj)
178 {
179     // property transition to slow first
180     if (!obj->GetJSHClass()->IsDictionaryMode()) {
181         JSObject::TransitionToDictionary(thread, obj);
182     }
183     obj->GetJSHClass()->SetIsDictionaryElement(true);
184     obj->GetJSHClass()->SetIsStableElements(false);
185 }
186 
SetPropertyOfObjHClass(const JSThread * thread,JSHandle<JSHClass> & jshclass,const JSHandle<JSTaggedValue> & key,const PropertyAttributes & attr)187 JSHandle<JSHClass> JSHClass::SetPropertyOfObjHClass(const JSThread *thread, JSHandle<JSHClass> &jshclass,
188                                                     const JSHandle<JSTaggedValue> &key,
189                                                     const PropertyAttributes &attr)
190 {
191     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
192     JSHClass *newClass = jshclass->FindTransitions(key.GetTaggedValue(), JSTaggedValue(attr.GetPropertyMetaData()));
193     if (newClass != nullptr) {
194         return JSHandle<JSHClass>(thread, newClass);
195     }
196 
197     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
198     newJsHClass->IncNumberOfProps();
199     uint32_t offset = attr.GetOffset();
200     {
201         JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, newJsHClass->GetLayout());
202         if (layoutInfoHandle->NumberOfElements() != static_cast<int>(offset)) {
203             layoutInfoHandle.Update(factory->CopyAndReSort(layoutInfoHandle, offset, offset + 1));
204         } else if (layoutInfoHandle->GetPropertiesCapacity() <= static_cast<int>(offset)) { // need to Grow
205             layoutInfoHandle.Update(
206                 factory->ExtendLayoutInfo(layoutInfoHandle, offset));
207         }
208         newJsHClass->SetLayout(thread, layoutInfoHandle);
209         layoutInfoHandle->AddKey(thread, offset, key.GetTaggedValue(), attr);
210     }
211 
212     AddTransitions(thread, jshclass, newJsHClass, key, attr);
213     return newJsHClass;
214 }
215 
AddProperty(const JSThread * thread,const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & key,const PropertyAttributes & attr)216 void JSHClass::AddProperty(const JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &key,
217                            const PropertyAttributes &attr)
218 {
219     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
220     JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
221     JSHClass *newClass = jshclass->FindTransitions(key.GetTaggedValue(), JSTaggedValue(attr.GetPropertyMetaData()));
222     if (newClass != nullptr) {
223         obj->SetClass(newClass);
224 #if ECMASCRIPT_ENABLE_IC
225         JSHClass::NotifyHclassChanged(thread, jshclass, JSHandle<JSHClass>(thread, newClass));
226 #endif
227         return;
228     }
229 
230     // 2. Create hclass
231     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
232 
233     // 3. Add Property and metaData
234     uint32_t offset = attr.GetOffset();
235     newJsHClass->IncNumberOfProps();
236 
237     {
238         JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, newJsHClass->GetLayout());
239 
240         if (layoutInfoHandle->NumberOfElements() != static_cast<int>(offset)) {
241             layoutInfoHandle.Update(factory->CopyAndReSort(layoutInfoHandle, offset, offset + 1));
242         } else if (layoutInfoHandle->GetPropertiesCapacity() <= static_cast<int>(offset)) {  // need to Grow
243             layoutInfoHandle.Update(
244                 factory->ExtendLayoutInfo(layoutInfoHandle, offset));
245         }
246         newJsHClass->SetLayout(thread, layoutInfoHandle);
247         layoutInfoHandle->AddKey(thread, offset, key.GetTaggedValue(), attr);
248     }
249 
250     // 4. Add newClass to old hclass's transitions.
251     AddTransitions(thread, jshclass, newJsHClass, key, attr);
252 
253     // 5. update hclass in object.
254 #if ECMASCRIPT_ENABLE_IC
255     JSHClass::NotifyHclassChanged(thread, jshclass, newJsHClass);
256 #endif
257     obj->SetClass(*newJsHClass);
258 }
259 
TransitionExtension(const JSThread * thread,const JSHandle<JSHClass> & jshclass)260 JSHandle<JSHClass> JSHClass::TransitionExtension(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
261 {
262     JSHandle<JSTaggedValue> key(thread->GlobalConstants()->GetHandledPreventExtensionsString());
263     {
264         auto *newClass = jshclass->FindTransitions(key.GetTaggedValue(), JSTaggedValue(0));
265         if (newClass != nullptr) {
266             return JSHandle<JSHClass>(thread, newClass);
267         }
268     }
269     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
270     // 2. new a hclass
271     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
272     newJsHClass->SetExtensible(false);
273 
274     JSTaggedValue attrs = newJsHClass->GetLayout();
275     {
276         JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, attrs);
277         layoutInfoHandle.Update(factory->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue());
278         newJsHClass->SetLayout(thread, layoutInfoHandle);
279     }
280 
281     // 3. Add newClass to old hclass's parent's transitions.
282     AddExtensionTransitions(thread, jshclass, newJsHClass, key);
283     // parent is the same as jshclass, already copy
284     return newJsHClass;
285 }
286 
TransitionProto(const JSThread * thread,const JSHandle<JSHClass> & jshclass,const JSHandle<JSTaggedValue> & proto)287 JSHandle<JSHClass> JSHClass::TransitionProto(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
288                                              const JSHandle<JSTaggedValue> &proto)
289 {
290     JSHandle<JSTaggedValue> key(thread->GlobalConstants()->GetHandledPrototypeString());
291 
292     {
293         auto *newClass = jshclass->FindProtoTransitions(key.GetTaggedValue(), proto.GetTaggedValue());
294         if (newClass != nullptr) {
295             return JSHandle<JSHClass>(thread, newClass);
296         }
297     }
298 
299     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
300     // 2. new a hclass
301     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
302     newJsHClass->SetPrototype(thread, proto.GetTaggedValue());
303 
304     JSTaggedValue layout = newJsHClass->GetLayout();
305     {
306         JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, layout);
307         layoutInfoHandle.Update(factory->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue());
308         newJsHClass->SetLayout(thread, layoutInfoHandle);
309     }
310 
311     // 3. Add newJsHClass to old jshclass's parent's transitions.
312     AddProtoTransitions(thread, jshclass, newJsHClass, key, proto);
313 
314     // parent is the same as jshclass, already copy
315     return newJsHClass;
316 }
317 
TransProtoWithoutLayout(const JSThread * thread,const JSHandle<JSHClass> & jshclass,const JSHandle<JSTaggedValue> & proto)318 JSHandle<JSHClass> JSHClass::TransProtoWithoutLayout(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
319                                                      const JSHandle<JSTaggedValue> &proto)
320 {
321     JSHandle<JSTaggedValue> key(thread->GlobalConstants()->GetHandledPrototypeString());
322 
323     {
324         auto *newClass = jshclass->FindProtoTransitions(key.GetTaggedValue(), proto.GetTaggedValue());
325         if (newClass != nullptr) {
326             return JSHandle<JSHClass>(thread, newClass);
327         }
328     }
329 
330     // 2. new a hclass
331     JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass);
332     newJsHClass->SetPrototype(thread, proto.GetTaggedValue());
333 
334     // 3. Add newJsHClass to old jshclass's parent's transitions.
335     AddProtoTransitions(thread, jshclass, newJsHClass, key, proto);
336 
337     // parent is the same as jshclass, already copy
338     return newJsHClass;
339 }
340 
SetPrototype(const JSThread * thread,JSTaggedValue proto)341 void JSHClass::SetPrototype(const JSThread *thread, JSTaggedValue proto)
342 {
343     JSHandle<JSTaggedValue> protoHandle(thread, proto);
344     SetPrototype(thread, protoHandle);
345 }
346 
SetPrototype(const JSThread * thread,const JSHandle<JSTaggedValue> & proto)347 void JSHClass::SetPrototype(const JSThread *thread, const JSHandle<JSTaggedValue> &proto)
348 {
349     // In the original version, whether the objcet is EcmaObject is determined,
350     // but proxy is not allowd.
351     if (proto->IsJSObject()) {
352         ShouldUpdateProtoClass(thread, proto);
353     }
354     SetProto(thread, proto);
355 }
356 
ShouldUpdateProtoClass(const JSThread * thread,const JSHandle<JSTaggedValue> & proto)357 void JSHClass::ShouldUpdateProtoClass(const JSThread *thread, const JSHandle<JSTaggedValue> &proto)
358 {
359     JSHandle<JSHClass> hclass(thread, proto->GetTaggedObject()->GetClass());
360     ASSERT(!Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(*hclass))->InReadOnlySpace());
361     if (!hclass->IsPrototype()) {
362         // If the objcet should be changed to the proto of an object,
363         // the original hclass cannot be shared.
364         JSHandle<JSHClass> newProtoClass = JSHClass::Clone(thread, hclass);
365         JSTaggedValue layout = newProtoClass->GetLayout();
366         // If the type of object is JSObject, the layout info value is initialized to the default value,
367         // if the value is not JSObject, the layout info value is initialized to null.
368         if (!layout.IsNull()) {
369             JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, layout);
370             layoutInfoHandle.Update(
371                 thread->GetEcmaVM()->GetFactory()->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue());
372             newProtoClass->SetLayout(thread, layoutInfoHandle);
373         }
374 
375 #if ECMASCRIPT_ENABLE_IC
376         // After the hclass is updated, check whether the proto chain status of ic is updated.
377         NotifyHclassChanged(thread, hclass, newProtoClass);
378 #endif
379         JSObject::Cast(proto->GetTaggedObject())->SetClass(*newProtoClass);
380         newProtoClass->SetIsPrototype(true);
381     }
382 }
383 
TransitionToDictionary(const JSThread * thread,const JSHandle<JSObject> & obj)384 void JSHClass::TransitionToDictionary(const JSThread *thread, const JSHandle<JSObject> &obj)
385 {
386     // 1. new a hclass
387     JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
388     JSHandle<JSHClass> newJsHClass = CloneWithoutInlinedProperties(thread, jshclass);
389 
390     {
391         DISALLOW_GARBAGE_COLLECTION;
392         // 2. Copy
393         newJsHClass->SetNumberOfProps(0);
394         newJsHClass->SetIsDictionaryMode(true);
395         ASSERT(newJsHClass->GetInlinedProperties() == 0);
396 
397         // 3. Add newJsHClass to ?
398 #if ECMASCRIPT_ENABLE_IC
399         JSHClass::NotifyHclassChanged(thread, JSHandle<JSHClass>(thread, obj->GetJSHClass()), newJsHClass);
400 #endif
401         obj->SetClass(newJsHClass);
402     }
403 }
404 
EnableProtoChangeMarker(const JSThread * thread,const JSHandle<JSHClass> & jshclass)405 JSHandle<JSTaggedValue> JSHClass::EnableProtoChangeMarker(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
406 {
407     JSTaggedValue proto = jshclass->GetPrototype();
408     if (!proto.IsECMAObject()) {
409         // Return JSTaggedValue directly. No proto check is needed.
410         UNREACHABLE();
411     }
412     JSHandle<JSObject> protoHandle(thread, proto);
413     JSHandle<JSHClass> protoClass(thread, protoHandle->GetJSHClass());
414     RegisterOnProtoChain(thread, protoClass);
415     JSTaggedValue protoChangeMarker = protoClass->GetProtoChangeMarker();
416     if (protoChangeMarker.IsProtoChangeMarker()) {
417         JSHandle<ProtoChangeMarker> markerHandle(thread, ProtoChangeMarker::Cast(protoChangeMarker.GetTaggedObject()));
418         if (!markerHandle->GetHasChanged()) {
419             return JSHandle<JSTaggedValue>(markerHandle);
420         }
421     }
422     JSHandle<ProtoChangeMarker> markerHandle = thread->GetEcmaVM()->GetFactory()->NewProtoChangeMarker();
423     markerHandle->SetHasChanged(false);
424     protoClass->SetProtoChangeMarker(thread, markerHandle.GetTaggedValue());
425     return JSHandle<JSTaggedValue>(markerHandle);
426 }
427 
NotifyHclassChanged(const JSThread * thread,JSHandle<JSHClass> oldHclass,JSHandle<JSHClass> newHclass)428 void JSHClass::NotifyHclassChanged(const JSThread *thread, JSHandle<JSHClass> oldHclass, JSHandle<JSHClass> newHclass)
429 {
430     if (!oldHclass->IsPrototype()) {
431         return;
432     }
433     // The old hclass is the same as new one
434     if (oldHclass.GetTaggedValue() == newHclass.GetTaggedValue()) {
435         return;
436     }
437     newHclass->SetIsPrototype(true);
438     JSHClass::NoticeThroughChain(thread, oldHclass);
439     JSHClass::RefreshUsers(thread, oldHclass, newHclass);
440 }
441 
RegisterOnProtoChain(const JSThread * thread,const JSHandle<JSHClass> & jshclass)442 void JSHClass::RegisterOnProtoChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
443 {
444     ASSERT(jshclass->IsPrototype());
445     JSHandle<JSHClass> user = jshclass;
446     JSHandle<ProtoChangeDetails> userDetails = GetProtoChangeDetails(thread, user);
447 
448     while (true) {
449         // Find the prototype chain as far as the hclass has not been registered.
450         if (userDetails->GetRegisterIndex() != static_cast<uint32_t>(ProtoChangeDetails::UNREGISTERED)) {
451             return;
452         }
453 
454         JSTaggedValue proto = user->GetPrototype();
455         if (!proto.IsHeapObject()) {
456             return;
457         }
458         if (proto.IsJSProxy()) {
459             return;
460         }
461         ASSERT(proto.IsECMAObject());
462         JSHandle<JSObject> protoHandle(thread, proto);
463         JSHandle<ProtoChangeDetails> protoDetails =
464             GetProtoChangeDetails(thread, JSHandle<JSHClass>(thread, protoHandle->GetJSHClass()));
465         JSTaggedValue listeners = protoDetails->GetChangeListener();
466         JSHandle<ChangeListener> listenersHandle;
467         if (listeners.IsUndefined()) {
468             listenersHandle = JSHandle<ChangeListener>(ChangeListener::Create(thread));
469         } else {
470             listenersHandle = JSHandle<ChangeListener>(thread, listeners);
471         }
472         uint32_t registerIndex = 0;
473         JSHandle<ChangeListener> newListeners = ChangeListener::Add(thread, listenersHandle, user, &registerIndex);
474         userDetails->SetRegisterIndex(registerIndex);
475         protoDetails->SetChangeListener(thread, newListeners.GetTaggedValue());
476         userDetails = protoDetails;
477         user = JSHandle<JSHClass>(thread, protoHandle->GetJSHClass());
478     }
479 }
480 
UnregisterOnProtoChain(const JSThread * thread,const JSHandle<JSHClass> & jshclass)481 bool JSHClass::UnregisterOnProtoChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
482 {
483     ASSERT(jshclass->IsPrototype());
484     if (!jshclass->GetProtoChangeDetails().IsProtoChangeDetails()) {
485         return false;
486     }
487     if (!jshclass->GetPrototype().IsECMAObject()) {
488         JSTaggedValue listeners =
489             ProtoChangeDetails::Cast(jshclass->GetProtoChangeDetails().GetTaggedObject())->GetChangeListener();
490         return !listeners.IsUndefined();
491     }
492     JSHandle<ProtoChangeDetails> currentDetails = GetProtoChangeDetails(thread, jshclass);
493     uint32_t index = currentDetails->GetRegisterIndex();
494     if (index == static_cast<uint32_t>(ProtoChangeDetails::UNREGISTERED)) {
495         return false;
496     }
497     JSTaggedValue proto = jshclass->GetPrototype();
498     ASSERT(proto.IsECMAObject());
499     JSTaggedValue protoDetailsValue = JSObject::Cast(proto.GetTaggedObject())->GetJSHClass()->GetProtoChangeDetails();
500     ASSERT(protoDetailsValue.IsProtoChangeDetails());
501     JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener();
502     ASSERT(!listenersValue.IsUndefined());
503     JSHandle<ChangeListener> listeners(thread, listenersValue.GetTaggedObject());
504     ASSERT(listeners->Get(index) == jshclass.GetTaggedValue());
505     listeners->Delete(thread, index);
506     return true;
507 }
508 
GetProtoChangeDetails(const JSThread * thread,const JSHandle<JSHClass> & jshclass)509 JSHandle<ProtoChangeDetails> JSHClass::GetProtoChangeDetails(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
510 {
511     JSTaggedValue protoDetails = jshclass->GetProtoChangeDetails();
512     if (protoDetails.IsProtoChangeDetails()) {
513         return JSHandle<ProtoChangeDetails>(thread, protoDetails);
514     }
515     JSHandle<ProtoChangeDetails> protoDetailsHandle = thread->GetEcmaVM()->GetFactory()->NewProtoChangeDetails();
516     jshclass->SetProtoChangeDetails(thread, protoDetailsHandle.GetTaggedValue());
517     return protoDetailsHandle;
518 }
519 
GetProtoChangeDetails(const JSThread * thread,const JSHandle<JSObject> & obj)520 JSHandle<ProtoChangeDetails> JSHClass::GetProtoChangeDetails(const JSThread *thread, const JSHandle<JSObject> &obj)
521 {
522     JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
523     return GetProtoChangeDetails(thread, jshclass);
524 }
525 
NoticeRegisteredUser(const JSThread * thread,const JSHandle<JSHClass> & jshclass)526 void JSHClass::NoticeRegisteredUser([[maybe_unused]] const JSThread *thread, const JSHandle<JSHClass> &jshclass)
527 {
528     ASSERT(jshclass->IsPrototype());
529     JSTaggedValue markerValue = jshclass->GetProtoChangeMarker();
530     if (markerValue.IsProtoChangeMarker()) {
531         ProtoChangeMarker *protoChangeMarker = ProtoChangeMarker::Cast(markerValue.GetTaggedObject());
532         protoChangeMarker->SetHasChanged(true);
533     }
534 }
535 
NoticeThroughChain(const JSThread * thread,const JSHandle<JSHClass> & jshclass)536 void JSHClass::NoticeThroughChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
537 {
538     NoticeRegisteredUser(thread, jshclass);
539     JSTaggedValue protoDetailsValue = jshclass->GetProtoChangeDetails();
540     if (!protoDetailsValue.IsProtoChangeDetails()) {
541         return;
542     }
543     JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener();
544     if (!listenersValue.IsTaggedArray()) {
545         return;
546     }
547     ChangeListener *listeners = ChangeListener::Cast(listenersValue.GetTaggedObject());
548     for (uint32_t i = 0; i < listeners->GetEnd(); i++) {
549         JSTaggedValue temp = listeners->Get(i);
550         if (temp.IsJSHClass()) {
551             NoticeThroughChain(thread, JSHandle<JSHClass>(thread, listeners->Get(i).GetTaggedObject()));
552         }
553     }
554 }
555 
RefreshUsers(const JSThread * thread,const JSHandle<JSHClass> & oldHclass,const JSHandle<JSHClass> & newHclass)556 void JSHClass::RefreshUsers(const JSThread *thread, const JSHandle<JSHClass> &oldHclass,
557                             const JSHandle<JSHClass> &newHclass)
558 {
559     ASSERT(oldHclass->IsPrototype());
560     ASSERT(newHclass->IsPrototype());
561     bool onceRegistered = UnregisterOnProtoChain(thread, oldHclass);
562 
563     newHclass->SetProtoChangeDetails(thread, oldHclass->GetProtoChangeDetails());
564     oldHclass->SetProtoChangeDetails(thread, JSTaggedValue::Undefined());
565     if (onceRegistered) {
566         if (newHclass->GetProtoChangeDetails().IsProtoChangeDetails()) {
567             ProtoChangeDetails::Cast(newHclass->GetProtoChangeDetails().GetTaggedObject())
568                 ->SetRegisterIndex(ProtoChangeDetails::UNREGISTERED);
569         }
570         RegisterOnProtoChain(thread, newHclass);
571     }
572 }
573 }  // namespace panda::ecmascript
574