blob: bd6c452e00d2a527e6b20afcdae04fe17694f0db [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.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodProcessingId;
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.naming.ClassNameMapper;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import java.io.BufferedReader;
import java.io.PrintStream;
import java.io.StringReader;
import java.util.stream.Collectors;
public class AssemblyWriter extends DexByteCodeWriter {
private final MethodProcessingId.Factory methodProcessingIdFactory =
new MethodProcessingId.Factory();
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 Timing timing = new Timing("AssemblyWriter");
public AssemblyWriter(
DexApplication application,
InternalOptions options,
boolean allInfo,
boolean writeIR,
boolean writeCode) {
super(application, options);
this.writeAllClassInfo = allInfo;
this.writeFields = allInfo;
this.writeAnnotations = allInfo;
this.writeIR = writeIR;
this.writeCode = writeCode;
if (writeIR) {
this.appInfo = AppInfo.createInitialAppInfo(application.toDirect());
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);
}
@Override
String getFileEnding() {
return ".dump";
}
@Override
void writeClassHeader(DexProgramClass clazz, PrintStream ps) {
String clazzName;
if (application.getProguardMap() != null) {
clazzName = application.getProguardMap().originalNameOf(clazz.type);
} else {
clazzName = clazz.type.toSourceString();
}
ps.println("# Bytecode for");
ps.println("# Class: '" + clazzName + "'");
if (writeAllClassInfo) {
writeAnnotations(clazz, clazz.annotations(), ps);
ps.println("# Flags: '" + clazz.accessFlags + "'");
if (clazz.superType != application.dexItemFactory.objectType) {
ps.println("# Extends: '" + clazz.superType.toSourceString() + "'");
}
for (DexType value : clazz.interfaces.values) {
ps.println("# Implements: '" + value.toSourceString() + "'");
}
if (!clazz.getInnerClasses().isEmpty()) {
ps.println("# InnerClasses:");
for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
ps.println(
"# Outer: "
+ (innerClassAttribute.getOuter() != null
? innerClassAttribute.getOuter().toSourceString()
: "-")
+ ", inner: "
+ innerClassAttribute.getInner().toSourceString()
+ ", 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: " + enclosingMethodAttribute.getEnclosingClass().toSourceString());
} else {
ps.println(
"# Method: " + enclosingMethodAttribute.getEnclosingMethod().toSourceString());
}
}
}
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) {
ClassNameMapper naming = application.getProguardMap();
FieldSignature fieldSignature = naming != null
? naming.originalSignatureOf(field.field)
: FieldSignature.fromDexField(field.field);
writeAnnotations(null, field.annotations(), ps);
ps.print(field.accessFlags + " ");
ps.print(fieldSignature);
if (field.isStatic() && field.hasExplicitStaticValue()) {
ps.print(" = " + field.getStaticValue());
}
ps.println();
}
}
@Override
void writeFieldsFooter(DexProgramClass clazz, PrintStream ps) {
ps.println();
}
@Override
void writeMethod(ProgramMethod method, PrintStream ps) {
DexEncodedMethod definition = method.getDefinition();
ClassNameMapper naming = application.getProguardMap();
String methodName =
naming != null
? naming.originalSignatureOf(method.getReference()).name
: method.getReference().name.toString();
ps.println("#");
ps.println("# Method: '" + methodName + "':");
writeAnnotations(null, definition.annotations(), ps);
ps.println("# " + definition.accessFlags);
ps.println("#");
ps.println();
if (!writeCode) {
return;
}
Code code = definition.getCode();
if (code != null) {
if (writeIR) {
writeIR(method, ps);
} else {
ps.println(code.toString(definition, naming));
}
}
}
private void writeIR(ProgramMethod method, PrintStream ps) {
CfgPrinter printer = new CfgPrinter();
IRConverter converter = new IRConverter(appInfo, timing, printer);
OneTimeMethodProcessor methodProcessor =
OneTimeMethodProcessor.create(method, methodProcessingIdFactory);
methodProcessor.forEachWave(
(ignore, methodProcesingId) ->
converter.processMethod(
method,
OptimizationFeedbackIgnore.getInstance(),
methodProcessor,
methodProcesingId));
ps.println(printer.toString());
}
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 {
String annotationString = annotation.toString();
ps.print(
new BufferedReader(new StringReader(annotationString))
.lines()
.collect(
Collectors.joining(
LINE_SEPARATOR + prefix + " ", prefix, LINE_SEPARATOR)));
}
}
}
}
}
@Override
void writeClassFooter(DexProgramClass clazz, PrintStream ps) {
}
}