/* *******************************************************************
 * Copyright (c) 2002-2010 Contributors
 * All rights reserved. 
 * This program and the accompanying materials are made available 
 * under the terms of the Eclipse Public License v1.0 
 * which accompanies this distribution and is available at 
 * http://www.eclipse.org/legal/epl-v10.html 
 *  
 * Contributors: 
 *     PARC                 initial implementation 
 *     Andy Clement  6Jul05 generics - signature attribute
 *     Abraham Nevado
 * ******************************************************************/

package org.aspectj.weaver.bcel;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Vector;

import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.Attribute;
import org.aspectj.apache.bcel.classfile.ConstantPool;
import org.aspectj.apache.bcel.classfile.Field;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.apache.bcel.classfile.Method;
import org.aspectj.apache.bcel.classfile.Signature;
import org.aspectj.apache.bcel.classfile.Synthetic;
import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen;
import org.aspectj.apache.bcel.generic.BasicType;
import org.aspectj.apache.bcel.generic.ClassGen;
import org.aspectj.apache.bcel.generic.FieldGen;
import org.aspectj.apache.bcel.generic.InstructionConstants;
import org.aspectj.apache.bcel.generic.InstructionFactory;
import org.aspectj.apache.bcel.generic.InstructionHandle;
import org.aspectj.apache.bcel.generic.InstructionList;
import org.aspectj.apache.bcel.generic.ObjectType;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.SourceLocation;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.AjAttribute.WeaverState;
import org.aspectj.weaver.AjAttribute.WeaverVersionInfo;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.MemberKind;
import org.aspectj.weaver.NameMangler;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.RuntimeVersion;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.SignatureUtils;
import org.aspectj.weaver.TypeVariable;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.UnresolvedType.TypeKind;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.WeaverStateInfo;
import org.aspectj.weaver.World;
import org.aspectj.weaver.bcel.asm.AsmDetector;
import org.aspectj.weaver.bcel.asm.StackMapAdder;

Lazy lazy lazy. We don't unpack the underlying class unless necessary. Things like new methods and annotations accumulate in here until they must be written out, don't add them to the underlying MethodGen! Things are slightly different if this represents an Aspect.
/** * Lazy lazy lazy. We don't unpack the underlying class unless necessary. Things like new methods and annotations accumulate in here * until they must be written out, don't add them to the underlying MethodGen! Things are slightly different if this represents an * Aspect. */
public final class LazyClassGen { private static final Type[] ARRAY_7STRING_INT = new Type[] { Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.INT }; private static final Type[] ARRAY_8STRING_INT = new Type[] { Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.INT }; private static final Type[] PARAMSIGNATURE_MAKESJP_METHOD = new Type[] { Type.STRING, Type.INT, Type.STRING, Type.CLASS, Type.CLASS_ARRAY, Type.STRING_ARRAY, Type.CLASS_ARRAY, Type.CLASS, Type.INT }; private static final Type[] PARAMSIGNATURE_MAKESJP_CONSTRUCTOR = new Type[] { Type.STRING, Type.INT, Type.CLASS, Type.CLASS_ARRAY, Type.STRING_ARRAY, Type.CLASS_ARRAY, Type.INT }; private static final Type[] PARAMSIGNATURE_MAKESJP_CATCHCLAUSE = new Type[] { Type.STRING, Type.CLASS, Type.CLASS, Type.STRING, Type.INT }; private static final Type[] PARAMSIGNATURE_MAKESJP_FIELD = new Type[] { Type.STRING, Type.INT, Type.STRING, Type.CLASS, Type.CLASS, Type.INT }; private static final Type[] PARAMSIGNATURE_MAKESJP_INITIALIZER = new Type[] { Type.STRING, Type.INT, Type.CLASS, Type.INT }; private static final Type[] PARAMSIGNATURE_MAKESJP_MONITOR = new Type[] { Type.STRING, Type.CLASS, Type.INT }; private static final Type[] PARAMSIGNATURE_MAKESJP_ADVICE = new Type[] { Type.STRING, Type.INT, Type.STRING, Type.CLASS, Type.CLASS_ARRAY, Type.STRING_ARRAY, Type.CLASS_ARRAY, Type.CLASS, Type.INT }; private static final int ACC_SYNTHETIC = 0x1000; private static final String[] NO_STRINGS = new String[0]; int highestLineNumber = 0; // ---- JSR 45 info private final SortedMap<String, InlinedSourceFileInfo> inlinedFiles = new TreeMap<String, InlinedSourceFileInfo>(); private boolean regenerateGenericSignatureAttribute = false; private BcelObjectType myType; // XXX is not set for types we create private ClassGen myGen; private final ConstantPool cp; private final World world; private final String packageName = null; private final List<BcelField> fields = new ArrayList<BcelField>(); private final List<LazyMethodGen> methodGens = new ArrayList<LazyMethodGen>(); private final List<LazyClassGen> classGens = new ArrayList<LazyClassGen>(); private final List<AnnotationGen> annotations = new ArrayList<AnnotationGen>(); private int childCounter = 0; private final InstructionFactory fact; private boolean isSerializable = false; private boolean hasSerialVersionUIDField = false; private boolean serialVersionUIDRequiresInitialization = false; private long calculatedSerialVersionUID; private boolean hasClinit = false; private ResolvedType[] extraSuperInterfaces = null; private ResolvedType superclass = null; // --- static class InlinedSourceFileInfo { int highestLineNumber; int offset; // calculated InlinedSourceFileInfo(int highestLineNumber) { this.highestLineNumber = highestLineNumber; } } void addInlinedSourceFileInfo(String fullpath, int highestLineNumber) { Object o = inlinedFiles.get(fullpath); if (o != null) { InlinedSourceFileInfo info = (InlinedSourceFileInfo) o; if (info.highestLineNumber < highestLineNumber) { info.highestLineNumber = highestLineNumber; } } else { inlinedFiles.put(fullpath, new InlinedSourceFileInfo(highestLineNumber)); } } void calculateSourceDebugExtensionOffsets() { int i = roundUpToHundreds(highestLineNumber); for (InlinedSourceFileInfo element : inlinedFiles.values()) { element.offset = i; i = roundUpToHundreds(i + element.highestLineNumber); } } private static int roundUpToHundreds(int i) { return ((i / 100) + 1) * 100; } int getSourceDebugExtensionOffset(String fullpath) { return inlinedFiles.get(fullpath).offset; } // private Unknown getSourceDebugExtensionAttribute() { // int nameIndex = cp.addUtf8("SourceDebugExtension"); // String data = getSourceDebugExtensionString(); // //System.err.println(data); // byte[] bytes = Utility.stringToUTF(data); // int length = bytes.length; // // return new Unknown(nameIndex, length, bytes, cp); // } // private LazyClassGen() {} // public static void main(String[] args) { // LazyClassGen m = new LazyClassGen(); // m.highestLineNumber = 37; // m.inlinedFiles.put("boo/baz/foo.java", new InlinedSourceFileInfo( 83)); // m.inlinedFiles.put("boo/barz/foo.java", new InlinedSourceFileInfo(292)); // m.inlinedFiles.put("boo/baz/moo.java", new InlinedSourceFileInfo(128)); // m.calculateSourceDebugExtensionOffsets(); // System.err.println(m.getSourceDebugExtensionString()); // } // For the entire pathname, we're using package names. This is probably // wrong. // private String getSourceDebugExtensionString() { // StringBuffer out = new StringBuffer(); // String myFileName = getFileName(); // // header section // out.append("SMAP\n"); // out.append(myFileName); // out.append("\nAspectJ\n"); // // stratum section // out.append("*S AspectJ\n"); // // file section // out.append("*F\n"); // out.append("1 "); // out.append(myFileName); // out.append("\n"); // int i = 2; // for (Iterator iter = inlinedFiles.keySet().iterator(); iter.hasNext();) { // String element = (String) iter.next(); // int ii = element.lastIndexOf('/'); // if (ii == -1) { // out.append(i++); out.append(' '); // out.append(element); out.append('\n'); // } else { // out.append("+ "); out.append(i++); out.append(' '); // out.append(element.substring(ii+1)); out.append('\n'); // out.append(element); out.append('\n'); // } // } // // emit line section // out.append("*L\n"); // out.append("1#1,"); // out.append(highestLineNumber); // out.append(":1,1\n"); // i = 2; // for (Iterator iter = inlinedFiles.values().iterator(); iter.hasNext();) { // InlinedSourceFileInfo element = (InlinedSourceFileInfo) iter.next(); // out.append("1#"); // out.append(i++); out.append(','); // out.append(element.highestLineNumber); out.append(":"); // out.append(element.offset + 1); out.append(",1\n"); // } // // end section // out.append("*E\n"); // // and finish up... // return out.toString(); // } // ---- end JSR45-related stuff
Emit disassembled class and newline to out
/** Emit disassembled class and newline to out */
public static void disassemble(String path, String name, PrintStream out) throws IOException { if (null == out) { return; } // out.println("classPath: " + classPath); BcelWorld world = new BcelWorld(path); UnresolvedType ut = UnresolvedType.forName(name); ut.setNeedsModifiableDelegate(true); LazyClassGen clazz = new LazyClassGen(BcelWorld.getBcelObjectType(world.resolve(ut))); clazz.print(out); out.println(); } public String getNewGeneratedNameTag() { return new Integer(childCounter++).toString(); } // ---- public LazyClassGen(String class_name, String super_class_name, String file_name, int access_flags, String[] interfaces, World world) { myGen = new ClassGen(class_name, super_class_name, file_name, access_flags, interfaces); cp = myGen.getConstantPool(); fact = new InstructionFactory(myGen, cp); regenerateGenericSignatureAttribute = true; this.world = world; } public void setMajorMinor(int major, int minor) { myGen.setMajor(major); myGen.setMinor(minor); } public int getMajor() { return myGen.getMajor(); } public int getMinor() { return myGen.getMinor(); } // Non child type, so it comes from a real type in the world. public LazyClassGen(BcelObjectType myType) { myGen = new ClassGen(myType.getJavaClass()); cp = myGen.getConstantPool(); fact = new InstructionFactory(myGen, cp); this.myType = myType; world = myType.getResolvedTypeX().getWorld(); /* Does this class support serialization */ if (implementsSerializable(getType())) { isSerializable = true; // ResolvedMember[] fields = getType().getDeclaredFields(); // for (int i = 0; i < fields.length; i++) { // ResolvedMember field = fields[i]; // if (field.getName().equals("serialVersionUID") // && field.isStatic() && field.getType().equals(UnresolvedType.LONG)) // { // hasSerialVersionUIDField = true; // } // } hasSerialVersionUIDField = hasSerialVersionUIDField(getType()); ResolvedMember[] methods = getType().getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { ResolvedMember method = methods[i]; if (method.getName().equals("<clinit>")) { if (method.getKind() != Member.STATIC_INITIALIZATION) { throw new RuntimeException("qui?"); } hasClinit = true; } } // Do we need to calculate an SUID and add it? if (!getType().isInterface() && !hasSerialVersionUIDField && world.isAddSerialVerUID()) { calculatedSerialVersionUID = myGen.getSUID(); FieldGen fg = new FieldGen(Constants.ACC_PRIVATE | Constants.ACC_FINAL | Constants.ACC_STATIC, BasicType.LONG, "serialVersionUID", getConstantPool()); addField(fg); hasSerialVersionUIDField = true; serialVersionUIDRequiresInitialization = true; // warn about what we've done? if (world.getLint().calculatingSerialVersionUID.isEnabled()) { world.getLint().calculatingSerialVersionUID.signal( new String[] { getClassName(), Long.toString(calculatedSerialVersionUID) + "L" }, null, null); } } } ResolvedMember[] methods = myType.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { addMethodGen(new LazyMethodGen((BcelMethod) methods[i], this)); } // Method[] methods = myGen.getMethods(); // for (int i = 0; i < methods.length; i++) { // addMethodGen(new LazyMethodGen(methods[i], this)); // } ResolvedMember[] fields = myType.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { this.fields.add((BcelField) fields[i]); } } public static boolean hasSerialVersionUIDField(ResolvedType type) { ResolvedMember[] fields = type.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { ResolvedMember field = fields[i]; if (field.getName().equals("serialVersionUID") && Modifier.isStatic(field.getModifiers()) && field.getType().equals(UnresolvedType.LONG)) { return true; } } return false; } // public void addAttribute(Attribute i) { // myGen.addAttribute(i); // } // ---- public String getInternalClassName() { return getConstantPool().getConstantString_CONSTANTClass(myGen.getClassNameIndex()); // getConstantPool().getConstantString( // myGen.getClassNameIndex(), // Constants.CONSTANT_Class); } public String getInternalFileName() { String str = getInternalClassName(); int index = str.lastIndexOf('/'); if (index == -1) { return getFileName(); } else { return str.substring(0, index + 1) + getFileName(); } }
Returns the packagename - if its the default package we return an empty string
/** * Returns the packagename - if its the default package we return an empty string */
public String getPackageName() { if (packageName != null) { return packageName; } String str = getInternalClassName(); int index = str.indexOf("<"); if (index != -1) { str = str.substring(0, index); // strip off the generics guff } index = str.lastIndexOf("/"); if (index == -1) { return ""; } return str.substring(0, index).replace('/', '.'); } public void addMethodGen(LazyMethodGen gen) { // assert gen.getClassName() == super.getClassName(); methodGens.add(gen); if (highestLineNumber < gen.highestLineNumber) { highestLineNumber = gen.highestLineNumber; } } public boolean removeMethodGen(LazyMethodGen gen) { return methodGens.remove(gen); } public void addMethodGen(LazyMethodGen gen, ISourceLocation sourceLocation) { addMethodGen(gen); if (!gen.getMethod().isPrivate()) { warnOnAddedMethod(gen.getMethod(), sourceLocation); } } public void errorOnAddedField(FieldGen field, ISourceLocation sourceLocation) { if (isSerializable && !hasSerialVersionUIDField) { getWorld().getLint().serialVersionUIDBroken.signal( new String[] { myType.getResolvedTypeX().getName(), field.getName() }, sourceLocation, null); } } public void warnOnAddedInterface(String name, ISourceLocation sourceLocation) { warnOnModifiedSerialVersionUID(sourceLocation, "added interface " + name); } public void warnOnAddedMethod(Method method, ISourceLocation sourceLocation) { warnOnModifiedSerialVersionUID(sourceLocation, "added non-private method " + method.getName()); } public void warnOnAddedStaticInitializer(Shadow shadow, ISourceLocation sourceLocation) { if (!hasClinit) { warnOnModifiedSerialVersionUID(sourceLocation, "added static initializer"); } } public void warnOnModifiedSerialVersionUID(ISourceLocation sourceLocation, String reason) { if (isSerializable && !hasSerialVersionUIDField) { getWorld().getLint().needsSerialVersionUIDField.signal(new String[] { myType.getResolvedTypeX().getName().toString(), reason }, sourceLocation, null); } } public World getWorld() { return world; } public List<LazyMethodGen> getMethodGens() { return methodGens; // ???Collections.unmodifiableList(methodGens); } public List<BcelField> getFieldGens() { return fields; } public boolean fieldExists(String name) { // Field[] allFields = myGen.getFields(); // if (allFields!=null) { // for (int i=0;i<allFields.length;i++) { // Field f = allFields[i]; // if (f.getName().equals(name)) { // return f; // } // } // } for (BcelField f: fields) { if (f.getName().equals(name)) { return true; } } return false; } private void writeBack(BcelWorld world) { if (getConstantPool().getSize() > Short.MAX_VALUE) { reportClassTooBigProblem(); return; } if (annotations.size() > 0) { for (AnnotationGen element : annotations) { myGen.addAnnotation(element); } // Attribute[] annAttributes = // org.aspectj.apache.bcel.classfile.Utility.getAnnotationAttributes( // getConstantPool(),annotations); // for (int i = 0; i < annAttributes.length; i++) { // Attribute attribute = annAttributes[i]; // System.err.println("Adding attribute for "+attribute); // myGen.addAttribute(attribute); // } } // Add a weaver version attribute to the file being produced (if // necessary...) if (!myGen.hasAttribute("org.aspectj.weaver.WeaverVersion")) { myGen.addAttribute(Utility.bcelAttribute(new AjAttribute.WeaverVersionInfo(), getConstantPool())); } // see 389678: TODO more finessing possible here? if (world.isOverWeaving()) { if (myGen.hasAttribute(WeaverState.AttributeName) && myType!=null && myType.getWeaverState() != null) { myGen.removeAttribute(myGen.getAttribute(WeaverState.AttributeName)); myGen.addAttribute(Utility.bcelAttribute(new AjAttribute.WeaverState(myType.getWeaverState()), getConstantPool())); } } else { if (!myGen.hasAttribute(WeaverState.AttributeName) && myType != null && myType.getWeaverState() != null) { myGen.addAttribute(Utility.bcelAttribute(new AjAttribute.WeaverState(myType.getWeaverState()), getConstantPool())); } } // FIXME ATAJ needed only for slow Aspects.aspectOf() - keep or remove // make a lot of test fail since the test compare weaved class file // based on some test data as text files... // if (!myGen.isInterface()) { // addAjClassField(); // } addAjcInitializers(); // 17Feb05 - ASC - Skip this for now - it crashes IBM 1.4.2 jvms // (pr80430). Will be revisited when contents // of attribute are confirmed to be correct. boolean sourceDebugExtensionSupportSwitchedOn = false; if (sourceDebugExtensionSupportSwitchedOn) { calculateSourceDebugExtensionOffsets(); } int len = methodGens.size(); myGen.setMethods(Method.NoMethods); for (LazyMethodGen gen : methodGens) { // we skip empty clinits if (isEmptyClinit(gen)) { continue; } myGen.addMethod(gen.getMethod()); } len = fields.size(); myGen.setFields(Field.NoFields); for (int i = 0; i < len; i++) { BcelField gen = fields.get(i); myGen.addField(gen.getField(cp)); } if (sourceDebugExtensionSupportSwitchedOn) { if (inlinedFiles.size() != 0) { if (hasSourceDebugExtensionAttribute(myGen)) { world.showMessage(IMessage.WARNING, WeaverMessages.format(WeaverMessages.OVERWRITE_JSR45, getFileName()), null, null); } // myGen.addAttribute(getSourceDebugExtensionAttribute()); } } fixupGenericSignatureAttribute(); }
When working with Java generics, a signature attribute is attached to the type which indicates how it was declared. This routine ensures the signature attribute for the class we are about to write out is correct. Basically its responsibilities are:
  1. Checking whether the attribute needs changing (ie. did weaving change the type hierarchy) - if it did, remove the old attribute
  2. Check if we need an attribute at all, are we generic? are our supertypes parameterized/generic?
  3. Build the new attribute which includes all typevariable, supertype and superinterface information
/** * When working with Java generics, a signature attribute is attached to the type which indicates how it was declared. This * routine ensures the signature attribute for the class we are about to write out is correct. Basically its responsibilities * are: * <ol> * <li> * Checking whether the attribute needs changing (ie. did weaving change the type hierarchy) - if it did, remove the old * attribute * <li> * Check if we need an attribute at all, are we generic? are our supertypes parameterized/generic? * <li> * Build the new attribute which includes all typevariable, supertype and superinterface information * </ol> */
private void fixupGenericSignatureAttribute() { if (getWorld() != null && !getWorld().isInJava5Mode()) { return; } // TODO asc generics Temporarily assume that types we generate dont need // a signature attribute (closure/etc).. will need // revisiting no doubt... // if (myType == null) { // return; // } // 1. Has anything changed that would require us to modify this // attribute? if (!regenerateGenericSignatureAttribute) { return; } // 2. Find the old attribute Signature sigAttr = null; if (myType != null) { // if null, this is a type built from scratch, it // won't already have a sig attribute sigAttr = (Signature) myGen.getAttribute("Signature"); } // 3. Do we need an attribute? boolean needAttribute = false; // If we had one before, we definetly still need one as types can't be // 'removed' from the hierarchy if (sigAttr != null) { needAttribute = true; } // check the interfaces if (!needAttribute) { if (myType != null) { ResolvedType[] interfaceRTXs = myType.getDeclaredInterfaces(); for (int i = 0; i < interfaceRTXs.length; i++) { ResolvedType typeX = interfaceRTXs[i]; if (typeX.isGenericType() || typeX.isParameterizedType()) { needAttribute = true; } } if (extraSuperInterfaces != null) { for (int i = 0; i < extraSuperInterfaces.length; i++) { ResolvedType interfaceType = extraSuperInterfaces[i]; if (interfaceType.isGenericType() || interfaceType.isParameterizedType()) { needAttribute = true; } } } } if (myType == null) { ResolvedType superclassRTX = superclass; if (superclassRTX != null) { if (superclassRTX.isGenericType() || superclassRTX.isParameterizedType()) { needAttribute = true; } } } else { // check the supertype ResolvedType superclassRTX = getSuperClass(); if (superclassRTX.isGenericType() || superclassRTX.isParameterizedType()) { needAttribute = true; } } } if (needAttribute) { StringBuffer signature = new StringBuffer(); // first, the type variables... if (myType != null) { TypeVariable[] tVars = myType.getTypeVariables(); if (tVars.length > 0) { signature.append("<"); for (int i = 0; i < tVars.length; i++) { TypeVariable variable = tVars[i]; signature.append(variable.getSignatureForAttribute()); } signature.append(">"); } } // now the supertype String supersig = getSuperClass().getSignatureForAttribute(); signature.append(supersig); if (myType != null) { ResolvedType[] interfaceRTXs = myType.getDeclaredInterfaces(); for (int i = 0; i < interfaceRTXs.length; i++) { String s = interfaceRTXs[i].getSignatureForAttribute(); signature.append(s); } if (extraSuperInterfaces != null) { for (int i = 0; i < extraSuperInterfaces.length; i++) { String s = extraSuperInterfaces[i].getSignatureForAttribute(); signature.append(s); } } } if (sigAttr != null) { myGen.removeAttribute(sigAttr); } myGen.addAttribute(createSignatureAttribute(signature.toString())); } }
Helper method to create a signature attribute based on a string signature: e.g. "Ljava/lang/Object;LI;"
/** * Helper method to create a signature attribute based on a string signature: e.g. "Ljava/lang/Object;LI<Ljava/lang/Double;>;" */
private Signature createSignatureAttribute(String signature) { int nameIndex = cp.addUtf8("Signature"); int sigIndex = cp.addUtf8(signature); return new Signature(nameIndex, 2, sigIndex, cp); } /** * */ private void reportClassTooBigProblem() { // PR 59208 // we've generated a class that is just toooooooooo big (you've been // generating programs // again haven't you? come on, admit it, no-one writes classes this big // by hand). // create an empty myGen so that we can give back a return value that // doesn't upset the // rest of the process. myGen = new ClassGen(myGen.getClassName(), myGen.getSuperclassName(), myGen.getFileName(), myGen.getModifiers(), myGen.getInterfaceNames()); // raise an error against this compilation unit. getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CLASS_TOO_BIG, this.getClassName()), new SourceLocation(new File(myGen.getFileName()), 0), null); } private static boolean hasSourceDebugExtensionAttribute(ClassGen gen) { return gen.hasAttribute("SourceDebugExtension"); } public JavaClass getJavaClass(BcelWorld world) { writeBack(world); return myGen.getJavaClass(); } public byte[] getJavaClassBytesIncludingReweavable(BcelWorld world) { writeBack(world); byte[] wovenClassFileData = myGen.getJavaClass().getBytes(); // At 1.6 stackmaps are optional, whilst at 1.7 and later they // are required (unless turning off the verifier) if ((myGen.getMajor() == Constants.MAJOR_1_6 && world.shouldGenerateStackMaps()) || myGen.getMajor() > Constants.MAJOR_1_6) { if (!AsmDetector.isAsmAround) { throw new BCException("Unable to find Asm for stackmap generation (Looking for 'aj.org.objectweb.asm.ClassReader'). Stackmap generation for woven code is required to avoid verify errors on a Java 1.7 or higher runtime"); }; wovenClassFileData = StackMapAdder.addStackMaps(world, wovenClassFileData); } WeaverStateInfo wsi = myType.getWeaverState();// getOrCreateWeaverStateInfo(); if (wsi != null && wsi.isReweavable() && !world.isOverWeaving()) { // && !reweavableDataInserted // reweavableDataInserted = true; return wsi.replaceKeyWithDiff(wovenClassFileData); } else { return wovenClassFileData; } } public void addGeneratedInner(LazyClassGen newClass) { classGens.add(newClass); } public void addInterface(ResolvedType newInterface, ISourceLocation sourceLocation) { regenerateGenericSignatureAttribute = true; if (extraSuperInterfaces == null) { extraSuperInterfaces = new ResolvedType[1]; extraSuperInterfaces[0] = newInterface; } else { ResolvedType[] x = new ResolvedType[extraSuperInterfaces.length + 1]; System.arraycopy(extraSuperInterfaces, 0, x, 1, extraSuperInterfaces.length); x[0] = newInterface; extraSuperInterfaces = x; } myGen.addInterface(newInterface.getRawName()); if (!newInterface.equals(UnresolvedType.SERIALIZABLE)) { warnOnAddedInterface(newInterface.getName(), sourceLocation); } } public void setSuperClass(ResolvedType newSuperclass) { regenerateGenericSignatureAttribute = true; superclass = newSuperclass; // myType.addParent(typeX); // used for the attribute if (newSuperclass.getGenericType() != null) { newSuperclass = newSuperclass.getGenericType(); } myGen.setSuperclassName(newSuperclass.getName()); // used in the real // class data } // public String getSuperClassname() { // return myGen.getSuperclassName(); // } public ResolvedType getSuperClass() { if (superclass != null) { return superclass; } return myType.getSuperclass(); } public String[] getInterfaceNames() { return myGen.getInterfaceNames(); } // non-recursive, may be a bug, ha ha. private List<LazyClassGen> getClassGens() { List<LazyClassGen> ret = new ArrayList<LazyClassGen>(); ret.add(this); ret.addAll(classGens); return ret; } public List<UnwovenClassFile.ChildClass> getChildClasses(BcelWorld world) { if (classGens.isEmpty()) { return Collections.emptyList(); } List<UnwovenClassFile.ChildClass> ret = new ArrayList<UnwovenClassFile.ChildClass>(); for (LazyClassGen clazz : classGens) { byte[] bytes = clazz.getJavaClass(world).getBytes(); String name = clazz.getName(); int index = name.lastIndexOf('$'); // XXX this could be bad, check use of dollar signs. name = name.substring(index + 1); ret.add(new UnwovenClassFile.ChildClass(name, bytes)); } return ret; } @Override public String toString() { return toShortString(); } public String toShortString() { String s = org.aspectj.apache.bcel.classfile.Utility.accessToString(myGen.getModifiers(), true); if (!s.equals("")) { s += " "; } s += org.aspectj.apache.bcel.classfile.Utility.classOrInterface(myGen.getModifiers()); s += " "; s += myGen.getClassName(); return s; } public String toLongString() { ByteArrayOutputStream s = new ByteArrayOutputStream(); print(new PrintStream(s)); return new String(s.toByteArray()); } public void print() { print(System.out); } public void print(PrintStream out) { List<LazyClassGen> classGens = getClassGens(); for (Iterator<LazyClassGen> iter = classGens.iterator(); iter.hasNext();) { LazyClassGen element = iter.next(); element.printOne(out); if (iter.hasNext()) { out.println(); } } } private void printOne(PrintStream out) { out.print(toShortString()); out.print(" extends "); out.print(org.aspectj.apache.bcel.classfile.Utility.compactClassName(myGen.getSuperclassName(), false)); int size = myGen.getInterfaces().length; if (size > 0) { out.print(" implements "); for (int i = 0; i < size; i++) { out.print(myGen.getInterfaceNames()[i]); if (i < size - 1) { out.print(", "); } } } out.print(":"); out.println(); // XXX make sure to pass types correctly around, so this doesn't happen. if (myType != null) { myType.printWackyStuff(out); } Field[] fields = myGen.getFields(); for (int i = 0, len = fields.length; i < len; i++) { out.print(" "); out.println(fields[i]); } List<LazyMethodGen> methodGens = getMethodGens(); for (Iterator<LazyMethodGen> iter = methodGens.iterator(); iter.hasNext();) { LazyMethodGen gen = iter.next(); // we skip empty clinits if (isEmptyClinit(gen)) { continue; } gen.print(out, (myType != null ? myType.getWeaverVersionAttribute() : WeaverVersionInfo.UNKNOWN)); if (iter.hasNext()) { out.println(); } } // out.println(" ATTRIBS: " + Arrays.asList(myGen.getAttributes())); out.println("end " + toShortString()); } private boolean isEmptyClinit(LazyMethodGen gen) { if (!gen.getName().equals("<clinit>")) { return false; } // System.err.println("checking clinig: " + gen); InstructionHandle start = gen.getBody().getStart(); while (start != null) { if (Range.isRangeHandle(start) || (start.getInstruction().opcode == Constants.RETURN)) { start = start.getNext(); } else { return false; } } return true; } public ConstantPool getConstantPool() { return cp; } public String getName() { return myGen.getClassName(); } public boolean isWoven() { return myType.getWeaverState() != null; } public boolean isReweavable() { if (myType.getWeaverState() == null) { return true; } return myType.getWeaverState().isReweavable(); } public Set<String> getAspectsAffectingType() { if (myType.getWeaverState() == null) { return null; } return myType.getWeaverState().getAspectsAffectingType(); } public WeaverStateInfo getOrCreateWeaverStateInfo(boolean inReweavableMode) { WeaverStateInfo ret = myType.getWeaverState(); if (ret != null) { return ret; } ret = new WeaverStateInfo(inReweavableMode); myType.setWeaverState(ret); return ret; } public InstructionFactory getFactory() { return fact; } public LazyMethodGen getStaticInitializer() { for (LazyMethodGen gen : methodGens) { // OPTIMIZE persist kind of member into the gen object? for clinit if (gen.getName().equals("<clinit>")) { return gen; } } LazyMethodGen clinit = new LazyMethodGen(Modifier.STATIC, Type.VOID, "<clinit>", new Type[0], NO_STRINGS, this); clinit.getBody().insert(InstructionConstants.RETURN); methodGens.add(clinit); return clinit; }
Retrieve the ajc$preClinit method - this method captures any initialization AspectJ wants to ensure happens in a class. It is called from the static initializer. Maintaining this separation enables overweaving to ignore join points added due to earlier weaves. If the ajc$preClinit method cannot be found, it is created and a call to it is placed in the real static initializer (the call is placed at the start of the static initializer).
Returns:the LazyMethodGen representing the ajc$ clinit
/** * Retrieve the ajc$preClinit method - this method captures any initialization AspectJ wants to ensure happens in a class. It is * called from the static initializer. Maintaining this separation enables overweaving to ignore join points added due to * earlier weaves. If the ajc$preClinit method cannot be found, it is created and a call to it is placed in the real static * initializer (the call is placed at the start of the static initializer). * * @return the LazyMethodGen representing the ajc$ clinit */
public LazyMethodGen getAjcPreClinit() { if (this.isInterface()) { throw new IllegalStateException(); } for (LazyMethodGen methodGen : methodGens) { if (methodGen.getName().equals(NameMangler.AJC_PRE_CLINIT_NAME)) { return methodGen; } } LazyMethodGen ajcPreClinit = new LazyMethodGen(Modifier.PRIVATE | Modifier.STATIC, Type.VOID, NameMangler.AJC_PRE_CLINIT_NAME, Type.NO_ARGS, NO_STRINGS, this); ajcPreClinit.getBody().insert(InstructionConstants.RETURN); methodGens.add(ajcPreClinit); getStaticInitializer().getBody().insert(Utility.createInvoke(fact, ajcPreClinit)); return ajcPreClinit; }
factory method for building multiple extended clinit methods. Constructs a new clinit method that invokes the previous one and then returns it. The index is used as a name suffix.
Params:
  • previousPreClinit –
  • i –
/** * factory method for building multiple extended clinit methods. Constructs a new clinit method that invokes the previous one * and then returns it. The index is used as a name suffix. * * @param previousPreClinit * @param i */
public LazyMethodGen createExtendedAjcPreClinit(LazyMethodGen previousPreClinit, int i) { LazyMethodGen ajcPreClinit = new LazyMethodGen(Modifier.PRIVATE | Modifier.STATIC, Type.VOID, NameMangler.AJC_PRE_CLINIT_NAME + i, Type.NO_ARGS, NO_STRINGS, this); ajcPreClinit.getBody().insert(InstructionConstants.RETURN); methodGens.add(ajcPreClinit); previousPreClinit.getBody().insert(Utility.createInvoke(fact, ajcPreClinit)); return ajcPreClinit; } // // reflective thisJoinPoint support private Map<BcelShadow, Field> tjpFields = new HashMap<BcelShadow, Field>(); Map<CacheKey, Field> annotationCachingFieldCache = new HashMap<CacheKey, Field>(); private int tjpFieldsCounter = -1; // -1 means not yet initialized private int annoFieldsCounter = 0; public static final ObjectType proceedingTjpType = new ObjectType("org.aspectj.lang.ProceedingJoinPoint"); public static final ObjectType tjpType = new ObjectType("org.aspectj.lang.JoinPoint"); public static final ObjectType staticTjpType = new ObjectType("org.aspectj.lang.JoinPoint$StaticPart"); public static final ObjectType typeForAnnotation = new ObjectType("java.lang.annotation.Annotation"); public static final ObjectType enclosingStaticTjpType = new ObjectType("org.aspectj.lang.JoinPoint$EnclosingStaticPart"); private static final ObjectType sigType = new ObjectType("org.aspectj.lang.Signature"); // private static final ObjectType slType = // new ObjectType("org.aspectj.lang.reflect.SourceLocation"); private static final ObjectType factoryType = new ObjectType("org.aspectj.runtime.reflect.Factory"); private static final ObjectType classType = new ObjectType("java.lang.Class"); public Field getTjpField(BcelShadow shadow, final boolean isEnclosingJp) { Field tjpField = tjpFields.get(shadow); if (tjpField != null) { return tjpField; } int modifiers = Modifier.STATIC; // J9: Can't always be final on Java 9 because it is set outside of clinit // But must be final in interface if (shadow.getEnclosingClass().isInterface()) { modifiers |= Modifier.FINAL; } // XXX - Do we ever inline before or after advice? If we do, then we // better include them in the check below. (or just change it to // shadow.getEnclosingMethod().getCanInline()) // If the enclosing method is around advice, we could inline the join // point that has led to this shadow. If we do that then the TJP we are // creating here must be PUBLIC so it is visible to the type in which the // advice is inlined. (PR71377) LazyMethodGen encMethod = shadow.getEnclosingMethod(); boolean shadowIsInAroundAdvice = false; if (encMethod != null && encMethod.getName().startsWith(NameMangler.PREFIX + "around")) { shadowIsInAroundAdvice = true; } if (getType().isInterface() || shadowIsInAroundAdvice) { modifiers |= Modifier.PUBLIC; } else { modifiers |= Modifier.PRIVATE; } ObjectType jpType = null; // Did not have different static joinpoint types in 1.2 if (world.isTargettingAspectJRuntime12()) { jpType = staticTjpType; } else { jpType = isEnclosingJp ? enclosingStaticTjpType : staticTjpType; } if (tjpFieldsCounter == -1) { // not yet initialized, do it now if (!world.isOverWeaving()) { tjpFieldsCounter = 0; } else { List<BcelField> existingFields = getFieldGens(); if (existingFields == null) { tjpFieldsCounter = 0; } else { BcelField lastField = null; // OPTIMIZE: go from last to first? for (BcelField field : existingFields) { if (field.getName().startsWith("ajc$tjp_")) { lastField = field; } } if (lastField == null) { tjpFieldsCounter = 0; } else { tjpFieldsCounter = Integer.parseInt(lastField.getName().substring(8)) + 1; // System.out.println("tjp counter starting at " + tjpFieldsCounter); } } } } if (!isInterface() && world.isTransientTjpFields()) { modifiers|=Modifier.TRANSIENT; } FieldGen fGen = new FieldGen(modifiers, jpType, "ajc$tjp_" + tjpFieldsCounter++, getConstantPool()); addField(fGen); tjpField = fGen.getField(); tjpFields.put(shadow, tjpField); return tjpField; }
Create a field in the type containing the shadow where the annotation retrieved during binding can be stored - for later fast access.
Params:
  • shadow – the shadow at which the @annotation result is being cached
Returns:a field
/** * Create a field in the type containing the shadow where the annotation retrieved during binding can be stored - for later fast * access. * * @param shadow the shadow at which the @annotation result is being cached * @return a field */
public Field getAnnotationCachingField(BcelShadow shadow, ResolvedType toType, boolean isWithin) { // Multiple annotation types at a shadow. A different field would be required for each CacheKey cacheKey = new CacheKey(shadow, toType, isWithin); Field field = annotationCachingFieldCache.get(cacheKey); // System.out.println(field + " for shadow " + shadow); if (field == null) { // private static Annotation ajc$anno$<nnn> StringBuilder sb = new StringBuilder(); sb.append(NameMangler.ANNOTATION_CACHE_FIELD_NAME); sb.append(annoFieldsCounter++); FieldGen annotationCacheField = new FieldGen(Modifier.PRIVATE | Modifier.STATIC, typeForAnnotation, sb.toString(), cp); addField(annotationCacheField); field = annotationCacheField.getField(); annotationCachingFieldCache.put(cacheKey, field); } return field; } static class CacheKey { private Object key; private ResolvedType annotationType; // If the annotation is being accessed via @annotation on a shadow then we can use the shadows toString() (so two shadows // the same share a variable), but if it is @withincode() or @within() we can't share them (as the shadows may look the same // but be occurring 'within' different things). In the within cases we continue to use the shadow itself as the key. CacheKey(BcelShadow shadow, ResolvedType annotationType, boolean isWithin) { this.key = isWithin ? shadow : shadow.toString(); this.annotationType = annotationType; } @Override public int hashCode() { return key.hashCode() * 37 + annotationType.hashCode(); } @Override public boolean equals(Object other) { if (!(other instanceof CacheKey)) { return false; } CacheKey oCacheKey = (CacheKey) other; return key.equals(oCacheKey.key) && annotationType.equals(oCacheKey.annotationType); } } // FIXME ATAJ needed only for slow Aspects.aspectOf - keep or remove // private void addAjClassField() { // // Andy: Why build it again?? // Field ajClassField = new FieldGen( // Modifier.PRIVATE | Modifier.FINAL | Modifier.STATIC, // classType, // "aj$class", // getConstantPool()).getField(); // addField(ajClassField); // // InstructionList il = new InstructionList(); // il.append(new PUSH(getConstantPool(), getClassName())); // il.append(fact.createInvoke("java.lang.Class", "forName", classType, // new Type[] {Type.STRING}, Constants.INVOKESTATIC)); // il.append(fact.createFieldAccess(getClassName(), ajClassField.getName(), // classType, Constants.PUTSTATIC)); // // getStaticInitializer().getBody().insert(il); // } private void addAjcInitializers() { if (tjpFields.size() == 0 && !serialVersionUIDRequiresInitialization) { return; } InstructionList[] il = null; if (tjpFields.size() > 0) { il = initializeAllTjps(); } if (serialVersionUIDRequiresInitialization) { InstructionList[] ilSVUID = new InstructionList[1]; ilSVUID[0] = new InstructionList(); ilSVUID[0].append(InstructionFactory.PUSH(getConstantPool(), calculatedSerialVersionUID)); ilSVUID[0].append(getFactory().createFieldAccess(getClassName(), "serialVersionUID", BasicType.LONG, Constants.PUTSTATIC)); if (il == null) { il = ilSVUID; } else { InstructionList[] newIl = new InstructionList[il.length + ilSVUID.length]; System.arraycopy(il, 0, newIl, 0, il.length); System.arraycopy(ilSVUID, 0, newIl, il.length, ilSVUID.length); il = newIl; } } LazyMethodGen prevMethod; LazyMethodGen nextMethod = null; if (this.isInterface()) { // Cannot sneak stuff into another static method in an interface prevMethod = getStaticInitializer(); } else { prevMethod = getAjcPreClinit(); } for (int counter = 1; counter <= il.length; counter++) { if (il.length > counter) { nextMethod = createExtendedAjcPreClinit(prevMethod, counter); } prevMethod.getBody().insert(il[counter - 1]); prevMethod = nextMethod; } } private InstructionList initInstructionList() { InstructionList list = new InstructionList(); InstructionFactory fact = getFactory(); // make a new factory list.append(fact.createNew(factoryType)); list.append(InstructionFactory.createDup(1)); list.append(InstructionFactory.PUSH(getConstantPool(), getFileName())); // load the current Class object // XXX check that this works correctly for inners/anonymous list.append(fact.PUSHCLASS(cp, myGen.getClassName())); // XXX do we need to worry about the fact the theorectically this could // throw // a ClassNotFoundException list.append(fact.createInvoke(factoryType.getClassName(), "<init>", Type.VOID, new Type[] { Type.STRING, classType }, Constants.INVOKESPECIAL)); list.append(InstructionFactory.createStore(factoryType, 0)); return list; } private InstructionList[] initializeAllTjps() { Vector<InstructionList> lists = new Vector<InstructionList>(); InstructionList list = initInstructionList(); lists.add(list); List<Map.Entry<BcelShadow, Field>> entries = new ArrayList<Map.Entry<BcelShadow, Field>>(tjpFields.entrySet()); Collections.sort(entries, new Comparator<Map.Entry<BcelShadow, Field>>() { @Override public int compare(Map.Entry<BcelShadow, Field> a, Map.Entry<BcelShadow, Field> b) { return (a.getValue()).getName().compareTo((b.getValue()).getName()); } }); long estimatedSize = 0; for (Iterator<Map.Entry<BcelShadow, Field>> i = entries.iterator(); i.hasNext();) { Map.Entry<BcelShadow, Field> entry = i.next(); if (estimatedSize > Constants.MAX_CODE_SIZE) { estimatedSize = 0; list = initInstructionList(); lists.add(list); } estimatedSize += entry.getValue().getSignature().getBytes().length; initializeTjp(fact, list, entry.getValue(), entry.getKey()); } InstructionList listArrayModel[] = new InstructionList[1]; return lists.toArray(listArrayModel); } private void initializeTjp(InstructionFactory fact, InstructionList list, Field field, BcelShadow shadow) { if (world.getTargetAspectjRuntimeLevel() == RuntimeVersion.V1_9) { initializeTjpOptimal(fact, list, field, shadow); return; } boolean fastSJP = false; // avoid fast SJP if it is for an enclosing joinpoint boolean isFastSJPAvailable = shadow.getWorld().isTargettingRuntime1_6_10() && !enclosingStaticTjpType.equals(field.getType()); Member sig = shadow.getSignature(); // load the factory list.append(InstructionFactory.createLoad(factoryType, 0)); // load the kind list.append(InstructionFactory.PUSH(getConstantPool(), shadow.getKind().getName())); // create the signature if (world.isTargettingAspectJRuntime12() || !isFastSJPAvailable || !sig.getKind().equals(Member.METHOD)) { list.append(InstructionFactory.createLoad(factoryType, 0)); } String signatureMakerName = SignatureUtils.getSignatureMakerName(sig); ObjectType signatureType = new ObjectType(SignatureUtils.getSignatureType(sig)); UnresolvedType[] exceptionTypes = null; if (world.isTargettingAspectJRuntime12()) { // TAG:SUPPORTING12: We didn't have optimized factory methods in 1.2 list.append(InstructionFactory.PUSH(cp, SignatureUtils.getSignatureString(sig, shadow.getWorld()))); list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY1, Constants.INVOKEVIRTUAL)); } else if (sig.getKind().equals(Member.METHOD)) { BcelWorld w = shadow.getWorld(); // For methods, push the parts of the signature on. list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); list.append(InstructionFactory.PUSH(cp, sig.getName())); list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterNames(w)))); exceptionTypes = sig.getExceptions(w); if (isFastSJPAvailable && exceptionTypes.length == 0) { fastSJP = true; } else { list.append(InstructionFactory.PUSH(cp, makeString(exceptionTypes))); } list.append(InstructionFactory.PUSH(cp, makeString(sig.getReturnType()))); // And generate a call to the variant of makeMethodSig() that takes the strings if (isFastSJPAvailable) { fastSJP = true; } else { list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY7, Constants.INVOKEVIRTUAL)); } } else if (sig.getKind().equals(Member.MONITORENTER)) { list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY1, Constants.INVOKEVIRTUAL)); } else if (sig.getKind().equals(Member.MONITOREXIT)) { list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY1, Constants.INVOKEVIRTUAL)); } else if (sig.getKind().equals(Member.HANDLER)) { BcelWorld w = shadow.getWorld(); list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterNames(w)))); list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY3, Constants.INVOKEVIRTUAL)); } else if (sig.getKind().equals(Member.CONSTRUCTOR)) { BcelWorld w = shadow.getWorld(); if (w.isJoinpointArrayConstructionEnabled() && sig.getDeclaringType().isArray()) { // its the magical new jp list.append(InstructionFactory.PUSH(cp, makeString(Modifier.PUBLIC))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); list.append(InstructionFactory.PUSH(cp, "")); // sig.getParameterNames? list.append(InstructionFactory.PUSH(cp, ""));// sig.getExceptions? list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY5, Constants.INVOKEVIRTUAL)); } else { list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterNames(w)))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getExceptions(w)))); list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY5, Constants.INVOKEVIRTUAL)); } } else if (sig.getKind().equals(Member.FIELD)) { BcelWorld w = shadow.getWorld(); list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); list.append(InstructionFactory.PUSH(cp, sig.getName())); // see pr227401 UnresolvedType dType = sig.getDeclaringType(); if (dType.getTypekind() == TypeKind.PARAMETERIZED || dType.getTypekind() == TypeKind.GENERIC) { dType = sig.getDeclaringType().resolve(world).getGenericType(); } list.append(InstructionFactory.PUSH(cp, makeString(dType))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getReturnType()))); list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY4, Constants.INVOKEVIRTUAL)); } else if (sig.getKind().equals(Member.ADVICE)) { BcelWorld w = shadow.getWorld(); list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); list.append(InstructionFactory.PUSH(cp, sig.getName())); list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterTypes()))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getParameterNames(w)))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getExceptions(w)))); list.append(InstructionFactory.PUSH(cp, makeString((sig.getReturnType())))); list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, new Type[] { Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING, Type.STRING }, Constants.INVOKEVIRTUAL)); } else if (sig.getKind().equals(Member.STATIC_INITIALIZATION)) { BcelWorld w = shadow.getWorld(); list.append(InstructionFactory.PUSH(cp, makeString(sig.getModifiers(w)))); list.append(InstructionFactory.PUSH(cp, makeString(sig.getDeclaringType()))); list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY2, Constants.INVOKEVIRTUAL)); } else { // TODO looks like this block is unused code list.append(InstructionFactory.PUSH(cp, SignatureUtils.getSignatureString(sig, shadow.getWorld()))); list.append(fact.createInvoke(factoryType.getClassName(), signatureMakerName, signatureType, Type.STRINGARRAY1, Constants.INVOKEVIRTUAL)); } // XXX should load source location from shadow list.append(Utility.createConstant(fact, shadow.getSourceLine())); final String factoryMethod; // TAG:SUPPORTING12: We didn't have makeESJP() in 1.2 if (world.isTargettingAspectJRuntime12()) { list.append(fact.createInvoke(factoryType.getClassName(), "makeSJP", staticTjpType, new Type[] { Type.STRING, sigType, Type.INT }, Constants.INVOKEVIRTUAL)); // put it in the field list.append(fact.createFieldAccess(getClassName(), field.getName(), staticTjpType, Constants.PUTSTATIC)); } else { if (staticTjpType.equals(field.getType())) { factoryMethod = "makeSJP"; } else if (enclosingStaticTjpType.equals(field.getType())) { factoryMethod = "makeESJP"; } else { throw new Error("should not happen"); } if (fastSJP) { if (exceptionTypes != null && exceptionTypes.length != 0) { list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), ARRAY_8STRING_INT, Constants.INVOKEVIRTUAL)); } else { list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), ARRAY_7STRING_INT, Constants.INVOKEVIRTUAL)); } } else { list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), new Type[] { Type.STRING, sigType, Type.INT }, Constants.INVOKEVIRTUAL)); } // put it in the field list.append(fact.createFieldAccess(getClassName(), field.getName(), field.getType(), Constants.PUTSTATIC)); } } public String getFactoryMethod(Field field, BcelShadow shadow) { StringBuilder b = new StringBuilder(); b.append("make"); MemberKind kind = shadow.getSignature().getKind(); if (kind.equals(Member.METHOD)) { b.append("Method"); } else if (kind.equals(Member.CONSTRUCTOR)) { b.append("Constructor"); } else if (kind.equals(Member.HANDLER)) { b.append("CatchClause"); } else if (kind.equals(Member.FIELD)) { b.append("Field"); } else if (kind.equals(Member.STATIC_INITIALIZATION)) { b.append("Initializer"); } else if (kind.equals(Member.MONITORENTER)) { b.append("Lock"); } else if (kind.equals(Member.MONITOREXIT)) { b.append("Unlock"); } else if (kind.equals(Member.ADVICE)) { b.append("Advice"); } else { throw new IllegalStateException(kind.toString()); } if (staticTjpType.equals(field.getType())) { b.append("SJP"); } else if (enclosingStaticTjpType.equals(field.getType())) { b.append("ESJP"); } return b.toString(); }
Generate optimal joinpoint initialization code. As of version 1.9.1 the runtime includes new factory methods for joinpoints that take classes, not strings and using them requires different code generation. Using these instead of the old ones means we can avoid deferred classloading for these types. By using the LDC instruction that loads classes, it also means anything modifying woven code and changing type names will also pick up on these references.
/** * Generate optimal joinpoint initialization code. * * As of version 1.9.1 the runtime includes new factory methods for joinpoints that take classes, not strings * and using them requires different code generation. Using these instead of the old ones means we can avoid * deferred classloading for these types. By using the LDC instruction that loads classes, it also means * anything modifying woven code and changing type names will also pick up on these references. */
private void initializeTjpOptimal(InstructionFactory fact, InstructionList list, Field field, BcelShadow shadow) { list.append(InstructionFactory.createLoad(factoryType, 0)); pushString(list, shadow.getKind().getName()); String factoryMethod = getFactoryMethod(field, shadow); Member sig = shadow.getSignature(); BcelWorld w = shadow.getWorld(); if (sig.getKind().equals(Member.METHOD)) { pushInt(list, sig.getModifiers(w)); pushString(list, sig.getName()); pushClass(list, sig.getDeclaringType()); pushClasses(list, sig.getParameterTypes()); pushStrings(list, sig.getParameterNames(w)); pushClasses(list, sig.getExceptions(w)); pushClass(list, sig.getReturnType()); pushInt(list, shadow.getSourceLine()); list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), PARAMSIGNATURE_MAKESJP_METHOD, Constants.INVOKEVIRTUAL)); } else if (sig.getKind().equals(Member.CONSTRUCTOR)) { if (w.isJoinpointArrayConstructionEnabled() && sig.getDeclaringType().isArray()) { pushInt(list, Modifier.PUBLIC); pushClass(list, sig.getDeclaringType()); pushClasses(list, sig.getParameterTypes()); pushStrings(list, null); pushClasses(list, null); } else { pushInt(list, sig.getModifiers(w)); pushClass(list, sig.getDeclaringType()); pushClasses(list, sig.getParameterTypes()); pushStrings(list, sig.getParameterNames(w)); pushClasses(list, sig.getExceptions(w)); } pushInt(list, shadow.getSourceLine()); list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), PARAMSIGNATURE_MAKESJP_CONSTRUCTOR, Constants.INVOKEVIRTUAL)); } else if (sig.getKind().equals(Member.HANDLER)) { pushClass(list, sig.getDeclaringType()); pushClass(list, sig.getParameterTypes()[0]); String pname = null; String[] pnames = sig.getParameterNames(w); if (pnames != null && pnames.length>0) { pname = pnames[0]; } pushString(list, pname); pushInt(list, shadow.getSourceLine()); list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), PARAMSIGNATURE_MAKESJP_CATCHCLAUSE, Constants.INVOKEVIRTUAL)); } else if (sig.getKind().equals(Member.FIELD)) { pushInt(list, sig.getModifiers(w)); pushString(list, sig.getName()); // see pr227401 UnresolvedType dType = sig.getDeclaringType(); if (dType.getTypekind() == TypeKind.PARAMETERIZED || dType.getTypekind() == TypeKind.GENERIC) { dType = sig.getDeclaringType().resolve(world).getGenericType(); } pushClass(list, dType); pushClass(list, sig.getReturnType()); pushInt(list,shadow.getSourceLine()); list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), PARAMSIGNATURE_MAKESJP_FIELD, Constants.INVOKEVIRTUAL)); } else if (sig.getKind().equals(Member.STATIC_INITIALIZATION)) { pushInt(list, sig.getModifiers(w)); pushClass(list, sig.getDeclaringType()); pushInt(list, shadow.getSourceLine()); list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), PARAMSIGNATURE_MAKESJP_INITIALIZER, Constants.INVOKEVIRTUAL)); } else if (sig.getKind().equals(Member.MONITORENTER)) { pushClass(list, sig.getDeclaringType()); pushInt(list, shadow.getSourceLine()); list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), PARAMSIGNATURE_MAKESJP_MONITOR, Constants.INVOKEVIRTUAL)); } else if (sig.getKind().equals(Member.MONITOREXIT)) { pushClass(list, sig.getDeclaringType()); pushInt(list, shadow.getSourceLine()); list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), PARAMSIGNATURE_MAKESJP_MONITOR, Constants.INVOKEVIRTUAL)); } else if (sig.getKind().equals(Member.ADVICE)) { pushInt(list, sig.getModifiers(w)); pushString(list, sig.getName()); pushClass(list, sig.getDeclaringType()); pushClasses(list, sig.getParameterTypes()); pushStrings(list, sig.getParameterNames(w)); pushClasses(list, sig.getExceptions(w)); pushClass(list, sig.getReturnType()); pushInt(list, shadow.getSourceLine()); list.append(fact.createInvoke(factoryType.getClassName(), factoryMethod, field.getType(), PARAMSIGNATURE_MAKESJP_ADVICE, Constants.INVOKEVIRTUAL)); } else { throw new IllegalStateException("not sure what to do: "+shadow); } list.append(fact.createFieldAccess(getClassName(), field.getName(), field.getType(), Constants.PUTSTATIC)); } private void pushStrings(InstructionList list, String[] strings) { // Build an array loaded with the strings if (strings == null || strings.length == 0) { list.append(InstructionFactory.ACONST_NULL); } else { list.append(InstructionFactory.PUSH(cp, strings.length)); list.append(fact.createNewArray(Type.STRING, (short)1)); for (int s=0;s<strings.length;s++) { list.append(InstructionFactory.DUP); list.append(InstructionFactory.PUSH(cp, s)); list.append(InstructionFactory.PUSH(cp, strings[s])); list.append(InstructionFactory.AASTORE); } } } private void pushClass(InstructionList list, UnresolvedType type) { if (type.isPrimitiveType()) { if (type.getSignature().equals("I")) { list.append(fact.createGetStatic("java/lang/Integer","TYPE", Type.CLASS)); } else if (type.getSignature().equals("D")) { list.append(fact.createGetStatic("java/lang/Double","TYPE", Type.CLASS)); } else if (type.getSignature().equals("S")) { list.append(fact.createGetStatic("java/lang/Short","TYPE", Type.CLASS)); } else if (type.getSignature().equals("J")) { list.append(fact.createGetStatic("java/lang/Long","TYPE", Type.CLASS)); } else if (type.getSignature().equals("F")) { list.append(fact.createGetStatic("java/lang/Float","TYPE", Type.CLASS)); } else if (type.getSignature().equals("C")) { list.append(fact.createGetStatic("java/lang/Character","TYPE", Type.CLASS)); } else if (type.getSignature().equals("B")) { list.append(fact.createGetStatic("java/lang/Byte","TYPE", Type.CLASS)); } else if (type.getSignature().equals("Z")) { list.append(fact.createGetStatic("java/lang/Boolean","TYPE", Type.CLASS)); } else if (type.getSignature().equals("V")) { list.append(InstructionFactory.ACONST_NULL); } return; } String classString = makeLdcClassString(type); if (classString == null) { list.append(InstructionFactory.ACONST_NULL); } else { list.append(fact.PUSHCLASS(cp, classString)); } } private void pushClasses(InstructionList list, UnresolvedType[] types) { // Build an array loaded with the class objects if (types == null || types.length == 0) { list.append(InstructionFactory.ACONST_NULL); } else { list.append(InstructionFactory.PUSH(cp, types.length)); list.append(fact.createNewArray(Type.CLASS, (short)1)); for (int t=0;t<types.length;t++) { list.append(InstructionFactory.DUP); list.append(InstructionFactory.PUSH(cp, t)); pushClass(list, types[t]); list.append(InstructionFactory.AASTORE); } } } private final void pushString(InstructionList list, String string) { list.append(InstructionFactory.PUSH(cp, string)); } private final void pushInt(InstructionList list, int value) { list.append(InstructionFactory.PUSH(cp, value)); } protected String makeString(int i) { return Integer.toString(i, 16); // ??? expensive } protected String makeString(UnresolvedType t) { // this is the inverse of the odd behavior for Class.forName w/ arrays if (t.isArray()) { // this behavior matches the string used by the eclipse compiler for // Foo.class literals return t.getSignature().replace('/', '.'); } else { if (t.isParameterizedType()) { return t.getRawType().getName(); } else { return t.getName(); } } } protected String makeLdcClassString(UnresolvedType type) { if (type.isVoid() || type.isPrimitiveType()) { return null; } if (type.isArray()) { return type.getSignature(); } else { if (type.isParameterizedType()) { type = type.getRawType(); } String signature = type.getSignature(); if (signature.length() ==1 ) { return signature; } return signature.substring(1,signature.length()-1); } } protected String makeString(UnresolvedType[] types) { if (types == null) { return ""; } StringBuilder buf = new StringBuilder(); for (int i = 0, len = types.length; i < len; i++) { if (i > 0) { buf.append(':'); } buf.append(makeString(types[i])); } return buf.toString(); } protected String makeString(String[] names) { if (names == null) { return ""; } StringBuilder buf = new StringBuilder(); for (int i = 0, len = names.length; i < len; i++) { if (i > 0) { buf.append(':'); } buf.append(names[i]); } return buf.toString(); } public ResolvedType getType() { if (myType == null) { return null; } return myType.getResolvedTypeX(); } public BcelObjectType getBcelObjectType() { return myType; } public String getFileName() { return myGen.getFileName(); } // for *new* fields private void addField(FieldGen field) { makeSyntheticAndTransientIfNeeded(field); BcelField bcelField = null; if (getBcelObjectType() != null) { bcelField = new BcelField(getBcelObjectType(), field.getField()); } else { bcelField = new BcelField(getName(), field.getField(), world); } fields.add(bcelField); // myGen.addField(field.getField()); } private void makeSyntheticAndTransientIfNeeded(FieldGen field) { if (field.getName().startsWith(NameMangler.PREFIX) && !field.getName().startsWith("ajc$interField$") && !field.getName().startsWith("ajc$instance$")) { // it's an aj added field // first do transient if (!field.isStatic()) { field.setModifiers(field.getModifiers() | Constants.ACC_TRANSIENT); } // then do synthetic if (getWorld().isInJava5Mode()) { // add the synthetic modifier flag field.setModifiers(field.getModifiers() | ACC_SYNTHETIC); } if (!hasSyntheticAttribute(field.getAttributes())) { // belt and braces, do the attribute even on Java 5 in addition // to the modifier flag // Attribute[] oldAttrs = field.getAttributes(); // Attribute[] newAttrs = new Attribute[oldAttrs.length + 1]; // System.arraycopy(oldAttrs, 0, newAttrs, 0, oldAttrs.length); ConstantPool cpg = myGen.getConstantPool(); int index = cpg.addUtf8("Synthetic"); Attribute synthetic = new Synthetic(index, 0, new byte[0], cpg); field.addAttribute(synthetic); // newAttrs[newAttrs.length - 1] = synthetic; // field.setAttributes(newAttrs); } } } private boolean hasSyntheticAttribute(List<Attribute> attributes) { for (int i = 0; i < attributes.size(); i++) { if ((attributes.get(i)).getName().equals("Synthetic")) { return true; } } return false; } public void addField(FieldGen field, ISourceLocation sourceLocation) { addField(field); if (!(field.isPrivate() && (field.isStatic() || field.isTransient()))) { errorOnAddedField(field, sourceLocation); } } public String getClassName() { return myGen.getClassName(); } public boolean isInterface() { return myGen.isInterface(); } public boolean isAbstract() { return myGen.isAbstract(); } public LazyMethodGen getLazyMethodGen(Member m) { return getLazyMethodGen(m.getName(), m.getSignature(), false); } public LazyMethodGen getLazyMethodGen(String name, String signature) { return getLazyMethodGen(name, signature, false); } public LazyMethodGen getLazyMethodGen(String name, String signature, boolean allowMissing) { for (LazyMethodGen gen : methodGens) { if (gen.getName().equals(name) && gen.getSignature().equals(signature)) { return gen; } } if (!allowMissing) { throw new BCException("Class " + this.getName() + " does not have a method " + name + " with signature " + signature); } return null; } public void forcePublic() { myGen.setModifiers(Utility.makePublic(myGen.getModifiers())); } public boolean hasAnnotation(UnresolvedType t) { // annotations on the real thing AnnotationGen agens[] = myGen.getAnnotations(); if (agens == null) { return false; } for (int i = 0; i < agens.length; i++) { AnnotationGen gen = agens[i]; if (t.equals(UnresolvedType.forSignature(gen.getTypeSignature()))) { return true; } } // annotations added during this weave return false; } public void addAnnotation(AnnotationGen a) { if (!hasAnnotation(UnresolvedType.forSignature(a.getTypeSignature()))) { annotations.add(new AnnotationGen(a, getConstantPool(), true)); } } public void addAttribute(AjAttribute attribute) { myGen.addAttribute(Utility.bcelAttribute(attribute, getConstantPool())); } public void addAttribute(Attribute attribute) { myGen.addAttribute(attribute); } public Collection<Attribute> getAttributes() { return myGen.getAttributes(); } // this test is like asking: // if // (UnresolvedType.SERIALIZABLE.resolve(getType().getWorld()).isAssignableFrom // (getType())) { // only we don't do that because this forces us to find all the supertypes // of the type, // and if one of them is missing we fail, and it's not worth failing just to // put out // a warning message! private boolean implementsSerializable(ResolvedType aType) { if (aType.getSignature().equals(UnresolvedType.SERIALIZABLE.getSignature())) { return true; } ResolvedType[] interfaces = aType.getDeclaredInterfaces(); for (int i = 0; i < interfaces.length; i++) { if (interfaces[i].isMissing()) { continue; } if (implementsSerializable(interfaces[i])) { return true; } } ResolvedType superType = aType.getSuperclass(); if (superType != null && !superType.isMissing()) { return implementsSerializable(superType); } return false; } public boolean isAtLeastJava5() { return (myGen.getMajor() >= Constants.MAJOR_1_5); }
Return the next available field name with the specified 'prefix', e.g. for prefix 'class$' where class$0, class$1 exist then return class$2
/** * Return the next available field name with the specified 'prefix', e.g. for prefix 'class$' where class$0, class$1 exist then * return class$2 */
public String allocateField(String prefix) { int highestAllocated = -1; List<BcelField> fs = getFieldGens(); for (BcelField field : fs) { if (field.getName().startsWith(prefix)) { try { int num = Integer.parseInt(field.getName().substring(prefix.length())); if (num > highestAllocated) { highestAllocated = num; } } catch (NumberFormatException nfe) { // something wrong with the number on the end of that // field... } } } return prefix + Integer.toString(highestAllocated + 1); } }