1/**
2 * Copyright (c) 2021-2022 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#ifndef COMPILER_TESTS_PANDA_RUNNER_H
17#define COMPILER_TESTS_PANDA_RUNNER_H
18
19#include "unit_test.h"
20#include "assembler/assembly-emitter.h"
21#include "assembler/assembly-parser.h"
22#include "include/runtime.h"
23
24namespace panda::test {
25class PandaRunner {
26public:
27    using Callback = int (*)(uintptr_t, uintptr_t);
28
29    PandaRunner()
30    {
31        auto exec_path = panda::os::file::File::GetExecutablePath();
32
33        std::vector<std::string> boot_panda_files = {exec_path.Value() + "/../pandastdlib/arkstdlib.abc"};
34
35        options_.SetBootPandaFiles(boot_panda_files);
36
37        options_.SetShouldLoadBootPandaFiles(true);
38        options_.SetShouldInitializeIntrinsics(false);
39        options_.SetNoAsyncJit(true);
40    }
41
42    void Parse(std::string_view source)
43    {
44        pandasm::Parser parser;
45
46        auto res = parser.Parse(source.data());
47        ASSERT_TRUE(res) << "Parse failed: " << res.Error().message << "\nLine " << res.Error().line_number << ": "
48                         << res.Error().whole_line;
49        file_ = pandasm::AsmEmitter::Emit(res.Value());
50    }
51
52    ~PandaRunner() = default;
53
54    void SetHook(Callback hook)
55    {
56        callback_ = hook;
57    }
58
59    void Run(std::string_view source)
60    {
61        return Run(source, std::vector<std::string> {});
62    }
63
64    void Run(std::string_view source, Callback hook)
65    {
66        callback_ = hook;
67        return Run(source, ssize_t(0));
68    }
69
70    void Run(std::string_view source, ssize_t expected_result)
71    {
72        expected_result_ = expected_result;
73        return Run(source, std::vector<std::string> {});
74    }
75
76    void Run(std::string_view source, const std::vector<std::string> &args)
77    {
78        auto finalizer = [](void *) {
79            callback_ = nullptr;
80            Runtime::Destroy();
81        };
82        std::unique_ptr<void, decltype(finalizer)> runtime_destroyer(&finalizer, finalizer);
83
84        compiler::CompilerLogger::SetComponents(GetCompilerOptions().GetCompilerLog());
85
86        Run(CreateRuntime(), source, args);
87    }
88
89    void Run(Runtime *runtime, std::string_view source, const std::vector<std::string> &args)
90    {
91        pandasm::Parser parser;
92        auto res = parser.Parse(source.data());
93        ASSERT_TRUE(res) << "Parse failed: " << res.Error().message << "\nLine " << res.Error().line_number << ": "
94                         << res.Error().whole_line;
95        auto pf = pandasm::AsmEmitter::Emit(res.Value());
96        runtime->GetClassLinker()->AddPandaFile(std::move(pf));
97
98        if (callback_) {
99            if (auto method = GetMethod("hook"); method != nullptr) {
100                method->SetCompiledEntryPoint(reinterpret_cast<void *>(Hook));
101            }
102        }
103
104        auto eres = runtime->Execute("_GLOBAL::main", args);
105        ASSERT_TRUE(eres) << static_cast<unsigned>(eres.Error());
106        if (expected_result_) {
107            ASSERT_EQ(eres.Value(), expected_result_.value());
108        }
109    }
110
111    Runtime *CreateRuntime()
112    {
113        Runtime::Create(options_);
114        return Runtime::GetCurrent();
115    }
116
117    RuntimeOptions &GetRuntimeOptions()
118    {
119        return options_;
120    }
121
122    compiler::CompilerOptions &GetCompilerOptions()
123    {
124        return compiler::options;
125    }
126
127    static Method *GetMethod(std::string_view method_name)
128    {
129        PandaString descriptor;
130        auto *thread = MTManagedThread::GetCurrent();
131        thread->ManagedCodeBegin();
132        auto cls = Runtime::GetCurrent()
133                       ->GetClassLinker()
134                       ->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY)
135                       ->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
136        thread->ManagedCodeEnd();
137        ASSERT(cls);
138        return cls->GetDirectMethod(utf::CStringAsMutf8(method_name.data()));
139    }
140
141private:
142    NO_OPTIMIZE static int Hook()
143    {
144        ASSERT(callback_);
145        if constexpr (RUNTIME_ARCH == Arch::AARCH64) {
146            uintptr_t fp;
147            uintptr_t lr;
148            ManagedThread::GetCurrent()->SetCurrentFrameIsCompiled(true);
149            asm("ldr %0, [fp, #0]" : "=r"(fp));
150            asm("ldr %0, [fp, #8]" : "=r"(lr));
151            ManagedThread::GetCurrent()->SetCurrentFrame(reinterpret_cast<Frame *>(fp));
152            return callback_(lr, fp);
153        } else if constexpr (RUNTIME_ARCH == Arch::AARCH32) {
154            uintptr_t fp;
155            uintptr_t lr;
156            ManagedThread::GetCurrent()->SetCurrentFrameIsCompiled(true);
157#if (defined(__clang__) || defined(PANDA_TARGET_ARM64))
158            asm("ldr %0, [fp, #0]" : "=r"(fp));
159            asm("ldr %0, [fp, #4]" : "=r"(lr));
160#else
161            // gcc compile header "push {r4, r11, lr}"
162            asm("ldr %0, [fp, #-4]" : "=r"(fp));
163            asm("ldr %0, [fp, #0]" : "=r"(lr));
164#endif
165            ManagedThread::GetCurrent()->SetCurrentFrame(reinterpret_cast<Frame *>(fp));
166            return callback_(lr, fp);
167        } else if constexpr (RUNTIME_ARCH == Arch::X86_64) {
168            uintptr_t fp;
169            uintptr_t lr;
170            ManagedThread::GetCurrent()->SetCurrentFrameIsCompiled(true);
171            asm("movq (%%rbp), %0" : "=r"(fp));
172            asm("movq 8(%%rbp), %0" : "=r"(lr));
173            ManagedThread::GetCurrent()->SetCurrentFrame(reinterpret_cast<Frame *>(fp));
174            return callback_(lr, fp);
175        } else {
176            return -1;
177        }
178    }
179
180private:
181    RuntimeOptions options_;
182    static inline Callback callback_ {nullptr};
183    std::unique_ptr<const panda_file::File> file_ {nullptr};
184    std::optional<ssize_t> expected_result_;
185};
186}  // namespace panda::test
187
188#endif  // COMPILER_TESTS_PANDA_RUNNER_H
189