• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License.  Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later,
9  * or the Apache License Version 2.0.
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  */
16 
17 package javassist.bytecode;
18 
19 import javassist.CtClass;
20 import javassist.CtPrimitiveType;
21 
22 class ByteVector implements Cloneable {
23     private byte[] buffer;
24     private int size;
25 
ByteVector()26     public ByteVector() {
27         buffer = new byte[64];
28         size = 0;
29     }
30 
31     @Override
clone()32     public Object clone() throws CloneNotSupportedException {
33         ByteVector bv = (ByteVector)super.clone();
34         bv.buffer = (byte[])buffer.clone();
35         return bv;
36     }
37 
getSize()38     public final int getSize() { return size; }
39 
copy()40     public final byte[] copy() {
41         byte[] b = new byte[size];
42         System.arraycopy(buffer, 0, b, 0, size);
43         return b;
44     }
45 
read(int offset)46     public int read(int offset) {
47         if (offset < 0 || size <= offset)
48             throw new ArrayIndexOutOfBoundsException(offset);
49 
50         return buffer[offset];
51     }
52 
write(int offset, int value)53     public void write(int offset, int value) {
54         if (offset < 0 || size <= offset)
55             throw new ArrayIndexOutOfBoundsException(offset);
56 
57         buffer[offset] = (byte)value;
58     }
59 
add(int code)60     public void add(int code) {
61         addGap(1);
62         buffer[size - 1] = (byte)code;
63     }
64 
add(int b1, int b2)65     public void add(int b1, int b2) {
66         addGap(2);
67         buffer[size - 2] = (byte)b1;
68         buffer[size - 1] = (byte)b2;
69     }
70 
add(int b1, int b2, int b3, int b4)71     public void add(int b1, int b2, int b3, int b4) {
72         addGap(4);
73         buffer[size - 4] = (byte)b1;
74         buffer[size - 3] = (byte)b2;
75         buffer[size - 2] = (byte)b3;
76         buffer[size - 1] = (byte)b4;
77     }
78 
addGap(int length)79     public void addGap(int length) {
80         if (size + length > buffer.length) {
81             int newSize = size << 1;
82             if (newSize < size + length)
83                 newSize = size + length;
84 
85             byte[] newBuf = new byte[newSize];
86             System.arraycopy(buffer, 0, newBuf, 0, size);
87             buffer = newBuf;
88         }
89 
90         size += length;
91     }
92 }
93 
94 /**
95  * A utility class for producing a bytecode sequence.
96  *
97  * <p>A <code>Bytecode</code> object is an unbounded array
98  * containing bytecode.  For example,
99  *
100  * <pre>
101  * ConstPool cp = ...;    // constant pool table
102  * Bytecode b = new Bytecode(cp, 1, 0);
103  * b.addIconst(3);
104  * b.addReturn(CtClass.intType);
105  * CodeAttribute ca = b.toCodeAttribute();</pre>
106  *
107  * <p>This program produces a Code attribute including a bytecode
108  * sequence:
109  *
110  * <pre>
111  * iconst_3
112  * ireturn</pre>
113  *
114  * @see ConstPool
115  * @see CodeAttribute
116  */
117 public class Bytecode extends ByteVector implements Cloneable, Opcode {
118     /**
119      * Represents the <code>CtClass</code> file using the
120      * constant pool table given to this <code>Bytecode</code> object.
121      */
122     public static final CtClass THIS = ConstPool.THIS;
123 
124     ConstPool constPool;
125     int maxStack, maxLocals;
126     ExceptionTable tryblocks;
127     private int stackDepth;
128 
129     /**
130      * Constructs a <code>Bytecode</code> object with an empty bytecode
131      * sequence.
132      *
133      * <p>The parameters <code>stacksize</code> and <code>localvars</code>
134      * specify initial values
135      * of <code>max_stack</code> and <code>max_locals</code>.
136      * They can be changed later.
137      *
138      * @param cp                constant pool table.
139      * @param stacksize         <code>max_stack</code>.
140      * @param localvars         <code>max_locals</code>.
141      */
Bytecode(ConstPool cp, int stacksize, int localvars)142     public Bytecode(ConstPool cp, int stacksize, int localvars) {
143         constPool = cp;
144         maxStack = stacksize;
145         maxLocals = localvars;
146         tryblocks = new ExceptionTable(cp);
147         stackDepth = 0;
148     }
149 
150     /**
151      * Constructs a <code>Bytecode</code> object with an empty bytecode
152      * sequence.  The initial values of <code>max_stack</code> and
153      * <code>max_locals</code> are zero.
154      *
155      * @param cp            constant pool table.
156      * @see Bytecode#setMaxStack(int)
157      * @see Bytecode#setMaxLocals(int)
158      */
Bytecode(ConstPool cp)159     public Bytecode(ConstPool cp) {
160         this(cp, 0, 0);
161     }
162 
163     /**
164      * Creates and returns a copy of this object.
165      * The constant pool object is shared between this object
166      * and the cloned object.
167      */
168     @Override
clone()169     public Object clone() {
170         try {
171             Bytecode bc = (Bytecode)super.clone();
172             bc.tryblocks = (ExceptionTable)tryblocks.clone();
173             return bc;
174         }
175         catch (CloneNotSupportedException cnse) {
176             throw new RuntimeException(cnse);
177         }
178     }
179 
180     /**
181      * Gets a constant pool table.
182      */
getConstPool()183     public ConstPool getConstPool() { return constPool; }
184 
185     /**
186      * Returns <code>exception_table</code>.
187      */
getExceptionTable()188     public ExceptionTable getExceptionTable() { return tryblocks; }
189 
190     /**
191      * Converts to a <code>CodeAttribute</code>.
192      */
toCodeAttribute()193     public CodeAttribute toCodeAttribute() {
194         return new CodeAttribute(constPool, maxStack, maxLocals,
195                                  get(), tryblocks);
196     }
197 
198     /**
199      * Returns the length of the bytecode sequence.
200      */
length()201     public int length() {
202         return getSize();
203     }
204 
205     /**
206      * Returns the produced bytecode sequence.
207      */
get()208     public byte[] get() {
209         return copy();
210     }
211 
212     /**
213      * Gets <code>max_stack</code>.
214      */
getMaxStack()215     public int getMaxStack() { return maxStack; }
216 
217     /**
218      * Sets <code>max_stack</code>.
219      *
220      * <p>This value may be automatically updated when an instruction
221      * is appended.  A <code>Bytecode</code> object maintains the current
222      * stack depth whenever an instruction is added
223      * by <code>addOpcode()</code>.  For example, if DUP is appended,
224      * the current stack depth is increased by one.  If the new stack
225      * depth is more than <code>max_stack</code>, then it is assigned
226      * to <code>max_stack</code>.  However, if branch instructions are
227      * appended, the current stack depth may not be correctly maintained.
228      *
229      * @see #addOpcode(int)
230      */
setMaxStack(int size)231     public void setMaxStack(int size) {
232         maxStack = size;
233     }
234 
235     /**
236      * Gets <code>max_locals</code>.
237      */
getMaxLocals()238     public int getMaxLocals() { return maxLocals; }
239 
240     /**
241      * Sets <code>max_locals</code>.
242      */
setMaxLocals(int size)243     public void setMaxLocals(int size) {
244         maxLocals = size;
245     }
246 
247     /**
248      * Sets <code>max_locals</code>.
249      *
250      * <p>This computes the number of local variables
251      * used to pass method parameters and sets <code>max_locals</code>
252      * to that number plus <code>locals</code>.
253      *
254      * @param isStatic          true if <code>params</code> must be
255      *                          interpreted as parameters to a static method.
256      * @param params            parameter types.
257      * @param locals            the number of local variables excluding
258      *                          ones used to pass parameters.
259      */
setMaxLocals(boolean isStatic, CtClass[] params, int locals)260     public void setMaxLocals(boolean isStatic, CtClass[] params,
261                              int locals) {
262         if (!isStatic)
263             ++locals;
264 
265         if (params != null) {
266             CtClass doubleType = CtClass.doubleType;
267             CtClass longType = CtClass.longType;
268             int n = params.length;
269             for (int i = 0; i < n; ++i) {
270                 CtClass type = params[i];
271                 if (type == doubleType || type == longType)
272                     locals += 2;
273                 else
274                     ++locals;
275             }
276         }
277 
278         maxLocals = locals;
279     }
280 
281     /**
282      * Increments <code>max_locals</code>.
283      */
incMaxLocals(int diff)284     public void incMaxLocals(int diff) {
285         maxLocals += diff;
286     }
287 
288     /**
289      * Adds a new entry of <code>exception_table</code>.
290      */
addExceptionHandler(int start, int end, int handler, CtClass type)291     public void addExceptionHandler(int start, int end,
292                                     int handler, CtClass type) {
293         addExceptionHandler(start, end, handler,
294                             constPool.addClassInfo(type));
295     }
296 
297     /**
298      * Adds a new entry of <code>exception_table</code>.
299      *
300      * @param type      the fully-qualified name of a throwable class.
301      */
addExceptionHandler(int start, int end, int handler, String type)302     public void addExceptionHandler(int start, int end,
303                                     int handler, String type) {
304         addExceptionHandler(start, end, handler,
305                             constPool.addClassInfo(type));
306     }
307 
308     /**
309      * Adds a new entry of <code>exception_table</code>.
310      */
addExceptionHandler(int start, int end, int handler, int type)311     public void addExceptionHandler(int start, int end,
312                                     int handler, int type) {
313         tryblocks.add(start, end, handler, type);
314     }
315 
316     /**
317      * Returns the length of bytecode sequence
318      * that have been added so far.
319      */
currentPc()320     public int currentPc() {
321         return getSize();
322     }
323 
324     /**
325      * Reads a signed 8bit value at the offset from the beginning of the
326      * bytecode sequence.
327      *
328      * @throws ArrayIndexOutOfBoundsException   if offset is invalid.
329      */
330     @Override
read(int offset)331     public int read(int offset) {
332         return super.read(offset);
333     }
334 
335     /**
336      * Reads a signed 16bit value at the offset from the beginning of the
337      * bytecode sequence.
338      */
read16bit(int offset)339     public int read16bit(int offset) {
340         int v1 = read(offset);
341         int v2 = read(offset + 1);
342         return (v1 << 8) + (v2 & 0xff);
343     }
344 
345     /**
346      * Reads a signed 32bit value at the offset from the beginning of the
347      * bytecode sequence.
348      */
read32bit(int offset)349     public int read32bit(int offset) {
350         int v1 = read16bit(offset);
351         int v2 = read16bit(offset + 2);
352         return (v1 << 16) + (v2 & 0xffff);
353     }
354 
355     /**
356      * Writes an 8bit value at the offset from the beginning of the
357      * bytecode sequence.
358      *
359      * @throws ArrayIndexOutOfBoundsException   if offset is invalid.
360      */
361     @Override
write(int offset, int value)362     public void write(int offset, int value) {
363         super.write(offset, value);
364     }
365 
366     /**
367      * Writes an 16bit value at the offset from the beginning of the
368      * bytecode sequence.
369      */
write16bit(int offset, int value)370     public void write16bit(int offset, int value) {
371         write(offset, value >> 8);
372         write(offset + 1, value);
373     }
374 
375     /**
376      * Writes an 32bit value at the offset from the beginning of the
377      * bytecode sequence.
378      */
write32bit(int offset, int value)379     public void write32bit(int offset, int value) {
380         write16bit(offset, value >> 16);
381         write16bit(offset + 2, value);
382     }
383 
384     /**
385      * Appends an 8bit value to the end of the bytecode sequence.
386      */
387     @Override
add(int code)388     public void add(int code) {
389         super.add(code);
390     }
391 
392     /**
393      * Appends a 32bit value to the end of the bytecode sequence.
394      */
add32bit(int value)395     public void add32bit(int value) {
396         add(value >> 24, value >> 16, value >> 8, value);
397     }
398 
399     /**
400      * Appends the length-byte gap to the end of the bytecode sequence.
401      *
402      * @param length    the gap length in byte.
403      */
404     @Override
addGap(int length)405     public void addGap(int length) {
406         super.addGap(length);
407     }
408 
409     /**
410      * Appends an 8bit opcode to the end of the bytecode sequence.
411      * The current stack depth is updated.
412      * <code>max_stack</code> is updated if the current stack depth
413      * is the deepest so far.
414      *
415      * <p>Note: some instructions such as INVOKEVIRTUAL does not
416      * update the current stack depth since the increment depends
417      * on the method signature.
418      * <code>growStack()</code> must be explicitly called.
419      */
addOpcode(int code)420     public void addOpcode(int code) {
421         add(code);
422         growStack(STACK_GROW[code]);
423     }
424 
425     /**
426      * Increases the current stack depth.
427      * It also updates <code>max_stack</code> if the current stack depth
428      * is the deepest so far.
429      *
430      * @param diff      the number added to the current stack depth.
431      */
growStack(int diff)432     public void growStack(int diff) {
433         setStackDepth(stackDepth + diff);
434     }
435 
436     /**
437      * Returns the current stack depth.
438      */
getStackDepth()439     public int getStackDepth() { return stackDepth; }
440 
441     /**
442      * Sets the current stack depth.
443      * It also updates <code>max_stack</code> if the current stack depth
444      * is the deepest so far.
445      *
446      * @param depth     new value.
447      */
setStackDepth(int depth)448     public void setStackDepth(int depth) {
449         stackDepth = depth;
450         if (stackDepth > maxStack)
451             maxStack = stackDepth;
452     }
453 
454     /**
455      * Appends a 16bit value to the end of the bytecode sequence.
456      * It never changes the current stack depth.
457      */
addIndex(int index)458     public void addIndex(int index) {
459         add(index >> 8, index);
460     }
461 
462     /**
463      * Appends ALOAD or (WIDE) ALOAD_&lt;n&gt;
464      *
465      * @param n         an index into the local variable array.
466      */
addAload(int n)467     public void addAload(int n) {
468         if (n < 4)
469             addOpcode(42 + n);          // aload_<n>
470         else if (n < 0x100) {
471             addOpcode(ALOAD);           // aload
472             add(n);
473         }
474         else {
475             addOpcode(WIDE);
476             addOpcode(ALOAD);
477             addIndex(n);
478         }
479     }
480 
481     /**
482      * Appends ASTORE or (WIDE) ASTORE_&lt;n&gt;
483      *
484      * @param n         an index into the local variable array.
485      */
addAstore(int n)486     public void addAstore(int n) {
487         if (n < 4)
488             addOpcode(75 + n);  // astore_<n>
489         else if (n < 0x100) {
490             addOpcode(ASTORE);          // astore
491             add(n);
492         }
493         else {
494             addOpcode(WIDE);
495             addOpcode(ASTORE);
496             addIndex(n);
497         }
498     }
499 
500     /**
501      * Appends ICONST or ICONST_&lt;n&gt;
502      *
503      * @param n         the pushed integer constant.
504      */
addIconst(int n)505     public void addIconst(int n) {
506         if (n < 6 && -2 < n)
507             addOpcode(3 + n);           // iconst_<i>   -1..5
508         else if (n <= 127 && -128 <= n) {
509             addOpcode(16);              // bipush
510             add(n);
511         }
512         else if (n <= 32767 && -32768 <= n) {
513             addOpcode(17);              // sipush
514             add(n >> 8);
515             add(n);
516         }
517         else
518             addLdc(constPool.addIntegerInfo(n));
519     }
520 
521     /**
522      * Appends an instruction for pushing zero or null on the stack.
523      * If the type is void, this method does not append any instruction.
524      *
525      * @param type      the type of the zero value (or null).
526      */
addConstZero(CtClass type)527     public void addConstZero(CtClass type) {
528         if (type.isPrimitive()) {
529             if (type == CtClass.longType)
530                 addOpcode(LCONST_0);
531             else if (type == CtClass.floatType)
532                 addOpcode(FCONST_0);
533             else if (type == CtClass.doubleType)
534                 addOpcode(DCONST_0);
535             else if (type == CtClass.voidType)
536                 throw new RuntimeException("void type?");
537             else
538                 addOpcode(ICONST_0);
539         }
540         else
541             addOpcode(ACONST_NULL);
542     }
543 
544     /**
545      * Appends ILOAD or (WIDE) ILOAD_&lt;n&gt;
546      *
547      * @param n         an index into the local variable array.
548      */
addIload(int n)549     public void addIload(int n) {
550         if (n < 4)
551             addOpcode(26 + n);          // iload_<n>
552         else if (n < 0x100) {
553             addOpcode(ILOAD);           // iload
554             add(n);
555         }
556         else {
557             addOpcode(WIDE);
558             addOpcode(ILOAD);
559             addIndex(n);
560         }
561     }
562 
563     /**
564      * Appends ISTORE or (WIDE) ISTORE_&lt;n&gt;
565      *
566      * @param n         an index into the local variable array.
567      */
addIstore(int n)568     public void addIstore(int n) {
569         if (n < 4)
570             addOpcode(59 + n);          // istore_<n>
571         else if (n < 0x100) {
572             addOpcode(ISTORE);          // istore
573             add(n);
574         }
575         else {
576             addOpcode(WIDE);
577             addOpcode(ISTORE);
578             addIndex(n);
579         }
580     }
581 
582     /**
583      * Appends LCONST or LCONST_&lt;n&gt;
584      *
585      * @param n         the pushed long integer constant.
586      */
addLconst(long n)587     public void addLconst(long n) {
588         if (n == 0 || n == 1)
589             addOpcode(9 + (int)n);              // lconst_<n>
590         else
591             addLdc2w(n);
592     }
593 
594     /**
595      * Appends LLOAD or (WIDE) LLOAD_&lt;n&gt;
596      *
597      * @param n         an index into the local variable array.
598      */
addLload(int n)599     public void addLload(int n) {
600         if (n < 4)
601             addOpcode(30 + n);          // lload_<n>
602         else if (n < 0x100) {
603             addOpcode(LLOAD);           // lload
604             add(n);
605         }
606         else {
607             addOpcode(WIDE);
608             addOpcode(LLOAD);
609             addIndex(n);
610         }
611     }
612 
613     /**
614      * Appends LSTORE or LSTORE_&lt;n&gt;
615      *
616      * @param n         an index into the local variable array.
617      */
addLstore(int n)618     public void addLstore(int n) {
619         if (n < 4)
620             addOpcode(63 + n);          // lstore_<n>
621         else if (n < 0x100) {
622             addOpcode(LSTORE);          // lstore
623             add(n);
624         }
625         else {
626             addOpcode(WIDE);
627             addOpcode(LSTORE);
628             addIndex(n);
629         }
630     }
631 
632     /**
633      * Appends DCONST or DCONST_&lt;n&gt;
634      *
635      * @param d         the pushed double constant.
636      */
addDconst(double d)637     public void addDconst(double d) {
638         if (d == 0.0 || d == 1.0)
639             addOpcode(14 + (int)d);             // dconst_<n>
640         else
641             addLdc2w(d);
642     }
643 
644     /**
645      * Appends DLOAD or (WIDE) DLOAD_&lt;n&gt;
646      *
647      * @param n         an index into the local variable array.
648      */
addDload(int n)649     public void addDload(int n) {
650         if (n < 4)
651             addOpcode(38 + n);          // dload_<n>
652         else if (n < 0x100) {
653             addOpcode(DLOAD);           // dload
654             add(n);
655         }
656         else {
657             addOpcode(WIDE);
658             addOpcode(DLOAD);
659             addIndex(n);
660         }
661     }
662 
663     /**
664      * Appends DSTORE or (WIDE) DSTORE_&lt;n&gt;
665      *
666      * @param n         an index into the local variable array.
667      */
addDstore(int n)668     public void addDstore(int n) {
669         if (n < 4)
670             addOpcode(71 + n);          // dstore_<n>
671         else if (n < 0x100) {
672             addOpcode(DSTORE);          // dstore
673             add(n);
674         }
675         else {
676             addOpcode(WIDE);
677             addOpcode(DSTORE);
678             addIndex(n);
679         }
680     }
681 
682     /**
683      * Appends FCONST or FCONST_&lt;n&gt;
684      *
685      * @param f         the pushed float constant.
686      */
addFconst(float f)687     public void addFconst(float f) {
688         if (f == 0.0f || f == 1.0f || f == 2.0f)
689             addOpcode(11 + (int)f);             // fconst_<n>
690         else
691             addLdc(constPool.addFloatInfo(f));
692     }
693 
694     /**
695      * Appends FLOAD or (WIDE) FLOAD_&lt;n&gt;
696      *
697      * @param n         an index into the local variable array.
698      */
addFload(int n)699     public void addFload(int n) {
700         if (n < 4)
701             addOpcode(34 + n);          // fload_<n>
702         else if (n < 0x100) {
703             addOpcode(FLOAD);           // fload
704             add(n);
705         }
706         else {
707             addOpcode(WIDE);
708             addOpcode(FLOAD);
709             addIndex(n);
710         }
711     }
712 
713     /**
714      * Appends FSTORE or FSTORE_&lt;n&gt;
715      *
716      * @param n         an index into the local variable array.
717      */
addFstore(int n)718     public void addFstore(int n) {
719         if (n < 4)
720             addOpcode(67 + n);          // fstore_<n>
721         else if (n < 0x100) {
722             addOpcode(FSTORE);          // fstore
723             add(n);
724         }
725         else {
726             addOpcode(WIDE);
727             addOpcode(FSTORE);
728             addIndex(n);
729         }
730     }
731 
732     /**
733      * Appends an instruction for loading a value from the
734      * local variable at the index <code>n</code>.
735      *
736      * @param n         the index.
737      * @param type      the type of the loaded value.
738      * @return          the size of the value (1 or 2 word).
739      */
addLoad(int n, CtClass type)740     public int addLoad(int n, CtClass type) {
741         if (type.isPrimitive()) {
742             if (type == CtClass.booleanType || type == CtClass.charType
743                 || type == CtClass.byteType || type == CtClass.shortType
744                 || type == CtClass.intType)
745                 addIload(n);
746             else if (type == CtClass.longType) {
747                 addLload(n);
748                 return 2;
749             }
750             else if(type == CtClass.floatType)
751                 addFload(n);
752             else if(type == CtClass.doubleType) {
753                 addDload(n);
754                 return 2;
755             }
756             else
757                 throw new RuntimeException("void type?");
758         }
759         else
760             addAload(n);
761 
762         return 1;
763     }
764 
765     /**
766      * Appends an instruction for storing a value into the
767      * local variable at the index <code>n</code>.
768      *
769      * @param n         the index.
770      * @param type      the type of the stored value.
771      * @return          2 if the type is long or double.  Otherwise 1.
772      */
addStore(int n, CtClass type)773     public int addStore(int n, CtClass type) {
774         if (type.isPrimitive()) {
775             if (type == CtClass.booleanType || type == CtClass.charType
776                 || type == CtClass.byteType || type == CtClass.shortType
777                 || type == CtClass.intType)
778                 addIstore(n);
779             else if (type == CtClass.longType) {
780                 addLstore(n);
781                 return 2;
782             }
783             else if (type == CtClass.floatType)
784                 addFstore(n);
785             else if (type == CtClass.doubleType) {
786                 addDstore(n);
787                 return 2;
788             }
789             else
790                 throw new RuntimeException("void type?");
791         }
792         else
793             addAstore(n);
794 
795         return 1;
796     }
797 
798     /**
799      * Appends instructions for loading all the parameters onto the
800      * operand stack.
801      *
802      * @param offset	the index of the first parameter.  It is 0
803      *			if the method is static.  Otherwise, it is 1.
804      */
addLoadParameters(CtClass[] params, int offset)805     public int addLoadParameters(CtClass[] params, int offset) {
806         int stacksize = 0;
807         if (params != null) {
808             int n = params.length;
809             for (int i = 0; i < n; ++i)
810                 stacksize += addLoad(stacksize + offset, params[i]);
811         }
812 
813         return stacksize;
814     }
815 
816     /**
817      * Appends CHECKCAST.
818      *
819      * @param c         the type.
820      */
addCheckcast(CtClass c)821     public void addCheckcast(CtClass c) {
822         addOpcode(CHECKCAST);
823         addIndex(constPool.addClassInfo(c));
824     }
825 
826     /**
827      * Appends CHECKCAST.
828      *
829      * @param classname         a fully-qualified class name.
830      */
addCheckcast(String classname)831     public void addCheckcast(String classname) {
832         addOpcode(CHECKCAST);
833         addIndex(constPool.addClassInfo(classname));
834     }
835 
836     /**
837      * Appends INSTANCEOF.
838      *
839      * @param classname         the class name.
840      */
addInstanceof(String classname)841     public void addInstanceof(String classname) {
842         addOpcode(INSTANCEOF);
843         addIndex(constPool.addClassInfo(classname));
844     }
845 
846     /**
847      * Appends GETFIELD.
848      *
849      * @param c         the class.
850      * @param name      the field name.
851      * @param type      the descriptor of the field type.
852      *
853      * @see Descriptor#of(CtClass)
854      */
addGetfield(CtClass c, String name, String type)855     public void addGetfield(CtClass c, String name, String type) {
856         add(GETFIELD);
857         int ci = constPool.addClassInfo(c);
858         addIndex(constPool.addFieldrefInfo(ci, name, type));
859         growStack(Descriptor.dataSize(type) - 1);
860     }
861 
862     /**
863      * Appends GETFIELD.
864      *
865      * @param c         the fully-qualified class name.
866      * @param name      the field name.
867      * @param type      the descriptor of the field type.
868      *
869      * @see Descriptor#of(CtClass)
870      */
addGetfield(String c, String name, String type)871     public void addGetfield(String c, String name, String type) {
872         add(GETFIELD);
873         int ci = constPool.addClassInfo(c);
874         addIndex(constPool.addFieldrefInfo(ci, name, type));
875         growStack(Descriptor.dataSize(type) - 1);
876     }
877 
878     /**
879      * Appends GETSTATIC.
880      *
881      * @param c         the class
882      * @param name      the field name
883      * @param type      the descriptor of the field type.
884      *
885      * @see Descriptor#of(CtClass)
886      */
addGetstatic(CtClass c, String name, String type)887     public void addGetstatic(CtClass c, String name, String type) {
888         add(GETSTATIC);
889         int ci = constPool.addClassInfo(c);
890         addIndex(constPool.addFieldrefInfo(ci, name, type));
891         growStack(Descriptor.dataSize(type));
892     }
893 
894     /**
895      * Appends GETSTATIC.
896      *
897      * @param c         the fully-qualified class name
898      * @param name      the field name
899      * @param type      the descriptor of the field type.
900      *
901      * @see Descriptor#of(CtClass)
902      */
addGetstatic(String c, String name, String type)903     public void addGetstatic(String c, String name, String type) {
904         add(GETSTATIC);
905         int ci = constPool.addClassInfo(c);
906         addIndex(constPool.addFieldrefInfo(ci, name, type));
907         growStack(Descriptor.dataSize(type));
908     }
909 
910     /**
911      * Appends INVOKESPECIAL.
912      *
913      * @param clazz     the target class.
914      * @param name      the method name.
915      * @param returnType        the return type.
916      * @param paramTypes        the parameter types.
917      */
addInvokespecial(CtClass clazz, String name, CtClass returnType, CtClass[] paramTypes)918     public void addInvokespecial(CtClass clazz, String name,
919                                  CtClass returnType, CtClass[] paramTypes) {
920         String desc = Descriptor.ofMethod(returnType, paramTypes);
921         addInvokespecial(clazz, name, desc);
922     }
923 
924     /**
925      * Appends INVOKESPECIAL.
926      *
927      * @param clazz     the target class.
928      * @param name      the method name
929      * @param desc      the descriptor of the method signature.
930      *
931      * @see Descriptor#ofMethod(CtClass,CtClass[])
932      * @see Descriptor#ofConstructor(CtClass[])
933      */
addInvokespecial(CtClass clazz, String name, String desc)934     public void addInvokespecial(CtClass clazz, String name, String desc) {
935         boolean isInterface = clazz == null ? false : clazz.isInterface();
936         addInvokespecial(isInterface,
937                          constPool.addClassInfo(clazz), name, desc);
938     }
939 
940     /**
941      * Appends INVOKESPECIAL.  The invoked method must not be a default
942      * method declared in an interface.
943      *
944      * @param clazz     the fully-qualified class name.
945      * @param name      the method name
946      * @param desc      the descriptor of the method signature.
947      *
948      * @see Descriptor#ofMethod(CtClass,CtClass[])
949      * @see Descriptor#ofConstructor(CtClass[])
950      */
addInvokespecial(String clazz, String name, String desc)951     public void addInvokespecial(String clazz, String name, String desc) {
952         addInvokespecial(false, constPool.addClassInfo(clazz), name, desc);
953     }
954 
955     /**
956      * Appends INVOKESPECIAL.  The invoked method must not be a default
957      * method declared in an interface.
958      *
959      * @param clazz     the index of <code>CONSTANT_Class_info</code>
960      *                  structure.
961      * @param name      the method name
962      * @param desc      the descriptor of the method signature.
963      *
964      * @see Descriptor#ofMethod(CtClass,CtClass[])
965      * @see Descriptor#ofConstructor(CtClass[])
966      */
addInvokespecial(int clazz, String name, String desc)967     public void addInvokespecial(int clazz, String name, String desc) {
968         addInvokespecial(false, clazz, name, desc);
969     }
970 
971     /**
972      * Appends INVOKESPECIAL.
973      *
974      * @param isInterface   true if the invoked method is a default method
975      *                      declared in an interface.
976      * @param clazz     the index of <code>CONSTANT_Class_info</code>
977      *                  structure.
978      * @param name      the method name
979      * @param desc      the descriptor of the method signature.
980      *
981      * @see Descriptor#ofMethod(CtClass,CtClass[])
982      * @see Descriptor#ofConstructor(CtClass[])
983      */
addInvokespecial(boolean isInterface, int clazz, String name, String desc)984     public void addInvokespecial(boolean isInterface, int clazz, String name, String desc) {
985         int index;
986         if (isInterface)
987             index = constPool.addInterfaceMethodrefInfo(clazz, name, desc);
988         else
989             index = constPool.addMethodrefInfo(clazz, name, desc);
990 
991         addInvokespecial(index, desc);
992     }
993 
994     /**
995      * Appends INVOKESPECIAL.
996      *
997      * @param index     the index of <code>CONSTANT_Methodref_info</code>
998      *                  or <code>CONSTANT_InterfaceMethodref_info</code>
999      * @param desc      the descriptor of the method signature.
1000      *
1001      * @see Descriptor#ofMethod(CtClass,CtClass[])
1002      * @see Descriptor#ofConstructor(CtClass[])
1003      */
addInvokespecial(int index, String desc)1004     public void addInvokespecial(int index, String desc) {
1005         add(INVOKESPECIAL);
1006         addIndex(index);
1007         growStack(Descriptor.dataSize(desc) - 1);
1008     }
1009 
1010     /**
1011      * Appends INVOKESTATIC.
1012      *
1013      * @param clazz     the target class.
1014      * @param name      the method name
1015      * @param returnType        the return type.
1016      * @param paramTypes        the parameter types.
1017      */
addInvokestatic(CtClass clazz, String name, CtClass returnType, CtClass[] paramTypes)1018     public void addInvokestatic(CtClass clazz, String name,
1019                                 CtClass returnType, CtClass[] paramTypes) {
1020         String desc = Descriptor.ofMethod(returnType, paramTypes);
1021         addInvokestatic(clazz, name, desc);
1022     }
1023 
1024     /**
1025      * Appends INVOKESTATIC.
1026      *
1027      * @param clazz     the target class.
1028      * @param name      the method name
1029      * @param desc      the descriptor of the method signature.
1030      *
1031      * @see Descriptor#ofMethod(CtClass,CtClass[])
1032      */
addInvokestatic(CtClass clazz, String name, String desc)1033     public void addInvokestatic(CtClass clazz, String name, String desc) {
1034         boolean isInterface;
1035         if (clazz == THIS)
1036             isInterface = false;
1037         else
1038             isInterface = clazz.isInterface();
1039 
1040         addInvokestatic(constPool.addClassInfo(clazz), name, desc, isInterface);
1041     }
1042 
1043     /**
1044      * Appends INVOKESTATIC.
1045      *
1046      * @param classname the fully-qualified class name.
1047      *                  It must not be an interface-type name.
1048      * @param name      the method name
1049      * @param desc      the descriptor of the method signature.
1050      *
1051      * @see Descriptor#ofMethod(CtClass,CtClass[])
1052      */
addInvokestatic(String classname, String name, String desc)1053     public void addInvokestatic(String classname, String name, String desc) {
1054         addInvokestatic(constPool.addClassInfo(classname), name, desc);
1055     }
1056 
1057     /**
1058      * Appends INVOKESTATIC.
1059      *
1060      * @param clazz     the index of <code>CONSTANT_Class_info</code>
1061      *                  structure.  It must not be an interface type.
1062      * @param name      the method name
1063      * @param desc      the descriptor of the method signature.
1064      *
1065      * @see Descriptor#ofMethod(CtClass,CtClass[])
1066      */
addInvokestatic(int clazz, String name, String desc)1067     public void addInvokestatic(int clazz, String name, String desc) {
1068         addInvokestatic(clazz, name, desc, false);
1069     }
1070 
addInvokestatic(int clazz, String name, String desc, boolean isInterface)1071     private void addInvokestatic(int clazz, String name, String desc,
1072                                  boolean isInterface) {
1073         add(INVOKESTATIC);
1074         int index;
1075         if (isInterface)
1076             index = constPool.addInterfaceMethodrefInfo(clazz, name, desc);
1077         else
1078             index = constPool.addMethodrefInfo(clazz, name, desc);
1079 
1080         addIndex(index);
1081         growStack(Descriptor.dataSize(desc));
1082     }
1083 
1084     /**
1085      * Appends INVOKEVIRTUAL.
1086      *
1087      * <p>The specified method must not be an inherited method.
1088      * It must be directly declared in the class specified
1089      * in <code>clazz</code>.
1090      *
1091      * @param clazz     the target class.
1092      * @param name      the method name
1093      * @param returnType        the return type.
1094      * @param paramTypes        the parameter types.
1095      */
addInvokevirtual(CtClass clazz, String name, CtClass returnType, CtClass[] paramTypes)1096     public void addInvokevirtual(CtClass clazz, String name,
1097                                  CtClass returnType, CtClass[] paramTypes) {
1098         String desc = Descriptor.ofMethod(returnType, paramTypes);
1099         addInvokevirtual(clazz, name, desc);
1100     }
1101 
1102     /**
1103      * Appends INVOKEVIRTUAL.
1104      *
1105      * <p>The specified method must not be an inherited method.
1106      * It must be directly declared in the class specified
1107      * in <code>clazz</code>.
1108      *
1109      * @param clazz     the target class.
1110      * @param name      the method name
1111      * @param desc      the descriptor of the method signature.
1112      *
1113      * @see Descriptor#ofMethod(CtClass,CtClass[])
1114      */
addInvokevirtual(CtClass clazz, String name, String desc)1115     public void addInvokevirtual(CtClass clazz, String name, String desc) {
1116         addInvokevirtual(constPool.addClassInfo(clazz), name, desc);
1117     }
1118 
1119     /**
1120      * Appends INVOKEVIRTUAL.
1121      *
1122      * <p>The specified method must not be an inherited method.
1123      * It must be directly declared in the class specified
1124      * in <code>classname</code>.
1125      *
1126      * @param classname the fully-qualified class name.
1127      * @param name      the method name
1128      * @param desc      the descriptor of the method signature.
1129      *
1130      * @see Descriptor#ofMethod(CtClass,CtClass[])
1131      */
addInvokevirtual(String classname, String name, String desc)1132     public void addInvokevirtual(String classname, String name, String desc) {
1133         addInvokevirtual(constPool.addClassInfo(classname), name, desc);
1134     }
1135 
1136     /**
1137      * Appends INVOKEVIRTUAL.
1138      *
1139      * <p>The specified method must not be an inherited method.
1140      * It must be directly declared in the class specified
1141      * by <code>clazz</code>.
1142      *
1143      * @param clazz     the index of <code>CONSTANT_Class_info</code>
1144      *                  structure.
1145      * @param name      the method name
1146      * @param desc      the descriptor of the method signature.
1147      *
1148      * @see Descriptor#ofMethod(CtClass,CtClass[])
1149      */
addInvokevirtual(int clazz, String name, String desc)1150     public void addInvokevirtual(int clazz, String name, String desc) {
1151         add(INVOKEVIRTUAL);
1152         addIndex(constPool.addMethodrefInfo(clazz, name, desc));
1153         growStack(Descriptor.dataSize(desc) - 1);
1154     }
1155 
1156     /**
1157      * Appends INVOKEINTERFACE.
1158      *
1159      * @param clazz     the target class.
1160      * @param name      the method name
1161      * @param returnType        the return type.
1162      * @param paramTypes        the parameter types.
1163      * @param count     the count operand of the instruction.
1164      */
addInvokeinterface(CtClass clazz, String name, CtClass returnType, CtClass[] paramTypes, int count)1165     public void addInvokeinterface(CtClass clazz, String name,
1166                                    CtClass returnType, CtClass[] paramTypes,
1167                                    int count) {
1168         String desc = Descriptor.ofMethod(returnType, paramTypes);
1169         addInvokeinterface(clazz, name, desc, count);
1170     }
1171 
1172     /**
1173      * Appends INVOKEINTERFACE.
1174      *
1175      * @param clazz     the target class.
1176      * @param name      the method name
1177      * @param desc      the descriptor of the method signature.
1178      * @param count     the count operand of the instruction.
1179      *
1180      * @see Descriptor#ofMethod(CtClass,CtClass[])
1181      */
addInvokeinterface(CtClass clazz, String name, String desc, int count)1182     public void addInvokeinterface(CtClass clazz, String name,
1183                                    String desc, int count) {
1184         addInvokeinterface(constPool.addClassInfo(clazz), name, desc,
1185                            count);
1186     }
1187 
1188     /**
1189      * Appends INVOKEINTERFACE.
1190      *
1191      * @param classname the fully-qualified class name.
1192      * @param name      the method name
1193      * @param desc      the descriptor of the method signature.
1194      * @param count     the count operand of the instruction.
1195      *
1196      * @see Descriptor#ofMethod(CtClass,CtClass[])
1197      */
addInvokeinterface(String classname, String name, String desc, int count)1198     public void addInvokeinterface(String classname, String name,
1199                                    String desc, int count) {
1200         addInvokeinterface(constPool.addClassInfo(classname), name, desc,
1201                            count);
1202     }
1203 
1204     /**
1205      * Appends INVOKEINTERFACE.
1206      *
1207      * @param clazz     the index of <code>CONSTANT_Class_info</code>
1208      *                  structure.
1209      * @param name      the method name
1210      * @param desc      the descriptor of the method signature.
1211      * @param count     the count operand of the instruction.
1212      *
1213      * @see Descriptor#ofMethod(CtClass,CtClass[])
1214      */
addInvokeinterface(int clazz, String name, String desc, int count)1215     public void addInvokeinterface(int clazz, String name,
1216                                    String desc, int count) {
1217         add(INVOKEINTERFACE);
1218         addIndex(constPool.addInterfaceMethodrefInfo(clazz, name, desc));
1219         add(count);
1220         add(0);
1221         growStack(Descriptor.dataSize(desc) - 1);
1222     }
1223 
1224     /**
1225      * Appends INVOKEDYNAMIC.
1226      *
1227      * @param bootstrap     an index into the <code>bootstrap_methods</code> array
1228      *                      of the bootstrap method table.
1229      * @param name          the method name.
1230      * @param desc          the method descriptor.
1231      * @see Descriptor#ofMethod(CtClass,CtClass[])
1232      * @since 3.17
1233      */
addInvokedynamic(int bootstrap, String name, String desc)1234     public void addInvokedynamic(int bootstrap, String name, String desc) {
1235         int nt = constPool.addNameAndTypeInfo(name, desc);
1236         int dyn = constPool.addInvokeDynamicInfo(bootstrap, nt);
1237         add(INVOKEDYNAMIC);
1238         addIndex(dyn);
1239         add(0, 0);
1240         growStack(Descriptor.dataSize(desc));   // assume ConstPool#REF_invokeStatic
1241     }
1242 
1243     /**
1244      * Appends LDC or LDC_W.  The pushed item is a <code>String</code>
1245      * object.
1246      *
1247      * @param s         the character string pushed by LDC or LDC_W.
1248      */
addLdc(String s)1249     public void addLdc(String s) {
1250         addLdc(constPool.addStringInfo(s));
1251     }
1252 
1253     /**
1254      * Appends LDC or LDC_W.
1255      *
1256      * @param i         index into the constant pool.
1257      */
addLdc(int i)1258     public void addLdc(int i) {
1259         if (i > 0xFF) {
1260             addOpcode(LDC_W);
1261             addIndex(i);
1262         }
1263         else {
1264             addOpcode(LDC);
1265             add(i);
1266         }
1267     }
1268 
1269     /**
1270      * Appends LDC2_W.  The pushed item is a long value.
1271      */
addLdc2w(long l)1272     public void addLdc2w(long l) {
1273         addOpcode(LDC2_W);
1274         addIndex(constPool.addLongInfo(l));
1275     }
1276 
1277     /**
1278      * Appends LDC2_W.  The pushed item is a double value.
1279      */
addLdc2w(double d)1280     public void addLdc2w(double d) {
1281         addOpcode(LDC2_W);
1282         addIndex(constPool.addDoubleInfo(d));
1283     }
1284 
1285     /**
1286      * Appends NEW.
1287      *
1288      * @param clazz     the class of the created instance.
1289      */
addNew(CtClass clazz)1290     public void addNew(CtClass clazz) {
1291         addOpcode(NEW);
1292         addIndex(constPool.addClassInfo(clazz));
1293     }
1294 
1295     /**
1296      * Appends NEW.
1297      *
1298      * @param classname         the fully-qualified class name.
1299      */
addNew(String classname)1300     public void addNew(String classname) {
1301         addOpcode(NEW);
1302         addIndex(constPool.addClassInfo(classname));
1303     }
1304 
1305     /**
1306      * Appends ANEWARRAY.
1307      *
1308      * @param classname         the qualified class name of the element type.
1309      */
addAnewarray(String classname)1310     public void addAnewarray(String classname) {
1311         addOpcode(ANEWARRAY);
1312         addIndex(constPool.addClassInfo(classname));
1313     }
1314 
1315     /**
1316      * Appends ICONST and ANEWARRAY.
1317      *
1318      * @param clazz     the elememnt type.
1319      * @param length    the array length.
1320      */
addAnewarray(CtClass clazz, int length)1321     public void addAnewarray(CtClass clazz, int length) {
1322         addIconst(length);
1323         addOpcode(ANEWARRAY);
1324         addIndex(constPool.addClassInfo(clazz));
1325     }
1326 
1327     /**
1328      * Appends NEWARRAY for primitive types.
1329      *
1330      * @param atype     <code>T_BOOLEAN</code>, <code>T_CHAR</code>, ...
1331      * @see Opcode
1332      */
addNewarray(int atype, int length)1333     public void addNewarray(int atype, int length) {
1334         addIconst(length);
1335         addOpcode(NEWARRAY);
1336         add(atype);
1337     }
1338 
1339     /**
1340      * Appends MULTINEWARRAY.
1341      *
1342      * @param clazz             the array type.
1343      * @param dimensions        the sizes of all dimensions.
1344      * @return          the length of <code>dimensions</code>.
1345      */
addMultiNewarray(CtClass clazz, int[] dimensions)1346     public int addMultiNewarray(CtClass clazz, int[] dimensions) {
1347         int len = dimensions.length;
1348         for (int i = 0; i < len; ++i)
1349             addIconst(dimensions[i]);
1350 
1351         growStack(len);
1352         return addMultiNewarray(clazz, len);
1353     }
1354 
1355     /**
1356      * Appends MULTINEWARRAY.  The size of every dimension must have been
1357      * already pushed on the stack.
1358      *
1359      * @param clazz             the array type.
1360      * @param dim               the number of the dimensions.
1361      * @return                  the value of <code>dim</code>.
1362      */
addMultiNewarray(CtClass clazz, int dim)1363     public int addMultiNewarray(CtClass clazz, int dim) {
1364         add(MULTIANEWARRAY);
1365         addIndex(constPool.addClassInfo(clazz));
1366         add(dim);
1367         growStack(1 - dim);
1368         return dim;
1369     }
1370 
1371     /**
1372      * Appends MULTINEWARRAY.
1373      *
1374      * @param desc      the type descriptor of the created array.
1375      * @param dim       dimensions.
1376      * @return          the value of <code>dim</code>.
1377      */
addMultiNewarray(String desc, int dim)1378     public int addMultiNewarray(String desc, int dim) {
1379         add(MULTIANEWARRAY);
1380         addIndex(constPool.addClassInfo(desc));
1381         add(dim);
1382         growStack(1 - dim);
1383         return dim;
1384     }
1385 
1386     /**
1387      * Appends PUTFIELD.
1388      *
1389      * @param c         the target class.
1390      * @param name      the field name.
1391      * @param desc      the descriptor of the field type.
1392      */
addPutfield(CtClass c, String name, String desc)1393     public void addPutfield(CtClass c, String name, String desc) {
1394         addPutfield0(c, null, name, desc);
1395     }
1396 
1397     /**
1398      * Appends PUTFIELD.
1399      *
1400      * @param classname         the fully-qualified name of the target class.
1401      * @param name      the field name.
1402      * @param desc      the descriptor of the field type.
1403      */
addPutfield(String classname, String name, String desc)1404     public void addPutfield(String classname, String name, String desc) {
1405         // if classnaem is null, the target class is THIS.
1406         addPutfield0(null, classname, name, desc);
1407     }
1408 
addPutfield0(CtClass target, String classname, String name, String desc)1409     private void addPutfield0(CtClass target, String classname,
1410                               String name, String desc) {
1411         add(PUTFIELD);
1412         // target is null if it represents THIS.
1413         int ci = classname == null ? constPool.addClassInfo(target)
1414                                    : constPool.addClassInfo(classname);
1415         addIndex(constPool.addFieldrefInfo(ci, name, desc));
1416         growStack(-1 - Descriptor.dataSize(desc));
1417     }
1418 
1419     /**
1420      * Appends PUTSTATIC.
1421      *
1422      * @param c         the target class.
1423      * @param name      the field name.
1424      * @param desc      the descriptor of the field type.
1425      */
addPutstatic(CtClass c, String name, String desc)1426     public void addPutstatic(CtClass c, String name, String desc) {
1427         addPutstatic0(c, null, name, desc);
1428     }
1429 
1430     /**
1431      * Appends PUTSTATIC.
1432      *
1433      * @param classname         the fully-qualified name of the target class.
1434      * @param fieldName         the field name.
1435      * @param desc              the descriptor of the field type.
1436      */
addPutstatic(String classname, String fieldName, String desc)1437     public void addPutstatic(String classname, String fieldName, String desc) {
1438         // if classname is null, the target class is THIS.
1439         addPutstatic0(null, classname, fieldName, desc);
1440     }
1441 
addPutstatic0(CtClass target, String classname, String fieldName, String desc)1442     private void addPutstatic0(CtClass target, String classname,
1443                                String fieldName, String desc) {
1444         add(PUTSTATIC);
1445         // target is null if it represents THIS.
1446         int ci = classname == null ? constPool.addClassInfo(target)
1447                                 : constPool.addClassInfo(classname);
1448         addIndex(constPool.addFieldrefInfo(ci, fieldName, desc));
1449         growStack(-Descriptor.dataSize(desc));
1450     }
1451 
1452     /**
1453      * Appends ARETURN, IRETURN, .., or RETURN.
1454      *
1455      * @param type      the return type.
1456      */
addReturn(CtClass type)1457     public void addReturn(CtClass type) {
1458         if (type == null)
1459             addOpcode(RETURN);
1460         else if (type.isPrimitive()) {
1461             CtPrimitiveType ptype = (CtPrimitiveType)type;
1462             addOpcode(ptype.getReturnOp());
1463         }
1464         else
1465             addOpcode(ARETURN);
1466     }
1467 
1468     /**
1469      * Appends RET.
1470      *
1471      * @param var       local variable
1472      */
addRet(int var)1473     public void addRet(int var) {
1474         if (var < 0x100) {
1475             addOpcode(RET);
1476             add(var);
1477         }
1478         else {
1479             addOpcode(WIDE);
1480             addOpcode(RET);
1481             addIndex(var);
1482         }
1483     }
1484 
1485     /**
1486      * Appends instructions for executing
1487      * <code>java.lang.System.println(<i>message</i>)</code>.
1488      *
1489      * @param message           printed message.
1490      */
addPrintln(String message)1491     public void addPrintln(String message) {
1492         addGetstatic("java.lang.System", "err", "Ljava/io/PrintStream;");
1493         addLdc(message);
1494         addInvokevirtual("java.io.PrintStream",
1495                          "println", "(Ljava/lang/String;)V");
1496     }
1497 }
1498