Merge remote-tracking branch 'redux/master' into sh4-pool
[tamarin-stm.git] / esc / src / cogen-expr.es
blob261b451e9f26a4a218192ecc5ec81c529e27bd33
1 /* -*- mode: java; tab-width: 4; insert-tabs-mode: nil; indent-tabs-mode: nil;  -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is [Open Source Virtual Machine.].
16  *
17  * The Initial Developer of the Original Code is
18  * Adobe System Incorporated.
19  * Portions created by the Initial Developer are Copyright (C) 2004-2006
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *   Adobe AS3 Team
24  *
25  * Alternatively, the contents of this file may be used under the terms of
26  * either the GNU General Public License Version 2 or later (the "GPL"), or
27  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28  * in which case the provisions of the GPL or the LGPL are applicable instead
29  * of those above. If you wish to allow use of your version of this file only
30  * under the terms of either the GPL or the LGPL, and not to allow others to
31  * use your version of this file under the terms of the MPL, indicate your
32  * decision by deleting the provisions above and replace them with the notice
33  * and other provisions required by the GPL or the LGPL. If you do not delete
34  * the provisions above, a recipient may use your version of this file under
35  * the terms of any one of the MPL, the GPL or the LGPL.
36  *
37  * ***** END LICENSE BLOCK ***** */
39 /* FIXME: handling 'super'.
40  *
41  * These work now:
42  *
43  * super.m(...)
44  *   calls the super class's "m" with "this" as the receiver:
45  *   'callsuper' instruction
46  *
47  * super(...)
48  *   calls the super class's constructor on the arguments:
49  *   'constructsuper' instruction
50  *   only legal in a constructor, the parser annotates the Ctor structure with the args,
51  *   don't need to handle this as an expression
52  *
53  *
54  * These have yet to be handled:
55  *
56  * super.x
57  *   picks up the 'x' member from the super class:
58  *   'getsuper' instruction
59  *
60  * super(o).m()
61  *   calls the super class's "m" with "o" as the receiver (o must be of a reasonable type):
62  *   'callsuper' instruction
63  */
65 use default namespace Gen,
66     namespace Gen;
68 use namespace Abc,
69     namespace Asm,
70     namespace Ast,
71     namespace Emit,
72     namespace Util;
74 internal var lastline = -1;
76 function cgExpr(ctx, e) {
77     if (emit_debug && e.pos > 0 && e.pos != lastline) {
78         ctx.asm.I_debugline(e.pos);
79         lastline = e.pos;
80     }
81     switch type (e) {
82     case (e:TernaryExpr) { cgTernaryExpr(ctx, e) }
83     case (e:BinaryExpr) { cgBinaryExpr(ctx, e) }
84     case (e:BinaryTypeExpr) { cgBinaryTypeExpr(ctx, e) }
85     case (e:UnaryExpr) { cgUnaryExpr(ctx, e) }
86     case (e:TypeOpExpr) { cgTypeOpExpr(ctx, e) }
87     case (e:ThisExpr) { cgThisExpr(ctx, e) }
88     case (e:YieldExpr) { cgYieldExpr(ctx, e) }
89     case (e:SuperExpr) { 
90         Gen::syntaxError(ctx, "A 'super' expression can't appear here");
91     }
92     case (e:LiteralExpr) { cgLiteralExpr(ctx, e) }
93     case (e:CallExpr) { cgCallExpr(ctx, e) }
94     case (e:ApplyTypeExpr) { cgApplyTypeExpr(ctx, e) }
95     case (e:LetExpr) { cgLetExpr(ctx, e) }
96     case (e:DynamicOverrideExpr) { cgDynamicOverrideExpr(ctx, e) }
97     case (e:NewExpr) { cgNewExpr(ctx, e) }
98     case (e:ObjectRef) { cgObjectRef(ctx, e) }
99     case (e:IdentExpr) { cgIdentExprNode(ctx, e) }
100     case (e:SetExpr) { cgSetExpr(ctx, e) }
101     case (e:InitExpr) { cgInitExpr(ctx, e) }
102     case (e:GetTemp) { cgGetTempExpr(ctx, e) }
103     case (e:GetParam) { cgGetParamExpr(ctx, e) }
104     case (e:GetCogenTemp) { cgGetCogenTemp(ctx, e) }
105     case (e:EvalScopeInitExpr) { cgEvalScopeInitExpr(ctx,e) }
106     case (e:*) { 
107         Gen::internalError(ctx, "Unimplemented expression type " + e);
108     }
109     }
112 function cgTernaryExpr(ctx, { e1: test, e2: consequent, e3: alternate }) {
113     let {asm} = ctx;
114     cgExpr(ctx, test);
115     let L0 = asm.I_iffalse(undefined);
116     cgExpr(ctx, consequent);
117     asm.I_coerce_a();
118     let L1 = asm.I_jump(undefined);
119     asm.I_label(L0);
120     cgExpr(ctx, alternate);
121     asm.I_coerce_a();
122     asm.I_label(L1);
125 function cgBinaryExpr(ctx, e) {
126     let {asm} = ctx;
127     if (e.op == logicalAndOp) {
128         cgExpr(ctx, e.e1);
129         asm.I_coerce_a();  // wrong, should coerce to LUB of lhs and rhs
130         asm.I_dup();
131         asm.I_convert_b();
132         let L0 = asm.I_iffalse(undefined);
133         asm.I_pop();
134         cgExpr(ctx, e.e2);
135         asm.I_coerce_a();  // wrong, should coerce to LUB of lhs and rhs
136         asm.I_label(L0);
137     }
138     else if (e.op == logicalOrOp) {
139         cgExpr(ctx, e.e1);
140         asm.I_coerce_a();  // wrong, should coerce to LUB of lhs and rhs
141         asm.I_dup();
142         asm.I_convert_b();
143         let L0 = asm.I_iftrue(undefined);
144         asm.I_pop();
145         cgExpr(ctx, e.e2);
146         asm.I_coerce_a();  // wrong, should coerce to LUB of lhs and rhs
147         asm.I_label(L0);
148     }
149     else if (e.op == commaOp) {
150         cgExpr(ctx, e.e1);
151         asm.I_pop();
152         cgExpr(ctx, e.e2);
153     }
154     else {
155         cgExpr(ctx, e.e1);
156         cgExpr(ctx, e.e2);
157         switch (e.op) {
158         case plusOp:               asm.I_add(); break;
159         case minusOp:              asm.I_subtract(); break;
160         case timesOp:              asm.I_multiply(); break;
161         case divideOp:             asm.I_divide(); break;
162         case remainderOp:          asm.I_modulo(); break;
163         case leftShiftOp:          asm.I_lshift(); break;
164         case rightShiftOp:         asm.I_rshift(); break;
165         case rightShiftUnsignedOp: asm.I_urshift(); break;
166         case bitwiseAndOp:         asm.I_bitand(); break;
167         case bitwiseOrOp:          asm.I_bitor(); break;
168         case bitwiseXorOp:         asm.I_bitxor(); break;
169         case instanceOfOp:         asm.I_instanceof(); break;
170         case inOp:                 asm.I_in(); break;
171         case equalOp:              asm.I_equals(); break;
172         case notEqualOp:           asm.I_equals(); asm.I_not(); break;
173         case strictEqualOp:        asm.I_strictequals(); break;
174         case strictNotEqualOp:     asm.I_strictequals(); asm.I_not(); break;
175         case lessOp:               asm.I_lessthan(); break;
176         case lessOrEqualOp:        asm.I_lessequals(); break;
177         case greaterOp:            asm.I_greaterthan(); break;
178         case greaterOrEqualOp:     asm.I_greaterequals(); break;
179         default:                   Gen::internalError(ctx, "Unimplemented binary operator " + e);
180         }
181     }
184 internal var id_TypeError = new Ast::Identifier(Token::sym_TypeError, Ast::publicNSSL);
186 function cgBinaryTypeExpr(ctx, {op, e1, e2}) {
187     let {asm, cp} = ctx;
188     switch (op) {
189     case castOp: {
190         // ES4 'cast'.
191         //
192         // OPTIMIZEME.  This would benefit from an "OP_coercelate"
193         // opcode for brevity and less control flow but the code
194         // below should be correct as far as the language and
195         // verifier are concerned.
196         //
197         // OPTIMIZEME.  Early-bind the right hand side if possible
198         // and use simpler code here (probably just a single
199         // OP_coerce instruction if that does not invoke any user
200         // defined converters).
201         cgExpr(ctx, e1);
202         asm.I_dup();
203         cgTypeExprHelper(ctx, e2);
204         asm.I_istypelate();
205         let L0 = asm.I_iftrue(undefined);
206         asm.I_pop();
207         // FIXME: should lookup directly in global obj or be unforgeable name.
208         asm.I_findproperty(cgIdentExpr(ctx, id_TypeError));
209         asm.I_pushstring(cp.stringUtf8("Cast failed."));
210         asm.I_constructprop(cgIdentExpr(ctx, id_TypeError), 1);
211         asm.I_throw();
212         asm.I_label(L0);
213         asm.I_coerce_a();
214         break;
215     }
216     case isOp: { 
217         // ES4 'is'.
218         //
219         // OPTIMIZEME.  Early-bind the right hand side if possible
220         // and use simpler code here (probably just a single
221         // OP_istype instruction).
222         cgExpr(ctx, e1);
223         cgTypeExprHelper(ctx, e2);
224         asm.I_istypelate();
225         break;
226     }
227     default:
228         Gen::internalError(ctx, "Unimplemented binary type operator " + op);
229     }
232 function cgTypeOpExpr(ctx, {ex}) {
233     cgTypeExprHelper(ctx, ex);
236 function cgTypeExprHelper(ctx, ty) {
237     let {asm, emitter} = ctx;
238     switch type (ty) {
239     case (ty:TypeName) {
240         asm.I_findpropstrict(cgIdentExpr(ctx, ty.ident));
241         asm.I_getproperty(cgIdentExpr(ctx, ty.ident));
242     }
243     case (ty:*) {
244         /* FIXME */
245         Gen::internalError(ctx, "Unimplemented type expression type " + ty);
246     }
247     }
250 function cgUnaryExpr(ctx, e) {
251     let {asm, emitter} = ctx;
253     switch (e.op & strictMask) {
254     case deleteOp: {
255         // FIXME: delete operator strict mode
256         switch type (e.e1) {
257         case (lr: IdentExpr) {
258             let bind = getIdentBinding(ctx, lr);
259             if( bind === Ast::nobind ) {
260                     asm.I_findproperty(cgIdentExpr(ctx, lr));
261                     asm.I_deleteproperty(cgIdentExpr(ctx, lr));
262                 }
263             else
264                 asm.I_pushtrue();  // can't delete a fixed property
265         }
266         case (or: ObjectRef) {
267             cgExpr(ctx, or.base);
268             asm.I_deleteproperty(cgIdentExpr(ctx, or.ident));
269         }
270         case (e1:*) {
271             cgExpr(ctx, e1);
272             asm.I_pop();
273             asm.I_pushtrue();
274         }
275         }
276         break;
277     }
279     case voidOp:
280         cgExpr(ctx, e.e1);
281         asm.I_pop();
282         asm.I_pushundefined();
283         break;
285     case typeOfOp:
286         if (e.e1 is IdentExpr) {
287             cgFindProp(ctx, e.e1);
288             cgGetProp(ctx, e.e1);
289         }
290         else 
291             cgExpr(ctx, e.e1);
292         asm.I_typeof();
293         break;
295     case preIncrOp: 
296         incdec(true, true); 
297         break;
299     case preDecrOp: 
300         incdec(true, false); 
301         break;
303     case postIncrOp: 
304         incdec(false, true); 
305         break;
307     case postDecrOp: 
308         incdec(false, false); 
309         break;
311     case unaryPlusOp:
312         cgExpr(ctx, e.e1);
313         asm.I_convert_d();
314         break;
316     case unaryMinusOp:
317         cgExpr(ctx, e.e1);
318         asm.I_negate();
319         break;
321     case bitwiseNotOp:
322         cgExpr(ctx, e.e1);
323         asm.I_bitnot();
324         break;
326     case logicalNotOp:
327         cgExpr(ctx, e.e1);
328         asm.I_not();
329         break;
331     default:
332         Gen::internalError(ctx, "Unimplemented unary operation " + op);
333     }
335     function incdec(pre, inc) {
336         switch type (e.e1) {
337         case (lr: IdentExpr) {
338             if (cgFindPropStrict(ctx, lr))  // Ugly-ish.  Object will be used by cgSetProp later.
339                 asm.I_dup();
340             cgGetProp(ctx, e.e1);
341         }
342         case (or:ObjectRef) {
343             cgExpr(ctx, or.base);
344             asm.I_dup();
345             cgGetProp(ctx, e.e1.ident);
346         }
347         case (x:*) { 
348             Gen::syntaxError(ctx, "Expression is not an lvalue: " + e.e1);
349         }
350         }
351         let t = asm.getTemp();
352         if (pre) {
353             if (inc)
354                 asm.I_increment();
355             else
356                 asm.I_decrement();
357             asm.I_dup();
358             asm.I_setlocal(t);
359         }
360         else {
361             // Postfix ops return value after conversion to number.
362             asm.I_convert_d();
363             asm.I_dup();
364             asm.I_setlocal(t);
365             if (inc)
366                 asm.I_increment();
367             else
368                 asm.I_decrement();
369         }
370         switch type (e.e1) {
371         case (lr: IdentExpr) {
372             cgSetProp(ctx, lr);
373         }
374         case (or: ObjectRef) {
375             cgSetProp(ctx, or.ident);
376         }
377         }
378         asm.I_getlocal(t);
379         asm.killTemp(t);
380     }
383 function cgThisExpr({asm}, e) {
384     asm.I_getlocal(0);
387 function cgYieldExpr(ctx, e) {
388     // FIXME
389     Gen::internalError(ctx, "Unimplemented 'yield' expression");
392 function cgCallExpr(ctx, e) {
393     let {asm, emitter} = ctx;
394     let nargs = e.args.length;
395     let evalTmp = 0;
396     let isEval = false;
397     let isSuperCall = false;
398     let name;
400     if (e.spread != null)
401         Gen::internalError(ctx, "Spread expression not implemented.");
403     switch type (e.expr) {
404     case (or: ObjectRef) {
405         if (or.base is SuperExpr) {
406             assert(or.base.ex == null); // If not then super(o) form, don't know what that is yet.  --lars
407             asm.I_getlocal(0);
408             isSuperCall = true;
409         }
410         else
411             cgExpr(ctx, or.base);
412     }
413     case (lr: IdentExpr) {
414         var bind = getIdentBinding(ctx, lr);
415         if( bind is RegBind ) {
416             asm.I_getlocal(bind.reg);            
417             asm.I_pushnull(); // Should we be using something else for this?
418         }
419         else {
420             cgFindPropStrict(ctx, lr);
421         }
422         if (lr is Identifier && lr.ident == Token::sym_eval) {
423             isEval = true;
424             evalTmp = asm.getTemp();   // save the
425             asm.I_dup();               //   object
426             asm.I_setlocal(evalTmp);   //     for later
427         }
428     }
429     case (x:*) {
430         cgExpr(ctx, e.expr);
431         asm.I_pushnull();
432     }
433     }
435     if (e.expr is ObjectRef) {
436         // Runtime/late parts appear on stack before arguments!
437         name = cgIdentExpr(ctx, e.expr.ident);
438     }
440     for ( let i=0 ; i < nargs ; i++ )
441         cgExpr(ctx, e.args[i]);
443     let L_skipcall;
445     if (isEval) {
446         // Code performs 'eval', cleans the stack, and jumps to L0
447         // if the eval operator kicks in, otherwise falls through
448         // to the regular call code below.
449         L_skipcall = cgEvalPrefix(ctx, evalTmp, nargs, e.strict);
450     }
452     switch type (e.expr) {
453     case (or: ObjectRef) {
454         if (isSuperCall)
455             asm.I_callsuper(name, nargs);
456         else
457             asm.I_callproperty(name, nargs);
458     }
459     case (lr: IdentExpr) {
460         // This is not right if the function is bound by "with".  In that
461         // case, I_callproperty would be more right.  That's the outlier, though.
462         var bind = getIdentBinding(ctx, lr);
463         if( bind is SlotBind || bind == Ast::nobind )
464             asm.I_callproplex(cgIdentExpr(ctx, lr), nargs);
465         else if ( bind is RegBind ) {
466             asm.I_call(nargs);
467         }
468     }
469     case (x:*) {
470         asm.I_call(nargs);
471     }
472     }
474     if (isEval) {
475         asm.I_label(L_skipcall);
476         asm.killTemp(evalTmp);
477     }
480 // First check that 'eval' really gets us the global eval binding.
482 // Assuming it really is the eval operator:
484 // Since the form of the call is eval(...) we know there are no
485 // late-bound name components on the evaluation stack, just the
486 // receiver object (which is the global object).  Clean up the
487 // stack by consing up an array of all the arguments.
489 // Call ESC::eval() on the argument array, a descriptor of the
490 // scopes (a string), and an array of scopes, outermost-first.
491 // The descriptor and the scope array have the same length.  A
492 // letter in the descriptor is "s" if the object is a regular
493 // scope object (to be pushed by "pushscope") and "w" if it is is
494 // a "with" object (to be pushed by "pushwith").
496 // ESC::evalCompiler() returns the result of the evaluation.
498 function cgEvalPrefix(ctx, evalTmp, nargs, strict) {
499     let {asm} = ctx;
500     let id_ESC = new Ast::Identifier(Token::sym_ESC, Ast::publicNSSL);
501     let id_evaluateInScopeArray = new Ast::QualifiedIdentifier(id_ESC, Token::sym_evaluateInScopeArray);
503     // Check it: Is this *really* the eval operator?
505     let L_normalcall = undefined;
507     /* FIXME: The following sanity tests only work when getglobalscope returns the object 
508        that actually holds the global variables.  Bugzilla 417342.
510        // The container must be === to the global object
511        asm.I_getlocal(evalTmp);
512        asm.I_getglobalscope();
513        asm.I_ifstrictne(L_normalcall);
515        // Property must be === to the predefined eval function, stored in the constant ESC::eval
516        asm.I_getlocal(evalTmp);
517        asm.I_getproperty(cgIdentExpr(ctx, lr.ident));
518        asm.I_getglobalscope();
519        asm.i_getproperty(cgIdentExpr(ctx, new Ast::QualifiedIdentifier(id_ESC,
520        new Ast::Identifier("eval", [[Ast::NoNS]]))));
521        asm.I_ifstrictne(L_normalcall);
522     */
524     asm.I_newarray(nargs);             // collapse the arguments
525     asm.I_setlocal(evalTmp);           // save it...
526     asm.I_pop();                       // get rid of the object
527     asm.I_findpropstrict(cgIdentExpr(ctx, id_evaluateInScopeArray));
528     asm.I_getlocal(evalTmp);           // argument array
530     // pushScopes() generates code that leaves an array of scope
531     // objects on the stack, followed by the scope descriptor
532     // string.
534     let numscopes = pushScopes(ctx);
535     if (strict)
536         asm.I_pushtrue();
537     else
538         asm.I_pushfalse();
539     asm.I_callproplex(cgIdentExpr(ctx, id_evaluateInScopeArray), 4);
541     L_skipcall = asm.I_jump(undefined);
543     if (L_normalcall != undefined)
544         asm.I_label(L_normalcall);
546     return L_skipcall;
548     function pushScopes(ctx) {
550         let scopedesc = "";
552         function rec(stk, inner) {
553             let level;
555             // Do not capture the global scope
556             if (stk.tag == "script")
557                 return 1;
559             if (stk.tag == "function") {
560                 level = rec(stk.link, false);
561                 if (inner)
562                     level = 0;
563             }
564             else
565                 level = rec(stk.link, inner);
567             // FIXME: There may be two scope objects per ctx rib,
568             // because push_this may be true too.
569             push = false;
570             switch (stk.tag) {
571             case "function":
572             case "catch":
573                 scopedesc = "s" + scopedesc;
574                 push = true;
575                 break;
576             case "with":
577                 scopedesc = "w" + scopedesc;
578                 push = true;
579                 break;
580             case "class":
581             case "instance":
582             case "break":
583             case "continue":
584             case "finally":
585                 break;
586             case "let":
587             default:
588                 Gen::internalError(ctx, "Unknown context tag: " + stk.tag);
589             }
591             if (push) {
592                 if (inner)
593                     asm.I_getscopeobject(level);
594                 else
595                     asm.I_getouterscope(level);
596                 return level + 1;
597             }
598             return level;
599         }
601         rec(ctx.stk, true);
603         let numscopes = scopedesc.length;
605         ctx.asm.I_newarray(numscopes);
606         ctx.asm.I_pushstring(ctx.cp.stringUtf8(scopedesc));
608         return numscopes;
609     }
612 function cgEvalScopeInitExpr(ctx, {index, how}) {
613     let {asm} = ctx;
614     asm.I_getlocal(1);
615     asm.I_getproperty(cgIdentExpr(ctx, new Ast::Identifier(String(index), Ast::publicNSSL)));
616     if (how == "w")
617         asm.I_pushwith();
618     else
619         asm.I_pushscope();
620     asm.I_pushundefined();
623 function cgApplyTypeExpr(ctx, e) {
624     // FIXME
625     Gen::internalError(ctx, "Unimplemented type application expression");
628 function cgLetExpr(ctx, {head, expr}) {
629     cgHead(ctx, head);
630     cgExpr(ctx, expr);
633 function cgDynamicOverrideExpr(ctx, {names, exprs, body}) {
634     assert(names.length == exprs.length);
636     // create code to secret current values away in temps
637     // set up a simplified try-finally.
638     // Duplicate the restoring code: the catch block restores
639     // and re-throws (but does not restore the scope chain?? It
640     // may need to, since variables to restore could be bound
641     // in various objects in the local scope chain.)
642     // The fallthrough code restores and continues.
644     // body
646     // finally handler
647     // create code to restore values from temps
648     
649     // FIXME: this code is only right if exceptions are not
650     // thrown.
651     //
652     // FIXME: this code evaluates subexpressions of the names several
653     // times.
655     let {asm} = ctx;
656     let limit = names.length;
657     let temps = [];
658     let temps2 = [];
660     // Save existing values
661     for ( let i=0 ; i < limit ; i++ ) {
662         temps[i] = asm.getTemp();
663         asm.I_findpropstrict(cgIdentExpr(ctx, names[i]));
664         asm.I_getproperty(cgIdentExpr(ctx, names[i]));
665         asm.I_setlocal(temps[i]);
666     }
667     // Compute new values
668     for ( let i=0 ; i < limit ; i++ ) {
669         temps2[i] = asm.getTemp();
670         cgExpr(ctx, exprs[i]);
671         asm.I_setlocal(temps2[i]);
672     }
673     // Assign new values
674     for ( let i=0 ; i < limit ; i++ ) {
675         asm.I_findpropstrict(cgIdentExpr(ctx, names[i]));
676         asm.I_getlocal(temps2[i]);
677         asm.I_setproperty(cgIdentExpr(ctx, names[i]));
678         asm.killTemp(temps2[i]);
679     }
680     // Evaluate body
681     cgExpr(ctx, body);
682     // Restore old values
683     for ( let i=0 ; i < limit ; i++ ) {
684         asm.I_findpropstrict(cgIdentExpr(ctx, names[i]));
685         asm.I_getlocal(temps[i]);
686         asm.I_setproperty(cgIdentExpr(ctx, names[i]));
687         asm.killTemp(temps[i]);
688     }
689     // Result of body is left on the stack.
691   
693 function cgNewExpr(ctx, {expr, args, spread}) {
694     let {asm} = ctx;
696     if (spread != null)
697         Gen::internalError(ctx, "Spread expression not implemented");
699     cgExpr(ctx, expr);
700     for ( let i=0, limit=args.length ; i < limit ; i++ )
701         cgExpr(ctx, args[i]);
702     asm.I_construct(args.length);
705 function cgObjectRef(ctx, {base, ident}) {
706     let {asm} = ctx;
707     cgExpr(ctx, base);
708     asm.I_getproperty(cgIdentExpr(ctx, ident));
711 // Not to be confused with the subroutine cgIdentExpr below.  That one
712 // ought to be renamed, not this one.
714 function cgIdentExprNode(ctx, ident) {
715     cgFindPropStrict(ctx, ident);
716     cgGetProp(ctx, ident);
719 function cgSetExpr(ctx, e) {
720     switch type (e.le) {
721     case (objref: Ast::ObjectRef) { cgSetObjectRefExpr(ctx, e) }
722     case (ident: Ast::IdentExpr)  { cgSetIdentExpr(ctx, e) }
723     case (x: *)                   { Gen::syntaxError(ctx, "Illegal lvalue " + x) }
724     }
727 function cgSetIdentExpr(ctx, {op, le, re}) {
728     let {asm, emitter} = ctx;
729     let opr = op & strictMask;
730     let t = asm.getTemp();
731         
732     if (opr == assignOp) {
733         cgFindProp(ctx, le);
734         cgExpr(ctx, re);
735     }
736     else {
737         if (cgFindPropStrict(ctx, le))
738             asm.I_dup();
739         cgGetProp(ctx, le);
740         cgOperate(ctx, re, opr, (op & strictFlag) != 0);
741     }
743     asm.I_dup();
744     asm.I_setlocal(t);
745     cgSetProp(ctx, le);
746     asm.I_getlocal(t);
747     asm.killTemp(t);
750 function cgSetObjectRefExpr(ctx, {op, le, re}) {
751     let {asm, emitter} = ctx;
752     let opr = op & strictMask;
753     let t = asm.getTemp();
755     cgExpr(ctx, le.base);
756     if( le.ident is Identifier )
757         le.ident.binding = Ast::nobind;
759     if (opr == assignOp) {
760         let name = cgIdentExpr(ctx, le.ident);   // order matters if it's a ComputedName
761         cgExpr(ctx, re);
762         asm.I_dup();
763         asm.I_setlocal(t);
764         asm.I_setproperty(name);
765     }
766     else {
767         let subtmp = null;     // stores indexing expression value
768         let subname = null;    // multiname to store under
769         asm.I_dup();           // object expr
771         if (le.ident is ComputedName) {
772             subtmp = asm.getTemp();
773             cgExpr(ctx, le.ident.expr);
774             asm.I_dup();
775             asm.I_setlocal(subtmp);
776             subname = emitter.multinameL(Ast::publicNSSL, false);
777             asm.I_getproperty(subname);
778         }
779         else
780             cgGetProp(ctx, le.ident);
782         cgOperate(ctx, re, opr, (op & strictFlag) != 0);
784         asm.I_dup();
785         asm.I_setlocal(t);
786         if (le.ident is ComputedName) {
787             asm.I_getlocal(subtmp);
788             asm.I_swap();
789             asm.I_setproperty(subname);
790             asm.killTemp(subtmp);
791         }
792         else
793             cgSetProp(ctx, le.ident);
794     }
795     asm.I_getlocal(t);
796     asm.killTemp(t);
799 function cgOperate(ctx, expr, op, is_strict) {
800     // FIXME: assignment operators strict mode
802     // the left-hand expression is on the stack.
803     let {asm} = ctx;
804     if (op == assignLogicalAndOp || op == assignLogicalOrOp) {
805         asm.I_dup();
806         asm.I_convert_b();
807         let L0 = (op == assignLogicalAndOp) ? asm.I_iffalse(undefined) : asm.I_iftrue(undefined);
808         asm.I_pop();
809         cgExpr(ctx, expr);
810         asm.I_coerce_a();
811         asm.I_label(L0);
812     }
813     else {
814         cgExpr(ctx, expr);
815         switch (op) {
816         case assignPlusOp:                asm.I_add(); break;
817         case assignMinusOp:               asm.I_subtract(); break;
818         case assignTimesOp:               asm.I_multiply(); break;
819         case assignDivideOp:              asm.I_divide(); break;
820         case assignRemainderOp:           asm.I_modulo(); break;
821         case assignLeftShiftOp:           asm.I_lshift(); break;
822         case assignRightShiftOp:          asm.I_rshift(); break;
823         case assignRightShiftUnsignedOp:  asm.I_urshift(); break;
824         case assignBitwiseAndOp:          asm.I_bitand(); break;
825         case assignBitwiseOrOp:           asm.I_bitor(); break;
826         case assignBitwiseXorOp:          asm.I_bitxor(); break;
827         default:                          Gen::internalError(ctx, "ASSIGNOP not supported " + op);
828         }
829     }
832 function cgInitExpr(ctx, e) {
833     let {asm} = ctx;
834     let baseOnStk = false;
835     if (e.target == instanceInit) {
836         // Load this on the stack
837         asm.I_getlocal(0);
838         baseOnStk = true;
839     }
840     cgInits(ctx, e.inits, baseOnStk);
841     asm.I_pushundefined(); // exprs need to leave something on the stack
842     // FIXME: should this be the value of the last init?
845 function cgLiteralExpr(ctx, e) {
847     function cgArrayInitializer(ctx, {exprs, spread}) {
848         let {asm} = ctx;
849         let i = 0;
850         let limit = exprs.length
852         if (spread != null)
853             Gen::internalError(ctx, "Spread expression in array initializer not implemented.");
855         // Use newarray to construct the dense prefix
856         for ( ; i < limit ; i++ ) {
857             let e = exprs[i];
858             if (e is Ast::LiteralUndefined)
859                 break;
860             cgExpr(ctx, e);
861         }
862         asm.I_newarray(i);
864         // Then init the other defined slots one by one
865         if (i < limit) {
866             let last_was_undefined = false;
867             for ( ; i < limit ; i++ ) {
868                 let e = exprs[i];
869                 if (!(e is Ast::LiteralUndefined)) {
870                     asm.I_dup();
871                     cgExpr(ctx, e);
872                     asm.I_setproperty(cgIdentExpr(ctx, new Ast::Identifier(Token::intern(i), Ast::publicNSSL)));
873                     last_was_undefined = false;
874                 }
875                 else
876                     last_was_undefined = true;
877             }
878             if (last_was_undefined) {
879                 asm.I_dup();
880                 asm.I_pushint(ctx.cp.int32(limit));
881                 asm.I_setproperty(cgIdentExpr(ctx, new Ast::Identifier(Token::sym_length, Ast::publicNSSL)));
882             }
883         }
884     }
886     function cgObjectInitializer(ctx, {fields:fields}) {
887         let {asm, emitter} = ctx;
888         asm.I_findpropstrict(ctx.emitter.Object_name);
889         asm.I_constructprop(ctx.emitter.Object_name, 0);
890         let t = asm.getTemp();
891         asm.I_setlocal(t);
892         for ( let i=0, limit=fields.length ; i < limit ; i++ ) {
893             switch type (fields[i]) {
894             case (lf: Ast::LiteralField) {
895                 // SYNTACTIC CONDITION.  If the object initializer is
896                 // used to produce a value (it's not used for
897                 // destructuring) then the ": expr" part is not
898                 // optional.
900                 if (lf.expr == null) 
901                     Gen::syntaxError(ctx, "Missing field value in object initializer: " + lf.ident);
903                 asm.I_getlocal(t);
904                 cgExpr(ctx, lf.expr);
905                 asm.I_setproperty(cgIdentExpr(ctx, lf.ident));
907             }
908             case (vf: Ast::VirtualField) {
909                 Gen::internalError(ctx, "VirtualField support missing.");
910             }
911             case (pf: Ast::ProtoField) {
912                 Gen::internalError(ctx, "ProtoField support missing.");
913             }
914             }
915         }
916         //asm.I_newobject(fields.length);
917         asm.I_getlocal(t);
918         asm.killTemp(t);
919     }
921     function cgRegExpLiteral(ctx, re) {
922         let {asm, cp} = ctx;
923         let src = re.src.text;
924         // src is "/.../flags"
925         //
926         // Note, ES4 semantics: recreate RE object every time.
927         // FIXME: re-compiles the RE every time.
928         let p = src.lastIndexOf('/');
929         // FIXME: We don't want findpropstrict because it can be used to spoof RegExp.
930         // But getglobalscope produces an object that does not have "RegExp" bound.
931         asm.I_findpropstrict(ctx.emitter.RegExp_name);
932         asm.I_pushstring(cp.stringUtf8(src.substring(1,p)));
933         asm.I_pushstring(cp.stringUtf8(src.substring(p+1)));
934         asm.I_constructprop(ctx.emitter.RegExp_name, 2);
935     }
937     let {asm, emitter} = ctx;
938     switch type (e) {
939     case (e:LiteralNull) { asm.I_pushnull() }
940     case (e:LiteralUndefined) { asm.I_pushundefined() }
941     case (e:LiteralInt) { 
942         let val = e.intValue;
943         if (val >= -128 && val < 128)
944             asm.I_pushbyte(val & 0xFF);  // pushbyte sign-extends
945         else
946             asm.I_pushint(ctx.cp.int32(val));
947     }
948     case (e:LiteralUInt) { 
949         asm.I_pushuint(ctx.cp.uint32(e.uintValue));
950     }
951     case (e:LiteralDouble) { 
952         let val = e.doubleValue;
953         if (isNaN(val))
954             asm.I_pushnan();
955         else
956             asm.I_pushdouble(ctx.cp.float64(val));
957     }
958     case (e:LiteralDecimal) { 
959         // FIXME: proper decimal support when the AVM can handle it!
960         asm.I_pushdouble(ctx.cp.float64(parseFloat(e.decimalValue)));
961     }
962     case (e:LiteralString) {
963         asm.I_pushstring(ctx.cp.symbolUtf8(e.strValue));
964     }
965     case (e:LiteralBoolean) {
966         if (e.booleanValue)
967             asm.I_pushtrue();
968         else
969             asm.I_pushfalse();
970     }
971     case (e:LiteralFunction) { 
972         if (e.func.name != null) {
973             // FIXME: correct for ES3 but not for ES4
974             let t = asm.getTemp();
975             asm.I_newobject(0);
976             asm.I_dup();
977             asm.I_setlocal(t);
978             asm.I_pushwith();
979             asm.I_newfunction(cgFunc(ctx, e.func));
980             asm.I_dup();
981             asm.I_getlocal(t);
982             asm.I_swap();
983             asm.I_setproperty(emitter.nameFromIdent(e.func.name.ident));
984             asm.killTemp(t);
985         }
986         else
987             asm.I_newfunction(cgFunc(ctx, e.func));
988     }
989     case (e:LiteralArray) { cgArrayInitializer(ctx, e) }
990     case (e:LiteralObject) { cgObjectInitializer(ctx, e) }
991     case (e:LiteralRegExp) { cgRegExpLiteral(ctx, e) }
992     // case (e:LiteralNamespace) { cgNamespaceLiteral(ctx, e) }
993     case (e:*) { 
994         Gen::internalError(ctx, "Unimplemented LiteralExpr " + e);
995     }
996     }
999 function cgGetTempExpr(ctx, {n}) {
1000     let {asm, emitter} = ctx;
1001     let id = new Ast::Identifier(Token::intern("$t" + n), Ast::publicNSSL);
1002     cgFindPropStrict(ctx, id);
1003     cgGetProp(ctx, id);
1006 function cgGetParamExpr({asm}, {n}) {
1007     asm.I_getlocal(n + 1);  //account for 'this'
1010 function cgGetCogenTemp({asm}, {n}) {
1011     asm.I_getlocal(n);
1013     
1014 function cgIdentExpr(ctx, e) {
1015     let {asm, emitter} = ctx;
1016     switch type(e) {
1017     case (id:Identifier) {
1018         return emitter.multiname(id,false);
1019     }
1020     case (ei:ComputedName) {
1021         cgExpr(ctx, ei.expr);
1022         return emitter.multinameL(Ast::publicNSSL,false);
1023     }
1024     case (qi:QualifiedIdentifier) { 
1025         switch type(qi.qual) {
1026         case( lr: Identifier ) {
1027             // Hack to deal with namespaces for now...
1028             // later we will have to implement a namespace lookup to resolve qualified typenames
1029             return emitter.qname(new Ast::Name(new Ast::UnforgeableNamespace(lr.ident), qi.ident), false);
1030         }
1031         case (lr: ForgeableNamespace) {
1032             return emitter.qname(new Ast::Name(lr, qi.ident), false);
1033         }
1034         case (lr: UnforgeableNamespace) {
1035             return emitter.qname(new Ast::Name(lr, qi.ident), false);
1036         }
1037         case( e:* ) {
1038             /// cgExpr(ctx, qi.qual);
1039             /// return emitter.rtqname(qi);
1040             Gen::internalError(ctx, "Unsupported form of qualified identifier " + qi);
1041         }
1042         }
1043     }
1044     case (x: PropName) {
1045         return emitter.fixtureNameToName(x);
1046     }
1047     case (x: TempName) {
1048         return emitter.fixtureNameToName(x);
1049     }
1050     case (x:*) { 
1051         Gen::internalError(ctx, "Unimplemented cgIdentExpr " + e);
1052     }
1053     }
1055     
1056 function getIdentBinding(ctx, id: IdentExpr) {
1057     if (id is Identifier) {
1058         if( id.binding === undefined )
1059             id.binding = findBinding(ctx, id.ident, id.nss);
1060         return id.binding;
1061     }
1062     return Ast::nobind;
1065 // Returns true if the result of the findprop is left on the stack
1066 function cgFindPropStrict(ctx, id:IdentExpr)
1067     cgFindProp(ctx, id, true);
1069 // Returns true if the result of the findprop is left on the stack
1070 function cgFindProp(ctx, id:IdentExpr, is_strict = false) {
1071     let asm = ctx.asm;
1073     switch type ( getIdentBinding(ctx, id) ) {
1074     case ( rb : RegBind ) {
1075         // Do nothing, we'll get/set the value later with a register
1076         return false;
1077     }
1078     case ( sb : SlotBind ) {
1079         // Load the scope the slot is in
1080         asm.I_getlocal(sb.scope);
1081     }
1082     case ( e : * ) {
1083         if( is_strict )
1084             asm.I_findpropstrict(cgIdentExpr(ctx, id));
1085         else
1086             asm.I_findproperty(cgIdentExpr(ctx, id));
1087     }
1088     }
1089     return true;
1092 function cgGetProp(ctx, id) {
1093     let asm = ctx.asm;
1095     switch type ( getIdentBinding(ctx, id) ) {
1096         case ( rb : RegBind ) {
1097             // Load the register
1098             asm.I_getlocal(rb.reg);
1099         }
1100         case ( sb : SlotBind ) {
1101             // Load the scope the slot is in
1102             if( sb.slot == -1)
1103                 asm.I_getproperty(cgIdentExpr(ctx, id));
1104             else
1105                 asm.I_getslot(sb.slot);
1106         }
1107         case ( e : * ) {
1108             asm.I_getproperty(cgIdentExpr(ctx, id));
1109         }
1110     }
1113 function cgSetProp(ctx, id) {
1114     let asm = ctx.asm;
1116     switch type ( getIdentBinding(ctx, id) ) {
1117         case ( rb : RegBind ) {
1118             // Set the register
1119             if( rb.type_index != 0 )
1120                 asm.I_coerce(rb.type_index);
1121             else
1122                 asm.I_coerce_a();
1123             asm.I_setlocal(rb.reg);
1124         }
1125         case ( sb : SlotBind ) {
1126             // Load the scope the slot is in
1127             if( sb.slot == -1)
1128                 asm.I_setproperty(cgIdentExpr(ctx, id));
1129             else
1130                 asm.I_setslot(sb.slot);
1131         }
1132         case ( e : * ) {
1133             asm.I_setproperty(cgIdentExpr(ctx, id));
1134         }
1135     }