1/*
2 * Copyright (c) 2023 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 <fstream>
17#include <iostream>
18#include <string>
19#include <thread>
20#include <vector>
21
22#include <securec.h>
23#include <uv.h>
24
25#include "ecmascript/base/string_helper.h"
26#include "ecmascript/js_runtime_options.h"
27#include "ecmascript/napi/include/jsnapi.h"
28#include "ecmascript/platform/file.h"
29#include "tooling/utils/utils.h"
30#ifdef PANDA_TARGET_MACOS
31#include <unistd.h>
32#include <sys/syscall.h>
33#endif
34static panda::ecmascript::Mutex g_mutex;
35static std::list<std::string> g_files;
36static std::list<std::string>::iterator g_iter;
37static panda::ecmascript::JSRuntimeOptions g_runtimeOptions;
38static uv_async_t *g_exitSignal = nullptr;
39static int g_threadCount = 0;
40static int g_runningCount = 0;
41
42static constexpr int MAX_THREAD = 1024;
43
44namespace OHOS::ArkCompiler::Toolchain {
45bool ExecutePandaFile(panda::ecmascript::EcmaVM *vm,
46                      const panda::ecmascript::JSRuntimeOptions &runtimeOptions,
47                      const std::string &file, const std::string &entry)
48{
49    panda::LocalScope scope(vm);
50
51    panda::ecmascript::EcmaContext *context1 = nullptr;
52    if (runtimeOptions.IsEnableContext()) {
53        context1 = panda::JSNApi::CreateJSContext(vm);
54        panda::JSNApi::SwitchCurrentContext(vm, context1);
55    }
56
57    if (runtimeOptions.WasAOTOutputFileSet()) {
58        panda::JSNApi::LoadAotFile(vm, "");
59    }
60
61    bool ret = panda::JSNApi::Execute(vm, file, entry);
62
63    if (runtimeOptions.IsEnableContext()) {
64        panda::JSNApi::DestroyJSContext(vm, context1);
65    }
66
67    return ret;
68}
69
70std::pair<std::string, std::string> GetNextPara()
71{
72    std::string fileName = *g_iter;
73    std::string fileAbc = fileName.substr(fileName.find_last_of('/') + 1);
74    std::string entry = fileAbc.substr(0, fileAbc.size() - 4);
75    g_iter++;
76    g_runningCount++;
77    return {fileName, entry};
78}
79
80std::string GetMsg(int ret, std::string& msg, std::string& fileName)
81{
82    if (!ret) {
83#ifdef PANDA_TARGET_MACOS
84        msg = "[FAILED] [" + std::to_string(syscall(SYS_thread_selfid)) + "] Run " +
85            fileName + " failed!";
86#else
87        msg = "[FAILED] [" + std::to_string(gettid()) + "] Run " + fileName + " failed!";
88#endif
89    } else {
90#ifdef PANDA_TARGET_MACOS
91        msg = "[PASS] [" + std::to_string(syscall(SYS_thread_selfid)) + "] Run " +
92            fileName + " success!";
93#else
94        msg = "[PASS] [" + std::to_string(gettid()) + "] Run " + fileName + " success!";
95#endif
96    }
97    return msg;
98}
99
100bool StartThread(uv_loop_t *loop)
101{
102    uv_thread_t tid = 0;
103    int ret = uv_thread_create(&tid, [] (void* arg) -> void {
104        while (true) {
105            g_mutex.Lock();
106            if (g_iter == g_files.end()) {
107                g_threadCount--;
108                if (g_threadCount == 0) {
109                    uv_async_send(g_exitSignal);
110                }
111                g_mutex.Unlock();
112                break;
113            }
114            auto [fileName, entry] = GetNextPara();
115            g_mutex.Unlock();
116            panda::ecmascript::EcmaVM *vm = panda::JSNApi::CreateEcmaVM(g_runtimeOptions);
117            if (vm == nullptr) {
118                std::cerr << "Cannot create vm." << std::endl;
119                return;
120            }
121            panda::JSNApi::SetBundle(vm, !g_runtimeOptions.GetMergeAbc());
122            bool ret = ExecutePandaFile(vm, g_runtimeOptions, fileName, entry);
123            panda::JSNApi::DestroyJSVM(vm);
124            auto loop = static_cast<uv_loop_t *>(arg);
125            auto work = new uv_work_t;
126            std::string msg = GetMsg(ret, msg, fileName);
127            work->data = new char[msg.size() + 1];
128            if (strncpy_s(static_cast<char*>(work->data), msg.size() + 1, msg.data(), msg.size()) != EOK) {
129                delete[] static_cast<char*>(work->data);
130                delete work;
131                std::abort();
132            }
133            uv_queue_work(loop, work, [] (uv_work_t*) {}, [] (uv_work_t* work, int) {
134                std::cerr << static_cast<char*>(work->data) << std::endl;
135                delete[] static_cast<char*>(work->data);
136                delete work;
137            });
138        }
139    }, loop);
140    return ret != 0;
141}
142
143int Main(const int argc, const char **argv)
144{
145    auto startTime =
146        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch())
147            .count();
148
149    std::cerr << "Run begin [" << getpid() << "]" << std::endl;
150    if (argc < 3) { // 3: at least have three arguments
151        std::cerr << "At least have three arguments." << std::endl;
152        return -1;
153    }
154
155    std::string countStr = argv[1];
156    int32_t count;
157    if (!Utils::StrToInt32(countStr, count)) {
158        std::cerr << "The argument about the number of threads is incorrect." << std::endl;
159        return -1;
160    }
161    g_threadCount = std::min(count, MAX_THREAD);
162
163    std::string filePath = argv[2];
164    std::string realPath;
165    if (!panda::ecmascript::RealPath(filePath, realPath, true)) {
166        std::cerr << "RealPath return fail";
167        return -1;
168    }
169
170    g_mutex.Lock();
171    std::string line;
172    std::ifstream in(realPath);
173    while (std::getline(in, line)) {
174        if (line.find_last_of(".abc") == std::string::npos) {  // endwith
175            std::cerr << "Not endwith .abc" << line << std::endl;
176            std::abort();
177        }
178        g_files.emplace_back(line);
179    }
180    g_iter = g_files.begin();
181    g_mutex.Unlock();
182
183    bool retOpt = g_runtimeOptions.ParseCommand(argc - 2, argv + 2);
184    if (!retOpt) {
185        std::cerr << "ParseCommand failed." << std::endl;
186        return -1;
187    }
188    panda::ecmascript::EcmaVM *vm = panda::JSNApi::CreateEcmaVM(g_runtimeOptions);
189    if (vm == nullptr) {
190        std::cerr << "Cannot create vm." << std::endl;
191        return -1;
192    }
193    panda::JSNApi::SetBundle(vm, !g_runtimeOptions.GetMergeAbc());
194
195    uv_loop_t* loop = uv_default_loop();
196    g_exitSignal = new uv_async_t;
197    g_exitSignal->data = loop;
198    uv_async_init(loop, g_exitSignal, []([[maybe_unused]] uv_async_t* handle) {
199        g_mutex.Lock();
200        assert (g_threadCount == 0);
201        g_mutex.Unlock();
202        auto loop = static_cast<uv_loop_t*>(handle->data);
203        uv_stop(loop);
204    });
205
206    int threadCountLocal = g_threadCount;
207    for (int i = 0; i < threadCountLocal; i++) {
208        StartThread(loop);
209    }
210
211    uv_run(loop, UV_RUN_DEFAULT);
212
213    uv_close(reinterpret_cast<uv_handle_t*>(g_exitSignal), [] (uv_handle_t* handle) {
214        if (handle != nullptr) {
215            delete reinterpret_cast<uv_handle_t*>(handle);
216            handle = nullptr;
217        }
218    });
219    panda::JSNApi::DestroyJSVM(vm);
220
221    auto endTime =
222        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch())
223            .count();
224
225    g_mutex.Lock();
226    const long long timeUnit = 1000'000'000;
227    std::cerr << "Run end, total file count: " << g_runningCount << ", used: "
228	      << ((endTime - startTime) / timeUnit) << "s." << std::endl;
229    g_mutex.Unlock();
230    return 0;
231}
232} // OHOS::ArkCompiler::Toolchain
233
234int main(int argc, const char **argv)
235{
236    return OHOS::ArkCompiler::Toolchain::Main(argc, argv);
237}
238