summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2021-06-10 22:12:21 +0200
committerAndreas Kling <kling@serenityos.org>2021-06-10 22:20:23 +0200
commitc3c68399b5e7019c30e5ac6fb7cb38d586f2a7f3 (patch)
tree4df63cef779db4f69bd47143c9019f0b3e023291 /Userland
parentd560ee118d0d253972770a06af99512723db9be1 (diff)
downloadserenity-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.cpp52
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Instruction.h1
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Op.cpp26
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Op.h14
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 {