View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.bcel.verifier.statics;
20  
21  import java.util.Arrays;
22  
23  import org.apache.bcel.Const;
24  import org.apache.bcel.Repository;
25  import org.apache.bcel.classfile.Attribute;
26  import org.apache.bcel.classfile.ClassFormatException;
27  import org.apache.bcel.classfile.Code;
28  import org.apache.bcel.classfile.CodeException;
29  import org.apache.bcel.classfile.Constant;
30  import org.apache.bcel.classfile.ConstantCP;
31  import org.apache.bcel.classfile.ConstantClass;
32  import org.apache.bcel.classfile.ConstantDouble;
33  import org.apache.bcel.classfile.ConstantDynamic;
34  import org.apache.bcel.classfile.ConstantFieldref;
35  import org.apache.bcel.classfile.ConstantFloat;
36  import org.apache.bcel.classfile.ConstantInteger;
37  import org.apache.bcel.classfile.ConstantInterfaceMethodref;
38  import org.apache.bcel.classfile.ConstantInvokeDynamic;
39  import org.apache.bcel.classfile.ConstantLong;
40  import org.apache.bcel.classfile.ConstantMethodref;
41  import org.apache.bcel.classfile.ConstantNameAndType;
42  import org.apache.bcel.classfile.ConstantString;
43  import org.apache.bcel.classfile.ConstantUtf8;
44  import org.apache.bcel.classfile.Field;
45  import org.apache.bcel.classfile.JavaClass;
46  import org.apache.bcel.classfile.LineNumber;
47  import org.apache.bcel.classfile.LineNumberTable;
48  import org.apache.bcel.classfile.LocalVariableTable;
49  import org.apache.bcel.classfile.Method;
50  import org.apache.bcel.generic.ALOAD;
51  import org.apache.bcel.generic.ANEWARRAY;
52  import org.apache.bcel.generic.ASTORE;
53  import org.apache.bcel.generic.ATHROW;
54  import org.apache.bcel.generic.ArrayType;
55  import org.apache.bcel.generic.BREAKPOINT;
56  import org.apache.bcel.generic.CHECKCAST;
57  import org.apache.bcel.generic.ConstantPoolGen;
58  import org.apache.bcel.generic.DLOAD;
59  import org.apache.bcel.generic.DSTORE;
60  import org.apache.bcel.generic.FLOAD;
61  import org.apache.bcel.generic.FSTORE;
62  import org.apache.bcel.generic.FieldInstruction;
63  import org.apache.bcel.generic.GETSTATIC;
64  import org.apache.bcel.generic.GotoInstruction;
65  import org.apache.bcel.generic.IINC;
66  import org.apache.bcel.generic.ILOAD;
67  import org.apache.bcel.generic.IMPDEP1;
68  import org.apache.bcel.generic.IMPDEP2;
69  import org.apache.bcel.generic.INSTANCEOF;
70  import org.apache.bcel.generic.INVOKEDYNAMIC;
71  import org.apache.bcel.generic.INVOKEINTERFACE;
72  import org.apache.bcel.generic.INVOKESPECIAL;
73  import org.apache.bcel.generic.INVOKESTATIC;
74  import org.apache.bcel.generic.INVOKEVIRTUAL;
75  import org.apache.bcel.generic.ISTORE;
76  import org.apache.bcel.generic.Instruction;
77  import org.apache.bcel.generic.InstructionHandle;
78  import org.apache.bcel.generic.InstructionList;
79  import org.apache.bcel.generic.InvokeInstruction;
80  import org.apache.bcel.generic.JsrInstruction;
81  import org.apache.bcel.generic.LDC;
82  import org.apache.bcel.generic.LDC2_W;
83  import org.apache.bcel.generic.LLOAD;
84  import org.apache.bcel.generic.LOOKUPSWITCH;
85  import org.apache.bcel.generic.LSTORE;
86  import org.apache.bcel.generic.LoadClass;
87  import org.apache.bcel.generic.MULTIANEWARRAY;
88  import org.apache.bcel.generic.NEW;
89  import org.apache.bcel.generic.NEWARRAY;
90  import org.apache.bcel.generic.ObjectType;
91  import org.apache.bcel.generic.PUTSTATIC;
92  import org.apache.bcel.generic.RET;
93  import org.apache.bcel.generic.ReferenceType;
94  import org.apache.bcel.generic.ReturnInstruction;
95  import org.apache.bcel.generic.TABLESWITCH;
96  import org.apache.bcel.generic.Type;
97  import org.apache.bcel.verifier.PassVerifier;
98  import org.apache.bcel.verifier.VerificationResult;
99  import org.apache.bcel.verifier.Verifier;
100 import org.apache.bcel.verifier.VerifierFactory;
101 import org.apache.bcel.verifier.exc.AssertionViolatedException;
102 import org.apache.bcel.verifier.exc.ClassConstraintException;
103 import org.apache.bcel.verifier.exc.InvalidMethodException;
104 import org.apache.bcel.verifier.exc.StaticCodeConstraintException;
105 import org.apache.bcel.verifier.exc.StaticCodeInstructionConstraintException;
106 import org.apache.bcel.verifier.exc.StaticCodeInstructionOperandConstraintException;
107 
108 /**
109  * This PassVerifier verifies a class file according to pass 3, static part as described in The Java Virtual Machine
110  * Specification, 2nd edition. More detailed information is to be found at the do_verify() method's documentation.
111  *
112  * @see #do_verify()
113  */
114 public final class Pass3aVerifier extends PassVerifier {
115 
116     /**
117      * This visitor class does the actual checking for the instruction operand's constraints.
118      */
119     private final class InstOperandConstraintVisitor extends org.apache.bcel.generic.EmptyVisitor {
120         /** The ConstantPoolGen instance this Visitor operates on. */
121         private final ConstantPoolGen constantPoolGen;
122 
123         /**
124          * Constructs a new instance.
125          */
126         InstOperandConstraintVisitor(final ConstantPoolGen constantPoolGen) {
127             this.constantPoolGen = constantPoolGen;
128         }
129 
130         /**
131          * A utility method to always raise an exception.
132          */
133         private void constraintViolated(final Instruction i, final String message) {
134             throw new StaticCodeInstructionOperandConstraintException("Instruction " + tostring(i) + " constraint violated: " + message);
135         }
136 
137         /**
138          * Looks for the method referenced by the given invoke instruction in the given class.
139          *
140          * @param jc the class that defines the referenced method
141          * @param invoke the instruction that references the method
142          * @return the referenced method or null if not found.
143          */
144         private Method getMethod(final JavaClass jc, final InvokeInstruction invoke) {
145             final Method[] ms = jc.getMethods();
146             for (final Method element : ms) {
147                 if (element.getName().equals(invoke.getMethodName(constantPoolGen))
148                     && Type.getReturnType(element.getSignature()).equals(invoke.getReturnType(constantPoolGen))
149                     && Arrays.equals(Type.getArgumentTypes(element.getSignature()), invoke.getArgumentTypes(constantPoolGen))) {
150                     return element;
151                 }
152             }
153 
154             return null;
155         }
156 
157         /**
158          * Looks for the method referenced by the given invoke instruction in the given class or its super classes and super
159          * interfaces.
160          *
161          * @param jc the class that defines the referenced method
162          * @param invoke the instruction that references the method
163          * @return the referenced method or null if not found.
164          */
165         private Method getMethodRecursive(final JavaClass jc, final InvokeInstruction invoke) throws ClassNotFoundException {
166             Method m;
167             // look in the given class
168             m = getMethod(jc, invoke);
169             if (m != null) {
170                 // method found in given class
171                 return m;
172             }
173             // method not found, look in super classes
174             for (final JavaClass superclass : jc.getSuperClasses()) {
175                 m = getMethod(superclass, invoke);
176                 if (m != null) {
177                     // method found in super class
178                     return m;
179                 }
180             }
181             // method not found, look in super interfaces
182             for (final JavaClass superclass : jc.getInterfaces()) {
183                 m = getMethod(superclass, invoke);
184                 if (m != null) {
185                     // method found in super interface
186                     return m;
187                 }
188             }
189             // method not found in the hierarchy
190             return null;
191         }
192 
193         private ObjectType getObjectType(final FieldInstruction o) {
194             final ReferenceType rt = o.getReferenceType(constantPoolGen);
195             if (rt instanceof ObjectType) {
196                 return (ObjectType) rt;
197             }
198             constraintViolated(o, "expecting ObjectType but got " + rt);
199             return null;
200         }
201 
202         // The target of each jump and branch instruction [...] must be the opcode [...]
203         // BCEL _DOES_ handle this.
204 
205         // tableswitch: BCEL will do it, supposedly.
206 
207         // lookupswitch: BCEL will do it, supposedly.
208 
209         /**
210          * A utility method to raise an exception if the index is not a valid constant pool index.
211          */
212         private void indexValid(final Instruction i, final int idx) {
213             if (idx < 0 || idx >= constantPoolGen.getSize()) {
214                 constraintViolated(i, "Illegal constant pool index '" + idx + "'.");
215             }
216         }
217 
218         /**
219          * Utility method to return the max_locals value of the method verified by the surrounding Pass3aVerifier instance.
220          */
221         private int maxLocals() {
222             try {
223                 return Repository.lookupClass(verifier.getClassName()).getMethods()[methodNo].getCode().getMaxLocals();
224             } catch (final ClassNotFoundException e) {
225                 // FIXME: maybe not the best way to handle this
226                 throw new AssertionViolatedException("Missing class: " + e, e);
227             }
228         }
229 
230         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
231         @Override
232         public void visitALOAD(final ALOAD o) {
233             final int idx = o.getIndex();
234             if (idx < 0) {
235                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
236             } else {
237                 final int maxminus1 = maxLocals() - 1;
238                 if (idx > maxminus1) {
239                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
240                 }
241             }
242         }
243 
244         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
245         @Override
246         public void visitANEWARRAY(final ANEWARRAY o) {
247             indexValid(o, o.getIndex());
248             final Constant c = constantPoolGen.getConstant(o.getIndex());
249             if (!(c instanceof ConstantClass)) {
250                 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
251             }
252             final Type t = o.getType(constantPoolGen);
253             if (t instanceof ArrayType) {
254                 final int dimensions = ((ArrayType) t).getDimensions();
255                 if (dimensions > Const.MAX_ARRAY_DIMENSIONS) {
256                     constraintViolated(o,
257                         "Not allowed to create an array with more than " + Const.MAX_ARRAY_DIMENSIONS + " dimensions; actual: " + dimensions);
258                 }
259             }
260         }
261 
262         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
263         @Override
264         public void visitASTORE(final ASTORE o) {
265             final int idx = o.getIndex();
266             if (idx < 0) {
267                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
268             } else {
269                 final int maxminus1 = maxLocals() - 1;
270                 if (idx > maxminus1) {
271                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
272                 }
273             }
274         }
275 
276         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
277         @Override
278         public void visitCHECKCAST(final CHECKCAST o) {
279             indexValid(o, o.getIndex());
280             final Constant c = constantPoolGen.getConstant(o.getIndex());
281             if (!(c instanceof ConstantClass)) {
282                 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
283             }
284         }
285 
286         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
287         @Override
288         public void visitDLOAD(final DLOAD o) {
289             final int idx = o.getIndex();
290             if (idx < 0) {
291                 constraintViolated(o, "Index '" + idx + "' must be non-negative."
292                     + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
293             } else {
294                 final int maxminus2 = maxLocals() - 2;
295                 if (idx > maxminus2) {
296                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
297                 }
298             }
299         }
300 
301         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
302         @Override
303         public void visitDSTORE(final DSTORE o) {
304             final int idx = o.getIndex();
305             if (idx < 0) {
306                 constraintViolated(o, "Index '" + idx + "' must be non-negative."
307                     + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
308             } else {
309                 final int maxminus2 = maxLocals() - 2;
310                 if (idx > maxminus2) {
311                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
312                 }
313             }
314         }
315 
316         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
317         // getfield, putfield, getstatic, putstatic
318         @Override
319         public void visitFieldInstruction(final FieldInstruction o) {
320             try {
321                 indexValid(o, o.getIndex());
322                 final Constant c = constantPoolGen.getConstant(o.getIndex());
323                 if (!(c instanceof ConstantFieldref)) {
324                     constraintViolated(o, "Indexing a constant that's not a CONSTANT_Fieldref but a '" + tostring(c) + "'.");
325                 }
326 
327                 final String fieldName = o.getFieldName(constantPoolGen);
328 
329                 final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName());
330                 final Field f = jc.findField(fieldName, o.getType(constantPoolGen));
331                 if (f == null) {
332                     constraintViolated(o, "Referenced field '" + fieldName + "' does not exist in class '" + jc.getClassName() + "'.");
333                 }
334             } catch (final ClassNotFoundException e) {
335                 // FIXME: maybe not the best way to handle this
336                 throw new AssertionViolatedException("Missing class: " + e, e);
337             }
338         }
339 
340         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
341         @Override
342         public void visitFLOAD(final FLOAD o) {
343             final int idx = o.getIndex();
344             if (idx < 0) {
345                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
346             } else {
347                 final int maxminus1 = maxLocals() - 1;
348                 if (idx > maxminus1) {
349                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
350                 }
351             }
352         }
353 
354         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
355         @Override
356         public void visitFSTORE(final FSTORE o) {
357             final int idx = o.getIndex();
358             if (idx < 0) {
359                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
360             } else {
361                 final int maxminus1 = maxLocals() - 1;
362                 if (idx > maxminus1) {
363                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
364                 }
365             }
366         }
367 
368         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
369         @Override
370         public void visitGETSTATIC(final GETSTATIC o) {
371             try {
372                 final String fieldName = o.getFieldName(constantPoolGen);
373                 final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName());
374                 final Field f = jc.findField(fieldName, o.getType(constantPoolGen));
375                 if (f == null) {
376                     throw new AssertionViolatedException("Field '" + fieldName + "' not found in " + jc.getClassName());
377                 }
378 
379                 if (!f.isStatic()) {
380                     constraintViolated(o, "Referenced field '" + f + "' is not static which it should be.");
381                 }
382             } catch (final ClassNotFoundException e) {
383                 // FIXME: maybe not the best way to handle this
384                 throw new AssertionViolatedException("Missing class: " + e, e);
385             }
386         }
387 
388         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
389         @Override
390         public void visitIINC(final IINC o) {
391             final int idx = o.getIndex();
392             if (idx < 0) {
393                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
394             } else {
395                 final int maxminus1 = maxLocals() - 1;
396                 if (idx > maxminus1) {
397                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
398                 }
399             }
400         }
401 
402         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
403         @Override
404         public void visitILOAD(final ILOAD o) {
405             final int idx = o.getIndex();
406             if (idx < 0) {
407                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
408             } else {
409                 final int maxminus1 = maxLocals() - 1;
410                 if (idx > maxminus1) {
411                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
412                 }
413             }
414         }
415 
416         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
417         @Override
418         public void visitINSTANCEOF(final INSTANCEOF o) {
419             indexValid(o, o.getIndex());
420             final Constant c = constantPoolGen.getConstant(o.getIndex());
421             if (!(c instanceof ConstantClass)) {
422                 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
423             }
424         }
425 
426         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
427         @Override
428         public void visitINVOKEDYNAMIC(final INVOKEDYNAMIC o) {
429             throw new UnsupportedOperationException("INVOKEDYNAMIC instruction is not supported at this time");
430         }
431 
432         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
433         @Override
434         public void visitInvokeInstruction(final InvokeInstruction o) {
435             indexValid(o, o.getIndex());
436             if (o instanceof INVOKEVIRTUAL || o instanceof INVOKESPECIAL || o instanceof INVOKESTATIC) {
437                 final Constant c = constantPoolGen.getConstant(o.getIndex());
438                 if (!(c instanceof ConstantMethodref)) {
439                     constraintViolated(o, "Indexing a constant that's not a CONSTANT_Methodref but a '" + tostring(c) + "'.");
440                 } else {
441                     // Constants are okay due to pass2.
442                     final ConstantNameAndType cnat = (ConstantNameAndType) constantPoolGen.getConstant(((ConstantMethodref) c).getNameAndTypeIndex());
443                     final ConstantUtf8 cutf8 = (ConstantUtf8) constantPoolGen.getConstant(cnat.getNameIndex());
444                     if (cutf8.getBytes().equals(Const.CONSTRUCTOR_NAME) && !(o instanceof INVOKESPECIAL)) {
445                         constraintViolated(o, "Only INVOKESPECIAL is allowed to invoke instance initialization methods.");
446                     }
447                     if (!cutf8.getBytes().equals(Const.CONSTRUCTOR_NAME) && cutf8.getBytes().startsWith("<")) {
448                         constraintViolated(o, "No method with a name beginning with '<' other than the instance initialization methods"
449                             + " may be called by the method invocation instructions.");
450                     }
451                 }
452             } else {
453                 final Constant c = constantPoolGen.getConstant(o.getIndex());
454                 if (!(c instanceof ConstantInterfaceMethodref) && !(c instanceof ConstantInvokeDynamic)) {
455                     constraintViolated(o, "Indexing a constant that's not a CONSTANT_InterfaceMethodref/InvokeDynamic but a '" + tostring(c) + "'.");
456                 }
457                 // TODO: From time to time check if BCEL allows to detect if the
458                 // 'count' operand is consistent with the information in the
459                 // CONSTANT_InterfaceMethodref and if the last operand is zero.
460                 // By now, BCEL hides those two operands because they're superfluous.
461 
462                 // Invoked method must not be <init> or <clinit>
463                 final ConstantNameAndType cnat = (ConstantNameAndType) constantPoolGen.getConstant(((ConstantCP) c).getNameAndTypeIndex());
464                 final String name = ((ConstantUtf8) constantPoolGen.getConstant(cnat.getNameIndex())).getBytes();
465                 if (name.equals(Const.CONSTRUCTOR_NAME)) {
466                     constraintViolated(o, "Method to invoke must not be '" + Const.CONSTRUCTOR_NAME + "'.");
467                 }
468                 if (name.equals(Const.STATIC_INITIALIZER_NAME)) {
469                     constraintViolated(o, "Method to invoke must not be '" + Const.STATIC_INITIALIZER_NAME + "'.");
470                 }
471             }
472 
473             // The LoadClassType is the method-declaring class, so we have to check the other types.
474 
475             Type t = o.getReturnType(constantPoolGen);
476             if (t instanceof ArrayType) {
477                 t = ((ArrayType) t).getBasicType();
478             }
479             if (t instanceof ObjectType) {
480                 final Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName());
481                 final VerificationResult vr = v.doPass2();
482                 if (vr.getStatus() != VerificationResult.VERIFIED_OK) {
483                     constraintViolated(o, "Return type class/interface could not be verified successfully: '" + vr.getMessage() + "'.");
484                 }
485             }
486 
487             final Type[] ts = o.getArgumentTypes(constantPoolGen);
488             for (final Type element : ts) {
489                 t = element;
490                 if (t instanceof ArrayType) {
491                     t = ((ArrayType) t).getBasicType();
492                 }
493                 if (t instanceof ObjectType) {
494                     final Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName());
495                     final VerificationResult vr = v.doPass2();
496                     if (vr.getStatus() != VerificationResult.VERIFIED_OK) {
497                         constraintViolated(o, "Argument type class/interface could not be verified successfully: '" + vr.getMessage() + "'.");
498                     }
499                 }
500             }
501 
502         }
503 
504         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
505         @Override
506         public void visitINVOKEINTERFACE(final INVOKEINTERFACE o) {
507             try {
508                 // INVOKEINTERFACE is a LoadClass; the Class where the referenced method is declared in,
509                 // is therefore resolved/verified.
510                 // INVOKEINTERFACE is an InvokeInstruction, the argument and return types are resolved/verified,
511                 // too. So are the allowed method names.
512                 final String className = o.getClassName(constantPoolGen);
513                 final JavaClass jc = Repository.lookupClass(className);
514                 final Method m = getMethodRecursive(jc, o);
515                 if (m == null) {
516                     constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
517                         + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
518                 }
519                 if (jc.isClass()) {
520                     constraintViolated(o, "Referenced class '" + jc.getClassName() + "' is a class, but not an interface as expected.");
521                 }
522             } catch (final ClassNotFoundException e) {
523                 // FIXME: maybe not the best way to handle this
524                 throw new AssertionViolatedException("Missing class: " + e, e);
525             }
526         }
527 
528         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
529         @Override
530         public void visitINVOKESPECIAL(final INVOKESPECIAL o) {
531             try {
532                 // INVOKESPECIAL is a LoadClass; the Class where the referenced method is declared in,
533                 // is therefore resolved/verified.
534                 // INVOKESPECIAL is an InvokeInstruction, the argument and return types are resolved/verified,
535                 // too. So are the allowed method names.
536                 final String className = o.getClassName(constantPoolGen);
537                 final JavaClass jc = Repository.lookupClass(className);
538                 final Method m = getMethodRecursive(jc, o);
539                 if (m == null) {
540                     constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
541                         + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
542                 }
543 
544                 JavaClass current = Repository.lookupClass(verifier.getClassName());
545                 if (current.isSuper() && Repository.instanceOf(current, jc) && !current.equals(jc)
546                     && !o.getMethodName(constantPoolGen).equals(Const.CONSTRUCTOR_NAME)) {
547                     // Special lookup procedure for ACC_SUPER classes.
548 
549                     int supidx = -1;
550 
551                     Method meth = null;
552                     while (supidx != 0) {
553                         supidx = current.getSuperclassNameIndex();
554                         current = Repository.lookupClass(current.getSuperclassName());
555 
556                         final Method[] meths = current.getMethods();
557                         for (final Method meth2 : meths) {
558                             if (meth2.getName().equals(o.getMethodName(constantPoolGen))
559                                 && Type.getReturnType(meth2.getSignature()).equals(o.getReturnType(constantPoolGen))
560                                 && Arrays.equals(Type.getArgumentTypes(meth2.getSignature()), o.getArgumentTypes(constantPoolGen))) {
561                                 meth = meth2;
562                                 break;
563                             }
564                         }
565                         if (meth != null) {
566                             break;
567                         }
568                     }
569                     if (meth == null) {
570                         constraintViolated(o, "ACC_SUPER special lookup procedure not successful: method '" + o.getMethodName(constantPoolGen)
571                             + "' with proper signature not declared in superclass hierarchy.");
572                     }
573                 }
574 
575             } catch (final ClassNotFoundException e) {
576                 // FIXME: maybe not the best way to handle this
577                 throw new AssertionViolatedException("Missing class: " + e, e);
578             }
579 
580         }
581 
582         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
583         @Override
584         public void visitINVOKESTATIC(final INVOKESTATIC o) {
585             try {
586                 // INVOKESTATIC is a LoadClass; the Class where the referenced method is declared in,
587                 // is therefore resolved/verified.
588                 // INVOKESTATIC is an InvokeInstruction, the argument and return types are resolved/verified,
589                 // too. So are the allowed method names.
590                 final String className = o.getClassName(constantPoolGen);
591                 final JavaClass jc = Repository.lookupClass(className);
592                 final Method m = getMethodRecursive(jc, o);
593                 if (m == null) {
594                     constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
595                         + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
596                 } else if (!m.isStatic()) { // implies it's not abstract, verified in pass 2.
597                     constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' has ACC_STATIC unset.");
598                 }
599 
600             } catch (final ClassNotFoundException e) {
601                 // FIXME: maybe not the best way to handle this
602                 throw new AssertionViolatedException("Missing class: " + e, e);
603             }
604         }
605 
606         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
607         @Override
608         public void visitINVOKEVIRTUAL(final INVOKEVIRTUAL o) {
609             try {
610                 // INVOKEVIRTUAL is a LoadClass; the Class where the referenced method is declared in,
611                 // is therefore resolved/verified.
612                 // INVOKEVIRTUAL is an InvokeInstruction, the argument and return types are resolved/verified,
613                 // too. So are the allowed method names.
614                 final String className = o.getClassName(constantPoolGen);
615                 final JavaClass jc;
616                 if (className.charAt(0) == '[') { // array type, for example invoke can be someArray.clone()
617                     jc = Repository.lookupClass("java.lang.Object");
618                 } else {
619                     jc = Repository.lookupClass(className);
620                 }
621                 final Method m = getMethodRecursive(jc, o);
622                 if (m == null) {
623                     constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
624                         + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
625                 }
626                 if (!jc.isClass()) {
627                     constraintViolated(o, "Referenced class '" + jc.getClassName() + "' is an interface, but not a class as expected.");
628                 }
629 
630             } catch (final ClassNotFoundException e) {
631                 // FIXME: maybe not the best way to handle this
632                 // throw new AssertionViolatedException("Missing class: " + e, e);
633                 addMessage("Unable to verify INVOKEVITUAL as cannot load target class: " + e.getCause());
634             }
635         }
636 
637         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
638         @Override
639         public void visitISTORE(final ISTORE o) {
640             final int idx = o.getIndex();
641             if (idx < 0) {
642                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
643             } else {
644                 final int maxminus1 = maxLocals() - 1;
645                 if (idx > maxminus1) {
646                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
647                 }
648             }
649         }
650 
651         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
652         // LDC and LDC_W (LDC_W is a subclass of LDC in BCEL's model)
653         @Override
654         public void visitLDC(final LDC ldc) {
655             indexValid(ldc, ldc.getIndex());
656             final Constant c = constantPoolGen.getConstant(ldc.getIndex());
657             if (c instanceof ConstantClass) {
658                 addMessage("Operand of LDC or LDC_W is CONSTANT_Class '" + tostring(c) + "' - this is only supported in JDK 1.5 and higher.");
659             } else if (!(c instanceof ConstantInteger || c instanceof ConstantFloat || c instanceof ConstantString || c instanceof ConstantDynamic)) {
660                 constraintViolated(ldc,
661                     "Operand of LDC or LDC_W must be one of CONSTANT_Integer, CONSTANT_Float, CONSTANT_String or CONSTANT_Dynamic but is '"
662                             + tostring(c) + "'.");
663             }
664         }
665 
666         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
667         // LDC2_W
668         @Override
669         public void visitLDC2_W(final LDC2_W o) {
670             indexValid(o, o.getIndex());
671             final Constant c = constantPoolGen.getConstant(o.getIndex());
672             if (!(c instanceof ConstantLong || c instanceof ConstantDouble)) {
673                 constraintViolated(o, "Operand of LDC2_W must be CONSTANT_Long or CONSTANT_Double, but is '" + tostring(c) + "'.");
674             }
675             try {
676                 indexValid(o, o.getIndex() + 1);
677             } catch (final StaticCodeInstructionOperandConstraintException e) {
678                 throw new AssertionViolatedException("Does not BCEL handle that? LDC2_W operand has a problem.", e);
679             }
680         }
681 
682         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
683         @Override
684         public void visitLLOAD(final LLOAD o) {
685             final int idx = o.getIndex();
686             if (idx < 0) {
687                 constraintViolated(o, "Index '" + idx + "' must be non-negative."
688                     + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
689             } else {
690                 final int maxminus2 = maxLocals() - 2;
691                 if (idx > maxminus2) {
692                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
693                 }
694             }
695         }
696 
697         ///////////////////////////////////////////////////////////
698         // The Java Virtual Machine Specification, pages 134-137 //
699         ///////////////////////////////////////////////////////////
700         /**
701          * Assures the generic preconditions of a LoadClass instance. The referenced class is loaded and pass2-verified.
702          */
703         @Override
704         public void visitLoadClass(final LoadClass loadClass) {
705             final ObjectType t = loadClass.getLoadClassType(constantPoolGen);
706             if (t != null) { // null means "no class is loaded"
707                 final Verifier v = VerifierFactory.getVerifier(t.getClassName());
708                 final VerificationResult vr = v.doPass1();
709                 if (vr.getStatus() != VerificationResult.VERIFIED_OK) {
710                     constraintViolated((Instruction) loadClass,
711                             "Class '" + loadClass.getLoadClassType(constantPoolGen).getClassName() + "' is referenced, but cannot be loaded: '" + vr + "'.");
712                 }
713             }
714         }
715 
716         /* Checks if the constraints of operands of the said instruction(s) are satisfied. */
717         // public void visitPUTFIELD(PUTFIELD o) {
718         // for performance reasons done in Pass 3b
719         // }
720 
721         /* Checks if the constraints of operands of the said instruction(s) are satisfied. */
722         // public void visitGETFIELD(GETFIELD o) {
723         // for performance reasons done in Pass 3b
724         // }
725 
726         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
727         @Override
728         public void visitLOOKUPSWITCH(final LOOKUPSWITCH o) {
729             final int[] matchs = o.getMatchs();
730             int max = Integer.MIN_VALUE;
731             for (int i = 0; i < matchs.length; i++) {
732                 if (matchs[i] == max && i != 0) {
733                     constraintViolated(o, "Match '" + matchs[i] + "' occurs more than once.");
734                 }
735                 if (matchs[i] < max) {
736                     constraintViolated(o, "Lookup table must be sorted but isn't.");
737                 } else {
738                     max = matchs[i];
739                 }
740             }
741         }
742 
743         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
744         @Override
745         public void visitLSTORE(final LSTORE o) {
746             final int idx = o.getIndex();
747             if (idx < 0) {
748                 constraintViolated(o, "Index '" + idx + "' must be non-negative."
749                     + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
750             } else {
751                 final int maxminus2 = maxLocals() - 2;
752                 if (idx > maxminus2) {
753                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
754                 }
755             }
756         }
757 
758         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
759         @Override
760         public void visitMULTIANEWARRAY(final MULTIANEWARRAY o) {
761             indexValid(o, o.getIndex());
762             final Constant c = constantPoolGen.getConstant(o.getIndex());
763             if (!(c instanceof ConstantClass)) {
764                 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
765             }
766             final int dimensions2create = o.getDimensions();
767             if (dimensions2create < 1) {
768                 constraintViolated(o, "Number of dimensions to create must be greater than zero.");
769             }
770             final Type t = o.getType(constantPoolGen);
771             if (t instanceof ArrayType) {
772                 final int dimensions = ((ArrayType) t).getDimensions();
773                 if (dimensions < dimensions2create) {
774                     constraintViolated(o, "Not allowed to create array with more dimensions ('" + dimensions2create
775                         + "') than the one referenced by the CONSTANT_Class '" + t + "'.");
776                 }
777             } else {
778                 constraintViolated(o, "Expecting a CONSTANT_Class referencing an array type."
779                     + " [Constraint not found in The Java Virtual Machine Specification, Second Edition, 4.8.1]");
780             }
781         }
782 
783         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
784         @Override
785         public void visitNEW(final NEW o) {
786             indexValid(o, o.getIndex());
787             final Constant c = constantPoolGen.getConstant(o.getIndex());
788             if (!(c instanceof ConstantClass)) {
789                 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
790             } else {
791                 final ConstantUtf8 cutf8 = (ConstantUtf8) constantPoolGen.getConstant(((ConstantClass) c).getNameIndex());
792                 final Type t = Type.getType("L" + cutf8.getBytes() + ";");
793                 if (t instanceof ArrayType) {
794                     constraintViolated(o, "NEW must not be used to create an array.");
795                 }
796             }
797 
798         }
799 
800         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
801         @Override
802         public void visitNEWARRAY(final NEWARRAY o) {
803             final byte t = o.getTypecode();
804             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
805                 || t == Const.T_INT || t == Const.T_LONG)) {
806                 constraintViolated(o, "Illegal type code '" + tostring(t) + "' for 'atype' operand.");
807             }
808         }
809 
810         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
811         @Override
812         public void visitPUTSTATIC(final PUTSTATIC o) {
813             try {
814                 final String fieldName = o.getFieldName(constantPoolGen);
815                 final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName());
816                 final Field f = jc.findField(fieldName, o.getType(constantPoolGen));
817                 if (f == null) {
818                     throw new AssertionViolatedException("Field '" + fieldName + "' not found in " + jc.getClassName());
819                 }
820 
821                 if (f.isFinal() && !verifier.getClassName().equals(getObjectType(o).getClassName())) {
822                     constraintViolated(o, "Referenced field '" + f + "' is final and must therefore be declared in the current class '"
823                             + verifier.getClassName() + "' which is not the case: it is declared in '" + o.getReferenceType(constantPoolGen) + "'.");
824                 }
825 
826                 if (!f.isStatic()) {
827                     constraintViolated(o, "Referenced field '" + f + "' is not static which it should be.");
828                 }
829 
830                 final String methName = Repository.lookupClass(verifier.getClassName()).getMethods()[methodNo].getName();
831 
832                 // If it's an interface, it can be set only in <clinit>.
833                 if (!jc.isClass() && !methName.equals(Const.STATIC_INITIALIZER_NAME)) {
834                     constraintViolated(o, "Interface field '" + f + "' must be set in a '" + Const.STATIC_INITIALIZER_NAME + "' method.");
835                 }
836             } catch (final ClassNotFoundException e) {
837                 // FIXME: maybe not the best way to handle this
838                 throw new AssertionViolatedException("Missing class: " + e, e);
839             }
840         }
841 
842         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
843         @Override
844         public void visitRET(final RET o) {
845             final int idx = o.getIndex();
846             if (idx < 0) {
847                 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
848             } else {
849                 final int maxminus1 = maxLocals() - 1;
850                 if (idx > maxminus1) {
851                     constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
852                 }
853             }
854         }
855 
856         // WIDE stuff is BCEL-internal and cannot be checked here.
857 
858         /** Checks if the constraints of operands of the said instruction(s) are satisfied. */
859         @Override
860         public void visitTABLESWITCH(final TABLESWITCH o) {
861             // "high" must be >= "low". We cannot check this, as BCEL hides
862             // it from us.
863         }
864     }
865 
866     /** A small utility method returning if a given int i is in the given int[] ints. */
867     private static boolean contains(final int[] ints, final int i) {
868         for (final int k : ints) {
869             if (k == i) {
870                 return true;
871             }
872         }
873         return false;
874     }
875 
876     /** The Verifier that created this. */
877     private final Verifier verifier;
878 
879     /**
880      * The method number to verify. This is the index in the array returned by JavaClass.getMethods().
881      */
882     private final int methodNo;
883 
884     /**
885      * The one and only InstructionList object used by an instance of this class. It's here for performance reasons by
886      * do_verify() and its callees.
887      */
888     private InstructionList instructionList;
889 
890     /**
891      * The one and only Code object used by an instance of this class. It's here for performance reasons by do_verify() and
892      * its callees.
893      */
894     private Code code;
895 
896     /** Should only be instantiated by a Verifier. */
897     public Pass3aVerifier(final Verifier verifier, final int methodNo) {
898         this.verifier = verifier;
899         this.methodNo = methodNo;
900     }
901 
902     /**
903      * These are the checks that could be done in pass 2 but are delayed to pass 3 for performance reasons. Also, these
904      * checks need access to the code array of the Code attribute of a Method so it's okay to perform them here. Also see
905      * the description of the do_verify() method.
906      *
907      * @throws ClassConstraintException if the verification fails.
908      * @see #do_verify()
909      */
910     private void delayedPass2Checks() {
911 
912         final int[] instructionPositions = instructionList.getInstructionPositions();
913         final int codeLength = code.getCode().length;
914 
915         /////////////////////
916         // LineNumberTable //
917         /////////////////////
918         final LineNumberTable lnt = code.getLineNumberTable();
919         if (lnt != null) {
920             final LineNumber[] lineNumbers = lnt.getLineNumberTable();
921             final IntList offsets = new IntList();
922             lineNumberLoop: for (final LineNumber lineNumber : lineNumbers) { // may appear in any order.
923                 for (final int instructionPosition : instructionPositions) {
924                     // TODO: Make this a binary search! The instructionPositions array is naturally ordered!
925                     final int offset = lineNumber.getStartPC();
926                     if (instructionPosition == offset) {
927                         if (offsets.contains(offset)) {
928                             addMessage("LineNumberTable attribute '" + code.getLineNumberTable() + "' refers to the same code offset ('" + offset
929                                 + "') more than once which is violating the semantics [but is sometimes produced by IBM's 'jikes' compiler].");
930                         } else {
931                             offsets.add(offset);
932                         }
933                         continue lineNumberLoop;
934                     }
935                 }
936                 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has a LineNumberTable attribute '" + code.getLineNumberTable()
937                     + "' referring to a code offset ('" + lineNumber.getStartPC() + "') that does not exist.");
938             }
939         }
940 
941         ///////////////////////////
942         // LocalVariableTable(s) //
943         ///////////////////////////
944         /*
945          * We cannot use code.getLocalVariableTable() because there could be more than only one. This is a bug in BCEL.
946          */
947         final Attribute[] atts = code.getAttributes();
948         for (final Attribute att : atts) {
949             if (att instanceof LocalVariableTable) {
950                 ((LocalVariableTable) att).forEach(localVariable -> {
951                     final int startpc = localVariable.getStartPC();
952                     final int length = localVariable.getLength();
953 
954                     if (!contains(instructionPositions, startpc)) {
955                         throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has a LocalVariableTable attribute '"
956                             + code.getLocalVariableTable() + "' referring to a code offset ('" + startpc + "') that does not exist.");
957                     }
958                     if (!contains(instructionPositions, startpc + length) && startpc + length != codeLength) {
959                         throw new ClassConstraintException(
960                             "Code attribute '" + tostring(code) + "' has a LocalVariableTable attribute '" + code.getLocalVariableTable()
961                                 + "' referring to a code offset start_pc+length ('" + (startpc + length) + "') that does not exist.");
962                     }
963                 });
964             }
965         }
966 
967         ////////////////////
968         // ExceptionTable //
969         ////////////////////
970         // In BCEL's "classfile" API, the startPC/endPC-notation is
971         // inclusive/exclusive as in the Java Virtual Machine Specification.
972         // WARNING: This is not true for BCEL's "generic" API.
973         final CodeException[] exceptionTable = code.getExceptionTable();
974         for (final CodeException element : exceptionTable) {
975             final int startpc = element.getStartPC();
976             final int endpc = element.getEndPC();
977             final int handlerpc = element.getHandlerPC();
978             if (startpc >= endpc) {
979                 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element
980                     + "' that has its start_pc ('" + startpc + "') not smaller than its end_pc ('" + endpc + "').");
981             }
982             if (!contains(instructionPositions, startpc)) {
983                 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element
984                     + "' that has a non-existant bytecode offset as its start_pc ('" + startpc + "').");
985             }
986             if (!contains(instructionPositions, endpc) && endpc != codeLength) {
987                 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element
988                     + "' that has a non-existant bytecode offset as its end_pc ('" + startpc + "') [that is also not equal to code_length ('" + codeLength
989                     + "')].");
990             }
991             if (!contains(instructionPositions, handlerpc)) {
992                 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element
993                     + "' that has a non-existant bytecode offset as its handler_pc ('" + handlerpc + "').");
994             }
995         }
996     }
997 
998     /**
999      * Pass 3a is the verification of static constraints of JVM code (such as legal targets of branch instructions). This is
1000      * the part of pass 3 where you do not need data flow analysis. JustIce also delays the checks for a correct exception
1001      * table of a Code attribute and correct line number entries in a LineNumberTable attribute of a Code attribute (which
1002      * conceptually belong to pass 2) to this pass. Also, most of the check for valid local variable entries in a
1003      * LocalVariableTable attribute of a Code attribute is delayed until this pass. All these checks need access to the code
1004      * array of the Code attribute.
1005      *
1006      * @throws InvalidMethodException if the method to verify does not exist.
1007      */
1008     @Override
1009     public VerificationResult do_verify() {
1010         try {
1011             if (verifier.doPass2().equals(VerificationResult.VR_OK)) {
1012                 // Okay, class file was loaded correctly by Pass 1
1013                 // and satisfies static constraints of Pass 2.
1014                 final JavaClass jc = Repository.lookupClass(verifier.getClassName());
1015                 final Method[] methods = jc.getMethods();
1016                 if (methodNo >= methods.length) {
1017                     throw new InvalidMethodException("METHOD DOES NOT EXIST!");
1018                 }
1019                 final Method method = methods[methodNo];
1020                 code = method.getCode();
1021 
1022                 // No Code? Nothing to verify!
1023                 if (method.isAbstract() || method.isNative()) { // IF mg HAS NO CODE (static constraint of Pass 2)
1024                     return VerificationResult.VR_OK;
1025                 }
1026 
1027                 // TODO:
1028                 // We want a very sophisticated code examination here with good explanations
1029                 // on where to look for an illegal instruction or such.
1030                 // Only after that we should try to build an InstructionList and throw an
1031                 // AssertionViolatedException if after our examination InstructionList building
1032                 // still fails.
1033                 // That examination should be implemented in a byte-oriented way, i.e. look for
1034                 // an instruction, make sure its validity, count its length, find the next
1035                 // instruction and so on.
1036                 try {
1037                     instructionList = new InstructionList(method.getCode().getCode());
1038                 } catch (final RuntimeException re) {
1039                     return new VerificationResult(VerificationResult.VERIFIED_REJECTED,
1040                         "Bad bytecode in the code array of the Code attribute of method '" + tostring(method) + "'.");
1041                 }
1042 
1043                 instructionList.setPositions(true);
1044 
1045                 // Start verification.
1046                 VerificationResult vr = VerificationResult.VR_OK; // default
1047                 try {
1048                     delayedPass2Checks();
1049                 } catch (final ClassConstraintException | ClassFormatException cce) {
1050                     return new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage());
1051                 }
1052                 try {
1053                     pass3StaticInstructionChecks();
1054                     pass3StaticInstructionOperandsChecks();
1055                 } catch (final StaticCodeConstraintException | ClassFormatException scce) {
1056                     vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, scce.getMessage());
1057                 } catch (final ClassCastException cce) {
1058                     vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Class Cast Exception: " + cce.getMessage());
1059                 }
1060                 return vr;
1061             }
1062             // did not pass Pass 2.
1063             return VerificationResult.VR_NOTYET;
1064         } catch (final ClassNotFoundException e) {
1065             // FIXME: maybe not the best way to handle this
1066             throw new AssertionViolatedException("Missing class: " + e, e);
1067         }
1068     }
1069 
1070     /** Returns the method number as supplied when instantiating. */
1071     public int getMethodNo() {
1072         return methodNo;
1073     }
1074 
1075     /**
1076      * These are the checks if constraints are satisfied which are described in the Java Virtual Machine Specification,
1077      * Second Edition as Static Constraints on the instructions of Java Virtual Machine Code (chapter 4.8.1).
1078      *
1079      * @throws StaticCodeConstraintException if the verification fails.
1080      */
1081     private void pass3StaticInstructionChecks() {
1082 
1083         // Code array must not be empty:
1084         // Enforced in pass 2 (also stated in the static constraints of the Code
1085         // array in vmspec2), together with pass 1 (reading code_length bytes and
1086         // interpreting them as code[]). So this must not be checked again here.
1087 
1088         if (code.getCode().length >= Const.MAX_CODE_SIZE) { // length must be LESS than the max
1089             throw new StaticCodeInstructionConstraintException(
1090                 "Code array in code attribute '" + tostring(code) + "' too big: must be smaller than " + Const.MAX_CODE_SIZE + "65536 bytes.");
1091         }
1092 
1093         // First opcode at offset 0: okay, that's clear. Nothing to do.
1094 
1095         // Only instances of the instructions documented in Section 6.4 may appear in
1096         // the code array.
1097 
1098         // For BCEL's sake, we cannot handle WIDE stuff, but hopefully BCEL does its job right :)
1099 
1100         // The last byte of the last instruction in the code array must be the byte at index
1101         // code_length-1 : See the do_verify() comments. We actually don't iterate through the
1102         // byte array, but use an InstructionList so we cannot check for this. But BCEL does
1103         // things right, so it's implicitly okay.
1104 
1105         // TODO: Check how BCEL handles (and will handle) instructions like IMPDEP1, IMPDEP2,
1106         // BREAKPOINT... that BCEL knows about but which are illegal anyway.
1107         // We currently go the safe way here.
1108         InstructionHandle ih = instructionList.getStart();
1109         while (ih != null) {
1110             final Instruction i = ih.getInstruction();
1111             if (i instanceof IMPDEP1) {
1112                 throw new StaticCodeInstructionConstraintException("IMPDEP1 must not be in the code, it is an illegal instruction for _internal_ JVM use!");
1113             }
1114             if (i instanceof IMPDEP2) {
1115                 throw new StaticCodeInstructionConstraintException("IMPDEP2 must not be in the code, it is an illegal instruction for _internal_ JVM use!");
1116             }
1117             if (i instanceof BREAKPOINT) {
1118                 throw new StaticCodeInstructionConstraintException("BREAKPOINT must not be in the code, it is an illegal instruction for _internal_ JVM use!");
1119             }
1120             ih = ih.getNext();
1121         }
1122 
1123         // The original verifier seems to do this check here, too.
1124         // An unreachable last instruction may also not fall through the
1125         // end of the code, which is stupid -- but with the original
1126         // verifier's subroutine semantics one cannot predict reachability.
1127         final Instruction last = instructionList.getEnd().getInstruction();
1128         if (!(last instanceof ReturnInstruction || last instanceof RET || last instanceof GotoInstruction || last instanceof ATHROW)) {
1129             throw new StaticCodeInstructionConstraintException(
1130                 "Execution must not fall off the bottom of the code array. This constraint is enforced statically as some existing verifiers do"
1131                     + " - so it may be a false alarm if the last instruction is not reachable.");
1132         }
1133     }
1134 
1135     /**
1136      * These are the checks for the satisfaction of constraints which are described in the Java Virtual Machine
1137      * Specification, Second Edition as Static Constraints on the operands of instructions of Java Virtual Machine Code
1138      * (chapter 4.8.1). BCEL parses the code array to create an InstructionList and therefore has to check some of these
1139      * constraints. Additional checks are also implemented here.
1140      *
1141      * @throws StaticCodeConstraintException if the verification fails.
1142      */
1143     private void pass3StaticInstructionOperandsChecks() {
1144         try {
1145             // When building up the InstructionList, BCEL has already done all those checks
1146             // mentioned in The Java Virtual Machine Specification, Second Edition, as
1147             // "static constraints on the operands of instructions in the code array".
1148             // TODO: see the do_verify() comments. Maybe we should really work on the
1149             // byte array first to give more comprehensive messages.
1150             // TODO: Review Exception API, possibly build in some "offending instruction" thing
1151             // when we're ready to insulate the offending instruction by doing the
1152             // above thing.
1153 
1154             // TODO: Implement as much as possible here. BCEL does _not_ check everything.
1155 
1156             final ConstantPoolGen cpg = new ConstantPoolGen(Repository.lookupClass(verifier.getClassName()).getConstantPool());
1157             final InstOperandConstraintVisitor v = new InstOperandConstraintVisitor(cpg);
1158 
1159             // Checks for the things BCEL does _not_ handle itself.
1160             InstructionHandle ih = instructionList.getStart();
1161             while (ih != null) {
1162                 final Instruction i = ih.getInstruction();
1163 
1164                 // An "own" constraint, due to JustIce's new definition of what "subroutine" means.
1165                 if (i instanceof JsrInstruction) {
1166                     final InstructionHandle target = ((JsrInstruction) i).getTarget();
1167                     if (target == instructionList.getStart()) {
1168                         throw new StaticCodeInstructionOperandConstraintException(
1169                             "Due to JustIce's clear definition of subroutines, no JSR or JSR_W may have a top-level instruction"
1170                                 + " (such as the very first instruction, which is targeted by instruction '" + tostring(ih) + "' as its target.");
1171                     }
1172                     if (!(target.getInstruction() instanceof ASTORE)) {
1173                         throw new StaticCodeInstructionOperandConstraintException(
1174                             "Due to JustIce's clear definition of subroutines, no JSR or JSR_W may target anything else"
1175                                 + " than an ASTORE instruction. Instruction '" + tostring(ih) + "' targets '" + tostring(target) + "'.");
1176                     }
1177                 }
1178 
1179                 // vmspec2, page 134-137
1180                 ih.accept(v);
1181 
1182                 ih = ih.getNext();
1183             }
1184 
1185         } catch (final ClassNotFoundException e) {
1186             // FIXME: maybe not the best way to handle this
1187             throw new AssertionViolatedException("Missing class: " + e, e);
1188         }
1189     }
1190 
1191     /**
1192      * This method is a slightly modified version of verifier.statics.StringRepresentation.toString(final Node obj) that
1193      * accepts any Object, not just a Node.
1194      *
1195      * Returns the String representation of the Object obj; this is obj.toString() if it does not throw any
1196      * RuntimeException, or else it is a string derived only from obj's class name.
1197      */
1198     protected String tostring(final Object obj) {
1199         String ret;
1200         try {
1201             ret = obj.toString();
1202         } catch (final RuntimeException e) {
1203             // including ClassFormatException, trying to convert the "signature" of a ReturnaddressType LocalVariable
1204             // (shouldn't occur, but people do crazy things)
1205             String s = obj.getClass().getName();
1206             s = s.substring(s.lastIndexOf(".") + 1);
1207             ret = "<<" + s + ">>";
1208         }
1209         return ret;
1210     }
1211 }