diff --git a/.eslintrc.js b/.eslintrc.js index 7333382fe3..a38335ee21 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -39,7 +39,6 @@ module.exports = { files: [ 'doc/api/esm.md', '*.mjs', - 'test/es-module/test-esm-example-loader.js', ], parserOptions: { sourceType: 'module' }, }, diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 01fb56b55d..bc532e5ec5 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -1219,6 +1219,19 @@ class V8_EXPORT Module { Local GetUnboundModuleScript(); }; +/** + * A Dynamic JavaScript module + */ +class V8_EXPORT DynamicModule : public Module { + public: + /** + * Set an export value, corresponding to an export name + */ + bool SetExport(Isolate* isolate, Local export_name, + Local value); + V8_INLINE static DynamicModule* Cast(v8::Module* module); +}; + /** * A compiled JavaScript script, tied to a Context which was active when the * script was compiled. @@ -1547,6 +1560,12 @@ class V8_EXPORT ScriptCompiler { CompileOptions options = kNoCompileOptions, NoCacheReason no_cache_reason = kNoCacheNoReason); + /** + * Create a new Dynamic Module. + */ + static V8_WARN_UNUSED_RESULT MaybeLocal CreateDynamicModule( + Isolate* isolate); + /** * Compile a function for a given context. This is equivalent to running * @@ -6421,6 +6440,15 @@ typedef void (*AddHistogramSampleCallback)(void* histogram, int sample); typedef void (*BeforeCallEnteredCallback)(Isolate*); typedef void (*CallCompletedCallback)(Isolate*); +/** + * HostExecuteDynamicModuleCallback is called at the exact point + * of execution of a dynamic module in the graph. + * + * It should call Module::SetExport for each defined export. + */ +typedef void (*HostExecuteDynamicModuleCallback)(Local context, + Local module); + /** * HostImportModuleDynamicallyCallback is called when we require the * embedder to load a module. This is used as part of the dynamic @@ -7450,6 +7478,13 @@ class V8_EXPORT Isolate { void SetAbortOnUncaughtExceptionCallback( AbortOnUncaughtExceptionCallback callback); + /* + * This specifies the callback called by Dynamic Module records + * at their exact point of execution in the module graph. + */ + void SetHostExecuteDynamicModuleCallback( + HostExecuteDynamicModuleCallback callback); + /** * This specifies the callback called by the upcoming dynamic * import() language feature to load modules. @@ -10163,6 +10198,13 @@ WasmCompiledModule* WasmCompiledModule::Cast(v8::Value* value) { return static_cast(value); } +DynamicModule* DynamicModule::Cast(v8::Module* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); +} + Promise::Resolver* Promise::Resolver::Cast(v8::Value* value) { #ifdef V8_ENABLE_CHECKS CheckCast(value); diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 64676f06c1..82ff14b3aa 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -2292,6 +2292,79 @@ MaybeLocal Module::Evaluate(Local context) { RETURN_ESCAPED(result); } +class IsIdentifierHelper { + public: + IsIdentifierHelper() : is_identifier_(false), first_char_(true) {} + + bool Check(i::String* string) { + i::ConsString* cons_string = i::String::VisitFlat(this, string, 0); + if (cons_string == nullptr) return is_identifier_; + // We don't support cons strings here. + return false; + } + void VisitOneByteString(const uint8_t* chars, int length) { + for (int i = 0; i < length; ++i) { + if (first_char_) { + first_char_ = false; + is_identifier_ = unicode_cache_.IsIdentifierStart(chars[0]); + } else { + is_identifier_ &= unicode_cache_.IsIdentifierPart(chars[i]); + } + } + } + void VisitTwoByteString(const uint16_t* chars, int length) { + for (int i = 0; i < length; ++i) { + if (first_char_) { + first_char_ = false; + is_identifier_ = unicode_cache_.IsIdentifierStart(chars[0]); + } else { + is_identifier_ &= unicode_cache_.IsIdentifierPart(chars[i]); + } + } + } + + private: + bool is_identifier_; + bool first_char_; + i::UnicodeCache unicode_cache_; + DISALLOW_COPY_AND_ASSIGN(IsIdentifierHelper); +}; + +bool DynamicModule::SetExport(Isolate* v8_isolate, Local export_name, + Local value) { + Utils::ApiCheck( + GetStatus() >= kEvaluating, "v8::Module::SetExport", + "v8::DynamicModule::SetExport must be used after instantiation"); + i::Isolate* isolate = reinterpret_cast(v8_isolate); + i::Handle self = Utils::OpenHandle(this); + i::Handle name = Utils::OpenHandle(*(export_name)); + i::Handle object(self->exports()->Lookup(name), isolate); + + i::Handle val = Utils::OpenHandle(*(value)); + + // Exports can be defined while executing + if (!object->IsCell()) { + if (GetStatus() == kEvaluating) { + i::Handle internal_name = + isolate->factory()->InternalizeString(name); + // Return *false* for any invalid identifier passed + IsIdentifierHelper helper; + if (!helper.Check(*internal_name)) return false; + auto cell = i::Module::CreateDynamicExport(isolate, self, internal_name); + cell->set_value(*val); + return true; + } + else { + Utils::ApiCheck(object->IsCell(), "v8::Module::SetExport", + "v8::Module::SetExport unable to find local export name"); + } + } + + auto cell = i::Handle::cast(object); + cell->set_value(*val); + return true; +} + namespace { i::Compiler::ScriptDetails GetScriptDetails( @@ -2417,44 +2490,12 @@ MaybeLocal ScriptCompiler::CompileModule( return ToApiHandle(i_isolate->factory()->NewModule(shared)); } +MaybeLocal ScriptCompiler::CreateDynamicModule( + Isolate* isolate) { + i::Isolate* i_isolate = reinterpret_cast(isolate); -class IsIdentifierHelper { - public: - IsIdentifierHelper() : is_identifier_(false), first_char_(true) {} - - bool Check(i::String* string) { - i::ConsString* cons_string = i::String::VisitFlat(this, string, 0); - if (cons_string == nullptr) return is_identifier_; - // We don't support cons strings here. - return false; - } - void VisitOneByteString(const uint8_t* chars, int length) { - for (int i = 0; i < length; ++i) { - if (first_char_) { - first_char_ = false; - is_identifier_ = unicode_cache_.IsIdentifierStart(chars[0]); - } else { - is_identifier_ &= unicode_cache_.IsIdentifierPart(chars[i]); - } - } - } - void VisitTwoByteString(const uint16_t* chars, int length) { - for (int i = 0; i < length; ++i) { - if (first_char_) { - first_char_ = false; - is_identifier_ = unicode_cache_.IsIdentifierStart(chars[0]); - } else { - is_identifier_ &= unicode_cache_.IsIdentifierPart(chars[i]); - } - } - } - - private: - bool is_identifier_; - bool first_char_; - i::UnicodeCache unicode_cache_; - DISALLOW_COPY_AND_ASSIGN(IsIdentifierHelper); -}; + return ToApiHandle(i_isolate->factory()->NewDynamicModule()); +} MaybeLocal ScriptCompiler::CompileFunctionInContext( Local v8_context, Source* source, size_t arguments_count, @@ -8286,6 +8327,12 @@ void Isolate::SetAbortOnUncaughtExceptionCallback( isolate->SetAbortOnUncaughtExceptionCallback(callback); } +void Isolate::SetHostExecuteDynamicModuleCallback( + HostExecuteDynamicModuleCallback callback) { + i::Isolate* isolate = reinterpret_cast(this); + isolate->SetHostExecuteDynamicModuleCallback(callback); +} + void Isolate::SetHostImportModuleDynamicallyCallback( HostImportModuleDynamicallyCallback callback) { i::Isolate* isolate = reinterpret_cast(this); diff --git a/deps/v8/src/heap/factory.cc b/deps/v8/src/heap/factory.cc index 9535eb4b88..ce6a75ae6f 100644 --- a/deps/v8/src/heap/factory.cc +++ b/deps/v8/src/heap/factory.cc @@ -3053,6 +3053,36 @@ Handle Factory::NewModule(Handle code) { module->set_import_meta(roots.the_hole_value()); module->set_dfs_index(-1); module->set_dfs_ancestor_index(-1); + module->set_module_type(Module::Type::kSourceTextModule); + return module; +} + +Handle Factory::NewDynamicModule() { + Handle exports = ObjectHashTable::New(isolate(), 0); + // For dynamic modules, regular_exports is an + // ArrayList until execution completion. + Handle regular_exports = ArrayList::New(isolate(), 0); + Handle dynamic_namespaces = ArrayList::New(isolate(), 0); + + ReadOnlyRoots roots(isolate()); + + Handle module = Handle::cast(NewStruct(MODULE_TYPE, TENURED)); + + module->set_code(roots.undefined_value()); + module->set_exports(*exports); + module->set_regular_exports(*regular_exports); + // Dynamic modules reuse regular_imports to track dynamic_namespaces. + module->set_regular_imports(*dynamic_namespaces); + module->set_hash(isolate()->GenerateIdentityHash(Smi::kMaxValue)); + module->set_module_namespace(roots.undefined_value()); + module->set_requested_modules(roots.empty_fixed_array()); + // module->set_script(); + module->set_status(Module::kUninstantiated); + module->set_exception(roots.the_hole_value()); + module->set_import_meta(roots.the_hole_value()); + module->set_dfs_index(-1); + module->set_dfs_ancestor_index(-1); + module->set_module_type(Module::Type::kDynamicModule); return module; } diff --git a/deps/v8/src/heap/factory.h b/deps/v8/src/heap/factory.h index 8c6d32090e..825035164a 100644 --- a/deps/v8/src/heap/factory.h +++ b/deps/v8/src/heap/factory.h @@ -635,6 +635,8 @@ class V8_EXPORT_PRIVATE Factory { Handle NewModule(Handle code); + Handle NewDynamicModule(); + Handle NewJSArrayBuffer( SharedFlag shared = SharedFlag::kNotShared, PretenureFlag pretenure = NOT_TENURED); diff --git a/deps/v8/src/isolate.cc b/deps/v8/src/isolate.cc index e6a9e95a2f..ac4341b5aa 100644 --- a/deps/v8/src/isolate.cc +++ b/deps/v8/src/isolate.cc @@ -4042,6 +4042,18 @@ void Isolate::SetHostInitializeImportMetaObjectCallback( host_initialize_import_meta_object_callback_ = callback; } +void Isolate::RunHostExecuteDynamicModuleCallback(Handle context, + Handle module) { + v8::Local api_context = Utils::ToLocal(context); + host_execute_dynamic_module_callback_( + api_context, Utils::Convert(module)); +} + +void Isolate::SetHostExecuteDynamicModuleCallback( + HostExecuteDynamicModuleCallback callback) { + host_execute_dynamic_module_callback_ = callback; +} + MaybeHandle Isolate::RunPrepareStackTraceCallback( Handle context, Handle error) { v8::Local api_context = Utils::ToLocal(context); diff --git a/deps/v8/src/isolate.h b/deps/v8/src/isolate.h index c25f143cf8..c6f0e63f2b 100644 --- a/deps/v8/src/isolate.h +++ b/deps/v8/src/isolate.h @@ -1506,6 +1506,11 @@ class Isolate : private HiddenFactory { Handle RunHostInitializeImportMetaObjectCallback( Handle module); + void SetHostExecuteDynamicModuleCallback( + HostExecuteDynamicModuleCallback callback); + void RunHostExecuteDynamicModuleCallback(Handle, + Handle module); + void SetPrepareStackTraceCallback(PrepareStackTraceCallback callback); MaybeHandle RunPrepareStackTraceCallback(Handle, Handle Error); @@ -1731,6 +1736,8 @@ class Isolate : private HiddenFactory { HostImportModuleDynamicallyCallback host_import_module_dynamically_callback_; HostInitializeImportMetaObjectCallback host_initialize_import_meta_object_callback_; + HostExecuteDynamicModuleCallback host_execute_dynamic_module_callback_ = + nullptr; base::Mutex rail_mutex_; double load_start_time_ms_; diff --git a/deps/v8/src/messages.h b/deps/v8/src/messages.h index 05d287faae..0c1c66d08c 100644 --- a/deps/v8/src/messages.h +++ b/deps/v8/src/messages.h @@ -598,6 +598,8 @@ class ErrorUtils : public AllStatic { T(DuplicateExport, "Duplicate export of '%'") \ T(DuplicateProto, \ "Duplicate __proto__ fields are not allowed in object literals") \ + T(DynamicModuleStarExport, \ + "Star exports from dynamic module '%' are not allowed") \ T(ForInOfLoopInitializer, \ "% loop variable declaration may not have an initializer.") \ T(ForInOfLoopMultiBindings, \ diff --git a/deps/v8/src/objects/module-inl.h b/deps/v8/src/objects/module-inl.h index 1a4f2b3efa..451bc54b66 100644 --- a/deps/v8/src/objects/module-inl.h +++ b/deps/v8/src/objects/module-inl.h @@ -30,6 +30,7 @@ SMI_ACCESSORS(Module, status, kStatusOffset) SMI_ACCESSORS(Module, dfs_index, kDfsIndexOffset) SMI_ACCESSORS(Module, dfs_ancestor_index, kDfsAncestorIndexOffset) SMI_ACCESSORS(Module, hash, kHashOffset) +SMI_ACCESSORS(Module, module_type, kModuleTypeOffset) ModuleInfo* Module::info() const { return (status() >= kEvaluating) diff --git a/deps/v8/src/objects/module.cc b/deps/v8/src/objects/module.cc index c4d2626e60..bf6bc6d75c 100644 --- a/deps/v8/src/objects/module.cc +++ b/deps/v8/src/objects/module.cc @@ -114,29 +114,22 @@ int Module::ImportIndex(int cell_index) { return -cell_index - 1; } -void Module::CreateIndirectExport(Isolate* isolate, Handle module, - Handle name, - Handle entry) { - Handle exports(module->exports(), isolate); - DCHECK(exports->Lookup(name)->IsTheHole(isolate)); - exports = ObjectHashTable::Put(exports, name, entry); - module->set_exports(*exports); -} - -void Module::CreateExport(Isolate* isolate, Handle module, - int cell_index, Handle names) { - DCHECK_LT(0, names->length()); - Handle cell = - isolate->factory()->NewCell(isolate->factory()->undefined_value()); - module->regular_exports()->set(ExportIndex(cell_index), *cell); +Handle Module::CreateDynamicExport(Isolate* isolate, + Handle module, + Handle name) { + DCHECK_EQ(module->module_type(), kDynamicModule); + Handle regular_exports = + handle(ArrayList::cast(module->regular_exports()), isolate); + Handle cell = + isolate->factory()->NewCell(isolate->factory()->the_hole_value()); + regular_exports = ArrayList::Add(isolate, regular_exports, cell); + module->set_regular_exports(*regular_exports); Handle exports(module->exports(), isolate); - for (int i = 0, n = names->length(); i < n; ++i) { - Handle name(String::cast(names->get(i)), isolate); - DCHECK(exports->Lookup(name)->IsTheHole(isolate)); - exports = ObjectHashTable::Put(exports, name, cell); - } - module->set_exports(*exports); + DCHECK(exports->Lookup(name)->IsTheHole(isolate)); + exports = ObjectHashTable::Put(exports, name, cell); + module->set_exports(*exports); + return cell; } Cell* Module::GetCell(int cell_index) { @@ -174,7 +167,11 @@ void Module::PrintStatusTransition(Status new_status) { StdoutStream os; os << "Changing module status from " << status() << " to " << new_status << " for "; - script()->GetNameOrSourceURL()->Print(os); + if (module_type() == kDynamicModule) { + os << "[dynamic]\n"; + } else { + script()->GetNameOrSourceURL()->Print(os); + } #ifndef OBJECT_PRINT os << "\n"; #endif // OBJECT_PRINT @@ -250,7 +247,7 @@ void Module::RecordError(Isolate* isolate) { Object* the_exception = isolate->pending_exception(); DCHECK(!the_exception->IsTheHole(isolate)); - set_code(info()); + if (module_type() != kDynamicModule) set_code(info()); #ifdef DEBUG PrintStatusTransition(Module::kErrored); #endif // DEBUG @@ -269,6 +266,7 @@ SharedFunctionInfo* Module::GetSharedFunctionInfo() const { DisallowHeapAllocation no_alloc; DCHECK_NE(status(), Module::kEvaluating); DCHECK_NE(status(), Module::kEvaluated); + DCHECK_NE(module_type(), Module::kDynamicModule); switch (status()) { case kUninstantiated: case kPreInstantiating: @@ -318,6 +316,17 @@ MaybeHandle Module::ResolveExport(Isolate* isolate, Handle module, return Handle::cast(object); } + // Dynamic modules allocate export cells as they are resolved. + if (module->module_type() == kDynamicModule) { + if (module->status() == kEvaluated) { + return isolate->Throw(isolate->factory()->NewSyntaxError( + MessageTemplate::kUnresolvableExport, + module_specifier, export_name), + &loc); + } + return CreateDynamicExport(isolate, module, export_name); + } + // Check for cycle before recursing. { // Attempt insertion with a null string set. @@ -346,6 +355,7 @@ MaybeHandle Module::ResolveExport(Isolate* isolate, Handle module, // Not yet resolved indirect export. Handle entry = Handle::cast(object); Handle import_name(String::cast(entry->import_name()), isolate); + Handle