Serenity Operating System
at master 156 lines 5.7 kB view raw
1/* 2 * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org> 3 * Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <LibJS/Runtime/ArrayBuffer.h> 9#include <LibTest/JavaScriptTestRunner.h> 10 11TEST_ROOT("Userland/Libraries/LibJS/Tests"); 12 13TESTJS_PROGRAM_FLAG(test262_parser_tests, "Run test262 parser tests", "test262-parser-tests", 0); 14 15TESTJS_GLOBAL_FUNCTION(is_strict_mode, isStrictMode, 0) 16{ 17 return JS::Value(vm.in_strict_mode()); 18} 19 20TESTJS_GLOBAL_FUNCTION(can_parse_source, canParseSource) 21{ 22 auto source = TRY(vm.argument(0).to_deprecated_string(vm)); 23 auto parser = JS::Parser(JS::Lexer(source)); 24 (void)parser.parse_program(); 25 return JS::Value(!parser.has_errors()); 26} 27 28TESTJS_GLOBAL_FUNCTION(run_queued_promise_jobs, runQueuedPromiseJobs) 29{ 30 vm.run_queued_promise_jobs(); 31 return JS::js_undefined(); 32} 33 34TESTJS_GLOBAL_FUNCTION(get_weak_set_size, getWeakSetSize) 35{ 36 auto* object = TRY(vm.argument(0).to_object(vm)); 37 if (!is<JS::WeakSet>(object)) 38 return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "WeakSet"); 39 auto* weak_set = static_cast<JS::WeakSet*>(object); 40 return JS::Value(weak_set->values().size()); 41} 42 43TESTJS_GLOBAL_FUNCTION(get_weak_map_size, getWeakMapSize) 44{ 45 auto* object = TRY(vm.argument(0).to_object(vm)); 46 if (!is<JS::WeakMap>(object)) 47 return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "WeakMap"); 48 auto* weak_map = static_cast<JS::WeakMap*>(object); 49 return JS::Value(weak_map->values().size()); 50} 51 52TESTJS_GLOBAL_FUNCTION(mark_as_garbage, markAsGarbage) 53{ 54 auto argument = vm.argument(0); 55 if (!argument.is_string()) 56 return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAString, TRY_OR_THROW_OOM(vm, argument.to_string_without_side_effects())); 57 58 auto& variable_name = argument.as_string(); 59 60 // In native functions we don't have a lexical environment so get the outer via the execution stack. 61 auto outer_environment = vm.execution_context_stack().last_matching([&](auto& execution_context) { 62 return execution_context->lexical_environment != nullptr; 63 }); 64 if (!outer_environment.has_value()) 65 return vm.throw_completion<JS::ReferenceError>(JS::ErrorType::UnknownIdentifier, TRY(variable_name.deprecated_string())); 66 67 auto reference = TRY(vm.resolve_binding(TRY(variable_name.deprecated_string()), outer_environment.value()->lexical_environment)); 68 69 auto value = TRY(reference.get_value(vm)); 70 71 if (!can_be_held_weakly(value)) 72 return vm.throw_completion<JS::TypeError>(JS::ErrorType::CannotBeHeldWeakly, DeprecatedString::formatted("Variable with name {}", TRY(variable_name.deprecated_string()))); 73 74 vm.heap().uproot_cell(&value.as_cell()); 75 TRY(reference.delete_(vm)); 76 77 return JS::js_undefined(); 78} 79 80TESTJS_GLOBAL_FUNCTION(detach_array_buffer, detachArrayBuffer) 81{ 82 auto array_buffer = vm.argument(0); 83 if (!array_buffer.is_object() || !is<JS::ArrayBuffer>(array_buffer.as_object())) 84 return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "ArrayBuffer"); 85 86 auto& array_buffer_object = static_cast<JS::ArrayBuffer&>(array_buffer.as_object()); 87 TRY(JS::detach_array_buffer(vm, array_buffer_object, vm.argument(1))); 88 return JS::js_null(); 89} 90 91TESTJS_RUN_FILE_FUNCTION(DeprecatedString const& test_file, JS::Interpreter& interpreter, JS::ExecutionContext&) 92{ 93 if (!test262_parser_tests) 94 return Test::JS::RunFileHookResult::RunAsNormal; 95 96 auto start_time = Test::get_time_in_ms(); 97 98 LexicalPath path(test_file); 99 auto dirname = path.dirname(); 100 enum { 101 Early, 102 Fail, 103 Pass, 104 ExplicitPass, 105 } expectation { Pass }; 106 107 if (dirname.ends_with("early"sv)) 108 expectation = Early; 109 else if (dirname.ends_with("fail"sv)) 110 expectation = Fail; 111 else if (dirname.ends_with("pass-explicit"sv)) 112 expectation = ExplicitPass; 113 else if (dirname.ends_with("pass"sv)) 114 expectation = Pass; 115 else 116 return Test::JS::RunFileHookResult::SkipFile; 117 118 auto program_type = path.basename().ends_with(".module.js"sv) ? JS::Program::Type::Module : JS::Program::Type::Script; 119 bool parse_succeeded = false; 120 if (program_type == JS::Program::Type::Module) 121 parse_succeeded = !Test::JS::parse_module(test_file, interpreter.realm()).is_error(); 122 else 123 parse_succeeded = !Test::JS::parse_script(test_file, interpreter.realm()).is_error(); 124 125 bool test_passed = true; 126 DeprecatedString message; 127 DeprecatedString expectation_string; 128 129 switch (expectation) { 130 case Early: 131 case Fail: 132 expectation_string = "File should not parse"; 133 test_passed = !parse_succeeded; 134 if (!test_passed) 135 message = "Expected the file to fail parsing, but it did not"; 136 break; 137 case Pass: 138 case ExplicitPass: 139 expectation_string = "File should parse"; 140 test_passed = parse_succeeded; 141 if (!test_passed) 142 message = "Expected the file to parse, but it did not"; 143 break; 144 } 145 146 auto test_result = test_passed ? Test::Result::Pass : Test::Result::Fail; 147 auto test_path = LexicalPath::relative_path(test_file, Test::JS::g_test_root); 148 auto duration_ms = Test::get_time_in_ms() - start_time; 149 return Test::JS::JSFileResult { 150 test_path, 151 {}, 152 duration_ms, 153 test_result, 154 { Test::Suite { test_path, "Parse file", test_result, { { expectation_string, test_result, message, static_cast<u64>(duration_ms) * 1000u } } } } 155 }; 156}