{
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;
}