mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
Instead of storing the function template of BaseObject for checking if an object is BaseObject by calling HasInstance, simply checks the first internal field of the object, which is always set in the BaseObject constructor. This is simpler and faster (there is now no need to iterate over the inheritance for the check). PR-URL: https://github.com/nodejs/node/pull/47217 Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
835 lines
27 KiB
C++
835 lines
27 KiB
C++
#include "module_wrap.h"
|
|
|
|
#include "env.h"
|
|
#include "memory_tracker-inl.h"
|
|
#include "node_contextify.h"
|
|
#include "node_errors.h"
|
|
#include "node_external_reference.h"
|
|
#include "node_internals.h"
|
|
#include "node_process-inl.h"
|
|
#include "node_watchdog.h"
|
|
#include "util-inl.h"
|
|
|
|
#include <sys/stat.h> // S_IFDIR
|
|
|
|
#include <algorithm>
|
|
|
|
namespace node {
|
|
namespace loader {
|
|
|
|
using errors::TryCatchScope;
|
|
|
|
using node::contextify::ContextifyContext;
|
|
using v8::Array;
|
|
using v8::ArrayBufferView;
|
|
using v8::Context;
|
|
using v8::EscapableHandleScope;
|
|
using v8::FixedArray;
|
|
using v8::Function;
|
|
using v8::FunctionCallbackInfo;
|
|
using v8::FunctionTemplate;
|
|
using v8::HandleScope;
|
|
using v8::Int32;
|
|
using v8::Integer;
|
|
using v8::IntegrityLevel;
|
|
using v8::Isolate;
|
|
using v8::Local;
|
|
using v8::MaybeLocal;
|
|
using v8::MicrotaskQueue;
|
|
using v8::Module;
|
|
using v8::ModuleRequest;
|
|
using v8::Number;
|
|
using v8::Object;
|
|
using v8::PrimitiveArray;
|
|
using v8::Promise;
|
|
using v8::ScriptCompiler;
|
|
using v8::ScriptOrigin;
|
|
using v8::String;
|
|
using v8::UnboundModuleScript;
|
|
using v8::Undefined;
|
|
using v8::Value;
|
|
|
|
ModuleWrap::ModuleWrap(Environment* env,
|
|
Local<Object> object,
|
|
Local<Module> module,
|
|
Local<String> url)
|
|
: BaseObject(env, object),
|
|
module_(env->isolate(), module),
|
|
id_(env->get_next_module_id()) {
|
|
env->id_to_module_map.emplace(id_, this);
|
|
|
|
Local<Value> undefined = Undefined(env->isolate());
|
|
object->SetInternalField(kURLSlot, url);
|
|
object->SetInternalField(kSyntheticEvaluationStepsSlot, undefined);
|
|
object->SetInternalField(kContextObjectSlot, undefined);
|
|
}
|
|
|
|
ModuleWrap::~ModuleWrap() {
|
|
HandleScope scope(env()->isolate());
|
|
Local<Module> module = module_.Get(env()->isolate());
|
|
env()->id_to_module_map.erase(id_);
|
|
auto range = env()->hash_to_module_map.equal_range(module->GetIdentityHash());
|
|
for (auto it = range.first; it != range.second; ++it) {
|
|
if (it->second == this) {
|
|
env()->hash_to_module_map.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Local<Context> ModuleWrap::context() const {
|
|
Local<Value> obj = object()->GetInternalField(kContextObjectSlot);
|
|
if (obj.IsEmpty()) return {};
|
|
return obj.As<Object>()->GetCreationContext().ToLocalChecked();
|
|
}
|
|
|
|
ModuleWrap* ModuleWrap::GetFromModule(Environment* env,
|
|
Local<Module> module) {
|
|
auto range = env->hash_to_module_map.equal_range(module->GetIdentityHash());
|
|
for (auto it = range.first; it != range.second; ++it) {
|
|
if (it->second->module_ == module) {
|
|
return it->second;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
ModuleWrap* ModuleWrap::GetFromID(Environment* env, uint32_t id) {
|
|
auto module_wrap_it = env->id_to_module_map.find(id);
|
|
if (module_wrap_it == env->id_to_module_map.end()) {
|
|
return nullptr;
|
|
}
|
|
return module_wrap_it->second;
|
|
}
|
|
|
|
// new ModuleWrap(url, context, source, lineOffset, columnOffset)
|
|
// new ModuleWrap(url, context, exportNames, syntheticExecutionFunction)
|
|
void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
|
|
CHECK(args.IsConstructCall());
|
|
CHECK_GE(args.Length(), 3);
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
Isolate* isolate = env->isolate();
|
|
|
|
Local<Object> that = args.This();
|
|
|
|
CHECK(args[0]->IsString());
|
|
Local<String> url = args[0].As<String>();
|
|
|
|
Local<Context> context;
|
|
ContextifyContext* contextify_context = nullptr;
|
|
if (args[1]->IsUndefined()) {
|
|
context = that->GetCreationContext().ToLocalChecked();
|
|
} else {
|
|
CHECK(args[1]->IsObject());
|
|
contextify_context = ContextifyContext::ContextFromContextifiedSandbox(
|
|
env, args[1].As<Object>());
|
|
CHECK_NOT_NULL(contextify_context);
|
|
context = contextify_context->context();
|
|
}
|
|
|
|
int line_offset = 0;
|
|
int column_offset = 0;
|
|
|
|
bool synthetic = args[2]->IsArray();
|
|
if (synthetic) {
|
|
// new ModuleWrap(url, context, exportNames, syntheticExecutionFunction)
|
|
CHECK(args[3]->IsFunction());
|
|
} else {
|
|
// new ModuleWrap(url, context, source, lineOffset, columOffset, cachedData)
|
|
CHECK(args[2]->IsString());
|
|
CHECK(args[3]->IsNumber());
|
|
line_offset = args[3].As<Int32>()->Value();
|
|
CHECK(args[4]->IsNumber());
|
|
column_offset = args[4].As<Int32>()->Value();
|
|
}
|
|
|
|
Local<PrimitiveArray> host_defined_options =
|
|
PrimitiveArray::New(isolate, HostDefinedOptions::kLength);
|
|
host_defined_options->Set(isolate, HostDefinedOptions::kType,
|
|
Number::New(isolate, ScriptType::kModule));
|
|
|
|
ShouldNotAbortOnUncaughtScope no_abort_scope(env);
|
|
TryCatchScope try_catch(env);
|
|
|
|
Local<Module> module;
|
|
|
|
{
|
|
Context::Scope context_scope(context);
|
|
if (synthetic) {
|
|
CHECK(args[2]->IsArray());
|
|
Local<Array> export_names_arr = args[2].As<Array>();
|
|
|
|
uint32_t len = export_names_arr->Length();
|
|
std::vector<Local<String>> export_names(len);
|
|
for (uint32_t i = 0; i < len; i++) {
|
|
Local<Value> export_name_val =
|
|
export_names_arr->Get(context, i).ToLocalChecked();
|
|
CHECK(export_name_val->IsString());
|
|
export_names[i] = export_name_val.As<String>();
|
|
}
|
|
|
|
module = Module::CreateSyntheticModule(isolate, url, export_names,
|
|
SyntheticModuleEvaluationStepsCallback);
|
|
} else {
|
|
ScriptCompiler::CachedData* cached_data = nullptr;
|
|
if (!args[5]->IsUndefined()) {
|
|
CHECK(args[5]->IsArrayBufferView());
|
|
Local<ArrayBufferView> cached_data_buf = args[5].As<ArrayBufferView>();
|
|
uint8_t* data =
|
|
static_cast<uint8_t*>(cached_data_buf->Buffer()->Data());
|
|
cached_data =
|
|
new ScriptCompiler::CachedData(data + cached_data_buf->ByteOffset(),
|
|
cached_data_buf->ByteLength());
|
|
}
|
|
|
|
Local<String> source_text = args[2].As<String>();
|
|
ScriptOrigin origin(isolate,
|
|
url,
|
|
line_offset,
|
|
column_offset,
|
|
true, // is cross origin
|
|
-1, // script id
|
|
Local<Value>(), // source map URL
|
|
false, // is opaque (?)
|
|
false, // is WASM
|
|
true, // is ES Module
|
|
host_defined_options);
|
|
ScriptCompiler::Source source(source_text, origin, cached_data);
|
|
ScriptCompiler::CompileOptions options;
|
|
if (source.GetCachedData() == nullptr) {
|
|
options = ScriptCompiler::kNoCompileOptions;
|
|
} else {
|
|
options = ScriptCompiler::kConsumeCodeCache;
|
|
}
|
|
if (!ScriptCompiler::CompileModule(isolate, &source, options)
|
|
.ToLocal(&module)) {
|
|
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
|
|
CHECK(!try_catch.Message().IsEmpty());
|
|
CHECK(!try_catch.Exception().IsEmpty());
|
|
AppendExceptionLine(env, try_catch.Exception(), try_catch.Message(),
|
|
ErrorHandlingMode::MODULE_ERROR);
|
|
try_catch.ReThrow();
|
|
}
|
|
return;
|
|
}
|
|
if (options == ScriptCompiler::kConsumeCodeCache &&
|
|
source.GetCachedData()->rejected) {
|
|
THROW_ERR_VM_MODULE_CACHED_DATA_REJECTED(
|
|
env, "cachedData buffer was rejected");
|
|
try_catch.ReThrow();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!that->Set(context, env->url_string(), url).FromMaybe(false)) {
|
|
return;
|
|
}
|
|
|
|
ModuleWrap* obj = new ModuleWrap(env, that, module, url);
|
|
|
|
if (synthetic) {
|
|
obj->synthetic_ = true;
|
|
obj->object()->SetInternalField(kSyntheticEvaluationStepsSlot, args[3]);
|
|
}
|
|
|
|
// Use the extras object as an object whose GetCreationContext() will be the
|
|
// original `context`, since the `Context` itself strictly speaking cannot
|
|
// be stored in an internal field.
|
|
obj->object()->SetInternalField(kContextObjectSlot,
|
|
context->GetExtrasBindingObject());
|
|
obj->contextify_context_ = contextify_context;
|
|
|
|
env->hash_to_module_map.emplace(module->GetIdentityHash(), obj);
|
|
|
|
host_defined_options->Set(isolate, HostDefinedOptions::kID,
|
|
Number::New(isolate, obj->id()));
|
|
|
|
that->SetIntegrityLevel(context, IntegrityLevel::kFrozen);
|
|
args.GetReturnValue().Set(that);
|
|
}
|
|
|
|
static Local<Object> createImportAssertionContainer(Environment* env,
|
|
Isolate* isolate, Local<FixedArray> raw_assertions) {
|
|
Local<Object> assertions =
|
|
Object::New(isolate, v8::Null(env->isolate()), nullptr, nullptr, 0);
|
|
for (int i = 0; i < raw_assertions->Length(); i += 3) {
|
|
assertions
|
|
->Set(env->context(),
|
|
raw_assertions->Get(env->context(), i).As<String>(),
|
|
raw_assertions->Get(env->context(), i + 1).As<Value>())
|
|
.ToChecked();
|
|
}
|
|
|
|
return assertions;
|
|
}
|
|
|
|
void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
Isolate* isolate = args.GetIsolate();
|
|
|
|
CHECK_EQ(args.Length(), 1);
|
|
CHECK(args[0]->IsFunction());
|
|
|
|
Local<Object> that = args.This();
|
|
|
|
ModuleWrap* obj;
|
|
ASSIGN_OR_RETURN_UNWRAP(&obj, that);
|
|
|
|
if (obj->linked_)
|
|
return;
|
|
obj->linked_ = true;
|
|
|
|
Local<Function> resolver_arg = args[0].As<Function>();
|
|
|
|
Local<Context> mod_context = obj->context();
|
|
Local<Module> module = obj->module_.Get(isolate);
|
|
|
|
Local<FixedArray> module_requests = module->GetModuleRequests();
|
|
const int module_requests_length = module_requests->Length();
|
|
MaybeStackBuffer<Local<Value>, 16> promises(module_requests_length);
|
|
|
|
// call the dependency resolve callbacks
|
|
for (int i = 0; i < module_requests_length; i++) {
|
|
Local<ModuleRequest> module_request =
|
|
module_requests->Get(env->context(), i).As<ModuleRequest>();
|
|
Local<String> specifier = module_request->GetSpecifier();
|
|
Utf8Value specifier_utf8(env->isolate(), specifier);
|
|
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
|
|
|
|
Local<FixedArray> raw_assertions = module_request->GetImportAssertions();
|
|
Local<Object> assertions =
|
|
createImportAssertionContainer(env, isolate, raw_assertions);
|
|
|
|
Local<Value> argv[] = {
|
|
specifier,
|
|
assertions,
|
|
};
|
|
|
|
MaybeLocal<Value> maybe_resolve_return_value =
|
|
resolver_arg->Call(mod_context, that, arraysize(argv), argv);
|
|
if (maybe_resolve_return_value.IsEmpty()) {
|
|
return;
|
|
}
|
|
Local<Value> resolve_return_value =
|
|
maybe_resolve_return_value.ToLocalChecked();
|
|
if (!resolve_return_value->IsPromise()) {
|
|
THROW_ERR_VM_MODULE_LINK_FAILURE(
|
|
env, "request for '%s' did not return promise", specifier_std);
|
|
return;
|
|
}
|
|
Local<Promise> resolve_promise = resolve_return_value.As<Promise>();
|
|
obj->resolve_cache_[specifier_std].Reset(env->isolate(), resolve_promise);
|
|
|
|
promises[i] = resolve_promise;
|
|
}
|
|
|
|
args.GetReturnValue().Set(
|
|
Array::New(isolate, promises.out(), promises.length()));
|
|
}
|
|
|
|
void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
Isolate* isolate = args.GetIsolate();
|
|
ModuleWrap* obj;
|
|
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
|
|
Local<Context> context = obj->context();
|
|
Local<Module> module = obj->module_.Get(isolate);
|
|
TryCatchScope try_catch(env);
|
|
USE(module->InstantiateModule(context, ResolveModuleCallback));
|
|
|
|
// clear resolve cache on instantiate
|
|
obj->resolve_cache_.clear();
|
|
|
|
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
|
|
CHECK(!try_catch.Message().IsEmpty());
|
|
CHECK(!try_catch.Exception().IsEmpty());
|
|
AppendExceptionLine(env, try_catch.Exception(), try_catch.Message(),
|
|
ErrorHandlingMode::MODULE_ERROR);
|
|
try_catch.ReThrow();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
Isolate* isolate = env->isolate();
|
|
ModuleWrap* obj;
|
|
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
|
|
Local<Context> context = obj->context();
|
|
Local<Module> module = obj->module_.Get(isolate);
|
|
|
|
ContextifyContext* contextify_context = obj->contextify_context_;
|
|
std::shared_ptr<MicrotaskQueue> microtask_queue;
|
|
if (contextify_context != nullptr)
|
|
microtask_queue = contextify_context->microtask_queue();
|
|
|
|
// module.evaluate(timeout, breakOnSigint)
|
|
CHECK_EQ(args.Length(), 2);
|
|
|
|
CHECK(args[0]->IsNumber());
|
|
int64_t timeout = args[0]->IntegerValue(env->context()).FromJust();
|
|
|
|
CHECK(args[1]->IsBoolean());
|
|
bool break_on_sigint = args[1]->IsTrue();
|
|
|
|
ShouldNotAbortOnUncaughtScope no_abort_scope(env);
|
|
TryCatchScope try_catch(env);
|
|
Isolate::SafeForTerminationScope safe_for_termination(env->isolate());
|
|
|
|
bool timed_out = false;
|
|
bool received_signal = false;
|
|
MaybeLocal<Value> result;
|
|
auto run = [&]() {
|
|
MaybeLocal<Value> result = module->Evaluate(context);
|
|
if (!result.IsEmpty() && microtask_queue)
|
|
microtask_queue->PerformCheckpoint(isolate);
|
|
return result;
|
|
};
|
|
if (break_on_sigint && timeout != -1) {
|
|
Watchdog wd(isolate, timeout, &timed_out);
|
|
SigintWatchdog swd(isolate, &received_signal);
|
|
result = run();
|
|
} else if (break_on_sigint) {
|
|
SigintWatchdog swd(isolate, &received_signal);
|
|
result = run();
|
|
} else if (timeout != -1) {
|
|
Watchdog wd(isolate, timeout, &timed_out);
|
|
result = run();
|
|
} else {
|
|
result = run();
|
|
}
|
|
|
|
if (result.IsEmpty()) {
|
|
CHECK(try_catch.HasCaught());
|
|
}
|
|
|
|
// Convert the termination exception into a regular exception.
|
|
if (timed_out || received_signal) {
|
|
if (!env->is_main_thread() && env->is_stopping())
|
|
return;
|
|
env->isolate()->CancelTerminateExecution();
|
|
// It is possible that execution was terminated by another timeout in
|
|
// which this timeout is nested, so check whether one of the watchdogs
|
|
// from this invocation is responsible for termination.
|
|
if (timed_out) {
|
|
THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout);
|
|
} else if (received_signal) {
|
|
THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(env);
|
|
}
|
|
}
|
|
|
|
if (try_catch.HasCaught()) {
|
|
if (!try_catch.HasTerminated())
|
|
try_catch.ReThrow();
|
|
return;
|
|
}
|
|
|
|
args.GetReturnValue().Set(result.ToLocalChecked());
|
|
}
|
|
|
|
void ModuleWrap::GetNamespace(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
Isolate* isolate = args.GetIsolate();
|
|
ModuleWrap* obj;
|
|
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
|
|
|
|
Local<Module> module = obj->module_.Get(isolate);
|
|
|
|
switch (module->GetStatus()) {
|
|
case v8::Module::Status::kUninstantiated:
|
|
case v8::Module::Status::kInstantiating:
|
|
return env->ThrowError(
|
|
"cannot get namespace, module has not been instantiated");
|
|
case v8::Module::Status::kInstantiated:
|
|
case v8::Module::Status::kEvaluating:
|
|
case v8::Module::Status::kEvaluated:
|
|
case v8::Module::Status::kErrored:
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
|
|
Local<Value> result = module->GetModuleNamespace();
|
|
args.GetReturnValue().Set(result);
|
|
}
|
|
|
|
void ModuleWrap::GetStatus(const FunctionCallbackInfo<Value>& args) {
|
|
Isolate* isolate = args.GetIsolate();
|
|
ModuleWrap* obj;
|
|
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
|
|
|
|
Local<Module> module = obj->module_.Get(isolate);
|
|
|
|
args.GetReturnValue().Set(module->GetStatus());
|
|
}
|
|
|
|
void ModuleWrap::GetStaticDependencySpecifiers(
|
|
const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
ModuleWrap* obj;
|
|
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
|
|
|
|
Local<Module> module = obj->module_.Get(env->isolate());
|
|
|
|
Local<FixedArray> module_requests = module->GetModuleRequests();
|
|
int count = module_requests->Length();
|
|
|
|
MaybeStackBuffer<Local<Value>, 16> specifiers(count);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
Local<ModuleRequest> module_request =
|
|
module_requests->Get(env->context(), i).As<ModuleRequest>();
|
|
specifiers[i] = module_request->GetSpecifier();
|
|
}
|
|
|
|
args.GetReturnValue().Set(
|
|
Array::New(env->isolate(), specifiers.out(), count));
|
|
}
|
|
|
|
void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
|
|
Isolate* isolate = args.GetIsolate();
|
|
ModuleWrap* obj;
|
|
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
|
|
|
|
Local<Module> module = obj->module_.Get(isolate);
|
|
args.GetReturnValue().Set(module->GetException());
|
|
}
|
|
|
|
MaybeLocal<Module> ModuleWrap::ResolveModuleCallback(
|
|
Local<Context> context,
|
|
Local<String> specifier,
|
|
Local<FixedArray> import_assertions,
|
|
Local<Module> referrer) {
|
|
Environment* env = Environment::GetCurrent(context);
|
|
if (env == nullptr) {
|
|
Isolate* isolate = context->GetIsolate();
|
|
THROW_ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE(isolate);
|
|
return MaybeLocal<Module>();
|
|
}
|
|
|
|
Isolate* isolate = env->isolate();
|
|
|
|
Utf8Value specifier_utf8(isolate, specifier);
|
|
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
|
|
|
|
ModuleWrap* dependent = GetFromModule(env, referrer);
|
|
if (dependent == nullptr) {
|
|
THROW_ERR_VM_MODULE_LINK_FAILURE(
|
|
env, "request for '%s' is from invalid module", specifier_std);
|
|
return MaybeLocal<Module>();
|
|
}
|
|
|
|
if (dependent->resolve_cache_.count(specifier_std) != 1) {
|
|
THROW_ERR_VM_MODULE_LINK_FAILURE(
|
|
env, "request for '%s' is not in cache", specifier_std);
|
|
return MaybeLocal<Module>();
|
|
}
|
|
|
|
Local<Promise> resolve_promise =
|
|
dependent->resolve_cache_[specifier_std].Get(isolate);
|
|
|
|
if (resolve_promise->State() != Promise::kFulfilled) {
|
|
THROW_ERR_VM_MODULE_LINK_FAILURE(
|
|
env, "request for '%s' is not yet fulfilled", specifier_std);
|
|
return MaybeLocal<Module>();
|
|
}
|
|
|
|
Local<Object> module_object = resolve_promise->Result().As<Object>();
|
|
if (module_object.IsEmpty() || !module_object->IsObject()) {
|
|
THROW_ERR_VM_MODULE_LINK_FAILURE(
|
|
env, "request for '%s' did not return an object", specifier_std);
|
|
return MaybeLocal<Module>();
|
|
}
|
|
|
|
ModuleWrap* module;
|
|
ASSIGN_OR_RETURN_UNWRAP(&module, module_object, MaybeLocal<Module>());
|
|
return module->module_.Get(isolate);
|
|
}
|
|
|
|
static MaybeLocal<Promise> ImportModuleDynamically(
|
|
Local<Context> context,
|
|
Local<v8::Data> host_defined_options,
|
|
Local<Value> resource_name,
|
|
Local<String> specifier,
|
|
Local<FixedArray> import_assertions) {
|
|
Isolate* isolate = context->GetIsolate();
|
|
Environment* env = Environment::GetCurrent(context);
|
|
if (env == nullptr) {
|
|
THROW_ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE(isolate);
|
|
return MaybeLocal<Promise>();
|
|
}
|
|
|
|
EscapableHandleScope handle_scope(isolate);
|
|
|
|
Local<Function> import_callback =
|
|
env->host_import_module_dynamically_callback();
|
|
|
|
Local<FixedArray> options = host_defined_options.As<FixedArray>();
|
|
if (options->Length() != HostDefinedOptions::kLength) {
|
|
Local<Promise::Resolver> resolver;
|
|
if (!Promise::Resolver::New(context).ToLocal(&resolver)) return {};
|
|
resolver
|
|
->Reject(context,
|
|
v8::Exception::TypeError(FIXED_ONE_BYTE_STRING(
|
|
context->GetIsolate(), "Invalid host defined options")))
|
|
.ToChecked();
|
|
return handle_scope.Escape(resolver->GetPromise());
|
|
}
|
|
|
|
Local<Value> object;
|
|
|
|
int type = options->Get(context, HostDefinedOptions::kType)
|
|
.As<Number>()
|
|
->Int32Value(context)
|
|
.ToChecked();
|
|
uint32_t id = options->Get(context, HostDefinedOptions::kID)
|
|
.As<Number>()
|
|
->Uint32Value(context)
|
|
.ToChecked();
|
|
if (type == ScriptType::kScript) {
|
|
contextify::ContextifyScript* wrap = env->id_to_script_map.find(id)->second;
|
|
object = wrap->object();
|
|
} else if (type == ScriptType::kModule) {
|
|
ModuleWrap* wrap = ModuleWrap::GetFromID(env, id);
|
|
object = wrap->object();
|
|
} else if (type == ScriptType::kFunction) {
|
|
auto it = env->id_to_function_map.find(id);
|
|
CHECK_NE(it, env->id_to_function_map.end());
|
|
object = it->second->object();
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
|
|
Local<Object> assertions =
|
|
createImportAssertionContainer(env, isolate, import_assertions);
|
|
|
|
Local<Value> import_args[] = {
|
|
object,
|
|
Local<Value>(specifier),
|
|
assertions,
|
|
};
|
|
|
|
Local<Value> result;
|
|
if (import_callback->Call(
|
|
context,
|
|
Undefined(isolate),
|
|
arraysize(import_args),
|
|
import_args).ToLocal(&result)) {
|
|
CHECK(result->IsPromise());
|
|
return handle_scope.Escape(result.As<Promise>());
|
|
}
|
|
|
|
return MaybeLocal<Promise>();
|
|
}
|
|
|
|
void ModuleWrap::SetImportModuleDynamicallyCallback(
|
|
const FunctionCallbackInfo<Value>& args) {
|
|
Isolate* isolate = args.GetIsolate();
|
|
Environment* env = Environment::GetCurrent(args);
|
|
HandleScope handle_scope(isolate);
|
|
|
|
CHECK_EQ(args.Length(), 1);
|
|
CHECK(args[0]->IsFunction());
|
|
Local<Function> import_callback = args[0].As<Function>();
|
|
env->set_host_import_module_dynamically_callback(import_callback);
|
|
|
|
isolate->SetHostImportModuleDynamicallyCallback(ImportModuleDynamically);
|
|
}
|
|
|
|
void ModuleWrap::HostInitializeImportMetaObjectCallback(
|
|
Local<Context> context, Local<Module> module, Local<Object> meta) {
|
|
Environment* env = Environment::GetCurrent(context);
|
|
if (env == nullptr)
|
|
return;
|
|
ModuleWrap* module_wrap = GetFromModule(env, module);
|
|
|
|
if (module_wrap == nullptr) {
|
|
return;
|
|
}
|
|
|
|
Local<Object> wrap = module_wrap->object();
|
|
Local<Function> callback =
|
|
env->host_initialize_import_meta_object_callback();
|
|
Local<Value> args[] = { wrap, meta };
|
|
TryCatchScope try_catch(env);
|
|
USE(callback->Call(
|
|
context, Undefined(env->isolate()), arraysize(args), args));
|
|
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
|
|
try_catch.ReThrow();
|
|
}
|
|
}
|
|
|
|
void ModuleWrap::SetInitializeImportMetaObjectCallback(
|
|
const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
Isolate* isolate = env->isolate();
|
|
|
|
CHECK_EQ(args.Length(), 1);
|
|
CHECK(args[0]->IsFunction());
|
|
Local<Function> import_meta_callback = args[0].As<Function>();
|
|
env->set_host_initialize_import_meta_object_callback(import_meta_callback);
|
|
|
|
isolate->SetHostInitializeImportMetaObjectCallback(
|
|
HostInitializeImportMetaObjectCallback);
|
|
}
|
|
|
|
MaybeLocal<Value> ModuleWrap::SyntheticModuleEvaluationStepsCallback(
|
|
Local<Context> context, Local<Module> module) {
|
|
Environment* env = Environment::GetCurrent(context);
|
|
Isolate* isolate = env->isolate();
|
|
|
|
ModuleWrap* obj = GetFromModule(env, module);
|
|
|
|
TryCatchScope try_catch(env);
|
|
Local<Function> synthetic_evaluation_steps =
|
|
obj->object()->GetInternalField(kSyntheticEvaluationStepsSlot)
|
|
.As<Function>();
|
|
obj->object()->SetInternalField(
|
|
kSyntheticEvaluationStepsSlot, Undefined(isolate));
|
|
MaybeLocal<Value> ret = synthetic_evaluation_steps->Call(context,
|
|
obj->object(), 0, nullptr);
|
|
if (ret.IsEmpty()) {
|
|
CHECK(try_catch.HasCaught());
|
|
}
|
|
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
|
|
CHECK(!try_catch.Message().IsEmpty());
|
|
CHECK(!try_catch.Exception().IsEmpty());
|
|
try_catch.ReThrow();
|
|
return MaybeLocal<Value>();
|
|
}
|
|
|
|
Local<Promise::Resolver> resolver;
|
|
if (!Promise::Resolver::New(context).ToLocal(&resolver)) {
|
|
return MaybeLocal<Value>();
|
|
}
|
|
|
|
resolver->Resolve(context, Undefined(isolate)).ToChecked();
|
|
return resolver->GetPromise();
|
|
}
|
|
|
|
void ModuleWrap::SetSyntheticExport(const FunctionCallbackInfo<Value>& args) {
|
|
Isolate* isolate = args.GetIsolate();
|
|
Local<Object> that = args.This();
|
|
|
|
ModuleWrap* obj;
|
|
ASSIGN_OR_RETURN_UNWRAP(&obj, that);
|
|
|
|
CHECK(obj->synthetic_);
|
|
|
|
CHECK_EQ(args.Length(), 2);
|
|
|
|
CHECK(args[0]->IsString());
|
|
Local<String> export_name = args[0].As<String>();
|
|
|
|
Local<Value> export_value = args[1];
|
|
|
|
Local<Module> module = obj->module_.Get(isolate);
|
|
USE(module->SetSyntheticModuleExport(isolate, export_name, export_value));
|
|
}
|
|
|
|
void ModuleWrap::CreateCachedData(const FunctionCallbackInfo<Value>& args) {
|
|
Isolate* isolate = args.GetIsolate();
|
|
Local<Object> that = args.This();
|
|
|
|
ModuleWrap* obj;
|
|
ASSIGN_OR_RETURN_UNWRAP(&obj, that);
|
|
|
|
CHECK(!obj->synthetic_);
|
|
|
|
Local<Module> module = obj->module_.Get(isolate);
|
|
|
|
CHECK_LT(module->GetStatus(), v8::Module::Status::kEvaluating);
|
|
|
|
Local<UnboundModuleScript> unbound_module_script =
|
|
module->GetUnboundModuleScript();
|
|
std::unique_ptr<ScriptCompiler::CachedData> cached_data(
|
|
ScriptCompiler::CreateCodeCache(unbound_module_script));
|
|
Environment* env = Environment::GetCurrent(args);
|
|
if (!cached_data) {
|
|
args.GetReturnValue().Set(Buffer::New(env, 0).ToLocalChecked());
|
|
} else {
|
|
MaybeLocal<Object> buf =
|
|
Buffer::Copy(env,
|
|
reinterpret_cast<const char*>(cached_data->data),
|
|
cached_data->length);
|
|
args.GetReturnValue().Set(buf.ToLocalChecked());
|
|
}
|
|
}
|
|
|
|
void ModuleWrap::Initialize(Local<Object> target,
|
|
Local<Value> unused,
|
|
Local<Context> context,
|
|
void* priv) {
|
|
Environment* env = Environment::GetCurrent(context);
|
|
Isolate* isolate = env->isolate();
|
|
|
|
Local<FunctionTemplate> tpl = NewFunctionTemplate(isolate, New);
|
|
tpl->InstanceTemplate()->SetInternalFieldCount(
|
|
ModuleWrap::kInternalFieldCount);
|
|
|
|
SetProtoMethod(isolate, tpl, "link", Link);
|
|
SetProtoMethod(isolate, tpl, "instantiate", Instantiate);
|
|
SetProtoMethod(isolate, tpl, "evaluate", Evaluate);
|
|
SetProtoMethod(isolate, tpl, "setExport", SetSyntheticExport);
|
|
SetProtoMethodNoSideEffect(
|
|
isolate, tpl, "createCachedData", CreateCachedData);
|
|
SetProtoMethodNoSideEffect(isolate, tpl, "getNamespace", GetNamespace);
|
|
SetProtoMethodNoSideEffect(isolate, tpl, "getStatus", GetStatus);
|
|
SetProtoMethodNoSideEffect(isolate, tpl, "getError", GetError);
|
|
SetProtoMethodNoSideEffect(isolate,
|
|
tpl,
|
|
"getStaticDependencySpecifiers",
|
|
GetStaticDependencySpecifiers);
|
|
|
|
SetConstructorFunction(context, target, "ModuleWrap", tpl);
|
|
|
|
SetMethod(context,
|
|
target,
|
|
"setImportModuleDynamicallyCallback",
|
|
SetImportModuleDynamicallyCallback);
|
|
SetMethod(context,
|
|
target,
|
|
"setInitializeImportMetaObjectCallback",
|
|
SetInitializeImportMetaObjectCallback);
|
|
|
|
#define V(name) \
|
|
target->Set(context, \
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), #name), \
|
|
Integer::New(env->isolate(), Module::Status::name)) \
|
|
.FromJust()
|
|
V(kUninstantiated);
|
|
V(kInstantiating);
|
|
V(kInstantiated);
|
|
V(kEvaluating);
|
|
V(kEvaluated);
|
|
V(kErrored);
|
|
#undef V
|
|
}
|
|
|
|
void ModuleWrap::RegisterExternalReferences(
|
|
ExternalReferenceRegistry* registry) {
|
|
registry->Register(New);
|
|
|
|
registry->Register(Link);
|
|
registry->Register(Instantiate);
|
|
registry->Register(Evaluate);
|
|
registry->Register(SetSyntheticExport);
|
|
registry->Register(CreateCachedData);
|
|
registry->Register(GetNamespace);
|
|
registry->Register(GetStatus);
|
|
registry->Register(GetError);
|
|
registry->Register(GetStaticDependencySpecifiers);
|
|
|
|
registry->Register(SetImportModuleDynamicallyCallback);
|
|
registry->Register(SetInitializeImportMetaObjectCallback);
|
|
}
|
|
} // namespace loader
|
|
} // namespace node
|
|
|
|
NODE_BINDING_CONTEXT_AWARE_INTERNAL(module_wrap,
|
|
node::loader::ModuleWrap::Initialize)
|
|
NODE_BINDING_EXTERNAL_REFERENCE(
|
|
module_wrap, node::loader::ModuleWrap::RegisterExternalReferences)
|