Object-Oriented Languages

Object-Oriented Languages

Object-oriented languages Object-oriented In a class-based object-oriented (OO) language, all values are objects, belonging to a class. Prototype-based OO languages do not have a concept of languages class, but we will not cover them here. Objects encapsulate both state, stored in fields, and Advanced Compiler Construction behavior, defined by methods. Michel Schinz — 2016–05–19 Two of the most important features of OO languages are inheritance and polymorphism. (parts based on Yoav Zibin's PhD thesis) 1 2 Inheritance Subtyping and polymorphism In typed OO languages, classes usually define types. These types are related to each other through a subtyping (or conformance) relation. Inheritance is a code reuse mechanism that enables one Intuitively, a type T1 is a subtype of a type T2 — written class to inherit all fields and methods of another class, called T1 ⊑ T2 — if T1 has at least the capabilities of T2. its superclass. When T1 ⊑ T2, a value of type T1 can be used everywhere a Inheritance is nothing but code copying, although it is value of type T2 is expected. usually implemented in a smarter way to prevent code This ability to use a value of a subtype of T where a value of explosion. type T is expected is called inclusion polymorphism. Inclusion polymorphism poses several interesting implementation challenges, by preventing the exact type of a value to be known at compilation time. 3 4 Subtyping ≠ inheritance "Duck typing" Inheritance and subtyping are not the same thing, but many OO languages tie them together by stating that: The distinction between inheritance and subtyping is 1. every class defines a type, and especially apparent in “dynamically typed” OO languages 2. the type of a class is a subtype of the type(s) of its like Smalltalk, Ruby, etc. superclass(es). In those languages, inheritance is used only to reuse code — This is a design choice, not an obligation! no notion of (static) type even exists! Several languages also have a way to separate inheritance Whether an object can be used in a given context only and subtyping in some cases. For example, Java interfaces depends on the set of methods it implements, and not on make it possible to define subtypes that are not subclasses. the position of its class in the inheritance hierarchy. C++ has private inheritance that allows the definition of subclasses that are not subtypes. 5 6 Polymorphism challenges The following problems are difficult to solve efficiently because of inclusion polymorphism: 1. object layout — arranging object fields in memory, 2. method dispatch — finding which concrete OO problem #1: implementation of a method to call, 3. membership tests — testing whether an object is an Object layout instance of some type. We will now examine each one in turn. 7 8 Object layout Object layout example class A { int x; The object layout problem consists in finding how to } arrange the fields of an object in memory so that they can class B extends A { be accessed efficiently. int y; Inclusion polymorphism makes the problem hard because it } forces the layout of different object types to be compatible void m(A a) { System.out.println(a.x); } in some way. at which Ideally, a field defined in a type T should appear at the same position in a does x offset in all subtypes of T. appear? 9 10 Single inheritance In single-inheritance languages where subtyping and inheritance are tied (e.g. Java), the object layout problem Case 1: can be solved easily as follows: The fields of a class are laid out sequentially, starting with single inheritance those of the superclass — if any. This ensures that all fields belonging to a type T1 appear at the same location in all values of type T2 ⊑ T1. 11 12 Example layout for A class A { offset field int x; } 0 x layout for B Case 2: class B extends A { offset field int y; 0 x } 1 y multiple inheritance void m(A a) { System.out.println(a.x); } access position 0 of a 13 14 Multiple inheritance Unidirectional layout In a multiple inheritance setting, the object layout problem If a standard, unidirectional layout is used, then some space becomes much more difficult. is wasted! Example: For example, in the following hierarchy, how should fields be laid out? layout for A layout for B offset field offset field wasted 0 x 0 – A B layout for C int x int y 1 y offset field 0 x C 1 y int z 2 z 15 16 Bidirectional layout Bidirectional layouts For this particular hierarchy, it is however possible to use a bidirectional layout to avoid wasting space. layout for A layout for B There does not always exist a bidirectional layout that wastes no space. offset field offset field Moreover, finding an optimal bidirectional layout — one 0 x –1 y layout for C minimizing the wasted space — has been shown to be NP- offset field complete. –1 y Finally, computing a good bidirectional layout requires the 0 x whole hierarchy to be known! It must be done at link time, and is not really compatible with Java-style run time linking. 1 z 17 18 Accessor methods Other techniques Another way of solving the object layout problem in a multiple inheritance setting is to always use accessor Bidirectional layout often wastes space, but field access is methods to read and write fields. extremely fast. Accessor methods never waste space, but The fields of a class can then be laid out freely. Whenever slow down field access. the offset of a field is not the same as in the superclass from Two-dimensional bidirectional layout slows down field which it is inherited, the corresponding accessor method(s) access slightly — compared to bidirectional layout — but are redefined. never wastes space. However, it also requires the full This reduces the object layout problem to the method hierarchy to be known. dispatch problem, which we will examine later. 19 20 Object layout summary Exercise In C++ implementations, the position of a field is not the same in all subtypes of the type that introduced it. For our example, instances of C would typically be laid out as follows The object layout problem can be solved trivially in a single- (gray fields contain information for method dispatch): inheritance setting, by laying out the fields sequentially, A B starting with those of the superclass. offset field int x int y In a multiple-inheritance setting, solutions to that problem 0 are more complicated, and must generally trade space for 1 x C speed, or speed for space. They also typically require the 2 whole hierarchy to be known in advance. int z 3 y 4 z With such a layout, what should happen when a method inherited from B is invoked on an instance of C? 21 22 Method dispatch The method dispatch problem consists in finding, given an object and a method identity, the exact piece of code to OO problem #2: execute. Inclusion polymorphism makes the problem hard since it prevents the problem to be solved statically — i.e. at method dispatch compilation time. Efficient dynamic dispatching methods therefore have to be devised. 23 24 Method dispatch example class A { int x; void m() { println("m in A"); } void n() { println("n in A"); } } class B extends A { int y; Case 1: void m() { println("m in B"); } void o() { println("o in B"); } } single subtyping void f(A a) { a.m(); } which implementation of m should be invoked? 25 26 Single subtyping Virtual methods table Hierarchy Memory organization A a1 0 VMT 0 A.m code for In single-inheritance languages where subtyping and int x 1 x 1 A.n A.m inheritance are tied, the method dispatch problem can be void m() solved easily as follows: void n() code for a2 0 VMT A.n Method pointers are stored sequentially, starting with 1 x those of the superclass, in a virtual methods table (VMT) code for B shared by all instances of the class. b 0 VMT 0 B.m B.m int y This ensures that the implementation for a given method is 1 x 1 A.n void m() code for always at the same position in the VMT, and can be void o() 2 y 2 B.o B.o extracted quickly. Program A a1 = new A(); A a2 = new A(); B b = new B(); 27 28 Dispatching with VMTs VMTs pros and cons Using a VMT, dispatching is accomplished in three steps: VMTs provide very efficient dispatching, and do not use 1. the VMT of the selector is extracted, much memory. They work even in languages like Java where 2. the code pointer for the invoked method is extracted new classes can be added to the bottom of the hierarchy at from the VMT, run time. 3. the method implementation is invoked. Unfortunately, they do not work for dynamic languages or in Each of these steps typically requires a single — but the presence of any kind of “multiple subtyping” — e.g. expensive — instruction on modern CPUs. multiple interface inheritance in Java. 29 30 Java interfaces To understand why VMTs cannot be used with multiple subtyping, consider Java interfaces: interface Drawable { void draw(); } void draw(List<Drawable> ds) { Case 2: for (Drawable d: ds) d.draw(); multiple subtyping } When the draw method is invoked, the only thing known about d is that it has a draw method — but nothing is known about its class, which be anywhere in the hierarchy! 31 32 Java interfaces Dispatching matrix A trivial way to solve the problem is to use a global Object Drawable dispatching matrix, containing code pointers and indexed hashCode draw by classes and methods.

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    20 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us