aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
blob: 36d732d8df290c88dd8d255efe6f2282c3b1a1c3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;

import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.DexDebugEvent.AdvanceLine;
import com.android.tools.r8.graph.DexDebugEvent.AdvancePC;
import com.android.tools.r8.graph.DexDebugEvent.Default;
import com.android.tools.r8.graph.DexDebugEvent.EndLocal;
import com.android.tools.r8.graph.DexDebugEvent.RestartLocal;
import com.android.tools.r8.graph.DexDebugEvent.SetEpilogueBegin;
import com.android.tools.r8.graph.DexDebugEvent.SetFile;
import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd;
import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
import com.android.tools.r8.naming.NamingLens;
import com.google.common.collect.ImmutableSet;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

public class DexItemFactory {

  private final Map<String, DexString> strings = new HashMap<>();
  private final Map<DexType, DexType> types = new HashMap<>();
  private final Map<DexField, DexField> fields = new HashMap<>();
  private final Map<DexProto, DexProto> protos = new HashMap<>();
  private final Map<DexMethod, DexMethod> methods = new HashMap<>();
  private final Map<DexCallSite, DexCallSite> callSites = new HashMap<>();
  private final Map<DexMethodHandle, DexMethodHandle> methodHandles = new HashMap<>();

  // DexDebugEvent Canonicalization.
  private final Int2ObjectMap<AdvanceLine> advanceLines = new Int2ObjectOpenHashMap<>();
  private final Int2ObjectMap<AdvancePC> advancePCs = new Int2ObjectOpenHashMap<>();
  private final Int2ObjectMap<Default> defaults = new Int2ObjectOpenHashMap<>();
  private final Int2ObjectMap<EndLocal> endLocals = new Int2ObjectOpenHashMap<>();
  private final Int2ObjectMap<RestartLocal> restartLocals = new Int2ObjectOpenHashMap<>();
  private final SetEpilogueBegin setEpilogueBegin = new SetEpilogueBegin();
  private final SetPrologueEnd setPrologueEnd = new SetPrologueEnd();
  private final Map<DexString, SetFile> setFiles = new HashMap<>();

  boolean sorted = false;

  public static final DexType catchAllType = new DexType(new DexString("CATCH_ALL"));
  private static final Set<DexItem> internalSentinels = ImmutableSet.of(catchAllType);

  public DexString booleanDescriptor = createString("Z");
  public DexString byteDescriptor = createString("B");
  public DexString charDescriptor = createString("C");
  public DexString doubleDescriptor = createString("D");
  public DexString floatDescriptor = createString("F");
  public DexString intDescriptor = createString("I");
  public DexString longDescriptor = createString("J");
  public DexString shortDescriptor = createString("S");
  public DexString voidDescriptor = createString("V");

  public DexString boxedBooleanDescriptor = createString("Ljava/lang/Boolean;");
  public DexString boxedByteDescriptor = createString("Ljava/lang/Byte;");
  public DexString boxedCharDescriptor = createString("Ljava/lang/Character;");
  public DexString boxedDoubleDescriptor = createString("Ljava/lang/Double;");
  public DexString boxedFloatDescriptor = createString("Ljava/lang/Float;");
  public DexString boxedIntDescriptor = createString("Ljava/lang/Integer;");
  public DexString boxedLongDescriptor = createString("Ljava/lang/Long;");
  public DexString boxedShortDescriptor = createString("Ljava/lang/Short;");
  public DexString boxedNumberDescriptor = createString("Ljava/lang/Number;");

  public DexString unboxBooleanMethodName = createString("booleanValue");
  public DexString unboxByteMethodName = createString("byteValue");
  public DexString unboxCharMethodName = createString("charValue");
  public DexString unboxShortMethodName = createString("shortValue");
  public DexString unboxIntMethodName = createString("intValue");
  public DexString unboxLongMethodName = createString("longValue");
  public DexString unboxFloatMethodName = createString("floatValue");
  public DexString unboxDoubleMethodName = createString("doubleValue");

  public DexString valueOfMethodName = createString("valueOf");

  public DexString getClassMethodName = createString("getClass");
  public DexString ordinalMethodName = createString("ordinal");
  public final DexString desiredAssertionStatusMethodName = createString("desiredAssertionStatus");
  public final DexString assertionsDisabled = createString("$assertionsDisabled");

  public DexString stringDescriptor = createString("Ljava/lang/String;");
  public DexString objectDescriptor = createString("Ljava/lang/Object;");
  public DexString classDescriptor = createString("Ljava/lang/Class;");
  public DexString enumDescriptor = createString("Ljava/lang/Enum;");
  public DexString annotationDescriptor = createString("Ljava/lang/annotation/Annotation;");
  public DexString throwableDescriptor = createString("Ljava/lang/Throwable;");
  public DexString objectsDescriptor = createString("Ljava/util/Objects;");

  public DexString constructorMethodName = createString(Constants.INSTANCE_INITIALIZER_NAME);
  public DexString classConstructorMethodName = createString(Constants.CLASS_INITIALIZER_NAME);

  public DexString thisName = createString("this");

  private DexString charArrayDescriptor = createString("[C");
  private DexType charArrayType = createType(charArrayDescriptor);
  public DexString throwableArrayDescriptor = createString("[Ljava/lang/Throwable;");

  public DexType booleanType = createType(booleanDescriptor);
  public DexType byteType = createType(byteDescriptor);
  public DexType charType = createType(charDescriptor);
  public DexType doubleType = createType(doubleDescriptor);
  public DexType floatType = createType(floatDescriptor);
  public DexType intType = createType(intDescriptor);
  public DexType longType = createType(longDescriptor);
  public DexType shortType = createType(shortDescriptor);
  public DexType voidType = createType(voidDescriptor);

  public DexType boxedBooleanType = createType(boxedBooleanDescriptor);
  public DexType boxedByteType = createType(boxedByteDescriptor);
  public DexType boxedCharType = createType(boxedCharDescriptor);
  public DexType boxedDoubleType = createType(boxedDoubleDescriptor);
  public DexType boxedFloatType = createType(boxedFloatDescriptor);
  public DexType boxedIntType = createType(boxedIntDescriptor);
  public DexType boxedLongType = createType(boxedLongDescriptor);
  public DexType boxedShortType = createType(boxedShortDescriptor);
  public DexType boxedNumberType = createType(boxedNumberDescriptor);

  public DexType stringType = createType(stringDescriptor);
  public DexType objectType = createType(objectDescriptor);
  public DexType enumType = createType(enumDescriptor);
  public DexType annotationType = createType(annotationDescriptor);
  public DexType throwableType = createType(throwableDescriptor);

  public DexType stringBuilderType = createType("Ljava/lang/StringBuilder;");
  public DexType stringBufferType = createType("Ljava/lang/StringBuffer;");

  public StringBuildingMethods stringBuilderMethods = new StringBuildingMethods(stringBuilderType);
  public StringBuildingMethods stringBufferMethods = new StringBuildingMethods(stringBufferType);
  public ObjectsMethods objectsMethods = new ObjectsMethods();
  public ObjectMethods objectMethods = new ObjectMethods();
  public LongMethods longMethods = new LongMethods();
  public ThrowableMethods throwableMethods = new ThrowableMethods();
  public ClassMethods classMethods = new ClassMethods();

  // Dex system annotations.
  // See https://source.android.com/devices/tech/dalvik/dex-format.html#system-annotation
  public final DexType annotationDefault = createType("Ldalvik/annotation/AnnotationDefault;");
  public final DexType annotationEnclosingClass = createType("Ldalvik/annotation/EnclosingClass;");
  public final DexType annotationEnclosingMethod = createType(
      "Ldalvik/annotation/EnclosingMethod;");
  public final DexType annotationInnerClass = createType("Ldalvik/annotation/InnerClass;");
  public final DexType annotationMemberClasses = createType("Ldalvik/annotation/MemberClasses;");
  public final DexType annotationMethodParameters = createType(
      "Ldalvik/annotation/MethodParameters;");
  public final DexType annotationSignature = createType("Ldalvik/annotation/Signature;");
  public final DexType annotationSourceDebugExtension = createType(
      "Ldalvik/annotation/SourceDebugExtension;");
  public final DexType annotationThrows = createType("Ldalvik/annotation/Throws;");

  public void clearSubtypeInformation() {
    types.values().forEach(DexType::clearSubtypeInformation);
  }

  public class LongMethods {

    public DexMethod compare;

    private LongMethods() {
      compare = createMethod(boxedLongDescriptor,
          createString("compare"), intDescriptor, new DexString[]{longDescriptor, longDescriptor});
    }
  }

  public class ThrowableMethods {

    public final DexMethod addSuppressed;
    public final DexMethod getSuppressed;

    private ThrowableMethods() {
      addSuppressed = createMethod(throwableDescriptor,
          createString("addSuppressed"), voidDescriptor, new DexString[]{throwableDescriptor});
      getSuppressed = createMethod(throwableDescriptor,
          createString("getSuppressed"), throwableArrayDescriptor, DexString.EMPTY_ARRAY);
    }
  }

  public class ObjectMethods {

    public DexMethod getClass;

    private ObjectMethods() {
      getClass = createMethod(objectsDescriptor,
          getClassMethodName, classDescriptor, new DexString[]{});
    }
  }

  public class ObjectsMethods {

    public DexMethod requireNonNull;

    private ObjectsMethods() {
      requireNonNull = createMethod(objectsDescriptor,
          createString("requireNonNull"), objectDescriptor, new DexString[]{objectDescriptor});
    }
  }

  public class ClassMethods {

    public DexMethod desiredAssertionStatus;

    private ClassMethods() {
      desiredAssertionStatus = createMethod(classDescriptor,
          desiredAssertionStatusMethodName, booleanDescriptor, new DexString[]{});
    }
  }


  public class StringBuildingMethods {

    public DexMethod appendBoolean;
    public DexMethod appendChar;
    public DexMethod appendCharArray;
    public DexMethod appendSubCharArray;
    public DexMethod appendCharSequence;
    public DexMethod appendSubCharSequence;
    public DexMethod appendInt;
    public DexMethod appendDouble;
    public DexMethod appendFloat;
    public DexMethod appendLong;
    public DexMethod appendObject;
    public DexMethod appendString;
    public DexMethod appendStringBuffer;
    public DexMethod toString;

    private StringBuildingMethods(DexType receiver) {
      DexType sbufType = createType(createString("Ljava/lang/StringBuffer;"));
      DexType charSequenceType = createType(createString("Ljava/lang/CharSequence;"));
      DexString append = createString("append");
      DexString toStringMethodName = createString("toString");


      appendBoolean = createMethod(receiver, createProto(receiver, booleanType), append);
      appendChar = createMethod(receiver, createProto(receiver, charType), append);
      appendCharArray = createMethod(receiver, createProto(receiver, charArrayType), append);
      appendSubCharArray =
          createMethod(receiver, createProto(receiver, charArrayType, intType, intType), append);
      appendCharSequence = createMethod(receiver, createProto(receiver, charSequenceType), append);
      appendSubCharSequence =
          createMethod(receiver, createProto(receiver, charSequenceType, intType, intType), append);
      appendInt = createMethod(receiver, createProto(receiver, intType), append);
      appendDouble = createMethod(receiver, createProto(receiver, doubleType), append);
      appendFloat = createMethod(receiver, createProto(receiver, floatType), append);
      appendLong = createMethod(receiver, createProto(receiver, longType), append);
      appendObject = createMethod(receiver, createProto(receiver, objectType), append);
      appendString = createMethod(receiver, createProto(receiver, stringType), append);
      appendStringBuffer = createMethod(receiver, createProto(receiver, sbufType), append);
      toString = createMethod(receiver, createProto(stringType), toStringMethodName);
    }

    public void forEachAppendMethod(Consumer<DexMethod> consumer) {
      consumer.accept(appendBoolean);
      consumer.accept(appendChar);
      consumer.accept(appendCharArray);
      consumer.accept(appendSubCharArray);
      consumer.accept(appendCharSequence);
      consumer.accept(appendSubCharSequence);
      consumer.accept(appendInt);
      consumer.accept(appendDouble);
      consumer.accept(appendFloat);
      consumer.accept(appendLong);
      consumer.accept(appendObject);
      consumer.accept(appendString);
      consumer.accept(appendStringBuffer);
      consumer.accept(appendBoolean);
    }
  }

  synchronized private static <T extends DexItem> T canonicalize(Map<T, T> map, T item) {
    assert item != null;
    assert !internalSentinels.contains(item);
    T previous = map.putIfAbsent(item, item);
    return previous == null ? item : previous;
  }

  synchronized private DexString canonicalizeString(String key) {
    assert key != null;
    return strings.computeIfAbsent(key, k -> new DexString(k));
  }

  public DexString createString(int size, byte[] content) {
    assert !sorted;
    return canonicalizeString(new DexString(size, content).toString());
  }

  public DexString createString(String source) {
    assert !sorted;
    return canonicalizeString(source);
  }

  public DexType createType(DexString descriptor) {
    assert !sorted;
    DexType type = new DexType(descriptor);
    return canonicalize(types, type);
  }

  public DexType createType(String descriptor) {
    return createType(createString(descriptor));
  }

  public DexField createField(DexType clazz, DexType type, DexString name) {
    assert !sorted;
    DexField field = new DexField(clazz, type, name);
    return canonicalize(fields, field);
  }

  public DexField createField(DexType clazz, DexType type, String name) {
    return createField(clazz, type, createString(name));
  }

  public DexProto createProto(DexString shorty, DexType returnType, DexTypeList parameters) {
    assert !sorted;
    DexProto proto = new DexProto(shorty, returnType, parameters);
    return canonicalize(protos, proto);
  }

  public DexProto createProto(DexString shorty, DexType returnType, DexType[] parameters) {
    assert !sorted;
    return createProto(shorty, returnType,
        parameters.length == 0 ? DexTypeList.empty() : new DexTypeList(parameters));
  }

  public DexProto createProto(DexType returnType, DexType... parameters) {
    return createProto(createShorty(returnType, parameters), returnType, parameters);
  }

  public DexString createShorty(DexType returnType, DexType[] argumentTypes) {
    StringBuilder shortyBuilder = new StringBuilder();
    shortyBuilder.append(returnType.toShorty());
    for (DexType argumentType : argumentTypes) {
      shortyBuilder.append(argumentType.toShorty());
    }
    return createString(shortyBuilder.toString());
  }

  public DexMethod createMethod(DexType holder, DexProto proto, DexString name) {
    assert !sorted;
    DexMethod method = new DexMethod(holder, proto, name);
    return canonicalize(methods, method);
  }

  public DexMethod createMethod(DexType holder, DexProto proto, String name) {
    return createMethod(holder, proto, createString(name));
  }

  public DexMethodHandle createMethodHandle(
      MethodHandleType type, Descriptor<? extends DexItem, ? extends Descriptor> fieldOrMethod) {
    assert !sorted;
    DexMethodHandle methodHandle = new DexMethodHandle(type, fieldOrMethod);
    return canonicalize(methodHandles, methodHandle);
  }

  public DexCallSite createCallSite(
      DexString methodName, DexProto methodProto,
      DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
    assert !sorted;
    DexCallSite callSite = new DexCallSite(methodName, methodProto, bootstrapMethod, bootstrapArgs);
    return canonicalize(callSites, callSite);
  }

  public DexMethod createMethod(DexString clazzDescriptor, DexString name,
      DexString returnTypeDescriptor,
      DexString[] parameterDescriptors) {
    assert !sorted;
    DexType clazz = createType(clazzDescriptor);
    DexType returnType = createType(returnTypeDescriptor);
    DexType[] parameterTypes = new DexType[parameterDescriptors.length];
    for (int i = 0; i < parameterDescriptors.length; i++) {
      parameterTypes[i] = createType(parameterDescriptors[i]);
    }
    DexProto proto = createProto(shorty(returnType, parameterTypes), returnType, parameterTypes);

    return createMethod(clazz, proto, name);
  }

  public AdvanceLine createAdvanceLine(int delta) {
    synchronized (advanceLines) {
      AdvanceLine result = advanceLines.get(delta);
      if (result == null) {
        result = new AdvanceLine(delta);
        advanceLines.put(delta, result);
      }
      return result;
    }
  }

  public AdvancePC createAdvancePC(int delta) {
    synchronized (advancePCs) {
      AdvancePC result = advancePCs.get(delta);
      if (result == null) {
        result = new AdvancePC(delta);
        advancePCs.put(delta, result);
      }
      return result;
    }
  }

  public Default createDefault(int value) {
    synchronized (defaults) {
      Default result = defaults.get(value);
      if (result == null) {
        result = new Default(value);
        defaults.put(value, result);
      }
      return result;
    }
  }

  public EndLocal createEndLocal(int registerNum) {
    synchronized (endLocals) {
      EndLocal result = endLocals.get(registerNum);
      if (result == null) {
        result = new EndLocal(registerNum);
        endLocals.put(registerNum, result);
      }
      return result;
    }
  }

  public RestartLocal createRestartLocal(int registerNum) {
    synchronized (restartLocals) {
      RestartLocal result = restartLocals.get(registerNum);
      if (result == null) {
        result = new RestartLocal(registerNum);
        restartLocals.put(registerNum, result);
      }
      return result;
    }
  }

  public SetEpilogueBegin createSetEpilogueBegin() {
    return setEpilogueBegin;
  }

  public SetPrologueEnd createSetPrologueEnd() {
    return setPrologueEnd;
  }

  public SetFile createSetFile(DexString fileName) {
    synchronized (setFiles) {
      SetFile result = setFiles.get(fileName);
      if (result == null) {
        result = new SetFile(fileName);
        setFiles.put(fileName, result);
      }
      return result;
    }
  }

  public boolean isConstructor(DexMethod method) {
    return method.name == constructorMethodName;
  }

  public boolean isClassConstructor(DexMethod method) {
    return method.name == classConstructorMethodName;
  }

  private DexString shorty(DexType returnType, DexType[] parameters) {
    StringBuilder builder = new StringBuilder();
    addToShorty(builder, returnType);
    for (DexType parameter : parameters) {
      addToShorty(builder, parameter);
    }
    return createString(builder.toString());
  }

  private void addToShorty(StringBuilder builder, DexType type) {
    char first = type.toDescriptorString().charAt(0);
    builder.append(first == '[' ? 'L' : first);
  }

  private static <S extends PresortedComparable<S>> void assignSortedIndices(Collection<S> items,
      NamingLens namingLens) {
    List<S> sorted = new ArrayList<>(items);
    sorted.sort((a, b) -> a.layeredCompareTo(b, namingLens));
    int i = 0;
    for (S value : sorted) {
      value.setSortedIndex(i++);
    }
  }

  synchronized public void sort(NamingLens namingLens) {
    assert !sorted;
    assignSortedIndices(strings.values(), namingLens);
    assignSortedIndices(types.values(), namingLens);
    assignSortedIndices(fields.values(), namingLens);
    assignSortedIndices(protos.values(), namingLens);
    assignSortedIndices(methods.values(), namingLens);
    sorted = true;
  }

  synchronized public void resetSortedIndices() {
    if (!sorted) {
      return;
    }
    // Only used for asserting that we don't use the sorted index after we build the graph.
    strings.values().forEach(IndexedDexItem::resetSortedIndex);
    types.values().forEach(IndexedDexItem::resetSortedIndex);
    fields.values().forEach(IndexedDexItem::resetSortedIndex);
    protos.values().forEach(IndexedDexItem::resetSortedIndex);
    methods.values().forEach(IndexedDexItem::resetSortedIndex);
    sorted = false;
  }

  synchronized public void forAllTypes(Consumer<DexType> f) {
    new ArrayList<>(types.values()).forEach(f);
  }
}