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