Improving inspect on class objects

Patrick R. Michaud pmichaud at pobox.com
Sun Jan 4 16:28:46 UTC 2009


I need some design/architect input from Allison on the behavior
of the C<inspect> opcode when used on class objects.  I'll start
off with the short version of the question, and then the long
explanation:

Short version:  Can we change attribute and method inspection for
class objects to return the _class->attrib_metadata and 
_class->methods hashes directly, instead of cloning the hashes 
(as it does now)?


Long version:

Each class object (i.e., instances of the Class PMC) carries 
"attrib_metadata" and "methods" hashes that keep track of the 
attributes and methods defined in the class.  The C<inspect> opcode 
can be used to introspect a class for its attributes and methods,
but currently this introspection returns a clone of the
attrib_metadata and methods hashes instead of the originals.
I suspect the cloning is intended to prevent introspection from being
used to modify the hashes held in the class object, but the cloning
here has some unfortunate side-effects.

(Note that cloning a Hash PMC in Parrot currently has "deep cloning" 
behavior -- the values in a cloned hash are clones of (not references to)
the values in the original.)

One side effect is that the attrib_metadata hash ends up being fairly
limited in its usefulness to store attribute metadata.  In Rakudo's case 
I thought I would be able to use the attrib_metadata hash to store 
additional metadata about attributes -- e.g., initialization types 
and default values.  However, the cloning behavior of inspect means 
that it's not possible to modify an existing attribute's metadata.  
About the best that can be done is to try to obtain and construct
all metadata for an attribute prior to adding it to the class, as
once it's in the class that metadata cannot be (easily) changed.

Beyond this, the current implementation of the attrib_metadata hash
only provides two entries:  the 'name' slot holds the name of the 
attribute, and the 'type' slot holds whatever additional PMC argument 
was passed to add_attribute.  It seems like attrib_metadata would be
more generally useful if more entries beyond just 'name' and 'type'
could be made available.

Here's an example of what I was effectively trying to do in Rakudo
(greatly simplified to make the intent clearer):

    # Perl 6:
    #    has $!x = 5;

    # add a new attribute to classobj
    .local pmc classobj
    classobj.'add_attribute'('$!x')

    # get the metadata hash for attribute $!x
    .local pmc attrmeta
    $P0 = inspect classobj, 'attributes'
    attrmeta = $P0['$!x']

    # set the implementation type and default value for $!x
    attrmeta['itype'] = 'Scalar'
    attrmeta['default'] = 5

Because the hash $P0 that comes back from C<inspect> is a clone of
the actual attrib_metadata hash, changes to that clone have no
effective impact on the one in the class object.  Also, the design
of the Perl 6 grammar means that it's difficult to reorder
things like 'default' to be available before the attribute is added.
(It can be done, it's just much more complex than doing it afterwards.)

Another side effect of the current behavior is that inspecting
the methods of a class ends up returning clones of the methods'
Sub PMCs instead of the originals:

    $ cat z.pir
    .sub 'main'
        .local pmc classobj
        classobj = newclass ['ABC']
    
        .local pmc method_found, method_ns, method_inspect
    
        ##  method obtained via find_method
        $P1 = new ['ABC']
        method_found = find_method $P1, 'foo'
    
        ##  method obtained from namespace
        method_ns = get_global ['ABC'], 'foo'
    
        ##  method obtained via inspect
        $P1 = inspect classobj, 'methods'
        method_inspect = $P1['foo']
    
        print "find_method =:= namespace --> "
        eq_addr method_found, method_ns, same_1
        print "not "
      same_1:
        say "same"
    
        print "find_method =:= inspect   --> "
        eq_addr method_found, method_inspect, same_2
        print "not "
      same_2:
        say "same"
    .end
    
    .namespace ['ABC']
    .sub 'foo' :method
        say 'ABC.foo'
    .end
    $ ./parrot z.pir
    find_method =:= namespace --> same
    find_method =:= inspect   --> not same
    $


Lastly, there's also the issue that the cloning makes inspection
of classes' attributes and methods into a much more expensive 
operation, and helps to explain why P6object, PCT, and Rakudo
were generating far more hashes than I expected.

Here's where I need a Parrot architect decision:

I don't know how important it is that the original hashes in
the class object be protected from modification.  If it's not
important, or we can just place suitable warnings against certain
types of modification, then the simplest answer is to have
inspect return those hashes directly, and I can make this change
easily.

If we really need to protect the hashes in the class object,
but can allow modification of the things they address, then
the next-best-answer is to perform a shallow-copy clone of the
hashes instead of a deep-copy clone.  This would permit the
attribute metadata modifications I was trying to do earlier
and avoiding the unintended cloning of methods.  I can also
make this change easily.

If we need something more radical than either of these, then we
need the architect to specify what it should be.

I have a pressing need for a solution to this -- as things stand
now it looks as though Rakudo and PCT will need to maintain
their own copies of this information because we can't easily
make use of Parrot's introspection.

Thanks,

Pm


More information about the parrot-dev mailing list