[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:
Daniel Clark 2021-01-25 17:38:33 -08:00 committed by Commit Bot
parent 81e7e2f437
commit a09c076f00
39 changed files with 791 additions and 75 deletions

View File

@ -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.

View File

@ -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);
}

View File

@ -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") \

View File

@ -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);

View File

@ -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,

View File

@ -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));

View File

@ -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_;

View File

@ -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);

View File

@ -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) {

View File

@ -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) \

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

@ -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())));

View File

@ -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();

View 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"}

View File

@ -0,0 +1 @@
undefined:0: Error: Invalid module type was asserted

View 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"}

View 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

View 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"

View File

@ -0,0 +1,4 @@
*modules-skip-3-import-assertions-fail.json:1: SyntaxError: Unexpected token ':'
{ "life": 42 }
^
SyntaxError: Unexpected token ':'

View File

@ -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; }

View File

@ -0,0 +1 @@
{ "life": 42 }

View 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());

View 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);

View 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());

View 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);

View 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);

View File

@ -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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View File

@ -0,0 +1 @@
{ "life": 42}

View 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; }