blob: f9c73aceccca5a74f0a2bc99ac60ecc8c214a009 [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.jar;
import static org.objectweb.asm.Opcodes.ASM6;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.ProguardMapSupplier;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.ExecutorService;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.util.CheckClassAdapter;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;
public class CfApplicationWriter {
private final DexApplication application;
private final NamingLens namingLens;
private final InternalOptions options;
public final ProguardMapSupplier proguardMapSupplier;
public final String deadCode;
public final String proguardSeedsData;
public CfApplicationWriter(
DexApplication application,
InternalOptions options,
String deadCode,
NamingLens namingLens,
String proguardSeedsData,
ProguardMapSupplier proguardMapSupplier) {
this.application = application;
this.namingLens = namingLens;
this.options = options;
this.proguardMapSupplier = proguardMapSupplier;
this.deadCode = deadCode;
this.proguardSeedsData = proguardSeedsData;
}
public void write(ClassFileConsumer consumer, ExecutorService executor) throws IOException {
application.timing.begin("CfApplicationWriter.write");
try {
writeApplication(consumer, executor);
} finally {
application.timing.end();
}
}
private void writeApplication(ClassFileConsumer consumer, ExecutorService executor)
throws IOException {
for (DexProgramClass clazz : application.classes()) {
if (clazz.getSynthesizedFrom().isEmpty()) {
writeClass(clazz, consumer);
} else {
throw new Unimplemented("No support for synthetics in the Java bytecode backend.");
}
}
ApplicationWriter.supplyAdditionalConsumers(
application, namingLens, options, deadCode, proguardMapSupplier, proguardSeedsData);
}
private void writeClass(DexProgramClass clazz, ClassFileConsumer consumer) throws IOException {
ClassWriter writer = new ClassWriter(0);
writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, null);
int version = clazz.getClassFileVersion();
int access = clazz.accessFlags.getAsCfAccessFlags();
String desc = clazz.type.toDescriptorString();
String name = clazz.type.getInternalName();
String signature = null; // TODO(zerny): Support generic signatures.
String superName =
clazz.type == options.itemFactory.objectType ? null : clazz.superType.getInternalName();
String[] interfaces = new String[clazz.interfaces.values.length];
for (int i = 0; i < clazz.interfaces.values.length; i++) {
interfaces[i] = clazz.interfaces.values[i].getInternalName();
}
writer.visit(version, access, name, signature, superName, interfaces);
if (clazz.getEnclosingMethod() != null) {
clazz.getEnclosingMethod().write(writer);
}
for (InnerClassAttribute entry : clazz.getInnerClasses()) {
entry.write(writer);
}
for (DexEncodedField field : clazz.staticFields()) {
writeField(field, writer);
}
for (DexEncodedField field : clazz.instanceFields()) {
writeField(field, writer);
}
for (DexEncodedMethod method : clazz.directMethods()) {
writeMethod(method, writer);
}
for (DexEncodedMethod method : clazz.virtualMethods()) {
writeMethod(method, writer);
}
writer.visitEnd();
byte[] result = writer.toByteArray();
assert verifyCf(result);
ExceptionUtils.withConsumeResourceHandler(
options.reporter, handler -> consumer.accept(result, desc, handler));
}
private Object getStaticValue(DexEncodedField field) {
if (!field.accessFlags.isStatic() || field.staticValue == null) {
return null;
}
return field.staticValue.asAsmEncodedObject();
}
private void writeField(DexEncodedField field, ClassWriter writer) {
int access = field.accessFlags.getAsCfAccessFlags();
String name = field.field.name.toString();
String desc = field.field.type.toDescriptorString();
String signature = null; // TODO(zerny): Support generic signatures.
Object value = getStaticValue(field);
writer.visitField(access, name, desc, signature, value);
// TODO(zerny): Add annotations to the field.
}
private void writeMethod(DexEncodedMethod method, ClassWriter writer) {
int access = method.accessFlags.getAsCfAccessFlags();
String name = method.method.name.toString();
String desc = method.descriptor();
String signature = null; // TODO(zerny): Support generic signatures.
String[] exceptions = null;
MethodVisitor visitor = writer.visitMethod(access, name, desc, signature, exceptions);
if (!method.accessFlags.isAbstract()) {
writeCode(method.getCode(), visitor);
}
}
private void writeCode(Code code, MethodVisitor visitor) {
if (code.isJarCode()) {
code.asJarCode().writeTo(visitor);
} else {
assert code.isCfCode();
code.asCfCode().write(visitor);
}
}
public static String printCf(byte[] result) {
ClassReader reader = new ClassReader(result);
ClassNode node = new ClassNode(ASM6);
reader.accept(node, ASM6);
StringWriter writer = new StringWriter();
for (MethodNode method : node.methods) {
writer.append(method.name).append(method.desc).append('\n');
TraceMethodVisitor visitor = new TraceMethodVisitor(new Textifier());
method.accept(visitor);
visitor.p.print(new PrintWriter(writer));
writer.append('\n');
}
return writer.toString();
}
private static boolean verifyCf(byte[] result) {
ClassReader reader = new ClassReader(result);
PrintWriter pw = new PrintWriter(System.out);
CheckClassAdapter.verify(reader, false, pw);
return true;
}
}