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
21using panda::ObjectRef;
22using panda::StringRef;
23using panda::SymbolRef;
24
25FunctionRef::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
51void 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
107Local<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
134void 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