Commit 53cb6654 authored by Daniel Clark's avatar Daniel Clark Committed by Commit Bot

[modules][api] Stop filtering import assertions based on list provided by the host

The change https://chromium-review.googlesource.com/c/v8/v8/+/2572173
implemented HostGetSupportedImportAssertions [1] in a fairly literal
sense, where the host supplies a list of supported import assertions
and V8 filters the import assertions in a ModuleRequest and exposes
only supported assertions via its API surface.

However, we've decided that the interop guarantees provided
by doing the filtering in V8 are probably not worth the added
complexity. Thus, this change removes the filtering. Going forward,
hosts will be expected to ignore unknown asserions received from V8.

This is mostly a revert of
https://chromium-review.googlesource.com/c/v8/v8/+/2572173, with
v8::Isolate::CreateParams::supported_import_assertions being kept
for now (since we first have to delete the Blink code that sets it),
and a new comment in v8.h instructing hosts to ignore unknown
assertions.

[1] https://tc39.es/proposal-import-assertions/#sec-hostgetsupportedimportassertions

Bug: v8:10958
Change-Id: I7e8e2a7fbfe2d5bf891805cff6c3160d0e6825cd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2643563Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarMythri Alle <mythria@chromium.org>
Commit-Queue: Dan Clark <daniec@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#72299}
parent 3b302d5c
...@@ -1564,6 +1564,13 @@ class V8_EXPORT ModuleRequest : public Data { ...@@ -1564,6 +1564,13 @@ class V8_EXPORT ModuleRequest : public Data {
* The keys and values are of type v8::String, and the source offsets are of * The keys and values are of type v8::String, and the source offsets are of
* type Int32. Use Module::SourceOffsetToLocation to convert the source * type Int32. Use Module::SourceOffsetToLocation to convert the source
* offsets to Locations with line/column numbers. * offsets to Locations with line/column numbers.
*
* All assertions present in the module request will be supplied in this
* list, regardless of whether they are supported by the host. Per
* https://tc39.es/proposal-import-assertions/#sec-hostgetsupportedimportassertions,
* hosts are expected to ignore assertions that they do not support (as
* opposed to, for example, triggering an error if an unsupported assertion is
* present).
*/ */
Local<FixedArray> GetImportAssertions() const; Local<FixedArray> GetImportAssertions() const;
...@@ -8437,18 +8444,9 @@ class V8_EXPORT Isolate { ...@@ -8437,18 +8444,9 @@ class V8_EXPORT Isolate {
*/ */
std::shared_ptr<CppHeapCreateParams> cpp_heap_params; std::shared_ptr<CppHeapCreateParams> cpp_heap_params;
/** V8_DEPRECATE_SOON(
* This list is provided by the embedder to indicate which import assertions "Setting this has no effect. Embedders should ignore import assertions "
* they want to handle. Only import assertions whose keys are present in "that they do not use.")
* supported_import_assertions will be included in the import assertions
* lists of ModuleRequests that will be passed to the embedder. If
* supported_import_assertions is left empty, then the embedder will not
* receive any import assertions.
*
* This corresponds to the list returned by the HostGetSupportedAssertions
* host-defined abstract operation:
* https://tc39.es/proposal-import-assertions/#sec-hostgetsupportedimportassertions
*/
std::vector<std::string> supported_import_assertions; std::vector<std::string> supported_import_assertions;
}; };
......
...@@ -8567,9 +8567,6 @@ void Isolate::Initialize(Isolate* isolate, ...@@ -8567,9 +8567,6 @@ void Isolate::Initialize(Isolate* isolate,
i_isolate->set_api_external_references(params.external_references); i_isolate->set_api_external_references(params.external_references);
i_isolate->set_allow_atomics_wait(params.allow_atomics_wait); i_isolate->set_allow_atomics_wait(params.allow_atomics_wait);
i_isolate->set_supported_import_assertions(
params.supported_import_assertions);
i_isolate->heap()->ConfigureHeap(params.constraints); i_isolate->heap()->ConfigureHeap(params.constraints);
if (params.cpp_heap_params) { if (params.cpp_heap_params) {
i_isolate->heap()->ConfigureCppHeap(params.cpp_heap_params); i_isolate->heap()->ConfigureCppHeap(params.cpp_heap_params);
......
...@@ -127,62 +127,23 @@ Handle<PrimitiveHeapObject> ToStringOrUndefined(LocalIsolate* isolate, ...@@ -127,62 +127,23 @@ Handle<PrimitiveHeapObject> ToStringOrUndefined(LocalIsolate* isolate,
template <typename LocalIsolate> template <typename LocalIsolate>
Handle<ModuleRequest> SourceTextModuleDescriptor::AstModuleRequest::Serialize( Handle<ModuleRequest> SourceTextModuleDescriptor::AstModuleRequest::Serialize(
LocalIsolate* isolate) const { LocalIsolate* isolate) const {
// Copy the assertions to a FixedArray, filtering out as we go the import // The import assertions will be stored in this array in the form:
// assertions that are not supported by the embedder. // [key1, value1, location1, key2, value2, location2, ...]
//
// This is O(m * n) where m is the number of import assertions in this
// request and n is the number of supported assertions. Both m and n are
// expected to be quite small, with m being 0 in the common case and n
// currently expected to be at most 1 since "type" is the only import
// assertion in use at the time of this writing. So for now we go with
// this simple nested loop approach, which should not result in a larger
// than necessary performance cost unless more than a few additional
// import assertions are standardized.
//
// The import assertions will be stored in the new array in the form: [key1,
// value1, location1, key2, value2, location2, ...].
const std::vector<std::string>& supported_assertions =
isolate->supported_import_assertions();
Handle<FixedArray> import_assertions_array = Handle<FixedArray> import_assertions_array =
isolate->factory()->NewFixedArray(static_cast<int>( isolate->factory()->NewFixedArray(static_cast<int>(
import_assertions()->size() * ModuleRequest::kAssertionEntrySize)); import_assertions()->size() * ModuleRequest::kAssertionEntrySize));
int filtered_assertions_index = 0;
for (auto iter = import_assertions()->cbegin();
iter != import_assertions()->cend(); ++iter) {
for (const std::string& supported_assertion : supported_assertions) {
Handle<String> assertion_key = iter->first->string();
if (assertion_key->IsEqualTo(Vector<const char>(
supported_assertion.c_str(), supported_assertion.length()))) {
import_assertions_array->set(filtered_assertions_index,
*iter->first->string());
import_assertions_array->set(filtered_assertions_index + 1,
*iter->second.first->string());
import_assertions_array->set(filtered_assertions_index + 2,
Smi::FromInt(iter->second.second.beg_pos));
filtered_assertions_index += ModuleRequest::kAssertionEntrySize;
break;
}
}
}
Handle<FixedArray> shortened_import_assertions_array; int i = 0;
if (filtered_assertions_index < import_assertions_array->length()) { for (auto iter = import_assertions()->cbegin();
// If we did filter out any assertions, create a FixedArray with the correct iter != import_assertions()->cend();
// length. This should be rare, since it would be unexpected for developers ++iter, i += ModuleRequest::kAssertionEntrySize) {
// to commonly use unsupported assertions. Note, we don't use import_assertions_array->set(i, *iter->first->string());
// FixedArray::ShrinkOrEmpty here since FixedArray::Shrink isn't available import_assertions_array->set(i + 1, *iter->second.first->string());
// on a LocalIsolate/LocalHeap. import_assertions_array->set(i + 2,
shortened_import_assertions_array = Smi::FromInt(iter->second.second.beg_pos));
isolate->factory()->NewFixedArray(filtered_assertions_index);
import_assertions_array->CopyTo(0, *shortened_import_assertions_array, 0,
filtered_assertions_index);
} else {
shortened_import_assertions_array = import_assertions_array;
} }
return v8::internal::ModuleRequest::New(isolate, specifier()->string(), return v8::internal::ModuleRequest::New(isolate, specifier()->string(),
shortened_import_assertions_array, import_assertions_array, position());
position());
} }
template Handle<ModuleRequest> template Handle<ModuleRequest>
SourceTextModuleDescriptor::AstModuleRequest::Serialize(Isolate* isolate) const; SourceTextModuleDescriptor::AstModuleRequest::Serialize(Isolate* isolate) const;
......
...@@ -1592,14 +1592,6 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { ...@@ -1592,14 +1592,6 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
void set_allow_atomics_wait(bool set) { allow_atomics_wait_ = set; } void set_allow_atomics_wait(bool set) { allow_atomics_wait_ = set; }
bool allow_atomics_wait() { return allow_atomics_wait_; } bool allow_atomics_wait() { return allow_atomics_wait_; }
void set_supported_import_assertions(
const std::vector<std::string>& supported_import_assertions) {
supported_import_assertions_ = supported_import_assertions;
}
const std::vector<std::string>& supported_import_assertions() const {
return supported_import_assertions_;
}
// Register a finalizer to be called at isolate teardown. // Register a finalizer to be called at isolate teardown.
void RegisterManagedPtrDestructor(ManagedPtrDestructor* finalizer); void RegisterManagedPtrDestructor(ManagedPtrDestructor* finalizer);
...@@ -1833,7 +1825,6 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { ...@@ -1833,7 +1825,6 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
PromiseHook promise_hook_ = nullptr; PromiseHook promise_hook_ = nullptr;
HostImportModuleDynamicallyCallback host_import_module_dynamically_callback_ = HostImportModuleDynamicallyCallback host_import_module_dynamically_callback_ =
nullptr; nullptr;
std::vector<std::string> supported_import_assertions_;
HostInitializeImportMetaObjectCallback HostInitializeImportMetaObjectCallback
host_initialize_import_meta_object_callback_ = nullptr; host_initialize_import_meta_object_callback_ = nullptr;
base::Mutex rail_mutex_; base::Mutex rail_mutex_;
......
...@@ -20,8 +20,7 @@ LocalIsolate::LocalIsolate(Isolate* isolate, ThreadKind kind) ...@@ -20,8 +20,7 @@ LocalIsolate::LocalIsolate(Isolate* isolate, ThreadKind kind)
thread_id_(ThreadId::Current()), thread_id_(ThreadId::Current()),
stack_limit_(kind == ThreadKind::kMain stack_limit_(kind == ThreadKind::kMain
? isolate->stack_guard()->real_climit() ? isolate->stack_guard()->real_climit()
: GetCurrentStackPosition() - FLAG_stack_size * KB), : GetCurrentStackPosition() - FLAG_stack_size * KB) {}
supported_import_assertions_(isolate->supported_import_assertions()) {}
LocalIsolate::~LocalIsolate() = default; LocalIsolate::~LocalIsolate() = default;
......
...@@ -85,10 +85,6 @@ class V8_EXPORT_PRIVATE LocalIsolate final : private HiddenLocalFactory { ...@@ -85,10 +85,6 @@ class V8_EXPORT_PRIVATE LocalIsolate final : private HiddenLocalFactory {
bool is_main_thread() const { return heap_.is_main_thread(); } bool is_main_thread() const { return heap_.is_main_thread(); }
const std::vector<std::string>& supported_import_assertions() const {
return supported_import_assertions_;
}
private: private:
friend class v8::internal::LocalFactory; friend class v8::internal::LocalFactory;
...@@ -101,7 +97,6 @@ class V8_EXPORT_PRIVATE LocalIsolate final : private HiddenLocalFactory { ...@@ -101,7 +97,6 @@ class V8_EXPORT_PRIVATE LocalIsolate final : private HiddenLocalFactory {
std::unique_ptr<LocalLogger> logger_; std::unique_ptr<LocalLogger> logger_;
ThreadId const thread_id_; ThreadId const thread_id_;
Address const stack_limit_; Address const stack_limit_;
std::vector<std::string> supported_import_assertions_;
}; };
template <base::MutexSharedType kIsShared> template <base::MutexSharedType kIsShared>
......
...@@ -148,13 +148,13 @@ MaybeLocal<Module> ResolveCallbackWithImportAssertions( ...@@ -148,13 +148,13 @@ MaybeLocal<Module> ResolveCallbackWithImportAssertions(
} else if (specifier->StrictEquals(v8_str("./bar.js"))) { } else if (specifier->StrictEquals(v8_str("./bar.js"))) {
CHECK_EQ(3, import_assertions->Length()); CHECK_EQ(3, import_assertions->Length());
Local<String> assertion_key = Local<String> assertion_key =
import_assertions->Get(context, 0).As<Value>().As<String>(); import_assertions->Get(env.local(), 0).As<Value>().As<String>();
CHECK(v8_str("a")->StrictEquals(assertion_key)); CHECK(v8_str("a")->StrictEquals(assertion_key));
Local<String> assertion_value = Local<String> assertion_value =
import_assertions->Get(context, 1).As<Value>().As<String>(); import_assertions->Get(env.local(), 1).As<Value>().As<String>();
CHECK(v8_str("b")->StrictEquals(assertion_value)); CHECK(v8_str("b")->StrictEquals(assertion_value));
Local<Data> assertion_source_offset_object = Local<Data> assertion_source_offset_object =
import_assertions->Get(context, 2); import_assertions->Get(env.local(), 2);
Local<Int32> assertion_source_offset_int32 = Local<Int32> assertion_source_offset_int32 =
assertion_source_offset_object.As<Value>() assertion_source_offset_object.As<Value>()
->ToInt32(context) ->ToInt32(context)
...@@ -176,25 +176,18 @@ TEST(ModuleInstantiationWithImportAssertions) { ...@@ -176,25 +176,18 @@ TEST(ModuleInstantiationWithImportAssertions) {
bool prev_top_level_await = i::FLAG_harmony_top_level_await; bool prev_top_level_await = i::FLAG_harmony_top_level_await;
bool prev_import_assertions = i::FLAG_harmony_import_assertions; bool prev_import_assertions = i::FLAG_harmony_import_assertions;
i::FLAG_harmony_import_assertions = true; i::FLAG_harmony_import_assertions = true;
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
create_params.supported_import_assertions = {"extra0", "a", "extra1"};
v8::Isolate* isolate = v8::Isolate::New(create_params);
for (auto top_level_await : {true, false}) { for (auto top_level_await : {true, false}) {
i::FLAG_harmony_top_level_await = top_level_await; i::FLAG_harmony_top_level_await = top_level_await;
v8::Isolate::Scope isolate_scope(isolate); Isolate* isolate = CcTest::isolate();
HandleScope scope(isolate); HandleScope scope(isolate);
Local<v8::Context> context = v8::Context::New(isolate); LocalContext env;
v8::Context::Scope context_scope(context);
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate);
Local<Module> module; Local<Module> module;
{ {
Local<String> source_text = v8_str( Local<String> source_text = v8_str(
"import './foo.js' assert { };\n" "import './foo.js' assert { };\n"
"export {} from './bar.js' assert { a: 'b', c: 'd' };"); "export {} from './bar.js' assert { a: 'b' };");
ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate()); ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
ScriptCompiler::Source source(source_text, origin); ScriptCompiler::Source source(source_text, origin);
module = ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked(); module = ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
...@@ -202,7 +195,7 @@ TEST(ModuleInstantiationWithImportAssertions) { ...@@ -202,7 +195,7 @@ TEST(ModuleInstantiationWithImportAssertions) {
Local<FixedArray> module_requests = module->GetModuleRequests(); Local<FixedArray> module_requests = module->GetModuleRequests();
CHECK_EQ(2, module_requests->Length()); CHECK_EQ(2, module_requests->Length());
Local<ModuleRequest> module_request_0 = Local<ModuleRequest> module_request_0 =
module_requests->Get(context, 0).As<ModuleRequest>(); module_requests->Get(env.local(), 0).As<ModuleRequest>();
CHECK(v8_str("./foo.js")->StrictEquals(module_request_0->GetSpecifier())); CHECK(v8_str("./foo.js")->StrictEquals(module_request_0->GetSpecifier()));
int offset = module_request_0->GetSourceOffset(); int offset = module_request_0->GetSourceOffset();
CHECK_EQ(7, offset); CHECK_EQ(7, offset);
...@@ -212,7 +205,7 @@ TEST(ModuleInstantiationWithImportAssertions) { ...@@ -212,7 +205,7 @@ TEST(ModuleInstantiationWithImportAssertions) {
CHECK_EQ(0, module_request_0->GetImportAssertions()->Length()); CHECK_EQ(0, module_request_0->GetImportAssertions()->Length());
Local<ModuleRequest> module_request_1 = Local<ModuleRequest> module_request_1 =
module_requests->Get(context, 1).As<ModuleRequest>(); module_requests->Get(env.local(), 1).As<ModuleRequest>();
CHECK(v8_str("./bar.js")->StrictEquals(module_request_1->GetSpecifier())); CHECK(v8_str("./bar.js")->StrictEquals(module_request_1->GetSpecifier()));
offset = module_request_1->GetSourceOffset(); offset = module_request_1->GetSourceOffset();
CHECK_EQ(45, offset); CHECK_EQ(45, offset);
...@@ -224,13 +217,13 @@ TEST(ModuleInstantiationWithImportAssertions) { ...@@ -224,13 +217,13 @@ TEST(ModuleInstantiationWithImportAssertions) {
module_request_1->GetImportAssertions(); module_request_1->GetImportAssertions();
CHECK_EQ(3, import_assertions_1->Length()); CHECK_EQ(3, import_assertions_1->Length());
Local<String> assertion_key = Local<String> assertion_key =
import_assertions_1->Get(context, 0).As<String>(); import_assertions_1->Get(env.local(), 0).As<String>();
CHECK(v8_str("a")->StrictEquals(assertion_key)); CHECK(v8_str("a")->StrictEquals(assertion_key));
Local<String> assertion_value = Local<String> assertion_value =
import_assertions_1->Get(context, 1).As<String>(); import_assertions_1->Get(env.local(), 1).As<String>();
CHECK(v8_str("b")->StrictEquals(assertion_value)); CHECK(v8_str("b")->StrictEquals(assertion_value));
int32_t assertion_source_offset = int32_t assertion_source_offset =
import_assertions_1->Get(context, 2).As<Int32>()->Value(); import_assertions_1->Get(env.local(), 2).As<Int32>()->Value();
CHECK_EQ(65, assertion_source_offset); CHECK_EQ(65, assertion_source_offset);
loc = module->SourceOffsetToLocation(assertion_source_offset); loc = module->SourceOffsetToLocation(assertion_source_offset);
CHECK_EQ(1, loc.GetLineNumber()); CHECK_EQ(1, loc.GetLineNumber());
...@@ -255,72 +248,6 @@ TEST(ModuleInstantiationWithImportAssertions) { ...@@ -255,72 +248,6 @@ TEST(ModuleInstantiationWithImportAssertions) {
ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked(); ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
} }
CHECK(
module->InstantiateModule(context, ResolveCallbackWithImportAssertions)
.FromJust());
CHECK_EQ(Module::kInstantiated, module->GetStatus());
MaybeLocal<Value> result = module->Evaluate(context);
CHECK_EQ(Module::kEvaluated, module->GetStatus());
if (i::FLAG_harmony_top_level_await) {
Local<Promise> promise = Local<Promise>::Cast(result.ToLocalChecked());
CHECK_EQ(promise->State(), v8::Promise::kFulfilled);
CHECK(promise->Result()->IsUndefined());
} else {
CHECK(!result.IsEmpty());
ExpectInt32("Object.expando", 42);
}
CHECK(!try_catch.HasCaught());
}
isolate->Dispose();
i::FLAG_harmony_top_level_await = prev_top_level_await;
i::FLAG_harmony_import_assertions = prev_import_assertions;
}
TEST(ModuleInstantiationWithImportAssertionsWithoutSupportedAssertions) {
bool prev_top_level_await = i::FLAG_harmony_top_level_await;
bool prev_import_assertions = i::FLAG_harmony_import_assertions;
i::FLAG_harmony_import_assertions = true;
for (auto top_level_await : {true, false}) {
i::FLAG_harmony_top_level_await = top_level_await;
Isolate* isolate = CcTest::isolate();
HandleScope scope(isolate);
LocalContext env;
v8::TryCatch try_catch(isolate);
Local<Module> module;
{
Local<String> source_text =
v8_str("import './foo.js' assert { a: 'b' };");
ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
ScriptCompiler::Source source(source_text, origin);
module = ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
CHECK_EQ(Module::kUninstantiated, module->GetStatus());
Local<FixedArray> module_requests = module->GetModuleRequests();
CHECK_EQ(1, module_requests->Length());
Local<ModuleRequest> module_request_0 =
module_requests->Get(env.local(), 0).As<ModuleRequest>();
CHECK(v8_str("./foo.js")->StrictEquals(module_request_0->GetSpecifier()));
int offset = module_request_0->GetSourceOffset();
CHECK_EQ(7, offset);
Location loc = module->SourceOffsetToLocation(offset);
CHECK_EQ(0, loc.GetLineNumber());
CHECK_EQ(7, loc.GetColumnNumber());
// No supported assertions were provided in the Isolate's CreateParams, so
// no import assertions should be visible on the API surface.
CHECK_EQ(0, module_request_0->GetImportAssertions()->Length());
}
// foo.js
{
Local<String> source_text = v8_str("Object.expando = 40");
ScriptOrigin origin = ModuleOrigin(v8_str("foo.js"), CcTest::isolate());
ScriptCompiler::Source source(source_text, origin);
fooModule =
ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
}
CHECK(module CHECK(module
->InstantiateModule(env.local(), ->InstantiateModule(env.local(),
ResolveCallbackWithImportAssertions) ResolveCallbackWithImportAssertions)
...@@ -335,7 +262,7 @@ TEST(ModuleInstantiationWithImportAssertionsWithoutSupportedAssertions) { ...@@ -335,7 +262,7 @@ TEST(ModuleInstantiationWithImportAssertionsWithoutSupportedAssertions) {
CHECK(promise->Result()->IsUndefined()); CHECK(promise->Result()->IsUndefined());
} else { } else {
CHECK(!result.IsEmpty()); CHECK(!result.IsEmpty());
ExpectInt32("Object.expando", 40); ExpectInt32("Object.expando", 42);
} }
CHECK(!try_catch.HasCaught()); CHECK(!try_catch.HasCaught());
} }
......
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