/**************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the LGPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.transform.inlining.weaver;
import org.objectweb.asm.*;
import org.codehaus.aspectwerkz.transform.Context;
import org.codehaus.aspectwerkz.transform.TransformationConstants;
import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
import java.util.Iterator;
/**
* A ClassAdapter that take care of all weaved class and add the glue between the class and its JIT dependencies.
* <p/>
* Adds a 'private static final Class aw$clazz' field a 'private static void ___AW_$_AW_$initJoinPoints()' method
* and patches the 'clinit' method.
* <p/>
* If the class has been made advisable, we also add a ___AW_$_AW_$emittedJoinPoints fields that gets populated.
*
*
* @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>
* TODO: for multi weaving, we could go on in adding several AW initJoinPoints_xxWeaveCount method, but then cannot be
* done with RW
*/
public class JoinPointInitVisitor extends ClassAdapter implements TransformationConstants {
private final ContextImpl m_ctx;
private boolean m_hasClinitMethod = false;
private boolean m_hasInitJoinPointsMethod = false;
private boolean m_hasClassField = false;
private boolean m_hasEmittedJoinPointsField = false;
/**
* Creates a new instance.
*
* @param cv
* @param ctx
*/
public JoinPointInitVisitor(final ClassVisitor cv, final Context ctx) {
super(cv);
m_ctx = (ContextImpl) ctx;
}
/**
* Visits the methods. If the AW joinPointsInit method is found, remember that, it means we are in a multi-weaving
* scheme. Patch the 'clinit' method if already present.
*
* TODO: multi-weaving will lead to several invocation of AW initJoinPoints and several assigment of __AW_Clazz in the patched clinit which slows down a bit the load time
* @see org.objectweb.asm.ClassVisitor#visitMethod(int, String, String, String, String[])
*/
public MethodVisitor visitMethod(final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
if (CLINIT_METHOD_NAME.equals(name)) {
m_hasClinitMethod = true;
// at the beginning of the existing <clinit> method
// ___AWClazz = Class.forName("TargetClassName");
// ___AW_$_AW_$initJoinPoints();
MethodVisitor ca = new InsertBeforeClinitCodeAdapter(cv.visitMethod(access, name, desc, signature, exceptions));
ca.visitMaxs(0, 0);
return ca;
} else if (INIT_JOIN_POINTS_METHOD_NAME.equals(name)) {
m_hasInitJoinPointsMethod = true;
// add the gathered JIT dependencies for multi-weaving support
MethodVisitor ca = new InsertBeforeInitJoinPointsCodeAdapter(
cv.visitMethod(access, name, desc, signature, exceptions)
);
ca.visitMaxs(0, 0);
return ca;
} else {
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
/**
* Remember if we have already the static class field for multi-weaving scheme.
*
* @param access
* @param name
* @param desc
* @param signature
* @param value
*/
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
if (TARGET_CLASS_FIELD_NAME.equals(name)) {
m_hasClassField = true;
} else if (EMITTED_JOINPOINTS_FIELD_NAME.equals(name)) {
m_hasEmittedJoinPointsField = true;
}
return super.visitField(access, name, desc, signature, value);
}
/**
* Finalize the visit. Add static class field if needed, add initJoinPoints method if needed, add <clinit>if
* needed.
*/
public void visitEnd() {
if (!m_ctx.isAdvised()) {
super.visitEnd();
return;
}
if (!m_hasClassField) {
// create field
// private final static Class aw$clazz = Class.forName("TargetClassName");
cv.visitField(
ACC_PRIVATE + ACC_FINAL + ACC_STATIC + ACC_SYNTHETIC,
TARGET_CLASS_FIELD_NAME,
CLASS_CLASS_SIGNATURE,
null,
null
);
}
if (!m_hasEmittedJoinPointsField && m_ctx.isMadeAdvisable()) {
// create field
// private final static Class aw$emittedJoinPoints that will host a Trove int Object map
cv.visitField(
ACC_PRIVATE + ACC_FINAL + ACC_STATIC + ACC_SYNTHETIC,
EMITTED_JOINPOINTS_FIELD_NAME,
"Lgnu/trove/TIntObjectHashMap;",
null,
null
);
}
if (!m_hasClinitMethod) {
MethodVisitor ca = new InsertBeforeClinitCodeAdapter(
cv.visitMethod(
ACC_STATIC,
CLINIT_METHOD_NAME,
NO_PARAM_RETURN_VOID_SIGNATURE,
null,
null
)
);
ca.visitInsn(RETURN);
ca.visitMaxs(0, 0);
}
if (!m_hasInitJoinPointsMethod) {
MethodVisitor mv = new InsertBeforeInitJoinPointsCodeAdapter(
cv.visitMethod(
ACC_PRIVATE + ACC_FINAL + ACC_STATIC + ACC_SYNTHETIC,
INIT_JOIN_POINTS_METHOD_NAME,
NO_PARAM_RETURN_VOID_SIGNATURE,
null,
null
)
);
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
}
cv.visitEnd();
}
/**
* Handles the method body of the <clinit>method.
*
* @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
*/
public class InsertBeforeClinitCodeAdapter extends MethodAdapter {
public InsertBeforeClinitCodeAdapter(MethodVisitor ca) {
super(ca);
if (!m_hasClassField) {
mv.visitLdcInsn(m_ctx.getClassName().replace('/', '.'));
mv.visitMethodInsn(INVOKESTATIC, CLASS_CLASS, FOR_NAME_METHOD_NAME, FOR_NAME_METHOD_SIGNATURE);
mv.visitFieldInsn(PUTSTATIC, m_ctx.getClassName(), TARGET_CLASS_FIELD_NAME, CLASS_CLASS_SIGNATURE);
}
if (!m_hasEmittedJoinPointsField && m_ctx.isMadeAdvisable()) {
// aw$emittedJoinPoints = new TIntObjectHashMap()
mv.visitTypeInsn(NEW, "gnu/trove/TIntObjectHashMap");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "gnu/trove/TIntObjectHashMap", "<init>", "()V");
mv.visitFieldInsn(PUTSTATIC, m_ctx.getClassName(), EMITTED_JOINPOINTS_FIELD_NAME, "Lgnu/trove/TIntObjectHashMap;");
}
if (!m_hasClassField) {
mv.visitMethodInsn(
INVOKESTATIC,
m_ctx.getClassName(),
INIT_JOIN_POINTS_METHOD_NAME,
NO_PARAM_RETURN_VOID_SIGNATURE
);
}
}
}
/**
* Handles the method body of the AW initJoinPoints method.
*
* @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>
*/
public class InsertBeforeInitJoinPointsCodeAdapter extends MethodAdapter {
public InsertBeforeInitJoinPointsCodeAdapter(MethodVisitor ca) {
super(ca);
// loop over emitted jp and insert call to "JoinPointManager.loadJoinPoint(...)"
// add calls to aw$emittedJoinPoints.put(.. new EmittedJoinPoint) if needed.
for (Iterator iterator = m_ctx.getEmittedJoinPoints().iterator(); iterator.hasNext();) {
EmittedJoinPoint jp = (EmittedJoinPoint) iterator.next();
mv.visitLdcInsn(new Integer(jp.getJoinPointType()));
mv.visitFieldInsn(GETSTATIC, m_ctx.getClassName(), TARGET_CLASS_FIELD_NAME, CLASS_CLASS_SIGNATURE);
mv.visitLdcInsn(jp.getCallerMethodName());
mv.visitLdcInsn(jp.getCallerMethodDesc());
mv.visitLdcInsn(new Integer(jp.getCallerMethodModifiers()));
mv.visitLdcInsn(jp.getCalleeClassName());
mv.visitLdcInsn(jp.getCalleeMemberName());
mv.visitLdcInsn(jp.getCalleeMemberDesc());
mv.visitLdcInsn(new Integer(jp.getCalleeMemberModifiers()));
mv.visitLdcInsn(new Integer(jp.getJoinPointHash()));
mv.visitLdcInsn(jp.getJoinPointClassName());
mv.visitMethodInsn(
INVOKESTATIC,
JOIN_POINT_MANAGER_CLASS_NAME,
LOAD_JOIN_POINT_METHOD_NAME,
LOAD_JOIN_POINT_METHOD_SIGNATURE
);
if (m_ctx.isMadeAdvisable()) {
// trove map
mv.visitFieldInsn(GETSTATIC, m_ctx.getClassName(), EMITTED_JOINPOINTS_FIELD_NAME, "Lgnu/trove/TIntObjectHashMap;");
// trove map key
mv.visitLdcInsn(new Integer(jp.getJoinPointClassName().hashCode()));
mv.visitTypeInsn(NEW, "org/codehaus/aspectwerkz/transform/inlining/EmittedJoinPoint");
mv.visitInsn(DUP);
mv.visitLdcInsn(new Integer(jp.getJoinPointType()));
mv.visitLdcInsn(m_ctx.getClassName());
mv.visitLdcInsn(jp.getCallerMethodName());
mv.visitLdcInsn(jp.getCallerMethodDesc());
mv.visitLdcInsn(new Integer(jp.getCallerMethodModifiers()));
mv.visitLdcInsn(jp.getCalleeClassName());
mv.visitLdcInsn(jp.getCalleeMemberName());
mv.visitLdcInsn(jp.getCalleeMemberDesc());
mv.visitLdcInsn(new Integer(jp.getCalleeMemberModifiers()));
mv.visitLdcInsn(new Integer(jp.getJoinPointHash()));
mv.visitLdcInsn(jp.getJoinPointClassName());
mv.visitMethodInsn(INVOKESPECIAL, "org/codehaus/aspectwerkz/transform/inlining/EmittedJoinPoint", "<init>",
"(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IILjava/lang/String;)V"
);
mv.visitMethodInsn(
INVOKEVIRTUAL,
"gnu/trove/TIntObjectHashMap",
"put",
"(ILjava/lang/Object;)Ljava/lang/Object;"
);
}
}
}
}
}