/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.ir.transform.expression; import gw.internal.gosu.ir.nodes.IRMethodFactory; import gw.internal.gosu.ir.transform.ExpressionTransformer; import gw.internal.gosu.ir.transform.TopLevelTransformationContext; import gw.internal.gosu.parser.BeanAccess; import gw.internal.gosu.parser.expressions.AdditiveExpression; import gw.internal.gosu.parser.expressions.Identifier; import gw.internal.gosu.runtime.GosuRuntimeMethods; import gw.lang.ir.IRExpression; import gw.lang.ir.expression.IRArithmeticExpression; import gw.lang.ir.expression.IRStringLiteralExpression; import gw.lang.parser.IParseTree; import gw.lang.parser.IParsedElement; import gw.lang.parser.StandardSymbolTable; import gw.lang.reflect.IMethodInfo; import gw.lang.reflect.IType; import gw.lang.reflect.java.JavaTypes; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Collections; /** */ public class AdditiveExpressionTransformer extends AbstractExpressionTransformer<AdditiveExpression> { public static IRExpression compile( TopLevelTransformationContext cc, AdditiveExpression expr ) { AdditiveExpressionTransformer gen = new AdditiveExpressionTransformer( cc, expr ); return gen.compile(); } private AdditiveExpressionTransformer( TopLevelTransformationContext cc, AdditiveExpression expr ) { super( cc, expr ); } protected IRExpression compile_impl() { boolean bNumeric = BeanAccess.isNumericType( _expr().getType() ); IMethodInfo operatorOverload = _expr().getOverride(); if( bNumeric && operatorOverload != null ) { // The operator is overloaded, call into it... IRExpression lhs = ExpressionTransformer.compile( _expr().getLHS(), _cc() ); IRExpression rhs = ExpressionTransformer.compile( _expr().getRHS(), _cc() ); return callMethod( IRMethodFactory.createIRMethod( operatorOverload, null ), lhs, exprList( rhs ) ); } else { if( isSimpleAddition() ) { return simpleAddition(); } else if( isBigDecimalAddition() ) { return bigDecimalAddition(); } else if( isBigIntegerAddition() ) { return bigIntegerAddition(); } else { if( isCompileTimeConstantConcatenation() ) { return concatenate(); } //## todo: handle other cases efficiently // (ex var a = "1" var b = "2" var c = a + b -> we should generate code using a StringBuilder) // else // { // // } return complexAddition( bNumeric ); } } } private boolean isBigDecimalAddition() { return !_expr().isNullSafe() && JavaTypes.BIG_DECIMAL().equals( _expr().getType() ) && JavaTypes.BIG_DECIMAL().equals( _expr().getLHS().getType() ) && JavaTypes.BIG_DECIMAL().equals( _expr().getRHS().getType() ); } private IRExpression bigDecimalAddition( ) { IType type = _expr().getType(); IRExpression lhs = ExpressionTransformer.compile( _expr().getLHS(), _cc() ); IRExpression rhs = ExpressionTransformer.compile( _expr().getRHS(), _cc() ); return callMethod( BigDecimal.class, _expr().isAdditive() ? "add" : "subtract", new Class[] {BigDecimal.class}, lhs, Collections.singletonList( rhs ) ); } private boolean isBigIntegerAddition() { return !_expr().isNullSafe() && JavaTypes.BIG_INTEGER().equals( _expr().getType() ) && JavaTypes.BIG_INTEGER().equals( _expr().getLHS().getType() ) && JavaTypes.BIG_INTEGER().equals( _expr().getRHS().getType() ); } private IRExpression bigIntegerAddition( ) { IType type = _expr().getType(); IRExpression lhs = ExpressionTransformer.compile( _expr().getLHS(), _cc() ); IRExpression rhs = ExpressionTransformer.compile( _expr().getRHS(), _cc() ); return callMethod( BigInteger.class, _expr().isAdditive() ? "add" : "subtract", new Class[] {BigInteger.class}, lhs, Collections.singletonList( rhs ) ); } private IRStringLiteralExpression concatenate() { return (IRStringLiteralExpression) pushConstant( GosuRuntimeMethods.toString( _expr().getLHS().evaluate() ) + GosuRuntimeMethods.toString( _expr().getRHS().evaluate() ) ); } private IRExpression simpleAddition( ) { IType type = _expr().getType(); // Push LHS IRExpression lhs = ExpressionTransformer.compile( _expr().getLHS(), _cc() ); // Maybe convert to expression type lhs = numberConvert( _expr().getLHS().getType(), type, lhs ); // Push RHS IRExpression rhs = ExpressionTransformer.compile( _expr().getRHS(), _cc() ); // Maybe convert to expression type rhs = numberConvert( _expr().getRHS().getType(), type, rhs ); return new IRArithmeticExpression(getDescriptor( type ), lhs, rhs, _expr().isAdditive() ? IRArithmeticExpression.Operation.Addition : IRArithmeticExpression.Operation.Subtraction ); } private IRExpression complexAddition( boolean bNumeric ) { // Call into Gosu's runtime for the answer. The arguments are the result type, the boxed LHS, the boxed RHS, // the LHS type, the RHS type, whether it's addition, and whether it's numeric IRExpression evaluateCall = callStaticMethod( AdditiveExpression.class, "evaluate", new Class[]{IType.class, Object.class, Object.class, IType.class, IType.class, boolean.class, boolean.class, boolean.class}, exprList( pushType( _expr().getType() ), boxValue( _expr().getLHS().getType(), ExpressionTransformer.compile( _expr().getLHS(), _cc() ) ), boxValue( _expr().getRHS().getType(), ExpressionTransformer.compile( _expr().getRHS(), _cc() ) ), pushType( _expr().getLHS().getType() ), pushType( _expr().getRHS().getType() ), pushConstant( _expr().isAdditive() ), pushConstant( _expr().isNullSafe() ), pushConstant( bNumeric ) ) ); // Ensure value is unboxed if type is primitive return unboxValueToType( _expr().getType(), evaluateCall ); } public boolean isSimpleAddition() { return isPrimitiveNumberType( _expr().getType() ) && isPrimitiveNumberType( _expr().getLHS().getType() ) && isPrimitiveNumberType( _expr().getRHS().getType() ); } private boolean isCompileTimeConstantConcatenation() { return _expr().getType() == JavaTypes.STRING() && _expr().isCompileTimeConstant() && !containsIdentifier( _expr().isAssignment() ? _expr().getParent() : _expr() ); } private boolean containsIdentifier( IParsedElement expr ) { if ( expr instanceof Identifier && !((Identifier) expr).isStaticFinalInitializedCompileTimeConstant() ) { return true; } IParseTree location = expr.getLocation(); if( location == null ) { return true; } for( IParseTree child: location.getChildren() ) { if( containsIdentifier( child.getParsedElement() ) ) { return true; } } return false; } }