mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 17:27:35 +00:00
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.
This commit is contained in:
parent
d560ee118d
commit
c3c68399b5
4 changed files with 92 additions and 1 deletions
|
@ -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
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
O(Increment) \
|
||||
O(Decrement) \
|
||||
O(Throw) \
|
||||
O(PushLexicalEnvironment) \
|
||||
O(EnterUnwindContext) \
|
||||
O(LeaveUnwindContext) \
|
||||
O(ContinuePendingUnwind)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue