| | 1 | #if defined( FINCH_DEBUG_COMPILE ) |
| | 2 | #error entering fi_util.t... |
| | 3 | #endif |
| | 4 | |
| | 5 | #if !defined( FINCH_UTILITY_LIBRARY ) |
| | 6 | |
| | 7 | #include "tads.h" |
| | 8 | #include "t3.h" |
| | 9 | #include "lookup.h" |
| | 10 | |
| | 11 | modify TadsObject |
| | 12 | _name = nil |
| | 13 | |
| | 14 | _init_TadsObject() {} |
| | 15 | |
| | 16 | construct() { |
| | 17 | _init_(); |
| | 18 | } |
| | 19 | |
| | 20 | _init_( [args] ) { |
| | 21 | if (self == TadsObject) return; |
| | 22 | |
| | 23 | local obj = (args.length > 0 ? args[1] : self); |
| | 24 | |
| | 25 | local lst = getSuperclassList(); |
| | 26 | foreach (local c in lst) |
| | 27 | c._init_( obj ); |
| | 28 | |
| | 29 | callDefinedMethod( obj, '_init_' + self._name ); |
| | 30 | } |
| | 31 | ; |
| | 32 | |
| | 33 | /** |
| | 34 | * Permits the Finch utilities by saving the global symbol hashtable |
| | 35 | * before it gets garbage-collected after preinit. |
| | 36 | */ |
| | 37 | FinchUtility: PreinitObject |
| | 38 | globalSymbols = nil |
| | 39 | |
| | 40 | storeGlobalSymbols = true |
| | 41 | |
| | 42 | execute() { |
| | 43 | globalSymbols = t3GetGlobalSymbols(); |
| | 44 | |
| | 45 | t3DebugTrace( T3DebugBreak ); |
| | 46 | |
| | 47 | globalSymbols.forEach( |
| | 48 | { tok, obj : |
| | 49 | dataType( obj ) == TypeObject && |
| | 50 | obj.ofKind( TadsObject ) |
| | 51 | ? obj._name = tok |
| | 52 | : nil |
| | 53 | } |
| | 54 | ); |
| | 55 | |
| | 56 | for (local o = firstObj(); o; o = nextObj( o )) { |
| | 57 | if (o.ofKind( TadsObject)) { |
| | 58 | o._init_(); |
| | 59 | } |
| | 60 | } |
| | 61 | } |
| | 62 | ; |
| | 63 | |
| | 64 | /** |
| | 65 | * Retrieves the global symbol hashtable, saved by the FinchUtility |
| | 66 | * PreinitObject. |
| | 67 | */ |
| | 68 | getGlobalSymbols() { |
| | 69 | return FinchUtility.globalSymbols; |
| | 70 | } |
| | 71 | |
| | 72 | /** |
| | 73 | * Calls the given method-name on the given object using the given |
| | 74 | * argument list. |
| | 75 | * |
| | 76 | * @param obj an object reference |
| | 77 | * @param meth a string containing a valid property name |
| | 78 | * @param params all following parameters will be passed sequentially |
| | 79 | * to the method |
| | 80 | * @return whatever the method returned |
| | 81 | * @exception NoSuchSymbolException if the method name can't be found in |
| | 82 | * the global symbol table |
| | 83 | * @exception NoSuchMethodException if the associated entry in the |
| | 84 | * global symbol table isn't a property |
| | 85 | * @exception InvocationTargetException if the method itself threw an |
| | 86 | * exception. |
| | 87 | */ |
| | 88 | callMethod( obj, meth, [params] ) { |
| | 89 | |
| | 90 | // get the actual method reference from the symbol table |
| | 91 | local prop = (getGlobalSymbols())[ meth ]; |
| | 92 | |
| | 93 | // if the symbol doesn't exist, throw an exception |
| | 94 | if (prop == nil) |
| | 95 | throw new NoSuchSymbolException( meth ); |
| | 96 | |
| | 97 | // if the symbol isn't a property, throw an exception |
| | 98 | else if (dataType( prop ) != TypeProp) |
| | 99 | throw new NoSuchMethodException( meth ); |
| | 100 | |
| | 101 | else { |
| | 102 | try { |
| | 103 | return obj.(prop)( params... ); |
| | 104 | } |
| | 105 | catch (Exception e) { |
| | 106 | throw new InvocationTargetException( obj, meth, e ); |
| | 107 | } |
| | 108 | } |
| | 109 | } |
| | 110 | |
| | 111 | /** |
| | 112 | * Calls the given method-name on the given object using the given |
| | 113 | * argument list. If the method does not exist, or exists but isn't |
| | 114 | * defined on this object, ignore it (instead of, say, trying to call |
| | 115 | * a method that we know won't ever be valid, since code can never be |
| | 116 | * assigned to a method in the current implementation of the VM). |
| | 117 | * |
| | 118 | * @param obj an object reference |
| | 119 | * @param meth a string containing a valid property name |
| | 120 | * @param params all following parameters will be passed sequentially |
| | 121 | * to the method |
| | 122 | * @return whatever the method returned |
| | 123 | * @exception NoSuchMethodException if the associated entry in the |
| | 124 | * global symbol table isn't a property |
| | 125 | * @exception InvocationTargetException if the method itself threw an |
| | 126 | * exception. |
| | 127 | */ |
| | 128 | callDefinedMethod( obj, meth, [params] ) { |
| | 129 | |
| | 130 | // get the actual method reference from the symbol table |
| | 131 | local prop = (getGlobalSymbols())[ meth ]; |
| | 132 | |
| | 133 | // if the symbol doesn't exist, abort. |
| | 134 | if (prop == nil) return nil; |
| | 135 | |
| | 136 | // if the property isn't defined by this object, abort. |
| | 137 | if (!obj.propDefined( prop )) return nil; |
| | 138 | |
| | 139 | // if the symbol isn't a property, throw an exception |
| | 140 | else if (dataType( prop ) != TypeProp) |
| | 141 | throw new NoSuchMethodException( meth ); |
| | 142 | |
| | 143 | else { |
| | 144 | try { |
| | 145 | return obj.(prop)( params... ); |
| | 146 | } |
| | 147 | catch (Exception e) { |
| | 148 | throw new InvocationTargetException( obj, meth, e ); |
| | 149 | } |
| | 150 | } |
| | 151 | } |
| | 152 | |
| | 153 | /** |
| | 154 | * Creates a new property, updating the global symbol table. |
| | 155 | * |
| | 156 | * @param str a string to correspond to the new property |
| | 157 | * @return the new property |
| | 158 | */ |
| | 159 | createGlobalProperty( str ) { |
| | 160 | |
| | 161 | // allocate a new property id |
| | 162 | local prop = t3AllocProp(); |
| | 163 | |
| | 164 | // assign it to the given string |
| | 165 | (getGlobalSymbols())[ str ] = prop; |
| | 166 | |
| | 167 | // return the new property |
| | 168 | return prop; |
| | 169 | } |
| | 170 | |
| | 171 | /** |
| | 172 | * A nearly meta-exception thrown when reflectively invoked code |
| | 173 | * throws an exception. |
| | 174 | */ |
| | 175 | class InvocationTargetException: Exception |
| | 176 | location = nil |
| | 177 | position = nil |
| | 178 | datum = nil |
| | 179 | |
| | 180 | construct( obj, meth, e ) { |
| | 181 | location = obj; |
| | 182 | position = meth; |
| | 183 | datum = e; |
| | 184 | } |
| | 185 | |
| | 186 | getMessage() { |
| | 187 | "InvocationTargetException: the method << position >> |
| | 188 | on the object << location._name >> threw the following |
| | 189 | exception: << datum.getMessage() >>. "; |
| | 190 | } |
| | 191 | ; |
| | 192 | |
| | 193 | /** |
| | 194 | * An exception thrown when a reflectively invoked method |
| | 195 | * does't actually exist. |
| | 196 | */ |
| | 197 | class NoSuchMethodException: Exception |
| | 198 | position = nil |
| | 199 | |
| | 200 | construct( meth ) { |
| | 201 | position = meth; |
| | 202 | } |
| | 203 | |
| | 204 | getMessage() { |
| | 205 | "NoSuchMethodException: the symbol << position >> |
| | 206 | does not correspond to a property address."; |
| | 207 | } |
| | 208 | ; |
| | 209 | |
| | 210 | /** |
| | 211 | * An exception thrown when a reflectively requested symbol |
| | 212 | * does't actually exist. |
| | 213 | */ |
| | 214 | class NoSuchSymbolException: Exception |
| | 215 | position = nil |
| | 216 | |
| | 217 | construct( sym ) { |
| | 218 | position = sym; |
| | 219 | } |
| | 220 | |
| | 221 | getMessage() { |
| | 222 | "NoSuchSymbolException: the symbol << position >> |
| | 223 | does not exist in the global symbols table."; |
| | 224 | } |
| | 225 | ; |
| | 226 | |
| | 227 | #endif |
| | 228 | |