You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Pass2Verifier.java 61KB

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