1 /*
2  * This file is part of DGD, https://github.com/dworkin/dgd
3  * Copyright (C) 1993-2010 Dworkin B.V.
4  * Copyright (C) 2010-2013 DGD Authors (see the commit log for details)
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 # include "dgd.h"
21 # include "str.h"
22 # include "array.h"
23 # include "object.h"
24 # include "xfloat.h"
25 # include "data.h"
26 # include "interpret.h"
27 # include "control.h"
28 # include "table.h"
29 # include <math.h>
30 
31 # define EXTENSION_MAJOR	0
32 # define EXTENSION_MINOR	6
33 
34 
35 /*
36  * NAME:	ext->frame_object()
37  * DESCRIPTION:	return the current object
38  */
ext_frame_object(frame * f)39 static object *ext_frame_object(frame *f)
40 {
41     return (f->lwobj == NULL) ? OBJW(f->oindex) : NULL;
42 }
43 
44 /*
45  * NAME:	ext->frame_dataspace()
46  * DESCRIPTION:	return the current dataspace
47  */
ext_frame_dataspace(frame * f)48 static dataspace *ext_frame_dataspace(frame *f)
49 {
50     return f->data;
51 }
52 
53 /*
54  * NAME:	ext->frame_arg()
55  * DESCRIPTION:	return the given argument
56  */
ext_frame_arg(frame * f,int nargs,int arg)57 static value *ext_frame_arg(frame *f, int nargs, int arg)
58 {
59     return f->sp + nargs - arg - 1;
60 }
61 
62 /*
63  * NAME:	ext->frame_atomic()
64  * DESCRIPTION:	running atomically?
65  */
ext_frame_atomic(frame * f)66 static int ext_frame_atomic(frame *f)
67 {
68     return (f->level != 0);
69 }
70 
71 /*
72  * NAME:	ext->frame_push_int()
73  * DESCRIPTION:	push a number on the stack
74  */
ext_frame_push_int(frame * f,Int i)75 Int ext_frame_push_int(frame *f, Int i)
76 {
77     (--f->sp)->type = T_INT;
78     return f->sp->u.number = i;
79 }
80 
81 /*
82  * NAME:	ext->frame_push_lvalue()
83  * DESCRIPTION:	push an lvalue on the stack
84  */
ext_frame_push_lvalue(frame * f,value * v,int t)85 void ext_frame_push_lvalue(frame *f, value *v, int t)
86 {
87     (--f->sp)->type = T_LVALUE;
88     f->sp->oindex = t;
89     f->sp->u.lval = v;
90 }
91 
92 /*
93  * NAME:	ext->frame_push_lvclass()
94  * DESCRIPTION:	push an lvalue class on the stack
95  */
ext_frame_push_lvclass(frame * f,Int t)96 void ext_frame_push_lvclass(frame *f, Int t)
97 {
98     f->lip->type = T_INT;
99     (f->lip++)->u.number = t;
100 }
101 
102 /*
103  * NAME:	ext->frame_pop_int()
104  * DESCRIPTION:	pop a number from the stack
105  */
ext_frame_pop_int(frame * f)106 Int ext_frame_pop_int(frame *f)
107 {
108     return (f->sp++)->u.number;
109 }
110 
111 /*
112  * NAME:	ext->frame_pop_truthval()
113  * DESCRIPTION:	pop a truth value from the stack
114  */
ext_frame_pop_truthval(frame * f)115 int ext_frame_pop_truthval(frame *f)
116 {
117     switch (f->sp->type) {
118     case T_NIL:
119 	f->sp++;
120 	return FALSE;
121 
122     case T_INT:
123 	return (f->sp++)->u.number != 0;
124 
125     case T_FLOAT:
126 	f->sp++;
127 	return !VFLT_ISZERO(f->sp - 1);
128 
129     case T_STRING:
130 	str_del(f->sp->u.string);
131 	break;
132 
133     case T_ARRAY:
134     case T_MAPPING:
135     case T_LWOBJECT:
136 	arr_del(f->sp->u.array);
137 	break;
138     }
139     f->sp++;
140     return TRUE;
141 }
142 
143 /*
144  * NAME:	ext->frame_store()
145  * DESCRIPTION:	store a value
146  */
ext_frame_store(frame * f)147 void ext_frame_store(frame *f)
148 {
149     i_store(f);
150     --f->sp;
151     f->sp[0] = f->sp[-1];
152 }
153 
154 /*
155  * NAME:	ext->frame_store_int()
156  * DESCRIPTION:	store an integer value
157  */
ext_frame_store_int(frame * f)158 Int ext_frame_store_int(frame *f)
159 {
160     i_store(f);
161     return f->sp[-2].u.number;
162 }
163 
164 /*
165  * NAME:	ext->frame_kfun()
166  * DESCRIPTION:	call a kernel function
167  */
ext_frame_kfun(frame * f,int n)168 void ext_frame_kfun(frame *f, int n)
169 {
170     register kfunc *kf;
171 
172     kf = &kftab[n];
173     if (PROTO_CLASS(kf->proto) & C_TYPECHECKED) {
174 	i_typecheck(f, (frame *) NULL, kf->name, "kfun", kf->proto,
175 		    PROTO_NARGS(kf->proto), TRUE);
176     }
177     n = (*kf->func)(f, PROTO_NARGS(kf->proto), kf);
178     if (n != 0) {
179 	error("Bad argument %d for kfun %s", n, kf->name);
180     }
181 }
182 
183 /*
184  * NAME:	ext->frame_kfun_arg()
185  * DESCRIPTION:	call a kernel function with variable # of arguments
186  */
ext_frame_kfun_arg(frame * f,int n,int nargs)187 void ext_frame_kfun_arg(frame *f, int n, int nargs)
188 {
189     register kfunc *kf;
190 
191     kf = &kftab[n];
192     if (PROTO_CLASS(kf->proto) & C_TYPECHECKED) {
193 	i_typecheck(f, (frame *) NULL, kf->name, "kfun", kf->proto, nargs,
194 		    TRUE);
195     }
196     n = (*kf->func)(f, nargs, kf);
197     if (n != 0) {
198 	error("Bad argument %d for kfun %s", n, kf->name);
199     }
200 }
201 
202 /*
203  * NAME:	ext->value_type()
204  * DESCRIPTION:	return the type of a value
205  */
ext_value_type(value * val)206 static int ext_value_type(value *val)
207 {
208     return val->type;
209 }
210 
211 /*
212  * NAME:	ext->value_nil()
213  * DESCRIPTION:	return nil
214  */
ext_value_nil()215 static value *ext_value_nil()
216 {
217     return &nil_value;
218 }
219 
220 /*
221  * NAME:	ext->value_temp()
222  * DESCRIPTION:	return a scratch value
223  */
ext_value_temp(dataspace * data)224 static value *ext_value_temp(dataspace *data)
225 {
226     static value temp;
227 
228     UNREFERENCED_PARAMETER(data);
229     return &temp;
230 }
231 
232 /*
233  * NAME:	ext->int_getval()
234  * DESCRIPTION:	retrieve an int from a value
235  */
ext_int_getval(value * val)236 static Int ext_int_getval(value *val)
237 {
238     return val->u.number;
239 }
240 
241 /*
242  * NAME:	ext->int_putval()
243  * DESCRIPTION:	store an int in a value
244  */
ext_int_putval(value * val,Int i)245 static void ext_int_putval(value *val, Int i)
246 {
247     PUT_INTVAL(val, i);
248 }
249 
250 /*
251  * NAME:	ext->int_div()
252  * DESCRIPTION:	perform integer division
253  */
ext_int_div(Int i,Int d)254 Int ext_int_div(Int i, Int d)
255 {
256     if (d == 0) {
257 	error("Division by zero");
258     }
259     if ((i | d) < 0) {
260 	Int r;
261 
262 	r = ((Uint) ((i < 0) ? -i : i)) / ((Uint) ((d < 0) ? -d : d));
263 	return ((i ^ d) < 0) ? -r : r;
264     }
265     return ((Uint) i) / ((Uint) d);
266 }
267 
268 /*
269  * NAME:	ext->int_mod()
270  * DESCRIPTION:	perform integer modulus
271  */
ext_int_mod(Int i,Int d)272 Int ext_int_mod(Int i, Int d)
273 {
274     if (d == 0) {
275 	error("Modulus by zero");
276     }
277     if (d < 0) {
278 	d = -d;
279     }
280     if (i < 0) {
281 	return - (Int) (((Uint) -i) % ((Uint) d));
282     }
283     return ((Uint) i) % ((Uint) d);
284 }
285 
286 /*
287  * NAME:	ext->int_lshift()
288  * DESCRIPTION:	perform left shift
289  */
ext_int_lshift(Int i,Int shift)290 Int ext_int_lshift(Int i, Int shift)
291 {
292     if ((shift & ~31) != 0) {
293 	if (shift < 0) {
294 	    error("Negative left shift");
295 	}
296 	return 0;
297     }
298     return (Uint) i << shift;
299 }
300 
301 /*
302  * NAME:	ext->int_rshift()
303  * DESCRIPTION:	perform right shift
304  */
ext_int_rshift(Int i,Int shift)305 Int ext_int_rshift(Int i, Int shift)
306 {
307     if ((shift & ~31) != 0) {
308 	if (shift < 0) {
309 	    error("Negative right shift");
310 	}
311 	return 0;
312     }
313     return (Uint) i >> shift;
314 }
315 
316 # ifndef NOFLOAT
317 /*
318  * NAME:	ext->float_getval()
319  * DESCRIPTION:	retrieve a float from a value
320  */
ext_float_getval(value * val)321 static long double ext_float_getval(value *val)
322 {
323     xfloat flt;
324     register double d;
325 
326     GET_FLT(val, flt);
327     if ((flt.high | flt.low) == 0) {
328 	return 0.0;
329     } else {
330 	d = ldexp((double) (0x10 | (flt.high & 0xf)), 32);
331 	d = ldexp(d + flt.low, ((flt.high >> 4) & 0x7ff) - 1023 - 36);
332 	return (long double) ((flt.high >> 15) ? -d : d);
333     }
334 }
335 
336 /*
337  * NAME:	ext->float_putval()
338  * DESCRIPTION:	store a float in a value
339  */
ext_float_putval(value * val,long double ld)340 static void ext_float_putval(value *val, long double ld)
341 {
342     double d;
343     xfloat flt;
344     bool sign;
345     int e;
346 
347     d = (double) ld;
348     if (d == 0.0) {
349 	flt.high = 0;
350 	flt.low = 0;
351     } else {
352 	sign = (d < 0.0);
353 	d = ldexp(frexp(fabs(d), &e), 5);
354 	flt.high = ((unsigned short) sign << 15) | ((e - 1 + 1023) << 4) |
355 		   ((unsigned short) d & 0xf);
356 	flt.low = (Uint) (ldexp(d - (int) d, 32) + 0.5);
357     }
358     PUT_FLTVAL(val, flt);
359 }
360 # endif
361 
362 /*
363  * NAME:	ext->string_getval()
364  * DESCRIPTION:	retrieve a string from a value
365  */
ext_string_getval(value * val)366 static string *ext_string_getval(value *val)
367 {
368     return val->u.string;
369 }
370 
371 /*
372  * NAME:	ext->string_putval()
373  * DESCRIPTION:	store a string in a value
374  */
ext_string_putval(value * val,string * str)375 static void ext_string_putval(value *val, string *str)
376 {
377     PUT_STRVAL_NOREF(val, str);
378 }
379 
380 /*
381  * NAME:	ext->string_new()
382  * DESCRIPTION:	create a new string
383  */
ext_string_new(dataspace * data,char * text,int len)384 static string *ext_string_new(dataspace *data, char *text, int len)
385 {
386     UNREFERENCED_PARAMETER(data);
387     return str_new(text, len);
388 }
389 
390 /*
391  * NAME:	ext->string_text()
392  * DESCRIPTION:	return string text
393  */
ext_string_text(string * str)394 static char *ext_string_text(string *str)
395 {
396     return str->text;
397 }
398 
399 /*
400  * NAME:	ext->string_length()
401  * DESCRIPTION:	return string length
402  */
ext_string_length(string * str)403 static int ext_string_length(string *str)
404 {
405     return str->len;
406 }
407 
408 /*
409  * NAME:	ext->object_putval()
410  * DESCRIPTION:	store an object in a value
411  */
ext_object_putval(value * val,object * obj)412 static void ext_object_putval(value *val, object *obj)
413 {
414     PUT_OBJVAL(val, obj);
415 }
416 
417 /*
418  * NAME:	ext->object_name()
419  * DESCRIPTION:	store the name of an object
420  */
ext_object_name(frame * f,object * obj,char * buf)421 static char *ext_object_name(frame *f, object *obj, char *buf)
422 {
423     UNREFERENCED_PARAMETER(f);
424     return o_name(buf, obj);
425 }
426 
427 /*
428  * NAME:	ext->object_isspecial()
429  * DESCRIPTION:	return TRUE if the given object is special, FALSE otherwise
430  */
ext_object_isspecial(object * obj)431 static int ext_object_isspecial(object *obj)
432 {
433     return ((obj->flags & O_SPECIAL) != 0);
434 }
435 
436 /*
437  * NAME:	ext->object_ismarked()
438  * DESCRIPTION:	return TRUE if the given object is marked, FALSE otherwise
439  */
ext_object_ismarked(object * obj)440 static int ext_object_ismarked(object *obj)
441 {
442     return ((obj->flags & O_SPECIAL) == O_SPECIAL);
443 }
444 
445 /*
446  * NAME:	ext->object_mark()
447  * DESCRIPTION:	mark the given object
448  */
ext_object_mark(object * obj)449 static void ext_object_mark(object *obj)
450 {
451     obj->flags |= O_SPECIAL;
452 }
453 
454 /*
455  * NAME:	ext->object_unmark()
456  * DESCRIPTION:	unmark the given object
457  */
ext_object_unmark(object * obj)458 static void ext_object_unmark(object *obj)
459 {
460     obj->flags &= ~O_SPECIAL;
461 }
462 
463 /*
464  * NAME:	ext->array_getval()
465  * DESCRIPTION:	retrieve an array from a value
466  */
ext_array_getval(value * val)467 static array *ext_array_getval(value *val)
468 {
469     return val->u.array;
470 }
471 
472 /*
473  * NAME:	ext->array_putval()
474  * DESCRIPTION:	store an array in a value
475  */
ext_array_putval(value * val,array * a)476 static void ext_array_putval(value *val, array *a)
477 {
478     PUT_ARRVAL_NOREF(val, a);
479 }
480 
481 /*
482  * NAME:	ext->array_new()
483  * DESCRIPTION:	create a new array
484  */
ext_array_new(dataspace * data,int size)485 static array *ext_array_new(dataspace *data, int size)
486 {
487     return arr_ext_new(data, size);
488 }
489 
490 /*
491  * NAME:	ext->array_index()
492  * DESCRIPTION:	return an array element
493  */
ext_array_index(array * a,int i)494 static value *ext_array_index(array *a, int i)
495 {
496     return &d_get_elts(a)[i];
497 }
498 
499 /*
500  * NAME:	ext->array_assign()
501  * DESCRIPTION:	assign a value to an array element
502  */
ext_array_assign(dataspace * data,array * a,int i,value * val)503 static void ext_array_assign(dataspace *data, array *a, int i, value *val)
504 {
505     d_assign_elt(data, a, &d_get_elts(a)[i], val);
506 }
507 
508 /*
509  * NAME:	ext->array_size()
510  * DESCRIPTION:	return the size of an array
511  */
ext_array_size(array * a)512 static int ext_array_size(array *a)
513 {
514     return a->size;
515 }
516 
517 /*
518  * NAME:	ext->mapping_putval()
519  * DESCRIPTION:	store a mapping in a value
520  */
ext_mapping_putval(value * val,array * m)521 static void ext_mapping_putval(value *val, array *m)
522 {
523     PUT_MAPVAL_NOREF(val, m);
524 }
525 
526 /*
527  * NAME:	ext->mapping_new()
528  * DESCRIPTION:	create a new mapping
529  */
ext_mapping_new(dataspace * data)530 static array *ext_mapping_new(dataspace *data)
531 {
532     return map_new(data, 0);
533 }
534 
535 /*
536  * NAME:	ext->mapping_index()
537  * DESCRIPTION:	return a value from a mapping
538  */
ext_mapping_index(array * m,value * idx)539 static value *ext_mapping_index(array *m, value *idx)
540 {
541     return map_index(m->primary->data, m, idx, (value *) NULL, (value *) NULL);
542 }
543 
544 /*
545  * NAME:	ext->mapping_assign()
546  * DESCRIPTION:	assign to a mapping value
547  */
ext_mapping_assign(dataspace * data,array * m,value * idx,value * val)548 static void ext_mapping_assign(dataspace *data, array *m, value *idx,
549 			       value *val)
550 {
551     map_index(data, m, idx, val, (value *) NULL);
552 }
553 
554 /*
555  * NAME:	ext->mapping_enum()
556  * DESCRIPTION:	return the nth enumerated index
557  */
ext_mapping_enum(array * m,int i)558 static value *ext_mapping_enum(array *m, int i)
559 {
560     map_compact(m->primary->data, m);
561     return &d_get_elts(m)[i];
562 }
563 
564 /*
565  * NAME:	ext->mapping_size()
566  * DESCRIPTION:	return the size of a mapping
567  */
ext_mapping_size(array * m)568 static int ext_mapping_size(array *m)
569 {
570     return map_size(m->primary->data, m);
571 }
572 
573 /*
574  * NAME:	ext->runtime_error()
575  * DESCRIPTION:	handle an error at runtime
576  */
ext_runtime_error(frame * f,char * mesg)577 static void ext_runtime_error(frame *f, char *mesg)
578 {
579     UNREFERENCED_PARAMETER(f);
580     error(mesg);
581 }
582 
583 /*
584  * NAME:	ext->runtime_rlimits()
585  * DESCRIPTION:	handle rlimits
586  */
ext_runtime_rlimits(frame * f)587 void ext_runtime_rlimits(frame *f)
588 {
589     if (f->sp[1].type != T_INT) {
590 	error("Bad rlimits depth type");
591     }
592     if (f->sp->type != T_INT) {
593 	error("Bad rlimits ticks type");
594     }
595     f->sp += 2;
596 
597     i_new_rlimits(f, f->sp[-1].u.number, f->sp[-2].u.number);
598 }
599 
600 /*
601  * NAME:	ext->runtime_rswitch()
602  * DESCRIPTION:	handle a range switch
603  */
ext_runtime_rswitch(Int i,Int * tab,int h)604 int ext_runtime_rswitch(Int i, Int *tab, int h)
605 {
606     register int l, m;
607     register Int *t;
608 
609     l = 0;
610     do {
611 	m = (l + h) >> 1;
612 	t = tab + (m << 1);
613 	if (i < *t++) {
614 	    h = m;	/* search in lower half */
615 	} else if (i > *t) {
616 	    l = m + 1;	/* search in upper half */
617 	} else {
618 	    return m + 1;	/* found */
619 	}
620     } while (l < h);
621 
622     return 0;		/* not found */
623 }
624 
625 /*
626  * NAME:	ext->runtime_sswitch()
627  * DESCRIPTION:	handle a str switch
628  */
ext_runtime_sswitch(frame * f,char * tab,int h)629 int ext_runtime_sswitch(frame *f, char *tab, int h)
630 {
631     register int l, m, c;
632     register char *t;
633     register string *s;
634     value *v;
635 
636     v = f->sp++;
637     if (VAL_NIL(v)) {
638 	return (tab[0] == 0);
639     } else if (v->type != T_STRING) {
640 	i_del_value(v);
641 	return 0;
642     }
643 
644     s = v->u.string;
645     if (*tab++ == 0) {
646 	tab -= 3;
647 	l = 1;
648     } else {
649 	l = 0;
650     }
651 
652     do {
653 	m = (l + h) >> 1;
654 	t = tab + 3 * m;
655 	c = str_cmp(s, d_get_strconst(f->p_ctrl, t[0],
656 				      (UCHAR(t[1]) << 8) + UCHAR(t[2])));
657 	if (c == 0) {
658 	    str_del(s);
659 	    return m + 1;	/* found */
660 	} else if (c < 0) {
661 	    h = m;	/* search in lower half */
662 	} else {
663 	    l = m + 1;	/* search in upper half */
664 	}
665     } while (l < h);
666 
667     str_del(s);
668     return 0;		/* not found */
669 }
670 
671 /*
672  * NAME:	ext->dgd()
673  * DESCRIPTION:	initialize extension interface
674  */
ext_dgd(char * module)675 bool ext_dgd(char *module)
676 {
677     voidf *ext_ext[1];
678     voidf *ext_frame[4];
679     voidf *ext_data[2];
680     voidf *ext_value[3];
681     voidf *ext_int[2];
682     voidf *ext_float[2];
683     voidf *ext_string[5];
684     voidf *ext_object[6];
685     voidf *ext_array[6];
686     voidf *ext_mapping[7];
687     voidf *ext_runtime[1];
688     voidf **ftabs[11];
689     int sizes[11];
690     int (*init) (int, int, voidf**[], int[]);
691 
692     init = (int (*) (int, int, voidf**[], int[])) P_dload(module, "ext_init");
693     if (init == NULL) {
694 	return FALSE;
695     }
696 
697     ext_ext[0] = (voidf *) &kf_ext_kfun;
698     ext_frame[0] = (voidf *) &ext_frame_object;
699     ext_frame[1] = (voidf *) &ext_frame_dataspace;
700     ext_frame[2] = (voidf *) &ext_frame_arg;
701     ext_frame[3] = (voidf *) &ext_frame_atomic;
702     ext_data[0] = (voidf *) &d_get_extravar;
703     ext_data[1] = (voidf *) &d_set_extravar;
704     ext_value[0] = (voidf *) &ext_value_type;
705     ext_value[1] = (voidf *) &ext_value_nil;
706     ext_value[2] = (voidf *) &ext_value_temp;
707     ext_int[0] = (voidf *) &ext_int_getval;
708     ext_int[1] = (voidf *) &ext_int_putval;
709 # ifndef NOFLOAT
710     ext_float[0] = (voidf *) &ext_float_getval;
711     ext_float[1] = (voidf *) &ext_float_putval;
712 # else
713     ext_float[0] = (voidf *) NULL;
714     ext_float[1] = (voidf *) NULL;
715 # endif
716     ext_string[0] = (voidf *) &ext_string_getval;
717     ext_string[1] = (voidf *) &ext_string_putval;
718     ext_string[2] = (voidf *) &ext_string_new;
719     ext_string[3] = (voidf *) &ext_string_text;
720     ext_string[4] = (voidf *) &ext_string_length;
721     ext_object[0] = (voidf *) &ext_object_putval;
722     ext_object[1] = (voidf *) &ext_object_name;
723     ext_object[2] = (voidf *) &ext_object_isspecial;
724     ext_object[3] = (voidf *) &ext_object_ismarked;
725     ext_object[4] = (voidf *) &ext_object_mark;
726     ext_object[5] = (voidf *) &ext_object_unmark;
727     ext_array[0] = (voidf *) &ext_array_getval;
728     ext_array[1] = (voidf *) &ext_array_putval;
729     ext_array[2] = (voidf *) &ext_array_new;
730     ext_array[3] = (voidf *) &ext_array_index;
731     ext_array[4] = (voidf *) &ext_array_assign;
732     ext_array[5] = (voidf *) &ext_array_size;
733     ext_mapping[0] = (voidf *) &ext_array_getval;
734     ext_mapping[1] = (voidf *) &ext_mapping_putval;
735     ext_mapping[2] = (voidf *) &ext_mapping_new;
736     ext_mapping[3] = (voidf *) &ext_mapping_index;
737     ext_mapping[4] = (voidf *) &ext_mapping_assign;
738     ext_mapping[5] = (voidf *) &ext_mapping_enum;
739     ext_mapping[6] = (voidf *) &ext_mapping_size;
740     ext_runtime[0] = (voidf *) &ext_runtime_error;
741 
742     ftabs[ 0] = ext_ext;	sizes[ 0] = 1;
743     ftabs[ 1] = ext_frame;	sizes[ 1] = 4;
744     ftabs[ 2] = ext_data;	sizes[ 2] = 2;
745     ftabs[ 3] = ext_value;	sizes[ 3] = 3;
746     ftabs[ 4] = ext_int;	sizes[ 4] = 2;
747     ftabs[ 5] = ext_float;	sizes[ 5] = 2;
748     ftabs[ 6] = ext_string;	sizes[ 6] = 5;
749     ftabs[ 7] = ext_object;	sizes[ 7] = 6;
750     ftabs[ 8] = ext_array;	sizes[ 8] = 6;
751     ftabs[ 9] = ext_mapping;	sizes[ 9] = 7;
752     ftabs[10] = ext_runtime;	sizes[10] = 1;
753 
754     if (!init(EXTENSION_MAJOR, EXTENSION_MINOR, ftabs, sizes)) {
755 	fatal("incompatible runtime extension");
756     }
757     return TRUE;
758 }
759