groovy-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cchamp...@apache.org
Subject [20/57] [abbrv] [partial] groovy git commit: Move Java source set into `src/main/java`
Date Thu, 14 Dec 2017 21:56:45 GMT
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/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/b25d0e55/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/b25d0e55/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();
+    }
+}


Mime
View raw message