| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2021, Linus Groh <linusg@serenityos.org> | 
					
						
							|  |  |  |  * Copyright (c) 2021-2022, David Tuin <davidot@serenityos.org> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  | #include <AK/DeprecatedString.h>
 | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | #include <AK/Format.h>
 | 
					
						
							|  |  |  | #include <AK/JsonObject.h>
 | 
					
						
							|  |  |  | #include <AK/Result.h>
 | 
					
						
							|  |  |  | #include <AK/ScopeGuard.h>
 | 
					
						
							|  |  |  | #include <AK/Vector.h>
 | 
					
						
							|  |  |  | #include <LibCore/ArgsParser.h>
 | 
					
						
							| 
									
										
										
										
											2023-02-09 03:02:46 +01:00
										 |  |  | #include <LibCore/File.h>
 | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | #include <LibJS/Bytecode/BasicBlock.h>
 | 
					
						
							|  |  |  | #include <LibJS/Bytecode/Generator.h>
 | 
					
						
							|  |  |  | #include <LibJS/Bytecode/Interpreter.h>
 | 
					
						
							|  |  |  | #include <LibJS/Contrib/Test262/GlobalObject.h>
 | 
					
						
							|  |  |  | #include <LibJS/Interpreter.h>
 | 
					
						
							| 
									
										
										
										
											2022-11-23 13:28:01 +01:00
										 |  |  | #include <LibJS/Parser.h>
 | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | #include <LibJS/Runtime/VM.h>
 | 
					
						
							|  |  |  | #include <LibJS/Script.h>
 | 
					
						
							|  |  |  | #include <fcntl.h>
 | 
					
						
							|  |  |  | #include <signal.h>
 | 
					
						
							|  |  |  | #include <unistd.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-20 06:53:14 +03:30
										 |  |  | #if !defined(AK_OS_MACOS) && !defined(AK_OS_EMSCRIPTEN)
 | 
					
						
							| 
									
										
										
										
											2022-09-11 10:42:44 +02:00
										 |  |  | // Only used to disable core dumps
 | 
					
						
							|  |  |  | #    include <sys/prctl.h>
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  | static DeprecatedString s_current_test = ""; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | static bool s_parse_only = false; | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  | static DeprecatedString s_harness_file_directory; | 
					
						
							| 
									
										
										
										
											2022-09-11 10:32:53 +02:00
										 |  |  | static bool s_automatic_harness_detection_mode = false; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | enum class NegativePhase { | 
					
						
							|  |  |  |     ParseOrEarly, | 
					
						
							|  |  |  |     Resolution, | 
					
						
							|  |  |  |     Runtime, | 
					
						
							|  |  |  |     Harness | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct TestError { | 
					
						
							|  |  |  |     NegativePhase phase { NegativePhase::ParseOrEarly }; | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     DeprecatedString type; | 
					
						
							|  |  |  |     DeprecatedString details; | 
					
						
							|  |  |  |     DeprecatedString harness_file; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | using ScriptOrModuleProgram = Variant<JS::NonnullGCPtr<JS::Script>, JS::NonnullGCPtr<JS::SourceTextModule>>; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template<typename ScriptType> | 
					
						
							|  |  |  | static Result<ScriptOrModuleProgram, TestError> parse_program(JS::Realm& realm, StringView source, StringView filepath) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     auto script_or_error = ScriptType::parse(source, realm, filepath); | 
					
						
							|  |  |  |     if (script_or_error.is_error()) { | 
					
						
							|  |  |  |         return TestError { | 
					
						
							|  |  |  |             NegativePhase::ParseOrEarly, | 
					
						
							|  |  |  |             "SyntaxError", | 
					
						
							| 
									
										
										
										
											2022-12-06 01:12:49 +00:00
										 |  |  |             script_or_error.error()[0].to_deprecated_string(), | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             "" | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ScriptOrModuleProgram { script_or_error.release_value() }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static Result<ScriptOrModuleProgram, TestError> parse_program(JS::Realm& realm, StringView source, StringView filepath, JS::Program::Type program_type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (program_type == JS::Program::Type::Script) | 
					
						
							|  |  |  |         return parse_program<JS::Script>(realm, source, filepath); | 
					
						
							|  |  |  |     return parse_program<JS::SourceTextModule>(realm, source, filepath); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template<typename InterpreterT> | 
					
						
							|  |  |  | static Result<void, TestError> run_program(InterpreterT& interpreter, ScriptOrModuleProgram& program) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-06-15 12:36:57 +02:00
										 |  |  |     auto result = program.visit( | 
					
						
							|  |  |  |         [&](auto& visitor) { | 
					
						
							|  |  |  |             return interpreter.run(*visitor); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (result.is_error()) { | 
					
						
							|  |  |  |         auto error_value = *result.throw_completion().value(); | 
					
						
							|  |  |  |         TestError error; | 
					
						
							|  |  |  |         error.phase = NegativePhase::Runtime; | 
					
						
							|  |  |  |         if (error_value.is_object()) { | 
					
						
							|  |  |  |             auto& object = error_value.as_object(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             auto name = object.get_without_side_effects("name"); | 
					
						
							|  |  |  |             if (!name.is_empty() && !name.is_accessor()) { | 
					
						
							| 
									
										
										
										
											2023-02-12 21:54:02 -05:00
										 |  |  |                 error.type = name.to_string_without_side_effects().release_value_but_fixme_should_propagate_errors().to_deprecated_string(); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             } else { | 
					
						
							|  |  |  |                 auto constructor = object.get_without_side_effects("constructor"); | 
					
						
							|  |  |  |                 if (constructor.is_object()) { | 
					
						
							|  |  |  |                     name = constructor.as_object().get_without_side_effects("name"); | 
					
						
							|  |  |  |                     if (!name.is_undefined()) | 
					
						
							| 
									
										
										
										
											2023-02-12 21:54:02 -05:00
										 |  |  |                         error.type = name.to_string_without_side_effects().release_value_but_fixme_should_propagate_errors().to_deprecated_string(); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             auto message = object.get_without_side_effects("message"); | 
					
						
							|  |  |  |             if (!message.is_empty() && !message.is_accessor()) | 
					
						
							| 
									
										
										
										
											2023-02-12 21:54:02 -05:00
										 |  |  |                 error.details = message.to_string_without_side_effects().release_value_but_fixme_should_propagate_errors().to_deprecated_string(); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         if (error.type.is_empty()) | 
					
						
							| 
									
										
										
										
											2023-02-12 21:54:02 -05:00
										 |  |  |             error.type = error_value.to_string_without_side_effects().release_value_but_fixme_should_propagate_errors().to_deprecated_string(); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |         return error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  | static HashMap<DeprecatedString, DeprecatedString> s_cached_harness_files; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | static Result<StringView, TestError> read_harness_file(StringView harness_file) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     auto cache = s_cached_harness_files.find(harness_file); | 
					
						
							|  |  |  |     if (cache == s_cached_harness_files.end()) { | 
					
						
							| 
									
										
										
										
											2023-02-09 03:02:46 +01:00
										 |  |  |         auto file_or_error = Core::File::open(DeprecatedString::formatted("{}{}", s_harness_file_directory, harness_file), Core::File::OpenMode::Read); | 
					
						
							| 
									
										
										
										
											2022-09-13 23:55:08 +02:00
										 |  |  |         if (file_or_error.is_error()) { | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             return TestError { | 
					
						
							|  |  |  |                 NegativePhase::Harness, | 
					
						
							|  |  |  |                 "filesystem", | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |                 DeprecatedString::formatted("Could not open file: {}", harness_file), | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |                 harness_file | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-11 17:49:00 +01:00
										 |  |  |         auto contents_or_error = file_or_error.value()->read_until_eof(); | 
					
						
							| 
									
										
										
										
											2022-09-13 23:55:08 +02:00
										 |  |  |         if (contents_or_error.is_error()) { | 
					
						
							|  |  |  |             return TestError { | 
					
						
							|  |  |  |                 NegativePhase::Harness, | 
					
						
							|  |  |  |                 "filesystem", | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |                 DeprecatedString::formatted("Could not read file: {}", harness_file), | 
					
						
							| 
									
										
										
										
											2022-09-13 23:55:08 +02:00
										 |  |  |                 harness_file | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         StringView contents_view = contents_or_error.value(); | 
					
						
							| 
									
										
										
										
											2022-12-06 01:12:49 +00:00
										 |  |  |         s_cached_harness_files.set(harness_file, contents_view.to_deprecated_string()); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |         cache = s_cached_harness_files.find(harness_file); | 
					
						
							|  |  |  |         VERIFY(cache != s_cached_harness_files.end()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return cache->value.view(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static Result<JS::NonnullGCPtr<JS::Script>, TestError> parse_harness_files(JS::Realm& realm, StringView harness_file) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     auto source_or_error = read_harness_file(harness_file); | 
					
						
							|  |  |  |     if (source_or_error.is_error()) | 
					
						
							|  |  |  |         return source_or_error.release_error(); | 
					
						
							|  |  |  |     auto program_or_error = parse_program<JS::Script>(realm, source_or_error.value(), harness_file); | 
					
						
							|  |  |  |     if (program_or_error.is_error()) { | 
					
						
							|  |  |  |         return TestError { | 
					
						
							|  |  |  |             NegativePhase::Harness, | 
					
						
							|  |  |  |             program_or_error.error().type, | 
					
						
							|  |  |  |             program_or_error.error().details, | 
					
						
							|  |  |  |             harness_file | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return program_or_error.release_value().get<JS::NonnullGCPtr<JS::Script>>(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-11 10:48:00 +02:00
										 |  |  | enum class StrictMode { | 
					
						
							|  |  |  |     Both, | 
					
						
							|  |  |  |     NoStrict, | 
					
						
							|  |  |  |     OnlyStrict | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static constexpr auto sta_harness_file = "sta.js"sv; | 
					
						
							|  |  |  | static constexpr auto assert_harness_file = "assert.js"sv; | 
					
						
							|  |  |  | static constexpr auto async_include = "doneprintHandle.js"sv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct TestMetadata { | 
					
						
							|  |  |  |     Vector<StringView> harness_files { sta_harness_file, assert_harness_file }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     StrictMode strict_mode { StrictMode::Both }; | 
					
						
							|  |  |  |     JS::Program::Type program_type { JS::Program::Type::Script }; | 
					
						
							|  |  |  |     bool is_async { false }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool is_negative { false }; | 
					
						
							|  |  |  |     NegativePhase phase { NegativePhase::ParseOrEarly }; | 
					
						
							|  |  |  |     StringView type; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static Result<void, TestError> run_test(StringView source, StringView filepath, TestMetadata const& metadata) | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-09-11 10:48:00 +02:00
										 |  |  |     if (s_parse_only || (metadata.is_negative && metadata.phase == NegativePhase::ParseOrEarly && metadata.program_type != JS::Program::Type::Module)) { | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |         // Creating the vm and interpreter is heavy so we just parse directly here.
 | 
					
						
							| 
									
										
										
										
											2022-09-11 10:48:00 +02:00
										 |  |  |         // We can also skip if we know the test is supposed to fail during parse
 | 
					
						
							|  |  |  |         // time. Unfortunately the phases of modules are not as clear and thus we
 | 
					
						
							|  |  |  |         // only do this for scripts. See also the comment at the end of verify_test.
 | 
					
						
							|  |  |  |         auto parser = JS::Parser(JS::Lexer(source, filepath), metadata.program_type); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |         auto program_or_error = parser.parse_program(); | 
					
						
							|  |  |  |         if (parser.has_errors()) { | 
					
						
							|  |  |  |             return TestError { | 
					
						
							|  |  |  |                 NegativePhase::ParseOrEarly, | 
					
						
							|  |  |  |                 "SyntaxError", | 
					
						
							| 
									
										
										
										
											2022-12-06 01:12:49 +00:00
										 |  |  |                 parser.errors()[0].to_deprecated_string(), | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |                 "" | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-17 10:44:47 -04:00
										 |  |  |     auto vm = MUST(JS::VM::create()); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |     vm->enable_default_host_import_module_dynamically_hook(); | 
					
						
							|  |  |  |     auto ast_interpreter = JS::Interpreter::create<JS::Test262::GlobalObject>(*vm); | 
					
						
							|  |  |  |     auto& realm = ast_interpreter->realm(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-11 10:48:00 +02:00
										 |  |  |     auto program_or_error = parse_program(realm, source, filepath, metadata.program_type); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |     if (program_or_error.is_error()) | 
					
						
							|  |  |  |         return program_or_error.release_error(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-22 15:59:18 +02:00
										 |  |  |     auto* bytecode_interpreter = vm->bytecode_interpreter_if_exists(); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     auto run_with_interpreter = [&](ScriptOrModuleProgram& program) { | 
					
						
							| 
									
										
										
										
											2023-06-22 15:59:18 +02:00
										 |  |  |         if (bytecode_interpreter) | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             return run_program(*bytecode_interpreter, program); | 
					
						
							|  |  |  |         return run_program(*ast_interpreter, program); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-11 10:48:00 +02:00
										 |  |  |     for (auto& harness_file : metadata.harness_files) { | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |         auto harness_program_or_error = parse_harness_files(realm, harness_file); | 
					
						
							|  |  |  |         if (harness_program_or_error.is_error()) | 
					
						
							|  |  |  |             return harness_program_or_error.release_error(); | 
					
						
							|  |  |  |         ScriptOrModuleProgram harness_program { harness_program_or_error.release_value() }; | 
					
						
							|  |  |  |         auto result = run_with_interpreter(harness_program); | 
					
						
							|  |  |  |         if (result.is_error()) { | 
					
						
							|  |  |  |             return TestError { | 
					
						
							|  |  |  |                 NegativePhase::Harness, | 
					
						
							|  |  |  |                 result.error().type, | 
					
						
							|  |  |  |                 result.error().details, | 
					
						
							|  |  |  |                 harness_file | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return run_with_interpreter(program_or_error.value()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  | static Result<TestMetadata, DeprecatedString> extract_metadata(StringView source) | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     auto lines = source.lines(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     TestMetadata metadata; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool parsing_negative = false; | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     DeprecatedString failed_message; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     auto parse_list = [&](StringView line) { | 
					
						
							|  |  |  |         auto start = line.find('['); | 
					
						
							|  |  |  |         if (!start.has_value()) | 
					
						
							|  |  |  |             return Vector<StringView> {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Vector<StringView> items; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         auto end = line.find_last(']'); | 
					
						
							|  |  |  |         if (!end.has_value() || end.value() <= start.value()) { | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |             failed_message = DeprecatedString::formatted("Can't parse list in '{}'", line); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             return items; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         auto list = line.substring_view(start.value() + 1, end.value() - start.value() - 1); | 
					
						
							|  |  |  |         for (auto const& item : list.split_view(","sv)) | 
					
						
							|  |  |  |             items.append(item.trim_whitespace(TrimMode::Both)); | 
					
						
							|  |  |  |         return items; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto second_word = [&](StringView line) { | 
					
						
							|  |  |  |         auto separator = line.find(' '); | 
					
						
							|  |  |  |         if (!separator.has_value() || separator.value() >= (line.length() - 1u)) { | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |             failed_message = DeprecatedString::formatted("Can't parse value after space in '{}'", line); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             return ""sv; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return line.substring_view(separator.value() + 1); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vector<StringView> include_list; | 
					
						
							|  |  |  |     bool parsing_includes_list = false; | 
					
						
							|  |  |  |     bool has_phase = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (auto raw_line : lines) { | 
					
						
							|  |  |  |         if (!failed_message.is_empty()) | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (raw_line.starts_with("---*/"sv)) { | 
					
						
							|  |  |  |             if (parsing_includes_list) { | 
					
						
							|  |  |  |                 for (auto& file : include_list) | 
					
						
							|  |  |  |                     metadata.harness_files.append(file); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return metadata; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         auto line = raw_line.trim_whitespace(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (parsing_includes_list) { | 
					
						
							|  |  |  |             if (line.starts_with('-')) { | 
					
						
							|  |  |  |                 include_list.append(second_word(line)); | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 if (include_list.is_empty()) { | 
					
						
							|  |  |  |                     failed_message = "Supposed to parse a list but found no entries"; | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 for (auto& file : include_list) | 
					
						
							|  |  |  |                     metadata.harness_files.append(file); | 
					
						
							|  |  |  |                 include_list.clear(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 parsing_includes_list = false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (parsing_negative) { | 
					
						
							|  |  |  |             if (line.starts_with("phase:"sv)) { | 
					
						
							|  |  |  |                 auto phase = second_word(line); | 
					
						
							|  |  |  |                 has_phase = true; | 
					
						
							|  |  |  |                 if (phase == "early"sv || phase == "parse"sv) { | 
					
						
							|  |  |  |                     metadata.phase = NegativePhase::ParseOrEarly; | 
					
						
							|  |  |  |                 } else if (phase == "resolution"sv) { | 
					
						
							|  |  |  |                     metadata.phase = NegativePhase::Resolution; | 
					
						
							|  |  |  |                 } else if (phase == "runtime"sv) { | 
					
						
							|  |  |  |                     metadata.phase = NegativePhase::Runtime; | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     has_phase = false; | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |                     failed_message = DeprecatedString::formatted("Unknown negative phase: {}", phase); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |                     break; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } else if (line.starts_with("type:"sv)) { | 
					
						
							|  |  |  |                 metadata.type = second_word(line); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 if (!has_phase) { | 
					
						
							|  |  |  |                     failed_message = "Failed to find phase in negative attributes"; | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if (metadata.type.is_null()) { | 
					
						
							|  |  |  |                     failed_message = "Failed to find type in negative attributes"; | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 parsing_negative = false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (line.starts_with("flags:"sv)) { | 
					
						
							|  |  |  |             auto flags = parse_list(line); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (auto flag : flags) { | 
					
						
							|  |  |  |                 if (flag == "raw"sv) { | 
					
						
							|  |  |  |                     metadata.strict_mode = StrictMode::NoStrict; | 
					
						
							|  |  |  |                     metadata.harness_files.clear(); | 
					
						
							|  |  |  |                 } else if (flag == "noStrict"sv) { | 
					
						
							|  |  |  |                     metadata.strict_mode = StrictMode::NoStrict; | 
					
						
							|  |  |  |                 } else if (flag == "onlyStrict"sv) { | 
					
						
							|  |  |  |                     metadata.strict_mode = StrictMode::OnlyStrict; | 
					
						
							|  |  |  |                 } else if (flag == "module"sv) { | 
					
						
							|  |  |  |                     VERIFY(metadata.strict_mode == StrictMode::Both); | 
					
						
							|  |  |  |                     metadata.program_type = JS::Program::Type::Module; | 
					
						
							|  |  |  |                     metadata.strict_mode = StrictMode::NoStrict; | 
					
						
							|  |  |  |                 } else if (flag == "async"sv) { | 
					
						
							|  |  |  |                     metadata.harness_files.append(async_include); | 
					
						
							|  |  |  |                     metadata.is_async = true; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else if (line.starts_with("includes:"sv)) { | 
					
						
							|  |  |  |             auto files = parse_list(line); | 
					
						
							|  |  |  |             if (files.is_empty()) { | 
					
						
							|  |  |  |                 parsing_includes_list = true; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 for (auto& file : files) | 
					
						
							|  |  |  |                     metadata.harness_files.append(file); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else if (line.starts_with("negative:"sv)) { | 
					
						
							|  |  |  |             metadata.is_negative = true; | 
					
						
							|  |  |  |             parsing_negative = true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (failed_message.is_empty()) | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         failed_message = DeprecatedString::formatted("Never reached end of comment '---*/'"); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return failed_message; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool verify_test(Result<void, TestError>& result, TestMetadata const& metadata, JsonObject& output) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (result.is_error()) { | 
					
						
							|  |  |  |         if (result.error().phase == NegativePhase::Harness) { | 
					
						
							|  |  |  |             output.set("harness_error", true); | 
					
						
							|  |  |  |             output.set("harness_file", result.error().harness_file); | 
					
						
							|  |  |  |             output.set("result", "harness_error"); | 
					
						
							|  |  |  |         } else if (result.error().phase == NegativePhase::Runtime) { | 
					
						
							|  |  |  |             auto& error_type = result.error().type; | 
					
						
							|  |  |  |             auto& error_details = result.error().details; | 
					
						
							|  |  |  |             if ((error_type == "InternalError"sv && error_details.starts_with("TODO("sv)) | 
					
						
							|  |  |  |                 || (error_type == "Test262Error"sv && error_details.ends_with(" but got a InternalError"sv))) { | 
					
						
							|  |  |  |                 output.set("todo_error", true); | 
					
						
							|  |  |  |                 output.set("result", "todo_error"); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (metadata.is_async && output.has("output"sv)) { | 
					
						
							| 
									
										
										
										
											2022-12-21 14:42:45 +00:00
										 |  |  |         auto output_messages = output.get_deprecated_string("output"sv); | 
					
						
							|  |  |  |         VERIFY(output_messages.has_value()); | 
					
						
							|  |  |  |         if (output_messages->contains("AsyncTestFailure:InternalError: TODO("sv)) { | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             output.set("todo_error", true); | 
					
						
							|  |  |  |             output.set("result", "todo_error"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto phase_to_string = [](NegativePhase phase) { | 
					
						
							|  |  |  |         switch (phase) { | 
					
						
							|  |  |  |         case NegativePhase::ParseOrEarly: | 
					
						
							|  |  |  |             return "parse"; | 
					
						
							|  |  |  |         case NegativePhase::Resolution: | 
					
						
							|  |  |  |             return "resolution"; | 
					
						
							|  |  |  |         case NegativePhase::Runtime: | 
					
						
							|  |  |  |             return "runtime"; | 
					
						
							|  |  |  |         case NegativePhase::Harness: | 
					
						
							|  |  |  |             return "harness"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto error_to_json = [&phase_to_string](TestError const& error) { | 
					
						
							|  |  |  |         JsonObject error_object; | 
					
						
							|  |  |  |         error_object.set("phase", phase_to_string(error.phase)); | 
					
						
							|  |  |  |         error_object.set("type", error.type); | 
					
						
							|  |  |  |         error_object.set("details", error.details); | 
					
						
							|  |  |  |         return error_object; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     JsonValue expected_error; | 
					
						
							|  |  |  |     JsonValue got_error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ScopeGuard set_error = [&] { | 
					
						
							|  |  |  |         JsonObject error_object; | 
					
						
							|  |  |  |         error_object.set("expected", expected_error); | 
					
						
							|  |  |  |         error_object.set("got", got_error); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         output.set("error", error_object); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!metadata.is_negative) { | 
					
						
							|  |  |  |         if (!result.is_error()) | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         got_error = JsonValue { error_to_json(result.error()) }; | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     JsonObject expected_error_object; | 
					
						
							|  |  |  |     expected_error_object.set("phase", phase_to_string(metadata.phase)); | 
					
						
							| 
									
										
										
										
											2022-12-06 01:12:49 +00:00
										 |  |  |     expected_error_object.set("type", metadata.type.to_deprecated_string()); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     expected_error = expected_error_object; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!result.is_error()) { | 
					
						
							|  |  |  |         if (s_parse_only && metadata.phase != NegativePhase::ParseOrEarly) { | 
					
						
							|  |  |  |             // Expected non-parse error but did not get it but we never got to that phase.
 | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto const& error = result.error(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     got_error = JsonValue { error_to_json(error) }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (metadata.program_type == JS::Program::Type::Module && metadata.type == "SyntaxError"sv) { | 
					
						
							|  |  |  |         // NOTE: Since the "phase" of negative results is both not defined and hard to
 | 
					
						
							|  |  |  |         //       track throughout the entire Module life span we will just accept any
 | 
					
						
							|  |  |  |         //       SyntaxError as the correct one.
 | 
					
						
							|  |  |  |         //       See for example:
 | 
					
						
							|  |  |  |         //       - test/language/module-code/instn-star-err-not-found.js
 | 
					
						
							|  |  |  |         //       - test/language/module-code/instn-resolve-err-syntax-1.js
 | 
					
						
							|  |  |  |         //       - test/language/import/json-invalid.js
 | 
					
						
							|  |  |  |         //       The first fails in runtime because there is no 'x' to export
 | 
					
						
							|  |  |  |         //       However this is during the linking phase of the upper module.
 | 
					
						
							|  |  |  |         //       Whereas the second fails with a SyntaxError because the linked module
 | 
					
						
							|  |  |  |         //       has one.
 | 
					
						
							|  |  |  |         //       The third test is the same as the second, upper module is fine but
 | 
					
						
							|  |  |  |         //       import a module with SyntaxError, however here the phase is runtime.
 | 
					
						
							| 
									
										
										
										
											2022-09-09 14:53:53 -07:00
										 |  |  |         //       In conclusion all the test which would cause the initial module to not
 | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |         //       be evaluated !should! have '$DONOTEVALUATE();' at the top causing a
 | 
					
						
							|  |  |  |         //       Reference error, meaning we just ignore the phase in the SyntaxError case.
 | 
					
						
							|  |  |  |         return error.type == metadata.type; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return error.phase == metadata.phase && error.type == metadata.type; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  | static bool extract_harness_directory(DeprecatedString const& test_file_path) | 
					
						
							| 
									
										
										
										
											2022-09-11 10:32:53 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     auto test_directory_index = test_file_path.find("test/"sv); | 
					
						
							|  |  |  |     if (!test_directory_index.has_value()) { | 
					
						
							|  |  |  |         warnln("Attempted to find harness directory from test file '{}', but did not find 'test/'", test_file_path); | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     s_harness_file_directory = DeprecatedString::formatted("{}harness/", test_file_path.substring_view(0, test_directory_index.value())); | 
					
						
							| 
									
										
										
										
											2022-09-11 10:32:53 +02:00
										 |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | static FILE* saved_stdout_fd; | 
					
						
							| 
									
										
										
										
											2022-09-11 10:25:25 +02:00
										 |  |  | static bool g_in_assert = false; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-11 10:25:25 +02:00
										 |  |  | [[noreturn]] static void handle_failed_assert(char const* assert_failed_message) | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-09-11 10:25:25 +02:00
										 |  |  |     if (!g_in_assert) { | 
					
						
							|  |  |  |         // Just in case we trigger an assert while creating the JSON output just
 | 
					
						
							|  |  |  |         // immediately stop if we are already in a failed assert.
 | 
					
						
							|  |  |  |         g_in_assert = true; | 
					
						
							|  |  |  |         JsonObject assert_fail_result; | 
					
						
							|  |  |  |         assert_fail_result.set("test", s_current_test); | 
					
						
							|  |  |  |         assert_fail_result.set("assert_fail", true); | 
					
						
							|  |  |  |         assert_fail_result.set("result", "assert_fail"); | 
					
						
							|  |  |  |         assert_fail_result.set("output", assert_failed_message); | 
					
						
							| 
									
										
										
										
											2022-12-06 01:12:49 +00:00
										 |  |  |         outln(saved_stdout_fd, "RESULT {}{}", assert_fail_result.to_deprecated_string(), '\0'); | 
					
						
							| 
									
										
										
										
											2022-09-11 10:25:25 +02:00
										 |  |  |         // (Attempt to) Ensure that messages are written before quitting.
 | 
					
						
							|  |  |  |         fflush(saved_stdout_fd); | 
					
						
							|  |  |  |         fflush(stderr); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     exit(12); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-18 09:07:15 -07:00
										 |  |  | // FIXME: Use a SIGABRT handler here instead of overriding internal libc assertion handlers.
 | 
					
						
							|  |  |  | //        Fixing this will likely require updating the test driver as well to pull the assertion failure
 | 
					
						
							|  |  |  | //        message out of stderr rather than from the json object printed to stdout.
 | 
					
						
							| 
									
										
										
										
											2022-10-09 15:23:23 -06:00
										 |  |  | #ifdef AK_OS_SERENITY
 | 
					
						
							| 
									
										
										
										
											2022-09-11 10:25:25 +02:00
										 |  |  | void __assertion_failed(char const* assertion) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     handle_failed_assert(assertion); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2022-12-18 09:07:15 -07:00
										 |  |  | #    ifdef ASSERT_FAIL_HAS_INT /* Set by CMake */
 | 
					
						
							| 
									
										
										
										
											2022-11-20 06:53:14 +03:30
										 |  |  | extern "C" __attribute__((__noreturn__)) void __assert_fail(char const* assertion, char const* file, int line, char const* function) | 
					
						
							|  |  |  | #    else
 | 
					
						
							| 
									
										
										
										
											2022-09-13 16:02:55 +02:00
										 |  |  | extern "C" __attribute__((__noreturn__)) void __assert_fail(char const* assertion, char const* file, unsigned int line, char const* function) | 
					
						
							| 
									
										
										
										
											2022-11-20 06:53:14 +03:30
										 |  |  | #    endif
 | 
					
						
							| 
									
										
										
										
											2022-09-11 10:25:25 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     auto full_message = DeprecatedString::formatted("{}:{}: {}: Assertion `{}' failed.", file, line, function, assertion); | 
					
						
							| 
									
										
										
										
											2022-09-11 10:25:25 +02:00
										 |  |  |     handle_failed_assert(full_message.characters()); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-09-11 10:25:25 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-28 17:03:07 +02:00
										 |  |  | constexpr int exit_wrong_arguments = 2; | 
					
						
							|  |  |  | constexpr int exit_stdout_setup_failed = 1; | 
					
						
							|  |  |  | constexpr int exit_setup_input_failure = 7; | 
					
						
							|  |  |  | constexpr int exit_read_file_failure = 3; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | int main(int argc, char** argv) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-02-21 15:14:41 +03:30
										 |  |  |     Vector<StringView> arguments; | 
					
						
							|  |  |  |     arguments.ensure_capacity(argc); | 
					
						
							|  |  |  |     for (auto i = 0; i < argc; ++i) | 
					
						
							|  |  |  |         arguments.append({ argv[i], strlen(argv[i]) }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |     int timeout = 10; | 
					
						
							|  |  |  |     bool enable_debug_printing = false; | 
					
						
							| 
									
										
										
										
											2022-09-11 10:42:44 +02:00
										 |  |  |     bool disable_core_dumping = false; | 
					
						
							| 
									
										
										
										
											2023-06-17 13:16:35 +02:00
										 |  |  |     bool use_bytecode = false; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Core::ArgsParser args_parser; | 
					
						
							|  |  |  |     args_parser.set_general_help("LibJS test262 runner for streaming tests"); | 
					
						
							|  |  |  |     args_parser.add_option(s_harness_file_directory, "Directory containing the harness files", "harness-location", 'l', "harness-files"); | 
					
						
							| 
									
										
										
										
											2023-06-17 13:16:35 +02:00
										 |  |  |     args_parser.add_option(use_bytecode, "Use the bytecode interpreter", "use-bytecode", 'b'); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |     args_parser.add_option(s_parse_only, "Only parse the files", "parse-only", 'p'); | 
					
						
							|  |  |  |     args_parser.add_option(timeout, "Seconds before test should timeout", "timeout", 't', "seconds"); | 
					
						
							|  |  |  |     args_parser.add_option(enable_debug_printing, "Enable debug printing", "debug", 'd'); | 
					
						
							| 
									
										
										
										
											2022-09-11 10:42:44 +02:00
										 |  |  |     args_parser.add_option(disable_core_dumping, "Disable core dumping", "disable-core-dump", 0); | 
					
						
							| 
									
										
										
										
											2023-02-21 15:14:41 +03:30
										 |  |  |     args_parser.parse(arguments); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 13:16:35 +02:00
										 |  |  |     JS::Bytecode::Interpreter::set_enabled(use_bytecode); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-20 06:53:14 +03:30
										 |  |  | #if !defined(AK_OS_MACOS) && !defined(AK_OS_EMSCRIPTEN)
 | 
					
						
							| 
									
										
										
										
											2022-09-11 10:42:44 +02:00
										 |  |  |     if (disable_core_dumping && prctl(PR_SET_DUMPABLE, 0, 0) < 0) { | 
					
						
							|  |  |  |         perror("prctl(PR_SET_DUMPABLE)"); | 
					
						
							| 
									
										
										
										
											2022-09-28 17:03:07 +02:00
										 |  |  |         return exit_wrong_arguments; | 
					
						
							| 
									
										
										
										
											2022-09-11 10:42:44 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |     if (s_harness_file_directory.is_empty()) { | 
					
						
							| 
									
										
										
										
											2022-09-11 10:32:53 +02:00
										 |  |  |         s_automatic_harness_detection_mode = true; | 
					
						
							|  |  |  |     } else if (!s_harness_file_directory.ends_with('/')) { | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         s_harness_file_directory = DeprecatedString::formatted("{}/", s_harness_file_directory); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (timeout <= 0) { | 
					
						
							| 
									
										
										
										
											2022-09-09 14:53:53 -07:00
										 |  |  |         warnln("timeout must be at least 1"); | 
					
						
							| 
									
										
										
										
											2022-09-28 17:03:07 +02:00
										 |  |  |         return exit_wrong_arguments; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     AK::set_debug_enabled(enable_debug_printing); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // The piping stuff is based on https://stackoverflow.com/a/956269.
 | 
					
						
							|  |  |  |     constexpr auto BUFFER_SIZE = 1 * KiB; | 
					
						
							|  |  |  |     char buffer[BUFFER_SIZE] = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto saved_stdout = dup(STDOUT_FILENO); | 
					
						
							|  |  |  |     if (saved_stdout < 0) { | 
					
						
							|  |  |  |         perror("dup"); | 
					
						
							| 
									
										
										
										
											2022-09-28 17:03:07 +02:00
										 |  |  |         return exit_stdout_setup_failed; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     saved_stdout_fd = fdopen(saved_stdout, "w"); | 
					
						
							|  |  |  |     if (!saved_stdout_fd) { | 
					
						
							|  |  |  |         perror("fdopen"); | 
					
						
							| 
									
										
										
										
											2022-09-28 17:03:07 +02:00
										 |  |  |         return exit_stdout_setup_failed; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int stdout_pipe[2]; | 
					
						
							|  |  |  |     if (pipe(stdout_pipe) < 0) { | 
					
						
							|  |  |  |         perror("pipe"); | 
					
						
							| 
									
										
										
										
											2022-09-28 17:03:07 +02:00
										 |  |  |         return exit_stdout_setup_failed; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto flags = fcntl(stdout_pipe[0], F_GETFL); | 
					
						
							|  |  |  |     flags |= O_NONBLOCK; | 
					
						
							|  |  |  |     fcntl(stdout_pipe[0], F_SETFL, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto flags2 = fcntl(stdout_pipe[1], F_GETFL); | 
					
						
							|  |  |  |     flags2 |= O_NONBLOCK; | 
					
						
							|  |  |  |     fcntl(stdout_pipe[1], F_SETFL, flags2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (dup2(stdout_pipe[1], STDOUT_FILENO) < 0) { | 
					
						
							|  |  |  |         perror("dup2"); | 
					
						
							| 
									
										
										
										
											2022-09-28 17:03:07 +02:00
										 |  |  |         return exit_stdout_setup_failed; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (close(stdout_pipe[1]) < 0) { | 
					
						
							|  |  |  |         perror("close"); | 
					
						
							| 
									
										
										
										
											2022-09-28 17:03:07 +02:00
										 |  |  |         return exit_stdout_setup_failed; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto collect_output = [&] { | 
					
						
							|  |  |  |         fflush(stdout); | 
					
						
							|  |  |  |         auto nread = read(stdout_pipe[0], buffer, BUFFER_SIZE); | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         DeprecatedString value; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (nread > 0) { | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |             value = DeprecatedString { buffer, static_cast<size_t>(nread) }; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             while (nread > 0) { | 
					
						
							|  |  |  |                 nread = read(stdout_pipe[0], buffer, BUFFER_SIZE); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return value; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-11 10:13:58 +02:00
										 |  |  | #define ARM_TIMER() \
 | 
					
						
							|  |  |  |     alarm(timeout) | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-11 10:13:58 +02:00
										 |  |  | #define DISARM_TIMER() \
 | 
					
						
							|  |  |  |     alarm(0) | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-09 03:02:46 +01:00
										 |  |  |     auto standard_input_or_error = Core::File::standard_input(); | 
					
						
							| 
									
										
										
										
											2022-09-13 23:55:08 +02:00
										 |  |  |     if (standard_input_or_error.is_error()) | 
					
						
							| 
									
										
										
										
											2022-09-28 17:03:07 +02:00
										 |  |  |         return exit_setup_input_failure; | 
					
						
							| 
									
										
										
										
											2022-09-13 23:55:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Array<u8, 1024> input_buffer {}; | 
					
						
							| 
									
										
										
										
											2023-05-03 18:45:18 -04:00
										 |  |  |     auto buffered_standard_input_or_error = Core::InputBufferedFile::create(standard_input_or_error.release_value()); | 
					
						
							| 
									
										
										
										
											2022-09-13 23:55:08 +02:00
										 |  |  |     if (buffered_standard_input_or_error.is_error()) | 
					
						
							| 
									
										
										
										
											2022-09-28 17:03:07 +02:00
										 |  |  |         return exit_setup_input_failure; | 
					
						
							| 
									
										
										
										
											2022-09-13 23:55:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     auto& buffered_input_stream = buffered_standard_input_or_error.value(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |     size_t count = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-13 23:55:08 +02:00
										 |  |  |     while (!buffered_input_stream->is_eof()) { | 
					
						
							|  |  |  |         auto path_or_error = buffered_input_stream->read_line(input_buffer); | 
					
						
							|  |  |  |         if (path_or_error.is_error() || path_or_error.value().is_empty()) | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             continue; | 
					
						
							| 
									
										
										
										
											2022-09-13 23:55:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         auto& path = path_or_error.value(); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         s_current_test = path; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-11 10:32:53 +02:00
										 |  |  |         if (s_automatic_harness_detection_mode) { | 
					
						
							|  |  |  |             if (!extract_harness_directory(path)) | 
					
						
							| 
									
										
										
										
											2022-09-28 17:03:07 +02:00
										 |  |  |                 return exit_read_file_failure; | 
					
						
							| 
									
										
										
										
											2022-09-11 10:32:53 +02:00
										 |  |  |             s_automatic_harness_detection_mode = false; | 
					
						
							|  |  |  |             VERIFY(!s_harness_file_directory.is_empty()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-09 03:02:46 +01:00
										 |  |  |         auto file_or_error = Core::File::open(path, Core::File::OpenMode::Read); | 
					
						
							| 
									
										
										
										
											2022-09-13 23:55:08 +02:00
										 |  |  |         if (file_or_error.is_error()) { | 
					
						
							| 
									
										
										
										
											2022-09-11 10:44:03 +02:00
										 |  |  |             warnln("Could not open file: {}", path); | 
					
						
							| 
									
										
										
										
											2022-09-28 17:03:07 +02:00
										 |  |  |             return exit_read_file_failure; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-09-13 23:55:08 +02:00
										 |  |  |         auto& file = file_or_error.value(); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         count++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         DeprecatedString source_with_strict; | 
					
						
							| 
									
										
										
										
											2022-09-13 23:55:08 +02:00
										 |  |  |         static StringView use_strict = "'use strict';\n"sv; | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |         static size_t strict_length = use_strict.length(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2022-12-11 17:49:00 +01:00
										 |  |  |             auto contents_or_error = file->read_until_eof(); | 
					
						
							| 
									
										
										
										
											2022-09-13 23:55:08 +02:00
										 |  |  |             if (contents_or_error.is_error()) { | 
					
						
							|  |  |  |                 warnln("Could not read contents of file: {}", path); | 
					
						
							| 
									
										
										
										
											2022-09-28 17:03:07 +02:00
										 |  |  |                 return exit_read_file_failure; | 
					
						
							| 
									
										
										
										
											2022-09-13 23:55:08 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |             auto& contents = contents_or_error.value(); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             StringBuilder builder { contents.size() + strict_length }; | 
					
						
							|  |  |  |             builder.append(use_strict); | 
					
						
							|  |  |  |             builder.append(contents); | 
					
						
							| 
									
										
										
										
											2022-12-06 01:12:49 +00:00
										 |  |  |             source_with_strict = builder.to_deprecated_string(); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         StringView with_strict = source_with_strict.view(); | 
					
						
							|  |  |  |         StringView original_contents = source_with_strict.substring_view(strict_length); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         JsonObject result_object; | 
					
						
							|  |  |  |         result_object.set("test", path); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ScopeGuard output_guard = [&] { | 
					
						
							| 
									
										
										
										
											2022-12-06 01:12:49 +00:00
										 |  |  |             outln(saved_stdout_fd, "RESULT {}{}", result_object.to_deprecated_string(), '\0'); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             fflush(saved_stdout_fd); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         auto metadata_or_error = extract_metadata(original_contents); | 
					
						
							|  |  |  |         if (metadata_or_error.is_error()) { | 
					
						
							|  |  |  |             result_object.set("result", "metadata_error"); | 
					
						
							|  |  |  |             result_object.set("metadata_error", true); | 
					
						
							|  |  |  |             result_object.set("metadata_output", metadata_or_error.error()); | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         auto& metadata = metadata_or_error.value(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         bool passed = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (metadata.strict_mode != StrictMode::OnlyStrict) { | 
					
						
							|  |  |  |             result_object.set("strict_mode", false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             ARM_TIMER(); | 
					
						
							| 
									
										
										
										
											2022-09-11 10:48:00 +02:00
										 |  |  |             auto result = run_test(original_contents, path, metadata); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             DISARM_TIMER(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |             DeprecatedString first_output = collect_output(); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             if (!first_output.is_null()) | 
					
						
							|  |  |  |                 result_object.set("output", first_output); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             passed = verify_test(result, metadata, result_object); | 
					
						
							|  |  |  |             if (metadata.is_async && !s_parse_only) { | 
					
						
							|  |  |  |                 if (!first_output.contains("Test262:AsyncTestComplete"sv) || first_output.contains("Test262:AsyncTestFailure"sv)) { | 
					
						
							|  |  |  |                     result_object.set("async_fail", true); | 
					
						
							|  |  |  |                     if (first_output.is_null()) | 
					
						
							|  |  |  |                         result_object.set("output", JsonValue { AK::JsonValue::Type::Null }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     passed = false; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (passed && metadata.strict_mode != StrictMode::NoStrict) { | 
					
						
							|  |  |  |             result_object.set("strict_mode", true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             ARM_TIMER(); | 
					
						
							| 
									
										
										
										
											2022-09-11 10:48:00 +02:00
										 |  |  |             auto result = run_test(with_strict, path, metadata); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             DISARM_TIMER(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |             DeprecatedString first_output = collect_output(); | 
					
						
							| 
									
										
										
										
											2022-09-11 09:14:32 +02:00
										 |  |  |             if (!first_output.is_null()) | 
					
						
							|  |  |  |                 result_object.set("strict_output", first_output); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             passed = verify_test(result, metadata, result_object); | 
					
						
							|  |  |  |             if (metadata.is_async && !s_parse_only) { | 
					
						
							|  |  |  |                 if (!first_output.contains("Test262:AsyncTestComplete"sv) || first_output.contains("Test262:AsyncTestFailure"sv)) { | 
					
						
							|  |  |  |                     result_object.set("async_fail", true); | 
					
						
							|  |  |  |                     if (first_output.is_null()) | 
					
						
							|  |  |  |                         result_object.set("output", JsonValue { AK::JsonValue::Type::Null }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     passed = false; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (passed) | 
					
						
							|  |  |  |             result_object.remove("strict_mode"sv); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!result_object.has("result"sv)) | 
					
						
							|  |  |  |             result_object.set("result"sv, passed ? "passed"sv : "failed"sv); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s_current_test = ""; | 
					
						
							|  |  |  |     outln(saved_stdout_fd, "DONE {}", count); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // After this point we have already written our output so pretend everything is fine if we get an error.
 | 
					
						
							|  |  |  |     if (dup2(saved_stdout, STDOUT_FILENO) < 0) { | 
					
						
							|  |  |  |         perror("dup2"); | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (fclose(saved_stdout_fd) < 0) { | 
					
						
							|  |  |  |         perror("fclose"); | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (close(stdout_pipe[0]) < 0) { | 
					
						
							|  |  |  |         perror("close"); | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } |