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, ®isterIndex);
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