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