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