• 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.code.Instruction;
7 import com.android.tools.r8.code.ReturnVoid;
8 import com.android.tools.r8.code.SwitchPayload;
9 import com.android.tools.r8.dex.IndexedItemCollection;
10 import com.android.tools.r8.dex.MixedSectionCollection;
11 import com.android.tools.r8.errors.Unreachable;
12 import com.android.tools.r8.ir.code.IRCode;
13 import com.android.tools.r8.ir.code.ValueNumberGenerator;
14 import com.android.tools.r8.ir.conversion.DexSourceCode;
15 import com.android.tools.r8.ir.conversion.IRBuilder;
16 import com.android.tools.r8.naming.ClassNameMapper;
17 import com.android.tools.r8.utils.InternalOptions;
18 import com.android.tools.r8.utils.StringUtils;
19 import java.util.Arrays;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Hashtable;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.Set;
27 
28 // DexCode corresponds to code item in dalvik/dex-format.html
29 public class DexCode extends Code {
30 
31   public final int registerSize;
32   public final int incomingRegisterSize;
33   public final int outgoingRegisterSize;
34   public final Try[] tries;
35   public final TryHandler[] handlers;
36   public final Instruction[] instructions;
37 
38   public final DexString highestSortingString;
39   private DexDebugInfo debugInfo;
40 
DexCode( int registerSize, int insSize, int outsSize, Instruction[] instructions, Try[] tries, TryHandler[] handlers, DexDebugInfo debugInfo, DexString highestSortingString)41   public DexCode(
42       int registerSize,
43       int insSize,
44       int outsSize,
45       Instruction[] instructions,
46       Try[] tries,
47       TryHandler[] handlers,
48       DexDebugInfo debugInfo,
49       DexString highestSortingString) {
50     this.incomingRegisterSize = insSize;
51     this.registerSize = registerSize;
52     this.outgoingRegisterSize = outsSize;
53     this.instructions = instructions;
54     this.tries = tries;
55     this.handlers = handlers;
56     this.debugInfo = debugInfo;
57     this.highestSortingString = highestSortingString;
58     hashCode();  // Cache the hash code eagerly.
59   }
60 
61   @Override
isDexCode()62   public boolean isDexCode() {
63     return true;
64   }
65 
66   @Override
asDexCode()67   public DexCode asDexCode() {
68     return this;
69   }
70 
getDebugInfo()71   public DexDebugInfo getDebugInfo() {
72     return debugInfo;
73   }
74 
setDebugInfo(DexDebugInfo debugInfo)75   public void setDebugInfo(DexDebugInfo debugInfo) {
76     this.debugInfo = debugInfo;
77   }
78 
debugInfoWithAdditionalFirstParameter(DexString name)79   public DexDebugInfo debugInfoWithAdditionalFirstParameter(DexString name) {
80     if (debugInfo == null) {
81       return null;
82     }
83     DexString[] parameters = debugInfo.parameters;
84     DexString[] newParameters = new DexString[parameters.length + 1];
85     newParameters[0] = name;
86     System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
87     return new DexDebugInfo(debugInfo.startLine, newParameters, debugInfo.events);
88   }
89 
codeSizeInBytes()90   public int codeSizeInBytes() {
91     Instruction last = instructions[instructions.length - 1];
92     return last.getOffset() + last.getSize();
93   }
94 
95   @Override
computeHashCode()96   public int computeHashCode() {
97     return incomingRegisterSize * 2
98         + registerSize * 3
99         + outgoingRegisterSize * 5
100         + Arrays.hashCode(instructions) * 7
101         + ((debugInfo == null) ? 0 : debugInfo.hashCode()) * 11
102         + Arrays.hashCode(tries) * 13
103         + Arrays.hashCode(handlers) * 17;
104   }
105 
106   @Override
computeEquals(Object other)107   public boolean computeEquals(Object other) {
108     if (other instanceof DexCode) {
109       DexCode o = (DexCode) other;
110       if (incomingRegisterSize != o.incomingRegisterSize) {
111         return false;
112       }
113       if (registerSize != o.registerSize) {
114         return false;
115       }
116       if (outgoingRegisterSize != o.outgoingRegisterSize) {
117         return false;
118       }
119       if (debugInfo == null) {
120         if (o.debugInfo != null) {
121           return false;
122         }
123       } else {
124         if (!debugInfo.equals(o.debugInfo)) {
125           return false;
126         }
127       }
128       if (!Arrays.equals(tries, o.tries)) {
129         return false;
130       }
131       if (!Arrays.equals(handlers, o.handlers)) {
132         return false;
133       }
134       // Save the most expensive operation to last.
135       return Arrays.equals(instructions, o.instructions);
136     }
137     return false;
138   }
139 
isEmptyVoidMethod()140   boolean isEmptyVoidMethod() {
141     return instructions.length == 1 && instructions[0] instanceof ReturnVoid;
142   }
143 
144   @Override
buildIR(DexEncodedMethod encodedMethod, InternalOptions options)145   public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
146     DexSourceCode source = new DexSourceCode(this, encodedMethod);
147     IRBuilder builder = new IRBuilder(encodedMethod, source, options);
148     return builder.build();
149   }
150 
buildIR( DexEncodedMethod encodedMethod, ValueNumberGenerator valueNumberGenerator, InternalOptions options)151   public IRCode buildIR(
152       DexEncodedMethod encodedMethod,
153       ValueNumberGenerator valueNumberGenerator,
154       InternalOptions options) {
155     DexSourceCode source = new DexSourceCode(this, encodedMethod);
156     IRBuilder builder = new IRBuilder(encodedMethod, source, valueNumberGenerator, options);
157     return builder.build();
158   }
159 
160   @Override
registerReachableDefinitions(UseRegistry registry)161   public void registerReachableDefinitions(UseRegistry registry) {
162     for (Instruction insn : instructions) {
163       insn.registerUse(registry);
164     }
165   }
166 
toString()167   public String toString() {
168     return toString(null, null);
169   }
170 
toString(DexEncodedMethod method, ClassNameMapper naming)171   public String toString(DexEncodedMethod method, ClassNameMapper naming) {
172     StringBuilder builder = new StringBuilder();
173     builder.append("registers: ").append(registerSize);
174     builder.append(", inputs: ").append(incomingRegisterSize);
175     builder.append(", outputs: ").append(outgoingRegisterSize).append("\n");
176     builder.append("------------------------------------------------------------\n");
177     builder.append("inst#  offset  instruction         arguments\n");
178     builder.append("------------------------------------------------------------\n");
179     DexDebugEntry debugInfo = null;
180     Iterator<DexDebugEntry> debugInfoIterator = Collections.emptyIterator();
181     if (getDebugInfo() != null && method != null) {
182       debugInfoIterator = new DexDebugEntryBuilder(method, new DexItemFactory()).build().iterator();
183       debugInfo = debugInfoIterator.hasNext() ? debugInfoIterator.next() : null;
184     }
185     int instructionNumber = 0;
186     for (Instruction insn : instructions) {
187       while (debugInfo != null && debugInfo.address == insn.getOffset()) {
188         builder.append("      ").append(debugInfo).append("\n");
189         debugInfo = debugInfoIterator.hasNext() ? debugInfoIterator.next() : null;
190       }
191       StringUtils.appendLeftPadded(builder, Integer.toString(instructionNumber++), 5);
192       builder.append(": ").append(insn.toString(naming)).append('\n');
193     }
194     if (debugInfoIterator.hasNext()) {
195       throw new Unreachable("Could not print all debug information.");
196     }
197     if (tries.length > 0) {
198       builder.append("Tries (numbers are offsets)\n");
199       for (Try atry : tries) {
200         builder.append("  ");
201         builder.append(atry.toString());
202         builder.append('\n');
203       }
204       if (handlers != null) {
205         builder.append("Handlers (numbers are offsets)\n");
206         for (int handlerIndex = 0; handlerIndex < handlers.length; handlerIndex++) {
207           TryHandler handler = handlers[handlerIndex];
208           builder.append("  ").append(handlerIndex).append(": ");
209           builder.append(handler.toString());
210           builder.append('\n');
211         }
212       }
213     }
214     return builder.toString();
215   }
216 
toSmaliString(ClassNameMapper naming)217   public String toSmaliString(ClassNameMapper naming) {
218     StringBuilder builder = new StringBuilder();
219     // Find labeled targets.
220     Map<Integer, Instruction> payloadUsers = new HashMap<>();
221     Set<Integer> labledTargets = new HashSet<>();
222     // Collect payload users and labeled targets for non-payload instructions.
223     for (Instruction dex : instructions) {
224       int[] targets = dex.getTargets();
225       if (targets != Instruction.NO_TARGETS && targets != Instruction.EXIT_TARGET) {
226         assert targets.length <= 2;
227         // For if instructions the second target is the fallthrough, for which no label is needed.
228         labledTargets.add(dex.getOffset() + targets[0]);
229       } else if (dex.hasPayload()) {
230         labledTargets.add(dex.getOffset() + dex.getPayloadOffset());
231         payloadUsers.put(dex.getOffset() + dex.getPayloadOffset(), dex);
232       }
233     }
234     // Collect labeled targets for payload instructions.
235     for (Instruction dex : instructions) {
236       if (dex.isSwitchPayload()) {
237         Instruction payloadUser = payloadUsers.get(dex.getOffset());
238         if (dex instanceof SwitchPayload) {
239           SwitchPayload payload = (SwitchPayload) dex;
240           for (int target : payload.switchTargetOffsets()) {
241             labledTargets.add(payloadUser.getOffset() + target);
242           }
243         }
244       }
245     }
246     // Generate smali for all instructions.
247     for (Instruction dex : instructions) {
248       if (labledTargets.contains(dex.getOffset())) {
249         builder.append("  :label_");
250         builder.append(dex.getOffset());
251         builder.append("\n");
252       }
253       if (dex.isSwitchPayload()) {
254         Instruction payloadUser = payloadUsers.get(dex.getOffset());
255         builder.append(dex.toSmaliString(payloadUser)).append('\n');
256       } else {
257         builder.append(dex.toSmaliString(naming)).append('\n');
258       }
259     }
260     if (tries.length > 0) {
261       builder.append("Tries (numbers are offsets)\n");
262       for (Try atry : tries) {
263         builder.append("  ");
264         builder.append(atry.toString());
265         builder.append('\n');
266       }
267       if (handlers != null) {
268         builder.append("Handlers (numbers are offsets)\n");
269         for (TryHandler handler : handlers) {
270           builder.append(handler.toString());
271           builder.append('\n');
272         }
273       }
274     }
275     return builder.toString();
276   }
277 
collectIndexedItems(IndexedItemCollection indexedItems)278   public void collectIndexedItems(IndexedItemCollection indexedItems) {
279     for (Instruction insn : instructions) {
280       insn.collectIndexedItems(indexedItems);
281     }
282     if (debugInfo != null) {
283       debugInfo.collectIndexedItems(indexedItems);
284     }
285     if (handlers != null) {
286       for (TryHandler handler : handlers) {
287         handler.collectIndexedItems(indexedItems);
288       }
289     }
290   }
291 
usesExceptionHandling()292   public boolean usesExceptionHandling() {
293     return tries.length != 0;
294   }
295 
296   @Override
collectMixedSectionItems(MixedSectionCollection mixedItems)297   void collectMixedSectionItems(MixedSectionCollection mixedItems) {
298     if (mixedItems.add(this)) {
299       if (debugInfo != null) {
300         debugInfo.collectMixedSectionItems(mixedItems);
301       }
302     }
303   }
304 
305   public static class Try extends DexItem {
306 
307     public static final int NO_INDEX = -1;
308 
309     private final int handlerOffset;
310     public /* offset */ int startAddress;
311     public /* offset */ int instructionCount;
312     public int handlerIndex;
313 
Try(int startAddress, int instructionCount, int handlerOffset)314     public Try(int startAddress, int instructionCount, int handlerOffset) {
315       this.startAddress = startAddress;
316       this.instructionCount = instructionCount;
317       this.handlerOffset = handlerOffset;
318       this.handlerIndex = NO_INDEX;
319     }
320 
setHandlerIndex(Hashtable<Integer, Integer> map)321     public void setHandlerIndex(Hashtable<Integer, Integer> map) {
322       handlerIndex = map.get(handlerOffset);
323     }
324 
hashCode()325     public int hashCode() {
326       return startAddress * 2 + instructionCount * 3 + handlerIndex * 5;
327     }
328 
equals(Object other)329     public boolean equals(Object other) {
330       if (this == other) {
331         return true;
332       }
333       if (other instanceof Try) {
334         Try o = (Try) other;
335         if (startAddress != o.startAddress) {
336           return false;
337         }
338         if (instructionCount != o.instructionCount) {
339           return false;
340         }
341         return handlerIndex == o.handlerIndex;
342       }
343       return false;
344     }
345 
toString()346     public String toString() {
347       return "["
348           + startAddress
349           + " .. "
350           + (startAddress + instructionCount - 1)
351           + "] -> "
352           + handlerIndex;
353     }
354 
355     @Override
collectIndexedItems(IndexedItemCollection indexedItems)356     void collectIndexedItems(IndexedItemCollection indexedItems) {
357       // Intentionally left empty.
358     }
359 
360     @Override
collectMixedSectionItems(MixedSectionCollection mixedItems)361     void collectMixedSectionItems(MixedSectionCollection mixedItems) {
362       // Should never be visited.
363       assert false;
364     }
365 
366   }
367 
368   public static class TryHandler extends DexItem {
369 
370     public static final int NO_HANDLER = -1;
371 
372     public final TypeAddrPair[] pairs;
373     public /* offset */ int catchAllAddr;
374 
TryHandler(TypeAddrPair[] pairs, int catchAllAddr)375     public TryHandler(TypeAddrPair[] pairs, int catchAllAddr) {
376       this.pairs = pairs;
377       this.catchAllAddr = catchAllAddr;
378     }
379 
hashCode()380     public int hashCode() {
381       return catchAllAddr + Arrays.hashCode(pairs) * 7;
382     }
383 
equals(Object other)384     public boolean equals(Object other) {
385       if (this == other) {
386         return true;
387       }
388       if (other instanceof TryHandler) {
389         TryHandler o = (TryHandler) other;
390         if (catchAllAddr != o.catchAllAddr) {
391           return false;
392         }
393         return Arrays.equals(pairs, o.pairs);
394       }
395       return false;
396     }
397 
collectIndexedItems(IndexedItemCollection indexedItems)398     public void collectIndexedItems(IndexedItemCollection indexedItems) {
399       collectAll(indexedItems, pairs);
400     }
401 
402     @Override
collectMixedSectionItems(MixedSectionCollection mixedItems)403     void collectMixedSectionItems(MixedSectionCollection mixedItems) {
404       // Should never be visited.
405       assert false;
406     }
407 
toString()408     public String toString() {
409       StringBuilder builder = new StringBuilder();
410       builder.append("[\n");
411       for (TypeAddrPair pair : pairs) {
412         builder.append("       ");
413         builder.append(pair.type);
414         builder.append(" -> ");
415         builder.append(pair.addr);
416         builder.append("\n");
417       }
418       if (catchAllAddr != NO_HANDLER) {
419         builder.append("       default -> ");
420         builder.append(catchAllAddr);
421         builder.append("\n");
422       }
423       builder.append("     ]");
424       return builder.toString();
425     }
426 
427     public static class TypeAddrPair extends DexItem {
428 
429       public final DexType type;
430       public final /* offset */ int addr;
431       public final /* offset to the start of an encoded_catch_handler. */ int offset;
432 
TypeAddrPair(DexType type, int addr, int offset)433       public TypeAddrPair(DexType type, int addr, int offset) {
434         this.type = type;
435         this.addr = addr;
436         this.offset = offset;
437       }
438 
collectIndexedItems(IndexedItemCollection indexedItems)439       public void collectIndexedItems(IndexedItemCollection indexedItems) {
440         type.collectIndexedItems(indexedItems);
441       }
442 
443       @Override
collectMixedSectionItems(MixedSectionCollection mixedItems)444       void collectMixedSectionItems(MixedSectionCollection mixedItems) {
445         // Should never be visited.
446         assert false;
447       }
448 
449       @Override
hashCode()450       public int hashCode() {
451         return type.hashCode() * 7 + addr;
452       }
453 
454       @Override
equals(Object other)455       public boolean equals(Object other) {
456         if (this == other) {
457           return true;
458         }
459         if (other instanceof TypeAddrPair) {
460           TypeAddrPair o = (TypeAddrPair) other;
461           return type.equals(o.type) && addr == o.addr;
462         }
463         return false;
464       }
465     }
466   }
467 }
468