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