[modules][api] Add version of HostImportModuleDynamically with import assertions
This change completes support for import assertions for dynamic import(). A new version of the HostImportModuleDynamically callback taking import assertions is added to the public API. The name is very verbose; we could consider removing the "ImportAssertions" part when the old API is removed. Bytecode generation is updated to pass the assertions, if present, to Runtime_DynamicImportCall. Isolate::RunHostImportModuleDynamicallyCallback extracts the assertions from the options bag, filters out the assertions not present in the list specified by the host in HostGetSupportedImportAssertions, and sorts them by code point order of the keys per https://tc39.es/proposal-import-assertions/#sec-import-call-runtime-semantics-evaluation. The resulting array is passed to the host in the callback. Bug: v8:10958 Change-Id: I931df00f954a9f9c65bff5bcf461ba1c8f11e94e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2620578 Commit-Queue: Dan Clark <daniec@microsoft.com> Reviewed-by: Camillo Bruni <cbruni@chromium.org> Reviewed-by: Mythri Alle <mythria@chromium.org> Reviewed-by: Marja Hölttä <marja@chromium.org> Cr-Commit-Position: refs/heads/master@{#72307}
This commit is contained in:
parent
81e7e2f437
commit
a09c076f00
52
include/v8.h
52
include/v8.h
@ -7436,7 +7436,7 @@ using CallCompletedCallback = void (*)(Isolate*);
|
||||
* The specifier is the name of the module that should be imported.
|
||||
*
|
||||
* The embedder must compile, instantiate, evaluate the Module, and
|
||||
* obtain it's namespace object.
|
||||
* obtain its namespace object.
|
||||
*
|
||||
* The Promise returned from this function is forwarded to userland
|
||||
* JavaScript. The embedder must resolve this promise with the module
|
||||
@ -7445,9 +7445,43 @@ using CallCompletedCallback = void (*)(Isolate*);
|
||||
* fails (e.g. due to stack overflow), the embedder must propagate
|
||||
* that exception by returning an empty MaybeLocal.
|
||||
*/
|
||||
using HostImportModuleDynamicallyCallback = MaybeLocal<Promise> (*)(
|
||||
Local<Context> context, Local<ScriptOrModule> referrer,
|
||||
Local<String> specifier);
|
||||
using HostImportModuleDynamicallyCallback V8_DEPRECATE_SOON(
|
||||
"Use HostImportModuleDynamicallyWithImportAssertionsCallback instead") =
|
||||
MaybeLocal<Promise> (*)(Local<Context> context,
|
||||
Local<ScriptOrModule> referrer,
|
||||
Local<String> specifier);
|
||||
|
||||
/**
|
||||
* HostImportModuleDynamicallyWithImportAssertionsCallback is called when we
|
||||
* require the embedder to load a module. This is used as part of the dynamic
|
||||
* import syntax.
|
||||
*
|
||||
* The referrer contains metadata about the script/module that calls
|
||||
* import.
|
||||
*
|
||||
* The specifier is the name of the module that should be imported.
|
||||
*
|
||||
* The import_assertions are import assertions for this request in the form:
|
||||
* [key1, value1, key2, value2, ...] where the keys and values are of type
|
||||
* v8::String. Note, unlike the FixedArray passed to ResolveModuleCallback and
|
||||
* returned from ModuleRequest::GetImportAssertions(), this array does not
|
||||
* contain the source Locations of the assertions.
|
||||
*
|
||||
* The embedder must compile, instantiate, evaluate the Module, and
|
||||
* obtain its namespace object.
|
||||
*
|
||||
* The Promise returned from this function is forwarded to userland
|
||||
* JavaScript. The embedder must resolve this promise with the module
|
||||
* namespace object. In case of an exception, the embedder must reject
|
||||
* this promise with the exception. If the promise creation itself
|
||||
* fails (e.g. due to stack overflow), the embedder must propagate
|
||||
* that exception by returning an empty MaybeLocal.
|
||||
*/
|
||||
using HostImportModuleDynamicallyWithImportAssertionsCallback =
|
||||
MaybeLocal<Promise> (*)(Local<Context> context,
|
||||
Local<ScriptOrModule> referrer,
|
||||
Local<String> specifier,
|
||||
Local<FixedArray> import_assertions);
|
||||
|
||||
/**
|
||||
* HostInitializeImportMetaObjectCallback is called the first time import.meta
|
||||
@ -8777,9 +8811,19 @@ class V8_EXPORT Isolate {
|
||||
* This specifies the callback called by the upcoming dynamic
|
||||
* import() language feature to load modules.
|
||||
*/
|
||||
V8_DEPRECATE_SOON(
|
||||
"Use the version of SetHostImportModuleDynamicallyCallback that takes a "
|
||||
"HostImportModuleDynamicallyWithImportAssertionsCallback instead")
|
||||
void SetHostImportModuleDynamicallyCallback(
|
||||
HostImportModuleDynamicallyCallback callback);
|
||||
|
||||
/**
|
||||
* This specifies the callback called by the upcoming dynamic
|
||||
* import() language feature to load modules.
|
||||
*/
|
||||
void SetHostImportModuleDynamicallyCallback(
|
||||
HostImportModuleDynamicallyWithImportAssertionsCallback callback);
|
||||
|
||||
/**
|
||||
* This specifies the callback called by the upcoming import.meta
|
||||
* language feature to retrieve host-defined meta data for a module.
|
||||
|
@ -8653,7 +8653,13 @@ void Isolate::SetAbortOnUncaughtExceptionCallback(
|
||||
}
|
||||
|
||||
void Isolate::SetHostImportModuleDynamicallyCallback(
|
||||
HostImportModuleDynamicallyCallback callback) {
|
||||
i::Isolate::DeprecatedHostImportModuleDynamicallyCallback callback) {
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
|
||||
isolate->SetHostImportModuleDynamicallyCallback(callback);
|
||||
}
|
||||
|
||||
void Isolate::SetHostImportModuleDynamicallyCallback(
|
||||
HostImportModuleDynamicallyWithImportAssertionsCallback callback) {
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
|
||||
isolate->SetHostImportModuleDynamicallyCallback(callback);
|
||||
}
|
||||
|
@ -125,10 +125,14 @@ namespace internal {
|
||||
T(NonCoercibleWithProperty, \
|
||||
"Cannot destructure property '%' of '%' as it is %.") \
|
||||
T(NonExtensibleProto, "% is not extensible") \
|
||||
T(NonObjectAssertOption, "The 'assert' option must be an object") \
|
||||
T(NonObjectInInstanceOfCheck, \
|
||||
"Right-hand side of 'instanceof' is not an object") \
|
||||
T(NonObjectPropertyLoad, "Cannot read property '%' of %") \
|
||||
T(NonObjectPropertyStore, "Cannot set property '%' of %") \
|
||||
T(NonObjectImportArgument, \
|
||||
"The second argument to import() must be an object") \
|
||||
T(NonStringImportAssertionValue, "Import assertion value must be a string") \
|
||||
T(NoSetterInCallback, "Cannot set property % of % which has only a getter") \
|
||||
T(NotAnIterator, "% is not an iterator") \
|
||||
T(NotAPromise, "% is not a promise") \
|
||||
|
174
src/d8/d8.cc
174
src/d8/d8.cc
@ -839,13 +839,46 @@ class ModuleEmbedderData {
|
||||
|
||||
public:
|
||||
explicit ModuleEmbedderData(Isolate* isolate)
|
||||
: module_to_specifier_map(10, ModuleGlobalHash(isolate)) {}
|
||||
: module_to_specifier_map(10, ModuleGlobalHash(isolate)),
|
||||
json_module_to_parsed_json_map(10, ModuleGlobalHash(isolate)) {}
|
||||
|
||||
// Map from normalized module specifier to Module.
|
||||
std::unordered_map<std::string, Global<Module>> specifier_to_module_map;
|
||||
static ModuleType ModuleTypeFromImportAssertions(
|
||||
Local<Context> context, Local<FixedArray> import_assertions,
|
||||
bool hasPositions) {
|
||||
Isolate* isolate = context->GetIsolate();
|
||||
const int kV8AssertionEntrySize = hasPositions ? 3 : 2;
|
||||
for (int i = 0; i < import_assertions->Length();
|
||||
i += kV8AssertionEntrySize) {
|
||||
Local<String> v8_assertion_key =
|
||||
import_assertions->Get(context, i).As<v8::String>();
|
||||
std::string assertion_key = ToSTLString(isolate, v8_assertion_key);
|
||||
|
||||
if (assertion_key == "type") {
|
||||
Local<String> v8_assertion_value =
|
||||
import_assertions->Get(context, i + 1).As<String>();
|
||||
std::string assertion_value = ToSTLString(isolate, v8_assertion_value);
|
||||
if (assertion_value == "json") {
|
||||
return ModuleType::kJSON;
|
||||
} else {
|
||||
// JSON is currently the only supported non-JS type
|
||||
return ModuleType::kInvalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no type is asserted, default to JS.
|
||||
return ModuleType::kJavaScript;
|
||||
}
|
||||
|
||||
// Map from (normalized module specifier, module type) pair to Module.
|
||||
std::map<std::pair<std::string, ModuleType>, Global<Module>> module_map;
|
||||
// Map from Module to its URL as defined in the ScriptOrigin
|
||||
std::unordered_map<Global<Module>, std::string, ModuleGlobalHash>
|
||||
module_to_specifier_map;
|
||||
// Map from JSON Module to its parsed content, for use in module
|
||||
// JSONModuleEvaluationSteps
|
||||
std::unordered_map<Global<Module>, Global<Value>, ModuleGlobalHash>
|
||||
json_module_to_parsed_json_map;
|
||||
};
|
||||
|
||||
enum { kModuleEmbedderDataIndex, kInspectorClientIndex };
|
||||
@ -869,7 +902,6 @@ MaybeLocal<Module> ResolveModuleCallback(Local<Context> context,
|
||||
Local<String> specifier,
|
||||
Local<FixedArray> import_assertions,
|
||||
Local<Module> referrer) {
|
||||
// TODO(v8:11189) Consider JSON modules support in d8.
|
||||
Isolate* isolate = context->GetIsolate();
|
||||
ModuleEmbedderData* d = GetModuleDataFromContext(context);
|
||||
auto specifier_it =
|
||||
@ -877,8 +909,11 @@ MaybeLocal<Module> ResolveModuleCallback(Local<Context> context,
|
||||
CHECK(specifier_it != d->module_to_specifier_map.end());
|
||||
std::string absolute_path = NormalizePath(ToSTLString(isolate, specifier),
|
||||
DirName(specifier_it->second));
|
||||
auto module_it = d->specifier_to_module_map.find(absolute_path);
|
||||
CHECK(module_it != d->specifier_to_module_map.end());
|
||||
ModuleType module_type = ModuleEmbedderData::ModuleTypeFromImportAssertions(
|
||||
context, import_assertions, true);
|
||||
auto module_it =
|
||||
d->module_map.find(std::make_pair(absolute_path, module_type));
|
||||
CHECK(module_it != d->module_map.end());
|
||||
return module_it->second.Get(isolate);
|
||||
}
|
||||
|
||||
@ -886,7 +921,8 @@ MaybeLocal<Module> ResolveModuleCallback(Local<Context> context,
|
||||
|
||||
MaybeLocal<Module> Shell::FetchModuleTree(Local<Module> referrer,
|
||||
Local<Context> context,
|
||||
const std::string& file_name) {
|
||||
const std::string& file_name,
|
||||
ModuleType module_type) {
|
||||
DCHECK(IsAbsolutePath(file_name));
|
||||
Isolate* isolate = context->GetIsolate();
|
||||
Local<String> source_text = ReadFile(isolate, file_name.c_str());
|
||||
@ -914,16 +950,39 @@ MaybeLocal<Module> Shell::FetchModuleTree(Local<Module> referrer,
|
||||
ScriptOrigin origin(
|
||||
isolate, String::NewFromUtf8(isolate, file_name.c_str()).ToLocalChecked(),
|
||||
0, 0, false, -1, Local<Value>(), false, false, true);
|
||||
ScriptCompiler::Source source(source_text, origin);
|
||||
|
||||
Local<Module> module;
|
||||
if (!CompileString<Module>(isolate, context, source_text, origin)
|
||||
.ToLocal(&module)) {
|
||||
return MaybeLocal<Module>();
|
||||
if (module_type == ModuleType::kJavaScript) {
|
||||
ScriptCompiler::Source source(source_text, origin);
|
||||
if (!CompileString<Module>(isolate, context, source_text, origin)
|
||||
.ToLocal(&module)) {
|
||||
return MaybeLocal<Module>();
|
||||
}
|
||||
} else if (module_type == ModuleType::kJSON) {
|
||||
Local<Value> parsed_json;
|
||||
if (!v8::JSON::Parse(context, source_text).ToLocal(&parsed_json)) {
|
||||
return MaybeLocal<Module>();
|
||||
}
|
||||
|
||||
std::vector<Local<String>> export_names{
|
||||
String::NewFromUtf8(isolate, "default").ToLocalChecked()};
|
||||
|
||||
module = v8::Module::CreateSyntheticModule(
|
||||
isolate,
|
||||
String::NewFromUtf8(isolate, file_name.c_str()).ToLocalChecked(),
|
||||
export_names, Shell::JSONModuleEvaluationSteps);
|
||||
|
||||
CHECK(d->json_module_to_parsed_json_map
|
||||
.insert(std::make_pair(Global<Module>(isolate, module),
|
||||
Global<Value>(isolate, parsed_json)))
|
||||
.second);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
CHECK(d->specifier_to_module_map
|
||||
.insert(std::make_pair(file_name, Global<Module>(isolate, module)))
|
||||
CHECK(d->module_map
|
||||
.insert(std::make_pair(std::make_pair(file_name, module_type),
|
||||
Global<Module>(isolate, module)))
|
||||
.second);
|
||||
CHECK(d->module_to_specifier_map
|
||||
.insert(std::make_pair(Global<Module>(isolate, module), file_name))
|
||||
@ -938,8 +997,23 @@ MaybeLocal<Module> Shell::FetchModuleTree(Local<Module> referrer,
|
||||
Local<String> name = module_request->GetSpecifier();
|
||||
std::string absolute_path =
|
||||
NormalizePath(ToSTLString(isolate, name), dir_name);
|
||||
if (d->specifier_to_module_map.count(absolute_path)) continue;
|
||||
if (FetchModuleTree(module, context, absolute_path).IsEmpty()) {
|
||||
Local<FixedArray> import_assertions = module_request->GetImportAssertions();
|
||||
ModuleType request_module_type =
|
||||
ModuleEmbedderData::ModuleTypeFromImportAssertions(
|
||||
context, import_assertions, true);
|
||||
|
||||
if (request_module_type == ModuleType::kInvalid) {
|
||||
Throw(isolate, "Invalid module type was asserted");
|
||||
return MaybeLocal<Module>();
|
||||
}
|
||||
|
||||
if (d->module_map.count(
|
||||
std::make_pair(absolute_path, request_module_type))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FetchModuleTree(module, context, absolute_path, request_module_type)
|
||||
.IsEmpty()) {
|
||||
return MaybeLocal<Module>();
|
||||
}
|
||||
}
|
||||
@ -947,19 +1021,53 @@ MaybeLocal<Module> Shell::FetchModuleTree(Local<Module> referrer,
|
||||
return module;
|
||||
}
|
||||
|
||||
MaybeLocal<Value> Shell::JSONModuleEvaluationSteps(Local<Context> context,
|
||||
Local<Module> module) {
|
||||
Isolate* isolate = context->GetIsolate();
|
||||
|
||||
ModuleEmbedderData* d = GetModuleDataFromContext(context);
|
||||
auto json_value_it =
|
||||
d->json_module_to_parsed_json_map.find(Global<Module>(isolate, module));
|
||||
CHECK(json_value_it != d->json_module_to_parsed_json_map.end());
|
||||
Local<Value> json_value = json_value_it->second.Get(isolate);
|
||||
|
||||
TryCatch try_catch(isolate);
|
||||
Maybe<bool> result = module->SetSyntheticModuleExport(
|
||||
isolate,
|
||||
String::NewFromUtf8Literal(isolate, "default",
|
||||
NewStringType::kInternalized),
|
||||
json_value);
|
||||
|
||||
// Setting the default export should never fail.
|
||||
CHECK(!try_catch.HasCaught());
|
||||
CHECK(!result.IsNothing() && result.FromJust());
|
||||
|
||||
if (i::FLAG_harmony_top_level_await) {
|
||||
Local<Promise::Resolver> resolver =
|
||||
Promise::Resolver::New(context).ToLocalChecked();
|
||||
resolver->Resolve(context, Undefined(isolate)).ToChecked();
|
||||
return resolver->GetPromise();
|
||||
}
|
||||
|
||||
return Undefined(isolate);
|
||||
}
|
||||
|
||||
struct DynamicImportData {
|
||||
DynamicImportData(Isolate* isolate_, Local<String> referrer_,
|
||||
Local<String> specifier_,
|
||||
Local<FixedArray> import_assertions_,
|
||||
Local<Promise::Resolver> resolver_)
|
||||
: isolate(isolate_) {
|
||||
referrer.Reset(isolate, referrer_);
|
||||
specifier.Reset(isolate, specifier_);
|
||||
import_assertions.Reset(isolate, import_assertions_);
|
||||
resolver.Reset(isolate, resolver_);
|
||||
}
|
||||
|
||||
Isolate* isolate;
|
||||
Global<String> referrer;
|
||||
Global<String> specifier;
|
||||
Global<FixedArray> import_assertions;
|
||||
Global<Promise::Resolver> resolver;
|
||||
};
|
||||
|
||||
@ -1020,15 +1128,16 @@ void Shell::ModuleResolutionFailureCallback(
|
||||
|
||||
MaybeLocal<Promise> Shell::HostImportModuleDynamically(
|
||||
Local<Context> context, Local<ScriptOrModule> referrer,
|
||||
Local<String> specifier) {
|
||||
Local<String> specifier, Local<FixedArray> import_assertions) {
|
||||
Isolate* isolate = context->GetIsolate();
|
||||
|
||||
MaybeLocal<Promise::Resolver> maybe_resolver =
|
||||
Promise::Resolver::New(context);
|
||||
Local<Promise::Resolver> resolver;
|
||||
if (maybe_resolver.ToLocal(&resolver)) {
|
||||
DynamicImportData* data = new DynamicImportData(
|
||||
isolate, referrer->GetResourceName().As<String>(), specifier, resolver);
|
||||
DynamicImportData* data =
|
||||
new DynamicImportData(isolate, referrer->GetResourceName().As<String>(),
|
||||
specifier, import_assertions, resolver);
|
||||
PerIsolateData::Get(isolate)->AddDynamicImportData(data);
|
||||
isolate->EnqueueMicrotask(Shell::DoHostImportModuleDynamically, data);
|
||||
return resolver->GetPromise();
|
||||
@ -1064,6 +1173,8 @@ void Shell::DoHostImportModuleDynamically(void* import_data) {
|
||||
|
||||
Local<String> referrer(import_data_->referrer.Get(isolate));
|
||||
Local<String> specifier(import_data_->specifier.Get(isolate));
|
||||
Local<FixedArray> import_assertions(
|
||||
import_data_->import_assertions.Get(isolate));
|
||||
Local<Promise::Resolver> resolver(import_data_->resolver.Get(isolate));
|
||||
|
||||
PerIsolateData* data = PerIsolateData::Get(isolate);
|
||||
@ -1072,21 +1183,33 @@ void Shell::DoHostImportModuleDynamically(void* import_data) {
|
||||
Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
|
||||
Context::Scope context_scope(realm);
|
||||
|
||||
ModuleType module_type = ModuleEmbedderData::ModuleTypeFromImportAssertions(
|
||||
realm, import_assertions, false);
|
||||
|
||||
TryCatch try_catch(isolate);
|
||||
try_catch.SetVerbose(true);
|
||||
|
||||
if (module_type == ModuleType::kInvalid) {
|
||||
Throw(isolate, "Invalid module type was asserted");
|
||||
CHECK(try_catch.HasCaught());
|
||||
resolver->Reject(realm, try_catch.Exception()).ToChecked();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string source_url = ToSTLString(isolate, referrer);
|
||||
std::string dir_name =
|
||||
DirName(NormalizePath(source_url, GetWorkingDirectory()));
|
||||
std::string file_name = ToSTLString(isolate, specifier);
|
||||
std::string absolute_path = NormalizePath(file_name, dir_name);
|
||||
|
||||
TryCatch try_catch(isolate);
|
||||
try_catch.SetVerbose(true);
|
||||
|
||||
ModuleEmbedderData* d = GetModuleDataFromContext(realm);
|
||||
Local<Module> root_module;
|
||||
auto module_it = d->specifier_to_module_map.find(absolute_path);
|
||||
if (module_it != d->specifier_to_module_map.end()) {
|
||||
auto module_it =
|
||||
d->module_map.find(std::make_pair(absolute_path, module_type));
|
||||
if (module_it != d->module_map.end()) {
|
||||
root_module = module_it->second.Get(isolate);
|
||||
} else if (!FetchModuleTree(Local<Module>(), realm, absolute_path)
|
||||
} else if (!FetchModuleTree(Local<Module>(), realm, absolute_path,
|
||||
module_type)
|
||||
.ToLocal(&root_module)) {
|
||||
CHECK(try_catch.HasCaught());
|
||||
resolver->Reject(realm, try_catch.Exception()).ToChecked();
|
||||
@ -1154,7 +1277,8 @@ bool Shell::ExecuteModule(Isolate* isolate, const char* file_name) {
|
||||
|
||||
Local<Module> root_module;
|
||||
|
||||
if (!FetchModuleTree(Local<Module>(), realm, absolute_path)
|
||||
if (!FetchModuleTree(Local<Module>(), realm, absolute_path,
|
||||
ModuleType::kJavaScript)
|
||||
.ToLocal(&root_module)) {
|
||||
CHECK(try_catch.HasCaught());
|
||||
ReportException(isolate, &try_catch);
|
||||
|
10
src/d8/d8.h
10
src/d8/d8.h
@ -26,6 +26,8 @@ namespace v8 {
|
||||
|
||||
class D8Console;
|
||||
|
||||
enum class ModuleType { kJavaScript, kJSON, kInvalid };
|
||||
|
||||
namespace internal {
|
||||
class CancelableTaskManager;
|
||||
} // namespace internal
|
||||
@ -534,7 +536,7 @@ class Shell : public i::AllStatic {
|
||||
static void RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static MaybeLocal<Promise> HostImportModuleDynamically(
|
||||
Local<Context> context, Local<ScriptOrModule> referrer,
|
||||
Local<String> specifier);
|
||||
Local<String> specifier, Local<FixedArray> import_assertions);
|
||||
static void ModuleResolutionSuccessCallback(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
static void ModuleResolutionFailureCallback(
|
||||
@ -630,7 +632,11 @@ class Shell : public i::AllStatic {
|
||||
int index);
|
||||
static MaybeLocal<Module> FetchModuleTree(v8::Local<v8::Module> origin_module,
|
||||
v8::Local<v8::Context> context,
|
||||
const std::string& file_name);
|
||||
const std::string& file_name,
|
||||
ModuleType module_type);
|
||||
|
||||
static MaybeLocal<Value> JSONModuleEvaluationSteps(Local<Context> context,
|
||||
Local<Module> module);
|
||||
|
||||
template <class T>
|
||||
static MaybeLocal<T> CompileString(Isolate* isolate, Local<Context> context,
|
||||
|
@ -4153,11 +4153,17 @@ MaybeHandle<JSPromise> NewRejectedPromise(Isolate* isolate,
|
||||
} // namespace
|
||||
|
||||
MaybeHandle<JSPromise> Isolate::RunHostImportModuleDynamicallyCallback(
|
||||
Handle<Script> referrer, Handle<Object> specifier) {
|
||||
Handle<Script> referrer, Handle<Object> specifier,
|
||||
MaybeHandle<Object> maybe_import_assertions_argument) {
|
||||
v8::Local<v8::Context> api_context =
|
||||
v8::Utils::ToLocal(Handle<Context>(native_context()));
|
||||
DCHECK_EQ(host_import_module_dynamically_callback_ != nullptr,
|
||||
host_import_module_dynamically_with_import_assertions_callback_ ==
|
||||
nullptr);
|
||||
|
||||
if (host_import_module_dynamically_callback_ == nullptr) {
|
||||
if (host_import_module_dynamically_callback_ == nullptr &&
|
||||
host_import_module_dynamically_with_import_assertions_callback_ ==
|
||||
nullptr) {
|
||||
Handle<Object> exception =
|
||||
factory()->NewError(error_function(), MessageTemplate::kUnsupported);
|
||||
return NewRejectedPromise(this, api_context, exception);
|
||||
@ -4174,22 +4180,150 @@ MaybeHandle<JSPromise> Isolate::RunHostImportModuleDynamicallyCallback(
|
||||
DCHECK(!has_pending_exception());
|
||||
|
||||
v8::Local<v8::Promise> promise;
|
||||
ASSIGN_RETURN_ON_SCHEDULED_EXCEPTION_VALUE(
|
||||
this, promise,
|
||||
host_import_module_dynamically_callback_(
|
||||
api_context, v8::Utils::ScriptOrModuleToLocal(referrer),
|
||||
v8::Utils::ToLocal(specifier_str)),
|
||||
MaybeHandle<JSPromise>());
|
||||
return v8::Utils::OpenHandle(*promise);
|
||||
|
||||
if (host_import_module_dynamically_with_import_assertions_callback_) {
|
||||
Handle<FixedArray> import_assertions_array;
|
||||
if (GetImportAssertionsFromArgument(maybe_import_assertions_argument)
|
||||
.ToHandle(&import_assertions_array)) {
|
||||
ASSIGN_RETURN_ON_SCHEDULED_EXCEPTION_VALUE(
|
||||
this, promise,
|
||||
host_import_module_dynamically_with_import_assertions_callback_(
|
||||
api_context, v8::Utils::ScriptOrModuleToLocal(referrer),
|
||||
v8::Utils::ToLocal(specifier_str),
|
||||
ToApiHandle<v8::FixedArray>(import_assertions_array)),
|
||||
MaybeHandle<JSPromise>());
|
||||
return v8::Utils::OpenHandle(*promise);
|
||||
} else {
|
||||
Handle<Object> exception(pending_exception(), this);
|
||||
clear_pending_exception();
|
||||
|
||||
return NewRejectedPromise(this, api_context, exception);
|
||||
}
|
||||
|
||||
} else {
|
||||
DCHECK_NOT_NULL(host_import_module_dynamically_callback_);
|
||||
ASSIGN_RETURN_ON_SCHEDULED_EXCEPTION_VALUE(
|
||||
this, promise,
|
||||
host_import_module_dynamically_callback_(
|
||||
api_context, v8::Utils::ScriptOrModuleToLocal(referrer),
|
||||
v8::Utils::ToLocal(specifier_str)),
|
||||
MaybeHandle<JSPromise>());
|
||||
return v8::Utils::OpenHandle(*promise);
|
||||
}
|
||||
}
|
||||
|
||||
MaybeHandle<FixedArray> Isolate::GetImportAssertionsFromArgument(
|
||||
MaybeHandle<Object> maybe_import_assertions_argument) {
|
||||
Handle<FixedArray> import_assertions_array = factory()->empty_fixed_array();
|
||||
Handle<Object> import_assertions_argument;
|
||||
if (!maybe_import_assertions_argument.ToHandle(&import_assertions_argument) ||
|
||||
import_assertions_argument->IsUndefined()) {
|
||||
return import_assertions_array;
|
||||
}
|
||||
|
||||
// The parser shouldn't have allowed the second argument to import() if
|
||||
// the flag wasn't enabled.
|
||||
DCHECK(FLAG_harmony_import_assertions);
|
||||
|
||||
if (!import_assertions_argument->IsJSReceiver()) {
|
||||
this->Throw(
|
||||
*factory()->NewTypeError(MessageTemplate::kNonObjectImportArgument));
|
||||
return MaybeHandle<FixedArray>();
|
||||
}
|
||||
|
||||
Handle<JSReceiver> import_assertions_argument_receiver =
|
||||
Handle<JSReceiver>::cast(import_assertions_argument);
|
||||
Handle<Name> key = factory()->assert_string();
|
||||
|
||||
Handle<Object> import_assertions_object;
|
||||
if (!JSReceiver::GetProperty(this, import_assertions_argument_receiver, key)
|
||||
.ToHandle(&import_assertions_object)) {
|
||||
// This can happen if the property has a getter function that throws
|
||||
// an error.
|
||||
return MaybeHandle<FixedArray>();
|
||||
}
|
||||
|
||||
// If there is no 'assert' option in the options bag, it's not an error. Just
|
||||
// do the import() as if no assertions were provided.
|
||||
if (import_assertions_object->IsUndefined()) return import_assertions_array;
|
||||
|
||||
if (!import_assertions_object->IsJSReceiver()) {
|
||||
this->Throw(
|
||||
*factory()->NewTypeError(MessageTemplate::kNonObjectAssertOption));
|
||||
return MaybeHandle<FixedArray>();
|
||||
}
|
||||
|
||||
Handle<JSReceiver> import_assertions_object_receiver =
|
||||
Handle<JSReceiver>::cast(import_assertions_object);
|
||||
|
||||
Handle<FixedArray> assertion_keys =
|
||||
KeyAccumulator::GetKeys(import_assertions_object_receiver,
|
||||
KeyCollectionMode::kOwnOnly, ENUMERABLE_STRINGS,
|
||||
GetKeysConversion::kConvertToString)
|
||||
.ToHandleChecked();
|
||||
|
||||
// Assertions passed to the host must be sorted by the code point order of the
|
||||
// key of each entry.
|
||||
auto stringCompare = [this](Handle<String> lhs, Handle<String> rhs) {
|
||||
return String::Compare(this, lhs, rhs) == ComparisonResult::kLessThan;
|
||||
};
|
||||
std::map<Handle<String>, Handle<String>, decltype(stringCompare)>
|
||||
sorted_assertions(stringCompare);
|
||||
|
||||
// Collect the assertions in the sorted map.
|
||||
for (int i = 0; i < assertion_keys->length(); i++) {
|
||||
Handle<String> assertion_key(String::cast(assertion_keys->get(i)), this);
|
||||
Handle<Object> assertion_value;
|
||||
if (!JSReceiver::GetProperty(this, import_assertions_object_receiver,
|
||||
assertion_key)
|
||||
.ToHandle(&assertion_value)) {
|
||||
// This can happen if the property has a getter function that throws
|
||||
// an error.
|
||||
return MaybeHandle<FixedArray>();
|
||||
}
|
||||
|
||||
if (!assertion_value->IsString()) {
|
||||
this->Throw(*factory()->NewTypeError(
|
||||
MessageTemplate::kNonStringImportAssertionValue));
|
||||
return MaybeHandle<FixedArray>();
|
||||
}
|
||||
|
||||
auto insertion_result = sorted_assertions.insert(
|
||||
std::make_pair(assertion_key, Handle<String>::cast(assertion_value)));
|
||||
|
||||
// Duplicate keys are not expected here.
|
||||
CHECK(insertion_result.second);
|
||||
}
|
||||
|
||||
// Move the assertions from the sorted map to the FixedArray that will be
|
||||
// passed to the host. They will be stored in the array in the form: [key1,
|
||||
// value1, key2, value2, ...].
|
||||
constexpr size_t kAssertionEntrySizeForDynamicImport = 2;
|
||||
import_assertions_array = factory()->NewFixedArray(static_cast<int>(
|
||||
sorted_assertions.size() * kAssertionEntrySizeForDynamicImport));
|
||||
int import_assertions_array_index = 0;
|
||||
for (const auto& pair : sorted_assertions) {
|
||||
import_assertions_array->set(import_assertions_array_index, *(pair.first));
|
||||
import_assertions_array->set(import_assertions_array_index + 1,
|
||||
*(pair.second));
|
||||
import_assertions_array_index += kAssertionEntrySizeForDynamicImport;
|
||||
}
|
||||
|
||||
return import_assertions_array;
|
||||
}
|
||||
|
||||
void Isolate::ClearKeptObjects() { heap()->ClearKeptObjects(); }
|
||||
|
||||
void Isolate::SetHostImportModuleDynamicallyCallback(
|
||||
HostImportModuleDynamicallyCallback callback) {
|
||||
DeprecatedHostImportModuleDynamicallyCallback callback) {
|
||||
host_import_module_dynamically_callback_ = callback;
|
||||
}
|
||||
|
||||
void Isolate::SetHostImportModuleDynamicallyCallback(
|
||||
HostImportModuleDynamicallyWithImportAssertionsCallback callback) {
|
||||
host_import_module_dynamically_with_import_assertions_callback_ = callback;
|
||||
}
|
||||
|
||||
MaybeHandle<JSObject> Isolate::RunHostInitializeImportMetaObjectCallback(
|
||||
Handle<SourceTextModule> module) {
|
||||
CHECK(module->import_meta().IsTheHole(this));
|
||||
|
@ -1523,10 +1523,23 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
|
||||
|
||||
void ClearKeptObjects();
|
||||
|
||||
// While deprecating v8::HostImportModuleDynamicallyCallback in v8.h we still
|
||||
// need to support the version of the API that uses it, but we can't directly
|
||||
// reference the deprecated version because of the enusing build warnings. So,
|
||||
// we declare this matching type for temporary internal use.
|
||||
// TODO(v8:10958) Delete this declaration and all references to it once
|
||||
// v8::HostImportModuleDynamicallyCallback is removed.
|
||||
typedef MaybeLocal<Promise> (*DeprecatedHostImportModuleDynamicallyCallback)(
|
||||
v8::Local<v8::Context> context, v8::Local<v8::ScriptOrModule> referrer,
|
||||
v8::Local<v8::String> specifier);
|
||||
|
||||
void SetHostImportModuleDynamicallyCallback(
|
||||
HostImportModuleDynamicallyCallback callback);
|
||||
DeprecatedHostImportModuleDynamicallyCallback callback);
|
||||
void SetHostImportModuleDynamicallyCallback(
|
||||
HostImportModuleDynamicallyWithImportAssertionsCallback callback);
|
||||
MaybeHandle<JSPromise> RunHostImportModuleDynamicallyCallback(
|
||||
Handle<Script> referrer, Handle<Object> specifier);
|
||||
Handle<Script> referrer, Handle<Object> specifier,
|
||||
MaybeHandle<Object> maybe_import_assertions_argument);
|
||||
|
||||
void SetHostInitializeImportMetaObjectCallback(
|
||||
HostInitializeImportMetaObjectCallback callback);
|
||||
@ -1823,8 +1836,21 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
|
||||
v8::Isolate::AtomicsWaitCallback atomics_wait_callback_ = nullptr;
|
||||
void* atomics_wait_callback_data_ = nullptr;
|
||||
PromiseHook promise_hook_ = nullptr;
|
||||
HostImportModuleDynamicallyCallback host_import_module_dynamically_callback_ =
|
||||
nullptr;
|
||||
DeprecatedHostImportModuleDynamicallyCallback
|
||||
host_import_module_dynamically_callback_ = nullptr;
|
||||
HostImportModuleDynamicallyWithImportAssertionsCallback
|
||||
host_import_module_dynamically_with_import_assertions_callback_ = nullptr;
|
||||
|
||||
// Helper function for RunHostImportModuleDynamicallyCallback.
|
||||
// Unpacks import assertions, if present, from the second argument to dynamic
|
||||
// import() and returns them in a FixedArray, sorted by code point order of
|
||||
// the keys, in the form [key1, value1, key2, value2, ...]. Returns an empty
|
||||
// MaybeHandle if an error was thrown. In this case, the host callback should
|
||||
// not be called and instead the caller should use the pending exception to
|
||||
// reject the import() call's Promise.
|
||||
MaybeHandle<FixedArray> GetImportAssertionsFromArgument(
|
||||
MaybeHandle<Object> maybe_import_assertions_argument);
|
||||
|
||||
HostInitializeImportMetaObjectCallback
|
||||
host_initialize_import_meta_object_callback_ = nullptr;
|
||||
base::Mutex rail_mutex_;
|
||||
|
@ -5705,8 +5705,12 @@ void BytecodeGenerator::VisitEmptyParentheses(EmptyParentheses* expr) {
|
||||
}
|
||||
|
||||
void BytecodeGenerator::VisitImportCallExpression(ImportCallExpression* expr) {
|
||||
RegisterList args = register_allocator()->NewRegisterList(2);
|
||||
const int register_count = expr->import_assertions() ? 3 : 2;
|
||||
RegisterList args = register_allocator()->NewRegisterList(register_count);
|
||||
VisitForRegisterValue(expr->specifier(), args[1]);
|
||||
if (expr->import_assertions()) {
|
||||
VisitForRegisterValue(expr->import_assertions(), args[2]);
|
||||
}
|
||||
builder()
|
||||
->MoveRegister(Register::function_closure(), args[0])
|
||||
.CallRuntime(Runtime::kDynamicImportCall, args);
|
||||
|
@ -14,19 +14,26 @@ namespace internal {
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DynamicImportCall) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(2, args.length());
|
||||
DCHECK_LE(2, args.length());
|
||||
DCHECK_GE(3, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, specifier, 1);
|
||||
|
||||
MaybeHandle<Object> import_assertions;
|
||||
if (args.length() == 3) {
|
||||
CHECK(args[2].IsObject());
|
||||
import_assertions = args.at<Object>(2);
|
||||
}
|
||||
|
||||
Handle<Script> script(Script::cast(function->shared().script()), isolate);
|
||||
|
||||
while (script->has_eval_from_shared()) {
|
||||
script = handle(Script::cast(script->eval_from_shared().script()), isolate);
|
||||
}
|
||||
|
||||
RETURN_RESULT_OR_FAILURE(
|
||||
isolate,
|
||||
isolate->RunHostImportModuleDynamicallyCallback(script, specifier));
|
||||
RETURN_RESULT_OR_FAILURE(isolate,
|
||||
isolate->RunHostImportModuleDynamicallyCallback(
|
||||
script, specifier, import_assertions));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_GetModuleNamespace) {
|
||||
|
@ -265,9 +265,9 @@ namespace internal {
|
||||
F(CreateObjectLiteralWithoutAllocationSite, 2, 1) \
|
||||
F(CreateRegExpLiteral, 4, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_MODULE(F, I) \
|
||||
F(DynamicImportCall, 2, 1) \
|
||||
I(GetImportMetaObject, 0, 1) \
|
||||
#define FOR_EACH_INTRINSIC_MODULE(F, I) \
|
||||
F(DynamicImportCall, -1 /* [2, 3] */, 1) \
|
||||
I(GetImportMetaObject, 0, 1) \
|
||||
F(GetModuleNamespace, 1, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_NUMBERS(F, I) \
|
||||
|
@ -83,7 +83,7 @@ bytecodes: [
|
||||
B(Mov), R(this), R(0),
|
||||
B(Mov), R(context), R(2),
|
||||
/* 48 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
|
||||
/* 53 S> */ B(Wide), B(LdaSmi), I16(272),
|
||||
/* 53 S> */ B(Wide), B(LdaSmi), I16(275),
|
||||
B(Star), R(3),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(4),
|
||||
@ -114,7 +114,7 @@ bytecodes: [
|
||||
B(Mov), R(this), R(0),
|
||||
B(Mov), R(context), R(2),
|
||||
/* 41 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
|
||||
/* 46 S> */ B(Wide), B(LdaSmi), I16(271),
|
||||
/* 46 S> */ B(Wide), B(LdaSmi), I16(274),
|
||||
B(Star), R(3),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(4),
|
||||
@ -145,7 +145,7 @@ bytecodes: [
|
||||
B(Mov), R(this), R(0),
|
||||
B(Mov), R(context), R(2),
|
||||
/* 48 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
|
||||
/* 53 S> */ B(Wide), B(LdaSmi), I16(272),
|
||||
/* 53 S> */ B(Wide), B(LdaSmi), I16(275),
|
||||
B(Star), R(3),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(4),
|
||||
@ -176,7 +176,7 @@ bytecodes: [
|
||||
B(Mov), R(this), R(0),
|
||||
B(Mov), R(context), R(2),
|
||||
/* 41 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
|
||||
/* 46 S> */ B(Wide), B(LdaSmi), I16(271),
|
||||
/* 46 S> */ B(Wide), B(LdaSmi), I16(274),
|
||||
B(Star), R(4),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(5),
|
||||
|
@ -56,7 +56,7 @@ bytecodes: [
|
||||
B(Mov), R(this), R(0),
|
||||
B(Mov), R(context), R(2),
|
||||
/* 44 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
|
||||
/* 49 S> */ B(Wide), B(LdaSmi), I16(270),
|
||||
/* 49 S> */ B(Wide), B(LdaSmi), I16(273),
|
||||
B(Star), R(3),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(4),
|
||||
@ -88,7 +88,7 @@ bytecodes: [
|
||||
B(Mov), R(this), R(0),
|
||||
B(Mov), R(context), R(2),
|
||||
/* 44 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
|
||||
/* 49 S> */ B(Wide), B(LdaSmi), I16(270),
|
||||
/* 49 S> */ B(Wide), B(LdaSmi), I16(273),
|
||||
B(Star), R(3),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(4),
|
||||
|
@ -24,7 +24,7 @@ bytecodes: [
|
||||
B(TestReferenceEqual), R(this),
|
||||
B(Mov), R(this), R(1),
|
||||
B(JumpIfTrue), U8(18),
|
||||
B(Wide), B(LdaSmi), I16(268),
|
||||
B(Wide), B(LdaSmi), I16(271),
|
||||
B(Star), R(2),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(3),
|
||||
@ -55,7 +55,7 @@ frame size: 2
|
||||
parameter count: 1
|
||||
bytecode array length: 16
|
||||
bytecodes: [
|
||||
/* 56 S> */ B(Wide), B(LdaSmi), I16(270),
|
||||
/* 56 S> */ B(Wide), B(LdaSmi), I16(273),
|
||||
B(Star), R(0),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(1),
|
||||
@ -82,7 +82,7 @@ frame size: 2
|
||||
parameter count: 1
|
||||
bytecode array length: 16
|
||||
bytecodes: [
|
||||
/* 56 S> */ B(Wide), B(LdaSmi), I16(270),
|
||||
/* 56 S> */ B(Wide), B(LdaSmi), I16(273),
|
||||
B(Star), R(0),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(1),
|
||||
@ -121,7 +121,7 @@ bytecodes: [
|
||||
/* 94 E> */ B(TestReferenceEqual), R(this),
|
||||
B(Mov), R(this), R(0),
|
||||
B(JumpIfTrue), U8(18),
|
||||
B(Wide), B(LdaSmi), I16(268),
|
||||
B(Wide), B(LdaSmi), I16(271),
|
||||
B(Star), R(2),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(3),
|
||||
@ -143,7 +143,7 @@ bytecodes: [
|
||||
/* 109 E> */ B(TestReferenceEqual), R(this),
|
||||
B(Mov), R(this), R(1),
|
||||
B(JumpIfTrue), U8(18),
|
||||
B(Wide), B(LdaSmi), I16(269),
|
||||
B(Wide), B(LdaSmi), I16(272),
|
||||
B(Star), R(3),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(4),
|
||||
@ -158,7 +158,7 @@ bytecodes: [
|
||||
/* 133 E> */ B(TestReferenceEqual), R(this),
|
||||
B(Mov), R(this), R(0),
|
||||
B(JumpIfTrue), U8(18),
|
||||
B(Wide), B(LdaSmi), I16(268),
|
||||
B(Wide), B(LdaSmi), I16(271),
|
||||
B(Star), R(2),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(3),
|
||||
@ -188,7 +188,7 @@ frame size: 2
|
||||
parameter count: 1
|
||||
bytecode array length: 16
|
||||
bytecodes: [
|
||||
/* 60 S> */ B(Wide), B(LdaSmi), I16(272),
|
||||
/* 60 S> */ B(Wide), B(LdaSmi), I16(275),
|
||||
B(Star), R(0),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(1),
|
||||
@ -214,7 +214,7 @@ frame size: 2
|
||||
parameter count: 1
|
||||
bytecode array length: 16
|
||||
bytecodes: [
|
||||
/* 53 S> */ B(Wide), B(LdaSmi), I16(271),
|
||||
/* 53 S> */ B(Wide), B(LdaSmi), I16(274),
|
||||
B(Star), R(0),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(1),
|
||||
@ -240,7 +240,7 @@ frame size: 2
|
||||
parameter count: 1
|
||||
bytecode array length: 16
|
||||
bytecodes: [
|
||||
/* 60 S> */ B(Wide), B(LdaSmi), I16(272),
|
||||
/* 60 S> */ B(Wide), B(LdaSmi), I16(275),
|
||||
B(Star), R(0),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(1),
|
||||
@ -266,7 +266,7 @@ frame size: 3
|
||||
parameter count: 1
|
||||
bytecode array length: 16
|
||||
bytecodes: [
|
||||
/* 46 S> */ B(Wide), B(LdaSmi), I16(271),
|
||||
/* 46 S> */ B(Wide), B(LdaSmi), I16(274),
|
||||
B(Star), R(1),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(2),
|
||||
|
@ -26103,7 +26103,7 @@ TEST(CorrectEnteredContext) {
|
||||
|
||||
v8::MaybeLocal<v8::Promise> HostImportModuleDynamicallyCallbackResolve(
|
||||
Local<Context> context, Local<v8::ScriptOrModule> referrer,
|
||||
Local<String> specifier) {
|
||||
Local<String> specifier, Local<FixedArray> import_assertions) {
|
||||
CHECK(!referrer.IsEmpty());
|
||||
String::Utf8Value referrer_utf8(
|
||||
context->GetIsolate(), Local<String>::Cast(referrer->GetResourceName()));
|
||||
@ -26116,6 +26116,8 @@ v8::MaybeLocal<v8::Promise> HostImportModuleDynamicallyCallbackResolve(
|
||||
String::Utf8Value specifier_utf8(context->GetIsolate(), specifier);
|
||||
CHECK_EQ(0, strcmp("index.js", *specifier_utf8));
|
||||
|
||||
CHECK_EQ(0, import_assertions->Length());
|
||||
|
||||
Local<v8::Promise::Resolver> resolver =
|
||||
v8::Promise::Resolver::New(context).ToLocalChecked();
|
||||
auto result = v8_str("hello world");
|
||||
@ -26127,7 +26129,6 @@ TEST(DynamicImport) {
|
||||
LocalContext context;
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
|
||||
isolate->SetHostImportModuleDynamicallyCallback(
|
||||
HostImportModuleDynamicallyCallbackResolve);
|
||||
|
||||
@ -26143,7 +26144,95 @@ TEST(DynamicImport) {
|
||||
referrer->set_name(*url);
|
||||
referrer->set_host_defined_options(*options);
|
||||
i::MaybeHandle<i::JSPromise> maybe_promise =
|
||||
i_isolate->RunHostImportModuleDynamicallyCallback(referrer, specifier);
|
||||
i_isolate->RunHostImportModuleDynamicallyCallback(
|
||||
referrer, specifier, i::MaybeHandle<i::Object>());
|
||||
i::Handle<i::JSPromise> promise = maybe_promise.ToHandleChecked();
|
||||
isolate->PerformMicrotaskCheckpoint();
|
||||
CHECK(result->Equals(i::String::cast(promise->result())));
|
||||
}
|
||||
|
||||
v8::MaybeLocal<v8::Promise>
|
||||
HostImportModuleDynamicallyWithAssertionsCallbackResolve(
|
||||
Local<Context> context, Local<v8::ScriptOrModule> referrer,
|
||||
Local<String> specifier, Local<v8::FixedArray> import_assertions) {
|
||||
CHECK(!referrer.IsEmpty());
|
||||
String::Utf8Value referrer_utf8(
|
||||
context->GetIsolate(), Local<String>::Cast(referrer->GetResourceName()));
|
||||
CHECK_EQ(0, strcmp("www.google.com", *referrer_utf8));
|
||||
CHECK(referrer->GetHostDefinedOptions()
|
||||
->Get(context->GetIsolate(), 0)
|
||||
->IsSymbol());
|
||||
|
||||
CHECK(!specifier.IsEmpty());
|
||||
String::Utf8Value specifier_utf8(context->GetIsolate(), specifier);
|
||||
CHECK_EQ(0, strcmp("index.js", *specifier_utf8));
|
||||
|
||||
// clang-format off
|
||||
const char* expected_assertions[][2] = {
|
||||
{"a", "z"},
|
||||
{"aa", "x"},
|
||||
{"b", "w"},
|
||||
{"c", "y"}
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
CHECK_EQ(8, import_assertions->Length());
|
||||
constexpr size_t kAssertionEntrySizeForDynamicImport = 2;
|
||||
for (int i = 0; i < static_cast<int>(arraysize(expected_assertions)); ++i) {
|
||||
Local<String> assertion_key =
|
||||
import_assertions
|
||||
->Get(context, (i * kAssertionEntrySizeForDynamicImport))
|
||||
.As<Value>()
|
||||
.As<String>();
|
||||
CHECK(v8_str(expected_assertions[i][0])->StrictEquals(assertion_key));
|
||||
Local<String> assertion_value =
|
||||
import_assertions
|
||||
->Get(context, (i * kAssertionEntrySizeForDynamicImport) + 1)
|
||||
.As<Value>()
|
||||
.As<String>();
|
||||
CHECK(v8_str(expected_assertions[i][1])->StrictEquals(assertion_value));
|
||||
}
|
||||
|
||||
Local<v8::Promise::Resolver> resolver =
|
||||
v8::Promise::Resolver::New(context).ToLocalChecked();
|
||||
auto result = v8_str("hello world");
|
||||
resolver->Resolve(context, result).ToChecked();
|
||||
return resolver->GetPromise();
|
||||
}
|
||||
|
||||
TEST(DynamicImportWithAssertions) {
|
||||
FLAG_SCOPE_EXTERNAL(harmony_import_assertions);
|
||||
|
||||
LocalContext context;
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
isolate->SetHostImportModuleDynamicallyCallback(
|
||||
HostImportModuleDynamicallyWithAssertionsCallbackResolve);
|
||||
|
||||
i::Handle<i::String> url(v8::Utils::OpenHandle(*v8_str("www.google.com")));
|
||||
i::Handle<i::Object> specifier(v8::Utils::OpenHandle(*v8_str("index.js")));
|
||||
i::Handle<i::String> result(v8::Utils::OpenHandle(*v8_str("hello world")));
|
||||
i::Handle<i::String> source(v8::Utils::OpenHandle(*v8_str("foo")));
|
||||
v8::Local<v8::Object> import_assertions =
|
||||
CompileRun(
|
||||
"var arg = { assert: { 'b': 'w', aa: 'x', c: 'y', a: 'z'} };"
|
||||
"arg;")
|
||||
->ToObject(context.local())
|
||||
.ToLocalChecked();
|
||||
|
||||
i::Handle<i::Object> i_import_assertions =
|
||||
v8::Utils::OpenHandle(*import_assertions);
|
||||
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
i::Handle<i::FixedArray> options = i_isolate->factory()->NewFixedArray(1);
|
||||
i::Handle<i::Symbol> symbol = i_isolate->factory()->NewSymbol();
|
||||
options->set(0, *symbol);
|
||||
i::Handle<i::Script> referrer = i_isolate->factory()->NewScript(source);
|
||||
referrer->set_name(*url);
|
||||
referrer->set_host_defined_options(*options);
|
||||
i::MaybeHandle<i::JSPromise> maybe_promise =
|
||||
i_isolate->RunHostImportModuleDynamicallyCallback(referrer, specifier,
|
||||
i_import_assertions);
|
||||
i::Handle<i::JSPromise> promise = maybe_promise.ToHandleChecked();
|
||||
isolate->PerformMicrotaskCheckpoint();
|
||||
CHECK(result->Equals(i::String::cast(promise->result())));
|
||||
|
@ -938,7 +938,7 @@ void DoHostImportModuleDynamically(void* import_data) {
|
||||
|
||||
v8::MaybeLocal<v8::Promise> HostImportModuleDynamicallyCallbackResolve(
|
||||
Local<Context> context, Local<v8::ScriptOrModule> referrer,
|
||||
Local<String> specifier) {
|
||||
Local<String> specifier, Local<FixedArray> import_assertions) {
|
||||
Isolate* isolate = context->GetIsolate();
|
||||
Local<v8::Promise::Resolver> resolver =
|
||||
v8::Promise::Resolver::New(context).ToLocalChecked();
|
||||
@ -951,7 +951,7 @@ v8::MaybeLocal<v8::Promise> HostImportModuleDynamicallyCallbackResolve(
|
||||
|
||||
v8::MaybeLocal<v8::Promise> HostImportModuleDynamicallyCallbackReject(
|
||||
Local<Context> context, Local<v8::ScriptOrModule> referrer,
|
||||
Local<String> specifier) {
|
||||
Local<String> specifier, Local<FixedArray> import_assertions) {
|
||||
Isolate* isolate = context->GetIsolate();
|
||||
Local<v8::Promise::Resolver> resolver =
|
||||
v8::Promise::Resolver::New(context).ToLocalChecked();
|
||||
|
9
test/message/fail/modules-import-assertions-fail-1.mjs
Normal file
9
test/message/fail/modules-import-assertions-fail-1.mjs
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// MODULE
|
||||
//
|
||||
// Flags: --harmony-import-assertions
|
||||
|
||||
import "modules-skip-1-import-assertions-fail.mjs" assert { type: "notARealType"}
|
1
test/message/fail/modules-import-assertions-fail-1.out
Normal file
1
test/message/fail/modules-import-assertions-fail-1.out
Normal file
@ -0,0 +1 @@
|
||||
undefined:0: Error: Invalid module type was asserted
|
9
test/message/fail/modules-import-assertions-fail-2.mjs
Normal file
9
test/message/fail/modules-import-assertions-fail-2.mjs
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// MODULE
|
||||
//
|
||||
// Flags: --harmony-import-assertions
|
||||
|
||||
import "modules-skip-1-import-assertions-fail.mjs" assert { type: "json"}
|
4
test/message/fail/modules-import-assertions-fail-2.out
Normal file
4
test/message/fail/modules-import-assertions-fail-2.out
Normal file
@ -0,0 +1,4 @@
|
||||
undefined:1: SyntaxError: Unexpected token / in JSON at position 0
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
^
|
||||
SyntaxError: Unexpected token / in JSON at position 0
|
9
test/message/fail/modules-import-assertions-fail-3.mjs
Normal file
9
test/message/fail/modules-import-assertions-fail-3.mjs
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// MODULE
|
||||
//
|
||||
// Flags: --harmony-import-assertions
|
||||
|
||||
import "modules-skip-3-import-assertions-fail.json"
|
4
test/message/fail/modules-import-assertions-fail-3.out
Normal file
4
test/message/fail/modules-import-assertions-fail-3.out
Normal file
@ -0,0 +1,4 @@
|
||||
*modules-skip-3-import-assertions-fail.json:1: SyntaxError: Unexpected token ':'
|
||||
{ "life": 42 }
|
||||
^
|
||||
SyntaxError: Unexpected token ':'
|
@ -0,0 +1,7 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// MODULE
|
||||
|
||||
export function life() { return 42; }
|
@ -0,0 +1 @@
|
||||
{ "life": 42 }
|
9
test/mjsunit/harmony/modules-import-assertions-1.mjs
Normal file
9
test/mjsunit/harmony/modules-import-assertions-1.mjs
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-import-assertions
|
||||
|
||||
import { life } from 'modules-skip-1.mjs' assert { };
|
||||
|
||||
assertEquals(42, life());
|
9
test/mjsunit/harmony/modules-import-assertions-2.mjs
Normal file
9
test/mjsunit/harmony/modules-import-assertions-2.mjs
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-import-assertions
|
||||
|
||||
import json from 'modules-skip-1.json' assert { type: 'json' };
|
||||
|
||||
assertEquals(42, json.life);
|
9
test/mjsunit/harmony/modules-import-assertions-3.mjs
Normal file
9
test/mjsunit/harmony/modules-import-assertions-3.mjs
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-import-assertions
|
||||
|
||||
import {life} from 'modules-skip-imports-json-1.mjs';
|
||||
|
||||
assertEquals(42, life());
|
9
test/mjsunit/harmony/modules-import-assertions-4.mjs
Normal file
9
test/mjsunit/harmony/modules-import-assertions-4.mjs
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-import-assertions
|
||||
|
||||
import json from 'modules-skip-1.json' assert { type: 'json', notARealAssertion: 'value'};
|
||||
|
||||
assertEquals(42, json.life);
|
12
test/mjsunit/harmony/modules-import-assertions-dynamic-1.mjs
Normal file
12
test/mjsunit/harmony/modules-import-assertions-dynamic-1.mjs
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --harmony-import-assertions
|
||||
|
||||
var life;
|
||||
import('modules-skip-1.mjs', { }).then(namespace => life = namespace.life());
|
||||
|
||||
%PerformMicrotaskCheckpoint();
|
||||
|
||||
assertEquals(42, life);
|
@ -0,0 +1,19 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --harmony-import-assertions
|
||||
|
||||
var result1;
|
||||
var result2;
|
||||
import('modules-skip-1.json', { get assert() { throw 'bad \'assert\' getter!'; } }).then(
|
||||
() => assertUnreachable('Should have failed due to throwing getter'),
|
||||
error => result1 = error);
|
||||
import('modules-skip-1.json', { assert: { get assertionKey() { throw 'bad \'assertionKey\' getter!'; } } }).then(
|
||||
() => assertUnreachable('Should have failed due to throwing getter'),
|
||||
error => result2 = error);
|
||||
|
||||
%PerformMicrotaskCheckpoint();
|
||||
|
||||
assertEquals('bad \'assert\' getter!', result1);
|
||||
assertEquals('bad \'assertionKey\' getter!', result2);
|
13
test/mjsunit/harmony/modules-import-assertions-dynamic-2.mjs
Normal file
13
test/mjsunit/harmony/modules-import-assertions-dynamic-2.mjs
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --harmony-import-assertions
|
||||
|
||||
var life;
|
||||
import('modules-skip-1.mjs', { assert: { } }).then(
|
||||
namespace => life = namespace.life());
|
||||
|
||||
%PerformMicrotaskCheckpoint();
|
||||
|
||||
assertEquals(42, life);
|
13
test/mjsunit/harmony/modules-import-assertions-dynamic-3.mjs
Normal file
13
test/mjsunit/harmony/modules-import-assertions-dynamic-3.mjs
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --harmony-import-assertions
|
||||
|
||||
var life;
|
||||
import('modules-skip-1.json', { assert: { type: 'json' } }).then(
|
||||
namespace => life = namespace.default.life);
|
||||
|
||||
%PerformMicrotaskCheckpoint();
|
||||
|
||||
assertEquals(42, life);
|
14
test/mjsunit/harmony/modules-import-assertions-dynamic-4.mjs
Normal file
14
test/mjsunit/harmony/modules-import-assertions-dynamic-4.mjs
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --harmony-import-assertions
|
||||
|
||||
var result;
|
||||
import('modules-skip-1.json', { assert: { type: 'notARealType' } }).then(
|
||||
() => assertUnreachable('Should have failed due to bad module type'),
|
||||
error => result = error.message);
|
||||
|
||||
%PerformMicrotaskCheckpoint();
|
||||
|
||||
assertEquals('Invalid module type was asserted', result);
|
12
test/mjsunit/harmony/modules-import-assertions-dynamic-5.mjs
Normal file
12
test/mjsunit/harmony/modules-import-assertions-dynamic-5.mjs
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --harmony-import-assertions
|
||||
|
||||
var life;
|
||||
import('modules-skip-imports-json-1.mjs',).then(namespace => life = namespace.life());
|
||||
|
||||
%PerformMicrotaskCheckpoint();
|
||||
|
||||
assertEquals(42, life);
|
13
test/mjsunit/harmony/modules-import-assertions-dynamic-6.mjs
Normal file
13
test/mjsunit/harmony/modules-import-assertions-dynamic-6.mjs
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --harmony-import-assertions
|
||||
|
||||
var life;
|
||||
import('modules-skip-1.json', { assert: { type: 'json', notARealAssertion: 'value' } }).then(
|
||||
namespace => life = namespace.default.life);
|
||||
|
||||
%PerformMicrotaskCheckpoint();
|
||||
|
||||
assertEquals(42, life);
|
63
test/mjsunit/harmony/modules-import-assertions-dynamic-7.mjs
Normal file
63
test/mjsunit/harmony/modules-import-assertions-dynamic-7.mjs
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --harmony-import-assertions
|
||||
|
||||
var result1;
|
||||
var result2;
|
||||
var result3;
|
||||
var result4;
|
||||
var result5;
|
||||
var result6;
|
||||
var result7;
|
||||
var result8;
|
||||
var result9;
|
||||
var result10;
|
||||
import('modules-skip-1.json', null).then(
|
||||
() => assertUnreachable('Should have failed due to non-object parameter'),
|
||||
error => result1 = error.message);
|
||||
import('modules-skip-1.json', 7).then(
|
||||
() => assertUnreachable('Should have failed due to non-object parameter'),
|
||||
error => result2 = error.message);
|
||||
import('modules-skip-1.json', 'string').then(
|
||||
() => assertUnreachable('Should have failed due to non-object parameter'),
|
||||
error => result3 = error.message);
|
||||
import('modules-skip-1.json', { assert: null}).then(
|
||||
() => assertUnreachable('Should have failed due to bad assert object'),
|
||||
error => result4 = error.message);
|
||||
import('modules-skip-1.json', { assert: 7}).then(
|
||||
() => assertUnreachable('Should have failed due to bad assert object'),
|
||||
error => result5 = error.message);
|
||||
import('modules-skip-1.json', { assert: 'string'}).then(
|
||||
() => assertUnreachable('Should have failed due to bad assert object'),
|
||||
error => result6 = error.message);
|
||||
import('modules-skip-1.json', { assert: { a: null }}).then(
|
||||
() => assertUnreachable('Should have failed due to bad assert object'),
|
||||
error => result7 = error.message);
|
||||
import('modules-skip-1.json', { assert: { a: undefined }}).then(
|
||||
() => assertUnreachable('Should have failed due to bad assertion value'),
|
||||
error => result8 = error.message);
|
||||
import('modules-skip-1.json', { assert: { a: 7 }}).then(
|
||||
() => assertUnreachable('Should have failed due to bad assertion value'),
|
||||
error => result9 = error.message);
|
||||
import('modules-skip-1.json', { assert: { a: { } }}).then(
|
||||
() => assertUnreachable('Should have failed due to bad assertion value'),
|
||||
error => result10 = error.message);
|
||||
|
||||
%PerformMicrotaskCheckpoint();
|
||||
|
||||
const argumentNotObjectError = 'The second argument to import() must be an object';
|
||||
const assertOptionNotObjectError = 'The \'assert\' option must be an object';
|
||||
const assertionValueNotStringError = 'Import assertion value must be a string';
|
||||
|
||||
assertEquals(argumentNotObjectError, result1);
|
||||
assertEquals(argumentNotObjectError, result2);
|
||||
assertEquals(argumentNotObjectError, result3);
|
||||
assertEquals(assertOptionNotObjectError, result4);
|
||||
assertEquals(assertOptionNotObjectError, result5);
|
||||
assertEquals(assertOptionNotObjectError, result6);
|
||||
assertEquals(assertionValueNotStringError, result7);
|
||||
assertEquals(assertionValueNotStringError, result8);
|
||||
assertEquals(assertionValueNotStringError, result9);
|
||||
assertEquals(assertionValueNotStringError, result10);
|
13
test/mjsunit/harmony/modules-import-assertions-dynamic-8.mjs
Normal file
13
test/mjsunit/harmony/modules-import-assertions-dynamic-8.mjs
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --harmony-import-assertions
|
||||
|
||||
var life;
|
||||
import('modules-skip-1.mjs', undefined).then(
|
||||
namespace => life = namespace.life());
|
||||
|
||||
%PerformMicrotaskCheckpoint();
|
||||
|
||||
assertEquals(42, life);
|
13
test/mjsunit/harmony/modules-import-assertions-dynamic-9.mjs
Normal file
13
test/mjsunit/harmony/modules-import-assertions-dynamic-9.mjs
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --harmony-import-assertions
|
||||
|
||||
var life;
|
||||
import('modules-skip-1.mjs', { assert: undefined }).then(
|
||||
namespace => life = namespace.life());
|
||||
|
||||
%PerformMicrotaskCheckpoint();
|
||||
|
||||
assertEquals(42, life);
|
1
test/mjsunit/harmony/modules-skip-1.json
Normal file
1
test/mjsunit/harmony/modules-skip-1.json
Normal file
@ -0,0 +1 @@
|
||||
{ "life": 42}
|
6
test/mjsunit/harmony/modules-skip-imports-json-1.mjs
Normal file
6
test/mjsunit/harmony/modules-skip-imports-json-1.mjs
Normal file
@ -0,0 +1,6 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import json from "modules-skip-1.json" assert { type: "json" };
|
||||
export function life() { return json.life; }
|
Loading…
Reference in New Issue
Block a user