summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2021-06-11 00:35:25 +0200
committerAndreas Kling <kling@serenityos.org>2021-06-11 00:36:18 +0200
commit9ee5029bc5ab24d614525acd3229300063aa1d68 (patch)
tree20e96f3ea5836d1765acc31ab09093a38379987e
parentb47246ec70e8243639772cc1cc280ed42a6c4ff5 (diff)
downloadserenity-9ee5029bc5ab24d614525acd3229300063aa1d68.zip
LibJS: Basic bytecode support for computed member expressions
Expressions like foo[1 + 2] now work, and you can assign to them as well! :^)
-rw-r--r--Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp14
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Instruction.h2
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Op.cpp30
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Op.h32
4 files changed, 75 insertions, 3 deletions
diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp
index e5e3704bbe..9b0c95b749 100644
--- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp
+++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp
@@ -375,14 +375,18 @@ void AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) con
generator.emit<Bytecode::Op::Store>(object_reg);
if (expression.is_computed()) {
- TODO();
+ expression.property().generate_bytecode(generator);
+ auto property_reg = generator.allocate_register();
+ generator.emit<Bytecode::Op::Store>(property_reg);
+ m_rhs->generate_bytecode(generator);
+ generator.emit<Bytecode::Op::PutByValue>(object_reg, property_reg);
} else {
VERIFY(is<Identifier>(expression.property()));
m_rhs->generate_bytecode(generator);
auto identifier_table_ref = generator.intern_string(static_cast<Identifier const&>(expression.property()).string());
generator.emit<Bytecode::Op::PutById>(object_reg, identifier_table_ref);
- return;
}
+ return;
}
TODO();
@@ -585,7 +589,11 @@ void MemberExpression::generate_bytecode(Bytecode::Generator& generator) const
object().generate_bytecode(generator);
if (is_computed()) {
- TODO();
+ auto object_reg = generator.allocate_register();
+ generator.emit<Bytecode::Op::Store>(object_reg);
+
+ property().generate_bytecode(generator);
+ generator.emit<Bytecode::Op::GetByValue>(object_reg);
} else {
VERIFY(is<Identifier>(property()));
auto identifier_table_ref = generator.intern_string(static_cast<Identifier const&>(property()).string());
diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h
index b3a622898f..c60fb451d8 100644
--- a/Userland/Libraries/LibJS/Bytecode/Instruction.h
+++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h
@@ -35,6 +35,8 @@
O(SetVariable) \
O(PutById) \
O(GetById) \
+ O(PutByValue) \
+ O(GetByValue) \
O(Jump) \
O(JumpConditional) \
O(JumpNullish) \
diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp
index 2d080627c1..265556b9b1 100644
--- a/Userland/Libraries/LibJS/Bytecode/Op.cpp
+++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp
@@ -291,6 +291,26 @@ void Yield::execute(Bytecode::Interpreter& interpreter) const
interpreter.do_return(object);
}
+void GetByValue::execute(Bytecode::Interpreter& interpreter) const
+{
+ if (auto* object = interpreter.reg(m_base).to_object(interpreter.global_object())) {
+ auto property_key = interpreter.accumulator().to_property_key(interpreter.global_object());
+ if (interpreter.vm().exception())
+ return;
+ interpreter.accumulator() = object->get(property_key);
+ }
+}
+
+void PutByValue::execute(Bytecode::Interpreter& interpreter) const
+{
+ if (auto* object = interpreter.reg(m_base).to_object(interpreter.global_object())) {
+ auto property_key = interpreter.reg(m_property).to_property_key(interpreter.global_object());
+ if (interpreter.vm().exception())
+ return;
+ object->put(property_key, interpreter.accumulator());
+ }
+}
+
String Load::to_string(Bytecode::Executable const&) const
{
return String::formatted("Load {}", m_src);
@@ -463,4 +483,14 @@ String Yield::to_string(Bytecode::Executable const&) const
return String::formatted("Yield return");
}
+String GetByValue::to_string(const Bytecode::Executable&) const
+{
+ return String::formatted("GetByValue base:{}", m_base);
+}
+
+String PutByValue::to_string(const Bytecode::Executable&) const
+{
+ return String::formatted("PutByValue base:{}, property:{}", m_base, m_property);
+}
+
}
diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h
index f5961b87e0..79212526ea 100644
--- a/Userland/Libraries/LibJS/Bytecode/Op.h
+++ b/Userland/Libraries/LibJS/Bytecode/Op.h
@@ -266,6 +266,38 @@ private:
StringTableIndex m_property;
};
+class GetByValue final : public Instruction {
+public:
+ explicit GetByValue(Register base)
+ : Instruction(Type::GetByValue)
+ , m_base(base)
+ {
+ }
+
+ void execute(Bytecode::Interpreter&) const;
+ String to_string(Bytecode::Executable const&) const;
+
+private:
+ Register m_base;
+};
+
+class PutByValue final : public Instruction {
+public:
+ PutByValue(Register base, Register property)
+ : Instruction(Type::PutByValue)
+ , m_base(base)
+ , m_property(property)
+ {
+ }
+
+ void execute(Bytecode::Interpreter&) const;
+ String to_string(Bytecode::Executable const&) const;
+
+private:
+ Register m_base;
+ Register m_property;
+};
+
class Jump : public Instruction {
public:
constexpr static bool IsTerminator = true;