blob: 75ee123630a857bc091eaaee5df61eb1821c9092 [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 static com.android.tools.r8.utils.StringUtils.LINE_SEPARATOR;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.contexts.CompilationContext;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.kotlin.KotlinMetadataWriter;
import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.RetracerForCodePrinting;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.StringUtils.BraceType;
import java.io.BufferedReader;
import java.io.PrintStream;
import java.io.StringReader;
import java.util.stream.Collectors;
public class AssemblyWriter extends DexByteCodeWriter {
private final boolean writeAllClassInfo;
private final boolean writeFields;
private final boolean writeAnnotations;
private final boolean writeIR;
private final boolean writeCode;
private final AppInfo appInfo;
private final Kotlin kotlin;
private final CompilationContext compilationContext;
private final RetracerForCodePrinting retracer;
public AssemblyWriter(
DexApplication application,
InternalOptions options,
boolean allInfo,
boolean writeIR,
boolean writeCode) {
super(application, options);
this.compilationContext = CompilationContext.createInitialContext(options);
this.writeAllClassInfo = allInfo;
this.writeFields = allInfo;
this.writeAnnotations = allInfo;
this.writeIR = writeIR;
this.writeCode = writeCode;
if (writeIR) {
this.appInfo =
AppInfo.createInitialAppInfo(
application.toDirect(), GlobalSyntheticsStrategy.forNonSynthesizing());
if (options.programConsumer == null) {
// Use class-file backend, since the CF frontend for testing does not support desugaring of
// synchronized methods for the DEX backend (b/109789541).
options.programConsumer = ClassFileConsumer.emptyConsumer();
}
options.outline.enabled = false;
} else {
this.appInfo = null;
}
kotlin = new Kotlin(application.dexItemFactory);
retracer =
RetracerForCodePrinting.create(application.getProguardMap(), application.options.reporter);
}
public static String getFileEnding() {
return ".dump";
}
@Override
@SuppressWarnings("ReferenceEquality")
void writeClassHeader(DexProgramClass clazz, PrintStream ps) {
String clazzName = retracer.toSourceString(clazz.getType());
ps.println("# Bytecode for");
ps.println("# Class: '" + clazzName + "'");
if (writeAllClassInfo) {
ClassSignature signature = clazz.getClassSignature();
if (signature != null && signature.hasSignature()) {
ps.println("# Signature: " + signature);
}
writeAnnotations(clazz, clazz.annotations(), ps);
ps.println("# Flags: '" + clazz.accessFlags + "'");
if (clazz.superType != application.dexItemFactory.objectType) {
ps.println("# Extends: '" + retracer.toSourceString(clazz.superType) + "'");
}
for (DexType value : clazz.interfaces.values) {
ps.println("# Implements: '" + retracer.toSourceString(value) + "'");
}
if (!clazz.getInnerClasses().isEmpty()) {
ps.println("# InnerClasses:");
for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
ps.println(
"# Outer: "
+ (innerClassAttribute.getOuter() != null
? retracer.toSourceString(innerClassAttribute.getOuter())
: "-")
+ ", inner: "
+ retracer.toSourceString(innerClassAttribute.getInner())
+ ", inner name: "
+ innerClassAttribute.getInnerName()
+ ", access: "
+ Integer.toHexString(innerClassAttribute.getAccess()));
}
}
EnclosingMethodAttribute enclosingMethodAttribute = clazz.getEnclosingMethodAttribute();
if (enclosingMethodAttribute != null) {
ps.println("# EnclosingMethod:");
if (enclosingMethodAttribute.getEnclosingClass() != null) {
ps.println(
"# Class: " + retracer.toSourceString(enclosingMethodAttribute.getEnclosingClass()));
} else {
ps.println(
"# Method: "
+ retracer.toSourceString(enclosingMethodAttribute.getEnclosingMethod()));
}
}
}
ps.println();
}
@Override
void writeFieldsHeader(DexProgramClass clazz, PrintStream ps) {
if (writeFields) {
ps.println("#");
ps.println("# Fields:");
ps.println("#");
}
}
@Override
void writeField(DexEncodedField field, PrintStream ps) {
if (writeFields) {
writeAnnotations(null, field.annotations(), ps);
ps.print(field.accessFlags + " ");
ps.print(retracer.toSourceString(field.getReference()));
if (field.isStatic() && field.hasExplicitStaticValue()) {
ps.print(" = " + field.getStaticValue());
}
ps.println();
if (!retracer.isEmpty()) {
ps.println("# Residual: '" + field.getReference().toSourceString() + "'");
}
if (field.hasNonIdentityOriginalFieldWitness()) {
ps.println("# Original: '" + field.getOriginalFieldWitness() + "'");
}
}
}
@Override
void writeFieldsFooter(DexProgramClass clazz, PrintStream ps) {
ps.println();
}
@Override
void writeMethod(ProgramMethod method, PrintStream ps) {
DexEncodedMethod definition = method.getDefinition();
ps.println("#");
ps.println("# Method: '" + retracer.toSourceString(definition.getReference()) + "':");
writeAnnotations(null, definition.annotations(), ps);
ps.println("# " + definition.accessFlags);
if (!retracer.isEmpty()) {
ps.println("# Residual: '" + definition.getReference().toSourceString() + "'");
}
ps.println("#");
ps.println();
if (!writeCode) {
return;
}
Code code = definition.getCode();
if (code != null) {
if (writeIR) {
writeIR(method);
} else {
ps.println(code.toString(definition, retracer));
}
}
}
private void writeIR(ProgramMethod method) {
IRConverter converter = new IRConverter(appInfo);
MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
OneTimeMethodProcessor methodProcessor =
OneTimeMethodProcessor.create(
method, eventConsumer, compilationContext.createProcessorContext());
methodProcessor.forEachWaveWithExtension(
(ignore, methodProcessingContext) ->
converter.processDesugaredMethod(
method,
OptimizationFeedbackIgnore.getInstance(),
methodProcessor,
methodProcessingContext,
MethodConversionOptions.forD8(converter.appView)));
}
@SuppressWarnings("ReferenceEquality")
private void writeAnnotations(
DexProgramClass clazz, DexAnnotationSet annotations, PrintStream ps) {
if (writeAnnotations) {
if (!annotations.isEmpty()) {
ps.println("# Annotations:");
String prefix = "# ";
for (DexAnnotation annotation : annotations.annotations) {
if (annotation.annotation.type == kotlin.factory.kotlinMetadataType) {
assert clazz != null : "Kotlin metadata is a class annotation";
KotlinMetadataWriter.writeKotlinMetadataAnnotation(prefix, annotation, ps, kotlin);
} else {
StringBuilder sb = new StringBuilder();
sb.append(annotation.getVisibility());
sb.append(" ");
sb.append(retracer.toSourceString(annotation.getAnnotationType()));
sb.append(
StringUtils.join(
",",
annotation.annotation.elements,
element ->
element.getName().toString() + " = " + getStringValue(element.getValue()),
BraceType.SQUARE));
ps.print(
new BufferedReader(new StringReader(sb.toString()))
.lines()
.collect(
Collectors.joining(
LINE_SEPARATOR + prefix + " ", prefix, LINE_SEPARATOR)));
}
}
}
}
}
private String getStringValue(DexValue value) {
if (value.isDexValueType()) {
return retracer.toSourceString(value.asDexValueType().getValue());
} else if (value.isDexValueMethodHandle()) {
return retracer.toSourceString(value.asDexValueMethodHandle().value.asMethod());
} else if (value.isDexValueMethod()) {
return retracer.toSourceString(value.asDexValueMethod().value);
} else if (value.isDexItemBasedValueString()) {
return retracer.toSourceString(value.asDexItemBasedValueString().value);
} else if (value.isDexValueEnum()) {
return retracer.toSourceString(value.asDexValueEnum().value);
} else if (value.isDexValueField()) {
return retracer.toSourceString(value.asDexValueField().value);
} else if (value.isDexValueArray()) {
return "[" + value.asDexValueArray() + "]";
} else {
return value.toString();
}
}
@Override
void writeClassFooter(DexProgramClass clazz, PrintStream ps) {
}
}