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