• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4 package com.android.tools.r8.graph;
5 
6 import com.android.tools.r8.ir.code.IRCode;
7 import com.android.tools.r8.ir.code.ValueNumberGenerator;
8 import com.android.tools.r8.ir.conversion.IRBuilder;
9 import com.android.tools.r8.ir.conversion.JarSourceCode;
10 import com.android.tools.r8.jar.JarRegisterEffectsVisitor;
11 import com.android.tools.r8.naming.ClassNameMapper;
12 import com.android.tools.r8.utils.InternalOptions;
13 import java.io.PrintWriter;
14 import java.io.StringWriter;
15 import java.util.IdentityHashMap;
16 import org.objectweb.asm.ClassReader;
17 import org.objectweb.asm.ClassVisitor;
18 import org.objectweb.asm.MethodVisitor;
19 import org.objectweb.asm.Opcodes;
20 import org.objectweb.asm.commons.JSRInlinerAdapter;
21 import org.objectweb.asm.tree.MethodNode;
22 import org.objectweb.asm.util.Textifier;
23 import org.objectweb.asm.util.TraceMethodVisitor;
24 
25 public class JarCode extends Code {
26 
27   public static class ReparseContext {
28 
29     // This will hold the content of the whole class. Once all the methods of the class are swapped
30     // from this to the actual JarCode, no other references would be left and the content can be
31     // GC'd.
32     public byte[] classCache;
33     public DexProgramClass owner;
34     private IdentityHashMap<DexMethod, JarCode> lookupMap = new IdentityHashMap<>();
35   }
36 
37   private final DexType clazz;
38   private MethodNode node;
39   private ReparseContext context;
40 
41   private final JarApplicationReader application;
42 
JarCode(DexMethod method, ReparseContext context, JarApplicationReader application)43   public JarCode(DexMethod method, ReparseContext context, JarApplicationReader application) {
44     this.clazz = method.getHolder();
45     this.context = context;
46     this.application = application;
47     context.lookupMap.put(method, this);
48   }
49 
50   @Override
isJarCode()51   public boolean isJarCode() {
52     return true;
53   }
54 
55   @Override
asJarCode()56   public JarCode asJarCode() {
57     return this;
58   }
59 
60   @Override
computeHashCode()61   protected int computeHashCode() {
62     triggerDelayedParsingIfNeccessary();
63     return node.hashCode();
64   }
65 
66   @Override
computeEquals(Object other)67   protected boolean computeEquals(Object other) {
68     triggerDelayedParsingIfNeccessary();
69     if (this == other) {
70       return true;
71     }
72     if (other instanceof JarCode) {
73       JarCode o = (JarCode) other;
74       o.triggerDelayedParsingIfNeccessary();
75       // TODO(zerny): This amounts to object equality.
76       return node.equals(o.node);
77     }
78     return false;
79   }
80 
81   @Override
buildIR(DexEncodedMethod encodedMethod, InternalOptions options)82   public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
83     triggerDelayedParsingIfNeccessary();
84     JarSourceCode source = new JarSourceCode(clazz, node, application);
85     IRBuilder builder = new IRBuilder(encodedMethod, source, options);
86     return builder.build();
87   }
88 
buildIR(DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options)89   public IRCode buildIR(DexEncodedMethod encodedMethod, ValueNumberGenerator generator,
90       InternalOptions options) {
91     triggerDelayedParsingIfNeccessary();
92     JarSourceCode source = new JarSourceCode(clazz, node, application);
93     IRBuilder builder = new IRBuilder(encodedMethod, source, generator, options);
94     return builder.build();
95   }
96 
97 
98   @Override
registerReachableDefinitions(UseRegistry registry)99   public void registerReachableDefinitions(UseRegistry registry) {
100     triggerDelayedParsingIfNeccessary();
101     node.instructions.accept(new JarRegisterEffectsVisitor(clazz, registry, application));
102   }
103 
104   @Override
toString()105   public String toString() {
106     triggerDelayedParsingIfNeccessary();
107     TraceMethodVisitor visitor = new TraceMethodVisitor(new Textifier());
108     node.accept(visitor);
109     StringWriter writer = new StringWriter();
110     visitor.p.print(new PrintWriter(writer));
111     return writer.toString();
112   }
113 
114   @Override
toString(DexEncodedMethod method, ClassNameMapper naming)115   public String toString(DexEncodedMethod method, ClassNameMapper naming) {
116     return toString();
117   }
118 
triggerDelayedParsingIfNeccessary()119   private void triggerDelayedParsingIfNeccessary() {
120     if (context != null) {
121       // The SecondVistor is in charge of setting the context to null.
122       DexProgramClass owner = context.owner;
123       new ClassReader(context.classCache).accept(new SecondVisitor(context, application),
124           ClassReader.SKIP_FRAMES);
125       assert verifyNoReparseContext(owner);
126     }
127   }
128 
129   /**
130    * Fills the MethodNodes of all the methods in the class and removes the ReparseContext.
131    */
132   private static class SecondVisitor extends ClassVisitor {
133 
134     private final ReparseContext context;
135     private final JarApplicationReader application;
136 
SecondVisitor(ReparseContext context, JarApplicationReader application)137     public SecondVisitor(ReparseContext context, JarApplicationReader application) {
138       super(Opcodes.ASM5);
139       this.context = context;
140       this.application = application;
141     }
142 
143     @Override
visitMethod(int access, String name, String desc, String signature, String[] exceptions)144     public MethodVisitor visitMethod(int access, String name, String desc, String signature,
145         String[] exceptions) {
146       MethodNode node = new JSRInlinerAdapter(null, access, name, desc, signature, exceptions);
147       JarCode code = context.lookupMap.get(application.getMethod(context.owner.type, name, desc));
148       if (code != null) {
149         code.context = null;
150         code.node = node;
151         return node;
152       }
153       return null;
154     }
155   }
156 
verifyNoReparseContext(DexProgramClass owner)157   private static boolean verifyNoReparseContext(DexProgramClass owner) {
158     for (DexEncodedMethod method : owner.virtualMethods()) {
159       Code code = method.getCode();
160       if (code != null && code.isJarCode()) {
161         if (code.asJarCode().context != null) {
162           return false;
163         }
164       }
165     }
166 
167     for (DexEncodedMethod method : owner.directMethods()) {
168       Code code = method.getCode();
169       if (code != null && code.isJarCode()) {
170         if (code.asJarCode().context != null) {
171           return false;
172         }
173       }
174     }
175     return true;
176   }
177 }
178