chapters method

Local Identifiers and the Problem of Inadvertent Capture

PJ is a tool that is designed to compose different specifications (i.e., hierarchies of classes, interfaces, etc.) by macro expansion. A standard problem of macro-expansion is inadvertent capture.  It arises when variables in different classes are given the same names, and ambiguity arises when the classes are composed.  Consider the following class hierarchy:

class top {
   int i;
   ... System.out.println(i); ...
}

class bottom extends top {
   double i;
   ... System.out.println(i); ...
}

The above is a legal Java program; the reason is that variable scoping differentiates the variable named "i" in class bottom from the variable named "i" in class top.  Now, look what happens when top and bottom are blindly merged into a single class:

class top {
   int i;
   double i;  // illegal
   ... System.out.println(i); ... // ambiguous
   ... System.out.println(i); ... 
}

That is, there are two variables named "i" and now references to variable "i" are ambiguous -- it is not obvious which variable is being referenced.  This is typical of inadvertent capture.  We will encounter such problems in PJ because of the way inheritance hierarchies are collapsed.  The ambiguous (and incorrect) code above will be produced by PJ if class top were composed with the following extension:

extends class top {
   double i;
   ... System.out.println(i); ...
}

The LocalID Declaration

A way to avoid inadvertent capture is to declare identifiers "local" to a particular class, and have them "mangled" so that they are forever unique.  In GenBorg, inadvertent capture is a bit more difficult because identifiers are local to a layer, and a layer encapsulates many classes (or class extensions).  Each class can reference variables and methods that are local to other classes of that layer. So assigning a mangled name to an identifier must be done consistently across all classes of a layer.  

The solution used in PJ is to introduce a LocalId declaration which simply lists the identifiers that are local to a layer and that are to be mangled.  In effect, the translation of a LocalId declaration is a textual search and replace performed on the body of the PJ declaration (class, state machine, etc.) thereby yielding the correct specification.  As an example, a way to declare class top above is:

LocalId i;   // list of names that are local to a layer, and are to be mangled

class top {
   int i;
   ... System.out.println(i); ...
}

When top is instantiated, the name "i" is mangled -- i.e., changed into a unique identifier.  Assume the name mangling changes "i" to "i$1", an instantiation of top yields:

class top {
   int i$1;
   ... System.out.println(i$1); ...
}

The actual mangling used is <variableName>$$<layerName>.

Examples

Consider the following base class specification with local identifiers i, j, ii, jj, and foo.

package Ctop; 

import Jakarta.util.*;

LocalId i, j;
LocalId ii, jj;
LocalId foo;

class top {
   static int i,j;
   int ii,jj;

   static { i = 4; }

   top() {  ii = 5; }
   top(int rj) { jj = rj; }

   void foo(float x, float y) { i = j = x+y; }
}

Now consider an extension to top that uses exactly the same identifiers locally:

package Cmid;

import java.util.*;

LocalId i, j, ii, jj, foo;

extends class top implements java.io.Serializable {

   static int i,j;
   int ii,jj;

   static { i = 4; }

   void foo(float x, float y) { i = j = x*y; }
}

PJ composes these two specifications to yield:

package Ctop; 

import Jakarta.util.*;

import java.util.*;

class top implements java.io.Serializable {
   static int i$$Ctop,j$$Ctop;

   static int i$$Cmid,j$$Cmid;

   static { i$$Ctop = 4; }
   static { i$$Cmid = 4; }

   int ii$$Ctop,jj$$Ctop;
   int ii$$Cmid,jj$$Cmid;

   top() {  ii$$Ctop = 5; }
   top(int rj) { jj$$Ctop = rj; }

   void foo$$Ctop(float x, float y) { i$$Ctop = j$$Ctop = x+y; }

   void foo$$Cmid(float x, float y) { i$$Cmid = j$$Cmid = x*y; }
}

Note that PJ first assigns unique (mangled) names to local identifiers before composing specifications.  It doesn't matter if PJ is composing classes, interfaces, state machines or whatever.  The LocalId feature works for them all. 

Finally, note that the LocalId declaration(s) list all of the identifiers that are local to a layer.  Not all identifiers listed need be present in the body of a base or extension specification.