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/14 21:56:48 UTC
[23/57] [abbrv] [partial] groovy git commit: Move Java source set
into `src/main/java`
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionWriter.java
new file mode 100644
index 0000000..c03515a
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionWriter.java
@@ -0,0 +1,328 @@
+/*
+ * 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.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+import static org.codehaus.groovy.syntax.Types.BITWISE_OR;
+import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_EQUAL;
+import static org.codehaus.groovy.syntax.Types.COMPARE_TO;
+import static org.codehaus.groovy.syntax.Types.LEFT_SHIFT;
+import static org.codehaus.groovy.syntax.Types.LEFT_SQUARE_BRACKET;
+import static org.codehaus.groovy.syntax.Types.MINUS_MINUS;
+import static org.codehaus.groovy.syntax.Types.PLUS;
+import static org.codehaus.groovy.syntax.Types.PLUS_PLUS;
+import static org.objectweb.asm.Opcodes.GOTO;
+import static org.objectweb.asm.Opcodes.ICONST_0;
+import static org.objectweb.asm.Opcodes.ICONST_1;
+import static org.objectweb.asm.Opcodes.ICONST_M1;
+import static org.objectweb.asm.Opcodes.IFEQ;
+import static org.objectweb.asm.Opcodes.IFGE;
+import static org.objectweb.asm.Opcodes.IFGT;
+import static org.objectweb.asm.Opcodes.IFLE;
+import static org.objectweb.asm.Opcodes.IFLT;
+import static org.objectweb.asm.Opcodes.IFNE;
+
+/**
+ * Base class for writing primitive typed operations
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public abstract class BinaryExpressionWriter {
+
+ private final WriterController controller;
+ private MethodCaller arraySet, arrayGet;
+
+ public BinaryExpressionWriter(WriterController controller, MethodCaller arraySet, MethodCaller arrayGet) {
+ this.controller = controller;
+ this.arraySet = arraySet;
+ this.arrayGet = arrayGet;
+ }
+
+ /**
+ * return writer controller
+ * @since 2.5.0
+ */
+ public WriterController getController() {
+ return controller;
+ }
+
+ protected static final int[] stdCompareCodes = {
+ IFEQ, // COMPARE_NOT_EQUAL 120
+ IFNE, // COMPARE_IDENTICAL 121
+ IFEQ, // COMPARE_NOT_IDENTICAL 122
+ IFNE, // COMPARE_EQUAL 123
+ IFGE, // COMPARE_LESS_THAN 124
+ IFGT, // COMPARE_LESS_THAN_EQUAL 125
+ IFLE, // COMPARE_GREATER_THAN 126
+ IFLT, // COMPARE_GREATER_THAN_EQUAL 127
+ };
+
+ protected abstract int getCompareCode();
+
+ /**
+ * writes some int standard operations for compares
+ * @param type the token type
+ * @return true if a successful std operator write
+ */
+ protected boolean writeStdCompare(int type, boolean simulate) {
+ type = type-COMPARE_NOT_EQUAL;
+ // look if really compare
+ if (type<0||type>7) return false;
+
+ if (!simulate) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ OperandStack operandStack = controller.getOperandStack();
+ // operands are on the stack already
+ int bytecode = stdCompareCodes[type];
+ mv.visitInsn(getCompareCode());
+ Label l1 = new Label();
+ mv.visitJumpInsn(bytecode,l1);
+ mv.visitInsn(ICONST_1);
+ Label l2 = new Label();
+ mv.visitJumpInsn(GOTO, l2);
+ mv.visitLabel(l1);
+ mv.visitInsn(ICONST_0);
+ mv.visitLabel(l2);
+ operandStack.replace(ClassHelper.boolean_TYPE, 2);
+ }
+ return true;
+ }
+
+ protected abstract void doubleTwoOperands(MethodVisitor mv);
+ protected abstract void removeTwoOperands(MethodVisitor mv);
+
+ protected boolean writeSpaceship(int type, boolean simulate) {
+ if (type != COMPARE_TO) return false;
+ /*
+ we will actually do
+
+ (x < y) ? -1 : ((x == y) ? 0 : 1)
+ which is the essence of what the call with Number would do
+ this compiles to something along
+
+ <x>
+ <y>
+ LCMP
+ IFGE L1
+ ICONST_M1
+ GOTO L2
+ L1
+ <x>
+ <y>
+ LCMP
+ IFNE L3
+ ICONST_0
+ GOTO L2
+ L3
+ ICONST_1
+ L2
+
+ since the operators are already on the stack and we don't want
+ to load them again, we will instead duplicate them. This will
+ require some pop actions in the branches!
+
+ DUP4 (operands: L1L2L1L2)
+ LCMP
+ IFGE L1 (operands: L1L2)
+ ICONST_M1 (operands: L1L2I)
+ GOTO L2
+ L1
+ ----- (operands: L1L2)
+ LCMP
+ IFNE L3 (operands: -)
+ ICONST_0 (operands: I)
+ GOTO L2
+ L3
+ - jump from L1 branch to here (operands: -)
+ ICONST_1 (operands: I)
+ L2
+ - if jump from GOTO L2 we have LLI, but need only I
+ - if from L3 branch we get only I
+
+ this means we have to pop of LL before loading -1
+
+ since there is no DUP4 we have to do this:
+ DUP2_X1
+ POP2
+ DUP2_X1
+ DUP2_X1
+ POP2
+ DUP2_X1
+ */
+ if (!simulate) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ // duplicate arguments
+ doubleTwoOperands(mv);
+
+ Label l1 = new Label();
+ mv.visitInsn(getCompareCode());
+ mv.visitJumpInsn(IFGE,l1);
+ // no jump, so -1, need to pop off surplus LL
+ removeTwoOperands(mv);
+ mv.visitInsn(ICONST_M1);
+ Label l2 = new Label();
+ mv.visitJumpInsn(GOTO, l2);
+
+ mv.visitLabel(l1);
+ Label l3 = new Label();
+ mv.visitInsn(getCompareCode());
+ mv.visitJumpInsn(IFNE,l3);
+ mv.visitInsn(ICONST_0);
+ mv.visitJumpInsn(GOTO,l2);
+
+ mv.visitLabel(l3);
+ mv.visitInsn(ICONST_1);
+
+ controller.getOperandStack().replace(ClassHelper.int_TYPE, 2);
+ }
+ return true;
+ }
+
+ protected abstract ClassNode getNormalOpResultType();
+ protected abstract int getStandardOperationBytecode(int type);
+
+ protected boolean writeStdOperators(int type, boolean simulate) {
+ type = type-PLUS;
+ if (type<0 || type>5 || type == 3 /*DIV*/) return false;
+
+ if (!simulate) {
+ int bytecode = getStandardOperationBytecode(type);
+ controller.getMethodVisitor().visitInsn(bytecode);
+ controller.getOperandStack().replace(getNormalOpResultType(), 2);
+ }
+ return true;
+ }
+
+ protected boolean writeDivision(boolean simulate) {
+ if (!supportsDivision()) return false;
+ if (!simulate) {
+ int bytecode = getStandardOperationBytecode(3 /*DIV*/);
+ controller.getMethodVisitor().visitInsn(bytecode);
+ controller.getOperandStack().replace(getDevisionOpResultType(), 2);
+ }
+ return true;
+ }
+
+ protected boolean supportsDivision() {
+ return false;
+ }
+
+ protected abstract ClassNode getDevisionOpResultType();
+
+ protected abstract int getBitwiseOperationBytecode(int type);
+
+ /**
+ * writes some the bitwise operations. type is one of BITWISE_OR,
+ * BITWISE_AND, BITWISE_XOR
+ * @param type the token type
+ * @return true if a successful bitwise operation write
+ */
+ protected boolean writeBitwiseOp(int type, boolean simulate) {
+ type = type-BITWISE_OR;
+ if (type<0 || type>2) return false;
+
+ if (!simulate) {
+ int bytecode = getBitwiseOperationBytecode(type);
+ controller.getMethodVisitor().visitInsn(bytecode);
+ controller.getOperandStack().replace(getNormalOpResultType(), 2);
+ }
+ return true;
+ }
+
+ protected abstract int getShiftOperationBytecode(int type);
+
+ /**
+ * Write shifting operations.
+ * Type is one of LEFT_SHIFT, RIGHT_SHIFT, or RIGHT_SHIFT_UNSIGNED
+ *
+ * @param type the token type
+ * @return true on a successful shift operation write
+ */
+ protected boolean writeShiftOp(int type, boolean simulate) {
+ type = type - LEFT_SHIFT;
+ if (type < 0 || type > 2) return false;
+
+ if (!simulate) {
+ int bytecode = getShiftOperationBytecode(type);
+ controller.getMethodVisitor().visitInsn(bytecode);
+ controller.getOperandStack().replace(getNormalOpResultType(), 2);
+ }
+ return true;
+ }
+
+ public boolean write(int operation, boolean simulate) {
+ return writeStdCompare(operation, simulate) ||
+ writeSpaceship(operation, simulate) ||
+ writeStdOperators(operation, simulate) ||
+ writeBitwiseOp(operation, simulate) ||
+ writeShiftOp(operation, simulate);
+ }
+
+ protected MethodCaller getArrayGetCaller() {
+ return arrayGet;
+ }
+
+ protected ClassNode getArrayGetResultType(){
+ return getNormalOpResultType();
+ }
+
+ protected MethodCaller getArraySetCaller() {
+ return arraySet;
+ }
+
+ public void setArraySetAndGet(MethodCaller arraySet, MethodCaller arrayGet) {
+ this.arraySet = arraySet;
+ this.arrayGet = arrayGet;
+ }
+
+ public boolean arrayGet(int operation, boolean simulate) {
+ if (operation!=LEFT_SQUARE_BRACKET) return false;
+
+ if (!simulate) {
+ getArrayGetCaller().call(controller.getMethodVisitor());
+ }
+ return true;
+ }
+
+ public boolean arraySet(boolean simulate) {
+ if (!simulate) {
+ getArraySetCaller().call(controller.getMethodVisitor());
+ }
+ return true;
+ }
+
+ public boolean writePostOrPrefixMethod(int operation, boolean simulate) {
+ if (operation!=PLUS_PLUS && operation!=MINUS_MINUS) return false;
+ if (!simulate) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ if (operation==PLUS_PLUS) {
+ writePlusPlus(mv);
+ } else {
+ writeMinusMinus(mv);
+ }
+ }
+ return true;
+ }
+
+ protected abstract void writePlusPlus(MethodVisitor mv);
+ protected abstract void writeMinusMinus(MethodVisitor mv);
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/BinaryFloatExpressionHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BinaryFloatExpressionHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryFloatExpressionHelper.java
new file mode 100644
index 0000000..6a4552a
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryFloatExpressionHelper.java
@@ -0,0 +1,111 @@
+/*
+ * 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.runtime.BytecodeInterface8;
+import org.objectweb.asm.MethodVisitor;
+
+import static org.objectweb.asm.Opcodes.DUP2;
+import static org.objectweb.asm.Opcodes.FADD;
+import static org.objectweb.asm.Opcodes.FCMPG;
+import static org.objectweb.asm.Opcodes.FCONST_1;
+import static org.objectweb.asm.Opcodes.FDIV;
+import static org.objectweb.asm.Opcodes.FMUL;
+import static org.objectweb.asm.Opcodes.FREM;
+import static org.objectweb.asm.Opcodes.FSUB;
+import static org.objectweb.asm.Opcodes.POP2;
+
+/**
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class BinaryFloatExpressionHelper extends BinaryExpressionWriter {
+
+ public BinaryFloatExpressionHelper(WriterController controller) {
+ super(controller, floatArraySet, floatArrayGet);
+ }
+
+ protected void doubleTwoOperands(MethodVisitor mv) {
+ mv.visitInsn(DUP2);
+ }
+
+ private static final MethodCaller
+ floatArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "fArrayGet"),
+ floatArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "fArraySet");
+
+ protected boolean writeBitwiseOp(int type, boolean simulate) {
+ if (!simulate) throw new GroovyBugError("should not reach here");
+ return false;
+ }
+
+ protected int getBitwiseOperationBytecode(int type) {
+ return -1;
+ }
+
+ protected int getCompareCode() {
+ return FCMPG;
+ }
+
+ protected ClassNode getNormalOpResultType() {
+ return ClassHelper.float_TYPE;
+ }
+
+ protected boolean writeShiftOp(int type, boolean simulate) {
+ if (!simulate) throw new GroovyBugError("should not reach here");
+ return false;
+ }
+
+ protected int getShiftOperationBytecode(int type) {
+ return -1;
+ }
+
+ private static final int[] stdOperations = {
+ FADD, // PLUS 200
+ FSUB, // MINUS 201
+ FMUL, // MULTIPLY 202
+ 0, // DIV, (203) but we don't want that one
+ FDIV, // INTDIV 204
+ FREM, // MOD 203
+ };
+
+ protected int getStandardOperationBytecode(int type) {
+ return stdOperations[type];
+ }
+
+ protected void removeTwoOperands(MethodVisitor mv) {
+ mv.visitInsn(POP2);
+ }
+
+ protected void writeMinusMinus(MethodVisitor mv) {
+ mv.visitInsn(FCONST_1);
+ mv.visitInsn(FSUB);
+ }
+
+ protected void writePlusPlus(MethodVisitor mv) {
+ mv.visitInsn(FCONST_1);
+ mv.visitInsn(FADD);
+ }
+
+ protected ClassNode getDevisionOpResultType() {
+ return ClassHelper.BigDecimal_TYPE;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/BinaryIntExpressionHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BinaryIntExpressionHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryIntExpressionHelper.java
new file mode 100644
index 0000000..dfa1245
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryIntExpressionHelper.java
@@ -0,0 +1,293 @@
+/*
+ * 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.runtime.BytecodeInterface8;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_EQUAL;
+import static org.codehaus.groovy.syntax.Types.COMPARE_TO;
+import static org.objectweb.asm.Opcodes.DUP2;
+import static org.objectweb.asm.Opcodes.GOTO;
+import static org.objectweb.asm.Opcodes.IADD;
+import static org.objectweb.asm.Opcodes.IAND;
+import static org.objectweb.asm.Opcodes.ICONST_0;
+import static org.objectweb.asm.Opcodes.ICONST_1;
+import static org.objectweb.asm.Opcodes.ICONST_M1;
+import static org.objectweb.asm.Opcodes.IDIV;
+import static org.objectweb.asm.Opcodes.IF_ICMPEQ;
+import static org.objectweb.asm.Opcodes.IF_ICMPGE;
+import static org.objectweb.asm.Opcodes.IF_ICMPGT;
+import static org.objectweb.asm.Opcodes.IF_ICMPLE;
+import static org.objectweb.asm.Opcodes.IF_ICMPLT;
+import static org.objectweb.asm.Opcodes.IF_ICMPNE;
+import static org.objectweb.asm.Opcodes.IMUL;
+import static org.objectweb.asm.Opcodes.IOR;
+import static org.objectweb.asm.Opcodes.IREM;
+import static org.objectweb.asm.Opcodes.ISHL;
+import static org.objectweb.asm.Opcodes.ISHR;
+import static org.objectweb.asm.Opcodes.ISUB;
+import static org.objectweb.asm.Opcodes.IUSHR;
+import static org.objectweb.asm.Opcodes.IXOR;
+import static org.objectweb.asm.Opcodes.POP2;
+
+public class BinaryIntExpressionHelper extends BinaryExpressionWriter {
+
+ private static final MethodCaller intArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "intArrayGet");
+ private static final MethodCaller intArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "intArraySet");
+
+ private static final int[] stdCompareCodes = {
+ IF_ICMPEQ, // COMPARE_NOT_EQUAL 120
+ IF_ICMPNE, // COMPARE_IDENTICAL 121
+ IF_ICMPEQ, // COMPARE_NOT_IDENTICAL 122
+ IF_ICMPNE, // COMPARE_EQUAL 123
+ IF_ICMPGE, // COMPARE_LESS_THAN 124
+ IF_ICMPGT, // COMPARE_LESS_THAN_EQUAL 125
+ IF_ICMPLE, // COMPARE_GREATER_THAN 126
+ IF_ICMPLT, // COMPARE_GREATER_THAN_EQUAL 127
+ };
+
+ private static final int[] stdOperations = {
+ IADD, // PLUS 200
+ ISUB, // MINUS 201
+ IMUL, // MULTIPLY 202
+ IDIV, // DIV 203
+ IDIV, // INTDIV 204
+ IREM, // MOD 203
+ };
+
+ private static final int[] bitOp = {
+ IOR, // BITWISE_OR / PIPE 340
+ IAND, // BITWISE_AND 341
+ IXOR, // BIWISE_XOR 342
+ };
+
+ /* unhandled types from from org.codehaus.groovy.syntax.Types
+ public static final int LOGICAL_OR = 162; // ||
+ public static final int LOGICAL_AND = 164; // &&
+
+ public static final int DIVIDE = 203; // /
+ public static final int STAR_STAR = 206; // **
+ public static final int POWER = STAR_STAR; //
+
+ public static final int PLUS_EQUAL = 210; // +=
+ public static final int MINUS_EQUAL = 211; // -=
+ public static final int MULTIPLY_EQUAL = 212; // *=
+ public static final int DIVIDE_EQUAL = 213; // /=
+ public static final int INTDIV_EQUAL = 214; // \=
+ public static final int MOD_EQUAL = 215; // %=
+ public static final int POWER_EQUAL = 216; // **=
+
+ public static final int PLUS_PLUS = 250; // ++
+ public static final int PREFIX_PLUS_PLUS = 251; // ++
+ public static final int POSTFIX_PLUS_PLUS = 252; // ++
+ public static final int PREFIX_PLUS = 253; // +
+
+ public static final int MINUS_MINUS = 260; // --
+ public static final int PREFIX_MINUS_MINUS = 261; // --
+ public static final int POSTFIX_MINUS_MINUS = 262; // --
+ public static final int PREFIX_MINUS = 263; // - (negation)
+*/
+ private static final int[] shiftOp = {
+ ISHL, // LEFT_SHIFT 280
+ ISHR, // RIGHT_SHIFT 281
+ IUSHR // RIGHT_SHIFT_UNSIGNED 282
+ };
+
+/*
+ public static final int LEFT_SHIFT_EQUAL = 285; // <<=
+ public static final int RIGHT_SHIFT_EQUAL = 286; // >>=
+ public static final int RIGHT_SHIFT_UNSIGNED_EQUAL = 287; // >>>=
+
+ public static final int BITWISE_OR_EQUAL = 350; // |=
+ public static final int BITWISE_AND_EQUAL = 351; // &=
+ public static final int BITWISE_XOR_EQUAL = 352; // ^=
+ public static final int BITWISE_NEGATION = REGEX_PATTERN; // ~
+ */
+
+ public BinaryIntExpressionHelper(WriterController wc) {
+ this(wc, intArraySet, intArrayGet);
+ }
+
+ /**
+ * @since 2.5.0
+ */
+ public BinaryIntExpressionHelper(WriterController wc, MethodCaller arraySet, MethodCaller arrayGet) {
+ super(wc, arraySet, arrayGet);
+ }
+
+
+ /**
+ * writes a std compare. This involves the tokens IF_ICMPEQ, IF_ICMPNE,
+ * IF_ICMPEQ, IF_ICMPNE, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE and IF_ICMPLT
+ * @param type the token type
+ * @return true if a successful std compare write
+ */
+ protected boolean writeStdCompare(int type, boolean simulate) {
+ type = type-COMPARE_NOT_EQUAL;
+ // look if really compare
+ if (type<0||type>7) return false;
+
+ if (!simulate) {
+ MethodVisitor mv = getController().getMethodVisitor();
+ OperandStack operandStack = getController().getOperandStack();
+ // operands are on the stack already
+ int bytecode = stdCompareCodes[type];
+ Label l1 = new Label();
+ mv.visitJumpInsn(bytecode,l1);
+ mv.visitInsn(ICONST_1);
+ Label l2 = new Label();
+ mv.visitJumpInsn(GOTO, l2);
+ mv.visitLabel(l1);
+ mv.visitInsn(ICONST_0);
+ mv.visitLabel(l2);
+ operandStack.replace(ClassHelper.boolean_TYPE, 2);
+ }
+ return true;
+ }
+
+ /**
+ * writes the spaceship operator, type should be COMPARE_TO
+ * @param type the token type
+ * @return true if a successful spaceship operator write
+ */
+ protected boolean writeSpaceship(int type, boolean simulate) {
+ if (type != COMPARE_TO) return false;
+ /*
+ we will actually do
+
+ (x < y) ? -1 : ((x == y) ? 0 : 1)
+ which is the essence of what the call with Integers would do
+ this compiles to something along
+
+ <x>
+ <y>
+ IF_ICMPGE L1
+ ICONST_M1
+ GOTO L2
+ L1
+ <x>
+ <y>
+ IF_ICMPNE L3
+ ICONST_0
+ GOTO L2
+ L3
+ ICONST_1
+ L2
+
+ since the operators are already on the stack and we don't want
+ to load them again, we will instead duplicate them. This will
+ require some pop actions in the branches!
+
+ DUP2 (operands: IIII)
+ IF_ICMPGE L1 (operands: II)
+ ICONST_M1 (operands: III)
+ GOTO L2
+ L1
+ ----- (operands: II)
+ IF_ICMPNE L3 (operands: -)
+ ICONST_0 (operands: I)
+ GOTO L2
+ L3
+ - jump from L1 branch to here (operands: -)
+ ICONST_1 (operands: I)
+ L2
+ - if jump from GOTO L2 we have III, but need only I
+ - if from L3 branch we get only I
+
+ this means we have to pop of II before loading -1
+
+ */
+ if (!simulate) {
+ MethodVisitor mv = getController().getMethodVisitor();
+ // duplicate int arguments
+ mv.visitInsn(DUP2);
+
+ Label l1 = new Label();
+ mv.visitJumpInsn(IF_ICMPGE,l1);
+ // no jump, so -1, need to pop off surplus II
+ mv.visitInsn(POP2);
+ mv.visitInsn(ICONST_M1);
+ Label l2 = new Label();
+ mv.visitJumpInsn(GOTO, l2);
+
+ mv.visitLabel(l1);
+ Label l3 = new Label();
+ mv.visitJumpInsn(IF_ICMPNE,l3);
+ mv.visitInsn(ICONST_0);
+ mv.visitJumpInsn(GOTO,l2);
+
+ mv.visitLabel(l3);
+ mv.visitInsn(ICONST_1);
+
+ getController().getOperandStack().replace(ClassHelper.int_TYPE, 2);
+ }
+ return true;
+ }
+
+ protected void doubleTwoOperands(MethodVisitor mv) {
+ mv.visitInsn(DUP2);
+ }
+
+ protected int getBitwiseOperationBytecode(int type) {
+ return bitOp[type];
+ }
+
+ protected int getCompareCode() {
+ return -1;
+ }
+
+ protected ClassNode getNormalOpResultType() {
+ return ClassHelper.int_TYPE;
+ }
+
+ protected int getShiftOperationBytecode(int type) {
+ return shiftOp[type];
+ }
+
+ protected int getStandardOperationBytecode(int type) {
+ return stdOperations[type];
+ }
+
+ protected void removeTwoOperands(MethodVisitor mv) {
+ mv.visitInsn(POP2);
+ }
+
+ protected void writeMinusMinus(MethodVisitor mv) {
+ mv.visitInsn(ICONST_1);
+ mv.visitInsn(ISUB);
+ }
+
+ protected void writePlusPlus(MethodVisitor mv) {
+ mv.visitInsn(ICONST_1);
+ mv.visitInsn(IADD);
+ }
+
+ protected ClassNode getDevisionOpResultType() {
+ return ClassHelper.int_TYPE;
+ }
+
+ @Override
+ protected boolean supportsDivision() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/BinaryLongExpressionHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BinaryLongExpressionHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryLongExpressionHelper.java
new file mode 100644
index 0000000..8bfaf98
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryLongExpressionHelper.java
@@ -0,0 +1,144 @@
+/*
+ * 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.runtime.BytecodeInterface8;
+import org.objectweb.asm.MethodVisitor;
+
+import static org.objectweb.asm.Opcodes.DUP2_X1;
+import static org.objectweb.asm.Opcodes.LADD;
+import static org.objectweb.asm.Opcodes.LAND;
+import static org.objectweb.asm.Opcodes.LCMP;
+import static org.objectweb.asm.Opcodes.LCONST_1;
+import static org.objectweb.asm.Opcodes.LDIV;
+import static org.objectweb.asm.Opcodes.LMUL;
+import static org.objectweb.asm.Opcodes.LOR;
+import static org.objectweb.asm.Opcodes.LREM;
+import static org.objectweb.asm.Opcodes.LSHL;
+import static org.objectweb.asm.Opcodes.LSHR;
+import static org.objectweb.asm.Opcodes.LSUB;
+import static org.objectweb.asm.Opcodes.LUSHR;
+import static org.objectweb.asm.Opcodes.LXOR;
+import static org.objectweb.asm.Opcodes.POP2;
+
+/**
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class BinaryLongExpressionHelper extends BinaryExpressionWriter {
+
+ /**
+ * @since 2.5.0
+ */
+ public BinaryLongExpressionHelper(WriterController controller, MethodCaller arraySet, MethodCaller arrayGet) {
+ super(controller, arraySet, arrayGet);
+ }
+
+ public BinaryLongExpressionHelper(WriterController controller) {
+ this(controller, longArraySet, longArrayGet);
+ }
+
+ protected void doubleTwoOperands(MethodVisitor mv) {
+ /*
+ since there is no DUP4 we have to do this:
+ DUP2_X1
+ POP2
+ DUP2_X1
+ DUP2_X1
+ POP2
+ DUP2_X1
+ */
+ mv.visitInsn(DUP2_X1);
+ mv.visitInsn(POP2);
+ mv.visitInsn(DUP2_X1);
+ mv.visitInsn(DUP2_X1);
+ mv.visitInsn(POP2);
+ mv.visitInsn(DUP2_X1);
+ }
+
+ protected void removeTwoOperands(MethodVisitor mv) {
+ mv.visitInsn(POP2);
+ mv.visitInsn(POP2);
+ }
+
+ private static final MethodCaller
+ longArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "lArrayGet"),
+ longArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "lArraySet");
+
+ private static final int[] bitOp = {
+ LOR, // BITWISE_OR / PIPE 340
+ LAND, // BITWISE_AND 341
+ LXOR, // BIWISE_XOR 342
+ };
+
+ protected int getBitwiseOperationBytecode(int type) {
+ return bitOp[type];
+ }
+
+ protected int getCompareCode() {
+ return LCMP;
+ }
+
+ protected ClassNode getNormalOpResultType() {
+ return ClassHelper.long_TYPE;
+ }
+
+ private static final int[] shiftOp = {
+ LSHL, // LEFT_SHIFT 280
+ LSHR, // RIGHT_SHIFT 281
+ LUSHR // RIGHT_SHIFT_UNSIGNED 282
+ };
+
+ protected int getShiftOperationBytecode(int type) {
+ return shiftOp[type];
+ }
+
+ private static final int[] stdOperations = {
+ LADD, // PLUS 200
+ LSUB, // MINUS 201
+ LMUL, // MULTIPLY 202
+ LDIV, // DIV 203
+ LDIV, // INTDIV 204
+ LREM, // MOD 203
+ };
+
+ protected int getStandardOperationBytecode(int type) {
+ return stdOperations[type];
+ }
+
+ protected void writeMinusMinus(MethodVisitor mv) {
+ mv.visitInsn(LCONST_1);
+ mv.visitInsn(LSUB);
+ }
+
+ protected void writePlusPlus(MethodVisitor mv) {
+ mv.visitInsn(LCONST_1);
+ mv.visitInsn(LADD);
+ }
+
+ protected ClassNode getDevisionOpResultType() {
+ return ClassHelper.long_TYPE;
+ }
+
+ @Override
+ protected boolean supportsDivision() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/BinaryObjectExpressionHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BinaryObjectExpressionHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryObjectExpressionHelper.java
new file mode 100644
index 0000000..31350d5
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryObjectExpressionHelper.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.groovy.classgen.asm;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.runtime.BytecodeInterface8;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class BinaryObjectExpressionHelper extends BinaryExpressionWriter {
+ private static final MethodCaller arrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "objectArrayGet");
+ private static final MethodCaller arraySet = MethodCaller.newStatic(BytecodeInterface8.class, "objectArraySet");
+
+ public BinaryObjectExpressionHelper(WriterController controller) {
+ super(controller, arraySet, arrayGet);
+ }
+
+ // dummy methods
+ public boolean writePostOrPrefixMethod(int operation, boolean simulate) {
+ if (simulate) return false;
+ throw new GroovyBugError("should not reach here");
+ }
+
+ public boolean write(int operation, boolean simulate) {
+ if (simulate) return false;
+ throw new GroovyBugError("should not reach here");
+ }
+
+ protected boolean writeDivision(boolean simulate) {
+ if (simulate) return false;
+ throw new GroovyBugError("should not reach here");
+ }
+
+ protected int getBitwiseOperationBytecode(int type) {
+ return -1;
+ }
+
+ protected int getCompareCode() {
+ return -1;
+ }
+
+ protected ClassNode getNormalOpResultType() {
+ return null;
+ }
+
+ protected ClassNode getDevisionOpResultType() {
+ return null;
+ }
+
+ protected int getShiftOperationBytecode(int type) {
+ return -1;
+ }
+
+ protected int getStandardOperationBytecode(int type) {
+ return -1;
+ }
+
+ protected void removeTwoOperands(MethodVisitor mv) {}
+ protected void writePlusPlus(MethodVisitor mv) {}
+ protected void writeMinusMinus(MethodVisitor mv) {}
+ protected void doubleTwoOperands(MethodVisitor mv) {}
+
+ @Override
+ protected ClassNode getArrayGetResultType() {
+ return ClassHelper.OBJECT_TYPE;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeDumper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeDumper.java b/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeDumper.java
new file mode 100644
index 0000000..935020f
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeDumper.java
@@ -0,0 +1,52 @@
+/*
+ * 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.control.BytecodeProcessor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.util.TraceClassVisitor;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+
+/**
+ * An utility class which can be used in test cases to dump generated bytecode.
+ *
+ * @author Cédric Champeau
+ * @since 2.4.0
+ */
+public class BytecodeDumper implements BytecodeProcessor {
+ public static final BytecodeDumper STANDARD_ERR = new BytecodeDumper(new PrintWriter(System.err));
+
+ private final Writer out;
+
+ public BytecodeDumper(final Writer out) {
+ this.out = out;
+ }
+
+ @Override
+ public byte[] processBytecode(final String name, final byte[] original) {
+ PrintWriter pw = out instanceof PrintWriter ? (PrintWriter) out : new PrintWriter(out);
+ TraceClassVisitor visitor = new TraceClassVisitor(pw);
+ ClassReader reader = new ClassReader(original);
+ reader.accept(visitor, 0);
+ return original;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeHelper.java
new file mode 100644
index 0000000..d9eea08
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeHelper.java
@@ -0,0 +1,751 @@
+/*
+ * 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.CompileUnit;
+import org.codehaus.groovy.ast.GenericsType;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.decompiled.DecompiledClassNode;
+import org.codehaus.groovy.reflection.ReflectionCache;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * A helper class for bytecode generation with AsmClassGenerator.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ */
+public class BytecodeHelper implements Opcodes {
+
+ private static String DTT_CLASSNAME = BytecodeHelper.getClassInternalName(DefaultTypeTransformation.class.getName());
+
+ public static String getClassInternalName(ClassNode t) {
+ if (t.isPrimaryClassNode() || t instanceof DecompiledClassNode) {
+ if (t.isArray()) return "[L"+getClassInternalName(t.getComponentType())+";";
+ return getClassInternalName(t.getName());
+ }
+ return getClassInternalName(t.getTypeClass());
+ }
+
+ public static String getClassInternalName(Class t) {
+ return org.objectweb.asm.Type.getInternalName(t);
+ }
+
+ /**
+ * @return the ASM internal name of the type
+ */
+ public static String getClassInternalName(String name) {
+ return name.replace('.', '/');
+ }
+
+ public static String getMethodDescriptor(ClassNode returnType, Parameter[] parameters) {
+ StringBuilder buffer = new StringBuilder("(");
+ for (int i = 0; i < parameters.length; i++) {
+ buffer.append(getTypeDescription(parameters[i].getType()));
+ }
+ buffer.append(")");
+ buffer.append(getTypeDescription(returnType));
+ return buffer.toString();
+ }
+
+ /**
+ * Returns a method descriptor for the given {@link org.codehaus.groovy.ast.MethodNode}.
+ *
+ * @param methodNode the method node for which to create the descriptor
+ * @return a method descriptor as defined in section JVMS section 4.3.3
+ */
+ public static String getMethodDescriptor(MethodNode methodNode) {
+ return getMethodDescriptor(methodNode.getReturnType(), methodNode.getParameters());
+ }
+
+ /**
+ * @return the ASM method type descriptor
+ */
+ public static String getMethodDescriptor(Class returnType, Class[] paramTypes) {
+ // lets avoid class loading
+ StringBuilder buffer = new StringBuilder("(");
+ for (int i = 0; i < paramTypes.length; i++) {
+ buffer.append(getTypeDescription(paramTypes[i]));
+ }
+ buffer.append(")");
+ buffer.append(getTypeDescription(returnType));
+ return buffer.toString();
+ }
+
+ public static String getTypeDescription(Class c) {
+ return org.objectweb.asm.Type.getDescriptor(c);
+ }
+
+ /**
+ * array types are special:
+ * eg.: String[]: classname: [Ljava.lang.String;
+ * Object: classname: java.lang.Object
+ * int[] : classname: [I
+ * unlike getTypeDescription '.' is not replaced by '/'.
+ * it seems that makes problems for
+ * the class loading if '.' is replaced by '/'
+ *
+ * @return the ASM type description for class loading
+ */
+ public static String getClassLoadingTypeDescription(ClassNode c) {
+ StringBuilder buf = new StringBuilder();
+ boolean array = false;
+ while (true) {
+ if (c.isArray()) {
+ buf.append('[');
+ c = c.getComponentType();
+ array = true;
+ } else {
+ if (ClassHelper.isPrimitiveType(c)) {
+ buf.append(getTypeDescription(c));
+ } else {
+ if (array) buf.append('L');
+ buf.append(c.getName());
+ if (array) buf.append(';');
+ }
+ return buf.toString();
+ }
+ }
+ }
+
+ /**
+ * array types are special:
+ * eg.: String[]: classname: [Ljava/lang/String;
+ * int[]: [I
+ *
+ * @return the ASM type description
+ */
+ public static String getTypeDescription(ClassNode c) {
+ return getTypeDescription(c, true);
+ }
+
+ /**
+ * array types are special:
+ * eg.: String[]: classname: [Ljava/lang/String;
+ * int[]: [I
+ *
+ * @return the ASM type description
+ */
+ private static String getTypeDescription(ClassNode c, boolean end) {
+ StringBuilder buf = new StringBuilder();
+ ClassNode d = c;
+ while (true) {
+ if (ClassHelper.isPrimitiveType(d.redirect())) {
+ d = d.redirect();
+ char car;
+ if (d == ClassHelper.int_TYPE) {
+ car = 'I';
+ } else if (d == ClassHelper.VOID_TYPE) {
+ car = 'V';
+ } else if (d == ClassHelper.boolean_TYPE) {
+ car = 'Z';
+ } else if (d == ClassHelper.byte_TYPE) {
+ car = 'B';
+ } else if (d == ClassHelper.char_TYPE) {
+ car = 'C';
+ } else if (d == ClassHelper.short_TYPE) {
+ car = 'S';
+ } else if (d == ClassHelper.double_TYPE) {
+ car = 'D';
+ } else if (d == ClassHelper.float_TYPE) {
+ car = 'F';
+ } else /* long */ {
+ car = 'J';
+ }
+ buf.append(car);
+ return buf.toString();
+ } else if (d.isArray()) {
+ buf.append('[');
+ d = d.getComponentType();
+ } else {
+ buf.append('L');
+ String name = d.getName();
+ int len = name.length();
+ for (int i = 0; i < len; ++i) {
+ char car = name.charAt(i);
+ buf.append(car == '.' ? '/' : car);
+ }
+ if (end) buf.append(';');
+ return buf.toString();
+ }
+ }
+ }
+
+ /**
+ * @return an array of ASM internal names of the type
+ */
+ public static String[] getClassInternalNames(ClassNode[] names) {
+ int size = names.length;
+ String[] answer = new String[size];
+ for (int i = 0; i < size; i++) {
+ answer[i] = getClassInternalName(names[i]);
+ }
+ return answer;
+ }
+
+ public static void pushConstant(MethodVisitor mv, int value) {
+ switch (value) {
+ case 0:
+ mv.visitInsn(ICONST_0);
+ break;
+ case 1:
+ mv.visitInsn(ICONST_1);
+ break;
+ case 2:
+ mv.visitInsn(ICONST_2);
+ break;
+ case 3:
+ mv.visitInsn(ICONST_3);
+ break;
+ case 4:
+ mv.visitInsn(ICONST_4);
+ break;
+ case 5:
+ mv.visitInsn(ICONST_5);
+ break;
+ default:
+ if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
+ mv.visitIntInsn(BIPUSH, value);
+ } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
+ mv.visitIntInsn(SIPUSH, value);
+ } else {
+ mv.visitLdcInsn(Integer.valueOf(value));
+ }
+ }
+ }
+
+ /**
+ * negate a boolean on stack. true->false, false->true
+ */
+ public static void negateBoolean(MethodVisitor mv) {
+ // code to negate the primitive boolean
+ Label endLabel = new Label();
+ Label falseLabel = new Label();
+ mv.visitJumpInsn(IFNE, falseLabel);
+ mv.visitInsn(ICONST_1);
+ mv.visitJumpInsn(GOTO, endLabel);
+ mv.visitLabel(falseLabel);
+ mv.visitInsn(ICONST_0);
+ mv.visitLabel(endLabel);
+ }
+
+ /**
+ * load a message on the stack and remove it right away. Good for put a mark in the generated bytecode for debugging purpose.
+ *
+ * @param msg
+ */
+ /*public void mark(String msg) {
+ mv.visitLdcInsn(msg);
+ mv.visitInsn(POP);
+ }*/
+
+ /**
+ * returns a name that Class.forName() can take. Notably for arrays:
+ * [I, [Ljava.lang.String; etc
+ * Regular object type: java.lang.String
+ *
+ * @param name
+ */
+ public static String formatNameForClassLoading(String name) {
+ if (name == null) {
+ return "java.lang.Object;";
+ }
+
+ if (name.equals("int")
+ || name.equals("long")
+ || name.equals("short")
+ || name.equals("float")
+ || name.equals("double")
+ || name.equals("byte")
+ || name.equals("char")
+ || name.equals("boolean")
+ || name.equals("void")
+ ) {
+ return name;
+ }
+
+ if (name.startsWith("[")) {
+ return name.replace('/', '.');
+ }
+
+ if (name.startsWith("L")) {
+ name = name.substring(1);
+ if (name.endsWith(";")) {
+ name = name.substring(0, name.length() - 1);
+ }
+ return name.replace('/', '.');
+ }
+
+ String prefix = "";
+ if (name.endsWith("[]")) { // todo need process multi
+ prefix = "[";
+ name = name.substring(0, name.length() - 2);
+ if (name.equals("int")) {
+ return prefix + "I";
+ } else if (name.equals("long")) {
+ return prefix + "J";
+ } else if (name.equals("short")) {
+ return prefix + "S";
+ } else if (name.equals("float")) {
+ return prefix + "F";
+ } else if (name.equals("double")) {
+ return prefix + "D";
+ } else if (name.equals("byte")) {
+ return prefix + "B";
+ } else if (name.equals("char")) {
+ return prefix + "C";
+ } else if (name.equals("boolean")) {
+ return prefix + "Z";
+ } else {
+ return prefix + "L" + name.replace('/', '.') + ";";
+ }
+ }
+ return name.replace('/', '.');
+
+ }
+
+ /*public void dup() {
+ mv.visitInsn(DUP);
+ }*/
+
+ public static void doReturn(MethodVisitor mv, ClassNode returnType) {
+ if (returnType == ClassHelper.double_TYPE) {
+ mv.visitInsn(DRETURN);
+ } else if (returnType == ClassHelper.float_TYPE) {
+ mv.visitInsn(FRETURN);
+ } else if (returnType == ClassHelper.long_TYPE) {
+ mv.visitInsn(LRETURN);
+ } else if (
+ returnType == ClassHelper.boolean_TYPE
+ || returnType == ClassHelper.char_TYPE
+ || returnType == ClassHelper.byte_TYPE
+ || returnType == ClassHelper.int_TYPE
+ || returnType == ClassHelper.short_TYPE) {
+ //byte,short,boolean,int are all IRETURN
+ mv.visitInsn(IRETURN);
+ } else if (returnType == ClassHelper.VOID_TYPE) {
+ mv.visitInsn(RETURN);
+ } else {
+ mv.visitInsn(ARETURN);
+ }
+
+ }
+
+ private static boolean hasGenerics(Parameter[] param) {
+ if (param.length == 0) return false;
+ for (int i = 0; i < param.length; i++) {
+ ClassNode type = param[i].getType();
+ if (hasGenerics(type)) return true;
+ }
+ return false;
+ }
+
+ private static boolean hasGenerics(ClassNode type) {
+ return type.isArray() ? hasGenerics(type.getComponentType()) : type.getGenericsTypes() != null;
+ }
+
+ public static String getGenericsMethodSignature(MethodNode node) {
+ GenericsType[] generics = node.getGenericsTypes();
+ Parameter[] param = node.getParameters();
+ ClassNode returnType = node.getReturnType();
+
+ if (generics == null && !hasGenerics(param) && !hasGenerics(returnType)) return null;
+
+ StringBuilder ret = new StringBuilder(100);
+ getGenericsTypeSpec(ret, generics);
+
+ GenericsType[] paramTypes = new GenericsType[param.length];
+ for (int i = 0; i < param.length; i++) {
+ ClassNode pType = param[i].getType();
+ if (pType.getGenericsTypes() == null || !pType.isGenericsPlaceHolder()) {
+ paramTypes[i] = new GenericsType(pType);
+ } else {
+ paramTypes[i] = pType.getGenericsTypes()[0];
+ }
+ }
+ addSubTypes(ret, paramTypes, "(", ")");
+ addSubTypes(ret, new GenericsType[]{new GenericsType(returnType)}, "", "");
+ return ret.toString();
+ }
+
+ private static boolean usesGenericsInClassSignature(ClassNode node) {
+ if (!node.isUsingGenerics()) return false;
+ if (hasGenerics(node)) return true;
+ ClassNode sclass = node.getUnresolvedSuperClass(false);
+ if (sclass.isUsingGenerics()) return true;
+ ClassNode[] interfaces = node.getInterfaces();
+ if (interfaces != null) {
+ for (int i = 0; i < interfaces.length; i++) {
+ if (interfaces[i].isUsingGenerics()) return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static String getGenericsSignature(ClassNode node) {
+ if (!usesGenericsInClassSignature(node)) return null;
+ GenericsType[] genericsTypes = node.getGenericsTypes();
+ StringBuilder ret = new StringBuilder(100);
+ getGenericsTypeSpec(ret, genericsTypes);
+ GenericsType extendsPart = new GenericsType(node.getUnresolvedSuperClass(false));
+ writeGenericsBounds(ret, extendsPart, true);
+ ClassNode[] interfaces = node.getInterfaces();
+ for (int i = 0; i < interfaces.length; i++) {
+ GenericsType interfacePart = new GenericsType(interfaces[i]);
+ writeGenericsBounds(ret, interfacePart, false);
+ }
+ return ret.toString();
+ }
+
+ private static void getGenericsTypeSpec(StringBuilder ret, GenericsType[] genericsTypes) {
+ if (genericsTypes == null) return;
+ ret.append('<');
+ for (int i = 0; i < genericsTypes.length; i++) {
+ String name = genericsTypes[i].getName();
+ ret.append(name);
+ ret.append(':');
+ writeGenericsBounds(ret, genericsTypes[i], true);
+ }
+ ret.append('>');
+ }
+
+ public static String getGenericsBounds(ClassNode type) {
+ GenericsType[] genericsTypes = type.getGenericsTypes();
+ if (genericsTypes == null) return null;
+ StringBuilder ret = new StringBuilder(100);
+ if (type.isGenericsPlaceHolder()) {
+ addSubTypes(ret, type.getGenericsTypes(), "", "");
+ } else {
+ GenericsType gt = new GenericsType(type);
+ writeGenericsBounds(ret, gt, false);
+ }
+
+ return ret.toString();
+ }
+
+ private static void writeGenericsBoundType(StringBuilder ret, ClassNode printType, boolean writeInterfaceMarker) {
+ if (writeInterfaceMarker && printType.isInterface()) ret.append(":");
+ if (printType.isGenericsPlaceHolder() && printType.getGenericsTypes()!=null) {
+ ret.append("T");
+ ret.append(printType.getGenericsTypes()[0].getName());
+ ret.append(";");
+ }
+ else {
+ ret.append(getTypeDescription(printType, false));
+ addSubTypes(ret, printType.getGenericsTypes(), "<", ">");
+ if (!ClassHelper.isPrimitiveType(printType)) ret.append(";");
+ }
+ }
+
+ private static void writeGenericsBounds(StringBuilder ret, GenericsType type, boolean writeInterfaceMarker) {
+ if (type.getUpperBounds() != null) {
+ ClassNode[] bounds = type.getUpperBounds();
+ for (int i = 0; i < bounds.length; i++) {
+ writeGenericsBoundType(ret, bounds[i], writeInterfaceMarker);
+ }
+ } else if (type.getLowerBound() != null) {
+ writeGenericsBoundType(ret, type.getLowerBound(), writeInterfaceMarker);
+ } else {
+ writeGenericsBoundType(ret, type.getType(), writeInterfaceMarker);
+ }
+ }
+
+ private static void addSubTypes(StringBuilder ret, GenericsType[] types, String start, String end) {
+ if (types == null) return;
+ ret.append(start);
+ for (int i = 0; i < types.length; i++) {
+ if (types[i].getType().isArray()) {
+ ret.append("[");
+ addSubTypes(ret, new GenericsType[]{new GenericsType(types[i].getType().getComponentType())}, "", "");
+ }
+ else {
+ if (types[i].isPlaceholder()) {
+ ret.append('T');
+ String name = types[i].getName();
+ ret.append(name);
+ ret.append(';');
+ } else if (types[i].isWildcard()) {
+ if (types[i].getUpperBounds() != null) {
+ ret.append('+');
+ writeGenericsBounds(ret, types[i], false);
+ } else if (types[i].getLowerBound() != null) {
+ ret.append('-');
+ writeGenericsBounds(ret, types[i], false);
+ } else {
+ ret.append('*');
+ }
+ } else {
+ writeGenericsBounds(ret, types[i], false);
+ }
+ }
+ }
+ ret.append(end);
+ }
+
+ public static void load(MethodVisitor mv, ClassNode type, int idx) {
+ if (type == ClassHelper.double_TYPE) {
+ mv.visitVarInsn(DLOAD, idx);
+ } else if (type == ClassHelper.float_TYPE) {
+ mv.visitVarInsn(FLOAD, idx);
+ } else if (type == ClassHelper.long_TYPE) {
+ mv.visitVarInsn(LLOAD, idx);
+ } else if (
+ type == ClassHelper.boolean_TYPE
+ || type == ClassHelper.char_TYPE
+ || type == ClassHelper.byte_TYPE
+ || type == ClassHelper.int_TYPE
+ || type == ClassHelper.short_TYPE) {
+ mv.visitVarInsn(ILOAD, idx);
+ } else {
+ mv.visitVarInsn(ALOAD, idx);
+ }
+ }
+
+
+ public static void doCast(MethodVisitor mv, ClassNode type) {
+ if (type == ClassHelper.OBJECT_TYPE) return;
+ if (ClassHelper.isPrimitiveType(type) && type != ClassHelper.VOID_TYPE) {
+ unbox(mv, type);
+ } else {
+ mv.visitTypeInsn(
+ CHECKCAST,
+ type.isArray() ?
+ BytecodeHelper.getTypeDescription(type) :
+ BytecodeHelper.getClassInternalName(type.getName()));
+ }
+ }
+
+ /**
+ * Given a wrapped number type (Byte, Integer, Short, ...), generates bytecode
+ * to convert it to a primitive number (int, long, double) using calls to
+ * wrapped.[targetType]Value()
+ * @param mv method visitor
+ * @param sourceType the wrapped number type
+ * @param targetType the primitive target type
+ */
+ public static void doCastToPrimitive(MethodVisitor mv, ClassNode sourceType, ClassNode targetType) {
+ mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(sourceType), targetType.getName() + "Value", "()" + BytecodeHelper.getTypeDescription(targetType), false);
+ }
+
+ /**
+ * Given a primitive number type (byte, integer, short, ...), generates bytecode
+ * to convert it to a wrapped number (Integer, Long, Double) using calls to
+ * [WrappedType].valueOf
+ * @param mv method visitor
+ * @param sourceType the primitive number type
+ * @param targetType the wrapped target type
+ */
+ public static void doCastToWrappedType(MethodVisitor mv, ClassNode sourceType, ClassNode targetType) {
+ mv.visitMethodInsn(INVOKESTATIC, getClassInternalName(targetType), "valueOf", "(" + getTypeDescription(sourceType) + ")" + getTypeDescription(targetType), false);
+ }
+
+ public static void doCast(MethodVisitor mv, Class type) {
+ if (type == Object.class) return;
+ if (type.isPrimitive() && type != Void.TYPE) {
+ unbox(mv, type);
+ } else {
+ mv.visitTypeInsn(
+ CHECKCAST,
+ type.isArray() ?
+ BytecodeHelper.getTypeDescription(type) :
+ BytecodeHelper.getClassInternalName(type.getName()));
+ }
+ }
+
+ /**
+ * Generates the bytecode to unbox the current value on the stack
+ */
+ public static void unbox(MethodVisitor mv, Class type) {
+ if (type.isPrimitive() && type != Void.TYPE) {
+ String returnString = "(Ljava/lang/Object;)" + BytecodeHelper.getTypeDescription(type);
+ mv.visitMethodInsn(INVOKESTATIC, DTT_CLASSNAME, type.getName() + "Unbox", returnString, false);
+ }
+ }
+
+ public static void unbox(MethodVisitor mv, ClassNode type) {
+ if (type.isPrimaryClassNode()) return;
+ unbox(mv, type.getTypeClass());
+ }
+
+ /**
+ * box top level operand
+ */
+ @Deprecated
+ public static boolean box(MethodVisitor mv, ClassNode type) {
+ if (type.isPrimaryClassNode()) return false;
+ return box(mv, type.getTypeClass());
+ }
+
+
+ /**
+ * Generates the bytecode to autobox the current value on the stack
+ */
+ @Deprecated
+ public static boolean box(MethodVisitor mv, Class type) {
+ if (ReflectionCache.getCachedClass(type).isPrimitive && type != void.class) {
+ String returnString = "(" + BytecodeHelper.getTypeDescription(type) + ")Ljava/lang/Object;";
+ mv.visitMethodInsn(INVOKESTATIC, DTT_CLASSNAME, "box", returnString, false);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Visits a class literal. If the type of the classnode is a primitive type,
+ * the generated bytecode will be a GETSTATIC Integer.TYPE.
+ * If the classnode is not a primitive type, we will generate a LDC instruction.
+ */
+ public static void visitClassLiteral(MethodVisitor mv, ClassNode classNode) {
+ if (ClassHelper.isPrimitiveType(classNode)) {
+ mv.visitFieldInsn(
+ GETSTATIC,
+ getClassInternalName(ClassHelper.getWrapper(classNode)),
+ "TYPE",
+ "Ljava/lang/Class;");
+ } else {
+ mv.visitLdcInsn(org.objectweb.asm.Type.getType(getTypeDescription(classNode)));
+ }
+ }
+
+ /**
+ * Tells if a class node is candidate for class literal bytecode optimization. If so,
+ * bytecode may use LDC instructions instead of static constant Class fields to retrieve
+ * class literals.
+ * @param classNode the classnode for which we want to know if bytecode optimization is possible
+ * @return true if the bytecode can be optimized
+ */
+ public static boolean isClassLiteralPossible(ClassNode classNode) {
+ // the current implementation only checks for public modifier, because Groovy used to allow
+ // handles on classes even if they are package protected and not in the same package.
+ // There are situations where we could make more fine grained checks, but be careful of
+ // potential breakage of existing code.
+ return Modifier.isPublic(classNode.getModifiers());
+ }
+
+ /**
+ * Returns true if the two classes share the same compilation unit.
+ * @param a class a
+ * @param b class b
+ * @return true if both classes share the same compilation unit
+ */
+ public static boolean isSameCompilationUnit(ClassNode a, ClassNode b) {
+ CompileUnit cu1 = a.getCompileUnit();
+ CompileUnit cu2 = b.getCompileUnit();
+ return cu1 !=null && cu2 !=null && cu1==cu2;
+ }
+
+ /**
+ * Computes a hash code for a string. The purpose of this hashcode is to be constant independently of
+ * the JDK being used.
+ * @param str the string for which to compute the hashcode
+ * @return hashcode of the string
+ */
+ public static int hashCode(String str) {
+ final char[] chars = str.toCharArray();
+ int h = 0;
+ for (int i = 0; i < chars.length; i++) {
+ h = 31 * h + chars[i];
+ }
+ return h;
+ }
+
+ /**
+ * Converts a primitive type to boolean.
+ *
+ * @param mv method visitor
+ * @param type primitive type to convert
+ */
+ public static void convertPrimitiveToBoolean(MethodVisitor mv, ClassNode type) {
+ if (type == ClassHelper.boolean_TYPE) {
+ return;
+ }
+ // Special handling is done for floating point types in order to
+ // handle checking for 0 or NaN values.
+ if (type == ClassHelper.double_TYPE) {
+ convertDoubleToBoolean(mv, type);
+ return;
+ } else if (type == ClassHelper.float_TYPE) {
+ convertFloatToBoolean(mv, type);
+ return;
+ }
+ Label trueLabel = new Label();
+ Label falseLabel = new Label();
+ // Convert long to int for IFEQ comparison using LCMP
+ if (type==ClassHelper.long_TYPE) {
+ mv.visitInsn(LCONST_0);
+ mv.visitInsn(LCMP);
+ }
+ // This handles byte, short, char and int
+ mv.visitJumpInsn(IFEQ, falseLabel);
+ mv.visitInsn(ICONST_1);
+ mv.visitJumpInsn(GOTO, trueLabel);
+ mv.visitLabel(falseLabel);
+ mv.visitInsn(ICONST_0);
+ mv.visitLabel(trueLabel);
+ }
+
+ private static void convertDoubleToBoolean(MethodVisitor mv, ClassNode type) {
+ Label trueLabel = new Label();
+ Label falseLabel = new Label();
+ Label falseLabelWithPop = new Label();
+ mv.visitInsn(DUP2); // will need the extra for isNaN call if required
+ mv.visitInsn(DCONST_0);
+ mv.visitInsn(DCMPL);
+ mv.visitJumpInsn(IFEQ, falseLabelWithPop);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "isNaN", "(D)Z", false);
+ mv.visitJumpInsn(IFNE, falseLabel);
+ mv.visitInsn(ICONST_1);
+ mv.visitJumpInsn(GOTO, trueLabel);
+ mv.visitLabel(falseLabelWithPop);
+ mv.visitInsn(POP2);
+ mv.visitLabel(falseLabel);
+ mv.visitInsn(ICONST_0);
+ mv.visitLabel(trueLabel);
+ }
+
+ private static void convertFloatToBoolean(MethodVisitor mv, ClassNode type) {
+ Label trueLabel = new Label();
+ Label falseLabel = new Label();
+ Label falseLabelWithPop = new Label();
+ mv.visitInsn(DUP); // will need the extra for isNaN call if required
+ mv.visitInsn(FCONST_0);
+ mv.visitInsn(FCMPL);
+ mv.visitJumpInsn(IFEQ, falseLabelWithPop);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "isNaN", "(F)Z", false);
+ mv.visitJumpInsn(IFNE, falseLabel);
+ mv.visitInsn(ICONST_1);
+ mv.visitJumpInsn(GOTO, trueLabel);
+ mv.visitLabel(falseLabelWithPop);
+ mv.visitInsn(POP);
+ mv.visitLabel(falseLabel);
+ mv.visitInsn(ICONST_0);
+ mv.visitLabel(trueLabel);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeVariable.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeVariable.java b/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeVariable.java
new file mode 100644
index 0000000..1f34bc3
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BytecodeVariable.java
@@ -0,0 +1,124 @@
+/*
+ * 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.objectweb.asm.Label;
+
+/**
+ * Represents compile time variable metadata while compiling a method.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ */
+public class BytecodeVariable {
+
+ public static final BytecodeVariable THIS_VARIABLE = new BytecodeVariable();
+ public static final BytecodeVariable SUPER_VARIABLE = new BytecodeVariable();
+
+ private final int index;
+ private ClassNode type;
+ private String name;
+ private final int prevCurrent;
+ private boolean holder;
+
+ // br for setting on the LocalVariableTable in the class file
+ // these fields should probably go to jvm Operand class
+ private Label startLabel = null;
+ private Label endLabel = null;
+ private boolean dynamicTyped;
+
+ private BytecodeVariable(){
+ dynamicTyped = true;
+ index=0;
+ holder=false;
+ prevCurrent=0;
+ }
+
+ public BytecodeVariable(int index, ClassNode type, String name, int prevCurrent) {
+ this.index = index;
+ this.type = type;
+ this.name = name;
+ this.prevCurrent = prevCurrent;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ClassNode getType() {
+ return type;
+ }
+
+ /**
+ * @return the stack index for this variable
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * @return is this local variable shared in other scopes (and so must use a ValueHolder)
+ */
+ public boolean isHolder() {
+ return holder;
+ }
+
+ public void setHolder(boolean holder) {
+ this.holder = holder;
+ }
+
+ public Label getStartLabel() {
+ return startLabel;
+ }
+
+ public void setStartLabel(Label startLabel) {
+ this.startLabel = startLabel;
+ }
+
+ public Label getEndLabel() {
+ return endLabel;
+ }
+
+ public void setEndLabel(Label endLabel) {
+ this.endLabel = endLabel;
+ }
+
+ public String toString() {
+ return name + "(index=" + index + ",type=" + type + ",holder="+holder+")";
+ }
+
+ public void setType(ClassNode type) {
+ this.type = type;
+ dynamicTyped |= type==ClassHelper.DYNAMIC_TYPE;
+ }
+
+ public void setDynamicTyped(boolean b) {
+ dynamicTyped = b;
+ }
+
+ public boolean isDynamicTyped() {
+ return dynamicTyped;
+ }
+
+ public int getPrevIndex() {
+ return prevCurrent;
+ }
+}