1 /**
2 * Copyright (c) 2021-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 "napi/ets_napi.h"
17 #include "plugins/ets/runtime/napi/ets_napi_internal.h"
18 #include "plugins/ets/runtime/napi/ets_napi_invoke_interface.h"
19
20 #include <sstream>
21 #include <vector>
22
23 #include "generated/base_options.h"
24 #include "libpandabase/utils/logger.h"
25 #include "plugins/ets/runtime/ets_coroutine.h"
26 #include "plugins/ets/runtime/ets_vm.h"
27
28 namespace ark::ets::napi {
DestroyEtsVM(EtsVM * vm)29 static ets_int DestroyEtsVM(EtsVM *vm)
30 {
31 if (vm == nullptr) {
32 LOG(ERROR, RUNTIME) << "Cannot destroy eTS VM, it is null";
33 return ETS_ERR;
34 }
35
36 auto runtime = Runtime::GetCurrent();
37 if (runtime == nullptr) {
38 LOG(ERROR, RUNTIME) << "Cannot destroy eTS VM, there is no current runtime";
39 return ETS_ERR;
40 }
41
42 auto pandaVm = PandaEtsVM::FromEtsVM(vm);
43 auto mainVm = runtime->GetPandaVM();
44 if (pandaVm == mainVm) {
45 Runtime::Destroy();
46 } else {
47 PandaEtsVM::Destroy(pandaVm);
48 }
49
50 return ETS_OK;
51 }
52
CheckVersionEtsNapi(ets_int version)53 bool CheckVersionEtsNapi(ets_int version)
54 {
55 return (version == ETS_NAPI_VERSION_1_0);
56 }
57
GetEnv(EtsVM * vm,EtsEnv ** pEnv,ets_int version)58 static ets_int GetEnv([[maybe_unused]] EtsVM *vm, EtsEnv **pEnv, [[maybe_unused]] ets_int version)
59 {
60 if (pEnv == nullptr) {
61 LOG(ERROR, RUNTIME) << "Cannot get environment, p_env is null";
62 return ETS_ERR;
63 }
64
65 auto coroutine = EtsCoroutine::GetCurrent();
66 if (coroutine == nullptr) {
67 LOG(ERROR, RUNTIME) << "Cannot get environment, there is no current coroutine";
68 return ETS_ERR;
69 }
70
71 if (!CheckVersionEtsNapi(version)) {
72 *pEnv = nullptr;
73 return ETS_ERR_VER;
74 }
75 *pEnv = coroutine->GetEtsNapiEnv();
76
77 return ETS_OK;
78 }
79
80 static const struct ETS_InvokeInterface S_INVOKE_INTERFACE = {DestroyEtsVM, GetEnv};
81
GetInvokeInterface()82 const ETS_InvokeInterface *GetInvokeInterface()
83 {
84 return &S_INVOKE_INTERFACE;
85 }
86
87 static EtsVMInitArgs g_sDefaultArgs = {0, 0, nullptr};
88
ETS_GetDefaultVMInitArgs(EtsVMInitArgs * vmArgs)89 extern "C" ets_int ETS_GetDefaultVMInitArgs(EtsVMInitArgs *vmArgs)
90 {
91 *vmArgs = g_sDefaultArgs;
92 return ETS_OK;
93 }
94
ETS_GetCreatedVMs(EtsVM ** vmBuf,ets_size bufLen,ets_size * nVms)95 extern "C" ets_int ETS_GetCreatedVMs(EtsVM **vmBuf, ets_size bufLen, ets_size *nVms)
96 {
97 if (nVms == nullptr) {
98 return ETS_ERR;
99 }
100
101 if (auto coroutine = EtsCoroutine::GetCurrent()) {
102 *nVms = 1;
103
104 if (vmBuf == nullptr || bufLen < 1) {
105 return ETS_ERR;
106 }
107
108 *vmBuf = coroutine->GetPandaVM();
109 } else {
110 *nVms = 0;
111 }
112
113 return ETS_OK;
114 }
115
116 static void *g_logPrintFunction = nullptr;
117
EtsMobileLogPrint(int id,int level,const char * component,const char * fmt,const char * msg)118 static void EtsMobileLogPrint(int id, int level, const char *component, const char *fmt, const char *msg)
119 {
120 int etsLevel = EtsMobileLogggerLevel::ETS_MOBILE_LOG_LEVEL_UNKNOWN;
121 switch (level) {
122 case ark::Logger::PandaLog2MobileLog::UNKNOWN:
123 etsLevel = EtsMobileLogggerLevel::ETS_MOBILE_LOG_LEVEL_UNKNOWN;
124 break;
125 case ark::Logger::PandaLog2MobileLog::DEFAULT:
126 etsLevel = EtsMobileLogggerLevel::ETS_MOBILE_LOG_LEVEL_DEFAULT;
127 break;
128 case ark::Logger::PandaLog2MobileLog::VERBOSE:
129 etsLevel = EtsMobileLogggerLevel::ETS_MOBILE_LOG_LEVEL_VERBOSE;
130 break;
131 case ark::Logger::PandaLog2MobileLog::DEBUG:
132 etsLevel = EtsMobileLogggerLevel::ETS_MOBILE_LOG_LEVEL_DEBUG;
133 break;
134 case ark::Logger::PandaLog2MobileLog::INFO:
135 etsLevel = EtsMobileLogggerLevel::ETS_MOBILE_LOG_LEVEL_INFO;
136 break;
137 case ark::Logger::PandaLog2MobileLog::WARN:
138 etsLevel = EtsMobileLogggerLevel::ETS_MOBILE_LOG_LEVEL_WARN;
139 break;
140 case ark::Logger::PandaLog2MobileLog::ERROR:
141 etsLevel = EtsMobileLogggerLevel::ETS_MOBILE_LOG_LEVEL_ERROR;
142 break;
143 case ark::Logger::PandaLog2MobileLog::FATAL:
144 etsLevel = EtsMobileLogggerLevel::ETS_MOBILE_LOG_LEVEL_FATAL;
145 break;
146 case ark::Logger::PandaLog2MobileLog::SILENT:
147 etsLevel = EtsMobileLogggerLevel::ETS_MOBILE_LOG_LEVEL_SILENT;
148 break;
149 default:
150 LOG(ERROR, RUNTIME) << "No such mobile log option";
151 }
152
153 auto logPrintCallback = reinterpret_cast<FuncMobileLogPrint>(g_logPrintFunction);
154 ASSERT(logPrintCallback != nullptr);
155 logPrintCallback(id, etsLevel, component, fmt, msg);
156 }
157
158 struct ParsedOptions final {
159 // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
160 std::vector<std::string> bootPandaFiles;
161 std::vector<std::string> aotFiles;
162 std::vector<std::string> arkFiles;
163 base_options::Options baseOptions;
164 // NOLINTEND(misc-non-private-member-variables-in-classes)
165
ParsedOptionsark::ets::napi::ParsedOptions166 explicit ParsedOptions(const std::string &exePath) : baseOptions(exePath) {}
167 };
168
SplitString(const std::string & from,char delim)169 static arg_list_t SplitString(const std::string &from, char delim)
170 {
171 arg_list_t pathes;
172 std::istringstream iss {from};
173 std::string path;
174 while (std::getline(iss, path, delim)) {
175 pathes.emplace_back(path);
176 }
177 return pathes;
178 }
179
ParseOptionsHelper(RuntimeOptions & runtimeOptions,ParsedOptions & parsedOptions,Span<const EtsVMOption> & options)180 static void ParseOptionsHelper(RuntimeOptions &runtimeOptions, ParsedOptions &parsedOptions,
181 Span<const EtsVMOption> &options)
182 {
183 for (auto &o : options) {
184 auto extraStr = reinterpret_cast<const char *>(o.extraInfo);
185
186 switch (o.option) {
187 case EtsOptionType::ETS_LOG_LEVEL:
188 parsedOptions.baseOptions.SetLogLevel(extraStr);
189 break;
190 case EtsOptionType::ETS_MOBILE_LOG:
191 g_logPrintFunction = const_cast<void *>(o.extraInfo);
192 runtimeOptions.SetMobileLog(reinterpret_cast<void *>(EtsMobileLogPrint));
193 break;
194 case EtsOptionType::ETS_BOOT_FILE:
195 parsedOptions.bootPandaFiles.emplace_back(extraStr);
196 break;
197 case EtsOptionType::ETS_AOT_FILE:
198 parsedOptions.aotFiles.emplace_back(extraStr);
199 break;
200 case EtsOptionType::ETS_ARK_FILE:
201 parsedOptions.arkFiles.emplace_back(extraStr);
202 break;
203 case EtsOptionType::ETS_JIT:
204 runtimeOptions.SetCompilerEnableJit(true);
205 break;
206 case EtsOptionType::ETS_NO_JIT:
207 runtimeOptions.SetCompilerEnableJit(false);
208 break;
209 case EtsOptionType::ETS_AOT:
210 runtimeOptions.SetEnableAn(true);
211 break;
212 case EtsOptionType::ETS_NO_AOT:
213 runtimeOptions.SetEnableAn(false);
214 break;
215 case EtsOptionType::ETS_GC_TRIGGER_TYPE:
216 runtimeOptions.SetGcTriggerType(extraStr);
217 break;
218 case EtsOptionType::ETS_GC_TYPE:
219 runtimeOptions.SetGcType(extraStr);
220 break;
221 case EtsOptionType::ETS_RUN_GC_IN_PLACE:
222 runtimeOptions.SetRunGcInPlace(true);
223 break;
224 case EtsOptionType::ETS_INTERPRETER_TYPE:
225 runtimeOptions.SetInterpreterType(extraStr);
226 break;
227 case EtsOptionType::ETS_NATIVE_LIBRARY_PATH:
228 runtimeOptions.SetNativeLibraryPath(SplitString(extraStr, ':'));
229 break;
230 default:
231 LOG(ERROR, RUNTIME) << "No such option";
232 }
233 }
234 }
235
ParseOptions(const EtsVMInitArgs * args,RuntimeOptions & runtimeOptions)236 static bool ParseOptions(const EtsVMInitArgs *args, RuntimeOptions &runtimeOptions)
237 {
238 ParsedOptions parsedOptions("");
239
240 runtimeOptions.SetLoadRuntimes({"ets"});
241
242 Span<const EtsVMOption> options(args->options, args->nOptions);
243 ParseOptionsHelper(runtimeOptions, parsedOptions, options);
244
245 Logger::Initialize(parsedOptions.baseOptions);
246
247 runtimeOptions.SetBootPandaFiles(parsedOptions.bootPandaFiles);
248 runtimeOptions.SetAotFiles(parsedOptions.aotFiles);
249 runtimeOptions.SetPandaFiles(parsedOptions.arkFiles);
250
251 return true;
252 }
253
ETS_CreateVM(EtsVM ** pVm,EtsEnv ** pEnv,EtsVMInitArgs * vmArgs)254 extern "C" ETS_EXPORT ets_int ETS_CreateVM(EtsVM **pVm, EtsEnv **pEnv, EtsVMInitArgs *vmArgs)
255 {
256 trace::ScopedTrace scopedTrace(__FUNCTION__);
257
258 if (pVm == nullptr || pEnv == nullptr) {
259 return ETS_ERR;
260 }
261
262 if (!CheckVersionEtsNapi(vmArgs->version)) {
263 LOG(ERROR, ETS_NAPI) << "Error: Unsupported Ets napi version = " << vmArgs->version;
264 return ETS_ERR_VER;
265 }
266
267 RuntimeOptions runtimeOptions;
268
269 if (!ParseOptions(vmArgs, runtimeOptions)) {
270 return ETS_ERR;
271 }
272
273 if (!Runtime::Create(runtimeOptions)) {
274 LOG(ERROR, RUNTIME) << "Cannot create runtime";
275 return ETS_ERR;
276 }
277
278 ASSERT(Runtime::GetCurrent() != nullptr);
279
280 auto coroutine = EtsCoroutine::GetCurrent();
281 ASSERT(coroutine != nullptr);
282
283 *pVm = coroutine->GetPandaVM();
284 ASSERT(*pVm != nullptr);
285
286 *pEnv = coroutine->GetEtsNapiEnv();
287 ASSERT(*pEnv != nullptr);
288
289 return ETS_OK;
290 }
291
292 // We have separate shared library with ets_napi called libetsnative.so
293 // libetsnative.so contains same three ETS_* functions as libarkruntime.so
294 // libarktuntime.so exposes three _internal_ETS_* aliases
295 // And libetsnative.so ETS_* functions just forward calls to _internal_ETS_* functions
296 extern "C" ETS_EXPORT ets_int _internal_ETS_GetDefaultVMInitArgs(EtsVMInitArgs *vmArgs)
297 __attribute__((alias("ETS_GetDefaultVMInitArgs")));
298 extern "C" ETS_EXPORT ets_int _internal_ETS_GetCreatedVMs(EtsVM **vmBuf, ets_size bufLen, ets_size *nVms)
299 __attribute__((alias("ETS_GetCreatedVMs")));
300 extern "C" ETS_EXPORT ets_int _internal_ETS_CreateVM(EtsVM **pVm, EtsEnv **pEnv, EtsVMInitArgs *vmArgs)
301 __attribute__((alias("ETS_CreateVM")));
302 } // namespace ark::ets::napi
303