You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2017/12/17 14:01:30 UTC

[19/62] [abbrv] [partial] groovy git commit: Move Java source set into `src/main/java`

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/StatementWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/StatementWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/StatementWriter.java
new file mode 100644
index 0000000..45d1908
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/StatementWriter.java
@@ -0,0 +1,638 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.codehaus.groovy.classgen.asm;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ClosureListExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.EmptyExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.BreakStatement;
+import org.codehaus.groovy.ast.stmt.CaseStatement;
+import org.codehaus.groovy.ast.stmt.CatchStatement;
+import org.codehaus.groovy.ast.stmt.ContinueStatement;
+import org.codehaus.groovy.ast.stmt.DoWhileStatement;
+import org.codehaus.groovy.ast.stmt.EmptyStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.stmt.IfStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.SwitchStatement;
+import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
+import org.codehaus.groovy.ast.stmt.ThrowStatement;
+import org.codehaus.groovy.ast.stmt.TryCatchStatement;
+import org.codehaus.groovy.ast.stmt.WhileStatement;
+import org.codehaus.groovy.classgen.asm.CompileStack.BlockRecorder;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+import java.util.Iterator;
+import java.util.List;
+
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.ATHROW;
+import static org.objectweb.asm.Opcodes.CHECKCAST;
+import static org.objectweb.asm.Opcodes.GOTO;
+import static org.objectweb.asm.Opcodes.IFEQ;
+import static org.objectweb.asm.Opcodes.MONITORENTER;
+import static org.objectweb.asm.Opcodes.MONITOREXIT;
+import static org.objectweb.asm.Opcodes.NOP;
+import static org.objectweb.asm.Opcodes.RETURN;
+
+public class StatementWriter {
+    // iterator
+    private static final MethodCaller iteratorNextMethod = MethodCaller.newInterface(Iterator.class, "next");
+    private static final MethodCaller iteratorHasNextMethod = MethodCaller.newInterface(Iterator.class, "hasNext");
+    
+    private final WriterController controller;
+    public StatementWriter(WriterController controller) {
+        this.controller = controller;
+    }
+
+    protected void writeStatementLabel(Statement statement) {
+        String name = statement.getStatementLabel();
+        if (name != null) {
+            Label label = controller.getCompileStack().createLocalLabel(name);
+            controller.getMethodVisitor().visitLabel(label);
+        }
+    }
+    
+    public void writeBlockStatement(BlockStatement block) {
+        CompileStack compileStack = controller.getCompileStack();
+
+        //GROOVY-4505 use no line number information for the block
+        writeStatementLabel(block);
+        
+        int mark = controller.getOperandStack().getStackLength();
+        compileStack.pushVariableScope(block.getVariableScope());
+        for (Statement statement : block.getStatements()) {
+            statement.visit(controller.getAcg());
+        }
+        compileStack.pop();
+
+        controller.getOperandStack().popDownTo(mark);
+    }
+
+    public void writeForStatement(ForStatement loop) {
+        Parameter loopVar = loop.getVariable();
+        if (loopVar == ForStatement.FOR_LOOP_DUMMY) {
+            writeForLoopWithClosureList(loop);
+        } else {
+            writeForInLoop(loop);
+        }
+    }
+    
+    protected void writeIteratorHasNext(MethodVisitor mv) {
+        iteratorHasNextMethod.call(mv);
+    }
+    
+    protected void writeIteratorNext(MethodVisitor mv) {
+        iteratorNextMethod.call(mv);
+    }
+    
+    protected void writeForInLoop(ForStatement loop) {
+        controller.getAcg().onLineNumber(loop,"visitForLoop");
+        writeStatementLabel(loop);
+        
+        CompileStack compileStack = controller.getCompileStack();
+        MethodVisitor mv = controller.getMethodVisitor();
+        OperandStack operandStack = controller.getOperandStack();
+
+        compileStack.pushLoop(loop.getVariableScope(), loop.getStatementLabels());
+
+        // Declare the loop counter.
+        BytecodeVariable variable = compileStack.defineVariable(loop.getVariable(), false);
+
+        // Then get the iterator and generate the loop control
+        MethodCallExpression iterator = new MethodCallExpression(loop.getCollectionExpression(), "iterator", new ArgumentListExpression());
+        iterator.visit(controller.getAcg());
+        operandStack.doGroovyCast(ClassHelper.Iterator_TYPE);
+
+        final int iteratorIdx = compileStack.defineTemporaryVariable("iterator", ClassHelper.Iterator_TYPE, true);
+
+        Label continueLabel = compileStack.getContinueLabel();
+        Label breakLabel = compileStack.getBreakLabel();
+
+        mv.visitLabel(continueLabel);
+        mv.visitVarInsn(ALOAD, iteratorIdx);
+        writeIteratorHasNext(mv);
+        // note: ifeq tests for ==0, a boolean is 0 if it is false
+        mv.visitJumpInsn(IFEQ, breakLabel);
+
+        mv.visitVarInsn(ALOAD, iteratorIdx);
+        writeIteratorNext(mv);
+        operandStack.push(ClassHelper.OBJECT_TYPE);
+        operandStack.storeVar(variable);
+
+        // Generate the loop body
+        loop.getLoopBlock().visit(controller.getAcg());
+
+        mv.visitJumpInsn(GOTO, continueLabel);
+        mv.visitLabel(breakLabel);
+
+        compileStack.removeVar(iteratorIdx);
+        compileStack.pop();
+    }
+
+    private void visitExpressionOfLoopStatement(Expression expression) {
+        if (expression instanceof ClosureListExpression) {
+            for (Expression e : ((ClosureListExpression) expression).getExpressions()) {
+                visitExpressionOrStatement(e);
+            }
+        } else {
+            visitExpressionOrStatement(expression);
+        }
+    }
+
+    protected void writeForLoopWithClosureList(ForStatement loop) {
+        controller.getAcg().onLineNumber(loop,"visitForLoop");
+        writeStatementLabel(loop);
+
+        MethodVisitor mv = controller.getMethodVisitor();
+        controller.getCompileStack().pushLoop(loop.getVariableScope(), loop.getStatementLabels());
+
+        ClosureListExpression clExpr = (ClosureListExpression) loop.getCollectionExpression();
+        controller.getCompileStack().pushVariableScope(clExpr.getVariableScope());
+
+        List<Expression> expressions = clExpr.getExpressions();
+        int size = expressions.size();
+
+        // middle element is condition, lower half is init, higher half is increment
+        int condIndex = (size - 1) / 2;
+
+        // visit init
+        for (int i = 0; i < condIndex; i++) {
+            visitExpressionOfLoopStatement(expressions.get(i));
+        }
+
+        Label continueLabel = controller.getCompileStack().getContinueLabel();
+        Label breakLabel = controller.getCompileStack().getBreakLabel();
+
+        Label cond = new Label();
+        mv.visitLabel(cond);
+        // visit condition leave boolean on stack
+        {
+            Expression condExpr = expressions.get(condIndex);
+            int mark = controller.getOperandStack().getStackLength();
+            condExpr.visit(controller.getAcg());
+            controller.getOperandStack().castToBool(mark,true);
+        }
+        // jump if we don't want to continue
+        // note: ifeq tests for ==0, a boolean is 0 if it is false
+        controller.getOperandStack().jump(IFEQ, breakLabel);
+
+        // Generate the loop body
+        loop.getLoopBlock().visit(controller.getAcg());
+
+        // visit increment
+        mv.visitLabel(continueLabel);
+        for (int i = condIndex + 1; i < size; i++) {
+            visitExpressionOfLoopStatement(expressions.get(i));
+        }
+
+        // jump to test the condition again
+        mv.visitJumpInsn(GOTO, cond);
+
+        // loop end
+        mv.visitLabel(breakLabel);
+
+        controller.getCompileStack().pop();
+        controller.getCompileStack().pop();
+    }
+    
+    private void visitExpressionOrStatement(Object o) {
+        if (o == EmptyExpression.INSTANCE) return;
+        if (o instanceof Expression) {
+            Expression expr = (Expression) o;
+            int mark = controller.getOperandStack().getStackLength();
+            expr.visit(controller.getAcg());
+            controller.getOperandStack().popDownTo(mark);
+        } else {
+            ((Statement) o).visit(controller.getAcg());
+        }
+    }
+
+    private void visitConditionOfLoopingStatement(BooleanExpression bool, Label breakLabel, MethodVisitor mv) {
+        boolean boolHandled = false;
+        if (bool.getExpression() instanceof ConstantExpression) {
+            ConstantExpression constant = (ConstantExpression) bool.getExpression();
+            if (constant.getValue()==Boolean.TRUE) {
+                boolHandled = true;
+                // do nothing
+            } else if (constant.getValue()==Boolean.FALSE) {
+                boolHandled = true;
+                mv.visitJumpInsn(GOTO, breakLabel);
+            }
+        }
+
+        if(!boolHandled) {
+            bool.visit(controller.getAcg());
+            controller.getOperandStack().jump(IFEQ, breakLabel);
+        }
+    }
+
+    public void writeWhileLoop(WhileStatement loop) {
+        controller.getAcg().onLineNumber(loop,"visitWhileLoop");
+        writeStatementLabel(loop);
+
+        MethodVisitor mv = controller.getMethodVisitor();
+
+        controller.getCompileStack().pushLoop(loop.getStatementLabels());
+        Label continueLabel = controller.getCompileStack().getContinueLabel();
+        Label breakLabel = controller.getCompileStack().getBreakLabel();
+
+        mv.visitLabel(continueLabel);
+
+        this.visitConditionOfLoopingStatement(loop.getBooleanExpression(), breakLabel, mv);
+        loop.getLoopBlock().visit(controller.getAcg());
+
+        mv.visitJumpInsn(GOTO, continueLabel);
+        mv.visitLabel(breakLabel);
+
+        controller.getCompileStack().pop();            
+    }
+
+    public void writeDoWhileLoop(DoWhileStatement loop) {
+        controller.getAcg().onLineNumber(loop,"visitDoWhileLoop");
+        writeStatementLabel(loop);
+
+        MethodVisitor mv = controller.getMethodVisitor();
+
+        controller.getCompileStack().pushLoop(loop.getStatementLabels());
+        Label continueLabel = controller.getCompileStack().getContinueLabel();
+        Label breakLabel = controller.getCompileStack().getBreakLabel();
+
+        mv.visitLabel(continueLabel);
+
+        loop.getLoopBlock().visit(controller.getAcg());
+        this.visitConditionOfLoopingStatement(loop.getBooleanExpression(), breakLabel, mv);
+
+        mv.visitJumpInsn(GOTO, continueLabel);
+        mv.visitLabel(breakLabel);
+
+        controller.getCompileStack().pop();
+    }
+
+    public void writeIfElse(IfStatement ifElse) {
+        controller.getAcg().onLineNumber(ifElse,"visitIfElse");
+        writeStatementLabel(ifElse);
+
+        MethodVisitor mv = controller.getMethodVisitor();
+
+        ifElse.getBooleanExpression().visit(controller.getAcg());
+        Label l0 = controller.getOperandStack().jump(IFEQ);
+
+        // if-else is here handled as a special version
+        // of a boolean expression
+        controller.getCompileStack().pushBooleanExpression();
+        ifElse.getIfBlock().visit(controller.getAcg());
+        controller.getCompileStack().pop();
+
+        if (ifElse.getElseBlock()==EmptyStatement.INSTANCE) {
+            mv.visitLabel(l0);
+        } else {
+            Label l1 = new Label();
+            mv.visitJumpInsn(GOTO, l1);
+            mv.visitLabel(l0);
+    
+            controller.getCompileStack().pushBooleanExpression();
+            ifElse.getElseBlock().visit(controller.getAcg());
+            controller.getCompileStack().pop();
+    
+            mv.visitLabel(l1);
+        } 
+    }
+
+    public void writeTryCatchFinally(TryCatchStatement statement) {
+        controller.getAcg().onLineNumber(statement, "visitTryCatchFinally");
+        writeStatementLabel(statement);
+        
+        MethodVisitor mv = controller.getMethodVisitor();
+        CompileStack compileStack = controller.getCompileStack();
+        OperandStack operandStack = controller.getOperandStack();
+
+        Statement tryStatement = statement.getTryStatement();
+        final Statement finallyStatement = statement.getFinallyStatement();
+
+        // start try block, label needed for exception table
+        Label tryStart = new Label();
+        mv.visitLabel(tryStart);
+        BlockRecorder tryBlock = makeBlockRecorder(finallyStatement);
+        tryBlock.startRange(tryStart);
+
+        tryStatement.visit(controller.getAcg());
+
+        // goto finally part
+        Label finallyStart = new Label();
+        mv.visitJumpInsn(GOTO, finallyStart);
+
+        Label tryEnd = new Label();
+        mv.visitLabel(tryEnd);
+        tryBlock.closeRange(tryEnd);
+        // pop for "makeBlockRecorder(finallyStatement)"
+        controller.getCompileStack().pop();
+
+        BlockRecorder catches = makeBlockRecorder(finallyStatement);
+        for (CatchStatement catchStatement : statement.getCatchStatements()) {
+            ClassNode exceptionType = catchStatement.getExceptionType();
+            String exceptionTypeInternalName = BytecodeHelper.getClassInternalName(exceptionType);
+
+            // start catch block, label needed for exception table
+            Label catchStart = new Label();
+            mv.visitLabel(catchStart);
+            catches.startRange(catchStart);
+
+            // create exception variable and store the exception
+            Parameter exceptionVariable = catchStatement.getVariable();
+            compileStack.pushState();
+            compileStack.defineVariable(exceptionVariable, true);
+            // handle catch body
+            catchStatement.visit(controller.getAcg());
+            // place holder to avoid problems with empty catch blocks
+            mv.visitInsn(NOP);
+            // pop for the variable
+            controller.getCompileStack().pop();
+
+            // end of catch
+            Label catchEnd = new Label();
+            mv.visitLabel(catchEnd);
+            catches.closeRange(catchEnd);
+
+            // goto finally start
+            mv.visitJumpInsn(GOTO, finallyStart);
+            compileStack.writeExceptionTable(tryBlock, catchStart, exceptionTypeInternalName);
+        }
+
+        // Label used to handle exceptions in catches and regularly
+        // visited finals.
+        Label catchAny = new Label();
+
+        // add "catch any" block to exception table for try part we do this 
+        // after the exception blocks, because else this one would supersede
+        // any of those otherwise
+        compileStack.writeExceptionTable(tryBlock, catchAny, null);
+        // same for the catch parts
+        compileStack.writeExceptionTable(catches, catchAny, null);
+
+        // pop for "makeBlockRecorder(catches)"
+        compileStack.pop();
+
+        // start finally
+        mv.visitLabel(finallyStart);
+        finallyStatement.visit(controller.getAcg());
+        mv.visitInsn(NOP);  //**
+
+        // goto after all-catching block
+        Label skipCatchAll = new Label();
+        mv.visitJumpInsn(GOTO, skipCatchAll);
+
+        // start a block catching any Exception
+        mv.visitLabel(catchAny);
+        //store exception
+        //TODO: maybe define a Throwable and use it here instead of Object
+        operandStack.push(ClassHelper.OBJECT_TYPE);
+        final int anyExceptionIndex = compileStack.defineTemporaryVariable("exception", true);
+
+        finallyStatement.visit(controller.getAcg());
+
+        // load the exception and rethrow it
+        mv.visitVarInsn(ALOAD, anyExceptionIndex);
+        mv.visitInsn(ATHROW);
+
+        mv.visitLabel(skipCatchAll);
+        compileStack.removeVar(anyExceptionIndex);
+    }
+    
+    private BlockRecorder makeBlockRecorder(final Statement finallyStatement) {
+        final BlockRecorder block = new BlockRecorder();
+        Runnable tryRunner = new Runnable() {
+            public void run() {
+                controller.getCompileStack().pushBlockRecorderVisit(block);
+                finallyStatement.visit(controller.getAcg());
+                controller.getCompileStack().popBlockRecorderVisit(block);
+            }
+        };
+        block.excludedStatement = tryRunner;
+        controller.getCompileStack().pushBlockRecorder(block);
+        return block;
+    }
+
+    public void writeSwitch(SwitchStatement statement) {
+        controller.getAcg().onLineNumber(statement, "visitSwitch");
+        writeStatementLabel(statement);
+
+        statement.getExpression().visit(controller.getAcg());
+
+        // switch does not have a continue label. use its parent's for continue
+        Label breakLabel = controller.getCompileStack().pushSwitch();
+
+        final int switchVariableIndex = controller.getCompileStack().defineTemporaryVariable("switch", true);
+
+        List caseStatements = statement.getCaseStatements();
+        int caseCount = caseStatements.size();
+        Label[] labels = new Label[caseCount + 1];
+        for (int i = 0; i < caseCount; i++) {
+            labels[i] = new Label();
+        }
+
+        int i = 0;
+        for (Iterator iter = caseStatements.iterator(); iter.hasNext(); i++) {
+            CaseStatement caseStatement = (CaseStatement) iter.next();
+            writeCaseStatement(caseStatement, switchVariableIndex, labels[i], labels[i + 1]);
+        }
+
+        statement.getDefaultStatement().visit(controller.getAcg());
+
+        controller.getMethodVisitor().visitLabel(breakLabel);
+
+        controller.getCompileStack().removeVar(switchVariableIndex);
+        controller.getCompileStack().pop();   
+    }
+    
+    protected void writeCaseStatement(
+            CaseStatement statement, int switchVariableIndex,
+            Label thisLabel, Label nextLabel) 
+    {
+        controller.getAcg().onLineNumber(statement, "visitCaseStatement");
+        MethodVisitor mv = controller.getMethodVisitor();
+        OperandStack operandStack = controller.getOperandStack();
+
+        mv.visitVarInsn(ALOAD, switchVariableIndex);
+        
+        statement.getExpression().visit(controller.getAcg());
+        operandStack.box();
+        controller.getBinaryExpressionHelper().getIsCaseMethod().call(mv);
+        operandStack.replace(ClassHelper.boolean_TYPE);
+
+        Label l0 = controller.getOperandStack().jump(IFEQ);
+
+        mv.visitLabel(thisLabel);
+
+        statement.getCode().visit(controller.getAcg());
+
+        // now if we don't finish with a break we need to jump past
+        // the next comparison
+        if (nextLabel != null) {
+            mv.visitJumpInsn(GOTO, nextLabel);
+        }
+
+        mv.visitLabel(l0);
+    }
+
+    public void writeBreak(BreakStatement statement) {
+        controller.getAcg().onLineNumber(statement, "visitBreakStatement");
+        writeStatementLabel(statement);
+
+        String name = statement.getLabel();
+        Label breakLabel = controller.getCompileStack().getNamedBreakLabel(name);
+        controller.getCompileStack().applyFinallyBlocks(breakLabel, true);
+
+        controller.getMethodVisitor().visitJumpInsn(GOTO, breakLabel);
+    }
+
+    public void writeContinue(ContinueStatement statement) {
+        controller.getAcg().onLineNumber(statement, "visitContinueStatement");
+        writeStatementLabel(statement);
+
+        String name = statement.getLabel();
+        Label continueLabel = controller.getCompileStack().getContinueLabel();
+        if (name != null) continueLabel = controller.getCompileStack().getNamedContinueLabel(name);
+        controller.getCompileStack().applyFinallyBlocks(continueLabel, false);
+        controller.getMethodVisitor().visitJumpInsn(GOTO, continueLabel);
+    }
+
+    public void writeSynchronized(SynchronizedStatement statement) {
+        controller.getAcg().onLineNumber(statement, "visitSynchronizedStatement");
+        writeStatementLabel(statement);
+        final MethodVisitor mv = controller.getMethodVisitor();
+        CompileStack compileStack = controller.getCompileStack();
+
+        statement.getExpression().visit(controller.getAcg());
+        controller.getOperandStack().box();
+        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);
+        // place holder for "empty" synchronized blocks, for example
+        // if there is only a break/continue.
+        mv.visitInsn(NOP);
+
+        Runnable finallyPart = new Runnable() {
+            public void run() {
+                mv.visitVarInsn(ALOAD, index);
+                mv.visitInsn(MONITOREXIT);
+            }
+        };
+        BlockRecorder fb = new BlockRecorder(finallyPart);
+        fb.startRange(synchronizedStart);
+        compileStack.pushBlockRecorder(fb);
+        statement.getCode().visit(controller.getAcg());
+
+        fb.closeRange(catchAll);
+        compileStack.writeExceptionTable(fb, catchAll, null);
+        compileStack.pop(); //pop fb
+
+        finallyPart.run();
+        mv.visitJumpInsn(GOTO, synchronizedEnd);
+        mv.visitLabel(catchAll);
+        finallyPart.run();
+        mv.visitInsn(ATHROW);
+
+        mv.visitLabel(synchronizedEnd);
+        compileStack.removeVar(index);
+    }
+
+    public void writeAssert(AssertStatement statement) {
+        controller.getAcg().onLineNumber(statement, "visitAssertStatement");
+        writeStatementLabel(statement);
+        controller.getAssertionWriter().writeAssertStatement(statement);
+    }
+
+    public void writeThrow(ThrowStatement statement) {
+        controller.getAcg().onLineNumber(statement, "visitThrowStatement");
+        writeStatementLabel(statement);
+        MethodVisitor mv = controller.getMethodVisitor();
+
+        statement.getExpression().visit(controller.getAcg());
+
+        // we should infer the type of the exception from the expression
+        mv.visitTypeInsn(CHECKCAST, "java/lang/Throwable");
+        mv.visitInsn(ATHROW);
+        
+        controller.getOperandStack().remove(1);
+    }
+
+    public void writeReturn(ReturnStatement statement) {
+        controller.getAcg().onLineNumber(statement, "visitReturnStatement");
+        writeStatementLabel(statement);
+        MethodVisitor mv = controller.getMethodVisitor();
+        OperandStack operandStack = controller.getOperandStack();
+        ClassNode returnType = controller.getReturnType();
+
+        if (returnType == ClassHelper.VOID_TYPE) {
+            if (!(statement.isReturningNullOrVoid())) {
+                //TODO: move to Verifier
+                controller.getAcg().throwException("Cannot use return statement with an expression on a method that returns void");
+            }
+            controller.getCompileStack().applyBlockRecorder();
+            mv.visitInsn(RETURN);
+            return;
+        }
+
+        Expression expression = statement.getExpression();
+        expression.visit(controller.getAcg());
+
+        operandStack.doGroovyCast(returnType);
+
+        if (controller.getCompileStack().hasBlockRecorder()) {
+            ClassNode type = operandStack.getTopOperand();
+            int returnValueIdx = controller.getCompileStack().defineTemporaryVariable("returnValue", returnType, true);
+            controller.getCompileStack().applyBlockRecorder();
+            operandStack.load(type, returnValueIdx);
+            controller.getCompileStack().removeVar(returnValueIdx);
+        }
+
+        BytecodeHelper.doReturn(mv, returnType);
+        operandStack.remove(1);
+    }
+
+    public void writeExpressionStatement(ExpressionStatement statement) {
+        controller.getAcg().onLineNumber(statement, "visitExpressionStatement: " + statement.getExpression().getClass().getName());
+        writeStatementLabel(statement);
+
+        Expression expression = statement.getExpression();
+
+        int mark = controller.getOperandStack().getStackLength();
+        expression.visit(controller.getAcg());
+        controller.getOperandStack().popDownTo(mark);
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/TypeChooser.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/TypeChooser.java b/src/main/java/org/codehaus/groovy/classgen/asm/TypeChooser.java
new file mode 100644
index 0000000..ec552b3
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/TypeChooser.java
@@ -0,0 +1,42 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.codehaus.groovy.classgen.asm;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.expr.Expression;
+
+/**
+ * Interface for modules which are capable of resolving the type of an expression.
+ * Several implementations are available, depending on whether you are in a dynamic
+ * or static compilation mode.
+ *
+ * @author Cedric Champeau
+ */
+public interface TypeChooser {
+
+    /**
+     * Resolve the type of an expression. Depending on the implementations, the
+     * returned type may be the declared type or an inferred type.
+     * @param expression the expression for which the type must be returned.
+     * @param classNode the classnode this expression belongs to
+     * @return the resolved type.
+     */
+    ClassNode resolveType(final Expression expression, ClassNode classNode);
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/UnaryExpressionHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/UnaryExpressionHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/UnaryExpressionHelper.java
new file mode 100644
index 0000000..0ebd9b4
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/UnaryExpressionHelper.java
@@ -0,0 +1,87 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.codehaus.groovy.classgen.asm;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.expr.BitwiseNegationExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.NotExpression;
+import org.codehaus.groovy.ast.expr.UnaryMinusExpression;
+import org.codehaus.groovy.ast.expr.UnaryPlusExpression;
+import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
+
+/**
+ * A helper class used to generate bytecode for unary expressions. AST transformations willing to use
+ * a custom unary expression helper may set the {@link WriterControllerFactory} node metadata on a
+ * class node to provide a custom {@link WriterController} which would in turn use a custom expression
+ * helper.
+ *
+ * @see BinaryExpressionHelper
+ *
+ * @author Cedric Champeau
+ */
+public class UnaryExpressionHelper {
+
+    // unary plus, unary minus, bitwise negation
+    static final MethodCaller unaryPlus = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "unaryPlus");
+    static final MethodCaller unaryMinus = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "unaryMinus");
+    static final MethodCaller bitwiseNegate = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "bitwiseNegate");
+
+    private final WriterController controller;
+
+    public UnaryExpressionHelper(final WriterController controller) {
+        this.controller = controller;
+    }
+
+    public void writeUnaryPlus(UnaryPlusExpression expression) {
+        Expression subExpression = expression.getExpression();
+        subExpression.visit(controller.getAcg());
+        controller.getOperandStack().box();
+        unaryPlus.call(controller.getMethodVisitor());
+        controller.getOperandStack().replace(ClassHelper.OBJECT_TYPE);
+        controller.getAssertionWriter().record(expression);
+    }
+
+    public void writeUnaryMinus(UnaryMinusExpression expression) {
+        Expression subExpression = expression.getExpression();
+        subExpression.visit(controller.getAcg());
+        controller.getOperandStack().box();
+        unaryMinus.call(controller.getMethodVisitor());
+        controller.getOperandStack().replace(ClassHelper.OBJECT_TYPE);
+        controller.getAssertionWriter().record(expression);
+    }
+
+    public void writeBitwiseNegate(BitwiseNegationExpression expression) {
+        Expression subExpression = expression.getExpression();
+        subExpression.visit(controller.getAcg());
+        controller.getOperandStack().box();
+        bitwiseNegate.call(controller.getMethodVisitor());
+        controller.getOperandStack().replace(ClassHelper.OBJECT_TYPE);
+        controller.getAssertionWriter().record(expression);
+    }
+
+    public void writeNotExpression(NotExpression expression) {
+        Expression subExpression = expression.getExpression();
+        int mark = controller.getOperandStack().getStackLength();
+        subExpression.visit(controller.getAcg());
+        controller.getOperandStack().castToBool(mark, true);
+        BytecodeHelper.negateBoolean(controller.getMethodVisitor());
+        controller.getAssertionWriter().record(expression);
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/VariableSlotLoader.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/VariableSlotLoader.java b/src/main/java/org/codehaus/groovy/classgen/asm/VariableSlotLoader.java
new file mode 100644
index 0000000..63e48ca
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/VariableSlotLoader.java
@@ -0,0 +1,50 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.codehaus.groovy.classgen.asm;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.classgen.BytecodeExpression;
+import org.objectweb.asm.MethodVisitor;
+
+public class VariableSlotLoader extends BytecodeExpression {
+
+    private final int idx;
+    private final OperandStack operandStack;
+    
+    public VariableSlotLoader(ClassNode type, int index, OperandStack os) {
+        super(type);
+        this.idx = index;
+        this.operandStack = os;
+    }
+    
+    public VariableSlotLoader(int index, OperandStack os) {
+        this.idx = index;
+        this.operandStack = os;
+    }
+    
+    @Override
+    public void visit(MethodVisitor mv) {
+        operandStack.load(this.getType(), idx);
+        operandStack.remove(1);
+    }
+
+    public int getIndex(){
+        return idx;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/WriterController.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/WriterController.java b/src/main/java/org/codehaus/groovy/classgen/asm/WriterController.java
new file mode 100644
index 0000000..952814c
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/WriterController.java
@@ -0,0 +1,401 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.codehaus.groovy.classgen.asm;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.InterfaceHelperClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.classgen.GeneratorContext;
+import org.codehaus.groovy.classgen.asm.indy.IndyBinHelper;
+import org.codehaus.groovy.classgen.asm.indy.IndyCallSiteWriter;
+import org.codehaus.groovy.classgen.asm.indy.InvokeDynamicWriter;
+import org.codehaus.groovy.classgen.asm.util.LoggableClassVisitor;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.SourceUnit;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class WriterController {
+    private static final String GROOVY_LOG_CLASSGEN = "groovy.log.classgen";
+    private static final boolean LOG_CLASSGEN;
+    static {
+        LOG_CLASSGEN = Boolean.valueOf(System.getProperty(GROOVY_LOG_CLASSGEN));
+    }
+    private AsmClassGenerator acg;
+    private MethodVisitor methodVisitor;
+    private CompileStack compileStack;
+    private OperandStack operandStack;
+    private ClassNode classNode;
+    private CallSiteWriter callSiteWriter;
+    private ClassVisitor cv;
+    private ClosureWriter closureWriter;
+    private String internalClassName;
+    private InvocationWriter invocationWriter;
+    private BinaryExpressionHelper binaryExpHelper, fastPathBinaryExpHelper;
+    private UnaryExpressionHelper unaryExpressionHelper, fastPathUnaryExpressionHelper;
+    private AssertionWriter assertionWriter;
+    private String internalBaseClassName;
+    private ClassNode outermostClass;
+    private MethodNode methodNode;
+    private SourceUnit sourceUnit;
+    private ConstructorNode constructorNode;
+    private GeneratorContext context;
+    private InterfaceHelperClassNode interfaceClassLoadingClass;
+    public boolean optimizeForInt = true;
+    private StatementWriter statementWriter;
+    private boolean fastPath = false;
+    private TypeChooser typeChooser;
+    private int bytecodeVersion = Opcodes.V1_5;
+    private int lineNumber = -1;
+    private int helperMethodIndex = 0;
+    private List<String> superMethodNames = new ArrayList<String>();
+
+    public void init(AsmClassGenerator asmClassGenerator, GeneratorContext gcon, ClassVisitor cv, ClassNode cn) {
+        CompilerConfiguration config = cn.getCompileUnit().getConfig();
+        Map<String,Boolean> optOptions = config.getOptimizationOptions();
+        boolean invokedynamic=false;
+        if (optOptions.isEmpty()) {
+            // IGNORE
+        } else if (Boolean.FALSE.equals(optOptions.get("all"))) {
+            optimizeForInt=false;
+            // set other optimizations options to false here
+        } else {
+            if (Boolean.TRUE.equals(optOptions.get(CompilerConfiguration.INVOKEDYNAMIC))) invokedynamic=true;
+            if (Boolean.FALSE.equals(optOptions.get("int"))) optimizeForInt=false;
+            if (invokedynamic) optimizeForInt=false;
+            // set other optimizations options to false here
+        }
+        this.classNode = cn;
+        this.outermostClass = null;
+        this.internalClassName = BytecodeHelper.getClassInternalName(classNode);
+
+        bytecodeVersion = chooseBytecodeVersion(invokedynamic, config.getTargetBytecode());
+
+        if (invokedynamic) {
+            this.invocationWriter = new InvokeDynamicWriter(this);
+            this.callSiteWriter = new IndyCallSiteWriter(this);
+            this.binaryExpHelper = new IndyBinHelper(this);
+        } else {
+            this.callSiteWriter = new CallSiteWriter(this);
+            this.invocationWriter = new InvocationWriter(this);
+            this.binaryExpHelper = new BinaryExpressionHelper(this);
+        }
+
+        this.unaryExpressionHelper = new UnaryExpressionHelper(this);
+        if (optimizeForInt) {
+            this.fastPathBinaryExpHelper = new BinaryExpressionMultiTypeDispatcher(this);
+            // todo: replace with a real fast path unary expression helper when available
+            this.fastPathUnaryExpressionHelper = new UnaryExpressionHelper(this);
+        } else {
+            this.fastPathBinaryExpHelper = this.binaryExpHelper;
+            this.fastPathUnaryExpressionHelper = new UnaryExpressionHelper(this);
+        }
+
+        this.operandStack = new OperandStack(this);
+        this.assertionWriter = new AssertionWriter(this);
+        this.closureWriter = new ClosureWriter(this);
+        this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass());
+        this.acg = asmClassGenerator;
+        this.sourceUnit = acg.getSourceUnit();
+        this.context = gcon;
+        this.compileStack = new CompileStack(this);
+        this.cv = this.createClassVisitor(cv);
+        if (optimizeForInt) {
+            this.statementWriter = new OptimizingStatementWriter(this);
+        } else {
+            this.statementWriter = new StatementWriter(this);
+        }
+        this.typeChooser = new StatementMetaTypeChooser();
+    }
+
+    private ClassVisitor createClassVisitor(ClassVisitor cv) {
+        if (!LOG_CLASSGEN) {
+            return cv;
+        }
+        if (cv instanceof LoggableClassVisitor) {
+            return cv;
+        }
+        return new LoggableClassVisitor(cv);
+    }
+    private static int chooseBytecodeVersion(final boolean invokedynamic, final String targetBytecode) {
+        if (invokedynamic) {
+            if (CompilerConfiguration.JDK8.equals(targetBytecode)) {
+                return Opcodes.V1_8;
+            }
+            return Opcodes.V1_7;
+        } else {
+            Integer bytecodeVersion = CompilerConfiguration.JDK_TO_BYTECODE_VERSION_MAP.get(targetBytecode);
+
+            if (null != bytecodeVersion) {
+                return bytecodeVersion;
+            }
+        }
+
+        throw new GroovyBugError("Bytecode version ["+targetBytecode+"] is not supported by the compiler");
+    }
+
+    public AsmClassGenerator getAcg() {
+        return acg;
+    }
+
+    public void setMethodVisitor(MethodVisitor methodVisitor) {
+        this.methodVisitor = methodVisitor;
+    }
+
+    public MethodVisitor getMethodVisitor() {
+        return methodVisitor;
+    }
+
+    public CompileStack getCompileStack() {
+        return compileStack;
+    }
+
+    public OperandStack getOperandStack() {
+        return operandStack;
+    }
+
+    public ClassNode getClassNode() {
+        return classNode;
+    }
+
+    public CallSiteWriter getCallSiteWriter() {
+        return callSiteWriter;
+    }
+
+    public ClassVisitor getClassVisitor() {
+        return cv;
+    }
+
+    public ClosureWriter getClosureWriter() {
+        return closureWriter;
+    }
+
+    public ClassVisitor getCv() {
+        return cv;
+    }
+
+    public String getInternalClassName() {
+        return internalClassName;
+    }
+
+    public InvocationWriter getInvocationWriter() {
+        return invocationWriter;
+    }
+
+    public BinaryExpressionHelper getBinaryExpressionHelper() {
+        if (fastPath) {
+            return fastPathBinaryExpHelper;
+        } else {
+            return binaryExpHelper;
+        }
+    }
+
+    public UnaryExpressionHelper getUnaryExpressionHelper() {
+        if (fastPath) {
+            return fastPathUnaryExpressionHelper;
+        } else {
+            return unaryExpressionHelper;
+        }
+    }
+
+    public AssertionWriter getAssertionWriter() {
+        return assertionWriter;
+    }
+
+    public TypeChooser getTypeChooser() {
+        return typeChooser;
+    }
+
+    public String getInternalBaseClassName() {
+        return internalBaseClassName;
+    }
+
+    public MethodNode getMethodNode() {
+        return methodNode;
+    }
+
+    public void setMethodNode(MethodNode mn) {
+        methodNode = mn;
+        constructorNode = null;
+    }
+
+    public ConstructorNode getConstructorNode(){
+        return constructorNode;
+    }
+
+    public void setConstructorNode(ConstructorNode cn) {
+        constructorNode = cn;
+        methodNode = null;
+    }
+
+    public boolean isNotClinit() {
+        return methodNode == null || !methodNode.getName().equals("<clinit>");
+    }
+
+    public SourceUnit getSourceUnit() {
+        return sourceUnit;
+    }
+
+    public boolean isStaticContext() {
+        if (compileStack!=null && compileStack.getScope()!=null) {
+            return compileStack.getScope().isInStaticContext();
+        }
+        if (!isInClosure()) return false;
+        if (constructorNode != null) return false;
+        return classNode.isStaticClass() || methodNode.isStatic();
+    }
+
+    public boolean isInClosure() {
+        return classNode.getOuterClass() != null
+                && classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE;
+    }
+
+    public boolean isInClosureConstructor() {
+        return constructorNode != null
+                && classNode.getOuterClass() != null
+                && classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE;
+    }
+
+    public boolean isNotExplicitThisInClosure(boolean implicitThis) {
+        return implicitThis || !isInClosure();
+    }
+
+
+    public boolean isStaticMethod() {
+        return methodNode != null && methodNode.isStatic();
+    }
+
+    public ClassNode getReturnType() {
+        if (methodNode != null) {
+            return methodNode.getReturnType();
+        } else if (constructorNode != null) {
+            return constructorNode.getReturnType();
+        } else {
+            throw new GroovyBugError("I spotted a return that is neither in a method nor in a constructor... I can not handle that");
+        }
+    }
+
+    public boolean isStaticConstructor() {
+        return methodNode != null && methodNode.getName().equals("<clinit>");
+    }
+
+    public boolean isConstructor() {
+        return constructorNode!=null;
+    }
+
+    /**
+     * @return true if we are in a script body, where all variables declared are no longer
+     *         local variables but are properties
+     */
+    public boolean isInScriptBody() {
+        if (classNode.isScriptBody()) {
+            return true;
+        } else {
+            return classNode.isScript() && methodNode != null && methodNode.getName().equals("run");
+        }
+    }
+
+    public String getClassName() {
+        String className;
+        if (!classNode.isInterface() || interfaceClassLoadingClass == null) {
+            className = internalClassName;
+        } else {
+            className = BytecodeHelper.getClassInternalName(interfaceClassLoadingClass);
+        }
+        return className;
+    }
+
+    public ClassNode getOutermostClass() {
+        if (outermostClass == null) {
+            outermostClass = classNode;
+            while (outermostClass instanceof InnerClassNode) {
+                outermostClass = outermostClass.getOuterClass();
+            }
+        }
+        return outermostClass;
+    }
+
+    public GeneratorContext getContext() {
+        return context;
+    }
+
+    public void setInterfaceClassLoadingClass(InterfaceHelperClassNode ihc) {
+        interfaceClassLoadingClass = ihc;
+    }
+
+    public InterfaceHelperClassNode getInterfaceClassLoadingClass() {
+        return interfaceClassLoadingClass;
+    }
+
+    public boolean shouldOptimizeForInt() {
+        return optimizeForInt;
+    }
+
+    public StatementWriter getStatementWriter() {
+        return statementWriter;
+    }
+
+    public void switchToFastPath() {
+        fastPath = true;
+        resetLineNumber();
+    }
+
+    public void switchToSlowPath() {
+        fastPath = false;
+        resetLineNumber();
+    }
+
+    public boolean isFastPath() {
+        return fastPath;
+    }
+
+    public int getBytecodeVersion() {
+        return bytecodeVersion;
+    }
+
+    public int getLineNumber() {
+        return lineNumber;
+    }
+
+    public void setLineNumber(int n) {
+        lineNumber = n;
+    }
+
+    public void resetLineNumber() {
+        setLineNumber(-1);
+    }
+
+    public int getNextHelperMethodIndex() {
+        return helperMethodIndex++;
+    }
+
+    public List<String> getSuperMethodNames() {
+        return superMethodNames;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/WriterControllerFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/WriterControllerFactory.java b/src/main/java/org/codehaus/groovy/classgen/asm/WriterControllerFactory.java
new file mode 100644
index 0000000..a47f309
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/WriterControllerFactory.java
@@ -0,0 +1,27 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.codehaus.groovy.classgen.asm;
+
+/**
+ * A non static factory to get alternative writer controller to be stored in the meta data
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public interface WriterControllerFactory {
+    WriterController makeController(WriterController normalController);
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/indy/IndyBinHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/indy/IndyBinHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/indy/IndyBinHelper.java
new file mode 100644
index 0000000..68ec884
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/indy/IndyBinHelper.java
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.codehaus.groovy.classgen.asm.indy;
+
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.EmptyExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.classgen.asm.BinaryExpressionHelper;
+import org.codehaus.groovy.classgen.asm.InvocationWriter;
+import org.codehaus.groovy.classgen.asm.WriterController;
+
+public class IndyBinHelper extends BinaryExpressionHelper {
+
+    public IndyBinHelper(WriterController wc) {
+        super(wc);
+    }
+
+    @Override
+    protected void writePostOrPrefixMethod(int op, String method, Expression expression, Expression orig) {
+        getController().getInvocationWriter().makeCall(
+                orig, EmptyExpression.INSTANCE, 
+                new ConstantExpression(method), 
+                MethodCallExpression.NO_ARGUMENTS, 
+                InvocationWriter.invokeMethod, 
+                false, false, false);
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/indy/IndyCallSiteWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/indy/IndyCallSiteWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/indy/IndyCallSiteWriter.java
new file mode 100644
index 0000000..d0aac70
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/indy/IndyCallSiteWriter.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.codehaus.groovy.classgen.asm.indy;
+
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.classgen.asm.CallSiteWriter;
+import org.codehaus.groovy.classgen.asm.WriterController;
+
+/**
+ * Dummy class used by the indy implementation.
+ * This class mostly contains empty stubs for calls to the call site writer,
+ * since this class is normally used to prepare call site caching and in indy
+ * call site caching is done by the jvm.
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class IndyCallSiteWriter extends CallSiteWriter {
+    private final WriterController controller;
+    public IndyCallSiteWriter(WriterController controller) {
+        super(controller);
+        this.controller = controller;
+    }
+    
+    @Override
+    public void generateCallSiteArray() {}
+    @Override
+    public void makeCallSite(Expression receiver, String message,
+            Expression arguments, boolean safe, boolean implicitThis,
+            boolean callCurrent, boolean callStatic) {}
+    @Override
+    public void makeSingleArgumentCall(Expression receiver, String message, Expression arguments, boolean safe) {}
+    @Override
+    public void prepareCallSite(String message) {}    
+    @Override
+    public void makeSiteEntry() {}
+    @Override
+    public void makeCallSiteArrayInitializer() {}
+    
+    @Override
+    public void makeGetPropertySite(Expression receiver, String name, boolean safe, boolean implicitThis) {
+        InvokeDynamicWriter idw = (InvokeDynamicWriter)controller.getInvocationWriter();
+        idw.writeGetProperty(receiver, name, safe, implicitThis, false);
+    }
+    @Override
+    public void makeGroovyObjectGetPropertySite(Expression receiver, String name, boolean safe, boolean implicitThis) {
+        InvokeDynamicWriter idw = (InvokeDynamicWriter)controller.getInvocationWriter();
+        idw.writeGetProperty(receiver, name, safe, implicitThis, true);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/indy/InvokeDynamicWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/indy/InvokeDynamicWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/indy/InvokeDynamicWriter.java
new file mode 100644
index 0000000..4e677d9
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/indy/InvokeDynamicWriter.java
@@ -0,0 +1,234 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.codehaus.groovy.classgen.asm.indy;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.CastExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.EmptyExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.tools.WideningCategories;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.classgen.asm.BytecodeHelper;
+import org.codehaus.groovy.classgen.asm.CompileStack;
+import org.codehaus.groovy.classgen.asm.InvocationWriter;
+import org.codehaus.groovy.classgen.asm.MethodCallerMultiAdapter;
+import org.codehaus.groovy.classgen.asm.OperandStack;
+import org.codehaus.groovy.classgen.asm.WriterController;
+import org.codehaus.groovy.runtime.wrappers.Wrapper;
+import org.codehaus.groovy.vmplugin.v7.IndyInterface;
+import org.objectweb.asm.Handle;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+
+import static org.codehaus.groovy.classgen.asm.BytecodeHelper.getTypeDescription;
+import static org.codehaus.groovy.vmplugin.v7.IndyInterface.CALL_TYPES.CAST;
+import static org.codehaus.groovy.vmplugin.v7.IndyInterface.CALL_TYPES.GET;
+import static org.codehaus.groovy.vmplugin.v7.IndyInterface.CALL_TYPES.INIT;
+import static org.codehaus.groovy.vmplugin.v7.IndyInterface.CALL_TYPES.METHOD;
+import static org.codehaus.groovy.vmplugin.v7.IndyInterface.GROOVY_OBJECT;
+import static org.codehaus.groovy.vmplugin.v7.IndyInterface.IMPLICIT_THIS;
+import static org.codehaus.groovy.vmplugin.v7.IndyInterface.SAFE_NAVIGATION;
+import static org.codehaus.groovy.vmplugin.v7.IndyInterface.SPREAD_CALL;
+import static org.codehaus.groovy.vmplugin.v7.IndyInterface.THIS_CALL;
+import static org.objectweb.asm.Opcodes.H_INVOKESTATIC;
+
+/**
+ * This Writer is used to generate the call invocation byte codes
+ * for usage by invokedynamic.
+ * 
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class InvokeDynamicWriter extends InvocationWriter {
+    
+    
+    private static final String INDY_INTERFACE_NAME = IndyInterface.class.getName().replace('.', '/');
+    private static final String BSM_METHOD_TYPE_DESCRIPTOR = 
+        MethodType.methodType(
+                CallSite.class, Lookup.class, String.class, MethodType.class,
+                String.class, int.class
+        ).toMethodDescriptorString();
+    private static final Handle BSM = 
+        new Handle(
+                H_INVOKESTATIC,
+                INDY_INTERFACE_NAME,
+                "bootstrap",
+                BSM_METHOD_TYPE_DESCRIPTOR);
+
+    private final WriterController controller;
+
+    public InvokeDynamicWriter(WriterController wc) {
+        super(wc);
+        this.controller = wc;
+    }
+
+    @Override
+    protected boolean makeCachedCall(Expression origin, ClassExpression sender,
+            Expression receiver, Expression message, Expression arguments,
+            MethodCallerMultiAdapter adapter, boolean safe, boolean spreadSafe,
+            boolean implicitThis, boolean containsSpreadExpression
+    ) {
+        // fixed number of arguments && name is a real String and no GString
+        if ((adapter == null || adapter == invokeMethod || adapter == invokeMethodOnCurrent || adapter == invokeStaticMethod) && !spreadSafe) {
+            String methodName = getMethodName(message);
+            if (methodName != null) {
+                makeIndyCall(adapter, receiver, implicitThis, safe, methodName, arguments);
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    private String prepareIndyCall(Expression receiver, boolean implicitThis) {
+        CompileStack compileStack = controller.getCompileStack();
+        OperandStack operandStack = controller.getOperandStack();
+
+        compileStack.pushLHS(false);
+        
+        // load normal receiver as first argument
+        compileStack.pushImplicitThis(implicitThis);
+        receiver.visit(controller.getAcg());
+        compileStack.popImplicitThis();
+        return "("+getTypeDescription(operandStack.getTopOperand());
+    }
+    
+    private void finishIndyCall(Handle bsmHandle, String methodName, String sig, int numberOfArguments, Object... bsmArgs) {
+        CompileStack compileStack = controller.getCompileStack();
+        OperandStack operandStack = controller.getOperandStack();
+
+        controller.getMethodVisitor().visitInvokeDynamicInsn(methodName, sig, bsmHandle, bsmArgs);
+
+        operandStack.replace(ClassHelper.OBJECT_TYPE, numberOfArguments);
+        compileStack.popLHS();
+    }
+    
+    private void makeIndyCall(MethodCallerMultiAdapter adapter, Expression receiver, boolean implicitThis, boolean safe, String methodName, Expression arguments) {
+        OperandStack operandStack = controller.getOperandStack();
+        
+        StringBuilder sig = new StringBuilder(prepareIndyCall(receiver, implicitThis));
+        
+        // load arguments
+        int numberOfArguments = 1;
+        ArgumentListExpression ae = makeArgumentList(arguments);
+        boolean containsSpreadExpression = AsmClassGenerator.containsSpreadExpression(arguments);
+        if (containsSpreadExpression) {
+            controller.getAcg().despreadList(ae.getExpressions(), true);
+            sig.append(getTypeDescription(Object[].class));
+        } else {
+            for (Expression arg : ae.getExpressions()) {
+                arg.visit(controller.getAcg());
+                if (arg instanceof CastExpression) {
+                    operandStack.box();
+                    controller.getAcg().loadWrapper(arg);
+                    sig.append(getTypeDescription(Wrapper.class));
+                } else {
+                    sig.append(getTypeDescription(operandStack.getTopOperand()));
+                }
+                numberOfArguments++;
+            }
+        }
+
+        sig.append(")Ljava/lang/Object;");
+        String callSiteName = METHOD.getCallSiteName();
+        if (adapter==null) callSiteName = INIT.getCallSiteName();
+        int flags = getMethodCallFlags(adapter, safe, containsSpreadExpression);
+        finishIndyCall(BSM, callSiteName, sig.toString(), numberOfArguments, methodName, flags);
+    }
+    
+    private static int getMethodCallFlags(MethodCallerMultiAdapter adapter, boolean safe, boolean spread) {
+        int ret = 0;
+        if (safe)                           ret |= SAFE_NAVIGATION;
+        if (adapter==invokeMethodOnCurrent) ret |= THIS_CALL;
+        if (spread)                         ret |= SPREAD_CALL;
+        return ret;
+    }
+
+    @Override
+    public void makeSingleArgumentCall(Expression receiver, String message, Expression arguments, boolean safe) {
+        makeIndyCall(invokeMethod, receiver, false, safe, message, arguments);
+    }
+
+    private static int getPropertyFlags(boolean safe, boolean implicitThis, boolean groovyObject) {
+        int flags = 0;
+        if (implicitThis)   flags |= IMPLICIT_THIS;
+        if (groovyObject)   flags |= GROOVY_OBJECT;
+        if (safe)           flags |= SAFE_NAVIGATION;
+        return flags;
+    }
+
+    protected void writeGetProperty(Expression receiver, String propertyName, boolean safe, boolean implicitThis, boolean groovyObject) {
+        String sig = prepareIndyCall(receiver, implicitThis);
+        sig += ")Ljava/lang/Object;";
+        int flags = getPropertyFlags(safe,implicitThis,groovyObject);
+        finishIndyCall(BSM, GET.getCallSiteName(), sig, 1, propertyName, flags);
+    }
+    
+    @Override
+    protected void writeNormalConstructorCall(ConstructorCallExpression call) {
+        makeCall(call, new ClassExpression(call.getType()), new ConstantExpression("<init>"), call.getArguments(), null, false, false, false);
+    }
+    
+    @Override
+    public void coerce(ClassNode from, ClassNode target) {
+        ClassNode wrapper = ClassHelper.getWrapper(target);
+        makeIndyCall(invokeMethod, EmptyExpression.INSTANCE, false, false, "asType", new ClassExpression(wrapper));
+        if (ClassHelper.boolean_TYPE.equals(target) || ClassHelper.Boolean_TYPE.equals(target)) {
+            writeIndyCast(ClassHelper.OBJECT_TYPE,target);
+        } else {
+            BytecodeHelper.doCast(controller.getMethodVisitor(), wrapper);
+            controller.getOperandStack().replace(wrapper);
+            controller.getOperandStack().doGroovyCast(target);
+        }
+    }
+
+    @Override
+    public void castToNonPrimitiveIfNecessary(ClassNode sourceType, ClassNode targetType) {
+        ClassNode boxedType = ClassHelper.getWrapper(sourceType);
+        if (WideningCategories.implementsInterfaceOrSubclassOf(boxedType, targetType)) {
+            controller.getOperandStack().box();
+            return;
+        }
+        writeIndyCast(sourceType, targetType);
+    }
+
+    private void writeIndyCast(ClassNode sourceType, ClassNode targetType) {
+        StringBuilder sig = new StringBuilder();
+        sig.append('(');
+        sig.append(getTypeDescription(sourceType));
+        sig.append(')');
+        sig.append(getTypeDescription(targetType));
+
+        controller.getMethodVisitor().visitInvokeDynamicInsn(
+                //TODO: maybe use a different bootstrap method since no arguments are needed here
+                CAST.getCallSiteName(), sig.toString(), BSM, "()", 0);
+        controller.getOperandStack().replace(targetType);
+    }
+
+    @Override
+    public void castNonPrimitiveToBool(ClassNode sourceType) {
+        writeIndyCast(sourceType, ClassHelper.boolean_TYPE);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/indy/sc/IndyStaticTypesMultiTypeDispatcher.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/indy/sc/IndyStaticTypesMultiTypeDispatcher.java b/src/main/java/org/codehaus/groovy/classgen/asm/indy/sc/IndyStaticTypesMultiTypeDispatcher.java
new file mode 100644
index 0000000..ebcf2a8
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/indy/sc/IndyStaticTypesMultiTypeDispatcher.java
@@ -0,0 +1,94 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.codehaus.groovy.classgen.asm.indy.sc;
+
+import org.codehaus.groovy.classgen.asm.BinaryExpressionWriter;
+import org.codehaus.groovy.classgen.asm.MethodCaller;
+import org.codehaus.groovy.classgen.asm.WriterController;
+import org.codehaus.groovy.classgen.asm.sc.StaticTypesBinaryExpressionMultiTypeDispatcher;
+import org.codehaus.groovy.vmplugin.v7.IndyInterface;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+/**
+ * Multi type dispatcher for binary expression backend combining indy and static compilation
+ * @author Jochen Theodorou
+ * @since 2.5.0
+ */
+public class IndyStaticTypesMultiTypeDispatcher extends StaticTypesBinaryExpressionMultiTypeDispatcher {
+    public IndyStaticTypesMultiTypeDispatcher(WriterController wc) {
+        super(wc);
+
+    }
+
+    private static final String INDY_INTERFACE_NAME = IndyInterface.class.getName().replace('.', '/');
+    private static final String BSM_METHOD_TYPE_DESCRIPTOR =
+            MethodType.methodType(
+                    CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class
+            ).toMethodDescriptorString();
+    private static final Handle BSM =
+            new Handle(
+                    H_INVOKESTATIC,
+                    INDY_INTERFACE_NAME,
+                    "staticArrayAccess",
+                    BSM_METHOD_TYPE_DESCRIPTOR);
+    private static class GenericArrayAccess extends MethodCaller {
+        private final String name, signature;
+        public GenericArrayAccess(String name, String signature) {
+            this.name = name;
+            this.signature = signature;
+        }
+        @Override public void call(MethodVisitor mv) {
+            mv.visitInvokeDynamicInsn(name, signature, BSM);
+        }
+    }
+
+    protected BinaryExpressionWriter[] initializeDelegateHelpers() {
+        BinaryExpressionWriter[] bewArray = super.initializeDelegateHelpers();
+        /* 1: int    */
+        bewArray[1].setArraySetAndGet(  new GenericArrayAccess("set","([III)V"),
+                                        new GenericArrayAccess("get","([II)I"));
+        /* 2: long   */
+        bewArray[2].setArraySetAndGet(  new GenericArrayAccess("set","([JIJ)V"),
+                                        new GenericArrayAccess("get","([JI)J"));
+        /* 3: double */
+        bewArray[3].setArraySetAndGet(  new GenericArrayAccess("set","([DID)V"),
+                                        new GenericArrayAccess("get","([DI)D"));
+        /* 4: char   */
+        bewArray[4].setArraySetAndGet(  new GenericArrayAccess("set","([CIC)V"),
+                                        new GenericArrayAccess("get","([CI)C"));
+        /* 5: byte   */
+        bewArray[5].setArraySetAndGet(  new GenericArrayAccess("set","([BIB)V"),
+                                        new GenericArrayAccess("get","([BI)B"));
+        /* 6: short  */
+        bewArray[6].setArraySetAndGet(  new GenericArrayAccess("set","([SIS)V"),
+                                        new GenericArrayAccess("get","([SI)S"));
+        /* 7: float  */
+        bewArray[7].setArraySetAndGet(  new GenericArrayAccess("get","([FIF)V"),
+                                        new GenericArrayAccess("set","([FI)F"));
+        /* 8: bool   */
+        bewArray[8].setArraySetAndGet(  new GenericArrayAccess("get","([ZIZ)V"),
+                                        new GenericArrayAccess("set","([ZI)Z"));
+        return bewArray;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/package.html
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/package.html b/src/main/java/org/codehaus/groovy/classgen/asm/package.html
new file mode 100644
index 0000000..cd40f7e
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/package.html
@@ -0,0 +1,29 @@
+<!--
+
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you 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.
+
+-->
+<html>
+  <head>
+    <title>package org.codehaus.groovy.classgen.asm.*</title>
+  </head>
+  <body>
+    <p>Helper classes for ASMClassGenerator. All classes in this package 
+    are for internal usage only.</p>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticCompilationMopWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticCompilationMopWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticCompilationMopWriter.java
new file mode 100644
index 0000000..5a09829
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticCompilationMopWriter.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.codehaus.groovy.classgen.asm.sc;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.classgen.asm.MopWriter;
+import org.codehaus.groovy.classgen.asm.WriterController;
+import org.codehaus.groovy.transform.stc.StaticTypesMarker;
+
+import java.util.LinkedList;
+
+/**
+ * A MOP Writer that skips the generation of MOP methods. This writer is used
+ * when a class is *fully* statically compiled. In mixed mode, MOP methods are
+ * still generated.
+ *
+ * @author Cédric Champeau
+ * @since 2.4.0
+ */
+public class StaticCompilationMopWriter extends MopWriter {
+
+    public static final MopWriter.Factory FACTORY = new MopWriter.Factory() {
+        @Override
+        public MopWriter create(final WriterController controller) {
+            return new StaticCompilationMopWriter(controller);
+        }
+    };
+
+    private final StaticTypesWriterController controller;
+
+    public StaticCompilationMopWriter(final WriterController wc) {
+        super(wc);
+        this.controller = (StaticTypesWriterController) wc;
+    }
+
+
+    public void createMopMethods() {
+        ClassNode classNode = controller.getClassNode();
+        LinkedList<MethodNode> requiredMopMethods = classNode.getNodeMetaData(StaticTypesMarker.SUPER_MOP_METHOD_REQUIRED);
+        if (requiredMopMethods!=null) {
+            generateMopCalls(requiredMopMethods, false);
+        }
+    }
+
+}