diff --git a/org.springsource.loaded.testdata/src/invokestatic/issue4/A.java b/org.springsource.loaded.testdata/src/invokestatic/issue4/A.java new file mode 100644 index 0000000..2d4e957 --- /dev/null +++ b/org.springsource.loaded.testdata/src/invokestatic/issue4/A.java @@ -0,0 +1,8 @@ +package invokestatic.issue4; + +public class A { + + public static String getString() { + return "String1"; + } +} diff --git a/org.springsource.loaded.testdata/src/invokestatic/issue4/AA.java b/org.springsource.loaded.testdata/src/invokestatic/issue4/AA.java new file mode 100644 index 0000000..6c40448 --- /dev/null +++ b/org.springsource.loaded.testdata/src/invokestatic/issue4/AA.java @@ -0,0 +1,8 @@ +package invokestatic.issue4; + +public class AA { + + public static String getString() { + return "String1"; + } +} diff --git a/org.springsource.loaded.testdata/src/invokestatic/issue4/AAA.java b/org.springsource.loaded.testdata/src/invokestatic/issue4/AAA.java new file mode 100644 index 0000000..7ffecb2 --- /dev/null +++ b/org.springsource.loaded.testdata/src/invokestatic/issue4/AAA.java @@ -0,0 +1,8 @@ +package invokestatic.issue4; + +public class AAA { + + public static String getString() { + return "String1"; + } +} diff --git a/org.springsource.loaded.testdata/src/invokestatic/issue4/B.java b/org.springsource.loaded.testdata/src/invokestatic/issue4/B.java new file mode 100644 index 0000000..317e1e3 --- /dev/null +++ b/org.springsource.loaded.testdata/src/invokestatic/issue4/B.java @@ -0,0 +1,9 @@ +package invokestatic.issue4; + +public class B extends A { + + public static String getMessage() { + return getString(); + } + +} diff --git a/org.springsource.loaded.testdata/src/invokestatic/issue4/BB.java b/org.springsource.loaded.testdata/src/invokestatic/issue4/BB.java new file mode 100644 index 0000000..36073fe --- /dev/null +++ b/org.springsource.loaded.testdata/src/invokestatic/issue4/BB.java @@ -0,0 +1,9 @@ +package invokestatic.issue4; + +public class BB extends AA { + + public static String getMessage() { + return BB.getString(); + } + +} diff --git a/org.springsource.loaded.testdata/src/invokestatic/issue4/BBB.java b/org.springsource.loaded.testdata/src/invokestatic/issue4/BBB.java new file mode 100644 index 0000000..a9a150b --- /dev/null +++ b/org.springsource.loaded.testdata/src/invokestatic/issue4/BBB.java @@ -0,0 +1,9 @@ +package invokestatic.issue4; + +public class BBB extends AAA { + + public static String getMessage() { + return AAA.getString(); + } + +} diff --git a/org.springsource.loaded/src/main/java/org/springsource/loaded/TypeRegistry.java b/org.springsource.loaded/src/main/java/org/springsource/loaded/TypeRegistry.java index ccf7215..c706bbf 100644 --- a/org.springsource.loaded/src/main/java/org/springsource/loaded/TypeRegistry.java +++ b/org.springsource.loaded/src/main/java/org/springsource/loaded/TypeRegistry.java @@ -1115,7 +1115,37 @@ public class TypeRegistry { .getFromLatestByDescriptor(nameAndDescriptor); boolean dispatchThroughDescriptor = false; if (method == null) { - // method has been deleted + // method has been deleted or is on a supertype + + // Search supertypes - block copied from invokespecialSearch below, TODO could refactor into common util + Object dispatcherToUse = null; + ReloadableType nextCandidate = reloadableType.getTypeRegistry().getReloadableType(reloadableType.getTypeDescriptor().getSupertypeName(),false); + boolean found = false; + while (nextCandidate != null) { + MethodMember m = null; + if (nextCandidate.hasBeenReloaded()) { + m = nextCandidate.getLiveVersion().incrementalTypeDescriptor.getFromLatestByDescriptor(nameAndDescriptor); + if (m != null && IncrementalTypeDescriptor.wasDeleted(m)) { + m = null; + } + // ignore catchers because the dynamic __execute method wont have an implementation of them, we should + // just keep looking for the real thing + if (m != null && MethodMember.isCatcher(m)) { + m = null; + } + } else { + m = nextCandidate.getMethod(nameAndDescriptor); + } + if (m != null) { + dispatcherToUse = reloadableType.getLatestDispatcherInstance(); + found = true; + break; + } + nextCandidate = reloadableType.getTypeRegistry().getReloadableType(reloadableType.getTypeDescriptor().getSupertypeName(),false); + } + if (found) { + return dispatcherToUse; + } throw new NoSuchMethodError(reloadableType.getBaseName() + "." + nameAndDescriptor); } else if (IncrementalTypeDescriptor.isBrandNewMethod(method)) { // definetly need to use the dispatcher diff --git a/org.springsource.loaded/src/test/java/org/springsource/loaded/test/ReloadableTypeTests.java b/org.springsource.loaded/src/test/java/org/springsource/loaded/test/ReloadableTypeTests.java index b716db6..7948979 100644 --- a/org.springsource.loaded/src/test/java/org/springsource/loaded/test/ReloadableTypeTests.java +++ b/org.springsource.loaded/src/test/java/org/springsource/loaded/test/ReloadableTypeTests.java @@ -166,6 +166,70 @@ public class ReloadableTypeTests extends SpringLoadedTests { assertEquals(3, runOnInstance(rtype.getClazz(), instance, "getProtectedStaticField").returnValue); } + + + // github issue 4 + @Test + public void invokeStaticReloading_gh4_1() throws Exception { + TypeRegistry tr = getTypeRegistry("invokestatic..*"); + ReloadableType A = tr.addType("invokestatic.issue4.A", loadBytesForClass("invokestatic.issue4.A")); + ReloadableType B = tr.addType("invokestatic.issue4.B", loadBytesForClass("invokestatic.issue4.B")); + + Result r = runUnguarded(B.getClazz(), "getMessage"); + assertEquals("String1",(String)r.returnValue); + + B.loadNewVersion(B.bytesInitial); + + r = runUnguarded(B.getClazz(), "getMessage"); + assertEquals("String1",(String)r.returnValue); + } + + @Test + public void invokeStaticReloading_gh4_2() throws Exception { + TypeRegistry tr = getTypeRegistry("invokestatic..*"); + ReloadableType AA = tr.addType("invokestatic.issue4.AA", loadBytesForClass("invokestatic.issue4.AA")); + ReloadableType BB = tr.addType("invokestatic.issue4.BB", loadBytesForClass("invokestatic.issue4.BB")); + + Result r = runUnguarded(BB.getClazz(), "getMessage"); + assertEquals("String1",(String)r.returnValue); + + BB.loadNewVersion(BB.bytesInitial); + + r = runUnguarded(BB.getClazz(), "getMessage"); + assertEquals("String1",(String)r.returnValue); + } + + @Test + public void invokeStaticReloading_gh4_3() throws Exception { + TypeRegistry tr = getTypeRegistry("invokestatic..*"); + ReloadableType AAA = tr.addType("invokestatic.issue4.AAA", loadBytesForClass("invokestatic.issue4.AAA")); + ReloadableType BBB = tr.addType("invokestatic.issue4.BBB", loadBytesForClass("invokestatic.issue4.BBB")); + + Result r = runUnguarded(BBB.getClazz(), "getMessage"); + assertEquals("String1",(String)r.returnValue); + + AAA.loadNewVersion(AAA.bytesInitial); + + r = runUnguarded(BBB.getClazz(), "getMessage"); + assertEquals("String1",(String)r.returnValue); + } + + @Test + public void invokeStaticReloading_gh4_4() throws Exception { + TypeRegistry tr = getTypeRegistry("invokestatic..*"); + ReloadableType A = tr.addType("invokestatic.issue4.A", loadBytesForClass("invokestatic.issue4.A")); + ReloadableType B = tr.addType("invokestatic.issue4.B", loadBytesForClass("invokestatic.issue4.B")); + + Result r = runUnguarded(B.getClazz(), "getMessage"); + assertEquals("String1",(String)r.returnValue); + + A.loadNewVersion(A.bytesInitial); + B.loadNewVersion(B.bytesInitial); + + r = runUnguarded(B.getClazz(), "getMessage"); + assertEquals("String1",(String)r.returnValue); + } + @Test public void protectedFieldAccessors2() throws Exception {