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.Constants;
  56. import com.sun.org.apache.bcel.internal.Repository;
  57. import com.sun.org.apache.bcel.internal.classfile.*;
  58. import com.sun.org.apache.bcel.internal.classfile.Deprecated; // Use _this_ one!
  59. import com.sun.org.apache.bcel.internal.classfile.DescendingVisitor; // Use _this_ one!
  60. import com.sun.org.apache.bcel.internal.classfile.EmptyVisitor; // Use _this_ one!
  61. import com.sun.org.apache.bcel.internal.classfile.Visitor; // Use _this_ one!
  62. import com.sun.org.apache.bcel.internal.generic.*;
  63. import com.sun.org.apache.bcel.internal.verifier.*;
  64. import com.sun.org.apache.bcel.internal.verifier.exc.*;
  65. import java.util.HashMap;
  66. import java.util.HashSet;
  67. /**
  68. * This PassVerifier verifies a class file according to
  69. * pass 2 as described in The Java Virtual Machine
  70. * Specification, 2nd edition.
  71. * More detailed information is to be found at the do_verify()
  72. * method's documentation.
  73. *
  74. * @version $Id: Pass2Verifier.java,v 1.1.1.1 2001/10/29 20:00:36 jvanzyl Exp $
  75. * @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A>
  76. * @see #do_verify()
  77. */
  78. public final class Pass2Verifier extends PassVerifier implements Constants{
  79. /**
  80. * The LocalVariableInfo instances used by Pass3bVerifier.
  81. * localVariablesInfos[i] denotes the information for the
  82. * local variables of method number i in the
  83. * JavaClass this verifier operates on.
  84. */
  85. private LocalVariablesInfo[] localVariablesInfos;
  86. /** The Verifier that created this. */
  87. private Verifier myOwner;
  88. /**
  89. * Should only be instantiated by a Verifier.
  90. *
  91. * @see Verifier
  92. */
  93. public Pass2Verifier(Verifier owner){
  94. myOwner = owner;
  95. }
  96. /**
  97. * Returns a LocalVariablesInfo object containing information
  98. * about the usage of the local variables in the Code attribute
  99. * of the said method or <B>null</B> if the class file this
  100. * Pass2Verifier operates on could not be pass-2-verified correctly.
  101. * The method number method_nr is the method you get using
  102. * <B>Repository.lookupClass(myOwner.getClassname()).getMethods()[method_nr];</B>.
  103. * You should not add own information. Leave that to JustIce.
  104. */
  105. public LocalVariablesInfo getLocalVariablesInfo(int method_nr){
  106. if (this.verify() != VerificationResult.VR_OK) return null; // It's cached, don't worry.
  107. if (method_nr < 0 || method_nr >= localVariablesInfos.length){
  108. throw new AssertionViolatedException("Method number out of range.");
  109. }
  110. return localVariablesInfos[method_nr];
  111. }
  112. /**
  113. * Pass 2 is the pass where static properties of the
  114. * class file are checked without looking into "Code"
  115. * arrays of methods.
  116. * This verification pass is usually invoked when
  117. * a class is resolved; and it may be possible that
  118. * this verification pass has to load in other classes
  119. * such as superclasses or implemented interfaces.
  120. * Therefore, Pass 1 is run on them.<BR>
  121. * Note that most referenced classes are <B>not</B> loaded
  122. * in for verification or for an existance check by this
  123. * pass; only the syntactical correctness of their names
  124. * and descriptors (a.k.a. signatures) is checked.<BR>
  125. * Very few checks that conceptually belong here
  126. * are delayed until pass 3a in JustIce. JustIce does
  127. * not only check for syntactical correctness but also
  128. * for semantical sanity - therefore it needs access to
  129. * the "Code" array of methods in a few cases. Please
  130. * see the pass 3a documentation, too.
  131. *
  132. * @see com.sun.org.apache.bcel.internal.verifier.statics.Pass3aVerifier
  133. */
  134. public VerificationResult do_verify(){
  135. VerificationResult vr1 = myOwner.doPass1();
  136. if (vr1.equals(VerificationResult.VR_OK)){
  137. // For every method, we could have information about the local variables out of LocalVariableTable attributes of
  138. // the Code attributes.
  139. localVariablesInfos = new LocalVariablesInfo[Repository.lookupClass(myOwner.getClassName()).getMethods().length];
  140. VerificationResult vr = VerificationResult.VR_OK; // default.
  141. try{
  142. constant_pool_entries_satisfy_static_constraints();
  143. field_and_method_refs_are_valid();
  144. every_class_has_an_accessible_superclass();
  145. final_methods_are_not_overridden();
  146. }
  147. catch (ClassConstraintException cce){
  148. vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage());
  149. }
  150. return vr;
  151. }
  152. else
  153. return VerificationResult.VR_NOTYET;
  154. }
  155. /**
  156. * Ensures that every class has a super class and that
  157. * <B>final</B> classes are not subclassed.
  158. * This means, the class this Pass2Verifier operates
  159. * on has proper super classes (transitively) up to
  160. * java.lang.Object.
  161. * The reason for really loading (and Pass1-verifying)
  162. * all of those classes here is that we need them in
  163. * Pass2 anyway to verify no final methods are overridden
  164. * (that could be declared anywhere in the ancestor hierarchy).
  165. *
  166. * @throws ClassConstraintException otherwise.
  167. */
  168. private void every_class_has_an_accessible_superclass(){
  169. HashSet hs = new HashSet(); // save class names to detect circular inheritance
  170. JavaClass jc = Repository.lookupClass(myOwner.getClassName());
  171. int supidx = -1;
  172. while (supidx != 0){
  173. supidx = jc.getSuperclassNameIndex();
  174. if (supidx == 0){
  175. if (jc != Repository.lookupClass(Type.OBJECT.getClassName())){
  176. throw new ClassConstraintException("Superclass of '"+jc.getClassName()+"' missing but not "+Type.OBJECT.getClassName()+" itself!");
  177. }
  178. }
  179. else{
  180. String supername = jc.getSuperclassName();
  181. if (! hs.add(supername)){ // If supername already is in the list
  182. throw new ClassConstraintException("Circular superclass hierarchy detected.");
  183. }
  184. Verifier v = VerifierFactory.getVerifier(supername);
  185. VerificationResult vr = v.doPass1();
  186. if (vr != VerificationResult.VR_OK){
  187. throw new ClassConstraintException("Could not load in ancestor class '"+supername+"'.");
  188. }
  189. jc = Repository.lookupClass(supername);
  190. if (jc.isFinal()){
  191. throw new ClassConstraintException("Ancestor class '"+supername+"' has the FINAL access modifier and must therefore not be subclassed.");
  192. }
  193. }
  194. }
  195. }
  196. /**
  197. * Ensures that <B>final</B> methods are not overridden.
  198. * <B>Precondition to run this method:
  199. * constant_pool_entries_satisfy_static_constraints() and
  200. * every_class_has_an_accessible_superclass() have to be invoked before
  201. * (in that order).</B>
  202. *
  203. * @throws ClassConstraintException otherwise.
  204. * @see #constant_pool_entries_satisfy_static_constraints()
  205. * @see #every_class_has_an_accessible_superclass()
  206. */
  207. private void final_methods_are_not_overridden(){
  208. HashMap hashmap = new HashMap();
  209. JavaClass jc = Repository.lookupClass(myOwner.getClassName());
  210. int supidx = -1;
  211. while (supidx != 0){
  212. supidx = jc.getSuperclassNameIndex();
  213. ConstantPoolGen cpg = new ConstantPoolGen(jc.getConstantPool());
  214. Method[] methods = jc.getMethods();
  215. for (int i=0; i<methods.length; i++){
  216. String name_and_sig = (methods[i].getName()+methods[i].getSignature());
  217. if (hashmap.containsKey(name_and_sig)){
  218. if (methods[i].isFinal()){
  219. throw new ClassConstraintException("Method '"+name_and_sig+"' in class '"+hashmap.get(name_and_sig)+"' overrides the final (not-overridable) definition in class '"+jc.getClassName()+"'.");
  220. }
  221. else{
  222. if (!methods[i].isStatic()){ // static methods don't inherit
  223. hashmap.put(name_and_sig, jc.getClassName());
  224. }
  225. }
  226. }
  227. else{
  228. if (!methods[i].isStatic()){ // static methods don't inherit
  229. hashmap.put(name_and_sig, jc.getClassName());
  230. }
  231. }
  232. }
  233. jc = Repository.lookupClass(jc.getSuperclassName()); // Well, for OBJECT this returns OBJECT so it works (could return anything but must not throw an Exception).
  234. }
  235. }
  236. /**
  237. * Ensures that the constant pool entries satisfy the static constraints
  238. * as described in The Java Virtual Machine Specification, 2nd Edition.
  239. *
  240. * @throws ClassConstraintException otherwise.
  241. */
  242. private void constant_pool_entries_satisfy_static_constraints(){
  243. // Most of the consistency is handled internally by BCEL; here
  244. // we only have to verify if the indices of the constants point
  245. // to constants of the appropriate type and such.
  246. JavaClass jc = Repository.lookupClass(myOwner.getClassName());
  247. new CPESSC_Visitor(jc); // constructor implicitely traverses jc
  248. }
  249. /**
  250. * A Visitor class that ensures the constant pool satisfies the static
  251. * constraints.
  252. * The visitXXX() methods throw ClassConstraintException instances otherwise.
  253. *
  254. * @see #constant_pool_entries_satisfy_static_constraints()
  255. */
  256. private class CPESSC_Visitor extends com.sun.org.apache.bcel.internal.classfile.EmptyVisitor implements Visitor{
  257. private Class CONST_Class;
  258. private Class CONST_Fieldref;
  259. private Class CONST_Methodref;
  260. private Class CONST_InterfaceMethodref;
  261. private Class CONST_String;
  262. private Class CONST_Integer;
  263. private Class CONST_Float;
  264. private Class CONST_Long;
  265. private Class CONST_Double;
  266. private Class CONST_NameAndType;
  267. private Class CONST_Utf8;
  268. private final JavaClass jc;
  269. private final ConstantPool cp; // ==jc.getConstantPool() -- only here to save typing work and computing power.
  270. private final int cplen; // == cp.getLength() -- to save computing power.
  271. private DescendingVisitor carrier;
  272. private HashSet field_names = new HashSet();
  273. private HashSet field_names_and_desc = new HashSet();
  274. private HashSet method_names_and_desc = new HashSet();
  275. private CPESSC_Visitor(JavaClass _jc){
  276. jc = _jc;
  277. cp = _jc.getConstantPool();
  278. cplen = cp.getLength();
  279. CONST_Class = com.sun.org.apache.bcel.internal.classfile.ConstantClass.class;
  280. CONST_Fieldref = com.sun.org.apache.bcel.internal.classfile.ConstantFieldref.class;
  281. CONST_Methodref = com.sun.org.apache.bcel.internal.classfile.ConstantMethodref.class;
  282. CONST_InterfaceMethodref = com.sun.org.apache.bcel.internal.classfile.ConstantInterfaceMethodref.class;
  283. CONST_String = com.sun.org.apache.bcel.internal.classfile.ConstantString.class;
  284. CONST_Integer = com.sun.org.apache.bcel.internal.classfile.ConstantInteger.class;
  285. CONST_Float = com.sun.org.apache.bcel.internal.classfile.ConstantFloat.class;
  286. CONST_Long = com.sun.org.apache.bcel.internal.classfile.ConstantLong.class;
  287. CONST_Double = com.sun.org.apache.bcel.internal.classfile.ConstantDouble.class;
  288. CONST_NameAndType = com.sun.org.apache.bcel.internal.classfile.ConstantNameAndType.class;
  289. CONST_Utf8 = com.sun.org.apache.bcel.internal.classfile.ConstantUtf8.class;
  290. carrier = new DescendingVisitor(_jc, this);
  291. carrier.visit();
  292. }
  293. private void checkIndex(Node referrer, int index, Class shouldbe){
  294. if ((index < 0) || (index >= cplen)){
  295. throw new ClassConstraintException("Invalid index '"+index+"' used by '"+tostring(referrer)+"'.");
  296. }
  297. Constant c = cp.getConstant(index);
  298. if (! shouldbe.isInstance(c)){
  299. String isnot = shouldbe.toString().substring(shouldbe.toString().lastIndexOf(".")+1); //Cut all before last "."
  300. throw new ClassCastException("Illegal constant '"+tostring(c)+"' at index '"+index+"'. '"+tostring(referrer)+"' expects a '"+shouldbe+"'.");
  301. }
  302. }
  303. ///////////////////////////////////////
  304. // ClassFile structure (vmspec2 4.1) //
  305. ///////////////////////////////////////
  306. public void visitJavaClass(JavaClass obj){
  307. Attribute[] atts = obj.getAttributes();
  308. boolean foundSourceFile = false;
  309. boolean foundInnerClasses = false;
  310. // Is there an InnerClass referenced?
  311. // This is a costly check; existing verifiers don't do it!
  312. boolean hasInnerClass = new InnerClassDetector(jc).innerClassReferenced();
  313. for (int i=0; i<atts.length; i++){
  314. if ((! (atts[i] instanceof SourceFile)) &&
  315. (! (atts[i] instanceof Deprecated)) &&
  316. (! (atts[i] instanceof InnerClasses)) &&
  317. (! (atts[i] instanceof Synthetic))){
  318. addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of the ClassFile structure '"+tostring(obj)+"' is unknown and will therefore be ignored.");
  319. }
  320. if (atts[i] instanceof SourceFile){
  321. if (foundSourceFile == false) foundSourceFile = true;
  322. else throw new ClassConstraintException("A ClassFile structure (like '"+tostring(obj)+"') may have no more than one SourceFile attribute."); //vmspec2 4.7.7
  323. }
  324. if (atts[i] instanceof InnerClasses){
  325. if (foundInnerClasses == false) foundInnerClasses = true;
  326. else{
  327. if (hasInnerClass){
  328. throw new ClassConstraintException("A Classfile structure (like '"+tostring(obj)+"') must have exactly one InnerClasses attribute if at least one Inner Class is referenced (which is the case). More than one InnerClasses attribute was found.");
  329. }
  330. }
  331. if (!hasInnerClass){
  332. addMessage("No referenced Inner Class found, but InnerClasses attribute '"+tostring(atts[i])+"' found. Strongly suggest removal of that attribute.");
  333. }
  334. }
  335. }
  336. if (hasInnerClass && !foundInnerClasses){
  337. //throw new ClassConstraintException("A Classfile structure (like '"+tostring(obj)+"') must have exactly one InnerClasses attribute if at least one Inner Class is referenced (which is the case). No InnerClasses attribute was found.");
  338. //vmspec2, page 125 says it would be a constraint: but existing verifiers
  339. //don't check it and javac doesn't satisfy it when it comes to anonymous
  340. //inner classes
  341. addMessage("A Classfile structure (like '"+tostring(obj)+"') must have exactly one InnerClasses attribute if at least one Inner Class is referenced (which is the case). No InnerClasses attribute was found.");
  342. }
  343. }
  344. /////////////////////////////
  345. // CONSTANTS (vmspec2 4.4) //
  346. /////////////////////////////
  347. public void visitConstantClass(ConstantClass obj){
  348. if (obj.getTag() != Constants.CONSTANT_Class){
  349. throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
  350. }
  351. checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
  352. }
  353. public void visitConstantFieldref(ConstantFieldref obj){
  354. if (obj.getTag() != Constants.CONSTANT_Fieldref){
  355. throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
  356. }
  357. checkIndex(obj, obj.getClassIndex(), CONST_Class);
  358. checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
  359. }
  360. public void visitConstantMethodref(ConstantMethodref obj){
  361. if (obj.getTag() != Constants.CONSTANT_Methodref){
  362. throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
  363. }
  364. checkIndex(obj, obj.getClassIndex(), CONST_Class);
  365. checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
  366. }
  367. public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj){
  368. if (obj.getTag() != Constants.CONSTANT_InterfaceMethodref){
  369. throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
  370. }
  371. checkIndex(obj, obj.getClassIndex(), CONST_Class);
  372. checkIndex(obj, obj.getNameAndTypeIndex(), CONST_NameAndType);
  373. }
  374. public void visitConstantString(ConstantString obj){
  375. if (obj.getTag() != Constants.CONSTANT_String){
  376. throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
  377. }
  378. checkIndex(obj, obj.getStringIndex(), CONST_Utf8);
  379. }
  380. public void visitConstantInteger(ConstantInteger obj){
  381. if (obj.getTag() != Constants.CONSTANT_Integer){
  382. throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
  383. }
  384. // no indices to check
  385. }
  386. public void visitConstantFloat(ConstantFloat obj){
  387. if (obj.getTag() != Constants.CONSTANT_Float){
  388. throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
  389. }
  390. //no indices to check
  391. }
  392. public void visitConstantLong(ConstantLong obj){
  393. if (obj.getTag() != Constants.CONSTANT_Long){
  394. throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
  395. }
  396. //no indices to check
  397. }
  398. public void visitConstantDouble(ConstantDouble obj){
  399. if (obj.getTag() != Constants.CONSTANT_Double){
  400. throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
  401. }
  402. //no indices to check
  403. }
  404. public void visitConstantNameAndType(ConstantNameAndType obj){
  405. if (obj.getTag() != Constants.CONSTANT_NameAndType){
  406. throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
  407. }
  408. checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
  409. //checkIndex(obj, obj.getDescriptorIndex(), CONST_Utf8); //inconsistently named in BCEL, see below.
  410. checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
  411. }
  412. public void visitConstantUtf8(ConstantUtf8 obj){
  413. if (obj.getTag() != Constants.CONSTANT_Utf8){
  414. throw new ClassConstraintException("Wrong constant tag in '"+tostring(obj)+"'.");
  415. }
  416. //no indices to check
  417. }
  418. //////////////////////////
  419. // FIELDS (vmspec2 4.5) //
  420. //////////////////////////
  421. public void visitField(Field obj){
  422. if (jc.isClass()){
  423. int maxone=0;
  424. if (obj.isPrivate()) maxone++;
  425. if (obj.isProtected()) maxone++;
  426. if (obj.isPublic()) maxone++;
  427. if (maxone > 1){
  428. throw new ClassConstraintException("Field '"+tostring(obj)+"' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set.");
  429. }
  430. if (obj.isFinal() && obj.isVolatile()){
  431. throw new ClassConstraintException("Field '"+tostring(obj)+"' must only have at most one of its ACC_FINAL, ACC_VOLATILE modifiers set.");
  432. }
  433. }
  434. else{ // isInterface!
  435. if (!obj.isPublic()){
  436. throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_PUBLIC modifier set but hasn't!");
  437. }
  438. if (!obj.isStatic()){
  439. throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_STATIC modifier set but hasn't!");
  440. }
  441. if (!obj.isFinal()){
  442. throw new ClassConstraintException("Interface field '"+tostring(obj)+"' must have the ACC_FINAL modifier set but hasn't!");
  443. }
  444. }
  445. if ((obj.getAccessFlags() & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_VOLATILE|ACC_TRANSIENT)) > 0){
  446. addMessage("Field '"+tostring(obj)+"' has access flag(s) other than ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_VOLATILE, ACC_TRANSIENT set (ignored).");
  447. }
  448. checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
  449. String name = obj.getName();
  450. if (! validFieldName(name)){
  451. throw new ClassConstraintException("Field '"+tostring(obj)+"' has illegal name '"+obj.getName()+"'.");
  452. }
  453. // A descriptor is often named signature in BCEL
  454. checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
  455. String sig = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
  456. try{
  457. Type t = Type.getType(sig);
  458. }
  459. catch (ClassFormatError cfe){ // sometimes BCEL is a little harsh describing exceptional situations.
  460. throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
  461. }
  462. String nameanddesc = (name+sig);
  463. if (field_names_and_desc.contains(nameanddesc)){
  464. throw new ClassConstraintException("No two fields (like '"+tostring(obj)+"') are allowed have same names and descriptors!");
  465. }
  466. if (field_names.contains(name)){
  467. addMessage("More than one field of name '"+name+"' detected (but with different type descriptors). This is very unusual.");
  468. }
  469. field_names_and_desc.add(nameanddesc);
  470. field_names.add(name);
  471. Attribute[] atts = obj.getAttributes();
  472. for (int i=0; i<atts.length; i++){
  473. if ((! (atts[i] instanceof ConstantValue)) &&
  474. (! (atts[i] instanceof Synthetic)) &&
  475. (! (atts[i] instanceof Deprecated))){
  476. addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Field '"+tostring(obj)+"' is unknown and will therefore be ignored.");
  477. }
  478. if (! (atts[i] instanceof ConstantValue)){
  479. addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Field '"+tostring(obj)+"' is not a ConstantValue and is therefore only of use for debuggers and such.");
  480. }
  481. }
  482. }
  483. ///////////////////////////
  484. // METHODS (vmspec2 4.6) //
  485. ///////////////////////////
  486. public void visitMethod(Method obj){
  487. checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
  488. String name = obj.getName();
  489. if (! validMethodName(name, true)){
  490. throw new ClassConstraintException("Method '"+tostring(obj)+"' has illegal name '"+name+"'.");
  491. }
  492. // A descriptor is often named signature in BCEL
  493. checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
  494. String sig = ((ConstantUtf8) (cp.getConstant(obj.getSignatureIndex()))).getBytes(); // Method's signature(=descriptor)
  495. Type t;
  496. Type[] ts; // needed below the try block.
  497. try{
  498. t = Type.getReturnType(sig);
  499. ts = Type.getArgumentTypes(sig);
  500. }
  501. catch (ClassFormatError cfe){
  502. // Well, BCEL sometimes is a little harsh describing exceptional situations.
  503. throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by Method '"+tostring(obj)+"'.");
  504. }
  505. // Check if referenced objects exist.
  506. Type act = t;
  507. if (act instanceof ArrayType) act = ((ArrayType) act).getBasicType();
  508. if (act instanceof ObjectType){
  509. Verifier v = VerifierFactory.getVerifier( ((ObjectType) act).getClassName() );
  510. VerificationResult vr = v.doPass1();
  511. if (vr != VerificationResult.VR_OK) {
  512. throw new ClassConstraintException("Method '"+tostring(obj)+"' has a return type that does not pass verification pass 1: '"+vr+"'.");
  513. }
  514. }
  515. for (int i=0; i<ts.length; i++){
  516. act = ts[i];
  517. if (act instanceof ArrayType) act = ((ArrayType) act).getBasicType();
  518. if (act instanceof ObjectType){
  519. Verifier v = VerifierFactory.getVerifier( ((ObjectType) act).getClassName() );
  520. VerificationResult vr = v.doPass1();
  521. if (vr != VerificationResult.VR_OK) {
  522. throw new ClassConstraintException("Method '"+tostring(obj)+"' has an argument type that does not pass verification pass 1: '"+vr+"'.");
  523. }
  524. }
  525. }
  526. // Nearly forgot this! Funny return values are allowed, but a non-empty arguments list makes a different method out of it!
  527. if (name.equals(STATIC_INITIALIZER_NAME) && (ts.length != 0)){
  528. throw new ClassConstraintException("Method '"+tostring(obj)+"' has illegal name '"+name+"'. It's name resembles the class or interface initialization method which it isn't because of its arguments (==descriptor).");
  529. }
  530. if (jc.isClass()){
  531. int maxone=0;
  532. if (obj.isPrivate()) maxone++;
  533. if (obj.isProtected()) maxone++;
  534. if (obj.isPublic()) maxone++;
  535. if (maxone > 1){
  536. throw new ClassConstraintException("Method '"+tostring(obj)+"' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set.");
  537. }
  538. if (obj.isAbstract()){
  539. if (obj.isFinal()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_FINAL modifier set.");
  540. if (obj.isNative()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_NATIVE modifier set.");
  541. if (obj.isPrivate()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_PRIVATE modifier set.");
  542. if (obj.isStatic()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_STATIC modifier set.");
  543. if (obj.isStrictfp()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_STRICT modifier set.");
  544. if (obj.isSynchronized()) throw new ClassConstraintException("Abstract method '"+tostring(obj)+"' must not have the ACC_SYNCHRONIZED modifier set.");
  545. }
  546. }
  547. else{ // isInterface!
  548. if (!name.equals(STATIC_INITIALIZER_NAME)){//vmspec2, p.116, 2nd paragraph
  549. if (!obj.isPublic()){
  550. throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must have the ACC_PUBLIC modifier set but hasn't!");
  551. }
  552. if (!obj.isAbstract()){
  553. throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must have the ACC_STATIC modifier set but hasn't!");
  554. }
  555. if ( obj.isPrivate() ||
  556. obj.isProtected() ||
  557. obj.isStatic() ||
  558. obj.isFinal() ||
  559. obj.isSynchronized() ||
  560. obj.isNative() ||
  561. obj.isStrictfp() ){
  562. throw new ClassConstraintException("Interface method '"+tostring(obj)+"' must not have any of the ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT modifiers set.");
  563. }
  564. }
  565. }
  566. // A specific instance initialization method... (vmspec2,Page 116).
  567. if (name.equals(CONSTRUCTOR_NAME)){
  568. //..may have at most one of ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC set: is checked above.
  569. //..may also have ACC_STRICT set, but none of the other flags in table 4.5 (vmspec2, page 115)
  570. if ( obj.isStatic() ||
  571. obj.isFinal() ||
  572. obj.isSynchronized() ||
  573. obj.isNative() ||
  574. obj.isAbstract() ){
  575. throw new ClassConstraintException("Instance initialization method '"+tostring(obj)+"' must not have any of the ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT modifiers set.");
  576. }
  577. }
  578. // Class and interface initialization methods...
  579. if (name.equals(STATIC_INITIALIZER_NAME)){
  580. if ((obj.getAccessFlags() & (~ACC_STRICT)) > 0){
  581. addMessage("Class or interface initialization method '"+tostring(obj)+"' has superfluous access modifier(s) set: everything but ACC_STRICT is ignored.");
  582. }
  583. if (obj.isAbstract()){
  584. throw new ClassConstraintException("Class or interface initialization method '"+tostring(obj)+"' must not be abstract. This contradicts the Java Language Specification, Second Edition (which omits this constraint) but is common practice of existing verifiers.");
  585. }
  586. }
  587. if ((obj.getAccessFlags() & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) > 0){
  588. addMessage("Method '"+tostring(obj)+"' has access flag(s) other than ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT set (ignored).");
  589. }
  590. String nameanddesc = (name+sig);
  591. if (method_names_and_desc.contains(nameanddesc)){
  592. throw new ClassConstraintException("No two methods (like '"+tostring(obj)+"') are allowed have same names and desciptors!");
  593. }
  594. method_names_and_desc.add(nameanddesc);
  595. Attribute[] atts = obj.getAttributes();
  596. int num_code_atts = 0;
  597. for (int i=0; i<atts.length; i++){
  598. if ((! (atts[i] instanceof Code)) &&
  599. (! (atts[i] instanceof ExceptionTable)) &&
  600. (! (atts[i] instanceof Synthetic)) &&
  601. (! (atts[i] instanceof Deprecated))){
  602. addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Method '"+tostring(obj)+"' is unknown and will therefore be ignored.");
  603. }
  604. if ((! (atts[i] instanceof Code)) &&
  605. (! (atts[i] instanceof ExceptionTable))){
  606. addMessage("Attribute '"+tostring(atts[i])+"' as an attribute of Method '"+tostring(obj)+"' is neither Code nor Exceptions and is therefore only of use for debuggers and such.");
  607. }
  608. if ((atts[i] instanceof Code) && (obj.isNative() || obj.isAbstract())){
  609. throw new ClassConstraintException("Native or abstract methods like '"+tostring(obj)+"' must not have a Code attribute like '"+tostring(atts[i])+"'."); //vmspec2 page120, 4.7.3
  610. }
  611. if (atts[i] instanceof Code) num_code_atts++;
  612. }
  613. if ( !obj.isNative() && !obj.isAbstract() && num_code_atts != 1){
  614. throw new ClassConstraintException("Non-native, non-abstract methods like '"+tostring(obj)+"' must have exactly one Code attribute (found: "+num_code_atts+").");
  615. }
  616. }
  617. ///////////////////////////////////////////////////////
  618. // ClassFile-structure-ATTRIBUTES (vmspec2 4.1, 4.7) //
  619. ///////////////////////////////////////////////////////
  620. public void visitSourceFile(SourceFile obj){//vmspec2 4.7.7
  621. // zero or one SourceFile attr per ClassFile: see visitJavaClass()
  622. checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
  623. String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
  624. if (! name.equals("SourceFile")){
  625. throw new ClassConstraintException("The SourceFile attribute '"+tostring(obj)+"' is not correctly named 'SourceFile' but '"+name+"'.");
  626. }
  627. checkIndex(obj, obj.getSourceFileIndex(), CONST_Utf8);
  628. String sourcefilename = ((ConstantUtf8) cp.getConstant(obj.getSourceFileIndex())).getBytes(); //==obj.getSourceFileName() ?
  629. String sourcefilenamelc = sourcefilename.toLowerCase();
  630. if ( (sourcefilename.indexOf('/') != -1) ||
  631. (sourcefilename.indexOf('\\') != -1) ||
  632. (sourcefilename.indexOf(':') != -1) ||
  633. (sourcefilenamelc.lastIndexOf(".java") == -1) ){
  634. addMessage("SourceFile attribute '"+tostring(obj)+"' has a funny name: remember not to confuse certain parsers working on javap's output. Also, this name ('"+sourcefilename+"') is considered an unqualified (simple) file name only.");
  635. }
  636. }
  637. public void visitDeprecated(Deprecated obj){//vmspec2 4.7.10
  638. checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
  639. String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
  640. if (! name.equals("Deprecated")){
  641. throw new ClassConstraintException("The Deprecated attribute '"+tostring(obj)+"' is not correctly named 'Deprecated' but '"+name+"'.");
  642. }
  643. }
  644. public void visitSynthetic(Synthetic obj){//vmspec2 4.7.6
  645. checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
  646. String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
  647. if (! name.equals("Synthetic")){
  648. throw new ClassConstraintException("The Synthetic attribute '"+tostring(obj)+"' is not correctly named 'Synthetic' but '"+name+"'.");
  649. }
  650. }
  651. public void visitInnerClasses(InnerClasses obj){//vmspec2 4.7.5
  652. // exactly one InnerClasses attr per ClassFile if some inner class is refernced: see visitJavaClass()
  653. checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
  654. String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
  655. if (! name.equals("InnerClasses")){
  656. throw new ClassConstraintException("The InnerClasses attribute '"+tostring(obj)+"' is not correctly named 'InnerClasses' but '"+name+"'.");
  657. }
  658. InnerClass[] ics = obj.getInnerClasses();
  659. for (int i=0; i<ics.length; i++){
  660. checkIndex(obj, ics[i].getInnerClassIndex(), CONST_Class);
  661. int outer_idx = ics[i].getOuterClassIndex();
  662. if (outer_idx != 0){
  663. checkIndex(obj, outer_idx, CONST_Class);
  664. }
  665. int innername_idx = ics[i].getInnerNameIndex();
  666. if (innername_idx != 0){
  667. checkIndex(obj, innername_idx, CONST_Utf8);
  668. }
  669. int acc = ics[i].getInnerAccessFlags();
  670. acc = acc & (~ (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT));
  671. if (acc != 0){
  672. addMessage("Unknown access flag for inner class '"+tostring(ics[i])+"' set (InnerClasses attribute '"+tostring(obj)+"').");
  673. }
  674. }
  675. // Semantical consistency is not yet checked by Sun, see vmspec2 4.7.5.
  676. // [marked TODO in JustIce]
  677. }
  678. ////////////////////////////////////////////////////////
  679. // field_info-structure-ATTRIBUTES (vmspec2 4.5, 4.7) //
  680. ////////////////////////////////////////////////////////
  681. public void visitConstantValue(ConstantValue obj){//vmspec2 4.7.2
  682. // Despite its name, this really is an Attribute,
  683. // not a constant!
  684. checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
  685. String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
  686. if (! name.equals("ConstantValue")){
  687. throw new ClassConstraintException("The ConstantValue attribute '"+tostring(obj)+"' is not correctly named 'ConstantValue' but '"+name+"'.");
  688. }
  689. Object pred = carrier.predecessor();
  690. if (pred instanceof Field){ //ConstantValue attributes are quite senseless if the predecessor is not a field.
  691. Field f = (Field) pred;
  692. // Field constraints have been checked before -- so we are safe using their type information.
  693. Type field_type = Type.getType(((ConstantUtf8) (cp.getConstant(f.getSignatureIndex()))).getBytes());
  694. int index = obj.getConstantValueIndex();
  695. if ((index < 0) || (index >= cplen)){
  696. throw new ClassConstraintException("Invalid index '"+index+"' used by '"+tostring(obj)+"'.");
  697. }
  698. Constant c = cp.getConstant(index);
  699. if (CONST_Long.isInstance(c) && field_type.equals(Type.LONG)){
  700. return;
  701. }
  702. if (CONST_Float.isInstance(c) && field_type.equals(Type.FLOAT)){
  703. return;
  704. }
  705. if (CONST_Double.isInstance(c) && field_type.equals(Type.DOUBLE)){
  706. return;
  707. }
  708. if (CONST_Integer.isInstance(c) && (field_type.equals(Type.INT) || field_type.equals(Type.SHORT) || field_type.equals(Type.CHAR) || field_type.equals(Type.BYTE) || field_type.equals(Type.BOOLEAN))){
  709. return;
  710. }
  711. if (CONST_String.isInstance(c) && field_type.equals(Type.STRING)){
  712. return;
  713. }
  714. throw new ClassConstraintException("Illegal type of ConstantValue '"+obj+"' embedding Constant '"+c+"'. It is referenced by field '"+tostring(f)+"' expecting a different type: '"+field_type+"'.");
  715. }
  716. }
  717. // SYNTHETIC: see above
  718. // DEPRECATED: see above
  719. /////////////////////////////////////////////////////////
  720. // method_info-structure-ATTRIBUTES (vmspec2 4.6, 4.7) //
  721. /////////////////////////////////////////////////////////
  722. public void visitCode(Code obj){//vmspec2 4.7.3
  723. // No code attribute allowed for native or abstract methods: see visitMethod(Method).
  724. // Code array constraints are checked in Pass3 (3a and 3b).
  725. checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
  726. String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
  727. if (! name.equals("Code")){
  728. throw new ClassConstraintException("The Code attribute '"+tostring(obj)+"' is not correctly named 'Code' but '"+name+"'.");
  729. }
  730. Method m = null; // satisfy compiler
  731. if (!(carrier.predecessor() instanceof Method)){
  732. addMessage("Code attribute '"+tostring(obj)+"' is not declared in a method_info structure but in '"+carrier.predecessor()+"'. Ignored.");
  733. return;
  734. }
  735. else{
  736. m = (Method) carrier.predecessor(); // we can assume this method was visited before;
  737. // i.e. the data consistency was verified.
  738. }
  739. if (obj.getCode().length == 0){
  740. throw new ClassConstraintException("Code array of Code attribute '"+tostring(obj)+"' (method '"+m+"') must not be empty.");
  741. }
  742. //In JustIce, the check for correct offsets into the code array is delayed to Pass 3a.
  743. CodeException[] exc_table = obj.getExceptionTable();
  744. for (int i=0; i<exc_table.length; i++){
  745. int exc_index = exc_table[i].getCatchType();
  746. if (exc_index != 0){ // if 0, it catches all Throwables
  747. checkIndex(obj, exc_index, CONST_Class);
  748. ConstantClass cc = (ConstantClass) (cp.getConstant(exc_index));
  749. checkIndex(cc, cc.getNameIndex(), CONST_Utf8); // cannot be sure this ConstantClass has already been visited (checked)!
  750. String cname = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes().replace('/','.');
  751. Verifier v = VerifierFactory.getVerifier(cname);
  752. VerificationResult vr = v.doPass1();
  753. if (vr != VerificationResult.VR_OK){
  754. throw new ClassConstraintException("Code attribute '"+tostring(obj)+"' (method '"+m+"') has an exception_table entry '"+tostring(exc_table[i])+"' that references '"+cname+"' as an Exception but it does not pass verification pass 1: "+vr);
  755. }
  756. else{
  757. // We cannot safely trust any other "instanceof" mechanism. We need to transitively verify
  758. // the ancestor hierarchy.
  759. JavaClass e = Repository.lookupClass(cname);
  760. JavaClass t = Repository.lookupClass(Type.THROWABLE.getClassName());
  761. JavaClass o = Repository.lookupClass(Type.OBJECT.getClassName());
  762. while (e != o){
  763. if (e == t) break; // It's a subclass of Throwable, OKAY, leave.
  764. v = VerifierFactory.getVerifier(e.getSuperclassName());
  765. vr = v.doPass1();
  766. if (vr != VerificationResult.VR_OK){
  767. throw new ClassConstraintException("Code attribute '"+tostring(obj)+"' (method '"+m+"') has an exception_table entry '"+tostring(exc_table[i])+"' that references '"+cname+"' as an Exception but '"+e.getSuperclassName()+"' in the ancestor hierachy does not pass verification pass 1: "+vr);
  768. }
  769. else{
  770. e = Repository.lookupClass(e.getSuperclassName());
  771. }
  772. }
  773. if (e != t) throw new ClassConstraintException("Code attribute '"+tostring(obj)+"' (method '"+m+"') has an exception_table entry '"+tostring(exc_table[i])+"' that references '"+cname+"' as an Exception but it is not a subclass of '"+t.getClassName()+"'.");
  774. }
  775. }
  776. }
  777. // Create object for local variables information
  778. // This is highly unelegant due to usage of the Visitor pattern.
  779. // TODO: rework it.
  780. int method_number = -1;
  781. Method[] ms = Repository.lookupClass(myOwner.getClassName()).getMethods();
  782. for (int mn=0; mn<ms.length; mn++){
  783. if (m == ms[mn]){
  784. method_number = mn;
  785. break;
  786. }
  787. }
  788. if (method_number < 0){ // Mmmmh. Can we be sure BCEL does not sometimes instantiate new objects?
  789. throw new AssertionViolatedException("Could not find a known BCEL Method object in the corresponding BCEL JavaClass object.");
  790. }
  791. localVariablesInfos[method_number] = new LocalVariablesInfo(obj.getMaxLocals());
  792. int num_of_lvt_attribs = 0;
  793. // Now iterate through the attributes the Code attribute has.
  794. Attribute[] atts = obj.getAttributes();
  795. for (int a=0; a<atts.length; a++){
  796. if ((! (atts[a] instanceof LineNumberTable)) &&
  797. (! (atts[a] instanceof LocalVariableTable))){
  798. addMessage("Attribute '"+tostring(atts[a])+"' as an attribute of Code attribute '"+tostring(obj)+"' (method '"+m+"') is unknown and will therefore be ignored.");
  799. }
  800. else{// LineNumberTable or LocalVariableTable
  801. addMessage("Attribute '"+tostring(atts[a])+"' as an attribute of Code attribute '"+tostring(obj)+"' (method '"+m+"') will effectively be ignored and is only useful for debuggers and such.");
  802. }
  803. //LocalVariableTable check (partially delayed to Pass3a).
  804. //Here because its easier to collect the information of the
  805. //(possibly more than one) LocalVariableTables belonging to
  806. //one certain Code attribute.
  807. if (atts[a] instanceof LocalVariableTable){ // checks conforming to vmspec2 4.7.9
  808. LocalVariableTable lvt = (LocalVariableTable) atts[a];
  809. checkIndex(lvt, lvt.getNameIndex(), CONST_Utf8);
  810. String lvtname = ((ConstantUtf8) cp.getConstant(lvt.getNameIndex())).getBytes();
  811. if (! lvtname.equals("LocalVariableTable")){
  812. throw new ClassConstraintException("The LocalVariableTable attribute '"+tostring(lvt)+"' is not correctly named 'LocalVariableTable' but '"+lvtname+"'.");
  813. }
  814. Code code = obj;
  815. int max_locals = code.getMaxLocals();
  816. //In JustIce, the check for correct offsets into the code array is delayed to Pass 3a.
  817. LocalVariable[] localvariables = lvt.getLocalVariableTable();
  818. for (int i=0; i<localvariables.length; i++){
  819. checkIndex(lvt, localvariables[i].getNameIndex(), CONST_Utf8);
  820. String localname = ((ConstantUtf8) cp.getConstant(localvariables[i].getNameIndex())).getBytes();
  821. if (!validJavaIdentifier(localname)){
  822. throw new ClassConstraintException("LocalVariableTable '"+tostring(lvt)+"' references a local variable by the name '"+localname+"' which is not a legal Java simple name.");
  823. }
  824. checkIndex(lvt, localvariables[i].getSignatureIndex(), CONST_Utf8);
  825. String localsig = ((ConstantUtf8) (cp.getConstant(localvariables[i].getSignatureIndex()))).getBytes(); // Local signature(=descriptor)
  826. Type t;
  827. try{
  828. t = Type.getType(localsig);
  829. }
  830. catch (ClassFormatError cfe){ // sometimes BCEL is a little harsh describing exceptional situations.
  831. throw new ClassConstraintException("Illegal descriptor (==signature) '"+localsig+"' used by LocalVariable '"+tostring(localvariables[i])+"' referenced by '"+tostring(lvt)+"'.");
  832. }
  833. int localindex = localvariables[i].getIndex();
  834. if ( ( (t==Type.LONG || t==Type.DOUBLE)? localindex+1:localindex) >= code.getMaxLocals()){
  835. throw new ClassConstraintException("LocalVariableTable attribute '"+tostring(lvt)+"' references a LocalVariable '"+tostring(localvariables[i])+"' with an index that exceeds the surrounding Code attribute's max_locals value of '"+code.getMaxLocals()+"'.");
  836. }
  837. try{
  838. localVariablesInfos[method_number].add(localindex, localname, localvariables[i].getStartPC(), localvariables[i].getLength(), t);
  839. }
  840. catch(LocalVariableInfoInconsistentException lviie){
  841. throw new ClassConstraintException("Conflicting information in LocalVariableTable '"+tostring(lvt)+"' found in Code attribute '"+tostring(obj)+"' (method '"+tostring(m)+"'). "+lviie.getMessage());
  842. }
  843. }// for all local variables localvariables[i] in the LocalVariableTable attribute atts[a] END
  844. num_of_lvt_attribs++;
  845. if (num_of_lvt_attribs > obj.getMaxLocals()){
  846. throw new ClassConstraintException("Number of LocalVariableTable attributes of Code attribute '"+tostring(obj)+"' (method '"+tostring(m)+"') exceeds number of local variable slots '"+obj.getMaxLocals()+"' ('There may be no more than one LocalVariableTable attribute per local variable in the Code attribute.').");
  847. }
  848. }// if atts[a] instanceof LocalVariableTable END
  849. }// for all attributes atts[a] END
  850. }// visitCode(Code) END
  851. public void visitExceptionTable(ExceptionTable obj){//vmspec2 4.7.4
  852. // incorrectly named, it's the Exceptions attribute (vmspec2 4.7.4)
  853. checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
  854. String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
  855. if (! name.equals("Exceptions")){
  856. throw new ClassConstraintException("The Exceptions attribute '"+tostring(obj)+"' is not correctly named 'Exceptions' but '"+name+"'.");
  857. }
  858. int[] exc_indices = obj.getExceptionIndexTable();
  859. for (int i=0; i<exc_indices.length; i++){
  860. checkIndex(obj, exc_indices[i], CONST_Class);
  861. ConstantClass cc = (ConstantClass) (cp.getConstant(exc_indices[i]));
  862. checkIndex(cc, cc.getNameIndex(), CONST_Utf8); // cannot be sure this ConstantClass has already been visited (checked)!
  863. String cname = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes().replace('/','.'); //convert internal notation on-the-fly to external notation
  864. Verifier v = VerifierFactory.getVerifier(cname);
  865. VerificationResult vr = v.doPass1();
  866. if (vr != VerificationResult.VR_OK){
  867. throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+"' as an Exception but it does not pass verification pass 1: "+vr);
  868. }
  869. else{
  870. // We cannot safely trust any other "instanceof" mechanism. We need to transitively verify
  871. // the ancestor hierarchy.
  872. JavaClass e = Repository.lookupClass(cname);
  873. JavaClass t = Repository.lookupClass(Type.THROWABLE.getClassName());
  874. JavaClass o = Repository.lookupClass(Type.OBJECT.getClassName());
  875. while (e != o){
  876. if (e == t) break; // It's a subclass of Throwable, OKAY, leave.
  877. v = VerifierFactory.getVerifier(e.getSuperclassName());
  878. vr = v.doPass1();
  879. if (vr != VerificationResult.VR_OK){
  880. throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+"' as an Exception but '"+e.getSuperclassName()+"' in the ancestor hierachy does not pass verification pass 1: "+vr);
  881. }
  882. else{
  883. e = Repository.lookupClass(e.getSuperclassName());
  884. }
  885. }
  886. if (e != t) throw new ClassConstraintException("Exceptions attribute '"+tostring(obj)+"' references '"+cname+"' as an Exception but it is not a subclass of '"+t.getClassName()+"'.");
  887. }
  888. }
  889. }
  890. // SYNTHETIC: see above
  891. // DEPRECATED: see above
  892. //////////////////////////////////////////////////////////////
  893. // code_attribute-structure-ATTRIBUTES (vmspec2 4.7.3, 4.7) //
  894. //////////////////////////////////////////////////////////////
  895. public void visitLineNumberTable(LineNumberTable obj){//vmspec2 4.7.8
  896. checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
  897. String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
  898. if (! name.equals("LineNumberTable")){
  899. throw new ClassConstraintException("The LineNumberTable attribute '"+tostring(obj)+"' is not correctly named 'LineNumberTable' but '"+name+"'.");
  900. }
  901. //In JustIce,this check is delayed to Pass 3a.
  902. //LineNumber[] linenumbers = obj.getLineNumberTable();
  903. // ...validity check...
  904. }
  905. public void visitLocalVariableTable(LocalVariableTable obj){//vmspec2 4.7.9
  906. //In JustIce,this check is partially delayed to Pass 3a.
  907. //The other part can be found in the visitCode(Code) method.
  908. }
  909. ////////////////////////////////////////////////////
  910. // MISC-structure-ATTRIBUTES (vmspec2 4.7.1, 4.7) //
  911. ////////////////////////////////////////////////////
  912. public void visitUnknown(Unknown obj){//vmspec2 4.7.1
  913. // Represents an unknown attribute.
  914. checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
  915. // Maybe only misnamed? Give a (warning) message.
  916. addMessage("Unknown attribute '"+tostring(obj)+"'. This attribute is not known in any context!");
  917. }
  918. //////////
  919. // BCEL //
  920. //////////
  921. public void visitLocalVariable(LocalVariable obj){
  922. // This does not represent an Attribute but is only
  923. // related to internal BCEL data representation.
  924. // see visitLocalVariableTable(LocalVariableTable)
  925. }
  926. public void visitCodeException(CodeException obj){
  927. // Code constraints are checked in Pass3 (3a and 3b).
  928. // This does not represent an Attribute but is only
  929. // related to internal BCEL data representation.
  930. // see visitCode(Code)
  931. }
  932. public void visitConstantPool(ConstantPool obj){
  933. // No need to. We're piggybacked by the DescendingVisitor.
  934. // This does not represent an Attribute but is only
  935. // related to internal BCEL data representation.
  936. }
  937. public void visitInnerClass(InnerClass obj){
  938. // This does not represent an Attribute but is only
  939. // related to internal BCEL data representation.
  940. }
  941. public void visitLineNumber(LineNumber obj){
  942. // This does not represent an Attribute but is only
  943. // related to internal BCEL data representation.
  944. // see visitLineNumberTable(LineNumberTable)
  945. }
  946. }
  947. /**
  948. * Ensures that the ConstantCP-subclassed entries of the constant
  949. * pool are valid. According to "Yellin: Low Level Security in Java",
  950. * this method does not verify the existence of referenced entities
  951. * (such as classes) but only the formal correctness (such as well-formed
  952. * signatures).
  953. * The visitXXX() methods throw ClassConstraintException instances otherwise.
  954. * <B>Precondition: index-style cross referencing in the constant
  955. * pool must be valid. Simply invoke constant_pool_entries_satisfy_static_constraints()
  956. * before.</B>
  957. *
  958. * @throws ClassConstraintException otherwise.
  959. * @see #constant_pool_entries_satisfy_static_constraints()
  960. */
  961. private void field_and_method_refs_are_valid(){
  962. JavaClass jc = Repository.lookupClass(myOwner.getClassName());
  963. DescendingVisitor v = new DescendingVisitor(jc, new FAMRAV_Visitor(jc));
  964. v.visit();
  965. }
  966. /**
  967. * A Visitor class that ensures the ConstantCP-subclassed entries
  968. * of the constant pool are valid.
  969. * <B>Precondition: index-style cross referencing in the constant
  970. * pool must be valid.</B>
  971. *
  972. * @see #constant_pool_entries_satisfy_static_constraints()
  973. * @see com.sun.org.apache.bcel.internal.classfile.ConstantCP
  974. */
  975. private class FAMRAV_Visitor extends EmptyVisitor implements Visitor{
  976. private final JavaClass jc;
  977. private final ConstantPool cp; // ==jc.getConstantPool() -- only here to save typing work.
  978. private FAMRAV_Visitor(JavaClass _jc){
  979. jc = _jc;
  980. cp = _jc.getConstantPool();
  981. }
  982. public void visitConstantFieldref(ConstantFieldref obj){
  983. if (obj.getTag() != Constants.CONSTANT_Fieldref){
  984. throw new ClassConstraintException("ConstantFieldref '"+tostring(obj)+"' has wrong tag!");
  985. }
  986. int name_and_type_index = obj.getNameAndTypeIndex();
  987. ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
  988. String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
  989. if (!validFieldName(name)){
  990. throw new ClassConstraintException("Invalid field name '"+name+"' referenced by '"+tostring(obj)+"'.");
  991. }
  992. int class_index = obj.getClassIndex();
  993. ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
  994. String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
  995. if (! validClassName(className)){
  996. throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
  997. }
  998. String sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
  999. try{
  1000. Type t = Type.getType(sig);
  1001. }
  1002. catch (ClassFormatError cfe){
  1003. // Well, BCEL sometimes is a little harsh describing exceptional situations.
  1004. throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
  1005. }
  1006. }
  1007. public void visitConstantMethodref(ConstantMethodref obj){
  1008. if (obj.getTag() != Constants.CONSTANT_Methodref){
  1009. throw new ClassConstraintException("ConstantMethodref '"+tostring(obj)+"' has wrong tag!");
  1010. }
  1011. int name_and_type_index = obj.getNameAndTypeIndex();
  1012. ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
  1013. String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
  1014. if (!validClassMethodName(name)){
  1015. throw new ClassConstraintException("Invalid (non-interface) method name '"+name+"' referenced by '"+tostring(obj)+"'.");
  1016. }
  1017. int class_index = obj.getClassIndex();
  1018. ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
  1019. String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
  1020. if (! validClassName(className)){
  1021. throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
  1022. }
  1023. String sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
  1024. try{
  1025. Type t = Type.getReturnType(sig);
  1026. Type[] ts = Type.getArgumentTypes(sig);
  1027. if ( name.equals(CONSTRUCTOR_NAME) && (t != Type.VOID) ){
  1028. throw new ClassConstraintException("Instance initialization method must have VOID return type.");
  1029. }
  1030. }
  1031. catch (ClassFormatError cfe){
  1032. // Well, BCEL sometimes is a little harsh describing exceptional situations.
  1033. throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
  1034. }
  1035. }
  1036. public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj){
  1037. if (obj.getTag() != Constants.CONSTANT_InterfaceMethodref){
  1038. throw new ClassConstraintException("ConstantInterfaceMethodref '"+tostring(obj)+"' has wrong tag!");
  1039. }
  1040. int name_and_type_index = obj.getNameAndTypeIndex();
  1041. ConstantNameAndType cnat = (ConstantNameAndType) (cp.getConstant(name_and_type_index));
  1042. String name = ((ConstantUtf8) (cp.getConstant(cnat.getNameIndex()))).getBytes(); // Field or Method name
  1043. if (!validInterfaceMethodName(name)){
  1044. throw new ClassConstraintException("Invalid (interface) method name '"+name+"' referenced by '"+tostring(obj)+"'.");
  1045. }
  1046. int class_index = obj.getClassIndex();
  1047. ConstantClass cc = (ConstantClass) (cp.getConstant(class_index));
  1048. String className = ((ConstantUtf8) (cp.getConstant(cc.getNameIndex()))).getBytes(); // Class Name in internal form
  1049. if (! validClassName(className)){
  1050. throw new ClassConstraintException("Illegal class name '"+className+"' used by '"+tostring(obj)+"'.");
  1051. }
  1052. String sig = ((ConstantUtf8) (cp.getConstant(cnat.getSignatureIndex()))).getBytes(); // Field or Method signature(=descriptor)
  1053. try{
  1054. Type t = Type.getReturnType(sig);
  1055. Type[] ts = Type.getArgumentTypes(sig);
  1056. if ( name.equals(STATIC_INITIALIZER_NAME) && (t != Type.VOID) ){
  1057. addMessage("Class or interface initialization method '"+STATIC_INITIALIZER_NAME+"' usually has VOID return type instead of '"+t+"'. Note this is really not a requirement of The Java Virtual Machine Specification, Second Edition.");
  1058. }
  1059. }
  1060. catch (ClassFormatError cfe){
  1061. // Well, BCEL sometimes is a little harsh describing exceptional situations.
  1062. throw new ClassConstraintException("Illegal descriptor (==signature) '"+sig+"' used by '"+tostring(obj)+"'.");
  1063. }
  1064. }
  1065. }
  1066. /**
  1067. * This method returns true if and only if the supplied String
  1068. * represents a valid Java class name.
  1069. */
  1070. private static final boolean validClassName(String name){
  1071. // Are there restrictions?
  1072. return true;
  1073. }
  1074. /**
  1075. * This method returns true if and only if the supplied String
  1076. * represents a valid method name.
  1077. * This is basically the same as a valid identifier name in the
  1078. * Java programming language, but the special name for
  1079. * the instance initialization method is allowed and the special name
  1080. * for the class/interface initialization method may be allowed.
  1081. */
  1082. private static boolean validMethodName(String name, boolean allowStaticInit){
  1083. if (validJavaLangMethodName(name)) return true;
  1084. if (allowStaticInit){
  1085. return (name.equals(CONSTRUCTOR_NAME) || name.equals(STATIC_INITIALIZER_NAME));
  1086. }
  1087. else{
  1088. return name.equals(CONSTRUCTOR_NAME);
  1089. }
  1090. }
  1091. /**
  1092. * This method returns true if and only if the supplied String
  1093. * represents a valid method name that may be referenced by
  1094. * ConstantMethodref objects.
  1095. */
  1096. private static boolean validClassMethodName(String name){
  1097. return validMethodName(name, false);
  1098. }
  1099. /**
  1100. * This method returns true if and only if the supplied String
  1101. * represents a valid Java programming language method name stored as a simple
  1102. * (non-qualified) name.
  1103. * Conforming to: The Java Virtual Machine Specification, Second Edition, ÷2.7, ÷2.7.1, ÷2.2.
  1104. */
  1105. private static boolean validJavaLangMethodName(String name){
  1106. if (!Character.isJavaIdentifierStart(name.charAt(0))) return false;
  1107. for (int i=1; i<name.length(); i++){
  1108. if (!Character.isJavaIdentifierPart(name.charAt(i))) return false;
  1109. }
  1110. return true;
  1111. }
  1112. /**
  1113. * This method returns true if and only if the supplied String
  1114. * represents a valid Java interface method name that may be
  1115. * referenced by ConstantInterfaceMethodref objects.
  1116. */
  1117. private static boolean validInterfaceMethodName(String name){
  1118. // I guess we should assume special names forbidden here.
  1119. if (name.startsWith("<")) return false;
  1120. return validJavaLangMethodName(name);
  1121. }
  1122. /**
  1123. * This method returns true if and only if the supplied String
  1124. * represents a valid Java identifier (so-called simple name).
  1125. */
  1126. private static boolean validJavaIdentifier(String name){
  1127. // vmspec2 2.7, vmspec2 2.2
  1128. if (!Character.isJavaIdentifierStart(name.charAt(0))) return false;
  1129. for (int i=1; i<name.length(); i++){
  1130. if (!Character.isJavaIdentifierPart(name.charAt(i))) return false;
  1131. }
  1132. return true;
  1133. }
  1134. /**
  1135. * This method returns true if and only if the supplied String
  1136. * represents a valid Java field name.
  1137. */
  1138. private static boolean validFieldName(String name){
  1139. // vmspec2 2.7, vmspec2 2.2
  1140. return validJavaIdentifier(name);
  1141. }
  1142. /**
  1143. * This class serves for finding out if a given JavaClass' ConstantPool
  1144. * references an Inner Class.
  1145. * The Java Virtual Machine Specification, Second Edition is not very precise
  1146. * about when an "InnerClasses" attribute has to appear. However, it states that
  1147. * there has to be exactly one InnerClasses attribute in the ClassFile structure
  1148. * if the constant pool of a class or interface refers to any class or interface
  1149. * "that is not a member of a package". Sun does not mean "member of the default
  1150. * package". In "Inner Classes Specification" they point out how a "bytecode name"
  1151. * is derived so one has to deduce what a class name of a class "that is not a
  1152. * member of a package" looks like: there is at least one character in the byte-
  1153. * code name that cannot be part of a legal Java Language Class name (and not equal
  1154. * to '/'). This assumption is wrong as the delimiter is '$' for which
  1155. * Character.isJavaIdentifierPart() == true.
  1156. * Hence, you really run into trouble if you have a toplevel class called
  1157. * "A$XXX" and another toplevel class called "A" with in inner class called "XXX".
  1158. * JustIce cannot repair this; please note that existing verifiers at this
  1159. * time even fail to detect missing InnerClasses attributes in pass 2.
  1160. */
  1161. private class InnerClassDetector extends EmptyVisitor{
  1162. private boolean hasInnerClass = false;
  1163. private JavaClass jc;
  1164. private ConstantPool cp;
  1165. private InnerClassDetector(){} // Don't use.
  1166. /** Constructs an InnerClassDetector working on the JavaClass _jc. */
  1167. public InnerClassDetector(JavaClass _jc){
  1168. jc = _jc;
  1169. cp = jc.getConstantPool();
  1170. (new DescendingVisitor(jc, this)).visit();
  1171. }
  1172. /**
  1173. * Returns if the JavaClass this InnerClassDetector is working on
  1174. * has an Inner Class reference in its constant pool.
  1175. */
  1176. public boolean innerClassReferenced(){
  1177. return hasInnerClass;
  1178. }
  1179. /** This method casually visits ConstantClass references. */
  1180. public void visitConstantClass(ConstantClass obj){
  1181. Constant c = cp.getConstant(obj.getNameIndex());
  1182. if (c instanceof ConstantUtf8){ //Ignore the case where it's not a ConstantUtf8 here, we'll find out later.
  1183. String classname = ((ConstantUtf8) c).getBytes();
  1184. if (classname.startsWith(jc.getClassName().replace('.','/')+"$")){
  1185. hasInnerClass = true;
  1186. }
  1187. }
  1188. }
  1189. }
  1190. /**
  1191. * This method is here to save typing work and improve code readability.
  1192. */
  1193. private static String tostring(Node n){
  1194. return new StringRepresentation(n).toString();
  1195. }
  1196. }