| | 1 | #ifdef RCSID |
| | 2 | static char RCSid[] = |
| | 3 | "$Header: d:/cvsroot/tads/tads3/TCT3IMG.CPP,v 1.1 1999/07/11 00:46:57 MJRoberts Exp $"; |
| | 4 | #endif |
| | 5 | |
| | 6 | /* |
| | 7 | * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. |
| | 8 | * |
| | 9 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 10 | * on using and copying this software. |
| | 11 | */ |
| | 12 | /* |
| | 13 | Name |
| | 14 | tct3.cpp - TADS 3 Compiler - T3 VM Code Generator - image writing functions |
| | 15 | Function |
| | 16 | Image writing routines for the T3-specific code generator |
| | 17 | Notes |
| | 18 | |
| | 19 | Modified |
| | 20 | 05/08/99 MJRoberts - Creation |
| | 21 | */ |
| | 22 | |
| | 23 | #include <stdio.h> |
| | 24 | #include <stdlib.h> |
| | 25 | |
| | 26 | #include "t3std.h" |
| | 27 | #include "os.h" |
| | 28 | #include "tcprs.h" |
| | 29 | #include "tct3.h" |
| | 30 | #include "tcgen.h" |
| | 31 | #include "vmtype.h" |
| | 32 | #include "vmwrtimg.h" |
| | 33 | #include "vmgram.h" |
| | 34 | #include "vmfile.h" |
| | 35 | #include "tcmain.h" |
| | 36 | #include "tcerr.h" |
| | 37 | #include "tcmake.h" |
| | 38 | #include "tctok.h" |
| | 39 | |
| | 40 | |
| | 41 | /* ------------------------------------------------------------------------ */ |
| | 42 | /* |
| | 43 | * Object file signature. The numerical suffix in the first part is the |
| | 44 | * format version number: whenever we make an incompatible change to the |
| | 45 | * format, we'll increment this number so that the linker will recognize an |
| | 46 | * incompatible file format and require a full rebuild. |
| | 47 | */ |
| | 48 | static const char obj_file_sig[] = "TADS3.Object.000E\n\r\032"; |
| | 49 | |
| | 50 | |
| | 51 | /* ------------------------------------------------------------------------ */ |
| | 52 | /* |
| | 53 | * Write an object file. The object file contains the raw byte streams |
| | 54 | * with the generated code; the fixup lists for the streams; the global |
| | 55 | * symbol table; and the function and metaclass dependency lists. |
| | 56 | */ |
| | 57 | void CTcGenTarg::write_to_object_file(CVmFile *fp, CTcMake *) |
| | 58 | { |
| | 59 | ulong flags; |
| | 60 | |
| | 61 | /* write the signature */ |
| | 62 | fp->write_bytes(obj_file_sig, sizeof(obj_file_sig) - 1); |
| | 63 | |
| | 64 | /* compute the object file flags */ |
| | 65 | flags = 0; |
| | 66 | if (G_debug) |
| | 67 | flags |= TCT3_OBJHDR_DEBUG; |
| | 68 | |
| | 69 | /* write the flags */ |
| | 70 | fp->write_int4(flags); |
| | 71 | |
| | 72 | /* write the constant and code pool indivisible object maxima */ |
| | 73 | fp->write_int4(max_str_len_); |
| | 74 | fp->write_int4(max_list_cnt_); |
| | 75 | fp->write_int4(max_bytecode_len_); |
| | 76 | |
| | 77 | /* |
| | 78 | * Write the maximum object and property ID's. When we load this |
| | 79 | * object file, we'll need to generate a translated ID number for |
| | 80 | * each object ID and for each property ID, to translate from the |
| | 81 | * numbering system in the object file to the final image file |
| | 82 | * numbering system. It is helpful if we know early on how many of |
| | 83 | * each there are, so that we can allocate table space accordingly. |
| | 84 | */ |
| | 85 | fp->write_int4(next_obj_); |
| | 86 | fp->write_int4(next_prop_); |
| | 87 | fp->write_int4(G_prs->get_enum_count()); |
| | 88 | |
| | 89 | /* write the function set dependency table */ |
| | 90 | write_funcdep_to_object_file(fp); |
| | 91 | |
| | 92 | /* |
| | 93 | * write the metaclass dependency table - note that we must do this |
| | 94 | * before writing the global symbol table, because upon loading the |
| | 95 | * object file, we must have the dependency table loaded before we |
| | 96 | * can load the symbols (so that any metaclass symbols can be |
| | 97 | * resolved to their dependency table indices) |
| | 98 | */ |
| | 99 | write_metadep_to_object_file(fp); |
| | 100 | |
| | 101 | /* write the global symbol table */ |
| | 102 | G_prs->write_to_object_file(fp); |
| | 103 | |
| | 104 | /* write the main code stream and its fixup list */ |
| | 105 | G_cs_main->write_to_object_file(fp); |
| | 106 | |
| | 107 | /* write the static code stream and its fixup list */ |
| | 108 | G_cs_static->write_to_object_file(fp); |
| | 109 | |
| | 110 | /* write the data stream and its fixup list */ |
| | 111 | G_ds->write_to_object_file(fp); |
| | 112 | |
| | 113 | /* write the object stream and its fixup list */ |
| | 114 | G_os->write_to_object_file(fp); |
| | 115 | |
| | 116 | /* write the intrinsic class modifier stream */ |
| | 117 | G_icmod_stream->write_to_object_file(fp); |
| | 118 | |
| | 119 | /* write the BigNumber stream and its fixup list */ |
| | 120 | G_bignum_stream->write_to_object_file(fp); |
| | 121 | |
| | 122 | /* write the static initializer ID stream */ |
| | 123 | G_static_init_id_stream->write_to_object_file(fp); |
| | 124 | |
| | 125 | /* write the object ID fixup list */ |
| | 126 | CTcIdFixup::write_to_object_file(fp, G_objfixup); |
| | 127 | |
| | 128 | /* write the property ID fixup list */ |
| | 129 | CTcIdFixup::write_to_object_file(fp, G_propfixup); |
| | 130 | |
| | 131 | /* write the enumerator ID fixup list */ |
| | 132 | CTcIdFixup::write_to_object_file(fp, G_enumfixup); |
| | 133 | |
| | 134 | /* write debugging information if we're compiling for the debugger */ |
| | 135 | if (G_debug) |
| | 136 | { |
| | 137 | tct3_debug_line_page *pg; |
| | 138 | |
| | 139 | /* write the source file list */ |
| | 140 | write_sources_to_object_file(fp); |
| | 141 | |
| | 142 | /* |
| | 143 | * Write the pointers to the debug line records in the code |
| | 144 | * stream, so that we can fix up the line records on re-loading |
| | 145 | * the object file (they'll need to be adjusted for the new |
| | 146 | * numbering system for the source file descriptors). First, |
| | 147 | * write the total number of pointers. |
| | 148 | */ |
| | 149 | fp->write_int4(debug_line_cnt_); |
| | 150 | |
| | 151 | /* now write the pointers, one page at a time */ |
| | 152 | for (pg = debug_line_head_ ; pg != 0 ; pg = pg->nxt) |
| | 153 | { |
| | 154 | size_t pgcnt; |
| | 155 | |
| | 156 | /* |
| | 157 | * if this is the last entry, it might only be partially |
| | 158 | * full; otherwise, it's completely full, because we always |
| | 159 | * fill a page before allocating a new one |
| | 160 | */ |
| | 161 | if (pg->nxt == 0) |
| | 162 | pgcnt = (size_t)(debug_line_cnt_ % TCT3_DEBUG_LINE_PAGE_SIZE); |
| | 163 | else |
| | 164 | pgcnt = TCT3_DEBUG_LINE_PAGE_SIZE; |
| | 165 | |
| | 166 | /* |
| | 167 | * Write the data - we prepared it in portable format, so we |
| | 168 | * can just copy it directly to the file. Note that each |
| | 169 | * entry is four bytes. |
| | 170 | */ |
| | 171 | fp->write_bytes((char *)pg->line_ofs, |
| | 172 | pgcnt * TCT3_DEBUG_LINE_REC_SIZE); |
| | 173 | } |
| | 174 | |
| | 175 | /* write the #define symbols */ |
| | 176 | G_tok->write_macros_to_file_for_debug(fp); |
| | 177 | } |
| | 178 | } |
| | 179 | |
| | 180 | /* ------------------------------------------------------------------------ */ |
| | 181 | /* |
| | 182 | * Write the function-set dependency table to an object file |
| | 183 | */ |
| | 184 | void CTcGenTarg::write_funcdep_to_object_file(CVmFile *fp) |
| | 185 | { |
| | 186 | tc_fnset_entry *cur; |
| | 187 | |
| | 188 | /* write the count */ |
| | 189 | fp->write_int2(fnset_cnt_); |
| | 190 | |
| | 191 | /* write the entries */ |
| | 192 | for (cur = fnset_head_ ; cur != 0 ; cur = cur->nxt) |
| | 193 | { |
| | 194 | size_t len; |
| | 195 | |
| | 196 | len = strlen(cur->nm); |
| | 197 | fp->write_int2(len); |
| | 198 | fp->write_bytes(cur->nm, len); |
| | 199 | } |
| | 200 | } |
| | 201 | |
| | 202 | /* |
| | 203 | * Write the metaclass dependency table to an object file |
| | 204 | */ |
| | 205 | void CTcGenTarg::write_metadep_to_object_file(CVmFile *fp) |
| | 206 | { |
| | 207 | tc_meta_entry *cur; |
| | 208 | |
| | 209 | /* write the count */ |
| | 210 | fp->write_int2(meta_cnt_); |
| | 211 | |
| | 212 | /* write the entries */ |
| | 213 | for (cur = meta_head_ ; cur != 0 ; cur = cur->nxt) |
| | 214 | { |
| | 215 | size_t len; |
| | 216 | |
| | 217 | len = strlen(cur->nm); |
| | 218 | fp->write_int2(len); |
| | 219 | fp->write_bytes(cur->nm, len); |
| | 220 | } |
| | 221 | } |
| | 222 | |
| | 223 | |
| | 224 | /* ------------------------------------------------------------------------ */ |
| | 225 | /* |
| | 226 | * Load an object file. We'll read the file, load its data into memory |
| | 227 | * (creating global symbol table entries and writing to the code and |
| | 228 | * data streams), fix up the fixups to the new base offsets in the |
| | 229 | * streams, and translate object and property ID values from the object |
| | 230 | * file numbering system to our in-memory numbering system (which will |
| | 231 | * usually differ after more than one object file is loaded, because the |
| | 232 | * numbering systems in the different files must be reconciled). |
| | 233 | * |
| | 234 | * Returns zero on success; logs errors and returns non-zero on error. |
| | 235 | */ |
| | 236 | int CTcGenTarg::load_object_file(CVmFile *fp, const textchar_t *fname) |
| | 237 | { |
| | 238 | char buf[128]; |
| | 239 | ulong obj_cnt; |
| | 240 | ulong prop_cnt; |
| | 241 | ulong enum_cnt; |
| | 242 | vm_obj_id_t *obj_xlat = 0; |
| | 243 | vm_prop_id_t *prop_xlat = 0; |
| | 244 | ulong *enum_xlat = 0; |
| | 245 | int err; |
| | 246 | ulong hdr_flags; |
| | 247 | ulong siz; |
| | 248 | ulong main_cs_start_ofs; |
| | 249 | ulong static_cs_start_ofs; |
| | 250 | |
| | 251 | /* |
| | 252 | * Before loading anything from the file, go through all of the |
| | 253 | * streams and set their object file base offset. All stream |
| | 254 | * offsets that we read from the object file will be relative to the |
| | 255 | * these values, since the object file stream data will be loaded in |
| | 256 | * after any data currently in the streams. |
| | 257 | */ |
| | 258 | G_cs_main->set_object_file_start_ofs(); |
| | 259 | G_cs_static->set_object_file_start_ofs(); |
| | 260 | G_ds->set_object_file_start_ofs(); |
| | 261 | G_os->set_object_file_start_ofs(); |
| | 262 | G_icmod_stream->set_object_file_start_ofs(); |
| | 263 | G_bignum_stream->set_object_file_start_ofs(); |
| | 264 | G_static_init_id_stream->set_object_file_start_ofs(); |
| | 265 | |
| | 266 | /* |
| | 267 | * note the main code stream's start offset, since we'll need this |
| | 268 | * later in order to process the debug line records; likewise, note |
| | 269 | * the static stream's start offset |
| | 270 | */ |
| | 271 | main_cs_start_ofs = G_cs_main->get_ofs(); |
| | 272 | static_cs_start_ofs = G_cs_static->get_ofs(); |
| | 273 | |
| | 274 | /* read the signature, and make sure it matches */ |
| | 275 | fp->read_bytes(buf, sizeof(obj_file_sig) - 1); |
| | 276 | if (memcmp(buf, obj_file_sig, sizeof(obj_file_sig) - 1) != 0) |
| | 277 | { |
| | 278 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_INV_SIG); |
| | 279 | return 1; |
| | 280 | } |
| | 281 | |
| | 282 | /* read the file header flags */ |
| | 283 | hdr_flags = fp->read_uint4(); |
| | 284 | |
| | 285 | /* |
| | 286 | * If we're linking with debugging information, but this object file |
| | 287 | * wasn't compiled with debugging information, we won't be able to |
| | 288 | * produce a complete debuggable image - log an error to that |
| | 289 | * effect. |
| | 290 | */ |
| | 291 | if (G_debug && (hdr_flags & TCT3_OBJHDR_DEBUG) == 0) |
| | 292 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 293 | TCERR_OBJFILE_NO_DBG, fname); |
| | 294 | |
| | 295 | /* |
| | 296 | * Read the constant and code pool indivisible object maxima. Note |
| | 297 | * each maximum that exceeds the current maximum, since we must keep |
| | 298 | * track of the largest indivisible object of each type in the |
| | 299 | * entire program. |
| | 300 | */ |
| | 301 | |
| | 302 | /* read and note the maximum string constant length */ |
| | 303 | siz = fp->read_uint4(); |
| | 304 | if (siz > max_str_len_) |
| | 305 | max_str_len_ = siz; |
| | 306 | |
| | 307 | /* read and note the maximum list size */ |
| | 308 | siz = fp->read_uint4(); |
| | 309 | if (siz > max_list_cnt_) |
| | 310 | max_list_cnt_ = siz; |
| | 311 | |
| | 312 | /* read and note the maximum code pool object size */ |
| | 313 | siz = fp->read_uint4(); |
| | 314 | if (siz > max_bytecode_len_) |
| | 315 | max_bytecode_len_ = siz; |
| | 316 | |
| | 317 | /* |
| | 318 | * read the object, property, and enumerator ID counts from the file |
| | 319 | * - these give the highest ID values that are assigned anywhere in |
| | 320 | * the object file's numbering system |
| | 321 | */ |
| | 322 | obj_cnt = fp->read_uint4(); |
| | 323 | prop_cnt = fp->read_uint4(); |
| | 324 | enum_cnt = fp->read_uint4(); |
| | 325 | |
| | 326 | /* |
| | 327 | * Allocate object and property ID translation tables. These are |
| | 328 | * simply arrays of ID's. Each element of an array gives the global |
| | 329 | * ID number assigned to the object whose local ID is the array |
| | 330 | * index. So, obj_xlat[local_id] = global_id. We need one element |
| | 331 | * in the object ID translation array for each local ID in the |
| | 332 | * object file, which is obj_cnt; likewise for properties and |
| | 333 | * prop_cnt. |
| | 334 | * |
| | 335 | * We're being a bit lazy here by using flat arrays. This could be |
| | 336 | * a problem for very large object files on 16-bit machines: if a |
| | 337 | * single object file has more than 16k object ID's (which means |
| | 338 | * that it defines and imports more than 16k unique objects), or |
| | 339 | * more than 32k property ID's, we'll go over the 64k allocation |
| | 340 | * limit on these machines. This seems unlikely to become a problem |
| | 341 | * in practice, but to ensure a graceful failure in such cases, |
| | 342 | * check the allocation requirement to make sure we don't go over |
| | 343 | * the present platform's architectural limits. |
| | 344 | */ |
| | 345 | if (obj_cnt * sizeof(obj_xlat[0]) > OSMALMAX |
| | 346 | || prop_cnt * sizeof(prop_xlat[0]) > OSMALMAX |
| | 347 | || enum_cnt * sizeof(enum_xlat[0]) > OSMALMAX) |
| | 348 | { |
| | 349 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_TOO_MANY_IDS); |
| | 350 | return 2; |
| | 351 | } |
| | 352 | |
| | 353 | /* allocate the translation arrays */ |
| | 354 | obj_xlat = (vm_obj_id_t *) |
| | 355 | t3malloc((size_t)(obj_cnt * sizeof(obj_xlat[0]))); |
| | 356 | prop_xlat = (vm_prop_id_t *) |
| | 357 | t3malloc((size_t)(prop_cnt * sizeof(prop_xlat[0]))); |
| | 358 | enum_xlat = (ulong *) |
| | 359 | t3malloc((size_t)(enum_cnt * sizeof(enum_xlat[0]))); |
| | 360 | |
| | 361 | /* check to make sure we got the memory */ |
| | 362 | if (obj_xlat == 0 || prop_xlat == 0 || enum_xlat == 0) |
| | 363 | { |
| | 364 | /* log an error and return failure */ |
| | 365 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_OUT_OF_MEM); |
| | 366 | err = 3; |
| | 367 | goto done; |
| | 368 | } |
| | 369 | |
| | 370 | /* |
| | 371 | * Clear out the translation arrays initially. We should, in the |
| | 372 | * course of loading the symbol table, assign a translation value |
| | 373 | * for every entry. If anything is left at zero (which is invalid |
| | 374 | * as an object or property ID), something must be wrong. |
| | 375 | */ |
| | 376 | memset(obj_xlat, 0, (size_t)(obj_cnt * sizeof(obj_xlat[0]))); |
| | 377 | memset(prop_xlat, 0, (size_t)(prop_cnt * sizeof(prop_xlat[0]))); |
| | 378 | memset(enum_xlat, 0, (size_t)(enum_cnt * sizeof(enum_xlat[0]))); |
| | 379 | |
| | 380 | /* read the function set dependency table */ |
| | 381 | load_funcdep_from_object_file(fp, fname); |
| | 382 | |
| | 383 | /* read the metaclass dependency table */ |
| | 384 | load_metadep_from_object_file(fp, fname); |
| | 385 | |
| | 386 | /* |
| | 387 | * Read the symbol table. This will create translation entries for |
| | 388 | * the object and property names found in the symbol table. |
| | 389 | */ |
| | 390 | if ((err = G_prs->load_object_file(fp, fname, obj_xlat, prop_xlat, |
| | 391 | enum_xlat)) != 0) |
| | 392 | { |
| | 393 | /* that failed - abort the load */ |
| | 394 | goto done; |
| | 395 | } |
| | 396 | |
| | 397 | /* read the main code stream and its fixup list */ |
| | 398 | G_cs_main->load_object_file(fp, fname); |
| | 399 | |
| | 400 | /* read the static code stream and its fixup list */ |
| | 401 | G_cs_static->load_object_file(fp, fname); |
| | 402 | |
| | 403 | /* read the data stream and its fixup list */ |
| | 404 | G_ds->load_object_file(fp, fname); |
| | 405 | |
| | 406 | /* read the object data stream and its fixup list */ |
| | 407 | G_os->load_object_file(fp, fname); |
| | 408 | |
| | 409 | /* read the intrinsic class modifier stream */ |
| | 410 | G_icmod_stream->load_object_file(fp, fname); |
| | 411 | |
| | 412 | /* read the BigNumber stream and its fixup list */ |
| | 413 | G_bignum_stream->load_object_file(fp, fname); |
| | 414 | |
| | 415 | /* read the static initializer ID stream */ |
| | 416 | G_static_init_id_stream->load_object_file(fp, fname); |
| | 417 | |
| | 418 | /* read the object ID fixup list */ |
| | 419 | CTcIdFixup::load_object_file(fp, obj_xlat, obj_cnt, TCGEN_XLAT_OBJ, 4, |
| | 420 | fname, G_keep_objfixups ? &G_objfixup : 0); |
| | 421 | |
| | 422 | /* read the property ID fixup list */ |
| | 423 | CTcIdFixup::load_object_file(fp, prop_xlat, prop_cnt, TCGEN_XLAT_PROP, 2, |
| | 424 | fname, G_keep_propfixups ? &G_propfixup : 0); |
| | 425 | |
| | 426 | /* read the enum ID fixup list */ |
| | 427 | CTcIdFixup::load_object_file(fp, enum_xlat, enum_cnt, TCGEN_XLAT_ENUM, 2, |
| | 428 | fname, G_keep_enumfixups ? &G_enumfixup : 0); |
| | 429 | |
| | 430 | /* if the object file contains debugging information, read that */ |
| | 431 | if ((hdr_flags & TCT3_OBJHDR_DEBUG) != 0) |
| | 432 | { |
| | 433 | /* load the debug records */ |
| | 434 | load_debug_records_from_object_file(fp, fname, |
| | 435 | main_cs_start_ofs, |
| | 436 | static_cs_start_ofs); |
| | 437 | } |
| | 438 | |
| | 439 | done: |
| | 440 | /* |
| | 441 | * free the ID translation arrays - we no longer need them after |
| | 442 | * loading the object file, because we translate everything in the |
| | 443 | * course of loading, so what's left in memory when we're done uses |
| | 444 | * the new global numbering system |
| | 445 | */ |
| | 446 | if (obj_xlat != 0) |
| | 447 | t3free(obj_xlat); |
| | 448 | if (prop_xlat != 0) |
| | 449 | t3free(prop_xlat); |
| | 450 | if (enum_xlat != 0) |
| | 451 | t3free(enum_xlat); |
| | 452 | |
| | 453 | /* return the result */ |
| | 454 | return err; |
| | 455 | } |
| | 456 | |
| | 457 | |
| | 458 | /* ------------------------------------------------------------------------ */ |
| | 459 | /* |
| | 460 | * Error handler for CTcTokenizer::load_macros_from_file() |
| | 461 | */ |
| | 462 | class MyLoadMacErr: public CTcTokLoadMacErr |
| | 463 | { |
| | 464 | public: |
| | 465 | MyLoadMacErr(const char *fname) { fname_ = fname; } |
| | 466 | |
| | 467 | /* log an error */ |
| | 468 | virtual void log_error(int err) |
| | 469 | { |
| | 470 | /* check the error code */ |
| | 471 | switch(err) |
| | 472 | { |
| | 473 | case 1: |
| | 474 | case 2: |
| | 475 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 476 | TCERR_OBJFILE_MACRO_SYM_TOO_LONG, fname_); |
| | 477 | break; |
| | 478 | } |
| | 479 | } |
| | 480 | |
| | 481 | private: |
| | 482 | /* the name of the object file we're loading */ |
| | 483 | const char *fname_; |
| | 484 | }; |
| | 485 | |
| | 486 | /* ------------------------------------------------------------------------ */ |
| | 487 | /* |
| | 488 | * Load the debug records from an object file |
| | 489 | */ |
| | 490 | void CTcGenTarg::load_debug_records_from_object_file( |
| | 491 | CVmFile *fp, const textchar_t *fname, |
| | 492 | ulong main_cs_start_ofs, ulong static_cs_start_ofs) |
| | 493 | { |
| | 494 | int first_filedesc; |
| | 495 | ulong line_table_cnt; |
| | 496 | |
| | 497 | /* |
| | 498 | * Note the starting number of our file descriptors - in the file, |
| | 499 | * we started numbering them at zero, but if we have already loaded |
| | 500 | * other object files before this one, we'll be numbering ours after |
| | 501 | * the ones previously loaded. So, we'll need to fix up the |
| | 502 | * references to the file descriptor indices accordingly. |
| | 503 | */ |
| | 504 | first_filedesc = G_tok->get_next_filedesc_index(); |
| | 505 | |
| | 506 | /* read the source file list */ |
| | 507 | read_sources_from_object_file(fp); |
| | 508 | |
| | 509 | /* |
| | 510 | * Read the line record pointers. For each line record table, we |
| | 511 | * must fix up the line records to reflect the file descriptor |
| | 512 | * numbering system. |
| | 513 | */ |
| | 514 | for (line_table_cnt = fp->read_uint4() ; line_table_cnt != 0 ; |
| | 515 | --line_table_cnt) |
| | 516 | { |
| | 517 | uchar stream_id; |
| | 518 | ulong ofs; |
| | 519 | CTcCodeStream *cs; |
| | 520 | ulong start_ofs; |
| | 521 | |
| | 522 | /* read the stream ID */ |
| | 523 | stream_id = fp->read_byte(); |
| | 524 | |
| | 525 | /* find the appropriate code stream */ |
| | 526 | cs = (CTcCodeStream *) |
| | 527 | CTcDataStream::get_stream_from_id(stream_id, fname); |
| | 528 | |
| | 529 | /* get the appropriate starting offset */ |
| | 530 | start_ofs = (cs == G_cs_main ? main_cs_start_ofs |
| | 531 | : static_cs_start_ofs); |
| | 532 | |
| | 533 | /* |
| | 534 | * Read the next line table offset - this is the offset in the |
| | 535 | * code stream of the next debug line table. Add our starting |
| | 536 | * offset to get the true offset. |
| | 537 | */ |
| | 538 | ofs = fp->read_uint4() + start_ofs; |
| | 539 | |
| | 540 | /* update this table */ |
| | 541 | fix_up_debug_line_table(cs, ofs, first_filedesc); |
| | 542 | } |
| | 543 | |
| | 544 | /* read the macro definitions */ |
| | 545 | CVmFileStream str(fp); |
| | 546 | MyLoadMacErr err_handler(fname); |
| | 547 | G_tok->load_macros_from_file(&str, &err_handler); |
| | 548 | } |
| | 549 | |
| | 550 | /* |
| | 551 | * Fix up a debug line record table for the current object file |
| | 552 | */ |
| | 553 | void CTcGenTarg::fix_up_debug_line_table(CTcCodeStream *cs, |
| | 554 | ulong line_table_ofs, |
| | 555 | int first_filedesc) |
| | 556 | { |
| | 557 | uint cnt; |
| | 558 | ulong ofs; |
| | 559 | |
| | 560 | /* read the number of line records in the table */ |
| | 561 | cnt = cs->readu2_at(line_table_ofs); |
| | 562 | |
| | 563 | /* adjust each entry */ |
| | 564 | for (ofs = line_table_ofs + 2 ; cnt != 0 ; |
| | 565 | --cnt, ofs += TCT3_LINE_ENTRY_SIZE) |
| | 566 | { |
| | 567 | uint filedesc; |
| | 568 | |
| | 569 | /* read the old file descriptor ID */ |
| | 570 | filedesc = cs->readu2_at(ofs + 2); |
| | 571 | |
| | 572 | /* adjust it to the new numbering system */ |
| | 573 | filedesc += first_filedesc; |
| | 574 | |
| | 575 | /* write it back */ |
| | 576 | cs->write2_at(ofs + 2, filedesc); |
| | 577 | } |
| | 578 | } |
| | 579 | |
| | 580 | /* ------------------------------------------------------------------------ */ |
| | 581 | /* |
| | 582 | * Load a function set dependency table from the object file. We can |
| | 583 | * add to the existing set of functions, but if we have N function sets |
| | 584 | * defined already, the first N in the file must match the ones we have |
| | 585 | * loaded exactly. |
| | 586 | */ |
| | 587 | void CTcGenTarg::load_funcdep_from_object_file(class CVmFile *fp, |
| | 588 | const textchar_t *fname) |
| | 589 | { |
| | 590 | int cnt; |
| | 591 | tc_fnset_entry *cur; |
| | 592 | |
| | 593 | /* read the count */ |
| | 594 | cnt = fp->read_int2(); |
| | 595 | |
| | 596 | /* read the entries */ |
| | 597 | for (cur = fnset_head_ ; cnt != 0 ; --cnt) |
| | 598 | { |
| | 599 | char buf[128]; |
| | 600 | size_t len; |
| | 601 | |
| | 602 | /* read this entry */ |
| | 603 | len = fp->read_uint2(); |
| | 604 | if (len + 1 > sizeof(buf)) |
| | 605 | { |
| | 606 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 607 | TCERR_OBJFILE_INV_FN_OR_META, fname); |
| | 608 | return; |
| | 609 | } |
| | 610 | |
| | 611 | /* read the name and null-terminate it */ |
| | 612 | fp->read_bytes(buf, len); |
| | 613 | buf[len] = '\0'; |
| | 614 | |
| | 615 | /* |
| | 616 | * if we are still scanning existing entries, make sure it |
| | 617 | * matches; otherwise, add it |
| | 618 | */ |
| | 619 | if (cur != 0) |
| | 620 | { |
| | 621 | const char *vsn; |
| | 622 | char *ent_vsn; |
| | 623 | size_t name_len; |
| | 624 | size_t ent_name_len; |
| | 625 | |
| | 626 | /* get the version suffixes, if any */ |
| | 627 | vsn = lib_find_vsn_suffix(buf, '/', 0, &name_len); |
| | 628 | ent_vsn = (char *) |
| | 629 | lib_find_vsn_suffix(cur->nm, '/', 0, &ent_name_len); |
| | 630 | |
| | 631 | /* if it doesn't match, it's an error */ |
| | 632 | if (name_len != ent_name_len |
| | 633 | || memcmp(cur->nm, buf, name_len) != 0) |
| | 634 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 635 | TCERR_OBJFILE_FNSET_CONFLICT, |
| | 636 | buf, fname); |
| | 637 | |
| | 638 | /* |
| | 639 | * if the new version string is higher than the old version |
| | 640 | * string, keep the new version string |
| | 641 | */ |
| | 642 | if (vsn != 0 && ent_vsn != 0 && strcmp(vsn, ent_vsn) > 0 |
| | 643 | && strlen(vsn) <= strlen(ent_vsn)) |
| | 644 | { |
| | 645 | /* |
| | 646 | * the new version is newer than the version in the |
| | 647 | * table - overwrite the table version with the new |
| | 648 | * version, so that the table keeps the newest version |
| | 649 | * mentioned anywhere (newer versions are upwardly |
| | 650 | * compatible with older versions, so the code that uses |
| | 651 | * the older version will be equally happy with the |
| | 652 | * newer version) |
| | 653 | */ |
| | 654 | strcpy(ent_vsn, vsn); |
| | 655 | } |
| | 656 | |
| | 657 | /* move on to the next one */ |
| | 658 | cur = cur->nxt; |
| | 659 | } |
| | 660 | else |
| | 661 | { |
| | 662 | /* we're past the existing list - add the new function set */ |
| | 663 | add_fnset(buf, len); |
| | 664 | } |
| | 665 | } |
| | 666 | } |
| | 667 | |
| | 668 | /* |
| | 669 | * Load a metaclass dependency table from the object file. We can add |
| | 670 | * to the existing set of metaclasses, but if we have N metaclasses |
| | 671 | * defined already, the first N in the file must match the ones we have |
| | 672 | * loaded exactly. |
| | 673 | */ |
| | 674 | void CTcGenTarg::load_metadep_from_object_file(class CVmFile *fp, |
| | 675 | const textchar_t *fname) |
| | 676 | { |
| | 677 | int cnt; |
| | 678 | tc_meta_entry *cur; |
| | 679 | |
| | 680 | /* read the count */ |
| | 681 | cnt = fp->read_int2(); |
| | 682 | |
| | 683 | /* read the entries */ |
| | 684 | for (cur = meta_head_ ; cnt != 0 ; --cnt) |
| | 685 | { |
| | 686 | char buf[128]; |
| | 687 | size_t len; |
| | 688 | |
| | 689 | /* read this entry */ |
| | 690 | len = fp->read_uint2(); |
| | 691 | if (len + 1 > sizeof(buf)) |
| | 692 | { |
| | 693 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 694 | TCERR_OBJFILE_INV_FN_OR_META, fname); |
| | 695 | return; |
| | 696 | } |
| | 697 | |
| | 698 | /* read the name and null-terminate it */ |
| | 699 | fp->read_bytes(buf, len); |
| | 700 | buf[len] = '\0'; |
| | 701 | |
| | 702 | /* |
| | 703 | * if we are still scanning existing entries, make sure it |
| | 704 | * matches; otherwise, add it |
| | 705 | */ |
| | 706 | if (cur != 0) |
| | 707 | { |
| | 708 | const char *vsn; |
| | 709 | char *ent_vsn; |
| | 710 | size_t name_len; |
| | 711 | size_t ent_name_len; |
| | 712 | |
| | 713 | /* find the version suffix, if any */ |
| | 714 | vsn = lib_find_vsn_suffix(buf, '/', 0, &name_len); |
| | 715 | |
| | 716 | /* find the version suffix in this entry's name */ |
| | 717 | ent_vsn = (char *) |
| | 718 | lib_find_vsn_suffix(cur->nm, '/', 0, &ent_name_len); |
| | 719 | |
| | 720 | /* if it doesn't match the entry name, it's an error */ |
| | 721 | if (name_len != ent_name_len |
| | 722 | || memcmp(cur->nm, buf, name_len) != 0) |
| | 723 | { |
| | 724 | /* log a mis-matched metaclass error */ |
| | 725 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 726 | TCERR_OBJFILE_META_CONFLICT, buf, fname); |
| | 727 | } |
| | 728 | |
| | 729 | /* |
| | 730 | * if the new version string is higher than the old version |
| | 731 | * string, keep the new version string |
| | 732 | */ |
| | 733 | if (vsn != 0 && ent_vsn != 0 && strcmp(vsn, ent_vsn) > 0 |
| | 734 | && strlen(vsn) <= strlen(ent_vsn)) |
| | 735 | { |
| | 736 | /* |
| | 737 | * the new version is newer than the version in the |
| | 738 | * table - overwrite the table version with the new |
| | 739 | * version, so that the table keeps the newest version |
| | 740 | * mentioned anywhere (newer versions are upwardly |
| | 741 | * compatible with older versions, so the code that uses |
| | 742 | * the older version will be equally happy with the |
| | 743 | * newer version) |
| | 744 | */ |
| | 745 | strcpy(ent_vsn, vsn); |
| | 746 | } |
| | 747 | |
| | 748 | /* move on to the next one */ |
| | 749 | cur = cur->nxt; |
| | 750 | } |
| | 751 | else |
| | 752 | { |
| | 753 | /* we're past the existing list - add the new metaclass */ |
| | 754 | add_meta(buf, len, 0); |
| | 755 | } |
| | 756 | } |
| | 757 | } |
| | 758 | |
| | 759 | |
| | 760 | /* ------------------------------------------------------------------------ */ |
| | 761 | /* |
| | 762 | * Write the source file list to an object file |
| | 763 | */ |
| | 764 | void CTcGenTarg::write_sources_to_object_file(CVmFile *fp) |
| | 765 | { |
| | 766 | CTcTokFileDesc *desc; |
| | 767 | |
| | 768 | /* write the number of entries */ |
| | 769 | fp->write_int2(G_tok->get_filedesc_count()); |
| | 770 | |
| | 771 | /* write the entries */ |
| | 772 | for (desc = G_tok->get_first_filedesc() ; desc != 0 ; |
| | 773 | desc = desc->get_next()) |
| | 774 | { |
| | 775 | size_t len; |
| | 776 | const char *fname; |
| | 777 | |
| | 778 | /* get the filename - use the resolved local filename */ |
| | 779 | fname = desc->get_fname(); |
| | 780 | |
| | 781 | /* write the length of the filename */ |
| | 782 | len = strlen(fname); |
| | 783 | fp->write_int2(len); |
| | 784 | |
| | 785 | /* write the filename */ |
| | 786 | fp->write_bytes(fname, len); |
| | 787 | } |
| | 788 | } |
| | 789 | |
| | 790 | /* |
| | 791 | * Read a source file list from an object file |
| | 792 | */ |
| | 793 | void CTcGenTarg::read_sources_from_object_file(CVmFile *fp) |
| | 794 | { |
| | 795 | uint cnt; |
| | 796 | uint i; |
| | 797 | |
| | 798 | /* read the number of entries */ |
| | 799 | cnt = fp->read_uint2(); |
| | 800 | |
| | 801 | /* read the entries */ |
| | 802 | for (i = 0 ; i < cnt ; ++i) |
| | 803 | { |
| | 804 | size_t len; |
| | 805 | char fname[OSFNMAX]; |
| | 806 | |
| | 807 | /* read the length of the entry */ |
| | 808 | len = fp->read_uint2(); |
| | 809 | |
| | 810 | /* see if it fits in our buffer */ |
| | 811 | if (len <= sizeof(fname)) |
| | 812 | { |
| | 813 | /* read it */ |
| | 814 | fp->read_bytes(fname, len); |
| | 815 | } |
| | 816 | else |
| | 817 | { |
| | 818 | /* it's too long - truncate to the buffer size */ |
| | 819 | fp->read_bytes(fname, sizeof(fname)); |
| | 820 | |
| | 821 | /* skip the rest */ |
| | 822 | fp->set_pos(fp->get_pos() + len - sizeof(fname)); |
| | 823 | |
| | 824 | /* note the truncated length */ |
| | 825 | len = sizeof(fname); |
| | 826 | } |
| | 827 | |
| | 828 | /* |
| | 829 | * Add it to the tokenizer list. Always create a new entry, |
| | 830 | * rather than re-using an existing entry. When loading |
| | 831 | * multiple object files, this might result in the same file |
| | 832 | * appearing as multiple different descriptors, but it's a small |
| | 833 | * price to pay (it doesn't add too much redundant space to the |
| | 834 | * image file, and in any case the information is only retained |
| | 835 | * when we're compiling for debugging) for a big gain in |
| | 836 | * simplicity (the source references in the object file can be |
| | 837 | * fixed up simply by adding the object file's base index to all |
| | 838 | * of the reference indices). |
| | 839 | */ |
| | 840 | G_tok->create_file_desc(fname, len); |
| | 841 | } |
| | 842 | } |
| | 843 | |
| | 844 | /* ------------------------------------------------------------------------ */ |
| | 845 | /* |
| | 846 | * Calculate pool layouts. This is called at the start of the link |
| | 847 | * phase: at this point, we know the sizes of the largest constant pool |
| | 848 | * and code pool objects, so we can figure the layouts of the pools. |
| | 849 | */ |
| | 850 | void CTcGenTarg::calc_pool_layouts(size_t *first_static_page) |
| | 851 | { |
| | 852 | size_t max_str; |
| | 853 | size_t max_list; |
| | 854 | size_t max_item; |
| | 855 | |
| | 856 | /* |
| | 857 | * We've parsed the entire program, so we now know the lengths of |
| | 858 | * the longest string constant and the longest list constant. From |
| | 859 | * this, we can figure the size of our constant pool pages: since |
| | 860 | * each list or string must be contained entirely in a single page, |
| | 861 | * the minimum page size is the length of the longest string or list. |
| | 862 | * |
| | 863 | * We must pick a power of two for our page size. We don't want to |
| | 864 | * make the page size too small; each page requires a small amount |
| | 865 | * of overhead, hence the more pages for a given total constant pool |
| | 866 | * size, the more overhead. We also don't want to make the page |
| | 867 | * size too large, because smaller page sizes will give us better |
| | 868 | * performance on small machines that will have to swap pages in and |
| | 869 | * out (the smaller a page, the less time it will take to load a |
| | 870 | * page). |
| | 871 | * |
| | 872 | * Start at 2k, which is big enough that the data part will |
| | 873 | * overwhelm the per-page overhead, but small enough that it can be |
| | 874 | * loaded quickly on a small machine. If that's at least twice the |
| | 875 | * length of the longest string or list, use it; otherwise, double |
| | 876 | * it and try again. |
| | 877 | */ |
| | 878 | |
| | 879 | /* |
| | 880 | * find the length in bytes of the longest string - we require the |
| | 881 | * length prefix in addition to the bytes of the string |
| | 882 | */ |
| | 883 | max_str = max_str_len_ + VMB_LEN; |
| | 884 | |
| | 885 | /* |
| | 886 | * find the length in bytes of the longest list - we require one |
| | 887 | * data holder per element, plus the length prefix |
| | 888 | */ |
| | 889 | max_list = (max_list_cnt_ * VMB_DATAHOLDER) + VMB_LEN; |
| | 890 | |
| | 891 | /* get the larger of the two - this will be our minimum size */ |
| | 892 | max_item = max_str; |
| | 893 | if (max_list > max_item) |
| | 894 | max_item = max_list; |
| | 895 | |
| | 896 | /* |
| | 897 | * if the maximum item size is under 16k, look for a size that will |
| | 898 | * hold twice the maximum item size; otherwise, relax this |
| | 899 | * requirement, since the pages are getting big, and look for |
| | 900 | * something that merely fits the largest element |
| | 901 | */ |
| | 902 | if (max_item < 16*1024) |
| | 903 | max_item <<= 1; |
| | 904 | |
| | 905 | /* calculate the constant pool layout */ |
| | 906 | const_layout_.calc_layout(G_ds, max_item, TRUE); |
| | 907 | |
| | 908 | /* calculate the main code pool layout */ |
| | 909 | code_layout_.calc_layout(G_cs_main, max_bytecode_len_, TRUE); |
| | 910 | |
| | 911 | /* note the number of pages of regular code */ |
| | 912 | *first_static_page = code_layout_.page_cnt_; |
| | 913 | |
| | 914 | /* |
| | 915 | * add the static pool into the code pool layout, since we'll |
| | 916 | * ultimately write the static code as part of the plain code pages |
| | 917 | */ |
| | 918 | code_layout_.calc_layout(G_cs_static, max_bytecode_len_, FALSE); |
| | 919 | } |
| | 920 | |
| | 921 | |
| | 922 | /* ------------------------------------------------------------------------ */ |
| | 923 | /* |
| | 924 | * Write the image file |
| | 925 | */ |
| | 926 | void CTcGenTarg::write_to_image(CVmFile *fp, uchar data_xor_mask, |
| | 927 | const char tool_data[4]) |
| | 928 | { |
| | 929 | tc_meta_entry *meta; |
| | 930 | CTcSymbol *sym; |
| | 931 | unsigned long main_ofs; |
| | 932 | vm_prop_id_t construct_prop = VM_INVALID_PROP; |
| | 933 | vm_prop_id_t finalize_prop = VM_INVALID_PROP; |
| | 934 | vm_prop_id_t objcall_prop = VM_INVALID_PROP; |
| | 935 | tc_fnset_entry *fnset; |
| | 936 | CVmImageWriter *image_writer; |
| | 937 | int bignum_idx; |
| | 938 | int int_class_idx; |
| | 939 | CTcPrsExport *exp; |
| | 940 | CTcDataStream *cs_list[2]; |
| | 941 | size_t first_static_code_page; |
| | 942 | |
| | 943 | /* |
| | 944 | * if we have any BigNumber data, get the BigNumber metaclass index |
| | 945 | * (or define it, if the program didn't do so itself) |
| | 946 | */ |
| | 947 | if (G_bignum_stream->get_ofs() != 0) |
| | 948 | bignum_idx = find_or_add_meta("bignumber", 9, 0); |
| | 949 | |
| | 950 | /* apply internal object/property ID fixups in the symbol table */ |
| | 951 | G_prs->apply_internal_fixups(); |
| | 952 | |
| | 953 | /* build the grammar productions */ |
| | 954 | G_prs->build_grammar_productions(); |
| | 955 | |
| | 956 | /* |
| | 957 | * Build the dictionaries. We must wait until after applying the |
| | 958 | * internal fixups to build the dictionaries, so that we have the |
| | 959 | * final, fully-resolved form of each object's vocabulary list before |
| | 960 | * we build the dictionaries. We must also wait until after we build |
| | 961 | * the grammar productions, because the grammar productions can add |
| | 962 | * dictionary entries for their literal token matchers. |
| | 963 | */ |
| | 964 | G_prs->build_dictionaries(); |
| | 965 | |
| | 966 | /* |
| | 967 | * Build the multi-method static initializers. Note: this must be done |
| | 968 | * before we generate the intrinsic class objects, because we might add |
| | 969 | * intrinsic class modifiers in the course of building the mm |
| | 970 | * initializers. |
| | 971 | */ |
| | 972 | build_multimethod_initializers(); |
| | 973 | |
| | 974 | /* make sure the the IntrinsicClass intrinsic class is itself defined */ |
| | 975 | int_class_idx = find_or_add_meta("intrinsic-class", 15, 0); |
| | 976 | |
| | 977 | /* build the IntrinsicClass objects */ |
| | 978 | build_intrinsic_class_objs(G_int_class_stream); |
| | 979 | |
| | 980 | /* calculate the final pool layouts */ |
| | 981 | calc_pool_layouts(&first_static_code_page); |
| | 982 | |
| | 983 | /* build the source line location maps, if debugging */ |
| | 984 | if (G_debug) |
| | 985 | build_source_line_maps(); |
| | 986 | |
| | 987 | /* look up the "_main" symbol in the global symbol table */ |
| | 988 | sym = G_prs->get_global_symtab()->find("_main"); |
| | 989 | |
| | 990 | /* |
| | 991 | * if there's no "_main" symbol, or it's not a function, it's an |
| | 992 | * error |
| | 993 | */ |
| | 994 | if (sym == 0) |
| | 995 | { |
| | 996 | /* "_main" isn't defined - log an error and abort */ |
| | 997 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MAIN_NOT_DEFINED); |
| | 998 | return; |
| | 999 | } |
| | 1000 | else if (sym->get_type() != TC_SYM_FUNC) |
| | 1001 | { |
| | 1002 | /* "_main" isn't a function - log an error and abort */ |
| | 1003 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MAIN_NOT_FUNC); |
| | 1004 | return; |
| | 1005 | } |
| | 1006 | else |
| | 1007 | { |
| | 1008 | /* |
| | 1009 | * Get the "_main" symbol's code pool address - this is the |
| | 1010 | * program's entrypoint. We can ask for this information at |
| | 1011 | * this point because we don't start writing the image file |
| | 1012 | * until after the final fixup pass, which is where this address |
| | 1013 | * is finally calculated. |
| | 1014 | */ |
| | 1015 | main_ofs = ((CTcSymFunc *)sym)->get_code_pool_addr(); |
| | 1016 | } |
| | 1017 | |
| | 1018 | /* get the constructor and finalizer property ID's */ |
| | 1019 | construct_prop = (tctarg_prop_id_t)G_prs->get_constructor_prop(); |
| | 1020 | finalize_prop = (tctarg_prop_id_t)G_prs->get_finalize_prop(); |
| | 1021 | objcall_prop = (tctarg_prop_id_t)G_prs->get_objcall_prop(); |
| | 1022 | |
| | 1023 | /* create our image writer */ |
| | 1024 | image_writer = new CVmImageWriter(fp); |
| | 1025 | |
| | 1026 | /* prepare the image file - use file format version 1 */ |
| | 1027 | image_writer->prepare(1, tool_data); |
| | 1028 | |
| | 1029 | /* write the entrypoint offset and data structure parameters */ |
| | 1030 | image_writer->write_entrypt(main_ofs, TCT3_METHOD_HDR_SIZE, |
| | 1031 | TCT3_EXC_ENTRY_SIZE, TCT3_LINE_ENTRY_SIZE, |
| | 1032 | TCT3_DBG_HDR_SIZE, TCT3_DBG_LCLSYM_HDR_SIZE, |
| | 1033 | TCT3_DBG_FMT_VSN); |
| | 1034 | |
| | 1035 | /* begin writing the symbolic items */ |
| | 1036 | image_writer->begin_sym_block(); |
| | 1037 | |
| | 1038 | /* run through the list of exports in the parser */ |
| | 1039 | for (exp = G_prs->get_exp_head() ; exp != 0 ; exp = exp->get_next()) |
| | 1040 | { |
| | 1041 | CTcPrsExport *exp2; |
| | 1042 | int dup_err_cnt; |
| | 1043 | |
| | 1044 | /* |
| | 1045 | * if this one's external name is null, it means that we've |
| | 1046 | * previously encountered it as a duplicate and marked it as such |
| | 1047 | * - in this case, simply skip it |
| | 1048 | */ |
| | 1049 | if (exp->get_ext_name() == 0) |
| | 1050 | continue; |
| | 1051 | |
| | 1052 | /* make sure it's not one of our special ones */ |
| | 1053 | if (exp->ext_name_matches("LastProp") |
| | 1054 | || exp->ext_name_matches("Constructor") |
| | 1055 | || exp->ext_name_matches("Destructor") |
| | 1056 | || exp->ext_name_matches("ObjectCallProp")) |
| | 1057 | { |
| | 1058 | /* it's a reserved export - flag an error */ |
| | 1059 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 1060 | TCERR_RESERVED_EXPORT, |
| | 1061 | (int)exp->get_ext_len(), |
| | 1062 | exp->get_ext_name()); |
| | 1063 | } |
| | 1064 | |
| | 1065 | |
| | 1066 | /* look up the symbol, defining as a property if undefined */ |
| | 1067 | sym = G_prs->get_global_symtab() |
| | 1068 | ->find_or_def_prop(exp->get_sym(), exp->get_sym_len(), FALSE); |
| | 1069 | |
| | 1070 | /* |
| | 1071 | * Scan the rest of the export list for duplicates. If we find |
| | 1072 | * the symbol external name exported with a different value, it's |
| | 1073 | * an error. |
| | 1074 | */ |
| | 1075 | for (dup_err_cnt = 0, exp2 = exp->get_next() ; exp2 != 0 ; |
| | 1076 | exp2 = exp2->get_next()) |
| | 1077 | { |
| | 1078 | /* if this one has already been marked as a duplicate, skip it */ |
| | 1079 | if (exp2->get_ext_name() == 0) |
| | 1080 | continue; |
| | 1081 | |
| | 1082 | /* check for a match of the external name */ |
| | 1083 | if (exp->ext_name_matches(exp2)) |
| | 1084 | { |
| | 1085 | /* |
| | 1086 | * This one matches, so it's a redundant export for the |
| | 1087 | * same name. If it's being exported as the same internal |
| | 1088 | * symbol as the other one, this is fine; otherwise it's |
| | 1089 | * an error, since the same external name can't be given |
| | 1090 | * two different meanings. |
| | 1091 | */ |
| | 1092 | if (!exp->sym_matches(exp2)) |
| | 1093 | { |
| | 1094 | /* |
| | 1095 | * It doesn't match - log an error. If we've already |
| | 1096 | * logged an error, show a continuation error; |
| | 1097 | * otherwise show the first error for the symbol. |
| | 1098 | */ |
| | 1099 | ++dup_err_cnt; |
| | 1100 | if (dup_err_cnt == 1) |
| | 1101 | { |
| | 1102 | /* it's the first error - show the long form */ |
| | 1103 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 1104 | TCERR_DUP_EXPORT, |
| | 1105 | (int)exp->get_ext_len(), |
| | 1106 | exp->get_ext_name(), |
| | 1107 | (int)exp->get_sym_len(), |
| | 1108 | exp->get_sym(), |
| | 1109 | (int)exp2->get_sym_len(), |
| | 1110 | exp2->get_sym()); |
| | 1111 | } |
| | 1112 | else |
| | 1113 | { |
| | 1114 | /* it's a follow-up error */ |
| | 1115 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 1116 | TCERR_DUP_EXPORT_AGAIN, |
| | 1117 | (int)exp->get_ext_len(), |
| | 1118 | exp->get_ext_name(), |
| | 1119 | (int)exp2->get_sym_len(), |
| | 1120 | exp2->get_sym()); |
| | 1121 | } |
| | 1122 | } |
| | 1123 | |
| | 1124 | /* |
| | 1125 | * Regardless of whether this one matches or not, remove |
| | 1126 | * it from the list by setting its external name to null - |
| | 1127 | * we only want to include each symbol in the export list |
| | 1128 | * once. |
| | 1129 | */ |
| | 1130 | exp2->set_extern_name(0, 0); |
| | 1131 | } |
| | 1132 | } |
| | 1133 | |
| | 1134 | /* write it out according to its type */ |
| | 1135 | switch(sym->get_type()) |
| | 1136 | { |
| | 1137 | case TC_SYM_OBJ: |
| | 1138 | /* write the object symbol */ |
| | 1139 | image_writer->write_sym_item_objid( |
| | 1140 | exp->get_ext_name(), exp->get_ext_len(), |
| | 1141 | ((CTcSymObj *)sym)->get_obj_id()); |
| | 1142 | break; |
| | 1143 | |
| | 1144 | case TC_SYM_PROP: |
| | 1145 | /* write the property symbol */ |
| | 1146 | image_writer->write_sym_item_propid( |
| | 1147 | exp->get_ext_name(), exp->get_ext_len(), |
| | 1148 | ((CTcSymProp *)sym)->get_prop()); |
| | 1149 | break; |
| | 1150 | |
| | 1151 | case TC_SYM_FUNC: |
| | 1152 | /* write the function symbol */ |
| | 1153 | image_writer->write_sym_item_func( |
| | 1154 | exp->get_ext_name(), exp->get_ext_len(), |
| | 1155 | ((CTcSymFunc *)sym)->get_code_pool_addr()); |
| | 1156 | break; |
| | 1157 | |
| | 1158 | default: |
| | 1159 | /* can't export other types */ |
| | 1160 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 1161 | TCERR_INVALID_TYPE_FOR_EXPORT, |
| | 1162 | (int)exp->get_sym_len(), exp->get_sym()); |
| | 1163 | break; |
| | 1164 | } |
| | 1165 | } |
| | 1166 | |
| | 1167 | /* |
| | 1168 | * write the last property ID - this is a special synthetic export |
| | 1169 | * that we provide automatically |
| | 1170 | */ |
| | 1171 | image_writer->write_sym_item_propid("LastProp", next_prop_); |
| | 1172 | |
| | 1173 | /* write our Constructor and Destructor property ID's */ |
| | 1174 | if (construct_prop != VM_INVALID_PROP) |
| | 1175 | image_writer->write_sym_item_propid("Constructor", construct_prop); |
| | 1176 | if (finalize_prop != VM_INVALID_PROP) |
| | 1177 | image_writer->write_sym_item_propid("Destructor", finalize_prop); |
| | 1178 | |
| | 1179 | /* |
| | 1180 | * write the special property ID for calling properties of anonymous |
| | 1181 | * function objects |
| | 1182 | */ |
| | 1183 | if (objcall_prop != VM_INVALID_PROP) |
| | 1184 | image_writer->write_sym_item_propid("ObjectCallProp", objcall_prop); |
| | 1185 | |
| | 1186 | /* done with the symbolic names */ |
| | 1187 | image_writer->end_sym_block(); |
| | 1188 | |
| | 1189 | /* write the function-set dependency table */ |
| | 1190 | image_writer->begin_func_dep(fnset_cnt_); |
| | 1191 | for (fnset = fnset_head_ ; fnset != 0 ; fnset = fnset->nxt) |
| | 1192 | image_writer->write_func_dep_item(fnset->nm); |
| | 1193 | image_writer->end_func_dep(); |
| | 1194 | |
| | 1195 | /* start the metaclass dependency table */ |
| | 1196 | image_writer->begin_meta_dep(meta_cnt_); |
| | 1197 | |
| | 1198 | /* write the metaclass dependency items */ |
| | 1199 | for (meta = meta_head_ ; meta != 0 ; meta = meta->nxt) |
| | 1200 | { |
| | 1201 | /* write the dependency item */ |
| | 1202 | image_writer->write_meta_dep_item(meta->nm); |
| | 1203 | |
| | 1204 | /* if there's an associated symbol, write the property list */ |
| | 1205 | if (meta->sym != 0) |
| | 1206 | { |
| | 1207 | CTcSymMetaProp *prop; |
| | 1208 | |
| | 1209 | /* scan the list of properties and write each one */ |
| | 1210 | for (prop = meta->sym->get_prop_head() ; prop != 0 ; |
| | 1211 | prop = prop->nxt_) |
| | 1212 | { |
| | 1213 | /* write this item's property */ |
| | 1214 | image_writer->write_meta_item_prop(prop->prop_->get_prop()); |
| | 1215 | } |
| | 1216 | } |
| | 1217 | } |
| | 1218 | |
| | 1219 | /* end the metaclass dependency table */ |
| | 1220 | image_writer->end_meta_dep(); |
| | 1221 | |
| | 1222 | /* write the code pool streams (don't bother masking the code bytes) */ |
| | 1223 | cs_list[0] = G_cs_main; |
| | 1224 | cs_list[1] = G_cs_static; |
| | 1225 | code_layout_.write_to_image(cs_list, 2, image_writer, 1, 0); |
| | 1226 | |
| | 1227 | /* |
| | 1228 | * write the constant pool (applying the constant pool data mask to |
| | 1229 | * obscure any text strings in the data) |
| | 1230 | */ |
| | 1231 | const_layout_.write_to_image(&G_ds, 1, image_writer, 2, data_xor_mask); |
| | 1232 | |
| | 1233 | /* write the "TADS object" data */ |
| | 1234 | write_tads_objects_to_image(G_os, image_writer, TCT3_METAID_TADSOBJ); |
| | 1235 | |
| | 1236 | /* write the intrinsic class modifier object data */ |
| | 1237 | write_tads_objects_to_image(G_icmod_stream, image_writer, |
| | 1238 | TCT3_METAID_ICMOD); |
| | 1239 | |
| | 1240 | /* write the dictionary data - this is a stream of dictionary objects */ |
| | 1241 | write_nontads_objs_to_image(G_dict_stream, image_writer, |
| | 1242 | TCT3_METAID_DICT, TRUE); |
| | 1243 | |
| | 1244 | /* write the grammar data - this is a stream of production objects */ |
| | 1245 | write_nontads_objs_to_image(G_gramprod_stream, image_writer, |
| | 1246 | TCT3_METAID_GRAMPROD, TRUE); |
| | 1247 | |
| | 1248 | /* if we have any BigNumber data, write it out */ |
| | 1249 | if (G_bignum_stream->get_ofs() != 0) |
| | 1250 | write_nontads_objs_to_image(G_bignum_stream, |
| | 1251 | image_writer, bignum_idx, FALSE); |
| | 1252 | |
| | 1253 | /* if we have any IntrinsicClass data, write it out */ |
| | 1254 | if (G_int_class_stream->get_ofs() != 0) |
| | 1255 | write_nontads_objs_to_image(G_int_class_stream, image_writer, |
| | 1256 | int_class_idx, FALSE); |
| | 1257 | |
| | 1258 | /* write the static initializer list */ |
| | 1259 | write_static_init_list(image_writer, |
| | 1260 | first_static_code_page * code_layout_.page_size_); |
| | 1261 | |
| | 1262 | /* write debug information if desired */ |
| | 1263 | if (G_debug) |
| | 1264 | { |
| | 1265 | /* write the source file table */ |
| | 1266 | write_sources_to_image(image_writer); |
| | 1267 | |
| | 1268 | /* write the global symbol table to the image file */ |
| | 1269 | write_global_symbols_to_image(image_writer); |
| | 1270 | |
| | 1271 | /* write the method header list */ |
| | 1272 | write_method_list_to_image(image_writer); |
| | 1273 | |
| | 1274 | /* write the macro records */ |
| | 1275 | write_macros_to_image(image_writer); |
| | 1276 | } |
| | 1277 | |
| | 1278 | /* finish the image file */ |
| | 1279 | image_writer->finish(); |
| | 1280 | |
| | 1281 | /* delete our image writer */ |
| | 1282 | delete image_writer; |
| | 1283 | image_writer = 0; |
| | 1284 | } |
| | 1285 | |
| | 1286 | /* ------------------------------------------------------------------------ */ |
| | 1287 | /* |
| | 1288 | * Write the static initializer ID list |
| | 1289 | */ |
| | 1290 | void CTcGenTarg::write_static_init_list(CVmImageWriter *image_writer, |
| | 1291 | ulong main_cs_size) |
| | 1292 | { |
| | 1293 | ulong rem; |
| | 1294 | ulong ofs; |
| | 1295 | ulong init_cnt; |
| | 1296 | |
| | 1297 | /* |
| | 1298 | * calculate the number of initializers - this is simply the size of |
| | 1299 | * the stream divided by the size of each record (4 bytes for object |
| | 1300 | * ID, 2 bytes for property ID) |
| | 1301 | */ |
| | 1302 | init_cnt = G_static_init_id_stream->get_ofs() / 6; |
| | 1303 | |
| | 1304 | /* add the multi-method initializer object, if there is one */ |
| | 1305 | if (mminit_obj_ != VM_INVALID_OBJ) |
| | 1306 | init_cnt += 1; |
| | 1307 | |
| | 1308 | /* start the block */ |
| | 1309 | image_writer->begin_sini_block(main_cs_size, init_cnt); |
| | 1310 | |
| | 1311 | /* write the multi-method initializer object, if applicable */ |
| | 1312 | if (mminit_obj_ != VM_INVALID_OBJ) |
| | 1313 | { |
| | 1314 | /* write the object data */ |
| | 1315 | char buf[6]; |
| | 1316 | oswp4(buf, mminit_obj_); /* the object ID */ |
| | 1317 | oswp2(buf+4, 1); /* our arbitrary initializer property ID */ |
| | 1318 | image_writer->write_bytes(buf, 6); |
| | 1319 | } |
| | 1320 | |
| | 1321 | /* write the bytes */ |
| | 1322 | for (ofs = 0, rem = G_static_init_id_stream->get_ofs() ; rem != 0 ; ) |
| | 1323 | { |
| | 1324 | const char *ptr; |
| | 1325 | ulong cur; |
| | 1326 | |
| | 1327 | /* get the next chunk */ |
| | 1328 | ptr = G_static_init_id_stream->get_block_ptr(ofs, rem, &cur); |
| | 1329 | |
| | 1330 | /* write this chunk */ |
| | 1331 | image_writer->write_bytes(ptr, cur); |
| | 1332 | |
| | 1333 | /* advance past this chunk */ |
| | 1334 | ofs += cur; |
| | 1335 | rem -= cur; |
| | 1336 | } |
| | 1337 | |
| | 1338 | /* end the block */ |
| | 1339 | image_writer->end_sini_block(); |
| | 1340 | } |
| | 1341 | |
| | 1342 | /* ------------------------------------------------------------------------ */ |
| | 1343 | /* |
| | 1344 | * Build synthesized code. This is called after all of the object files |
| | 1345 | * are loaded and before we generate the final image file, to give the |
| | 1346 | * linker a chance to generate any automatically generated code. We use |
| | 1347 | * this to generate the stub base functions for multi-methods. |
| | 1348 | */ |
| | 1349 | struct mmstub_ctx |
| | 1350 | { |
| | 1351 | mmstub_ctx() |
| | 1352 | { |
| | 1353 | mmc = 0; |
| | 1354 | cnt = 0; |
| | 1355 | } |
| | 1356 | |
| | 1357 | /* _multiMethodCall function symbol */ |
| | 1358 | CTcSymFunc *mmc; |
| | 1359 | |
| | 1360 | /* number of multi-method stubs we generated */ |
| | 1361 | int cnt; |
| | 1362 | }; |
| | 1363 | |
| | 1364 | void CTcGenTarg::build_synthesized_code() |
| | 1365 | { |
| | 1366 | mmstub_ctx ctx; |
| | 1367 | |
| | 1368 | /* look up the _multiMethodCall function */ |
| | 1369 | ctx.mmc = (CTcSymFunc *)G_prs->get_global_symtab()->find( |
| | 1370 | "_multiMethodCall", 16); |
| | 1371 | |
| | 1372 | /* |
| | 1373 | * our generated code isn't part of any object file - flag a new object |
| | 1374 | * file so that we don't get confused into thinking this came from the |
| | 1375 | * last object file loaded |
| | 1376 | */ |
| | 1377 | G_cs_static->set_object_file_start_ofs(); |
| | 1378 | G_os->set_object_file_start_ofs(); |
| | 1379 | |
| | 1380 | /* build out the stubs for the multi-method base functions */ |
| | 1381 | G_prs->get_global_symtab()->enum_entries(&multimethod_stub_cb, &ctx); |
| | 1382 | |
| | 1383 | /* |
| | 1384 | * if we generated any stubs, we definitely need _multiMethodCall to be |
| | 1385 | * defined - if it's not, it's an error |
| | 1386 | */ |
| | 1387 | if (ctx.cnt != 0 && (ctx.mmc == 0 || ctx.mmc->get_type() != TC_SYM_FUNC)) |
| | 1388 | { |
| | 1389 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MISSING_MMREG, |
| | 1390 | "_multiMethodCall"); |
| | 1391 | } |
| | 1392 | } |
| | 1393 | |
| | 1394 | /* callback context - build multi-method base function stubs */ |
| | 1395 | void CTcGenTarg::multimethod_stub_cb(void *ctx0, CTcSymbol *sym) |
| | 1396 | { |
| | 1397 | mmstub_ctx *ctx = (mmstub_ctx *)ctx0; |
| | 1398 | |
| | 1399 | /* if this is a function, check to see if it's a multi-method stub */ |
| | 1400 | if (sym->get_type() == TC_SYM_FUNC) |
| | 1401 | { |
| | 1402 | CTcSymFunc *fsym = (CTcSymFunc *)sym; |
| | 1403 | |
| | 1404 | /* |
| | 1405 | * It's a base function if it's marked as a multi-method and it |
| | 1406 | * doesn't have a '*' in its name. (If there's a '*', it's a |
| | 1407 | * concrete multi-method rather than a base function.) |
| | 1408 | */ |
| | 1409 | if (fsym->is_multimethod()) |
| | 1410 | { |
| | 1411 | /* it's marked as a multi-method - check for a decorated name */ |
| | 1412 | const char *p = sym->getstr(); |
| | 1413 | size_t rem = sym->getlen(); |
| | 1414 | for ( ; rem != 0 && *p != '*' ; ++p, --rem) ; |
| | 1415 | if (rem == 0) |
| | 1416 | { |
| | 1417 | tct3_method_gen_ctx gen_ctx; |
| | 1418 | |
| | 1419 | /* |
| | 1420 | * It's a multi-method base function - build out its stub. |
| | 1421 | * The stub function is a varargs function with no fixed |
| | 1422 | * parameters - i.e., funcName(...). Its body simply calls |
| | 1423 | * _multiMethodCall with a pointer to itself as the base |
| | 1424 | * function. |
| | 1425 | */ |
| | 1426 | G_cg->open_method(G_cs_main, |
| | 1427 | fsym, fsym->get_fixup_list_anchor(), |
| | 1428 | 0, 0, 0, TRUE, FALSE, FALSE, &gen_ctx); |
| | 1429 | |
| | 1430 | /* set the anchor in the function symbol */ |
| | 1431 | fsym->set_anchor(gen_ctx.anchor); |
| | 1432 | |
| | 1433 | /* |
| | 1434 | * turn the arguments into a list, leaving this on the |
| | 1435 | * stack as the second argument for _multiMethodCall |
| | 1436 | */ |
| | 1437 | G_cg->write_op(OPC_PUSHPARLST); |
| | 1438 | G_cs->write(0); |
| | 1439 | G_cg->note_push(); |
| | 1440 | |
| | 1441 | /* push the function address argument */ |
| | 1442 | CTcConstVal funcval; |
| | 1443 | funcval.set_funcptr(fsym); |
| | 1444 | CTPNConst cfunc(&funcval); |
| | 1445 | cfunc.gen_code(FALSE, FALSE); |
| | 1446 | G_cg->note_push(); |
| | 1447 | |
| | 1448 | /* |
| | 1449 | * call _multiMethodCall, if it's defined (if not, the |
| | 1450 | * caller will flag it as an error, so we don't need to |
| | 1451 | * worry about that here - just skip generating the call) |
| | 1452 | */ |
| | 1453 | if (ctx->mmc != 0) |
| | 1454 | ctx->mmc->gen_code_call(FALSE, 2, FALSE); |
| | 1455 | |
| | 1456 | /* return the result */ |
| | 1457 | G_cg->write_op(OPC_RETVAL); |
| | 1458 | G_cg->note_pop(); |
| | 1459 | |
| | 1460 | /* finish the method */ |
| | 1461 | G_cg->close_method(0, 0, 0, &gen_ctx); |
| | 1462 | G_cg->close_method_cleanup(&gen_ctx); |
| | 1463 | |
| | 1464 | /* the stub symbol now has a definition */ |
| | 1465 | fsym->set_extern(FALSE); |
| | 1466 | |
| | 1467 | /* count it */ |
| | 1468 | ctx->cnt += 1; |
| | 1469 | } |
| | 1470 | } |
| | 1471 | } |
| | 1472 | } |
| | 1473 | |
| | 1474 | /* ------------------------------------------------------------------------ */ |
| | 1475 | /* |
| | 1476 | * Start a OBJS header for a TadsObject to a given stream. This only |
| | 1477 | * writes the fixed part; the caller must then write the superclass list |
| | 1478 | * and the property table. After the contents have been written, call |
| | 1479 | * close_tadsobj() to finalize the header data. |
| | 1480 | */ |
| | 1481 | void CTcGenTarg::open_tadsobj(tct3_tadsobj_ctx *ctx, |
| | 1482 | CTcDataStream *stream, |
| | 1483 | vm_obj_id_t obj_id, |
| | 1484 | int sc_cnt, int prop_cnt, |
| | 1485 | unsigned int internal_flags, |
| | 1486 | unsigned int vm_flags) |
| | 1487 | { |
| | 1488 | /* remember the stream in the context */ |
| | 1489 | ctx->stream = stream; |
| | 1490 | |
| | 1491 | /* write the internal header */ |
| | 1492 | stream->write2(internal_flags); |
| | 1493 | |
| | 1494 | /* note the start of the VM object data */ |
| | 1495 | ctx->obj_ofs = stream->get_ofs(); |
| | 1496 | |
| | 1497 | /* write the fixed header data */ |
| | 1498 | stream->write_obj_id(obj_id); /* object ID */ |
| | 1499 | stream->write2(0); /* byte size placeholder - we'll fix up at "close" */ |
| | 1500 | stream->write2(sc_cnt); /* superclass count */ |
| | 1501 | stream->write2(prop_cnt); /* property count */ |
| | 1502 | stream->write2(vm_flags); /* object flags */ |
| | 1503 | } |
| | 1504 | |
| | 1505 | /* |
| | 1506 | * Close a TadsObject header. This must be called after the object's |
| | 1507 | * contents have been written so that we can fix up the header with the |
| | 1508 | * actual data size. |
| | 1509 | */ |
| | 1510 | void CTcGenTarg::close_tadsobj(tct3_tadsobj_ctx *ctx) |
| | 1511 | { |
| | 1512 | /* fix up the object size data */ |
| | 1513 | ctx->stream->write2_at(ctx->obj_ofs + 4, |
| | 1514 | ctx->stream->get_ofs() - ctx->obj_ofs - 6); |
| | 1515 | } |
| | 1516 | |
| | 1517 | |
| | 1518 | /* ------------------------------------------------------------------------ */ |
| | 1519 | /* |
| | 1520 | * Linker support: ensure that the given intrinsic class has a modifier |
| | 1521 | * object. If there's no modifier, we'll create one and add the code for |
| | 1522 | * it to the object stream. |
| | 1523 | * |
| | 1524 | * This should only be called during the linking phase, after code |
| | 1525 | * generation is completed. If you want to create a modifier during |
| | 1526 | * compilation, you should instead use CTcParser::find_or_def_obj(), since |
| | 1527 | * that creates the necessary structures for object file generation and |
| | 1528 | * later linking. |
| | 1529 | */ |
| | 1530 | void CTcGenTarg::linker_ensure_mod_obj(CTcSymMetaclass *mc) |
| | 1531 | { |
| | 1532 | /* if there's no modifier object, create one */ |
| | 1533 | if (mc->get_mod_obj() == 0) |
| | 1534 | { |
| | 1535 | /* create a modifier object */ |
| | 1536 | CTcSymObj *mod_sym = CTcSymObj::synthesize_modified_obj_sym(FALSE); |
| | 1537 | |
| | 1538 | /* set it to be an IntrinsicClassModifier object */ |
| | 1539 | mod_sym->set_metaclass(TC_META_ICMOD); |
| | 1540 | |
| | 1541 | /* link the modifier to the metaclass */ |
| | 1542 | mc->set_mod_obj(mod_sym); |
| | 1543 | |
| | 1544 | /* |
| | 1545 | * generate the object data - this is simply an empty object with |
| | 1546 | * no superclasses, and it goes in the intrinsic class modifier |
| | 1547 | * stream |
| | 1548 | */ |
| | 1549 | tct3_tadsobj_ctx obj_ctx; |
| | 1550 | G_cg->open_tadsobj( |
| | 1551 | &obj_ctx, G_icmod_stream, |
| | 1552 | mod_sym->get_obj_id(), 0, 0, 0, 0); |
| | 1553 | G_cg->close_tadsobj(&obj_ctx); |
| | 1554 | } |
| | 1555 | } |
| | 1556 | |
| | 1557 | /* |
| | 1558 | * Ensure that the given intrinsic class has a modifier object, by name. |
| | 1559 | */ |
| | 1560 | void CTcGenTarg::linker_ensure_mod_obj(const char *name, size_t len) |
| | 1561 | { |
| | 1562 | /* look up the symbol */ |
| | 1563 | CTcSymMetaclass *mc = (CTcSymMetaclass *)G_prs->get_global_symtab() |
| | 1564 | ->find(name, len); |
| | 1565 | |
| | 1566 | /* if we found the metaclass symbol, add a modifier if needed */ |
| | 1567 | if (mc != 0 && mc->get_type() == TC_SYM_METACLASS) |
| | 1568 | linker_ensure_mod_obj(mc); |
| | 1569 | } |
| | 1570 | |
| | 1571 | |
| | 1572 | /* ------------------------------------------------------------------------ */ |
| | 1573 | /* |
| | 1574 | * Build the multi-method initializers |
| | 1575 | */ |
| | 1576 | |
| | 1577 | /* enumerator callback context */ |
| | 1578 | struct mminit_ctx |
| | 1579 | { |
| | 1580 | mminit_ctx() |
| | 1581 | { |
| | 1582 | mmr = 0; |
| | 1583 | cnt = 0; |
| | 1584 | } |
| | 1585 | |
| | 1586 | /* _multiMethodRegister function symbol */ |
| | 1587 | CTcSymFunc *mmr; |
| | 1588 | |
| | 1589 | /* number of multi-method registrations we generated */ |
| | 1590 | int cnt; |
| | 1591 | }; |
| | 1592 | |
| | 1593 | /* main initializer builder */ |
| | 1594 | void CTcGenTarg::build_multimethod_initializers() |
| | 1595 | { |
| | 1596 | tct3_method_gen_ctx genctx; |
| | 1597 | mminit_ctx ctx; |
| | 1598 | |
| | 1599 | /* look up the _multiMethodRegister function */ |
| | 1600 | ctx.mmr = (CTcSymFunc *)G_prs->get_global_symtab()->find( |
| | 1601 | "_multiMethodRegister", 20); |
| | 1602 | |
| | 1603 | /* |
| | 1604 | * open the method - it's a static initializer, so write it to the |
| | 1605 | * static stream |
| | 1606 | */ |
| | 1607 | G_cg->open_method(G_cs_static, 0, 0, 0, 0, 0, FALSE, FALSE, FALSE, |
| | 1608 | &genctx); |
| | 1609 | |
| | 1610 | /* scan the symbol table for multimethods and generate initializers */ |
| | 1611 | G_prs->get_global_symtab()->enum_entries(&multimethod_init_cb, &ctx); |
| | 1612 | |
| | 1613 | /* |
| | 1614 | * if we found any multi-methods, generate a call to |
| | 1615 | * _multiMethodBuildBindings |
| | 1616 | */ |
| | 1617 | if (ctx.cnt != 0) |
| | 1618 | { |
| | 1619 | /* look up the function - it's an error if it's not defined */ |
| | 1620 | CTcSymFunc *mmb = (CTcSymFunc *)G_prs->get_global_symtab()->find( |
| | 1621 | "_multiMethodBuildBindings", 25); |
| | 1622 | if (mmb == 0 || mmb->get_type() != TC_SYM_FUNC) |
| | 1623 | { |
| | 1624 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MISSING_MMREG, |
| | 1625 | "_multiMethodBuildBindings"); |
| | 1626 | return; |
| | 1627 | } |
| | 1628 | |
| | 1629 | /* write the call instruction */ |
| | 1630 | G_cg->write_op(OPC_CALL); |
| | 1631 | G_cs->write(0); /* argument count */ |
| | 1632 | mmb->add_abs_fixup(G_cs); /* function address fixup */ |
| | 1633 | G_cs->write4(0); /* fixup placeholder */ |
| | 1634 | } |
| | 1635 | |
| | 1636 | /* close the method and clean up */ |
| | 1637 | G_cg->close_method(0, 0, 0, &genctx); |
| | 1638 | G_cg->close_method_cleanup(&genctx); |
| | 1639 | |
| | 1640 | /* |
| | 1641 | * if we generated any registrations, create the initializer object - |
| | 1642 | * this will go in the static initializer block to trigger invocation |
| | 1643 | * of the registration routine at load time |
| | 1644 | */ |
| | 1645 | if (ctx.cnt != 0) |
| | 1646 | { |
| | 1647 | /* we have multi-methods, so we definitely need _multiMethodRegister */ |
| | 1648 | if (ctx.mmr == 0 || ctx.mmr->get_type() != TC_SYM_FUNC) |
| | 1649 | { |
| | 1650 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MISSING_MMREG, |
| | 1651 | "_multiMethodRegister"); |
| | 1652 | return; |
| | 1653 | } |
| | 1654 | |
| | 1655 | /* create an anonymous object to hold the initializer code */ |
| | 1656 | mminit_obj_ = G_cg->new_obj_id(); |
| | 1657 | |
| | 1658 | /* write the object header: no superclasses, 1 property */ |
| | 1659 | tct3_tadsobj_ctx obj_ctx; |
| | 1660 | open_tadsobj(&obj_ctx, G_os, mminit_obj_, 0, 1, 0, 0); |
| | 1661 | |
| | 1662 | /* write the static initializer property */ |
| | 1663 | G_os->write2(1); /* arbitrary property ID */ |
| | 1664 | G_os->write(VM_CODEOFS); /* offset of the code we just generated */ |
| | 1665 | CTcAbsFixup::add_abs_fixup( |
| | 1666 | &genctx.anchor->fixup_info_.internal_fixup_head_, |
| | 1667 | G_os, G_os->get_ofs()); |
| | 1668 | G_os->write4(0); /* placeholder */ |
| | 1669 | |
| | 1670 | /* fix up the object size data */ |
| | 1671 | close_tadsobj(&obj_ctx); |
| | 1672 | } |
| | 1673 | |
| | 1674 | /* switch back to the main stream */ |
| | 1675 | G_cs = G_cs_main; |
| | 1676 | } |
| | 1677 | |
| | 1678 | /* callback context - build multi-method registration calls */ |
| | 1679 | void CTcGenTarg::multimethod_init_cb(void *ctx0, CTcSymbol *sym) |
| | 1680 | { |
| | 1681 | mminit_ctx *ctx = (mminit_ctx *)ctx0; |
| | 1682 | |
| | 1683 | /* if this is a function, check to see if it's a multi-method instance */ |
| | 1684 | if (sym->get_type() == TC_SYM_FUNC) |
| | 1685 | { |
| | 1686 | CTcSymFunc *fsym = (CTcSymFunc *)sym; |
| | 1687 | |
| | 1688 | /* |
| | 1689 | * multi-method instances have names of the form |
| | 1690 | * "name*type1,type2", so check the name to see if it fits the |
| | 1691 | * pattern |
| | 1692 | */ |
| | 1693 | const char *p = sym->getstr(); |
| | 1694 | size_t rem = sym->getlen(); |
| | 1695 | int is_mm = FALSE; |
| | 1696 | for ( ; rem != 0 ; ++p, --rem) |
| | 1697 | { |
| | 1698 | /* |
| | 1699 | * if we found a '*', it's a multimethod; otherwise, if it's |
| | 1700 | * anything other than a symbol character, it's not a |
| | 1701 | * multimethod |
| | 1702 | */ |
| | 1703 | if (*p == '*') |
| | 1704 | { |
| | 1705 | is_mm = TRUE; |
| | 1706 | break; |
| | 1707 | } |
| | 1708 | else if (!is_sym(*p)) |
| | 1709 | break; |
| | 1710 | } |
| | 1711 | |
| | 1712 | /* |
| | 1713 | * If it's a multi-method symbol, build the initializer. If it's |
| | 1714 | * the base function for a multi-method, build out the stub |
| | 1715 | * function. |
| | 1716 | */ |
| | 1717 | if (is_mm) |
| | 1718 | { |
| | 1719 | int argc; |
| | 1720 | |
| | 1721 | /* note the function base name - it's the part up to the '*' */ |
| | 1722 | const char *funcname = sym->getstr(); |
| | 1723 | size_t funclen = (size_t)(p - funcname); |
| | 1724 | |
| | 1725 | /* look up the base function symbol */ |
| | 1726 | CTcSymFunc *base_sym = (CTcSymFunc *)G_prs->get_global_symtab() |
| | 1727 | ->find(funcname, funclen); |
| | 1728 | |
| | 1729 | /* if it's not defined as a function, ignore it */ |
| | 1730 | if (base_sym == 0 || base_sym->get_type() != TC_SYM_FUNC) |
| | 1731 | return; |
| | 1732 | |
| | 1733 | /* |
| | 1734 | * skip to the end of the string, and remove the '*' from the |
| | 1735 | * length count |
| | 1736 | */ |
| | 1737 | p += rem; |
| | 1738 | --rem; |
| | 1739 | |
| | 1740 | /* |
| | 1741 | * Run through the decorated name and look up each mentioned |
| | 1742 | * class. We need to push the parameters onto the stack in |
| | 1743 | * reverse order to match the VM calling conventions. |
| | 1744 | */ |
| | 1745 | for (argc = 0 ; rem != 0 ; ++argc) |
| | 1746 | { |
| | 1747 | /* remember where the current name starts */ |
| | 1748 | size_t plen; |
| | 1749 | |
| | 1750 | /* skip the terminator for this item */ |
| | 1751 | --p, --rem; |
| | 1752 | |
| | 1753 | /* scan backwards to the previous delimiter */ |
| | 1754 | for (plen = 0 ; rem != 0 && *(p-1) != ';' ; |
| | 1755 | --p, --rem, ++plen) ; |
| | 1756 | |
| | 1757 | /* look up this name */ |
| | 1758 | if (plen == 0) |
| | 1759 | { |
| | 1760 | /* |
| | 1761 | * empty name - this slot accepts any type; represent |
| | 1762 | * this in the run-time formal list with 'nil' |
| | 1763 | */ |
| | 1764 | G_cg->write_op(OPC_PUSHNIL); |
| | 1765 | |
| | 1766 | /* |
| | 1767 | * An untyped slot is implicitly an Object slot, so we |
| | 1768 | * need to make sure that Object can participate in the |
| | 1769 | * binding property system by ensuring that it has a |
| | 1770 | * modifier object. |
| | 1771 | */ |
| | 1772 | G_cg->linker_ensure_mod_obj("Object", 6); |
| | 1773 | } |
| | 1774 | else if (plen == 3 && memcmp(p, "...", 3) == 0) |
| | 1775 | { |
| | 1776 | /* |
| | 1777 | * varargs indicator - represent this in the list with |
| | 1778 | * the literal string '...' |
| | 1779 | */ |
| | 1780 | CTcConstVal val; |
| | 1781 | val.set_sstr("...", 3); |
| | 1782 | CTPNConst cval(&val); |
| | 1783 | cval.gen_code(FALSE, FALSE); |
| | 1784 | |
| | 1785 | /* |
| | 1786 | * a varargs slot is implicitly an Object slot, so make |
| | 1787 | * sure Object has a modifier object |
| | 1788 | */ |
| | 1789 | G_cg->linker_ensure_mod_obj("Object", 6); |
| | 1790 | } |
| | 1791 | else |
| | 1792 | { |
| | 1793 | /* class name - look it up */ |
| | 1794 | CTcSymbol *cl = G_prs->get_global_symtab()->find(p, plen); |
| | 1795 | CTcConstVal val; |
| | 1796 | |
| | 1797 | /* |
| | 1798 | * if it's missing, unresolved, or not an object, flag |
| | 1799 | * an error |
| | 1800 | */ |
| | 1801 | if (cl == 0 |
| | 1802 | || (cl->get_type() == TC_SYM_OBJ |
| | 1803 | && ((CTcSymObj *)cl)->is_extern())) |
| | 1804 | { |
| | 1805 | G_tcmain->log_error( |
| | 1806 | 0, 0, TC_SEV_ERROR, TCERR_UNDEF_SYM, |
| | 1807 | (int)plen, p); |
| | 1808 | return; |
| | 1809 | } |
| | 1810 | else if (cl->get_type() == TC_SYM_OBJ) |
| | 1811 | { |
| | 1812 | /* get the object information */ |
| | 1813 | CTcSymObj *co = (CTcSymObj *)cl; |
| | 1814 | val.set_obj(co->get_obj_id(), co->get_metaclass()); |
| | 1815 | } |
| | 1816 | else if (cl->get_type() == TC_SYM_METACLASS) |
| | 1817 | { |
| | 1818 | /* get the metaclass information */ |
| | 1819 | CTcSymMetaclass *cm = (CTcSymMetaclass *)cl; |
| | 1820 | val.set_obj(cm->get_class_obj(), TC_META_UNKNOWN); |
| | 1821 | |
| | 1822 | /* |
| | 1823 | * If this metaclass doesn't have a modifier |
| | 1824 | * object, create one for it. This is needed |
| | 1825 | * because the run-time library's multi-method |
| | 1826 | * implementation stores the method binding |
| | 1827 | * information in properties of the parameter |
| | 1828 | * objects. Since we're using this metaclass as a |
| | 1829 | * parameter type, we'll need to write at least one |
| | 1830 | * property to it. We can only write properties to |
| | 1831 | * intrinsic class objects when they're equipped |
| | 1832 | * with modifier objects. |
| | 1833 | * |
| | 1834 | * The presence of a modifier object has no effect |
| | 1835 | * at all on performance for ordinary method call |
| | 1836 | * operations on the intrinsic class, and the |
| | 1837 | * modifier itself is just a bare object, so the |
| | 1838 | * cost of creating this extra object is trivial. |
| | 1839 | */ |
| | 1840 | G_cg->linker_ensure_mod_obj(cm); |
| | 1841 | } |
| | 1842 | else |
| | 1843 | { |
| | 1844 | /* it's not a valid object type */ |
| | 1845 | G_tcmain->log_error( |
| | 1846 | 0, 0, TC_SEV_ERROR, TCERR_MMPARAM_NOT_OBJECT, |
| | 1847 | (int)plen, p, (int)funclen, funcname); |
| | 1848 | return; |
| | 1849 | } |
| | 1850 | |
| | 1851 | /* |
| | 1852 | * represent the object or class in the parameter list |
| | 1853 | * with the object reference |
| | 1854 | */ |
| | 1855 | CTPNConst cval(&val); |
| | 1856 | cval.gen_code(FALSE, FALSE); |
| | 1857 | } |
| | 1858 | |
| | 1859 | /* note the value we pushed */ |
| | 1860 | G_cg->note_push(); |
| | 1861 | } |
| | 1862 | |
| | 1863 | /* build and push the list from the parameters */ |
| | 1864 | if (argc <= 255) |
| | 1865 | { |
| | 1866 | G_cg->write_op(OPC_NEW1); |
| | 1867 | G_cs->write((char)argc); |
| | 1868 | } |
| | 1869 | else |
| | 1870 | { |
| | 1871 | G_cg->write_op(OPC_NEW2); |
| | 1872 | G_cs->write2(argc); |
| | 1873 | } |
| | 1874 | G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_LIST)); |
| | 1875 | G_cg->write_op(OPC_GETR0); |
| | 1876 | G_cg->note_pop(argc); |
| | 1877 | G_cg->note_push(); |
| | 1878 | |
| | 1879 | /* push the function pointer argument */ |
| | 1880 | G_cg->write_op(OPC_PUSHFNPTR); |
| | 1881 | fsym->add_abs_fixup(G_cs); |
| | 1882 | G_cs->write4(0); |
| | 1883 | G_cg->note_push(); |
| | 1884 | |
| | 1885 | /* push the base function pointer argument */ |
| | 1886 | CTcConstVal funcval; |
| | 1887 | funcval.set_funcptr(base_sym); |
| | 1888 | CTPNConst cfunc(&funcval); |
| | 1889 | cfunc.gen_code(FALSE, FALSE); |
| | 1890 | G_cg->note_push(); |
| | 1891 | |
| | 1892 | /* |
| | 1893 | * call _multiMethodRegister, if it's available (if it's not, |
| | 1894 | * our caller will flag this as an error, so just skip the code |
| | 1895 | * generation here) |
| | 1896 | */ |
| | 1897 | if (ctx->mmr != 0) |
| | 1898 | { |
| | 1899 | G_cg->write_op(OPC_CALL); |
| | 1900 | G_cs->write(3); /* argument count */ |
| | 1901 | ctx->mmr->add_abs_fixup(G_cs); /* function address fixup */ |
| | 1902 | G_cs->write4(0); /* fixup placeholder */ |
| | 1903 | } |
| | 1904 | |
| | 1905 | /* the 3 arguments will be gone on return */ |
| | 1906 | G_cg->note_pop(3); |
| | 1907 | |
| | 1908 | /* count the registration */ |
| | 1909 | ctx->cnt += 1; |
| | 1910 | } |
| | 1911 | } |
| | 1912 | } |
| | 1913 | |
| | 1914 | /* ------------------------------------------------------------------------ */ |
| | 1915 | /* |
| | 1916 | * Build the IntrinsicClass objects |
| | 1917 | */ |
| | 1918 | void CTcGenTarg::build_intrinsic_class_objs(CTcDataStream *str) |
| | 1919 | { |
| | 1920 | tc_meta_entry *meta; |
| | 1921 | uint idx; |
| | 1922 | |
| | 1923 | /* |
| | 1924 | * run through the dependency table, and create an IntrinsicClass |
| | 1925 | * object for each entry |
| | 1926 | */ |
| | 1927 | for (idx = 0, meta = meta_head_ ; meta != 0 ; meta = meta->nxt, ++idx) |
| | 1928 | { |
| | 1929 | /* |
| | 1930 | * if we have a symbol for this class, add the object to the |
| | 1931 | * intrinsic class stream |
| | 1932 | */ |
| | 1933 | if (meta->sym != 0) |
| | 1934 | { |
| | 1935 | /* write the OBJS header */ |
| | 1936 | str->write4(meta->sym->get_class_obj()); |
| | 1937 | str->write2(8); |
| | 1938 | |
| | 1939 | /* |
| | 1940 | * write the data - the data length (8), followed by the |
| | 1941 | * intrinsic class index that this object is associated |
| | 1942 | * with, followed by the modifier object |
| | 1943 | */ |
| | 1944 | str->write2(8); |
| | 1945 | str->write2(idx); |
| | 1946 | str->write4(meta->sym->get_mod_obj() == 0 |
| | 1947 | ? VM_INVALID_OBJ |
| | 1948 | : meta->sym->get_mod_obj()->get_obj_id()); |
| | 1949 | |
| | 1950 | /* |
| | 1951 | * fix up the inheritance chain in the modifier objects, if |
| | 1952 | * necessary |
| | 1953 | */ |
| | 1954 | meta->sym->fix_mod_obj_sc_list(); |
| | 1955 | } |
| | 1956 | } |
| | 1957 | } |
| | 1958 | |
| | 1959 | /* ------------------------------------------------------------------------ */ |
| | 1960 | /* |
| | 1961 | * Build the source file line maps. These maps provide listings from |
| | 1962 | * the source location to the executable location, so the debugger can |
| | 1963 | * do things such as set a breakpoint at a given source file location. |
| | 1964 | */ |
| | 1965 | void CTcGenTarg::build_source_line_maps() |
| | 1966 | { |
| | 1967 | CTcStreamAnchor *anchor; |
| | 1968 | |
| | 1969 | /* go through the list of anchors in the code stream */ |
| | 1970 | for (anchor = G_cs->get_first_anchor() ; anchor != 0 ; |
| | 1971 | anchor = anchor->nxt_) |
| | 1972 | { |
| | 1973 | ulong start_ofs; |
| | 1974 | ulong start_addr; |
| | 1975 | ulong dbg_ofs; |
| | 1976 | uint cnt; |
| | 1977 | ulong ofs; |
| | 1978 | |
| | 1979 | /* get the anchor's stream offset */ |
| | 1980 | start_ofs = anchor->get_ofs(); |
| | 1981 | |
| | 1982 | /* get the anchor's absolute address in the image file */ |
| | 1983 | start_addr = anchor->get_addr(); |
| | 1984 | |
| | 1985 | /* read the debug table offset from the method header */ |
| | 1986 | dbg_ofs = start_ofs + G_cs->readu2_at(start_ofs + 8); |
| | 1987 | |
| | 1988 | /* if there's no debug table for this method, go on to the next */ |
| | 1989 | if (dbg_ofs == start_ofs) |
| | 1990 | continue; |
| | 1991 | |
| | 1992 | /* read the number of line entries */ |
| | 1993 | cnt = G_cs->readu2_at(dbg_ofs + TCT3_DBG_HDR_SIZE); |
| | 1994 | |
| | 1995 | /* go through the individual line entries */ |
| | 1996 | for (ofs = dbg_ofs + TCT3_DBG_HDR_SIZE + 2 ; cnt != 0 ; |
| | 1997 | --cnt, ofs += TCT3_LINE_ENTRY_SIZE) |
| | 1998 | { |
| | 1999 | uint file_id; |
| | 2000 | ulong linenum; |
| | 2001 | uint method_ofs; |
| | 2002 | ulong line_addr; |
| | 2003 | CTcTokFileDesc *file_desc; |
| | 2004 | |
| | 2005 | /* |
| | 2006 | * get the file position, and the byte-code offset from the |
| | 2007 | * start of the method of the executable code for the line |
| | 2008 | */ |
| | 2009 | method_ofs = G_cs->readu2_at(ofs); |
| | 2010 | file_id = G_cs->readu2_at(ofs + 2); |
| | 2011 | linenum = G_cs->readu4_at(ofs + 4); |
| | 2012 | |
| | 2013 | /* calculate the absolute address of the line in the image file */ |
| | 2014 | line_addr = start_addr + method_ofs; |
| | 2015 | |
| | 2016 | /* find the given file descriptor */ |
| | 2017 | file_desc = G_tok->get_filedesc(file_id); |
| | 2018 | |
| | 2019 | /* |
| | 2020 | * get the original file descriptor, since we always want to |
| | 2021 | * add to the original, not to the duplicates, if the file |
| | 2022 | * appears more than once (because this is a one-way mapping |
| | 2023 | * from file to byte-code location - we thus require a |
| | 2024 | * single index) |
| | 2025 | */ |
| | 2026 | if (file_desc->get_orig() != 0) |
| | 2027 | file_desc = file_desc->get_orig(); |
| | 2028 | |
| | 2029 | /* add this line to the file descriptor */ |
| | 2030 | file_desc->add_source_line(linenum, line_addr); |
| | 2031 | } |
| | 2032 | } |
| | 2033 | } |
| | 2034 | |
| | 2035 | |
| | 2036 | /* ------------------------------------------------------------------------ */ |
| | 2037 | /* |
| | 2038 | * Callback to write enumerated source lines to an image file |
| | 2039 | */ |
| | 2040 | static void write_source_lines_cb(void *ctx, ulong linenum, ulong code_addr) |
| | 2041 | { |
| | 2042 | CVmImageWriter *image_writer; |
| | 2043 | |
| | 2044 | /* get the image writer */ |
| | 2045 | image_writer = (CVmImageWriter *)ctx; |
| | 2046 | |
| | 2047 | /* write the data */ |
| | 2048 | image_writer->write_srcf_line_entry(linenum, code_addr); |
| | 2049 | } |
| | 2050 | |
| | 2051 | /* |
| | 2052 | * Write the list of source file descriptors to an image file |
| | 2053 | */ |
| | 2054 | void CTcGenTarg::write_sources_to_image(CVmImageWriter *image_writer) |
| | 2055 | { |
| | 2056 | CTcTokFileDesc *desc; |
| | 2057 | |
| | 2058 | /* write the block prefix */ |
| | 2059 | image_writer->begin_srcf_block(G_tok->get_filedesc_count()); |
| | 2060 | |
| | 2061 | /* write the entries */ |
| | 2062 | for (desc = G_tok->get_first_filedesc() ; desc != 0 ; |
| | 2063 | desc = desc->get_next()) |
| | 2064 | { |
| | 2065 | const char *fname; |
| | 2066 | |
| | 2067 | /* |
| | 2068 | * Get the filename. Use the fully resolved local filename, so |
| | 2069 | * that the debugger can correlate the resolved file back to the |
| | 2070 | * project configuration. This ties the debug records to the local |
| | 2071 | * directory structure, but the only drawback of this is that the |
| | 2072 | * program must be recompiled wherever it's to be used with the |
| | 2073 | * debugger. |
| | 2074 | */ |
| | 2075 | fname = desc->get_fname(); |
| | 2076 | |
| | 2077 | /* |
| | 2078 | * if we're in test reporting mode, write only the root name, not |
| | 2079 | * the full name - this insulates test logs from the details of |
| | 2080 | * local pathname conventions and the local directory structure, |
| | 2081 | * allowing for more portable test logs |
| | 2082 | */ |
| | 2083 | if (G_tcmain->get_test_report_mode()) |
| | 2084 | fname = os_get_root_name((char *)fname); |
| | 2085 | |
| | 2086 | /* begin this entry */ |
| | 2087 | image_writer->begin_srcf_entry(desc->get_orig_index(), fname); |
| | 2088 | |
| | 2089 | /* write the source lines */ |
| | 2090 | desc->enum_source_lines(write_source_lines_cb, image_writer); |
| | 2091 | |
| | 2092 | /* end this entry */ |
| | 2093 | image_writer->end_srcf_entry(); |
| | 2094 | } |
| | 2095 | |
| | 2096 | /* end the block */ |
| | 2097 | image_writer->end_srcf_block(); |
| | 2098 | } |
| | 2099 | |
| | 2100 | /* |
| | 2101 | * Write the method header list to the image file |
| | 2102 | */ |
| | 2103 | void CTcGenTarg::write_method_list_to_image(CVmImageWriter *image_writer) |
| | 2104 | { |
| | 2105 | CTcStreamAnchor *anchor; |
| | 2106 | |
| | 2107 | /* begin the method header list block in the image file */ |
| | 2108 | image_writer->begin_mhls_block(); |
| | 2109 | |
| | 2110 | /* go through the list of anchors in the code stream */ |
| | 2111 | for (anchor = G_cs->get_first_anchor() ; anchor != 0 ; |
| | 2112 | anchor = anchor->nxt_) |
| | 2113 | { |
| | 2114 | /* write this entry's code pool address */ |
| | 2115 | image_writer->write_mhls_entry(anchor->get_addr()); |
| | 2116 | } |
| | 2117 | |
| | 2118 | /* end the block */ |
| | 2119 | image_writer->end_mhls_block(); |
| | 2120 | } |
| | 2121 | |
| | 2122 | /* |
| | 2123 | * Write the preprocessor macros to the image file, for debugger use |
| | 2124 | */ |
| | 2125 | void CTcGenTarg::write_macros_to_image(CVmImageWriter *image_writer) |
| | 2126 | { |
| | 2127 | /* begin the macro block */ |
| | 2128 | image_writer->begin_macr_block(); |
| | 2129 | |
| | 2130 | /* |
| | 2131 | * ask the tokenizer to dump the data directly to the file underlying |
| | 2132 | * the image writer |
| | 2133 | */ |
| | 2134 | G_tok->write_macros_to_file_for_debug(image_writer->get_fp()); |
| | 2135 | |
| | 2136 | /* end the macro block */ |
| | 2137 | image_writer->end_macr_block(); |
| | 2138 | } |
| | 2139 | |
| | 2140 | /* ------------------------------------------------------------------------ */ |
| | 2141 | /* |
| | 2142 | * Callback context for global symbol table writer |
| | 2143 | */ |
| | 2144 | struct write_sym_to_image_cb |
| | 2145 | { |
| | 2146 | /* number of symbols written */ |
| | 2147 | ulong count; |
| | 2148 | |
| | 2149 | /* the image writer */ |
| | 2150 | CVmImageWriter *image_writer; |
| | 2151 | }; |
| | 2152 | |
| | 2153 | /* |
| | 2154 | * Callback for writing the global symbol table to an object file |
| | 2155 | */ |
| | 2156 | static void write_sym_to_image(void *ctx0, CTcSymbol *sym) |
| | 2157 | { |
| | 2158 | write_sym_to_image_cb *ctx; |
| | 2159 | |
| | 2160 | /* cast the context */ |
| | 2161 | ctx = (write_sym_to_image_cb *)ctx0; |
| | 2162 | |
| | 2163 | /* |
| | 2164 | * If the symbol's name starts with a period, don't write it - the |
| | 2165 | * compiler constructs certain private symbol names for its own |
| | 2166 | * internal use, and marks them as such by starting the name with a |
| | 2167 | * period. These symbols cannot be used to evaluate expressions, so |
| | 2168 | * they're of no use in teh global symbol table in the image file. |
| | 2169 | */ |
| | 2170 | if (sym->get_sym()[0] == '.') |
| | 2171 | return; |
| | 2172 | |
| | 2173 | /* ask the symbol to do the work */ |
| | 2174 | if (sym->write_to_image_file_global(ctx->image_writer)) |
| | 2175 | { |
| | 2176 | /* we wrote the symbol - count it */ |
| | 2177 | ++(ctx->count); |
| | 2178 | } |
| | 2179 | } |
| | 2180 | |
| | 2181 | /* |
| | 2182 | * Write the global symbol table to an object file |
| | 2183 | */ |
| | 2184 | void CTcGenTarg::write_global_symbols_to_image(CVmImageWriter *image_writer) |
| | 2185 | { |
| | 2186 | write_sym_to_image_cb ctx; |
| | 2187 | |
| | 2188 | /* set up the callback context */ |
| | 2189 | ctx.count = 0; |
| | 2190 | ctx.image_writer = image_writer; |
| | 2191 | |
| | 2192 | /* start the block */ |
| | 2193 | image_writer->begin_gsym_block(); |
| | 2194 | |
| | 2195 | /* ask the symbol table to enumerate itself through our symbol writer */ |
| | 2196 | G_prs->get_global_symtab()->enum_entries(&write_sym_to_image, &ctx); |
| | 2197 | |
| | 2198 | /* end the block */ |
| | 2199 | image_writer->end_gsym_block(ctx.count); |
| | 2200 | } |
| | 2201 | |
| | 2202 | /* ------------------------------------------------------------------------ */ |
| | 2203 | /* |
| | 2204 | * Look up a property |
| | 2205 | */ |
| | 2206 | vm_prop_id_t CTcGenTarg::look_up_prop(const char *propname, int required, |
| | 2207 | int err_if_undef, int err_if_not_prop) |
| | 2208 | { |
| | 2209 | CTcSymbol *sym; |
| | 2210 | |
| | 2211 | /* look up the symbol */ |
| | 2212 | sym = G_prs->get_global_symtab()->find(propname); |
| | 2213 | |
| | 2214 | /* check to see if it's defined and of the proper type */ |
| | 2215 | if (sym == 0) |
| | 2216 | { |
| | 2217 | /* log the 'undefined' error */ |
| | 2218 | G_tcmain->log_error(0, 0, required ? TC_SEV_ERROR : TC_SEV_PEDANTIC, |
| | 2219 | err_if_undef); |
| | 2220 | } |
| | 2221 | else if (sym->get_type() != TC_SYM_PROP) |
| | 2222 | { |
| | 2223 | /* log the 'not a property' error */ |
| | 2224 | G_tcmain->log_error(0, 0, required ? TC_SEV_ERROR : TC_SEV_PEDANTIC, |
| | 2225 | err_if_not_prop); |
| | 2226 | } |
| | 2227 | else |
| | 2228 | { |
| | 2229 | /* return the property ID */ |
| | 2230 | return ((CTcSymProp *)sym)->get_prop(); |
| | 2231 | } |
| | 2232 | |
| | 2233 | /* if we got here, we didn't find a valid property */ |
| | 2234 | return VM_INVALID_PROP; |
| | 2235 | } |
| | 2236 | |
| | 2237 | |
| | 2238 | /* ------------------------------------------------------------------------ */ |
| | 2239 | /* |
| | 2240 | * Write a TADS object stream to the image file. We'll write blocks of |
| | 2241 | * size up to somewhat less than 64k, to ensure that the file is usable on |
| | 2242 | * 16-bit machines. |
| | 2243 | */ |
| | 2244 | void CTcGenTarg::write_tads_objects_to_image(CTcDataStream *os, |
| | 2245 | CVmImageWriter *image_writer, |
| | 2246 | int meta_idx) |
| | 2247 | { |
| | 2248 | /* write the persistent (non-transient) objects */ |
| | 2249 | write_tads_objects_to_image(os, image_writer, meta_idx, FALSE); |
| | 2250 | |
| | 2251 | /* write the transient objects */ |
| | 2252 | write_tads_objects_to_image(os, image_writer, meta_idx, TRUE); |
| | 2253 | } |
| | 2254 | |
| | 2255 | /* |
| | 2256 | * Write the TADS object stream to the image file, writing only persistent |
| | 2257 | * or transient objects. |
| | 2258 | */ |
| | 2259 | void CTcGenTarg::write_tads_objects_to_image(CTcDataStream *os, |
| | 2260 | CVmImageWriter *image_writer, |
| | 2261 | int meta_idx, int trans) |
| | 2262 | { |
| | 2263 | ulong start_ofs; |
| | 2264 | |
| | 2265 | /* keep going until we've written the whole file */ |
| | 2266 | for (start_ofs = 0 ; start_ofs < os->get_ofs() ; ) |
| | 2267 | { |
| | 2268 | ulong ofs; |
| | 2269 | uint siz; |
| | 2270 | uint cnt; |
| | 2271 | uint block_size; |
| | 2272 | |
| | 2273 | /* |
| | 2274 | * Scan the stream. Each entry in the stream is a standard |
| | 2275 | * object record, which means that it starts with the object ID |
| | 2276 | * (UINT4) and the length (UINT2) of the metaclass-specific |
| | 2277 | * data, which is then followed by the metaclass data. Skip as |
| | 2278 | * many objects as we can while staying within our approximately |
| | 2279 | * 64k limit. |
| | 2280 | */ |
| | 2281 | for (block_size = 0, ofs = start_ofs, cnt = 0 ; ; ) |
| | 2282 | { |
| | 2283 | uint flags; |
| | 2284 | ulong rem_len; |
| | 2285 | size_t orig_prop_cnt; |
| | 2286 | size_t write_prop_cnt; |
| | 2287 | size_t write_size; |
| | 2288 | ulong next_ofs; |
| | 2289 | ulong orig_ofs; |
| | 2290 | |
| | 2291 | /* if we've reached the end of the stream, we're done */ |
| | 2292 | if (ofs >= os->get_ofs()) |
| | 2293 | break; |
| | 2294 | |
| | 2295 | /* remember the starting offset */ |
| | 2296 | orig_ofs = ofs; |
| | 2297 | |
| | 2298 | /* read our internal flags */ |
| | 2299 | flags = os->readu2_at(ofs + TCT3_OBJ_INTERNHDR_FLAGS_OFS); |
| | 2300 | |
| | 2301 | /* |
| | 2302 | * get the size of this block - this is the |
| | 2303 | * metaclass-specific data size at offset 4 in the T3 |
| | 2304 | * metaclass header, plus the size of the T3 metaclass |
| | 2305 | * header, plus the size of our internal header |
| | 2306 | */ |
| | 2307 | siz = TCT3_OBJ_INTERNHDR_SIZE |
| | 2308 | + TCT3_META_HEADER_SIZE |
| | 2309 | + os->readu2_at(ofs + TCT3_META_HEADER_OFS + 4); |
| | 2310 | |
| | 2311 | /* |
| | 2312 | * Calculate the offset of the next block. Note that this is |
| | 2313 | * the current offset plus the original block size; the amount |
| | 2314 | * of data we end up writing might be less than the original |
| | 2315 | * block size because we might have deleted property slots |
| | 2316 | * when we sorted and compressed the property table. |
| | 2317 | */ |
| | 2318 | next_ofs = ofs + siz; |
| | 2319 | |
| | 2320 | /* if this object was deleted, skip it */ |
| | 2321 | if ((flags & TCT3_OBJ_REPLACED) != 0) |
| | 2322 | { |
| | 2323 | ofs = next_ofs; |
| | 2324 | continue; |
| | 2325 | } |
| | 2326 | |
| | 2327 | /* |
| | 2328 | * if this object is of the wrong persistent/transient type, |
| | 2329 | * skip it |
| | 2330 | */ |
| | 2331 | if (((flags & TCT3_OBJ_TRANSIENT) != 0) != (trans != 0)) |
| | 2332 | { |
| | 2333 | ofs = next_ofs; |
| | 2334 | continue; |
| | 2335 | } |
| | 2336 | |
| | 2337 | /* |
| | 2338 | * if this would push us over the limit, stop here and start a |
| | 2339 | * new block |
| | 2340 | */ |
| | 2341 | if (block_size + siz > 64000L) |
| | 2342 | break; |
| | 2343 | |
| | 2344 | /* |
| | 2345 | * We must sort the property table, in order of ascending |
| | 2346 | * property ID, before we write the image file. We had to |
| | 2347 | * wait until now to do this, because the final property ID |
| | 2348 | * assignments aren't made until link time. |
| | 2349 | */ |
| | 2350 | write_prop_cnt = sort_object_prop_table(os, ofs); |
| | 2351 | |
| | 2352 | /* note the original property count */ |
| | 2353 | orig_prop_cnt = CTPNStmObject::get_stream_prop_cnt(os, ofs); |
| | 2354 | |
| | 2355 | /* |
| | 2356 | * Then temporarily pdate the property count in the stream, in |
| | 2357 | * case we changed it in the sorting process. |
| | 2358 | * |
| | 2359 | * Calculate the new size of the data to write. Note that we |
| | 2360 | * must add in the size of the T3 metaclass header, since this |
| | 2361 | * isn't reflected in the data size. |
| | 2362 | */ |
| | 2363 | write_size = |
| | 2364 | CTPNStmObject::set_stream_prop_cnt(os, ofs, write_prop_cnt) |
| | 2365 | + TCT3_META_HEADER_SIZE; |
| | 2366 | |
| | 2367 | /* |
| | 2368 | * if this is the first object in this block, write the |
| | 2369 | * block header |
| | 2370 | */ |
| | 2371 | if (cnt == 0) |
| | 2372 | image_writer->begin_objs_block(meta_idx, FALSE, trans); |
| | 2373 | |
| | 2374 | /* |
| | 2375 | * skip past our internal header - we don't want to write |
| | 2376 | * our internal header to the image file, since this was |
| | 2377 | * purely for our own use in the compiler and linker |
| | 2378 | */ |
| | 2379 | ofs += TCT3_OBJ_INTERNHDR_SIZE; |
| | 2380 | |
| | 2381 | /* |
| | 2382 | * write the object data; write the size returned from |
| | 2383 | * sorting the property table, which might be different than |
| | 2384 | * the original block data size in the stream, because we |
| | 2385 | * might have compressed the property table |
| | 2386 | */ |
| | 2387 | for (rem_len = write_size ; rem_len != 0 ; ) |
| | 2388 | { |
| | 2389 | const char *p; |
| | 2390 | ulong avail_len; |
| | 2391 | |
| | 2392 | /* get the next block */ |
| | 2393 | p = os->get_block_ptr(ofs, rem_len, &avail_len); |
| | 2394 | |
| | 2395 | /* write it out */ |
| | 2396 | image_writer->write_objs_bytes(p, avail_len); |
| | 2397 | |
| | 2398 | /* move past this block */ |
| | 2399 | ofs += avail_len; |
| | 2400 | rem_len -= avail_len; |
| | 2401 | } |
| | 2402 | |
| | 2403 | /* count the object */ |
| | 2404 | ++cnt; |
| | 2405 | |
| | 2406 | /* restore the original stream property count */ |
| | 2407 | CTPNStmObject::set_stream_prop_cnt(os, orig_ofs, orig_prop_cnt); |
| | 2408 | |
| | 2409 | /* move on to the next block */ |
| | 2410 | ofs = next_ofs; |
| | 2411 | } |
| | 2412 | |
| | 2413 | /* if we wrote any objects, end the block */ |
| | 2414 | if (cnt != 0) |
| | 2415 | image_writer->end_objs_block(cnt); |
| | 2416 | |
| | 2417 | /* move on to the next block */ |
| | 2418 | start_ofs = ofs; |
| | 2419 | } |
| | 2420 | } |
| | 2421 | |
| | 2422 | /* ------------------------------------------------------------------------ */ |
| | 2423 | /* |
| | 2424 | * Write an object stream of non-TADS objects to the image file |
| | 2425 | */ |
| | 2426 | void CTcGenTarg::write_nontads_objs_to_image(CTcDataStream *os, |
| | 2427 | CVmImageWriter *image_writer, |
| | 2428 | int meta_idx, int large_objs) |
| | 2429 | { |
| | 2430 | ulong start_ofs; |
| | 2431 | |
| | 2432 | /* keep going until we've written the whole file */ |
| | 2433 | for (start_ofs = 0 ; start_ofs < os->get_ofs() ; ) |
| | 2434 | { |
| | 2435 | ulong ofs; |
| | 2436 | uint siz; |
| | 2437 | uint cnt; |
| | 2438 | uint block_size; |
| | 2439 | |
| | 2440 | /* |
| | 2441 | * Scan the stream. Each entry in the stream is either a small or |
| | 2442 | * large object record,, which means that it starts with the |
| | 2443 | * object ID (UINT4) and the length (UINT2 for small, UINT4 for |
| | 2444 | * large) of the metaclass-specific data, which is then followed |
| | 2445 | * by the metaclass data. |
| | 2446 | * |
| | 2447 | * Include as many objects as we can while staying within our |
| | 2448 | * approximately 64k limit, if this is a small-format block; fill |
| | 2449 | * the block without limit if this is a large-format block. |
| | 2450 | */ |
| | 2451 | for (block_size = 0, ofs = start_ofs, cnt = 0 ; ; ) |
| | 2452 | { |
| | 2453 | ulong rem_len; |
| | 2454 | ulong next_ofs; |
| | 2455 | |
| | 2456 | /* if we've reached the end of the stream, we're done */ |
| | 2457 | if (ofs >= os->get_ofs()) |
| | 2458 | break; |
| | 2459 | |
| | 2460 | /* |
| | 2461 | * get the size of this block - this is the |
| | 2462 | * metaclass-specific data size at offset 4 in the T3 |
| | 2463 | * metaclass header, plus the size of the T3 metaclass |
| | 2464 | * header |
| | 2465 | */ |
| | 2466 | if (large_objs) |
| | 2467 | { |
| | 2468 | /* |
| | 2469 | * Get the 32-bit size value. Note that we don't worry |
| | 2470 | * about limiting the overall block size to 64k when we're |
| | 2471 | * writing a "large" object block. |
| | 2472 | */ |
| | 2473 | siz = (ulong)os->readu4_at(ofs + 4) |
| | 2474 | + TCT3_LARGE_META_HEADER_SIZE; |
| | 2475 | } |
| | 2476 | else |
| | 2477 | { |
| | 2478 | /* get the 16-bit size value */ |
| | 2479 | siz = (ulong)os->read2_at(ofs + 4) |
| | 2480 | + TCT3_META_HEADER_SIZE; |
| | 2481 | |
| | 2482 | /* |
| | 2483 | * Since this is a small-object block, limit the aggregate |
| | 2484 | * size of the entire block to 64k. So, if this block |
| | 2485 | * would push us over the 64k aggregate for the block, |
| | 2486 | * start a new OBJS block with this object. |
| | 2487 | */ |
| | 2488 | if (cnt != 0 && block_size + siz > 64000L) |
| | 2489 | break; |
| | 2490 | } |
| | 2491 | |
| | 2492 | /* |
| | 2493 | * if this is the first object in this block, write the |
| | 2494 | * block header - the dictionary uses large object headers, |
| | 2495 | * so note that |
| | 2496 | */ |
| | 2497 | if (cnt == 0) |
| | 2498 | image_writer->begin_objs_block(meta_idx, large_objs, FALSE); |
| | 2499 | |
| | 2500 | /* calculate the offset of the next block */ |
| | 2501 | next_ofs = ofs + siz; |
| | 2502 | |
| | 2503 | /* write the object data */ |
| | 2504 | for (rem_len = siz ; rem_len != 0 ; ) |
| | 2505 | { |
| | 2506 | const char *p; |
| | 2507 | ulong avail_len; |
| | 2508 | |
| | 2509 | /* get the next block */ |
| | 2510 | p = os->get_block_ptr(ofs, rem_len, &avail_len); |
| | 2511 | |
| | 2512 | /* write it out */ |
| | 2513 | image_writer->write_objs_bytes(p, avail_len); |
| | 2514 | |
| | 2515 | /* move past this block */ |
| | 2516 | ofs += avail_len; |
| | 2517 | rem_len -= avail_len; |
| | 2518 | } |
| | 2519 | |
| | 2520 | /* count the object */ |
| | 2521 | ++cnt; |
| | 2522 | |
| | 2523 | /* move on to the next block */ |
| | 2524 | ofs = next_ofs; |
| | 2525 | } |
| | 2526 | |
| | 2527 | /* if we wrote any objects, end the block */ |
| | 2528 | if (cnt != 0) |
| | 2529 | image_writer->end_objs_block(cnt); |
| | 2530 | |
| | 2531 | /* move on to the next block */ |
| | 2532 | start_ofs = ofs; |
| | 2533 | } |
| | 2534 | } |
| | 2535 | |
| | 2536 | |
| | 2537 | /* ------------------------------------------------------------------------ */ |
| | 2538 | /* |
| | 2539 | * Property comparison callback function for qsort() when invoked from |
| | 2540 | * sort_object_prop_table() |
| | 2541 | */ |
| | 2542 | //extern "C" int prop_compare(const void *p1, const void *p2); |
| | 2543 | extern "C" { |
| | 2544 | static int prop_compare(const void *p1, const void *p2) |
| | 2545 | { |
| | 2546 | uint id1, id2; |
| | 2547 | |
| | 2548 | /* get the ID's */ |
| | 2549 | id1 = osrp2(p1); |
| | 2550 | id2 = osrp2(p2); |
| | 2551 | |
| | 2552 | /* compare them and return the result */ |
| | 2553 | return (id1 < id2 ? -1 : id1 == id2 ? 0 : 1); |
| | 2554 | } |
| | 2555 | } |
| | 2556 | |
| | 2557 | /* |
| | 2558 | * Sort an object's property table. This puts the property table into |
| | 2559 | * order of ascending property ID, and deletes any unused properties from |
| | 2560 | * the table. |
| | 2561 | * |
| | 2562 | * Note that we do NOT update the stream to indicate the reduced number of |
| | 2563 | * properties if we delete any properties. Instead, we simply return the |
| | 2564 | * new number of properties. |
| | 2565 | */ |
| | 2566 | size_t CTcGenTarg::sort_object_prop_table(CTcDataStream *os, ulong start_ofs) |
| | 2567 | { |
| | 2568 | uint prop_table_size; |
| | 2569 | ulong orig_prop_cnt; |
| | 2570 | uint prop_cnt; |
| | 2571 | ulong prop_ofs; |
| | 2572 | size_t src, dst; |
| | 2573 | |
| | 2574 | /* read the number of properties from the header */ |
| | 2575 | prop_cnt = CTPNStmObject::get_stream_prop_cnt(os, start_ofs); |
| | 2576 | |
| | 2577 | /* remember the original property count, in case we delete unused slots */ |
| | 2578 | orig_prop_cnt = prop_cnt; |
| | 2579 | |
| | 2580 | /* calculate the property table size */ |
| | 2581 | prop_table_size = prop_cnt * TCT3_TADSOBJ_PROP_SIZE; |
| | 2582 | |
| | 2583 | /* get the offset of the first property */ |
| | 2584 | prop_ofs = CTPNStmObject::get_stream_first_prop_ofs(os, start_ofs); |
| | 2585 | |
| | 2586 | /* reallocate the sort buffer if necessary */ |
| | 2587 | if (prop_table_size > sort_buf_size_) |
| | 2588 | { |
| | 2589 | /* increase the sort buffer size to the next 4k increment */ |
| | 2590 | sort_buf_size_ = (prop_table_size + 4095) & ~4096; |
| | 2591 | |
| | 2592 | /* reallocate the buffer */ |
| | 2593 | sort_buf_ = (char *)t3realloc(sort_buf_, sort_buf_size_); |
| | 2594 | if (sort_buf_ == 0 || sort_buf_size_ < prop_table_size) |
| | 2595 | G_tok->throw_internal_error(TCERR_CODEGEN_NO_MEM); |
| | 2596 | } |
| | 2597 | |
| | 2598 | /* extract the table into our buffer */ |
| | 2599 | os->copy_to_buf(sort_buf_, prop_ofs, prop_table_size); |
| | 2600 | |
| | 2601 | /* |
| | 2602 | * Compress the table by removing any properties that have been |
| | 2603 | * marked as deleted -- if we had any 'modify + replace' properties |
| | 2604 | * that we resolved at link time, we will have marked those |
| | 2605 | * properties for deletion by setting their property ID's to zero in |
| | 2606 | * the table. Scan the table for any such properties and remove |
| | 2607 | * them now. |
| | 2608 | */ |
| | 2609 | for (src = dst = 0, prop_cnt = 0 ; src < prop_table_size ; |
| | 2610 | src += TCT3_TADSOBJ_PROP_SIZE) |
| | 2611 | { |
| | 2612 | /* if this property isn't marked for deletion, keep it */ |
| | 2613 | if (osrp2(sort_buf_ + src) != VM_INVALID_PROP) |
| | 2614 | { |
| | 2615 | /* |
| | 2616 | * we're keeping it - if we can move it to a lower table |
| | 2617 | * position, copy the data to the new position, otherwise |
| | 2618 | * leave it alone |
| | 2619 | */ |
| | 2620 | if (src != dst) |
| | 2621 | memcpy(sort_buf_ + dst, sort_buf_ + src, |
| | 2622 | TCT3_TADSOBJ_PROP_SIZE); |
| | 2623 | |
| | 2624 | /* |
| | 2625 | * advance the destination pointer past this slot, since |
| | 2626 | * we're going to keep the data in the slot |
| | 2627 | */ |
| | 2628 | dst += TCT3_TADSOBJ_PROP_SIZE; |
| | 2629 | |
| | 2630 | /* count this property, since we're keeping it */ |
| | 2631 | ++prop_cnt; |
| | 2632 | } |
| | 2633 | } |
| | 2634 | |
| | 2635 | /* sort the table */ |
| | 2636 | qsort(sort_buf_, prop_cnt, TCT3_TADSOBJ_PROP_SIZE, &prop_compare); |
| | 2637 | |
| | 2638 | /* add back any unused slots after all of the sorted slots */ |
| | 2639 | for ( ; dst < prop_table_size ; dst += TCT3_TADSOBJ_PROP_SIZE) |
| | 2640 | oswp2(sort_buf_ + dst, VM_INVALID_PROP); |
| | 2641 | |
| | 2642 | /* put the sorted table back in the buffer */ |
| | 2643 | os->write_at(prop_ofs, sort_buf_, prop_table_size); |
| | 2644 | |
| | 2645 | /* return the (possibly reduced) number of properties */ |
| | 2646 | return prop_cnt; |
| | 2647 | } |
| | 2648 | |
| | 2649 | |
| | 2650 | /* |
| | 2651 | * callback context for enumerating a dictionary |
| | 2652 | */ |
| | 2653 | struct enum_dict_ctx |
| | 2654 | { |
| | 2655 | /* number of entries written so far */ |
| | 2656 | uint cnt; |
| | 2657 | }; |
| | 2658 | |
| | 2659 | /* |
| | 2660 | * Generate code for a dictionary object |
| | 2661 | */ |
| | 2662 | void CTcGenTarg::gen_code_for_dict(CTcDictEntry *dict) |
| | 2663 | { |
| | 2664 | long size_ofs; |
| | 2665 | long entry_cnt_ofs; |
| | 2666 | long end_ofs; |
| | 2667 | enum_dict_ctx ctx; |
| | 2668 | |
| | 2669 | /* |
| | 2670 | * Write the OBJS header - object ID plus byte count for |
| | 2671 | * metaclass-specific data (use a placeholder length for now) |
| | 2672 | */ |
| | 2673 | G_dict_stream->write4(dict->get_sym()->get_obj_id()); |
| | 2674 | size_ofs = G_dict_stream->get_ofs(); |
| | 2675 | G_dict_stream->write4(0); |
| | 2676 | |
| | 2677 | /* |
| | 2678 | * Write the metaclass-specific data for the 'dictionary' metaclass |
| | 2679 | */ |
| | 2680 | |
| | 2681 | /* write a nil comparator object initially */ |
| | 2682 | G_dict_stream->write4(0); |
| | 2683 | |
| | 2684 | /* write a placeholder for the entry count */ |
| | 2685 | entry_cnt_ofs = G_dict_stream->get_ofs(); |
| | 2686 | G_dict_stream->write2(0); |
| | 2687 | |
| | 2688 | /* write the dictionary entries */ |
| | 2689 | ctx.cnt = 0; |
| | 2690 | dict->get_hash_table()->enum_entries(&enum_dict_gen_cb, &ctx); |
| | 2691 | |
| | 2692 | /* remember the ending offset of the table */ |
| | 2693 | end_ofs = G_dict_stream->get_ofs(); |
| | 2694 | |
| | 2695 | /* go back and fix up the total size of the object data */ |
| | 2696 | G_dict_stream->write4_at(size_ofs, end_ofs - size_ofs - 4); |
| | 2697 | |
| | 2698 | /* fix up the dictionary entry count */ |
| | 2699 | G_dict_stream->write2_at(entry_cnt_ofs, ctx.cnt); |
| | 2700 | } |
| | 2701 | |
| | 2702 | /* |
| | 2703 | * Callback - enumerate dictionary entries for code generation |
| | 2704 | */ |
| | 2705 | void CTcGenTarg::enum_dict_gen_cb(void *ctx0, CVmHashEntry *entry0) |
| | 2706 | { |
| | 2707 | enum_dict_ctx *ctx = (enum_dict_ctx *)ctx0; |
| | 2708 | CVmHashEntryPrsDict *entry = (CVmHashEntryPrsDict *)entry0; |
| | 2709 | char buf[255]; |
| | 2710 | size_t len; |
| | 2711 | char *p; |
| | 2712 | size_t rem; |
| | 2713 | uint cnt; |
| | 2714 | CTcPrsDictItem *item; |
| | 2715 | |
| | 2716 | /* count this entry */ |
| | 2717 | ++(ctx->cnt); |
| | 2718 | |
| | 2719 | /* limit the key length to 255 bytes */ |
| | 2720 | len = entry->getlen(); |
| | 2721 | if (len > 255) |
| | 2722 | len = 255; |
| | 2723 | |
| | 2724 | /* copy the entry to our buffer */ |
| | 2725 | memcpy(buf, entry->getstr(), len); |
| | 2726 | |
| | 2727 | /* apply the XOR obfuscation to the key text */ |
| | 2728 | for (p = buf, rem = len ; rem != 0 ; ++p, --rem) |
| | 2729 | *p ^= 0xBD; |
| | 2730 | |
| | 2731 | /* write the length of the key followed by the key string */ |
| | 2732 | G_dict_stream->write((uchar)len); |
| | 2733 | G_dict_stream->write(buf, len); |
| | 2734 | |
| | 2735 | /* count the items in this entry */ |
| | 2736 | for (cnt = 0, item = entry->get_list() ; item != 0 ; |
| | 2737 | ++cnt, item = item->nxt_) ; |
| | 2738 | |
| | 2739 | /* write the number of entries */ |
| | 2740 | G_dict_stream->write2(cnt); |
| | 2741 | |
| | 2742 | /* write the entries */ |
| | 2743 | for (item = entry->get_list() ; item != 0 ; item = item->nxt_) |
| | 2744 | { |
| | 2745 | /* write the object ID and property ID of this entry */ |
| | 2746 | G_dict_stream->write4(item->obj_); |
| | 2747 | G_dict_stream->write2(item->prop_); |
| | 2748 | } |
| | 2749 | } |
| | 2750 | |
| | 2751 | /* |
| | 2752 | * Generate code for a grammar production |
| | 2753 | */ |
| | 2754 | void CTcGenTarg::gen_code_for_gramprod(CTcGramProdEntry *prod) |
| | 2755 | { |
| | 2756 | long size_ofs; |
| | 2757 | long end_ofs; |
| | 2758 | uint cnt; |
| | 2759 | CTcGramProdAlt *alt; |
| | 2760 | CTcDataStream *str = G_gramprod_stream; |
| | 2761 | |
| | 2762 | /* |
| | 2763 | * write the OBJS header - object ID plus byte count for |
| | 2764 | * metaclass-specific data (use a placeholder length for now) |
| | 2765 | */ |
| | 2766 | str->write4(prod->get_prod_sym()->get_obj_id()); |
| | 2767 | size_ofs = str->get_ofs(); |
| | 2768 | str->write4(0); |
| | 2769 | |
| | 2770 | /* |
| | 2771 | * Write the metaclass-specific data for the 'grammar-production' |
| | 2772 | * metaclass |
| | 2773 | */ |
| | 2774 | |
| | 2775 | /* count the alternatives */ |
| | 2776 | for (cnt = 0, alt = prod->get_alt_head() ; alt != 0 ; |
| | 2777 | ++cnt, alt = alt->get_next()) ; |
| | 2778 | |
| | 2779 | /* |
| | 2780 | * If this production has no alternatives and was not explicitly |
| | 2781 | * declared, flag an error indicating that the production is |
| | 2782 | * undeclared. We treat this as an error because there's a good chance |
| | 2783 | * that the an alternative referring to the production misspelled the |
| | 2784 | * name. If the production was explicitly declared, then we have |
| | 2785 | * sufficient confirmation that the name is correct, so no error is |
| | 2786 | * indicated. |
| | 2787 | */ |
| | 2788 | if (cnt == 0 && !prod->is_declared()) |
| | 2789 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 2790 | TCERR_GRAMPROD_HAS_NO_ALTS, |
| | 2791 | (int)prod->get_prod_sym()->get_sym_len(), |
| | 2792 | prod->get_prod_sym()->get_sym()); |
| | 2793 | |
| | 2794 | /* |
| | 2795 | * The count has to fit in 16 bits; it's surprisingly easy to exceed |
| | 2796 | * this by using the power of permutation (with nested '|' |
| | 2797 | * alternatives), so check for overflow and flag an error. Even though |
| | 2798 | * it's not hard to exceed the limit, it's not desirable to create so |
| | 2799 | * many permutations, so the limit isn't really in need of being |
| | 2800 | * raised; it's better to rewrite a rule with a huge number of |
| | 2801 | * permutations using sub-productions. |
| | 2802 | */ |
| | 2803 | if (cnt > 65535) |
| | 2804 | G_tcmain->log_error(0, 0, TC_SEV_ERROR, |
| | 2805 | TCERR_GRAMPROD_TOO_MANY_ALTS, |
| | 2806 | (int)prod->get_prod_sym()->get_sym_len(), |
| | 2807 | prod->get_prod_sym()->get_sym()); |
| | 2808 | |
| | 2809 | /* write the number of alternatives */ |
| | 2810 | str->write2(cnt); |
| | 2811 | |
| | 2812 | /* write the alternatives */ |
| | 2813 | for (alt = prod->get_alt_head() ; alt != 0 ; alt = alt->get_next()) |
| | 2814 | { |
| | 2815 | CTcGramProdTok *tok; |
| | 2816 | |
| | 2817 | /* write the score and badness for the alternative */ |
| | 2818 | str->write2(alt->get_score()); |
| | 2819 | str->write2(alt->get_badness()); |
| | 2820 | |
| | 2821 | /* write the processor object ID for this alternative */ |
| | 2822 | str->write4(alt->get_processor_obj()->get_obj_id()); |
| | 2823 | |
| | 2824 | /* count the tokens in this alternative */ |
| | 2825 | for (cnt = 0, tok = alt->get_tok_head() ; tok != 0 ; |
| | 2826 | ++cnt, tok = tok->get_next()) ; |
| | 2827 | |
| | 2828 | /* write the token count */ |
| | 2829 | str->write2(cnt); |
| | 2830 | |
| | 2831 | /* write the tokens */ |
| | 2832 | for (tok = alt->get_tok_head() ; tok != 0 ; tok = tok->get_next()) |
| | 2833 | { |
| | 2834 | size_t idx; |
| | 2835 | |
| | 2836 | /* write the property association */ |
| | 2837 | str->write2((uint)tok->get_prop_assoc()); |
| | 2838 | |
| | 2839 | /* write the token data */ |
| | 2840 | switch(tok->get_type()) |
| | 2841 | { |
| | 2842 | case TCGRAM_PROD: |
| | 2843 | /* write the type */ |
| | 2844 | str->write((uchar)VMGRAM_MATCH_PROD); |
| | 2845 | |
| | 2846 | /* write the sub-production object ID */ |
| | 2847 | str->write4((ulong)tok->getval_prod()->get_obj_id()); |
| | 2848 | break; |
| | 2849 | |
| | 2850 | case TCGRAM_PART_OF_SPEECH: |
| | 2851 | /* write the type */ |
| | 2852 | str->write((uchar)VMGRAM_MATCH_SPEECH); |
| | 2853 | |
| | 2854 | /* write the part-of-speech property */ |
| | 2855 | str->write2((uint)tok->getval_part_of_speech()); |
| | 2856 | break; |
| | 2857 | |
| | 2858 | case TCGRAM_PART_OF_SPEECH_LIST: |
| | 2859 | /* write the type */ |
| | 2860 | str->write((uchar)VMGRAM_MATCH_NSPEECH); |
| | 2861 | |
| | 2862 | /* write the number of elements in the property list */ |
| | 2863 | str->write2((uint)tok->getval_part_list_len()); |
| | 2864 | |
| | 2865 | /* write each element */ |
| | 2866 | for (idx = 0 ; idx < tok->getval_part_list_len() ; ++idx) |
| | 2867 | str->write2((uint)tok->getval_part_list_ele(idx)); |
| | 2868 | |
| | 2869 | /* done */ |
| | 2870 | break; |
| | 2871 | |
| | 2872 | case TCGRAM_LITERAL: |
| | 2873 | /* write the type */ |
| | 2874 | str->write((uchar)VMGRAM_MATCH_LITERAL); |
| | 2875 | |
| | 2876 | /* write the string length prefix */ |
| | 2877 | str->write2(tok->getval_literal_len()); |
| | 2878 | |
| | 2879 | /* write the string text */ |
| | 2880 | str->write(tok->getval_literal_txt(), |
| | 2881 | tok->getval_literal_len()); |
| | 2882 | |
| | 2883 | /* |
| | 2884 | * add the word to the dictionary that was active when the |
| | 2885 | * alternative was defined |
| | 2886 | */ |
| | 2887 | if (alt->get_dict() != 0) |
| | 2888 | { |
| | 2889 | /* |
| | 2890 | * there's a dictionary - add the word, associating it |
| | 2891 | * with the production object and with the parser's |
| | 2892 | * miscVocab property |
| | 2893 | */ |
| | 2894 | alt->get_dict()->add_word( |
| | 2895 | tok->getval_literal_txt(), tok->getval_literal_len(), |
| | 2896 | FALSE, prod->get_prod_sym()->get_obj_id(), |
| | 2897 | G_prs->get_miscvocab_prop()); |
| | 2898 | } |
| | 2899 | break; |
| | 2900 | |
| | 2901 | case TCGRAM_TOKEN_TYPE: |
| | 2902 | /* write the type */ |
| | 2903 | str->write((uchar)VMGRAM_MATCH_TOKTYPE); |
| | 2904 | |
| | 2905 | /* write the enum ID of the token */ |
| | 2906 | str->write4(tok->getval_token_type()); |
| | 2907 | break; |
| | 2908 | |
| | 2909 | case TCGRAM_STAR: |
| | 2910 | /* write the type - there's no additional data */ |
| | 2911 | str->write((uchar)VMGRAM_MATCH_STAR); |
| | 2912 | break; |
| | 2913 | |
| | 2914 | default: |
| | 2915 | assert(FALSE); |
| | 2916 | break; |
| | 2917 | } |
| | 2918 | } |
| | 2919 | } |
| | 2920 | |
| | 2921 | /* remember the ending offset of the object data */ |
| | 2922 | end_ofs = str->get_ofs(); |
| | 2923 | |
| | 2924 | /* go back and fix up the total size of the object data */ |
| | 2925 | str->write4_at(size_ofs, end_ofs - size_ofs - 4); |
| | 2926 | } |
| | 2927 | |
| | 2928 | |
| | 2929 | /* ------------------------------------------------------------------------ */ |
| | 2930 | /* |
| | 2931 | * Data Stream Layout Manager |
| | 2932 | */ |
| | 2933 | |
| | 2934 | /* |
| | 2935 | * calculate the size of the pool pages, given the size of the largest |
| | 2936 | * single item |
| | 2937 | */ |
| | 2938 | void CTcStreamLayout::calc_layout(CTcDataStream *ds, ulong max_len, |
| | 2939 | int is_first) |
| | 2940 | { |
| | 2941 | ulong rem; |
| | 2942 | ulong free_ofs; |
| | 2943 | CTcStreamAnchor *anchor; |
| | 2944 | |
| | 2945 | /* if this is the first page, handle some things specially */ |
| | 2946 | if (is_first) |
| | 2947 | { |
| | 2948 | ulong pgsiz; |
| | 2949 | |
| | 2950 | /* |
| | 2951 | * Starting at 2k, look for a page size that will fit the |
| | 2952 | * desired minimum size. |
| | 2953 | */ |
| | 2954 | for (pgsiz = 2048 ; pgsiz < max_len ; pgsiz <<= 1) ; |
| | 2955 | |
| | 2956 | /* remember our selected page size */ |
| | 2957 | page_size_ = pgsiz; |
| | 2958 | |
| | 2959 | /* start at the bottom of the first page */ |
| | 2960 | rem = pgsiz; |
| | 2961 | free_ofs = 0; |
| | 2962 | page_cnt_ = 1; |
| | 2963 | } |
| | 2964 | else |
| | 2965 | { |
| | 2966 | /* |
| | 2967 | * this isn't the first page - if there are no anchors, don't |
| | 2968 | * bother adding anything |
| | 2969 | */ |
| | 2970 | if (ds->get_first_anchor() == 0) |
| | 2971 | return; |
| | 2972 | |
| | 2973 | /* |
| | 2974 | * start at the end of the last existing page - this will ensure |
| | 2975 | * that everything added from the new stream will go onto a |
| | 2976 | * brand new page after everything from the previous stream |
| | 2977 | */ |
| | 2978 | rem = 0; |
| | 2979 | free_ofs = page_size_ * page_cnt_; |
| | 2980 | } |
| | 2981 | |
| | 2982 | /* |
| | 2983 | * Run through the list of stream anchors and calculate the layout. |
| | 2984 | * For each item, assign its final pool address and apply its |
| | 2985 | * fixups. |
| | 2986 | */ |
| | 2987 | for (anchor = ds->get_first_anchor() ; anchor != 0 ; |
| | 2988 | anchor = anchor->nxt_) |
| | 2989 | { |
| | 2990 | ulong len; |
| | 2991 | |
| | 2992 | /* |
| | 2993 | * if this anchor has been marked as replaced, don't include it |
| | 2994 | * in our calculations, because we don't want to include this |
| | 2995 | * block in the image file |
| | 2996 | */ |
| | 2997 | if (anchor->is_replaced()) |
| | 2998 | continue; |
| | 2999 | |
| | 3000 | /* |
| | 3001 | * if this item fits on the current page, assign it the next |
| | 3002 | * sequential address; otherwise, go to the next page |
| | 3003 | * |
| | 3004 | * if this anchor is at the dividing point, put it on the next |
| | 3005 | * page, unless we just started a new page |
| | 3006 | */ |
| | 3007 | len = anchor->get_len(ds); |
| | 3008 | if (len > rem) |
| | 3009 | { |
| | 3010 | /* |
| | 3011 | * we must start the next page - skip to the next page by |
| | 3012 | * moving past the remaining free space on this page |
| | 3013 | */ |
| | 3014 | free_ofs += rem; |
| | 3015 | |
| | 3016 | /* count the new page */ |
| | 3017 | ++page_cnt_; |
| | 3018 | |
| | 3019 | /* the whole next page is available to us now */ |
| | 3020 | rem = page_size_; |
| | 3021 | } |
| | 3022 | |
| | 3023 | /* |
| | 3024 | * set the anchor's final address, which will apply fixups for |
| | 3025 | * the object's fixup list |
| | 3026 | */ |
| | 3027 | anchor->set_addr(free_ofs); |
| | 3028 | |
| | 3029 | /* advance past this block */ |
| | 3030 | free_ofs += len; |
| | 3031 | rem -= len; |
| | 3032 | } |
| | 3033 | |
| | 3034 | /* if there's no data at all, we have zero pages */ |
| | 3035 | if (free_ofs == 0) |
| | 3036 | page_cnt_ = 0; |
| | 3037 | } |
| | 3038 | |
| | 3039 | |
| | 3040 | /* |
| | 3041 | * Write our stream to an image file |
| | 3042 | */ |
| | 3043 | void CTcStreamLayout::write_to_image(CTcDataStream **ds_arr, size_t ds_cnt, |
| | 3044 | CVmImageWriter *image_writer, |
| | 3045 | int pool_id, uchar xor_mask) |
| | 3046 | { |
| | 3047 | CTcStreamAnchor *anchor; |
| | 3048 | ulong free_ofs; |
| | 3049 | ulong next_page_start; |
| | 3050 | int pgnum; |
| | 3051 | |
| | 3052 | /* write the constant pool definition block - the pool's ID is 2 */ |
| | 3053 | image_writer->write_pool_def(pool_id, page_cnt_, page_size_, TRUE); |
| | 3054 | |
| | 3055 | /* |
| | 3056 | * start out before the first page - the next page starts with the |
| | 3057 | * item at offset zero |
| | 3058 | */ |
| | 3059 | pgnum = 0; |
| | 3060 | next_page_start = 0; |
| | 3061 | |
| | 3062 | /* run through each stream */ |
| | 3063 | for ( ; ds_cnt != 0 ; ++ds_arr, --ds_cnt) |
| | 3064 | { |
| | 3065 | CTcDataStream *ds; |
| | 3066 | |
| | 3067 | /* get the current stream */ |
| | 3068 | ds = *ds_arr; |
| | 3069 | |
| | 3070 | /* run through the anchor list for this stream */ |
| | 3071 | for (anchor = ds->get_first_anchor() ; anchor != 0 ; |
| | 3072 | anchor = anchor->nxt_) |
| | 3073 | { |
| | 3074 | ulong len; |
| | 3075 | ulong stream_ofs; |
| | 3076 | ulong addr; |
| | 3077 | |
| | 3078 | /* |
| | 3079 | * if this anchor is marked as replaced, skip it entirely - |
| | 3080 | * we omit replaced blocks from the image file, because |
| | 3081 | * they're completely unreachable |
| | 3082 | */ |
| | 3083 | if (anchor->is_replaced()) |
| | 3084 | continue; |
| | 3085 | |
| | 3086 | /* |
| | 3087 | * if this item's assigned address is on the next page, move |
| | 3088 | * to the next page |
| | 3089 | */ |
| | 3090 | len = anchor->get_len(ds); |
| | 3091 | addr = anchor->get_addr(); |
| | 3092 | if (addr == next_page_start) |
| | 3093 | { |
| | 3094 | /* if this isn't the first page, close the previous page */ |
| | 3095 | if (pgnum != 0) |
| | 3096 | image_writer->end_pool_page(); |
| | 3097 | |
| | 3098 | /* start the new page */ |
| | 3099 | image_writer->begin_pool_page(pool_id, pgnum, TRUE, xor_mask); |
| | 3100 | |
| | 3101 | /* this item is at the start of the new page */ |
| | 3102 | free_ofs = next_page_start; |
| | 3103 | |
| | 3104 | /* count the new page */ |
| | 3105 | ++pgnum; |
| | 3106 | |
| | 3107 | /* calculate the address of the start of the next page */ |
| | 3108 | next_page_start += page_size_; |
| | 3109 | } |
| | 3110 | |
| | 3111 | /* advance past this block */ |
| | 3112 | free_ofs += len; |
| | 3113 | |
| | 3114 | /* |
| | 3115 | * write the data from the stream to the image file - we |
| | 3116 | * must iterate over the chunks the code stream returns, |
| | 3117 | * since it might not be able to return the entire block in |
| | 3118 | * a single operation |
| | 3119 | */ |
| | 3120 | for (stream_ofs = anchor->get_ofs() ; len != 0 ; ) |
| | 3121 | { |
| | 3122 | ulong cur; |
| | 3123 | const char *ptr; |
| | 3124 | |
| | 3125 | /* get the pointer to this chunk */ |
| | 3126 | ptr = ds->get_block_ptr(stream_ofs, len, &cur); |
| | 3127 | |
| | 3128 | /* write this chunk */ |
| | 3129 | image_writer->write_pool_page_bytes(ptr, cur, xor_mask); |
| | 3130 | |
| | 3131 | /* advance our pointers past this chunk */ |
| | 3132 | len -= cur; |
| | 3133 | stream_ofs += cur; |
| | 3134 | } |
| | 3135 | } |
| | 3136 | } |
| | 3137 | |
| | 3138 | /* if we started a page, end it */ |
| | 3139 | if (pgnum != 0) |
| | 3140 | image_writer->end_pool_page(); |
| | 3141 | } |
| | 3142 | |
| | 3143 | /* ------------------------------------------------------------------------ */ |
| | 3144 | /* |
| | 3145 | * Object Symbol subclass - image-file functions |
| | 3146 | */ |
| | 3147 | |
| | 3148 | /* |
| | 3149 | * mark the compiled data for the object as a 'class' object |
| | 3150 | */ |
| | 3151 | void CTcSymObj::mark_compiled_as_class() |
| | 3152 | { |
| | 3153 | uint flags; |
| | 3154 | CTcDataStream *str; |
| | 3155 | |
| | 3156 | /* get the appropriate stream for generating the data */ |
| | 3157 | str = get_stream(); |
| | 3158 | |
| | 3159 | /* get my original object flags */ |
| | 3160 | flags = CTPNStmObject::get_stream_obj_flags(str, stream_ofs_); |
| | 3161 | |
| | 3162 | /* add in the 'class' flag */ |
| | 3163 | flags |= TCT3_OBJFLG_CLASS; |
| | 3164 | |
| | 3165 | /* set the updated flags */ |
| | 3166 | CTPNStmObject::set_stream_obj_flags(str, stream_ofs_, flags); |
| | 3167 | } |
| | 3168 | |
| | 3169 | /* |
| | 3170 | * Delete a property from our modified base classes |
| | 3171 | */ |
| | 3172 | void CTcSymObj::delete_prop_from_mod_base(tctarg_prop_id_t prop_id) |
| | 3173 | { |
| | 3174 | uint prop_cnt; |
| | 3175 | uint i; |
| | 3176 | CTcDataStream *str; |
| | 3177 | |
| | 3178 | /* get the correct data stream */ |
| | 3179 | str = get_stream(); |
| | 3180 | |
| | 3181 | /* get the number of properties in the object */ |
| | 3182 | prop_cnt = CTPNStmObject::get_stream_prop_cnt(str, stream_ofs_); |
| | 3183 | |
| | 3184 | /* find the property in our property table */ |
| | 3185 | for (i = 0 ; i < prop_cnt ; ++i) |
| | 3186 | { |
| | 3187 | /* if this property ID matches, delete it */ |
| | 3188 | if (CTPNStmObject::get_stream_prop_id(str, stream_ofs_, i) |
| | 3189 | == prop_id) |
| | 3190 | { |
| | 3191 | /* delete the object by setting its ID to 'invalid' */ |
| | 3192 | CTPNStmObject::set_stream_prop_id(str, stream_ofs_, i, |
| | 3193 | VM_INVALID_PROP); |
| | 3194 | |
| | 3195 | /* |
| | 3196 | * there's no need to look any further - a property can |
| | 3197 | * occur only once in an object |
| | 3198 | */ |
| | 3199 | break; |
| | 3200 | } |
| | 3201 | } |
| | 3202 | } |
| | 3203 | |
| | 3204 | /* |
| | 3205 | * Build the dictionary |
| | 3206 | */ |
| | 3207 | void CTcSymObj::build_dictionary() |
| | 3208 | { |
| | 3209 | uint prop_cnt; |
| | 3210 | uint i; |
| | 3211 | |
| | 3212 | /* |
| | 3213 | * Inherit the default handling - this will explicitly add all |
| | 3214 | * superclass dictionary data into my own internal dictionary list, |
| | 3215 | * so that we don't have to worry at all about superclasses here. |
| | 3216 | * This will also add our words to my associated dictionary object. |
| | 3217 | */ |
| | 3218 | CTcSymObjBase::build_dictionary(); |
| | 3219 | |
| | 3220 | /* if I'm not a regular tads object, there's nothing to do here */ |
| | 3221 | if (metaclass_ != TC_META_TADSOBJ) |
| | 3222 | return; |
| | 3223 | |
| | 3224 | /* |
| | 3225 | * Examine my properties. Each time we find a property whose value |
| | 3226 | * is set to vocab-list, replace it with an actual list of strings |
| | 3227 | * for my vocabulary words associated with the property. |
| | 3228 | */ |
| | 3229 | |
| | 3230 | /* get the number of properties in the object */ |
| | 3231 | prop_cnt = CTPNStmObject::get_stream_prop_cnt(G_os, stream_ofs_); |
| | 3232 | |
| | 3233 | /* find the property in our property table */ |
| | 3234 | for (i = 0 ; i < prop_cnt ; ++i) |
| | 3235 | { |
| | 3236 | CTcConstVal val; |
| | 3237 | vm_datatype_t prop_type; |
| | 3238 | |
| | 3239 | /* get this property value */ |
| | 3240 | prop_type = CTPNStmObject::get_stream_prop_type(G_os, stream_ofs_, i); |
| | 3241 | |
| | 3242 | /* |
| | 3243 | * if it's a vocabulary list placeholder, replace it with the |
| | 3244 | * actual list of vocabulary strings |
| | 3245 | */ |
| | 3246 | if (prop_type == VM_VOCAB_LIST) |
| | 3247 | { |
| | 3248 | vm_prop_id_t prop_id; |
| | 3249 | CTcVocabEntry *entry; |
| | 3250 | CTPNList *lst; |
| | 3251 | ulong prop_val_ofs; |
| | 3252 | |
| | 3253 | /* get the property ID */ |
| | 3254 | prop_id = CTPNStmObject::get_stream_prop_id(G_os, stream_ofs_, i); |
| | 3255 | |
| | 3256 | /* get the value offset of this property */ |
| | 3257 | prop_val_ofs = CTPNStmObject:: |
| | 3258 | get_stream_prop_val_ofs(G_os, stream_ofs_, i); |
| | 3259 | |
| | 3260 | /* create a list */ |
| | 3261 | lst = new CTPNList(); |
| | 3262 | |
| | 3263 | /* |
| | 3264 | * scan my internal vocabulary list and add the entries |
| | 3265 | * associated with this property |
| | 3266 | */ |
| | 3267 | for (entry = vocab_ ; entry != 0 ; entry = entry->nxt_) |
| | 3268 | { |
| | 3269 | /* if this one matches our property, add it */ |
| | 3270 | if (entry->prop_ == prop_id) |
| | 3271 | { |
| | 3272 | CTcConstVal str_val; |
| | 3273 | CTcPrsNode *ele; |
| | 3274 | |
| | 3275 | /* create a string element */ |
| | 3276 | str_val.set_sstr(entry->txt_, entry->len_); |
| | 3277 | ele = new CTPNConst(&str_val); |
| | 3278 | |
| | 3279 | /* add it to the list */ |
| | 3280 | lst->add_element(ele); |
| | 3281 | } |
| | 3282 | } |
| | 3283 | |
| | 3284 | /* |
| | 3285 | * Overwrite the original property value with the new list. |
| | 3286 | * If the list is empty, this object doesn't define or |
| | 3287 | * inherit any vocabulary of this property at all, so we can |
| | 3288 | * clear the property entirely. |
| | 3289 | */ |
| | 3290 | if (lst->get_count() == 0) |
| | 3291 | { |
| | 3292 | /* |
| | 3293 | * delete the property from the object by setting its |
| | 3294 | * property ID to 'invalid' |
| | 3295 | */ |
| | 3296 | CTPNStmObject:: |
| | 3297 | set_stream_prop_id(G_os, stream_ofs_, i, VM_INVALID_PROP); |
| | 3298 | } |
| | 3299 | else |
| | 3300 | { |
| | 3301 | /* write the list value to the property */ |
| | 3302 | val.set_list(lst); |
| | 3303 | G_cg->write_const_as_dh(G_os, prop_val_ofs, &val); |
| | 3304 | } |
| | 3305 | } |
| | 3306 | } |
| | 3307 | } |
| | 3308 | |
| | 3309 | |
| | 3310 | /* ------------------------------------------------------------------------ */ |
| | 3311 | /* |
| | 3312 | * Symbol table entry routines for writing a symbol to the global symbol |
| | 3313 | * table in the debug records in the image file |
| | 3314 | */ |
| | 3315 | |
| | 3316 | /* |
| | 3317 | * write the symbol to an image file's global symbol table |
| | 3318 | */ |
| | 3319 | int CTcSymFunc::write_to_image_file_global(class CVmImageWriter *image_writer) |
| | 3320 | { |
| | 3321 | char buf[128]; |
| | 3322 | |
| | 3323 | /* build our extra data buffer */ |
| | 3324 | oswp4(buf, get_code_pool_addr()); |
| | 3325 | oswp2(buf + 4, get_argc()); |
| | 3326 | buf[6] = (is_varargs() != 0); |
| | 3327 | buf[7] = (has_retval() != 0); |
| | 3328 | |
| | 3329 | /* write the data */ |
| | 3330 | image_writer->write_gsym_entry(get_sym(), get_sym_len(), |
| | 3331 | (int)TC_SYM_FUNC, buf, 8); |
| | 3332 | |
| | 3333 | /* we wrote the symbol */ |
| | 3334 | return TRUE; |
| | 3335 | } |
| | 3336 | |
| | 3337 | /* |
| | 3338 | * write the symbol to an image file's global symbol table |
| | 3339 | */ |
| | 3340 | int CTcSymObj::write_to_image_file_global(class CVmImageWriter *image_writer) |
| | 3341 | { |
| | 3342 | char buf[128]; |
| | 3343 | |
| | 3344 | /* store our object ID in the extra data buffer */ |
| | 3345 | oswp4(buf, obj_id_); |
| | 3346 | |
| | 3347 | /* add our modifying object ID, if we have a modifying object */ |
| | 3348 | if (get_modifying_sym() != 0) |
| | 3349 | oswp4(buf + 4, get_modifying_sym()->get_obj_id()); |
| | 3350 | else |
| | 3351 | oswp4(buf + 4, 0); |
| | 3352 | |
| | 3353 | /* write the data */ |
| | 3354 | image_writer->write_gsym_entry(get_sym(), get_sym_len(), |
| | 3355 | (int)TC_SYM_OBJ, buf, 8); |
| | 3356 | |
| | 3357 | /* we wrote the symbol */ |
| | 3358 | return TRUE; |
| | 3359 | } |
| | 3360 | |
| | 3361 | /* |
| | 3362 | * write the symbol to an image file's global symbol table |
| | 3363 | */ |
| | 3364 | int CTcSymProp::write_to_image_file_global(class CVmImageWriter *image_writer) |
| | 3365 | { |
| | 3366 | char buf[128]; |
| | 3367 | |
| | 3368 | /* build our extra data buffer */ |
| | 3369 | oswp2(buf, (uint)get_prop()); |
| | 3370 | |
| | 3371 | /* write the data */ |
| | 3372 | image_writer->write_gsym_entry(get_sym(), get_sym_len(), |
| | 3373 | (int)TC_SYM_PROP, buf, 2); |
| | 3374 | |
| | 3375 | /* we wrote the symbol */ |
| | 3376 | return TRUE; |
| | 3377 | } |
| | 3378 | |
| | 3379 | /* |
| | 3380 | * write the symbol to an image file's global symbol table |
| | 3381 | */ |
| | 3382 | int CTcSymEnum::write_to_image_file_global(class CVmImageWriter *image_writer) |
| | 3383 | { |
| | 3384 | char buf[128]; |
| | 3385 | |
| | 3386 | /* build our extra data buffer */ |
| | 3387 | oswp4(buf, get_enum_id()); |
| | 3388 | |
| | 3389 | /* build our flags */ |
| | 3390 | buf[4] = 0; |
| | 3391 | if (is_token_) |
| | 3392 | buf[4] |= 1; |
| | 3393 | |
| | 3394 | /* write the data */ |
| | 3395 | image_writer->write_gsym_entry(get_sym(), get_sym_len(), |
| | 3396 | (int)TC_SYM_ENUM, buf, 5); |
| | 3397 | |
| | 3398 | /* we wrote the symbol */ |
| | 3399 | return TRUE; |
| | 3400 | } |
| | 3401 | |
| | 3402 | /* |
| | 3403 | * write the symbol to an image file's global symbol table |
| | 3404 | */ |
| | 3405 | int CTcSymMetaclass:: |
| | 3406 | write_to_image_file_global(class CVmImageWriter *image_writer) |
| | 3407 | { |
| | 3408 | char buf[128]; |
| | 3409 | |
| | 3410 | /* build our extra data buffer */ |
| | 3411 | oswp2(buf, meta_idx_); |
| | 3412 | oswp4(buf + 2, class_obj_); |
| | 3413 | |
| | 3414 | /* write the data */ |
| | 3415 | image_writer->write_gsym_entry(get_sym(), get_sym_len(), |
| | 3416 | (int)TC_SYM_METACLASS, buf, 6); |
| | 3417 | |
| | 3418 | /* we wrote the symbol */ |
| | 3419 | return TRUE; |
| | 3420 | } |
| | 3421 | |
| | 3422 | /* |
| | 3423 | * Fix up the inheritance chain in the modifier objects |
| | 3424 | */ |
| | 3425 | void CTcSymMetaclass::fix_mod_obj_sc_list() |
| | 3426 | { |
| | 3427 | CTcSymObj *obj; |
| | 3428 | CTcSymObj *obj_base; |
| | 3429 | |
| | 3430 | /* |
| | 3431 | * go through our chain of modifier objects, and make sure the |
| | 3432 | * stream data for each object points to its correct superclass |
| | 3433 | */ |
| | 3434 | for (obj = mod_obj_ ; obj != 0 ; obj = obj_base) |
| | 3435 | { |
| | 3436 | CTcDataStream *str; |
| | 3437 | |
| | 3438 | /* get the correct data stream */ |
| | 3439 | str = obj->get_stream(); |
| | 3440 | |
| | 3441 | /* get the base object for this symbol */ |
| | 3442 | obj_base = obj->get_mod_base_sym(); |
| | 3443 | |
| | 3444 | /* |
| | 3445 | * if there's no base object, there's no superclass entry to |
| | 3446 | * adjust for this object |
| | 3447 | */ |
| | 3448 | if (obj_base == 0) |
| | 3449 | break; |
| | 3450 | |
| | 3451 | /* |
| | 3452 | * set the superclass in this object to point to this base |
| | 3453 | * object |
| | 3454 | */ |
| | 3455 | CTPNStmObject::set_stream_sc(str, obj->get_stream_ofs(), |
| | 3456 | 0, obj_base->get_obj_id()); |
| | 3457 | } |
| | 3458 | } |
| | 3459 | |
| | 3460 | /* |
| | 3461 | * write the symbol to an image file's global symbol table |
| | 3462 | */ |
| | 3463 | int CTcSymBif::write_to_image_file_global(class CVmImageWriter *image_writer) |
| | 3464 | { |
| | 3465 | char buf[128]; |
| | 3466 | |
| | 3467 | /* build our extra data buffer */ |
| | 3468 | oswp2(buf, get_func_idx()); |
| | 3469 | oswp2(buf + 2, get_func_set_id()); |
| | 3470 | buf[4] = (has_retval() != 0); |
| | 3471 | oswp2(buf + 5, get_min_argc()); |
| | 3472 | oswp2(buf + 7, get_max_argc()); |
| | 3473 | buf[9] = (is_varargs() != 0); |
| | 3474 | |
| | 3475 | /* write the data */ |
| | 3476 | image_writer->write_gsym_entry(get_sym(), get_sym_len(), |
| | 3477 | (int)TC_SYM_BIF, buf, 10); |
| | 3478 | |
| | 3479 | /* we wrote the symbol */ |
| | 3480 | return TRUE; |
| | 3481 | } |
| | 3482 | |
| | 3483 | /* |
| | 3484 | * write the symbol to an image file's global symbol table |
| | 3485 | */ |
| | 3486 | int CTcSymExtfn::write_to_image_file_global(class CVmImageWriter *iw) |
| | 3487 | { |
| | 3488 | //$$$ to be implemented |
| | 3489 | assert(FALSE); |
| | 3490 | return FALSE; |
| | 3491 | } |
| | 3492 | |