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