All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aspectj.weaver.bcel.LazyClassGen Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/* *******************************************************************
 * 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) { if ( AsmDetector.rootCause instanceof ClassNotFoundException || AsmDetector.rootCause instanceof NoClassDefFoundError ) { // Fix https://github.com/eclipse-aspectj/aspectj/issues/251, using "replace('Ä', 'Ö')" to avoid // non-relocated class names to be embedded into the error message during compile time, making it end up in // the class constant pool unwantedly, as this clashes with post-compile ASM package relocation. String errorMessage = "Unable to find ASM classes (" + AsmDetector.CLASS_READER.replace('Ä', 'Ö') + ", " + AsmDetector.CLASS_VISITOR.replace('Ä', 'Ö') + ") " + "for stackmap generation. Stackmap generation for woven code is required to avoid verify errors " + "on a Java 1.7 or higher runtime."; throw new BCException(errorMessage, AsmDetector.rootCause); } throw new BCException("Error processing class file", AsmDetector.rootCause); } 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); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy