@@ -54,7 +54,14 @@ package org.aspectj.apache.bcel.generic; | |||
* <http://www.apache.org/>. | |||
*/ | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.DataOutputStream; | |||
import java.lang.reflect.Modifier; | |||
import java.security.MessageDigest; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collections; | |||
import java.util.Comparator; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
@@ -77,8 +84,10 @@ import org.aspectj.apache.bcel.generic.annotation.AnnotationGen; | |||
* existing java class (file). | |||
* | |||
* @see JavaClass | |||
* @version $Id: ClassGen.java,v 1.5 2005/03/10 12:15:04 aclement Exp $ | |||
* @version $Id: ClassGen.java,v 1.6 2006/03/09 17:25:48 aclement Exp $ | |||
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> | |||
* | |||
* Upgraded, Andy Clement 9th Mar 06 - calculates SUID | |||
*/ | |||
public class ClassGen extends AccessFlags implements Cloneable { | |||
/* Corresponds to the fields found in a JavaClass object. | |||
@@ -538,4 +547,153 @@ public class ClassGen extends AccessFlags implements Cloneable { | |||
public final boolean isEnum() { | |||
return (access_flags & Constants.ACC_ENUM) != 0; | |||
} | |||
/** | |||
* Calculate the SerialVersionUID for a class. | |||
*/ | |||
public long getSUID() { | |||
try { | |||
Field[] fields = getFields(); | |||
Method[] methods = getMethods(); | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
DataOutputStream dos = new DataOutputStream(baos); | |||
// 1. classname | |||
dos.writeUTF(getClassName()); | |||
// 2. classmodifiers: ACC_PUBLIC, ACC_FINAL, ACC_INTERFACE, and ACC_ABSTRACT | |||
int classmods = 0; | |||
classmods|=(isPublic()?Constants.ACC_PUBLIC:0); | |||
classmods|=(isFinal()?Constants.ACC_FINAL:0); | |||
classmods|=(isInterface()?Constants.ACC_INTERFACE:0); | |||
if (isInterface() && isAbstract()) { // remove abstract if we have it but have no methods | |||
if (methods.length>0) classmods|=Constants.ACC_ABSTRACT; | |||
} | |||
dos.writeInt(classmods); | |||
// 3. ordered list of interfaces | |||
List list = new ArrayList(); | |||
String[] names = getInterfaceNames(); | |||
if (names!=null) { | |||
Arrays.sort(names); | |||
for (int i = 0; i < names.length; i++) dos.writeUTF(names[i]); | |||
} | |||
// 4. ordered list of fields (ignoring private static and private transient fields): | |||
// (relevant modifiers are ACC_PUBLIC, ACC_PRIVATE, | |||
// ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_VOLATILE, | |||
// ACC_TRANSIENT) | |||
list.clear(); | |||
for (int i = 0; i < fields.length; i++) { | |||
Field field = fields[i]; | |||
if (!(field.isPrivate() && field.isStatic()) && | |||
!(field.isPrivate() && field.isTransient())) list.add(field); | |||
} | |||
Collections.sort(list,new FieldComparator()); | |||
int relevantFlags = Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED | | |||
Constants.ACC_STATIC | Constants.ACC_FINAL | Constants.ACC_VOLATILE | Constants.ACC_TRANSIENT; | |||
for (Iterator iter = list.iterator(); iter.hasNext();) { | |||
Field f = (Field) iter.next(); | |||
dos.writeUTF(f.getName()); | |||
dos.writeInt(relevantFlags&f.getModifiers()); | |||
dos.writeUTF(f.getType().getSignature()); | |||
} | |||
// some up front method processing: discover clinit, init and ordinary methods of interest: | |||
list.clear(); // now used for methods | |||
List ctors = new ArrayList(); | |||
boolean hasClinit = false; | |||
for (int i = 0; i < methods.length; i++) { | |||
Method m = methods[i]; | |||
boolean couldBeInitializer = m.getName().charAt(0)=='<'; | |||
if (couldBeInitializer && m.getName().equals("<clinit>")) { | |||
hasClinit=true; | |||
} else if (couldBeInitializer && m.getName().equals("<init>")) { | |||
if (!m.isPrivate()) ctors.add(m); | |||
} else { | |||
if (!m.isPrivate()) list.add(m); | |||
} | |||
} | |||
Collections.sort(ctors, new ConstructorComparator()); | |||
Collections.sort(list, new MethodComparator()); | |||
// 5. If a class initializer exists, write out the following: | |||
// 1. The name of the method, <clinit>. | |||
// 2. The modifier of the method, java.lang.reflect.Modifier.STATIC, written as a 32-bit integer. | |||
// 3. The descriptor of the method, ()V. | |||
if (hasClinit) { | |||
dos.writeUTF("<clinit>"); | |||
dos.writeInt(Modifier.STATIC); | |||
dos.writeUTF("()V"); | |||
} | |||
// for methods and constructors: | |||
// ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, | |||
// ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT | |||
relevantFlags = | |||
Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED | | |||
Constants.ACC_STATIC | Constants.ACC_FINAL | Constants.ACC_SYNCHRONIZED | | |||
Constants.ACC_NATIVE | Constants.ACC_ABSTRACT | Constants.ACC_STRICT; | |||
// 6. sorted non-private constructors | |||
for (Iterator iter = ctors.iterator(); iter.hasNext();) { | |||
Method m = (Method) iter.next(); | |||
dos.writeUTF(m.getName()); // <init> | |||
dos.writeInt(relevantFlags & m.getModifiers()); | |||
dos.writeUTF(m.getSignature().replace('/','.')); | |||
} | |||
// 7. sorted non-private methods | |||
for (Iterator iter = list.iterator(); iter.hasNext();) { | |||
Method m = (Method) iter.next(); | |||
dos.writeUTF(m.getName()); | |||
dos.writeInt(relevantFlags & m.getModifiers()); | |||
dos.writeUTF(m.getSignature().replace('/','.')); | |||
} | |||
dos.flush(); | |||
dos.close(); | |||
byte[] bs = baos.toByteArray(); | |||
MessageDigest md = MessageDigest.getInstance("SHA"); | |||
byte[] result = md.digest(bs); | |||
long suid = 0L; | |||
int pos = result.length>8?7:result.length-1; // use the bytes we have | |||
while (pos>=0) { | |||
suid = suid<<8 | ((long)result[pos--]&0xff); | |||
} | |||
// if it was definetly 8 everytime... | |||
// long suid = ((long)(sha[0]&0xff) | (long)(sha[1]&0xff) << 8 | | |||
// (long)(sha[2]&0xff) << 16 | (long)(sha[3]&0xff) << 24 | | |||
// (long)(sha[4]&0xff) << 32 | (long)(sha[5]&0xff) << 40 | | |||
// (long)(sha[6]&0xff) << 48 | (long)(sha[7]&0xff) << 56); | |||
return suid; | |||
} catch (Exception e) { | |||
System.err.println("Unable to calculate suid for "+getClassName()); | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
private static class FieldComparator implements Comparator { | |||
public int compare(Object arg0, Object arg1) { | |||
return ((Field)arg0).getName().compareTo(((Field)arg1).getName()); | |||
} | |||
} | |||
private static class ConstructorComparator implements Comparator { | |||
public int compare(Object arg0, Object arg1) { | |||
// can ignore the name... | |||
return ((Method)arg0).getSignature().compareTo(((Method)arg1).getSignature()); | |||
} | |||
} | |||
private static class MethodComparator implements Comparator { | |||
public int compare(Object arg0, Object arg1) { | |||
Method m1 = (Method)arg0; | |||
Method m2 = (Method)arg1; | |||
int result = m1.getName().compareTo(m2.getName()); | |||
if (result!=0) return result; | |||
return m1.getSignature().compareTo(m2.getSignature()); | |||
} | |||
} | |||
} |
@@ -431,6 +431,16 @@ | |||
</para></listitem> | |||
</varlistentry> | |||
<varlistentry> | |||
<term>-XaddSerialVersionUID</term> | |||
<listitem><para>Causes the compiler to calculate and add | |||
the SerialVersionUID field to any type implementing | |||
Serializable that is affected by an aspect. The field | |||
is calculated based on the class before weaving has | |||
taken place. | |||
</para></listitem> | |||
</varlistentry> | |||
<varlistentry> | |||
<term>-Xreweavable[:compress]</term> | |||
<listitem><para>(Experimental - deprecated as now default) |
@@ -533,6 +533,8 @@ public class BuildArgParser extends Main { | |||
buildConfig.setGenerateJavadocsInModelMode(true); | |||
} else if (arg.equals("-Xdev:NoAtAspectJProcessing")) { | |||
buildConfig.setNoAtAspectJAnnotationProcessing(true); | |||
} else if (arg.equals("-XaddSerialVersionUID")) { | |||
buildConfig.setAddSerialVerUID(true); | |||
} else if (arg.equals("-Xdev:Pinpoint")) { | |||
buildConfig.setXdevPinpointMode(true); | |||
} else if (arg.equals("-Xjoinpoints:arrayconstruction")) { |
@@ -132,6 +132,8 @@ xoption.usage = {0} non-standard options:\n\ | |||
\t by AspectJ.\n\ | |||
\t-XserializableAspects allows aspects to implement serializable\n\ | |||
\t-XterminateAfterCompilation compile classes then terminate before weaving\n\ | |||
\t-XaddSerialVersionUID calculates and adds the serialVersionUID to any\n\ | |||
\t serializable type woven by an aspect\n\ | |||
\t-Xajruntimelevel:<level> allows code to be generated that targets\n\ | |||
\t a 1.2 or a 1.5 level AspectJ runtime (default 1.5)\n\ | |||
\t-XhasMember allow hasmethod() and hasfield type patterns in\n\ |
@@ -388,6 +388,7 @@ public class AjBuildConfig { | |||
} | |||
setOutxmlName(global.getOutxmlName()); | |||
setXconfigurationInfo(global.getXconfigurationInfo()); | |||
setAddSerialVerUID(global.isAddSerialVerUID()); | |||
} | |||
void join(Collection local, Collection global) { | |||
@@ -523,6 +524,13 @@ public class AjBuildConfig { | |||
public boolean isXdevPinpoint() { | |||
return options.xdevPinpoint; | |||
} | |||
public void setAddSerialVerUID(boolean b) { | |||
options.addSerialVerUID = b; | |||
} | |||
public boolean isAddSerialVerUID() { | |||
return options.addSerialVerUID; | |||
} | |||
public boolean isXNotReweavable() { |
@@ -633,6 +633,7 @@ public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourc | |||
cp.addAll(buildConfig.getClasspath()); | |||
BcelWorld bcelWorld = new BcelWorld(cp, handler, null); | |||
bcelWorld.setBehaveInJava5Way(buildConfig.getBehaveInJava5Way()); | |||
bcelWorld.setAddSerialVerUID(buildConfig.isAddSerialVerUID()); | |||
bcelWorld.performExtraConfiguration(buildConfig.getXconfigurationInfo()); | |||
bcelWorld.setTargetAspectjRuntimeLevel(buildConfig.getTargetAspectjRuntimeLevel()); | |||
bcelWorld.setOptionalJoinpoints(buildConfig.getXJoinpoints()); |
@@ -80,6 +80,7 @@ public class AjCompilerOptions extends CompilerOptions { | |||
public String targetAspectjRuntimeLevel = Constants.RUNTIME_LEVEL_DEFAULT; | |||
public String xConfigurationInfo; | |||
public boolean addSerialVerUID = false; | |||
// these next four not exposed by IDEs | |||
public boolean generateModel = false; |
@@ -0,0 +1,5 @@ | |||
aspect AnAspect { | |||
before(): staticinitialization(BigHorribleClass) { | |||
} | |||
} |
@@ -0,0 +1,19 @@ | |||
import java.io.Serializable; | |||
import java.lang.reflect.Field; | |||
public class Basic implements Serializable { | |||
public static void main(String[] args) { | |||
try { | |||
Basic b = (Basic)Basic.class.newInstance(); | |||
Field f = Basic.class.getDeclaredField("serialVersionUID"); | |||
long l = f.getLong(b); | |||
System.err.println("SerialVersionUID is "+l); | |||
} catch (Exception e) { | |||
System.err.println("Problem: "+e.toString()); | |||
} | |||
} | |||
} | |||
aspect X { | |||
before(): staticinitialization(Basic) {} | |||
} |
@@ -0,0 +1,28 @@ | |||
import java.io.Serializable; | |||
import java.lang.reflect.Field; | |||
public class BigHorribleClass implements Serializable,Comparable { | |||
public static void main(String[] args) { | |||
try { | |||
BigHorribleClass b = (BigHorribleClass)BigHorribleClass.class.newInstance(); | |||
Field f = BigHorribleClass.class.getDeclaredField("serialVersionUID"); | |||
long l = f.getLong(b); | |||
System.err.println("SerialVersionUID is "+l); | |||
} catch (Exception e) { | |||
System.err.println("Problem: "+e.toString()); | |||
} | |||
} | |||
public int anInt; | |||
public static boolean aBoolean = false; | |||
public long foo = 376; | |||
public void m() {} | |||
public int compareTo(Object o) { return 0;} | |||
public String m2(boolean b,long l, String s) { return "";} | |||
public static transient short fo2 = 3; | |||
} |
@@ -21,6 +21,7 @@ public class AllTestsAspectJ151 { | |||
suite.addTest(Ajc151Tests.suite()); | |||
suite.addTest(NewarrayJoinpointTests.suite()); | |||
suite.addTest(AtAroundTests.suite()); | |||
suite.addTest(SerialVersionUIDTests.suite()); | |||
//$JUnit-END$ | |||
return suite; | |||
} |
@@ -0,0 +1,35 @@ | |||
/******************************************************************************* | |||
* Copyright (c) 2006 IBM Corporation and others. | |||
* All rights reserved. This program and the accompanying materials | |||
* are made available under the terms of the Common Public License v1.0 | |||
* which accompanies this distribution, and is available at | |||
* http://www.eclipse.org/legal/epl-v10.html | |||
* | |||
* Contributors: | |||
* Andy Clement - initial implementation | |||
*******************************************************************************/ | |||
package org.aspectj.systemtest.ajc151; | |||
import java.io.File; | |||
import junit.framework.Test; | |||
import org.aspectj.testing.XMLBasedAjcTestCase; | |||
public class SerialVersionUIDTests extends XMLBasedAjcTestCase { | |||
public void testTheBasics() { runTest("basic"); } | |||
public void testTheBasicsWithLint() { runTest("basic - lint"); } | |||
public void testHorrible() { runTest("horrible"); } | |||
// | |||
public static Test suite() { | |||
return XMLBasedAjcTestCase.loadSuite(SerialVersionUIDTests.class); | |||
} | |||
protected File getSpecFile() { | |||
return new File("../tests/src/org/aspectj/systemtest/ajc151/serialversionuid.xml"); | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
<!DOCTYPE suite SYSTEM "../tests/ajcTestSuite.dtd"[]> | |||
<!-- AspectJ v1.5.1 Tests --> | |||
<suite> | |||
<ajc-test dir="features151/serialveruid" title="basic"> | |||
<compile files="Basic.java" options="-1.5 -XaddSerialVersionUID"/> | |||
<run class="Basic"> | |||
<stderr> | |||
<line text="SerialVersionUID is 7052682057082172300"/> | |||
</stderr> | |||
</run> | |||
</ajc-test> | |||
<ajc-test dir="features151/serialveruid" title="basic - lint"> | |||
<compile files="Basic.java" options="-1.5 -XaddSerialVersionUID -Xlint:warning"> | |||
<message kind="warning" text="calculated SerialVersionUID for type Basic"/> | |||
</compile> | |||
<run class="Basic"> | |||
<stderr> | |||
<line text="SerialVersionUID is 7052682057082172300"/> | |||
</stderr> | |||
</run> | |||
</ajc-test> | |||
<ajc-test dir="features151/serialveruid" title="horrible"> | |||
<compile files="BigHorribleClass.java,AnAspect.java" options="-1.5 -XaddSerialVersionUID -Xlint:warning"> | |||
<message kind="warning" text="calculated SerialVersionUID for type BigHorribleClass"/> | |||
</compile> | |||
<run class="BigHorribleClass"> | |||
<stderr> | |||
<line text="SerialVersionUID is 6512414869923012873"/> | |||
</stderr> | |||
</run> | |||
</ajc-test> | |||
</suite> |
@@ -114,6 +114,9 @@ public class Lint { | |||
public final Kind swallowedExceptionInCatchBlock = | |||
new Kind("swallowedExceptionInCatchBlock","exception swallowed in catch block"); | |||
public final Kind calculatingSerialVersionUID = | |||
new Kind("calculatingSerialVersionUID","calculated SerialVersionUID for type {0} to be {1}"); | |||
// there are a lot of messages in the cant find type family - I'm defining an umbrella lint warning that | |||
// allows a user to control their severity (for e.g. ltw or binary weaving) | |||
public final Kind cantFindType = |
@@ -90,6 +90,9 @@ public abstract class World implements Dump.INode { | |||
/** Flags for the new joinpoints that are 'optional' */ | |||
private boolean optionalJoinpoint_ArrayConstruction = false; // Command line flag: "arrayconstruction" | |||
private boolean addSerialVerUID = false; | |||
private Properties extraConfiguration = null; | |||
// Records whether ASM is around ... so we might use it for delegates | |||
@@ -1061,6 +1064,9 @@ public abstract class World implements Dump.INode { | |||
workInProgress1.remove(baseClass); | |||
} | |||
public void setAddSerialVerUID(boolean b) { addSerialVerUID=b;} | |||
public boolean isAddSerialVerUID() { return addSerialVerUID;} | |||
public void flush() { | |||
// System.err.println("BEFORE FLUSHING"); | |||
// System.err.println(typeMap.toString()); |
@@ -40,3 +40,4 @@ cantFindTypeAffectingJPMatch = warning | |||
unorderedAdviceAtShadow=ignore | |||
swallowedExceptionInCatchBlock=warning | |||
calculatingSerialVersionUID=ignore |
@@ -39,6 +39,7 @@ import org.aspectj.apache.bcel.classfile.Method; | |||
import org.aspectj.apache.bcel.classfile.Signature; | |||
import org.aspectj.apache.bcel.classfile.Unknown; | |||
import org.aspectj.apache.bcel.classfile.annotation.Annotation; | |||
import org.aspectj.apache.bcel.generic.BasicType; | |||
import org.aspectj.apache.bcel.generic.ClassGen; | |||
import org.aspectj.apache.bcel.generic.ConstantPoolGen; | |||
import org.aspectj.apache.bcel.generic.FieldGen; | |||
@@ -69,6 +70,7 @@ import org.aspectj.weaver.WeaverStateInfo; | |||
import org.aspectj.weaver.World; | |||
import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; | |||
/** | |||
* Lazy lazy lazy. | |||
* We don't unpack the underlying class unless necessary. Things | |||
@@ -99,6 +101,8 @@ public final class LazyClassGen { | |||
private boolean isSerializable = false; | |||
private boolean hasSerialVersionUIDField = false; | |||
private boolean serialVersionUIDRequiresInitialization = false; | |||
private long calculatedSerialVersionUID; | |||
private boolean hasClinit = false; | |||
// --- | |||
@@ -279,12 +283,28 @@ public final class LazyClassGen { | |||
hasClinit = true; | |||
} | |||
} | |||
// Do we need to calculate an SUID and add it? | |||
if (!hasSerialVersionUIDField && world.isAddSerialVerUID()) { | |||
calculatedSerialVersionUID = myGen.getSUID(); | |||
Field fg = new FieldGen( | |||
Constants.ACC_PRIVATE|Constants.ACC_FINAL|Constants.ACC_STATIC, | |||
BasicType.LONG,"serialVersionUID",getConstantPoolGen()).getField(); | |||
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); | |||
} | |||
} | |||
Method[] methods = myGen.getMethods(); | |||
for (int i = 0; i < methods.length; i++) { | |||
addMethodGen(new LazyMethodGen(methods[i], this)); | |||
} | |||
} | |||
public static boolean hasSerialVersionUIDField (ResolvedType type) { | |||
@@ -935,10 +955,21 @@ public final class LazyClassGen { | |||
// } | |||
private void addAjcInitializers() { | |||
if (tjpFields.size() == 0) return; | |||
if (tjpFields.size() == 0 && !serialVersionUIDRequiresInitialization) return; | |||
InstructionList il = null; | |||
if (tjpFields.size()>0) { | |||
il = initializeAllTjps(); | |||
} | |||
if (serialVersionUIDRequiresInitialization) { | |||
if (il==null) { | |||
il= new InstructionList(); | |||
} | |||
il.append(new PUSH(getConstantPoolGen(),calculatedSerialVersionUID)); | |||
il.append(getFactory().createFieldAccess(getClassName(), "serialVersionUID", BasicType.LONG, Constants.PUTSTATIC)); | |||
} | |||
InstructionList il = initializeAllTjps(); | |||
getStaticInitializer().getBody().insert(il); | |||
} | |||