1 /*
2 * Copyright (c) 2022-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 <map>
17 #include "convertors-ets.h"
18 #include "signatures.h"
19 #include "interop-logging.h"
20 #include "interop-types.h"
21
22 static const char* callCallbackFromNative = "callCallbackFromNative";
23 static const char* callCallbackFromNativeSig = "I[BI:I";
24
25 static const char* FAST_NATIVE_PREFIX = "#F$";
26
27 const bool registerByOne = true;
28
registerNatives(ets_env * env,const ets_class clazz,const std::vector<std::tuple<std::string,std::string,void *,int>> impls)29 static bool registerNatives(ets_env *env, const ets_class clazz, const std::vector<std::tuple<std::string, std::string, void*, int>> impls) {
30 std::vector<EtsNativeMethod> methods;
31 methods.reserve(impls.size());
32 bool result = true;
33 for (const auto &[name, type, func, flag] : impls) {
34 EtsNativeMethod method;
35 method.name = name.c_str();
36 method.func = func;
37 method.signature = (flag & ETS_SLOW_NATIVE_FLAG) == 0 ? FAST_NATIVE_PREFIX : nullptr;
38 if (registerByOne) {
39 result &= env->RegisterNatives(clazz, &method, 1) >= 0;
40 if (env->ErrorCheck()) {
41 env->ErrorClear();
42 }
43 }
44 else {
45 methods.push_back(method);
46 }
47 }
48 if (!registerByOne) {
49 result = env->RegisterNatives(clazz, methods.data(), static_cast<ets_int>(methods.size())) >= 0;
50 }
51 return registerByOne ? true : result;
52 }
53
registerAllModules(ets_env * env)54 bool registerAllModules(ets_env *env) {
55 auto moduleNames = EtsExports::getInstance()->getModules();
56
57 for (auto it = moduleNames.begin(); it != moduleNames.end(); ++it) {
58 std::string classpath = EtsExports::getInstance()->getClasspath(*it);
59 ets_class nativeModule = env->FindClass(classpath.c_str());
60 if (nativeModule == nullptr) {
61 LOGE("Cannot find managed class %s", classpath.c_str());
62 continue;
63 }
64 if (!registerNatives(env, nativeModule, EtsExports::getInstance()->getMethods(*it))) {
65 return false;
66 }
67 }
68
69 return true;
70 }
71
EtsNapiOnLoad(ets_env * env)72 extern "C" ETS_EXPORT ets_int ETS_CALL EtsNapiOnLoad(ets_env *env) {
73 if (!registerAllModules(env)) {
74 LOGE("Failed to register ets modules");
75 return ETS_ERR;
76 }
77 auto interopClasspath = EtsExports::getInstance()->getClasspath("InteropNativeModule");
78 auto interopClass = env->FindClass(interopClasspath.c_str());
79 if (interopClass == nullptr) {
80 LOGE("Can not find InteropNativeModule classpath to set callback dispatcher");
81 return ETS_ERR;
82 }
83 if (!setKoalaEtsNapiCallbackDispatcher(env, interopClass, callCallbackFromNative, callCallbackFromNativeSig)) {
84 LOGE("Failed to set koala ets callback dispatcher");
85 return ETS_ERR;
86 }
87 return ETS_NAPI_VERSION_1_0;
88 }
89
getInstance()90 EtsExports* EtsExports::getInstance() {
91 static EtsExports *instance = nullptr;
92 if (instance == nullptr) {
93 instance = new EtsExports();
94 }
95 return instance;
96 }
97
getModules()98 std::vector<std::string> EtsExports::getModules() {
99 std::vector<std::string> result;
100 for (auto it = implementations.begin(); it != implementations.end(); ++it) {
101 result.push_back(it->first);
102 }
103 return result;
104 }
105
getMethods(const std::string & module)106 const std::vector<std::tuple<std::string, std::string, void*, int>>& EtsExports::getMethods(const std::string& module) {
107 auto it = implementations.find(module);
108 if (it == implementations.end()) {
109 LOGE("Module %s is not registered", module.c_str());
110 }
111 return it->second;
112 }
113
addMethod(const char * module,const char * name,const char * type,void * impl,int flags)114 void EtsExports::addMethod(const char* module, const char *name, const char *type, void *impl, int flags) {
115 auto it = implementations.find(module);
116 if (it == implementations.end()) {
117 it = implementations.insert(std::make_pair(module, std::vector<std::tuple<std::string, std::string, void*, int>>())).first;
118 }
119 it->second.push_back(std::make_tuple(name, convertType(name, type), impl, flags));
120 }
121
setClasspath(const char * module,const char * classpath)122 void EtsExports::setClasspath(const char* module, const char *classpath) {
123 auto it = classpaths.find(module);
124 if (it == classpaths.end()) {
125 classpaths.insert(std::make_pair(module, classpath));
126 } else {
127 LOGE("Classpath for module %s was redefined", module);
128 }
129 }
130
131 static std::map<std::string, std::string> g_defaultClasspaths = {
132 {"InteropNativeModule", "@koalaui/interop/InteropNativeModule/InteropNativeModule"},
133 {"TestNativeModule", "@koalaui/arkts-arkui/generated/arkts/TestNativeModule/TestNativeModule"},
134 {"ArkUINativeModule", "@koalaui/arkts-arkui/generated/arkts/ArkUINativeModule/ArkUINativeModule"},
135 {"ArkUIGeneratedNativeModule", "@koalaui/arkts-arkui/generated/arkts/ArkUIGeneratedNativeModule/ArkUIGeneratedNativeModule"},
136 };
getClasspath(const std::string & module)137 const std::string& EtsExports::getClasspath(const std::string& module) {
138 auto it = classpaths.find(module);
139 if (it != classpaths.end()) {
140 return it->second;
141 }
142 auto defaultClasspath = g_defaultClasspaths.find(module);
143 if (defaultClasspath != g_defaultClasspaths.end()) {
144 return defaultClasspath->second;
145 }
146 INTEROP_FATAL("Classpath for module %s was not registered", module.c_str());
147 }
148