United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-6394986 : assert must cause the enclosing top level class to be initialized?

Details
Type:
Bug
Submit Date:
2006-03-07
Status:
Closed
Updated Date:
2017-05-16
Project Name:
JDK
Resolved Date:
2011-07-21
Component:
specification
OS:
generic
Sub-Component:
language
CPU:
generic
Priority:
P4
Resolution:
Fixed
Affected Versions:
6,7,8,9
Fixed Versions:
7 (rc)

Related Reports
Backport:
Relates:
Relates:

Sub Tasks

Description
JLS  chapter "*14.10 The assert Statement*" says:

-----------------JLS---------------------
An assert statement causes the enclosing top level class (if it exists) to be initialized, if it has not already been initialized (??12.4.1).

Discussion

Note that an assertion that is enclosed by a top-level interface does not cause initialization. stmt312

Usually, the top level class enclosing an assertion will already be initialized. However, if the assertion is located within a static nested class, it may be that the initialization has nottaken place.
---------------------------------------

The following code is written to test assertion:
 "An assert statement causes the enclosing top level class (if it exists) to be initialized".

In this test "assert" statement is located within a static nested class:

===============Code================
class Test01 {
   static {
       stmt31201.i = 1;
   }

   static class Test01_1 {
       boolean test() {
           boolean enabled = false;
           assert enabled = true;
           return enabled;
       }
   }
}

public class stmt31201 {
   static int i = 0;

   public static void main(String args[]) {
       System.exit(run(args, System.out) + 95/*STATUS_TEMP*/);
   }

   public static int run(String argv[], PrintStream out) {
       out.println("i = " + i);
       Test01.Test01_1 obj = new Test01.Test01_1();
       obj.test();
       out.println("i = " + i);
       if (i != 1) {
           out.println("Top-level class hasn't been initialized");
           return 2;
       }
       return 0;
   }
}
===============Output============================
i = 0
i = 0
Top-level class hasn't been initialized
================================================

Hence the static initializer in Test_01 hasn't been executed.
It seems that realization contradicts the specification.

                                    

Comments
EVALUATION

Jon writes: The corollary question that was in my mind is: what is the window of time within which I can set the assertion status for a top level class?

The answer is currently undefined, I believe, but no later than the JVM queries the class loader for the assertion status of the top level class.
                                     
2010-09-28
EVALUATION

The preferred way to force the class to be initialized is to use Class.forName as in:
  
        try {        
            Class.forName(klass.getName(), true, klass.getClassLoader());    
        } catch (ClassNotFoundException e) {
	    // Can't happen
        }
                                     
2010-09-28
EVALUATION

.
                                     
2010-09-28
EVALUATION

In the case of classes Test01 and Test01_1, there is no problem with static initializers not having run, because the dereference of stmt31201.i in the assert statement will cause Test01 to be initialized (if it wasn't already). There are however scenarios, characterized in JLS3 14.10, where an assert is executed before its containing class is initialized. So initialization must happen before an assert statement in a class for which assertions are enabled.

Initialization is orthogonal to class nesting, so while assertion status is related to class nesting, assertion status should be orthogonal to class nesting. But in JLS3, it isn't: an assert statement forces initialization of the top-level class to let the JVM learn definitely whether assertions are enabled for the top-level class. If the top-level initialization wasn't forced, a loader could _technically_ lie in a very confusing way: a JVM initializing a nested class could ask its loader if assertions are enabled for it, and the loader could ponder the assertion status of the top-level class and respond "No" _even if the user enabled assertions for the top-level class_. How come? Because JLS3 14.10 says: "Whether or not a top-level class enables assertions is determined by its defining class loader _before_ the [top-level] class is initialized, and cannot be changed thereafter." When exactly is "before"? Must a loader have determined the assertion status of the top-level class at the point in time when the nested class is initialized? It wouldn't be clear; a bad loader could decide arbitrarily while initializing a nested class that assertions are not enabled for the top-level class - the determination has been made before the top-level class is initialized, see! - and then there would be an obvious inconsistency should the top-level class (for which the user wished to enabled assertions) ever be initialized itself. This inconsistency is prevented by the extra requirement that assert forces initialization of the top-level class; that forces a loader to accurately reflect the top-level class's intended assertion status, which will later be accurately reflected in the nested class's assertion status.

However, on further investigation, it turns out that a loader _cannot_ lie because ClassLoader does not expose getAssertionStatus(Class). The JVM consults privately with the ClassLoader implementation to get class assertion status. No user-defined loader can change the default answer, which is presumably truthful (reflects user intention) and consistent across multiple queries. So, after all, requiring top-level class initialization is unnecessary.

Just for fun, observe that determining a top-level class's assertion status _before_ the top-level class is initialized technically conflicts with another JLS3 statement: "At the time a class is initialized, the class's class loader determines whether assertions are enabled".

And morally, if assertions are disabled for a top-level class (and hence its static nested classes too), then having an assertion (in the top-level class or a static nested class) trigger initialization would seem undesirable. JLS3 14.10 says: "If the assertion is disabled, evaluation of the assertion has no effect whatsoever" - initializing a top-level class can have arbitrary visible effects! A static nested class is not so special that its assertions, even if disabled, get to trigger top-level class initialization.

The way forward is to bring the JLS in line with javac - don't force gratuitous top-level class initialization. (For example, "assert true;" is a no-op.) All 14.10 needs to say is:

1) An assert statement that is executed after its class has completed initialization is enabled if and only if the host system has determined that the top level class that lexically contains the assert statement enables assertions.

2) Whether or not a top level class enables assertions is determined no later than the earliest of the initialization of the top level class and the initialization of any class nested in the top level class, and cannot be changed after it has been determined.
  // Making this determination does not trigger top level class initialization.

3) An assert statement that is executed before its class has completed initialization is enabled.
                                     
2006-11-30
EVALUATION

There is no reason to believe that the specification should be changed.  Asserts force initialization because that is the only way that static fields can be used within the assert.  If the enclosing class has not been initialized, then the behaviour of the assert is misleading (at the very least) and undefined (at worst). If the provided example such that the assertion is: 
 
  assert stmt31201.i == 1; 
 
Then this assertion will fail because the static initializer hasn't been run.  This result is unexpected. 
 
However, changing the behaviour could introduce compatibility problems.  We need to understand the scope of the compatibilty problems to make a decision.  It seems that the compatibility impact would be restricted to code which somewhat recklessly relies on the initializer not being run.  Of course, this would only be relevant  when assertions are enabled, which isn't usually the case for production code.  If we require this initialization, then it is possible that if a bug exists due to an uninitialized class which was then initialized because assertions are enabled, the bug might cease to be reproducible.
                                     
2006-08-09
EVALUATION

This is an inconsistency between JLS and javac implementation.
                                     
2006-03-16



Hardware and Software, Engineered to Work Together