1 /*
2  * Copyright (c) 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 #include "native_sendable.h"
17 
18 #include "ark_native_engine.h"
19 #include "native_engine/native_utils.h"
20 
21 using panda::ObjectRef;
22 using panda::StringRef;
23 using panda::SymbolRef;
24 
CreateSendablePropertiesInfos(napi_env env,const NapiPropertyDescriptor * properties,size_t propertiesLength)25 FunctionRef::SendablePropertiesInfos NativeSendable::CreateSendablePropertiesInfos(
26     napi_env env,
27     const NapiPropertyDescriptor* properties,
28     size_t propertiesLength)
29 {
30     FunctionRef::SendablePropertiesInfos infos;
31 
32     for (size_t i = 0; i < propertiesLength; ++i) {
33         if (properties[i].attributes & NATIVE_STATIC) {
34             InitSendablePropertiesInfo(env, infos.staticPropertiesInfo, properties[i]);
35         } else if (properties[i].attributes & NATIVE_INSTANCE) {
36             InitSendablePropertiesInfo(env, infos.instancePropertiesInfo, properties[i]);
37         } else if (properties[i].attributes & NATIVE_INSTANCE_OBJECT) {
38             InitSendablePropertiesInfo(env, infos.instancePropertiesInfo, properties[i],
39                                        FunctionRef::SendableType::OBJECT);
40         } else if (properties[i].attributes & NATIVE_INSTANCE_GENERIC) {
41             InitSendablePropertiesInfo(env, infos.instancePropertiesInfo, properties[i],
42                                        FunctionRef::SendableType::GENERIC);
43         } else {
44             InitSendablePropertiesInfo(env, infos.nonStaticPropertiesInfo, properties[i]);
45         }
46     }
47 
48     return infos;
49 }
50 
InitSendablePropertiesInfo(napi_env env,FunctionRef::SendablePropertiesInfo & info,NapiPropertyDescriptor propertyDescriptor,FunctionRef::SendableType type)51 void NativeSendable::InitSendablePropertiesInfo(napi_env env,
52                                                 FunctionRef::SendablePropertiesInfo& info,
53                                                 NapiPropertyDescriptor propertyDescriptor,
54                                                 FunctionRef::SendableType type)
55 {
56     auto engine = reinterpret_cast<NativeEngine*>(env);
57     auto vm = engine->GetEcmaVm();
58 
59     bool writable = (propertyDescriptor.attributes & NATIVE_WRITABLE) != 0;
60     bool enumable = (propertyDescriptor.attributes & NATIVE_ENUMERABLE) != 0;
61     bool configable = (propertyDescriptor.attributes & NATIVE_CONFIGURABLE) != 0;
62 
63     Local<StringRef> key;
64     if (propertyDescriptor.utf8name == nullptr) {
65         key = LocalValueFromJsValue(propertyDescriptor.name);
66     } else {
67         key = StringRef::NewFromUtf8(vm, propertyDescriptor.utf8name);
68     }
69     info.keys.push_back(key);
70 
71     if (propertyDescriptor.getter != nullptr || propertyDescriptor.setter != nullptr) {
72         Local<JSValueRef> localGetter = JSValueRef::Undefined(vm);
73         Local<JSValueRef> localSetter = JSValueRef::Undefined(vm);
74 
75         if (propertyDescriptor.getter != nullptr) {
76             localGetter =
77                 NapiNativeCreateSendableFunction(env, "getter", propertyDescriptor.getter, propertyDescriptor.data);
78         }
79         if (propertyDescriptor.setter != nullptr) {
80             localSetter =
81                 NapiNativeCreateSendableFunction(env, "setter", propertyDescriptor.setter, propertyDescriptor.data);
82         }
83 
84         Local<JSValueRef> val = ObjectRef::CreateSendableAccessorData(vm, localGetter, localSetter);
85         info.types.push_back(FunctionRef::SendableType::OBJECT);
86         info.attributes.push_back(PropertyAttribute(val, false, enumable, configable));
87     } else if (propertyDescriptor.method != nullptr) {
88         std::string fullName;
89         if (propertyDescriptor.utf8name != nullptr) {
90             fullName += propertyDescriptor.utf8name;
91         } else {
92             fullName += key->IsString(vm) ? Local<StringRef>(key)->ToString(vm)
93                                           : Local<SymbolRef>(key)->GetDescription(vm)->ToString(vm);
94         }
95 
96         Local<JSValueRef> func =
97             NapiNativeCreateSendableFunction(env, fullName.c_str(), propertyDescriptor.method, propertyDescriptor.data);
98         info.types.push_back(FunctionRef::SendableType::OBJECT);
99         info.attributes.push_back(PropertyAttribute(func, writable, enumable, configable));
100     } else {
101         Local<JSValueRef> val = LocalValueFromJsValue(propertyDescriptor.value);
102         info.types.push_back(type);
103         info.attributes.push_back(PropertyAttribute(val, writable, enumable, configable));
104     }
105 }
106 
NapiNativeCreateSendableFunction(napi_env env,const char * name,NapiNativeCallback cb,void * value)107 Local<JSValueRef> NativeSendable::NapiNativeCreateSendableFunction(napi_env env,
108                                                                    const char* name,
109                                                                    NapiNativeCallback cb,
110                                                                    void* value)
111 {
112     auto engine = reinterpret_cast<NativeEngine*>(env);
113     auto vm = const_cast<EcmaVM*>(engine->GetEcmaVm());
114     NapiFunctionInfo* funcInfo = NapiFunctionInfo::CreateNewInstance();
115     if (funcInfo == nullptr) {
116         HILOG_ERROR("funcInfo is nullptr");
117         return JSValueRef::Undefined(vm);
118     }
119     funcInfo->callback = cb;
120     funcInfo->data = value;
121 
122     Local<FunctionRef> fn = FunctionRef::NewSendable(
123         vm, ArkNativeFunctionCallBack,
124         [](void* env, void* externalPointer, void* data) {
125             auto info = reinterpret_cast<NapiFunctionInfo*>(data);
126             if (info != nullptr) {
127                 delete info;
128             }
129         },
130         reinterpret_cast<void*>(funcInfo), true);
131     return fn;
132 }
133 
NapiDefineSendabledProperty(napi_env env,Local<ObjectRef> & obj,NapiPropertyDescriptor & propertyDescriptor,Local<JSValueRef> & propertyName,bool & result)134 void NativeSendable::NapiDefineSendabledProperty(napi_env env,
135                                                  Local<ObjectRef>& obj,
136                                                  NapiPropertyDescriptor& propertyDescriptor,
137                                                  Local<JSValueRef>& propertyName,
138                                                  bool& result)
139 {
140     auto engine = reinterpret_cast<NativeEngine*>(env);
141     auto vm = engine->GetEcmaVm();
142 
143     bool enumable = (propertyDescriptor.attributes & NATIVE_ENUMERABLE) != 0;
144     bool configable = (propertyDescriptor.attributes & NATIVE_CONFIGURABLE) != 0;
145 
146     if (propertyDescriptor.getter != nullptr || propertyDescriptor.setter != nullptr) {
147         Local<JSValueRef> localGetter = JSValueRef::Undefined(vm);
148         Local<JSValueRef> localSetter = JSValueRef::Undefined(vm);
149 
150         if (propertyDescriptor.getter != nullptr) {
151             localGetter =
152                 NapiNativeCreateSendableFunction(env, "getter", propertyDescriptor.getter, propertyDescriptor.data);
153         }
154         if (propertyDescriptor.setter != nullptr) {
155             localSetter =
156                 NapiNativeCreateSendableFunction(env, "setter", propertyDescriptor.setter, propertyDescriptor.data);
157         }
158 
159         PropertyAttribute attr(JSValueRef::Undefined(vm), false, enumable, configable);
160         // note(lzl): SetSendableAccessorProperty?
161         result = obj->SetAccessorProperty(vm, propertyName, localGetter, localSetter, attr);
162     } else if (propertyDescriptor.method != nullptr) {
163         std::string fullName;
164         if (propertyDescriptor.utf8name != nullptr) {
165             fullName += propertyDescriptor.utf8name;
166         } else {
167             fullName += propertyName->IsString(vm) ? Local<StringRef>(propertyName)->ToString(vm)
168                                                    : Local<SymbolRef>(propertyName)->GetDescription(vm)->ToString(vm);
169         }
170 
171         Local<JSValueRef> func =
172             NapiNativeCreateSendableFunction(env, fullName.c_str(), propertyDescriptor.method, propertyDescriptor.data);
173         result = obj->Set(vm, propertyName, func);
174     } else {
175         Local<JSValueRef> val = LocalValueFromJsValue(propertyDescriptor.value);
176         result = obj->Set(vm, propertyName, val);
177     }
178 }
179