瀏覽代碼

LibJS/Bytecode: Perform ToNumeric on accumulator before postfix inc/dec

This ensures we get the expected behavior of code like:

    let a = []
    let b = a++

(Where b should be 0, not [], because JavaScript.)
Andreas Kling 2 年之前
父節點
當前提交
d364d99cb8

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

@@ -1966,6 +1966,7 @@ Bytecode::CodeGenerationErrorOr<void> UpdateExpression::generate_bytecode(Byteco
     Optional<Bytecode::Register> previous_value_for_postfix_reg;
     Optional<Bytecode::Register> previous_value_for_postfix_reg;
     if (!m_prefixed) {
     if (!m_prefixed) {
         previous_value_for_postfix_reg = generator.allocate_register();
         previous_value_for_postfix_reg = generator.allocate_register();
+        generator.emit<Bytecode::Op::ToNumeric>();
         generator.emit<Bytecode::Op::Store>(*previous_value_for_postfix_reg);
         generator.emit<Bytecode::Op::Store>(*previous_value_for_postfix_reg);
     }
     }
 
 

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

@@ -88,6 +88,7 @@
     O(SuperCall)                     \
     O(SuperCall)                     \
     O(Throw)                         \
     O(Throw)                         \
     O(ThrowIfNotObject)              \
     O(ThrowIfNotObject)              \
+    O(ToNumeric)                     \
     O(Typeof)                        \
     O(Typeof)                        \
     O(TypeofVariable)                \
     O(TypeofVariable)                \
     O(UnaryMinus)                    \
     O(UnaryMinus)                    \

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

@@ -1091,6 +1091,12 @@ ThrowCompletionOr<void> TypeofVariable::execute_impl(Bytecode::Interpreter& inte
     return {};
     return {};
 }
 }
 
 
+ThrowCompletionOr<void> ToNumeric::execute_impl(Bytecode::Interpreter& interpreter) const
+{
+    interpreter.accumulator() = TRY(interpreter.accumulator().to_numeric(interpreter.vm()));
+    return {};
+}
+
 DeprecatedString Load::to_deprecated_string_impl(Bytecode::Executable const&) const
 DeprecatedString Load::to_deprecated_string_impl(Bytecode::Executable const&) const
 {
 {
     return DeprecatedString::formatted("Load {}", m_src);
     return DeprecatedString::formatted("Load {}", m_src);
@@ -1448,4 +1454,9 @@ DeprecatedString TypeofVariable::to_deprecated_string_impl(Bytecode::Executable
     return DeprecatedString::formatted("TypeofVariable {} ({})", m_identifier, executable.identifier_table->get(m_identifier));
     return DeprecatedString::formatted("TypeofVariable {} ({})", m_identifier, executable.identifier_table->get(m_identifier));
 }
 }
 
 
+DeprecatedString ToNumeric::to_deprecated_string_impl(Bytecode::Executable const&) const
+{
+    return "ToNumeric"sv;
+}
+
 }
 }

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

@@ -835,6 +835,19 @@ public:
     void replace_references_impl(Register, Register) { }
     void replace_references_impl(Register, Register) { }
 };
 };
 
 
+class ToNumeric final : public Instruction {
+public:
+    ToNumeric()
+        : Instruction(Type::ToNumeric)
+    {
+    }
+
+    ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
+    DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const;
+    void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
+    void replace_references_impl(Register, Register) { }
+};
+
 class Throw final : public Instruction {
 class Throw final : public Instruction {
 public:
 public:
     constexpr static bool IsTerminator = true;
     constexpr static bool IsTerminator = true;