KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openlaszlo > sc > Instructions


1 /* -*- mode: JDE; c-basic-offset: 2; -*- */
2
3 /***
4  * Instructions.java
5  *
6  * Description: SWF Action Code instruction opcode constants
7  */

8
9 /* J_LZ_COPYRIGHT_BEGIN *******************************************************
10 * Copyright 2001-2004 Laszlo Systems, Inc. All Rights Reserved. *
11 * Use is subject to license terms. *
12 * J_LZ_COPYRIGHT_END *********************************************************/

13
14 package org.openlaszlo.sc;
15 import java.io.*;
16 import java.nio.*;
17 import java.util.*;
18 import org.openlaszlo.sc.Actions.Action;
19 import org.openlaszlo.sc.Values.*;
20
21 public class Instructions {
22   private static String JavaDoc runtime;
23
24   public static void setRuntime(String JavaDoc target) {
25     runtime = target.intern();
26   }
27
28   public static String JavaDoc getRuntime() {
29     return runtime;
30   }
31   // Reverse lookup, for disassembly and compiling assembler
32

33   public static Map ActionNames;
34
35   // Action -> String
36
public static String JavaDoc actionName(Action op) {
37     if (ActionNames == null) {
38       HashMap names = new HashMap();
39       // TODO [2004-03-01 ptw] Surely there is a better way to iterate
40
// over the entries of a hash table?
41
for (Iterator i = Actions.items().iterator(); i.hasNext(); ) {
42         Action value = (Action)((Map.Entry)i.next()).getValue();
43         String JavaDoc name = value.name;
44         if (name.equals(name.toUpperCase())) {
45           name = name.toLowerCase();
46         } else {
47           name = name.substring(0, 1).toLowerCase() + name.substring(1);
48         }
49         names.put(value, name);
50       }
51       ActionNames = names;
52     }
53     String JavaDoc name = (String JavaDoc)ActionNames.get(op);
54     return name;
55   }
56
57   /*
58    * Instructions
59    *
60    * The code generator creates instructions, which are passed to the
61    * assembler for pretty-printing or bytecode generation. Each
62    * instruction knows how to print itself, and how to add its bytes to a
63    * byte array.
64    */

65
66   public static Map NameInstruction = new HashMap();
67
68   // TODO [2004-03-01 ptw] Surely there is a better way
69
public static List items() {
70     List l = new ArrayList();
71     for (Iterator i = NameInstruction.entrySet().iterator(); i.hasNext(); ) {
72         Instruction value = (Instruction)((Map.Entry)i.next()).getValue();
73         l.add(value);
74     }
75     return l;
76   }
77
78   /***
79    * Registers
80    */

81   public static class Register implements Serializable {
82     public String JavaDoc name;
83     public byte regno;
84
85     public static Set AUTO_REG = new LinkedHashSet();
86     static {
87       AUTO_REG.add("this");
88       AUTO_REG.add("arguments");
89       AUTO_REG.add("super");
90       AUTO_REG.add("_root");
91       AUTO_REG.add("_parent");
92       AUTO_REG.add("_global");
93     }
94
95     public Register(String JavaDoc name) {
96       this.name = name;
97       this.regno = (byte)-1;
98     }
99
100     static public Register make(String JavaDoc name) {
101       if (AUTO_REG.contains(name)) {
102         return new AutoRegister(name);
103       } else {
104         return new Register(name);
105       }
106     }
107
108     public String JavaDoc toString() {
109       return "r:" + regno + "='" + name + "'";
110     }
111   }
112
113   // Hard-wired registers that are automatically filled in,
114
// according to the regflags
115
public static class AutoRegister extends Register {
116     public short flag;
117     public short notFlag;
118
119     public static Map FLAGS = new HashMap();
120     static {
121       FLAGS.put("this", new Integer JavaDoc(0x1));
122       FLAGS.put("arguments", new Integer JavaDoc(0x4));
123       FLAGS.put("super", new Integer JavaDoc(0x10));
124       FLAGS.put("_root", new Integer JavaDoc(0x40));
125       FLAGS.put("_parent", new Integer JavaDoc(0x80));
126       FLAGS.put("_global", new Integer JavaDoc(0x100));
127     }
128
129     public static Map NOT_FLAGS = new HashMap();
130     static {
131       NOT_FLAGS.put("this", new Integer JavaDoc(0x2));
132       NOT_FLAGS.put("arguments", new Integer JavaDoc(0x8));
133       NOT_FLAGS.put("super", new Integer JavaDoc(0x20));
134       NOT_FLAGS.put("_root", new Integer JavaDoc(0x0));
135       NOT_FLAGS.put("_parent", new Integer JavaDoc(0x0));
136       NOT_FLAGS.put("_global", new Integer JavaDoc(0x0));
137     }
138
139     // TODO: [2004-03-29 ptw] These flags turn off the creation of
140
// this, arguments, and super in the activation record if they are
141
// not used in the function body. Need to compute that from the
142
// free references in the unlikely case that one of these is not
143
// registered but still used (e.g., closed over).
144
public static short DEFAULT_FLAGS = (short)(0x2 | 0x8 | 0x20);
145
146     public AutoRegister(String JavaDoc name) {
147       super(name);
148       flag = ((Integer JavaDoc)FLAGS.get(name)).shortValue();
149       notFlag = ((Integer JavaDoc)NOT_FLAGS.get(name)).shortValue();
150     }
151   }
152
153
154   /***
155    * The abstract superclass of instructions. Subclasses are
156    * ConcreteInstruction and PseudoInstruction.
157    */

158   public abstract static class Instruction implements Serializable {
159     protected static final boolean[] curriedInstructions = new boolean[256];
160     
161     // default is to return null, meaning no information
162
public StackModel updateStackModel(StackModel model) {
163       return null;
164     }
165
166     public static Instruction curry(Action op) {
167       Instruction inst;
168       if (op == Actions.PUSH) {
169         inst = new PUSHInstruction(op);
170       } else if (op == Actions.DefineFunction) {
171         inst = new DefineFunctionInstruction(op);
172       } else if (op == Actions.DefineFunction2) {
173         inst = new DefineFunction2Instruction(op);
174       } else if (TargetInstruction.OPCODES.contains(op)) {
175         inst = new TargetInstruction(op);
176       } else {
177         inst = new ConcreteInstruction(op);
178       }
179       NameInstruction.put(actionName(op), inst);
180       curriedInstructions[Actions.opcodeIndex(op.opcode)] = true;
181       return inst;
182     }
183
184     public abstract void writeBytes(ByteBuffer bytes, Map constants);
185
186     // Python interface(s)
187
public boolean getHasTarget() {
188       return this instanceof TargetInstruction;
189     }
190
191     public boolean getIsLabel() {
192       return this instanceof LABELInstruction;
193     }
194
195     public boolean getIsPush() {
196       return this instanceof PUSHInstruction;
197     }
198
199     public boolean getIsBranchIfFalse() {
200       return this instanceof BranchIfFalseInstruction;
201     }
202
203     public Instruction __findattr__(String JavaDoc name) {
204       if (NameInstruction.containsKey(name)) {
205         return (Instruction)NameInstruction.get(name);
206       }
207       return null;
208     }
209
210     public abstract Instruction __call__();
211
212     public abstract Instruction __call__(Object JavaDoc arg);
213
214     public abstract Instruction __call__(Object JavaDoc[] args);
215   }
216
217   /***
218    * Represents an instruction backed by bytecode.
219    */

220   public static class ConcreteInstruction extends Instruction {
221     public Action op;
222     public List args;
223
224     protected ConcreteInstruction(Action op) {
225       this(op, null);
226     }
227
228     protected ConcreteInstruction(Action op, List args) {
229       super();
230       this.op = op;
231       // Copy the arglist so it can be munged (e.g., push merging)
232
if (args != null) this.args = new ArrayList(args);
233     }
234
235     public Object JavaDoc readResolve() {
236         if (curriedInstructions[Actions.opcodeIndex(op.opcode)] && this.args == null)
237             return (Instruction) NameInstruction.get(actionName(op));
238         return this;
239     }
240     
241     public Instruction __call__() {
242       assert (! this.op.args);
243       return this;
244     }
245
246     public Instruction __call__(Object JavaDoc arg) {
247       assert this.op.args;
248       return new ConcreteInstruction(this.op, Collections.singletonList(arg));
249     }
250
251     public Instruction __call__(Object JavaDoc[] args) {
252       assert this.op.args;
253       return new ConcreteInstruction(this.op, Arrays.asList(args));
254     }
255
256     public String JavaDoc toString() {
257       Action op = this.op;
258       String JavaDoc s = actionName(op);
259       if (this.args != null) {
260         s += " " + this.argsString();
261       }
262       return s;
263     }
264
265     public StackModel updateStackModel(StackModel model) {
266       Action op = this.op;
267       int arity = op.arity;
268       int returns = op.returns;
269       // Compute the arity for vararg instructions from the stack model
270
// TODO: [2002-11-24 ptw] separate classes rather than re-dispatching
271
if (op == Actions.CallFunction ||
272           op == Actions.NEW) {
273           if (model.size() >= 2 && Values.isInteger(model.get(-2))) {
274             arity = ((Number JavaDoc)model.get(-2)).intValue() + 2;
275             returns = 1;
276           } else {
277             arity = -1;
278             returns = -1;
279           }
280       } else if (op == Actions.CallMethod ||
281                  op == Actions.NewMethod) {
282           if (model.size() >= 3 && Values.isInteger(model.get(-3))) {
283             arity = ((Number JavaDoc)model.get(-3)).intValue() + 3;
284             returns = 1;
285           } else {
286             arity = -1;
287             returns = -1;
288           }
289       } else if (op == Actions.InitArray) {
290           if (model.size() >=1 && Values.isInteger(model.get(-1))) {
291             arity = ((Number JavaDoc)model.get(-1)).intValue() + 1;
292             returns = 1;
293           } else {
294             arity = -1;
295             returns = -1;
296           }
297       } else if (op == Actions.InitObject) {
298           if (model.size() >=1 && Values.isInteger(model.get(-1))) {
299             arity = ((Number JavaDoc)model.get(-1)).intValue() * 2 + 1;
300             returns = 1;
301           } else {
302             arity = -1;
303             returns = -1;
304           }
305       }
306       // System.out.println(this.toString() + " arity: " + arity + " returns: " + returns);
307
// Adjust the stack model according to the instruction arity
308
if (arity != -1 && returns != -1) {
309         // TODO [2002-12-24 ptw] For now, we just invalidate any
310
// information in the model. We could interpret some
311
// instructions to update the model, but it is only used
312
// for calls right now
313
if (model.size() >= arity) {
314           model.removeRange(( - arity), model.size());
315           model.addAll(Collections.nCopies(returns, null));
316           return model;
317         } else {
318           // Needed operands outside of this block
319
return null;
320         }
321       } else {
322         return null;
323       }
324     }
325
326     public String JavaDoc argsString() {
327       Action op = this.op;
328       if (op == Actions.SetRegister) {
329         return "r:" + this.args.get(0).toString();
330       } else {
331         StringBuffer JavaDoc s = new StringBuffer JavaDoc();
332         for (Iterator i = this.args.iterator(); i.hasNext(); ) {
333           Object JavaDoc next = i.next();
334           s.append("'" + next.toString() + "'");
335           if (i.hasNext()) s.append(" ");
336         }
337         return s.toString();
338       }
339     }
340
341     /***
342      * Appends the bytecode to bytes, resolving constant references
343      * against constants.
344      */

345     public void writeBytes(ByteBuffer bytes, Map constants) {
346       bytes.order(ByteOrder.LITTLE_ENDIAN);
347       assert bytes.order() == ByteOrder.LITTLE_ENDIAN;
348       bytes.put(this.op.opcode);
349       // Flash has two types of instructions: single-byte instructions,
350
// which have their low bit set, and multi-byte instructions, in which
351
// the opcode byte is followed by a two-byte length word.
352
if ((this.op.opcode & 0x80) != 0) {
353         int offset = bytes.position();
354         bytes.putShort((short)0);
355         if (this.op.args) {
356           this.writeArgs(bytes, constants);
357           int size = bytes.position() - offset - 2;
358           if (size > ((1<<16)-1)) {
359               throw new CompilerException("offset out of range in " + this);
360           }
361           bytes.putShort(offset, (short)size);
362         }
363       }
364     }
365
366     public void writeArgs(ByteBuffer bytes, Map constants) {
367       Action op = this.op;
368       List args = this.args;
369       if (op == Actions.CONSTANTS) {
370         int n = args.size();
371         if (n > ((1<<16)-1)) {
372             throw new CompilerException("too many arguments in " + this);
373         }
374         bytes.putShort((short)n);
375         try {
376           for (Iterator i = args.iterator(); i.hasNext(); ) {
377             String JavaDoc encoding = "UTF-8";
378             bytes.put(((String JavaDoc)i.next()).getBytes(encoding));
379             bytes.put((byte)0);
380           }
381         } catch (UnsupportedEncodingException e) {
382           assert false : "this can't happen";
383         }
384       } else if (op == Actions.SetRegister) {
385         Integer JavaDoc regno = ((Integer JavaDoc)args.get(0));
386         if (regno.intValue() != regno.byteValue()) {
387           throw new CompilerException("invalid register number");
388         }
389         bytes.put(regno.byteValue());
390       } else {
391         throw new CompilerException("unimplemented: code generation for " + this.toString());
392       }
393     }
394
395     // This is an upper bound estimate for most instructions
396
public int argsBytes() {
397       Action op = this.op;
398       if (op == Actions.CONSTANTS) {
399         int b = 2;
400         for (Iterator i = this.args.iterator(); i.hasNext(); ) {
401           b += ((String JavaDoc)i.next()).length() + 1;
402         }
403         return b;
404       } else if (op == Actions.SetRegister) {
405         return 1;
406       } else {
407         return 0;
408       }
409     }
410   }
411
412
413   /***
414    * Target instructions have a target. During code generation, this is
415    * a label (an arbitrary object, currently a string). During assembly,
416    * this is replaced by an integer offset.
417    */

418
419   public static class TargetInstruction extends ConcreteInstruction {
420     public short targetOffset;
421
422     // These are all the actions that have this type.
423
public static Set OPCODES = new HashSet();
424     static {
425       OPCODES.add(Actions.BRANCH);
426       OPCODES.add(Actions.BranchIfTrue);
427       OPCODES.add(Actions.DefineFunction);
428       OPCODES.add(Actions.WITH);
429       OPCODES.add(Actions.DefineFunction2);
430     }
431
432     protected TargetInstruction(Action op) {
433       this(op, null);
434     }
435
436     protected TargetInstruction(Action op, List args) {
437       this(op, args, (short)0);
438     }
439
440     protected TargetInstruction(Action op, List args, short targetOffset) {
441       super(op, args);
442       this.targetOffset = targetOffset;
443     }
444
445     public TargetInstruction makeTargetInstruction(List args) {
446       return new TargetInstruction(this.op, args);
447     }
448
449     public Instruction __call__(Object JavaDoc arg) {
450       return makeTargetInstruction(Collections.singletonList(arg));
451     }
452
453     public Instruction __call__(Object JavaDoc[] args) {
454       return makeTargetInstruction(Arrays.asList(args));
455     }
456
457     public Object JavaDoc getTarget() {
458       return this.args.get(0);
459     }
460
461     public TargetInstruction replaceTarget(Object JavaDoc target) {
462       TargetInstruction replace = makeTargetInstruction(this.args);
463       replace.args.set(0, target);
464       return replace;
465     }
466
467     public void writeArgs(ByteBuffer bytes, Map pool) {
468       bytes.putShort(this.targetOffset);
469     }
470
471     public int argsBytes() {
472       return 2;
473     }
474   }
475
476
477   public static class DefineFunctionInstruction extends TargetInstruction {
478
479     protected DefineFunctionInstruction(Action op) {
480       this(op, null);
481     }
482
483     protected DefineFunctionInstruction(Action op, List args) {
484       this(op, args, (short)0);
485     }
486
487     protected DefineFunctionInstruction(Action op, List args, short targetOffset) {
488       super(op, args);
489       assert op == Actions.DefineFunction;
490       this.targetOffset = targetOffset;
491     }
492
493     public TargetInstruction makeTargetInstruction(List args) {
494       return new DefineFunctionInstruction(Actions.DefineFunction, args);
495     }
496
497     public void writeArgs(ByteBuffer bytes, Map pool) {
498       try {
499         List args = this.args;
500         String JavaDoc fname = (String JavaDoc)args.get(1);
501         if (fname == null) fname = "";
502         List fnargs = args.subList(2, args.size());
503         int nargs = fnargs.size();
504         bytes.put(fname.getBytes("UTF-8"));
505         bytes.put((byte)0);
506         if (nargs != (short)nargs) {
507           throw new CompilerException("too many arguments");
508         }
509         bytes.putShort((short)nargs);
510         for (Iterator i = fnargs.iterator(); i.hasNext(); ) {
511           bytes.put(((String JavaDoc)i.next()).getBytes("UTF-8"));
512           bytes.put((byte)0);
513         }
514         bytes.putShort(this.targetOffset);
515       } catch (UnsupportedEncodingException e) {
516         assert false : "this can't happen";
517       }
518     }
519
520     public int argsBytes() {
521       List args = this.args;
522       String JavaDoc fname = (String JavaDoc)args.get(1);
523       if (fname == null) fname = "";
524       args = args.subList(2, args.size());
525       int b = fname.length() + 1 + 2; // Geez, why not just size!?!?
526
for (Iterator i = args.iterator(); i.hasNext(); ) {
527         b += ((String JavaDoc)i.next()).length() + 1;
528       }
529       b += 2;
530       return b;
531     }
532
533     public String JavaDoc toString() {
534       StringBuffer JavaDoc b = new StringBuffer JavaDoc();
535       b.append("function ");
536       Object JavaDoc name = this.args.get(1);
537       if (name != null) {
538         b.append(name.toString());
539       }
540       b.append("(");
541       List args = this.args.subList(2, this.args.size());
542       for (Iterator i = args.iterator(); i.hasNext(); ) {
543         Object JavaDoc next = i.next();
544         // --- better way to escape strings?
545
if (next instanceof String JavaDoc) {
546           b.append("'" + next.toString() + "'");
547         } else {
548           b.append(next.toString());
549         }
550         if (i.hasNext()) b.append(", ");
551       }
552       b.append(")");
553       return b.toString();
554     }
555   }
556
557
558   /*
559    * Flash 7 DefineFunction2 Instruction
560    *
561    * Similar to function, but parameters can be assigned to
562    * 'registers'
563    * Output format: string name, short nargs, byte nregs, short
564    * regflags, [byte regno, string arg]*, short ninstrs
565    */

566   public static class DefineFunction2Instruction extends DefineFunctionInstruction {
567
568     protected DefineFunction2Instruction(Action op) {
569       this(op, null);
570     }
571
572     protected DefineFunction2Instruction(Action op, List args) {
573       this(op, args, (short)0);
574     }
575
576     protected DefineFunction2Instruction(Action op, List args, short targetOffset) {
577       super(op, args);
578       assert op == Actions.DefineFunction2;
579       this.targetOffset = targetOffset;
580     }
581
582     public TargetInstruction makeTargetInstruction(List args) {
583       return new DefineFunction2Instruction(Actions.DefineFunction2, args);
584     }
585
586     public void writeArgs(ByteBuffer bytes, Map pool) {
587       try {
588         List args = this.args;
589         String JavaDoc fname = (String JavaDoc)args.get(1);
590         if (fname == null) fname = "";
591         byte nregs = ((Integer JavaDoc)args.get(2)).byteValue();
592         List fnargs = args.subList(3, args.size());
593         // string name
594
bytes.put(fname.getBytes("UTF-8"));
595         bytes.put((byte)0);
596         // short nargs (will be back-patched)
597
short nargs = 0;
598         int nargsPos = bytes.position();
599         bytes.putShort(nargs);
600         // byte nregs
601
bytes.put(nregs);
602         // short regflags (will be back-patched)
603
short regflags = AutoRegister.DEFAULT_FLAGS;
604         int regflagsPos = bytes.position();
605         bytes.putShort((short)0);
606         for (Iterator i = fnargs.iterator(); i.hasNext(); ) {
607           Object JavaDoc arg = i.next();
608           if (arg instanceof Register) {
609             Register reg = (Register)arg;
610             nregs++;
611             if (reg instanceof AutoRegister) {
612               AutoRegister areg = (AutoRegister)reg;
613               regflags &= ~(areg.notFlag);
614               regflags |= areg.flag;
615             } else {
616               nargs++;
617               // byte regno
618
bytes.put(reg.regno);
619               // string arg
620
bytes.put(reg.name.getBytes("UTF-8"));
621               bytes.put((byte)0);
622             }
623           } else if (arg instanceof String JavaDoc) {
624             nargs++;
625             // byte regno
626
// 0 means not registered
627
bytes.put((byte)0);
628             // string arg
629
bytes.put(((String JavaDoc)arg).getBytes("UTF-8"));
630             bytes.put((byte)0);
631           } else {
632             System.out.println("Unknown arg: " + arg);
633           }
634         }
635         // Backpatch nregs and flags
636
bytes.putShort(nargsPos, nargs);
637         bytes.putShort(regflagsPos, regflags);
638         // short ninstrs
639
bytes.putShort(this.targetOffset);
640       } catch (UnsupportedEncodingException e) {
641         assert false : "this can't happen";
642       }
643     }
644
645
646     public int argsBytes() {
647       List args = this.args;
648       String JavaDoc fname = (String JavaDoc)args.get(1);
649       if (fname == null) fname = "";
650       args = args.subList(2, args.size());
651       int b = fname.length() + 1 + 2 + 1 + 2; // Geez, why not just size!?!?
652
for (Iterator i = args.iterator(); i.hasNext(); ) {
653         Object JavaDoc arg = i.next();
654         if (arg instanceof Register) {
655           if (arg instanceof AutoRegister) {
656             ;
657           } else {
658             b += 1+ ((Register)arg).name.length() + 1;
659           }
660         } else {
661           b += ((String JavaDoc)arg).length() + 1;
662         }
663       }
664       b += 2;
665       return b;
666     }
667
668     public String JavaDoc toString() {
669       StringBuffer JavaDoc b = new StringBuffer JavaDoc();
670       b.append("function2 ");
671       Object JavaDoc name = this.args.get(1);
672       if (name != null) {
673         b.append(name.toString());
674       }
675       List args = this.args.subList(3, this.args.size());
676       b.append("(");
677       for (Iterator i = args.iterator(); i.hasNext(); ) {
678         Object JavaDoc next = i.next();
679         if (next instanceof AutoRegister) {
680           continue;
681         }
682         if (next instanceof String JavaDoc) {
683           // --- better way to escape strings?
684
b.append("'" + next.toString() + "'");
685         } else {
686           b.append(next.toString());
687         }
688         if (i.hasNext()) b.append(", ");
689       }
690       b.append(") (");
691       for (Iterator i = args.iterator(); i.hasNext(); ) {
692         Object JavaDoc next = i.next();
693         boolean firsttime = true;
694         if (next instanceof AutoRegister) {
695           if (firsttime) {
696             firsttime = false;
697           } else {
698             b.append(", ");
699           }
700           b.append(next.toString());
701         }
702       }
703       b.append(")");
704       return b.toString();
705     }
706   }
707
708   public static class PUSHInstruction extends ConcreteInstruction {
709     private int cachedArgsBytes;
710
711     protected PUSHInstruction(Action op) {
712       this(op, null);
713     }
714
715     protected PUSHInstruction(Action op, List args) {
716       super(Actions.PUSH, args);
717       assert op == Actions.PUSH;
718       this.cachedArgsBytes = -1;
719     }
720
721     public PUSHInstruction(List args) {
722       this(Actions.PUSH, args);
723     }
724
725     // Python interfaces
726
public Instruction __call__(Object JavaDoc arg) {
727       return new PUSHInstruction(Collections.singletonList(arg));
728     }
729
730     public Instruction __call__(Object JavaDoc[] args) {
731       return new PUSHInstruction(Arrays.asList(args));
732     }
733
734     public boolean isVolatile() {
735       for (Iterator i = this.args.iterator(); i.hasNext(); ) {
736         if (Values.isRegister(i.next())) {
737           return true;
738         }
739       }
740       return false;
741     }
742
743     public int argsBytes() {
744       if (this.cachedArgsBytes == -1) {
745         this.cachedArgsBytes = this.computeArgsBytes(this.args);
746       }
747       return this.cachedArgsBytes;
748     }
749
750     public StackModel updateStackModel(StackModel model) {
751       model.notePush(this, this.argsModel());
752       return model;
753     }
754
755     // Return the args in a format for the stack model. Only
756
// numbers and strings are modelled (for vararg instructions)
757
private List argsModel() {
758       List model = new ArrayList();
759       for (Iterator i = this.args.iterator(); i.hasNext(); ) {
760         Object JavaDoc v = i.next();
761         if (v instanceof String JavaDoc ||
762             v instanceof Number JavaDoc) {
763           model.add(v);
764         } else {
765           model.add(null);
766         }
767       }
768       return model;
769     }
770
771     public boolean merge(PUSHInstruction other, StackModel model) {
772       // Limit of multi-byte instruction length
773
if (this.argsBytes() + other.argsBytes() >= 65536) {
774         return false;
775       }
776       // We know they are cached now
777
this.cachedArgsBytes += other.cachedArgsBytes;
778       // Merged args are inserted after the args that are still on
779
// the stack
780
int i = model.pushDepth(this);
781       this.args.addAll(i, other.args);
782       model.notePush(this, other.argsModel());
783       return true;
784     }
785
786     // Can only make this optimization if the last value is still on
787
// the stack
788
public boolean dup(StackModel model) {
789       int i = model.pushDepth(this);
790       if (i > 0) {
791         Object JavaDoc last = this.args.get(i - 1);
792         if (model.size() >= 1 && model.get(-1) == last) {
793           List lastargs = Collections.singletonList(last);
794           int lastBytes = this.computeArgsBytes(lastargs);
795           // Limit of multi-byte instruction length
796
if (this.argsBytes() + lastBytes >= 65536){
797             return false;
798           }
799           // Must have been cached above
800
this.cachedArgsBytes += lastBytes;
801           this.args.addAll(i, lastargs);
802           model.notePush(this, lastargs);
803           return true;
804         } else {
805 // System.out.println("dup.i: " + i);
806
// System.out.println("dup.model: " + model);
807
return false;
808         }
809       } else {
810         return false;
811       }
812     }
813
814     public void writeArgs(ByteBuffer bytes, Map constants) {
815       try {
816         for (Iterator i = this.args.iterator(); i.hasNext(); ) {
817           Object JavaDoc o = i.next();
818
819           // Numbers are written as integers or floats if there is no
820
// loss of precision, otherwise as doubles
821
// FIXME [2002-02-25 ptw] Use a language with dynamic dispatch
822
if (o instanceof Number JavaDoc) {
823             Number JavaDoc n = (Number JavaDoc)o;
824             if (n.doubleValue() == n.intValue()) {
825               bytes.put(PushTypes.Integer);
826               bytes.putInt(n.intValue());
827             } else if (n.doubleValue() == n.floatValue()) {
828               bytes.put(PushTypes.Float);
829               bytes.putFloat(n.floatValue());
830             } else {
831               bytes.put(PushTypes.Double);
832               // SWF has the opposite word order
833
long lb = Double.doubleToRawLongBits(n.doubleValue());
834               bytes.putInt((int)(lb >> 32));
835               bytes.putInt((int)lb);
836             }
837           } else if (o instanceof Value) {
838             Value v = (Value)o;
839             bytes.put(v.type);
840             if (v instanceof ParameterizedValue) {
841               bytes.put(((ParameterizedValue)v).value);
842             }
843           } else if (o instanceof String JavaDoc) {
844             String JavaDoc s = (String JavaDoc)o;
845             if (constants != null && constants.containsKey(s)) {
846               int index = ((Integer JavaDoc)constants.get(s)).intValue();
847               if (index < 1<<8) {
848                 bytes.put(PushTypes.CONSTANT_INDEX8);
849                 bytes.put((byte)index);
850               } else {
851                 assert index < 1<<16;
852                 bytes.put(PushTypes.CONSTANT_INDEX16);
853                 bytes.putShort((short)index);
854               }
855             } else {
856               bytes.put(PushTypes.String);
857               bytes.put(s.getBytes("UTF-8"));
858               bytes.put((byte)0);
859             }
860           }
861         }
862
863       } catch (UnsupportedEncodingException e) {
864         assert false : "this can't happen";
865       }
866     }
867
868     // compute an upper bound for the size of the arguments. cf., computePushArg
869
public int computeArgsBytes(List args) {
870       // 'exact' answer
871
// int b = 0;
872
// for (Iterator i = args.iterator(); i.hasNext(); ) {
873
// b += computePushArg(i.next()).size;
874
// }
875
// return b;
876
// quick upper bound instead:
877
int b = 0;
878       for (Iterator i = args.iterator(); i.hasNext(); ) {
879         Object JavaDoc o = i.next();
880
881         if (o instanceof Number JavaDoc) {
882           Number JavaDoc n = (Number JavaDoc)o;
883           if (n.doubleValue() == n.intValue()) {
884             b += 1 + 4;
885           } else if (n.doubleValue() == n.floatValue()) {
886             b += 1 + 4;
887           } else {
888             b += 1 + 8;
889           }
890         } else if (o instanceof Value) {
891           Value v = (Value)o;
892           b += 1;
893           if (v instanceof ParameterizedValue) {
894             b += 1;
895           }
896         } else if (o instanceof String JavaDoc) {
897           String JavaDoc s = (String JavaDoc)o;
898           b += 1 + s.length() + 1;
899         }
900       }
901       return b;
902     }
903
904     // Python interface to PUSHInstruction constants
905
public List getargs() {
906       return this.args;
907     }
908   }
909
910   // Pseudo-instructions used during assembly
911

912   /***
913    * Represents an instruction that isn't backed by bytecode.
914    * PseudoInstructions are resolved by the assembler.
915    */

916   public static class PseudoInstruction extends Instruction {
917     public Object JavaDoc name;
918
919     public PseudoInstruction(Object JavaDoc name) {
920       this.name = name;
921     }
922
923     public String JavaDoc toString() {
924       return this.name.toString();
925     }
926
927     public void writeBytes(ByteBuffer bytes, Map constants) {
928       ;
929     }
930
931     public int argsBytes() {
932       return 0;
933     }
934
935     public Instruction __call__() {
936       assert false;
937       return null;
938     }
939
940     public Instruction __call__(Object JavaDoc arg) {
941       assert false;
942       return null;
943     }
944
945     public Instruction __call__(Object JavaDoc[] args) {
946       assert false;
947       return null;
948     }
949   }
950
951   /***
952    * A BranchIfFalse pseudo-instruction is used to simplify the code
953    * generator. It is replaced by (NOT, BranchIfTrue) when the code
954    * generator emits to the assembler
955    */

956   public static class BranchIfFalseInstruction extends PseudoInstruction {
957
958     protected BranchIfFalseInstruction(Object JavaDoc target) {
959       super(target);
960     }
961
962     public Instruction __call__(Object JavaDoc target) {
963       return new BranchIfFalseInstruction(target);
964     }
965
966     public Instruction __call__(int target) {
967       return new BranchIfFalseInstruction(new Integer JavaDoc(target));
968     }
969
970     public String JavaDoc toString() {
971       return "branchIfFalse " + this.name;
972     }
973
974     // Python interface
975
public Object JavaDoc getTarget() {
976       return this.name;
977     }
978   }
979
980   /***
981    * A LABEL pseudo-instruction of the form @code{LABEL(n)} is added to
982    * the instruction sequence at the target for a label. (Label targets
983    * are used for conditional expressions, control structures, and the
984    * ends of definitions and @code{with} statements.)
985    */

986   public static class LABELInstruction extends PseudoInstruction {
987
988     protected LABELInstruction(Object JavaDoc name) {
989       super(name);
990     }
991
992     public Instruction __call__(Object JavaDoc name) {
993       return new LABELInstruction(name);
994     }
995
996     public Instruction __call__(int target) {
997       return new LABELInstruction(new Integer JavaDoc(target));
998     }
999
1000    public String JavaDoc toString() {
1001      return super.toString() + ":";
1002    }
1003  }
1004
1005  public static class COMMENTInstruction extends PseudoInstruction {
1006
1007    protected COMMENTInstruction(String JavaDoc comment) {
1008      super(comment);
1009    }
1010
1011    public Instruction __call__(Object JavaDoc comment) {
1012      return new COMMENTInstruction((String JavaDoc)comment);
1013    }
1014
1015    public String JavaDoc toString() {
1016      return ";; " + super.toString();
1017    }
1018
1019    // Allow PUSHInstruction to optimize across this.
1020
public StackModel updateStackModel(StackModel model) {
1021      return model;
1022    }
1023  }
1024
1025  // Print running statistics about the current assembly state
1026
public static class CHECKPOINTInstruction extends COMMENTInstruction {
1027    protected CHECKPOINTInstruction(String JavaDoc message) {
1028      super(message);
1029    }
1030
1031    public Instruction __call__(Object JavaDoc message) {
1032      return new CHECKPOINTInstruction((String JavaDoc)message);
1033    }
1034
1035    public void writeBytes(ByteBuffer bytes, Map constants) {
1036      System.out.println(super.toString() + "\t" + bytes.position());
1037    }
1038  }
1039
1040  // Pass arbitrary bytes into the object
1041
public static class BLOBInstruction extends PseudoInstruction {
1042    byte[] blob;
1043
1044    protected BLOBInstruction(String JavaDoc repr, byte[] blob) {
1045      super(repr);
1046      this.blob = blob;
1047    }
1048
1049    public Instruction __call__(String JavaDoc repr, byte[] blob) {
1050      return new BLOBInstruction(repr, blob);
1051    }
1052
1053    public void writeBytes(ByteBuffer bytes, Map constants) {
1054      bytes.put(this.blob);
1055    }
1056
1057    public int argsBytes() {
1058      return (this.blob.length);
1059    }
1060  }
1061
1062  // For each symbol in the actions package, create a symbol with the
1063
// same name in this package. If the instruction takes arguments,
1064
// the symbol names a constructor, otherwise it names an instance.
1065
// (This matches standard algebraic datatype syntax in functional
1066
// languages, and it ends up being more convenient and easier to use
1067
// than it sounds: for example, POP and BRANCH('label') are both
1068
// instructions.)
1069

1070  public static Instruction NONE = Instruction.curry(Actions.NONE);
1071  public static Instruction NextFrame = Instruction.curry(Actions.NextFrame);
1072  public static Instruction PreviousFrame = Instruction.curry(Actions.PreviousFrame);
1073  public static Instruction PLAY = Instruction.curry(Actions.PLAY);
1074  public static Instruction STOP = Instruction.curry(Actions.STOP);
1075  public static Instruction ToggleQuality = Instruction.curry(Actions.ToggleQuality);
1076  public static Instruction StopSounds = Instruction.curry(Actions.StopSounds);
1077  public static Instruction NumericAdd = Instruction.curry(Actions.NumericAdd);
1078  public static Instruction SUBTRACT = Instruction.curry(Actions.SUBTRACT);
1079  public static Instruction MULTIPLY = Instruction.curry(Actions.MULTIPLY);
1080  public static Instruction DIVIDE = Instruction.curry(Actions.DIVIDE);
1081  public static Instruction OldEquals = Instruction.curry(Actions.OldEquals);
1082  public static Instruction OldLessThan = Instruction.curry(Actions.OldLessThan);
1083  public static Instruction LogicalAnd = Instruction.curry(Actions.LogicalAnd);
1084  public static Instruction LogicalOr = Instruction.curry(Actions.LogicalOr);
1085  public static Instruction NOT = Instruction.curry(Actions.NOT);
1086  public static Instruction StringEqual = Instruction.curry(Actions.StringEqual);
1087  public static Instruction StringLength = Instruction.curry(Actions.StringLength);
1088  public static Instruction SUBSTRING = Instruction.curry(Actions.SUBSTRING);
1089  public static Instruction POP = Instruction.curry(Actions.POP);
1090  public static Instruction INT = Instruction.curry(Actions.INT);
1091  public static Instruction GetVariable = Instruction.curry(Actions.GetVariable);
1092  public static Instruction SetVariable = Instruction.curry(Actions.SetVariable);
1093  public static Instruction SetTargetExpression = Instruction.curry(Actions.SetTargetExpression);
1094  public static Instruction StringConcat = Instruction.curry(Actions.StringConcat);
1095  public static Instruction GetProperty = Instruction.curry(Actions.GetProperty);
1096  public static Instruction SetProperty = Instruction.curry(Actions.SetProperty);
1097  public static Instruction DuplicateMovieClip = Instruction.curry(Actions.DuplicateMovieClip);
1098  public static Instruction RemoveClip = Instruction.curry(Actions.RemoveClip);
1099  public static Instruction TRACE = Instruction.curry(Actions.TRACE);
1100  public static Instruction StartDragMovie = Instruction.curry(Actions.StartDragMovie);
1101  public static Instruction StopDragMovie = Instruction.curry(Actions.StopDragMovie);
1102  public static Instruction StringLessThan = Instruction.curry(Actions.StringLessThan);
1103  public static Instruction RANDOM = Instruction.curry(Actions.RANDOM);
1104  public static Instruction MBLENGTH = Instruction.curry(Actions.MBLENGTH);
1105  public static Instruction ORD = Instruction.curry(Actions.ORD);
1106  public static Instruction CHR = Instruction.curry(Actions.CHR);
1107  public static Instruction GetTimer = Instruction.curry(Actions.GetTimer);
1108  public static Instruction MBSUBSTRING = Instruction.curry(Actions.MBSUBSTRING);
1109  public static Instruction MBORD = Instruction.curry(Actions.MBORD);
1110  public static Instruction MBCHR = Instruction.curry(Actions.MBCHR);
1111  public static Instruction GotoFrame = Instruction.curry(Actions.GotoFrame);
1112  public static Instruction GetUrl = Instruction.curry(Actions.GetUrl);
1113  public static Instruction WaitForFrame = Instruction.curry(Actions.WaitForFrame);
1114  public static Instruction SetTarget = Instruction.curry(Actions.SetTarget);
1115  public static Instruction GotoLabel = Instruction.curry(Actions.GotoLabel);
1116  public static Instruction WaitForFrameExpression = Instruction.curry(Actions.WaitForFrameExpression);
1117  public static Instruction PUSH = Instruction.curry(Actions.PUSH);
1118  public static Instruction BRANCH = Instruction.curry(Actions.BRANCH);
1119  public static Instruction GetURL2 = Instruction.curry(Actions.GetURL2);
1120  public static Instruction BranchIfTrue = Instruction.curry(Actions.BranchIfTrue);
1121  public static Instruction CallFrame = Instruction.curry(Actions.CallFrame);
1122  public static Instruction GotoExpression = Instruction.curry(Actions.GotoExpression);
1123
1124  // Flash 5
1125
public static Instruction DELETE = Instruction.curry(Actions.DELETE);
1126  public static Instruction DELETE2 = Instruction.curry(Actions.DELETE2);
1127  public static Instruction VarEquals = Instruction.curry(Actions.VarEquals);
1128  public static Instruction CallFunction = Instruction.curry(Actions.CallFunction);
1129  public static Instruction RETURN = Instruction.curry(Actions.RETURN);
1130  public static Instruction MODULO = Instruction.curry(Actions.MODULO);
1131  public static Instruction NEW = Instruction.curry(Actions.NEW);
1132  public static Instruction VAR = Instruction.curry(Actions.VAR);
1133  public static Instruction InitArray = Instruction.curry(Actions.InitArray);
1134  public static Instruction InitObject = Instruction.curry(Actions.InitObject);
1135  public static Instruction TypeOf = Instruction.curry(Actions.TypeOf);
1136  public static Instruction TargetPath = Instruction.curry(Actions.TargetPath);
1137  public static Instruction ENUMERATE = Instruction.curry(Actions.ENUMERATE);
1138  public static Instruction ADD = Instruction.curry(Actions.ADD);
1139  public static Instruction LessThan = Instruction.curry(Actions.LessThan);
1140  public static Instruction EQUALS = Instruction.curry(Actions.EQUALS);
1141  public static Instruction ObjectToNumber = Instruction.curry(Actions.ObjectToNumber);
1142  public static Instruction ObjectToString = Instruction.curry(Actions.ObjectToString);
1143  public static Instruction DUP = Instruction.curry(Actions.DUP);
1144  public static Instruction SWAP = Instruction.curry(Actions.SWAP);
1145  public static Instruction GetMember = Instruction.curry(Actions.GetMember);
1146  public static Instruction SetMember = Instruction.curry(Actions.SetMember);
1147  public static Instruction Increment = Instruction.curry(Actions.Increment);
1148  public static Instruction Decrement = Instruction.curry(Actions.Decrement);
1149  public static Instruction CallMethod = Instruction.curry(Actions.CallMethod);
1150  public static Instruction NewMethod = Instruction.curry(Actions.NewMethod);
1151  public static Instruction BitwiseAnd = Instruction.curry(Actions.BitwiseAnd);
1152  public static Instruction BitwiseOr = Instruction.curry(Actions.BitwiseOr);
1153  public static Instruction BitwiseXor = Instruction.curry(Actions.BitwiseXor);
1154  public static Instruction ShiftLeft = Instruction.curry(Actions.ShiftLeft);
1155  public static Instruction ShiftRight = Instruction.curry(Actions.ShiftRight);
1156  public static Instruction UShiftRight = Instruction.curry(Actions.UShiftRight);
1157  public static Instruction SetRegister = Instruction.curry(Actions.SetRegister);
1158  public static Instruction CONSTANTS = Instruction.curry(Actions.CONSTANTS);
1159  public static Instruction WITH = Instruction.curry(Actions.WITH);
1160  public static Instruction DefineFunction = Instruction.curry(Actions.DefineFunction);
1161
1162  // Flash 6
1163
public static Instruction InstanceOf = Instruction.curry(Actions.InstanceOf);
1164  public static Instruction EnumerateValue = Instruction.curry(Actions.EnumerateValue);
1165  public static Instruction StrictEquals = Instruction.curry(Actions.StrictEquals);
1166  public static Instruction GreaterThan = Instruction.curry(Actions.GreaterThan);
1167  public static Instruction StringGreaterThan = Instruction.curry(Actions.StringGreaterThan);
1168
1169  // Flash 7
1170
public static Instruction DefineFunction2 = Instruction.curry(Actions.DefineFunction2);
1171
1172  // Psuedo-instructions
1173
public static Instruction BranchIfFalse = new BranchIfFalseInstruction("");
1174  public static Instruction LABEL = new LABELInstruction("");
1175  public static Instruction COMMENT = new COMMENTInstruction("");
1176  public static Instruction CHECKPOINT = new CHECKPOINTInstruction("");
1177  public static Instruction BLOB = new BLOBInstruction("", null);
1178  static {
1179    NameInstruction.put("BranchIfFalse", BranchIfFalse);
1180    NameInstruction.put("LABEL", LABEL);
1181    NameInstruction.put("COMMENT", COMMENT);
1182    NameInstruction.put("CHECKPOINT", CHECKPOINT);
1183    NameInstruction.put("BLOB", BLOB);
1184  }
1185}
1186
Popular Tags