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