/* ******************************************************************* * 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 v 2.0 * which accompanies this distribution and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt * * 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. */ 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 inlinedFiles = new TreeMap<>(); 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 fields = new ArrayList<>(); private final List methodGens = new ArrayList<>(); private final List classGens = new ArrayList<>(); private final List annotations = new ArrayList<>(); 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) { InlinedSourceFileInfo info = inlinedFiles.get(fullpath); if (info != null) { 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 */ 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 Integer.toString(childCounter++); } // ---- 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 (ResolvedMember method : methods) { if (method.getName().equals("")) { 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 (ResolvedMember method : methods) { addMethodGen(new LazyMethodGen((BcelMethod) method, this)); } // Method[] methods = myGen.getMethods(); // for (int i = 0; i < methods.length; i++) { // addMethodGen(new LazyMethodGen(methods[i], this)); // } ResolvedMember[] fields = myType.getDeclaredFields(); for (ResolvedMember field : fields) { this.fields.add((BcelField) field); } } public static boolean hasSerialVersionUIDField(ResolvedType type) { ResolvedMember[] fields = type.getDeclaredFields(); for (ResolvedMember field : fields) { 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 */ 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 getMethodGens() { return methodGens; // ???Collections.unmodifiableList(methodGens); } public List getFieldGens() { return fields; } public boolean fieldExists(String name) { // Field[] allFields = myGen.getFields(); // if (allFields!=null) { // for (int i=0;i 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 *
*/ 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 (ResolvedType typeX : interfaceRTXs) { if (typeX.isGenericType() || typeX.isParameterizedType()) { needAttribute = true; } } if (extraSuperInterfaces != null) { for (ResolvedType interfaceType : extraSuperInterfaces) { 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) { StringBuilder signature = new StringBuilder(); // first, the type variables... if (myType != null) { TypeVariable[] tVars = myType.getTypeVariables(); if (tVars.length > 0) { signature.append("<"); for (TypeVariable variable : tVars) { signature.append(variable.getSignatureForAttribute()); } signature.append(">"); } } // now the supertype String supersig = getSuperClass().getSignatureForAttribute(); signature.append(supersig); if (myType != null) { ResolvedType[] interfaceRTXs = myType.getDeclaredInterfaces(); for (ResolvedType interfaceRTX : interfaceRTXs) { String s = interfaceRTX.getSignatureForAttribute(); signature.append(s); } if (extraSuperInterfaces != null) { for (ResolvedType extraSuperInterface : extraSuperInterfaces) { String s = extraSuperInterface.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;" */ 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 classes (" + AsmDetector.CLASS_READER + ", " + AsmDetector.CLASS_VISITOR + ") " + "for stackmap generation. Stackmap generation for woven code is required to avoid verify errors " + "on a Java 1.7 or higher runtime.", AsmDetector.reasonAsmIsMissing ); } wovenClassFileData = StackMapAdder.addStackMaps(world, myGen.getClassName(), 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 getClassGens() { List ret = new ArrayList<>(); ret.add(this); ret.addAll(classGens); return ret; } public List getChildClasses(BcelWorld world) { if (classGens.isEmpty()) { return Collections.emptyList(); } List ret = new ArrayList<>(); 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 classGens = getClassGens(); for (Iterator 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 (Field field : fields) { out.print(" "); out.println(field); } List methodGens = getMethodGens(); for (Iterator 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("")) { 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 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("")) { return gen; } } LazyMethodGen clinit = new LazyMethodGen(Modifier.STATIC, Type.VOID, "", Type.NO_ARGS, 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). * * @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); InstructionList clinitBody = getStaticInitializer().getBody(); clinitBody.insert(Utility.createInvoke(fact, ajcPreClinit)); if (serialVersionUIDRequiresInitialization) { InstructionList il = new InstructionList(); il.append(InstructionFactory.PUSH(getConstantPool(), calculatedSerialVersionUID)); il.append(getFactory().createFieldAccess(getClassName(), "serialVersionUID", BasicType.LONG, Constants.PUTSTATIC)); clinitBody.insert(il); } 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. * * @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 tjpFields = new HashMap<>(); Map annotationCachingFieldCache = new HashMap<>(); 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 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. * * @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$ 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 = initializeAllTjps(); 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(), "", Type.VOID, new Type[] { Type.STRING, classType }, Constants.INVOKESPECIAL)); list.append(InstructionFactory.createStore(factoryType, 0)); return list; } private InstructionList[] initializeAllTjps() { Vector lists = new Vector<>(); InstructionList list = initInstructionList(); lists.add(list); List> entries = new ArrayList<>(tjpFields.entrySet()); entries.sort(new Comparator>() { @Override public int compare(Map.Entry a, Map.Entry b) { return (a.getValue()).getName().compareTo((b.getValue()).getName()); } }); long estimatedSize = 0; for (Map.Entry entry : entries) { 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. */ 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 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 attributes) { for (Attribute attribute : attributes) { if (attribute.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 (AnnotationGen gen : agens) { 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 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 (ResolvedType anInterface : interfaces) { if (anInterface.isMissing()) { continue; } if (implementsSerializable(anInterface)) { 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 */ public String allocateField(String prefix) { int highestAllocated = -1; List 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); } }