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