http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/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/0edfcde9/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:[email protected]">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/0edfcde9/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:[email protected]">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/0edfcde9/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/0edfcde9/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..a85c7fd --- /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 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); + } + } +}
