/src/kilim/analysis/ClassFlow.java

http://github.com/kilim/kilim · Java · 187 lines · 138 code · 24 blank · 25 comment · 26 complexity · 72ee80527de0433fcaf6eae597ceeef4 MD5 · raw file

  1. /* Copyright (c) 2006, Sriram Srinivasan
  2. *
  3. * You may distribute this software under the terms of the license
  4. * specified in the file "License"
  5. */
  6. package kilim.analysis;
  7. import kilim.*;
  8. import kilim.mirrors.Detector;
  9. import java.io.IOException;
  10. import java.io.InputStream;
  11. import java.util.ArrayList;
  12. import org.objectweb.asm.ClassReader;
  13. import org.objectweb.asm.MethodVisitor;
  14. import org.objectweb.asm.Opcodes;
  15. import org.objectweb.asm.tree.ClassNode;
  16. import org.objectweb.asm.tree.FieldNode;
  17. /**
  18. * This class reads a .class file (or stream), wraps each method with a MethodFlow object and optionally analyzes it.
  19. *
  20. */
  21. public class ClassFlow extends ClassNode {
  22. ArrayList<MethodFlow> methodFlows;
  23. public ClassReader cr;
  24. String classDesc;
  25. /**
  26. * true if any of the methods contained in the class file is pausable. ClassWeaver uses it later to avoid weaving if
  27. * isPausable isn't true.
  28. */
  29. private boolean isPausable;
  30. /**
  31. * true if the .class being read is already woven.
  32. */
  33. public boolean isWoven = false;
  34. public KilimContext context;
  35. /** the original bytecode associated with the class */
  36. public byte [] code;
  37. public ClassFlow(KilimContext context,InputStream is) throws IOException {
  38. super(Constants.KILIM_ASM);
  39. this.context = context;
  40. cr = new ClassReader(is);
  41. code = cr.b;
  42. }
  43. public ClassFlow(KilimContext context,String aClassName) throws IOException {
  44. super(Constants.KILIM_ASM);
  45. this.context = context;
  46. cr = new ClassReader(aClassName);
  47. code = cr.b;
  48. }
  49. @Override
  50. @SuppressWarnings( { "unchecked" })
  51. public MethodVisitor visitMethod(
  52. final int access,
  53. final String name,
  54. final String desc,
  55. final String signature,
  56. final String[] exceptions)
  57. {
  58. MethodFlow mn = new MethodFlow( this, access, name, desc, signature,
  59. exceptions, context.detector);
  60. super.methods.add(mn);
  61. return mn;
  62. }
  63. public ArrayList<MethodFlow> getMethodFlows() {
  64. assert (methodFlows != null) : "ClassFlow.analyze not called";
  65. return methodFlows;
  66. }
  67. public ArrayList<MethodFlow> analyze(boolean forceAnalysis) throws KilimException {
  68. // cr.accept(this, ClassReader.SKIP_DEBUG);
  69. try {
  70. cr.accept(this, /*flags*/ClassReader.SKIP_FRAMES);
  71. for (Object o : this.fields) {
  72. FieldNode fn = (FieldNode) o;
  73. if (fn.name.equals(Constants.WOVEN_FIELD)) {
  74. isWoven = true;
  75. break;
  76. }
  77. }
  78. if (isWoven && !forceAnalysis)
  79. return new ArrayList<MethodFlow>(); // This is a hack.
  80. cr = null; // We don't need this any more.
  81. classDesc = TypeDesc.getInterned("L" + name + ';');
  82. ArrayList<MethodFlow> flows = new ArrayList<MethodFlow>(methods.size());
  83. String msg = "";
  84. for (Object o : methods) {
  85. try {
  86. MethodFlow mf = (MethodFlow) o;
  87. if (mf.isBridge()) {
  88. MethodFlow mmf = getOrigWithSameSig(mf);
  89. if (mmf != null)
  90. mf.setPausable(mmf.isPausable());
  91. }
  92. mf.verifyPausables();
  93. if (mf.isPausable())
  94. isPausable = true;
  95. if ((mf.needsWeaving() || forceAnalysis) && (!mf.isAbstract())) {
  96. mf.analyze();
  97. }
  98. flows.add(mf);
  99. } catch (KilimException ke) {
  100. msg = msg + ke.getMessage() + "\n-------------------------------------------------\n";
  101. }
  102. }
  103. if (msg.length() > 0) {
  104. throw new KilimException(msg);
  105. }
  106. methodFlows = flows;
  107. return flows;
  108. } finally {
  109. }
  110. }
  111. private MethodFlow getOrigWithSameSig(MethodFlow bridgeMethod) {
  112. for (Object o : methods) {
  113. MethodFlow mf = (MethodFlow) o;
  114. if (mf == bridgeMethod)
  115. continue;
  116. if (mf.name.equals(bridgeMethod.name)) {
  117. String mfArgs = mf.desc.substring(0, mf.desc.indexOf(')'));
  118. String bmArgs = bridgeMethod.desc.substring(0, bridgeMethod.desc.indexOf(')'));
  119. if (mfArgs.equals(bmArgs))
  120. return mf;
  121. }
  122. }
  123. return null;
  124. // throw new AssertionError("Bridge method found, but original method does not exist\nBridge method:" +
  125. // this.name + "::" + bridgeMethod.name + bridgeMethod.desc);
  126. }
  127. public String getClassDescriptor() {
  128. return classDesc;
  129. }
  130. public String getClassName() {
  131. return super.name.replace('/', '.');
  132. }
  133. public boolean isPausable() {
  134. getMethodFlows(); // check analyze has been run.
  135. return isPausable;
  136. }
  137. boolean isInterface() {
  138. return (this.access & Opcodes.ACC_INTERFACE) != 0;
  139. }
  140. boolean isJava7() {
  141. return (version & 0x00FF) < 52;
  142. }
  143. /*
  144. * If this class is a functional interface, return the one "Single Abstract
  145. * Method". If there is more than one abstract method, return null.
  146. * SAM methods are given special treatment
  147. */
  148. public MethodFlow getSAM() {
  149. if (!isInterface() || isJava7()) {
  150. return null;
  151. }
  152. MethodFlow sam = null;
  153. for (MethodFlow mf: methodFlows) {
  154. if (mf.isAbstract()) {
  155. if (sam != null) {
  156. return null;
  157. }
  158. sam = mf;
  159. }
  160. }
  161. return sam;
  162. }
  163. }