/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.ir.transform;
import gw.internal.gosu.ir.nodes.IRMethod;
import gw.internal.gosu.ir.nodes.IRMethodFactory;
import gw.internal.gosu.ir.nodes.IRPropertyFactory;
import gw.internal.gosu.ir.transform.statement.SyntheticFunctionStatementTransformer;
import gw.internal.gosu.parser.DynamicFunctionSymbol;
import gw.internal.gosu.parser.IBlockClassInternal;
import gw.internal.gosu.parser.ParseTree;
import gw.internal.gosu.parser.Statement;
import gw.internal.gosu.parser.ThisConstructorFunctionSymbol;
import gw.internal.gosu.parser.TypeLord;
import gw.internal.gosu.parser.statements.FunctionStatement;
import gw.internal.gosu.parser.statements.MethodCallStatement;
import gw.internal.gosu.parser.statements.SyntheticFunctionStatement;
import gw.lang.ir.IRExpression;
import gw.lang.ir.IRStatement;
import gw.lang.ir.IRSymbol;
import gw.lang.ir.expression.IRStringLiteralExpression;
import gw.lang.ir.statement.IRAssignmentStatement;
import gw.lang.ir.statement.IRCatchClause;
import gw.lang.ir.statement.IRMethodCallStatement;
import gw.lang.ir.statement.IRReturnStatement;
import gw.lang.ir.statement.IRStatementList;
import gw.lang.ir.statement.IRTryCatchFinallyStatement;
import gw.lang.parser.IFunctionSymbol;
import gw.lang.parser.IParseTree;
import gw.lang.parser.IParsedElement;
import gw.lang.parser.ISymbol;
import gw.lang.parser.IToken;
import gw.lang.parser.statements.IFunctionStatement;
import gw.lang.parser.statements.IReturnStatement;
import gw.lang.parser.statements.IStatementList;
import gw.lang.parser.statements.ITerminalStatement;
import gw.lang.parser.statements.IThrowStatement;
import gw.lang.reflect.IRelativeTypeInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.java.JavaTypes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
*/
public class FunctionStatementTransformer extends AbstractElementTransformer<FunctionStatement>
{
private static final boolean GW_PROFILER_WRAPPING_ENABLED = System.getProperty( "gw.enable.profiler.wrapping" ) != null || System.getProperty( "gw.enable.profiler.wrapping.tags" ) != null;
private DynamicFunctionSymbol _dfs;
FunctionStatementTransformer( DynamicFunctionSymbol dfs, TopLevelTransformationContext cc )
{
super( cc, dfs == null ? null : dfs.getDeclFunctionStmt() );
_dfs = dfs;
}
IRStatement compile()
{
List<IRStatement> statements = new ArrayList<IRStatement>();
if( _cc().isBlockInvoke() )
{
checkcastArgs( statements );
}
assignCapturedParamIndexes( statements );
compileConstructorInitializers( statements );
Statement statement = (Statement)_dfs.getValueDirectly();
if( statement instanceof SyntheticFunctionStatement )
{
statements.add( SyntheticFunctionStatementTransformer.compile( _dfs, _cc(), (SyntheticFunctionStatement)statement ) );
}
else
{
statements.add( StatementTransformer.compile( _cc(), statement ) );
}
handleImplicitReturns( statement, statements );
IRStatementList functionBody = new IRStatementList( false, statements );
if( GW_PROFILER_WRAPPING_ENABLED )
{
functionBody = wrapFunctionBodyForProfiler( functionBody );
}
return functionBody;
}
private static final AtomicInteger _numGeneratedMethods = new AtomicInteger(1);
private IRStatementList wrapFunctionBodyForProfiler( IRStatementList functionBody )
{
if( _dfs.getScriptPart() == null )
{
// The function stmt belongs to a block; we currently do not support block-level profiling
return functionBody;
}
String strippedClassName = TypeLord.getPureGenericType( _dfs.getScriptPart().getContainingType() ).getName();
String generatedClassName = "_profiler." + strippedClassName + "_" + _numGeneratedMethods.getAndIncrement();
// ProfilerFrame frame = gw.api.profiler.Profiler.push( ProfilerTag.GOSU_METHOD_WRAPPER )
IRExpression field = getField( IRPropertyFactory.createIRProperty( TypeSystem.getByFullName( "gw.api.profiler.ProfilerTag", TypeSystem.getGlobalModule() ).getTypeInfo().getProperty( "GOSU_METHOD_WRAPPER" ) ), null );
IRMethod m = IRMethodFactory.createIRMethod( TypeSystem.getByFullName( "gw.api.profiler.Profiler", TypeSystem.getGlobalModule() ),
"push", TypeSystem.getByFullName( "gw.api.profiler.ProfilerFrame", TypeSystem.getGlobalModule() ),
new IType[] {TypeSystem.getByFullName( "gw.api.profiler.ProfilerTag", TypeSystem.getGlobalModule() )},
IRelativeTypeInfo.Accessibility.PUBLIC, true );
IRExpression push = callMethod( m, null, Arrays.asList( field ) );
IRSymbol tempSymbol = _cc().makeAndIndexTempSymbol( push.getType() );
IRAssignmentStatement initAssignment = buildAssignment( tempSymbol, push );
// frame.setProperty( <generatedClassName>_<methodName> );
m = IRMethodFactory.createIRMethod( TypeSystem.getByFullName( "gw.api.profiler.ProfilerFrame", TypeSystem.getGlobalModule() ),
"setProperty", JavaTypes.pVOID(),
new IType[] {JavaTypes.STRING()},
IRelativeTypeInfo.Accessibility.PUBLIC, false );
IRExpression info = new IRStringLiteralExpression( generatedClassName + "_" + generateMethodName() );
IRExpression setProp = callMethod( m, identifier( tempSymbol ), Arrays.asList( info ) );
IRMethodCallStatement setPropStmt = new IRMethodCallStatement( setProp );
// gw.api.profiler.Profiler.pop( frame )
m = IRMethodFactory.createIRMethod( TypeSystem.getByFullName( "gw.api.profiler.Profiler", TypeSystem.getGlobalModule() ),
"pop", JavaTypes.pVOID(),
new IType[] {TypeSystem.getByFullName( "gw.api.profiler.ProfilerFrame", TypeSystem.getGlobalModule() )},
IRelativeTypeInfo.Accessibility.PUBLIC, true );
IRExpression pop = callMethod( m, null, Arrays.asList( (IRExpression)identifier( tempSymbol ) ) );
IRMethodCallStatement popStmt = new IRMethodCallStatement( pop );
IRTryCatchFinallyStatement tryFinally = new IRTryCatchFinallyStatement( functionBody, Collections.<IRCatchClause>emptyList(), popStmt );
return new IRStatementList( functionBody.hasScope(), initAssignment, setPropStmt, tryFinally );
}
private String generateMethodName()
{
String methodName = _dfs.getName();
String simpleMethodName = methodName.substring( 0, methodName.indexOf( "(" ) );
if( simpleMethodName.startsWith( "@" ) )
{
if( _dfs.getArgs().isEmpty() )
{
simpleMethodName = "get" + simpleMethodName.substring( 1 );
}
else
{
simpleMethodName = "set" + simpleMethodName.substring( 1 );
}
}
else if( simpleMethodName.equals( "super" ) )
{
simpleMethodName = "super1";
}
//if there is a FunctionStatement associated with this, use it to get the line number
FunctionStatement statement = _dfs.getDeclFunctionStmt();
if( statement != null )
{
simpleMethodName += "_line_" + statement.getLineNum();
}
return simpleMethodName;
}
private void handleImplicitReturns(Statement statement, List<IRStatement> statements)
{
IType returnType = _dfs.getReturnType();
boolean[] bAbsolute = {false};
ITerminalStatement terminalStmt = statement.getLeastSignificantTerminalStatement( bAbsolute );
if( _cc().isBlockInvoke() )
{
if( terminalStmt == null || !bAbsolute[0] )
{
//visit a label
IRReturnStatement returnStatement = new IRReturnStatement( null, nullLiteral() );
returnStatement.setLineNumber( statement.getLineNum() );
statements.add( returnStatement );
}
else if( _dfs.isLoopImplicitReturn() )
{
addImplicitReturn( statements, returnType );
}
}
else if( returnType == JavaTypes.pVOID() &&
(!bAbsolute[0] ||
!(terminalStmt instanceof IReturnStatement) &&
!(terminalStmt instanceof IThrowStatement) &&
!(terminalStmt instanceof gw.internal.gosu.parser.statements.LoopStatement)) )
{
IRReturnStatement returnStmt = new IRReturnStatement();
returnStmt.setLineNumber( getLastLineOfFunction( statement ) );
statements.add( returnStmt );
}
else if( _dfs.isLoopImplicitReturn() )
{
addImplicitReturn( statements, returnType );
}
}
private void addImplicitReturn( List<IRStatement> statements, IType returnType )
{
// This return stmt is never executed, it's only here to pacify Java's bytecode verifier
// which doesn't perform the static analysis thoroughly enough to understand that a return
// is not needed here.
gw.lang.ir.statement.IRImplicitReturnStatement returnStatement =
returnType == JavaTypes.pVOID()
? new gw.lang.ir.statement.IRImplicitReturnStatement()
: new gw.lang.ir.statement.IRImplicitReturnStatement( null, getDefaultConstIns( returnType ) );
statements.add( returnStatement );
}
private void compileConstructorInitializers( List<IRStatement> statements )
{
MethodCallStatement mc = _dfs.getInitializer();
if( mc == null )
{
// Not a constructor
return;
}
if( mc.getLineNum() <= 0 && mc.getParent() != null )
{
// So the debugger stops freaking out after stepping *into* a ctor
mc.setLineNum( mc.getParent().getLineNum() );
}
_cc().initCapturedSymbolFields( statements );
_cc().initTypeVarFields( statements );
statements.add( StatementTransformer.compile( _cc(), mc ) );
IFunctionSymbol initializerDfs = mc.getMethodCall().getFunctionSymbol();
if( !(initializerDfs instanceof ThisConstructorFunctionSymbol) )
{
_cc().initializeInstanceFields( statements );
}
}
private void assignCapturedParamIndexes( List<IRStatement> statements )
{
for( ISymbol paramSym : getActualArgSymbols() ) {
if (paramSym.isValueBoxed()) {
IRSymbol symbol = new IRSymbol(paramSym.getName(), getDescriptor(paramSym.getType().getArrayType()), false);
_cc().putSymbol(symbol);
IRExpression expression = identifier(_cc().getSymbol(symbol.getName() + "$$unboxedParam"));
IRExpression value = buildInitializedArray(getDescriptor(paramSym.getType()), exprList(expression));
statements.add(buildAssignment(symbol, value));
}
}
}
private List<ISymbol> getActualArgSymbols()
{
List<ISymbol> iSymbolList;
if( _cc().isBlockInvoke() )
{
iSymbolList = ((IBlockClassInternal)getGosuClass()).getBlock().getArgs();
}
else
{
iSymbolList = _dfs.getArgs();
}
return iSymbolList;
}
private void checkcastArgs( List<IRStatement> statements )
{
for( ISymbol paramSym : getActualArgSymbols() )
{
IType actualType = paramSym.getType();
IRSymbol properlyTypedSymbol = new IRSymbol( paramSym.getName() + (paramSym.isValueBoxed() ? "$$unboxedParam" : ""), getDescriptor(actualType), false );
_cc().putSymbol( properlyTypedSymbol );
IRExpression value;
if( actualType.isPrimitive() )
{
value = unboxValueToType( actualType, identifier( _cc().getSymbol( paramSym.getName() + "$$blockParam" ) ) );
}
else
{
value = checkCast( actualType, identifier( _cc().getSymbol( paramSym.getName() + "$$blockParam" ) ) );
}
statements.add( buildAssignment( properlyTypedSymbol, value ) );
}
}
public int getLastLineOfFunction( Statement stmt )
{
if( stmt == null )
{
return -1;
}
Statement temp = stmt;
while( stmt != null && !(stmt instanceof IFunctionStatement) )
{
stmt = (Statement)stmt.getParent();
}
if( stmt == null )
{
stmt = temp;
}
ParseTree location = stmt.getLocation();
List<IParseTree> children = location == null ? null : location.getChildren();
if( children != null && children.size() > 0 )
{
for( IParseTree child : children )
{
IParsedElement pe = child.getParsedElement();
if( pe instanceof IStatementList )
{
stmt = (Statement)pe;
break;
}
}
}
List<IToken> tokens = stmt.getTokens();
for( int i = tokens.size()-1; i >= 0; i-- )
{
IToken token = tokens.get( i );
if( token.getText().equals( "}" ) )
{
return token.getLine();
}
}
return -1;
}
}