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