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.naming; 5 6 import com.android.tools.r8.graph.DexField; 7 import com.android.tools.r8.graph.DexMethod; 8 import com.android.tools.r8.graph.DexType; 9 import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind; 10 import java.io.IOException; 11 import java.io.StringWriter; 12 import java.io.Writer; 13 import java.util.ArrayList; 14 import java.util.Arrays; 15 import java.util.LinkedList; 16 import java.util.List; 17 18 /** 19 * Stores renaming information for a member. 20 * <p> 21 * This includes the signature, the original name and inlining range information. 22 */ 23 public class MemberNaming { 24 25 private static final int UNDEFINED_START_NUMBER = -1; 26 27 @Override equals(Object o)28 public boolean equals(Object o) { 29 if (this == o) { 30 return true; 31 } 32 if (!(o instanceof MemberNaming)) { 33 return false; 34 } 35 36 MemberNaming that = (MemberNaming) o; 37 return signature.equals(that.signature) 38 && renamedSignature.equals(that.renamedSignature) 39 && topLevelRange.equals(that.topLevelRange) 40 && inlineInformation.equals(that.inlineInformation); 41 } 42 43 @Override hashCode()44 public int hashCode() { 45 int result = signature.hashCode(); 46 result = 31 * result + renamedSignature.hashCode(); 47 result = 31 * result + inlineInformation.hashCode(); 48 result = 31 * result + topLevelRange.hashCode(); 49 return result; 50 } 51 52 /** 53 * Original signature of the member 54 */ 55 final Signature signature; 56 /** 57 * Renamed signature where the name (but not the types) have been renamed. 58 */ 59 final Signature renamedSignature; 60 public final List<InlineInformation> inlineInformation = new LinkedList<>(); 61 public final Range topLevelRange; 62 63 private int collapsedStartLineNumber = UNDEFINED_START_NUMBER; 64 private int originalStartLineNumber = UNDEFINED_START_NUMBER; 65 MemberNaming(Signature signature, String renamedName, Range inlinedLineRange)66 MemberNaming(Signature signature, String renamedName, Range inlinedLineRange) { 67 this.signature = signature; 68 this.renamedSignature = signature.asRenamed(renamedName); 69 topLevelRange = inlinedLineRange == null ? fakeZeroRange : inlinedLineRange; 70 } 71 addInliningRange(Range inlinedRange, Signature signature, Range originalRange)72 public void addInliningRange(Range inlinedRange, Signature signature, Range originalRange) { 73 inlineInformation.add(new InlineInformation(inlinedRange, originalRange, signature)); 74 } 75 getInlineRanges()76 public List<Range> getInlineRanges() { 77 List<Range> inlineRanges = new ArrayList<>(); 78 for (InlineInformation information : inlineInformation) { 79 if (information.isActualInlining()) { 80 inlineRanges.add(information.inlinedRange); 81 } 82 } 83 return inlineRanges; 84 } 85 getOriginalSignature()86 public Signature getOriginalSignature() { 87 return signature; 88 } 89 getRenamedName()90 public String getRenamedName() { 91 return renamedSignature.name; 92 } 93 setCollapsedStartLineNumber(int value)94 public void setCollapsedStartLineNumber(int value) { 95 assert collapsedStartLineNumber == UNDEFINED_START_NUMBER; 96 collapsedStartLineNumber = value; 97 } 98 isMethodNaming()99 public boolean isMethodNaming() { 100 return signature.kind() == SignatureKind.METHOD; 101 } 102 getCollapsedStartLineNumber()103 private int getCollapsedStartLineNumber() { 104 return collapsedStartLineNumber; 105 } 106 write(Writer writer, boolean collapseRanges, boolean indent)107 protected void write(Writer writer, boolean collapseRanges, boolean indent) throws IOException { 108 if (indent) { 109 writer.append(" "); 110 } 111 int rangeCounter = 112 collapseRanges ? getCollapsedStartLineNumber() : InlineInformation.DO_NOT_COLLAPSE; 113 // Avoid printing the range information if there was none in the original file. 114 if (topLevelRange != fakeZeroRange || rangeCounter != UNDEFINED_START_NUMBER) { 115 if (collapseRanges) { 116 // Skip ranges for methods that are used only once, as they do not have debug information. 117 if (rangeCounter != UNDEFINED_START_NUMBER) { 118 String rangeString = Integer.toString(rangeCounter); 119 writer.append(rangeString).append(":").append(rangeString).append(":"); 120 } else { 121 rangeCounter = 0; 122 } 123 } else { 124 writer.append(topLevelRange.toString()); 125 writer.append(':'); 126 } 127 } else { 128 // We might end up in a case where we have no line information for the top entry but still 129 // have inline ranges. Just to be sure, set rangeCounter to a useful value. 130 if (collapseRanges) { 131 rangeCounter = 0; 132 } 133 } 134 signature.write(writer); 135 if (originalStartLineNumber != UNDEFINED_START_NUMBER) { 136 // If we have original line number information, print it here. 137 String originalSourceLineString = Integer.toString(originalStartLineNumber); 138 writer.append(':') 139 .append(originalSourceLineString) 140 .append(':') 141 .append(originalSourceLineString); 142 } 143 writer.append(" -> "); 144 writer.append(renamedSignature.name); 145 writer.append("\n"); 146 for (InlineInformation information : inlineInformation) { 147 assert !collapseRanges || rangeCounter >= 0; 148 if (collapseRanges && information.isActualInlining()) { 149 rangeCounter++; 150 } 151 information.write(writer, rangeCounter, indent); 152 } 153 } 154 155 @Override toString()156 public String toString() { 157 try { 158 StringWriter writer = new StringWriter(); 159 write(writer, false, false); 160 return writer.toString(); 161 } catch (IOException e) { 162 return e.toString(); 163 } 164 } 165 setOriginalStartLineNumber(int originalStartLineNumber)166 public void setOriginalStartLineNumber(int originalStartLineNumber) { 167 assert this.originalStartLineNumber == UNDEFINED_START_NUMBER; 168 this.originalStartLineNumber = originalStartLineNumber; 169 } 170 171 public abstract static class Signature { 172 173 public final String name; 174 Signature(String name)175 protected Signature(String name) { 176 this.name = name; 177 } 178 asRenamed(String renamedName)179 abstract Signature asRenamed(String renamedName); 180 kind()181 abstract public SignatureKind kind(); 182 183 @Override equals(Object o)184 abstract public boolean equals(Object o); 185 186 @Override hashCode()187 abstract public int hashCode(); 188 write(Writer builder)189 abstract void write(Writer builder) throws IOException; 190 191 @Override toString()192 public String toString() { 193 try { 194 StringWriter writer = new StringWriter(); 195 write(writer); 196 return writer.toString(); 197 } catch (IOException e) { 198 return e.toString(); 199 } 200 } 201 202 enum SignatureKind { 203 METHOD, 204 FIELD 205 } 206 } 207 208 public static class FieldSignature extends Signature { 209 210 public final String type; 211 FieldSignature(String name, String type)212 public FieldSignature(String name, String type) { 213 super(name); 214 this.type = type; 215 } 216 fromDexField(DexField field)217 public static FieldSignature fromDexField(DexField field) { 218 return new FieldSignature(field.name.toSourceString(), 219 field.type.toSourceString()); 220 } 221 222 @Override asRenamed(String renamedName)223 Signature asRenamed(String renamedName) { 224 return new FieldSignature(renamedName, type); 225 } 226 227 @Override kind()228 public SignatureKind kind() { 229 return SignatureKind.FIELD; 230 } 231 232 @Override equals(Object o)233 public boolean equals(Object o) { 234 if (this == o) { 235 return true; 236 } 237 if (!(o instanceof FieldSignature)) { 238 return false; 239 } 240 FieldSignature that = (FieldSignature) o; 241 return name.equals(that.name) && type.equals(that.type); 242 } 243 244 @Override hashCode()245 public int hashCode() { 246 return name.hashCode() * 31 + type.hashCode(); 247 } 248 249 @Override toString()250 public String toString() { 251 return type + " " + name; 252 } 253 254 @Override write(Writer writer)255 void write(Writer writer) throws IOException { 256 writer.append(type); 257 writer.append(' '); 258 writer.append(name); 259 } 260 } 261 262 public static class MethodSignature extends Signature { 263 264 public final String type; 265 public final String[] parameters; 266 MethodSignature(String name, String type, String[] parameters)267 public MethodSignature(String name, String type, String[] parameters) { 268 super(name); 269 this.type = type; 270 this.parameters = parameters; 271 } 272 fromDexMethod(DexMethod method)273 public static MethodSignature fromDexMethod(DexMethod method) { 274 String[] paramNames = new String[method.proto.parameters.values.length]; 275 DexType[] values = method.proto.parameters.values; 276 for (int i = 0; i < values.length; i++) { 277 paramNames[i] = values[i].toSourceString(); 278 } 279 return new MethodSignature(method.name.toSourceString(), 280 method.proto.returnType.toSourceString(), paramNames); 281 } 282 283 @Override asRenamed(String renamedName)284 Signature asRenamed(String renamedName) { 285 return new MethodSignature(renamedName, type, parameters); 286 } 287 288 @Override kind()289 public SignatureKind kind() { 290 return SignatureKind.METHOD; 291 } 292 293 @Override equals(Object o)294 public boolean equals(Object o) { 295 if (this == o) { 296 return true; 297 } 298 if (!(o instanceof MethodSignature)) { 299 return false; 300 } 301 302 MethodSignature that = (MethodSignature) o; 303 return type.equals(that.type) 304 && name.equals(that.name) 305 && Arrays.equals(parameters, that.parameters); 306 } 307 308 @Override hashCode()309 public int hashCode() { 310 return (type.hashCode() * 17 311 + name.hashCode()) * 31 312 + Arrays.hashCode(parameters); 313 } 314 315 @Override write(Writer writer)316 void write(Writer writer) throws IOException { 317 writer.append(type) 318 .append(' ') 319 .append(name) 320 .append('('); 321 for (int i = 0; i < parameters.length; i++) { 322 writer.append(parameters[i]); 323 if (i < parameters.length - 1) { 324 writer.append(','); 325 } 326 } 327 writer.append(')'); 328 } 329 } 330 331 public class InlineInformation { 332 static final int DO_NOT_COLLAPSE = -1; 333 334 public final Range inlinedRange; 335 public final Range originalRange; 336 public final Signature signature; 337 InlineInformation(Range inlinedRange, Range originalRange, Signature signature)338 public InlineInformation(Range inlinedRange, Range originalRange, Signature signature) { 339 this.inlinedRange = inlinedRange; 340 this.originalRange = originalRange; 341 this.signature = signature; 342 } 343 isActualInlining()344 public boolean isActualInlining() { 345 return !(originalRange instanceof SingleLineRange); 346 } 347 write(Writer writer, int collapsedRange, boolean indent)348 public void write(Writer writer, int collapsedRange, boolean indent) throws IOException { 349 if (indent) { 350 writer.append(" "); 351 } 352 if (collapsedRange == DO_NOT_COLLAPSE) { 353 writer.append(inlinedRange.toString()); 354 } else { 355 writer.append(Range.toCollapsedString(collapsedRange)); 356 } 357 writer.append(":"); 358 signature.write(writer); 359 if (originalRange != null) { 360 writer.append(':') 361 .append(originalRange.toString()); 362 } 363 writer.append(" -> ") 364 .append(renamedSignature.name); 365 writer.append("\n"); 366 } 367 368 @Override equals(Object o)369 public boolean equals(Object o) { 370 if (this == o) { 371 return true; 372 } 373 if (!(o instanceof InlineInformation)) { 374 return false; 375 } 376 377 InlineInformation that = (InlineInformation) o; 378 379 return inlinedRange.equals(that.inlinedRange) 380 && ((originalRange == null && that.originalRange == null) 381 || originalRange.equals(that.originalRange)) 382 && signature.equals(that.signature); 383 384 } 385 386 @Override hashCode()387 public int hashCode() { 388 int result = inlinedRange.hashCode(); 389 result = 31 * result + originalRange.hashCode(); 390 result = 31 * result + signature.hashCode(); 391 return result; 392 } 393 } 394 395 /** 396 * Represents a linenumber range. 397 */ 398 public static class Range { 399 400 public final int from; 401 public final int to; 402 Range(int from, int to)403 Range(int from, int to) { 404 this.from = from; 405 this.to = to; 406 } 407 contains(int value)408 public boolean contains(int value) { 409 return value >= from && value <= to; 410 } 411 isSingle()412 public boolean isSingle() { 413 return false; 414 } 415 416 @Override toString()417 public String toString() { 418 return from + ":" + to; 419 } 420 toCollapsedString(int value)421 public static String toCollapsedString(int value) { 422 return value + ":" + value; 423 } 424 425 @Override equals(Object o)426 public boolean equals(Object o) { 427 if (this == o) { 428 return true; 429 } 430 if (!(o instanceof Range)) { 431 return false; 432 } 433 434 Range range = (Range) o; 435 return from == range.from && to == range.to; 436 } 437 438 @Override hashCode()439 public int hashCode() { 440 int result = from; 441 result = 31 * result + to; 442 return result; 443 } 444 445 } 446 447 /** 448 * Represents a single linenumber range (':' followed by a signle number), which 449 * is different semantically from a normal range that has the same from and to. 450 */ 451 public static class SingleLineRange extends Range { SingleLineRange(int fromAndTo)452 public SingleLineRange(int fromAndTo) { 453 super(fromAndTo, fromAndTo); 454 } 455 456 @Override isSingle()457 public boolean isSingle() { 458 return true; 459 } 460 461 @Override toString()462 public String toString() { 463 return Integer.toString(from); 464 } 465 } 466 467 public final static Range fakeZeroRange = new Range(0, 0); 468 } 469