/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.ir.transform.expression; import gw.internal.gosu.ir.nodes.IRMethod; import gw.internal.gosu.ir.nodes.IRMethodFactory; import gw.internal.gosu.ir.nodes.JavaClassIRType; import gw.internal.gosu.ir.transform.ExpressionTransformer; import gw.internal.gosu.ir.transform.GosuFragmentTransformer; import gw.internal.gosu.ir.transform.TopLevelTransformationContext; import gw.internal.gosu.parser.CommonSymbolsScope; import gw.internal.gosu.parser.DynamicFunctionSymbol; import gw.internal.gosu.parser.Expression; import gw.internal.gosu.parser.InitConstructorFunctionSymbol; import gw.internal.gosu.parser.SuperConstructorFunctionSymbol; import gw.internal.gosu.parser.ThisConstructorFunctionSymbol; import gw.internal.gosu.parser.expressions.Identifier; import gw.internal.gosu.parser.expressions.MethodCallExpression; import gw.internal.gosu.parser.statements.BeanMethodCallStatement; import gw.internal.gosu.runtime.GosuRuntimeMethods; import gw.internal.gosu.template.TemplateGenerator; import gw.lang.function.IBlock; import gw.lang.ir.IRExpression; import gw.lang.ir.IRStatement; import gw.lang.ir.IRSymbol; import gw.lang.ir.IRType; import gw.lang.ir.IRTypeConstants; import gw.lang.parser.IExpression; import gw.lang.parser.IFunctionSymbol; import gw.lang.parser.StandardSymbolTable; import gw.lang.reflect.IAttributedFeatureInfo; import gw.lang.reflect.IFunctionType; import gw.lang.reflect.IMethodInfo; import gw.lang.reflect.IPlaceholder; import gw.lang.reflect.IRelativeTypeInfo; import gw.lang.reflect.IType; import gw.lang.reflect.gs.IExternalSymbolMap; import gw.lang.reflect.gs.IGosuEnhancement; import gw.lang.reflect.java.JavaTypes; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** */ public class MethodCallExpressionTransformer extends AbstractExpressionTransformer<MethodCallExpression> { public static IRExpression compile( TopLevelTransformationContext cc, MethodCallExpression expr ) { MethodCallExpressionTransformer compiler = new MethodCallExpressionTransformer( cc, expr ); return compiler.compile(); } private MethodCallExpressionTransformer( TopLevelTransformationContext cc, MethodCallExpression expr ) { super( cc, expr ); } protected IRExpression compile_impl() { IFunctionSymbol symbol = _expr().getFunctionSymbol(); if( _cc().isExternalSymbol( symbol.getName() ) ) { return callExternalProgramSymbol(symbol); } else if( symbol instanceof DynamicFunctionSymbol ) { return callDynamicFunctionSymbol(symbol); } else if( symbol instanceof CommonSymbolsScope.LockedDownSymbol ) { // 'Global' static function call e.g., print() return callGlobalStaticFunction(symbol); } else if( symbol == TemplateGenerator.PRINT_CONTENT_SYMBOL.get() ) { return callPrintContent(); } else if( symbol.getType() instanceof IPlaceholder && ((IPlaceholder)symbol.getType()).isPlaceholder() ) { return callBlockViaDynamicType( symbol ); } else { throw new UnsupportedOperationException( "Don't know how to compile symbol: " + symbol.getType() ); } } private IRExpression callBlockViaDynamicType( IFunctionSymbol symbol ) { if( !(symbol.getType() instanceof IPlaceholder) ) { throw new IllegalArgumentException( "Expecting symbol to have dynamic type" ); } // Generates: ((IBlock)symbolValue).invokeWithArgs( args ) Identifier identifier = new Identifier(); identifier.setSymbol( symbol, symbol.getDynamicSymbolTable() ); IRExpression idExpr = IdentifierTransformer.compile( _cc(), identifier ); idExpr = buildCast( JavaClassIRType.get( IBlock.class ), idExpr ); List<IRExpression> irArgs = new ArrayList<IRExpression>(); pushArgumentsNoCasting( null, _expr().getArgs(), irArgs ); IRExpression objArray = collectArgsIntoObjArray( irArgs ); return callMethod( IBlock.class, "invokeWithArgs", new Class[]{Object[].class}, idExpr, Collections.singletonList( objArray ) ); } private IRExpression callExternalProgramSymbol(IFunctionSymbol symbol) { List<IRExpression> argValues = new ArrayList<IRExpression>(); Expression[] args = _expr().getArgs(); if (args != null) { for( int i = 0; i < args.length; i++ ) { IExpression arg = args[i]; IRExpression irArg = ExpressionTransformer.compile( arg, _cc() ); if( arg.getType().isPrimitive() ) { irArg = boxValue( arg.getType(), irArg ); } argValues.add( irArg ); } } IRExpression methodCall = callMethod( IExternalSymbolMap.class, "invoke", new Class[]{String.class, Object[].class}, pushExternalSymbolsMap(), exprList( pushConstant( symbol.getName() ), buildInitializedArray(IRTypeConstants.OBJECT(), argValues ) ) ); IType returnType = ((IFunctionType)symbol.getType()).getReturnType(); if( returnType != JavaTypes.pVOID() ) { return unboxValueToType( returnType, methodCall ); } else { return methodCall; } } private IRExpression callDynamicFunctionSymbol(IFunctionSymbol symbol) { DynamicFunctionSymbol dfs = (DynamicFunctionSymbol)symbol; IRExpression result; if( dfs instanceof ThisConstructorFunctionSymbol || dfs instanceof SuperConstructorFunctionSymbol || dfs instanceof InitConstructorFunctionSymbol) { // Call 'this( xxx )' or 'super( xxx )' from ctor result = callSuperOrThisConstructorSymbol( dfs, dfs instanceof SuperConstructorFunctionSymbol || dfs instanceof InitConstructorFunctionSymbol ); } else { // It's a normal method call, either static or instance: determine the root appropriately IRExpression root; boolean isOnThis = false; if (dfs.isStatic()) { root = null; } else if( isMemberOnEnhancementOfEnclosingType( dfs ) ) { root = pushOuter( ((IGosuEnhancement) dfs.getGosuClass()).getEnhancedType() ); } else if( isMemberOnEnclosingType( dfs ) != null ) { root = pushOuter( dfs.getGosuClass() ); } else { root = pushThis(); isOnThis = true; } IAttributedFeatureInfo methodOrConstructorInfo = dfs.getMethodOrConstructorInfo(); if( methodOrConstructorInfo instanceof IMethodInfo) { IRMethod mi = IRMethodFactory.createIRMethod( (IMethodInfo)methodOrConstructorInfo, _expr().getFunctionType() ); if (isOnThis && mi.getAccessibility() == IRelativeTypeInfo.Accessibility.PRIVATE) { // private methods are always invoked as special result = callMethod( mi, root, pushArguments( mi ), _expr().getNamedArgOrder(), true ); } else { result = callMethod( mi, root, pushArguments( mi ), _expr().getNamedArgOrder() ); } } else { throw new UnsupportedOperationException(); } } return castIfReturnTypeDerivedFromTypeVariable( dfs, result ); } private IRExpression callSuperOrThisConstructorSymbol( DynamicFunctionSymbol dfs, boolean isSuper ) { IRExpression result; List<IRStatement> bonusStatements = new ArrayList<IRStatement>(); _cc().maybeAssignOuterRef( bonusStatements ); // Assemble the args List<IRExpression> implicitArgs = new ArrayList<IRExpression>(); IType targetType; if( isSuper ) { // In the case of a super call, we want to push the appropriate enclosing arguments _cc().maybePushSupersEnclosingThisRef( implicitArgs ); targetType = _cc().getSuperType(); } else { // In the case of a this call, we want to pass along the implicit outer argument, if any if( _cc().isNonStaticInnerClass() ) { implicitArgs.add( identifier( _cc().getSymbol( _cc().getOuterThisParamName() ) ) ); } targetType = getGosuClass(); } pushCapturedSymbols( _cc().getSuperType(), implicitArgs, true ); // This is a bit nasty, but if the super type requires external symbol capture, then we need to use // the identifier instead of the field value, since it's prior to the super invocation. So we do it // ourselves, rather than in pushCapturedSymbols if( requiresExternalSymbolCapture( _cc().getSuperType() ) ) { implicitArgs.add( identifier( _cc().getSymbol( GosuFragmentTransformer.SYMBOLS_PARAM_NAME + "arg" ) ) ); } int iTypeParams = pushTypeParametersForConstructor( _expr(), targetType, implicitArgs ); pushEnumSuperConstructorArguments( implicitArgs ); IRMethod irMethod = IRMethodFactory.createConstructorIRMethod( targetType, dfs, iTypeParams ); List<IRExpression> explicitArgs = pushArguments( irMethod ); IRExpression methodCall = callSpecialMethod( getDescriptor( targetType ), irMethod, pushThis(), implicitArgs, explicitArgs, _expr().getNamedArgOrder() ); _cc().markSuperInvoked(); if( bonusStatements.isEmpty() ) { result = methodCall; } else { result = buildComposite( bonusStatements.get( 0 ), methodCall ); } return result; } private IRExpression callGlobalStaticFunction(IFunctionSymbol symbol) { if( symbol.getDisplayName().equals( StandardSymbolTable.PRINT.getName() ) ) { IRMethod method = IRMethodFactory.createIRMethod( GosuRuntimeMethods.class, "print", Object.class ); return callMethod( method, null, pushArguments( method ) ); } else { throw new UnsupportedOperationException(symbol.getDisplayName()); } } private IRExpression callPrintContent() { IRSymbol currentTemplate = TemplateStringLiteralTransformer.getCurrentTemplateSymbol(); return callMethod( StringBuilder.class, "append", new Class[]{Object.class}, identifier( currentTemplate ), exprList( ExpressionTransformer.compile( _expr().getArgs()[0], _cc() ) ) ); } private IRExpression castIfReturnTypeDerivedFromTypeVariable( DynamicFunctionSymbol dfs, IRExpression root ) { if( !(_expr().getParent() instanceof BeanMethodCallStatement) ) { IType retType = _expr().getReturnType(); if( retType != JavaTypes.pVOID() && !retType.isPrimitive() ) { IAttributedFeatureInfo methodOrConstructorInfo = dfs.getMethodOrConstructorInfo(); if( methodOrConstructorInfo instanceof IMethodInfo ) { IMethodInfo mi = (IMethodInfo)methodOrConstructorInfo; IRMethod irMethod = IRMethodFactory.createIRMethod( mi, null ); if( !getDescriptor( retType ).isAssignableFrom( irMethod.getReturnType() ) ) { return checkCast( retType, root ); } } } } return root; } private List<IRExpression> pushArguments( IRMethod irMethod ) { List<IRExpression> irArgs = new ArrayList<IRExpression>(); Expression[] args = _expr().getArgs(); if( args != null ) { List<IRType> explicitParamTypes = irMethod.getExplicitParameterTypes(); for( int i = 0; i < args.length; i++ ) { IExpression arg = args[i]; IRExpression irArg = ExpressionTransformer.compile( arg, _cc() ); // Maybe cast if not directly assignable (e.g., cross cast) IRType paramClass = explicitParamTypes.get( i ); if( !paramClass.isAssignableFrom( irArg.getType() ) ) { irArg = buildCast( paramClass, irArg ); } irArgs.add( irArg ); } } return irArgs; } }