Yes.
If you are going to build from svn head, it may be best to track changes
being made to svn head (i.e. get on the commits mailing list so you get
mail with each commit.. the commit typically contains comments that
describe what this commit is about).
In this case, we have added support for detecting statically leakage of
this from constructors. (In Java it is possible to call an instance
method on this from a constructor, which can then be used to read values
of fields which have in fact not yet been initialized.)
The proto design fixes this. Am enclosing below the current wiki page
describing the proto design. This will make its way to the external wiki
eventually.
Here is what you need to do. Since getUserParameters is being called
from within a constructor, it needs to be declared as a proto method:
public proto def getUserParameters(var param_file_name:String ) ...
Now in the body of this method, the type of this is "proto C" (if the
class on which this method is being defined is C). Values of type proto
C can only be communicated through type proto D, where C is a subtype of
D. Such values can only be used to assign fields of the class C, they
cannot be used to read the fields of C. Further such a value can be
written into a field of type D (where C is a subtype of D), provided
that the receiver is itself of a proto type.
If declaring getUserParameters as a proto method causes a problem (e.g.
because it reads the values of some fields on this), then consider
defining a make method, and calling it, instead of calling the
constructor. Here is how the make method would be defined:
public def make(fileName: String) {
val r = new State(fileName);
r.getUserParameters(fileName);
return r;
}
Now in your code wherever you were calling new State(fileName), now
call make(fileName), and you should be all set.
Best,
Vijay
Design of |proto| qualifier on types (Version 1.6.0.2)
Java and X10 v1.7 permit |this| to escape from a constructor before an
object is fully initialized. This means that fields can be read before
they are written into. This is a problem for the X10 language design
since fields may have constrained types, and the default value |null|
may not be a legal value for the type.
Design goals
* The design should guarantee that fields are read only after they
are initialized.
* It should be possible to create immutable cyclic object graphs.
o These typically require a constructor to be invoked with an
object under construction as an argument. Mutable cyclic
graphs are not a problem because the backpointer can always
be initialized to |null| and then changed later.
* It should be possible to call user-defined methods during object
creation (so that the transformation between the values supplied
as parameters to a constructor and the values actually placed in
fields is arbitrary).
* The design should be minimally invasive. Most programmers should
not have to be concerned about this problem.
* The design should not entail any runtime overhead.
Note the paper by Myers et al in POPL 2009 on masked types. It addresses
a more (perhaps needlessly) complicated situation with a complicated
solution that permits the programmer to track which fields of an object
have been initialized, via explicit typestates.
The |proto| type qualifier
For every type |T| (where |T| is not a type variable), we introduce the
type |proto T|. This is the set of all values |v| of type |T| that are
still being formed in that the constructor for the value has not yet
exited or the value points through its fields (transitively) to an
object that is still being formed. We say that |v| is /incomplete/.
Below, a " |proto| type" is a type |proto T| for some |T|, and a "
|proto| value" is a value |v| whose static type is a |proto| type.
|proto| types are permitted to occur only as types of method parameters
or local variables or as return types for methods and constructors; they
may not occur in cast statements, extends or implements clauses, or
catch clauses.
Within a constructor for class |C| the type of |this| is |proto C|.
We also permit instance methods of a class |C| to be qualified with
|proto|; the type of |this| in such methods is |proto C|.
Below we say that an expression has a |proto| type if its type is |proto
T| for some type |T|.
We list the rules for using a value |v| of type |proto T| (for some |T|).
1. |proto| is covariant, i.e. |S <: T| implies |proto S <: proto T|.
However, there is no connection between the types |proto T| and
|T|; neither is a subtype of the other.
2. The fields of |v| cannot be read (that is |v.f| is not a
permissible r-value, even if the type of |v| has a field named |f|).
* This is the defining property of |proto| types.
3. |v| 's (accessible) instance fields can be assigned.
4. |v| can be assigned to instance fields of type |S| (for |T <: S|)
provided that the receiver of the field assignment has a |proto|
type.
5. If |v| is the receiver of a method invocation, the method must be
an instance method marked |proto|.
6. |v| can be passed as argument into a constructor or method call,
or returned from a method.
* The return type of a method taking an argument at a |proto|
type must be |void| or a |proto| type. The return type of a
constructor taking an argument at a |proto| type must be a
|proto| type.
7. A generic class (method) type parameter |T| can be instantiated
with the type |proto S| (where |S| is not a type parameter
itself), provided that the class (method) body satisfies the
conditions above for |proto S|.
These conditions are checked statically by the compiler.
During code generation, the type |proto T| is treated as if it were |T|.
That is, there is no run-time cost to |proto| types.
Examples
*Immutable |CircularList| .* This requires a constructor with an
argument at a |proto| type.
class CircularList[T] {
val datum:T;
val tail: CircularList[T];
def this(d:T): CircularList[T] {
this.datum = d;
this.tail = this;
}
def this(ds:List[T]{length >= 1}):CircularList[T] {
this.datum = ds(0);
this.tail = ds.length == 1 ? this : new CircularList[T](ds.tail(),
this);
}
private def this(ds:List[T]{length >= 1}, t:proto CircularList[T]):proto
CircularList[T] {
this.datum = ds(0);
this.tail = ds.length == 1 ? t : new CircularList[T](ds.tail(), t);
}
}
|proto| types can be transmitted to remote places as usual. In the class
above we can replace the second constructor with:
private def this(ds:List[T]{length >= 1}, t:proto CircularList[T]):proto
CircularList[T] {
this.datum = ds(0);
val dTail = ds.tail();
this.tail = ds.length == 1 ? t : at (dTail) new CircularList[T](dTail,
t);
}
Discussion
The need for instance methods that can be called on |this| during the
execution of a constructor can be illustrated by an example provided by
Dave Cunningham. In an object-oriented framework, a field at a
superclass may need to be initialized based on code defined at a subclass.
* Add scene example discussion.
The above rules ensure that an incomplete object can only be assigned to
fields which are instance fields of an incomplete object. Note that the
design requires that the type of instance fields is not |proto| -- but
no type-unsoundness results since the only way to access these fields is
through a value of a |proto| type, and hence these fields cannot be
read. Values of |proto| types can exist only on the stack in stack
frames nested below a constructor invocation.
The rules ensure that if the return type of a method or constructor is
|proto| then it must have an argument whose type is |proto|. This is key
to establishing the /subformula/ property:
* If an expression has a |proto| type then it has a subterm of
|proto| type.
The subformula property establishes that |proto| values cannot be
assigned to a static field. In X10, static fields must be initialized.
However, per language rules, no local variables or method parameters can
be in scope in the static initializer, hence no variable of |proto| type
is available to the initializer.
The key to the design is (a) the return type of a constructor may be
non- |proto| (even though the type of |this| in the body of the
constructor is |proto|), (b) the only |proto| value created out of thin
air is |this| inside a constructor, (c) it is not the case that |T <:
proto T|, i.e. fully formed values are not considered incomplete. The
last condition ensures that an incomplete object cannot be leaked by
assigning it to a field of a completed object.
The return from a constructor encapsulates the magic time and place in
the entire system at which a |proto| value becomes a "normal" value
(whose fields can now be accessed, and whose instance methods may now be
invoked). Further, by ensuring that the return type of a method or
constructor is |proto| if it takes a |proto| argument |o|, we ensure
that even locally completed objects (i.e. objects whose constructor
invocation has returned) are treated as incomplete as long as the
constructor for |o| is still on the stack. The only thing that can be
done with |proto| objects is that they can be discarded (become garbage)
or they can be assigned to fields of a |proto| object whose constructor
invocation is on the call stack. Such fields cannot be accessed until
that constructor invocation returns.
More precisely, the following invariant is maintained by the above
rules: An object on the heap is a |proto| object if and only if it is
not referenced other than:
1. through variables of a |proto| type in local or enclosing stack
frames up to the stack frame that represents the original object
construction.
2. through fields of |proto| objects
Update from converting XRX to work with |proto|
Most of the translation was straightforward. The only annoyance is that
there were many places where fields are initialized in a constructor and
then the field is used later (e.g. in initializing other fields). The
current approach requires that new local variables be introduced to
track the values assigned to fields -- since the field cannot be read in
the constructor, only written.
This can become cumbersome. The following idiom is often more practical.
Hide the constructors (mark them |private|) and define them to just
initialize the fields with various objects. The inter-relationship
between these objects might not be set up correctly. So then provide
static |make| methods that first create the object and then invoke an
instance method on the object to finish the initialization. Now this
method is a "normal" method (i.e. not a |proto| method) since the
constructor has exited. Hence |this| can be used to access the fields.
The main drawback with this method is that the types of the fields need
to be weak enough that the initial assignments satisfy them. In practice
this has not been a problem so far.
See e.g. the code for |RectLayout| and |PolyScanner|.
In many cases it was simpler to write |make| methods, and hide the
constructors (e.g. mark them |private|). The constructor is used merely
to initialize the fields
TODO
* Add rules from the link below governing transmission of |proto|
values across |async| boundaries.
* Figure out rules that ensure that properties are all assigned
before |this| is leaked. This means that the type of |this| in the
body of a constructor of return type |T| after the invocation of
|properties(...)| is |proto T|. Hence |this| can escape into
method invocations that are expecting some constraints to be
satisfied by the corresponding argument. The subsequent assignment
to this |proto| value's |val| and |var| fields cannot change the
/type/ of the object, since the type depends only on properties.
o Candidate rule: The first statement in the body of a
constructor must be a call to a |this| constructor (in which
case there must be no other statement in the body of the
constructor), or it must be a |super(...)| call (as is
currently the case), and the second statement must be a
|properties(...)| call (if the class has properties), and
the third statement |S| may be anything at all (including a
compound statement). The runtime treats the body of the
second kind of constructor as if it were defined as
|super(...); if (init_properties) properties(...) else S|
where |init_properties| is an implicit runtime variable
treated as follows. The execution of each |new| statement
proceeds by identifying the appropriate constructor and then
executing it /twice/, once with |init_properties| set to
|true| and then set to |false|. Thus in the first phase the
inheritance tree is walked up from the leaf to the root,
with the |properties(...)| calls in each constructor
executed in order from the root to the leaf. At the end of
this phase the type of the object being constructed is
established. Now in the second phase the inheritance tree is
walked up from the leaf to the root and the statement |S| in
each constructor is executed in order from the root to the
leaf. During this phase, |this| may escape in code running
at any level of the hierarchy.
FAQ
History
An earlier version of this proposal
<http://orquesta.watson.ibm.com/mediawiki/index.php/Ante-objects> was
written up a couple of years ago.
Updated with comments from Dave Cunningham.
Comments
Please add comments here, sign and date them.
-- VijaySaraswat
<http://xj.watson.ibm.com/twiki/bin/view/Main/VijaySaraswat> - 21 Aug 2009
------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now. http://p.sf.net/sfu/bobj-july
_______________________________________________
X10-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/x10-users