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