| Object layout for Java objects |
| ------------------------------ |
| |
| Each Java object will have the following basic layout: |
| |
| struct llvm_java_object_base { |
| struct llvm_java_object_header header; |
| struct llvm_java_object_vtable* vtable; |
| }; |
| |
| Additional fields go to the end of the struct. |
| |
| |
| |
| The 'llvm_java_object_header' is not defined yet. |
| |
| struct llvm_java_object_header { |
| // gc info, hash info, locking |
| }; |
| |
| |
| |
| The vtable holds a nested struct of a 'llvm_java_object_typeinfo' |
| object. All methods are added after the nested struct and calls to |
| them are made by fetching the correct function pointer from the vtable |
| at runtime. |
| |
| struct llvm_java_object_vtable { |
| struct java_object_typeinfo typeinfo; |
| }; |
| |
| |
| |
| For each class we keep track of its depth and an array of vtables to |
| all its superclasses. The depth of a class is the number of |
| superclasses it has. So java.lang.Object has depth 0, class A (extends |
| java.lang.Object) has depth 1 and so on. We also keep an array of |
| pointers to interface vtables. Each interface (vtable) gets a unique |
| number and it is indexed to this array. This mostly empty array is |
| filled up to the last interface implemented (the interface with the |
| largest index in this array). Since interfaces cannot implement other |
| interfaces (they can only extend) the lastIface and interfaces are |
| used to differentiate between class typeinfo's and interface |
| typeinfo's. More specifically if interfaces == -1 then this typeinfo |
| is for an interface and the lastIface field is the unique number of |
| this interface in the objects interfaces array. The field lastIface is |
| the max index of all implemented interfaces. For a class that doesn't |
| implement any it is -1. An additional field describes the elementSize |
| of an array. This allows us to implement System.arraycopy(). |
| |
| struct llvm_java_object_typeinfo { |
| jint depth; |
| struct llvm_java_object_vtable** vtables; |
| jint lastIface; |
| union { |
| struct llvm_java_object_vtable** interfaces; |
| jint interfaceFlag; |
| }; |
| jint elementSize; |
| }; |
| |
| The structure of llvm_java_object_typeinfo allows constant time |
| dynamic type checks: |
| |
| int isInstanceOf(struct llvm_java_object_base* obj, |
| struct llvm_java_object_vtable* clazz) { |
| struct llvm_java_object_vtable* objClazz = obj->vtable; |
| if (objClazz == clazz) |
| return 1; |
| // we are checking against a class' typeinfo |
| if (clazz->typeinfo.interfaceFlag != -1) |
| return objClazz->typeinfo.depth > clazz->typeinfo.depth && |
| objClazz->typeinfo.vtables[clazz->typeinfo.depth] == clazz; |
| // otherwise we are checking against an interface's typeinfo |
| else |
| return objClazz->typeinfo.lastIface >= clazz->typeinfo.lastIface && |
| objClazz->typeinfo.interfaces[clazz->typeinfo.lastIface]; |
| } |
| |
| Object Layout for Java Arrays |
| ----------------------------- |
| |
| Java primitive arrays inherit from java.lang.Object so they will at |
| minimum have a nested java.lang.Object struct as their first element. |
| |
| struct javaIntArray { |
| struct java_lang_Object; |
| int length; |
| int array[0]; |
| }; |
| |
| The typeinfo struct inside this class' vtable will be filled in the |
| same way as if this was a proper class. This will allow isInstanceOf |
| to work transparently for primitive java arrays. |
| |
| For reference type arrays we use the same struct for all types. The |
| only difference is the vtable (actually the vtables are identical but |
| they have different typeinfo structs to reflect the hierarchy of the |
| contained elements): |
| |
| struct javaObjectArray { |
| struct java_lang_Object; |
| int length; |
| java_lang_Object array[0]; |
| }; |
| |
| The typeinfo struct will reflect a hierarchy of array types as if B[] |
| derives from A[] if B derives from A. This will allow isInstanceOf to |
| work transparently for object arrays as well. |