/src/serenity/Userland/Libraries/LibJS/SourceTextModule.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2021-2023, Andreas Kling <kling@serenityos.org> |
3 | | * Copyright (c) 2022, David Tuin <davidot@serenityos.org> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <AK/Debug.h> |
9 | | #include <AK/QuickSort.h> |
10 | | #include <LibJS/Bytecode/Interpreter.h> |
11 | | #include <LibJS/Parser.h> |
12 | | #include <LibJS/Runtime/AsyncFunctionDriverWrapper.h> |
13 | | #include <LibJS/Runtime/ECMAScriptFunctionObject.h> |
14 | | #include <LibJS/Runtime/GlobalEnvironment.h> |
15 | | #include <LibJS/Runtime/ModuleEnvironment.h> |
16 | | #include <LibJS/Runtime/PromiseCapability.h> |
17 | | #include <LibJS/SourceTextModule.h> |
18 | | |
19 | | namespace JS { |
20 | | |
21 | | JS_DEFINE_ALLOCATOR(SourceTextModule); |
22 | | |
23 | | // 16.2.2.2 Static Semantics: WithClauseToAttributes, https://tc39.es/proposal-import-attributes/#sec-with-clause-to-attributes |
24 | | static Vector<ImportAttribute> with_clause_to_assertions(Vector<ImportAttribute> const& source_attributes) |
25 | 0 | { |
26 | | // WithClause : AttributesKeyword { WithEntries , opt } |
27 | | // 1. Let attributes be WithClauseToAttributes of WithEntries. |
28 | 0 | Vector<ImportAttribute> attributes; |
29 | | |
30 | | // AssertEntries : AssertionKey : StringLiteral |
31 | | // AssertEntries : AssertionKey : StringLiteral , WithEntries |
32 | |
|
33 | 0 | for (auto const& attribute : source_attributes) { |
34 | | // 1. Let key be the PropName of AttributeKey. |
35 | | // 2. Let entry be the ImportAttribute Record { [[Key]]: key, [[Value]]: SV of StringLiteral }. |
36 | | // 3. Return « entry ». |
37 | 0 | attributes.empend(attribute); |
38 | 0 | } |
39 | | |
40 | | // 2. Sort attributes according to the lexicographic order of their [[Key]] fields, treating the value of each such field as a sequence of UTF-16 code unit values. NOTE: This sorting is observable only in that hosts are prohibited from distinguishing among attributes by the order they occur in. |
41 | | // Note: The sorting is done in construction of the ModuleRequest object. |
42 | | |
43 | | // 3. Return attributes. |
44 | 0 | return attributes; |
45 | 0 | } |
46 | | |
47 | | // 16.2.1.3 Static Semantics: ModuleRequests, https://tc39.es/ecma262/#sec-static-semantics-modulerequests |
48 | | static Vector<ModuleRequest> module_requests(Program& program) |
49 | 0 | { |
50 | | // A List of all the ModuleSpecifier strings used by the module represented by this record to request the importation of a module. |
51 | | // Note: The List is source text occurrence ordered! |
52 | 0 | struct RequestedModuleAndSourceIndex { |
53 | 0 | u32 source_offset { 0 }; |
54 | 0 | ModuleRequest const* module_request { nullptr }; |
55 | 0 | }; |
56 | |
|
57 | 0 | Vector<RequestedModuleAndSourceIndex> requested_modules_with_indices; |
58 | |
|
59 | 0 | for (auto& import_statement : program.imports()) |
60 | 0 | requested_modules_with_indices.empend(import_statement->start_offset(), &import_statement->module_request()); |
61 | |
|
62 | 0 | for (auto& export_statement : program.exports()) { |
63 | 0 | for (auto& export_entry : export_statement->entries()) { |
64 | 0 | if (!export_entry.is_module_request()) |
65 | 0 | continue; |
66 | 0 | requested_modules_with_indices.empend(export_statement->start_offset(), &export_statement->module_request()); |
67 | 0 | } |
68 | 0 | } |
69 | | |
70 | | // Note: The List is source code occurrence ordered. https://tc39.es/proposal-import-attributes/#table-cyclic-module-fields |
71 | 0 | quick_sort(requested_modules_with_indices, [&](RequestedModuleAndSourceIndex const& lhs, RequestedModuleAndSourceIndex const& rhs) { |
72 | 0 | return lhs.source_offset < rhs.source_offset; |
73 | 0 | }); |
74 | |
|
75 | 0 | Vector<ModuleRequest> requested_modules_in_source_order; |
76 | 0 | requested_modules_in_source_order.ensure_capacity(requested_modules_with_indices.size()); |
77 | 0 | for (auto& module : requested_modules_with_indices) { |
78 | | // 16.2.1.3 Static Semantics: ModuleRequests, https://tc39.es/proposal-import-attributes/#sec-static-semantics-modulerequests |
79 | 0 | if (module.module_request->attributes.is_empty()) { |
80 | | // ExportDeclaration : export ExportFromClause FromClause ; |
81 | | // ImportDeclaration : import ImportClause FromClause ; |
82 | | |
83 | | // 2. Let specifier be SV of FromClause. |
84 | | // 3. Return a List whose sole element is the ModuleRequest Record { [[Specifer]]: specifier, [[Attributes]]: « » }. |
85 | 0 | requested_modules_in_source_order.empend(module.module_request->module_specifier); |
86 | 0 | } else { |
87 | | // ExportDeclaration : export ExportFromClause FromClause WithClause ; |
88 | | // ImportDeclaration : import ImportClause FromClause WithClause ; |
89 | | |
90 | | // 1. Let specifier be the SV of FromClause. |
91 | | // 2. Let attributes be WithClauseToAttributes of WithClause. |
92 | 0 | auto attributes = with_clause_to_assertions(module.module_request->attributes); |
93 | | // NOTE: We have to modify the attributes in place because else it might keep unsupported ones. |
94 | 0 | const_cast<ModuleRequest*>(module.module_request)->attributes = move(attributes); |
95 | | |
96 | | // 3. Return a List whose sole element is the ModuleRequest Record { [[Specifer]]: specifier, [[Attributes]]: attributes }. |
97 | 0 | requested_modules_in_source_order.empend(module.module_request->module_specifier, module.module_request->attributes); |
98 | 0 | } |
99 | 0 | } |
100 | |
|
101 | 0 | return requested_modules_in_source_order; |
102 | 0 | } |
103 | | |
104 | | SourceTextModule::SourceTextModule(Realm& realm, StringView filename, Script::HostDefined* host_defined, bool has_top_level_await, NonnullRefPtr<Program> body, Vector<ModuleRequest> requested_modules, |
105 | | Vector<ImportEntry> import_entries, Vector<ExportEntry> local_export_entries, |
106 | | Vector<ExportEntry> indirect_export_entries, Vector<ExportEntry> star_export_entries, |
107 | | RefPtr<ExportStatement const> default_export) |
108 | 0 | : CyclicModule(realm, filename, has_top_level_await, move(requested_modules), host_defined) |
109 | 0 | , m_ecmascript_code(move(body)) |
110 | 0 | , m_execution_context(ExecutionContext::create()) |
111 | 0 | , m_import_entries(move(import_entries)) |
112 | 0 | , m_local_export_entries(move(local_export_entries)) |
113 | 0 | , m_indirect_export_entries(move(indirect_export_entries)) |
114 | 0 | , m_star_export_entries(move(star_export_entries)) |
115 | 0 | , m_default_export(move(default_export)) |
116 | 0 | { |
117 | 0 | } |
118 | | |
119 | 0 | SourceTextModule::~SourceTextModule() = default; |
120 | | |
121 | | void SourceTextModule::visit_edges(Cell::Visitor& visitor) |
122 | 0 | { |
123 | 0 | Base::visit_edges(visitor); |
124 | 0 | visitor.visit(m_import_meta); |
125 | 0 | m_execution_context->visit_edges(visitor); |
126 | 0 | } |
127 | | |
128 | | // 16.2.1.6.1 ParseModule ( sourceText, realm, hostDefined ), https://tc39.es/ecma262/#sec-parsemodule |
129 | | Result<NonnullGCPtr<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse(StringView source_text, Realm& realm, StringView filename, Script::HostDefined* host_defined) |
130 | 0 | { |
131 | | // 1. Let body be ParseText(sourceText, Module). |
132 | 0 | auto parser = Parser(Lexer(source_text, filename), Program::Type::Module); |
133 | 0 | auto body = parser.parse_program(); |
134 | | |
135 | | // 2. If body is a List of errors, return body. |
136 | 0 | if (parser.has_errors()) |
137 | 0 | return parser.errors(); |
138 | | |
139 | | // 3. Let requestedModules be the ModuleRequests of body. |
140 | 0 | auto requested_modules = module_requests(*body); |
141 | | |
142 | | // 4. Let importEntries be ImportEntries of body. |
143 | 0 | Vector<ImportEntry> import_entries; |
144 | 0 | for (auto const& import_statement : body->imports()) |
145 | 0 | import_entries.extend(import_statement->entries()); |
146 | | |
147 | | // 5. Let importedBoundNames be ImportedLocalNames(importEntries). |
148 | | // Note: Since we have to potentially extract the import entry we just use importEntries |
149 | | // In the future it might be an optimization to have a set/map of string to speed up the search. |
150 | | |
151 | | // 6. Let indirectExportEntries be a new empty List. |
152 | 0 | Vector<ExportEntry> indirect_export_entries; |
153 | | |
154 | | // 7. Let localExportEntries be a new empty List. |
155 | 0 | Vector<ExportEntry> local_export_entries; |
156 | | |
157 | | // 8. Let starExportEntries be a new empty List. |
158 | 0 | Vector<ExportEntry> star_export_entries; |
159 | | |
160 | | // Note: Not in the spec but makes it easier to find the default. |
161 | 0 | RefPtr<ExportStatement const> default_export; |
162 | | |
163 | | // 9. Let exportEntries be ExportEntries of body. |
164 | | // 10. For each ExportEntry Record ee of exportEntries, do |
165 | 0 | for (auto const& export_statement : body->exports()) { |
166 | |
|
167 | 0 | if (export_statement->is_default_export()) { |
168 | 0 | VERIFY(!default_export); |
169 | 0 | VERIFY(export_statement->entries().size() == 1); |
170 | 0 | VERIFY(export_statement->has_statement()); |
171 | | |
172 | 0 | auto const& entry = export_statement->entries()[0]; |
173 | 0 | VERIFY(entry.kind == ExportEntry::Kind::NamedExport); |
174 | 0 | VERIFY(!entry.is_module_request()); |
175 | 0 | VERIFY(import_entries.find_if( |
176 | 0 | [&](ImportEntry const& import_entry) { |
177 | 0 | return import_entry.local_name == entry.local_or_import_name; |
178 | 0 | }) |
179 | 0 | .is_end()); |
180 | 0 | default_export = export_statement; |
181 | 0 | } |
182 | | |
183 | 0 | for (auto const& export_entry : export_statement->entries()) { |
184 | | |
185 | | // Special case, export {} from "module" should add "module" to |
186 | | // required_modules but not any import or export so skip here. |
187 | 0 | if (export_entry.kind == ExportEntry::Kind::EmptyNamedExport) { |
188 | 0 | VERIFY(export_statement->entries().size() == 1); |
189 | 0 | break; |
190 | 0 | } |
191 | | |
192 | | // a. If ee.[[ModuleRequest]] is null, then |
193 | 0 | if (!export_entry.is_module_request()) { |
194 | |
|
195 | 0 | auto in_imported_bound_names = import_entries.find_if( |
196 | 0 | [&](ImportEntry const& import_entry) { |
197 | 0 | return import_entry.local_name == export_entry.local_or_import_name; |
198 | 0 | }); |
199 | | |
200 | | // i. If ee.[[LocalName]] is not an element of importedBoundNames, then |
201 | 0 | if (in_imported_bound_names.is_end()) { |
202 | | // 1. Append ee to localExportEntries. |
203 | 0 | local_export_entries.empend(export_entry); |
204 | 0 | } |
205 | | // ii. Else, |
206 | 0 | else { |
207 | | // 1. Let ie be the element of importEntries whose [[LocalName]] is the same as ee.[[LocalName]]. |
208 | 0 | auto& import_entry = *in_imported_bound_names; |
209 | | |
210 | | // 2. If ie.[[ImportName]] is namespace-object, then |
211 | 0 | if (import_entry.is_namespace()) { |
212 | | // a. NOTE: This is a re-export of an imported module namespace object. |
213 | | // b. Append ee to localExportEntries. |
214 | 0 | local_export_entries.empend(export_entry); |
215 | 0 | } |
216 | | // 3. Else, |
217 | 0 | else { |
218 | | // a. NOTE: This is a re-export of a single name. |
219 | | // b. Append the ExportEntry Record { [[ModuleRequest]]: ie.[[ModuleRequest]], [[ImportName]]: ie.[[ImportName]], [[LocalName]]: null, [[ExportName]]: ee.[[ExportName]] } to indirectExportEntries. |
220 | 0 | indirect_export_entries.empend(ExportEntry::indirect_export_entry(import_entry.module_request(), export_entry.export_name, import_entry.import_name)); |
221 | 0 | } |
222 | 0 | } |
223 | 0 | } |
224 | | // b. Else if ee.[[ImportName]] is all-but-default, then |
225 | 0 | else if (export_entry.kind == ExportEntry::Kind::ModuleRequestAllButDefault) { |
226 | | // i. Assert: ee.[[ExportName]] is null. |
227 | 0 | VERIFY(!export_entry.export_name.has_value()); |
228 | | // ii. Append ee to starExportEntries. |
229 | 0 | star_export_entries.empend(export_entry); |
230 | 0 | } |
231 | | // c. Else, |
232 | 0 | else { |
233 | | // i. Append ee to indirectExportEntries. |
234 | 0 | indirect_export_entries.empend(export_entry); |
235 | 0 | } |
236 | 0 | } |
237 | 0 | } |
238 | | |
239 | | // 11. Let async be body Contains await. |
240 | 0 | bool async = body->has_top_level_await(); |
241 | | |
242 | | // 12. Return Source Text Module Record { |
243 | | // [[Realm]]: realm, [[Environment]]: empty, [[Namespace]]: empty, [[CycleRoot]]: empty, [[HasTLA]]: async, |
244 | | // [[AsyncEvaluation]]: false, [[TopLevelCapability]]: empty, [[AsyncParentModules]]: « », |
245 | | // [[PendingAsyncDependencies]]: empty, [[Status]]: unlinked, [[EvaluationError]]: empty, |
246 | | // [[HostDefined]]: hostDefined, [[ECMAScriptCode]]: body, [[Context]]: empty, [[ImportMeta]]: empty, |
247 | | // [[RequestedModules]]: requestedModules, [[ImportEntries]]: importEntries, [[LocalExportEntries]]: localExportEntries, |
248 | | // [[IndirectExportEntries]]: indirectExportEntries, [[StarExportEntries]]: starExportEntries, [[DFSIndex]]: empty, [[DFSAncestorIndex]]: empty }. |
249 | 0 | return realm.heap().allocate_without_realm<SourceTextModule>( |
250 | 0 | realm, |
251 | 0 | filename, |
252 | 0 | host_defined, |
253 | 0 | async, |
254 | 0 | move(body), |
255 | 0 | move(requested_modules), |
256 | 0 | move(import_entries), |
257 | 0 | move(local_export_entries), |
258 | 0 | move(indirect_export_entries), |
259 | 0 | move(star_export_entries), |
260 | 0 | move(default_export)); |
261 | 0 | } |
262 | | |
263 | | // 16.2.1.6.2 GetExportedNames ( [ exportStarSet ] ), https://tc39.es/ecma262/#sec-getexportednames |
264 | | ThrowCompletionOr<Vector<DeprecatedFlyString>> SourceTextModule::get_exported_names(VM& vm, Vector<Module*> export_star_set) |
265 | 0 | { |
266 | 0 | dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] get_export_names of {}", filename()); |
267 | | // 1. Assert: module.[[Status]] is not new. |
268 | 0 | VERIFY(m_status != ModuleStatus::New); |
269 | | |
270 | | // 2. If exportStarSet is not present, set exportStarSet to a new empty List. |
271 | | // Note: This is done by default argument |
272 | | |
273 | | // 3. If exportStarSet contains module, then |
274 | 0 | if (export_star_set.contains_slow(this)) { |
275 | | // a. Assert: We've reached the starting point of an export * circularity. |
276 | | // FIXME: How do we check that? |
277 | | |
278 | | // b. Return a new empty List. |
279 | 0 | return Vector<DeprecatedFlyString> {}; |
280 | 0 | } |
281 | | |
282 | | // 4. Append module to exportStarSet. |
283 | 0 | export_star_set.append(this); |
284 | | |
285 | | // 5. Let exportedNames be a new empty List. |
286 | 0 | Vector<DeprecatedFlyString> exported_names; |
287 | | |
288 | | // 6. For each ExportEntry Record e of module.[[LocalExportEntries]], do |
289 | 0 | for (auto& entry : m_local_export_entries) { |
290 | | // a. Assert: module provides the direct binding for this export. |
291 | | // FIXME: How do we check that? |
292 | | |
293 | | // b. Assert: e.[[ExportName]] is not null. |
294 | 0 | VERIFY(entry.export_name.has_value()); |
295 | | |
296 | | // c. Append e.[[ExportName]] to exportedNames. |
297 | 0 | exported_names.empend(entry.export_name.value()); |
298 | 0 | } |
299 | | |
300 | | // 7. For each ExportEntry Record e of module.[[IndirectExportEntries]], do |
301 | 0 | for (auto& entry : m_indirect_export_entries) { |
302 | | // a. a. Assert: module imports a specific binding for this export. |
303 | | // FIXME: How do we check that? |
304 | | |
305 | | // b. Assert: e.[[ExportName]] is not null. |
306 | 0 | VERIFY(entry.export_name.has_value()); |
307 | | |
308 | | // c. Append e.[[ExportName]] to exportedNames. |
309 | 0 | exported_names.empend(entry.export_name.value()); |
310 | 0 | } |
311 | | |
312 | | // 8. For each ExportEntry Record e of module.[[StarExportEntries]], do |
313 | 0 | for (auto& entry : m_star_export_entries) { |
314 | | // a. Assert: e.[[ModuleRequest]] is not null. |
315 | | // b. Let requestedModule be GetImportedModule(module, e.[[ModuleRequest]]). |
316 | 0 | auto requested_module = get_imported_module(entry.module_request()); |
317 | | |
318 | | // c. Let starNames be ? requestedModule.GetExportedNames(exportStarSet). |
319 | 0 | auto star_names = TRY(requested_module->get_exported_names(vm, export_star_set)); |
320 | | |
321 | | // d. For each element n of starNames, do |
322 | 0 | for (auto& name : star_names) { |
323 | | // i. If SameValue(n, "default") is false, then |
324 | 0 | if (name != "default"sv) { |
325 | | // 1. If n is not an element of exportedNames, then |
326 | 0 | if (!exported_names.contains_slow(name)) { |
327 | | // a. Append n to exportedNames. |
328 | 0 | exported_names.empend(name); |
329 | 0 | } |
330 | 0 | } |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | | // 9. Return exportedNames. |
335 | 0 | return exported_names; |
336 | 0 | } |
337 | | |
338 | | // 16.2.1.6.4 InitializeEnvironment ( ), https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment |
339 | | ThrowCompletionOr<void> SourceTextModule::initialize_environment(VM& vm) |
340 | 0 | { |
341 | | // 1. For each ExportEntry Record e of module.[[IndirectExportEntries]], do |
342 | 0 | for (auto& entry : m_indirect_export_entries) { |
343 | | // a. Let resolution be ? module.ResolveExport(e.[[ExportName]]). |
344 | 0 | auto resolution = TRY(resolve_export(vm, entry.export_name.value())); |
345 | | // b. If resolution is null or ambiguous, throw a SyntaxError exception. |
346 | 0 | if (!resolution.is_valid()) |
347 | 0 | return vm.throw_completion<SyntaxError>(ErrorType::InvalidOrAmbiguousExportEntry, entry.export_name); |
348 | | |
349 | | // c. Assert: resolution is a ResolvedBinding Record. |
350 | 0 | VERIFY(resolution.is_valid()); |
351 | 0 | } |
352 | | |
353 | | // 2. Assert: All named exports from module are resolvable. |
354 | | // Note: We check all the indirect export entries above in step 1 and all |
355 | | // the local named exports are resolvable by construction. |
356 | | |
357 | | // 3. Let realm be module.[[Realm]]. |
358 | | // 4. Assert: realm is not undefined. |
359 | | // Note: This must be true because we use a reference. |
360 | | |
361 | | // 5. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]). |
362 | 0 | auto environment = vm.heap().allocate_without_realm<ModuleEnvironment>(&realm().global_environment()); |
363 | | |
364 | | // 6. Set module.[[Environment]] to env. |
365 | 0 | set_environment(environment); |
366 | | |
367 | | // 7. For each ImportEntry Record in of module.[[ImportEntries]], do |
368 | 0 | for (auto& import_entry : m_import_entries) { |
369 | | // a. Let importedModule be GetImportedModule(module, in.[[ModuleRequest]]). |
370 | 0 | auto imported_module = get_imported_module(import_entry.module_request()); |
371 | | // b. NOTE: The above call cannot fail because imported module requests are a subset of module.[[RequestedModules]], and these have been resolved earlier in this algorithm. |
372 | | |
373 | | // c. If in.[[ImportName]] is namespace-object, then |
374 | 0 | if (import_entry.is_namespace()) { |
375 | | // i. Let namespace be ? GetModuleNamespace(importedModule). |
376 | 0 | auto* namespace_ = TRY(imported_module->get_module_namespace(vm)); |
377 | | |
378 | | // ii. Perform ! env.CreateImmutableBinding(in.[[LocalName]], true). |
379 | 0 | MUST(environment->create_immutable_binding(vm, import_entry.local_name, true)); |
380 | | |
381 | | // iii. Perform ! env.InitializeBinding(in.[[LocalName]], namespace, normal). |
382 | 0 | MUST(environment->initialize_binding(vm, import_entry.local_name, namespace_, Environment::InitializeBindingHint::Normal)); |
383 | 0 | } |
384 | | // d. Else, |
385 | 0 | else { |
386 | | // i. Let resolution be ? importedModule.ResolveExport(in.[[ImportName]]). |
387 | 0 | auto resolution = TRY(imported_module->resolve_export(vm, import_entry.import_name.value())); |
388 | | |
389 | | // ii. If resolution is null or ambiguous, throw a SyntaxError exception. |
390 | 0 | if (!resolution.is_valid()) |
391 | 0 | return vm.throw_completion<SyntaxError>(ErrorType::InvalidOrAmbiguousExportEntry, import_entry.import_name); |
392 | | |
393 | | // iii. If resolution.[[BindingName]] is namespace, then |
394 | 0 | if (resolution.is_namespace()) { |
395 | | // 1. Let namespace be ? GetModuleNamespace(resolution.[[Module]]). |
396 | 0 | auto* namespace_ = TRY(resolution.module->get_module_namespace(vm)); |
397 | | |
398 | | // 2. Perform ! env.CreateImmutableBinding(in.[[LocalName]], true). |
399 | 0 | MUST(environment->create_immutable_binding(vm, import_entry.local_name, true)); |
400 | | |
401 | | // 3. Perform ! env.InitializeBinding(in.[[LocalName]], namespace, normal). |
402 | 0 | MUST(environment->initialize_binding(vm, import_entry.local_name, namespace_, Environment::InitializeBindingHint::Normal)); |
403 | 0 | } |
404 | | // iv. Else, |
405 | 0 | else { |
406 | | // 1. Perform env.CreateImportBinding(in.[[LocalName]], resolution.[[Module]], resolution.[[BindingName]]). |
407 | 0 | MUST(environment->create_import_binding(import_entry.local_name, resolution.module, resolution.export_name)); |
408 | 0 | } |
409 | 0 | } |
410 | 0 | } |
411 | | |
412 | | // 8. Let moduleContext be a new ECMAScript code execution context. |
413 | | // Note: this has already been created during the construction of this object. |
414 | | |
415 | | // 9. Set the Function of moduleContext to null. |
416 | | |
417 | | // 10. Assert: module.[[Realm]] is not undefined. |
418 | | // Note: This must be true because we use a reference. |
419 | | |
420 | | // 11. Set the Realm of moduleContext to module.[[Realm]]. |
421 | 0 | m_execution_context->realm = &realm(); |
422 | | |
423 | | // 12. Set the ScriptOrModule of moduleContext to module. |
424 | 0 | m_execution_context->script_or_module = NonnullGCPtr<Module>(*this); |
425 | | |
426 | | // 13. Set the VariableEnvironment of moduleContext to module.[[Environment]]. |
427 | 0 | m_execution_context->variable_environment = environment; |
428 | | |
429 | | // 14. Set the LexicalEnvironment of moduleContext to module.[[Environment]]. |
430 | 0 | m_execution_context->lexical_environment = environment; |
431 | | |
432 | | // 15. Set the PrivateEnvironment of moduleContext to null. |
433 | | |
434 | | // 16. Set module.[[Context]] to moduleContext. |
435 | | // Note: We're already working on that one. |
436 | | |
437 | | // 17. Push moduleContext onto the execution context stack; moduleContext is now the running execution context. |
438 | 0 | TRY(vm.push_execution_context(*m_execution_context, {})); |
439 | | |
440 | | // 18. Let code be module.[[ECMAScriptCode]]. |
441 | | |
442 | | // 19. Let varDeclarations be the VarScopedDeclarations of code. |
443 | | // Note: We just loop through them in step 21. |
444 | | |
445 | | // 20. Let declaredVarNames be a new empty List. |
446 | 0 | Vector<DeprecatedFlyString> declared_var_names; |
447 | | |
448 | | // 21. For each element d of varDeclarations, do |
449 | | // a. For each element dn of the BoundNames of d, do |
450 | | // NOTE: Due to the use of MUST with `create_mutable_binding` and `initialize_binding` below, |
451 | | // an exception should not result from `for_each_var_declared_identifier`. |
452 | 0 | MUST(m_ecmascript_code->for_each_var_declared_identifier([&](auto const& identifier) { |
453 | 0 | auto const& name = identifier.string(); |
454 | | // i. If dn is not an element of declaredVarNames, then |
455 | 0 | if (!declared_var_names.contains_slow(name)) { |
456 | | // 1. Perform ! env.CreateMutableBinding(dn, false). |
457 | 0 | MUST(environment->create_mutable_binding(vm, name, false)); |
458 | | |
459 | | // 2. Perform ! env.InitializeBinding(dn, undefined, normal). |
460 | 0 | MUST(environment->initialize_binding(vm, name, js_undefined(), Environment::InitializeBindingHint::Normal)); |
461 | | |
462 | | // 3. Append dn to declaredVarNames. |
463 | 0 | declared_var_names.empend(name); |
464 | 0 | } |
465 | 0 | })); |
466 | | |
467 | | // 22. Let lexDeclarations be the LexicallyScopedDeclarations of code. |
468 | | // Note: We only loop through them in step 24. |
469 | | |
470 | | // 23. Let privateEnv be null. |
471 | 0 | PrivateEnvironment* private_environment = nullptr; |
472 | | |
473 | | // 24. For each element d of lexDeclarations, do |
474 | | // NOTE: Due to the use of MUST in the callback, an exception should not result from `for_each_lexically_scoped_declaration`. |
475 | 0 | MUST(m_ecmascript_code->for_each_lexically_scoped_declaration([&](Declaration const& declaration) { |
476 | | // a. For each element dn of the BoundNames of d, do |
477 | | // NOTE: Due to the use of MUST with `create_immutable_binding`, `create_mutable_binding` and `initialize_binding` below, |
478 | | // an exception should not result from `for_each_bound_identifier`. |
479 | 0 | MUST(declaration.for_each_bound_identifier([&](auto const& identifier) { |
480 | 0 | auto const& name = identifier.string(); |
481 | | // i. If IsConstantDeclaration of d is true, then |
482 | 0 | if (declaration.is_constant_declaration()) { |
483 | | // 1. Perform ! env.CreateImmutableBinding(dn, true). |
484 | 0 | MUST(environment->create_immutable_binding(vm, name, true)); |
485 | 0 | } |
486 | | // ii. Else, |
487 | 0 | else { |
488 | | // 1. Perform ! env.CreateMutableBinding(dn, false). |
489 | 0 | MUST(environment->create_mutable_binding(vm, name, false)); |
490 | 0 | } |
491 | | |
492 | | // iii. If d is a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration, then |
493 | 0 | if (declaration.is_function_declaration()) { |
494 | 0 | VERIFY(is<FunctionDeclaration>(declaration)); |
495 | 0 | auto const& function_declaration = static_cast<FunctionDeclaration const&>(declaration); |
496 | | |
497 | | // 1. Let fo be InstantiateFunctionObject of d with arguments env and privateEnv. |
498 | | // NOTE: Special case if the function is a default export of an anonymous function |
499 | | // it has name "*default*" but internally should have name "default". |
500 | 0 | DeprecatedFlyString function_name = function_declaration.name(); |
501 | 0 | if (function_name == ExportStatement::local_name_for_default) |
502 | 0 | function_name = "default"sv; |
503 | 0 | auto function = ECMAScriptFunctionObject::create(realm(), function_name, function_declaration.source_text(), function_declaration.body(), function_declaration.parameters(), function_declaration.function_length(), function_declaration.local_variables_names(), environment, private_environment, function_declaration.kind(), function_declaration.is_strict_mode(), |
504 | 0 | function_declaration.parsing_insights()); |
505 | | |
506 | | // 2. Perform ! env.InitializeBinding(dn, fo, normal). |
507 | 0 | MUST(environment->initialize_binding(vm, name, function, Environment::InitializeBindingHint::Normal)); |
508 | 0 | } |
509 | 0 | })); |
510 | 0 | })); |
511 | | |
512 | | // Note: The default export name is also part of the local lexical declarations but |
513 | | // instead of making that a special case in the parser we just check it here. |
514 | | // This is only needed for things which are not declarations. |
515 | | // For more info check Parser::parse_export_statement. |
516 | | // Furthermore, that declaration is not constant. so we take 24.a.ii |
517 | 0 | if (m_default_export) { |
518 | 0 | VERIFY(m_default_export->has_statement()); |
519 | | |
520 | 0 | auto const& statement = m_default_export->statement(); |
521 | 0 | if (!is<Declaration>(statement)) { |
522 | 0 | auto const& name = m_default_export->entries()[0].local_or_import_name; |
523 | 0 | dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] Adding default export to lexical declarations: local name: {}, Expression: {}", name, statement.class_name()); |
524 | | |
525 | | // 1. Perform ! env.CreateMutableBinding(dn, false). |
526 | 0 | MUST(environment->create_mutable_binding(vm, name.value(), false)); |
527 | | |
528 | | // Note: Since this is not a function declaration 24.a.iii never applies |
529 | 0 | } |
530 | 0 | } |
531 | | |
532 | | // 25. Remove moduleContext from the execution context stack. |
533 | 0 | vm.pop_execution_context(); |
534 | | |
535 | | // 26. Return unused. |
536 | 0 | return {}; |
537 | 0 | } |
538 | | |
539 | | // 16.2.1.6.3 ResolveExport ( exportName [ , resolveSet ] ), https://tc39.es/ecma262/#sec-resolveexport |
540 | | ThrowCompletionOr<ResolvedBinding> SourceTextModule::resolve_export(VM& vm, DeprecatedFlyString const& export_name, Vector<ResolvedBinding> resolve_set) |
541 | 0 | { |
542 | | // 1. Assert: module.[[Status]] is not new. |
543 | 0 | VERIFY(m_status != ModuleStatus::New); |
544 | | |
545 | | // 2. If resolveSet is not present, set resolveSet to a new empty List. |
546 | | // Note: This is done by the default argument. |
547 | | |
548 | | // 3. For each Record { [[Module]], [[ExportName]] } r of resolveSet, do |
549 | 0 | for (auto& [type, module, exported_name] : resolve_set) { |
550 | | // a. If module and r.[[Module]] are the same Module Record and SameValue(exportName, r.[[ExportName]]) is true, then |
551 | 0 | if (module == this && exported_name == export_name) { |
552 | | // i. Assert: This is a circular import request. |
553 | | |
554 | | // ii. Return null. |
555 | 0 | return ResolvedBinding::null(); |
556 | 0 | } |
557 | 0 | } |
558 | | |
559 | | // 4. Append the Record { [[Module]]: module, [[ExportName]]: exportName } to resolveSet. |
560 | 0 | resolve_set.append({ ResolvedBinding::Type::BindingName, this, export_name }); |
561 | | |
562 | | // 5. For each ExportEntry Record e of module.[[LocalExportEntries]], do |
563 | 0 | for (auto& entry : m_local_export_entries) { |
564 | | // a. If SameValue(exportName, e.[[ExportName]]) is true, then |
565 | 0 | if (export_name != entry.export_name) |
566 | 0 | continue; |
567 | | |
568 | | // i. Assert: module provides the direct binding for this export. |
569 | | // FIXME: What does this mean? |
570 | | |
571 | | // ii. Return ResolvedBinding Record { [[Module]]: module, [[BindingName]]: e.[[LocalName]] }. |
572 | 0 | return ResolvedBinding { |
573 | 0 | ResolvedBinding::Type::BindingName, |
574 | 0 | this, |
575 | 0 | entry.local_or_import_name.value(), |
576 | 0 | }; |
577 | 0 | } |
578 | | |
579 | | // 5. For each ExportEntry Record e of module.[[IndirectExportEntries]], do |
580 | 0 | for (auto& entry : m_indirect_export_entries) { |
581 | | // a. If SameValue(exportName, e.[[ExportName]]) is true, then |
582 | 0 | if (export_name != entry.export_name) |
583 | 0 | continue; |
584 | | |
585 | | // i. Assert: e.[[ModuleRequest]] is not null. |
586 | | // ii. Let importedModule be GetImportedModule(module, e.[[ModuleRequest]]). |
587 | 0 | auto imported_module = get_imported_module(entry.module_request()); |
588 | | |
589 | | // iii. If e.[[ImportName]] is all, then |
590 | 0 | if (entry.kind == ExportEntry::Kind::ModuleRequestAll) { |
591 | | // 1. Assert: module does not provide the direct binding for this export. |
592 | | // FIXME: What does this mean? / How do we check this |
593 | | |
594 | | // 2. Return ResolvedBinding Record { [[Module]]: importedModule, [[BindingName]]: namespace }. |
595 | 0 | return ResolvedBinding { |
596 | 0 | ResolvedBinding::Type::Namespace, |
597 | 0 | imported_module.ptr(), |
598 | 0 | {} |
599 | 0 | }; |
600 | 0 | } |
601 | | // iv. Else, |
602 | 0 | else { |
603 | | // 1. Assert: module imports a specific binding for this export. |
604 | | // FIXME: What does this mean? / How do we check this |
605 | | |
606 | | // 2. Return ? importedModule.ResolveExport(e.[[ImportName]], resolveSet). |
607 | 0 | return imported_module->resolve_export(vm, entry.local_or_import_name.value(), resolve_set); |
608 | 0 | } |
609 | 0 | } |
610 | | |
611 | | // 7. If SameValue(exportName, "default") is true, then |
612 | 0 | if (export_name == "default"sv) { |
613 | | // a. Assert: A default export was not explicitly defined by this module. |
614 | | // FIXME: What does this mean? / How do we check this |
615 | | |
616 | | // b. Return null. |
617 | 0 | return ResolvedBinding::null(); |
618 | | // c. NOTE: A default export cannot be provided by an export * from "mod" declaration. |
619 | 0 | } |
620 | | |
621 | | // 8. Let starResolution be null. |
622 | 0 | ResolvedBinding star_resolution = ResolvedBinding::null(); |
623 | | |
624 | | // 9. For each ExportEntry Record e of module.[[StarExportEntries]], do |
625 | 0 | for (auto& entry : m_star_export_entries) { |
626 | | // a. Assert: e.[[ModuleRequest]] is not null. |
627 | | // b. Let importedModule be GetImportedModule(module, e.[[ModuleRequest]]). |
628 | 0 | auto imported_module = get_imported_module(entry.module_request()); |
629 | | |
630 | | // c. Let resolution be ? importedModule.ResolveExport(exportName, resolveSet). |
631 | 0 | auto resolution = TRY(imported_module->resolve_export(vm, export_name, resolve_set)); |
632 | | |
633 | | // d. If resolution is ambiguous, return ambiguous. |
634 | 0 | if (resolution.is_ambiguous()) |
635 | 0 | return ResolvedBinding::ambiguous(); |
636 | | |
637 | | // e. If resolution is not null, then |
638 | 0 | if (resolution.type == ResolvedBinding::Null) |
639 | 0 | continue; |
640 | | |
641 | | // i. Assert: resolution is a ResolvedBinding Record. |
642 | 0 | VERIFY(resolution.is_valid()); |
643 | | |
644 | | // ii. If starResolution is null, set starResolution to resolution. |
645 | 0 | if (star_resolution.type == ResolvedBinding::Null) { |
646 | 0 | star_resolution = resolution; |
647 | 0 | } |
648 | | // iii. Else, |
649 | 0 | else { |
650 | | // 1. Assert: There is more than one * import that includes the requested name. |
651 | | // FIXME: Assert this |
652 | | |
653 | | // 2. If resolution.[[Module]] and starResolution.[[Module]] are not the same Module Record, return ambiguous. |
654 | 0 | if (resolution.module != star_resolution.module) |
655 | 0 | return ResolvedBinding::ambiguous(); |
656 | | |
657 | | // 3. If resolution.[[BindingName]] is namespace and starResolution.[[BindingName]] is not namespace, or if resolution.[[BindingName]] is not namespace and starResolution.[[BindingName]] is namespace, return ambiguous. |
658 | 0 | if (resolution.is_namespace() != star_resolution.is_namespace()) |
659 | 0 | return ResolvedBinding::ambiguous(); |
660 | | |
661 | | // 4. If resolution.[[BindingName]] is a String, starResolution.[[BindingName]] is a String, and SameValue(resolution.[[BindingName]], starResolution.[[BindingName]]) is false, return ambiguous. |
662 | 0 | if (!resolution.is_namespace() && resolution.export_name != star_resolution.export_name) { |
663 | | // Note: Because we know from the previous if that either both are namespaces or both are string we can check just one |
664 | 0 | return ResolvedBinding::ambiguous(); |
665 | 0 | } |
666 | 0 | } |
667 | 0 | } |
668 | | |
669 | | // 10. Return starResolution. |
670 | 0 | return star_resolution; |
671 | 0 | } |
672 | | |
673 | | // 16.2.1.6.5 ExecuteModule ( [ capability ] ), https://tc39.es/ecma262/#sec-source-text-module-record-execute-module |
674 | | ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GCPtr<PromiseCapability> capability) |
675 | 0 | { |
676 | 0 | dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] SourceTextModule::execute_module({}, PromiseCapability @ {})", filename(), capability.ptr()); |
677 | | |
678 | | // 1. Let moduleContext be a new ECMAScript code execution context. |
679 | 0 | auto module_context = ExecutionContext::create(); |
680 | | |
681 | | // Note: This is not in the spec but we require it. |
682 | 0 | module_context->is_strict_mode = true; |
683 | | |
684 | | // 2. Set the Function of moduleContext to null. |
685 | | |
686 | | // 3. Set the Realm of moduleContext to module.[[Realm]]. |
687 | 0 | module_context->realm = &realm(); |
688 | | |
689 | | // 4. Set the ScriptOrModule of moduleContext to module. |
690 | 0 | module_context->script_or_module = NonnullGCPtr<Module>(*this); |
691 | | |
692 | | // 5. Assert: module has been linked and declarations in its module environment have been instantiated. |
693 | 0 | VERIFY(m_status != ModuleStatus::New); |
694 | 0 | VERIFY(m_status != ModuleStatus::Unlinked); |
695 | 0 | VERIFY(m_status != ModuleStatus::Linking); |
696 | 0 | VERIFY(environment()); |
697 | | |
698 | | // 6. Set the VariableEnvironment of moduleContext to module.[[Environment]]. |
699 | 0 | module_context->variable_environment = environment(); |
700 | | |
701 | | // 7. Set the LexicalEnvironment of moduleContext to module.[[Environment]]. |
702 | 0 | module_context->lexical_environment = environment(); |
703 | | |
704 | | // 8. Suspend the currently running execution context. |
705 | | // FIXME: We don't have suspend yet |
706 | | |
707 | | // 9. If module.[[HasTLA]] is false, then |
708 | 0 | if (!m_has_top_level_await) { |
709 | | // a. Assert: capability is not present. |
710 | 0 | VERIFY(capability == nullptr); |
711 | | // b. Push moduleContext onto the execution context stack; moduleContext is now the running execution context. |
712 | 0 | TRY(vm.push_execution_context(*module_context, {})); |
713 | | |
714 | | // c. Let result be the result of evaluating module.[[ECMAScriptCode]]. |
715 | 0 | Completion result; |
716 | |
|
717 | 0 | auto maybe_executable = Bytecode::compile(vm, m_ecmascript_code, FunctionKind::Normal, "ShadowRealmEval"sv); |
718 | 0 | if (maybe_executable.is_error()) |
719 | 0 | result = maybe_executable.release_error(); |
720 | 0 | else { |
721 | 0 | auto executable = maybe_executable.release_value(); |
722 | |
|
723 | 0 | auto result_and_return_register = vm.bytecode_interpreter().run_executable(*executable, {}); |
724 | 0 | if (result_and_return_register.value.is_error()) { |
725 | 0 | result = result_and_return_register.value.release_error(); |
726 | 0 | } else { |
727 | | // Resulting value is in the accumulator. |
728 | 0 | result = result_and_return_register.return_register_value.value_or(js_undefined()); |
729 | 0 | } |
730 | 0 | } |
731 | | |
732 | | // d. Let env be moduleContext's LexicalEnvironment. |
733 | 0 | auto env = module_context->lexical_environment; |
734 | 0 | VERIFY(is<DeclarativeEnvironment>(*env)); |
735 | | |
736 | | // e. Set result to DisposeResources(env, result). |
737 | 0 | result = dispose_resources(vm, static_cast<DeclarativeEnvironment*>(env.ptr()), result); |
738 | | |
739 | | // f. Suspend moduleContext and remove it from the execution context stack. |
740 | 0 | vm.pop_execution_context(); |
741 | | |
742 | | // g. Resume the context that is now on the top of the execution context stack as the running execution context. |
743 | | // FIXME: We don't have resume yet. |
744 | | |
745 | | // h. If result is an abrupt completion, then |
746 | 0 | if (result.is_error()) { |
747 | | // i. Return ? result. |
748 | 0 | return result.release_error(); |
749 | 0 | } |
750 | 0 | } |
751 | | // 10. Else, |
752 | 0 | else { |
753 | | // a. Assert: capability is a PromiseCapability Record. |
754 | 0 | VERIFY(capability != nullptr); |
755 | | |
756 | | // b. Perform AsyncBlockStart(capability, module.[[ECMAScriptCode]], moduleContext). |
757 | | |
758 | | // AD-HOC: We implement asynchronous execution via synthetic generator functions, |
759 | | // so we fake "AsyncBlockStart" here by creating an async function to wrap |
760 | | // the top-level module code. |
761 | | // FIXME: Improve this situation, so we can match the spec better. |
762 | | |
763 | | // AD-HOC: We push/pop the moduleContext around the function construction to ensure that the async execution context |
764 | | // captures the module execution context. |
765 | 0 | vm.push_execution_context(*module_context); |
766 | |
|
767 | 0 | FunctionParsingInsights parsing_insights; |
768 | 0 | parsing_insights.uses_this_from_environment = true; |
769 | 0 | parsing_insights.uses_this = true; |
770 | 0 | auto module_wrapper_function = ECMAScriptFunctionObject::create( |
771 | 0 | realm(), "module code with top-level await", StringView {}, this->m_ecmascript_code, |
772 | 0 | {}, 0, {}, environment(), nullptr, FunctionKind::Async, true, parsing_insights); |
773 | 0 | module_wrapper_function->set_is_module_wrapper(true); |
774 | |
|
775 | 0 | vm.pop_execution_context(); |
776 | |
|
777 | 0 | auto result = call(vm, Value { module_wrapper_function }, js_undefined(), ReadonlySpan<Value> {}); |
778 | | |
779 | | // AD-HOC: This is basically analogous to what AsyncBlockStart would do. |
780 | 0 | if (result.is_throw_completion()) { |
781 | 0 | MUST(call(vm, *capability->reject(), js_undefined(), result.throw_completion().value().value())); |
782 | 0 | } else { |
783 | 0 | MUST(call(vm, *capability->resolve(), js_undefined(), result.value())); |
784 | 0 | } |
785 | 0 | } |
786 | | |
787 | | // 11. Return unused. |
788 | 0 | return {}; |
789 | 0 | } |
790 | | |
791 | | } |