1 4 package com.tc.aspectwerkz.transform.inlining.weaver; 5 6 import java.util.HashMap ; 7 import java.util.HashSet ; 8 import java.util.Iterator ; 9 import java.util.List ; 10 import java.util.Map ; 11 import java.util.Set ; 12 13 import com.tc.asm.ClassAdapter; 14 import com.tc.asm.ClassVisitor; 15 import com.tc.asm.MethodAdapter; 16 import com.tc.asm.MethodVisitor; 17 import com.tc.asm.Type; 18 19 import com.tc.aspectwerkz.DeploymentModel; 20 import com.tc.aspectwerkz.expression.PointcutType; 21 import com.tc.aspectwerkz.expression.ExpressionContext; 22 import com.tc.aspectwerkz.reflect.ClassInfo; 23 import com.tc.aspectwerkz.reflect.FieldInfo; 24 import com.tc.aspectwerkz.reflect.MethodInfo; 25 import com.tc.aspectwerkz.definition.MixinDefinition; 26 import com.tc.aspectwerkz.definition.SystemDefinition; 27 import com.tc.aspectwerkz.exception.DefinitionException; 28 import com.tc.aspectwerkz.transform.InstrumentationContext; 29 import com.tc.aspectwerkz.transform.TransformationConstants; 30 import com.tc.aspectwerkz.transform.inlining.AsmHelper; 31 32 37 public class AddMixinMethodsVisitor extends ClassAdapter implements TransformationConstants { 38 39 private final InstrumentationContext m_ctx; 40 private String m_declaringTypeName; 41 private final ClassInfo m_classInfo; 42 private final Set m_addedMethods; 43 private ExpressionContext m_expressionContext; 44 private boolean m_hasClinit = false; 45 private Map m_mixinFields; 46 private boolean m_isAdvised = false; 47 48 56 public AddMixinMethodsVisitor(final ClassVisitor cv, 57 final ClassInfo classInfo, 58 final InstrumentationContext ctx, 59 final Set addedMethods) { 60 super(cv); 61 m_classInfo = classInfo; 62 m_ctx = (InstrumentationContext) ctx; 63 m_addedMethods = addedMethods; 64 m_expressionContext = new ExpressionContext(PointcutType.WITHIN, m_classInfo, m_classInfo); 65 } 66 67 76 public void visit(final int version, 77 final int access, 78 final String name, 79 final String signature, 80 final String superName, 81 final String [] interfaces) { 82 ExpressionContext ctx = new ExpressionContext(PointcutType.WITHIN, m_classInfo, m_classInfo); 83 if (!classFilter(m_classInfo, ctx, m_ctx.getDefinitions())) { 84 m_declaringTypeName = name; 85 m_mixinFields = new HashMap (); 86 87 for (int i = 0; i < m_classInfo.getFields().length; i++) { 89 FieldInfo fieldInfo = m_classInfo.getFields()[i]; 90 if (fieldInfo.getName().startsWith(MIXIN_FIELD_NAME)) { 91 m_mixinFields.put(fieldInfo.getType(), fieldInfo); 92 } 93 } 94 95 addMixinMembers(); 97 } 98 super.visit(version, access, name, signature, superName, interfaces); 99 } 100 101 104 private void addMixinMembers() { 105 int index = 0; 106 for (Iterator it = m_ctx.getDefinitions().iterator(); it.hasNext();) { 107 List mixinDefs = ((SystemDefinition) it.next()).getMixinDefinitions(m_expressionContext); 108 109 Set interfaceSet = new HashSet (); 111 for (Iterator it2 = mixinDefs.iterator(); it2.hasNext();) { 112 interfaceSet.addAll(((MixinDefinition) it2.next()).getInterfaceClassNames()); 113 } 114 119 for (Iterator it2 = mixinDefs.iterator(); it2.hasNext();) { 120 final MixinDefinition mixinDef = (MixinDefinition) it2.next(); 121 final ClassInfo mixinImpl = mixinDef.getMixinImpl(); 122 final DeploymentModel deploymentModel = mixinDef.getDeploymentModel(); 123 124 if (m_mixinFields.containsKey(mixinImpl)) { 125 continue; 126 } 127 final MixinFieldInfo fieldInfo = new MixinFieldInfo(); 128 fieldInfo.fieldName = MIXIN_FIELD_NAME + index; 129 fieldInfo.mixinClassInfo = mixinImpl; 130 131 addMixinField(fieldInfo, deploymentModel, mixinDef); 132 addMixinMethods(fieldInfo, mixinDef); 133 134 index++; 135 m_isAdvised = true; 136 } 137 } 138 } 139 140 150 public MethodVisitor visitMethod(final int access, 151 final String name, 152 final String desc, 153 final String signature, 154 final String [] exceptions) { 155 if (m_isAdvised) { 156 if (name.equals(CLINIT_METHOD_NAME)) { 157 m_hasClinit = true; 158 MethodVisitor mv = new PrependToClinitMethodCodeAdapter( 159 cv.visitMethod(access, name, desc, signature, exceptions) 160 ); 161 mv.visitMaxs(0, 0); 162 return mv; 163 } else if (name.equals(INIT_METHOD_NAME)) { 164 MethodVisitor mv = new AppendToInitMethodCodeAdapter( 165 cv.visitMethod(access, name, desc, signature, exceptions) 166 ); 167 mv.visitMaxs(0, 0); 168 return mv; 169 } 170 } 171 return super.visitMethod(access, name, desc, signature, exceptions); 172 } 173 174 177 public void visitEnd() { 178 if (m_isAdvised && !m_hasClinit) { 179 MethodVisitor mv = cv.visitMethod( 181 ACC_STATIC, CLINIT_METHOD_NAME, NO_PARAM_RETURN_VOID_SIGNATURE, null, null 182 ); 183 for (Iterator i4 = m_mixinFields.values().iterator(); i4.hasNext();) { 184 MixinFieldInfo fieldInfo = (MixinFieldInfo) i4.next(); 185 if (fieldInfo.isStatic) { 186 initializeStaticMixinField(mv, fieldInfo); 187 } 188 } 189 190 mv.visitInsn(RETURN); 191 mv.visitMaxs(0, 0); 192 } 193 super.visitEnd(); 194 } 195 196 202 private void initializeStaticMixinField(final MethodVisitor mv, final MixinFieldInfo fieldInfo) { 203 mv.visitLdcInsn(fieldInfo.mixinClassInfo.getName().replace('/', '.')); 204 if (fieldInfo.isPerJVM) { 205 mv.visitFieldInsn( 206 GETSTATIC, 207 m_declaringTypeName, 208 TARGET_CLASS_FIELD_NAME, 209 CLASS_CLASS_SIGNATURE 210 ); 211 mv.visitMethodInsn( 212 INVOKEVIRTUAL, 213 CLASS_CLASS, 214 GETCLASSLOADER_METHOD_NAME, 215 CLASS_CLASS_GETCLASSLOADER_METHOD_SIGNATURE 216 ); 217 mv.visitMethodInsn( 218 INVOKESTATIC, 219 MIXINS_CLASS_NAME, 220 MIXIN_OF_METHOD_NAME, 221 MIXIN_OF_METHOD_PER_JVM_SIGNATURE 222 ); 223 } else { 224 mv.visitFieldInsn( 225 GETSTATIC, 226 m_declaringTypeName, 227 TARGET_CLASS_FIELD_NAME, 228 CLASS_CLASS_SIGNATURE 229 ); 230 mv.visitMethodInsn( 231 INVOKESTATIC, 232 MIXINS_CLASS_NAME, 233 MIXIN_OF_METHOD_NAME, 234 MIXIN_OF_METHOD_PER_CLASS_SIGNATURE 235 ); 236 } 237 mv.visitTypeInsn(CHECKCAST, fieldInfo.mixinClassInfo.getName().replace('.', '/')); 238 mv.visitFieldInsn( 239 PUTSTATIC, 240 m_declaringTypeName, 241 fieldInfo.fieldName, 242 fieldInfo.mixinClassInfo.getSignature() 243 ); 244 } 245 246 252 private void initializeMemberMixinField(final MethodVisitor mv, final MixinFieldInfo fieldInfo) { 253 mv.visitVarInsn(ALOAD, 0); 254 mv.visitLdcInsn(fieldInfo.mixinClassInfo.getName().replace('/', '.')); 255 mv.visitVarInsn(ALOAD, 0); 256 mv.visitMethodInsn( 257 INVOKESTATIC, 258 MIXINS_CLASS_NAME, 259 MIXIN_OF_METHOD_NAME, 260 MIXIN_OF_METHOD_PER_INSTANCE_SIGNATURE 261 ); 262 mv.visitTypeInsn(CHECKCAST, fieldInfo.mixinClassInfo.getName().replace('.', '/')); 263 mv.visitFieldInsn( 264 PUTFIELD, 265 m_declaringTypeName, 266 fieldInfo.fieldName, 267 fieldInfo.mixinClassInfo.getSignature() 268 ); 269 } 270 271 278 private void addMixinField(final MixinFieldInfo fieldInfo, 279 final DeploymentModel deploymentModel, 280 final MixinDefinition mixinDef) { 281 final String signature = fieldInfo.mixinClassInfo.getSignature(); 282 int modifiers = 0; 283 if (deploymentModel.equals(DeploymentModel.PER_CLASS) || deploymentModel.equals(DeploymentModel.PER_JVM)) { 284 fieldInfo.isStatic = true; 285 fieldInfo.isPerJVM = deploymentModel.equals(DeploymentModel.PER_JVM); 286 modifiers = ACC_PRIVATE + ACC_FINAL + ACC_STATIC; 287 } else if (deploymentModel.equals(DeploymentModel.PER_INSTANCE)) { 289 fieldInfo.isStatic = false; 290 modifiers = ACC_PRIVATE + ACC_FINAL; 291 } 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 MethodVisitor 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 MethodAdapter { 394 395 public PrependToClinitMethodCodeAdapter(final MethodVisitor 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 MethodAdapter { 412 413 public AppendToInitMethodCodeAdapter(final MethodVisitor 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(mv, 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 |