blob: bb0fbf8fe6954a9893a8849e3048815fba3b3ee2 [file] [log] [blame]
// Copyright (c) 2017, 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.IndexedItemCollection;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import org.objectweb.asm.ClassWriter;
/** Representation of an entry in the Java InnerClasses attribute table. */
public class InnerClassAttribute {
// Access flags to the inner class as declared in the program source.
private final int access;
// Type of the inner class.
private final DexType inner;
// Type of the enclosing class, null if the inner class is top-level, local or anonymous.
private final DexType outer;
// Short name of the inner class, null if the inner class is anonymous.
private final DexString innerName;
// Create a named inner-class attribute, but with some arbitrary/unknown name.
// This is needed to partially map back to the Java attribute structures when reading DEX inputs.
public static InnerClassAttribute createUnknownNamedInnerClass(DexType inner, DexType outer) {
return new InnerClassAttribute(0, inner, outer, DexItemFactory.unknownTypeName);
}
public InnerClassAttribute(int access, DexType inner, DexType outer, DexString innerName) {
assert inner != null;
this.access = access;
this.inner = inner;
this.outer = outer;
this.innerName = innerName;
}
public static List<InnerClassAttribute> emptyList() {
return Collections.emptyList();
}
public void forEachType(Consumer<DexType> consumer) {
if (inner != null) {
consumer.accept(inner);
}
if (outer != null) {
consumer.accept(outer);
}
}
public boolean isNamed() {
return innerName != null;
}
public boolean isAnonymous() {
return innerName == null;
}
public int getAccess() {
return access;
}
public DexType getInner() {
return inner;
}
public DexType getOuter() {
return outer;
}
public DexString getInnerName() {
return innerName;
}
public void write(ClassWriter writer, NamingLens lens, InternalOptions options) {
String internalName = lens.lookupInternalName(inner);
writer.visitInnerClass(
internalName,
outer == null ? null : lens.lookupInternalName(outer),
innerName == null ? null : lens.lookupInnerName(this, options).toString(),
access);
}
public void collectIndexedItems(IndexedItemCollection indexedItems) {
inner.collectIndexedItems(indexedItems);
if (outer != null) {
outer.collectIndexedItems(indexedItems);
}
if (innerName != null) {
innerName.collectIndexedItems(indexedItems);
}
}
public DexType getLiveContext(AppView<? extends AppInfoWithLiveness> appView) {
DexType context = getOuter();
if (context == null) {
DexClass inner = appView.definitionFor(getInner());
if (inner != null && inner.getEnclosingMethodAttribute() != null) {
EnclosingMethodAttribute enclosingMethodAttribute = inner.getEnclosingMethodAttribute();
if (enclosingMethodAttribute.getEnclosingClass() != null) {
context = enclosingMethodAttribute.getEnclosingClass();
} else {
DexMethod enclosingMethod = enclosingMethodAttribute.getEnclosingMethod();
if (!appView.appInfo().isLiveMethod(enclosingMethod)) {
// EnclosingMethodAttribute will be pruned as it references the pruned method.
// Hence, the current InnerClassAttribute will be removed too. No live context.
return null;
}
context = enclosingMethod.holder;
}
}
}
return context;
}
@Override
public String toString() {
return "[access : " + Integer.toHexString(access)
+ ", inner: " + inner.toDescriptorString()
+ ", outer: " + (outer == null ? "null" : outer.toDescriptorString())
+ ", innerName: " + (innerName == null ? "(anonymous)" : innerName.toString())
+ "]";
}
}