Pass3aVerifier.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.bcel.verifier.statics;

  18. import java.util.Arrays;

  19. import org.apache.bcel.Const;
  20. import org.apache.bcel.Repository;
  21. import org.apache.bcel.classfile.Attribute;
  22. import org.apache.bcel.classfile.ClassFormatException;
  23. import org.apache.bcel.classfile.Code;
  24. import org.apache.bcel.classfile.CodeException;
  25. import org.apache.bcel.classfile.Constant;
  26. import org.apache.bcel.classfile.ConstantCP;
  27. import org.apache.bcel.classfile.ConstantClass;
  28. import org.apache.bcel.classfile.ConstantDouble;
  29. import org.apache.bcel.classfile.ConstantDynamic;
  30. import org.apache.bcel.classfile.ConstantFieldref;
  31. import org.apache.bcel.classfile.ConstantFloat;
  32. import org.apache.bcel.classfile.ConstantInteger;
  33. import org.apache.bcel.classfile.ConstantInterfaceMethodref;
  34. import org.apache.bcel.classfile.ConstantInvokeDynamic;
  35. import org.apache.bcel.classfile.ConstantLong;
  36. import org.apache.bcel.classfile.ConstantMethodref;
  37. import org.apache.bcel.classfile.ConstantNameAndType;
  38. import org.apache.bcel.classfile.ConstantString;
  39. import org.apache.bcel.classfile.ConstantUtf8;
  40. import org.apache.bcel.classfile.Field;
  41. import org.apache.bcel.classfile.JavaClass;
  42. import org.apache.bcel.classfile.LineNumber;
  43. import org.apache.bcel.classfile.LineNumberTable;
  44. import org.apache.bcel.classfile.LocalVariableTable;
  45. import org.apache.bcel.classfile.Method;
  46. import org.apache.bcel.generic.ALOAD;
  47. import org.apache.bcel.generic.ANEWARRAY;
  48. import org.apache.bcel.generic.ASTORE;
  49. import org.apache.bcel.generic.ATHROW;
  50. import org.apache.bcel.generic.ArrayType;
  51. import org.apache.bcel.generic.BREAKPOINT;
  52. import org.apache.bcel.generic.CHECKCAST;
  53. import org.apache.bcel.generic.ConstantPoolGen;
  54. import org.apache.bcel.generic.DLOAD;
  55. import org.apache.bcel.generic.DSTORE;
  56. import org.apache.bcel.generic.FLOAD;
  57. import org.apache.bcel.generic.FSTORE;
  58. import org.apache.bcel.generic.FieldInstruction;
  59. import org.apache.bcel.generic.GETSTATIC;
  60. import org.apache.bcel.generic.GotoInstruction;
  61. import org.apache.bcel.generic.IINC;
  62. import org.apache.bcel.generic.ILOAD;
  63. import org.apache.bcel.generic.IMPDEP1;
  64. import org.apache.bcel.generic.IMPDEP2;
  65. import org.apache.bcel.generic.INSTANCEOF;
  66. import org.apache.bcel.generic.INVOKEDYNAMIC;
  67. import org.apache.bcel.generic.INVOKEINTERFACE;
  68. import org.apache.bcel.generic.INVOKESPECIAL;
  69. import org.apache.bcel.generic.INVOKESTATIC;
  70. import org.apache.bcel.generic.INVOKEVIRTUAL;
  71. import org.apache.bcel.generic.ISTORE;
  72. import org.apache.bcel.generic.Instruction;
  73. import org.apache.bcel.generic.InstructionHandle;
  74. import org.apache.bcel.generic.InstructionList;
  75. import org.apache.bcel.generic.InvokeInstruction;
  76. import org.apache.bcel.generic.JsrInstruction;
  77. import org.apache.bcel.generic.LDC;
  78. import org.apache.bcel.generic.LDC2_W;
  79. import org.apache.bcel.generic.LLOAD;
  80. import org.apache.bcel.generic.LOOKUPSWITCH;
  81. import org.apache.bcel.generic.LSTORE;
  82. import org.apache.bcel.generic.LoadClass;
  83. import org.apache.bcel.generic.MULTIANEWARRAY;
  84. import org.apache.bcel.generic.NEW;
  85. import org.apache.bcel.generic.NEWARRAY;
  86. import org.apache.bcel.generic.ObjectType;
  87. import org.apache.bcel.generic.PUTSTATIC;
  88. import org.apache.bcel.generic.RET;
  89. import org.apache.bcel.generic.ReferenceType;
  90. import org.apache.bcel.generic.ReturnInstruction;
  91. import org.apache.bcel.generic.TABLESWITCH;
  92. import org.apache.bcel.generic.Type;
  93. import org.apache.bcel.verifier.PassVerifier;
  94. import org.apache.bcel.verifier.VerificationResult;
  95. import org.apache.bcel.verifier.Verifier;
  96. import org.apache.bcel.verifier.VerifierFactory;
  97. import org.apache.bcel.verifier.exc.AssertionViolatedException;
  98. import org.apache.bcel.verifier.exc.ClassConstraintException;
  99. import org.apache.bcel.verifier.exc.InvalidMethodException;
  100. import org.apache.bcel.verifier.exc.StaticCodeConstraintException;
  101. import org.apache.bcel.verifier.exc.StaticCodeInstructionConstraintException;
  102. import org.apache.bcel.verifier.exc.StaticCodeInstructionOperandConstraintException;

  103. /**
  104.  * This PassVerifier verifies a class file according to pass 3, static part as described in The Java Virtual Machine
  105.  * Specification, 2nd edition. More detailed information is to be found at the do_verify() method's documentation.
  106.  *
  107.  * @see #do_verify()
  108.  */
  109. public final class Pass3aVerifier extends PassVerifier {

  110.     /**
  111.      * This visitor class does the actual checking for the instruction operand's constraints.
  112.      */
  113.     private final class InstOperandConstraintVisitor extends org.apache.bcel.generic.EmptyVisitor {
  114.         /** The ConstantPoolGen instance this Visitor operates on. */
  115.         private final ConstantPoolGen constantPoolGen;

  116.         /** The only Constructor. */
  117.         InstOperandConstraintVisitor(final ConstantPoolGen constantPoolGen) {
  118.             this.constantPoolGen = constantPoolGen;
  119.         }

  120.         /**
  121.          * A utility method to always raise an exception.
  122.          */
  123.         private void constraintViolated(final Instruction i, final String message) {
  124.             throw new StaticCodeInstructionOperandConstraintException("Instruction " + tostring(i) + " constraint violated: " + message);
  125.         }

  126.         /**
  127.          * Looks for the method referenced by the given invoke instruction in the given class.
  128.          *
  129.          * @param jc the class that defines the referenced method
  130.          * @param invoke the instruction that references the method
  131.          * @return the referenced method or null if not found.
  132.          */
  133.         private Method getMethod(final JavaClass jc, final InvokeInstruction invoke) {
  134.             final Method[] ms = jc.getMethods();
  135.             for (final Method element : ms) {
  136.                 if (element.getName().equals(invoke.getMethodName(constantPoolGen))
  137.                     && Type.getReturnType(element.getSignature()).equals(invoke.getReturnType(constantPoolGen))
  138.                     && Arrays.equals(Type.getArgumentTypes(element.getSignature()), invoke.getArgumentTypes(constantPoolGen))) {
  139.                     return element;
  140.                 }
  141.             }

  142.             return null;
  143.         }

  144.         /**
  145.          * Looks for the method referenced by the given invoke instruction in the given class or its super classes and super
  146.          * interfaces.
  147.          *
  148.          * @param jc the class that defines the referenced method
  149.          * @param invoke the instruction that references the method
  150.          * @return the referenced method or null if not found.
  151.          */
  152.         private Method getMethodRecursive(final JavaClass jc, final InvokeInstruction invoke) throws ClassNotFoundException {
  153.             Method m;
  154.             // look in the given class
  155.             m = getMethod(jc, invoke);
  156.             if (m != null) {
  157.                 // method found in given class
  158.                 return m;
  159.             }
  160.             // method not found, look in super classes
  161.             for (final JavaClass superclass : jc.getSuperClasses()) {
  162.                 m = getMethod(superclass, invoke);
  163.                 if (m != null) {
  164.                     // method found in super class
  165.                     return m;
  166.                 }
  167.             }
  168.             // method not found, look in super interfaces
  169.             for (final JavaClass superclass : jc.getInterfaces()) {
  170.                 m = getMethod(superclass, invoke);
  171.                 if (m != null) {
  172.                     // method found in super interface
  173.                     return m;
  174.                 }
  175.             }
  176.             // method not found in the hierarchy
  177.             return null;
  178.         }

  179.         private ObjectType getObjectType(final FieldInstruction o) {
  180.             final ReferenceType rt = o.getReferenceType(constantPoolGen);
  181.             if (rt instanceof ObjectType) {
  182.                 return (ObjectType) rt;
  183.             }
  184.             constraintViolated(o, "expecting ObjectType but got " + rt);
  185.             return null;
  186.         }

  187.         // The target of each jump and branch instruction [...] must be the opcode [...]
  188.         // BCEL _DOES_ handle this.

  189.         // tableswitch: BCEL will do it, supposedly.

  190.         // lookupswitch: BCEL will do it, supposedly.

  191.         /**
  192.          * A utility method to raise an exception if the index is not a valid constant pool index.
  193.          */
  194.         private void indexValid(final Instruction i, final int idx) {
  195.             if (idx < 0 || idx >= constantPoolGen.getSize()) {
  196.                 constraintViolated(i, "Illegal constant pool index '" + idx + "'.");
  197.             }
  198.         }

  199.         /**
  200.          * Utility method to return the max_locals value of the method verified by the surrounding Pass3aVerifier instance.
  201.          */
  202.         private int maxLocals() {
  203.             try {
  204.                 return Repository.lookupClass(verifier.getClassName()).getMethods()[methodNo].getCode().getMaxLocals();
  205.             } catch (final ClassNotFoundException e) {
  206.                 // FIXME: maybe not the best way to handle this
  207.                 throw new AssertionViolatedException("Missing class: " + e, e);
  208.             }
  209.         }

  210.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  211.         @Override
  212.         public void visitALOAD(final ALOAD o) {
  213.             final int idx = o.getIndex();
  214.             if (idx < 0) {
  215.                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
  216.             } else {
  217.                 final int maxminus1 = maxLocals() - 1;
  218.                 if (idx > maxminus1) {
  219.                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
  220.                 }
  221.             }
  222.         }

  223.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  224.         @Override
  225.         public void visitANEWARRAY(final ANEWARRAY o) {
  226.             indexValid(o, o.getIndex());
  227.             final Constant c = constantPoolGen.getConstant(o.getIndex());
  228.             if (!(c instanceof ConstantClass)) {
  229.                 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
  230.             }
  231.             final Type t = o.getType(constantPoolGen);
  232.             if (t instanceof ArrayType) {
  233.                 final int dimensions = ((ArrayType) t).getDimensions();
  234.                 if (dimensions > Const.MAX_ARRAY_DIMENSIONS) {
  235.                     constraintViolated(o,
  236.                         "Not allowed to create an array with more than " + Const.MAX_ARRAY_DIMENSIONS + " dimensions;" + " actual: " + dimensions);
  237.                 }
  238.             }
  239.         }

  240.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  241.         @Override
  242.         public void visitASTORE(final ASTORE o) {
  243.             final int idx = o.getIndex();
  244.             if (idx < 0) {
  245.                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
  246.             } else {
  247.                 final int maxminus1 = maxLocals() - 1;
  248.                 if (idx > maxminus1) {
  249.                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
  250.                 }
  251.             }
  252.         }

  253.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  254.         @Override
  255.         public void visitCHECKCAST(final CHECKCAST o) {
  256.             indexValid(o, o.getIndex());
  257.             final Constant c = constantPoolGen.getConstant(o.getIndex());
  258.             if (!(c instanceof ConstantClass)) {
  259.                 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
  260.             }
  261.         }

  262.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  263.         @Override
  264.         public void visitDLOAD(final DLOAD o) {
  265.             final int idx = o.getIndex();
  266.             if (idx < 0) {
  267.                 constraintViolated(o, "Index '" + idx + "' must be non-negative."
  268.                     + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
  269.             } else {
  270.                 final int maxminus2 = maxLocals() - 2;
  271.                 if (idx > maxminus2) {
  272.                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
  273.                 }
  274.             }
  275.         }

  276.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  277.         @Override
  278.         public void visitDSTORE(final DSTORE o) {
  279.             final int idx = o.getIndex();
  280.             if (idx < 0) {
  281.                 constraintViolated(o, "Index '" + idx + "' must be non-negative."
  282.                     + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
  283.             } else {
  284.                 final int maxminus2 = maxLocals() - 2;
  285.                 if (idx > maxminus2) {
  286.                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
  287.                 }
  288.             }
  289.         }

  290.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  291.         // getfield, putfield, getstatic, putstatic
  292.         @Override
  293.         public void visitFieldInstruction(final FieldInstruction o) {
  294.             try {
  295.                 indexValid(o, o.getIndex());
  296.                 final Constant c = constantPoolGen.getConstant(o.getIndex());
  297.                 if (!(c instanceof ConstantFieldref)) {
  298.                     constraintViolated(o, "Indexing a constant that's not a CONSTANT_Fieldref but a '" + tostring(c) + "'.");
  299.                 }

  300.                 final String fieldName = o.getFieldName(constantPoolGen);

  301.                 final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName());
  302.                 final Field f = jc.findField(fieldName, o.getType(constantPoolGen));
  303.                 if (f == null) {
  304.                     constraintViolated(o, "Referenced field '" + fieldName + "' does not exist in class '" + jc.getClassName() + "'.");
  305.                 }
  306.             } catch (final ClassNotFoundException e) {
  307.                 // FIXME: maybe not the best way to handle this
  308.                 throw new AssertionViolatedException("Missing class: " + e, e);
  309.             }
  310.         }

  311.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  312.         @Override
  313.         public void visitFLOAD(final FLOAD o) {
  314.             final int idx = o.getIndex();
  315.             if (idx < 0) {
  316.                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
  317.             } else {
  318.                 final int maxminus1 = maxLocals() - 1;
  319.                 if (idx > maxminus1) {
  320.                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
  321.                 }
  322.             }
  323.         }

  324.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  325.         @Override
  326.         public void visitFSTORE(final FSTORE o) {
  327.             final int idx = o.getIndex();
  328.             if (idx < 0) {
  329.                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
  330.             } else {
  331.                 final int maxminus1 = maxLocals() - 1;
  332.                 if (idx > maxminus1) {
  333.                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
  334.                 }
  335.             }
  336.         }

  337.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  338.         @Override
  339.         public void visitGETSTATIC(final GETSTATIC o) {
  340.             try {
  341.                 final String fieldName = o.getFieldName(constantPoolGen);
  342.                 final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName());
  343.                 final Field f = jc.findField(fieldName, o.getType(constantPoolGen));
  344.                 if (f == null) {
  345.                     throw new AssertionViolatedException("Field '" + fieldName + "' not found in " + jc.getClassName());
  346.                 }

  347.                 if (!f.isStatic()) {
  348.                     constraintViolated(o, "Referenced field '" + f + "' is not static which it should be.");
  349.                 }
  350.             } catch (final ClassNotFoundException e) {
  351.                 // FIXME: maybe not the best way to handle this
  352.                 throw new AssertionViolatedException("Missing class: " + e, e);
  353.             }
  354.         }

  355.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  356.         @Override
  357.         public void visitIINC(final IINC o) {
  358.             final int idx = o.getIndex();
  359.             if (idx < 0) {
  360.                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
  361.             } else {
  362.                 final int maxminus1 = maxLocals() - 1;
  363.                 if (idx > maxminus1) {
  364.                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
  365.                 }
  366.             }
  367.         }

  368.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  369.         @Override
  370.         public void visitILOAD(final ILOAD o) {
  371.             final int idx = o.getIndex();
  372.             if (idx < 0) {
  373.                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
  374.             } else {
  375.                 final int maxminus1 = maxLocals() - 1;
  376.                 if (idx > maxminus1) {
  377.                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
  378.                 }
  379.             }
  380.         }

  381.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  382.         @Override
  383.         public void visitINSTANCEOF(final INSTANCEOF o) {
  384.             indexValid(o, o.getIndex());
  385.             final Constant c = constantPoolGen.getConstant(o.getIndex());
  386.             if (!(c instanceof ConstantClass)) {
  387.                 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
  388.             }
  389.         }

  390.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  391.         @Override
  392.         public void visitINVOKEDYNAMIC(final INVOKEDYNAMIC o) {
  393.             throw new UnsupportedOperationException("INVOKEDYNAMIC instruction is not supported at this time");
  394.         }

  395.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  396.         @Override
  397.         public void visitInvokeInstruction(final InvokeInstruction o) {
  398.             indexValid(o, o.getIndex());
  399.             if (o instanceof INVOKEVIRTUAL || o instanceof INVOKESPECIAL || o instanceof INVOKESTATIC) {
  400.                 final Constant c = constantPoolGen.getConstant(o.getIndex());
  401.                 if (!(c instanceof ConstantMethodref)) {
  402.                     constraintViolated(o, "Indexing a constant that's not a CONSTANT_Methodref but a '" + tostring(c) + "'.");
  403.                 } else {
  404.                     // Constants are okay due to pass2.
  405.                     final ConstantNameAndType cnat = (ConstantNameAndType) constantPoolGen.getConstant(((ConstantMethodref) c).getNameAndTypeIndex());
  406.                     final ConstantUtf8 cutf8 = (ConstantUtf8) constantPoolGen.getConstant(cnat.getNameIndex());
  407.                     if (cutf8.getBytes().equals(Const.CONSTRUCTOR_NAME) && !(o instanceof INVOKESPECIAL)) {
  408.                         constraintViolated(o, "Only INVOKESPECIAL is allowed to invoke instance initialization methods.");
  409.                     }
  410.                     if (!cutf8.getBytes().equals(Const.CONSTRUCTOR_NAME) && cutf8.getBytes().startsWith("<")) {
  411.                         constraintViolated(o, "No method with a name beginning with '<' other than the instance initialization methods"
  412.                             + " may be called by the method invocation instructions.");
  413.                     }
  414.                 }
  415.             } else {
  416.                 final Constant c = constantPoolGen.getConstant(o.getIndex());
  417.                 if (!(c instanceof ConstantInterfaceMethodref) && !(c instanceof ConstantInvokeDynamic)) {
  418.                     constraintViolated(o, "Indexing a constant that's not a CONSTANT_InterfaceMethodref/InvokeDynamic but a '" + tostring(c) + "'.");
  419.                 }
  420.                 // TODO: From time to time check if BCEL allows to detect if the
  421.                 // 'count' operand is consistent with the information in the
  422.                 // CONSTANT_InterfaceMethodref and if the last operand is zero.
  423.                 // By now, BCEL hides those two operands because they're superfluous.

  424.                 // Invoked method must not be <init> or <clinit>
  425.                 final ConstantNameAndType cnat = (ConstantNameAndType) constantPoolGen.getConstant(((ConstantCP) c).getNameAndTypeIndex());
  426.                 final String name = ((ConstantUtf8) constantPoolGen.getConstant(cnat.getNameIndex())).getBytes();
  427.                 if (name.equals(Const.CONSTRUCTOR_NAME)) {
  428.                     constraintViolated(o, "Method to invoke must not be '" + Const.CONSTRUCTOR_NAME + "'.");
  429.                 }
  430.                 if (name.equals(Const.STATIC_INITIALIZER_NAME)) {
  431.                     constraintViolated(o, "Method to invoke must not be '" + Const.STATIC_INITIALIZER_NAME + "'.");
  432.                 }
  433.             }

  434.             // The LoadClassType is the method-declaring class, so we have to check the other types.

  435.             Type t = o.getReturnType(constantPoolGen);
  436.             if (t instanceof ArrayType) {
  437.                 t = ((ArrayType) t).getBasicType();
  438.             }
  439.             if (t instanceof ObjectType) {
  440.                 final Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName());
  441.                 final VerificationResult vr = v.doPass2();
  442.                 if (vr.getStatus() != VerificationResult.VERIFIED_OK) {
  443.                     constraintViolated(o, "Return type class/interface could not be verified successfully: '" + vr.getMessage() + "'.");
  444.                 }
  445.             }

  446.             final Type[] ts = o.getArgumentTypes(constantPoolGen);
  447.             for (final Type element : ts) {
  448.                 t = element;
  449.                 if (t instanceof ArrayType) {
  450.                     t = ((ArrayType) t).getBasicType();
  451.                 }
  452.                 if (t instanceof ObjectType) {
  453.                     final Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName());
  454.                     final VerificationResult vr = v.doPass2();
  455.                     if (vr.getStatus() != VerificationResult.VERIFIED_OK) {
  456.                         constraintViolated(o, "Argument type class/interface could not be verified successfully: '" + vr.getMessage() + "'.");
  457.                     }
  458.                 }
  459.             }

  460.         }

  461.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  462.         @Override
  463.         public void visitINVOKEINTERFACE(final INVOKEINTERFACE o) {
  464.             try {
  465.                 // INVOKEINTERFACE is a LoadClass; the Class where the referenced method is declared in,
  466.                 // is therefore resolved/verified.
  467.                 // INVOKEINTERFACE is an InvokeInstruction, the argument and return types are resolved/verified,
  468.                 // too. So are the allowed method names.
  469.                 final String className = o.getClassName(constantPoolGen);
  470.                 final JavaClass jc = Repository.lookupClass(className);
  471.                 final Method m = getMethodRecursive(jc, o);
  472.                 if (m == null) {
  473.                     constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
  474.                         + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
  475.                 }
  476.                 if (jc.isClass()) {
  477.                     constraintViolated(o, "Referenced class '" + jc.getClassName() + "' is a class, but not an interface as expected.");
  478.                 }
  479.             } catch (final ClassNotFoundException e) {
  480.                 // FIXME: maybe not the best way to handle this
  481.                 throw new AssertionViolatedException("Missing class: " + e, e);
  482.             }
  483.         }

  484.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  485.         @Override
  486.         public void visitINVOKESPECIAL(final INVOKESPECIAL o) {
  487.             try {
  488.                 // INVOKESPECIAL is a LoadClass; the Class where the referenced method is declared in,
  489.                 // is therefore resolved/verified.
  490.                 // INVOKESPECIAL is an InvokeInstruction, the argument and return types are resolved/verified,
  491.                 // too. So are the allowed method names.
  492.                 final String className = o.getClassName(constantPoolGen);
  493.                 final JavaClass jc = Repository.lookupClass(className);
  494.                 final Method m = getMethodRecursive(jc, o);
  495.                 if (m == null) {
  496.                     constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
  497.                         + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
  498.                 }

  499.                 JavaClass current = Repository.lookupClass(verifier.getClassName());
  500.                 if (current.isSuper() && Repository.instanceOf(current, jc) && !current.equals(jc)
  501.                     && !o.getMethodName(constantPoolGen).equals(Const.CONSTRUCTOR_NAME)) {
  502.                     // Special lookup procedure for ACC_SUPER classes.

  503.                     int supidx = -1;

  504.                     Method meth = null;
  505.                     while (supidx != 0) {
  506.                         supidx = current.getSuperclassNameIndex();
  507.                         current = Repository.lookupClass(current.getSuperclassName());

  508.                         final Method[] meths = current.getMethods();
  509.                         for (final Method meth2 : meths) {
  510.                             if (meth2.getName().equals(o.getMethodName(constantPoolGen))
  511.                                 && Type.getReturnType(meth2.getSignature()).equals(o.getReturnType(constantPoolGen))
  512.                                 && Arrays.equals(Type.getArgumentTypes(meth2.getSignature()), o.getArgumentTypes(constantPoolGen))) {
  513.                                 meth = meth2;
  514.                                 break;
  515.                             }
  516.                         }
  517.                         if (meth != null) {
  518.                             break;
  519.                         }
  520.                     }
  521.                     if (meth == null) {
  522.                         constraintViolated(o, "ACC_SUPER special lookup procedure not successful: method '" + o.getMethodName(constantPoolGen)
  523.                             + "' with proper signature not declared in superclass hierarchy.");
  524.                     }
  525.                 }

  526.             } catch (final ClassNotFoundException e) {
  527.                 // FIXME: maybe not the best way to handle this
  528.                 throw new AssertionViolatedException("Missing class: " + e, e);
  529.             }

  530.         }

  531.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  532.         @Override
  533.         public void visitINVOKESTATIC(final INVOKESTATIC o) {
  534.             try {
  535.                 // INVOKESTATIC is a LoadClass; the Class where the referenced method is declared in,
  536.                 // is therefore resolved/verified.
  537.                 // INVOKESTATIC is an InvokeInstruction, the argument and return types are resolved/verified,
  538.                 // too. So are the allowed method names.
  539.                 final String className = o.getClassName(constantPoolGen);
  540.                 final JavaClass jc = Repository.lookupClass(className);
  541.                 final Method m = getMethodRecursive(jc, o);
  542.                 if (m == null) {
  543.                     constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
  544.                         + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
  545.                 } else if (!m.isStatic()) { // implies it's not abstract, verified in pass 2.
  546.                     constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' has ACC_STATIC unset.");
  547.                 }

  548.             } catch (final ClassNotFoundException e) {
  549.                 // FIXME: maybe not the best way to handle this
  550.                 throw new AssertionViolatedException("Missing class: " + e, e);
  551.             }
  552.         }

  553.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  554.         @Override
  555.         public void visitINVOKEVIRTUAL(final INVOKEVIRTUAL o) {
  556.             try {
  557.                 // INVOKEVIRTUAL is a LoadClass; the Class where the referenced method is declared in,
  558.                 // is therefore resolved/verified.
  559.                 // INVOKEVIRTUAL is an InvokeInstruction, the argument and return types are resolved/verified,
  560.                 // too. So are the allowed method names.
  561.                 final String className = o.getClassName(constantPoolGen);
  562.                 JavaClass jc;
  563.                 if (className.charAt(0) == '[') { // array type, e.g. invoke can be someArray.clone()
  564.                     jc = Repository.lookupClass("java.lang.Object");
  565.                 } else {
  566.                     jc = Repository.lookupClass(className);
  567.                 }
  568.                 final Method m = getMethodRecursive(jc, o);
  569.                 if (m == null) {
  570.                     constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
  571.                         + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
  572.                 }
  573.                 if (!jc.isClass()) {
  574.                     constraintViolated(o, "Referenced class '" + jc.getClassName() + "' is an interface, but not a class as expected.");
  575.                 }

  576.             } catch (final ClassNotFoundException e) {
  577.                 // FIXME: maybe not the best way to handle this
  578.                 // throw new AssertionViolatedException("Missing class: " + e, e);
  579.                 addMessage("Unable to verify INVOKEVITUAL as cannot load target class: " + e.getCause());
  580.             }
  581.         }

  582.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  583.         @Override
  584.         public void visitISTORE(final ISTORE o) {
  585.             final int idx = o.getIndex();
  586.             if (idx < 0) {
  587.                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
  588.             } else {
  589.                 final int maxminus1 = maxLocals() - 1;
  590.                 if (idx > maxminus1) {
  591.                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
  592.                 }
  593.             }
  594.         }

  595.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  596.         // LDC and LDC_W (LDC_W is a subclass of LDC in BCEL's model)
  597.         @Override
  598.         public void visitLDC(final LDC ldc) {
  599.             indexValid(ldc, ldc.getIndex());
  600.             final Constant c = constantPoolGen.getConstant(ldc.getIndex());
  601.             if (c instanceof ConstantClass) {
  602.                 addMessage("Operand of LDC or LDC_W is CONSTANT_Class '" + tostring(c) + "' - this is only supported in JDK 1.5 and higher.");
  603.             } else if (!(c instanceof ConstantInteger || c instanceof ConstantFloat || c instanceof ConstantString || c instanceof ConstantDynamic)) {
  604.                 constraintViolated(ldc,
  605.                     "Operand of LDC or LDC_W must be one of CONSTANT_Integer, CONSTANT_Float, CONSTANT_String or CONSTANT_Dynamic but is '"
  606.                             + tostring(c) + "'.");
  607.             }
  608.         }

  609.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  610.         // LDC2_W
  611.         @Override
  612.         public void visitLDC2_W(final LDC2_W o) {
  613.             indexValid(o, o.getIndex());
  614.             final Constant c = constantPoolGen.getConstant(o.getIndex());
  615.             if (!(c instanceof ConstantLong || c instanceof ConstantDouble)) {
  616.                 constraintViolated(o, "Operand of LDC2_W must be CONSTANT_Long or CONSTANT_Double, but is '" + tostring(c) + "'.");
  617.             }
  618.             try {
  619.                 indexValid(o, o.getIndex() + 1);
  620.             } catch (final StaticCodeInstructionOperandConstraintException e) {
  621.                 throw new AssertionViolatedException("Does not BCEL handle that? LDC2_W operand has a problem.", e);
  622.             }
  623.         }

  624.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  625.         @Override
  626.         public void visitLLOAD(final LLOAD o) {
  627.             final int idx = o.getIndex();
  628.             if (idx < 0) {
  629.                 constraintViolated(o, "Index '" + idx + "' must be non-negative."
  630.                     + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
  631.             } else {
  632.                 final int maxminus2 = maxLocals() - 2;
  633.                 if (idx > maxminus2) {
  634.                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
  635.                 }
  636.             }
  637.         }

  638.         ///////////////////////////////////////////////////////////
  639.         // The Java Virtual Machine Specification, pages 134-137 //
  640.         ///////////////////////////////////////////////////////////
  641.         /**
  642.          * Assures the generic preconditions of a LoadClass instance. The referenced class is loaded and pass2-verified.
  643.          */
  644.         @Override
  645.         public void visitLoadClass(final LoadClass loadClass) {
  646.             final ObjectType t = loadClass.getLoadClassType(constantPoolGen);
  647.             if (t != null) { // null means "no class is loaded"
  648.                 final Verifier v = VerifierFactory.getVerifier(t.getClassName());
  649.                 final VerificationResult vr = v.doPass1();
  650.                 if (vr.getStatus() != VerificationResult.VERIFIED_OK) {
  651.                     constraintViolated((Instruction) loadClass,
  652.                             "Class '" + loadClass.getLoadClassType(constantPoolGen).getClassName() + "' is referenced, but cannot be loaded: '" + vr + "'.");
  653.                 }
  654.             }
  655.         }

  656.         /* Checks if the constraints of operands of the said instruction(s) are satisfied. */
  657.         // public void visitPUTFIELD(PUTFIELD o) {
  658.         // for performance reasons done in Pass 3b
  659.         // }

  660.         /* Checks if the constraints of operands of the said instruction(s) are satisfied. */
  661.         // public void visitGETFIELD(GETFIELD o) {
  662.         // for performance reasons done in Pass 3b
  663.         // }

  664.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  665.         @Override
  666.         public void visitLOOKUPSWITCH(final LOOKUPSWITCH o) {
  667.             final int[] matchs = o.getMatchs();
  668.             int max = Integer.MIN_VALUE;
  669.             for (int i = 0; i < matchs.length; i++) {
  670.                 if (matchs[i] == max && i != 0) {
  671.                     constraintViolated(o, "Match '" + matchs[i] + "' occurs more than once.");
  672.                 }
  673.                 if (matchs[i] < max) {
  674.                     constraintViolated(o, "Lookup table must be sorted but isn't.");
  675.                 } else {
  676.                     max = matchs[i];
  677.                 }
  678.             }
  679.         }

  680.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  681.         @Override
  682.         public void visitLSTORE(final LSTORE o) {
  683.             final int idx = o.getIndex();
  684.             if (idx < 0) {
  685.                 constraintViolated(o, "Index '" + idx + "' must be non-negative."
  686.                     + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
  687.             } else {
  688.                 final int maxminus2 = maxLocals() - 2;
  689.                 if (idx > maxminus2) {
  690.                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
  691.                 }
  692.             }
  693.         }

  694.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  695.         @Override
  696.         public void visitMULTIANEWARRAY(final MULTIANEWARRAY o) {
  697.             indexValid(o, o.getIndex());
  698.             final Constant c = constantPoolGen.getConstant(o.getIndex());
  699.             if (!(c instanceof ConstantClass)) {
  700.                 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
  701.             }
  702.             final int dimensions2create = o.getDimensions();
  703.             if (dimensions2create < 1) {
  704.                 constraintViolated(o, "Number of dimensions to create must be greater than zero.");
  705.             }
  706.             final Type t = o.getType(constantPoolGen);
  707.             if (t instanceof ArrayType) {
  708.                 final int dimensions = ((ArrayType) t).getDimensions();
  709.                 if (dimensions < dimensions2create) {
  710.                     constraintViolated(o, "Not allowed to create array with more dimensions ('" + dimensions2create
  711.                         + "') than the one referenced by the CONSTANT_Class '" + t + "'.");
  712.                 }
  713.             } else {
  714.                 constraintViolated(o, "Expecting a CONSTANT_Class referencing an array type."
  715.                     + " [Constraint not found in The Java Virtual Machine Specification, Second Edition, 4.8.1]");
  716.             }
  717.         }

  718.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  719.         @Override
  720.         public void visitNEW(final NEW o) {
  721.             indexValid(o, o.getIndex());
  722.             final Constant c = constantPoolGen.getConstant(o.getIndex());
  723.             if (!(c instanceof ConstantClass)) {
  724.                 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
  725.             } else {
  726.                 final ConstantUtf8 cutf8 = (ConstantUtf8) constantPoolGen.getConstant(((ConstantClass) c).getNameIndex());
  727.                 final Type t = Type.getType("L" + cutf8.getBytes() + ";");
  728.                 if (t instanceof ArrayType) {
  729.                     constraintViolated(o, "NEW must not be used to create an array.");
  730.                 }
  731.             }

  732.         }

  733.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  734.         @Override
  735.         public void visitNEWARRAY(final NEWARRAY o) {
  736.             final byte t = o.getTypecode();
  737.             if (!(t == Const.T_BOOLEAN || t == Const.T_CHAR || t == Const.T_FLOAT || t == Const.T_DOUBLE || t == Const.T_BYTE || t == Const.T_SHORT
  738.                 || t == Const.T_INT || t == Const.T_LONG)) {
  739.                 constraintViolated(o, "Illegal type code '" + tostring(t) + "' for 'atype' operand.");
  740.             }
  741.         }

  742.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  743.         @Override
  744.         public void visitPUTSTATIC(final PUTSTATIC o) {
  745.             try {
  746.                 final String fieldName = o.getFieldName(constantPoolGen);
  747.                 final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName());
  748.                 final Field f = jc.findField(fieldName, o.getType(constantPoolGen));
  749.                 if (f == null) {
  750.                     throw new AssertionViolatedException("Field '" + fieldName + "' not found in " + jc.getClassName());
  751.                 }

  752.                 if (f.isFinal() && !verifier.getClassName().equals(getObjectType(o).getClassName())) {
  753.                     constraintViolated(o, "Referenced field '" + f + "' is final and must therefore be declared in the current class '"
  754.                             + verifier.getClassName() + "' which is not the case: it is declared in '" + o.getReferenceType(constantPoolGen) + "'.");
  755.                 }

  756.                 if (!f.isStatic()) {
  757.                     constraintViolated(o, "Referenced field '" + f + "' is not static which it should be.");
  758.                 }

  759.                 final String methName = Repository.lookupClass(verifier.getClassName()).getMethods()[methodNo].getName();

  760.                 // If it's an interface, it can be set only in <clinit>.
  761.                 if (!jc.isClass() && !methName.equals(Const.STATIC_INITIALIZER_NAME)) {
  762.                     constraintViolated(o, "Interface field '" + f + "' must be set in a '" + Const.STATIC_INITIALIZER_NAME + "' method.");
  763.                 }
  764.             } catch (final ClassNotFoundException e) {
  765.                 // FIXME: maybe not the best way to handle this
  766.                 throw new AssertionViolatedException("Missing class: " + e, e);
  767.             }
  768.         }

  769.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  770.         @Override
  771.         public void visitRET(final RET o) {
  772.             final int idx = o.getIndex();
  773.             if (idx < 0) {
  774.                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
  775.             } else {
  776.                 final int maxminus1 = maxLocals() - 1;
  777.                 if (idx > maxminus1) {
  778.                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
  779.                 }
  780.             }
  781.         }

  782.         // WIDE stuff is BCEL-internal and cannot be checked here.

  783.         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
  784.         @Override
  785.         public void visitTABLESWITCH(final TABLESWITCH o) {
  786.             // "high" must be >= "low". We cannot check this, as BCEL hides
  787.             // it from us.
  788.         }
  789.     }

  790.     /** A small utility method returning if a given int i is in the given int[] ints. */
  791.     private static boolean contains(final int[] ints, final int i) {
  792.         for (final int k : ints) {
  793.             if (k == i) {
  794.                 return true;
  795.             }
  796.         }
  797.         return false;
  798.     }

  799.     /** The Verifier that created this. */
  800.     private final Verifier verifier;

  801.     /**
  802.      * The method number to verify. This is the index in the array returned by JavaClass.getMethods().
  803.      */
  804.     private final int methodNo;

  805.     /**
  806.      * The one and only InstructionList object used by an instance of this class. It's here for performance reasons by
  807.      * do_verify() and its callees.
  808.      */
  809.     private InstructionList instructionList;

  810.     /**
  811.      * The one and only Code object used by an instance of this class. It's here for performance reasons by do_verify() and
  812.      * its callees.
  813.      */
  814.     private Code code;

  815.     /** Should only be instantiated by a Verifier. */
  816.     public Pass3aVerifier(final Verifier verifier, final int methodNo) {
  817.         this.verifier = verifier;
  818.         this.methodNo = methodNo;
  819.     }

  820.     /**
  821.      * These are the checks that could be done in pass 2 but are delayed to pass 3 for performance reasons. Also, these
  822.      * checks need access to the code array of the Code attribute of a Method so it's okay to perform them here. Also see
  823.      * the description of the do_verify() method.
  824.      *
  825.      * @throws ClassConstraintException if the verification fails.
  826.      * @see #do_verify()
  827.      */
  828.     private void delayedPass2Checks() {

  829.         final int[] instructionPositions = instructionList.getInstructionPositions();
  830.         final int codeLength = code.getCode().length;

  831.         /////////////////////
  832.         // LineNumberTable //
  833.         /////////////////////
  834.         final LineNumberTable lnt = code.getLineNumberTable();
  835.         if (lnt != null) {
  836.             final LineNumber[] lineNumbers = lnt.getLineNumberTable();
  837.             final IntList offsets = new IntList();
  838.             lineNumberLoop: for (final LineNumber lineNumber : lineNumbers) { // may appear in any order.
  839.                 for (final int instructionPosition : instructionPositions) {
  840.                     // TODO: Make this a binary search! The instructionPositions array is naturally ordered!
  841.                     final int offset = lineNumber.getStartPC();
  842.                     if (instructionPosition == offset) {
  843.                         if (offsets.contains(offset)) {
  844.                             addMessage("LineNumberTable attribute '" + code.getLineNumberTable() + "' refers to the same code offset ('" + offset
  845.                                 + "') more than once" + " which is violating the semantics [but is sometimes produced by IBM's 'jikes' compiler].");
  846.                         } else {
  847.                             offsets.add(offset);
  848.                         }
  849.                         continue lineNumberLoop;
  850.                     }
  851.                 }
  852.                 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has a LineNumberTable attribute '" + code.getLineNumberTable()
  853.                     + "' referring to a code offset ('" + lineNumber.getStartPC() + "') that does not exist.");
  854.             }
  855.         }

  856.         ///////////////////////////
  857.         // LocalVariableTable(s) //
  858.         ///////////////////////////
  859.         /*
  860.          * We cannot use code.getLocalVariableTable() because there could be more than only one. This is a bug in BCEL.
  861.          */
  862.         final Attribute[] atts = code.getAttributes();
  863.         for (final Attribute att : atts) {
  864.             if (att instanceof LocalVariableTable) {
  865.                 ((LocalVariableTable) att).forEach(localVariable -> {
  866.                     final int startpc = localVariable.getStartPC();
  867.                     final int length = localVariable.getLength();

  868.                     if (!contains(instructionPositions, startpc)) {
  869.                         throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has a LocalVariableTable attribute '"
  870.                             + code.getLocalVariableTable() + "' referring to a code offset ('" + startpc + "') that does not exist.");
  871.                     }
  872.                     if (!contains(instructionPositions, startpc + length) && startpc + length != codeLength) {
  873.                         throw new ClassConstraintException(
  874.                             "Code attribute '" + tostring(code) + "' has a LocalVariableTable attribute '" + code.getLocalVariableTable()
  875.                                 + "' referring to a code offset start_pc+length ('" + (startpc + length) + "') that does not exist.");
  876.                     }
  877.                 });
  878.             }
  879.         }

  880.         ////////////////////
  881.         // ExceptionTable //
  882.         ////////////////////
  883.         // In BCEL's "classfile" API, the startPC/endPC-notation is
  884.         // inclusive/exclusive as in the Java Virtual Machine Specification.
  885.         // WARNING: This is not true for BCEL's "generic" API.
  886.         final CodeException[] exceptionTable = code.getExceptionTable();
  887.         for (final CodeException element : exceptionTable) {
  888.             final int startpc = element.getStartPC();
  889.             final int endpc = element.getEndPC();
  890.             final int handlerpc = element.getHandlerPC();
  891.             if (startpc >= endpc) {
  892.                 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element
  893.                     + "' that has its start_pc ('" + startpc + "') not smaller than its end_pc ('" + endpc + "').");
  894.             }
  895.             if (!contains(instructionPositions, startpc)) {
  896.                 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element
  897.                     + "' that has a non-existant bytecode offset as its start_pc ('" + startpc + "').");
  898.             }
  899.             if (!contains(instructionPositions, endpc) && endpc != codeLength) {
  900.                 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element
  901.                     + "' that has a non-existant bytecode offset as its end_pc ('" + startpc + "') [that is also not equal to code_length ('" + codeLength
  902.                     + "')].");
  903.             }
  904.             if (!contains(instructionPositions, handlerpc)) {
  905.                 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element
  906.                     + "' that has a non-existant bytecode offset as its handler_pc ('" + handlerpc + "').");
  907.             }
  908.         }
  909.     }

  910.     /**
  911.      * Pass 3a is the verification of static constraints of JVM code (such as legal targets of branch instructions). This is
  912.      * the part of pass 3 where you do not need data flow analysis. JustIce also delays the checks for a correct exception
  913.      * table of a Code attribute and correct line number entries in a LineNumberTable attribute of a Code attribute (which
  914.      * conceptually belong to pass 2) to this pass. Also, most of the check for valid local variable entries in a
  915.      * LocalVariableTable attribute of a Code attribute is delayed until this pass. All these checks need access to the code
  916.      * array of the Code attribute.
  917.      *
  918.      * @throws InvalidMethodException if the method to verify does not exist.
  919.      */
  920.     @Override
  921.     public VerificationResult do_verify() {
  922.         try {
  923.             if (verifier.doPass2().equals(VerificationResult.VR_OK)) {
  924.                 // Okay, class file was loaded correctly by Pass 1
  925.                 // and satisfies static constraints of Pass 2.
  926.                 final JavaClass jc = Repository.lookupClass(verifier.getClassName());
  927.                 final Method[] methods = jc.getMethods();
  928.                 if (methodNo >= methods.length) {
  929.                     throw new InvalidMethodException("METHOD DOES NOT EXIST!");
  930.                 }
  931.                 final Method method = methods[methodNo];
  932.                 code = method.getCode();

  933.                 // No Code? Nothing to verify!
  934.                 if (method.isAbstract() || method.isNative()) { // IF mg HAS NO CODE (static constraint of Pass 2)
  935.                     return VerificationResult.VR_OK;
  936.                 }

  937.                 // TODO:
  938.                 // We want a very sophisticated code examination here with good explanations
  939.                 // on where to look for an illegal instruction or such.
  940.                 // Only after that we should try to build an InstructionList and throw an
  941.                 // AssertionViolatedException if after our examination InstructionList building
  942.                 // still fails.
  943.                 // That examination should be implemented in a byte-oriented way, i.e. look for
  944.                 // an instruction, make sure its validity, count its length, find the next
  945.                 // instruction and so on.
  946.                 try {
  947.                     instructionList = new InstructionList(method.getCode().getCode());
  948.                 } catch (final RuntimeException re) {
  949.                     return new VerificationResult(VerificationResult.VERIFIED_REJECTED,
  950.                         "Bad bytecode in the code array of the Code attribute of method '" + tostring(method) + "'.");
  951.                 }

  952.                 instructionList.setPositions(true);

  953.                 // Start verification.
  954.                 VerificationResult vr = VerificationResult.VR_OK; // default
  955.                 try {
  956.                     delayedPass2Checks();
  957.                 } catch (final ClassConstraintException | ClassFormatException cce) {
  958.                     return new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage());
  959.                 }
  960.                 try {
  961.                     pass3StaticInstructionChecks();
  962.                     pass3StaticInstructionOperandsChecks();
  963.                 } catch (final StaticCodeConstraintException | ClassFormatException scce) {
  964.                     vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, scce.getMessage());
  965.                 } catch (final ClassCastException cce) {
  966.                     vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Class Cast Exception: " + cce.getMessage());
  967.                 }
  968.                 return vr;
  969.             }
  970.             // did not pass Pass 2.
  971.             return VerificationResult.VR_NOTYET;
  972.         } catch (final ClassNotFoundException e) {
  973.             // FIXME: maybe not the best way to handle this
  974.             throw new AssertionViolatedException("Missing class: " + e, e);
  975.         }
  976.     }

  977.     /** Returns the method number as supplied when instantiating. */
  978.     public int getMethodNo() {
  979.         return methodNo;
  980.     }

  981.     /**
  982.      * These are the checks if constraints are satisfied which are described in the Java Virtual Machine Specification,
  983.      * Second Edition as Static Constraints on the instructions of Java Virtual Machine Code (chapter 4.8.1).
  984.      *
  985.      * @throws StaticCodeConstraintException if the verification fails.
  986.      */
  987.     private void pass3StaticInstructionChecks() {

  988.         // Code array must not be empty:
  989.         // Enforced in pass 2 (also stated in the static constraints of the Code
  990.         // array in vmspec2), together with pass 1 (reading code_length bytes and
  991.         // interpreting them as code[]). So this must not be checked again here.

  992.         if (code.getCode().length >= Const.MAX_CODE_SIZE) { // length must be LESS than the max
  993.             throw new StaticCodeInstructionConstraintException(
  994.                 "Code array in code attribute '" + tostring(code) + "' too big: must be smaller than " + Const.MAX_CODE_SIZE + "65536 bytes.");
  995.         }

  996.         // First opcode at offset 0: okay, that's clear. Nothing to do.

  997.         // Only instances of the instructions documented in Section 6.4 may appear in
  998.         // the code array.

  999.         // For BCEL's sake, we cannot handle WIDE stuff, but hopefully BCEL does its job right :)

  1000.         // The last byte of the last instruction in the code array must be the byte at index
  1001.         // code_length-1 : See the do_verify() comments. We actually don't iterate through the
  1002.         // byte array, but use an InstructionList so we cannot check for this. But BCEL does
  1003.         // things right, so it's implicitly okay.

  1004.         // TODO: Check how BCEL handles (and will handle) instructions like IMPDEP1, IMPDEP2,
  1005.         // BREAKPOINT... that BCEL knows about but which are illegal anyway.
  1006.         // We currently go the safe way here.
  1007.         InstructionHandle ih = instructionList.getStart();
  1008.         while (ih != null) {
  1009.             final Instruction i = ih.getInstruction();
  1010.             if (i instanceof IMPDEP1) {
  1011.                 throw new StaticCodeInstructionConstraintException("IMPDEP1 must not be in the code, it is an illegal instruction for _internal_ JVM use!");
  1012.             }
  1013.             if (i instanceof IMPDEP2) {
  1014.                 throw new StaticCodeInstructionConstraintException("IMPDEP2 must not be in the code, it is an illegal instruction for _internal_ JVM use!");
  1015.             }
  1016.             if (i instanceof BREAKPOINT) {
  1017.                 throw new StaticCodeInstructionConstraintException("BREAKPOINT must not be in the code, it is an illegal instruction for _internal_ JVM use!");
  1018.             }
  1019.             ih = ih.getNext();
  1020.         }

  1021.         // The original verifier seems to do this check here, too.
  1022.         // An unreachable last instruction may also not fall through the
  1023.         // end of the code, which is stupid -- but with the original
  1024.         // verifier's subroutine semantics one cannot predict reachability.
  1025.         final Instruction last = instructionList.getEnd().getInstruction();
  1026.         if (!(last instanceof ReturnInstruction || last instanceof RET || last instanceof GotoInstruction || last instanceof ATHROW)) {
  1027.             throw new StaticCodeInstructionConstraintException(
  1028.                 "Execution must not fall off the bottom of the code array." + " This constraint is enforced statically as some existing verifiers do"
  1029.                     + " - so it may be a false alarm if the last instruction is not reachable.");
  1030.         }
  1031.     }

  1032.     /**
  1033.      * These are the checks for the satisfaction of constraints which are described in the Java Virtual Machine
  1034.      * Specification, Second Edition as Static Constraints on the operands of instructions of Java Virtual Machine Code
  1035.      * (chapter 4.8.1). BCEL parses the code array to create an InstructionList and therefore has to check some of these
  1036.      * constraints. Additional checks are also implemented here.
  1037.      *
  1038.      * @throws StaticCodeConstraintException if the verification fails.
  1039.      */
  1040.     private void pass3StaticInstructionOperandsChecks() {
  1041.         try {
  1042.             // When building up the InstructionList, BCEL has already done all those checks
  1043.             // mentioned in The Java Virtual Machine Specification, Second Edition, as
  1044.             // "static constraints on the operands of instructions in the code array".
  1045.             // TODO: see the do_verify() comments. Maybe we should really work on the
  1046.             // byte array first to give more comprehensive messages.
  1047.             // TODO: Review Exception API, possibly build in some "offending instruction" thing
  1048.             // when we're ready to insulate the offending instruction by doing the
  1049.             // above thing.

  1050.             // TODO: Implement as much as possible here. BCEL does _not_ check everything.

  1051.             final ConstantPoolGen cpg = new ConstantPoolGen(Repository.lookupClass(verifier.getClassName()).getConstantPool());
  1052.             final InstOperandConstraintVisitor v = new InstOperandConstraintVisitor(cpg);

  1053.             // Checks for the things BCEL does _not_ handle itself.
  1054.             InstructionHandle ih = instructionList.getStart();
  1055.             while (ih != null) {
  1056.                 final Instruction i = ih.getInstruction();

  1057.                 // An "own" constraint, due to JustIce's new definition of what "subroutine" means.
  1058.                 if (i instanceof JsrInstruction) {
  1059.                     final InstructionHandle target = ((JsrInstruction) i).getTarget();
  1060.                     if (target == instructionList.getStart()) {
  1061.                         throw new StaticCodeInstructionOperandConstraintException(
  1062.                             "Due to JustIce's clear definition of subroutines, no JSR or JSR_W may have a top-level instruction"
  1063.                                 + " (such as the very first instruction, which is targeted by instruction '" + tostring(ih) + "' as its target.");
  1064.                     }
  1065.                     if (!(target.getInstruction() instanceof ASTORE)) {
  1066.                         throw new StaticCodeInstructionOperandConstraintException(
  1067.                             "Due to JustIce's clear definition of subroutines, no JSR or JSR_W may target anything else"
  1068.                                 + " than an ASTORE instruction. Instruction '" + tostring(ih) + "' targets '" + tostring(target) + "'.");
  1069.                     }
  1070.                 }

  1071.                 // vmspec2, page 134-137
  1072.                 ih.accept(v);

  1073.                 ih = ih.getNext();
  1074.             }

  1075.         } catch (final ClassNotFoundException e) {
  1076.             // FIXME: maybe not the best way to handle this
  1077.             throw new AssertionViolatedException("Missing class: " + e, e);
  1078.         }
  1079.     }

  1080.     /**
  1081.      * This method is a slightly modified version of verifier.statics.StringRepresentation.toString(final Node obj) that
  1082.      * accepts any Object, not just a Node.
  1083.      *
  1084.      * Returns the String representation of the Object obj; this is obj.toString() if it does not throw any
  1085.      * RuntimeException, or else it is a string derived only from obj's class name.
  1086.      */
  1087.     protected String tostring(final Object obj) {
  1088.         String ret;
  1089.         try {
  1090.             ret = obj.toString();
  1091.         }

  1092.         catch (final RuntimeException e) {
  1093.             // including ClassFormatException, trying to convert the "signature" of a ReturnaddressType LocalVariable
  1094.             // (shouldn't occur, but people do crazy things)
  1095.             String s = obj.getClass().getName();
  1096.             s = s.substring(s.lastIndexOf(".") + 1);
  1097.             ret = "<<" + s + ">>";
  1098.         }
  1099.         return ret;
  1100.     }
  1101. }