tagging Groovy 1.8.0 rc 2

git-svn-id: http://svn.codehaus.org/groovy/tags/GROOVY_1_8_0_RC_2@21780 a5544e8c-8a19-0410-ba12-f9af4593a198
diff --git a/build.properties b/build.properties
index 91f7357..c65e07b 100644
--- a/build.properties
+++ b/build.properties
@@ -1,6 +1,6 @@
-groovyVersion = 1.8.0-rc-2-SNAPSHOT
+groovyVersion = 1.8.0-rc-2
 # bundle version format: major('.'minor('.'micro('.'qualifier)?)?)? (first 3 only digits)
-groovyBundleVersion = 1.8.0.rc-2-SNAPSHOT
+groovyBundleVersion = 1.8.0.rc-2
 
 #  Many people have reported problems testing UberTestCaseGroovySourceSubPackages, others have no difficulties with the default
 #  values ant junit task uses.  The decision has been taken to provide the values to try and cause the least
diff --git a/pom.xml b/pom.xml
index 3328c27..6d3dfcb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
     <name>Groovy</name>
     <packaging>jar</packaging>
     
-    <version>1.8.0-rc-2-SNAPSHOT</version>
+    <version>1.8.0-rc-2</version>
 
     <description>
         Groovy: A powerful, dynamic language for the JVM
diff --git a/src/main/org/codehaus/groovy/ast/expr/MethodCallExpression.java b/src/main/org/codehaus/groovy/ast/expr/MethodCallExpression.java
index 0aec273..691e035 100644
--- a/src/main/org/codehaus/groovy/ast/expr/MethodCallExpression.java
+++ b/src/main/org/codehaus/groovy/ast/expr/MethodCallExpression.java
@@ -82,6 +82,7 @@
         answer.setSpreadSafe(spreadSafe);
         answer.setImplicitThis(implicitThis);
         answer.setSourcePosition(this);
+        answer.setMethodTarget(target);
         return answer;
     }
 
@@ -105,7 +106,7 @@
       this.method = method;
     }
 
-  /**
+    /**
      * This method returns the method name as String if it is no dynamic
      * calculated method name, but a constant.
      */
@@ -197,11 +198,23 @@
         return usesGenerics;
 	}
 
+    /**
+     * Sets a method call target for a direct method call. 
+     * WARNING: A method call made this way will run outside of the MOP! 
+     * @param mn the target as MethodNode, mn==null means no target
+     */
     public void setMethodTarget(MethodNode mn) {
         this.target = mn;
-        setType(target.getReturnType());
+        if (mn!=null) {
+            setType(target.getReturnType());
+        } else {
+            setType(ClassHelper.OBJECT_TYPE);
+        }
     }
     
+    /**
+     * @return the target as method node if set
+     */
     public MethodNode getMethodTarget() {
         return target;
     }
diff --git a/src/main/org/codehaus/groovy/classgen/asm/InvocationWriter.java b/src/main/org/codehaus/groovy/classgen/asm/InvocationWriter.java
index 625b7cc..9a28410 100644
--- a/src/main/org/codehaus/groovy/classgen/asm/InvocationWriter.java
+++ b/src/main/org/codehaus/groovy/classgen/asm/InvocationWriter.java
@@ -15,7 +15,6 @@
  */
 package org.codehaus.groovy.classgen.asm;
 
-import java.util.ArrayList;
 import java.util.List;
 
 import org.codehaus.groovy.ast.ClassHelper;
@@ -88,6 +87,57 @@
         makeCall(origin, new ClassExpression(cn), receiver, message, arguments,
                 adapter, safe, spreadSafe, implicitThis);
     }
+    
+    private boolean writeDirectMethodCall(MethodNode target, boolean implicitThis,  Expression receiver, TupleExpression args) {
+        if (target==null) return false;
+        
+        String methodName = target.getName();
+        CompileStack compileStack = controller.getCompileStack();
+        OperandStack operandStack = controller.getOperandStack();
+        
+        MethodVisitor mv = controller.getMethodVisitor();
+        int opcode = INVOKEVIRTUAL;
+        if (target.isStatic()) {
+            opcode = INVOKESTATIC;
+        } else if (target.isPrivate()) {
+            opcode = INVOKESPECIAL;
+        }
+
+        // handle receiver
+        int argumentsToRemove = 0;
+        if (opcode!=INVOKESTATIC) {
+            if (receiver!=null) {
+                // load receiver if not static invocation
+                compileStack.pushImplicitThis(implicitThis);
+                receiver.visit(controller.getAcg());
+                operandStack.doGroovyCast(target.getDeclaringClass());
+                compileStack.popImplicitThis();
+                argumentsToRemove++;
+            } else {
+                mv.visitIntInsn(ALOAD,0);
+            }
+        }
+        
+        // load arguments
+        Parameter[] para = target.getParameters();
+        List<Expression> argumentList = args.getExpressions();
+        for (int i=0; i<argumentList.size(); i++) {
+            argumentList.get(i).visit(controller.getAcg());
+            controller.getOperandStack().doGroovyCast(para[i].getType());
+        }
+
+        String owner = BytecodeHelper.getClassInternalName(target.getDeclaringClass());
+        String desc = BytecodeHelper.getMethodDescriptor(target.getReturnType(), target.getParameters());
+        mv.visitMethodInsn(opcode, owner, methodName, desc);
+        ClassNode ret = target.getReturnType().redirect();
+        if (ret==ClassHelper.VOID_TYPE) {
+            ret = ClassHelper.OBJECT_TYPE;
+            mv.visitInsn(ACONST_NULL);
+        }
+        argumentsToRemove += args.getExpressions().size();
+        controller.getOperandStack().replace(ret, argumentsToRemove);
+        return true;
+    }
 
     private void makeCall(
             Expression origin, ClassExpression sender,
@@ -95,22 +145,17 @@
             MethodCallerMultiAdapter adapter,
             boolean safe, boolean spreadSafe, boolean implicitThis
     ) { 
+        // optimization path
         boolean fittingAdapter =    adapter == invokeMethodOnCurrent ||
                                     adapter == invokeStaticMethod;
         if (fittingAdapter && controller.optimizeForInt && controller.isFastPath()) {
             String methodName = getMethodName(message);
             if (methodName != null) {
-                List<Parameter> plist = new ArrayList(16);
                 TupleExpression args;
                 if (arguments instanceof TupleExpression) {
                     args = (TupleExpression) arguments;
-                    for (Expression arg : args.getExpressions()) {
-                        plist.add(new Parameter(arg.getType(),""));
-                    }
-                    
                 } else {
                     args = new TupleExpression(receiver);
-                    plist.add(new Parameter(arguments.getType(),""));
                 }
 
                 StatementMeta meta = null;
@@ -118,39 +163,18 @@
                 MethodNode mn = null;
                 if (meta!=null) mn = meta.target;
                 
-                if (mn !=null) {
-                    MethodVisitor mv = controller.getMethodVisitor();
-                    int opcode = INVOKEVIRTUAL;
-                    if (mn.isStatic()) {
-                        opcode = INVOKESTATIC;
-                    } else if (mn.isPrivate()) {
-                        opcode = INVOKESPECIAL;
-                    }
-                    
-                    if (opcode!=INVOKESTATIC) mv.visitIntInsn(ALOAD,0);
-                    Parameter[] para = mn.getParameters();
-                    List<Expression> argumentList = args.getExpressions();
-                    for (int i=0; i<argumentList.size(); i++) {
-                        argumentList.get(i).visit(controller.getAcg());
-                        controller.getOperandStack().doGroovyCast(para[i].getType());
-                    }
-                    
-                    String owner = BytecodeHelper.getClassInternalName(mn.getDeclaringClass());
-                    String desc = BytecodeHelper.getMethodDescriptor(mn.getReturnType(), mn.getParameters());
-                    mv.visitMethodInsn(opcode, owner, methodName, desc);
-                    ClassNode ret = mn.getReturnType().redirect();
-                    if (ret==ClassHelper.VOID_TYPE) {
-                        ret = ClassHelper.OBJECT_TYPE;
-                        mv.visitInsn(ACONST_NULL);
-                    }
-                    controller.getOperandStack().replace(ret,args.getExpressions().size());
-                    return;
-                }
-                
+                if (writeDirectMethodCall(mn, true, null, args)) return;                
             }
         }
         
+        boolean containsSpreadExpression = AsmClassGenerator.containsSpreadExpression(arguments);
+        if (!containsSpreadExpression && origin instanceof MethodCallExpression) {
+            MethodCallExpression mce = (MethodCallExpression) origin;
+            MethodNode target = mce.getMethodTarget();
+            if (writeDirectMethodCall(target, implicitThis, receiver, makeArgumentList(arguments))) return;
+        }
         
+        // prepare call site
         if ((adapter == invokeMethod || adapter == invokeMethodOnCurrent || adapter == invokeStaticMethod) && !spreadSafe) {
             String methodName = getMethodName(message);
 
@@ -170,7 +194,7 @@
         // ensure VariableArguments are read, not stored
         compileStack.pushLHS(false);
 
-        // sender
+        // sender only for call sites
         if (adapter == AsmClassGenerator.setProperty) {
             ConstantExpression.NULL.visit(acg);
         } else {
@@ -183,6 +207,7 @@
         operandStack.box();
         compileStack.popImplicitThis();
         
+        
         int operandsToRemove = 2;
         // message
         if (message != null) {
@@ -192,19 +217,9 @@
         }
 
         // arguments
-        boolean containsSpreadExpression = AsmClassGenerator.containsSpreadExpression(arguments);
         int numberOfArguments = containsSpreadExpression ? -1 : AsmClassGenerator.argumentSize(arguments);
         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);
-            }
+            ArgumentListExpression ae = makeArgumentList(arguments);
             if (containsSpreadExpression) {
                 acg.despreadList(ae.getExpressions(), true);
             } else {
@@ -227,6 +242,20 @@
         operandStack.replace(ClassHelper.OBJECT_TYPE,operandsToRemove);
     }
 
+    private ArgumentListExpression makeArgumentList(Expression arguments) {
+        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);
+        }
+        return ae;
+    }
+
     private String getMethodName(Expression message) {
         String methodName = null;
         if (message instanceof CastExpression) {
diff --git a/src/main/org/codehaus/groovy/control/ResolveVisitor.java b/src/main/org/codehaus/groovy/control/ResolveVisitor.java
index d27cb0b..1cad48c 100644
--- a/src/main/org/codehaus/groovy/control/ResolveVisitor.java
+++ b/src/main/org/codehaus/groovy/control/ResolveVisitor.java
@@ -1092,6 +1092,7 @@
         result.setSpreadSafe(mce.isSpreadSafe());
         result.setSourcePosition(mce);
         result.setGenericsTypes(mce.getGenericsTypes());
+        result.setMethodTarget(mce.getMethodTarget());
         return result;
     }
     
diff --git a/src/main/org/codehaus/groovy/control/StaticImportVisitor.java b/src/main/org/codehaus/groovy/control/StaticImportVisitor.java
index bf90604..d673722 100644
--- a/src/main/org/codehaus/groovy/control/StaticImportVisitor.java
+++ b/src/main/org/codehaus/groovy/control/StaticImportVisitor.java
@@ -277,6 +277,7 @@
         result.setSafe(mce.isSafe());

         result.setImplicitThis(mce.isImplicitThis());

         result.setSpreadSafe(mce.isSpreadSafe());

+        result.setMethodTarget(mce.getMethodTarget());

         setSourcePosition(result, mce);

         return result;

     }

diff --git a/src/test/org/codehaus/groovy/classgen/asm/AbstractBytecodeTestCase.groovy b/src/test/org/codehaus/groovy/classgen/asm/AbstractBytecodeTestCase.groovy
index a6944ae..7431a7d 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/AbstractBytecodeTestCase.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/AbstractBytecodeTestCase.groovy
@@ -23,9 +23,14 @@
      */
     InstructionSequence compile(Map options=[method:"run"], String scriptText) {
         def cu = new CompilationUnit()
-        cu.addSource("script", scriptText)
+        def su = cu.addSource("script", scriptText)
+        cu.compile(Phases.CONVERSION)
+        if (options.conversionAction!=null) {
+            options.conversionAction(su)
+        }
         cu.compile(Phases.CLASS_GENERATION)
-
+        
+        
         def output = new StringWriter()
         def tcf = new TraceClassVisitor(new PrintWriter(output)) {
             MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
diff --git a/src/test/org/codehaus/groovy/classgen/asm/DirectMethodCallTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/DirectMethodCallTest.groovy
new file mode 100644
index 0000000..1f7c272
--- /dev/null
+++ b/src/test/org/codehaus/groovy/classgen/asm/DirectMethodCallTest.groovy
@@ -0,0 +1,33 @@
+package org.codehaus.groovy.classgen.asm
+
+import java.util.List;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+
+/**
+ * @author Jochen Theodorou
+ */
+class DirectMethodCallTest extends AbstractBytecodeTestCase {
+    
+  void testVirtual() {
+      def target = ClassHelper.Integer_TYPE.getMethod("toString", new Parameter[0])
+      def makeDirectCall = {su ->
+          su. getAST().classes[0].
+              getMethod("run", new Parameter[0]).code.
+              statements.last().expression.methodTarget = target;
+      }
+      
+      assert compile (method:"run", conversionAction: makeDirectCall, """
+          def a = 1; 
+          a.toString()
+      """).hasSequence([
+              "INVOKEVIRTUAL java/lang/Integer.toString ()Ljava/lang/String;"
+      ])
+  }
+}