Fix increment and decrement operators
authorLars Knoll <lars.knoll@digia.com>
Wed, 12 Dec 2012 21:46:57 +0000 (22:46 +0100)
committerErik Verbruggen <erik.verbruggen@digia.com>
Wed, 12 Dec 2012 13:57:43 +0000 (14:57 +0100)
These operators have semantics that are different from
(foo + 1), as they always convert the LHS to a number first.

Change-Id: I3fb4a1a328e3dfcb334875435c3cec90d01b67dd
Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
moth/qv4isel_moth.cpp
qmljs_runtime.cpp
qmljs_runtime.h
qv4codegen.cpp
qv4ir.cpp
qv4ir_p.h
qv4isel_masm.cpp

index 328fdce..5b00ccf 100644 (file)
@@ -653,6 +653,8 @@ void InstructionSelection::visitMove(IR::Move *s)
                 case IR::OpUMinus: op = VM::__qmljs_uminus; break;
                 case IR::OpUPlus: op = VM::__qmljs_uplus; break;
                 case IR::OpCompl: op = VM::__qmljs_compl; break;
+                case IR::OpIncrement: op = VM::__qmljs_increment; break;
+                case IR::OpDecrement: op = VM::__qmljs_decrement; break;
                 default: assert(!"unreachable"); break;
                 } // switch
 
index 25a3c11..5ef2c6c 100644 (file)
@@ -873,6 +873,28 @@ void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *
     ctx->createMutableBinding(name, deletable);
 }
 
+Value __qmljs_increment(Value value, ExecutionContext *ctx)
+{
+    TRACE1(value);
+
+    if (value.isInteger())
+        return Value::fromInt32(value.integerValue() + 1);
+
+    double d = __qmljs_to_number(value, ctx);
+    return Value::fromDouble(d + 1);
+}
+
+Value __qmljs_decrement(Value value, ExecutionContext *ctx)
+{
+    TRACE1(value);
+
+    if (value.isInteger())
+        return Value::fromInt32(value.integerValue() - 1);
+
+    double d = __qmljs_to_number(value, ctx);
+    return Value::fromDouble(d - 1);
+}
+
 } // extern "C"
 
 
index 2de6f19..263c151 100644 (file)
@@ -174,6 +174,8 @@ Value __qmljs_uplus(Value value, ExecutionContext *ctx);
 Value __qmljs_uminus(Value value, ExecutionContext *ctx);
 Value __qmljs_compl(Value value, ExecutionContext *ctx);
 Value __qmljs_not(Value value, ExecutionContext *ctx);
+Value __qmljs_increment(Value value, ExecutionContext *ctx);
+Value __qmljs_decrement(Value value, ExecutionContext *ctx);
 
 Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index);
 Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name);
index 4802786..fef6b4e 100644 (file)
@@ -515,6 +515,10 @@ IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr)
                 return expr;
             case IR::OpCompl:
                 return _block->CONST(IR::NumberType, ~VM::Value::toInt32(c->value));
+            case IR::OpIncrement:
+                return _block->CONST(IR::NumberType, c->value + 1);
+            case IR::OpDecrement:
+                return _block->CONST(IR::NumberType, c->value - 1);
             default:
                 break;
             }
@@ -567,6 +571,8 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right)
                 case IR::OpUMinus:
                 case IR::OpUPlus:
                 case IR::OpCompl:
+                case IR::OpIncrement:
+                case IR::OpDecrement:
                 case IR::OpInvalid:
                     break;
                 }
@@ -1310,13 +1316,21 @@ bool Codegen::visit(ObjectLiteral *ast)
 
 bool Codegen::visit(PostDecrementExpression *ast)
 {
+    // ###
+    //    Throw a SyntaxError exception if the following conditions are all true:
+    //    Type(lhs) is Reference is true
+    //    IsStrictReference(lhs) is true
+    //    Type(GetBase(lhs)) is Environment Record
+    //    GetReferencedName(lhs) is either "eval" or "arguments"
+
+
     Result expr = expression(ast->base);
     if (_expr.accept(nx)) {
-        move(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub);
+        move(*expr, unop(IR::OpDecrement, *expr));
     } else {
         const unsigned t = _block->newTemp();
         move(_block->TEMP(t), *expr);
-        move(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub);
+        move(*expr, unop(IR::OpDecrement, _block->TEMP(t)));
         _expr.code = _block->TEMP(t);
     }
     return false;
@@ -1324,13 +1338,20 @@ bool Codegen::visit(PostDecrementExpression *ast)
 
 bool Codegen::visit(PostIncrementExpression *ast)
 {
+    // ###
+    //    Throw a SyntaxError exception if the following conditions are all true:
+    //    Type(lhs) is Reference is true
+    //    IsStrictReference(lhs) is true
+    //    Type(GetBase(lhs)) is Environment Record
+    //    GetReferencedName(lhs) is either "eval" or "arguments"
+
     Result expr = expression(ast->base);
     if (_expr.accept(nx)) {
-        move(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd);
+        move(*expr, unop(IR::OpIncrement, *expr));
     } else {
         const unsigned t = _block->newTemp();
         move(_block->TEMP(t), *expr);
-        move(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd);
+        move(*expr, unop(IR::OpIncrement, _block->TEMP(t)));
         _expr.code = _block->TEMP(t);
     }
     return false;
@@ -1338,8 +1359,15 @@ bool Codegen::visit(PostIncrementExpression *ast)
 
 bool Codegen::visit(PreDecrementExpression *ast)
 {
+    // ###
+    //    Throw a SyntaxError exception if the following conditions are all true:
+    //    Type(lhs) is Reference is true
+    //    IsStrictReference(lhs) is true
+    //    Type(GetBase(lhs)) is Environment Record
+    //    GetReferencedName(lhs) is either "eval" or "arguments"
+
     Result expr = expression(ast->expression);
-    move(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub);
+    move(*expr, unop(IR::OpDecrement, *expr));
     if (_expr.accept(nx)) {
         // nothing to do
     } else {
@@ -1350,9 +1378,15 @@ bool Codegen::visit(PreDecrementExpression *ast)
 
 bool Codegen::visit(PreIncrementExpression *ast)
 {
-    Result expr = expression(ast->expression);
-    move(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd);
+    // ###
+    //    Throw a SyntaxError exception if the following conditions are all true:
+    //    Type(lhs) is Reference is true
+    //    IsStrictReference(lhs) is true
+    //    Type(GetBase(lhs)) is Environment Record
+    //    GetReferencedName(lhs) is either "eval" or "arguments"
 
+    Result expr = expression(ast->expression);
+    move(*expr, unop(IR::OpIncrement, *expr));
     if (_expr.accept(nx)) {
         // nothing to do
     } else {
index 3cceeb8..33b8b77 100644 (file)
--- a/qv4ir.cpp
+++ b/qv4ir.cpp
@@ -73,6 +73,8 @@ const char *opname(AluOp op)
     case OpUMinus: return "-";
     case OpUPlus: return "+";
     case OpCompl: return "~";
+    case OpIncrement: return "++";
+    case OpDecrement: return "--";
 
     case OpBitAnd: return "&";
     case OpBitOr: return "|";
index c6d05d2..b605e60 100644 (file)
--- a/qv4ir_p.h
+++ b/qv4ir_p.h
@@ -115,6 +115,8 @@ enum AluOp {
     OpUMinus,
     OpUPlus,
     OpCompl,
+    OpIncrement,
+    OpDecrement,
 
     OpBitAnd,
     OpBitOr,
index d667fa5..cabd1f7 100644 (file)
@@ -187,6 +187,8 @@ const Assembler::BinaryOperationInfo Assembler::binaryOperations[QQmlJS::IR::Las
     NULL_OP, // OpUMinus
     NULL_OP, // OpUPlus
     NULL_OP, // OpCompl
+    NULL_OP, // OpIncrement
+    NULL_OP, // OpDecrement
 
     INLINE_OP(__qmljs_bit_and, &Assembler::inline_and32, &Assembler::inline_and32), // OpBitAnd
     INLINE_OP(__qmljs_bit_or, &Assembler::inline_or32, &Assembler::inline_or32), // OpBitOr
@@ -688,6 +690,8 @@ void InstructionSelection::visitMove(IR::Move *s)
                     case IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break;
                     case IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break;
                     case IR::OpCompl: setOp(op, opName, __qmljs_compl); break;
+                    case IR::OpIncrement: setOp(op, opName, __qmljs_increment); break;
+                    case IR::OpDecrement: setOp(op, opName, __qmljs_decrement); break;
                     default: assert(!"unreachable"); break;
                     } // switch