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.

TransformAccessArrayField.java 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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.convert;
  17. import javassist.CannotCompileException;
  18. import javassist.CodeConverter.ArrayAccessReplacementMethodNames;
  19. import javassist.CtClass;
  20. import javassist.NotFoundException;
  21. import javassist.bytecode.BadBytecode;
  22. import javassist.bytecode.CodeIterator;
  23. import javassist.bytecode.ConstPool;
  24. import javassist.bytecode.Descriptor;
  25. import javassist.bytecode.MethodInfo;
  26. import javassist.bytecode.analysis.Analyzer;
  27. import javassist.bytecode.analysis.Frame;
  28. /**
  29. * A transformer which replaces array access with static method invocations.
  30. *
  31. * @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
  32. * @author Jason T. Greene
  33. * @version $Revision: 1.8 $
  34. */
  35. public final class TransformAccessArrayField extends Transformer {
  36. private final String methodClassname;
  37. private final ArrayAccessReplacementMethodNames names;
  38. private Frame[] frames;
  39. private int offset;
  40. public TransformAccessArrayField(Transformer next, String methodClassname,
  41. ArrayAccessReplacementMethodNames names) throws NotFoundException {
  42. super(next);
  43. this.methodClassname = methodClassname;
  44. this.names = names;
  45. }
  46. @Override
  47. public void initialize(ConstPool cp, CtClass clazz, MethodInfo minfo) throws CannotCompileException {
  48. /*
  49. * This transformer must be isolated from other transformers, since some
  50. * of them affect the local variable and stack maximums without updating
  51. * the code attribute to reflect the changes. This screws up the
  52. * data-flow analyzer, since it relies on consistent code state. Even
  53. * if the attribute values were updated correctly, we would have to
  54. * detect it, and redo analysis, which is not cheap. Instead, we are
  55. * better off doing all changes in initialize() before everyone else has
  56. * a chance to muck things up.
  57. */
  58. CodeIterator iterator = minfo.getCodeAttribute().iterator();
  59. while (iterator.hasNext()) {
  60. try {
  61. int pos = iterator.next();
  62. int c = iterator.byteAt(pos);
  63. if (c == AALOAD)
  64. initFrames(clazz, minfo);
  65. if (c == AALOAD || c == BALOAD || c == CALOAD || c == DALOAD
  66. || c == FALOAD || c == IALOAD || c == LALOAD
  67. || c == SALOAD) {
  68. pos = replace(cp, iterator, pos, c, getLoadReplacementSignature(c));
  69. } else if (c == AASTORE || c == BASTORE || c == CASTORE
  70. || c == DASTORE || c == FASTORE || c == IASTORE
  71. || c == LASTORE || c == SASTORE) {
  72. pos = replace(cp, iterator, pos, c, getStoreReplacementSignature(c));
  73. }
  74. } catch (Exception e) {
  75. throw new CannotCompileException(e);
  76. }
  77. }
  78. }
  79. @Override
  80. public void clean() {
  81. frames = null;
  82. offset = -1;
  83. }
  84. @Override
  85. public int transform(CtClass tclazz, int pos, CodeIterator iterator,
  86. ConstPool cp) throws BadBytecode {
  87. // Do nothing, see above comment
  88. return pos;
  89. }
  90. private Frame getFrame(int pos) throws BadBytecode {
  91. return frames[pos - offset]; // Adjust pos
  92. }
  93. private void initFrames(CtClass clazz, MethodInfo minfo) throws BadBytecode {
  94. if (frames == null) {
  95. frames = ((new Analyzer())).analyze(clazz, minfo);
  96. offset = 0; // start tracking changes
  97. }
  98. }
  99. private int updatePos(int pos, int increment) {
  100. if (offset > -1)
  101. offset += increment;
  102. return pos + increment;
  103. }
  104. private String getTopType(int pos) throws BadBytecode {
  105. Frame frame = getFrame(pos);
  106. if (frame == null)
  107. return null;
  108. CtClass clazz = frame.peek().getCtClass();
  109. return clazz != null ? Descriptor.toJvmName(clazz) : null;
  110. }
  111. private int replace(ConstPool cp, CodeIterator iterator, int pos,
  112. int opcode, String signature) throws BadBytecode {
  113. String castType = null;
  114. String methodName = getMethodName(opcode);
  115. if (methodName != null) {
  116. // See if the object must be cast
  117. if (opcode == AALOAD) {
  118. castType = getTopType(iterator.lookAhead());
  119. // Do not replace an AALOAD instruction that we do not have a type for
  120. // This happens when the state is guaranteed to be null (Type.UNINIT)
  121. // So we don't really care about this case.
  122. if (castType == null)
  123. return pos;
  124. if ("java/lang/Object".equals(castType))
  125. castType = null;
  126. }
  127. // The gap may include extra padding
  128. // Write a nop in case the padding pushes the instruction forward
  129. iterator.writeByte(NOP, pos);
  130. CodeIterator.Gap gap
  131. = iterator.insertGapAt(pos, castType != null ? 5 : 2, false);
  132. pos = gap.position;
  133. int mi = cp.addClassInfo(methodClassname);
  134. int methodref = cp.addMethodrefInfo(mi, methodName, signature);
  135. iterator.writeByte(INVOKESTATIC, pos);
  136. iterator.write16bit(methodref, pos + 1);
  137. if (castType != null) {
  138. int index = cp.addClassInfo(castType);
  139. iterator.writeByte(CHECKCAST, pos + 3);
  140. iterator.write16bit(index, pos + 4);
  141. }
  142. pos = updatePos(pos, gap.length);
  143. }
  144. return pos;
  145. }
  146. private String getMethodName(int opcode) {
  147. String methodName = null;
  148. switch (opcode) {
  149. case AALOAD:
  150. methodName = names.objectRead();
  151. break;
  152. case BALOAD:
  153. methodName = names.byteOrBooleanRead();
  154. break;
  155. case CALOAD:
  156. methodName = names.charRead();
  157. break;
  158. case DALOAD:
  159. methodName = names.doubleRead();
  160. break;
  161. case FALOAD:
  162. methodName = names.floatRead();
  163. break;
  164. case IALOAD:
  165. methodName = names.intRead();
  166. break;
  167. case SALOAD:
  168. methodName = names.shortRead();
  169. break;
  170. case LALOAD:
  171. methodName = names.longRead();
  172. break;
  173. case AASTORE:
  174. methodName = names.objectWrite();
  175. break;
  176. case BASTORE:
  177. methodName = names.byteOrBooleanWrite();
  178. break;
  179. case CASTORE:
  180. methodName = names.charWrite();
  181. break;
  182. case DASTORE:
  183. methodName = names.doubleWrite();
  184. break;
  185. case FASTORE:
  186. methodName = names.floatWrite();
  187. break;
  188. case IASTORE:
  189. methodName = names.intWrite();
  190. break;
  191. case SASTORE:
  192. methodName = names.shortWrite();
  193. break;
  194. case LASTORE:
  195. methodName = names.longWrite();
  196. break;
  197. }
  198. if (methodName.equals(""))
  199. methodName = null;
  200. return methodName;
  201. }
  202. private String getLoadReplacementSignature(int opcode) throws BadBytecode {
  203. switch (opcode) {
  204. case AALOAD:
  205. return "(Ljava/lang/Object;I)Ljava/lang/Object;";
  206. case BALOAD:
  207. return "(Ljava/lang/Object;I)B";
  208. case CALOAD:
  209. return "(Ljava/lang/Object;I)C";
  210. case DALOAD:
  211. return "(Ljava/lang/Object;I)D";
  212. case FALOAD:
  213. return "(Ljava/lang/Object;I)F";
  214. case IALOAD:
  215. return "(Ljava/lang/Object;I)I";
  216. case SALOAD:
  217. return "(Ljava/lang/Object;I)S";
  218. case LALOAD:
  219. return "(Ljava/lang/Object;I)J";
  220. }
  221. throw new BadBytecode(opcode);
  222. }
  223. private String getStoreReplacementSignature(int opcode) throws BadBytecode {
  224. switch (opcode) {
  225. case AASTORE:
  226. return "(Ljava/lang/Object;ILjava/lang/Object;)V";
  227. case BASTORE:
  228. return "(Ljava/lang/Object;IB)V";
  229. case CASTORE:
  230. return "(Ljava/lang/Object;IC)V";
  231. case DASTORE:
  232. return "(Ljava/lang/Object;ID)V";
  233. case FASTORE:
  234. return "(Ljava/lang/Object;IF)V";
  235. case IASTORE:
  236. return "(Ljava/lang/Object;II)V";
  237. case SASTORE:
  238. return "(Ljava/lang/Object;IS)V";
  239. case LASTORE:
  240. return "(Ljava/lang/Object;IJ)V";
  241. }
  242. throw new BadBytecode(opcode);
  243. }
  244. }