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.

StackMap.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. /*
  2. * Javassist, a Java-bytecode translator toolkit.
  3. * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. Alternatively, the contents of this file may be used under
  8. * the terms of the GNU Lesser General Public License Version 2.1 or later,
  9. * or the Apache License Version 2.0.
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. */
  16. package javassist.bytecode;
  17. import java.io.ByteArrayOutputStream;
  18. import java.io.DataInputStream;
  19. import java.io.IOException;
  20. import java.util.Map;
  21. import javassist.CannotCompileException;
  22. import javassist.bytecode.StackMapTable.InsertLocal;
  23. import javassist.bytecode.StackMapTable.NewRemover;
  24. import javassist.bytecode.StackMapTable.Shifter;
  25. /**
  26. * Another <code>stack_map</code> attribute defined in CLDC 1.1 for J2ME.
  27. *
  28. * <p>This is an entry in the attributes table of a Code attribute.
  29. * It was introduced by J2ME CLDC 1.1 (JSR 139) for pre-verification.
  30. *
  31. * <p>According to the CLDC specification, the sizes of some fields are not 16bit
  32. * but 32bit if the code size is more than 64K or the number of the local variables
  33. * is more than 64K. However, for the J2ME CLDC technology, they are always 16bit.
  34. * The implementation of the StackMap class assumes they are 16bit.
  35. *
  36. * @see MethodInfo#doPreverify
  37. * @see StackMapTable
  38. * @since 3.12
  39. */
  40. public class StackMap extends AttributeInfo {
  41. /**
  42. * The name of this attribute <code>"StackMap"</code>.
  43. */
  44. public static final String tag = "StackMap";
  45. /**
  46. * Constructs a <code>stack_map</code> attribute.
  47. */
  48. StackMap(ConstPool cp, byte[] newInfo) {
  49. super(cp, tag, newInfo);
  50. }
  51. StackMap(ConstPool cp, int name_id, DataInputStream in)
  52. throws IOException
  53. {
  54. super(cp, name_id, in);
  55. }
  56. /**
  57. * Returns <code>number_of_entries</code>.
  58. */
  59. public int numOfEntries() {
  60. return ByteArray.readU16bit(info, 0);
  61. }
  62. /**
  63. * <code>Top_variable_info.tag</code>.
  64. */
  65. public static final int TOP = 0;
  66. /**
  67. * <code>Integer_variable_info.tag</code>.
  68. */
  69. public static final int INTEGER = 1;
  70. /**
  71. * <code>Float_variable_info.tag</code>.
  72. */
  73. public static final int FLOAT = 2;
  74. /**
  75. * <code>Double_variable_info.tag</code>.
  76. */
  77. public static final int DOUBLE = 3;
  78. /**
  79. * <code>Long_variable_info.tag</code>.
  80. */
  81. public static final int LONG = 4;
  82. /**
  83. * <code>Null_variable_info.tag</code>.
  84. */
  85. public static final int NULL = 5;
  86. /**
  87. * <code>UninitializedThis_variable_info.tag</code>.
  88. */
  89. public static final int THIS = 6;
  90. /**
  91. * <code>Object_variable_info.tag</code>.
  92. */
  93. public static final int OBJECT = 7;
  94. /**
  95. * <code>Uninitialized_variable_info.tag</code>.
  96. */
  97. public static final int UNINIT = 8;
  98. /**
  99. * Makes a copy.
  100. */
  101. public AttributeInfo copy(ConstPool newCp, Map classnames) {
  102. Copier copier = new Copier(this, newCp, classnames);
  103. copier.visit();
  104. return copier.getStackMap();
  105. }
  106. /**
  107. * A code walker for a StackMap attribute.
  108. */
  109. public static class Walker {
  110. byte[] info;
  111. /**
  112. * Constructs a walker.
  113. */
  114. public Walker(StackMap sm) {
  115. info = sm.get();
  116. }
  117. /**
  118. * Visits each entry of the stack map frames.
  119. */
  120. public void visit() {
  121. int num = ByteArray.readU16bit(info, 0);
  122. int pos = 2;
  123. for (int i = 0; i < num; i++) {
  124. int offset = ByteArray.readU16bit(info, pos);
  125. int numLoc = ByteArray.readU16bit(info, pos + 2);
  126. pos = locals(pos + 4, offset, numLoc);
  127. int numStack = ByteArray.readU16bit(info, pos);
  128. pos = stack(pos + 2, offset, numStack);
  129. }
  130. }
  131. /**
  132. * Invoked when <code>locals</code> of <code>stack_map_frame</code>
  133. * is visited.
  134. */
  135. public int locals(int pos, int offset, int num) {
  136. return typeInfoArray(pos, offset, num, true);
  137. }
  138. /**
  139. * Invoked when <code>stack</code> of <code>stack_map_frame</code>
  140. * is visited.
  141. */
  142. public int stack(int pos, int offset, int num) {
  143. return typeInfoArray(pos, offset, num, false);
  144. }
  145. /**
  146. * Invoked when an array of <code>verification_type_info</code> is
  147. * visited.
  148. *
  149. * @param num the number of elements.
  150. * @param isLocals true if this array is for <code>locals</code>.
  151. * false if it is for <code>stack</code>.
  152. */
  153. public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
  154. for (int k = 0; k < num; k++)
  155. pos = typeInfoArray2(k, pos);
  156. return pos;
  157. }
  158. int typeInfoArray2(int k, int pos) {
  159. byte tag = info[pos];
  160. if (tag == OBJECT) {
  161. int clazz = ByteArray.readU16bit(info, pos + 1);
  162. objectVariable(pos, clazz);
  163. pos += 3;
  164. }
  165. else if (tag == UNINIT) {
  166. int offsetOfNew = ByteArray.readU16bit(info, pos + 1);
  167. uninitialized(pos, offsetOfNew);
  168. pos += 3;
  169. }
  170. else {
  171. typeInfo(pos, tag);
  172. pos++;
  173. }
  174. return pos;
  175. }
  176. /**
  177. * Invoked when an element of <code>verification_type_info</code>
  178. * (except <code>Object_variable_info</code> and
  179. * <code>Uninitialized_variable_info</code>) is visited.
  180. */
  181. public void typeInfo(int pos, byte tag) {}
  182. /**
  183. * Invoked when an element of type <code>Object_variable_info</code>
  184. * is visited.
  185. */
  186. public void objectVariable(int pos, int clazz) {}
  187. /**
  188. * Invoked when an element of type <code>Uninitialized_variable_info</code>
  189. * is visited.
  190. */
  191. public void uninitialized(int pos, int offset) {}
  192. }
  193. static class Copier extends Walker {
  194. byte[] dest;
  195. ConstPool srcCp, destCp;
  196. Map classnames;
  197. Copier(StackMap map, ConstPool newCp, Map classnames) {
  198. super(map);
  199. srcCp = map.getConstPool();
  200. dest = new byte[info.length];
  201. destCp = newCp;
  202. this.classnames = classnames;
  203. }
  204. public void visit() {
  205. int num = ByteArray.readU16bit(info, 0);
  206. ByteArray.write16bit(num, dest, 0);
  207. super.visit();
  208. }
  209. public int locals(int pos, int offset, int num) {
  210. ByteArray.write16bit(offset, dest, pos - 4);
  211. return super.locals(pos, offset, num);
  212. }
  213. public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
  214. ByteArray.write16bit(num, dest, pos - 2);
  215. return super.typeInfoArray(pos, offset, num, isLocals);
  216. }
  217. public void typeInfo(int pos, byte tag) {
  218. dest[pos] = tag;
  219. }
  220. public void objectVariable(int pos, int clazz) {
  221. dest[pos] = OBJECT;
  222. int newClazz = srcCp.copy(clazz, destCp, classnames);
  223. ByteArray.write16bit(newClazz, dest, pos + 1);
  224. }
  225. public void uninitialized(int pos, int offset) {
  226. dest[pos] = UNINIT;
  227. ByteArray.write16bit(offset, dest, pos + 1);
  228. }
  229. public StackMap getStackMap() {
  230. return new StackMap(destCp, dest);
  231. }
  232. }
  233. /**
  234. * Updates this stack map table when a new local variable is inserted
  235. * for a new parameter.
  236. *
  237. * @param index the index of the added local variable.
  238. * @param tag the type tag of that local variable.
  239. * It is available by <code>StackMapTable.typeTagOf(char)</code>.
  240. * @param classInfo the index of the <code>CONSTANT_Class_info</code> structure
  241. * in a constant pool table. This should be zero unless the tag
  242. * is <code>ITEM_Object</code>.
  243. *
  244. * @see javassist.CtBehavior#addParameter(javassist.CtClass)
  245. * @see StackMapTable#typeTagOf(char)
  246. * @see ConstPool
  247. */
  248. public void insertLocal(int index, int tag, int classInfo)
  249. throws BadBytecode
  250. {
  251. byte[] data = new InsertLocal(this, index, tag, classInfo).doit();
  252. this.set(data);
  253. }
  254. static class SimpleCopy extends Walker {
  255. Writer writer;
  256. SimpleCopy(StackMap map) {
  257. super(map);
  258. writer = new Writer();
  259. }
  260. byte[] doit() {
  261. visit();
  262. return writer.toByteArray();
  263. }
  264. public void visit() {
  265. int num = ByteArray.readU16bit(info, 0);
  266. writer.write16bit(num);
  267. super.visit();
  268. }
  269. public int locals(int pos, int offset, int num) {
  270. writer.write16bit(offset);
  271. return super.locals(pos, offset, num);
  272. }
  273. public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
  274. writer.write16bit(num);
  275. return super.typeInfoArray(pos, offset, num, isLocals);
  276. }
  277. public void typeInfo(int pos, byte tag) {
  278. writer.writeVerifyTypeInfo(tag, 0);
  279. }
  280. public void objectVariable(int pos, int clazz) {
  281. writer.writeVerifyTypeInfo(OBJECT, clazz);
  282. }
  283. public void uninitialized(int pos, int offset) {
  284. writer.writeVerifyTypeInfo(UNINIT, offset);
  285. }
  286. }
  287. static class InsertLocal extends SimpleCopy {
  288. private int varIndex;
  289. private int varTag, varData;
  290. InsertLocal(StackMap map, int varIndex, int varTag, int varData) {
  291. super(map);
  292. this.varIndex = varIndex;
  293. this.varTag = varTag;
  294. this.varData = varData;
  295. }
  296. public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
  297. if (!isLocals || num < varIndex)
  298. return super.typeInfoArray(pos, offset, num, isLocals);
  299. writer.write16bit(num + 1);
  300. for (int k = 0; k < num; k++) {
  301. if (k == varIndex)
  302. writeVarTypeInfo();
  303. pos = typeInfoArray2(k, pos);
  304. }
  305. if (num == varIndex)
  306. writeVarTypeInfo();
  307. return pos;
  308. }
  309. private void writeVarTypeInfo() {
  310. if (varTag == OBJECT)
  311. writer.writeVerifyTypeInfo(OBJECT, varData);
  312. else if (varTag == UNINIT)
  313. writer.writeVerifyTypeInfo(UNINIT, varData);
  314. else
  315. writer.writeVerifyTypeInfo(varTag, 0);
  316. }
  317. }
  318. void shiftPc(int where, int gapSize, boolean exclusive)
  319. throws BadBytecode
  320. {
  321. new Shifter(this, where, gapSize, exclusive).visit();
  322. }
  323. static class Shifter extends Walker {
  324. private int where, gap;
  325. private boolean exclusive;
  326. public Shifter(StackMap smt, int where, int gap, boolean exclusive) {
  327. super(smt);
  328. this.where = where;
  329. this.gap = gap;
  330. this.exclusive = exclusive;
  331. }
  332. public int locals(int pos, int offset, int num) {
  333. if (exclusive ? where <= offset : where < offset)
  334. ByteArray.write16bit(offset + gap, info, pos - 4);
  335. return super.locals(pos, offset, num);
  336. }
  337. }
  338. /**
  339. * Undocumented method. Do not use; internal-use only.
  340. *
  341. * <p>This method is for javassist.convert.TransformNew.
  342. * It is called to update the stack map when
  343. * the NEW opcode (and the following DUP) is removed.
  344. *
  345. * @param where the position of the removed NEW opcode.
  346. */
  347. public void removeNew(int where) throws CannotCompileException {
  348. byte[] data = new NewRemover(this, where).doit();
  349. this.set(data);
  350. }
  351. static class NewRemover extends SimpleCopy {
  352. int posOfNew;
  353. NewRemover(StackMap map, int where) {
  354. super(map);
  355. posOfNew = where;
  356. }
  357. public int stack(int pos, int offset, int num) {
  358. return stackTypeInfoArray(pos, offset, num);
  359. }
  360. private int stackTypeInfoArray(int pos, int offset, int num) {
  361. int p = pos;
  362. int count = 0;
  363. for (int k = 0; k < num; k++) {
  364. byte tag = info[p];
  365. if (tag == OBJECT)
  366. p += 3;
  367. else if (tag == UNINIT) {
  368. int offsetOfNew = ByteArray.readU16bit(info, p + 1);
  369. if (offsetOfNew == posOfNew)
  370. count++;
  371. p += 3;
  372. }
  373. else
  374. p++;
  375. }
  376. writer.write16bit(num - count);
  377. for (int k = 0; k < num; k++) {
  378. byte tag = info[pos];
  379. if (tag == OBJECT) {
  380. int clazz = ByteArray.readU16bit(info, pos + 1);
  381. objectVariable(pos, clazz);
  382. pos += 3;
  383. }
  384. else if (tag == UNINIT) {
  385. int offsetOfNew = ByteArray.readU16bit(info, pos + 1);
  386. if (offsetOfNew != posOfNew)
  387. uninitialized(pos, offsetOfNew);
  388. pos += 3;
  389. }
  390. else {
  391. typeInfo(pos, tag);
  392. pos++;
  393. }
  394. }
  395. return pos;
  396. }
  397. }
  398. /**
  399. * Prints this stack map.
  400. */
  401. public void print(java.io.PrintWriter out) {
  402. new Printer(this, out).print();
  403. }
  404. static class Printer extends Walker {
  405. private java.io.PrintWriter writer;
  406. public Printer(StackMap map, java.io.PrintWriter out) {
  407. super(map);
  408. writer = out;
  409. }
  410. public void print() {
  411. int num = ByteArray.readU16bit(info, 0);
  412. writer.println(num + " entries");
  413. visit();
  414. }
  415. public int locals(int pos, int offset, int num) {
  416. writer.println(" * offset " + offset);
  417. return super.locals(pos, offset, num);
  418. }
  419. }
  420. /**
  421. * Internal use only.
  422. */
  423. public static class Writer {
  424. // see javassist.bytecode.stackmap.MapMaker
  425. private ByteArrayOutputStream output;
  426. /**
  427. * Constructs a writer.
  428. */
  429. public Writer() {
  430. output = new ByteArrayOutputStream();
  431. }
  432. /**
  433. * Converts the written data into a byte array.
  434. */
  435. public byte[] toByteArray() {
  436. return output.toByteArray();
  437. }
  438. /**
  439. * Converts to a <code>StackMap</code> attribute.
  440. */
  441. public StackMap toStackMap(ConstPool cp) {
  442. return new StackMap(cp, output.toByteArray());
  443. }
  444. /**
  445. * Writes a <code>union verification_type_info</code> value.
  446. *
  447. * @param data <code>cpool_index</code> or <code>offset</code>.
  448. */
  449. public void writeVerifyTypeInfo(int tag, int data) {
  450. output.write(tag);
  451. if (tag == StackMap.OBJECT || tag == StackMap.UNINIT)
  452. write16bit(data);
  453. }
  454. /**
  455. * Writes a 16bit value.
  456. */
  457. public void write16bit(int value) {
  458. output.write((value >>> 8) & 0xff);
  459. output.write(value & 0xff);
  460. }
  461. }
  462. }