cfad47cfa3/tads3/vmbif.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#ifdef RCSID
2
static char RCSid[] =
3
"$Header: d:/cvsroot/tads/tads3/vmbif.cpp,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $";
4
#endif
5
6
/* 
7
 *   Copyright (c) 1998, 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
  vmbif.cpp - built-in function set table implementation
15
Function
16
  
17
Notes
18
  
19
Modified
20
  12/05/98 MJRoberts  - Creation
21
*/
22
23
#include <stdlib.h>
24
#include <string.h>
25
26
#include "t3std.h"
27
#include "utf8.h"
28
#include "charmap.h"
29
#include "vmtype.h"
30
#include "vmerr.h"
31
#include "vmerrnum.h"
32
#include "vmglob.h"
33
#include "vmbif.h"
34
#include "vmbifreg.h"
35
#include "vmstr.h"
36
#include "vmobj.h"
37
#include "vmrun.h"
38
39
40
/* ------------------------------------------------------------------------ */
41
/*
42
 *   Create the function set table with a given number of initial entries 
43
 */
44
CVmBifTable::CVmBifTable(size_t init_entries)
45
{
46
    /* allocate space for our entries */
47
    if (init_entries != 0)
48
    {
49
        /* allocate the space */
50
        table_ = (vm_bif_entry_t **)
51
                 t3malloc(init_entries * sizeof(table_[0]));
52
        names_ = (char **)
53
                 t3malloc(init_entries * sizeof(names_[0]));
54
    }
55
    else
56
    {
57
        /* we have no entries */
58
        table_ = 0;
59
        names_ = 0;
60
    }
61
62
    /* no entries are defined yet */
63
    count_ = 0;
64
65
    /* remember the allocation size */
66
    alloc_ = init_entries;
67
}
68
69
/* ------------------------------------------------------------------------ */
70
/*
71
 *   Delete the table 
72
 */
73
CVmBifTable::~CVmBifTable()
74
{
75
    /* free the table, if we ever allocated one */
76
    if (table_ != 0)
77
        t3free(table_);
78
79
    /* free the function set names list, if we allocated it */
80
    if (names_ != 0)
81
    {
82
        /* clear the table to delete the entries in the 'names_' array */
83
        clear();
84
85
        /* free the table */
86
        t3free(names_);
87
    }
88
}
89
90
/* ------------------------------------------------------------------------ */
91
/*
92
 *   Clear all entries from the table 
93
 */
94
void CVmBifTable::clear()
95
{
96
    /* delete the 'names' entries, if we allocated any */
97
    if (names_ != 0)
98
    {
99
        size_t i;
100
101
        /* free each element */
102
        for (i = 0 ; i < count_ ; ++i)
103
            lib_free_str(names_[i]);
104
    }
105
    
106
    /* 
107
     *   Reset the entry counter.  Note that this doesn't affect any
108
     *   allocation; we keep a separate count of the number of table slots
109
     *   we have allocated.  Table slots don't have any additional
110
     *   associated memory, so we don't need to worry about cleaning
111
     *   anything up at this point.  
112
     */
113
    count_ = 0;
114
}
115
116
/* ------------------------------------------------------------------------ */
117
/*
118
 *   Ensure that we have space for a given number of entries 
119
 */
120
void CVmBifTable::ensure_space(size_t entries, size_t increment)
121
{
122
    /* if we don't have enough space, allocate more */
123
    if (entries >= alloc_)
124
    {
125
        size_t new_table_size;
126
        size_t new_names_size;
127
128
        /* increase the allocation size by the given increment */
129
        alloc_ += increment;
130
131
        /* if it's still too small, bump it up to the required size */
132
        if (alloc_ < entries)
133
            alloc_ = entries;
134
135
        /* compute the new sizes */
136
        new_table_size = alloc_ * sizeof(table_[0]);
137
        new_names_size = alloc_ * sizeof(names_[0]);
138
139
        /* 
140
         *   if we have a table already, reallocate it at the larger size;
141
         *   otherwise, allocate a new table 
142
         */
143
        if (table_ != 0)
144
        {
145
            table_ = (vm_bif_entry_t **)t3realloc(table_, new_table_size);
146
            names_ = (char **)t3realloc(names_, new_names_size);
147
        }
148
        else
149
        {
150
            table_ = (vm_bif_entry_t **)t3malloc(new_table_size);
151
            names_ = (char **)t3malloc(alloc_ * new_names_size);
152
        }
153
    }
154
}
155
156
/* ------------------------------------------------------------------------ */
157
/*
158
 *   Add an entry to the table 
159
 */
160
void CVmBifTable::add_entry(const char *func_set_id)
161
{
162
    vm_bif_entry_t *entry;
163
    const char *vsn;
164
    size_t name_len;
165
166
    /* ensure we have space for one more entry */
167
    ensure_space(count_ + 1, 5);
168
169
    /* find the version suffix in the name, if any */
170
    vsn = lib_find_vsn_suffix(func_set_id, '/', "000000", &name_len);
171
172
    /* look up the function set by name */
173
    for (entry = G_bif_reg_table ; entry->func_set_id != 0 ; ++entry)
174
    {
175
        const char *entry_vsn;
176
        size_t entry_name_len;
177
178
        /* find the version number in this entry */
179
        entry_vsn = lib_find_vsn_suffix(entry->func_set_id, '/', "000000",
180
                                        &entry_name_len);
181
182
        /* see if this is a match */
183
        if (name_len == entry_name_len
184
            && memcmp(func_set_id, entry->func_set_id, name_len) == 0)
185
        {
186
            /* 
187
             *   make sure the version provided in the VM is at least as
188
             *   high as the requested version 
189
             */
190
            if (strcmp(vsn, entry_vsn) > 0)
191
                err_throw_a(VMERR_FUNCSET_TOO_OLD, 2,
192
                            ERR_TYPE_TEXTCHAR, func_set_id,
193
                            ERR_TYPE_TEXTCHAR, entry_vsn);
194
195
            /* 
196
             *   It's a match - add the new entry.  Simply keep a pointer
197
             *   to the static table entry. 
198
             */
199
            table_[count_] = entry;
200
201
            /* store the new name element as well */
202
            names_[count_] = lib_copy_str(func_set_id);
203
204
            /* count the new entry */
205
            ++count_;
206
207
            /* we're done */
208
            return;
209
        }
210
    }
211
212
    /* we didn't find it - handle it according to our resolution mode */
213
    add_entry_unresolved(func_set_id);
214
}
215
216
/* ------------------------------------------------------------------------ */
217
/*
218
 *   Function Set helper functions 
219
 */
220
221
/* 
222
 *   check arguments; throws an error if the argument count doesn't match
223
 *   the given value 
224
 */
225
void CVmBif::check_argc(VMG_ uint argc, uint needed_argc)
226
{
227
    if (argc != needed_argc)
228
        err_throw(VMERR_WRONG_NUM_OF_ARGS);
229
}
230
231
/* 
232
 *   check arguments; throws an error if the argument count is outside of
233
 *   the given range 
234
 */
235
void CVmBif::check_argc_range(VMG_ uint argc, uint argc_min, uint argc_max)
236
{
237
    if (argc < argc_min || argc > argc_max)
238
        err_throw(VMERR_WRONG_NUM_OF_ARGS);
239
}
240
241
/*
242
 *   return a string value 
243
 */
244
void CVmBif::retval_str(VMG_ const char *str)
245
{
246
    retval_str(vmg_ str, strlen(str));
247
}
248
249
void CVmBif::retval_str(VMG_ const char *str, size_t len)
250
{
251
    vm_obj_id_t str_obj;
252
253
    /* create a string to hold the return value */
254
    str_obj = CVmObjString::create(vmg_ FALSE, str, len);
255
256
    /* return the string */
257
    retval_obj(vmg_ str_obj);
258
}
259
260
/*
261
 *   return a string value that came from the UI character set
262
 */
263
void CVmBif::retval_ui_str(VMG_ const char *str)
264
{
265
    retval_ui_str(vmg_ str, strlen(str));
266
}
267
268
void CVmBif::retval_ui_str(VMG_ const char *str, size_t len)
269
{
270
    /* make a string object from the UI string, and return it */
271
    retval_obj(vmg_ str_from_ui_str(vmg_ str, len));
272
}
273
274
/*
275
 *   create a new string object from a string in the UI character set 
276
 */
277
vm_obj_id_t CVmBif::str_from_ui_str(VMG_ const char *str)
278
{
279
    return str_from_ui_str(vmg_ str, strlen(str));
280
}
281
282
vm_obj_id_t CVmBif::str_from_ui_str(VMG_ const char *str, size_t len)
283
{
284
    char *outp;
285
    size_t outlen;
286
    vm_obj_id_t str_id;
287
    CVmObjString *str_obj;
288
289
    /* figure out how much space we need */
290
    outp = 0;
291
    outlen = 0;
292
    outlen = G_cmap_from_ui->map(0, &outlen, str, len);
293
294
    /* allocate a string of that size */
295
    str_id = CVmObjString::create(vmg_ FALSE, outlen);
296
    str_obj = (CVmObjString *)vm_objp(vmg_ str_id);
297
298
    /* map the string into the new string buffer */
299
    outp = str_obj->cons_get_buf();
300
    G_cmap_from_ui->map(&outp, &outlen, str, len);
301
302
    /* return the new string object */
303
    return str_id;
304
}
305
306
/*
307
 *   return an object value
308
 */
309
void CVmBif::retval_obj(VMG_ vm_obj_id_t obj)
310
{
311
    if (obj == VM_INVALID_OBJ)
312
        G_interpreter->get_r0()->set_nil();
313
    else
314
        G_interpreter->get_r0()->set_obj(obj);
315
}
316
317
/*
318
 *   return a property value 
319
 */
320
void CVmBif::retval_prop(VMG_ vm_prop_id_t prop)
321
{
322
    G_interpreter->get_r0()->set_propid(prop);
323
}
324
325
/*
326
 *   return an integer value 
327
 */
328
void CVmBif::retval_int(VMG_ long val)
329
{
330
    G_interpreter->get_r0()->set_int(val);
331
}
332
333
/*
334
 *   return true 
335
 */
336
void CVmBif::retval_true(VMG0_)
337
{
338
    G_interpreter->get_r0()->set_true();
339
}
340
341
/*
342
 *   return nil 
343
 */
344
void CVmBif::retval_nil(VMG0_)
345
{
346
    G_interpreter->get_r0()->set_nil();
347
}
348
349
/*
350
 *   return a boolean value - nil if false, true if true 
351
 */
352
void CVmBif::retval_bool(VMG_ int val)
353
{
354
    G_interpreter->get_r0()->set_logical(val);
355
}
356
357
/*
358
 *   return a function pointer value 
359
 */
360
void CVmBif::retval_fnptr(VMG_ pool_ofs_t ofs)
361
{
362
    G_interpreter->get_r0()->set_fnptr(ofs);
363
}
364
365
/*
366
 *   return a value 
367
 */
368
void CVmBif::retval(VMG_ const vm_val_t *val)
369
{
370
    *G_interpreter->get_r0() = *val;
371
}
372
373
/* ------------------------------------------------------------------------ */
374
/*
375
 *   Pop a string value 
376
 */
377
const char *CVmBif::pop_str_val(VMG0_)
378
{
379
    vm_val_t val;
380
    CVmObject *obj;
381
    const char *p;
382
383
    /* pop the value */
384
    G_stk->pop(&val);
385
386
    /* see what we have */
387
    switch(val.typ)
388
    {
389
    case VM_SSTRING:
390
        /* it's a constant string - get the constant pool pointer */
391
        return G_const_pool->get_ptr(val.val.ofs);
392
393
    case VM_OBJ:
394
        /* get the object */
395
        obj = vm_objp(vmg_ val.val.obj);
396
397
        /* get the string value, if it has one */
398
        p = obj->get_as_string(vmg0_);
399
400
        /* if it has a string value, return it */
401
        if (p != 0)
402
            return p;
403
404
        /* we didn't get a string value */
405
        break;
406
407
    default:
408
        /* other types don't have a string value */
409
        break;
410
    }
411
412
    /* if we got here, the value isn't a string */
413
    err_throw(VMERR_STRING_VAL_REQD);
414
    AFTER_ERR_THROW(return 0;)
415
}
416
417
/* ------------------------------------------------------------------------ */
418
/*
419
 *   Pop a list value
420
 */
421
const char *CVmBif::pop_list_val(VMG0_)
422
{
423
    vm_val_t val;
424
    CVmObject *obj;
425
    const char *p;
426
427
    /* pop the value */
428
    G_stk->pop(&val);
429
430
    /* see what we have */
431
    switch(val.typ)
432
    {
433
    case VM_LIST:
434
        /* it's a constant list - get the constant pool pointer */
435
        return G_const_pool->get_ptr(val.val.ofs);
436
437
    case VM_OBJ:
438
        /* get the object */
439
        obj = vm_objp(vmg_ val.val.obj);
440
441
        /* get the list value, if it has one */
442
        p = obj->get_as_list();
443
444
        /* if it has a liist value, return it */
445
        if (p != 0)
446
            return p;
447
448
        /* we didn't get a list value */
449
        break;
450
451
    default:
452
        /* other types don't have a list value */
453
        break;
454
    }
455
456
    /* if we got here, the value isn't a list */
457
    err_throw(VMERR_LIST_VAL_REQD);
458
    AFTER_ERR_THROW(return 0;)
459
}
460
461
/* ------------------------------------------------------------------------ */
462
/*
463
 *   Pop a string into a buffer, and null-terminate the result. 
464
 */
465
void CVmBif::pop_str_val_buf(VMG_ char *buf, size_t buflen)
466
{
467
    const char *strp;
468
    size_t copy_len;
469
470
    /* pop the string value */
471
    strp = pop_str_val(vmg0_);
472
473
    /* 
474
     *   get the length, but limit it to our buffer size, less one byte
475
     *   for null termination 
476
     */
477
    copy_len = vmb_get_len(strp);
478
    if (copy_len > buflen - 1)
479
        copy_len = utf8_ptr::s_trunc(strp + VMB_LEN, buflen - 1);
480
481
    /* copy the string */
482
    memcpy(buf, strp + VMB_LEN, copy_len);
483
484
    /* null-terminate the result */
485
    buf[copy_len] = '\0';
486
}
487
488
489
/* ------------------------------------------------------------------------ */
490
/*
491
 *   Pop a string into a buffer, translating the string into the filename
492
 *   character set and null-terminating the result.  
493
 */
494
void CVmBif::pop_str_val_fname(VMG_ char *buf, size_t buflen)
495
{
496
    const char *strp;
497
    size_t copy_len;
498
499
    /* pop the string value */
500
    strp = pop_str_val(vmg0_);
501
502
    /* get the length */
503
    copy_len = vmb_get_len(strp);
504
505
    /* 
506
     *   map it into the local filename character set and store the result
507
     *   in the output buffer - reserve one byte for the null termination
508
     *   byte 
509
     */
510
    copy_len = G_cmap_to_fname->map_utf8(buf, buflen - 1,
511
                                         strp + VMB_LEN, copy_len, 0);
512
513
    /* null-terminate the result */
514
    buf[copy_len] = '\0';
515
}
516
517
/*
518
 *   Pop a string into a buffer, translating the string into the user
519
 *   interface character set and null-terminating the result.  
520
 */
521
char *CVmBif::pop_str_val_ui(VMG_ char *buf, size_t buflen)
522
{
523
    const char *strp;
524
    size_t copy_len;
525
526
    /* pop the string value */
527
    strp = pop_str_val(vmg0_);
528
529
    /* get the length */
530
    copy_len = vmb_get_len(strp);
531
532
    /* 
533
     *   if they didn't allocate any space for the buffer, allocate one on
534
     *   the caller's behalf 
535
     */
536
    if (buflen == 0)
537
    {
538
        /* figure out how much space we need */
539
        buflen = G_cmap_to_ui->map_utf8(0, 0, strp + VMB_LEN, copy_len, 0);
540
541
        /* add space for null termination */
542
        buflen += 1;
543
544
        /* allocate the buffer */
545
        buf = (char *)t3malloc(buflen);
546
547
        /* if that failed, return null */
548
        if (buf == 0)
549
            return 0;
550
    }
551
552
    /* 
553
     *   map it into the local UI character set and store the result in
554
     *   the output buffer - reserve one byte for the null termination
555
     *   byte 
556
     */
557
    copy_len = G_cmap_to_ui->map_utf8(buf, buflen - 1,
558
                                      strp + VMB_LEN, copy_len, 0);
559
560
    /* null-terminate the result */
561
    buf[copy_len] = '\0';
562
563
    /* return the buffer */
564
    return buf;
565
}
566
567
/* ------------------------------------------------------------------------ */
568
/*
569
 *   Pop an integer value 
570
 */
571
int CVmBif::pop_int_val(VMG0_)
572
{
573
    vm_val_t val;
574
575
    /* pop a number */
576
    G_interpreter->pop_int(vmg_ &val);
577
578
    /* return the value */
579
    return (int)val.val.intval;
580
}
581
582
/*
583
 *   Pop a long integer value 
584
 */
585
int CVmBif::pop_long_val(VMG0_)
586
{
587
    vm_val_t val;
588
589
    /* pop a number */
590
    G_interpreter->pop_int(vmg_ &val);
591
592
    /* return the value */
593
    return val.val.intval;
594
}
595
596
/*
597
 *   Pop a true/nil logical value 
598
 */
599
int CVmBif::pop_bool_val(VMG0_)
600
{
601
    vm_val_t val;
602
603
    /* pop a value */
604
    G_stk->pop(&val);
605
606
    /* check the type */
607
    switch(val.typ)
608
    {
609
    case VM_NIL:
610
        /* nil - interpret this as false */
611
        return FALSE;
612
613
    case VM_TRUE:
614
        /* true */
615
        return TRUE;
616
617
    case VM_INT:
618
        /* integer - return true if it's nonzero */
619
        return (val.val.intval != 0);
620
621
    default:
622
        /* anything else is unacceptable */
623
        err_throw(VMERR_BAD_TYPE_BIF);
624
625
        /* 
626
         *   (for the compiler's benefit, which doesn't know err_throw
627
         *   doesn't return and thus might want to warn about our failure to
628
         *   return a value) 
629
         */
630
        AFTER_ERR_THROW(return FALSE;)
631
    }
632
}
633
634
/*
635
 *   Pop an object reference value
636
 */
637
vm_obj_id_t CVmBif::pop_obj_val(VMG0_)
638
{
639
    vm_val_t val;
640
641
    /* pop an object reference */
642
    G_interpreter->pop_obj(vmg_ &val);
643
644
    /* return the value */
645
    return val.val.obj;
646
}
647
648
/*
649
 *   Pop a property ID value 
650
 */
651
vm_prop_id_t CVmBif::pop_propid_val(VMG0_)
652
{
653
    vm_val_t val;
654
655
    /* pop a property ID */
656
    G_interpreter->pop_prop(vmg_ &val);
657
658
    /* return the value */
659
    return val.val.prop;
660
}
661