1/*
2 * Copyright (c) 2021 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 ECMASCRIPT_TESTS_TEST_HELPER_H
17#define ECMASCRIPT_TESTS_TEST_HELPER_H
18
19#include "ecmascript/interpreter/interpreter.h"
20#include "ecmascript/ecma_runtime_call_info.h"
21#include "ecmascript/ecma_string-inl.h"
22#include "ecmascript/ecma_vm.h"
23#include "ecmascript/js_function.h"
24#include "ecmascript/js_handle.h"
25#include "ecmascript/js_thread.h"
26#include "ecmascript/mem/mem_common.h"
27#include "ecmascript/napi/include/jsnapi.h"
28#include "ecmascript/object_factory-inl.h"
29#include "ecmascript/checkpoint/thread_state_transition.h"
30#include "gtest/gtest.h"
31
32namespace panda::test {
33using panda::ecmascript::EcmaHandleScope;
34using panda::ecmascript::EcmaRuntimeCallInfo;
35using panda::ecmascript::EcmaVM;
36using panda::ecmascript::InterpretedFrame;
37using panda::ecmascript::InterpretedBuiltinFrame;
38using panda::ecmascript::InterpretedEntryFrame;
39using panda::ecmascript::JSTaggedType;
40using panda::ecmascript::JSTaggedValue;
41using panda::ecmascript::JSThread;
42using panda::ecmascript::NUM_MANDATORY_JSFUNC_ARGS;
43using panda::ecmascript::JSRuntimeOptions;
44using panda::ecmascript::JSFunction;
45using panda::ecmascript::JSHandle;
46
47#define HWTEST_F_L0(testsuit, testcase) HWTEST_F(testsuit, testcase, testing::ext::TestSize.Level0)
48#define HWTEST_P_L0(testsuit, testcase) HWTEST_P(testsuit, testcase, testing::ext::TestSize.Level0)
49#define EXPECT_EXCEPTION()                           \
50    EXPECT_TRUE(thread->HasPendingException());      \
51    EXPECT_TRUE(thread->GetException().IsJSError()); \
52    thread->ClearException()
53
54class TestHelper {
55public:
56    static EcmaRuntimeCallInfo* CreateEcmaRuntimeCallInfo(JSThread *thread, JSTaggedValue newTgt, uint32_t argvLength)
57    {
58        const uint8_t testDecodedSize = 2;
59        // argvLength includes number of int64_t to store value and tag of function, 'this' and call args
60        // It doesn't include new.target argument
61        int32_t numActualArgs = argvLength / testDecodedSize + 1;
62        JSTaggedType *sp = const_cast<JSTaggedType *>(thread->GetCurrentSPFrame());
63
64        size_t frameSize = 0;
65        if (thread->IsAsmInterpreter()) {
66            frameSize = InterpretedEntryFrame::NumOfMembers() + numActualArgs;
67        } else {
68            frameSize = InterpretedFrame::NumOfMembers() + numActualArgs;
69        }
70        JSTaggedType *newSp = sp - frameSize;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
71        for (int i = numActualArgs; i > 0; i--) {
72            newSp[i - 1] = JSTaggedValue::Undefined().GetRawData();
73        }
74        EcmaRuntimeCallInfo *ecmaRuntimeCallInfo = reinterpret_cast<EcmaRuntimeCallInfo *>(newSp - 2);
75        *(--newSp) = numActualArgs;
76        *(--newSp) = panda::ecmascript::ToUintPtr(thread);
77        ecmaRuntimeCallInfo->SetNewTarget(newTgt);
78        return ecmaRuntimeCallInfo;
79    }
80
81    static JSTaggedType *SetupFrame(JSThread *thread, EcmaRuntimeCallInfo *info)
82    {
83        JSTaggedType *sp = const_cast<JSTaggedType *>(thread->GetCurrentSPFrame());
84        size_t frameSize = 0;
85        if (thread->IsAsmInterpreter()) {
86            // 2 means thread and numArgs
87            frameSize = InterpretedEntryFrame::NumOfMembers() + info->GetArgsNumber() + NUM_MANDATORY_JSFUNC_ARGS + 2;
88        } else {
89            // 2 means thread and numArgs
90            frameSize = InterpretedFrame::NumOfMembers() + info->GetArgsNumber() + NUM_MANDATORY_JSFUNC_ARGS + 2;
91        }
92        JSTaggedType *newSp = sp - frameSize;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
93
94        InterpretedEntryFrame *state = reinterpret_cast<InterpretedEntryFrame *>(newSp) - 1;
95        state->base.type = ecmascript::FrameType::INTERPRETER_ENTRY_FRAME;
96        state->base.prev = sp;
97        state->pc = nullptr;
98        thread->SetCurrentSPFrame(newSp);
99        return sp;
100    }
101
102    static void TearDownFrame(JSThread *thread, JSTaggedType *prev)
103    {
104        thread->SetCurrentSPFrame(prev);
105    }
106
107    // If you want to call once create, you can refer to BuiltinsMathTest for detail.
108    static void CreateEcmaVMWithScope(EcmaVM *&instance, JSThread *&thread, EcmaHandleScope *&scope,
109        bool tryLoadStubFile = false, bool useCInterpreter = false, bool startManagedCode = true)
110    {
111        JSRuntimeOptions options;
112        options.SetEnableForceGC(true);
113        if (tryLoadStubFile) {
114            options.SetEnableAsmInterpreter(true);
115        }
116        if (useCInterpreter) {
117            options.SetEnableAsmInterpreter(false);
118        }
119        instance = JSNApi::CreateEcmaVM(options);
120        instance->SetEnableForceGC(true);
121        ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
122        thread = instance->GetJSThread();
123        if (startManagedCode) {
124            thread->ManagedCodeBegin();
125        }
126        scope = new EcmaHandleScope(thread);
127    }
128
129    static inline void DestroyEcmaVMWithScope(EcmaVM *instance, EcmaHandleScope *scope, bool exitManagedCode = true)
130    {
131        delete scope;
132        scope = nullptr;
133        if (exitManagedCode) {
134            instance->GetJSThread()->ManagedCodeEnd();
135        }
136        instance->SetEnableForceGC(false);
137        JSNApi::DestroyJSVM(instance);
138    }
139
140    static EcmaRuntimeCallInfo* CreateEcmaRuntimeCallInfo(JSThread *thread, std::vector<JSTaggedValue>& args,
141        int32_t maxArgLen, JSTaggedValue thisValue = JSTaggedValue::Undefined())
142    {
143        auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), maxArgLen);
144        ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
145        ecmaRuntimeCallInfo->SetThis(thisValue);
146        for (size_t i = 0; i < args.size(); i++) {
147            ecmaRuntimeCallInfo->SetCallArg(i, args[i]);
148        }
149        return ecmaRuntimeCallInfo;
150    }
151
152    static EcmaRuntimeCallInfo* CreateEcmaRuntimeCallInfo(JSThread *thread, JSHandle<JSFunction>& newTarget,
153        std::vector<JSTaggedValue>& args, int32_t maxArgLen, JSTaggedValue thisValue = JSTaggedValue::Undefined())
154    {
155        auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), maxArgLen);
156        ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue());
157        ecmaRuntimeCallInfo->SetThis(thisValue);
158        for (size_t i = 0; i < args.size(); i++) {
159            ecmaRuntimeCallInfo->SetCallArg(i, args[i]);
160        }
161        return ecmaRuntimeCallInfo;
162    }
163};
164
165class BaseTestWithOutScope : public testing::Test {
166public:
167    static void SetUpTestCase()
168    {
169        GTEST_LOG_(INFO) << "SetUpTestCase";
170    }
171
172    static void TearDownTestCase()
173    {
174        GTEST_LOG_(INFO) << "TearDownCase";
175    }
176
177    void SetUp() override {}
178
179    void TearDown() override {}
180};
181
182template<bool icuPath = false>
183class BaseTestWithScope : public BaseTestWithOutScope {
184public:
185    void SetUp() override
186    {
187        if (!icuPath) {
188            TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
189        } else {
190            JSRuntimeOptions options;
191#if defined(PANDA_TARGET_LINUX) && defined(ICU_PATH)
192        // for consistency requirement, use ohos_icu4j/data as icu-data-path
193            options.SetIcuDataPath(ICU_PATH);
194#endif
195            options.SetEnableForceGC(true);
196            instance = JSNApi::CreateEcmaVM(options);
197            instance->SetEnableForceGC(true);
198            ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
199            thread = instance->GetJSThread();
200            thread->ManagedCodeBegin();
201            scope = new EcmaHandleScope(thread);
202        }
203    }
204
205    void TearDown() override
206    {
207        TestHelper::DestroyEcmaVMWithScope(instance, scope);
208    }
209
210    EcmaVM *instance {nullptr};
211    EcmaHandleScope *scope {nullptr};
212    JSThread *thread {nullptr};
213};
214}  // namespace panda::test
215#endif  // ECMASCRIPT_TESTS_TEST_HELPER_H
216