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:33 UTC
[22/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/CallSiteWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/CallSiteWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/CallSiteWriter.java
new file mode 100644
index 0000000..716f97e
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/CallSiteWriter.java
@@ -0,0 +1,387 @@
+/*
+ * 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.InterfaceHelperClassNode;
+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.Expression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.runtime.callsite.CallSite;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.objectweb.asm.Opcodes.AALOAD;
+import static org.objectweb.asm.Opcodes.AASTORE;
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
+import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
+import static org.objectweb.asm.Opcodes.ACONST_NULL;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.ANEWARRAY;
+import static org.objectweb.asm.Opcodes.ARETURN;
+import static org.objectweb.asm.Opcodes.ASTORE;
+import static org.objectweb.asm.Opcodes.CHECKCAST;
+import static org.objectweb.asm.Opcodes.DUP;
+import static org.objectweb.asm.Opcodes.GETFIELD;
+import static org.objectweb.asm.Opcodes.GETSTATIC;
+import static org.objectweb.asm.Opcodes.IFNONNULL;
+import static org.objectweb.asm.Opcodes.IFNULL;
+import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+import static org.objectweb.asm.Opcodes.NEW;
+import static org.objectweb.asm.Opcodes.PUTSTATIC;
+import static org.objectweb.asm.Opcodes.RETURN;
+
+/**
+ * This class represents non public API used by AsmClassGenerator. Don't
+ * use this class in your code
+ */
+public class CallSiteWriter {
+
+ private static String [] sig = new String [255];
+ private static String getCreateArraySignature(int numberOfArguments) {
+ if (sig[numberOfArguments] == null) {
+ StringBuilder sb = new StringBuilder("(");
+ for (int i = 0; i != numberOfArguments; ++i) {
+ sb.append("Ljava/lang/Object;");
+ }
+ sb.append(")[Ljava/lang/Object;");
+ sig[numberOfArguments] = sb.toString();
+ }
+ return sig[numberOfArguments];
+ }
+ private static final int
+ MOD_PRIVSS = ACC_PRIVATE+ACC_STATIC+ACC_SYNTHETIC,
+ MOD_PUBSS = ACC_PUBLIC+ACC_STATIC+ACC_SYNTHETIC;
+ private static final ClassNode CALLSITE_ARRAY_NODE = ClassHelper.make(CallSite[].class);
+ private static final String
+ GET_CALLSITE_METHOD = "$getCallSiteArray",
+ CALLSITE_CLASS = "org/codehaus/groovy/runtime/callsite/CallSite",
+ CALLSITE_DESC = "[Lorg/codehaus/groovy/runtime/callsite/CallSite;",
+ GET_CALLSITE_DESC = "()"+CALLSITE_DESC,
+ CALLSITE_ARRAY_CLASS = "org/codehaus/groovy/runtime/callsite/CallSiteArray",
+ GET_CALLSITEARRAY_DESC = "()Lorg/codehaus/groovy/runtime/callsite/CallSiteArray;",
+ CALLSITE_FIELD = "$callSiteArray",
+ REF_CLASS = "java/lang/ref/SoftReference",
+ REF_DESC = "L"+REF_CLASS+";",
+ METHOD_OO_DESC = "(Ljava/lang/Object;)Ljava/lang/Object;",
+ CREATE_CSA_METHOD = "$createCallSiteArray";
+ public static final String CONSTRUCTOR = "<$constructor$>";
+
+ private final List callSites = new ArrayList(32);
+ private int callSiteArrayVarIndex = -1;
+ private final WriterController controller;
+
+ public CallSiteWriter(WriterController wc) {
+ this.controller = wc;
+ ClassNode node = controller.getClassNode();
+ if(node instanceof InterfaceHelperClassNode) {
+ InterfaceHelperClassNode ihcn = (InterfaceHelperClassNode) node;
+ callSites.addAll(ihcn.getCallSites());
+ }
+ }
+
+ public void makeSiteEntry() {
+ if (controller.isNotClinit()) {
+ controller.getMethodVisitor().visitMethodInsn(INVOKESTATIC, controller.getInternalClassName(), GET_CALLSITE_METHOD, GET_CALLSITE_DESC, false);
+ controller.getOperandStack().push(CALLSITE_ARRAY_NODE);
+ callSiteArrayVarIndex = controller.getCompileStack().defineTemporaryVariable("$local$callSiteArray", CALLSITE_ARRAY_NODE, true);
+ }
+ }
+
+ public void generateCallSiteArray() {
+ if (!controller.getClassNode().isInterface()) {
+ controller.getClassVisitor().visitField(MOD_PRIVSS, CALLSITE_FIELD, REF_DESC, null, null);
+ generateCreateCallSiteArray();
+ generateGetCallSiteArray();
+ }
+ }
+
+ private void generateGetCallSiteArray() {
+ int visibility = (controller.getClassNode() instanceof InterfaceHelperClassNode) ? MOD_PUBSS : MOD_PRIVSS;
+ MethodVisitor mv = controller.getClassVisitor().visitMethod(visibility, GET_CALLSITE_METHOD, GET_CALLSITE_DESC, null, null);
+ controller.setMethodVisitor(mv);
+ mv.visitCode();
+ mv.visitFieldInsn(GETSTATIC, controller.getInternalClassName(), "$callSiteArray", "Ljava/lang/ref/SoftReference;");
+ Label l0 = new Label();
+ mv.visitJumpInsn(IFNULL, l0);
+ mv.visitFieldInsn(GETSTATIC, controller.getInternalClassName(), "$callSiteArray", "Ljava/lang/ref/SoftReference;");
+ mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ref/SoftReference", "get", "()Ljava/lang/Object;", false);
+ mv.visitTypeInsn(CHECKCAST, "org/codehaus/groovy/runtime/callsite/CallSiteArray");
+ mv.visitInsn(DUP);
+ mv.visitVarInsn(ASTORE, 0);
+ Label l1 = new Label();
+ mv.visitJumpInsn(IFNONNULL, l1);
+ mv.visitLabel(l0);
+ mv.visitMethodInsn(INVOKESTATIC, controller.getInternalClassName(), "$createCallSiteArray", "()Lorg/codehaus/groovy/runtime/callsite/CallSiteArray;", false);
+ mv.visitVarInsn(ASTORE, 0);
+ mv.visitTypeInsn(NEW, "java/lang/ref/SoftReference");
+ mv.visitInsn(DUP);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/ref/SoftReference", "<init>", "(Ljava/lang/Object;)V", false);
+ mv.visitFieldInsn(PUTSTATIC, controller.getInternalClassName(), "$callSiteArray", "Ljava/lang/ref/SoftReference;");
+ mv.visitLabel(l1);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, "org/codehaus/groovy/runtime/callsite/CallSiteArray", "array", "[Lorg/codehaus/groovy/runtime/callsite/CallSite;");
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ private void generateCreateCallSiteArray() {
+ List<String> callSiteInitMethods = new LinkedList<String>();
+ int index = 0;
+ int methodIndex = 0;
+ final int size = callSites.size();
+ final int maxArrayInit = 5000;
+ // create array initialization methods
+ while (index < size) {
+ methodIndex++;
+ String methodName = "$createCallSiteArray_" + methodIndex;
+ callSiteInitMethods.add(methodName);
+ MethodVisitor mv = controller.getClassVisitor().visitMethod(MOD_PRIVSS, methodName, "([Ljava/lang/String;)V", null, null);
+ controller.setMethodVisitor(mv);
+ mv.visitCode();
+ int methodLimit = size;
+ // check if the next block is over the max allowed
+ if ((methodLimit - index) > maxArrayInit) {
+ methodLimit = index + maxArrayInit;
+ }
+ for (; index < methodLimit; index++) {
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitLdcInsn(index);
+ mv.visitLdcInsn(callSites.get(index));
+ mv.visitInsn(AASTORE);
+ }
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(2,1);
+ mv.visitEnd();
+ }
+ // create base createCallSiteArray method
+ MethodVisitor mv = controller.getClassVisitor().visitMethod(MOD_PRIVSS, CREATE_CSA_METHOD, GET_CALLSITEARRAY_DESC, null, null);
+ controller.setMethodVisitor(mv);
+ mv.visitCode();
+ mv.visitLdcInsn(size);
+ mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
+ mv.visitVarInsn(ASTORE, 0);
+ for (String methodName : callSiteInitMethods) {
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESTATIC, controller.getInternalClassName(), methodName, "([Ljava/lang/String;)V", false);
+ }
+
+ mv.visitTypeInsn(NEW, CALLSITE_ARRAY_CLASS);
+ mv.visitInsn(DUP);
+ controller.getAcg().visitClassExpression(new ClassExpression(controller.getClassNode()));
+
+ mv.visitVarInsn(ALOAD, 0);
+
+ mv.visitMethodInsn(INVOKESPECIAL, CALLSITE_ARRAY_CLASS, "<init>", "(Ljava/lang/Class;[Ljava/lang/String;)V", false);
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(0,0);
+ mv.visitEnd();
+ }
+
+ private int allocateIndex(String name) {
+ callSites.add(name);
+ return callSites.size()-1;
+ }
+
+ private void invokeSafe(boolean safe, String unsafeMethod, String safeMethod) {
+ String method = unsafeMethod;
+ if (safe) method = safeMethod;
+ controller.getMethodVisitor().visitMethodInsn(INVOKEINTERFACE, CALLSITE_CLASS, method, METHOD_OO_DESC, true);
+ controller.getOperandStack().replace(ClassHelper.OBJECT_TYPE);
+ }
+
+ public void prepareCallSite(String message) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ if (controller.isNotClinit()) {
+ mv.visitVarInsn(ALOAD, callSiteArrayVarIndex);
+ } else {
+ mv.visitMethodInsn(INVOKESTATIC, controller.getClassName(), GET_CALLSITE_METHOD, GET_CALLSITE_DESC, false);
+ }
+ final int index = allocateIndex(message);
+ mv.visitLdcInsn(index);
+ mv.visitInsn(AALOAD);
+ }
+
+ private void prepareSiteAndReceiver(Expression receiver, String methodName, boolean implicitThis) {
+ prepareSiteAndReceiver(receiver, methodName, implicitThis, false);
+ }
+
+ protected void prepareSiteAndReceiver(Expression receiver, String methodName, boolean implicitThis, boolean lhs) {
+ //site
+ prepareCallSite(methodName);
+
+ // receiver
+ CompileStack compileStack = controller.getCompileStack();
+ compileStack.pushImplicitThis(implicitThis);
+ compileStack.pushLHS(lhs);
+ receiver.visit(controller.getAcg());
+ controller.getOperandStack().box();
+ compileStack.popLHS();
+ compileStack.popImplicitThis();
+ }
+
+ protected void visitBoxedArgument(Expression exp) {
+ exp.visit(controller.getAcg());
+ if (!(exp instanceof TupleExpression)) {
+ // we are not in a tuple, so boxing might be missing for
+ // this single argument call
+ controller.getOperandStack().box();
+ }
+ }
+
+ public final void makeSingleArgumentCall(Expression receiver, String message, Expression arguments) {
+ makeSingleArgumentCall(receiver, message, arguments, false);
+ }
+
+ public void makeSingleArgumentCall(Expression receiver, String message, Expression arguments, boolean safe) {
+ OperandStack operandStack = controller.getOperandStack();
+ int m1 = operandStack.getStackLength();
+ //slow Path
+ prepareSiteAndReceiver(receiver, message, false, controller.getCompileStack().isLHS());
+ visitBoxedArgument(arguments);
+ int m2 = operandStack.getStackLength();
+ controller.getMethodVisitor().visitMethodInsn(INVOKEINTERFACE, "org/codehaus/groovy/runtime/callsite/CallSite", safe ? "callSafe" : "call", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true);
+ operandStack.replace(ClassHelper.OBJECT_TYPE, m2-m1);
+ }
+
+ public void makeGroovyObjectGetPropertySite(Expression receiver, String methodName, boolean safe, boolean implicitThis) {
+ prepareSiteAndReceiver(receiver, methodName, implicitThis);
+ invokeSafe(safe, "callGroovyObjectGetProperty", "callGroovyObjectGetPropertySafe");
+ }
+
+ public void makeGetPropertySite(Expression receiver, String methodName, boolean safe, boolean implicitThis) {
+ prepareSiteAndReceiver(receiver, methodName, implicitThis);
+ invokeSafe(safe, "callGetProperty", "callGetPropertySafe");
+ }
+
+ public void makeCallSite(Expression receiver, String message, Expression arguments, boolean safe, boolean implicitThis, boolean callCurrent, boolean callStatic) {
+ prepareSiteAndReceiver(receiver, message, implicitThis);
+
+ CompileStack compileStack = controller.getCompileStack();
+ compileStack.pushImplicitThis(implicitThis);
+ compileStack.pushLHS(false);
+ boolean constructor = message.equals(CONSTRUCTOR);
+ OperandStack operandStack = controller.getOperandStack();
+
+ // arguments
+ boolean containsSpreadExpression = AsmClassGenerator.containsSpreadExpression(arguments);
+ int numberOfArguments = containsSpreadExpression ? -1 : AsmClassGenerator.argumentSize(arguments);
+ int operandsToReplace = 1;
+ if (numberOfArguments > MethodCallerMultiAdapter.MAX_ARGS || containsSpreadExpression) {
+ ArgumentListExpression ae;
+ if (arguments instanceof ArgumentListExpression) {
+ ae = (ArgumentListExpression) arguments;
+ } else if (arguments instanceof TupleExpression) {
+ TupleExpression te = (TupleExpression) arguments;
+ ae = new ArgumentListExpression(te.getExpressions());
+ } else {
+ ae = new ArgumentListExpression();
+ ae.addExpression(arguments);
+ }
+ controller.getCompileStack().pushImplicitThis(false);
+ if (containsSpreadExpression) {
+ numberOfArguments = -1;
+ controller.getAcg().despreadList(ae.getExpressions(), true);
+ } else {
+ numberOfArguments = ae.getExpressions().size();
+ for (int i = 0; i < numberOfArguments; i++) {
+ Expression argument = ae.getExpression(i);
+ argument.visit(controller.getAcg());
+ operandStack.box();
+ if (argument instanceof CastExpression) controller.getAcg().loadWrapper(argument);
+ }
+ operandsToReplace += numberOfArguments;
+ }
+ controller.getCompileStack().popImplicitThis();
+ }
+ controller.getCompileStack().popLHS();
+ controller.getCompileStack().popImplicitThis();
+
+ MethodVisitor mv = controller.getMethodVisitor();
+
+ if (numberOfArguments > 4) {
+ final String createArraySignature = getCreateArraySignature(numberOfArguments);
+ mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/ArrayUtil", "createArray", createArraySignature, false);
+ //TODO: use pre-generated Object[]
+ operandStack.replace(ClassHelper.OBJECT_TYPE.makeArray(),numberOfArguments);
+ operandsToReplace = operandsToReplace-numberOfArguments+1;
+ }
+
+ final String desc = getDescForParamNum(numberOfArguments);
+ if (callStatic) {
+ mv.visitMethodInsn(INVOKEINTERFACE, CALLSITE_CLASS, "callStatic", "(Ljava/lang/Class;" + desc, true);
+ } else if (constructor) {
+ mv.visitMethodInsn(INVOKEINTERFACE, CALLSITE_CLASS, "callConstructor", "(Ljava/lang/Object;" + desc, true);
+ } else if (callCurrent) {
+ mv.visitMethodInsn(INVOKEINTERFACE, CALLSITE_CLASS, "callCurrent", "(Lgroovy/lang/GroovyObject;" + desc, true);
+ } else if (safe) {
+ mv.visitMethodInsn(INVOKEINTERFACE, CALLSITE_CLASS, "callSafe", "(Ljava/lang/Object;" + desc, true);
+ } else {
+ mv.visitMethodInsn(INVOKEINTERFACE, CALLSITE_CLASS, "call", "(Ljava/lang/Object;" + desc, true);
+ }
+ operandStack.replace(ClassHelper.OBJECT_TYPE,operandsToReplace);
+ }
+
+ private static String getDescForParamNum(int numberOfArguments) {
+ switch (numberOfArguments) {
+ case 0:
+ return ")Ljava/lang/Object;";
+ case 1:
+ return "Ljava/lang/Object;)Ljava/lang/Object;";
+ case 2:
+ return "Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;";
+ case 3:
+ return "Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;";
+ case 4:
+ return "Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;";
+ default:
+ return "[Ljava/lang/Object;)Ljava/lang/Object;";
+ }
+ }
+
+ public List<String> getCallSites() {
+ return callSites;
+ }
+
+ public void makeCallSiteArrayInitializer() {
+ final String classInternalName = BytecodeHelper.getClassInternalName(controller.getClassNode());
+ MethodVisitor mv = controller.getMethodVisitor();
+ mv.visitInsn(ACONST_NULL);
+ mv.visitFieldInsn(PUTSTATIC, classInternalName, "$callSiteArray", "Ljava/lang/ref/SoftReference;");
+ }
+
+ public boolean hasCallSiteUse() {
+ return callSiteArrayVarIndex>=0;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
new file mode 100644
index 0000000..af305ba
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
@@ -0,0 +1,392 @@
+/*
+ * 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.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.VariableScope;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.FieldExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.classgen.Verifier;
+import org.objectweb.asm.MethodVisitor;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
+import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.DUP;
+import static org.objectweb.asm.Opcodes.GETFIELD;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+import static org.objectweb.asm.Opcodes.NEW;
+
+public class ClosureWriter {
+
+ protected interface UseExistingReference {}
+
+ private final Map<Expression,ClassNode> closureClassMap;
+ private final WriterController controller;
+ private final WriterControllerFactory factory;
+
+ public ClosureWriter(WriterController wc) {
+ this.controller = wc;
+ closureClassMap = new HashMap<Expression,ClassNode>();
+ factory = new WriterControllerFactory() {
+ public WriterController makeController(final WriterController normalController) {
+ return controller;
+ }
+ };
+ }
+
+ public void writeClosure(ClosureExpression expression) {
+ CompileStack compileStack = controller.getCompileStack();
+ MethodVisitor mv = controller.getMethodVisitor();
+ ClassNode classNode = controller.getClassNode();
+ AsmClassGenerator acg = controller.getAcg();
+
+ // generate closure as public class to make sure it can be properly invoked by classes of the
+ // Groovy runtime without circumventing JVM access checks (see CachedMethod for example).
+ int mods = ACC_PUBLIC;
+ if (classNode.isInterface()) {
+ mods |= ACC_STATIC;
+ }
+ ClassNode closureClass = getOrAddClosureClass(expression, mods);
+ String closureClassinternalName = BytecodeHelper.getClassInternalName(closureClass);
+ List constructors = closureClass.getDeclaredConstructors();
+ ConstructorNode node = (ConstructorNode) constructors.get(0);
+
+ Parameter[] localVariableParams = node.getParameters();
+
+ mv.visitTypeInsn(NEW, closureClassinternalName);
+ mv.visitInsn(DUP);
+ if (controller.isStaticMethod() || compileStack.isInSpecialConstructorCall()) {
+ (new ClassExpression(classNode)).visit(acg);
+ (new ClassExpression(controller.getOutermostClass())).visit(acg);
+ } else {
+ mv.visitVarInsn(ALOAD, 0);
+ controller.getOperandStack().push(ClassHelper.OBJECT_TYPE);
+ loadThis();
+ }
+
+ // now let's load the various parameters we're passing
+ // we start at index 2 because the first variable we pass
+ // is the owner instance and at this point it is already
+ // on the stack
+ for (int i = 2; i < localVariableParams.length; i++) {
+ Parameter param = localVariableParams[i];
+ String name = param.getName();
+ loadReference(name, controller);
+ if (param.getNodeMetaData(ClosureWriter.UseExistingReference.class)==null) {
+ param.setNodeMetaData(ClosureWriter.UseExistingReference.class,Boolean.TRUE);
+ }
+ }
+
+ // we may need to pass in some other constructors
+ //cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", prototype + ")V");
+ mv.visitMethodInsn(INVOKESPECIAL, closureClassinternalName, "<init>", BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, localVariableParams), false);
+ controller.getOperandStack().replace(ClassHelper.CLOSURE_TYPE, localVariableParams.length);
+ }
+
+ public static void loadReference(String name, WriterController controller) {
+ CompileStack compileStack = controller.getCompileStack();
+ MethodVisitor mv = controller.getMethodVisitor();
+ ClassNode classNode = controller.getClassNode();
+ AsmClassGenerator acg = controller.getAcg();
+
+ // compileStack.containsVariable(name) means to ask if the variable is already declared
+ // compileStack.getScope().isReferencedClassVariable(name) means to ask if the variable is a field
+ // If it is no field and is not yet declared, then it is either a closure shared variable or
+ // an already declared variable.
+ if (!compileStack.containsVariable(name) && compileStack.getScope().isReferencedClassVariable(name)) {
+ acg.visitFieldExpression(new FieldExpression(classNode.getDeclaredField(name)));
+ } else {
+ BytecodeVariable v = compileStack.getVariable(name, !classNodeUsesReferences(controller.getClassNode()));
+ if (v == null) {
+ // variable is not on stack because we are
+ // inside a nested Closure and this variable
+ // was not used before
+ // then load it from the Closure field
+ FieldNode field = classNode.getDeclaredField(name);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, controller.getInternalClassName(), name, BytecodeHelper.getTypeDescription(field.getType()));
+ } else {
+ mv.visitVarInsn(ALOAD, v.getIndex());
+ }
+ controller.getOperandStack().push(ClassHelper.REFERENCE_TYPE);
+ }
+ }
+
+ public ClassNode getOrAddClosureClass(ClosureExpression expression, int mods) {
+ ClassNode closureClass = closureClassMap.get(expression);
+ if (closureClass == null) {
+ closureClass = createClosureClass(expression, mods);
+ closureClassMap.put(expression, closureClass);
+ controller.getAcg().addInnerClass(closureClass);
+ closureClass.addInterface(ClassHelper.GENERATED_CLOSURE_Type);
+ closureClass.putNodeMetaData(WriterControllerFactory.class, factory);
+ }
+ return closureClass;
+ }
+
+ private static boolean classNodeUsesReferences(ClassNode classNode) {
+ boolean ret = classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE;
+ if (ret) return ret;
+ if (classNode instanceof InnerClassNode) {
+ InnerClassNode inner = (InnerClassNode) classNode;
+ return inner.isAnonymous();
+ }
+ return false;
+ }
+
+ protected ClassNode createClosureClass(ClosureExpression expression, int mods) {
+ ClassNode classNode = controller.getClassNode();
+ ClassNode outerClass = controller.getOutermostClass();
+ MethodNode methodNode = controller.getMethodNode();
+ String name = classNode.getName() + "$"
+ + controller.getContext().getNextClosureInnerName(outerClass, classNode, methodNode); // add a more informative name
+ boolean staticMethodOrInStaticClass = controller.isStaticMethod() || classNode.isStaticClass();
+
+ Parameter[] parameters = expression.getParameters();
+ if (parameters == null) {
+ parameters = Parameter.EMPTY_ARRAY;
+ } else if (parameters.length == 0) {
+ // let's create a default 'it' parameter
+ Parameter it = new Parameter(ClassHelper.OBJECT_TYPE, "it", ConstantExpression.NULL);
+ parameters = new Parameter[]{it};
+ Variable ref = expression.getVariableScope().getDeclaredVariable("it");
+ if (ref!=null) it.setClosureSharedVariable(ref.isClosureSharedVariable());
+ }
+
+ Parameter[] localVariableParams = getClosureSharedVariables(expression);
+ removeInitialValues(localVariableParams);
+
+ InnerClassNode answer = new InnerClassNode(classNode, name, mods, ClassHelper.CLOSURE_TYPE.getPlainNodeReference());
+ answer.setEnclosingMethod(controller.getMethodNode());
+ answer.setSynthetic(true);
+ answer.setUsingGenerics(outerClass.isUsingGenerics());
+ answer.setSourcePosition(expression);
+
+ if (staticMethodOrInStaticClass) {
+ answer.setStaticClass(true);
+ }
+ if (controller.isInScriptBody()) {
+ answer.setScriptBody(true);
+ }
+ MethodNode method =
+ answer.addMethod("doCall", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, parameters, ClassNode.EMPTY_ARRAY, expression.getCode());
+ method.setSourcePosition(expression);
+
+ VariableScope varScope = expression.getVariableScope();
+ if (varScope == null) {
+ throw new RuntimeException(
+ "Must have a VariableScope by now! for expression: " + expression + " class: " + name);
+ } else {
+ method.setVariableScope(varScope.copy());
+ }
+ if (parameters.length > 1
+ || (parameters.length == 1
+ && parameters[0].getType() != null
+ && parameters[0].getType() != ClassHelper.OBJECT_TYPE
+ && !ClassHelper.OBJECT_TYPE.equals(parameters[0].getType().getComponentType())))
+ {
+
+ // let's add a typesafe call method
+ MethodNode call = answer.addMethod(
+ "call",
+ ACC_PUBLIC,
+ ClassHelper.OBJECT_TYPE,
+ parameters,
+ ClassNode.EMPTY_ARRAY,
+ new ReturnStatement(
+ new MethodCallExpression(
+ VariableExpression.THIS_EXPRESSION,
+ "doCall",
+ new ArgumentListExpression(parameters))));
+ call.setSourcePosition(expression);
+ }
+
+ // let's make the constructor
+ BlockStatement block = new BlockStatement();
+ // this block does not get a source position, because we don't
+ // want this synthetic constructor to show up in corbertura reports
+ VariableExpression outer = new VariableExpression("_outerInstance");
+ outer.setSourcePosition(expression);
+ block.getVariableScope().putReferencedLocalVariable(outer);
+ VariableExpression thisObject = new VariableExpression("_thisObject");
+ thisObject.setSourcePosition(expression);
+ block.getVariableScope().putReferencedLocalVariable(thisObject);
+ TupleExpression conArgs = new TupleExpression(outer, thisObject);
+ block.addStatement(
+ new ExpressionStatement(
+ new ConstructorCallExpression(
+ ClassNode.SUPER,
+ conArgs)));
+
+ // let's assign all the parameter fields from the outer context
+ for (Parameter param : localVariableParams) {
+ String paramName = param.getName();
+ ClassNode type = param.getType();
+ if (true) {
+ VariableExpression initialValue = new VariableExpression(paramName);
+ initialValue.setAccessedVariable(param);
+ initialValue.setUseReferenceDirectly(true);
+ ClassNode realType = type;
+ type = ClassHelper.makeReference();
+ param.setType(ClassHelper.makeReference());
+ FieldNode paramField = answer.addField(paramName, ACC_PRIVATE | ACC_SYNTHETIC, type, initialValue);
+ paramField.setOriginType(ClassHelper.getWrapper(param.getOriginType()));
+ paramField.setHolder(true);
+ String methodName = Verifier.capitalize(paramName);
+
+ // let's add a getter & setter
+ Expression fieldExp = new FieldExpression(paramField);
+ answer.addMethod(
+ "get" + methodName,
+ ACC_PUBLIC,
+ realType.getPlainNodeReference(),
+ Parameter.EMPTY_ARRAY,
+ ClassNode.EMPTY_ARRAY,
+ new ReturnStatement(fieldExp));
+ }
+ }
+
+ Parameter[] params = new Parameter[2 + localVariableParams.length];
+ params[0] = new Parameter(ClassHelper.OBJECT_TYPE, "_outerInstance");
+ params[1] = new Parameter(ClassHelper.OBJECT_TYPE, "_thisObject");
+ System.arraycopy(localVariableParams, 0, params, 2, localVariableParams.length);
+
+ ASTNode sn = answer.addConstructor(ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, block);
+ sn.setSourcePosition(expression);
+
+ correctAccessedVariable(answer,expression);
+
+ return answer;
+ }
+
+ private static void correctAccessedVariable(final InnerClassNode closureClass, ClosureExpression ce) {
+ CodeVisitorSupport visitor = new CodeVisitorSupport() {
+ @Override
+ public void visitVariableExpression(VariableExpression expression) {
+ Variable v = expression.getAccessedVariable();
+ if (v==null) return;
+ if (!(v instanceof FieldNode)) return;
+ String name = expression.getName();
+ FieldNode fn = closureClass.getDeclaredField(name);
+ if (fn != null) { // only overwrite if we find something more specific
+ expression.setAccessedVariable(fn);
+ }
+ }
+ };
+ visitor.visitClosureExpression(ce);
+ }
+
+ /*
+ * this method is called for local variables shared between scopes.
+ * These variables must not have init values because these would
+ * then in later steps be used to create multiple versions of the
+ * same method, in this case the constructor. A closure should not
+ * have more than one constructor!
+ */
+ private static void removeInitialValues(Parameter[] params) {
+ for (int i = 0; i < params.length; i++) {
+ if (params[i].hasInitialExpression()) {
+ Parameter p = new Parameter(params[i].getType(), params[i].getName());
+ p.setOriginType(p.getOriginType());
+ params[i] = p;
+ }
+ }
+ }
+
+ public boolean addGeneratedClosureConstructorCall(ConstructorCallExpression call) {
+ ClassNode classNode = controller.getClassNode();
+ if (!classNode.declaresInterface(ClassHelper.GENERATED_CLOSURE_Type)) return false;
+
+ AsmClassGenerator acg = controller.getAcg();
+ OperandStack operandStack = controller.getOperandStack();
+
+ MethodVisitor mv = controller.getMethodVisitor();
+ mv.visitVarInsn(ALOAD, 0);
+ ClassNode callNode = classNode.getSuperClass();
+ TupleExpression arguments = (TupleExpression) call.getArguments();
+ if (arguments.getExpressions().size()!=2) throw new GroovyBugError("expected 2 arguments for closure constructor super call, but got"+arguments.getExpressions().size());
+ arguments.getExpression(0).visit(acg);
+ operandStack.box();
+ arguments.getExpression(1).visit(acg);
+ operandStack.box();
+ //TODO: replace with normal String, p not needed
+ Parameter p = new Parameter(ClassHelper.OBJECT_TYPE,"_p");
+ String descriptor = BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, new Parameter[]{p,p});
+ mv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(callNode), "<init>", descriptor, false);
+ operandStack.remove(2);
+ return true;
+ }
+
+ protected Parameter[] getClosureSharedVariables(ClosureExpression ce) {
+ VariableScope scope = ce.getVariableScope();
+ Parameter[] ret = new Parameter[scope.getReferencedLocalVariablesCount()];
+ int index = 0;
+ for (Iterator iter = scope.getReferencedLocalVariablesIterator(); iter.hasNext();) {
+ Variable element = (org.codehaus.groovy.ast.Variable) iter.next();
+ Parameter p = new Parameter(element.getType(), element.getName());
+ p.setOriginType(element.getOriginType());
+ p.setClosureSharedVariable(element.isClosureSharedVariable());
+ ret[index] = p;
+ index++;
+ }
+ return ret;
+ }
+
+ private void loadThis() {
+ MethodVisitor mv = controller.getMethodVisitor();
+ mv.visitVarInsn(ALOAD, 0);
+ if (controller.isInClosure()) {
+ mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Closure", "getThisObject", "()Ljava/lang/Object;", false);
+ controller.getOperandStack().push(ClassHelper.OBJECT_TYPE);
+ } else {
+ controller.getOperandStack().push(controller.getClassNode());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/CompileStack.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/CompileStack.java b/src/main/java/org/codehaus/groovy/classgen/asm/CompileStack.java
new file mode 100644
index 0000000..35133bb
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/CompileStack.java
@@ -0,0 +1,872 @@
+/*
+ * 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.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.VariableScope;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+/**
+ * This class is a helper for AsmClassGenerator. It manages
+ * different aspects of the code of a code block like
+ * handling labels, defining variables, and scopes.
+ * After a MethodNode is visited clear should be called, for
+ * initialization the method init should be used.
+ * <p>
+ * Some Notes:
+ * <ul>
+ * <li> every push method will require a later pop call
+ * <li> method parameters may define a category 2 variable, so
+ * don't ignore the type stored in the variable object
+ * <li> the index of the variable may not be as assumed when
+ * the variable is a parameter of a method because the
+ * parameter may be used in a closure, so don't ignore
+ * the stored variable index
+ * <li> the names of temporary variables can be ignored. The names
+ * are only used for debugging and do not conflict with each
+ * other or normal variables. For accessing, the index of the
+ * variable must be used.
+ * <li> never mix temporary and normal variables by changes to this class.
+ * While the name is very important for a normal variable, it is only a
+ * helper construct for temporary variables. That means for example a
+ * name for a temporary variable can be used multiple times without
+ * conflict. So mixing them both may lead to the problem that a normal
+ * or temporary variable is hidden or even removed. That must not happen!
+ * </ul>
+ *
+ *
+ * @see org.codehaus.groovy.classgen.AsmClassGenerator
+ * @author Jochen Theodorou
+ */
+public class CompileStack implements Opcodes {
+ /**
+ * TODO: remove optimization of this.foo -> this.@foo
+ *
+ */
+
+ // state flag
+ private boolean clear=true;
+ // current scope
+ private VariableScope scope;
+ // current label for continue
+ private Label continueLabel;
+ // current label for break
+ private Label breakLabel;
+ // available variables on stack
+ private Map stackVariables = new HashMap();
+ // index of the last variable on stack
+ private int currentVariableIndex = 1;
+ // index for the next variable on stack
+ private int nextVariableIndex = 1;
+ // currently temporary variables in use
+ private final LinkedList temporaryVariables = new LinkedList();
+ // overall used variables for a method/constructor
+ private final LinkedList usedVariables = new LinkedList();
+ // map containing named labels of parenting blocks
+ private Map superBlockNamedLabels = new HashMap();
+ // map containing named labels of current block
+ private Map currentBlockNamedLabels = new HashMap();
+ // list containing finally blocks
+ // such a block is created by synchronized or finally and
+ // must be called for break/continue/return
+ private LinkedList<BlockRecorder> finallyBlocks = new LinkedList<BlockRecorder>();
+ private final LinkedList<BlockRecorder> visitedBlocks = new LinkedList<BlockRecorder>();
+
+ private Label thisStartLabel, thisEndLabel;
+
+// private MethodVisitor mv;
+
+ // helper to handle different stack based variables
+ private final LinkedList stateStack = new LinkedList();
+
+ // handle different states for the implicit "this"
+ private final LinkedList<Boolean> implicitThisStack = new LinkedList<Boolean>();
+ // handle different states for being on the left hand side
+ private final LinkedList<Boolean> lhsStack = new LinkedList<Boolean>();
+ {
+ implicitThisStack.add(false);
+ lhsStack.add(false);
+ }
+
+ // defines the first variable index usable after
+ // all parameters of a method
+ private int localVariableOffset;
+ // this is used to store the goals for a "break foo" call
+ // in a loop where foo is a label.
+ private final Map namedLoopBreakLabel = new HashMap();
+ // this is used to store the goals for a "continue foo" call
+ // in a loop where foo is a label.
+ private final Map namedLoopContinueLabel = new HashMap();
+ private String className;
+ private final LinkedList<ExceptionTableEntry> typedExceptions = new LinkedList<ExceptionTableEntry>();
+ private final LinkedList<ExceptionTableEntry> untypedExceptions = new LinkedList<ExceptionTableEntry>();
+ // stores if on left-hand-side during compilation
+ private boolean lhs;
+ // stores if implicit or explicit this is used.
+ private boolean implicitThis;
+ private final WriterController controller;
+ private boolean inSpecialConstructorCall;
+
+ protected static class LabelRange {
+ public Label start;
+ public Label end;
+ }
+
+ public static class BlockRecorder {
+ private boolean isEmpty = true;
+ public Runnable excludedStatement;
+ public final LinkedList<LabelRange> ranges;
+ public BlockRecorder() {
+ ranges = new LinkedList<LabelRange>();
+ }
+ public BlockRecorder(Runnable excludedStatement) {
+ this();
+ this.excludedStatement = excludedStatement;
+ }
+ public void startRange(Label start) {
+ LabelRange range = new LabelRange();
+ range.start = start;
+ ranges.add(range);
+ isEmpty = false;
+ }
+ public void closeRange(Label end) {
+ ranges.getLast().end = end;
+ }
+ }
+
+ private static class ExceptionTableEntry {
+ Label start,end,goal;
+ String sig;
+ }
+
+ private class StateStackElement {
+ final VariableScope scope;
+ final Label continueLabel;
+ final Label breakLabel;
+ final Map stackVariables;
+ final Map currentBlockNamedLabels;
+ final LinkedList<BlockRecorder> finallyBlocks;
+ final boolean inSpecialConstructorCall;
+
+ StateStackElement() {
+ scope = CompileStack.this.scope;
+ continueLabel = CompileStack.this.continueLabel;
+ breakLabel = CompileStack.this.breakLabel;
+ stackVariables = CompileStack.this.stackVariables;
+ currentBlockNamedLabels = CompileStack.this.currentBlockNamedLabels;
+ finallyBlocks = CompileStack.this.finallyBlocks;
+ inSpecialConstructorCall = CompileStack.this.inSpecialConstructorCall;
+ }
+ }
+
+ public CompileStack(WriterController wc) {
+ this.controller = wc;
+ }
+
+ public void pushState() {
+ stateStack.add(new StateStackElement());
+ stackVariables = new HashMap(stackVariables);
+ finallyBlocks = new LinkedList(finallyBlocks);
+ }
+
+ private void popState() {
+ if (stateStack.isEmpty()) {
+ throw new GroovyBugError("Tried to do a pop on the compile stack without push.");
+ }
+ StateStackElement element = (StateStackElement) stateStack.removeLast();
+ scope = element.scope;
+ continueLabel = element.continueLabel;
+ breakLabel = element.breakLabel;
+ stackVariables = element.stackVariables;
+ finallyBlocks = element.finallyBlocks;
+ inSpecialConstructorCall = element.inSpecialConstructorCall;
+ }
+
+ public Label getContinueLabel() {
+ return continueLabel;
+ }
+
+ public Label getBreakLabel() {
+ return breakLabel;
+ }
+
+ public void removeVar(int tempIndex) {
+ final BytecodeVariable head = (BytecodeVariable) temporaryVariables.removeFirst();
+ if (head.getIndex() != tempIndex) {
+ temporaryVariables.addFirst(head);
+ MethodNode methodNode = controller.getMethodNode();
+ if (methodNode==null) {
+ methodNode = controller.getConstructorNode();
+ }
+ throw new GroovyBugError(
+ "In method "+ (methodNode!=null?methodNode.getText():"<unknown>") + ", " +
+ "CompileStack#removeVar: tried to remove a temporary " +
+ "variable with index "+ tempIndex + " in wrong order. " +
+ "Current temporary variables=" + temporaryVariables);
+ }
+ }
+
+ private void setEndLabels(){
+ Label endLabel = new Label();
+ controller.getMethodVisitor().visitLabel(endLabel);
+ for (Iterator iter = stackVariables.values().iterator(); iter.hasNext();) {
+ BytecodeVariable var = (BytecodeVariable) iter.next();
+ var.setEndLabel(endLabel);
+ }
+ thisEndLabel = endLabel;
+ }
+
+ public void pop() {
+ setEndLabels();
+ popState();
+ }
+
+ public VariableScope getScope() {
+ return scope;
+ }
+
+ /**
+ * creates a temporary variable.
+ *
+ * @param var defines type and name
+ * @param store defines if the toplevel argument of the stack should be stored
+ * @return the index used for this temporary variable
+ */
+ public int defineTemporaryVariable(Variable var, boolean store) {
+ return defineTemporaryVariable(var.getName(), var.getType(),store);
+ }
+
+ public BytecodeVariable getVariable(String variableName ) {
+ return getVariable(variableName, true);
+ }
+
+ /**
+ * Returns a normal variable.
+ * <p>
+ * If <code>mustExist</code> is true and the normal variable doesn't exist,
+ * then this method will throw a GroovyBugError. It is not the intention of
+ * this method to let this happen! And the exception should not be used for
+ * flow control - it is just acting as an assertion. If the exception is thrown
+ * then it indicates a bug in the class using CompileStack.
+ * This method can also not be used to return a temporary variable.
+ * Temporary variables are not normal variables.
+ *
+ * @param variableName name of the variable
+ * @param mustExist throw exception if variable does not exist
+ * @return the normal variable or null if not found (and <code>mustExist</code> not true)
+ */
+ public BytecodeVariable getVariable(String variableName, boolean mustExist) {
+ if (variableName.equals("this")) return BytecodeVariable.THIS_VARIABLE;
+ if (variableName.equals("super")) return BytecodeVariable.SUPER_VARIABLE;
+ BytecodeVariable v = (BytecodeVariable) stackVariables.get(variableName);
+ if (v == null && mustExist)
+ throw new GroovyBugError("tried to get a variable with the name " + variableName + " as stack variable, but a variable with this name was not created");
+ return v;
+ }
+
+ /**
+ * creates a temporary variable.
+ *
+ * @param name defines type and name
+ * @param store defines if the top-level argument of the stack should be stored
+ * @return the index used for this temporary variable
+ */
+ public int defineTemporaryVariable(String name,boolean store) {
+ return defineTemporaryVariable(name, ClassHelper.DYNAMIC_TYPE,store);
+ }
+
+ /**
+ * creates a temporary variable.
+ *
+ * @param name defines the name
+ * @param node defines the node
+ * @param store defines if the top-level argument of the stack should be stored
+ * @return the index used for this temporary variable
+ */
+ public int defineTemporaryVariable(String name, ClassNode node, boolean store) {
+ BytecodeVariable answer = defineVar(name, node, false, false);
+ temporaryVariables.addFirst(answer); // TRICK: we add at the beginning so when we find for remove or get we always have the last one
+ usedVariables.removeLast();
+
+ if (store) controller.getOperandStack().storeVar(answer);
+
+ return answer.getIndex();
+ }
+
+ private void resetVariableIndex(boolean isStatic) {
+ temporaryVariables.clear();
+ if (!isStatic) {
+ currentVariableIndex=1;
+ nextVariableIndex=1;
+ } else {
+ currentVariableIndex=0;
+ nextVariableIndex=0;
+ }
+ }
+
+ /**
+ * Clears the state of the class. This method should be called
+ * after a MethodNode is visited. Note that a call to init will
+ * fail if clear is not called before
+ */
+ public void clear() {
+ if (stateStack.size()>1) {
+ int size = stateStack.size()-1;
+ throw new GroovyBugError("the compile stack contains "+size+" more push instruction"+(size==1?"":"s")+" than pops.");
+ }
+ if (lhsStack.size()>1) {
+ int size = lhsStack.size()-1;
+ throw new GroovyBugError("lhs stack is supposed to be empty, but has " +
+ size + " elements left.");
+ }
+ if (implicitThisStack.size()>1) {
+ int size = implicitThisStack.size()-1;
+ throw new GroovyBugError("implicit 'this' stack is supposed to be empty, but has " +
+ size + " elements left.");
+ }
+ clear = true;
+ MethodVisitor mv = controller.getMethodVisitor();
+ // br experiment with local var table so debuggers can retrieve variable names
+ if (true) {//AsmClassGenerator.CREATE_DEBUG_INFO) {
+ if (thisEndLabel==null) setEndLabels();
+
+ if (!scope.isInStaticContext()) {
+ // write "this"
+ mv.visitLocalVariable("this", className, null, thisStartLabel, thisEndLabel, 0);
+ }
+
+ for (Iterator iterator = usedVariables.iterator(); iterator.hasNext();) {
+ BytecodeVariable v = (BytecodeVariable) iterator.next();
+ ClassNode t = v.getType();
+ if (v.isHolder()) t = ClassHelper.REFERENCE_TYPE;
+ String type = BytecodeHelper.getTypeDescription(t);
+ Label start = v.getStartLabel();
+ Label end = v.getEndLabel();
+ mv.visitLocalVariable(v.getName(), type, null, start, end, v.getIndex());
+ }
+ }
+
+ //exception table writing
+ for (ExceptionTableEntry ep : typedExceptions) {
+ mv.visitTryCatchBlock(ep.start, ep.end, ep.goal, ep.sig);
+ }
+ //exception table writing
+ for (ExceptionTableEntry ep : untypedExceptions) {
+ mv.visitTryCatchBlock(ep.start, ep.end, ep.goal, ep.sig);
+ }
+
+
+ pop();
+ typedExceptions.clear();
+ untypedExceptions.clear();
+ stackVariables.clear();
+ usedVariables.clear();
+ scope = null;
+ finallyBlocks.clear();
+ resetVariableIndex(false);
+ superBlockNamedLabels.clear();
+ currentBlockNamedLabels.clear();
+ namedLoopBreakLabel.clear();
+ namedLoopContinueLabel.clear();
+ continueLabel=null;
+ breakLabel=null;
+ thisStartLabel=null;
+ thisEndLabel=null;
+ mv = null;
+ }
+
+ public void addExceptionBlock (Label start, Label end, Label goal,
+ String sig)
+ {
+ // this code is in an extra method to avoid
+ // lazy initialization issues
+ ExceptionTableEntry ep = new ExceptionTableEntry();
+ ep.start = start;
+ ep.end = end;
+ ep.sig = sig;
+ ep.goal = goal;
+ if (sig==null) {
+ untypedExceptions.add(ep);
+ } else {
+ typedExceptions.add(ep);
+ }
+ }
+
+ /**
+ * initializes this class for a MethodNode. This method will
+ * automatically define variables for the method parameters
+ * and will create references if needed. The created variables
+ * can be accessed by calling getVariable().
+ *
+ */
+ public void init(VariableScope el, Parameter[] parameters) {
+ if (!clear) throw new GroovyBugError("CompileStack#init called without calling clear before");
+ clear=false;
+ pushVariableScope(el);
+ defineMethodVariables(parameters,el.isInStaticContext());
+ this.className = BytecodeHelper.getTypeDescription(controller.getClassNode());
+ }
+
+ /**
+ * Causes the state-stack to add an element and sets
+ * the given scope as new current variable scope. Creates
+ * a element for the state stack so pop has to be called later
+ */
+ public void pushVariableScope(VariableScope el) {
+ pushState();
+ scope = el;
+ superBlockNamedLabels = new HashMap(superBlockNamedLabels);
+ superBlockNamedLabels.putAll(currentBlockNamedLabels);
+ currentBlockNamedLabels = new HashMap();
+ }
+
+ /**
+ * Should be called when descending into a loop that defines
+ * also a scope. Calls pushVariableScope and prepares labels
+ * for a loop structure. Creates a element for the state stack
+ * so pop has to be called later, TODO: @Deprecate
+ */
+ public void pushLoop(VariableScope el, String labelName) {
+ pushVariableScope(el);
+ continueLabel = new Label();
+ breakLabel = new Label();
+ if (labelName != null) {
+ initLoopLabels(labelName);
+ }
+ }
+
+ /**
+ * Should be called when descending into a loop that defines
+ * also a scope. Calls pushVariableScope and prepares labels
+ * for a loop structure. Creates a element for the state stack
+ * so pop has to be called later
+ */
+ public void pushLoop(VariableScope el, List<String> labelNames) {
+ pushVariableScope(el);
+ continueLabel = new Label();
+ breakLabel = new Label();
+ if (labelNames != null) {
+ for (String labelName : labelNames) {
+ initLoopLabels(labelName);
+ }
+ }
+ }
+
+ private void initLoopLabels(String labelName) {
+ namedLoopBreakLabel.put(labelName,breakLabel);
+ namedLoopContinueLabel.put(labelName,continueLabel);
+ }
+
+ /**
+ * Should be called when descending into a loop that does
+ * not define a scope. Creates a element for the state stack
+ * so pop has to be called later, TODO: @Deprecate
+ */
+ public void pushLoop(String labelName) {
+ pushState();
+ continueLabel = new Label();
+ breakLabel = new Label();
+ initLoopLabels(labelName);
+ }
+
+ /**
+ * Should be called when descending into a loop that does
+ * not define a scope. Creates a element for the state stack
+ * so pop has to be called later
+ */
+ public void pushLoop(List<String> labelNames) {
+ pushState();
+ continueLabel = new Label();
+ breakLabel = new Label();
+ if (labelNames != null) {
+ for (String labelName : labelNames) {
+ initLoopLabels(labelName);
+ }
+ }
+ }
+
+ /**
+ * Used for <code>break foo</code> inside a loop to end the
+ * execution of the marked loop. This method will return the
+ * break label of the loop if there is one found for the name.
+ * If not, the current break label is returned.
+ */
+ public Label getNamedBreakLabel(String name) {
+ Label label = getBreakLabel();
+ Label endLabel = null;
+ if (name!=null) endLabel = (Label) namedLoopBreakLabel.get(name);
+ if (endLabel!=null) label = endLabel;
+ return label;
+ }
+
+ /**
+ * Used for <code>continue foo</code> inside a loop to continue
+ * the execution of the marked loop. This method will return
+ * the break label of the loop if there is one found for the
+ * name. If not, getLabel is used.
+ */
+ public Label getNamedContinueLabel(String name) {
+ Label label = getLabel(name);
+ Label endLabel = null;
+ if (name!=null) endLabel = (Label) namedLoopContinueLabel.get(name);
+ if (endLabel!=null) label = endLabel;
+ return label;
+ }
+
+ /**
+ * Creates a new break label and a element for the state stack
+ * so pop has to be called later
+ */
+ public Label pushSwitch(){
+ pushState();
+ breakLabel = new Label();
+ return breakLabel;
+ }
+
+ /**
+ * because a boolean Expression may not be evaluated completely
+ * it is important to keep the registers clean
+ */
+ public void pushBooleanExpression(){
+ pushState();
+ }
+
+ private BytecodeVariable defineVar(String name, ClassNode type, boolean holder, boolean useReferenceDirectly) {
+ int prevCurrent = currentVariableIndex;
+ makeNextVariableID(type,useReferenceDirectly);
+ int index = currentVariableIndex;
+ if (holder && !useReferenceDirectly) index = localVariableOffset++;
+ BytecodeVariable answer = new BytecodeVariable(index, type, name, prevCurrent);
+ usedVariables.add(answer);
+ answer.setHolder(holder);
+ return answer;
+ }
+
+ private void makeLocalVariablesOffset(Parameter[] paras, boolean isInStaticContext) {
+ resetVariableIndex(isInStaticContext);
+
+ for (Parameter para : paras) {
+ makeNextVariableID(para.getType(), false);
+ }
+ localVariableOffset = nextVariableIndex;
+
+ resetVariableIndex(isInStaticContext);
+ }
+
+ private void defineMethodVariables(Parameter[] paras, boolean isInStaticContext) {
+ Label startLabel = new Label();
+ thisStartLabel = startLabel;
+ controller.getMethodVisitor().visitLabel(startLabel);
+
+ makeLocalVariablesOffset(paras,isInStaticContext);
+
+ for (Parameter para : paras) {
+ String name = para.getName();
+ BytecodeVariable answer;
+ ClassNode type = para.getType();
+ if (para.isClosureSharedVariable()) {
+ boolean useExistingReference = para.getNodeMetaData(ClosureWriter.UseExistingReference.class) != null;
+ answer = defineVar(name, para.getOriginType(), true, useExistingReference);
+ answer.setStartLabel(startLabel);
+ if (!useExistingReference) {
+ controller.getOperandStack().load(type, currentVariableIndex);
+ controller.getOperandStack().box();
+
+ // GROOVY-4237, the original variable should always appear
+ // in the variable index, otherwise some programs get into
+ // trouble. So we define a dummy variable for the packaging
+ // phase and let it end right away before the normal
+ // reference will be used
+ Label newStart = new Label();
+ controller.getMethodVisitor().visitLabel(newStart);
+ BytecodeVariable var = new BytecodeVariable(currentVariableIndex, para.getOriginType(), name, currentVariableIndex);
+ var.setStartLabel(startLabel);
+ var.setEndLabel(newStart);
+ usedVariables.add(var);
+ answer.setStartLabel(newStart);
+
+ createReference(answer);
+ }
+ } else {
+ answer = defineVar(name, type, false, false);
+ answer.setStartLabel(startLabel);
+ }
+ stackVariables.put(name, answer);
+ }
+
+ nextVariableIndex = localVariableOffset;
+ }
+
+ private void createReference(BytecodeVariable reference) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ mv.visitTypeInsn(NEW, "groovy/lang/Reference");
+ mv.visitInsn(DUP_X1);
+ mv.visitInsn(SWAP);
+ mv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "(Ljava/lang/Object;)V", false);
+ mv.visitVarInsn(ASTORE, reference.getIndex());
+ }
+
+ private static void pushInitValue(ClassNode type, MethodVisitor mv) {
+ if (ClassHelper.isPrimitiveType(type)) {
+ if (type== ClassHelper.long_TYPE) {
+ mv.visitInsn(LCONST_0);
+ } else if (type== ClassHelper.double_TYPE) {
+ mv.visitInsn(DCONST_0);
+ } else if (type== ClassHelper.float_TYPE) {
+ mv.visitInsn(FCONST_0);
+ } else {
+ mv.visitLdcInsn(0);
+ }
+ } else {
+ mv.visitInsn(ACONST_NULL);
+ }
+ }
+
+ /**
+ * Defines a new Variable using an AST variable.
+ * @param initFromStack if true the last element of the
+ * stack will be used to initialize
+ * the new variable. If false null
+ * will be used.
+ */
+ public BytecodeVariable defineVariable(Variable v, boolean initFromStack) {
+ return defineVariable(v, v.getOriginType(), initFromStack);
+ }
+
+ public BytecodeVariable defineVariable(Variable v, ClassNode variableType, boolean initFromStack) {
+ String name = v.getName();
+ BytecodeVariable answer = defineVar(name, variableType, v.isClosureSharedVariable(), v.isClosureSharedVariable());
+ stackVariables.put(name, answer);
+
+ MethodVisitor mv = controller.getMethodVisitor();
+ Label startLabel = new Label();
+ answer.setStartLabel(startLabel);
+ ClassNode type = answer.getType().redirect();
+ OperandStack operandStack = controller.getOperandStack();
+
+ if (!initFromStack) {
+ if (ClassHelper.isPrimitiveType(v.getOriginType()) && ClassHelper.getWrapper(v.getOriginType()) == variableType) {
+ pushInitValue(v.getOriginType(), mv);
+ operandStack.push(v.getOriginType());
+ operandStack.box();
+ operandStack.remove(1);
+ } else {
+ pushInitValue(type, mv);
+ }
+ }
+ operandStack.push(answer.getType());
+ if (answer.isHolder()) {
+ operandStack.box();
+ operandStack.remove(1);
+ createReference(answer);
+ } else {
+ operandStack.storeVar(answer);
+ }
+
+ mv.visitLabel(startLabel);
+ return answer;
+ }
+
+ /**
+ * @param name the name of the variable of interest
+ * @return true if a variable is already defined
+ */
+ public boolean containsVariable(String name) {
+ return stackVariables.containsKey(name);
+ }
+
+ /**
+ * Calculates the index of the next free register stores it
+ * and sets the current variable index to the old value
+ */
+ private void makeNextVariableID(ClassNode type, boolean useReferenceDirectly) {
+ currentVariableIndex = nextVariableIndex;
+ if ((type== ClassHelper.long_TYPE || type== ClassHelper.double_TYPE) && !useReferenceDirectly) {
+ nextVariableIndex++;
+ }
+ nextVariableIndex++;
+ }
+
+ /**
+ * Returns the label for the given name
+ */
+ public Label getLabel(String name) {
+ if (name==null) return null;
+ Label l = (Label) superBlockNamedLabels.get(name);
+ if (l==null) l = createLocalLabel(name);
+ return l;
+ }
+
+ /**
+ * creates a new named label
+ */
+ public Label createLocalLabel(String name) {
+ Label l = (Label) currentBlockNamedLabels.get(name);
+ if (l==null) {
+ l = new Label();
+ currentBlockNamedLabels.put(name,l);
+ }
+ return l;
+ }
+
+ public void applyFinallyBlocks(Label label, boolean isBreakLabel) {
+ // first find the state defining the label. That is the state
+ // directly after the state not knowing this label. If no state
+ // in the list knows that label, then the defining state is the
+ // current state.
+ StateStackElement result = null;
+ for (ListIterator iter = stateStack.listIterator(stateStack.size()); iter.hasPrevious();) {
+ StateStackElement element = (StateStackElement) iter.previous();
+ if (!element.currentBlockNamedLabels.values().contains(label)) {
+ if (isBreakLabel && element.breakLabel != label) {
+ result = element;
+ break;
+ }
+ if (!isBreakLabel && element.continueLabel != label) {
+ result = element;
+ break;
+ }
+ }
+ }
+
+ List<BlockRecorder> blocksToRemove;
+ if (result==null) {
+ // all Blocks do know the label, so use all finally blocks
+ blocksToRemove = (List<BlockRecorder>) Collections.EMPTY_LIST;
+ } else {
+ blocksToRemove = result.finallyBlocks;
+ }
+
+ List<BlockRecorder> blocks = new LinkedList<BlockRecorder>(finallyBlocks);
+ blocks.removeAll(blocksToRemove);
+ applyBlockRecorder(blocks);
+ }
+
+
+ private void applyBlockRecorder(List<BlockRecorder> blocks) {
+ if (blocks.isEmpty() || blocks.size() == visitedBlocks.size()) return;
+
+ MethodVisitor mv = controller.getMethodVisitor();
+ Label newStart = new Label();
+
+ for (BlockRecorder fb : blocks) {
+ if (visitedBlocks.contains(fb)) continue;
+
+ Label end = new Label();
+ mv.visitInsn(NOP);
+ mv.visitLabel(end);
+
+ fb.closeRange(end);
+
+ // we exclude the finally block from the exception table
+ // here to avoid double visiting of finally statements
+ fb.excludedStatement.run();
+
+ fb.startRange(newStart);
+ }
+
+ mv.visitInsn(NOP);
+ mv.visitLabel(newStart);
+ }
+
+ public void applyBlockRecorder() {
+ applyBlockRecorder(finallyBlocks);
+ }
+
+ public boolean hasBlockRecorder() {
+ return !finallyBlocks.isEmpty();
+ }
+
+ public void pushBlockRecorder(BlockRecorder recorder) {
+ pushState();
+ finallyBlocks.addFirst(recorder);
+ }
+
+ public void pushBlockRecorderVisit(BlockRecorder finallyBlock) {
+ visitedBlocks.add(finallyBlock);
+ }
+
+ public void popBlockRecorderVisit(BlockRecorder finallyBlock) {
+ visitedBlocks.remove(finallyBlock);
+ }
+
+ public void writeExceptionTable(BlockRecorder block, Label goal, String sig) {
+ if (block.isEmpty) return;
+ MethodVisitor mv = controller.getMethodVisitor();
+ for (LabelRange range : block.ranges) {
+ mv.visitTryCatchBlock(range.start, range.end, goal, sig);
+ }
+ }
+
+// public MethodVisitor getMethodVisitor() {
+// return mv;
+// }
+
+ public boolean isLHS() {
+ return lhs;
+ }
+
+ public void pushLHS(boolean lhs) {
+ lhsStack.add(lhs);
+ this.lhs = lhs;
+ }
+
+ public void popLHS() {
+ lhsStack.removeLast();
+ this.lhs = lhsStack.getLast();
+ }
+
+ public void pushImplicitThis(boolean implicitThis) {
+ implicitThisStack.add(implicitThis);
+ this.implicitThis = implicitThis;
+ }
+
+ public boolean isImplicitThis() {
+ return implicitThis;
+ }
+
+ public void popImplicitThis() {
+ implicitThisStack.removeLast();
+ this.implicitThis = implicitThisStack.getLast();
+ }
+
+ public boolean isInSpecialConstructorCall() {
+ return inSpecialConstructorCall;
+ }
+
+ public void pushInSpecialConstructorCall() {
+ pushState();
+ inSpecialConstructorCall = true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/DelegatingController.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/DelegatingController.java b/src/main/java/org/codehaus/groovy/classgen/asm/DelegatingController.java
new file mode 100644
index 0000000..22acbaa
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/DelegatingController.java
@@ -0,0 +1,276 @@
+/*
+ * 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.ConstructorNode;
+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.control.SourceUnit;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * This class will delegate all calls to a WriterController given in the constructor.
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class DelegatingController extends WriterController {
+ private final WriterController delegationController;
+
+ public DelegatingController(WriterController normalController) {
+ this.delegationController = normalController;
+ }
+
+ @Override
+ public void init(final AsmClassGenerator asmClassGenerator, final GeneratorContext gcon, final ClassVisitor cv, final ClassNode cn) {
+ delegationController.init(asmClassGenerator, gcon, cv, cn);
+ }
+
+ @Override
+ public void setMethodNode(final MethodNode mn) {
+ delegationController.setMethodNode(mn);
+ }
+
+ @Override
+ public void setConstructorNode(final ConstructorNode cn) {
+ delegationController.setConstructorNode(cn);
+ }
+
+ @Override
+ public boolean isFastPath() {
+ return delegationController.isFastPath();
+ }
+
+ @Override
+ public CallSiteWriter getCallSiteWriter() {
+ return delegationController.getCallSiteWriter();
+ }
+
+ @Override
+ public StatementWriter getStatementWriter() {
+ return delegationController.getStatementWriter();
+ }
+
+ @Override
+ public TypeChooser getTypeChooser() {
+ return delegationController.getTypeChooser();
+ }
+
+ @Override
+ public AsmClassGenerator getAcg() {
+ return delegationController.getAcg();
+ }
+
+ @Override
+ public AssertionWriter getAssertionWriter() {
+ return delegationController.getAssertionWriter();
+ }
+
+ @Override
+ public BinaryExpressionHelper getBinaryExpressionHelper() {
+ return delegationController.getBinaryExpressionHelper();
+ }
+
+ @Override
+ public UnaryExpressionHelper getUnaryExpressionHelper() {
+ return delegationController.getUnaryExpressionHelper();
+ }
+
+ @Override
+ public String getClassName() {
+ return delegationController.getClassName();
+ }
+
+ @Override
+ public ClassNode getClassNode() {
+ return delegationController.getClassNode();
+ }
+
+ @Override
+ public ClassVisitor getClassVisitor() {
+ return delegationController.getClassVisitor();
+ }
+
+ @Override
+ public ClosureWriter getClosureWriter() {
+ return delegationController.getClosureWriter();
+ }
+
+ @Override
+ public CompileStack getCompileStack() {
+ return delegationController.getCompileStack();
+ }
+
+ @Override
+ public ConstructorNode getConstructorNode() {
+ return delegationController.getConstructorNode();
+ }
+
+ @Override
+ public GeneratorContext getContext() {
+ return delegationController.getContext();
+ }
+
+ @Override
+ public ClassVisitor getCv() {
+ return delegationController.getCv();
+ }
+
+ @Override
+ public InterfaceHelperClassNode getInterfaceClassLoadingClass() {
+ return delegationController.getInterfaceClassLoadingClass();
+ }
+
+ @Override
+ public String getInternalBaseClassName() {
+ return delegationController.getInternalBaseClassName();
+ }
+
+ @Override
+ public String getInternalClassName() {
+ return delegationController.getInternalClassName();
+ }
+
+ @Override
+ public InvocationWriter getInvocationWriter() {
+ return delegationController.getInvocationWriter();
+ }
+
+ @Override
+ public MethodNode getMethodNode() {
+ return delegationController.getMethodNode();
+ }
+
+ @Override
+ public MethodVisitor getMethodVisitor() {
+ return delegationController.getMethodVisitor();
+ }
+
+ @Override
+ public OperandStack getOperandStack() {
+ return delegationController.getOperandStack();
+ }
+
+ @Override
+ public ClassNode getOutermostClass() {
+ return delegationController.getOutermostClass();
+ }
+
+ @Override
+ public ClassNode getReturnType() {
+ return delegationController.getReturnType();
+ }
+
+ @Override
+ public SourceUnit getSourceUnit() {
+ return delegationController.getSourceUnit();
+ }
+
+ @Override
+ public boolean isConstructor() {
+ return delegationController.isConstructor();
+ }
+
+ @Override
+ public boolean isInClosure() {
+ return delegationController.isInClosure();
+ }
+
+ @Override
+ public boolean isInClosureConstructor() {
+ return delegationController.isInClosureConstructor();
+ }
+
+ @Override
+ public boolean isNotClinit() {
+ return delegationController.isNotClinit();
+ }
+
+ @Override
+ public boolean isInScriptBody() {
+ return delegationController.isInScriptBody();
+ }
+
+ @Override
+ public boolean isNotExplicitThisInClosure(boolean implicitThis) {
+ return delegationController.isNotExplicitThisInClosure(implicitThis);
+ }
+
+ @Override
+ public boolean isStaticConstructor() {
+ return delegationController.isStaticConstructor();
+ }
+
+ @Override
+ public boolean isStaticContext() {
+ return delegationController.isStaticContext();
+ }
+
+ @Override
+ public boolean isStaticMethod() {
+ return delegationController.isStaticMethod();
+ }
+
+ @Override
+ public void setInterfaceClassLoadingClass(InterfaceHelperClassNode ihc) {
+ delegationController.setInterfaceClassLoadingClass(ihc);
+ }
+
+ @Override
+ public void setMethodVisitor(MethodVisitor methodVisitor) {
+ delegationController.setMethodVisitor(methodVisitor);
+ }
+
+ @Override
+ public boolean shouldOptimizeForInt() {
+ return delegationController.shouldOptimizeForInt();
+ }
+
+ @Override
+ public void switchToFastPath() {
+ delegationController.switchToFastPath();
+ }
+
+ @Override
+ public void switchToSlowPath() {
+ delegationController.switchToSlowPath();
+ }
+
+ @Override
+ public int getBytecodeVersion() {
+ return delegationController.getBytecodeVersion();
+ }
+
+ @Override
+ public void setLineNumber(int n) {
+ delegationController.setLineNumber(n);
+ }
+
+ @Override
+ public int getLineNumber() {
+ return delegationController.getLineNumber();
+ }
+
+ @Override
+ public void resetLineNumber() {
+ delegationController.resetLineNumber();
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/ExpressionAsVariableSlot.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/ExpressionAsVariableSlot.java b/src/main/java/org/codehaus/groovy/classgen/asm/ExpressionAsVariableSlot.java
new file mode 100644
index 0000000..a9e568b
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/ExpressionAsVariableSlot.java
@@ -0,0 +1,81 @@
+/*
+ * 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.expr.Expression;
+import org.codehaus.groovy.classgen.BytecodeExpression;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * Helper class that takes an Expression and if visited will load it normally,
+ * storing the result in a helper variable, which then can be requested after
+ * the visit is completed. A copy of the variable will stay on the stack.
+ * Subsequent visits will load the stored value instead of visiting the
+ * expression again
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class ExpressionAsVariableSlot extends BytecodeExpression {
+ private int index = -1;
+ private final Expression exp;
+ private final WriterController controller;
+ private final String name;
+
+ public ExpressionAsVariableSlot(WriterController controller, Expression expression, String name) {
+ this.exp = expression;
+ this.controller = controller;
+ this.name = name;
+ }
+
+ public ExpressionAsVariableSlot(WriterController controller, Expression expression) {
+ this(controller, expression, "ExpressionAsVariableSlot_TEMP");
+ }
+
+ @Override
+ public void visit(MethodVisitor mv) {
+ OperandStack os = controller.getOperandStack();
+ if (index == -1) { // first visit
+ // visit expression
+ exp.visit(controller.getAcg());
+ // make copy & set type
+ os.dup();
+ this.setType(os.getTopOperand());
+ // store copy in temporary variable
+ CompileStack compileStack = controller.getCompileStack();
+ index = compileStack.defineTemporaryVariable(name, getType(), true);
+ } else {
+ os.load(getType(), index);
+ }
+ // since the calling code will push the type again, we better remove it here
+ os.remove(1);
+ }
+
+ /**
+ * returns the index of the bytecode variable
+ */
+ public int getIndex() {
+ if (index == -1) throw new GroovyBugError("index requested before visit!");
+ return index;
+ }
+
+ @Override
+ public String getText() {
+ return exp.getText();
+ }
+}