Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JEP-370 Implementation (Part 2) #8414

Merged
merged 7 commits into from
Feb 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion jcl/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
package java.lang.invoke;

import java.lang.reflect.Method;
import sun.invoke.util.Wrapper;

/*
* Stub class to compile OpenJDK j.l.i.MethodHandleImpl
Expand Down Expand Up @@ -116,7 +117,30 @@ enum BasicType {
V_TYPE;

static BasicType basicType(Class<?> cls) {
throw OpenJDKCompileStub.OpenJDKCompileStubThrowError();
/* Wrapper.forPrimitiveType throws an IllegalArgumentException for
* non-primitive types (L_TYPE).
*/
Wrapper wrapper = Wrapper.forPrimitiveType(cls);
BasicType basicType = null;
if (wrapper != null) {
char basicTypeChar = wrapper.basicTypeChar();
if ((basicTypeChar == 'C') || (basicTypeChar == 'B') || (basicTypeChar == 'Z')
|| (basicTypeChar == 'I') || (basicTypeChar == 'S')
) {
basicType = I_TYPE;
} else if (basicTypeChar == 'J') {
basicType = J_TYPE;
} else if (basicTypeChar == 'F') {
basicType = F_TYPE;
} else if (basicTypeChar == 'D') {
basicType = D_TYPE;
} else if (basicTypeChar == 'V') {
basicType = V_TYPE;
} else {
throw new InternalError("Unknown basic type char: " + basicTypeChar);
}
}
return basicType;
DanHeidinga marked this conversation as resolved.
Show resolved Hide resolved
}
}
/*[IF Java10]*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ public MemberName(MethodHandle methodHandle) {
/*[ENDIF] Java11 */

/*[IF Java14]*/
Method method;

public MemberName(Method method) {
throw OpenJDKCompileStub.OpenJDKCompileStubThrowError();
this.method = method;
}

public boolean isStatic() {
Expand Down
208 changes: 195 additions & 13 deletions jcl/src/java.base/share/classes/java/lang/invoke/VarHandle.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
/*[IF Java12]*/
import java.util.Optional;
/*[ENDIF]*/
Expand All @@ -46,9 +47,12 @@
import java.util.Objects;
/*[ENDIF] Java12 */

/*[IF Java14]*/
import java.util.Map;
import java.util.HashMap;

/*[IF Java14]*/
import java.util.function.BiFunction;
import java.lang.reflect.Method;
/*[ENDIF] Java14 */

/**
Expand Down Expand Up @@ -227,9 +231,13 @@ public enum AccessMode {
boolean isSetter;
private String methodName;

/*[IF Java14]*/
static final Map<String, AccessMode> methodNameToAccessMode = null;
/*[ENDIF] Java14 */
static final Map<String, AccessMode> methodNameToAccessMode;
static {
methodNameToAccessMode = new HashMap<>();
for (AccessMode accessMode : values()) {
methodNameToAccessMode.put(accessMode.methodName, accessMode);
}
}

AccessMode(String methodName, AccessType signatureType, boolean isSetter) {
this.methodName = methodName;
Expand All @@ -251,10 +259,9 @@ public String methodName() {
* @return The AccessMode associated with the provided method name.
*/
public static AccessMode valueFromMethodName(String methodName) {
for (AccessMode m : values()) {
if (m.methodName.equals(methodName)) {
return m;
}
AccessMode accessMode = methodNameToAccessMode.get(methodName);
if (accessMode != null) {
return accessMode;
}

/*[MSG "K0633", "{0} is not a valid AccessMode."]*/
Expand All @@ -269,13 +276,65 @@ enum AccessType {
COMPARE_AND_EXCHANGE,
GET_AND_UPDATE;

MethodType accessModeType(Class<?> param1, Class<?> param2, Class<?>... params) {
throw OpenJDKCompileStub.OpenJDKCompileStubThrowError();
/**
* Gets the MethodType associated with the AccessType.
*
* This method gets invoked by the derived VarHandle classes through accessModeTypeUncached.
*
* OpenJ9 only uses it to retrieve the receiver class, which is not available from VarForm.
*
* @param receiver class of the derived VarHandle.
* @param type is the field type or value type.
* @param args is the list of intermediate argument classes in the derived VarHandle's
* AccessMode methods.
* @return the MethodType for the corresponding AccessType.
*/
MethodType accessModeType(Class<?> receiver, Class<?> type, Class<?>... args) {
List<Class<?>> paramList = new ArrayList<>();
Class<?> returnType = null;
switch (this) {
case GET:
returnType = type;
paramList.add(receiver);
Collections.addAll(paramList, args);
break;
case SET:
returnType = void.class;
paramList.add(receiver);
Collections.addAll(paramList, args);
paramList.add(type);
break;
case COMPARE_AND_SET:
returnType = boolean.class;
paramList.add(receiver);
Collections.addAll(paramList, args);
Collections.addAll(paramList, type, type);
break;
case COMPARE_AND_EXCHANGE:
returnType = type;
paramList.add(receiver);
Collections.addAll(paramList, args);
Collections.addAll(paramList, type, type);
break;
case GET_AND_UPDATE:
returnType = type;
paramList.add(receiver);
Collections.addAll(paramList, args);
paramList.add(type);
break;
default:
throw new InternalError("Invalid AccessType.");
}
return MethodType.methodType(returnType, paramList);
}
}

static final Unsafe _unsafe = Unsafe.getUnsafe();
static final Lookup _lookup = Lookup.internalPrivilegedLookup;

/*[IF Java14]*/
static final BiFunction<String, List<Integer>, ArrayIndexOutOfBoundsException> AIOOBE_SUPPLIER = null;
babsingh marked this conversation as resolved.
Show resolved Hide resolved
/*[ENDIF] Java14 */

private final MethodHandle[] handleTable;
final Class<?> fieldType;
Expand Down Expand Up @@ -304,13 +363,136 @@ MethodType accessModeType(Class<?> param1, Class<?> param2, Class<?>... params)
/**
* Constructs a generic VarHandle instance.
*
* @param varForm An instance of VarForm.
* @param varForm an instance of VarForm.
*/
VarHandle(VarForm varForm) {
throw OpenJDKCompileStub.OpenJDKCompileStubThrowError();
AccessMode[] accessModes = AccessMode.values();
int numAccessModes = accessModes.length;

/* The first argument in AccessType.GET MethodType is the receiver class. */
Class<?> receiverActual = accessModeTypeUncached(AccessMode.GET).parameterType(0);
Class<?> receiverVarForm = varForm.methodType_table[AccessType.GET.ordinal()].parameterType(0);

/* Specify the exact operation method types if the actual receiver doesn't match the
babsingh marked this conversation as resolved.
Show resolved Hide resolved
* receiver derived from VarForm.
*/
MethodType[] operationMTsExact = null;
if (receiverActual != receiverVarForm) {
operationMTsExact = new MethodType[numAccessModes];
}

MethodType[] operationMTs = new MethodType[numAccessModes];
Class<?> operationsClass = null;

for (int i = 0; i < numAccessModes; i++) {
MemberName memberName = varForm.memberName_table[i];
if (memberName != null) {
Method method = memberName.method;
if (method != null) {
operationMTs[i] = MethodType.methodType(method.getReturnType(), method.getParameterTypes());
DanHeidinga marked this conversation as resolved.
Show resolved Hide resolved
if (operationMTsExact != null) {
/* Replace with the actual receiver, which is expected when the operation method
* is invoked. The receiver is the second argument.
*/
operationMTsExact[i] = operationMTs[i].changeParameterType(1, receiverActual);
}
if (operationsClass == null) {
operationsClass = method.getDeclaringClass();
}
}
}
}

MethodType getter = operationMTs[AccessMode.GET.ordinal()];
MethodType setter = operationMTs[AccessMode.SET.ordinal()];

if (operationMTsExact != null) {
getter = operationMTsExact[AccessMode.GET.ordinal()];
setter = operationMTsExact[AccessMode.SET.ordinal()];
}

this.fieldType = setter.parameterType(setter.parameterCount() - 1);
this.coordinateTypes = getter.parameterArray();
if (operationMTsExact != null) {
this.handleTable = populateMHsJEP370(operationsClass, operationMTs, operationMTsExact);
} else {
this.handleTable = populateMHsJEP370(operationsClass, operationMTs, operationMTs);
}
this.modifiers = 0;
}

static final BiFunction<String, List<Integer>, ArrayIndexOutOfBoundsException> AIOOBE_SUPPLIER = null;
/**
* This is derived from populateMHs in order to support the OpenJDK VarHandle operations classes, which
* don't extend the VarHandleOperations class.
*
* @param operationsClass the class which has all AccessMode methods defined.
* @param lookupTypes the method types for the AccessMode methods in the operationsClass.
* @param exactType the exact method types to be expected when VarHandle AccessMode methods are invoked.
* @return a MethodHandle array which is used to initialize the handleTable.
*/
static MethodHandle[] populateMHsJEP370(Class<?> operationsClass, MethodType[] lookupTypes, MethodType[] exactTypes) {
MethodHandle[] operationMHs = new MethodHandle[AccessMode.values().length];

try {
/* Lookup the MethodHandles corresponding to access modes. */
for (AccessMode accessMode : AccessMode.values()) {
MethodType lookupType = lookupTypes[accessMode.ordinal()];
if (lookupType != null) {
operationMHs[accessMode.ordinal()] = _lookup.findStatic(operationsClass, accessMode.methodName(), lookupType);
if (lookupTypes == exactTypes) {
operationMHs[accessMode.ordinal()] = permuateHandleJEP370(operationMHs[accessMode.ordinal()]);
} else {
/* Clone the MethodHandles with the exact types if different set of exactTypes are provided. */
MethodType exactType = exactTypes[accessMode.ordinal()];
if (exactType != null) {
operationMHs[accessMode.ordinal()] = operationMHs[accessMode.ordinal()].cloneWithNewType(exactType);
}
operationMHs[accessMode.ordinal()] = permuateHandleJEP370(operationMHs[accessMode.ordinal()]);
}
}
}
} catch (IllegalAccessException | NoSuchMethodException e) {
/* [MSG "K0623", "Unable to create MethodHandle to VarHandle operation."] */
InternalError error = new InternalError(com.ibm.oti.util.Msg.getString("K0623")); //$NON-NLS-1$
error.initCause(e);
throw error;
}

return operationMHs;
}

/**
* Generate a MethodHandle which translates:
* FROM {Receiver, Intermediate ..., Value, VarHandle}ReturnType
* TO {VarHandle, Receiver, Intermediate ..., Value}ReturnType
*
* @param methodHandle to be permuted.
* @return the adapter MethodHandle which performs the translation.
*/
static MethodHandle permuateHandleJEP370(MethodHandle methodHandle) {
/* HandleType = {VarHandle, Receiver, Intermediate ..., Value}
* PermuteType = {Receiver, Intermediate ..., Value, VarHandle}
*/
MethodType permuteMethodType = methodHandle.type;
int parameterCount = permuteMethodType.parameterCount();
Class<?> firstParameter = permuteMethodType.parameterType(0);
permuteMethodType = permuteMethodType.dropFirstParameterType();
permuteMethodType = permuteMethodType.appendParameterTypes(firstParameter);

/* reorder specifies the mapping between PermuteType and HandleType
* reorder = {parameterCount - 1, 0, 1, 2, ..., parameterCount - 2}
*/
int[] reorder = new int[parameterCount];
for (int i = 0; i < parameterCount; i++) {
if (i == 0) {
reorder[i] = parameterCount - 1;
} else {
reorder[i] = i - 1;
}
}

return MethodHandles.permuteArguments(methodHandle, permuteMethodType, reorder);
}
/*[ENDIF] Java14 */

Class<?> getDefiningClass() {
Expand Down