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