paul@810 | 1 | = Representing Program Objects = |
paul@810 | 2 | |
paul@861 | 3 | Certain representations have been chosen for program objects that attempt to |
paul@861 | 4 | support efficient access to those objects during the execution of a program. |
paul@810 | 5 | |
paul@810 | 6 | <<TableOfContents(2,2)>> |
paul@810 | 7 | |
paul@810 | 8 | == Attributes == |
paul@810 | 9 | |
paul@861 | 10 | The principal means of referring to an object in a program is by using an |
paul@861 | 11 | '''attribute''', having this name because it is the representation of an |
paul@861 | 12 | attribute in classes, instances and modules. Each attribute can hold a |
paul@861 | 13 | reference to an object, known as the '''value''', or other kinds of data: |
paul@810 | 14 | |
paul@810 | 15 | {{{#!graphviz |
paul@810 | 16 | //format=svg |
paul@810 | 17 | //transform=notugly |
paul@810 | 18 | digraph attributes { |
paul@810 | 19 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Attributes"]; |
paul@810 | 20 | edge [fontsize="13.0",fontname="Helvetica",tooltip="Attributes"]; |
paul@810 | 21 | rankdir=TB; |
paul@810 | 22 | |
paul@810 | 23 | attrA [label="attribute | { value |<value> reference to object }",shape=record]; |
paul@810 | 24 | obj1 [label="<main> object | { attr1 | value } | { attr2 | value } | ...",shape=record]; |
paul@810 | 25 | |
paul@810 | 26 | attrB [label="attribute | { intvalue |<intvalue> 12345 }",shape=record]; |
paul@810 | 27 | |
paul@810 | 28 | attrA:value -> obj1:main; |
paul@810 | 29 | } |
paul@810 | 30 | }}} |
paul@810 | 31 | |
paul@861 | 32 | Although a value indicates a specific object of interest for an attribute, if |
paul@861 | 33 | the object is callable then additional '''context''' information may be |
paul@861 | 34 | required to call the object. Such context information is not stored in an |
paul@861 | 35 | attribute record but is instead obtained from the object itself, if |
paul@861 | 36 | appropriate. |
paul@810 | 37 | |
paul@810 | 38 | === Integer Representation === |
paul@810 | 39 | |
paul@861 | 40 | The `intvalue` member of the attribute structure is employed instead of the |
paul@861 | 41 | `value` member to store integer values. Since `value` normally holds a |
paul@861 | 42 | pointer, and since pointers are often aligned to certain address boundaries on |
paul@861 | 43 | many modern platforms (usually four-byte boundaries on 32-bit platforms, |
paul@861 | 44 | eight-byte boundaries on 64-bit platforms, two-byte boundaries on platforms |
paul@861 | 45 | with 16-bit addressing), the lowest significant bit (bit 0) will typically be |
paul@861 | 46 | zero for a valid pointer. Consequently, by setting bit 0 to 1, other data can |
paul@861 | 47 | be stored in the remaining bits and be distinguished from pointer information. |
paul@861 | 48 | Obviously, operations on attributes first need to test whether the `value` |
paul@861 | 49 | member or the `intvalue` member is in use by testing bit 0. |
paul@810 | 50 | |
paul@861 | 51 | Thus, integers are transformed and stored directly within attributes, and they |
paul@861 | 52 | are therefore passed around by value. When an attribute of an integer needs to |
paul@861 | 53 | be accessed, the operation usually providing the `value` member, thus |
paul@861 | 54 | obtaining an instance under normal circumstances, instead provides the address |
paul@861 | 55 | of a common integer instance. This instance may then provide instance |
paul@861 | 56 | attributes, whose values will be the same for all integers, or class |
paul@861 | 57 | attributes through a reference to the integer (`int`) class. |
paul@810 | 58 | |
paul@861 | 59 | Each method provided by `int`, when called, will be given the original |
paul@861 | 60 | attribute for which the method was obtained as its context. In such methods, |
paul@861 | 61 | operations on the context via `self` will either involve the propagation of |
paul@861 | 62 | the attribute to other functions or methods or attribute accesses on `self`, |
paul@861 | 63 | yielding common instance attributes or class attributes as already described. |
paul@861 | 64 | Only native functions will attempt to interpret the attribute in a different |
paul@861 | 65 | way, decoding the representation, performing arithmetic or logical operations, |
paul@861 | 66 | and encoding the result in a new attribute. |
paul@810 | 67 | |
paul@810 | 68 | == Contexts and Wrappers == |
paul@810 | 69 | |
paul@861 | 70 | The context of an object is of significance if that object is callable. For |
paul@861 | 71 | example, an instance may permit access to a method via an attribute. Since the |
paul@861 | 72 | method will be callable, and since the method is accessed via the instance, |
paul@861 | 73 | the context of that method will be the instance. In order to retain both |
paul@861 | 74 | context and value information, a '''wrapper''' may be created. |
paul@810 | 75 | |
paul@810 | 76 | {{{#!graphviz |
paul@810 | 77 | //format=svg |
paul@810 | 78 | //transform=notugly |
paul@810 | 79 | digraph wrappers { |
paul@810 | 80 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Wrappers"]; |
paul@810 | 81 | edge [fontsize="13.0",fontname="Helvetica",tooltip="Wrappers"]; |
paul@810 | 82 | rankdir=TB; |
paul@810 | 83 | |
paul@810 | 84 | inst [label="<main> instance | { attr1 |<attr1> reference to method } | { attr2 | value } | ...",shape=record]; |
paul@810 | 85 | method [label="<main> method | ...",shape=record]; |
paul@810 | 86 | wrapper [label="<main> wrapper | { __context__ |<context> reference to instance } | { __value__ |<value> reference to method } | ...",shape=record]; |
paul@810 | 87 | |
paul@810 | 88 | inst:attr1 -> method:main; |
paul@810 | 89 | wrapper:context -> inst:main; |
paul@810 | 90 | wrapper:value -> method:main; |
paul@810 | 91 | } |
paul@810 | 92 | }}} |
paul@810 | 93 | |
paul@861 | 94 | The context is not always the accessor of the object - in this case, the |
paul@861 | 95 | instance - because the object may already be a wrapper with its own context. |
paul@810 | 96 | |
paul@810 | 97 | == Objects == |
paul@810 | 98 | |
paul@861 | 99 | Classes, instances and modules are objects, and all of these kinds of objects |
paul@861 | 100 | provide metadata describing the type of each object, together with a |
paul@861 | 101 | collection of attributes forming the contents of such objects. |
paul@810 | 102 | |
paul@810 | 103 | {{{#!graphviz |
paul@810 | 104 | //format=svg |
paul@810 | 105 | //transform=notugly |
paul@810 | 106 | digraph objects { |
paul@810 | 107 | node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Objects"]; |
paul@810 | 108 | edge [fontsize="13.0",fontname="Helvetica",tooltip="Objects"]; |
paul@810 | 109 | rankdir=TB; |
paul@810 | 110 | |
paul@810 | 111 | instC [label="<main> instance of C | { 0 | reference to\ninstance table } | { __class__ |<cls> reference\nto C } | { a | value } | { b | value } | ...",shape=record]; |
paul@810 | 112 | clsC [label="<main> class C | { class identifier | reference to\nclass table } | { __class__ |<cls> reference\nto type } | { __fn__ | instantiator\nreference } | { __args__ | reference to\nparameter table } | { f | <f> reference\nto f } | { g | value } | ...",shape=record]; |
paul@810 | 113 | methodF [label="<main> function f | { 0 | reference to\ninstance table } | { __class__ |<cls> reference\nto function } | { __fn__ | method\nreference } | { __args__ | reference to\nparameter table } | ...",shape=record]; |
paul@810 | 114 | function [label="<main> class function | { class identifier | reference to\nclass table } | { __class__ |<cls> reference\nto type } | ...",shape=record]; |
paul@810 | 115 | type [label="<main> class type | { class identifier | reference to\nclass table } | { __class__ |<cls> reference\nto type } | ...",shape=record]; |
paul@810 | 116 | |
paul@810 | 117 | instC:cls -> clsC:main; |
paul@810 | 118 | clsC:cls -> type:main; |
paul@810 | 119 | clsC:f -> methodF:main; |
paul@810 | 120 | methodF:cls -> function:main; |
paul@810 | 121 | } |
paul@810 | 122 | }}} |
paul@810 | 123 | |
paul@861 | 124 | Classes are represented by structures whose members reference class attributes |
paul@861 | 125 | and class metadata (the class table), as well as incorporating invocation |
paul@861 | 126 | metadata (the `__args__` and `__fn__` special attributes). |
paul@810 | 127 | |
paul@861 | 128 | Instances are represented by structures whose members reference instance |
paul@861 | 129 | attributes (including `__class__` which indicates the class instantiated by a |
paul@861 | 130 | given instance) and instance metadata (the instance table), as well as |
paul@861 | 131 | incorporating invocation metadata (the `__args__` and `__fn__` special |
paul@861 | 132 | attributes). |
paul@810 | 133 | |
paul@861 | 134 | Functions are instances of a general function type that does not permit |
paul@861 | 135 | additional, general instance attributes. However, function instance structures |
paul@861 | 136 | may have extra members corresponding to default parameter values. Access to |
paul@861 | 137 | such extra members is performed using the minimum and maximum values provided |
paul@861 | 138 | via `__args__` and with knowledge of the number of declared instance |
paul@861 | 139 | attributes indicated by the instance table for the function type. |
paul@810 | 140 | |
paul@861 | 141 | Modules are represented by structures whose members reference module |
paul@861 | 142 | attributes and module metadata (the module table). |
paul@810 | 143 | |
paul@810 | 144 | == Special Members == |
paul@810 | 145 | |
paul@861 | 146 | All object representations support the following special members describing |
paul@861 | 147 | the invocation properties of each object: |
paul@810 | 148 | |
paul@810 | 149 | {{{#!table |
paul@861 | 150 | `__args__` || the minimum number of arguments supported in an invocation and a |
paul@861 | 151 | .. reference to the parameter table for the object |
paul@810 | 152 | == |
paul@861 | 153 | `__fn__` || a reference to a native function containing the actual code run |
paul@861 | 154 | .. when calling the object |
paul@810 | 155 | }}} |
paul@810 | 156 | |
paul@861 | 157 | Classes are invoked in order to create instances of each class |
paul@861 | 158 | ('''instantiation'''). Instances may support invocation by providing a |
paul@861 | 159 | `__call__` method. Functions are supported by instances of a general function |
paul@861 | 160 | class. Modules are generally not callable and will not actually provide these |
paul@861 | 161 | special members in practice. |
paul@810 | 162 | |
paul@810 | 163 | All object representations support information about their type: |
paul@810 | 164 | |
paul@810 | 165 | {{{#!table |
paul@810 | 166 | `__class__` |
paul@861 | 167 | || the class of the object: instances refer to their classes, classes refer to |
paul@861 | 168 | .. the `type` class, functions are instances that refer to the `function` |
paul@861 | 169 | .. class, modules refer to the `module` class |
paul@810 | 170 | }}} |
paul@810 | 171 | |
paul@810 | 172 | Certain kinds of object support other descriptive attributes: |
paul@810 | 173 | |
paul@810 | 174 | {{{#!table |
paul@810 | 175 | `__name__` || the name of a class or a function |
paul@810 | 176 | == |
paul@810 | 177 | `__parent__` || the parent scope of a class or a function |
paul@810 | 178 | }}} |
paul@810 | 179 | |
paul@861 | 180 | Objects supported by native, system-level functionality require a means of |
paul@861 | 181 | retaining information in special attributes: |
paul@810 | 182 | |
paul@810 | 183 | {{{#!table |
paul@810 | 184 | `__data__` || private data manipulated at the native level |
paul@810 | 185 | }}} |
paul@810 | 186 | |
paul@861 | 187 | Strings support special annotation attributes that permit their use in |
paul@861 | 188 | dynamically resolving attributes: |
paul@810 | 189 | |
paul@810 | 190 | {{{#!table |
paul@861 | 191 | `__key__` || the code and position of the attribute whose name is represented |
paul@861 | 192 | .. by the string |
paul@810 | 193 | }}} |
paul@810 | 194 | |
paul@861 | 195 | Such "key" attributes provide information that can be used to inspect an |
paul@861 | 196 | object table and to test for the presence of an attribute. With such |
paul@861 | 197 | information, the `getattr` and `hasattr` functions can be supported. |
paul@810 | 198 | |
paul@810 | 199 | == Attribute Tables == |
paul@810 | 200 | |
paul@861 | 201 | In order to provide information about the attributes supported by each object, |
paul@861 | 202 | the structure of each object will reference a table containing entries |
paul@861 | 203 | describing these supported attributes. The size of this table is declared |
paul@861 | 204 | within the table structure, and for each position in the table an entry |
paul@861 | 205 | corresponding to the same position within an actual object structure describes |
paul@861 | 206 | the nature of the attribute at that position. |
paul@810 | 207 | |
paul@810 | 208 | == Parameter Tables == |
paul@810 | 209 | |
paul@861 | 210 | In order to support argument validation and keyword arguments in invocations, |
paul@861 | 211 | a structure is referenced by `__args__` that indicates... |
paul@810 | 212 | |
paul@810 | 213 | * The minimum number of parameters supported by a callable |
paul@810 | 214 | * The maximum number of parameters supported by a callable |
paul@810 | 215 | * The size of the table describing the parameters |
paul@861 | 216 | * A table of entries with each entry indicating the nature of a parameter (in |
paul@861 | 217 | effect, which name it uses, albeit as a generated code instead of a string) |
paul@861 | 218 | and the position of the parameter in any argument list prepared for an |
paul@861 | 219 | invocation |
paul@810 | 220 | |
paul@861 | 221 | Parameter tables only need to be consulted at run-time if the nature of a |
paul@861 | 222 | callable is undetermined. By supporting a uniform interface, the arguments |
paul@861 | 223 | used in an invocation can be tested against the description provided by |
paul@861 | 224 | `__args__` and the table. |