code.c

00001 #include <stdio.h>
00002 #include <stdarg.h>
00003 #include <string.h>
00004 #include "nasal.h"
00005 #include "code.h"
00006 
00008 // Debugging stuff. ////////////////////////////////////////////////////
00010 //#define INTERPRETER_DUMP
00011 #if !defined(INTERPRETER_DUMP)
00012 # define DBG(expr) /* noop */
00013 #else
00014 # define DBG(expr) expr
00015 # include <stdio.h>
00016 # include <stdlib.h>
00017 #endif
00018 char* opStringDEBUG(int op);
00019 void printOpDEBUG(int ip, int op);
00020 void printStackDEBUG(struct Context* ctx);
00022 
00023 #ifdef _MSC_VER
00024 #define vsnprintf _vsnprintf
00025 #endif
00026 
00027 struct Globals* globals = 0;
00028 
00029 static naRef bindFunction(struct Context* ctx, struct Frame* f, naRef code);
00030 
00031 #define ERR(c, msg) naRuntimeError((c),(msg))
00032 void naRuntimeError(struct Context* c, const char* fmt, ...)
00033 {
00034     va_list ap;
00035     va_start(ap, fmt);
00036     vsnprintf(c->error, sizeof(c->error), fmt, ap);
00037     va_end(ap);
00038     longjmp(c->jumpHandle, 1);
00039 }
00040 
00041 void naRethrowError(naContext subc)
00042 {
00043     strncpy(subc->callParent->error, subc->error, sizeof(subc->error));
00044     subc->callParent->dieArg = subc->dieArg;
00045     longjmp(subc->callParent->jumpHandle, 1);
00046 }
00047 
00048 #define END_PTR ((void*)1)
00049 #define IS_END(r) (IS_REF((r)) && PTR((r)).obj == END_PTR)
00050 static naRef endToken()
00051 {
00052     naRef r;
00053     SETPTR(r, END_PTR);
00054     return r;
00055 }
00056 
00057 static int boolify(struct Context* ctx, naRef r)
00058 {
00059     if(IS_NUM(r)) return r.num != 0;
00060     if(IS_NIL(r) || IS_END(r)) return 0;
00061     if(IS_STR(r)) {
00062         double d;
00063         if(naStr_len(r) == 0) return 0;
00064         if(naStr_tonum(r, &d)) return d != 0;
00065         else return 1;
00066     }
00067     ERR(ctx, "non-scalar used in boolean context");
00068     return 0;
00069 }
00070 
00071 static double numify(struct Context* ctx, naRef o)
00072 {
00073     double n;
00074     if(IS_NUM(o)) return o.num;
00075     else if(IS_NIL(o)) ERR(ctx, "nil used in numeric context");
00076     else if(!IS_STR(o)) ERR(ctx, "non-scalar in numeric context");
00077     else if(naStr_tonum(o, &n)) return n;
00078     else ERR(ctx, "non-numeric string in numeric context");
00079     return 0;
00080 }
00081 
00082 static naRef stringify(struct Context* ctx, naRef r)
00083 {
00084     if(IS_STR(r)) return r;
00085     if(IS_NUM(r)) return naStr_fromnum(naNewString(ctx), r.num);
00086     ERR(ctx, "non-scalar in string context");
00087     return naNil();
00088 }
00089 
00090 static int checkVec(struct Context* ctx, naRef vec, naRef idx)
00091 {
00092     int i = (int)numify(ctx, idx);
00093     if(i < 0) i += naVec_size(vec);
00094     if(i < 0 || i >= naVec_size(vec))
00095         naRuntimeError(ctx, "vector index %d out of bounds (size: %d)",
00096                        i, naVec_size(vec));
00097     return i;
00098 }
00099 
00100 static int checkStr(struct Context* ctx, naRef str, naRef idx)
00101 {
00102     int i = (int)numify(ctx, idx);
00103     if(i < 0) i += naStr_len(str);
00104     if(i < 0 || i >= naStr_len(str))
00105         naRuntimeError(ctx, "string index %d out of bounds (size: %d)",
00106                        i, naStr_len(str));
00107     return i;
00108 }
00109 
00110 static naRef containerGet(struct Context* ctx, naRef box, naRef key)
00111 {
00112     naRef result = naNil();
00113     if(!IS_SCALAR(key)) ERR(ctx, "container index not scalar");
00114     if(IS_HASH(box)) {
00115         naHash_get(box, key, &result);
00116     } else if(IS_VEC(box)) {
00117         result = naVec_get(box, checkVec(ctx, box, key));
00118     } else if(IS_STR(box)) {
00119         result = naNum((unsigned char)naStr_data(box)[checkStr(ctx, box, key)]);
00120     } else {
00121         ERR(ctx, "extract from non-container");
00122     }
00123     return result;
00124 }
00125 
00126 static void containerSet(struct Context* ctx, naRef box, naRef key, naRef val)
00127 {
00128     if(!IS_SCALAR(key))   ERR(ctx, "container index not scalar");
00129     else if(IS_HASH(box)) naHash_set(box, key, val);
00130     else if(IS_VEC(box))  naVec_set(box, checkVec(ctx, box, key), val);
00131     else if(IS_STR(box)) {
00132         if(PTR(box).str->hashcode)
00133             ERR(ctx, "cannot change immutable string");
00134         naStr_data(box)[checkStr(ctx, box, key)] = (char)numify(ctx, val);
00135     } else ERR(ctx, "insert into non-container");
00136 }
00137 
00138 static void initTemps(struct Context* c)
00139 {
00140     c->tempsz = 4;
00141     c->temps = naAlloc(c->tempsz * sizeof(struct naObj*));
00142     c->ntemps = 0;
00143 }
00144 
00145 static void initContext(struct Context* c)
00146 {
00147     int i;
00148     c->fTop = c->opTop = c->markTop = 0;
00149     for(i=0; i<NUM_NASAL_TYPES; i++)
00150         c->nfree[i] = 0;
00151 
00152     if(c->tempsz > 32) {
00153         naFree(c->temps);
00154         initTemps(c);
00155     }
00156 
00157     c->callParent = 0;
00158     c->callChild = 0;
00159     c->dieArg = naNil();
00160     c->error[0] = 0;
00161     c->userData = 0;
00162 }
00163 
00164 static void initGlobals()
00165 {
00166     int i;
00167     struct Context* c;
00168     globals = (struct Globals*)naAlloc(sizeof(struct Globals));
00169     naBZero(globals, sizeof(struct Globals));
00170 
00171     globals->sem = naNewSem();
00172     globals->lock = naNewLock();
00173 
00174     globals->allocCount = 256; // reasonable starting value
00175     for(i=0; i<NUM_NASAL_TYPES; i++)
00176         naGC_init(&(globals->pools[i]), i);
00177     globals->deadsz = 256;
00178     globals->ndead = 0;
00179     globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz);
00180 
00181     // Initialize a single context
00182     globals->freeContexts = 0;
00183     globals->allContexts = 0;
00184     c = naNewContext();
00185 
00186     globals->symbols = naNewHash(c);
00187     globals->save = naNewVector(c);
00188 
00189     // Cache pre-calculated "me", "arg" and "parents" scalars
00190     globals->meRef = naInternSymbol(naStr_fromdata(naNewString(c), "me", 2));
00191     globals->argRef = naInternSymbol(naStr_fromdata(naNewString(c), "arg", 3));
00192     globals->parentsRef = naInternSymbol(naStr_fromdata(naNewString(c), "parents", 7));
00193 
00194     naFreeContext(c);
00195 }
00196 
00197 struct Context* naNewContext()
00198 {
00199     struct Context* c;
00200     if(globals == 0)
00201         initGlobals();
00202 
00203     LOCK();
00204     c = globals->freeContexts;
00205     if(c) {
00206         globals->freeContexts = c->nextFree;
00207         c->nextFree = 0;
00208         UNLOCK();
00209         initContext(c);
00210     } else {
00211         UNLOCK();
00212         c = (struct Context*)naAlloc(sizeof(struct Context));
00213         initTemps(c);
00214         initContext(c);
00215         LOCK();
00216         c->nextAll = globals->allContexts;
00217         c->nextFree = 0;
00218         globals->allContexts = c;
00219         UNLOCK();
00220     }
00221     return c;
00222 }
00223 
00224 struct Context* naSubContext(struct Context* super)
00225 {
00226     struct Context* ctx = naNewContext();
00227     if(super->callChild) naFreeContext(super->callChild);
00228     ctx->callParent = super;
00229     super->callChild = ctx;
00230     return ctx;
00231 }
00232 
00233 void naFreeContext(struct Context* c)
00234 {
00235     c->ntemps = 0;
00236     if(c->callChild) naFreeContext(c->callChild);
00237     if(c->callParent) c->callParent->callChild = 0;
00238     LOCK();
00239     c->nextFree = globals->freeContexts;
00240     globals->freeContexts = c;
00241     UNLOCK();
00242 }
00243 
00244 // Note that opTop is incremented separately, to avoid situations
00245 // where the "r" expression also references opTop.  The SGI compiler
00246 // is known to have issues with such code.
00247 #define PUSH(r) do { \
00248     if(ctx->opTop >= MAX_STACK_DEPTH) ERR(ctx, "stack overflow"); \
00249     ctx->opStack[ctx->opTop] = r; \
00250     ctx->opTop++;                 \
00251     } while(0)
00252 
00253 static void setupArgs(naContext ctx, struct Frame* f, naRef* args, int nargs)
00254 {
00255     int i;
00256     struct naCode* c = PTR(PTR(f->func).func->code).code;
00257 
00258     // Set the argument symbols, and put any remaining args in a vector
00259     if(nargs < c->nArgs)
00260         naRuntimeError(ctx, "too few function args (have %d need %d)",
00261             nargs, c->nArgs);
00262     for(i=0; i<c->nArgs; i++)
00263         naHash_newsym(PTR(f->locals).hash,
00264                       &c->constants[c->argSyms[i]], &args[i]);
00265     args += c->nArgs;
00266     nargs -= c->nArgs;
00267     for(i=0; i<c->nOptArgs; i++, nargs--) {
00268         naRef val = nargs > 0 ? args[i] : c->constants[c->optArgVals[i]];
00269         if(IS_CODE(val))
00270             val = bindFunction(ctx, &ctx->fStack[ctx->fTop-2], val);
00271         naHash_newsym(PTR(f->locals).hash, &c->constants[c->optArgSyms[i]], 
00272                       &val);
00273     }
00274     args += c->nOptArgs;
00275     if(c->needArgVector || nargs > 0) {
00276         naRef argsv = naNewVector(ctx);
00277         naVec_setsize(argsv, nargs > 0 ? nargs : 0);
00278         for(i=0; i<nargs; i++)
00279             PTR(argsv).vec->rec->array[i] = *args++;
00280         naHash_newsym(PTR(f->locals).hash, &c->restArgSym, &argsv);
00281     }
00282 }
00283 
00284 static struct Frame* setupFuncall(struct Context* ctx, int nargs, int mcall)
00285 {
00286     naRef *frame;
00287     struct Frame* f;
00288     
00289     DBG(printf("setupFuncall(nargs:%d, mcall:%d)\n", nargs, mcall);)
00290 
00291     frame = &ctx->opStack[ctx->opTop - nargs - 1];
00292     if(!IS_FUNC(frame[0]))
00293         ERR(ctx, "function/method call invoked on uncallable object");
00294 
00295     ctx->opFrame = ctx->opTop - (nargs + 1 + mcall);
00296 
00297     // Just do native calls right here
00298     if(PTR(PTR(frame[0]).func->code).obj->type == T_CCODE) {
00299         naRef obj = mcall ? frame[-1] : naNil();
00300         naCFunction fp = PTR(PTR(frame[0]).func->code).ccode->fptr;
00301         naRef result = (*fp)(ctx, obj, nargs, frame + 1);
00302         ctx->opTop = ctx->opFrame;
00303         PUSH(result);
00304         return &(ctx->fStack[ctx->fTop-1]);
00305     }
00306     
00307     if(ctx->fTop >= MAX_RECURSION) ERR(ctx, "call stack overflow");
00308     
00309     // Note: assign nil first, otherwise the naNew() can cause a GC,
00310     // which will now (after fTop++) see the *old* reference as a
00311     // markable value!
00312     f = &(ctx->fStack[ctx->fTop++]);
00313     f->locals = f->func = naNil();
00314     f->locals = naNewHash(ctx);
00315     f->func = frame[0];
00316     f->ip = 0;
00317     f->bp = ctx->opFrame;
00318 
00319     if(mcall)
00320         naHash_set(f->locals, globals->meRef, frame[-1]);
00321 
00322     setupArgs(ctx, f, frame+1, nargs);
00323 
00324     ctx->opTop = f->bp; // Pop the stack last, to avoid GC lossage
00325     DBG(printf("Entering frame %d with %d args\n", ctx->fTop-1, nargs);)
00326     return f;
00327 }
00328 
00329 static naRef evalEquality(int op, naRef ra, naRef rb)
00330 {
00331     int result = naEqual(ra, rb);
00332     return naNum((op==OP_EQ) ? result : !result);
00333 }
00334 
00335 static naRef evalCat(naContext ctx, naRef l, naRef r)
00336 {
00337     if(IS_VEC(l) && IS_VEC(r)) {
00338         int i, ls = naVec_size(l), rs = naVec_size(r);
00339         naRef v = naNewVector(ctx);
00340         naVec_setsize(v, ls + rs);
00341         for(i=0; i<ls; i+=1) naVec_set(v, i, naVec_get(l, i));
00342         for(i=0; i<rs; i+=1) naVec_set(v, i+ls, naVec_get(r, i));
00343         return v;
00344     } else {
00345         naRef a = stringify(ctx, l);
00346         naRef b = stringify(ctx, r);
00347         return naStr_concat(naNewString(ctx), a, b);
00348     }
00349 }
00350 
00351 // When a code object comes out of the constant pool and shows up on
00352 // the stack, it needs to be bound with the lexical context.
00353 static naRef bindFunction(struct Context* ctx, struct Frame* f, naRef code)
00354 {
00355     naRef result = naNewFunc(ctx, code);
00356     PTR(result).func->namespace = f->locals;
00357     PTR(result).func->next = f->func;
00358     return result;
00359 }
00360 
00361 static int getClosure(struct naFunc* c, naRef sym, naRef* result)
00362 {
00363     while(c) {
00364         if(naHash_get(c->namespace, sym, result)) return 1;
00365         c = PTR(c->next).func;
00366     }
00367     return 0;
00368 }
00369 
00370 static naRef getLocal2(struct Context* ctx, struct Frame* f, naRef sym)
00371 {
00372     naRef result;
00373     if(!naHash_get(f->locals, sym, &result))
00374         if(!getClosure(PTR(f->func).func, sym, &result))
00375             naRuntimeError(ctx, "undefined symbol: %s", naStr_data(sym));
00376     return result;
00377 }
00378 
00379 static void getLocal(struct Context* ctx, struct Frame* f,
00380                      naRef* sym, naRef* out)
00381 {
00382     struct naFunc* func;
00383     struct naStr* str = PTR(*sym).str;
00384     if(naHash_sym(PTR(f->locals).hash, str, out))
00385         return;
00386     func = PTR(f->func).func;
00387     while(func && PTR(func->namespace).hash) {
00388         if(naHash_sym(PTR(func->namespace).hash, str, out))
00389             return;
00390         func = PTR(func->next).func;
00391     }
00392     // Now do it again using the more general naHash_get().  This will
00393     // only be necessary if something has created the value in the
00394     // namespace using the more generic hash syntax
00395     // (e.g. namespace["symbol"] and not namespace.symbol).
00396     *out = getLocal2(ctx, f, *sym);
00397 }
00398 
00399 static int setClosure(naRef func, naRef sym, naRef val)
00400 {
00401     struct naFunc* c = PTR(func).func;
00402     if(c == 0) { return 0; }
00403     else if(naHash_tryset(c->namespace, sym, val)) { return 1; }
00404     else { return setClosure(c->next, sym, val); }
00405 }
00406 
00407 static naRef setSymbol(struct Frame* f, naRef sym, naRef val)
00408 {
00409     // Try the locals first, if not already there try the closures in
00410     // order.  Finally put it in the locals if nothing matched.
00411     if(!naHash_tryset(f->locals, sym, val))
00412         if(!setClosure(f->func, sym, val))
00413             naHash_set(f->locals, sym, val);
00414     return val;
00415 }
00416 
00417 // Funky API: returns null to indicate no member, an empty string to
00418 // indicate success, or a non-empty error message.  Works this way so
00419 // we can generate smart error messages without throwing them with a
00420 // longjmp -- this gets called under naMember_get() from C code.
00421 static const char* getMember_r(naRef obj, naRef field, naRef* out, int count)
00422 {
00423     int i;
00424     naRef p;
00425     struct VecRec* pv;
00426     if(--count < 0) return "too many parents";
00427     if(!IS_HASH(obj)) return 0;
00428     if(naHash_get(obj, field, out)) return "";
00429     if(!naHash_get(obj, globals->parentsRef, &p)) return 0;
00430     if(!IS_VEC(p)) return "object \"parents\" field not vector";
00431     pv = PTR(p).vec->rec;
00432     for(i=0; i<pv->size; i++) {
00433         const char* err = getMember_r(pv->array[i], field, out, count);
00434         if(err) return err; /* either an error or success */
00435     }
00436     return 0;
00437 }
00438 
00439 static void getMember(struct Context* ctx, naRef obj, naRef fld,
00440                       naRef* result, int count)
00441 {
00442     const char* err = getMember_r(obj, fld, result, count);
00443     if(!err)   naRuntimeError(ctx, "No such member: %s", naStr_data(fld));
00444     if(err[0]) naRuntimeError(ctx, err);
00445 }
00446 
00447 int naMember_get(naRef obj, naRef field, naRef* out)
00448 {
00449     const char* err = getMember_r(obj, field, out, 64);
00450     return err && !err[0];
00451 }
00452 
00453 // OP_EACH works like a vector get, except that it leaves the vector
00454 // and index on the stack, increments the index after use, and
00455 // pushes a nil if the index is beyond the end.
00456 static void evalEach(struct Context* ctx, int useIndex)
00457 {
00458     int idx = (int)(ctx->opStack[ctx->opTop-1].num);
00459     naRef vec = ctx->opStack[ctx->opTop-2];
00460     if(!IS_VEC(vec)) ERR(ctx, "foreach enumeration of non-vector");
00461     if(!PTR(vec).vec->rec || idx >= PTR(vec).vec->rec->size) {
00462         PUSH(endToken());
00463         return;
00464     }
00465     ctx->opStack[ctx->opTop-1].num = idx+1; // modify in place
00466     PUSH(useIndex ? naNum(idx) : naVec_get(vec, idx));
00467 }
00468 
00469 #define ARG() cd->byteCode[f->ip++]
00470 #define CONSTARG() cd->constants[ARG()]
00471 #define POP() ctx->opStack[--ctx->opTop]
00472 #define STK(n) (ctx->opStack[ctx->opTop-(n)])
00473 #define FIXFRAME() f = &(ctx->fStack[ctx->fTop-1]); \
00474     cd = PTR(PTR(f->func).func->code).code;
00475 static naRef run(struct Context* ctx)
00476 {
00477     struct Frame* f;
00478     struct naCode* cd;
00479     int op, arg;
00480     naRef a, b;
00481 
00482     ctx->dieArg = naNil();
00483     ctx->error[0] = 0;
00484 
00485     FIXFRAME();
00486 
00487     while(1) {
00488         op = cd->byteCode[f->ip++];
00489         DBG(printf("Stack Depth: %d\n", ctx->opTop));
00490         DBG(printOpDEBUG(f->ip-1, op));
00491         switch(op) {
00492         case OP_POP:
00493             ctx->opTop--;
00494             break;
00495         case OP_DUP:
00496             PUSH(ctx->opStack[ctx->opTop-1]);
00497             break;
00498         case OP_DUP2:
00499             PUSH(ctx->opStack[ctx->opTop-2]);
00500             PUSH(ctx->opStack[ctx->opTop-2]);
00501             break;
00502         case OP_XCHG:
00503             a = STK(1); STK(1) = STK(2); STK(2) = a;
00504             break;
00505 
00506 #define BINOP(expr) do { \
00507     double l = IS_NUM(STK(2)) ? STK(2).num : numify(ctx, STK(2)); \
00508     double r = IS_NUM(STK(1)) ? STK(1).num : numify(ctx, STK(1)); \
00509     SETNUM(STK(2), expr);                                         \
00510     ctx->opTop--; } while(0)
00511 
00512         case OP_PLUS:  BINOP(l + r);         break;
00513         case OP_MINUS: BINOP(l - r);         break;
00514         case OP_MUL:   BINOP(l * r);         break;
00515         case OP_DIV:   BINOP(l / r);         break;
00516         case OP_LT:    BINOP(l <  r ? 1 : 0); break;
00517         case OP_LTE:   BINOP(l <= r ? 1 : 0); break;
00518         case OP_GT:    BINOP(l >  r ? 1 : 0); break;
00519         case OP_GTE:   BINOP(l >= r ? 1 : 0); break;
00520 #undef BINOP
00521 
00522         case OP_EQ: case OP_NEQ:
00523             STK(2) = evalEquality(op, STK(2), STK(1));
00524             ctx->opTop--;
00525             break;
00526         case OP_CAT:
00527             STK(2) = evalCat(ctx, STK(2), STK(1));
00528             ctx->opTop -= 1;
00529             break;
00530         case OP_NEG:
00531             STK(1) = naNum(-numify(ctx, STK(1)));
00532             break;
00533         case OP_NOT:
00534             STK(1) = naNum(boolify(ctx, STK(1)) ? 0 : 1);
00535             break;
00536         case OP_PUSHCONST:
00537             a = CONSTARG();
00538             if(IS_CODE(a)) a = bindFunction(ctx, f, a);
00539             PUSH(a);
00540             break;
00541         case OP_PUSHONE:
00542             PUSH(naNum(1));
00543             break;
00544         case OP_PUSHZERO:
00545             PUSH(naNum(0));
00546             break;
00547         case OP_PUSHNIL:
00548             PUSH(naNil());
00549             break;
00550         case OP_PUSHEND:
00551             PUSH(endToken());
00552             break;
00553         case OP_NEWVEC:
00554             PUSH(naNewVector(ctx));
00555             break;
00556         case OP_VAPPEND:
00557             naVec_append(STK(2), STK(1));
00558             ctx->opTop--;
00559             break;
00560         case OP_NEWHASH:
00561             PUSH(naNewHash(ctx));
00562             break;
00563         case OP_HAPPEND:
00564             naHash_set(STK(3), STK(2), STK(1));
00565             ctx->opTop -= 2;
00566             break;
00567         case OP_LOCAL:
00568             a = CONSTARG();
00569             getLocal(ctx, f, &a, &b);
00570             PUSH(b);
00571             break;
00572         case OP_SETSYM:
00573             STK(2) = setSymbol(f, STK(2), STK(1));
00574             ctx->opTop--;
00575             break;
00576         case OP_SETLOCAL:
00577             naHash_set(f->locals, STK(2), STK(1));
00578             STK(2) = STK(1); // FIXME: reverse order of arguments instead!
00579             ctx->opTop--;
00580             break;
00581         case OP_MEMBER:
00582             getMember(ctx, STK(1), CONSTARG(), &STK(1), 64);
00583             break;
00584         case OP_SETMEMBER:
00585             if(!IS_HASH(STK(3))) ERR(ctx, "non-objects have no members");
00586             naHash_set(STK(3), STK(2), STK(1));
00587             STK(3) = STK(1); // FIXME: fix arg order instead
00588             ctx->opTop -= 2;
00589             break;
00590         case OP_INSERT:
00591             containerSet(ctx, STK(3), STK(2), STK(1));
00592             STK(3) = STK(1); // FIXME: codegen order again...
00593             ctx->opTop -= 2;
00594             break;
00595         case OP_EXTRACT:
00596             STK(2) = containerGet(ctx, STK(2), STK(1));
00597             ctx->opTop--;
00598             break;
00599         case OP_JMPLOOP:
00600             // Identical to JMP, except for locking
00601             naCheckBottleneck();
00602             f->ip = cd->byteCode[f->ip];
00603             DBG(printf("   [Jump to: %d]\n", f->ip);)
00604             break;
00605         case OP_JMP:
00606             f->ip = cd->byteCode[f->ip];
00607             DBG(printf("   [Jump to: %d]\n", f->ip);)
00608             break;
00609         case OP_JIFEND:
00610             arg = ARG();
00611             if(IS_END(STK(1))) {
00612                 ctx->opTop--; // Pops **ONLY** if it's nil!
00613                 f->ip = arg;
00614                 DBG(printf("   [Jump to: %d]\n", f->ip);)
00615             }
00616             break;
00617         case OP_JIFTRUE:
00618             arg = ARG();
00619             if(boolify(ctx, STK(1))) {
00620                 f->ip = arg;
00621                 DBG(printf("   [Jump to: %d]\n", f->ip);)
00622             }
00623             break;
00624         case OP_JIFNOT:
00625             arg = ARG();
00626             if(!boolify(ctx, STK(1))) {
00627                 f->ip = arg;
00628                 DBG(printf("   [Jump to: %d]\n", f->ip);)
00629             }
00630             break;
00631         case OP_JIFNOTPOP:
00632             arg = ARG();
00633             if(!boolify(ctx, POP())) {
00634                 f->ip = arg;
00635                 DBG(printf("   [Jump to: %d]\n", f->ip);)
00636             }
00637             break;
00638         case OP_FCALL:
00639             f = setupFuncall(ctx, ARG(), 0);
00640             cd = PTR(PTR(f->func).func->code).code;
00641             break;
00642         case OP_MCALL:
00643             f = setupFuncall(ctx, ARG(), 1);
00644             cd = PTR(PTR(f->func).func->code).code;
00645             break;
00646         case OP_RETURN:
00647             a = STK(1);
00648             ctx->dieArg = naNil();
00649             if(ctx->callChild) naFreeContext(ctx->callChild);
00650             if(--ctx->fTop <= 0) return a;
00651             ctx->opTop = f->bp + 1; // restore the correct opstack frame!
00652             STK(1) = a;
00653             FIXFRAME();
00654             break;
00655         case OP_EACH:
00656             evalEach(ctx, 0);
00657             break;
00658         case OP_INDEX:
00659             evalEach(ctx, 1);
00660             break;
00661         case OP_MARK: // save stack state (e.g. "setjmp")
00662             if(ctx->markTop >= MAX_MARK_DEPTH)
00663                 ERR(ctx, "mark stack overflow");
00664             ctx->markStack[ctx->markTop++] = ctx->opTop;
00665             break;
00666         case OP_UNMARK: // pop stack state set by mark
00667             ctx->markTop--;
00668             break;
00669         case OP_BREAK: // restore stack state (FOLLOW WITH JMP!)
00670             ctx->opTop = ctx->markStack[ctx->markTop-1];
00671             break;
00672         case OP_BREAK2: // same, but also pop the mark stack
00673             ctx->opTop = ctx->markStack[--ctx->markTop];
00674             break;
00675         default:
00676             ERR(ctx, "BUG: bad opcode");
00677         }
00678         ctx->ntemps = 0; // reset GC temp vector
00679         DBG(printStackDEBUG(ctx);)
00680     }
00681     return naNil(); // unreachable
00682 }
00683 #undef POP
00684 #undef CONSTARG
00685 #undef STK
00686 #undef FIXFRAME
00687 
00688 void naSave(struct Context* ctx, naRef obj)
00689 {
00690     naVec_append(globals->save, obj);
00691 }
00692 
00693 int naStackDepth(struct Context* ctx)
00694 {
00695     return ctx ? ctx->fTop + naStackDepth(ctx->callChild): 0;
00696 }
00697 
00698 static int findFrame(naContext ctx, naContext* out, int fn)
00699 {
00700     int sd = naStackDepth(ctx->callChild);
00701     if(fn < sd) return findFrame(ctx->callChild, out, fn);
00702     *out = ctx;
00703     return ctx->fTop - 1 - (fn - sd);
00704 }
00705 
00706 int naGetLine(struct Context* ctx, int frame)
00707 {
00708     struct Frame* f;
00709     frame = findFrame(ctx, &ctx, frame);
00710     f = &ctx->fStack[frame];
00711     if(IS_FUNC(f->func) && IS_CODE(PTR(f->func).func->code)) {
00712         struct naCode* c = PTR(PTR(f->func).func->code).code;
00713         unsigned short* p = c->lineIps + c->nLines - 2;
00714         while(p >= c->lineIps && p[0] > f->ip)
00715             p -= 2;
00716         return p[1];
00717     }
00718     return -1;
00719 }
00720 
00721 naRef naGetSourceFile(struct Context* ctx, int frame)
00722 {
00723     naRef f;
00724     frame = findFrame(ctx, &ctx, frame);
00725     f = ctx->fStack[frame].func;
00726     f = PTR(f).func->code;
00727     return PTR(f).code->srcFile;
00728 }
00729 
00730 char* naGetError(struct Context* ctx)
00731 {
00732     if(IS_STR(ctx->dieArg))
00733         return (char*)PTR(ctx->dieArg).str->data;
00734     return ctx->error[0] ? ctx->error : 0;
00735 }
00736 
00737 naRef naBindFunction(naContext ctx, naRef code, naRef closure)
00738 {
00739     naRef func = naNewFunc(ctx, code);
00740     PTR(func).func->namespace = closure;
00741     PTR(func).func->next = naNil();
00742     return func;
00743 }
00744 
00745 naRef naBindToContext(naContext ctx, naRef code)
00746 {
00747     naRef func = naNewFunc(ctx, code);
00748     struct Frame* f = &ctx->fStack[ctx->fTop-1];
00749     PTR(func).func->namespace = f->locals;
00750     PTR(func).func->next = f->func;
00751     return func;
00752 }
00753 
00754 naRef naCall(naContext ctx, naRef func, int argc, naRef* args,
00755              naRef obj, naRef locals)
00756 {
00757     int i;
00758     naRef result;
00759     if(!ctx->callParent) naModLock();
00760 
00761     // We might have to allocate objects, which can call the GC.  But
00762     // the call isn't on the Nasal stack yet, so the GC won't find our
00763     // C-space arguments.
00764     naTempSave(ctx, func);
00765     for(i=0; i<argc; i++)
00766         naTempSave(ctx, args[i]);
00767     naTempSave(ctx, obj);
00768     naTempSave(ctx, locals);
00769 
00770     // naRuntimeError() calls end up here:
00771     if(setjmp(ctx->jumpHandle)) {
00772         if(!ctx->callParent) naModUnlock(ctx);
00773         return naNil();
00774     }
00775 
00776     if(IS_CCODE(PTR(func).func->code)) {
00777         naCFunction fp = PTR(PTR(func).func->code).ccode->fptr;
00778         result = (*fp)(ctx, obj, argc, args);
00779         if(!ctx->callParent) naModUnlock();
00780         return result;
00781     }
00782 
00783     if(IS_NIL(locals))
00784         locals = naNewHash(ctx);
00785     if(!IS_FUNC(func)) {
00786         func = naNewFunc(ctx, func);
00787         PTR(func).func->namespace = locals;
00788     }
00789     if(!IS_NIL(obj))
00790         naHash_set(locals, globals->meRef, obj);
00791 
00792     ctx->opTop = ctx->markTop = 0;
00793     ctx->fTop = 1;
00794     ctx->fStack[0].func = func;
00795     ctx->fStack[0].locals = locals;
00796     ctx->fStack[0].ip = 0;
00797     ctx->fStack[0].bp = ctx->opTop;
00798 
00799     if(args) setupArgs(ctx, ctx->fStack, args, argc);
00800 
00801     result = run(ctx);
00802     if(!ctx->callParent) naModUnlock(ctx);
00803     return result;
00804 }
00805 
00806 naRef naContinue(naContext ctx)
00807 {
00808     naRef result;
00809     if(!ctx->callParent) naModLock();
00810 
00811     ctx->dieArg = naNil();
00812     ctx->error[0] = 0;
00813 
00814     if(setjmp(ctx->jumpHandle)) {
00815         if(!ctx->callParent) naModUnlock(ctx);
00816         else naRethrowError(ctx);
00817         return naNil();
00818     }
00819 
00820     // Wipe off the old function arguments, and push the expected
00821     // result (either the result of our subcontext, or a synthesized
00822     // nil if the thrown error was from an extension function or
00823     // in-script die() call) before re-running the code from the
00824     // instruction following the error.
00825     ctx->opTop = ctx->opFrame;
00826     PUSH(ctx->callChild ? naContinue(ctx->callChild) : naNil());
00827 
00828     // Getting here means the child completed successfully.  But
00829     // because its original C stack was longjmp'd out of existence,
00830     // there is no one left to free the context, so we have to do it.
00831     // This is fragile, but unfortunately required.
00832     if(ctx->callChild) naFreeContext(ctx->callChild);
00833 
00834     result = run(ctx);
00835     if(!ctx->callParent) naModUnlock();
00836     return result;
00837 }

Generated on Mon Dec 17 09:30:54 2007 for SimGear by  doxygen 1.5.1