From 77cd434710a370d965cea1d61c2d011017a634e1 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 19 Mar 2026 12:26:15 -0500 Subject: [PATCH] LibJS: Remove C++ compiler pipeline fallback paths Now that the Rust pipeline is the sole compilation path, remove all C++ parser/codegen fallback paths from the callers: - Script::parse() no longer falls back to C++ Parser - SourceTextModule::parse() no longer falls back to C++ Parser - perform_eval() no longer falls back to C++ Parser + Generator - create_dynamic_function() no longer falls back to C++ Parser - ShadowRealm eval no longer falls back to C++ Parser + Generator - Interpreter::run(Script&) no longer falls back to Generator Also remove the now-dead old constructors that took C++ AST nodes, the module_requests() helper, and AST dump code from js.cpp. --- Libraries/LibJS/Bytecode/Interpreter.cpp | 7 - .../LibJS/Runtime/AbstractOperations.cpp | 66 +--- .../LibJS/Runtime/FunctionConstructor.cpp | 74 +---- Libraries/LibJS/Runtime/ShadowRealm.cpp | 51 +-- Libraries/LibJS/Script.cpp | 92 +----- Libraries/LibJS/Script.h | 10 - Libraries/LibJS/SourceTextModule.cpp | 312 +----------------- Libraries/LibJS/SourceTextModule.h | 6 - Utilities/js.cpp | 9 +- 9 files changed, 47 insertions(+), 580 deletions(-) diff --git a/Libraries/LibJS/Bytecode/Interpreter.cpp b/Libraries/LibJS/Bytecode/Interpreter.cpp index 32b3aee54e..d5a901fe87 100644 --- a/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -117,13 +117,6 @@ ThrowCompletionOr Interpreter::run(Script& script_record, GC::Ptr executable = script_record.cached_executable(); - if (!executable && result.type() == Completion::Type::Normal) { - executable = JS::Bytecode::Generator::generate_from_ast_node(vm, *script_record.parse_node(), {}); - if (executable) { - script_record.cache_executable(*executable); - script_record.drop_ast(); - } - } if (executable && g_dump_bytecode) executable->dump(); diff --git a/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Libraries/LibJS/Runtime/AbstractOperations.cpp index 0fecf1aca4..78852e796d 100644 --- a/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -9,10 +9,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -674,51 +672,15 @@ ThrowCompletionOr perform_eval(VM& vm, Value x, CallerMode strict_caller, // g. If inDerivedConstructor is false, and body Contains SuperCall, throw a SyntaxError exception. // h. If inClassFieldInitializer is true, and ContainsArguments of body is true, throw a SyntaxError exception. - GC::Ptr executable; - bool strict_eval = false; - EvalDeclarationData eval_declaration_data; - auto rust_compilation = RustIntegration::compile_eval(*code_string, vm, strict_caller, in_function, in_method, in_derived_constructor, in_class_field_initializer); - if (rust_compilation.has_value()) { - if (rust_compilation->is_error()) - return vm.throw_completion(rust_compilation->release_error()); - auto& eval_result = rust_compilation->value(); - executable = eval_result.executable; - strict_eval = eval_result.is_strict_mode; - eval_declaration_data = move(eval_result.declaration_data); - } - - RefPtr cpp_program; - - if (!executable) { - Parser::EvalInitialState initial_state { - .in_eval_function_context = in_function, - .allow_super_property_lookup = in_method, - .allow_super_constructor_call = in_derived_constructor, - .in_class_field_initializer = in_class_field_initializer, - }; - - Parser parser(Lexer(SourceCode::create({}, code_string->utf16_string())), Program::Type::Script, move(initial_state)); - cpp_program = parser.parse_program(strict_caller == CallerMode::Strict); - - // b. If script is a List of errors, throw a SyntaxError exception. - if (parser.has_errors()) { - auto& error = parser.errors()[0]; - return vm.throw_completion(error.to_string()); - } - - // 14. If strictCaller is true, let strictEval be true. - if (strict_caller == CallerMode::Strict) - strict_eval = true; - // 15. Else, let strictEval be IsStrict of script. - else - strict_eval = cpp_program->is_strict_mode(); - - eval_declaration_data = EvalDeclarationData::create(vm, *cpp_program, strict_eval); - - // NB: Bytecode compilation is deferred until after EvalDeclarationInstantiation, - // which sets annex B flags on AST nodes that affect codegen. - } + if (!rust_compilation.has_value()) + return vm.throw_completion("Failed to compile eval code"_string); + if (rust_compilation->is_error()) + return vm.throw_completion(rust_compilation->release_error()); + auto& eval_result = rust_compilation->value(); + auto executable = eval_result.executable; + auto strict_eval = eval_result.is_strict_mode; + auto eval_declaration_data = move(eval_result.declaration_data); // 16. Let runningContext be the running execution context. // 17. NOTE: If direct is true, runningContext will be the execution context that performed the direct eval. If direct is false, runningContext will be the execution context for the invocation of the eval function. @@ -770,12 +732,6 @@ ThrowCompletionOr perform_eval(VM& vm, Value x, CallerMode strict_caller, // 30. Let result be Completion(EvalDeclarationInstantiation(body, varEnv, lexEnv, privateEnv, strictEval)). TRY(eval_declaration_instantiation(vm, eval_declaration_data, variable_environment, lexical_environment, private_environment, strict_eval)); - // Compile C++ AST after EDI, since EDI sets annex B flags on AST nodes. - if (cpp_program) { - executable = Bytecode::Generator::generate_from_ast_node(vm, *cpp_program, {}); - executable->name = "eval"_utf16_fly_string; - } - if (Bytecode::g_dump_bytecode) executable->dump(); @@ -815,16 +771,16 @@ ThrowCompletionOr perform_eval(VM& vm, Value x, CallerMode strict_caller, stack.deallocate(stack_mark); }; - Optional eval_result; + Optional result; - eval_result = TRY(vm.bytecode_interpreter().run_executable(*eval_context, *executable, {})); + result = TRY(vm.bytecode_interpreter().run_executable(*eval_context, *executable, {})); // 32. If result.[[Type]] is normal and result.[[Value]] is empty, then // a. Set result to NormalCompletion(undefined). // NOTE: Step 33 and 34 is handled by `pop_guard` above. // 35. Return ? result. // NOTE: Step 35 is also performed with each use of `TRY` above. - return eval_result.value_or(js_undefined()); + return result.value_or(js_undefined()); } EvalDeclarationData EvalDeclarationData::create(VM& vm, Program const& program, bool strict) diff --git a/Libraries/LibJS/Runtime/FunctionConstructor.cpp b/Libraries/LibJS/Runtime/FunctionConstructor.cpp index 5b2fc06ece..3542933fdb 100644 --- a/Libraries/LibJS/Runtime/FunctionConstructor.cpp +++ b/Libraries/LibJS/Runtime/FunctionConstructor.cpp @@ -4,8 +4,6 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include #include #include #include @@ -153,73 +151,11 @@ ThrowCompletionOr> FunctionConstructor::create GC::Ptr function_data; auto rust_compilation = RustIntegration::compile_dynamic_function(vm, source_text, parameters_string, body_parse_string, kind); - if (rust_compilation.has_value()) { - if (rust_compilation->is_error()) - return vm.throw_completion(rust_compilation->release_error()); - function_data = rust_compilation->value(); - } - - if (!function_data) { - u8 parse_options = FunctionNodeParseOptions::CheckForFunctionAndName; - if (kind == FunctionKind::Async || kind == FunctionKind::AsyncGenerator) - parse_options |= FunctionNodeParseOptions::IsAsyncFunction; - if (kind == FunctionKind::Generator || kind == FunctionKind::AsyncGenerator) - parse_options |= FunctionNodeParseOptions::IsGeneratorFunction; - - // 17. Let parameters be ParseText(P, parameterSym). - i32 function_length = 0; - auto parameters_parser = Parser(Lexer(SourceCode::create({}, Utf16String::from_utf8(parameters_string)))); - auto parameters = parameters_parser.parse_formal_parameters(function_length, parse_options); - - // 18. If parameters is a List of errors, throw a SyntaxError exception. - if (parameters_parser.has_errors()) { - auto error = parameters_parser.errors()[0]; - return vm.throw_completion(error.to_string()); - } - - // 19. Let body be ParseText(bodyParseString, bodySym). - FunctionParsingInsights parsing_insights; - auto body_parser = Parser::parse_function_body_from_string(body_parse_string, parse_options, parameters, kind, parsing_insights); - - // 20. If body is a List of errors, throw a SyntaxError exception. - if (body_parser.has_errors()) { - auto error = body_parser.errors()[0]; - return vm.throw_completion(error.to_string()); - } - - // 21. NOTE: The parameters and body are parsed separately to ensure that each is valid alone. For example, new Function("/*", "*/ ) {") does not evaluate to a function. - // 22. NOTE: If this step is reached, sourceText must have the syntax of exprSym (although the reverse implication does not hold). The purpose of the next two steps is to enforce any Early Error rules which apply to exprSym directly. - - // 23. Let expr be ParseText(sourceText, exprSym). - auto source_parser = Parser(Lexer(SourceCode::create({}, Utf16String::from_utf8(source_text)))); - // This doesn't need any parse_options, it determines those & the function type based on the tokens that were found. - auto expr = source_parser.parse_function_node(); - source_parser.run_scope_analysis(); - - // 24. If expr is a List of errors, throw a SyntaxError exception. - if (source_parser.has_errors()) { - auto error = source_parser.errors()[0]; - return vm.throw_completion(error.to_string()); - } - - // 28. Let F be OrdinaryFunctionCreate(proto, sourceText, parameters, body, non-lexical-this, env, privateEnv). - parsing_insights.might_need_arguments_object = true; - - function_data = vm.heap().allocate( - vm, - expr->kind(), - "anonymous"_utf16_fly_string, - expr->function_length(), - expr->parameters(), - expr->body(), - Utf16View {}, - expr->is_strict_mode(), - false, - parsing_insights, - expr->local_variables_names()); - function_data->m_source_text_owner = Utf16String::from_utf8(source_text); - function_data->m_source_text = function_data->m_source_text_owner.utf16_view(); - } + if (!rust_compilation.has_value()) + return vm.throw_completion("Failed to compile dynamic function"_string); + if (rust_compilation->is_error()) + return vm.throw_completion(rust_compilation->release_error()); + function_data = rust_compilation->value(); // 25. Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto). auto* prototype = TRY(get_prototype_from_constructor(vm, *new_target, fallback_prototype)); diff --git a/Libraries/LibJS/Runtime/ShadowRealm.cpp b/Libraries/LibJS/Runtime/ShadowRealm.cpp index dca4bcaa8b..2ee1644974 100644 --- a/Libraries/LibJS/Runtime/ShadowRealm.cpp +++ b/Libraries/LibJS/Runtime/ShadowRealm.cpp @@ -6,8 +6,6 @@ #include #include -#include -#include #include #include #include @@ -124,48 +122,15 @@ ThrowCompletionOr perform_shadow_realm_eval(VM& vm, Value source, Realm& // 2. Perform the following substeps in an implementation-defined order, possibly interleaving parsing and error detection: - GC::Ptr executable; - bool strict_eval = false; - EvalDeclarationData eval_declaration_data; - auto rust_compilation = RustIntegration::compile_shadow_realm_eval(*source_text, vm); - if (rust_compilation.has_value()) { - if (rust_compilation->is_error()) - return vm.throw_completion(rust_compilation->release_error()); - auto& eval_result = rust_compilation->value(); - executable = eval_result.executable; - strict_eval = eval_result.is_strict_mode; - eval_declaration_data = move(eval_result.declaration_data); - } - - if (!executable) { - // a. Let script be ParseText(StringToCodePoints(sourceText), Script). - auto parser = Parser(Lexer(SourceCode::create({}, source_text->utf16_string())), Program::Type::Script, Parser::EvalInitialState {}); - auto program = parser.parse_program(); - - // b. If script is a List of errors, throw a SyntaxError exception. - if (parser.has_errors()) { - auto& error = parser.errors()[0]; - return vm.throw_completion(error.to_string()); - } - - // c. If script Contains ScriptBody is false, return undefined. - if (program->children().is_empty()) - return js_undefined(); - - // d. Let body be the ScriptBody of script. - // e. If body Contains NewTarget is true, throw a SyntaxError exception. - // f. If body Contains SuperProperty is true, throw a SyntaxError exception. - // g. If body Contains SuperCall is true, throw a SyntaxError exception. - // FIXME: Implement these, we probably need a generic way of scanning the AST for certain nodes. - - // 3. Let strictEval be IsStrict of script. - strict_eval = program->is_strict_mode(); - - eval_declaration_data = EvalDeclarationData::create(vm, program, strict_eval); - - executable = Bytecode::compile(vm, program, FunctionKind::Normal, "ShadowRealmEval"_utf16_fly_string); - } + if (!rust_compilation.has_value()) + return vm.throw_completion("Failed to compile ShadowRealm eval code"_string); + if (rust_compilation->is_error()) + return vm.throw_completion(rust_compilation->release_error()); + auto& compilation_result = rust_compilation->value(); + auto executable = compilation_result.executable; + auto strict_eval = compilation_result.is_strict_mode; + auto eval_declaration_data = move(compilation_result.declaration_data); // 4. Let runningContext be the running execution context. // 5. If runningContext is not already suspended, suspend runningContext. diff --git a/Libraries/LibJS/Script.cpp b/Libraries/LibJS/Script.cpp index 083bfee8f7..5bbb4b41bd 100644 --- a/Libraries/LibJS/Script.cpp +++ b/Libraries/LibJS/Script.cpp @@ -4,10 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include -#include -#include #include #include #include @@ -27,22 +24,11 @@ GC_DEFINE_ALLOCATOR(Script); Result, Vector> Script::parse(StringView source_text, Realm& realm, StringView filename, HostDefined* host_defined, size_t line_number_offset) { auto rust_compilation = RustIntegration::compile_script(source_text, realm, filename, line_number_offset); - if (rust_compilation.has_value()) { - if (rust_compilation->is_error()) - return rust_compilation->release_error(); - return realm.heap().allocate