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);
+ }
+ }
+
+}