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.

MethodGen.java 35KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169
  1. package org.aspectj.apache.bcel.generic;
  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.ArrayList;
  56. import java.util.Hashtable;
  57. import java.util.Iterator;
  58. import java.util.List;
  59. import java.util.Stack;
  60. import org.aspectj.apache.bcel.Constants;
  61. import org.aspectj.apache.bcel.classfile.Attribute;
  62. import org.aspectj.apache.bcel.classfile.Code;
  63. import org.aspectj.apache.bcel.classfile.CodeException;
  64. import org.aspectj.apache.bcel.classfile.ConstantPool;
  65. import org.aspectj.apache.bcel.classfile.ExceptionTable;
  66. import org.aspectj.apache.bcel.classfile.LineNumber;
  67. import org.aspectj.apache.bcel.classfile.LineNumberTable;
  68. import org.aspectj.apache.bcel.classfile.LocalVariable;
  69. import org.aspectj.apache.bcel.classfile.LocalVariableTable;
  70. import org.aspectj.apache.bcel.classfile.Method;
  71. import org.aspectj.apache.bcel.classfile.Utility;
  72. import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen;
  73. import org.aspectj.apache.bcel.classfile.annotation.RuntimeAnnos;
  74. import org.aspectj.apache.bcel.classfile.annotation.RuntimeParamAnnos;
  75. /**
  76. * Template class for building up a method. This is done by defining exception handlers, adding thrown exceptions, local variables
  77. * and attributes, whereas the 'LocalVariableTable' and 'LineNumberTable' attributes will be set automatically for the code. Use
  78. * stripAttributes() if you don't like this.
  79. *
  80. * While generating code it may be necessary to insert NOP operations. You can use the `removeNOPs' method to get rid off them. The
  81. * resulting method object can be obtained via the `getMethod()' method.
  82. *
  83. * @version $Id: MethodGen.java,v 1.17 2011/05/19 23:23:46 aclement Exp $
  84. * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
  85. * @author <A HREF="http://www.vmeng.com/beard">Patrick C. Beard</A> [setMaxStack()]
  86. * @see InstructionList
  87. * @see Method
  88. */
  89. public class MethodGen extends FieldGenOrMethodGen {
  90. private String classname;
  91. private Type[] parameterTypes;
  92. private String[] parameterNames;
  93. private int maxLocals;
  94. private int maxStack;
  95. private InstructionList il;
  96. // Indicates whether to produce code attributes for LineNumberTable and LocalVariableTable, like javac -O
  97. private boolean stripAttributes;
  98. private int highestLineNumber = 0;
  99. private ArrayList<LocalVariableGen> localVariablesList = new ArrayList<LocalVariableGen>();
  100. private ArrayList<LineNumberGen> lineNumbersList = new ArrayList<LineNumberGen>();
  101. private ArrayList<CodeExceptionGen> exceptionsList = new ArrayList<CodeExceptionGen>();
  102. private ArrayList<String> exceptionsThrown = new ArrayList<String>();
  103. private ArrayList<Attribute> codeAttributesList = new ArrayList<Attribute>();
  104. private List<AnnotationGen>[] param_annotations; // Array of lists containing AnnotationGen objects
  105. private boolean hasParameterAnnotations = false;
  106. private boolean haveUnpackedParameterAnnotations = false;
  107. /**
  108. * Declare method. If the method is non-static the constructor automatically declares a local variable `$this' in slot 0. The
  109. * actual code is contained in the `il' parameter, which may further manipulated by the user. But he must take care not to
  110. * remove any instruction (handles) that are still referenced from this object.
  111. *
  112. * For example one may not add a local variable and later remove the instructions it refers to without causing havoc. It is safe
  113. * however if you remove that local variable, too.
  114. *
  115. * @param access_flags access qualifiers
  116. * @param return_type method type
  117. * @param arg_types argument types
  118. * @param arg_names argument names (if this is null, default names will be provided for them)
  119. * @param method_name name of method
  120. * @param class_name class name containing this method (may be null, if you don't care)
  121. * @param il instruction list associated with this method, may be null only for abstract or native methods
  122. * @param cp constant pool
  123. */
  124. public MethodGen(int access_flags, Type return_type, Type[] arg_types, String[] arg_names, String method_name,
  125. String class_name, InstructionList il, ConstantPool cp) {
  126. this.modifiers = access_flags;
  127. this.type = return_type;
  128. this.parameterTypes = arg_types;
  129. this.parameterNames = arg_names;
  130. this.name = method_name;
  131. this.classname = class_name;
  132. this.il = il;
  133. this.cp = cp;
  134. // OPTIMIZE this code messes with the local variables - do we need it?
  135. // boolean abstract_ = isAbstract() || isNative();
  136. // InstructionHandle start = null;
  137. // InstructionHandle end = null;
  138. //
  139. // if (!abstract_) {
  140. // start = il.getStart();
  141. // end = il.getEnd();
  142. //
  143. // /* Add local variables, namely the implicit `this' and the arguments
  144. // */
  145. // // if(!isStatic() && (class_name != null)) { // Instance method -> `this' is local var 0
  146. // // addLocalVariable("this", new ObjectType(class_name), start, end);
  147. // // }
  148. // }
  149. // if(arg_types != null) {
  150. // int size = arg_types.length;
  151. //
  152. // for(int i=0; i < size; i++) {
  153. // if(Type.VOID == arg_types[i]) {
  154. // throw new ClassGenException("'void' is an illegal argument type for a method");
  155. // }
  156. // }
  157. //
  158. // if(arg_names != null) { // Names for variables provided?
  159. // if(size != arg_names.length)
  160. // throw new ClassGenException("Mismatch in argument array lengths: " +
  161. // size + " vs. " + arg_names.length);
  162. // } else { // Give them dummy names
  163. // // arg_names = new String[size];
  164. // //
  165. // // for(int i=0; i < size; i++)
  166. // // arg_names[i] = "arg" + i;
  167. // //
  168. // // setArgumentNames(arg_names);
  169. // }
  170. // if(!abstract_) {
  171. // for(int i=0; i < size; i++) {
  172. // // addLocalVariable(arg_names[i], arg_types[i], start, end);
  173. // }
  174. // }
  175. // }
  176. }
  177. public int getHighestlinenumber() {
  178. return highestLineNumber;
  179. }
  180. /**
  181. * Instantiate from existing method.
  182. *
  183. * @param m method
  184. * @param class_name class name containing this method
  185. * @param cp constant pool
  186. */
  187. public MethodGen(Method m, String class_name, ConstantPool cp) {
  188. this(m, class_name, cp, false);
  189. }
  190. // OPTIMIZE should always use tags and never anything else!
  191. public MethodGen(Method m, String class_name, ConstantPool cp, boolean useTags) {
  192. this(m.getModifiers(),
  193. // OPTIMIZE implementation of getReturnType() and getArgumentTypes() on Method seems weak
  194. m.getReturnType(), m.getArgumentTypes(), null /* may be overridden anyway */, m.getName(), class_name, ((m
  195. .getModifiers() & (Constants.ACC_ABSTRACT | Constants.ACC_NATIVE)) == 0) ? new InstructionList(m.getCode()
  196. .getCode()) : null, cp);
  197. Attribute[] attributes = m.getAttributes();
  198. for (int i = 0; i < attributes.length; i++) {
  199. Attribute a = attributes[i];
  200. if (a instanceof Code) {
  201. Code code = (Code) a;
  202. setMaxStack(code.getMaxStack());
  203. setMaxLocals(code.getMaxLocals());
  204. CodeException[] ces = code.getExceptionTable();
  205. InstructionHandle[] arrayOfInstructions = il.getInstructionsAsArray();
  206. // process the exception table
  207. // -
  208. if (ces != null) {
  209. for (CodeException ce : ces) {
  210. int type = ce.getCatchType();
  211. ObjectType catchType = null;
  212. if (type > 0) {
  213. String cen = m.getConstantPool().getConstantString_CONSTANTClass(type);
  214. catchType = new ObjectType(cen);
  215. }
  216. int end_pc = ce.getEndPC();
  217. int length = m.getCode().getCode().length;
  218. InstructionHandle end;
  219. if (length == end_pc) { // May happen, because end_pc is exclusive
  220. end = il.getEnd();
  221. } else {
  222. end = il.findHandle(end_pc, arrayOfInstructions);// il.findHandle(end_pc);
  223. end = end.getPrev(); // Make it inclusive
  224. }
  225. addExceptionHandler(il.findHandle(ce.getStartPC(), arrayOfInstructions), end, il.findHandle(ce
  226. .getHandlerPC(), arrayOfInstructions), catchType);
  227. }
  228. }
  229. Attribute[] codeAttrs = code.getAttributes();
  230. for (int j = 0; j < codeAttrs.length; j++) {
  231. a = codeAttrs[j];
  232. if (a instanceof LineNumberTable) {
  233. LineNumber[] ln = ((LineNumberTable) a).getLineNumberTable();
  234. if (useTags) {
  235. // abracadabra, lets create tags rather than linenumbergens.
  236. for (int k = 0; k < ln.length; k++) {
  237. LineNumber l = ln[k];
  238. int lnum = l.getLineNumber();
  239. if (lnum > highestLineNumber) {
  240. highestLineNumber = lnum;
  241. }
  242. LineNumberTag lt = new LineNumberTag(lnum);
  243. il.findHandle(l.getStartPC(), arrayOfInstructions, true).addTargeter(lt);
  244. }
  245. } else {
  246. for (int k = 0; k < ln.length; k++) {
  247. LineNumber l = ln[k];
  248. addLineNumber(il.findHandle(l.getStartPC(), arrayOfInstructions, true), l.getLineNumber());
  249. }
  250. }
  251. } else if (a instanceof LocalVariableTable) {
  252. // Lets have a go at creating Tags directly
  253. if (useTags) {
  254. LocalVariable[] lv = ((LocalVariableTable) a).getLocalVariableTable();
  255. for (int k = 0; k < lv.length; k++) {
  256. LocalVariable l = lv[k];
  257. Type t = Type.getType(l.getSignature());
  258. LocalVariableTag lvt = new LocalVariableTag(t, l.getSignature(), l.getName(), l.getIndex(), l
  259. .getStartPC());
  260. InstructionHandle start = il.findHandle(l.getStartPC(), arrayOfInstructions, true);
  261. byte b = t.getType();
  262. if (b != Constants.T_ADDRESS) {
  263. int increment = t.getSize();
  264. if (l.getIndex() + increment > maxLocals) {
  265. maxLocals = l.getIndex() + increment;
  266. }
  267. }
  268. int end = l.getStartPC() + l.getLength();
  269. do {
  270. start.addTargeter(lvt);
  271. start = start.getNext();
  272. } while (start != null && start.getPosition() < end);
  273. }
  274. } else {
  275. LocalVariable[] lv = ((LocalVariableTable) a).getLocalVariableTable();
  276. removeLocalVariables();
  277. for (int k = 0; k < lv.length; k++) {
  278. LocalVariable l = lv[k];
  279. InstructionHandle start = il.findHandle(l.getStartPC(), arrayOfInstructions);
  280. InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength(), arrayOfInstructions);
  281. // AMC, this actually gives us the first instruction AFTER the range,
  282. // so move back one... (findHandle can't cope with mid-instruction indices)
  283. if (end != null) {
  284. end = end.getPrev();
  285. }
  286. // Repair malformed handles
  287. if (null == start) {
  288. start = il.getStart();
  289. }
  290. if (null == end) {
  291. end = il.getEnd();
  292. }
  293. addLocalVariable(l.getName(), Type.getType(l.getSignature()), l.getIndex(), start, end);
  294. }
  295. }
  296. } else {
  297. addCodeAttribute(a);
  298. }
  299. }
  300. } else if (a instanceof ExceptionTable) {
  301. String[] names = ((ExceptionTable) a).getExceptionNames();
  302. for (int j = 0; j < names.length; j++) {
  303. addException(names[j]);
  304. }
  305. } else if (a instanceof RuntimeAnnos) {
  306. RuntimeAnnos runtimeAnnotations = (RuntimeAnnos) a;
  307. List<AnnotationGen> l = runtimeAnnotations.getAnnotations();
  308. annotationList.addAll(l);
  309. // for (Iterator<AnnotationGen> it = l.iterator(); it.hasNext();) {
  310. // AnnotationGen element = it.next();
  311. // addAnnotation(new AnnotationGen(element, cp, false));
  312. // }
  313. } else {
  314. addAttribute(a);
  315. }
  316. }
  317. }
  318. public LocalVariableGen addLocalVariable(String name, Type type, int slot, InstructionHandle start, InstructionHandle end) {
  319. int size = type.getSize();
  320. if (slot + size > maxLocals) {
  321. maxLocals = slot + size;
  322. }
  323. LocalVariableGen l = new LocalVariableGen(slot, name, type, start, end);
  324. int i = localVariablesList.indexOf(l);
  325. if (i >= 0) {
  326. localVariablesList.set(i, l); // Overwrite if necessary
  327. } else {
  328. localVariablesList.add(l);
  329. }
  330. return l;
  331. }
  332. /**
  333. * Adds a local variable to this method and assigns an index automatically.
  334. *
  335. * @param name variable name
  336. * @param type variable type
  337. * @param start from where the variable is valid, if this is null, it is valid from the start
  338. * @param end until where the variable is valid, if this is null, it is valid to the end
  339. * @return new local variable object
  340. * @see LocalVariable
  341. */
  342. public LocalVariableGen addLocalVariable(String name, Type type, InstructionHandle start, InstructionHandle end) {
  343. return addLocalVariable(name, type, maxLocals, start, end);
  344. }
  345. /**
  346. * Remove a local variable, its slot will not be reused, if you do not use addLocalVariable with an explicit index argument.
  347. */
  348. public void removeLocalVariable(LocalVariableGen l) {
  349. localVariablesList.remove(l);
  350. }
  351. /**
  352. * Remove all local variables.
  353. */
  354. public void removeLocalVariables() {
  355. localVariablesList.clear();
  356. }
  357. /**
  358. * Sort local variables by index
  359. */
  360. private static final void sort(LocalVariableGen[] vars, int l, int r) {
  361. int i = l, j = r;
  362. int m = vars[(l + r) / 2].getIndex();
  363. LocalVariableGen h;
  364. do {
  365. while (vars[i].getIndex() < m) {
  366. i++;
  367. }
  368. while (m < vars[j].getIndex()) {
  369. j--;
  370. }
  371. if (i <= j) {
  372. h = vars[i];
  373. vars[i] = vars[j];
  374. vars[j] = h; // Swap elements
  375. i++;
  376. j--;
  377. }
  378. } while (i <= j);
  379. if (l < j) {
  380. sort(vars, l, j);
  381. }
  382. if (i < r) {
  383. sort(vars, i, r);
  384. }
  385. }
  386. /*
  387. * If the range of the variable has not been set yet, it will be set to be valid from the start to the end of the instruction
  388. * list.
  389. *
  390. * @return array of declared local variables sorted by index
  391. */
  392. public LocalVariableGen[] getLocalVariables() {
  393. int size = localVariablesList.size();
  394. LocalVariableGen[] lg = new LocalVariableGen[size];
  395. localVariablesList.toArray(lg);
  396. for (int i = 0; i < size; i++) {
  397. if (lg[i].getStart() == null) {
  398. lg[i].setStart(il.getStart());
  399. }
  400. if (lg[i].getEnd() == null) {
  401. lg[i].setEnd(il.getEnd());
  402. }
  403. }
  404. if (size > 1) {
  405. sort(lg, 0, size - 1);
  406. }
  407. return lg;
  408. }
  409. /**
  410. * @return `LocalVariableTable' attribute of all the local variables of this method.
  411. */
  412. public LocalVariableTable getLocalVariableTable(ConstantPool cp) {
  413. LocalVariableGen[] lg = getLocalVariables();
  414. int size = lg.length;
  415. LocalVariable[] lv = new LocalVariable[size];
  416. for (int i = 0; i < size; i++) {
  417. lv[i] = lg[i].getLocalVariable(cp);
  418. }
  419. return new LocalVariableTable(cp.addUtf8("LocalVariableTable"), 2 + lv.length * 10, lv, cp);
  420. }
  421. /**
  422. * Give an instruction a line number corresponding to the source code line.
  423. *
  424. * @param ih instruction to tag
  425. * @return new line number object
  426. * @see LineNumber
  427. */
  428. public LineNumberGen addLineNumber(InstructionHandle ih, int src_line) {
  429. LineNumberGen l = new LineNumberGen(ih, src_line);
  430. lineNumbersList.add(l);
  431. return l;
  432. }
  433. /**
  434. * Remove a line number.
  435. */
  436. public void removeLineNumber(LineNumberGen l) {
  437. lineNumbersList.remove(l);
  438. }
  439. /**
  440. * Remove all line numbers.
  441. */
  442. public void removeLineNumbers() {
  443. lineNumbersList.clear();
  444. }
  445. /*
  446. * @return array of line numbers
  447. */
  448. public LineNumberGen[] getLineNumbers() {
  449. LineNumberGen[] lg = new LineNumberGen[lineNumbersList.size()];
  450. lineNumbersList.toArray(lg);
  451. return lg;
  452. }
  453. /**
  454. * @return 'LineNumberTable' attribute for all the local variables of this method.
  455. */
  456. public LineNumberTable getLineNumberTable(ConstantPool cp) {
  457. int size = lineNumbersList.size();
  458. LineNumber[] ln = new LineNumber[size];
  459. for (int i = 0; i < size; i++) {
  460. ln[i] = lineNumbersList.get(i).getLineNumber();
  461. }
  462. return new LineNumberTable(cp.addUtf8("LineNumberTable"), 2 + ln.length * 4, ln, cp);
  463. }
  464. /**
  465. * Add an exception handler, i.e., specify region where a handler is active and an instruction where the actual handling is
  466. * done.
  467. *
  468. * @param start_pc Start of region (inclusive)
  469. * @param end_pc End of region (inclusive)
  470. * @param handler_pc Where handling is done
  471. * @param catch_type class type of handled exception or null if any exception is handled
  472. * @return new exception handler object
  473. */
  474. public CodeExceptionGen addExceptionHandler(InstructionHandle start_pc, InstructionHandle end_pc, InstructionHandle handler_pc,
  475. ObjectType catch_type) {
  476. if ((start_pc == null) || (end_pc == null) || (handler_pc == null)) {
  477. throw new ClassGenException("Exception handler target is null instruction");
  478. }
  479. CodeExceptionGen c = new CodeExceptionGen(start_pc, end_pc, handler_pc, catch_type);
  480. exceptionsList.add(c);
  481. return c;
  482. }
  483. /**
  484. * Remove an exception handler.
  485. */
  486. public void removeExceptionHandler(CodeExceptionGen c) {
  487. exceptionsList.remove(c);
  488. }
  489. /**
  490. * Remove all line numbers.
  491. */
  492. public void removeExceptionHandlers() {
  493. exceptionsList.clear();
  494. }
  495. /*
  496. * @return array of declared exception handlers
  497. */
  498. public CodeExceptionGen[] getExceptionHandlers() {
  499. CodeExceptionGen[] cg = new CodeExceptionGen[exceptionsList.size()];
  500. exceptionsList.toArray(cg);
  501. return cg;
  502. }
  503. /**
  504. * @return code exceptions for `Code' attribute
  505. */
  506. private CodeException[] getCodeExceptions() {
  507. int size = exceptionsList.size();
  508. CodeException[] c_exc = new CodeException[size];
  509. try {
  510. for (int i = 0; i < size; i++) {
  511. CodeExceptionGen c = exceptionsList.get(i);
  512. c_exc[i] = c.getCodeException(cp);
  513. }
  514. } catch (ArrayIndexOutOfBoundsException e) {
  515. }
  516. return c_exc;
  517. }
  518. /**
  519. * Add an exception possibly thrown by this method.
  520. *
  521. * @param class_name (fully qualified) name of exception
  522. */
  523. public void addException(String class_name) {
  524. exceptionsThrown.add(class_name);
  525. }
  526. /**
  527. * Remove an exception.
  528. */
  529. public void removeException(String c) {
  530. exceptionsThrown.remove(c);
  531. }
  532. /**
  533. * Remove all exceptions.
  534. */
  535. public void removeExceptions() {
  536. exceptionsThrown.clear();
  537. }
  538. /*
  539. * @return array of thrown exceptions
  540. */
  541. public String[] getExceptions() {
  542. String[] e = new String[exceptionsThrown.size()];
  543. exceptionsThrown.toArray(e);
  544. return e;
  545. }
  546. /**
  547. * @return `Exceptions' attribute of all the exceptions thrown by this method.
  548. */
  549. private ExceptionTable getExceptionTable(ConstantPool cp) {
  550. int size = exceptionsThrown.size();
  551. int[] ex = new int[size];
  552. try {
  553. for (int i = 0; i < size; i++) {
  554. ex[i] = cp.addClass(exceptionsThrown.get(i));
  555. }
  556. } catch (ArrayIndexOutOfBoundsException e) {
  557. }
  558. return new ExceptionTable(cp.addUtf8("Exceptions"), 2 + 2 * size, ex, cp);
  559. }
  560. /**
  561. * Add an attribute to the code. Currently, the JVM knows about the LineNumberTable, LocalVariableTable and StackMap attributes,
  562. * where the former two will be generated automatically and the latter is used for the MIDP only. Other attributes will be
  563. * ignored by the JVM but do no harm.
  564. *
  565. * @param a attribute to be added
  566. */
  567. public void addCodeAttribute(Attribute a) {
  568. codeAttributesList.add(a);
  569. }
  570. public void addParameterAnnotationsAsAttribute(ConstantPool cp) {
  571. if (!hasParameterAnnotations) {
  572. return;
  573. }
  574. Attribute[] attrs = Utility.getParameterAnnotationAttributes(cp, param_annotations);
  575. if (attrs != null) {
  576. for (int i = 0; i < attrs.length; i++) {
  577. addAttribute(attrs[i]);
  578. }
  579. }
  580. }
  581. /**
  582. * Remove a code attribute.
  583. */
  584. public void removeCodeAttribute(Attribute a) {
  585. codeAttributesList.remove(a);
  586. }
  587. /**
  588. * Remove all code attributes.
  589. */
  590. public void removeCodeAttributes() {
  591. codeAttributesList.clear();
  592. }
  593. /**
  594. * @return all attributes of this method.
  595. */
  596. public Attribute[] getCodeAttributes() {
  597. Attribute[] attributes = new Attribute[codeAttributesList.size()];
  598. codeAttributesList.toArray(attributes);
  599. return attributes;
  600. }
  601. /**
  602. * Get method object. Never forget to call setMaxStack() or setMaxStack(max), respectively, before calling this method (the same
  603. * applies for max locals).
  604. *
  605. * @return method object
  606. */
  607. public Method getMethod() {
  608. String signature = getSignature();
  609. int name_index = cp.addUtf8(name);
  610. int signature_index = cp.addUtf8(signature);
  611. /*
  612. * Also updates positions of instructions, i.e., their indices
  613. */
  614. byte[] byte_code = null;
  615. if (il != null) {
  616. try {
  617. byte_code = il.getByteCode();
  618. } catch (Exception e) {
  619. throw new IllegalStateException("Unexpected problem whilst preparing bytecode for " + this.getClassName() + "."
  620. + this.getName() + this.getSignature(), e);
  621. }
  622. }
  623. LineNumberTable lnt = null;
  624. LocalVariableTable lvt = null;
  625. // J5TODO: LocalVariableTypeTable support!
  626. /*
  627. * Create LocalVariableTable and LineNumberTable attributes (for debuggers, e.g.)
  628. */
  629. if ((localVariablesList.size() > 0) && !stripAttributes) {
  630. addCodeAttribute(lvt = getLocalVariableTable(cp));
  631. }
  632. if ((lineNumbersList.size() > 0) && !stripAttributes) {
  633. addCodeAttribute(lnt = getLineNumberTable(cp));
  634. }
  635. Attribute[] code_attrs = getCodeAttributes();
  636. /*
  637. * Each attribute causes 6 additional header bytes
  638. */
  639. int attrs_len = 0;
  640. for (int i = 0; i < code_attrs.length; i++) {
  641. attrs_len += (code_attrs[i].getLength() + 6);
  642. }
  643. CodeException[] c_exc = getCodeExceptions();
  644. int exc_len = c_exc.length * 8; // Every entry takes 8 bytes
  645. Code code = null;
  646. if ((il != null) && !isAbstract()) {
  647. // Remove any stale code attribute
  648. List<Attribute> attributes = getAttributes();
  649. for (int i = 0; i < attributes.size(); i++) {
  650. Attribute a = attributes.get(i);
  651. if (a instanceof Code) {
  652. removeAttribute(a);
  653. }
  654. }
  655. code = new Code(cp.addUtf8("Code"), 8 + byte_code.length + // prologue byte code
  656. 2 + exc_len + // exceptions
  657. 2 + attrs_len, // attributes
  658. maxStack, maxLocals, byte_code, c_exc, code_attrs, cp);
  659. addAttribute(code);
  660. }
  661. addAnnotationsAsAttribute(cp);
  662. addParameterAnnotationsAsAttribute(cp);
  663. ExceptionTable et = null;
  664. if (exceptionsThrown.size() > 0) {
  665. addAttribute(et = getExceptionTable(cp)); // Add `Exceptions' if there are "throws" clauses
  666. }
  667. Method m = new Method(modifiers, name_index, signature_index, getAttributesImmutable(), cp);
  668. // Undo effects of adding attributes
  669. // OPTIMIZE why redo this? is there a better way to clean up?
  670. if (lvt != null) {
  671. removeCodeAttribute(lvt);
  672. }
  673. if (lnt != null) {
  674. removeCodeAttribute(lnt);
  675. }
  676. if (code != null) {
  677. removeAttribute(code);
  678. }
  679. if (et != null) {
  680. removeAttribute(et);
  681. }
  682. // J5TODO: Remove the annotation attributes that may have been added
  683. return m;
  684. }
  685. /**
  686. * Set maximum number of local variables.
  687. */
  688. public void setMaxLocals(int m) {
  689. maxLocals = m;
  690. }
  691. public int getMaxLocals() {
  692. return maxLocals;
  693. }
  694. /**
  695. * Set maximum stack size for this method.
  696. */
  697. public void setMaxStack(int m) {
  698. maxStack = m;
  699. }
  700. public int getMaxStack() {
  701. return maxStack;
  702. }
  703. /**
  704. * @return class that contains this method
  705. */
  706. public String getClassName() {
  707. return classname;
  708. }
  709. public void setClassName(String class_name) {
  710. this.classname = class_name;
  711. }
  712. public void setReturnType(Type return_type) {
  713. setType(return_type);
  714. }
  715. public Type getReturnType() {
  716. return getType();
  717. }
  718. public void setArgumentTypes(Type[] arg_types) {
  719. this.parameterTypes = arg_types;
  720. }
  721. public Type[] getArgumentTypes() {
  722. return this.parameterTypes;
  723. }// OPTIMIZE dont need clone here? (Type[])arg_types.clone(); }
  724. public void setArgumentType(int i, Type type) {
  725. parameterTypes[i] = type;
  726. }
  727. public Type getArgumentType(int i) {
  728. return parameterTypes[i];
  729. }
  730. public void setArgumentNames(String[] arg_names) {
  731. this.parameterNames = arg_names;
  732. }
  733. public String[] getArgumentNames() {
  734. if (parameterNames != null) {
  735. return parameterNames.clone();
  736. } else {
  737. return new String[0];
  738. }
  739. }
  740. public void setArgumentName(int i, String name) {
  741. parameterNames[i] = name;
  742. }
  743. public String getArgumentName(int i) {
  744. return parameterNames[i];
  745. }
  746. public InstructionList getInstructionList() {
  747. return il;
  748. }
  749. public void setInstructionList(InstructionList il) {
  750. this.il = il;
  751. }
  752. @Override
  753. public String getSignature() {
  754. return Utility.toMethodSignature(type, parameterTypes);
  755. }
  756. /**
  757. * Computes max. stack size by performing control flow analysis.
  758. */
  759. public void setMaxStack() {
  760. if (il != null) {
  761. maxStack = getMaxStack(cp, il, getExceptionHandlers());
  762. } else {
  763. maxStack = 0;
  764. }
  765. }
  766. /**
  767. * Compute maximum number of local variables based on the parameter count and bytecode usage of variables.
  768. */
  769. public void setMaxLocals() {
  770. setMaxLocals(false);
  771. }
  772. /**
  773. * Compute maximum number of local variables.
  774. *
  775. * @param respectLocalVariableTable if true and the local variable table indicates more are in use
  776. * than the code suggests, respect the higher value from the local variable table data.
  777. */
  778. public void setMaxLocals(boolean respectLocalVariableTable) {
  779. if (il != null) {
  780. int max = isStatic() ? 0 : 1;
  781. if (parameterTypes != null) {
  782. for (int i = 0; i < parameterTypes.length; i++) {
  783. max += parameterTypes[i].getSize();
  784. }
  785. }
  786. for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) {
  787. Instruction ins = ih.getInstruction();
  788. if ((ins instanceof InstructionLV) || (ins instanceof RET)) {
  789. int index = ins.getIndex() + ins.getType(cp).getSize();
  790. if (index > max) {
  791. max = index;
  792. }
  793. }
  794. }
  795. if (!respectLocalVariableTable || max > maxLocals) {
  796. maxLocals = max;
  797. }
  798. } else {
  799. if (!respectLocalVariableTable) {
  800. maxLocals = 0;
  801. }
  802. }
  803. }
  804. public void stripAttributes(boolean flag) {
  805. stripAttributes = flag;
  806. }
  807. static final class BranchTarget {
  808. InstructionHandle target;
  809. int stackDepth;
  810. BranchTarget(InstructionHandle target, int stackDepth) {
  811. this.target = target;
  812. this.stackDepth = stackDepth;
  813. }
  814. }
  815. static final class BranchStack {
  816. Stack<BranchTarget> branchTargets = new Stack<BranchTarget>();
  817. Hashtable<InstructionHandle, BranchTarget> visitedTargets = new Hashtable<InstructionHandle, BranchTarget>();
  818. public void push(InstructionHandle target, int stackDepth) {
  819. if (visited(target)) {
  820. return;
  821. }
  822. branchTargets.push(visit(target, stackDepth));
  823. }
  824. public BranchTarget pop() {
  825. if (!branchTargets.empty()) {
  826. BranchTarget bt = branchTargets.pop();
  827. return bt;
  828. }
  829. return null;
  830. }
  831. private final BranchTarget visit(InstructionHandle target, int stackDepth) {
  832. BranchTarget bt = new BranchTarget(target, stackDepth);
  833. visitedTargets.put(target, bt);
  834. return bt;
  835. }
  836. private final boolean visited(InstructionHandle target) {
  837. return (visitedTargets.get(target) != null);
  838. }
  839. }
  840. /**
  841. * Computes stack usage of an instruction list by performing control flow analysis.
  842. *
  843. * @return maximum stack depth used by method
  844. */
  845. public static int getMaxStack(ConstantPool cp, InstructionList il, CodeExceptionGen[] et) {
  846. BranchStack branchTargets = new BranchStack();
  847. int stackDepth = 0;
  848. int maxStackDepth = 0;
  849. /*
  850. * Initially, populate the branch stack with the exception handlers, because these aren't (necessarily) branched to
  851. * explicitly. In each case, the stack will have depth 1, containing the exception object.
  852. */
  853. for (int i = 0, max = et.length; i < max; i++) {
  854. InstructionHandle handlerPos = et[i].getHandlerPC();
  855. if (handlerPos != null) {
  856. // it must be at least 1 since there is an exception handler
  857. maxStackDepth = 1;
  858. branchTargets.push(handlerPos, 1);
  859. }
  860. }
  861. InstructionHandle ih = il.getStart();
  862. while (ih != null) {
  863. Instruction instruction = ih.getInstruction();
  864. short opcode = instruction.opcode;
  865. int prod = instruction.produceStack(cp);
  866. int con = instruction.consumeStack(cp);
  867. int delta = prod - con;
  868. stackDepth += delta;
  869. if (stackDepth > maxStackDepth) {
  870. maxStackDepth = stackDepth;
  871. }
  872. // choose the next instruction based on whether current is a branch.
  873. if (instruction instanceof InstructionBranch) {
  874. InstructionBranch branch = (InstructionBranch) instruction;
  875. if (instruction instanceof InstructionSelect) {
  876. // explore all of the select's targets. the default target is handled below.
  877. InstructionSelect select = (InstructionSelect) branch;
  878. InstructionHandle[] targets = select.getTargets();
  879. for (int i = 0; i < targets.length; i++) {
  880. branchTargets.push(targets[i], stackDepth);
  881. }
  882. // nothing to fall through to.
  883. ih = null;
  884. } else if (!(branch.isIfInstruction())) {
  885. // if an instruction that comes back to following PC,
  886. // push next instruction, with stack depth reduced by 1.
  887. if (opcode == Constants.JSR || opcode == Constants.JSR_W) {
  888. branchTargets.push(ih.getNext(), stackDepth - 1);
  889. }
  890. ih = null;
  891. }
  892. // for all branches, the target of the branch is pushed on the branch stack.
  893. // conditional branches have a fall through case, selects don't, and
  894. // jsr/jsr_w return to the next instruction.
  895. branchTargets.push(branch.getTarget(), stackDepth);
  896. } else {
  897. // check for instructions that terminate the method.
  898. if (opcode == Constants.ATHROW || opcode == Constants.RET
  899. || (opcode >= Constants.IRETURN && opcode <= Constants.RETURN)) {
  900. ih = null;
  901. }
  902. }
  903. // normal case, go to the next instruction.
  904. if (ih != null) {
  905. ih = ih.getNext();
  906. }
  907. // if we have no more instructions, see if there are any deferred branches to explore.
  908. if (ih == null) {
  909. BranchTarget bt = branchTargets.pop();
  910. if (bt != null) {
  911. ih = bt.target;
  912. stackDepth = bt.stackDepth;
  913. }
  914. }
  915. }
  916. return maxStackDepth;
  917. }
  918. /**
  919. * Return string representation close to declaration format, `public static void main(String[]) throws IOException', e.g.
  920. *
  921. * @return String representation of the method.
  922. */
  923. @Override
  924. public final String toString() {
  925. String access = Utility.accessToString(modifiers);
  926. String signature = Utility.toMethodSignature(type, parameterTypes);
  927. signature = Utility.methodSignatureToString(signature, name, access, true, getLocalVariableTable(cp));
  928. StringBuffer buf = new StringBuffer(signature);
  929. if (exceptionsThrown.size() > 0) {
  930. for (Iterator<String> e = exceptionsThrown.iterator(); e.hasNext();) {
  931. buf.append("\n\t\tthrows " + e.next());
  932. }
  933. }
  934. return buf.toString();
  935. }
  936. // J5TODO: Should param_annotations be an array of arrays? Rather than an array of lists, this
  937. // is more likely to suggest to the caller it is readonly (which a List does not).
  938. /**
  939. * Return a list of AnnotationGen objects representing parameter annotations
  940. */
  941. public List<AnnotationGen> getAnnotationsOnParameter(int i) {
  942. ensureExistingParameterAnnotationsUnpacked();
  943. if (!hasParameterAnnotations || i > parameterTypes.length) {
  944. return null;
  945. }
  946. return param_annotations[i];
  947. }
  948. /**
  949. * Goes through the attributes on the method and identifies any that are RuntimeParameterAnnotations, extracting their contents
  950. * and storing them as parameter annotations. There are two kinds of parameter annotation - visible and invisible. Once they
  951. * have been unpacked, these attributes are deleted. (The annotations will be rebuilt as attributes when someone builds a Method
  952. * object out of this MethodGen object).
  953. */
  954. private void ensureExistingParameterAnnotationsUnpacked() {
  955. if (haveUnpackedParameterAnnotations) {
  956. return;
  957. }
  958. // Find attributes that contain parameter annotation data
  959. List<Attribute> attrs = getAttributes();
  960. RuntimeParamAnnos paramAnnVisAttr = null;
  961. RuntimeParamAnnos paramAnnInvisAttr = null;
  962. for (Attribute attribute : attrs) {
  963. if (attribute instanceof RuntimeParamAnnos) {
  964. if (!hasParameterAnnotations) {
  965. param_annotations = new List[parameterTypes.length];
  966. for (int j = 0; j < parameterTypes.length; j++) {
  967. param_annotations[j] = new ArrayList<AnnotationGen>();
  968. }
  969. }
  970. hasParameterAnnotations = true;
  971. RuntimeParamAnnos rpa = (RuntimeParamAnnos) attribute;
  972. if (rpa.areVisible()) {
  973. paramAnnVisAttr = rpa;
  974. } else {
  975. paramAnnInvisAttr = rpa;
  976. }
  977. for (int j = 0; j < parameterTypes.length; j++) {
  978. // This returns Annotation[] ...
  979. AnnotationGen[] annos = rpa.getAnnotationsOnParameter(j);
  980. // ... which needs transforming into an AnnotationGen[] ...
  981. // List<AnnotationGen> mutable = makeMutableVersion(immutableArray);
  982. // ... then add these to any we already know about
  983. for (AnnotationGen anAnnotation : annos) {
  984. param_annotations[j].add(anAnnotation);
  985. }
  986. }
  987. }
  988. }
  989. if (paramAnnVisAttr != null) {
  990. removeAttribute(paramAnnVisAttr);
  991. }
  992. if (paramAnnInvisAttr != null) {
  993. removeAttribute(paramAnnInvisAttr);
  994. }
  995. haveUnpackedParameterAnnotations = true;
  996. }
  997. private List /* AnnotationGen */<AnnotationGen> makeMutableVersion(AnnotationGen[] mutableArray) {
  998. List<AnnotationGen> result = new ArrayList<AnnotationGen>();
  999. for (int i = 0; i < mutableArray.length; i++) {
  1000. result.add(new AnnotationGen(mutableArray[i], getConstantPool(), false));
  1001. }
  1002. return result;
  1003. }
  1004. public void addParameterAnnotation(int parameterIndex, AnnotationGen annotation) {
  1005. ensureExistingParameterAnnotationsUnpacked();
  1006. if (!hasParameterAnnotations) {
  1007. param_annotations = new List[parameterTypes.length];
  1008. hasParameterAnnotations = true;
  1009. }
  1010. List<AnnotationGen> existingAnnotations = param_annotations[parameterIndex];
  1011. if (existingAnnotations != null) {
  1012. existingAnnotations.add(annotation);
  1013. } else {
  1014. List<AnnotationGen> l = new ArrayList<AnnotationGen>();
  1015. l.add(annotation);
  1016. param_annotations[parameterIndex] = l;
  1017. }
  1018. }
  1019. }