diff options
author | Andreas Kling <kling@serenityos.org> | 2021-06-10 22:12:21 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-06-10 22:20:23 +0200 |
commit | c3c68399b5e7019c30e5ac6fb7cb38d586f2a7f3 (patch) | |
tree | 4df63cef779db4f69bd47143c9019f0b3e023291 /Userland | |
parent | d560ee118d0d253972770a06af99512723db9be1 (diff) | |
download | serenity-c3c68399b5e7019c30e5ac6fb7cb38d586f2a7f3.zip |
LibJS: Generate bytecode for entering nested lexical environments
This adds a new PushLexicalEnvironment instruction that creates a new
LexicalEnvironment and pushes it on the VM's scope stack.
There is no corresponding PopLexicalEnvironment instruction yet,
so this will behave incorrectly with let/const scopes for example.
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp | 52 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/Instruction.h | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/Op.cpp | 26 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/Op.h | 14 |
4 files changed, 92 insertions, 1 deletions
diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 2fdfb567f0..028683b934 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -11,6 +11,8 @@ #include <LibJS/Bytecode/Instruction.h> #include <LibJS/Bytecode/Op.h> #include <LibJS/Bytecode/Register.h> +#include <LibJS/Bytecode/StringTable.h> +#include <LibJS/Runtime/ScopeObject.h> namespace JS { @@ -27,6 +29,41 @@ void ScopeNode::generate_bytecode(Bytecode::Generator& generator) const generator.emit<Bytecode::Op::SetVariable>(generator.intern_string(function.name())); } + HashMap<u32, Variable> scope_variables_with_declaration_kind; + + bool is_program_node = is<Program>(*this); + for (auto& declaration : variables()) { + for (auto& declarator : declaration.declarations()) { + if (is_program_node && declaration.declaration_kind() == DeclarationKind::Var) { + declarator.target().visit( + [&](const NonnullRefPtr<Identifier>& id) { + generator.emit<Bytecode::Op::LoadImmediate>(js_undefined()); + generator.emit<Bytecode::Op::PutById>(Bytecode::Register::global_object(), generator.intern_string(id->string())); + }, + [&](const NonnullRefPtr<BindingPattern>& binding) { + binding->for_each_assigned_name([&](const auto& name) { + generator.emit<Bytecode::Op::LoadImmediate>(js_undefined()); + generator.emit<Bytecode::Op::PutById>(Bytecode::Register::global_object(), generator.intern_string(name)); + }); + }); + } else { + declarator.target().visit( + [&](const NonnullRefPtr<Identifier>& id) { + scope_variables_with_declaration_kind.set((size_t)generator.intern_string(id->string()).value(), { js_undefined(), declaration.declaration_kind() }); + }, + [&](const NonnullRefPtr<BindingPattern>& binding) { + binding->for_each_assigned_name([&](const auto& name) { + scope_variables_with_declaration_kind.set((size_t)generator.intern_string(name).value(), { js_undefined(), declaration.declaration_kind() }); + }); + }); + } + } + } + + if (!scope_variables_with_declaration_kind.is_empty()) { + generator.emit<Bytecode::Op::PushLexicalEnvironment>(move(scope_variables_with_declaration_kind)); + } + for (auto& child : children()) { child.generate_bytecode(generator); if (generator.is_current_block_terminated()) @@ -560,8 +597,21 @@ void FunctionDeclaration::generate_bytecode(Bytecode::Generator&) const { } -void VariableDeclaration::generate_bytecode(Bytecode::Generator&) const +void VariableDeclaration::generate_bytecode(Bytecode::Generator& generator) const { + for (auto& declarator : m_declarations) { + if (declarator.init()) + declarator.init()->generate_bytecode(generator); + else + generator.emit<Bytecode::Op::LoadImmediate>(js_undefined()); + declarator.target().visit( + [&](const NonnullRefPtr<Identifier>& id) { + generator.emit<Bytecode::Op::SetVariable>(generator.intern_string(id->string())); + }, + [&](const NonnullRefPtr<BindingPattern>&) { + TODO(); + }); + } } void CallExpression::generate_bytecode(Bytecode::Generator& generator) const diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index 19a80e5aa9..901b291139 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -58,6 +58,7 @@ O(Increment) \ O(Decrement) \ O(Throw) \ + O(PushLexicalEnvironment) \ O(EnterUnwindContext) \ O(LeaveUnwindContext) \ O(ContinuePendingUnwind) diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp index c8991de6e2..27f51e8a77 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -12,6 +12,8 @@ #include <LibJS/Runtime/Array.h> #include <LibJS/Runtime/BigInt.h> #include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/LexicalEnvironment.h> +#include <LibJS/Runtime/ScopeObject.h> #include <LibJS/Runtime/ScriptFunction.h> #include <LibJS/Runtime/Value.h> @@ -266,6 +268,15 @@ void ContinuePendingUnwind::execute(Bytecode::Interpreter& interpreter) const interpreter.continue_pending_unwind(m_resume_target); } +void PushLexicalEnvironment::execute(Bytecode::Interpreter& interpreter) const +{ + HashMap<FlyString, Variable> resolved_variables; + for (auto& it : m_variables) + resolved_variables.set(interpreter.current_executable().get_string(it.key), it.value); + auto* block_lexical_environment = interpreter.vm().heap().allocate<LexicalEnvironment>(interpreter.global_object(), move(resolved_variables), interpreter.vm().current_scope()); + interpreter.vm().call_frame().scope = block_lexical_environment; +} + String Load::to_string(Bytecode::Executable const&) const { return String::formatted("Load {}", m_src); @@ -416,4 +427,19 @@ String ContinuePendingUnwind::to_string(Bytecode::Executable const&) const return String::formatted("ContinuePendingUnwind resume:{}", m_resume_target); } +String PushLexicalEnvironment::to_string(const Bytecode::Executable& executable) const +{ + StringBuilder builder; + builder.append("PushLexicalEnvironment"); + if (!m_variables.is_empty()) { + builder.append(" {"); + Vector<String> names; + for (auto& it : m_variables) + names.append(executable.get_string(it.key)); + builder.join(", ", names); + builder.append("}"); + } + return builder.to_string(); +} + } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 73e792c1df..e270003ee9 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -14,6 +14,7 @@ #include <LibJS/Bytecode/Register.h> #include <LibJS/Bytecode/StringTable.h> #include <LibJS/Heap/Cell.h> +#include <LibJS/Runtime/ScopeObject.h> #include <LibJS/Runtime/Value.h> namespace JS::Bytecode::Op { @@ -453,6 +454,19 @@ public: private: Label m_resume_target; }; + +class PushLexicalEnvironment final : public Instruction { +public: + PushLexicalEnvironment(HashMap<u32, Variable> variables) + : Instruction(Type::PushLexicalEnvironment) + , m_variables(move(variables)) + { + } + void execute(Bytecode::Interpreter&) const; + String to_string(Bytecode::Executable const&) const; + + HashMap<u32, Variable> m_variables; +}; } namespace JS::Bytecode { |