diff --git a/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java b/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java index 2d55711..93423ae 100644 --- a/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java +++ b/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java @@ -95,11 +95,17 @@ public class ReloadableType { private Class superclazz; private ReloadableType superRtype; + private ReloadableType[] interfaceRtypes; + + List> associatedSubtypes = null; /** Caches Method objects for this reloadable type. This cache should be invalidated (set to null) when a type is reloaded! */ private JavaMethodCache javaMethodCache; private final static int IS_RESOLVED = 0x0001; + + // Indicates that this type or one in its hierarchy (super/sub) has been reloaded + private final static int IMPACTED_BY_RELOAD = 0x0002; private int bits; @@ -402,16 +408,64 @@ public class ReloadableType { } else { liveVersion.staticInitializedNeedsRerunningOnDefine = false; } + // For performance: + // - tag the relevant types that may have been affected by this being reloaded, i.e. this type and any reloadable types in the same hierachy + tagAsAffectedByReload(); + tagSupertypesAsAffectedByReload(); + tagSubtypesAsAffectedByReload(); + typeRegistry.fireReloadEvent(this, versionsuffix); reloadProxiesIfNecessary(versionsuffix); - } - // dump(newbytedata); + // dump(newbytedata); return reload; } + private void tagSupertypesAsAffectedByReload() { + ReloadableType superRtype = getSuperRtype(); + if (superRtype!=null) { + superRtype.tagAsAffectedByReload(); + // need to recurse up with the tagging + superRtype.tagSupertypesAsAffectedByReload(); + } + + // need to recurse through super interfaces too + ReloadableType[] superinterfaceRtypes = getInterfacesRtypes(); + if (superinterfaceRtypes!=null) { + for (ReloadableType superinterfaceRtype: superinterfaceRtypes) { + superinterfaceRtype.tagAsAffectedByReload(); + superinterfaceRtype.tagSupertypesAsAffectedByReload(); + } + } + } + + // TODO who is clearing up dead entries? + private void tagSubtypesAsAffectedByReload() { + if (associatedSubtypes !=null) { + for (Reference ref: associatedSubtypes) { + ReloadableType rsubtype = ref.get(); + if (rsubtype != null) { + rsubtype.tagAsAffectedByReload(); + rsubtype.tagSubtypesAsAffectedByReload(); + } + } + } + } + + private void tagAsAffectedByReload() { + bits |= IMPACTED_BY_RELOAD; + invokersCache_getMethods = null; + invokersCache_getDeclaredMethods = null; + } + + public boolean isAffectedByReload() { + return (bits&IMPACTED_BY_RELOAD)!=0; + } + + + // TODO cache these field objects to avoid digging for them every time? /** * When an enum type is reloaded, two caches need to be cleared out from the Class object for the enum type. @@ -984,6 +1038,10 @@ public class ReloadableType { public String getSlashedSupertypeName() { return getTypeDescriptor().getSupertypeName(); } + + public String[] getSlashedSuperinterfacesName() { + return getTypeDescriptor().getSuperinterfacesName(); + } @UsedByGeneratedCode public __DynamicallyDispatchable getDispatcher() { @@ -1467,21 +1525,101 @@ public class ReloadableType { this.superclazz = superclazz; } + /** + * Return the ReloadableType representing the superclass of this type. If the supertype + * is not reloadable, this method will return null. The ReloadableType that is returned + * may not be within the same type registry, if the supertype was loaded by a different + * classloader. + * + * @return the ReloadableType for the supertype or null if it is not reloadable + */ public ReloadableType getSuperRtype() { if (superRtype != null) { return superRtype; } if (superclazz == null) { - return null; - } else { + // Not filled in yet? Why is this code different to the interface case? + String name = this.getSlashedSupertypeName(); + if (name == null) { + return null; + } + else { + ReloadableType rtype = typeRegistry.getReloadableSuperType(name); + superRtype = rtype; + return superRtype; + } + } + else { ClassLoader superClassLoader = superclazz.getClassLoader(); TypeRegistry superTypeRegistry = TypeRegistry.getTypeRegistryFor(superClassLoader); superRtype = superTypeRegistry.getReloadableType(superclazz); return superRtype; } } + + public ReloadableType[] getInterfacesRtypes() { + if (interfaceRtypes != null) { + return interfaceRtypes; + } + if (this.getSlashedSuperinterfacesName() == null) { + return null; + } else { + List reloadableInterfaces = new ArrayList(); + String[] names = this.getSlashedSuperinterfacesName(); + for (String name: names) { + ReloadableType interfaceRtype = typeRegistry.getReloadableSuperType(name); + if (interfaceRtype != null) { // If null then that interface is not reloadable + reloadableInterfaces.add(interfaceRtype); + } + } + interfaceRtypes = reloadableInterfaces.toArray(new ReloadableType[reloadableInterfaces.size()]); + return interfaceRtypes; + } + } + public boolean hasStaticInitializer() { return this.typedescriptor.hasClinit(); } + + /** + * @param child the new reloadable subtype to record + */ + public void recordSubtype(ReloadableType child) { + if (associatedSubtypes == null) { + associatedSubtypes = new ArrayList>(); + } + associatedSubtypes.add(new WeakReference(child)); + if (this.isAffectedByReload()) { + child.tagAsAffectedByReload(); + child.tagSubtypesAsAffectedByReload(); + } + } + + public List> getAssociatedSubtypes() { + return associatedSubtypes; + } + + /** + * For this specified reloadable type, records the type with its parent types + * (super class and super interfaces). With this information the system can run faster + * when reloading has occurred. + */ + public void createTypeAssociations() { + // Connect the child to the parent rtype and interface rtypes + ClassLoader classLoader = getClazz().getClassLoader(); + if (classLoader == null) { + return; + } + ReloadableType srtype = getSuperRtype(); + if (srtype!=null) { + srtype.recordSubtype(this); + } + ReloadableType[] irtypes = getInterfacesRtypes(); + if (irtypes!=null) { + for (ReloadableType irtype: irtypes) { + irtype.recordSubtype(this); + } + } + } } diff --git a/springloaded/src/main/java/org/springsource/loaded/TypeRegistry.java b/springloaded/src/main/java/org/springsource/loaded/TypeRegistry.java index 3f3b2ad..36fea19 100644 --- a/springloaded/src/main/java/org/springsource/loaded/TypeRegistry.java +++ b/springloaded/src/main/java/org/springsource/loaded/TypeRegistry.java @@ -1176,6 +1176,11 @@ public class TypeRegistry { if (reloadableType == null) { reloadableType = searchForReloadableType(typeId, typeRegistry); } + + // Check 2: Info computed earlier + if (reloadableType!=null && !reloadableType.isAffectedByReload()) { + return null; + } if (reloadableType != null && reloadableType.hasBeenReloaded()) { MethodMember method = reloadableType.getLiveVersion().incrementalTypeDescriptor @@ -1318,6 +1323,12 @@ public class TypeRegistry { @UsedByGeneratedCode public static __DynamicallyDispatchable ispcheck(int ids, String nameAndDescriptor) { + + // TOD why no check about whether anything has been reloaded??? + if (nothingReloaded) { + return null; + } + if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.FINER)) { log.entering("TypeRegistry", "spcheck", new Object[] { ids, nameAndDescriptor }); } @@ -1328,6 +1339,10 @@ public class TypeRegistry { if (reloadableType == null) { reloadableType = searchForReloadableType(typeId, typeRegistry); } + // Check 2: Info computed earlier +// if (!reloadableType.isAffectedByReload()) { +// return false; +// } // Search for the dispatcher we can call __DynamicallyDispatchable o = (__DynamicallyDispatchable) invokespecialSearch(reloadableType, nameAndDescriptor); return o; @@ -1388,6 +1403,10 @@ public class TypeRegistry { if (reloadableType == null) { reloadableType = searchForReloadableType(typeId, typeRegistry); } + // Check 2: Info computed earlier + if (reloadableType!=null && !reloadableType.isAffectedByReload()) { + return false; + } if (reloadableType != null && reloadableType.hasBeenReloaded()) { MethodMember method = reloadableType.getLiveVersion().incrementalTypeDescriptor .getFromLatestByDescriptor(nameAndDescriptor); @@ -1567,6 +1586,7 @@ public class TypeRegistry { */ @UsedByGeneratedCode public static boolean ivicheck(int ids, String nameAndDescriptor) { + // Check 1: FAST: Has anything at all been reloaded? if (nothingReloaded) { return false; } @@ -1574,13 +1594,14 @@ public class TypeRegistry { // log.entering("TypeRegistry", "ivicheck", new Object[] { ids, nameAndDescriptor }); // } - // TODO [perf] global check (anything been reloaded?) + // TODO [perf] global check (anything been reloaded?) // TODO [perf] local check (type or anything in its hierarchy reloaded) int registryId = ids >>> 16; int typeId = ids & 0xffff; TypeRegistry typeRegistry = registryInstances[registryId].get(); ReloadableType reloadableType = typeRegistry.getReloadableType(typeId); + // Ok, think about what null means here. It means this registry has not loaded this type as a reloadable type. That doesn't // mean it isn't reloadable as a parent loaded may have found it. We have 3 options: @@ -1597,6 +1618,11 @@ public class TypeRegistry { reloadableType = searchForReloadableType(typeId, typeRegistry); } + // Check 2: Info computed earlier + if (reloadableType!=null && !reloadableType.isAffectedByReload()) { + return false; + } + if (reloadableType != null && reloadableType.hasBeenReloaded()) { MethodMember method = reloadableType.getLiveVersion().incrementalTypeDescriptor .getFromLatestByDescriptor(nameAndDescriptor); @@ -1665,6 +1691,7 @@ public class TypeRegistry { if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) { log.info(" parent) { + // TODO performance - can we make this cheaper? + ClassLoader parentClassLoader = parent.getClassLoader(); + if (parentClassLoader == null) { + return; + } + TypeRegistry parentTypeRegistry = TypeRegistry.getTypeRegistryFor(parent.getClassLoader()); + ReloadableType parentReloadableType = parentTypeRegistry.getReloadableType(parent); + if (parentReloadableType != null) { + parentReloadableType.recordSubtype(child); + } + } + +} + + + diff --git a/springloaded/src/main/java/org/springsource/loaded/TypeRewriter.java b/springloaded/src/main/java/org/springsource/loaded/TypeRewriter.java index c39b4e9..8f77aae 100644 --- a/springloaded/src/main/java/org/springsource/loaded/TypeRewriter.java +++ b/springloaded/src/main/java/org/springsource/loaded/TypeRewriter.java @@ -26,6 +26,7 @@ import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; import org.springsource.loaded.Utils.ReturnType; @@ -764,7 +765,10 @@ public class TypeRewriter implements Constants { class ClinitPrepender implements Prepender, Constants { MethodVisitor mv; - + + private final static String descriptorFor_getReloadableType = "(II)"+lReloadableType; + private final static String descriptorFor_associateReloadableType = "("+lReloadableType+"Ljava/lang/Class;)V"; + ClinitPrepender(MethodVisitor mv) { this.mv = mv; } @@ -775,12 +779,13 @@ public class TypeRewriter implements Constants { // TODO optimization: could collapse ints into one but this snippet isn't put in many places mv.visitLdcInsn(rtype.getTypeRegistryId()); mv.visitLdcInsn(rtype.getId()); - mv.visitMethodInsn(INVOKESTATIC, tRegistryType, "getReloadableType", "(II)" + lReloadableType); + mv.visitMethodInsn(INVOKESTATIC, tRegistryType, "getReloadableType", descriptorFor_getReloadableType, false); + mv.visitFieldInsn(PUTSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType); // mv.visitFieldInsn(GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType); // mv.visitLdcInsn(Type.getObjectType(rtype.getSlashedSupertypeName()));//Type("L" + rtype.getSlashedSupertypeName() + ";")); // faster way? // mv.visitMethodInsn(INVOKEVIRTUAL, tReloadableType, "setSuperclass", "(Ljava/lang/Class;)V"); - + // only in the top most type - what about interfaces?? if (GlobalConfiguration.fieldRewriting) { mv.visitFieldInsn(GETSTATIC, slashedname, fStaticFieldsName, lStaticStateManager); @@ -795,14 +800,14 @@ public class TypeRewriter implements Constants { // If the static initializer has changed, call the new version through the ___clinit___ method mv.visitFieldInsn(Opcodes.GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tReloadableType, "clinitchanged", "()I"); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, tReloadableType, "clinitchanged", "()I", false); // 2. Create the if statement Label wasZero = new Label(); mv.visitJumpInsn(Opcodes.IFEQ, wasZero); // if == 0, jump to where we can do the original thing // 3. grab the latest dispatcher and call it through the interface mv.visitFieldInsn(Opcodes.GETSTATIC, slashedname, fReloadableTypeFieldName, lReloadableType); - mv.visitMethodInsn(INVOKEVIRTUAL, tReloadableType, "fetchLatest", "()Ljava/lang/Object;"); + mv.visitMethodInsn(INVOKEVIRTUAL, tReloadableType, "fetchLatest", "()Ljava/lang/Object;", false); mv.visitTypeInsn(CHECKCAST, Utils.getInterfaceName(slashedname)); mv.visitMethodInsn(INVOKEINTERFACE, Utils.getInterfaceName(slashedname), mStaticInitializerName, "()V"); mv.visitInsn(RETURN); diff --git a/springloaded/src/main/java/org/springsource/loaded/ri/ReloadableTypeMethodProvider.java b/springloaded/src/main/java/org/springsource/loaded/ri/ReloadableTypeMethodProvider.java index 11e9a60..a29a41e 100644 --- a/springloaded/src/main/java/org/springsource/loaded/ri/ReloadableTypeMethodProvider.java +++ b/springloaded/src/main/java/org/springsource/loaded/ri/ReloadableTypeMethodProvider.java @@ -71,7 +71,8 @@ public class ReloadableTypeMethodProvider extends TypeDescriptorMethodProvider { @Override public List getDeclaredMethods() { - if (TypeRegistry.nothingReloaded && rtype.invokersCache_getDeclaredMethods != null) { + if (rtype.invokersCache_getDeclaredMethods != null) { +// if (TypeRegistry.nothingReloaded && rtype.invokersCache_getDeclaredMethods != null) { // use the cached version, it will not change if a reload hasn't occurred return rtype.invokersCache_getDeclaredMethods; } @@ -82,8 +83,8 @@ public class ReloadableTypeMethodProvider extends TypeDescriptorMethodProvider { @Override public Collection getMethods() { - if (TypeRegistry.nothingReloaded && rtype.invokersCache_getMethods != null) { - // use the cached version, it will not change if a reload hasn't occurred +// if ((TypeRegistry.nothingReloaded || !rtype.isAffectedByReload()) && + if (rtype.invokersCache_getMethods != null) { return rtype.invokersCache_getMethods; } Collection invokers = super.getMethods(); diff --git a/testdata-subloader/src/main/java/subassociatedtypes/CB.java b/testdata-subloader/src/main/java/subassociatedtypes/CB.java new file mode 100644 index 0000000..03d734e --- /dev/null +++ b/testdata-subloader/src/main/java/subassociatedtypes/CB.java @@ -0,0 +1,7 @@ +package subassociatedtypes; + +import associatedtypes.CM; + +public class CB extends CM implements IB,IB2 { + +} diff --git a/testdata-subloader/src/main/java/subassociatedtypes/IB.java b/testdata-subloader/src/main/java/subassociatedtypes/IB.java new file mode 100644 index 0000000..52a724d --- /dev/null +++ b/testdata-subloader/src/main/java/subassociatedtypes/IB.java @@ -0,0 +1,5 @@ +package subassociatedtypes; + +public interface IB { + +} diff --git a/testdata-subloader/src/main/java/subassociatedtypes/IB2.java b/testdata-subloader/src/main/java/subassociatedtypes/IB2.java new file mode 100644 index 0000000..7482e49 --- /dev/null +++ b/testdata-subloader/src/main/java/subassociatedtypes/IB2.java @@ -0,0 +1,5 @@ +package subassociatedtypes; + +public interface IB2 { + +} diff --git a/testdata-superloader/src/main/java/associatedtypes/CM.java b/testdata-superloader/src/main/java/associatedtypes/CM.java new file mode 100644 index 0000000..7b30aad --- /dev/null +++ b/testdata-superloader/src/main/java/associatedtypes/CM.java @@ -0,0 +1,6 @@ +package associatedtypes; + +public class CM extends CT implements IM,IM2 { + + public static void run() {} +} diff --git a/testdata-superloader/src/main/java/associatedtypes/CT.java b/testdata-superloader/src/main/java/associatedtypes/CT.java new file mode 100644 index 0000000..9aa77dc --- /dev/null +++ b/testdata-superloader/src/main/java/associatedtypes/CT.java @@ -0,0 +1,5 @@ +package associatedtypes; + +public class CT implements IT, IT2 { + +} diff --git a/testdata-superloader/src/main/java/associatedtypes/IM.java b/testdata-superloader/src/main/java/associatedtypes/IM.java new file mode 100644 index 0000000..c7851b5 --- /dev/null +++ b/testdata-superloader/src/main/java/associatedtypes/IM.java @@ -0,0 +1,5 @@ +package associatedtypes; + +public interface IM { + +} diff --git a/testdata-superloader/src/main/java/associatedtypes/IM2.java b/testdata-superloader/src/main/java/associatedtypes/IM2.java new file mode 100644 index 0000000..522ddff --- /dev/null +++ b/testdata-superloader/src/main/java/associatedtypes/IM2.java @@ -0,0 +1,5 @@ +package associatedtypes; + +public interface IM2 { + +} diff --git a/testdata-superloader/src/main/java/associatedtypes/IT.java b/testdata-superloader/src/main/java/associatedtypes/IT.java new file mode 100644 index 0000000..2bfa467 --- /dev/null +++ b/testdata-superloader/src/main/java/associatedtypes/IT.java @@ -0,0 +1,5 @@ +package associatedtypes; + +public interface IT { + +} diff --git a/testdata-superloader/src/main/java/associatedtypes/IT2.java b/testdata-superloader/src/main/java/associatedtypes/IT2.java new file mode 100644 index 0000000..e4e55a2 --- /dev/null +++ b/testdata-superloader/src/main/java/associatedtypes/IT2.java @@ -0,0 +1,5 @@ +package associatedtypes; + +public interface IT2 { + +}