/* Software Name : AsmDex
* Version : 1.0
*
* Copyright © 2012 France Télécom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.ow2.asmdex;
import java.util.List;
import org.ow2.asmdex.encodedValue.EncodedValue;
import org.ow2.asmdex.encodedValue.EncodedValueAnnotation;
import org.ow2.asmdex.encodedValue.EncodedValueArray;
import org.ow2.asmdex.encodedValue.EncodedValueFactory;
import org.ow2.asmdex.encodedValue.EncodedValueType;
import org.ow2.asmdex.structureWriter.AnnotationElement;
import org.ow2.asmdex.structureWriter.AnnotationItem;
import org.ow2.asmdex.structureWriter.ClassDefinitionItem;
import org.ow2.asmdex.structureWriter.ConstantPool;
import org.ow2.asmdex.structureWriter.Field;
import org.ow2.asmdex.structureWriter.Method;
/**
* Class that takes care of visiting a Class for the Application Writer to write it.
*
* @author Julien Névo
*/
public class ClassWriter extends ClassVisitor {
/**
* The Application Writer of this Class Writer. It is only useful for the optimization that consists
* in copying the Constant Pool and skips the visit of Methods that aren't transformed by any Adapter.
*/
private ApplicationWriter applicationWriter;
/**
* The Constant Pool of the Application.
*/
private ConstantPool constantPool;
/**
* Contains all the information of the Class.
*/
private ClassDefinitionItem classDefinitionItem;
/**
* Stores the Method this is linked to, if any, which means the current Class is defined in this Method
* body. This Method is set by visitOuterClass and is never reset after as, according to the documentation,
* a Class may have either of an EnclosingClass or EnclosingMethod annotation.
*/
private Method enclosingMethod = null;
/**
* Constructor.
* @param applicationWriter the Application Writer.
* @param constantPool the constantPool of the Application.
* @param access the access flags of the class.
* @param name the name of the class.
* @param signature the signature of the class. Used only for generics. May be null.
* @param superName the name of the super class.
* @param interfaces the name of the interfaces of the class.
*/
public ClassWriter(ApplicationWriter applicationWriter, ConstantPool constantPool, int access,
String name, String[] signature, String superName, String[] interfaces) {
super(Opcodes.ASM4);
this.applicationWriter = applicationWriter;
this.constantPool = constantPool;
classDefinitionItem = constantPool.addClassToConstantPool(name, superName, access,
interfaces, signature);
}
// -----------------------------------------------------
// Getters and Setters.
// -----------------------------------------------------
/**
* Returns the ApplicationWriter of this Class Writer.
*/
public ApplicationWriter getApplicationWriter() {
return applicationWriter;
}
/**
* Returns the name of the Class.
* @return the name of the Class.
*/
public String getName() {
return classDefinitionItem.getClassName();
}
/**
* Returns the current class definition item.
* @return the current class definition item.
*/
public ClassDefinitionItem getClassDefinitionItem() {
return classDefinitionItem;
}
/**
* Returns the Constant Pool related to this Application.
* @return the Constant Pool related to this Application.
*/
public ConstantPool getConstantPool() {
return constantPool;
}
// -----------------------------------------------------
// Visitor methods.
// -----------------------------------------------------
@Override
public void visit(int version, int access, String name, String[] signature,
String superName, String[] interfaces) {
if (ApplicationWriter.DISPLAY_WRITER_INFORMATION) {
System.out.print(" ClassWriter : visit. Name = " + name
+ ", SuperClassName = " + superName
);
if (interfaces != null) {
System.out.print(". Interfaces = ");
for (int i = 0; i < interfaces.length; i++) {
System.out.print(interfaces[i] + ", ");
}
}
if (signature != null) {
System.out.print(". Signature = ");
for (int i = 0; i < signature.length; i++) {
System.out.print(signature[i] + ", ");
}
}
System.out.println();
}
// Creates the Signature Annotation if it exists.
// Note that we use the signature that was *already* set by the ApplicationWriter, not the one
// that is just given, even though they should be the same.
String[] savedSignature = classDefinitionItem.getSignature();
if (savedSignature != null) {
int size = savedSignature.length;
if (size > 0) {
AnnotationVisitor av = visitAnnotation(Constants.SIGNATURE_ANNOTATION_INTERNAL_NAME, false);
AnnotationVisitor subAv = av.visitArray(Constants.VALUE_STRING);
for (String string : savedSignature) {
subAv.visit(null, string);
}
subAv.visitEnd();
av.visitEnd();
}
}
}
@Override
public void visitSource(String source, String debug) {
if (ApplicationWriter.DISPLAY_WRITER_INFORMATION) {
System.out.println(" ClassWriter : visitSource. Source = " + source);
}
classDefinitionItem.setSourceFileName(source);
constantPool.addStringToConstantPool(source);
}
@Override
public void visitEnd() {
if (ApplicationWriter.DISPLAY_WRITER_INFORMATION) {
System.out.println(" ClassWriter : visitEnd.");
}
// Checks if the MemberClass array is Null. If not, we create a dalvik.annotation.MemberClasses around it
// and adds it as an Class Annotation.
EncodedValueArray array = classDefinitionItem.getMemberClassArray();
if (array != null) {
String val = Constants.VALUE_STRING;
constantPool.addStringToConstantPool(val);
constantPool.addTypeToConstantPool(Constants.MEMBER_CLASSES_ANNOTATION_INTERNAL_NAME);
AnnotationItem annotationItem = new AnnotationItem(Opcodes.VISIBILITY_SYSTEM,
Constants.MEMBER_CLASSES_ANNOTATION_INTERNAL_NAME);
AnnotationElement annotationElement = new AnnotationElement(val, array);
annotationItem.addAnnotationElement(annotationElement);
classDefinitionItem.addAnnotationItem(annotationItem);
constantPool.addAnnotationItemToConstantPool(annotationItem);
}
// Checks if the Class contains Default Annotations. If yes, we have to create <i>ONE</i>
// AnnotationItem regrouping them.
List<AnnotationItem> defaultAnnotationItems = classDefinitionItem.getAnnotationItemsForDefaultAnnotation();
if ((defaultAnnotationItems != null) && (defaultAnnotationItems.size() > 0)) {
EncodedValueAnnotation createdEncodedValueAnnotation = null;
String encodedAnnotationType = null;
for (AnnotationItem annotationItems : defaultAnnotationItems) {
// There should only one AnnotationElement, but we make a loop for security.
for (AnnotationElement currentElement : annotationItems.getAnnotationElements()) {
EncodedValueAnnotation currentEncodedValue = (EncodedValueAnnotation)currentElement.getEncodedValue();
encodedAnnotationType = currentEncodedValue.getAnnotationType();
if (createdEncodedValueAnnotation == null) {
createdEncodedValueAnnotation = new EncodedValueAnnotation(encodedAnnotationType);
}
// There should be one AnnotationElement in the Annotation, but we make a loop for security.
for (AnnotationElement currentAnnotationElement : currentEncodedValue.getAnnotationElements()) {
createdEncodedValueAnnotation.addAnnotationElement(currentAnnotationElement);
}
}
}
// We set up the DefaultAnnotation we need to create.
AnnotationItem createdAnnotationItem = new AnnotationItem(Opcodes.VISIBILITY_SYSTEM,
Constants.ANNOTATION_DEFAULT_INTERNAL_NAME);
// It consists of only one EncodedAnnotation, which has one AnnotationElement, of type "value"
// and of one EncodedValue of VALUE_ANNOTATION type.
AnnotationElement createdAnnotationElement = new AnnotationElement(Constants.VALUE_STRING, createdEncodedValueAnnotation);
createdAnnotationItem.addAnnotationElement(createdAnnotationElement);
// Adds the newly created AnnotationItem to the Constant Pool and to the Class.
classDefinitionItem.addAnnotationItem(createdAnnotationItem);
constantPool.addAnnotationItemToConstantPool(createdAnnotationItem);
}
// Now that the annotation_set_item are fully known, we can register it to the Constant Pool.
constantPool.addAnnotationSetItemToConstantPool(classDefinitionItem.getAnnotationSetItem());
// We also build the annotation_directory_item.
classDefinitionItem.buildAnnotationDirectoryItem();
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String[] signature, String[] exceptions) {
if (ApplicationWriter.DISPLAY_WRITER_INFORMATION) {
System.out.print(" ClassWriter : visitMethod. Access = " + access +
", Name = " + name + ", Desc = " + desc);
if (signature != null) {
System.out.print(". Signature = ");
for (String sig : signature) {
System.out.print(sig +", ");
}
}
if (exceptions != null) {
System.out.print(". Exceptions = ");
for (String exception : exceptions) {
System.out.print(exception +", ");
}
}
System.out.println();
}
MethodWriter methodWriter = new MethodWriter(this, access, name, desc, signature, exceptions);
// Registers the method to the Class. It may be virtual or direct.
Method method = methodWriter.getMethod();
classDefinitionItem.addMethod(method);
// If the Method is using This (non static and non constructor), the String "this" is added to the
// Constant Pool.
if (method.isUsingThis()) {
constantPool.addStringToConstantPool(Constants.THIS_STRING);
}
// Manages the Signature Annotation if it exists.
String[] savedSignature = method.getSignature();
if (savedSignature != null) {
int size = savedSignature.length;
if (size > 0) {
AnnotationVisitor av = methodWriter.visitAnnotation(Constants.SIGNATURE_ANNOTATION_INTERNAL_NAME, false);
AnnotationVisitor subAv = av.visitArray(Constants.VALUE_STRING);
for (String string : savedSignature) {
subAv.visit(null, string);
}
subAv.visitEnd();
av.visitEnd();
}
}
// If there are exceptions, we need to create a dalvik.annotation.Throws annotation.
// It consists of an Array Annotation with the type of the Exceptions as elements.
if ((exceptions != null) && (exceptions.length > 0)) {
String valString = Constants.VALUE_STRING;
constantPool.addStringToConstantPool(valString);
constantPool.addTypeToConstantPool(Constants.EXCEPTION_ANNOTATION_INTERNAL_NAME);
EncodedValueArray array = new EncodedValueArray();
for (String exception : exceptions) {
constantPool.addTypeToConstantPool(exception);
EncodedValue value = new EncodedValueType(exception);
array.addEncodedValue(value);
}
AnnotationElement annotationElement = new AnnotationElement(valString, array);
AnnotationItem annotationItem = new AnnotationItem(Opcodes.VISIBILITY_SYSTEM,
Constants.EXCEPTION_ANNOTATION_INTERNAL_NAME);
annotationItem.addAnnotationElement(annotationElement);
method.addAnnotationItem(annotationItem);
constantPool.addAnnotationItemToConstantPool(annotationItem);
}
return methodWriter;
}
@Override
public FieldVisitor visitField(int access, String name, String desc,
String[] signature, Object value) {
if (ApplicationWriter.DISPLAY_WRITER_INFORMATION) {
System.out.print(" ClassWriter : visitField. Access = " + access +
", Name = " + name + ", Desc = " + desc + ", value = " + value
);
if (signature != null) {
System.out.print(". Signature = ");
for (String sig : signature) {
System.out.print(sig +", ");
}
}
System.out.println();
}
FieldWriter fieldWriter = new FieldWriter(constantPool, getName(), access, name, desc,
signature, value);
// Registers the Field to the Class. It may be a static or instance Field.
Field field = fieldWriter.getField();
classDefinitionItem.addField(field);
// Manages the Signature Annotation if it exists.
String[] savedSignature = field.getSignature();
if (savedSignature != null) {
int size = savedSignature.length;
if (size > 0) {
AnnotationVisitor av = fieldWriter.visitAnnotation(Constants.SIGNATURE_ANNOTATION_INTERNAL_NAME, false);
AnnotationVisitor subAv = av.visitArray(Constants.VALUE_STRING);
for (String string : savedSignature) {
subAv.visit(null, string);
}
subAv.visitEnd();
av.visitEnd();
}
}
return fieldWriter;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (ApplicationWriter.DISPLAY_WRITER_INFORMATION) {
System.out.println(" ClassWriter : visitAnnotation. Desc = " + desc +
", visible = " + visible
);
}
// Creates an AnnotationItem, incomplete for now (no Elements), and registers it to the Class.
// We do not add it to the Constant Pool now because of this incompleteness.
AnnotationWriter annotationWriter = AnnotationWriter.createAnnotationWriter(desc, visible, constantPool, null);
classDefinitionItem.addAnnotationItem(annotationWriter.getAnnotationItem());
return annotationWriter;
}
@Override
public void visitInnerClass(String name, String outerName,
String innerName, int access) {
if (ApplicationWriter.DISPLAY_WRITER_INFORMATION) {
System.out.println(" ClassWriter : visitInnerClass. Name = " + name
+ ", outerName = " + outerName
+ ", innerName = " + innerName
+ ", access = " + access
);
}
// Register all the given data to the ConstantPool. If the name is Null, the OuterName
// is not a Type, but just a String.
if (name == null) {
constantPool.addStringToConstantPool(outerName);
} else {
constantPool.addTypeToConstantPool(name);
constantPool.addTypeToConstantPool(outerName);
constantPool.addStringToConstantPool(innerName);
}
// We're inside an Inner Class, visiting it.
// Two annotations need to be created. The Enclosing Class/Method, and the Inner Class.
// If the enclosingMethod reference is null, we create an Enclosing Class, else an Enclosing
// Method.
// Encodes the first annotation : Enclosing Class or Enclosing Method.
boolean isEnclosingClass = (enclosingMethod == null);
String annotationType = (isEnclosingClass ? Constants.ENCLOSING_CLASS_ANNOTATION_INTERNAL_NAME :
Constants.ENCLOSING_METHOD_ANNOTATION_INTERNAL_NAME);
String val = Constants.VALUE_STRING;
constantPool.addStringToConstantPool(val);
constantPool.addTypeToConstantPool(annotationType);
AnnotationItem annotationItem = new AnnotationItem(Opcodes.VISIBILITY_SYSTEM, annotationType);
EncodedValue encodedValue;
if (isEnclosingClass) {
// Enclosing Class.
encodedValue = EncodedValueFactory.getEncodedValue(outerName, Opcodes.VALUE_TYPE);
} else {
// Enclosing Method.
encodedValue = EncodedValueFactory.getEncodedValue(enclosingMethod, Opcodes.VALUE_METHOD);
}
AnnotationElement annotationElement = new AnnotationElement(val, encodedValue);
annotationItem.addAnnotationElement(annotationElement);
constantPool.addAnnotationItemToConstantPool(annotationItem);
classDefinitionItem.addAnnotationItem(annotationItem);
// Encodes the second annotation : the Inner Class.
annotationItem = new AnnotationItem(Opcodes.VISIBILITY_SYSTEM,
Constants.INNER_CLASS_ANNOTATION_INTERNAL_NAME);
// Encodes the "name".
val = Constants.NAME_STRING;
constantPool.addStringToConstantPool(val);
constantPool.addTypeToConstantPool(Constants.INNER_CLASS_ANNOTATION_INTERNAL_NAME);
// According to the documentation, if the class is anonymous, the name becomes a Null value.
if (innerName == null) { // DOUBLE CHECK IF NAME OR INNER NAME.
encodedValue = EncodedValueFactory.getEncodedNullValue();
} else {
encodedValue = EncodedValueFactory.getEncodedValue(innerName, Opcodes.VALUE_STRING);
}
annotationElement = new AnnotationElement(val, encodedValue);
annotationItem.addAnnotationElement(annotationElement);
// Encodes the "accessFlags".
val = Constants.ACCESS_FLAGS_STRING;
constantPool.addStringToConstantPool(val);
encodedValue = EncodedValueFactory.getEncodedValue(access, Opcodes.VALUE_INT);
annotationElement = new AnnotationElement(val, encodedValue);
annotationItem.addAnnotationElement(annotationElement);
constantPool.addAnnotationItemToConstantPool(annotationItem);
classDefinitionItem.addAnnotationItem(annotationItem);
}
@Override
public void visitMemberClass(String name, String outerName,
String innerName) {
if (name != null) {
constantPool.addTypeToConstantPool(name);
}
classDefinitionItem.addMemberClassValue(name, constantPool);
}
@Override
public void visitOuterClass(String owner, String name, String desc) {
if (ApplicationWriter.DISPLAY_WRITER_INFORMATION) {
System.out.println(" ClassWriter : visitOuterClass. Name = " + name
+ ", owner = " + owner
+ ", desc = " + desc
);
}
// We found a reference to a Method. This Method could have been found before, or
// not, in which case Constant Pool adds it.
// We store this Method, which means that the next visitInnerClass will take it in account, generating
// a Enclosing Method annotation instead of a Enclosing Class annotation.
enclosingMethod = constantPool.addMethodToConstantPool(name, owner, desc, Opcodes.ACC_UNKNOWN, null, null);
}
@Override
public void visitAttribute(Object attr) {
// This method is ignored, as Attributes are not supported by AsmDex.
}
}