1 8 package org.codehaus.aspectwerkz.transform.inlining.weaver; 9 10 import java.util.Iterator ; 11 import java.util.Set ; 12 import java.util.List ; 13 import java.util.Map ; 14 import java.util.HashMap ; 15 import java.util.HashSet ; 16 17 import org.objectweb.asm.*; 18 import org.codehaus.aspectwerkz.transform.Context; 19 import org.codehaus.aspectwerkz.transform.TransformationConstants; 20 import org.codehaus.aspectwerkz.transform.TransformationUtil; 21 import org.codehaus.aspectwerkz.transform.inlining.ContextImpl; 22 import org.codehaus.aspectwerkz.transform.inlining.AsmHelper; 23 import org.codehaus.aspectwerkz.definition.SystemDefinition; 24 import org.codehaus.aspectwerkz.definition.MixinDefinition; 25 import org.codehaus.aspectwerkz.expression.ExpressionContext; 26 import org.codehaus.aspectwerkz.expression.PointcutType; 27 import org.codehaus.aspectwerkz.reflect.ClassInfo; 28 import org.codehaus.aspectwerkz.reflect.MethodInfo; 29 import org.codehaus.aspectwerkz.reflect.ClassInfoHelper; 30 import org.codehaus.aspectwerkz.reflect.FieldInfo; 31 import org.codehaus.aspectwerkz.DeploymentModel; 32 import org.codehaus.aspectwerkz.DeploymentModel; 33 import org.codehaus.aspectwerkz.exception.DefinitionException; 34 35 40 public class AddMixinMethodsVisitor extends ClassAdapter implements TransformationConstants { 41 42 private final ContextImpl m_ctx; 43 private String m_declaringTypeName; 44 private final ClassInfo m_classInfo; 45 private final Set m_addedMethods; 46 private ExpressionContext m_expressionContext; 47 private boolean m_hasClinit = false; 48 private Map m_mixinFields; 49 private boolean m_isAdvised = false; 50 51 59 public AddMixinMethodsVisitor(final ClassVisitor cv, 60 final ClassInfo classInfo, 61 final Context ctx, 62 final Set addedMethods) { 63 super(cv); 64 m_classInfo = classInfo; 65 m_ctx = (ContextImpl) ctx; 66 m_addedMethods = addedMethods; 67 m_expressionContext = new ExpressionContext(PointcutType.WITHIN, m_classInfo, m_classInfo); 68 } 69 70 79 public void visit(final int version, 80 final int access, 81 final String name, 82 final String superName, 83 final String [] interfaces, 84 final String sourceFile) { 85 ExpressionContext ctx = new ExpressionContext(PointcutType.WITHIN, m_classInfo, m_classInfo); 86 if (!classFilter(m_classInfo, ctx, m_ctx.getDefinitions())) { 87 m_declaringTypeName = name; 88 m_mixinFields = new HashMap (); 89 90 for (int i = 0; i < m_classInfo.getFields().length; i++) { 92 FieldInfo fieldInfo = m_classInfo.getFields()[i]; 93 if (fieldInfo.getName().startsWith(MIXIN_FIELD_NAME)) { 94 m_mixinFields.put(fieldInfo.getType(), fieldInfo); 95 } 96 } 97 98 addMixinMembers(); 100 } 101 super.visit(version, access, name, superName, interfaces, sourceFile); 102 } 103 104 107 private void addMixinMembers() { 108 int index = 0; 109 for (Iterator it = m_ctx.getDefinitions().iterator(); it.hasNext();) { 110 List mixinDefs = ((SystemDefinition) it.next()).getMixinDefinitions(m_expressionContext); 111 112 Set interfaceSet = new HashSet (); 114 for (Iterator it2 = mixinDefs.iterator(); it2.hasNext();) { 115 interfaceSet.addAll(((MixinDefinition) it2.next()).getInterfaceClassNames()); 116 } 117 if (ClassInfoHelper.hasMethodClash(interfaceSet, m_ctx.getLoader())) { 118 return; 119 } 120 121 for (Iterator it2 = mixinDefs.iterator(); it2.hasNext();) { 122 final MixinDefinition mixinDef = (MixinDefinition) it2.next(); 123 final ClassInfo mixinImpl = mixinDef.getMixinImpl(); 124 final DeploymentModel deploymentModel = mixinDef.getDeploymentModel(); 125 126 if (m_mixinFields.containsKey(mixinImpl)) { 127 continue; 128 } 129 final MixinFieldInfo fieldInfo = new MixinFieldInfo(); 130 fieldInfo.fieldName = MIXIN_FIELD_NAME + index; 131 fieldInfo.mixinClassInfo = mixinImpl; 132 133 addMixinField(fieldInfo, deploymentModel, mixinDef); 134 addMixinMethods(fieldInfo, mixinDef); 135 136 index++; 137 m_isAdvised = true; 138 } 139 } 140 } 141 142 152 public CodeVisitor visitMethod(final int access, 153 final String name, 154 final String desc, 155 final String [] exceptions, 156 final Attribute attrs) { 157 if (m_isAdvised) { 158 if (name.equals(CLINIT_METHOD_NAME)) { 159 m_hasClinit = true; 160 CodeVisitor mv = new PrependToClinitMethodCodeAdapter( 161 cv.visitMethod(access, name, desc, exceptions, attrs) 162 ); 163 mv.visitMaxs(0, 0); 164 return mv; 165 } else if (name.equals(INIT_METHOD_NAME)) { 166 CodeVisitor mv = new AppendToInitMethodCodeAdapter( 167 cv.visitMethod(access, name, desc, exceptions, attrs) 168 ); 169 mv.visitMaxs(0, 0); 170 return mv; 171 } 172 } 173 return super.visitMethod(access, name, desc, exceptions, attrs); 174 } 175 176 179 public void visitEnd() { 180 if (m_isAdvised && !m_hasClinit) { 181 CodeVisitor mv = cv.visitMethod( 183 ACC_STATIC, CLINIT_METHOD_NAME, NO_PARAM_RETURN_VOID_SIGNATURE, null, null 184 ); 185 for (Iterator i4 = m_mixinFields.values().iterator(); i4.hasNext();) { 186 MixinFieldInfo fieldInfo = (MixinFieldInfo) i4.next(); 187 if (fieldInfo.isStatic) { 188 initializeStaticMixinField(mv, fieldInfo); 189 } 190 } 191 192 mv.visitInsn(RETURN); 193 mv.visitMaxs(0, 0); 194 } 195 super.visitEnd(); 196 } 197 198 204 private void initializeStaticMixinField(final CodeVisitor mv, final MixinFieldInfo fieldInfo) { 205 mv.visitLdcInsn(fieldInfo.mixinClassInfo.getName().replace('/', '.')); 206 if (fieldInfo.isPerJVM) { 207 mv.visitFieldInsn( 208 GETSTATIC, 209 m_declaringTypeName, 210 TARGET_CLASS_FIELD_NAME, 211 CLASS_CLASS_SIGNATURE 212 ); 213 mv.visitMethodInsn( 214 INVOKEVIRTUAL, 215 CLASS_CLASS, 216 GETCLASSLOADER_METHOD_NAME, 217 CLASS_CLASS_GETCLASSLOADER_METHOD_SIGNATURE 218 ); 219 mv.visitMethodInsn( 220 INVOKESTATIC, 221 MIXINS_CLASS_NAME, 222 MIXIN_OF_METHOD_NAME, 223 MIXIN_OF_METHOD_PER_JVM_SIGNATURE 224 ); 225 } else { 226 mv.visitFieldInsn( 227 GETSTATIC, 228 m_declaringTypeName, 229 TARGET_CLASS_FIELD_NAME, 230 CLASS_CLASS_SIGNATURE 231 ); 232 mv.visitMethodInsn( 233 INVOKESTATIC, 234 MIXINS_CLASS_NAME, 235 MIXIN_OF_METHOD_NAME, 236 MIXIN_OF_METHOD_PER_CLASS_SIGNATURE 237 ); 238 } 239 mv.visitTypeInsn(CHECKCAST, fieldInfo.mixinClassInfo.getName().replace('.', '/')); 240 mv.visitFieldInsn( 241 PUTSTATIC, 242 m_declaringTypeName, 243 fieldInfo.fieldName, 244 fieldInfo.mixinClassInfo.getSignature() 245 ); 246 } 247 248 254 private void initializeMemberMixinField(final CodeVisitor mv, final MixinFieldInfo fieldInfo) { 255 mv.visitVarInsn(ALOAD, 0); 256 mv.visitLdcInsn(fieldInfo.mixinClassInfo.getName().replace('/', '.')); 257 mv.visitVarInsn(ALOAD, 0); 258 mv.visitMethodInsn( 259 INVOKESTATIC, 260 MIXINS_CLASS_NAME, 261 MIXIN_OF_METHOD_NAME, 262 MIXIN_OF_METHOD_PER_INSTANCE_SIGNATURE 263 ); 264 mv.visitTypeInsn(CHECKCAST, fieldInfo.mixinClassInfo.getName().replace('.', '/')); 265 mv.visitFieldInsn( 266 PUTFIELD, 267 m_declaringTypeName, 268 fieldInfo.fieldName, 269 fieldInfo.mixinClassInfo.getSignature() 270 ); 271 } 272 273 280 private void addMixinField(final MixinFieldInfo fieldInfo, 281 final DeploymentModel deploymentModel, 282 final MixinDefinition mixinDef) { 283 final String signature = fieldInfo.mixinClassInfo.getSignature(); 284 int modifiers = 0; 285 if (deploymentModel.equals(DeploymentModel.PER_CLASS) || deploymentModel.equals(DeploymentModel.PER_JVM)) { 286 fieldInfo.isStatic = true; 287 fieldInfo.isPerJVM = deploymentModel.equals(DeploymentModel.PER_JVM); 288 modifiers = ACC_PRIVATE + ACC_FINAL + ACC_STATIC + ACC_SYNTHETIC; 289 } else if (deploymentModel.equals(DeploymentModel.PER_INSTANCE)) { 290 fieldInfo.isStatic = false; 291 modifiers = ACC_PRIVATE + ACC_FINAL + ACC_SYNTHETIC; 292 } else { 293 throw new DefinitionException( 294 "deployment model [" + mixinDef.getDeploymentModel() + 295 "] for mixin [" + mixinDef.getMixinImpl().getName() + 296 "] is not supported" 297 ); 298 299 } 300 if (mixinDef.isTransient()) { 301 modifiers += ACC_TRANSIENT; 302 } 303 cv.visitField(modifiers, fieldInfo.fieldName, signature, null, null); 304 m_mixinFields.put(mixinDef.getMixinImpl(), fieldInfo); 305 } 306 307 313 private void addMixinMethods(final MixinFieldInfo fieldInfo, final MixinDefinition mixinDef) { 314 for (Iterator it3 = mixinDef.getMethodsToIntroduce().iterator(); it3.hasNext();) { 315 MethodInfo methodInfo = (MethodInfo) it3.next(); 316 final String methodName = methodInfo.getName(); 317 final String methodSignature = methodInfo.getSignature(); 318 319 if (m_addedMethods.contains(AlreadyAddedMethodVisitor.getMethodKey(methodName, methodSignature))) { 320 continue; 321 } 322 323 CodeVisitor mv = cv.visitMethod( 324 ACC_PUBLIC + ACC_SYNTHETIC, 325 methodName, 326 methodSignature, 327 null, 328 null 329 ); 330 if (fieldInfo.isStatic) { 331 mv.visitFieldInsn( 332 GETSTATIC, 333 m_declaringTypeName, 334 fieldInfo.fieldName, 335 fieldInfo.mixinClassInfo.getSignature() 336 ); 337 } else { 338 mv.visitVarInsn(ALOAD, 0); 339 mv.visitFieldInsn( 340 GETFIELD, 341 m_declaringTypeName, 342 fieldInfo.fieldName, 343 fieldInfo.mixinClassInfo.getSignature() 344 ); 345 } 346 AsmHelper.loadArgumentTypes(mv, Type.getArgumentTypes(methodSignature), false); 347 mv.visitMethodInsn( 348 INVOKEVIRTUAL, 349 fieldInfo.mixinClassInfo.getName().replace('.', '/'), 350 methodName, 351 methodSignature 352 ); 353 AsmHelper.addReturnStatement(mv, Type.getReturnType(methodSignature)); 354 mv.visitMaxs(0, 0); 355 } 356 } 357 358 366 public static boolean classFilter(final ClassInfo classInfo, 367 final ExpressionContext ctx, 368 final Set definitions) { 369 for (Iterator it = definitions.iterator(); it.hasNext();) { 370 SystemDefinition systemDef = (SystemDefinition) it.next(); 371 if (classInfo.isInterface()) { 372 return true; 373 } 374 String className = classInfo.getName().replace('/', '.'); 375 if (systemDef.inExcludePackage(className)) { 376 return true; 377 } 378 if (!systemDef.inIncludePackage(className)) { 379 return true; 380 } 381 if (systemDef.hasMixin(ctx)) { 382 return false; 383 } 384 } 385 return true; 386 } 387 388 393 public class PrependToClinitMethodCodeAdapter extends CodeAdapter { 394 395 public PrependToClinitMethodCodeAdapter(final CodeVisitor ca) { 396 super(ca); 397 for (Iterator i4 = m_mixinFields.values().iterator(); i4.hasNext();) { 398 MixinFieldInfo fieldInfo = (MixinFieldInfo) i4.next(); 399 if (fieldInfo.isStatic) { 400 initializeStaticMixinField(ca, fieldInfo); 401 } 402 } 403 } 404 } 405 406 411 public class AppendToInitMethodCodeAdapter extends CodeAdapter { 412 413 public AppendToInitMethodCodeAdapter(final CodeVisitor ca) { 414 super(ca); 415 } 416 417 public void visitInsn(final int opcode) { 418 if (opcode == RETURN) { 419 for (Iterator i4 = m_mixinFields.values().iterator(); i4.hasNext();) { 420 MixinFieldInfo fieldInfo = (MixinFieldInfo) i4.next(); 421 if (!fieldInfo.isStatic) { 422 initializeMemberMixinField(cv, fieldInfo); 423 } 424 } 425 } 426 super.visitInsn(opcode); 427 } 428 } 429 430 private static class MixinFieldInfo { 431 private String fieldName; 432 private ClassInfo mixinClassInfo; 433 private boolean isStatic; 434 private boolean isPerJVM = false; 435 } 436 } | Popular Tags |