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