in src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionHelper.java [395:594]
public void evaluateEqual(final BinaryExpression expression, final boolean defineVariable) {
AsmClassGenerator acg = controller.getAcg();
MethodVisitor mv = controller.getMethodVisitor();
CompileStack compileStack = controller.getCompileStack();
OperandStack operandStack = controller.getOperandStack();
Expression leftExpression = expression.getLeftExpression();
Expression rightExpression = expression.getRightExpression();
boolean singleAssignment = !(leftExpression instanceof TupleExpression);
boolean directAssignment = defineVariable && singleAssignment; //def x=y
boolean returnRightValue = !Boolean.TRUE.equals(expression.getNodeMetaData(AsmClassGenerator.ELIDE_EXPRESSION_VALUE));
// TODO: LHS has not been visited -- it could be a variable in a closure and type chooser is not aware.
ClassNode lhsType = controller.getTypeChooser().resolveType(leftExpression, controller.getClassNode());
if (directAssignment && rightExpression instanceof EmptyExpression) {
BytecodeVariable v = compileStack.defineVariable((Variable) leftExpression, lhsType, false);
if (returnRightValue) operandStack.loadOrStoreVariable(v, false);
return;
}
// evaluate RHS and store its value
if (lhsType.isArray() && rightExpression instanceof ListExpression) { // array = [ ... ]
Expression array = new ArrayExpression(lhsType.getComponentType(), ((ListExpression) rightExpression).getExpressions());
array.setSourcePosition(rightExpression);
array.setType(lhsType);
array.visit(acg);
} else if (rightExpression instanceof EmptyExpression) { // define field
/* */ if (ClassHelper.isPrimitiveDouble(lhsType)) {
mv.visitInsn(DCONST_0);
} else if (ClassHelper.isPrimitiveFloat(lhsType)) {
mv.visitInsn(FCONST_0);
} else if (ClassHelper.isPrimitiveLong(lhsType)) {
mv.visitInsn(LCONST_0);
} else if (ClassHelper.isPrimitiveType(lhsType)) {
mv.visitInsn(ICONST_0);
} else {
mv.visitInsn(ACONST_NULL);
}
operandStack.push(lhsType);
} else {
rightExpression.visit(acg);
}
ClassNode rhsType = operandStack.getTopOperand();
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()) && isNullConstant(rightExpression)) {
operandStack.doGroovyCast(var.getOriginType());
// these two are never reached in bytecode and only there
// to avoid verify errors and compiler infrastructure hazzle
operandStack.box();
operandStack.doGroovyCast(lhsType);
}
// normal type transformation
if (!ClassHelper.isPrimitiveType(lhsType) && isNullConstant(rightExpression)) {
operandStack.replace(lhsType);
} else {
operandStack.doGroovyCast(lhsType);
}
// store value
BytecodeVariable v = compileStack.defineVariable(var, lhsType, true);
operandStack.remove(1);
if (returnRightValue) {
new VariableSlotLoader(lhsType, v.getIndex(), operandStack).visit(acg);
}
return;
}
// GROOVY-10918: direct store to local variable or parameter (no temp)
if (!defineVariable && leftExpression instanceof VariableExpression) {
BytecodeVariable v = compileStack.getVariable(leftExpression.getText(), false);
if (v != null) {
if (returnRightValue) operandStack.dup();
operandStack.storeVar(v);
return;
}
}
// GROOVY-11288: get value from the stack
if (singleAssignment && !returnRightValue
&& !(leftExpression instanceof BinaryExpression)) {
compileStack.pushLHS(true);
leftExpression.visit(acg);
compileStack.popLHS();
return;
}
int rhsValueId = compileStack.defineTemporaryVariable("$rhs", rhsType, true);
// TODO: if RHS is already a VariableSlotLoader, then skip creating a new one
Expression rhsValueLoader = new VariableSlotLoader(rhsType, rhsValueId, operandStack);
// subscript assignment
if (leftExpression instanceof BinaryExpression) {
var leftBinExpr = (BinaryExpression) leftExpression;
if (leftBinExpr.getOperation().getType() == LEFT_SQUARE_BRACKET) {
assignToArray(expression, leftBinExpr.getLeftExpression(), leftBinExpr.getRightExpression(), rhsValueLoader, leftBinExpr.isSafe());
}
compileStack.removeVar(rhsValueId);
return;
}
compileStack.pushLHS(true);
if (singleAssignment) {
int mark = operandStack.getStackLength();
rhsValueLoader.visit(acg);
leftExpression.visit(acg);
operandStack.remove(operandStack.getStackLength() - mark);
} else { // multiple declaration or assignment
MethodCallExpression iterator = callX(rhsValueLoader, "iterator");
iterator.setImplicitThis(false);
iterator.visit(acg);
int iteratorId = compileStack.defineTemporaryVariable("$iter", true);
Expression seq = new VariableSlotLoader(iteratorId, operandStack);
MethodCallExpression hasNext = callX(seq, "hasNext");
hasNext.setImplicitThis(false);
boolX(hasNext).visit(acg);
Label done = new Label(), useGetAt = new Label();
Label useGetAt_noPop = operandStack.jump(IFEQ);
MethodCallExpression next = callX(seq, "next");
next.setImplicitThis(false);
next.visit(acg);
// check if first element is RHS; indicative of DGM#iterator(Object)
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, rhsValueId);
mv.visitJumpInsn(IF_ACMPEQ, useGetAt);
boolean first = true;
for (Expression e : (TupleExpression) leftExpression) {
if (first) {
first = false;
} else {
ternaryX(hasNext, next, nullX()).visit(acg);
}
if (defineVariable) {
Variable v = (Variable) e;
operandStack.doGroovyCast(v);
compileStack.defineVariable(v, true);
operandStack.remove(1);
} else {
e.visit(acg);
}
}
mv.visitJumpInsn(GOTO, done);
mv.visitLabel(useGetAt);
mv.visitInsn(POP); // discard result of "rhs.iterator().next()"
mv.visitLabel(useGetAt_noPop);
int i = 0;
for (Expression e : (TupleExpression) leftExpression) {
MethodCallExpression getAt = callX(rhsValueLoader, "getAt", constX(i++, true));
getAt.setImplicitThis(false);
getAt.visit(acg);
if (defineVariable) {
Variable v = (Variable) e;
operandStack.doGroovyCast(v);
BytecodeVariable bcv = compileStack.getVariable(v.getName());
if (bcv.isHolder()) {
operandStack.box();
operandStack.remove(1);
compileStack.createReference(bcv);
continue; // Reference stored in v
}
}
e.visit(acg);
}
mv.visitLabel(done);
compileStack.removeVar(iteratorId);
}
compileStack.popLHS();
if (returnRightValue)
rhsValueLoader.visit(acg);
compileStack.removeVar(rhsValueId);
}