A Simple Assertion Facility For the Javatm Programming Language

Final Release



 

I. Introduction

This document specifies a simple assertion facility for the Java programming language. In its intended usage, an assertion is a statement containing a boolean expression that the programmer believes to be true at the time the statement is executed. For example, after unmarshalling all of the elements from a data buffer, a programmer might assert that the number of bytes of data remaining in the buffer is zero. The system executes the assertion by evaluating the boolean expression and reporting an error if the expression evaluates to false. By verifying that the boolean expression is indeed true, the system corroborates the programmer's knowledge of the program and increases confidence that the program is free of bugs. Assertion checking may be disabled for increased performance. Typically, assertion-checking is enabled during program development and testing, and disabled for deployment.

Because assertions may be disabled, programs must not assume that the expressions contained in assertions will be evaluated. Thus, these boolean expressions should generally be free of side effects: evaluating such a boolean expression should not affect any state that is visible after the evaluation is complete. It is not illegal for a boolean expression contained in an assertion to have a side effect, but it is generally inappropriate, as it could cause program behavior to vary depending on whether assertions were enabled or disabled.

Along similar lines, assertions should not be used for argument-checking in public methods. Argument-checking is typically part of the contract of a method, and this contract must be upheld whether assertions are enabled or disabled. Another problem with using assertions for argument checking is that erroneous arguments should result in an appropriate runtime exception (such as IllegalArgumentException, IndexOutOfBoundsException or NullPointerException). An assertion failure will not throw an appropriate exception. Again, it is not illegal to use assertions for argument checking on public methods, but it is generally inappropriate.

All section number references to The Java Language Specification (JLS) contained in this document refer to the second edition.

II. Syntax

A new keyword is added to the language: assert. The use of this keyword is governed by one modified production and one new production in the grammar:
    StatementWithoutTrailingSubstatement:
        <All current possibilities, as per JLS, Section 14.5>
        AssertStatement

    AssertStatement:
        assert Expression1 ;
        assert Expression1 : Expression2 ;
In both forms of the assert statement, Expression1 must have type boolean or a compile-time error results. In the second form, Expression2 must have a value or a compile-time error results. In other words, Expression2 must not be void. (A void expression is a method invocation that invokes a method that is declared void (JLS 15.1).) 

III. Semantics

Assertions may be enabled or disabled on a per-class basis. At the time a class is initialized, prior to the execution of any field initializers for class variables (JLS 8.3.2.1) and static initializers (JLS 8.7), the class's class loader determines whether assertions are enabled or disabled as described below. Once a class has been initialized, its assertion status (enabled or disabled) does not change.

If assertions are disabled in a class, an assert statement contained in that class does nothing. In particular, its "arguments" ( Expression1 and, if it is present, Expression2) are not evaluated. Execution of an assert statement that is contained in a class in which assertions are disabled always completes normally.

If assertions are enabled in a class, an assert statement contained in that class is executed by first evaluating Expression1. If evaluation of Expression1 completes abruptly for some reason, the assert statement completes abruptly for the same reason. Otherwise, execution continues by making a choice based on the value of Expression1:

There is one corner case that demands special treatment. Recall that the assertion status of a class is set at the time it is initialized. It is possible, though generally not desirable, to execute methods or constructors prior to initialization. This can happen when a class hierarchy contains a circularity in its static initialization, as in the following example:
public class Foo {
    public static void main(String args[]) {
        Baz.testAsserts(); // Will execute after Baz is initialized.
    }
}

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"));
    }
}
Invoking Baz.testAsserts() causes Baz to get initialized. Before this can happen, Bar must get initialized. Bar's static initializer again invokes Baz.testAsserts(). Because initialization of Baz is already in progress by the current thread, the second invocation executes immediately, though Baz is not initialized (JLS 12.4.2).

If an assert statement executes before its class is initialized, as in the above example, the execution must behave as if assertions were enabled in the class. In other words, if the program above is executed without enabling assertions, it must print:

Asserts enabled
Asserts disabled

IV. Definite Assignment and Reachability

The following rules apply to a statement assert Expression1 or assert Expression1 : Expression2: The following rule applies to a statement assertExpression1 : Expression2: It is intended that AssertionError never be caught, but it is possible to do so, thus the rules for try statements should treat assertions appearing in a try block similarly to the current treatment of throw statements.

Reachability: An assert statement can complete normally iff it is reachable.

V. Enabling and Disabling Assertions

Each class loader maintains a default assertion status, a boolean value that determines whether assertions are, by default, enabled or disabled in new classes that are subsequently initialized by the class loader. A newly created class loader's default assertion status is false (disabled). It can be changed at any time by invoking a new method in class ClassLoader:
    /**
     * Sets the default assertion status for this class loader.  This setting
     * determines whether classes loaded by this class loader and initialized
     * in the future will have assertions enabled or disabled by default.
     * This setting may be overridden on a per-package or per-class basis by
     * invoking {@link #setPackageAssertionStatus(String,boolean)} or {@link
     * #setClassAssertionStatus(String,boolean)}.
     *
     * @param enabled true if classes loaded by this class loader
     *        will henceforth have assertions enabled by default,
     *        false if they will have assertions disabled by default.
     */
    public void setDefaultAssertionStatus(boolean enabled);
If, at the time that a class is initialized, its class loader has been given specific instructions regarding the assertion status of the class's package name or its class name (via either of the two new methods in ClassLoader described below), those instructions take precedence over the class loader's default assertion status. Otherwise, the class's assertions are enabled or disabled as specified by its class loader's default assertion status.

The following method allows the invoker to set a per-package default assertion status. Note that a per-package default actually applies to a package and any "subpackages."

 
    /**
     * Sets the package default assertion status for the named
     * package.  The package default assertion status determines the
     * assertion status for classes initialized in the future that belong
     * to the named package or any of its "subpackages."
     *
     * A subpackage of a package named p is any package whose name
     * begins with "p." .  For example, javax.swing.text is
     * a subpackage of javax.swing, and both java.util
     * and java.lang.reflect are subpackages of java.
     *
     * In the event that multiple package defaults apply to a given
     * class, the package default pertaining to the most specific package
     * takes precedence over the others.  For example, if
     * java.lang and java.lang.reflect both have
     * package defaults associated with them, the latter package
     * default applies to classes in java.lang.reflect.
     *
     * Package defaults take precedence over the class loader's default
     * assertion status, and may be overridden on a per-class basis by
     * invoking {@link #setClassAssertionStatus(String,boolean)}.
     *
     * @param packageName the name of the package whose package default
     *        assertion status is to be set. A null value
     *        indicates the unnamed package that is "current"
     *        (JLS 7.4.2).
     * @param enabled true if classes loaded by this classloader
     *        and belonging to the named package or any of its subpackages
     *        will have assertions enabled by default, false if they
     *        will have assertions disabled by default.
     */
    public synchronized void setPackageAssertionStatus(String packageName,
                                                       boolean enabled);
The following method is used to set assertion status on a per-class basis:
    /**
     * Sets the desired assertion status for the named top-level class in
     * this class loader and any nested classes contained therein.
     * This setting takes precedence over the  class loader's default
     * assertion status, and over any applicable per-package default.
     * This method has no effect if the named class has already been
     * initialized.  (Once a class is initialized, its assertion status cannot
     * change.)
     *
     * If the named class is not a top-level class, this call will have no
     * effect on the actual assertion status of any class, and its return
     * value is undefined.
     *
     * @param className the fully qualified class name of the top-level class
     *        whose assertion status is to be set.
     * @param enabled true if the named class is to have assertions
     *        enabled when (and if) it is initialized, false if the
     *        class is to have assertions disabled.
     */
    public void setClassAssertionStatus(String className, boolean enabled);
The following method clears any assertion status settings associated with a class loader:
    /**
     * Sets the default assertion status for this class loader to
     * false and discards any package defaults or class assertion
     * status settings associated with the class loader.  This call is
     * provided so that class loaders can be made to ignore any command line
     * assertion status directives and "start with a clean slate."
     */
    public void clearAssertionStatus();
Finally, a method is added to Class to allow the desired assertion status for a specified class to be queried. Few programmers will have any need for this method; it is provided for the benefit of the JRE itself. (It allows a class to determine at the time that it is initialized whether assertions should be enabled.)
    /**
     * Returns the assertion status that would be assigned to this
     * class if it were to be initialized at the time this method is invoked.
     * If this class has had its assertion status set, the most recent
     * setting will be returned; otherwise, if any package default assertion
     * status pertains to this class, the most recent setting for the most
     * specific pertinent package default assertion status is returned;
     * otherwise, if this class is not a system class (i.e., it has a
     * class loader) its class loader's default assertion status is returned;
     * otherwise, the system class default assertion status is returned.
     *
     * Few programmers will have any need for this method; it is provided
     * for the benefit of the JRE itself.  (It allows a class to determine at
     * the time that it is initialized whether assertions should be enabled.)
     * Note that this method is not guaranteed to return the actual
     * assertion status that was (or will be) associated with this class when
     * it was (or will be) initialized.
     *
     * @return the desired assertion status of the specified class.
     * @see    setClassAssertionStatus
     * @see    setPackageAssertionStatus
     * @see    setDefaultAssertionStatus
     */
    public boolean desiredAssertionStatus();
The new ClassLoader instance methods for setting desired assertion status will generally not be used by normal programs, but by interpreters, such as java, and by other execution environments. Interpreters should provide some interpreter-specific facility for users to enable and disable asserts. One possible approach is described in Appendix II.

The new ClassLoader instance methods for setting desired assertion status cannot be used on system classes, which do not have a class loader. No public APIs are provided to enable or disable asserts on system classes, but JRE implementors are strongly encouraged to implement a non-public mechanism whereby their interpreters can set the desired assertion status for system classes. (Such a mechanism is briefly described in Appendix IV.)

VI. New Error Class

The following error class is added to package java.lang:
/**
 * Thrown to indicate that an assertion has failed.
 *
 * The seven one-argument public constructors provided by this
 * class ensure that the assertion error returned by the invocation:
 *
 *     new AssertionError(expression)
 *
 * has as its detail message the string conversion of expression
 * (as defined in The Java Language Specification, Section 15.18.1.1)
 * regardless of the type of expression.
 */
public class AssertionError extends Error {
    /**
     * Constructs an AssertionError with no detail message.
     */
    public AssertionError();

    /**
     * Constructs an AssertionError with its detail message derived from
     * the specified object reference, which is converted to a string as
     * defined in The Java Language Specification, Section 15.18.1.1.
     *
     * If the specified object is an instance of Throwable, it
     * becomes the cause of the newly constructed assertion error.
     */
    public AssertionError(Object detailMessage);

    /**
     * Constructs an AssertionError with its detail message derived from
     * the specified boolean, which is converted to a string as
     * defined in The Java Language Specification, Section 15.18.1.1.
     */
    public AssertionError(boolean detailMessage);

    /**
     * Constructs an AssertionError with its detail message derived from
     * the specified char, which is converted to a string as
     * defined in The Java Language Specification, Section 15.18.1.1.
     */
    public AssertionError(char detailMessage);

    /**
     * Constructs an AssertionError with its detail message derived from
     * the specified int, which is converted to a string as
     * defined in The Java Language Specification, Section 15.18.1.1.
     */
    public AssertionError(int detailMessage);

    /**
     * Constructs an AssertionError with its detail message derived from
     * the specified long, which is converted to a string as
     * defined in The Java Language Specification, Section 15.18.1.1.
     */
    public AssertionError(long detailMessage);

    /**
     * Constructs an AssertionError with its detail message derived from
     * the specified float, which is converted to a string as
     * defined in The Java Language Specification, Section 15.18.1.1.
     */
    public AssertionError(float detailMessage);

    /**
     * Constructs an AssertionError with its detail message derived from
     * the specified double, which is converted to a string as
     * defined in The Java Language Specification, Section 15.18.1.1.
     */
    public AssertionError(double detailMessage);
}

The material contained in the appendices below is not part of this specification, but is intended to inform the use and implementation of the facility. In the parlance of the standards community, these appendices are non-normative.

Appendix I. Usage Notes

In this Appendix we present examples of appropriate and inappropriate use of the assert construct. This appendix is not meant to be exhaustive, but merely to convey the flavor of the intended usage of the construct.

Internal Invariants

It is generally appropriate to sprinkle code liberally with short assertions indicating important assumptions concerning the program's behavior. In the absence of an assertion facility, many programmers did this with comments. For example:
    if (i%3 == 0) {
        ...
    } else if (i%3 == 1) {
        ...
    } else { // (i%3 == 2)
        ...
    }
Whenever you come across a comment that asserts an invariant, you should change it to an assert:
    if (i % 3 == 0) {
        ...
    } else if (i%3 == 1) {
        ...
    } else {
        assert i%3 == 2;
        ...
    }
The above example, where an assert protects the else-clause in a multiway if-statement is quite common. Note, incidentally, that the assertion in the above example may fail if i is negative, as the % operator is not a true mod operator, but computes the remainder, which may be negative.

Control-Flow Invariants

Another good candidate for an assertion is a switch statement with no default case:
    switch(suit) {
      case Suit.CLUBS:
        ...
        break;

      case Suit.DIAMONDS:
        ...
        break;

      case Suit.HEARTS:
        ...
        break;

      case Suit.SPADES:
        ...
    }
It is probably the case that the programmer believes that one of the four cases in the above switch statement will always be executed. This assumption may be tested by adding the following default case:
      default:
        assert false;
More generally, the statement
    assert false;
should be placed at locations the programmer believes to be "unreached" For example, suppose you have a method that looks like this:
    void foo() {
        for (...) {
            if (...)
                return;
         }
        // Execution should never reach this point!!!
    }
The final comment should be replaced by
    assert false;
This technique must be used with some discretion; if a statement is unreachable (JLS 14.20), you'll get a compile time error if you try to assert that it is unreached.

Preconditions, Postconditions, and Class Invariants

While the assert construct is not a full-blown design-by-contract facility, it can help support an informal design-by-contract style of programming.

Preconditions

By existing convention, in the Java programming language preconditions on public methods are enforced by explicit checks inside methods resulting in particular, specified exceptions. For example:
    /**
     * Sets the refresh rate.
     *
     * @param  rate refresh rate, in frames per second.
     * @throws IllegalArgumentException if rate <= 0 or
     *          rate > MAX_REFRESH_RATE.
     */
     public void setRefreshRate(int rate) {
         // Enforce specified precondition in public method
         if (rate <= 0 || rate > MAX_REFRESH_RATE)
             throw new IllegalArgumentException("Illegal rate: " + rate);

         setRefreshInterval(1000/rate);
     }
This convention is unaffected by the addition of the assert construct. This construct is inappropriate for such preconditions, as the enclosing method guarantees that it will enforce the argument checks whether or not assertions are enabled. Further, the assert construct does not throw an exception of the specified type.

If, however, there is a precondition on a nonpublic method and the author of a class believes the precondition to hold no matter what a client does with the class, then an assertion is appropriate:

   /**
    * Sets the refresh interval (which must correspond to a legal frame rate).
    *
    * @param  interval refresh interval in milliseconds.
    */
    private void setRefreshInterval(int interval) {
        // Validate adherence to precondition in nonpublic method
        assert interval > 0 && interval <= 1000/MAX_REFRESH_RATE;

        ... // Set the refresh interval
    }
Note, by the way, that the above assertion will fail if MAX_REFRESH_RATE is greater than 1000 and the user selects a refresh rate greater than 1000; this would, in fact, indicate a bug in the library!

Postconditions

Postcondition checks are best implemented via assertions, whether or not they are in public methods. For example:
    /**
     * Returns a BigInteger whose value is (this-1 mod m).
     *
     * @param  m the modulus.
     * @return this-1 mod m.
     * @throws ArithmeticException  m <= 0 or this BigInteger
     *         has no multiplicative inverse mod m (that is, this BigInteger
     *         is not relatively prime to m).
     */
    public BigInteger modInverse(BigInteger m) {
        if (m.signum <= 0)
            throw new ArithmeticException("Modulus not positive: " + m);
        if (!this.gcd(m).equals(ONE))
            throw new ArithmeticException(this + " not invertible mod " + m);

        ... // Do the computation

        assert this.multiply(result).mod(m).equals(ONE);
        return result;
    }
In practice, one would not check the second precondition, (this.gcd(m).equals(ONE)) prior to performing the computation, because it is wasteful; this precondition is checked as a side effect of performing the modular multiplicative inverse computation by standard algorithms.

Occasionally, it is necessary to save some data prior to performing a computation in order to check a postcondition after it is complete. This can be done with two assert statements and the help of a simple inner class designed to save the state of one or more variables so they can be checked (or rechecked) after the computation. For example, suppose you have a piece of code that looks like this:

    void foo(int[] array) {
        // Manipulate array
        ...

        // At this point, array will contain exactly the ints that it did
        // prior to manipulation, in the same order.
    }
Here is how you could modify the above method to turn the textual assertion into a functional one:
    import java.util.Arrays;

    void foo(final int[] array) {

        class DataCopy {
            private int[] arrayCopy;

            DataCopy() { arrayCopy = (int[]) array.clone(); }

            boolean isConsistent() { return Arrays.equals(array, arrayCopy); }
        }

        DataCopy copy = null;

        // Never fails. If asserts enabled, saves array copy as side effect.
        assert (copy = new DataCopy()) != null;

        ... // Manipulate array

        assert copy.isConsistent();
     }
Note that this idiom easily generalizes to save more than one data field, and to test arbitrarily complex assertions concerning pre-computation and post-computation values. This idiom is less than pretty, but it is expedient. The first assert statement (which is executed solely for its side effect) could be replaced by the more expressive:
    copy = new DataCopy();
but this would copy the array whether or not asserts were enabled, violating the dictum that asserts should have no cost when disabled.

Class Invariants

As noted above, assertions are appropriate for checking internal invariants. The assertion mechanism itself does not enforce any particular style. It is sometimes convenient to combine many expressions that check required constraints into a single internal method that can then be invoked by assertions. For example, suppose one were to implement a balanced tree data structure of some sort. It might be appropriate to implement a private method that checked that the tree was indeed balanced as per the dictates of the data structure:
    // Returns true if this tree is properly balanced
    private boolean balanced() {
        ...
    }
This method is a class invariant. It should be always be true before and after any method completes. To check that this is indeed the case, each public method and constructor should contain the line:
    assert balanced();
immediately prior to each return. It is generally overkill to place similar checks at the head of each public method unless the data structure is implemented by native methods. In this case, it is possible that a memory corruption bug could corrupt "native peer" data structure in between method invocations. A failure of the assertion at the head of such a method would indicate that such memory corruption had occurred. Similarly, it may be advisable to include class invariant checks at the head of methods in classes whose state is modifiable by other classes. (Better yet, design classes so that their state is not directly visible to other classes!)

Lock-Status Assertions

Classes designed for multithreaded use often have non-public methods with preconditions relating to whether or not some lock is held. For example, it is not uncommon to see something like this:
   private Object[] a;

   public synchronized int find(Object key) {
      return find(key, a, 0, a.length);
   }

   // Recursive helper method - always called with a lock on this object
   private int find(Object key, Object[] arr, int start, int len) {
       ...
   }
A static method called holdsLock has been added to Thread to test whether the current thread holds the lock on a specified object. This method can be used in combination with an assert statement to supplement a comment describing a lock-status precondition with an assertion validating the precondition:
   // Recursive helper method - always called with a lock on this.
   private int find(Object key, Object[] arr, int start, int len) {
       assert Thread.holdsLock(this);  // lock-status assertion
   }
Note that it is also possible to write a lock-status assertion asserting that a given lock isn't held.

Removing all Trace of Assertions from Class Files

Programmers developing for resource-constrained devices may wish to strip assertions out of class files entirely. While this makes it impossible to enable assertions in the field, it also reduces class file size, possibly leading to improved class loading performance. In the absence of a high quality JIT, it could lead to decreased footprint and improved runtime performance.

The assertion facility offers no direct support for stripping assertions out of class files. However, the assert statement may be used in conjunction with the "conditional compilation" idiom described in JLS 14.20:

     static final boolean asserts = ... ; // false to eliminate asserts

     if (asserts)
         assert <expr> ;
If asserts are used in this fashion, the compiler is free to eliminate all traces of these asserts from the class files that it generates. It is recommended that this be done where appropriate to support generation of code for resource-constrained devices.

Requiring that Assertions are Enabled

On a related note, programmers of certain critical systems might wish to ensure that assertions are not disabled in the field. Here is an idiom that prevents a class from being initialized if assertions have been disabled for that class:
    static {
        boolean assertsEnabled = false;
        assert assertsEnabled = true; // Intentional side effect
        if (!assertsEnabled)
            throw new RuntimeException("Asserts must be enabled!");
    }

Appendix II. Enabling and Disabling Assertions in the Interpreter

Sun's interpreters (java and java_g) will disable assertions by default. Four command-line switches will be added to Sun's interpreters to selectively enable or disable assertions. These command-line switches are not part of the specification for assertions, but are illustrative of how an implementation of the Java runtime environment might enable and disable assertions. (We do not specify flags as part of the Java platform.)

The following switch enables assertions at various granularities:

java [ -enableassertions | -ea  ] [:<package name>"..." | :<class name> ]
With no arguments, the switch enables assertions by default. With one argument ending in "...", the switch enables assertions in the specified package and any subpackages by default. If the argument is simply "...", the switch enables assertions in the unnamed package in the current working directory. With one argument not ending in "...", the switch enables assertions in the specified class.

The following switch disables assertions in similar fashion:

java [ -disableassertions | -da ] [:<package name>"..." | :<class name> ]
If a single command line contains multiple instances of these switches, they are processed in order before loading any classes. So, for example, to run a program with assertions enabled only in package com.wombat.fruitbat (and any subpackages), the following command could be used:
java -ea:com.wombat.fruitbat... <Main Class>
To run a program with assertions enabled in package com.wombat.fruitbat but disabled in class com.wombat.fruitbat.Brickbat, the following command could be used:
java -ea:com.wombat.fruitbat... -da:com.wombat.fruitbat.Brickbat <Main Class>
The above switches apply to all class loaders, and to system classes (which do not have a class loader). There is one exception to this rule: in their no-argument form, the switches do not apply to system classes. This makes it easy to turn on asserts in all classes except for system classes. A separate switch is provided to enable asserts in all system classes (that is, to set the default assertion status for system classes to true).
 java [ -enablesystemassertions | -esa ]
For symmetry, a corresponding switch is provided to disable asserts in all system classes, though it is not clear that there exists a use for this switch:
 java [ -disablesystemassertions | -dsa ]

Appendix III. Source Compatibility

The addition of the assert keyword to the Java Programming Language causes existing programs that use assert as an identifier to become invalid. The addition of this keyword does not, however, cause any problems with the use of preexisting binaries (.class files). In order to ease the transition from a world where assert is a legal identifier to one where it isn't, it is suggested that compilers support two modes of operation in the initial J2SE release supporting assertions. (For the sake of the explanation, let us assume that this is release 1.4.) The developer selects the appropriate the mode of operation in some manner appropriate to the compiler. Sun's javac will have a command line switch that serves this function:
    -source <release>
where <release> can be "1.3" or "1.4". In the absence of this flag, the behavior will default to "1.3" for maximal source compatibility. Support for 1.3 source compatibility will likely be phased out over time.

Appendix IV. Implementation Notes

It is highly desirable that the execution of disabled assertions have little or no cost at runtime. Further, it is required by this specification that the assertion facility does not require any changes to the class file format or the JVM specification. An implementation that meets these criteria is sketched below. Note that this section describes only one way in which this specification can be implemented; it places no constraints on Java platform implementors, who are free to implement the specification in any manner they see fit.

Class ClassLoader gets three new private fields, which it uses to keep track of desired assertion status directives:

    private boolean defaultAssertionStatus = false;

    // Maps String packageName to Boolean package default assertion status
    private Map packageAssertionStatus = new HashMap();

    // Maps String fullyQualifiedClassName to Boolean assertionStatus
    private Map classAssertionStatus   = new HashMap();
These fields are maintained by the four new public ClassLoader methods in the obvious fashion. In fact, the only thing that these methods do is to modify these fields (or the maps they reference).

The compiler adds a synthetic field to every class that contains one or more assert statements. The synthetic field contains the complement of the actual assertion status of the class:

    static final boolean $assertionsDisabled =
        !<EnclosingTopLevelClassName>.class.desiredAssertionStatus();
This declaration must be placed before all other static variable declarations and static initializers in the class. The name of the field is not part of the specification: it is local to the class and can vary from implementation to implementation and class to class. This field may occur in contexts where static fields are not permitted by the Java programming language, such as inner classes; because it is a synthetic field, this does not present a problem. While the presence of such a field violates the JLS, it does not violate the JVMS.

The assert statement, then, is merely syntactic sugar for this:

do {
    if (!($assertionsDisabled || (Expression1)))
        throw new java.lang.AssertionError(Expression2);
} while(false);
If Expression2 is not present in the assert statement, it is omitted from the above expansion.

The surrounding do-while loop is merely an artifice to transform the if statement to the correct syntactic category (StatementWithoutTrailingSubstatement), and has no semantics associated with it. Note that this implementation works equally well in static methods, instance methods, constructors, static initializers, and instance initializers.

The runtime does not need to treat this code specially in order to "strip out" assertions from classes when they're disabled! Because $assertionsDisabled is final, the JIT can easily determine that the entire do statement has no effect, and eliminate it in the compiled code that it generates. Present day JITs (including the Java HotSpot™ performance engine) do this optimization.

Note also that $assertionsDisabled resolves to the so-named field in the "declaring class" for the containing method. The resulting behavior is clearly desirable, though it may be somewhat surprising. If one class extends another, and assertions are enabled only in the subclass, assertions contained in (any) inherited superclass methods will not be executed.

Note that this implementation does not support control over assertions in system classes, but it can easily be extended to do so as follows. The interpreter makes the command line flags indicating desired assertion status available, via an undocumented JVM entry point. The Class.desiredAssertionStatus method is modified to delegate to this entry point for system classes (which have no class loader). As an optimization, this method can delegate to the JVM for all classes whose class loader has not yet had any of the new ClassLoader methods invoked. Thus, the ClassLoader needn't initialize its packageAssertionStatus and classAssertionStatus fields unless one of the four "desired assertion status directives" is invoked on it. Once this happens, the ClassLoader creates the maps and dumps into them all of the directives described by command line flags. (This requires a second undocumented JVM entry point to obtain the command-line directives.)

Note that this technique has the added advantage of allowing the assert statement to be used in "low-level" system classes. Little java code must be executed in order to determine the desired assertion status of a system class, so it can be done early in the JVM bootstrap sequence. We anticipate that this technique will allow asserts in all system classes except perhaps Object.

Appendix V. Design FAQ

This collection of frequently asked questions concerning the design of the assertion facility is meant to serve as a design rationale.

General Questions

  1. Why provide an assertion facility at all, given that one can program assertions atop the Java programming language with no special support?

  2. Although ad hoc implementations are possible, they are of necessity either ugly (requiring an if statement for each assertion) or inefficient (evaluating the condition even if assertions are disabled). Further, each ad hoc implementation has its own means of enabling and disabling assertions, which lessens the utility of these implementations, especially for debugging in the field. As a result of these shortcomings, assertions have never become a part of the culture. Adding assertion support to the platform stands a good chance of rectifying this situation over time.

  3. Why does this facility justify a language change, as opposed to a library solution?

  4. We recognize that a language change is a serious effort, not to be undertaken lightly. The library approach was considered seriously. It was, however, deemed essential that the runtime cost of assertions be negligible if they are disabled. In order to achieve this with a library, the programmer is forced to hard-code each assertion as an if statement. Many programmers would not do this: either they'd omit the if statement and performance would suffer, or they'd ignore the facility entirely. Note also that assertions were contained in James Gosling's original specification for the Java programming language, nee Oak. Assertions were removed from the Oak specification because time constraints prevented a satisfactory design and implementation.

  5. Why not provide a full-fledged design-by-contract facility with preconditions, postconditions and class invariants, like the one in the Eiffel programming language?

  6. We seriously considered providing such a facility, but were unable to convince ourselves that it is possible to graft it onto the Java programming language without massive changes to the Java platform libraries, and massive inconsistencies between old and new libraries. Further, we were not convinced that such a facility would preserve the simplicity that is the hallmark of the Java programming language. On balance, we came to the conclusion that a simple boolean assertion facility was a clear knee-in-the-curve, and far less risky. It's worth noting that adding a boolean assertion facility to the language doesn't preclude adding a full-fledged design-by-contract facility at some time in the future.

    The simple assertion facility does enable a limited form of design-by-contract style programming. The assert statement is appropriate for postcondition and class invariant checking, and precondition checking in non-public methods. Precondition checking in public methods should still be performed by checks inside methods that result in particular, documented exceptions, such as IllegalArgumentException and IllegalStateException.

  7. In addition to boolean assertions, why not provide an assert-like construct to suppress the execution of an entire block of code if assertions are disabled?

  8. Providing such a construct would encourage programmers to put complex assertions inline, when they are better relegated to separate methods.

Compatibility

  1. Won't the new keyword cause compatibility problems with existing programs that use assert as an identifier?

  2. Yes, for source files. (Binaries for classes that use assert as an identifier will continue to work fine.) To ease the transition, we describe a strategy whereby developers can continue to use assert as an identifier during a transitional period.

  3. Doesn't the implementation sketch in Appendix IV produce class files that cannot be run against older JREs?

  4. Yes. Class files will contain calls to the new ClassLoader and Class methods, such as desiredAssertionStatus. If a class file containing calls to these methods is run against an older JRE (whose ClassLoader class doesn't define the methods), the program will fail at run time, throwing a NoSuchMethodError. While it would be possible to circumvent this problem by catching the NoSuchMethodError, Java platform implementors are under no obligation to do this.

Detailed Syntax and Semantics

  1. Why allow primitive types in Expression2?

  2. There is no compelling reason to restrict the type of this expression. Allowing arbitrary types provides convenience, for example, allowing developers to easily associate a unique integer code with each assertion. Further, it makes this expression feel like the argument to System.err.println(...), which is desirable.

The AssertionError Class

  1. When an AssertionError is generated by an assert statement in which Expression2 is absent, why isn't the program text of the asserted condition used as the detail message (such as "height < maxHeight")?

  2. While doing so might improve out-of-the-box usefulness of assertions in some cases, the benefit doesn't justify the cost of adding all these string constants to .class files and runtime images.

  3. Why doesn't an AssertionError allow access to the object that generated it? Similarly, why not pass an arbitrary object from the assertion to the AssertionError in place of a "detail message"?

  4. Access to these objects would encourage programmers to attempt to recover from assertion failures, which defeats the purpose of the facility.

  5. Why not provide "context accessors" (getFile, getLine, getMethod) on AssertionError?

  6. This facility is best provided on Throwable, so it may be used for all throwables, not just assertion errors. We enhanced Throwable with the getStackTrace method, providing programmatic access to this information for every stack frame.

  7. Why is AssertionError a subclass of Error rather than RuntimeException?

  8. This issue was controversial. The expert group discussed it at at length and came to the conclusion that Error was more appropriate, to discourage programmers from attempting to recover from assertion failures. It is, in general, difficult or impossible to localize the source of an assertion failure. Such a failure indicates that the program is operating "outside of known space," and attempts to continue execution are likely to be harmful. Further, convention dictates that methods specify most runtime exceptions they may throw (via "@throws" tags). It makes little sense to include in a method's specification the circumstances under which it may generate an assertion failure. This information may be regarded as an implementation detail, which can change from implementation to implementation and release to release.

Enabling and Disabling Assertions

  1. Why not provide a compiler flag to completely eliminate assertions from object files?

  2. It is a firm requirement that it be possible to enable assertions in the field for enhanced serviceability. It would have been possible to also permit developers to eliminate assertions from object files at compile time. However, since assertions can contain side effects (though they generally should not), such a flag could alter the behavior of a program in significant ways. It is viewed as good thing that there is only one semantics associated with each valid program. Also, we want to encourage users to leave asserts in object files so they can be enabled in the field. Finally, the standard "conditional compilation" idiom described in JLS 14.20 can be used to achieve this effect for developers who really want it.

  3. Why does the setPackageAssertionStatus method (and the corresponding interpreter flag) have "package tree" semantics instead of the more obvious package semantics?

  4. Hierarchical control is useful, as programmers really do use package hierarchies to organize their code. For example, package-tree semantics allow assertions to be enabled or disabled in all of Swing in one fell swoop.

  5. Why doesn't the setClassAssertionStatus method throw an exception if it is invoked when it's too late to set the assertion status (after the named class has been initialized)?

  6. No action (other than perhaps a warning message) is necessary or desirable if it's too late to set the assertion status. Furthermore, it is, in general, difficult for the programmer to predict whether a given class has been initialized.

  7. Why not overload a single method to take the place of setDefaultAssertionStatus and setAssertionStatus?

  8. Clarity in method naming is for the greater good.

  9. Why not tweak the semantics of desiredAssertionStatus to make it more "programmer friendly" by returning the actual assertion status if a class is already initialized?

  10. It's not clear that there would be any use for the resulting method. The method isn't designed for application programmer use, and it seems inadvisable to make it slower and more complex than necessary.

  11. Why is there no RuntimePermission to prevent applets from enabling/disabling assertions?

  12. While applets have no reason to call any of the ClassLoader methods for modifying desired assertion status, allowing them to do so seems harmless. At worst, an applet can mount a weak denial-of-service attack by turning on asserts in classes that have yet to be initialized. Moreover, applets can only affect the assertion status of classes that are to be initialized by class loaders that the applets can get their hands on. There already exists a RuntimePermission to prevent untrusted code from gaining access to class loaders("getClassLoader").

  13. Why not provide a construct to query the assertion status of the containing class?

  14. Such a construct would encourage people to inline complex assertion code, which we view as a bad thing:

        if (assertsEnabled()) {
            ...
        }
    Further, it is straightforward to query the assertion status atop the current API, if you feel you must:
       boolean assertsEnabled = false;
       assert assertsEnabled = true;  // Intentional side effect!!!
       // Now assertsEnabled is set to the correct value
  15. Why does an assert statement that executes before its class is initialized behave as if assertions were enabled in the class?

  16. Few programmers are aware of the fact that a class's constructors and methods can run prior to its initialization. When this happens, it is quite likely that the class's invariants have not yet been established, which can cause serious and subtle bugs. Any assertion that executes in this state is likely to fail, alerting the programmer to the problem. Thus, it is generally helpful to the programmer to execute all assertions encountered while in this state.

Copyright 2000-2002 Sun Microsystems, Inc., 901 San Antonio Road, Palo Alto, California 94303 U.S.A. All rights reserved.