mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
This commit enables deferred import for statically imported modules, by using the corresponding functionality in V8. It adds two tests: - smoke test exercising the basic syntax - test for deferred import of module with its own imports More test coverage should be added for mixing ESM and CJS modules, as well as TypeScript modules. Refs: https://github.com/tc39/proposal-defer-import-eval Signed-off-by: Maya Lekova <maya@igalia.com> PR-URL: https://github.com/nodejs/node/pull/63712 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com> Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
232 lines
8.1 KiB
C++
232 lines
8.1 KiB
C++
#ifndef SRC_MODULE_WRAP_H_
|
|
#define SRC_MODULE_WRAP_H_
|
|
|
|
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#include <optional>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
#include "base_object.h"
|
|
#include "v8-script.h"
|
|
|
|
namespace node {
|
|
|
|
class IsolateData;
|
|
class Environment;
|
|
class ExternalReferenceRegistry;
|
|
|
|
namespace contextify {
|
|
class ContextifyContext;
|
|
}
|
|
|
|
namespace loader {
|
|
|
|
enum ScriptType : int {
|
|
kScript,
|
|
kModule,
|
|
kFunction,
|
|
};
|
|
|
|
enum HostDefinedOptions : int {
|
|
kID = 8,
|
|
kLength = 9,
|
|
};
|
|
|
|
enum ModulePhase : int {
|
|
kSourcePhase = 1,
|
|
kDeferPhase = 2,
|
|
kEvaluationPhase = 3,
|
|
};
|
|
|
|
/**
|
|
* ModuleCacheKey is used to uniquely identify a module request
|
|
* in the module cache. It is a composition of the module specifier
|
|
* and the import attributes. ModuleImportPhase is not included
|
|
* in the key.
|
|
*/
|
|
struct ModuleCacheKey : public MemoryRetainer {
|
|
using ImportAttributeVector =
|
|
std::vector<std::pair<std::string, std::string>>;
|
|
|
|
std::string specifier;
|
|
ImportAttributeVector import_attributes;
|
|
// A hash of the specifier, and import attributes.
|
|
// This does not guarantee uniqueness, but is used to reduce
|
|
// the number of comparisons needed when checking for equality.
|
|
std::size_t hash;
|
|
|
|
SET_MEMORY_INFO_NAME(ModuleCacheKey)
|
|
SET_SELF_SIZE(ModuleCacheKey)
|
|
void MemoryInfo(MemoryTracker* tracker) const override;
|
|
|
|
// Returns a string representation of the ModuleCacheKey.
|
|
std::string ToString() const;
|
|
|
|
template <int elements_per_attribute = 3>
|
|
static ModuleCacheKey From(v8::Local<v8::String> specifier,
|
|
v8::Local<v8::FixedArray> import_attributes);
|
|
static ModuleCacheKey From(v8::Local<v8::ModuleRequest> v8_request);
|
|
|
|
struct Hash {
|
|
std::size_t operator()(const ModuleCacheKey& request) const {
|
|
return request.hash;
|
|
}
|
|
};
|
|
|
|
// Equality operator for ModuleCacheKey.
|
|
bool operator==(const ModuleCacheKey& other) const {
|
|
// Hash does not provide uniqueness guarantee, so ignore it.
|
|
return specifier == other.specifier &&
|
|
import_attributes == other.import_attributes;
|
|
}
|
|
|
|
private:
|
|
// Use public ModuleCacheKey::From to create instances.
|
|
ModuleCacheKey(std::string specifier,
|
|
ImportAttributeVector import_attributes,
|
|
std::size_t hash)
|
|
: specifier(specifier),
|
|
import_attributes(import_attributes),
|
|
hash(hash) {}
|
|
};
|
|
|
|
class ModuleWrap : public BaseObject {
|
|
public:
|
|
enum InternalFields {
|
|
kModuleSlot = BaseObject::kInternalFieldCount,
|
|
kModuleSourceObjectSlot,
|
|
kSyntheticEvaluationStepsSlot,
|
|
kContextObjectSlot, // Object whose creation context is the target Context
|
|
kLinkedRequestsSlot, // Array of linked requests, each is a ModuleWrap JS
|
|
// wrapper object.
|
|
kInternalFieldCount
|
|
};
|
|
|
|
static void CreatePerIsolateProperties(IsolateData* isolate_data,
|
|
v8::Local<v8::ObjectTemplate> target);
|
|
static void CreatePerContextProperties(v8::Local<v8::Object> target,
|
|
v8::Local<v8::Value> unused,
|
|
v8::Local<v8::Context> context,
|
|
void* priv);
|
|
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
|
|
static void HostInitializeImportMetaObjectCallback(
|
|
v8::Local<v8::Context> context,
|
|
v8::Local<v8::Module> module,
|
|
v8::Local<v8::Object> meta);
|
|
|
|
v8::Local<v8::Context> context() const;
|
|
v8::Maybe<bool> CheckUnsettledTopLevelAwait();
|
|
bool HasAsyncGraph();
|
|
|
|
SET_MEMORY_INFO_NAME(ModuleWrap)
|
|
SET_SELF_SIZE(ModuleWrap)
|
|
SET_NO_MEMORY_INFO()
|
|
|
|
bool IsNotIndicativeOfMemoryLeakAtExit() const override {
|
|
// XXX: The garbage collection rules for ModuleWrap are *super* unclear.
|
|
// Do these objects ever get GC'd? Are we just okay with leaking them?
|
|
return true;
|
|
}
|
|
|
|
bool IsLinked() const { return linked_; }
|
|
|
|
static v8::Local<v8::PrimitiveArray> GetHostDefinedOptions(
|
|
v8::Isolate* isolate, v8::Local<v8::Symbol> symbol);
|
|
|
|
// When user_cached_data is not std::nullopt, use the code cache if it's not
|
|
// nullptr, otherwise don't use code cache.
|
|
// TODO(joyeecheung): when it is std::nullopt, use on-disk cache
|
|
// See: https://github.com/nodejs/node/issues/47472
|
|
static v8::MaybeLocal<v8::Module> CompileSourceTextModule(
|
|
Realm* realm,
|
|
v8::Local<v8::String> source_text,
|
|
v8::Local<v8::String> url,
|
|
int line_offset,
|
|
int column_offset,
|
|
v8::Local<v8::PrimitiveArray> host_defined_options,
|
|
std::optional<v8::ScriptCompiler::CachedData*> user_cached_data,
|
|
bool* cache_rejected);
|
|
|
|
static void CreateRequiredModuleFacade(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
|
|
private:
|
|
ModuleWrap(Realm* realm,
|
|
v8::Local<v8::Object> object,
|
|
v8::Local<v8::Module> module,
|
|
v8::Local<v8::String> url,
|
|
v8::Local<v8::Object> context_object,
|
|
v8::Local<v8::Value> synthetic_evaluation_step);
|
|
~ModuleWrap() override;
|
|
|
|
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void GetModuleRequests(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void SetModuleSourceObject(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void GetModuleSourceObject(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
|
|
static void Link(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Instantiate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Evaluate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void EvaluateSync(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void GetNamespace(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void GetStatus(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void GetError(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
|
|
static void HasAsyncGraph(v8::Local<v8::Name> property,
|
|
const v8::PropertyCallbackInfo<v8::Value>& args);
|
|
|
|
static void SetImportModuleDynamicallyCallback(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void SetImportMetaResolveInitializer(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void SetInitializeImportMetaObjectCallback(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static v8::MaybeLocal<v8::Value> SyntheticModuleEvaluationStepsCallback(
|
|
v8::Local<v8::Context> context, v8::Local<v8::Module> module);
|
|
static void SetSyntheticExport(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void CreateCachedData(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
|
|
static v8::MaybeLocal<v8::Module> ResolveModuleCallback(
|
|
v8::Local<v8::Context> context,
|
|
size_t module_request_index,
|
|
v8::Local<v8::Module> referrer);
|
|
static v8::MaybeLocal<v8::Object> ResolveSourceCallback(
|
|
v8::Local<v8::Context> context,
|
|
size_t module_request_index,
|
|
v8::Local<v8::Module> referrer);
|
|
static ModuleWrap* GetFromModule(node::Environment*, v8::Local<v8::Module>);
|
|
|
|
// This method may throw a JavaScript exception, so the return type is
|
|
// wrapped in a Maybe.
|
|
static v8::Maybe<ModuleWrap*> ResolveModule(v8::Local<v8::Context> context,
|
|
size_t module_request_index,
|
|
v8::Local<v8::Module> referrer);
|
|
|
|
std::string url_;
|
|
v8::Global<v8::Module> module_;
|
|
contextify::ContextifyContext* contextify_context_ = nullptr;
|
|
bool synthetic_ = false;
|
|
bool linked_ = false;
|
|
// This depends on the module to be instantiated so it begins with a
|
|
// nullopt value.
|
|
std::optional<bool> has_async_graph_ = std::nullopt;
|
|
int module_hash_;
|
|
// Corresponds to the ModuleWrap* of the wrappers in kLinkedRequestsSlot.
|
|
// These are populated during Link(), and are only valid after that as
|
|
// convenient shortcuts, but do not hold the ModuleWraps alive. The actual
|
|
// strong references come from the array in kLinkedRequestsSlot.
|
|
std::vector<ModuleWrap*> linked_module_wraps_;
|
|
};
|
|
|
|
} // namespace loader
|
|
} // namespace node
|
|
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#endif // SRC_MODULE_WRAP_H_
|