mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-16 04:59:23 +00:00
This has quite a lot of fall out. But the majority of it is just type or UDL substitution, where the changes just fall through to other function calls. By changing property key storage to UTF-16, the main affected areas are: * NativeFunction names must now be UTF-16 * Bytecode identifiers must now be UTF-16 * Module/binding names must now be UTF-16
208 lines
7.7 KiB
C++
208 lines
7.7 KiB
C++
/*
|
|
* Copyright (c) 2021-2025, Andreas Kling <andreas@ladybird.org>
|
|
* Copyright (c) 2022, David Tuin <davidot@serenityos.org>
|
|
* Copyright (c) 2023, networkException <networkexception@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/GenericShorthands.h>
|
|
#include <LibJS/CyclicModule.h>
|
|
#include <LibJS/Module.h>
|
|
#include <LibJS/Runtime/ModuleEnvironment.h>
|
|
#include <LibJS/Runtime/ModuleNamespaceObject.h>
|
|
#include <LibJS/Runtime/ModuleRequest.h>
|
|
#include <LibJS/Runtime/Promise.h>
|
|
#include <LibJS/Runtime/VM.h>
|
|
|
|
namespace JS {
|
|
|
|
GC_DEFINE_ALLOCATOR(Module);
|
|
GC_DEFINE_ALLOCATOR(GraphLoadingState);
|
|
|
|
Module::Module(Realm& realm, ByteString filename, Script::HostDefined* host_defined)
|
|
: m_realm(realm)
|
|
, m_host_defined(host_defined)
|
|
, m_filename(move(filename))
|
|
{
|
|
}
|
|
|
|
Module::~Module() = default;
|
|
|
|
void Module::visit_edges(Cell::Visitor& visitor)
|
|
{
|
|
Base::visit_edges(visitor);
|
|
visitor.visit(m_realm);
|
|
visitor.visit(m_environment);
|
|
visitor.visit(m_namespace);
|
|
if (m_host_defined)
|
|
m_host_defined->visit_host_defined_self(visitor);
|
|
}
|
|
|
|
// 16.2.1.5.1 EvaluateModuleSync ( module ), https://tc39.es/ecma262/#sec-EvaluateModuleSync
|
|
ThrowCompletionOr<void> Module::evaluate_module_sync(VM& vm)
|
|
{
|
|
// 1. Assert: module is not a Cyclic Module Record.
|
|
// 2. Let promise be module.Evaluate().
|
|
auto promise = TRY(evaluate(vm));
|
|
|
|
// 3. Assert: promise.[[PromiseState]] is either FULFILLED or REJECTED.
|
|
VERIFY(first_is_one_of(promise->state(), Promise::State::Fulfilled, Promise::State::Rejected));
|
|
|
|
// 4. If promise.[[PromiseState]] is REJECTED, then
|
|
if (promise->state() == Promise::State::Rejected) {
|
|
// a. If promise.[[PromiseIsHandled]] is false, perform HostPromiseRejectionTracker(promise, "handle").
|
|
if (!promise->is_handled())
|
|
vm.host_promise_rejection_tracker(promise, Promise::RejectionOperation::Handle);
|
|
|
|
// b. Set promise.[[PromiseIsHandled]] to true.
|
|
promise->set_is_handled();
|
|
|
|
// c. Return ThrowCompletion(promise.[[PromiseResult]]).
|
|
return throw_completion(promise->result());
|
|
}
|
|
|
|
// 5. Return UNUSED.
|
|
return {};
|
|
}
|
|
|
|
// 16.2.1.5.1.1 InnerModuleLinking ( module, stack, index ), https://tc39.es/ecma262/#sec-InnerModuleLinking
|
|
ThrowCompletionOr<u32> Module::inner_module_linking(VM& vm, Vector<Module*>&, u32 index)
|
|
{
|
|
// 1. If module is not a Cyclic Module Record, then
|
|
// a. Perform ? module.Link().
|
|
TRY(link(vm));
|
|
// b. Return index.
|
|
return index;
|
|
}
|
|
|
|
// 16.2.1.5.2.1 InnerModuleEvaluation ( module, stack, index ), https://tc39.es/ecma262/#sec-innermoduleevaluation
|
|
ThrowCompletionOr<u32> Module::inner_module_evaluation(VM& vm, Vector<Module*>&, u32 index)
|
|
{
|
|
// 1. If module is not a Cyclic Module Record, then
|
|
// a. Perform ? EvaluateModuleSync(module).
|
|
TRY(evaluate_module_sync(vm));
|
|
|
|
// b. Return index.
|
|
return index;
|
|
}
|
|
|
|
// 16.2.1.9 FinishLoadingImportedModule ( referrer, specifier, payload, result ), https://tc39.es/ecma262/#sec-FinishLoadingImportedModule
|
|
void finish_loading_imported_module(ImportedModuleReferrer referrer, ModuleRequest const& module_request, ImportedModulePayload payload, ThrowCompletionOr<GC::Ref<Module>> const& result)
|
|
{
|
|
// 1. If result is a normal completion, then
|
|
if (!result.is_error()) {
|
|
// 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>& {
|
|
VERIFY_NOT_REACHED();
|
|
__builtin_unreachable();
|
|
},
|
|
[](auto& script_or_module) -> Vector<ModuleWithSpecifier>& {
|
|
return script_or_module->loaded_modules();
|
|
});
|
|
|
|
bool found_record = false;
|
|
|
|
// a. If referrer.[[LoadedModules]] contains a Record whose [[Specifier]] is specifier, then
|
|
for (auto const& record : loaded_modules) {
|
|
if (record.specifier == module_request.module_specifier) {
|
|
// i. Assert: That Record's [[Module]] is result.[[Value]].
|
|
VERIFY(record.module == result.value());
|
|
found_record = true;
|
|
}
|
|
}
|
|
|
|
// b. Else,
|
|
if (!found_record) {
|
|
auto module = result.value();
|
|
|
|
// i. Append the Record { [[Specifier]]: specifier, [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]].
|
|
loaded_modules.append(ModuleWithSpecifier {
|
|
.specifier = module_request.module_specifier.to_string(),
|
|
.module = GC::Ref<Module>(*module) });
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. If payload is a GraphLoadingState Record, then
|
|
if (payload.has<GC::Ref<GraphLoadingState>>()) {
|
|
// a. Perform ContinueModuleLoading(payload, result)
|
|
continue_module_loading(payload.get<GC::Ref<GraphLoadingState>>(), result);
|
|
}
|
|
// 3. Else,
|
|
else {
|
|
// a. Perform ContinueDynamicImport(payload, result).
|
|
continue_dynamic_import(payload.get<GC::Ref<PromiseCapability>>(), result);
|
|
}
|
|
|
|
// 4. Return unused.
|
|
}
|
|
|
|
// 16.2.1.10 GetModuleNamespace ( module ), https://tc39.es/ecma262/#sec-getmodulenamespace
|
|
GC::Ref<Object> Module::get_module_namespace(VM& vm)
|
|
{
|
|
// 1. Assert: If module is a Cyclic Module Record, then module.[[Status]] is not NEW or UNLINKED.
|
|
// FIXME: Spec bug: https://github.com/tc39/ecma262/issues/3114
|
|
|
|
// 2. Let namespace be module.[[Namespace]].
|
|
auto namespace_ = m_namespace;
|
|
|
|
// 3. If namespace is EMPTY, then
|
|
if (!namespace_) {
|
|
// a. Let exportedNames be module.GetExportedNames().
|
|
auto exported_names = get_exported_names(vm);
|
|
|
|
// b. Let unambiguousNames be a new empty List.
|
|
Vector<Utf16FlyString> unambiguous_names;
|
|
|
|
// c. For each element name of exportedNames, do
|
|
for (auto& name : exported_names) {
|
|
// i. Let resolution be module.ResolveExport(name).
|
|
auto resolution = resolve_export(vm, name);
|
|
|
|
// ii. If resolution is a ResolvedBinding Record, append name to unambiguousNames.
|
|
if (resolution.is_valid())
|
|
unambiguous_names.append(name);
|
|
}
|
|
|
|
// d. Set namespace to ModuleNamespaceCreate(module, unambiguousNames).
|
|
namespace_ = module_namespace_create(unambiguous_names);
|
|
}
|
|
|
|
// 4. Return namespace.
|
|
return *namespace_;
|
|
}
|
|
|
|
Vector<Utf16FlyString> Module::get_exported_names(VM& vm)
|
|
{
|
|
HashTable<Module const*> export_star_set;
|
|
return get_exported_names(vm, export_star_set);
|
|
}
|
|
|
|
// 10.4.6.12 ModuleNamespaceCreate ( module, exports ), https://tc39.es/ecma262/#sec-modulenamespacecreate
|
|
GC::Ref<Object> Module::module_namespace_create(Vector<Utf16FlyString> unambiguous_names)
|
|
{
|
|
auto& realm = this->realm();
|
|
|
|
// 1. Assert: module.[[Namespace]] is empty.
|
|
VERIFY(!m_namespace);
|
|
|
|
// 2. Let internalSlotsList be the internal slots listed in Table 34.
|
|
// 3. Let M be MakeBasicObject(internalSlotsList).
|
|
// 4. Set M's essential internal methods to the definitions specified in 10.4.6.
|
|
// 5. Set M.[[Module]] to module.
|
|
// 6. Let sortedExports be a List whose elements are the elements of exports ordered as if an Array of the same values had been sorted using %Array.prototype.sort% using undefined as comparefn.
|
|
// 7. Set M.[[Exports]] to sortedExports.
|
|
// 8. Create own properties of M corresponding to the definitions in 28.3.
|
|
auto module_namespace = realm.create<ModuleNamespaceObject>(realm, this, move(unambiguous_names));
|
|
|
|
// 9. Set module.[[Namespace]] to M.
|
|
m_namespace = module_namespace;
|
|
|
|
// 10. Return M.
|
|
return module_namespace;
|
|
}
|
|
|
|
}
|