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