9 The run-time veneer ------------------- "I think you now have a fully-stocked veneer shop, with the rest of the compiler being a pencil sharpener bolted to the wall in the back room." -- Andrew Plotkin, letter to the author, 10 January 1999 9.1 Services provided by the veneer ------------------------------- The "veneer" is a thin section of code generated by Inform as an intermediate layer between the code compiled from the source, and the Z-machine itself: like the veneer on a table, it gilds the surface of the Z-machine, or so the term is supposed to mean. It consists of 38 routines, 4 class-objects, 2 properties and 2 global variables, together with several tables in the Z-machine's dynamic memory and a number of strings, notably the source-code names of arrays and properties, which help in printing out good error messages. The veneer adds a major data structure not present in the Z-machine architecture: the concept of "individual property", a mechanism to allow games to have more or less unlimited numbers of properties which don't need to be declared before use. The "veneer.c" section of Inform organises the compilation of the routines; each one is compiled only if actually needed, and if no over-riding definition has already been given in the source code. (For instance, "PrintShortName" is a veneer routine but the Inform library provides a much fuller version than the one in "veneer.c".) Note that the full Inform source code for these routines is present in static strings in "veneer.c" (which are compiled using the lexer's "read from a string" mode). The routines come in five groups. Note that names with double underscores in are so chosen not to clash with identifiers used by the programmer. Main__ This is not really a routine at all, but is the piece of code which the Z-machine's PC is initially set to. It simply makes a function call to Main(), and then quits. Box__Routine Code to display an array of static strings in a black "quotations" box, as required by the "box" statement. Printing routines PrintShortName, DefArt, CDefArt, etc.; all normally over-ridden within the Inform library. Also RT__Err, "print an error message at run-time". Object orientation routines Routines to implement message sending, access to individual properties, "metaclass", "ofclass" and "provides". Assertion routines Routines needed by "strict mode" to check that proposed object moves, memory read/writes, etc. will not crash the Z-machine if executed. Since veneer routines are compiled at the end of the pass (when it's known which will be needed), the code area looks like this: start of code area --> 00 (routine header for Main__) initial PC value --> @call_1n Main @quit ... routines as defined in source code... veneer routines as required, in order of appearance in the "veneer.c" table (Note that Main__ is assembled as a routine. In the Version 6 Z-machine, this is called to begin execution. In other versions, the initial PC value is set to the call_1n instruction -- which is why Main__ must be first: the initial PC value has to lie in the bottom 64K of Z-machine memory. Note also: for the Versions 3 and 4 Z-machine more primitive call opcodes are used to get into Main.) The four objects in the veneer are the class-objects for the four metaclasses defined in all Inform programs: Class, Object, Routine and String. The object tree therefore looks like this: 1 Class 2 Object 3 Routine 4 String ... objects and class-objects as defined in source code ... The two global variables are "self" and "sender", used in message-passing, and which are two of the 7 highest-numbered global variables which Inform reserves to its own use as "registers". 9.2 Specification of the veneer routines ------------------------------------ Please note that some of the specifications given here may change in future compiler releases without warning or apology -- so it is unwise to write code which accesses the veneer directly. Box__Routine(maxw, table) Display a "Trinity"-style quotation box, whose text is given as a table array of packed addresses of strings giving individual lines, and whose maximum text width is "maxw". R_Process(action, noun, second) Implement . (The Inform library does this properly by defining its own "R_Process" routine; the ordinary veneer would just print text like "<23 2 3>".) DefArt(object) IndefArt(object) CDefArt(object) Print the object's name, prefixed by definite/indefinite/capitalised definite article. (Again, the veneer's plain version is very plain.) PrintShortName(object) Print just the object's short name: but give a sensible response, and in particular don't crash the Z-machine, even if "object" is any illegal value. EnglishNumber(n) Print out "n" in words. (The Inform library does this properly: the plain veneer just prints n as a number.) Print__PName(property) Print a textual description of the property, e.g. "before" or "Coin::before". WV__Pr(object, property, value) Implement object.property = value, printing suitable run-time errors if either object or property are invalid or if the object doesn't provide property. RV__Pr(object, property) Likewise but simply read object.property. CA__Pr(object, property, a, b, c, d, e, f) Implement object.property(a, b, c, d, e, f) with a to f being optional. Note that object might be a class, routine or string and this is where the messages provided by these pseudo-objects are implemented. IB__Pr(object, property) Implement ++object.property. IA__Pr(object, property) Implement object.property++. DB__Pr(object, property) Implement --object.property. DA__Pr(object, property) Implement object.property++. RA__Pr(object, property) Implement object.&property (much more difficult than it sounds: this is where individual property tables are effectively implemented). RL__Pr(object, property) Implement object.#property. RA__SC(class, property) Implement class::property, returning this property number. OP__Pr(object, property) Implement the condition "object provides property". OC__Cl(object, class) Implement the condition "object ofclass class". Copy__Primitive(o1, o2) Make o1 a copy of o2, in the sense that: the attributes of o1 become exactly those of o2; and for every property provided by o2, that property of o1 has the o2 value copied over. RT__Err(error_number, ...parameters...) Print the appropriate run-time error message, which always takes the form "[** Programming error: ... **]". , , "class (object number ...) has no property to [and nor does any other object" , , " (object number ...) has no property to [and nor does any other object" , , - "class (object number ...) is not of class " , , - " (object number ...) is not of class " 1, "class : 'create' can have 0 to 3 parameters only" 2, "tried to test "in" or "notin" of object " 3, "tried to test "has" or "hasnt" of object " 4, "tried to find the "parent" of object " 5, "tried to find the "eldest" of object " 6, "tried to find the "child" of object " 7, "tried to find the "younger" of object " 8, "tried to find the "sibling" of object " 9, "tried to find the "children" of object " 10, "tried to find the "youngest" of object " 11, "tried to find the "elder" of object " 12, "tried to use "objectloop" of object " 13, "tried to use "}" at end of "objectloop" of object " 14, "tried to "give" an attribute to object " 15, "tried to "remove" object " 16, "tried to "move" to " where is illegal 17, "tried to "move" to " where is illegal 18, "tried to "move" to , which would make a loop: in ... in in " 19, "tried to "give" or test "has" or "hasnt" for a non-attribute of " 20, "tried to divide by zero" 21, "tried to find the ".&" of object " 22, "tried to find the ".#" of object " 23, "tried to find the "." of object " 24, "tried to read outside memory using ->" 25, "tried to read outside memory using -->" 26, "tried to write outside memory using ->" 27, "tried to write outside memory using -->" 28, , , , "tried to read from -> in the , which has entries to " The array types are: 0 = byte -> array, 1 = word --> array, 2 = string array, 3 = table array and the array name is the packed string address at #array_names_table-->. 29, , , , "tried to read from --> in the , which has entries to " 30, , , , "tried to write to -> in the , which has entries to " 31, , , , "tried to write to --> in the , which has entries to " 32, "objectloop was broken because the object was moved while the loop passed through it" 33, "tried to print (char) which is not a valid ZSCII character code for output" 34, "tried to print (address) on something not the byte address of a string" 35, "tried to print (string) on something not a string" 36, "tried to print (object) on something not an object or class" Z__Region(value) Returns 1 if value is a Z-machine object number, 2 if a packed address within the code area, 3 if a packed address within the strings area and 0 otherwise. Unsigned__Compare(x, y) Returns 1 if x>y, 0 if x=y, -1 if xoffset, do so; otherwise print an error message. RT__ChLDW(base, offset) If it is legal to read base-->offset, do so; otherwise print an error message. RT__ChLDB(base, offset, value) If it is legal to set base->offset = value, do so; otherwise print an error message. RT__ChLDW(base, offset) If it is legal to set base-->offset = value, do so; otherwise print an error message. Symb__Tab(code, num) Only present in games compiled with -X for Infix. In effect this holds some static data in the code area, rather than using up the limited readable memory area with further tables. If code is 1, returns data on the array numbered "num" and stores further in temp_var2 and temp_var3; if code is 2, ditto for routines; if 3, for constants. This routine is subject to change without notice, to put it mildly. 9.3 Properties 2 and 3, "ofclass" and "metaclass" --------------------------------------------- Inform reserves common properties 2 and 3 to itself, and uses them as follows: property 2 (additive): a word array containing the class-object numbers of all classes of which this object is a member; the property is absent if the object belongs to no classes. (Note that metaclasses do not appear here: tree objects are all members of either Object or Class but neither is listed here.) property 3: a byte address pointing to the individual properties table for this object; property 3 is absent if it has no individual properties. The veneer tests "X ofclass Y" according to the rules: if X is a valid Z-machine object number: if X is a class-object (tested by: if X is a child of Class) then only if Y is Class otherwise only if Y appears in the property 2 list for X if X is the packed address of a routine: only if Y is Routine if X is the packed address of a string: only if Y is String giving a run-time error if Y isn't a class-object. Note that determining which of these ranges contains X needs code essentially the same as that in the Inform 5/12 library routine "ZRegion": indeed, this is how the veneer routine implementing the "metaclass" function works. 9.4 Class numbering and class objects --------------------------------- In Inform 6 each class definition results in the creation of a related object, the class-object for that class. Class objects have: their internal symbol-name as short name; no attributes (*); no (common) properties except possibly for property 3. All except for Class are placed in the object tree as children of Class. Immediately following the property values table for a class (which is bound to be short, since it can only contain the short name and perhaps property 3) is a six-byte block of attributes which would be inherited from the class (*), and then a second property values table, specifying the properties which members of the class would inherit from it. (These values are accessed at run time to implement the superclass access operator :: when applied to a common property.) Property 3 is used to hold the byte address of the individual properties table of properties which members would inherit. It is absent if there are no such properties. (These are accessed at run-time to implement the superclass access operator :: when applied to an individual property.) Note that there are two numbering systems used for classes: the "class number" counts 0, 1, 2, 3, ... in order of declaration, with Class numbered 0. (This is used in superclass message number coding.) Classes are also referred to by the object numbers of their class objects. It's necessary to be able to convert between the two at run time; the "class numbers table" is a word array whose Nth entry is the class-object number corresponding to class number N. [(*) This is a change made in Inform 6.12. Previously, class objects had the attributes which would be inherited from the class. 6.12 moves this information to a harmless block of six bytes elsewhere, essentially so that loops like "objectloop (x has animate)" do not pick up classes as well as objects.] 9.5 Individual property identifiers ------------------------------- Just as the common properties are identified by property numbers 1 to 63, so the individual properties are identified by numbers 64 upward. The group 64 to 71 is used to identify the properties provided by inheritance from the metaclasses: Id Name Provided by 64 create class-objects other than metaclass-objects 65 recreate ditto 66 destroy ditto 67 remaining ditto 68 copy ditto 69 call objects of metaclass Routine 70 print objects of metaclass String 71 print_to_array ditto However, in addition to this a property ID number with its top bit set has a special meaning: Bit 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 set -------------------------- ----------------------------- entry number class number within class's i.p. table (counting from 0) which means "the given class's inheritable value of the property with this ID number", and is used to implement the "superclass" operator as something which acts on ID numbers: Class :: identifier is implemented by writing the class number into the bottom bits and searching as above. Further, a property ID number in the form: Bit 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 00 set --------------------- ----------------------------- common prop number class number refers to "the given class's inheritable value of this common property". As a special rule, in the case of class Object this accesses the Z-machine default property value. Thus, since the library defines Property cant_go "You can't go that way."; then X.Object::cant_go will always evaluate to "You can't go that way." for any Object X. Note that the need to be able to pack such descriptions into 16 bits is one reason classes need to have a 0, 1, 2, ... numbering system in addition to the class-object system of numbering (which has many lacunae). Even as it is, the present veneer implementation constrains us to a maximum of 256 class definitions, each able to provide at most 128 individual properties (in addition to the potentially 63 declared as common using Property); more work may be needed to alleviate this at a later date. 9.6 Individual property tables -------------------------- For each object that provides any individual properties at all, property 3 holds the byte address of its individual property table. The entries in this table are pairs of identifier number and current value: thus, raw identifier number (2 bytes) but with top bit set if "private" to the object in question property length (1 byte) current value (as many bytes as specified in the property length, though this will always be an even number >= 2) terminated by a zero word, 00 00. (Note that there is no property 0, common or individual.) The properties can occur in any order and need not be in numerical order of their IDs (which is helpful to assist linking, though it may have speed implications). The following Inform code prints out an object's table: [ IndivTable obj x n; x = obj.3; if (x == 0) rfalse; print "Table for ", (object) obj, " is at ", (hex) x, "^"; while (x-->0 ~= 0) { print (hex) x-->0, " (", (property) x-->0, ") length ", x->2, ": "; for (n = 0: n< (x->2)/2: n++) print (hex) (x+3)-->n, " "; new_line; x = x + x->2 + 3; } ]; [ hex x y; y = (x & $ff00) / $100; x = x & $ff; print (hdigit) y/$10, (hdigit) y, (hdigit) x/$10, (hdigit) x; ]; [ hdigit x; x = x % $10; if (x<10) print x; else print (char) 'a'+x-10; ]; From an inheritance point of view, individual properties are always non-additive: a specified value overrides and replaces an inherited value. (The idea is that access to superclass values is a more elegant alternative to using additive properties to make class values remain available to inheritors which have altered them.) The veneer contains routines to implement "provides", ".", ".&" and ".#", together with routines to apply ++ and -- as pre- or postfix operators, and a routine to implement = in the context of individual properties. These all lapse back into ordinary Z-machine instructions for common properties if they find a property number in the range 1 to 63. Note that the veneer's implementation of "provides" contains a special rule meaning that class-objects do not provide any properties except the five inherited from the metaclass Object, even though they have a table in the above format (which contains the inheritable values). The veneer's highest-level routine sends a message to an individual property. (This same routine contains implementations of the eight messages numbered 64 to 71.) Note that the trickiest part is keeping "self" and "sender" right: push the current "self" value push the current "sender" value set "sender" to "self" set "self" to the destination object now make the function call pull to "sender" pull to "self" Note that the object creation/deletion system works in a quite simple way: the pool of uncreated duplicate members of a class are exactly the children of its class-object. Objects are re-initialised when they are returned to this pool, leaving them "fresh" and ready for recreation. 9.7 Availability of symbol names at run-time ---------------------------------------- It is not possible to determine at compile-time if a message is wrongly addressed, i.e., if a message is sent to property X of object O but object O does not provide X. For one thing, availability depends on who is calling (if the property is private to O); for another, message identifiers do not need to be constants. Therefore a good error-reporting mechanism is needed at run-time; errors like "Object 32 has no property 57" would make debugging hopelessly difficult. The only way to achieve this is for the property names to be available at run time. Similarly, it's helpful for debugging purposes if attribute names and action names can also be available. To this end, Inform compiles a table of identifier names into every story file (though not into modules) and provides a system constant called #identifiers_table pointing to the start of the table. The table contains: --> 0 = Number of property names plus 1 = P --> 1 = Packed address of string containing name of property 1 to --> (P-1) = Packed address of string containing name of property P-1 (properties 1 to 63 will be common, and subsequent ones individual: this table includes both kinds; note that there is no property numbered 0) --> P = Packed address of string containing name of attribute 0 to --> (P+47) = Packed address of string containing name of attribute 47 --> (P+48) = Packed address of string containing name of action 0 and so on, followed by #array_names_table-->0 = Packed address of string containing name of array 0 and so on. (Array numbers are allocated from 0 upwards as arrays are declared in the source code: they're used by the veneer only for printing array-bound-violation error messages, and only in strict mode.) In the case of (common) property and attribute names, the ability of programmers to "alias" names together means that these names will sometimes be given as a list of possibilities with slashes in between. Note that printing property names so as to properly handle usage of :: is harder than simply looking up in this table (for instance, there are times when the right output should look like "Coin::value"). Inform provides the construct print (property) X; to handle this. But the corresponding cases for attributes and actions are easier. Simply define: [ DebugAction a anames; if (a>=256) print ""; anames = #identifiers_table; anames = anames + 2*(anames-->0) + 2*48; print (string) anames-->a; ]; [ DebugAttribute a anames; if (a<0 || a>=48) print ""; else { anames = #identifiers_table; anames = anames + 2*(anames-->0); print (string) anames-->a; } ]; (These are in fact both part of Library 6/3 anyway: a different DebugAction routine is present in Library 6/1 and 6/2 if DEBUG is set.) You can then: print (DebugAction) X; print (DebugAttribute) X; It is also for error reporting that the names of the classes are needed at run-time, which is why the short name of a class-object is the name of the class.