| // 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.ClassFileConsumer; |
| import com.android.tools.r8.ir.conversion.IRConverter; |
| 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.KotlinClassMetadataReader; |
| import com.android.tools.r8.kotlin.KotlinInfo; |
| 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.PrintStream; |
| |
| public class AssemblyWriter extends DexByteCodeWriter { |
| |
| private final boolean writeAllClassInfo; |
| private final boolean writeFields; |
| private final boolean writeAnnotations; |
| private final boolean writeIR; |
| private final AppInfoWithSubtyping appInfo; |
| private final Kotlin kotlin; |
| private final Timing timing = new Timing("AssemblyWriter"); |
| |
| public AssemblyWriter( |
| DexApplication application, InternalOptions options, boolean allInfo, boolean writeIR) { |
| super(application, options); |
| this.writeAllClassInfo = allInfo; |
| this.writeFields = allInfo; |
| this.writeAnnotations = allInfo; |
| this.writeIR = writeIR; |
| if (writeIR) { |
| this.appInfo = new AppInfoWithSubtyping(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() + "'"); |
| } |
| } |
| 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(DexEncodedMethod method, PrintStream ps) { |
| ClassNameMapper naming = application.getProguardMap(); |
| String methodName = naming != null |
| ? naming.originalSignatureOf(method.method).name |
| : method.method.name.toString(); |
| ps.println("#"); |
| ps.println("# Method: '" + methodName + "':"); |
| writeAnnotations(null, method.annotations(), ps); |
| ps.println("# " + method.accessFlags); |
| ps.println("#"); |
| ps.println(); |
| Code code = method.getCode(); |
| if (code != null) { |
| if (writeIR) { |
| writeIR(method, ps); |
| } else { |
| ps.println(code.toString(method, naming)); |
| } |
| } |
| } |
| |
| private void writeIR(DexEncodedMethod method, PrintStream ps) { |
| CfgPrinter printer = new CfgPrinter(); |
| new IRConverter(appInfo, options, timing, printer) |
| .processMethod( |
| method, |
| OptimizationFeedbackIgnore.getInstance(), |
| OneTimeMethodProcessor.getInstance()); |
| ps.println(printer.toString()); |
| } |
| |
| private void writeAnnotations( |
| DexProgramClass clazz, DexAnnotationSet annotations, PrintStream ps) { |
| if (writeAnnotations) { |
| if (!annotations.isEmpty()) { |
| ps.println("# Annotations:"); |
| for (DexAnnotation annotation : annotations.annotations) { |
| if (annotation.annotation.type == kotlin.metadata.kotlinMetadataType) { |
| assert clazz != null : "Kotlin metadata is a class annotation"; |
| writeKotlinMetadata(clazz, annotation, ps); |
| } else { |
| ps.print("# "); |
| ps.println(annotation); |
| } |
| } |
| } |
| } |
| } |
| |
| private void writeKotlinMetadata( |
| DexProgramClass clazz, DexAnnotation annotation, PrintStream ps) { |
| assert annotation.annotation.type == kotlin.metadata.kotlinMetadataType; |
| KotlinInfo kotlinInfo = |
| KotlinClassMetadataReader.createKotlinInfo(kotlin, clazz, annotation.annotation); |
| ps.println(kotlinInfo.toString("# ")); |
| } |
| |
| @Override |
| void writeClassFooter(DexProgramClass clazz, PrintStream ps) { |
| |
| } |
| } |