/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.ir.transform.expression;
import gw.internal.gosu.ir.nodes.JavaClassIRType;
import gw.internal.gosu.parser.BeanAccess;
import gw.internal.gosu.parser.ParserBase;
import gw.internal.gosu.parser.expressions.ConditionalExpression;
import gw.internal.gosu.parser.expressions.EqualityExpression;
import gw.internal.gosu.parser.expressions.NullExpression;
import gw.internal.gosu.ir.transform.ExpressionTransformer;
import gw.internal.gosu.ir.transform.TopLevelTransformationContext;
import gw.lang.ir.IRExpression;
import gw.lang.ir.IRSymbol;
import gw.lang.ir.expression.IRConditionalAndExpression;
import gw.lang.ir.expression.IRConditionalOrExpression;
import gw.lang.ir.expression.IREqualityExpression;
import gw.lang.ir.expression.IRNotExpression;
import gw.lang.ir.statement.IRAssignmentStatement;
import gw.lang.reflect.IType;
import gw.lang.reflect.java.JavaTypes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
*/
public class EqualityExpressionTransformer extends AbstractExpressionTransformer<EqualityExpression>
{
public static IRExpression compile( TopLevelTransformationContext cc, EqualityExpression expr )
{
EqualityExpressionTransformer gen = new EqualityExpressionTransformer( cc, expr );
return gen.compile();
}
private EqualityExpressionTransformer( TopLevelTransformationContext cc, EqualityExpression expr )
{
super( cc, expr );
}
protected IRExpression compile_impl()
{
IType lhsType = _expr().getLHS().getType();
IType rhsType = _expr().getRHS().getType();
if( _expr().getLHS() instanceof NullExpression ||
_expr().getRHS() instanceof NullExpression )
{
return compareToNull();
}
else if( lhsType.isPrimitive() && rhsType.isPrimitive() )
{
return comparePrimitives();
}
else if( lhsType == rhsType )
{
if( (JavaTypes.NUMBER().isAssignableFrom( lhsType ) ||
JavaTypes.IDIMENSION().isAssignableFrom( lhsType )) &&
JavaTypes.COMPARABLE().isAssignableFrom( lhsType ) )
{
// Standard Number types in Java implement Comparable to handle ==, <, > etc.
return compareWithCompareTo();
}
return compareWithEquals();
}
//## todo: maybe do a quick identity compare, if same we can avoid calling into gs runtime
// else if( !lhsType.isPrimitive() && !rhsType.isPrimitive() )
// {
//
// }
else
{
return compareDynamically();
}
}
private IRExpression comparePrimitives()
{
IRExpression lhs = ExpressionTransformer.compile( _expr().getLHS(), _cc() );
IRExpression rhs = ExpressionTransformer.compile( _expr().getRHS(), _cc() );
// Get the upper bound type
IType lhsType = _expr().getLHS().getType();
IType rhsType = _expr().getRHS().getType();
// If neither side is a boolean, then find the upper-bound of their types
// for a number conversion
if (lhsType != JavaTypes.pBOOLEAN() && rhsType != JavaTypes.pBOOLEAN()) {
IType type = ParserBase.resolveType( lhsType, '>', rhsType );
lhs = numberConvert( _expr().getLHS().getType(), type, lhs );
rhs = numberConvert( _expr().getRHS().getType(), type, rhs );
}
return new IREqualityExpression( lhs, rhs, _expr().isEquals() );
}
private IRExpression compareToNull()
{
IRExpression lhs = ExpressionTransformer.compile( _expr().getLHS(), _cc() );
if ( !( _expr().getLHS() instanceof NullExpression ) ) {
lhs = boxValue( _expr().getLHS().getType(), lhs );
}
IRExpression rhs = ExpressionTransformer.compile( _expr().getRHS(), _cc() );
if ( !( _expr().getRHS() instanceof NullExpression ) ) {
rhs = boxValue( _expr().getRHS().getType(), rhs );
}
return new IREqualityExpression( lhs, rhs, _expr().isEquals() );
}
private IRExpression compareWithEquals()
{
// Generates following code:
// Object lhs = <lhs-expr>
// Object rhs = <rhs-expr>
// [!](lhs == rhs || (lhs != null && rhs != null && lhs.equals( rhs )))
IRSymbol lhsTemp = _cc().makeAndIndexTempSymbol( getDescriptor( JavaTypes.OBJECT() ) );
IRAssignmentStatement tempLhsAssignment = buildAssignment( lhsTemp, ExpressionTransformer.compile( _expr().getLHS(), _cc() ) );
IRSymbol rhsTemp = _cc().makeAndIndexTempSymbol( getDescriptor( JavaTypes.OBJECT() ) );
IRAssignmentStatement tempRhsAssignment = buildAssignment( rhsTemp, ExpressionTransformer.compile( _expr().getRHS(), _cc() ) );
IRExpression callEquals = callMethod( Object.class, "equals", new Class[]{Object.class},
identifier( lhsTemp ),
Collections.singletonList( (IRExpression)identifier( rhsTemp ) ) );
IRExpression theExpr = new IRConditionalOrExpression( buildEquals( identifier( lhsTemp ), identifier( rhsTemp ) ),
new IRConditionalAndExpression( buildNotEquals( identifier( lhsTemp ), nullLiteral() ),
new IRConditionalAndExpression(
buildNotEquals( identifier( rhsTemp ), nullLiteral() ),
callEquals ) ) );
return buildComposite( tempLhsAssignment,
tempRhsAssignment,
_expr().isEquals() ? theExpr : new IRNotExpression( theExpr ) );
}
private IRExpression compareWithCompareTo()
{
// Generates following code:
// Comparable lhs = <lhs-expr>
// Comparable rhs = <rhs-expr>
// [!](lhs == rhs || (lhs != null && rhs != null && lhs.compareTo( rhs ) == 0))
IRSymbol lhsTemp = _cc().makeAndIndexTempSymbol( getDescriptor( JavaTypes.COMPARABLE() ) );
IRAssignmentStatement tempLhsAssignment = buildAssignment( lhsTemp, ExpressionTransformer.compile( _expr().getLHS(), _cc() ) );
IRSymbol rhsTemp = _cc().makeAndIndexTempSymbol( getDescriptor( JavaTypes.COMPARABLE() ) );
IRAssignmentStatement tempRhsAssignment = buildAssignment( rhsTemp, ExpressionTransformer.compile( _expr().getRHS(), _cc() ) );
IRExpression callCompareTo = callMethod( Comparable.class, "compareTo", new Class[]{Object.class},
identifier( lhsTemp ),
Collections.singletonList( (IRExpression)identifier( rhsTemp ) ) );
IRExpression theExpr = new IRConditionalOrExpression( buildEquals( identifier( lhsTemp ), identifier( rhsTemp ) ),
new IRConditionalAndExpression( buildNotEquals( identifier( lhsTemp ), nullLiteral() ),
new IRConditionalAndExpression(
buildNotEquals( identifier( rhsTemp ), nullLiteral() ),
buildEquals( callCompareTo, pushConstant( 0 ) ) ) ) );
return buildComposite( tempLhsAssignment,
tempRhsAssignment,
_expr().isEquals() ? theExpr : new IRNotExpression( theExpr ) );
}
private IRExpression compareDynamically()
{
List<IRExpression> args = new ArrayList<IRExpression>();
// Push the LHS expression value and make sure it's boxed for the method call
args.add( boxValue( _expr().getLHS().getType(), ExpressionTransformer.compile( _expr().getLHS(), _cc() ) ) );
// Push the LHS Type
args.add( pushType( _expr().getLHS().getType() ) );
// Push is-equals
args.add( pushConstant( _expr().isEquals() ) );
// Push the RHS expression value and make sure it's boxed for the method call
args.add( boxValue( _expr().getRHS().getType(), ExpressionTransformer.compile( _expr().getRHS(), _cc() ) ) );
// Push the RHS Type
args.add( pushType( _expr().getRHS().getType() ) );
// Call into Gosu runtime for equality
return callStaticMethod( EqualityExpressionTransformer.class, "evaluate",
new Class[]{Object.class, IType.class, boolean.class, Object.class, IType.class},
args);
}
public static boolean evaluate( Object lhsValue, IType lhsType, boolean bEquals, Object rhsValue, IType rhsType )
{
boolean bValue;
if( lhsValue != null && rhsValue != null && BeanAccess.isNumericType( lhsType ) )
{
bValue = ConditionalExpression.compareNumbers( lhsValue, rhsValue, lhsType, rhsType ) == 0 ? Boolean.TRUE : Boolean.FALSE;
}
else
{
bValue = BeanAccess.areValuesEqual( lhsType, lhsValue,
rhsType, rhsValue );
}
return bEquals ? bValue : !bValue;
}
}