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);
+ }
+ }
+}