/************************************************************************************** * 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.ClassAdapter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.Type; import org.objectweb.asm.Label; import org.codehaus.aspectwerkz.definition.SystemDefinition; import org.codehaus.aspectwerkz.expression.ExpressionContext; import org.codehaus.aspectwerkz.expression.PointcutType; import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType; import org.codehaus.aspectwerkz.reflect.ClassInfo; import org.codehaus.aspectwerkz.reflect.FieldInfo; import org.codehaus.aspectwerkz.reflect.MemberInfo; import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo; import org.codehaus.aspectwerkz.transform.Context; import org.codehaus.aspectwerkz.transform.TransformationUtil; import org.codehaus.aspectwerkz.transform.TransformationConstants; import org.codehaus.aspectwerkz.transform.inlining.compiler.AbstractJoinPointCompiler; import org.codehaus.aspectwerkz.transform.inlining.ContextImpl; import org.codehaus.aspectwerkz.transform.inlining.AsmHelper; import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint; import java.lang.reflect.Modifier; import java.util.Iterator; import java.util.Set; /** * Instruments method SET and GET join points by replacing PUTFIELD and GETFIELD instructions with invocations * of the compiled join point. * * @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a> */ public class FieldSetFieldGetVisitor extends ClassAdapter implements TransformationConstants { private final ContextImpl m_ctx; private final ClassLoader m_loader; private final ClassInfo m_callerClassInfo; private Label m_lastLabelForLineNumber = EmittedJoinPoint.NO_LINE_NUMBER; /** * Creates a new instance. * * @param cv * @param loader * @param classInfo * @param ctx */ public FieldSetFieldGetVisitor(final ClassVisitor cv, final ClassLoader loader, final ClassInfo classInfo, final Context ctx) { super(cv); m_loader = loader; m_callerClassInfo = classInfo; m_ctx = (ContextImpl) ctx; } /** * Visits the caller methods. * * @param access * @param name * @param desc * @param signature * @param exceptions * @return */ public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) { if (name.startsWith(WRAPPER_METHOD_PREFIX)) { return super.visitMethod(access, name, desc, signature,exceptions); } MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions); return mv == null ? null : new ReplacePutFieldAndGetFieldInstructionCodeAdapter( mv, m_loader, m_callerClassInfo, m_ctx.getClassName(), name, desc ); } /** * Replaces PUTFIELD and GETFIELD instructions with a call to the compiled JoinPoint instance. * * @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a> * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> */ public class ReplacePutFieldAndGetFieldInstructionCodeAdapter extends AfterObjectInitializationCodeAdapter { private final ClassLoader m_loader; private final ClassInfo m_callerClassInfo; private final String m_callerClassName; private final String m_callerMethodName; private final String m_callerMethodDesc; private final MemberInfo m_callerMemberInfo; /** * Creates a new instance. * * @param ca * @param loader * @param callerClassInfo * @param callerClassName * @param callerMethodName * @param callerMethodDesc */ public ReplacePutFieldAndGetFieldInstructionCodeAdapter(final MethodVisitor ca, final ClassLoader loader, final ClassInfo callerClassInfo, final String callerClassName, final String callerMethodName, final String callerMethodDesc) { super(ca, callerMethodName); m_loader = loader; m_callerClassInfo = callerClassInfo; m_callerClassName = callerClassName; m_callerMethodName = callerMethodName; m_callerMethodDesc = callerMethodDesc; if (CLINIT_METHOD_NAME.equals(m_callerMethodName)) { m_callerMemberInfo = m_callerClassInfo.staticInitializer(); } else if (INIT_METHOD_NAME.equals(m_callerMethodName)) { int hash = AsmHelper.calculateConstructorHash(m_callerMethodDesc); m_callerMemberInfo = m_callerClassInfo.getConstructor(hash); } else { int hash = AsmHelper.calculateMethodHash(m_callerMethodName, m_callerMethodDesc); m_callerMemberInfo = m_callerClassInfo.getMethod(hash); } if (m_callerMemberInfo == null) { System.err.println( "AW::WARNING " + "metadata structure could not be build for method [" + m_callerClassInfo.getName().replace('/', '.') + '.' + m_callerMethodName + ':' + m_callerMethodDesc + ']' ); } } /** * Label * * @param label */ public void visitLabel(Label label) { m_lastLabelForLineNumber = label; super.visitLabel(label); } /** * Visits PUTFIELD and GETFIELD instructions. * * @param opcode * @param className * @param fieldName * @param fieldDesc */ public void visitFieldInsn(final int opcode, final String className, final String fieldName, final String fieldDesc) { if (className.endsWith(AbstractJoinPointCompiler.JOIN_POINT_CLASS_SUFFIX) || fieldName.startsWith(ASPECTWERKZ_PREFIX) || fieldName.startsWith(SYNTHETIC_MEMBER_PREFIX) || // synthetic field fieldName.equals(SERIAL_VERSION_UID_FIELD_NAME) // can have been added by the weaver (not safe) ) { super.visitFieldInsn(opcode, className, fieldName, fieldDesc); return; } // if within ctor, make sure object initialization has been reached if (!m_isObjectInitialized) { super.visitFieldInsn(opcode, className, fieldName, fieldDesc); return; } final Type fieldType = Type.getType(fieldDesc); final int joinPointHash = AsmHelper.calculateFieldHash(fieldName, fieldDesc); final ClassInfo classInfo = AsmClassInfo.getClassInfo(className, m_loader); final FieldInfo fieldInfo = getFieldInfo(classInfo, className, fieldName, fieldDesc, joinPointHash); if (opcode == PUTFIELD || opcode == PUTSTATIC) { handleFieldModification(fieldInfo, opcode, className, fieldName, fieldDesc, joinPointHash); } else if (opcode == GETFIELD || opcode == GETSTATIC) { handleFieldAccess(fieldInfo, opcode, className, fieldName, fieldDesc, joinPointHash, fieldType); } else { super.visitFieldInsn(opcode, className, fieldName, fieldDesc); } } /** * Handles field access. * * @param fieldInfo * @param opcode * @param className * @param fieldName * @param fieldDesc * @param joinPointHash * @param fieldType */ private void handleFieldAccess(final FieldInfo fieldInfo, final int opcode, final String className, final String fieldName, final String fieldDesc, int joinPointHash, final Type fieldType) { if (m_callerMemberInfo == null) { super.visitFieldInsn(opcode, className, fieldName, fieldDesc); return; } ExpressionContext ctx = new ExpressionContext(PointcutType.GET, fieldInfo, m_callerMemberInfo); if (fieldFilter(m_ctx.getDefinitions(), ctx, fieldInfo)) { super.visitFieldInsn(opcode, className, fieldName, fieldDesc); } else { m_ctx.markAsAdvised(); String joinPointClassName = TransformationUtil.getJoinPointClassName( m_callerClassName, m_callerMethodName, m_callerMethodDesc, className, JoinPointType.FIELD_GET_INT, joinPointHash ); // no param to field, so pass a default value to the invoke method AsmHelper.addDefaultValue(this, fieldType); // if static context load NULL else 'this' if (Modifier.isStatic(m_callerMemberInfo.getModifiers())) { visitInsn(ACONST_NULL); } else { visitVarInsn(ALOAD, 0); } // add the call to the join point super.visitMethodInsn( INVOKESTATIC, joinPointClassName, INVOKE_METHOD_NAME, TransformationUtil.getInvokeSignatureForFieldJoinPoints( fieldInfo.getModifiers(), fieldDesc, m_callerClassName, className ) ); // emit the joinpoint m_ctx.addEmittedJoinPoint( new EmittedJoinPoint( JoinPointType.FIELD_GET_INT, m_callerClassName, m_callerMethodName, m_callerMethodDesc, m_callerMemberInfo.getModifiers(), className, fieldName, fieldDesc, fieldInfo.getModifiers(), joinPointHash, joinPointClassName, m_lastLabelForLineNumber ) ); } } /** * Handles field modification. * * @param fieldInfo * @param opcode * @param className * @param fieldName * @param fieldDesc * @param joinPointHash */ private void handleFieldModification(final FieldInfo fieldInfo, final int opcode, final String className, final String fieldName, final String fieldDesc, final int joinPointHash) { if (m_callerMemberInfo == null) { super.visitFieldInsn(opcode, className, fieldName, fieldDesc); return; } ExpressionContext ctx = new ExpressionContext(PointcutType.SET, fieldInfo, m_callerMemberInfo); if (fieldFilter(m_ctx.getDefinitions(), ctx, fieldInfo)) { super.visitFieldInsn(opcode, className, fieldName, fieldDesc); } else { m_ctx.markAsAdvised(); String joinPointClassName = TransformationUtil.getJoinPointClassName( m_callerClassName, m_callerMethodName, m_callerMethodDesc, className, JoinPointType.FIELD_SET_INT, joinPointHash ); // load the caller instance (this), or null if in a static context // note that callee instance [optional] and args are already on the stack if (Modifier.isStatic(m_callerMemberInfo.getModifiers())) { visitInsn(ACONST_NULL); } else { visitVarInsn(ALOAD, 0); } // add the call to the join point super.visitMethodInsn( INVOKESTATIC, joinPointClassName, INVOKE_METHOD_NAME, TransformationUtil.getInvokeSignatureForFieldJoinPoints( fieldInfo.getModifiers(), fieldDesc, m_callerClassName, className ) ); final int sort = Type.getType(fieldDesc).getSort(); if (sort != Type.LONG && sort != Type.DOUBLE) { super.visitInsn(POP); } else { //AW-437 super.visitInsn(POP2); } // emit the joinpoint m_ctx.addEmittedJoinPoint( new EmittedJoinPoint( JoinPointType.FIELD_SET_INT, m_callerClassName, m_callerMethodName, m_callerMethodDesc, m_callerMemberInfo.getModifiers(), className, fieldName, fieldDesc, fieldInfo.getModifiers(), joinPointHash, joinPointClassName, m_lastLabelForLineNumber ) ); } } /** * Returns the field info. * * @param classInfo * @param className * @param fieldName * @param fieldDesc * @param joinPointHash * @return */ private FieldInfo getFieldInfo(final ClassInfo classInfo, final String className, final String fieldName, final String fieldDesc, final int joinPointHash) { FieldInfo fieldInfo = classInfo.getField(joinPointHash); if (fieldInfo == null) { throw new Error( "field info metadata structure could not be build for field: " + className + '.' + fieldName + ':' + fieldDesc ); } return fieldInfo; } /** * Filters out the fields that are not eligible for transformation. * * @param definitions * @param ctx * @param fieldInfo * @return boolean true if the field should be filtered out */ public boolean fieldFilter(final Set definitions, final ExpressionContext ctx, final FieldInfo fieldInfo) { if (fieldInfo.getName().startsWith(ORIGINAL_METHOD_PREFIX)) { return true; } for (Iterator it = definitions.iterator(); it.hasNext();) { if (((SystemDefinition) it.next()).hasPointcut(ctx)) { return false; } else { continue; } } return true; } } }