ladybird/Libraries/LibJS/Runtime/FunctionConstructor.cpp
Andreas Kling 7025dd1fa7 Libraries: Parse JS strings from UTF-16
Thread UTF-16 string input through JSON, script parsing, Date parsing,
Intl option parsing, Temporal parsing, and the helper library boundaries
that feed those parsers. Preserve ASCII fast paths where the source data
is known to be ASCII.
2026-06-22 19:51:25 +02: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<Utf16String> 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_utf16_string(vm)));
}
// 9. Let bodyString be ? ToString(bodyArg).
auto body_string = TRY(body_arg.to_utf16_string(vm));
// 10. Let currentRealm be the current Realm Record.
auto& realm = *vm.current_realm();
// 11. Let P be the empty String.
Utf16String 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 = Utf16String::join(',', parameter_strings);
}
// 13. Let bodyParseString be the string-concatenation of 0x000A (LINE FEED), bodyString, and 0x000A (LINE FEED).
auto body_parse_string = Utf16String::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 = Utf16String::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));
}
}