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 |