package org.graalvm.compiler.serviceprovider;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import jdk.vm.ci.code.BytecodePosition;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.meta.SpeculationLog.SpeculationReason;
public final class SpeculationReasonGroup {
private final int id;
private final String name;
private final Class<?>[] signature;
private static final AtomicInteger nextId = new AtomicInteger(1);
public SpeculationReasonGroup(String name, Class<?>... signature) {
this.id = nextId.get();
this.name = name;
this.signature = signature;
for (Class<?> c : signature) {
if (!isOfSupportedType(c)) {
throw new IllegalArgumentException("Unsupported speculation context type: " + c.getName());
}
}
}
@Override
public String toString() {
return String.format("%s{id:%d, sig=%s}", name, id, Arrays.toString(signature));
}
public SpeculationReason createSpeculationReason(Object... context) {
assert checkSignature(context);
return GraalServices.createSpeculationReason(id, name, context);
}
private static final Set<Class<?>> SUPPORTED_EXACT_TYPES = new HashSet<>(Arrays.asList(
String.class,
int.class,
long.class,
float.class,
double.class,
BytecodePosition.class));
private static boolean isOfSupportedType(Class<?> c) {
if (SUPPORTED_EXACT_TYPES.contains(c)) {
return true;
}
if (Enum.class.isAssignableFrom(c)) {
return true;
}
if (SpeculationContextObject.class.isAssignableFrom(c)) {
return true;
}
if (ResolvedJavaMethod.class.isAssignableFrom(c) || ResolvedJavaType.class.isAssignableFrom(c)) {
return true;
}
return false;
}
static Class<?> toBox(Class<?> c) {
if (c == int.class) {
return Integer.class;
}
if (c == long.class) {
return Long.class;
}
if (c == float.class) {
return Float.class;
}
if (c == double.class) {
return Double.class;
}
return c;
}
private boolean checkSignature(Object[] context) {
assert signature.length == context.length : name + ": Incorrect number of context arguments. Expected " + signature.length + ", got " + context.length;
for (int i = 0; i < context.length; i++) {
Object o = context[i];
Class<?> c = signature[i];
if (o != null) {
if (c == ResolvedJavaMethod.class || c == ResolvedJavaType.class || SpeculationContextObject.class.isAssignableFrom(c)) {
c.cast(o);
} else {
Class<?> oClass = o.getClass();
assert toBox(c) == oClass : name + ": Context argument " + i + " is not a " + c.getName() + " but a " + oClass.getName();
}
} else {
if (c.isPrimitive() || Enum.class.isAssignableFrom(c)) {
throw new AssertionError(name + ": Cannot pass null for argument " + i);
}
}
}
return true;
}