/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.ir.compiler.bytecode; import gw.internal.ext.org.objectweb.asm.Label; import gw.internal.ext.org.objectweb.asm.MethodVisitor; import gw.internal.gosu.compiler.NamedLabel; import gw.lang.ir.IRElement; import gw.lang.ir.IRSymbol; import gw.lang.ir.IRType; import gw.lang.ir.statement.IRTerminalStatement; import gw.lang.ir.statement.IRTryCatchFinallyStatement; import gw.util.GosuExceptionUtil; import gw.util.Stack; import java.util.ArrayList; import java.util.List; public class IRBytecodeContext { private MethodVisitor _mv; private Stack<IRCompilerScope> _scopes; private List<IRCompilerLocalVar> _allLocalVars; private Stack<IRFinallyCodePartitioner> _finallyStatements; private int _tempVarCount; private Stack<Label> _breakLabels; private Stack<Label> _continueLabels; private Label _lastVisitedLabel; private int _lastLineNumber; public IRBytecodeContext(MethodVisitor mv) { _mv = mv; _scopes = new Stack<IRCompilerScope>(); pushScope(); _allLocalVars = new ArrayList<IRCompilerLocalVar>(); _finallyStatements = new Stack<IRFinallyCodePartitioner>(); _breakLabels = new Stack<Label>(); _continueLabels = new Stack<Label>(); _lastLineNumber = -1; } public MethodVisitor getMv() { return _mv; } public void visitLabel(Label label) { _lastVisitedLabel = label; _mv.visitLabel( label ); for( IRCompilerLocalVar lv : _allLocalVars ) { if( lv.getStartLabel() == null ) { lv.setStartLabel( label ); } if( isOutOfScope( lv ) && lv.getEndLabel() == null ) { lv.setEndLabel( label ); } } } public int getLocalCount() { return _allLocalVars.size(); } public int getMaxScopeSize() { int iMax = 0; for( IRCompilerLocalVar local : _allLocalVars ) { iMax = Math.max( local.getScope().getLocalVars().size(), iMax ); } return iMax; } private boolean isOutOfScope( IRCompilerLocalVar lv ) { return !lv.getScope().isActive(); } public void visitLocalVars() { for( IRCompilerLocalVar lv : _allLocalVars ) { if( !lv.isTemp() ) { try { if (!lv.getStartLabel().equals(lv.getEndLabel())) { _mv.visitLocalVariable( lv.getName(), lv.getType().getDescriptor(), null, lv.getStartLabel(), lv.getEndLabel(), lv.getIndex()); } } catch( Exception e ) { throw GosuExceptionUtil.forceThrow( e, lv.getName() ); } } } } public void pushScope() { _scopes.push(new IRCompilerScope(_scopes.isEmpty() ? null : _scopes.peek())); } public void popScope() { IRCompilerScope oldScope = _scopes.pop(); oldScope.scopeRemoved(); } public void indexThis(IRType type) { Label label = new Label(); visitLabel( label ); IRCompilerLocalVar thisVar = getLocalVar( new IRSymbol( "this", type, false ) ); thisVar.setStartLabel( label ); } public void indexSymbols(List<IRSymbol> symbols) { for (IRSymbol symbol : symbols) { getLocalVar( symbol ); } } public IRCompilerLocalVar getLocalVar(IRSymbol symbol) { IRCompilerLocalVar localVar = _scopes.peek().findLocalVar( symbol ); if (localVar == null) { localVar = _scopes.peek().createLocalVar(symbol); //## note: We don't assign the start label here because local vars are not in scope until after their declaration //## The start label is assigned during in visitLabel() above. // // if (_lastVisitedLabel != null) { // localVar.setStartLabel(_lastVisitedLabel); // } _allLocalVars.add( localVar ); } return localVar; } public IRCompilerLocalVar makeTempVar(IRType type) { return getLocalVar( new IRSymbol( "$$compilertemp$$" + (_tempVarCount++), type, true) ); } public IRFinallyCodePartitioner pushFinallyStatement( IRTryCatchFinallyStatement tryCatchFinallyStmt ) { IRFinallyCodePartitioner partition = new IRFinallyCodePartitioner( this, tryCatchFinallyStmt ); _finallyStatements.push( partition ); return partition; } public void popFinallyStatement( IRFinallyCodePartitioner partition ) { IRFinallyCodePartitioner popped = _finallyStatements.pop(); if( popped != partition ) { throw new IllegalStateException( "Finally statements out of order. " + "Expected '" + partition + "', but got '" + popped ); } } public boolean hasFinallyStatements() { return !_finallyStatements.isEmpty(); } public Stack<IRFinallyCodePartitioner> getFinallyParitioners() { return _finallyStatements; } public IRFinallyCodePartitioner peekFinallyPartitioner() { return _finallyStatements.peek(); } public void inlineFinallyStatements( IRTerminalStatement stmt ) { if( !hasFinallyStatements() ) { return; } Stack<IRFinallyCodePartitioner> partitions = getFinallyParitioners(); List<IRFinallyCodePartitioner> inlinedFinallys = new ArrayList<IRFinallyCodePartitioner>(); for( int i = partitions.size() - 1; i >= 0; i-- ) { IRFinallyCodePartitioner partition = partitions.get( i ); if( !partition.appliesTo( stmt ) ) { // once a try block does not apply to a point, no enclosing try blocks do break; } partition.inlineFinally(); inlinedFinallys.add( partition ); } // stop the finally coverage for all nested finally's at the very end of the inlined finally statements NamedLabel endLabel = new NamedLabel( "EndFinally" ); visitLabel( endLabel ); for( IRFinallyCodePartitioner inlinedFinally : inlinedFinallys ) { inlinedFinally.endInlineFinally( endLabel ); } } public void compile( IRElement element ) { IRBytecodeCompiler.compileIRElement( element, this ); } public void pushBreakLabel( Label label ) { _breakLabels.push( label ); } public void popBreakLabel() { _breakLabels.pop(); } public void pushContinueLabel( Label label ) { _continueLabels.push( label ); } public void popContinueLabel() { _continueLabels.pop(); } public Label getCurrentBreakLabel() { return _breakLabels.peek(); } public Label getCurrentContinueLabel() { return _continueLabels.peek(); } public int setLineNumber( int lineNumber ) { int lastLineNumber = _lastLineNumber; if( lineNumber > 0 && lineNumber != lastLineNumber ) { MethodVisitor mv = getMv(); Label label = new Label(); visitLabel( label ); mv.visitLineNumber( lineNumber, label ); _lastLineNumber = lineNumber; } return lastLineNumber; } }