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