/src/serenity/Userland/Libraries/LibJS/Runtime/ModuleEnvironment.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2022, David Tuin <davidot@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/TypeCasts.h> |
8 | | #include <LibJS/Runtime/GlobalEnvironment.h> |
9 | | #include <LibJS/Runtime/ModuleEnvironment.h> |
10 | | #include <LibJS/Runtime/VM.h> |
11 | | |
12 | | namespace JS { |
13 | | |
14 | | JS_DEFINE_ALLOCATOR(ModuleEnvironment); |
15 | | |
16 | | // 9.1.2.6 NewModuleEnvironment ( E ), https://tc39.es/ecma262/#sec-newmoduleenvironment |
17 | | ModuleEnvironment::ModuleEnvironment(Environment* outer_environment) |
18 | 0 | : DeclarativeEnvironment(outer_environment) |
19 | 0 | { |
20 | 0 | } |
21 | | |
22 | | // 9.1.1.5.1 GetBindingValue ( N, S ), https://tc39.es/ecma262/#sec-module-environment-records-getbindingvalue-n-s |
23 | | ThrowCompletionOr<Value> ModuleEnvironment::get_binding_value(VM& vm, DeprecatedFlyString const& name, bool strict) |
24 | 0 | { |
25 | | // 1. Assert: S is true. |
26 | 0 | VERIFY(strict); |
27 | | |
28 | | // 2. Assert: envRec has a binding for N. |
29 | 0 | auto* indirect_binding = get_indirect_binding(name); |
30 | 0 | VERIFY(indirect_binding || !DeclarativeEnvironment::has_binding(name).is_error()); |
31 | | |
32 | | // 3. If the binding for N is an indirect binding, then |
33 | 0 | if (indirect_binding) { |
34 | | // a. Let M and N2 be the indirection values provided when this binding for N was created. |
35 | | |
36 | | // b. Let targetEnv be M.[[Environment]]. |
37 | 0 | auto* target_env = indirect_binding->module->environment(); |
38 | | |
39 | | // c. If targetEnv is empty, throw a ReferenceError exception. |
40 | 0 | if (!target_env) |
41 | 0 | return vm.throw_completion<ReferenceError>(ErrorType::ModuleNoEnvironment); |
42 | | |
43 | | // d. Return ? targetEnv.GetBindingValue(N2, true). |
44 | 0 | return target_env->get_binding_value(vm, indirect_binding->binding_name, true); |
45 | 0 | } |
46 | | |
47 | | // 4. If the binding for N in envRec is an uninitialized binding, throw a ReferenceError exception. |
48 | | // 5. Return the value currently bound to N in envRec. |
49 | | // Note: Step 4 & 5 are the steps performed by declarative environment GetBindingValue |
50 | 0 | return DeclarativeEnvironment::get_binding_value(vm, name, strict); |
51 | 0 | } |
52 | | |
53 | | // 9.1.1.5.2 DeleteBinding ( N ), https://tc39.es/ecma262/#sec-module-environment-records-deletebinding-n |
54 | | ThrowCompletionOr<bool> ModuleEnvironment::delete_binding(VM&, DeprecatedFlyString const&) |
55 | 0 | { |
56 | | // The DeleteBinding concrete method of a module Environment Record is never used within this specification. |
57 | 0 | VERIFY_NOT_REACHED(); |
58 | 0 | } |
59 | | |
60 | | // 9.1.1.5.4 GetThisBinding ( ), https://tc39.es/ecma262/#sec-module-environment-records-getthisbinding |
61 | | ThrowCompletionOr<Value> ModuleEnvironment::get_this_binding(VM&) const |
62 | 0 | { |
63 | | // 1. Return undefined. |
64 | 0 | return js_undefined(); |
65 | 0 | } |
66 | | |
67 | | // 9.1.1.5.5 CreateImportBinding ( N, M, N2 ), https://tc39.es/ecma262/#sec-createimportbinding |
68 | | ThrowCompletionOr<void> ModuleEnvironment::create_import_binding(DeprecatedFlyString name, Module* module, DeprecatedFlyString binding_name) |
69 | 0 | { |
70 | | // 1. Assert: envRec does not already have a binding for N. |
71 | 0 | VERIFY(!get_indirect_binding(name)); |
72 | | // 2. Assert: When M.[[Environment]] is instantiated it will have a direct binding for N2. |
73 | | // FIXME: I don't know what this means or how to check it. |
74 | | |
75 | | // 3. Create an immutable indirect binding in envRec for N that references M and N2 as its target binding and record that the binding is initialized. |
76 | | // Note: We use the fact that the binding is in this list as it being initialized. |
77 | 0 | m_indirect_bindings.append({ move(name), |
78 | 0 | module, |
79 | 0 | move(binding_name) }); |
80 | | |
81 | | // 4. Return unused. |
82 | 0 | return {}; |
83 | 0 | } |
84 | | |
85 | | ModuleEnvironment::IndirectBinding const* ModuleEnvironment::get_indirect_binding(DeprecatedFlyString const& name) const |
86 | 0 | { |
87 | 0 | auto binding_or_end = m_indirect_bindings.find_if([&](IndirectBinding const& binding) { |
88 | 0 | return binding.name == name; |
89 | 0 | }); |
90 | 0 | if (binding_or_end.is_end()) |
91 | 0 | return nullptr; |
92 | | |
93 | 0 | return &(*binding_or_end); |
94 | 0 | } |
95 | | |
96 | | Optional<ModuleEnvironment::BindingAndIndex> ModuleEnvironment::find_binding_and_index(DeprecatedFlyString const& name) const |
97 | 0 | { |
98 | 0 | auto* indirect_binding = get_indirect_binding(name); |
99 | 0 | if (indirect_binding != nullptr) { |
100 | 0 | auto* target_env = indirect_binding->module->environment(); |
101 | 0 | if (!target_env) |
102 | 0 | return {}; |
103 | | |
104 | 0 | VERIFY(is<ModuleEnvironment>(target_env)); |
105 | 0 | auto& target_module_environment = static_cast<ModuleEnvironment&>(*target_env); |
106 | 0 | auto result = target_module_environment.find_binding_and_index(indirect_binding->binding_name); |
107 | 0 | if (!result.has_value()) |
108 | 0 | return {}; |
109 | | |
110 | | // NOTE: We must pretend this binding is actually from this environment |
111 | | // so as specified by |
112 | | // 9.1.1.5.5 CreateImportBinding ( N, M, N2 ), https://tc39.es/ecma262/#sec-createimportbinding |
113 | | // It creates a new initialized immutable indirect binding for the |
114 | | // name N. A binding must not already exist in this Environment |
115 | | // Record for N. N2 is the name of a binding that exists in M's |
116 | | // Module Environment Record. Accesses to the value of the new |
117 | | // binding will indirectly access the bound value of the target |
118 | | // binding. |
119 | | // We don't alter the name of the binding as the name is only used |
120 | | // for lookup. |
121 | 0 | Binding copy_binding = result->binding(); |
122 | 0 | copy_binding.mutable_ = false; |
123 | 0 | copy_binding.can_be_deleted = false; |
124 | 0 | copy_binding.initialized = true; |
125 | 0 | return BindingAndIndex { copy_binding }; |
126 | 0 | } |
127 | | |
128 | 0 | return DeclarativeEnvironment::find_binding_and_index(name); |
129 | 0 | } |
130 | | |
131 | | void ModuleEnvironment::visit_edges(Visitor& visitor) |
132 | 0 | { |
133 | 0 | Base::visit_edges(visitor); |
134 | 0 | for (auto& indirect_binding : m_indirect_bindings) |
135 | 0 | visitor.visit(indirect_binding.module); |
136 | 0 | } |
137 | | |
138 | | } |