]> git.mxchange.org Git - simgear.git/blob - simgear/nasal/code.c
9e5907f6266b7e67b74f732b52f86f64835b8ca7
[simgear.git] / simgear / nasal / code.c
1 #include "nasal.h"
2 #include "code.h"
3
4 ////////////////////////////////////////////////////////////////////////
5 // Debugging stuff. ////////////////////////////////////////////////////
6 ////////////////////////////////////////////////////////////////////////
7 //#define DEBUG_NASAL
8 #if !defined(DEBUG_NASAL)
9 # define DBG(expr) /* noop */
10 #else
11 # define DBG(expr) expr
12 # include <stdio.h>
13 # include <stdlib.h>
14 #endif
15 char* opStringDEBUG(int op);
16 void printOpDEBUG(int ip, int op);
17 void printStackDEBUG(struct Context* ctx);
18 ////////////////////////////////////////////////////////////////////////
19
20 struct Globals* globals = 0;
21
22 static naRef bindFunction(struct Context* ctx, struct Frame* f, naRef code);
23
24 #define ERR(c, msg) naRuntimeError((c),(msg))
25 void naRuntimeError(struct Context* c, char* msg)
26
27     c->error = msg;
28     longjmp(c->jumpHandle, 1);
29 }
30
31 static int boolify(struct Context* ctx, naRef r)
32 {
33     if(IS_NUM(r)) return r.num != 0;
34     if(IS_NIL(r)) return 0;
35     if(IS_STR(r)) {
36         double d;
37         if(naStr_len(r) == 0) return 0;
38         if(naStr_tonum(r, &d)) return d != 0;
39         else return 1;
40     }
41     ERR(ctx, "non-scalar used in boolean context");
42     return 0;
43 }
44
45 static double numify(struct Context* ctx, naRef o)
46 {
47     double n;
48     if(IS_NUM(o)) return o.num;
49     else if(IS_NIL(o)) ERR(ctx, "nil used in numeric context");
50     else if(!IS_STR(o)) ERR(ctx, "non-scalar in numeric context");
51     else if(naStr_tonum(o, &n)) return n;
52     else ERR(ctx, "non-numeric string in numeric context");
53     return 0;
54 }
55
56 static naRef stringify(struct Context* ctx, naRef r)
57 {
58     if(IS_STR(r)) return r;
59     if(IS_NUM(r)) return naStr_fromnum(naNewString(ctx), r.num);
60     ERR(ctx, "non-scalar in string context");
61     return naNil();
62 }
63
64 static int checkVec(struct Context* ctx, naRef vec, naRef idx)
65 {
66     int i = (int)numify(ctx, idx);
67     if(i < 0) i += naVec_size(vec);
68     if(i < 0 || i >= naVec_size(vec)) ERR(ctx, "vector index out of bounds");
69     return i;
70 }
71
72 static int checkStr(struct Context* ctx, naRef str, naRef idx)
73 {
74     int i = (int)numify(ctx, idx);
75     if(i < 0) i += naStr_len(str);
76     if(i < 0 || i >= naStr_len(str)) ERR(ctx, "string index out of bounds");
77     return i;
78 }
79
80 static naRef containerGet(struct Context* ctx, naRef box, naRef key)
81 {
82     naRef result = naNil();
83     if(!IS_SCALAR(key)) ERR(ctx, "container index not scalar");
84     if(IS_HASH(box)) {
85         if(!naHash_get(box, key, &result))
86             ERR(ctx, "undefined value in container");
87     } else if(IS_VEC(box)) {
88         result = naVec_get(box, checkVec(ctx, box, key));
89     } else if(IS_STR(box)) {
90         result = naNum((unsigned char)naStr_data(box)[checkStr(ctx, box, key)]);
91     } else {
92         ERR(ctx, "extract from non-container");
93     }
94     return result;
95 }
96
97 static void containerSet(struct Context* ctx, naRef box, naRef key, naRef val)
98 {
99     if(!IS_SCALAR(key))   ERR(ctx, "container index not scalar");
100     else if(IS_HASH(box)) naHash_set(box, key, val);
101     else if(IS_VEC(box))  naVec_set(box, checkVec(ctx, box, key), val);
102     else if(IS_STR(box)) {
103         if(box.ref.ptr.str->hashcode)
104             ERR(ctx, "cannot change immutable string");
105         naStr_data(box)[checkStr(ctx, box, key)] = (char)numify(ctx, val);
106     } else ERR(ctx, "insert into non-container");
107 }
108
109 static void initTemps(struct Context* c)
110 {
111     c->tempsz = 4;
112     c->temps = naAlloc(c->tempsz * sizeof(struct naObj*));
113     c->ntemps = 0;
114 }
115
116 static void initContext(struct Context* c)
117 {
118     int i;
119     c->fTop = c->opTop = c->markTop = 0;
120     for(i=0; i<NUM_NASAL_TYPES; i++)
121         c->nfree[i] = 0;
122
123     if(c->tempsz > 32) {
124         naFree(c->temps);
125         initTemps(c);
126     }
127
128     c->callParent = 0;
129     c->callChild = 0;
130     c->dieArg = naNil();
131     c->error = 0;
132 }
133
134 static void initGlobals()
135 {
136     int i;
137     struct Context* c;
138     globals = (struct Globals*)naAlloc(sizeof(struct Globals));
139     naBZero(globals, sizeof(struct Globals));
140
141     globals->sem = naNewSem();
142     globals->lock = naNewLock();
143
144     globals->allocCount = 256; // reasonable starting value
145     for(i=0; i<NUM_NASAL_TYPES; i++)
146         naGC_init(&(globals->pools[i]), i);
147     globals->deadsz = 256;
148     globals->ndead = 0;
149     globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz);
150
151     // Initialize a single context
152     globals->freeContexts = 0;
153     globals->allContexts = 0;
154     c = naNewContext();
155
156     globals->symbols = naNewHash(c);
157     globals->save = naNewVector(c);
158
159     // Cache pre-calculated "me", "arg" and "parents" scalars
160     globals->meRef = naInternSymbol(naStr_fromdata(naNewString(c), "me", 2));
161     globals->argRef = naInternSymbol(naStr_fromdata(naNewString(c), "arg", 3));
162     globals->parentsRef = naInternSymbol(naStr_fromdata(naNewString(c), "parents", 7));
163
164     naFreeContext(c);
165 }
166
167 struct Context* naNewContext()
168 {
169     struct Context* c;
170     if(globals == 0)
171         initGlobals();
172
173     LOCK();
174     c = globals->freeContexts;
175     if(c) {
176         globals->freeContexts = c->nextFree;
177         c->nextFree = 0;
178         UNLOCK();
179         initContext(c);
180     } else {
181         UNLOCK();
182         c = (struct Context*)naAlloc(sizeof(struct Context));
183         initTemps(c);
184         initContext(c);
185         LOCK();
186         c->nextAll = globals->allContexts;
187         c->nextFree = 0;
188         globals->allContexts = c;
189         UNLOCK();
190     }
191     return c;
192 }
193
194 void naFreeContext(struct Context* c)
195 {
196     c->ntemps = 0;
197     LOCK();
198     c->nextFree = globals->freeContexts;
199     globals->freeContexts = c;
200     UNLOCK();
201 }
202
203 #if !defined(sgi)
204 # define PUSH(r) do { \
205     if(ctx->opTop >= MAX_STACK_DEPTH) ERR(ctx, "stack overflow"); \
206     ctx->opStack[ctx->opTop++] = r; \
207     } while(0);
208 #else
209 # define PUSH(r)  _PUSH((ctx), (r))
210 void _PUSH(struct Context* ctx, naRef r) {
211    if(ctx->opTop >= MAX_STACK_DEPTH) ERR(ctx, "stack overflow");
212     ctx->opStack[ctx->opTop++] = r;
213 }
214 #endif
215
216 static void setupArgs(naContext ctx, struct Frame* f, naRef* args, int nargs)
217 {
218     int i;
219     struct naCode* c = f->func.ref.ptr.func->code.ref.ptr.code;
220
221     // Set the argument symbols, and put any remaining args in a vector
222     if(nargs < c->nArgs) ERR(ctx, "not enough arguments to function call");
223     for(i=0; i<c->nArgs; i++)
224         naHash_newsym(f->locals.ref.ptr.hash,
225                       &c->constants[c->argSyms[i]], &args[i]);
226     args += c->nArgs;
227     nargs -= c->nArgs;
228     for(i=0; i<c->nOptArgs; i++, nargs--) {
229         naRef val = nargs > 0 ? args[i] : c->constants[c->optArgVals[i]];
230         if(IS_CODE(val))
231             val = bindFunction(ctx, &ctx->fStack[ctx->fTop-2], val);
232         naHash_newsym(f->locals.ref.ptr.hash, &c->constants[c->optArgSyms[i]], 
233                       &val);
234     }
235     args += c->nOptArgs;
236     if(c->needArgVector || nargs > 0) {
237         naRef argsv = naNewVector(ctx);
238         naVec_setsize(argsv, nargs > 0 ? nargs : 0);
239         for(i=0; i<nargs; i++)
240             argsv.ref.ptr.vec->rec->array[i] = *args++;
241         naHash_newsym(f->locals.ref.ptr.hash, &c->restArgSym, &argsv);
242     }
243 }
244
245 struct Frame* setupFuncall(struct Context* ctx, int nargs, int mcall, int tail)
246 {
247     naRef *frame;
248     struct Frame* f;
249     
250     DBG(printf("setupFuncall(nargs:%d, mcall:%d)\n", nargs, mcall);)
251
252     frame = &ctx->opStack[ctx->opTop - nargs - 1];
253     if(!IS_FUNC(frame[0]))
254         ERR(ctx, "function/method call invoked on uncallable object");
255
256     // Just do native calls right here, and don't touch the stack
257     // frames; return the current one (unless it's a tail call!).
258     if(frame[0].ref.ptr.func->code.ref.ptr.obj->type == T_CCODE) {
259         naRef obj = mcall ? frame[-1] : naNil();
260         naCFunction fp = frame[0].ref.ptr.func->code.ref.ptr.ccode->fptr;
261         naRef result = (*fp)(ctx, obj, nargs, frame + 1);
262         ctx->opTop -= nargs + 1 + mcall;
263         PUSH(result);
264         return &(ctx->fStack[ctx->fTop-1]);
265     }
266     
267     if(tail) ctx->fTop--;
268     else if(ctx->fTop >= MAX_RECURSION) ERR(ctx, "call stack overflow");
269
270     // Note: assign nil first, otherwise the naNew() can cause a GC,
271     // which will now (after fTop++) see the *old* reference as a
272     // markable value!
273     f = &(ctx->fStack[ctx->fTop++]);
274     f->locals = f->func = naNil();
275     f->locals = naNewHash(ctx);
276     f->func = frame[0];
277     f->ip = 0;
278     f->bp = ctx->opTop - (nargs + 1 + mcall);
279
280     if(mcall)
281         naHash_set(f->locals, globals->meRef, frame[-1]);
282
283     setupArgs(ctx, f, frame+1, nargs);
284
285     ctx->opTop = f->bp; // Pop the stack last, to avoid GC lossage
286     DBG(printf("Entering frame %d with %d args\n", ctx->fTop-1, nargs);)
287     return f;
288 }
289
290 static naRef evalAndOr(struct Context* ctx, int op, naRef ra, naRef rb)
291 {
292     int a = boolify(ctx, ra);
293     int b = boolify(ctx, rb);
294     int result;
295     if(op == OP_AND) result = a && b ? 1 : 0;
296     else             result = a || b ? 1 : 0;
297     return naNum(result);
298 }
299
300 static naRef evalEquality(int op, naRef ra, naRef rb)
301 {
302     int result = naEqual(ra, rb);
303     return naNum((op==OP_EQ) ? result : !result);
304 }
305
306 // When a code object comes out of the constant pool and shows up on
307 // the stack, it needs to be bound with the lexical context.
308 static naRef bindFunction(struct Context* ctx, struct Frame* f, naRef code)
309 {
310     naRef result = naNewFunc(ctx, code);
311     result.ref.ptr.func->namespace = f->locals;
312     result.ref.ptr.func->next = f->func;
313     return result;
314 }
315
316 static int getClosure(struct naFunc* c, naRef sym, naRef* result)
317 {
318     while(c) {
319         if(naHash_get(c->namespace, sym, result)) return 1;
320         c = c->next.ref.ptr.func;
321     }
322     return 0;
323 }
324
325 static naRef getLocal2(struct Context* ctx, struct Frame* f, naRef sym)
326 {
327     naRef result;
328     if(!naHash_get(f->locals, sym, &result))
329         if(!getClosure(f->func.ref.ptr.func, sym, &result))
330             ERR(ctx, "undefined symbol");
331     return result;
332 }
333
334 static void getLocal(struct Context* ctx, struct Frame* f,
335                      naRef* sym, naRef* out)
336 {
337     struct naFunc* func;
338     struct naStr* str = sym->ref.ptr.str;
339     if(naHash_sym(f->locals.ref.ptr.hash, str, out))
340         return;
341     func = f->func.ref.ptr.func;
342     while(func && func->namespace.ref.ptr.hash) {
343         if(naHash_sym(func->namespace.ref.ptr.hash, str, out))
344             return;
345         func = func->next.ref.ptr.func;
346     }
347     // Now do it again using the more general naHash_get().  This will
348     // only be necessary if something has created the value in the
349     // namespace using the more generic hash syntax
350     // (e.g. namespace["symbol"] and not namespace.symbol).
351     *out = getLocal2(ctx, f, *sym);
352 }
353
354 static int setClosure(naRef func, naRef sym, naRef val)
355 {
356     struct naFunc* c = func.ref.ptr.func;
357     if(c == 0) { return 0; }
358     else if(naHash_tryset(c->namespace, sym, val)) { return 1; }
359     else { return setClosure(c->next, sym, val); }
360 }
361
362 static naRef setSymbol(struct Frame* f, naRef sym, naRef val)
363 {
364     // Try the locals first, if not already there try the closures in
365     // order.  Finally put it in the locals if nothing matched.
366     if(!naHash_tryset(f->locals, sym, val))
367         if(!setClosure(f->func, sym, val))
368             naHash_set(f->locals, sym, val);
369     return val;
370 }
371
372 // Recursively descend into the parents lists
373 static int getMember(struct Context* ctx, naRef obj, naRef fld,
374                      naRef* result, int count)
375 {
376     naRef p;
377     if(--count < 0) ERR(ctx, "too many parents");
378     if(!IS_HASH(obj)) ERR(ctx, "non-objects have no members");
379     if(naHash_get(obj, fld, result)) {
380         return 1;
381     } else if(naHash_get(obj, globals->parentsRef, &p)) {
382         if(IS_VEC(p)) {
383             int i;
384             struct VecRec* v = p.ref.ptr.vec->rec;
385             for(i=0; i<v->size; i++)
386                 if(getMember(ctx, v->array[i], fld, result, count))
387                     return 1;
388         } else
389             ERR(ctx, "parents field not vector");
390     }
391     return 0;
392 }
393
394 // OP_EACH works like a vector get, except that it leaves the vector
395 // and index on the stack, increments the index after use, and pops
396 // the arguments and pushes a nil if the index is beyond the end.
397 static void evalEach(struct Context* ctx, int useIndex)
398 {
399     int idx = (int)(ctx->opStack[ctx->opTop-1].num);
400     naRef vec = ctx->opStack[ctx->opTop-2];
401     if(!IS_VEC(vec)) naRuntimeError(ctx, "foreach enumeration of non-vector");
402     if(!vec.ref.ptr.vec->rec || idx >= vec.ref.ptr.vec->rec->size) {
403         ctx->opTop -= 2; // pop two values
404         PUSH(naNil());
405         return;
406     }
407     ctx->opStack[ctx->opTop-1].num = idx+1; // modify in place
408     PUSH(useIndex ? naNum(idx) : naVec_get(vec, idx));
409 }
410
411 #define ARG() cd->byteCode[f->ip++]
412 #define CONSTARG() cd->constants[ARG()]
413 #define POP() ctx->opStack[--ctx->opTop]
414 #define STK(n) (ctx->opStack[ctx->opTop-(n)])
415 #define FIXFRAME() f = &(ctx->fStack[ctx->fTop-1]); \
416                    cd = f->func.ref.ptr.func->code.ref.ptr.code;
417 static naRef run(struct Context* ctx)
418 {
419     struct Frame* f;
420     struct naCode* cd;
421     int op, arg;
422     naRef a, b, c;
423
424     FIXFRAME();
425
426     while(1) {
427         op = cd->byteCode[f->ip++];
428         DBG(printf("Stack Depth: %d\n", ctx->opTop));
429         DBG(printOpDEBUG(f->ip-1, op));
430         switch(op) {
431         case OP_POP:
432             ctx->opTop--;
433             break;
434         case OP_DUP:
435             PUSH(ctx->opStack[ctx->opTop-1]);
436             break;
437         case OP_DUP2:
438             PUSH(ctx->opStack[ctx->opTop-2]);
439             PUSH(ctx->opStack[ctx->opTop-2]);
440             break;
441         case OP_XCHG:
442             a = STK(1); STK(1) = STK(2); STK(2) = a;
443             break;
444
445 #define BINOP(expr) do { \
446     double l = IS_NUM(STK(2)) ? STK(2).num : numify(ctx, STK(2)); \
447     double r = IS_NUM(STK(1)) ? STK(1).num : numify(ctx, STK(1)); \
448     STK(2).ref.reftag = ~NASAL_REFTAG; \
449     STK(2).num = expr; \
450     ctx->opTop--; } while(0)
451
452         case OP_PLUS:  BINOP(l + r);         break;
453         case OP_MINUS: BINOP(l - r);         break;
454         case OP_MUL:   BINOP(l * r);         break;
455         case OP_DIV:   BINOP(l / r);         break;
456         case OP_LT:    BINOP(l <  r ? 1 : 0); break;
457         case OP_LTE:   BINOP(l <= r ? 1 : 0); break;
458         case OP_GT:    BINOP(l >  r ? 1 : 0); break;
459         case OP_GTE:   BINOP(l >= r ? 1 : 0); break;
460
461 #undef BINOP
462
463         case OP_EQ: case OP_NEQ:
464             STK(2) = evalEquality(op, STK(2), STK(1));
465             ctx->opTop--;
466             break;
467         case OP_AND: case OP_OR:
468             STK(2) = evalAndOr(ctx, op, STK(2), STK(1));
469             ctx->opTop--;
470             break;
471         case OP_CAT:
472             // stringify can call the GC, so don't take stuff of the stack!
473             a = stringify(ctx, ctx->opStack[ctx->opTop-1]);
474             b = stringify(ctx, ctx->opStack[ctx->opTop-2]);
475             c = naStr_concat(naNewString(ctx), b, a);
476             ctx->opTop -= 2;
477             PUSH(c);
478             break;
479         case OP_NEG:
480             STK(1) = naNum(-numify(ctx, STK(1)));
481             break;
482         case OP_NOT:
483             STK(1) = naNum(boolify(ctx, STK(1)) ? 0 : 1);
484             break;
485         case OP_PUSHCONST:
486             a = CONSTARG();
487             if(IS_CODE(a)) a = bindFunction(ctx, f, a);
488             PUSH(a);
489             break;
490         case OP_PUSHONE:
491             PUSH(naNum(1));
492             break;
493         case OP_PUSHZERO:
494             PUSH(naNum(0));
495             break;
496         case OP_PUSHNIL:
497             PUSH(naNil());
498             break;
499         case OP_NEWVEC:
500             PUSH(naNewVector(ctx));
501             break;
502         case OP_VAPPEND:
503             naVec_append(STK(2), STK(1));
504             ctx->opTop--;
505             break;
506         case OP_NEWHASH:
507             PUSH(naNewHash(ctx));
508             break;
509         case OP_HAPPEND:
510             naHash_set(STK(3), STK(2), STK(1));
511             ctx->opTop -= 2;
512             break;
513         case OP_LOCAL:
514             a = CONSTARG();
515             getLocal(ctx, f, &a, &b);
516             PUSH(b);
517             break;
518         case OP_SETSYM:
519             STK(2) = setSymbol(f, STK(2), STK(1));
520             ctx->opTop--;
521             break;
522         case OP_SETLOCAL:
523             naHash_set(f->locals, STK(2), STK(1));
524             STK(2) = STK(1); // FIXME: reverse order of arguments instead!
525             ctx->opTop--;
526             break;
527         case OP_MEMBER:
528             if(!getMember(ctx, STK(1), CONSTARG(), &STK(1), 64))
529                 ERR(ctx, "no such member");
530             break;
531         case OP_SETMEMBER:
532             if(!IS_HASH(STK(3))) ERR(ctx, "non-objects have no members");
533             naHash_set(STK(3), STK(2), STK(1));
534             STK(3) = STK(1); // FIXME: fix arg order instead
535             ctx->opTop -= 2;
536             break;
537         case OP_INSERT:
538             containerSet(ctx, STK(3), STK(2), STK(1));
539             STK(3) = STK(1); // FIXME: codegen order again...
540             ctx->opTop -= 2;
541             break;
542         case OP_EXTRACT:
543             STK(2) = containerGet(ctx, STK(2), STK(1));
544             ctx->opTop--;
545             break;
546         case OP_JMPLOOP:
547             // Identical to JMP, except for locking
548             naCheckBottleneck();
549             f->ip = cd->byteCode[f->ip];
550             DBG(printf("   [Jump to: %d]\n", f->ip);)
551             break;
552         case OP_JMP:
553             f->ip = cd->byteCode[f->ip];
554             DBG(printf("   [Jump to: %d]\n", f->ip);)
555             break;
556         case OP_JIFNIL:
557             arg = ARG();
558             if(IS_NIL(STK(1))) {
559                 ctx->opTop--; // Pops **ONLY** if it's nil!
560                 f->ip = arg;
561                 DBG(printf("   [Jump to: %d]\n", f->ip);)
562             }
563             break;
564         case OP_JIFNOT:
565             arg = ARG();
566             if(!boolify(ctx, POP())) {
567                 f->ip = arg;
568                 DBG(printf("   [Jump to: %d]\n", f->ip);)
569             }
570             break;
571         case OP_FCALL:
572             f = setupFuncall(ctx, ARG(), 0, 0);
573             cd = f->func.ref.ptr.func->code.ref.ptr.code;
574             break;
575         case OP_FTAIL:
576             f = setupFuncall(ctx, ARG(), 0, 1);
577             cd = f->func.ref.ptr.func->code.ref.ptr.code;
578             break;
579         case OP_MCALL:
580             f = setupFuncall(ctx, ARG(), 1, 0);
581             cd = f->func.ref.ptr.func->code.ref.ptr.code;
582             break;
583         case OP_MTAIL:
584             f = setupFuncall(ctx, ARG(), 1, 1);
585             cd = f->func.ref.ptr.func->code.ref.ptr.code;
586             break;
587         case OP_RETURN:
588             a = STK(1);
589             if(--ctx->fTop <= 0) return a;
590             ctx->opTop = f->bp + 1; // restore the correct opstack frame!
591             STK(1) = a;
592             FIXFRAME();
593             break;
594         case OP_EACH:
595             evalEach(ctx, 0);
596             break;
597         case OP_INDEX:
598             evalEach(ctx, 1);
599             break;
600         case OP_MARK: // save stack state (e.g. "setjmp")
601             if(ctx->markTop >= MAX_MARK_DEPTH)
602                 naRuntimeError(ctx, "mark stack overflow");
603             ctx->markStack[ctx->markTop++] = ctx->opTop;
604             break;
605         case OP_UNMARK: // pop stack state set by mark
606             ctx->markTop--;
607             break;
608         case OP_BREAK: // restore stack state (FOLLOW WITH JMP!)
609             ctx->opTop = ctx->markStack[--ctx->markTop];
610             break;
611         default:
612             ERR(ctx, "BUG: bad opcode");
613         }
614         ctx->ntemps = 0; // reset GC temp vector
615         DBG(printStackDEBUG(ctx);)
616     }
617     return naNil(); // unreachable
618 }
619 #undef POP
620 #undef CONSTARG
621 #undef STK
622 #undef FIXFRAME
623
624 void naSave(struct Context* ctx, naRef obj)
625 {
626     naVec_append(globals->save, obj);
627 }
628
629 // FIXME: handle ctx->callParent
630 int naStackDepth(struct Context* ctx)
631 {
632     return ctx->fTop;
633 }
634
635 // FIXME: handle ctx->callParent
636 int naGetLine(struct Context* ctx, int frame)
637 {
638     struct Frame* f = &ctx->fStack[ctx->fTop-1-frame];
639     naRef func = f->func;
640     int ip = f->ip;
641     if(IS_FUNC(func) && IS_CODE(func.ref.ptr.func->code)) {
642         struct naCode* c = func.ref.ptr.func->code.ref.ptr.code;
643         unsigned short* p = c->lineIps + c->nLines - 2;
644         while(p >= c->lineIps && p[0] > ip)
645             p -= 2;
646         return p[1];
647     }
648     return -1;
649 }
650
651 // FIXME: handle ctx->callParent
652 naRef naGetSourceFile(struct Context* ctx, int frame)
653 {
654     naRef f = ctx->fStack[ctx->fTop-1-frame].func;
655     f = f.ref.ptr.func->code;
656     return f.ref.ptr.code->srcFile;
657 }
658
659 char* naGetError(struct Context* ctx)
660 {
661     if(IS_STR(ctx->dieArg))
662         return (char*)ctx->dieArg.ref.ptr.str->data;
663     return ctx->error;
664 }
665
666 naRef naBindFunction(naContext ctx, naRef code, naRef closure)
667 {
668     naRef func = naNewFunc(ctx, code);
669     func.ref.ptr.func->namespace = closure;
670     func.ref.ptr.func->next = naNil();
671     return func;
672 }
673
674 naRef naBindToContext(naContext ctx, naRef code)
675 {
676     naRef func = naNewFunc(ctx, code);
677     struct Frame* f = &ctx->fStack[ctx->fTop-1];
678     func.ref.ptr.func->namespace = f->locals;
679     func.ref.ptr.func->next = f->func;
680     return func;
681 }
682
683 naRef naCall(naContext ctx, naRef func, int argc, naRef* args,
684              naRef obj, naRef locals)
685 {
686     int i;
687     naRef result;
688     if(!ctx->callParent) naModLock(ctx);
689
690     // We might have to allocate objects, which can call the GC.  But
691     // the call isn't on the Nasal stack yet, so the GC won't find our
692     // C-space arguments.
693     naTempSave(ctx, func);
694     for(i=0; i<argc; i++)
695         naTempSave(ctx, args[i]);
696     naTempSave(ctx, obj);
697     naTempSave(ctx, locals);
698
699     if(IS_CCODE(func.ref.ptr.func->code)) {
700         naCFunction fp = func.ref.ptr.func->code.ref.ptr.ccode->fptr;
701         result = (*fp)(ctx, obj, argc, args);
702         if(!ctx->callParent) naModUnlock(ctx);
703         return result;
704     }
705
706     if(IS_NIL(locals))
707         locals = naNewHash(ctx);
708     if(!IS_FUNC(func))
709         func = naNewFunc(ctx, func); // bind bare code objects
710     if(!IS_NIL(obj))
711         naHash_set(locals, globals->meRef, obj);
712
713     ctx->dieArg = naNil();
714
715     ctx->opTop = ctx->markTop = 0;
716     ctx->fTop = 1;
717     ctx->fStack[0].func = func;
718     ctx->fStack[0].locals = locals;
719     ctx->fStack[0].ip = 0;
720     ctx->fStack[0].bp = ctx->opTop;
721
722     setupArgs(ctx, ctx->fStack, args, argc);
723
724     // Return early if an error occurred.  It will be visible to the
725     // caller via naGetError().
726     ctx->error = 0;
727     if(setjmp(ctx->jumpHandle)) {
728         if(!ctx->callParent) naModUnlock(ctx);
729         return naNil();
730     }
731
732     result = run(ctx);
733     if(!ctx->callParent) naModUnlock(ctx);
734     return result;
735 }
736