1 /*************************************************************************************** 2 * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. * 3 * http://aspectwerkz.codehaus.org * 4 * ---------------------------------------------------------------------------------- * 5 * The software in this package is published under the terms of the LGPL license * 6 * a copy of which has been included with this distribution in the license.txt file. * 7 **************************************************************************************/ 8 package org.codehaus.aspectwerkz.transform.inlining.compiler; 9 10 import java.util.Map; 11 import java.util.Iterator; 12 import java.util.WeakHashMap; 13 import java.util.HashSet; 14 import java.util.Set; 15 16 import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType; 17 import org.codehaus.aspectwerkz.expression.ExpressionContext; 18 import org.codehaus.aspectwerkz.expression.PointcutType; 19 import org.codehaus.aspectwerkz.expression.ExpressionInfo; 20 import org.codehaus.aspectwerkz.reflect.MethodInfo; 21 import org.codehaus.aspectwerkz.reflect.ClassInfo; 22 import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo; 23 import org.codehaus.aspectwerkz.transform.inlining.AsmHelper; 24 import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint; 25 26 /*** 27 * TODO is factory a good name, now that it does so much more? 28 * <p/> 29 * Factory for the different join point implementations. 30 * Compiles a new join point on the fly and loads the class. 31 * 32 * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a> 33 */ 34 public class JoinPointFactory { 35 36 /*** 37 * Stores the compilation infos - mapped to the last compiled join point class based on this compilation info. 38 */ 39 private static final Map COMPILATION_INFO_REPOSITORY = new WeakHashMap(); 40 41 /*** 42 * Compiles and loades a join point class, one specific class for each distinct join point. 43 * 44 * @param model the model for the compilation 45 * @param loader the class loader that the compiled join point should live in 46 * @return the compiled join point class 47 */ 48 public static Class compileJoinPointAndAttachToClassLoader(final CompilationInfo.Model model, 49 final ClassLoader loader) { 50 return attachToClassLoader(model.getJoinPointClassName(), loader, compileJoinPoint(model)); 51 } 52 53 /*** 54 * Loads a join point class, one specific class for each distinct join point. 55 * 56 * @param joinpointClassName 57 * @param loader the class loader that the compiled join point should live in 58 * @param bytecode of the joinpoint 59 * @return the compiled join point class 60 */ 61 public static Class attachToClassLoader(final String joinpointClassName, 62 final ClassLoader loader, 63 final byte[] bytecode) { 64 return AsmHelper.defineClass(loader, bytecode, joinpointClassName); 65 } 66 67 /*** 68 * Adds or updates a compilation info. The class key is always the first compiled join point class. 69 * 70 * @param clazz 71 * @param compilationInfo 72 */ 73 public static void addCompilationInfo(final Class clazz, final CompilationInfo compilationInfo) { 74 COMPILATION_INFO_REPOSITORY.put(clazz, compilationInfo); 75 } 76 77 /*** 78 * Compiles a join point class, one specific class for each distinct join point. 79 * 80 * @param model the model for the compilation 81 * @return the compiled join point bytecode 82 */ 83 public static byte[] compileJoinPoint(final CompilationInfo.Model model) { 84 switch (model.getEmittedJoinPoint().getJoinPointType()) { 85 case JoinPointType.METHOD_EXECUTION_INT: 86 return new MethodExecutionJoinPointCompiler(model).compile(); 87 case JoinPointType.METHOD_CALL_INT: 88 return new MethodCallJoinPointCompiler(model).compile(); 89 case JoinPointType.CONSTRUCTOR_EXECUTION_INT: 90 return new ConstructorExecutionJoinPointCompiler(model).compile(); 91 case JoinPointType.CONSTRUCTOR_CALL_INT: 92 return new ConstructorCallJoinPointCompiler(model).compile(); 93 case JoinPointType.FIELD_SET_INT: 94 return new FieldSetJoinPointCompiler(model).compile(); 95 case JoinPointType.FIELD_GET_INT: 96 return new FieldGetJoinPointCompiler(model).compile(); 97 case JoinPointType.HANDLER_INT: 98 return new HandlerJoinPointCompiler(model).compile(); 99 case JoinPointType.STATIC_INITIALIZATION_INT: 100 return new StaticInitializationJoinPointCompiler(model).compile(); 101 default: 102 throw new UnsupportedOperationException( 103 "join point type is not supported: " + model.getEmittedJoinPoint().getJoinPointType() 104 ); 105 } 106 } 107 108 /*** 109 * Redefines the originally compiled join point. 110 * 111 * @param compilationInfo the model for the compilation 112 * @return the compiled join point bytecode 113 */ 114 public static byte[] redefineJoinPoint(final CompilationInfo compilationInfo) { 115 switch (compilationInfo.getInitialModel().getEmittedJoinPoint().getJoinPointType()) { 116 case JoinPointType.METHOD_EXECUTION_INT: 117 return new MethodExecutionJoinPointRedefiner(compilationInfo).compile(); 118 case JoinPointType.METHOD_CALL_INT: 119 return new MethodCallJoinPointRedefiner(compilationInfo).compile(); 120 case JoinPointType.CONSTRUCTOR_EXECUTION_INT: 121 return new ConstructorExecutionJoinPointRedefiner(compilationInfo).compile(); 122 case JoinPointType.CONSTRUCTOR_CALL_INT: 123 return new ConstructorCallJoinPointRedefiner(compilationInfo).compile(); 124 case JoinPointType.FIELD_SET_INT: 125 return new FieldSetJoinPointRedefiner(compilationInfo).compile(); 126 case JoinPointType.FIELD_GET_INT: 127 return new FieldGetJoinPointRedefiner(compilationInfo).compile(); 128 case JoinPointType.HANDLER_INT: 129 return new HandlerJoinPointRedefiner(compilationInfo).compile(); 130 default: 131 throw new UnsupportedOperationException( 132 "join point type is not supported: " + 133 compilationInfo.getInitialModel().getEmittedJoinPoint().getJoinPointType() 134 ); 135 } 136 } 137 138 /*** 139 * Returns a list with all the join point compilers that matches a specific pointcut expression. 140 * <p/> 141 * To be used for redefinition of the join point compilers only. This since the compilers must have been created 142 * in advance to exist in the repository (which is done when the target class is loaded). 143 * 144 * @param expression the pointcut expression 145 * @return a set with the matching emitted join point 146 */ 147 public static Set getJoinPointsMatching(final ExpressionInfo expression) { 148 final Set matchingJoinPointInfos = new HashSet(); 149 for (Iterator it = COMPILATION_INFO_REPOSITORY.entrySet().iterator(); it.hasNext();) { 150 final Map.Entry entry = (Map.Entry) it.next(); 151 152 final Class clazz = (Class) entry.getKey(); 153 final CompilationInfo compilationInfo = (CompilationInfo) entry.getValue(); 154 final EmittedJoinPoint joinPoint = (EmittedJoinPoint) compilationInfo. 155 getInitialModel().getEmittedJoinPoint(); 156 final ClassLoader loader = clazz.getClassLoader(); 157 158 final ClassInfo calleeClassInfo = AsmClassInfo.getClassInfo(joinPoint.getCalleeClassName(), loader); 159 final ClassInfo callerClassInfo = AsmClassInfo.getClassInfo(joinPoint.getCallerClassName(), loader); 160 final MethodInfo callerMethodInfo = getCallerMethodInfo(callerClassInfo, joinPoint); 161 162 ExpressionContext ctx = null; 163 switch (joinPoint.getJoinPointType()) { 164 case JoinPointType.METHOD_EXECUTION_INT: 165 ctx = new ExpressionContext( 166 PointcutType.EXECUTION, 167 calleeClassInfo.getMethod(joinPoint.getJoinPointHash()), 168 callerMethodInfo 169 ); 170 break; 171 case JoinPointType.METHOD_CALL_INT: 172 ctx = new ExpressionContext( 173 PointcutType.CALL, 174 calleeClassInfo.getMethod(joinPoint.getJoinPointHash()), 175 callerMethodInfo 176 ); 177 break; 178 case JoinPointType.CONSTRUCTOR_EXECUTION_INT: 179 ctx = new ExpressionContext( 180 PointcutType.EXECUTION, 181 calleeClassInfo.getConstructor(joinPoint.getJoinPointHash()), 182 callerMethodInfo 183 ); 184 break; 185 case JoinPointType.CONSTRUCTOR_CALL_INT: 186 ctx = new ExpressionContext( 187 PointcutType.CALL, 188 calleeClassInfo.getConstructor(joinPoint.getJoinPointHash()), 189 callerMethodInfo 190 ); 191 break; 192 case JoinPointType.FIELD_SET_INT: 193 ctx = new ExpressionContext( 194 PointcutType.SET, 195 calleeClassInfo.getField(joinPoint.getJoinPointHash()), 196 callerMethodInfo 197 ); 198 break; 199 case JoinPointType.FIELD_GET_INT: 200 ctx = new ExpressionContext( 201 PointcutType.GET, 202 calleeClassInfo.getField(joinPoint.getJoinPointHash()), 203 callerMethodInfo 204 ); 205 break; 206 case JoinPointType.HANDLER_INT: 207 ctx = new ExpressionContext( 208 PointcutType.HANDLER, 209 AsmClassInfo.getClassInfo(joinPoint.getCalleeClassName(), loader), 210 callerMethodInfo 211 ); 212 break; 213 case JoinPointType.STATIC_INITIALIZATION_INT: 214 ctx = new ExpressionContext( 215 PointcutType.STATIC_INITIALIZATION, 216 calleeClassInfo.staticInitializer(), 217 calleeClassInfo 218 ); 219 } 220 if (expression.getExpression().match(ctx)) { 221 matchingJoinPointInfos.add(new MatchingJoinPointInfo(clazz, compilationInfo, ctx)); 222 } 223 } 224 return matchingJoinPointInfos; 225 } 226 227 /*** 228 * Returns the emitted join point structure for a specific JIT generated join point class. 229 * 230 * @param clazz the join point class 231 * @return the emitted join point structure 232 */ 233 public static EmittedJoinPoint getEmittedJoinPoint(final Class clazz) { 234 return (EmittedJoinPoint) COMPILATION_INFO_REPOSITORY.get(clazz); 235 } 236 237 /*** 238 * Grabs the caller method info. 239 * 240 * @param callerClassInfo 241 * @param emittedJoinPoint 242 * @return 243 */ 244 private static MethodInfo getCallerMethodInfo(final ClassInfo callerClassInfo, 245 final EmittedJoinPoint emittedJoinPoint) { 246 MethodInfo callerMethodInfo = null; 247 MethodInfo[] callerMethods = callerClassInfo.getMethods(); 248 for (int i = 0; i < callerMethods.length; i++) { 249 MethodInfo method = callerMethods[i]; 250 if (method.getName().equals(emittedJoinPoint.getCallerMethodName()) && 251 method.getSignature().equals(emittedJoinPoint.getCallerMethodDesc())) { 252 callerMethodInfo = method; 253 break; 254 } 255 } 256 return callerMethodInfo; 257 } 258 }