[ 
https://issues.apache.org/jira/browse/GROOVY-10909?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Eric Milles closed GROOVY-10909.
--------------------------------
    Resolution: Incomplete

I was able to reduce the number of branches by 2 with this kind of thing, but 
the extra complexity did not seem worth it.  And you would still need to 
provide null for both in operands to get the branch coverage.

{code:java}

    private Expression transformInOperation(final BinaryExpression bin, final 
boolean in) {
        Expression leftExpression = bin.getLeftExpression(), rightExpression = 
bin.getRightExpression();

        // transform "left [!]in right" into "right.is[Not]Case(left)"
        MethodCallExpression call = callX(rightExpression, in ? "isCase" : 
"isNotCase", leftExpression);
        call.setImplicitThis(false); call.setSourcePosition(bin); 
call.copyNodeMetaData(bin);
        
call.setMethodTarget(bin.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
        // GROOVY-7473: no null test for simple cases
        if (rightExpression instanceof ListExpression
                || rightExpression instanceof MapExpression
                || rightExpression instanceof RangeExpression
                || rightExpression instanceof ClassExpression
                ||(rightExpression instanceof ConstantExpression
                            && !isNullConstant(rightExpression))
                // rightExpression instanceof VariableExpression
                || isThisOrSuper(rightExpression))//GROOVY-10909
            return staticCompilationTransformer.transform(call);

        // GROOVY-6137, GROOVY-7473: null safety and one-time evaluation
/*
        call.setObjectExpression(rightExpression = 
transformRepeatedReference(rightExpression));
        Expression safe = ternaryX(new 
CompareToNullExpression(rightExpression,true), new 
CompareToNullExpression(leftExpression,in), call);
        safe.putNodeMetaData("classgen.callback", 
classgenCallback(call.getObjectExpression()));
        return staticCompilationTransformer.transform(safe);
*/
        return new 
BinaryExpression(staticCompilationTransformer.transform(leftExpression), 
bin.getOperation(), staticCompilationTransformer.transform(rightExpression)) {
            {
                setType(ClassHelper.boolean_TYPE);
            }
            @Override
            public Expression transformExpression(ExpressionTransformer 
transformer) {
                setRightExpression(transformer.transform(getRightExpression()));
                setLeftExpression(transformer.transform(getLeftExpression()));
                return this;
            }
            @Override
            public void visit(GroovyCodeVisitor visitor) {
                if (!(visitor instanceof AsmClassGenerator)) {
                    visitor.visitBinaryExpression(this);
                    return;
                }

                WriterController wc = 
((AsmClassGenerator)visitor).getController();
                MethodVisitor mv = wc.getMethodVisitor();
                Label done = new Label();

                getRightExpression().visit(visitor); // case value
                ClassNode caseType = wc.getOperandStack().getTopOperand();
                if (!ClassHelper.isPrimitiveType(caseType)) {
                    wc.getOperandStack().dup();
                    Label makeCall = 
wc.getOperandStack().jump(Opcodes.IFNONNULL);
                    wc.getOperandStack().pop(); // drop null
                    new CompareToNullExpression(getLeftExpression(), 
in).visit(visitor);
                    mv.visitJumpInsn(Opcodes.GOTO, done);
                    mv.visitLabel(makeCall);
                }
                wc.getOperandStack().remove(1); // case value or null check

                int tmp = 
wc.getCompileStack().defineTemporaryVariable("$caseValue", caseType, false);
                BytecodeHelper.store(mv, caseType, tmp);
                int top = wc.getOperandStack().getStackLength();

                call.setObjectExpression(bytecodeX(caseType, x -> { 
BytecodeHelper.load(mv, caseType, tmp); }));
                call.setArguments(getLeftExpression()); // switch value
                call.visit(visitor);
                wc.getOperandStack().castToBool(top, false);
                wc.getCompileStack().removeVar(tmp);
                mv.visitLabel(done);
/*
                Expression caseValue = 
transformRepeatedReference(getRightExpression());
                caseValue.visit(visitor);

                ClassNode caseType = wc.getOperandStack().getTopOperand();
                if (!ClassHelper.isPrimitiveType(caseType)) {
                    Label makeCall = 
wc.getOperandStack().jump(Opcodes.IFNONNULL);
                    new CompareToNullExpression(getLeftExpression(), 
in).visit(visitor);
                    mv.visitJumpInsn(Opcodes.GOTO, done);
                    mv.visitLabel(makeCall);
                } else {
                    wc.getOperandStack().pop();
                }

                int mark = wc.getOperandStack().getStackLength();
                call.setArguments(getLeftExpression());
                call.setObjectExpression(caseValue);
                call.visit(visitor);
                wc.getOperandStack().castToBool(mark, false);
                wc.getOperandStack().remove(1);
                mv.visitLabel(done);

                classgenCallback(caseValue).accept(wc);
*/
            }
        };
    }
{code}

> bytecode for in, !in, !() and ?:
> --------------------------------
>
>                 Key: GROOVY-10909
>                 URL: https://issues.apache.org/jira/browse/GROOVY-10909
>             Project: Groovy
>          Issue Type: Improvement
>          Components: bytecode, Compiler, Static compilation
>            Reporter: Eric Milles
>            Assignee: Eric Milles
>            Priority: Minor
>              Labels: bytecode
>
> Consider the following:
> {code:groovy}
> List getList() { ['xx'] }
> @groovy.transform.CompileStatic
> void test(object) {
>   def others = [], strings = []
>   if (object in list) { // 4 of 8 branches missed
>     strings << object
>   }
>   if (!(object in list)) { // 4 of 10 branched missed
>     others << object
>   }
>   println others
>   println strings
> }
> test(null)
> test('xx')
> test([''])
> {code}
> When unit testing, I noticed 4 of 10 branches missed for "if (!(item in 
> list)) ...".  When compiled and executed dynamically, it shows 2 of 2 
> branches covered (safe call to "list.isCase(item)").
> Static compilation transforms "item in list" into "list == null ? item == 
> null : list.isCase(item)".  This is certainly where all the branches are 
> coming from.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to