package net.sourceforge.javautil.bytecode.impl.asm;
import java.util.Map;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import net.sourceforge.javautil.bytecode.BytecodeException;
import net.sourceforge.javautil.bytecode.BytecodeCompiler.Version;
import net.sourceforge.javautil.bytecode.api.IBytecodeAnnotation;
import net.sourceforge.javautil.bytecode.api.IBytecodeField;
import net.sourceforge.javautil.bytecode.api.BytecodeFieldDeclared;
import net.sourceforge.javautil.bytecode.api.IBytecodeResolvable;
import net.sourceforge.javautil.bytecode.api.MethodDescriptor;
import net.sourceforge.javautil.bytecode.api.TypeMemberAccess;
import net.sourceforge.javautil.bytecode.api.IBytecodeAnnotation.AnnotationValue;
import net.sourceforge.javautil.bytecode.api.event.BytecodeEvent;
import net.sourceforge.javautil.bytecode.api.type.AbstractType;
import net.sourceforge.javautil.bytecode.api.type.BytecodeAnnotationDeclaration;
import net.sourceforge.javautil.bytecode.api.type.IBytecodeWriterType;
import net.sourceforge.javautil.bytecode.api.type.method.IBytecodeMethod;
import net.sourceforge.javautil.bytecode.api.type.method.BytecodeMethodConcrete;
/**
* The standard ASM implementation for writing
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class BytecodeWriterTypeASM implements IBytecodeWriterType<BytecodeContextTypeASM>, Opcodes {
protected final BytecodeWriterMethodASM writer = new BytecodeWriterMethodASM();
public void begin(BytecodeContextTypeASM context) {
IBytecodeResolvable[] ifaces = context.getEnclosingType().getInterfaces();
String[] interfaces = new String[ifaces.length];
for (int i=0; i<ifaces.length; i++) {
interfaces[i] = ifaces[i].getType().getName();
}
IBytecodeResolvable st = context.getEnclosingType().getSuperType() == null ?
context.getResolutionPool().resolve(Object.class.getName()) : context.getEnclosingType().getSuperType();
context.getClassWriter().visit(this.getVersion(context.getVersion()), getAccess(context.getEnclosingType()),
context.getEnclosingType().getType().getName(), null,
st.getType().getName(),
interfaces
);
for (IBytecodeAnnotation annotation : context.getEnclosingType().getDeclaredAnnotations()) {
this.declareAnnotation(context, annotation, context.getClassWriter().visitAnnotation(
annotation.getType().getType().toDescriptorString(), true
));
}
context.getClassWriter().visitSource(null, null);
}
public void writeMethod(BytecodeContextTypeASM context, IBytecodeMethod method) {
MethodDescriptor md = method.getDescriptor();
String[] exceptions = new String[md.getExceptionTypes() == null ? 0 : md.getExceptionTypes().length];
for (int i=0; i<exceptions.length; i++) {
exceptions[i] = md.getExceptionTypes()[i].getName();
}
int access = getAccess(method);
String desc = md.toDescriptorString();
MethodVisitor mv = context.getClassWriter().visitMethod(access, method.getName(), desc, null, exceptions);
for (IBytecodeAnnotation ba : method.getDeclaredAnnotations()) {
this.declareAnnotation(context, ba, mv.visitAnnotation(ba.getType().getType().toDescriptorString(), true));
}
if (method instanceof BytecodeMethodConcrete) {
BytecodeContextMethodASM ctx = new BytecodeContextMethodASM(mv, method, context, writer);
((BytecodeMethodConcrete) method).write(ctx);
} else {
mv.visitEnd();
}
}
public void declareField(BytecodeContextTypeASM context, String name, IBytecodeField field) {
FieldVisitor fv = context.getClassWriter().visitField(getAccess(field.getAccess()), name, field.getType().toDescriptorString(), null, null);
for (IBytecodeAnnotation ba : field.getDeclaredAnnotations()) {
this.declareAnnotation(context, ba, fv.visitAnnotation(ba.getType().getType().toDescriptorString(), true));
}
fv.visitEnd();
}
public void end(BytecodeContextTypeASM context) {
context.getClassWriter().visitEnd();
}
public void handle(BytecodeEvent evt) {
}
/**
* @param context The context in which to declare the annotation
* @param ba The annotation declaration
* @param av The annotation visitor
*/
protected void declareAnnotation (BytecodeContextTypeASM context, IBytecodeAnnotation ba, AnnotationVisitor av) {
IBytecodeResolvable enumType = context.getResolutionPool().resolve(Enum.class.getName());
IBytecodeResolvable annoType = context.getResolutionPool().resolve(Enum.class.getName());
Map<String, AnnotationValue> values = ba.getValues();
for (String name : values.keySet()) {
AnnotationValue value = values.get(name);
IBytecodeResolvable avt = context.getResolutionPool().resolve( value.getValue().getClass().getName() );
if (avt.isInstanceof(context.getResolutionPool(), enumType)) {
// TODO: support enums
} else if (avt.getType().isArray() &&
context.getResolutionPool().resolve(
avt.getType().getComponentType().getClassName()
).isInstanceof(context.getResolutionPool(), annoType)) {
// TODO: support annotation arrays
} else {
av.visit(name, value.getValue());
}
}
av.visitEnd();
}
/**
* @param version The framework version
* @return The ASM equivalent
*/
protected int getVersion (Version version) {
switch (version) {
case Version1_1: return V1_1;
case Version1_2: return V1_2;
case Version1_3: return V1_3;
case Version1_4: return V1_4;
case Version1_5: return V1_5;
case Version6: return V1_6;
case Version7: return V1_7;
}
throw new BytecodeException("Version of bytecode/class file not supported: " + version);
}
/**
* @param type The type to get access information for
* @return The access setting for the type (includes {@link #getAccess(TypeMemberAccess)})
*/
protected int getAccess (AbstractType type) {
int access = 0;
switch (type.getClassType()) {
case Class: access += Opcodes.ACC_SUPER; break;
case Enum: access += Opcodes.ACC_ENUM; break;
case Annotation: access += Opcodes.ACC_ANNOTATION;
case Interface: access += Opcodes.ACC_INTERFACE; break;
}
return access + getAccess(type.getAccess());
}
/**
* @param tma The framework access settings
* @return The ASM translated access setting for the TMA
*/
protected int getAccess (TypeMemberAccess tma) {
int access = 0;
switch (tma.getScope()) {
case Private: access += Opcodes.ACC_PRIVATE; break;
case Protected: access += Opcodes.ACC_PROTECTED; break;
case Public: access += Opcodes.ACC_PUBLIC;
}
if (tma.isAbstract()) access += Opcodes.ACC_ABSTRACT;
if (tma.isFinal()) access += Opcodes.ACC_FINAL;
if (tma.isStatic()) access += Opcodes.ACC_STATIC;
return access;
}
/**
* @param method The method for which to get access
* @return The access setting for the specified method
*/
protected int getAccess (IBytecodeMethod method) {
int access = 0;
if (method.getDescriptor().isVarArgs()) access += Opcodes.ACC_VARARGS;
return access + getAccess(method.getAccess());
}
}