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