Serenity Operating System
at master 160 lines 7.6 kB view raw
1/* 2 * Copyright (c) 2022, David Tuin <davidot@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <LibJS/Runtime/AbstractOperations.h> 8#include <LibJS/Runtime/Completion.h> 9#include <LibJS/Runtime/GlobalEnvironment.h> 10#include <LibJS/Runtime/ModuleEnvironment.h> 11#include <LibJS/Runtime/VM.h> 12#include <LibJS/SyntheticModule.h> 13 14namespace JS { 15 16// 1.2.1 CreateSyntheticModule ( exportNames, evaluationSteps, realm, hostDefined ), https://tc39.es/proposal-json-modules/#sec-createsyntheticmodule 17SyntheticModule::SyntheticModule(Vector<DeprecatedFlyString> export_names, SyntheticModule::EvaluationFunction evaluation_steps, Realm& realm, StringView filename) 18 : Module(realm, filename) 19 , m_export_names(move(export_names)) 20 , m_evaluation_steps(move(evaluation_steps)) 21{ 22 // 1. Return Synthetic Module Record { [[Realm]]: realm, [[Environment]]: undefined, [[Namespace]]: undefined, [[HostDefined]]: hostDefined, [[ExportNames]]: exportNames, [[EvaluationSteps]]: evaluationSteps }. 23} 24 25// 1.2.3.1 GetExportedNames( exportStarSet ), https://tc39.es/proposal-json-modules/#sec-smr-getexportednames 26ThrowCompletionOr<Vector<DeprecatedFlyString>> SyntheticModule::get_exported_names(VM&, Vector<Module*>) 27{ 28 // 1. Return module.[[ExportNames]]. 29 return m_export_names; 30} 31 32// 1.2.3.2 ResolveExport( exportName, resolveSet ), https://tc39.es/proposal-json-modules/#sec-smr-resolveexport 33ThrowCompletionOr<ResolvedBinding> SyntheticModule::resolve_export(VM&, DeprecatedFlyString const& export_name, Vector<ResolvedBinding>) 34{ 35 // 1. If module.[[ExportNames]] does not contain exportName, return null. 36 if (!m_export_names.contains_slow(export_name)) 37 return ResolvedBinding::null(); 38 39 // 2. Return ResolvedBinding Record { [[Module]]: module, [[BindingName]]: exportName }. 40 return ResolvedBinding { ResolvedBinding::BindingName, this, export_name }; 41} 42 43// 1.2.3.3 Link ( ), https://tc39.es/proposal-json-modules/#sec-smr-instantiate 44ThrowCompletionOr<void> SyntheticModule::link(VM& vm) 45{ 46 // Note: Has some changes from PR: https://github.com/tc39/proposal-json-modules/pull/13. 47 // Which includes renaming it from Instantiate ( ) to Link ( ). 48 49 // 1. Let realm be module.[[Realm]]. 50 // 2. Assert: realm is not undefined. 51 // Note: This must be true because we use a reference. 52 53 // 3. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]). 54 auto environment = vm.heap().allocate_without_realm<ModuleEnvironment>(&realm().global_environment()); 55 56 // 4. Set module.[[Environment]] to env. 57 set_environment(environment); 58 59 // 5. For each exportName in module.[[ExportNames]], 60 for (auto& export_name : m_export_names) { 61 // a. Perform ! envRec.CreateMutableBinding(exportName, false). 62 MUST(environment->create_mutable_binding(vm, export_name, false)); 63 64 // b. Perform ! envRec.InitializeBinding(exportName, undefined, normal). 65 MUST(environment->initialize_binding(vm, export_name, js_undefined(), Environment::InitializeBindingHint::Normal)); 66 } 67 68 // 6. Return unused. 69 return {}; 70} 71 72// 1.2.3.4 Evaluate ( ), https://tc39.es/proposal-json-modules/#sec-smr-Evaluate 73ThrowCompletionOr<Promise*> SyntheticModule::evaluate(VM& vm) 74{ 75 // Note: Has some changes from PR: https://github.com/tc39/proposal-json-modules/pull/13. 76 // 1. Suspend the currently running execution context. 77 // FIXME: We don't have suspend yet. 78 79 // 2. Let moduleContext be a new ECMAScript code execution context. 80 ExecutionContext module_context { vm.heap() }; 81 82 // 3. Set the Function of moduleContext to null. 83 // Note: This is the default value. 84 85 // 4. Set the Realm of moduleContext to module.[[Realm]]. 86 module_context.realm = &realm(); 87 88 // 5. Set the ScriptOrModule of moduleContext to module. 89 module_context.script_or_module = NonnullGCPtr<Module>(*this); 90 91 // 6. Set the VariableEnvironment of moduleContext to module.[[Environment]]. 92 module_context.variable_environment = environment(); 93 94 // 7. Set the LexicalEnvironment of moduleContext to module.[[Environment]]. 95 module_context.lexical_environment = environment(); 96 97 // 8. Push moduleContext on to the execution context stack; moduleContext is now the running execution context. 98 TRY(vm.push_execution_context(module_context, {})); 99 100 // 9. Let result be the result of performing module.[[EvaluationSteps]](module). 101 auto result = m_evaluation_steps(*this); 102 103 // 10. Suspend moduleContext and remove it from the execution context stack. 104 vm.pop_execution_context(); 105 106 // 11. Resume the context that is now on the top of the execution context stack as the running execution context. 107 108 // 12. Return Completion(result). 109 // Note: Because we expect it to return a promise we convert this here. 110 auto promise = Promise::create(realm()); 111 if (result.is_error()) { 112 VERIFY(result.throw_completion().value().has_value()); 113 promise->reject(*result.throw_completion().value()); 114 } else { 115 // Note: This value probably isn't visible to JS code? But undefined is fine anyway. 116 promise->fulfill(js_undefined()); 117 } 118 return promise.ptr(); 119} 120 121// 1.2.2 SetSyntheticModuleExport ( module, exportName, exportValue ), https://tc39.es/proposal-json-modules/#sec-setsyntheticmoduleexport 122ThrowCompletionOr<void> SyntheticModule::set_synthetic_module_export(DeprecatedFlyString const& export_name, Value export_value) 123{ 124 auto& vm = this->realm().vm(); 125 126 // Note: Has some changes from PR: https://github.com/tc39/proposal-json-modules/pull/13. 127 // 1. Return ? module.[[Environment]].SetMutableBinding(name, value, true). 128 return environment()->set_mutable_binding(vm, export_name, export_value, true); 129} 130 131// 1.3 CreateDefaultExportSyntheticModule ( defaultExport ), https://tc39.es/proposal-json-modules/#sec-create-default-export-synthetic-module 132NonnullGCPtr<SyntheticModule> SyntheticModule::create_default_export_synthetic_module(Value default_export, Realm& realm, StringView filename) 133{ 134 // Note: Has some changes from PR: https://github.com/tc39/proposal-json-modules/pull/13. 135 // 1. Let closure be the a Abstract Closure with parameters (module) that captures defaultExport and performs the following steps when called: 136 auto closure = [default_export = make_handle(default_export)](SyntheticModule& module) -> ThrowCompletionOr<void> { 137 // a. Return ? module.SetSyntheticExport("default", defaultExport). 138 return module.set_synthetic_module_export("default", default_export.value()); 139 }; 140 141 // 2. Return CreateSyntheticModule("default", closure, realm) 142 return realm.heap().allocate_without_realm<SyntheticModule>(Vector<DeprecatedFlyString> { "default" }, move(closure), realm, filename); 143} 144 145// 1.4 ParseJSONModule ( source ), https://tc39.es/proposal-json-modules/#sec-parse-json-module 146ThrowCompletionOr<NonnullGCPtr<Module>> parse_json_module(StringView source_text, Realm& realm, StringView filename) 147{ 148 auto& vm = realm.vm(); 149 150 // 1. Let jsonParse be realm's intrinsic object named "%JSON.parse%". 151 auto* json_parse = realm.intrinsics().json_parse_function(); 152 153 // 2. Let json be ? Call(jsonParse, undefined, « sourceText »). 154 auto json = TRY(call(vm, *json_parse, js_undefined(), MUST_OR_THROW_OOM(PrimitiveString::create(realm.vm(), source_text)))); 155 156 // 3. Return CreateDefaultExportSyntheticModule(json, realm, hostDefined). 157 return SyntheticModule::create_default_export_synthetic_module(json, realm, filename); 158} 159 160}