buildConfig.setProceedOnError(true);
} else if (new File(arg).isDirectory()) {
showError("dir arg not permitted: " + arg);
+ } else if (arg.equals("-1.5")) {
+ buildConfig.setBehaveInJava5Way(true);
+// this would enable the '-source 1.5' to do the same as '-1.5' but doesnt sound quite right as
+// as an option right now as it doesnt mean we support 1.5 source code - people will get confused...
+// } else if (arg.equals("-source")) {
+// if (args.size() > nextArgIndex) {
+// String level = ((ConfigParser.Arg)args.get(nextArgIndex)).getValue();
+// if (!level.equals("1.5")) {
+// unparsedArgs.add("-source");
+// unparsedArgs.add(level);
+// } else {
+// buildConfig.setJava5Behaviour(true);
+// }
+// args.remove(args.get(nextArgIndex));
+// }
} else {
// argfile, @file parsed by superclass
// no eclipse options parsed:
\ Compliance options:\n\
\ -1.3 use 1.3 compliance level (implicit -source 1.3 -target 1.1)\n\
\ -1.4 + use 1.4 compliance level (implicit -source 1.3 -target 1.2)\n\
+\ -1.5 set behaviour of the weaver to 1.5 mode\n\
\ -source <version> set source level (1.3 or 1.4)\n\
\ -target <version> set classfile target (1.1 to 1.4)\n\
\ \n\
}
private void doDeclareParents(DeclareParents declareParents, SourceTypeBinding sourceType) {
- List newParents = declareParents.findMatchingNewParents(factory.fromEclipse(sourceType));
+ List newParents = declareParents.findMatchingNewParents(factory.fromEclipse(sourceType),false);
if (!newParents.isEmpty()) {
for (Iterator i = newParents.iterator(); i.hasNext(); ) {
ResolvedTypeX parent = (ResolvedTypeX)i.next();
public boolean getProceedOnError() {
return options.proceedOnError;
}
+
+ public void setBehaveInJava5Way(boolean b) {
+ options.behaveInJava5Way = b;
+ }
+
+ public boolean getBehaveInJava5Way() {
+ return options.behaveInJava5Way;
+ }
}
List cp = buildConfig.getBootclasspath();
cp.addAll(buildConfig.getClasspath());
bcelWorld = new BcelWorld(cp, handler, null);
+ bcelWorld.setBehaveInJava5Way(buildConfig.getBehaveInJava5Way());
bcelWorld.setXnoInline(buildConfig.isXnoInline());
bcelWorld.setXlazyTjp(buildConfig.isXlazyTjp());
bcelWeaver = new BcelWeaver(bcelWorld);
public boolean xReweavableCompress = false;
public boolean showWeavingInformation = false;
+ // If true - autoboxing behaves differently ...
+ public boolean behaveInJava5Way = false;
+
// these next three not exposed by IDEs
public boolean generateModel = false;
public boolean generateJavaDocsInModel = false;
private void initWorldAndWeaver(AjCompilerOptions options) {
cpManager = new EclipseClassPathManager(nameEnvironment);
myBcelWorld = new BcelWorld(cpManager,new UnhandledMessageHandler(getProject()),null /*(xrefHandler)*/);
+ myBcelWorld.setBehaveInJava5Way(options.behaveInJava5Way);
myBcelWorld.setXnoInline(options.xNoInline);
myBcelWorld.setXlazyTjp(options.xLazyThisJoinPoint);
setLintProperties(myBcelWorld,options);
assertTrue(new File(lintFile).exists());
assertEquals(getCanonicalPath(new File(lintFile)),config.getLintSpecFile().getAbsolutePath());
}
+
+ /**
+ * The option '-1.5' are currently eaten by the AspectJ argument parser - since
+ * the JDT compiler upon which we are based doesn't understand them - *this should change* when we
+ * switch to a 1.5 compiler base. They are currently used to determine whether the weaver should
+ * behave in a '1.5' way - for example autoboxing behaves differently when the 1.5 flag is specified.
+ * Under 1.4 Integer != int
+ * Under 1.5 Integer == int
+ * (this applies to all primitive types)
+ */
+ public void testSource15() throws InvalidInputException {
+// AjBuildConfig config = genBuildConfig(new String[]{"-source","1.5"},messageWriter);
+// assertTrue("should be in 1.5 mode",config.getJave5Behaviour());
+ AjBuildConfig config = genBuildConfig(new String[]{"-1.5"},messageWriter);
+ assertTrue("should be in 1.5 mode",config.getBehaveInJava5Way());
+ config = genBuildConfig(new String[]{"-source","1.4"},messageWriter);
+ assertTrue("should not be in 1.5 mode",!config.getBehaveInJava5Way());
+ assertTrue("should be in 1.4 mode",config.getOptions().sourceLevel == CompilerOptions.JDK1_4);
+ config = genBuildConfig(new String[]{"-source","1.3"},messageWriter);
+ assertTrue("should not be in 1.5 mode",!config.getBehaveInJava5Way());
+ assertTrue("should be in 1.3 mode",config.getOptions().sourceLevel == CompilerOptions.JDK1_3);
+ }
public void testOptions() throws InvalidInputException {
// AjdtCommand command = new AjdtCommand();
public final Kind noJoinpointsForBridgeMethods =
new Kind("noJoinpointsForBridgeMethods","pointcut did not match on the method call to a bridge method. Bridge methods are generated by the compiler and have no join points");
+ public final Kind enumAsTargetForDecpIgnored =
+ new Kind("enumAsTargetForDecpIgnored","enum type {0} matches a declare parents type pattern but is being ignored");
+
+ public final Kind annotationAsTargetForDecpIgnored =
+ new Kind("annotationAsTargetForDecpIgnored","annotation type {0} matches a declare parents type pattern but is being ignored");
+
public final Kind cantMatchArrayTypeOnVarargs =
new Kind("cantMatchArrayTypeOnVarargs","an array type as the last parameter in a signature does not match on the varargs declared method: {0}");
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.ISourceLocation;
public boolean needsNoConversionFrom(TypeX other, World world) {
return needsNoConversionFrom(other);
}
+
+ // This set contains pairs of types whose signatures are concatenated
+ // together, this means with a fast lookup we can tell if two types
+ // are equivalent.
+ private static Set validBoxing = new HashSet();
+
+ static {
+ validBoxing.add("Ljava/lang/Byte;B");
+ validBoxing.add("Ljava/lang/Character;C");
+ validBoxing.add("Ljava/lang/Double;D");
+ validBoxing.add("Ljava/lang/Float;F");
+ validBoxing.add("Ljava/lang/Integer;I");
+ validBoxing.add("Ljava/lang/Long;J");
+ validBoxing.add("Ljava/lang/Short;S");
+ validBoxing.add("Ljava/lang/Boolean;Z");
+ validBoxing.add("BLjava/lang/Byte;");
+ validBoxing.add("CLjava/lang/Character;");
+ validBoxing.add("DLjava/lang/Double;");
+ validBoxing.add("FLjava/lang/Float;");
+ validBoxing.add("ILjava/lang/Integer;");
+ validBoxing.add("JLjava/lang/Long;");
+ validBoxing.add("SLjava/lang/Short;");
+ validBoxing.add("ZLjava/lang/Boolean;");
+ }
+
public final boolean isConvertableFrom(TypeX other) {
if (this.equals(OBJECT) || other.equals(OBJECT)) return true;
+ if (world.behaveInJava5Way) {
+ if (this.isPrimitive()^other.isPrimitive()) { // If one is primitive and the other isnt
+ if (validBoxing.contains(this.getSignature()+other.getSignature())) return true;
+ }
+ }
return this.isCoerceableFrom(other);
}
+
// utilities
public ResolvedTypeX getResolvedComponentType() {
return null;
}
public final boolean isAssignableFrom(TypeX o) {
- if (o.isPrimitive()) return false;
+ if (o.isPrimitive()) {
+ if (!world.behaveInJava5Way) return false;
+ if (validBoxing.contains(this.getSignature()+o.getSignature())) return true;
+ }
ResolvedTypeX other = o.resolve(world);
return isAssignableFrom(other);
return false;
}
public final boolean isAssignableFrom(TypeX other) {
- if (! other.isPrimitive()) return false;
+ if (!other.isPrimitive()) {
+ if (!world.behaveInJava5Way) return false;
+ return validBoxing.contains(this.getSignature()+other.getSignature());
+ }
return assignTable[((Primitive)other).index][index];
}
public final boolean isCoerceableFrom(TypeX other) {
public boolean isAssignableFrom(TypeX other, World world) {
// primitives override this method, so we know we're not primitive.
// So if the other is primitive, don't bother asking the world anything.
- if (other.isPrimitive()) return false;
+ if (other.isPrimitive() && !world.behaveInJava5Way) return false;
return world.isAssignableFrom(this, other);
}
public static final TypeX ERROR = forSignature("Ljava/lang/Error;");
public static final TypeX AT_INHERITED = forSignature("Ljava/lang/annotation/Inherited;");
public static final TypeX AT_RETENTION = forSignature("Ljava/lang/annotation/Retention;");
+ public static final TypeX ENUM = forSignature("Ljava/lang/Enum;");
+ public static final TypeX ANNOTATION = forSignature("Ljava/lang/annotation/Annotation;");
+
// ---- helpers
protected boolean XnoInline;
protected boolean XlazyTjp;
+
+ public boolean behaveInJava5Way = false;
+
private List dumpState_cantFindTypeExceptions = null;
protected World() {
//System.out.println("resolve: " + ty + " world " + typeMap.keySet());
String signature = ty.getSignature();
ResolvedTypeX ret = typeMap.get(signature);
- if (ret != null) return ret;
+ if (ret != null) { ret.world = this; return ret; } // Set the world for the RTX
if (ty.isArray()) {
ret = new ResolvedTypeX.Array(signature, this, resolve(ty.getComponentType(), allowMissing));
}
protected final boolean isAssignableFrom(TypeX type, TypeX other) {
- return resolve(type).isAssignableFrom(other);
+ return resolve(type).isAssignableFrom(resolve(other));
}
public boolean needsNoConversionFrom(TypeX type, TypeX other) {
);
}
}
+
+ public void setBehaveInJava5Way(boolean b) {
+ behaveInJava5Way = b;
+ }
}
// need to do any declare parents before the matching below
for (Iterator i = declareParentsList.iterator(); i.hasNext(); ) {
DeclareParents p = (DeclareParents)i.next();
- List newParents = p.findMatchingNewParents(onType);
+ List newParents = p.findMatchingNewParents(onType,true);
if (!newParents.isEmpty()) {
BcelObjectType classType = BcelWorld.getBcelObjectType(onType);
//System.err.println("need to do declare parents for: " + onType);
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.ClassParser;
return ret;
}
+ // Lookup table, for converting between pairs of types, it gives
+ // us the method name in the Conversions class
+ private static Hashtable validBoxing = new Hashtable();
+
+ static {
+ validBoxing.put("Ljava/lang/Byte;B","byteObject");
+ validBoxing.put("Ljava/lang/Character;C","charObject");
+ validBoxing.put("Ljava/lang/Double;D","doubleObject");
+ validBoxing.put("Ljava/lang/Float;F","floatObject");
+ validBoxing.put("Ljava/lang/Integer;I","intObject");
+ validBoxing.put("Ljava/lang/Long;J","longObject");
+ validBoxing.put("Ljava/lang/Short;S","shortObject");
+ validBoxing.put("Ljava/lang/Boolean;Z","booleanObject");
+ validBoxing.put("BLjava/lang/Byte;","byteValue");
+ validBoxing.put("CLjava/lang/Character;","charValue");
+ validBoxing.put("DLjava/lang/Double;","doubleValue");
+ validBoxing.put("FLjava/lang/Float;","floatValue");
+ validBoxing.put("ILjava/lang/Integer;","intValue");
+ validBoxing.put("JLjava/lang/Long;","longValue");
+ validBoxing.put("SLjava/lang/Short;","shortValue");
+ validBoxing.put("ZLjava/lang/Boolean;","booleanValue");
+ }
+
public static void appendConversion(
InstructionList il,
InstructionFactory fact,
if (! toType.isConvertableFrom(fromType)) {
throw new BCException("can't convert from " + fromType + " to " + toType);
}
- if (toType.needsNoConversionFrom(fromType)) return;
-
+ // XXX I'm sure this test can be simpler but my brain hurts and this works
+ if (!toType.getWorld().behaveInJava5Way) {
+ if (toType.needsNoConversionFrom(fromType)) return;
+ } else {
+ if (toType.needsNoConversionFrom(fromType) && !(toType.isPrimitive()^fromType.isPrimitive())) return;
+ }
if (toType.equals(ResolvedTypeX.VOID)) {
// assert fromType.equals(TypeX.OBJECT)
il.append(InstructionFactory.createPop(fromType.getSize()));
Type.OBJECT,
new Type[] { from },
Constants.INVOKESTATIC));
+ } else if (toType.getWorld().behaveInJava5Way && validBoxing.get(toType.getSignature()+fromType.getSignature())!=null) {
+ // XXX could optimize by using any java boxing code that may be just before the call...
+ Type from = BcelWorld.makeBcelType(fromType);
+ Type to = BcelWorld.makeBcelType(toType);
+ String name = (String)validBoxing.get(toType.getSignature()+fromType.getSignature());
+ if (toType.isPrimitive()) {
+ il.append(
+ fact.createInvoke(
+ "org.aspectj.runtime.internal.Conversions",
+ name,
+ to,
+ new Type[]{Type.OBJECT},
+ Constants.INVOKESTATIC));
+ } else {
+ il.append(
+ fact.createInvoke(
+ "org.aspectj.runtime.internal.Conversions",
+ name,
+ Type.OBJECT,
+ new Type[] { from },
+ Constants.INVOKESTATIC));
+ il.append(fact.createCheckCast((ReferenceType) to));
+ }
} else if (fromType.isPrimitive()) {
// assert toType.isPrimitive()
Type from = BcelWorld.makeBcelType(fromType);
public class DeclareParents extends Declare {
private TypePattern child;
private TypePatternList parents;
+ private boolean isWildChild = false;
public DeclareParents(TypePattern child, List parents) {
private DeclareParents(TypePattern child, TypePatternList parents) {
this.child = child;
this.parents = parents;
+ if (child instanceof WildTypePattern) isWildChild = true;
}
public boolean match(ResolvedTypeX typeX) {
return ret;
}
+ public boolean parentsIncludeInterface(World w) {
+ for (int i = 0; i < parents.size(); i++) {
+ if (parents.get(i).getExactType().isInterface(w)) return true;
+ }
+ return false;
+ }
+ public boolean parentsIncludeClass(World w) {
+ for (int i = 0; i < parents.size(); i++) {
+ if (parents.get(i).getExactType().isClass(w)) return true;
+ }
+ return false;
+ }
+
public void resolve(IScope scope) {
child = child.resolveBindings(scope, Bindings.NONE, false, false);
parents = parents.resolveBindings(scope, Bindings.NONE, false, true);
-// for (int i=0; i < parents.size(); i++) {
-// parents.get(i).assertExactType(scope.getMessageHandler());
-// }
+
+// Could assert this ...
+// for (int i=0; i < parents.size(); i++) {
+// parents.get(i).assertExactType(scope.getMessageHandler());
+// }
}
public TypePatternList getParents() {
return false;
}
- private ResolvedTypeX maybeGetNewParent(ResolvedTypeX targetType, TypePattern typePattern, World world) {
+ private ResolvedTypeX maybeGetNewParent(ResolvedTypeX targetType, TypePattern typePattern, World world,boolean reportErrors) {
if (typePattern == TypePattern.NO) return null; // already had an error here
TypeX iType = typePattern.getExactType();
ResolvedTypeX parentType = iType.resolve(world);
this.getSourceLocation(), null);
return null;
}
-
+
if (parentType.isAssignableFrom(targetType)) return null; // already a parent
+
+ // Enum types that are targetted for decp through a wild type pattern get linted
+ if (reportErrors && isWildChild && targetType.isEnum()) {
+ world.getLint().enumAsTargetForDecpIgnored.signal(targetType.toString(),getSourceLocation());
+ }
+
+ // Annotation types that are targetted for decp through a wild type pattern get linted
+ if (reportErrors && isWildChild && targetType.isAnnotation()) {
+ world.getLint().annotationAsTargetForDecpIgnored.signal(targetType.toString(),getSourceLocation());
+ }
+
+ // 1. Can't use decp to make an enum/annotation type implement an interface
+ if (targetType.isEnum() && parentType.isInterface()) {
+ if (reportErrors && !isWildChild) {
+ world.showMessage(IMessage.ERROR,
+ WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ENUM_TO_IMPL_INTERFACE,targetType),getSourceLocation(),null);
+ }
+ return null;
+ }
+ if (targetType.isAnnotation() && parentType.isInterface()) {
+ if (reportErrors && !isWildChild) {
+ world.showMessage(IMessage.ERROR,WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ANNOTATION_TO_IMPL_INTERFACE,targetType),getSourceLocation(),null);
+ }
+ return null;
+ }
+
+ // 2. Can't use decp to change supertype of an enum/annotation
+ if (targetType.isEnum() && parentType.isClass()) {
+ if (reportErrors && !isWildChild) {
+ world.showMessage(IMessage.ERROR,WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ENUM_TO_EXTEND_CLASS,targetType),getSourceLocation(),null);
+ }
+ return null;
+ }
+ if (targetType.isAnnotation() && parentType.isClass()) {
+ if (reportErrors && !isWildChild) {
+ world.showMessage(IMessage.ERROR,WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ANNOTATION_TO_EXTEND_CLASS,targetType),getSourceLocation(),null);
+ }
+ return null;
+ }
+
+ // 3. Can't use decp to declare java.lang.Enum/java.lang.annotation.Annotation as the parent of a type
+ if (parentType.getSignature().equals(TypeX.ENUM.getSignature())) {
+ if (reportErrors && !isWildChild) {
+ world.showMessage(IMessage.ERROR,
+ WeaverMessages.format(WeaverMessages.CANT_DECP_TO_MAKE_ENUM_SUPERTYPE,targetType),getSourceLocation(),null);
+ }
+ return null;
+ }
+ if (parentType.getSignature().equals(TypeX.ANNOTATION.getSignature())) {
+ if (reportErrors && !isWildChild) {
+ world.showMessage(IMessage.ERROR,
+ WeaverMessages.format(WeaverMessages.CANT_DECP_TO_MAKE_ANNOTATION_SUPERTYPE,targetType),getSourceLocation(),null);
+ }
+ return null;
+ }
+
if (targetType.isAssignableFrom(parentType)) {
world.showMessage(IMessage.ERROR,
}
- public List/*<ResolvedTypeX>*/ findMatchingNewParents(ResolvedTypeX onType) {
+ public List/*<ResolvedTypeX>*/ findMatchingNewParents(ResolvedTypeX onType,boolean reportErrors) {
if (!match(onType)) return Collections.EMPTY_LIST;
List ret = new ArrayList();
for (int i=0; i < parents.size(); i++) {
- ResolvedTypeX t = maybeGetNewParent(onType, parents.get(i), onType.getWorld());
+ ResolvedTypeX t = maybeGetNewParent(onType, parents.get(i), onType.getWorld(),reportErrors);
if (t != null) ret.add(t);
}