You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2017/12/14 21:56:49 UTC

[24/57] [abbrv] [partial] groovy git commit: Move Java source set into `src/main/java`

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/AssertionWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/AssertionWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/AssertionWriter.java
new file mode 100644
index 0000000..2200c30
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/AssertionWriter.java
@@ -0,0 +1,257 @@
+/*
+ *  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.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
+import org.codehaus.groovy.control.Janitor;
+import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
+import org.codehaus.groovy.runtime.powerassert.SourceText;
+import org.codehaus.groovy.runtime.powerassert.SourceTextNotAvailableException;
+import org.codehaus.groovy.syntax.Token;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.ATHROW;
+import static org.objectweb.asm.Opcodes.DUP;
+import static org.objectweb.asm.Opcodes.GOTO;
+import static org.objectweb.asm.Opcodes.IFEQ;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+import static org.objectweb.asm.Opcodes.NEW;
+import static org.objectweb.asm.Opcodes.POP;
+
+public class AssertionWriter {
+    // assert
+    private static final MethodCaller assertFailedMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "assertFailed");
+    
+    private static class AssertionTracker {
+        int recorderIndex;
+        SourceText sourceText;
+    }
+
+    private final WriterController controller;
+    private AssertionTracker assertionTracker;
+    private AssertionTracker disabledTracker;
+    
+    public AssertionWriter(WriterController wc) {
+        this.controller = wc;
+    }
+    
+    public void writeAssertStatement(AssertStatement statement) {
+        MethodVisitor mv = controller.getMethodVisitor();
+        OperandStack operandStack = controller.getOperandStack();
+        
+        boolean rewriteAssert = true;
+        // don't rewrite assertions with message
+        rewriteAssert = statement.getMessageExpression() == ConstantExpression.NULL;
+        AssertionTracker oldTracker = assertionTracker;
+        Janitor janitor = new Janitor();
+        final Label tryStart = new Label();
+        if (rewriteAssert){
+            assertionTracker = new AssertionTracker();
+            try {
+                // because source position seems to be more reliable for statements
+                // than for expressions, we get the source text for the whole statement
+                assertionTracker.sourceText = new SourceText(statement, controller.getSourceUnit(), janitor);
+                mv.visitTypeInsn(NEW, "org/codehaus/groovy/runtime/powerassert/ValueRecorder");
+                mv.visitInsn(DUP);
+                mv.visitMethodInsn(INVOKESPECIAL, "org/codehaus/groovy/runtime/powerassert/ValueRecorder", "<init>", "()V", false);
+                //TODO: maybe use more specialized type here
+                controller.getOperandStack().push(ClassHelper.OBJECT_TYPE);
+                assertionTracker.recorderIndex = controller.getCompileStack().defineTemporaryVariable("recorder", true);
+                mv.visitLabel(tryStart);
+            } catch (SourceTextNotAvailableException e) {
+                // set assertionTracker to null to deactivate AssertionWriter#record calls
+                assertionTracker = null;
+                // don't rewrite assertions w/o source text
+                rewriteAssert = false;
+            }
+        }
+        
+        statement.getBooleanExpression().visit(controller.getAcg());
+
+        Label exceptionThrower = operandStack.jump(IFEQ);
+
+        // do nothing, but clear the value recorder
+        if (rewriteAssert) {
+            //clean up assertion recorder
+            mv.visitVarInsn(ALOAD, assertionTracker.recorderIndex);
+            mv.visitMethodInsn(INVOKEVIRTUAL, "org/codehaus/groovy/runtime/powerassert/ValueRecorder", "clear", "()V", false);
+        }
+        Label afterAssert = new Label();
+        mv.visitJumpInsn(GOTO, afterAssert);
+        mv.visitLabel(exceptionThrower);
+        
+        if (rewriteAssert) {
+            mv.visitLdcInsn(assertionTracker.sourceText.getNormalizedText());
+            mv.visitVarInsn(ALOAD, assertionTracker.recorderIndex);
+            mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/powerassert/AssertionRenderer", "render", "(Ljava/lang/String;Lorg/codehaus/groovy/runtime/powerassert/ValueRecorder;)Ljava/lang/String;", false);
+        } else {
+            writeSourcelessAssertText(statement);
+        }
+        operandStack.push(ClassHelper.STRING_TYPE);
+        AssertionTracker savedTracker = assertionTracker;
+        assertionTracker = null;
+        
+        // now the optional exception expression
+        statement.getMessageExpression().visit(controller.getAcg());
+        operandStack.box();
+        assertFailedMethod.call(mv);
+        operandStack.remove(2); // assertFailed called static with 2 arguments 
+        
+        if (rewriteAssert) {
+            final Label tryEnd = new Label();
+            mv.visitLabel(tryEnd);
+            mv.visitJumpInsn(GOTO, afterAssert);
+            // finally block to clean assertion recorder
+            final Label catchAny = new Label();
+            mv.visitLabel(catchAny);
+            mv.visitVarInsn(ALOAD, savedTracker.recorderIndex);
+            mv.visitMethodInsn(INVOKEVIRTUAL, "org/codehaus/groovy/runtime/powerassert/ValueRecorder", "clear", "()V", false);
+            mv.visitInsn(ATHROW);
+            // add catch any block to exception table
+            controller.getCompileStack().addExceptionBlock(tryStart, tryEnd, catchAny, null);
+        }
+        
+        mv.visitLabel(afterAssert);
+        if (rewriteAssert) {
+            controller.getCompileStack().removeVar(savedTracker.recorderIndex);
+        }
+        assertionTracker = oldTracker;
+        // close possibly open file handles from getting a sample for 
+        // power asserts
+        janitor.cleanup();
+    }
+    
+    private void writeSourcelessAssertText(AssertStatement statement) {
+        MethodVisitor mv = controller.getMethodVisitor();
+        OperandStack operandStack = controller.getOperandStack();
+        
+        BooleanExpression booleanExpression = statement.getBooleanExpression();
+        // push expression string onto stack
+        String expressionText = booleanExpression.getText();
+        List<String> list = new ArrayList<String>();
+        addVariableNames(booleanExpression, list);
+        if (list.isEmpty()) {
+            mv.visitLdcInsn(expressionText);
+        } else {
+            boolean first = true;
+
+            // let's create a new expression
+            mv.visitTypeInsn(NEW, "java/lang/StringBuffer");
+            mv.visitInsn(DUP);
+            mv.visitLdcInsn(expressionText + ". Values: ");
+            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "(Ljava/lang/String;)V", false);
+            //TODO: maybe use more special type StringBuffer here
+            operandStack.push(ClassHelper.OBJECT_TYPE);
+            int tempIndex = controller.getCompileStack().defineTemporaryVariable("assert", true);
+
+            for (String name : list) {
+                String text = name + " = ";
+                if (first) {
+                    first = false;
+                } else {
+                    text = ", " + text;
+                }
+
+                mv.visitVarInsn(ALOAD, tempIndex);
+                mv.visitLdcInsn(text);
+                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;", false);
+                mv.visitInsn(POP);
+
+                mv.visitVarInsn(ALOAD, tempIndex);
+                new VariableExpression(name).visit(controller.getAcg());
+                operandStack.box();
+                mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/InvokerHelper", "toString", "(Ljava/lang/Object;)Ljava/lang/String;", false);
+                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;", false);
+                mv.visitInsn(POP);
+                operandStack.remove(1);
+            }
+            mv.visitVarInsn(ALOAD, tempIndex);
+            controller.getCompileStack().removeVar(tempIndex);
+        }
+    }
+    
+    public void record(Expression expression) {
+        if (assertionTracker==null) return;
+        record(assertionTracker.sourceText.getNormalizedColumn(expression.getLineNumber(), expression.getColumnNumber()));
+    }
+
+    public void record(Token op) {
+        if (assertionTracker==null) return;
+        record(assertionTracker.sourceText.getNormalizedColumn(op.getStartLine(), op.getStartColumn()));
+    }
+    
+    private void record(int normalizedColumn) {
+        if (assertionTracker==null) return;
+        
+        MethodVisitor mv = controller.getMethodVisitor();
+        OperandStack operandStack = controller.getOperandStack();
+        
+        operandStack.dup();
+        operandStack.box();
+        
+        mv.visitVarInsn(ALOAD, assertionTracker.recorderIndex);
+        operandStack.push(ClassHelper.OBJECT_TYPE);
+        //helper.swapWithObject(ClassHelper.OBJECT_TYPE);
+        operandStack.swap();
+        mv.visitLdcInsn(normalizedColumn);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "org/codehaus/groovy/runtime/powerassert/ValueRecorder", "record", "(Ljava/lang/Object;I)Ljava/lang/Object;", false);
+        mv.visitInsn(POP);
+        operandStack.remove(2);
+    }
+    
+    private void addVariableNames(Expression expression, List<String> list) {
+        if (expression instanceof BooleanExpression) {
+            BooleanExpression boolExp = (BooleanExpression) expression;
+            addVariableNames(boolExp.getExpression(), list);
+        } else if (expression instanceof BinaryExpression) {
+            BinaryExpression binExp = (BinaryExpression) expression;
+            addVariableNames(binExp.getLeftExpression(), list);
+            addVariableNames(binExp.getRightExpression(), list);
+        } else if (expression instanceof VariableExpression) {
+            VariableExpression varExp = (VariableExpression) expression;
+            list.add(varExp.getName());
+        }
+    }
+
+    public void disableTracker() {
+        if (assertionTracker==null) return;
+        disabledTracker = assertionTracker;
+        assertionTracker = null;
+    }
+
+    public void reenableTracker() {
+        if (disabledTracker==null) return;
+        assertionTracker = disabledTracker;
+        disabledTracker = null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/BinaryBooleanExpressionHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BinaryBooleanExpressionHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryBooleanExpressionHelper.java
new file mode 100644
index 0000000..18e3169
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryBooleanExpressionHelper.java
@@ -0,0 +1,94 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.classgen.asm;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.runtime.BytecodeInterface8;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class BinaryBooleanExpressionHelper extends BinaryIntExpressionHelper {
+
+    public BinaryBooleanExpressionHelper(WriterController wc) {
+        super(wc, boolArraySet, boolArrayGet);
+    }
+    
+    private static final MethodCaller 
+        boolArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "zArrayGet"),
+        boolArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "zArraySet");
+
+    @Override
+    protected ClassNode getArrayGetResultType() {
+        return ClassHelper.boolean_TYPE;
+    }
+    
+    public boolean writePostOrPrefixMethod(int operation, boolean simulate) {
+        if (simulate) return false;
+        throw new GroovyBugError("should not reach here");
+    }
+    
+    @Override
+    protected boolean writeStdOperators(int type, boolean simulate) {
+        if (simulate) return false;
+        throw new GroovyBugError("should not reach here");
+    }
+    
+    protected boolean writeDivision(boolean simulate) {
+        if (simulate) return false;
+        throw new GroovyBugError("should not reach here");
+    }
+
+    protected int getBitwiseOperationBytecode(int type) {
+        return -1;
+    }
+    
+    protected ClassNode getNormalOpResultType() {
+        return ClassHelper.boolean_TYPE;
+    }
+    
+    protected ClassNode getDevisionOpResultType() {
+        return ClassHelper.boolean_TYPE;
+    }
+    
+    protected int getShiftOperationBytecode(int type) {
+        return -1;
+    }
+    
+    protected int getStandardOperationBytecode(int type) {
+        return -1;
+    }
+    
+    protected void removeTwoOperands(MethodVisitor mv) {
+        throw new GroovyBugError("should not reach here");
+    }
+    protected void writePlusPlus(MethodVisitor mv) {
+        throw new GroovyBugError("should not reach here");
+    }
+    protected void writeMinusMinus(MethodVisitor mv) {
+        throw new GroovyBugError("should not reach here");
+    }
+    protected void doubleTwoOperands(MethodVisitor mv) {
+        throw new GroovyBugError("should not reach here");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/BinaryDoubleExpressionHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BinaryDoubleExpressionHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryDoubleExpressionHelper.java
new file mode 100644
index 0000000..736e060
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryDoubleExpressionHelper.java
@@ -0,0 +1,106 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.classgen.asm;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.runtime.BytecodeInterface8;
+import org.objectweb.asm.MethodVisitor;
+
+import static org.objectweb.asm.Opcodes.DADD;
+import static org.objectweb.asm.Opcodes.DCMPG;
+import static org.objectweb.asm.Opcodes.DCONST_1;
+import static org.objectweb.asm.Opcodes.DDIV;
+import static org.objectweb.asm.Opcodes.DMUL;
+import static org.objectweb.asm.Opcodes.DREM;
+import static org.objectweb.asm.Opcodes.DSUB;
+
+/**
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class BinaryDoubleExpressionHelper extends BinaryLongExpressionHelper {
+
+
+    public BinaryDoubleExpressionHelper(WriterController controller) {
+        super(controller, doubleArraySet, doubleArrayGet);
+    }
+
+    private static final MethodCaller 
+        doubleArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "dArrayGet"),
+        doubleArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "dArraySet");
+
+    protected boolean writeBitwiseOp(int op, boolean simulate) {
+        if (!simulate) throw new GroovyBugError("should not reach here");
+        return false;   
+    }
+    
+    protected int getBitwiseOperationBytecode(int op) {
+        return -1;
+    }
+
+    protected int getCompareCode() {
+        return DCMPG;
+    }
+
+    protected ClassNode getNormalOpResultType() {
+        return ClassHelper.double_TYPE;
+    }
+
+    protected boolean writeShiftOp(int type, boolean simulate) {
+        if (!simulate) throw new GroovyBugError("should not reach here");
+        return false;   
+    }
+    
+    protected int getShiftOperationBytecode(int type) {
+        return -1;
+    }
+
+    private static final int[] stdOperations = {
+        DADD,           //  PLUS        200
+        DSUB,           //  MINUS       201
+        DMUL,           //  MULTIPLY    202
+        DDIV,           //  DIV         203
+        DDIV,           //  INTDIV      204
+        DREM,           //  MOD         203
+    };
+    
+    protected int getStandardOperationBytecode(int type) {
+        return stdOperations[type];
+    }
+    
+    protected void writeMinusMinus(MethodVisitor mv) {
+        mv.visitInsn(DCONST_1);
+        mv.visitInsn(DSUB);
+    }
+    
+    protected void writePlusPlus(MethodVisitor mv) {
+        mv.visitInsn(DCONST_1);
+        mv.visitInsn(DADD);
+    }
+    
+    protected ClassNode getDevisionOpResultType() {
+        return ClassHelper.double_TYPE;
+    }
+    
+    @Override
+    protected boolean supportsDivision() {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionHelper.java
new file mode 100644
index 0000000..3279c14
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionHelper.java
@@ -0,0 +1,962 @@
+/*
+ *  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.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ArrayExpression;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ElvisOperatorExpression;
+import org.codehaus.groovy.ast.expr.EmptyExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.FieldExpression;
+import org.codehaus.groovy.ast.expr.ListExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.NotExpression;
+import org.codehaus.groovy.ast.expr.PostfixExpression;
+import org.codehaus.groovy.ast.expr.PrefixExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.TernaryExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.tools.WideningCategories;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.classgen.BytecodeExpression;
+import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+import static org.codehaus.groovy.syntax.Types.BITWISE_AND;
+import static org.codehaus.groovy.syntax.Types.BITWISE_AND_EQUAL;
+import static org.codehaus.groovy.syntax.Types.BITWISE_OR;
+import static org.codehaus.groovy.syntax.Types.BITWISE_OR_EQUAL;
+import static org.codehaus.groovy.syntax.Types.BITWISE_XOR;
+import static org.codehaus.groovy.syntax.Types.BITWISE_XOR_EQUAL;
+import static org.codehaus.groovy.syntax.Types.COMPARE_EQUAL;
+import static org.codehaus.groovy.syntax.Types.COMPARE_GREATER_THAN;
+import static org.codehaus.groovy.syntax.Types.COMPARE_GREATER_THAN_EQUAL;
+import static org.codehaus.groovy.syntax.Types.COMPARE_IDENTICAL;
+import static org.codehaus.groovy.syntax.Types.COMPARE_LESS_THAN;
+import static org.codehaus.groovy.syntax.Types.COMPARE_LESS_THAN_EQUAL;
+import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_EQUAL;
+import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_IDENTICAL;
+import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_IN;
+import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_INSTANCEOF;
+import static org.codehaus.groovy.syntax.Types.COMPARE_TO;
+import static org.codehaus.groovy.syntax.Types.DIVIDE;
+import static org.codehaus.groovy.syntax.Types.DIVIDE_EQUAL;
+import static org.codehaus.groovy.syntax.Types.ELVIS_EQUAL;
+import static org.codehaus.groovy.syntax.Types.EQUAL;
+import static org.codehaus.groovy.syntax.Types.FIND_REGEX;
+import static org.codehaus.groovy.syntax.Types.INTDIV;
+import static org.codehaus.groovy.syntax.Types.INTDIV_EQUAL;
+import static org.codehaus.groovy.syntax.Types.KEYWORD_IN;
+import static org.codehaus.groovy.syntax.Types.KEYWORD_INSTANCEOF;
+import static org.codehaus.groovy.syntax.Types.LEFT_SHIFT;
+import static org.codehaus.groovy.syntax.Types.LEFT_SHIFT_EQUAL;
+import static org.codehaus.groovy.syntax.Types.LEFT_SQUARE_BRACKET;
+import static org.codehaus.groovy.syntax.Types.LOGICAL_AND;
+import static org.codehaus.groovy.syntax.Types.LOGICAL_OR;
+import static org.codehaus.groovy.syntax.Types.MATCH_REGEX;
+import static org.codehaus.groovy.syntax.Types.MINUS;
+import static org.codehaus.groovy.syntax.Types.MINUS_EQUAL;
+import static org.codehaus.groovy.syntax.Types.MOD;
+import static org.codehaus.groovy.syntax.Types.MOD_EQUAL;
+import static org.codehaus.groovy.syntax.Types.MULTIPLY;
+import static org.codehaus.groovy.syntax.Types.MULTIPLY_EQUAL;
+import static org.codehaus.groovy.syntax.Types.PLUS;
+import static org.codehaus.groovy.syntax.Types.PLUS_EQUAL;
+import static org.codehaus.groovy.syntax.Types.POWER;
+import static org.codehaus.groovy.syntax.Types.POWER_EQUAL;
+import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT;
+import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT_EQUAL;
+import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT_UNSIGNED;
+import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT_UNSIGNED_EQUAL;
+import static org.objectweb.asm.Opcodes.ACONST_NULL;
+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.INSTANCEOF;
+
+public class BinaryExpressionHelper {
+    //compare
+    private static final MethodCaller compareIdenticalMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareIdentical");
+    private static final MethodCaller compareNotIdenticalMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareNotIdentical");
+    private static final MethodCaller compareEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareEqual");
+    private static final MethodCaller compareNotEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareNotEqual");
+    private static final MethodCaller compareToMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareTo");
+    private static final MethodCaller compareLessThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThan");
+    private static final MethodCaller compareLessThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThanEqual");
+    private static final MethodCaller compareGreaterThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThan");
+    private static final MethodCaller compareGreaterThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThanEqual");
+    //regexpr
+    private static final MethodCaller findRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "findRegex");
+    private static final MethodCaller matchRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "matchRegex");
+    // isCase
+    private static final MethodCaller isCaseMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "isCase");
+    // isNotCase
+    private static final MethodCaller isNotCaseMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "isNotCase");
+
+    private final WriterController controller;
+    private final UnaryExpressionHelper unaryExpressionHelper;
+    
+    public BinaryExpressionHelper(WriterController wc) {
+        this.controller = wc;
+        this.unaryExpressionHelper = new UnaryExpressionHelper(this.controller);
+    }
+    
+    public WriterController getController(){
+        return controller;
+    }
+    
+    public void eval(BinaryExpression expression) {
+        switch (expression.getOperation().getType()) {
+        case EQUAL: // = assignment
+            evaluateEqual(expression, false);
+            break;
+
+        case COMPARE_EQUAL: // ==
+            evaluateCompareExpression(compareEqualMethod, expression);
+            break;
+
+        case COMPARE_NOT_EQUAL:
+            evaluateCompareExpression(compareNotEqualMethod, expression);
+            break;
+
+        case COMPARE_TO:
+            evaluateCompareTo(expression);
+            break;
+
+        case COMPARE_GREATER_THAN:
+            evaluateCompareExpression(compareGreaterThanMethod, expression);
+            break;
+
+        case COMPARE_GREATER_THAN_EQUAL:
+            evaluateCompareExpression(compareGreaterThanEqualMethod, expression);
+            break;
+
+        case COMPARE_LESS_THAN:
+            evaluateCompareExpression(compareLessThanMethod, expression);
+            break;
+
+        case COMPARE_LESS_THAN_EQUAL:
+            evaluateCompareExpression(compareLessThanEqualMethod, expression);
+            break;
+
+        case LOGICAL_AND:
+            evaluateLogicalAndExpression(expression);
+            break;
+
+        case LOGICAL_OR:
+            evaluateLogicalOrExpression(expression);
+            break;
+
+        case BITWISE_AND:
+            evaluateBinaryExpression("and", expression);
+            break;
+
+        case BITWISE_AND_EQUAL:
+            evaluateBinaryExpressionWithAssignment("and", expression);
+            break;
+
+        case BITWISE_OR:
+            evaluateBinaryExpression("or", expression);
+            break;
+
+        case BITWISE_OR_EQUAL:
+            evaluateBinaryExpressionWithAssignment("or", expression);
+            break;
+
+        case BITWISE_XOR:
+            evaluateBinaryExpression("xor", expression);
+            break;
+
+        case BITWISE_XOR_EQUAL:
+            evaluateBinaryExpressionWithAssignment("xor", expression);
+            break;
+
+        case PLUS:
+            evaluateBinaryExpression("plus", expression);
+            break;
+
+        case PLUS_EQUAL:
+            evaluateBinaryExpressionWithAssignment("plus", expression);
+            break;
+
+        case MINUS:
+            evaluateBinaryExpression("minus", expression);
+            break;
+
+        case MINUS_EQUAL:
+            evaluateBinaryExpressionWithAssignment("minus", expression);
+            break;
+
+        case MULTIPLY:
+            evaluateBinaryExpression("multiply", expression);
+            break;
+
+        case MULTIPLY_EQUAL:
+            evaluateBinaryExpressionWithAssignment("multiply", expression);
+            break;
+
+        case DIVIDE:
+            evaluateBinaryExpression("div", expression);
+            break;
+
+        case DIVIDE_EQUAL:
+            //SPG don't use divide since BigInteger implements directly
+            //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
+            evaluateBinaryExpressionWithAssignment("div", expression);
+            break;
+
+        case INTDIV:
+            evaluateBinaryExpression("intdiv", expression);
+            break;
+
+        case INTDIV_EQUAL:
+            evaluateBinaryExpressionWithAssignment("intdiv", expression);
+            break;
+
+        case MOD:
+            evaluateBinaryExpression("mod", expression);
+            break;
+
+        case MOD_EQUAL:
+            evaluateBinaryExpressionWithAssignment("mod", expression);
+            break;
+
+        case POWER:
+            evaluateBinaryExpression("power", expression);
+            break;
+
+        case POWER_EQUAL:
+            evaluateBinaryExpressionWithAssignment("power", expression);
+            break;
+
+        case ELVIS_EQUAL:
+            evaluateElvisEqual(expression);
+            break;
+
+        case LEFT_SHIFT:
+            evaluateBinaryExpression("leftShift", expression);
+            break;
+
+        case LEFT_SHIFT_EQUAL:
+            evaluateBinaryExpressionWithAssignment("leftShift", expression);
+            break;
+
+        case RIGHT_SHIFT:
+            evaluateBinaryExpression("rightShift", expression);
+            break;
+
+        case RIGHT_SHIFT_EQUAL:
+            evaluateBinaryExpressionWithAssignment("rightShift", expression);
+            break;
+
+        case RIGHT_SHIFT_UNSIGNED:
+            evaluateBinaryExpression("rightShiftUnsigned", expression);
+            break;
+
+        case RIGHT_SHIFT_UNSIGNED_EQUAL:
+            evaluateBinaryExpressionWithAssignment("rightShiftUnsigned", expression);
+            break;
+
+        case KEYWORD_INSTANCEOF:
+            evaluateInstanceof(expression);
+            break;
+
+        case COMPARE_NOT_INSTANCEOF:
+            evaluateNotInstanceof(expression);
+            break;
+
+        case FIND_REGEX:
+            evaluateCompareExpression(findRegexMethod, expression);
+            break;
+
+        case MATCH_REGEX:
+            evaluateCompareExpression(matchRegexMethod, expression);
+            break;
+
+        case LEFT_SQUARE_BRACKET:
+            if (controller.getCompileStack().isLHS()) {
+                evaluateEqual(expression, false);
+            } else {
+                evaluateBinaryExpression("getAt", expression);
+            }
+            break;
+
+        case KEYWORD_IN:
+            evaluateCompareExpression(isCaseMethod, expression);
+            break;
+
+        case COMPARE_NOT_IN:
+            evaluateCompareExpression(isNotCaseMethod, expression);
+            break;
+
+        case COMPARE_IDENTICAL:
+            evaluateCompareExpression(compareIdenticalMethod, expression);
+            break;
+
+        case COMPARE_NOT_IDENTICAL:
+            evaluateCompareExpression(compareNotIdenticalMethod, expression);
+            break;
+
+        default:
+            throw new GroovyBugError("Operation: " + expression.getOperation() + " not supported");
+        }
+    }
+    
+    protected void assignToArray(Expression parent, Expression receiver, Expression index, Expression rhsValueLoader, boolean safe) {
+        // let's replace this assignment to a subscript operator with a
+        // method call
+        // e.g. x[5] = 10
+        // -> (x, [], 5), =, 10
+        // -> methodCall(x, "putAt", [5, 10])
+        ArgumentListExpression ae = new ArgumentListExpression(index,rhsValueLoader);
+        controller.getInvocationWriter().makeCall(
+                parent, receiver, new ConstantExpression("putAt"),
+                ae, InvocationWriter.invokeMethod, safe, false, false);
+        controller.getOperandStack().pop();
+        // return value of assignment
+        rhsValueLoader.visit(controller.getAcg());
+    }
+
+    private static boolean isNull(Expression exp) {
+        if (exp instanceof ConstantExpression){
+            return ((ConstantExpression) exp).getValue()==null;
+        } else {
+            return false;
+        }
+    }
+
+    public void evaluateElvisEqual(BinaryExpression expression) {
+        Token operation = expression.getOperation();
+        BinaryExpression elvisAssignmentExpression =
+                new BinaryExpression(
+                        expression.getLeftExpression(),
+                        Token.newSymbol(Types.EQUAL, operation.getStartLine(), operation.getStartColumn()),
+                        new ElvisOperatorExpression(expression.getLeftExpression(), expression.getRightExpression())
+                );
+
+        this.evaluateEqual(elvisAssignmentExpression, false);
+    }
+
+    public void evaluateEqual(BinaryExpression expression, boolean defineVariable) {
+        AsmClassGenerator acg = controller.getAcg();
+        CompileStack compileStack = controller.getCompileStack();
+        OperandStack operandStack = controller.getOperandStack();
+        Expression rightExpression = expression.getRightExpression();
+        Expression leftExpression = expression.getLeftExpression();
+        ClassNode lhsType = controller.getTypeChooser().resolveType(leftExpression, controller.getClassNode());
+
+        if (    defineVariable &&
+                rightExpression instanceof EmptyExpression &&
+                !(leftExpression instanceof TupleExpression) )
+        {
+            VariableExpression ve = (VariableExpression) leftExpression;
+            BytecodeVariable var = compileStack.defineVariable(ve, controller.getTypeChooser().resolveType(ve, controller.getClassNode()), false);
+            operandStack.loadOrStoreVariable(var, false);
+            return;
+        }
+
+        // let's evaluate the RHS and store the result
+        ClassNode rhsType;
+        if (rightExpression instanceof ListExpression && lhsType.isArray()) {
+            ListExpression list = (ListExpression) rightExpression;
+            ArrayExpression array = new ArrayExpression(lhsType.getComponentType(), list.getExpressions());
+            array.setSourcePosition(list);
+            array.visit(acg);
+        } else if (rightExpression instanceof EmptyExpression) {
+            rhsType = leftExpression.getType();
+            loadInitValue(rhsType);
+        } else {
+            rightExpression.visit(acg);
+        }
+        rhsType = operandStack.getTopOperand();
+
+        boolean directAssignment = defineVariable && !(leftExpression instanceof TupleExpression);
+        int rhsValueId;
+        if (directAssignment) {
+            VariableExpression var = (VariableExpression) leftExpression;
+            if (var.isClosureSharedVariable() && ClassHelper.isPrimitiveType(rhsType)) {
+                // GROOVY-5570: if a closure shared variable is a primitive type, it must be boxed
+                rhsType = ClassHelper.getWrapper(rhsType);
+                operandStack.box();
+            }
+
+            // ensure we try to unbox null to cause a runtime NPE in case we assign 
+            // null to a primitive typed variable, even if it is used only in boxed 
+            // form as it is closure shared
+            if (var.isClosureSharedVariable() && ClassHelper.isPrimitiveType(var.getOriginType()) && isNull(rightExpression)) {
+                operandStack.doGroovyCast(var.getOriginType());
+                // these two are never reached in bytecode and only there 
+                // to avoid verifyerrors and compiler infrastructure hazzle
+                operandStack.box(); 
+                operandStack.doGroovyCast(lhsType);
+            }
+            // normal type transformation 
+            if (!ClassHelper.isPrimitiveType(lhsType) && isNull(rightExpression)) {
+                operandStack.replace(lhsType);
+            } else {
+                operandStack.doGroovyCast(lhsType);
+            }
+            rhsType = lhsType;
+            rhsValueId = compileStack.defineVariable(var, lhsType, true).getIndex();
+        } else {
+            rhsValueId = compileStack.defineTemporaryVariable("$rhs", rhsType, true);
+        }
+        //TODO: if rhs is VariableSlotLoader already, then skip crating a new one
+        BytecodeExpression rhsValueLoader = new VariableSlotLoader(rhsType,rhsValueId,operandStack);
+        
+        // assignment for subscript
+        if (leftExpression instanceof BinaryExpression) {
+            BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
+            if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
+                assignToArray(expression, leftBinExpr.getLeftExpression(), leftBinExpr.getRightExpression(), rhsValueLoader, leftBinExpr.isSafe());
+            }
+            compileStack.removeVar(rhsValueId);
+            return;
+        }
+        
+        compileStack.pushLHS(true);
+
+        // multiple declaration
+        if (leftExpression instanceof TupleExpression) {
+            TupleExpression tuple = (TupleExpression) leftExpression;
+            int i = 0;
+            for (Expression e : tuple.getExpressions()) {
+                VariableExpression var = (VariableExpression) e;
+                MethodCallExpression call = new MethodCallExpression(
+                        rhsValueLoader, "getAt",
+                        new ArgumentListExpression(new ConstantExpression(i)));
+                call.visit(acg);
+                i++;
+                if (defineVariable) {
+                    operandStack.doGroovyCast(var);
+                    compileStack.defineVariable(var, true);
+                    operandStack.remove(1);
+                } else {
+                    acg.visitVariableExpression(var);
+                }
+            }
+        } 
+        // single declaration
+        else if (defineVariable) {
+            rhsValueLoader.visit(acg);
+            operandStack.remove(1);
+            compileStack.popLHS();
+            return;
+        } 
+        // normal assignment
+        else {
+            int mark = operandStack.getStackLength();
+            // to leave a copy of the rightExpression value on the stack after the assignment.
+            rhsValueLoader.visit(acg);
+            TypeChooser typeChooser = controller.getTypeChooser();
+            ClassNode targetType = typeChooser.resolveType(leftExpression, controller.getClassNode());
+            operandStack.doGroovyCast(targetType);
+            leftExpression.visit(acg);
+            operandStack.remove(operandStack.getStackLength()-mark);
+        }
+        compileStack.popLHS();
+        
+        // return value of assignment
+        rhsValueLoader.visit(acg);
+        compileStack.removeVar(rhsValueId);
+    }
+
+    private void loadInitValue(ClassNode type) {
+        MethodVisitor mv = controller.getMethodVisitor();
+        if (ClassHelper.isPrimitiveType(type)) {
+            mv.visitLdcInsn(0);
+        } else {
+            mv.visitInsn(ACONST_NULL);
+        }
+        controller.getOperandStack().push(type);
+    }
+
+    protected void evaluateCompareExpression(MethodCaller compareMethod, BinaryExpression expression) {
+        Expression leftExp = expression.getLeftExpression();
+        TypeChooser typeChooser = controller.getTypeChooser();
+        ClassNode cn = controller.getClassNode();
+        ClassNode leftType = typeChooser.resolveType(leftExp,cn);
+        Expression rightExp = expression.getRightExpression();
+        ClassNode rightType = typeChooser.resolveType(rightExp,cn);
+
+        boolean done = false;
+        if (    ClassHelper.isPrimitiveType(leftType) &&
+                ClassHelper.isPrimitiveType(rightType))
+        {
+            BinaryExpressionMultiTypeDispatcher helper = new BinaryExpressionMultiTypeDispatcher(getController());
+            done = helper.doPrimitiveCompare(leftType, rightType, expression);
+        }
+        
+        if (!done) {
+            AsmClassGenerator acg = controller.getAcg();
+            OperandStack operandStack = controller.getOperandStack();
+            
+            leftExp.visit(acg);
+            operandStack.box();
+            rightExp.visit(acg);
+            operandStack.box();
+    
+            compareMethod.call(controller.getMethodVisitor());
+            ClassNode resType = ClassHelper.boolean_TYPE;
+            if (compareMethod==findRegexMethod) {
+                resType = ClassHelper.OBJECT_TYPE;
+            } 
+            operandStack.replace(resType,2);
+        }
+    }
+    
+    private void evaluateCompareTo(BinaryExpression expression) {
+        Expression leftExpression = expression.getLeftExpression();
+        AsmClassGenerator acg = controller.getAcg();
+        OperandStack operandStack = controller.getOperandStack();
+        
+        leftExpression.visit(acg);
+        operandStack.box();
+
+        // if the right hand side is a boolean expression, we need to autobox
+        Expression rightExpression = expression.getRightExpression();
+        rightExpression.visit(acg);
+        operandStack.box();
+
+        compareToMethod.call(controller.getMethodVisitor());
+        operandStack.replace(ClassHelper.Integer_TYPE,2);
+    }
+
+    private void evaluateLogicalAndExpression(BinaryExpression expression) {
+        MethodVisitor mv = controller.getMethodVisitor();
+        AsmClassGenerator acg = controller.getAcg();
+        OperandStack operandStack = controller.getOperandStack();
+
+        expression.getLeftExpression().visit(acg);
+        operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
+        Label falseCase = operandStack.jump(IFEQ);
+
+        expression.getRightExpression().visit(acg);
+        operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
+        operandStack.jump(IFEQ,falseCase);
+
+        ConstantExpression.PRIM_TRUE.visit(acg);
+        Label trueCase = new Label();
+        mv.visitJumpInsn(GOTO, trueCase);
+
+        mv.visitLabel(falseCase);
+        ConstantExpression.PRIM_FALSE.visit(acg);
+
+        mv.visitLabel(trueCase);
+        operandStack.remove(1); // have to remove 1 because of the GOTO
+    }
+    
+    private void evaluateLogicalOrExpression(BinaryExpression expression) {
+        MethodVisitor mv = controller.getMethodVisitor();
+        AsmClassGenerator acg = controller.getAcg();
+        OperandStack operandStack = controller.getOperandStack();
+
+        Label end = new Label();
+
+        expression.getLeftExpression().visit(acg);
+        operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
+        Label trueCase = operandStack.jump(IFNE);
+        
+        expression.getRightExpression().visit(acg);
+        operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
+        Label falseCase = operandStack.jump(IFEQ);
+        
+        mv.visitLabel(trueCase);
+        ConstantExpression.PRIM_TRUE.visit(acg);
+        operandStack.jump(GOTO, end);
+
+        mv.visitLabel(falseCase);
+        ConstantExpression.PRIM_FALSE.visit(acg);
+        
+        mv.visitLabel(end);
+    }
+    
+    protected void evaluateBinaryExpression(String message, BinaryExpression binExp) {
+        CompileStack compileStack = controller.getCompileStack();
+
+        Expression receiver = binExp.getLeftExpression();
+        Expression arguments = binExp.getRightExpression();
+
+        // ensure VariableArguments are read, not stored
+        compileStack.pushLHS(false);
+        controller.getInvocationWriter().makeSingleArgumentCall(receiver, message, arguments, binExp.isSafe());
+        compileStack.popLHS();        
+    }
+
+    protected void evaluateArrayAssignmentWithOperator(String method, BinaryExpression expression, BinaryExpression leftBinExpr) {
+        CompileStack compileStack    = getController().getCompileStack();
+        AsmClassGenerator acg             = getController().getAcg();
+        OperandStack os              = getController().getOperandStack();
+
+        // e.g. x[a] += b
+        // to avoid loading x and a twice we transform the expression to use
+        // ExpressionAsVariableSlot
+        // -> subscript=a, receiver=x, receiver[subscript]+b, =, receiver[subscript]
+        // -> subscript=a, receiver=x, receiver#getAt(subscript)#plus(b), =, receiver#putAt(subscript)
+        // -> subscript=a, receiver=x, receiver#putAt(subscript, receiver#getAt(subscript)#plus(b))
+        // the result of x[a] += b is x[a]+b, thus:
+        // -> subscript=a, receiver=x, receiver#putAt(subscript, ret=receiver#getAt(subscript)#plus(b)), ret
+        ExpressionAsVariableSlot subscript = new ExpressionAsVariableSlot(controller, leftBinExpr.getRightExpression(), "subscript");
+        ExpressionAsVariableSlot receiver  = new ExpressionAsVariableSlot(controller, leftBinExpr.getLeftExpression(), "receiver");
+        MethodCallExpression getAt = new MethodCallExpression(receiver, "getAt", new ArgumentListExpression(subscript));
+        MethodCallExpression operation = new MethodCallExpression(getAt, method, expression.getRightExpression());
+        ExpressionAsVariableSlot ret = new ExpressionAsVariableSlot(controller, operation, "ret");
+        MethodCallExpression putAt = new MethodCallExpression(receiver, "putAt", new ArgumentListExpression(subscript, ret));
+
+        putAt.visit(acg);
+        os.pop();
+        os.load(ret.getType(), ret.getIndex());
+
+        compileStack.removeVar(ret.getIndex());
+        compileStack.removeVar(subscript.getIndex());
+        compileStack.removeVar(receiver.getIndex());
+    }
+
+    protected void evaluateBinaryExpressionWithAssignment(String method, BinaryExpression expression) {
+        Expression leftExpression = expression.getLeftExpression();
+        AsmClassGenerator acg = controller.getAcg();
+        OperandStack operandStack = controller.getOperandStack();
+        
+        if (leftExpression instanceof BinaryExpression) {
+            BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
+            if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
+                evaluateArrayAssignmentWithOperator(method, expression, leftBinExpr);
+                return;
+            }
+        } 
+
+        evaluateBinaryExpression(method, expression);
+
+        // br to leave a copy of rvalue on the stack. see also isPopRequired()
+        operandStack.dup();
+        
+        controller.getCompileStack().pushLHS(true);
+        leftExpression.visit(acg);
+        controller.getCompileStack().popLHS();
+    }
+    
+    private void evaluateInstanceof(BinaryExpression expression) {
+        OperandStack operandStack = controller.getOperandStack();
+        
+        expression.getLeftExpression().visit(controller.getAcg());
+        operandStack.box();
+        Expression rightExp = expression.getRightExpression();
+        ClassNode classType;
+        if (rightExp instanceof ClassExpression) {
+            ClassExpression classExp = (ClassExpression) rightExp;
+            classType = classExp.getType();
+        } else {
+            throw new RuntimeException(
+                    "Right hand side of the instanceof keyword must be a class name, not: " + rightExp);
+        }
+        String classInternalName = BytecodeHelper.getClassInternalName(classType);
+        controller.getMethodVisitor().visitTypeInsn(INSTANCEOF, classInternalName);
+        operandStack.replace(ClassHelper.boolean_TYPE);
+    }
+
+    private void evaluateNotInstanceof(BinaryExpression expression) {
+        unaryExpressionHelper.writeNotExpression(
+                new NotExpression(
+                        new BinaryExpression(
+                                expression.getLeftExpression(),
+                                Token.newSymbol(KEYWORD_INSTANCEOF, -1, -1),
+                                expression.getRightExpression()
+                        )
+                )
+        );
+    }
+
+    public MethodCaller getIsCaseMethod() {
+        return isCaseMethod;
+    }
+
+    private void evaluatePostfixMethod(int op, String method, Expression expression, Expression orig) {
+        CompileStack compileStack = controller.getCompileStack();
+        final OperandStack operandStack = controller.getOperandStack();
+        
+        // load Expressions
+        VariableSlotLoader usesSubscript = loadWithSubscript(expression);
+
+        // save copy for later
+        operandStack.dup();
+        ClassNode expressionType = operandStack.getTopOperand();
+        int tempIdx = compileStack.defineTemporaryVariable("postfix_" + method, expressionType, true);
+        
+        // execute Method
+        execMethodAndStoreForSubscriptOperator(op,method,expression,usesSubscript,orig);
+        
+        // remove the result of the method call
+        operandStack.pop();        
+        
+        //reload saved value
+        operandStack.load(expressionType, tempIdx);
+        compileStack.removeVar(tempIdx);
+        if (usesSubscript!=null) compileStack.removeVar(usesSubscript.getIndex());
+    }
+
+    public void evaluatePostfixMethod(PostfixExpression expression) {
+        int op = expression.getOperation().getType();
+        switch (op) {
+            case Types.PLUS_PLUS:
+                evaluatePostfixMethod(op, "next", expression.getExpression(), expression);
+                break;
+            case Types.MINUS_MINUS:
+                evaluatePostfixMethod(op, "previous", expression.getExpression(), expression);
+                break;
+        }
+    }
+
+    public void evaluatePrefixMethod(PrefixExpression expression) {
+        int type = expression.getOperation().getType();
+        switch (type) {
+            case Types.PLUS_PLUS:
+                evaluatePrefixMethod(type, "next", expression.getExpression(), expression);
+                break;
+            case Types.MINUS_MINUS:
+                evaluatePrefixMethod(type, "previous", expression.getExpression(), expression);
+                break;
+        }
+    }
+    
+    private void evaluatePrefixMethod(int op, String method, Expression expression, Expression orig) {
+        // load Expressions
+        VariableSlotLoader usesSubscript = loadWithSubscript(expression);
+        
+        // execute Method
+        execMethodAndStoreForSubscriptOperator(op,method,expression,usesSubscript,orig);
+
+        // new value is already on stack, so nothing to do here
+        if (usesSubscript!=null) controller.getCompileStack().removeVar(usesSubscript.getIndex());
+    }
+    
+    private VariableSlotLoader loadWithSubscript(Expression expression) {
+        final OperandStack operandStack = controller.getOperandStack();
+        // if we have a BinaryExpression, let us check if it is with
+        // subscription
+        if (expression instanceof BinaryExpression) {
+            BinaryExpression be = (BinaryExpression) expression;
+            if (be.getOperation().getType()== Types.LEFT_SQUARE_BRACKET) {
+                // right expression is the subscript expression
+                // we store the result of the subscription on the stack
+                Expression subscript = be.getRightExpression();
+                subscript.visit(controller.getAcg());
+                ClassNode subscriptType = operandStack.getTopOperand();
+                int id = controller.getCompileStack().defineTemporaryVariable("$subscript", subscriptType, true);
+                VariableSlotLoader subscriptExpression = new VariableSlotLoader(subscriptType, id, operandStack);
+                // do modified visit
+                BinaryExpression newBe = new BinaryExpression(be.getLeftExpression(), be.getOperation(), subscriptExpression);
+                newBe.copyNodeMetaData(be);
+                newBe.setSourcePosition(be);
+                newBe.visit(controller.getAcg());
+                return subscriptExpression;
+            } 
+        } 
+        
+        // normal loading of expression
+        expression.visit(controller.getAcg());
+        return null;
+    }
+    
+    private void execMethodAndStoreForSubscriptOperator(int op, String method, Expression expression, VariableSlotLoader usesSubscript, Expression orig) {
+        final OperandStack operandStack = controller.getOperandStack();
+        writePostOrPrefixMethod(op,method,expression,orig);
+
+        // we need special code for arrays to store the result (like for a[1]++)
+        if (usesSubscript!=null) {
+            CompileStack compileStack = controller.getCompileStack();
+            BinaryExpression be = (BinaryExpression) expression;
+            
+            ClassNode methodResultType = operandStack.getTopOperand();
+            final int resultIdx = compileStack.defineTemporaryVariable("postfix_" + method, methodResultType, true);
+            BytecodeExpression methodResultLoader = new VariableSlotLoader(methodResultType, resultIdx, operandStack);
+            
+            // execute the assignment, this will leave the right side 
+            // (here the method call result) on the stack
+            assignToArray(be, be.getLeftExpression(), usesSubscript, methodResultLoader, be.isSafe());
+
+            compileStack.removeVar(resultIdx);
+        } 
+        // here we handle a.b++ and a++
+        else if (expression instanceof VariableExpression ||
+            expression instanceof FieldExpression ||
+            expression instanceof PropertyExpression)
+        {
+            operandStack.dup();
+            controller.getCompileStack().pushLHS(true);
+            expression.visit(controller.getAcg());
+            controller.getCompileStack().popLHS();
+        }
+        // other cases don't need storing, so nothing to be done for them
+    }
+
+    protected void writePostOrPrefixMethod(int op, String method, Expression expression, Expression orig) {
+        final OperandStack operandStack = controller.getOperandStack();
+        // at this point the receiver will be already on the stack.
+        // in a[1]++ the method will be "++" aka "next" and the receiver a[1]
+        
+        ClassNode BEType = controller.getTypeChooser().resolveType(expression, controller.getClassNode());
+        Expression callSiteReceiverSwap = new BytecodeExpression(BEType) {
+            @Override
+            public void visit(MethodVisitor mv) {
+                // CallSite is normally not showing up on the 
+                // operandStack, so we place a dummy here with same
+                // slot length.
+                operandStack.push(ClassHelper.OBJECT_TYPE);
+                // change (receiver,callsite) to (callsite,receiver)
+                operandStack.swap();
+                setType(operandStack.getTopOperand());
+                
+                // no need to keep any of those on the operand stack
+                // after this expression is processed, the operand stack
+                // will contain callSiteReceiverSwap.getType()
+                operandStack.remove(2);
+            }
+        };
+        // execute method
+        // this will load the callsite and the receiver normally in the wrong
+        // order since the receiver is already present, but before the callsite
+        // Therefore we use callSiteReceiverSwap to correct the order. 
+        // After this call the JVM operand stack will contain the the result of
+        // the method call... usually simply Object in operandStack
+        controller.getCallSiteWriter().makeCallSite(
+                callSiteReceiverSwap,
+                method,
+                MethodCallExpression.NO_ARGUMENTS,
+                false, false, false, false);
+        // now rhs is completely done and we need only to store. In a[1]++ this 
+        // would be a.getAt(1).next() for the rhs, "lhs" code is a.putAt(1, rhs)
+         
+    }
+    
+    private void evaluateElvisOperatorExpression(ElvisOperatorExpression expression) {
+        MethodVisitor mv = controller.getMethodVisitor();
+        CompileStack compileStack = controller.getCompileStack();
+        OperandStack operandStack = controller.getOperandStack();
+        TypeChooser typeChooser = controller.getTypeChooser();
+        
+        Expression boolPart = expression.getBooleanExpression().getExpression();
+        Expression falsePart = expression.getFalseExpression();
+        
+        ClassNode truePartType = typeChooser.resolveType(boolPart, controller.getClassNode());
+        ClassNode falsePartType = typeChooser.resolveType(falsePart, controller.getClassNode());
+        ClassNode common = WideningCategories.lowestUpperBound(truePartType, falsePartType);
+        
+        // x?:y is equal to x?x:y, which evals to 
+        //      var t=x; boolean(t)?t:y
+        // first we load x, dup it, convert the dupped to boolean, then 
+        // jump depending on the value. For true we are done, for false we
+        // have to load y, thus we first remove x and then load y. 
+        // But since x and y may have different stack lengths, this cannot work
+        // Thus we have to have to do the following:
+        // Be X the type of x, Y the type of y and S the common supertype of 
+        // X and Y, then we have to see x?:y as  
+        //      var t=x;boolean(t)?S(t):S(y)
+        // so we load x, dup it, store the value in a local variable (t), then 
+        // do boolean conversion. In the true part load t and cast it to S, 
+        // in the false part load y and cast y to S 
+
+        // load x, dup it, store one in $t and cast the remaining one to boolean
+        int mark = operandStack.getStackLength();
+        boolPart.visit(controller.getAcg());
+        operandStack.dup();
+        if (ClassHelper.isPrimitiveType(truePartType) && !ClassHelper.isPrimitiveType(operandStack.getTopOperand())) {
+            truePartType = ClassHelper.getWrapper(truePartType);
+        }
+        int retValueId = compileStack.defineTemporaryVariable("$t", truePartType, true);
+        operandStack.castToBool(mark,true);
+        
+        Label l0 = operandStack.jump(IFEQ);
+        // true part: load $t and cast to S
+        operandStack.load(truePartType, retValueId);
+        operandStack.doGroovyCast(common);
+        Label l1 = new Label();
+        mv.visitJumpInsn(GOTO, l1);
+        
+        // false part: load false expression and cast to S
+        mv.visitLabel(l0);
+        falsePart.visit(controller.getAcg());        
+        operandStack.doGroovyCast(common);
+        
+        // finish and cleanup
+        mv.visitLabel(l1);
+        compileStack.removeVar(retValueId);
+        controller.getOperandStack().replace(common, 2);        
+        
+    }
+
+    private void evaluateNormalTernary(TernaryExpression expression) {
+        MethodVisitor mv = controller.getMethodVisitor();
+        OperandStack operandStack = controller.getOperandStack();
+        TypeChooser typeChooser = controller.getTypeChooser();
+        
+        Expression boolPart = expression.getBooleanExpression();
+        Expression truePart = expression.getTrueExpression();
+        Expression falsePart = expression.getFalseExpression();
+        
+        ClassNode truePartType = typeChooser.resolveType(truePart, controller.getClassNode());
+        ClassNode falsePartType = typeChooser.resolveType(falsePart, controller.getClassNode());
+        ClassNode common = WideningCategories.lowestUpperBound(truePartType, falsePartType);
+
+        // we compile b?x:y as 
+        //      boolean(b)?S(x):S(y), S = common super type of x,y
+        // so we load b, do boolean conversion. 
+        // In the true part load x and cast it to S, 
+        // in the false part load y and cast y to S 
+
+        // load b and convert to boolean
+        int mark = operandStack.getStackLength();
+        boolPart.visit(controller.getAcg());
+        operandStack.castToBool(mark,true);
+        
+        Label l0 = operandStack.jump(IFEQ);
+        // true part: load x and cast to S
+        truePart.visit(controller.getAcg());
+        operandStack.doGroovyCast(common);
+        Label l1 = new Label();
+        mv.visitJumpInsn(GOTO, l1);
+        
+        // false part: load y and cast to S
+        mv.visitLabel(l0);
+        falsePart.visit(controller.getAcg());        
+        operandStack.doGroovyCast(common);
+        
+        // finish and cleanup
+        mv.visitLabel(l1);
+        controller.getOperandStack().replace(common, 2);        
+        
+    }
+
+    public void evaluateTernary(TernaryExpression expression) {
+        if (expression instanceof ElvisOperatorExpression) {
+            evaluateElvisOperatorExpression((ElvisOperatorExpression) expression);
+        } else {
+            evaluateNormalTernary(expression);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionMultiTypeDispatcher.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionMultiTypeDispatcher.java b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionMultiTypeDispatcher.java
new file mode 100644
index 0000000..640964f
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionMultiTypeDispatcher.java
@@ -0,0 +1,422 @@
+/*
+ *  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.DynamicVariable;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.runtime.BytecodeInterface8;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.byte_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.char_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.double_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.float_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.int_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.short_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.isIntCategory;
+import static org.codehaus.groovy.ast.tools.WideningCategories.isLongCategory;
+import static org.codehaus.groovy.ast.tools.WideningCategories.isNumberCategory;
+import static org.codehaus.groovy.syntax.TokenUtil.removeAssignment;
+import static org.codehaus.groovy.syntax.Types.DIVIDE;
+import static org.codehaus.groovy.syntax.Types.LEFT_SHIFT;
+import static org.codehaus.groovy.syntax.Types.LEFT_SQUARE_BRACKET;
+import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT;
+import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT_UNSIGNED;
+
+/**
+ * This class is for internal use only!
+ * This class will dispatch to the right type adapters according to the 
+ * kind of binary expression that is provided.
+ */
+public class BinaryExpressionMultiTypeDispatcher extends BinaryExpressionHelper {
+    
+    private static class BinaryCharExpressionHelper extends BinaryIntExpressionHelper {
+        public BinaryCharExpressionHelper(WriterController wc) {
+            super(wc, charArraySet, charArrayGet);
+        }
+        private static final MethodCaller
+            charArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "cArrayGet"),
+            charArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "cArraySet");
+        @Override protected ClassNode getArrayGetResultType() { return ClassHelper.char_TYPE; }
+    }
+    
+    private static class BinaryByteExpressionHelper extends BinaryIntExpressionHelper {
+        public BinaryByteExpressionHelper(WriterController wc) {
+            super(wc, byteArraySet, byteArrayGet);
+        }
+        private static final MethodCaller
+            byteArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "bArrayGet"),
+            byteArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "bArraySet");
+        @Override protected ClassNode getArrayGetResultType() { return ClassHelper.byte_TYPE; }
+    }
+    
+    private static class BinaryShortExpressionHelper extends BinaryIntExpressionHelper {
+        public BinaryShortExpressionHelper(WriterController wc) {
+            super(wc, shortArraySet, shortArrayGet);
+        }
+        private static final MethodCaller
+            shortArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "sArrayGet"),
+            shortArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "sArraySet");
+        @Override protected ClassNode getArrayGetResultType() { return ClassHelper.short_TYPE; }
+    }
+    
+    protected BinaryExpressionWriter[] binExpWriter = initializeDelegateHelpers();
+
+    protected BinaryExpressionWriter[] initializeDelegateHelpers() {
+        return new BinaryExpressionWriter[]{
+                /* 0: dummy  */ new BinaryObjectExpressionHelper(getController()),
+                /* 1: int    */ new BinaryIntExpressionHelper(getController()),
+                /* 2: long   */ new BinaryLongExpressionHelper(getController()),
+                /* 3: double */ new BinaryDoubleExpressionHelper(getController()),
+                /* 4: char   */ new BinaryCharExpressionHelper(getController()),
+                /* 5: byte   */ new BinaryByteExpressionHelper(getController()),
+                /* 6: short  */ new BinaryShortExpressionHelper(getController()),
+                /* 7: float  */ new BinaryFloatExpressionHelper(getController()),
+                /* 8: bool   */ new BinaryBooleanExpressionHelper(getController()),
+        };
+    }
+
+    public static final Map<ClassNode,Integer> typeMap = new HashMap<ClassNode,Integer>(14);
+    static {
+        typeMap.put(int_TYPE,       1); typeMap.put(long_TYPE,          2);
+        typeMap.put(double_TYPE,    3); typeMap.put(char_TYPE,          4);
+        typeMap.put(byte_TYPE,      5); typeMap.put(short_TYPE,         6);
+        typeMap.put(float_TYPE,     7); typeMap.put(boolean_TYPE,       8);
+    }
+    public static final String[] typeMapKeyNames = {"dummy", "int", "long", "double", "char", "byte", "short", "float", "boolean"};
+
+    public BinaryExpressionMultiTypeDispatcher(WriterController wc) {
+        super(wc);
+    }
+
+    private static int getOperandConversionType(ClassNode leftType, ClassNode rightType) {
+        if (isIntCategory(leftType) && isIntCategory(rightType)) return 1;
+        if (isLongCategory(leftType) && isLongCategory(rightType)) return 2;
+        if (isBigDecCategory(leftType) && isBigDecCategory(rightType)) return 0;
+        if (isDoubleCategory(leftType) && isDoubleCategory(rightType)) return 3;
+        return 0;
+    }
+    
+    protected int getOperandType(ClassNode type) {
+        Integer ret = typeMap.get(type);
+        if (ret==null) return 0;
+        return ret;
+    }
+
+    @Deprecated
+    protected boolean doPrimtiveCompare(ClassNode leftType, ClassNode rightType, BinaryExpression binExp) {
+        return doPrimitiveCompare(leftType, rightType, binExp);
+    }
+
+    protected boolean doPrimitiveCompare(ClassNode leftType, ClassNode rightType, BinaryExpression binExp) {
+        Expression leftExp = binExp.getLeftExpression();
+        Expression rightExp = binExp.getRightExpression();
+        int operation = binExp.getOperation().getType();
+        
+        int operationType = getOperandConversionType(leftType,rightType);
+        BinaryExpressionWriter bew = binExpWriter[operationType];
+
+        if (!bew.write(operation, true)) return false;
+            
+        AsmClassGenerator acg = getController().getAcg();
+        OperandStack os = getController().getOperandStack();
+        leftExp.visit(acg);
+        os.doGroovyCast(bew.getNormalOpResultType());
+        rightExp.visit(acg);
+        os.doGroovyCast(bew.getNormalOpResultType());
+        bew.write(operation, false);
+        
+        return true;
+    }
+    
+    @Override
+    protected void evaluateCompareExpression(final MethodCaller compareMethod, BinaryExpression binExp) {
+        ClassNode current =  getController().getClassNode();
+        TypeChooser typeChooser = getController().getTypeChooser();
+        
+        Expression leftExp = binExp.getLeftExpression();
+        ClassNode leftType = typeChooser.resolveType(leftExp, current);
+        Expression rightExp = binExp.getRightExpression();
+        ClassNode rightType = typeChooser.resolveType(rightExp, current);
+        
+        if (!doPrimitiveCompare(leftType, rightType, binExp)) {
+            super.evaluateCompareExpression(compareMethod, binExp);
+        }
+    }
+    
+    @Override
+    protected void evaluateBinaryExpression(final String message, BinaryExpression binExp) {
+        int operation = removeAssignment(binExp.getOperation().getType());
+        ClassNode current =  getController().getClassNode();
+
+        Expression leftExp = binExp.getLeftExpression();
+        ClassNode leftTypeOrig = getController().getTypeChooser().resolveType(leftExp, current);
+        ClassNode leftType = leftTypeOrig;
+        Expression rightExp = binExp.getRightExpression();
+        ClassNode rightType = getController().getTypeChooser().resolveType(rightExp, current);
+        
+        AsmClassGenerator acg = getController().getAcg();
+        OperandStack os = getController().getOperandStack();
+        
+        if (operation==LEFT_SQUARE_BRACKET) {
+            leftType = leftTypeOrig.getComponentType();
+            int operationType = getOperandType(leftType);
+            BinaryExpressionWriter bew = binExpWriter[operationType];
+            if (    leftTypeOrig.isArray() && isIntCastableType(rightExp) && 
+                    bew.arrayGet(operation, true) &&
+                    !binExp.isSafe())
+            {
+                leftExp.visit(acg);
+                os.doGroovyCast(leftTypeOrig);
+                rightExp.visit(acg);
+                os.doGroovyCast(int_TYPE);
+                bew.arrayGet(operation, false);
+                os.replace(bew.getArrayGetResultType(),2);
+            } else {
+                super.evaluateBinaryExpression(message, binExp);
+            }
+        } else if (operation == DIVIDE) { 
+            int operationType = getOperandType(getController().getTypeChooser().resolveType(binExp, current));
+            BinaryExpressionWriter bew = binExpWriter[operationType];
+            if (bew.writeDivision(true)) {
+                leftExp.visit(acg);
+                os.doGroovyCast(bew.getDevisionOpResultType());
+                rightExp.visit(acg);
+                os.doGroovyCast(bew.getDevisionOpResultType());
+                bew.writeDivision(false);
+            } else {
+                super.evaluateBinaryExpression(message, binExp);
+            }
+        } else {
+            int operationType = getOperandConversionType(leftType,rightType);
+            BinaryExpressionWriter bew = binExpWriter[operationType];
+            
+            if ( isShiftOperation(operation) && isIntCastableType(rightExp) &&
+                 bew.write(operation, true)) 
+            {
+                leftExp.visit(acg);
+                os.doGroovyCast(bew.getNormalOpResultType());
+                rightExp.visit(acg);
+                os.doGroovyCast(int_TYPE);
+                bew.write(operation, false);
+            } else if (bew.write(operation, true)) {
+                leftExp.visit(acg);
+                os.doGroovyCast(bew.getNormalOpResultType());
+                rightExp.visit(acg);
+                os.doGroovyCast(bew.getNormalOpResultType());
+                bew.write(operation, false);
+            } else {
+                super.evaluateBinaryExpression(message, binExp);
+            }
+        }
+    }
+    
+    private boolean isIntCastableType(Expression rightExp) {
+        ClassNode type = getController().getTypeChooser().resolveType(rightExp, getController().getClassNode());
+        return isNumberCategory(type);
+    }
+
+    private static boolean isShiftOperation(int operation) {
+        return  operation==LEFT_SHIFT   || 
+                operation==RIGHT_SHIFT  ||
+                operation==RIGHT_SHIFT_UNSIGNED;
+    }
+
+    private static boolean isAssignmentToArray(BinaryExpression binExp) {
+        Expression leftExpression = binExp.getLeftExpression();
+        if (!(leftExpression instanceof BinaryExpression)) return false;
+        BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
+        return leftBinExpr.getOperation().getType() == LEFT_SQUARE_BRACKET;
+    }
+
+    private boolean doAssignmentToArray(BinaryExpression binExp) {
+        if (!isAssignmentToArray(binExp)) return false;
+        // we need to handle only assignment to arrays combined with an operation
+        // special here. e.g x[a] += b
+        
+        int operation = removeAssignment(binExp.getOperation().getType());
+        ClassNode current =  getController().getClassNode();
+        
+        Expression leftExp = binExp.getLeftExpression();
+        ClassNode leftType = getController().getTypeChooser().resolveType(leftExp, current);
+        Expression rightExp = binExp.getRightExpression();
+        ClassNode rightType = getController().getTypeChooser().resolveType(rightExp, current);
+        
+        int operationType = getOperandType(leftType);
+        BinaryExpressionWriter bew = binExpWriter[operationType];
+        
+        boolean simulationSuccess = bew.arrayGet(LEFT_SQUARE_BRACKET, true);
+        simulationSuccess = simulationSuccess && bew.write(operation, true);
+        simulationSuccess = simulationSuccess && bew.arraySet(true);
+        if (!simulationSuccess) return false;
+        
+        AsmClassGenerator acg = getController().getAcg();
+        OperandStack operandStack = getController().getOperandStack();
+        CompileStack compileStack = getController().getCompileStack();
+               
+        // for x[a] += b we have the structure:
+        //   x = left(left(binExp))), b = right(binExp), a = right(left(binExp)))
+        // for array set we need these values on stack: array, index, right 
+        // for array get we need these values on stack: array, index
+        // to eval the expression we need x[a] = x[a]+b
+        // -> arraySet(x,a, x[a]+b) 
+        // -> arraySet(x,a, arrayGet(x,a,b))
+        // --> x,a, x,a, b as operands
+        // --> load x, load a, DUP2, call arrayGet, load b, call operation,call arraySet
+        // since we cannot DUP2 here easily we will save the subscript and DUP x
+        // --> sub=a, load x, DUP, load sub, call arrayGet, load b, call operation, load sub, call arraySet
+        
+        BinaryExpression arrayWithSubscript = (BinaryExpression) leftExp;
+        Expression subscript = arrayWithSubscript.getRightExpression();
+
+        // load array index: sub=a [load x, DUP, load sub, call arrayGet, load b, call operation, load sub, call arraySet]
+        subscript.visit(acg);
+        operandStack.doGroovyCast(int_TYPE);
+        int subscriptValueId = compileStack.defineTemporaryVariable("$sub", ClassHelper.int_TYPE, true);
+        
+        // load array: load x and DUP [load sub, call arrayGet, load b, call operation, load sub, call arraySet] 
+        arrayWithSubscript.getLeftExpression().visit(acg);
+        operandStack.doGroovyCast(leftType.makeArray());
+        operandStack.dup();
+        
+        // array get: load sub, call arrayGet [load b, call operation, load sub, call arraySet]
+        operandStack.load(ClassHelper.int_TYPE, subscriptValueId);
+        bew.arrayGet(LEFT_SQUARE_BRACKET, false);
+        operandStack.replace(leftType, 2);
+        
+        // complete rhs: load b, call operation [load sub, call arraySet]
+        binExp.getRightExpression().visit(acg);
+        if (! (bew instanceof BinaryObjectExpressionHelper)) {
+            // in primopts we convert to the left type for supported binary operations
+            operandStack.doGroovyCast(leftType);  
+        }
+        bew.write(operation, false);
+        
+        // let us save that value for the return
+        operandStack.dup();
+        int resultValueId = compileStack.defineTemporaryVariable("$result", rightType, true);
+
+        // array set: load sub, call arraySet []
+        operandStack.load(ClassHelper.int_TYPE, subscriptValueId);
+        operandStack.swap();
+        bew.arraySet(false);
+        operandStack.remove(3); // 3 operands, the array, the index and the value!
+
+        // load return value
+        operandStack.load(rightType, resultValueId);
+        
+        // cleanup
+        compileStack.removeVar(resultValueId);
+        compileStack.removeVar(subscriptValueId);
+        return true;
+    }
+    
+    @Override
+    protected void evaluateBinaryExpressionWithAssignment(String method, BinaryExpression binExp) {
+        if (doAssignmentToArray(binExp)) return;
+        if (doAssignmentToLocalVariable(method, binExp)) return;
+        super.evaluateBinaryExpressionWithAssignment(method, binExp);
+    }
+
+    private boolean doAssignmentToLocalVariable(String method, BinaryExpression binExp) {
+        Expression left = binExp.getLeftExpression();
+        if (left instanceof VariableExpression) {
+            VariableExpression ve = (VariableExpression) left;
+            Variable v = ve.getAccessedVariable();
+            if (v instanceof DynamicVariable) return false;
+            if (v instanceof PropertyExpression) return false;
+            /* field and declaration we don't return false */
+        } else {
+            return false;
+        }
+        
+        evaluateBinaryExpression(method, binExp);
+        getController().getOperandStack().dup();
+        getController().getCompileStack().pushLHS(true);
+        binExp.getLeftExpression().visit(getController().getAcg());
+        getController().getCompileStack().popLHS();
+        
+        return true;
+    }
+
+    @Override
+    protected void assignToArray(Expression orig, Expression receiver, Expression index, Expression rhsValueLoader, boolean safe) {
+        ClassNode current = getController().getClassNode();
+        ClassNode arrayType = getController().getTypeChooser().resolveType(receiver, current);
+        ClassNode arrayComponentType = arrayType.getComponentType();
+        int operationType = getOperandType(arrayComponentType);
+        BinaryExpressionWriter bew = binExpWriter[operationType];
+        AsmClassGenerator acg = getController().getAcg();
+        
+        if (bew.arraySet(true) && arrayType.isArray() && !safe) {
+            OperandStack operandStack   =   getController().getOperandStack();
+            
+            // load the array
+            receiver.visit(acg);
+            operandStack.doGroovyCast(arrayType);
+            
+            // load index
+            index.visit(acg);
+            operandStack.doGroovyCast(int_TYPE);
+            
+            // load rhs
+            rhsValueLoader.visit(acg);
+            operandStack.doGroovyCast(arrayComponentType);
+            
+            // store value in array
+            bew.arraySet(false);
+            
+            // load return value && correct operand stack stack
+            operandStack.remove(3);
+            rhsValueLoader.visit(acg);
+        } else {        
+            super.assignToArray(orig, receiver, index, rhsValueLoader, safe);
+        }
+    }
+    
+    @Override
+    protected void writePostOrPrefixMethod(int op, String method, Expression expression, Expression orig) {
+        ClassNode type = getController().getTypeChooser().resolveType(orig, getController().getClassNode());
+        int operationType = getOperandType(type);
+        BinaryExpressionWriter bew = binExpWriter[operationType];
+        if (bew.writePostOrPrefixMethod(op,true)) {
+            OperandStack operandStack   =   getController().getOperandStack();
+            // at this point the receiver will be already on the stack
+            operandStack.doGroovyCast(type);
+            bew.writePostOrPrefixMethod(op,false);
+            operandStack.replace(bew.getNormalOpResultType());
+        } else {
+            super.writePostOrPrefixMethod(op, method, expression, orig);
+        }
+    }
+}