1 /*
2 * Copyright (c) 2021-2024 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 #ifndef ECMASCRIPT_JS_HCLASS_INL_H
17 #define ECMASCRIPT_JS_HCLASS_INL_H
18
19 #include "ecmascript/js_hclass.h"
20
21 #include "ecmascript/byte_array.h"
22 #include "ecmascript/ic/proto_change_details.h"
23 #include "ecmascript/js_bigint.h"
24 #include "ecmascript/layout_info.h"
25 #include "ecmascript/layout_info-inl.h"
26 #include "ecmascript/mem/assert_scope.h"
27 #include "ecmascript/transitions_dictionary.h"
28
29 namespace panda::ecmascript {
30
ProtoIsFastJSArray(const JSThread * thread,const JSHandle<JSTaggedValue> proto,const JSHandle<JSHClass> hclass)31 bool JSHClass::ProtoIsFastJSArray(const JSThread *thread, const JSHandle<JSTaggedValue> proto,
32 const JSHandle<JSHClass> hclass)
33 {
34 // Since we currently only support ElementsKind for JSArray initial hclass,
35 // if an object's hclass has a non-generic ElementsKind, it must be one of the JSArray initial hclass.
36 // if an object's hclass has a Generic ElementsKind, it might be the JSArray initial generic elementskind hclass,
37 // which therefore needs further hclass comparison.
38 if (proto->IsJSArray()) {
39 JSTaggedValue genericArrayHClass = thread->GlobalConstants()->GetElementHoleTaggedClass();
40 if (!Elements::IsGeneric(hclass->GetElementsKind()) || hclass.GetTaggedValue() == genericArrayHClass) {
41 return true;
42 }
43 }
44 return false;
45 }
46
AddTransitions(const JSThread * thread,const JSHandle<JSHClass> & parent,const JSHandle<JSHClass> & child,const JSHandle<JSTaggedValue> & key,PropertyAttributes attributes)47 void JSHClass::AddTransitions(const JSThread *thread, const JSHandle<JSHClass> &parent, const JSHandle<JSHClass> &child,
48 const JSHandle<JSTaggedValue> &key, PropertyAttributes attributes)
49 {
50 UpdateRootHClass(thread, parent, child);
51 JSTaggedValue transitions = parent->GetTransitions();
52 if (transitions.IsUndefined()) {
53 JSTaggedValue weakChild = JSTaggedValue(child.GetTaggedValue().CreateAndGetWeakRef());
54 parent->SetTransitions(thread, weakChild);
55 return;
56 }
57 JSMutableHandle<TransitionsDictionary> dict(thread, JSTaggedValue::Undefined());
58 if (transitions.IsWeak()) {
59 auto cachedHClass = JSHClass::Cast(transitions.GetTaggedWeakRef());
60 if (cachedHClass->HasProps()) {
61 uint32_t last = cachedHClass->LastPropIndex();
62 LayoutInfo* layoutInfo = LayoutInfo::Cast(cachedHClass->GetLayout().GetTaggedObject());
63 auto metaData = JSHandle<JSTaggedValue>(thread,
64 JSTaggedValue(layoutInfo->GetAttr(last).GetPropertyMetaData()));
65 auto lastKey = JSHandle<JSTaggedValue>(thread, layoutInfo->GetKey(last));
66 auto lastHClass = JSHandle<JSTaggedValue>(thread, cachedHClass);
67 dict.Update(TransitionsDictionary::Create(thread));
68 transitions = TransitionsDictionary::PutIfAbsent(thread, dict, lastKey, lastHClass,
69 metaData).GetTaggedValue();
70 }
71 }
72 auto metaData = JSHandle<JSTaggedValue>(thread, JSTaggedValue(attributes.GetPropertyMetaData()));
73 dict.Update(transitions);
74 transitions = TransitionsDictionary::PutIfAbsent(thread, dict, key, JSHandle<JSTaggedValue>(child),
75 metaData).GetTaggedValue();
76 parent->SetTransitions(thread, transitions);
77 }
78
AddExtensionTransitions(const JSThread * thread,const JSHandle<JSHClass> & parent,const JSHandle<JSHClass> & child,const JSHandle<JSTaggedValue> & key)79 void JSHClass::AddExtensionTransitions(const JSThread *thread, const JSHandle<JSHClass> &parent,
80 const JSHandle<JSHClass> &child, const JSHandle<JSTaggedValue> &key)
81 {
82 auto attr = JSHandle<JSTaggedValue>(thread, PropertyAttributes(0).GetTaggedValue());
83 AddProtoTransitions(thread, parent, child, key, attr);
84 }
85
AddProtoTransitions(const JSThread * thread,const JSHandle<JSHClass> & parent,const JSHandle<JSHClass> & child,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & proto)86 void JSHClass::AddProtoTransitions(const JSThread *thread, const JSHandle<JSHClass> &parent,
87 const JSHandle<JSHClass> &child, const JSHandle<JSTaggedValue> &key,
88 const JSHandle<JSTaggedValue> &proto)
89 {
90 ALLOW_LOCAL_TO_SHARE_WEAK_REF_HANDLE;
91 UpdateRootHClass(thread, parent, child);
92 JSTaggedValue transitions = parent->GetTransitions();
93 JSMutableHandle<TransitionsDictionary> dict(thread, JSTaggedValue::Undefined());
94 if (transitions.IsUndefined()) {
95 transitions = TransitionsDictionary::Create(thread).GetTaggedValue();
96 } else if (transitions.IsWeak()) {
97 auto cachedHClass = JSHClass::Cast(transitions.GetTaggedWeakRef());
98 if (cachedHClass->HasProps()) {
99 uint32_t last = cachedHClass->LastPropIndex();
100 LayoutInfo* layoutInfo = LayoutInfo::Cast(cachedHClass->GetLayout().GetTaggedObject());
101 auto metaData = JSHandle<JSTaggedValue>(thread,
102 JSTaggedValue(layoutInfo->GetAttr(last).GetPropertyMetaData()));
103 auto lastKey = JSHandle<JSTaggedValue>(thread, layoutInfo->GetKey(last));
104 auto lastHClass = JSHandle<JSTaggedValue>(thread, cachedHClass);
105 dict.Update(TransitionsDictionary::Create(thread));
106 transitions = TransitionsDictionary::PutIfAbsent(thread, dict, lastKey, lastHClass,
107 metaData).GetTaggedValue();
108 }
109 }
110 dict.Update(transitions);
111 transitions =
112 TransitionsDictionary::PutIfAbsent(thread, dict, key, JSHandle<JSTaggedValue>(child), proto).GetTaggedValue();
113 parent->SetTransitions(thread, transitions);
114 }
115
FindTransitions(const JSTaggedValue & key,const JSTaggedValue & metaData,const Representation & rep)116 inline JSHClass *JSHClass::FindTransitions(const JSTaggedValue &key, const JSTaggedValue &metaData,
117 const Representation &rep)
118 {
119 DISALLOW_GARBAGE_COLLECTION;
120 JSTaggedValue transitions = GetTransitions();
121 if (transitions.IsUndefined()) {
122 return nullptr;
123 }
124 if (transitions.IsWeak()) {
125 auto cachedHClass = JSHClass::Cast(transitions.GetTaggedWeakRef());
126 if (cachedHClass->PropsIsEmpty()) {
127 return nullptr;
128 }
129 int last = static_cast<int>(cachedHClass->LastPropIndex());
130 LayoutInfo *layoutInfo = LayoutInfo::Cast(cachedHClass->GetLayout().GetTaggedObject());
131 auto lastMetaData = layoutInfo->GetAttr(last).GetPropertyMetaData();
132 auto lastKey = layoutInfo->GetKey(last);
133 if (lastMetaData == metaData.GetInt() && key == lastKey) {
134 return CheckHClassForRep(cachedHClass, rep);
135 }
136 return nullptr;
137 }
138
139 ASSERT(transitions.IsTaggedArray());
140 TransitionsDictionary *dict = TransitionsDictionary::Cast(transitions.GetTaggedObject());
141 auto entry = dict->FindEntry(key, metaData);
142 if (entry == -1) {
143 return nullptr;
144 }
145
146 JSTaggedValue ret = dict->GetValue(entry);
147 if (ret.IsUndefined()) {
148 return nullptr;
149 }
150
151 return CheckHClassForRep(JSHClass::Cast(ret.GetTaggedWeakRef()), rep);
152 }
153
FindProtoTransitions(const JSTaggedValue & key,const JSTaggedValue & proto)154 inline JSHClass *JSHClass::FindProtoTransitions(const JSTaggedValue &key, const JSTaggedValue &proto)
155 {
156 DISALLOW_GARBAGE_COLLECTION;
157 JSTaggedValue transitions = GetTransitions();
158 if (transitions.IsWeak() || !transitions.IsTaggedArray()) {
159 ASSERT(transitions.IsUndefined() || transitions.IsWeak());
160 return nullptr;
161 }
162 ASSERT(transitions.IsTaggedArray());
163 TransitionsDictionary *dict = TransitionsDictionary::Cast(transitions.GetTaggedObject());
164 auto entry = dict->FindEntry(key, proto);
165 if (entry == -1) {
166 return nullptr;
167 }
168
169 JSTaggedValue ret = dict->GetValue(entry);
170 if (ret.IsUndefined()) {
171 return nullptr;
172 }
173
174 return JSHClass::Cast(ret.GetTaggedWeakRef());
175 }
176
RestoreElementsKindToGeneric(JSHClass * newJsHClass)177 inline void JSHClass::RestoreElementsKindToGeneric(JSHClass *newJsHClass)
178 {
179 newJsHClass->SetElementsKind(ElementsKind::GENERIC);
180 }
181
CheckHClassForRep(JSHClass * hclass,const Representation & rep)182 inline JSHClass *JSHClass::CheckHClassForRep(JSHClass *hclass, const Representation &rep)
183 {
184 if (!hclass->IsAOT()) {
185 return hclass;
186 }
187 if (rep == Representation::NONE) {
188 return hclass;
189 }
190
191 int last = static_cast<int>(hclass->LastPropIndex());
192 LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
193 auto lastRep = layoutInfo->GetAttr(last).GetRepresentation();
194 auto result = hclass;
195 if (lastRep == Representation::INT) {
196 if (rep != Representation::INT) {
197 result = nullptr;
198 }
199 } else if (lastRep == Representation::DOUBLE) {
200 if (rep != Representation::INT && rep != Representation::DOUBLE) {
201 result = nullptr;
202 }
203 }
204 return result;
205 }
206
UpdatePropertyMetaData(const JSThread * thread,const JSTaggedValue & key,const PropertyAttributes & metaData)207 inline void JSHClass::UpdatePropertyMetaData(const JSThread *thread, [[maybe_unused]] const JSTaggedValue &key,
208 const PropertyAttributes &metaData)
209 {
210 DISALLOW_GARBAGE_COLLECTION;
211 ASSERT(!GetLayout().IsNull());
212 LayoutInfo *layoutInfo = LayoutInfo::Cast(GetLayout().GetTaggedObject());
213 ASSERT(layoutInfo->GetLength() != 0);
214 uint32_t entry = metaData.GetOffset();
215
216 layoutInfo->SetNormalAttr(thread, entry, metaData);
217 }
218
HasReferenceField()219 inline bool JSHClass::HasReferenceField()
220 {
221 auto type = GetObjectType();
222 switch (type) {
223 case JSType::LINE_STRING:
224 case JSType::CONSTANT_STRING:
225 case JSType::JS_NATIVE_POINTER:
226 return false;
227 default:
228 return true;
229 }
230 }
231
SizeFromJSHClass(TaggedObject * header)232 inline size_t JSHClass::SizeFromJSHClass(TaggedObject *header)
233 {
234 // CAUTION! Never use T::Cast(header) in this function
235 // it would cause issue during GC because hclass may forward to a new addres
236 // and the casting method would still use the old address.
237 auto type = GetObjectType();
238 size_t size = 0;
239 switch (type) {
240 case JSType::TAGGED_ARRAY:
241 case JSType::TAGGED_DICTIONARY:
242 case JSType::LEXICAL_ENV:
243 case JSType::SENDABLE_ENV:
244 case JSType::CONSTANT_POOL:
245 case JSType::AOT_LITERAL_INFO:
246 case JSType::VTABLE:
247 case JSType::COW_TAGGED_ARRAY:
248 case JSType::MUTANT_TAGGED_ARRAY:
249 case JSType::COW_MUTANT_TAGGED_ARRAY:
250 case JSType::PROFILE_TYPE_INFO:
251 size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(),
252 reinterpret_cast<TaggedArray *>(header)->GetLength());
253 break;
254 case JSType::BYTE_ARRAY:
255 size = ByteArray::ComputeSize(reinterpret_cast<ByteArray *>(header)->GetByteLength(),
256 reinterpret_cast<ByteArray *>(header)->GetArrayLength());
257 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
258 break;
259 case JSType::LINE_STRING:
260 size = LineEcmaString::ObjectSize(reinterpret_cast<EcmaString* >(header));
261 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
262 break;
263 case JSType::CONSTANT_STRING:
264 size = ConstantString::SIZE;
265 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
266 break;
267 case JSType::TREE_STRING:
268 size = TreeEcmaString::SIZE;
269 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
270 break;
271 case JSType::SLICED_STRING:
272 size = SlicedString::SIZE;
273 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
274 break;
275 case JSType::MACHINE_CODE_OBJECT:
276 size = reinterpret_cast<MachineCode *>(header)->GetMachineCodeObjectSize();
277 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
278 break;
279 case JSType::BIGINT:
280 size = BigInt::ComputeSize(reinterpret_cast<BigInt *>(header)->GetLength());
281 size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
282 break;
283 default:
284 ASSERT(GetObjectSize() != 0);
285 size = GetObjectSize();
286 break;
287 }
288 ASSERT(AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT)) == size);
289 return size;
290 }
291
Copy(const JSThread * thread,const JSHClass * jshclass)292 inline void JSHClass::Copy(const JSThread *thread, const JSHClass *jshclass)
293 {
294 DISALLOW_GARBAGE_COLLECTION;
295
296 // copy jshclass
297 SetPrototype(thread, jshclass->GetPrototype());
298 SetBitField(jshclass->GetBitField());
299 SetIsAllTaggedProp(jshclass->IsAllTaggedProp());
300 SetNumberOfProps(jshclass->NumberOfProps());
301 }
302
FindRootHClass(JSHClass * hclass)303 inline JSHClass *JSHClass::FindRootHClass(JSHClass *hclass)
304 {
305 auto root = hclass;
306 while (!ProfileType(root->GetProfileType()).IsRootType()) {
307 auto parent = root->GetParent();
308 if (!parent.IsJSHClass()) {
309 break;
310 }
311 root = JSHClass::Cast(parent.GetTaggedObject());
312 }
313 return root;
314 }
315
FindProtoHClass(JSHClass * hclass)316 inline JSTaggedValue JSHClass::FindProtoHClass(JSHClass *hclass)
317 {
318 auto proto = hclass->GetProto();
319 if (proto.IsJSObject()) {
320 auto prototypeObj = JSObject::Cast(proto);
321 return JSTaggedValue(prototypeObj->GetClass());
322 }
323 return JSTaggedValue::Undefined();
324 }
325
FindProtoRootHClass(JSHClass * hclass)326 inline JSTaggedValue JSHClass::FindProtoRootHClass(JSHClass *hclass)
327 {
328 auto proto = hclass->GetProto();
329 if (proto.IsJSObject()) {
330 auto prototypeObj = JSObject::Cast(proto);
331 auto prototypeHClass = prototypeObj->GetClass();
332 return JSTaggedValue(JSHClass::FindRootHClass(prototypeHClass));
333 }
334 return JSTaggedValue::Undefined();
335 }
336
UpdateRootHClass(const JSThread * thread,const JSHandle<JSHClass> & parent,const JSHandle<JSHClass> & child)337 inline void JSHClass::UpdateRootHClass(const JSThread *thread, const JSHandle<JSHClass> &parent,
338 const JSHandle<JSHClass> &child)
339 {
340 if (thread->GetEcmaVM()->IsEnablePGOProfiler()) {
341 child->SetParent(thread, parent);
342 }
343 }
344
FindPropertyEntry(const JSThread * thread,JSHClass * hclass,JSTaggedValue key)345 inline int JSHClass::FindPropertyEntry(const JSThread *thread, JSHClass *hclass, JSTaggedValue key)
346 {
347 DISALLOW_GARBAGE_COLLECTION;
348 LayoutInfo *layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
349 uint32_t propsNumber = hclass->NumberOfProps();
350 int entry = layout->FindElementWithCache(thread, hclass, key, propsNumber);
351 return entry;
352 }
353
CompleteObjSizeTracking()354 inline void JSHClass::CompleteObjSizeTracking()
355 {
356 if (!IsObjSizeTrackingInProgress()) {
357 return;
358 }
359 uint32_t finalInObjPropsNum = JSHClass::VisitTransitionAndFindMaxNumOfProps(this);
360 if (finalInObjPropsNum < GetInlinedProperties()) {
361 // UpdateObjSize with finalInObjPropsNum
362 JSHClass::VisitTransitionAndUpdateObjSize(this, finalInObjPropsNum);
363 }
364 SetConstructionCounter(0); // fini ObjSizeTracking
365 }
366
ObjSizeTrackingStep()367 inline void JSHClass::ObjSizeTrackingStep()
368 {
369 if (!IsObjSizeTrackingInProgress()) {
370 return;
371 }
372 uint32_t constructionCounter = GetConstructionCounter();
373 ASSERT(constructionCounter != 0);
374 SetConstructionCounter(--constructionCounter);
375 if (constructionCounter == 0) {
376 uint32_t finalInObjPropsNum = JSHClass::VisitTransitionAndFindMaxNumOfProps(this);
377 if (finalInObjPropsNum < GetInlinedProperties()) {
378 // UpdateObjSize with finalInObjPropsNum
379 JSHClass::VisitTransitionAndUpdateObjSize(this, finalInObjPropsNum);
380 }
381 }
382 }
383
384 template<bool isOnlyIncludeNotFound>
MarkProtoChanged(const JSThread * thread,const JSHandle<JSHClass> & jshclass)385 void JSHClass::MarkProtoChanged([[maybe_unused]] const JSThread *thread, const JSHandle<JSHClass> &jshclass)
386 {
387 DISALLOW_GARBAGE_COLLECTION;
388 ASSERT(jshclass->IsPrototype());
389 JSTaggedValue markerValue = jshclass->GetProtoChangeMarker();
390 if (markerValue.IsProtoChangeMarker()) {
391 ProtoChangeMarker *protoChangeMarker = ProtoChangeMarker::Cast(markerValue.GetTaggedObject());
392 if constexpr (isOnlyIncludeNotFound) {
393 protoChangeMarker->SetNotFoundHasChanged(true);
394 } else {
395 protoChangeMarker->SetHasChanged(true);
396 }
397 }
398 }
399
400 template<bool isOnlyIncludeNotFound /* = false*/>
NoticeThroughChain(const JSThread * thread,const JSHandle<JSHClass> & jshclass,JSTaggedValue addedKey)401 void JSHClass::NoticeThroughChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
402 JSTaggedValue addedKey)
403 {
404 DISALLOW_GARBAGE_COLLECTION;
405 MarkProtoChanged<isOnlyIncludeNotFound>(thread, jshclass);
406 JSTaggedValue protoDetailsValue = jshclass->GetProtoChangeDetails();
407 if (!protoDetailsValue.IsProtoChangeDetails()) {
408 return;
409 }
410 JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener();
411 if (!listenersValue.IsTaggedArray()) {
412 return;
413 }
414 ChangeListener *listeners = ChangeListener::Cast(listenersValue.GetTaggedObject());
415 for (uint32_t i = 0; i < listeners->GetEnd(); i++) {
416 JSTaggedValue temp = listeners->Get(i);
417 if (temp.IsJSHClass()) {
418 NoticeThroughChain<isOnlyIncludeNotFound>(thread,
419 JSHandle<JSHClass>(thread, listeners->Get(i).GetTaggedObject()), addedKey);
420 }
421 }
422 }
423
424 template<bool checkDuplicateKeys /* = false*/>
AddPropertyToNewHClass(const JSThread * thread,JSHandle<JSHClass> & jshclass,JSHandle<JSHClass> & newJsHClass,const JSHandle<JSTaggedValue> & key,const PropertyAttributes & attr)425 void JSHClass::AddPropertyToNewHClass(const JSThread *thread, JSHandle<JSHClass> &jshclass,
426 JSHandle<JSHClass> &newJsHClass,
427 const JSHandle<JSTaggedValue> &key,
428 const PropertyAttributes &attr)
429 {
430 ASSERT(!jshclass->IsDictionaryMode());
431 ASSERT(!newJsHClass->IsDictionaryMode());
432 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
433 // Add Property and metaData
434 uint32_t offset = attr.GetOffset();
435 newJsHClass->IncNumberOfProps();
436
437 {
438 JSMutableHandle<LayoutInfo> layoutInfoHandle(thread, newJsHClass->GetLayout());
439
440 if (layoutInfoHandle->NumberOfElements() != static_cast<int>(offset)) {
441 layoutInfoHandle.Update(factory->CopyAndReSort(layoutInfoHandle, offset, offset + 1));
442 } else if (layoutInfoHandle->GetPropertiesCapacity() <= static_cast<int>(offset)) { // need to Grow
443 layoutInfoHandle.Update(
444 factory->ExtendLayoutInfo(layoutInfoHandle, offset));
445 }
446 newJsHClass->SetLayout(thread, layoutInfoHandle);
447 layoutInfoHandle->AddKey<checkDuplicateKeys>(thread, offset, key.GetTaggedValue(), attr);
448 }
449
450 // Add newClass to old hclass's transitions.
451 AddTransitions(thread, jshclass, newJsHClass, key, attr);
452
453 if UNLIKELY(key.GetTaggedValue() == thread->GlobalConstants()->GetConstructorString()
454 && (jshclass->IsJSArray() || jshclass->IsTypedArray())) {
455 newJsHClass->SetHasConstructor(true);
456 }
457 }
458
459 template<bool checkDuplicateKeys /* = false*/>
SetPropertyOfObjHClass(const JSThread * thread,JSHandle<JSHClass> & jshclass,const JSHandle<JSTaggedValue> & key,const PropertyAttributes & attr,const Representation & rep,bool withInlinedProperties,uint32_t numInlinedProps)460 JSHandle<JSHClass> JSHClass::SetPropertyOfObjHClass(const JSThread *thread, JSHandle<JSHClass> &jshclass,
461 const JSHandle<JSTaggedValue> &key,
462 const PropertyAttributes &attr, const Representation &rep,
463 bool withInlinedProperties, uint32_t numInlinedProps)
464 {
465 JSHClass *newClass = jshclass->FindTransitions(
466 key.GetTaggedValue(), JSTaggedValue(attr.GetPropertyMetaData()), rep);
467 if (newClass != nullptr) {
468 newClass->SetPrototype(thread, jshclass->GetPrototype());
469 return JSHandle<JSHClass>(thread, newClass);
470 }
471
472 JSHandle<JSHClass> newJsHClass = JSHClass::Clone(thread, jshclass, withInlinedProperties, numInlinedProps);
473 AddPropertyToNewHClass<checkDuplicateKeys>(thread, jshclass, newJsHClass, key, attr);
474 return newJsHClass;
475 }
476 } // namespace panda::ecmascript
477
478 #endif // ECMASCRIPT_JS_HCLASS_INL_H
479