1 8 package org.codehaus.aspectwerkz.annotation.instrumentation.asm; 9 10 import com.thoughtworks.qdox.model.JavaField; 11 import com.thoughtworks.qdox.model.JavaMethod; 12 13 import org.codehaus.aspectwerkz.annotation.instrumentation.AttributeEnhancer; 14 import org.codehaus.aspectwerkz.definition.DescriptorUtil; 15 import org.codehaus.aspectwerkz.exception.WrappedRuntimeException; 16 import org.codehaus.aspectwerkz.expression.QDoxParser; 17 import org.codehaus.aspectwerkz.reflect.TypeConverter; 18 import org.codehaus.aspectwerkz.transform.inlining.AsmHelper; 19 import org.objectweb.asm.Attribute; 20 import org.objectweb.asm.ClassAdapter; 21 import org.objectweb.asm.ClassReader; 22 import org.objectweb.asm.ClassVisitor; 23 import org.objectweb.asm.ClassWriter; 24 import org.objectweb.asm.CodeVisitor; 25 import org.objectweb.asm.attrs.RuntimeInvisibleAnnotations; 26 import org.objectweb.asm.attrs.Attributes; 27 28 import java.io.ByteArrayOutputStream ; 29 import java.io.File ; 30 import java.io.FileOutputStream ; 31 import java.io.IOException ; 32 import java.io.InputStream ; 33 import java.io.ObjectOutputStream ; 34 import java.net.URL ; 35 import java.net.URLClassLoader ; 36 import java.util.ArrayList ; 37 import java.util.Arrays ; 38 import java.util.Iterator ; 39 import java.util.List ; 40 41 47 public class AsmAttributeEnhancer implements AttributeEnhancer { 48 51 private ClassReader m_reader = null; 52 53 56 private String m_classFileName = null; 57 58 61 private String m_className = null; 62 63 66 private URLClassLoader m_loader = null; 67 68 71 private List m_classAttributes = new ArrayList (); 72 73 76 private List m_constructorAttributes = new ArrayList (); 77 78 81 private List m_methodAttributes = new ArrayList (); 82 83 86 private List m_fieldAttributes = new ArrayList (); 87 88 95 public boolean initialize(final String className, final URL [] classPath) { 96 try { 97 m_className = className; 98 m_loader = new URLClassLoader (classPath); 99 m_classFileName = className.replace('.', '/') + ".class"; 100 InputStream classAsStream = m_loader.getResourceAsStream(m_classFileName); 101 if (classAsStream == null) { 102 return false; 103 } 104 try { 106 m_reader = new ClassReader(classAsStream); 107 } catch (Exception e) { 108 throw new ClassNotFoundException (m_className, e); 109 } finally { 110 classAsStream.close(); } 112 } catch (Exception e) { 113 throw new WrappedRuntimeException(e); 114 } 115 return true; 116 } 117 118 123 public void insertClassAttribute(final Object attribute) { 124 if (m_reader == null) { 125 throw new IllegalStateException ("attribute enhancer is not initialized"); 126 } 127 final byte[] serializedAttribute = serialize(attribute); 128 m_classAttributes.add(serializedAttribute); 129 } 130 131 137 public void insertFieldAttribute(final JavaField field, final Object attribute) { 138 if (m_reader == null) { 139 throw new IllegalStateException ("attribute enhancer is not initialized"); 140 } 141 final byte[] serializedAttribute = serialize(attribute); 142 m_fieldAttributes.add(new FieldAttributeInfo(field, serializedAttribute)); 143 } 144 145 151 public void insertMethodAttribute(final JavaMethod method, final Object attribute) { 152 if (m_reader == null) { 153 throw new IllegalStateException ("attribute enhancer is not initialized"); 154 } 155 final String [] methodParamTypes = new String [method.getParameters().length]; 156 for (int i = 0; i < methodParamTypes.length; i++) { 157 methodParamTypes[i] = TypeConverter.convertTypeToJava(method.getParameters()[i].getType()); 158 } 159 final byte[] serializedAttribute = serialize(attribute); 160 m_methodAttributes.add(new MethodAttributeInfo(method, serializedAttribute)); 161 } 162 163 169 public void insertConstructorAttribute(final JavaMethod constructor, final Object attribute) { 170 if (m_reader == null) { 171 throw new IllegalStateException ("attribute enhancer is not initialized"); 172 } 173 final String [] methodParamTypes = new String [constructor.getParameters().length]; 174 for (int i = 0; i < methodParamTypes.length; i++) { 175 methodParamTypes[i] = TypeConverter.convertTypeToJava(constructor.getParameters()[i].getType()); 176 } 177 final byte[] serializedAttribute = serialize(attribute); 178 m_constructorAttributes.add(new MethodAttributeInfo(constructor, serializedAttribute)); 179 } 180 181 186 public void write(final String destDir) { 187 if (m_reader == null) { 188 throw new IllegalStateException ("attribute enhancer is not initialized"); 189 } 190 try { 191 ClassWriter writer = AsmHelper.newClassWriter(true); 193 m_reader.accept(new AttributeClassAdapter(writer), Attributes.getDefaultAttributes(), false); 194 195 String path = destDir + File.separator + m_classFileName; 197 File file = new File (path); 198 File parentFile = file.getParentFile(); 199 if (!parentFile.exists()) { 200 if (!parentFile.mkdirs()) { 202 throw new RuntimeException ( 203 "could not create dir structure needed to write file " 204 + path 205 + " to disk" 206 ); 207 } 208 } 209 FileOutputStream os = new FileOutputStream (destDir + File.separator + m_classFileName); 210 os.write(writer.toByteArray()); 211 os.close(); 212 } catch (IOException e) { 213 throw new WrappedRuntimeException(e); 214 } 215 } 216 217 223 public static byte[] serialize(final Object attribute) { 224 try { 225 ByteArrayOutputStream baos = new ByteArrayOutputStream (); 226 ObjectOutputStream oos = new ObjectOutputStream (baos); 227 oos.writeObject(attribute); 228 return baos.toByteArray(); 229 } catch (IOException e) { 230 throw new WrappedRuntimeException(e); 231 } 232 } 233 234 239 public String [] getNearestInterfacesInHierarchy(final String innerClassName) { 240 if (m_loader == null) { 241 throw new IllegalStateException ("attribute enhancer is not initialized"); 242 } 243 try { 244 Class innerClass = Class.forName(innerClassName, false, m_loader); 245 return getNearestInterfacesInHierarchy(innerClass); 246 } catch (ClassNotFoundException e) { 247 throw new RuntimeException ("could not load mixin for mixin implicit interface: " + e.toString()); 248 } catch (NoClassDefFoundError er) { 249 throw new RuntimeException ( 251 "could not find dependency for mixin implicit interface: " 252 + innerClassName 253 + " due to: " 254 + er.toString() 255 ); 256 } 257 } 258 259 264 private String [] getNearestInterfacesInHierarchy(final Class root) { 265 if (root == null) { 266 return new String []{}; 267 } 268 Class [] implementedClasses = root.getInterfaces(); 269 String [] interfaces = null; 270 if (implementedClasses.length == 0) { 271 interfaces = getNearestInterfacesInHierarchy(root.getSuperclass()); 272 } else { 273 interfaces = new String [implementedClasses.length]; 274 for (int i = 0; i < implementedClasses.length; i++) { 275 interfaces[i] = implementedClasses[i].getName(); 276 } 277 } 278 return interfaces; 279 } 280 281 286 private class AttributeClassAdapter extends ClassAdapter { 287 private static final String INIT_METHOD_NAME = "<init>"; 288 289 private boolean classLevelAnnotationDone = false; 290 291 public AttributeClassAdapter(final ClassVisitor cv) { 292 super(cv); 293 } 294 295 public void visitField(final int access, 296 final String name, 297 final String desc, 298 final Object value, 299 final Attribute attrs) { 300 301 RuntimeInvisibleAnnotations invisible = CustomAttributeHelper.linkRuntimeInvisibleAnnotations(attrs); 302 for (Iterator it = m_fieldAttributes.iterator(); it.hasNext();) { 303 FieldAttributeInfo struct = (FieldAttributeInfo) it.next(); 304 if (name.equals(struct.field.getName())) { 305 invisible.annotations.add(CustomAttributeHelper.createCustomAnnotation(struct.attribute)); 306 } 307 } 308 if (invisible.annotations.size() == 0) { 309 invisible = null; 310 } 311 super.visitField(access, name, desc, value, (attrs != null) ? attrs : invisible); 312 } 313 314 public CodeVisitor visitMethod(final int access, 315 final String name, 316 final String desc, 317 final String [] exceptions, 318 final Attribute attrs) { 319 320 RuntimeInvisibleAnnotations invisible = CustomAttributeHelper.linkRuntimeInvisibleAnnotations(attrs); 321 if (!name.equals(INIT_METHOD_NAME)) { 322 for (Iterator it = m_methodAttributes.iterator(); it.hasNext();) { 323 MethodAttributeInfo struct = (MethodAttributeInfo) it.next(); 324 JavaMethod method = struct.method; 325 String [] parameters = QDoxParser.getJavaMethodParametersAsStringArray(method); 326 if (name.equals(method.getName()) && Arrays.equals(parameters, DescriptorUtil.getParameters(desc))) { 327 invisible.annotations.add(CustomAttributeHelper.createCustomAnnotation(struct.attribute)); 328 } 329 } 330 } else { 331 for (Iterator it = m_constructorAttributes.iterator(); it.hasNext();) { 332 MethodAttributeInfo struct = (MethodAttributeInfo) it.next(); 333 JavaMethod method = struct.method; 334 String [] parameters = QDoxParser.getJavaMethodParametersAsStringArray(method); 335 if (name.equals(INIT_METHOD_NAME) && Arrays.equals(parameters, DescriptorUtil.getParameters(desc))) { 336 invisible.annotations.add(CustomAttributeHelper.createCustomAnnotation(struct.attribute)); 337 } 338 } 339 } 340 if (invisible.annotations.size() == 0) { 341 invisible = null; 342 } 343 return cv.visitMethod(access, name, desc, exceptions, (attrs != null) ? attrs : invisible); 344 } 345 346 public void visitAttribute(Attribute attrs) { 347 classLevelAnnotationDone = true; 348 RuntimeInvisibleAnnotations invisible = CustomAttributeHelper.linkRuntimeInvisibleAnnotations(attrs); 349 for (Iterator it = m_classAttributes.iterator(); it.hasNext();) { 350 byte[] bytes = (byte[]) it.next(); 351 invisible.annotations.add(CustomAttributeHelper.createCustomAnnotation(bytes)); 352 } 353 if (invisible.annotations.size() == 0) { 354 invisible = null; 355 } 356 super.visitAttribute((attrs != null) ? attrs : invisible); 357 } 358 359 public void visitEnd() { 360 if (!classLevelAnnotationDone) { 361 classLevelAnnotationDone = true; 362 RuntimeInvisibleAnnotations invisible = CustomAttributeHelper.linkRuntimeInvisibleAnnotations(null); 363 for (Iterator it = m_classAttributes.iterator(); it.hasNext();) { 364 byte[] bytes = (byte[]) it.next(); 365 invisible.annotations.add(CustomAttributeHelper.createCustomAnnotation(bytes)); 366 } 367 if (invisible.annotations.size() > 0) { 368 super.visitAttribute(invisible); 369 } 370 super.visitEnd(); 371 } 372 } 373 } 374 375 378 private static class FieldAttributeInfo { 379 public final byte[] attribute; 380 public final JavaField field; 381 382 public FieldAttributeInfo(final JavaField field, final byte[] attribute) { 383 this.field = field; 384 this.attribute = attribute; 385 } 386 } 387 388 391 private static class MethodAttributeInfo { 392 public final byte[] attribute; 393 public final JavaMethod method; 394 395 public MethodAttributeInfo(final JavaMethod method, final byte[] attribute) { 396 this.method = method; 397 this.attribute = attribute; 398 } 399 } 400 } | Popular Tags |