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