Serenity Operating System
at master 141 lines 6.0 kB view raw
1/* 2 * Copyright (c) 2022, David Tuin <davidot@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <LibJS/AST.h> 8#include <LibJS/Bytecode/Generator.h> 9#include <LibJS/Bytecode/Interpreter.h> 10#include <LibJS/Interpreter.h> 11#include <LibJS/Runtime/VM.h> 12#include <LibJS/Script.h> 13#include <LibTest/TestCase.h> 14 15#define SETUP_AND_PARSE(source) \ 16 auto vm = JS::VM::create(); \ 17 auto ast_interpreter = JS::Interpreter::create<JS::GlobalObject>(*vm); \ 18 \ 19 auto script_or_error = JS::Script::parse(source##sv, ast_interpreter->realm()); \ 20 EXPECT(!script_or_error.is_error()); \ 21 \ 22 auto script = script_or_error.release_value(); \ 23 auto const& program = script->parse_node(); \ 24 JS::Bytecode::Interpreter bytecode_interpreter(ast_interpreter->realm()); 25 26#define EXPECT_NO_EXCEPTION(executable) \ 27 auto executable = MUST(JS::Bytecode::Generator::generate(program)); \ 28 auto result = bytecode_interpreter.run(*executable); \ 29 EXPECT(!result.is_error()); \ 30 if (result.is_error()) \ 31 dbgln("Error: {}", MUST(result.throw_completion().value()->to_deprecated_string(vm))); 32 33#define EXPECT_NO_EXCEPTION_WITH_OPTIMIZATIONS(executable) \ 34 auto& passes = JS::Bytecode::Interpreter::optimization_pipeline(); \ 35 passes.perform(*executable); \ 36 \ 37 auto result_with_optimizations = bytecode_interpreter.run(*executable); \ 38 \ 39 EXPECT(!result_with_optimizations.is_error()); \ 40 if (result_with_optimizations.is_error()) \ 41 dbgln("Error: {}", MUST(result_with_optimizations.throw_completion().value()->to_deprecated_string(vm))); 42 43#define EXPECT_NO_EXCEPTION_ALL(source) \ 44 SETUP_AND_PARSE("(() => {\n" source "\n})()") \ 45 EXPECT_NO_EXCEPTION(executable) \ 46 EXPECT_NO_EXCEPTION_WITH_OPTIMIZATIONS(executable) 47 48TEST_CASE(empty_program) 49{ 50 EXPECT_NO_EXCEPTION_ALL(""); 51} 52 53TEST_CASE(if_statement_pass) 54{ 55 EXPECT_NO_EXCEPTION_ALL("if (false) throw new Exception('failed');"); 56} 57 58TEST_CASE(if_statement_fail) 59{ 60 SETUP_AND_PARSE("if (true) throw new Exception('failed');"); 61 62 auto executable = MUST(JS::Bytecode::Generator::generate(program)); 63 auto result = bytecode_interpreter.run(*executable); 64 EXPECT(result.is_error()); 65} 66 67TEST_CASE(trivial_program) 68{ 69 EXPECT_NO_EXCEPTION_ALL("if (1 + 1 !== 2) throw new Exception('failed');"); 70} 71 72TEST_CASE(variables) 73{ 74 EXPECT_NO_EXCEPTION_ALL("var a = 1; \n" 75 "if (a + 1 !== 2) throw new Exception('failed'); "); 76} 77 78TEST_CASE(function_call) 79{ 80 EXPECT_NO_EXCEPTION_ALL("if (!isNaN(NaN)) throw new Exception('failed'); "); 81} 82 83TEST_CASE(function_delcaration_and_call) 84{ 85 EXPECT_NO_EXCEPTION_ALL("var passed = false; \n" 86 "function f() { passed = true; return 1; }\n" 87 "if (f() !== 1) throw new Exception('failed');\n" 88 // The passed !== true is needed as otherwise UBSAN 89 // complains about unaligned access, until that 90 // is fixed or ignored care must be taken to prevent such cases in tests. 91 "if (passed !== true) throw new Exception('failed');"); 92} 93 94TEST_CASE(generator_function_call) 95{ 96 EXPECT_NO_EXCEPTION_ALL("function *g() { yield 2; }\n" 97 "var gen = g();\n" 98 "var result = gen.next();\n" 99 "if (result.value !== 2) throw new Exception('failed');"); 100} 101 102TEST_CASE(loading_multiple_files) 103{ 104 // This is a testcase which is very much like test-js and test262 105 // which load some common files first and only then the actual test file. 106 107 SETUP_AND_PARSE("function f() { return 'hello'; }"); 108 109 { 110 EXPECT_NO_EXCEPTION(common_file_executable); 111 } 112 113 { 114 auto test_file_script_or_error = JS::Script::parse("if (f() !== 'hello') throw new Exception('failed'); "sv, ast_interpreter->realm()); 115 EXPECT(!test_file_script_or_error.is_error()); 116 117 auto test_file_script = test_file_script_or_error.release_value(); 118 auto const& test_file_program = test_file_script->parse_node(); 119 120 auto executable = MUST(JS::Bytecode::Generator::generate(test_file_program)); 121 auto result = bytecode_interpreter.run(*executable); 122 EXPECT(!result.is_error()); 123 } 124} 125 126TEST_CASE(catch_exception) 127{ 128 // FIXME: Currently it seems that try/catch with finally is broken so we test both at once. 129 EXPECT_NO_EXCEPTION_ALL("var hitCatch = false;\n" 130 "var hitFinally = false;\n" 131 "try {\n" 132 " a();\n" 133 "} catch (e) {\n" 134 " hitCatch = e instanceof ReferenceError;\n" 135 " !1\n" // This is here to fix the alignment issue until that is actually resolved. 136 "} finally {\n" 137 " hitFinally = true;\n" 138 "}\n" 139 "if (hitCatch !== true) throw new Exception('failed');\n" 140 "if (hitFinally !== true) throw new Exception('failed');"); 141}