Search in sources :

Example 6 with CurrentLiveVersion

use of org.springsource.loaded.CurrentLiveVersion in project spring-loaded by spring-projects.

the class ReflectiveInterceptor method jlrConstructorGetDeclaredAnnotations.

public static Annotation[] jlrConstructorGetDeclaredAnnotations(Constructor<?> c) {
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(c.getDeclaringClass());
    if (rtype == null) {
        //Nothing special to be done
        return c.getDeclaredAnnotations();
    } else {
        // Constructor could have changed...
        CurrentLiveVersion clv = rtype.getLiveVersion();
        Method executor = clv.getExecutorMethod(rtype.getCurrentConstructor(Type.getConstructorDescriptor(c)));
        return executor.getAnnotations();
    }
}
Also used : ReloadableType(org.springsource.loaded.ReloadableType) CurrentLiveVersion(org.springsource.loaded.CurrentLiveVersion) Method(java.lang.reflect.Method)

Example 7 with CurrentLiveVersion

use of org.springsource.loaded.CurrentLiveVersion in project spring-loaded by spring-projects.

the class Java8 method callLambdaMetaFactory.

// TODO [perf] How about a table of CallSites indexed by invokedynamic number through the class file. Computed on first reference but cleared on reload. Possibly extend this to all invoke types!
// TODO [lambda] Need to handle altMetaFactory which is used when the lambdas are more 'complex' (e.g. Serializable)
public static CallSite callLambdaMetaFactory(ReloadableType rtype, Object[] bsmArgs, Object lookup, String indyNameAndDescriptor, Class<?> executorClass) throws Exception {
    MethodHandles.Lookup caller = (MethodHandles.Lookup) lookup;
    ClassLoader callerLoader = caller.lookupClass().getClassLoader();
    int descriptorStart = indyNameAndDescriptor.indexOf('(');
    String invokedName = indyNameAndDescriptor.substring(0, descriptorStart);
    MethodType invokedType = MethodType.fromMethodDescriptorString(indyNameAndDescriptor.substring(descriptorStart), callerLoader);
    // Use bsmArgs to build the parameters
    MethodType samMethodType = MethodType.fromMethodDescriptorString((((Type) bsmArgs[0]).getDescriptor()), callerLoader);
    Handle bsmArgsHandle = (Handle) bsmArgs[1];
    String owner = bsmArgsHandle.getOwner();
    String name = bsmArgsHandle.getName();
    String descriptor = bsmArgsHandle.getDesc();
    MethodType implMethodType = MethodType.fromMethodDescriptorString(descriptor, callerLoader);
    // Looking up the lambda$run method in the caller class (note the caller class is the executor, which gets us around the
    // problem of having to hack into LambdaMetafactory to intercept reflection)
    MethodHandle implMethod = null;
    switch(bsmArgsHandle.getTag()) {
        case Opcodes.H_INVOKESTATIC:
            implMethod = caller.findStatic(caller.lookupClass(), name, implMethodType);
            break;
        case Opcodes.H_INVOKESPECIAL:
            // will be static with a new leading parameter.
            if (executorClass == null) {
                // TODO is final parameter here correct?
                implMethod = caller.findSpecial(caller.lookupClass(), name, implMethodType, caller.lookupClass());
            } else {
                implMethod = caller.findStatic(caller.lookupClass(), name, MethodType.fromMethodDescriptorString("(L" + owner + ";" + descriptor.substring(1), callerLoader));
            }
            break;
        case Opcodes.H_INVOKEVIRTUAL:
            // There is a possibility to 'shortcut' here. Basically we are trying to resolve a callsite reference
            // to the method that satisfies it. The easiest option is to just find the method on the originally
            // loaded version of the target class and return that. A more optimal shortcut could return the
            // method on the executor class if the target has been reloaded (effectively bypassing the method
            // on the originally loaded version since we know that it will be acting as a pass through). But this
            // opens up a can of worms related to visibility. The executor is loaded into the child classloader,
            // and if the caller has not been reloaded it will not be able to 'see' the executor (since it is in
            // a child classloader). So, basically keep this dumb (but reliable) for now.
            TypeRegistry typeRegistry = rtype.getTypeRegistry();
            ReloadableType ownerRType = typeRegistry.getReloadableType(owner);
            if (null == ownerRType || !ownerRType.hasBeenReloaded()) {
                // target containing the reference/lambdaMethod has not been reloaded, no need to get over
                // complicated.
                Class<?> clazz = callerLoader.loadClass(owner.replace("/", "."));
                implMethod = caller.findVirtual(clazz, name, implMethodType);
            } else {
                MethodMember targetReferenceMethodMember = ownerRType.getCurrentMethod(name, descriptor);
                String targetReferenceDescriptor = targetReferenceMethodMember.getDescriptor();
                MethodType targetReferenceMethodType = MethodType.fromMethodDescriptorString(targetReferenceDescriptor, callerLoader);
                Class<?> targetReferenceClass = ownerRType.getClazz();
                MethodMember currentMethod = ownerRType.getCurrentMethod(name, descriptor);
                if (currentMethod.original == null) {
                    // caller and reloaded target are in the same child classloader (no visibility problem).
                    if (!rtype.hasBeenReloaded()) {
                        throw new IllegalStateException("Assertion violated: When a method added on reload is being referenced" + "in target type '" + ownerRType.getName() + "', expected the caller to also have been reloaded: '" + rtype.getName() + "'");
                    }
                    CurrentLiveVersion ownerLiveVersion = ownerRType.getLiveVersion();
                    Class<?> ownerExecutorClass = ownerLiveVersion.getExecutorClass();
                    Method executorMethod = ownerLiveVersion.getExecutorMethod(currentMethod);
                    String methodDescriptor = Type.getType(executorMethod).getDescriptor();
                    MethodType type = MethodType.fromMethodDescriptorString(methodDescriptor, callerLoader);
                    implMethod = caller.findStatic(ownerExecutorClass, name, type);
                } else {
                    // This finds the reference method on the originally loaded class. It will pass through
                    // to the actual code on the reloaded version.
                    implMethod = caller.findVirtual(targetReferenceClass, name, targetReferenceMethodType);
                }
            }
            break;
        case Opcodes.H_NEWINVOKESPECIAL:
            Class<?> clazz = callerLoader.loadClass(owner.replace("/", "."));
            implMethod = caller.findConstructor(clazz, implMethodType);
            break;
        case Opcodes.H_INVOKEINTERFACE:
            Handle h = (Handle) bsmArgs[1];
            String interfaceOwner = h.getOwner();
            // TODO Should there not be a more direct way to this than classloading?
            // TODO What about when this is a method added to the interface on a reload? It won't really exist, should we point
            // to the executor? or something else? (maybe just directly the real method that will satisfy the interface - if it can be worked out)
            // interface type, eg StreamB$Foo
            Class<?> interfaceClass = callerLoader.loadClass(interfaceOwner.replace('/', '.'));
            implMethod = caller.findVirtual(interfaceClass, name, implMethodType);
            break;
        default:
            throw new IllegalStateException("nyi " + bsmArgsHandle.getTag());
    }
    MethodType instantiatedMethodType = MethodType.fromMethodDescriptorString((((Type) bsmArgs[2]).getDescriptor()), callerLoader);
    return LambdaMetafactory.metafactory(caller, invokedName, invokedType, samMethodType, implMethod, instantiatedMethodType);
}
Also used : MethodType(java.lang.invoke.MethodType) CurrentLiveVersion(org.springsource.loaded.CurrentLiveVersion) Method(java.lang.reflect.Method) TypeRegistry(org.springsource.loaded.TypeRegistry) MethodMember(org.springsource.loaded.MethodMember) MethodHandle(java.lang.invoke.MethodHandle) Handle(org.objectweb.asm.Handle) MethodHandles(java.lang.invoke.MethodHandles) Type(org.objectweb.asm.Type) MethodType(java.lang.invoke.MethodType) ReloadableType(org.springsource.loaded.ReloadableType) ReloadableType(org.springsource.loaded.ReloadableType) MethodHandle(java.lang.invoke.MethodHandle)

Example 8 with CurrentLiveVersion

use of org.springsource.loaded.CurrentLiveVersion in project spring-loaded by spring-projects.

the class ReloadedTypeInvoker method create.

public static Invoker create(ReloadableTypeMethodProvider declaringType, final MethodMember methodMember) {
    if (Modifier.isStatic(methodMember.getModifiers())) {
        // Since static methods don't change parameter lists, they just invoke the executor
        return new ReloadedTypeInvoker(declaringType, methodMember) {

            @Override
            public Object invoke(Object target, Object... params) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
                CurrentLiveVersion clv = rtype.getLiveVersion();
                Method executor = clv.getExecutorMethod(methodMember);
                return executor.invoke(target, params);
            }
        };
    } else {
        // Non static method invokers need to add target as a first param
        return new ReloadedTypeInvoker(declaringType, methodMember) {

            @Override
            public Object invoke(Object target, Object... params) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
                CurrentLiveVersion clv = rtype.getLiveVersion();
                Method executor = clv.getExecutorMethod(methodMember);
                if (params == null) {
                    return executor.invoke(null, target);
                } else {
                    Object[] ps = new Object[params.length + 1];
                    System.arraycopy(params, 0, ps, 1, params.length);
                    ps[0] = target;
                    return executor.invoke(null, ps);
                }
            }
        };
    }
}
Also used : CurrentLiveVersion(org.springsource.loaded.CurrentLiveVersion) Method(java.lang.reflect.Method)

Example 9 with CurrentLiveVersion

use of org.springsource.loaded.CurrentLiveVersion in project spring-loaded by spring-projects.

the class ReflectiveInterceptor method jlrConstructorNewInstance.

public static Object jlrConstructorNewInstance(Constructor<?> c, Object... params) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchMethodException {
    //Note: unlike for methods we don't need to handle the reloadable but not reloaded case specially, that is because there
    // is no inheritance on constructors, so reloaded superclasses can affect method lookup in the same way.
    Class<?> clazz = c.getDeclaringClass();
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(clazz);
    if (rtype == null) {
        c = asAccessibleConstructor(c, true);
        //Nothing special to be done
        return c.newInstance(params);
    } else {
        // Constructor may have changed...
        // this is the right thing to do but makes a mess of getDeclaredConstructors (and affects getDeclaredConstructor)
        //			// TODO  should check about constructor changing
        //			rtype.getTypeDescriptor().getConstructor("").
        boolean ctorChanged = rtype.getLiveVersion().hasConstructorChanged(Utils.toConstructorDescriptor(c.getParameterTypes()));
        if (!ctorChanged) {
            // if we let the getDeclaredConstructor(s) code run as is, it may create invalid ctors, if we want to run the real one we should discover it here and use it.
            // would it be cheaper to fix up getDeclaredConstructor to always return valid ones if we are going to use them, or should we intercept here? probably the former...
            c = asAccessibleConstructor(c, true);
            return c.newInstance(params);
        }
        asAccessibleConstructor(c, false);
        CurrentLiveVersion clv = rtype.getLiveVersion();
        Method executor = clv.getExecutorMethod(rtype.getCurrentConstructor(Type.getConstructorDescriptor(c)));
        Constructor<?> magicConstructor = clazz.getConstructor(C.class);
        Object instance = magicConstructor.newInstance((Object) null);
        Object[] instanceAndParams;
        if (params == null || params.length == 0) {
            instanceAndParams = new Object[] { instance };
        } else {
            //Must add instance as first param: executor is a static method.
            instanceAndParams = new Object[params.length + 1];
            instanceAndParams[0] = instance;
            System.arraycopy(params, 0, instanceAndParams, 1, params.length);
        }
        executor.invoke(null, instanceAndParams);
        return instance;
    }
}
Also used : ReloadableType(org.springsource.loaded.ReloadableType) CurrentLiveVersion(org.springsource.loaded.CurrentLiveVersion) AccessibleObject(java.lang.reflect.AccessibleObject) Method(java.lang.reflect.Method)

Example 10 with CurrentLiveVersion

use of org.springsource.loaded.CurrentLiveVersion in project spring-loaded by spring-projects.

the class ReflectiveInterceptor method jlrConstructorGetAnnotation.

public static Annotation jlrConstructorGetAnnotation(Constructor<?> c, Class<? extends Annotation> annotType) {
    ReloadableType rtype = getReloadableTypeIfHasBeenReloaded(c.getDeclaringClass());
    if (rtype == null) {
        //Nothing special to be done
        return c.getAnnotation(annotType);
    } else {
        // Constructor could have changed...
        CurrentLiveVersion clv = rtype.getLiveVersion();
        Method executor = clv.getExecutorMethod(rtype.getCurrentConstructor(Type.getConstructorDescriptor(c)));
        return executor.getAnnotation(annotType);
    }
}
Also used : ReloadableType(org.springsource.loaded.ReloadableType) CurrentLiveVersion(org.springsource.loaded.CurrentLiveVersion) Method(java.lang.reflect.Method)

Aggregations

CurrentLiveVersion (org.springsource.loaded.CurrentLiveVersion)11 ReloadableType (org.springsource.loaded.ReloadableType)10 Method (java.lang.reflect.Method)9 MethodMember (org.springsource.loaded.MethodMember)4 MethodHandle (java.lang.invoke.MethodHandle)1 MethodHandles (java.lang.invoke.MethodHandles)1 MethodType (java.lang.invoke.MethodType)1 AccessibleObject (java.lang.reflect.AccessibleObject)1 Field (java.lang.reflect.Field)1 InvocationTargetException (java.lang.reflect.InvocationTargetException)1 Handle (org.objectweb.asm.Handle)1 Type (org.objectweb.asm.Type)1 ReloadException (org.springsource.loaded.ReloadException)1 TypeRegistry (org.springsource.loaded.TypeRegistry)1