• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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/global_env.h"
17 #include "ecmascript/js_function.h"
18 #include "ecmascript/js_object-inl.h"
19 #include "ecmascript/layout_info.h"
20 #include "ecmascript/napi/include/jsnapi_expo.h"
21 #include "ecmascript/napi/jsnapi_class_creation_helper.h"
22 #include "ecmascript/napi/jsnapi_helper.h"
23 #include "ecmascript/object_factory.h"
24 
25 namespace panda::ecmascript {
ConstructDescByAttr(const JSThread * thread,const PropertyAttribute & attr,PropertyDescriptor * desc)26 void JSNApiClassCreationHelper::ConstructDescByAttr(const JSThread *thread, const PropertyAttribute &attr,
27                                                     PropertyDescriptor *desc)
28 {
29     new (desc) PropertyDescriptor(thread, JSNApiHelper::ToJSHandle(attr.GetValue(thread->GetEcmaVM())),
30                                   attr.IsWritable(), attr.IsEnumerable(), attr.IsConfigurable());
31 }
32 
DestructDesc(PropertyDescriptor * desc)33 void JSNApiClassCreationHelper::DestructDesc(PropertyDescriptor *desc)
34 {
35     desc->~PropertyDescriptor();
36 }
37 
DestructAttr(PropertyAttribute * attr)38 void JSNApiClassCreationHelper::DestructAttr(PropertyAttribute *attr)
39 {
40     // Call ~PropertyAttribute directly for attrs from napi_define_class, which were construct by using placement new.
41     attr->~PropertyAttribute();
42 }
43 
TryAddOriKeyAndOriAttrToHClass(const JSThread * thread,const Local<JSValueRef> & oriKey,const PropertyAttribute & oriAttr,PropertyDescriptor & desc,size_t & attrOffset,JSHandle<JSHClass> & hClass)44 bool JSNApiClassCreationHelper::TryAddOriKeyAndOriAttrToHClass(const JSThread *thread, const Local<JSValueRef> &oriKey,
45                                                                const PropertyAttribute &oriAttr,
46                                                                PropertyDescriptor &desc, size_t &attrOffset,
47                                                                JSHandle<JSHClass> &hClass)
48 {
49     // If return false, it means that property must be add by slow path(DefinePropertyOrThrow).
50     JSMutableHandle<JSTaggedValue> key(JSNApiHelper::ToJSMutableHandle(oriKey));
51     JSTaggedValue keyValue = key.GetTaggedValue();
52     EcmaVM *vm = thread->GetEcmaVM();
53     if (key->IsString() && !EcmaStringAccessor(keyValue).IsInternString()) {
54         // update string stable
55         key.Update(JSTaggedValue(vm->GetFactory()->InternString(key)));
56     }
57     ConstructDescByAttr(thread, oriAttr, &desc);
58     // If property key is element index, property must be add by slow path(DefinePropertyOrThrow).
59     if (UNLIKELY(!JSTaggedValue::IsPureString(const_cast<JSThread *>(thread), keyValue))) {
60         return false;
61     }
62     // If property key is repeat key, property must be add by slow path.
63     JSHandle<ecmascript::LayoutInfo> layoutInfoHandle(thread, hClass->GetLayout(thread));
64     if (UNLIKELY(layoutInfoHandle->CheckIsDuplicateKey(thread, attrOffset, key->GetKeyHashCode(thread), keyValue))) {
65         return false;
66     }
67     ASSERT(static_cast<int32_t>(attrOffset) == hClass->GetNextInlinedPropsIndex());
68     JSHClass::AddInlinedPropToHClass(thread, desc, attrOffset++, key, hClass);
69     return true;
70 }
71 
NewClassFuncProtoWithProperties(JSThread * thread,const JSHandle<JSFunction> & func,size_t propertyCount,size_t nonStaticPropCount,const Local<JSValueRef> * keys,PropertyAttribute * attrs,PropertyDescriptor * descs)72 JSHandle<JSObject> JSNApiClassCreationHelper::NewClassFuncProtoWithProperties(
73     JSThread *thread, const JSHandle<JSFunction> &func, size_t propertyCount, size_t nonStaticPropCount,
74     const Local<JSValueRef> *keys, PropertyAttribute *attrs, PropertyDescriptor *descs)
75 {
76     ASSERT(propertyCount >= nonStaticPropCount);
77     JSHandle<GlobalEnv> env = thread->GetGlobalEnv();
78     size_t inlNonStaticPropCount =
79         std::min(nonStaticPropCount, static_cast<size_t>(ecmascript::PropertyAttributes::MAX_FAST_PROPS_CAPACITY));
80     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
81     JSHandle<JSHClass> classPrototypeHClass = factory->CreateClassFuncProtoHClass(thread, inlNonStaticPropCount);
82     // Step1: create hClass of protoOrHClass in class function
83     // Set "constructor" in prototype
84     PropertyDescriptor ctorDesc(thread, JSHandle<JSTaggedValue>::Cast(func), true, false, true);
85     ASSERT(classPrototypeHClass->GetNextInlinedPropsIndex() != -1);
86     const size_t startInlPropIdx = static_cast<size_t>(classPrototypeHClass->GetNextInlinedPropsIndex());
87     JSHandle<JSTaggedValue> ctorKey = thread->GlobalConstants()->GetHandledConstructorString();
88     JSHClass::AddInlinedPropToHClass(thread, ctorDesc, startInlPropIdx, ctorKey, classPrototypeHClass);
89     // Set inlined property slot of non-static properties
90     const size_t nonStaticPropStartIdx = propertyCount - 1; // 1: array length of keys and attrs is propertyCount
91     std::bitset<ecmascript::PropertyAttributes::MAX_FAST_PROPS_CAPACITY> propTags;
92     if (inlNonStaticPropCount > 0) {
93         ASSERT(propertyCount >= inlNonStaticPropCount);
94         size_t nextInlinedPropIdx = startInlPropIdx + 1; // 1: constructor attr set first
95         // Properties in keys and attrs are stored in the following way:
96         // +------------------+ ... + -------------------------------------+ ... +---------------------+
97         // | first staticProp | ... | last staticProp | last nonStaticProp | ... | first nonStaticProp |
98         // +------------------+ ... + -------------------------------------+ ... +---------------------+
99         for (size_t i = 0; i < inlNonStaticPropCount; ++i) {
100             size_t curNonStaticPropIdx = nonStaticPropStartIdx - i;
101             propTags[curNonStaticPropIdx] =
102                 TryAddOriKeyAndOriAttrToHClass(thread, keys[curNonStaticPropIdx], attrs[curNonStaticPropIdx],
103                                                descs[curNonStaticPropIdx], nextInlinedPropIdx, classPrototypeHClass);
104         }
105     }
106     // Step2: create protoOrHClass object in terms of the created hclass
107     JSHandle<JSObject> classPrototype = factory->NewJSObjectWithInit(classPrototypeHClass);
108     // Step3: set values of inlined properties
109     size_t curInlPropIdx = startInlPropIdx;
110     classPrototype->SetPropertyInlinedProps<true>(thread, curInlPropIdx++, ctorDesc.GetValue().GetTaggedValue());
111     if (inlNonStaticPropCount > 0) {
112         JSHandle<JSTaggedValue> classPrototypeHandle(classPrototype);
113         for (size_t i = 0; i < inlNonStaticPropCount; ++i) {
114             size_t curNonStaticPropIdx = nonStaticPropStartIdx - i;
115             if (propTags[curNonStaticPropIdx]) {
116                 classPrototype->SetPropertyInlinedProps<true>(thread, curInlPropIdx++,
117                                                               descs[curNonStaticPropIdx].GetValue().GetTaggedValue());
118             } else {
119                 JSTaggedValue::DefinePropertyOrThrow(thread, classPrototypeHandle,
120                                                      JSNApiHelper::ToJSMutableHandle(keys[curNonStaticPropIdx]),
121                                                      descs[curNonStaticPropIdx]);
122             }
123             // Corresponds to ConstructDescByAttr in TryAddOriKeyAndOriAttrToHClass
124             DestructDesc(&descs[curNonStaticPropIdx]);
125             DestructAttr(&attrs[curNonStaticPropIdx]);
126         }
127     }
128     // Add non-static properties that out bound of MAX_FAST_PROPS_CAPACITY
129     if (nonStaticPropCount > ecmascript::PropertyAttributes::MAX_FAST_PROPS_CAPACITY) {
130         JSHandle<JSTaggedValue> classPrototypeHandle(classPrototype);
131         for (size_t i = inlNonStaticPropCount; i < nonStaticPropCount; ++i) {
132             size_t curNonStaticPropIdx = nonStaticPropStartIdx - i;
133             ConstructDescByAttr(thread, attrs[curNonStaticPropIdx], &descs[curNonStaticPropIdx]);
134             JSTaggedValue::DefinePropertyOrThrow(thread, classPrototypeHandle,
135                                                  JSNApiHelper::ToJSMutableHandle(keys[curNonStaticPropIdx]),
136                                                  descs[curNonStaticPropIdx]);
137             DestructDesc(&descs[curNonStaticPropIdx]);
138             DestructAttr(&attrs[curNonStaticPropIdx]);
139         }
140     }
141     return classPrototype;
142 }
143 
CreateClassFuncWithProperties(JSThread * thread,const char * name,InternalFunctionCallback nativeFunc,bool callNapi,size_t propertyCount,size_t staticPropCount,const Local<JSValueRef> * keys,PropertyAttribute * attrs,PropertyDescriptor * descs)144 JSHandle<JSFunction> JSNApiClassCreationHelper::CreateClassFuncWithProperties(
145     JSThread *thread, const char *name, InternalFunctionCallback nativeFunc, bool callNapi, size_t propertyCount,
146     size_t staticPropCount, const Local<JSValueRef> *keys, PropertyAttribute *attrs, PropertyDescriptor *descs)
147 {
148     ASSERT(propertyCount >= staticPropCount);
149     size_t inlinedStaticPropCount =
150         std::min(staticPropCount, static_cast<size_t>(ecmascript::PropertyAttributes::MAX_FAST_PROPS_CAPACITY));
151     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
152     JSHandle<JSHClass> functionClass = factory->CreateClassFuncHClass(thread, inlinedStaticPropCount);
153     // Step1: create hClass of class function
154     // Set name of class function
155     JSHandle<JSTaggedValue> functionName(factory->NewFromUtf8(name));
156     PropertyDescriptor nameDesc(thread, functionName, false, false, true);
157     ASSERT(functionClass->GetNextInlinedPropsIndex() != -1);
158     const size_t startInlPropIdx = static_cast<size_t>(functionClass->GetNextInlinedPropsIndex());
159     JSHandle<JSTaggedValue> nameKey = thread->GlobalConstants()->GetHandledNameString();
160     JSHClass::AddInlinedPropToHClass(thread, nameDesc, startInlPropIdx, nameKey, functionClass);
161     // Set static properties of class function
162     std::bitset<ecmascript::PropertyAttributes::MAX_FAST_PROPS_CAPACITY> propTags;
163     size_t nextInlinedPropIdx = startInlPropIdx + 1; // 1: first static property was set after class function name
164     // Properties in keys and attrs are stored in the following way:
165     // +------------------+ ... + -------------------------------------+ ... +---------------------+
166     // | first staticProp | ... | last staticProp | last nonStaticProp | ... | first nonStaticProp |
167     // +------------------+ ... + -------------------------------------+ ... +---------------------+
168     for (size_t i = 0; i < inlinedStaticPropCount; ++i) {
169         propTags[i] =
170             TryAddOriKeyAndOriAttrToHClass(thread, keys[i], attrs[i], descs[i], nextInlinedPropIdx, functionClass);
171     }
172     // Step2: create object of class function
173     JSHandle<JSFunction> classFunc = factory->NewJSFunctionByHClass(reinterpret_cast<void *>(nativeFunc), functionClass,
174                                                                     ecmascript::FunctionKind::CLASS_CONSTRUCTOR);
175     // Step3: set values of inlined properties
176     size_t curInlPropIdx = startInlPropIdx;
177     classFunc->SetPropertyInlinedProps<true>(thread, curInlPropIdx++, nameDesc.GetValue().GetTaggedValue());
178     JSHandle<JSTaggedValue> classFuncHandle(classFunc);
179     for (size_t i = 0; i < inlinedStaticPropCount; ++i) {
180         if (propTags[i]) {
181             classFunc->SetPropertyInlinedProps<true>(thread, curInlPropIdx++,
182                                                      descs[i].GetValue().GetTaggedValue());
183         } else {
184             JSTaggedValue::DefinePropertyOrThrow(thread, classFuncHandle, JSNApiHelper::ToJSMutableHandle(keys[i]),
185                                                  descs[i]);
186         }
187         DestructDesc(&descs[i]); // Corresponds to ConstructDescByAttr in TryAddOriKeyAndOriAttrToHClass
188         DestructAttr(&attrs[i]);
189     }
190     // Add static properties that out bound of MAX_FAST_PROPS_CAPACITY
191     for (size_t i = inlinedStaticPropCount; i < staticPropCount; ++i) {
192         ConstructDescByAttr(thread, attrs[i], &descs[i]);
193         JSTaggedValue::DefinePropertyOrThrow(thread, classFuncHandle, JSNApiHelper::ToJSMutableHandle(keys[i]),
194                                              descs[i]);
195         DestructDesc(&descs[i]);
196         DestructAttr(&attrs[i]);
197     }
198 
199     JSHandle<JSObject> clsPrototype = NewClassFuncProtoWithProperties(
200         thread, classFunc, propertyCount, propertyCount - staticPropCount, keys, attrs, descs);
201     JSFunction::InitClassFunctionWithClsPrototype(thread, classFunc, callNapi, clsPrototype);
202 
203     return classFunc;
204 }
205 } // namespace panda::ecmascript
206