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

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