/src/serenity/Userland/Libraries/LibJS/Module.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2021, 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 | | #pragma once |
9 | | |
10 | | #include <AK/DeprecatedFlyString.h> |
11 | | #include <LibJS/Heap/GCPtr.h> |
12 | | #include <LibJS/ModuleLoading.h> |
13 | | #include <LibJS/Runtime/Environment.h> |
14 | | #include <LibJS/Runtime/Realm.h> |
15 | | #include <LibJS/Script.h> |
16 | | |
17 | | namespace JS { |
18 | | |
19 | | struct ResolvedBinding { |
20 | | enum Type { |
21 | | BindingName, |
22 | | Namespace, |
23 | | Ambiguous, |
24 | | Null, |
25 | | }; |
26 | | |
27 | | static ResolvedBinding null() |
28 | 0 | { |
29 | 0 | return {}; |
30 | 0 | } |
31 | | |
32 | | static ResolvedBinding ambiguous() |
33 | 0 | { |
34 | 0 | ResolvedBinding binding; |
35 | 0 | binding.type = Ambiguous; |
36 | 0 | return binding; |
37 | 0 | } |
38 | | |
39 | | Type type { Null }; |
40 | | GCPtr<Module> module; |
41 | | DeprecatedFlyString export_name; |
42 | | |
43 | | bool is_valid() const |
44 | 0 | { |
45 | 0 | return type == BindingName || type == Namespace; |
46 | 0 | } |
47 | | |
48 | | bool is_namespace() const |
49 | 0 | { |
50 | 0 | return type == Namespace; |
51 | 0 | } |
52 | | |
53 | | bool is_ambiguous() const |
54 | 0 | { |
55 | 0 | return type == Ambiguous; |
56 | 0 | } |
57 | | }; |
58 | | |
59 | | // https://tc39.es/ecma262/#graphloadingstate-record |
60 | | struct GraphLoadingState : public Cell { |
61 | | JS_CELL(GraphLoadingState, Cell); |
62 | | JS_DECLARE_ALLOCATOR(GraphLoadingState); |
63 | | |
64 | | public: |
65 | | struct HostDefined : Cell { |
66 | | JS_CELL(HostDefined, Cell); |
67 | | |
68 | | public: |
69 | | virtual ~HostDefined() = default; |
70 | | }; |
71 | | |
72 | | GCPtr<PromiseCapability> promise_capability; // [[PromiseCapability]] |
73 | | bool is_loading { false }; // [[IsLoading]] |
74 | | size_t pending_module_count { 0 }; // [[PendingModulesCount]] |
75 | | HashTable<JS::GCPtr<CyclicModule>> visited; // [[Visited]] |
76 | | GCPtr<HostDefined> host_defined; // [[HostDefined]] |
77 | | |
78 | | private: |
79 | | GraphLoadingState(GCPtr<PromiseCapability> promise_capability, bool is_loading, size_t pending_module_count, HashTable<JS::GCPtr<CyclicModule>> visited, GCPtr<HostDefined> host_defined) |
80 | 0 | : promise_capability(move(promise_capability)) |
81 | 0 | , is_loading(is_loading) |
82 | 0 | , pending_module_count(pending_module_count) |
83 | 0 | , visited(move(visited)) |
84 | 0 | , host_defined(move(host_defined)) |
85 | 0 | { |
86 | 0 | } |
87 | | virtual void visit_edges(Cell::Visitor&) override; |
88 | | }; |
89 | | |
90 | | // 16.2.1.4 Abstract Module Records, https://tc39.es/ecma262/#sec-abstract-module-records |
91 | | class Module : public Cell { |
92 | | JS_CELL(Module, Cell); |
93 | | JS_DECLARE_ALLOCATOR(Module); |
94 | | |
95 | | public: |
96 | | virtual ~Module() override; |
97 | | |
98 | 0 | Realm& realm() { return *m_realm; } |
99 | 0 | Realm const& realm() const { return *m_realm; } |
100 | | |
101 | 0 | StringView filename() const { return m_filename; } |
102 | | |
103 | 0 | Environment* environment() { return m_environment; } |
104 | | |
105 | 0 | Script::HostDefined* host_defined() const { return m_host_defined; } |
106 | | |
107 | | ThrowCompletionOr<Object*> get_module_namespace(VM& vm); |
108 | | |
109 | | virtual ThrowCompletionOr<void> link(VM& vm) = 0; |
110 | | virtual ThrowCompletionOr<Promise*> evaluate(VM& vm) = 0; |
111 | | |
112 | | virtual ThrowCompletionOr<Vector<DeprecatedFlyString>> get_exported_names(VM& vm, Vector<Module*> export_star_set = {}) = 0; |
113 | | virtual ThrowCompletionOr<ResolvedBinding> resolve_export(VM& vm, DeprecatedFlyString const& export_name, Vector<ResolvedBinding> resolve_set = {}) = 0; |
114 | | |
115 | | virtual ThrowCompletionOr<u32> inner_module_linking(VM& vm, Vector<Module*>& stack, u32 index); |
116 | | virtual ThrowCompletionOr<u32> inner_module_evaluation(VM& vm, Vector<Module*>& stack, u32 index); |
117 | | |
118 | | virtual PromiseCapability& load_requested_modules(GCPtr<GraphLoadingState::HostDefined>) = 0; |
119 | | |
120 | | protected: |
121 | | Module(Realm&, ByteString filename, Script::HostDefined* host_defined = nullptr); |
122 | | |
123 | | virtual void visit_edges(Cell::Visitor&) override; |
124 | | |
125 | | void set_environment(Environment* environment) |
126 | 0 | { |
127 | 0 | m_environment = environment; |
128 | 0 | } |
129 | | |
130 | | private: |
131 | | Object* module_namespace_create(VM& vm, Vector<DeprecatedFlyString> unambiguous_names); |
132 | | |
133 | | // These handles are only safe as long as the VM they live in is valid. |
134 | | // But evaluated modules SHOULD be stored in the VM so unless you intentionally |
135 | | // destroy the VM but keep the modules this should not happen. Because VM |
136 | | // stores modules with a RefPtr we cannot just store the VM as that leads to |
137 | | // cycles. |
138 | | GCPtr<Realm> m_realm; // [[Realm]] |
139 | | GCPtr<Environment> m_environment; // [[Environment]] |
140 | | GCPtr<Object> m_namespace; // [[Namespace]] |
141 | | Script::HostDefined* m_host_defined { nullptr }; // [[HostDefined]] |
142 | | |
143 | | // Needed for potential lookups of modules. |
144 | | ByteString m_filename; |
145 | | }; |
146 | | |
147 | | class CyclicModule; |
148 | | struct GraphLoadingState; |
149 | | |
150 | | void finish_loading_imported_module(ImportedModuleReferrer, ModuleRequest const&, ImportedModulePayload, ThrowCompletionOr<NonnullGCPtr<Module>> const&); |
151 | | |
152 | | } |