1 /*
2 * Copyright (c) 2024 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 #include "ecmascript/module/module_manager_helper.h"
16
17 #include "ecmascript/interpreter/slow_runtime_stub.h"
18 #include "ecmascript/jspandafile/js_pandafile_executor.h"
19 #include "ecmascript/require/js_cjs_module.h"
20 #include "ecmascript/module/module_path_helper.h"
21
22 namespace panda::ecmascript {
GetModuleValue(JSThread * thread,JSHandle<SourceTextModule> module,int index)23 JSTaggedValue ModuleManagerHelper::GetModuleValue(JSThread *thread, JSHandle<SourceTextModule> module, int index)
24 {
25 ModuleTypes moduleType = module->GetTypes();
26 if (SourceTextModule::IsNativeModule(moduleType) || SourceTextModule::IsCjsModule(moduleType)) {
27 return GetNativeOrCjsModuleValue(thread, module.GetTaggedValue(), index);
28 }
29 return module->GetModuleValue(thread, index, false);
30 }
31
GetNativeOrCjsModuleValue(JSThread * thread,JSTaggedValue resolvedModule,int32_t index)32 JSTaggedValue ModuleManagerHelper::GetNativeOrCjsModuleValue(JSThread *thread,
33 JSTaggedValue resolvedModule,
34 int32_t index)
35 {
36 JSHandle<JSTaggedValue> exports = GetNativeOrCjsExports(thread, resolvedModule);
37 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
38 return SourceTextModule::GetValueFromExportObject(thread, exports, index);
39 }
40
GetNativeOrCjsExports(JSThread * thread,JSTaggedValue resolvedModule)41 JSHandle<JSTaggedValue> ModuleManagerHelper::GetNativeOrCjsExports(JSThread *thread, JSTaggedValue resolvedModule)
42 {
43 JSHandle<SourceTextModule> module(thread, resolvedModule);
44 // if cjsModule is not JSObject, means cjs uses default exports.
45 JSMutableHandle<JSTaggedValue> exports(thread, thread->GlobalConstants()->GetUndefined());
46 ModuleTypes moduleType = module->GetTypes();
47 if (SourceTextModule::IsNativeModule(moduleType)) {
48 exports.Update(module->GetModuleValue(thread, 0, false));
49 if (!exports->IsJSObject()) {
50 LOG_FULL(WARN) << "Load native module failed, so is " << SourceTextModule::GetModuleName(resolvedModule);
51 }
52 return exports;
53 }
54 if (SourceTextModule::IsCjsModule(moduleType)) {
55 CString cjsModuleName = SourceTextModule::GetModuleName(module.GetTaggedValue());
56 JSHandle<JSTaggedValue> moduleNameHandle(thread->GetEcmaVM()->GetFactory()->NewFromUtf8(cjsModuleName));
57 exports.Update(CjsModule::SearchFromModuleCache(thread, moduleNameHandle).GetTaggedValue());
58 if (exports->IsHole()) {
59 CString errorMsg =
60 "Loading cjs module:" + SourceTextModule::GetModuleName(module.GetTaggedValue()) + ", failed";
61 JSHandle<JSTaggedValue> exception(thread, JSTaggedValue::Exception());
62 THROW_NEW_ERROR_WITH_MSG_AND_RETURN_VALUE(thread,
63 ErrorType::SYNTAX_ERROR, errorMsg.c_str(), exception);
64 }
65 return exports;
66 }
67 return exports;
68 }
69
GetModuleValue(JSThread * thread,JSHandle<SourceTextModule> module,JSTaggedValue bindingName)70 JSTaggedValue ModuleManagerHelper::GetModuleValue(JSThread *thread, JSHandle<SourceTextModule> module,
71 JSTaggedValue bindingName)
72 {
73 JSHandle<JSTaggedValue> exports = GetNativeOrCjsExports(thread, module.GetTaggedValue());
74 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
75 if (UNLIKELY(JSTaggedValue::SameValue(
76 bindingName, thread->GlobalConstants()->GetHandledDefaultString().GetTaggedValue()))) {
77 return exports.GetTaggedValue();
78 }
79 // need fix
80 return JSHandle<JSTaggedValue>(thread, SlowRuntimeStub::LdObjByName(thread,
81 exports.GetTaggedValue(),
82 bindingName,
83 false,
84 JSTaggedValue::Undefined())).GetTaggedValue();
85 }
86
GetModuleValueFromIndexBinding(JSThread * thread,JSHandle<SourceTextModule> module,JSTaggedValue resolvedBinding)87 JSTaggedValue ModuleManagerHelper::GetModuleValueFromIndexBinding(JSThread *thread,
88 JSHandle<SourceTextModule> module,
89 JSTaggedValue resolvedBinding)
90 {
91 JSHandle<ResolvedRecordIndexBinding> binding(thread, resolvedBinding);
92 JSHandle<SourceTextModule> resolvedModule = GetResolvedRecordIndexBindingModule(thread, module, binding);
93 return GetModuleValue(thread, resolvedModule, binding->GetIndex());
94 }
95
GetResolvedRecordIndexBindingModule(JSThread * thread,JSHandle<SourceTextModule> module,JSHandle<ResolvedRecordIndexBinding> binding)96 JSHandle<SourceTextModule> ModuleManagerHelper::GetResolvedRecordIndexBindingModule(
97 JSThread *thread, JSHandle<SourceTextModule> module, JSHandle<ResolvedRecordIndexBinding> binding)
98 {
99 JSHandle<JSTaggedValue> recordName(thread, binding->GetModuleRecord());
100 ASSERT(recordName->IsString());
101 // recordName is string, find at current vm
102 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
103 CString recordNameStr = ModulePathHelper::Utf8ConvertToString(recordName.GetTaggedValue());
104 if (!moduleManager->IsEvaluatedModule(recordNameStr)) {
105 auto isMergedAbc = !module->GetEcmaModuleRecordNameString().empty();
106 CString fileName = ModulePathHelper::Utf8ConvertToString((binding->GetAbcFileName()));
107 if (!JSPandaFileExecutor::LazyExecuteModule(thread,
108 recordNameStr, fileName, isMergedAbc)) { // LCOV_EXCL_BR_LINE
109 LOG_ECMA(FATAL) << "LazyExecuteModule failed";
110 }
111 }
112 return moduleManager->HostGetImportedModule(recordNameStr);
113 }
114
GetModuleValueFromRecordBinding(JSThread * thread,JSHandle<SourceTextModule> module,JSTaggedValue resolvedBinding)115 JSTaggedValue ModuleManagerHelper::GetModuleValueFromRecordBinding(JSThread *thread,
116 JSHandle<SourceTextModule> module,
117 JSTaggedValue resolvedBinding)
118 {
119 JSHandle<ResolvedRecordBinding> binding(thread, resolvedBinding);
120 JSHandle<SourceTextModule> resolvedModule = GetResolvedRecordBindingModule(thread, module, binding);
121 return GetModuleValue(thread, resolvedModule, binding->GetBindingName());
122 }
123
GetResolvedRecordBindingModule(JSThread * thread,JSHandle<SourceTextModule> module,JSHandle<ResolvedRecordBinding> binding)124 JSHandle<SourceTextModule> ModuleManagerHelper::GetResolvedRecordBindingModule(JSThread *thread,
125 JSHandle<SourceTextModule> module,
126 JSHandle<ResolvedRecordBinding> binding)
127 {
128 JSHandle<JSTaggedValue> recordName(thread, binding->GetModuleRecord());
129 ASSERT(recordName->IsString());
130 CString recordNameStr = ModulePathHelper::Utf8ConvertToString(recordName.GetTaggedValue());
131 // moduleRecord is string, find at current vm
132 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
133 if (!moduleManager->IsEvaluatedModule(recordNameStr)) {
134 auto isMergedAbc = !module->GetEcmaModuleRecordNameString().empty();
135 CString fileName = module->GetEcmaModuleFilenameString();
136 if (!JSPandaFileExecutor::LazyExecuteModule(thread,
137 recordNameStr, fileName, isMergedAbc)) { // LCOV_EXCL_BR_LINE
138 LOG_ECMA(FATAL) << "LazyExecuteModule failed";
139 }
140 }
141 return moduleManager->HostGetImportedModule(recordNameStr);
142 }
143
GetLazyModuleValueFromIndexBinding(JSThread * thread,JSHandle<SourceTextModule> module,JSTaggedValue resolvedBinding)144 JSTaggedValue ModuleManagerHelper::GetLazyModuleValueFromIndexBinding(JSThread *thread,
145 JSHandle<SourceTextModule> module,
146 JSTaggedValue resolvedBinding)
147 {
148 JSHandle<ResolvedRecordIndexBinding> binding(thread, resolvedBinding);
149 JSHandle<JSTaggedValue> recordName(thread, binding->GetModuleRecord());
150 ASSERT(recordName->IsString());
151 // moduleRecord is string, find at current vm
152 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
153 CString recordNameStr = ModulePathHelper::Utf8ConvertToString(recordName.GetTaggedValue());
154 JSHandle<SourceTextModule> resolvedModule;
155 if (moduleManager->IsLocalModuleLoaded(recordNameStr)) {
156 resolvedModule = moduleManager->HostGetImportedModule(recordNameStr);
157 if (!moduleManager->IsEvaluatedModule(recordNameStr)) {
158 SourceTextModule::Evaluate(thread, resolvedModule, nullptr);
159 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
160 }
161 } else {
162 auto isMergedAbc = !module->GetEcmaModuleRecordNameString().empty();
163 CString fileName = ModulePathHelper::Utf8ConvertToString(binding->GetAbcFileName());
164 if (!JSPandaFileExecutor::LazyExecuteModule(thread,
165 recordNameStr, fileName, isMergedAbc)) { // LCOV_EXCL_BR_LINE
166 LOG_ECMA(FATAL) << "LazyExecuteModule failed";
167 }
168 resolvedModule = moduleManager->HostGetImportedModule(recordNameStr);
169 }
170 return GetModuleValue(thread, resolvedModule, binding->GetIndex());
171 }
172
GetLazyModuleValueFromRecordBinding(JSThread * thread,JSHandle<SourceTextModule> module,JSTaggedValue resolvedBinding)173 JSTaggedValue ModuleManagerHelper::GetLazyModuleValueFromRecordBinding(
174 JSThread *thread, JSHandle<SourceTextModule> module, JSTaggedValue resolvedBinding)
175 {
176 JSHandle<ResolvedRecordBinding> binding(thread, resolvedBinding);
177 JSHandle<JSTaggedValue> recordName(thread, binding->GetModuleRecord());
178 ASSERT(recordName->IsString());
179 // moduleRecord is string, find at current vm
180 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
181 CString recordNameStr = ModulePathHelper::Utf8ConvertToString(recordName.GetTaggedValue());
182 JSHandle<SourceTextModule> resolvedModule;
183 if (moduleManager->IsLocalModuleLoaded(recordNameStr)) {
184 resolvedModule = moduleManager->HostGetImportedModule(recordNameStr);
185 if (!moduleManager->IsEvaluatedModule(recordNameStr)) {
186 const ModuleTypes moduleType = resolvedModule->GetTypes();
187 if (SourceTextModule::IsNativeModule(moduleType)) {
188 SourceTextModule::EvaluateNativeModule(thread, resolvedModule, moduleType);
189 } else {
190 SourceTextModule::Evaluate(thread, resolvedModule, nullptr);
191 }
192 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
193 }
194 } else {
195 auto isMergedAbc = !module->GetEcmaModuleRecordNameString().empty();
196 CString fileName = module->GetEcmaModuleFilenameString();
197 if (!JSPandaFileExecutor::LazyExecuteModule(thread,
198 recordNameStr, fileName, isMergedAbc)) { // LCOV_EXCL_BR_LINE
199 LOG_ECMA(FATAL) << "LazyExecuteModule failed";
200 }
201 resolvedModule = moduleManager->HostGetImportedModule(recordNameStr);
202 }
203 return GetModuleValue(thread, resolvedModule, binding->GetBindingName());
204 }
205
UpdateBindingAndGetModuleValue(JSThread * thread,JSHandle<SourceTextModule> module,JSHandle<SourceTextModule> requiredModule,int32_t index,JSTaggedValue bindingName)206 JSTaggedValue ModuleManagerHelper::UpdateBindingAndGetModuleValue(JSThread *thread, JSHandle<SourceTextModule> module,
207 JSHandle<SourceTextModule> requiredModule, int32_t index, JSTaggedValue bindingName)
208 {
209 // Get esm environment
210 JSHandle<JSTaggedValue> moduleEnvironment(thread, module->GetEnvironment());
211 ASSERT(!moduleEnvironment->IsUndefined());
212 JSHandle<TaggedArray> environment = JSHandle<TaggedArray>::Cast(moduleEnvironment);
213 // rebinding here
214 JSHandle<JSTaggedValue> exports = GetNativeOrCjsExports(thread, requiredModule.GetTaggedValue());
215 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
216 JSHandle<JSTaggedValue> exportName(thread, bindingName);
217 JSHandle<JSTaggedValue> resolution =
218 SourceTextModule::ResolveExportObject(thread, requiredModule, exports, exportName);
219 // ii. If resolution is null or "ambiguous", throw a SyntaxError exception.
220 if (resolution->IsNull() || resolution->IsString()) {
221 CString requestMod = ModulePathHelper::ReformatPath(SourceTextModule::GetModuleName(
222 requiredModule.GetTaggedValue()));
223 CString recordStr = ModulePathHelper::ReformatPath(SourceTextModule::GetModuleName(
224 module.GetTaggedValue()));
225 CString msg = "the requested module '" + requestMod + SourceTextModule::GetResolveErrorReason(resolution) +
226 ModulePathHelper::Utf8ConvertToString(bindingName) +
227 "' which imported by '" + recordStr + "'";
228 THROW_NEW_ERROR_WITH_MSG_AND_RETURN_VALUE(
229 thread, ErrorType::SYNTAX_ERROR, msg.c_str(), JSTaggedValue::Exception());
230 }
231 // iii. Call envRec.CreateImportBinding(
232 // in.[[LocalName]], resolution.[[Module]], resolution.[[BindingName]]).
233 environment->Set(thread, index, resolution);
234 ASSERT(resolution->IsResolvedIndexBinding());
235 ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolution.GetTaggedValue());
236 return SourceTextModule::GetValueFromExportObject(thread, exports, binding->GetIndex());
237 }
238 } // namespace panda::ecmascript
239