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 
32 namespace panda::test {
33 using panda::ecmascript::EcmaHandleScope;
34 using panda::ecmascript::EcmaRuntimeCallInfo;
35 using panda::ecmascript::EcmaVM;
36 using panda::ecmascript::InterpretedFrame;
37 using panda::ecmascript::InterpretedBuiltinFrame;
38 using panda::ecmascript::InterpretedEntryFrame;
39 using panda::ecmascript::JSTaggedType;
40 using panda::ecmascript::JSTaggedValue;
41 using panda::ecmascript::JSThread;
42 using panda::ecmascript::NUM_MANDATORY_JSFUNC_ARGS;
43 using panda::ecmascript::JSRuntimeOptions;
44 using panda::ecmascript::JSFunction;
45 using 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 
54 class TestHelper {
55 public:
CreateEcmaRuntimeCallInfo(JSThread *thread, JSTaggedValue newTgt, uint32_t argvLength)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 
SetupFrame(JSThread *thread, EcmaRuntimeCallInfo *info)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 
TearDownFrame(JSThread *thread, JSTaggedType *prev)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.
CreateEcmaVMWithScope(EcmaVM *&instance, JSThread *&thread, EcmaHandleScope *&scope, bool tryLoadStubFile = false, bool useCInterpreter = false, bool startManagedCode = true)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 
DestroyEcmaVMWithScope(EcmaVM *instance, EcmaHandleScope *scope, bool exitManagedCode = true)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 
CreateEcmaRuntimeCallInfo(JSThread *thread, std::vector<JSTaggedValue>& args, int32_t maxArgLen, JSTaggedValue thisValue = JSTaggedValue::Undefined())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 
CreateEcmaRuntimeCallInfo(JSThread *thread, JSHandle<JSFunction>& newTarget, std::vector<JSTaggedValue>& args, int32_t maxArgLen, JSTaggedValue thisValue = JSTaggedValue::Undefined())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 
165 class BaseTestWithOutScope : public testing::Test {
166 public:
SetUpTestCase()167     static void SetUpTestCase()
168     {
169         GTEST_LOG_(INFO) << "SetUpTestCase";
170     }
171 
TearDownTestCase()172     static void TearDownTestCase()
173     {
174         GTEST_LOG_(INFO) << "TearDownCase";
175     }
176 
177     void SetUp() override {}
178 
179     void TearDown() override {}
180 };
181 
182 template<bool icuPath = false>
183 class BaseTestWithScope : public BaseTestWithOutScope {
184 public:
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