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