Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java assertions - $assertionsDisabled vs $assertionsEnabled

Assertions in java compile down to a private synthetic static boolean added to a test - the proposal is nicely documented here:

JSR Assertion Proposal

In it, we create

final private static boolean $assertionsEnabled = ClassLoader.desiredAssertionStatus(className);

and then

assert(X)
becomes
if ($assertionsEnabled && !x) { throw }

Which makes perfect sense ;)

However, I've noticed that what I actually get is

public void test1(String s) {
    assert (!s.equals("Fred"));
    System.out.println(s);
}

becomes

static final /* synthetic */ boolean $assertionsDisabled;

public void test1(String s) {
    if ((!(AssertTest.$assertionsDisabled)) && (s.equals("Fred"))) {
        throw new AssertionError();
    }
    System.out.println(s);
}

static {
    AssertTest.$assertionsDisabled = !(AssertTest.class.desiredAssertionStatus());
}

I can't find any documentation as to why they went with a NEGATIVE test, rather than a positive test - i.e. the original proposal captured assertionsENABLED, now we use assertionsDISABLED.

The only thing I can think of is that this would possibly (POSSIBLY!) generate better branch prediction, but that seems like a pretty lame guess to me - the Java philosophy is (almost) always to make the bytecode simple, and let the JIT sort out optimisations.

( note that this isn't a question about how assertions work - I know that! :) )

( As an aside, it's quite interesting to see that this leads to incorrect tutorials! 6.2.1 of this tutorial, which someone quoted in response to a previous SO question on assertions gets the sense of the test wrong! :)

Any ideas?

like image 872
lab27 Avatar asked May 31 '13 09:05

lab27


2 Answers

This is actually done for a reason, not merely for something like a more compact bytecode or faster condition execution. If you look at the Java Language Specification, §14.10, you will see the following note:

An assert statement that is executed before its class or interface has completed initialization is enabled.

There's also an example that contains an initialization loop:

class Bar {
    static {
        Baz.testAsserts(); 
        // Will execute before Baz is initialized!
    }
}
class Baz extends Bar {
    static void testAsserts() {
        boolean enabled = false;
        assert  enabled = true;
        System.out.println("Asserts " + 
               (enabled ? "enabled" : "disabled"));
    }
}

As Bar is a superclass for Baz, it must be initialized before Baz initialization. However, its initializer executes an assertion statement in the context of Baz class that is not initialized yet, so there was no chance to set the $assertionsDisabled field. In this case, the field has its default value, and everything works according to the spec: assertion is executed. Had we have an $assertionsEnabled field, the assertions for an uninitialized class would not be executed, so it would violate the specification.

like image 102
Tagir Valeev Avatar answered Oct 19 '22 23:10

Tagir Valeev


The boolean is actually implemented with an integer. There is a common believe that comparison with zero is quicker, but I don't see any reason to use disable instead of enabled.

IMHO, as false is the default for boolean, I try to chose a flag which has a default value of false In this case $assertionsEnabled would make more sense.

like image 42
Peter Lawrey Avatar answered Oct 19 '22 23:10

Peter Lawrey