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
22 namespace panda::test {
23 using namespace panda;
24 using namespace panda::ecmascript;
25 using FunctionCallbackInfo = Local<JSValueRef>(*)(JsiRuntimeCallInfo *);
26 std::condition_variable g_semaphore;
27
28 class ThreadTerminationTest : public testing::Test {
29 public:
SetUpTestCase()30 static void SetUpTestCase()
31 {
32 GTEST_LOG_(INFO) << "SetUpTestCase";
33 }
34
TearDownTestCase()35 static void TearDownTestCase()
36 {
37 GTEST_LOG_(INFO) << "TearDownCase";
38 }
39
SetUp()40 void SetUp() override
41 {
42 TestHelper::CreateEcmaVMWithScope(vm_, thread_, scope_);
43 }
44
TearDown()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
RegisterGlobalTemplate(const EcmaVM * vm,FunctionCallbackInfo terminate,FunctionCallbackInfo fail,FunctionCallbackInfo signal)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
67 protected:
68 EcmaVM *vm_ {nullptr};
69 JSThread *thread_ = {nullptr};
70 EcmaHandleScope *scope_ {nullptr};
71 };
72
73 class TerminatorThread {
74 public:
TerminatorThread(EcmaVM * vm)75 explicit TerminatorThread(EcmaVM *vm) : vm_(vm) {}
76 ~TerminatorThread() = default;
77
Run()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
RunWithSleep()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
Join()95 void Join()
96 {
97 thread_.join();
98 }
99
100 private:
101 EcmaVM *vm_ {nullptr};
102 std::mutex mutex_;
103 std::thread thread_;
104 };
105
TerminateThread(JsiRuntimeCallInfo * runtimeCallInfo)106 Local<JSValueRef> ThreadTerminationTest::TerminateThread(JsiRuntimeCallInfo *runtimeCallInfo)
107 {
108 EcmaVM *vm = runtimeCallInfo->GetVM();
109 DFXJSNApi::TerminateExecution(vm);
110 return JSValueRef::Undefined(vm);
111 }
112
Fail(JsiRuntimeCallInfo * runtimeCallInfo)113 Local<JSValueRef> ThreadTerminationTest::Fail([[maybe_unused]]JsiRuntimeCallInfo *runtimeCallInfo)
114 {
115 UNREACHABLE();
116 }
117
Signal(JsiRuntimeCallInfo * runtimeCallInfo)118 Local<JSValueRef> ThreadTerminationTest::Signal(JsiRuntimeCallInfo *runtimeCallInfo)
119 {
120 EcmaVM *vm = runtimeCallInfo->GetVM();
121 g_semaphore.notify_one();
122 return JSValueRef::Undefined(vm);
123 }
124
HWTEST_F_L0(ThreadTerminationTest,TerminateFromThreadItself)125 HWTEST_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
HWTEST_F_L0(ThreadTerminationTest,TerminateFromOtherThread)138 HWTEST_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
HWTEST_F_L0(ThreadTerminationTest,TerminateFromOtherThreadWithoutCall)154 HWTEST_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
HWTEST_F_L0(ThreadTerminationTest,TerminateClearArrayJoinStack)170 HWTEST_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
HWTEST_F_L0(ThreadTerminationTest,TerminateInMicroTask)183 HWTEST_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
HWTEST_F_L0(ThreadTerminationTest,TerminateWithoutExecutingMicroTask)195 HWTEST_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