* @param outerClass the outer class whose inner classes should be redefined
* @param versionNumber the target version number
*/
- public static void toVersion(Class<?> outerClass, int versionNumber) {
+ public static void toVersion(Class<?> outerClass, int versionNumber, Class<?>... extraClasses) {
assert versionNumber >= 0;
if (versionNumber == getCurrentVersion(outerClass)) {
Map<String, File> files = findClassesWithVersion(outerClass, versionNumber);
+ for (Class<?> extra : extraClasses) {
+ if (parseClassVersion(extra.getSimpleName()) == versionNumber) {
+ String packageName = extra.getPackage().getName().replace('.', '/');
+ URL url = extra.getClassLoader().getResource(packageName);
+ if (url == null) {
+ throw new IllegalArgumentException("Cannot find URL corresponding to the package '" + packageName + "'");
+ }
+ File file = new File(url.getFile(), extra.getSimpleName() + ".class");
+ files.put(extra.getName(), file);
+ }
+ }
+
try {
Map<Class<?>, byte[]> map = buildRedefinitionMap(files);
}
File folder = new File(url.getFile());
for (File f : folder.listFiles(IsClassFile.INSTANCE)) {
- String fileName = f.getName();
String simpleName = f.getName().substring(0, f.getName().length() - CLASS_FILE_SUFFIX.length());
String name = baseClass.getPackage().getName() + '.' + simpleName;
- if (isInnerClass(name, baseClass) && parseClassVersion(fileName) == version) {
+ if (isInnerClass(name, baseClass) && parseClassVersion(simpleName) == version) {
classes.put(name, f);
}
}
/**
* Parse version of the class from the class name. Classes are named in the form of [Name]___[Version]
*/
- private static int parseClassVersion(String name) {
- int index = name.indexOf(IDENTIFIER);
+ private static int parseClassVersion(String simpleName) {
+ int index = simpleName.indexOf(IDENTIFIER);
if (index == -1) {
return 0;
}
- return Integer.valueOf(name.substring(index + IDENTIFIER.length(), name.length() - CLASS_FILE_SUFFIX.length()));
+ return Integer.valueOf(simpleName.substring(index + IDENTIFIER.length(), simpleName.length()));
}
private static String stripVersion(String className) {
import static org.junit.Assert.assertEquals;
/**
- * Tests for lambda expressions
+ * Tests for lambda expressions.
+ *
+ * These lambdas are sneaky. First, it seems like generated lambda method names are arbitrary and depend
+ * on the compilation order. However, for redefinition test we want to make sure that generated method names would
+ * actually match in old and new versions, so we have keep classes being redefined outside of this inner class.
*
* @author Ivan Dubrov
*/
__toVersion__(0);
}
- // Version 0
- public static class A {
- public Callable<Integer> createLambda() {
- return () -> 10;
- }
-
- public Callable<Integer> createLambda2() {
- return () -> 20;
- }
- }
-
- // Version 1
- public static class A___1 {
- public Callable<Integer> createLambda2() {
- return () -> 40;
- }
-
- public Callable<Integer> createLambda() {
- return () -> 30;
- }
- }
-
@Test
- @Ignore
public void testMethodLambda() throws Exception {
- A a = new A();
+ LambdaA a = new LambdaA();
Callable<Integer> lambda = a.createLambda();
Callable<Integer> lambda2 = a.createLambda2();
assertEquals(10, (int) lambda.call());
assertEquals(20, (int) lambda2.call());
- __toVersion__(1);
+ __toVersion__(1, LambdaA___1.class);
assertEquals(30, (int) lambda.call());
assertEquals(40, (int) lambda2.call());