mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-05 01:10:24 +00:00
LibJS: Introduce NativeJavaScriptBackedFunction
This hosts the ability to compile and run JavaScript to implement native functions. This is particularly useful for any native function that is not a normal function, for example async functions such as Array.fromAsync, which require yielding. These functions are not allowed to observe anything from outside their environment. Any global identifiers will instead be assumed to be a reference to an abstract operation or a constant. The generator will inject the appropriate bytecode if the name of the global identifier matches a known name. Anything else will cause a code generation error.
This commit is contained in:
committed by
Andreas Kling
parent
899c6ebffc
commit
a63b0cfaba
Notes:
github-actions[bot]
2025-11-30 10:56:11 +00:00
Author: https://github.com/Lubrsi Commit: https://github.com/LadybirdBrowser/ladybird/commit/a63b0cfaba6 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6728 Reviewed-by: https://github.com/Hendiadyoin1 Reviewed-by: https://github.com/awesomekling
@@ -474,8 +474,10 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> Identifier::generate_by
|
||||
return local;
|
||||
}
|
||||
|
||||
if (is_global() && m_string == "undefined"sv) {
|
||||
return generator.add_constant(js_undefined());
|
||||
if (is_global()) {
|
||||
auto maybe_constant = TRY(generator.maybe_generate_builtin_constant(*this));
|
||||
if (maybe_constant.has_value())
|
||||
return maybe_constant.release_value();
|
||||
}
|
||||
|
||||
auto dst = choose_dst(generator, preferred_dst);
|
||||
@@ -1718,6 +1720,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> CallExpression::generat
|
||||
|
||||
Optional<ScopedOperand> original_callee;
|
||||
auto original_this_value = generator.add_constant(js_undefined());
|
||||
auto dst = choose_dst(generator, preferred_dst);
|
||||
Bytecode::Op::CallType call_type = Bytecode::Op::CallType::Call;
|
||||
|
||||
if (is<NewExpression>(this)) {
|
||||
@@ -1741,6 +1744,11 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> CallExpression::generat
|
||||
// NOTE: If the identifier refers to a known "local" or "global", we know it can't be
|
||||
// a `with` binding, so we can skip this.
|
||||
auto& identifier = static_cast<Identifier const&>(*m_callee);
|
||||
if (generator.builtin_abstract_operations_enabled() && identifier.is_global()) {
|
||||
TRY(generator.generate_builtin_abstract_operation(identifier, arguments(), dst));
|
||||
return dst;
|
||||
}
|
||||
|
||||
if (identifier.string() == "eval"sv) {
|
||||
call_type = Bytecode::Op::CallType::DirectEval;
|
||||
}
|
||||
@@ -1776,7 +1784,6 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> CallExpression::generat
|
||||
expression_string_index = generator.intern_string(expression_string.release_value());
|
||||
|
||||
bool has_spread = any_of(arguments(), [](auto& argument) { return argument.is_spread; });
|
||||
auto dst = choose_dst(generator, preferred_dst);
|
||||
|
||||
if (has_spread) {
|
||||
auto arguments = TRY(arguments_to_array_for_call(generator, this->arguments())).value();
|
||||
|
||||
16
Libraries/LibJS/Bytecode/BuiltinAbstractOperationsEnabled.h
Normal file
16
Libraries/LibJS/Bytecode/BuiltinAbstractOperationsEnabled.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
enum class BuiltinAbstractOperationsEnabled : bool {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
}
|
||||
@@ -13,11 +13,12 @@
|
||||
#include <LibJS/Bytecode/Op.h>
|
||||
#include <LibJS/Bytecode/Register.h>
|
||||
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
||||
#include <LibJS/Runtime/NativeJavaScriptBackedFunction.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
Generator::Generator(VM& vm, GC::Ptr<SharedFunctionInstanceData const> shared_function_instance_data, MustPropagateCompletion must_propagate_completion)
|
||||
Generator::Generator(VM& vm, GC::Ptr<SharedFunctionInstanceData const> shared_function_instance_data, MustPropagateCompletion must_propagate_completion, BuiltinAbstractOperationsEnabled builtin_abstract_operations_enabled)
|
||||
: m_vm(vm)
|
||||
, m_string_table(make<StringTable>())
|
||||
, m_identifier_table(make<IdentifierTable>())
|
||||
@@ -26,6 +27,7 @@ Generator::Generator(VM& vm, GC::Ptr<SharedFunctionInstanceData const> shared_fu
|
||||
, m_accumulator(*this, Operand(Register::accumulator()))
|
||||
, m_this_value(*this, Operand(Register::this_value()))
|
||||
, m_must_propagate_completion(must_propagate_completion == MustPropagateCompletion::Yes)
|
||||
, m_builtin_abstract_operations_enabled(builtin_abstract_operations_enabled == BuiltinAbstractOperationsEnabled::Yes)
|
||||
, m_shared_function_instance_data(shared_function_instance_data)
|
||||
{
|
||||
}
|
||||
@@ -215,9 +217,9 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(S
|
||||
return {};
|
||||
}
|
||||
|
||||
CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind, GC::Ptr<SharedFunctionInstanceData const> shared_function_instance_data, MustPropagateCompletion must_propagate_completion, Vector<LocalVariable> local_variable_names)
|
||||
CodeGenerationErrorOr<GC::Ref<Executable>> Generator::compile(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind, GC::Ptr<SharedFunctionInstanceData const> shared_function_instance_data, MustPropagateCompletion must_propagate_completion, BuiltinAbstractOperationsEnabled builtin_abstract_operations_enabled, Vector<LocalVariable> local_variable_names)
|
||||
{
|
||||
Generator generator(vm, shared_function_instance_data, must_propagate_completion);
|
||||
Generator generator(vm, shared_function_instance_data, must_propagate_completion, builtin_abstract_operations_enabled);
|
||||
|
||||
if (is<Program>(node))
|
||||
generator.m_strict = static_cast<Program const&>(node).is_strict_mode() ? Strict::Yes : Strict::No;
|
||||
@@ -498,12 +500,12 @@ CodeGenerationErrorOr<GC::Ref<Executable>> Generator::generate_from_ast_node(VM&
|
||||
Vector<LocalVariable> local_variable_names;
|
||||
if (is<ScopeNode>(node))
|
||||
local_variable_names = static_cast<ScopeNode const&>(node).local_variables_names();
|
||||
return compile(vm, node, enclosing_function_kind, {}, MustPropagateCompletion::Yes, move(local_variable_names));
|
||||
return compile(vm, node, enclosing_function_kind, {}, MustPropagateCompletion::Yes, BuiltinAbstractOperationsEnabled::No, move(local_variable_names));
|
||||
}
|
||||
|
||||
CodeGenerationErrorOr<GC::Ref<Executable>> Generator::generate_from_function(VM& vm, GC::Ref<SharedFunctionInstanceData const> shared_function_instance_data)
|
||||
CodeGenerationErrorOr<GC::Ref<Executable>> Generator::generate_from_function(VM& vm, GC::Ref<SharedFunctionInstanceData const> shared_function_instance_data, BuiltinAbstractOperationsEnabled builtin_abstract_operations_enabled)
|
||||
{
|
||||
return compile(vm, *shared_function_instance_data->m_ecmascript_code, shared_function_instance_data->m_kind, shared_function_instance_data, MustPropagateCompletion::No, shared_function_instance_data->m_local_variables_names);
|
||||
return compile(vm, *shared_function_instance_data->m_ecmascript_code, shared_function_instance_data->m_kind, shared_function_instance_data, MustPropagateCompletion::No, builtin_abstract_operations_enabled, shared_function_instance_data->m_local_variables_names);
|
||||
}
|
||||
|
||||
void Generator::grow(size_t additional_size)
|
||||
@@ -1470,4 +1472,43 @@ ScopedOperand Generator::add_constant(Value value)
|
||||
return append_new_constant();
|
||||
}
|
||||
|
||||
CodeGenerationErrorOr<void> Generator::generate_builtin_abstract_operation(Identifier const& builtin_identifier, ReadonlySpan<CallExpression::Argument> arguments, ScopedOperand const&)
|
||||
{
|
||||
VERIFY(m_builtin_abstract_operations_enabled);
|
||||
for (auto const& argument : arguments) {
|
||||
if (argument.is_spread) {
|
||||
return CodeGenerationError {
|
||||
argument.value.ptr(),
|
||||
"Spread arguments not allowed for builtin abstract operations"sv,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
auto const& operation_name = builtin_identifier.string();
|
||||
|
||||
dbgln("Unknown builtin abstract operation: '{}'", operation_name);
|
||||
return CodeGenerationError {
|
||||
&builtin_identifier,
|
||||
"Unknown builtin abstract operation"sv,
|
||||
};
|
||||
}
|
||||
|
||||
CodeGenerationErrorOr<Optional<ScopedOperand>> Generator::maybe_generate_builtin_constant(Identifier const& builtin_identifier)
|
||||
{
|
||||
auto const& constant_name = builtin_identifier.string();
|
||||
|
||||
if (constant_name == "undefined"sv) {
|
||||
return add_constant(js_undefined());
|
||||
}
|
||||
|
||||
if (!m_builtin_abstract_operations_enabled)
|
||||
return OptionalNone {};
|
||||
|
||||
dbgln("Unknown builtin constant: '{}'", constant_name);
|
||||
return CodeGenerationError {
|
||||
&builtin_identifier,
|
||||
"Unknown builtin constant"sv,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <AK/SinglyLinkedList.h>
|
||||
#include <LibJS/AST.h>
|
||||
#include <LibJS/Bytecode/BasicBlock.h>
|
||||
#include <LibJS/Bytecode/BuiltinAbstractOperationsEnabled.h>
|
||||
#include <LibJS/Bytecode/CodeGenerationError.h>
|
||||
#include <LibJS/Bytecode/Executable.h>
|
||||
#include <LibJS/Bytecode/IdentifierTable.h>
|
||||
@@ -40,7 +41,7 @@ public:
|
||||
};
|
||||
|
||||
static CodeGenerationErrorOr<GC::Ref<Executable>> generate_from_ast_node(VM&, ASTNode const&, FunctionKind = FunctionKind::Normal);
|
||||
static CodeGenerationErrorOr<GC::Ref<Executable>> generate_from_function(VM&, GC::Ref<SharedFunctionInstanceData const> shared_function_instance_data);
|
||||
static CodeGenerationErrorOr<GC::Ref<Executable>> generate_from_function(VM&, GC::Ref<SharedFunctionInstanceData const> shared_function_instance_data, BuiltinAbstractOperationsEnabled builtin_abstract_operations_enabled = BuiltinAbstractOperationsEnabled::No);
|
||||
|
||||
CodeGenerationErrorOr<void> emit_function_declaration_instantiation(SharedFunctionInstanceData const& shared_function_instance_data);
|
||||
|
||||
@@ -361,10 +362,15 @@ public:
|
||||
|
||||
[[nodiscard]] bool must_propagate_completion() const { return m_must_propagate_completion; }
|
||||
|
||||
[[nodiscard]] bool builtin_abstract_operations_enabled() const { return m_builtin_abstract_operations_enabled; }
|
||||
|
||||
CodeGenerationErrorOr<void> generate_builtin_abstract_operation(Identifier const& builtin_identifier, ReadonlySpan<CallExpression::Argument> arguments, ScopedOperand const& dst);
|
||||
CodeGenerationErrorOr<Optional<ScopedOperand>> maybe_generate_builtin_constant(Identifier const& builtin_identifier);
|
||||
|
||||
private:
|
||||
VM& m_vm;
|
||||
|
||||
static CodeGenerationErrorOr<GC::Ref<Executable>> compile(VM&, ASTNode const&, FunctionKind, GC::Ptr<SharedFunctionInstanceData const>, MustPropagateCompletion, Vector<LocalVariable> local_variable_names);
|
||||
static CodeGenerationErrorOr<GC::Ref<Executable>> compile(VM&, ASTNode const&, FunctionKind, GC::Ptr<SharedFunctionInstanceData const>, MustPropagateCompletion, BuiltinAbstractOperationsEnabled, Vector<LocalVariable> local_variable_names);
|
||||
|
||||
enum class JumpType {
|
||||
Continue,
|
||||
@@ -373,7 +379,7 @@ private:
|
||||
void generate_scoped_jump(JumpType);
|
||||
void generate_labelled_jump(JumpType, FlyString const& label);
|
||||
|
||||
Generator(VM&, GC::Ptr<SharedFunctionInstanceData const>, MustPropagateCompletion);
|
||||
Generator(VM&, GC::Ptr<SharedFunctionInstanceData const>, MustPropagateCompletion, BuiltinAbstractOperationsEnabled);
|
||||
~Generator() = default;
|
||||
|
||||
void grow(size_t);
|
||||
@@ -426,6 +432,7 @@ private:
|
||||
|
||||
bool m_finished { false };
|
||||
bool m_must_propagate_completion { true };
|
||||
bool m_builtin_abstract_operations_enabled { false };
|
||||
|
||||
GC::Ptr<SharedFunctionInstanceData const> m_shared_function_instance_data;
|
||||
|
||||
|
||||
@@ -739,11 +739,11 @@ ThrowCompletionOr<GC::Ref<Bytecode::Executable>> compile(VM& vm, ASTNode const&
|
||||
return bytecode_executable;
|
||||
}
|
||||
|
||||
ThrowCompletionOr<GC::Ref<Bytecode::Executable>> compile(VM& vm, GC::Ref<SharedFunctionInstanceData const> shared_function_instance_data)
|
||||
ThrowCompletionOr<GC::Ref<Bytecode::Executable>> compile(VM& vm, GC::Ref<SharedFunctionInstanceData const> shared_function_instance_data, BuiltinAbstractOperationsEnabled builtin_abstract_operations_enabled)
|
||||
{
|
||||
auto const& name = shared_function_instance_data->m_name;
|
||||
|
||||
auto executable_result = Bytecode::Generator::generate_from_function(vm, shared_function_instance_data);
|
||||
auto executable_result = Bytecode::Generator::generate_from_function(vm, shared_function_instance_data, builtin_abstract_operations_enabled);
|
||||
if (executable_result.is_error())
|
||||
return vm.throw_completion<InternalError>(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string()));
|
||||
|
||||
@@ -1290,7 +1290,7 @@ inline Value new_function(Interpreter& interpreter, FunctionNode const& function
|
||||
|
||||
if (home_object.has_value()) {
|
||||
auto home_object_value = interpreter.get(home_object.value());
|
||||
static_cast<ECMAScriptFunctionObject&>(value.as_function()).set_home_object(&home_object_value.as_object());
|
||||
as<ECMAScriptFunctionObject>(value.as_function()).set_home_object(&home_object_value.as_object());
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -2896,7 +2896,7 @@ ThrowCompletionOr<void> SuperCallWithArgumentArray::execute_impl(Bytecode::Inter
|
||||
TRY(this_environment.bind_this_value(vm, result));
|
||||
|
||||
// 9. Let F be thisER.[[FunctionObject]].
|
||||
auto& f = this_environment.function_object();
|
||||
auto& f = as<ECMAScriptFunctionObject>(this_environment.function_object());
|
||||
|
||||
// 10. Assert: F is an ECMAScript function object.
|
||||
// NOTE: This is implied by the strong C++ type.
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Bytecode/BuiltinAbstractOperationsEnabled.h>
|
||||
#include <LibJS/Bytecode/Executable.h>
|
||||
#include <LibJS/Bytecode/Label.h>
|
||||
#include <LibJS/Bytecode/Register.h>
|
||||
@@ -102,6 +103,6 @@ private:
|
||||
JS_API extern bool g_dump_bytecode;
|
||||
|
||||
ThrowCompletionOr<GC::Ref<Bytecode::Executable>> compile(VM&, ASTNode const&, JS::FunctionKind kind, Utf16FlyString const& name);
|
||||
ThrowCompletionOr<GC::Ref<Bytecode::Executable>> compile(VM&, GC::Ref<SharedFunctionInstanceData const>);
|
||||
ThrowCompletionOr<GC::Ref<Bytecode::Executable>> compile(VM&, GC::Ref<SharedFunctionInstanceData const>, BuiltinAbstractOperationsEnabled builtin_abstract_operations_enabled);
|
||||
|
||||
}
|
||||
|
||||
@@ -158,6 +158,7 @@ set(SOURCES
|
||||
Runtime/ModuleEnvironment.cpp
|
||||
Runtime/ModuleNamespaceObject.cpp
|
||||
Runtime/NativeFunction.cpp
|
||||
Runtime/NativeJavaScriptBackedFunction.cpp
|
||||
Runtime/NumberConstructor.cpp
|
||||
Runtime/NumberObject.cpp
|
||||
Runtime/NumberPrototype.cpp
|
||||
|
||||
@@ -201,6 +201,7 @@ class ModuleEnvironment;
|
||||
class Module;
|
||||
struct ModuleRequest;
|
||||
class NativeFunction;
|
||||
class NativeJavaScriptBackedFunction;
|
||||
class ObjectEnvironment;
|
||||
class Parser;
|
||||
struct ParserError;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <LibJS/Runtime/FunctionObject.h>
|
||||
#include <LibJS/Runtime/GlobalEnvironment.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/NativeJavaScriptBackedFunction.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibJS/Runtime/ObjectEnvironment.h>
|
||||
#include <LibJS/Runtime/PromiseCapability.h>
|
||||
@@ -458,6 +459,36 @@ GC::Ref<FunctionEnvironment> new_function_environment(ECMAScriptFunctionObject&
|
||||
return env;
|
||||
}
|
||||
|
||||
// 9.1.2.4 NewFunctionEnvironment ( F, newTarget ), https://tc39.es/ecma262/#sec-newfunctionenvironment
|
||||
// 4.1.2.2 NewFunctionEnvironment ( F, newTarget ), https://tc39.es/proposal-explicit-resource-management/#sec-newfunctionenvironment
|
||||
GC::Ref<FunctionEnvironment> new_function_environment(NativeJavaScriptBackedFunction& function, Object* new_target)
|
||||
{
|
||||
auto& heap = function.heap();
|
||||
|
||||
// 1. Let env be a new function Environment Record containing no bindings.
|
||||
auto env = heap.allocate<FunctionEnvironment>(nullptr);
|
||||
|
||||
// 2. Set env.[[FunctionObject]] to F.
|
||||
env->set_function_object(function);
|
||||
|
||||
// 3. If F.[[ThisMode]] is lexical, set env.[[ThisBindingStatus]] to lexical.
|
||||
if (function.this_mode() == ThisMode::Lexical)
|
||||
env->set_this_binding_status(FunctionEnvironment::ThisBindingStatus::Lexical);
|
||||
// 4. Else, set env.[[ThisBindingStatus]] to uninitialized.
|
||||
else
|
||||
env->set_this_binding_status(FunctionEnvironment::ThisBindingStatus::Uninitialized);
|
||||
|
||||
// 5. Set env.[[NewTarget]] to newTarget.
|
||||
env->set_new_target(new_target ?: js_undefined());
|
||||
|
||||
// 6. Set env.[[OuterEnv]] to F.[[Environment]].
|
||||
// 7. Set env.[[DisposeCapability]] to NewDisposeCapability().
|
||||
// NOTE: Done in step 1 via the FunctionEnvironment constructor.
|
||||
|
||||
// 8. Return env.
|
||||
return env;
|
||||
}
|
||||
|
||||
// 9.2.1.1 NewPrivateEnvironment ( outerPrivEnv ), https://tc39.es/ecma262/#sec-newprivateenvironment
|
||||
GC::Ref<PrivateEnvironment> new_private_environment(VM& vm, PrivateEnvironment* outer)
|
||||
{
|
||||
@@ -586,7 +617,7 @@ ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller,
|
||||
auto& this_function_environment_record = static_cast<FunctionEnvironment&>(*this_environment_record);
|
||||
|
||||
// i. Let F be thisEnvRec.[[FunctionObject]].
|
||||
auto& function = this_function_environment_record.function_object();
|
||||
auto& function = as<ECMAScriptFunctionObject>(this_function_environment_record.function_object());
|
||||
|
||||
// ii. Set inFunction to true.
|
||||
in_function = true;
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace JS {
|
||||
GC::Ref<DeclarativeEnvironment> new_declarative_environment(Environment&);
|
||||
JS_API GC::Ref<ObjectEnvironment> new_object_environment(Object&, bool is_with_environment, Environment*);
|
||||
GC::Ref<FunctionEnvironment> new_function_environment(ECMAScriptFunctionObject&, Object* new_target);
|
||||
GC::Ref<FunctionEnvironment> new_function_environment(NativeJavaScriptBackedFunction&, Object* new_target);
|
||||
GC::Ref<PrivateEnvironment> new_private_environment(VM& vm, PrivateEnvironment* outer);
|
||||
GC::Ref<Environment> get_this_environment(VM&);
|
||||
JS_API bool can_be_held_weakly(Value);
|
||||
|
||||
@@ -12,30 +12,41 @@
|
||||
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
||||
#include <LibJS/Runtime/GeneratorResult.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/NativeJavaScriptBackedFunction.h>
|
||||
#include <LibJS/Runtime/PromiseConstructor.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(AsyncGenerator);
|
||||
|
||||
GC::Ref<AsyncGenerator> AsyncGenerator::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context)
|
||||
GC::Ref<AsyncGenerator> AsyncGenerator::create(Realm& realm, Value initial_value, Variant<GC::Ref<ECMAScriptFunctionObject>, GC::Ref<NativeJavaScriptBackedFunction>> generating_function, NonnullOwnPtr<ExecutionContext> execution_context)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto generating_function_prototype = MUST(generating_function->get(vm.names.prototype, cache));
|
||||
auto generating_function_prototype = MUST(generating_function.visit([&vm](auto function) {
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
return function->get(vm.names.prototype, cache);
|
||||
}));
|
||||
GC::Ptr<Object> generating_function_prototype_object = nullptr;
|
||||
if (!generating_function_prototype.is_nullish())
|
||||
generating_function_prototype_object = MUST(generating_function_prototype.to_object(vm));
|
||||
auto object = realm.create<AsyncGenerator>(realm, generating_function_prototype_object, move(execution_context));
|
||||
object->m_generating_function = generating_function;
|
||||
object->m_previous_value = initial_value;
|
||||
return object;
|
||||
|
||||
auto generating_executable = generating_function.visit(
|
||||
[](GC::Ref<ECMAScriptFunctionObject> function) -> GC::Ref<Bytecode::Executable> {
|
||||
return function->bytecode_executable().as_nonnull();
|
||||
},
|
||||
[](GC::Ref<NativeJavaScriptBackedFunction> function) -> GC::Ref<Bytecode::Executable> {
|
||||
return function->bytecode_executable();
|
||||
});
|
||||
|
||||
return realm.create<AsyncGenerator>(realm, generating_function_prototype_object, move(execution_context), generating_executable, initial_value);
|
||||
}
|
||||
|
||||
AsyncGenerator::AsyncGenerator(Realm& realm, Object* prototype, NonnullOwnPtr<ExecutionContext> context)
|
||||
AsyncGenerator::AsyncGenerator(Realm& realm, Object* prototype, NonnullOwnPtr<ExecutionContext> context, GC::Ref<Bytecode::Executable> bytecode_executable, Value initial_value)
|
||||
: Object(realm, prototype)
|
||||
, m_async_generator_context(move(context))
|
||||
, m_generating_executable(bytecode_executable)
|
||||
, m_previous_value(initial_value)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -48,7 +59,7 @@ void AsyncGenerator::visit_edges(Cell::Visitor& visitor)
|
||||
visitor.visit(request.completion.value());
|
||||
visitor.visit(request.capability);
|
||||
}
|
||||
visitor.visit(m_generating_function);
|
||||
visitor.visit(m_generating_executable);
|
||||
visitor.visit(m_previous_value);
|
||||
visitor.visit(m_current_promise);
|
||||
m_async_generator_context->visit_edges(visitor);
|
||||
@@ -188,7 +199,7 @@ void AsyncGenerator::execute(VM& vm, Completion completion)
|
||||
// We should never enter `execute` again after the generator is complete.
|
||||
VERIFY(continuation_address.has_value());
|
||||
|
||||
auto result_value = bytecode_interpreter.run_executable(vm.running_execution_context(), *m_generating_function->bytecode_executable(), continuation_address, completion_cell);
|
||||
auto result_value = bytecode_interpreter.run_executable(vm.running_execution_context(), m_generating_executable, continuation_address, completion_cell);
|
||||
|
||||
if (!result_value.is_throw_completion()) {
|
||||
m_previous_value = result_value.release_value();
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
Completed,
|
||||
};
|
||||
|
||||
static GC::Ref<AsyncGenerator> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr<ExecutionContext>);
|
||||
static GC::Ref<AsyncGenerator> create(Realm&, Value, Variant<GC::Ref<ECMAScriptFunctionObject>, GC::Ref<NativeJavaScriptBackedFunction>>, NonnullOwnPtr<ExecutionContext>);
|
||||
|
||||
virtual ~AsyncGenerator() override;
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
Optional<String> const& generator_brand() const { return m_generator_brand; }
|
||||
|
||||
private:
|
||||
AsyncGenerator(Realm&, Object* prototype, NonnullOwnPtr<ExecutionContext>);
|
||||
AsyncGenerator(Realm&, Object* prototype, NonnullOwnPtr<ExecutionContext>, GC::Ref<Bytecode::Executable>, Value);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
@@ -58,7 +58,7 @@ private:
|
||||
Vector<AsyncGeneratorRequest> m_async_generator_queue; // [[AsyncGeneratorQueue]]
|
||||
Optional<String> m_generator_brand; // [[GeneratorBrand]]
|
||||
|
||||
GC::Ptr<ECMAScriptFunctionObject> m_generating_function;
|
||||
GC::Ref<Bytecode::Executable> m_generating_executable;
|
||||
Value m_previous_value;
|
||||
GC::Ptr<Promise> m_current_promise;
|
||||
};
|
||||
|
||||
@@ -217,7 +217,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::get_stack_frame_size(size_t& r
|
||||
if (is_module_wrapper()) {
|
||||
executable = TRY(Bytecode::compile(vm(), ecmascript_code(), kind(), name()));
|
||||
} else {
|
||||
executable = TRY(Bytecode::compile(vm(), shared_data()));
|
||||
executable = TRY(Bytecode::compile(vm(), shared_data(), Bytecode::BuiltinAbstractOperationsEnabled::No));
|
||||
}
|
||||
}
|
||||
registers_and_constants_and_locals_count = executable->registers_and_constants_and_locals_count;
|
||||
@@ -607,9 +607,9 @@ ThrowCompletionOr<Value> ECMAScriptFunctionObject::ordinary_call_evaluate_body(V
|
||||
return result;
|
||||
|
||||
if (kind() == FunctionKind::AsyncGenerator)
|
||||
return AsyncGenerator::create(*context.realm, result, this, context.copy());
|
||||
return AsyncGenerator::create(*context.realm, result, GC::Ref { *this }, context.copy());
|
||||
|
||||
auto generator_object = GeneratorObject::create(*context.realm, result, this, context.copy());
|
||||
auto generator_object = GeneratorObject::create(*context.realm, result, GC::Ref { *this }, context.copy());
|
||||
|
||||
// NOTE: Async functions are entirely transformed to generator functions, and wrapped in a custom driver that returns a promise
|
||||
// See AwaitExpression::generate_bytecode() for the transformation.
|
||||
|
||||
@@ -32,7 +32,11 @@ ThrowCompletionOr<Value> FunctionEnvironment::get_super_base() const
|
||||
VERIFY(m_function_object);
|
||||
|
||||
// 1. Let home be envRec.[[FunctionObject]].[[HomeObject]].
|
||||
auto home_object = m_function_object->home_object();
|
||||
auto* ecmascript_function_object = as_if<ECMAScriptFunctionObject>(*m_function_object);
|
||||
if (!ecmascript_function_object)
|
||||
return js_undefined();
|
||||
|
||||
auto home_object = ecmascript_function_object->home_object();
|
||||
|
||||
// 2. If home is undefined, return undefined.
|
||||
if (!home_object)
|
||||
@@ -57,7 +61,10 @@ bool FunctionEnvironment::has_super_binding() const
|
||||
{
|
||||
if (this_binding_status() == ThisBindingStatus::Lexical)
|
||||
return false;
|
||||
if (!function_object().home_object())
|
||||
auto* ecmascript_function_object = as_if<ECMAScriptFunctionObject>(*m_function_object);
|
||||
if (!ecmascript_function_object)
|
||||
return false;
|
||||
if (!ecmascript_function_object->home_object())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ public:
|
||||
ThisBindingStatus this_binding_status() const { return m_this_binding_status; }
|
||||
void set_this_binding_status(ThisBindingStatus status) { m_this_binding_status = status; }
|
||||
|
||||
ECMAScriptFunctionObject& function_object() { return *m_function_object; }
|
||||
ECMAScriptFunctionObject const& function_object() const { return *m_function_object; }
|
||||
void set_function_object(ECMAScriptFunctionObject& function) { m_function_object = &function; }
|
||||
FunctionObject& function_object() { return *m_function_object; }
|
||||
FunctionObject const& function_object() const { return *m_function_object; }
|
||||
void set_function_object(FunctionObject& function) { m_function_object = &function; }
|
||||
|
||||
Value new_target() const { return m_new_target; }
|
||||
void set_new_target(Value new_target)
|
||||
@@ -53,7 +53,7 @@ private:
|
||||
|
||||
Value m_this_value; // [[ThisValue]]
|
||||
ThisBindingStatus m_this_binding_status { ThisBindingStatus::Uninitialized }; // [[ThisBindingStatus]]
|
||||
GC::Ptr<ECMAScriptFunctionObject> m_function_object; // [[FunctionObject]]
|
||||
GC::Ptr<FunctionObject> m_function_object; // [[FunctionObject]]
|
||||
Value m_new_target { js_undefined() }; // [[NewTarget]]
|
||||
};
|
||||
|
||||
|
||||
@@ -13,30 +13,50 @@
|
||||
#include <LibJS/Runtime/GeneratorResult.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Iterator.h>
|
||||
#include <LibJS/Runtime/NativeJavaScriptBackedFunction.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(GeneratorObject);
|
||||
|
||||
GC::Ref<GeneratorObject> GeneratorObject::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context)
|
||||
GC::Ref<GeneratorObject> GeneratorObject::create(Realm& realm, Value initial_value, Variant<GC::Ref<ECMAScriptFunctionObject>, GC::Ref<NativeJavaScriptBackedFunction>> generating_function, NonnullOwnPtr<ExecutionContext> execution_context)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
|
||||
Value generating_function_prototype;
|
||||
if (generating_function->kind() == FunctionKind::Async) {
|
||||
|
||||
auto kind = generating_function.visit(
|
||||
[](auto function) {
|
||||
return function->kind();
|
||||
});
|
||||
|
||||
if (kind == FunctionKind::Async) {
|
||||
// We implement async functions by transforming them to generator function in the bytecode
|
||||
// interpreter. However an async function does not have a prototype and should not be
|
||||
// changed thus we hardcode the prototype.
|
||||
generating_function_prototype = realm.intrinsics().generator_prototype();
|
||||
} else {
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
generating_function_prototype = MUST(generating_function->get(vm.names.prototype, cache));
|
||||
generating_function_prototype = MUST(generating_function.visit([&vm](auto function) {
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
return function->get(vm.names.prototype, cache);
|
||||
}));
|
||||
}
|
||||
|
||||
GC::Ptr<Object> generating_function_prototype_object = nullptr;
|
||||
if (!generating_function_prototype.is_nullish())
|
||||
generating_function_prototype_object = MUST(generating_function_prototype.to_object(vm));
|
||||
|
||||
auto generating_executable = generating_function.visit(
|
||||
[](GC::Ref<ECMAScriptFunctionObject> function) -> GC::Ref<Bytecode::Executable> {
|
||||
return function->bytecode_executable().as_nonnull();
|
||||
},
|
||||
[](GC::Ref<NativeJavaScriptBackedFunction> function) -> GC::Ref<Bytecode::Executable> {
|
||||
return function->bytecode_executable();
|
||||
});
|
||||
|
||||
auto object = realm.create<GeneratorObject>(realm, generating_function_prototype_object, move(execution_context));
|
||||
object->m_generating_function = generating_function;
|
||||
object->m_generating_executable = generating_executable;
|
||||
object->m_previous_value = initial_value;
|
||||
return object;
|
||||
}
|
||||
@@ -51,7 +71,7 @@ GeneratorObject::GeneratorObject(Realm& realm, Object* prototype, NonnullOwnPtr<
|
||||
void GeneratorObject::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_generating_function);
|
||||
visitor.visit(m_generating_executable);
|
||||
visitor.visit(m_previous_value);
|
||||
m_execution_context->visit_edges(visitor);
|
||||
}
|
||||
@@ -110,7 +130,7 @@ ThrowCompletionOr<GeneratorObject::IterationResult> GeneratorObject::execute(VM&
|
||||
// We should never enter `execute` again after the generator is complete.
|
||||
VERIFY(next_block.has_value());
|
||||
|
||||
auto result_value = bytecode_interpreter.run_executable(vm.running_execution_context(), *m_generating_function->bytecode_executable(), next_block, completion_cell);
|
||||
auto result_value = bytecode_interpreter.run_executable(vm.running_execution_context(), *m_generating_executable, next_block, completion_cell);
|
||||
|
||||
vm.pop_execution_context();
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class GeneratorObject : public Object {
|
||||
GC_DECLARE_ALLOCATOR(GeneratorObject);
|
||||
|
||||
public:
|
||||
static GC::Ref<GeneratorObject> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr<ExecutionContext>);
|
||||
static GC::Ref<GeneratorObject> create(Realm&, Value, Variant<GC::Ref<ECMAScriptFunctionObject>, GC::Ref<NativeJavaScriptBackedFunction>>, NonnullOwnPtr<ExecutionContext>);
|
||||
virtual ~GeneratorObject() override = default;
|
||||
void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
@@ -53,7 +53,7 @@ protected:
|
||||
|
||||
private:
|
||||
NonnullOwnPtr<ExecutionContext> m_execution_context;
|
||||
GC::Ptr<ECMAScriptFunctionObject> m_generating_function;
|
||||
GC::Ptr<Bytecode::Executable> m_generating_executable;
|
||||
Value m_previous_value;
|
||||
GeneratorState m_generator_state { GeneratorState::SuspendedStart };
|
||||
Optional<StringView> m_generator_brand;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <LibJS/Bytecode/Interpreter.h>
|
||||
#include <LibJS/Runtime/FunctionEnvironment.h>
|
||||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
#include <LibJS/Runtime/NativeJavaScriptBackedFunction.h>
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
|
||||
@@ -127,8 +128,21 @@ ThrowCompletionOr<Value> NativeFunction::internal_call(ExecutionContext& callee_
|
||||
// 8. Perform any necessary implementation-defined initialization of calleeContext.
|
||||
callee_context.this_value = this_argument;
|
||||
|
||||
callee_context.lexical_environment = caller_context.lexical_environment;
|
||||
callee_context.variable_environment = caller_context.variable_environment;
|
||||
if (function_environment_needed()) {
|
||||
// 7. Let localEnv be NewFunctionEnvironment(F, newTarget).
|
||||
auto local_environment = new_function_environment(as<NativeJavaScriptBackedFunction>(*this), nullptr);
|
||||
local_environment->ensure_capacity(function_environment_bindings_count());
|
||||
|
||||
// 8. Set the LexicalEnvironment of calleeContext to localEnv.
|
||||
callee_context.lexical_environment = local_environment;
|
||||
|
||||
// 9. Set the VariableEnvironment of calleeContext to localEnv.
|
||||
callee_context.variable_environment = local_environment;
|
||||
} else {
|
||||
callee_context.lexical_environment = caller_context.lexical_environment;
|
||||
callee_context.variable_environment = caller_context.variable_environment;
|
||||
}
|
||||
|
||||
// Note: Keeping the private environment is probably only needed because of async methods in classes
|
||||
// calling async_block_start which goes through a NativeFunction here.
|
||||
callee_context.private_environment = caller_context.private_environment;
|
||||
@@ -169,8 +183,20 @@ ThrowCompletionOr<GC::Ref<Object>> NativeFunction::internal_construct(ExecutionC
|
||||
// 7. Set the ScriptOrModule of calleeContext to null.
|
||||
// Note: This is already the default value.
|
||||
|
||||
callee_context.lexical_environment = caller_context.lexical_environment;
|
||||
callee_context.variable_environment = caller_context.variable_environment;
|
||||
if (function_environment_needed()) {
|
||||
// 7. Let localEnv be NewFunctionEnvironment(F, newTarget).
|
||||
auto local_environment = new_function_environment(as<NativeJavaScriptBackedFunction>(*this), nullptr);
|
||||
local_environment->ensure_capacity(function_environment_bindings_count());
|
||||
|
||||
// 8. Set the LexicalEnvironment of calleeContext to localEnv.
|
||||
callee_context.lexical_environment = local_environment;
|
||||
|
||||
// 9. Set the VariableEnvironment of calleeContext to localEnv.
|
||||
callee_context.variable_environment = local_environment;
|
||||
} else {
|
||||
callee_context.lexical_environment = caller_context.lexical_environment;
|
||||
callee_context.variable_environment = caller_context.variable_environment;
|
||||
}
|
||||
|
||||
// </8.> --------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -52,6 +52,9 @@ public:
|
||||
|
||||
Optional<Bytecode::Builtin> builtin() const { return m_builtin; }
|
||||
|
||||
virtual bool function_environment_needed() const { return false; }
|
||||
virtual size_t function_environment_bindings_count() const { return 0; }
|
||||
|
||||
protected:
|
||||
NativeFunction(Utf16FlyString name, Object& prototype);
|
||||
NativeFunction(AK::Function<ThrowCompletionOr<Value>(VM&)>, Object* prototype, Realm& realm, Optional<Bytecode::Builtin> builtin);
|
||||
|
||||
144
Libraries/LibJS/Runtime/NativeJavaScriptBackedFunction.cpp
Normal file
144
Libraries/LibJS/Runtime/NativeJavaScriptBackedFunction.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibJS/Bytecode/BuiltinAbstractOperationsEnabled.h>
|
||||
#include <LibJS/Bytecode/Generator.h>
|
||||
#include <LibJS/Bytecode/Interpreter.h>
|
||||
#include <LibJS/Runtime/AsyncFunctionDriverWrapper.h>
|
||||
#include <LibJS/Runtime/AsyncGenerator.h>
|
||||
#include <LibJS/Runtime/GeneratorObject.h>
|
||||
#include <LibJS/Runtime/NativeJavaScriptBackedFunction.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(NativeJavaScriptBackedFunction);
|
||||
|
||||
// 10.3.3 CreateBuiltinFunction ( behaviour, length, name, additionalInternalSlotsList [ , realm [ , prototype [ , prefix ] ] ] ), https://tc39.es/ecma262/#sec-createbuiltinfunction
|
||||
GC::Ref<NativeJavaScriptBackedFunction> NativeJavaScriptBackedFunction::create(Realm& realm, FunctionNode const& function_node, PropertyKey const& name, i32 length)
|
||||
{
|
||||
// 1. If realm is not present, set realm to the current Realm Record.
|
||||
// 2. If prototype is not present, set prototype to realm.[[Intrinsics]].[[%Function.prototype%]].
|
||||
auto prototype = realm.intrinsics().function_prototype();
|
||||
|
||||
// 3. Let internalSlotsList be a List containing the names of all the internal slots that 10.3 requires for the built-in function object that is about to be created.
|
||||
// 4. Append to internalSlotsList the elements of additionalInternalSlotsList.
|
||||
|
||||
// 5. Let func be a new built-in function object that, when called, performs the action described by behaviour using the provided arguments as the values of the corresponding parameters specified by behaviour. The new function object has internal slots whose names are the elements of internalSlotsList, and an [[InitialName]] internal slot.
|
||||
// 6. Set func.[[Prototype]] to prototype.
|
||||
// 7. Set func.[[Extensible]] to true.
|
||||
// 8. Set func.[[Realm]] to realm.
|
||||
// 9. Set func.[[InitialName]] to null.
|
||||
auto shared_data = realm.heap().allocate<SharedFunctionInstanceData>(realm.vm(),
|
||||
function_node.kind(),
|
||||
function_node.name(),
|
||||
function_node.function_length(),
|
||||
function_node.parameters(),
|
||||
*function_node.body_ptr(),
|
||||
function_node.source_text(),
|
||||
function_node.is_strict_mode(),
|
||||
function_node.is_arrow_function(),
|
||||
function_node.parsing_insights(),
|
||||
function_node.local_variables_names());
|
||||
|
||||
auto function = realm.create<NativeJavaScriptBackedFunction>(shared_data, *prototype);
|
||||
|
||||
function->unsafe_set_shape(realm.intrinsics().native_function_shape());
|
||||
|
||||
// 10. Perform SetFunctionLength(func, length).
|
||||
function->put_direct(realm.intrinsics().native_function_length_offset(), Value { length });
|
||||
|
||||
// 11. If prefix is not present, then
|
||||
// a. Perform SetFunctionName(func, name).
|
||||
// 12. Else,
|
||||
// a. Perform SetFunctionName(func, name, prefix).
|
||||
function->put_direct(realm.intrinsics().native_function_name_offset(), function->make_function_name(name, OptionalNone {}));
|
||||
|
||||
// 13. Return func.
|
||||
return function;
|
||||
}
|
||||
|
||||
NativeJavaScriptBackedFunction::NativeJavaScriptBackedFunction(GC::Ref<SharedFunctionInstanceData const> shared_function_instance_data, Object& prototype)
|
||||
: NativeFunction(shared_function_instance_data->m_name, prototype)
|
||||
, m_shared_function_instance_data(shared_function_instance_data)
|
||||
{
|
||||
}
|
||||
|
||||
void NativeJavaScriptBackedFunction::visit_edges(Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_shared_function_instance_data);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> NativeJavaScriptBackedFunction::get_stack_frame_size(size_t& registers_and_constants_and_locals_count, size_t& argument_count)
|
||||
{
|
||||
auto& bytecode_executable = this->bytecode_executable();
|
||||
registers_and_constants_and_locals_count = bytecode_executable.number_of_registers + bytecode_executable.constants.size() + bytecode_executable.local_variable_names.size();
|
||||
argument_count = max(argument_count, m_shared_function_instance_data->m_function_length);
|
||||
return {};
|
||||
}
|
||||
|
||||
ThrowCompletionOr<Value> NativeJavaScriptBackedFunction::call()
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
auto result = TRY(vm.bytecode_interpreter().run_executable(vm.running_execution_context(), bytecode_executable(), {}));
|
||||
|
||||
auto kind = this->kind();
|
||||
if (kind == FunctionKind::Normal)
|
||||
return result;
|
||||
|
||||
auto& realm = *vm.current_realm();
|
||||
if (kind == FunctionKind::AsyncGenerator)
|
||||
return AsyncGenerator::create(realm, result, GC::Ref { *this }, vm.running_execution_context().copy());
|
||||
|
||||
auto generator_object = GeneratorObject::create(realm, result, GC::Ref { *this }, vm.running_execution_context().copy());
|
||||
|
||||
// NOTE: Async functions are entirely transformed to generator functions, and wrapped in a custom driver that returns a promise
|
||||
// See AwaitExpression::generate_bytecode() for the transformation.
|
||||
if (kind == FunctionKind::Async)
|
||||
return AsyncFunctionDriverWrapper::create(realm, generator_object);
|
||||
|
||||
VERIFY(kind == FunctionKind::Generator);
|
||||
return generator_object;
|
||||
}
|
||||
|
||||
Bytecode::Executable& NativeJavaScriptBackedFunction::bytecode_executable()
|
||||
{
|
||||
auto& executable = m_shared_function_instance_data->m_executable;
|
||||
if (!executable) {
|
||||
executable = MUST(Bytecode::compile(vm(), m_shared_function_instance_data, Bytecode::BuiltinAbstractOperationsEnabled::Yes));
|
||||
}
|
||||
|
||||
return *executable;
|
||||
}
|
||||
|
||||
FunctionKind NativeJavaScriptBackedFunction::kind() const
|
||||
{
|
||||
return m_shared_function_instance_data->m_kind;
|
||||
}
|
||||
|
||||
ThisMode NativeJavaScriptBackedFunction::this_mode() const
|
||||
{
|
||||
return m_shared_function_instance_data->m_this_mode;
|
||||
}
|
||||
|
||||
bool NativeJavaScriptBackedFunction::function_environment_needed() const
|
||||
{
|
||||
return m_shared_function_instance_data->m_function_environment_needed;
|
||||
}
|
||||
|
||||
size_t NativeJavaScriptBackedFunction::function_environment_bindings_count() const
|
||||
{
|
||||
return m_shared_function_instance_data->m_function_environment_bindings_count;
|
||||
}
|
||||
|
||||
bool NativeJavaScriptBackedFunction::is_strict_mode() const
|
||||
{
|
||||
return m_shared_function_instance_data->m_strict;
|
||||
}
|
||||
|
||||
}
|
||||
43
Libraries/LibJS/Runtime/NativeJavaScriptBackedFunction.h
Normal file
43
Libraries/LibJS/Runtime/NativeJavaScriptBackedFunction.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
#include <LibJS/Runtime/SharedFunctionInstanceData.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
class NativeJavaScriptBackedFunction final : public NativeFunction {
|
||||
JS_OBJECT(NativeJavaScriptBackedFunction, NativeFunction);
|
||||
GC_DECLARE_ALLOCATOR(NativeJavaScriptBackedFunction);
|
||||
|
||||
public:
|
||||
static GC::Ref<NativeJavaScriptBackedFunction> create(Realm&, FunctionNode const& function_node, PropertyKey const& name, i32 length);
|
||||
|
||||
virtual ~NativeJavaScriptBackedFunction() override = default;
|
||||
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
virtual ThrowCompletionOr<void> get_stack_frame_size(size_t& registers_and_constants_and_locals_count, size_t& argument_count) override;
|
||||
|
||||
virtual ThrowCompletionOr<Value> call() override;
|
||||
|
||||
Bytecode::Executable& bytecode_executable();
|
||||
FunctionKind kind() const;
|
||||
ThisMode this_mode() const;
|
||||
|
||||
virtual bool function_environment_needed() const override;
|
||||
virtual size_t function_environment_bindings_count() const override;
|
||||
virtual bool is_strict_mode() const override;
|
||||
|
||||
private:
|
||||
explicit NativeJavaScriptBackedFunction(GC::Ref<SharedFunctionInstanceData const> shared_function_instance_data, Object& prototype);
|
||||
|
||||
GC::Ref<SharedFunctionInstanceData const> m_shared_function_instance_data;
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user