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