/*
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.invoke;
import sun.invoke.util.VerifyType;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import sun.invoke.empty.Empty;
import sun.invoke.util.ValueConversions;
import sun.invoke.util.Wrapper;
import sun.misc.Unsafe;
import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
/**
* Trusted implementation code for MethodHandle.
* @author jrose
*/
/*non-public*/ abstract class MethodHandleImpl {
/// Factory methods to create method handles:
private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE;
static void initStatics() {
// Trigger preceding sequence.
}
/** Look up a given method.
* Callable only from sun.invoke and related packages.
*
* The resulting method handle type will be of the given type,
* with a receiver type {@code rcvc} prepended if the member is not static.
*
* Access checks are made as of the given lookup class.
* In particular, if the method is protected and {@code defc} is in a
* different package from the lookup class, then {@code rcvc} must be
* the lookup class or a subclass.
* @param token Proof that the lookup class has access to this package.
* @param member Resolved method or constructor to call.
* @param name Name of the desired method.
* @param rcvc Receiver type of desired non-static method (else null)
* @param doDispatch whether the method handle will test the receiver type
* @param lookupClass access-check relative to this class
* @return a direct handle to the matching method
* @throws IllegalAccessException if the given method cannot be accessed by the lookup class
*/
static
MethodHandle findMethod(MemberName method,
boolean doDispatch, Class> lookupClass) throws IllegalAccessException {
MethodType mtype = method.getMethodType();
if (!method.isStatic()) {
// adjust the advertised receiver type to be exactly the one requested
// (in the case of invokespecial, this will be the calling class)
Class> recvType = method.getDeclaringClass();
mtype = mtype.insertParameterTypes(0, recvType);
}
DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass);
if (!mh.isValid())
throw method.makeAccessException("no direct method handle", lookupClass);
assert(mh.type() == mtype);
if (!method.isVarargs())
return mh;
int argc = mtype.parameterCount();
if (argc != 0) {
Class> arrayType = mtype.parameterType(argc-1);
if (arrayType.isArray())
return AdapterMethodHandle.makeVarargsCollector(mh, arrayType);
}
throw method.makeAccessException("cannot make variable arity", null);
}
static
MethodHandle makeAllocator(MethodHandle rawConstructor) {
MethodType rawConType = rawConstructor.type();
Class> allocateClass = rawConType.parameterType(0);
// Wrap the raw (unsafe) constructor with the allocation of a suitable object.
if (AdapterMethodHandle.canCollectArguments(rawConType, MethodType.methodType(allocateClass), 0, true)) {
// allocator(arg...)
// [fold]=> cookedConstructor(obj=allocate(C), arg...)
// [dup,collect]=> identity(obj, void=rawConstructor(obj, arg...))
MethodHandle returner = MethodHandles.identity(allocateClass);
MethodType ctype = rawConType.insertParameterTypes(0, allocateClass).changeReturnType(allocateClass);
MethodHandle cookedConstructor = AdapterMethodHandle.makeCollectArguments(returner, rawConstructor, 1, false);
assert(cookedConstructor.type().equals(ctype));
ctype = ctype.dropParameterTypes(0, 1);
cookedConstructor = AdapterMethodHandle.makeCollectArguments(cookedConstructor, returner, 0, true);
MethodHandle allocator = new AllocateObject(allocateClass);
// allocate() => new C(void)
assert(allocator.type().equals(MethodType.methodType(allocateClass)));
ctype = ctype.dropParameterTypes(0, 1);
MethodHandle fold = foldArguments(cookedConstructor, ctype, 0, allocator);
return fold;
}
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
MethodHandle allocator
= AllocateObject.make(allocateClass, rawConstructor);
assert(allocator.type()
.equals(rawConType.dropParameterTypes(0, 1).changeReturnType(rawConType.parameterType(0))));
return allocator;
}
static final class AllocateObject extends BoundMethodHandle {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private final Class allocateClass;
private final MethodHandle rawConstructor;
private AllocateObject(MethodHandle invoker,
Class allocateClass, MethodHandle rawConstructor) {
super(invoker);
this.allocateClass = allocateClass;
this.rawConstructor = rawConstructor;
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
}
// for allocation only:
private AllocateObject(Class allocateClass) {
super(ALLOCATE.asType(MethodType.methodType(allocateClass, AllocateObject.class)));
this.allocateClass = allocateClass;
this.rawConstructor = null;
}
static MethodHandle make(Class> allocateClass, MethodHandle rawConstructor) {
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
MethodType rawConType = rawConstructor.type();
assert(rawConType.parameterType(0) == allocateClass);
MethodType newType = rawConType.dropParameterTypes(0, 1).changeReturnType(allocateClass);
int nargs = rawConType.parameterCount() - 1;
if (nargs < INVOKES.length) {
MethodHandle invoke = INVOKES[nargs];
MethodType conType = CON_TYPES[nargs];
MethodHandle gcon = convertArguments(rawConstructor, conType, rawConType, 0);
if (gcon == null) return null;
MethodHandle galloc = new AllocateObject(invoke, allocateClass, gcon);
assert(galloc.type() == newType.generic());
return convertArguments(galloc, newType, galloc.type(), 0);
} else {
MethodHandle invoke = VARARGS_INVOKE;
MethodType conType = CON_TYPES[nargs];
MethodHandle gcon = spreadArgumentsFromPos(rawConstructor, conType, 1);
if (gcon == null) return null;
MethodHandle galloc = new AllocateObject(invoke, allocateClass, gcon);
return collectArguments(galloc, newType, 1, null);
}
}
@Override
String debugString() {
return addTypeString(allocateClass.getSimpleName(), this);
}
@SuppressWarnings("unchecked")
private C allocate() throws InstantiationException {
return (C) unsafe.allocateInstance(allocateClass);
}
private C invoke_V(Object... av) throws Throwable {
C obj = allocate();
rawConstructor.invokeExact((Object)obj, av);
return obj;
}
private C invoke_L0() throws Throwable {
C obj = allocate();
rawConstructor.invokeExact((Object)obj);
return obj;
}
private C invoke_L1(Object a0) throws Throwable {
C obj = allocate();
rawConstructor.invokeExact((Object)obj, a0);
return obj;
}
private C invoke_L2(Object a0, Object a1) throws Throwable {
C obj = allocate();
rawConstructor.invokeExact((Object)obj, a0, a1);
return obj;
}
private C invoke_L3(Object a0, Object a1, Object a2) throws Throwable {
C obj = allocate();
rawConstructor.invokeExact((Object)obj, a0, a1, a2);
return obj;
}
private C invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable {
C obj = allocate();
rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3);
return obj;
}
private C invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable {
C obj = allocate();
rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3, a4);
return obj;
}
private C invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable {
C obj = allocate();
rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3, a4, a5);
return obj;
}
private C invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable {
C obj = allocate();
rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3, a4, a5, a6);
return obj;
}
private C invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable {
C obj = allocate();
rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3, a4, a5, a6, a7);
return obj;
}
static MethodHandle[] makeInvokes() {
ArrayList invokes = new ArrayList();
MethodHandles.Lookup lookup = IMPL_LOOKUP;
for (;;) {
int nargs = invokes.size();
String name = "invoke_L"+nargs;
MethodHandle invoke = null;
try {
invoke = lookup.findVirtual(AllocateObject.class, name, MethodType.genericMethodType(nargs));
} catch (ReflectiveOperationException ex) {
}
if (invoke == null) break;
invokes.add(invoke);
}
assert(invokes.size() == 9); // current number of methods
return invokes.toArray(new MethodHandle[0]);
};
static final MethodHandle[] INVOKES = makeInvokes();
// For testing use this:
//static final MethodHandle[] INVOKES = Arrays.copyOf(makeInvokes(), 2);
static final MethodHandle VARARGS_INVOKE;
static final MethodHandle ALLOCATE;
static {
try {
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "invoke_V", MethodType.genericMethodType(0, true));
ALLOCATE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "allocate", MethodType.genericMethodType(0));
} catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
}
// Corresponding generic constructor types:
static final MethodType[] CON_TYPES = new MethodType[INVOKES.length];
static {
for (int i = 0; i < INVOKES.length; i++)
CON_TYPES[i] = makeConType(INVOKES[i]);
}
static final MethodType VARARGS_CON_TYPE = makeConType(VARARGS_INVOKE);
static MethodType makeConType(MethodHandle invoke) {
MethodType invType = invoke.type();
return invType.changeParameterType(0, Object.class).changeReturnType(void.class);
}
}
static
MethodHandle accessField(MemberName member, boolean isSetter,
Class> lookupClass) {
// Use sun. misc.Unsafe to dig up the dirt on the field.
MethodHandle mh = new FieldAccessor(member, isSetter);
return mh;
}
static
MethodHandle accessArrayElement(Class> arrayClass, boolean isSetter) {
if (!arrayClass.isArray())
throw newIllegalArgumentException("not an array: "+arrayClass);
Class> elemClass = arrayClass.getComponentType();
MethodHandle[] mhs = FieldAccessor.ARRAY_CACHE.get(elemClass);
if (mhs == null) {
if (!FieldAccessor.doCache(elemClass))
return FieldAccessor.ahandle(arrayClass, isSetter);
mhs = new MethodHandle[] {
FieldAccessor.ahandle(arrayClass, false),
FieldAccessor.ahandle(arrayClass, true)
};
if (mhs[0].type().parameterType(0) == Class.class) {
mhs[0] = mhs[0].bindTo(elemClass);
mhs[1] = mhs[1].bindTo(elemClass);
}
synchronized (FieldAccessor.ARRAY_CACHE) {} // memory barrier
FieldAccessor.ARRAY_CACHE.put(elemClass, mhs);
}
return mhs[isSetter ? 1 : 0];
}
static final class FieldAccessor extends BoundMethodHandle {
private static final Unsafe unsafe = Unsafe.getUnsafe();
final Object base; // for static refs only
final long offset;
final String name;
FieldAccessor(MemberName field, boolean isSetter) {
super(fhandle(field.getDeclaringClass(), field.getFieldType(), isSetter, field.isStatic()));
this.offset = (long) field.getVMIndex();
this.name = field.getName();
this.base = staticBase(field);
}
@Override
String debugString() { return addTypeString(name, this); }
int getFieldI(C obj) { return unsafe.getInt(obj, offset); }
void setFieldI(C obj, int x) { unsafe.putInt(obj, offset, x); }
long getFieldJ(C obj) { return unsafe.getLong(obj, offset); }
void setFieldJ(C obj, long x) { unsafe.putLong(obj, offset, x); }
float getFieldF(C obj) { return unsafe.getFloat(obj, offset); }
void setFieldF(C obj, float x) { unsafe.putFloat(obj, offset, x); }
double getFieldD(C obj) { return unsafe.getDouble(obj, offset); }
void setFieldD(C obj, double x) { unsafe.putDouble(obj, offset, x); }
boolean getFieldZ(C obj) { return unsafe.getBoolean(obj, offset); }
void setFieldZ(C obj, boolean x) { unsafe.putBoolean(obj, offset, x); }
byte getFieldB(C obj) { return unsafe.getByte(obj, offset); }
void setFieldB(C obj, byte x) { unsafe.putByte(obj, offset, x); }
short getFieldS(C obj) { return unsafe.getShort(obj, offset); }
void setFieldS(C obj, short x) { unsafe.putShort(obj, offset, x); }
char getFieldC(C obj) { return unsafe.getChar(obj, offset); }
void setFieldC(C obj, char x) { unsafe.putChar(obj, offset, x); }
@SuppressWarnings("unchecked")
V getFieldL(C obj) { return (V) unsafe.getObject(obj, offset); }
@SuppressWarnings("unchecked")
void setFieldL(C obj, V x) { unsafe.putObject(obj, offset, x); }
// cast (V) is OK here, since we wrap convertArguments around the MH.
static Object staticBase(final MemberName field) {
if (!field.isStatic()) return null;
return AccessController.doPrivileged(new PrivilegedAction