/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.ir.compiler.bytecode.statement; import gw.internal.ext.org.objectweb.asm.Label; import gw.internal.ext.org.objectweb.asm.MethodVisitor; import gw.internal.ext.org.objectweb.asm.Opcodes; import gw.internal.gosu.ir.compiler.bytecode.AbstractBytecodeCompiler; import gw.internal.gosu.ir.compiler.bytecode.IRBytecodeContext; import gw.lang.ir.IRStatement; import gw.lang.ir.IRTypeConstants; import gw.lang.ir.statement.IRCaseClause; import gw.lang.ir.statement.IRSwitchStatement; import java.util.ArrayList; import java.util.List; public class IRSwitchStatementCompiler extends AbstractBytecodeCompiler { public static void compile( IRSwitchStatement statement, IRBytecodeContext context ) { // Note, we don't use the lookupswitch or tableswitch instructions here. // Given the differences in Gosu's switch vs. Java's and the very // small gain in perf from the switch bytecode instructions (when they // can be applied), it doesn't seem worth the trouble... for now. MethodVisitor mv = context.getMv(); context.compile( statement.getInit() ); Label endSwitchLabel = new Label(); int iCaseMatchedIndex = context.makeTempVar(IRTypeConstants.pBOOLEAN()).getIndex(); mv.visitInsn( Opcodes.ICONST_0 ); mv.visitVarInsn( Opcodes.ISTORE, iCaseMatchedIndex ); // Generate the branch instructions corresponding with cases List<Label> caseBodyLabels = new ArrayList<Label>(); List<IRCaseClause> cases = statement.getCases(); for( IRCaseClause caseClause : cases ) { Label caseBodyLabel = new Label(); caseBodyLabels.add( caseBodyLabel ); // Check to see if the case matches context.compile( caseClause.getCondition() ); // If matches, Jump to the code corresponding with the case mv.visitJumpInsn( Opcodes.IFNE, caseBodyLabel ); } // Jump directly to the default label if no cases matches Label defaultLabel = new Label(); mv.visitJumpInsn( Opcodes.GOTO, defaultLabel ); // Generate and label the case bodies for( int i = 0; i < cases.size(); i++ ) { IRCaseClause caseClause = cases.get( i ); Label caseBodyLabel = caseBodyLabels.get( i ); context.visitLabel( caseBodyLabel ); compileCaseBody( endSwitchLabel, caseClause.getStatements(), context ); } // Generate and label the default body context.visitLabel( defaultLabel ); compileCaseBody( endSwitchLabel, statement.getDefaultStatements(), context ); context.visitLabel( endSwitchLabel ); } private static void compileCaseBody( Label endSwitchLabel, List<IRStatement> statements, IRBytecodeContext context ) { // Push a scope so that locals don't bleed through to the next case context.pushBreakLabel( endSwitchLabel ); context.pushScope(); try { for( IRStatement caseStatement : statements ) { context.compile( caseStatement ); } } finally { context.popScope(); context.popBreakLabel(); } } }