1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-09 17:44:56 +09:00

LibJS: Update spec steps / links for the JSON modules proposal

This proposal has reached stage 4 and been merged into the main ECMA-262
spec. See:

3feb1ba
This commit is contained in:
Timothy Flynn 2025-04-28 16:22:48 -04:00 committed by Tim Flynn
parent 3867a192a1
commit 2401764697
Notes: github-actions[bot] 2025-04-29 11:34:58 +00:00
5 changed files with 191 additions and 130 deletions

View file

@ -419,22 +419,57 @@ JS_DEFINE_NATIVE_FUNCTION(JSONObject::parse)
{
auto& realm = *vm.current_realm();
auto string = TRY(vm.argument(0).to_string(vm));
auto text = vm.argument(0);
auto reviver = vm.argument(1);
auto json = JsonValue::from_string(string);
if (json.is_error())
return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed);
Value unfiltered = parse_json_value(vm, json.value());
// 1. Let jsonString be ? ToString(text).
auto json_string = TRY(text.to_string(vm));
// 2. Let unfiltered be ? ParseJSON(jsonString).
auto unfiltered = TRY(parse_json(vm, json_string));
// 3. If IsCallable(reviver) is true, then
if (reviver.is_function()) {
// a. Let root be OrdinaryObjectCreate(%Object.prototype%).
auto root = Object::create(realm, realm.intrinsics().object_prototype());
auto root_name = String {};
// b. Let rootName be the empty String.
String root_name;
// c. Perform ! CreateDataPropertyOrThrow(root, rootName, unfiltered).
MUST(root->create_data_property_or_throw(root_name, unfiltered));
// d. Return ? InternalizeJSONProperty(root, rootName, reviver).
return internalize_json_property(vm, root, root_name, reviver.as_function());
}
// 4. Else,
// a. Return unfiltered.
return unfiltered;
}
// 25.5.1.1 ParseJSON ( text ), https://tc39.es/ecma262/#sec-ParseJSON
ThrowCompletionOr<Value> JSONObject::parse_json(VM& vm, StringView text)
{
auto json = JsonValue::from_string(text);
// 1. If StringToCodePoints(text) is not a valid JSON text as specified in ECMA-404, throw a SyntaxError exception.
if (json.is_error())
return vm.throw_completion<SyntaxError>(ErrorType::JsonMalformed);
// 2. Let scriptString be the string-concatenation of "(", text, and ");".
// 3. Let script be ParseText(scriptString, Script).
// 4. NOTE: The early error rules defined in 13.2.5.1 have special handling for the above invocation of ParseText.
// 5. Assert: script is a Parse Node.
// 6. Let result be ! Evaluation of script.
auto result = JSONObject::parse_json_value(vm, json.value());
// 7. NOTE: The PropertyDefinitionEvaluation semantics defined in 13.2.5.5 have special handling for the above evaluation.
// 8. Assert: result is either a String, a Number, a Boolean, an Object that is defined by either an ArrayLiteral or an ObjectLiteral, or null.
// 9. Return result.
return result;
}
Value JSONObject::parse_json_value(VM& vm, JsonValue const& value)
{
if (value.is_object())
@ -448,7 +483,7 @@ Value JSONObject::parse_json_value(VM& vm, JsonValue const& value)
if (value.is_string())
return PrimitiveString::create(vm, value.as_string());
if (value.is_bool())
return Value(static_cast<bool>(value.as_bool()));
return Value(value.as_bool());
VERIFY_NOT_REACHED();
}

View file

@ -22,6 +22,7 @@ public:
// test-js to communicate between the JS tests and the C++ test runner.
static ThrowCompletionOr<Optional<String>> stringify_impl(VM&, Value value, Value replacer, Value space);
static ThrowCompletionOr<Value> parse_json(VM&, StringView text);
static Value parse_json_value(VM&, JsonValue const&);
private:

View file

@ -525,10 +525,9 @@ VM::StoredModule* VM::get_stored_module(ImportedModuleReferrer const&, ByteStrin
// FinishLoadingImportedModule(referrer, specifier, payload, result) where result is a normal completion,
// then it must perform FinishLoadingImportedModule(referrer, specifier, payload, result) with the same result each time.
// Editor's Note from https://tc39.es/proposal-json-modules/#sec-hostresolveimportedmodule
// The above text implies that is recommended but not required that hosts do not use moduleRequest.[[Assertions]]
// as part of the module cache key. In either case, an exception thrown from an import with a given assertion list
// does not rule out success of another import with the same specifier but a different assertion list.
// Editor's Note from https://tc39.es/ecma262/#sec-hostresolveimportedmodule
// The above text requires that hosts support JSON modules when imported with type: "json" (and HostLoadImportedModule
// completes normally), but it does not prohibit hosts from supporting JSON modules when imported without type: "json".
// FIXME: This should probably check referrer as well.
auto end_or_module = m_loaded_modules.find_if([&](StoredModule const& stored_module) {
@ -612,6 +611,9 @@ void VM::load_imported_module(ImportedModuleReferrer referrer, ModuleRequest con
// - If this operation is called multiple times with the same (referrer, specifier) pair and it performs
// FinishLoadingImportedModule(referrer, specifier, payload, result) where result is a normal completion,
// then it must perform FinishLoadingImportedModule(referrer, specifier, payload, result) with the same result each time.
// - If moduleRequest.[[Attributes]] has an entry entry such that entry.[[Key]] is "type" and entry.[[Value]] is "json",
// when the host environment performs FinishLoadingImportedModule(referrer, moduleRequest, payload, result), result
// must either be the Completion Record returned by an invocation of ParseJSONModule or a throw completion.
// - The operation must treat payload as an opaque value to be passed through to FinishLoadingImportedModule.
//
// The actual process performed is host-defined, but typically consists of performing whatever I/O operations are necessary to
@ -680,10 +682,9 @@ void VM::load_imported_module(ImportedModuleReferrer referrer, ModuleRequest con
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] resolved {} + {} -> {}", base_path, module_request.module_specifier, filename);
#endif
auto* loaded_module_or_end = get_stored_module(referrer, filename, module_type);
if (loaded_module_or_end != nullptr) {
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] load_imported_module({}) already loaded at {}", filename, loaded_module_or_end->module.ptr());
finish_loading_imported_module(referrer, module_request, payload, *loaded_module_or_end->module);
if (auto* loaded_module = get_stored_module(referrer, filename, module_type)) {
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] load_imported_module({}) already loaded at {}", filename, loaded_module->module.ptr());
finish_loading_imported_module(referrer, module_request, payload, *loaded_module->module);
return;
}
@ -710,12 +711,13 @@ void VM::load_imported_module(ImportedModuleReferrer referrer, ModuleRequest con
StringView const content_view { file_content_or_error.value().bytes() };
auto module = [&]() -> ThrowCompletionOr<GC::Ref<Module>> {
// If assertions has an entry entry such that entry.[[Key]] is "type", let type be entry.[[Value]]. The following requirements apply:
// If type is "json", then this algorithm must either invoke ParseJSONModule and return the resulting Completion Record, or throw an exception.
auto module = [&, content = file_content_or_error.release_value()]() -> ThrowCompletionOr<GC::Ref<Module>> {
// If moduleRequest.[[Attributes]] has an entry entry such that entry.[[Key]] is "type" and entry.[[Value]] is "json",
// when the host environment performs FinishLoadingImportedModule(referrer, moduleRequest, payload, result), result
// must either be the Completion Record returned by an invocation of ParseJSONModule or a throw completion.
if (module_type == "json"sv) {
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] reading and parsing JSON module {}", filename);
return parse_json_module(content_view, *current_realm(), filename);
return parse_json_module(*current_realm(), content_view, filename);
}
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] reading and parsing as SourceTextModule module {}", filename);
@ -727,16 +729,17 @@ void VM::load_imported_module(ImportedModuleReferrer referrer, ModuleRequest con
return throw_completion<SyntaxError>(module_or_errors.error().first().to_byte_string());
}
auto module = module_or_errors.release_value();
return module_or_errors.release_value();
}();
if (!module.is_error()) {
m_loaded_modules.empend(
referrer,
module->filename(),
module.value()->filename(),
String {}, // Null type
make_root<Module>(*module),
make_root(module.value()),
true);
return module;
}();
}
finish_loading_imported_module(referrer, module_request, payload, module);
}

View file

@ -7,6 +7,7 @@
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/GlobalEnvironment.h>
#include <LibJS/Runtime/JSONObject.h>
#include <LibJS/Runtime/ModuleEnvironment.h>
#include <LibJS/Runtime/PromiseCapability.h>
#include <LibJS/Runtime/PromiseConstructor.h>
@ -17,23 +18,94 @@ namespace JS {
GC_DEFINE_ALLOCATOR(SyntheticModule);
// 1.2.1 CreateSyntheticModule ( exportNames, evaluationSteps, realm, hostDefined ), https://tc39.es/proposal-json-modules/#sec-createsyntheticmodule
SyntheticModule::SyntheticModule(Vector<FlyString> export_names, SyntheticModule::EvaluationFunction evaluation_steps, Realm& realm, StringView filename)
: Module(realm, filename)
SyntheticModule::SyntheticModule(Realm& realm, Vector<FlyString> export_names, SyntheticModule::EvaluationFunction evaluation_steps, ByteString filename)
: Module(realm, move(filename))
, m_export_names(move(export_names))
, m_evaluation_steps(move(evaluation_steps))
, m_evaluation_steps(evaluation_steps)
{
// 1. Return Synthetic Module Record { [[Realm]]: realm, [[Environment]]: undefined, [[Namespace]]: undefined, [[HostDefined]]: hostDefined, [[ExportNames]]: exportNames, [[EvaluationSteps]]: evaluationSteps }.
}
// 1.2.3.1 GetExportedNames( exportStarSet ), https://tc39.es/proposal-json-modules/#sec-smr-getexportednames
void SyntheticModule::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_evaluation_steps);
}
// 16.2.1.8.1 CreateDefaultExportSyntheticModule ( defaultExport ), https://tc39.es/ecma262/#sec-create-default-export-synthetic-module
GC::Ref<SyntheticModule> SyntheticModule::create_default_export_synthetic_module(Realm& realm, Value default_export, ByteString filename)
{
// 1. Let realm be the current Realm Record.
// 2. Let setDefaultExport be a new Abstract Closure with parameters (module) that captures defaultExport and
// performs the following steps when called:
auto set_default_export = GC::create_function(realm.heap(), [default_export](SyntheticModule& module) -> ThrowCompletionOr<void> {
// a. Perform SetSyntheticModuleExport(module, "default", defaultExport).
TRY(module.set_synthetic_module_export("default"_fly_string, default_export));
// b. Return NormalCompletion(UNUSED).
return {};
});
// 2. Return the Synthetic Module Record { [[Realm]]: realm, [[Environment]]: empty, [[Namespace]]: empty, [[HostDefined]]: undefined, [[ExportNames]]: « "default" », [[EvaluationSteps]]: setDefaultExport }.
return realm.heap().allocate<SyntheticModule>(realm, Vector<FlyString> { "default"_fly_string }, set_default_export, move(filename));
}
// 16.2.1.8.2 ParseJSONModule ( source ), https://tc39.es/ecma262/#sec-create-default-export-synthetic-module
ThrowCompletionOr<GC::Ref<Module>> parse_json_module(Realm& realm, StringView source_text, ByteString filename)
{
auto& vm = realm.vm();
// 1. Let json be ? ParseJSON(source).
auto json = TRY(JSONObject::parse_json(vm, source_text));
// 3. Return CreateDefaultExportSyntheticModule(json).
return SyntheticModule::create_default_export_synthetic_module(realm, json, move(filename));
}
// 16.2.1.8.3 SetSyntheticModuleExport ( module, exportName, exportValue ), https://tc39.es/ecma262/#sec-setsyntheticmoduleexport
ThrowCompletionOr<void> SyntheticModule::set_synthetic_module_export(FlyString const& export_name, Value export_value)
{
auto& vm = this->vm();
// 1. Assert: module.[[ExportNames]] contains exportName.
VERIFY(m_export_names.contains_slow(export_name));
// 2. Let envRec be module.[[Environment]].
auto environment_record = environment();
// 3. Assert: envRec is not EMPTY.
VERIFY(environment_record);
// 4. Perform envRec.SetMutableBinding(exportName, exportValue, true).
TRY(environment_record->set_mutable_binding(vm, export_name, export_value, true));
// 5. Return UNUSED.
return {};
}
// 16.2.1.8.4.1 LoadRequestedModules ( ), https://tc39.es/ecma262/#sec-smr-LoadRequestedModules
PromiseCapability& SyntheticModule::load_requested_modules(GC::Ptr<GraphLoadingState::HostDefined>)
{
auto& realm = this->realm();
auto& vm = realm.vm();
// 1. Return ! PromiseResolve(%Promise%, undefined).
auto promise_capability = MUST(new_promise_capability(vm, realm.intrinsics().promise_constructor()));
(void)MUST(call(vm, *promise_capability->resolve(), js_undefined(), js_undefined()));
// NOTE: We need to return a PromiseCapability, rather than a Promise, so we flatten PromiseResolve here.
// This is likely a spec bug, see https://matrixlogs.bakkot.com/WHATWG/2023-02-13#L1
return promise_capability;
}
// 16.2.1.8.4.2 GetExportedNames ( ), https://tc39.es/ecma262/#sec-smr-getexportednames
Vector<FlyString> SyntheticModule::get_exported_names(VM&, HashTable<Module const*>&)
{
// 1. Return module.[[ExportNames]].
return m_export_names;
}
// 1.2.3.2 ResolveExport( exportName, resolveSet ), https://tc39.es/proposal-json-modules/#sec-smr-resolveexport
// 16.2.1.8.4.3 ResolveExport ( exportName ), https://tc39.es/ecma262/#sec-smr-resolveexport
ResolvedBinding SyntheticModule::resolve_export(VM&, FlyString const& export_name, Vector<ResolvedBinding>)
{
// 1. If module.[[ExportNames]] does not contain exportName, return null.
@ -44,132 +116,78 @@ ResolvedBinding SyntheticModule::resolve_export(VM&, FlyString const& export_nam
return ResolvedBinding { ResolvedBinding::BindingName, this, export_name };
}
// 1.2.3.3 Link ( ), https://tc39.es/proposal-json-modules/#sec-smr-instantiate
// 16.2.1.8.4.4 Link ( ), https://tc39.es/ecma262/#sec-smr-Link
ThrowCompletionOr<void> SyntheticModule::link(VM& vm)
{
// Note: Has some changes from PR: https://github.com/tc39/proposal-json-modules/pull/13.
// Which includes renaming it from Instantiate ( ) to Link ( ).
// 1. Let realm be module.[[Realm]].
// 2. Assert: realm is not undefined.
// Note: This must be true because we use a reference.
auto& realm = this->realm();
// 3. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]).
auto environment = vm.heap().allocate<ModuleEnvironment>(&realm().global_environment());
// 2. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]).
auto environment = vm.heap().allocate<ModuleEnvironment>(&realm.global_environment());
// 4. Set module.[[Environment]] to env.
// 3. Set module.[[Environment]] to env.
set_environment(environment);
// 5. For each exportName in module.[[ExportNames]],
for (auto& export_name : m_export_names) {
// a. Perform ! envRec.CreateMutableBinding(exportName, false).
// 4. For each String exportName of module.[[ExportNames]], do
for (auto const& export_name : m_export_names) {
// a. Perform ! env.CreateMutableBinding(exportName, false).
MUST(environment->create_mutable_binding(vm, export_name, false));
// b. Perform ! envRec.InitializeBinding(exportName, undefined, normal).
// b. Perform ! env.InitializeBinding(exportName, undefined).
MUST(environment->initialize_binding(vm, export_name, js_undefined(), Environment::InitializeBindingHint::Normal));
}
// 6. Return unused.
// 5. Return NormalCompletion(unused).
return {};
}
// 1.2.3.4 Evaluate ( ), https://tc39.es/proposal-json-modules/#sec-smr-Evaluate
// 16.2.1.8.4.5 Evaluate ( ), https://tc39.es/ecma262/#sec-smr-Evaluate
ThrowCompletionOr<GC::Ref<Promise>> SyntheticModule::evaluate(VM& vm)
{
// Note: Has some changes from PR: https://github.com/tc39/proposal-json-modules/pull/13.
// 1. Suspend the currently running execution context.
// NOTE: Done by the push on step 8.
auto& realm = this->realm();
// 2. Let moduleContext be a new ECMAScript code execution context.
// 1. Let moduleContext be a new ECMAScript code execution context.
// 2. Set the Function of moduleContext to null.
ExecutionContext* module_context = nullptr;
ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK(module_context, 0, 0);
// 3. Set the Function of moduleContext to null.
// Note: This is the default value.
// 3. Set the Realm of moduleContext to module.[[Realm]].
module_context->realm = &realm;
// 4. Set the Realm of moduleContext to module.[[Realm]].
module_context->realm = &realm();
// 5. Set the ScriptOrModule of moduleContext to module.
// 4. Set the ScriptOrModule of moduleContext to module.
module_context->script_or_module = GC::Ref<Module>(*this);
// 6. Set the VariableEnvironment of moduleContext to module.[[Environment]].
// 5. Set the VariableEnvironment of moduleContext to module.[[Environment]].
module_context->variable_environment = environment();
// 7. Set the LexicalEnvironment of moduleContext to module.[[Environment]].
// 6. Set the LexicalEnvironment of moduleContext to module.[[Environment]].
module_context->lexical_environment = environment();
// 8. Push moduleContext on to the execution context stack; moduleContext is now the running execution context.
// 7. Suspend the running execution context.
// 8. Push moduleContext onto the execution context stack; moduleContext is now the running execution context.
TRY(vm.push_execution_context(*module_context, {}));
// 9. Let result be the result of performing module.[[EvaluationSteps]](module).
auto result = m_evaluation_steps(*this);
// 9. Let steps be module.[[EvaluationSteps]].
// 10. Let result be Completion(steps(module)).
auto result = m_evaluation_steps->function()(*this);
// 10. Suspend moduleContext and remove it from the execution context stack.
// 11. Suspend moduleContext and remove it from the execution context stack.
// 12. Resume the context that is now on the top of the execution context stack as the running execution context.
vm.pop_execution_context();
// 11. Resume the context that is now on the top of the execution context stack as the running execution context.
// 13. Let pc be ! NewPromiseCapability(%Promise%).
auto promise_capability = MUST(new_promise_capability(vm, realm.intrinsics().promise_constructor()));
// 12. Return Completion(result).
// Note: Because we expect it to return a promise we convert this here.
auto promise = Promise::create(realm());
if (result.is_error()) {
promise->reject(result.throw_completion().value());
} else {
// Note: This value probably isn't visible to JS code? But undefined is fine anyway.
promise->fulfill(js_undefined());
}
// 14. IfAbruptRejectPromise(result, pc).
if (result.is_error())
MUST(call(vm, *promise_capability->reject(), JS::js_undefined(), result.release_error().value()));
return promise;
}
// 15. Perform ! Call(pc.[[Resolve]], undefined, « undefined »).
else
MUST(call(vm, *promise_capability->resolve(), js_undefined(), js_undefined()));
// 1.2.2 SetSyntheticModuleExport ( module, exportName, exportValue ), https://tc39.es/proposal-json-modules/#sec-setsyntheticmoduleexport
ThrowCompletionOr<void> SyntheticModule::set_synthetic_module_export(FlyString const& export_name, Value export_value)
{
auto& vm = this->realm().vm();
// Note: Has some changes from PR: https://github.com/tc39/proposal-json-modules/pull/13.
// 1. Return ? module.[[Environment]].SetMutableBinding(name, value, true).
return environment()->set_mutable_binding(vm, export_name, export_value, true);
}
// 1.3 CreateDefaultExportSyntheticModule ( defaultExport ), https://tc39.es/proposal-json-modules/#sec-create-default-export-synthetic-module
GC::Ref<SyntheticModule> SyntheticModule::create_default_export_synthetic_module(Value default_export, Realm& realm, StringView filename)
{
// Note: Has some changes from PR: https://github.com/tc39/proposal-json-modules/pull/13.
// 1. Let closure be the a Abstract Closure with parameters (module) that captures defaultExport and performs the following steps when called:
auto closure = [default_export = make_root(default_export)](SyntheticModule& module) -> ThrowCompletionOr<void> {
// a. Return ? module.SetSyntheticExport("default", defaultExport).
return module.set_synthetic_module_export("default"_fly_string, default_export.value());
};
// 2. Return CreateSyntheticModule("default", closure, realm)
return realm.heap().allocate<SyntheticModule>(Vector<FlyString> { "default"_fly_string }, move(closure), realm, filename);
}
// 1.4 ParseJSONModule ( source ), https://tc39.es/proposal-json-modules/#sec-parse-json-module
ThrowCompletionOr<GC::Ref<Module>> parse_json_module(StringView source_text, Realm& realm, StringView filename)
{
auto& vm = realm.vm();
// 1. Let jsonParse be realm's intrinsic object named "%JSON.parse%".
auto json_parse = realm.intrinsics().json_parse_function();
// 2. Let json be ? Call(jsonParse, undefined, « sourceText »).
auto json = TRY(call(vm, *json_parse, js_undefined(), PrimitiveString::create(realm.vm(), source_text)));
// 3. Return CreateDefaultExportSyntheticModule(json, realm, hostDefined).
return SyntheticModule::create_default_export_synthetic_module(json, realm, filename);
}
// 1.2.3.1 LoadRequestedModules ( ), https://tc39.es/proposal-json-modules/#sec-smr-LoadRequestedModules
PromiseCapability& SyntheticModule::load_requested_modules(GC::Ptr<GraphLoadingState::HostDefined>)
{
// 1. Return ! PromiseResolve(%Promise%, undefined).
auto& constructor = *vm().current_realm()->intrinsics().promise_constructor();
auto promise_capability = MUST(new_promise_capability(vm(), &constructor));
MUST(call(vm(), *promise_capability->resolve(), js_undefined(), js_undefined()));
return promise_capability;
// 16. Return pc.[[Promise]].
return static_cast<Promise&>(*promise_capability->promise());
}
}

View file

@ -6,35 +6,39 @@
#pragma once
#include <LibGC/Function.h>
#include <LibGC/Ptr.h>
#include <LibJS/Module.h>
namespace JS {
// 1.2 Synthetic Module Records, https://tc39.es/proposal-json-modules/#sec-synthetic-module-records
// 16.2.1.8 Synthetic Module Records, https://tc39.es/ecma262/#sec-synthetic-module-records
class SyntheticModule final : public Module {
GC_CELL(SyntheticModule, Module);
GC_DECLARE_ALLOCATOR(SyntheticModule);
public:
using EvaluationFunction = Function<ThrowCompletionOr<void>(SyntheticModule&)>;
using EvaluationFunction = GC::Ref<GC::Function<ThrowCompletionOr<void>(SyntheticModule&)>>;
static GC::Ref<SyntheticModule> create_default_export_synthetic_module(Value default_export, Realm& realm, StringView filename);
static GC::Ref<SyntheticModule> create_default_export_synthetic_module(Realm& realm, Value default_export, ByteString filename);
ThrowCompletionOr<void> set_synthetic_module_export(FlyString const& export_name, Value export_value);
virtual ThrowCompletionOr<void> link(VM& vm) override;
virtual ThrowCompletionOr<GC::Ref<Promise>> evaluate(VM& vm) override;
virtual PromiseCapability& load_requested_modules(GC::Ptr<GraphLoadingState::HostDefined>) override;
virtual Vector<FlyString> get_exported_names(VM& vm, HashTable<Module const*>& export_star_set) override;
virtual ResolvedBinding resolve_export(VM& vm, FlyString const& export_name, Vector<ResolvedBinding> resolve_set) override;
virtual PromiseCapability& load_requested_modules(GC::Ptr<GraphLoadingState::HostDefined>) override;
virtual ThrowCompletionOr<void> link(VM& vm) override;
virtual ThrowCompletionOr<GC::Ref<Promise>> evaluate(VM& vm) override;
private:
SyntheticModule(Vector<FlyString> export_names, EvaluationFunction evaluation_steps, Realm& realm, StringView filename);
SyntheticModule(Realm& realm, Vector<FlyString> export_names, EvaluationFunction evaluation_steps, ByteString filename);
virtual void visit_edges(Cell::Visitor&) override;
Vector<FlyString> m_export_names; // [[ExportNames]]
EvaluationFunction m_evaluation_steps; // [[EvaluationSteps]]
};
ThrowCompletionOr<GC::Ref<Module>> parse_json_module(StringView source_text, Realm& realm, StringView filename);
ThrowCompletionOr<GC::Ref<Module>> parse_json_module(Realm& realm, StringView source_text, ByteString filename);
}