System classes as indicated by the configuration are loaded first. After this the root classes are loaded. For each class all other classes which are referenced in the constant pool and which have a class file (arrays may be referenced but have no class files) are kept in an import list and subsequently loaded.
System classes are classes like the kernel or heap. They must be loaded in any case.
In the first run all entries are read. During the second run objects of type item are created if necessary and references are resolved. For every class, method or field an object is inserted in the object directory. In the third run the constant pool is reduced and includes now only references to the object directory.
Each item has the fields index, offset and address. The meaning of this fields varies with the type of the item and is described in Fields of Item.
Special consideration has to be paided for references to arrays. If an array is created as in
ClassA[] obj = new ClassA[];
there will be a reference to [LClassA; in the constant pool and the class file reader creates an array descriptor object. However, there is no such reference for the following code
Object obj = new ClassA[];
and in the Bytecode we only have anewarray ClassA. For this case we must make sure that an array descriptor is created as well. The same is true for arrays of base types.
For each class an array interfaces holds all interfaces which this class implements. In if this interfaces extend other interfaces, the superinterfaces are included in this array as well.
The Bytecode of all methods are analyzed and the attribute flags of all classes and methods are properly set. For this purpose each object of the object directory has a field accAndPropFlags. In the interface org.deepjava.classItems.ICclassFileConsts many flags are defined, such as
byte apfPublic = 0, apfPrivate = 1, apfProtected = 2, apfStatic = 3, ... byte dpfBase = 15; dpfClassLoaded = dpfBase+0, dpfRootClass = dpfBase+1, ...
For instance, each class will be marked if an instance of it is ever created or a class will be marked if one of its methods is ever called with invokeinterface.
When reading the constant pool of a class many fields or methods of other classes are needed for whom entries in the object directory were not yet created. For these stubs are created. At the end all stubs have to be replaced by their real object.
At the end all stubs have to be replaced, miscellaneous class information is added, fields and methods are sorted.
Generally all items are linked in lists (using the field next). All root classes are listed in a array rootClasses which is a static member of the class org.deepjava.classItems.Class. There are more lists (all static members in the same class), like:
There a a special case where an interface needs a type descriptor. This happens when type testing for an array of interfaces, see Type Checking.
Object o = new InterfaceA[10]; if (o instanceof InterfaceA[]) ...
Such interfaces are listed and linked with the field nextTypeChkInterface. This field is not shown in the following overview.
Arrays have java/lang/Object as superclass, therefore their type descriptor must allow for calling its inherited methods. Further there are two arrays containing lists with regular classes and interfaces, respectively. Each list contains classes with the same extension level. The field nextExtLevClass is used for linking. In the figure above the links are not all shown for simplicity.
When parsing the constant pool all fields are denoted with the tag Fieldref. Each Fieldref entry has two further indices into the constant pool. These denote the class and an entry NameAndType, which again has to further indices with the name and the type. For each field a new object is added to the object directory.
The class file reader adds all fields of a class in three linked lists:
The three lists are again linked among each other. The last entry of the instFields points to the first entry of classFields and the last entry of classFields points to the first entry of constFields. With this structure searching for a certain field object will be highly efficient. Additionally two references are kept, which point to the first instance or class field which is a reference. For this purposes all fields, which denote references, are entered at the end of the list.
The figure below shows an example for a class with two instance and two class fields as well as two constant fields. One of the instance fields is of a primitive type and one is a reference. Both class fields are of type reference.
For efficiency reasons all fields of an object or a class (defined as static) with the same size in memory should be placed together, which is done by sorting. For this purpose there are three static arrays in org.deepjava.classItems.Class. When parsing the fields their size and type is determined and entered according to their size into the appropriate array. Finally, the arrays are read out in proper order and the objects are entered in the lists as shown in the previous figure.
When parsing the constant pool all constants can be found in there. According to their tag different objects are created for them.
When parsing the fields a check is made whether the attribute ConstantValue is set. This is the case for fields which are defined as final. If so, another object ConstField will be created which contains the name of the field, its type and a reference to an object Constant.
Let's take a look at an example:
static final int a = 1;
For the class field a an object of type ConstField with the name “a” will be created. At the same time an object with the value of 1 of type StdConstant will be created. This object has the name “#”.
Important: The assignment of a value to a variable declared as final can be made when declaring it or in the constructor. This leads to the following cases.
class ClassA { static final int a; static final int b = 2000000; static final String str1 = "test1"; static final String[] str2 = {"abc", "def"}; static final Object obj = new Object(); final int c = 3000000; final int d; static final double e = 0.1; static final double[] f = {0.5, 0,6}; ClassA() {d = 4000000;} static {a = 2000000;} } interface ITest3 { int x = 100; String str3 = "test3"; String[] str4 = {"a1", "a2"}; }
For interfaces we get:
Important: When generating the SSA loading of a constant is translated with sCloadConst. If the constant is of type boolean, byte, short or int, which are not placed in the constant pool but in the code directly, a new object StdConstant must be created with the appropriate value and no name. In all other cases sCloadConst references the constant object built by the class file reader.
All reference types can have methods, even arrays as they inherit from java/lang/Object. The methods get sorted for building the type descriptors, see Type Descriptor.
Local variables within a method are dealt with in a separate section of the bytecode, the local variable table
. First of all it contains information about the slot the variable occupies together with the range where the variable is defined. Different locals might occupy the same slot, even with different types.