SynchronizationTransformTests.java
/*******************************************************************************
* Copyright (c) 2006 IBM Corporation and others.
* 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 implementation
*******************************************************************************/
package org.aspectj.systemtest.ajc152;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.PrintStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.apache.bcel.classfile.Method;
import org.aspectj.testing.XMLBasedAjcTestCase;
import org.aspectj.testing.util.TestUtil;
import org.aspectj.testing.util.TestUtil.LineStream;
import org.aspectj.weaver.ReferenceType;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.World;
import org.aspectj.weaver.bcel.BcelObjectType;
import org.aspectj.weaver.bcel.BcelWorld;
import org.aspectj.weaver.bcel.LazyClassGen;
import org.aspectj.weaver.bcel.LazyMethodGen;
import junit.framework.Test;
/**
* Method transformation, example:
*
* public synchronized void m(); Code: Stack=2, Locals=1, Args_size=1 0: getstatic #2; //Field
* java/lang/System.err:Ljava/io/PrintStream; 3: ldc #3; //String hello 5: invokevirtual #4; //Method
* java/io/PrintStream.println:(Ljava/lang/String;)V 8: getstatic #2; //Field java/lang/System.err:Ljava/io/PrintStream; 11: ldc #5;
* //String world 13: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 16: return LineNumberTable: line
* 4: 0 line 5: 8 line 6: 16
*
* public void m2(); Code: Stack=2, Locals=3, Args_size=1 0: aload_0 1: dup 2: astore_1 3: monitorenter 4: getstatic #2; //Field
* java/lang/System.err:Ljava/io/PrintStream; 7: ldc #3; //String hello 9: invokevirtual #4; //Method
* java/io/PrintStream.println:(Ljava/lang/String;)V 12: getstatic #2; //Field java/lang/System.err:Ljava/io/PrintStream; 15: ldc
* #5; //String world 17: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 20: aload_1 21: monitorexit
* 22: goto 30 25: astore_2 26: aload_1 27: monitorexit 28: aload_2 29: athrow 30: return Exception table: from to target type 4 22
* 25 any 25 28 25 any
*/
public class SynchronizationTransformTests extends XMLBasedAjcTestCase {
private static boolean regenerate;
static {
regenerate = false;
}
private World world;
public void testInvestigatingTransforming() {
runTest("investigation");
checkMethod("Investigation", "b"); // similar output to One.b
checkMethod("Investigation", "c");
checkMethod("Investigation", "d");
checkMethod("Investigation", "e");
}
public void testTransform1() {
runTest("One");
checkMethod("One", "b");
checkMethod("One", "c");
checkMethod("One", "e");
}
// before() on execution jp
public void testTransform2() {
runTest("Two");
checkMethod("C", "ma");
}
public void testTransform2XlintOff() {
runTest("Two - xlintoff");
checkMethod("C", "ma");
}
// after() returning/after() throwing on execution jp
// after() returning -> make all returns go through the same exit point and make
// it call the advice
// after() throwing -> add a catch block that calls the advice
public void testTransform3() {
runTest("Three");
checkMethod("C", "m3");
checkMethod("C", "m32");
checkMethod("C", "m33"); // like m() but synchronized block
checkMethod("C", "m34"); // like m2() but synchronized block
}
// like testTransform3() but pointcuts explicitly specify synchronized
public void testTransform4() {
runTest("Four");
checkMethod("C", "m");
checkMethod("C", "m2");
}
// Java5 variant
public void testStaticSynchronizedMethodTransformJava5() {
runTest("Five - Java5");
checkMethod("C", "b");
}
public void testLockPcdOnTransformedNonStaticMethod() {
runTest("lock pcd on transformed non-static method");
}
public void testUnlockPcdOnTransformedNonStaticMethod() {
runTest("unlock pcd on transformed non-static method");
}
public void testLockPcdOnTransformedStaticMethod() {
runTest("lock pcd on transformed static method - J5");
}
public void testUnlockPcdOnTransformedStaticMethod() {
runTest("unlock pcd on transformed static method - J5");
}
public void testLockPcdOnTransformedStaticMethodPreJ5() {
runTest("lock pcd on transformed static method - preJ5");
}
public void testUnlockPcdOnTransformedStaticMethodPreJ5() {
runTest("unlock pcd on transformed static method - preJ5");
}
public void testJoinpointsEnabledButNoLock() {
runTest("joinpoints enabled but no lock");
}
public void testTransformWithLTW() {
runTest("transform with LTW");
}
public void testTransformStaticMethodPreJava5() {
runTest("transform static method - preJ5");
}
public void testTransformStaticMethodPreJava5_2() {
runTest("transform static method - packages - preJ5");
}
// more complex code sequences...
public void testOtherTargeters() {
runTest("other targeters");
}
// --- infrastructure below
private void checkMethod(String typename, String methodname) {
LazyMethodGen m = getMethod(typename, methodname);
File expectedF = new File(".." + File.separator + "tests" + File.separator + "features152" + File.separator
+ "synchronization" + File.separator + "transformed" + File.separator + "expected" + File.separator + typename
+ "." + methodname + ".txt");
if (regenerate) {
saveMethod(expectedF, m);
} else {
compareMethod(expectedF, m);
}
}
private LazyMethodGen getMethod(String typename, String methodname) {
BcelObjectType type = getBcelObjectFor(typename);
LazyClassGen lcg = type.getLazyClassGen();
List<LazyMethodGen> methods = lcg.getMethodGens();
for (LazyMethodGen element: methods) {
if (element.getName().equals(methodname)) {
return element;
}
}
return null;
}
private BcelObjectType getBcelObjectFor(String clazzname) {
ensureWorldSetup();
ResolvedType rt = world.resolve(clazzname);
if (rt == null)
fail("Couldn't find class " + clazzname);
ReferenceType rtt = (ReferenceType) rt;
BcelObjectType bot = (BcelObjectType) rtt.getDelegate();
return bot;
}
private void ensureWorldSetup() {
if (world == null) {
world = new BcelWorld(getSandboxDirectory() + File.pathSeparator + System.getProperty("java.class.path"));
}
}
protected Method getMethod(JavaClass cl, String methodname) {
Method[] methods = cl.getMethods();
for (Method m : methods) {
if (m.getName().equals(methodname)) {
return m;
}
}
return null;
}
public void dump(String title, String[] strs) {
System.err.println(title);
for (int i = 0; i < strs.length; i++) {
System.err.println(i + ") " + strs[i]);
}
}
private void compareMethod(File f, LazyMethodGen m) {
BufferedReader fr;
if (!f.exists()) {
fail("Can't find expected output file " + f);
}
try {
// Load the file in
fr = new BufferedReader(new FileReader(f));
String line = null;
List<String> originalFileContents = new ArrayList<>();
while ((line = fr.readLine()) != null)
originalFileContents.add(line);
String[] fileContents = (String[]) originalFileContents.toArray(new String[] {});
LineStream ls = new TestUtil.LineStream();
m.print(ls, null);
String[] lines = ls.getLines();
for (int i = 0; i < lines.length; i++) {
String existingLine = lines[i];
if (!fileContents[i].contains("MethodDeclarationLineNumber") && !fileContents[i].equals(existingLine)) {
dump("File contents:", fileContents);
dump("Actual:", lines);
fail("\nDifference in method " + m.getName() + " on line " + i + " between the expected:\n" + fileContents[i]
+ "\nand the found:\n" + existingLine);
}
}
} catch (Exception e) {
fail("Unexpected exception saving weaving messages:" + e);
}
}
private String stringify(List<String> l) {
StringBuilder result = new StringBuilder();
for (String str : l) {
result.append(str);
result.append("\n");
}
return result.toString();
}
private void saveMethod(File f, LazyMethodGen m) {
System.out.println("Saving method into " + f.getName());
try {
m.print(new PrintStream(new FileOutputStream(f)), null);
} catch (FileNotFoundException e) {
e.printStackTrace();
fail("Couldn't store the method in file " + f);
}
}
// --- helpers
// Half finished - could check there is only one relationship for unlock() rather than two - but
// that seems to be the case anyway (peculiar...)
// private void checkModel1() {
// // Verifies only one unlock relationship, not two
// IProgramElement unlockNode =
// AsmManager.getDefault().getHierarchy().findElementForLabel(AsmManager.getDefault().getHierarchy().getRoot(),
// IProgramElement.Kind.CODE,"unlock(void java.lang.Object.<unlock>(java.lang.Object))");
// assertTrue("Couldn't find the unlock node",unlockNode!=null);
// List l = AsmManager.getDefault().getRelationshipMap().get(unlockNode);
// assertTrue("should be one entry :"+l,l!=null && l.size()==1);
// IRelationship ir = (IRelationship)l.get(0);
// System.err.println(ir);
// List targs = ir.getTargets();
// System.err.println(targs.size());
// System.err.println(targs.get(0));
// }
// ---
public static Test suite() {
return XMLBasedAjcTestCase.loadSuite(SynchronizationTransformTests.class);
}
protected URL getSpecFile() {
return getClassResource("synchronization.xml");
}
public void tearDown() {
world = null;
}
}