Author: allison Date: Thu Sep 13 11:05:11 2007 New Revision: 21263 Modified: trunk/docs/pdds/draft/pdd17_pmc.pod
Log: [pdd] Reordering major section in PDD 17, some cleanups in reference material. Modified: trunk/docs/pdds/draft/pdd17_pmc.pod ============================================================================== --- trunk/docs/pdds/draft/pdd17_pmc.pod (original) +++ trunk/docs/pdds/draft/pdd17_pmc.pod Thu Sep 13 11:05:11 2007 @@ -238,6 +238,106 @@ more) types of PMC, while particular Python datatypes will map onto different types of PMC. +=head2 Interaction between PMCs and high-level objects + +{{ Address the problem of high-level objects inheriting from low-level PMCs, +and any structural changes to low-level PMCs that might require. }} + +High-level objects, as specified in PDD15, need to be able to inherit from +PMCs. This is achieved through the combination of a C<PMCProxy> PMC and +delegation. + +For PDD15 objects, there is a corresponding instance of the C<Class> PMC. For +a PMC, however, the class definition is written in C and compiled away. There +needs to be something that can be placed in the parents list for a PDD15 +class. That something is the C<PMCProxy> PMC. Like a PDD15 class, it is +stored as the C<class> element in the namespace associated with a PMC, provides +introspection facilities and can sit in an inheritance hierarchy. + +The PMCProxy PMCs are only created when needed for subclassing a low-level +PMC, to avoid a large load of unused PMCProxy objects. When created, they are +cached in the class slot of the namespace corresponding to the low-level PMC, +so they are only created once. + +Therefore, subclassing a PMC looks, at a PIR level, like subclassing a high +level class. + + $P0 = get_class 'Hash' + $P1 = newclass 'MyClass' + addparent $P1, $P0 # The new class inherits from the Hash PMC + +Or, more briefly: + + $P1 = subclass 'Hash', 'MyClass' + +PMCs store state in a very different way to PDD15 objects. When a method +inherited from a PMC is called on a PDD15 object, that method needs to get +a chunk of state that it can use as it wishes. Further to this, if multiple +PMCs are inherited from, they may use the state in different ways. Users of +Parrot at a PIR level should not have to care about such issues. + +(AR: I'm willing to make some changes in how low-level PMCs store state in +order to make this easier.) + +Therefore, when a PDD15 object is instantiated and it inherits from PMCs, an +instance of each of the PMCs that it inherits from is also created. These are +stored at the end of the object's attributes store. + +When a method is called on an object whose class has PMC parents, the call is +delegated up to each of the PMC parents, in MRO order. The invocant must be +the appropriate PMC instance from the object's attribute store. For v-table +calls, this is just a case of passing the correct thing. For other methods +that are located by C<find_method>, a C<Bound_NCI> PMC is used to ensure that +the method from the PMC is invoked with the correct instance, so it has access +to the correct state. + +(AR: Why isn't it called directly on the delegated PMC stored in the delegating +object's data store?) + +When a PMC calls a method on itself, it may have been overridden in a high +level class. Therefore dynamic dispatches of method calls need to be done with +the vtable of the high level class, not the vtable of the PMC itself. + +For PMCs that are instantiated and not overridden, the C<real_self> pointer in +the PMC structure is a reference to the PMC instance itself. If, however, it +is just acting to provide the state a PMC needs but is not the real instance, +this pointer will point to the instance of the high level class (an instance +of the C<Object> PMC). Method dispatches using C<DYNSELF> will always go +through this mechanism. + +(AR: See comment above about delegated calls. Better to have the delegator +object only exist for delegated PMCs, and have DYNSELF check for it.) + +Attribute access is, like method access, delegated upwards. Since attribute +lookup is a vtable method, the down case is covered by the previous paragraph. + +(AR: What about overriding a low-level attribute with a high-level attribute? +Can you override the core data store of a Hash? The core struct members of an +STMLog? This would be a lot easier if low-level PMCs had a standard way of +defining their attributes (core struct members), which could be used to +generate struct declarations, to automatically fill a PMC's C<inspect>, +C<get_attr>, and C<set_attr> vtable functions, to annotate PMCProxy objects, +and to allow state for compile-time roles to be generated in the same struct as +state from the PMC itself.) + +}} + +If a low-level PMC expects to be overridden by high-level classes (which means +all the core low-level PMC types), it must respect a standard interface. It +must implement the C<get_attr> and C<set_attr> vtable entries for any core +attributes it expects to be accessible to subclasses. + +Subclassing a low-level class from a high-level class makes an entry in the +high-level class's list of parents. This entry is a proxy class object for the +low-level PMC class that provides access to the low-level PMC's vtable +(including the implementations of get_attr and set_attr), and defines the +storage that the low-level object will need within the high-level object (this +could be as simple as a single integer attribute that gives an index into the +object's data store where an instance of the low-level class is stored). + + +=head1 REFERENCE + =head2 Vtable Functions Vtables decouple the interface and implementation of various object functions. @@ -255,12 +355,16 @@ =over 4 -=item void init(INTERP, PMC* self) +=item init + + void init(INTERP, PMC* self) Called when a PMC is first instantiated. It takes an unused PMC parameter and turns it into a PMC of the appropriate class. -=item void init_pmc(INTERP, PMC* self, PMC* initializer) +=item init_pmc + + void init_pmc(INTERP, PMC* self, PMC* initializer) Alternative entry point called when a PMC is first instantiated. Accepts a PMC parameter used to initialize the given object. Interpretation of the PMC @@ -270,7 +374,9 @@ NOTE: It is strongly suggested that init_pmc(PMCNULL) be equivalent to init(), though there will of necessity be exceptions. -=item void morph(INTERP, PMC* self, INTVAL type) +=item morph + + void morph(INTERP, PMC* self, INTVAL type) Turn the PMC into a PMC of type I<type>. If the morphing can't be done in any reasonable way -- for instance if an integer is asked to turn into an Array -- @@ -281,7 +387,9 @@ to a particular type, and isn't meant as a general purpose casting tool. Compilers should only emit valid morphing operations. -=item void mark(INTERP, PMC* self) +=item mark + + void mark(INTERP, PMC* self) Called when the DOD is tracing live PMCs. If this method is called then the code must mark all strings and PMCs that it contains as live, otherwise they @@ -301,7 +409,9 @@ internal structures. It should have no side-effects from the C level either. This routine may not throw an exception. -=item void destroy(INTERP, PMC* self) +=item destroy + + void destroy(INTERP, PMC* self) Called when the PMC is destroyed. This method is called by the DOD when it determines that a PMC is dead and that the PMC has marked itself as having a @@ -313,44 +423,63 @@ This method may not throw an exception. It will be ignored if it does. -=item PMC* clone(INTERP, PMC* self) +=item clone + + PMC* clone(INTERP, PMC* self) Return a clone of a PMC. =item defined + INTVAL defined(INTERP, PMC* self) + +Return a true value if the PMC is defined, false otherwise. + =back =head3 Accessors =over 4 -=item PMC* getprop(INTERP, PMC* self, STRING* key) +=item getprop + + PMC* getprop(INTERP, PMC* self, STRING* key) Return the value from the property hash of I<self> keyed by I<key>. The key should not be NULL. -=item void setprop(INTERP, PMC* self, STRING* key, PMC* value) +=item setprop + + void setprop(INTERP, PMC* self, STRING* key, PMC* value) Set the value in the property hash of I<self> that is keyed by I<key> to the value of I<value>. The key should not be NULL. -=item void delprop(INTERP, PMC* self, STRING* key) +=item delprop + + void delprop(INTERP, PMC* self, STRING* key) Delete the value from the property hash of I<self> keyed by I<key>. The key should not be NULL. -=item PMC* getprops(INTERP, PMC* self) +=item getprops + + PMC* getprops(INTERP, PMC* self) Return the entire property hash for I<self>. -=item INTVAL type(INTERP, PMC* self) +=item type + + INTVAL type(INTERP, PMC* self) Return the type of the PMC. Type is a unique number associated with the PMC when the PMC's class is loaded. Negative numbers are considered -interpreter-specific, non-public types. +interpreter-specific, non-public types. [NOTE: will be deprecated when type IDs +are deprecated.] + +=item subtype -=item UINTVAL subtype(INTERP, PMC* self, INTVAL type) [deprecated] + UINTVAL subtype(INTERP, PMC* self, INTVAL type) [deprecated] Return the subtype of a PMC. (Note that this may be unimplemented, and may go away). This is intended to return information about the PMC--what type of @@ -359,7 +488,9 @@ [This can be adequately handled by C<does> and C<inspect>.] -=item STRING* name(INTERP, PMC* self) +=item name + + STRING* name(INTERP, PMC* self) Return the name of the class for the PMC. @@ -747,20 +878,29 @@ Set the pointer value of the element indexed by a string key. -=item INTVAL type_keyed(INTERP, PMC* self, PMC* key) +=item type_keyed + + INTVAL type_keyed(INTERP, PMC* self, PMC* key) Return the type number of the PMC indexed by a PMC key. The I<key> parameter -is guaranteed not to be NULL for this method. +is guaranteed not to be NULL for this method. [NOTE: To be deprecated when type +IDs are deprecated.] + +=item type_keyed_int -=item INTVAL type_keyed_int(INTERP, PMC* self, INTVAL key) + INTVAL type_keyed_int(INTERP, PMC* self, INTVAL key) Return the type number of the PMC indexed by an integer key. The I<key> -parameter is guaranteed not to be NULL for this method. +parameter is guaranteed not to be NULL for this method. [NOTE: To be deprecated +when type IDs are deprecated.] + +=item type_keyed_str -=item INTVAL type_keyed_str(INTERP, PMC* self, STRING* key) + INTVAL type_keyed_str(INTERP, PMC* self, STRING* key) Return the type number of the PMC indexed by a string key. The I<key> -parameter is guaranteed not to be NULL for this method. +parameter is guaranteed not to be NULL for this method. [NOTE: To be deprecated +when type IDs are deprecated.] =item pop_integer @@ -862,7 +1002,7 @@ =item splice -=item void splice(INTERP, PMC* self, PMC* value, INTVAL offset, INTVAL count) + void splice(INTERP, PMC* self, PMC* value, INTVAL offset, INTVAL count) Replace the I<count> PMCs at offset I<offset> from the beginning of I<self> with the PMCs in the aggregate I<value>. @@ -1104,101 +1244,6 @@ =back -=head2 Interaction between PMCs and high-level objects - -{{ Address the problem of high-level objects inheriting from low-level PMCs, -and any structural changes to low-level PMCs that might require. }} - -{{ PROPOSED: - -High-level objects, as specified in PDD15, need to be able to inherit from -PMCs. This is achieved through the combination of a C<PMCProxy> PMC and -delegation. - -For PDD15 objects, there is a corresponding instance of the C<Class> PMC. For -a PMC, however, the class definition is written in C and compiled away. There -needs to be something that can be placed in the parents list for a PDD15 -class. That something is the C<PMCProxy> PMC. Like a PDD15 class, it is -attached to the namespace that a PMC is attached to, provides introspection -facilities and can sit in an inheritance hierarchy. - -AR: The PMCProxy PMCs are only created when needed for subclassing a low-level -PMC, to avoid a large load of unused PMCProxy objects. When created, they are -cached in the class slot of the namespace corresponding to the low-level PMC, -so they are only created once. - -Therefore, subclassing a PMC looks, at a PIR level, like subclassing a high -level class. - - $P0 = get_class 'Hash' - $P1 = new 'Class' - addparent $P1, $P0 # The new class inherits from the Hash PMC - -PMCs store state in a very different way to PDD15 objects. When a method -inherited from a PMC is called on a PDD15 object, that method needs to get -a chunk of state that it can use as it wishes. Further to this, if multiple -PMCs are inherited from, they may use the state in different ways. Users of -Parrot at a PIR level should not have to care about such issues. - -(AR: I'm willing to make some changes in how low-level PMCs store state in -order to make this easier.) - -Therefore, when a PDD15 object is instantiated and it inherits from PMCs, an -instance of each of the PMCs that it inherits from is also created. These are -stored at the end of the object's attributes store. - -When a method is called on an object whose class has PMC parents, the call is -delegated up to each of the PMC parents, in MRO order. The invocant must be -the appropriate PMC instance from the object's attribute store. For v-table -calls, this is just a case of passing the correct thing. For other methods -that are located by C<find_method>, a C<Bound_NCI> PMC is used to ensure that -the method from the PMC is invoked with the correct instance, so it has access -to the correct state. - -(AR: Why isn't it called directly on the delegated PMC stored in the delegating -object's data store?) - -When a PMC calls a method on itself, it may have been overridden in a high -level class. Therefore dynamic dispatches of method calls need to be done with -the vtable of the high level class, not the vtable of the PMC itself. - -For PMCs that are instantiated and not overridden, the C<real_self> pointer in -the PMC structure is a reference to the PMC instance itself. If, however, it -is just acting to provide the state a PMC needs but is not the real instance, -this pointer will point to the instance of the high level class (an instance -of the C<Object> PMC). Method dispatches using C<DYNSELF> will always go -through this mechanism. - -(AR: See comment above about delegated calls. Better to have the delegator -object only exist for delegated PMCs, and have DYNSELF check for it.) - -Attribute access is, like method access, delegated upwards. Since attribute -lookup is a vtable method, the down case is covered by the previous paragraph. - -(AR: What about overriding a low-level attribute with a high-level attribute? -Can you override the core data store of a Hash? The core struct members of an -STMLog? This would be a lot easier if low-level PMCs had a standard way of -defining their attributes (core struct members), which could be used to -generate struct declarations, to automatically fill a PMC's C<inspect>, -C<get_attr>, and C<set_attr> vtable functions, to annotate PMCProxy objects, -and to allow state for compile-time roles to be generated in the same struct as -state from the PMC itself.) - -}} - -If a low-level PMC expects to be overridden by high-level classes (which means -all the core low-level PMC types), it must respect a standard interface. It -must implement the C<get_attr> and C<set_attr> vtable entries for any core -attributes it expects to be accessible to subclasses. - -Subclassing a low-level class from a high-level class makes an entry in the -high-level class's list of parents. This entry is a proxy class object for the -low-level PMC class that provides access to the low-level PMC's vtable -(including the implementations of get_attr and set_attr), and defines the -storage that the low-level object will need within the high-level object (this -could be as simple as a single integer attribute that gives an index into the -object's data store where an instance of the low-level class is stored). - =head2 Core PMCs