blob: 680391d6d178393a8f664d45ce3e8a9531e4216f [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.jill.frontend.java;
import com.android.jill.backend.jayce.JayceWriter;
import com.android.jill.backend.jayce.Token;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
/**
* Write class node of Asm into Jayce.
*/
public class ClassNodeWriter extends JillWriter {
public static final int COMPILE_TIME_CONSTANT = 0x20000;
@Nonnull
private final AnnotationWriter annotWriter;
private static final int ORDINAL_UNKNOWN = -1;
public ClassNodeWriter(@Nonnull JayceWriter writer,
@Nonnull SourceInfoWriter sourceInfoWriter) {
super(writer, sourceInfoWriter);
annotWriter = new AnnotationWriter(writer, sourceInfoWriter);
}
public void write(@Nonnull ClassNode cn) throws IOException {
if (AsmHelper.isAnnotation(cn)) {
writeAnnotation(cn);
} else if (AsmHelper.isInterface(cn)) {
writeInterface(cn);
} else if (AsmHelper.isEnum(cn)){
writeEnum(cn);
} else {
writeClass(cn);
}
}
private void writeEnum(@Nonnull ClassNode cn) throws IOException {
sourceInfoWriter.writeDebugBegin(cn);
writer.writeKeyword(Token.ENUM);
writer.writeOpen();
writer.writeInt(AsmHelper.getModifiers(cn));
writer.writeId(AsmHelper.getDescriptor(cn));
writer.writeId(cn.superName != null ? Type.getObjectType(cn.superName).getDescriptor() : null);
writer.writeIds(AsmHelper.getDescriptorsFromInternalNames(cn.interfaces));
writeEnclosingInformation(cn);
writingInners(cn);
writeEnumFields(cn);
writeMethods(cn);
annotWriter.writeAnnotations(cn);
writer.writeOpenNodeList(); // Markers
writeOriginalTypeInfoMarker(cn);
writeThisRefTypeInfoMarker(cn);
writer.writeCloseNodeList();
sourceInfoWriter.writeDebugEnd(cn);
writer.writeClose();
}
private void writeClass(@Nonnull ClassNode cn) throws IOException {
sourceInfoWriter.writeDebugBegin(cn);
writer.writeKeyword(Token.CLASS);
writer.writeOpen();
writer.writeInt(AsmHelper.getModifiers(cn));
writer.writeId(AsmHelper.getDescriptor(cn));
writer.writeId(cn.superName != null ? Type.getObjectType(cn.superName).getDescriptor() : null);
writer.writeIds(AsmHelper.getDescriptorsFromInternalNames(cn.interfaces));
writeEnclosingInformation(cn);
writingInners(cn);
writeFields(cn);
writeMethods(cn);
annotWriter.writeAnnotations(cn);
writer.writeOpenNodeList(); // Markers
writeOriginalTypeInfoMarker(cn);
writeThisRefTypeInfoMarker(cn);
writer.writeCloseNodeList();
sourceInfoWriter.writeDebugEnd(cn);
writer.writeClose();
}
private void writeInterface(@Nonnull ClassNode cn) throws IOException {
assert isPackageInfoIfNotAbstract(cn);
sourceInfoWriter.writeDebugBegin(cn);
writer.writeKeyword(Token.INTERFACE);
writer.writeOpen();
writer.writeInt(AsmHelper.getModifiers(cn) | Opcodes.ACC_ABSTRACT);
writer.writeId(AsmHelper.getDescriptor(cn));
writer.writeIds(AsmHelper.getDescriptorsFromInternalNames(cn.interfaces));
writeEnclosingInformation(cn);
writingInners(cn);
writeFields(cn);
writeMethods(cn);
annotWriter.writeAnnotations(cn);
writer.writeOpenNodeList(); // Markers
writeOriginalTypeInfoMarker(cn);
writer.writeCloseNodeList();
sourceInfoWriter.writeDebugEnd(cn);
writer.writeClose();
}
private boolean isPackageInfoIfNotAbstract(@Nonnull ClassNode cn) {
return !(((AsmHelper.getModifiers(cn) & Opcodes.ACC_ABSTRACT) == 0)
&& !cn.name.endsWith("package-info"));
}
private void writeAnnotation(@Nonnull ClassNode cn) throws IOException {
sourceInfoWriter.writeDebugBegin(cn);
writer.writeKeyword(Token.ANNOTATION_TYPE);
writer.writeOpen();
annotWriter.writeRetentionPolicy(cn);
writer.writeInt(AsmHelper.getModifiers(cn));
writer.writeId(AsmHelper.getDescriptor(cn));
writer.writeIds(AsmHelper.getDescriptorsFromInternalNames(cn.interfaces));
writeEnclosingInformation(cn);
writingInners(cn);
writeFields(cn);
writeAnnotationMethods(cn);
annotWriter.writeAnnotations(cn);
writer.writeOpenNodeList(); // Markers
writeOriginalTypeInfoMarker(cn);
writer.writeCloseNodeList();
sourceInfoWriter.writeDebugEnd(cn);
writer.writeClose();
}
private void writeThisRefTypeInfoMarker(@Nonnull ClassNode cn) throws IOException {
String thisRefSignature = null;
for (MethodNode mn : cn.methods) {
if (!AsmHelper.isStatic(mn) && mn.localVariables != null) {
for (LocalVariableNode lvn : mn.localVariables) {
if (lvn.name.equals("this")) {
if (thisRefSignature == null) {
thisRefSignature = lvn.signature;
} else {
assert thisRefSignature.equals(lvn.signature);
}
}
}
}
}
if (thisRefSignature != null) {
writer.writeKeyword(Token.THIS_REF_TYPE_INFO);
writer.writeOpen();
writeValue(thisRefSignature);
writer.writeClose();
}
}
private void writeOriginalTypeInfoMarker(@Nonnull ClassNode cn) throws IOException {
writer.writeKeyword(Token.ORIGINAL_TYPE_INFO);
writer.writeOpen();
if (AsmHelper.isGenericSignature(cn)) {
writeValue(cn.signature);
} else {
writer.writeNull();
}
writer.writeString(AsmHelper.getSourceName(cn));
writer.writeClose();
}
private void writeOriginalTypeInfoMarker(@Nonnull FieldNode fn) throws IOException {
if (fn.signature != null) {
writer.writeKeyword(Token.ORIGINAL_TYPE_INFO);
writer.writeOpen();
writeValue(fn.signature);
// writer.writeNull(); // Source name not set
writer.writeString(null);
writer.writeClose();
} else {
writer.writeNull();
}
}
private void writeFields(@Nonnull ClassNode cn) throws IOException {
writer.writeOpenNodeList();
for (FieldNode fn : cn.fields) {
writeField(cn, fn, Token.FIELD);
}
writer.writeCloseNodeList();
}
private void writeEnumFields(@Nonnull ClassNode cn) throws IOException {
writer.writeOpenNodeList();
for (FieldNode fn : cn.fields) {
if (!AsmHelper.isEnumField(fn)) {
writeField(cn, fn, Token.FIELD);
} else {
writeField(cn, fn, Token.ENUM_FIELD);
}
}
writer.writeCloseNodeList();
}
private void writeField(@Nonnull ClassNode cn, @Nonnull FieldNode fn, @Nonnull Token kind)
throws IOException {
assert kind == Token.FIELD || kind == Token.ENUM_FIELD;
sourceInfoWriter.writeDebugBegin(cn, fn);
writer.writeKeyword(kind);
writer.writeOpen();
writer.writeInt(fn.value != null ? AsmHelper.getModifiers(fn) | COMPILE_TIME_CONSTANT
: AsmHelper.getModifiers(fn));
writer.writeString(fn.desc);
writer.writeString(fn.name);
writeFieldValue(cn, fn);
if (kind == Token.ENUM_FIELD) {
writer.writeInt(ORDINAL_UNKNOWN);
}
annotWriter.writeAnnotations(fn);
writer.writeOpenNodeList(); // Markers
writeOriginalTypeInfoMarker(fn);
writer.writeCloseNodeList();
sourceInfoWriter.writeDebugEnd(cn, fn);
writer.writeClose();
}
private void writeFieldValue(@Nonnull ClassNode cn, @Nonnull FieldNode fn) throws IOException {
if (AsmHelper.isStatic(fn)) {
Object value = fn.value;
if (value instanceof Integer) {
int intValue = ((Integer) value).intValue();
if (fn.desc.equals("Z")) {
writeValue(intValue != 0);
} else if (fn.desc.equals("B")) {
writeValue((byte) intValue);
} else if (fn.desc.equals("C")) {
writeValue((char) intValue);
} else if (fn.desc.equals("S")) {
writeValue((short) intValue);
} else {
writeValue(intValue);
}
} else if (value instanceof Long) {
writeValue(((Long) value).longValue());
} else if (value instanceof Float) {
writeValue(((Float) value).floatValue());
} else if (value instanceof Double) {
writeValue(((Double) value).doubleValue());
} else if (value instanceof String) {
writeValue((String) value);
} else {
writer.writeNull(); // No initial value
}
} else {
writer.writeNull(); // No initial value
}
}
private void writeAnnotationMethods(@Nonnull ClassNode cn) throws IOException {
assert AsmHelper.isAnnotation(cn);
writer.writeOpenNodeList();
for (MethodNode mn : cn.methods) {
new MethodBodyWriter(writer, annotWriter, cn, mn, sourceInfoWriter).write();
}
writer.writeCloseNodeList();
}
private void writeMethods(@Nonnull ClassNode cn) throws IOException {
writer.writeOpenNodeList();
for (MethodNode mn : cn.methods) {
new MethodBodyWriter(writer, annotWriter, cn, mn, sourceInfoWriter).write();
}
writer.writeCloseNodeList();
}
private void writingInners(@Nonnull ClassNode cn) throws IOException {
List<InnerClassNode> innerClasses = cn.innerClasses;
List<String> innerIds = new ArrayList<String>();
if (innerClasses != null) {
// Class is either an inner/local/anonymous or has inner classes
InnerClassNode matchingInnerClassNode = null;
for (InnerClassNode innerClassNode : innerClasses) {
if (innerClassNode.outerName != null && innerClassNode.outerName.equals(cn.name)
&& innerClassNode.name != null) {
innerIds.add(Type.getObjectType(innerClassNode.name).getDescriptor());
}
}
}
writer.writeIds(innerIds);
}
private void writeEnclosingInformation(@Nonnull ClassNode cn) throws IOException {
List<InnerClassNode> innerClasses = cn.innerClasses;
if (innerClasses != null) {
// Class is either an inner/local/anonymous or has inner classes
InnerClassNode matchingInnerClassNode = null;
for (InnerClassNode innerClassNode : innerClasses) {
if (innerClassNode.name.equals(cn.name)) {
matchingInnerClassNode = innerClassNode;
break;
}
}
if (matchingInnerClassNode != null) {
// Inner, anonymous or local)
if (cn.outerMethod != null) {
// Local or anonymous in method
assert cn.outerMethodDesc != null;
if (!(AsmHelper.isInterface(cn) || AsmHelper.isAnnotation(cn))) {
// EnclosingClass
writer.writeId(Type.getObjectType(cn.outerClass).getDescriptor()); // EnclosingType
writer.writeId(Type.getObjectType(cn.outerClass).getDescriptor());
writer.writeId(cn.outerMethod + cn.outerMethodDesc); // EnclosingMethod
} else {
writer.writeId(null); // EnclosingType
}
} else {
// Inner or anonymous as init of field
String outerClassName =
(cn.outerClass != null) ? (cn.outerClass) : (matchingInnerClassNode.outerName);
if (outerClassName != null) {
writer.writeId(Type.getObjectType(outerClassName).getDescriptor()); // EnclosingType
} else {
writer.writeId(null); // EnclosingType unknown
}
if (!(AsmHelper.isInterface(cn) || AsmHelper.isAnnotation(cn))) {
writer.writeId(null); // EnclosingMethodClass
writer.writeId(null); // EnclosingMethod
}
}
} else {
writer.writeId(null); // EnclosingType
if (!(AsmHelper.isInterface(cn) || AsmHelper.isAnnotation(cn))) {
writer.writeId(null); // EnclosingMethodClass
writer.writeId(null); // EnclosingMethod
}
}
} else {
writer.writeId(null); // EnclosingType
if (!(AsmHelper.isInterface(cn) || AsmHelper.isAnnotation(cn))) {
writer.writeId(null); // EnclosingMethodClass
writer.writeId(null); // EnclosingMethod
}
}
}
}