/* * Copyright 2009-2010 MBTE Sweden AB. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.mbte.groovypp.compiler; import groovy.lang.EmptyRange; import groovy.lang.TypePolicy; import org.codehaus.groovy.ast.*; import static org.codehaus.groovy.ast.ClassHelper.*; import org.codehaus.groovy.ast.expr.*; import org.codehaus.groovy.ast.stmt.*; import org.codehaus.groovy.classgen.BytecodeHelper; import org.codehaus.groovy.classgen.BytecodeInstruction; import org.codehaus.groovy.classgen.BytecodeSequence; import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; import org.codehaus.groovy.syntax.Types; import org.codehaus.groovy.syntax.Token; import org.mbte.groovypp.compiler.bytecode.*; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class StaticCompiler extends CompilerTransformer implements Opcodes { private StaticMethodBytecode methodBytecode; // exception blocks list private List<Runnable> exceptionBlocks = new ArrayList<Runnable>(); final boolean shouldImproveReturnType; ClassNode calculatedReturnType = TypeUtil.NULL_TYPE; private Label startLabel = new Label (); public StaticCompiler(SourceUnit su, SourceUnitContext context, StaticMethodBytecode methodBytecode, StackAwareMethodAdapter mv, CompilerStack compileStack, int debug, TypePolicy policy, String baseClosureName) { super(su, methodBytecode.methodNode.getDeclaringClass(), methodBytecode.methodNode, mv, compileStack, debug, policy, baseClosureName, context); this.methodBytecode = methodBytecode; shouldImproveReturnType = methodNode.getName().equals("doCall"); mv.visitLabel(startLabel); if (methodNode instanceof ConstructorNode && !((ConstructorNode) methodNode).firstStatementIsSpecialConstructorCall()) { // invokes the super class constructor mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(classNode.getSuperClass()), "<init>", "()V"); } } protected Statement getCode() { return methodBytecode.code; } protected void setCode(Statement statement) { methodBytecode.code = statement; } protected SourceUnit getSourceUnit() { return methodBytecode.su; } private int lastLine = -1; @Override protected void visitStatement(Statement statement) { super.visitStatement(statement); int line = statement.getLineNumber(); if (line >= 0 && mv != null && line != lastLine) { Label l = new Label(); mv.visitLabel(l); mv.visitLineNumber(line, l); lastLine = line; } } @Override public void visitAssertStatement(AssertStatement statement) { visitStatement(statement); Label noError = new Label(); BytecodeExpr condition = transformLogical(statement.getBooleanExpression().getExpression(), noError, true); Expression msg = statement.getMessageExpression(); if (msg instanceof ConstantExpression && ((ConstantExpression)msg).getValue() == null) msg = new ConstantExpression("\n" + su.getSample(statement.getLineNumber(), 0, null)); BytecodeExpr msgExpr = (BytecodeExpr) transform(msg); condition.visit(mv); mv.visitTypeInsn(NEW, "java/lang/AssertionError"); mv.visitInsn(DUP); if (msgExpr != null) msgExpr.visit(mv); else mv.visitLdcInsn("<no message>"); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/AssertionError", "<init>", "(Ljava/lang/Object;)V"); mv.visitInsn(ATHROW); mv.visitLabel(noError); } private static final String DTT = BytecodeHelper.getClassInternalName(DefaultTypeTransformation.class.getName()); public static void branch(BytecodeExpr be, int op, Label label, MethodVisitor mv) { // type non-primitive final ClassNode type = be.getType(); if (type == ClassHelper.Boolean_TYPE) { BytecodeExpr.unbox(ClassHelper.boolean_TYPE, mv); } else { if (ClassHelper.isPrimitiveType(type)) { // unwrapper - primitive if (type == ClassHelper.byte_TYPE || type == ClassHelper.short_TYPE || type == ClassHelper.char_TYPE || type == ClassHelper.int_TYPE) { } else if (type == ClassHelper.long_TYPE) { mv.visitInsn(L2I); } else if (type == ClassHelper.float_TYPE) { mv.visitInsn(F2I); } else if (type == ClassHelper.double_TYPE) { mv.visitInsn(D2I); } } else { mv.visitMethodInsn(INVOKESTATIC, DTT, "castToBoolean", "(Ljava/lang/Object;)Z"); } } mv.visitJumpInsn(op, label); } @Override public void visitBlockStatement(BlockStatement block) { compileStack.pushVariableScope(block.getVariableScope()); for (Statement statement : block.getStatements() ) { if (statement instanceof BytecodeSequence) visitBytecodeSequence((BytecodeSequence) statement); else statement.visit(this); } compileStack.pop(); } @Override public void visitBreakStatement(BreakStatement statement) { visitStatement(statement); String name = statement.getLabel(); Label breakLabel = compileStack.getNamedBreakLabel(name); if (breakLabel == null) { addError("Illegal break label '" + name + "'", statement); } compileStack.applyFinallyBlocks(breakLabel, true); mv.visitJumpInsn(GOTO, breakLabel); } @Override public void visitExpressionStatement(ExpressionStatement statement) { visitStatement(statement); super.visitExpressionStatement(statement); final BytecodeExpr be = transformSynthetic((BytecodeExpr) statement.getExpression()); be.visit(mv); final ClassNode type = be.getType(); if (type != ClassHelper.VOID_TYPE && type != ClassHelper.void_WRAPPER_TYPE) { BytecodeExpr.pop(type, mv); } } private void visitForLoopWithIterator(ForStatement forLoop, BytecodeExpr collectionExpression) { compileStack.pushLoop(forLoop.getVariableScope(), forLoop.getStatementLabel()); Label continueLabel = compileStack.getContinueLabel(); Label breakLabel = compileStack.getBreakLabel(); BytecodeExpr fakeObject = new BytecodeExpr(collectionExpression, collectionExpression.getType()) { protected void compile(MethodVisitor mv) {} }; MethodCallExpression iterator = new MethodCallExpression( fakeObject, "iterator", new ArgumentListExpression()); iterator.setSourcePosition(collectionExpression); BytecodeExpr expr = (BytecodeExpr) transform(iterator); expr.visit(mv); ClassNode etype = ClassHelper.OBJECT_TYPE; ClassNode iteratorType = expr.getType(); GenericsType[] generics = iteratorType.getGenericsTypes(); if (generics != null && generics.length == 1) { if (!TypeUtil.isSuper(generics[0])) { etype = generics[0].getType(); } } if (forLoop.getVariable().isDynamicTyped()) forLoop.getVariable().setType(etype); else etype = forLoop.getVariable().getType(); Register variable = compileStack.defineVariable(forLoop.getVariable(), false); final int iteratorIdx = compileStack.defineTemporaryVariable( "iterator", ClassHelper.make(Iterator.class), true); mv.startLoopVisitLabel(continueLabel); mv.visitVarInsn(ALOAD, iteratorIdx); mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z"); mv.visitJumpInsn(IFEQ, breakLabel); mv.visitVarInsn(ALOAD, iteratorIdx); mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;"); if (ClassHelper.isPrimitiveType(etype)) { BytecodeExpr.unbox(etype, mv); } else { BytecodeExpr.cast(ClassHelper.OBJECT_TYPE, etype, mv); } BytecodeExpr.store(etype, variable.getIndex(), mv); forLoop.getLoopBlock().visit(this); mv.visitJumpInsn(GOTO, continueLabel); mv.visitLabel(breakLabel); compileStack.pop(); } private void visitForLoopWithClosures(ForStatement forLoop) { compileStack.pushLoop(forLoop.getVariableScope(), forLoop.getStatementLabel()); ClosureListExpression closureExpression = (ClosureListExpression) forLoop.getCollectionExpression(); compileStack.pushVariableScope(closureExpression.getVariableScope()); Label continueLabel = compileStack.getContinueLabel(); Label breakLabel = compileStack.getBreakLabel(); List<Expression> loopExpr = closureExpression.getExpressions(); if (!(loopExpr.get(0) instanceof EmptyExpression)) { final BytecodeExpr initExpression = (BytecodeExpr) transform(loopExpr.get(0)); initExpression.visit(mv); BytecodeExpr.pop(initExpression.getType(), mv); } Label cond = new Label(); mv.startLoopVisitLabel(cond); if (!(loopExpr.get(1) instanceof EmptyExpression)) { final BytecodeExpr binaryExpression = transformLogical(loopExpr.get(1), breakLabel, false); binaryExpression.visit(mv); } forLoop.getLoopBlock().visit(this); mv.visitLabel(continueLabel); if (!(loopExpr.get(2) instanceof EmptyExpression)) { final BytecodeExpr incrementExpression = (BytecodeExpr) transform(loopExpr.get(2)); incrementExpression.visit(mv); final ClassNode type = incrementExpression.getType(); if (type != ClassHelper.VOID_TYPE && type != ClassHelper.void_WRAPPER_TYPE) { if (type == ClassHelper.long_TYPE || type == ClassHelper.double_TYPE) { mv.visitInsn(POP2); } else { mv.visitInsn(POP); } } } mv.visitJumpInsn(GOTO, cond); mv.visitLabel(breakLabel); compileStack.pop(); compileStack.pop(); } @Override public void visitForLoop(ForStatement forLoop) { visitStatement(forLoop); Parameter loopVar = forLoop.getVariable(); if (loopVar == ForStatement.FOR_LOOP_DUMMY) { visitForLoopWithClosures(forLoop); } else { BytecodeExpr collectionExpression = (BytecodeExpr) transformToGround(forLoop.getCollectionExpression()); collectionExpression.visit(mv); Label nullLabel = new Label(), endLabel = new Label(); mv.visitInsn(DUP); mv.visitJumpInsn(IFNULL, nullLabel); if (collectionExpression.getType().isArray()) { visitForLoopWithArray(forLoop, collectionExpression.getType().getComponentType()); } else if (forLoop.getCollectionExpression() instanceof RangeExpression && TypeUtil.equal(TypeUtil.RANGE_OF_INTEGERS_TYPE, collectionExpression.getType()) && (forLoop.getVariable().isDynamicTyped() || forLoop.getVariable().getType().equals(ClassHelper.int_TYPE))) { // This is the IntRange (or EmptyRange). Iterate with index. visitForLoopWithIntRange(forLoop); } else { visitForLoopWithIterator(forLoop, collectionExpression); } mv.visitJumpInsn(GOTO, endLabel); mv.visitLabel(nullLabel); mv.visitInsn(POP); mv.visitLabel(endLabel); } } private void visitForLoopWithArray(ForStatement forLoop, ClassNode componentType) { compileStack.pushLoop(forLoop.getVariableScope(), forLoop.getStatementLabel()); forLoop.getVariable().setType(componentType); Label breakLabel = compileStack.getBreakLabel(); Label continueLabel = compileStack.getContinueLabel(); int array = compileStack.defineTemporaryVariable("$array$", ClassHelper.OBJECT_TYPE, true); mv.visitInsn(ICONST_0); int idx = compileStack.defineTemporaryVariable("$idx$", ClassHelper.int_TYPE, true); mv.startLoopVisitLabel(continueLabel); mv.visitVarInsn(ILOAD, idx); mv.visitVarInsn(ALOAD, array); mv.visitInsn(ARRAYLENGTH); mv.visitJumpInsn(IF_ICMPGE, breakLabel); mv.visitVarInsn(ALOAD, array); mv.visitVarInsn(ILOAD, idx); if (ClassHelper.isPrimitiveType(componentType)) { if (componentType == ClassHelper.long_TYPE) mv.visitInsn(LALOAD); else if (componentType == ClassHelper.float_TYPE) mv.visitInsn(FALOAD); else if (componentType == ClassHelper.double_TYPE) mv.visitInsn(DALOAD); else mv.visitInsn(IALOAD); } else mv.visitInsn(AALOAD); compileStack.defineVariable(forLoop.getVariable(), true); forLoop.getLoopBlock().visit(this); mv.visitVarInsn(ILOAD, idx); mv.visitInsn(ICONST_1); mv.visitInsn(IADD); mv.visitVarInsn(ISTORE, idx); mv.visitJumpInsn(GOTO, continueLabel); mv.visitLabel(breakLabel); compileStack.pop(); } private void visitForLoopWithIntRange(ForStatement forLoop) { compileStack.pushLoop(forLoop.getVariableScope(), forLoop.getStatementLabel()); forLoop.getVariable().setType(ClassHelper.int_TYPE); Label breakLabel = compileStack.getBreakLabel(); Label continueLabel = compileStack.getContinueLabel(); mv.visitInsn(DUP); int collIdx = compileStack.defineTemporaryVariable("$coll$", ClassHelper.OBJECT_TYPE, true); mv.visitTypeInsn(INSTANCEOF, BytecodeHelper.getClassInternalName(EmptyRange.class)); mv.visitJumpInsn(IFNE, breakLabel); mv.visitVarInsn(ALOAD, collIdx); mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "groovy/lang/Range", "getFrom", "()Ljava/lang/Comparable;"); BytecodeExpr.unbox(ClassHelper.int_TYPE, mv); mv.visitVarInsn(ALOAD, collIdx); mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "groovy/lang/Range", "getTo", "()Ljava/lang/Comparable;"); BytecodeExpr.unbox(ClassHelper.int_TYPE, mv); mv.visitVarInsn(ALOAD, collIdx); mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "groovy/lang/Range", "isReverse", "()Z"); mv.visitInsn(DUP); int isReverse = compileStack.defineTemporaryVariable("$isReverse$", ClassHelper.boolean_TYPE, true); Label lElse1 = new Label(); mv.visitJumpInsn(IFEQ, lElse1); mv.visitInsn(SWAP); mv.visitLabel(lElse1); int otherEnd = compileStack.defineTemporaryVariable("$otherEnd$", ClassHelper.int_TYPE, true); int thisEnd = compileStack.defineTemporaryVariable("$thisEnd$", ClassHelper.int_TYPE, true); Register it = compileStack.defineVariable(forLoop.getVariable(), false); mv.startLoopVisitLabel(continueLabel); mv.visitVarInsn(ILOAD, otherEnd); mv.visitVarInsn(ILOAD, thisEnd); Label lElse2 = new Label(), lDone2 = new Label(); mv.visitVarInsn(ILOAD, isReverse); mv.visitJumpInsn(IFNE, lElse2); mv.visitJumpInsn(IF_ICMPLT, breakLabel); mv.visitJumpInsn(GOTO, lDone2); mv.visitLabel(lElse2); mv.visitJumpInsn(IF_ICMPGT, breakLabel); mv.visitLabel(lDone2); mv.visitVarInsn(ILOAD, thisEnd); mv.visitInsn(DUP); mv.visitVarInsn(ISTORE, it.getIndex()); mv.visitInsn(ICONST_1); mv.visitVarInsn(ILOAD, isReverse); Label lElse3 = new Label(), lDone3 = new Label(); mv.visitJumpInsn(IFNE, lElse3); mv.visitInsn(IADD); mv.visitJumpInsn(GOTO, lDone3); mv.visitLabel(lElse3); mv.visitInsn(ISUB); mv.visitLabel(lDone3); mv.visitVarInsn(ISTORE, thisEnd); forLoop.getLoopBlock().visit(this); mv.visitJumpInsn(GOTO, continueLabel); mv.visitLabel(breakLabel); compileStack.pop(); } @Override public void visitIfElse(IfStatement ifElse) { visitStatement(ifElse); final BooleanExpression ifExpr = ifElse.getBooleanExpression(); Label elseLabel = new Label(); final BytecodeExpr condition = transformLogical(ifExpr, elseLabel, false); condition.visit(mv); compileStack.pushBooleanExpression(); ifElse.getIfBlock().visit(this); compileStack.pop(); Label endLabel = new Label(); if (ifElse.getElseBlock() != EmptyStatement.INSTANCE) { mv.visitJumpInsn(GOTO, endLabel); } mv.visitLabel(elseLabel); if (ifElse.getElseBlock() != EmptyStatement.INSTANCE) { compileStack.pushBooleanExpression(); ifElse.getElseBlock().visit(this); compileStack.pop(); mv.visitLabel(endLabel); } } @Override public void visitReturnStatement(ReturnStatement statement) { visitStatement(statement); Expression returnExpression = statement.getExpression(); if (!shouldImproveReturnType && !methodNode.getReturnType().equals(ClassHelper.VOID_TYPE)) { CastExpression castExpression = new CastExpression(methodNode.getReturnType(), returnExpression); castExpression.setSourcePosition(returnExpression); returnExpression = castExpression; } BytecodeExpr bytecodeExpr = (BytecodeExpr) transformToGround(returnExpression); if (bytecodeExpr instanceof ResolvedMethodBytecodeExpr) { ResolvedMethodBytecodeExpr resolvedMethodBytecodeExpr = (ResolvedMethodBytecodeExpr) bytecodeExpr; if (resolvedMethodBytecodeExpr.getMethodNode() == methodNode) { if (methodNode.isStatic() || resolvedMethodBytecodeExpr.getObject().isThis() || methodNode.isPrivate() || (methodNode.getModifiers() & ACC_FINAL) != 0) { tailRecursive(resolvedMethodBytecodeExpr); return; } } } bytecodeExpr.visit(mv); ClassNode exprType = bytecodeExpr.getType(); ClassNode returnType = methodNode.getReturnType(); if (returnType.equals(ClassHelper.VOID_TYPE)) { compileStack.applyFinallyBlocks(); } else { if (shouldImproveReturnType) { if (bytecodeExpr.getType().equals(ClassHelper.VOID_TYPE)) { mv.visitInsn(ACONST_NULL); } else { BytecodeExpr.box(exprType, mv); exprType = TypeUtil.wrapSafely(exprType); calculatedReturnType = TypeUtil.commonType(calculatedReturnType, exprType); BytecodeExpr.cast(exprType, calculatedReturnType, mv); } } else { if (bytecodeExpr.getType().equals(ClassHelper.VOID_TYPE)) { mv.visitInsn(ACONST_NULL); } else { BytecodeExpr.box(exprType, mv); BytecodeExpr.cast(TypeUtil.wrapSafely(exprType), TypeUtil.wrapSafely(returnType), mv); } } if (compileStack.hasFinallyBlocks()) { int returnValueIdx = compileStack.defineTemporaryVariable("returnValue", ClassHelper.OBJECT_TYPE, true); compileStack.applyFinallyBlocks(); mv.visitVarInsn(ALOAD, returnValueIdx); } BytecodeExpr.unbox(returnType, mv); } bytecodeExpr.doReturn(returnType, mv); } private void tailRecursive(ResolvedMethodBytecodeExpr resolvedMethodBytecodeExpr) { Parameter[] parameters = methodNode.getParameters(); int varIndex = methodNode.isStatic() ? 0 : 1; if (varIndex != 0) { resolvedMethodBytecodeExpr.getObject().visit(mv); } for (int i = 0; i != parameters.length; ++i) { BytecodeExpr be = (BytecodeExpr) resolvedMethodBytecodeExpr.getBargs().getExpressions().get(i); be.visit(mv); final ClassNode paramType = parameters[i].getType(); final ClassNode type = be.getType(); BytecodeExpr.box(type, mv); BytecodeExpr.cast(TypeUtil.wrapSafely(type), TypeUtil.wrapSafely(paramType), mv); BytecodeExpr.unbox(paramType, mv); varIndex += (paramType == ClassHelper.long_TYPE || paramType == ClassHelper.double_TYPE) ? 2 : 1; } for (int i = parameters.length-1; i >= 0; --i) { final ClassNode paramType = parameters[i].getType(); varIndex -= (paramType == ClassHelper.long_TYPE || paramType == ClassHelper.double_TYPE) ? 2 : 1; if (paramType == double_TYPE) { mv.visitVarInsn(Opcodes.DSTORE, varIndex); } else if (paramType == float_TYPE) { mv.visitVarInsn(Opcodes.FSTORE, varIndex); } else if (paramType == long_TYPE) { mv.visitVarInsn(Opcodes.LSTORE, varIndex); } else if ( paramType == boolean_TYPE || paramType == char_TYPE || paramType == byte_TYPE || paramType == int_TYPE || paramType == short_TYPE) { mv.visitVarInsn(Opcodes.ISTORE, varIndex); } else { mv.visitVarInsn(Opcodes.ASTORE, varIndex); } } if (!methodNode.isStatic()) { mv.visitVarInsn(ASTORE, 0); } mv.visitJumpInsn(GOTO, startLabel); return; } @Override public void visitWhileLoop(WhileStatement loop) { visitStatement(loop); compileStack.pushLoop(loop.getStatementLabel()); Label continueLabel = compileStack.getContinueLabel(); Label breakLabel = compileStack.getBreakLabel(); final BytecodeExpr be = transformLogical(loop.getBooleanExpression().getExpression(), breakLabel, false); mv.startLoopVisitLabel(continueLabel); be.visit(mv); loop.getLoopBlock().visit(this); mv.visitJumpInsn(GOTO, continueLabel); mv.visitLabel(breakLabel); compileStack.pop(); } public void visitSwitch(SwitchStatement statement) { visitStatement(statement); List caseStatements = statement.getCaseStatements(); BytecodeExpr cond = (BytecodeExpr) transform(statement.getExpression()); cond.visit(mv); if (statement.getExpression() instanceof VariableExpression) { final VariableExpression ve = (VariableExpression) statement.getExpression(); if(!ve.getName().equals("this")) { for (Iterator iter = caseStatements.iterator(); iter.hasNext(); ) { CaseStatement caseStatement = (CaseStatement) iter.next(); final Expression option = caseStatement.getExpression(); if (option instanceof ClassExpression) { final BlockStatement newCode = new BlockStatement(); final VariableExpression newVar = new VariableExpression(ve.getName(), option.getType()); final DeclarationExpression newVarDecl = new DeclarationExpression( newVar, Token.newSymbol(Types.EQUAL, -1, -1), ve ); newVarDecl.setSourcePosition(caseStatement); newCode.addStatement( new ExpressionStatement( newVarDecl ) ); newCode.addStatement(caseStatement.getCode()); caseStatement.setCode(newCode); newCode.visit(new ClassCodeExpressionTransformer() { @Override public Expression transform(Expression exp) { if (exp instanceof VariableExpression) { VariableExpression vexp = (VariableExpression) exp; if (vexp.getName().equals(ve.getName())) { vexp.setAccessedVariable(newVar); } } return super.transform(exp); } protected SourceUnit getSourceUnit() { return su; } }); } } } } // switch does not have a continue label. use its parent's for continue Label breakLabel = compileStack.pushSwitch(); int switchVariableIndex = compileStack.defineTemporaryVariable("switch", cond.getType(), true); int caseCount = caseStatements.size(); Label[] codeLabels = new Label[caseCount]; Label[] condLabels = new Label[caseCount + 1]; int i; for (i = 0; i < caseCount; i++) { codeLabels[i] = new Label(); condLabels[i] = new Label(); } Label defaultLabel = new Label(); i = 0; for (Iterator iter = caseStatements.iterator(); iter.hasNext(); i++) { CaseStatement caseStatement = (CaseStatement) iter.next(); mv.visitLabel(condLabels[i]); visitStatement(caseStatement); BytecodeExpr.load(cond.getType(), switchVariableIndex, mv); BytecodeExpr option = (BytecodeExpr) transformToGround(caseStatement.getExpression()); if (ClassHelper.isPrimitiveType(option.getType()) && ClassHelper.isPrimitiveType(cond.getType())) { option.visit(mv); final BytecodeExpr caseValue = new BytecodeExpr(option, option.getType()) { protected void compile(MethodVisitor mv) { } }; final BytecodeExpr switchValue = new BytecodeExpr(cond, cond.getType()) { protected void compile(MethodVisitor mv) { } }; BinaryExpression eq = new BinaryExpression(caseValue, Token.newSymbol(Types.COMPARE_EQUAL, -1, -1), switchValue); eq.setSourcePosition(caseValue); transformLogical(eq, codeLabels[i], true).visit(mv); } else { if (ClassHelper.isPrimitiveType(cond.getType())) { if (caseStatement.getExpression() instanceof ClassExpression) { addError("Primitive type " + cond.getType().getName() + " con not be instance of " + ((ClassExpression)caseStatement.getExpression()).getType().getName(), caseStatement.getExpression()); continue; } BytecodeExpr.box(cond.getType(), mv); option.visit(mv); BytecodeExpr.box(option.getType(), mv); Label next = i == caseCount - 1 ? defaultLabel : condLabels[i + 1]; Label notNull = new Label(); mv.visitInsn(DUP); mv.visitJumpInsn(IFNONNULL, notNull); mv.visitJumpInsn(IF_ACMPEQ, codeLabels[i]); mv.visitJumpInsn(GOTO, next); mv.visitLabel(notNull); final BytecodeExpr caseValue = new BytecodeExpr(option, TypeUtil.wrapSafely(option.getType())) { protected void compile(MethodVisitor mv) { } }; final BytecodeExpr switchValue = new BytecodeExpr(cond, TypeUtil.wrapSafely(cond.getType())) { protected void compile(MethodVisitor mv) { mv.visitInsn(SWAP); } }; MethodCallExpression exp = new MethodCallExpression(caseValue, "isCase", new ArgumentListExpression(switchValue)); exp.setSourcePosition(caseValue); transformLogical(exp, codeLabels[i], true).visit(mv); } else { if (caseStatement.getExpression() instanceof ClassExpression) { BytecodeExpr.box(cond.getType(), mv); mv.visitTypeInsn(INSTANCEOF, BytecodeHelper.getClassInternalName(caseStatement.getExpression().getType())); mv.visitJumpInsn(IFNE, codeLabels[i]); } else { option.visit(mv); BytecodeExpr.box(option.getType(), mv); Label next = i == caseCount - 1 ? defaultLabel : condLabels[i + 1]; Label notNull = new Label(); mv.visitInsn(DUP); mv.visitJumpInsn(IFNONNULL, notNull); mv.visitJumpInsn(IF_ACMPEQ, codeLabels[i]); mv.visitJumpInsn(GOTO, next); mv.visitLabel(notNull); final BytecodeExpr caseValue = new BytecodeExpr(option, TypeUtil.wrapSafely(option.getType())) { protected void compile(MethodVisitor mv) { } }; final BytecodeExpr switchValue = new BytecodeExpr(cond, TypeUtil.wrapSafely(cond.getType())) { protected void compile(MethodVisitor mv) { mv.visitInsn(SWAP); } }; MethodCallExpression exp = new MethodCallExpression(caseValue, "isCase", new ArgumentListExpression(switchValue)); exp.setSourcePosition(caseValue); transformLogical(exp, codeLabels[i], true).visit(mv); } } } } mv.visitJumpInsn(GOTO, defaultLabel); i = 0; for (Iterator iter = caseStatements.iterator(); iter.hasNext(); i++) { CaseStatement caseStatement = (CaseStatement) iter.next(); visitStatement(caseStatement); mv.visitLabel(codeLabels[i]); caseStatement.getCode().visit(this); } mv.visitLabel(defaultLabel); statement.getDefaultStatement().visit(this); mv.visitLineNumber(statement.getLastLineNumber(), new Label()); mv.visitLabel(breakLabel); compileStack.pop(); } @Override public void visitCaseStatement(CaseStatement statement) { } @Override public void visitDoWhileLoop(DoWhileStatement loop) { visitStatement(loop); super.visitDoWhileLoop(loop); throw new UnsupportedOperationException(); } @Override public void visitSynchronizedStatement(SynchronizedStatement sync) { visitStatement(sync); sync.setExpression(transform(sync.getExpression())); ((BytecodeExpr) sync.getExpression()).visit(mv); final int index = compileStack.defineTemporaryVariable("synchronized", ClassHelper.OBJECT_TYPE, true); final Label synchronizedStart = new Label(); final Label synchronizedEnd = new Label(); final Label catchAll = new Label(); mv.visitVarInsn(ALOAD, index); mv.visitInsn(MONITORENTER); mv.visitLabel(synchronizedStart); Runnable finallyPart = new Runnable() { public void run() { mv.visitVarInsn(ALOAD, index); mv.visitInsn(MONITOREXIT); } }; compileStack.pushFinallyBlock(finallyPart); sync.getCode().visit(this); finallyPart.run(); mv.visitJumpInsn(GOTO, synchronizedEnd); mv.startExceptionBlock(); // exception variable mv.visitLabel(catchAll); finallyPart.run(); mv.visitInsn(ATHROW); mv.visitLabel(synchronizedEnd); compileStack.popFinallyBlock(); exceptionBlocks.add(new Runnable() { public void run() { mv.visitTryCatchBlock(synchronizedStart, catchAll, catchAll, null); } }); } @Override public void visitThrowStatement(ThrowStatement ts) { visitStatement(ts); super.visitThrowStatement(ts); final BytecodeExpr thrown = (BytecodeExpr) ts.getExpression(); if (!TypeUtil.isDirectlyAssignableFrom(TypeUtil.THROWABLE, thrown.getType())) { addError("Only java.lang.Throwable objects may be thrown", thrown); } thrown.visit(mv); mv.visitInsn(ATHROW); } public void visitContinueStatement(ContinueStatement statement) { visitStatement(statement); String name = statement.getLabel(); Label continueLabel = compileStack.getContinueLabel(); if (name != null) continueLabel = compileStack.getNamedContinueLabel(name); if (continueLabel == null) { addError("Illegal continue label '" + name + "'", statement); } compileStack.applyFinallyBlocks(continueLabel, false); mv.visitJumpInsn(GOTO, continueLabel); } public void visitTryCatchFinally(TryCatchStatement statement) { visitStatement(statement); Statement tryStatement = statement.getTryStatement(); final Statement finallyStatement = statement.getFinallyStatement(); int anyExceptionIndex = compileStack.defineTemporaryVariable("exception", DYNAMIC_TYPE, false); if (!finallyStatement.isEmpty()) { compileStack.pushFinallyBlock( new Runnable() { public void run() { compileStack.pushFinallyBlockVisit(this); finallyStatement.visit(StaticCompiler.this); compileStack.popFinallyBlockVisit(this); } } ); } // start try block, label needed for exception table final Label tryStart = new Label(); mv.visitLabel(tryStart); tryStatement.visit(this); // goto finally part final Label finallyStart = new Label(); mv.visitJumpInsn(GOTO, finallyStart); // marker needed for Exception table final Label greEnd = new Label(); mv.visitLabel(greEnd); final Label tryEnd = new Label(); mv.visitLabel(tryEnd); for (CatchStatement catchStatement : statement.getCatchStatements()) { ClassNode exceptionType = catchStatement.getExceptionType(); // start catch block, label needed for exception table final Label catchStart = new Label(); mv.visitLabel(catchStart); mv.startExceptionBlock(); // create exception variable and store the exception compileStack.pushState(); compileStack.defineVariable(catchStatement.getVariable(), true); // handle catch body catchStatement.visit(this); compileStack.pop(); // goto finally start mv.visitJumpInsn(GOTO, finallyStart); // add exception to table final String exceptionTypeInternalName = BytecodeHelper.getClassInternalName(exceptionType); exceptionBlocks.add(new Runnable() { public void run() { mv.visitTryCatchBlock(tryStart, tryEnd, catchStart, exceptionTypeInternalName); } }); } // marker needed for the exception table final Label endOfAllCatches = new Label(); mv.visitLabel(endOfAllCatches); // remove the finally, don't let it visit itself if (!finallyStatement.isEmpty()) compileStack.popFinallyBlock(); // start finally mv.visitLabel(finallyStart); finallyStatement.visit(this); // goto end of finally Label afterFinally = new Label(); mv.visitJumpInsn(GOTO, afterFinally); // start a block catching any Exception final Label catchAny = new Label(); mv.visitLabel(catchAny); mv.startExceptionBlock(); //store exception mv.visitVarInsn(ASTORE, anyExceptionIndex); finallyStatement.visit(this); // load the exception and rethrow it mv.visitVarInsn(ALOAD, anyExceptionIndex); mv.visitInsn(ATHROW); // end of all catches and finally parts mv.visitLabel(afterFinally); mv.visitInsn(NOP); // add catch any block to exception table exceptionBlocks.add(new Runnable() { public void run() { mv.visitTryCatchBlock(tryStart, endOfAllCatches, catchAny, null); } }); } public void execute() { checkWeakerOverriding(); addReturnIfNeeded(); compileStack.init(methodNode.getVariableScope(), methodNode.getParameters(), mv, methodNode.getDeclaringClass()); getCode().visit(this); compileStack.clear(); for (Runnable runnable : exceptionBlocks) { runnable.run(); } } private void checkWeakerOverriding() { ClassNode clazz = methodNode.getDeclaringClass(); if (methodNode.isPublic()) return; if (methodNode instanceof ConstructorNode || methodNode.isStaticConstructor()) return; ClassNode superClass = clazz.getSuperClass(); MethodNode superMethod = findSuperMethod(methodNode, clazz, superClass); if (superMethod != null && isWeaker(methodNode, superMethod)) { addError("Attempting to assign weaker access to " + PresentationUtil.getText(methodNode), methodNode); } else { for (ClassNode intf : clazz.getInterfaces()) { superMethod = findSuperMethod(methodNode, clazz, intf); if (superMethod != null && isWeaker(methodNode, superMethod)) { addError("Attempting to assign weaker access to " + PresentationUtil.getText(methodNode), methodNode); } } } } /** * precondition: !method.isPublic() */ private boolean isWeaker(MethodNode method, MethodNode superMethod) { if (superMethod.isPublic()) return true; if (superMethod.isProtected()) return !method.isProtected(); return false; } private MethodNode findSuperMethod(MethodNode method, ClassNode clazz, ClassNode superClass) { Parameter[] methodParameters = method.getParameters(); Parameter[] params = new Parameter[methodParameters.length]; for (int i = 0; i < params.length; i++) { ClassNode type = TypeUtil.mapTypeFromSuper(methodParameters[i].getType(), superClass, clazz); params[i] = new Parameter(type, methodParameters[i].getName()); } return superClass.getMethod(method.getName(), params); } public void visitBytecodeSequence(BytecodeSequence sequence) { visitStatement(sequence); ((BytecodeInstruction) sequence.getInstructions().get(0)).visit(mv); } public LocalVarInferenceTypes getLocalVarInferenceTypes() { return mv.getLocalVarInferenceTypes(); } }