mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 04:52:45 +00:00 
			
		
		
		
	LibJS: Follow the spec with storing im- and export entries
Because we can have arbitrary in- and export names with strings we can
have '*' and '' which means using '*' as an indicating namespace imports
failed / behaved incorrectly for string imports '*'.
We now use more specific types to indicate these special states instead
of these 'magic' string values.
Do note that 'default' is not actually a magic string value but one
specified by the spec. And you can in fact export the default value by
doing: `export { 1 as default }`.
			
			
This commit is contained in:
		
							parent
							
								
									8473f6caee
								
							
						
					
					
						commit
						e0e4ead2c8
					
				
					 11 changed files with 190 additions and 96 deletions
				
			
		|  | @ -4231,12 +4231,17 @@ void ExportStatement::dump(int indent) const | ||||||
| 
 | 
 | ||||||
|     for (auto& entry : m_entries) { |     for (auto& entry : m_entries) { | ||||||
|         print_indent(indent + 2); |         print_indent(indent + 2); | ||||||
|         out("ModuleRequest: {}", entry.module_request.module_specifier); |         out("ExportName: {}, ImportName: {}, LocalName: {}, ModuleRequest: ", | ||||||
|         dump_assert_clauses(entry.module_request); |             string_or_null(entry.export_name), | ||||||
|         outln(", ImportName: {}, LocalName: {}, ExportName: {}", |             entry.is_module_request() ? string_or_null(entry.local_or_import_name) : "null", | ||||||
|             entry.kind == ExportEntry::Kind::ModuleRequest ? string_or_null(entry.local_or_import_name) : "null", |             entry.is_module_request() ? "null" : string_or_null(entry.local_or_import_name)); | ||||||
|             entry.kind != ExportEntry::Kind::ModuleRequest ? string_or_null(entry.local_or_import_name) : "null", |         if (entry.is_module_request()) { | ||||||
|             string_or_null(entry.export_name)); |             out("{}", entry.m_module_request->module_specifier); | ||||||
|  |             dump_assert_clauses(*entry.m_module_request); | ||||||
|  |             outln(); | ||||||
|  |         } else { | ||||||
|  |             outln("null"); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (m_statement) { |     if (m_statement) { | ||||||
|  |  | ||||||
|  | @ -268,19 +268,18 @@ struct ModuleRequest { | ||||||
| 
 | 
 | ||||||
| class ImportStatement final : public Statement { | class ImportStatement final : public Statement { | ||||||
| public: | public: | ||||||
|  |     // ImportEntry Record, https://tc39.es/ecma262/#table-importentry-record-fields
 | ||||||
|     struct ImportEntry { |     struct ImportEntry { | ||||||
|         FlyString import_name; |         FlyString import_name;       // [[ImportName]] if a String
 | ||||||
|         FlyString local_name; |         FlyString local_name;        // [[LocalName]]
 | ||||||
|  |         bool is_namespace { false }; // [[ImportName]] if `namespace-object`
 | ||||||
| 
 | 
 | ||||||
|         ImportEntry(FlyString import_name_, FlyString local_name_) |         ImportEntry(FlyString import_name_, FlyString local_name_, bool is_namespace_ = false) | ||||||
|             : import_name(move(import_name_)) |             : import_name(move(import_name_)) | ||||||
|             , local_name(move(local_name_)) |             , local_name(move(local_name_)) | ||||||
|  |             , is_namespace(is_namespace_) | ||||||
|         { |         { | ||||||
|         } |             VERIFY(!is_namespace || import_name.is_null()); | ||||||
| 
 |  | ||||||
|         bool is_namespace() const |  | ||||||
|         { |  | ||||||
|             return import_name == "*"sv; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         ModuleRequest const& module_request() const |         ModuleRequest const& module_request() const | ||||||
|  | @ -291,7 +290,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     private: |     private: | ||||||
|         friend ImportStatement; |         friend ImportStatement; | ||||||
|         ModuleRequest* m_module_request; |         ModuleRequest* m_module_request; // [[ModuleRequest]]
 | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     explicit ImportStatement(SourceRange source_range, ModuleRequest from_module, Vector<ImportEntry> entries = {}) |     explicit ImportStatement(SourceRange source_range, ModuleRequest from_module, Vector<ImportEntry> entries = {}) | ||||||
|  | @ -320,52 +319,74 @@ class ExportStatement final : public Statement { | ||||||
| public: | public: | ||||||
|     static FlyString local_name_for_default; |     static FlyString local_name_for_default; | ||||||
| 
 | 
 | ||||||
|  |     // ExportEntry Record, https://tc39.es/ecma262/#table-exportentry-records
 | ||||||
|     struct ExportEntry { |     struct ExportEntry { | ||||||
|         enum class Kind { |         enum class Kind { | ||||||
|             ModuleRequest, |             NamedExport, | ||||||
|             LocalExport |             ModuleRequestAll, | ||||||
|  |             ModuleRequestAllButDefault, | ||||||
|         } kind; |         } kind; | ||||||
|         // Can always have
 |  | ||||||
|         FlyString export_name; |  | ||||||
| 
 | 
 | ||||||
|         // Only if module request
 |         FlyString export_name;          // [[ExportName]]
 | ||||||
|         ModuleRequest module_request; |         FlyString local_or_import_name; // Either [[ImportName]] or [[LocalName]]
 | ||||||
| 
 | 
 | ||||||
|         // Has just one of ones below
 |         ExportEntry(Kind export_kind, FlyString export_name_, FlyString local_or_import_name_) | ||||||
|         FlyString local_or_import_name; |             : kind(export_kind) | ||||||
| 
 |  | ||||||
|         ExportEntry(FlyString export_name, FlyString local_name) |  | ||||||
|             : kind(Kind::LocalExport) |  | ||||||
|             , export_name(move(export_name)) |  | ||||||
|             , local_or_import_name(move(local_name)) |  | ||||||
|         { |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         ExportEntry(ModuleRequest module_request_, FlyString import_name, FlyString export_name_) |  | ||||||
|             : kind(Kind::ModuleRequest) |  | ||||||
|             , export_name(move(export_name_)) |             , export_name(move(export_name_)) | ||||||
|             , module_request(move(module_request_)) |             , local_or_import_name(move(local_or_import_name_)) | ||||||
|             , local_or_import_name(move(import_name)) |  | ||||||
|         { |         { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         bool is_all_but_default() const |         bool is_module_request() const | ||||||
|         { |         { | ||||||
|             return kind == Kind::ModuleRequest && local_or_import_name == "*"sv && export_name.is_null(); |             return m_module_request != nullptr; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         bool is_all() const |         static ExportEntry indirect_export_entry(ModuleRequest const& module_request, FlyString export_name, FlyString import_name) | ||||||
|         { |         { | ||||||
|             return kind == Kind::ModuleRequest && local_or_import_name == "*"sv && !export_name.is_empty(); |             ExportEntry entry { Kind::NamedExport, move(export_name), move(import_name) }; | ||||||
|  |             entry.m_module_request = &module_request; | ||||||
|  |             return entry; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ModuleRequest const& module_request() const | ||||||
|  |         { | ||||||
|  |             VERIFY(m_module_request); | ||||||
|  |             return *m_module_request; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     private: | ||||||
|  |         ModuleRequest const* m_module_request { nullptr }; // [[ModuleRequest]]
 | ||||||
|  |         friend ExportStatement; | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         static ExportEntry named_export(FlyString export_name, FlyString local_name) | ||||||
|  |         { | ||||||
|  |             return ExportEntry { Kind::NamedExport, move(export_name), move(local_name) }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         static ExportEntry all_but_default_entry() | ||||||
|  |         { | ||||||
|  |             return ExportEntry { Kind::ModuleRequestAllButDefault, {}, {} }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         static ExportEntry all_module_request(FlyString export_name) | ||||||
|  |         { | ||||||
|  |             return ExportEntry { Kind::ModuleRequestAll, move(export_name), {} }; | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     explicit ExportStatement(SourceRange source_range, RefPtr<ASTNode> statement, Vector<ExportEntry> entries, bool is_default_export) |     ExportStatement(SourceRange source_range, RefPtr<ASTNode> statement, Vector<ExportEntry> entries, bool is_default_export, ModuleRequest module_request) | ||||||
|         : Statement(source_range) |         : Statement(source_range) | ||||||
|         , m_statement(move(statement)) |         , m_statement(move(statement)) | ||||||
|         , m_entries(move(entries)) |         , m_entries(move(entries)) | ||||||
|         , m_is_default_export(is_default_export) |         , m_is_default_export(is_default_export) | ||||||
|  |         , m_module_request(move(module_request)) | ||||||
|     { |     { | ||||||
|  |         if (!m_module_request.module_specifier.is_null()) { | ||||||
|  |             for (auto& entry : m_entries) | ||||||
|  |                 entry.m_module_request = &m_module_request; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     virtual Completion execute(Interpreter&, GlobalObject&) const override; |     virtual Completion execute(Interpreter&, GlobalObject&) const override; | ||||||
|  | @ -389,6 +410,7 @@ private: | ||||||
|     RefPtr<ASTNode> m_statement; |     RefPtr<ASTNode> m_statement; | ||||||
|     Vector<ExportEntry> m_entries; |     Vector<ExportEntry> m_entries; | ||||||
|     bool m_is_default_export { false }; |     bool m_is_default_export { false }; | ||||||
|  |     ModuleRequest m_module_request; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class Program final : public ScopeNode { | class Program final : public ScopeNode { | ||||||
|  |  | ||||||
|  | @ -544,7 +544,7 @@ void Parser::parse_module(Program& program) | ||||||
|         if (export_statement.has_statement()) |         if (export_statement.has_statement()) | ||||||
|             continue; |             continue; | ||||||
|         for (auto& entry : export_statement.entries()) { |         for (auto& entry : export_statement.entries()) { | ||||||
|             if (entry.kind == ExportStatement::ExportEntry::Kind::ModuleRequest) |             if (entry.is_module_request()) | ||||||
|                 return; |                 return; | ||||||
| 
 | 
 | ||||||
|             auto const& exported_name = entry.local_or_import_name; |             auto const& exported_name = entry.local_or_import_name; | ||||||
|  | @ -4058,7 +4058,6 @@ ModuleRequest Parser::parse_module_request() | ||||||
|     return request; |     return request; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static FlyString namespace_string_value = "*"; |  | ||||||
| static FlyString default_string_value = "default"; | static FlyString default_string_value = "default"; | ||||||
| 
 | 
 | ||||||
| NonnullRefPtr<ImportStatement> Parser::parse_import_statement(Program& program) | NonnullRefPtr<ImportStatement> Parser::parse_import_statement(Program& program) | ||||||
|  | @ -4133,7 +4132,7 @@ NonnullRefPtr<ImportStatement> Parser::parse_import_statement(Program& program) | ||||||
|         if (match_imported_binding()) { |         if (match_imported_binding()) { | ||||||
|             auto namespace_position = position(); |             auto namespace_position = position(); | ||||||
|             auto namespace_name = consume().value(); |             auto namespace_name = consume().value(); | ||||||
|             entries_with_location.append({ { namespace_string_value, namespace_name }, namespace_position }); |             entries_with_location.append({ ImportStatement::ImportEntry({}, namespace_name, true), namespace_position }); | ||||||
|         } else { |         } else { | ||||||
|             syntax_error(String::formatted("Unexpected token: {}", m_state.current_token.name())); |             syntax_error(String::formatted("Unexpected token: {}", m_state.current_token.name())); | ||||||
|         } |         } | ||||||
|  | @ -4223,6 +4222,8 @@ NonnullRefPtr<ImportStatement> Parser::parse_import_statement(Program& program) | ||||||
| 
 | 
 | ||||||
| NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program) | NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program) | ||||||
| { | { | ||||||
|  |     using ExportEntry = ExportStatement::ExportEntry; | ||||||
|  | 
 | ||||||
|     // We use the extended syntax which adds:
 |     // We use the extended syntax which adds:
 | ||||||
|     //  ExportDeclaration:
 |     //  ExportDeclaration:
 | ||||||
|     //      export ExportFromClause FromClause [no LineTerminator here] AssertClause ;
 |     //      export ExportFromClause FromClause [no LineTerminator here] AssertClause ;
 | ||||||
|  | @ -4247,21 +4248,15 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program) | ||||||
|     consume(TokenType::Export); |     consume(TokenType::Export); | ||||||
| 
 | 
 | ||||||
|     struct EntryAndLocation { |     struct EntryAndLocation { | ||||||
|         ExportStatement::ExportEntry entry; |         ExportEntry entry; | ||||||
|         Position position; |         Position position; | ||||||
| 
 |  | ||||||
|         void to_module_request(ModuleRequest from_module) |  | ||||||
|         { |  | ||||||
|             entry.kind = ExportStatement::ExportEntry::Kind::ModuleRequest; |  | ||||||
|             entry.module_request = move(from_module); |  | ||||||
|         } |  | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     Vector<EntryAndLocation> entries_with_location; |     Vector<EntryAndLocation> entries_with_location; | ||||||
| 
 | 
 | ||||||
|     RefPtr<ASTNode> expression = {}; |     RefPtr<ASTNode> expression = {}; | ||||||
| 
 |  | ||||||
|     bool is_default = false; |     bool is_default = false; | ||||||
|  |     ModuleRequest from_specifier; | ||||||
| 
 | 
 | ||||||
|     if (match_default()) { |     if (match_default()) { | ||||||
|         is_default = true; |         is_default = true; | ||||||
|  | @ -4353,7 +4348,7 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program) | ||||||
|             local_name = ExportStatement::local_name_for_default; |             local_name = ExportStatement::local_name_for_default; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         entries_with_location.append({ { default_string_value, move(local_name) }, default_position }); |         entries_with_location.append({ ExportEntry::named_export(default_string_value, move(local_name)), default_position }); | ||||||
|     } else { |     } else { | ||||||
|         enum FromSpecifier { |         enum FromSpecifier { | ||||||
|             NotAllowed, |             NotAllowed, | ||||||
|  | @ -4370,12 +4365,12 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program) | ||||||
|                 if (match_identifier_name()) { |                 if (match_identifier_name()) { | ||||||
|                     auto namespace_position = position(); |                     auto namespace_position = position(); | ||||||
|                     auto exported_name = consume().value(); |                     auto exported_name = consume().value(); | ||||||
|                     entries_with_location.append({ { exported_name, namespace_string_value }, namespace_position }); |                     entries_with_location.append({ ExportEntry::all_module_request(exported_name), namespace_position }); | ||||||
|                 } else { |                 } else { | ||||||
|                     expected("identifier"); |                     expected("identifier"); | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 entries_with_location.append({ { {}, namespace_string_value }, asterisk_position }); |                 entries_with_location.append({ ExportEntry::all_but_default_entry(), asterisk_position }); | ||||||
|             } |             } | ||||||
|             check_for_from = Required; |             check_for_from = Required; | ||||||
|         } else if (match_declaration()) { |         } else if (match_declaration()) { | ||||||
|  | @ -4384,10 +4379,10 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program) | ||||||
|             m_state.current_scope_pusher->add_declaration(declaration); |             m_state.current_scope_pusher->add_declaration(declaration); | ||||||
|             if (is<FunctionDeclaration>(*declaration)) { |             if (is<FunctionDeclaration>(*declaration)) { | ||||||
|                 auto& func = static_cast<FunctionDeclaration&>(*declaration); |                 auto& func = static_cast<FunctionDeclaration&>(*declaration); | ||||||
|                 entries_with_location.append({ { func.name(), func.name() }, func.source_range().start }); |                 entries_with_location.append({ ExportEntry::named_export(func.name(), func.name()), func.source_range().start }); | ||||||
|             } else if (is<ClassDeclaration>(*declaration)) { |             } else if (is<ClassDeclaration>(*declaration)) { | ||||||
|                 auto& class_declaration = static_cast<ClassDeclaration&>(*declaration); |                 auto& class_declaration = static_cast<ClassDeclaration&>(*declaration); | ||||||
|                 entries_with_location.append({ { class_declaration.name(), class_declaration.name() }, class_declaration.source_range().start }); |                 entries_with_location.append({ ExportEntry::named_export(class_declaration.name(), class_declaration.name()), class_declaration.source_range().start }); | ||||||
|             } else { |             } else { | ||||||
|                 VERIFY(is<VariableDeclaration>(*declaration)); |                 VERIFY(is<VariableDeclaration>(*declaration)); | ||||||
|                 auto& variables = static_cast<VariableDeclaration&>(*declaration); |                 auto& variables = static_cast<VariableDeclaration&>(*declaration); | ||||||
|  | @ -4395,11 +4390,11 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program) | ||||||
|                 for (auto& decl : variables.declarations()) { |                 for (auto& decl : variables.declarations()) { | ||||||
|                     decl.target().visit( |                     decl.target().visit( | ||||||
|                         [&](NonnullRefPtr<Identifier> const& identifier) { |                         [&](NonnullRefPtr<Identifier> const& identifier) { | ||||||
|                             entries_with_location.append({ { identifier->string(), identifier->string() }, identifier->source_range().start }); |                             entries_with_location.append({ ExportEntry::named_export(identifier->string(), identifier->string()), identifier->source_range().start }); | ||||||
|                         }, |                         }, | ||||||
|                         [&](NonnullRefPtr<BindingPattern> const& binding) { |                         [&](NonnullRefPtr<BindingPattern> const& binding) { | ||||||
|                             binding->for_each_bound_name([&](auto& name) { |                             binding->for_each_bound_name([&](auto& name) { | ||||||
|                                 entries_with_location.append({ { name, name }, decl_position }); |                                 entries_with_location.append({ ExportEntry::named_export(name, name), decl_position }); | ||||||
|                             }); |                             }); | ||||||
|                         }); |                         }); | ||||||
|                 } |                 } | ||||||
|  | @ -4412,11 +4407,11 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program) | ||||||
|             for (auto& decl : variable_declaration->declarations()) { |             for (auto& decl : variable_declaration->declarations()) { | ||||||
|                 decl.target().visit( |                 decl.target().visit( | ||||||
|                     [&](NonnullRefPtr<Identifier> const& identifier) { |                     [&](NonnullRefPtr<Identifier> const& identifier) { | ||||||
|                         entries_with_location.append({ { identifier->string(), identifier->string() }, identifier->source_range().start }); |                         entries_with_location.append({ ExportEntry::named_export(identifier->string(), identifier->string()), identifier->source_range().start }); | ||||||
|                     }, |                     }, | ||||||
|                     [&](NonnullRefPtr<BindingPattern> const& binding) { |                     [&](NonnullRefPtr<BindingPattern> const& binding) { | ||||||
|                         binding->for_each_bound_name([&](auto& name) { |                         binding->for_each_bound_name([&](auto& name) { | ||||||
|                             entries_with_location.append({ { name, name }, variable_position }); |                             entries_with_location.append({ ExportEntry::named_export(name, name), variable_position }); | ||||||
|                         }); |                         }); | ||||||
|                     }); |                     }); | ||||||
|             } |             } | ||||||
|  | @ -4444,18 +4439,13 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program) | ||||||
|                 auto identifier_position = position(); |                 auto identifier_position = position(); | ||||||
|                 auto identifier = parse_export_specifier(true); |                 auto identifier = parse_export_specifier(true); | ||||||
| 
 | 
 | ||||||
|                 if (identifier.is_empty()) |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 if (match_as()) { |                 if (match_as()) { | ||||||
|                     consume(TokenType::Identifier); |                     consume(TokenType::Identifier); | ||||||
|                     auto export_name = parse_export_specifier(false); |                     auto export_name = parse_export_specifier(false); | ||||||
|                     if (export_name.is_empty()) |  | ||||||
|                         break; |  | ||||||
| 
 | 
 | ||||||
|                     entries_with_location.append({ { move(export_name), move(identifier) }, identifier_position }); |                     entries_with_location.append({ ExportEntry::named_export(move(export_name), move(identifier)), identifier_position }); | ||||||
|                 } else { |                 } else { | ||||||
|                     entries_with_location.append({ { identifier, identifier }, identifier_position }); |                     entries_with_location.append({ ExportEntry::named_export(identifier, identifier), identifier_position }); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 if (!match(TokenType::Comma)) |                 if (!match(TokenType::Comma)) | ||||||
|  | @ -4472,12 +4462,7 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program) | ||||||
| 
 | 
 | ||||||
|         if (check_for_from != NotAllowed && match_from()) { |         if (check_for_from != NotAllowed && match_from()) { | ||||||
|             consume(TokenType::Identifier); |             consume(TokenType::Identifier); | ||||||
|             auto from_specifier = parse_module_request(); |             from_specifier = parse_module_request(); | ||||||
| 
 |  | ||||||
|             // FIXME: We can probably store only one module request
 |  | ||||||
|             //        per ExportStatement like we do with ImportStatement.
 |  | ||||||
|             for (auto& entry : entries_with_location) |  | ||||||
|                 entry.to_module_request(from_specifier); |  | ||||||
|         } else if (check_for_from == Required) { |         } else if (check_for_from == Required) { | ||||||
|             expected("from"); |             expected("from"); | ||||||
|         } |         } | ||||||
|  | @ -4503,6 +4488,6 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program) | ||||||
|         entries.append(move(entry.entry)); |         entries.append(move(entry.entry)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return create_ast_node<ExportStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression), move(entries), is_default); |     return create_ast_node<ExportStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression), move(entries), is_default, move(from_specifier)); | ||||||
| } | } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -37,9 +37,9 @@ static Vector<FlyString> module_requests(Program const& program) | ||||||
| 
 | 
 | ||||||
|     for (auto const& export_statement : program.exports()) { |     for (auto const& export_statement : program.exports()) { | ||||||
|         for (auto const& export_entry : export_statement.entries()) { |         for (auto const& export_entry : export_statement.entries()) { | ||||||
|             if (export_entry.kind != ExportStatement::ExportEntry::Kind::ModuleRequest) |             if (!export_entry.is_module_request()) | ||||||
|                 continue; |                 continue; | ||||||
|             requested_modules_with_indices.append({ export_entry.module_request.module_specifier.view(), |             requested_modules_with_indices.append({ export_entry.module_request().module_specifier.view(), | ||||||
|                 export_statement.source_range().start.offset }); |                 export_statement.source_range().start.offset }); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -115,7 +115,8 @@ Result<NonnullRefPtr<SourceTextModule>, Vector<Parser::Error>> SourceTextModule: | ||||||
|             VERIFY(export_statement.has_statement()); |             VERIFY(export_statement.has_statement()); | ||||||
| 
 | 
 | ||||||
|             auto const& entry = export_statement.entries()[0]; |             auto const& entry = export_statement.entries()[0]; | ||||||
|             VERIFY(entry.kind == ExportStatement::ExportEntry::Kind::LocalExport); |             VERIFY(entry.kind == ExportStatement::ExportEntry::Kind::NamedExport); | ||||||
|  |             VERIFY(!entry.is_module_request()); | ||||||
|             VERIFY(import_entries.find_if( |             VERIFY(import_entries.find_if( | ||||||
|                                      [&](ImportEntry const& import_entry) { |                                      [&](ImportEntry const& import_entry) { | ||||||
|                                          return import_entry.local_name == entry.local_or_import_name; |                                          return import_entry.local_name == entry.local_or_import_name; | ||||||
|  | @ -127,7 +128,7 @@ Result<NonnullRefPtr<SourceTextModule>, Vector<Parser::Error>> SourceTextModule: | ||||||
|         for (auto const& export_entry : export_statement.entries()) { |         for (auto const& export_entry : export_statement.entries()) { | ||||||
| 
 | 
 | ||||||
|             // a. If ee.[[ModuleRequest]] is null, then
 |             // a. If ee.[[ModuleRequest]] is null, then
 | ||||||
|             if (export_entry.kind == ExportStatement::ExportEntry::Kind::LocalExport) { |             if (!export_entry.is_module_request()) { | ||||||
| 
 | 
 | ||||||
|                 auto in_imported_bound_names = import_entries.find_if( |                 auto in_imported_bound_names = import_entries.find_if( | ||||||
|                     [&](ImportEntry const& import_entry) { |                     [&](ImportEntry const& import_entry) { | ||||||
|  | @ -145,8 +146,10 @@ Result<NonnullRefPtr<SourceTextModule>, Vector<Parser::Error>> SourceTextModule: | ||||||
|                     auto& import_entry = *in_imported_bound_names; |                     auto& import_entry = *in_imported_bound_names; | ||||||
| 
 | 
 | ||||||
|                     // 2. If ie.[[ImportName]] is namespace-object, then
 |                     // 2. If ie.[[ImportName]] is namespace-object, then
 | ||||||
|                     if (import_entry.is_namespace()) { |                     if (import_entry.is_namespace) { | ||||||
|                         // a. NOTE: This is a re-export of an imported module namespace object.
 |                         // a. NOTE: This is a re-export of an imported module namespace object.
 | ||||||
|  |                         VERIFY(export_entry.is_module_request() && export_entry.kind != ExportStatement::ExportEntry::Kind::NamedExport); | ||||||
|  | 
 | ||||||
|                         // b. Append ee to localExportEntries.
 |                         // b. Append ee to localExportEntries.
 | ||||||
|                         local_export_entries.empend(export_entry); |                         local_export_entries.empend(export_entry); | ||||||
|                     } |                     } | ||||||
|  | @ -154,12 +157,12 @@ Result<NonnullRefPtr<SourceTextModule>, Vector<Parser::Error>> SourceTextModule: | ||||||
|                     else { |                     else { | ||||||
|                         // a. NOTE: This is a re-export of a single name.
 |                         // a. NOTE: This is a re-export of a single name.
 | ||||||
|                         // b. Append the ExportEntry Record { [[ModuleRequest]]: ie.[[ModuleRequest]], [[ImportName]]: ie.[[ImportName]], [[LocalName]]: null, [[ExportName]]: ee.[[ExportName]] } to indirectExportEntries.
 |                         // b. Append the ExportEntry Record { [[ModuleRequest]]: ie.[[ModuleRequest]], [[ImportName]]: ie.[[ImportName]], [[LocalName]]: null, [[ExportName]]: ee.[[ExportName]] } to indirectExportEntries.
 | ||||||
|                         indirect_export_entries.empend(import_entry.module_request(), import_entry.import_name, export_entry.export_name); |                         indirect_export_entries.empend(ExportEntry::indirect_export_entry(import_entry.module_request(), import_entry.import_name, export_entry.export_name)); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             // b. Else if ee.[[ImportName]] is all-but-default, then
 |             // b. Else if ee.[[ImportName]] is all-but-default, then
 | ||||||
|             else if (export_entry.is_all_but_default()) { |             else if (export_entry.kind == ExportStatement::ExportEntry::Kind::ModuleRequestAllButDefault) { | ||||||
|                 // i. Assert: ee.[[ExportName]] is null.
 |                 // i. Assert: ee.[[ExportName]] is null.
 | ||||||
|                 VERIFY(export_entry.export_name.is_null()); |                 VERIFY(export_entry.export_name.is_null()); | ||||||
|                 // ii. Append ee to starExportEntries.
 |                 // ii. Append ee to starExportEntries.
 | ||||||
|  | @ -230,7 +233,7 @@ ThrowCompletionOr<Vector<FlyString>> SourceTextModule::get_exported_names(VM& vm | ||||||
|     // 7. For each ExportEntry Record e of module.[[StarExportEntries]], do
 |     // 7. For each ExportEntry Record e of module.[[StarExportEntries]], do
 | ||||||
|     for (auto& entry : m_star_export_entries) { |     for (auto& entry : m_star_export_entries) { | ||||||
|         // a. Let requestedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]).
 |         // a. Let requestedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]).
 | ||||||
|         auto requested_module = TRY(vm.host_resolve_imported_module(this, entry.module_request)); |         auto requested_module = TRY(vm.host_resolve_imported_module(this, entry.module_request())); | ||||||
| 
 | 
 | ||||||
|         // b. Let starNames be ? requestedModule.GetExportedNames(exportStarSet).
 |         // b. Let starNames be ? requestedModule.GetExportedNames(exportStarSet).
 | ||||||
|         auto star_names = TRY(requested_module->get_exported_names(vm, export_star_set)); |         auto star_names = TRY(requested_module->get_exported_names(vm, export_star_set)); | ||||||
|  | @ -293,7 +296,7 @@ Completion SourceTextModule::initialize_environment(VM& vm) | ||||||
|         // b. NOTE: The above call cannot fail because imported module requests are a subset of module.[[RequestedModules]], and these have been resolved earlier in this algorithm.
 |         // b. NOTE: The above call cannot fail because imported module requests are a subset of module.[[RequestedModules]], and these have been resolved earlier in this algorithm.
 | ||||||
| 
 | 
 | ||||||
|         // c. If in.[[ImportName]] is namespace-object, then
 |         // c. If in.[[ImportName]] is namespace-object, then
 | ||||||
|         if (import_entry.is_namespace()) { |         if (import_entry.is_namespace) { | ||||||
|             // i. Let namespace be ? GetModuleNamespace(importedModule).
 |             // i. Let namespace be ? GetModuleNamespace(importedModule).
 | ||||||
|             auto* namespace_ = TRY(imported_module->get_module_namespace(vm)); |             auto* namespace_ = TRY(imported_module->get_module_namespace(vm)); | ||||||
| 
 | 
 | ||||||
|  | @ -489,10 +492,10 @@ ThrowCompletionOr<ResolvedBinding> SourceTextModule::resolve_export(VM& vm, FlyS | ||||||
|             continue; |             continue; | ||||||
| 
 | 
 | ||||||
|         // i. Let importedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]).
 |         // i. Let importedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]).
 | ||||||
|         auto imported_module = TRY(vm.host_resolve_imported_module(this, entry.module_request)); |         auto imported_module = TRY(vm.host_resolve_imported_module(this, entry.module_request())); | ||||||
| 
 | 
 | ||||||
|         // ii. If e.[[ImportName]] is all, then
 |         // ii. If e.[[ImportName]] is all, then
 | ||||||
|         if (entry.is_all()) { |         if (entry.kind == ExportStatement::ExportEntry::Kind::ModuleRequestAll) { | ||||||
|             // 1. Assert: module does not provide the direct binding for this export.
 |             // 1. Assert: module does not provide the direct binding for this export.
 | ||||||
|             // FIXME: What does this mean? / How do we check this
 |             // FIXME: What does this mean? / How do we check this
 | ||||||
| 
 | 
 | ||||||
|  | @ -529,7 +532,7 @@ ThrowCompletionOr<ResolvedBinding> SourceTextModule::resolve_export(VM& vm, FlyS | ||||||
|     // 8. For each ExportEntry Record e of module.[[StarExportEntries]], do
 |     // 8. For each ExportEntry Record e of module.[[StarExportEntries]], do
 | ||||||
|     for (auto& entry : m_star_export_entries) { |     for (auto& entry : m_star_export_entries) { | ||||||
|         // a. Let importedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]).
 |         // a. Let importedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]).
 | ||||||
|         auto imported_module = TRY(vm.host_resolve_imported_module(this, entry.module_request)); |         auto imported_module = TRY(vm.host_resolve_imported_module(this, entry.module_request())); | ||||||
| 
 | 
 | ||||||
|         // b. Let resolution be ? importedModule.ResolveExport(exportName, resolveSet).
 |         // b. Let resolution be ? importedModule.ResolveExport(exportName, resolveSet).
 | ||||||
|         auto resolution = TRY(imported_module->resolve_export(vm, export_name, resolve_set)); |         auto resolution = TRY(imported_module->resolve_export(vm, export_name, resolve_set)); | ||||||
|  |  | ||||||
|  | @ -1,25 +1,22 @@ | ||||||
| // Because you can't easily load modules directly we load them via here and check
 | // Because you can't easily load modules directly we load them via here and check
 | ||||||
| // if they passed by checking the result
 | // if they passed by checking the result
 | ||||||
| 
 | 
 | ||||||
| function expectModulePassed(filename) { | function validTestModule(filename) { | ||||||
|     if (!filename.endsWith(".mjs") || !filename.startsWith("./")) { |     if (!filename.endsWith(".mjs") || !filename.startsWith("./")) { | ||||||
|         throw new ExpectationError( |         throw new ExpectationError( | ||||||
|             "Expected module name to start with './' " + |             `Expected module name to start with './' and end with '.mjs' but got '${filename}'` | ||||||
|                 "and end with '.mjs' but got '" + |  | ||||||
|                 filename + |  | ||||||
|                 "'" |  | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     async function getModule() { | function expectModulePassed(filename) { | ||||||
|         return import(filename); |     validTestModule(filename); | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     let moduleLoaded = false; |     let moduleLoaded = false; | ||||||
|     let moduleResult = null; |     let moduleResult = null; | ||||||
|     let thrownError = null; |     let thrownError = null; | ||||||
| 
 | 
 | ||||||
|     getModule() |     import(filename) | ||||||
|         .then(result => { |         .then(result => { | ||||||
|             moduleLoaded = true; |             moduleLoaded = true; | ||||||
|             moduleResult = result; |             moduleResult = result; | ||||||
|  | @ -36,10 +33,36 @@ function expectModulePassed(filename) { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     expect(moduleLoaded).toBeTrue(); |     expect(moduleLoaded).toBeTrue(); | ||||||
| 
 |  | ||||||
|     return moduleResult; |     return moduleResult; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | function expectedModuleToThrowSyntaxError(filename, message) { | ||||||
|  |     validTestModule(filename); | ||||||
|  | 
 | ||||||
|  |     let moduleLoaded = false; | ||||||
|  |     let thrownError = null; | ||||||
|  | 
 | ||||||
|  |     import(filename) | ||||||
|  |         .then(() => { | ||||||
|  |             moduleLoaded = true; | ||||||
|  |         }) | ||||||
|  |         .catch(error => { | ||||||
|  |             thrownError = error; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     runQueuedPromiseJobs(); | ||||||
|  | 
 | ||||||
|  |     if (thrownError) { | ||||||
|  |         expect(() => { | ||||||
|  |             throw thrownError; | ||||||
|  |         }).toThrowWithMessage(SyntaxError, message); | ||||||
|  |     } else { | ||||||
|  |         throw new ExpectationError( | ||||||
|  |             `Expected module: '${filename}' to fail to load with a syntax error but did not throw.` | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| describe("testing behavior", () => { | describe("testing behavior", () => { | ||||||
|     // To ensure the other tests are interpreter correctly we first test the underlying
 |     // To ensure the other tests are interpreter correctly we first test the underlying
 | ||||||
|     // mechanisms so these tests don't use expectModulePassed.
 |     // mechanisms so these tests don't use expectModulePassed.
 | ||||||
|  | @ -131,6 +154,25 @@ describe("in- and exports", () => { | ||||||
|     test("declaration exports which can be used in the module it self", () => { |     test("declaration exports which can be used in the module it self", () => { | ||||||
|         expectModulePassed("./declarations-tests.mjs"); |         expectModulePassed("./declarations-tests.mjs"); | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     test("string '*' is not a full namespace import", () => { | ||||||
|  |         expectModulePassed("./string-import-names.mjs"); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test("can combine string and default exports", () => { | ||||||
|  |         expectModulePassed("./string-import-namespace.mjs"); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test("can re export string names", () => { | ||||||
|  |         expectModulePassed("./string-import-namespace-indirect.mjs"); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test("re exporting all-but-default does not export a default value", () => { | ||||||
|  |         expectedModuleToThrowSyntaxError( | ||||||
|  |             "./indirect-export-without-default.mjs", | ||||||
|  |             "Invalid or ambiguous export entry 'default'" | ||||||
|  |         ); | ||||||
|  |     }); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| describe("loops", () => { | describe("loops", () => { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | // This is an all-but-default export and should thus only provide '*'.
 | ||||||
|  | export * from "./default-and-star-export.mjs"; | ||||||
|  | @ -0,0 +1,6 @@ | ||||||
|  | export default "defaultValue"; | ||||||
|  | 
 | ||||||
|  | const star = "starExportValue"; | ||||||
|  | const empty = "empty"; | ||||||
|  | 
 | ||||||
|  | export { star as "*", empty as "" }; | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | // Since 'default-and-star-export-indirect.mjs' only contains an all-but-default re export of
 | ||||||
|  | // 'default-and-star-export.mjs' it should not have a default value.
 | ||||||
|  | import defaultExportIndirect from "./default-and-star-export-indirect.mjs"; | ||||||
|  | @ -0,0 +1,12 @@ | ||||||
|  | import { "*" as starImport, "" as emptyImport } from "./default-and-star-export.mjs"; | ||||||
|  | 
 | ||||||
|  | import { | ||||||
|  |     "*" as starImportIndirect, | ||||||
|  |     "" as emptyImportIndirect, | ||||||
|  | } from "./default-and-star-export-indirect.mjs"; | ||||||
|  | 
 | ||||||
|  | export const passed = | ||||||
|  |     starImport === "starExportValue" && | ||||||
|  |     starImportIndirect === "starExportValue" && | ||||||
|  |     emptyImport === "empty" && | ||||||
|  |     emptyImportIndirect === "empty"; | ||||||
|  | @ -0,0 +1,6 @@ | ||||||
|  | import * as indirectNs from "./default-and-star-export-indirect.mjs"; | ||||||
|  | 
 | ||||||
|  | export const passed = | ||||||
|  |     indirectNs["*"] === "starExportValue" && | ||||||
|  |     indirectNs[""] === "empty" && | ||||||
|  |     indirectNs.default === undefined; | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | import * as ns from "./default-and-star-export.mjs"; | ||||||
|  | import defaultExport from "./default-and-star-export.mjs"; | ||||||
|  | 
 | ||||||
|  | export const passed = | ||||||
|  |     ns.default === "defaultValue" && | ||||||
|  |     ns["*"] === "starExportValue" && | ||||||
|  |     ns[""] === "empty" && | ||||||
|  |     defaultExport === "defaultValue"; | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 davidot
						davidot