~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to tads/tads3/vmbift3.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}