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.drools.asm.util;
030:
031: import java.util.HashMap;
032:
033: import org.drools.asm.AnnotationVisitor;
034: import org.drools.asm.Attribute;
035: import org.drools.asm.Label;
036: import org.drools.asm.MethodAdapter;
037: import org.drools.asm.MethodVisitor;
038: import org.drools.asm.Opcodes;
039: import org.drools.asm.Type;
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: final String s = "BBBBBBBBBBBBBBBBCCIAADDDDDAAAAAAAAAAAAAAAAAAAABBBBBBBBDD"
083: + "DDDAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
084: + "BBBBBBBBBBBBBBBBBBBJBBBBBBBBBBBBBBBBBBBBHHHHHHHHHHHHHHHHD"
085: + "KLBBBBBBFFFFGGGGAECEBBEEBBAMHHAA";
086: TYPE = new int[s.length()];
087: for (int i = 0; i < CheckMethodAdapter.TYPE.length; ++i) {
088: CheckMethodAdapter.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(this .mv.visitAnnotation(desc,
319: visible));
320: }
321:
322: public AnnotationVisitor visitAnnotationDefault() {
323: checkEndMethod();
324: return new CheckAnnotationAdapter(this .mv
325: .visitAnnotationDefault(), 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(this .mv
334: .visitParameterAnnotation(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: this .mv.visitAttribute(attr);
344: }
345:
346: public void visitCode() {
347: this .startCode = true;
348: this .mv.visitCode();
349: }
350:
351: public void visitInsn(final int opcode) {
352: checkStartCode();
353: checkEndCode();
354: checkOpcode(opcode, 0);
355: this .mv.visitInsn(opcode);
356: }
357:
358: public void visitIntInsn(final int opcode, final int operand) {
359: checkStartCode();
360: checkEndCode();
361: checkOpcode(opcode, 1);
362: switch (opcode) {
363: case Opcodes.BIPUSH:
364: checkSignedByte(operand, "Invalid operand");
365: break;
366: case Opcodes.SIPUSH:
367: checkSignedShort(operand, "Invalid operand");
368: break;
369: // case Constants.NEWARRAY:
370: default:
371: if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) {
372: throw new IllegalArgumentException(
373: "Invalid operand (must be an array type code T_...): "
374: + operand);
375: }
376: }
377: this .mv.visitIntInsn(opcode, operand);
378: }
379:
380: public void visitVarInsn(final int opcode, final int var) {
381: checkStartCode();
382: checkEndCode();
383: checkOpcode(opcode, 2);
384: checkUnsignedShort(var, "Invalid variable index");
385: this .mv.visitVarInsn(opcode, var);
386: }
387:
388: public void visitTypeInsn(final int opcode, final String desc) {
389: checkStartCode();
390: checkEndCode();
391: checkOpcode(opcode, 3);
392: if (desc != null && desc.length() > 0 && desc.charAt(0) == '[') {
393: checkDesc(desc, false);
394: } else {
395: checkInternalName(desc, "type");
396: }
397: if (opcode == Opcodes.NEW && desc.charAt(0) == '[') {
398: throw new IllegalArgumentException(
399: "NEW cannot be used to create arrays: " + desc);
400: }
401: this .mv.visitTypeInsn(opcode, desc);
402: }
403:
404: public void visitFieldInsn(final int opcode, final String owner,
405: final String name, final String desc) {
406: checkStartCode();
407: checkEndCode();
408: checkOpcode(opcode, 4);
409: checkInternalName(owner, "owner");
410: checkIdentifier(name, "name");
411: checkDesc(desc, false);
412: this .mv.visitFieldInsn(opcode, owner, name, desc);
413: }
414:
415: public void visitMethodInsn(final int opcode, final String owner,
416: final String name, final String desc) {
417: checkStartCode();
418: checkEndCode();
419: checkOpcode(opcode, 5);
420: checkMethodIdentifier(name, "name");
421: if (!name.equals("clone")) {
422: // In JDK1.5, clone method can be called on array class descriptors
423: checkInternalName(owner, "owner");
424: }
425: checkMethodDesc(desc);
426: this .mv.visitMethodInsn(opcode, owner, name, desc);
427: }
428:
429: public void visitJumpInsn(final int opcode, final Label label) {
430: checkStartCode();
431: checkEndCode();
432: checkOpcode(opcode, 6);
433: checkLabel(label, false, "label");
434: this .mv.visitJumpInsn(opcode, label);
435: }
436:
437: public void visitLabel(final Label label) {
438: checkStartCode();
439: checkEndCode();
440: checkLabel(label, false, "label");
441: if (this .labels.get(label) != null) {
442: throw new IllegalArgumentException("Already visited label");
443: } else {
444: this .labels.put(label, new Integer(this .labels.size()));
445: }
446: this .mv.visitLabel(label);
447: }
448:
449: public void visitLdcInsn(final Object cst) {
450: checkStartCode();
451: checkEndCode();
452: if (!(cst instanceof Type)) {
453: checkConstant(cst);
454: }
455: this .mv.visitLdcInsn(cst);
456: }
457:
458: public void visitIincInsn(final int var, final int increment) {
459: checkStartCode();
460: checkEndCode();
461: checkUnsignedShort(var, "Invalid variable index");
462: checkSignedShort(increment, "Invalid increment");
463: this .mv.visitIincInsn(var, increment);
464: }
465:
466: public void visitTableSwitchInsn(final int min, final int max,
467: final Label dflt, final Label labels[]) {
468: checkStartCode();
469: checkEndCode();
470: if (max < min) {
471: throw new IllegalArgumentException("Max = " + max
472: + " must be greater than or equal to min = " + min);
473: }
474: checkLabel(dflt, false, "default label");
475: if (labels == null || labels.length != max - min + 1) {
476: throw new IllegalArgumentException(
477: "There must be min - min + 1 labels");
478: }
479: for (int i = 0; i < labels.length; ++i) {
480: checkLabel(labels[i], false, "label at index " + i);
481: }
482: this .mv.visitTableSwitchInsn(min, max, dflt, labels);
483: }
484:
485: public void visitLookupSwitchInsn(final Label dflt,
486: final int keys[], final Label labels[]) {
487: checkEndCode();
488: checkStartCode();
489: checkLabel(dflt, false, "default label");
490: if (keys == null || labels == null
491: || keys.length != labels.length) {
492: throw new IllegalArgumentException(
493: "There must be the same number of keys and labels");
494: }
495: for (int i = 0; i < labels.length; ++i) {
496: checkLabel(labels[i], false, "label at index " + i);
497: }
498: this .mv.visitLookupSwitchInsn(dflt, keys, labels);
499: }
500:
501: public void visitMultiANewArrayInsn(final String desc,
502: final int dims) {
503: checkStartCode();
504: checkEndCode();
505: checkDesc(desc, false);
506: if (desc.charAt(0) != '[') {
507: throw new IllegalArgumentException(
508: "Invalid descriptor (must be an array type descriptor): "
509: + desc);
510: }
511: if (dims < 1) {
512: throw new IllegalArgumentException(
513: "Invalid dimensions (must be greater than 0): "
514: + dims);
515: }
516: if (dims > desc.lastIndexOf('[') + 1) {
517: throw new IllegalArgumentException(
518: "Invalid dimensions (must not be greater than dims(desc)): "
519: + dims);
520: }
521: this .mv.visitMultiANewArrayInsn(desc, dims);
522: }
523:
524: public void visitTryCatchBlock(final Label start, final Label end,
525: final Label handler, final String type) {
526: checkStartCode();
527: checkEndCode();
528: if (type != null) {
529: checkInternalName(type, "type");
530: }
531: this .mv.visitTryCatchBlock(start, end, handler, type);
532: }
533:
534: public void visitLocalVariable(final String name,
535: final String desc, final String signature,
536: final Label start, final Label end, final int index) {
537: checkStartCode();
538: checkEndCode();
539: checkIdentifier(name, "name");
540: checkDesc(desc, false);
541: checkLabel(start, true, "start label");
542: checkLabel(end, true, "end label");
543: checkUnsignedShort(index, "Invalid variable index");
544: final int s = ((Integer) this .labels.get(start)).intValue();
545: final int e = ((Integer) this .labels.get(end)).intValue();
546: if (e < s) {
547: throw new IllegalArgumentException(
548: "Invalid start and end labels (end must be greater than start)");
549: }
550: this .mv.visitLocalVariable(name, desc, signature, start, end,
551: index);
552: }
553:
554: public void visitLineNumber(final int line, final Label start) {
555: checkStartCode();
556: checkEndCode();
557: checkUnsignedShort(line, "Invalid line number");
558: checkLabel(start, true, "start label");
559: this .mv.visitLineNumber(line, start);
560: }
561:
562: public void visitMaxs(final int maxStack, final int maxLocals) {
563: checkStartCode();
564: checkEndCode();
565: this .endCode = true;
566: checkUnsignedShort(maxStack, "Invalid min stack");
567: checkUnsignedShort(maxLocals, "Invalid min locals");
568: this .mv.visitMaxs(maxStack, maxLocals);
569: }
570:
571: public void visitEnd() {
572: checkEndMethod();
573: this .endMethod = true;
574: this .mv.visitEnd();
575: }
576:
577: // -------------------------------------------------------------------------
578:
579: /**
580: * Checks that the visitCode method has been called.
581: */
582: void checkStartCode() {
583: if (!this .startCode) {
584: throw new IllegalStateException(
585: "Cannot visit instructions before visitCode has been called.");
586: }
587: }
588:
589: /**
590: * Checks that the visitMaxs method has not been called.
591: */
592: void checkEndCode() {
593: if (this .endCode) {
594: throw new IllegalStateException(
595: "Cannot visit instructions after visitMaxs has been called.");
596: }
597: }
598:
599: /**
600: * Checks that the visitEnd method has not been called.
601: */
602: void checkEndMethod() {
603: if (this .endMethod) {
604: throw new IllegalStateException(
605: "Cannot visit elements after visitEnd has been called.");
606: }
607: }
608:
609: /**
610: * Checks that the type of the given opcode is equal to the given type.
611: *
612: * @param opcode the opcode to be checked.
613: * @param type the expected opcode type.
614: */
615: static void checkOpcode(final int opcode, final int type) {
616: if (opcode < 0 || opcode > 199
617: || CheckMethodAdapter.TYPE[opcode] != type) {
618: throw new IllegalArgumentException("Invalid opcode: "
619: + opcode);
620: }
621: }
622:
623: /**
624: * Checks that the given value is a signed byte.
625: *
626: * @param value the value to be checked.
627: * @param msg an message to be used in case of error.
628: */
629: static void checkSignedByte(final int value, final String msg) {
630: if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
631: throw new IllegalArgumentException(msg
632: + " (must be a signed byte): " + value);
633: }
634: }
635:
636: /**
637: * Checks that the given value is a signed short.
638: *
639: * @param value the value to be checked.
640: * @param msg an message to be used in case of error.
641: */
642: static void checkSignedShort(final int value, final String msg) {
643: if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
644: throw new IllegalArgumentException(msg
645: + " (must be a signed short): " + value);
646: }
647: }
648:
649: /**
650: * Checks that the given value is an unsigned short.
651: *
652: * @param value the value to be checked.
653: * @param msg an message to be used in case of error.
654: */
655: static void checkUnsignedShort(final int value, final String msg) {
656: if (value < 0 || value > 65535) {
657: throw new IllegalArgumentException(msg
658: + " (must be an unsigned short): " + value);
659: }
660: }
661:
662: /**
663: * Checks that the given value is an {@link Integer}, a{@link Float}, a
664: * {@link Long}, a {@link Double} or a {@link String}.
665: *
666: * @param cst the value to be checked.
667: */
668: static void checkConstant(final Object cst) {
669: if (!(cst instanceof Integer) && !(cst instanceof Float)
670: && !(cst instanceof Long) && !(cst instanceof Double)
671: && !(cst instanceof String)) {
672: throw new IllegalArgumentException("Invalid constant: "
673: + cst);
674: }
675: }
676:
677: /**
678: * Checks that the given string is a valid Java identifier.
679: *
680: * @param name the string to be checked.
681: * @param msg a message to be used in case of error.
682: */
683: static void checkIdentifier(final String name, final String msg) {
684: checkIdentifier(name, 0, -1, msg);
685: }
686:
687: /**
688: * Checks that the given substring is a valid Java identifier.
689: *
690: * @param name the string to be checked.
691: * @param start index of the first character of the identifier (inclusive).
692: * @param end index of the last character of the identifier (exclusive). -1
693: * is equivalent to <tt>name.length()</tt> if name is not
694: * <tt>null</tt>.
695: * @param msg a message to be used in case of error.
696: */
697: static void checkIdentifier(final String name, final int start,
698: final int end, final String msg) {
699: if (name == null
700: || (end == -1 ? name.length() <= start : end <= start)) {
701: throw new IllegalArgumentException("Invalid " + msg
702: + " (must not be null or empty)");
703: }
704: if (!Character.isJavaIdentifierStart(name.charAt(start))) {
705: throw new IllegalArgumentException("Invalid " + msg
706: + " (must be a valid Java identifier): " + name);
707: }
708: final int max = (end == -1 ? name.length() : end);
709: for (int i = start + 1; i < max; ++i) {
710: if (!Character.isJavaIdentifierPart(name.charAt(i))) {
711: throw new IllegalArgumentException("Invalid " + msg
712: + " (must be a valid Java identifier): " + name);
713: }
714: }
715: }
716:
717: /**
718: * Checks that the given string is a valid Java identifier or is equal to
719: * '<init>' or '<clinit>'.
720: *
721: * @param name the string to be checked.
722: * @param msg a message to be used in case of error.
723: */
724: static void checkMethodIdentifier(final String name,
725: final String msg) {
726: if (name == null || name.length() == 0) {
727: throw new IllegalArgumentException("Invalid " + msg
728: + " (must not be null or empty)");
729: }
730: if (name.equals("<init>") || name.equals("<clinit>")) {
731: return;
732: }
733: if (!Character.isJavaIdentifierStart(name.charAt(0))) {
734: throw new IllegalArgumentException(
735: "Invalid "
736: + msg
737: + " (must be a '<init>', '<clinit>' or a valid Java identifier): "
738: + name);
739: }
740: for (int i = 1; i < name.length(); ++i) {
741: if (!Character.isJavaIdentifierPart(name.charAt(i))) {
742: throw new IllegalArgumentException(
743: "Invalid "
744: + msg
745: + " (must be '<init>' or '<clinit>' or a valid Java identifier): "
746: + name);
747: }
748: }
749: }
750:
751: /**
752: * Checks that the given string is a valid internal class name.
753: *
754: * @param name the string to be checked.
755: * @param msg a message to be used in case of error.
756: */
757: static void checkInternalName(final String name, final String msg) {
758: checkInternalName(name, 0, -1, msg);
759: }
760:
761: /**
762: * Checks that the given substring is a valid internal class name.
763: *
764: * @param name the string to be checked.
765: * @param start index of the first character of the identifier (inclusive).
766: * @param end index of the last character of the identifier (exclusive). -1
767: * is equivalent to <tt>name.length()</tt> if name is not
768: * <tt>null</tt>.
769: * @param msg a message to be used in case of error.
770: */
771: static void checkInternalName(final String name, final int start,
772: final int end, final String msg) {
773: if (name == null || name.length() == 0) {
774: throw new IllegalArgumentException("Invalid " + msg
775: + " (must not be null or empty)");
776: }
777: final int max = (end == -1 ? name.length() : end);
778: try {
779: int begin = start;
780: int slash;
781: do {
782: slash = name.indexOf('/', begin + 1);
783: if (slash == -1 || slash > max) {
784: slash = max;
785: }
786: checkIdentifier(name, begin, slash, null);
787: begin = slash + 1;
788: } while (slash != max);
789: } catch (final IllegalArgumentException _) {
790: throw new IllegalArgumentException(
791: "Invalid "
792: + msg
793: + " (must be a fully qualified class name in internal form): "
794: + name);
795: }
796: }
797:
798: /**
799: * Checks that the given string is a valid type descriptor.
800: *
801: * @param desc the string to be checked.
802: * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid.
803: */
804: static void checkDesc(final String desc, final boolean canBeVoid) {
805: final int end = checkDesc(desc, 0, canBeVoid);
806: if (end != desc.length()) {
807: throw new IllegalArgumentException("Invalid descriptor: "
808: + desc);
809: }
810: }
811:
812: /**
813: * Checks that a the given substring is a valid type descriptor.
814: *
815: * @param desc the string to be checked.
816: * @param start index of the first character of the identifier (inclusive).
817: * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid.
818: * @return the index of the last character of the type decriptor, plus one.
819: */
820: static int checkDesc(final String desc, final int start,
821: final boolean canBeVoid) {
822: if (desc == null || start >= desc.length()) {
823: throw new IllegalArgumentException(
824: "Invalid type descriptor (must not be null or empty)");
825: }
826: int index;
827: switch (desc.charAt(start)) {
828: case 'V':
829: if (canBeVoid) {
830: return start + 1;
831: } else {
832: throw new IllegalArgumentException(
833: "Invalid descriptor: " + desc);
834: }
835: case 'Z':
836: case 'C':
837: case 'B':
838: case 'S':
839: case 'I':
840: case 'F':
841: case 'J':
842: case 'D':
843: return start + 1;
844: case '[':
845: index = start + 1;
846: while (index < desc.length() && desc.charAt(index) == '[') {
847: ++index;
848: }
849: if (index < desc.length()) {
850: return checkDesc(desc, index, false);
851: } else {
852: throw new IllegalArgumentException(
853: "Invalid descriptor: " + desc);
854: }
855: case 'L':
856: index = desc.indexOf(';', start);
857: if (index == -1 || index - start < 2) {
858: throw new IllegalArgumentException(
859: "Invalid descriptor: " + desc);
860: }
861: try {
862: checkInternalName(desc, start + 1, index, null);
863: } catch (final IllegalArgumentException _) {
864: throw new IllegalArgumentException(
865: "Invalid descriptor: " + desc);
866: }
867: return index + 1;
868: default:
869: throw new IllegalArgumentException("Invalid descriptor: "
870: + desc);
871: }
872: }
873:
874: /**
875: * Checks that the given string is a valid method descriptor.
876: *
877: * @param desc the string to be checked.
878: */
879: static void checkMethodDesc(final String desc) {
880: if (desc == null || desc.length() == 0) {
881: throw new IllegalArgumentException(
882: "Invalid method descriptor (must not be null or empty)");
883: }
884: if (desc.charAt(0) != '(' || desc.length() < 3) {
885: throw new IllegalArgumentException("Invalid descriptor: "
886: + desc);
887: }
888: int start = 1;
889: if (desc.charAt(start) != ')') {
890: do {
891: if (desc.charAt(start) == 'V') {
892: throw new IllegalArgumentException(
893: "Invalid descriptor: " + desc);
894: }
895: start = checkDesc(desc, start, false);
896: } while (start < desc.length() && desc.charAt(start) != ')');
897: }
898: start = checkDesc(desc, start + 1, true);
899: if (start != desc.length()) {
900: throw new IllegalArgumentException("Invalid descriptor: "
901: + desc);
902: }
903: }
904:
905: /**
906: * Checks that the given label is not null. This method can also check that
907: * the label has been visited.
908: *
909: * @param label the label to be checked.
910: * @param checkVisited <tt>true</tt> to check that the label has been
911: * visited.
912: * @param msg a message to be used in case of error.
913: */
914: void checkLabel(final Label label, final boolean checkVisited,
915: final String msg) {
916: if (label == null) {
917: throw new IllegalArgumentException("Invalid " + msg
918: + " (must not be null)");
919: }
920: if (checkVisited && this .labels.get(label) == null) {
921: throw new IllegalArgumentException("Invalid " + msg
922: + " (must be visited first)");
923: }
924: }
925: }
|