blob: d6c5bdfe966bae117bfb4b640682be1c9772c5a5 [file] [log] [blame]
// 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.graph.DexMethodHandle.MethodHandleType;
import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaring;
import com.android.tools.r8.keepanno.ast.KeepDeclaration;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.objectweb.asm.Type;
/**
* Common structures used while reading in a Java application from jar files.
*
* The primary use of this class is to canonicalize dex items during read.
* The addition of classes to the builder also takes place through this class.
* It does not currently support multithreaded reading.
*/
public class JarApplicationReader {
public final InternalOptions options;
private final ConcurrentHashMap<String, Type> asmObjectTypeCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Type> asmTypeCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, DexString> stringCache = new ConcurrentHashMap<>();
private final ApplicationReaderMap applicationReaderMap;
private final DexApplicationReadFlags.Builder readFlagsBuilder;
private final List<KeepDeclaration> keepDeclarations = new ArrayList<>();
public JarApplicationReader(
InternalOptions options, DexApplicationReadFlags.Builder readFlagsBuilder) {
this.options = options;
this.readFlagsBuilder = readFlagsBuilder;
applicationReaderMap = ApplicationReaderMap.getInstance(options);
}
public JarApplicationReader(InternalOptions options) {
this(options, DexApplicationReadFlags.builder());
}
public void addKeepDeclaration(KeepDeclaration declaration) {
synchronized (keepDeclarations) {
keepDeclarations.add(declaration);
}
}
public List<KeepDeclaration> getKeepDeclarations() {
return keepDeclarations;
}
public Type getAsmObjectType(String name) {
return asmObjectTypeCache.computeIfAbsent(name, Type::getObjectType);
}
public Type getAsmType(String name) {
return asmTypeCache.computeIfAbsent(name, Type::getType);
}
public DexItemFactory getFactory() {
return options.itemFactory;
}
public DexString getString(String string) {
return stringCache.computeIfAbsent(string, options.itemFactory::createString);
}
public DexType getType(Type type) {
return getTypeFromDescriptor(type.getDescriptor());
}
public DexType getTypeFromName(String name) {
assert isValidInternalName(name);
return getType(getAsmObjectType(name));
}
public DexType getTypeFromDescriptor(String desc) {
assert isValidDescriptor(desc);
String actualDesc = applicationReaderMap.getDescriptor(desc);
return options.itemFactory.createType(getString(actualDesc));
}
public DexTypeList getTypeListFromNames(String[] names) {
if (names.length == 0) {
return DexTypeList.empty();
}
DexType[] types = new DexType[names.length];
for (int i = 0; i < names.length; i++) {
types[i] = getTypeFromName(names[i]);
}
return new DexTypeList(types);
}
public DexTypeList getTypeListFromDescriptors(String[] descriptors) {
if (descriptors.length == 0) {
return DexTypeList.empty();
}
DexType[] types = new DexType[descriptors.length];
for (int i = 0; i < descriptors.length; i++) {
types[i] = getTypeFromDescriptor(descriptors[i]);
}
return new DexTypeList(types);
}
public DexField getField(String owner, String name, String desc) {
return getField(getTypeFromName(owner), name, desc);
}
public DexField getField(DexType owner, String name, String desc) {
return options.itemFactory.createField(owner, getTypeFromDescriptor(desc), getString(name));
}
public DexMethod getMethod(String owner, String name, String desc) {
return getMethod(getTypeFromName(owner), name, desc);
}
public DexMethod getMethod(DexType owner, String name, String desc) {
return options.itemFactory.createMethod(owner, getProto(desc), getString(name));
}
public DexCallSite getCallSite(String methodName, String methodProto,
DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
return options.itemFactory.createCallSite(
getString(methodName), getProto(methodProto), bootstrapMethod, bootstrapArgs);
}
public DexMethodHandle getMethodHandle(
MethodHandleType type,
DexMember<? extends DexItem, ? extends DexMember<?, ?>> fieldOrMethod,
boolean isInterface) {
return options.itemFactory.createMethodHandle(type, fieldOrMethod, isInterface);
}
public DexProto getProto(String desc) {
assert isValidDescriptor(desc);
String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(desc);
String[] argumentDescriptors = DescriptorUtils.getArgumentTypeDescriptors(desc);
return options.itemFactory.createProto(
getTypeFromDescriptor(returnTypeDescriptor),
getTypeListFromDescriptors(argumentDescriptors));
}
private boolean isValidDescriptor(String desc) {
return getAsmType(desc).getDescriptor().equals(desc);
}
private boolean isValidInternalName(String name) {
return getAsmObjectType(name).getInternalName().equals(name);
}
public Type getReturnType(final String methodDescriptor) {
return getAsmType(DescriptorUtils.getReturnTypeDescriptor(methodDescriptor));
}
public void addRecordWitness(DexType witness, ClassKind<?> classKind) {
if (classKind == ClassKind.PROGRAM) {
readFlagsBuilder.addRecordWitness(witness);
}
}
public void checkFieldForRecord(DexField dexField, ClassKind<?> classKind) {
if (options.shouldDesugarRecords() && RecordDesugaring.refersToRecord(dexField, getFactory())) {
addRecordWitness(dexField.getHolderType(), classKind);
}
}
public void checkMethodForRecord(DexMethod dexMethod, ClassKind<?> classKind) {
if (options.shouldDesugarRecords()
&& RecordDesugaring.refersToRecord(dexMethod, getFactory())) {
addRecordWitness(dexMethod.getHolderType(), classKind);
}
}
public void addVarHandleWitness(DexType witness, ClassKind<?> classKind) {
if (classKind == ClassKind.PROGRAM) {
readFlagsBuilder.addVarHandleWitness(witness);
}
}
public void checkFieldForVarHandle(DexField dexField, ClassKind<?> classKind) {
if (options.shouldDesugarVarHandle()
&& VarHandleDesugaring.refersToVarHandle(dexField, getFactory())) {
addVarHandleWitness(dexField.getHolderType(), classKind);
}
}
public void checkMethodForVarHandle(DexMethod dexMethod, ClassKind<?> classKind) {
if (options.shouldDesugarVarHandle()
&& VarHandleDesugaring.refersToVarHandle(dexMethod, getFactory())) {
addVarHandleWitness(dexMethod.getHolderType(), classKind);
}
}
public void addMethodHandlesLookupWitness(DexType witness, ClassKind<?> classKind) {
if (classKind == ClassKind.PROGRAM) {
readFlagsBuilder.addMethodHandlesLookupWitness(witness);
}
}
@SuppressWarnings("ReferenceEquality")
public void checkClassForMethodHandlesLookup(DexClass dexClass, ClassKind<?> classKind) {
if (options.shouldDesugarVarHandle()) {
if (VarHandleDesugaring.refersToMethodHandlesLookup(dexClass.getType(), getFactory())) {
addVarHandleWitness(dexClass.getType(), classKind);
}
dexClass
.getInnerClasses()
.forEach(
attribute -> {
// MethodHandles$Lookup has no inner classes.
assert !VarHandleDesugaring.refersToMethodHandlesLookup(
attribute.getOuter(), getFactory());
if (VarHandleDesugaring.refersToMethodHandlesLookup(
attribute.getInner(), getFactory())) {
addMethodHandlesLookupWitness(dexClass.getType(), classKind);
// When the inner MethodHandles$Lookup is present the outer is MethodHandles, and
// in that case the enqueuer will process all methods in the library class
// MethodHandles which have references to VarHandle.
assert attribute.getOuter() == getFactory().methodHandlesType;
addVarHandleWitness(dexClass.getType(), classKind);
}
});
}
}
public void checkFieldForMethodHandlesLookup(DexField dexField, ClassKind<?> classKind) {
if (options.shouldDesugarVarHandle()
&& VarHandleDesugaring.refersToMethodHandlesLookup(dexField, getFactory())) {
addMethodHandlesLookupWitness(dexField.getHolderType(), classKind);
}
}
public void checkMethodForMethodHandlesLookup(DexMethod dexMethod, ClassKind<?> classKind) {
if (options.shouldDesugarVarHandle()
&& VarHandleDesugaring.refersToMethodHandlesLookup(dexMethod, getFactory())) {
addMethodHandlesLookupWitness(dexMethod.getHolderType(), classKind);
}
}
}