/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.ir.transform.expression;
import gw.config.CommonServices;
import gw.internal.gosu.ir.nodes.IRTypeFactory;
import gw.internal.gosu.ir.nodes.JavaClassIRType;
import gw.lang.ir.IRExpression;
import gw.lang.ir.IRStatement;
import gw.lang.ir.IRSymbol;
import gw.internal.gosu.ir.transform.ExpressionTransformer;
import gw.internal.gosu.ir.transform.TopLevelTransformationContext;
import gw.internal.gosu.parser.CompoundType;
import gw.lang.ir.IRType;
import gw.lang.ir.expression.IRCompositeExpression;
import gw.lang.ir.expression.IRIdentifier;
import gw.lang.ir.expression.IRInstanceOfExpression;
import gw.lang.parser.GosuParserTypes;
import gw.lang.parser.ICoercer;
import gw.lang.parser.coercers.BasePrimitiveCoercer;
import gw.lang.parser.coercers.FunctionFromInterfaceCoercer;
import gw.lang.parser.coercers.IdentityCoercer;
import gw.lang.parser.coercers.RuntimeCoercer;
import gw.lang.parser.expressions.ITypeAsExpression;
import gw.lang.reflect.IFunctionType;
import gw.lang.reflect.IRelativeTypeInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.Modifier;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.java.GosuTypes;
import gw.lang.reflect.java.JavaTypes;
import gw.util.concurrent.LockingLazyVar;
import java.lang.reflect.Field;
import java.util.Collections;
/**
*/
public class TypeAsTransformer extends AbstractExpressionTransformer<ITypeAsExpression>
{
public static IRExpression compile( TopLevelTransformationContext cc, ITypeAsExpression expr )
{
TypeAsTransformer gen = new TypeAsTransformer( cc, expr );
return gen.compile();
}
private TypeAsTransformer( TopLevelTransformationContext cc, ITypeAsExpression expr )
{
super( cc, expr );
}
protected IRExpression compile_impl()
{
// Push the LHS expression value
IRExpression root = ExpressionTransformer.compile( _expr().getLHS(), _cc() );
IType asType = _expr().getType();
IType lhsType = _expr().getLHS().getType();
if( _expr().getType().getName().equals( GosuTypes.IMONITORLOCK().getName() ) )
{
return root;
}
if( lhsType == asType )
{
return root;
}
if( isPrimitiveNumberType( asType ) &&
isPrimitiveNumberType( lhsType ) )
{
return numberConvert( lhsType, asType, root );
}
if( isPrimitiveNumberType( lhsType ) )
{
// Handle boxing directly (bypass coercion manager)
if( asType.isAssignableFrom( TypeSystem.getBoxType( lhsType ) ) )
{
return boxValue( lhsType, root );
}
if( isNonBigBoxedNumberType( asType ) )
{
IType primitiveAsType = TypeSystem.getPrimitiveType( asType );
if( isPrimitiveNumberType( primitiveAsType ) )
{
return boxValueToType( asType, numberConvert( lhsType, primitiveAsType, root ) );
}
}
}
IRExpression asPrimitive = maybeMakePrimitive( root );
if( asPrimitive != null )
{
return asPrimitive;
}
IRExpression result = callCoercer( root );
if( asType.isPrimitive() )
{
result = unboxValueToType( asType, result );
}
return result;
}
private IRExpression callCoercer( IRExpression root )
{
// Ensure the value is boxed (the coercer takes an Object)
IType lhsType = _expr().getLHS().getType();
if( lhsType.isPrimitive() )
{
root = boxValue( lhsType, root );
}
ICoercer coercer = _expr().getCoercer();
if( (coercer == IdentityCoercer.instance() && !_expr().getType().isPrimitive()) ||
_expr().getType() instanceof CompoundType )
{
if( (!lhsType.isPrimitive() || lhsType == JavaTypes.pVOID()) && lhsType != _expr().getType() )
{
if( (lhsType == JavaTypes.OBJECT() || lhsType.isInterface()) && isBytecodeType( _expr().getType() ) )
{
//## hack:
// The lhs is Object or an interface and is being cast to a bytecode-based type;
// The "coercer" is the identity coercer, which, in a sane world, would mean
// a straight cast and no actual coercion. Since we are insane poeple, it's possible
// there could be a runtime coercer involved (dammit), we have to check the type
// of lhs at runtime and do the cast if the type checks out. The idea is to avoid
// doing a runtime coercion because that is much more expensive than a
// simple checkcast.
//
// Geneate code like the following:
//
// LhsType temp = <lhs-expr>
// temp instanceof AsType ? (AsType)temp : coerce( temp, AsType )
//
IRType asType = getDescriptor( _expr().getType() );
IRSymbol rootValue = _cc().makeAndIndexTempSymbol( root.getType() );
root = buildComposite(
buildAssignment( rootValue, root ),
buildTernary( new IRInstanceOfExpression( identifier( rootValue ), asType ),
checkCast( _expr().getType(), identifier( rootValue ) ),
coerce( identifier( rootValue ), RuntimeCoercer.instance() ),
asType ) );
}
else
{
root = checkCast( _expr().getType(), root );
}
}
// Boxing the value is sufficient; identity coercer simply returns whatever you pass in
return root;
}
return coerce( root, coercer );
}
private IRExpression coerce( IRExpression root, ICoercer coercer )
{
IRExpression result;
//special handling to inline the function from interface coercer (needs more context information)
if (coercer instanceof FunctionFromInterfaceCoercer) {
IFunctionType returnType = (IFunctionType) _expr().getReturnType();
int length = returnType.getParameterTypes().length;
String functionTypeName = "gw.lang.function.IFunction" + length;
IType functionType = TypeSystem.getByFullName(functionTypeName);
result = callStaticMethod(FunctionFromInterfaceCoercer.class, "doCoercion", new Class[]{Class.class, Class.class, Object.class}, exprList(
classLiteral(IRTypeFactory.get(functionType)),
classLiteral(IRTypeFactory.get(_expr().getLHS().getType())),
root
));
} else {
// Push the coercer
IRExpression coercerExpression = null;
if (coercer != null) {
if (coercer instanceof BasePrimitiveCoercer) {
for (Field f : BasePrimitiveCoercer.class.getDeclaredFields()) {
try {
if (Modifier.isPublic(f.getModifiers()) && LockingLazyVar.class.isAssignableFrom(f.getType())) {
Object value = f.get(null);
LockingLazyVar<BasePrimitiveCoercer> lv = (LockingLazyVar<BasePrimitiveCoercer>) value;
if (lv.get() == coercer) {
IRExpression coercerField = getStaticField(TypeSystem.get(BasePrimitiveCoercer.class), f.getName(),
JavaClassIRType.get(LockingLazyVar.class), IRelativeTypeInfo.Accessibility.PUBLIC);
IRSymbol coercerSym = new IRSymbol(_cc().makeTempSymbolName(), JavaClassIRType.get(LockingLazyVar.class), true);
_cc().putSymbol(coercerSym);
IRStatement tempAssignStmt = buildAssignment(coercerSym, coercerField);
IRExpression getCoercerCall = buildMethodCall(LockingLazyVar.class, "get", Object.class, new Class[0], new IRIdentifier(coercerSym), Collections.<IRExpression>emptyList());
coercerExpression = new IRCompositeExpression(tempAssignStmt, getCoercerCall);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
} else {
coercerExpression = callStaticMethod(coercer.getClass(), "instance", new Class[0], exprList());
}
} else {
coercerExpression = pushNull();
}
result = callStaticMethod(TypeAsTransformer.class, "coerceValue", new Class[]{Object.class, IType.class, ICoercer.class},
exprList(root, pushType(_expr().getType()), coercerExpression));
}
if (!_expr().getType().isPrimitive()) {
result = checkCast(_expr().getType(), result);
}
return result;
}
private IRExpression maybeMakePrimitive( IRExpression lhsExpression )
{
IType asType = _expr().getType();
if( !asType.isPrimitive() )
{
return null;
}
IType lhsType = _expr().getLHS().getType();
if( lhsType.isPrimitive() )
{
return null;
}
if( lhsType == JavaTypes.BOOLEAN() &&
asType == JavaTypes.pBOOLEAN() )
{
return convertBoxedToPrimitive( lhsType, Boolean.class, "booleanValue", lhsExpression);
}
else if( lhsType == JavaTypes.BYTE() &&
asType == JavaTypes.pBYTE() )
{
return convertBoxedToPrimitive( lhsType, Byte.class, "byteValue", lhsExpression);
}
else if( lhsType == JavaTypes.CHARACTER() &&
asType == JavaTypes.pCHAR() )
{
return convertBoxedToPrimitive( lhsType, Character.class, "charValue", lhsExpression);
}
else if( lhsType == JavaTypes.SHORT() &&
asType == JavaTypes.pSHORT() )
{
return convertBoxedToPrimitive( lhsType, Short.class, "shortValue", lhsExpression);
}
else if( lhsType == JavaTypes.INTEGER() &&
asType == JavaTypes.pINT() )
{
return convertBoxedToPrimitive( lhsType, Integer.class, "intValue", lhsExpression);
}
else if( lhsType == JavaTypes.LONG() &&
asType == JavaTypes.pLONG() )
{
return convertBoxedToPrimitive( lhsType, Long.class, "longValue", lhsExpression);
}
else if( lhsType == JavaTypes.FLOAT() &&
asType == JavaTypes.pFLOAT() )
{
return convertBoxedToPrimitive( lhsType, Float.class, "floatValue", lhsExpression);
}
else if( lhsType == JavaTypes.DOUBLE() &&
asType == JavaTypes.pDOUBLE() )
{
return convertBoxedToPrimitive( lhsType, Double.class, "doubleValue", lhsExpression);
}
else
{
return null;
}
}
public static Object coerceValue( Object value, IType type, ICoercer coercer )
{
if( type == GosuParserTypes.NUMBER_TYPE() )
{
return CommonServices.getCoercionManager().makeDoubleFrom( value );
}
else if( type == GosuParserTypes.STRING_TYPE() )
{
return CommonServices.getCoercionManager().makeStringFrom( value );
}
else if( type == GosuParserTypes.DATETIME_TYPE() )
{
return CommonServices.getCoercionManager().makeDateFrom( value );
}
if( coercer != null && (value != null || coercer.handlesNull()) )
{
return coercer.coerceValue( type, value );
}
return value;
}
private IRExpression convertBoxedToPrimitive(IType lhsType, Class cls, String methodName, IRExpression lhsExpression) {
IRSymbol tempRootSymbol = _cc().makeAndIndexTempSymbol( getDescriptor( lhsType ) );
return buildComposite(
buildAssignment( tempRootSymbol, lhsExpression ),
buildNullCheckTernary(
identifier( tempRootSymbol ),
convertBoxedNullToPrimitive( lhsType ),
callMethod( cls, methodName, new Class[0], identifier(tempRootSymbol), exprList() ) ) );
}
}