9.12 Classes and Objects

     

Parrot's object system is a new addition in version 0.1.0. Objects still have some rough edges (for example, you currently can't add new attributes to a class after it has been instantiated ), but they're functional for basic use.

This section revolves around one complete example that defines a class, instantiates objects, and uses them. The whole example is included at the end of the section.

9.12.1 Class Declaration

The newclass opcode defines a new class. It takes two arguments, the name of the class and the destination register for the class PMC. All classes (and objects) inherit from the ParrotClass PMC, which is the core of the Parrot object system.

 newclass P1, "Foo" 

To instantiate a new object of a particular class, you first look up the integer value for the class type with the find_type opcode, then create an object of that type with the new opcode:

 find_type I1, "Foo" new P3, I1 

The new opcode also checks to see if the class defines a method named "_ _init" and calls it if it exists.

9.12.2 Attributes

The addattribute opcode creates a slot in the class for an attribute (sometimes known as an instance variable ) and associates it with a name:

 addattribute P1, ".i"                # Foo.i 

This chunk of code from the _ _init method looks up the position of the first attribute, creates a PerlInt PMC, and stores it as the first attribute:

 classoffset I0, P2, "Foo"     # first "Foo" attribute of object P2 new P6, .PerlInt              # create storage for the attribute setattribute P2, I0, P6       # store the first attribute 

The classoffset opcodetakes a PMC containing an object and the name of its class, and returns an integer index for the position of the first attribute. The setattribute opcode uses the integer index to store a PMC value in one of the object's attribute slots. This example initializes the first attribute. The second attribute would be at I0 + 1 , the third attribute at I0 + 2 , etc.:

 inc I0 setattribute P2, I0, P7       # store next attribute  . . . 

There is also support for named parameters with fully qualified parameter names (although this is a little bit slower than getting the class offset once and accessing several attributes by index):

 new P6, .PerlInt setattribute P2, "Foo\x0.i", P6   # store the attribute 

You use the same integer index to retrieve the value of an attribute. The getattribute opcode takes an object and an index as arguments and returns the attribute PMC at that position:

 classoffset I0, P2, "Foo"         # first "Foo" attribute of object P2 getattribute P10, P2, I0          # indexed get of attribute 

or:

 getattribute P10, P2, "Foo\x0.i"  # named get 

To set the value of an attribute PMC, first retrieve it with getattribute and then assign to the returned PMC. Because PMC registers are only pointers to values, you don't need to store the PMC again after you modify its value:

 getattribute P10, P2, I0 set P10, I5 

9.12.3 Methods

Methods in PASM are just subroutines installed in the namespace of the class. You define a method with the .pcc_sub directive before the label:

 .pcc_sub _half:   classoffset I0, P2, "Foo"   getattribute P10, P2, I0   set I5, P10                   # get value   div I5, 2   invoke P1 

This routine returns half of the value of the first attribute of the object. Method calls use the Parrot-calling conventions so they always pass the invocant object (often called self ) in P2 . Invoking the return continuation in P1 returns control to the caller.

The .pcc_sub directive automatically stores the subroutine as a global in the current namespace. The .namespace directive sets the current namespace:

 .namespace [ "Foo" ] 

If no namespace is set, or if the namespace is explicitly set to an empty string, then the subroutine is stored in the outermost namespace.

The callmethodcc opcode makes a method call. It follows the Parrot-calling conventions, so it expects to find the invocant object in P2 , the method object in P0 , etc. It adds one bit of magic, though. If you pass the name of the method in S0 , callmethodcc looks up that method name in the invocant object and stores the method object in P0 for you:

 set S0, "_half"             # set method name set P2, P3                  # the object savetop                     # preserve registers callmethodcc                # create return continuation, call restoretop print I5                    # result of method call print "\n" 

The callmethodcc opcode also generates a return continuation and stores it in P1 . The callmethod opcode doesn't generate a return continuation, but is otherwise identical to callmethodcc . Just like ordinary subroutine calls, you have to preserve and restore any registers you want to keep after a method call. Whether you store individual registers, register frames, or half register frames is up to you.

9.12.3.1 Overriding vtable functions

Every object inherits a default set of vtable functions from the ParrotObject PMC, but you can also override them with your own methods. The vtable functions have predefined names that start with a double underscore (_ _). The following code defines a method named _ _init in the Foo class that initializes the first attribute of the object with an integer:

 .pcc_sub _ _init:   classoffset I0, P2, "Foo"     # lookup first attribute position   new P6, .PerlInt              # create storage for the attribute   setattribute P2, I0, P6       # store the first attribute   invoke P1                     # return 

Ordinary methods have to be called explicitly, but the vtable functions are called implicitly in many different contexts. Parrot saves and restores registers for you in these calls. The _ _init method is called whenever a new object is constructed :

 find_type I1, "Foo" new P3, I1          # call _ _init if it exists 

A few other vtable functions in the complete code example for this section are _ _set_integer_native , _ _add , _ _get_integer , _ _get_string , and _ _increment . The set opcode calls Foo's _ _set_integer_native vtable function when its destination register is a Foo object and the source register is a native integer:

 set P3, 30          # call _ _set_integer_native method 

The add opcode calls Foo's _ _add vtable function when it adds two Foo objects:

 new P4, I1          # same with P4 set P4, 12 new P5, I1          # create a new store for add add P5, P3, P4      # _ _add method 

The inc opcode calls Foo's _ _increment vtable function when it increments a Foo object:

 inc P3              # _ _increment 

Foo's _ _get_integer and _ _get_string vtable functions are called whenever an integer or string value is retrieved from a Foo object:

 set I10, P5         # _ _get_integer  . . .  print P5            # calls _ _get_string, prints 'fortytwo' 

9.12.4 Inheritance

The subclass opcode creates a new class that inherits methods and attributes from another class. It takes three arguments: the destination register for the new class, a register containing the parent class, and the name of the new class:

 subclass P3, P1, "Bar" 

For multiple inheritance, the addparent opcode adds additional parents to a subclass.

 newclass P4, "Baz" addparent P3, P4 

To override an inherited method, define a method with the same name in the namespace of the subclass. The following code overrides Bar's _ _increment method so it decrements the value instead of incrementing it:

 .namespace [ "Bar" ] .pcc_sub _ _increment:   classoffset I0, P2, "Foo"     # get Foo's attribute slot offset   getattribute P10, P2, I0      # get the first Foo attribute   dec P10                       # the evil line   invoke P1 

Notice that the attribute inherited from Foo can be looked up only with the Foo class name, not the Bar class name. This preserves the distinction between attributes that belong to the class and inherited attributes.

Object creation for subclasses is the same as for ordinary classes:

 find_type I1, "Bar" new P5, I1 

Calls to inherited methods are just like calls to methods defined in the class:

 set P5, 42                  # inherited _ _set_integer_native inc P5                      # overridden _ _increment print P5                    # prints 41 as Bar's _ _increment decrements print "\n" set S0, "_half"             # set method name set P2, P5                  # the object savetop                     # preserve registers callmethodcc                # create return continuation, call restoretop print I5 print "\n" 

9.12.5 Additional Object Opcodes

The isa and can opcodes are also useful when working with objects. isa checks whether an object belongs to or inherits from a particular class. can checks whether an object has a particular method. Both return a true or false value.

 isa I0, P3, "Foo"                # 1 isa I0, P3, "Bar"                # 1 can I0, P3, "_ _add"                # 1 

9.12.6 Complete Example

 newclass P1, "Foo"   addattribute P1, "$.i"        # Foo.i   find_type I1, "Foo"   new P3, I1                    # call _ _init if it exists   set P3, 30                    # call _ _set_integer_native method   new P4, I1                    # same with P4   set P4, 12   new P5, I1                    # create a new LHS for add   add P5, P3, P4                # _ _add method   set I10, P5                   # _ _get_integer   print I10   print "\n"   print P5                      # calls _ _get_string prints 'fortytwo'   print "\n"   inc P3                        # _ _increment   add P5, P3, P4   print P5                      # calls _ _get_string prints '43'   print "\n"   subclass P3, P1, "Bar"   find_type I1, "Bar"   new P3, I1   set P3, 100   new P4, I1   set P4, 200   new P5, I1   add P5, P3, P4   print P5                      # prints 300   print "\n"   set P5, 42   print P5                      # prints 'fortytwo'   print "\n"   inc P5   print P5                      # prints 41 as Bar's   print "\n"                    # _ _increment decrements   set S0, "_half"               # set method name   set P2, P3                    # the object   savetop                       # preserve registers   callmethodcc                  # create return continuation, call   restoretop   print I5                      # prints 50   print "\n"   end     .namespace [ "Foo" ] .pcc_sub _ _init:   classoffset I0, P2, "Foo"     # lookup first attribute position   new P6, .PerlInt              # create a store for the attribute   setattribute P2, I0, P6       # store the first attribute   invoke P1                     # return .pcc_sub _ _set_integer_native:   classoffset I0, P2, "Foo"   getattribute P10, P2, I0   set P10, I5                   # assign passed in value   invoke P1 .pcc_sub _ _get_integer:   classoffset I0, P2, "Foo"   getattribute P10, P2, I0   set I5, P10                   # return value   invoke P1 .pcc_sub _ _get_string:   classoffset I0, P2, "Foo"   getattribute P10, P2, I0   set I5, P10   set S5, P10                   # get stringified value   ne I5, 42, ok   set S5, "fortytwo"            # or return modified one ok:   invoke P1 .pcc_sub _ _increment:   classoffset I0, P2, "Foo"   getattribute P10, P2, I0      # as with all aggregates, this   inc P10                       # has reference semantics - no   invoke P1                     # setattribute needed .pcc_sub _ _add:   classoffset I0, P2, "Foo"   getattribute P10, P2, I0      # object   getattribute P11, P5, I0      # argument   getattribute P12, P6, I0      # destination   add P12, P10, P11   invoke P1 .pcc_sub _half:                 # I5 = _half(self)   classoffset I0, P2, "Foo"   getattribute P10, P2, I0   set I5, P10                   # get value   div I5, 2   invoke P1     .namespace [ "Bar" ] .pcc_sub _ _increment:   classoffset I0, P2, "Foo"     # get Foo's attribute slot offset   getattribute P10, P2, I0      # get the first Foo attribute   dec P10                       # the evil line   invoke P1     # end of object example 

This example prints out:

 42 fortytwo 43 300 fortytwo 41 50 



Perl 6 and Parrot Essentials
Perl 6 and Parrot Essentials, Second Edition
ISBN: 059600737X
EAN: 2147483647
Year: 2003
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net