1 /*
2 * Copyright (c) 2025 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 "ecmascript/builtins/builtins_promise_job.h"
17 #include "ecmascript/global_env.h"
18 #include "ecmascript/tests/test_helper.h"
19 #include "ecmascript/module/napi_module_loader.h"
20 #include "ecmascript/ecma_vm.h"
21 #include "ecmascript/js_promise.h"
22 #include "ecmascript/js_array.h"
23
24 using namespace panda;
25 using namespace panda::ecmascript;
26 using namespace panda::ecmascript::builtins;
27
28 using FunctionCallbackInfo = Local<JSValueRef> (*)(JsiRuntimeCallInfo *);
29 namespace panda::test {
30 class BuiltinsPromiseJobTest : public BaseTestWithScope<true> {
31 public:
32 static Local<JSValueRef> MockGetModule(JsiRuntimeCallInfo *runtimeCallInfo);
33 static Local<JSValueRef> MockGetModuleJSError(JsiRuntimeCallInfo *runtimeCallInfo);
34 };
35
MockGetModule(JsiRuntimeCallInfo * runtimeCallInfo)36 Local<JSValueRef> BuiltinsPromiseJobTest::MockGetModule(JsiRuntimeCallInfo *runtimeCallInfo)
37 {
38 auto *thread = runtimeCallInfo->GetThread();
39 auto vm = thread->GetEcmaVM();
40 Local<StringRef> requestPath = StringRef::NewFromUtf8(vm, "requestPath");
41 Local<ObjectRef> exportObejct = ObjectRef::New(vm);
42 exportObejct->Set(vm, requestPath, runtimeCallInfo->GetCallArgRef(0));
43 return exportObejct;
44 }
45
MockGetModuleJSError(JsiRuntimeCallInfo * runtimeCallInfo)46 Local<JSValueRef> BuiltinsPromiseJobTest::MockGetModuleJSError(JsiRuntimeCallInfo *runtimeCallInfo)
47 {
48 auto *thread = runtimeCallInfo->GetThread();
49 auto vm = thread->GetEcmaVM();
50 JsiFastNativeScope fastNativeScope(vm);
51 Local<JSValueRef> error(JSValueRef::Undefined(vm));
52 error = Exception::Error(vm, runtimeCallInfo->GetCallArgRef(0));
53 Local<JSValueRef> codeKey = StringRef::NewFromUtf8(vm, "code");
54 Local<JSValueRef> codeValue = runtimeCallInfo->GetCallArgRef(0);
55 Local<ObjectRef> errorObj(error);
56 errorObj->Set(vm, codeKey, codeValue);
57 JSNApi::ThrowException(vm, error);
58 return runtimeCallInfo->GetCallArgRef(0);
59 }
60
61 // dynamic import static module after load 1.0 module failed
HWTEST_F_L0(BuiltinsPromiseJobTest,DynamicImportJobCatchException)62 HWTEST_F_L0(BuiltinsPromiseJobTest, DynamicImportJobCatchException)
63 {
64 /**
65 * Both the handle and the stack are allocated using maloc.
66 * When newJsError is called, the C interpreter will step back one frame before executing.
67 * In the UT, there is only one frame, and stepping back causes it to step on the handle address.
68 * This is a special scenario caused by the UT, and it would not occur during normal execution.
69 */
70 if (!thread->IsAsmInterpreter()) {
71 return;
72 }
73 auto vm = thread->GetEcmaVM();
74 ObjectFactory *factory = vm->GetFactory();
75 JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
76
77 auto globalConstants = thread->GlobalConstants();
78 JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
79 EXPECT_TRUE(arr != nullptr);
80 JSHandle<JSTaggedValue> pandaObject(thread, arr);
81 JSTaggedValue::SetProperty(thread, pandaObject,
82 globalConstants->GetHandledGetModuleString(),
83 JSNApiHelper::ToJSHandle(FunctionRef::New(const_cast<panda::EcmaVM*>(vm), MockGetModule)));
84 Local<ObjectRef> globalObject = JSNApi::GetGlobalObject(vm);
85 globalObject->Set(vm,
86 JSNApiHelper::ToLocal<StringRef>(globalConstants->GetHandledPandaString()),
87 JSNApiHelper::ToLocal<JSValueRef>(pandaObject));
88
89 JSHandle<JSTaggedValue> promiseFunc = env->GetPromiseFunction();
90 JSHandle<JSPromise> jsPromise =
91 JSHandle<JSPromise>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(promiseFunc), promiseFunc));
92 JSHandle<ResolvingFunctionsRecord> resolvingFunctions =
93 JSPromise::CreateResolvingFunctions(thread, jsPromise);
94 JSHandle<JSPromiseReactionsFunction> resolve(thread, resolvingFunctions->GetResolveFunction(thread));
95 JSHandle<JSPromiseReactionsFunction> reject(thread, resolvingFunctions->GetRejectFunction(thread));
96 JSHandle<JSTaggedValue> dirPath(factory->NewFromASCII("./main.abc"));
97 JSHandle<JSTaggedValue> specifier(factory->NewFromASCII("exportFile"));
98 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 14);
99 Local<JSValueRef> contextValue = JSNApi::GetCurrentContext(vm);
100 JSHandle<LexicalEnv> lexicalEnv(JSNApiHelper::ToJSHandle(contextValue));
101 JSHandle<JSFunction> funHandle = factory->NewJSFunction(env);
102 funHandle->SetLexicalEnv(thread, lexicalEnv.GetTaggedValue());
103 ecmaRuntimeCallInfo->SetFunction(funHandle.GetTaggedValue());
104 ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
105 ecmaRuntimeCallInfo->SetCallArg(0, resolve.GetTaggedValue());
106 ecmaRuntimeCallInfo->SetCallArg(1, reject.GetTaggedValue());
107 ecmaRuntimeCallInfo->SetCallArg(2, dirPath.GetTaggedValue());
108 ecmaRuntimeCallInfo->SetCallArg(3, specifier.GetTaggedValue());
109 ecmaRuntimeCallInfo->SetCallArg(4, JSTaggedValue::Undefined());
110 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
111 BuiltinsPromiseJob::DynamicImportJob(ecmaRuntimeCallInfo);
112 TestHelper::TearDownFrame(thread, prev);
113 JSHandle<JSTaggedValue> result(thread, jsPromise->GetPromiseResult(thread));
114 EXPECT_EQ(result->IsJSProxy(), true);
115 JSHandle<JSTaggedValue> requestPath(factory->NewFromASCII("requestPath"));
116 EXPECT_EQ(JSTaggedValue::SameValue(thread, JSTaggedValue::GetProperty(thread, result, requestPath).GetValue(),
117 specifier), true);
118 }
119
120 // throw 1.2 load failed
HWTEST_F_L0(BuiltinsPromiseJobTest,DynamicImportJobCatchException2)121 HWTEST_F_L0(BuiltinsPromiseJobTest, DynamicImportJobCatchException2)
122 {
123 /**
124 * Both the handle and the stack are allocated using maloc.
125 * When newJsError is called, the C interpreter will step back one frame before executing.
126 * In the UT, there is only one frame, and stepping back causes it to step on the handle address.
127 * This is a special scenario caused by the UT, and it would not occur during normal execution.
128 */
129 if (!thread->IsAsmInterpreter()) {
130 return;
131 }
132 auto vm = thread->GetEcmaVM();
133 ObjectFactory *factory = vm->GetFactory();
134 JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
135
136 auto globalConstants = thread->GlobalConstants();
137 JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
138 EXPECT_TRUE(arr != nullptr);
139 JSHandle<JSTaggedValue> pandaObject(thread, arr);
140 JSTaggedValue::SetProperty(thread, pandaObject,
141 globalConstants->GetHandledGetModuleString(),
142 JSNApiHelper::ToJSHandle(FunctionRef::New(const_cast<panda::EcmaVM*>(vm), MockGetModuleJSError)));
143 Local<ObjectRef> globalObject = JSNApi::GetGlobalObject(vm);
144 globalObject->Set(vm,
145 JSNApiHelper::ToLocal<StringRef>(globalConstants->GetHandledPandaString()),
146 JSNApiHelper::ToLocal<JSValueRef>(pandaObject));
147
148 JSHandle<JSTaggedValue> promiseFunc = env->GetPromiseFunction();
149 JSHandle<JSPromise> jsPromise =
150 JSHandle<JSPromise>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(promiseFunc), promiseFunc));
151 JSHandle<ResolvingFunctionsRecord> resolvingFunctions =
152 JSPromise::CreateResolvingFunctions(thread, jsPromise);
153 JSHandle<JSPromiseReactionsFunction> resolve(thread, resolvingFunctions->GetResolveFunction(thread));
154 JSHandle<JSPromiseReactionsFunction> reject(thread, resolvingFunctions->GetRejectFunction(thread));
155 JSHandle<JSTaggedValue> dirPath(factory->NewFromASCII("./main.abc"));
156 JSHandle<JSTaggedValue> specifier(factory->NewFromASCII("exportFile"));
157 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 14);
158 Local<JSValueRef> contextValue = JSNApi::GetCurrentContext(vm);
159 JSHandle<LexicalEnv> lexicalEnv(JSNApiHelper::ToJSHandle(contextValue));
160 JSHandle<JSFunction> funHandle = factory->NewJSFunction(env);
161 funHandle->SetLexicalEnv(thread, lexicalEnv.GetTaggedValue());
162 ecmaRuntimeCallInfo->SetFunction(funHandle.GetTaggedValue());
163 ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
164 ecmaRuntimeCallInfo->SetCallArg(0, resolve.GetTaggedValue());
165 ecmaRuntimeCallInfo->SetCallArg(1, reject.GetTaggedValue());
166 ecmaRuntimeCallInfo->SetCallArg(2, dirPath.GetTaggedValue());
167 ecmaRuntimeCallInfo->SetCallArg(3, specifier.GetTaggedValue());
168 ecmaRuntimeCallInfo->SetCallArg(4, JSTaggedValue::Undefined());
169 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
170 BuiltinsPromiseJob::DynamicImportJob(ecmaRuntimeCallInfo);
171 TestHelper::TearDownFrame(thread, prev);
172 JSHandle<JSTaggedValue> error(thread, jsPromise->GetPromiseResult(thread));
173 JSHandle<JSTaggedValue> code(factory->NewFromASCII("code"));
174 JSHandle<JSTaggedValue> message = JSTaggedValue::GetProperty(thread, error, code).GetValue();
175 EXPECT_EQ(JSTaggedValue::SameValue(thread, message, specifier), true);
176 thread->ClearException();
177 }
178 }
179