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