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