All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.amygdalum.testrecorder.values.LambdaSignature Maven / Gradle / Ivy

package net.amygdalum.testrecorder.values;

import static net.amygdalum.testrecorder.asm.ByteCode.classFrom;

import java.io.Serializable;
import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandleInfo;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.util.Arrays;

public class LambdaSignature implements Serializable {

	private String capturingClass;

	private String instantiatedMethodType;

	private String functionalInterfaceClass;
	private String functionalInterfaceMethodName;
	private String functionalInterfaceMethodSignature;

	private String implClass;
	private int implMethodKind;
	private String implMethodName;
	private String implMethodSignature;
	
	public LambdaSignature() {
	}

	public LambdaSignature withCapturingClass(String capturingClass) {
		this.capturingClass = capturingClass;
		return this;
	}

	public LambdaSignature withInstantiatedMethodType(String instantiatedMethodType) {
		this.instantiatedMethodType = instantiatedMethodType;
		return this;
	}

	public LambdaSignature withFunctionalInterface(String functionalInterfaceClass, String functionalInterfaceMethodName, String functionalInterfaceMethodSignature) {
		this.functionalInterfaceClass = functionalInterfaceClass;
		this.functionalInterfaceMethodName = functionalInterfaceMethodName;
		this.functionalInterfaceMethodSignature = functionalInterfaceMethodSignature;
		return this;
	}

	public LambdaSignature withImplMethod(String implClass, int implMethodKind, String implMethodName, String implMethodSignature) {
		this.implClass = implClass;
		this.implMethodKind = implMethodKind;
		this.implMethodName = implMethodName;
		this.implMethodSignature = implMethodSignature;
		return this;
	}

	@SuppressWarnings("unchecked")
	public  T deserialize(Class resultType, Object... capturedArgs) {
		try {
			Class capturingClass = classFrom(this.capturingClass, resultType.getClassLoader());
			ClassLoader cl = capturingClass.getClassLoader();
			Class implClass = classFrom(this.implClass, cl);
			Class interfaceType = classFrom(this.functionalInterfaceClass, cl);

			Lookup lookup = privateLookup(implClass);

			MethodType implMethodType = MethodType.fromMethodDescriptorString(implMethodSignature, cl);
			
			MethodType interfaceMethodType = MethodType.fromMethodDescriptorString(functionalInterfaceMethodSignature, null);

			MethodHandle implMethod = implMethod(lookup, implClass, implMethodType);

			MethodType factoryMethodType = factoryType(interfaceType, interfaceMethodType, implClass, implMethodType);

			MethodType instantiatedMethodType = instantiatedType(interfaceMethodType, implMethodType);

			CallSite callSite = LambdaMetafactory.altMetafactory(lookup, functionalInterfaceMethodName, factoryMethodType, interfaceMethodType, implMethod, instantiatedMethodType, 1);

			return (T) callSite.dynamicInvoker().invokeWithArguments(capturedArgs);
		} catch (RuntimeException e) {
			throw e;
		} catch (Throwable e) {
			throw new RuntimeException(e);
		}
	}

	private MethodType factoryType(Class interfaceType, MethodType interfaceMethodType, Class implClass, MethodType implMethodType) {
		MethodType factoryType = MethodType.methodType(interfaceType, Arrays.copyOf(implMethodType.parameterArray(), implMethodType.parameterCount() - interfaceMethodType.parameterCount()));
		if (isInstanceMethod()) {
			return factoryType.insertParameterTypes(0, implClass);
		}
		return factoryType;
	}

	private MethodType instantiatedType(MethodType interfaceMethodType, MethodType implType) {
		if (implType.parameterCount() > interfaceMethodType.parameterCount()) {
			return implType.dropParameterTypes(0, implType.parameterCount() - interfaceMethodType.parameterCount());
		} else if (implType.returnType() == void.class && interfaceMethodType.returnType() != void.class){
			return implType.changeReturnType(interfaceMethodType.returnType());
		} else {
			return implType;
		}
	}

	private boolean isInstanceMethod() {
		return implMethodKind != MethodHandleInfo.REF_invokeStatic
			&& implMethodKind != MethodHandleInfo.REF_newInvokeSpecial;
	}

	private MethodHandle implMethod(Lookup lookup, Class implClass, MethodType implType) throws NoSuchMethodException, IllegalAccessException {
		switch (implMethodKind) {
		case MethodHandleInfo.REF_invokeInterface:
		case MethodHandleInfo.REF_invokeVirtual:
			return lookup.findVirtual(implClass, implMethodName, implType);
		case MethodHandleInfo.REF_invokeSpecial:
			return lookup.findSpecial(implClass, implMethodName, implType, implClass);
		case MethodHandleInfo.REF_invokeStatic:
			return lookup.findStatic(implClass, implMethodName, implType);
		case MethodHandleInfo.REF_newInvokeSpecial:
			return lookup.findConstructor(implClass, implType);
		default:
			throw new RuntimeException("Unsupported impl method kind " + implMethodKind);
		}
	}

	private Lookup privateLookup(Class clazz) throws ReflectiveOperationException {
		Constructor constructor = Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE);
		constructor.setAccessible(true);
		return constructor.newInstance(clazz, 15);
	}

	public String getCapturingClass() {
		return capturingClass;
	}

	public String getInstantiatedMethodType() {
		return instantiatedMethodType;
	}

	public String getFunctionalInterfaceClass() {
		return functionalInterfaceClass;
	}

	public String getFunctionalInterfaceMethodName() {
		return functionalInterfaceMethodName;
	}

	public String getFunctionalInterfaceMethodSignature() {
		return functionalInterfaceMethodSignature;
	}

	public String getImplClass() {
		return implClass;
	}

	public int getImplMethodKind() {
		return implMethodKind;
	}

	public String getImplMethodName() {
		return implMethodName;
	}

	public String getImplMethodSignature() {
		return implMethodSignature;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy