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 <condition_variable> 17 18#include "ecmascript/napi/include/dfx_jsnapi.h" 19#include "ecmascript/napi/include/jsnapi.h" 20#include "ecmascript/tests/test_helper.h" 21 22namespace panda::test { 23using namespace panda; 24using namespace panda::ecmascript; 25using FunctionCallbackInfo = Local<JSValueRef>(*)(JsiRuntimeCallInfo *); 26std::condition_variable g_semaphore; 27 28class ThreadTerminationTest : public testing::Test { 29public: 30 static void SetUpTestCase() 31 { 32 GTEST_LOG_(INFO) << "SetUpTestCase"; 33 } 34 35 static void TearDownTestCase() 36 { 37 GTEST_LOG_(INFO) << "TearDownCase"; 38 } 39 40 void SetUp() override 41 { 42 TestHelper::CreateEcmaVMWithScope(vm_, thread_, scope_); 43 } 44 45 void TearDown() override 46 { 47 TestHelper::DestroyEcmaVMWithScope(vm_, scope_); 48 } 49 50 static Local<JSValueRef> TerminateThread(JsiRuntimeCallInfo *runtimeCallInfo); 51 static Local<JSValueRef> Fail(JsiRuntimeCallInfo *runtimeCallInfo); 52 static Local<JSValueRef> Signal(JsiRuntimeCallInfo *runtimeCallInfo); 53 54 static void RegisterGlobalTemplate(const EcmaVM *vm, FunctionCallbackInfo terminate, FunctionCallbackInfo fail, 55 FunctionCallbackInfo signal) 56 { 57 [[maybe_unused]] EcmaHandleScope handleScope(vm->GetJSThread()); 58 Local<ObjectRef> globalObj = JSNApi::GetGlobalObject(vm); 59 globalObj->Set(vm, StringRef::NewFromUtf8(vm, "terminate"), FunctionRef::New( 60 const_cast<panda::EcmaVM*>(vm), terminate)); 61 globalObj->Set(vm, StringRef::NewFromUtf8(vm, "fail"), FunctionRef::New( 62 const_cast<panda::EcmaVM*>(vm), fail)); 63 globalObj->Set(vm, StringRef::NewFromUtf8(vm, "signal"), FunctionRef::New( 64 const_cast<panda::EcmaVM*>(vm), Signal)); 65 } 66 67protected: 68 EcmaVM *vm_ {nullptr}; 69 JSThread *thread_ = {nullptr}; 70 EcmaHandleScope *scope_ {nullptr}; 71}; 72 73class TerminatorThread { 74public: 75 explicit TerminatorThread(EcmaVM *vm) : vm_(vm) {} 76 ~TerminatorThread() = default; 77 78 void Run() 79 { 80 thread_ = std::thread([this]() { 81 std::unique_lock<std::mutex> lock(mutex_); 82 g_semaphore.wait(lock); 83 DFXJSNApi::TerminateExecution(vm_); 84 }); 85 } 86 87 void RunWithSleep() 88 { 89 thread_ = std::thread([this]() { 90 usleep(1000000); // 1000000 : 1s for loop to execute 91 DFXJSNApi::TerminateExecution(vm_); 92 }); 93 } 94 95 void Join() 96 { 97 thread_.join(); 98 } 99 100private: 101 EcmaVM *vm_ {nullptr}; 102 std::mutex mutex_; 103 std::thread thread_; 104}; 105 106Local<JSValueRef> ThreadTerminationTest::TerminateThread(JsiRuntimeCallInfo *runtimeCallInfo) 107{ 108 EcmaVM *vm = runtimeCallInfo->GetVM(); 109 DFXJSNApi::TerminateExecution(vm); 110 return JSValueRef::Undefined(vm); 111} 112 113Local<JSValueRef> ThreadTerminationTest::Fail([[maybe_unused]]JsiRuntimeCallInfo *runtimeCallInfo) 114{ 115 UNREACHABLE(); 116} 117 118Local<JSValueRef> ThreadTerminationTest::Signal(JsiRuntimeCallInfo *runtimeCallInfo) 119{ 120 EcmaVM *vm = runtimeCallInfo->GetVM(); 121 g_semaphore.notify_one(); 122 return JSValueRef::Undefined(vm); 123} 124 125HWTEST_F_L0(ThreadTerminationTest, TerminateFromThreadItself) 126{ 127 JSThread *thread = vm_->GetAssociatedJSThread(); 128 EcmaContext::MountContext(thread); 129 JSNApi::EnableUserUncaughtErrorHandler(vm_); 130 RegisterGlobalTemplate(vm_, TerminateThread, Fail, Signal); 131 TryCatch tryCatch(vm_); 132 std::string baseFileName = MODULE_ABC_PATH "termination_1.abc"; 133 JSNApi::Execute(vm_, baseFileName, "termination_1"); 134 EXPECT_TRUE(tryCatch.HasCaught()); 135 EcmaContext::UnmountContext(thread); 136} 137 138HWTEST_F_L0(ThreadTerminationTest, TerminateFromOtherThread) 139{ 140 JSThread *thread = vm_->GetAssociatedJSThread(); 141 EcmaContext::MountContext(thread); 142 TerminatorThread terminatorThread(vm_); 143 terminatorThread.Run(); 144 JSNApi::EnableUserUncaughtErrorHandler(vm_); 145 RegisterGlobalTemplate(vm_, TerminateThread, Fail, Signal); 146 TryCatch tryCatch(vm_); 147 std::string baseFileName = MODULE_ABC_PATH "termination_2.abc"; 148 JSNApi::Execute(vm_, baseFileName, "termination_2"); 149 EXPECT_TRUE(tryCatch.HasCaught()); 150 terminatorThread.Join(); 151 EcmaContext::UnmountContext(thread); 152} 153 154HWTEST_F_L0(ThreadTerminationTest, TerminateFromOtherThreadWithoutCall) 155{ 156 JSThread *thread = vm_->GetAssociatedJSThread(); 157 EcmaContext::MountContext(thread); 158 TerminatorThread terminatorThread(vm_); 159 terminatorThread.RunWithSleep(); 160 JSNApi::EnableUserUncaughtErrorHandler(vm_); 161 RegisterGlobalTemplate(vm_, TerminateThread, Fail, Signal); 162 TryCatch tryCatch(vm_); 163 std::string baseFileName = MODULE_ABC_PATH "termination_3.abc"; 164 JSNApi::Execute(vm_, baseFileName, "termination_3"); 165 EXPECT_TRUE(tryCatch.HasCaught()); 166 terminatorThread.Join(); 167 EcmaContext::UnmountContext(thread); 168} 169 170HWTEST_F_L0(ThreadTerminationTest, TerminateClearArrayJoinStack) 171{ 172 JSThread *thread = vm_->GetAssociatedJSThread(); 173 EcmaContext::MountContext(thread); 174 JSNApi::EnableUserUncaughtErrorHandler(vm_); 175 RegisterGlobalTemplate(vm_, TerminateThread, Fail, Signal); 176 TryCatch tryCatch(vm_); 177 std::string baseFileName = MODULE_ABC_PATH "termination_4.abc"; 178 JSNApi::Execute(vm_, baseFileName, "termination_4"); 179 EXPECT_TRUE(tryCatch.HasCaught()); 180 EcmaContext::UnmountContext(thread); 181} 182 183HWTEST_F_L0(ThreadTerminationTest, TerminateInMicroTask) 184{ 185 JSThread *thread = vm_->GetAssociatedJSThread(); 186 EcmaContext::MountContext(thread); 187 JSNApi::EnableUserUncaughtErrorHandler(vm_); 188 RegisterGlobalTemplate(vm_, TerminateThread, Fail, Signal); 189 TryCatch tryCatch(vm_); 190 std::string baseFileName = MODULE_ABC_PATH "termination_5.abc"; 191 JSNApi::Execute(vm_, baseFileName, "termination_5"); 192 EcmaContext::UnmountContext(thread); 193} 194 195HWTEST_F_L0(ThreadTerminationTest, TerminateWithoutExecutingMicroTask) 196{ 197 JSThread *thread = vm_->GetAssociatedJSThread(); 198 EcmaContext::MountContext(thread); 199 JSNApi::EnableUserUncaughtErrorHandler(vm_); 200 RegisterGlobalTemplate(vm_, TerminateThread, Fail, Signal); 201 TryCatch tryCatch(vm_); 202 std::string baseFileName = MODULE_ABC_PATH "termination_6.abc"; 203 JSNApi::Execute(vm_, baseFileName, "termination_6"); 204 EXPECT_TRUE(tryCatch.HasCaught()); 205 EcmaContext::UnmountContext(thread); 206} 207} // namespace panda::test 208