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