--- /dev/null
+
+public class ArrayCloning {
+
+ public static void main(String[] args) {
+ ArrayCloning ArrayCloning = new ArrayCloning();
+ Integer[] clonedStaticField = ArrayCloning.clone1();
+ checkIdentical(clonedStaticField,ArrayCloning.staticField);
+
+ Integer[] clonedField = ArrayCloning.clone2();
+ checkIdentical(clonedField,ArrayCloning.nonStaticField);
+
+ Integer[] clown = null;
+
+ clown = ArrayCloning.clone3();
+ clown = ArrayCloning.clone4();
+ Integer[][] ArrayCloningArrayCloning = ArrayCloning.clone5();
+ }
+
+ public static void checkIdentical(Integer[] one, Integer[] two) {
+ if (one[0]!=two[0]) throw new RuntimeException("Not the same (a)");
+ if (one[1]!=two[1]) throw new RuntimeException("Not the same (b)");
+ }
+
+ private static Integer[] staticField = new Integer[2];
+
+ private Integer[] nonStaticField = new Integer[2];
+
+ public ArrayCloning() {
+ nonStaticField[0] = new Integer(32);
+ nonStaticField[1] = new Integer(64);
+ }
+
+ static {
+ staticField[0] = new Integer(1);
+ staticField[1] = new Integer(2);
+ }
+
+ public Integer[] clone1() {
+ System.err.println("Clone call on a static field");
+ return (Integer[])staticField.clone();
+ }
+
+ public Integer[] clone2() {
+ System.err.println("Clone call on a non-static field");
+ return (Integer[])nonStaticField.clone();
+ }
+
+ public Integer[] clone3() {
+ System.err.println("Clone call on a local variable");
+ Integer[] ArrayCloningArrayCloning = staticField;
+ return (Integer[])ArrayCloningArrayCloning.clone();
+ }
+
+
+ // Clone call on anonymous 'thing' !
+ public Integer[] clone4() {
+ System.err.println("Clone call on a 1 dimensional anonymous integer array");
+ return (Integer[])new Integer[5].clone();
+ }
+
+ // Sick
+ public Integer[][] clone5() {
+ System.err.println("Clone call on a 2 dimensional anonymous integer array");
+ return (Integer[][])new Integer[5][3].clone();
+ }
+
+
+}
+
+aspect HelloWorldAspect {
+ Object[] around(): call(* java.lang.Object.clone()) && within(ArrayCloning) {
+ Object[] ret = proceed();
+ return (Object[])ret.clone();
+ }
+}
import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.Field;
import org.aspectj.apache.bcel.generic.ACONST_NULL;
+import org.aspectj.apache.bcel.generic.ANEWARRAY;
import org.aspectj.apache.bcel.generic.ArrayType;
import org.aspectj.apache.bcel.generic.BranchInstruction;
import org.aspectj.apache.bcel.generic.ConstantPoolGen;
import org.aspectj.apache.bcel.generic.InstructionList;
import org.aspectj.apache.bcel.generic.InstructionTargeter;
import org.aspectj.apache.bcel.generic.InvokeInstruction;
+import org.aspectj.apache.bcel.generic.LoadInstruction;
+import org.aspectj.apache.bcel.generic.MULTIANEWARRAY;
import org.aspectj.apache.bcel.generic.NEW;
import org.aspectj.apache.bcel.generic.ObjectType;
import org.aspectj.apache.bcel.generic.ReturnInstruction;
} else {
initializeArgVars(); // gotta pop off the args before we find the target
TypeX type = getTargetType();
+ type = ensureTargetTypeIsCorrect(type);
targetVar = genTempVar(type, "ajc$target");
range.insert(targetVar.createStore(fact), Range.OutsideBefore);
targetVar.setPositionInAroundState(hasThis() ? 1 : 0);
}
}
+
+ /* PR 72528
+ * This method double checks the target type under certain conditions. The Java 1.4
+ * compilers seem to take calls to clone methods on array types and create bytecode that
+ * looks like clone is being called on Object. If we advise a clone call with around
+ * advice we extract the call into a helper method which we can then refer to. Because the
+ * type in the bytecode for the call to clone is Object we create a helper method with
+ * an Object parameter - this is not correct as we have lost the fact that the actual
+ * type is an array type. If we don't do the check below we will create code that fails
+ * java verification. This method checks for the peculiar set of conditions and if they
+ * are true, it has a sneak peek at the code before the call to see what is on the stack.
+ */
+ public TypeX ensureTargetTypeIsCorrect(TypeX tx) {
+ if (tx.equals(ResolvedTypeX.OBJECT) && getKind() == MethodCall &&
+ getSignature().getReturnType().equals(ResolvedTypeX.OBJECT) &&
+ getSignature().getArity()==0 &&
+ getSignature().getName().charAt(0) == 'c' &&
+ getSignature().getName().equals("clone")) {
+
+ // Lets go back through the code from the start of the shadow
+ InstructionHandle searchPtr = range.getStart().getPrev();
+ while (Range.isRangeHandle(searchPtr) ||
+ searchPtr.getInstruction() instanceof StoreInstruction) { // ignore this instruction - it doesnt give us the info we want
+ searchPtr = searchPtr.getPrev();
+ }
+
+ // A load instruction may tell us the real type of what the clone() call is on
+ if (searchPtr.getInstruction() instanceof LoadInstruction) {
+ LoadInstruction li = (LoadInstruction)searchPtr.getInstruction();
+ li.getIndex();
+ LocalVariableTag lvt = LazyMethodGen.getLocalVariableTag(searchPtr,li.getIndex());
+ return lvt.getType();
+ }
+ // A field access instruction may tell us the real type of what the clone() call is on
+ if (searchPtr.getInstruction() instanceof FieldInstruction) {
+ FieldInstruction si = (FieldInstruction)searchPtr.getInstruction();
+ Type t = si.getFieldType(getEnclosingClass().getConstantPoolGen());
+ return BcelWorld.fromBcel(t);
+ }
+ // A new array instruction obviously tells us it is an array type !
+ if (searchPtr.getInstruction() instanceof ANEWARRAY) {
+ //ANEWARRAY ana = (ANEWARRAY)searchPoint.getInstruction();
+ //Type t = ana.getType(getEnclosingClass().getConstantPoolGen());
+ // Just use a standard java.lang.object array - that will work fine
+ return BcelWorld.fromBcel(new ArrayType(Type.OBJECT,1));
+ }
+ // A multi new array instruction obviously tells us it is an array type !
+ if (searchPtr.getInstruction() instanceof MULTIANEWARRAY) {
+ MULTIANEWARRAY ana = (MULTIANEWARRAY)searchPtr.getInstruction();
+ // Type t = ana.getType(getEnclosingClass().getConstantPoolGen());
+ // t = new ArrayType(t,ana.getDimensions());
+ // Just use a standard java.lang.object array - that will work fine
+ return BcelWorld.fromBcel(new ArrayType(Type.OBJECT,ana.getDimensions()));
+ }
+ throw new BCException("Can't determine real target of clone() when processing instruction "+
+ searchPtr.getInstruction());
+ }
+ return tx;
+ }
+
+
public void initializeArgVars() {
if (argVars != null) return;
InstructionFactory fact = getFactory();
modifiers |= Modifier.STATIC;
if (targetVar != null && targetVar != thisVar) {
TypeX targetType = getTargetType();
+ targetType = ensureTargetTypeIsCorrect(targetType);
ResolvedMember resolvedMember = getSignature().resolve(world);
+
if (resolvedMember != null && Modifier.isProtected(resolvedMember.getModifiers()) &&
- !samePackage(targetType.getPackageName(), getEnclosingType().getPackageName()))
+ !samePackage(targetType.getPackageName(), getEnclosingType().getPackageName()) &&
+ !resolvedMember.getName().equals("clone"))
{
if (!targetType.isAssignableFrom(getThisType(), world)) {
throw new BCException("bad bytecode");