Implementation of OO languages ============================== 1) How the program below would look like in C? public class Person { private int age; public Person(int age) { this.age = age; } public void happyBirthday() { age++; } public int getAge() { return age; } public static void main(String args[]) { Person p1 = new Person(27); Person p2 = new Person(25); p1.happyBirthday(); System.out.println(p1.getAge()); System.out.println(p2.getAge()); } } --//-- #include #include struct PersonStr { int age; }; typedef struct PersonStr* Person; Person newPerson(int age) { Person p = (struct PersonStr*)malloc(sizeof(struct PersonStr)); p->age = age; return p; } void happyBirthday(Person this) { this->age++; } int getAge(Person this) { return this->age; } int main() { Person p1 = newPerson(27); Person p2 = newPerson(25); happyBirthday(p1); printf("%d\n", getAge(p1)); printf("%d\n", getAge(p2)); } 2) What will be printed by the program below? class Vehicle { int position; void move(int x) { System.out.println("Vehicle::move"); position = position + x; } } class Car extends Vehicle { int passengers; void await(Vehicle v) { System.out.println("Car::await"); if (v.position < position) v.move(position - v.position); else this.move(10); } } class Truck extends Vehicle { void move(int x) { System.out.println("Truck::move"); if (x <= 55) { position = position + x; } } } public class Driver { public static void main(String args[]) { Truck t = new Truck(); Car c = new Car(); Vehicle v = c; c.passengers = 2; c.move(60); v.move(70); c.await(t); } } 3) How does the implementation of a method finds the variables in the scope of the class where this method belongs? 4) Consider the method Vehicle::move. How would the implementation of this method look like in C? void move(Vehicle::descriptor* this, int x) { System.out.println("Vehicle::move"); this->position = this->position + this->x; } 5) How does the running environment finds an attribute of an object? E.g: v.position? 6) How does the running environment finds a method of an object? 7) Draw the layout of data for each object in Driver::main. Vehicle: Car: Truck: Class desc ----------------------------------------------- position position position passengers Virtual tab ---------------------------------------------- Vehicle::move Vehicle::move Truck::move Car::await 8) Draw the virtual tables and class descriptors for the following classes: class A { int x = 0; int f() {...} } class B extends A { int g() {...} } class C extends B { int g() {...} } class D extends C { int y = 0; int f() {...} } A: B: C: D: CD: ----------------------------------------------- x x x x y VT: ---------------------------------------------- A::f A::f A::f D::f B::g C::g C::g 9) What will be printed by the program below? class A { public static String f = "a"; } class B extends A { public static String f = "b"; } class C extends B { public static String f = "c"; } class D extends A { public static String f = "d"; } public class Stat { public static void main(String args[]) { System.out.println(A.f); System.out.println(B.f); System.out.println(C.f); System.out.println(D.f); A a = new A(); A b = new B(); A c = new C(); A d = new D(); System.out.println(a.f); System.out.println(b.f); System.out.println(c.f); System.out.println(d.f); } } 10) How is the access to static fields implemented? 10.1) When is the address of static fields known? 11) What is the sequence of instructions that must be performed to find a field f inside an object o in a language with simple inheritance? - Find the pointer to o's descriptor. - read the value at offset(f); 12) How would the virtual tables of the objects below look like? #include class A { public: virtual int a() { return 1; }; }; class B { public: virtual int b() { return 2; }; virtual int c() { return 3; }; }; class C : public A { public: virtual int d() { return 4; }; }; class D : public B, public C { public: virtual int e() { return 5; }; }; int main() { D od; printf("%d\n", od.a()); printf("%d\n", od.b()); printf("%d\n", od.c()); printf("%d\n", od.d()); printf("%d\n", od.e()); return 0; } - What about: A B C D a a a b b c c d d e 13) What would be a good algorithm to layout the virtual tables of the objects in face of multiple inheritance? 14) You see that there is a bit of a waste of space. Finding the most economical layout of data is hard of easy? * The graph coloring approach: - For each class property p (field or method), create a node np - For each other property p' that might appear with p in a hierarchy of classes, add an edge from p to p' - Color the graph. Each color is a slot in the vtable. 15) Would you know how to reduce the optimal layout problem to a NP-complete problem? - Crate a class N with a method n() for each node n in the graph - while there are nodes in the graph: - Choose a node n in the graph: - Make every class M of a node m that interferes with n to extend N - Consider the graph a-b-c-a-d #include class A { public: virtual int a() { return 1; }; }; class B : public A { public: virtual int b() { return 2; }; }; class C : public A, public B { public: virtual int c() { return 3; }; }; class D : public A { public: virtual int d() { return 4; }; }; int main() { A o1; B o2; C o3; D o4; printf("%d\n", o1.a()); printf("%d\n", o2.b()); printf("%d\n", o3.c()); printf("%d\n", o3.a()); // Will not compile due to a stupid C++ limitation. printf("%d\n", o4.d()); printf("%d\n", o4.a()); return 0; } 16) What is a problem with this approach in languages that allow dynamic class loading? 17) A problem with the graph coloring approach is that we can have a lot of empty slots int he vtables. How to handle these empty slots? - We can make an index of slots in each class descriptor, e.g: Object: a b a a c d b c d e Class descriptor: A B C D a 1 1 1 b 1 2 c 2 3 d 2 4 e 5 18) Using the index system, we are treading time for space. How many steps are now necessary to find a method? - Fetch the descriptor pointer from the object - Fetch the field-offset value from the descriptor. - Fetch the method address at the appropriate offset in the object. *) Another way to deal with multiple inheritance is to resort to hashtables. In this case, we are treading even more time for space. In order to fetch a field x of object c, the compiler generates the following code: - Fetch the class descriptor d at the offset 0 from object c. - Fetch the field name f from the address offset d + Ktab + hash(x). - Test whether f = x. If so: - Fetch the field offset k from d + Ftab + hash(x). - Fetch the contents of the field from c + k. 19) How is this test performed? if (a instanceof D) { ... } t1 = a.descriptor L1: if t1 = A goto True t1 = t1.super if t1 = null goto False goto L1 20) What is the complexity of the test above? 21) Is it possible to do it faster? * Display: when the maximum inheritance level is known at compile time: - Store the descriptor of the static type T at depth j - Store the descriptr of T.super at depth j - 1 - Store the descriptr of T.super.super at depth j - 2 - etc 22) Given these displays, how is the instanceof test performed? Let S be the descriptor of the static type. Let D be the descriptor of the query type. - Fetch the descriptor d of the object, at o + 0 - Fetch x, the j-th class-pointer at d + DISPLAY + j - Compare x with D. 23) How are coercions implemented? E.g: D d = (D)a; 24) What can we optimize in: if (a instanceof D) { D d = (D)a; } 25) Some languages, like Modula-3 or Scala, have a typecase-like constructor: TYPECASE expr OF C1(v1) => s1 | C2(v2) => s2 ... | Cn(vn) => sn ELSE s0 END How are these typecase commands implemented? 26) What are the protection levels used in Java? public protected _________ private app x . . . sub x x . . pack x x x . class x x x . 27) How is encapsulation implemented in the object oriented system? 28) Some languages provide objects, but do not provide classes. Which languages? 29) How are objects created in such languages? 30) How are objects implemented in such languages? 31) What are the advantages and disadvantages of this model of object orientation? 32) What are important optimizations that a compiler can perform in an object oriented language? 33) What is the advantage of replacing a dynamic method invocation by a C-style jump and link call? - Pre-fetching - Inline expansion 34) How to discover the target of a call, e.g: o.m()? - Type hierarchy analysis - Data-flow analysis.