| /* |
| * 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 |
| } |
| } |
| } |
| |
| } |