Skip to content

Commit

Permalink
LibJS: FDI bytecode WIP
Browse files Browse the repository at this point in the history
LibJS: FDI bytecode WIP CreateMappedArguments

LibJS: FDI bytecode WIP CreateRestParams

LibWeb: FDI bytecode WIP create variable for non-locals

LibJS: FDI bytecode WIP CreateUnmappedArguments

LibJS: FDI bytecode WIP fill arguments with js_undefined

LibJS: FDI bytecode WIP init annexB

LibJS: FDI bytecode WIP default params for bindings

LibJS: FDI bytecode WIP rest hack

LibJS: FDI bytecode WIP init annexB functions

LibWeb: FDI bytecode WIP

LibWeb: FDI bytecode WIP create lex env when needed

LibJS: FDI bytecode WIP mapped args length

LibJS: FDI bytecode WIP WIP

LibJS: FDI bytecode WIP comment out default params executables

LibJS: FDI bytecode WIP hmmmmmmm

LibJS: FDI bytecode WIP some progress

LibJS: FDI bytecode WIP some progress

LibJS: FDI bytecode WIP wat

LibJS: FDI bytecode WIP save argument count in copied EC

LibJS: FDI bytecode WIP dupes

LibJS: FDI bytecode WIP module wrappers fix

LibJS: FDI bytecode WIP some progress

LibJS: FDI bytecode WIP some progress

LibJS: FDI bytecode WIP some progress

LibJS: FDI bytecode WIP some cleanup

LibJS: FDI bytecode WIP some cleanup

LibJS: FDI bytecode WIP some cleanup

LibJS: FDI bytecode WIP some cleanup

LibJS: FDI bytecode WIP some cleanup

LibJS: FDI bytecode WIP cleanup

LibJS: FDI bytecode WIP emit_fdi() separate func

LibJS: FDI bytecode WIP WIP

LibJS: FDI bytecode WIP

LibJS: FDI bytecode WIP

LibJS: FDI bytecode WIP

LibJS: FDI bytecode WIP

LibJS: FDI bytecode WIP

LibJS: FDI bytecode WIP

LibJS: FDI bytecode WIP
  • Loading branch information
kalenikaliaksandr committed May 9, 2024
1 parent 49b1a20 commit 331770b
Show file tree
Hide file tree
Showing 19 changed files with 420 additions and 456 deletions.
13 changes: 11 additions & 2 deletions Userland/Libraries/LibJS/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,9 @@ class FunctionNode {
FunctionKind kind() const { return m_kind; }
UsesThis uses_this() const { return m_uses_this; }

virtual bool has_name() const = 0;
virtual Value instantiate_ordinary_function_expression(VM&, DeprecatedFlyString given_name) const = 0;

protected:
FunctionNode(RefPtr<Identifier const> name, ByteString source_text, NonnullRefPtr<Statement const> body, Vector<FunctionParameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function, Vector<DeprecatedFlyString> local_variables_names, UsesThis uses_this)
: m_name(move(name))
Expand Down Expand Up @@ -772,6 +775,12 @@ class FunctionDeclaration final

void set_should_do_additional_annexB_steps() { m_is_hoisted = true; }

bool has_name() const override { return true; }
Value instantiate_ordinary_function_expression(VM&, DeprecatedFlyString) const override
{
VERIFY_NOT_REACHED();
}

private:
bool m_is_hoisted { false };
};
Expand All @@ -793,9 +802,9 @@ class FunctionExpression final
virtual Bytecode::CodeGenerationErrorOr<Optional<Bytecode::Operand>> generate_bytecode(Bytecode::Generator&, Optional<Bytecode::Operand> preferred_dst = {}) const override;
virtual Bytecode::CodeGenerationErrorOr<Optional<Bytecode::Operand>> generate_bytecode_with_lhs_name(Bytecode::Generator&, Optional<Bytecode::IdentifierTableIndex> lhs_name, Optional<Bytecode::Operand> preferred_dst = {}) const;

bool has_name() const { return !name().is_empty(); }
bool has_name() const override { return !name().is_empty(); }

Value instantiate_ordinary_function_expression(VM&, DeprecatedFlyString given_name) const;
Value instantiate_ordinary_function_expression(VM&, DeprecatedFlyString given_name) const override;

private:
virtual bool is_function_expression() const override { return true; }
Expand Down
2 changes: 1 addition & 1 deletion Userland/Libraries/LibJS/Bytecode/CommonImplementations.h
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ inline ThrowCompletionOr<void> set_variable(
return {};
}

inline Value new_function(VM& vm, FunctionExpression const& function_node, Optional<IdentifierTableIndex> const& lhs_name, Optional<Operand> const& home_object)
inline Value new_function(VM& vm, FunctionNode const& function_node, Optional<IdentifierTableIndex> const& lhs_name, Optional<Operand> const& home_object)
{
Value value;

Expand Down
201 changes: 192 additions & 9 deletions Userland/Libraries/LibJS/Bytecode/Generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <LibJS/Bytecode/Instruction.h>
#include <LibJS/Bytecode/Op.h>
#include <LibJS/Bytecode/Register.h>
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
#include <LibJS/Runtime/VM.h>

namespace JS::Bytecode {
Expand All @@ -25,21 +26,190 @@ Generator::Generator(VM& vm)
{
}

CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::generate(VM& vm, ASTNode const& node, ReadonlySpan<FunctionParameter> parameters, FunctionKind enclosing_function_kind)
CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(ECMAScriptFunctionObject const& function)
{
Generator generator(vm);
auto& generator = *this;

if (function.m_has_parameter_expressions) {
generator.emit<Op::CreateLexicalEnvironment>();
}

for (auto const& parameter_name : function.m_parameter_names) {
if (parameter_name.value == ECMAScriptFunctionObject::ParameterIsLocal::No) {
auto id = generator.intern_identifier(parameter_name.key);
generator.emit<Op::CreateVariable>(id, Op::EnvironmentMode::Lexical, false);
if (function.m_has_duplicates) {
generator.emit<Op::SetVariable>(id, generator.add_constant(js_undefined()), generator.next_environment_variable_cache(), Op::SetVariable::InitializationMode::Initialize, Op::EnvironmentMode::Lexical);
}
}
}

if (function.m_arguments_object_needed) {
if (function.m_strict || !function.has_simple_parameter_list()) {
generator.emit<Op::CreateUnmappedArguments>(function.m_strict);
} else {
generator.emit<Op::CreateMappedArguments>(function.m_strict);
}
}

auto const& formal_parameters = function.formal_parameters();
for (u32 param_index = 0; param_index < formal_parameters.size(); ++param_index) {
auto const& parameter = formal_parameters[param_index];

if (parameter.is_rest) {
auto dst = Operand { Operand::Type::Argument, param_index };
generator.emit<Op::CreateRestParams>(dst, param_index);
} else if (parameter.default_value) {
auto& if_undefined_block = generator.make_block();
auto& if_not_undefined_block = generator.make_block();

generator.emit<Op::JumpUndefined>(
Operand { Operand::Type::Argument, param_index },
Label { if_undefined_block },
Label { if_not_undefined_block });

generator.switch_to_basic_block(if_undefined_block);
auto operand = TRY(parameter.default_value->generate_bytecode(generator));
generator.emit<Op::Mov>(Operand { Operand::Type::Argument, param_index }, *operand);
generator.emit<Op::Jump>(Label { if_not_undefined_block });

generator.switch_to_basic_block(if_not_undefined_block);
}

if (auto const* identifier = parameter.binding.get_pointer<NonnullRefPtr<Identifier const>>(); identifier) {
if ((*identifier)->is_local()) {
auto local_variable_index = (*identifier)->local_variable_index();
generator.emit<Op::Mov>(Operand(Operand::Type::Local, local_variable_index),
Operand { Operand::Type::Argument, param_index });

for (auto const& parameter : parameters) {
if (auto const* identifier = parameter.binding.get_pointer<NonnullRefPtr<Identifier const>>();
identifier && (*identifier)->is_local()) {
generator.set_local_initialized((*identifier)->local_variable_index());
generator.set_local_initialized((*identifier)->local_variable_index());
} else {
auto id = generator.intern_identifier((*identifier)->string());
auto init_mode = function.m_has_duplicates ? Op::SetVariable::InitializationMode::Set : Op::SetVariable::InitializationMode::Initialize;
generator.emit<Op::SetVariable>(id, Operand { Operand::Type::Argument, param_index },
generator.next_environment_variable_cache(),
init_mode,
Op::EnvironmentMode::Lexical);
}
} else if (auto const* binding_pattern = parameter.binding.get_pointer<NonnullRefPtr<BindingPattern const>>(); binding_pattern) {
auto input_operand = Operand { Operand::Type::Argument, param_index };
auto init_mode = function.m_has_duplicates ? Op::SetVariable::InitializationMode::Set : Bytecode::Op::SetVariable::InitializationMode::Initialize;
TRY((*binding_pattern)->generate_bytecode(generator, init_mode, input_operand, false));
}
}

ScopeNode const* scope_body = nullptr;
if (is<ScopeNode>(*function.m_ecmascript_code))
scope_body = static_cast<ScopeNode const*>(function.m_ecmascript_code.ptr());

if (!function.m_has_parameter_expressions) {
if (scope_body) {
for (auto const& variable_to_initialize : function.m_var_names_to_initialize_binding) {
auto const& id = variable_to_initialize.identifier;
if (id.is_local()) {
generator.emit<Op::Mov>(Operand(Operand::Type::Local, id.local_variable_index()), generator.add_constant(js_undefined()));
} else {
auto intern_id = generator.intern_identifier(id.string());
generator.emit<Op::CreateVariable>(intern_id, Op::EnvironmentMode::Var, false);
generator.emit<Op::SetVariable>(intern_id, generator.add_constant(js_undefined()), generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize, Op::EnvironmentMode::Var);
}
}
}
} else {
generator.emit<Op::CreateVariableEnvironment>();

if (scope_body) {
for (auto const& variable_to_initialize : function.m_var_names_to_initialize_binding) {
auto const& id = variable_to_initialize.identifier;

auto initial_value = Operand { generator.allocate_register() };
if (!variable_to_initialize.parameter_binding || variable_to_initialize.function_name) {
generator.emit<Op::Mov>(initial_value, generator.add_constant(js_undefined()));
} else {
if (id.is_local()) {
generator.emit<Op::Mov>(initial_value, Operand(Operand::Type::Local, id.local_variable_index()));
} else {
generator.emit<Op::GetVariable>(initial_value, generator.intern_identifier(id.string()), generator.next_environment_variable_cache());
}
}

if (id.is_local()) {
generator.emit<Op::Mov>(Operand(Operand::Type::Local, id.local_variable_index()), initial_value);
} else {
auto intern_id = generator.intern_identifier(id.string());
generator.emit<Op::CreateVariable>(intern_id, Op::EnvironmentMode::Var, false);
generator.emit<Op::SetVariable>(intern_id, initial_value, generator.next_environment_variable_cache(), Op::SetVariable::InitializationMode::Initialize, Op::EnvironmentMode::Var);
}
}
}
}

if (!function.m_strict && scope_body) {
// NOTE: Due to the use of MUST with `create_mutable_binding` and `initialize_binding` below,
// an exception should not result from `for_each_function_hoistable_with_annexB_extension`.
for (auto const& function_name : function.m_function_names_to_initialize_binding) {
auto intern_id = generator.intern_identifier(function_name);
generator.emit<Op::CreateVariable>(intern_id, Op::EnvironmentMode::Var, false);
generator.emit<Op::SetVariable>(intern_id, generator.add_constant(js_undefined()), generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize, Op::EnvironmentMode::Var);
}
}

if (!function.m_strict) {
bool can_elide_declarative_environment = !function.m_contains_direct_call_to_eval && (!scope_body || !scope_body->has_lexical_declarations());
if (!can_elide_declarative_environment) {
generator.emit<Op::CreateLexicalEnvironment>();
}
}

if (scope_body) {
MUST(scope_body->for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
MUST(declaration.for_each_bound_identifier([&](auto const& id) {
if (id.is_local()) {
return;
}

generator.emit<Op::CreateVariable>(generator.intern_identifier(id.string()),
Op::EnvironmentMode::Lexical,
declaration.is_constant_declaration(),
false,
declaration.is_constant_declaration());
}));
}));
}

for (auto const& declaration : function.m_functions_to_initialize) {
auto dst = Operand(generator.allocate_register());
generator.emit<Op::NewFunction>(dst, declaration, OptionalNone {});
if (declaration.name_identifier()->is_local()) {
generator.emit<Op::Mov>(Operand(Operand::Type::Local, declaration.name_identifier()->local_variable_index()), dst);
} else {
generator.emit<Op::SetVariable>(generator.intern_identifier(declaration.name()), dst, generator.next_environment_variable_cache(), Op::SetVariable::InitializationMode::Set, Op::EnvironmentMode::Var);
}
}

return {};
}

template<typename EmitFunctionDeclarationInstantiationIfNeeded>
CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::emit_function_body_bytecode(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind, EmitFunctionDeclarationInstantiationIfNeeded emit_fdi_if_needed)
{
Generator generator(vm);

generator.switch_to_basic_block(generator.make_block());
SourceLocationScope scope(generator, node);
generator.m_enclosing_function_kind = enclosing_function_kind;
if (generator.is_in_generator_or_async_function()) {
if (generator.is_in_async_function() && !generator.is_in_generator_function()) {
// Immediately yield with no value.
auto& start_block = generator.make_block();
generator.emit<Bytecode::Op::Yield>(Label { start_block }, generator.add_constant(js_undefined()));
generator.switch_to_basic_block(start_block);
// NOTE: This doesn't have to handle received throw/return completions, as GeneratorObject::resume_abrupt
// will not enter the generator from the SuspendedStart state and immediately completes the generator.
}

TRY(emit_fdi_if_needed(generator));

if (generator.is_in_generator_function()) {
// Immediately yield with no value.
auto& start_block = generator.make_block();
generator.emit<Bytecode::Op::Yield>(Label { start_block }, generator.add_constant(js_undefined()));
Expand Down Expand Up @@ -71,8 +241,6 @@ CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::generate(VM& vm, ASTN
is_strict_mode = static_cast<FunctionBody const&>(node).in_strict_mode();
else if (is<FunctionDeclaration>(node))
is_strict_mode = static_cast<FunctionDeclaration const&>(node).is_strict_mode();
else if (is<FunctionExpression>(node))
is_strict_mode = static_cast<FunctionExpression const&>(node).is_strict_mode();

size_t size_needed = 0;
for (auto& block : generator.m_root_basic_blocks) {
Expand Down Expand Up @@ -211,6 +379,21 @@ CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::generate(VM& vm, ASTN
return executable;
}

CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::generate_from_ast_node(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind)
{
return emit_function_body_bytecode(vm, node, enclosing_function_kind, [&](Generator&) -> CodeGenerationErrorOr<void> {
// Do nothing
return {};
});
}

CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::generate_from_function(VM& vm, ECMAScriptFunctionObject const& function)
{
return emit_function_body_bytecode(vm, function.ecmascript_code(), function.kind(), [&](Bytecode::Generator& generator) {
return generator.emit_function_declaration_instantiation(function);
});
}

void Generator::grow(size_t additional_size)
{
VERIFY(m_current_basic_block);
Expand Down
9 changes: 8 additions & 1 deletion Userland/Libraries/LibJS/Bytecode/Generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ class Generator {
Function,
Block,
};
static CodeGenerationErrorOr<NonnullGCPtr<Executable>> generate(VM&, ASTNode const&, ReadonlySpan<FunctionParameter> parameters, FunctionKind = FunctionKind::Normal);

static CodeGenerationErrorOr<NonnullGCPtr<Executable>> generate_from_ast_node(VM&, ASTNode const&, FunctionKind = FunctionKind::Normal);
static CodeGenerationErrorOr<NonnullGCPtr<Executable>> generate_from_function(VM&, ECMAScriptFunctionObject const& function);

CodeGenerationErrorOr<void> emit_function_declaration_instantiation(ECMAScriptFunctionObject const& function);

Register allocate_register();

Expand Down Expand Up @@ -283,6 +287,9 @@ class Generator {
private:
VM& m_vm;

template<typename EmitFunctionDeclarationInstantiationIfNeeded>
static CodeGenerationErrorOr<NonnullGCPtr<Executable>> emit_function_body_bytecode(VM&, ASTNode const&, FunctionKind, EmitFunctionDeclarationInstantiationIfNeeded);

enum class JumpType {
Continue,
Break,
Expand Down
4 changes: 4 additions & 0 deletions Userland/Libraries/LibJS/Bytecode/Instruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@
O(ContinuePendingUnwind) \
O(CopyObjectExcludingProperties) \
O(CreateLexicalEnvironment) \
O(CreateVariableEnvironment) \
O(CreateVariable) \
O(CreateRestParams) \
O(CreateMappedArguments) \
O(CreateUnmappedArguments) \
O(Decrement) \
O(DeleteById) \
O(DeleteByIdWithThis) \
Expand Down
Loading

0 comments on commit 331770b

Please sign in to comment.