/************************************************************************************** * 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.compiler; import java.util.Map; import java.util.Iterator; import java.util.WeakHashMap; import java.util.HashSet; import java.util.Set; import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType; import org.codehaus.aspectwerkz.expression.ExpressionContext; import org.codehaus.aspectwerkz.expression.PointcutType; import org.codehaus.aspectwerkz.expression.ExpressionInfo; import org.codehaus.aspectwerkz.reflect.MethodInfo; import org.codehaus.aspectwerkz.reflect.ClassInfo; import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo; import org.codehaus.aspectwerkz.transform.inlining.AsmHelper; import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint; /** * TODO is factory a good name, now that it does so much more? * <p/> * Factory for the different join point implementations. * Compiles a new join point on the fly and loads the class. * * @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a> */ public class JoinPointFactory { /** * Stores the compilation infos - mapped to the last compiled join point class based on this compilation info. */ private static final Map COMPILATION_INFO_REPOSITORY = new WeakHashMap(); /** * Compiles and loades a join point class, one specific class for each distinct join point. * * @param model the model for the compilation * @param loader the class loader that the compiled join point should live in * @return the compiled join point class */ public static Class compileJoinPointAndAttachToClassLoader(final CompilationInfo.Model model, final ClassLoader loader) { return attachToClassLoader(model.getJoinPointClassName(), loader, compileJoinPoint(model)); } /** * Loads a join point class, one specific class for each distinct join point. * * @param joinpointClassName * @param loader the class loader that the compiled join point should live in * @param bytecode of the joinpoint * @return the compiled join point class */ public static Class attachToClassLoader(final String joinpointClassName, final ClassLoader loader, final byte[] bytecode) { return AsmHelper.defineClass(loader, bytecode, joinpointClassName); } /** * Adds or updates a compilation info. The class key is always the first compiled join point class. * * @param clazz * @param compilationInfo */ public static void addCompilationInfo(final Class clazz, final CompilationInfo compilationInfo) { COMPILATION_INFO_REPOSITORY.put(clazz, compilationInfo); } /** * Compiles a join point class, one specific class for each distinct join point. * * @param model the model for the compilation * @return the compiled join point bytecode */ public static byte[] compileJoinPoint(final CompilationInfo.Model model) { switch (model.getEmittedJoinPoint().getJoinPointType()) { case JoinPointType.METHOD_EXECUTION_INT: return new MethodExecutionJoinPointCompiler(model).compile(); case JoinPointType.METHOD_CALL_INT: return new MethodCallJoinPointCompiler(model).compile(); case JoinPointType.CONSTRUCTOR_EXECUTION_INT: return new ConstructorExecutionJoinPointCompiler(model).compile(); case JoinPointType.CONSTRUCTOR_CALL_INT: return new ConstructorCallJoinPointCompiler(model).compile(); case JoinPointType.FIELD_SET_INT: return new FieldSetJoinPointCompiler(model).compile(); case JoinPointType.FIELD_GET_INT: return new FieldGetJoinPointCompiler(model).compile(); case JoinPointType.HANDLER_INT: return new HandlerJoinPointCompiler(model).compile(); case JoinPointType.STATIC_INITIALIZATION_INT: return new StaticInitializationJoinPointCompiler(model).compile(); default: throw new UnsupportedOperationException( "join point type is not supported: " + model.getEmittedJoinPoint().getJoinPointType() ); } } /** * Redefines the originally compiled join point. * * @param compilationInfo the model for the compilation * @return the compiled join point bytecode */ public static byte[] redefineJoinPoint(final CompilationInfo compilationInfo) { switch (compilationInfo.getInitialModel().getEmittedJoinPoint().getJoinPointType()) { case JoinPointType.METHOD_EXECUTION_INT: return new MethodExecutionJoinPointRedefiner(compilationInfo).compile(); case JoinPointType.METHOD_CALL_INT: return new MethodCallJoinPointRedefiner(compilationInfo).compile(); case JoinPointType.CONSTRUCTOR_EXECUTION_INT: return new ConstructorExecutionJoinPointRedefiner(compilationInfo).compile(); case JoinPointType.CONSTRUCTOR_CALL_INT: return new ConstructorCallJoinPointRedefiner(compilationInfo).compile(); case JoinPointType.FIELD_SET_INT: return new FieldSetJoinPointRedefiner(compilationInfo).compile(); case JoinPointType.FIELD_GET_INT: return new FieldGetJoinPointRedefiner(compilationInfo).compile(); case JoinPointType.HANDLER_INT: return new HandlerJoinPointRedefiner(compilationInfo).compile(); default: throw new UnsupportedOperationException( "join point type is not supported: " + compilationInfo.getInitialModel().getEmittedJoinPoint().getJoinPointType() ); } } /** * Returns a list with all the join point compilers that matches a specific pointcut expression. * <p/> * To be used for redefinition of the join point compilers only. This since the compilers must have been created * in advance to exist in the repository (which is done when the target class is loaded). * * @param expression the pointcut expression * @return a set with the matching emitted join point */ public static Set getJoinPointsMatching(final ExpressionInfo expression) { final Set matchingJoinPointInfos = new HashSet(); for (Iterator it = COMPILATION_INFO_REPOSITORY.entrySet().iterator(); it.hasNext();) { final Map.Entry entry = (Map.Entry) it.next(); final Class clazz = (Class) entry.getKey(); final CompilationInfo compilationInfo = (CompilationInfo) entry.getValue(); final EmittedJoinPoint joinPoint = (EmittedJoinPoint) compilationInfo. getInitialModel().getEmittedJoinPoint(); final ClassLoader loader = clazz.getClassLoader(); final ClassInfo calleeClassInfo = AsmClassInfo.getClassInfo(joinPoint.getCalleeClassName(), loader); final ClassInfo callerClassInfo = AsmClassInfo.getClassInfo(joinPoint.getCallerClassName(), loader); final MethodInfo callerMethodInfo = getCallerMethodInfo(callerClassInfo, joinPoint); ExpressionContext ctx = null; switch (joinPoint.getJoinPointType()) { case JoinPointType.METHOD_EXECUTION_INT: ctx = new ExpressionContext( PointcutType.EXECUTION, calleeClassInfo.getMethod(joinPoint.getJoinPointHash()), callerMethodInfo ); break; case JoinPointType.METHOD_CALL_INT: ctx = new ExpressionContext( PointcutType.CALL, calleeClassInfo.getMethod(joinPoint.getJoinPointHash()), callerMethodInfo ); break; case JoinPointType.CONSTRUCTOR_EXECUTION_INT: ctx = new ExpressionContext( PointcutType.EXECUTION, calleeClassInfo.getConstructor(joinPoint.getJoinPointHash()), callerMethodInfo ); break; case JoinPointType.CONSTRUCTOR_CALL_INT: ctx = new ExpressionContext( PointcutType.CALL, calleeClassInfo.getConstructor(joinPoint.getJoinPointHash()), callerMethodInfo ); break; case JoinPointType.FIELD_SET_INT: ctx = new ExpressionContext( PointcutType.SET, calleeClassInfo.getField(joinPoint.getJoinPointHash()), callerMethodInfo ); break; case JoinPointType.FIELD_GET_INT: ctx = new ExpressionContext( PointcutType.GET, calleeClassInfo.getField(joinPoint.getJoinPointHash()), callerMethodInfo ); break; case JoinPointType.HANDLER_INT: ctx = new ExpressionContext( PointcutType.HANDLER, AsmClassInfo.getClassInfo(joinPoint.getCalleeClassName(), loader), callerMethodInfo ); break; case JoinPointType.STATIC_INITIALIZATION_INT: ctx = new ExpressionContext( PointcutType.STATIC_INITIALIZATION, calleeClassInfo.staticInitializer(), calleeClassInfo ); } if (expression.getExpression().match(ctx)) { matchingJoinPointInfos.add(new MatchingJoinPointInfo(clazz, compilationInfo, ctx)); } } return matchingJoinPointInfos; } /** * Returns the emitted join point structure for a specific JIT generated join point class. * * @param clazz the join point class * @return the emitted join point structure */ public static EmittedJoinPoint getEmittedJoinPoint(final Class clazz) { return (EmittedJoinPoint) COMPILATION_INFO_REPOSITORY.get(clazz); } /** * Grabs the caller method info. * * @param callerClassInfo * @param emittedJoinPoint * @return */ private static MethodInfo getCallerMethodInfo(final ClassInfo callerClassInfo, final EmittedJoinPoint emittedJoinPoint) { MethodInfo callerMethodInfo = null; MethodInfo[] callerMethods = callerClassInfo.getMethods(); for (int i = 0; i < callerMethods.length; i++) { MethodInfo method = callerMethods[i]; if (method.getName().equals(emittedJoinPoint.getCallerMethodName()) && method.getSignature().equals(emittedJoinPoint.getCallerMethodDesc())) { callerMethodInfo = method; break; } } return callerMethodInfo; } }