Browse Source

LibJS: Implement bytecode generation for UpdateExpression :^)

Added Increment and Decrement bytecode ops to support this. Postfix
updates use a temporary register to preserve the original value.

Note that this patch only implements Identifier updates. Member
expression updates are a TODO.
Andreas Kling 4 years ago
parent
commit
59eedd6de0

+ 1 - 0
Userland/Libraries/LibJS/AST.h

@@ -937,6 +937,7 @@ public:
 
     virtual Value execute(Interpreter&, GlobalObject&) const override;
     virtual void dump(int indent) const override;
+    virtual void generate_bytecode(Bytecode::Generator&) const override;
 
 private:
     UpdateOp m_op;

+ 28 - 0
Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

@@ -638,4 +638,32 @@ void TemplateLiteral::generate_bytecode(Bytecode::Generator& generator) const
         }
     }
 }
+
+void UpdateExpression::generate_bytecode(Bytecode::Generator& generator) const
+{
+    if (is<Identifier>(*m_argument)) {
+        auto& identifier = static_cast<Identifier const&>(*m_argument);
+        generator.emit<Bytecode::Op::GetVariable>(identifier.string());
+
+        Optional<Bytecode::Register> previous_value_for_postfix_reg;
+        if (!m_prefixed) {
+            previous_value_for_postfix_reg = generator.allocate_register();
+            generator.emit<Bytecode::Op::Store>(*previous_value_for_postfix_reg);
+        }
+
+        if (m_op == UpdateOp::Increment)
+            generator.emit<Bytecode::Op::Increment>();
+        else
+            generator.emit<Bytecode::Op::Decrement>();
+
+        generator.emit<Bytecode::Op::SetVariable>(identifier.string());
+
+        if (!m_prefixed)
+            generator.emit<Bytecode::Op::Load>(*previous_value_for_postfix_reg);
+        return;
+    }
+
+    TODO();
+}
+
 }

+ 3 - 1
Userland/Libraries/LibJS/Bytecode/Instruction.h

@@ -54,7 +54,9 @@
     O(UnsignedRightShift)         \
     O(In)                         \
     O(InstanceOf)                 \
-    O(ConcatString)
+    O(ConcatString)               \
+    O(Increment)                  \
+    O(Decrement)
 
 namespace JS::Bytecode {
 

+ 34 - 0
Userland/Libraries/LibJS/Bytecode/Op.cpp

@@ -231,6 +231,30 @@ void Return::execute(Bytecode::Interpreter& interpreter) const
     interpreter.do_return(interpreter.accumulator().value_or(js_undefined()));
 }
 
+void Increment::execute(Bytecode::Interpreter& interpreter) const
+{
+    auto old_value = interpreter.accumulator().to_numeric(interpreter.global_object());
+    if (interpreter.vm().exception())
+        return;
+
+    if (old_value.is_number())
+        interpreter.accumulator() = Value(old_value.as_double() + 1);
+    else
+        interpreter.accumulator() = js_bigint(interpreter.vm().heap(), old_value.as_bigint().big_integer().plus(Crypto::SignedBigInteger { 1 }));
+}
+
+void Decrement::execute(Bytecode::Interpreter& interpreter) const
+{
+    auto old_value = interpreter.accumulator().to_numeric(interpreter.global_object());
+    if (interpreter.vm().exception())
+        return;
+
+    if (old_value.is_number())
+        interpreter.accumulator() = Value(old_value.as_double() - 1);
+    else
+        interpreter.accumulator() = js_bigint(interpreter.vm().heap(), old_value.as_bigint().big_integer().minus(Crypto::SignedBigInteger { 1 }));
+}
+
 String Load::to_string() const
 {
     return String::formatted("Load {}", m_src);
@@ -349,4 +373,14 @@ String Return::to_string() const
     return "Return";
 }
 
+String Increment::to_string() const
+{
+    return "Increment";
+}
+
+String Decrement::to_string() const
+{
+    return "Decrement";
+}
+
 }

+ 22 - 0
Userland/Libraries/LibJS/Bytecode/Op.h

@@ -373,6 +373,28 @@ public:
     String to_string() const;
 };
 
+class Increment final : public Instruction {
+public:
+    Increment()
+        : Instruction(Type::Increment)
+    {
+    }
+
+    void execute(Bytecode::Interpreter&) const;
+    String to_string() const;
+};
+
+class Decrement final : public Instruction {
+public:
+    Decrement()
+        : Instruction(Type::Decrement)
+    {
+    }
+
+    void execute(Bytecode::Interpreter&) const;
+    String to_string() const;
+};
+
 }
 
 namespace JS::Bytecode {