====== Interfaces ======
Interfaces generally don't have to be translated. All fields are constant and the methods are abstract. Interfaces don't need a type descriptor with a single exception as mentioned in [[.:type_descriptor|Type Descriptors]]. They also don't need a constant block and don't have to be included in the system table compared to regular classes (see [[dev:crosscompiler:backend_ppc:linker32|Linker32]]). However, here again, there is an exception to this rule. And special care has to be given to interface methods.
===== Class Constructors =====
Interfaces cannot have instances and have no constructors. They may have a class constructor as in:
Interface IA {
Object obj = new Object();
}
Another such case would be:
Interface IA {
String[] str1 = {"abc", "def"}; // array created and filled in clinit
String str2 = "hello"; // no clinit necessary
}
Such an interface must be inserted into the list //initClasses// (see [[.:cfr|Class File Reader]]) and must be added to the systemtable so that its class constructor can be called when classes are loaded (see [[dev:crosscompiler:backend_ppc:linker32|Linker32]]).\\
===== Methods =====
The Bytecode instruction to call interface methods is //invokeinterface//.
==== invokeinterface ====
When is this instruction issued? An example for this:
[{{ .:interfacemethodcall.png?400&direct | // Example for various interface methods //}}]
C obj = new C();
obj.m2(); // invokevirtual is issued
D obj = new D();
obj.m3(); // invokevirtual is issued
IA obj = new E();
obj.m2(); // invokeinterface is issued
((E)obj).m3(); // invokevirtual is issued
IB obj = new E();
((C)obj).m2(); // invokevirtual is issued
((E)obj).m3(); // invokevirtual is issued
IC obj = new E();
obj.m2(); // invokeinterface is issued
((E)obj).m3(); // invokevirtual is issued
IB obj = new E();
((IC)obj).m2(); // invokeinterface is issued
Conclusion: Solely when the static type is an interface type will the instruction //invokeinterface// be issued. As soon as a type cast to a regular class is made //invokevirtual// will be issued.
==== Interface Methods have no Fixed Index in the Method Table ====
The index into the method table cannot be fix. The following picture shows why.
[{{ .:interfacemethod.png?450&direct | // Two implementations of the same interface // }}]
Interface A is implemented by class C and D and with it the method //methodA1()//.
Let us consider the code:
InterfaceA obj1 = new ClassC();
obj1.methodA1();
InterfaceA obj2 = new ClassD();
obj2.methodA1();
How can the address of //methodA1()// be found for each of the two cases. The static type of //obj1// is //InterfaceA//. The compiler can calculate that //methodA1()// is the first method within this interface. The dynamic type of //obj1// is //ClassC//. With //obj2// this will be //ClassD//. In the type descriptors of //ClassC// and //ClassD// the //methodA1()// will not be at the same index. It's also of no use to put all the interface methods at indices starting with 0, because classes might implement a set of diverging interfaces.\\
Another problem can be seen in the following class diagramm:
[{{ .:interfacemethodproblem2.png?450&direct | // Methods with identical names in different interfaces // }}]
Here again the methods from //InterfaceA// and //InterfaceB// cannot be placed consecutively in the type descriptor.
The JVM resolves these references at runtime. In our system we must also ensure that according to the dynamic type the proper method can be found.
==== Searching an Interface Method in the Method Table ====
Out implementation differentiates between two cases, see below. With the instruction //invokeinterface// the type descriptor will be fetched. In there and with the aid of the correct offset a //delegate// method is fetched. \\
This //delegate// method must be placed at the same place in all associated type descriptors. Only at runtime the proper //delegate// method is retrieved and the proper interface method can be fetched.
=== Case 1 ===
This is most common case. A class implements 1 interface which has 1 method -> this method is inserted instead of a //delegate// method.
=== Case 2 ===
If a class implements one interface with several methods or several interfaces -> in the type descriptor we place the address of a //delegate// method at the index //interfaceDelegateMethod//. This method is a [[.:backend_ppc:code_generator#compiler_specific_subroutines|Compiler Specific Subroutine]]. Before calling the //delegate// method, the compiler puts the information about the specific interface the method belongs to together with the method index into a scratch register. For this purpose all interfaces are given a unique id. To give an example. The scratch register contains 0x00020003 -> the interface has id=2 and the method no. 3 within this interface is called.
The //delegate// method fetches the proper interface from the interface table (adjacent to the delegate method in the type descriptor). Each interface has an entry with 32 bit. The first 16 bit are reserved for the //id//, whereas the next 16 bit contain the offset into the method table. The offset is taken from the field //size//. With the first 16 bit given in the scratch register, the correct interface is searched. With the second half of the register you get the offset to the method table for this specific interface. Together with the method index from the scratch register the desired method can finally be found.\\
Important: For jumping into the interface method the LR cannot be used anymore, because the LR contains the address of the caller and the LR was not saved so far. This happens only at the start of the interface method. For practical implementation see notes in the code generator for the desired architecture, [[dev:crosscompiler:backend:start|Backend]].
==== Special Case ====
What happens if two independent interfaces each give a method with identical signature and class A implements both? A will have the address of the method once in its method table and will have the method listed in both interface blocks.