8038261: JSR292: cache and reuse typed array accessors
Reviewed-by: vlivanov, psandoz
This commit is contained in:
parent
0b424b49d8
commit
693c89e3b9
@ -35,6 +35,7 @@ import static java.lang.invoke.LambdaForm.*;
|
|||||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||||
import static java.lang.invoke.MethodHandleStatics.*;
|
import static java.lang.invoke.MethodHandleStatics.*;
|
||||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||||
|
import java.lang.invoke.MethodHandleImpl.ArrayAccessor;
|
||||||
import sun.invoke.util.ValueConversions;
|
import sun.invoke.util.ValueConversions;
|
||||||
import sun.invoke.util.VerifyAccess;
|
import sun.invoke.util.VerifyAccess;
|
||||||
import sun.invoke.util.VerifyType;
|
import sun.invoke.util.VerifyType;
|
||||||
@ -427,6 +428,40 @@ class InvokerBytecodeGenerator {
|
|||||||
emitStoreInsn(L_TYPE, index);
|
emitStoreInsn(L_TYPE, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte arrayTypeCode(Wrapper elementType) {
|
||||||
|
switch (elementType) {
|
||||||
|
case BOOLEAN: return Opcodes.T_BOOLEAN;
|
||||||
|
case BYTE: return Opcodes.T_BYTE;
|
||||||
|
case CHAR: return Opcodes.T_CHAR;
|
||||||
|
case SHORT: return Opcodes.T_SHORT;
|
||||||
|
case INT: return Opcodes.T_INT;
|
||||||
|
case LONG: return Opcodes.T_LONG;
|
||||||
|
case FLOAT: return Opcodes.T_FLOAT;
|
||||||
|
case DOUBLE: return Opcodes.T_DOUBLE;
|
||||||
|
case OBJECT: return 0; // in place of Opcodes.T_OBJECT
|
||||||
|
default: throw new InternalError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int arrayInsnOpcode(byte tcode, int aaop) throws InternalError {
|
||||||
|
assert(aaop == Opcodes.AASTORE || aaop == Opcodes.AALOAD);
|
||||||
|
int xas;
|
||||||
|
switch (tcode) {
|
||||||
|
case Opcodes.T_BOOLEAN: xas = Opcodes.BASTORE; break;
|
||||||
|
case Opcodes.T_BYTE: xas = Opcodes.BASTORE; break;
|
||||||
|
case Opcodes.T_CHAR: xas = Opcodes.CASTORE; break;
|
||||||
|
case Opcodes.T_SHORT: xas = Opcodes.SASTORE; break;
|
||||||
|
case Opcodes.T_INT: xas = Opcodes.IASTORE; break;
|
||||||
|
case Opcodes.T_LONG: xas = Opcodes.LASTORE; break;
|
||||||
|
case Opcodes.T_FLOAT: xas = Opcodes.FASTORE; break;
|
||||||
|
case Opcodes.T_DOUBLE: xas = Opcodes.DASTORE; break;
|
||||||
|
case 0: xas = Opcodes.AASTORE; break;
|
||||||
|
default: throw new InternalError();
|
||||||
|
}
|
||||||
|
return xas - Opcodes.AASTORE + aaop;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void freeFrameLocal(int oldFrameLocal) {
|
private void freeFrameLocal(int oldFrameLocal) {
|
||||||
int i = indexForFrameLocal(oldFrameLocal);
|
int i = indexForFrameLocal(oldFrameLocal);
|
||||||
if (i < 0) return;
|
if (i < 0) return;
|
||||||
@ -616,6 +651,10 @@ class InvokerBytecodeGenerator {
|
|||||||
i = i+2; // Jump to the end of GWC idiom
|
i = i+2; // Jump to the end of GWC idiom
|
||||||
} else if (isNewArray(rtype, name)) {
|
} else if (isNewArray(rtype, name)) {
|
||||||
emitNewArray(rtype, name);
|
emitNewArray(rtype, name);
|
||||||
|
} else if (isArrayLoad(member)) {
|
||||||
|
emitArrayLoad(name);
|
||||||
|
} else if (isArrayStore(member)) {
|
||||||
|
emitArrayStore(name);
|
||||||
} else if (isStaticallyInvocable(member)) {
|
} else if (isStaticallyInvocable(member)) {
|
||||||
emitStaticInvoke(name);
|
emitStaticInvoke(name);
|
||||||
} else {
|
} else {
|
||||||
@ -634,6 +673,35 @@ class InvokerBytecodeGenerator {
|
|||||||
return classFile;
|
return classFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isArrayLoad(MemberName member) {
|
||||||
|
return member != null &&
|
||||||
|
member.getDeclaringClass() == ArrayAccessor.class &&
|
||||||
|
member.getName() != null &&
|
||||||
|
member.getName().startsWith("getElement");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isArrayStore(MemberName member) {
|
||||||
|
return member != null &&
|
||||||
|
member.getDeclaringClass() == ArrayAccessor.class &&
|
||||||
|
member.getName() != null &&
|
||||||
|
member.getName().startsWith("setElement");
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitArrayLoad(Name name) { emitArrayOp(name, Opcodes.AALOAD); }
|
||||||
|
void emitArrayStore(Name name) { emitArrayOp(name, Opcodes.AASTORE); }
|
||||||
|
|
||||||
|
void emitArrayOp(Name name, int arrayOpcode) {
|
||||||
|
assert arrayOpcode == Opcodes.AALOAD || arrayOpcode == Opcodes.AASTORE;
|
||||||
|
Class<?> elementType = name.function.methodType().parameterType(0).getComponentType();
|
||||||
|
assert elementType != null;
|
||||||
|
emitPushArguments(name);
|
||||||
|
if (elementType.isPrimitive()) {
|
||||||
|
Wrapper w = Wrapper.forPrimitiveType(elementType);
|
||||||
|
arrayOpcode = arrayInsnOpcode(arrayTypeCode(w), arrayOpcode);
|
||||||
|
}
|
||||||
|
mv.visitInsn(arrayOpcode);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit an invoke for the given name.
|
* Emit an invoke for the given name.
|
||||||
*/
|
*/
|
||||||
|
@ -52,27 +52,54 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
|||||||
}
|
}
|
||||||
|
|
||||||
static MethodHandle makeArrayElementAccessor(Class<?> arrayClass, boolean isSetter) {
|
static MethodHandle makeArrayElementAccessor(Class<?> arrayClass, boolean isSetter) {
|
||||||
|
if (arrayClass == Object[].class)
|
||||||
|
return (isSetter ? ArrayAccessor.OBJECT_ARRAY_SETTER : ArrayAccessor.OBJECT_ARRAY_GETTER);
|
||||||
if (!arrayClass.isArray())
|
if (!arrayClass.isArray())
|
||||||
throw newIllegalArgumentException("not an array: "+arrayClass);
|
throw newIllegalArgumentException("not an array: "+arrayClass);
|
||||||
MethodHandle accessor = ArrayAccessor.getAccessor(arrayClass, isSetter);
|
MethodHandle[] cache = ArrayAccessor.TYPED_ACCESSORS.get(arrayClass);
|
||||||
MethodType srcType = accessor.type().erase();
|
int cacheIndex = (isSetter ? ArrayAccessor.SETTER_INDEX : ArrayAccessor.GETTER_INDEX);
|
||||||
MethodType lambdaType = srcType.invokerType();
|
MethodHandle mh = cache[cacheIndex];
|
||||||
Name[] names = arguments(1, lambdaType);
|
if (mh != null) return mh;
|
||||||
Name[] args = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount());
|
mh = ArrayAccessor.getAccessor(arrayClass, isSetter);
|
||||||
names[names.length - 1] = new Name(accessor.asType(srcType), (Object[]) args);
|
MethodType correctType = ArrayAccessor.correctType(arrayClass, isSetter);
|
||||||
LambdaForm form = new LambdaForm("getElement", lambdaType.parameterCount(), names);
|
if (mh.type() != correctType) {
|
||||||
MethodHandle mh = SimpleMethodHandle.make(srcType, form);
|
assert(mh.type().parameterType(0) == Object[].class);
|
||||||
if (ArrayAccessor.needCast(arrayClass)) {
|
assert((isSetter ? mh.type().parameterType(2) : mh.type().returnType()) == Object.class);
|
||||||
mh = mh.bindTo(arrayClass);
|
assert(isSetter || correctType.parameterType(0).getComponentType() == correctType.returnType());
|
||||||
|
// safe to view non-strictly, because element type follows from array type
|
||||||
|
mh = mh.viewAsType(correctType);
|
||||||
|
}
|
||||||
|
// Atomically update accessor cache.
|
||||||
|
synchronized(cache) {
|
||||||
|
if (cache[cacheIndex] == null) {
|
||||||
|
cache[cacheIndex] = mh;
|
||||||
|
} else {
|
||||||
|
// Throw away newly constructed accessor and use cached version.
|
||||||
|
mh = cache[cacheIndex];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mh = mh.asType(ArrayAccessor.correctType(arrayClass, isSetter));
|
|
||||||
return mh;
|
return mh;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class ArrayAccessor {
|
static final class ArrayAccessor {
|
||||||
/// Support for array element access
|
/// Support for array element access
|
||||||
static final HashMap<Class<?>, MethodHandle> GETTER_CACHE = new HashMap<>(); // TODO use it
|
static final int GETTER_INDEX = 0, SETTER_INDEX = 1, INDEX_LIMIT = 2;
|
||||||
static final HashMap<Class<?>, MethodHandle> SETTER_CACHE = new HashMap<>(); // TODO use it
|
static final ClassValue<MethodHandle[]> TYPED_ACCESSORS
|
||||||
|
= new ClassValue<MethodHandle[]>() {
|
||||||
|
@Override
|
||||||
|
protected MethodHandle[] computeValue(Class<?> type) {
|
||||||
|
return new MethodHandle[INDEX_LIMIT];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static final MethodHandle OBJECT_ARRAY_GETTER, OBJECT_ARRAY_SETTER;
|
||||||
|
static {
|
||||||
|
MethodHandle[] cache = TYPED_ACCESSORS.get(Object[].class);
|
||||||
|
cache[GETTER_INDEX] = OBJECT_ARRAY_GETTER = getAccessor(Object[].class, false);
|
||||||
|
cache[SETTER_INDEX] = OBJECT_ARRAY_SETTER = getAccessor(Object[].class, true);
|
||||||
|
|
||||||
|
assert(InvokerBytecodeGenerator.isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_GETTER.internalMemberName()));
|
||||||
|
assert(InvokerBytecodeGenerator.isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_SETTER.internalMemberName()));
|
||||||
|
}
|
||||||
|
|
||||||
static int getElementI(int[] a, int i) { return a[i]; }
|
static int getElementI(int[] a, int i) { return a[i]; }
|
||||||
static long getElementJ(long[] a, int i) { return a[i]; }
|
static long getElementJ(long[] a, int i) { return a[i]; }
|
||||||
@ -94,45 +121,21 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
|||||||
static void setElementC(char[] a, int i, char x) { a[i] = x; }
|
static void setElementC(char[] a, int i, char x) { a[i] = x; }
|
||||||
static void setElementL(Object[] a, int i, Object x) { a[i] = x; }
|
static void setElementL(Object[] a, int i, Object x) { a[i] = x; }
|
||||||
|
|
||||||
static Object getElementL(Class<?> arrayClass, Object[] a, int i) { arrayClass.cast(a); return a[i]; }
|
|
||||||
static void setElementL(Class<?> arrayClass, Object[] a, int i, Object x) { arrayClass.cast(a); a[i] = x; }
|
|
||||||
|
|
||||||
// Weakly typed wrappers of Object[] accessors:
|
|
||||||
static Object getElementL(Object a, int i) { return getElementL((Object[])a, i); }
|
|
||||||
static void setElementL(Object a, int i, Object x) { setElementL((Object[]) a, i, x); }
|
|
||||||
static Object getElementL(Object arrayClass, Object a, int i) { return getElementL((Class<?>) arrayClass, (Object[])a, i); }
|
|
||||||
static void setElementL(Object arrayClass, Object a, int i, Object x) { setElementL((Class<?>) arrayClass, (Object[])a, i, x); }
|
|
||||||
|
|
||||||
static boolean needCast(Class<?> arrayClass) {
|
|
||||||
Class<?> elemClass = arrayClass.getComponentType();
|
|
||||||
return !elemClass.isPrimitive() && elemClass != Object.class;
|
|
||||||
}
|
|
||||||
static String name(Class<?> arrayClass, boolean isSetter) {
|
static String name(Class<?> arrayClass, boolean isSetter) {
|
||||||
Class<?> elemClass = arrayClass.getComponentType();
|
Class<?> elemClass = arrayClass.getComponentType();
|
||||||
if (elemClass == null) throw newIllegalArgumentException("not an array", arrayClass);
|
if (elemClass == null) throw newIllegalArgumentException("not an array", arrayClass);
|
||||||
return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(elemClass);
|
return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(elemClass);
|
||||||
}
|
}
|
||||||
static final boolean USE_WEAKLY_TYPED_ARRAY_ACCESSORS = false; // FIXME: decide
|
|
||||||
static MethodType type(Class<?> arrayClass, boolean isSetter) {
|
static MethodType type(Class<?> arrayClass, boolean isSetter) {
|
||||||
Class<?> elemClass = arrayClass.getComponentType();
|
Class<?> elemClass = arrayClass.getComponentType();
|
||||||
Class<?> arrayArgClass = arrayClass;
|
Class<?> arrayArgClass = arrayClass;
|
||||||
if (!elemClass.isPrimitive()) {
|
if (!elemClass.isPrimitive()) {
|
||||||
arrayArgClass = Object[].class;
|
arrayArgClass = Object[].class;
|
||||||
if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS)
|
elemClass = Object.class;
|
||||||
arrayArgClass = Object.class;
|
|
||||||
}
|
}
|
||||||
if (!needCast(arrayClass)) {
|
|
||||||
return !isSetter ?
|
return !isSetter ?
|
||||||
MethodType.methodType(elemClass, arrayArgClass, int.class) :
|
MethodType.methodType(elemClass, arrayArgClass, int.class) :
|
||||||
MethodType.methodType(void.class, arrayArgClass, int.class, elemClass);
|
MethodType.methodType(void.class, arrayArgClass, int.class, elemClass);
|
||||||
} else {
|
|
||||||
Class<?> classArgClass = Class.class;
|
|
||||||
if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS)
|
|
||||||
classArgClass = Object.class;
|
|
||||||
return !isSetter ?
|
|
||||||
MethodType.methodType(Object.class, classArgClass, arrayArgClass, int.class) :
|
|
||||||
MethodType.methodType(void.class, classArgClass, arrayArgClass, int.class, Object.class);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
static MethodType correctType(Class<?> arrayClass, boolean isSetter) {
|
static MethodType correctType(Class<?> arrayClass, boolean isSetter) {
|
||||||
Class<?> elemClass = arrayClass.getComponentType();
|
Class<?> elemClass = arrayClass.getComponentType();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user