/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser; import gw.config.CommonServices; import gw.internal.gosu.compiler.FunctionClassUtil; import gw.internal.gosu.parser.expressions.BeanMethodCallExpression; import gw.internal.gosu.parser.expressions.BlockExpression; import gw.internal.gosu.parser.expressions.Identifier; import gw.internal.gosu.parser.statements.ReturnStatement; import gw.lang.reflect.java.JavaTypes; import gw.internal.gosu.parser.statements.VarStatement; import gw.lang.function.IBlock; import gw.lang.parser.GlobalScope; import gw.lang.parser.ICapturedSymbol; import gw.lang.parser.IParsedElement; import gw.lang.parser.IStatement; import gw.lang.parser.ISymbol; import gw.lang.parser.Keyword; import gw.lang.parser.StandardSymbolTable; import gw.lang.reflect.FunctionType; import gw.lang.reflect.IFunctionType; import gw.lang.reflect.IType; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.GosuClassTypeLoader; import gw.lang.reflect.gs.ICompilableType; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; public class BlockClass extends SyntheticClass implements IBlockClassInternal { private static final AtomicInteger TMP_INT_IDENTIFIER = new AtomicInteger( 0 ); private BlockClass( BlockExpression blk ) { super("_todo_remove_me", "_todo_remove_me." + GosuClassTypeLoader.BLOCK_PREFIX + TMP_INT_IDENTIFIER.incrementAndGet() + GosuClassTypeLoader.BLOCK_POSTFIX, TypeSystem.getTypeLoader(GosuClassTypeLoader.class), null, CommonServices.getEntityAccess().getDefaultTypeUses()); initCompilationState(); createNewParseInfo(); getParseInfo().setBlock(blk); } private BlockClass( ICompilableType enclosingClass, int i, BlockExpression blk ) { super( enclosingClass.getName(), GosuClassTypeLoader.BLOCK_PREFIX + i + GosuClassTypeLoader.BLOCK_POSTFIX, enclosingClass.getTypeLoader(), enclosingClass.getSourceFileHandle(), enclosingClass.getTypeUsesMap() ); createNewParseInfo(); getParseInfo().setBlock(blk); initType( enclosingClass ); initCompilationState(); } @Override public Map<String, ICapturedSymbol> getCapturedSymbols() { return getParseInfo().getBlock().getCapturedSymbols(); } private void initType( ICompilableType enclosingClass ) { setEnclosingType( enclosingClass ); BlockExpression block = getBlock(); if( block.getArgs().size() < IBlock.MAX_ARGS ) { IType functionClassForArity = FunctionClassUtil.getFunctionClassForArity(block.getArgs().size()); setSuperType(functionClassForArity); } else { // This is a bad block that will have errors, so just set up a super type with zero args setSuperType(FunctionClassUtil.getFunctionClassForArity(0)); } } public void update() { createNewParseInfo(); getParseInfo().addDefaultConstructor(new StandardSymbolTable()); // implement the one method that blocks implement implementInvoke(); implementToString(); VarStatement varstmt = new VarStatement(); varstmt.setModifierInfo( new ModifierInfo(0) ); varstmt.setPublic( true ); varstmt.setSymbol( new DynamicSymbol( this, new StandardSymbolTable(), "_returnType", JavaTypes.ITYPE(), null ) ); varstmt.setScope( GlobalScope.EXECUTION ); getParseInfo().addMemberField( varstmt ); } private void implementInvoke() { BlockExpression block = getBlock(); IParsedElement body = block.getBody(); DynamicFunctionSymbol value; if( body instanceof Expression ) { Expression expression = (Expression)body; ReturnStatement syntheticReturnStatement = new ReturnStatement(); syntheticReturnStatement.setValue( expression ); syntheticReturnStatement.initLocation(expression.getLocation().getOffset(), expression.getLocation().getLength(), expression.getLineNum(), expression.getColumn(), expression.getLocation().getScriptPartId()); value = new DynamicFunctionSymbol( null, INVOKE_METHOD_NAME, convertToObjectSignature(block), convertToObjectSymbols(block), syntheticReturnStatement ); } else { value = new DynamicFunctionSymbol( null, INVOKE_METHOD_NAME, convertToObjectSignature(block), convertToObjectSymbols(block), (IStatement)body ); } value.setClassMember( true ); value.setPublic( true ); value.setFinal( true ); getParseInfo().addMemberFunction(value); } private void implementToString() { Identifier thisId = new Identifier(); thisId.setSymbol( new Symbol( Keyword.KW_this.getName(), this, null ), new StandardSymbolTable() ); thisId.setType( this ); BeanMethodCallExpression toStrCall = new BeanMethodCallExpression(); toStrCall.setMethodDescriptor( JavaTypes.IBLOCK().getTypeInfo().getMethod( "toString" ) ); toStrCall.setRootExpression( thisId ); toStrCall.setType( JavaTypes.STRING() ); ReturnStatement returnStmt = new ReturnStatement(); returnStmt.setValue( toStrCall ); } private IFunctionType convertToObjectSignature( BlockExpression blk ) { IFunctionType functionType = blk.getType(); IType[] iTypes = new IType[functionType.getParameterTypes().length]; for( int i = 0; i < iTypes.length; i++ ) { iTypes[i] = JavaTypes.OBJECT(); } return new FunctionType( blk.getFunctionName(), JavaTypes.OBJECT(), iTypes ); } @Override public void addCapturedSymbol( ICapturedSymbol sym ) { getBlock().addCapturedSymbol(sym); } @Override public IType getEnclosingNonBlockType() { ICompilableTypeInternal type = getEnclosingType(); while( type instanceof IBlockClassInternal ) { type = type.getEnclosingType(); } return type.getEnclosingNonBlockType(); } private List<ISymbol> convertToObjectSymbols( BlockExpression blk ) { List<ISymbol> syms = new ArrayList<ISymbol>(); for( ISymbol iSymbol : blk.getArgs() ) { Symbol symbol = new Symbol( (Symbol)iSymbol ); symbol.setType( JavaTypes.OBJECT() ); syms.add( symbol ); } return syms; } public BlockExpression getBlock() { return getParseInfo().getBlock(); } @Override public IType getBlockType() { return getBlock().getType(); } public static IBlockClassInternal create( ICompilableTypeInternal enclosingClass, BlockExpression block, boolean staticBlock ) { BlockClass blockClass; if( enclosingClass != null ) { blockClass = new BlockClass( enclosingClass, enclosingClass.getBlockCount(), block ); } else { blockClass = new BlockClass( block ); } if( staticBlock ) { blockClass.markStatic(); } return (IBlockClassInternal)blockClass.getOrCreateTypeReference(); } @Override public boolean isAnonymous() { return true; } }