/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.ir.transform.statement; import gw.config.CommonServices; import gw.internal.gosu.ir.compiler.bytecode.expression.IRMethodCallExpressionCompiler; import gw.internal.gosu.ir.nodes.JavaClassIRType; import gw.internal.gosu.ir.transform.ExpressionTransformer; import gw.internal.gosu.ir.transform.TopLevelTransformationContext; import gw.internal.gosu.parser.Symbol; import gw.internal.gosu.parser.statements.ForEachStatement; import gw.lang.ir.IRExpression; import gw.lang.ir.IRStatement; import gw.lang.ir.IRSymbol; import gw.lang.ir.IRTypeConstants; import gw.lang.ir.statement.IRAssignmentStatement; import gw.lang.ir.statement.IRForEachStatement; import gw.lang.reflect.IType; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.interval.AbstractIntIterator; import gw.lang.reflect.interval.AbstractLongIterator; import gw.lang.reflect.interval.IntegerInterval; import gw.lang.reflect.interval.LongInterval; import gw.lang.reflect.java.JavaTypes; import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; /** */ public class ForEachStatementTransformer extends AbstractStatementTransformer<ForEachStatement> { public static IRStatement compile( TopLevelTransformationContext cc, ForEachStatement stmt ) { ForEachStatementTransformer compiler = new ForEachStatementTransformer( cc, stmt ); return compiler.compile(); } private ForEachStatementTransformer( TopLevelTransformationContext cc, ForEachStatement stmt ) { super( cc, stmt ); } @Override protected IRStatement compile_impl() { // Push a scope in case foreach body not a statement list _cc().pushScope( false ); try { IRForEachStatement forLoop = makeLoopImpl( _cc(), ExpressionTransformer.compile( _stmt().getInExpression(), _cc() ), _stmt().getInExpression().getType(), _stmt().getIdentifier(), _stmt().getIndexIdentifier(), _stmt().getIteratorIdentifier() ); forLoop.setBody( _cc().compile( _stmt().getStatement() ) ); return forLoop; } finally { _cc().popScope(); } } /** * Helper for creating iterative loops. Note that after calling this method, you should compile and call * gw.internal.gosu.ir.nodes.statement.IRForEachStatement#setBody(gw.internal.gosu.ir.nodes.IRStatement) on the * IRForEachStatement. Since the body often depends on symbols introduced in the loop, you must usually compile it * after the loop has been created. Thus it cannot be an argument to this function. */ public static IRForEachStatement makeLoop( TopLevelTransformationContext cc, IRExpression rootExpression, IType type, Symbol identifier, Symbol indexSymbol ) { return new ForEachStatementTransformer( cc, null ) .makeLoopImpl( cc, rootExpression, type, identifier, indexSymbol, null ); } private IRForEachStatement makeLoopImpl( TopLevelTransformationContext cc, IRExpression rootExpression, IType type, Symbol identifier, Symbol indexSymbol, Symbol iteratorIdentifier ) { IRForEachStatement forLoop = new IRForEachStatement(); if( isArrayIteration( type ) ) { makeArrayLoop( cc, rootExpression, forLoop, identifier ); } else if( identifier.getType() == JavaTypes.pINT() && !JavaTypes.NUMBER_INTERVAL().isAssignableFrom( type ) ) { makeIntLoop( cc, rootExpression, forLoop, identifier ); } else { makeIteratorLoop( cc, rootExpression, forLoop, identifier, iteratorIdentifier ); } if( indexSymbol != null ) { // index variable init IRAssignmentStatement indexAssignment = initLocalVar( indexSymbol, numericLiteral( -1 ) ); forLoop.addInitializer( indexAssignment ); // increment index IRSymbol indexIRSymbol = indexAssignment.getSymbol(); if( indexSymbol.isValueBoxed() ) { IRExpression increment = buildAddition( buildArrayLoad( identifier( indexIRSymbol ), 0, getDescriptor( indexSymbol.getType() ) ), numericLiteral( 1 ) ); forLoop.addIncrementor( buildArrayStore( identifier( indexIRSymbol ), numericLiteral( 0 ), increment, IRTypeConstants.pINT()) ); } else { IRExpression increment = buildAddition( identifier( indexIRSymbol ), numericLiteral( 1 ) ); forLoop.addIncrementor( buildAssignment( indexIRSymbol, increment ) ); } } return forLoop; } private void makeIteratorLoop( TopLevelTransformationContext cc, IRExpression rootExpression, IRForEachStatement forLoop, Symbol identifier, Symbol iteratorIdentifier ) { // iterator temporary variable init IRExpression iteratorInit = callStaticMethod( ForEachStatementTransformer.class, "makeIterator", new Class[]{Object.class, boolean.class}, exprList( rootExpression, pushConstant( _stmt() != null && _stmt().isStructuralIterable() ) ) ); if( rootExpression.getType() == JavaClassIRType.get( IntegerInterval.class ) ) { iteratorInit = checkCast( AbstractIntIterator.class, iteratorInit ); } else if( rootExpression.getType() == JavaClassIRType.get( LongInterval.class ) ) { iteratorInit = checkCast( AbstractLongIterator.class, iteratorInit ); } IRSymbol irSymbol; if( iteratorIdentifier != null ) { irSymbol = makeIRSymbol( iteratorIdentifier ); } else { irSymbol = cc.makeAndIndexTempSymbol( iteratorInit.getType() ); } IRAssignmentStatement iterator = buildAssignment( irSymbol, iteratorInit ); forLoop.addInitializer( iterator ); // null check the iterator forLoop.setIdentifierToNullCheck( identifier( iterator.getSymbol() ) ); // loop variable init IRAssignmentStatement loopInitializer = initLocalVarWithDefault( identifier ); forLoop.addInitializer( loopInitializer ); IRSymbol loopIdentifier = loopInitializer.getSymbol(); // loop test forLoop.setLoopTest( callMethod( Iterator.class, "hasNext", new Class[0], identifier( iterator.getSymbol() ), Collections.<IRExpression>emptyList() ) ); // increment iterator IRExpression nextValue; if( rootExpression.getType() == JavaClassIRType.get( IntegerInterval.class ) ) { // Optimize for integer intervals (no boxing) nextValue = callMethod( AbstractIntIterator.class, "nextInt", new Class[0], identifier( iterator.getSymbol() ), Collections.<IRExpression>emptyList() ); } else if( rootExpression.getType() == JavaClassIRType.get( LongInterval.class ) ) { // Optimize for long intervals (no boxing) nextValue = callMethod( AbstractLongIterator.class, "nextLong", new Class[0], identifier( iterator.getSymbol() ), Collections.<IRExpression>emptyList() ); } else { IRExpression nextMethodCall = callMethod( Iterator.class, "next", new Class[0], identifier( iterator.getSymbol() ), Collections.<IRExpression>emptyList() ); // checkcast to actual type nextValue = checkCast( identifier.getType(), nextMethodCall ); } if( identifier.isValueBoxed() ) { forLoop.addIncrementor( buildAssignment( loopIdentifier, buildInitializedArray( getDescriptor( identifier.getType() ), Collections.singletonList( nextValue ) ) ) ); } else { forLoop.addIncrementor( buildAssignment( loopIdentifier, nextValue ) ); } } private void makeArrayLoop( TopLevelTransformationContext cc, IRExpression rootExpression, IRForEachStatement forLoop, Symbol identifier ) { // array temporary variable init IRAssignmentStatement array = buildAssignment( cc.makeAndIndexTempSymbol( rootExpression.getType() ), rootExpression ); forLoop.addInitializer( array ); // null check the array forLoop.setIdentifierToNullCheck( identifier( array.getSymbol() ) ); // array length init IRAssignmentStatement arrayLen = buildAssignment( cc.makeAndIndexTempSymbol(IRTypeConstants.pINT()), buildAddition( numericLiteral( -1 ), buildNullCheckTernary( identifier( array.getSymbol() ), numericLiteral( -1 ), buildArrayLength( identifier( array.getSymbol() ) ) ) ) ); forLoop.addInitializer( arrayLen ); // array position init IRAssignmentStatement arrayPos = buildAssignment( cc.makeAndIndexTempSymbol(IRTypeConstants.pINT()), numericLiteral( -1 ) ); forLoop.addInitializer( arrayPos ); // loop variable init IRAssignmentStatement loopInitializer = initLocalVarWithDefault( identifier ); forLoop.addInitializer( loopInitializer ); IRSymbol loopIdentifier = loopInitializer.getSymbol(); // loop test forLoop.setLoopTest( buildNotEquals( identifier( arrayPos.getSymbol() ), identifier( arrayLen.getSymbol() ) ) ); // increment array position forLoop.addIncrementor( buildAssignment( arrayPos.getSymbol(), buildAddition( identifier( arrayPos.getSymbol() ), numericLiteral( 1 ) ) ) ); // update loop variable IRExpression nextValue = buildArrayLoad( identifier( array.getSymbol() ), identifier( arrayPos.getSymbol() ), getDescriptor( identifier.getType() ) ); if( identifier.isValueBoxed() ) { forLoop.addIncrementor( buildAssignment( loopIdentifier, buildInitializedArray( getDescriptor( identifier.getType() ), Collections.singletonList( nextValue ) ) ) ); } else { forLoop.addIncrementor( buildAssignment( loopIdentifier, nextValue ) ); } } private void makeIntLoop( TopLevelTransformationContext cc, IRExpression rootExpression, IRForEachStatement forLoop, Symbol identifier ) { // int temporary variable IRAssignmentStatement intToCountTo = buildAssignment( cc.makeAndIndexTempSymbol(IRTypeConstants.pINT()), buildAddition(makeInt(rootExpression), numericLiteral( -1 ) ) ); forLoop.addInitializer( intToCountTo ); // loop variable init IRAssignmentStatement loopInitializer = initLocalVar( identifier, numericLiteral( -1 ) ); forLoop.addInitializer( loopInitializer ); IRSymbol loopIdentifier = loopInitializer.getSymbol(); if( identifier.isValueBoxed() ) { // loop test forLoop.setLoopTest( buildGreaterThan( identifier( intToCountTo.getSymbol() ), buildArrayLoad( identifier( loopIdentifier ), 0, loopIdentifier.getType().getComponentType() ) ) ); // increment loop variable IRExpression incrementedValue = buildAddition( buildArrayLoad( identifier( loopIdentifier ), 0, loopIdentifier.getType().getComponentType() ), numericLiteral( 1 ) ); forLoop.addIncrementor( buildAssignment( loopIdentifier, buildInitializedArray( getDescriptor( identifier.getType() ), Collections.singletonList( incrementedValue ) ) ) ); } else { // loop test forLoop.setLoopTest( buildGreaterThan( identifier( intToCountTo.getSymbol() ), identifier( loopIdentifier ) ) ); // increment loop variable IRExpression incrementedValue = buildAddition( identifier( loopIdentifier ), numericLiteral( 1 ) ); forLoop.addIncrementor( buildAssignment( loopIdentifier, incrementedValue ) ); } } private IRExpression makeInt(IRExpression rootExpression) { if (rootExpression.getType().isInt()) { return rootExpression; } else if (IRTypeConstants.NUMBER().isAssignableFrom(rootExpression.getType())) { return buildMethodCall(Number.class, "intValue", int.class, new Class[0], rootExpression, Collections.<IRExpression>emptyList()); } else if (rootExpression.getType().isPrimitive()) { return numberConvert(rootExpression.getType(), IRTypeConstants.pINT(), rootExpression); } else { throw new IllegalArgumentException("Cannot create an int from value of type " + rootExpression.getType()); } } private static boolean isArrayIteration( IType iterationType ) { return isBytecodeType( iterationType ) && iterationType.isArray(); } @SuppressWarnings({"UnusedDeclaration"}) public static Iterator makeIterator( Object obj, boolean bStructuralIterable ) { if( obj == null ) { return null; } if( obj instanceof Iterable ) { return ((Iterable)obj).iterator(); } if( bStructuralIterable ) { return ((Iterable)IRMethodCallExpressionCompiler.constructProxy( obj, Iterable.class.getName() )).iterator(); } if( obj instanceof Iterator ) { return(Iterator)obj; } // Treat a string as a list of characters if( obj instanceof String ) { return new StringIterator( (String)obj ); } if( obj instanceof Number ) { return new NumberIterator( (Number)obj ); } if( TypeSystem.getFromObject( obj ).isArray() ) { return new ArrayIterator( obj, TypeSystem.getFromObject( obj ) ); } // Oh well. Convert to a List of length one and iterate that single element. return Collections.nCopies( 1, obj ).iterator(); } static final class ArrayIterator implements Iterator { private int _iCsr; private Object _array; private IType _arrayType; ArrayIterator( Object array, IType arrayType ) { _iCsr = 0; _arrayType = arrayType; _array = CommonServices.getCoercionManager().convertValue(array, _arrayType); } public boolean hasNext() { return _iCsr < _arrayType.getArrayLength( _array ); } public Object next() { if( !hasNext() ) { throw new NoSuchElementException( "No element at index [" + _iCsr + "] for the array." ); } return _arrayType.getArrayComponent( _array, _iCsr++ ); } public void remove() { throw new UnsupportedOperationException( "Sorry, ArrayIterator does not support remove()." ); } } static final class StringIterator implements Iterator { int iCsr = 0; private final String _strObj; public StringIterator( String strObj ) { _strObj = strObj; } public boolean hasNext() { return iCsr < _strObj.length(); } public Object next() { if( !hasNext() ) { throw new NoSuchElementException( "No element at index [" + iCsr + "] for character iterator" ); } return String.valueOf( _strObj.charAt( iCsr++ ) ); } public void remove() { throw new UnsupportedOperationException( "Sorry, String character iterator does not support remove()." ); } } static final class NumberIterator implements Iterator { private int _iIndex; private final int _iNum; public NumberIterator( Number numObj ) { _iNum = numObj.intValue(); } public void remove() { throw new UnsupportedOperationException( "Sorry, the integer iterator does not support remove()." ); } public boolean hasNext() { return _iIndex < _iNum; } public Object next() { return _iIndex++; } } }