• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2  * ASM: a very small and fast Java bytecode manipulation framework
3  * Copyright (c) 2000-2005 INRIA, France Telecom
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the copyright holders nor the names of its
15  *    contributors may be used to endorse or promote products derived from
16  *    this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28  * THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 package org.objectweb.asm.util;
31 
32 import org.objectweb.asm.AnnotationVisitor;
33 import org.objectweb.asm.Label;
34 import org.objectweb.asm.MethodAdapter;
35 import org.objectweb.asm.MethodVisitor;
36 import org.objectweb.asm.Opcodes;
37 import org.objectweb.asm.Attribute;
38 import org.objectweb.asm.Type;
39 
40 import java.util.HashMap;
41 
42 /**
43  * A {@link MethodAdapter} that checks that its methods are properly used. More
44  * precisely this code adapter checks each instruction individually (i.e., each
45  * visit method checks some preconditions based <i>only</i> on its arguments -
46  * such as the fact that the given opcode is correct for a given visit method),
47  * but does <i>not</i> check the <i>sequence</i> of instructions. For example,
48  * in a method whose signature is <tt>void m ()</tt>, the invalid instruction
49  * IRETURN, or the invalid sequence IADD L2I will <i>not</i> be detected by
50  * this code adapter.
51  *
52  * @author Eric Bruneton
53  */
54 public class CheckMethodAdapter extends MethodAdapter {
55 
56     /**
57      * <tt>true</tt> if the visitCode method has been called.
58      */
59     private boolean startCode;
60 
61     /**
62      * <tt>true</tt> if the visitMaxs method has been called.
63      */
64     private boolean endCode;
65 
66     /**
67      * <tt>true</tt> if the visitEnd method has been called.
68      */
69     private boolean endMethod;
70 
71     /**
72      * The already visited labels. This map associate Integer values to Label
73      * keys.
74      */
75     private HashMap labels;
76 
77     /**
78      * Code of the visit method to be used for each opcode.
79      */
80     private final static int[] TYPE;
81 
82     static {
83         String s = "BBBBBBBBBBBBBBBBCCIAADDDDDAAAAAAAAAAAAAAAAAAAABBBBBBBBDD"
84                 + "DDDAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
85                 + "BBBBBBBBBBBBBBBBBBBJBBBBBBBBBBBBBBBBBBBBHHHHHHHHHHHHHHHHD"
86                 + "KLBBBBBBFFFFGGGGAECEBBEEBBAMHHAA";
87         TYPE = new int[s.length()];
88         for (int i = 0; i < TYPE.length; ++i) {
89             TYPE[i] = (s.charAt(i) - 'A' - 1);
90         }
91     }
92 
93     // code to generate the above string
94     // public static void main (String[] args) {
95     // int[] TYPE = new int[] {
96     // 0, //NOP
97     // 0, //ACONST_NULL
98     // 0, //ICONST_M1
99     // 0, //ICONST_0
100     // 0, //ICONST_1
101     // 0, //ICONST_2
102     // 0, //ICONST_3
103     // 0, //ICONST_4
104     // 0, //ICONST_5
105     // 0, //LCONST_0
106     // 0, //LCONST_1
107     // 0, //FCONST_0
108     // 0, //FCONST_1
109     // 0, //FCONST_2
110     // 0, //DCONST_0
111     // 0, //DCONST_1
112     // 1, //BIPUSH
113     // 1, //SIPUSH
114     // 7, //LDC
115     // -1, //LDC_W
116     // -1, //LDC2_W
117     // 2, //ILOAD
118     // 2, //LLOAD
119     // 2, //FLOAD
120     // 2, //DLOAD
121     // 2, //ALOAD
122     // -1, //ILOAD_0
123     // -1, //ILOAD_1
124     // -1, //ILOAD_2
125     // -1, //ILOAD_3
126     // -1, //LLOAD_0
127     // -1, //LLOAD_1
128     // -1, //LLOAD_2
129     // -1, //LLOAD_3
130     // -1, //FLOAD_0
131     // -1, //FLOAD_1
132     // -1, //FLOAD_2
133     // -1, //FLOAD_3
134     // -1, //DLOAD_0
135     // -1, //DLOAD_1
136     // -1, //DLOAD_2
137     // -1, //DLOAD_3
138     // -1, //ALOAD_0
139     // -1, //ALOAD_1
140     // -1, //ALOAD_2
141     // -1, //ALOAD_3
142     // 0, //IALOAD
143     // 0, //LALOAD
144     // 0, //FALOAD
145     // 0, //DALOAD
146     // 0, //AALOAD
147     // 0, //BALOAD
148     // 0, //CALOAD
149     // 0, //SALOAD
150     // 2, //ISTORE
151     // 2, //LSTORE
152     // 2, //FSTORE
153     // 2, //DSTORE
154     // 2, //ASTORE
155     // -1, //ISTORE_0
156     // -1, //ISTORE_1
157     // -1, //ISTORE_2
158     // -1, //ISTORE_3
159     // -1, //LSTORE_0
160     // -1, //LSTORE_1
161     // -1, //LSTORE_2
162     // -1, //LSTORE_3
163     // -1, //FSTORE_0
164     // -1, //FSTORE_1
165     // -1, //FSTORE_2
166     // -1, //FSTORE_3
167     // -1, //DSTORE_0
168     // -1, //DSTORE_1
169     // -1, //DSTORE_2
170     // -1, //DSTORE_3
171     // -1, //ASTORE_0
172     // -1, //ASTORE_1
173     // -1, //ASTORE_2
174     // -1, //ASTORE_3
175     // 0, //IASTORE
176     // 0, //LASTORE
177     // 0, //FASTORE
178     // 0, //DASTORE
179     // 0, //AASTORE
180     // 0, //BASTORE
181     // 0, //CASTORE
182     // 0, //SASTORE
183     // 0, //POP
184     // 0, //POP2
185     // 0, //DUP
186     // 0, //DUP_X1
187     // 0, //DUP_X2
188     // 0, //DUP2
189     // 0, //DUP2_X1
190     // 0, //DUP2_X2
191     // 0, //SWAP
192     // 0, //IADD
193     // 0, //LADD
194     // 0, //FADD
195     // 0, //DADD
196     // 0, //ISUB
197     // 0, //LSUB
198     // 0, //FSUB
199     // 0, //DSUB
200     // 0, //IMUL
201     // 0, //LMUL
202     // 0, //FMUL
203     // 0, //DMUL
204     // 0, //IDIV
205     // 0, //LDIV
206     // 0, //FDIV
207     // 0, //DDIV
208     // 0, //IREM
209     // 0, //LREM
210     // 0, //FREM
211     // 0, //DREM
212     // 0, //INEG
213     // 0, //LNEG
214     // 0, //FNEG
215     // 0, //DNEG
216     // 0, //ISHL
217     // 0, //LSHL
218     // 0, //ISHR
219     // 0, //LSHR
220     // 0, //IUSHR
221     // 0, //LUSHR
222     // 0, //IAND
223     // 0, //LAND
224     // 0, //IOR
225     // 0, //LOR
226     // 0, //IXOR
227     // 0, //LXOR
228     // 8, //IINC
229     // 0, //I2L
230     // 0, //I2F
231     // 0, //I2D
232     // 0, //L2I
233     // 0, //L2F
234     // 0, //L2D
235     // 0, //F2I
236     // 0, //F2L
237     // 0, //F2D
238     // 0, //D2I
239     // 0, //D2L
240     // 0, //D2F
241     // 0, //I2B
242     // 0, //I2C
243     // 0, //I2S
244     // 0, //LCMP
245     // 0, //FCMPL
246     // 0, //FCMPG
247     // 0, //DCMPL
248     // 0, //DCMPG
249     // 6, //IFEQ
250     // 6, //IFNE
251     // 6, //IFLT
252     // 6, //IFGE
253     // 6, //IFGT
254     // 6, //IFLE
255     // 6, //IF_ICMPEQ
256     // 6, //IF_ICMPNE
257     // 6, //IF_ICMPLT
258     // 6, //IF_ICMPGE
259     // 6, //IF_ICMPGT
260     // 6, //IF_ICMPLE
261     // 6, //IF_ACMPEQ
262     // 6, //IF_ACMPNE
263     // 6, //GOTO
264     // 6, //JSR
265     // 2, //RET
266     // 9, //TABLESWITCH
267     // 10, //LOOKUPSWITCH
268     // 0, //IRETURN
269     // 0, //LRETURN
270     // 0, //FRETURN
271     // 0, //DRETURN
272     // 0, //ARETURN
273     // 0, //RETURN
274     // 4, //GETSTATIC
275     // 4, //PUTSTATIC
276     // 4, //GETFIELD
277     // 4, //PUTFIELD
278     // 5, //INVOKEVIRTUAL
279     // 5, //INVOKESPECIAL
280     // 5, //INVOKESTATIC
281     // 5, //INVOKEINTERFACE
282     // -1, //UNUSED
283     // 3, //NEW
284     // 1, //NEWARRAY
285     // 3, //ANEWARRAY
286     // 0, //ARRAYLENGTH
287     // 0, //ATHROW
288     // 3, //CHECKCAST
289     // 3, //INSTANCEOF
290     // 0, //MONITORENTER
291     // 0, //MONITOREXIT
292     // -1, //WIDE
293     // 11, //MULTIANEWARRAY
294     // 6, //IFNULL
295     // 6, //IFNONNULL
296     // -1, //GOTO_W
297     // -1 //JSR_W
298     // };
299     // for (int i = 0; i < TYPE.length; ++i) {
300     // System.out.print((char)(TYPE[i] + 1 + 'A'));
301     // }
302     // System.out.println();
303     // }
304 
305     /**
306      * Constructs a new {@link CheckMethodAdapter} object.
307      *
308      * @param cv the code visitor to which this adapter must delegate calls.
309      */
CheckMethodAdapter(final MethodVisitor cv)310     public CheckMethodAdapter(final MethodVisitor cv) {
311         super(cv);
312         this.labels = new HashMap();
313     }
314 
visitAnnotation( final String desc, final boolean visible)315     public AnnotationVisitor visitAnnotation(
316         final String desc,
317         final boolean visible)
318     {
319         checkEndMethod();
320         checkDesc(desc, false);
321         return new CheckAnnotationAdapter(mv.visitAnnotation(desc, visible));
322     }
323 
visitAnnotationDefault()324     public AnnotationVisitor visitAnnotationDefault() {
325         checkEndMethod();
326         return new CheckAnnotationAdapter(mv.visitAnnotationDefault(), false);
327     }
328 
visitParameterAnnotation( final int parameter, final String desc, final boolean visible)329     public AnnotationVisitor visitParameterAnnotation(
330         final int parameter,
331         final String desc,
332         final boolean visible)
333     {
334         checkEndMethod();
335         checkDesc(desc, false);
336         return new CheckAnnotationAdapter(mv.visitParameterAnnotation(parameter,
337                 desc,
338                 visible));
339     }
340 
visitAttribute(final Attribute attr)341     public void visitAttribute(final Attribute attr) {
342         checkEndMethod();
343         if (attr == null) {
344             throw new IllegalArgumentException("Invalid attribute (must not be null)");
345         }
346         mv.visitAttribute(attr);
347     }
348 
visitCode()349     public void visitCode() {
350         startCode = true;
351         mv.visitCode();
352     }
353 
visitInsn(final int opcode)354     public void visitInsn(final int opcode) {
355         checkStartCode();
356         checkEndCode();
357         checkOpcode(opcode, 0);
358         mv.visitInsn(opcode);
359     }
360 
visitIntInsn(final int opcode, final int operand)361     public void visitIntInsn(final int opcode, final int operand) {
362         checkStartCode();
363         checkEndCode();
364         checkOpcode(opcode, 1);
365         switch (opcode) {
366             case Opcodes.BIPUSH:
367                 checkSignedByte(operand, "Invalid operand");
368                 break;
369             case Opcodes.SIPUSH:
370                 checkSignedShort(operand, "Invalid operand");
371                 break;
372             // case Constants.NEWARRAY:
373             default:
374                 if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) {
375                     throw new IllegalArgumentException("Invalid operand (must be an array type code T_...): "
376                             + operand);
377                 }
378         }
379         mv.visitIntInsn(opcode, operand);
380     }
381 
visitVarInsn(final int opcode, final int var)382     public void visitVarInsn(final int opcode, final int var) {
383         checkStartCode();
384         checkEndCode();
385         checkOpcode(opcode, 2);
386         checkUnsignedShort(var, "Invalid variable index");
387         mv.visitVarInsn(opcode, var);
388     }
389 
visitTypeInsn(final int opcode, final String desc)390     public void visitTypeInsn(final int opcode, final String desc) {
391         checkStartCode();
392         checkEndCode();
393         checkOpcode(opcode, 3);
394         if (desc != null && desc.length() > 0 && desc.charAt(0) == '[') {
395             checkDesc(desc, false);
396         } else {
397             checkInternalName(desc, "type");
398         }
399         if (opcode == Opcodes.NEW && desc.charAt(0) == '[') {
400             throw new IllegalArgumentException("NEW cannot be used to create arrays: "
401                     + desc);
402         }
403         mv.visitTypeInsn(opcode, desc);
404     }
405 
visitFieldInsn( final int opcode, final String owner, final String name, final String desc)406     public void visitFieldInsn(
407         final int opcode,
408         final String owner,
409         final String name,
410         final String desc)
411     {
412         checkStartCode();
413         checkEndCode();
414         checkOpcode(opcode, 4);
415         checkInternalName(owner, "owner");
416         checkIdentifier(name, "name");
417         checkDesc(desc, false);
418         mv.visitFieldInsn(opcode, owner, name, desc);
419     }
420 
visitMethodInsn( final int opcode, final String owner, final String name, final String desc)421     public void visitMethodInsn(
422         final int opcode,
423         final String owner,
424         final String name,
425         final String desc)
426     {
427         checkStartCode();
428         checkEndCode();
429         checkOpcode(opcode, 5);
430         checkMethodIdentifier(name, "name");
431         if (!name.equals("clone")) {
432             // In JDK1.5, clone method can be called on array class descriptors
433             checkInternalName(owner, "owner");
434         }
435         checkMethodDesc(desc);
436         mv.visitMethodInsn(opcode, owner, name, desc);
437     }
438 
visitJumpInsn(final int opcode, final Label label)439     public void visitJumpInsn(final int opcode, final Label label) {
440         checkStartCode();
441         checkEndCode();
442         checkOpcode(opcode, 6);
443         checkLabel(label, false, "label");
444         mv.visitJumpInsn(opcode, label);
445     }
446 
visitLabel(final Label label)447     public void visitLabel(final Label label) {
448         checkStartCode();
449         checkEndCode();
450         checkLabel(label, false, "label");
451         if (labels.get(label) != null) {
452             throw new IllegalArgumentException("Already visited label");
453         } else {
454             labels.put(label, new Integer(labels.size()));
455         }
456         mv.visitLabel(label);
457     }
458 
visitLdcInsn(final Object cst)459     public void visitLdcInsn(final Object cst) {
460         checkStartCode();
461         checkEndCode();
462         if (!(cst instanceof Type)) {
463             checkConstant(cst);
464         }
465         mv.visitLdcInsn(cst);
466     }
467 
visitIincInsn(final int var, final int increment)468     public void visitIincInsn(final int var, final int increment) {
469         checkStartCode();
470         checkEndCode();
471         checkUnsignedShort(var, "Invalid variable index");
472         checkSignedShort(increment, "Invalid increment");
473         mv.visitIincInsn(var, increment);
474     }
475 
visitTableSwitchInsn( final int min, final int max, final Label dflt, final Label labels[])476     public void visitTableSwitchInsn(
477         final int min,
478         final int max,
479         final Label dflt,
480         final Label labels[])
481     {
482         checkStartCode();
483         checkEndCode();
484         if (max < min) {
485             throw new IllegalArgumentException("Max = " + max
486                     + " must be greater than or equal to min = " + min);
487         }
488         checkLabel(dflt, false, "default label");
489         if (labels == null || labels.length != max - min + 1) {
490             throw new IllegalArgumentException("There must be max - min + 1 labels");
491         }
492         for (int i = 0; i < labels.length; ++i) {
493             checkLabel(labels[i], false, "label at index " + i);
494         }
495         mv.visitTableSwitchInsn(min, max, dflt, labels);
496     }
497 
visitLookupSwitchInsn( final Label dflt, final int keys[], final Label labels[])498     public void visitLookupSwitchInsn(
499         final Label dflt,
500         final int keys[],
501         final Label labels[])
502     {
503         checkEndCode();
504         checkStartCode();
505         checkLabel(dflt, false, "default label");
506         if (keys == null || labels == null || keys.length != labels.length) {
507             throw new IllegalArgumentException("There must be the same number of keys and labels");
508        }
509         for (int i = 0; i < labels.length; ++i) {
510             checkLabel(labels[i], false, "label at index " + i);
511         }
512         mv.visitLookupSwitchInsn(dflt, keys, labels);
513     }
514 
visitMultiANewArrayInsn(final String desc, final int dims)515     public void visitMultiANewArrayInsn(final String desc, final int dims) {
516         checkStartCode();
517         checkEndCode();
518         checkDesc(desc, false);
519         if (desc.charAt(0) != '[') {
520             throw new IllegalArgumentException("Invalid descriptor (must be an array type descriptor): "
521                     + desc);
522         }
523         if (dims < 1) {
524             throw new IllegalArgumentException("Invalid dimensions (must be greater than 0): "
525                     + dims);
526         }
527         if (dims > desc.lastIndexOf('[') + 1) {
528             throw new IllegalArgumentException("Invalid dimensions (must not be greater than dims(desc)): "
529                     + dims);
530         }
531         mv.visitMultiANewArrayInsn(desc, dims);
532     }
533 
visitTryCatchBlock( final Label start, final Label end, final Label handler, final String type)534     public void visitTryCatchBlock(
535         final Label start,
536         final Label end,
537         final Label handler,
538         final String type)
539     {
540         checkStartCode();
541         checkEndCode();
542         if (type != null) {
543             checkInternalName(type, "type");
544         }
545         mv.visitTryCatchBlock(start, end, handler, type);
546     }
547 
visitLocalVariable( final String name, final String desc, final String signature, final Label start, final Label end, final int index)548     public void visitLocalVariable(
549         final String name,
550         final String desc,
551         final String signature,
552         final Label start,
553         final Label end,
554         final int index)
555     {
556         checkStartCode();
557         checkEndCode();
558         checkIdentifier(name, "name");
559         checkDesc(desc, false);
560         checkLabel(start, true, "start label");
561         checkLabel(end, true, "end label");
562         checkUnsignedShort(index, "Invalid variable index");
563         int s = ((Integer) labels.get(start)).intValue();
564         int e = ((Integer) labels.get(end)).intValue();
565         if (e < s) {
566             throw new IllegalArgumentException("Invalid start and end labels (end must be greater than start)");
567         }
568         mv.visitLocalVariable(name, desc, signature, start, end, index);
569     }
570 
visitLineNumber(final int line, final Label start)571     public void visitLineNumber(final int line, final Label start) {
572         checkStartCode();
573         checkEndCode();
574         checkUnsignedShort(line, "Invalid line number");
575         checkLabel(start, true, "start label");
576         mv.visitLineNumber(line, start);
577     }
578 
visitMaxs(final int maxStack, final int maxLocals)579     public void visitMaxs(final int maxStack, final int maxLocals) {
580         checkStartCode();
581         checkEndCode();
582         endCode = true;
583         checkUnsignedShort(maxStack, "Invalid max stack");
584         checkUnsignedShort(maxLocals, "Invalid max locals");
585         mv.visitMaxs(maxStack, maxLocals);
586     }
587 
visitEnd()588     public void visitEnd() {
589         checkEndMethod();
590         endMethod = true;
591         mv.visitEnd();
592     }
593 
594     // -------------------------------------------------------------------------
595 
596     /**
597      * Checks that the visitCode method has been called.
598      */
checkStartCode()599     void checkStartCode() {
600         if (!startCode) {
601             throw new IllegalStateException("Cannot visit instructions before visitCode has been called.");
602         }
603     }
604 
605     /**
606      * Checks that the visitMaxs method has not been called.
607      */
checkEndCode()608     void checkEndCode() {
609         if (endCode) {
610             throw new IllegalStateException("Cannot visit instructions after visitMaxs has been called.");
611         }
612     }
613 
614     /**
615      * Checks that the visitEnd method has not been called.
616      */
checkEndMethod()617     void checkEndMethod() {
618         if (endMethod) {
619             throw new IllegalStateException("Cannot visit elements after visitEnd has been called.");
620         }
621     }
622 
623     /**
624      * Checks that the type of the given opcode is equal to the given type.
625      *
626      * @param opcode the opcode to be checked.
627      * @param type the expected opcode type.
628      */
checkOpcode(final int opcode, final int type)629     static void checkOpcode(final int opcode, final int type) {
630         if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) {
631             throw new IllegalArgumentException("Invalid opcode: " + opcode);
632         }
633     }
634 
635     /**
636      * Checks that the given value is a signed byte.
637      *
638      * @param value the value to be checked.
639      * @param msg an message to be used in case of error.
640      */
checkSignedByte(final int value, final String msg)641     static void checkSignedByte(final int value, final String msg) {
642         if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
643             throw new IllegalArgumentException(msg
644                     + " (must be a signed byte): " + value);
645         }
646     }
647 
648     /**
649      * Checks that the given value is a signed short.
650      *
651      * @param value the value to be checked.
652      * @param msg an message to be used in case of error.
653      */
checkSignedShort(final int value, final String msg)654     static void checkSignedShort(final int value, final String msg) {
655         if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
656             throw new IllegalArgumentException(msg
657                     + " (must be a signed short): " + value);
658         }
659     }
660 
661     /**
662      * Checks that the given value is an unsigned short.
663      *
664      * @param value the value to be checked.
665      * @param msg an message to be used in case of error.
666      */
checkUnsignedShort(final int value, final String msg)667     static void checkUnsignedShort(final int value, final String msg) {
668         if (value < 0 || value > 65535) {
669             throw new IllegalArgumentException(msg
670                     + " (must be an unsigned short): " + value);
671         }
672     }
673 
674     /**
675      * Checks that the given value is an {@link Integer}, a{@link Float}, a
676      * {@link Long}, a {@link Double} or a {@link String}.
677      *
678      * @param cst the value to be checked.
679      */
checkConstant(final Object cst)680     static void checkConstant(final Object cst) {
681         if (!(cst instanceof Integer) && !(cst instanceof Float)
682                 && !(cst instanceof Long) && !(cst instanceof Double)
683                 && !(cst instanceof String))
684         {
685             throw new IllegalArgumentException("Invalid constant: " + cst);
686         }
687     }
688 
689     /**
690      * Checks that the given string is a valid Java identifier.
691      *
692      * @param name the string to be checked.
693      * @param msg a message to be used in case of error.
694      */
checkIdentifier(final String name, final String msg)695     static void checkIdentifier(final String name, final String msg) {
696         checkIdentifier(name, 0, -1, msg);
697     }
698 
699     /**
700      * Checks that the given substring is a valid Java identifier.
701      *
702      * @param name the string to be checked.
703      * @param start index of the first character of the identifier (inclusive).
704      * @param end index of the last character of the identifier (exclusive). -1
705      *        is equivalent to <tt>name.length()</tt> if name is not
706      *        <tt>null</tt>.
707      * @param msg a message to be used in case of error.
708      */
checkIdentifier( final String name, final int start, final int end, final String msg)709     static void checkIdentifier(
710         final String name,
711         final int start,
712         final int end,
713         final String msg)
714     {
715         if (name == null || (end == -1 ? name.length() <= start : end <= start))
716         {
717             throw new IllegalArgumentException("Invalid " + msg
718                     + " (must not be null or empty)");
719         }
720         if (!Character.isJavaIdentifierStart(name.charAt(start))) {
721             throw new IllegalArgumentException("Invalid " + msg
722                     + " (must be a valid Java identifier): " + name);
723         }
724         int max = (end == -1 ? name.length() : end);
725         for (int i = start + 1; i < max; ++i) {
726             if (!Character.isJavaIdentifierPart(name.charAt(i))) {
727                 throw new IllegalArgumentException("Invalid " + msg
728                         + " (must be a valid Java identifier): " + name);
729             }
730         }
731     }
732 
733     /**
734      * Checks that the given string is a valid Java identifier or is equal to
735      * '&lt;init&gt;' or '&lt;clinit&gt;'.
736      *
737      * @param name the string to be checked.
738      * @param msg a message to be used in case of error.
739      */
checkMethodIdentifier(final String name, final String msg)740     static void checkMethodIdentifier(final String name, final String msg) {
741         if (name == null || name.length() == 0) {
742             throw new IllegalArgumentException("Invalid " + msg
743                     + " (must not be null or empty)");
744         }
745         if (name.equals("<init>") || name.equals("<clinit>")) {
746             return;
747         }
748         if (!Character.isJavaIdentifierStart(name.charAt(0))) {
749             throw new IllegalArgumentException("Invalid "
750                     + msg
751                     + " (must be a '<init>', '<clinit>' or a valid Java identifier): "
752                     + name);
753         }
754         for (int i = 1; i < name.length(); ++i) {
755             if (!Character.isJavaIdentifierPart(name.charAt(i))) {
756                 throw new IllegalArgumentException("Invalid "
757                         + msg
758                         + " (must be '<init>' or '<clinit>' or a valid Java identifier): "
759                         + name);
760             }
761         }
762     }
763 
764     /**
765      * Checks that the given string is a valid internal class name.
766      *
767      * @param name the string to be checked.
768      * @param msg a message to be used in case of error.
769      */
checkInternalName(final String name, final String msg)770     static void checkInternalName(final String name, final String msg) {
771         checkInternalName(name, 0, -1, msg);
772     }
773 
774     /**
775      * Checks that the given substring is a valid internal class name.
776      *
777      * @param name the string to be checked.
778      * @param start index of the first character of the identifier (inclusive).
779      * @param end index of the last character of the identifier (exclusive). -1
780      *        is equivalent to <tt>name.length()</tt> if name is not
781      *        <tt>null</tt>.
782      * @param msg a message to be used in case of error.
783      */
checkInternalName( final String name, final int start, final int end, final String msg)784     static void checkInternalName(
785         final String name,
786         final int start,
787         final int end,
788         final String msg)
789     {
790         if (name == null || name.length() == 0) {
791             throw new IllegalArgumentException("Invalid " + msg
792                     + " (must not be null or empty)");
793         }
794         int max = (end == -1 ? name.length() : end);
795         try {
796             int begin = start;
797             int slash;
798             do {
799                 slash = name.indexOf('/', begin + 1);
800                 if (slash == -1 || slash > max) {
801                     slash = max;
802                 }
803                 checkIdentifier(name, begin, slash, null);
804                 begin = slash + 1;
805             } while (slash != max);
806         } catch (IllegalArgumentException ex) {
807             throw new IllegalArgumentException("Invalid "
808                     + msg
809                     + " (must be a fully qualified class name in internal form): "
810                     + name);
811         }
812     }
813 
814     /**
815      * Checks that the given string is a valid type descriptor.
816      *
817      * @param desc the string to be checked.
818      * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid.
819      */
checkDesc(final String desc, final boolean canBeVoid)820     static void checkDesc(final String desc, final boolean canBeVoid) {
821         int end = checkDesc(desc, 0, canBeVoid);
822         if (end != desc.length()) {
823             throw new IllegalArgumentException("Invalid descriptor: " + desc);
824         }
825     }
826 
827     /**
828      * Checks that a the given substring is a valid type descriptor.
829      *
830      * @param desc the string to be checked.
831      * @param start index of the first character of the identifier (inclusive).
832      * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid.
833      * @return the index of the last character of the type decriptor, plus one.
834      */
checkDesc( final String desc, final int start, final boolean canBeVoid)835     static int checkDesc(
836         final String desc,
837         final int start,
838         final boolean canBeVoid)
839     {
840         if (desc == null || start >= desc.length()) {
841             throw new IllegalArgumentException("Invalid type descriptor (must not be null or empty)");
842         }
843         int index;
844         switch (desc.charAt(start)) {
845             case 'V':
846                 if (canBeVoid) {
847                     return start + 1;
848                 } else {
849                     throw new IllegalArgumentException("Invalid descriptor: "
850                             + desc);
851                 }
852             case 'Z':
853             case 'C':
854             case 'B':
855             case 'S':
856             case 'I':
857             case 'F':
858             case 'J':
859             case 'D':
860                 return start + 1;
861             case '[':
862                 index = start + 1;
863                 while (index < desc.length() && desc.charAt(index) == '[') {
864                     ++index;
865                 }
866                 if (index < desc.length()) {
867                     return checkDesc(desc, index, false);
868                 } else {
869                     throw new IllegalArgumentException("Invalid descriptor: "
870                             + desc);
871                 }
872             case 'L':
873                 index = desc.indexOf(';', start);
874                 if (index == -1 || index - start < 2) {
875                     throw new IllegalArgumentException("Invalid descriptor: "
876                             + desc);
877                 }
878                 try {
879                     checkInternalName(desc, start + 1, index, null);
880                 } catch (IllegalArgumentException ex) {
881                     throw new IllegalArgumentException("Invalid descriptor: "
882                             + desc);
883                 }
884                 return index + 1;
885             default:
886                 throw new IllegalArgumentException("Invalid descriptor: "
887                         + desc);
888         }
889     }
890 
891     /**
892      * Checks that the given string is a valid method descriptor.
893      *
894      * @param desc the string to be checked.
895      */
checkMethodDesc(final String desc)896     static void checkMethodDesc(final String desc) {
897         if (desc == null || desc.length() == 0) {
898             throw new IllegalArgumentException("Invalid method descriptor (must not be null or empty)");
899         }
900         if (desc.charAt(0) != '(' || desc.length() < 3) {
901             throw new IllegalArgumentException("Invalid descriptor: " + desc);
902         }
903         int start = 1;
904         if (desc.charAt(start) != ')') {
905             do {
906                 if (desc.charAt(start) == 'V') {
907                     throw new IllegalArgumentException("Invalid descriptor: "
908                             + desc);
909                 }
910                 start = checkDesc(desc, start, false);
911             } while (start < desc.length() && desc.charAt(start) != ')');
912         }
913         start = checkDesc(desc, start + 1, true);
914         if (start != desc.length()) {
915             throw new IllegalArgumentException("Invalid descriptor: " + desc);
916         }
917     }
918 
919     /**
920      * Checks that the given label is not null. This method can also check that
921      * the label has been visited.
922      *
923      * @param label the label to be checked.
924      * @param checkVisited <tt>true</tt> to check that the label has been
925      *        visited.
926      * @param msg a message to be used in case of error.
927      */
checkLabel( final Label label, final boolean checkVisited, final String msg)928     void checkLabel(
929         final Label label,
930         final boolean checkVisited,
931         final String msg)
932     {
933         if (label == null) {
934             throw new IllegalArgumentException("Invalid " + msg
935                     + " (must not be null)");
936         }
937         if (checkVisited && labels.get(label) == null) {
938             throw new IllegalArgumentException("Invalid " + msg
939                     + " (must be visited first)");
940         }
941     }
942 }
943