public void evaluateEqual()

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