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 17KB

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