/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.ir.transform.statement; import gw.config.CommonServices; import gw.internal.gosu.ir.transform.TopLevelTransformationContext; import gw.internal.gosu.parser.DelegateFunctionSymbol; import gw.internal.gosu.parser.DynamicFunctionSymbol; import gw.internal.gosu.parser.Expression; import gw.internal.gosu.parser.TemplateRenderFunctionSymbol; import gw.internal.gosu.parser.Statement; import gw.internal.gosu.parser.Symbol; import gw.internal.gosu.parser.VarPropertyGetFunctionSymbol; import gw.internal.gosu.parser.VarPropertySetFunctionSymbol; import gw.internal.gosu.parser.expressions.ArrayAccess; import gw.internal.gosu.parser.expressions.BeanMethodCallExpression; import gw.internal.gosu.parser.expressions.Identifier; import gw.internal.gosu.parser.expressions.ImplicitTypeAsExpression; import gw.internal.gosu.parser.expressions.MemberAccess; import gw.internal.gosu.parser.expressions.NewExpression; import gw.internal.gosu.parser.expressions.NullExpression; import gw.internal.gosu.parser.expressions.NumericLiteral; import gw.internal.gosu.parser.expressions.TypeLiteral; import gw.internal.gosu.parser.statements.ArrayAssignmentStatement; import gw.internal.gosu.parser.statements.BeanMethodCallStatement; import gw.internal.gosu.parser.statements.MemberAssignmentStatement; import gw.internal.gosu.parser.statements.ReturnStatement; import gw.internal.gosu.parser.statements.StatementList; import gw.internal.gosu.parser.statements.SyntheticFunctionStatement; import gw.internal.gosu.parser.statements.VarStatement; import gw.lang.ir.IRExpression; import gw.lang.ir.IRStatement; import gw.lang.ir.statement.IRReturnStatement; import gw.lang.parser.ISymbol; import gw.lang.parser.StandardSymbolTable; import gw.lang.reflect.FunctionType; import gw.lang.reflect.IParameterInfo; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.java.JavaTypes; import java.util.ArrayList; import java.util.List; /** */ public class SyntheticFunctionStatementTransformer extends AbstractStatementTransformer<SyntheticFunctionStatement> { private DynamicFunctionSymbol _dfs; public static IRStatement compile( DynamicFunctionSymbol dfs, TopLevelTransformationContext cc, SyntheticFunctionStatement stmt ) { SyntheticFunctionStatementTransformer gen = new SyntheticFunctionStatementTransformer( dfs, cc, stmt ); return gen.compile(); } private SyntheticFunctionStatementTransformer( DynamicFunctionSymbol dfs, TopLevelTransformationContext cc, SyntheticFunctionStatement stmt ) { super( cc, stmt ); _dfs = dfs; } @Override protected IRStatement compile_impl() { if( _dfs instanceof VarPropertyGetFunctionSymbol ) { return compileReturnStatementForGetter(); } else if( _dfs instanceof VarPropertySetFunctionSymbol ) { return compileAssignmentStatementForSetter(); } else if( _dfs instanceof DelegateFunctionSymbol ) { return compileDelegatedMethod(); } else if( _dfs instanceof TemplateRenderFunctionSymbol ) { return compileForwardingMethod(); } else { throw new UnsupportedOperationException( "Found a SyntheticFunctionStatement with an unexpected dynamic function symbol type " + _dfs.getClass() ); } } private IRStatement compileForwardingMethod() { TemplateRenderFunctionSymbol forwardingDfs = (TemplateRenderFunctionSymbol)_dfs; BeanMethodCallExpression methodCall = new BeanMethodCallExpression(); methodCall.setMethodDescriptor( forwardingDfs.getMi() ); methodCall.setType( forwardingDfs.getReturnType() ); List<Statement> stmts = new ArrayList<Statement>(); IParameterInfo[] targetMethodParams = forwardingDfs.getMi().getParameters(); List<Expression> args = new ArrayList<Expression>(); args.add( new TypeLiteral( forwardingDfs.getTemplateType() ) ); for( int i = 0; i < forwardingDfs.getArgs().size(); i++ ) { if( i+2 == targetMethodParams.length && targetMethodParams[i+1].getFeatureType().isArray() ) { // Handle var-arg argument Symbol forwarding_var_args_ = new Symbol( "_forwarding_var_args_", JavaTypes.OBJECT().getArrayType(), null ); forwarding_var_args_.setIndex( 1 ); VarStatement arrayVarStmt = new VarStatement(); arrayVarStmt.setSymbol( forwarding_var_args_ ); NewExpression arrayInit = new NewExpression(); arrayInit.setType( forwarding_var_args_.getType() ); int iSize = forwardingDfs.getArgs().size() - i; arrayInit.addSizeExpression( new NumericLiteral( String.valueOf( iSize ), iSize, JavaTypes.pINT() ) ); arrayVarStmt.setAsExpression( arrayInit ); stmts.add( arrayVarStmt ); Identifier array = new Identifier(); array.setSymbol( forwarding_var_args_, null ); array.setType( JavaTypes.OBJECT().getArrayType() ); for( int j = i; j < forwardingDfs.getArgs().size(); j++ ) { Expression argId = new Identifier(); ISymbol argSym = forwardingDfs.getArgs().get( j ); ((Identifier)argId).setSymbol( argSym, null ); argId.setType( ((Identifier)argId).getSymbol().getType() ); if( argSym.getType().isPrimitive() ) { argId = box( argId ); } ArrayAccess access = new ArrayAccess(); access.setRootExpression( array ); access.setMemberExpression( new NumericLiteral( String.valueOf( j - i ), j - i, JavaTypes.pINT() ) ); access.setType( JavaTypes.OBJECT() ); ArrayAssignmentStatement assignmentStmt = new ArrayAssignmentStatement(); assignmentStmt.setArrayAccessExpression( access ); assignmentStmt.setExpression( argId ); stmts.add( assignmentStmt ); } args.add( array ); break; } else { Identifier argId = new Identifier(); argId.setSymbol( forwardingDfs.getArgs().get( i ), null ); argId.setType( argId.getSymbol().getType() ); args.add( argId ); } } if( args.size() < targetMethodParams.length ) { // Handle case where template has no args args.add( new NullExpression() ); } methodCall.setArgs( args.toArray( new Expression[args.size()] ) ); if( _dfs.getReturnType() != JavaTypes.pVOID() ) { ReturnStatement returnStatement = new ReturnStatement(); returnStatement.setValue( methodCall ); stmts.add( returnStatement ); } else { BeanMethodCallStatement methodCallStatement = new BeanMethodCallStatement(); methodCallStatement.setBeanMethodCall( methodCall ); stmts.add( methodCallStatement ); } StatementList stmtList = new StatementList( new StandardSymbolTable() ); stmtList.setStatements( stmts ); return StatementListTransformer.compile( _cc(), stmtList ); } private Expression box( Expression expr ) { ImplicitTypeAsExpression cast = new ImplicitTypeAsExpression(); cast.setLHS( expr ); cast.setCoercer( CommonServices.getCoercionManager().resolveCoercerStatically( JavaTypes.OBJECT(), expr.getType() ) ); cast.setType( TypeSystem.getBoxType( expr.getType() ) ); return cast; } private IRStatement compileAssignmentStatementForSetter() { VarPropertySetFunctionSymbol setter = (VarPropertySetFunctionSymbol)_dfs; final ISymbol valueSymbol = setter.getArgs().get( 0 ); return buildFieldSet( getDescriptor( getGosuClass() ), setter.getVarIdentifier().toString(), getDescriptor( valueSymbol.getType() ), (setter.isStatic() ? null : pushThis()), identifier( _cc().getSymbol( valueSymbol.getName() ) ) ); } private IRStatement compileReturnStatementForGetter() { VarPropertyGetFunctionSymbol getter = (VarPropertyGetFunctionSymbol)_dfs; IRExpression returnValue = buildFieldGet( getDescriptor( getGosuClass() ), getter.getVarIdentifier().toString(), getDescriptor( getter.getReturnType() ), (getter.isStatic() ? null : pushThis()) ); return new IRReturnStatement( null, returnValue ); } private IRStatement compileDelegatedMethod() { DelegateFunctionSymbol delegateSymbol = (DelegateFunctionSymbol)_dfs; Identifier id = new Identifier(); ISymbol varSymbol = getGosuClass().getMemberField( delegateSymbol.getDelegateStmt().getIdentifierName() ).getSymbol(); id.setSymbol( varSymbol, null ); id.setType( varSymbol.getType() ); if( delegateSymbol.getName().startsWith( "@" ) ) { String propertyName = delegateSymbol.getDisplayName().substring( 1 ); if( delegateSymbol.getArgs().isEmpty() ) { // Getter MemberAccess memberAccess = new MemberAccess(); memberAccess.setRootExpression( id ); memberAccess.setMemberName( propertyName ); memberAccess.setType( delegateSymbol.getReturnType() ); ReturnStatement returnStatement = new ReturnStatement(); returnStatement.setValue( memberAccess ); return ReturnStatementTransformer.compile( _cc(), returnStatement ); } else { // Setter Identifier argId = new Identifier(); argId.setSymbol( delegateSymbol.getArgs().get( 0 ), null ); argId.setType( argId.getSymbol().getType() ); MemberAssignmentStatement memberAssignment = new MemberAssignmentStatement(); memberAssignment.setRootExpression( id ); memberAssignment.setMemberName( propertyName ); memberAssignment.setExpression( argId ); return MemberAssignmentStatementTransformer.compile( _cc(), memberAssignment ); } } else { /// Normal method call BeanMethodCallExpression methodCall = new BeanMethodCallExpression(); methodCall.setRootExpression( id ); methodCall.setMethodDescriptor( delegateSymbol.getMi() ); methodCall.setType( delegateSymbol.getReturnType() ); // PL-21982 methodCall.setFunctionType( new FunctionType(delegateSymbol.getMi())); Expression[] args = new Expression[delegateSymbol.getArgs().size()]; for( int i = 0; i < delegateSymbol.getArgs().size(); i++ ) { Identifier argId = new Identifier(); argId.setSymbol( delegateSymbol.getArgs().get( i ), null ); argId.setType( argId.getSymbol().getType() ); args[i] = argId; } methodCall.setArgs( args ); if( _dfs.getReturnType() != JavaTypes.pVOID() ) { ReturnStatement returnStatement = new ReturnStatement(); returnStatement.setValue( methodCall ); return ReturnStatementTransformer.compile( _cc(), returnStatement ); } else { BeanMethodCallStatement methodCallStatement = new BeanMethodCallStatement(); methodCallStatement.setBeanMethodCall( methodCall ); return BeanMethodCallStatementTransformer.compile( _cc(), methodCallStatement ); } } } }