]> git.mxchange.org Git - simgear.git/blob - simgear/nasal/code.c
Cygwin fixes.
[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 #define PUSH(r) do { \
204     if(ctx->opTop >= MAX_STACK_DEPTH) ERR(ctx, "stack overflow"); \
205     ctx->opStack[ctx->opTop++] = r; \
206     } while(0)
207
208 static void setupArgs(naContext ctx, struct Frame* f, naRef* args, int nargs)
209 {
210     int i;
211     struct naCode* c = f->func.ref.ptr.func->code.ref.ptr.code;
212
213     // Set the argument symbols, and put any remaining args in a vector
214     if(nargs < c->nArgs) ERR(ctx, "not enough arguments to function call");
215     for(i=0; i<c->nArgs; i++)
216         naHash_newsym(f->locals.ref.ptr.hash,
217                       &c->constants[c->argSyms[i]], &args[i]);
218     args += c->nArgs;
219     nargs -= c->nArgs;
220     for(i=0; i<c->nOptArgs; i++, nargs--) {
221         naRef val = nargs > 0 ? args[i] : c->constants[c->optArgVals[i]];
222         if(IS_CODE(val))
223             val = bindFunction(ctx, &ctx->fStack[ctx->fTop-2], val);
224         naHash_newsym(f->locals.ref.ptr.hash, &c->constants[c->optArgSyms[i]], 
225                       &val);
226     }
227     args += c->nOptArgs;
228     if(c->needArgVector || nargs > 0) {
229         naRef argsv = naNewVector(ctx);
230         naVec_setsize(argsv, nargs > 0 ? nargs : 0);
231         for(i=0; i<nargs; i++)
232             argsv.ref.ptr.vec->rec->array[i] = *args++;
233         naHash_newsym(f->locals.ref.ptr.hash, &c->restArgSym, &argsv);
234     }
235 }
236
237 struct Frame* setupFuncall(struct Context* ctx, int nargs, int mcall, int tail)
238 {
239     naRef *frame;
240     struct Frame* f;
241     
242     DBG(printf("setupFuncall(nargs:%d, mcall:%d)\n", nargs, mcall);)
243
244     frame = &ctx->opStack[ctx->opTop - nargs - 1];
245     if(!IS_FUNC(frame[0]))
246         ERR(ctx, "function/method call invoked on uncallable object");
247
248     // Just do native calls right here, and don't touch the stack
249     // frames; return the current one (unless it's a tail call!).
250     if(frame[0].ref.ptr.func->code.ref.ptr.obj->type == T_CCODE) {
251         naRef obj = mcall ? frame[-1] : naNil();
252         naCFunction fp = frame[0].ref.ptr.func->code.ref.ptr.ccode->fptr;
253         naRef result = (*fp)(ctx, obj, nargs, frame + 1);
254         ctx->opTop -= nargs + 1 + mcall;
255         PUSH(result);
256         return &(ctx->fStack[ctx->fTop-1]);
257     }
258     
259     if(tail) ctx->fTop--;
260     else if(ctx->fTop >= MAX_RECURSION) ERR(ctx, "call stack overflow");
261
262     // Note: assign nil first, otherwise the naNew() can cause a GC,
263     // which will now (after fTop++) see the *old* reference as a
264     // markable value!
265     f = &(ctx->fStack[ctx->fTop++]);
266     f->locals = f->func = naNil();
267     f->locals = naNewHash(ctx);
268     f->func = frame[0];
269     f->ip = 0;
270     f->bp = ctx->opTop - (nargs + 1 + mcall);
271
272     if(mcall)
273         naHash_set(f->locals, globals->meRef, frame[-1]);
274
275     setupArgs(ctx, f, frame+1, nargs);
276
277     ctx->opTop = f->bp; // Pop the stack last, to avoid GC lossage
278     DBG(printf("Entering frame %d with %d args\n", ctx->fTop-1, nargs);)
279     return f;
280 }
281
282 static naRef evalAndOr(struct Context* ctx, int op, naRef ra, naRef rb)
283 {
284     int a = boolify(ctx, ra);
285     int b = boolify(ctx, rb);
286     int result;
287     if(op == OP_AND) result = a && b ? 1 : 0;
288     else             result = a || b ? 1 : 0;
289     return naNum(result);
290 }
291
292 static naRef evalEquality(int op, naRef ra, naRef rb)
293 {
294     int result = naEqual(ra, rb);
295     return naNum((op==OP_EQ) ? result : !result);
296 }
297
298 // When a code object comes out of the constant pool and shows up on
299 // the stack, it needs to be bound with the lexical context.
300 static naRef bindFunction(struct Context* ctx, struct Frame* f, naRef code)
301 {
302     naRef result = naNewFunc(ctx, code);
303     result.ref.ptr.func->namespace = f->locals;
304     result.ref.ptr.func->next = f->func;
305     return result;
306 }
307
308 static int getClosure(struct naFunc* c, naRef sym, naRef* result)
309 {
310     while(c) {
311         if(naHash_get(c->namespace, sym, result)) return 1;
312         c = c->next.ref.ptr.func;
313     }
314     return 0;
315 }
316
317 static naRef getLocal2(struct Context* ctx, struct Frame* f, naRef sym)
318 {
319     naRef result;
320     if(!naHash_get(f->locals, sym, &result))
321         if(!getClosure(f->func.ref.ptr.func, sym, &result))
322             ERR(ctx, "undefined symbol");
323     return result;
324 }
325
326 static void getLocal(struct Context* ctx, struct Frame* f,
327                      naRef* sym, naRef* out)
328 {
329     struct naFunc* func;
330     struct naStr* str = sym->ref.ptr.str;
331     if(naHash_sym(f->locals.ref.ptr.hash, str, out))
332         return;
333     func = f->func.ref.ptr.func;
334     while(func && func->namespace.ref.ptr.hash) {
335         if(naHash_sym(func->namespace.ref.ptr.hash, str, out))
336             return;
337         func = func->next.ref.ptr.func;
338     }
339     // Now do it again using the more general naHash_get().  This will
340     // only be necessary if something has created the value in the
341     // namespace using the more generic hash syntax
342     // (e.g. namespace["symbol"] and not namespace.symbol).
343     *out = getLocal2(ctx, f, *sym);
344 }
345
346 static int setClosure(naRef func, naRef sym, naRef val)
347 {
348     struct naFunc* c = func.ref.ptr.func;
349     if(c == 0) { return 0; }
350     else if(naHash_tryset(c->namespace, sym, val)) { return 1; }
351     else { return setClosure(c->next, sym, val); }
352 }
353
354 static naRef setSymbol(struct Frame* f, naRef sym, naRef val)
355 {
356     // Try the locals first, if not already there try the closures in
357     // order.  Finally put it in the locals if nothing matched.
358     if(!naHash_tryset(f->locals, sym, val))
359         if(!setClosure(f->func, sym, val))
360             naHash_set(f->locals, sym, val);
361     return val;
362 }
363
364 // Recursively descend into the parents lists
365 static int getMember(struct Context* ctx, naRef obj, naRef fld,
366                      naRef* result, int count)
367 {
368     naRef p;
369     if(--count < 0) ERR(ctx, "too many parents");
370     if(!IS_HASH(obj)) ERR(ctx, "non-objects have no members");
371     if(naHash_get(obj, fld, result)) {
372         return 1;
373     } else if(naHash_get(obj, globals->parentsRef, &p)) {
374         if(IS_VEC(p)) {
375             int i;
376             struct VecRec* v = p.ref.ptr.vec->rec;
377             for(i=0; i<v->size; i++)
378                 if(getMember(ctx, v->array[i], fld, result, count))
379                     return 1;
380         } else
381             ERR(ctx, "parents field not vector");
382     }
383     return 0;
384 }
385
386 // OP_EACH works like a vector get, except that it leaves the vector
387 // and index on the stack, increments the index after use, and pops
388 // the arguments and pushes a nil if the index is beyond the end.
389 static void evalEach(struct Context* ctx, int useIndex)
390 {
391     int idx = (int)(ctx->opStack[ctx->opTop-1].num);
392     naRef vec = ctx->opStack[ctx->opTop-2];
393     if(!IS_VEC(vec)) naRuntimeError(ctx, "foreach enumeration of non-vector");
394     if(!vec.ref.ptr.vec->rec || idx >= vec.ref.ptr.vec->rec->size) {
395         ctx->opTop -= 2; // pop two values
396         PUSH(naNil());
397         return;
398     }
399     ctx->opStack[ctx->opTop-1].num = idx+1; // modify in place
400     PUSH(useIndex ? naNum(idx) : naVec_get(vec, idx));
401 }
402
403 #define ARG() cd->byteCode[f->ip++]
404 #define CONSTARG() cd->constants[ARG()]
405 #define POP() ctx->opStack[--ctx->opTop]
406 #define STK(n) (ctx->opStack[ctx->opTop-(n)])
407 #define FIXFRAME() f = &(ctx->fStack[ctx->fTop-1]); \
408                    cd = f->func.ref.ptr.func->code.ref.ptr.code;
409 static naRef run(struct Context* ctx)
410 {
411     struct Frame* f;
412     struct naCode* cd;
413     int op, arg;
414     naRef a, b, c;
415
416     FIXFRAME();
417
418     while(1) {
419         op = cd->byteCode[f->ip++];
420         DBG(printf("Stack Depth: %d\n", ctx->opTop));
421         DBG(printOpDEBUG(f->ip-1, op));
422         switch(op) {
423         case OP_POP:
424             ctx->opTop--;
425             break;
426         case OP_DUP:
427             PUSH(ctx->opStack[ctx->opTop-1]);
428             break;
429         case OP_DUP2:
430             PUSH(ctx->opStack[ctx->opTop-2]);
431             PUSH(ctx->opStack[ctx->opTop-2]);
432             break;
433         case OP_XCHG:
434             a = STK(1); STK(1) = STK(2); STK(2) = a;
435             break;
436
437 #define BINOP(expr) do { \
438     double l = IS_NUM(STK(2)) ? STK(2).num : numify(ctx, STK(2)); \
439     double r = IS_NUM(STK(1)) ? STK(1).num : numify(ctx, STK(1)); \
440     STK(2).ref.reftag = ~NASAL_REFTAG; \
441     STK(2).num = expr; \
442     ctx->opTop--; } while(0)
443
444         case OP_PLUS:  BINOP(l + r);         break;
445         case OP_MINUS: BINOP(l - r);         break;
446         case OP_MUL:   BINOP(l * r);         break;
447         case OP_DIV:   BINOP(l / r);         break;
448         case OP_LT:    BINOP(l <  r ? 1 : 0); break;
449         case OP_LTE:   BINOP(l <= r ? 1 : 0); break;
450         case OP_GT:    BINOP(l >  r ? 1 : 0); break;
451         case OP_GTE:   BINOP(l >= r ? 1 : 0); break;
452
453 #undef BINOP
454
455         case OP_EQ: case OP_NEQ:
456             STK(2) = evalEquality(op, STK(2), STK(1));
457             ctx->opTop--;
458             break;
459         case OP_AND: case OP_OR:
460             STK(2) = evalAndOr(ctx, op, STK(2), STK(1));
461             ctx->opTop--;
462             break;
463         case OP_CAT:
464             // stringify can call the GC, so don't take stuff of the stack!
465             a = stringify(ctx, ctx->opStack[ctx->opTop-1]);
466             b = stringify(ctx, ctx->opStack[ctx->opTop-2]);
467             c = naStr_concat(naNewString(ctx), b, a);
468             ctx->opTop -= 2;
469             PUSH(c);
470             break;
471         case OP_NEG:
472             STK(1) = naNum(-numify(ctx, STK(1)));
473             break;
474         case OP_NOT:
475             STK(1) = naNum(boolify(ctx, STK(1)) ? 0 : 1);
476             break;
477         case OP_PUSHCONST:
478             a = CONSTARG();
479             if(IS_CODE(a)) a = bindFunction(ctx, f, a);
480             PUSH(a);
481             break;
482         case OP_PUSHONE:
483             PUSH(naNum(1));
484             break;
485         case OP_PUSHZERO:
486             PUSH(naNum(0));
487             break;
488         case OP_PUSHNIL:
489             PUSH(naNil());
490             break;
491         case OP_NEWVEC:
492             PUSH(naNewVector(ctx));
493             break;
494         case OP_VAPPEND:
495             naVec_append(STK(2), STK(1));
496             ctx->opTop--;
497             break;
498         case OP_NEWHASH:
499             PUSH(naNewHash(ctx));
500             break;
501         case OP_HAPPEND:
502             naHash_set(STK(3), STK(2), STK(1));
503             ctx->opTop -= 2;
504             break;
505         case OP_LOCAL:
506             a = CONSTARG();
507             getLocal(ctx, f, &a, &b);
508             PUSH(b);
509             break;
510         case OP_SETSYM:
511             STK(2) = setSymbol(f, STK(2), STK(1));
512             ctx->opTop--;
513             break;
514         case OP_SETLOCAL:
515             naHash_set(f->locals, STK(2), STK(1));
516             STK(2) = STK(1); // FIXME: reverse order of arguments instead!
517             ctx->opTop--;
518             break;
519         case OP_MEMBER:
520             if(!getMember(ctx, STK(1), CONSTARG(), &STK(1), 64))
521                 ERR(ctx, "no such member");
522             break;
523         case OP_SETMEMBER:
524             if(!IS_HASH(STK(3))) ERR(ctx, "non-objects have no members");
525             naHash_set(STK(3), STK(2), STK(1));
526             STK(3) = STK(1); // FIXME: fix arg order instead
527             ctx->opTop -= 2;
528             break;
529         case OP_INSERT:
530             containerSet(ctx, STK(3), STK(2), STK(1));
531             STK(3) = STK(1); // FIXME: codegen order again...
532             ctx->opTop -= 2;
533             break;
534         case OP_EXTRACT:
535             STK(2) = containerGet(ctx, STK(2), STK(1));
536             ctx->opTop--;
537             break;
538         case OP_JMPLOOP:
539             // Identical to JMP, except for locking
540             naCheckBottleneck();
541             f->ip = cd->byteCode[f->ip];
542             DBG(printf("   [Jump to: %d]\n", f->ip);)
543             break;
544         case OP_JMP:
545             f->ip = cd->byteCode[f->ip];
546             DBG(printf("   [Jump to: %d]\n", f->ip);)
547             break;
548         case OP_JIFNIL:
549             arg = ARG();
550             if(IS_NIL(STK(1))) {
551                 ctx->opTop--; // Pops **ONLY** if it's nil!
552                 f->ip = arg;
553                 DBG(printf("   [Jump to: %d]\n", f->ip);)
554             }
555             break;
556         case OP_JIFNOT:
557             arg = ARG();
558             if(!boolify(ctx, POP())) {
559                 f->ip = arg;
560                 DBG(printf("   [Jump to: %d]\n", f->ip);)
561             }
562             break;
563         case OP_FCALL:
564             f = setupFuncall(ctx, ARG(), 0, 0);
565             cd = f->func.ref.ptr.func->code.ref.ptr.code;
566             break;
567         case OP_FTAIL:
568             f = setupFuncall(ctx, ARG(), 0, 1);
569             cd = f->func.ref.ptr.func->code.ref.ptr.code;
570             break;
571         case OP_MCALL:
572             f = setupFuncall(ctx, ARG(), 1, 0);
573             cd = f->func.ref.ptr.func->code.ref.ptr.code;
574             break;
575         case OP_MTAIL:
576             f = setupFuncall(ctx, ARG(), 1, 1);
577             cd = f->func.ref.ptr.func->code.ref.ptr.code;
578             break;
579         case OP_RETURN:
580             a = STK(1);
581             if(--ctx->fTop <= 0) return a;
582             ctx->opTop = f->bp + 1; // restore the correct opstack frame!
583             STK(1) = a;
584             FIXFRAME();
585             break;
586         case OP_EACH:
587             evalEach(ctx, 0);
588             break;
589         case OP_INDEX:
590             evalEach(ctx, 1);
591             break;
592         case OP_MARK: // save stack state (e.g. "setjmp")
593             if(ctx->markTop >= MAX_MARK_DEPTH)
594                 naRuntimeError(ctx, "mark stack overflow");
595             ctx->markStack[ctx->markTop++] = ctx->opTop;
596             break;
597         case OP_UNMARK: // pop stack state set by mark
598             ctx->markTop--;
599             break;
600         case OP_BREAK: // restore stack state (FOLLOW WITH JMP!)
601             ctx->opTop = ctx->markStack[--ctx->markTop];
602             break;
603         default:
604             ERR(ctx, "BUG: bad opcode");
605         }
606         ctx->ntemps = 0; // reset GC temp vector
607         DBG(printStackDEBUG(ctx);)
608     }
609     return naNil(); // unreachable
610 }
611 #undef POP
612 #undef CONSTARG
613 #undef STK
614 #undef FIXFRAME
615
616 void naSave(struct Context* ctx, naRef obj)
617 {
618     naVec_append(globals->save, obj);
619 }
620
621 // FIXME: handle ctx->callParent
622 int naStackDepth(struct Context* ctx)
623 {
624     return ctx->fTop;
625 }
626
627 // FIXME: handle ctx->callParent
628 int naGetLine(struct Context* ctx, int frame)
629 {
630     struct Frame* f = &ctx->fStack[ctx->fTop-1-frame];
631     naRef func = f->func;
632     int ip = f->ip;
633     if(IS_FUNC(func) && IS_CODE(func.ref.ptr.func->code)) {
634         struct naCode* c = func.ref.ptr.func->code.ref.ptr.code;
635         unsigned short* p = c->lineIps + c->nLines - 2;
636         while(p >= c->lineIps && p[0] > ip)
637             p -= 2;
638         return p[1];
639     }
640     return -1;
641 }
642
643 // FIXME: handle ctx->callParent
644 naRef naGetSourceFile(struct Context* ctx, int frame)
645 {
646     naRef f = ctx->fStack[ctx->fTop-1-frame].func;
647     f = f.ref.ptr.func->code;
648     return f.ref.ptr.code->srcFile;
649 }
650
651 char* naGetError(struct Context* ctx)
652 {
653     if(IS_STR(ctx->dieArg))
654         return (char*)ctx->dieArg.ref.ptr.str->data;
655     return ctx->error;
656 }
657
658 naRef naBindFunction(naContext ctx, naRef code, naRef closure)
659 {
660     naRef func = naNewFunc(ctx, code);
661     func.ref.ptr.func->namespace = closure;
662     func.ref.ptr.func->next = naNil();
663     return func;
664 }
665
666 naRef naBindToContext(naContext ctx, naRef code)
667 {
668     naRef func = naNewFunc(ctx, code);
669     struct Frame* f = &ctx->fStack[ctx->fTop-1];
670     func.ref.ptr.func->namespace = f->locals;
671     func.ref.ptr.func->next = f->func;
672     return func;
673 }
674
675 naRef naCall(naContext ctx, naRef func, int argc, naRef* args,
676              naRef obj, naRef locals)
677 {
678     int i;
679     naRef result;
680     if(!ctx->callParent) naModLock(ctx);
681
682     // We might have to allocate objects, which can call the GC.  But
683     // the call isn't on the Nasal stack yet, so the GC won't find our
684     // C-space arguments.
685     naTempSave(ctx, func);
686     for(i=0; i<argc; i++)
687         naTempSave(ctx, args[i]);
688     naTempSave(ctx, obj);
689     naTempSave(ctx, locals);
690
691     if(IS_CCODE(func.ref.ptr.func->code)) {
692         naCFunction fp = func.ref.ptr.func->code.ref.ptr.ccode->fptr;
693         result = (*fp)(ctx, obj, argc, args);
694         if(!ctx->callParent) naModUnlock(ctx);
695         return result;
696     }
697
698     if(IS_NIL(locals))
699         locals = naNewHash(ctx);
700     if(!IS_FUNC(func))
701         func = naNewFunc(ctx, func); // bind bare code objects
702     if(!IS_NIL(obj))
703         naHash_set(locals, globals->meRef, obj);
704
705     ctx->dieArg = naNil();
706
707     ctx->opTop = ctx->markTop = 0;
708     ctx->fTop = 1;
709     ctx->fStack[0].func = func;
710     ctx->fStack[0].locals = locals;
711     ctx->fStack[0].ip = 0;
712     ctx->fStack[0].bp = ctx->opTop;
713
714     setupArgs(ctx, ctx->fStack, args, argc);
715
716     // Return early if an error occurred.  It will be visible to the
717     // caller via naGetError().
718     ctx->error = 0;
719     if(setjmp(ctx->jumpHandle)) {
720         if(!ctx->callParent) naModUnlock(ctx);
721         return naNil();
722     }
723
724     result = run(ctx);
725     if(!ctx->callParent) naModUnlock(ctx);
726     return result;
727 }
728