/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.ir.transform.statement;
import gw.internal.gosu.ir.nodes.IRMethodFactory;
import gw.internal.gosu.ir.transform.ExpressionTransformer;
import gw.internal.gosu.ir.transform.TopLevelTransformationContext;
import gw.internal.gosu.parser.statements.UsingStatement;
import gw.internal.gosu.runtime.GosuRuntimeMethods;
import gw.lang.IReentrant;
import gw.lang.ir.IRExpression;
import gw.lang.ir.IRStatement;
import gw.lang.ir.IRSymbol;
import gw.lang.ir.expression.IRIdentifier;
import gw.lang.ir.statement.IRAssignmentStatement;
import gw.lang.ir.statement.IRMonitorLockAcquireStatement;
import gw.lang.ir.statement.IRMonitorLockReleaseStatement;
import gw.lang.ir.statement.IRStatementList;
import gw.lang.ir.statement.IRTryCatchFinallyStatement;
import gw.lang.parser.IExpression;
import gw.lang.parser.IParsedElement;
import gw.lang.parser.expressions.IVarStatement;
import gw.lang.reflect.IRelativeTypeInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.java.GosuTypes;
import gw.lang.reflect.java.JavaTypes;
import java.io.Closeable;
import java.util.Collections;
import java.util.Iterator;
import java.util.concurrent.locks.Lock;
/**
*/
public class UsingStatementTransformer extends AbstractStatementTransformer<UsingStatement>
{
public static IRStatement compile( TopLevelTransformationContext cc, UsingStatement stmt )
{
UsingStatementTransformer compiler = new UsingStatementTransformer( cc, stmt );
return compiler.compile();
}
private UsingStatementTransformer( TopLevelTransformationContext cc, UsingStatement stmt )
{
super( cc, stmt );
}
@Override
protected IRStatement compile_impl()
{
// Push a scope in case using body not a statement list
_cc().pushScope( false );
try
{
return compileUsingVars( getUsingVars() );
}
finally
{
_cc().popScope();
}
}
private Iterator<? extends IParsedElement> getUsingVars()
{
if( _stmt().hasVarStatements() )
{
return _stmt().getVarStatements().iterator();
}
else
{
return Collections.singleton( _stmt().getExpression() ).iterator();
}
}
private IRStatement compileUsingVars( Iterator<? extends IParsedElement> usingVars )
{
if( !usingVars.hasNext() )
{
// if there are no vars left, return the body of the using stmt.
IRStatement body = _cc().compile( _stmt().getStatement() );
visitStatementLineNumber( body, _stmt().getStatement() );
return body;
}
else
{
IParsedElement pe = usingVars.next();
IType type = getType( pe );
IRStatementList stmtList = new IRStatementList( false );
IRSymbol symbol = initVar( pe, stmtList );
stmtList.addStatement( aquire( symbol, type ) );
stmtList.addStatement( new IRTryCatchFinallyStatement( compileUsingVars( usingVars ),
Collections.EMPTY_LIST,
release( symbol, type ) ) );
return stmtList;
}
}
private IRSymbol initVar( IParsedElement pe, IRStatementList stmtList )
{
if( pe instanceof IExpression )
{
// evaluate the expression and store it to a temp symbol
IRExpression expression = ExpressionTransformer.compile( (IExpression)pe, _cc() );
IRSymbol tempSymbol = _cc().makeAndIndexTempSymbol( expression.getType() );
IRAssignmentStatement initAssignment = buildAssignment( tempSymbol, expression );
stmtList.addStatement( initAssignment );
return tempSymbol;
}
else
{
IVarStatement varStmt = (IVarStatement)pe;
// initialize the var
IRExpression expression = ExpressionTransformer.compile( varStmt.getAsExpression(), _cc() );
if( varStmt.getSymbol().isValueBoxed() )
{
// Box the value
expression = buildInitializedArray( getDescriptor( varStmt.getSymbol().getType() ), Collections.singletonList( expression ) );
}
IRSymbol varSymbol = _cc().createSymbol( varStmt.getSymbol().getName(),
varStmt.getSymbol().isValueBoxed()
? getDescriptor( varStmt.getSymbol().getType() ).getArrayType()
: getDescriptor( varStmt.getSymbol().getType() ) );
IRAssignmentStatement initAssignment = buildAssignment( varSymbol, expression ); // assign the initial value
stmtList.addStatement( initAssignment );
visitStatementLineNumber( initAssignment, varStmt );
// copy it to a temp symbol (so that reassignment doesn't affect the enter/exit semantics)
IRSymbol tempSymbol = _cc().makeAndIndexTempSymbol( expression.getType() );
IRAssignmentStatement tempVar = buildAssignment( tempSymbol, identifier( initAssignment.getSymbol() ) );// reassign to a temp var
stmtList.addStatement( tempVar );
return tempSymbol;
}
}
private IType getType( IParsedElement pe )
{
if( pe instanceof IExpression )
{
return ((IExpression) pe).getType();
}
else
{
return ((IVarStatement)pe).getType();
}
}
private IRStatement aquire( IRSymbol symbol, IType type )
{
IRAssignmentStatement tempVar = null;
if( symbol.getType().isArray() )
{
IRExpression boxedValue = buildArrayLoad( new IRIdentifier( symbol ), 0, getDescriptor( type ) );
symbol = _cc().makeAndIndexTempSymbol( getDescriptor( type ) );
tempVar = buildAssignment( symbol, boxedValue );
}
IRStatement acquireStmt;
if( type.equals(GosuTypes.IMONITORLOCK()) )
{
acquireStmt = new IRMonitorLockAcquireStatement( identifier( symbol ) );
}
else if( JavaTypes.LOCK().isAssignableFrom( type ) )
{
acquireStmt = buildIf( buildNotEquals( identifier( symbol ), nullLiteral() ),
buildMethodCall( callMethod( Lock.class, "lock", new Class[0], identifier( symbol ), exprList() ) ) );
}
else if( JavaTypes.getGosuType( IReentrant.class ).isAssignableFrom( type ) )
{
acquireStmt = buildIf( buildNotEquals( identifier( symbol ), nullLiteral() ),
buildMethodCall( callMethod( IReentrant.class, "enter", new Class[0], identifier( symbol ), exprList() ) ) );
}
else
{
acquireStmt = buildMethodCall( callStaticMethod( GosuRuntimeMethods.class, "invokeLockMethod", new Class[]{Object.class},
exprList( identifier( symbol ) ) ) );
}
if( tempVar != null )
{
acquireStmt = new IRStatementList( false, tempVar, acquireStmt );
}
return acquireStmt;
}
private IRStatement release( IRSymbol symbol, IType type )
{
IRAssignmentStatement tempVar = null;
if( symbol.getType().isArray() )
{
IRExpression boxedValue = buildArrayLoad( new IRIdentifier( symbol ), 0, getDescriptor( type ) );
symbol = _cc().makeAndIndexTempSymbol( getDescriptor( type ) );
tempVar = buildAssignment( symbol, boxedValue );
}
IRStatement releaseStmt;
if( type.equals(GosuTypes.IMONITORLOCK()) )
{
releaseStmt = new IRMonitorLockReleaseStatement( identifier( symbol ) );
}
else if( JavaTypes.LOCK().isAssignableFrom( type ) )
{
releaseStmt = buildIf( buildNotEquals( identifier( symbol ), nullLiteral() ),
buildMethodCall( callMethod( Lock.class, "unlock", new Class[0], identifier( symbol ), exprList() ) ) );
}
else if( JavaTypes.getGosuType( IReentrant.class ).isAssignableFrom( type ) )
{
releaseStmt = buildIf( buildNotEquals( identifier( symbol ), nullLiteral() ),
buildMethodCall( callMethod( IReentrant.class, "exit", new Class[0], identifier( symbol ), exprList() ) ) );
}
else if( JavaTypes.getJreType( Closeable.class ).isAssignableFrom( type ) )
{
releaseStmt = buildIf( buildNotEquals( identifier( symbol ), nullLiteral() ),
buildMethodCall( callMethod( Closeable.class, "close", new Class[0], identifier( symbol ), exprList() ) ) );
}
else {
if( GosuTypes.IDISPOSABLE().isAssignableFrom(type) )
{
releaseStmt = buildIf( buildNotEquals( identifier( symbol ), nullLiteral() ),
buildMethodCall( callMethod( IRMethodFactory.createIRMethod(GosuTypes.IDISPOSABLE(),
"dispose",
JavaTypes.pVOID(),
IType.EMPTY_ARRAY,
IRelativeTypeInfo.Accessibility.PUBLIC,
false),
identifier( symbol ),
exprList() ) ) );
}
else
{
releaseStmt = buildMethodCall( callStaticMethod( GosuRuntimeMethods.class, "invokeUnlockOrDisposeOrCloseMethod", new Class[]{Object.class},
exprList( identifier( symbol ) ) ) );
}
}
if( tempVar != null )
{
releaseStmt = new IRStatementList( false, tempVar, releaseStmt );
}
if(_stmt().getFinallyStatement() != null )
{
IRStatement finallyBody = _cc().compile( _stmt().getFinallyStatement() );
releaseStmt = new IRStatementList( false, releaseStmt, finallyBody );
}
return releaseStmt;
}
}