/* eoperators.c: this file contains all of the operators called from inside eval_instruction() in interpret.c. */ #include <sys/types.h> #include <sys/stat.h> #ifndef LATTICE #include <sys/time.h> #else #include <time.h> #endif #ifndef LATTICE #include <sys/ioctl.h> #include <netdb.h> #endif #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <ctype.h> #include <signal.h> #ifndef LATTICE #include <memory.h> #else #include "amiga.h" #endif #include <setjmp.h> #include "config.h" #include "lint.h" #include "interpret.h" #include "buffer.h" #include "mapping.h" #include "object.h" #include "exec.h" #include "efun_protos.h" #include "comm.h" #include "opcodes.h" #include "switch.h" #include "stralloc.h" #include "debug.h" /* Needed these from interpret.c. But what for? -SH */ #define TRACE_CALL_OTHER 2 #define TRACE_RETURN 4 #define TRACE_ARGS 8 #define TRACE_HEART_BEAT 32 #define TRACETST(b) (command_giver->interactive->trace_level & (b)) #define TRACEP(b) \ (command_giver && command_giver->interactive && TRACETST(b) && \ (command_giver->interactive->trace_prefix == 0 || \ (current_object && strpref(command_giver->interactive->trace_prefix, \ current_object->name))) ) #define TRACEHB (current_heart_beat == 0 \ || (command_giver->interactive->trace_level & TRACE_HEART_BEAT)) extern int d_flag; extern char *pc; extern int tracedepth; extern int current_time; extern char *last_verb; extern struct svalue *fp; /* Pointer to first argument. */ extern int function_index_offset; /* Needed for inheritance */ extern int variable_index_offset; /* Needed for inheritance */ extern struct object *previous_ob; extern struct object *master_ob; extern struct svalue *expected_stack; extern userid_t *backbone_uid; extern struct svalue const0, const1, const0u, const0n; extern struct object *current_heart_beat, *current_interactive; extern struct svalue catch_value; /* Used to throw an error to a catch */ extern short *break_sp; /* Points to address to branch to * at next F_BREAK */ extern struct control_stack *csp; /* Points to last element pushed */ /* Points to value of last push. */ extern struct svalue *sp; extern int eval_cost; static int i; static float r; static struct svalue *argp; INLINE void f_and(num_arg, instruction) int num_arg, instruction; { if (sp->type == T_POINTER && (sp-1)->type == T_POINTER) { extern struct vector *intersect_array PROT((struct vector *, struct vector *)); (sp-1)->u.vec = intersect_array(sp->u.vec, (sp-1)->u.vec); sp--; return; } if ((sp-1)->type != T_NUMBER) bad_arg(1, instruction); if (sp->type != T_NUMBER) bad_arg(2, instruction); i = (sp-1)->u.number & sp->u.number; sp--; sp->u.number = i; } INLINE void f_and_eq(num_arg, instruction) int num_arg, instruction; { if (sp[-1].type != T_LVALUE) bad_arg(1, instruction); argp = sp[-1].u.lvalue; if (argp->type != T_NUMBER) error("Bad left type to &=.\n"); if (sp->type != T_NUMBER) error("Bad right type to &="); i = argp->u.number & sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } INLINE void f_parse_command(num_arg, instruction) int num_arg, instruction; { struct svalue *arg; num_arg = EXTRACT_UCHAR(pc); pc++; arg = sp - num_arg + 1; if (arg[0].type != T_STRING) bad_arg(1, instruction); if (arg[1].type != T_OBJECT && arg[1].type != T_POINTER) bad_arg(2, instruction); if (arg[2].type != T_STRING) bad_arg(3, instruction); if (arg[1].type == T_POINTER) check_for_destr(arg[1].u.vec); i = parse(arg[0].u.string, &arg[1], arg[2].u.string, &arg[3], num_arg-3); pop_n_elems(num_arg); /* Get rid of all arguments */ push_number(i); /* Push the result value */ } INLINE void f_div_eq(num_arg, instruction) int num_arg, instruction; { if((sp - 1)->type != T_LVALUE) bad_arg(1, instruction); argp = (sp - 1)->u.lvalue; if(argp->type == T_NUMBER){ if(sp->type == T_NUMBER){ if(sp->u.number == 0) error("Division by 0nn\n"); i = argp->u.number / sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } else if(sp->type == T_REAL){ if(sp->u.real == 0.) error("Division by 0.nr\n"); r = argp->u.number / sp->u.real; pop_n_elems(2); push_real(r); assign_svalue(argp, sp); } else { error("Bad right type to /="); } } else if(argp->type == T_REAL){ if(sp->type == T_NUMBER){ if(sp->u.number == 0) error("Division by 0rn\n"); r = argp->u.real / sp->u.number; pop_n_elems(2); push_real(r); assign_svalue(argp, sp); } else if(sp->type == T_REAL){ if(sp->u.real == 0.) error("Division by 0.rr\n"); r = argp->u.real / sp->u.real; pop_n_elems(2); push_real(r); assign_svalue(argp, sp); } else { error("Bad right type to /="); } } else { error("Bad left type to /=\n"); } } INLINE void f_eq(num_arg, instruction) int num_arg, instruction; { switch(sp->type){ case T_NUMBER: if((sp-1)->type == T_NUMBER){ i = ((sp-1)->u.number == sp->u.number); } else if((sp-1)->type == T_REAL){ i = ((sp-1)->u.real == sp->u.number); } else { pop_stack(); assign_svalue(sp, &const0); return; } break; case T_REAL: if((sp-1)->type == T_NUMBER){ i = ((sp-1)->u.number == sp->u.real); } else if((sp-1)->type == T_REAL){ i = ((sp-1)->u.real == sp->u.real); } else { pop_stack(); assign_svalue(sp, &const0); return; } break; case T_POINTER: if((sp-1)->type == T_POINTER){ i = ((sp-1)->u.vec == sp->u.vec); } else { pop_stack(); assign_svalue(sp, &const0); return; } break; case T_STRING: if((sp-1)->type == T_STRING){ i = (strcmp((sp-1)->u.string, sp->u.string) == 0); } else { pop_stack(); assign_svalue(sp, &const0); return; } break; case T_OBJECT: if((sp-1)->type == T_OBJECT){ i = ((sp-1)->u.ob == sp->u.ob); } else { pop_stack(); assign_svalue(sp, &const0); return; } break; case T_MAPPING: if((sp-1)->type == T_MAPPING){ i = ((sp-1)->u.map == sp->u.map); } else { pop_stack(); assign_svalue(sp, &const0); return; } break; default: i = 0; break; } pop_n_elems(2); push_number(i); } INLINE void f_lsh(num_arg, instruction) int num_arg, instruction; { if ((sp-1)->type != T_NUMBER) bad_arg(1, instruction); if (sp->type != T_NUMBER) bad_arg(2, instruction); i = (sp-1)->u.number << sp->u.number; sp--; sp->u.number = i; } INLINE void f_lsh_eq(num_arg, instruction) int num_arg, instruction; { if (sp[-1].type != T_LVALUE) bad_arg(1, instruction); argp = sp[-1].u.lvalue; if (argp->type != T_NUMBER) error("Bad left type to <<=.\n"); if (sp->type != T_NUMBER) error("Bad right type to <<="); i = argp->u.number << sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } INLINE void f_mod_eq(num_arg, instruction) int num_arg, instruction; { if (sp[-1].type != T_LVALUE) bad_arg(1, instruction); argp = sp[-1].u.lvalue; if (argp->type != T_NUMBER) error("Bad left type to %=.\n"); if (sp->type != T_NUMBER) error("Bad right type to %="); if (sp->u.number == 0) error("Division by 0\n"); i = argp->u.number % sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } INLINE void f_mult_eq(num_arg, instruction) int num_arg, instruction; { if((sp - 1)->type != T_LVALUE) bad_arg(1, instruction); argp = (sp - 1)->u.lvalue; if(argp->type == T_NUMBER){ if(sp->type == T_NUMBER){ i = argp->u.number * sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } else if(sp->type == T_REAL){ r = argp->u.number * sp->u.real; pop_n_elems(2); push_real(r); assign_svalue(argp, sp); } else { error("Bad right type to *="); } } else if(argp->type == T_REAL){ if(sp->type == T_NUMBER){ r = argp->u.real * sp->u.number; pop_n_elems(2); push_real(r); assign_svalue(argp, sp); } else if(sp->type == T_REAL){ r = argp->u.real * sp->u.real; pop_n_elems(2); push_real(r); assign_svalue(argp, sp); } else { error("Bad right type to *="); } } else if(argp->type == T_MAPPING){ if(sp->type == T_MAPPING){ struct mapping *m; m = compose_mapping(argp->u.map, sp->u.map); pop_n_elems(2); push_mapping(m); assign_svalue(argp, sp); } else { error("Bad right type to *="); } } else { error("Bad left type to *=\n"); } } INLINE void f_ne(num_arg, instruction) int num_arg, instruction; { switch(sp->type){ case T_NUMBER: if((sp-1)->type == T_NUMBER){ i = ((sp-1)->u.number != sp->u.number); } else if((sp-1)->type == T_REAL){ i = ((sp-1)->u.real != sp->u.number); } else { pop_stack(); assign_svalue(sp, &const1); return; } break; case T_REAL: if((sp-1)->type == T_NUMBER){ i = ((sp-1)->u.number != sp->u.real); } else if((sp-1)->type == T_REAL){ i = ((sp-1)->u.real != sp->u.real); } else { pop_stack(); assign_svalue(sp, &const1); return; } break; case T_POINTER: if((sp-1)->type == T_POINTER){ i = ((sp-1)->u.vec != sp->u.vec); } else { pop_stack(); assign_svalue(sp, &const1); return; } break; case T_STRING: if((sp-1)->type == T_STRING){ i = strcmp((sp-1)->u.string, sp->u.string); } else { pop_stack(); assign_svalue(sp, &const1); return; } break; case T_OBJECT: if((sp-1)->type == T_OBJECT){ i = ((sp-1)->u.ob != sp->u.ob); } else { pop_stack(); assign_svalue(sp, &const1); return; } break; case T_MAPPING: if((sp-1)->type == T_MAPPING){ i = ((sp-1)->u.map != sp->u.map); } else { pop_stack(); assign_svalue(sp, &const1); return; } break; default: i = 1; break; } pop_n_elems(2); push_number(i); } INLINE void f_or(num_arg, instruction) int num_arg, instruction; { if ((sp-1)->type != T_NUMBER) bad_arg(1, instruction); if (sp->type != T_NUMBER) bad_arg(2, instruction); i = (sp-1)->u.number | sp->u.number; sp--; sp->u.number = i; } INLINE void f_or_eq(num_arg, instruction) int num_arg, instruction; { if (sp[-1].type != T_LVALUE) bad_arg(1, instruction); argp = sp[-1].u.lvalue; if (sp[-1].type != T_LVALUE) bad_arg(1, instruction); argp = sp[-1].u.lvalue; if (argp->type != T_NUMBER) error("Bad left type to |=.\n"); if (sp->type != T_NUMBER) error("Bad right type to |="); i = argp->u.number | sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } INLINE void f_range(num_arg, instruction) int num_arg, instruction; { if (sp[-1].type != T_NUMBER) error("Bad type of start interval to [ .. ] range.\n"); if (sp[0].type != T_NUMBER) error("Bad type of end interval to [ .. ] range.\n"); if (sp[-2].type == T_POINTER) { struct vector *v; v = slice_array(sp[-2].u.vec, sp[-1].u.number, sp[0].u.number); pop_n_elems(3); if (v) { push_vector(v); v->ref--; /* Will make ref count == 1 */ } else push_number(0); } else if (sp[-2].type == T_STRING) { int len, from, to; char *res; len = strlen(sp[-2].u.string); from = sp[-1].u.number; if (from < 0) from = len + from; if (from >= len) { pop_n_elems(3); push_string("", STRING_CONSTANT); return; } to = sp[0].u.number; if (to < 0) to = len + to; if (to < from) { pop_n_elems(3); push_string("", STRING_CONSTANT); return; } if (to >= len) to = len-1; if (to == len-1) { res = string_copy(sp[-2].u.string + from); pop_n_elems(3); push_malloced_string(res); return; } res = DXALLOC(to - from + 2, 37, "f_range"); strncpy(res, sp[-2].u.string + from, to - from + 1); res[to - from + 1] = '\0'; pop_n_elems(3); push_malloced_string(res); } else if (sp[-2].type == T_BUFFER) { int len, from, to; struct buffer *rbuf; len = sp[-2].u.buf->size; from = sp[-1].u.number; if (from < 0) from = len + from; if (from >= len) { pop_n_elems(3); push_buffer(null_buffer()); return; } to = sp[0].u.number; if (to < 0) to = len + to; if (to < from) { pop_n_elems(3); push_buffer(null_buffer()); return; } if (to >= len) to = len-1; rbuf = allocate_buffer(to - from + 1); rbuf->ref--; memcpy(rbuf->item, sp[-2].u.buf->item + from, to - from + 1); pop_n_elems(3); push_buffer(rbuf); } else error("Bad argument to [ .. ] range operand.\n"); } INLINE void f_rsh(num_arg, instruction) int num_arg, instruction; { if ((sp-1)->type != T_NUMBER) bad_arg(1, instruction); if (sp->type != T_NUMBER) bad_arg(2, instruction); i = (sp-1)->u.number >> sp->u.number; sp--; sp->u.number = i; } INLINE void f_rsh_eq(num_arg, instruction) int num_arg, instruction; { if (sp[-1].type != T_LVALUE) bad_arg(1, instruction); argp = sp[-1].u.lvalue; if (argp->type != T_NUMBER) error("Bad left type to >>=.\n"); if (sp->type != T_NUMBER) error("Bad right type to >>="); i = argp->u.number >> sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } INLINE void f_sub_eq(num_arg, instruction) int num_arg, instruction; { if((sp - 1)->type != T_LVALUE) bad_arg(1, instruction); argp = (sp - 1)->u.lvalue; switch(argp->type){ case T_NUMBER: if(sp->type == T_NUMBER){ argp->u.number -= sp->u.number; sp--; } else if(sp->type == T_REAL){ argp->u.number -= sp->u.real; sp--; } else { error("Bad type number to rhs -="); } break; case T_REAL: if(sp->type == T_NUMBER){ argp->u.real -= sp->u.number; sp--; } else if(sp->type == T_REAL){ argp->u.real -= sp->u.real; sp--; } else { error("Bad type number to rhs -="); } break; case T_POINTER: if(sp->type == T_POINTER){ struct vector *subtract_array PROT((struct vector*,struct vector*)); struct vector *v, *w; v = sp->u.vec; if(v->ref > 1){ v = slice_array(v, 0, v->size-1 ); v->ref--; } w = subtract_array(argp->u.vec, v); free_vector(argp->u.vec); free_vector(v); /* no longer freed in subtract_array() */ eval_cost += (w->size << 3); sp--; argp->u.vec = w; } else { error("Bad type number to rhs -="); } break; default: error("Bad left type to -=\n"); } assign_svalue_no_free(sp, argp); } /* * Structure of F_SWITCH: * table type (1 byte) * address of table (1 short) * address of break (1 short) * address of default (1 short) * then all the switch code * switch table (varies) * * Table type is either * 0xfe - integer labels, direct lookup. * Table is followed by 1 long that is minimum key value. * Each table entry is a short address to jump to. * 0xfN - integer labels. N is size as a power of 2. * Each table entry is 1 long (key) followed by 1 short (address). * 0xNf - string labels. Otherwise same as for integer labels. * * For normal string or integer tables, if the address is 0 or 1, * the key is the lower end of a range, and the upper end is in * the next entry. If it's a 0, the second address indicates a * direct lookup table (currently this case is never generated by * the compiler). If it's a 1, the second address is used for * all keys in the range (corresponds to 'case x..y:' labels). * * Binary search is used on the normal tables. */ /* offsets from 'pc' */ #define SW_TYPE 0 #define SW_TABLE 1 #define SW_BREAK 3 #define SW_DEFAULT 5 #define ENTRY_SIZE 6 /* offsets used for range (L_ for lower member, U_ for upper member) */ #define L_LOWER 0 #define L_TYPE 4 #define L_UPPER 6 #define L_ADDR 10 #define U_LOWER -6 #define U_TYPE -2 #define U_UPPER 0 #define U_ADDR 4 #define COPY_SHORT(dst, src) \ { ((char *)(dst))[0] = ((char *)(src))[0]; \ ((char *)(dst))[1] = ((char *)(src))[1]; } #define COPY_LONG(dst, src) \ { ((char *)(dst))[0] = ((char *)(src))[0]; \ ((char *)(dst))[1] = ((char *)(src))[1]; \ ((char *)(dst))[2] = ((char *)(src))[2]; \ ((char *)(dst))[3] = ((char *)(src))[3]; } INLINE void f_switch(num_arg, instruction) int num_arg, instruction; { extern char* findstring PROT((char*)); unsigned short offset, break_adr; int d, s = 0, r; char *l,*end_tab; static unsigned short off_tab[] = { 0*ENTRY_SIZE, 1*ENTRY_SIZE, 3*ENTRY_SIZE, 7*ENTRY_SIZE, 15*ENTRY_SIZE, 31*ENTRY_SIZE, 63*ENTRY_SIZE, 127*ENTRY_SIZE, 255*ENTRY_SIZE, 511*ENTRY_SIZE, 1023*ENTRY_SIZE, 2047*ENTRY_SIZE, 4095*ENTRY_SIZE, 8191*ENTRY_SIZE, }; COPY_SHORT(&offset, pc+SW_TABLE); COPY_SHORT(&break_adr, pc+SW_BREAK); *--break_sp = break_adr; if ( ( i = EXTRACT_UCHAR(pc) >> 4 ) != 0xf ) { /* String table, find correct key */ if ( sp->type == T_NUMBER && !sp->u.number ) { /* special case: 0 as a string */ s = (int)ZERO_AS_STR_CASE_LABEL; } else if ( sp->type == T_STRING ) { switch(sp->subtype) { case STRING_SHARED: s = (int)sp->u.string; break; default: s = (int)findstring(sp->u.string); break; } if (s == 0) { /* Take default case now - else we could be get confused * with ZERO_AS_STR_CASE_LABEL. */ COPY_SHORT(&offset, pc+SW_DEFAULT); pop_stack(); pc = current_prog->p.i.program + offset; return; } } else { bad_arg(1, instruction); } } else { /* Integer table, check type */ if (sp->type != T_NUMBER) bad_arg(1, instruction); s = sp->u.number; i = (int)pc[0] &0xf ; } pop_stack(); end_tab = current_prog->p.i.program + break_adr; /* * i is the table size as a power of 2. Tells us where to * start searching. i==14 is a special case. */ if ( i >= 14 ) if ( i == 14 ) { /* fastest switch format : lookup table */ l = current_prog->p.i.program + offset; COPY_LONG(&d, end_tab-4); /* d is minimum value - see if in range or not */ if ( s >= d && l + (s=(s-d)*sizeof(short)) < (end_tab - 4) ) { COPY_SHORT(&offset, &l[s]); if (offset) { pc = current_prog->p.i.program + offset; return; } } /* default */ COPY_SHORT(&offset, pc+SW_DEFAULT); pc = current_prog->p.i.program + offset; return; } else fatal("unsupported switch table format.\n"); /* * l - current entry we are looking at. * d - size to add/subtract from l each iteration. * s - key we're looking for * r - key l is pointing at */ l = current_prog->p.i.program + offset + off_tab[i]; d = (int)(off_tab[i]+ENTRY_SIZE) >> 1; if (d < ENTRY_SIZE) d=0; for(;;) { COPY_LONG(&r, l); if (s < r) { if (d < ENTRY_SIZE) { /* test if entry is part of a range */ /* Don't worry about reading from F_BREAK (byte before table) */ COPY_SHORT(&offset, l+U_TYPE); if (offset <= 1) { COPY_LONG(&r, l+U_LOWER); if (s >= r) { /* s is in the range */ COPY_SHORT(&offset, l+U_ADDR); if (!offset) { /* range with lookup table */ l = current_prog->p.i.program + offset + (s-r) * sizeof(short); COPY_SHORT(&offset, l); } /* else normal range and offset is correct */ break; } } /* key not found, use default address */ COPY_SHORT(&offset, pc+SW_DEFAULT); break; } else { /* d >= ENTRY_SIZE */ l -= d; d >>= 1; } } else if (s > r) { if (d < ENTRY_SIZE) { /* test if entry is part of a range */ COPY_SHORT(&offset, l+L_TYPE); if (offset <= 1) { COPY_LONG(&r, l+L_UPPER); if (s <= r) { /* s is in the range */ COPY_SHORT(&offset, l+L_ADDR); if (!offset) { /* range with lookup table */ l = current_prog->p.i.program + offset + (s-r) * sizeof(short); COPY_SHORT(&offset, l); } /* else normal range and offset is correct */ break; } } /* use default address */ COPY_SHORT(&offset, pc+SW_DEFAULT); break; } else { /* d >= ENTRY_SIZE */ l += d; /* if table isn't a power of 2 in size, fix us up */ while (l >= end_tab) { d >>= 1; if (d < ENTRY_SIZE) { d = 0; break; } l -= d; } if (l == end_tab) { /* use default address */ COPY_SHORT(&offset, pc+SW_DEFAULT); break; } d >>= 1; } } else { /* s == r */ COPY_SHORT(&offset, l+U_ADDR); /* found the key - but could be part of a range... */ if ( !l[U_TYPE] && !l[U_TYPE+1] ) { /* end of range with lookup table */ COPY_LONG(&r, l+U_LOWER); l = current_prog->p.i.program + offset + (s-r)*sizeof(short); COPY_SHORT(&offset, l); } if (offset <= 1) { COPY_SHORT(&offset, l+L_ADDR); if (!offset) { /* start of range with lookup table */ l = current_prog->p.i.program + offset; COPY_SHORT(&offset, l); } /* else normal range, offset is correct */ } break; } } /* now do jump */ pc = current_prog->p.i.program + offset; } INLINE void f_xor(num_arg, instruction) int num_arg, instruction; { if ((sp-1)->type != T_NUMBER) bad_arg(1, instruction); if (sp->type != T_NUMBER) bad_arg(2, instruction); i = (sp-1)->u.number ^ sp->u.number; sp--; sp->u.number = i; } INLINE void f_xor_eq(num_arg, instruction) int num_arg, instruction; { if (sp[-1].type != T_LVALUE) bad_arg(1, instruction); argp = sp[-1].u.lvalue; if (argp->type != T_NUMBER) error("Bad left type to ^=.\n"); if (sp->type != T_NUMBER) error("Bad right type to ^="); i = argp->u.number ^ sp->u.number; pop_n_elems(2); push_number(i); assign_svalue(argp, sp); } INLINE struct funp * make_funp(sob, sfun) struct svalue *sob, *sfun; { struct funp *fp; fp = (struct funp *)DMALLOC(sizeof(struct funp), 38, "make_funp"); if (current_object) { if (current_object->euid) { fp->euid = current_object->euid; } else { fp->euid = current_object->uid; } } else { fp->euid = NULL; } assign_svalue_no_free(&fp->obj, sob); assign_svalue_no_free(&fp->fun, sfun); fp->ref = 1; return fp; } INLINE void push_funp(fp) struct funp *fp; { fp->ref++; sp++; sp->type = T_FUNCTION; sp->u.fp = fp; } INLINE void free_funp(fp) struct funp *fp; { fp->ref--; if (fp->ref > 0) { return; } free_svalue(&fp->obj); free_svalue(&fp->fun); FREE(fp); } INLINE void f_function_constructor(num_arg, instruction) int num_arg, instruction; { struct funp *fp; fp = make_funp(sp - 1, sp); pop_n_elems(2); push_funp(fp); fp->ref--; } INLINE void f_this_function_constructor(num_arg, instruction) int num_arg, instruction; { struct funp *fp; if (current_object->flags & O_DESTRUCTED) push_number(0); else push_object(current_object); /* Note: The stack order is reversed here compared to f_function_constructor */ fp = make_funp(sp , sp - 1); pop_n_elems(2); push_funp(fp); fp->ref--; } INLINE void f_function_split(num_arg, instruction) int num_arg, instruction; { struct svalue *obj, *fun; struct funp *tmp; if (sp->type != T_FUNCTION) { bad_arg(1, instruction); } obj = &sp->u.fp->obj; fun = &sp->u.fp->fun; tmp = sp->u.fp; sp--; /* don't free the funp here since that would also free obj and fun */ if ((obj->type == T_OBJECT) && (obj->u.ob->flags & O_DESTRUCTED)) { assign_svalue(obj, &const0n); push_null(); } else { push_svalue(obj); } push_svalue(fun); free_funp(tmp); /* go ahead and free it here since the pushes make it ok */ } INLINE void f_aggregate_assoc(num_arg, instruction) int num_arg, instruction; { struct mapping *m; unsigned short num; ((char *)&num)[0] = pc[0]; ((char *)&num)[1] = pc[1]; pc += 2; m = load_mapping_from_aggregate(sp - num + 1, num); pop_n_elems((int)num); push_mapping(m); m->ref--; } INLINE void f_aggregate(num_arg, instruction) int num_arg, instruction; { struct vector *v; unsigned short num; ((char *)&num)[0] = pc[0]; ((char *)&num)[1] = pc[1]; pc += 2; v = allocate_array((int)num); for (i=0; i < (int)num; i++) assign_svalue_no_free(&v->item[i], sp + i - num + 1); pop_n_elems((int)num); push_vector(v); v->ref--; } INLINE void f_sscanf(num_arg, instruction) int num_arg, instruction; { num_arg = EXTRACT_UCHAR(pc); pc++; i = inter_sscanf(num_arg); pop_n_elems(num_arg); push_number(i); }