2.5 Program Environments and Abstract Machine States
2.5.2 The Environment
The environment of a program fragment species not only which objects exist, but also the access paths by which they may be reached. Changes in the accessibility (or visibility)
2.5 Program Environments and Abstract Machine States 27 of objects are generally associated with procedure call and return, and for this reason the procedure call hierarchy forms a part of the environment. We shall now consider questions of lifetime and visibility; the related topic of procedure parameter transmission will be deferred to Section 2.5.3.
That part of the execution history of a program during which an object exists is called the extent of the object. The extent rules of most programming languages classify objects as follows:
Static: The extent of the object is the entire execution history of the program.
Automatic: The extent is the execution of a specied syntactic construct (usually a procedure or block).
Unrestricted: The extent begins at a programmer-specied point and ends (at least theoretically) at the end of the program's execution.
Controlled: The programmer species both the beginning and end of the extent by explicit construction and destruction of objects.
Objects in COBOL and the blank common block of FORTRAN are examples of static extent. Local variables in ALGOL 60 or Pascal, as well as local variables in FORTRAN sub- programs, are examples of automatic extent. (Labeled common blocks in FORTRAN 66 also have automatic extent, see Section 10.2.5 of the standard.) List elements in LISP and objects created by the heap generator of ALGOL 68 have unrestricted extent, and the anonymous variables of Pascal are controlled (created by
new
and discarded bydispose
).The possibility of a dangling reference arises whenever a reference can be created to an object of restricted extent. To avoid errors, we must guarantee that the referenced object exists at the times when references to it are actually attempted. A sucient condition to make this guarantee is the ALGOL 68 rule (also used in LAX) prohibiting assignment of references or procedures in which the extent of the right-hand side is smaller than the reference to which it is assigned. It has the advantage that it can be checked by the compiler in many cases, and a dynamic run-time check can always be made in the absence of objects with controlled extent. When a language provides objects with controlled extent, as do PL/1 and Pascal, then the burden of avoiding dangling references falls exclusively upon the programmer.
LAX constant are the only object having static extent. Variables are generally automatic, although it is possible to generate unrestricted variables. The language has no objects with controlled extent, because such objects do not result in any new problems for the compiler. Static variables were omitted because the techniques used to deal with automatic variables apply to them essentially without change.
By the scope of an identier denition we understand the region of the program within which we can use the identier with the dened meaning. The scope of an identier denition is generally determined statically by the syntactic construct of the program in which it is directly contained. A range is a syntactic construct that may have identier denitions associated with it. In a block-structured language, inner ranges are not part of outer ranges. Usually any range may contain at most one denition of an identier. Exceptions to this rule may occur when a single identier may be used for distinct purposes, for example as an object and as the target of a jump. In ALGOL-like languages the scope of a denition includes the range in which it occurs and all enclosed ranges not containing denitions of the same identier.
Consider the eld selection p.f. The position immediately following the dot belongs
to the scope of the declaration of p's record type. In fact, only the eld selectors of that
record type are permitted in this position. On the other hand, although the statement
s
type declaration, the denitions from the inspection's environment remain valid in
s
unless overridden by eld selector denitions. In COBOL and PL/1, f can be written in place of p.f (partial qualication) if there is no other denition of f in the surrounding range.The concept of static block structure has the consequence that items not declared in a procedure are taken from the static surrounding of the procedure. A second possibility is that used in APL and LISP: Nonlocal items of functions are taken from the dynamic environment of the procedure call.
In the case of recursive procedure calls, identically-declared objects with nested extents may exist at the same time. Diculties may arise if an object is introduced (say, by parameter transmission) into a program fragment where its original declaration is hidden by another declaration of the same identier. Figure 2.3 illustrates the problem. This program makes two nested calls ofp, so that two incarnations,q1andq2, of the procedureq and two variables
i1 and i2 exist at the same time. The program should print the values 1, 4 and 1 of i2,i1 andk. This behavior can be explained by using the contour model.
procedure
outer ;var
n , k : integer ;procedure
p (procedure
f ;var
j : integer );label
1;var
i : integer ;procedure
q ;label
2;begin
(* q *) n := n + 1;if
n = 4then
q ; n := n + 1;if
n = 7then
2 : j := j + 1; i := i + 1;end
; (* q *)begin
(* p *) i := 0; n := n + 1;if
n = 2then
p (q , i )else
j := j + 1;if
n = 3then
1 : f ; i := i + 1; writeln (' i = ', i :1);end
; (* p *)procedure
empty ;begin end
;begin
(* outer *)n := 1; k := 0; p (empty , k );
writeln (' k = ', k :1);
end
; (* outer *)Figure 2.3: Complex Procedure Interactions in Pascal
The contour model captures the state of the program execution as a combination of the (invariant) program text and the structured set of objects (state) existing at respective points in time. Further, two pointers, ip and ep belong to the state. ip is the instruction
pointer, which indicates the position in the program text. For block-structured languages the state consists of a collection of nested local environments called contours. Each contour corresponds to a range and contains the objects dened in that range. If the environment pointerep addresses a contourc, then all of the objects declared inc and enclosing contours
2.5 Program Environments and Abstract Machine States 29 are accessible. The contour addressed byep is called the local contour. The object identied
by a given identier is found by scanning the contours from inner to outer, beginning at the local contour, until a denition for the specied identier is found.
The structure of the state is changed by the following actions: Construction or removal of an object.
Procedure call or range entry. Procedure return or range exit. Jump out of a range.
When an object with automatic extent is created, it lies in a contour corresponding to the program construct in which it was declared; static objects behave exactly like objects declared in the main program with automatic extent. Objects with unrestricted extent and controlled objects lie in their own contours, which do not correspond to program constructs. Upon entry into a range, a new contour is established within the local contour and the environment pointerep is set to point to it. Upon range exit this procedure is reversed: the
local contour is removed andep set to point to the immediately surrounding contour.
Upon procedure call, a new contourc is established andep set to point to it. In contrast
to range entry, however,c is established within the contourc' addressed byep at the time of
procedure declaration. We termc' the static predecessor of c to distinguish it from c", the
dynamic predecessor, to whichep pointed immediately before the procedure call. The pointer
toc" must be stored inc as a local object. Upon return from a procedure the local contour
of the procedure is discarded and the environment pointer reset to its dynamic predecessor. To execute a jump into an enclosing range b, blocks and procedures are exited and the
corresponding contours discarded until a contourc corresponding to b is reached such that c contained the contour of the jump. c becomes the new local contour, to which ep will
point, andip is set to the jump target. If the jump target is determined dynamically as a
parameter or the content of a label variable, as is possible in ALGOL 60, then that parameter or variable must specify both the target address and the contour that will become the new local contour.
Figures 2.4 and 2.5 show the contour model for the state existing at two points during the execution of the program of Figure 2.3. Notice that several contours correspond to the same range when a procedure is called recursively. Further, the values of actual parameters of a procedure call should be computed before the environment pointer is altered. If this is not done, the pointer for parameter computation must be restored (as is necessary for name parameters in ALGOL 60).
In order to unify the state manipulation, procedures and blocks are often processed iden- tically. A block is then a parameterless procedure called `on the spot'. The contour of a block thus has a dynamic predecessor identical with its static predecessor. The lifetimes of local objects in blocks can be determined by the compiler, and a static overlay structure for them can be set up within the contour of the enclosing procedure. The main program is counted as a procedure for this purpose. The scope rules are not altered by this transformation. Con- tours for blocks can be dispensed with, and all objects placed in the contour of the enclosing procedure. Arrays with dynamic bounds lead to diculties with this optimization, since the bounds can be determined only at the time of actual block entry.
The rules discussed so far do not permit description of either LISP or SIMULA. In LISP a functionf may have as its result a functiong that accesses the local storage of f. Since this
storage must also exist during the call of g, the contour of f must be retained at least until g becomes inaccessible. Analogously, a SIMULA class k (an object of unrestricted extent)
may have name parameters from the contour in which it was instantiated. This contour must therefore be retained at least untilk becomes inaccessible.
n: 3 k: 0
p
Contour for procedure outer
empty Contour for procedure p
f = j = k
q1
empty
Contour for procedure p f = q1
j = i1 i2: 0
q2 ep
i1: 1
Note: Arrows show dynamic predecessor
Figure 2.4: Contours Existing When Control Reaches Label 1 in Figure 2.3
k: 0
p
Contour for procedure outer
empty Contour for procedure p
f = j = k
q1
empty
Contour for procedure p f = q1 j = i1 i2: 0 q2 n: 7 i1: 2 q
Contour for procedure
ep
2.5 Program Environments and Abstract Machine States 31 We solve these problems by adopting a uniform retention strategy that discards an object only when that object becomes inaccessible. Accessibility is dened relative to the current contour. Whenever an object in a contourc references another object in a dierent contour, c', we implement that reference by an explicit pointer fromc to c'. (Such references include
the dynamic predecessors of the contour, all reference parameters, and any explicit pointers established by the user.) A contour is accessible if it can be reached from the current contour by following any sequence of pointers or by a downhill walk. The dangling reference problem vanishes when this retention strategy is used.