/* Copyright 1991, 1993 - 1997 J"orn Rennecke */ #include <stdarg.h> #include <stdio.h> #include "common.h" #include "interpret.h" #include "instrs.h" #include "object.h" #include "uid.h" unsigned char const_invalid[2] = { T_INVALID, 0 }; struct lvalue catch_value = { T_LVALUE, /* type */ 255, /* ref */ LVALUE_CATCH, /* lvalue_type */ }; static struct lvalue fresh_char_lvalue = { T_CHAR_LVALUE }; svalue fresh_char_lvalue_svalue = { (p_int)&fresh_char_lvalue.ref }; #if defined(i386) /* * With the current problems of gcc in register allocation, it is too * expensive to keep fp in a register on i386. Thus we can consider * everything that can be adressed with an 8 bit offset on the stack * to be a 'register' in the same class. Plus there are two registers * for sp and pc that are in a better class - being real registers. */ #define FP_CLASS_REGISTERS 34 #endif #ifndef FP_CLASS_REGISTERS #define FP_CLASS_REGISTERS 3 #endif #if defined(i386) && defined(__GNUC__) #if 1 #define RG0 asm("%ebx") #endif #if 1 #define RG1 asm("%esi") #endif #endif #ifndef RG0 #define RG0 #endif #ifndef RG1 #define RG1 #endif #ifndef RG3 #define RG3 #endif int inter_errno, last_eval_cost, eval_cost, tracing; enum eval_state eval_switch; struct inter_stack inter_stack; svalue *inter_sp, *inter_general_stack_bottom; struct frame *inter_fp, *inter_ex_fp, *inter_external_stack_bottom; static void free_lvalue(svalue); static svalue add_number(svalue, svalue); static void do_trace(struct frame *fp, svalue* sp, uint8* pc); #define CHECK_TIME (eval_switch &= eval_cost >> (8 * (sizeof eval_cost) - 1)) INLINE void transfer_svalue(svalue *dest, svalue source) { svalue sv; sv = *dest; if (!SV_IS_NUMBER(sv)) { if (IS_LVALUE(sv)) { switch(SV_LVALUE(sv).type) { default: fatal("Unknown lvalue type\n"); return; case LVALUE_SIMPLE: case LVALUE_PROTECTED: dest = SV_LVALUE(sv).lvalue; sv = *dest; } } FREE_SVALUE(sv); } *dest = source; } static svalue indexing_nirwana; enum byte { z=0, m1 = 255}; svalue interpreter( register struct frame *fp, register svalue *sp) { register uint8 *pc RG1; #if FP_CLASS_REGISTERS > 3 svalue *fp_variable; #else #define fp_variable fp->variable #endif #if FP_CLASS_REGISTERS > 4 union control_virtual fp_virtual; #else #define fp_virtual fp->virtual #endif #if FP_CLASS_REGISTERS > 5 svalue *fp_shared; #else #define fp_shared fp->shared #endif new_function_call: pc = fp->funstart; #ifndef fp_variable fp_variable = fp->variable; #endif #ifndef fp_virtual fp_virtual = fp->virtual; #endif #ifndef fp_shared fp_shared = fp->shared; #endif check_time: CHECK_TIME; for(;;) { int instruction; next_instruction: instruction = *pc++ & eval_switch; dispatch_anew: eval_cost++; switch((uint8)instruction) { case F_ERROR: if (!inter_errno) { if (eval_cost >= 0) { inter_errno = IE_EVALCOST; } else if (eval_switch) { fatal("Illegal Instruction 0\n"); break; } else if (tracing) { do_trace(fp, sp, pc); instruction = pc[-1]; goto dispatch_anew; } } return CONST_INVALID; case 255: fatal("Illegal Instruction (selected values to span the switch)\n"); return SV_NULL; default: fatal("Illegal Instruction\n"); return SV_NULL; case F_RETURN0: (*++sp).i = 0; case F_RETURN: { svalue tos; tos = *sp--; /* * Deallocate local variables, unpopped values from lazy popping, and * break return adresses. No lvalues here. * Usually, sp >= &fp->locals[-1]. But efun closures directly eat up * the arguments, and fp > sp. */ while (sp >= &fp->locals[0]) { svalue sv = *sp; FREE_SVALUE(sv); sp--; } switch(fp->return_mode.i) { case IR_LOCAL_XF: inter_ex_fp--; case IR_LOCAL: { int i; i = fp->funstart[-2]; sp = &fp->arguments[0]; if (i) do { svalue arg; arg = *sp--; if (!SV_IS_NUMBER(arg)) { if (IS_ALLOCED_LVALUE(arg)) { free_lvalue(arg); } else { FREE_ALLOCED_SVALUE(arg); } } } while (--i); *++sp = tos; pc = fp->pc; fp = fp->previous; break; } case IR_EXTERN_XF: inter_ex_fp--; case IR_EXTERN: return tos; case IR_CATCH: sp = &fp->arguments[0]; fp = fp->previous; *sp = tos; break; default: { /* * New function call. We don't know how many arguments there are, * thus (*fp->return_mode.fun)() has to call make_frame() . */ struct control_ret cntret; cntret = (*fp->return_mode.fun)(tos, fp); fp = cntret.fp; sp = cntret.sp; goto new_function_call; } } break; } case F_CALL_FUNCTION_BY_INDEX: { int num_arg, ix, fx, iix; struct program *prog; svalue *variables; struct control_ret cntret; num_arg = *pc; ix = UEXTRACT16(pc+1); prog = SV_OBJECT(fp->object).program; variables = fp->variable; fx = ix; iix = prog->flag.many_inherits ? fp_virtual.function_16[fx] : fp_virtual.function_8[fx]; while(iix > 0) { struct inherit *inheritp; inheritp = &prog->inherit[iix]; prog = (struct program *)(inheritp->program & ~3); fx -= inheritp->virtual_offset; variables += inheritp->variable_offset; iix = prog->virtual.function_8[fx]; } ix -= fx; if (iix < 0) fx = iix + prog->redefine_offset; cntret = make_frame(sp, num_arg, PR_PCODE(prog) + prog->new_function[fx].start); cntret.fp->variable = variables; cntret.fp->previous = fp; cntret.fp->virtual.function_8 = fp_virtual.function_8 + ix; cntret.fp->object = fp->object; fp = cntret.fp; fp->pc = pc + 3; fp->program = prog; fp->shared = prog->shared; fp->return_mode.i = IR_LOCAL; sp = cntret.sp; goto new_function_call; } case F_CALL_OTHER: { int num_arg; svalue ob; struct call_cache_entry *centry; struct control_ret cntret; struct program *prog; num_arg = *pc++; ob = sp[1-num_arg]; if (SV_IS_NUMBER(ob)) goto bad_arg_1; if (SV_TYPE(ob) != T_OBJECT) { if (!SV_IS_STRING(ob)) goto bad_arg_1; inter_sp = sp; inter_fp = fp; ob = find_object(ob, MAX_INHERIT_DEPTH); if (!ob.p) goto bad_arg_1; } ASSIGN_EVAL_COST(&SV_OBJECT(fp->object)); { struct cache_call_ret ccret; ccret = cache_call(ob, sp[2-num_arg], fp); ob = ccret.u.ob; centry = ccret.entry; } if (!centry) { svalue sv; do { sv = *sp--; FREE_SVALUE(sv); } while(--num_arg); break; } cntret = make_frame(sp, num_arg - 2, centry->funstart); cntret.fp->previous = fp; fp = cntret.fp; fp->pc = pc; fp->funstart = centry->funstart; fp->object = ob; fp->variable = SV_OBJECT(ob).variable + centry->cache_variable_index_offset; prog = centry->program; fp->program = prog; fp->shared = prog->shared; fp->virtual.function_8 = prog->virtual.function_8 + centry->cache_virtual_index_offset; fp->return_mode.i = IR_LOCAL; sp = cntret.sp; goto new_function_call; } case F_VARARGS: if (!sp->i) *sp = REF_INC(NIL_ARRAY); break; { svalue sv; case F_THIS_OBJECT: sv = fp->object; goto push; case F_V_VIRTUAL: sv = fp_variable[ fp_virtual.variable[pc[0]] + pc [1] ]; pc += 2; goto push; case F_V_GLOBAL16: sv = fp->variable[UEXTRACT16(pc)]; pc += 2; goto push; case F_V_GLOBAL: sv = fp->variable[*pc++]; goto push; case F_V_LOCAL: /* Could extend offset to -0x10f if compiler would always use F_PICK? where appropriate. */ sv = sp[*pc++ - 0xff]; goto push; case F_PICK0: case F_PICK1: case F_PICK2: case F_PICK3: case F_PICK4: case F_PICK5: case F_PICK6: case F_PICK7: case F_PICK8: case F_PICK9: case F_PICKA: case F_PICKB: case F_PICKC: case F_PICKD: case F_PICKE: case F_PICKF: sv = sp[ pc[-1] - F_PICK0 ]; goto push; case F_V_PARAM: sv = fp->arguments[*pc++ - 0xff]; goto push; case F_SHARED: sv = fp_shared[UEXTRACT16(pc)]; pc += 2; goto push; case F_CSHARED3: sv = fp_shared[*pc++ + 0x300]; goto push; case F_CSHARED2: sv = fp_shared[*pc++ + 0x200]; goto push; case F_CSHARED1: sv = fp_shared[*pc++ + 0x100]; goto push; case F_CSHARED0: sv = fp_shared[*pc++]; push: COPY_SVALUE_IN_VAR(sv); *++sp = sv; break; } case F_NCLIT: (++sp)->i = -*pc++ << 1; break; case F_CLIT: (++sp)->i = *pc++ << 1; break; case F_CONST0: (++sp)->i = 0 << 1; break; case F_CONST1: (++sp)->i = 1 << 1; break; { svalue *svp; enum use_lvalue_code code; case F_LV_NIL: { svalue old, new; pc++; new = *sp; if (SV_IS_NUMBER(new)) goto ulv_bad_left; svp = &indexing_nirwana; old = *svp; FREE_SVALUE(old); #ifndef RISC /* preserving new across _free_svalue() makes svp go into memory on i386 */ new = *sp; #endif *svp = new; sp--; goto use_lvalue; } case F_LV_PARAM: svp = &fp->arguments[*pc - 0xff]; pc += 2; goto use_lvalue; case F_LV_VIRTUAL: { svp = &fp_variable[ fp_virtual.variable[pc[0]] + pc [1] ]; pc += 3; goto use_lvalue; } case F_LV_GLOBAL16: svp = &fp_variable[UEXTRACT16(pc)]; pc += 3; goto use_lvalue; case F_LV_GLOBAL: svp = &fp_variable[*pc]; pc += 2; goto use_lvalue; case F_LV_LOCAL16: svp = &sp[UEXTRACT16(pc) - 0x100ff]; pc += 3; goto use_lvalue; case F_LV_LOCAL: svp = &sp[*pc - 0xff]; pc += 2; use_lvalue: code = pc[-1]; use_lvalue_dispatch: switch(code) { case ULV_ASSIGN: { svalue sv = *svp; FREE_SVALUE(sv); sv = *sp; *svp = sv; COPY_SVALUE_IN_VAR(sv); *sp = sv; /* in case of ENOMEM, the bogus 0 is on the stack, not in the variable */ break; } case ULV_VOID_ASSIGN: { svalue sv = *svp; FREE_SVALUE(sv); *svp = *(sp--); break; } case ULV_HAIRY_ASSIGN: { svalue sv = *sp; /* * In case of ENOMEM, we don't leave a value, but the assignment is * correct. */ if (_COPY_SVALUE_IN_VAR(sv)) *++sp = sv; } case ULV_VOID_HAIRY_ASSIGN: { /* * Inlining transfer_svalue() does not work right, because the source * is fetched too early, resulting in a register loss. */ svalue sv; sv = *svp; if (!SV_IS_NUMBER(sv)) { if (IS_LVALUE(sv)) { switch(SV_LVALUE(sv).lvalue_type) { default: fatal("Unknown lvalue type\n"); continue; case LVALUE_CBR_CHAR: { svalue sv2; sv2 = *SV_LVALUE(sv).lvalue; if (sv2.p != SV_LVALUE(sv).parent.p || !SV_IS_2REF_STRING(sv2)) { sv2 = *sp; if (!SV_IS_NUMBER(sv2)) deallocate_bogus_char: FREE_ALLOCED_SVALUE(sv2); } else { case LVALUE_CHAR: sv2 = *sp; if (!SV_IS_NUMBER(sv2)) goto deallocate_bogus_char; *SV_LVALUE(sv).index2.p = sv2.i >> 1; } sp--; goto next_instruction; } case LVALUE_SIMPLE: case LVALUE_PROTECTED: svp = SV_LVALUE(sv).lvalue; sv = *svp; } } FREE_ALLOCED_SVALUE(sv); } *svp = *sp--; break; } case ULV_LV_SINDEX: (++sp)->i = UEXTRACT16(pc) << 1; pc += 2; goto ulv_lv_index; case ULV_LV_CINDEX: { (++sp)->i = *pc++ << 1; } ulv_lv_index: case ULV_LV_INDEX: { svalue sv = *svp; switch(SV_TYPE(sv)) { case T_LSTRING: case T_ILSTRING: case T_GLSTRING: { uint8 *str; mp_uint len, i; str = SV_LSTRING(sv); len = SV_LSTRLEN(sv); goto ulv_lv_index_got_string; case T_STRING: if (SV_REF(sv) != 1) { do { str = SV_STRING(sv); len = SV_STRLEN(sv); FREE_ALLOCED_SVALUE(sv); *svp = sv = make_astring(str, len); goto ulv_lv_index_got_strlen; case T_GSTRING: if (SV_REF(sv) == 1) { *svp = unshare_string(sv); goto ulv_lv_index_get_string; } case T_ISTRING:; } while (SV_REF(sv) != 1); SV_TYPE_LOC(sv) += T_STRING - T_ISTRING; } ulv_lv_index_get_string: len = SV_STRLEN(sv); ulv_lv_index_got_strlen: str = SV_STRING(sv); ulv_lv_index_got_string: i = (sp--)->i; if (i & 1) goto ulv_lv_bad_right; i >>= 1; if (i >= len) goto ulv_index_error; fresh_char_lvalue.index2.p = &str[i]; svp = &fresh_char_lvalue_svalue; break; } case T_MAPPING: svp = get_map_lvalue(sv, *sp--, 1); break; case T_LARRAY: { mp_uint len, i; len = SV_ARRAY(sv).x.x->len; goto ulv_lv_index_got_alen; case T_ARRAY: len = SV_ARRAY(sv).len; ulv_lv_index_got_alen: i = (sp--)->i; if (i & 1) goto ulv_lv_bad_right; i >>= 1; if (i >= len) { ulv_index_error: /* The index has already been verified to be an integer, thus it doesn't matter if sp points to it - and it thus will be freed - or not. */ inter_errno = IE_BAD_INDEX; error_arg[0].i = (pc[-1] + ULV_CLOSURE_OFFSET) << 1; goto raise_error; } svp = SV_ARRAY(sv).member + i; break; } } code = eval_switch & *pc++; goto use_lvalue_dispatch; } case ULV_LV_SRINDEX: (++sp)->i = UEXTRACT16(pc) << 1; pc += 2; goto ulv_lv_rindex; case ULV_LV_CRINDEX: { (++sp)->i = *pc++ << 1; } ulv_lv_rindex: case ULV_LV_RINDEX: { svalue sv = *svp; switch(SV_TYPE(sv)) { case T_LSTRING: case T_ILSTRING: case T_GLSTRING: { uint8 *str; mp_uint len, i; str = SV_LSTRING(sv); len = SV_LSTRLEN(sv); goto ulv_lv_rindex_got_string; case T_STRING: case T_ISTRING: case T_GSTRING: str = SV_STRING(sv); len = SV_STRLEN(sv); ulv_lv_rindex_got_string: i = (sp--)->i; if (i & 1) goto ulv_lv_bad_right; i >>= 1; i = -i; i -= len; if (i >= len) goto ulv_index_error; fresh_char_lvalue.index2.p = &str[i]; svp = &fresh_char_lvalue_svalue; break; } case T_LARRAY: { mp_uint len, i; len = SV_ARRAY(sv).x.x->len; goto ulv_lv_rindex_got_len; case T_ARRAY: len = SV_ARRAY(sv).len; ulv_lv_rindex_got_len: i = (sp--)->i; if (i & 1) goto ulv_lv_bad_right; i >>= 1; i = -i; i -= len; if (i >= len) goto ulv_index_error; svp = SV_ARRAY(sv).member + i; break; } } code = eval_switch & *pc++; goto use_lvalue_dispatch; } case ULV_SINDEX: (++sp)->i = UEXTRACT16(pc) << 1; pc += 2; goto ulv_index; case ULV_CINDEX: { (++sp)->i = *pc++ << 1; } ulv_index: case ULV_INDEX: { svalue sv = *svp; switch(SV_TYPE(sv)) { case T_LSTRING: case T_ILSTRING: case T_GLSTRING: { uint8 *str; mp_uint len, i; str = SV_LSTRING(sv); len = SV_LSTRLEN(sv); goto ulv_index_got_string; case T_STRING: case T_ISTRING: case T_GSTRING: str = SV_STRING(sv); len = SV_STRLEN(sv); ulv_index_got_string: i = sp->i; if (i & 1) goto ulv_bad_right; i >>= 1; if (i >= len) goto ulv_index_error; (sp)->i = str[i] << 1; goto next_instruction; } case T_MAPPING: svp = get_map_lvalue(sv, *sp, 0); FREE_SVALUE(*sp); break; case T_LARRAY: { mp_uint len, i; len = SV_ARRAY(sv).x.x->len; goto ulv_index_got_len; case T_ARRAY: len = SV_ARRAY(sv).len; ulv_index_got_len: i = sp->i; if (i & 1) goto ulv_bad_right; i >>= 1; if (i >= len) goto ulv_index_error; svp = SV_ARRAY(sv).member + i; break; } } sv = *svp; COPY_SVALUE_IN_VAR(sv); *sp = sv; break; } case ULV_RINDEX: { svalue sv = *svp; switch(SV_TYPE(sv)) { case T_LSTRING: case T_ILSTRING: case T_GLSTRING: { uint8 *str; mp_uint len, i; str = SV_LSTRING(sv); len = SV_LSTRLEN(sv); goto ulv_rindex_got_string; case T_STRING: case T_ISTRING: case T_GSTRING: str = SV_STRING(sv); len = SV_STRLEN(sv); ulv_rindex_got_string: i = sp->i; if (i & 1) goto ulv_bad_right; i >>= 1; i = -i; i -= len; if (i >= len) goto ulv_index_error; sp->i = str[i] << 1; goto next_instruction; } case T_LARRAY: { mp_uint len, i; len = SV_ARRAY(sv).x.x->len; goto ulv_rindex_got_len; case T_ARRAY: len = SV_ARRAY(sv).len; ulv_rindex_got_len: i = sp->i; if (i & 1) goto ulv_bad_right; i >>= 1; i = -i; i -= len; if (i >= len) goto ulv_index_error; svp = SV_ARRAY(sv).member + i; break; } } sv = *svp; COPY_SVALUE_IN_VAR(sv); *sp = sv; break; } case ULV_PRE_DEC: case ULV_POST_DEC: case ULV_PRE_INC: case ULV_POST_INC: { static int offtab[] = { 0, 2, 0, 0, 0, -2 }; svalue sv = *svp; if (SV_IS_NUMBER(sv)) { int index = code - ULV_PRE_DEC; svp->i = sv.i = sv.i + (code & 4) - 2; index = pc[-1] - ULV_PRE_DEC; sv.i += offtab[index]; (++sp)->i = sv.i; } else switch(SV_TYPE(sv)) { } break; } case ULV_PRE_DEC_BBRANCH: { svalue sv = *svp; if (SV_IS_NUMBER(sv)) { svp->i = sv.i - 2; if (sv.i) goto bbranch; goto branch_not_taken; } else switch(SV_TYPE(sv)) { } break; } #ifdef RISC #define TEST_VOID ULV_ASS_IS_VOID(code) #else #define TEST_VOID ULV_ASS_IS_VOID(pc[-1]) #endif #define CHECK_VOID \ if (TEST_VOID) \ sp--; \ goto next_instruction; #define INT_ASSIGN_OP(CODE, OP, ADJI, ADJC, RESMASK) \ case ULV_##CODE: case ULV_VOID_##CODE: \ { \ for (;;) { \ svalue sv1; \ sv1 = *svp; \ if (SV_IS_NUMBER(sv1)) { \ svalue sv2; \ sv2 = *sp; \ if (!SV_IS_NUMBER(sv2)) \ goto ulv_check_right; \ ADJI(sv1.i, sv2.i); \ *sp = svp->i = (sv1.i OP sv2.i) & RESMASK; \ CHECK_VOID; \ } \ switch(SV_TYPE(sv1)) { \ /* case T_RAW_LVALUE: */ \ default: \ goto ulv_check_left; \ case T_LVALUE: \ svp = SV_LVALUE(sv1).lvalue; \ continue; \ case T_CBR_CHAR_LVALUE: \ { \ svalue sv2 = *SV_LVALUE(sv1).lvalue; \ if (sv2.p != SV_LVALUE(sv1).parent.p || !SV_IS_2REF_STRING(sv2)) \ goto ulv_discard_char; \ sv1 = *svp; \ } \ case T_CHAR_LVALUE: \ { \ svalue sv2; \ sv2 = *sp; \ if (!SV_IS_NUMBER(sv2)) \ goto ulv_check_right; \ sv2.i >>= 1; \ ADJC(*SV_LVALUE(sv1).index2.p, sv2.i); \ sv2.i = *SV_LVALUE(sv1).index2.p OP##= sv2.i; \ sp->i = sv2.i << 1; \ CHECK_VOID; \ } \ } \ } break; } #define NIL_ADJ(v1,v2) #define MUL_ADJ(v1,v2) ((v2) >>= 1) #define RSHIFTIADJ(v1,v2) MUL_ADJ(v1,v2); \ if (v2 > sizeof(p_int)*8-1) v2 = sizeof(p_int)*8-1; #define RSHIFTCADJ(v1,v2) if (v2 > 7) v2 = 7 #define LSHIFTIADJ(v1,v2) MUL_ADJ(v1,v2);if (v2 > sizeof(p_int)*8-1) v1=0, v2=0 #define LSHIFTCADJ(v1,v2) if (v2 > 7) v1 = 0, v2 = 0 INT_ASSIGN_OP(AND, &, NIL_ADJ, NIL_ADJ, ~0) INT_ASSIGN_OP(OR, |, NIL_ADJ, NIL_ADJ, ~0) INT_ASSIGN_OP(XOR, ^, NIL_ADJ, NIL_ADJ, ~0) INT_ASSIGN_OP(MUL, *, MUL_ADJ, NIL_ADJ, ~0) INT_ASSIGN_OP(DIV, /, MUL_ADJ, NIL_ADJ, ~1) INT_ASSIGN_OP(MOD, %, NIL_ADJ, NIL_ADJ, ~0) INT_ASSIGN_OP(RSH, >>, RSHIFTIADJ, RSHIFTCADJ, ~1) INT_ASSIGN_OP(LSH, <<, LSHIFTIADJ, LSHIFTCADJ, ~0) case ULV_SUB: case ULV_VOID_SUB: { for (;;) { svalue sv1 = *svp; if (SV_IS_NUMBER(sv1)) { svalue sv2; sv2 = *sp; if (!SV_IS_NUMBER(sv2)) goto ulv_check_right; *sp = svp->i = sv1.i - sv2.i; CHECK_VOID; } switch(SV_TYPE(sv1)) { /* case T_RAW_LVALUE: */ default: goto ulv_bad_left; case T_LVALUE: svp = SV_LVALUE(sv1).lvalue; continue; case T_CBR_CHAR_LVALUE: { svalue sv2 = *SV_LVALUE(sv1).lvalue; if (sv2.p != SV_LVALUE(sv1).parent.p || !SV_IS_2REF_STRING(sv2)) goto ulv_discard_char; } case T_CHAR_LVALUE: { svalue sv2; sv2 = *sp; if (!SV_IS_NUMBER(sv2)) goto ulv_bad_right; sv2.i >>= 1; sv2.i = *SV_LVALUE(sv1).index2.p -= sv2.i; sp->i = sv2.i << 1; CHECK_VOID; } } } break; } case ULV_ADD: case ULV_VOID_ADD: { svalue sv1; for (;;) { sv1 = *svp; if (SV_IS_NUMBER(sv1)) { svalue sv2; sv2 = *sp; if (SV_IS_NUMBER(sv2)) { *sp = svp->i = sv1.i + sv2.i; CHECK_VOID; } *svp = add_number(sv1, sv2); if (TEST_VOID) sp--; else *sp = REF_INC(*svp); goto next_instruction; } switch(SV_TYPE(sv1)) { case T_STRING: case T_GSTRING: case T_ISTRING: case T_LSTRING: case T_GLSTRING: case T_ILSTRING: *svp = add_string(sv1, *sp); if (TEST_VOID) { sp--; } else { *sp = REF_INC(*svp); } goto next_instruction; /* case T_RAW_LVALUE: */ default: goto ulv_bad_left; case T_LVALUE: svp = SV_LVALUE(sv1).lvalue; continue; #ifdef CBR case T_CBR_CHAR_LVALUE: { svalue sv2 = *SV_LVALUE(sv1).lvalue; if (sv2.p != SV_LVALUE(sv1).parent.p || !SV_IS_2REF_STRING(sv2)) goto ulv_discard_char; } #endif case T_CHAR_LVALUE: { svalue sv2; sv2 = *sp; if (!SV_IS_NUMBER(sv2)) goto ulv_bad_right; sv2.i >>= 1; sv2.i = *SV_LVALUE(sv1).index2.p += sv2.i; sp->i = sv2.i << 1; CHECK_VOID; } } } break; } case ULV_INC: case ULV_DEC: { svalue sv1; for (;;) { sv1 = *svp; if (SV_IS_NUMBER(sv1)) { svp->i = sv1.i + code - (ULV_DEC + 2); break; } switch(SV_TYPE(sv1)) { /* case T_RAW_LVALUE: */ default: goto ulv_bad_left; case T_LVALUE: svp = SV_LVALUE(sv1).lvalue; continue; #ifndef CBR case T_CBR_CHAR_LVALUE: { svalue sv2 = *SV_LVALUE(sv1).lvalue; if (sv2.p != SV_LVALUE(sv1).parent.p || !SV_IS_2REF_STRING(sv2)) goto ulv_discard_char; #if 0 goto next_instruction; #endif } #endif case T_CHAR_LVALUE: { #ifndef RISC code = pc[-1]; #endif *SV_LVALUE(sv1).index2.p += ( code - (ULV_DEC + 2) ) >> 1; goto next_instruction; } } } break; } case ULV_ERROR: /* Out of memory from get_map_lvalue */ goto raise_error; case ULV_PLV_INDEX: fatal("unimplemented\n"); return 0; ulv_discard_char: { svalue sv = *sp; if (!SV_IS_NUMBER(sv)) goto ulv_bad_right; CHECK_VOID; } ulv_check_left: { svalue sv = *svp; if (SV_TYPE(sv) == T_DESTRUCTED) { FREE_ALLOCED_SVALUE(sv); svp->i = 0; goto use_lvalue; } } ulv_bad_left: if (!inter_errno) { inter_errno = IE_BAD_EFUN_ARG; error_arg[0].i = 1 << 1; error_arg[1].i = (pc[-1] + ULV_CLOSURE_OFFSET) << 1; } goto raise_error; ulv_check_right: { svalue sv = *sp; if (SV_TYPE(sv) == T_DESTRUCTED) { FREE_ALLOCED_SVALUE(sv); sp->i = 0; goto use_lvalue; } } ulv_bad_right: if (!inter_errno) { inter_errno = IE_BAD_EFUN_ARG; error_arg[1].i = (pc[-1] + ULV_CLOSURE_OFFSET) << 1; } goto raise_error; ulv_lv_bad_right: sp++; goto ulv_bad_right; } break; } #define INT_OP(CODE, OP, ADJ, RESMASK) \ case F_##CODE: \ { \ svalue sv, sv2; \ \ sv2 = *sp; \ sv = *--sp; \ if ((sv.i | sv2.i) & 1) \ goto check_arg1_2; \ ADJ(sv.i, sv2.i); \ sp->i = (sv.i OP sv2.i) & RESMASK; \ break; \ } INT_OP(AND, &, NIL_ADJ, ~0) INT_OP(OR, |, NIL_ADJ, ~0) INT_OP(XOR, ^, NIL_ADJ, ~0) INT_OP(MULTIPLY, *, MUL_ADJ, ~0) INT_OP(DIVIDE, /, MUL_ADJ, ~1) INT_OP(MOD, %, NIL_ADJ, ~0) INT_OP(RSH, >>, RSHIFTIADJ, ~1) INT_OP(LSH, <<, LSHIFTIADJ, ~0) case F_SUB: { svalue sv, sv2; sv2 = *sp; sv = *--sp; if ((sv.i | sv2.i) & 1) goto check_arg1_2; sp->i = sv.i - sv2.i; break; } case F_ADD: { svalue sv, sv2; sv2 = *sp; sv = *--sp; if (SV_IS_NUMBER(sv)) { if (SV_IS_NUMBER(sv2)) { sp->i = sv.i + sv2.i; break; } *sp = add_number(sv, sv2); break; } switch (SV_TYPE(sv)) { case T_STRING: case T_GSTRING: case T_ISTRING: case T_LSTRING: case T_GLSTRING: case T_ILSTRING: *sp = add_string(sv, sv2); break; default: goto check_arg1_2; } break; } check_arg1_2: { svalue sv; sv = *sp++; if (!SV_IS_NUMBER(sv)) { if (SV_TYPE(sv) == T_DESTRUCTED) { FREE_ALLOCED_SVALUE(sv); sp[-1].i = 0; instruction = pc[-1]; goto dispatch_anew; } goto bad_arg_1; } sv = *sp; if (SV_TYPE(sv) == T_DESTRUCTED) { FREE_ALLOCED_SVALUE(sv); sp->i = 0; instruction = pc[-1]; goto dispatch_anew; } goto bad_arg_2; } { svalue sv; case F_BBRANCH_ON_ZERO: sv = *sp--; if (!sv.i) goto bbranch; maybe_freeing_branch_not_taken: if (SV_IS_NUMBER(sv)) { branch_not_taken: pc++; break; } if (SV_TYPE(sv) == T_DESTRUCTED) goto freeing_bbranch; freeing_branch_not_taken: pc++; FREE_ALLOCED_SVALUE(sv); break; case F_BBRANCH_ON_NON_ZERO: sv = *sp--; if (!sv.i) goto branch_not_taken; if (SV_IS_NUMBER(sv)) goto bbranch; if (SV_TYPE(sv) == T_DESTRUCTED) goto freeing_branch_not_taken; freeing_bbranch: FREE_ALLOCED_SVALUE(sv); bbranch: pc -= *pc; CHECK_TIME; instruction = pc[-1] & eval_switch; goto dispatch_anew; case F_BRANCH_ON_ZERO: sv = *sp--; if (!sv.i) goto branch_taken; if (SV_IS_NUMBER(sv)) goto branch_not_taken; if (SV_TYPE(sv) != T_DESTRUCTED) goto freeing_branch_not_taken; freeing_branch_taken: FREE_ALLOCED_SVALUE(sv); case F_BRANCH: branch_taken: pc += *pc; break; case F_BRANCH_ON_NON_ZERO: sv = *sp--; if (!sv.i) goto branch_not_taken; if (SV_IS_NUMBER(sv)) goto branch_taken; if (SV_TYPE(sv) != T_DESTRUCTED) goto freeing_branch_taken; goto freeing_branch_not_taken; case F_LAND: sv = *sp; if (!sv.i) goto branch_taken; sp--; goto maybe_freeing_branch_not_taken; case F_LOR: sv = *sp; if (sv.p) goto branch_taken; sp--; goto branch_not_taken; } { svalue sv; case F_LBRANCH_ON_NON_ZERO: sv = *sp--; if (!sv.i) goto lbranch_not_taken; if (SV_IS_NUMBER(sv)) goto lbranch_taken; if (SV_TYPE(sv) != T_DESTRUCTED) goto freeing_lbranch_taken; freeing_lbranch_not_taken: pc += 2; FREE_ALLOCED_SVALUE(sv); break; lbranch_not_taken: pc += 2; break; case F_LBRANCH_ON_ZERO: sv = *sp--; if (!sv.i) goto lbranch_taken; if (SV_IS_NUMBER(sv)) goto lbranch_not_taken; if (SV_TYPE(sv) != T_DESTRUCTED) goto freeing_lbranch_not_taken; freeing_lbranch_taken: FREE_ALLOCED_SVALUE(sv); case F_LBRANCH: lbranch_taken: pc += EXTRACT16(pc); break; } { svalue sv; case F_XLBRANCH_ON_NON_ZERO: sv = *sp--; if (!sv.i) goto xlbranch_not_taken; if (SV_IS_NUMBER(sv)) goto xlbranch_taken; if (SV_TYPE(sv) != T_DESTRUCTED) goto freeing_xlbranch_taken; freeing_xlbranch_not_taken: pc += 3; FREE_ALLOCED_SVALUE(sv); break; xlbranch_not_taken: pc += 3; break; case F_XLBRANCH_ON_ZERO: sv = *sp--; if (!sv.i) goto xlbranch_taken; if (SV_IS_NUMBER(sv)) goto xlbranch_not_taken; if (SV_TYPE(sv) != T_DESTRUCTED) goto freeing_xlbranch_not_taken; freeing_xlbranch_taken: FREE_ALLOCED_SVALUE(sv); case F_XLBRANCH: xlbranch_taken: pc += EXTRACT24(pc); break; } case F_CATCH: sp += 2; sp[-1].p = pc; *sp = TO_SVALUE(&catch_value); pc++; break; case F_END_CATCH: /* discard catch value and pc, and insert 0 */ (*--sp).i = 0; break; case F_POP: { svalue sv = *sp--; FREE_SVALUE(sv); break; } case F_NEGATE: { svalue sv = *sp; if (SV_IS_NUMBER(sv)) sp->i = - sv.i; else if (SV_TYPE(sv) == T_FLOAT) SV_FLOAT(*sp) = - SV_FLOAT(sv); else goto bad_arg_1; break; } case F_NOT: { svalue sv = *sp; if (sv.i) { if (!SV_IS_NUMBER(sv)) { sp->i = (SV_TYPE(sv) == T_DESTRUCTED) << 1; FREE_ALLOCED_SVALUE(sv); break; } sp->i = 0; } else { sp->i = 2; } break; } case F_EQ: { svalue sv2 = *sp--; svalue sv1 = *sp; if (SV_IS_NUMBER(sv1)) { if (SV_IS_NUMBER(sv2)) { sp->i = (sv1.i == sv2.i) << 1; break; } sp->i = (sv1.i == 0 && SV_TYPE(sv2) == T_DESTRUCTED) << 1; FREE_ALLOCED_SVALUE(sv2); break; } if (SV_IS_NUMBER(sv2)) { sp->i = (sv2.i == 0 && SV_TYPE(sv1) == T_DESTRUCTED) << 1; FREE_ALLOCED_SVALUE(sv1); break; } switch (SV_TYPE(sv1)) { case T_DESTRUCTED: sp->i = (SV_TYPE(sv2) == T_DESTRUCTED) << 1; break; case T_STRING: case T_LSTRING: case T_ISTRING: case T_ILSTRING: sv1 = make_string_global(sv1); case T_GSTRING: case T_GLSTRING: sv2 = make_string_global(sv2); case T_OBJECT: case T_ARRAY: case T_MAPPING: sp->i = (sv1.p == sv2.p) << 1; break; } FREE_ALLOCED_SVALUE(sv1); FREE_ALLOCED_SVALUE(sv2); break; } case F_NE: { svalue sv2 = *sp--; svalue sv1 = *sp; if (SV_IS_NUMBER(sv1)) { if (SV_IS_NUMBER(sv2)) { sp->i = (sv1.i != sv2.i) << 1; break; } sp->i = (sv1.i != 0 || SV_TYPE(sv2) != T_DESTRUCTED) << 1; FREE_ALLOCED_SVALUE(sv2); break; } if (SV_IS_NUMBER(sv2)) { sp->i = (sv2.i != 0 || SV_TYPE(sv1) != T_DESTRUCTED) << 1; FREE_ALLOCED_SVALUE(sv1); break; } switch (SV_TYPE(sv1)) { case T_DESTRUCTED: sp->i = (SV_TYPE(sv2) != T_DESTRUCTED) << 1; break; case T_STRING: case T_LSTRING: case T_ISTRING: case T_ILSTRING: sv1 = make_string_global(sv1); case T_GSTRING: case T_GLSTRING: sv2 = make_string_global(sv2); case T_OBJECT: case T_ARRAY: case T_MAPPING: sp->i = (sv1.p != sv2.p) << 1; break; } FREE_ALLOCED_SVALUE(sv1); FREE_ALLOCED_SVALUE(sv2); break; } case F_GE: { svalue sv2 = *sp--; svalue sv1 = *sp; if (SV_IS_NUMBER(sv1)) { if (SV_IS_NUMBER(sv2)) { sp->i = (sv1.i >= sv2.i) << 1; break; } if (SV_TYPE (sv2) == T_FLOAT) { sp->i = (sv1.i >> 1 >= SV_FLOAT(sv2)) << 1; FREE_ALLOCED_SVALUE(sv2); } sp++; goto bad_arg_2; } if (SV_IS_STRING (sv1)) { if (SV_IS_NUMBER(sv2) || !SV_IS_STRING(sv2)) { sp++; goto bad_arg_2; } sp->i = (sv_strcmp(sv1, sv2) >= 0) << 1; FREE_ALLOCED_SVALUE(sv1); FREE_ALLOCED_SVALUE(sv2); break; } if (SV_TYPE (sv1) == T_FLOAT) { if (SV_IS_NUMBER(sv2)) { sp->i = (SV_FLOAT(sv1) >= sv2.i >> 1) << 1; FREE_ALLOCED_SVALUE(sv1); break; } if (SV_TYPE (sv2) != T_FLOAT) { sp++; goto bad_arg_2; } sp->i = (SV_FLOAT(sv1) >= SV_FLOAT(sv2)) << 1; FREE_ALLOCED_SVALUE(sv1); FREE_ALLOCED_SVALUE(sv2); break; } goto bad_arg_1; } case F_LE: { svalue sv2 = *sp--; svalue sv1 = *sp; if (SV_IS_NUMBER(sv1)) { if (SV_IS_NUMBER(sv2)) { sp->i = (sv1.i <= sv2.i) << 1; break; } if (SV_TYPE (sv2) == T_FLOAT) { sp->i = (sv1.i >> 1 <= SV_FLOAT(sv2)) << 1; FREE_ALLOCED_SVALUE(sv2); } sp++; goto bad_arg_2; } if (SV_IS_STRING (sv1)) { if (SV_IS_NUMBER(sv2) || !SV_IS_STRING(sv2)) { sp++; goto bad_arg_2; } sp->i = (sv_strcmp(sv1, sv2) <= 0) << 1; FREE_ALLOCED_SVALUE(sv1); FREE_ALLOCED_SVALUE(sv2); break; } if (SV_TYPE (sv1) == T_FLOAT) { if (SV_IS_NUMBER(sv2)) { sp->i = (SV_FLOAT(sv1) <= sv2.i >> 1) << 1; FREE_ALLOCED_SVALUE(sv1); break; } if (SV_TYPE (sv2) != T_FLOAT) { sp++; goto bad_arg_2; } sp->i = (SV_FLOAT(sv1) <= SV_FLOAT(sv2)) << 1; FREE_ALLOCED_SVALUE(sv1); FREE_ALLOCED_SVALUE(sv2); break; } goto bad_arg_1; } case F_GT: { svalue sv2 = *sp--; svalue sv1 = *sp; if (SV_IS_NUMBER(sv1)) { if (SV_IS_NUMBER(sv2)) { sp->i = (sv1.i > sv2.i) << 1; break; } if (SV_TYPE (sv2) == T_FLOAT) { sp->i = (sv1.i >> 1 > SV_FLOAT(sv2)) << 1; FREE_ALLOCED_SVALUE(sv2); } sp++; goto bad_arg_2; } if (SV_IS_STRING (sv1)) { if (SV_IS_NUMBER(sv2) || !SV_IS_STRING(sv2)) { sp++; goto bad_arg_2; } sp->i = (sv_strcmp(sv1, sv2) > 0) << 1; FREE_ALLOCED_SVALUE(sv1); FREE_ALLOCED_SVALUE(sv2); break; } if (SV_TYPE (sv1) == T_FLOAT) { if (SV_IS_NUMBER(sv2)) { sp->i = (SV_FLOAT(sv1) > sv2.i >> 1) << 1; FREE_ALLOCED_SVALUE(sv1); break; } if (SV_TYPE (sv2) != T_FLOAT) { sp++; goto bad_arg_2; } sp->i = (SV_FLOAT(sv1) > SV_FLOAT(sv2)) << 1; FREE_ALLOCED_SVALUE(sv1); FREE_ALLOCED_SVALUE(sv2); break; } goto bad_arg_1; } case F_LT: { svalue sv2 = *sp--; svalue sv1 = *sp; if (SV_IS_NUMBER(sv1)) { if (SV_IS_NUMBER(sv2)) { sp->i = (sv1.i < sv2.i) << 1; break; } if (SV_TYPE (sv2) == T_FLOAT) { sp->i = (sv1.i >> 1 < SV_FLOAT(sv2)) << 1; FREE_ALLOCED_SVALUE(sv2); } sp++; goto bad_arg_2; } if (SV_IS_STRING (sv1)) { if (SV_IS_NUMBER(sv2) || !SV_IS_STRING(sv2)) { sp++; goto bad_arg_2; } sp->i = (sv_strcmp(sv1, sv2) < 0) << 1; FREE_ALLOCED_SVALUE(sv1); FREE_ALLOCED_SVALUE(sv2); break; } if (SV_TYPE (sv1) == T_FLOAT) { if (SV_IS_NUMBER(sv2)) { sp->i = (SV_FLOAT(sv1) < sv2.i >> 1) << 1; FREE_ALLOCED_SVALUE(sv1); break; } if (SV_TYPE (sv2) != T_FLOAT) { sp++; goto bad_arg_2; } sp->i = (SV_FLOAT(sv1) < SV_FLOAT(sv2)) << 1; FREE_ALLOCED_SVALUE(sv1); FREE_ALLOCED_SVALUE(sv2); break; } goto bad_arg_1; } case F_SIZEOF: { svalue sv = *sp; p_int size; if (SV_IS_NUMBER (sv)) { if (!sv.i) break; goto bad_arg_1; } switch (SV_TYPE(sv)) { default: goto bad_arg_1; case T_STRING: case T_GSTRING: case T_ISTRING: size = SV_STRLEN(sv); break; case T_LSTRING: case T_GLSTRING: case T_ILSTRING: size = SV_LSTRLEN(sv); break; case T_ARRAY: size = SV_ARRAY_LEN(sv); break; case T_LARRAY: size = SV_LARRAY_LEN(sv); break; case T_MAPPING: { struct hmap_x *hm = SV_MAPPING(sv).x.hash; size = CMAP_SIZE(SV_MAPPING(sv).condensed) + (MAPX_TYPE(hm) == IT_X_HMAP ? hm->used - hm->condensed_deleted : 0); break; } } FREE_ALLOCED_SVALUE(sv); sp->i = size << 1; break; } case F_CLONE_OBJECT: sp = clone_object(sp, fp); break; case F_TEXT_MESSAGE: sp = f_text_message(sp, fp); break; case F_RANGE: { svalue sv = *sp--; sp = f_range(sp, fp, sv, 0); break; } case F_RANGE2: *++sp = INT_SVALUE(1); case F_NR_RANGE: { svalue sv = *sp--; sp = f_range(sp, fp, sv, 1); break; } case F_MEMBER: sp = f_member(sp); break; case F_CLOSURE: { int ix = UEXTRACT16(pc); svalue sv; pc += 2; if (ix >= CLOSURE_EFUN_OFFS) { if (ix < CLOSURE_SIMUL_EFUN_OFFS && instrs[ix - CLOSURE_EFUN_OFFS].Default == -1) ix += CLOSURE_OPERATOR - CLOSURE_EFUN; sv = ALLOC_TTS(T_CLOSURE, 1, ix, sizeof(struct efun_closure)); if (sv.p) SV_CLOSURE(sv).efun.ob = fp->object; } else if (ix >= CLOSURE_IDENTIFIER_OFFS) { sv = ALLOC_TTS(T_CLOSURE, 1, CLOSURE_IDENTIFIER, sizeof(struct lfun_closure)); ix += -CLOSURE_IDENTIFIER_OFFS + (fp_variable - SV_OBJECT(fp->object).variable); if (sv.p) { SV_CLOSURE(sv).lfun.index = ix; SV_CLOSURE(sv).lfun.ob = fp->object; } } else { sv = ALLOC_TTS(T_CLOSURE, 1, CLOSURE_LFUN, sizeof(struct lfun_closure)); ix += fp_virtual.function_8 - SV_OBJECT(fp->object).program->virtual.function_8; if (sv.p) { SV_CLOSURE(sv).lfun.index = ix; SV_CLOSURE(sv).lfun.ob = fp->object; } } *++sp = sv; break; } case F_SPRINTF: sp = f_sprintf(sp, *pc++); break; case F_ESCAPE: #define XCASE(n) case n - 0x100 switch(*pc++) { default: fatal("Illegal Instruction\n"); return SV_NULL; XCASE(F_SET_INTERACTIVE_HOOK): sp = f_set_interactive_hook(sp, fp); break; XCASE(F_SHADOW): sp = f_shadow(sp, fp); break; XCASE(F_PREVIOUS_OBJECT): { svalue sv = *sp; struct frame *pfp = fp; if (!SV_IS_NUMBER(sv)) goto bad_xarg_1; do pfp = pfp->previous; while (pfp && (sv.i -= 2) >= 0); if (pfp) *sp = REF_INC(pfp->object); else sp->i = 0; break; } XCASE(F_RN_RANGE): { svalue sv = *sp--; sp = f_range(sp, fp, sv, -2); break; } XCASE(F_R_RANGE2): *++sp = INT_SVALUE(1); XCASE(F_RR_RANGE): { svalue sv = *sp--; sp = f_range(sp, fp, sv, -1); break; } XCASE(F_GET_DIR): sp = f_get_dir(sp, fp); break; XCASE(F_WRITE_FILE): sp = f_write_file(sp, fp); break; XCASE(F_TYPEOF): { svalue sv = (*sp); int i = SV_TYPE(sv); FREE_SVALUE(sv); sp->i = i << 1; break; } XCASE(F_UNDEF): { if (!inter_errno) { inter_errno = IE_UNDEF; } goto raise_error; } } break; case F_TEFUN: sp = (*efun_table[*pc++ - 128])(sp); break; }} /* end main switch and for(;;) */ bad_xarg_1: pc--; bad_arg_1: if (inter_errno) goto raise_error; error_arg[0].i = 1 << 1; goto bad_arg_x; bad_arg_2: if (inter_errno) goto raise_error; error_arg[0].i = 2 << 1; bad_arg_x: inter_errno = IE_BAD_EFUN_ARG; goto raise_error; raise_error: for(;fp->return_mode.i < IR_CATCH;) { fp = fp->previous; } fatal("Error handling unimplemented\n"); return SV_NULLP; } #define CACHE_SIZE (1 << CALL_CACHE_BITS) #define LEAF_INHERIT_CACHE_SIZE (1 << LEAF_INHERIT_CACHE_BITS) struct call_cache_cell call_cache[CACHE_SIZE]; struct leaf_inherit_cache_cell leaf_inherit_cache[LEAF_INHERIT_CACHE_SIZE]; struct cache_call_ret cache_call_1(struct program *prog, svalue fun) { register p_int id; register struct call_cache_cell *cell; register struct cache_call_ret ret; if (prog->flag.leaf_inherit) { int fx; static struct call_cache_entry entry; ret = cache_call_1((struct program *)(prog->inherit[1].program & ~3), fun); if ((int8)ret.u.unstatic <= 0) /* Found in superclass. Because this is a leaf inherit, offsets for virtual and variable are both 0, no matter if inherited or not. Thus, we can return the unaltered result. */ return ret; if (prog->function.search.offset <= 8) { /* No more than 15 functions to search (max four iterations). Don't bother with the cache. */ fx = leaf_inherit_find_function(prog, fun); } else { register p_int id; register struct leaf_inherit_cache_cell *cell; id = prog->id_number; cell = &leaf_inherit_cache[ ( id ^ fun.i ^ (fun.i >> LEAF_INHERIT_CACHE_BITS) ) & (LEAF_INHERIT_CACHE_SIZE-1) ]; if (cell->tag[0].cache_id == id && cell->tag[0].name.p == fun.p) fx = cell->index[0]; else if (cell->tag[1].cache_id == id && cell->tag[1].name.p == fun.p) fx = cell->index[1]; else if (cell->tag[2].cache_id == id && cell->tag[2].name.p == fun.p) fx = cell->index[2]; else { int i; i = cell->last_written - 1; if (i < 0) i = 2; cell->last_written = i; cell->tag[i].cache_id = id; FREE_ALLOCED_SVALUE(cell->tag[i].name); REF_INC_IN_VAR(fun); cell->tag[i].name = fun; fx = leaf_inherit_find_function(prog, fun); cell->index[i] = fx; } } if (fx < 0) { ret.u.unstatic = 1; } else { entry.program = prog; entry.funstart = PR_PCODE(prog) + prog->new_function[fx].start; ret.u.unstatic = (prog->new_function[fx].flags & TYPE__STATIC) / (0x80 / TYPE__STATIC); ret.entry = &entry; } return ret; } id = prog->id_number; cell = &call_cache[ ( id ^ fun.i ^ ( fun.i >> CALL_CACHE_BITS ) ) & (CACHE_SIZE-1) ]; if (cell->entry[0].cache_id == id && cell->entry[0].name.p == fun.p) { cell->last_written = 0; ret.u.unstatic = cell->unstatic[0]; ret.entry = &cell->entry[0]; return ret; } else if (cell->entry[1].cache_id == id && cell->entry[1].name.p == fun.p) { cell->last_written = 1; ret.u.unstatic = cell->unstatic[1]; ret.entry = &cell->entry[1]; return ret; } else if (cell->entry[2].cache_id == id && cell->entry[2].name.p == fun.p) { cell->last_written = 2; ret.u.unstatic = cell->unstatic[2]; ret.entry = &cell->entry[2]; return ret; } else { int i, fx; i = cell->last_written - 1; if (i < 0) i = 2; cell->last_written = i; ret.entry = &cell->entry[i]; ret.entry->cache_id = id; FREE_ALLOCED_SVALUE(ret.entry->name); ret.entry->name = fun; REF_INC_IN_VAR(fun); fx = find_function(prog, fun); if (fx >= 0) { int function_offset, variable_offset, iix; uint8 *funstart; ret.u.unstatic = 1; iix = prog->flag.many_inherits ? prog->virtual.function_16[fx] : prog->virtual.function_8[fx]; function_offset = fx; variable_offset = 0; while(iix) { struct inherit *inheritp; p_int progi; inheritp = &prog->inherit[iix]; progi = inheritp->program; ret.u.unstatic &= progi; prog = (struct program *)(progi & ~3); fx -= inheritp->virtual_offset; variable_offset += inheritp->variable_offset; iix = prog->virtual.function_8[fx]; } ret.u.unstatic <<= 7; ret.u.unstatic &= prog->new_function[fx].flags; funstart = PR_PCODE(prog) + prog->new_function[fx].start; cell->unstatic[i] = ret.u.unstatic; ret.entry->program = prog; ret.entry->funstart = funstart; ret.entry->cache_virtual_index_offset = function_offset - fx; ret.entry->cache_variable_index_offset = variable_offset; return ret; } cell->unstatic[i] = ret.u.unstatic = 1; return ret; } } /* * If the function is static, check fp->object for match. * return a 'not found' cell for static w/ object mismatch */ struct cache_call_ret cache_call(svalue ob, svalue fun, struct frame *fp) { register struct cache_call_ret ret; /* DONT call make_string_global here, since this would mean we would have to free fun right here, lest the copy of the caller could be trans- formed to a dangling pointer if it was an ISTRING with ref count 1. */ fun = findstring(fun); if (OP_X_FLAGS(SV_OBJECTP(ob)) & O_X_SHADOWED) { do { ob = SV_OBJECT(ob).x.x->shadowed_by; } while (SV_OBJECT(ob).x.x->shadowed_by.i); } retry_for_shadowee: if (SV_OBJECT(ob).flags & O_SWAPPED) { if (load_ob_from_swap(ob) < 0) { /* IE_NOMEM */ static uint8 error_pcode[] = { 0, 1, /* eat variable as argument */ F_RAISE_ERROR >> F_ESCAPE_BITS, F_RAISE_ERROR & 0xff }; static struct call_cache_entry error_entry = { NIL_STRING, 0, &nil_program, &error_pcode[2] }; ret.entry = &error_entry; goto do_return; } } ret = cache_call_1(SV_OBJECT(ob).program, fun); if (!(ret.u.unstatic & 0x80) && ret.u.unstatic + ob.p != fp->object.p) { if (O_HAS_X(&SV_OBJECT(ob)) && (ob = SV_OBJECT(ob).x.x->shadowing).i ) { goto retry_for_shadowee; } ret.entry = 0; } do_return: ret.u.ob = ob; return ret; } /* * having make_varargs_frame as a separate function at least keeps * the other parts of make_varargs() free of bogus register allocations */ static INLINE struct control_ret make_varargs_frame( svalue *sp, int num_arg, uint8 *funstart) { struct control_ret ret; int i; svalue a, v, *dest; struct varargs_lv_field *lvp; ret.sp = sp; a = ALLOC(T_ARRAY, 1, sizeof SV_ARRAY(a) - 4 - sizeof SV_ARRAY(a).member + sizeof SV_ARRAY(a).member[0] * num_arg); if (!a.i) { do { svalue sv = *ret.sp--; FREE_SVALUE(sv); } while(--num_arg); v.i = 0; } else { SV_ARRAY(a).len = num_arg; dest = &SV_ARRAY(a).member[num_arg]; v.i = 0; do { svalue sv = *--sp; if (!SV_IS_NUMBER(sv) && SV_TYPE(sv) == T_LVALUE) { if (!v.i) { mp_int size; size = sizeof SV_VARARGS(v) - 4 - sizeof SV_VARARGS(v).lvalues + sizeof SV_VARARGS(v).lvalues[0] * num_arg; v = ALLOC(T_VARARGS, 1, size); /* * In oder to be able to use a scratchpad register for sv, * we have to re-read it after this function call. */ sv = *sp; if (!v.i) goto get_copy; SV_VARARGS(v).alloced_size = size; SV_ARRAY(a).ref = 2; SV_VARARGS(v).array = a; lvp = &SV_VARARGS(v).lvalues[0]; } lvp->index = num_arg; lvp->lvalue = sv; lvp++; get_copy: sv = *SV_LVALUE(sv).lvalue; COPY_SVALUE_IN_VAR(sv); } *--dest = sv; } while (--num_arg); } ret.fp = (struct frame *)ret.sp; ret.fp->funstart = funstart; ret.sp = (svalue *)&ret.fp[1]; *++ret.sp = v; i = funstart[-1] - 2; if (i) do { (++ret.sp)->i = 0; } while (--i); *++ret.sp = a; return ret; } struct control_ret make_frame(svalue *sp, int num_arg, uint8 *funstart) { /* funstart[-2] : num_arg funstart[-1]: num_local */ /* * If there is an out of memory condition, errno and eval_switch will * be set appropriately by alloc(). We have still to make sure that * we don't dereference the NULL pointers and that subsequent operations * leave the heap in a consistent state. */ struct control_ret ret; int i; ret.sp = sp; num_arg -= funstart[-2]; if (num_arg) { if (num_arg > 0) { if (funstart[0] == F_VARARGS) { return make_varargs_frame(ret.sp, num_arg, funstart); } else { do { svalue sv = *ret.sp--; FREE_SVALUE(sv); } while(--num_arg); } } else { do { (++ret.sp)->i = 0; } while (++num_arg); } } ret.fp = (struct frame *)ret.sp; ret.fp->funstart = funstart; ret.sp = CONTROL_LOCALS(ret.fp) - 1; i = funstart[-1]; if (i) do { (++ret.sp)->i = 0; } while (--i); return ret; } void push_svalue(svalue sv) { *++inter_sp = COPY_SVALUE(sv); } svalue call_hook(svalue hook, svalue object, int num_arg) { svalue *sp; if (SV_IS_NUMBER(hook)) { sp = inter_sp; } else { switch(SV_TYPE(hook)) { default: REF_INC_IN_VAR(hook); sp = inter_sp; break; case T_STRING: case T_ISTRING: case T_GSTRING: case T_LSTRING: case T_ILSTRING: case T_GLSTRING: { struct call_cache_entry *centry; struct control_ret cntret; { struct cache_call_ret ccret; ccret = cache_call(object, hook, inter_fp); centry = ccret.entry; object = ccret.u.ob; } hook.i = 0; sp = inter_sp; if (centry) { struct program *prog; cntret = make_frame(sp, num_arg, centry->funstart); cntret.fp->previous = inter_fp; cntret.fp->object = object; cntret.fp->variable = SV_OBJECT(object).variable + centry->cache_variable_index_offset; cntret.fp->program = prog = centry->program; cntret.fp->virtual.function_8 = prog->virtual.function_8; cntret.fp->shared = prog->shared; cntret.fp->return_mode.i = IR_EXTERN; hook = interpreter(cntret.fp, cntret.sp); sp = &cntret.fp->arguments[0]; num_arg = FUNSTART2NARGS(cntret.fp->funstart); } break; } case T_CLOSURE: { struct control_ret cntret = closure_frame(hook, inter_sp, inter_fp, num_arg, 0, IR_EXTERN); hook = interpreter(cntret.fp, cntret.sp); sp = &cntret.fp->arguments[0]; num_arg = FUNSTART2NARGS(cntret.fp->funstart); break; } } } while (--num_arg >= 0) { svalue sv = *sp--; FREE_SVALUE(sv); } inter_sp = sp; return hook; } static void free_lvalue(svalue sv) { switch(SV_LVALUE(sv).type) { default: fatal("Unknown lvalue type\n"); return; case LVALUE_SIMPLE: return; case LVALUE_CBR_CHAR: FREE_SVALUE(SV_LVALUE(sv).index1); /* svalue lvalue points into (0 if none) */ case LVALUE_PROTECTED: case LVALUE_NN_INDEXED: case LVALUE_NR_INDEXED: case LVALUE_RN_INDEXED: case LVALUE_RR_INDEXED: sv = SV_LVALUE(sv).parent; FREE_SVALUE(sv); return; } } void free_varargs(svalue sv) { /* FIXME: assign values from the array, free the latter */ free_block(sv.p, SV_VARARGS(sv).alloced_size); } static svalue add_number(svalue sv0, svalue sv1) { svalue sv2; if (SV_IS_STRING(sv2)) { char buf[P_INT_PRINT_SIZE]; sprintf(buf, "%ld", sv0.i >> 1); sv0 = make_string(buf, strlen(buf)); sv2 = add_string(sv0, sv1); } else if (SV_TYPE(sv1) == T_FLOAT) { sv2 = ALLOC_FLOAT; SV_FLOAT(sv2) = (sv0.i >> 1) + SV_FLOAT(sv1); } else if (SV_TYPE(sv1) == T_DESTRUCTED) { sv2 = sv0; } else { bad_efun_arg(2); return sv1; } FREE_ALLOCED_SVALUE(sv1); return sv2; } void error(int ie_errno, ...) { extern char error_nargs[]; int nargs, i; va_list va; svalue *dest, sv; nargs = error_nargs[ie_errno]; PUSH_NUMBER(ie_errno); dest = inter_sp += nargs; va_start(va, ie_errno); for (i = 0; i < nargs; i++) { dest[i] = va_arg(va, svalue); } va_end(va); sv = call_hook(driver_hook[H_RUNTIME_ERROR], master_ob, nargs+1); FREE_SVALUE(sv); } static void do_trace(struct frame *fp, svalue* sp, uint8* pc) { fatal("tracing unimplemented\n"); } void bad_efun_arg(int n) { if (!inter_errno) { eval_switch = 0; inter_errno = IE_BAD_EFUN_ARG; error_arg[0].i = n << 1; } } void fpe_handler() { if (!inter_errno) { inter_errno = IE_SIGFPE; eval_switch = off; } } p_int _privilege_violation(p_int what, svalue where, svalue *sp) { svalue fp_object = inter_fp->object; svalue sv; if (SV_OBJECT(fp_object).x.uid->self->name.p == driver_hook[H_PRIVILEGED_UID].p) return 1; (++sp)->i = what; REF_INC_IN_VAR(fp_object); *++sp = fp_object; REF_INC_IN_VAR(where); *++sp = where; inter_sp = sp; sv = call_hook(driver_hook[H_PRIVILEGE_VIOLATION], fp_object, 3); if (!SV_IS_NUMBER(sv) || sv.i < 0) { error(IE_PRIVILEGED, "%d%O", what, where); } return sv.i; } void assert_master_ob_loaded() { master_ob = find_object(master_name, MAX_INHERIT_DEPTH); } svalue *f_set_driver_hook(svalue *sp) { svalue hn, hv, sv, *svp; hn = sp[-1]; if (!SV_IS_NUMBER(hn)) { bad_efun_arg(1); return sp; } hv = sp[0]; svp = &driver_hook[hn.i >> 1]; sv = *svp; *svp = hv; FREE_SVALUE(sv); return sp - 2; } void nilframe() { struct frame *fp = (struct frame *)(inter_stack.general-1); inter_fp = fp; inter_sp = (svalue *)&fp[1]; fp->object = TO_SVALUE(&nil_object); inter_ex_fp = inter_stack.external-1; } void initialize_interpreter() { int i; SV_STRREF(NIL_STRING) = ~0; for (i = NELEM(call_cache); --i >= 0; ) { call_cache[i].entry[0].name = NIL_STRING; call_cache[i].entry[1].name = NIL_STRING; call_cache[i].entry[2].name = NIL_STRING; } nilframe(); eval_cost = -MAX_COST; eval_switch = on; }