/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.ir.transform.expression;
import gw.internal.gosu.ir.transform.TopLevelTransformationContext;
import gw.internal.gosu.ir.transform.util.AccessibilityUtil;
import gw.internal.gosu.parser.*;
import gw.internal.gosu.parser.OuterFunctionSymbol;
import gw.lang.ir.IRExpression;
import gw.internal.gosu.ir.nodes.IRMethod;
import gw.internal.gosu.ir.nodes.IRMethodFactory;
import gw.internal.gosu.ir.nodes.IRProperty;
import gw.internal.gosu.ir.nodes.IRPropertyFactory;
import gw.lang.parser.*;
import gw.lang.parser.expressions.IIdentifierExpression;
import gw.lang.reflect.gs.ICompilableType;
import gw.lang.reflect.gs.IExternalSymbolMap;
import gw.lang.reflect.IType;
import gw.lang.reflect.gs.IGosuProgram;
import gw.lang.reflect.java.JavaTypes;
import java.util.Arrays;
import java.util.Collections;
/**
*/
public class IdentifierTransformer extends AbstractExpressionTransformer<IIdentifierExpression>
{
public static IRExpression compile( TopLevelTransformationContext cc, IIdentifierExpression expr )
{
IdentifierTransformer compiler = new IdentifierTransformer( cc, expr );
return compiler.compile();
}
private IdentifierTransformer( TopLevelTransformationContext cc, IIdentifierExpression expr )
{
super( cc, expr );
}
protected IRExpression compile_impl()
{
ISymbol symbol = _expr().getSymbol();
ICompilableType gsClass = getGosuClass();
if( (Keyword.KW_this.equals( symbol.getName() ) ||
Keyword.KW_super.equals( symbol.getName() )) &&
// 'this' must be an external symbol when in a program e.g., studio debugger expression
(!(gsClass instanceof IGosuProgram) || gsClass.isAnonymous()) )
{
if( _cc().isBlockInvoke() && _cc().currentlyCompilingBlock() )
{
while( gsClass instanceof IBlockClass )
{
gsClass = gsClass.getEnclosingType();
}
return pushOuter( gsClass );
}
else
{
return pushThis();
}
}
else if( symbol instanceof DynamicPropertySymbol && ((DynamicPropertySymbol)symbol).getGetterDfs() instanceof OuterFunctionSymbol )
{
// 'outer'
return pushOuterForOuterSymbol();
}
else
{
return pushSymbolValue( symbol );
}
}
public IRExpression pushSymbolValue( ISymbol symbol )
{
IType type = symbol.getType();
Class symClass;
IReducedSymbol reducedSym;
if( symbol instanceof ReducedSymbol.SyntheticSymbol )
{
reducedSym = ((ReducedSymbol.SyntheticSymbol) symbol).getReducedSymbol();
symClass = reducedSym.getSymbolClass();
}
else
{
reducedSym = symbol;
symClass = symbol.getClass();
}
if ( _cc().isExternalSymbol(reducedSym.getName()) )
{
// unbox( symbols.getValue( name ) )
return unboxValueToType(reducedSym.getType(),
callMethod( IExternalSymbolMap.class, "getValue", new Class[]{String.class, int.class},
pushExternalSymbolsMap(),
Arrays.asList( pushConstant( reducedSym.getName() ), pushConstant( getArrayDims( reducedSym ) ))));
}
else if( ScopedDynamicSymbol.class.isAssignableFrom( symClass ) )
{
return getScopedSymbolValue( reducedSym );
}
else if( DynamicSymbol.class.isAssignableFrom( symClass ) )
{
// Instance or Static field
IRProperty irProp = IRPropertyFactory.createIRProperty( reducedSym );
if( !irProp.isStatic() )
{
if( isMemberOnEnclosingType( reducedSym ) != null )
{
// Instance field from 'outer'
return getField_new( irProp, pushOuter( reducedSym.getGosuClass() ), getDescriptor( reducedSym.getType() ) );
}
else
{
// Instance field from 'this'
return getField_new( irProp, pushThis(), getDescriptor( reducedSym.getType() ) );
}
}
else
{
// Static field
return getField_new( irProp, null, getDescriptor( reducedSym.getType() ) );
}
}
else if( CapturedSymbol.class.isAssignableFrom( symClass ) )
{
// Captured symbol is stored as a Field on an anonymous inner class (one elem array of symbol's type)
// e.g., val$myFiield[0] = value. Note a captured symbol is duplicated in all nested classes.
IRProperty irProp = IRPropertyFactory.createIRProperty( getGosuClass(), reducedSym );
return getField_new( irProp, pushThis(), getDescriptor( reducedSym.getType() ) );
}
else if( reducedSym.getIndex() >= 0 )
{
// Local var
if( reducedSym.isValueBoxed() )
{
// Local var is captured in an anonymous inner class.
// Symbol's value maintained as a one elem array of symbol's type.
return buildArrayLoad( identifier( _cc().getSymbol( reducedSym.getName() ) ), 0, getDescriptor( type ));
}
else
{
// Simple local var
return identifier( _cc().getSymbol( reducedSym.getName() ) );
}
}
else if( DynamicPropertySymbol.class.isAssignableFrom( symClass ) )
{
IRProperty irProp;
if( reducedSym instanceof DynamicPropertySymbol )
{
irProp = IRPropertyFactory.createIRProperty((DynamicPropertySymbol) reducedSym);
}
else
{
irProp = IRPropertyFactory.createIRProperty((ReducedDynamicPropertySymbol) reducedSym);
}
IRExpression root;
if( irProp.isStatic() )
{
root = null;
}
else
{
ICompilableType targetType = isMemberOnEnclosingType( reducedSym );
if( targetType != null )
{
root = pushOuter( targetType );
}
else
{
root = pushThis();
}
}
IRExpression getterCall = callMethod( irProp.getGetterMethod(), root, Collections.<IRExpression>emptyList() );
return castResultingTypeIfNecessary(getDescriptor(reducedSym.getType()), irProp.getType(), getterCall);
}
else
{
throw new UnsupportedOperationException( "Don't know how to compile symbol: " + reducedSym.getClass().getSimpleName() + ": " + reducedSym.getName() + ": " + reducedSym.getType() );
}
}
private int getArrayDims( IReducedSymbol reducedSym ) {
IType type = reducedSym.getType();
if( type == JavaTypes.OBJECT() ) {
// Special case for handling Object -- pcf does strange stuff where it says a var is Object but expects a on dim array
return -1;
}
int iDims;
for( iDims = 0; type.isArray(); iDims++ ) {
type = type.getComponentType();
}
return iDims;
}
protected IRExpression pushOuterForOuterSymbol()
{
IRExpression root = pushOuter();
IType currentClass = getGosuClass();
while( currentClass instanceof IBlockClass )
{
currentClass = currentClass.getEnclosingType();
IRMethod irMethod = IRMethodFactory.createIRMethod( currentClass, OUTER_ACCESS, currentClass.getEnclosingType(), new IType[]{currentClass}, AccessibilityUtil.forOuterAccess(), true );
root = callMethod( irMethod, null, Collections.singletonList( root ) );
}
IType outer = currentClass.getEnclosingType();
while( outer instanceof IBlockClass )
{
IType enclosing = getRuntimeEnclosingType( outer );
IRMethod irMethod = IRMethodFactory.createIRMethod( outer, OUTER_ACCESS, enclosing, new IType[]{outer}, AccessibilityUtil.forOuterAccess(), true );
root = callMethod( irMethod, null, Collections.singletonList( root ) );
outer = enclosing;
}
return root;
}
}