The goal is to find the address and static type for the value designated by x.f. This could be a built-in value, a method or object reference. The compiler knows the static type of x, while f is assumed here to be a constant. The following treatment can be easily generalized to handle the cases where f is an expression.
The class of x can be found based upon the static type of x. For instance, x may be a dynamically typed data item in which case the data tag implies the class. Alternatively, it could be a reference to a vtable for an object or for a view of an object defined by an interface.
There is more than one way to approach this problem. This offers the compiler the opportunity to select between them according to the relative costs in any context. The costs involve compile-time costs and run-time costs and further break down into time and space costs.
Not all cases apply in all contexts. If x.f is bound, the value it is bound to shouldn't vary according to which case is used. This involves a small overhead for the specific classes and properties where this ambiguity might otherwise arise.
The slot address and type can be computed trivially from x and the class definition.
Generate code to get the class of x and see if it is the class that contains f. If not generate an exception. A generalization of this handles the case where f only occurs in one class or its subclasses.
Generate code to get the class of x and compare it with the class of this. If not fall back to more general cases. Note that this case applies whether f is a public, package protected, protected or private property of this class.
Each class is required to define a generic method for mapping property names to slots. A pointer to this method is placed at a fixed offset in the vtable for the class. The compiler generates code to get the class of x and to call the lookup method via this offset. The method provides further checks for inappropriate access to private or protected or package protected fields. But see case 6 for an alternative approach for package protected properties.
This is a generalization of case 1. The compiler generates a method for each such f that maps x to the desired slot based upon the class of x as determined at run-time.
The call to get the class of x behaves in the same way as for normal objects. This makes it practical to use the same lookup mechanism as in case 3. The difference is that the interface's lookup method needs to defererence the property to the slot used in the class the interface applies to. In this case x references a vtable for the view onto the object specified by the interface.
If a class has package private properties, then the compiler generates an interface definition for the view of that class from other packages. This reduces the problem to case 5. If the class doesn't have package private properties it is treated in the same way as a class in the current package. Note that this allows classes to export multiple views of themselves. A package version selects a set of such views.
This is treated by a generalization of case 2. An exception is generated if f is a private property of the superclass. This says either that x doesn't have property f or that an attempt has been made to access a private field of a superclass, according to the compiler settings. If the class of x is known at compile-time the error can be detected during compilation.
This can be handled by interpreting x as if it were class C. In other words by finding the offset for property f in class C and applying this offset to x. If x is a subclass of C and the subclass and C differ in the way f is defined (e.g. f is private to C) this approach can yield different results to applying case 3 (dynamic lookup). The difference can be avoided by an additional check if needed depending on f. A similar problem occurs if f is defined by the subclass but not by C. When this happens case 8 fails and case 3 can be tried.