0001: /***
0002: * ASM: a very small and fast Java bytecode manipulation framework
0003: * Copyright (c) 2000-2005 INRIA, France Telecom
0004: * All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions
0008: * are met:
0009: * 1. Redistributions of source code must retain the above copyright
0010: * notice, this list of conditions and the following disclaimer.
0011: * 2. Redistributions in binary form must reproduce the above copyright
0012: * notice, this list of conditions and the following disclaimer in the
0013: * documentation and/or other materials provided with the distribution.
0014: * 3. Neither the name of the copyright holders nor the names of its
0015: * contributors may be used to endorse or promote products derived from
0016: * this software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
0028: * THE POSSIBILITY OF SUCH DAMAGE.
0029: */package com.tc.asm.util;
0030:
0031: import com.tc.asm.AnnotationVisitor;
0032: import com.tc.asm.Attribute;
0033: import com.tc.asm.Label;
0034: import com.tc.asm.MethodAdapter;
0035: import com.tc.asm.MethodVisitor;
0036: import com.tc.asm.Opcodes;
0037: import com.tc.asm.Type;
0038:
0039: import java.util.HashMap;
0040:
0041: /**
0042: * A {@link MethodAdapter} that checks that its methods are properly used. More
0043: * precisely this code adapter checks each instruction individually (i.e., each
0044: * visit method checks some preconditions based <i>only</i> on its arguments -
0045: * such as the fact that the given opcode is correct for a given visit method),
0046: * but does <i>not</i> check the <i>sequence</i> of instructions. For example,
0047: * in a method whose signature is <tt>void m ()</tt>, the invalid instruction
0048: * IRETURN, or the invalid sequence IADD L2I will <i>not</i> be detected by
0049: * this code adapter.
0050: *
0051: * @author Eric Bruneton
0052: */
0053: public class CheckMethodAdapter extends MethodAdapter {
0054:
0055: /**
0056: * <tt>true</tt> if the visitCode method has been called.
0057: */
0058: private boolean startCode;
0059:
0060: /**
0061: * <tt>true</tt> if the visitMaxs method has been called.
0062: */
0063: private boolean endCode;
0064:
0065: /**
0066: * <tt>true</tt> if the visitEnd method has been called.
0067: */
0068: private boolean endMethod;
0069:
0070: /**
0071: * The already visited labels. This map associate Integer values to Label
0072: * keys.
0073: */
0074: private HashMap labels;
0075:
0076: /**
0077: * Code of the visit method to be used for each opcode.
0078: */
0079: private final static int[] TYPE;
0080:
0081: static {
0082: String s = "BBBBBBBBBBBBBBBBCCIAADDDDDAAAAAAAAAAAAAAAAAAAABBBBBBBBDD"
0083: + "DDDAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
0084: + "BBBBBBBBBBBBBBBBBBBJBBBBBBBBBBBBBBBBBBBBHHHHHHHHHHHHHHHHD"
0085: + "KLBBBBBBFFFFGGGGAECEBBEEBBAMHHAA";
0086: TYPE = new int[s.length()];
0087: for (int i = 0; i < TYPE.length; ++i) {
0088: TYPE[i] = s.charAt(i) - 'A' - 1;
0089: }
0090: }
0091:
0092: // code to generate the above string
0093: // public static void main (String[] args) {
0094: // int[] TYPE = new int[] {
0095: // 0, //NOP
0096: // 0, //ACONST_NULL
0097: // 0, //ICONST_M1
0098: // 0, //ICONST_0
0099: // 0, //ICONST_1
0100: // 0, //ICONST_2
0101: // 0, //ICONST_3
0102: // 0, //ICONST_4
0103: // 0, //ICONST_5
0104: // 0, //LCONST_0
0105: // 0, //LCONST_1
0106: // 0, //FCONST_0
0107: // 0, //FCONST_1
0108: // 0, //FCONST_2
0109: // 0, //DCONST_0
0110: // 0, //DCONST_1
0111: // 1, //BIPUSH
0112: // 1, //SIPUSH
0113: // 7, //LDC
0114: // -1, //LDC_W
0115: // -1, //LDC2_W
0116: // 2, //ILOAD
0117: // 2, //LLOAD
0118: // 2, //FLOAD
0119: // 2, //DLOAD
0120: // 2, //ALOAD
0121: // -1, //ILOAD_0
0122: // -1, //ILOAD_1
0123: // -1, //ILOAD_2
0124: // -1, //ILOAD_3
0125: // -1, //LLOAD_0
0126: // -1, //LLOAD_1
0127: // -1, //LLOAD_2
0128: // -1, //LLOAD_3
0129: // -1, //FLOAD_0
0130: // -1, //FLOAD_1
0131: // -1, //FLOAD_2
0132: // -1, //FLOAD_3
0133: // -1, //DLOAD_0
0134: // -1, //DLOAD_1
0135: // -1, //DLOAD_2
0136: // -1, //DLOAD_3
0137: // -1, //ALOAD_0
0138: // -1, //ALOAD_1
0139: // -1, //ALOAD_2
0140: // -1, //ALOAD_3
0141: // 0, //IALOAD
0142: // 0, //LALOAD
0143: // 0, //FALOAD
0144: // 0, //DALOAD
0145: // 0, //AALOAD
0146: // 0, //BALOAD
0147: // 0, //CALOAD
0148: // 0, //SALOAD
0149: // 2, //ISTORE
0150: // 2, //LSTORE
0151: // 2, //FSTORE
0152: // 2, //DSTORE
0153: // 2, //ASTORE
0154: // -1, //ISTORE_0
0155: // -1, //ISTORE_1
0156: // -1, //ISTORE_2
0157: // -1, //ISTORE_3
0158: // -1, //LSTORE_0
0159: // -1, //LSTORE_1
0160: // -1, //LSTORE_2
0161: // -1, //LSTORE_3
0162: // -1, //FSTORE_0
0163: // -1, //FSTORE_1
0164: // -1, //FSTORE_2
0165: // -1, //FSTORE_3
0166: // -1, //DSTORE_0
0167: // -1, //DSTORE_1
0168: // -1, //DSTORE_2
0169: // -1, //DSTORE_3
0170: // -1, //ASTORE_0
0171: // -1, //ASTORE_1
0172: // -1, //ASTORE_2
0173: // -1, //ASTORE_3
0174: // 0, //IASTORE
0175: // 0, //LASTORE
0176: // 0, //FASTORE
0177: // 0, //DASTORE
0178: // 0, //AASTORE
0179: // 0, //BASTORE
0180: // 0, //CASTORE
0181: // 0, //SASTORE
0182: // 0, //POP
0183: // 0, //POP2
0184: // 0, //DUP
0185: // 0, //DUP_X1
0186: // 0, //DUP_X2
0187: // 0, //DUP2
0188: // 0, //DUP2_X1
0189: // 0, //DUP2_X2
0190: // 0, //SWAP
0191: // 0, //IADD
0192: // 0, //LADD
0193: // 0, //FADD
0194: // 0, //DADD
0195: // 0, //ISUB
0196: // 0, //LSUB
0197: // 0, //FSUB
0198: // 0, //DSUB
0199: // 0, //IMUL
0200: // 0, //LMUL
0201: // 0, //FMUL
0202: // 0, //DMUL
0203: // 0, //IDIV
0204: // 0, //LDIV
0205: // 0, //FDIV
0206: // 0, //DDIV
0207: // 0, //IREM
0208: // 0, //LREM
0209: // 0, //FREM
0210: // 0, //DREM
0211: // 0, //INEG
0212: // 0, //LNEG
0213: // 0, //FNEG
0214: // 0, //DNEG
0215: // 0, //ISHL
0216: // 0, //LSHL
0217: // 0, //ISHR
0218: // 0, //LSHR
0219: // 0, //IUSHR
0220: // 0, //LUSHR
0221: // 0, //IAND
0222: // 0, //LAND
0223: // 0, //IOR
0224: // 0, //LOR
0225: // 0, //IXOR
0226: // 0, //LXOR
0227: // 8, //IINC
0228: // 0, //I2L
0229: // 0, //I2F
0230: // 0, //I2D
0231: // 0, //L2I
0232: // 0, //L2F
0233: // 0, //L2D
0234: // 0, //F2I
0235: // 0, //F2L
0236: // 0, //F2D
0237: // 0, //D2I
0238: // 0, //D2L
0239: // 0, //D2F
0240: // 0, //I2B
0241: // 0, //I2C
0242: // 0, //I2S
0243: // 0, //LCMP
0244: // 0, //FCMPL
0245: // 0, //FCMPG
0246: // 0, //DCMPL
0247: // 0, //DCMPG
0248: // 6, //IFEQ
0249: // 6, //IFNE
0250: // 6, //IFLT
0251: // 6, //IFGE
0252: // 6, //IFGT
0253: // 6, //IFLE
0254: // 6, //IF_ICMPEQ
0255: // 6, //IF_ICMPNE
0256: // 6, //IF_ICMPLT
0257: // 6, //IF_ICMPGE
0258: // 6, //IF_ICMPGT
0259: // 6, //IF_ICMPLE
0260: // 6, //IF_ACMPEQ
0261: // 6, //IF_ACMPNE
0262: // 6, //GOTO
0263: // 6, //JSR
0264: // 2, //RET
0265: // 9, //TABLESWITCH
0266: // 10, //LOOKUPSWITCH
0267: // 0, //IRETURN
0268: // 0, //LRETURN
0269: // 0, //FRETURN
0270: // 0, //DRETURN
0271: // 0, //ARETURN
0272: // 0, //RETURN
0273: // 4, //GETSTATIC
0274: // 4, //PUTSTATIC
0275: // 4, //GETFIELD
0276: // 4, //PUTFIELD
0277: // 5, //INVOKEVIRTUAL
0278: // 5, //INVOKESPECIAL
0279: // 5, //INVOKESTATIC
0280: // 5, //INVOKEINTERFACE
0281: // -1, //UNUSED
0282: // 3, //NEW
0283: // 1, //NEWARRAY
0284: // 3, //ANEWARRAY
0285: // 0, //ARRAYLENGTH
0286: // 0, //ATHROW
0287: // 3, //CHECKCAST
0288: // 3, //INSTANCEOF
0289: // 0, //MONITORENTER
0290: // 0, //MONITOREXIT
0291: // -1, //WIDE
0292: // 11, //MULTIANEWARRAY
0293: // 6, //IFNULL
0294: // 6, //IFNONNULL
0295: // -1, //GOTO_W
0296: // -1 //JSR_W
0297: // };
0298: // for (int i = 0; i < TYPE.length; ++i) {
0299: // System.out.print((char)(TYPE[i] + 1 + 'A'));
0300: // }
0301: // System.out.println();
0302: // }
0303:
0304: /**
0305: * Constructs a new {@link CheckMethodAdapter} object.
0306: *
0307: * @param cv the code visitor to which this adapter must delegate calls.
0308: */
0309: public CheckMethodAdapter(final MethodVisitor cv) {
0310: super (cv);
0311: this .labels = new HashMap();
0312: }
0313:
0314: public AnnotationVisitor visitAnnotation(final String desc,
0315: final boolean visible) {
0316: checkEndMethod();
0317: checkDesc(desc, false);
0318: return new CheckAnnotationAdapter(mv.visitAnnotation(desc,
0319: visible));
0320: }
0321:
0322: public AnnotationVisitor visitAnnotationDefault() {
0323: checkEndMethod();
0324: return new CheckAnnotationAdapter(mv.visitAnnotationDefault(),
0325: false);
0326: }
0327:
0328: public AnnotationVisitor visitParameterAnnotation(
0329: final int parameter, final String desc,
0330: final boolean visible) {
0331: checkEndMethod();
0332: checkDesc(desc, false);
0333: return new CheckAnnotationAdapter(mv.visitParameterAnnotation(
0334: parameter, desc, visible));
0335: }
0336:
0337: public void visitAttribute(final Attribute attr) {
0338: checkEndMethod();
0339: if (attr == null) {
0340: throw new IllegalArgumentException(
0341: "Invalid attribute (must not be null)");
0342: }
0343: mv.visitAttribute(attr);
0344: }
0345:
0346: public void visitCode() {
0347: startCode = true;
0348: mv.visitCode();
0349: }
0350:
0351: public void visitFrame(final int type, final int nLocal,
0352: final Object[] local, final int nStack, final Object[] stack) {
0353: int mLocal;
0354: int mStack;
0355: switch (type) {
0356: case Opcodes.F_NEW:
0357: case Opcodes.F_FULL:
0358: mLocal = Integer.MAX_VALUE;
0359: mStack = Integer.MAX_VALUE;
0360: break;
0361:
0362: case Opcodes.F_SAME:
0363: mLocal = 0;
0364: mStack = 0;
0365: break;
0366:
0367: case Opcodes.F_SAME1:
0368: mLocal = 0;
0369: mStack = 1;
0370: break;
0371:
0372: case Opcodes.F_APPEND:
0373: case Opcodes.F_CHOP:
0374: mLocal = 3;
0375: mStack = 0;
0376: break;
0377:
0378: default:
0379: throw new IllegalArgumentException("Invalid frame type "
0380: + type);
0381: }
0382:
0383: if (nLocal > mLocal) {
0384: throw new IllegalArgumentException("Invalid nLocal="
0385: + nLocal + " for frame type " + type);
0386: }
0387: if (nStack > mStack) {
0388: throw new IllegalArgumentException("Invalid nStack="
0389: + nStack + " for frame type " + type);
0390: }
0391:
0392: if (type != Opcodes.F_CHOP) {
0393: if (nLocal > 0 && (local == null || local.length < nLocal)) {
0394: throw new IllegalArgumentException(
0395: "Array local[] is shorter than nLocal");
0396: }
0397: for (int i = 0; i < nLocal; ++i) {
0398: checkFrameValue(local[i]);
0399: }
0400: }
0401: if (nStack > 0 && (stack == null || stack.length < nStack)) {
0402: throw new IllegalArgumentException(
0403: "Array stack[] is shorter than nStack");
0404: }
0405: for (int i = 0; i < nStack; ++i) {
0406: checkFrameValue(stack[i]);
0407: }
0408:
0409: mv.visitFrame(type, nLocal, local, nStack, stack);
0410: }
0411:
0412: public void visitInsn(final int opcode) {
0413: checkStartCode();
0414: checkEndCode();
0415: checkOpcode(opcode, 0);
0416: mv.visitInsn(opcode);
0417: }
0418:
0419: public void visitIntInsn(final int opcode, final int operand) {
0420: checkStartCode();
0421: checkEndCode();
0422: checkOpcode(opcode, 1);
0423: switch (opcode) {
0424: case Opcodes.BIPUSH:
0425: checkSignedByte(operand, "Invalid operand");
0426: break;
0427: case Opcodes.SIPUSH:
0428: checkSignedShort(operand, "Invalid operand");
0429: break;
0430: // case Constants.NEWARRAY:
0431: default:
0432: if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) {
0433: throw new IllegalArgumentException(
0434: "Invalid operand (must be an array type code T_...): "
0435: + operand);
0436: }
0437: }
0438: mv.visitIntInsn(opcode, operand);
0439: }
0440:
0441: public void visitVarInsn(final int opcode, final int var) {
0442: checkStartCode();
0443: checkEndCode();
0444: checkOpcode(opcode, 2);
0445: checkUnsignedShort(var, "Invalid variable index");
0446: mv.visitVarInsn(opcode, var);
0447: }
0448:
0449: public void visitTypeInsn(final int opcode, final String type) {
0450: checkStartCode();
0451: checkEndCode();
0452: checkOpcode(opcode, 3);
0453: checkInternalName(type, "type");
0454: if (opcode == Opcodes.NEW && type.charAt(0) == '[') {
0455: throw new IllegalArgumentException(
0456: "NEW cannot be used to create arrays: " + type);
0457: }
0458: mv.visitTypeInsn(opcode, type);
0459: }
0460:
0461: public void visitFieldInsn(final int opcode, final String owner,
0462: final String name, final String desc) {
0463: checkStartCode();
0464: checkEndCode();
0465: checkOpcode(opcode, 4);
0466: checkInternalName(owner, "owner");
0467: checkIdentifier(name, "name");
0468: checkDesc(desc, false);
0469: mv.visitFieldInsn(opcode, owner, name, desc);
0470: }
0471:
0472: public void visitMethodInsn(final int opcode, final String owner,
0473: final String name, final String desc) {
0474: checkStartCode();
0475: checkEndCode();
0476: checkOpcode(opcode, 5);
0477: checkMethodIdentifier(name, "name");
0478: checkInternalName(owner, "owner");
0479: checkMethodDesc(desc);
0480: mv.visitMethodInsn(opcode, owner, name, desc);
0481: }
0482:
0483: public void visitJumpInsn(final int opcode, final Label label) {
0484: checkStartCode();
0485: checkEndCode();
0486: checkOpcode(opcode, 6);
0487: checkLabel(label, false, "label");
0488: mv.visitJumpInsn(opcode, label);
0489: }
0490:
0491: public void visitLabel(final Label label) {
0492: checkStartCode();
0493: checkEndCode();
0494: checkLabel(label, false, "label");
0495: if (labels.get(label) != null) {
0496: throw new IllegalArgumentException("Already visited label");
0497: } else {
0498: labels.put(label, new Integer(labels.size()));
0499: }
0500: mv.visitLabel(label);
0501: }
0502:
0503: public void visitLdcInsn(final Object cst) {
0504: checkStartCode();
0505: checkEndCode();
0506: if (!(cst instanceof Type)) {
0507: checkConstant(cst);
0508: }
0509: mv.visitLdcInsn(cst);
0510: }
0511:
0512: public void visitIincInsn(final int var, final int increment) {
0513: checkStartCode();
0514: checkEndCode();
0515: checkUnsignedShort(var, "Invalid variable index");
0516: checkSignedShort(increment, "Invalid increment");
0517: mv.visitIincInsn(var, increment);
0518: }
0519:
0520: public void visitTableSwitchInsn(final int min, final int max,
0521: final Label dflt, final Label labels[]) {
0522: checkStartCode();
0523: checkEndCode();
0524: if (max < min) {
0525: throw new IllegalArgumentException("Max = " + max
0526: + " must be greater than or equal to min = " + min);
0527: }
0528: checkLabel(dflt, false, "default label");
0529: if (labels == null || labels.length != max - min + 1) {
0530: throw new IllegalArgumentException(
0531: "There must be max - min + 1 labels");
0532: }
0533: for (int i = 0; i < labels.length; ++i) {
0534: checkLabel(labels[i], false, "label at index " + i);
0535: }
0536: mv.visitTableSwitchInsn(min, max, dflt, labels);
0537: }
0538:
0539: public void visitLookupSwitchInsn(final Label dflt,
0540: final int keys[], final Label labels[]) {
0541: checkEndCode();
0542: checkStartCode();
0543: checkLabel(dflt, false, "default label");
0544: if (keys == null || labels == null
0545: || keys.length != labels.length) {
0546: throw new IllegalArgumentException(
0547: "There must be the same number of keys and labels");
0548: }
0549: for (int i = 0; i < labels.length; ++i) {
0550: checkLabel(labels[i], false, "label at index " + i);
0551: }
0552: mv.visitLookupSwitchInsn(dflt, keys, labels);
0553: }
0554:
0555: public void visitMultiANewArrayInsn(final String desc,
0556: final int dims) {
0557: checkStartCode();
0558: checkEndCode();
0559: checkDesc(desc, false);
0560: if (desc.charAt(0) != '[') {
0561: throw new IllegalArgumentException(
0562: "Invalid descriptor (must be an array type descriptor): "
0563: + desc);
0564: }
0565: if (dims < 1) {
0566: throw new IllegalArgumentException(
0567: "Invalid dimensions (must be greater than 0): "
0568: + dims);
0569: }
0570: if (dims > desc.lastIndexOf('[') + 1) {
0571: throw new IllegalArgumentException(
0572: "Invalid dimensions (must not be greater than dims(desc)): "
0573: + dims);
0574: }
0575: mv.visitMultiANewArrayInsn(desc, dims);
0576: }
0577:
0578: public void visitTryCatchBlock(final Label start, final Label end,
0579: final Label handler, final String type) {
0580: checkStartCode();
0581: checkEndCode();
0582: if (type != null) {
0583: checkInternalName(type, "type");
0584: }
0585: mv.visitTryCatchBlock(start, end, handler, type);
0586: }
0587:
0588: public void visitLocalVariable(final String name,
0589: final String desc, final String signature,
0590: final Label start, final Label end, final int index) {
0591: checkStartCode();
0592: checkEndCode();
0593: checkIdentifier(name, "name");
0594: checkDesc(desc, false);
0595: checkLabel(start, true, "start label");
0596: checkLabel(end, true, "end label");
0597: checkUnsignedShort(index, "Invalid variable index");
0598: int s = ((Integer) labels.get(start)).intValue();
0599: int e = ((Integer) labels.get(end)).intValue();
0600: if (e < s) {
0601: throw new IllegalArgumentException(
0602: "Invalid start and end labels (end must be greater than start)");
0603: }
0604: mv.visitLocalVariable(name, desc, signature, start, end, index);
0605: }
0606:
0607: public void visitLineNumber(final int line, final Label start) {
0608: checkStartCode();
0609: checkEndCode();
0610: checkUnsignedShort(line, "Invalid line number");
0611: checkLabel(start, true, "start label");
0612: mv.visitLineNumber(line, start);
0613: }
0614:
0615: public void visitMaxs(final int maxStack, final int maxLocals) {
0616: checkStartCode();
0617: checkEndCode();
0618: endCode = true;
0619: checkUnsignedShort(maxStack, "Invalid max stack");
0620: checkUnsignedShort(maxLocals, "Invalid max locals");
0621: mv.visitMaxs(maxStack, maxLocals);
0622: }
0623:
0624: public void visitEnd() {
0625: checkEndMethod();
0626: endMethod = true;
0627: mv.visitEnd();
0628: }
0629:
0630: // -------------------------------------------------------------------------
0631:
0632: /**
0633: * Checks that the visitCode method has been called.
0634: */
0635: void checkStartCode() {
0636: if (!startCode) {
0637: throw new IllegalStateException(
0638: "Cannot visit instructions before visitCode has been called.");
0639: }
0640: }
0641:
0642: /**
0643: * Checks that the visitMaxs method has not been called.
0644: */
0645: void checkEndCode() {
0646: if (endCode) {
0647: throw new IllegalStateException(
0648: "Cannot visit instructions after visitMaxs has been called.");
0649: }
0650: }
0651:
0652: /**
0653: * Checks that the visitEnd method has not been called.
0654: */
0655: void checkEndMethod() {
0656: if (endMethod) {
0657: throw new IllegalStateException(
0658: "Cannot visit elements after visitEnd has been called.");
0659: }
0660: }
0661:
0662: /**
0663: * Checks a stack frame value.
0664: *
0665: * @param value the value to be checked.
0666: */
0667: static void checkFrameValue(final Object value) {
0668: if (value == Opcodes.TOP || value == Opcodes.INTEGER
0669: || value == Opcodes.FLOAT || value == Opcodes.LONG
0670: || value == Opcodes.DOUBLE || value == Opcodes.NULL
0671: || value == Opcodes.UNINITIALIZED_THIS) {
0672: return;
0673: }
0674: if (value instanceof String) {
0675: checkInternalName((String) value,
0676: "Invalid stack frame value");
0677: return;
0678: }
0679: if (!(value instanceof Label)) {
0680: throw new IllegalArgumentException(
0681: "Invalid stack frame value: " + value);
0682: }
0683: }
0684:
0685: /**
0686: * Checks that the type of the given opcode is equal to the given type.
0687: *
0688: * @param opcode the opcode to be checked.
0689: * @param type the expected opcode type.
0690: */
0691: static void checkOpcode(final int opcode, final int type) {
0692: if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) {
0693: throw new IllegalArgumentException("Invalid opcode: "
0694: + opcode);
0695: }
0696: }
0697:
0698: /**
0699: * Checks that the given value is a signed byte.
0700: *
0701: * @param value the value to be checked.
0702: * @param msg an message to be used in case of error.
0703: */
0704: static void checkSignedByte(final int value, final String msg) {
0705: if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
0706: throw new IllegalArgumentException(msg
0707: + " (must be a signed byte): " + value);
0708: }
0709: }
0710:
0711: /**
0712: * Checks that the given value is a signed short.
0713: *
0714: * @param value the value to be checked.
0715: * @param msg an message to be used in case of error.
0716: */
0717: static void checkSignedShort(final int value, final String msg) {
0718: if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
0719: throw new IllegalArgumentException(msg
0720: + " (must be a signed short): " + value);
0721: }
0722: }
0723:
0724: /**
0725: * Checks that the given value is an unsigned short.
0726: *
0727: * @param value the value to be checked.
0728: * @param msg an message to be used in case of error.
0729: */
0730: static void checkUnsignedShort(final int value, final String msg) {
0731: if (value < 0 || value > 65535) {
0732: throw new IllegalArgumentException(msg
0733: + " (must be an unsigned short): " + value);
0734: }
0735: }
0736:
0737: /**
0738: * Checks that the given value is an {@link Integer}, a{@link Float}, a
0739: * {@link Long}, a {@link Double} or a {@link String}.
0740: *
0741: * @param cst the value to be checked.
0742: */
0743: static void checkConstant(final Object cst) {
0744: if (!(cst instanceof Integer) && !(cst instanceof Float)
0745: && !(cst instanceof Long) && !(cst instanceof Double)
0746: && !(cst instanceof String)) {
0747: throw new IllegalArgumentException("Invalid constant: "
0748: + cst);
0749: }
0750: }
0751:
0752: /**
0753: * Checks that the given string is a valid Java identifier.
0754: *
0755: * @param name the string to be checked.
0756: * @param msg a message to be used in case of error.
0757: */
0758: static void checkIdentifier(final String name, final String msg) {
0759: checkIdentifier(name, 0, -1, msg);
0760: }
0761:
0762: /**
0763: * Checks that the given substring is a valid Java identifier.
0764: *
0765: * @param name the string to be checked.
0766: * @param start index of the first character of the identifier (inclusive).
0767: * @param end index of the last character of the identifier (exclusive). -1
0768: * is equivalent to <tt>name.length()</tt> if name is not
0769: * <tt>null</tt>.
0770: * @param msg a message to be used in case of error.
0771: */
0772: static void checkIdentifier(final String name, final int start,
0773: final int end, final String msg) {
0774: if (name == null
0775: || (end == -1 ? name.length() <= start : end <= start)) {
0776: throw new IllegalArgumentException("Invalid " + msg
0777: + " (must not be null or empty)");
0778: }
0779: if (!Character.isJavaIdentifierStart(name.charAt(start))) {
0780: throw new IllegalArgumentException("Invalid " + msg
0781: + " (must be a valid Java identifier): " + name);
0782: }
0783: int max = end == -1 ? name.length() : end;
0784: for (int i = start + 1; i < max; ++i) {
0785: if (!Character.isJavaIdentifierPart(name.charAt(i))) {
0786: throw new IllegalArgumentException("Invalid " + msg
0787: + " (must be a valid Java identifier): " + name);
0788: }
0789: }
0790: }
0791:
0792: /**
0793: * Checks that the given string is a valid Java identifier or is equal to
0794: * '<init>' or '<clinit>'.
0795: *
0796: * @param name the string to be checked.
0797: * @param msg a message to be used in case of error.
0798: */
0799: static void checkMethodIdentifier(final String name,
0800: final String msg) {
0801: if (name == null || name.length() == 0) {
0802: throw new IllegalArgumentException("Invalid " + msg
0803: + " (must not be null or empty)");
0804: }
0805: if (name.equals("<init>") || name.equals("<clinit>")) {
0806: return;
0807: }
0808: if (!Character.isJavaIdentifierStart(name.charAt(0))) {
0809: throw new IllegalArgumentException(
0810: "Invalid "
0811: + msg
0812: + " (must be a '<init>', '<clinit>' or a valid Java identifier): "
0813: + name);
0814: }
0815: for (int i = 1; i < name.length(); ++i) {
0816: if (!Character.isJavaIdentifierPart(name.charAt(i))) {
0817: throw new IllegalArgumentException(
0818: "Invalid "
0819: + msg
0820: + " (must be '<init>' or '<clinit>' or a valid Java identifier): "
0821: + name);
0822: }
0823: }
0824: }
0825:
0826: /**
0827: * Checks that the given string is a valid internal class name.
0828: *
0829: * @param name the string to be checked.
0830: * @param msg a message to be used in case of error.
0831: */
0832: static void checkInternalName(final String name, final String msg) {
0833: if (name == null || name.length() == 0) {
0834: throw new IllegalArgumentException("Invalid " + msg
0835: + " (must not be null or empty)");
0836: }
0837: if (name.charAt(0) == '[') {
0838: checkDesc(name, false);
0839: } else {
0840: checkInternalName(name, 0, -1, msg);
0841: }
0842: }
0843:
0844: /**
0845: * Checks that the given substring is a valid internal class name.
0846: *
0847: * @param name the string to be checked.
0848: * @param start index of the first character of the identifier (inclusive).
0849: * @param end index of the last character of the identifier (exclusive). -1
0850: * is equivalent to <tt>name.length()</tt> if name is not
0851: * <tt>null</tt>.
0852: * @param msg a message to be used in case of error.
0853: */
0854: static void checkInternalName(final String name, final int start,
0855: final int end, final String msg) {
0856: int max = end == -1 ? name.length() : end;
0857: try {
0858: int begin = start;
0859: int slash;
0860: do {
0861: slash = name.indexOf('/', begin + 1);
0862: if (slash == -1 || slash > max) {
0863: slash = max;
0864: }
0865: checkIdentifier(name, begin, slash, null);
0866: begin = slash + 1;
0867: } while (slash != max);
0868: } catch (IllegalArgumentException _) {
0869: throw new IllegalArgumentException(
0870: "Invalid "
0871: + msg
0872: + " (must be a fully qualified class name in internal form): "
0873: + name);
0874: }
0875: }
0876:
0877: /**
0878: * Checks that the given string is a valid type descriptor.
0879: *
0880: * @param desc the string to be checked.
0881: * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid.
0882: */
0883: static void checkDesc(final String desc, final boolean canBeVoid) {
0884: int end = checkDesc(desc, 0, canBeVoid);
0885: if (end != desc.length()) {
0886: throw new IllegalArgumentException("Invalid descriptor: "
0887: + desc);
0888: }
0889: }
0890:
0891: /**
0892: * Checks that a the given substring is a valid type descriptor.
0893: *
0894: * @param desc the string to be checked.
0895: * @param start index of the first character of the identifier (inclusive).
0896: * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid.
0897: * @return the index of the last character of the type decriptor, plus one.
0898: */
0899: static int checkDesc(final String desc, final int start,
0900: final boolean canBeVoid) {
0901: if (desc == null || start >= desc.length()) {
0902: throw new IllegalArgumentException(
0903: "Invalid type descriptor (must not be null or empty)");
0904: }
0905: int index;
0906: switch (desc.charAt(start)) {
0907: case 'V':
0908: if (canBeVoid) {
0909: return start + 1;
0910: } else {
0911: throw new IllegalArgumentException(
0912: "Invalid descriptor: " + desc);
0913: }
0914: case 'Z':
0915: case 'C':
0916: case 'B':
0917: case 'S':
0918: case 'I':
0919: case 'F':
0920: case 'J':
0921: case 'D':
0922: return start + 1;
0923: case '[':
0924: index = start + 1;
0925: while (index < desc.length() && desc.charAt(index) == '[') {
0926: ++index;
0927: }
0928: if (index < desc.length()) {
0929: return checkDesc(desc, index, false);
0930: } else {
0931: throw new IllegalArgumentException(
0932: "Invalid descriptor: " + desc);
0933: }
0934: case 'L':
0935: index = desc.indexOf(';', start);
0936: if (index == -1 || index - start < 2) {
0937: throw new IllegalArgumentException(
0938: "Invalid descriptor: " + desc);
0939: }
0940: try {
0941: checkInternalName(desc, start + 1, index, null);
0942: } catch (IllegalArgumentException _) {
0943: throw new IllegalArgumentException(
0944: "Invalid descriptor: " + desc);
0945: }
0946: return index + 1;
0947: default:
0948: throw new IllegalArgumentException("Invalid descriptor: "
0949: + desc);
0950: }
0951: }
0952:
0953: /**
0954: * Checks that the given string is a valid method descriptor.
0955: *
0956: * @param desc the string to be checked.
0957: */
0958: static void checkMethodDesc(final String desc) {
0959: if (desc == null || desc.length() == 0) {
0960: throw new IllegalArgumentException(
0961: "Invalid method descriptor (must not be null or empty)");
0962: }
0963: if (desc.charAt(0) != '(' || desc.length() < 3) {
0964: throw new IllegalArgumentException("Invalid descriptor: "
0965: + desc);
0966: }
0967: int start = 1;
0968: if (desc.charAt(start) != ')') {
0969: do {
0970: if (desc.charAt(start) == 'V') {
0971: throw new IllegalArgumentException(
0972: "Invalid descriptor: " + desc);
0973: }
0974: start = checkDesc(desc, start, false);
0975: } while (start < desc.length() && desc.charAt(start) != ')');
0976: }
0977: start = checkDesc(desc, start + 1, true);
0978: if (start != desc.length()) {
0979: throw new IllegalArgumentException("Invalid descriptor: "
0980: + desc);
0981: }
0982: }
0983:
0984: /**
0985: * Checks a class signature.
0986: *
0987: * @param signature a string containing the signature that must be checked.
0988: */
0989: static void checkClassSignature(final String signature) {
0990: // ClassSignature:
0991: // FormalTypeParameters? ClassTypeSignature ClassTypeSignature*
0992:
0993: int pos = 0;
0994: if (getChar(signature, 0) == '<') {
0995: pos = checkFormalTypeParameters(signature, pos);
0996: }
0997: pos = checkClassTypeSignature(signature, pos);
0998: while (getChar(signature, pos) == 'L') {
0999: pos = checkClassTypeSignature(signature, pos);
1000: }
1001: if (pos != signature.length()) {
1002: throw new IllegalArgumentException(signature
1003: + ": error at index " + pos);
1004: }
1005: }
1006:
1007: /**
1008: * Checks a method signature.
1009: *
1010: * @param signature a string containing the signature that must be checked.
1011: */
1012: static void checkMethodSignature(final String signature) {
1013: // MethodTypeSignature:
1014: // FormalTypeParameters? ( TypeSignature* ) ( TypeSignature | V ) (
1015: // ^ClassTypeSignature | ^TypeVariableSignature )*
1016:
1017: int pos = 0;
1018: if (getChar(signature, 0) == '<') {
1019: pos = checkFormalTypeParameters(signature, pos);
1020: }
1021: pos = checkChar('(', signature, pos);
1022: while ("ZCBSIFJDL[T".indexOf(getChar(signature, pos)) != -1) {
1023: pos = checkTypeSignature(signature, pos);
1024: }
1025: pos = checkChar(')', signature, pos);
1026: if (getChar(signature, pos) == 'V') {
1027: ++pos;
1028: } else {
1029: pos = checkTypeSignature(signature, pos);
1030: }
1031: while (getChar(signature, pos) == '^') {
1032: ++pos;
1033: if (getChar(signature, pos) == 'L') {
1034: pos = checkClassTypeSignature(signature, pos);
1035: } else {
1036: pos = checkTypeVariableSignature(signature, pos);
1037: }
1038: }
1039: if (pos != signature.length()) {
1040: throw new IllegalArgumentException(signature
1041: + ": error at index " + pos);
1042: }
1043: }
1044:
1045: /**
1046: * Checks a field signature.
1047: *
1048: * @param signature a string containing the signature that must be checked.
1049: */
1050: static void checkFieldSignature(final String signature) {
1051: int pos = checkFieldTypeSignature(signature, 0);
1052: if (pos != signature.length()) {
1053: throw new IllegalArgumentException(signature
1054: + ": error at index " + pos);
1055: }
1056: }
1057:
1058: /**
1059: * Checks the formal type parameters of a class or method signature.
1060: *
1061: * @param signature a string containing the signature that must be checked.
1062: * @param pos index of first character to be checked.
1063: * @return the index of the first character after the checked part.
1064: */
1065: private static int checkFormalTypeParameters(
1066: final String signature, int pos) {
1067: // FormalTypeParameters:
1068: // < FormalTypeParameter+ >
1069:
1070: pos = checkChar('<', signature, pos);
1071: pos = checkFormalTypeParameter(signature, pos);
1072: while (getChar(signature, pos) != '>') {
1073: pos = checkFormalTypeParameter(signature, pos);
1074: }
1075: return pos + 1;
1076: }
1077:
1078: /**
1079: * Checks a formal type parameter of a class or method signature.
1080: *
1081: * @param signature a string containing the signature that must be checked.
1082: * @param pos index of first character to be checked.
1083: * @return the index of the first character after the checked part.
1084: */
1085: private static int checkFormalTypeParameter(final String signature,
1086: int pos) {
1087: // FormalTypeParameter:
1088: // Identifier : FieldTypeSignature? (: FieldTypeSignature)*
1089:
1090: pos = checkIdentifier(signature, pos);
1091: pos = checkChar(':', signature, pos);
1092: if ("L[T".indexOf(getChar(signature, pos)) != -1) {
1093: pos = checkFieldTypeSignature(signature, pos);
1094: }
1095: while (getChar(signature, pos) == ':') {
1096: pos = checkFieldTypeSignature(signature, pos + 1);
1097: }
1098: return pos;
1099: }
1100:
1101: /**
1102: * Checks a field type signature.
1103: *
1104: * @param signature a string containing the signature that must be checked.
1105: * @param pos index of first character to be checked.
1106: * @return the index of the first character after the checked part.
1107: */
1108: private static int checkFieldTypeSignature(final String signature,
1109: int pos) {
1110: // FieldTypeSignature:
1111: // ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature
1112: //
1113: // ArrayTypeSignature:
1114: // [ TypeSignature
1115:
1116: switch (getChar(signature, pos)) {
1117: case 'L':
1118: return checkClassTypeSignature(signature, pos);
1119: case '[':
1120: return checkTypeSignature(signature, pos + 1);
1121: default:
1122: return checkTypeVariableSignature(signature, pos);
1123: }
1124: }
1125:
1126: /**
1127: * Checks a class type signature.
1128: *
1129: * @param signature a string containing the signature that must be checked.
1130: * @param pos index of first character to be checked.
1131: * @return the index of the first character after the checked part.
1132: */
1133: private static int checkClassTypeSignature(final String signature,
1134: int pos) {
1135: // ClassTypeSignature:
1136: // L Identifier ( / Identifier )* TypeArguments? ( . Identifier
1137: // TypeArguments? )* ;
1138:
1139: pos = checkChar('L', signature, pos);
1140: pos = checkIdentifier(signature, pos);
1141: while (getChar(signature, pos) == '/') {
1142: pos = checkIdentifier(signature, pos + 1);
1143: }
1144: if (getChar(signature, pos) == '<') {
1145: pos = checkTypeArguments(signature, pos);
1146: }
1147: while (getChar(signature, pos) == '.') {
1148: pos = checkIdentifier(signature, pos + 1);
1149: if (getChar(signature, pos) == '<') {
1150: pos = checkTypeArguments(signature, pos);
1151: }
1152: }
1153: return checkChar(';', signature, pos);
1154: }
1155:
1156: /**
1157: * Checks the type arguments in a class type signature.
1158: *
1159: * @param signature a string containing the signature that must be checked.
1160: * @param pos index of first character to be checked.
1161: * @return the index of the first character after the checked part.
1162: */
1163: private static int checkTypeArguments(final String signature,
1164: int pos) {
1165: // TypeArguments:
1166: // < TypeArgument+ >
1167:
1168: pos = checkChar('<', signature, pos);
1169: pos = checkTypeArgument(signature, pos);
1170: while (getChar(signature, pos) != '>') {
1171: pos = checkTypeArgument(signature, pos);
1172: }
1173: return pos + 1;
1174: }
1175:
1176: /**
1177: * Checks a type argument in a class type signature.
1178: *
1179: * @param signature a string containing the signature that must be checked.
1180: * @param pos index of first character to be checked.
1181: * @return the index of the first character after the checked part.
1182: */
1183: private static int checkTypeArgument(final String signature, int pos) {
1184: // TypeArgument:
1185: // * | ( ( + | - )? FieldTypeSignature )
1186:
1187: char c = getChar(signature, pos);
1188: if (c == '*') {
1189: return pos + 1;
1190: } else if (c == '+' || c == '-') {
1191: pos = pos + 1;
1192: }
1193: return checkFieldTypeSignature(signature, pos);
1194: }
1195:
1196: /**
1197: * Checks a type variable signature.
1198: *
1199: * @param signature a string containing the signature that must be checked.
1200: * @param pos index of first character to be checked.
1201: * @return the index of the first character after the checked part.
1202: */
1203: private static int checkTypeVariableSignature(
1204: final String signature, int pos) {
1205: // TypeVariableSignature:
1206: // T Identifier ;
1207:
1208: pos = checkChar('T', signature, pos);
1209: pos = checkIdentifier(signature, pos);
1210: return checkChar(';', signature, pos);
1211: }
1212:
1213: /**
1214: * Checks a type signature.
1215: *
1216: * @param signature a string containing the signature that must be checked.
1217: * @param pos index of first character to be checked.
1218: * @return the index of the first character after the checked part.
1219: */
1220: private static int checkTypeSignature(final String signature,
1221: int pos) {
1222: // TypeSignature:
1223: // Z | C | B | S | I | F | J | D | FieldTypeSignature
1224:
1225: switch (getChar(signature, pos)) {
1226: case 'Z':
1227: case 'C':
1228: case 'B':
1229: case 'S':
1230: case 'I':
1231: case 'F':
1232: case 'J':
1233: case 'D':
1234: return pos + 1;
1235: default:
1236: return checkFieldTypeSignature(signature, pos);
1237: }
1238: }
1239:
1240: /**
1241: * Checks an identifier.
1242: *
1243: * @param signature a string containing the signature that must be checked.
1244: * @param pos index of first character to be checked.
1245: * @return the index of the first character after the checked part.
1246: */
1247: private static int checkIdentifier(final String signature, int pos) {
1248: if (!Character.isJavaIdentifierStart(getChar(signature, pos))) {
1249: throw new IllegalArgumentException(signature
1250: + ": identifier expected at index " + pos);
1251: }
1252: ++pos;
1253: while (Character.isJavaIdentifierPart(getChar(signature, pos))) {
1254: ++pos;
1255: }
1256: return pos;
1257: }
1258:
1259: /**
1260: * Checks a single character.
1261: *
1262: * @param signature a string containing the signature that must be checked.
1263: * @param pos index of first character to be checked.
1264: * @return the index of the first character after the checked part.
1265: */
1266: private static int checkChar(final char c, final String signature,
1267: int pos) {
1268: if (getChar(signature, pos) == c) {
1269: return pos + 1;
1270: }
1271: throw new IllegalArgumentException(signature + ": '" + c
1272: + "' expected at index " + pos);
1273: }
1274:
1275: /**
1276: * Returns the signature car at the given index.
1277: *
1278: * @param signature a signature.
1279: * @param pos an index in signature.
1280: * @return the character at the given index, or 0 if there is no such
1281: * character.
1282: */
1283: private static char getChar(final String signature, int pos) {
1284: return pos < signature.length() ? signature.charAt(pos)
1285: : (char) 0;
1286: }
1287:
1288: /**
1289: * Checks that the given label is not null. This method can also check that
1290: * the label has been visited.
1291: *
1292: * @param label the label to be checked.
1293: * @param checkVisited <tt>true</tt> to check that the label has been
1294: * visited.
1295: * @param msg a message to be used in case of error.
1296: */
1297: void checkLabel(final Label label, final boolean checkVisited,
1298: final String msg) {
1299: if (label == null) {
1300: throw new IllegalArgumentException("Invalid " + msg
1301: + " (must not be null)");
1302: }
1303: if (checkVisited && labels.get(label) == null) {
1304: throw new IllegalArgumentException("Invalid " + msg
1305: + " (must be visited first)");
1306: }
1307: }
1308: }
|