mirror of
https://github.com/zebrajr/ladybird.git
synced 2025-12-06 00:19:53 +01:00
LibJS: Sync additional Import Attributes spec changes
Some steps were not updated with tc39/ecma262#3057. This patch syncs the remaining changes.
This commit is contained in:
parent
f8c4043460
commit
61c36e2865
|
|
@ -85,32 +85,35 @@ void inner_module_loading(VM& vm, JS::GraphLoadingState& state, GC::Ref<Module>
|
|||
// c. Set state.[[PendingModulesCount]] to state.[[PendingModulesCount]] + requestedModulesCount.
|
||||
state.pending_module_count += requested_modules_count;
|
||||
|
||||
// d. For each String required of module.[[RequestedModules]], do
|
||||
for (auto const& required : cyclic_module->requested_modules()) {
|
||||
bool found_record_in_loaded_modules = false;
|
||||
auto find_record_in_loaded_modules = [&](ModuleRequest const& request) -> Optional<LoadedModuleRequest const&> {
|
||||
return AK::find_value(cyclic_module->loaded_modules(), [&](auto const& record) { return module_requests_equal(record, request); });
|
||||
};
|
||||
|
||||
// i. If module.[[LoadedModules]] contains a Record whose [[Specifier]] is required, then
|
||||
for (auto const& record : cyclic_module->loaded_modules()) {
|
||||
if (record.specifier == required.module_specifier) {
|
||||
// 1. Let record be that Record.
|
||||
// d. For each ModuleRequest Record request of module.[[RequestedModules]], do
|
||||
for (auto const& request : cyclic_module->requested_modules()) {
|
||||
// i. If AllImportAttributesSupported(request.[[Attributes]]) is false, then
|
||||
if (!all_import_attributes_supported(vm, request.attributes)) {
|
||||
// 1. Let error be ThrowCompletion(a newly created SyntaxError object).
|
||||
auto error = vm.throw_completion<SyntaxError>(ErrorType::ImportAttributeUnsupported);
|
||||
|
||||
// 2. Perform InnerModuleLoading(state, record.[[Module]]).
|
||||
inner_module_loading(vm, state, record.module);
|
||||
|
||||
found_record_in_loaded_modules = true;
|
||||
break;
|
||||
}
|
||||
// 2. Perform ContinueModuleLoading(state, error).
|
||||
continue_module_loading(state, error);
|
||||
}
|
||||
|
||||
// ii. Else,
|
||||
if (!found_record_in_loaded_modules) {
|
||||
// 1. Perform HostLoadImportedModule(module, required, state.[[HostDefined]], state).
|
||||
vm.host_load_imported_module(GC::Ref { *cyclic_module }, required, state.host_defined, GC::Ref<GraphLoadingState> { state });
|
||||
// ii. Else if module.[[LoadedModules]] contains a LoadedModuleRequest Record record
|
||||
// such that ModuleRequestsEqual(record, request) is true, then
|
||||
else if (auto record = find_record_in_loaded_modules(request); record.has_value()) {
|
||||
// 1. Perform InnerModuleLoading(state, record.[[Module]]).
|
||||
inner_module_loading(vm, state, record->module);
|
||||
}
|
||||
// iii. Else,
|
||||
else {
|
||||
// 1. Perform HostLoadImportedModule(module, request, state.[[HostDefined]], state).
|
||||
vm.host_load_imported_module(GC::Ref { *cyclic_module }, request, state.host_defined, GC::Ref<GraphLoadingState> { state });
|
||||
|
||||
// 2. NOTE: HostLoadImportedModule will call FinishLoadingImportedModule, which re-enters the graph loading process through ContinueModuleLoading.
|
||||
}
|
||||
|
||||
// iii. If state.[[IsLoading]] is false, return UNUSED.
|
||||
// iv. If state.[[IsLoading]] is false, return UNUSED.
|
||||
if (!state.is_loading)
|
||||
return;
|
||||
}
|
||||
|
|
@ -253,14 +256,13 @@ ThrowCompletionOr<u32> CyclicModule::inner_module_linking(VM& vm, Vector<Module*
|
|||
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] module: {} has requested modules: [{}]", filename(), request_module_names.string_view());
|
||||
#endif
|
||||
|
||||
// 9. For each String required of module.[[RequestedModules]], do
|
||||
for (auto& required_string : m_requested_modules) {
|
||||
ModuleRequest required { required_string };
|
||||
// 9. For each ModuleRequest Record request of module.[[RequestedModules]], do
|
||||
for (auto& request : m_requested_modules) {
|
||||
|
||||
// a. Let requiredModule be GetImportedModule(module, required).
|
||||
auto required_module = get_imported_module(required);
|
||||
// a. Let requiredModule be GetImportedModule(module, request).
|
||||
auto required_module = get_imported_module(request);
|
||||
|
||||
// b. Set index to ? InnerModuleLinking(requiredModule, stack, index).
|
||||
// b. Set index to ? InnerModuleLinking(requiredModule, stack, index).
|
||||
index = TRY(required_module->inner_module_linking(vm, stack, index));
|
||||
|
||||
// c. If requiredModule is a Cyclic Module Record, then
|
||||
|
|
@ -460,11 +462,11 @@ ThrowCompletionOr<u32> CyclicModule::inner_module_evaluation(VM& vm, Vector<Modu
|
|||
// 10. Append module to stack.
|
||||
stack.append(this);
|
||||
|
||||
// 11. For each String required of module.[[RequestedModules]], do
|
||||
for (auto& required : m_requested_modules) {
|
||||
// 11. For each ModuleRequest Record request of module.[[RequestedModules]], do
|
||||
for (auto& request : m_requested_modules) {
|
||||
|
||||
// a. Let requiredModule be GetImportedModule(module, required).
|
||||
auto required_module = get_imported_module(required);
|
||||
// a. Let requiredModule be GetImportedModule(module, request).
|
||||
auto required_module = get_imported_module(request);
|
||||
|
||||
// b. Set index to ? InnerModuleEvaluation(requiredModule, stack, index).
|
||||
index = TRY(required_module->inner_module_evaluation(vm, stack, index));
|
||||
|
|
@ -810,26 +812,24 @@ void CyclicModule::async_module_execution_rejected(VM& vm, Value error)
|
|||
// 9. Return unused.
|
||||
}
|
||||
|
||||
// 16.2.1.7 GetImportedModule ( referrer, specifier ), https://tc39.es/ecma262/#sec-GetImportedModule
|
||||
// 16.2.1.9 GetImportedModule ( referrer, request ), https://tc39.es/ecma262/#sec-GetImportedModule
|
||||
GC::Ref<Module> CyclicModule::get_imported_module(ModuleRequest const& request)
|
||||
{
|
||||
// 1. Assert: Exactly one element of referrer.[[LoadedModules]] is a Record whose [[Specifier]] is specifier,
|
||||
// since LoadRequestedModules has completed successfully on referrer prior to invoking this abstract operation.
|
||||
size_t element_with_specifier_count = 0;
|
||||
for (auto const& loaded_module : m_loaded_modules) {
|
||||
if (loaded_module.specifier == request.module_specifier)
|
||||
++element_with_specifier_count;
|
||||
// 1. Let records be a List consisting of each LoadedModuleRequest Record r of referrer.[[LoadedModules]]
|
||||
// such that ModuleRequestsEqual(r, request) is true.
|
||||
Vector<LoadedModuleRequest> records;
|
||||
for (auto const& r : m_loaded_modules) {
|
||||
if (module_requests_equal(r, request))
|
||||
records.append(r);
|
||||
}
|
||||
VERIFY(element_with_specifier_count == 1);
|
||||
|
||||
for (auto const& loaded_module : m_loaded_modules) {
|
||||
if (loaded_module.specifier == request.module_specifier) {
|
||||
// 2. Let record be the Record in referrer.[[LoadedModules]] whose [[Specifier]] is specifier.
|
||||
// 3. Return record.[[Module]].
|
||||
return loaded_module.module;
|
||||
}
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
// 2. Assert: records has exactly one element, since LoadRequestedModules has completed successfully
|
||||
// on referrer prior to invoking this abstract operation.
|
||||
VERIFY(records.size() == 1);
|
||||
|
||||
// 3. Let record be the sole element of records.
|
||||
// 4. Return record.[[Module]].
|
||||
return records.first().module;
|
||||
}
|
||||
|
||||
// 13.3.10.1.1 ContinueDynamicImport ( promiseCapability, moduleCompletion ), https://tc39.es/ecma262/#sec-ContinueDynamicImport
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ public:
|
|||
void set_status(ModuleStatus status) { m_status = status; }
|
||||
|
||||
Vector<ModuleRequest> const& requested_modules() const { return m_requested_modules; }
|
||||
Vector<ModuleWithSpecifier> const& loaded_modules() const { return m_loaded_modules; }
|
||||
Vector<ModuleWithSpecifier>& loaded_modules() { return m_loaded_modules; }
|
||||
Vector<LoadedModuleRequest> const& loaded_modules() const { return m_loaded_modules; }
|
||||
Vector<LoadedModuleRequest>& loaded_modules() { return m_loaded_modules; }
|
||||
|
||||
protected:
|
||||
CyclicModule(Realm& realm, StringView filename, bool has_top_level_await, Vector<ModuleRequest> requested_modules, Script::HostDefined* host_defined);
|
||||
|
|
@ -56,7 +56,7 @@ protected:
|
|||
virtual ThrowCompletionOr<void> initialize_environment(VM& vm);
|
||||
virtual ThrowCompletionOr<void> execute_module(VM& vm, GC::Ptr<PromiseCapability> capability = {});
|
||||
|
||||
[[nodiscard]] GC::Ref<Module> get_imported_module(ModuleRequest const&);
|
||||
[[nodiscard]] GC::Ref<Module> get_imported_module(ModuleRequest const& request);
|
||||
|
||||
void execute_async_module(VM& vm);
|
||||
void gather_available_ancestors(Vector<CyclicModule*>& exec_list);
|
||||
|
|
@ -68,7 +68,7 @@ protected:
|
|||
Optional<u32> m_dfs_index; // [[DFSIndex]]
|
||||
Optional<u32> m_dfs_ancestor_index; // [[DFSAncestorIndex]]
|
||||
Vector<ModuleRequest> m_requested_modules; // [[RequestedModules]]
|
||||
Vector<ModuleWithSpecifier> m_loaded_modules; // [[LoadedModules]]
|
||||
Vector<LoadedModuleRequest> m_loaded_modules; // [[LoadedModules]]
|
||||
GC::Ptr<CyclicModule> m_cycle_root; // [[CycleRoot]]
|
||||
bool m_has_top_level_await { false }; // [[HasTLA]]
|
||||
bool m_async_evaluation { false }; // [[AsyncEvaluation]]
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ enum class DeclarationKind;
|
|||
struct AlreadyResolved;
|
||||
class JobCallback;
|
||||
struct ModuleRequest;
|
||||
struct ModuleWithSpecifier;
|
||||
struct LoadedModuleRequest;
|
||||
|
||||
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype
|
||||
class ProxyObject;
|
||||
|
|
|
|||
|
|
@ -95,20 +95,20 @@ void finish_loading_imported_module(ImportedModuleReferrer referrer, ModuleReque
|
|||
// NOTE: Only Script and CyclicModule referrers have the [[LoadedModules]] internal slot.
|
||||
if (referrer.has<GC::Ref<Script>>() || referrer.has<GC::Ref<CyclicModule>>()) {
|
||||
auto& loaded_modules = referrer.visit(
|
||||
[](GC::Ref<JS::Realm>&) -> Vector<ModuleWithSpecifier>& {
|
||||
[](GC::Ref<JS::Realm>&) -> Vector<LoadedModuleRequest>& {
|
||||
VERIFY_NOT_REACHED();
|
||||
__builtin_unreachable();
|
||||
},
|
||||
[](auto& script_or_module) -> Vector<ModuleWithSpecifier>& {
|
||||
[](auto& script_or_module) -> Vector<LoadedModuleRequest>& {
|
||||
return script_or_module->loaded_modules();
|
||||
});
|
||||
|
||||
bool found_record = false;
|
||||
|
||||
// a. If referrer.[[LoadedModules]] contains a Record whose [[Specifier]] is specifier, then
|
||||
// a. If referrer.[[LoadedModules]] contains a LoadedModuleRequest Record record such that ModuleRequestsEqual(record, moduleRequest) is true, then
|
||||
for (auto const& record : loaded_modules) {
|
||||
if (record.specifier == module_request.module_specifier) {
|
||||
// i. Assert: That Record's [[Module]] is result.[[Value]].
|
||||
if (module_requests_equal(record, module_request)) {
|
||||
// i. Assert: record.[[Module]] and result.[[Value]] are the same Module Record.
|
||||
VERIFY(record.module == result.value());
|
||||
found_record = true;
|
||||
}
|
||||
|
|
@ -118,9 +118,10 @@ void finish_loading_imported_module(ImportedModuleReferrer referrer, ModuleReque
|
|||
if (!found_record) {
|
||||
auto module = result.value();
|
||||
|
||||
// i. Append the Record { [[Specifier]]: specifier, [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]].
|
||||
loaded_modules.append(ModuleWithSpecifier {
|
||||
// i. Append the LoadedModuleRequest Record { [[Specifier]]: moduleRequest.[[Specifier]], [[Attributes]]: moduleRequest.[[Attributes]], [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]].
|
||||
loaded_modules.append(LoadedModuleRequest {
|
||||
.specifier = module_request.module_specifier.to_utf16_string(),
|
||||
.attributes = module_request.attributes,
|
||||
.module = GC::Ref<Module>(*module) });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1764,7 +1764,7 @@ Completion dispose_resources(VM& vm, DisposeCapability& dispose_capability, Comp
|
|||
}
|
||||
|
||||
// 16.2.1.12 AllImportAttributesSupported ( attributes ), https://tc39.es/ecma262/#sec-AllImportAttributesSupported
|
||||
static bool all_import_attributes_supported(VM& vm, Vector<ImportAttribute> const& attributes)
|
||||
bool all_import_attributes_supported(VM& vm, Vector<ImportAttribute> const& attributes)
|
||||
{
|
||||
// 1. Let supported be HostGetSupportedImportAttributes().
|
||||
auto supported = vm.host_get_supported_import_attributes();
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Iterator.h>
|
||||
#include <LibJS/Runtime/KeyedCollections.h>
|
||||
#include <LibJS/Runtime/ModuleRequest.h>
|
||||
#include <LibJS/Runtime/PrivateEnvironment.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
|
|
@ -69,6 +70,8 @@ ThrowCompletionOr<GC::Ptr<FunctionObject>> get_dispose_method(VM&, Value, Enviro
|
|||
Completion dispose(VM&, Value, Environment::InitializeBindingHint, GC::Ptr<FunctionObject> method);
|
||||
Completion dispose_resources(VM&, DisposeCapability&, Completion);
|
||||
|
||||
bool all_import_attributes_supported(VM& vm, Vector<ImportAttribute> const& attributes);
|
||||
|
||||
ThrowCompletionOr<Value> perform_import_call(VM&, Value specifier, Value options_value);
|
||||
|
||||
enum class CanonicalIndexMode {
|
||||
|
|
|
|||
|
|
@ -7,17 +7,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Utf16FlyString.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibJS/Module.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
struct ModuleWithSpecifier {
|
||||
Utf16String specifier; // [[Specifier]]
|
||||
GC::Ref<Module> module; // [[Module]]
|
||||
};
|
||||
|
||||
// https://tc39.es/ecma262/#importattribute-record
|
||||
struct ImportAttribute {
|
||||
Utf16String key;
|
||||
|
|
@ -26,6 +22,13 @@ struct ImportAttribute {
|
|||
bool operator==(ImportAttribute const&) const = default;
|
||||
};
|
||||
|
||||
// https://tc39.es/ecma262/#loadedmodulerequest-record
|
||||
struct LoadedModuleRequest {
|
||||
Utf16String specifier; // [[Specifier]]
|
||||
Vector<ImportAttribute> attributes; // [[Attributes]]
|
||||
GC::Ref<Module> module; // [[Module]]
|
||||
};
|
||||
|
||||
// https://tc39.es/ecma262/#modulerequest-record
|
||||
struct ModuleRequest {
|
||||
ModuleRequest() = default;
|
||||
|
|
@ -48,4 +51,39 @@ struct ModuleRequest {
|
|||
bool operator==(ModuleRequest const&) const = default;
|
||||
};
|
||||
|
||||
inline auto const& specifier_of(ModuleRequest const& r) { return r.module_specifier; }
|
||||
inline auto const& specifier_of(LoadedModuleRequest const& r) { return r.specifier; }
|
||||
|
||||
template<typename T>
|
||||
concept ModuleRequestLike = IsOneOf<RemoveCVReference<T>, ModuleRequest, LoadedModuleRequest>;
|
||||
|
||||
// 16.2.1.3.1 ModuleRequestsEqual ( left, right ), https://tc39.es/ecma262/#sec-modulerequestsequal
|
||||
template<ModuleRequestLike L, ModuleRequestLike R>
|
||||
bool module_requests_equal(L const& left, R const& right)
|
||||
{
|
||||
// 1. If left.[[Specifier]] is not right.[[Specifier]], return false.
|
||||
if (specifier_of(left) != specifier_of(right))
|
||||
return false;
|
||||
|
||||
// 2. Let leftAttrs be left.[[Attributes]].
|
||||
// 3. Let rightAttrs be right.[[Attributes]].
|
||||
auto const& left_attrs = left.attributes;
|
||||
auto const& right_attrs = right.attributes;
|
||||
|
||||
// 4. Let leftAttrsCount be the number of elements in leftAttrs.
|
||||
// 5. Let rightAttrsCount be the number of elements in rightAttrs.
|
||||
// 6. If leftAttrsCount ≠ rightAttrsCount, return false.
|
||||
if (left_attrs.size() != right_attrs.size())
|
||||
return false;
|
||||
|
||||
// 7. For each ImportAttribute Record l of leftAttrs
|
||||
// a. If rightAttrs does not contain an ImportAttribute Record r such that l.[[Key]] is r.[[Key]] and l.[[Value]] is r.[[Value]], return false.
|
||||
// 8. Return true.
|
||||
return AK::all_of(left_attrs, [&right_attrs](auto const& l) {
|
||||
return AK::any_of(right_attrs, [&l](auto const& r) {
|
||||
return l.key == r.key && l.value == r.value;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ public:
|
|||
|
||||
Realm& realm() { return *m_realm; }
|
||||
Program const& parse_node() const { return *m_parse_node; }
|
||||
Vector<ModuleWithSpecifier>& loaded_modules() { return m_loaded_modules; }
|
||||
Vector<ModuleWithSpecifier> const& loaded_modules() const { return m_loaded_modules; }
|
||||
Vector<LoadedModuleRequest>& loaded_modules() { return m_loaded_modules; }
|
||||
Vector<LoadedModuleRequest> const& loaded_modules() const { return m_loaded_modules; }
|
||||
|
||||
HostDefined* host_defined() const { return m_host_defined; }
|
||||
StringView filename() const LIFETIME_BOUND { return m_filename; }
|
||||
|
|
@ -53,7 +53,7 @@ private:
|
|||
|
||||
GC::Ptr<Realm> m_realm; // [[Realm]]
|
||||
NonnullRefPtr<Program> m_parse_node; // [[ECMAScriptCode]]
|
||||
Vector<ModuleWithSpecifier> m_loaded_modules; // [[LoadedModules]]
|
||||
Vector<LoadedModuleRequest> m_loaded_modules; // [[LoadedModules]]
|
||||
|
||||
// Needed for potential lookups of modules.
|
||||
ByteString m_filename;
|
||||
|
|
|
|||
|
|
@ -196,6 +196,18 @@ int main(int argc, char** argv)
|
|||
if (!g_vm) {
|
||||
g_vm = JS::VM::create();
|
||||
g_vm->set_dynamic_imports_allowed(true);
|
||||
|
||||
// Configure the test VM to support additional import attributes
|
||||
// This allows tests to use import attributes beyond just "type"
|
||||
Test::JS::g_vm->host_get_supported_import_attributes = []() -> Vector<Utf16String> {
|
||||
return {
|
||||
"type"_utf16,
|
||||
"key"_utf16, // Used in modules/import-with-attributes.mjs test
|
||||
"key1"_utf16, // Used in modules/basic-modules.js
|
||||
"key2"_utf16, // Used in modules/import-with-attributes.mjs test
|
||||
"default"_utf16, // Used in modules/import-with-attributes.mjs test
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Test::JS::TestRunner test_runner(test_root, common_path, print_times, print_progress, print_json, per_file);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user