/* * Copyright (c) 2020-2023, Linus Groh * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include 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> FunctionConstructor::create_dynamic_function(VM& vm, FunctionObject& constructor, FunctionObject* new_target, FunctionKind kind, ReadonlySpan 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 (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 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 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("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)); // 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 FunctionConstructor::call() { return TRY(construct(*this)); } // 20.2.1.1 Function ( ...parameterArgs, bodyArg ), https://tc39.es/ecma262/#sec-function-p1-p2-pn-body ThrowCompletionOr> FunctionConstructor::construct(FunctionObject& new_target) { auto& vm = this->vm(); ReadonlySpan arguments = vm.running_execution_context().arguments_span(); ReadonlySpan 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)); } }