| // Copyright 2019 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. |
| |
| #include "src/objects/source-text-module.h" |
| |
| #include "src/api/api-inl.h" |
| #include "src/ast/modules.h" |
| #include "src/builtins/accessors.h" |
| #include "src/objects/js-generator-inl.h" |
| #include "src/objects/module-inl.h" |
| #include "src/objects/objects-inl.h" |
| #include "src/objects/shared-function-info.h" |
| #include "src/utils/ostreams.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| struct StringHandleHash { |
| V8_INLINE size_t operator()(Handle<String> string) const { |
| return string->Hash(); |
| } |
| }; |
| |
| struct StringHandleEqual { |
| V8_INLINE bool operator()(Handle<String> lhs, Handle<String> rhs) const { |
| return lhs->Equals(*rhs); |
| } |
| }; |
| |
| class UnorderedStringSet |
| : public std::unordered_set<Handle<String>, StringHandleHash, |
| StringHandleEqual, |
| ZoneAllocator<Handle<String>>> { |
| public: |
| explicit UnorderedStringSet(Zone* zone) |
| : std::unordered_set<Handle<String>, StringHandleHash, StringHandleEqual, |
| ZoneAllocator<Handle<String>>>( |
| 2 /* bucket count */, StringHandleHash(), StringHandleEqual(), |
| ZoneAllocator<Handle<String>>(zone)) {} |
| }; |
| |
| class UnorderedStringMap |
| : public std::unordered_map< |
| Handle<String>, Handle<Object>, StringHandleHash, StringHandleEqual, |
| ZoneAllocator<std::pair<const Handle<String>, Handle<Object>>>> { |
| public: |
| explicit UnorderedStringMap(Zone* zone) |
| : std::unordered_map< |
| Handle<String>, Handle<Object>, StringHandleHash, StringHandleEqual, |
| ZoneAllocator<std::pair<const Handle<String>, Handle<Object>>>>( |
| 2 /* bucket count */, StringHandleHash(), StringHandleEqual(), |
| ZoneAllocator<std::pair<const Handle<String>, Handle<Object>>>( |
| zone)) {} |
| }; |
| |
| class Module::ResolveSet |
| : public std::unordered_map< |
| Handle<Module>, UnorderedStringSet*, ModuleHandleHash, |
| ModuleHandleEqual, |
| ZoneAllocator<std::pair<const Handle<Module>, UnorderedStringSet*>>> { |
| public: |
| explicit ResolveSet(Zone* zone) |
| : std::unordered_map<Handle<Module>, UnorderedStringSet*, |
| ModuleHandleHash, ModuleHandleEqual, |
| ZoneAllocator<std::pair<const Handle<Module>, |
| UnorderedStringSet*>>>( |
| 2 /* bucket count */, ModuleHandleHash(), ModuleHandleEqual(), |
| ZoneAllocator<std::pair<const Handle<Module>, UnorderedStringSet*>>( |
| zone)), |
| zone_(zone) {} |
| |
| Zone* zone() const { return zone_; } |
| |
| private: |
| Zone* zone_; |
| }; |
| |
| SharedFunctionInfo SourceTextModule::GetSharedFunctionInfo() const { |
| DisallowHeapAllocation no_alloc; |
| switch (status()) { |
| case kUninstantiated: |
| case kPreInstantiating: |
| DCHECK(code().IsSharedFunctionInfo()); |
| return SharedFunctionInfo::cast(code()); |
| case kInstantiating: |
| DCHECK(code().IsJSFunction()); |
| return JSFunction::cast(code()).shared(); |
| case kInstantiated: |
| case kEvaluating: |
| case kEvaluated: |
| DCHECK(code().IsJSGeneratorObject()); |
| return JSGeneratorObject::cast(code()).function().shared(); |
| case kErrored: |
| UNREACHABLE(); |
| } |
| |
| UNREACHABLE(); |
| } |
| |
| int SourceTextModule::ExportIndex(int cell_index) { |
| DCHECK_EQ(SourceTextModuleDescriptor::GetCellIndexKind(cell_index), |
| SourceTextModuleDescriptor::kExport); |
| return cell_index - 1; |
| } |
| |
| int SourceTextModule::ImportIndex(int cell_index) { |
| DCHECK_EQ(SourceTextModuleDescriptor::GetCellIndexKind(cell_index), |
| SourceTextModuleDescriptor::kImport); |
| return -cell_index - 1; |
| } |
| |
| void SourceTextModule::CreateIndirectExport( |
| Isolate* isolate, Handle<SourceTextModule> module, Handle<String> name, |
| Handle<SourceTextModuleInfoEntry> entry) { |
| Handle<ObjectHashTable> exports(module->exports(), isolate); |
| DCHECK(exports->Lookup(name).IsTheHole(isolate)); |
| exports = ObjectHashTable::Put(exports, name, entry); |
| module->set_exports(*exports); |
| } |
| |
| void SourceTextModule::CreateExport(Isolate* isolate, |
| Handle<SourceTextModule> module, |
| int cell_index, Handle<FixedArray> names) { |
| DCHECK_LT(0, names->length()); |
| Handle<Cell> cell = |
| isolate->factory()->NewCell(isolate->factory()->undefined_value()); |
| module->regular_exports().set(ExportIndex(cell_index), *cell); |
| |
| Handle<ObjectHashTable> exports(module->exports(), isolate); |
| for (int i = 0, n = names->length(); i < n; ++i) { |
| Handle<String> name(String::cast(names->get(i)), isolate); |
| DCHECK(exports->Lookup(name).IsTheHole(isolate)); |
| exports = ObjectHashTable::Put(exports, name, cell); |
| } |
| module->set_exports(*exports); |
| } |
| |
| Cell SourceTextModule::GetCell(int cell_index) { |
| DisallowHeapAllocation no_gc; |
| Object cell; |
| switch (SourceTextModuleDescriptor::GetCellIndexKind(cell_index)) { |
| case SourceTextModuleDescriptor::kImport: |
| cell = regular_imports().get(ImportIndex(cell_index)); |
| break; |
| case SourceTextModuleDescriptor::kExport: |
| cell = regular_exports().get(ExportIndex(cell_index)); |
| break; |
| case SourceTextModuleDescriptor::kInvalid: |
| UNREACHABLE(); |
| break; |
| } |
| return Cell::cast(cell); |
| } |
| |
| Handle<Object> SourceTextModule::LoadVariable(Isolate* isolate, |
| Handle<SourceTextModule> module, |
| int cell_index) { |
| return handle(module->GetCell(cell_index).value(), isolate); |
| } |
| |
| void SourceTextModule::StoreVariable(Handle<SourceTextModule> module, |
| int cell_index, Handle<Object> value) { |
| DisallowHeapAllocation no_gc; |
| DCHECK_EQ(SourceTextModuleDescriptor::GetCellIndexKind(cell_index), |
| SourceTextModuleDescriptor::kExport); |
| module->GetCell(cell_index).set_value(*value); |
| } |
| |
| MaybeHandle<Cell> SourceTextModule::ResolveExport( |
| Isolate* isolate, Handle<SourceTextModule> module, |
| Handle<String> module_specifier, Handle<String> export_name, |
| MessageLocation loc, bool must_resolve, Module::ResolveSet* resolve_set) { |
| Handle<Object> object(module->exports().Lookup(export_name), isolate); |
| if (object->IsCell()) { |
| // Already resolved (e.g. because it's a local export). |
| return Handle<Cell>::cast(object); |
| } |
| |
| // Check for cycle before recursing. |
| { |
| // Attempt insertion with a null string set. |
| auto result = resolve_set->insert({module, nullptr}); |
| UnorderedStringSet*& name_set = result.first->second; |
| if (result.second) { |
| // |module| wasn't in the map previously, so allocate a new name set. |
| Zone* zone = resolve_set->zone(); |
| name_set = zone->New<UnorderedStringSet>(zone); |
| } else if (name_set->count(export_name)) { |
| // Cycle detected. |
| if (must_resolve) { |
| return isolate->ThrowAt<Cell>( |
| isolate->factory()->NewSyntaxError( |
| MessageTemplate::kCyclicModuleDependency, export_name, |
| module_specifier), |
| &loc); |
| } |
| return MaybeHandle<Cell>(); |
| } |
| name_set->insert(export_name); |
| } |
| |
| if (object->IsSourceTextModuleInfoEntry()) { |
| // Not yet resolved indirect export. |
| Handle<SourceTextModuleInfoEntry> entry = |
| Handle<SourceTextModuleInfoEntry>::cast(object); |
| Handle<String> import_name(String::cast(entry->import_name()), isolate); |
| Handle<Script> script(module->script(), isolate); |
| MessageLocation new_loc(script, entry->beg_pos(), entry->end_pos()); |
| |
| Handle<Cell> cell; |
| if (!ResolveImport(isolate, module, import_name, entry->module_request(), |
| new_loc, true, resolve_set) |
| .ToHandle(&cell)) { |
| DCHECK(isolate->has_pending_exception()); |
| return MaybeHandle<Cell>(); |
| } |
| |
| // The export table may have changed but the entry in question should be |
| // unchanged. |
| Handle<ObjectHashTable> exports(module->exports(), isolate); |
| DCHECK(exports->Lookup(export_name).IsSourceTextModuleInfoEntry()); |
| |
| exports = ObjectHashTable::Put(exports, export_name, cell); |
| module->set_exports(*exports); |
| return cell; |
| } |
| |
| DCHECK(object->IsTheHole(isolate)); |
| return SourceTextModule::ResolveExportUsingStarExports( |
| isolate, module, module_specifier, export_name, loc, must_resolve, |
| resolve_set); |
| } |
| |
| MaybeHandle<Cell> SourceTextModule::ResolveImport( |
| Isolate* isolate, Handle<SourceTextModule> module, Handle<String> name, |
| int module_request_index, MessageLocation loc, bool must_resolve, |
| Module::ResolveSet* resolve_set) { |
| Handle<Module> requested_module( |
| Module::cast(module->requested_modules().get(module_request_index)), |
| isolate); |
| Handle<ModuleRequest> module_request( |
| ModuleRequest::cast( |
| module->info().module_requests().get(module_request_index)), |
| isolate); |
| Handle<String> module_specifier(String::cast(module_request->specifier()), |
| isolate); |
| MaybeHandle<Cell> result = |
| Module::ResolveExport(isolate, requested_module, module_specifier, name, |
| loc, must_resolve, resolve_set); |
| DCHECK_IMPLIES(isolate->has_pending_exception(), result.is_null()); |
| return result; |
| } |
| |
| MaybeHandle<Cell> SourceTextModule::ResolveExportUsingStarExports( |
| Isolate* isolate, Handle<SourceTextModule> module, |
| Handle<String> module_specifier, Handle<String> export_name, |
| MessageLocation loc, bool must_resolve, Module::ResolveSet* resolve_set) { |
| if (!export_name->Equals(ReadOnlyRoots(isolate).default_string())) { |
| // Go through all star exports looking for the given name. If multiple star |
| // exports provide the name, make sure they all map it to the same cell. |
| Handle<Cell> unique_cell; |
| Handle<FixedArray> special_exports(module->info().special_exports(), |
| isolate); |
| for (int i = 0, n = special_exports->length(); i < n; ++i) { |
| i::Handle<i::SourceTextModuleInfoEntry> entry( |
| i::SourceTextModuleInfoEntry::cast(special_exports->get(i)), isolate); |
| if (!entry->export_name().IsUndefined(isolate)) { |
| continue; // Indirect export. |
| } |
| |
| Handle<Script> script(module->script(), isolate); |
| MessageLocation new_loc(script, entry->beg_pos(), entry->end_pos()); |
| |
| Handle<Cell> cell; |
| if (ResolveImport(isolate, module, export_name, entry->module_request(), |
| new_loc, false, resolve_set) |
| .ToHandle(&cell)) { |
| if (unique_cell.is_null()) unique_cell = cell; |
| if (*unique_cell != *cell) { |
| return isolate->ThrowAt<Cell>(isolate->factory()->NewSyntaxError( |
| MessageTemplate::kAmbiguousExport, |
| module_specifier, export_name), |
| &loc); |
| } |
| } else if (isolate->has_pending_exception()) { |
| return MaybeHandle<Cell>(); |
| } |
| } |
| |
| if (!unique_cell.is_null()) { |
| // Found a unique star export for this name. |
| Handle<ObjectHashTable> exports(module->exports(), isolate); |
| DCHECK(exports->Lookup(export_name).IsTheHole(isolate)); |
| exports = ObjectHashTable::Put(exports, export_name, unique_cell); |
| module->set_exports(*exports); |
| return unique_cell; |
| } |
| } |
| |
| // Unresolvable. |
| if (must_resolve) { |
| return isolate->ThrowAt<Cell>( |
| isolate->factory()->NewSyntaxError(MessageTemplate::kUnresolvableExport, |
| module_specifier, export_name), |
| &loc); |
| } |
| return MaybeHandle<Cell>(); |
| } |
| |
| bool SourceTextModule::PrepareInstantiate( |
| Isolate* isolate, Handle<SourceTextModule> module, |
| v8::Local<v8::Context> context, v8::Module::ResolveCallback callback) { |
| // Obtain requested modules. |
| Handle<SourceTextModuleInfo> module_info(module->info(), isolate); |
| Handle<FixedArray> module_requests(module_info->module_requests(), isolate); |
| Handle<FixedArray> requested_modules(module->requested_modules(), isolate); |
| for (int i = 0, length = module_requests->length(); i < length; ++i) { |
| Handle<ModuleRequest> module_request( |
| ModuleRequest::cast(module_requests->get(i)), isolate); |
| Handle<String> specifier(module_request->specifier(), isolate); |
| // TODO(v8:10958) Pass import assertions to the callback |
| v8::Local<v8::Module> api_requested_module; |
| if (!callback(context, v8::Utils::ToLocal(specifier), |
| v8::Utils::ToLocal(Handle<Module>::cast(module))) |
| .ToLocal(&api_requested_module)) { |
| isolate->PromoteScheduledException(); |
| return false; |
| } |
| Handle<Module> requested_module = Utils::OpenHandle(*api_requested_module); |
| requested_modules->set(i, *requested_module); |
| } |
| |
| // Recurse. |
| for (int i = 0, length = requested_modules->length(); i < length; ++i) { |
| Handle<Module> requested_module(Module::cast(requested_modules->get(i)), |
| isolate); |
| if (!Module::PrepareInstantiate(isolate, requested_module, context, |
| callback)) { |
| return false; |
| } |
| } |
| |
| // Set up local exports. |
| // TODO(neis): Create regular_exports array here instead of in factory method? |
| for (int i = 0, n = module_info->RegularExportCount(); i < n; ++i) { |
| int cell_index = module_info->RegularExportCellIndex(i); |
| Handle<FixedArray> export_names(module_info->RegularExportExportNames(i), |
| isolate); |
| CreateExport(isolate, module, cell_index, export_names); |
| } |
| |
| // Partially set up indirect exports. |
| // For each indirect export, we create the appropriate slot in the export |
| // table and store its SourceTextModuleInfoEntry there. When we later find |
| // the correct Cell in the module that actually provides the value, we replace |
| // the SourceTextModuleInfoEntry by that Cell (see ResolveExport). |
| Handle<FixedArray> special_exports(module_info->special_exports(), isolate); |
| for (int i = 0, n = special_exports->length(); i < n; ++i) { |
| Handle<SourceTextModuleInfoEntry> entry( |
| SourceTextModuleInfoEntry::cast(special_exports->get(i)), isolate); |
| Handle<Object> export_name(entry->export_name(), isolate); |
| if (export_name->IsUndefined(isolate)) continue; // Star export. |
| CreateIndirectExport(isolate, module, Handle<String>::cast(export_name), |
| entry); |
| } |
| |
| DCHECK_EQ(module->status(), kPreInstantiating); |
| return true; |
| } |
| |
| bool SourceTextModule::RunInitializationCode(Isolate* isolate, |
| Handle<SourceTextModule> module) { |
| DCHECK_EQ(module->status(), kInstantiating); |
| Handle<JSFunction> function(JSFunction::cast(module->code()), isolate); |
| DCHECK_EQ(MODULE_SCOPE, function->shared().scope_info().scope_type()); |
| Handle<Object> receiver = isolate->factory()->undefined_value(); |
| |
| Handle<ScopeInfo> scope_info(function->shared().scope_info(), isolate); |
| Handle<Context> context = isolate->factory()->NewModuleContext( |
| module, isolate->native_context(), scope_info); |
| function->set_context(*context); |
| |
| MaybeHandle<Object> maybe_generator = |
| Execution::Call(isolate, function, receiver, 0, {}); |
| Handle<Object> generator; |
| if (!maybe_generator.ToHandle(&generator)) { |
| DCHECK(isolate->has_pending_exception()); |
| return false; |
| } |
| DCHECK_EQ(*function, Handle<JSGeneratorObject>::cast(generator)->function()); |
| module->set_code(JSGeneratorObject::cast(*generator)); |
| return true; |
| } |
| |
| bool SourceTextModule::MaybeTransitionComponent( |
| Isolate* isolate, Handle<SourceTextModule> module, |
| ZoneForwardList<Handle<SourceTextModule>>* stack, Status new_status) { |
| DCHECK(new_status == kInstantiated || new_status == kEvaluated); |
| SLOW_DCHECK( |
| // {module} is on the {stack}. |
| std::count_if(stack->begin(), stack->end(), |
| [&](Handle<Module> m) { return *m == *module; }) == 1); |
| DCHECK_LE(module->dfs_ancestor_index(), module->dfs_index()); |
| if (module->dfs_ancestor_index() == module->dfs_index()) { |
| // This is the root of its strongly connected component. |
| Handle<SourceTextModule> ancestor; |
| do { |
| ancestor = stack->front(); |
| stack->pop_front(); |
| DCHECK_EQ(ancestor->status(), |
| new_status == kInstantiated ? kInstantiating : kEvaluating); |
| if (new_status == kInstantiated) { |
| if (!SourceTextModule::RunInitializationCode(isolate, ancestor)) |
| return false; |
| } |
| ancestor->SetStatus(new_status); |
| } while (*ancestor != *module); |
| } |
| return true; |
| } |
| |
| bool SourceTextModule::FinishInstantiate( |
| Isolate* isolate, Handle<SourceTextModule> module, |
| ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index, |
| Zone* zone) { |
| // Instantiate SharedFunctionInfo and mark module as instantiating for |
| // the recursion. |
| Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(module->code()), |
| isolate); |
| Handle<JSFunction> function = |
| isolate->factory()->NewFunctionFromSharedFunctionInfo( |
| shared, isolate->native_context()); |
| module->set_code(*function); |
| module->SetStatus(kInstantiating); |
| module->set_dfs_index(*dfs_index); |
| module->set_dfs_ancestor_index(*dfs_index); |
| stack->push_front(module); |
| (*dfs_index)++; |
| |
| // Recurse. |
| Handle<FixedArray> requested_modules(module->requested_modules(), isolate); |
| for (int i = 0, length = requested_modules->length(); i < length; ++i) { |
| Handle<Module> requested_module(Module::cast(requested_modules->get(i)), |
| isolate); |
| if (!Module::FinishInstantiate(isolate, requested_module, stack, dfs_index, |
| zone)) { |
| return false; |
| } |
| |
| DCHECK_NE(requested_module->status(), kEvaluating); |
| DCHECK_GE(requested_module->status(), kInstantiating); |
| SLOW_DCHECK( |
| // {requested_module} is instantiating iff it's on the {stack}. |
| (requested_module->status() == kInstantiating) == |
| std::count_if(stack->begin(), stack->end(), [&](Handle<Module> m) { |
| return *m == *requested_module; |
| })); |
| |
| if (requested_module->status() == kInstantiating) { |
| // SyntheticModules go straight to kInstantiated so this must be a |
| // SourceTextModule |
| module->set_dfs_ancestor_index(std::min( |
| module->dfs_ancestor_index(), |
| SourceTextModule::cast(*requested_module).dfs_ancestor_index())); |
| } |
| } |
| |
| Handle<Script> script(module->script(), isolate); |
| Handle<SourceTextModuleInfo> module_info(module->info(), isolate); |
| |
| // Resolve imports. |
| Handle<FixedArray> regular_imports(module_info->regular_imports(), isolate); |
| for (int i = 0, n = regular_imports->length(); i < n; ++i) { |
| Handle<SourceTextModuleInfoEntry> entry( |
| SourceTextModuleInfoEntry::cast(regular_imports->get(i)), isolate); |
| Handle<String> name(String::cast(entry->import_name()), isolate); |
| MessageLocation loc(script, entry->beg_pos(), entry->end_pos()); |
| ResolveSet resolve_set(zone); |
| Handle<Cell> cell; |
| if (!ResolveImport(isolate, module, name, entry->module_request(), loc, |
| true, &resolve_set) |
| .ToHandle(&cell)) { |
| return false; |
| } |
| module->regular_imports().set(ImportIndex(entry->cell_index()), *cell); |
| } |
| |
| // Resolve indirect exports. |
| Handle<FixedArray> special_exports(module_info->special_exports(), isolate); |
| for (int i = 0, n = special_exports->length(); i < n; ++i) { |
| Handle<SourceTextModuleInfoEntry> entry( |
| SourceTextModuleInfoEntry::cast(special_exports->get(i)), isolate); |
| Handle<Object> name(entry->export_name(), isolate); |
| if (name->IsUndefined(isolate)) continue; // Star export. |
| MessageLocation loc(script, entry->beg_pos(), entry->end_pos()); |
| ResolveSet resolve_set(zone); |
| if (ResolveExport(isolate, module, Handle<String>(), |
| Handle<String>::cast(name), loc, true, &resolve_set) |
| .is_null()) { |
| return false; |
| } |
| } |
| |
| return MaybeTransitionComponent(isolate, module, stack, kInstantiated); |
| } |
| |
| void SourceTextModule::FetchStarExports(Isolate* isolate, |
| Handle<SourceTextModule> module, |
| Zone* zone, |
| UnorderedModuleSet* visited) { |
| DCHECK_GE(module->status(), Module::kInstantiating); |
| |
| if (module->module_namespace().IsJSModuleNamespace()) return; // Shortcut. |
| |
| bool cycle = !visited->insert(module).second; |
| if (cycle) return; |
| Handle<ObjectHashTable> exports(module->exports(), isolate); |
| UnorderedStringMap more_exports(zone); |
| |
| // TODO(neis): Only allocate more_exports if there are star exports. |
| // Maybe split special_exports into indirect_exports and star_exports. |
| |
| ReadOnlyRoots roots(isolate); |
| Handle<FixedArray> special_exports(module->info().special_exports(), isolate); |
| for (int i = 0, n = special_exports->length(); i < n; ++i) { |
| Handle<SourceTextModuleInfoEntry> entry( |
| SourceTextModuleInfoEntry::cast(special_exports->get(i)), isolate); |
| if (!entry->export_name().IsUndefined(roots)) { |
| continue; // Indirect export. |
| } |
| |
| Handle<Module> requested_module( |
| Module::cast(module->requested_modules().get(entry->module_request())), |
| isolate); |
| |
| // Recurse. |
| if (requested_module->IsSourceTextModule()) |
| FetchStarExports(isolate, |
| Handle<SourceTextModule>::cast(requested_module), zone, |
| visited); |
| |
| // Collect all of [requested_module]'s exports that must be added to |
| // [module]'s exports (i.e. to [exports]). We record these in |
| // [more_exports]. Ambiguities (conflicting exports) are marked by mapping |
| // the name to undefined instead of a Cell. |
| Handle<ObjectHashTable> requested_exports(requested_module->exports(), |
| isolate); |
| for (InternalIndex i : requested_exports->IterateEntries()) { |
| Object key; |
| if (!requested_exports->ToKey(roots, i, &key)) continue; |
| Handle<String> name(String::cast(key), isolate); |
| |
| if (name->Equals(roots.default_string())) continue; |
| if (!exports->Lookup(name).IsTheHole(roots)) continue; |
| |
| Handle<Cell> cell(Cell::cast(requested_exports->ValueAt(i)), isolate); |
| auto insert_result = more_exports.insert(std::make_pair(name, cell)); |
| if (!insert_result.second) { |
| auto it = insert_result.first; |
| if (*it->second == *cell || it->second->IsUndefined(roots)) { |
| // We already recorded this mapping before, or the name is already |
| // known to be ambiguous. In either case, there's nothing to do. |
| } else { |
| DCHECK(it->second->IsCell()); |
| // Different star exports provide different cells for this name, hence |
| // mark the name as ambiguous. |
| it->second = roots.undefined_value_handle(); |
| } |
| } |
| } |
| } |
| |
| // Copy [more_exports] into [exports]. |
| for (const auto& elem : more_exports) { |
| if (elem.second->IsUndefined(isolate)) continue; // Ambiguous export. |
| DCHECK(!elem.first->Equals(ReadOnlyRoots(isolate).default_string())); |
| DCHECK(elem.second->IsCell()); |
| exports = ObjectHashTable::Put(exports, elem.first, elem.second); |
| } |
| module->set_exports(*exports); |
| } |
| |
| Handle<JSModuleNamespace> SourceTextModule::GetModuleNamespace( |
| Isolate* isolate, Handle<SourceTextModule> module, int module_request) { |
| Handle<Module> requested_module( |
| Module::cast(module->requested_modules().get(module_request)), isolate); |
| return Module::GetModuleNamespace(isolate, requested_module); |
| } |
| |
| MaybeHandle<JSObject> SourceTextModule::GetImportMeta( |
| Isolate* isolate, Handle<SourceTextModule> module) { |
| Handle<HeapObject> import_meta(module->import_meta(), isolate); |
| if (import_meta->IsTheHole(isolate)) { |
| if (!isolate->RunHostInitializeImportMetaObjectCallback(module).ToHandle( |
| &import_meta)) { |
| return {}; |
| } |
| module->set_import_meta(*import_meta); |
| } |
| return Handle<JSObject>::cast(import_meta); |
| } |
| |
| MaybeHandle<Object> SourceTextModule::EvaluateMaybeAsync( |
| Isolate* isolate, Handle<SourceTextModule> module) { |
| // In the event of errored evaluation, return a rejected promise. |
| if (module->status() == kErrored) { |
| // If we have a top level capability we assume it has already been |
| // rejected, and return it here. Otherwise create a new promise and |
| // reject it with the module's exception. |
| if (module->top_level_capability().IsJSPromise()) { |
| Handle<JSPromise> top_level_capability( |
| JSPromise::cast(module->top_level_capability()), isolate); |
| DCHECK(top_level_capability->status() == Promise::kRejected && |
| top_level_capability->result() == module->exception()); |
| return top_level_capability; |
| } |
| Handle<JSPromise> capability = isolate->factory()->NewJSPromise(); |
| JSPromise::Reject(capability, handle(module->exception(), isolate)); |
| return capability; |
| } |
| |
| // Start of Evaluate () Concrete Method |
| // 2. Assert: module.[[Status]] is "linked" or "evaluated". |
| CHECK(module->status() == kInstantiated || module->status() == kEvaluated); |
| |
| // 3. If module.[[Status]] is "evaluated", set module to |
| // GetAsyncCycleRoot(module). |
| if (module->status() == kEvaluated) { |
| module = GetAsyncCycleRoot(isolate, module); |
| } |
| |
| // 4. If module.[[TopLevelCapability]] is not undefined, then |
| // a. Return module.[[TopLevelCapability]].[[Promise]]. |
| if (module->top_level_capability().IsJSPromise()) { |
| return handle(JSPromise::cast(module->top_level_capability()), isolate); |
| } |
| DCHECK(module->top_level_capability().IsUndefined()); |
| |
| // 6. Let capability be ! NewPromiseCapability(%Promise%). |
| Handle<JSPromise> capability = isolate->factory()->NewJSPromise(); |
| |
| // 7. Set module.[[TopLevelCapability]] to capability. |
| module->set_top_level_capability(*capability); |
| DCHECK(module->top_level_capability().IsJSPromise()); |
| |
| // 9. If result is an abrupt completion, then |
| Handle<Object> unused_result; |
| if (!Evaluate(isolate, module).ToHandle(&unused_result)) { |
| // If the exception was a termination exception, rejecting the promise |
| // would resume execution, and our API contract is to return an empty |
| // handle. The module's status should be set to kErrored and the |
| // exception field should be set to `null`. |
| if (!isolate->is_catchable_by_javascript(isolate->pending_exception())) { |
| DCHECK_EQ(module->status(), kErrored); |
| DCHECK_EQ(module->exception(), *isolate->factory()->null_value()); |
| return {}; |
| } |
| |
| // d. Perform ! Call(capability.[[Reject]], undefined, |
| // «result.[[Value]]»). |
| isolate->clear_pending_exception(); |
| JSPromise::Reject(capability, handle(module->exception(), isolate)); |
| } else { |
| // 10. Otherwise, |
| // a. Assert: module.[[Status]] is "evaluated"... |
| CHECK_EQ(module->status(), kEvaluated); |
| |
| // b. If module.[[AsyncEvaluating]] is false, then |
| if (!module->async_evaluating()) { |
| // i. Perform ! Call(capability.[[Resolve]], undefined, |
| // «undefined»). |
| JSPromise::Resolve(capability, isolate->factory()->undefined_value()) |
| .ToHandleChecked(); |
| } |
| } |
| |
| // 11. Return capability.[[Promise]]. |
| return capability; |
| } |
| |
| MaybeHandle<Object> SourceTextModule::Evaluate( |
| Isolate* isolate, Handle<SourceTextModule> module) { |
| // Evaluate () Concrete Method continued from EvaluateMaybeAsync. |
| CHECK(module->status() == kInstantiated || module->status() == kEvaluated); |
| |
| // 5. Let stack be a new empty List. |
| Zone zone(isolate->allocator(), ZONE_NAME); |
| ZoneForwardList<Handle<SourceTextModule>> stack(&zone); |
| unsigned dfs_index = 0; |
| |
| // 8. Let result be InnerModuleEvaluation(module, stack, 0). |
| // 9. If result is an abrupt completion, then |
| Handle<Object> result; |
| if (!InnerModuleEvaluation(isolate, module, &stack, &dfs_index) |
| .ToHandle(&result)) { |
| // a. For each Cyclic Module Record m in stack, do |
| for (auto& descendant : stack) { |
| // i. Assert: m.[[Status]] is "evaluating". |
| CHECK_EQ(descendant->status(), kEvaluating); |
| // ii. Set m.[[Status]] to "evaluated". |
| // iii. Set m.[[EvaluationError]] to result. |
| Module::RecordErrorUsingPendingException(isolate, descendant); |
| } |
| |
| #ifdef DEBUG |
| if (isolate->is_catchable_by_javascript(isolate->pending_exception())) { |
| CHECK_EQ(module->exception(), isolate->pending_exception()); |
| } else { |
| CHECK_EQ(module->exception(), *isolate->factory()->null_value()); |
| } |
| #endif // DEBUG |
| } else { |
| // 10. Otherwise, |
| // c. Assert: stack is empty. |
| DCHECK(stack.empty()); |
| } |
| return result; |
| } |
| |
| void SourceTextModule::AsyncModuleExecutionFulfilled( |
| Isolate* isolate, Handle<SourceTextModule> module) { |
| // 1. Assert: module.[[Status]] is "evaluated". |
| CHECK(module->status() == kEvaluated || module->status() == kErrored); |
| |
| // 2. If module.[[AsyncEvaluating]] is false, |
| if (!module->async_evaluating()) { |
| // a. Assert: module.[[EvaluationError]] is not undefined. |
| CHECK_EQ(module->status(), kErrored); |
| |
| // b. Return undefined. |
| return; |
| } |
| |
| // 3. Assert: module.[[EvaluationError]] is undefined. |
| CHECK_EQ(module->status(), kEvaluated); |
| |
| // 4. Set module.[[AsyncEvaluating]] to false. |
| module->set_async_evaluating(false); |
| |
| // 5. For each Module m of module.[[AsyncParentModules]], do |
| for (int i = 0; i < module->AsyncParentModuleCount(); i++) { |
| Handle<SourceTextModule> m = module->GetAsyncParentModule(isolate, i); |
| |
| // a. If module.[[DFSIndex]] is not equal to module.[[DFSAncestorIndex]], |
| // then |
| if (module->dfs_index() != module->dfs_ancestor_index()) { |
| // i. Assert: m.[[DFSAncestorIndex]] is equal to |
| // module.[[DFSAncestorIndex]]. |
| DCHECK_LE(m->dfs_ancestor_index(), module->dfs_ancestor_index()); |
| } |
| // b. Decrement m.[[PendingAsyncDependencies]] by 1. |
| m->DecrementPendingAsyncDependencies(); |
| |
| // c. If m.[[PendingAsyncDependencies]] is 0 and m.[[EvaluationError]] is |
| // undefined, then |
| if (!m->HasPendingAsyncDependencies() && m->status() == kEvaluated) { |
| // i. Assert: m.[[AsyncEvaluating]] is true. |
| DCHECK(m->async_evaluating()); |
| |
| // ii. Let cycleRoot be ! GetAsyncCycleRoot(m). |
| auto cycle_root = GetAsyncCycleRoot(isolate, m); |
| |
| // iii. If cycleRoot.[[EvaluationError]] is not undefined, |
| // return undefined. |
| if (cycle_root->status() == kErrored) { |
| return; |
| } |
| |
| // iv. If m.[[Async]] is true, then |
| if (m->async()) { |
| // 1. Perform ! ExecuteAsyncModule(m). |
| ExecuteAsyncModule(isolate, m); |
| } else { |
| // v. Otherwise, |
| // 1. Let result be m.ExecuteModule(). |
| // 2. If result is a normal completion, |
| Handle<Object> unused_result; |
| if (ExecuteModule(isolate, m).ToHandle(&unused_result)) { |
| // a. Perform ! AsyncModuleExecutionFulfilled(m). |
| AsyncModuleExecutionFulfilled(isolate, m); |
| } else { |
| // 3. Otherwise, |
| // a. Perform ! AsyncModuleExecutionRejected(m, |
| // result.[[Value]]). |
| Handle<Object> exception(isolate->pending_exception(), isolate); |
| isolate->clear_pending_exception(); |
| AsyncModuleExecutionRejected(isolate, m, exception); |
| } |
| } |
| } |
| } |
| |
| // 6. If module.[[TopLevelCapability]] is not undefined, then |
| if (!module->top_level_capability().IsUndefined(isolate)) { |
| // a. Assert: module.[[DFSIndex]] is equal to module.[[DFSAncestorIndex]]. |
| DCHECK_EQ(module->dfs_index(), module->dfs_ancestor_index()); |
| |
| // b. Perform ! Call(module.[[TopLevelCapability]].[[Resolve]], |
| // undefined, «undefined»). |
| Handle<JSPromise> capability( |
| JSPromise::cast(module->top_level_capability()), isolate); |
| JSPromise::Resolve(capability, isolate->factory()->undefined_value()) |
| .ToHandleChecked(); |
| } |
| |
| // 7. Return undefined. |
| } |
| |
| void SourceTextModule::AsyncModuleExecutionRejected( |
| Isolate* isolate, Handle<SourceTextModule> module, |
| Handle<Object> exception) { |
| DCHECK(isolate->is_catchable_by_javascript(*exception)); |
| |
| // 1. Assert: module.[[Status]] is "evaluated". |
| CHECK(module->status() == kEvaluated || module->status() == kErrored); |
| |
| // 2. If module.[[AsyncEvaluating]] is false, |
| if (!module->async_evaluating()) { |
| // a. Assert: module.[[EvaluationError]] is not undefined. |
| CHECK_EQ(module->status(), kErrored); |
| |
| // b. Return undefined. |
| return; |
| } |
| |
| // 4. Set module.[[EvaluationError]] to ThrowCompletion(error). |
| Module::RecordError(isolate, module, exception); |
| |
| // 5. Set module.[[AsyncEvaluating]] to false. |
| module->set_async_evaluating(false); |
| |
| // 6. For each Module m of module.[[AsyncParentModules]], do |
| for (int i = 0; i < module->AsyncParentModuleCount(); i++) { |
| Handle<SourceTextModule> m = module->GetAsyncParentModule(isolate, i); |
| |
| // a. If module.[[DFSIndex]] is not equal to module.[[DFSAncestorIndex]], |
| // then |
| if (module->dfs_index() != module->dfs_ancestor_index()) { |
| // i. Assert: m.[[DFSAncestorIndex]] is equal to |
| // module.[[DFSAncestorIndex]]. |
| DCHECK_EQ(m->dfs_ancestor_index(), module->dfs_ancestor_index()); |
| } |
| // b. Perform ! AsyncModuleExecutionRejected(m, error). |
| AsyncModuleExecutionRejected(isolate, m, exception); |
| } |
| |
| // 7. If module.[[TopLevelCapability]] is not undefined, then |
| if (!module->top_level_capability().IsUndefined(isolate)) { |
| // a. Assert: module.[[DFSIndex]] is equal to module.[[DFSAncestorIndex]]. |
| DCHECK(module->dfs_index() == module->dfs_ancestor_index()); |
| |
| // b. Perform ! Call(module.[[TopLevelCapability]].[[Reject]], |
| // undefined, «error»). |
| Handle<JSPromise> capability( |
| JSPromise::cast(module->top_level_capability()), isolate); |
| JSPromise::Reject(capability, exception); |
| } |
| |
| // 8. Return undefined. |
| } |
| |
| void SourceTextModule::ExecuteAsyncModule(Isolate* isolate, |
| Handle<SourceTextModule> module) { |
| // 1. Assert: module.[[Status]] is "evaluating" or "evaluated". |
| CHECK(module->status() == kEvaluating || module->status() == kEvaluated); |
| |
| // 2. Assert: module.[[Async]] is true. |
| DCHECK(module->async()); |
| |
| // 3. Set module.[[AsyncEvaluating]] to true. |
| module->set_async_evaluating(true); |
| |
| // 4. Let capability be ! NewPromiseCapability(%Promise%). |
| Handle<JSPromise> capability = isolate->factory()->NewJSPromise(); |
| |
| // 5. Let stepsFulfilled be the steps of a CallAsyncModuleFulfilled |
| Handle<JSFunction> steps_fulfilled( |
| isolate->native_context()->call_async_module_fulfilled(), isolate); |
| |
| ScopedVector<Handle<Object>> empty_argv(0); |
| |
| // 6. Let onFulfilled be CreateBuiltinFunction(stepsFulfilled, |
| // «[[Module]]»). |
| // 7. Set onFulfilled.[[Module]] to module. |
| Handle<JSBoundFunction> on_fulfilled = |
| isolate->factory() |
| ->NewJSBoundFunction(steps_fulfilled, module, empty_argv) |
| .ToHandleChecked(); |
| |
| // 8. Let stepsRejected be the steps of a CallAsyncModuleRejected. |
| Handle<JSFunction> steps_rejected( |
| isolate->native_context()->call_async_module_rejected(), isolate); |
| |
| // 9. Let onRejected be CreateBuiltinFunction(stepsRejected, «[[Module]]»). |
| // 10. Set onRejected.[[Module]] to module. |
| Handle<JSBoundFunction> on_rejected = |
| isolate->factory() |
| ->NewJSBoundFunction(steps_rejected, module, empty_argv) |
| .ToHandleChecked(); |
| |
| // 11. Perform ! PerformPromiseThen(capability.[[Promise]], |
| // onFulfilled, onRejected). |
| Handle<Object> argv[] = {on_fulfilled, on_rejected}; |
| Execution::CallBuiltin(isolate, isolate->promise_then(), capability, |
| arraysize(argv), argv) |
| .ToHandleChecked(); |
| |
| // 12. Perform ! module.ExecuteModule(capability). |
| // Note: In V8 we have broken module.ExecuteModule into |
| // ExecuteModule for synchronous module execution and |
| // InnerExecuteAsyncModule for asynchronous execution. |
| InnerExecuteAsyncModule(isolate, module, capability).ToHandleChecked(); |
| |
| // 13. Return. |
| } |
| |
| MaybeHandle<Object> SourceTextModule::InnerExecuteAsyncModule( |
| Isolate* isolate, Handle<SourceTextModule> module, |
| Handle<JSPromise> capability) { |
| // If we have an async module, then it has an associated |
| // JSAsyncFunctionObject, which we then evaluate with the passed in promise |
| // capability. |
| Handle<JSAsyncFunctionObject> async_function_object( |
| JSAsyncFunctionObject::cast(module->code()), isolate); |
| async_function_object->set_promise(*capability); |
| Handle<JSFunction> resume( |
| isolate->native_context()->async_module_evaluate_internal(), isolate); |
| Handle<Object> result; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result, |
| Execution::TryCall(isolate, resume, async_function_object, 0, nullptr, |
| Execution::MessageHandling::kKeepPending, nullptr, |
| false), |
| Object); |
| return result; |
| } |
| |
| MaybeHandle<Object> SourceTextModule::ExecuteModule( |
| Isolate* isolate, Handle<SourceTextModule> module) { |
| // Synchronous modules have an associated JSGeneratorObject. |
| Handle<JSGeneratorObject> generator(JSGeneratorObject::cast(module->code()), |
| isolate); |
| Handle<JSFunction> resume( |
| isolate->native_context()->generator_next_internal(), isolate); |
| Handle<Object> result; |
| |
| // With top_level_await, we need to catch any exceptions and reject |
| // the top level capability. |
| if (FLAG_harmony_top_level_await) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result, |
| Execution::TryCall(isolate, resume, generator, 0, nullptr, |
| Execution::MessageHandling::kKeepPending, nullptr, |
| false), |
| Object); |
| } else { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result, |
| Execution::Call(isolate, resume, generator, 0, nullptr), Object); |
| } |
| DCHECK(JSIteratorResult::cast(*result).done().BooleanValue(isolate)); |
| return handle(JSIteratorResult::cast(*result).value(), isolate); |
| } |
| |
| MaybeHandle<Object> SourceTextModule::InnerModuleEvaluation( |
| Isolate* isolate, Handle<SourceTextModule> module, |
| ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index) { |
| STACK_CHECK(isolate, MaybeHandle<Object>()); |
| |
| // InnerModuleEvaluation(module, stack, index) |
| // 2. If module.[[Status]] is "evaluated", then |
| // a. If module.[[EvaluationError]] is undefined, return index. |
| // (We return undefined instead) |
| if (module->status() == kEvaluated || module->status() == kEvaluating) { |
| return isolate->factory()->undefined_value(); |
| } |
| |
| // b. Otherwise return module.[[EvaluationError]]. |
| // (We throw on isolate and return a MaybeHandle<Object> |
| // instead) |
| if (module->status() == kErrored) { |
| isolate->Throw(module->exception()); |
| return MaybeHandle<Object>(); |
| } |
| |
| // 4. Assert: module.[[Status]] is "linked". |
| CHECK_EQ(module->status(), kInstantiated); |
| |
| // 5. Set module.[[Status]] to "evaluating". |
| module->SetStatus(kEvaluating); |
| |
| // 6. Set module.[[DFSIndex]] to index. |
| module->set_dfs_index(*dfs_index); |
| |
| // 7. Set module.[[DFSAncestorIndex]] to index. |
| module->set_dfs_ancestor_index(*dfs_index); |
| |
| // 8. Set module.[[PendingAsyncDependencies]] to 0. |
| DCHECK(!module->HasPendingAsyncDependencies()); |
| |
| // 9. Set module.[[AsyncParentModules]] to a new empty List. |
| Handle<ArrayList> async_parent_modules = ArrayList::New(isolate, 0); |
| module->set_async_parent_modules(*async_parent_modules); |
| |
| // 10. Set index to index + 1. |
| (*dfs_index)++; |
| |
| // 11. Append module to stack. |
| stack->push_front(module); |
| |
| // Recursion. |
| Handle<FixedArray> requested_modules(module->requested_modules(), isolate); |
| |
| // 12. For each String required that is an element of |
| // module.[[RequestedModules]], do |
| for (int i = 0, length = requested_modules->length(); i < length; ++i) { |
| Handle<Module> requested_module(Module::cast(requested_modules->get(i)), |
| isolate); |
| // d. If requiredModule is a Cyclic Module Record, then |
| if (requested_module->IsSourceTextModule()) { |
| Handle<SourceTextModule> required_module( |
| SourceTextModule::cast(*requested_module), isolate); |
| RETURN_ON_EXCEPTION( |
| isolate, |
| InnerModuleEvaluation(isolate, required_module, stack, dfs_index), |
| Object); |
| |
| // i. Assert: requiredModule.[[Status]] is either "evaluating" or |
| // "evaluated". |
| // (We also assert the module cannot be errored, because if it was |
| // we would have already returned from InnerModuleEvaluation) |
| CHECK_GE(required_module->status(), kEvaluating); |
| CHECK_NE(required_module->status(), kErrored); |
| |
| // ii. Assert: requiredModule.[[Status]] is "evaluating" if and |
| // only if requiredModule is in stack. |
| SLOW_DCHECK( |
| (requested_module->status() == kEvaluating) == |
| std::count_if(stack->begin(), stack->end(), [&](Handle<Module> m) { |
| return *m == *requested_module; |
| })); |
| |
| // iii. If requiredModule.[[Status]] is "evaluating", then |
| if (required_module->status() == kEvaluating) { |
| // 1. Set module.[[DFSAncestorIndex]] to |
| // min( |
| // module.[[DFSAncestorIndex]], |
| // requiredModule.[[DFSAncestorIndex]]). |
| module->set_dfs_ancestor_index( |
| std::min(module->dfs_ancestor_index(), |
| required_module->dfs_ancestor_index())); |
| } else { |
| // iv. Otherwise, |
| // 1. Set requiredModule to GetAsyncCycleRoot(requiredModule). |
| required_module = GetAsyncCycleRoot(isolate, required_module); |
| |
| // 2. Assert: requiredModule.[[Status]] is "evaluated". |
| CHECK_GE(required_module->status(), kEvaluated); |
| |
| // 3. If requiredModule.[[EvaluationError]] is not undefined, |
| // return module.[[EvaluationError]]. |
| // (If there was an exception on the original required module |
| // we would have already returned. This check handles the case |
| // where the AsyncCycleRoot has an error. Instead of returning |
| // the exception, we throw on isolate and return a |
| // MaybeHandle<Object>) |
| if (required_module->status() == kErrored) { |
| isolate->Throw(required_module->exception()); |
| return MaybeHandle<Object>(); |
| } |
| } |
| // v. If requiredModule.[[AsyncEvaluating]] is true, then |
| if (required_module->async_evaluating()) { |
| // 1. Set module.[[PendingAsyncDependencies]] to |
| // module.[[PendingAsyncDependencies]] + 1. |
| module->IncrementPendingAsyncDependencies(); |
| |
| // 2. Append module to requiredModule.[[AsyncParentModules]]. |
| AddAsyncParentModule(isolate, required_module, module); |
| } |
| } else { |
| RETURN_ON_EXCEPTION(isolate, Module::Evaluate(isolate, requested_module), |
| Object); |
| } |
| } |
| |
| // The spec returns the module index for proper numbering of dependencies. |
| // However, we pass the module index by pointer instead. |
| // |
| // Before async modules v8 returned the value result from calling next |
| // on the module's implicit iterator. We preserve this behavior for |
| // synchronous modules, but return undefined for AsyncModules. |
| Handle<Object> result = isolate->factory()->undefined_value(); |
| |
| // 14. If module.[[PendingAsyncDependencies]] is > 0, set |
| // module.[[AsyncEvaluating]] to true. |
| if (module->HasPendingAsyncDependencies()) { |
| module->set_async_evaluating(true); |
| } else if (module->async()) { |
| // 15. Otherwise, if module.[[Async]] is true, |
| // perform ! ExecuteAsyncModule(module). |
| SourceTextModule::ExecuteAsyncModule(isolate, module); |
| } else { |
| // 16. Otherwise, perform ? module.ExecuteModule(). |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, result, ExecuteModule(isolate, module), |
| Object); |
| } |
| |
| CHECK(MaybeTransitionComponent(isolate, module, stack, kEvaluated)); |
| return result; |
| } |
| |
| Handle<SourceTextModule> SourceTextModule::GetAsyncCycleRoot( |
| Isolate* isolate, Handle<SourceTextModule> module) { |
| // 1. Assert: module.[[Status]] is "evaluated". |
| CHECK_GE(module->status(), kEvaluated); |
| |
| // 2. If module.[[AsyncParentModules]] is an empty List, return module. |
| if (module->AsyncParentModuleCount() == 0) { |
| return module; |
| } |
| |
| // 3. Repeat, while module.[[DFSIndex]] is greater than |
| // module.[[DFSAncestorIndex]], |
| while (module->dfs_index() > module->dfs_ancestor_index()) { |
| // a. Assert: module.[[AsyncParentModules]] is a non-empty List. |
| DCHECK_GT(module->AsyncParentModuleCount(), 0); |
| |
| // b. Let nextCycleModule be the first element of |
| // module.[[AsyncParentModules]]. |
| Handle<SourceTextModule> next_cycle_module = |
| module->GetAsyncParentModule(isolate, 0); |
| |
| // c. Assert: nextCycleModule.[[DFSAncestorIndex]] is less than or equal |
| // to module.[[DFSAncestorIndex]]. |
| DCHECK_LE(next_cycle_module->dfs_ancestor_index(), |
| module->dfs_ancestor_index()); |
| |
| // d. Set module to nextCycleModule |
| module = next_cycle_module; |
| } |
| |
| // 4. Assert: module.[[DFSIndex]] is equal to module.[[DFSAncestorIndex]]. |
| DCHECK_EQ(module->dfs_index(), module->dfs_ancestor_index()); |
| |
| // 5. Return module. |
| return module; |
| } |
| |
| void SourceTextModule::Reset(Isolate* isolate, |
| Handle<SourceTextModule> module) { |
| Factory* factory = isolate->factory(); |
| |
| DCHECK(module->import_meta().IsTheHole(isolate)); |
| |
| Handle<FixedArray> regular_exports = |
| factory->NewFixedArray(module->regular_exports().length()); |
| Handle<FixedArray> regular_imports = |
| factory->NewFixedArray(module->regular_imports().length()); |
| Handle<FixedArray> requested_modules = |
| factory->NewFixedArray(module->requested_modules().length()); |
| |
| if (module->status() == kInstantiating) { |
| module->set_code(JSFunction::cast(module->code()).shared()); |
| } |
| module->set_regular_exports(*regular_exports); |
| module->set_regular_imports(*regular_imports); |
| module->set_requested_modules(*requested_modules); |
| module->set_dfs_index(-1); |
| module->set_dfs_ancestor_index(-1); |
| } |
| |
| } // namespace internal |
| } // namespace v8 |