minor bug fixes
[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 public class MethodGen implements CGConst {
7     private final static boolean EMIT_NOPS = false;
8     
9     private static final int NO_CODE = -1;
10     private static final int FINISHED = -2;
11     
12     private final CPGen cp;
13     private final String name;
14     private final Type ret;
15     private final Type[] args;
16     private final int flags;
17     private final ClassGen.AttrGen attrs;
18     private final ClassGen.AttrGen codeAttrs;
19     private final Hashtable exnTable = new Hashtable();
20     private final Hashtable thrownExceptions = new Hashtable();
21     
22     private int maxStack = 16;
23     private int maxLocals;
24     
25     private int size;
26     private int capacity;
27     private byte[] op;
28     private Object[] arg;
29     
30     MethodGen(ClassGen owner, String name, Type ret, Type[] args, int flags) {
31         if((flags & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) != 0)
32             throw new IllegalArgumentException("invalid flags");
33         this.cp = owner.cp;
34         this.name = name;
35         this.ret = ret;
36         this.args = args;
37         this.flags = flags;
38         
39         attrs = new ClassGen.AttrGen(cp);
40         codeAttrs = new ClassGen.AttrGen(cp);
41         
42         cp.addUtf8(name);
43         cp.addUtf8(getDescriptor());
44         
45         if((owner.flags & ACC_INTERFACE) != 0 || (flags & (ACC_ABSTRACT|ACC_NATIVE)) != 0) size = capacity = -1;
46         
47         maxLocals = Math.max(args.length + (flags&ACC_STATIC)==0 ? 1 : 0,4);
48     }
49     
50     public String getDescriptor() { return MethodRef.getDescriptor(ret,args); }
51     
52     private class ExnTableEnt {
53         public int start;
54         public int end;
55         public int handler;
56         public CPGen.Ent typeEnt;
57         public ExnTableEnt(int start, int end, int handler, CPGen.Ent typeEnt) {
58             this.start = start;
59             this.end = end;
60             this.handler = handler;
61             this.typeEnt = typeEnt;
62         }
63         public void dump(DataOutput o, int[] pc, int endPC) throws IOException {
64             o.writeShort(pc[start]);
65             o.writeShort(end==pc.length ? endPC : pc[end]);
66             o.writeShort(pc[handler]);
67             o.writeShort(typeEnt.getIndex());
68         }
69     }
70     
71     public final void addExceptionHandler(int startPC, int endPC, int handlerPC, Type.Object type) {
72         exnTable.put(type, new ExnTableEnt(startPC,endPC,handlerPC,cp.add(type)));
73     }
74     
75     public final void addThrow(Type.Object type) {
76         thrownExceptions.put(type,cp.add(type));
77     }
78     
79     private final void grow() { if(size == capacity) grow(size+1); }
80     private final void grow(int newCap) {
81         if(capacity == NO_CODE) throw new IllegalStateException("method can't have code");
82         if(capacity == FINISHED) throw new IllegalStateException("method has been finished");
83         if(newCap <= capacity) return;
84         newCap = Math.max(newCap,capacity == 0 ? 256 : capacity*2);
85         
86         byte[] op2 = new byte[newCap];
87         if(capacity != 0) System.arraycopy(op,0,op2,0,size);
88         op = op2;
89         
90         Object[] arg2 = new Object[newCap];
91         if(capacity != 0) System.arraycopy(arg,0,arg2,0,size);
92         arg = arg2;
93         
94         capacity = newCap;
95     }
96     public final int size() { return size; }
97     
98     // These two are optimized for speed, they don't call set() below
99     public final int add(byte op) {
100         int s = size;
101         if(s == capacity) grow();
102         this.op[s] = op;
103         size++;
104         return s;
105     }
106     public final void set(int pos, byte op) { this.op[pos] = op; }
107         
108     public final int add(byte op, Object arg) { if(capacity == size) grow(); set(size,op,arg); return size++; }
109     public final int add(byte op, boolean arg) { if(capacity == size) grow(); set(size,op,arg); return size++; }
110     public final int add(byte op, int arg) { if(capacity == size) grow(); set(size,op,arg); return size++; }
111     
112     public final byte get(int pos) { return op[pos]; }
113     public final Object getArg(int pos) { return arg[pos]; }
114     
115     public final void setArg(int pos, int arg) { set(pos,op[pos],N(arg)); }
116     public final void setArg(int pos, Object arg) { set(pos,op[pos],arg); }
117     
118     
119     public final void set(int pos, byte op, boolean b) { set(pos,op,b?1:0); }
120     
121     // This MUST handle x{LOAD,STORE} and LDC with an int arg WITHOUT falling back to set(int,byte,Object)
122     public final void set(int pos, byte op, int n) {
123         Object arg = null;
124         OUTER: switch(op) {
125             case LDC:
126                 switch(n) {
127                     case -1: op = ICONST_M1; break OUTER;
128                     case 0:  op = ICONST_0;  break OUTER;
129                     case 1:  op = ICONST_1;  break OUTER;
130                     case 2:  op = ICONST_2;  break OUTER; 
131                     case 3:  op = ICONST_3;  break OUTER;
132                     case 4:  op = ICONST_4;  break OUTER;
133                     case 5:  op = ICONST_5;  break OUTER;
134                 }
135                 if(n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); } 
136                 else if(n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
137                 else { arg = cp.add(N(n)); }
138                 break;
139             case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
140             case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
141                 if(n >= 0 && n <= 3) {
142                     byte base = 0;
143                     switch(op) {
144                         case ILOAD:  base = ILOAD_0;  break;
145                         case ISTORE: base = ISTORE_0; break;
146                         case LLOAD:  base = LLOAD_0;  break;
147                         case LSTORE: base = LSTORE_0; break; 
148                         case FLOAD:  base = FLOAD_0;  break;
149                         case FSTORE: base = FSTORE_0; break;
150                         case DLOAD:  base = DLOAD_0;  break;
151                         case DSTORE: base = DSTORE_0; break;
152                         case ALOAD:  base = ALOAD_0;  break;
153                         case ASTORE: base = ASTORE_0; break;
154                     }
155                     op = (byte)((base&0xff) + n);
156                 } else {
157                     if(n >= maxLocals) maxLocals = n + 1;
158                     arg = N(n);
159                 }
160                 break;
161             default:
162                 set(pos,op,N(n));
163                 return;
164         }            
165         this.op[pos] = op;
166         this.arg[pos] = arg;
167     }
168     
169     public final void set(int pos, byte op, Object arg) {
170         switch(op) {
171             case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
172             case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
173                 // set(int,byte,int) always handles these ops itself
174                 set(pos,op,((Integer)arg).intValue());
175                 return;
176             case LDC:
177                 // set(int,byte,int) always handles these opts itself
178                 if(arg instanceof Integer) { set(pos,op,((Integer)arg).intValue()); return; }
179                 if(arg instanceof Boolean) { set(pos,op,((Boolean)arg).booleanValue()); return; }
180                 
181                 if(arg instanceof Long) {
182                     long l = ((Long)arg).longValue();
183                     if(l == 0L) { this.op[pos] = LCONST_0; return; }
184                     if(l == 1L) { this.op[pos] = LCONST_1; return; }
185                 }
186                 
187                 if(arg instanceof Long || arg instanceof Double) op = LDC2_W;
188                 break;
189             case INVOKEINTERFACE:
190                 if(arg instanceof MethodRef) arg = new MethodRef.I((MethodRef)arg);
191                 break;
192         }
193         int opdata = OP_DATA[op&0xff];
194         if((opdata&OP_CPENT_FLAG) != 0 && !(arg instanceof CPGen.Ent))
195             arg = cp.add(arg);
196         else if((opdata&OP_VALID_FLAG) == 0)
197             throw new IllegalArgumentException("unknown bytecode");
198         this.op[pos] = op;
199         this.arg[pos] = arg;
200     }
201     
202     public static class SI {
203         public final Object[] targets;
204         public Object defaultTarget;
205
206         SI(int size) { targets = new Object[size]; }
207         public void setTarget(int pos, Object val) { targets[pos] = val; }
208         public void setTarget(int pos, int val) { targets[pos] = N(val); }
209         public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
210         public void setDefaultTarget(Object o) { defaultTarget = o; }
211         public int size() { return targets.length; }
212         
213         public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
214         public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }        
215     }
216     
217     public static class TSI extends SI {
218         public final int lo;
219         public final int hi;
220         public int defaultTarget = -1;
221         public TSI(int lo, int hi) {
222             super(hi-lo+1);
223             this.lo = lo;
224             this.hi = hi;
225         }
226         public void setTargetForVal(int val, Object o) { setTarget(val-lo,o); }
227         public void setTargetForVal(int val, int n) { setTarget(val-lo,n); }
228     }
229     
230     public static class LSI extends SI {
231         public final int[] vals;
232         public LSI(int size) {
233            super(size);
234            this.vals = new int[size];
235         }
236         public final void setVal(int pos, int val) { vals[pos] = val; }
237     }
238     
239     public static class Pair {
240         public int i1;
241         public int i2;
242         public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
243     }
244         
245     public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
246     public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
247     
248     public void finish() {
249         try {
250             _finish();
251         } catch(IOException e) {
252             throw new Error("should never happen");
253         }
254     }
255     
256     private Object resolveTarget(Object arg) {
257         int target;
258         if(arg instanceof PhantomTarget) {
259             target = ((PhantomTarget)arg).getTarget();
260             if(target == -1) throw new IllegalStateException("unresolved phantom target");
261             arg = N(target);
262         } else {
263             target = ((Integer)arg).intValue();
264         }
265         if(target < 0 || target >= size)
266             throw new IllegalStateException("invalid target address");
267         return arg;
268     }
269     
270     private void _finish() throws IOException {
271         if(size == FINISHED) return;
272         
273         ByteArrayOutputStream baos = new ByteArrayOutputStream();
274         DataOutput o = new DataOutputStream(baos);
275     
276         int[] pc = new int[size];
277         int[] maxpc = pc;
278         int p,i;
279         
280         // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
281         for(i=0,p=0;i<size;i++) {
282             byte op = this.op[i];
283             int opdata = OP_DATA[op&0xff];
284             int j;
285             maxpc[i] = p;
286             
287             if((opdata & OP_BRANCH_FLAG)!= 0) { 
288                 try { 
289                     arg[i] = resolveTarget(arg[i]);
290                 } catch(RuntimeException e) {
291                     System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
292                     throw e;
293                 }
294             }
295             
296             switch(op) {
297                 case GOTO:
298                 case JSR:
299                     p += 3;
300                     break;
301                 case NOP:
302                     if(EMIT_NOPS) p++;
303                     break;
304                 case LOOKUPSWITCH:
305                 case TABLESWITCH: {
306                     SI si = (SI) arg[i];
307                     Object[] targets = si.targets;
308                     for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
309                     si.defaultTarget = resolveTarget(si.defaultTarget);
310                     p += 1 + 3 + 4; // opcode itself, padding, default
311                     if(op == TABLESWITCH) p += 4 + 4 + targets.length * 4; // lo, hi, targets
312                     else p += 4 + targets.length * 4 * 2; // count, key,val * targets
313                     if(op == LOOKUPSWITCH) {
314                         int[] vals = ((LSI)si).vals;
315                         for(j=1;j<vals.length;j++)
316                             if(vals[j] <= vals[j-1])
317                                 throw new IllegalStateException("out of order/duplicate lookupswitch values");
318                     }
319                     break;
320                 }
321                 case LDC:
322                     j = ((CPGen.Ent)arg[i]).getIndex();
323                     if(j >= 256) this.op[i] = op = LDC_W;
324                     // fall through
325                 default:
326                     if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here");
327                     p += 1 + j;
328                     break;
329             }
330         }
331         
332         // Pass2 - Widen instructions if they can possibly be too short
333         for(i=0;i<size;i++) {
334             switch(op[i]) {
335                 case GOTO:
336                 case JSR: {
337                     int arg = ((Integer)this.arg[i]).intValue();
338                     int diff = maxpc[arg] - maxpc[i];
339                     if(diff < -32768 || diff > 32767)
340                         op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
341                     break;
342                 }
343             }
344         }
345         
346         // Pass3 - Calculate actual pc
347         for(i=0,p=0;i<size;i++) {
348             byte op = this.op[i];
349             pc[i] = p;
350             switch(op) {
351                 case NOP:
352                     if(EMIT_NOPS) p++;
353                     break;
354                 case TABLESWITCH:
355                 case LOOKUPSWITCH: {
356                     SI si = (SI) arg[i];
357                     p++; // opcode itself
358                     p = (p + 3) & ~3; // padding
359                     p += 4; // default
360                     if(op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
361                     else p += 4 + si.size() * 4 * 2; // count, key,val * targets
362                     break;
363                 }
364                 default: {
365                     int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
366                     if(l == 7) throw new Error("shouldn't be here");
367                     p += 1 + l;                    
368                 }
369             }
370         }
371         
372         int codeSize = p;
373         
374         if(codeSize >= 65536) throw new ClassGen.Exn("method too large in size");
375         
376         o.writeShort(maxStack);
377         o.writeShort(maxLocals);
378         o.writeInt(codeSize);
379         
380         // Pass 4 - Actually write the bytecodes
381         for(i=0;i<size;i++) {
382             byte op = this.op[i];
383             int opdata = OP_DATA[op&0xff];
384             if(op == NOP && !EMIT_NOPS) continue;
385             
386             o.writeByte(op&0xff);
387             int argLength = opdata & OP_ARG_LENGTH_MASK;
388             
389             if(argLength == 0) continue; // skip if no args
390             
391             // Write args
392             Object arg = this.arg[i];  
393             
394             switch(op) {
395                 case IINC: {
396                     Pair pair = (Pair) arg;
397                     if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassGen.Exn("overflow of iinc arg"); 
398                     o.writeByte(pair.i1);
399                     o.writeByte(pair.i2);
400                 }
401                 case TABLESWITCH:
402                 case LOOKUPSWITCH: {
403                     SI si = (SI) arg;
404                     int mypc = pc[i];
405                     for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
406                     o.writeInt(pc[si.getDefaultTarget()] - mypc);
407                     if(op == LOOKUPSWITCH) {
408                         int[] vals = ((LSI)si).vals;
409                         o.writeInt(si.size());
410                         for(int j=0;j<si.size();j++) {
411                             o.writeInt(vals[j]);
412                             o.writeInt(pc[si.getTarget(j)] - mypc);
413                         }
414                     } else {
415                         TSI tsi = (TSI) si;
416                         o.writeInt(tsi.lo);
417                         o.writeInt(tsi.hi);
418                         for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
419                     }
420                     break;
421                 }
422                 case WIDE:
423                     throw new Error("WIDE instruction not yet supported");
424                     
425                 default:
426                     if((opdata & OP_BRANCH_FLAG) != 0) {
427                         int v = pc[((Integer)arg).intValue()] - pc[i];
428                         if(argLength == 2) {
429                             if(v < -32768 || v > 32767) throw new ClassGen.Exn("overflow of s2 offset");
430                             o.writeShort(v);
431                         } else if(argLength == 4) {
432                             o.writeInt(v);
433                         } else {
434                             throw new Error("should never happen");
435                         }
436                     } else if((opdata & OP_CPENT_FLAG) != 0) {
437                         int v = ((CPGen.Ent)arg).getIndex();
438                         if(argLength == 1) o.writeByte(v);
439                         else if(argLength == 2) o.writeShort(v);
440                         else throw new Error("should never happen");
441                     } else if(argLength == 7) {
442                         throw new Error("should never happen - variable length instruction not explicitly handled");
443                     } else {
444                         int iarg  = ((Integer)arg).intValue();
445                         if(argLength == 1) {
446                             if(iarg < -128 || iarg >= 256) throw new ClassGen.Exn("overflow of s/u1 option");
447                             o.writeByte(iarg);
448                         } else if(argLength == 2) {
449                             if(iarg < -32768 || iarg >= 65536) throw new ClassGen.Exn("overflow of s/u2 option"); 
450                             o.writeShort(iarg);
451                         } else {
452                             throw new Error("should never happen");
453                         }
454                     }
455                     break;
456             }
457         }
458
459         //if(baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
460         
461         o.writeShort(exnTable.size());
462         for(Enumeration e = exnTable.keys();e.hasMoreElements();)
463             ((ExnTableEnt)exnTable.get(e.nextElement())).dump(o,pc,codeSize);
464         
465         o.writeShort(codeAttrs.size());
466         codeAttrs.dump(o);
467         
468         baos.close();
469         
470         byte[] codeAttribute = baos.toByteArray();
471         attrs.add("Code",codeAttribute);
472         
473         baos.reset();
474         o.writeShort(thrownExceptions.size());
475         for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
476             o.writeShort(((CPGen.Ent)thrownExceptions.get(e.nextElement())).getIndex());
477         attrs.add("Exceptions",baos.toByteArray());
478         
479         size = capacity = FINISHED;        
480     }
481         
482     public void dump(DataOutput o) throws IOException {
483         o.writeShort(flags);
484         o.writeShort(cp.getUtf8Index(name));
485         o.writeShort(cp.getUtf8Index(getDescriptor()));
486         o.writeShort(attrs.size());
487         attrs.dump(o);
488     }
489     
490     public static byte negate(byte op) {
491         switch(op) {
492             case IFEQ: return IFNE;
493             case IFNE: return IFEQ;
494             case IFLT: return IFGE;
495             case IFGE: return IFLT;
496             case IFGT: return IFLE;
497             case IFLE: return IFGT;
498             case IF_ICMPEQ: return IF_ICMPNE;
499             case IF_ICMPNE: return IF_ICMPEQ;
500             case IF_ICMPLT: return IF_ICMPGE;
501             case IF_ICMPGE: return IF_ICMPLT;
502             case IF_ICMPGT: return IF_ICMPLE;
503             case IF_ICMPLE: return IF_ICMPGT;
504             case IF_ACMPEQ: return IF_ACMPNE;
505             case IF_ACMPNE: return IF_ACMPEQ;
506             
507             default:
508                 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
509         }
510     }
511     
512     public static class PhantomTarget {
513         private int target = -1;
514         public void setTarget(int target) { this.target = target; }
515         public int getTarget() { return target; }
516     }
517     
518     private static Integer N(int n) { return new Integer(n); }
519     private static Long N(long n) { return new Long(n); }
520     private static Float N(float f) { return new Float(f); }
521     private static Double N(double d) { return new Double(d); }
522     private static int max(int a, int b) { return a > b ? a : b; }
523     
524     private static final int OP_BRANCH_FLAG = 1<<3;
525     private static final int OP_CPENT_FLAG = 1<<4;
526     private static final int OP_VALID_FLAG = 1<<5;
527     private static final int OP_ARG_LENGTH_MASK = 7;
528     private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
529     private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
530     private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
531     private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
532     
533     // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
534     private static final byte[] OP_DATA = {
535             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
536             0x21, 0x22, 0x31, 0x32, 0x32, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
537             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
538             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20,
539             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
540             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
541             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
542             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
543             0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
544             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
545             0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x21, 0x27, 0x27, 0x20, 0x20, 0x20, 0x20,
546             0x20, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x01, 0x32, 0x21, 0x32, 0x20, 0x20,
547             0x32, 0x32, 0x20, 0x20, 0x27, 0x23, 0x2a, 0x2a, 0x2c, 0x2c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
548             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
549             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
550             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
551     };
552 }