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 */
CVmBifTable(size_t init_entries)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 */
~CVmBifTable()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 */
clear()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 */
ensure_space(size_t entries,size_t increment)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 */
add_entry(const char * func_set_id)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 */
check_argc(VMG_ uint argc,uint needed_argc)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 */
check_argc_range(VMG_ uint argc,uint argc_min,uint argc_max)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 */
retval_str(VMG_ const char * str)244 void CVmBif::retval_str(VMG_ const char *str)
245 {
246 retval_str(vmg_ str, strlen(str));
247 }
248
retval_str(VMG_ const char * str,size_t len)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 an object value
262 */
retval_obj(VMG_ vm_obj_id_t obj)263 void CVmBif::retval_obj(VMG_ vm_obj_id_t obj)
264 {
265 if (obj == VM_INVALID_OBJ)
266 G_interpreter->get_r0()->set_nil();
267 else
268 G_interpreter->get_r0()->set_obj(obj);
269 }
270
271 /*
272 * return a property value
273 */
retval_prop(VMG_ vm_prop_id_t prop)274 void CVmBif::retval_prop(VMG_ vm_prop_id_t prop)
275 {
276 G_interpreter->get_r0()->set_propid(prop);
277 }
278
279 /*
280 * return an integer value
281 */
retval_int(VMG_ long val)282 void CVmBif::retval_int(VMG_ long val)
283 {
284 G_interpreter->get_r0()->set_int(val);
285 }
286
287 /*
288 * return true
289 */
retval_true(VMG0_)290 void CVmBif::retval_true(VMG0_)
291 {
292 G_interpreter->get_r0()->set_true();
293 }
294
295 /*
296 * return nil
297 */
retval_nil(VMG0_)298 void CVmBif::retval_nil(VMG0_)
299 {
300 G_interpreter->get_r0()->set_nil();
301 }
302
303 /*
304 * return a boolean value - nil if false, true if true
305 */
retval_bool(VMG_ int val)306 void CVmBif::retval_bool(VMG_ int val)
307 {
308 G_interpreter->get_r0()->set_logical(val);
309 }
310
311 /*
312 * return a function pointer value
313 */
retval_fnptr(VMG_ pool_ofs_t ofs)314 void CVmBif::retval_fnptr(VMG_ pool_ofs_t ofs)
315 {
316 G_interpreter->get_r0()->set_fnptr(ofs);
317 }
318
319 /*
320 * return a value
321 */
retval(VMG_ const vm_val_t * val)322 void CVmBif::retval(VMG_ const vm_val_t *val)
323 {
324 *G_interpreter->get_r0() = *val;
325 }
326
327 /* ------------------------------------------------------------------------ */
328 /*
329 * Pop a string value
330 */
pop_str_val(VMG0_)331 const char *CVmBif::pop_str_val(VMG0_)
332 {
333 vm_val_t val;
334 CVmObject *obj;
335 const char *p;
336
337 /* pop the value */
338 G_stk->pop(&val);
339
340 /* see what we have */
341 switch(val.typ)
342 {
343 case VM_SSTRING:
344 /* it's a constant string - get the constant pool pointer */
345 return G_const_pool->get_ptr(val.val.ofs);
346
347 case VM_OBJ:
348 /* get the object */
349 obj = vm_objp(vmg_ val.val.obj);
350
351 /* get the string value, if it has one */
352 p = obj->get_as_string(vmg0_);
353
354 /* if it has a string value, return it */
355 if (p != 0)
356 return p;
357
358 /* we didn't get a string value */
359 break;
360
361 default:
362 /* other types don't have a string value */
363 break;
364 }
365
366 /* if we got here, the value isn't a string */
367 err_throw(VMERR_STRING_VAL_REQD);
368 return 0;
369 }
370
371 /* ------------------------------------------------------------------------ */
372 /*
373 * Pop a list value
374 */
pop_list_val(VMG0_)375 const char *CVmBif::pop_list_val(VMG0_)
376 {
377 vm_val_t val;
378 CVmObject *obj;
379 const char *p;
380
381 /* pop the value */
382 G_stk->pop(&val);
383
384 /* see what we have */
385 switch(val.typ)
386 {
387 case VM_LIST:
388 /* it's a constant list - get the constant pool pointer */
389 return G_const_pool->get_ptr(val.val.ofs);
390
391 case VM_OBJ:
392 /* get the object */
393 obj = vm_objp(vmg_ val.val.obj);
394
395 /* get the list value, if it has one */
396 p = obj->get_as_list();
397
398 /* if it has a liist value, return it */
399 if (p != 0)
400 return p;
401
402 /* we didn't get a list value */
403 break;
404
405 default:
406 /* other types don't have a list value */
407 break;
408 }
409
410 /* if we got here, the value isn't a list */
411 err_throw(VMERR_LIST_VAL_REQD);
412 return 0;
413 }
414
415 /* ------------------------------------------------------------------------ */
416 /*
417 * Pop a string into a buffer, and null-terminate the result.
418 */
pop_str_val_buf(VMG_ char * buf,size_t buflen)419 void CVmBif::pop_str_val_buf(VMG_ char *buf, size_t buflen)
420 {
421 const char *strp;
422 size_t copy_len;
423
424 /* pop the string value */
425 strp = pop_str_val(vmg0_);
426
427 /*
428 * get the length, but limit it to our buffer size, less one byte
429 * for null termination
430 */
431 copy_len = vmb_get_len(strp);
432 if (copy_len > buflen - 1)
433 copy_len = utf8_ptr::s_trunc(strp + VMB_LEN, buflen - 1);
434
435 /* copy the string */
436 memcpy(buf, strp + VMB_LEN, copy_len);
437
438 /* null-terminate the result */
439 buf[copy_len] = '\0';
440 }
441
442
443 /* ------------------------------------------------------------------------ */
444 /*
445 * Pop a string into a buffer, translating the string into the filename
446 * character set and null-terminating the result.
447 */
pop_str_val_fname(VMG_ char * buf,size_t buflen)448 void CVmBif::pop_str_val_fname(VMG_ char *buf, size_t buflen)
449 {
450 const char *strp;
451 size_t copy_len;
452
453 /* pop the string value */
454 strp = pop_str_val(vmg0_);
455
456 /* get the length */
457 copy_len = vmb_get_len(strp);
458
459 /*
460 * map it into the local filename character set and store the result
461 * in the output buffer - reserve one byte for the null termination
462 * byte
463 */
464 copy_len = G_cmap_to_fname->map_utf8(buf, buflen - 1,
465 strp + VMB_LEN, copy_len, 0);
466
467 /* null-terminate the result */
468 buf[copy_len] = '\0';
469 }
470
471 /*
472 * Pop a string into a buffer, translating the string into the user
473 * interface character set and null-terminating the result.
474 */
pop_str_val_ui(VMG_ char * buf,size_t buflen)475 void CVmBif::pop_str_val_ui(VMG_ char *buf, size_t buflen)
476 {
477 const char *strp;
478 size_t copy_len;
479
480 /* pop the string value */
481 strp = pop_str_val(vmg0_);
482
483 /* get the length */
484 copy_len = vmb_get_len(strp);
485
486 /*
487 * map it into the local UI character set and store the result in
488 * the output buffer - reserve one byte for the null termination
489 * byte
490 */
491 copy_len = G_cmap_to_ui->map_utf8(buf, buflen - 1,
492 strp + VMB_LEN, copy_len, 0);
493
494 /* null-terminate the result */
495 buf[copy_len] = '\0';
496 }
497
498 /* ------------------------------------------------------------------------ */
499 /*
500 * Pop an integer value
501 */
pop_int_val(VMG0_)502 int CVmBif::pop_int_val(VMG0_)
503 {
504 vm_val_t val;
505
506 /* pop a number */
507 G_interpreter->pop_int(vmg_ &val);
508
509 /* return the value */
510 return (int)val.val.intval;
511 }
512
513 /*
514 * Pop a long integer value
515 */
pop_long_val(VMG0_)516 int CVmBif::pop_long_val(VMG0_)
517 {
518 vm_val_t val;
519
520 /* pop a number */
521 G_interpreter->pop_int(vmg_ &val);
522
523 /* return the value */
524 return val.val.intval;
525 }
526
527 /*
528 * Pop a true/nil logical value
529 */
pop_bool_val(VMG0_)530 int CVmBif::pop_bool_val(VMG0_)
531 {
532 vm_val_t val;
533
534 /* pop a value */
535 G_stk->pop(&val);
536
537 /* check the type */
538 switch(val.typ)
539 {
540 case VM_NIL:
541 /* nil - interpret this as false */
542 return FALSE;
543
544 case VM_TRUE:
545 /* true */
546 return TRUE;
547
548 case VM_INT:
549 /* integer - return true if it's nonzero */
550 return (val.val.intval != 0);
551
552 default:
553 /* anything else is unacceptable */
554 err_throw(VMERR_BAD_TYPE_BIF);
555
556 /*
557 * (for the compiler's benefit, which doesn't know err_throw
558 * doesn't return and thus might want to warn about our failure to
559 * return a value)
560 */
561 return FALSE;
562 }
563 }
564
565 /*
566 * Pop an object reference value
567 */
pop_obj_val(VMG0_)568 vm_obj_id_t CVmBif::pop_obj_val(VMG0_)
569 {
570 vm_val_t val;
571
572 /* pop an object reference */
573 G_interpreter->pop_obj(vmg_ &val);
574
575 /* return the value */
576 return val.val.obj;
577 }
578
579 /*
580 * Pop a property ID value
581 */
pop_propid_val(VMG0_)582 vm_prop_id_t CVmBif::pop_propid_val(VMG0_)
583 {
584 vm_val_t val;
585
586 /* pop a property ID */
587 G_interpreter->pop_prop(vmg_ &val);
588
589 /* return the value */
590 return val.val.prop;
591 }
592
593