package scotch.symbol;
import static java.util.Arrays.stream;
import static me.qmx.jitescript.util.CodegenUtils.p;
import static me.qmx.jitescript.util.CodegenUtils.sig;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Objects;
import me.qmx.jitescript.CodeBlock;
public class MethodSignature {
public static MethodSignature constructor(String descriptor) {
return fromString(MethodTypeDescriptor.SPECIAL, descriptor);
}
public static MethodSignature fromMethod(Method method) {
return new MethodSignature(
MethodTypeDescriptor.fromAccess(method),
p(method.getDeclaringClass()),
method.getName(),
sig(method.getReturnType(), method.getParameterTypes())
);
}
public static MethodSignature fromMethod(Class<?> clazz, String methodName) {
try {
return stream(clazz.getMethods())
.filter(method -> methodName.equals(method.getName()))
.findFirst()
.map(MethodSignature::fromMethod)
.orElseThrow(() -> new NoSuchMethodException("Could not find method " + methodName));
} catch (ReflectiveOperationException exception) {
throw new RuntimeException(exception);
}
}
public static MethodSignature methodSignature(String descriptor) {
return fromString(MethodTypeDescriptor.STATIC, descriptor);
}
public static MethodSignature staticMethod(String className, String methodName, String signature) {
return new MethodSignature(MethodTypeDescriptor.STATIC, className, methodName, signature);
}
public static MethodSignature fromString(MethodTypeDescriptor methodTypeDescriptor, String descriptor) {
String[] parts = descriptor.split(":");
return new MethodSignature(methodTypeDescriptor, parts[0], parts[1], parts[2]);
}
private final MethodTypeDescriptor methodTypeDescriptor;
private final String className;
private final String methodName;
private final String signature;
private MethodSignature(MethodTypeDescriptor methodTypeDescriptor, String className, String methodName, String signature) {
this.methodTypeDescriptor = methodTypeDescriptor;
this.className = className;
this.methodName = methodName;
this.signature = signature;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof MethodSignature) {
MethodSignature other = (MethodSignature) o;
return Objects.equals(methodTypeDescriptor, other.methodTypeDescriptor)
&& Objects.equals(className, other.className)
&& Objects.equals(methodName, other.methodName)
&& Objects.equals(signature, other.signature);
} else {
return false;
}
}
public String getClassName() {
return className;
}
@Override
public int hashCode() {
return Objects.hash(methodTypeDescriptor, className, methodName, signature);
}
public CodeBlock reference() {
return methodTypeDescriptor.generate(this);
}
@Override
public String toString() {
return className + ":" + methodName + ":" + signature;
}
public enum MethodTypeDescriptor {
STATIC {
@Override
public CodeBlock generate(MethodSignature signature) {
return new CodeBlock() {{
invokestatic(signature.className, signature.methodName, signature.signature);
}};
}
},
VIRTUAL {
@Override
public CodeBlock generate(MethodSignature signature) {
return new CodeBlock() {{
invokevirtual(signature.className, signature.methodName, signature.signature);
}};
}
},
INTERFACE {
@Override
public CodeBlock generate(MethodSignature signature) {
return new CodeBlock() {{
invokeinterface(signature.className, signature.methodName, signature.signature);
}};
}
},
SPECIAL {
@Override
public CodeBlock generate(MethodSignature signature) {
return new CodeBlock() {{
invokespecial(signature.className, signature.methodName, signature.signature);
}};
}
};
public static MethodTypeDescriptor fromAccess(Method method) {
if (Modifier.isStatic(method.getModifiers())) {
return STATIC;
} else if (Modifier.isInterface(method.getModifiers())) {
return INTERFACE;
} else {
return VIRTUAL;
}
}
public abstract CodeBlock generate(MethodSignature signature);
}
}