| /* ******************************************************************* |
| * 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. |
| */ |
| 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 */ |
| 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 |
| */ |
| 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: |
| * <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<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). |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| */ |
| 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 |
| */ |
| 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); |
| } |
| |
| } |