View Javadoc
1 package org.apache.bcel.verifier.statics; 2 3 /* ==================================================================== 4 * The Apache Software License, Version 1.1 5 * 6 * Copyright (c) 2001 The Apache Software Foundation. All rights 7 * reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. The end-user documentation included with the redistribution, 22 * if any, must include the following acknowledgment: 23 * "This product includes software developed by the 24 * Apache Software Foundation (http://www.apache.org/)." 25 * Alternately, this acknowledgment may appear in the software itself, 26 * if and wherever such third-party acknowledgments normally appear. 27 * 28 * 4. The names "Apache" and "Apache Software Foundation" and 29 * "Apache BCEL" must not be used to endorse or promote products 30 * derived from this software without prior written permission. For 31 * written permission, please contact apache@apache.org. 32 * 33 * 5. Products derived from this software may not be called "Apache", 34 * "Apache BCEL", nor may "Apache" appear in their name, without 35 * prior written permission of the Apache Software Foundation. 36 * 37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48 * SUCH DAMAGE. 49 * ==================================================================== 50 * 51 * This software consists of voluntary contributions made by many 52 * individuals on behalf of the Apache Software Foundation. For more 53 * information on the Apache Software Foundation, please see 54 * <http://www.apache.org/>;. 55 */ 56 57 import org.apache.bcel.*; 58 import org.apache.bcel.generic.*; 59 import org.apache.bcel.classfile.*; 60 import org.apache.bcel.verifier.*; 61 import org.apache.bcel.verifier.exc.*; 62 63 /*** 64 * This PassVerifier verifies a class file according to 65 * pass 3, static part as described in The Java Virtual 66 * Machine Specification, 2nd edition. 67 * More detailed information is to be found at the do_verify() 68 * method's documentation. 69 * 70 * @version $Id: Pass3aVerifier.java,v 1.3 2002/06/13 09:32:50 enver Exp $ 71 * @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> 72 * @see #do_verify() 73 */ 74 public final class Pass3aVerifier extends PassVerifier{ 75 76 /*** The Verifier that created this. */ 77 private Verifier myOwner; 78 79 /*** 80 * The method number to verify. 81 * This is the index in the array returned 82 * by JavaClass.getMethods(). 83 */ 84 private int method_no; 85 86 /*** 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. */ 87 InstructionList instructionList; 88 /*** 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. */ 89 Code code; 90 91 /*** Should only be instantiated by a Verifier. */ 92 public Pass3aVerifier(Verifier owner, int method_no){ 93 myOwner = owner; 94 this.method_no = method_no; 95 } 96 97 /*** 98 * Pass 3a is the verification of static constraints of 99 * JVM code (such as legal targets of branch instructions). 100 * This is the part of pass 3 where you do not need data 101 * flow analysis. 102 * JustIce also delays the checks for a correct exception 103 * table of a Code attribute and correct line number entries 104 * in a LineNumberTable attribute of a Code attribute (which 105 * conceptually belong to pass 2) to this pass. Also, most 106 * of the check for valid local variable entries in a 107 * LocalVariableTable attribute of a Code attribute is 108 * delayed until this pass. 109 * All these checks need access to the code array of the 110 * Code attribute. 111 * 112 * @throws InvalidMethodException if the method to verify does not exist. 113 */ 114 public VerificationResult do_verify(){ 115 if (myOwner.doPass2().equals(VerificationResult.VR_OK)){ 116 // Okay, class file was loaded correctly by Pass 1 117 // and satisfies static constraints of Pass 2. 118 JavaClass jc = Repository.lookupClass(myOwner.getClassName()); 119 Method[] methods = jc.getMethods(); 120 if (method_no >= methods.length){ 121 throw new InvalidMethodException("METHOD DOES NOT EXIST!"); 122 } 123 Method method = methods[method_no]; 124 code = method.getCode(); 125 126 // No Code? Nothing to verify! 127 if ( method.isAbstract() || method.isNative() ){ // IF mg HAS NO CODE (static constraint of Pass 2) 128 return VerificationResult.VR_OK; 129 } 130 131 // TODO: 132 // We want a very sophisticated code examination here with good explanations 133 // on where to look for an illegal instruction or such. 134 // Only after that we should try to build an InstructionList and throw an 135 // AssertionViolatedException if after our examination InstructionList building 136 // still fails. 137 // That examination should be implemented in a byte-oriented way, i.e. look for 138 // an instruction, make sure its validity, count its length, find the next 139 // instruction and so on. 140 try{ 141 instructionList = new InstructionList(method.getCode().getCode()); 142 } 143 catch(RuntimeException re){ 144 return new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Bad bytecode in the code array of the Code attribute of method '"+method+"'."); 145 } 146 147 instructionList.setPositions(true); 148 149 // Start verification. 150 VerificationResult vr = VerificationResult.VR_OK; //default 151 try{ 152 delayedPass2Checks(); 153 } 154 catch(ClassConstraintException cce){ 155 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage()); 156 return vr; 157 } 158 try{ 159 pass3StaticInstructionChecks(); 160 pass3StaticInstructionOperandsChecks(); 161 } 162 catch(StaticCodeConstraintException scce){ 163 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, scce.getMessage()); 164 } 165 return vr; 166 } 167 else{ //did not pass Pass 2. 168 return VerificationResult.VR_NOTYET; 169 } 170 } 171 172 /*** 173 * These are the checks that could be done in pass 2 but are delayed to pass 3 174 * for performance reasons. Also, these checks need access to the code array 175 * of the Code attribute of a Method so it's okay to perform them here. 176 * Also see the description of the do_verify() method. 177 * 178 * @throws ClassConstraintException if the verification fails. 179 * @see #do_verify() 180 */ 181 private void delayedPass2Checks(){ 182 183 int[] instructionPositions = instructionList.getInstructionPositions(); 184 int codeLength = code.getCode().length; 185 186 ///////////////////// 187 // LineNumberTable // 188 ///////////////////// 189 LineNumberTable lnt = code.getLineNumberTable(); 190 if (lnt != null){ 191 LineNumber[] lineNumbers = lnt.getLineNumberTable(); 192 IntList offsets = new IntList(); 193 lineNumber_loop: for (int i=0; i < lineNumbers.length; i++){ // may appear in any order. 194 for (int j=0; j < instructionPositions.length; j++){ 195 // TODO: Make this a binary search! The instructionPositions array is naturally ordered! 196 int offset = lineNumbers[i].getStartPC(); 197 if (instructionPositions[j] == offset){ 198 if (offsets.contains(offset)){ 199 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]."); 200 } 201 else{ 202 offsets.add(offset); 203 } 204 continue lineNumber_loop; 205 } 206 } 207 throw new ClassConstraintException("Code attribute '"+code+"' has a LineNumberTable attribute '"+code.getLineNumberTable()+"' referring to a code offset ('"+lineNumbers[i].getStartPC()+"') that does not exist."); 208 } 209 } 210 211 /////////////////////////// 212 // LocalVariableTable(s) // 213 /////////////////////////// 214 /* We cannot use code.getLocalVariableTable() because there could be more 215 than only one. This is a bug in BCEL. */ 216 Attribute[] atts = code.getAttributes(); 217 for (int a=0; a<atts.length; a++){ 218 if (atts[a] instanceof LocalVariableTable){ 219 LocalVariableTable lvt = (LocalVariableTable) atts[a]; 220 if (lvt != null){ 221 LocalVariable[] localVariables = lvt.getLocalVariableTable(); 222 for (int i=0; i<localVariables.length; i++){ 223 int startpc = localVariables[i].getStartPC(); 224 int length = localVariables[i].getLength(); 225 226 if (!contains(instructionPositions, startpc)){ 227 throw new ClassConstraintException("Code attribute '"+code+"' has a LocalVariableTable attribute '"+code.getLocalVariableTable()+"' referring to a code offset ('"+startpc+"') that does not exist."); 228 } 229 if ( (!contains(instructionPositions, startpc+length)) && (startpc+length != codeLength) ){ 230 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."); 231 } 232 } 233 } 234 } 235 } 236 237 //////////////////// 238 // ExceptionTable // 239 //////////////////// 240 // In BCEL's "classfile" API, the startPC/endPC-notation is 241 // inclusive/exclusive as in the Java Virtual Machine Specification. 242 // WARNING: This is not true for BCEL's "generic" API. 243 CodeException[] exceptionTable = code.getExceptionTable(); 244 for (int i=0; i<exceptionTable.length; i++){ 245 int startpc = exceptionTable[i].getStartPC(); 246 int endpc = exceptionTable[i].getEndPC(); 247 int handlerpc = exceptionTable[i].getHandlerPC(); 248 if (startpc >= endpc){ 249 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+"')."); 250 } 251 if (!contains(instructionPositions, startpc)){ 252 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+"')."); 253 } 254 if ( (!contains(instructionPositions, endpc)) && (endpc != codeLength)){ 255 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+"')]."); 256 } 257 if (!contains(instructionPositions, handlerpc)){ 258 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+"')."); 259 } 260 } 261 } 262 263 /*** 264 * These are the checks if constraints are satisfied which are described in the 265 * Java Virtual Machine Specification, Second Edition as Static Constraints on 266 * the instructions of Java Virtual Machine Code (chapter 4.8.1). 267 * 268 * @throws StaticCodeConstraintException if the verification fails. 269 */ 270 private void pass3StaticInstructionChecks(){ 271 272 // Code array must not be empty: 273 // Enforced in pass 2 (also stated in the static constraints of the Code 274 // array in vmspec2), together with pass 1 (reading code_length bytes and 275 // interpreting them as code[]). So this must not be checked again here. 276 277 if (! (code.getCode().length < 65536)){// contradicts vmspec2 page 152 ("Limitations"), but is on page 134. 278 throw new StaticCodeInstructionConstraintException("Code array in code attribute '"+code+"' too big: must be smaller than 65536 bytes."); 279 } 280 281 // First opcode at offset 0: okay, that's clear. Nothing to do. 282 283 // Only instances of the instructions documented in Section 6.4 may appear in 284 // the code array. 285 286 // For BCEL's sake, we cannot handle WIDE stuff, but hopefully BCEL does its job right :) 287 288 // The last byte of the last instruction in the code array must be the byte at index 289 // code_length-1 : See the do_verify() comments. We actually don't iterate through the 290 // byte array, but use an InstructionList so we cannot check for this. But BCEL does 291 // things right, so it's implicitly okay. 292 293 // TODO: Check how BCEL handles (and will handle) instructions like IMPDEP1, IMPDEP2, 294 // BREAKPOINT... that BCEL knows about but which are illegal anyway. 295 // We currently go the safe way here. 296 InstructionHandle ih = instructionList.getStart(); 297 while (ih != null){ 298 Instruction i = ih.getInstruction(); 299 if (i instanceof IMPDEP1){ 300 throw new StaticCodeInstructionConstraintException("IMPDEP1 must not be in the code, it is an illegal instruction for _internal_ JVM use!"); 301 } 302 if (i instanceof IMPDEP2){ 303 throw new StaticCodeInstructionConstraintException("IMPDEP2 must not be in the code, it is an illegal instruction for _internal_ JVM use!"); 304 } 305 if (i instanceof BREAKPOINT){ 306 throw new StaticCodeInstructionConstraintException("BREAKPOINT must not be in the code, it is an illegal instruction for _internal_ JVM use!"); 307 } 308 ih = ih.getNext(); 309 } 310 311 // The original verifier seems to do this check here, too. 312 // An unreachable last instruction may also not fall through the 313 // end of the code, which is stupid -- but with the original 314 // verifier's subroutine semantics one cannot predict reachability. 315 Instruction last = instructionList.getEnd().getInstruction(); 316 if (! ((last instanceof ReturnInstruction) || 317 (last instanceof RET) || 318 (last instanceof GotoInstruction) || 319 (last instanceof ATHROW) )) // JSR / JSR_W would possibly RETurn and then fall off the code! 320 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."); 321 } 322 323 /*** 324 * These are the checks for the satisfaction of constraints which are described in the 325 * Java Virtual Machine Specification, Second Edition as Static Constraints on 326 * the operands of instructions of Java Virtual Machine Code (chapter 4.8.1). 327 * BCEL parses the code array to create an InstructionList and therefore has to check 328 * some of these constraints. Additional checks are also implemented here. 329 * 330 * @throws StaticCodeConstraintException if the verification fails. 331 */ 332 private void pass3StaticInstructionOperandsChecks(){ 333 // When building up the InstructionList, BCEL has already done all those checks 334 // mentioned in The Java Virtual Machine Specification, Second Edition, as 335 // "static constraints on the operands of instructions in the code array". 336 // TODO: see the do_verify() comments. Maybe we should really work on the 337 // byte array first to give more comprehensive messages. 338 // TODO: Review Exception API, possibly build in some "offending instruction" thing 339 // when we're ready to insulate the offending instruction by doing the 340 // above thing. 341 342 // TODO: Implement as much as possible here. BCEL does _not_ check everything. 343 344 ConstantPoolGen cpg = new ConstantPoolGen(Repository.lookupClass(myOwner.getClassName()).getConstantPool()); 345 InstOperandConstraintVisitor v = new InstOperandConstraintVisitor(cpg); 346 347 // Checks for the things BCEL does _not_ handle itself. 348 InstructionHandle ih = instructionList.getStart(); 349 while (ih != null){ 350 Instruction i = ih.getInstruction(); 351 352 // An "own" constraint, due to JustIce's new definition of what "subroutine" means. 353 if (i instanceof JsrInstruction){ 354 InstructionHandle target = ((JsrInstruction) i).getTarget(); 355 if (target == instructionList.getStart()){ 356 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."); 357 } 358 if (!(target.getInstruction() instanceof ASTORE)){ 359 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+"'."); 360 } 361 } 362 363 // vmspec2, page 134-137 364 ih.accept(v); 365 366 ih = ih.getNext(); 367 } 368 369 } 370 371 /*** A small utility method returning if a given int i is in the given int[] ints. */ 372 private static boolean contains(int[] ints, int i){ 373 for (int j=0; j<ints.length; j++){ 374 if (ints[j]==i) return true; 375 } 376 return false; 377 } 378 379 /*** Returns the method number as supplied when instantiating. */ 380 public int getMethodNo(){ 381 return method_no; 382 } 383 384 /*** 385 * This visitor class does the actual checking for the instruction 386 * operand's constraints. 387 */ 388 private class InstOperandConstraintVisitor extends org.apache.bcel.generic.EmptyVisitor{ 389 /*** The ConstantPoolGen instance this Visitor operates on. */ 390 private ConstantPoolGen cpg; 391 392 /*** The only Constructor. */ 393 InstOperandConstraintVisitor(ConstantPoolGen cpg){ 394 this.cpg = cpg; 395 } 396 397 /*** 398 * Utility method to return the max_locals value of the method verified 399 * by the surrounding Pass3aVerifier instance. 400 */ 401 private int max_locals(){ 402 return Repository.lookupClass(myOwner.getClassName()).getMethods()[method_no].getCode().getMaxLocals(); 403 } 404 405 /*** 406 * A utility method to always raise an exeption. 407 */ 408 private void constraintViolated(Instruction i, String message) { 409 throw new StaticCodeInstructionOperandConstraintException("Instruction "+i+" constraint violated: "+message); 410 } 411 412 /*** 413 * A utility method to raise an exception if the index is not 414 * a valid constant pool index. 415 */ 416 private void indexValid(Instruction i, int idx){ 417 if (idx < 0 || idx >= cpg.getSize()){ 418 constraintViolated(i, "Illegal constant pool index '"+idx+"'."); 419 } 420 } 421 422 /////////////////////////////////////////////////////////// 423 // The Java Virtual Machine Specification, pages 134-137 // 424 /////////////////////////////////////////////////////////// 425 /*** 426 * Assures the generic preconditions of a LoadClass instance. 427 * The referenced class is loaded and pass2-verified. 428 */ 429 public void visitLoadClass(LoadClass o){ 430 ObjectType t = o.getLoadClassType(cpg); 431 if (t != null){// null means "no class is loaded" 432 Verifier v = VerifierFactory.getVerifier(t.getClassName()); 433 VerificationResult vr = v.doPass1(); 434 if (vr.getStatus() != VerificationResult.VERIFIED_OK){ 435 constraintViolated((Instruction) o, "Class '"+o.getLoadClassType(cpg).getClassName()+"' is referenced, but cannot be loaded: '"+vr+"'."); 436 } 437 } 438 } 439 440 // The target of each jump and branch instruction [...] must be the opcode [...] 441 // BCEL _DOES_ handle this. 442 443 // tableswitch: BCEL will do it, supposedly. 444 445 // lookupswitch: BCEL will do it, supposedly. 446 447 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 448 // LDC and LDC_W (LDC_W is a subclass of LDC in BCEL's model) 449 public void visitLDC(LDC o){ 450 indexValid(o, o.getIndex()); 451 Constant c = cpg.getConstant(o.getIndex()); 452 if (! ( (c instanceof ConstantInteger) || 453 (c instanceof ConstantFloat) || 454 (c instanceof ConstantString) ) ){ 455 constraintViolated(o, "Operand of LDC or LDC_W must be one of CONSTANT_Integer, CONSTANT_Float or CONSTANT_String, but is '"+c+"'."); 456 } 457 } 458 459 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 460 // LDC2_W 461 public void visitLDC2_W(LDC2_W o){ 462 indexValid(o, o.getIndex()); 463 Constant c = cpg.getConstant(o.getIndex()); 464 if (! ( (c instanceof ConstantLong) || 465 (c instanceof ConstantDouble) ) ){ 466 constraintViolated(o, "Operand of LDC2_W must be CONSTANT_Long or CONSTANT_Double, but is '"+c+"'."); 467 } 468 try{ 469 indexValid(o, o.getIndex()+1); 470 } 471 catch(StaticCodeInstructionOperandConstraintException e){ 472 throw new AssertionViolatedException("OOPS: Does not BCEL handle that? LDC2_W operand has a problem."); 473 } 474 } 475 476 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 477 //getfield, putfield, getstatic, putstatic 478 public void visitFieldInstruction(FieldInstruction o){ 479 indexValid(o, o.getIndex()); 480 Constant c = cpg.getConstant(o.getIndex()); 481 if (! (c instanceof ConstantFieldref)){ 482 constraintViolated(o, "Indexing a constant that's not a CONSTANT_Fieldref but a '"+c+"'."); 483 } 484 485 String field_name = o.getFieldName(cpg); 486 487 JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName()); 488 Field[] fields = jc.getFields(); 489 Field f = null; 490 for (int i=0; i<fields.length; i++){ 491 if (fields[i].getName().equals(field_name)){ 492 f = fields[i]; 493 break; 494 } 495 } 496 if (f == null){ 497 /* TODO: also look up if the field is inherited! */ 498 constraintViolated(o, "Referenced field '"+field_name+"' does not exist in class '"+jc.getClassName()+"'."); 499 } 500 else{ 501 /* TODO: Check if assignment compatibility is sufficient. 502 What does Sun do? */ 503 Type f_type = Type.getType(f.getSignature()); 504 Type o_type = o.getType(cpg); 505 506 /* TODO: Is there a way to make BCEL tell us if a field 507 has a void method's signature, i.e. "()I" instead of "I"? */ 508 509 if (! f_type.equals(o_type)){ 510 constraintViolated(o, "Referenced field '"+field_name+"' has type '"+f_type+"' instead of '"+o_type+"' as expected."); 511 } 512 /* TODO: Check for access modifiers here. */ 513 } 514 } 515 516 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 517 public void visitInvokeInstruction(InvokeInstruction o){ 518 indexValid(o, o.getIndex()); 519 if ( (o instanceof INVOKEVIRTUAL) || 520 (o instanceof INVOKESPECIAL) || 521 (o instanceof INVOKESTATIC) ){ 522 Constant c = cpg.getConstant(o.getIndex()); 523 if (! (c instanceof ConstantMethodref)){ 524 constraintViolated(o, "Indexing a constant that's not a CONSTANT_Methodref but a '"+c+"'."); 525 } 526 else{ 527 // Constants are okay due to pass2. 528 ConstantNameAndType cnat = (ConstantNameAndType) (cpg.getConstant(((ConstantMethodref) c).getNameAndTypeIndex())); 529 ConstantUtf8 cutf8 = (ConstantUtf8) (cpg.getConstant(cnat.getNameIndex())); 530 if (cutf8.getBytes().equals(Constants.CONSTRUCTOR_NAME) && (!(o instanceof INVOKESPECIAL)) ){ 531 constraintViolated(o, "Only INVOKESPECIAL is allowed to invoke instance initialization methods."); 532 } 533 if ( (! (cutf8.getBytes().equals(Constants.CONSTRUCTOR_NAME)) ) && (cutf8.getBytes().startsWith("<")) ){ 534 constraintViolated(o, "No method with a name beginning with '<' other than the instance initialization methods may be called by the method invocation instructions."); 535 } 536 } 537 } 538 else{ //if (o instanceof INVOKEINTERFACE){ 539 Constant c = cpg.getConstant(o.getIndex()); 540 if (! (c instanceof ConstantInterfaceMethodref)){ 541 constraintViolated(o, "Indexing a constant that's not a CONSTANT_InterfaceMethodref but a '"+c+"'."); 542 } 543 // TODO: From time to time check if BCEL allows to detect if the 544 // 'count' operand is consistent with the information in the 545 // CONSTANT_InterfaceMethodref and if the last operand is zero. 546 // By now, BCEL hides those two operands because they're superfluous. 547 548 // Invoked method must not be <init> or <clinit> 549 ConstantNameAndType cnat = (ConstantNameAndType) (cpg.getConstant(((ConstantInterfaceMethodref)c).getNameAndTypeIndex())); 550 String name = ((ConstantUtf8) (cpg.getConstant(cnat.getNameIndex()))).getBytes(); 551 if (name.equals(Constants.CONSTRUCTOR_NAME)){ 552 constraintViolated(o, "Method to invoke must not be '"+Constants.CONSTRUCTOR_NAME+"'."); 553 } 554 if (name.equals(Constants.STATIC_INITIALIZER_NAME)){ 555 constraintViolated(o, "Method to invoke must not be '"+Constants.STATIC_INITIALIZER_NAME+"'."); 556 } 557 } 558 559 // The LoadClassType is the method-declaring class, so we have to check the other types. 560 561 Type t = o.getReturnType(cpg); 562 if (t instanceof ArrayType){ 563 t = ((ArrayType) t).getBasicType(); 564 } 565 if (t instanceof ObjectType){ 566 Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName()); 567 VerificationResult vr = v.doPass2(); 568 if (vr.getStatus() != VerificationResult.VERIFIED_OK){ 569 constraintViolated(o, "Return type class/interface could not be verified successfully: '"+vr.getMessage()+"'."); 570 } 571 } 572 573 Type[] ts = o.getArgumentTypes(cpg); 574 for (int i=0; i<ts.length; i++){ 575 t = ts[i]; 576 if (t instanceof ArrayType){ 577 t = ((ArrayType) t).getBasicType(); 578 } 579 if (t instanceof ObjectType){ 580 Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName()); 581 VerificationResult vr = v.doPass2(); 582 if (vr.getStatus() != VerificationResult.VERIFIED_OK){ 583 constraintViolated(o, "Argument type class/interface could not be verified successfully: '"+vr.getMessage()+"'."); 584 } 585 } 586 } 587 588 } 589 590 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 591 public void visitINSTANCEOF(INSTANCEOF o){ 592 indexValid(o, o.getIndex()); 593 Constant c = cpg.getConstant(o.getIndex()); 594 if (! (c instanceof ConstantClass)){ 595 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'."); 596 } 597 } 598 599 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 600 public void visitCHECKCAST(CHECKCAST o){ 601 indexValid(o, o.getIndex()); 602 Constant c = cpg.getConstant(o.getIndex()); 603 if (! (c instanceof ConstantClass)){ 604 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'."); 605 } 606 } 607 608 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 609 public void visitNEW(NEW o){ 610 indexValid(o, o.getIndex()); 611 Constant c = cpg.getConstant(o.getIndex()); 612 if (! (c instanceof ConstantClass)){ 613 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'."); 614 } 615 else{ 616 ConstantUtf8 cutf8 = (ConstantUtf8) (cpg.getConstant( ((ConstantClass) c).getNameIndex() )); 617 Type t = Type.getType("L"+cutf8.getBytes()+";"); 618 if (t instanceof ArrayType){ 619 constraintViolated(o, "NEW must not be used to create an array."); 620 } 621 } 622 623 } 624 625 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 626 public void visitMULTIANEWARRAY(MULTIANEWARRAY o){ 627 indexValid(o, o.getIndex()); 628 Constant c = cpg.getConstant(o.getIndex()); 629 if (! (c instanceof ConstantClass)){ 630 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'."); 631 } 632 int dimensions2create = o.getDimensions(); 633 if (dimensions2create < 1){ 634 constraintViolated(o, "Number of dimensions to create must be greater than zero."); 635 } 636 Type t = o.getType(cpg); 637 if (t instanceof ArrayType){ 638 int dimensions = ((ArrayType) t).getDimensions(); 639 if (dimensions < dimensions2create){ 640 constraintViolated(o, "Not allowed to create array with more dimensions ('+dimensions2create+') than the one referenced by the CONSTANT_Class '"+t+"'."); 641 } 642 } 643 else{ 644 constraintViolated(o, "Expecting a CONSTANT_Class referencing an array type. [Constraint not found in The Java Virtual Machine Specification, Second Edition, 4.8.1]"); 645 } 646 } 647 648 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 649 public void visitANEWARRAY(ANEWARRAY o){ 650 indexValid(o, o.getIndex()); 651 Constant c = cpg.getConstant(o.getIndex()); 652 if (! (c instanceof ConstantClass)){ 653 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'."); 654 } 655 Type t = o.getType(cpg); 656 if (t instanceof ArrayType){ 657 int dimensions = ((ArrayType) t).getDimensions(); 658 if (dimensions >= 255){ 659 constraintViolated(o, "Not allowed to create an array with more than 255 dimensions."); 660 } 661 } 662 } 663 664 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 665 public void visitNEWARRAY(NEWARRAY o){ 666 byte t = o.getTypecode(); 667 if (! ( (t == Constants.T_BOOLEAN) || 668 (t == Constants.T_CHAR) || 669 (t == Constants.T_FLOAT) || 670 (t == Constants.T_DOUBLE) || 671 (t == Constants.T_BYTE) || 672 (t == Constants.T_SHORT) || 673 (t == Constants.T_INT) || 674 (t == Constants.T_LONG) ) ){ 675 constraintViolated(o, "Illegal type code '+t+' for 'atype' operand."); 676 } 677 } 678 679 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 680 public void visitILOAD(ILOAD o){ 681 int idx = o.getIndex(); 682 if (idx < 0){ 683 constraintViolated(o, "Index '"+idx+"' must be non-negative."); 684 } 685 else{ 686 int maxminus1 = max_locals()-1; 687 if (idx > maxminus1){ 688 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); 689 } 690 } 691 } 692 693 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 694 public void visitFLOAD(FLOAD o){ 695 int idx = o.getIndex(); 696 if (idx < 0){ 697 constraintViolated(o, "Index '"+idx+"' must be non-negative."); 698 } 699 else{ 700 int maxminus1 = max_locals()-1; 701 if (idx > maxminus1){ 702 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); 703 } 704 } 705 } 706 707 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 708 public void visitALOAD(ALOAD o){ 709 int idx = o.getIndex(); 710 if (idx < 0){ 711 constraintViolated(o, "Index '"+idx+"' must be non-negative."); 712 } 713 else{ 714 int maxminus1 = max_locals()-1; 715 if (idx > maxminus1){ 716 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); 717 } 718 } 719 } 720 721 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 722 public void visitISTORE(ISTORE o){ 723 int idx = o.getIndex(); 724 if (idx < 0){ 725 constraintViolated(o, "Index '"+idx+"' must be non-negative."); 726 } 727 else{ 728 int maxminus1 = max_locals()-1; 729 if (idx > maxminus1){ 730 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); 731 } 732 } 733 } 734 735 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 736 public void visitFSTORE(FSTORE o){ 737 int idx = o.getIndex(); 738 if (idx < 0){ 739 constraintViolated(o, "Index '"+idx+"' must be non-negative."); 740 } 741 else{ 742 int maxminus1 = max_locals()-1; 743 if (idx > maxminus1){ 744 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); 745 } 746 } 747 } 748 749 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 750 public void visitASTORE(ASTORE o){ 751 int idx = o.getIndex(); 752 if (idx < 0){ 753 constraintViolated(o, "Index '"+idx+"' must be non-negative."); 754 } 755 else{ 756 int maxminus1 = max_locals()-1; 757 if (idx > maxminus1){ 758 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); 759 } 760 } 761 } 762 763 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 764 public void visitIINC(IINC o){ 765 int idx = o.getIndex(); 766 if (idx < 0){ 767 constraintViolated(o, "Index '"+idx+"' must be non-negative."); 768 } 769 else{ 770 int maxminus1 = max_locals()-1; 771 if (idx > maxminus1){ 772 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); 773 } 774 } 775 } 776 777 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 778 public void visitRET(RET o){ 779 int idx = o.getIndex(); 780 if (idx < 0){ 781 constraintViolated(o, "Index '"+idx+"' must be non-negative."); 782 } 783 else{ 784 int maxminus1 = max_locals()-1; 785 if (idx > maxminus1){ 786 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'."); 787 } 788 } 789 } 790 791 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 792 public void visitLLOAD(LLOAD o){ 793 int idx = o.getIndex(); 794 if (idx < 0){ 795 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.]"); 796 } 797 else{ 798 int maxminus2 = max_locals()-2; 799 if (idx > maxminus2){ 800 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'."); 801 } 802 } 803 } 804 805 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 806 public void visitDLOAD(DLOAD o){ 807 int idx = o.getIndex(); 808 if (idx < 0){ 809 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.]"); 810 } 811 else{ 812 int maxminus2 = max_locals()-2; 813 if (idx > maxminus2){ 814 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'."); 815 } 816 } 817 } 818 819 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 820 public void visitLSTORE(LSTORE o){ 821 int idx = o.getIndex(); 822 if (idx < 0){ 823 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.]"); 824 } 825 else{ 826 int maxminus2 = max_locals()-2; 827 if (idx > maxminus2){ 828 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'."); 829 } 830 } 831 } 832 833 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 834 public void visitDSTORE(DSTORE o){ 835 int idx = o.getIndex(); 836 if (idx < 0){ 837 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.]"); 838 } 839 else{ 840 int maxminus2 = max_locals()-2; 841 if (idx > maxminus2){ 842 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'."); 843 } 844 } 845 } 846 847 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 848 public void visitLOOKUPSWITCH(LOOKUPSWITCH o){ 849 int[] matchs = o.getMatchs(); 850 int max = Integer.MIN_VALUE; 851 for (int i=0; i<matchs.length; i++){ 852 if (matchs[i] == max && i != 0){ 853 constraintViolated(o, "Match '"+matchs[i]+"' occurs more than once."); 854 } 855 if (matchs[i] < max){ 856 constraintViolated(o, "Lookup table must be sorted but isn't."); 857 } 858 else{ 859 max = matchs[i]; 860 } 861 } 862 } 863 864 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 865 public void visitTABLESWITCH(TABLESWITCH o){ 866 // "high" must be >= "low". We cannot check this, as BCEL hides 867 // it from us. 868 } 869 870 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 871 public void visitPUTSTATIC(PUTSTATIC o){ 872 String field_name = o.getFieldName(cpg); 873 JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName()); 874 Field[] fields = jc.getFields(); 875 Field f = null; 876 for (int i=0; i<fields.length; i++){ 877 if (fields[i].getName().equals(field_name)){ 878 f = fields[i]; 879 break; 880 } 881 } 882 if (f == null){ 883 throw new AssertionViolatedException("Field not found?!?"); 884 } 885 886 if (f.isFinal()){ 887 if (!(myOwner.getClassName().equals(o.getClassType(cpg).getClassName()))){ 888 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()+"'."); 889 } 890 } 891 892 if (! (f.isStatic())){ 893 constraintViolated(o, "Referenced field '"+f+"' is not static which it should be."); 894 } 895 896 String meth_name = Repository.lookupClass(myOwner.getClassName()).getMethods()[method_no].getName(); 897 898 // If it's an interface, it can be set only in <clinit>. 899 if ((!(jc.isClass())) && (!(meth_name.equals(Constants.STATIC_INITIALIZER_NAME)))){ 900 constraintViolated(o, "Interface field '"+f+"' must be set in a '"+Constants.STATIC_INITIALIZER_NAME+"' method."); 901 } 902 } 903 904 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 905 public void visitGETSTATIC(GETSTATIC o){ 906 String field_name = o.getFieldName(cpg); 907 JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName()); 908 Field[] fields = jc.getFields(); 909 Field f = null; 910 for (int i=0; i<fields.length; i++){ 911 if (fields[i].getName().equals(field_name)){ 912 f = fields[i]; 913 break; 914 } 915 } 916 if (f == null){ 917 throw new AssertionViolatedException("Field not found?!?"); 918 } 919 920 if (! (f.isStatic())){ 921 constraintViolated(o, "Referenced field '"+f+"' is not static which it should be."); 922 } 923 } 924 925 /* Checks if the constraints of operands of the said instruction(s) are satisfied. */ 926 //public void visitPUTFIELD(PUTFIELD o){ 927 // for performance reasons done in Pass 3b 928 //} 929 930 /* Checks if the constraints of operands of the said instruction(s) are satisfied. */ 931 //public void visitGETFIELD(GETFIELD o){ 932 // for performance reasons done in Pass 3b 933 //} 934 935 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 936 public void visitINVOKEINTERFACE(INVOKEINTERFACE o){ 937 // INVOKEINTERFACE is a LoadClass; the Class where the referenced method is declared in, 938 // is therefore resolved/verified. 939 // INVOKEINTERFACE is an InvokeInstruction, the argument and return types are resolved/verified, 940 // too. So are the allowed method names. 941 String classname = o.getClassName(cpg); 942 JavaClass jc = Repository.lookupClass(classname); 943 Method[] ms = jc.getMethods(); 944 Method m = null; 945 for (int i=0; i<ms.length; i++){ 946 if ( (ms[i].getName().equals(o.getMethodName(cpg))) && 947 (Type.getReturnType(ms[i].getSignature()).equals(o.getReturnType(cpg))) && 948 (objarrayequals(Type.getArgumentTypes(ms[i].getSignature()), o.getArgumentTypes(cpg))) ){ 949 m = ms[i]; 950 break; 951 } 952 } 953 if (m == null){ 954 constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' with expected signature not found in class '"+jc.getClassName()+"'. The native verfier does allow the method to be declared in some superinterface, which the Java Virtual Machine Specification, Second Edition does not."); 955 } 956 if (jc.isClass()){ 957 constraintViolated(o, "Referenced class '"+jc.getClassName()+"' is a class, but not an interface as expected."); 958 } 959 } 960 961 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 962 public void visitINVOKESPECIAL(INVOKESPECIAL o){ 963 // INVOKESPECIAL is a LoadClass; the Class where the referenced method is declared in, 964 // is therefore resolved/verified. 965 // INVOKESPECIAL is an InvokeInstruction, the argument and return types are resolved/verified, 966 // too. So are the allowed method names. 967 String classname = o.getClassName(cpg); 968 JavaClass jc = Repository.lookupClass(classname); 969 Method[] ms = jc.getMethods(); 970 Method m = null; 971 for (int i=0; i<ms.length; i++){ 972 if ( (ms[i].getName().equals(o.getMethodName(cpg))) && 973 (Type.getReturnType(ms[i].getSignature()).equals(o.getReturnType(cpg))) && 974 (objarrayequals(Type.getArgumentTypes(ms[i].getSignature()), o.getArgumentTypes(cpg))) ){ 975 m = ms[i]; 976 break; 977 } 978 } 979 if (m == null){ 980 constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' with expected signature not found in class '"+jc.getClassName()+"'. The native verfier does allow the method to be declared in some superclass or implemented interface, which the Java Virtual Machine Specification, Second Edition does not."); 981 } 982 983 JavaClass current = Repository.lookupClass(myOwner.getClassName()); 984 if (current.isSuper()){ 985 986 if ((Repository.instanceOf( current, jc )) && (!current.equals(jc))){ 987 988 if (! (o.getMethodName(cpg).equals(Constants.CONSTRUCTOR_NAME) )){ 989 // Special lookup procedure for ACC_SUPER classes. 990 991 int supidx = -1; 992 993 Method meth = null; 994 while (supidx != 0){ 995 supidx = current.getSuperclassNameIndex(); 996 current = Repository.lookupClass(current.getSuperclassName()); 997 998 Method[] meths = current.getMethods(); 999 for (int i=0; i<meths.length; i++){ 1000 if ( (meths[i].getName().equals(o.getMethodName(cpg))) && 1001 (Type.getReturnType(meths[i].getSignature()).equals(o.getReturnType(cpg))) && 1002 (objarrayequals(Type.getArgumentTypes(meths[i].getSignature()), o.getArgumentTypes(cpg))) ){ 1003 meth = meths[i]; 1004 break; 1005 } 1006 } 1007 if (meth != null) break; 1008 } 1009 if (meth == null){ 1010 constraintViolated(o, "ACC_SUPER special lookup procedure not successful: method '"+o.getMethodName(cpg)+"' with proper signature not declared in superclass hierarchy."); 1011 } 1012 } 1013 } 1014 } 1015 1016 1017 } 1018 1019 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 1020 public void visitINVOKESTATIC(INVOKESTATIC o){ 1021 // INVOKESTATIC is a LoadClass; the Class where the referenced method is declared in, 1022 // is therefore resolved/verified. 1023 // INVOKESTATIC is an InvokeInstruction, the argument and return types are resolved/verified, 1024 // too. So are the allowed method names. 1025 String classname = o.getClassName(cpg); 1026 JavaClass jc = Repository.lookupClass(classname); 1027 Method[] ms = jc.getMethods(); 1028 Method m = null; 1029 for (int i=0; i<ms.length; i++){ 1030 if ( (ms[i].getName().equals(o.getMethodName(cpg))) && 1031 (Type.getReturnType(ms[i].getSignature()).equals(o.getReturnType(cpg))) && 1032 (objarrayequals(Type.getArgumentTypes(ms[i].getSignature()), o.getArgumentTypes(cpg))) ){ 1033 m = ms[i]; 1034 break; 1035 } 1036 } 1037 if (m == null){ 1038 constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' with expected signature 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."); 1039 } 1040 1041 if (! (m.isStatic())){ // implies it's not abstract, verified in pass 2. 1042 constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' has ACC_STATIC unset."); 1043 } 1044 1045 } 1046 1047 1048 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 1049 public void visitINVOKEVIRTUAL(INVOKEVIRTUAL o){ 1050 // INVOKEVIRTUAL is a LoadClass; the Class where the referenced method is declared in, 1051 // is therefore resolved/verified. 1052 // INVOKEVIRTUAL is an InvokeInstruction, the argument and return types are resolved/verified, 1053 // too. So are the allowed method names. 1054 String classname = o.getClassName(cpg); 1055 JavaClass jc = Repository.lookupClass(classname); 1056 Method[] ms = jc.getMethods(); 1057 Method m = null; 1058 for (int i=0; i<ms.length; i++){ 1059 if ( (ms[i].getName().equals(o.getMethodName(cpg))) && 1060 (Type.getReturnType(ms[i].getSignature()).equals(o.getReturnType(cpg))) && 1061 (objarrayequals(Type.getArgumentTypes(ms[i].getSignature()), o.getArgumentTypes(cpg))) ){ 1062 m = ms[i]; 1063 break; 1064 } 1065 } 1066 if (m == null){ 1067 constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' with expected signature not found in class '"+jc.getClassName()+"'. The native verfier does allow the method to be declared in some superclass or implemented interface, which the Java Virtual Machine Specification, Second Edition does not."); 1068 } 1069 if (! (jc.isClass())){ 1070 constraintViolated(o, "Referenced class '"+jc.getClassName()+"' is an interface, but not a class as expected."); 1071 } 1072 1073 } 1074 1075 1076 // WIDE stuff is BCEL-internal and cannot be checked here. 1077 1078 /*** 1079 * A utility method like equals(Object) for arrays. 1080 * The equality of the elements is based on their equals(Object) 1081 * method instead of their object identity. 1082 */ 1083 private boolean objarrayequals(Object[] o, Object[] p){ 1084 if (o.length != p.length){ 1085 return false; 1086 } 1087 1088 for (int i=0; i<o.length; i++){ 1089 if (! (o[i].equals(p[i])) ){ 1090 return false; 1091 } 1092 } 1093 1094 return true; 1095 } 1096 1097 } 1098 }

This page was automatically generated by Maven