/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.ir.transform; import gw.lang.parser.IExpression; import gw.lang.parser.expressions.IProgram; import gw.lang.parser.expressions.IMemberAccessExpression; import gw.lang.parser.expressions.IParenthesizedExpression; import gw.lang.reflect.gs.FragmentInstance; import gw.lang.reflect.gs.IExternalSymbolMap; import gw.lang.reflect.java.JavaTypes; import gw.lang.ir.statement.IRMethodCallStatement; import gw.internal.gosu.parser.Statement; import gw.internal.gosu.parser.expressions.ImplicitTypeAsExpression; import gw.internal.gosu.parser.fragments.GosuFragment; import gw.internal.gosu.parser.statements.ReturnStatement; import gw.internal.gosu.parser.statements.ThrowStatement; import gw.lang.ir.IRClass; import gw.lang.ir.IRType; import gw.lang.ir.IRStatement; import gw.lang.ir.IRSymbol; import gw.lang.ir.IRExpression; import gw.lang.ir.IRTypeConstants; import gw.lang.ir.SyntheticIRType; import gw.internal.gosu.ir.nodes.IRMethodFactory; import gw.internal.gosu.ir.nodes.IRMethod; import gw.lang.ir.statement.IRMethodStatement; import gw.lang.ir.statement.IRReturnStatement; import gw.lang.ir.statement.IRStatementList; import gw.lang.ir.statement.IRSyntheticStatement; import gw.internal.ext.org.objectweb.asm.Opcodes; import gw.lang.parser.statements.ITerminalStatement; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class GosuFragmentTransformer extends AbstractElementTransformer<IExpression> { public static final String SYMBOLS_PARAM_NAME = "$symbols$"; public static final String SYMBOLS_PARAM_ARG_NAME = "$symbols$arg"; private GosuFragment _fragment; private GosuFragmentTransformationContext _context; private SyntheticIRType _irType; public GosuFragmentTransformer(GosuFragment fragment) { super(null, null); _fragment = fragment; _irType = new SyntheticIRType( FragmentInstance.class, fragment.getName(), fragment.getRelativeName()); _context = new GosuFragmentTransformationContext(_fragment, _irType, fragment.getName(), false ); setCc( _context ); } public static IRClass transform( GosuFragment fragment ) { return new GosuFragmentTransformer( fragment ).transform(); } private IRClass transform() { IRClass irClass = new IRClass(); addHeader( irClass ); addDefaultConstructor( irClass ); addEvaluateMethod( irClass ); addEvaluateRootMethod( irClass ); return irClass; } private void addHeader( IRClass irClass ) { irClass.setName( _fragment.getName() ); irClass.setThisType( _irType ); irClass.setModifiers( Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SUPER ); irClass.setSuperType( getDescriptor( FragmentInstance.class ) ); } private void addDefaultConstructor( IRClass irClass ) { _context.initBodyContext( false ); _context.pushScope( true ); List<IRStatement> statements = new ArrayList<IRStatement>(); IRMethod irMethod = IRMethodFactory.createConstructorIRMethod( _cc().getSuperType(), new IRType[0] ); statements.add( new IRMethodCallStatement( callSpecialMethod( getDescriptor( _cc().getSuperType() ), irMethod, pushThis(), Collections.<IRExpression>emptyList() ) ) ); statements.add( new IRReturnStatement() ); IRMethodStatement methodStatement = new IRMethodStatement( new IRStatementList( true, statements), "<init>", Opcodes.ACC_PUBLIC, IRTypeConstants.pVOID(), Collections.<IRSymbol>emptyList()); irClass.addMethod(methodStatement); } private void addEvaluateMethod( IRClass irClass ) { // public abstract Object evaluate(IExternalSymbolMap symbols); IRSymbol symbolsParam = new IRSymbol(SYMBOLS_PARAM_NAME, getDescriptor(IExternalSymbolMap.class), false); _context.initBodyContext( false ); _context.pushScope( true ); _context.putSymbol(symbolsParam); List<IRStatement> statements = new ArrayList<IRStatement>(); IExpression expression = _fragment.getExpression(); if (expression instanceof IProgram) { Statement mainStatement = (Statement) ((IProgram) expression).getMainStatement(); statements.add( StatementTransformer.compile( _context, mainStatement)); // If the program doesn't terminate, then add in an explicit return null at the end. // This is likely in the case of a program that doesn't actually return a value boolean[] bAbsolute = {false}; ITerminalStatement terminalStmt = mainStatement.getLeastSignificantTerminalStatement( bAbsolute ); if( !bAbsolute[0] || !(terminalStmt instanceof ReturnStatement) && !(terminalStmt instanceof ThrowStatement) ) { statements.add( new IRReturnStatement( null, nullLiteral() ) ); } } else if (expression.getType().equals(JavaTypes.pVOID())) { // If the expression has a void type, such as if it's a method call with no return value, then compile // it as a synthetic statement and explicitly insert a return null statements.add( new IRSyntheticStatement( ExpressionTransformer.compile( expression, _context ) ) ); statements.add( new IRReturnStatement( null, nullLiteral() ) ); } else { // If the expression has a value, just return that (after boxing it, if necessary) IRExpression returnValue = ExpressionTransformer.compile( expression, _context ); if (returnValue.getType().isPrimitive()) { returnValue = boxValue( returnValue.getType(), returnValue ); } statements.add( new IRReturnStatement( null, returnValue ) ); } IRMethodStatement methodStatement = new IRMethodStatement( new IRStatementList( true, statements), "evaluate", Opcodes.ACC_PUBLIC, IRTypeConstants.OBJECT(), Collections.singletonList(symbolsParam)); irClass.addMethod(methodStatement); } private void addEvaluateRootMethod( IRClass irClass ) { // Only bother adding in the method if the fragment's root is an IMemberAccessExpression; otherwise the method // is already implemented to return null on FragmentInstance IExpression expr = maybeUnwrap( _fragment.getExpression() ); if ( expr instanceof IMemberAccessExpression) { // public abstract Object evaluateRootExpression(IExternalSymbolMap symbols); IRSymbol symbolsParam = new IRSymbol(SYMBOLS_PARAM_NAME, getDescriptor(IExternalSymbolMap.class), false); _context.initBodyContext( false ); _context.pushScope( true ); _context.putSymbol(symbolsParam); List<IRStatement> statements = new ArrayList<IRStatement>(); IRExpression returnValue = ExpressionTransformer.compile( ((IMemberAccessExpression) expr).getRootExpression(), _context ); if (returnValue.getType().isPrimitive()) { returnValue = boxValue( returnValue.getType(), returnValue ); } statements.add( new IRReturnStatement( null, returnValue ) ); IRMethodStatement methodStatement = new IRMethodStatement( new IRStatementList( true, statements), "evaluateRootExpression", Opcodes.ACC_PUBLIC, IRTypeConstants.OBJECT(), Collections.singletonList(symbolsParam)); irClass.addMethod(methodStatement); } } private IExpression maybeUnwrap( IExpression expression ) { if( expression instanceof ImplicitTypeAsExpression ) { return maybeUnwrap( ((ImplicitTypeAsExpression)expression).getLHS() ); } else if( expression instanceof IParenthesizedExpression ) { return maybeUnwrap( ((IParenthesizedExpression)expression).getExpression() ); } return expression; } }