You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2017/12/17 14:01:31 UTC
[20/62] [abbrv] [partial] groovy git commit: Move Java source set
into `src/main/java`
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/OperandStack.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/OperandStack.java b/src/main/java/org/codehaus/groovy/classgen/asm/OperandStack.java
new file mode 100644
index 0000000..213a82b
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/OperandStack.java
@@ -0,0 +1,700 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.groovy.classgen.asm;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.expr.CastExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.tools.WideningCategories;
+import org.codehaus.groovy.classgen.ClassGeneratorException;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.objectweb.asm.Opcodes.ACONST_NULL;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.ASTORE;
+import static org.objectweb.asm.Opcodes.BIPUSH;
+import static org.objectweb.asm.Opcodes.CHECKCAST;
+import static org.objectweb.asm.Opcodes.D2F;
+import static org.objectweb.asm.Opcodes.D2I;
+import static org.objectweb.asm.Opcodes.D2L;
+import static org.objectweb.asm.Opcodes.DCONST_0;
+import static org.objectweb.asm.Opcodes.DCONST_1;
+import static org.objectweb.asm.Opcodes.DSTORE;
+import static org.objectweb.asm.Opcodes.DUP;
+import static org.objectweb.asm.Opcodes.DUP2;
+import static org.objectweb.asm.Opcodes.DUP2_X1;
+import static org.objectweb.asm.Opcodes.DUP2_X2;
+import static org.objectweb.asm.Opcodes.DUP_X2;
+import static org.objectweb.asm.Opcodes.F2D;
+import static org.objectweb.asm.Opcodes.F2I;
+import static org.objectweb.asm.Opcodes.F2L;
+import static org.objectweb.asm.Opcodes.FCONST_0;
+import static org.objectweb.asm.Opcodes.FCONST_1;
+import static org.objectweb.asm.Opcodes.FCONST_2;
+import static org.objectweb.asm.Opcodes.FSTORE;
+import static org.objectweb.asm.Opcodes.GETSTATIC;
+import static org.objectweb.asm.Opcodes.I2B;
+import static org.objectweb.asm.Opcodes.I2C;
+import static org.objectweb.asm.Opcodes.I2D;
+import static org.objectweb.asm.Opcodes.I2F;
+import static org.objectweb.asm.Opcodes.I2L;
+import static org.objectweb.asm.Opcodes.I2S;
+import static org.objectweb.asm.Opcodes.ICONST_0;
+import static org.objectweb.asm.Opcodes.ICONST_1;
+import static org.objectweb.asm.Opcodes.ICONST_2;
+import static org.objectweb.asm.Opcodes.ICONST_3;
+import static org.objectweb.asm.Opcodes.ICONST_4;
+import static org.objectweb.asm.Opcodes.ICONST_5;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+import static org.objectweb.asm.Opcodes.ISTORE;
+import static org.objectweb.asm.Opcodes.L2D;
+import static org.objectweb.asm.Opcodes.L2F;
+import static org.objectweb.asm.Opcodes.L2I;
+import static org.objectweb.asm.Opcodes.LCONST_0;
+import static org.objectweb.asm.Opcodes.LCONST_1;
+import static org.objectweb.asm.Opcodes.LSTORE;
+import static org.objectweb.asm.Opcodes.NEW;
+import static org.objectweb.asm.Opcodes.POP;
+import static org.objectweb.asm.Opcodes.POP2;
+import static org.objectweb.asm.Opcodes.SIPUSH;
+import static org.objectweb.asm.Opcodes.SWAP;
+
+public class OperandStack {
+
+ private final WriterController controller;
+ private final List<ClassNode> stack = new ArrayList<ClassNode>();
+
+ public OperandStack(WriterController wc) {
+ this.controller = wc;
+ }
+
+ public int getStackLength() {
+ return stack.size();
+ }
+
+ public void popDownTo(int elements) {
+ int last = stack.size();
+ MethodVisitor mv = controller.getMethodVisitor();
+ while (last>elements) {
+ last--;
+ ClassNode element = popWithMessage(last);
+ if (isTwoSlotType(element)) {
+ mv.visitInsn(POP2);
+ } else {
+ mv.visitInsn(POP);
+ }
+ }
+ }
+
+ private ClassNode popWithMessage(int last) {
+ try {
+ return stack.remove(last);
+ } catch (ArrayIndexOutOfBoundsException ai) {
+ String method = controller.getMethodNode() == null ?
+ controller.getConstructorNode().getTypeDescriptor() :
+ controller.getMethodNode().getTypeDescriptor();
+ throw new GroovyBugError("Error while popping argument from operand stack tracker in class " +
+ controller.getClassName() + " method " + method + ".");
+ }
+ }
+
+ /**
+ * returns true for long and double
+ */
+ private static boolean isTwoSlotType(ClassNode type) {
+ return type==ClassHelper.long_TYPE || type==ClassHelper.double_TYPE;
+ }
+
+ /**
+ * ensure last marked parameter on the stack is a primitive boolean
+ * if mark==stack size, we assume an empty expression or statement.
+ * was used and we will use the value given in emptyDefault as boolean
+ * if mark==stack.size()-1 the top element will be cast to boolean using
+ * Groovy truth.
+ * In other cases we throw a GroovyBugError
+ */
+ public void castToBool(int mark, boolean emptyDefault) {
+ int size = stack.size();
+ MethodVisitor mv = controller.getMethodVisitor();
+ if (mark==size) {
+ // no element, so use emptyDefault
+ if (emptyDefault) {
+ mv.visitIntInsn(BIPUSH, 1);
+ } else {
+ mv.visitIntInsn(BIPUSH, 0);
+ }
+ stack.add(null);
+ } else if (mark==stack.size()-1) {
+ ClassNode last = stack.get(size-1);
+ // nothing to do in that case
+ if (last == ClassHelper.boolean_TYPE) return;
+ // not a primitive type, so call booleanUnbox
+ if (!ClassHelper.isPrimitiveType(last)) {
+ controller.getInvocationWriter().castNonPrimitiveToBool(last);
+ } else {
+ BytecodeHelper.convertPrimitiveToBoolean(mv, last);
+ }
+ } else {
+ throw new GroovyBugError(
+ "operand stack contains "+stack.size()+
+ " elements, but we expected only "+mark
+ );
+ }
+ stack.set(mark,ClassHelper.boolean_TYPE);
+ }
+
+ /**
+ * remove operand stack top element using bytecode pop
+ */
+ public void pop() {
+ popDownTo(stack.size()-1);
+ }
+
+ public Label jump(int ifIns) {
+ Label label = new Label();
+ jump(ifIns,label);
+ return label;
+ }
+
+ public void jump(int ifIns, Label label) {
+ controller.getMethodVisitor().visitJumpInsn(ifIns, label);
+ // remove the boolean from the operand stack tracker
+ remove(1);
+ }
+
+ /**
+ * duplicate top element
+ */
+ public void dup() {
+ ClassNode type = getTopOperand();
+ stack.add(type);
+ MethodVisitor mv = controller.getMethodVisitor();
+ if (type == ClassHelper.double_TYPE || type == ClassHelper.long_TYPE) {
+ mv.visitInsn(DUP2);
+ } else {
+ mv.visitInsn(DUP);
+ }
+ }
+
+ public ClassNode box() {
+ MethodVisitor mv = controller.getMethodVisitor();
+ int size = stack.size();
+ ClassNode type = stack.get(size-1);
+ if (ClassHelper.isPrimitiveType(type) && ClassHelper.VOID_TYPE!=type) {
+ ClassNode wrapper = ClassHelper.getWrapper(type);
+ BytecodeHelper.doCastToWrappedType(mv, type, wrapper);
+ type = wrapper;
+ } // else nothing to box
+ stack.set(size-1, type);
+ return type;
+ }
+
+ /**
+ * Remove amount elements from the operand stack, without using pop.
+ * For example after a method invocation
+ */
+ public void remove(int amount) {
+ int size = stack.size();
+ for (int i=size-1; i>size-1-amount; i--) {
+ popWithMessage(i);
+ }
+ }
+
+ /**
+ * push operand on stack
+ */
+ public void push(ClassNode type) {
+ stack.add(type);
+ }
+
+ /**
+ * swap two top level operands
+ */
+ public void swap() {
+ MethodVisitor mv = controller.getMethodVisitor();
+ int size = stack.size();
+ ClassNode b = stack.get(size-1);
+ ClassNode a = stack.get(size-2);
+ // dup_x1: ---
+ // dup_x2: aab -> baab
+ // dup2_x1: abb -> bbabb
+ // dup2_x2: aabb -> bbaabb
+ // b = top element, a = element under b
+ // top element at right
+ if (isTwoSlotType(a)) { // aa
+ if (isTwoSlotType(b)) { // aabb
+ // aabb -> bbaa
+ mv.visitInsn(DUP2_X2); // bbaabb
+ mv.visitInsn(POP2); // bbaa
+ } else {
+ // aab -> baa
+ mv.visitInsn(DUP_X2); // baab
+ mv.visitInsn(POP); // baa
+ }
+ } else { // a
+ if (isTwoSlotType(b)) { //abb
+ // abb -> bba
+ mv.visitInsn(DUP2_X1); // bbabb
+ mv.visitInsn(POP2); // bba
+ } else {
+ // ab -> ba
+ mv.visitInsn(SWAP);
+ }
+ }
+ stack.set(size-1,a);
+ stack.set(size-2,b);
+ }
+
+ /**
+ * replace top level element with new element of given type
+ */
+ public void replace(ClassNode type) {
+ int size = stack.size();
+ try {
+ if (size==0) throw new ArrayIndexOutOfBoundsException("size==0");
+ } catch (ArrayIndexOutOfBoundsException ai) {
+ System.err.println("index problem in "+controller.getSourceUnit().getName());
+ throw ai;
+ }
+ stack.set(size-1, type);
+ }
+
+ /**
+ * replace n top level elements with new element of given type
+ */
+ public void replace(ClassNode type, int n) {
+ remove(n);
+ push(type);
+ }
+
+ /**
+ * do Groovy cast for top level element
+ */
+ public void doGroovyCast(ClassNode targetType) {
+ doConvertAndCast(targetType,false);
+ }
+
+ public void doGroovyCast(Variable v) {
+ ClassNode targetType = v.getOriginType();
+ doConvertAndCast(targetType,false);
+ }
+
+ public void doAsType(ClassNode targetType) {
+ doConvertAndCast(targetType,true);
+ }
+
+ private void throwExceptionForNoStackElement(int size, ClassNode targetType, boolean coerce) {
+ if (size>0) return;
+ StringBuilder sb = new StringBuilder();
+ sb.append("Internal compiler error while compiling ").append(controller.getSourceUnit().getName()).append("\n");
+ MethodNode methodNode = controller.getMethodNode();
+ if (methodNode!=null) {
+ sb.append("Method: ");
+ sb.append(methodNode);
+ sb.append("\n");
+ }
+ ConstructorNode constructorNode = controller.getConstructorNode();
+ if (constructorNode!=null) {
+ sb.append("Constructor: ");
+ sb.append(methodNode);
+ sb.append("\n");
+ }
+ sb.append("Line ").append(controller.getLineNumber()).append(",");
+ sb.append(" expecting ").append(coerce ? "coercion" : "casting").append(" to ").append(targetType.toString(false));
+ sb.append(" but operand stack is empty");
+ throw new ArrayIndexOutOfBoundsException(sb.toString());
+ }
+
+ private void doConvertAndCast(ClassNode targetType, boolean coerce) {
+ int size = stack.size();
+ throwExceptionForNoStackElement(size, targetType, coerce);
+
+ ClassNode top = stack.get(size-1);
+ targetType = targetType.redirect();
+ if (targetType == top) return;
+
+ if (coerce) {
+ controller.getInvocationWriter().coerce(top,targetType);
+ return;
+ }
+
+ boolean primTarget = ClassHelper.isPrimitiveType(targetType);
+ boolean primTop = ClassHelper.isPrimitiveType(top);
+
+ if (primTop && primTarget) {
+ // here we box and unbox to get the goal type
+ if (convertPrimitive(top, targetType)) {
+ replace(targetType);
+ return;
+ }
+ box();
+ } else if (primTarget) {
+ // top is not primitive so unbox
+ // leave that BH#doCast later
+ } else {
+ // top might be primitive, target is not
+ // so let invocation writer box if needed and do groovy cast otherwise
+ controller.getInvocationWriter().castToNonPrimitiveIfNecessary(top, targetType);
+ }
+
+ MethodVisitor mv = controller.getMethodVisitor();
+ if (primTarget && !ClassHelper.boolean_TYPE.equals(targetType) && !primTop && ClassHelper.getWrapper(targetType).equals(top)) {
+ BytecodeHelper.doCastToPrimitive(mv, top, targetType);
+ } else {
+ top = stack.get(size-1);
+ if (!WideningCategories.implementsInterfaceOrSubclassOf(top, targetType)) {
+ BytecodeHelper.doCast(mv,targetType);
+ }
+ }
+ replace(targetType);
+ }
+
+ private boolean convertFromInt(ClassNode target) {
+ int convertCode;
+ if (target==ClassHelper.char_TYPE){
+ convertCode = I2C;
+ } else if (target==ClassHelper.byte_TYPE){
+ convertCode = I2B;
+ } else if (target==ClassHelper.short_TYPE){
+ convertCode = I2S;
+ } else if (target==ClassHelper.long_TYPE){
+ convertCode = I2L;
+ } else if (target==ClassHelper.float_TYPE){
+ convertCode = I2F;
+ } else if (target==ClassHelper.double_TYPE){
+ convertCode = I2D;
+ } else {
+ return false;
+ }
+ controller.getMethodVisitor().visitInsn(convertCode);
+ return true;
+ }
+
+ private boolean convertFromLong(ClassNode target) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ if (target==ClassHelper.int_TYPE){
+ mv.visitInsn(L2I);
+ return true;
+ } else if ( target==ClassHelper.char_TYPE ||
+ target==ClassHelper.byte_TYPE ||
+ target==ClassHelper.short_TYPE)
+ {
+ mv.visitInsn(L2I);
+ return convertFromInt(target);
+ } else if (target==ClassHelper.double_TYPE){
+ mv.visitInsn(L2D);
+ return true;
+ } else if (target==ClassHelper.float_TYPE){
+ mv.visitInsn(L2F);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean convertFromDouble(ClassNode target) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ if (target==ClassHelper.int_TYPE){
+ mv.visitInsn(D2I);
+ return true;
+ } else if ( target==ClassHelper.char_TYPE ||
+ target==ClassHelper.byte_TYPE ||
+ target==ClassHelper.short_TYPE)
+ {
+ mv.visitInsn(D2I);
+ return convertFromInt(target);
+ } else if (target==ClassHelper.long_TYPE){
+ mv.visitInsn(D2L);
+ return true;
+ } else if (target==ClassHelper.float_TYPE){
+ mv.visitInsn(D2F);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean convertFromFloat(ClassNode target) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ if (target==ClassHelper.int_TYPE){
+ mv.visitInsn(F2I);
+ return true;
+ } else if ( target==ClassHelper.char_TYPE ||
+ target==ClassHelper.byte_TYPE ||
+ target==ClassHelper.short_TYPE)
+ {
+ mv.visitInsn(F2I);
+ return convertFromInt(target);
+ } else if (target==ClassHelper.long_TYPE){
+ mv.visitInsn(F2L);
+ return true;
+ } else if (target==ClassHelper.double_TYPE){
+ mv.visitInsn(F2D);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean convertPrimitive(ClassNode top, ClassNode target) {
+ if (top==target) return true;
+ if (top==ClassHelper.int_TYPE) {
+ return convertFromInt(target);
+ } else if ( top==ClassHelper.char_TYPE ||
+ top==ClassHelper.byte_TYPE ||
+ top==ClassHelper.short_TYPE)
+ {
+ return target == ClassHelper.int_TYPE || convertFromInt(target);
+ } else if ( top==ClassHelper.float_TYPE) {
+ return convertFromFloat(target);
+ } else if ( top==ClassHelper.double_TYPE) {
+ return convertFromDouble(target);
+ } else if ( top==ClassHelper.long_TYPE) {
+ return convertFromLong(target);
+ }
+ return false;
+ }
+
+ /**
+ * load the constant on the operand stack.
+ */
+ public void pushConstant(ConstantExpression expression) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ Object value = expression.getValue();
+ ClassNode origType = expression.getType().redirect();
+ ClassNode type = ClassHelper.getUnwrapper(origType);
+ boolean boxing = origType!=type;
+ boolean asPrimitive = boxing || ClassHelper.isPrimitiveType(type);
+
+ if (value == null) {
+ mv.visitInsn(ACONST_NULL);
+ } else if (boxing && value instanceof Boolean) {
+ // special path for boxed boolean
+ Boolean bool = (Boolean) value;
+ String text = bool ? "TRUE" : "FALSE";
+ mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", text, "Ljava/lang/Boolean;");
+ boxing = false;
+ type = origType;
+ } else if (asPrimitive) {
+ pushPrimitiveConstant(mv, value, type);
+ } else if (value instanceof BigDecimal) {
+ String className = BytecodeHelper.getClassInternalName(value.getClass().getName());
+ mv.visitTypeInsn(NEW, className);
+ mv.visitInsn(DUP);
+ mv.visitLdcInsn(value.toString());
+ mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "(Ljava/lang/String;)V", false);
+ } else if (value instanceof BigInteger) {
+ String className = BytecodeHelper.getClassInternalName(value.getClass().getName());
+ mv.visitTypeInsn(NEW, className);
+ mv.visitInsn(DUP);
+ mv.visitLdcInsn(value.toString());
+ mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "(Ljava/lang/String;)V", false);
+ } else if (value instanceof String) {
+ mv.visitLdcInsn(value);
+ } else {
+ throw new ClassGeneratorException(
+ "Cannot generate bytecode for constant: " + value + " of type: " + type.getName());
+ }
+
+ push(type);
+ if (boxing) box();
+ }
+
+ private static void pushPrimitiveConstant(final MethodVisitor mv, final Object value, final ClassNode type) {
+ boolean isInt = ClassHelper.int_TYPE.equals(type);
+ boolean isShort = ClassHelper.short_TYPE.equals(type);
+ boolean isByte = ClassHelper.byte_TYPE.equals(type);
+ boolean isChar = ClassHelper.char_TYPE.equals(type);
+ if (isInt || isShort || isByte || isChar) {
+ int val = isInt?(Integer)value:isShort?(Short)value:isChar?(Character)value:(Byte)value;
+ switch (val) {
+ 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 (val>=Byte.MIN_VALUE && val<=Byte.MAX_VALUE) {
+ mv.visitIntInsn(BIPUSH, val);
+ } else if (val>=Short.MIN_VALUE && val<=Short.MAX_VALUE) {
+ mv.visitIntInsn(SIPUSH, val);
+ } else {
+ mv.visitLdcInsn(value);
+ }
+ }
+ } else if (ClassHelper.long_TYPE.equals(type)) {
+ if ((Long)value==0L) {
+ mv.visitInsn(LCONST_0);
+ } else if ((Long)value==1L) {
+ mv.visitInsn(LCONST_1);
+ } else {
+ mv.visitLdcInsn(value);
+ }
+ } else if (ClassHelper.float_TYPE.equals(type)) {
+ if ((Float)value==0f) {
+ mv.visitInsn(FCONST_0);
+ } else if ((Float)value==1f) {
+ mv.visitInsn(FCONST_1);
+ } else if ((Float)value==2f) {
+ mv.visitInsn(FCONST_2);
+ } else {
+ mv.visitLdcInsn(value);
+ }
+ } else if (ClassHelper.double_TYPE.equals(type)) {
+ if ((Double)value==0d) {
+ mv.visitInsn(DCONST_0);
+ } else if ((Double)value==1d) {
+ mv.visitInsn(DCONST_1);
+ } else {
+ mv.visitLdcInsn(value);
+ }
+ } else if (ClassHelper.boolean_TYPE.equals(type)) {
+ boolean b = (Boolean) value;
+ if (b) {
+ mv.visitInsn(ICONST_1);
+ } else {
+ mv.visitInsn(ICONST_0);
+ }
+ } else {
+ mv.visitLdcInsn(value);
+ }
+ }
+
+ public void pushDynamicName(Expression name) {
+ if (name instanceof ConstantExpression) {
+ ConstantExpression ce = (ConstantExpression) name;
+ Object value = ce.getValue();
+ if (value instanceof String) {
+ pushConstant(ce);
+ return;
+ }
+ }
+ new CastExpression(ClassHelper.STRING_TYPE, name).visit(controller.getAcg());
+ }
+
+ public void loadOrStoreVariable(BytecodeVariable variable, boolean useReferenceDirectly) {
+ CompileStack compileStack = controller.getCompileStack();
+
+ if (compileStack.isLHS()) {
+ storeVar(variable);
+ } else {
+ MethodVisitor mv = controller.getMethodVisitor();
+ int idx = variable.getIndex();
+ ClassNode type = variable.getType();
+
+ if (variable.isHolder()) {
+ mv.visitVarInsn(ALOAD, idx);
+ if (!useReferenceDirectly) {
+ mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;", false);
+ BytecodeHelper.doCast(mv, type);
+ push(type);
+ } else {
+ push(ClassHelper.REFERENCE_TYPE);
+ }
+ } else {
+ load(type,idx);
+ }
+ }
+ }
+
+ public void storeVar(BytecodeVariable variable) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ int idx = variable.getIndex();
+ ClassNode type = variable.getType();
+ // value is on stack
+ if (variable.isHolder()) {
+ doGroovyCast(type);
+ box();
+ mv.visitVarInsn(ALOAD, idx);
+ mv.visitTypeInsn(CHECKCAST, "groovy/lang/Reference");
+ mv.visitInsn(SWAP);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V", false);
+ } else {
+ doGroovyCast(type);
+ if (type == ClassHelper.double_TYPE) {
+ mv.visitVarInsn(DSTORE, idx);
+ } else if (type == ClassHelper.float_TYPE) {
+ mv.visitVarInsn(FSTORE, idx);
+ } else if (type == ClassHelper.long_TYPE) {
+ mv.visitVarInsn(LSTORE, 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(ISTORE, idx);
+ } else {
+ mv.visitVarInsn(ASTORE, idx);
+ }
+ }
+ // remove RHS value from operand stack
+ remove(1);
+ }
+
+ public void load(ClassNode type, int idx) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ BytecodeHelper.load(mv, type, idx);
+ push(type);
+ }
+
+ public void pushBool(boolean inclusive) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ mv.visitLdcInsn(inclusive);
+ push(ClassHelper.boolean_TYPE);
+ }
+
+ public String toString() {
+ return "OperandStack(size="+stack.size()+":"+stack.toString()+")";
+ }
+
+ public ClassNode getTopOperand() {
+ int size = stack.size();
+ try {
+ if (size==0) throw new ArrayIndexOutOfBoundsException("size==0");
+ } catch (ArrayIndexOutOfBoundsException ai) {
+ System.err.println("index problem in "+controller.getSourceUnit().getName());
+ throw ai;
+ }
+ return stack.get(size-1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/OptimizingStatementWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/OptimizingStatementWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/OptimizingStatementWriter.java
new file mode 100644
index 0000000..e553477
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/OptimizingStatementWriter.java
@@ -0,0 +1,948 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.groovy.classgen.asm;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.VariableScope;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BitwiseNegationExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.DeclarationExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.PostfixExpression;
+import org.codehaus.groovy.ast.expr.PrefixExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.UnaryMinusExpression;
+import org.codehaus.groovy.ast.expr.UnaryPlusExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.DoWhileStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.stmt.IfStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.WhileStatement;
+import org.codehaus.groovy.ast.tools.ParameterUtils;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.classgen.Verifier;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.runtime.BytecodeInterface8;
+import org.codehaus.groovy.syntax.Types;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.codehaus.groovy.ast.ClassHelper.BigDecimal_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.GROOVY_INTERCEPTABLE_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.double_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.int_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
+import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
+import static org.codehaus.groovy.ast.tools.WideningCategories.isBigDecCategory;
+import static org.codehaus.groovy.ast.tools.WideningCategories.isDoubleCategory;
+import static org.codehaus.groovy.ast.tools.WideningCategories.isFloatingCategory;
+import static org.codehaus.groovy.ast.tools.WideningCategories.isIntCategory;
+import static org.codehaus.groovy.ast.tools.WideningCategories.isLongCategory;
+import static org.codehaus.groovy.classgen.asm.BinaryExpressionMultiTypeDispatcher.typeMap;
+import static org.codehaus.groovy.classgen.asm.BinaryExpressionMultiTypeDispatcher.typeMapKeyNames;
+import static org.objectweb.asm.Opcodes.ACC_FINAL;
+import static org.objectweb.asm.Opcodes.GETSTATIC;
+import static org.objectweb.asm.Opcodes.GOTO;
+import static org.objectweb.asm.Opcodes.IFEQ;
+import static org.objectweb.asm.Opcodes.IFNE;
+import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
+
+/**
+ * A class to write out the optimized statements
+ */
+public class OptimizingStatementWriter extends StatementWriter {
+
+ private static class FastPathData {
+ private Label pathStart = new Label();
+ private Label afterPath = new Label();
+ }
+
+ public static class ClassNodeSkip{}
+
+ public static class StatementMeta {
+ private boolean optimize=false;
+ protected MethodNode target;
+ protected ClassNode type;
+ protected VariableExpression declaredVariableExpression;
+ protected boolean[] involvedTypes = new boolean[typeMapKeyNames.length];
+ public void chainInvolvedTypes(OptimizeFlagsCollector opt) {
+ for (int i=0; i<typeMapKeyNames.length; i++) {
+ if (opt.current.involvedTypes[i]) {
+ this.involvedTypes[i] = true;
+ }
+ }
+ }
+ public String toString() {
+ StringBuilder ret = new StringBuilder("optimize=" + optimize + " target=" + target + " type=" + type + " involvedTypes=");
+ for (int i=0; i<typeMapKeyNames.length; i++) {
+ if (involvedTypes[i]) ret.append(" ").append(typeMapKeyNames[i]);
+ }
+ return ret.toString();
+ }
+ }
+
+ private static final MethodCaller[] guards = {
+ null,
+ MethodCaller.newStatic(BytecodeInterface8.class, "isOrigInt"),
+ MethodCaller.newStatic(BytecodeInterface8.class, "isOrigL"),
+ MethodCaller.newStatic(BytecodeInterface8.class, "isOrigD"),
+ MethodCaller.newStatic(BytecodeInterface8.class, "isOrigC"),
+ MethodCaller.newStatic(BytecodeInterface8.class, "isOrigB"),
+ MethodCaller.newStatic(BytecodeInterface8.class, "isOrigS"),
+ MethodCaller.newStatic(BytecodeInterface8.class, "isOrigF"),
+ MethodCaller.newStatic(BytecodeInterface8.class, "isOrigZ"),
+ };
+
+ private static final MethodCaller disabledStandardMetaClass = MethodCaller.newStatic(BytecodeInterface8.class, "disabledStandardMetaClass");
+ private boolean fastPathBlocked = false;
+ private final WriterController controller;
+
+ public OptimizingStatementWriter(WriterController controller) {
+ super(controller);
+ this.controller = controller;
+ }
+
+ private boolean notEnableFastPath(StatementMeta meta) {
+ // return false if cannot do fast path and if are already on the path
+ return fastPathBlocked || meta==null || !meta.optimize || controller.isFastPath();
+ }
+
+ private FastPathData writeGuards(StatementMeta meta, Statement statement) {
+ if (notEnableFastPath(meta)) return null;
+ controller.getAcg().onLineNumber(statement, null);
+ MethodVisitor mv = controller.getMethodVisitor();
+ FastPathData fastPathData = new FastPathData();
+ Label slowPath = new Label();
+
+ for (int i=0; i<guards.length; i++) {
+ if (meta.involvedTypes[i]) {
+ guards[i].call(mv);
+ mv.visitJumpInsn(IFEQ, slowPath);
+ }
+ }
+
+ // meta class check with boolean holder
+ String owner = BytecodeHelper.getClassInternalName(controller.getClassNode());
+ MethodNode mn = controller.getMethodNode();
+ if (mn!=null) {
+ mv.visitFieldInsn(GETSTATIC, owner, Verifier.STATIC_METACLASS_BOOL, "Z");
+ mv.visitJumpInsn(IFNE, slowPath);
+ }
+
+ //standard metaclass check
+ disabledStandardMetaClass.call(mv);
+ mv.visitJumpInsn(IFNE, slowPath);
+
+ // other guards here
+
+ mv.visitJumpInsn(GOTO, fastPathData.pathStart);
+ mv.visitLabel(slowPath);
+
+ return fastPathData;
+ }
+
+ private void writeFastPathPrelude(FastPathData meta) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ mv.visitJumpInsn(GOTO, meta.afterPath);
+ mv.visitLabel(meta.pathStart);
+ controller.switchToFastPath();
+ }
+
+ private void writeFastPathEpilogue(FastPathData meta) {
+ MethodVisitor mv = controller.getMethodVisitor();
+ mv.visitLabel(meta.afterPath);
+ controller.switchToSlowPath();
+ }
+
+ @Override
+ public void writeBlockStatement(BlockStatement statement) {
+ StatementMeta meta = statement.getNodeMetaData(StatementMeta.class);
+ FastPathData fastPathData = writeGuards(meta, statement);
+
+ if (fastPathData==null) {
+ // normal mode with different paths
+ // important is to not to have a fastpathblock here,
+ // otherwise the per expression statement improvement
+ // is impossible
+ super.writeBlockStatement(statement);
+ } else {
+ // fast/slow path generation
+ boolean oldFastPathBlock = fastPathBlocked;
+ fastPathBlocked = true;
+ super.writeBlockStatement(statement);
+ fastPathBlocked = oldFastPathBlock;
+
+ writeFastPathPrelude(fastPathData);
+ super.writeBlockStatement(statement);
+ writeFastPathEpilogue(fastPathData);
+ }
+ }
+
+ @Override
+ public void writeDoWhileLoop(DoWhileStatement statement) {
+ if (controller.isFastPath()) {
+ super.writeDoWhileLoop(statement);
+ } else {
+ StatementMeta meta = statement.getNodeMetaData(StatementMeta.class);
+ FastPathData fastPathData = writeGuards(meta, statement);
+
+ boolean oldFastPathBlock = fastPathBlocked;
+ fastPathBlocked = true;
+ super.writeDoWhileLoop(statement);
+ fastPathBlocked = oldFastPathBlock;
+
+ if (fastPathData==null) return;
+ writeFastPathPrelude(fastPathData);
+ super.writeDoWhileLoop(statement);
+ writeFastPathEpilogue(fastPathData);
+ }
+ }
+
+ @Override
+ protected void writeIteratorHasNext(MethodVisitor mv) {
+ if (controller.isFastPath()) {
+ mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z", true);
+ } else {
+ super.writeIteratorHasNext(mv);
+ }
+ }
+
+ @Override
+ protected void writeIteratorNext(MethodVisitor mv) {
+ if (controller.isFastPath()) {
+ mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;", true);
+ } else {
+ super.writeIteratorNext(mv);
+ }
+ }
+
+ @Override
+ protected void writeForInLoop(ForStatement statement) {
+ if (controller.isFastPath()) {
+ super.writeForInLoop(statement);
+ } else {
+ StatementMeta meta = statement.getNodeMetaData(StatementMeta.class);
+ FastPathData fastPathData = writeGuards(meta, statement);
+
+ boolean oldFastPathBlock = fastPathBlocked;
+ fastPathBlocked = true;
+ super.writeForInLoop(statement);
+ fastPathBlocked = oldFastPathBlock;
+
+ if (fastPathData==null) return;
+ writeFastPathPrelude(fastPathData);
+ super.writeForInLoop(statement);
+ writeFastPathEpilogue(fastPathData);
+ }
+ }
+
+ @Override
+ protected void writeForLoopWithClosureList(ForStatement statement) {
+ if (controller.isFastPath()) {
+ super.writeForLoopWithClosureList(statement);
+ } else {
+ StatementMeta meta = statement.getNodeMetaData(StatementMeta.class);
+ FastPathData fastPathData = writeGuards(meta, statement);
+
+ boolean oldFastPathBlock = fastPathBlocked;
+ fastPathBlocked = true;
+ super.writeForLoopWithClosureList(statement);
+ fastPathBlocked = oldFastPathBlock;
+
+ if (fastPathData==null) return;
+ writeFastPathPrelude(fastPathData);
+ super.writeForLoopWithClosureList(statement);
+ writeFastPathEpilogue(fastPathData);
+ }
+ }
+
+ @Override
+ public void writeWhileLoop(WhileStatement statement) {
+ if (controller.isFastPath()) {
+ super.writeWhileLoop(statement);
+ } else {
+ StatementMeta meta = statement.getNodeMetaData(StatementMeta.class);
+ FastPathData fastPathData = writeGuards(meta, statement);
+
+ boolean oldFastPathBlock = fastPathBlocked;
+ fastPathBlocked = true;
+ super.writeWhileLoop(statement);
+ fastPathBlocked = oldFastPathBlock;
+
+ if (fastPathData==null) return;
+ writeFastPathPrelude(fastPathData);
+ super.writeWhileLoop(statement);
+ writeFastPathEpilogue(fastPathData);
+ }
+ }
+
+ @Override
+ public void writeIfElse(IfStatement statement) {
+ StatementMeta meta = statement.getNodeMetaData(StatementMeta.class);
+ FastPathData fastPathData = writeGuards(meta, statement);
+
+ if (fastPathData==null) {
+ super.writeIfElse(statement);
+ } else {
+ boolean oldFastPathBlock = fastPathBlocked;
+ fastPathBlocked = true;
+ super.writeIfElse(statement);
+ fastPathBlocked = oldFastPathBlock;
+
+ if (fastPathData == null) return;
+ writeFastPathPrelude(fastPathData);
+ super.writeIfElse(statement);
+ writeFastPathEpilogue(fastPathData);
+ }
+ }
+
+ private boolean isNewPathFork(StatementMeta meta) {
+ // meta.optimize -> can do fast path
+ if (meta==null || meta.optimize==false) return false;
+ // fastPathBlocked -> slow path
+ if (fastPathBlocked) return false;
+ // controller.isFastPath() -> fastPath
+ return !controller.isFastPath();
+ }
+
+ @Override
+ public void writeReturn(ReturnStatement statement) {
+ if (controller.isFastPath()) {
+ super.writeReturn(statement);
+ } else {
+ StatementMeta meta = statement.getNodeMetaData(StatementMeta.class);
+ if (isNewPathFork(meta) && writeDeclarationExtraction(statement)) {
+ if (meta.declaredVariableExpression != null) {
+ // declaration was replaced by assignment so we need to define the variable
+ controller.getCompileStack().defineVariable(meta.declaredVariableExpression, false);
+ }
+ FastPathData fastPathData = writeGuards(meta, statement);
+
+ boolean oldFastPathBlock = fastPathBlocked;
+ fastPathBlocked = true;
+ super.writeReturn(statement);
+ fastPathBlocked = oldFastPathBlock;
+
+ if (fastPathData==null) return;
+ writeFastPathPrelude(fastPathData);
+ super.writeReturn(statement);
+ writeFastPathEpilogue(fastPathData);
+ } else {
+ super.writeReturn(statement);
+ }
+ }
+ }
+
+ @Override
+ public void writeExpressionStatement(ExpressionStatement statement) {
+ if (controller.isFastPath()) {
+ super.writeExpressionStatement(statement);
+ } else {
+ StatementMeta meta = statement.getNodeMetaData(StatementMeta.class);
+ // we have to have handle DelcarationExpressions special, since their
+ // entry should be outside the optimization path, we have to do that of
+ // course only if we are actually going to do two different paths,
+ // otherwise it is not needed
+ //
+ // there are several cases to be considered now.
+ // (1) no fast path possible, so just do super
+ // (2) fast path possible, and at path split point (meaning not in
+ // fast path and not in slow path). Here we have to extract the
+ // Declaration and replace by an assignment
+ // (3) fast path possible and in slow or fastPath. Nothing to do here.
+ //
+ // the only case we need to handle is then (2).
+
+ if (isNewPathFork(meta) && writeDeclarationExtraction(statement)) {
+ if (meta.declaredVariableExpression != null) {
+ // declaration was replaced by assignment so we need to define the variable
+ controller.getCompileStack().defineVariable(meta.declaredVariableExpression, false);
+ }
+ FastPathData fastPathData = writeGuards(meta, statement);
+
+ boolean oldFastPathBlock = fastPathBlocked;
+ fastPathBlocked = true;
+ super.writeExpressionStatement(statement);
+ fastPathBlocked = oldFastPathBlock;
+
+ if (fastPathData==null) return;
+ writeFastPathPrelude(fastPathData);
+ super.writeExpressionStatement(statement);
+ writeFastPathEpilogue(fastPathData);
+ } else {
+ super.writeExpressionStatement(statement);
+ }
+ }
+ }
+
+ private boolean writeDeclarationExtraction(Statement statement) {
+ Expression ex = null;
+ if (statement instanceof ReturnStatement) {
+ ReturnStatement rs = (ReturnStatement) statement;
+ ex = rs.getExpression();
+ } else if (statement instanceof ExpressionStatement) {
+ ExpressionStatement es = (ExpressionStatement) statement;
+ ex = es.getExpression();
+ } else {
+ throw new GroovyBugError("unknown statement type :"+statement.getClass());
+ }
+ if (!(ex instanceof DeclarationExpression)) return true;
+ DeclarationExpression declaration = (DeclarationExpression) ex;
+ ex = declaration.getLeftExpression();
+ if (ex instanceof TupleExpression) return false;
+
+ // stash declared variable in case we do subsequent visits after we
+ // change to assignment only
+ StatementMeta meta = statement.getNodeMetaData(StatementMeta.class);
+ if (meta != null) {
+ meta.declaredVariableExpression = declaration.getVariableExpression();
+ }
+
+ // change statement to do assignment only
+ BinaryExpression assignment = new BinaryExpression(
+ declaration.getLeftExpression(),
+ declaration.getOperation(),
+ declaration.getRightExpression());
+ assignment.setSourcePosition(declaration);
+ assignment.copyNodeMetaData(declaration);
+ // replace statement code
+ if (statement instanceof ReturnStatement) {
+ ReturnStatement rs = (ReturnStatement) statement;
+ rs.setExpression(assignment);
+ } else if (statement instanceof ExpressionStatement) {
+ ExpressionStatement es = (ExpressionStatement) statement;
+ es.setExpression(assignment);
+ } else {
+ throw new GroovyBugError("unknown statement type :"+statement.getClass());
+ }
+ return true;
+ }
+
+ public static void setNodeMeta(TypeChooser chooser, ClassNode classNode) {
+ if (classNode.getNodeMetaData(ClassNodeSkip.class)!=null) return;
+ new OptVisitor(chooser).visitClass(classNode);
+ }
+
+ private static StatementMeta addMeta(ASTNode node) {
+ StatementMeta metaOld = node.getNodeMetaData(StatementMeta.class);
+ StatementMeta meta = metaOld;
+ if (meta==null) meta = new StatementMeta();
+ meta.optimize = true;
+ if (metaOld==null) node.setNodeMetaData(StatementMeta.class, meta);
+ return meta;
+ }
+
+ private static StatementMeta addMeta(ASTNode node, OptimizeFlagsCollector opt) {
+ StatementMeta meta = addMeta(node);
+ meta.chainInvolvedTypes(opt);
+ return meta;
+ }
+
+ private static class OptimizeFlagsCollector {
+ private static class OptimizeFlagsEntry {
+ private boolean canOptimize = false;
+ private boolean shouldOptimize = false;
+ private boolean[] involvedTypes = new boolean[typeMapKeyNames.length];
+ }
+ private OptimizeFlagsEntry current = new OptimizeFlagsEntry();
+ private final LinkedList<OptimizeFlagsEntry> olderEntries = new LinkedList<OptimizeFlagsEntry>();
+ public void push() {
+ olderEntries.addLast(current);
+ current = new OptimizeFlagsEntry();
+ }
+ public void pop(boolean propagateFlags){
+ OptimizeFlagsEntry old = current;
+ current = olderEntries.removeLast();
+ if (propagateFlags) {
+ chainCanOptimize(old.canOptimize);
+ chainShouldOptimize(old.shouldOptimize);
+ for (int i=0; i<typeMapKeyNames.length; i++) current.involvedTypes[i] |= old.involvedTypes[i];
+ }
+ }
+ public String toString() {
+ StringBuilder ret;
+ if (current.shouldOptimize) {
+ ret = new StringBuilder("should optimize, can = " + current.canOptimize);
+ } else if (current.canOptimize) {
+ ret = new StringBuilder("can optimize");
+ } else {
+ ret = new StringBuilder("don't optimize");
+ }
+ ret.append(" involvedTypes =");
+ for (int i=0; i<typeMapKeyNames.length; i++) {
+ if (current.involvedTypes[i]) ret.append(" ").append(typeMapKeyNames[i]);
+ }
+ return ret.toString();
+ }
+ /**
+ * @return true iff we should Optimize - this is almost seen as must
+ */
+ private boolean shouldOptimize() {
+ return current.shouldOptimize;
+ }
+ /**
+ * @return true iff we can optimize, but not have to
+ */
+ private boolean canOptimize() {
+ return current.canOptimize || current.shouldOptimize;
+ }
+ /**
+ * set "should" to true, if not already
+ */
+ public void chainShouldOptimize(boolean opt) {
+ current.shouldOptimize = shouldOptimize() || opt;
+ }
+ /**
+ * set "can" to true, if not already
+ */
+ public void chainCanOptimize(boolean opt) {
+ current.canOptimize = current.canOptimize || opt;
+ }
+ public void chainInvolvedType(ClassNode type) {
+ Integer res = typeMap.get(type);
+ if (res==null) return;
+ current.involvedTypes[res] = true;
+ }
+ public void reset() {
+ current.canOptimize = false;
+ current.shouldOptimize = false;
+ current.involvedTypes = new boolean[typeMapKeyNames.length];
+ }
+ }
+
+ private static class OptVisitor extends ClassCodeVisitorSupport {
+ private final TypeChooser typeChooser;
+
+ public OptVisitor(final TypeChooser chooser) {
+ this.typeChooser = chooser;
+ }
+
+ @Override protected SourceUnit getSourceUnit() {return null;}
+
+ private ClassNode node;
+ private OptimizeFlagsCollector opt = new OptimizeFlagsCollector();
+ private boolean optimizeMethodCall = true;
+ private VariableScope scope;
+ private static final VariableScope nonStaticScope = new VariableScope();
+
+ @Override
+ public void visitClass(ClassNode node) {
+ this.optimizeMethodCall = !node.implementsInterface(GROOVY_INTERCEPTABLE_TYPE);
+ this.node = node;
+ this.scope = nonStaticScope;
+ super.visitClass(node);
+ this.scope=null;
+ this.node=null;
+ }
+
+ @Override
+ public void visitMethod(MethodNode node) {
+ scope = node.getVariableScope();
+ super.visitMethod(node);
+ opt.reset();
+ }
+
+ @Override
+ public void visitConstructor(ConstructorNode node) {
+ scope = node.getVariableScope();
+ super.visitConstructor(node);
+ }
+
+ @Override
+ public void visitReturnStatement(ReturnStatement statement) {
+ opt.push();
+ super.visitReturnStatement(statement);
+ if (opt.shouldOptimize()) addMeta(statement,opt);
+ opt.pop(opt.shouldOptimize());
+ }
+
+ @Override
+ public void visitUnaryMinusExpression(UnaryMinusExpression expression) {
+ //TODO: implement int operations for this
+ super.visitUnaryMinusExpression(expression);
+ StatementMeta meta = addMeta(expression);
+ meta.type = OBJECT_TYPE;
+ }
+
+ @Override
+ public void visitUnaryPlusExpression(UnaryPlusExpression expression) {
+ //TODO: implement int operations for this
+ super.visitUnaryPlusExpression(expression);
+ StatementMeta meta = addMeta(expression);
+ meta.type = OBJECT_TYPE;
+ }
+
+ @Override
+ public void visitBitwiseNegationExpression(BitwiseNegationExpression expression) {
+ //TODO: implement int operations for this
+ super.visitBitwiseNegationExpression(expression);
+ StatementMeta meta = addMeta(expression);
+ meta.type = OBJECT_TYPE;
+ }
+
+ private void addTypeInformation(Expression expression, Expression orig) {
+ ClassNode type = typeChooser.resolveType(expression, node);
+ if (isPrimitiveType(type)) {
+ StatementMeta meta = addMeta(orig);
+ meta.type = type;
+ opt.chainShouldOptimize(true);
+ opt.chainInvolvedType(type);
+ }
+ }
+
+ @Override
+ public void visitPrefixExpression(PrefixExpression expression) {
+ super.visitPrefixExpression(expression);
+ addTypeInformation(expression.getExpression(),expression);
+ }
+
+ @Override
+ public void visitPostfixExpression(PostfixExpression expression) {
+ super.visitPostfixExpression(expression);
+ addTypeInformation(expression.getExpression(),expression);
+ }
+
+ @Override
+ public void visitDeclarationExpression(DeclarationExpression expression) {
+ Expression right = expression.getRightExpression();
+ right.visit(this);
+
+ ClassNode leftType = typeChooser.resolveType(expression.getLeftExpression(), node);
+ Expression rightExpression = expression.getRightExpression();
+ ClassNode rightType = optimizeDivWithIntOrLongTarget(rightExpression, leftType);
+ if (rightType==null) rightType = typeChooser.resolveType(expression.getRightExpression(), node);
+ if (isPrimitiveType(leftType) && isPrimitiveType(rightType)) {
+ // if right is a constant, then we optimize only if it makes
+ // a block complete, so we set a maybe
+ if (right instanceof ConstantExpression) {
+ opt.chainCanOptimize(true);
+ } else {
+ opt.chainShouldOptimize(true);
+ }
+ StatementMeta meta = addMeta(expression);
+ ClassNode declarationType = typeChooser.resolveType(expression, node);
+ meta.type = declarationType!=null?declarationType:leftType;
+ opt.chainInvolvedType(leftType);
+ opt.chainInvolvedType(rightType);
+ }
+ }
+
+ @Override
+ public void visitBinaryExpression(BinaryExpression expression) {
+ if (expression.getNodeMetaData(StatementMeta.class)!=null) return;
+ super.visitBinaryExpression(expression);
+
+ ClassNode leftType = typeChooser.resolveType(expression.getLeftExpression(), node);
+ ClassNode rightType = typeChooser.resolveType(expression.getRightExpression(), node);
+ ClassNode resultType = null;
+ int operation = expression.getOperation().getType();
+
+ if (operation==Types.LEFT_SQUARE_BRACKET && leftType.isArray()) {
+ opt.chainShouldOptimize(true);
+ resultType = leftType.getComponentType();
+ } else {
+ switch (operation) {
+ case Types.COMPARE_EQUAL:
+ case Types.COMPARE_LESS_THAN:
+ case Types.COMPARE_LESS_THAN_EQUAL:
+ case Types.COMPARE_GREATER_THAN:
+ case Types.COMPARE_GREATER_THAN_EQUAL:
+ case Types.COMPARE_NOT_EQUAL:
+ if (isIntCategory(leftType) && isIntCategory(rightType)) {
+ opt.chainShouldOptimize(true);
+ } else if (isLongCategory(leftType) && isLongCategory(rightType)) {
+ opt.chainShouldOptimize(true);
+ } else if (isDoubleCategory(leftType) && isDoubleCategory(rightType)) {
+ opt.chainShouldOptimize(true);
+ } else {
+ opt.chainCanOptimize(true);
+ }
+ resultType = boolean_TYPE;
+ break;
+ case Types.LOGICAL_AND: case Types.LOGICAL_AND_EQUAL:
+ case Types.LOGICAL_OR: case Types.LOGICAL_OR_EQUAL:
+ if (boolean_TYPE.equals(leftType) && boolean_TYPE.equals(rightType)) {
+ opt.chainShouldOptimize(true);
+ } else {
+ opt.chainCanOptimize(true);
+ }
+ expression.setType(boolean_TYPE);
+ resultType = boolean_TYPE;
+ break;
+ case Types.DIVIDE: case Types.DIVIDE_EQUAL:
+ if (isLongCategory(leftType) && isLongCategory(rightType)) {
+ resultType = BigDecimal_TYPE;
+ opt.chainShouldOptimize(true);
+ } else if (isBigDecCategory(leftType) && isBigDecCategory(rightType)) {
+ // no optimization for BigDecimal yet
+ //resultType = BigDecimal_TYPE;
+ } else if (isDoubleCategory(leftType) && isDoubleCategory(rightType)) {
+ resultType = double_TYPE;
+ opt.chainShouldOptimize(true);
+ }
+ break;
+ case Types.POWER: case Types.POWER_EQUAL:
+ //TODO: implement
+ break;
+ case Types.ASSIGN:
+ resultType = optimizeDivWithIntOrLongTarget(expression.getRightExpression(), leftType);
+ opt.chainCanOptimize(true);
+ break;
+ default:
+ if (isIntCategory(leftType) && isIntCategory(rightType)) {
+ resultType = int_TYPE;
+ opt.chainShouldOptimize(true);
+ } else if (isLongCategory(leftType) && isLongCategory(rightType)) {
+ resultType = long_TYPE;
+ opt.chainShouldOptimize(true);
+ } else if (isBigDecCategory(leftType) && isBigDecCategory(rightType)) {
+ // no optimization for BigDecimal yet
+ //resultType = BigDecimal_TYPE;
+ } else if (isDoubleCategory(leftType) && isDoubleCategory(rightType)) {
+ resultType = double_TYPE;
+ opt.chainShouldOptimize(true);
+ }
+ }
+ }
+
+ if (resultType!=null) {
+ StatementMeta meta = addMeta(expression);
+ meta.type = resultType;
+ opt.chainInvolvedType(resultType);
+ opt.chainInvolvedType(leftType);
+ opt.chainInvolvedType(rightType);
+ }
+ }
+
+ /**
+ * method to optimize Z = X/Y with Z being int or long style
+ * @returns null if the optimization cannot be applied, otherwise it
+ * will return the new target type
+ */
+ private ClassNode optimizeDivWithIntOrLongTarget(Expression rhs, ClassNode assignmentTartgetType) {
+ if (!(rhs instanceof BinaryExpression)) return null;
+ BinaryExpression binExp = (BinaryExpression) rhs;
+ int op = binExp.getOperation().getType();
+ if (op!=Types.DIVIDE && op!=Types.DIVIDE_EQUAL) return null;
+
+ ClassNode originalResultType = typeChooser.resolveType(binExp, node);
+ if ( !originalResultType.equals(BigDecimal_TYPE) ||
+ !(isLongCategory(assignmentTartgetType) || isFloatingCategory(assignmentTartgetType))
+ ) {
+ return null;
+ }
+
+ ClassNode leftType = typeChooser.resolveType(binExp.getLeftExpression(), node);
+ if (!isLongCategory(leftType)) return null;
+ ClassNode rightType = typeChooser.resolveType(binExp.getRightExpression(), node);
+ if (!isLongCategory(rightType)) return null;
+
+ ClassNode target;
+ if (isIntCategory(leftType) && isIntCategory(rightType)) {
+ target = int_TYPE;
+ } else if (isLongCategory(leftType) && isLongCategory(rightType)) {
+ target = long_TYPE;
+ } else if (isDoubleCategory(leftType) && isDoubleCategory(rightType)) {
+ target = double_TYPE;
+ } else {
+ return null;
+ }
+ StatementMeta meta = addMeta(rhs);
+ meta.type = target;
+ opt.chainInvolvedType(target);
+ return target;
+ }
+
+ @Override
+ public void visitExpressionStatement(ExpressionStatement statement) {
+ if (statement.getNodeMetaData(StatementMeta.class)!=null) return;
+ opt.push();
+ super.visitExpressionStatement(statement);
+ if (opt.shouldOptimize()) addMeta(statement,opt);
+ opt.pop(opt.shouldOptimize());
+ }
+
+ @Override
+ public void visitBlockStatement(BlockStatement block) {
+ opt.push();
+ boolean optAll = true;
+ for (Statement statement : block.getStatements()) {
+ opt.push();
+ statement.visit(this);
+ optAll = optAll && opt.canOptimize();
+ opt.pop(true);
+ }
+ if (block.isEmpty()) {
+ opt.chainCanOptimize(true);
+ opt.pop(true);
+ } else {
+ opt.chainShouldOptimize(optAll);
+ if (optAll) addMeta(block,opt);
+ opt.pop(optAll);
+ }
+ }
+
+ @Override
+ public void visitIfElse(IfStatement statement) {
+ opt.push();
+ super.visitIfElse(statement);
+ if (opt.shouldOptimize()) addMeta(statement,opt);
+ opt.pop(opt.shouldOptimize());
+ }
+
+ /*@Override
+ public void visitConstantExpression(ConstantExpression expression) {
+ super.visitConstantExpression(expression);
+ opt.chainShouldOptimize(true);
+ }*/
+
+ @Override
+ public void visitStaticMethodCallExpression(StaticMethodCallExpression expression) {
+ if (expression.getNodeMetaData(StatementMeta.class)!=null) return;
+ super.visitStaticMethodCallExpression(expression);
+
+ setMethodTarget(expression,expression.getMethod(), expression.getArguments(), true);
+ }
+
+ @Override
+ public void visitMethodCallExpression(MethodCallExpression expression) {
+ if (expression.getNodeMetaData(StatementMeta.class)!=null) return;
+ super.visitMethodCallExpression(expression);
+
+ Expression object = expression.getObjectExpression();
+ boolean setTarget = AsmClassGenerator.isThisExpression(object);
+ if (!setTarget) {
+ if (!(object instanceof ClassExpression)) return;
+ setTarget = object.equals(node);
+ }
+
+ if (!setTarget) return;
+ setMethodTarget(expression, expression.getMethodAsString(), expression.getArguments(), true);
+ }
+
+ @Override
+ public void visitConstructorCallExpression(ConstructorCallExpression call) {
+ if (call.getNodeMetaData(StatementMeta.class)!=null) return;
+ super.visitConstructorCallExpression(call);
+
+ // we cannot a target for the constructor call, since we cannot easily
+ // check the meta class of the other class
+ // setMethodTarget(call, "<init>", call.getArguments(), false);
+ }
+
+ private void setMethodTarget(Expression expression, String name, Expression callArgs, boolean isMethod) {
+ if (name==null) return;
+ if (!optimizeMethodCall) return;
+ if (AsmClassGenerator.containsSpreadExpression(callArgs)) return;
+ // find method call target
+ Parameter[] paraTypes = null;
+ if (callArgs instanceof ArgumentListExpression) {
+ ArgumentListExpression args = (ArgumentListExpression) callArgs;
+ int size = args.getExpressions().size();
+ paraTypes = new Parameter[size];
+ int i=0;
+ for (Expression exp: args.getExpressions()) {
+ ClassNode type = typeChooser.resolveType(exp, node);
+ if (!validTypeForCall(type)) return;
+ paraTypes[i] = new Parameter(type,"");
+ i++;
+ }
+ } else {
+ ClassNode type = typeChooser.resolveType(callArgs, node);
+ if (!validTypeForCall(type)) return;
+ paraTypes = new Parameter[]{new Parameter(type,"")};
+ }
+
+ MethodNode target;
+ ClassNode type;
+ if (isMethod) {
+ target = node.getMethod(name, paraTypes);
+ if (target==null) return;
+ if (!target.getDeclaringClass().equals(node)) return;
+ if (scope.isInStaticContext() && !target.isStatic()) return;
+ type = target.getReturnType().redirect();
+ } else {
+ type = expression.getType();
+ target = selectConstructor(type, paraTypes);
+ if (target==null) return;
+ }
+
+ StatementMeta meta = addMeta(expression);
+ meta.target = target;
+ meta.type = type;
+ opt.chainShouldOptimize(true);
+ }
+
+ private static MethodNode selectConstructor(ClassNode node, Parameter[] paraTypes) {
+ List<ConstructorNode> cl = node.getDeclaredConstructors();
+ MethodNode res = null;
+ for (ConstructorNode cn : cl) {
+ if (ParameterUtils.parametersEqual(cn.getParameters(), paraTypes)) {
+ res = cn;
+ break;
+ }
+ }
+ if (res !=null && res.isPublic()) return res;
+ return null;
+ }
+
+ private static boolean validTypeForCall(ClassNode type) {
+ // do call only for final classes and primitive types
+ if (isPrimitiveType(type)) return true;
+ return (type.getModifiers() & ACC_FINAL) > 0;
+ }
+
+ @Override
+ public void visitClosureExpression(ClosureExpression expression) {
+ return;
+ }
+
+ @Override
+ public void visitForLoop(ForStatement statement) {
+ opt.push();
+ super.visitForLoop(statement);
+ if (opt.shouldOptimize()) addMeta(statement,opt);
+ opt.pop(opt.shouldOptimize());
+ }
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/StatementMetaTypeChooser.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/StatementMetaTypeChooser.java b/src/main/java/org/codehaus/groovy/classgen/asm/StatementMetaTypeChooser.java
new file mode 100644
index 0000000..2ada09d
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/StatementMetaTypeChooser.java
@@ -0,0 +1,58 @@
+/*
+ * 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.FieldNode;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+
+/**
+ * A {@link TypeChooser} which is aware of statement metadata.
+ *
+ * @author Jochen Theodorou
+ * @author Cedric Champeau
+ */
+public class StatementMetaTypeChooser implements TypeChooser {
+ public ClassNode resolveType(final Expression exp, final ClassNode current) {
+ if (exp instanceof ClassExpression) return ClassHelper.CLASS_Type;
+ OptimizingStatementWriter.StatementMeta meta = exp.getNodeMetaData(OptimizingStatementWriter.StatementMeta.class);
+ ClassNode type = null;
+ if (meta != null) type = meta.type;
+ if (type != null) return type;
+ if (exp instanceof VariableExpression) {
+ VariableExpression ve = (VariableExpression) exp;
+ if (ve.isClosureSharedVariable()) return ve.getType();
+ type = ve.getOriginType();
+ if (ve.getAccessedVariable() instanceof FieldNode) {
+ FieldNode fn = (FieldNode) ve.getAccessedVariable();
+ if (!fn.getDeclaringClass().equals(current)) return fn.getOriginType();
+ }
+ } else if (exp instanceof Variable) {
+ Variable v = (Variable) exp;
+ type = v.getOriginType();
+ } else {
+ type = exp.getType();
+ }
+ return type.redirect();
+ }
+}