/* Evaluate compiled expression nodes */ #include #include #include "string.h" #include "PROTO.h" #include "object.h" #include "objimpl.h" #include "intobject.h" #include "stringobject.h" #include "tupleobject.h" #include "listobject.h" #include "dictobject.h" #include "builtinobject.h" #include "methodobject.h" #include "moduleobject.h" #include "context.h" #include "funcobject.h" #include "classobject.h" #include "token.h" #include "graminit.h" #include "run.h" #include "import.h" #include "support.h" #include "sysmodule.h" #include "compile.h" #include "opcode.h" /* List access macros */ #ifdef NDEBUG #define GETITEM(v, i) GETLISTITEM((listobject *)(v), (i)) #define GETITEMNAME(v, i) GETSTRINGVALUE((stringobject *)GETITEM((v), (i))) #else #define GETITEM(v, i) getlistitem((v), (i)) #define GETITEMNAME(v, i) getstringvalue(getlistitem((v), (i))) #endif typedef struct { int b_type; /* what kind of block this is */ int b_handler; /* where to jump to find handler */ int b_level; /* value stack level to pop to */ } block; typedef struct _frame { OB_HEAD struct _frame *f_back; /* previous frame, or NULL */ codeobject *f_code; /* code segment */ object *f_locals; /* local symbol table (dictobject) */ object *f_globals; /* global symbol table (dictobject) */ object **f_valuestack; /* malloc'ed array */ block *f_blockstack; /* malloc'ed array */ int f_nvalues; /* size of f_valuestack */ int f_nblocks; /* size of f_blockstack */ int f_ivalue; /* index in f_valuestack */ int f_iblock; /* index in f_blockstack */ int f_nexti; /* index in f_code (next instruction) */ } frameobject; #define is_frameobject(op) ((op)->ob_type == &Frametype) static void frame_dealloc(f) frameobject *f; { XDECREF(f->f_back); XDECREF(f->f_code); XDECREF(f->f_locals); XDECREF(f->f_globals); XDEL(f->f_valuestack); XDEL(f->f_blockstack); DEL(f); } typeobject Frametype = { OB_HEAD_INIT(&Typetype) 0, "frame", sizeof(frameobject), 0, frame_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ }; static frameobject * newframeobject PROTO( (frameobject *, codeobject *, object *, object *, int, int)); static frameobject * newframeobject(back, code, locals, globals, nvalues, nblocks) frameobject *back; codeobject *code; object *locals; object *globals; int nvalues; int nblocks; { frameobject *f; if ((back != NULL && !is_frameobject(back)) || code == NULL || !is_codeobject(code) || locals == NULL || !is_dictobject(locals) || globals == NULL || !is_dictobject(globals) || nvalues < 0 || nblocks < 0) { err_badcall(); return NULL; } f = NEWOBJ(frameobject, &Frametype); if (f != NULL) { if (back) INCREF(back); f->f_back = back; INCREF(code); f->f_code = code; INCREF(locals); f->f_locals = locals; INCREF(globals); f->f_globals = globals; f->f_valuestack = NEW(object *, nvalues+1); f->f_blockstack = NEW(block, nblocks+1); f->f_nvalues = nvalues; f->f_nblocks = nblocks; f->f_ivalue = f->f_iblock = f->f_nexti = 0; if (f->f_valuestack == NULL || f->f_blockstack == NULL) { err_nomem(); DECREF(f); f = NULL; } } return f; } #define GETUSTRINGVALUE(s) ((unsigned char *)GETSTRINGVALUE(s)) #define Push(f, v) ((f)->f_valuestack[(f)->f_ivalue++] = (v)) #define Pop(f) ((f)->f_valuestack[--(f)->f_ivalue]) #define Top(f) ((f)->f_valuestack[(f)->f_ivalue-1]) #define Empty(f) ((f)->f_ivalue == 0) #define Full(f) ((f)->f_ivalue == (f)->f_nvalues) #define Nextbyte(f) (GETUSTRINGVALUE((f)->f_code->co_code)[(f)->f_nexti++]) #define Peekbyte(f) (GETUSTRINGVALUE((f)->f_code->co_code)[(f)->f_nexti]) #define Peekint(f) \ (GETUSTRINGVALUE((f)->f_code->co_code)[(f)->f_nexti] + \ GETUSTRINGVALUE((f)->f_code->co_code)[(f)->f_nexti+1]) #define Prevbyte(f) (GETUSTRINGVALUE((f)->f_code->co_code)[(f)->f_nexti-1]) #define Jumpto(f, x) ((f)->f_nexti = (x)) #define Jumpby(f, x) ((f)->f_nexti += (x)) #define Getconst(f, i) (GETITEM((f)->f_code->co_consts, (i))) #define Getname(f, i) (GETITEMNAME((f)->f_code->co_names, (i))) /* Corresponding functions, for debugging */ static void push(f, v) frameobject *f; object *v; { if (Full(f)) { printf("stack overflow\n"); abort(); } Push(f, v); } static object * pop(f) frameobject *f; { if (Empty(f)) { printf("stack underflow\n"); abort(); } return Pop(f); } static object * top(f) frameobject *f; { if (Empty(f)) { printf("stack underflow\n"); abort(); } return Top(f); } static int nextbyte(f) frameobject *f; { stringobject *code = (f)->f_code->co_code; if (f->f_nexti >= getstringsize((object *)code)) { printf("ran off end of instructions\n"); abort(); } return GETUSTRINGVALUE(code)[f->f_nexti++]; } static int nextint(f) frameobject *f; { int a, b; #ifdef NDEBUG a = Nextbyte(f); b = Nextbyte(f); #else a = nextbyte(f); b = nextbyte(f); #endif return a + (b << 8); } /* Tracing versions */ static void trace_push(f, v) frameobject *f; object *v; { printf("\tpush "); printobject(v, stdout, 0); printf("\n"); push(f, v); } static object * trace_pop(f) frameobject *f; { object *v; v = pop(f); printf("\tpop "); printobject(v, stdout, 0); printf("\n"); return v; } static object * trace_top(f) frameobject *f; { object *v; v = top(f); printf("\ttop "); printobject(v, stdout, 0); printf("\n"); return v; } static int trace_nextop(f) frameobject *f; { int op; int arg; printf("%d: ", f->f_nexti); op = nextbyte(f); if (op < HAVE_ARGUMENT) printf("op %3d\n", op); else { arg = Peekint(f); printf("op %d arg %d\n", op, arg); } return op; } /* Block management */ static void setup_block(f, type, handler) frameobject *f; int type; int handler; { block *b; if (f->f_iblock >= f->f_nblocks) { printf("block stack overflow\n"); abort(); } b = &f->f_blockstack[f->f_iblock++]; b->b_type = type; b->b_level = f->f_ivalue; b->b_handler = handler + f->f_nexti; } static block * pop_block(f) frameobject *f; { block *b; if (f->f_iblock <= 0) { printf("block stack underflow\n"); abort(); } b = &f->f_blockstack[--f->f_iblock]; while (f->f_ivalue > b->b_level) { object *v = Pop(f); XDECREF(v); } return b; } /* XXX Mixing "print ...," and direct file I/O on stdin/stdout XXX has some bad consequences. The needspace flag should XXX really be part of the file object. */ static int needspace; void flushline() { FILE *fp = sysgetfile("stdout", stdout); if (needspace) { fprintf(fp, "\n"); needspace = 0; } } static object * checkerror(ctx, v) context *ctx; object *v; { if (v == NULL) puterrno(ctx); return v; } static object * add(ctx, v, w) context *ctx; object *v, *w; { if (v->ob_type->tp_as_number != NULL) v = (*v->ob_type->tp_as_number->nb_add)(v, w); else if (v->ob_type->tp_as_sequence != NULL) v = (*v->ob_type->tp_as_sequence->sq_concat)(v, w); else { type_error(ctx, "+ not supported by operands"); return NULL; } return checkerror(ctx, v); } static object * sub(ctx, v, w) context *ctx; object *v, *w; { if (v->ob_type->tp_as_number != NULL) return checkerror(ctx, (*v->ob_type->tp_as_number->nb_subtract)(v, w)); type_error(ctx, "bad operand type(s) for -"); return NULL; } static object * mul(ctx, v, w) context *ctx; object *v, *w; { typeobject *tp; if (is_intobject(v) && w->ob_type->tp_as_sequence != NULL) { /* int*sequence -- swap v and w */ object *tmp = v; v = w; w = tmp; } tp = v->ob_type; if (tp->tp_as_number != NULL) return checkerror(ctx, (*tp->tp_as_number->nb_multiply)(v, w)); if (tp->tp_as_sequence != NULL) { if (!is_intobject(w)) { type_error(ctx, "can't multiply sequence with non-int"); return NULL; } if (tp->tp_as_sequence->sq_repeat == NULL) { type_error(ctx, "sequence does not support *"); return NULL; } return checkerror(ctx, (*tp->tp_as_sequence->sq_repeat) (v, (int)getintvalue(w))); } type_error(ctx, "bad operand type(s) for *"); return NULL; } static object * div(ctx, v, w) context *ctx; object *v, *w; { if (v->ob_type->tp_as_number != NULL) return checkerror(ctx, (*v->ob_type->tp_as_number->nb_divide)(v, w)); type_error(ctx, "bad operand type(s) for /"); return NULL; } static object * rem(ctx, v, w) context *ctx; object *v, *w; { if (v->ob_type->tp_as_number != NULL) return checkerror(ctx, (*v->ob_type->tp_as_number->nb_remainder)(v, w)); type_error(ctx, "bad operand type(s) for %"); return NULL; } static object * neg(ctx, v) context *ctx; object *v; { if (v->ob_type->tp_as_number != NULL) return checkerror(ctx, (*v->ob_type->tp_as_number->nb_negative)(v)); type_error(ctx, "bad operand type(s) for unary -"); return NULL; } static object * pos(ctx, v) context *ctx; object *v; { if (v->ob_type->tp_as_number != NULL) return checkerror(ctx, (*v->ob_type->tp_as_number->nb_positive)(v)); type_error(ctx, "bad operand type(s) for unary +"); return NULL; } static object * not(ctx, v) context *ctx; object *v; { int outcome = testbool(ctx, v); if (ctx->ctx_exception) return NULL; return checkerror(ctx, newintobject((long) !outcome)); } static object * call_builtin(ctx, func, args) context *ctx; object *func; object *args; { if (is_builtinobject(func)) { function funcptr = getbuiltinfunction(func); return (*funcptr)(ctx, args); } if (is_methodobject(func)) { method meth = getmethod(func); object *self = getself(func); return checkerror(ctx, (*meth)(self, args)); } if (is_classobject(func)) { if (args != NULL) { type_error(ctx, "classobject() allows no arguments"); return NULL; } return checkerror(ctx, newclassmemberobject(func)); } type_error(ctx, "call of non-function"); return NULL; } static object *eval_compiled PROTO((context *, codeobject *, object *, int)); /* XXX Eventually, this should not call eval_compiled recursively but create a new frame */ static object * call_function(ctx, func, args) context *ctx; object *func; object *args; { object *newargs = NULL; object *savelocals, *newlocals, *saveglobals; object *c, *v; if (is_classmethodobject(func)) { object *self = classmethodgetself(func); func = classmethodgetfunc(func); if (args == NULL) { args = self; } else { newargs = checkerror(ctx, newtupleobject(2)); if (newargs == NULL) return NULL; INCREF(self); INCREF(args); settupleitem(newargs, 0, self); settupleitem(newargs, 1, args); args = newargs; } } else { if (!is_funcobject(func)) { type_error(ctx, "call of non-function"); return NULL; } } c = checkerror(ctx, getfunccode(func)); if (c == NULL) { XDECREF(newargs); return NULL; } if (!is_codeobject(c)) { printf("Bad code\n"); abort(); } newlocals = checkerror(ctx, newdictobject()); if (newlocals == NULL) { XDECREF(newargs); return NULL; } savelocals = ctx->ctx_locals; ctx->ctx_locals = newlocals; saveglobals = ctx->ctx_globals; ctx->ctx_globals = getfuncglobals(func); v = eval_compiled(ctx, (codeobject *)c, args, 1); DECREF(ctx->ctx_locals); ctx->ctx_locals = savelocals; ctx->ctx_globals = saveglobals; XDECREF(newargs); return v; } static object * apply_subscript(ctx, v, w) context *ctx; object *v, *w; { typeobject *tp = v->ob_type; if (tp->tp_as_sequence == NULL && tp->tp_as_mapping == NULL) { type_error(ctx, "unsubscriptable object"); return NULL; } if (tp->tp_as_sequence != NULL) { int i; if (!is_intobject(w)) { type_error(ctx, "sequence subscript not int"); return NULL; } i = getintvalue(w); return checkerror(ctx, (*tp->tp_as_sequence->sq_item)(v, i)); } return checkerror(ctx, (*tp->tp_as_mapping->mp_subscript)(v, w)); } static object * loop_subscript(ctx, v, w) context *ctx; object *v, *w; { sequence_methods *sq = v->ob_type->tp_as_sequence; int i, n; if (sq == NULL) { type_error(ctx, "loop over non-sequence"); return NULL; } i = getintvalue(w); n = (*sq->sq_length)(v); if (i >= n) return NULL; /* End of loop */ return checkerror(ctx, (*sq->sq_item)(v, i)); } static int slice_index(ctx, v, isize, pi) context *ctx; object *v; int isize; int *pi; { if (v != NULL) { if (!is_intobject(v)) { type_error(ctx, "slice index must be int"); return 0; } *pi = getintvalue(v); if (*pi < 0) *pi += isize; } return 1; } static object * apply_slice(ctx, u, v, w) /* u[v:w] */ context *ctx; object *u, *v, *w; { typeobject *tp = u->ob_type; int ilow, ihigh, isize; if (tp->tp_as_sequence == NULL) { type_error(ctx, "only sequences can be sliced"); return NULL; } ilow = 0; isize = ihigh = (*tp->tp_as_sequence->sq_length)(u); if (!slice_index(ctx, v, isize, &ilow)) return NULL; if (!slice_index(ctx, w, isize, &ihigh)) return NULL; return checkerror(ctx, (*tp->tp_as_sequence->sq_slice)(u, ilow, ihigh)); } static void assign_subscript(ctx, w, key, v) context *ctx; object *w; object *key; object *v; { typeobject *tp = w->ob_type; sequence_methods *sq; mapping_methods *mp; int (*func)(); int err; if ((sq = tp->tp_as_sequence) != NULL && (func = sq->sq_ass_item) != NULL) { if (!is_intobject(key)) { type_error(ctx, "sequence subscript must be integer"); return; } err = (*func)(w, (int)getintvalue(key), v); } else if ((mp = tp->tp_as_mapping) != NULL && (func = mp->mp_ass_subscript) != NULL) { err = (*func)(w, key, v); } else { type_error(ctx, "can't assign to this subscripted object"); return; } if (err != 0) puterrno(ctx); } static void assign_slice(ctx, u, v, w, x) /* u[v:w] = x */ context *ctx; object *u, *v, *w, *x; { typeobject *tp = u->ob_type; int ilow, ihigh, isize; if (tp->tp_as_sequence == NULL || tp->tp_as_sequence->sq_ass_slice == NULL) { type_error(ctx, "unassignable slice"); return; } ilow = 0; isize = ihigh = (*tp->tp_as_sequence->sq_length)(u); if (!slice_index(ctx, v, isize, &ilow)) return; if (!slice_index(ctx, w, isize, &ihigh)) return; if ((*tp->tp_as_sequence->sq_ass_slice)(u, ilow, ihigh, x) != 0) puterrno(ctx); } static int cmp_exception(err, v) object *err, *v; { if (is_tupleobject(v)) { int i, n; n = gettuplesize(v); for (i = 0; i < n; i++) { if (err == gettupleitem(v, i)) return 1; } return 0; } return err == v; } static object * cmp_outcome(ctx, op, v, w) register context *ctx; enum cmp_op op; register object *v; register object *w; { register int cmp; register int res = 0; switch (op) { case IN: case NOT_IN: cmp = cmp_member(ctx, v, w); break; case IS: case IS_NOT: cmp = (v == w); break; case EXC_MATCH: cmp = cmp_exception(v, w); break; default: cmp = cmp_values(ctx, v, w); } if (ctx->ctx_exception) return NULL; switch (op) { case EXC_MATCH: case IS: case IN: res = cmp; break; case IS_NOT: case NOT_IN: res = !cmp; break; case LT: res = cmp < 0; break; case LE: res = cmp <= 0; break; case EQ: res = cmp == 0; break; case NE: res = cmp != 0; break; case GT: res = cmp > 0; break; case GE: res = cmp >= 0; break; /* XXX no default? */ } v = res ? True : False; INCREF(v); return v; } static void import_from(ctx, v, name) context *ctx; object *v; char *name; { object *w, *x; w = getmoduledict(v); if (name[0] == '*') { int i; int n = getdictsize(w); for (i = 0; i < n; i++) { name = getdictkey(w, i); if (name == NULL || name[0] == '_') continue; x = dictlookup(w, name); if (x == NULL) { /* XXX can't happen? */ name_error(ctx, name); break; } if (dictinsert(ctx->ctx_locals, name, x) != 0) { puterrno(ctx); break; } } } else { x = dictlookup(w, name); if (x == NULL) name_error(ctx, name); else if (dictinsert(ctx->ctx_locals, name, x) != 0) puterrno(ctx); } } static object * build_class(ctx, v, w) context *ctx; object *v; /* None or tuple containing base classes */ object *w; /* dictionary */ { if (is_tupleobject(v)) { int i; for (i = gettuplesize(v); --i >= 0; ) { object *x = gettupleitem(v, i); if (!is_classobject(x)) { type_error(ctx, "base is not a class object"); return NULL; } } } else { v = NULL; } if (!is_dictobject(w)) { sys_error(ctx, "build_class with non-dictionary"); return NULL; } return checkerror(ctx, newclassobject(v, w)); } static object * eval_compiled(ctx, co, arg, needvalue) context *ctx; codeobject *co; object *arg; int needvalue; { frameobject *f; register object *v; register object *w; register object *u; register object *x; char *name; int i, op; FILE *fp; #ifndef NDEBUG int trace = dictlookup(ctx->ctx_globals, "__trace") != NULL; #endif f = newframeobject( (frameobject *)NULL, /*back*/ co, /*code*/ ctx->ctx_locals, /*locals*/ ctx->ctx_globals, /*globals*/ 50, /*nvalues*/ 20); /*nblocks*/ if (f == NULL) { puterrno(ctx); return NULL; } #define EMPTY() Empty(f) #define FULL() Full(f) #define GETCONST(i) Getconst(f, i) #define GETNAME(i) Getname(f, i) #define JUMPTO(x) Jumpto(f, x) #define JUMPBY(x) Jumpby(f, x) #ifdef NDEBUG #define PUSH(v) Push(f, v) #define TOP() Top(f) #define POP() Pop(f) #else #define PUSH(v) if(trace) trace_push(f, v); else push(f, v) #define TOP() (trace ? trace_top(f) : top(f)) #define POP() (trace ? trace_pop(f) : pop(f)) #endif if (arg != NULL) { INCREF(arg); PUSH(arg); } while (f->f_nexti < getstringsize((object *)f->f_code->co_code) && !ctx->ctx_exception) { #ifdef NDEBUG op = Nextbyte(f); #else op = trace ? trace_nextop(f) : nextbyte(f); #endif if (op >= HAVE_ARGUMENT) i = nextint(f); switch (op) { case DUP_TOP: v = TOP(); INCREF(v); PUSH(v); break; case POP_TOP: v = POP(); DECREF(v); break; case ROT_TWO: v = POP(); w = POP(); PUSH(v); PUSH(w); break; case ROT_THREE: v = POP(); w = POP(); x = POP(); PUSH(v); PUSH(x); PUSH(w); break; case UNARY_POSITIVE: v = POP(); u = pos(ctx, v); DECREF(v); PUSH(u); break; case UNARY_NEGATIVE: v = POP(); u = neg(ctx, v); DECREF(v); PUSH(u); break; case UNARY_NOT: v = POP(); u = not(ctx, v); DECREF(v); PUSH(u); break; case UNARY_CONVERT: v = POP(); u = checkerror(ctx, reprobject(v)); DECREF(v); PUSH(u); break; case UNARY_CALL: v = POP(); if (is_classmethodobject(v) || is_funcobject(v)) u = call_function(ctx, v, (object *)NULL); else u = call_builtin(ctx, v, (object *)NULL); DECREF(v); PUSH(u); break; case BINARY_MULTIPLY: w = POP(); v = POP(); u = mul(ctx, v, w); DECREF(v); DECREF(w); PUSH(u); break; case BINARY_DIVIDE: w = POP(); v = POP(); u = div(ctx, v, w); DECREF(v); DECREF(w); PUSH(u); break; case BINARY_MODULO: w = POP(); v = POP(); u = rem(ctx, v, w); DECREF(v); DECREF(w); PUSH(u); break; case BINARY_ADD: w = POP(); v = POP(); u = add(ctx, v, w); DECREF(v); DECREF(w); PUSH(u); break; case BINARY_SUBTRACT: w = POP(); v = POP(); u = sub(ctx, v, w); DECREF(v); DECREF(w); PUSH(u); break; case BINARY_SUBSCR: w = POP(); v = POP(); u = apply_subscript(ctx, v, w); DECREF(v); DECREF(w); PUSH(u); break; case BINARY_CALL: w = POP(); v = POP(); if (is_classmethodobject(v) || is_funcobject(v)) u = call_function(ctx, v, w); else u = call_builtin(ctx, v, w); DECREF(v); DECREF(w); PUSH(u); break; case SLICE+0: case SLICE+1: case SLICE+2: case SLICE+3: op -= SLICE; if (op & 2) w = POP(); else w = NULL; if (op & 1) v = POP(); else v = NULL; u = POP(); x = apply_slice(ctx, u, v, w); DECREF(u); XDECREF(v); XDECREF(w); PUSH(x); break; case STORE_SLICE+0: case STORE_SLICE+1: case STORE_SLICE+2: case STORE_SLICE+3: op -= SLICE; if (op & 2) w = POP(); else w = NULL; if (op & 1) v = POP(); else v = NULL; u = POP(); x = POP(); assign_slice(ctx, u, v, w, x); /* u[v:w] = x */ DECREF(x); DECREF(u); XDECREF(v); XDECREF(w); break; case DELETE_SLICE+0: case DELETE_SLICE+1: case DELETE_SLICE+2: case DELETE_SLICE+3: op -= SLICE; if (op & 2) w = POP(); else w = NULL; if (op & 1) v = POP(); else v = NULL; u = POP(); x = NULL; assign_slice(ctx, u, v, w, x); /* del u[v:w] */ DECREF(u); XDECREF(v); XDECREF(w); break; case STORE_SUBSCR: w = POP(); v = POP(); u = POP(); /* v[w] = u */ assign_subscript(ctx, v, w, u); DECREF(u); DECREF(v); DECREF(w); break; case DELETE_SUBSCR: w = POP(); v = POP(); /* del v[w] */ assign_subscript(ctx, v, w, (object *)NULL); DECREF(v); DECREF(w); break; case PRINT_EXPR: v = POP(); fp = sysgetfile("stdout", stdout); /* Print value except if procedure result */ if (v != None) { flushline(); printobject(v, fp, 0); fprintf(fp, "\n"); } DECREF(v); break; case PRINT_ITEM: v = POP(); fp = sysgetfile("stdout", stdout); if (needspace) fprintf(fp, " "); if (is_stringobject(v)) { char *s = getstringvalue(v); int len = getstringsize(v); fwrite(s, 1, len, fp); if (len > 0 && s[len-1] == '\n') needspace = 0; else needspace = 1; } else { printobject(v, fp, 0); needspace = 1; } DECREF(v); break; case PRINT_NEWLINE: fp = sysgetfile("stdout", stdout); fprintf(fp, "\n"); needspace = 0; break; case BREAK_LOOP: raise_pseudo(ctx, BREAK_PSEUDO); break; case RAISE_EXCEPTION: v = POP(); w = POP(); if (!is_stringobject(w)) { DECREF(v); DECREF(v); type_error(ctx, "exceptions must be strings"); } else { raise_exception(ctx, w, v); } break; case LOAD_LOCALS: v = ctx->ctx_locals; INCREF(v); PUSH(v); break; case RETURN_VALUE: v = POP(); raise_pseudo(ctx, RETURN_PSEUDO); ctx->ctx_errval = v; break; case REQUIRE_ARGS: if (EMPTY()) type_error(ctx, "function expects argument(s)"); break; case REFUSE_ARGS: if (!EMPTY()) type_error(ctx, "function expects no argument(s)"); break; case BUILD_FUNCTION: v = POP(); x = checkerror(ctx, newfuncobject(v, ctx->ctx_globals)); DECREF(v); PUSH(x); break; case POP_BLOCK: (void) pop_block(f); break; case END_FINALLY: v = POP(); w = POP(); if (is_intobject(v)) { raise_pseudo(ctx, (int)getintvalue(v)); DECREF(v); if (w == None) DECREF(w); else ctx->ctx_errval = w; } else if (is_stringobject(v)) raise_exception(ctx, v, w); else if (v == None) { DECREF(v); DECREF(w); } else { sys_error(ctx, "'finally' pops bad exception"); } break; case BUILD_CLASS: w = POP(); v = POP(); x = build_class(ctx, v, w); PUSH(x); DECREF(v); DECREF(w); break; case STORE_NAME: name = GETNAME(i); v = POP(); if (dictinsert(ctx->ctx_locals, name, v) != 0) mem_error(ctx, "insert in symbol table"); DECREF(v); break; case DELETE_NAME: name = GETNAME(i); if (dictremove(ctx->ctx_locals, name) != 0) name_error(ctx, name); break; case UNPACK_TUPLE: v = POP(); if (!is_tupleobject(v)) { type_error(ctx, "unpack non-tuple"); } else if (gettuplesize(v) != i) { error(ctx, "unpack tuple of wrong size"); } else { for (; --i >= 0; ) { w = gettupleitem(v, i); INCREF(w); PUSH(w); } } DECREF(v); break; case UNPACK_LIST: v = POP(); if (!is_listobject(v)) { type_error(ctx, "unpack non-list"); } else if (getlistsize(v) != i) { error(ctx, "unpack tuple of wrong size"); } else { for (; --i >= 0; ) { w = getlistitem(v, i); INCREF(w); PUSH(w); } } DECREF(v); break; case STORE_ATTR: name = GETNAME(i); v = POP(); u = POP(); /* v.name = u */ if (v->ob_type->tp_setattr == NULL) { type_error(ctx, "object without writable attributes"); } else { if ((*v->ob_type->tp_setattr)(v, name, u) != 0) puterrno(ctx); } DECREF(v); DECREF(u); break; case DELETE_ATTR: name = GETNAME(i); v = POP(); /* del v.name */ if (v->ob_type->tp_setattr == NULL) { type_error(ctx, "object without writable attributes"); } else { if ((*v->ob_type->tp_setattr) (v, name, (object *)NULL) != 0) puterrno(ctx); } DECREF(v); break; case LOAD_CONST: v = GETCONST(i); INCREF(v); PUSH(v); break; case LOAD_NAME: name = GETNAME(i); v = dictlookup(ctx->ctx_locals, name); if (v == NULL) { v = dictlookup(ctx->ctx_globals, name); if (v == NULL) v = dictlookup(ctx->ctx_builtins, name); } if (v == NULL) name_error(ctx, name); /* XXX could optimize */ else INCREF(v); PUSH(v); break; case BUILD_TUPLE: v = checkerror(ctx, newtupleobject(i)); if (v != NULL) { for (; --i >= 0;) { w = POP(); settupleitem(v, i, w); } } PUSH(v); break; case BUILD_LIST: v = checkerror(ctx, newlistobject(i)); if (v != NULL) { for (; --i >= 0;) { w = POP(); setlistitem(v, i, w); } } PUSH(v); break; case BUILD_MAP: v = checkerror(ctx, newdictobject()); PUSH(v); break; case LOAD_ATTR: name = GETNAME(i); v = POP(); if (v->ob_type->tp_getattr == NULL) { type_error(ctx, "attribute-less object"); u = NULL; } else { u = checkerror(ctx, (*v->ob_type->tp_getattr)(v, name)); } DECREF(v); PUSH(u); break; case COMPARE_OP: w = POP(); v = POP(); u = cmp_outcome(ctx, (enum cmp_op)i, v, w); DECREF(v); DECREF(w); PUSH(u); break; case IMPORT_NAME: name = GETNAME(i); u = import_module(ctx, name); if (u != NULL) { INCREF(u); PUSH(u); } break; case IMPORT_FROM: name = GETNAME(i); v = TOP(); import_from(ctx, v, name); break; case JUMP_FORWARD: JUMPBY(i); break; case JUMP_IF_FALSE: if (!testbool(ctx, TOP())) JUMPBY(i); break; case JUMP_IF_TRUE: if (testbool(ctx, TOP())) JUMPBY(i); break; case JUMP_ABSOLUTE: JUMPTO(i); /* XXX Should check for interrupts more often? */ if (intrcheck()) intr_error(ctx); break; case FOR_LOOP: /* for v in s: ... On entry: stack contains s, i. On exit: stack contains s, i+1, s[i]; but if loop exhausted: s, i are popped, and we jump */ w = POP(); /* Loop index */ v = POP(); /* Sequence object */ x = loop_subscript(ctx, v, w); if (x != NULL) { PUSH(v); u = checkerror(ctx, newintobject(getintvalue(w)+1)); PUSH(u); DECREF(w); PUSH(x); } else { DECREF(v); DECREF(w); JUMPBY(i); } break; case SETUP_LOOP: setup_block(f, SETUP_LOOP, i); break; case SETUP_EXCEPT: setup_block(f, SETUP_EXCEPT, i); break; case SETUP_FINALLY: setup_block(f, SETUP_FINALLY, i); break; default: printf("opcode %d\n", op); sys_error(ctx, "eval_compiled: unknown opcode"); break; } /* Unwind block stack if an exception occurred */ while (ctx->ctx_exception && f->f_iblock > 0) { block *b = pop_block(f); if (b->b_type == SETUP_LOOP && ctx->ctx_exception == BREAK_PSEUDO) { clear_exception(ctx); JUMPTO(b->b_handler); break; } else if (b->b_type == SETUP_FINALLY || b->b_type == SETUP_EXCEPT && ctx->ctx_exception == CATCHABLE_EXCEPTION) { v = ctx->ctx_errval; if (v == NULL) v = None; INCREF(v); PUSH(v); v = ctx->ctx_error; if (v == NULL) v = newintobject(ctx->ctx_exception); else INCREF(v); PUSH(v); clear_exception(ctx); JUMPTO(b->b_handler); break; } } } if (ctx->ctx_exception) { while (!EMPTY()) { v = POP(); XDECREF(v); } v = NULL; if (ctx->ctx_exception == RETURN_PSEUDO) { if (needvalue) { v = ctx->ctx_errval; INCREF(v); clear_exception(ctx); } else { /* XXX Can detect this statically! */ type_error(ctx, "unexpected return statement"); } } } else { if (needvalue) v = POP(); else v = NULL; if (!EMPTY()) { sys_error(ctx, "stack not cleaned up"); XDECREF(v); while (!EMPTY()) { v = POP(); XDECREF(v); } v = NULL; } } #undef EMPTY #undef FULL #undef GETCONST #undef GETNAME #undef JUMPTO #undef JUMPBY #undef POP #undef TOP #undef PUSH DECREF(f); return v; } /* Provisional interface until everything is compilable */ #include "node.h" static object * eval_or_exec(ctx, n, arg, needvalue) context *ctx; node *n; object *arg; int needvalue; { object *v; codeobject *co = compile(n); freenode(n); /* XXX A rather strange place to do this! */ if (co == NULL) { puterrno(ctx); return NULL; } v = eval_compiled(ctx, co, arg, needvalue); DECREF(co); return v; } object * eval_node(ctx, n) context *ctx; node *n; { return eval_or_exec(ctx, n, (object *)NULL, 1/*needvalue*/); } void exec_node(ctx, n) context *ctx; node *n; { (void) eval_or_exec(ctx, n, (object *)NULL, 0/*needvalue*/); }