0fc86066298e47146232b5920db5e474e9f2309f
[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 TSI(int lo, int hi) {
221             super(hi-lo+1);
222             this.lo = lo;
223             this.hi = hi;
224         }
225         public void setTargetForVal(int val, Object o) { setTarget(val-lo,o); }
226         public void setTargetForVal(int val, int n) { setTarget(val-lo,n); }
227     }
228     
229     public static class LSI extends SI {
230         public final int[] vals;
231         public LSI(int size) {
232            super(size);
233            this.vals = new int[size];
234         }
235         public final void setVal(int pos, int val) { vals[pos] = val; }
236     }
237     
238     public static class Pair {
239         public int i1;
240         public int i2;
241         public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
242     }
243         
244     public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
245     public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
246     
247     public void finish() {
248         try {
249             _finish();
250         } catch(IOException e) {
251             throw new Error("should never happen");
252         }
253     }
254     
255     private Object resolveTarget(Object arg) {
256         int target;
257         if(arg instanceof PhantomTarget) {
258             target = ((PhantomTarget)arg).getTarget();
259             if(target == -1) throw new IllegalStateException("unresolved phantom target");
260             arg = N(target);
261         } else {
262             target = ((Integer)arg).intValue();
263         }
264         if(target < 0 || target >= size)
265             throw new IllegalStateException("invalid target address");
266         return arg;
267     }
268     
269     private void _finish() throws IOException {
270         if(size == FINISHED) return;
271         
272         ByteArrayOutputStream baos = new ByteArrayOutputStream();
273         DataOutput o = new DataOutputStream(baos);
274     
275         int[] pc = new int[size];
276         int[] maxpc = pc;
277         int p,i;
278         
279         // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
280         for(i=0,p=0;i<size;i++) {
281             byte op = this.op[i];
282             int opdata = OP_DATA[op&0xff];
283             int j;
284             maxpc[i] = p;
285             
286             if((opdata & OP_BRANCH_FLAG)!= 0) { 
287                 try { 
288                     arg[i] = resolveTarget(arg[i]);
289                 } catch(RuntimeException e) {
290                     System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
291                     throw e;
292                 }
293             }
294             
295             switch(op) {
296                 case GOTO:
297                 case JSR:
298                     p += 3;
299                     break;
300                 case NOP:
301                     if(EMIT_NOPS) p++;
302                     break;
303                 case LOOKUPSWITCH:
304                 case TABLESWITCH: {
305                     SI si = (SI) arg[i];
306                     Object[] targets = si.targets;
307                     for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
308                     si.defaultTarget = resolveTarget(si.defaultTarget);
309                     p += 1 + 3 + 4; // opcode itself, padding, default
310                     if(op == TABLESWITCH) p += 4 + 4 + targets.length * 4; // lo, hi, targets
311                     else p += 4 + targets.length * 4 * 2; // count, key,val * targets
312                     if(op == LOOKUPSWITCH) {
313                         int[] vals = ((LSI)si).vals;
314                         for(j=1;j<vals.length;j++)
315                             if(vals[j] <= vals[j-1])
316                                 throw new IllegalStateException("out of order/duplicate lookupswitch values");
317                     }
318                     break;
319                 }
320                 case LDC:
321                     j = ((CPGen.Ent)arg[i]).getIndex();
322                     if(j >= 256) this.op[i] = op = LDC_W;
323                     // fall through
324                 default:
325                     if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here");
326                     p += 1 + j;
327                     break;
328             }
329         }
330         
331         // Pass2 - Widen instructions if they can possibly be too short
332         for(i=0;i<size;i++) {
333             switch(op[i]) {
334                 case GOTO:
335                 case JSR: {
336                     int arg = ((Integer)this.arg[i]).intValue();
337                     int diff = maxpc[arg] - maxpc[i];
338                     if(diff < -32768 || diff > 32767)
339                         op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
340                     break;
341                 }
342             }
343         }
344         
345         // Pass3 - Calculate actual pc
346         for(i=0,p=0;i<size;i++) {
347             byte op = this.op[i];
348             pc[i] = p;
349             switch(op) {
350                 case NOP:
351                     if(EMIT_NOPS) p++;
352                     break;
353                 case TABLESWITCH:
354                 case LOOKUPSWITCH: {
355                     SI si = (SI) arg[i];
356                     p++; // opcode itself
357                     p = (p + 3) & ~3; // padding
358                     p += 4; // default
359                     if(op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
360                     else p += 4 + si.size() * 4 * 2; // count, key,val * targets
361                     break;
362                 }
363                 default: {
364                     int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
365                     if(l == 7) throw new Error("shouldn't be here");
366                     p += 1 + l;                    
367                 }
368             }
369         }
370         
371         int codeSize = p;
372         
373         if(codeSize >= 65536) throw new ClassGen.Exn("method too large in size");
374         
375         o.writeShort(maxStack);
376         o.writeShort(maxLocals);
377         o.writeInt(codeSize);
378         
379         // Pass 4 - Actually write the bytecodes
380         for(i=0;i<size;i++) {
381             byte op = this.op[i];
382             int opdata = OP_DATA[op&0xff];
383             if(op == NOP && !EMIT_NOPS) continue;
384             
385             o.writeByte(op&0xff);
386             int argLength = opdata & OP_ARG_LENGTH_MASK;
387             
388             if(argLength == 0) continue; // skip if no args
389             
390             // Write args
391             Object arg = this.arg[i];  
392             
393             switch(op) {
394                 case IINC: {
395                     Pair pair = (Pair) arg;
396                     if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassGen.Exn("overflow of iinc arg"); 
397                     o.writeByte(pair.i1);
398                     o.writeByte(pair.i2);
399                     break;
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 }