Coverage Report

Created: 2025-03-04 07:22

/src/serenity/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <LibJS/Lexer.h>
8
#include <LibJS/Parser.h>
9
#include <LibJS/Runtime/AbstractOperations.h>
10
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
11
#include <LibJS/Runtime/Error.h>
12
#include <LibJS/Runtime/FunctionConstructor.h>
13
#include <LibJS/Runtime/FunctionObject.h>
14
#include <LibJS/Runtime/GeneratorPrototype.h>
15
#include <LibJS/Runtime/GlobalEnvironment.h>
16
#include <LibJS/Runtime/GlobalObject.h>
17
#include <LibJS/Runtime/Realm.h>
18
19
namespace JS {
20
21
JS_DEFINE_ALLOCATOR(FunctionConstructor);
22
23
FunctionConstructor::FunctionConstructor(Realm& realm)
24
62
    : NativeFunction(realm.vm().names.Function.as_string(), realm.intrinsics().function_prototype())
25
62
{
26
62
}
27
28
void FunctionConstructor::initialize(Realm& realm)
29
62
{
30
62
    auto& vm = this->vm();
31
62
    Base::initialize(realm);
32
33
    // 20.2.2.2 Function.prototype, https://tc39.es/ecma262/#sec-function.prototype
34
62
    define_direct_property(vm.names.prototype, realm.intrinsics().function_prototype(), 0);
35
36
62
    define_direct_property(vm.names.length, Value(1), Attribute::Configurable);
37
62
}
38
39
// NON-STANDARD: Exists to simplify calling CreateDynamicFunction using strong types, instead of a Value.
40
// Analogous to parts of the following two AO's - and basically just extracts the body and parameters as strings.
41
//
42
// 20.2.1.1 Function ( ...parameterArgs, bodyArg ), https://tc39.es/ecma262/#sec-function-p1-p2-pn-body
43
// 20.2.1.1.1 CreateDynamicFunction ( constructor, newTarget, kind, parameterArgs, bodyArg ), https://tc39.es/ecma262/#sec-createdynamicfunction
44
ThrowCompletionOr<ParameterArgumentsAndBody> extract_parameter_arguments_and_body(VM& vm, Span<Value> arguments)
45
0
{
46
0
    if (arguments.is_empty())
47
0
        return ParameterArgumentsAndBody {};
48
49
0
    auto parameter_values = arguments.slice(0, arguments.size() - 1);
50
51
0
    Vector<String> parameters;
52
0
    parameters.ensure_capacity(parameter_values.size());
53
0
    for (auto const& parameter_value : parameter_values)
54
0
        parameters.unchecked_append(TRY(parameter_value.to_string(vm)));
55
56
0
    auto body = TRY(arguments.last().to_string(vm));
57
58
0
    return ParameterArgumentsAndBody {
59
0
        .parameters = move(parameters),
60
0
        .body = move(body),
61
0
    };
62
0
}
63
64
// 20.2.1.1.1 CreateDynamicFunction ( constructor, newTarget, kind, parameterArgs, bodyArg ), https://tc39.es/ecma262/#sec-createdynamicfunction
65
ThrowCompletionOr<NonnullGCPtr<ECMAScriptFunctionObject>> FunctionConstructor::create_dynamic_function(VM& vm, FunctionObject& constructor, FunctionObject* new_target, FunctionKind kind, ReadonlySpan<String> parameter_strings, String const& body_string)
66
0
{
67
    // 1. If newTarget is undefined, set newTarget to constructor.
68
0
    if (new_target == nullptr)
69
0
        new_target = &constructor;
70
71
0
    StringView prefix;
72
0
    NonnullGCPtr<Object> (Intrinsics::*fallback_prototype)() = nullptr;
73
74
0
    switch (kind) {
75
    // 2. If kind is normal, then
76
0
    case FunctionKind::Normal:
77
        // a. Let prefix be "function".
78
0
        prefix = "function"sv;
79
80
        // b. Let exprSym be the grammar symbol FunctionExpression.
81
        // c. Let bodySym be the grammar symbol FunctionBody[~Yield, ~Await].
82
        // d. Let parameterSym be the grammar symbol FormalParameters[~Yield, ~Await].
83
84
        // e. Let fallbackProto be "%Function.prototype%".
85
0
        fallback_prototype = &Intrinsics::function_prototype;
86
0
        break;
87
88
    // 3. Else if kind is generator, then
89
0
    case FunctionKind::Generator:
90
        // a. Let prefix be "function*".
91
0
        prefix = "function*"sv;
92
93
        // b. Let exprSym be the grammar symbol GeneratorExpression.
94
        // c. Let bodySym be the grammar symbol GeneratorBody.
95
        // d. Let parameterSym be the grammar symbol FormalParameters[+Yield, ~Await].
96
97
        // e. Let fallbackProto be "%GeneratorFunction.prototype%".
98
0
        fallback_prototype = &Intrinsics::generator_function_prototype;
99
0
        break;
100
101
    // 4. Else if kind is async, then
102
0
    case FunctionKind::Async:
103
        // a. Let prefix be "async function".
104
0
        prefix = "async function"sv;
105
106
        // b. Let exprSym be the grammar symbol AsyncFunctionExpression.
107
        // c. Let bodySym be the grammar symbol AsyncFunctionBody.
108
        // d. Let parameterSym be the grammar symbol FormalParameters[~Yield, +Await].
109
110
        // e. Let fallbackProto be "%AsyncFunction.prototype%".
111
0
        fallback_prototype = &Intrinsics::async_function_prototype;
112
0
        break;
113
114
    // 5. Else,
115
0
    case FunctionKind::AsyncGenerator:
116
        // a. Assert: kind is async-generator.
117
118
        // b. Let prefix be "async function*".
119
0
        prefix = "async function*"sv;
120
121
        // c. Let exprSym be the grammar symbol AsyncGeneratorExpression.
122
        // d. Let bodySym be the grammar symbol AsyncGeneratorBody.
123
        // e. Let parameterSym be the grammar symbol FormalParameters[+Yield, +Await].
124
125
        // f. Let fallbackProto be "%AsyncGeneratorFunction.prototype%".
126
0
        fallback_prototype = &Intrinsics::async_generator_function_prototype;
127
0
        break;
128
129
0
    default:
130
0
        VERIFY_NOT_REACHED();
131
0
    }
132
133
    // 6. Let argCount be the number of elements in parameterArgs.
134
0
    auto arg_count = parameter_strings.size();
135
136
    // NOTE: Done by caller
137
    // 7. Let parameterStrings be a new empty List.
138
    // 8. For each element arg of parameterArgs, do
139
    //     a. Append ? ToString(arg) to parameterStrings.
140
    // 9. Let bodyString be ? ToString(bodyArg).
141
142
    // 10. Let currentRealm be the current Realm Record.
143
0
    auto& realm = *vm.current_realm();
144
145
    // 11. Perform ? HostEnsureCanCompileStrings(currentRealm, parameterStrings, bodyString, false).
146
0
    TRY(vm.host_ensure_can_compile_strings(realm, parameter_strings, body_string, EvalMode::Indirect));
147
148
    // 12. Let P be the empty String.
149
0
    String parameters_string;
150
151
    // 13. If argCount > 0, then
152
0
    if (arg_count > 0) {
153
        // a. Set P to parameterStrings[0].
154
        // b. Let k be 1.
155
        // c. Repeat, while k < argCount,
156
        //     i. Let nextArgString be parameterStrings[k].
157
        //     ii. Set P to the string-concatenation of P, "," (a comma), and nextArgString.
158
        //     iii. Set k to k + 1.
159
0
        parameters_string = MUST(String::join(',', parameter_strings));
160
0
    }
161
162
    // 14. Let bodyParseString be the string-concatenation of 0x000A (LINE FEED), bodyString, and 0x000A (LINE FEED).
163
0
    auto body_parse_string = ByteString::formatted("\n{}\n", body_string);
164
165
    // 15. Let sourceString be the string-concatenation of prefix, " anonymous(", P, 0x000A (LINE FEED), ") {", bodyParseString, and "}".
166
    // 16. Let sourceText be StringToCodePoints(sourceString).
167
0
    auto source_text = ByteString::formatted("{} anonymous({}\n) {{{}}}", prefix, parameters_string, body_parse_string);
168
169
0
    u8 parse_options = FunctionNodeParseOptions::CheckForFunctionAndName;
170
0
    if (kind == FunctionKind::Async || kind == FunctionKind::AsyncGenerator)
171
0
        parse_options |= FunctionNodeParseOptions::IsAsyncFunction;
172
0
    if (kind == FunctionKind::Generator || kind == FunctionKind::AsyncGenerator)
173
0
        parse_options |= FunctionNodeParseOptions::IsGeneratorFunction;
174
175
    // 17. Let parameters be ParseText(P, parameterSym).
176
0
    i32 function_length = 0;
177
0
    auto parameters_parser = Parser { Lexer { parameters_string } };
178
0
    auto parameters = parameters_parser.parse_formal_parameters(function_length, parse_options);
179
180
    // 18. If parameters is a List of errors, throw a SyntaxError exception.
181
0
    if (parameters_parser.has_errors()) {
182
0
        auto error = parameters_parser.errors()[0];
183
0
        return vm.throw_completion<SyntaxError>(error.to_string());
184
0
    }
185
186
    // 19. Let body be ParseText(bodyParseString, bodySym).
187
0
    FunctionParsingInsights parsing_insights;
188
0
    auto body_parser = Parser::parse_function_body_from_string(body_parse_string, parse_options, parameters, kind, parsing_insights);
189
190
    // 20. If body is a List of errors, throw a SyntaxError exception.
191
0
    if (body_parser.has_errors()) {
192
0
        auto error = body_parser.errors()[0];
193
0
        return vm.throw_completion<SyntaxError>(error.to_string());
194
0
    }
195
196
    // 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.
197
    // 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.
198
199
    // 23. Let expr be ParseText(sourceText, exprSym).
200
0
    auto source_parser = Parser { Lexer { source_text } };
201
    // This doesn't need any parse_options, it determines those & the function type based on the tokens that were found.
202
0
    auto expr = source_parser.parse_function_node<FunctionExpression>();
203
204
    // 24. If expr is a List of errors, throw a SyntaxError exception.
205
0
    if (source_parser.has_errors()) {
206
0
        auto error = source_parser.errors()[0];
207
0
        return vm.throw_completion<SyntaxError>(error.to_string());
208
0
    }
209
210
    // 25. Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
211
0
    auto* prototype = TRY(get_prototype_from_constructor(vm, *new_target, fallback_prototype));
212
213
    // 26. Let env be currentRealm.[[GlobalEnv]].
214
0
    auto& environment = realm.global_environment();
215
216
    // 27. Let privateEnv be null.
217
0
    PrivateEnvironment* private_environment = nullptr;
218
219
    // 28. Let F be OrdinaryFunctionCreate(proto, sourceText, parameters, body, non-lexical-this, env, privateEnv).
220
0
    parsing_insights.might_need_arguments_object = true;
221
0
    auto function = ECMAScriptFunctionObject::create(realm, "anonymous", *prototype, move(source_text), expr->body(), expr->parameters(), expr->function_length(), expr->local_variables_names(), &environment, private_environment, expr->kind(), expr->is_strict_mode(), parsing_insights);
222
223
    // FIXME: Remove the name argument from create() and do this instead.
224
    // 29. Perform SetFunctionName(F, "anonymous").
225
226
    // 30. If kind is generator, then
227
0
    if (kind == FunctionKind::Generator) {
228
        // a. Let prototype be OrdinaryObjectCreate(%GeneratorFunction.prototype.prototype%).
229
0
        prototype = Object::create_prototype(realm, realm.intrinsics().generator_function_prototype_prototype());
230
231
        // b. Perform ! DefinePropertyOrThrow(F, "prototype", PropertyDescriptor { [[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).
232
0
        function->define_direct_property(vm.names.prototype, prototype, Attribute::Writable);
233
0
    }
234
    // 31. Else if kind is asyncGenerator, then
235
0
    else if (kind == FunctionKind::AsyncGenerator) {
236
        // a. Let prototype be OrdinaryObjectCreate(%AsyncGeneratorFunction.prototype.prototype%).
237
0
        prototype = Object::create_prototype(realm, realm.intrinsics().async_generator_function_prototype_prototype());
238
239
        // b. Perform ! DefinePropertyOrThrow(F, "prototype", PropertyDescriptor { [[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).
240
0
        function->define_direct_property(vm.names.prototype, prototype, Attribute::Writable);
241
0
    }
242
    // 32. Else if kind is normal, perform MakeConstructor(F).
243
0
    else if (kind == FunctionKind::Normal) {
244
        // FIXME: Implement MakeConstructor
245
0
        prototype = Object::create_prototype(realm, realm.intrinsics().object_prototype());
246
0
        prototype->define_direct_property(vm.names.constructor, function, Attribute::Writable | Attribute::Configurable);
247
0
        function->define_direct_property(vm.names.prototype, prototype, Attribute::Writable);
248
0
    }
249
250
    // 33. NOTE: Functions whose kind is async are not constructible and do not have a [[Construct]] internal method or a "prototype" property.
251
252
    // 34. Return F.
253
0
    return function;
254
0
}
255
256
// 20.2.1.1 Function ( p1, p2, … , pn, body ), https://tc39.es/ecma262/#sec-function-p1-p2-pn-body
257
ThrowCompletionOr<Value> FunctionConstructor::call()
258
0
{
259
0
    return TRY(construct(*this));
260
0
}
261
262
// 20.2.1.1 Function ( ...parameterArgs, bodyArg ), https://tc39.es/ecma262/#sec-function-p1-p2-pn-body
263
ThrowCompletionOr<NonnullGCPtr<Object>> FunctionConstructor::construct(FunctionObject& new_target)
264
0
{
265
0
    auto& vm = this->vm();
266
267
    // 1. Let C be the active function object.
268
0
    auto* constructor = vm.active_function_object();
269
270
    // 2. If bodyArg is not present, set bodyArg to the empty String.
271
    // NOTE: This does that, as well as the string extraction done inside of CreateDynamicFunction
272
0
    auto extracted = TRY(extract_parameter_arguments_and_body(vm, vm.running_execution_context().arguments));
273
274
    // 3. Return ? CreateDynamicFunction(C, NewTarget, normal, parameterArgs, bodyArg).
275
0
    return TRY(create_dynamic_function(vm, *constructor, &new_target, FunctionKind::Normal, extracted.parameters, extracted.body));
276
0
}
277
278
}