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