/*******************************************************************************
* Copyright (c) 2008 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:
* Andy Clement - initial API and implementation
*******************************************************************************/
package org.aspectj.systemtest.ajc169;
import java.lang.reflect.Modifier;
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.annotation.AnnotationGen;
import org.aspectj.testing.XMLBasedAjcTestCase;
import junit.framework.Test;
/**
* What would a completely transparent weave be? Is there a minimal subset that makes sense? What is the roadmap to get there? What
* needs testing
*
* --- 'Transparent' here is meaning that the resultant bytecode is more representative of the original declarations, so that it
* looks like the intertype declaration and associated constructs have been seamless added to the affected targets.
*
*
* Fully transparent weaving, what would we like to have: - ITDs appear exactly as declared: 'private int A.i' will create 'private
* int i' in A
*
* - What is the benefit? - although this isn't really in keeping with the AspectJ definition of what an ITD represents, having the
* end result look like the declaration does make it easier for users simply looking at the resultant class file or attempting
* reflection to access what they just ITD'd in place
*
*
* testing For transparent weaving of ITD fields - annotations on new fields - AJDT model - AjType support - what happens to it? -
* advice on within() how does that get affected? - visibility - accessors created when required? - handling clashes with existing
* fields - handling clashes with other aspects - generic declarations - interface declarations - initializers - static and
* non-static - accessibility from advice, for read and write
*
* Design
* The intention will be 'new code' uses the new style whilst old code continues to cause the old code to be built. Whether the code
* wants to use the old or new naming should be apparent from the
*
* @author Andy Clement
*/
public class TransparentWeavingTests extends org.aspectj.testing.XMLBasedAjcTestCase {
// Simple private ITD onto a target
public void testSimplePrivate() throws Exception {
runTest("one - private");
checkForField("OnePrivate", Modifier.PRIVATE, "x");
}
// Default visibility ITD field
public void testSimpleDefault() throws Exception {
runTest("one - default");
checkForField("OneDefault", 0, "x");
}
// annotated private ITD
public void testSimplePrivateAnnotated() throws Exception {
runTest("one - private - annotated");
Field f = checkForField("OnePrivateAnnotated", Modifier.PRIVATE, "x");
AnnotationGen[] annos = f.getAnnotations();
assertTrue(annos.length > 0); // 0==Anno 1==ajcITD
assertEquals("LAnno;", annos[0].getTypeSignature());
}
// annotated default ITD
public void testSimpleDefaultAnnotated() throws Exception {
runTest("one - default - annotated");
Field f = checkForField("OneDefaultAnnotated", 0, "x");
AnnotationGen[] annos = f.getAnnotations();
assertTrue(annos.length > 0); // 0==Anno 1==ajcITD
assertEquals("LAnno;", annos[0].getTypeSignature());
}
// Simple private ITD with getter/setter usage
public void testSimplePrivateWithAccessors() throws Exception {
runTest("one - private - accessors");
}
// check initializer runs OK
public void testSimplePrivateInitializer() throws Exception {
runTest("one - private - initializer");
}
public void testDeclareAtOnPrivateItd() throws Exception {
runTest("declare at on private itd");
Field f = checkForField("OneDeclareAt", Modifier.PRIVATE, "x");
AnnotationGen[] annos = f.getAnnotations();
assertTrue(annos.length > 0); // 1==Anno 0==ajcITD
assertEquals("LAnno;", annos[1].getTypeSignature());
}
// declare @field on a field that already has one
public void testDeclareAtTwo() throws Exception {
runTest("declare at two");
Field f = checkForField("DeclareAtTwo", Modifier.PRIVATE, "x");
AnnotationGen[] annos = f.getAnnotations();
assertTrue(annos.length > 2); // 1==Anno 0==ajcITD
assertEquals("LAnno;", annos[0].getTypeSignature());
assertEquals("LAnno2;", annos[2].getTypeSignature());
}
public void testTwoItdsOnTarget() throws Exception {
runTest("two itds on target");
// Aspect X gets the field, aspect Y gets a mangled one
if (hasField("TwoItdsOnTarget", "ajc$interField$Y$x")) {
checkForField("TwoItdsOnTarget", Modifier.PRIVATE, "x");
checkForField("TwoItdsOnTarget", Modifier.PUBLIC, "ajc$interField$Y$x");
} else {
checkForField("TwoItdsOnTarget", Modifier.PRIVATE, "x");
checkForField("TwoItdsOnTarget", Modifier.PUBLIC, "ajc$interField$X$x");
}
}
public void testTwoItdsOnTargetThatAlreadyHasIt() throws Exception {
runTest("two itds on target that already has it");
// Aspect X gets the field, aspect Y gets a mangled one
checkForField("TwoItdsOnTargetHasAlready", Modifier.PUBLIC, "ajc$interField$X$x");
checkForField("TwoItdsOnTargetHasAlready", Modifier.PUBLIC, "ajc$interField$Y$x");
}
public void testInteractingOldAndNew() throws Exception {
runTest("interacting old and new");
int SYNTHETIC = 0x00001000; // 4096
if (hasField("InteractingOldAndNew", "ajc$interField$Y$i")) {
checkForField("InteractingOldAndNew", Modifier.PRIVATE, "i");
checkForField("InteractingOldAndNew", Modifier.PUBLIC, "ajc$interField$Y$i");
} else {
checkForField("InteractingOldAndNew", Modifier.PRIVATE, "i");
checkForField("InteractingOldAndNew", Modifier.PUBLIC, "ajc$interField$X$i");
}
checkForMethod("InteractingOldAndNew", Modifier.PUBLIC | Modifier.STATIC, "main");
checkForMethod("InteractingOldAndNew", Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC, "ajc$get$i");
checkForMethod("InteractingOldAndNew", Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC, "ajc$set$i");
checkForMethod("InteractingOldAndNew", Modifier.PUBLIC, "getI1");
checkForMethod("InteractingOldAndNew", Modifier.PUBLIC, "getI2");
checkForMethod("InteractingOldAndNew", Modifier.PUBLIC, "setI1");
checkForMethod("InteractingOldAndNew", Modifier.PUBLIC, "setI2");
}
public void testPrivateGenerics() throws Exception {
runTest("generics - private");
Field f = checkForField("Generics", Modifier.PRIVATE, "listOfString");
assertEquals("Ljava/util/List;", f.getGenericSignature());
f = checkForField("Generics", Modifier.PRIVATE, "thing");
assertEquals("TX;", f.getGenericSignature());
}
// ---
private boolean hasField(String clazzname, String name) {
try {
JavaClass jc = getClassFrom(ajc.getSandboxDirectory(), clazzname);
Field[] fs = jc.getFields();
StringBuilder fields = new StringBuilder();
for (Field f : fs) {
fields.append(f.getName()).append(" ");
if (f.getName().equals(name)) {
return true;
}
}
} catch (Exception e) {
return false;
}
return false;
}
private Field checkForField(String clazzname, int modifiers, String name) throws Exception {
JavaClass jc = getClassFrom(ajc.getSandboxDirectory(), clazzname);
Field[] fs = jc.getFields();
StringBuilder fields = new StringBuilder();
for (Field f : fs) {
fields.append(f.getName()).append(" ");
if (f.getName().equals(name)) {
if (f.getModifiers() != modifiers) {
fail("Found field " + name + " in " + clazzname + " but modifiers were wrong, they were " + f.getModifiers());
}
return f;
}
}
fail("Did not find field " + name + " in class " + clazzname + ". Found fields: " + fields.toString());
return null;
}
private Method checkForMethod(String clazzname, int modifiers, String name) throws Exception {
JavaClass jc = getClassFrom(ajc.getSandboxDirectory(), clazzname);
Method[] fs = jc.getMethods();
StringBuilder methods = new StringBuilder();
methods.append("\n");
for (Method f : fs) {
methods.append(f.getName()).append("\n");
if (f.getName().equals(name)) {
if (f.getModifiers() != modifiers) {
fail("Found method " + name + " in " + clazzname + " but modifiers were wrong, they were " + f.getModifiers());
}
return f;
}
System.out.println(f.getGenericSignature());
}
fail("Did not find method " + name + " in class " + clazzname + ". Found methods: " + methods.toString());
return null;
}
// public itd onto a target that already has a field of that name
// just to check what goes wrong and who checks it
public void testPublicClash() throws Exception {
runTest("two");
}
public void testPrivateClash() throws Exception {
runTest("three");
JavaClass jc = getClassFrom(ajc.getSandboxDirectory(), "Three");
Field[] fs = jc.getFields();
for (Field f : fs) {
System.out.println(f);
}
// public int ajc$interField$X$xPrivate [RuntimeVisibleAnnotations]
// public Integer ajc$interField$$yDefault [RuntimeVisibleAnnotations]
// public String zPublic [RuntimeVisibleAnnotations]
}
// --
public static Test suite() {
return XMLBasedAjcTestCase.loadSuite(TransparentWeavingTests.class);
}
@Override
protected java.net.URL getSpecFile() {
return getClassResource("transparentweaving.xml");
}
}