| | 1 | #ifdef RCSID |
| | 2 | static char RCSid[] = |
| | 3 | "$Header: d:/cvsroot/tads/tads3/VMBIFTAD.CPP,v 1.3 1999/07/11 00:46:58 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 | vmbift3.cpp - T3 VM system interface function set |
| | 15 | Function |
| | 16 | |
| | 17 | Notes |
| | 18 | |
| | 19 | Modified |
| | 20 | 04/05/99 MJRoberts - Creation |
| | 21 | */ |
| | 22 | |
| | 23 | #include <stdio.h> |
| | 24 | #include <string.h> |
| | 25 | |
| | 26 | #include "utf8.h" |
| | 27 | #include "vmbif.h" |
| | 28 | #include "vmbift3.h" |
| | 29 | #include "vmstack.h" |
| | 30 | #include "vmerr.h" |
| | 31 | #include "vmerrnum.h" |
| | 32 | #include "vmglob.h" |
| | 33 | #include "vmpool.h" |
| | 34 | #include "vmobj.h" |
| | 35 | #include "vmrun.h" |
| | 36 | #include "vmstr.h" |
| | 37 | #include "vmvsn.h" |
| | 38 | #include "vmimage.h" |
| | 39 | #include "vmlst.h" |
| | 40 | #include "vmtobj.h" |
| | 41 | #include "vmfunc.h" |
| | 42 | #include "vmpredef.h" |
| | 43 | #include "vmsrcf.h" |
| | 44 | #include "charmap.h" |
| | 45 | |
| | 46 | |
| | 47 | /* |
| | 48 | * run the garbage collector |
| | 49 | */ |
| | 50 | void CVmBifT3::run_gc(VMG_ uint argc) |
| | 51 | { |
| | 52 | /* no arguments are allowed */ |
| | 53 | check_argc(vmg_ argc, 0); |
| | 54 | |
| | 55 | /* run the garbage collector */ |
| | 56 | G_obj_table->gc_full(vmg0_); |
| | 57 | } |
| | 58 | |
| | 59 | /* |
| | 60 | * set the SAY instruction's handler function |
| | 61 | */ |
| | 62 | #define SETSAY_NO_FUNC 1 |
| | 63 | #define SETSAY_NO_METHOD 2 |
| | 64 | void CVmBifT3::set_say(VMG_ uint argc) |
| | 65 | { |
| | 66 | vm_val_t *arg = G_stk->get(0); |
| | 67 | vm_val_t val; |
| | 68 | |
| | 69 | /* one argument is required */ |
| | 70 | check_argc(vmg_ argc, 1); |
| | 71 | |
| | 72 | /* check to see if we're setting the default display method */ |
| | 73 | if (arg->typ == VM_PROP |
| | 74 | || (arg->typ == VM_INT && arg->val.intval == SETSAY_NO_METHOD)) |
| | 75 | { |
| | 76 | vm_prop_id_t prop; |
| | 77 | |
| | 78 | /* |
| | 79 | * the return value is the old property pointer (or |
| | 80 | * SETSAY_NO_METHOD if there was no valid property set previously) |
| | 81 | */ |
| | 82 | prop = G_interpreter->get_say_method(); |
| | 83 | if (prop != VM_INVALID_PROP) |
| | 84 | retval_prop(vmg_ prop); |
| | 85 | else |
| | 86 | retval_int(vmg_ SETSAY_NO_METHOD); |
| | 87 | |
| | 88 | /* get the new value */ |
| | 89 | G_stk->pop(&val); |
| | 90 | |
| | 91 | /* if it's SETSAY_NO_METHOD, set it to the invalid prop ID */ |
| | 92 | if (val.typ == VM_INT) |
| | 93 | val.set_propid(VM_INVALID_PROP); |
| | 94 | |
| | 95 | /* set the method */ |
| | 96 | G_interpreter->set_say_method(val.val.prop); |
| | 97 | } |
| | 98 | else if (arg->typ == VM_FUNCPTR |
| | 99 | || arg->typ == VM_OBJ |
| | 100 | || (arg->typ == VM_INT && arg->val.intval == SETSAY_NO_FUNC)) |
| | 101 | { |
| | 102 | /* |
| | 103 | * the return value is the old function (or SETSAY_NO_FUNC if the |
| | 104 | * old function was nil) |
| | 105 | */ |
| | 106 | G_interpreter->get_say_func(&val); |
| | 107 | if (val.typ != VM_NIL) |
| | 108 | retval(vmg_ &val); |
| | 109 | else |
| | 110 | retval_int(vmg_ SETSAY_NO_FUNC); |
| | 111 | |
| | 112 | /* get the new function value */ |
| | 113 | G_stk->pop(&val); |
| | 114 | |
| | 115 | /* if it's SETSAY_NO_FUNC, set the function to nil */ |
| | 116 | if (val.typ == VM_INT) |
| | 117 | val.set_nil(); |
| | 118 | |
| | 119 | /* set the new function */ |
| | 120 | G_interpreter->set_say_func(vmg_ &val); |
| | 121 | } |
| | 122 | else |
| | 123 | { |
| | 124 | /* invalid type */ |
| | 125 | err_throw(VMERR_BAD_TYPE_BIF); |
| | 126 | } |
| | 127 | } |
| | 128 | |
| | 129 | /* |
| | 130 | * get the VM version number |
| | 131 | */ |
| | 132 | void CVmBifT3::get_vm_vsn(VMG_ uint argc) |
| | 133 | { |
| | 134 | /* no arguments are allowed */ |
| | 135 | check_argc(vmg_ argc, 0); |
| | 136 | |
| | 137 | /* set the integer return value */ |
| | 138 | retval_int(vmg_ T3VM_VSN_NUMBER); |
| | 139 | } |
| | 140 | |
| | 141 | /* |
| | 142 | * get the VM identification string |
| | 143 | */ |
| | 144 | void CVmBifT3::get_vm_id(VMG_ uint argc) |
| | 145 | { |
| | 146 | /* no arguments are allowed */ |
| | 147 | check_argc(vmg_ argc, 0); |
| | 148 | |
| | 149 | /* set the integer return value */ |
| | 150 | retval_str(vmg_ T3VM_IDENTIFICATION); |
| | 151 | } |
| | 152 | |
| | 153 | |
| | 154 | /* |
| | 155 | * get the VM banner string |
| | 156 | */ |
| | 157 | void CVmBifT3::get_vm_banner(VMG_ uint argc) |
| | 158 | { |
| | 159 | /* no arguments are allowed */ |
| | 160 | check_argc(vmg_ argc, 0); |
| | 161 | |
| | 162 | /* return the string */ |
| | 163 | retval_str(vmg_ T3VM_BANNER_STRING); |
| | 164 | } |
| | 165 | |
| | 166 | /* |
| | 167 | * get the 'preinit' status - true if preinit, nil if normal |
| | 168 | */ |
| | 169 | void CVmBifT3::get_vm_preinit_mode(VMG_ uint argc) |
| | 170 | { |
| | 171 | /* no arguments allowed */ |
| | 172 | check_argc(vmg_ argc, 0); |
| | 173 | |
| | 174 | /* return the preinit mode */ |
| | 175 | retval_int(vmg_ G_preinit_mode); |
| | 176 | } |
| | 177 | |
| | 178 | /* |
| | 179 | * get the runtime symbol table |
| | 180 | */ |
| | 181 | void CVmBifT3::get_global_symtab(VMG_ uint argc) |
| | 182 | { |
| | 183 | /* check arguments */ |
| | 184 | check_argc(vmg_ argc, 0); |
| | 185 | |
| | 186 | /* return the loader's symbol table object, if any */ |
| | 187 | retval_obj(vmg_ G_image_loader->get_reflection_symtab()); |
| | 188 | } |
| | 189 | |
| | 190 | /* |
| | 191 | * allocate a new property ID |
| | 192 | */ |
| | 193 | void CVmBifT3::alloc_new_prop(VMG_ uint argc) |
| | 194 | { |
| | 195 | /* check arguments */ |
| | 196 | check_argc(vmg_ argc, 0); |
| | 197 | |
| | 198 | /* allocate and return a new property ID */ |
| | 199 | retval_prop(vmg_ G_image_loader->alloc_new_prop(vmg0_)); |
| | 200 | } |
| | 201 | |
| | 202 | /* |
| | 203 | * get a stack trace |
| | 204 | */ |
| | 205 | void CVmBifT3::get_stack_trace(VMG_ uint argc) |
| | 206 | { |
| | 207 | int single_level; |
| | 208 | int level; |
| | 209 | vm_val_t *fp; |
| | 210 | vm_val_t lst_val; |
| | 211 | CVmObjList *lst; |
| | 212 | pool_ofs_t entry_addr; |
| | 213 | ulong method_ofs; |
| | 214 | vm_val_t stack_info_cls; |
| | 215 | |
| | 216 | /* check arguments */ |
| | 217 | check_argc_range(vmg_ argc, 0, 1); |
| | 218 | |
| | 219 | /* get the imported stack information class */ |
| | 220 | stack_info_cls.set_obj(G_predef->stack_info_cls); |
| | 221 | if (stack_info_cls.val.obj == VM_INVALID_OBJ) |
| | 222 | { |
| | 223 | /* |
| | 224 | * there's no stack information class - we can't return any |
| | 225 | * meaningful information, so just return nil |
| | 226 | */ |
| | 227 | retval_nil(vmg0_); |
| | 228 | return; |
| | 229 | } |
| | 230 | |
| | 231 | /* check to see if we're fetching a single level or the full trace */ |
| | 232 | if (argc >= 1) |
| | 233 | { |
| | 234 | /* get the single level, and adjust to a 0 base */ |
| | 235 | single_level = pop_int_val(vmg0_) - 1; |
| | 236 | |
| | 237 | /* make sure it's in range */ |
| | 238 | if (single_level < 0) |
| | 239 | err_throw(VMERR_BAD_VAL_BIF); |
| | 240 | |
| | 241 | /* we won't need a return list */ |
| | 242 | lst_val.set_nil(); |
| | 243 | lst = 0; |
| | 244 | } |
| | 245 | else |
| | 246 | { |
| | 247 | /* |
| | 248 | * We're returning a full list, so we need to allocate the list for |
| | 249 | * the return value. First, count stack levels to see how big a |
| | 250 | * list we'll need. |
| | 251 | */ |
| | 252 | |
| | 253 | /* start at the current function */ |
| | 254 | fp = G_interpreter->get_frame_ptr(); |
| | 255 | |
| | 256 | /* traverse the stack to determine the frame depth */ |
| | 257 | for (level = 0 ; fp != 0 ; |
| | 258 | fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp), ++level) ; |
| | 259 | |
| | 260 | /* create the list */ |
| | 261 | lst_val.set_obj(CVmObjList::create(vmg_ FALSE, level)); |
| | 262 | lst = (CVmObjList *)vm_objp(vmg_ lst_val.val.obj); |
| | 263 | |
| | 264 | /* protect the list from garbage collection while we work */ |
| | 265 | G_stk->push(&lst_val); |
| | 266 | |
| | 267 | /* flag that we're doing the whole stack */ |
| | 268 | single_level = -1; |
| | 269 | } |
| | 270 | |
| | 271 | /* set up at the current function */ |
| | 272 | fp = G_interpreter->get_frame_ptr(); |
| | 273 | entry_addr = G_interpreter->get_entry_ptr(); |
| | 274 | method_ofs = G_interpreter->get_method_ofs(); |
| | 275 | |
| | 276 | /* traverse the frames */ |
| | 277 | for (level = 0 ; fp != 0 ; |
| | 278 | fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp), ++level) |
| | 279 | { |
| | 280 | int fr_argc; |
| | 281 | int i; |
| | 282 | vm_obj_id_t def_obj; |
| | 283 | vm_val_t info_self; |
| | 284 | vm_val_t info_func; |
| | 285 | vm_val_t info_obj; |
| | 286 | vm_val_t info_prop; |
| | 287 | vm_val_t info_args; |
| | 288 | vm_val_t info_srcloc; |
| | 289 | CVmObjList *arglst; |
| | 290 | vm_val_t ele; |
| | 291 | CVmFuncPtr func_ptr; |
| | 292 | |
| | 293 | /* if we're looking for a single level, and this isn't it, skip it */ |
| | 294 | if (single_level >= 0 && level != single_level) |
| | 295 | goto done_with_level; |
| | 296 | |
| | 297 | /* |
| | 298 | * start with the information values to nil - we'll set the |
| | 299 | * appropriate ones when we find out what we have |
| | 300 | */ |
| | 301 | info_func.set_nil(); |
| | 302 | info_obj.set_nil(); |
| | 303 | info_prop.set_nil(); |
| | 304 | info_self.set_nil(); |
| | 305 | |
| | 306 | /* get the number of arguments to the function in this frame */ |
| | 307 | fr_argc = G_interpreter->get_argc_from_frame(vmg_ fp); |
| | 308 | |
| | 309 | /* set up a function pointer for the method's entry address */ |
| | 310 | func_ptr.set((const uchar *)G_code_pool->get_ptr(entry_addr)); |
| | 311 | |
| | 312 | /* |
| | 313 | * to ensure we don't flush the caller out of the code pool cache, |
| | 314 | * resolve the current entrypoint address immediately - we always |
| | 315 | * have room for at least two code pages in the cache, so we know |
| | 316 | * resolving just one won't throw the previous one out, so we |
| | 317 | * simply need to make the current one most recently used by |
| | 318 | * resolving it |
| | 319 | */ |
| | 320 | G_code_pool->get_ptr(G_interpreter->get_entry_ptr()); |
| | 321 | |
| | 322 | /* get the current frame's defining object */ |
| | 323 | def_obj = G_interpreter->get_defining_obj_from_frame(vmg_ fp); |
| | 324 | |
| | 325 | /* determine whether it's an object.prop or a function call */ |
| | 326 | if (method_ofs == 0) |
| | 327 | { |
| | 328 | /* |
| | 329 | * a zero method offset indicates a recursive VM invocation |
| | 330 | * from a native function, so we have no information on the |
| | 331 | * call at all |
| | 332 | */ |
| | 333 | fr_argc = 0; |
| | 334 | } |
| | 335 | else if (def_obj == VM_INVALID_OBJ) |
| | 336 | { |
| | 337 | /* it's a function call */ |
| | 338 | info_func.set_fnptr(entry_addr); |
| | 339 | } |
| | 340 | else |
| | 341 | { |
| | 342 | /* it's an object.prop invocation */ |
| | 343 | info_obj.set_obj(def_obj); // $$$ walk up to base modified obj? |
| | 344 | info_prop.set_propid( |
| | 345 | G_interpreter->get_target_prop_from_frame(vmg_ fp)); |
| | 346 | |
| | 347 | /* get the 'self' in this frame */ |
| | 348 | info_self.set_obj(G_interpreter->get_self_from_frame(vmg_ fp)); |
| | 349 | } |
| | 350 | |
| | 351 | /* |
| | 352 | * build the argument list and source location, except for system |
| | 353 | * routines |
| | 354 | */ |
| | 355 | if (method_ofs != 0) |
| | 356 | { |
| | 357 | /* allocate a list object to store the argument list */ |
| | 358 | info_args.set_obj(CVmObjList::create(vmg_ FALSE, fr_argc)); |
| | 359 | arglst = (CVmObjList *)vm_objp(vmg_ info_args.val.obj); |
| | 360 | |
| | 361 | /* push the argument list for gc protection */ |
| | 362 | G_stk->push(&info_args); |
| | 363 | |
| | 364 | /* build the argument list */ |
| | 365 | for (i = 0 ; i < fr_argc ; ++i) |
| | 366 | { |
| | 367 | /* add this element to the argument list */ |
| | 368 | arglst->cons_set_element( |
| | 369 | i, G_interpreter->get_param_from_frame(vmg_ fp, i)); |
| | 370 | } |
| | 371 | |
| | 372 | /* get the source location */ |
| | 373 | get_source_info(vmg_ entry_addr, method_ofs, &info_srcloc); |
| | 374 | } |
| | 375 | else |
| | 376 | { |
| | 377 | /* |
| | 378 | * it's a system routine - no argument information is |
| | 379 | * available, so return nil rather than an empty list to to |
| | 380 | * indicate the absence |
| | 381 | */ |
| | 382 | info_args.set_nil(); |
| | 383 | |
| | 384 | /* there's obviously no source location for system code */ |
| | 385 | info_srcloc.set_nil(); |
| | 386 | } |
| | 387 | |
| | 388 | /* |
| | 389 | * We have all of the information on this level now, so create the |
| | 390 | * information object for the level. This is an object of the |
| | 391 | * exported stack-info class, which is a TadsObject type. |
| | 392 | */ |
| | 393 | G_stk->push(&info_srcloc); |
| | 394 | G_stk->push(&info_args); |
| | 395 | G_stk->push(&info_self); |
| | 396 | G_stk->push(&info_prop); |
| | 397 | G_stk->push(&info_obj); |
| | 398 | G_stk->push(&info_func); |
| | 399 | G_stk->push(&stack_info_cls); |
| | 400 | ele.set_obj(CVmObjTads::create_from_stack(vmg_ 0, 7)); |
| | 401 | |
| | 402 | /* |
| | 403 | * the argument list is safely stashed away in the stack info |
| | 404 | * object, so we can discard our gc protection for it now |
| | 405 | */ |
| | 406 | if (method_ofs != 0) |
| | 407 | G_stk->discard(); |
| | 408 | |
| | 409 | /* |
| | 410 | * if we're fetching a single level, this is it - return the new |
| | 411 | * stack info object and we're done |
| | 412 | */ |
| | 413 | if (single_level >= 0) |
| | 414 | { |
| | 415 | /* return the single level object */ |
| | 416 | retval_obj(vmg_ ele.val.obj); |
| | 417 | |
| | 418 | /* we're done */ |
| | 419 | return; |
| | 420 | } |
| | 421 | |
| | 422 | /* add the new element to our list */ |
| | 423 | lst->cons_set_element(level, &ele); |
| | 424 | |
| | 425 | done_with_level: |
| | 426 | /* move on to the enclosing frame */ |
| | 427 | entry_addr = |
| | 428 | G_interpreter->get_enclosing_entry_ptr_from_frame(vmg_ fp); |
| | 429 | method_ofs = G_interpreter->get_return_ofs_from_frame(vmg_ fp); |
| | 430 | } |
| | 431 | |
| | 432 | /* return the list */ |
| | 433 | retval_obj(vmg_ lst_val.val.obj); |
| | 434 | |
| | 435 | /* discard our gc protection */ |
| | 436 | G_stk->discard(); |
| | 437 | } |
| | 438 | |
| | 439 | /* |
| | 440 | * Get the source file information for a given code pool offset. If debug |
| | 441 | * records aren't available for the given location, returns nil. Returns |
| | 442 | * a list containing the source file information: the first element is a |
| | 443 | * string giving the name of the file, and the second element is an |
| | 444 | * integer giving the line number in the file. Returns nil if no source |
| | 445 | * information is available for the given byte code location. |
| | 446 | */ |
| | 447 | void CVmBifT3::get_source_info(VMG_ ulong entry_addr, ulong method_ofs, |
| | 448 | vm_val_t *retval) |
| | 449 | { |
| | 450 | CVmFuncPtr func_ptr; |
| | 451 | CVmDbgLinePtr line_ptr; |
| | 452 | ulong stm_start; |
| | 453 | ulong stm_end; |
| | 454 | CVmObjList *lst; |
| | 455 | vm_val_t ele; |
| | 456 | CVmSrcfEntry *srcf; |
| | 457 | CVmObjString *str; |
| | 458 | const char *fname; |
| | 459 | size_t map_len; |
| | 460 | |
| | 461 | /* presume we won't be able to find source information for the location */ |
| | 462 | retval->set_nil(); |
| | 463 | |
| | 464 | /* set up a debug table pointer for the function or method */ |
| | 465 | func_ptr.set((const uchar *)G_code_pool->get_ptr(entry_addr)); |
| | 466 | |
| | 467 | /* |
| | 468 | * resolve the current caller's entry code page to ensure it isn't |
| | 469 | * flushed out of the code pool cache |
| | 470 | */ |
| | 471 | G_code_pool->get_ptr(G_interpreter->get_entry_ptr()); |
| | 472 | |
| | 473 | /* get the debug information for the given location */ |
| | 474 | if (!CVmRun::get_stm_bounds(vmg_ &func_ptr, method_ofs, |
| | 475 | &line_ptr, &stm_start, &stm_end)) |
| | 476 | { |
| | 477 | /* no source information available - return failure */ |
| | 478 | return; |
| | 479 | } |
| | 480 | |
| | 481 | /* get the source file record - if we can't find it, return failure */ |
| | 482 | srcf = (G_srcf_table != 0 |
| | 483 | ? G_srcf_table->get_entry(line_ptr.get_source_id()) : 0); |
| | 484 | if (srcf == 0) |
| | 485 | return; |
| | 486 | |
| | 487 | /* |
| | 488 | * Create a list for the return value. The return list has two |
| | 489 | * elements: the name of the source file containing this code, and the |
| | 490 | * line number in the file. |
| | 491 | */ |
| | 492 | retval->set_obj(CVmObjList::create(vmg_ FALSE, 2)); |
| | 493 | lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); |
| | 494 | |
| | 495 | /* push the list for gc protection */ |
| | 496 | G_stk->push(retval); |
| | 497 | |
| | 498 | /* get the filename string */ |
| | 499 | fname = srcf->get_name(); |
| | 500 | |
| | 501 | /* |
| | 502 | * determine how long the string will be when translated to utf8 from |
| | 503 | * the local filename character set |
| | 504 | */ |
| | 505 | map_len = G_cmap_from_fname->map_str(0, 0, fname); |
| | 506 | |
| | 507 | /* |
| | 508 | * create a string value to hold the filename, and store it in the |
| | 509 | * first element of the return list (note that this automatically |
| | 510 | * protects the new string from garbage collection, by virtue of the |
| | 511 | * list referencing the string and the list itself being protected) |
| | 512 | */ |
| | 513 | ele.set_obj(CVmObjString::create(vmg_ FALSE, map_len)); |
| | 514 | lst->cons_set_element(0, &ele); |
| | 515 | |
| | 516 | /* map the string into the buffer we allocated for it */ |
| | 517 | str = (CVmObjString *)vm_objp(vmg_ ele.val.obj); |
| | 518 | G_cmap_from_fname->map_str(str->cons_get_buf(), map_len, fname); |
| | 519 | |
| | 520 | /* set the second element of the list to the source line number */ |
| | 521 | ele.set_int(line_ptr.get_source_line()); |
| | 522 | lst->cons_set_element(1, &ele); |
| | 523 | |
| | 524 | /* discard our gc protection */ |
| | 525 | G_stk->discard(); |
| | 526 | } |
| | 527 | |
| | 528 | |
| | 529 | |
| | 530 | /* ------------------------------------------------------------------------ */ |
| | 531 | /* |
| | 532 | * T3 VM Test function set. This function set contains internal test |
| | 533 | * and debug functions. These functions are not meant for use by |
| | 534 | * "normal" programs - they provide internal access to certain VM state |
| | 535 | * that is not useful or meaningful except for testing and debugging the |
| | 536 | * VM itself. |
| | 537 | */ |
| | 538 | |
| | 539 | /* |
| | 540 | * Get an object's internal ID. Takes an object instance and returns an |
| | 541 | * integer giving the object's VM ID number. This is effectively an |
| | 542 | * address that can be used to refer to the object. Because this value |
| | 543 | * is returned as an integer, it is NOT a reference to the object for |
| | 544 | * the purposes of garbage collection or finalization. |
| | 545 | */ |
| | 546 | void CVmBifT3Test::get_obj_id(VMG_ uint argc) |
| | 547 | { |
| | 548 | vm_val_t val; |
| | 549 | |
| | 550 | /* one argument required */ |
| | 551 | check_argc(vmg_ argc, 1); |
| | 552 | |
| | 553 | /* get the object value */ |
| | 554 | G_interpreter->pop_obj(vmg_ &val); |
| | 555 | |
| | 556 | /* return the object ID as an integer */ |
| | 557 | retval_int(vmg_ (long)val.val.obj); |
| | 558 | } |
| | 559 | |
| | 560 | /* |
| | 561 | * Get an object's garbage collection state. Takes an object ID (NOT an |
| | 562 | * object reference -- this is the integer value returned by get_obj_id) |
| | 563 | * and returns a bit mask with the garbage collector state. |
| | 564 | * |
| | 565 | * (retval & 0x000F) gives the free state. 0 is free, 1 is in use. |
| | 566 | * |
| | 567 | * (retval & 0x00F0) gives the reachable state. 0x00 is unreachable, |
| | 568 | * 0x10 is finalizer-reachable, and 0x20 is fully reachable. |
| | 569 | * |
| | 570 | * (retval & 0x0F00) gives the finalizer state. 0x000 is unfinalizable, |
| | 571 | * 0x100 is finalizable, and 0x200 is finalized. |
| | 572 | * |
| | 573 | * (retval & 0xF000) gives the object ID validity. 0 is valid, 0xF000 |
| | 574 | * is invalid. |
| | 575 | */ |
| | 576 | void CVmBifT3Test::get_obj_gc_state(VMG_ uint argc) |
| | 577 | { |
| | 578 | vm_val_t val; |
| | 579 | |
| | 580 | /* one argument required */ |
| | 581 | check_argc(vmg_ argc, 1); |
| | 582 | |
| | 583 | /* pop the string */ |
| | 584 | G_interpreter->pop_int(vmg_ &val); |
| | 585 | |
| | 586 | /* return the internal garbage collector state of the object */ |
| | 587 | retval_int(vmg_ |
| | 588 | (long)G_obj_table->get_obj_internal_state(val.val.intval)); |
| | 589 | } |
| | 590 | |
| | 591 | /* |
| | 592 | * Get the Unicode character code of the first character of a string |
| | 593 | */ |
| | 594 | void CVmBifT3Test::get_charcode(VMG_ uint argc) |
| | 595 | { |
| | 596 | const char *str; |
| | 597 | |
| | 598 | /* one argument required */ |
| | 599 | check_argc(vmg_ argc, 1); |
| | 600 | |
| | 601 | /* get the object ID as an integer */ |
| | 602 | str = pop_str_val(vmg0_); |
| | 603 | |
| | 604 | /* |
| | 605 | * if the string is empty, return nil; otherwise, return the Unicode |
| | 606 | * character code of the first character |
| | 607 | */ |
| | 608 | if (vmb_get_len(str) == 0) |
| | 609 | { |
| | 610 | /* empty string - return nil */ |
| | 611 | retval_nil(vmg0_); |
| | 612 | } |
| | 613 | else |
| | 614 | { |
| | 615 | /* |
| | 616 | * get the character code of the first character and return it |
| | 617 | * as an integer |
| | 618 | */ |
| | 619 | retval_int(vmg_ (int)utf8_ptr::s_getch(str + VMB_LEN)); |
| | 620 | } |
| | 621 | } |