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 auto* factory = vm_->GetFactory();
129 JSHandle<GlobalEnv> globalEnv = factory->NewGlobalEnv();
130 SaveAndSwitchEnv(thread, globalEnv);
131 JSNApi::EnableUserUncaughtErrorHandler(vm_);
132 RegisterGlobalTemplate(vm_, TerminateThread, Fail, Signal);
133 TryCatch tryCatch(vm_);
134 std::string baseFileName = MODULE_ABC_PATH "termination_1.abc";
135 JSNApi::Execute(vm_, baseFileName, "termination_1");
136 EXPECT_TRUE(tryCatch.HasCaught());
137 }
138
HWTEST_F_L0(ThreadTerminationTest,TerminateFromOtherThread)139 HWTEST_F_L0(ThreadTerminationTest, TerminateFromOtherThread)
140 {
141 JSThread *thread = vm_->GetAssociatedJSThread();
142 auto* factory = vm_->GetFactory();
143 JSHandle<GlobalEnv> globalEnv = factory->NewGlobalEnv();
144 SaveAndSwitchEnv(thread, globalEnv);
145 TerminatorThread terminatorThread(vm_);
146 terminatorThread.Run();
147 JSNApi::EnableUserUncaughtErrorHandler(vm_);
148 RegisterGlobalTemplate(vm_, TerminateThread, Fail, Signal);
149 TryCatch tryCatch(vm_);
150 std::string baseFileName = MODULE_ABC_PATH "termination_2.abc";
151 JSNApi::Execute(vm_, baseFileName, "termination_2");
152 EXPECT_TRUE(tryCatch.HasCaught());
153 terminatorThread.Join();
154 }
155
HWTEST_F_L0(ThreadTerminationTest,TerminateFromOtherThreadWithoutCall)156 HWTEST_F_L0(ThreadTerminationTest, TerminateFromOtherThreadWithoutCall)
157 {
158 JSThread *thread = vm_->GetAssociatedJSThread();
159 auto* factory = vm_->GetFactory();
160 JSHandle<GlobalEnv> globalEnv = factory->NewGlobalEnv();
161 SaveAndSwitchEnv(thread, globalEnv);
162 TerminatorThread terminatorThread(vm_);
163 terminatorThread.RunWithSleep();
164 JSNApi::EnableUserUncaughtErrorHandler(vm_);
165 RegisterGlobalTemplate(vm_, TerminateThread, Fail, Signal);
166 TryCatch tryCatch(vm_);
167 std::string baseFileName = MODULE_ABC_PATH "termination_3.abc";
168 JSNApi::Execute(vm_, baseFileName, "termination_3");
169 EXPECT_TRUE(tryCatch.HasCaught());
170 terminatorThread.Join();
171 }
172
HWTEST_F_L0(ThreadTerminationTest,TerminateClearArrayJoinStack)173 HWTEST_F_L0(ThreadTerminationTest, TerminateClearArrayJoinStack)
174 {
175 JSThread *thread = vm_->GetAssociatedJSThread();
176 auto* factory = vm_->GetFactory();
177 JSHandle<GlobalEnv> globalEnv = factory->NewGlobalEnv();
178 SaveAndSwitchEnv(thread, globalEnv);
179 JSNApi::EnableUserUncaughtErrorHandler(vm_);
180 RegisterGlobalTemplate(vm_, TerminateThread, Fail, Signal);
181 TryCatch tryCatch(vm_);
182 std::string baseFileName = MODULE_ABC_PATH "termination_4.abc";
183 JSNApi::Execute(vm_, baseFileName, "termination_4");
184 EXPECT_TRUE(tryCatch.HasCaught());
185 }
186
HWTEST_F_L0(ThreadTerminationTest,TerminateInMicroTask)187 HWTEST_F_L0(ThreadTerminationTest, TerminateInMicroTask)
188 {
189 JSThread *thread = vm_->GetAssociatedJSThread();
190 auto* factory = vm_->GetFactory();
191 JSHandle<GlobalEnv> globalEnv = factory->NewGlobalEnv();
192 SaveAndSwitchEnv(thread, globalEnv);
193 JSNApi::EnableUserUncaughtErrorHandler(vm_);
194 RegisterGlobalTemplate(vm_, TerminateThread, Fail, Signal);
195 TryCatch tryCatch(vm_);
196 std::string baseFileName = MODULE_ABC_PATH "termination_5.abc";
197 JSNApi::Execute(vm_, baseFileName, "termination_5");
198 }
199
HWTEST_F_L0(ThreadTerminationTest,TerminateWithoutExecutingMicroTask)200 HWTEST_F_L0(ThreadTerminationTest, TerminateWithoutExecutingMicroTask)
201 {
202 JSThread *thread = vm_->GetAssociatedJSThread();
203 auto* factory = vm_->GetFactory();
204 JSHandle<GlobalEnv> globalEnv = factory->NewGlobalEnv();
205 SaveAndSwitchEnv(thread, globalEnv);
206 JSNApi::EnableUserUncaughtErrorHandler(vm_);
207 RegisterGlobalTemplate(vm_, TerminateThread, Fail, Signal);
208 TryCatch tryCatch(vm_);
209 std::string baseFileName = MODULE_ABC_PATH "termination_6.abc";
210 JSNApi::Execute(vm_, baseFileName, "termination_6");
211 EXPECT_TRUE(tryCatch.HasCaught());
212 }
213 } // namespace panda::test
214