Commit a09c076f authored by Daniel Clark's avatar Daniel Clark Committed by Commit Bot

[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: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarMythri Alle <mythria@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72307}
parent 81e7e2f4
......@@ -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") \
......
This diff is collapsed.
......@@ -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(
DeprecatedHostImportModuleDynamicallyCallback callback);
void SetHostImportModuleDynamicallyCallback(
HostImportModuleDynamicallyCallback callback);
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();
......
// 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"}
undefined:0: Error: Invalid module type was asserted
\ No newline at end of file
// 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"}
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
\ No newline at end of file
// 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"
*modules-skip-3-import-assertions-fail.json:1: SyntaxError: Unexpected token ':'
{ "life": 42 }
^
SyntaxError: Unexpected token ':'
\ No newline at end of file
// 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; }
// 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());
// 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);
// 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());
// 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);
// 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);
// 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);
\ No newline at end of file
// 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);
// 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);
// 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);
// 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);
// 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);
// 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);
// 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);
// 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);
// 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; }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment