brians changes to MethodGen
[org.ibex.classgen.git] / src / org / ibex / classgen / MethodGen.java
1 package org.ibex.classgen;
2
3 import java.io.*;
4 import java.util.*;
5
6 /** A class representing a method in a generated classfile
7     @see ClassFile#addMethod */
8 public class MethodGen implements CGConst {
9     private final static boolean EMIT_NOPS = false;
10     
11     private static final int NO_CODE = -1;
12
13     private final ClassFile cf;
14     private final String name;
15     private final Type ret;
16     private final Type[] args;
17     private final int flags;
18     private final ClassFile.AttrGen attrs;
19     private final ClassFile.AttrGen codeAttrs;
20     private final Vector exnTable = new Vector();
21     private final Hashtable thrownExceptions = new Hashtable();
22     
23     private int maxStack = 16;
24     private int maxLocals;
25     
26     private int size;
27     private int capacity;
28     private byte[] op;
29     private Object[] arg;
30     private ConstantPool.Ent[] cparg;
31     
32     public void debugToString(StringBuffer sb, String constructorName) {
33         // This is intentionally a local variable so it can be removed by gcclass
34         final String[] OP_NAMES = new String[]{
35             "nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2", 
36             "iconst_3", "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0", 
37             "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush", 
38             "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload", 
39             "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3", 
40             "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1", 
41             "fload_2", "fload_3", "dload_0", "dload_1", "dload_2", "dload_3", 
42             "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload", 
43             "faload", "daload", "aaload", "baload", "caload", "saload", 
44             "istore", "lstore", "fstore", "dstore", "astore", "istore_0", 
45             "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2", 
46             "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0", 
47             "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2", 
48             "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore", 
49             "bastore", "castore", "sastore", "pop", "pop2", "dup", 
50             "dup_x1", "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap", 
51             "iadd", "ladd", "fadd", "dadd", "isub", "lsub", 
52             "fsub", "dsub", "imul", "lmul", "fmul", "dmul", 
53             "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem", 
54             "frem", "drem", "ineg", "lneg", "fneg", "dneg", 
55             "ishl", "lshl", "ishr", "lshr", "iushr", "lushr", 
56             "iand", "land", "ior", "lor", "ixor", "lxor", 
57             "iinc", "i2l", "i2f", "i2d", "l2i", "l2f", 
58             "l2d", "f2i", "f2l", "f2d", "d2i", "d2l", 
59             "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl", 
60             "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt", 
61             "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt", 
62             "if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto", 
63             "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn", 
64             "freturn", "dreturn", "areturn", "return", "getstatic", "putstatic", 
65             "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface", 
66             "", "new", "newarray", "anewarray", "arraylength", "athrow", 
67             "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", "multianewarray", 
68             "ifnull", "ifnonnull", "goto_w", "jsr_w", "", "", 
69             "", "", "", "", "", "", 
70             "", "", "", "", "", "", 
71             "", "", "", "", "", "", 
72             "", "", "", "", "", "", 
73             "", "", "", "", "", "", 
74             "", "", "", "", "", "", 
75             "", "", "", "", "", "", 
76             "", "", "", "", "", "", 
77             "", "", "", ""
78         };
79         
80         sb.append("  ").append(ClassFile.flagsToString(flags,false));
81
82         if (name.equals("<clinit>")) sb.append("static ");
83         else {
84             if (name.equals("<init>"))
85                 sb.append(constructorName);
86             else
87                 sb.append(ret.debugToString()).append(" ").append(name);
88             sb.append("(");
89             for(int i=0; i<args.length; i++)
90                 sb.append((i==0?"":", ")+args[i].debugToString());
91             sb.append(") ");
92             if(thrownExceptions.size() > 0) {
93                 sb.append("throws");
94                 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
95                     sb.append(" ").append(((Type.Class)e.nextElement()).debugToString()).append(",");
96                 sb.setLength(sb.length()-1);
97                 sb.append(" ");
98             }
99         }
100         if((flags & (ACC_NATIVE|ACC_ABSTRACT))==0) {
101             sb.append("{\n");
102             for(int i=0;i<size();i++) {
103                 sb.append("    ");
104                 for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
105                 sb.append(i).append(": ");
106                 sb.append(OP_NAMES[op[i]&0xff]);
107                 String s = null;
108                 if(arg[i] instanceof Type) s = ((Type)arg[i]).debugToString();
109                 else if(arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
110                 else if(arg[i] instanceof String) s = "\"" + s + "\"";
111                 else if(arg[i] != null) s = arg[i].toString();
112                 if(s != null) sb.append(" ").append(s);
113                 sb.append("\n");
114             }
115             sb.append("  }\n");
116         } else {
117             sb.append(";");
118         }
119     }
120
121     MethodGen(ClassFile cf, DataInput in, ConstantPool cp, boolean ownerInterface) throws IOException {
122         this.cf = cf;
123         flags = in.readShort();
124         if((flags & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) != 0)
125             throw new ClassFile.ClassReadExn("invalid flags");
126         this.name = cp.getUtf8KeyByIndex(in.readShort());
127         //System.err.println("Processing " + name + "...");
128         Type.Class.Method m = cf.getType().method(name+cp.getUtf8KeyByIndex(in.readShort()));
129         this.ret = m.returnType;
130         this.args = new Type[m.getNumArgs()];
131         for(int i=0; i<args.length; i++) args[i] = m.getArgType(i);
132         this.attrs = new ClassFile.AttrGen(in,cp);
133         
134         if((flags & (ACC_NATIVE|ACC_ABSTRACT))==0)  {
135             byte[] codeAttr = (byte[]) attrs.get("Code");
136             if(codeAttr == null) throw new ClassFile.ClassReadExn("code attr expected");
137             DataInputStream ci = new DataInputStream(new ByteArrayInputStream(codeAttr));
138             maxStack = ci.readUnsignedShort();
139             maxLocals = ci.readUnsignedShort();
140             int codeLen = ci.readInt();
141             int[] bytecodeMap = parseCode(ci,codeLen,cp);
142             int numExns = ci.readUnsignedShort();
143             while(numExns-- > 0)
144                 exnTable.addElement(new ExnTableEnt(ci,cp,bytecodeMap));
145             codeAttrs = new ClassFile.AttrGen(ci,cp);
146             // FEATURE: Support these
147             // NOTE: Until we can support them properly we HAVE to delete them,
148             //       they'll be incorrect after we rewrite the constant pool, etc
149             codeAttrs.remove("LineNumberTable");
150             codeAttrs.remove("LocalVariableTable");
151             
152         } else {
153             codeAttrs = new ClassFile.AttrGen();
154         }
155
156         if(attrs.contains("Exceptions")) {
157             DataInputStream ei = new DataInputStream(new ByteArrayInputStream((byte[]) attrs.get("Exceptions")));
158             int exnCount = ei.readUnsignedShort();
159             while(exnCount-- > 0) {
160                 Type.Class t = (Type.Class) cp.getKeyByIndex(ei.readUnsignedShort());
161                 thrownExceptions.put(t,t);
162             }
163         }
164     }
165         
166     final int[] parseCode(DataInputStream in, int codeLen, ConstantPool cp) throws IOException {
167         int[] map = new int[codeLen];
168         int pc;
169         for(pc=0;pc<map.length;pc++) map[pc] = -1;
170         for(pc=0;pc<codeLen;) {
171             byte op = in.readByte();
172             int opdata = OP_DATA[op&0xff];
173             //System.err.println("Processing " + Integer.toString(op&0xff,16) + " at " + pc);
174             if((opdata&OP_VALID_FLAG)==0) throw new ClassFile.ClassReadExn("invalid bytecode " + (op&0xff));
175             int argLength = opdata & OP_ARG_LENGTH_MASK;
176             int mypc = pc;
177             map[mypc] = size();
178             pc += 1 + (argLength == 7 ? 0 : argLength);
179             if(argLength == 0)  { add(op); continue; }
180             Object arg;
181             switch(op) {
182                 case IINC:
183                     arg = new Pair(in.readUnsignedByte(),in.readByte());
184                     break;
185                 case TABLESWITCH:
186                 case LOOKUPSWITCH:
187                     SI si;
188                     for(;(pc&3) != 0;pc++) if(in.readByte() != 0) throw new ClassFile.ClassReadExn("invalid padding");
189                     int def = in.readInt() + mypc;
190                     pc += 4;
191                     if(op == LOOKUPSWITCH) {
192                         LSI lsi = new LSI(in.readInt());
193                         pc += 4;
194                         for(int i=0;i<lsi.size();i++) {
195                             lsi.setVal(i,in.readInt());
196                             lsi.setTarget(i,in.readInt() + mypc);
197                             pc += 8;
198                         }
199                         si = lsi;
200                     } else {
201                         int lo = in.readInt();
202                         int hi = in.readInt();
203                         pc += 8;
204                         TSI tsi = new TSI(lo,hi);
205                         for(int i=0;i<tsi.size();i++) { tsi.setTarget(i,in.readInt() + mypc); pc += 4; }
206                         si = tsi;
207                     }
208                     si.setDefaultTarget(def);
209                     arg = si;
210                     break;
211                 case WIDE: {
212                     byte wideop = in.readByte();
213                     arg = wideop == IINC 
214                         ? new Wide(wideop,in.readUnsignedShort(),in.readShort()) 
215                         : new Wide(wideop,in.readUnsignedShort());
216                     pc += wideop == IINC ? 5 : 3;
217                     break;
218                 }
219                 case MULTIANEWARRAY:
220                     arg = new MultiANewArray((Type.Class)cp.getKeyByIndex(in.readUnsignedShort()),in.readUnsignedByte());
221                     break;
222                 case INVOKEINTERFACE: {
223                     ConstantPool.Ent ent = cp.getByIndex(in.readUnsignedShort());
224                     if(ent.tag != CONSTANT_INTERFACEMETHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode");
225                     arg = ((ConstantPool.InterfaceMethodKey)ent.key()).method;
226                     if(in.readByte() == 0 || in.readByte() != 0) throw new ClassFile.ClassReadExn("illegal count or 0 arg to invokeinterface");
227                     break;
228                 }
229                 default:
230                     if((opdata&OP_CPENT_FLAG)!=0) {
231                         ConstantPool.Ent ent = cp.getByIndex(argLength == 2 ? in.readUnsignedShort() : argLength == 1 ? in.readUnsignedByte() : -1);
232                         int tag = ent.tag;
233                         Object key = ent.key();
234                         switch(op) {
235                             case LDC:
236                             case LDC_W:
237                             case LDC2_W:
238                                 switch(tag) {
239                                     case CONSTANT_INTEGER:
240                                     case CONSTANT_FLOAT:
241                                     case CONSTANT_LONG:
242                                     case CONSTANT_DOUBLE:
243                                     case CONSTANT_STRING:
244                                     case CONSTANT_CLASS:
245                                         break;
246                                     default:
247                                         throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
248                                 }
249                                 break;
250                             case GETSTATIC:
251                             case PUTSTATIC:
252                             case GETFIELD:
253                             case PUTFIELD:
254                                 if(tag != CONSTANT_FIELDREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
255                                 break;
256                             case INVOKEVIRTUAL:
257                             case INVOKESPECIAL:
258                             case INVOKESTATIC:
259                                 if(tag != CONSTANT_METHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
260                                 break;
261                             case NEW:
262                             case ANEWARRAY:
263                             case CHECKCAST:
264                             case INSTANCEOF:
265                                 if(tag != CONSTANT_CLASS) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
266                                 break;                        
267                             default:
268                                 throw new Error("should never happen");
269                         }
270                         arg = key;
271                     } else {
272                         // treat everything else (including branches for now) as plain old ints
273                         int n;
274                         boolean unsigned = (opdata&OP_UNSIGNED_FLAG)!=0;
275                         if(argLength == 1) n = unsigned ? in.readUnsignedByte() : in.readByte();
276                         else if(argLength == 2) n = unsigned ? in.readUnsignedShort() : in.readShort();
277                         else throw new Error("should never happen");
278                         if((opdata&OP_BRANCH_FLAG)!=0) n += mypc;
279                         arg = N(n);
280                     }
281                     break;
282             }
283             add(op,arg);
284         }
285         if(pc != codeLen) throw new ClassFile.ClassReadExn("didn't read enough code (" + pc + "/" + codeLen + " in " + name + ")");
286         for(int i=0;i<size();i++) {
287             switch(op[i]) {
288                 case TABLESWITCH:
289                 case LOOKUPSWITCH:
290                 {
291                     SI si = (SI) arg[i];
292                     
293                     int pos = map[si.getDefaultTarget()];
294                     if(pos < 0)  throw new ClassFile.ClassReadExn("default target points to invalid bytecode: " + si.getDefaultTarget());
295                     si.setDefaultTarget(pos);
296                     
297                     for(int j=0;j<si.size();j++) {
298                         pos = map[si.getTarget(j)];
299                         if(pos < 0)  throw new ClassFile.ClassReadExn("target points to invalid bytecode");
300                         si.setTarget(j,pos);
301                     }
302                     break;
303                 }
304                 default:
305                     if(OP_BRANCH(op[i])) {
306                         int pos = map[((Integer)arg[i]).intValue()];
307                         if(pos < 0)  throw new ClassFile.ClassReadExn("branch points to invalid bytecode");
308                         arg[i] = N(pos);
309                     }
310                     break;
311             }
312         }
313         return map;
314     }
315
316     MethodGen(ClassFile cf, String name, Type ret, Type[] args, int flags, boolean ownerInterface) {
317         if((flags & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) != 0)
318             throw new IllegalArgumentException("invalid flags");
319         this.cf = cf;
320         this.name = name;
321         this.ret = ret;
322         this.args = args;
323         this.flags = flags;
324         
325         attrs = new ClassFile.AttrGen();
326         codeAttrs = new ClassFile.AttrGen();
327         
328         if(ownerInterface || (flags & (ACC_ABSTRACT|ACC_NATIVE)) != 0) size = capacity = -1;
329         
330         maxLocals = Math.max(args.length + (flags&ACC_STATIC)==0 ? 1 : 0, 4);
331     }
332     
333     class ExnTableEnt {
334         final int start;
335         final int end;
336         final int handler;
337         final Type.Class type; // null type means all exceptions (for finally)
338         
339         ExnTableEnt(DataInput in, ConstantPool cp, int[] bytecodeMap) throws IOException {
340             int startPC = in.readUnsignedShort();
341             int endPC = in.readUnsignedShort();
342             int handlerPC = in.readUnsignedShort();
343             int index = in.readUnsignedShort();
344             this.type = index == 0 ? null : (Type.Class) cp.getKeyByIndex(index);
345             int max = bytecodeMap.length;
346             if(startPC >= max || bytecodeMap[startPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
347             if(endPC >= max || bytecodeMap[endPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
348             if(handlerPC >= max || bytecodeMap[handlerPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
349             this.start = bytecodeMap[startPC];
350             this.end = bytecodeMap[endPC];
351             this.handler = bytecodeMap[handlerPC];
352         }
353         ExnTableEnt(int start, int end, int handler, Type.Class type) {
354             this.start = start;
355             this.end = end;
356             this.handler = handler;
357             this.type = type;
358         }
359         void finish(ConstantPool cp) { if(type != null) cp.add(type); }
360         void dump(DataOutput o, int[] pc, int endPC, ConstantPool cp) throws IOException {
361             o.writeShort(pc[start]);
362             o.writeShort(end==pc.length ? endPC : pc[end]);
363             o.writeShort(pc[handler]);
364             o.writeShort(type == null ? 0 : cp.getIndex(type));
365         }
366     }
367     
368     /** Adds an exception handler for the range [<i>start</i>, <i>end</i>) pointing to <i>handler</i>
369         @param start The instruction to start at (inclusive)
370         @param end The instruction to end at (exclusive)
371         @param handler The instruction of the excepton handler
372         @param type The type of exception that is to be handled (MUST inherit from Throwable)
373     */
374     public final void addExceptionHandler(int start, int end, int handler, Type.Class type) {
375         exnTable.addElement(new ExnTableEnt(start, end, handler, type));
376     }
377     
378     /** Adds a exception type that can be thrown from this method
379         NOTE: This isn't enforced by the JVM. This is for reference only. A method can throw exceptions not declared to be thrown 
380         @param type The type of exception that can be thrown 
381     */
382     public final void addThrow(Type.Class type) { thrownExceptions.put(type, type); }
383     
384     private final void grow() { if(size == capacity) grow(size+1); }
385     private final void grow(int newCap) {
386         if(capacity == NO_CODE) throw new IllegalStateException("method can't have code");
387         if(newCap <= capacity) return;
388         newCap = Math.max(newCap, capacity == 0 ? 256 : capacity*2);
389         
390         byte[] op2 = new byte[newCap];
391         if(capacity != 0) System.arraycopy(op, 0, op2, 0, size);
392         op = op2;
393         
394         Object[] arg2 = new Object[newCap];
395         if(capacity != 0) System.arraycopy(arg, 0, arg2, 0, size);
396         arg = arg2;
397         
398         capacity = newCap;
399     }
400     
401     /** Returns the size (in instructions) of this method 
402         @return The size of the method (in instructions)
403     */
404     public final int size() { return size; }
405     
406     // These two are optimized for speed, they don't call set() below
407     /** Add a bytecode (with no argument) to the method */
408     public final int add(byte op) {
409         int s = size;
410         if(s == capacity) grow();
411         this.op[s] = op;
412         size++;
413         return s;
414     }
415     /** Set the bytecode at position <i>pos</i> to <i>op</i> */
416     public final void set(int pos, byte op) { this.op[pos] = op; }
417         
418     /** Adds a bytecode, <i>op</i>, with argument <i>arg</i> to the method 
419         @return The position of the new bytecode
420         */
421     public final int add(byte op, Object arg) { if(capacity == size) grow(); set(size, op, arg); return size++; }
422     /** Adds a bytecode with a boolean argument - equivalent to add(op, arg?1:0);
423         @return The position of the new bytecode
424         @see #add(byte, int)
425     */
426     public final int add(byte op, boolean arg) { if(capacity == size) grow(); set(size, op, arg); return size++; }
427     /** Adds a bytecode with an integer argument. This is equivalent to add(op, new Integer(arg)), but optimized to prevent the allocation when possible
428         @return The position of the new bytecode
429         @see #add(byte, Object)
430     */
431     public final int add(byte op, int arg) { if(capacity == size) grow(); set(size, op, arg); return size++; }
432     
433     /** Gets the bytecode at position <i>pos</i>
434         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
435     */
436     public final byte get(int pos) { return op[pos]; }
437     
438     /** Gets the bytecode at position <i>pos</i>. NOTE: This isn't necessarily the same object that was set with add or set.
439         Arguments for instructions which access the constant pool (LDC, INVOKEVIRTUAL, etc) are converted to a more efficient
440         interal form when they are added. The value returned from this method for these instruction can be reused, but there
441         is no way to retrieve the original object 
442         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
443     */    
444     public final Object getArg(int pos) { return arg[pos]; }
445     
446     /** Sets the argument for <i>pos</i> to <i>arg</i>. This is equivalent to set(pos, op, new Integer(arg)), but optimized to prevent the allocation when possible.
447         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
448         @see #setArg(int, Object) */
449     public final void setArg(int pos, int arg) { set(pos, op[pos], N(arg)); }
450     /** Sets the argument for <i>pos</i> to <i>arg</i>.
451         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
452     */
453     public final void setArg(int pos, Object arg) { set(pos, op[pos], arg); }
454     
455     /** Sets the bytecode and argument  at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly. 
456         This is equivalent to set(pos, op, arg?1:0) 
457         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
458     */
459     public final void set(int pos, byte op, boolean arg) { set(pos, op, arg?1:0); }
460     
461     // This MUST handle x{LOAD, STORE} and LDC with an int arg WITHOUT falling back to set(int, byte, Object)
462     /** Sets the bytecode and argument  at <i>pos</i> to <i>op</i> and <i>n</i> respectivly.
463         This is equivalent to set(pos, op, new Integer(n)), but optimized to prevent the allocation when possible.
464         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
465     */
466     public final void set(int pos, byte op, int n) {
467         Object arg = null;
468         OUTER: switch(op) {
469             case LDC:
470                 switch(n) {
471                     case -1: op = ICONST_M1; break OUTER;
472                     case 0:  op = ICONST_0;  break OUTER;
473                     case 1:  op = ICONST_1;  break OUTER;
474                     case 2:  op = ICONST_2;  break OUTER; 
475                     case 3:  op = ICONST_3;  break OUTER;
476                     case 4:  op = ICONST_4;  break OUTER;
477                     case 5:  op = ICONST_5;  break OUTER;
478                 }
479                 if(n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); } 
480                 else if(n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
481                 else { arg = N(n); }
482                 break;
483             case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
484             case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
485                 if(n >= maxLocals) maxLocals = n + 1;
486                 if(n >= 0 && n <= 3) {
487                     byte base = 0;
488                     switch(op) {
489                         case ILOAD:  base = ILOAD_0;  break;
490                         case ISTORE: base = ISTORE_0; break;
491                         case LLOAD:  base = LLOAD_0;  break;
492                         case LSTORE: base = LSTORE_0; break; 
493                         case FLOAD:  base = FLOAD_0;  break;
494                         case FSTORE: base = FSTORE_0; break;
495                         case DLOAD:  base = DLOAD_0;  break;
496                         case DSTORE: base = DSTORE_0; break;
497                         case ALOAD:  base = ALOAD_0;  break;
498                         case ASTORE: base = ASTORE_0; break;
499                     }
500                     op = (byte)((base&0xff) + n);
501                 } else {
502                     arg = N(n);
503                 }
504                 break;
505             default:
506                 set(pos, op, N(n));
507                 return;
508         }            
509         this.op[pos] = op;
510         this.arg[pos] = arg;
511     }
512     
513     /** Sets the bytecode and argument  at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
514         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
515         */
516     public final void set(int pos, byte op, Object arg) {
517         switch(op) {
518             case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
519             case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
520                 // set(int, byte, int) always handles these ops itself
521                 set(pos, op, ((Integer)arg).intValue());
522                 return;
523             case LDC:
524                 // set(int, byte, int) always handles these opts itself
525                 if(arg instanceof Integer) { set(pos, op, ((Integer)arg).intValue()); return; }
526                 if(arg instanceof Boolean) { set(pos, op, ((Boolean)arg).booleanValue()); return; }
527                 
528                 if(arg instanceof Long) {
529                     long l = ((Long)arg).longValue();
530                     if(l == 0L || l == 1L) {
531                         this.op[pos] = l == 0L ? LCONST_0 : LCONST_1;
532                         this.arg[pos] = null; 
533                         return;
534                     }
535                     op = LDC2_W;
536                 } else if(arg instanceof Double) {
537                     op = LDC2_W;
538                 }
539                 break;
540         }
541         if((OP_DATA[op&0xff]&OP_VALID_FLAG) == 0) throw new IllegalArgumentException("unknown bytecode");
542         this.op[pos] = op;
543         this.arg[pos] = arg;
544     }
545     
546     /** This class represents the arguments to the TABLESWITH and LOOKUPSWITCH bytecodes
547         @see MethodGen.TSI
548         @see MethodGen.LSI
549     */
550     public static abstract class SI {
551         public final Object[] targets;
552         public Object defaultTarget;
553
554         SI(int size) { targets = new Object[size]; }
555         public void setTarget(int pos, Object val) { targets[pos] = val; }
556         public void setTarget(int pos, int val) { targets[pos] = N(val); }
557         public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
558         public void setDefaultTarget(Object o) { defaultTarget = o; }
559         public int size() { return targets.length; }
560         
561         public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
562         public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }   
563         
564         abstract int length();
565     }
566     
567     /** This class represents the arguments to the TABLESWITCH bytecode */
568     public static class TSI extends SI {
569         public final int lo;
570         public final int hi;
571         public TSI(int lo, int hi) {
572             super(hi-lo+1);
573             this.lo = lo;
574             this.hi = hi;
575         }
576         public void setTargetForVal(int val, Object o) { setTarget(val-lo, o); }
577         public void setTargetForVal(int val, int n) { setTarget(val-lo, n); }
578         
579         int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
580     }
581     
582     /** This class represents the arguments to the LOOKUPSWITCH bytecode */
583     public static class LSI extends SI {
584         public final int[] vals;
585         public LSI(int size) {
586            super(size);
587            this.vals = new int[size];
588         }
589         public final void setVal(int pos, int val) { vals[pos] = val; }
590         
591         int length() { return 8 + targets.length * 8; } // key/val per target, default, count
592     }
593     
594     /** This class represents the arguments to byecodes that take two integer arguments. */
595     public static class Pair {
596         public int i1;
597         public int i2;
598         public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
599     }
600     
601     public static class MultiANewArray {
602         public Type.Class type;
603         public int dims;
604         public MultiANewArray(Type.Class type, int dims) { this.type = type; this.dims = dims; }
605     }
606     
607     public static class Wide {
608         public final byte op;
609         public final int varNum;
610         public final int n;
611         Wide(byte op, int varNum) { this(op, varNum, 0); }
612         Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
613     }
614         
615     /** Sets the maximum number of locals in the function to <i>maxLocals</i>. NOTE: This defaults to 0 and is automatically increased as
616         necessary when *LOAD/*STORE bytecodes are added. You do not need to call this function in most cases */
617     public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
618     /** Sets the maxinum size of th stack for this function  to <i>maxStack</i>. This defaults to 16< */
619     public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
620     
621     /** Computes the final bytecode for this method. 
622         @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
623         @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
624     */
625     void finish(ConstantPool cp) {
626         cp.addUtf8(name);
627         cp.addUtf8(Type.methodTypeDescriptor(args,ret));
628         
629         for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
630             cp.add(e.nextElement());
631         
632         if(size == NO_CODE) return;
633         for(int i=0;i<exnTable.size();i++)
634             ((ExnTableEnt)exnTable.elementAt(i)).finish(cp);
635         
636         // We'll set these correctly later
637         if((flags & (ACC_NATIVE|ACC_ABSTRACT))==0) attrs.put("Code","");
638         if(thrownExceptions.size() > 0) attrs.put("Exceptions","");
639         attrs.finish(cp);
640         codeAttrs.finish(cp);
641         
642         cparg = new ConstantPool.Ent[size];
643         
644         for(int i=0, p=0;i<size;i++) {
645             switch(op[i]) {
646                 case LDC:
647                 case LDC_W:
648                 case LDC2_W:
649                 case GETSTATIC:
650                 case PUTSTATIC:
651                 case GETFIELD:
652                 case PUTFIELD:
653                 case INVOKEVIRTUAL:
654                 case INVOKESPECIAL:
655                 case INVOKESTATIC:
656                 case NEW:
657                 case ANEWARRAY:
658                 case CHECKCAST:
659                 case INSTANCEOF:
660                     cparg[i] = cp.add(arg[i]);
661                     break;
662                 case INVOKEINTERFACE:
663                     cparg[i] = cp.add(new ConstantPool.InterfaceMethodKey((Type.Class.Method)arg[i]));
664                     break;
665                 case MULTIANEWARRAY:
666                     cparg[i] = cp.add(((MultiANewArray)arg[i]).type);
667                     break;
668             }
669         }
670     }
671     
672     private Object resolveTarget(Object arg) {
673         int target;
674         if(arg instanceof PhantomTarget) {
675             target = ((PhantomTarget)arg).getTarget();
676             if(target == -1) throw new IllegalStateException("unresolved phantom target");
677             arg = N(target);
678         } else {
679             target = ((Integer)arg).intValue();
680         }
681         if(target < 0 || target >= size)
682             throw new IllegalStateException("invalid target address " + target + "/" + size);
683         return arg;
684     }
685     
686     private void generateCode(ConstantPool cp) {
687         try {
688             _generateCode(cp);
689         } catch(IOException e) {
690             throw new Error("should never happen");
691         }
692     }
693     
694     private void _generateCode(ConstantPool cp) throws IOException {
695         ByteArrayOutputStream baos = new ByteArrayOutputStream();
696         DataOutput o = new DataOutputStream(baos);
697     
698         int[] pc = new int[size];
699         int[] maxpc = pc;
700         int p, i;
701         
702         // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
703         for(i=0, p=0;i<size;i++) {
704             byte op = this.op[i];
705             int opdata = OP_DATA[op&0xff];
706             int j;
707             maxpc[i] = p;
708             
709             if((opdata & OP_BRANCH_FLAG)!= 0) { 
710                 try { 
711                     arg[i] = resolveTarget(arg[i]);
712                 } catch(RuntimeException e) {
713                     System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
714                     throw e;
715                 }
716             }
717             
718             switch(op) {
719                 // Speical caculations
720                 case GOTO:
721                 case JSR: {
722                     int arg = ((Integer)this.arg[i]).intValue();
723                     if(arg < i && p - maxpc[arg] <= 32768) p += 3; 
724                     else p += 5;
725                     continue;
726                 }
727                 case NOP:
728                     if(EMIT_NOPS) p++;
729                     continue;
730                 case LOOKUPSWITCH:
731                 case TABLESWITCH: {
732                     SI si = (SI) arg[i];
733                     Object[] targets = si.targets;
734                     for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
735                     si.defaultTarget = resolveTarget(si.defaultTarget);
736                     p += 1 + 3 + si.length(); // opcode itself, padding, data
737                     if(op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
738                         int[] vals = ((LSI)si).vals;
739                         for(j=1;j<vals.length;j++)
740                             if(vals[j] <= vals[j-1])
741                                 throw new IllegalStateException("out of order/duplicate lookupswitch values");
742                     }
743                     continue;
744                 }
745                 case WIDE:
746                     p += ((Wide)arg[i]).op == IINC ? 5 : 3;
747                     continue;
748                 // May need widening
749                 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
750                 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
751                 case RET: {
752                     int arg = ((Integer)this.arg[i]).intValue();
753                     if(arg > 255) {
754                         this.op[i] = WIDE;
755                         this.arg[i] = new Wide(op, arg);
756                         p += 4;
757                         continue;
758                     }
759                     break;
760                 }
761                 case IINC: {
762                     Pair pair = (Pair) this.arg[i];
763                     if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
764                         this.op[i] = WIDE;
765                         this.arg[i] = new Wide(IINC, pair.i1, pair.i2);
766                         p += 6;
767                         continue;
768                     }
769                     break;
770                 }
771                 case LDC:
772                     j = cp.getIndex(cparg[i]);
773                     if(j >= 256) {
774                         this.op[i] = op = LDC_W;
775                         p += 3;
776                         continue;
777                     }
778                     break;
779                 default:
780             }
781             if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here " + Integer.toString(op&0xff,16));
782             p += 1 + j;
783         }
784         
785         // Pass2 - Widen instructions if they can possibly be too short
786         for(i=0;i<size;i++) {
787             switch(op[i]) {
788                 case GOTO:
789                 case JSR: {
790                     int arg = ((Integer)this.arg[i]).intValue();
791                     int diff = maxpc[arg] - maxpc[i];
792                     if(diff < -32768 || diff > 32767)
793                         op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
794                     break;
795                 }
796             }
797         }
798         
799         // Pass3 - Calculate actual pc
800         for(i=0, p=0;i<size;i++) {
801             byte op = this.op[i];
802             pc[i] = p;
803             switch(op) {
804                 case NOP:
805                     if(EMIT_NOPS) p++;
806                     break;
807                 case TABLESWITCH:
808                 case LOOKUPSWITCH: {
809                     SI si = (SI) arg[i];
810                     p++; // opcode itself
811                     p = (p + 3) & ~3; // padding
812                     p += 4; // default
813                     if(op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
814                     else p += 4 + si.size() * 4 * 2; // count, key, val * targets
815                     break;
816                 }
817                 case WIDE:
818                     p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
819                     break;                
820                 default: {
821                     int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
822                     if(l == 7) throw new Error("shouldn't be here");
823                     p += 1 + l;                    
824                 }
825             }
826         }
827         int codeSize = p;
828         
829         if(codeSize >= 65536) throw new ClassFile.Exn("method too large in size");
830         
831         o.writeShort(maxStack);
832         o.writeShort(maxLocals);
833         o.writeInt(codeSize);
834         
835         // Pass 4 - Actually write the bytecodes
836         for(i=0;i<size;i++) {
837             byte op = this.op[i];
838             int opdata = OP_DATA[op&0xff];
839             if(op == NOP && !EMIT_NOPS) continue;
840             o.writeByte(op);
841             int argLength = opdata & OP_ARG_LENGTH_MASK;
842             
843             if(argLength == 0) continue; // skip if no args
844             
845             // Write args
846             Object arg = this.arg[i];  
847             
848             switch(op) {
849                 case IINC: {
850                     Pair pair = (Pair) arg;
851                     if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassFile.Exn("overflow of iinc arg"); 
852                     o.writeByte(pair.i1);
853                     o.writeByte(pair.i2);
854                     break;
855                 }
856                 case TABLESWITCH:
857                 case LOOKUPSWITCH: {
858                     SI si = (SI) arg;
859                     int mypc = pc[i];
860                     for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
861                     o.writeInt(pc[si.getDefaultTarget()] - mypc);
862                     if(op == LOOKUPSWITCH) {
863                         int[] vals = ((LSI)si).vals;
864                         o.writeInt(si.size());
865                         for(int j=0;j<si.size();j++) {
866                             o.writeInt(vals[j]);
867                             o.writeInt(pc[si.getTarget(j)] - mypc);
868                         }
869                     } else {
870                         TSI tsi = (TSI) si;
871                         o.writeInt(tsi.lo);
872                         o.writeInt(tsi.hi);
873                         for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
874                     }
875                     break;
876                 }
877                 case WIDE: {
878                     Wide wide = (Wide) arg;
879                     o.writeByte(wide.op);
880                     o.writeShort(wide.varNum);
881                     if(wide.op == IINC) o.writeShort(wide.n);
882                     break;
883                 }
884                 case MULTIANEWARRAY: {
885                     o.writeShort(cp.getIndex(cparg[i]));
886                     int v = ((MultiANewArray) arg).dims;
887                     if(v >= 256) throw new ClassFile.Exn("overflow of dimensions in multianewarray");
888                     o.writeByte(v);
889                     break;
890                 }
891                 case INVOKEINTERFACE:
892                     o.writeShort(cp.getIndex(cparg[i]));
893                     o.writeByte(((Type.Class.Method)arg).argTypes.length + 1);
894                     o.writeByte(0);
895                     break;
896                 default:
897                     if((opdata & OP_BRANCH_FLAG) != 0) {
898                         int v = pc[((Integer)arg).intValue()] - pc[i];
899                         if(argLength == 2) {
900                             if(v < -32768 || v > 32767) throw new ClassFile.Exn("overflow of s2 offset");
901                             o.writeShort(v);
902                         } else if(argLength == 4) {
903                             o.writeInt(v);
904                         } else {
905                             throw new Error("should never happen");
906                         }
907                     } else if((opdata & OP_CPENT_FLAG) != 0) {
908                         int v = cp.getIndex(cparg[i]);
909                         if(argLength == 1) o.writeByte(v);
910                         else if(argLength == 2) o.writeShort(v);
911                         else throw new Error("should never happen");
912                     } else if(argLength == 7) {
913                         throw new Error("should never happen - variable length instruction not explicitly handled");
914                     } else {
915                         int iarg  = ((Integer)arg).intValue();
916                         if(argLength == 1) {
917                             if((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 256 : (iarg < -128 || iarg >= 128)) throw new ClassFile.Exn("overflow of s/u1 option");
918                             o.writeByte(iarg);
919                         } else if(argLength == 2) {
920                             if((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 65536 : (iarg < -32768 || iarg >= 32768)) throw new ClassFile.Exn("overflow of s/u2 option");
921                             o.writeShort(iarg);
922                         } else {
923                             throw new Error("should never happen");
924                         }
925                     }
926                     break;
927             }
928         }
929
930         //if(baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
931         
932         o.writeShort(exnTable.size());
933         for(i=0;i<exnTable.size();i++)
934             ((ExnTableEnt)exnTable.elementAt(i)).dump(o, pc, codeSize, cp);
935         
936         codeAttrs.dump(o,cp);
937         baos.close();
938         
939         byte[] codeAttribute = baos.toByteArray();
940         attrs.put("Code", codeAttribute);        
941     }
942         
943     void generateExceptions(ConstantPool cp) throws IOException {
944         if(thrownExceptions.size() > 0) {
945             ByteArrayOutputStream baos = new ByteArrayOutputStream();
946             DataOutputStream o = new DataOutputStream(baos);
947             o.writeShort(thrownExceptions.size());
948             for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
949                 o.writeShort(cp.getIndex(thrownExceptions.get(e.nextElement())));
950             baos.close();
951             attrs.put("Exceptions", baos.toByteArray());
952         }
953     }
954     
955     void dump(DataOutput o, ConstantPool cp) throws IOException {
956         if((flags & (ACC_NATIVE|ACC_ABSTRACT))==0) generateCode(cp);
957         generateExceptions(cp);
958         
959         o.writeShort(flags);
960         o.writeShort(cp.getUtf8Index(name));
961         o.writeShort(cp.getUtf8Index(Type.methodTypeDescriptor(args,ret)));
962         attrs.dump(o,cp);
963     }
964     
965     /** Negates the IF* instruction, <i>op</i>  (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ,  etc)
966         @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
967     public static byte negate(byte op) {
968         switch(op) {
969             case IFEQ: return IFNE;
970             case IFNE: return IFEQ;
971             case IFLT: return IFGE;
972             case IFGE: return IFLT;
973             case IFGT: return IFLE;
974             case IFLE: return IFGT;
975             case IF_ICMPEQ: return IF_ICMPNE;
976             case IF_ICMPNE: return IF_ICMPEQ;
977             case IF_ICMPLT: return IF_ICMPGE;
978             case IF_ICMPGE: return IF_ICMPLT;
979             case IF_ICMPGT: return IF_ICMPLE;
980             case IF_ICMPLE: return IF_ICMPGT;
981             case IF_ACMPEQ: return IF_ACMPNE;
982             case IF_ACMPNE: return IF_ACMPEQ;
983             
984             default:
985                 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
986         }
987     }
988     
989     /** Class that represents a target that isn't currently know. The target MUST be set with setTarget() before the classfile is written. 
990         This class is more or less a mutable integer */
991     public static class PhantomTarget {
992         private int target = -1;
993         public void setTarget(int target) { this.target = target; }
994         public int getTarget() { return target; }
995     }
996     
997     private static Integer N(int n) { return new Integer(n); }
998     private static Long N(long n) { return new Long(n); }
999     private static Float N(float f) { return new Float(f); }
1000     private static Double N(double d) { return new Double(d); }
1001     private static int max(int a, int b) { return a > b ? a : b; }
1002     
1003     private static final int OP_BRANCH_FLAG = 1<<3;
1004     private static final int OP_CPENT_FLAG = 1<<4;
1005     private static final int OP_UNSIGNED_FLAG = 1<<5;
1006     private static final int OP_VALID_FLAG = 1<<6;
1007     private static final int OP_ARG_LENGTH_MASK = 7;
1008     private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
1009     private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
1010     private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
1011     private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
1012     private static final boolean OP_UNSIGNED(byte op) { return (OP_DATA[op&0xff]&OP_UNSIGNED_FLAG ) != 0; }
1013     
1014     // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
1015     private static final byte[] OP_DATA = {
1016         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
1017         0x41, 0x42, 0x51, 0x52, 0x52, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
1018         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
1019         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 
1020         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
1021         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
1022         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
1023         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
1024         0x40, 0x40, 0x40, 0x40, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
1025         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 
1026         0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x41, 0x47, 0x47, 0x40, 0x40, 0x40, 0x40, 
1027         0x40, 0x40, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x54, 0x01, 0x52, 0x41, 0x52, 0x40, 0x40, 
1028         0x52, 0x52, 0x40, 0x40, 0x47, 0x53, 0x4a, 0x4a, 0x4c, 0x4c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
1029         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
1030         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
1031         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
1032     };
1033 }