Merge remote-tracking branch 'redux/master' into sh4-pool
[tamarin-stm.git] / esc / src / asm.es
blobf148e497adfaa8357c97eba5d55fce9b5b2af943
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 use default namespace Asm,
40     namespace Asm;
42 /*******************************************************************
43  * ABC constants
44  */
46 const CONSTANT_Utf8               = 0x01;
47 const CONSTANT_Integer            = 0x03;
48 const CONSTANT_UInt               = 0x04;
49 const CONSTANT_PrivateNamespace   = 0x05;
50 const CONSTANT_Double             = 0x06;
51 const CONSTANT_QName              = 0x07; // ns::name, const ns, const name
52 const CONSTANT_Namespace          = 0x08;
53 const CONSTANT_Multiname          = 0x09; // [ns...]::name, const [ns...], const name
54 const CONSTANT_False              = 0x0A;
55 const CONSTANT_True               = 0x0B;
56 const CONSTANT_Null               = 0x0C;
57 const CONSTANT_QNameA             = 0x0D; // @ns::name, const ns, const name
58 const CONSTANT_MultinameA         = 0x0E; // @[ns...]::name, const [ns...], const name
59 const CONSTANT_RTQName            = 0x0F; // ns::name, var ns, const name
60 const CONSTANT_RTQNameA           = 0x10; // @ns::name, var ns, const name
61 const CONSTANT_RTQNameL           = 0x11; // ns::[name], var ns, var name
62 const CONSTANT_RTQNameLA          = 0x12; // @ns::[name], var ns, var name
63 const CONSTANT_NameL              = 0x13; // o[name], var name
64 const CONSTANT_NameLA             = 0x14; // @[name], var name
65 const CONSTANT_NamespaceSet       = 0x15;
66 const CONSTANT_PackageNamespace   = 0x16; // namespace for a package
67 const CONSTANT_PackageInternalNS  = 0x17;
68 const CONSTANT_ProtectedNamespace = 0x18;
69 const CONSTANT_ExplicitNamespace  = 0x19;
70 const CONSTANT_StaticProtectedNS  = 0x1A;
71 const CONSTANT_MultinameL         = 0x1B;
72 const CONSTANT_MultinameLA        = 0x1C;
74 const CONSTANT_ClassSealed        = 0x01;
75 const CONSTANT_ClassFinal         = 0x02;
76 const CONSTANT_ClassInterface     = 0x04;
77 const CONSTANT_ClassProtectedNs   = 0x08;
79 const TRAIT_Slot                  = 0;
80 const TRAIT_Method                = 1;
81 const TRAIT_Getter                = 2;
82 const TRAIT_Setter                = 3;
83 const TRAIT_Class                 = 4;
84 const TRAIT_Function              = 5;
85 const TRAIT_Const                 = 6;
87 const ATTR_Final                  = 0x01;
88 const ATTR_Override               = 0x02;
89 const ATTR_Metadata               = 0x04;
91 const SLOT_var                    = 0;
92 const SLOT_method                 = 1;
93 const SLOT_getter                 = 2;
94 const SLOT_setter                 = 3;
95 const SLOT_class                  = 4;
96 const SLOT_function               = 6;
98 const METHOD_Arguments            = 0x1;
99 const METHOD_Activation           = 0x2;
100 const METHOD_Needrest             = 0x4;
101 const METHOD_HasOptional          = 0x8;
102 const METHOD_IgnoreRest           = 0x10;
103 const METHOD_Native               = 0x20;
104 const METHOD_Setsdxns             = 0x40;
105 const METHOD_HasParamNames        = 0x80;
107 const OP_bkpt:int = 0x01;
108 const OP_nop:int = 0x02;
109 const OP_throw:int = 0x03;
110 const OP_getsuper:int = 0x04;
111 const OP_setsuper:int = 0x05;
112 const OP_dxns:int = 0x06;
113 const OP_dxnslate:int = 0x07;
114 const OP_kill:int = 0x08;
115 const OP_label:int = 0x09;
116 const OP_ifnlt:int = 0x0C;
117 const OP_ifnle:int = 0x0D;
118 const OP_ifngt:int = 0x0E;
119 const OP_ifnge:int = 0x0F;
120 const OP_jump:int = 0x10;
121 const OP_iftrue:int = 0x11;
122 const OP_iffalse:int = 0x12;
123 const OP_ifeq:int = 0x13;
124 const OP_ifne:int = 0x14;
125 const OP_iflt:int = 0x15;
126 const OP_ifle:int = 0x16;
127 const OP_ifgt:int = 0x17;
128 const OP_ifge:int = 0x18;
129 const OP_ifstricteq:int = 0x19;
130 const OP_ifstrictne:int = 0x1A;
131 const OP_lookupswitch:int = 0x1B;
132 const OP_pushwith:int = 0x1C;
133 const OP_popscope:int = 0x1D;
134 const OP_nextname:int = 0x1E;
135 const OP_hasnext:int = 0x1F;
136 const OP_pushnull:int = 0x20;
137 const OP_pushundefined:int = 0x21;
138 const OP_pushconstant:int = 0x22;
139 const OP_nextvalue:int = 0x23;
140 const OP_pushbyte:int = 0x24;
141 const OP_pushshort:int = 0x25;
142 const OP_pushtrue:int = 0x26;
143 const OP_pushfalse:int = 0x27;
144 const OP_pushnan:int = 0x28;
145 const OP_pop:int = 0x29;
146 const OP_dup:int = 0x2A;
147 const OP_swap:int = 0x2B;
148 const OP_pushstring:int = 0x2C;
149 const OP_pushint:int = 0x2D;
150 const OP_pushuint:int = 0x2E;
151 const OP_pushdouble:int = 0x2F;
152 const OP_pushscope:int = 0x30;
153 const OP_pushnamespace:int = 0x31;
154 const OP_hasnext2:int = 0x32;
155 const OP_newfunction:int = 0x40;
156 const OP_call:int = 0x41;
157 const OP_construct:int = 0x42;
158 const OP_callmethod:int = 0x43;
159 const OP_callstatic:int = 0x44;
160 const OP_callsuper:int = 0x45;
161 const OP_callproperty:int = 0x46;
162 const OP_returnvoid:int = 0x47;
163 const OP_returnvalue:int = 0x48;
164 const OP_constructsuper:int = 0x49;
165 const OP_constructprop:int = 0x4A;
166 const OP_callsuperid:int = 0x4B;
167 const OP_callproplex:int = 0x4C;
168 const OP_callinterface:int = 0x4D;
169 const OP_callsupervoid:int = 0x4E;
170 const OP_callpropvoid:int = 0x4F;
171 const OP_newobject:int = 0x55;
172 const OP_newarray:int = 0x56;
173 const OP_newactivation:int = 0x57;
174 const OP_newclass:int = 0x58;
175 const OP_getdescendants:int = 0x59;
176 const OP_newcatch:int = 0x5A;
177 const OP_findpropstrict:int = 0x5D;
178 const OP_findproperty:int = 0x5E;
179 const OP_finddef:int = 0x5F;
180 const OP_getlex:int = 0x60;
181 const OP_setproperty:int = 0x61;
182 const OP_getlocal:int = 0x62;
183 const OP_setlocal:int = 0x63;
184 const OP_getglobalscope:int = 0x64;
185 const OP_getscopeobject:int = 0x65;
186 const OP_getproperty:int = 0x66;
187 const OP_getouterscope:int = 0x67;
188 const OP_initproperty:int = 0x68;
189 const OP_setpropertylate:int = 0x69;
190 const OP_deleteproperty:int = 0x6A;
191 const OP_deletepropertylate:int = 0x6B;
192 const OP_getslot:int = 0x6C;
193 const OP_setslot:int = 0x6D;
194 const OP_getglobalslot:int = 0x6E;
195 const OP_setglobalslot:int = 0x6F;
196 const OP_convert_s:int = 0x70;
197 const OP_esc_xelem:int = 0x71;
198 const OP_esc_xattr:int = 0x72;
199 const OP_convert_i:int = 0x73;
200 const OP_convert_u:int = 0x74;
201 const OP_convert_d:int = 0x75;
202 const OP_convert_b:int = 0x76;
203 const OP_convert_o:int = 0x77;
204 const OP_checkfilter:int = 0x78;
205 const OP_coerce:int = 0x80;
206 const OP_coerce_b:int = 0x81;
207 const OP_coerce_a:int = 0x82;
208 const OP_coerce_i:int = 0x83;
209 const OP_coerce_d:int = 0x84;
210 const OP_coerce_s:int = 0x85;
211 const OP_astype:int = 0x86;
212 const OP_astypelate:int = 0x87;
213 const OP_coerce_u:int = 0x88;
214 const OP_coerce_o:int = 0x89;
215 const OP_negate:int = 0x90;
216 const OP_increment:int = 0x91;
217 const OP_inclocal:int = 0x92;
218 const OP_decrement:int = 0x93;
219 const OP_declocal:int = 0x94;
220 const OP_typeof:int = 0x95;
221 const OP_not:int = 0x96;
222 const OP_bitnot:int = 0x97;
223 const OP_concat:int = 0x9A;
224 const OP_add_d:int = 0x9B;
225 const OP_add:int = 0xA0;
226 const OP_subtract:int = 0xA1;
227 const OP_multiply:int = 0xA2;
228 const OP_divide:int = 0xA3;
229 const OP_modulo:int = 0xA4;
230 const OP_lshift:int = 0xA5;
231 const OP_rshift:int = 0xA6;
232 const OP_urshift:int = 0xA7;
233 const OP_bitand:int = 0xA8;
234 const OP_bitor:int = 0xA9;
235 const OP_bitxor:int = 0xAA;
236 const OP_equals:int = 0xAB;
237 const OP_strictequals:int = 0xAC;
238 const OP_lessthan:int = 0xAD;
239 const OP_lessequals:int = 0xAE;
240 const OP_greaterthan:int = 0xAF;
241 const OP_greaterequals:int = 0xB0;
242 const OP_instanceof:int = 0xB1;
243 const OP_istype:int = 0xB2;
244 const OP_istypelate:int = 0xB3;
245 const OP_in:int = 0xB4;
246 const OP_increment_i:int = 0xC0;
247 const OP_decrement_i:int = 0xC1;
248 const OP_inclocal_i:int = 0xC2;
249 const OP_declocal_i:int = 0xC3;
250 const OP_negate_i:int = 0xC4;
251 const OP_add_i:int = 0xC5;
252 const OP_subtract_i:int = 0xC6;
253 const OP_multiply_i:int = 0xC7;
254 const OP_getlocal0:int = 0xD0;
255 const OP_getlocal1:int = 0xD1;
256 const OP_getlocal2:int = 0xD2;
257 const OP_getlocal3:int = 0xD3;
258 const OP_setlocal0:int = 0xD4;
259 const OP_setlocal1:int = 0xD5;
260 const OP_setlocal2:int = 0xD6;
261 const OP_setlocal3:int = 0xD7;
262 const OP_debug:int = 0xEF;
263 const OP_debugline:int = 0xF0;
264 const OP_debugfile:int = 0xF1;
265 const OP_bkptline:int = 0xF2;
266 const OP_timestamp:int = 0xF3;
268 /*********************************************************************************
269  * AVM2 assembler for one code block.
271  * This is a lightweight class that is used to emit bytes for
272  * instructions and data, to maintain stack and scope depths,
273  * count local slots used, and to handle branch targets and
274  * backpatching.  It has no code generation logic save for fairly
275  * simple abstractions (eg, I_getlocal() maps to "getlocal_n" or
276  * to the general "getlocal" instruction, depending on its
277  * parameter value).
279  * FIXME:
280  *  - There needs to be a way to set the scope stack depth to 0, to be used
281  *    when generating code for exception handling
282  *  - It would be nice if we could check that every join point has the same
283  *    stack depth, this requires that the next linear instruction following
284  *    an unconditional nonreturning control flow (return, throw, jump) is
285  *    a label always, or is ignored for the purposes of computing the stack
286  *    depth.
287  *  - Ditto for the scope depth, really.
288  */
290 var listify = false;
292 class AVM2Assembler
294     const indent = "        ";
296     private var code = new ABCByteStream;
297     private var nextLabel = 1000;
298     private var backpatches = [];
299     private var current_scope_depth = 0;
300     private var max_scope_depth = 0;
301     private var current_stack_depth = 0;
302     private var max_stack_depth = 0;
303     private var nextTemp;
304     private var freeTemps = [];
305     private var constants;
306     private var set_dxns = false;
307     private var need_activation = false;
308     private var attr = null;
310     function AVM2Assembler(constants, numberOfFormals, attr)
311         : constants = constants
312         , current_scope_depth = 0
313         , attr = attr
314         , nextTemp = numberOfFormals + 1 + (attr.uses_arguments || attr.uses_rest ? 1 : 0) // local 0 is always "this"
315     {
316     }
318     function get maxStack() { return max_stack_depth }
319     function get maxLocal() { return nextTemp }
320     function get maxScope() { return max_scope_depth }
321     function get flags() { 
322         var f = 0;
323         if (set_dxns)
324             f |= METHOD_Setsdxns;
325         if (need_activation)
326             f |= METHOD_Activation;
327         if (attr.uses_arguments)
328             f |= METHOD_Arguments;
329         if (attr.uses_rest)
330             f |= METHOD_Needrest;
331         return f;
332     }
334     private function listL(n) {
335         if (listify)
336             print(n);
337     }
339     private function list1(name) {
340         if (listify)
341             print(indent + name);
342     }
344     private function list2(name, v) {
345         if (listify)
346             print(indent + name + " " + v);
347     }
349     private function list3(name, v1, v2) {
350         if (listify)
351             print(indent + name + " " + v1 + " " + v2);
352     }
354     private function list5(name, v1, v2, v3, v4) {
355         if (listify)
356             print(indent + name + " " + v1 + " " + v2 + " " + v3 + " " + v4);
357     }
358     /*         function listn(name, ...rest) {
359                if (listify)
360                print(indent + name + " " + rest.join(" "));
361                }
362     */
364     // Instructions that push one value, with a single opcode byte
365     private function pushOne(name, opcode) {
366         stack(1);
367         list1(name);
368         code.uint8(opcode);
369     }
371     function I_dup() { pushOne("dup", 0x2A) }
372     function I_getglobalscope() { pushOne("getglobalscope", 0x64) }
373     function I_getlocal_0() { pushOne("getlocal_0", 0xD0) }
374     function I_getlocal_1() { pushOne("getlocal_1", 0xD1) }
375     function I_getlocal_2() { pushOne("getlocal_2", 0xD2) }
376     function I_getlocal_3() { pushOne("getlocal_3", 0xD3) }
377     function I_newactivation() { need_activation=true; pushOne("newactivation", 0x57) }
378     function I_pushfalse() { pushOne("pushfalse", 0x27) }
379     function I_pushnan() { pushOne("pushnan", 0x28) }
380     function I_pushnull() { pushOne("pushnull", 0x20) }
381     function I_pushtrue() { pushOne("pushtrue", 0x26) }
382     function I_pushundefined() { pushOne("pushundefined", 0x21) }
384     // Instructions that push one value, with an opcode byte followed by a u30 argument
385     private function pushOneU30(name, opcode, v) {
386         stack(1);
387         list2(name, v);
388         code.uint8(opcode);
389         code.uint30(v);
390     }
392     function I_getglobalslot(index) { pushOneU30("getglobalslot", 0x6E, index) }
393     function I_getlex(index) { pushOneU30("getlex", 0x60, index) }
394     function I_getscopeobject(index) { pushOneU30("getscopeobject", 0x65, index) }
395     function I_getouterscope(index) { pushOneU30("getouterscope", 0x67, index) }
396     function I_newcatch(index) { pushOneU30("newcatch", 0x5A, index) }
397     function I_newfunction(index) { pushOneU30("newfunction", 0x40, index) }
398     function I_pushdouble(index) { pushOneU30("pushdouble", 0x2F, index) }
399     function I_pushint(index) { pushOneU30("pushint", 0x2D, index) }
400     function I_pushnamespace(index) { pushOneU30("pushnamespace", 0x31, index) }
401     function I_pushshort(v) { pushOneU30("pushshort", 0x25, v) }
402     function I_pushstring(index) { pushOneU30("pushstring", 0x2C, index) }
403     function I_pushuint(index) { pushOneU30("pushuint", 0x2E, index) }
405     // start a catch block.  increments stack by 1 for the exception object
406     function startCatch() { stack(1) }
407         
408     // Instructions that pop one value, with a single opcode byte
409     private function dropOne(name, opcode) {
410         stack(-1);
411         list1(name);
412         code.uint8(opcode);
413     }
415     function I_add() { dropOne("add", 0xA0) }
416     function I_add_i() { dropOne("add_i", 0xC5) }
417     function I_astypelate() { dropOne("astypelate", 0x87) }
418     function I_bitand() { dropOne("bitand", 0xA8) }
419     function I_bitor() { dropOne("bitor", 0xA9) }
420     function I_bitxor() { dropOne("bitxor", 0xAA) }
421     function I_divide() { dropOne("divide", 0xA3) }
422     function I_dxnslate() { set_dxns=true; dropOne("dxnslate", 0x07) }
423     function I_equals() { dropOne("Equals", 0xAB) }
424     function I_greaterequals() { dropOne("greaterequals", 0xB0) }
425     function I_greaterthan() { dropOne("greaterthan", 0xAF) }
426     function I_hasnext() { dropOne("hasnext", 0x1F) }
427     function I_in() { dropOne("in", 0xB4) }
428     function I_instanceof() { dropOne("instanceof", 0xB1) }
429     function I_istypelate() { dropOne("istypelate", 0xB3) }
430     function I_lessequals() { dropOne("lessequals", 0xAE) }
431     function I_lessthan() { dropOne("lessthan", 0xAD) }
432     function I_lshift() { dropOne("lshift", 0xA5) }
433     function I_modulo() { dropOne("modulo", 0xA4) }
434     function I_multiply() { dropOne("multiply", 0xA2) }
435     function I_multiply_i() { dropOne("multiply_i", 0xC7) }
436     function I_nextname() { dropOne("nextname", 0x1E) }
437     function I_nextvalue() { dropOne("nextvalue", 0x23) }
438     function I_pop() { dropOne("pop", 0x29) }
439     function I_pushscope() { scope(1); dropOne("pushscope", 0x30) }
440     function I_pushwith() { scope(1); dropOne("pushwith", 0x1C) }
441     function I_returnvalue() { dropOne("returnvalue", 0x48) }
442     function I_rshift() { dropOne("rshift", 0xA6) }
443     function I_setlocal_0() { dropOne("setlocal_0", 0xD4) }
444     function I_setlocal_1() { dropOne("setlocal_1", 0xD5) }
445     function I_setlocal_2() { dropOne("setlocal_2", 0xD6) }
446     function I_setlocal_3() { dropOne("setlocal_3", 0xD7) }
447     function I_strictequals() { dropOne("strictequals", 0xAC) }
448     function I_subtract() { dropOne("subtract", 0xA1) }
449     function I_subtract_i() { dropOne("subtract_i", 0xC6) }
450     function I_throw() { dropOne("throw", 0x03) }
451     function I_urshift() { dropOne("urshift", 0xA7) }
453     // Instructions that pop one value, with an opcode byte followed by an u30 argument
454     private function dropOneU30(name, opcode, v) {
455         stack(-1);
456         list2(name, v);
457         code.uint8(opcode);
458         code.uint30(v);
459     }
461     function I_setglobalslot(index) { dropOneU30("setglobalslot", 0x6F, index) }
463     // Instructions that do not change the stack height, with a single opcode byte
464     private function dropNone(name, opcode)
465     {
466         //stack(0);
467         list1(name);
468         code.uint8(opcode);
469     }
471     function I_bitnot() { dropNone("bitnot", 0x97) }
472     function I_checkfilter() { dropNone("checkfilter", 0x78) }
473     function I_coerce_a() { dropNone("coerce_a", 0x82) }
474     function I_coerce_s() { dropNone("coerce_s", 0x85) }
475     function I_convert_b() { dropNone("convert_b", 0x76) }
476     function I_convert_d() { dropNone("convert_d", 0x75) }
477     function I_convert_i() { dropNone("convert_i", 0x73) }
478     function I_convert_o() { dropNone("convert_o", 0x77) }
479     function I_convert_s() { dropNone("convert_s", 0x70) }
480     function I_convert_u() { dropNone("convert_u", 0x74) }
481     function I_decrement() { dropNone("decrement", 0x93) }
482     function I_decrement_i() { dropNone("decrement_i", 0xC1) }
483     function I_esc_xattr() { dropNone("esc_xattr", 0x72) }
484     function I_esc_xelem() { dropNone("esc_xattr", 0x71) }
485     function I_increment() { dropNone("increment", 0x91) }
486     function I_increment_i() { dropNone("increment_i", 0xC0) }
487     function I_negate() { dropNone("negate", 0x90) }
488     function I_negate_i() { dropNone("negate_i", 0xC4) }
489     function I_nop() { dropNone("nop", 0x02) }
490     function I_not() { dropNone("not", 0x96) }
491     function I_popscope() { scope(-1); dropNone("popscope", 0x1D) }
492     function I_returnvoid() { dropNone("returnvoid", 0x47) }
493     function I_swap() { dropNone("swap", 0x2B) }
494     function I_typeof() { dropNone("typeof", 0x95) }
496     // Instructions that do not change the stack height, with an opcode byte
497     // followed by a u30 argument
498     private function dropNoneU30(name, opcode, x) {
499         //stack(0)
500         list2(name, x);
501         code.uint8(opcode);
502         code.uint30(x);
503     }
505     function I_astype(index) { dropNoneU30("astype", 0x86, index) }
506     function I_coerce(index) { dropNoneU30("coerce", 0x80, index) }
507     function I_debugfile(index) { dropNoneU30("debugfile", 0xF1, index) }
508     function I_debugline(linenum) { dropNoneU30("debugline", 0xF0, linenum) }
509     function I_declocal(reg) { dropNoneU30("declocal", 0x94, reg) }
510     function I_declocal_i(reg) { dropNoneU30("declocal_i", 0xC3, reg) }
511     function I_dxns(index) { set_dxns=true; dropNoneU30("dxns", 0x06, index) }
512     function I_getslot(index) { dropNoneU30("getslot", 0x6C, index) }
513     function I_inclocal(reg) { dropNoneU30("inclocal", 0x92, reg) }
514     function I_inclocal_i(reg) { dropNoneU30("inclocal_i", 0xC2, reg) }
515     function I_istype(index) { dropNoneU30("istype", 0xB2, index) }
516     function I_kill(index) { dropNoneU30("kill", 0x08, index) }
517     function I_newclass(index) { dropNoneU30("newclass", 0x58, index) }
519     function I_getlocal(index) {
520         switch (index) {
521         case 0: I_getlocal_0(); break;
522         case 1: I_getlocal_1(); break;
523         case 2: I_getlocal_2(); break;
524         case 3: I_getlocal_3(); break;
525         default: pushOneU30("getlocal", 0x62, index);
526         }
527     }
529     function I_setlocal(index) {
530         switch (index) {
531         case 0: I_setlocal_0(); break;
532         case 1: I_setlocal_1(); break;
533         case 2: I_setlocal_2(); break;
534         case 3: I_setlocal_3(); break;
535         default: dropOneU30("setlocal", 0x63, index);
536         }
537     }
539     // Local control flow instructions and I_label():
540     //  - If called without an argument return a "label" that can later be
541     //    passed to I_label() to give the label an actual value.
542     //  - If called with an argument, the argument must have been returned
543     //    from a control flow instruction or from I_label().  It represents
544     //    a transfer target.
545     //
546     // A "label" is a data structure with these fields:
547     //  - name (uint): a symbolic name for the label, to be used in listings
548     //  - address (int): either -1 for "unknown" or the address of the label
549     //  - stack (uint): the stack depth at label creation time; this is the
550     //        stack depth at the target too [except for exception handling]
551     //  - scope (uint): the scope stack depth at label creation time; this is the
552     //        scope stack depth at the target too [except for exception handling]
553     //
554     // The method newLabel() can be called to return a label that
555     // will later be defined by I_label and referenced by control
556     // flow instructions, without creating a jump instruction at
557     // the point where the label is created.  Typically this is
558     // used to create branch targets for "break" and "continue".
560     function newLabel() {
561         return { "name": nextLabel++, "address": -1, "stack": current_stack_depth, "scope": current_scope_depth };
562     }
564     private function relativeOffset(base, L) {
565         if (L.address != -1)
566             code.int24(L.address - base);
567         else {
568             backpatches.push({ "loc": code.length, "base": base, "label": L });
569             code.int24(0);
570         }
571     }
573     private function jmp(stk, name, opcode, L) {
574         stack(stk);
576         if (L === undefined)
577             L = newLabel();
579         list2(name, L.name);
580         code.uint8(opcode);
581         relativeOffset(code.length+3, L);
583         return L;
584     }
586     function I_label(L) {
587         var here = code.length;
588         var define = false;
589         if (L === undefined) {
590             define = true;
591             L = newLabel();
592         }
593         else {
594             Util::assert( L.address == -1 );
595             current_stack_depth = L.stack;
596             current_scope_depth = L.scope;
597         }
598         L.address = here;
599         listL(L.name + ":   -- " + L.stack + "/" + L.scope);
600         if (define) {
601             code.uint8(0x09);
602             list1("label");
603         }
604         return L;
605     }
607     function I_ifeq(L) { return jmp(-2, "ifeq", 0x13, L) }
608     function I_ifge(L) { return jmp(-2, "ifge", 0x18, L) }
609     function I_ifgt(L) { return jmp(-2, "ifgt", 0x17, L) }
610     function I_ifle(L) { return jmp(-2, "ifle", 0x16, L) }
611     function I_iflt(L) { return jmp(-2, "iflt", 0x15, L) }
612     function I_ifne(L) { return jmp(-2, "ifne", 0x14, L) }
613     function I_ifnge(L) { return jmp(-2, "ifnge", 0x0F, L) }
614     function I_ifngt(L) { return jmp(-2, "ifngt", 0x0E, L) }
615     function I_ifnle(L) { return jmp(-2, "ifnle", 0x0D, L) }
616     function I_ifnlt(L) { return jmp(-2, "ifnlt", 0x0C, L) }
617     function I_ifstricteq(L) { return jmp(-2, "ifstricteq", 0x19, L) }
618     function I_ifstrictne(L) { return jmp(-2, "ifstrictne", 0x1A, L) }
620     function I_iffalse(L) { return jmp(-1, "iffalse", 0x12, L) }
621     function I_iftrue(L) { return jmp(-1, "iftrue", 0x11, L) }
623     function I_jump(L) { return jmp(0, "jump", 0x10, L) }
625     // Here, case_labels must be an array with a "length" property
626     // that denotes the number of case labels in the array.
627     // length cannot be 0.
628     //
629     // Either default_label is undefined and all the elements of
630     // case_labels are also undefined, or default_label is a label
631     // structure, and all the elements of case_labels between 0
632     // and length-1 are label structures as well.
633     //
634     // In the former case, labels are created for the
635     // default_label and for all the case_labels; the array is
636     // updated; and the new default_label is returned.
638     function I_lookupswitch(default_label, case_labels) {
639         Util::assert( case_labels.push ); /*FIXME ES4: really "case_labels is Array" */
640         Util::assert( case_labels.length > 0 );
642         stack(-1);
644         if (default_label === undefined) {
645             default_label = newLabel();
646             for ( var i=0, limit=case_labels.length ; i < limit ; i++ ) {
647                 Util::assert( case_labels[i] === undefined );
648                 case_labels[i] = newLabel();
649             }
650         }
652         function map_func(L) { return L.name };
653         list3("lookupswitch", default_label.name, Util::map(map_func, case_labels));
654         var base = code.length;
655         code.uint8(0x1B);
656         relativeOffset(base, default_label);
657         code.uint30(case_labels.length-1);
658         for ( var i=0, limit=case_labels.length ; i < limit ; i++ )
659             relativeOffset(base, case_labels[i]);
661         return default_label;
662     }
664     // Standard function calls
665     private function call(name, opcode, nargs) {
666         stack(1-(nargs+2)); /* pop function/receiver/args; push result */
667         list2(name, nargs);
668         code.uint8(opcode);
669         code.uint30(nargs);
670     }
672     private function construct(name, opcode, nargs) {
673         stack(1-(nargs+1)); /* pop function/receiver/args; push result */
674         list2(name, nargs);
675         code.uint8(opcode);
676         code.uint30(nargs);
677     }
679     function I_call(nargs) { call("call", 0x41, nargs) }
680     function I_construct(nargs) { construct("construct", 0x42, nargs) }
682     function I_constructsuper(nargs) {
683         stack(nargs+1); /* pop receiver/args */
684         list2("constructsuper", nargs);
685         code.uint8(0x49);
686         code.uint30(nargs);
687     }
689     private function callIDX(name, opcode, index, nargs) {
690         stack(1-(nargs+1)); /* pop receiver/args; push result */
691         list3(name, index, nargs);
692         code.uint8(opcode);
693         code.uint30(index);
694         code.uint30(nargs);
695     }
697     function I_callmethod(index, nargs) { callIDX("callmethod", 0x43, index, nargs) }
698     function I_callstatic(index, nargs) { callIDX("callstatic", 0x44, index, nargs) }
700     private function callMN(name, opcode, index, nargs, isVoid) {
701         /* pop receiver/NS?/Name?/args; push result? */
702         var hasRTNS = constants.hasRTNS(index);
703         var hasRTName = constants.hasRTName(index);
704         stack((isVoid ? 0 : 1) - (1 + (hasRTNS ? 1 : 0) + (hasRTName ? 1 : 0) + nargs));
705         list3(name + (hasRTNS ? "<NS>" : "") + (hasRTName ? "<Name>" : ""), index, nargs);
706         code.uint8(opcode);
707         code.uint30(index);
708         code.uint30(nargs);
709     }
711     function I_callsuper(index, nargs) { callMN("callsuper", 0x45, index, nargs, false) }
712     function I_callproperty(index, nargs) { callMN("callproperty", 0x46, index, nargs, false) }
713     function I_constructprop(index, nargs) { callMN("constructprop", 0x4A, index, nargs, false) }
714     function I_callproplex(index, nargs) { callMN("callproplex", 0x4C, index, nargs, false) }
715     function I_callsupervoid(index, nargs) { callMN("callsupervoid", 0x4E, index, nargs, true) }
716     function I_callpropvoid(index, nargs) { callMN("callpropvoid", 0x4F, index, nargs, true) }
718     function I_debug(debug_type, index, reg, extra=0) {
719         //stack(0);
720         list5("debug", debug_type, index, reg, extra);
721         code.uint8(0xEF);
722         code.uint8(debug_type);
723         code.uint30(index);
724         code.uint8(reg);
725         code.uint30(extra);
726     }
728     /* Generic property operation when there may be a namespace or
729        name on the stack.  The instruction pops and pushes some
730        fixed amount and may pop one or two more items, depending
731        on the kind of name that index references.
732     */
733     private function propU30(name, pops, pushes, opcode, index) {
734         var hasRTNS = constants.hasRTNS(index);
735         var hasRTName = constants.hasRTName(index);
736         stack(pushes - (pops + (hasRTNS ? 1 : 0) + (hasRTName ? 1 : 0)));
737         list2(name + (hasRTNS ? "<NS>" : "") + (hasRTName ? "<Name>" : ""), index);
738         code.uint8(opcode);
739         code.uint30(index);
740     }
742     function I_deleteproperty(index) { propU30("deleteproperty", 1, 1, 0x6A, index) }
743     function I_getdescendants(index) { propU30("getdescendants", 1, 1, 0x59, index) }
744     function I_getproperty(index) { propU30("getproperty", 1, 1, 0x66, index); }
745     function I_getsuper(index) { propU30("getsuper", 1, 1, 0x04, index); }
746     function I_findproperty(index) { propU30("findproperty", 0, 1, 0x5E, index) }
747     function I_findpropstrict(index) { propU30("findpropstrict", 0, 1, 0x5D, index) }
748     function I_initproperty(index) { propU30("initproperty", 2, 0, 0x68, index) }
749     function I_setproperty(index) { propU30("setproperty", 2, 0, 0x61, index) }
750     function I_setsuper(index) { propU30("setsuper", 2, 0, 0x05, index) }
752     function I_hasnext2(object_reg, index_reg) {
753         stack(1);
754         code.uint8(0x32);
755         code.uint30(object_reg);
756         code.uint30(index_reg);
757     }
759     function I_newarray(nargs) {
760         stack(1 - nargs);
761         list2("newarray", nargs);
762         code.uint8(0x56);
763         code.uint30(nargs);
764     }
766     function I_newobject(nargs) {
767         stack(1 - (2 * nargs));
768         list2("newobject", nargs);
769         code.uint8(0x55);
770         code.uint30(nargs);
771     }
773     function I_pushbyte(b) {
774         stack(1);
775         list2("pushbyte", b);
776         code.uint8(0x24);
777         code.uint8(b);
778     }
780     function I_setslot(index) {
781         stack(-2);
782         list2("setslot", index);
783         code.uint8(0x6D);
784         code.uint30(index);
785     }
787     function getTemp() {
788         if (freeTemps.length > 0)
789             return freeTemps.pop();
790         else
791             return nextTemp++;
792     }
794     function killTemp(t) {
795         freeTemps.push(t);
796         I_kill(t);
797     }
799     function get length() {
800         return code.length;
801     }
803     function finalize() {
804         resolveBackpatches();
805         return code;
806     }
808     private function resolveBackpatches() {
809         for ( var i=0, limit=backpatches.length ; i < limit ; i++ ) {
810             var bp = backpatches[i];
811             if (bp.label.address == -1)
812                 Util::internalError("", 0, "Missing definition for label " + bp.label.name); // FIXME: source pos
813             var v = bp.label.address - bp.base;
814             code.setInt24(bp.loc, v);
815         }
816         backpatches.length = 0;
817     }
819     private function stack(n) {
820         current_stack_depth = current_stack_depth + n;
821         if (current_stack_depth > max_stack_depth) {
822             max_stack_depth = current_stack_depth;
823         }
824     }
826     private function scope(n) {
827         current_scope_depth = current_scope_depth + n;
828         if (current_scope_depth > max_scope_depth)
829             max_scope_depth = current_scope_depth;
830     }