ladybird/Libraries/LibJS/Runtime/FunctionConstructor.cpp
Andreas Kling 77cd434710 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.
2026-03-19 21:55:10 -05:00

238 lines
9.9 KiB
C++

/*
* Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/FunctionConstructor.h>
#include <LibJS/Runtime/FunctionObject.h>
#include <LibJS/Runtime/GeneratorPrototype.h>
#include <LibJS/Runtime/GlobalEnvironment.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Realm.h>
#include <LibJS/RustIntegration.h>
#include <LibJS/SourceCode.h>
namespace JS {
GC_DEFINE_ALLOCATOR(FunctionConstructor);
FunctionConstructor::FunctionConstructor(Realm& realm)
: NativeFunction(realm.vm().names.Function.as_string(), realm.intrinsics().function_prototype())
{
}
void FunctionConstructor::initialize(Realm& realm)
{
auto& vm = this->vm();
Base::initialize(realm);
// 20.2.2.2 Function.prototype, https://tc39.es/ecma262/#sec-function.prototype
define_direct_property(vm.names.prototype, realm.intrinsics().function_prototype(), 0);
define_direct_property(vm.names.length, Value(1), Attribute::Configurable);
}
// 20.2.1.1.1 CreateDynamicFunction ( constructor, newTarget, kind, parameterArgs, bodyArg ), https://tc39.es/ecma262/#sec-createdynamicfunction
ThrowCompletionOr<GC::Ref<ECMAScriptFunctionObject>> FunctionConstructor::create_dynamic_function(VM& vm, FunctionObject& constructor, FunctionObject* new_target, FunctionKind kind, ReadonlySpan<Value> parameter_args, Value body_arg)
{
// 1. If newTarget is undefined, set newTarget to constructor.
if (new_target == nullptr)
new_target = &constructor;
StringView prefix;
GC::Ref<Object> (Intrinsics::*fallback_prototype)() = nullptr;
switch (kind) {
// 2. If kind is normal, then
case FunctionKind::Normal:
// a. Let prefix be "function".
prefix = "function"sv;
// b. Let exprSym be the grammar symbol FunctionExpression.
// c. Let bodySym be the grammar symbol FunctionBody[~Yield, ~Await].
// d. Let parameterSym be the grammar symbol FormalParameters[~Yield, ~Await].
// e. Let fallbackProto be "%Function.prototype%".
fallback_prototype = &Intrinsics::function_prototype;
break;
// 3. Else if kind is generator, then
case FunctionKind::Generator:
// a. Let prefix be "function*".
prefix = "function*"sv;
// b. Let exprSym be the grammar symbol GeneratorExpression.
// c. Let bodySym be the grammar symbol GeneratorBody.
// d. Let parameterSym be the grammar symbol FormalParameters[+Yield, ~Await].
// e. Let fallbackProto be "%GeneratorFunction.prototype%".
fallback_prototype = &Intrinsics::generator_function_prototype;
break;
// 4. Else if kind is async, then
case FunctionKind::Async:
// a. Let prefix be "async function".
prefix = "async function"sv;
// b. Let exprSym be the grammar symbol AsyncFunctionExpression.
// c. Let bodySym be the grammar symbol AsyncFunctionBody.
// d. Let parameterSym be the grammar symbol FormalParameters[~Yield, +Await].
// e. Let fallbackProto be "%AsyncFunction.prototype%".
fallback_prototype = &Intrinsics::async_function_prototype;
break;
// 5. Else,
case FunctionKind::AsyncGenerator:
// a. Assert: kind is async-generator.
// b. Let prefix be "async function*".
prefix = "async function*"sv;
// c. Let exprSym be the grammar symbol AsyncGeneratorExpression.
// d. Let bodySym be the grammar symbol AsyncGeneratorBody.
// e. Let parameterSym be the grammar symbol FormalParameters[+Yield, +Await].
// f. Let fallbackProto be "%AsyncGeneratorFunction.prototype%".
fallback_prototype = &Intrinsics::async_generator_function_prototype;
break;
default:
VERIFY_NOT_REACHED();
}
// 6. Let argCount be the number of elements in parameterArgs.
auto arg_count = parameter_args.size();
// 7. Let parameterStrings be a new empty List.
Vector<String> parameter_strings;
parameter_strings.ensure_capacity(arg_count);
// 8. For each element arg of parameterArgs, do
for (auto const& parameter_value : parameter_args) {
// a. Append ? ToString(arg) to parameterStrings.
parameter_strings.unchecked_append(TRY(parameter_value.to_string(vm)));
}
// 9. Let bodyString be ? ToString(bodyArg).
auto body_string = TRY(body_arg.to_string(vm));
// 10. Let currentRealm be the current Realm Record.
auto& realm = *vm.current_realm();
// 11. Let P be the empty String.
String parameters_string;
// 12. If argCount > 0, then
if (arg_count > 0) {
// a. Set P to parameterStrings[0].
// b. Let k be 1.
// c. Repeat, while k < argCount,
// i. Let nextArgString be parameterStrings[k].
// ii. Set P to the string-concatenation of P, "," (a comma), and nextArgString.
// iii. Set k to k + 1.
parameters_string = MUST(String::join(',', parameter_strings));
}
// 13. Let bodyParseString be the string-concatenation of 0x000A (LINE FEED), bodyString, and 0x000A (LINE FEED).
auto body_parse_string = ByteString::formatted("\n{}\n", body_string);
// 14. Let sourceString be the string-concatenation of prefix, " anonymous(", P, 0x000A (LINE FEED), ") {", bodyParseString, and "}".
// 15. Let sourceText be StringToCodePoints(sourceString).
auto source_text = ByteString::formatted("{} anonymous({}\n) {{{}}}", prefix, parameters_string, body_parse_string);
// 16. Perform ? HostEnsureCanCompileStrings(currentRealm, parameterStrings, bodyString, sourceString, FUNCTION, parameterArgs, bodyArg).
TRY(vm.host_ensure_can_compile_strings(realm, parameter_strings, body_string, source_text, CompilationType::Function, parameter_args, body_arg));
GC::Ptr<SharedFunctionInstanceData> function_data;
auto rust_compilation = RustIntegration::compile_dynamic_function(vm, source_text, parameters_string, body_parse_string, kind);
if (!rust_compilation.has_value())
return vm.throw_completion<SyntaxError>("Failed to compile dynamic function"_string);
if (rust_compilation->is_error())
return vm.throw_completion<SyntaxError>(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));
// 26. Let env be currentRealm.[[GlobalEnv]].
auto& environment = realm.global_environment();
// 27. Let privateEnv be null.
PrivateEnvironment* private_environment = nullptr;
auto function = ECMAScriptFunctionObject::create_from_function_data(
realm,
*function_data,
&environment,
private_environment,
*prototype);
// FIXME: Remove the name argument from create() and do this instead.
// 29. Perform SetFunctionName(F, "anonymous").
// 30. If kind is generator, then
if (kind == FunctionKind::Generator) {
// a. Let prototype be OrdinaryObjectCreate(%GeneratorFunction.prototype.prototype%).
prototype = Object::create_prototype(realm, realm.intrinsics().generator_function_prototype_prototype());
// b. Perform ! DefinePropertyOrThrow(F, "prototype", PropertyDescriptor { [[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).
function->define_direct_property(vm.names.prototype, prototype, Attribute::Writable);
}
// 31. Else if kind is asyncGenerator, then
else if (kind == FunctionKind::AsyncGenerator) {
// a. Let prototype be OrdinaryObjectCreate(%AsyncGeneratorFunction.prototype.prototype%).
prototype = Object::create_prototype(realm, realm.intrinsics().async_generator_function_prototype_prototype());
// b. Perform ! DefinePropertyOrThrow(F, "prototype", PropertyDescriptor { [[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).
function->define_direct_property(vm.names.prototype, prototype, Attribute::Writable);
}
// 32. Else if kind is normal, perform MakeConstructor(F).
else if (kind == FunctionKind::Normal) {
// FIXME: Implement MakeConstructor
prototype = Object::create_prototype(realm, realm.intrinsics().object_prototype());
prototype->define_direct_property(vm.names.constructor, function, Attribute::Writable | Attribute::Configurable);
function->define_direct_property(vm.names.prototype, prototype, Attribute::Writable);
}
// 33. NOTE: Functions whose kind is async are not constructible and do not have a [[Construct]] internal method or a "prototype" property.
// 34. Return F.
return function;
}
// 20.2.1.1 Function ( p1, p2, … , pn, body ), https://tc39.es/ecma262/#sec-function-p1-p2-pn-body
ThrowCompletionOr<Value> FunctionConstructor::call()
{
return TRY(construct(*this));
}
// 20.2.1.1 Function ( ...parameterArgs, bodyArg ), https://tc39.es/ecma262/#sec-function-p1-p2-pn-body
ThrowCompletionOr<GC::Ref<Object>> FunctionConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
ReadonlySpan<Value> arguments = vm.running_execution_context().arguments_span();
ReadonlySpan<Value> parameter_args = arguments;
if (!parameter_args.is_empty())
parameter_args = parameter_args.slice(0, parameter_args.size() - 1);
// 1. Let C be the active function object.
auto* constructor = vm.active_function_object();
// 2. If bodyArg is not present, set bodyArg to the empty String.
Value body_arg = &vm.empty_string();
if (!arguments.is_empty())
body_arg = arguments.last();
// 3. Return ? CreateDynamicFunction(C, NewTarget, normal, parameterArgs, bodyArg).
return TRY(create_dynamic_function(vm, *constructor, &new_target, FunctionKind::Normal, parameter_args, body_arg));
}
}