diff --git a/base/org.codehaus.groovy25/.checkstyle b/base/org.codehaus.groovy25/.checkstyle
index 935fb52d90..afc32e171d 100644
--- a/base/org.codehaus.groovy25/.checkstyle
+++ b/base/org.codehaus.groovy25/.checkstyle
@@ -46,6 +46,7 @@
+
diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/asm/CallSiteWriter.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/asm/CallSiteWriter.java
new file mode 100644
index 0000000000..b733f4cc6c
--- /dev/null
+++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/asm/CallSiteWriter.java
@@ -0,0 +1,404 @@
+/*
+ * 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.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.runtime.callsite.CallSite;
+import groovyjarjarasm.asm.Label;
+import groovyjarjarasm.asm.MethodVisitor;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import static groovyjarjarasm.asm.Opcodes.AALOAD;
+import static groovyjarjarasm.asm.Opcodes.AASTORE;
+import static groovyjarjarasm.asm.Opcodes.ACC_PRIVATE;
+import static groovyjarjarasm.asm.Opcodes.ACC_PUBLIC;
+import static groovyjarjarasm.asm.Opcodes.ACC_STATIC;
+import static groovyjarjarasm.asm.Opcodes.ACC_SYNTHETIC;
+import static groovyjarjarasm.asm.Opcodes.ACONST_NULL;
+import static groovyjarjarasm.asm.Opcodes.ALOAD;
+import static groovyjarjarasm.asm.Opcodes.ANEWARRAY;
+import static groovyjarjarasm.asm.Opcodes.ARETURN;
+import static groovyjarjarasm.asm.Opcodes.ASTORE;
+import static groovyjarjarasm.asm.Opcodes.CHECKCAST;
+import static groovyjarjarasm.asm.Opcodes.DUP;
+import static groovyjarjarasm.asm.Opcodes.GETFIELD;
+import static groovyjarjarasm.asm.Opcodes.GETSTATIC;
+import static groovyjarjarasm.asm.Opcodes.IFNONNULL;
+import static groovyjarjarasm.asm.Opcodes.IFNULL;
+import static groovyjarjarasm.asm.Opcodes.INVOKEINTERFACE;
+import static groovyjarjarasm.asm.Opcodes.INVOKESPECIAL;
+import static groovyjarjarasm.asm.Opcodes.INVOKESTATIC;
+import static groovyjarjarasm.asm.Opcodes.INVOKEVIRTUAL;
+import static groovyjarjarasm.asm.Opcodes.NEW;
+import static groovyjarjarasm.asm.Opcodes.NOP;
+import static groovyjarjarasm.asm.Opcodes.PUTSTATIC;
+import static groovyjarjarasm.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 final int SIG_ARRAY_LENGTH = 255;
+ private static String [] sig = new String [SIG_ARRAY_LENGTH];
+ private static String getCreateArraySignature(int numberOfArguments) {
+ if (numberOfArguments >= SIG_ARRAY_LENGTH) {
+ throw new IllegalArgumentException(String.format(
+ "The max number of supported arguments is %s, but found %s",
+ SIG_ARRAY_LENGTH, 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()) {
+ // GRECLIPSE add -- GROOVY-9076
+ controller.getMethodVisitor().visitInsn(NOP);
+ // GRECLIPSE end
+ 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", "", "(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 callSiteInitMethods = new LinkedList();
+ 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, "", "(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 void makeSingleArgumentCall(Expression receiver, String message, Expression arguments) {
+ 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", "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 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;
+ }
+
+ public void fallbackAttributeOrPropertySite(PropertyExpression expression, Expression objectExpression, String name, MethodCallerMultiAdapter adapter) {
+ if (controller.getCompileStack().isLHS()) controller.getOperandStack().box();
+ controller.getInvocationWriter().makeCall(
+ expression,
+ objectExpression, // receiver
+ new CastExpression(ClassHelper.STRING_TYPE, expression.getProperty()), // messageName
+ MethodCallExpression.NO_ARGUMENTS, adapter,
+ expression.isSafe(), expression.isSpreadSafe(), expression.isImplicitThis()
+ );
+ }
+}
diff --git a/base/org.codehaus.groovy30/.checkstyle b/base/org.codehaus.groovy30/.checkstyle
index 31f8e85a68..e13eecd0d0 100644
--- a/base/org.codehaus.groovy30/.checkstyle
+++ b/base/org.codehaus.groovy30/.checkstyle
@@ -45,6 +45,7 @@
+
diff --git a/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/asm/CallSiteWriter.java b/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/asm/CallSiteWriter.java
new file mode 100644
index 0000000000..3b8ed8f059
--- /dev/null
+++ b/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/asm/CallSiteWriter.java
@@ -0,0 +1,408 @@
+/*
+ * 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.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.runtime.callsite.CallSite;
+import groovyjarjarasm.asm.Label;
+import groovyjarjarasm.asm.MethodVisitor;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import static groovyjarjarasm.asm.Opcodes.AALOAD;
+import static groovyjarjarasm.asm.Opcodes.AASTORE;
+import static groovyjarjarasm.asm.Opcodes.ACC_PRIVATE;
+import static groovyjarjarasm.asm.Opcodes.ACC_PUBLIC;
+import static groovyjarjarasm.asm.Opcodes.ACC_STATIC;
+import static groovyjarjarasm.asm.Opcodes.ACC_SYNTHETIC;
+import static groovyjarjarasm.asm.Opcodes.ACONST_NULL;
+import static groovyjarjarasm.asm.Opcodes.ALOAD;
+import static groovyjarjarasm.asm.Opcodes.ANEWARRAY;
+import static groovyjarjarasm.asm.Opcodes.ARETURN;
+import static groovyjarjarasm.asm.Opcodes.ASTORE;
+import static groovyjarjarasm.asm.Opcodes.CHECKCAST;
+import static groovyjarjarasm.asm.Opcodes.DUP;
+import static groovyjarjarasm.asm.Opcodes.GETFIELD;
+import static groovyjarjarasm.asm.Opcodes.GETSTATIC;
+import static groovyjarjarasm.asm.Opcodes.IFNONNULL;
+import static groovyjarjarasm.asm.Opcodes.IFNULL;
+import static groovyjarjarasm.asm.Opcodes.INVOKEINTERFACE;
+import static groovyjarjarasm.asm.Opcodes.INVOKESPECIAL;
+import static groovyjarjarasm.asm.Opcodes.INVOKESTATIC;
+import static groovyjarjarasm.asm.Opcodes.INVOKEVIRTUAL;
+import static groovyjarjarasm.asm.Opcodes.NEW;
+import static groovyjarjarasm.asm.Opcodes.NOP;
+import static groovyjarjarasm.asm.Opcodes.PUTSTATIC;
+import static groovyjarjarasm.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 final int SIG_ARRAY_LENGTH = 255;
+ private static String [] sig = new String [SIG_ARRAY_LENGTH];
+ private static String getCreateArraySignature(int numberOfArguments) {
+ if (numberOfArguments >= SIG_ARRAY_LENGTH) {
+ throw new IllegalArgumentException(String.format(
+ "The max number of supported arguments is %s, but found %s",
+ SIG_ARRAY_LENGTH, 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()) {
+ // GRECLIPSE add -- GROOVY-9076
+ controller.getMethodVisitor().visitInsn(NOP);
+ // GRECLIPSE end
+ 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", "", "(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 callSiteInitMethods = new LinkedList();
+ 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, "", "(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 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;
+ }
+
+ public void fallbackAttributeOrPropertySite(PropertyExpression expression, Expression objectExpression, String name, MethodCallerMultiAdapter adapter) {
+ if (controller.getCompileStack().isLHS()) controller.getOperandStack().box();
+ controller.getInvocationWriter().makeCall(
+ expression,
+ objectExpression, // receiver
+ new CastExpression(ClassHelper.STRING_TYPE, expression.getProperty()), // messageName
+ MethodCallExpression.NO_ARGUMENTS, adapter,
+ expression.isSafe(), expression.isSpreadSafe(), expression.isImplicitThis()
+ );
+ }
+}