| | 1 | /* $Header: d:/cvsroot/tads/tads3/VMMETA.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ |
| | 2 | |
| | 3 | /* |
| | 4 | * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. |
| | 5 | * |
| | 6 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 7 | * on using and copying this software. |
| | 8 | */ |
| | 9 | /* |
| | 10 | Name |
| | 11 | vmmeta.h - metaclass dependency table |
| | 12 | Function |
| | 13 | The metaclass dependency table establishes the correspondence between |
| | 14 | metaclass index numbers and metaclasses. The load image file contains |
| | 15 | a list of the metaclasses that the program requires, identifying each |
| | 16 | required metaclass with its universally unique identifier string. At |
| | 17 | load time, we scan this list in the load image and create a dependency |
| | 18 | table. |
| | 19 | Notes |
| | 20 | |
| | 21 | Modified |
| | 22 | 12/01/98 MJRoberts - Creation |
| | 23 | */ |
| | 24 | |
| | 25 | #ifndef VMMETA_H |
| | 26 | #define VMMETA_H |
| | 27 | |
| | 28 | #include <stdlib.h> |
| | 29 | |
| | 30 | #include "t3std.h" |
| | 31 | #include "vmtype.h" |
| | 32 | #include "vmglob.h" |
| | 33 | #include "vmobj.h" |
| | 34 | |
| | 35 | /* ------------------------------------------------------------------------ */ |
| | 36 | /* |
| | 37 | * Metaclass table entry. This contains information on the metaclass at |
| | 38 | * one index in the table. |
| | 39 | */ |
| | 40 | struct vm_meta_entry_t |
| | 41 | { |
| | 42 | /* the class descriptor object for the metaclass */ |
| | 43 | class CVmMetaclass *meta_; |
| | 44 | |
| | 45 | /* |
| | 46 | * name of metaclass as read from image file (this is important for |
| | 47 | * rewriting an image file during preinit, since it ensures that we |
| | 48 | * store only the version number required by the compiled image |
| | 49 | * file, not the version number actually implemented in the preinit |
| | 50 | * interpreter) |
| | 51 | */ |
| | 52 | char *image_meta_name_; |
| | 53 | |
| | 54 | /* |
| | 55 | * the IntrinsicClass object (a CVmObjClass object) that represents |
| | 56 | * this class |
| | 57 | */ |
| | 58 | vm_obj_id_t class_obj_; |
| | 59 | |
| | 60 | /* |
| | 61 | * Property translation list. The translation list is an array of |
| | 62 | * internal function numbers, indexed by property ID. However, not |
| | 63 | * all property ID's are necessarily represented in the index - |
| | 64 | * instead, the index contains entries only from a minimum property |
| | 65 | * ID, and only contains a specified number of entries. So, given |
| | 66 | * property ID 'prop_id', we find the internal function number for |
| | 67 | * the property at index (prop_id - min_prop_id) in the table. |
| | 68 | * |
| | 69 | * We use an unsigned short (16 bits) for each internal function |
| | 70 | * number - this limits the number of functions per metaclass to |
| | 71 | * 65535, which should be more than adequate while keeping memory |
| | 72 | * usage within reason. |
| | 73 | */ |
| | 74 | vm_prop_id_t min_prop_; |
| | 75 | size_t prop_xlat_cnt_; |
| | 76 | unsigned short *prop_xlat_; |
| | 77 | |
| | 78 | /* |
| | 79 | * Function table. This is the reverse of the property translation |
| | 80 | * list: this table gives the property ID for each of the metaclass |
| | 81 | * functions. The first entry is the property of function index 1, |
| | 82 | * the second entry is function index 2, and so on. |
| | 83 | */ |
| | 84 | vm_prop_id_t *func_xlat_; |
| | 85 | size_t func_xlat_cnt_; |
| | 86 | |
| | 87 | /* relese storage */ |
| | 88 | void release_mem() |
| | 89 | { |
| | 90 | /* if this entry has a property table, delete it */ |
| | 91 | if (prop_xlat_ != 0) |
| | 92 | { |
| | 93 | /* free it */ |
| | 94 | t3free(prop_xlat_); |
| | 95 | |
| | 96 | /* forget the pointer */ |
| | 97 | prop_xlat_ = 0; |
| | 98 | } |
| | 99 | |
| | 100 | /* if this entry has a function translation table, delete it */ |
| | 101 | if (func_xlat_ != 0) |
| | 102 | { |
| | 103 | /* free it */ |
| | 104 | t3free(func_xlat_); |
| | 105 | |
| | 106 | /* forget the pointer */ |
| | 107 | func_xlat_ = 0; |
| | 108 | } |
| | 109 | |
| | 110 | /* relesae our stored name */ |
| | 111 | lib_free_str(image_meta_name_); |
| | 112 | } |
| | 113 | |
| | 114 | /* store the metaclass name as loaded from the image file */ |
| | 115 | void set_meta_name(const char *nm) |
| | 116 | { |
| | 117 | /* allocate space and store the name */ |
| | 118 | image_meta_name_ = lib_copy_str(nm); |
| | 119 | } |
| | 120 | |
| | 121 | /* |
| | 122 | * Add a property to the table, given the property ID and the |
| | 123 | * 1-based index of the corresponding function in the metaclass's |
| | 124 | * internal function table. |
| | 125 | */ |
| | 126 | void add_prop_xlat(vm_prop_id_t prop, ushort func_idx) |
| | 127 | { |
| | 128 | /* |
| | 129 | * Add the translation entry from property ID to function index |
| | 130 | * - note that we store this as a 1-based function table index |
| | 131 | * value, because we reserve function index 0 to indicate that |
| | 132 | * the property is invalid and has no function translation |
| | 133 | */ |
| | 134 | prop_xlat_[prop - min_prop_] = func_idx; |
| | 135 | |
| | 136 | /* |
| | 137 | * Add the translation entry from function index to property ID. |
| | 138 | * Note that we must adjust the 1-based function index down one |
| | 139 | * to get the index into our internal array. |
| | 140 | */ |
| | 141 | func_xlat_[func_idx - 1] = prop; |
| | 142 | } |
| | 143 | |
| | 144 | /* |
| | 145 | * Get the translation for a given property ID. This returns zero |
| | 146 | * if the property does not map to a valid function index for the |
| | 147 | * metaclass, or the 1-based index of the function in the |
| | 148 | * metaclass's "vtable" if the property is defined. |
| | 149 | */ |
| | 150 | unsigned short xlat_prop(vm_prop_id_t prop) const |
| | 151 | { |
| | 152 | /* |
| | 153 | * Check to see if the property is represented in the table. If |
| | 154 | * it's not within the range of properties in the table, there |
| | 155 | * is no translation for the property. |
| | 156 | */ |
| | 157 | if (prop < min_prop_ || prop >= min_prop_ + prop_xlat_cnt_) |
| | 158 | { |
| | 159 | /* the property isn't in the table, so it has no translation */ |
| | 160 | return 0; |
| | 161 | } |
| | 162 | |
| | 163 | /* return the function index from the table */ |
| | 164 | return prop_xlat_[prop - min_prop_]; |
| | 165 | } |
| | 166 | |
| | 167 | /* |
| | 168 | * Translate a function index to a property ID. The function index |
| | 169 | * is given as a 1-based index, for consistency with the return |
| | 170 | * value from xlat_prop(). |
| | 171 | */ |
| | 172 | vm_prop_id_t xlat_func(unsigned short func_idx) const |
| | 173 | { |
| | 174 | /* |
| | 175 | * if it's zero, or it's greater than the number of functions in |
| | 176 | * our table, it has no property translation |
| | 177 | */ |
| | 178 | if (func_idx == 0 || func_idx > func_xlat_cnt_) |
| | 179 | return VM_INVALID_PROP; |
| | 180 | |
| | 181 | /* |
| | 182 | * return the entry from our table for the given index, adjusted |
| | 183 | * to a 1-based index value |
| | 184 | */ |
| | 185 | return func_xlat_[func_idx - 1]; |
| | 186 | } |
| | 187 | }; |
| | 188 | |
| | 189 | |
| | 190 | /* ------------------------------------------------------------------------ */ |
| | 191 | /* |
| | 192 | * The metaclass table. One global instance of this table is generally |
| | 193 | * created when the image file is loaded. |
| | 194 | */ |
| | 195 | class CVmMetaTable |
| | 196 | { |
| | 197 | public: |
| | 198 | /* |
| | 199 | * Create a table with a given number of initial entries. The table |
| | 200 | * may be expanded in the future if necessary, but if the caller can |
| | 201 | * predict the maximum number of entries required, we can |
| | 202 | * preallocate the table at its final size and thus avoid the |
| | 203 | * overhead and memory fragmentation of expanding the table. |
| | 204 | */ |
| | 205 | CVmMetaTable(size_t init_entries); |
| | 206 | |
| | 207 | /* delete the table */ |
| | 208 | ~CVmMetaTable(); |
| | 209 | |
| | 210 | /* clear all existing entries from the table */ |
| | 211 | void clear(); |
| | 212 | |
| | 213 | /* |
| | 214 | * Add an entry to the table, given the metaclass identifier (a |
| | 215 | * string giving the universally unique name for the metaclass). |
| | 216 | * Fills in the next available slot. Throws an error if the |
| | 217 | * metaclass is not present in the system. |
| | 218 | * |
| | 219 | * If min_prop and max_prop are both VM_INVALID_PROP, the metaclass |
| | 220 | * has no properties in the translation table. |
| | 221 | */ |
| | 222 | void add_entry(const char *metaclass_id, size_t func_cnt, |
| | 223 | vm_prop_id_t min_prop, vm_prop_id_t max_prop); |
| | 224 | |
| | 225 | /* add an entry to the table given the registration table index */ |
| | 226 | void add_entry(const char *metaclass_id, uint idx, size_t func_cnt, |
| | 227 | vm_prop_id_t min_prop, vm_prop_id_t max_prop); |
| | 228 | |
| | 229 | /* |
| | 230 | * Add an entry to the table given the registration table index, but |
| | 231 | * only if the metaclass at this index isn't already defined in the |
| | 232 | * dependency table; if the metaclass is already in the dependency |
| | 233 | * table, this function has no effect. |
| | 234 | */ |
| | 235 | void add_entry_if_new(uint reg_table_idx, size_t func_cnt, |
| | 236 | vm_prop_id_t min_prop, vm_prop_id_t max_prop); |
| | 237 | |
| | 238 | /* |
| | 239 | * Get the dependency table index for a metaclass, given the |
| | 240 | * registration table index. If the metaclass is not present in the |
| | 241 | * metaclass table, we'll return -1. |
| | 242 | */ |
| | 243 | int get_dependency_index(uint reg_table_idx) const |
| | 244 | { return reverse_map_[reg_table_idx]; } |
| | 245 | |
| | 246 | /* |
| | 247 | * get the depencency table entry for a metaclass, given the |
| | 248 | * registration table index |
| | 249 | */ |
| | 250 | vm_meta_entry_t *get_entry_from_reg(int reg_idx) |
| | 251 | { |
| | 252 | int dep_idx; |
| | 253 | |
| | 254 | /* translate the registration table index into a dependency index */ |
| | 255 | dep_idx = get_dependency_index(reg_idx); |
| | 256 | |
| | 257 | /* |
| | 258 | * if we have a valid dependency index, return it; otherwise, |
| | 259 | * return null |
| | 260 | */ |
| | 261 | if (dep_idx >= 0) |
| | 262 | return &table_[dep_idx]; |
| | 263 | else |
| | 264 | return 0; |
| | 265 | } |
| | 266 | |
| | 267 | /* |
| | 268 | * Given a registration table index for a metaclass and a property |
| | 269 | * ID, retrieve the metaclass function vector index for the property |
| | 270 | * according to the image file's property mapping for the metaclass. |
| | 271 | * Returns zero if the property is not in the mapping, 1 for the |
| | 272 | * first function in the vector, 2 for the second function, and so |
| | 273 | * on. |
| | 274 | * |
| | 275 | * Note that we use the registration table index, NOT the dependency |
| | 276 | * index, because this function is provided for use by the metaclass |
| | 277 | * itself, which only has convenient access to its registration |
| | 278 | * index. |
| | 279 | */ |
| | 280 | int prop_to_vector_idx(uint reg_table_idx, vm_prop_id_t prop) |
| | 281 | { |
| | 282 | vm_meta_entry_t *entry; |
| | 283 | |
| | 284 | /* get the entry for the registration table index */ |
| | 285 | entry = get_entry_from_reg(reg_table_idx); |
| | 286 | |
| | 287 | /* |
| | 288 | * if there's no entry for this registration index, there's |
| | 289 | * obviously no metaclass function mapping for the property |
| | 290 | */ |
| | 291 | if (entry == 0) |
| | 292 | return 0; |
| | 293 | |
| | 294 | /* return the property translation */ |
| | 295 | return entry->xlat_prop(prop); |
| | 296 | } |
| | 297 | |
| | 298 | /* get the entry at a given dependency table index */ |
| | 299 | vm_meta_entry_t *get_entry(int idx) { return &table_[idx]; } |
| | 300 | |
| | 301 | /* get the total number of entries in the table */ |
| | 302 | size_t get_count() const { return count_; } |
| | 303 | |
| | 304 | /* |
| | 305 | * Invoke the VM-stack-based constructor for the metaclass at the |
| | 306 | * given index to create a new instance of that metaclass. Throws |
| | 307 | * an error if no such entry is defined. Returns the object ID of |
| | 308 | * the constructed object. |
| | 309 | */ |
| | 310 | vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, |
| | 311 | uint idx, uint argc); |
| | 312 | |
| | 313 | /* invoke a static property of a metaclass */ |
| | 314 | int call_static_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, |
| | 315 | uint idx, uint *argc, vm_prop_id_t prop); |
| | 316 | |
| | 317 | /* |
| | 318 | * Create an object with the given ID and load the object from the |
| | 319 | * image file. |
| | 320 | */ |
| | 321 | void create_from_image(VMG_ uint idx, vm_obj_id_t id, |
| | 322 | const char *ptr, size_t siz); |
| | 323 | |
| | 324 | /* |
| | 325 | * create an object in preparation for loading the object from a |
| | 326 | * saved state file |
| | 327 | */ |
| | 328 | void create_for_restore(VMG_ uint idx, vm_obj_id_t id); |
| | 329 | |
| | 330 | /* |
| | 331 | * write the dependency table to a file, for later reloading with |
| | 332 | * read_from_file() |
| | 333 | */ |
| | 334 | void write_to_file(class CVmFile *fp); |
| | 335 | |
| | 336 | /* |
| | 337 | * Read the dependency table from a file, as previously written by |
| | 338 | * write_to_file(). Returns zero on success, or an error code on |
| | 339 | * failure. We will entirely clear the current table before |
| | 340 | * loading, so the new table will replace the existing table. |
| | 341 | */ |
| | 342 | int read_from_file(class CVmFile *fp); |
| | 343 | |
| | 344 | /* rebuild the image file (for use after preinitialization) */ |
| | 345 | void rebuild_image(class CVmImageWriter *writer); |
| | 346 | |
| | 347 | /* |
| | 348 | * Create an IntrinsicClass object for each metaclass that doesn't |
| | 349 | * already have an IntrinsicClass object. This ensures that every |
| | 350 | * intrinsic class in use has an associated IntrinsicClass instance, |
| | 351 | * even if the compiler didn't generate one. |
| | 352 | */ |
| | 353 | void create_intrinsic_class_instances(VMG0_); |
| | 354 | |
| | 355 | /* forget IntrinsicClass objects we created at startup */ |
| | 356 | void forget_intrinsic_class_instances(VMG0_); |
| | 357 | |
| | 358 | private: |
| | 359 | /* |
| | 360 | * Ensure we have space for a given number of entries, allocating |
| | 361 | * more if necessary. If we must allocate more space, we'll |
| | 362 | * increase the current allocation size by at least the given |
| | 363 | * increment (more if necessary to bring it up to the required |
| | 364 | * size). |
| | 365 | */ |
| | 366 | void ensure_space(size_t entries, size_t increment); |
| | 367 | |
| | 368 | /* the table array */ |
| | 369 | vm_meta_entry_t *table_; |
| | 370 | |
| | 371 | /* |
| | 372 | * reverse dependency map - each entry in this table is the |
| | 373 | * dependency table index of the metaclass at the given registration |
| | 374 | * table index: |
| | 375 | * |
| | 376 | * reserve_map[registration_table_index] = dependency_table_index |
| | 377 | * |
| | 378 | * This information lets us determine if a given metaclass is in the |
| | 379 | * current dependency table at all, and if so what it's index is. |
| | 380 | * We store -1 in each entry that doesn't appear in the dependency |
| | 381 | * table. |
| | 382 | */ |
| | 383 | int *reverse_map_; |
| | 384 | |
| | 385 | /* number of entries defined in the table */ |
| | 386 | size_t count_; |
| | 387 | |
| | 388 | /* number of entries allocated for the table */ |
| | 389 | size_t alloc_; |
| | 390 | }; |
| | 391 | |
| | 392 | |
| | 393 | #endif /* VMMETA_H */ |