Skip to content

Commit

Permalink
[jOOQ#117] Try loading super types until it works
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaseder committed Nov 23, 2021
1 parent a5d6dd7 commit a0122da
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 12 deletions.
27 changes: 27 additions & 0 deletions jOOR-java-6/src/main/java/org/joor/Compile.java
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,33 @@

































Expand Down
51 changes: 51 additions & 0 deletions jOOR-java-6/src/test/java/org/joor/test/CompileTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,57 @@

























































Expand Down
35 changes: 31 additions & 4 deletions jOOR-java-8/src/main/java/org/joor/Compile.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -246,10 +248,35 @@ Map<String, byte[]> classes() {
Class<?> loadAndReturnMainClass(String mainClassName, ThrowingBiFunction<String, byte[], Class<?>> definer) throws Exception {
Class<?> result = null;

for (Entry<String, byte[]> entry : classes().entrySet()) {
Class<?> c = definer.apply(entry.getKey(), entry.getValue());
if (mainClassName.equals(entry.getKey()))
result = c;
// [#117] We don't know the subclass hierarchy of the top level
// classes in the compilation unit, and we can't find out
// without either:
//
// - class loading them (which fails due to NoClassDefFoundError)
// - using a library like ASM (which is a big and painful dependency)
//
// Simple workaround: try until it works, in O(n^2), where n
// can be reasonably expected to be small.
Deque<Entry<String, byte[]>> queue = new ArrayDeque<>(classes().entrySet());
int n1 = queue.size();

// Try at most n times
for (int i1 = 0; i1 < n1 && !queue.isEmpty(); i1++) {
int n2 = queue.size();

for (int i2 = 0; i2 < n2; i2 ++) {
Entry<String, byte[]> entry = queue.pop();

try {
Class<?> c = definer.apply(entry.getKey(), entry.getValue());

if (mainClassName.equals(entry.getKey()))
result = c;
}
catch (ReflectException e) {
queue.offer(entry);
}
}
}

return result;
Expand Down
55 changes: 53 additions & 2 deletions jOOR-java-8/src/test/java/org/joor/test/CompileTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public void testCompileWithClasspathDependency() throws Exception {
assertEquals(new ArrayList<Object>(), v.validateTestClass(null));
}

@Test
@Test
public void testCompileLocalInterfaceHierarchy() throws Exception {
I i = Reflect.compile("org.joor.test.CompileTest1", "package org.joor.test; public class CompileTest1 implements org.joor.test.I {}").create().get();
assertEquals("I.m()", i.m());
Expand Down Expand Up @@ -86,7 +86,7 @@ public void testCompileDifferentPackage() {
assertEquals("Hello World!", supplier.get());
}

@Test
@Test
public void testRecompileSameClassName() {

// The class loader will cache the class name by default, so a new content shouldn't affect the type
Expand Down Expand Up @@ -174,6 +174,57 @@ public void testCompileTopLevelClasses() {
int foo = Reflect.onClass(c).create().call("other").call("foo").get();
assertEquals(42, foo);
}

@Test
public void testClassLoadingOrder1() {
Class<?> a;

a = Reflect.compile("p.A", "package p; public class A extends B {} class B {}").type();
assertEquals("p.A", a.getName());
assertEquals("p.B", a.getSuperclass().getName());

a = Reflect.compile("p.A", "package p; class B {} public class A extends B {}").type();
assertEquals("p.A", a.getName());
assertEquals("p.B", a.getSuperclass().getName());
}

@Test
public void testClassLoadingOrder2() {
Class<?> d;

d = Reflect.compile("p.D", "package p; public class D extends B {} class B {}").type();
assertEquals("p.D", d.getName());
assertEquals("p.B", d.getSuperclass().getName());

d = Reflect.compile("p.D", "package p; class B {} public class D extends B {}").type();
assertEquals("p.D", d.getName());
assertEquals("p.B", d.getSuperclass().getName());
}

























}

interface I extends J {
Expand Down
35 changes: 31 additions & 4 deletions jOOR/src/main/java/org/joor/Compile.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -246,10 +248,35 @@ Map<String, byte[]> classes() {
Class<?> loadAndReturnMainClass(String mainClassName, ThrowingBiFunction<String, byte[], Class<?>> definer) throws Exception {
Class<?> result = null;

for (Entry<String, byte[]> entry : classes().entrySet()) {
Class<?> c = definer.apply(entry.getKey(), entry.getValue());
if (mainClassName.equals(entry.getKey()))
result = c;
// [#117] We don't know the subclass hierarchy of the top level
// classes in the compilation unit, and we can't find out
// without either:
//
// - class loading them (which fails due to NoClassDefFoundError)
// - using a library like ASM (which is a big and painful dependency)
//
// Simple workaround: try until it works, in O(n^2), where n
// can be reasonably expected to be small.
Deque<Entry<String, byte[]>> queue = new ArrayDeque<>(classes().entrySet());
int n1 = queue.size();

// Try at most n times
for (int i1 = 0; i1 < n1 && !queue.isEmpty(); i1++) {
int n2 = queue.size();

for (int i2 = 0; i2 < n2; i2 ++) {
Entry<String, byte[]> entry = queue.pop();

try {
Class<?> c = definer.apply(entry.getKey(), entry.getValue());

if (mainClassName.equals(entry.getKey()))
result = c;
}
catch (ReflectException e) {
queue.offer(entry);
}
}
}

return result;
Expand Down
55 changes: 53 additions & 2 deletions jOOR/src/test/java/org/joor/test/CompileTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public void testCompileWithClasspathDependency() throws Exception {
assertEquals(new ArrayList<Object>(), v.validateTestClass(null));
}

@Test /* [java-9] */ (expected = Throwable.class) // [#77] /* [/java-9] */
@Test /* [java-9] */ (expected = Throwable.class) // [#77] /* [/java-9] */
public void testCompileLocalInterfaceHierarchy() throws Exception {
I i = Reflect.compile("org.joor.test.CompileTest1", "package org.joor.test; public class CompileTest1 implements org.joor.test.I {}").create().get();
assertEquals("I.m()", i.m());
Expand Down Expand Up @@ -86,7 +86,7 @@ public void testCompileDifferentPackage() {
assertEquals("Hello World!", supplier.get());
}

@Test /* [java-9] */ (expected = Throwable.class) // [#76] /* [/java-9] */
@Test/* [java-9] */ (expected = Throwable.class) // [#76] /* [/java-9] */
public void testRecompileSameClassName() {

// The class loader will cache the class name by default, so a new content shouldn't affect the type
Expand Down Expand Up @@ -174,6 +174,57 @@ public void testCompileTopLevelClasses() {
int foo = Reflect.onClass(c).create().call("other").call("foo").get();
assertEquals(42, foo);
}

@Test
public void testClassLoadingOrder1() {
Class<?> a;

a = Reflect.compile("p.A", "package p; public class A extends B {} class B {}").type();
assertEquals("p.A", a.getName());
assertEquals("p.B", a.getSuperclass().getName());

a = Reflect.compile("p.A", "package p; class B {} public class A extends B {}").type();
assertEquals("p.A", a.getName());
assertEquals("p.B", a.getSuperclass().getName());
}

@Test
public void testClassLoadingOrder2() {
Class<?> d;

d = Reflect.compile("p.D", "package p; public class D extends B {} class B {}").type();
assertEquals("p.D", d.getName());
assertEquals("p.B", d.getSuperclass().getName());

d = Reflect.compile("p.D", "package p; class B {} public class D extends B {}").type();
assertEquals("p.D", d.getName());
assertEquals("p.B", d.getSuperclass().getName());
}

/* [java-9] */

// This test seems to fail in Java 8. p.B doesn't correctly extend p.C
@Test
public void testClassLoadingOrder3() {
Class<?> a = Reflect.compile("p.A", "package p; "
+ "public class A extends B {} "
+ "class B extends C {} "
+ "class C implements D {} "
+ "interface D extends E {} "
+ "interface E {}").type();

Class<?> b = a.getSuperclass();
Class<?> c = b.getSuperclass();
assertEquals("p.A", a.getName());
assertEquals("p.B", b.getName());
assertEquals("p.C", c.getName());
assertEquals(1, c.getInterfaces().length);
assertEquals("p.D", c.getInterfaces()[0].getName());
assertEquals(1, c.getInterfaces()[0].getInterfaces().length);
assertEquals("p.E", c.getInterfaces()[0].getInterfaces()[0].getName());
}

/* [/java-9] */
}

interface I extends J {
Expand Down

0 comments on commit a0122da

Please sign in to comment.