package com.oracle.svm.methodhandles;
import static com.oracle.svm.core.util.VMError.shouldNotReachHere;
import java.lang.invoke.MethodType;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.hub.DynamicHub;
import jdk.vm.ci.meta.JavaKind;
final class MethodHandleIntrinsic {
enum Variant {
InvokeBasic(Modifier.FINAL | Modifier.NATIVE),
Link(Modifier.STATIC | Modifier.NATIVE),
UnsafeFieldAccess(Modifier.PUBLIC | Modifier.NATIVE),
Make(Modifier.STATIC),
CopyExtend(Modifier.FINAL),
BMHSpecies(Modifier.STATIC),
Arg(Modifier.FINAL),
Identity(Modifier.STATIC | Modifier.PRIVATE),
Zero(Modifier.STATIC | Modifier.PRIVATE),
Cast(Modifier.PUBLIC),
Array(Modifier.STATIC | Modifier.PRIVATE),
FillArray(Modifier.STATIC | Modifier.PRIVATE),
GetElement(Modifier.STATIC),
SetElement(Modifier.STATIC),
Length(Modifier.STATIC);
int flags;
Variant(int flags) {
this.flags = flags;
}
}
static Map<Variant, Map<String, Map<JavaKind, Map<Integer, MethodHandleIntrinsic>>>> cache = new HashMap<>();
static final String NO_SPECIES = "";
static final Set<String> unsafeFieldAccessMethodNames = new HashSet<>();
static {
for (String op : Arrays.asList("get", "put")) {
for (String type : Arrays.asList("Object", "Boolean", "Byte", "Short", "Char", "Int", "Long", "Float", "Double")) {
for (String isVolatile : Arrays.asList("", "Volatile")) {
unsafeFieldAccessMethodNames.add(op + type + isVolatile);
}
}
}
}
final Variant variant;
final String species;
final JavaKind kind;
final int index;
private MethodHandleIntrinsic(Variant variant, String species, JavaKind kind, int index) {
this.variant = variant;
this.species = species;
this.kind = kind;
this.index = index;
}
private static MethodHandleIntrinsic intrinsic(Variant variant, String species, JavaKind kind, int index) {
return cache.computeIfAbsent(variant, (v) -> new HashMap<>())
.computeIfAbsent(species, (s) -> new HashMap<>())
.computeIfAbsent(kind, (t) -> new HashMap<>())
.computeIfAbsent(index, (i) -> new MethodHandleIntrinsic(variant, species, kind, index));
}
static MethodHandleIntrinsic intrinsic(Variant variant) {
return intrinsic(variant, NO_SPECIES, JavaKind.Illegal, -1);
}
static MethodHandleIntrinsic intrinsic(Variant variant, JavaKind kind) {
return intrinsic(variant, NO_SPECIES, kind, -1);
}
static MethodHandleIntrinsic intrinsic(Variant variant, String species) {
return intrinsic(variant, species, JavaKind.Illegal, -1);
}
static MethodHandleIntrinsic intrinsic(Variant variant, JavaKind kind, int index) {
return intrinsic(variant, NO_SPECIES, kind, index);
}
private static JavaKind kindForKey(char key) {
if (key == 'L') {
return JavaKind.Object;
}
return JavaKind.fromPrimitiveOrVoidTypeChar(key);
}
Object execute(Object... args) throws Throwable {
switch (variant) {
case InvokeBasic: {
assert args.length >= 1;
Target_java_lang_invoke_MethodHandle mh = (Target_java_lang_invoke_MethodHandle) args[0];
Object[] invokeArgs = Arrays.copyOfRange(args, 1, args.length);
return mh.invokeBasic(invokeArgs);
}
case Link:
throw shouldNotReachHere("linkTo methods should not be executed");
case UnsafeFieldAccess:
throw shouldNotReachHere("unsafe field access methods should not be executed");
case Make: {
assert args.length >= 2;
MethodType methodType = (MethodType) args[0];
Target_java_lang_invoke_LambdaForm form = (Target_java_lang_invoke_LambdaForm) args[1];
Object[] actualArgs = new Object[args.length - 2];
System.arraycopy(args, 2, actualArgs, 0, actualArgs.length);
return BoundMethodHandleUtils.make(methodType, form, species, actualArgs);
}
case CopyExtend: {
assert (kind == JavaKind.Void && args.length == 3) || args.length == 4;
Target_java_lang_invoke_SimpleMethodHandle bmh = (Target_java_lang_invoke_SimpleMethodHandle) args[0];
MethodType methodType = (MethodType) args[1];
Target_java_lang_invoke_LambdaForm form = (Target_java_lang_invoke_LambdaForm) args[2];
Object newArg = (kind != JavaKind.Void) ? args[3] : null;
switch (kind) {
case Object:
return bmh.copyWithExtendL(methodType, form, newArg);
case Int:
return bmh.copyWithExtendI(methodType, form, (int) newArg);
case Long:
return bmh.copyWithExtendJ(methodType, form, (long) newArg);
case Float:
return bmh.copyWithExtendF(methodType, form, (float) newArg);
case Double:
return bmh.copyWithExtendD(methodType, form, (double) newArg);
case Void:
return bmh.copyWith(methodType, form);
}
throw shouldNotReachHere("illegal kind");
}
case BMHSpecies: {
assert args.length == 1;
Target_java_lang_invoke_SimpleMethodHandle bmh = (Target_java_lang_invoke_SimpleMethodHandle) args[0];
return bmh.speciesData;
}
case Arg: {
assert args.length == 1;
Target_java_lang_invoke_MethodHandle mh = (Target_java_lang_invoke_MethodHandle) args[0];
if (args[0] instanceof Target_java_lang_invoke_MethodHandleImpl_IntrinsicMethodHandle) {
mh = (SubstrateUtil.cast(args[0], Target_java_lang_invoke_MethodHandleImpl_IntrinsicMethodHandle.class).getTarget());
}
Target_java_lang_invoke_SimpleMethodHandle bmh = SubstrateUtil.cast(mh, Target_java_lang_invoke_SimpleMethodHandle.class);
return bmh.args[index];
}
case Identity:
assert args.length == 1;
return args[0];
case Zero:
assert args.length == 0;
switch (kind) {
case Object:
return null;
case Int:
return 0;
case Long:
return 0L;
case Float:
return 0F;
case Double:
return 0D;
default:
throw shouldNotReachHere("Unknown zero kind: " + kind);
}
case Cast: {
assert args.length == 2;
Class<?> clazz = (Class<?>) args[0];
Object obj = args[1];
return clazz.cast(obj);
}
case Array:
return Arrays.copyOf(args, args.length);
case FillArray: {
assert args.length >= 3;
Integer pos = (Integer) args[0];
Object[] dest = (Object[]) args[1];
Object[] src = Arrays.copyOfRange(args, 2, args.length);
System.arraycopy(src, 0, dest, pos, src.length);
return dest;
}
case GetElement: {
assert args.length == 2;
Object array = args[0];
int i = (int) args[1];
switch (kind) {
case Object:
return ((Object[]) array)[i];
case Boolean:
return ((boolean[]) array)[i];
case Byte:
return ((byte[]) array)[i];
case Short:
return ((short[]) array)[i];
case Char:
return ((char[]) array)[i];
case Int:
return ((int[]) array)[i];
case Long:
return ((long[]) array)[i];
case Float:
return ((float[]) array)[i];
case Double:
return ((double[]) array)[i];
default:
throw shouldNotReachHere("Illegal intrinsic kind: " + kind);
}
}
case SetElement: {
assert args.length == 3;
Object array = args[0];
int i = (int) args[1];
Object value = args[2];
switch (kind) {
case Object:
((Object[]) array)[i] = value;
return null;
case Boolean:
((boolean[]) array)[i] = (boolean) value;
return null;
case Byte:
((byte[]) array)[i] = (byte) value;
return null;
case Short:
((short[]) array)[i] = (short) value;
return null;
case Char:
((char[]) array)[i] = (char) value;
return null;
case Int:
((int[]) array)[i] = (int) value;
return null;
case Long:
((long[]) array)[i] = (long) value;
return null;
case Float:
((float[]) array)[i] = (float) value;
return null;
case Double:
((double[]) array)[i] = (double) value;
return null;
default:
throw shouldNotReachHere("Illegal intrinsic kind: " + kind);
}
}
case Length: {
assert args.length == 1;
Object array = args[0];
switch (kind) {
case Object:
return ((Object[]) array).length;
case Boolean:
return ((boolean[]) array).length;
case Byte:
return ((byte[]) array).length;
case Short:
return ((short[]) array).length;
case Char:
return ((char[]) array).length;
case Int:
return ((int[]) array).length;
case Long:
return ((long[]) array).length;
case Float:
return ((float[]) array).length;
case Double:
return ((double[]) array).length;
default:
throw shouldNotReachHere("Illegal intrinsic kind: " + kind);
}
}
default:
throw shouldNotReachHere("Unknown intrinsic: " + this);
}
}
static MethodHandleIntrinsic resolve(Target_java_lang_invoke_MemberName memberName) {
Class<?> declaringClass = memberName.getDeclaringClass();
String name = memberName.name;
if (declaringClass == Target_java_lang_invoke_MethodHandle.class) {
switch (name) {
case "invokeBasic":
return MethodHandleIntrinsic.intrinsic(Variant.InvokeBasic);
case "linkToVirtual":
case "linkToStatic":
case "linkToSpecial":
case "linkToInterface":
return MethodHandleIntrinsic.intrinsic(Variant.Link);
}
} else if ("jdk.internal.misc.Unsafe".equals(declaringClass.getTypeName()) &&
unsafeFieldAccessMethodNames.contains(name)) {
return MethodHandleIntrinsic.intrinsic(Variant.UnsafeFieldAccess);
} else if (declaringClass == Target_java_lang_invoke_BoundMethodHandle.class ||
declaringClass == Target_java_lang_invoke_BoundMethodHandle_Species_L.class ||
declaringClass.getTypeName().startsWith("java.lang.invoke.BoundMethodHandle$Species_")) {
if (name.startsWith("arg")) {
JavaKind kind = kindForKey(name.charAt("arg".length()));
int index = Integer.parseInt(name.substring("arg".length() + 1));
return MethodHandleIntrinsic.intrinsic(Variant.Arg, kind, index);
}
switch (name) {
case "make": {
Class<?>[] paramTypes = memberName.getMethodType().parameterArray();
StringBuilder species = new StringBuilder();
for (int i = 2; i < paramTypes.length; ++i) {
JavaKind kind = JavaKind.fromJavaClass(paramTypes[i]);
species.append(kind == JavaKind.Object ? 'L' : kind.getTypeChar());
}
return MethodHandleIntrinsic.intrinsic(Variant.Make, species.toString());
}
case "BMH_SPECIES":
return MethodHandleIntrinsic.intrinsic(Variant.BMHSpecies);
case "copyWithExtendL":
case "copyWithExtendI":
case "copyWithExtendJ":
case "copyWithExtendF":
case "copyWithExtendD":
JavaKind kind = kindForKey(name.charAt("copyWithExtend".length()));
return MethodHandleIntrinsic.intrinsic(Variant.CopyExtend, kind);
case "copyWith":
return MethodHandleIntrinsic.intrinsic(Variant.CopyExtend);
}
} else if (declaringClass == Target_java_lang_invoke_LambdaForm.class) {
switch (name) {
case "identity_L":
case "identity_I":
case "identity_J":
case "identity_F":
case "identity_D":
case "identity_V": {
JavaKind kind = kindForKey(name.charAt("identity_".length()));
return MethodHandleIntrinsic.intrinsic(Variant.Identity, kind);
}
case "zero_L":
case "zero_I":
case "zero_J":
case "zero_F":
case "zero_D": {
JavaKind kind = kindForKey(name.charAt("zero_".length()));
return MethodHandleIntrinsic.intrinsic(Variant.Zero, kind);
}
}
} else if (declaringClass == DynamicHub.class) {
if ("cast".equals(name)) {
return MethodHandleIntrinsic.intrinsic(Variant.Cast);
}
} else if (declaringClass == Target_java_lang_invoke_MethodHandleImpl.class) {
switch (name) {
case "array":
return MethodHandleIntrinsic.intrinsic(Variant.Array);
case "fillArray":
return MethodHandleIntrinsic.intrinsic(Variant.FillArray);
}
} else if (declaringClass == Target_java_lang_invoke_MethodHandleImpl_ArrayAccessor.class) {
switch (name) {
case "getElementL":
case "getElementZ":
case "getElementB":
case "getElementS":
case "getElementC":
case "getElementI":
case "getElementJ":
case "getElementF":
case "getElementD": {
JavaKind kind = kindForKey(name.charAt("getElement".length()));
return MethodHandleIntrinsic.intrinsic(Variant.GetElement, kind);
}
case "setElementL":
case "setElementZ":
case "setElementB":
case "setElementS":
case "setElementC":
case "setElementI":
case "setElementJ":
case "setElementF":
case "setElementD": {
JavaKind kind = kindForKey(name.charAt("setElement".length()));
return MethodHandleIntrinsic.intrinsic(Variant.SetElement, kind);
}
case "lengthL":
case "lengthZ":
case "lengthB":
case "lengthS":
case "lengthC":
case "lengthI":
case "lengthJ":
case "lengthF":
case "lengthD": {
JavaKind kind = kindForKey(name.charAt("length".length()));
return MethodHandleIntrinsic.intrinsic(Variant.Length, kind);
}
}
}
return null;
}
}
@TargetClass(className = "java.lang.invoke.MethodHandleImpl", innerClass = "IntrinsicMethodHandle", onlyWith = MethodHandlesSupported.class)
final class Target_java_lang_invoke_MethodHandleImpl_IntrinsicMethodHandle {
@Alias
protected native Target_java_lang_invoke_MethodHandle getTarget();
}