Objects and Classes David Walker CS 320 Advanced Languages • advanced programming features – ML data types, exceptions, modules, objects, concurrency, ... – fun to use, but require special techniques to compile and optimize – today will be looking at how to compile objects and classes similar to those found in Java • Appel chapter 14.1-14.4 Object Tiger • Add class declarations to Tiger: – classes are a collection of • field (variable) declarations • method declarations – every class declaration gives a new name to a class – a class may inherit methods and fields from the class it extends – the class “object” sits at the top of the class hierarchy; it has no fields and no methods An Object Tiger Class class name field declaration superclass that Vehicle inherits from let class Vehicle extends Object { var position := start method move (x : int) = (position := position + x) } in ... end method declaration Another Object Tiger Class new field declaration let class Vehicle extends Object { var position := start method move (x:int) = (position := position + x) } class Car extends Vehicle { var passengers := 0 new method declaration method await(v:Vehicle) = if (v.position < position) then v.move(position – v.position) else self.move(10) call to inherited v’s “position” field } method current object’s “position” field in ... end Yet Another Object Tiger Class let class Vehicle extends Object { var position := start method move (x:int) = position := position + x } class Car extends Vehicle { ... } class Truck extends Vehicle { method move (x:int) = if x <= 55 then position := position * x in ... end method override Using the Classes let class Vehicle extends Object { ... } class Car extends Vehicle { ... } class Truck extends Vehicle {...} a car calls an inherited method var t := new Truck var c := new Car var v : Vehicle := c in c.passengers := 2; c.move(60); v.move(70); c.await(t); end new object created subtyping allows a car to be viewed and used as a generic vehicle subtyping allows a truck to be viewed and used as a generic vehicle Implementing Object Tiger • Some key problems: – how do we access object fields? • both inherited fields and fields for the current object? – how do we access method code? • if the current class does not define a particular method, where do we go to get the inherited method code? • how do we handle method override? Class Hierarchy • The class hierarchy is the graph of inheritence relationships in a program: Object Vehicle Car Truck – In a single-inheritence (SI) language, the graph is a tree – In a multiple-inheritence (MI) language, the graph is a dag – Multiple-inheritence languages are much trickier to implement than single-inheritence languages Object Layout (SI) • Objects are laid out somewhat like records – each variable has a slot in the record – in order to implement field lookup we need to have a systematic way to find a given field • eg: v.position • v may be a generic vehicle or it may be a car or a truck • we need to put “position” in the same place in the record that implements vehicles, cars and trucks Object Layout (SI) • Solution: extension on the right – lay out the inherited fields first in the same order as in the parent (SI => only 1 parent) – lay out the newly declared to the right Object Layout (SI) class A extends Object { var a := 0 } class B extends A { var b := 0 var c := 0 } class C extends A { var d := 0 } class D extends B { var e := 0 } Object Layout (SI) class A extends Object { var a := 0 } class B extends A { var b := 0 var c := 0 } class C extends A { var d := 0 } class D extends B { var e := 0 } A a Object Layout (SI) class A extends Object { var a := 0 } class B extends A { var b := 0 var c := 0 } class C extends A { var d := 0 } class D extends B { var e := 0 } A B a a b c Object Layout (SI) class A extends Object { var a := 0 } class B extends A { var b := 0 var c := 0 } class C extends A { var d := 0 } class D extends B { var e := 0 } A B C a a a b d c Object Layout (SI) class A extends Object { var a := 0 } class B extends A { var b := 0 var c := 0 } class C extends A { var d := 0 } class D extends B { var e := 0 } A B C D a a a a b d b c c e Static & Dynamic Methods • The result of compiling a method is some machine code located at a particular address – at a method invocation point, we need to figure out what code location to jump to • Java has static & dynamic methods – to resolve static method calls, we look at the static type of the calling object – to resolve dynamic method calls, we need the dynamic type of the calling object Static Methods let class A extends Object { static method foo (x:int) = ... static method bar (x:int) = ... } class B extends A { static method foo (x:int) = ... } var a : A = new A var b : A = new B var c : B = new B in a.foo(3); (* calls foo in class A *) b.foo(3); (* calls foo in class A *) c.bar(3); (* calls bar in class A *) c.foo(3); (* calls foo in class B *) – during semantic analysis, the compiler knows: • static type (class) of the object calling the method • the list of methods in each class – and determines • the closest method (up the class hierarchy) with the given name – and inserts • instructions to pass object as self parameter • a direct call to the known method Dynamic Methods let class A extends Object { method foo (x:int) = ... method bar (x:int) = ... } class B extends A { method foo (x:int) = ... } var a : A = new A var b : A = new B var c : A = if long-and-tricky-computation then a else b in c.foo(3) – Method called depends on object’s dynamic type • During semantic analysis, may be unknown – At run-time, we determine which code to jump to • object stores a pointer to its method table (v-table) as well as its object vars – At compile-time, we generate code to • look up v-table in object • extract method from table • jump to method body Object Layout II (SI) class A extends Object { var a := 0; method f () } class B extends A { method g () } class C extends B { method g () } class D extends C { var b := 0 ; method f () } Object Layout II (SI) class A extends Object { var a := 0; method f () } class B extends A { method g () } class C extends B { method g () } class D extends C { var b := 0 ; method f () } A a A_f Object Layout II (SI) class A extends Object { var a := 0; method f () } class B extends A { method g () } class C extends B { method g () } class D extends C { var b := 0 ; method f () } A_f A B a a A_f B_g Object Layout II (SI) class A extends Object { var a := 0; method f () } class B extends A { method g () } class C extends B { method g () } class D extends C { var b := 0 ; method f () } A_f A B C a a a A_f A_f B_g C_g Object Layout II (SI) class A extends Object { var a := 0; method f () } class B extends A { method g () } class C extends B { method g () } class D extends C { var b := 0 ; method f () } A B C D a a a a b A_f A_f A_f D_f B_g C_g C_g Object Layout II (SI) class A extends Object { var a := 0; method f () } class B extends A { method g () } class C extends B { method g () } class D extends C { var b := 0 ; method f () } A B C D a a a a b A_f D A_f A_f D_f a B_g C_g C_g b Multiple Inheritence • Multiple inheritence is trickier to implement than single inheritence because creating objects of a subclass from their subclass by “extension on the right” doesn’t work – if C inherits from both A and B, we can’t put A’s variables at the front and put B’s variables at the front of the object in the same place! – we need to do a global analysis to determine object layout Object Layout (MI) class A extends Object { var a := 0 } class B extends Object { var b := 0 var c := 0 } class C extends A { var d := 0 } A B C a b a c d Object Layout (MI) class A extends Object { var a := 0 } class B extends Object { var b := 0 var c := 0 } class C extends A { var d := 0 } class D extends A,B,C { var e := 0 } A B C D a b a a b c d c d e Object Layout (MI) class A extends Object { var a := 0 } class B extends Object { var b := 0 var c := 0 } class C extends A { var d := 0 } class D extends A,B,C { var e := 0 } A B a C D a a b b c c d d e Object Layout (MI) class A extends Object { var a := 0 } class B extends Object { var b := 0 var c := 0 } class C extends A { var d := 0 } class D extends A,B,C { var e := 0 } A B a C a D a b b c c d d e Determine object layout by: • global graph coloring! • a node for each field name • an interference edge between names that coexist in the same class (due to inheritence) Object Layout (MI) class A extends Object { var a := 0 } class B extends Object { var b := 0 var c := 0 } class C extends A { var d := 0 } class D extends A,B,C { var e := 0 } A B a C D a a b b c c wasted space in every object d d e Object Layout II (MI) class A extends Object { var a := 0 } class B extends Object { var b := 0 var c := 0 } class C extends A { var d := 0 } class D extends A,B,C { var e := 0 } A B C D a b a a a: 1 d b b: 2 c c: 3 d d: 4 e e: 5 a: 1 c a: 1 a: b: 1 2 d: 2 wasted space per class Object Layout II (MI) class A extends Object { var a := 0 } class B extends Object { var b := 0 var c := 0 } class C extends A { var d := 0 } class D extends A,B,C { var e := 0 } A B a b c a: 1 a: b: To fetch a field using this representation, we: • load the first field of the object to get the class descriptor c • load M [c + fieldOffset] 1 2 Note: fieldOffset can be precomputed using global graph coloring as before A Problem • Global analyses are the bane of modern computing – many applications use dynamically linked libraries, mobile code, get patches to fix bugs, etc. – when we don’t have the whole program at compiletime, we can’t do global analyses! – solution (most of the time): • a smart custom linker • still tricky when new code is linked dynamically to code that is already running – hence, Java has single inheritence Other OO Features • Down-casts and type tests – Java has casting mechanism “(C) x” to cast variable x to class C • at run time we look up x’s dynamic type and determine whether it is a subtype of C • these type casts are currently pervasive and a source of both inefficiency and errors • Java and C# (?) now have parametric polymorphism, a la ML, to make many of these unnecessary casts go away – chalk another one up to programming language research!! Other OO Features • Protection mechanisms – to encapsulate local state within an object, Java has “private” “protected” and “public” qualifiers • private methods/fields can’t be called/used outside of the class in which they are defined – during semantic analysis (type checking), the compiler maintains this information in the symbol table for each class Summary • Object-oriented languages provide new challenges for compiler writers – how to find fields and methods – how to make field and method access just as efficient as ordinary function call and variable lookup – lots of ongoing research in OO language implementation tackles these and other interesting questions Compiler Overview Front End Optimization - lexing (2) - parsing (3,4) - type checking (5) Middle End - activation records, stacks (6) - intermediate reps (7,8) - compiling objects (14) Back End - instruction selection (9) - register allocation (11) - garbage collection (13) - dataflow analysis (17) - loop analysis (18) - SSA (19)