• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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