public Value evaluate()

in modules/asc/src/java/macromedia/asc/semantics/ConstantEvaluator.java [1560:1882]


    public Value evaluate(Context cx, BinaryExpressionNode node)
    {
        Value val = null;
        Value lhsval = null;
        Value rhsval = null;
        TypeInfo resultType = null;

        TypeInfo[] lhstype = new TypeInfo[]{ null };
        TypeInfo[] rhstype = new TypeInfo[]{ null };
        if (node.lhs != null)
        {
            lhsval = node.lhs.evaluate(cx,this);
            if (lhsval != null)
            {
                Value constVal = lhsval.getValue(cx);
                Slot s = lhsval instanceof ReferenceValue ? ((ReferenceValue)lhsval).getSlot(cx): null;
                lhstype[0] = s != null ? s.getType():lhsval.getType(cx);      // get the static type from the reference value, or literal
                lhsval = constVal!=null&&constVal.hasValue()?constVal:null;
            }
        }
        if (node.rhs != null)
        {
            rhsval = node.rhs.evaluate(cx,this);
            if (rhsval != null)
            {
                Value constVal = rhsval.getValue(cx);
                Slot s = rhsval instanceof ReferenceValue ? ((ReferenceValue)rhsval).getSlot(cx): null;
                rhstype[0] = s != null ? s.getType():rhsval.getType(cx);      // get the static type from the reference value, or literal
                rhsval = constVal!=null&&constVal.hasValue()?constVal:null;

                if (node.op == AS_TOKEN)     // save rhsval as result of "as"
                {
                    TypeValue typeval = (constVal instanceof TypeValue) ? (TypeValue)constVal : cx.objectType();
                    resultType = typeval.getDefaultTypeInfo();
                }
            }
        }

        int slot_index = 0;


        if (cx.useStaticSemantics() && lhstype[0] != null && rhstype[0] != null ) // null types are o.k., they mean don't do type checking or op-code overriding.
        {
            switch(node.op)
            {
                // check for comparisions between incompatable types
                case EQUALS_TOKEN:
                case NOTEQUALS_TOKEN:
                case LESSTHAN_TOKEN:
                case GREATERTHAN_TOKEN:
                case LESSTHANOREQUALS_TOKEN:
                case GREATERTHANOREQUALS_TOKEN:
                case STRICTNOTEQUALS_TOKEN:
                case STRICTEQUALS_TOKEN:

                    if ( lhstype[0].getTypeValue() == cx.noType() || rhstype[0].getTypeValue() == cx.noType() ||
                         lhstype[0].getTypeValue() == cx.booleanType() || rhstype[0].getTypeValue() == cx.booleanType() ||
                         lhstype[0].isInterface()      || rhstype[0].isInterface() ||
                         lhstype[0].includes(cx,rhstype[0]) || rhstype[0].includes(cx,lhstype[0]) )
                    {
                        // no problem
                        // why boolean?  since everyting implicitly converts to boolean,
                        //  var s:string = "",
                        //  if (s) {} // should be equivalent to
                        //  if (s == true) { }
                    }
                    // check comparision between null and un-nullable type
                    else if (lhstype[0].getTypeValue() == cx.nullType() || rhstype[0].getTypeValue() == cx.nullType())
                    {
                        if (lhstype[0].getTypeValue() == cx.nullType() &&
                            (rhstype[0].getTypeValue().isNumeric(cx)   || rhstype[0].getTypeValue() == cx.booleanType() ))
                        {
                            cx.error(node.pos(), kError_IncompatableValueComparison, lhstype[0].getName(cx).toString(), rhstype[0].getName(cx).toString());
                        }
                            // yes, this could be combined with the above, but it would be hard to read
                        else if (rhstype[0].getTypeValue() == cx.nullType() &&
                                 (lhstype[0].getTypeValue().isNumeric(cx)   || lhstype[0].getTypeValue() == cx.booleanType() ))
                        {
                            cx.error(node.pos(), kError_IncompatableValueComparison, lhstype[0].getName(cx).toString(), rhstype[0].getName(cx).toString());
                        }
                        // else no problem
                    }
                    // E4X allows  XML to be compared directly with Strings
                    else if ( (lhstype[0].getTypeValue() == cx.xmlType() && rhstype[0].getTypeValue() == cx.stringType()) ||
                         (rhstype[0].getTypeValue() == cx.stringType() && lhstype[0].getTypeValue() == cx.xmlType()) )
                    {
                        // no problem, <a>test</a> == "test"; // is true
                    }
                    // Check for comparision between unrelated types (unless its between number types)
                    else if ( !((lhstype[0].getTypeValue().isNumeric(cx)) && (rhstype[0].getTypeValue().isNumeric(cx))) )
                    {
                        cx.error(node.pos(), kError_IncompatableValueComparison, lhstype[0].getName(cx).toString(), rhstype[0].getName(cx).toString());
                    }

                    break;
            }
        }

        TypeValue lhstypeval = lhstype[0] != null ? lhstype[0].getTypeValue() : null;
        TypeValue rhstypeval = rhstype[0] != null ? rhstype[0].getTypeValue() : null;
        
        TypeValue currentNumberType = cx.doubleType();
        if (node.numberUsage != null)
        	switch (node.numberUsage.get_usage()) {
         	case NumberUsage.use_int:
        		currentNumberType = cx.intType();
        		break;
        	case NumberUsage.use_uint:
        		currentNumberType = cx.uintType();
        		break;
        	case NumberUsage.use_decimal:
        		currentNumberType = cx.decimalType();
        		break;
        	case NumberUsage.use_double:
        	case NumberUsage.use_Number:
        	default:
        		currentNumberType = cx.doubleType();
        	}


        // now process op as normal
        switch (node.op)
        {
            case MULT_TOKEN:
                slot_index = SLOT_Global_MultiplyOp;
                // RES this is probably wrong with uint as a full citizen
                if ((lhstypeval == cx.intType() || lhstypeval == cx.uintType()) && (rhstypeval == cx.intType() || rhstypeval == cx.uintType()))
                {
                    resultType = cx.intType().getDefaultTypeInfo();
                }
                cx.coerce(node.lhs,lhstype,currentNumberType);
                cx.coerce(node.rhs,rhstype,currentNumberType);
                break;
            case DIV_TOKEN:
                slot_index = SLOT_Global_DivideOp;
                cx.coerce(node.lhs,lhstype,currentNumberType);
                cx.coerce(node.rhs,rhstype,currentNumberType);
                break;
            case MODULUS_TOKEN:
                slot_index = SLOT_Global_ModulusOp;
                cx.coerce(node.lhs,lhstype,currentNumberType);
                cx.coerce(node.rhs,rhstype,currentNumberType);
                break;
            case PLUS_TOKEN:
                if( lhsval != null && rhsval != null && lhsval.hasValue() && rhsval.hasValue() )
                {
                    // cn: see bug 124260.  A literal number might not always print as its compile time string rep
                    /* cn actually, this is causing errors even when lhs and rhs are strings
                    if( (lhstype[0] == cx.stringType() && rhstype[0] != cx.numberType()) ||
                        (rhstype[0] == cx.stringType() && lhstype[0] != cx.numberType()))
                    {
                        node.lhs = cx.getNodeFactory().literalString(lhsval.toString()+rhsval.toString(),0);
                        node.op = EMPTY_TOKEN;
                        val = node.lhs.evaluate(cx,this);  // get the folded value
                    }
                    */
                }
                slot_index = SLOT_Global_BinaryPlusOp;
                if ( (lhstypeval == cx.xmlListType() || lhstypeval == cx.xmlType()) &&
                     (rhstypeval == cx.xmlListType() || rhstypeval == cx.xmlType()))
                {
                    resultType = cx.xmlListType().getDefaultTypeInfo();
                }
                else if ( (lhstypeval != null) && (lhstypeval.isNumeric(cx)) &&
                          (rhstypeval != null) && (rhstypeval.isNumeric(cx)) )
                {
                    resultType = currentNumberType.getDefaultTypeInfo();
                }
                else if ( lhstypeval == cx.stringType() || rhstypeval == cx.stringType() ) // anything + a string is a string.
                {
                    resultType = cx.stringType().getDefaultTypeInfo();
                }
                else
                {
                    resultType = cx.noType().getDefaultTypeInfo(); // can't tell.  Could be number, string or xmllist
                }

                break;
            case MINUS_TOKEN:
                slot_index = SLOT_Global_BinaryMinusOp;
                cx.coerce(node.lhs,lhstype,currentNumberType);
                cx.coerce(node.rhs,rhstype,currentNumberType);
                if ((lhstypeval == cx.intType() || lhstypeval == cx.uintType()) && (rhstypeval == cx.intType() || rhstypeval == cx.uintType()))
                    resultType = cx.intType().getDefaultTypeInfo();
                break;
            case LEFTSHIFT_TOKEN:
                slot_index = SLOT_Global_LeftShiftOp;
                cx.coerce(node.lhs,lhstype,currentNumberType);
                cx.coerce(node.rhs,rhstype,currentNumberType);
                break;
            case RIGHTSHIFT_TOKEN:
                slot_index = SLOT_Global_RightShiftOp;
                cx.coerce(node.lhs,lhstype,currentNumberType);
                cx.coerce(node.rhs,rhstype,currentNumberType);
                break;
            case UNSIGNEDRIGHTSHIFT_TOKEN:
                slot_index = SLOT_Global_UnsignedRightShiftOp;
                cx.coerce(node.lhs,lhstype,currentNumberType);
                cx.coerce(node.rhs,rhstype,currentNumberType);
                break;
            case LESSTHAN_TOKEN:
                slot_index = SLOT_Global_LessThanOp;
                break;
            case GREATERTHAN_TOKEN:
                slot_index = SLOT_Global_GreaterThanOp;
                break;
            case LESSTHANOREQUALS_TOKEN:
                slot_index = SLOT_Global_LessThanOrEqualOp;
                break;
            case GREATERTHANOREQUALS_TOKEN:
                slot_index = SLOT_Global_GreaterThanOrEqualOp;
                break;
            case INSTANCEOF_TOKEN:
                slot_index = SLOT_Global_InstanceofOp;
                break;
            case IN_TOKEN:
                slot_index = SLOT_Global_InOp;
                break;
            case IS_TOKEN:
                slot_index = SLOT_Global_IsLateOp;
                cx.coerce(node.rhs,rhstype,cx.typeType());
                break;
            case AS_TOKEN:
                slot_index = SLOT_Global_AsLateOp;
                cx.coerce(node.rhs,rhstype,cx.typeType());
                break;
            case EQUALS_TOKEN:
                slot_index = SLOT_Global_EqualsOp;
                break;
            case NOTEQUALS_TOKEN:
                slot_index = SLOT_Global_NotEqualsOp;
                break;
            case STRICTEQUALS_TOKEN:
                slot_index = SLOT_Global_StrictEqualsOp;
                break;
            case STRICTNOTEQUALS_TOKEN:
                slot_index = SLOT_Global_StrictNotEqualsOp;
                break;
            case BITWISEAND_TOKEN:
                slot_index = SLOT_Global_BitwiseAndOp;
                cx.coerce(node.lhs,lhstype,currentNumberType);
                cx.coerce(node.rhs,rhstype,currentNumberType);
                break;
            case BITWISEXOR_TOKEN:
                slot_index = SLOT_Global_BitwiseXorOp;
                cx.coerce(node.lhs,lhstype,currentNumberType);
                cx.coerce(node.rhs,rhstype,currentNumberType);
                break;
            case BITWISEOR_TOKEN:
                slot_index = SLOT_Global_BitwiseOrOp;
                cx.coerce(node.lhs,lhstype,currentNumberType);
                cx.coerce(node.rhs,rhstype,currentNumberType);
                break;
            case LOGICALAND_TOKEN:
                slot_index = SLOT_Global_LogicalAndOp;

                if (lhstype[0] == rhstype[0])
                    resultType = lhstype[0];
                /*
                else if (lhstype[0] != null && lhstype[0] != cx.noType() && rhstype[0] != null && rhstype[0] != cx.noType())
                    resultType = cx.objectType(); // if its not null or *, then we at least know it can't be undefined.
                */
                break;

            case LOGICALOR_TOKEN:
                slot_index = SLOT_Global_LogicalOrOp;
                if (lhstype[0] == rhstype[0])
                    resultType = lhstype[0];
                /*
                else if (lhstype[0] != null && lhstype[0] != cx.noType() && rhstype[0] != null && rhstype[0] != cx.noType())
                    resultType = cx.objectType(); // if its not null or *, then we at least know it can't be undefined.
                */
                break;

            case EMPTY_TOKEN:
                // do nothing, already been folded
                break;
            default:
                cx.internalError("unrecognized binary operator");
                break;
        }

        Slot slot;

        if( node.op != EMPTY_TOKEN )
        {
            // ObjectValue global = null;

            ObjectValue global = cx.builtinScope();
            if (lhstypeval != cx.uintType() && rhstypeval != cx.uintType() ) // overloading not working correctly for uints
            {
                slot_index  = global.getOverloadIndex(cx, slot_index, lhstypeval, rhstypeval);
            }
            slot = global.getSlot(cx, slot_index);

            // Now we know the types expected by the overloaded operator.
            // Coerce the operands.
            node.lhs = cx.coerce(node.lhs, lhstype, size(slot.getTypes()) > 0 ? slot.getTypes().get(0) : null);
            node.rhs = cx.coerce(node.rhs, rhstype, size(slot.getTypes()) > 0 ? slot.getTypes().get(1) : null);

            node.lhstype = lhstype[0];
            node.rhstype = rhstype[0];

            // Save the slot index in the node for later use
            // by the code generator.
            node.slot = slot;

            if( lhsval instanceof ObjectValue && rhsval instanceof ObjectValue )
            {
                val = computeBinaryExpr(cx,node.op,(ObjectValue)lhsval,(ObjectValue)rhsval, node.numberUsage);
            }
            else if (resultType != null)
            {
                val = resultType.getPrototype();
            }
            else
            {
                val = slot.getType().getPrototype();
            }
        }

        return val;
    }