xref: /inferno-os/appl/lib/ecmascript/exec.b (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
1exec(ex: ref Exec, code: ref Code): Completion
2{
3	ssp := ex.sp;
4
5	r := estmt(ex, code, 0, code.npc);
6
7	if(r.kind == CThrow)
8		ex.sp = ssp;
9
10	if(ssp != ex.sp)
11		runtime(ex, InternalError, "internal error: exec stack not balanced");
12
13	if(r.lab != nil)
14		runtime(ex, InternalError, "internal error: label out of stack");
15	return r;
16}
17
18estmt(ex: ref Exec, code: ref Code, pc, epc: int): Completion
19{
20	e: ref Ref;
21	ev: ref Val;
22	k, apc, pc2, apc2, pc3, apc3, c: int;
23	lab: string;
24	labs: list of string;
25
26	osp := ex.sp;
27
28{
29	v : ref Val = nil;
30	k1 := CNormal;
31	while(pc < epc){
32		v1 : ref Val = nil;
33
34		labs = nil;
35		op := int code.ops[pc++];
36		while(op == Llabel){
37			(pc, c) = getconst(code.ops, pc);
38			labs = code.strs[c] :: labs;
39			op = int code.ops[pc++];
40		}
41		if(debug['e'] > 1)
42			print("estmt(pc %d, sp %d) %s\n", pc-1, ex.sp, tokname(op));
43		case op {
44		Lbreak =>
45			return (CBreak, v, nil);
46		Lcontinue =>
47			return (CContinue, v, nil);
48		Lbreaklab =>
49			(pc, c) = getconst(code.ops, pc);
50			return (CBreak, v, code.strs[c]);
51		Lcontinuelab =>
52			(pc, c) = getconst(code.ops, pc);
53			return (CContinue, v, code.strs[c]);
54		Lreturn =>
55			(pc, v) = eexpval(ex, code, pc, code.npc);
56			return (CReturn, v, nil);
57		'{' =>
58			(pc, apc) = getjmp(code.ops, pc);
59			(k1, v1, lab) = estmt(ex, code, pc, apc);
60			pc = apc;
61		Lif =>
62			(pc, apc) = getjmp(code.ops, pc);
63			(pc, ev) = eexpval(ex, code, pc, apc);
64			(pc, apc) = getjmp(code.ops, pc);
65			(pc2, apc2) = getjmp(code.ops, apc);
66			if(toBoolean(ex, ev) != false)
67				(k1, v1, lab) = estmt(ex, code, pc, apc);
68			else if(pc2 != apc2)
69				(k1, v1, lab) = estmt(ex, code, pc2, apc2);
70			pc = apc2;
71		Lwhile =>
72			(pc, apc) = getjmp(code.ops, pc);
73			(pc2, apc2) = getjmp(code.ops, apc);
74			for(;;){
75				(nil, ev) = eexpval(ex, code, pc, apc);
76				if(toBoolean(ex, ev) == false)
77					break;
78				(k, v1, lab) = estmt(ex, code, pc2, apc2);
79				if(v1 != nil)
80					v = v1;
81				if(k == CBreak || k == CContinue){
82					if(initlabs(lab, labs)){
83						if(k == CBreak)
84							break;
85						else
86							continue;
87					}
88					else
89						return (k, v1, lab);
90				}
91				if(k == CReturn || k == CThrow)
92					return (k, v1, nil);
93			}
94			pc = apc2;
95		Ldo =>
96			(pc, apc) = getjmp(code.ops, pc);
97			(pc2, apc2) = getjmp(code.ops, apc);
98			for(;;){
99				(k, v1, lab) = estmt(ex, code,  pc, apc);
100				if(v1 != nil)
101					v = v1;
102				if(k == CBreak || k == CContinue){
103					if(initlabs(lab, labs)){
104						if(k == CBreak)
105							break;
106						else
107							continue;
108					}
109					else
110						return (k, v1, lab);
111				}
112				if(k == CReturn || k == CThrow)
113					return (k, v1, nil);
114				(nil, ev) = eexpval(ex, code, pc2, apc2);
115				if(toBoolean(ex, ev) == false)
116					break;
117			}
118			pc = apc2;
119		Lfor or
120		Lforvar =>
121			(pc, apc) = getjmp(code.ops, pc);
122			(pc, nil) = eexpval(ex, code, pc, apc);
123			(pc, apc) = getjmp(code.ops, pc);
124			(pc2, apc2) = getjmp(code.ops, apc);
125			(pc3, apc3) = getjmp(code.ops, apc2);
126			for(;;){
127				(nil, e) = eexp(ex, code, pc, apc);
128				if(e != nil && toBoolean(ex, getValue(ex, e)) == false)
129					break;
130				(k, v1, lab) = estmt(ex, code, pc3, apc3);
131				if(v1 != nil)
132					v = v1;
133				if(k == CBreak || k == CContinue){
134					if(initlabs(lab, labs)){
135						if(k == CBreak)
136							break;
137						else
138							continue;
139					}
140					else
141						return (k, v1, lab);
142				}
143				if(k == CReturn || k == CThrow)
144					return (k, v1, nil);
145				eexpval(ex, code, pc2, apc2);
146			}
147			pc = apc3;
148		Lforin or
149		Lforvarin =>
150			(pc, apc) = getjmp(code.ops, pc);
151			(pc2, apc2) = getjmp(code.ops, apc);
152			(pc3, apc3) = getjmp(code.ops, apc2);
153			if(op == Lforvarin){
154				(nil, nil) = eexp(ex, code, pc, apc);
155				# during for only evaluate the id, not the initializer
156				apc = pc + 1;
157			}
158			(nil, ev) = eexpval(ex, code, pc2, apc2);
159			bo := toObject(ex, ev);
160
161			#
162			# note this won't enumerate host properties
163			#
164			enum:
165			for(o := bo; o != nil; o = o.prototype){
166				if(o.host != nil && o.host != me)
167					continue;
168				for(i := 0; i < len o.props; i++){
169					if(o.props[i] == nil
170					|| (o.props[i].attr & DontEnum)
171					|| propshadowed(bo, o, o.props[i].name))
172						continue;
173					(nil, e) = eexp(ex, code, pc, apc);
174					putValue(ex, e, strval(o.props[i].name));
175					(k, v1, lab) = estmt(ex, code, pc3, apc3);
176					if(v1 != nil)
177						v = v1;
178					if(k == CBreak || k == CContinue){
179						if(initlabs(lab, labs)){
180							if(k == CBreak)
181								break enum;
182							else
183								continue enum;
184						}
185						else
186							return (k, v1, lab);
187					}
188					if(k == CReturn || k == CThrow)
189						return (k, v1, nil);
190				}
191			}
192			pc = apc3;
193		Lwith =>
194			(pc, apc) = getjmp(code.ops, pc);
195			(pc, ev) = eexpval(ex, code, pc, apc);
196			pushscope(ex, toObject(ex, ev));
197			(pc, apc) = getjmp(code.ops, pc);
198			(k1, v1, lab) = estmt(ex, code, pc, apc);
199			popscope(ex);
200			pc = apc;
201		';' =>
202			;
203		Lvar =>
204			(pc, apc) = getjmp(code.ops, pc);
205			(pc, nil) = eexp(ex, code, pc, apc);
206		Lswitch =>
207			(pc, apc) = getjmp(code.ops, pc);
208			(pc, ev) = eexpval(ex, code, pc, apc);
209			(pc, apc) = getjmp(code.ops, pc);
210			(k1, v1, lab) = ecaseblk(ex, code, ev, pc, apc, labs);
211			pc = apc;
212		Lthrow =>
213			(pc, v) = eexpval(ex, code, pc, code.npc);
214			ex.error = toString(ex, v);
215			return (CThrow, v, nil);
216		Lprint =>
217			(pc, v1) = eexpval(ex, code, pc, code.npc);
218			print("%s\n", toString(ex, v1));
219		Ltry =>
220			(pc, apc) = getjmp(code.ops, pc);
221			(k1, v1, lab) = estmt(ex, code,  pc, apc);
222			(kc, vc) := (k1, v1);
223			(pc, apc) = getjmp(code.ops, apc);
224			if(pc != apc){
225				(pc, c) = getconst(code.ops, ++pc);
226				if(k1 == CThrow){
227					o := mkobj(ex.objproto, "Object");
228					valinstant(o, DontDelete, code.strs[c], v1);
229					pushscope(ex, o);
230					(k1, v1, lab) = estmt(ex, code, pc, apc);
231					popscope(ex);
232					if(k1 != CNormal)
233						(kc, vc) = (k1, v1);
234				}
235			}
236			(pc, apc) = getjmp(code.ops, apc);
237			if(pc != apc){
238				(k, v, lab) = estmt(ex, code, pc, apc);
239				if(k == CNormal)
240					(k1, v1) = (kc, vc);
241				else
242					(k1, v1) = (k, v);
243			}
244			pc = apc;
245		* =>
246			(pc, e) = eexp(ex, code, pc-1, code.npc);
247			if(e != nil)
248				v1 = getValue(ex, e);
249			if(debug['v'])
250				print("%s\n", toString(ex, v1));
251		}
252
253		if(v1 != nil)
254			v = v1;
255		if(k1 == CBreak && lab != nil && inlabs(lab, labs))
256			(k1, lab) = (CNormal, nil);
257		if(k1 != CNormal)
258			return (k1, v, lab);
259	}
260	return (CNormal, v, nil);
261}
262exception{
263	"throw" =>
264		ex.sp = osp;
265		return (CThrow, ex.errval, nil);
266}
267}
268
269ecaseblk(ex : ref Exec, code : ref Code, sv : ref Val, pc, epc : int, labs: list of string) : Completion
270{	defpc, nextpc, clausepc, apc : int;
271	ev : ref Val;
272	lab: string;
273
274	k := CNormal;
275	v := undefined;
276	matched := 0;
277
278	(pc, defpc) = getjmp(code.ops, pc);
279	clausepc = pc;
280	(pc, nextpc) = getjmp(code.ops, pc);
281	for (; pc <= epc; (clausepc, (pc, nextpc)) = (nextpc, getjmp(code.ops, nextpc))) {
282		if (nextpc == epc) {
283			if (matched || defpc == epc)
284				break;
285			# do the default
286			matched = 1;
287			nextpc = defpc;
288			continue;
289		}
290		if (!matched && clausepc == defpc)
291			# skip default case - still scanning guards
292			continue;
293		if (clausepc != defpc) {
294			# only case clauses have guard exprs
295			(pc, apc) = getjmp(code.ops, pc);
296			if (matched)
297				pc = apc;
298			else {
299				(pc, ev) = eexpval(ex, code, pc, apc);
300				if (identical(sv, ev))
301					matched = 1;
302				else
303					continue;
304			}
305		}
306		(k, v, lab) = estmt(ex, code, pc, nextpc);
307		if(k == CBreak && initlabs(lab, labs))
308			return (CNormal, v, nil);
309		if(k == CBreak || k == CContinue || k == CReturn || k == CThrow)
310			return (k, v, lab);
311	}
312	return (k, v, lab);
313}
314
315identical(v1, v2 : ref Val) : int
316{
317	if (v1.ty != v2.ty)
318		return 0;
319	ret := 0;
320	case v1.ty{
321	TUndef or
322	TNull =>
323		ret = 1;
324	TNum =>
325		if(v1.num == v2.num)
326			ret = 1;
327	TBool =>
328		if(v1 == v2)
329			ret = 1;
330	TStr =>
331		if(v1.str == v2.str)
332			ret = 1;
333	TObj =>
334		if(v1.obj == v2.obj)
335			ret = 1;
336	TRegExp =>
337		if(v1.rev == v2.rev)
338			ret = 1;
339	}
340	return ret;
341}
342
343eexpval(ex: ref Exec, code: ref Code, pc, epc: int): (int, ref Val)
344{
345	e: ref Ref;
346
347	(pc, e) = eexp(ex, code, pc, epc);
348	if(e == nil)
349		v := undefined;
350	else
351		v = getValue(ex, e);
352	return (pc, v);
353}
354
355eexp(ex: ref Exec, code: ref Code, pc, epc: int): (int, ref Ref)
356{
357	o, th: ref Obj;
358	a1: ref Ref;
359	v, v1, v2: ref Val;
360	s: string;
361	r1, r2: real;
362	c, apc, i1, i2: int;
363
364	savesp := ex.sp;
365out:	while(pc < epc){
366		op := int code.ops[pc++];
367		if(debug['e'] > 1){
368			case op{
369			Lid or
370			Lstr or
371			Lregexp =>
372				(nil, c) = getconst(code.ops, pc);
373				print("eexp(pc %d, sp %d) %s '%s'\n", pc-1, ex.sp, tokname(op), code.strs[c]);
374			Lnum =>
375				(nil, c) = getconst(code.ops, pc);
376				print("eexp(pc %d, sp %d) %s '%g'\n", pc-1, ex.sp, tokname(op), code.nums[c]);
377			* =>
378				print("eexp(pc %d, sp %d) %s\n", pc-1, ex.sp, tokname(op));
379			}
380		}
381		case op{
382		Lthis =>
383			v1 = objval(ex.this);
384		Lnum =>
385			(pc, c) = getconst(code.ops, pc);
386			v1 = numval(code.nums[c]);
387		Lstr =>
388			(pc, c) = getconst(code.ops, pc);
389			v1 = strval(code.strs[c]);
390		Lregexp =>
391			(pc, c) = getconst(code.ops, pc);
392			(p, f) := rsplit(code.strs[c]);
393			o = nregexp(ex, nil, array[] of { strval(p), strval(f) });
394			v1 = objval(o);
395			# v1 = regexpval(p, f, 0);
396		Lid =>
397			(pc, c) = getconst(code.ops, pc);
398			epush(ex, esprimid(ex, code.strs[c]));
399			continue;
400		Lnoval =>
401			v1 = undefined;
402		'.' =>
403			a1 = epop(ex);
404			v1 = epopval(ex);
405			epush(ex, ref Ref(1, nil, toObject(ex, v1), a1.name));
406			continue;
407		'[' =>
408			v2 = epopval(ex);
409			v1 = epopval(ex);
410			epush(ex, ref Ref(1, nil, toObject(ex, v1), toString(ex, v2)));
411			continue;
412		Lpostinc or
413		Lpostdec =>
414			a1 = epop(ex);
415			r1 = toNumber(ex, getValue(ex, a1));
416			v1 = numval(r1);
417			if(op == Lpostinc)
418				r1++;
419			else
420				r1--;
421			putValue(ex, a1, numval(r1));
422		Linc or
423		Ldec or
424		Lpreadd or
425		Lpresub =>
426			a1 = epop(ex);
427			r1 = toNumber(ex, getValue(ex, a1));
428			case op{
429			Linc =>
430				r1++;
431			Ldec =>
432				r1--;
433			Lpresub =>
434				r1 = -r1;
435			}
436			v1 = numval(r1);
437			if(op == Linc || op == Ldec)
438				putValue(ex, a1, v1);
439		'~' =>
440			v = epopval(ex);
441			i1 = toInt32(ex, v);
442			i1 = ~i1;
443			v1 = numval(real i1);
444		'!' =>
445			v = epopval(ex);
446			v1 = toBoolean(ex, v);
447			if(v1 == true)
448				v1 = false;
449			else
450				v1 = true;
451		Ltypeof =>
452			a1 = epop(ex);
453			if(a1.isref && getBase(ex, a1) == nil)
454				s = "undefined";
455			else case (v1 = getValue(ex, a1)).ty{
456			TUndef =>
457				s = "undefined";
458			TNull =>
459				s = "object";
460			TBool =>
461				s = "boolean";
462			TNum =>
463				s = "number";
464			TStr =>
465				s = "string";
466			TObj =>
467				if(v1.obj.call != nil)
468					s = "function";
469				else
470					s = "object";
471			TRegExp =>
472				s = "regexp";
473			}
474			v1 = strval(s);
475		Ldelete =>
476			a1 = epop(ex);
477			o = getBase(ex, a1);
478			s = getPropertyName(ex, a1);
479			if(o != nil)
480				esdelete(ex, o, s, 0);
481			v1 = undefined;
482		Lvoid =>
483			epopval(ex);
484			v = undefined;
485		'*' or
486		'/' or
487		'%' or
488		'-' =>
489			v2 = epopval(ex);
490			a1 = epop(ex);
491			r1 = toNumber(ex, getValue(ex, a1));
492			r2 = toNumber(ex, v2);
493			case op{
494			'*' =>
495				r1 = r1 * r2;
496			'/' =>
497				r1 = r1 / r2;
498			'%' =>
499				r1 = fmod(r1, r2);
500			'-' =>
501				r1 = r1 - r2;
502			}
503			v1 = numval(r1);
504		'+' =>
505			v2 = epopval(ex);
506			a1 = epop(ex);
507			v1 = toPrimitive(ex, getValue(ex, a1), NoHint);
508			v2 = toPrimitive(ex, v2, NoHint);
509			if(v1.ty == TStr || v2.ty == TStr)
510				v1 = strval(toString(ex, v1)+toString(ex, v2));
511			else
512				v1 = numval(toNumber(ex, v1)+toNumber(ex, v2));
513		Llsh or
514		Lrsh or
515		Lrshu or
516		'&' or
517		'^' or
518		'|' =>
519			v2 = epopval(ex);
520			a1 = epop(ex);
521			i1 = toInt32(ex, getValue(ex, a1));
522			i2 = toInt32(ex, v2);
523			case op{
524			Llsh =>
525				i1 <<= i2 & 16r1f;
526			Lrsh =>
527				i1 >>= i2 & 16r1f;
528			Lrshu =>
529				i1 = int (((big i1) & 16rffffffff) >> (i2 & 16r1f));
530			'&' =>
531				i1 &= i2;
532			'|' =>
533				i1 |= i2;
534			'^' =>
535				i1 ^= i2;
536			}
537			v1 = numval(real i1);
538		'=' or
539		Las =>
540			v1 = epopval(ex);
541			a1 = epop(ex);
542			putValue(ex, a1, v1);
543		'<' or
544		'>' or
545		Lleq or
546		Lgeq =>
547			v2 = epopval(ex);
548			v1 = epopval(ex);
549			if(op == '>' || op == Lleq){
550				v = v1;
551				v1 = v2;
552				v2 = v;
553			}
554			v1 = toPrimitive(ex, v1, TNum);
555			v2 = toPrimitive(ex, v2, TNum);
556			if(v1.ty == TStr && v2.ty == TStr){
557				if(v1.str < v2.str)
558					v1 = true;
559				else
560					v1 = false;
561			}else{
562				r1 = toNumber(ex, v1);
563				r2 = toNumber(ex, v2);
564				if(isnan(r1) || isnan(r2))
565					v1 = undefined;
566				else if(r1 < r2)
567					v1 = true;
568				else
569					v1 = false;
570			}
571			if(op == Lgeq || op == Lleq){
572				if(v1 == false)
573					v1 = true;
574				else
575					v1 = false;
576			}
577		Lin =>
578			v2 = epopval(ex);
579			v1 = epopval(ex);
580			if(v2.ty != TObj)
581				runtime(ex, TypeError, "rhs of 'in' not an object");
582			s = toString(ex, v1);
583			v1 = eshasproperty(ex, v2.obj, s, 0);
584		Linstanceof =>
585			v2 = epopval(ex);
586			v1 = epopval(ex);
587			if(v2.ty != TObj)
588				runtime(ex, TypeError, "rhs of 'instanceof' not an object");
589			if(!isfuncobj(v2.obj))
590				runtime(ex, TypeError, "rhs of 'instanceof' not a function");
591			if(v1.ty != TObj)
592				v1 = false;
593			else{
594				v2 = esget(ex, v2.obj, "prototype", 0);
595				if(v2.ty != TObj)
596					runtime(ex, TypeError, "prototype value not an object");
597				o = v2.obj;
598				for(p := v1.obj.prototype; p != nil; p = p.prototype){
599					if(p == o){
600						v1 = true;
601						break;
602					}
603				}
604				if(p == nil)
605					v1 = false;
606			}
607		Leq or
608		Lneq or
609		Lseq or
610		Lsne =>
611			strict := op == Lseq || op == Lsne;
612			v2 = epopval(ex);
613			v1 = epopval(ex);
614			v = false;
615			while(v1.ty != v2.ty){
616				if(strict)
617					break;
618				if(isnull(v1) && v2 == undefined
619				|| v1 == undefined && isnull(v2))
620					v1 = v2;
621				else if(v1.ty == TNum && v2.ty == TStr)
622					v2 = numval(toNumber(ex, v2));
623				else if(v1.ty == TStr && v2.ty == TNum)
624					v1 = numval(toNumber(ex, v1));
625				else if(v1.ty == TBool)
626					v1 = numval(toNumber(ex, v1));
627				else if(v2.ty == TBool)
628					v2 = numval(toNumber(ex, v2));
629				else if(v2.ty == TObj && (v1.ty == TStr || v1.ty == TNum))
630					v2 = toPrimitive(ex, v2, NoHint);
631				else if(v1.ty == TObj && (v2.ty == TStr || v2.ty == TNum))
632					v1 = toPrimitive(ex, v1, NoHint);
633				else{
634					v1 = true;
635					v2 = false;
636				}
637			}
638			if(v1.ty != v2.ty)
639				v = false;
640			else{
641				case v1.ty{
642				TUndef or
643				TNull =>
644					v = true;
645				TNum =>
646					if(v1.num == v2.num)
647						v = true;
648				TBool =>
649					if(v1 == v2)
650						v = true;
651				TStr =>
652					if(v1.str == v2.str)
653						v = true;
654				TObj =>
655					if(v1.obj == v2.obj)
656						v = true;
657				TRegExp =>
658					if(v1.rev.p == v2.rev.p && v1.rev.f == v2.rev.f)
659						v = true;
660				}
661			}
662			if(op == Lneq || op == Lsne){
663				if(v == false)
664					v = true;
665				else
666					v = false;
667			}
668			v1 = v;
669		Landand =>
670			v1 = epopval(ex);
671			(pc, apc) = getjmp(code.ops, pc);
672			if(toBoolean(ex, v1) != false){
673				(pc, a1) = eexp(ex, code, pc, apc);
674				v1 = getValue(ex, a1);
675			}
676			pc = apc;
677		Loror =>
678			v1 = epopval(ex);
679			(pc, apc) = getjmp(code.ops, pc);
680			if(toBoolean(ex, v1) != true){
681				(pc, a1) = eexp(ex, code, pc, apc);
682				v1 = getValue(ex, a1);
683			}
684			pc = apc;
685		'?' =>
686			v1 = epopval(ex);
687			(pc, apc) = getjmp(code.ops, pc);
688			v1 = toBoolean(ex, v1);
689			if(v1 == true)
690				(pc, a1) = eexp(ex, code, pc, apc);
691			pc = apc;
692			(pc, apc) = getjmp(code.ops, pc);
693			if(v1 != true)
694				(pc, a1) = eexp(ex, code, pc, apc);
695			pc = apc;
696			v1 = getValue(ex, a1);
697		Lasop =>
698			a1 = epop(ex);
699			epush(ex, a1);
700			v1 = getValue(ex, a1);
701		Lgetval =>
702			v1 = epopval(ex);
703		',' =>
704			v1 = epopval(ex);
705			epop(ex);
706			# a1's value already gotten by Lgetval
707		'(' or
708		')' =>
709			continue;
710		Larrinit =>
711			o = narray(ex, nil, nil);
712			(pc, c) = getconst(code.ops, pc);
713			esput(ex, o, "length", numval(real c), 0);
714			c = ex.sp-c;
715			for(sp := c; sp < ex.sp; sp++){
716				v = getValue(ex, ex.stack[sp]);
717				if(v != undefined)
718					esput(ex, o, string (sp-c), v, 0);
719			}
720			ex.sp = c;
721			v1 = objval(o);
722		Lobjinit =>
723			o = nobj(ex, nil, nil);
724			(pc, c) = getconst(code.ops, pc);
725			c = ex.sp-2*c;
726			for(sp := c; sp < ex.sp; sp += 2){
727				v = getValue(ex, ex.stack[sp]);
728				if(isnum(v) || isstr(v))
729					p := toString(ex, v);
730				else
731					p = ex.stack[sp].name;
732				v = getValue(ex, ex.stack[sp+1]);
733				esput(ex, o, p, v, 0);
734			}
735			ex.sp = c;
736			v1 = objval(o);
737		Lcall or
738		Lnewcall =>
739			(pc, c) = getconst(code.ops, pc);
740			args := array[c] of ref Val;
741			c = ex.sp - c;
742			for(sp := c; sp < ex.sp; sp++)
743				args[sp-c] = getValue(ex, ex.stack[sp]);
744			ex.sp = c;
745			a1 = epop(ex);
746			v = getValue(ex, a1);
747			o = getobj(v);
748			if(op == Lcall){
749				if(o == nil || o.call == nil)
750					runtime(ex, TypeError, "can only call function objects ("+a1.name+")");
751				th = nil;
752				if(a1.isref){
753					th = getBase(ex, a1);
754					if(th != nil && isactobj(th))
755						th = nil;
756				}
757
758				# have to execute functions in the same context as they
759				# were defined, but need to use current stack.
760				if (o.call.ex == nil)
761					a1 = escall(ex, v.obj, th, args, 0);
762				else {
763					fnex := ref *o.call.ex;
764					fnex.stack = ex.stack;
765					fnex.sp = ex.sp;
766					fnex.scopechain = fnex.global :: nil;
767					# drop ref to stack to avoid array duplication should stack grow
768					ex.stack = nil;
769					osp := ex.sp;
770					# can get an exception here that corrupts ex etc.
771#aardvark:=99;
772#test:=99;
773# zebra:=99;
774					{
775						a1 = escall(fnex, v.obj, th, args, 0);
776					}
777					exception e{
778						"throw" =>
779							# copy up error so as it gets reported properly
780							ex.error = fnex.error;
781							ex.errval = fnex.errval;
782							ex.stack = fnex.stack;
783							ex.sp = osp;
784#							raise e;
785							raise "throw";
786					}
787					# restore stack, sp is OK as escall() ensures that stack is balanced
788					ex.stack = fnex.stack;
789				}
790			}else{
791				if(o == nil || o.construct == nil)
792					runtime(ex, TypeError, "new must be given a constructor object");
793				a1 = valref(objval(esconstruct(ex, o, args)));
794			}
795			epush(ex, a1);
796			args = nil;
797			continue;
798		Lnew =>
799			v = epopval(ex);
800			o = getobj(v);
801			if(o == nil || o.construct == nil)
802				runtime(ex, TypeError, "new must be given a constructor object");
803			v1 = objval(esconstruct(ex, o, nil));
804		Lfunction =>
805			(pc, c) = getconst(code.ops, pc);
806			v1 = objval(code.fexps[c]);
807		';' =>
808			break out;
809		* =>
810			fatal(ex, sprint("eexp: unknown op %s\n", tokname(op)));
811		}
812		epushval(ex, v1);
813	}
814
815	if(savesp == ex.sp)
816		return (pc, nil);
817
818	if(savesp != ex.sp-1)
819		print("unbalanced stack in eexp: %d %d\n", savesp, ex.sp);
820	return (pc, epop(ex));
821}
822
823epushval(ex: ref Exec, v: ref Val)
824{
825	epush(ex, valref(v));
826}
827
828epush(ex: ref Exec, r: ref Ref)
829{
830	if(ex.sp >= len ex.stack){
831		st := array[2 * len ex.stack] of ref Ref;
832		st[:] = ex.stack;
833		ex.stack = st;
834	}
835	ex.stack[ex.sp++] = r;
836}
837
838epop(ex: ref Exec): ref Ref
839{
840	if(ex.sp == 0)
841		fatal(ex, "popping too far off the estack\n");
842	return ex.stack[--ex.sp];
843}
844
845epopval(ex: ref Exec): ref Val
846{
847	if(ex.sp == 0)
848		fatal(ex, "popping too far off the estack\n");
849	return getValue(ex, ex.stack[--ex.sp]);
850}
851
852inlabs(lab: string, labs: list of string): int
853{
854	for(l := labs; l != nil; l = tl l)
855		if(hd l == lab)
856			return 1;
857	return 0;
858}
859
860initlabs(lab: string, labs: list of string): int
861{
862	return lab == nil || inlabs(lab, labs);
863}
864