Compilers
Informal semantics of new T Allocate locations to hold all attributes of an object of class T Essentially, allocate a new object Set attributes with their default values Evaluate the initializers and set the resulting attribute values Return the newly allocated object
For each class A there is a default value D A D int = Int(0) D bool = Bool(false) D string = String(0, ) D A = void (for any other class A)
For a class A we write class(a) = (a 1 : T 1 e 1,, a n : T n e n ) where a i are the attributes (including the inherited ones) T i are the attributes declared types e i are the initializers
T 0 = if (T == SELF_TYPE and so = X( )) then X else T class(t 0 ) = (a 1 : T 1 e 1,, a n : T n e n ) l i = newloc(s) for i = 1,,n v = T 0 (a 1 = l 1,,a n = l n ) S 1 = S[D T1 /l 1,,D Tn /l n ] E = [a 1 : l 1,, a n : l n ] v, E, S 1 d { a 1 e 1 ; ; a n e n ; } : v n, S 2 so, E, S d new T : v, S 2
The first three steps allocate the object The remaining steps initialize it By evaluating a sequence of assignments State in which the initializers are evaluated Self is the current object Only the attributes are in scope (same as in typing) Initial values of attributes are the defaults
Informal semantics of e 0.f(e 1,,e n ) Evaluate the arguments in order e 1,,e n Evaluate e 0 to the target object Let X be the dynamic type of the target object Fetch from X the definition of f (with n args.) Create n new locations and an environment that maps f s formal arguments to those locations Initialize the locations with the actual arguments Set self to the target object and evaluate f s body
For a class A and a method f of A (possibly inherited): impl(a, f) = (x 1,, x n, e body ) where x i are the names of the formal arguments e body is the body of the method
so, E, S d e 1 : v 1, S 1 so, E, S 1 d e 2 : v 2, S 2 so, E, S n-1 d e n : v n, S n so, E, S n d e 0 : v 0, S n+1 v 0 = X(a 1 = l 1,, a m = l m ) impl(x, f) = (x 1,, x n, e body ) l xi = newloc(s n+1 ) for i = 1,,n E = [a 1 : l 1,,a m : l m ][x 1 /l x1,, x n /l xn ] S n+2 = S n+1 [v 1 /l x1,,v n /l xn ] v 0, E, S n+2 d e body : v, S n+3 so, E, S d e 0.f(e 1,,e n ) : v, S n+3
What is the final value of S 5 in the dispatch of obj.foo(i) below? so, [i:l i ], S 1 d i : 3, S 2 so, [i:l i ], S 2 d obj : C(a = l obj_a ), S 3 impl(c, foo) = (x, x + a) l x = newloc(s 3 ) S 4 = S 3 [3/l x ] C(a = l obj_a ), [a:l obj_a ][x/l x ], S 4 d x + a : 4, S 5 so, [i:l i ], [l obj_a 1, l i 3] d obj.foo(i) : 4, S 5 Class C { a: Int <- 0; foo(x: Int) : Int { x + a }; }; [l i 3] [l obj_a 1, l i 3] [l obj_a 1, l i 3, l x 3] It cannot be determined from the information given.
The body of the method is invoked with E mapping formal arguments and self s attributes S like the caller s except with actual arguments bound to the locations allocated for formals The notion of the frame is implicit New locations are allocated for actual arguments The semantics of static dispatch is similar
Runtime Errors Operational rules do not cover all cases Consider the dispatch example: so, E, S n d e 0 : v 0,S n+1 v 0 = X(a 1 = l 1,, a m = l m ) impl(x, f) = (x 1,, x n, e body )
There are some runtime errors that the type checker does not prevent A dispatch on void Division by zero Substring out of range Heap overflow In such cases execution must abort gracefully With an error message, not with a segfault
Operational rules are very precise & detailed Nothing is left unspecified Read them carefully Most languages do not have a well specified operational semantics When portability is important an operational semantics becomes essential