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