8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
Reviewed-by: mhaupt, shade
This commit is contained in:
parent
bb160cec9c
commit
fff5c5bf8d
@ -846,7 +846,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Lookup LOOKUP = Lookup.IMPL_LOOKUP;
|
static final Lookup LOOKUP = Lookup.IMPL_LOOKUP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All subclasses must provide such a value describing their type signature.
|
* All subclasses must provide such a value describing their type signature.
|
||||||
|
@ -259,7 +259,7 @@ class DirectMethodHandle extends MethodHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void maybeCompile(LambdaForm lform, MemberName m) {
|
private static void maybeCompile(LambdaForm lform, MemberName m) {
|
||||||
if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class))
|
if (lform.vmentry == null && VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class))
|
||||||
// Help along bootstrapping...
|
// Help along bootstrapping...
|
||||||
lform.compileToBytecode();
|
lform.compileToBytecode();
|
||||||
}
|
}
|
||||||
@ -562,25 +562,36 @@ class DirectMethodHandle extends MethodHandle {
|
|||||||
return lform;
|
return lform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Wrapper[] ALL_WRAPPERS = Wrapper.values();
|
||||||
|
|
||||||
private static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) {
|
private static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) {
|
||||||
boolean isGetter = (formOp & 1) == (AF_GETFIELD & 1);
|
boolean isGetter = (formOp & 1) == (AF_GETFIELD & 1);
|
||||||
boolean isStatic = (formOp >= AF_GETSTATIC);
|
boolean isStatic = (formOp >= AF_GETSTATIC);
|
||||||
boolean needsInit = (formOp >= AF_GETSTATIC_INIT);
|
boolean needsInit = (formOp >= AF_GETSTATIC_INIT);
|
||||||
boolean needsCast = (ftypeKind == FT_CHECKED_REF);
|
boolean needsCast = (ftypeKind == FT_CHECKED_REF);
|
||||||
Wrapper fw = (needsCast ? Wrapper.OBJECT : Wrapper.values()[ftypeKind]);
|
Wrapper fw = (needsCast ? Wrapper.OBJECT : ALL_WRAPPERS[ftypeKind]);
|
||||||
Class<?> ft = fw.primitiveType();
|
Class<?> ft = fw.primitiveType();
|
||||||
assert(ftypeKind(needsCast ? String.class : ft) == ftypeKind);
|
assert(ftypeKind(needsCast ? String.class : ft) == ftypeKind);
|
||||||
String tname = fw.primitiveSimpleName();
|
|
||||||
String ctname = Character.toUpperCase(tname.charAt(0)) + tname.substring(1);
|
// getObject, putIntVolatile, etc.
|
||||||
if (isVolatile) ctname += "Volatile";
|
StringBuilder nameBuilder = new StringBuilder();
|
||||||
String getOrPut = (isGetter ? "get" : "put");
|
if (isGetter) {
|
||||||
String linkerName = (getOrPut + ctname); // getObject, putIntVolatile, etc.
|
nameBuilder.append("get");
|
||||||
|
} else {
|
||||||
|
nameBuilder.append("put");
|
||||||
|
}
|
||||||
|
nameBuilder.append(fw.primitiveSimpleName());
|
||||||
|
nameBuilder.setCharAt(3, Character.toUpperCase(nameBuilder.charAt(3)));
|
||||||
|
if (isVolatile) {
|
||||||
|
nameBuilder.append("Volatile");
|
||||||
|
}
|
||||||
|
|
||||||
MethodType linkerType;
|
MethodType linkerType;
|
||||||
if (isGetter)
|
if (isGetter)
|
||||||
linkerType = MethodType.methodType(ft, Object.class, long.class);
|
linkerType = MethodType.methodType(ft, Object.class, long.class);
|
||||||
else
|
else
|
||||||
linkerType = MethodType.methodType(void.class, Object.class, long.class, ft);
|
linkerType = MethodType.methodType(void.class, Object.class, long.class, ft);
|
||||||
MemberName linker = new MemberName(Unsafe.class, linkerName, linkerType, REF_invokeVirtual);
|
MemberName linker = new MemberName(Unsafe.class, nameBuilder.toString(), linkerType, REF_invokeVirtual);
|
||||||
try {
|
try {
|
||||||
linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, NoSuchMethodException.class);
|
linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, NoSuchMethodException.class);
|
||||||
} catch (ReflectiveOperationException ex) {
|
} catch (ReflectiveOperationException ex) {
|
||||||
@ -635,11 +646,16 @@ class DirectMethodHandle extends MethodHandle {
|
|||||||
if (needsCast && isGetter)
|
if (needsCast && isGetter)
|
||||||
names[POST_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
|
names[POST_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
|
||||||
for (Name n : names) assert(n != null);
|
for (Name n : names) assert(n != null);
|
||||||
String fieldOrStatic = (isStatic ? "Static" : "Field");
|
// add some detail to the lambdaForm debugname,
|
||||||
String lambdaName = (linkerName + fieldOrStatic); // significant only for debugging
|
// significant only for debugging
|
||||||
if (needsCast) lambdaName += "Cast";
|
if (isStatic) {
|
||||||
if (needsInit) lambdaName += "Init";
|
nameBuilder.append("Static");
|
||||||
return new LambdaForm(lambdaName, ARG_LIMIT, names, RESULT);
|
} else {
|
||||||
|
nameBuilder.append("Field");
|
||||||
|
}
|
||||||
|
if (needsCast) nameBuilder.append("Cast");
|
||||||
|
if (needsInit) nameBuilder.append("Init");
|
||||||
|
return new LambdaForm(nameBuilder.toString(), ARG_LIMIT, names, RESULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,8 +40,8 @@ import java.io.FileOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static java.lang.invoke.LambdaForm.*;
|
import static java.lang.invoke.LambdaForm.*;
|
||||||
@ -68,6 +68,7 @@ class InvokerBytecodeGenerator {
|
|||||||
private static final String LFN_SIG = "L" + LFN + ";";
|
private static final String LFN_SIG = "L" + LFN + ";";
|
||||||
private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
|
private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
|
||||||
private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V";
|
private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V";
|
||||||
|
private static final String CLASS_PREFIX = LF + "$";
|
||||||
|
|
||||||
/** Name of its super class*/
|
/** Name of its super class*/
|
||||||
static final String INVOKER_SUPER_NAME = OBJ;
|
static final String INVOKER_SUPER_NAME = OBJ;
|
||||||
@ -96,15 +97,15 @@ class InvokerBytecodeGenerator {
|
|||||||
/** Main constructor; other constructors delegate to this one. */
|
/** Main constructor; other constructors delegate to this one. */
|
||||||
private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
|
private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
|
||||||
String className, String invokerName, MethodType invokerType) {
|
String className, String invokerName, MethodType invokerType) {
|
||||||
if (invokerName.contains(".")) {
|
int p = invokerName.indexOf('.');
|
||||||
int p = invokerName.indexOf('.');
|
if (p > -1) {
|
||||||
className = invokerName.substring(0, p);
|
className = invokerName.substring(0, p);
|
||||||
invokerName = invokerName.substring(p+1);
|
invokerName = invokerName.substring(p + 1);
|
||||||
}
|
}
|
||||||
if (DUMP_CLASS_FILES) {
|
if (DUMP_CLASS_FILES) {
|
||||||
className = makeDumpableClassName(className);
|
className = makeDumpableClassName(className);
|
||||||
}
|
}
|
||||||
this.className = LF + "$" + className;
|
this.className = CLASS_PREFIX + className;
|
||||||
this.sourceFile = "LambdaForm$" + className;
|
this.sourceFile = "LambdaForm$" + className;
|
||||||
this.lambdaForm = lambdaForm;
|
this.lambdaForm = lambdaForm;
|
||||||
this.invokerName = invokerName;
|
this.invokerName = invokerName;
|
||||||
@ -201,38 +202,34 @@ class InvokerBytecodeGenerator {
|
|||||||
|
|
||||||
class CpPatch {
|
class CpPatch {
|
||||||
final int index;
|
final int index;
|
||||||
final String placeholder;
|
|
||||||
final Object value;
|
final Object value;
|
||||||
CpPatch(int index, String placeholder, Object value) {
|
CpPatch(int index, Object value) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.placeholder = placeholder;
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value;
|
return "CpPatch/index="+index+",value="+value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Object, CpPatch> cpPatches = new HashMap<>();
|
private final ArrayList<CpPatch> cpPatches = new ArrayList<>();
|
||||||
|
|
||||||
int cph = 0; // for counting constant placeholders
|
private int cph = 0; // for counting constant placeholders
|
||||||
|
|
||||||
String constantPlaceholder(Object arg) {
|
String constantPlaceholder(Object arg) {
|
||||||
String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
|
String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
|
||||||
if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(arg) + ">>"; // debugging aid
|
if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(arg) + ">>";
|
||||||
if (cpPatches.containsKey(cpPlaceholder)) {
|
// TODO check if arg is already in the constant pool
|
||||||
throw new InternalError("observed CP placeholder twice: " + cpPlaceholder);
|
|
||||||
}
|
|
||||||
// insert placeholder in CP and remember the patch
|
// insert placeholder in CP and remember the patch
|
||||||
int index = cw.newConst((Object) cpPlaceholder); // TODO check if already in the constant pool
|
int index = cw.newConst((Object) cpPlaceholder);
|
||||||
cpPatches.put(cpPlaceholder, new CpPatch(index, cpPlaceholder, arg));
|
cpPatches.add(new CpPatch(index, arg));
|
||||||
return cpPlaceholder;
|
return cpPlaceholder;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object[] cpPatches(byte[] classFile) {
|
Object[] cpPatches(byte[] classFile) {
|
||||||
int size = getConstantPoolSize(classFile);
|
int size = getConstantPoolSize(classFile);
|
||||||
Object[] res = new Object[size];
|
Object[] res = new Object[size];
|
||||||
for (CpPatch p : cpPatches.values()) {
|
for (CpPatch p : cpPatches) {
|
||||||
if (p.index >= size)
|
if (p.index >= size)
|
||||||
throw new InternalError("in cpool["+size+"]: "+p+"\n"+Arrays.toString(Arrays.copyOf(classFile, 20)));
|
throw new InternalError("in cpool["+size+"]: "+p+"\n"+Arrays.toString(Arrays.copyOf(classFile, 20)));
|
||||||
res[p.index] = p.value;
|
res[p.index] = p.value;
|
||||||
@ -741,7 +738,7 @@ class InvokerBytecodeGenerator {
|
|||||||
continue;
|
continue;
|
||||||
case IDENTITY:
|
case IDENTITY:
|
||||||
assert(name.arguments.length == 1);
|
assert(name.arguments.length == 1);
|
||||||
emitPushArguments(name);
|
emitPushArguments(name, 0);
|
||||||
continue;
|
continue;
|
||||||
case ZERO:
|
case ZERO:
|
||||||
assert(name.arguments.length == 0);
|
assert(name.arguments.length == 0);
|
||||||
@ -795,7 +792,7 @@ class InvokerBytecodeGenerator {
|
|||||||
assert arrayOpcode == Opcodes.AALOAD || arrayOpcode == Opcodes.AASTORE || arrayOpcode == Opcodes.ARRAYLENGTH;
|
assert arrayOpcode == Opcodes.AALOAD || arrayOpcode == Opcodes.AASTORE || arrayOpcode == Opcodes.ARRAYLENGTH;
|
||||||
Class<?> elementType = name.function.methodType().parameterType(0).getComponentType();
|
Class<?> elementType = name.function.methodType().parameterType(0).getComponentType();
|
||||||
assert elementType != null;
|
assert elementType != null;
|
||||||
emitPushArguments(name);
|
emitPushArguments(name, 0);
|
||||||
if (arrayOpcode != Opcodes.ARRAYLENGTH && elementType.isPrimitive()) {
|
if (arrayOpcode != Opcodes.ARRAYLENGTH && elementType.isPrimitive()) {
|
||||||
Wrapper w = Wrapper.forPrimitiveType(elementType);
|
Wrapper w = Wrapper.forPrimitiveType(elementType);
|
||||||
arrayOpcode = arrayInsnOpcode(arrayTypeCode(w), arrayOpcode);
|
arrayOpcode = arrayInsnOpcode(arrayTypeCode(w), arrayOpcode);
|
||||||
@ -824,7 +821,7 @@ class InvokerBytecodeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// push arguments
|
// push arguments
|
||||||
emitPushArguments(name);
|
emitPushArguments(name, 0);
|
||||||
|
|
||||||
// invocation
|
// invocation
|
||||||
MethodType type = name.function.methodType();
|
MethodType type = name.function.methodType();
|
||||||
@ -923,7 +920,7 @@ class InvokerBytecodeGenerator {
|
|||||||
assert(!(member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual));
|
assert(!(member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual));
|
||||||
|
|
||||||
// push arguments
|
// push arguments
|
||||||
emitPushArguments(name);
|
emitPushArguments(name, 0);
|
||||||
|
|
||||||
// invocation
|
// invocation
|
||||||
if (member.isMethod()) {
|
if (member.isMethod()) {
|
||||||
@ -1444,13 +1441,10 @@ class InvokerBytecodeGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void emitPushArguments(Name args) {
|
|
||||||
emitPushArguments(args, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void emitPushArguments(Name args, int start) {
|
private void emitPushArguments(Name args, int start) {
|
||||||
|
MethodType type = args.function.methodType();
|
||||||
for (int i = start; i < args.arguments.length; i++) {
|
for (int i = start; i < args.arguments.length; i++) {
|
||||||
emitPushArgument(args, i);
|
emitPushArgument(type.parameterType(i), args.arguments[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,9 +149,9 @@ class LambdaForm {
|
|||||||
static final int ARG_TYPE_LIMIT = ARG_TYPES.length;
|
static final int ARG_TYPE_LIMIT = ARG_TYPES.length;
|
||||||
static final int TYPE_LIMIT = ALL_TYPES.length;
|
static final int TYPE_LIMIT = ALL_TYPES.length;
|
||||||
|
|
||||||
private final char btChar;
|
final char btChar;
|
||||||
private final Class<?> btClass;
|
final Class<?> btClass;
|
||||||
private final Wrapper btWrapper;
|
final Wrapper btWrapper;
|
||||||
|
|
||||||
private BasicType(char btChar, Class<?> btClass, Wrapper wrapper) {
|
private BasicType(char btChar, Class<?> btClass, Wrapper wrapper) {
|
||||||
this.btChar = btChar;
|
this.btChar = btChar;
|
||||||
@ -1366,10 +1366,11 @@ class LambdaForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String basicTypeSignature(MethodType type) {
|
public static String basicTypeSignature(MethodType type) {
|
||||||
char[] sig = new char[type.parameterCount() + 2];
|
int params = type.parameterCount();
|
||||||
|
char[] sig = new char[params + 2];
|
||||||
int sigp = 0;
|
int sigp = 0;
|
||||||
for (Class<?> pt : type.parameterList()) {
|
while (sigp < params) {
|
||||||
sig[sigp++] = basicTypeChar(pt);
|
sig[sigp] = basicTypeChar(type.parameterType(sigp++));
|
||||||
}
|
}
|
||||||
sig[sigp++] = '_';
|
sig[sigp++] = '_';
|
||||||
sig[sigp++] = basicTypeChar(type.returnType());
|
sig[sigp++] = basicTypeChar(type.returnType());
|
||||||
@ -1407,7 +1408,7 @@ class LambdaForm {
|
|||||||
|
|
||||||
static final class Name {
|
static final class Name {
|
||||||
final BasicType type;
|
final BasicType type;
|
||||||
private short index;
|
@Stable short index;
|
||||||
final NamedFunction function;
|
final NamedFunction function;
|
||||||
final Object constraint; // additional type information, if not null
|
final Object constraint; // additional type information, if not null
|
||||||
@Stable final Object[] arguments;
|
@Stable final Object[] arguments;
|
||||||
|
@ -78,7 +78,7 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
|
|||||||
private int flags; // modifier bits; see reflect.Modifier
|
private int flags; // modifier bits; see reflect.Modifier
|
||||||
//@Injected JVM_Method* vmtarget;
|
//@Injected JVM_Method* vmtarget;
|
||||||
//@Injected int vmindex;
|
//@Injected int vmindex;
|
||||||
private Object resolution; // if null, this guy is resolved
|
Object resolution; // if null, this guy is resolved
|
||||||
|
|
||||||
/** Return the declaring class of this member.
|
/** Return the declaring class of this member.
|
||||||
* In the case of a bare name and type, the declaring class will be null.
|
* In the case of a bare name and type, the declaring class will be null.
|
||||||
@ -826,7 +826,7 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
|
|||||||
return resolution == null;
|
return resolution == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initResolved(boolean isResolved) {
|
void initResolved(boolean isResolved) {
|
||||||
assert(this.resolution == null); // not initialized yet!
|
assert(this.resolution == null); // not initialized yet!
|
||||||
if (!isResolved)
|
if (!isResolved)
|
||||||
this.resolution = this;
|
this.resolution = this;
|
||||||
|
@ -38,10 +38,9 @@ import sun.invoke.empty.Empty;
|
|||||||
import sun.invoke.util.ValueConversions;
|
import sun.invoke.util.ValueConversions;
|
||||||
import sun.invoke.util.VerifyType;
|
import sun.invoke.util.VerifyType;
|
||||||
import sun.invoke.util.Wrapper;
|
import sun.invoke.util.Wrapper;
|
||||||
|
import sun.security.action.GetPropertyAction;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.PrivilegedAction;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -61,18 +60,9 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||||||
*/
|
*/
|
||||||
/*non-public*/ abstract class MethodHandleImpl {
|
/*non-public*/ abstract class MethodHandleImpl {
|
||||||
// Do not adjust this except for special platforms:
|
// Do not adjust this except for special platforms:
|
||||||
private static final int MAX_ARITY;
|
private static final int MAX_ARITY = Integer.decode(
|
||||||
static {
|
GetPropertyAction.privilegedGetProperty(
|
||||||
final Object[] values = { 255 };
|
MethodHandleImpl.class.getName()+".MAX_ARITY", "255"));
|
||||||
AccessController.doPrivileged(new PrivilegedAction<>() {
|
|
||||||
@Override
|
|
||||||
public Void run() {
|
|
||||||
values[0] = Integer.getInteger(MethodHandleImpl.class.getName()+".MAX_ARITY", 255);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
MAX_ARITY = (Integer) values[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Factory methods to create method handles:
|
/// Factory methods to create method handles:
|
||||||
|
|
||||||
@ -652,7 +642,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||||||
MethodType srcType = targetType // (a..., [b...])=>r
|
MethodType srcType = targetType // (a..., [b...])=>r
|
||||||
.dropParameterTypes(collectArgPos, collectArgPos+collectValCount);
|
.dropParameterTypes(collectArgPos, collectArgPos+collectValCount);
|
||||||
if (!retainOriginalArgs) { // (a..., b...)=>r
|
if (!retainOriginalArgs) { // (a..., b...)=>r
|
||||||
srcType = srcType.insertParameterTypes(collectArgPos, collectorType.parameterList());
|
srcType = srcType.insertParameterTypes(collectArgPos, collectorType.parameterArray());
|
||||||
}
|
}
|
||||||
// in arglist: [0: ...keep1 | cpos: collect... | cpos+cacount: keep2... ]
|
// in arglist: [0: ...keep1 | cpos: collect... | cpos+cacount: keep2... ]
|
||||||
// out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ]
|
// out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ]
|
||||||
@ -1097,7 +1087,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||||||
int arity = type.parameterCount();
|
int arity = type.parameterCount();
|
||||||
if (arity > 1) {
|
if (arity > 1) {
|
||||||
MethodHandle mh = throwException(type.dropParameterTypes(1, arity));
|
MethodHandle mh = throwException(type.dropParameterTypes(1, arity));
|
||||||
mh = MethodHandles.dropArguments(mh, 1, type.parameterList().subList(1, arity));
|
mh = MethodHandles.dropArguments(mh, 1, Arrays.copyOfRange(type.parameterArray(), 1, arity));
|
||||||
return mh;
|
return mh;
|
||||||
}
|
}
|
||||||
return makePairwiseConvert(NF_throwException.resolvedHandle(), type, false, true);
|
return makePairwiseConvert(NF_throwException.resolvedHandle(), type, false, true);
|
||||||
|
@ -77,7 +77,7 @@ public class MethodHandles {
|
|||||||
|
|
||||||
private MethodHandles() { } // do not instantiate
|
private MethodHandles() { } // do not instantiate
|
||||||
|
|
||||||
private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
|
static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
|
||||||
|
|
||||||
// See IMPL_LOOKUP below.
|
// See IMPL_LOOKUP below.
|
||||||
|
|
||||||
@ -3268,12 +3268,11 @@ assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
|
|||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
|
MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
|
||||||
return dropArguments0(target, pos, copyTypes(valueTypes));
|
return dropArguments0(target, pos, copyTypes(valueTypes.toArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Class<?>> copyTypes(List<Class<?>> types) {
|
private static List<Class<?>> copyTypes(Object[] array) {
|
||||||
Object[] a = types.toArray();
|
return Arrays.asList(Arrays.copyOf(array, array.length, Class[].class));
|
||||||
return Arrays.asList(Arrays.copyOf(a, a.length, Class[].class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static
|
private static
|
||||||
@ -3352,13 +3351,13 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
|
|||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) {
|
MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) {
|
||||||
return dropArguments(target, pos, Arrays.asList(valueTypes));
|
return dropArguments0(target, pos, copyTypes(valueTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
// private version which allows caller some freedom with error handling
|
// private version which allows caller some freedom with error handling
|
||||||
private static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos,
|
private static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos,
|
||||||
boolean nullOnFailure) {
|
boolean nullOnFailure) {
|
||||||
newTypes = copyTypes(newTypes);
|
newTypes = copyTypes(newTypes.toArray());
|
||||||
List<Class<?>> oldTypes = target.type().parameterList();
|
List<Class<?>> oldTypes = target.type().parameterList();
|
||||||
int match = oldTypes.size();
|
int match = oldTypes.size();
|
||||||
if (skip != 0) {
|
if (skip != 0) {
|
||||||
@ -3900,10 +3899,14 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
|
|||||||
int foldVals = rtype == void.class ? 0 : 1;
|
int foldVals = rtype == void.class ? 0 : 1;
|
||||||
int afterInsertPos = foldPos + foldVals;
|
int afterInsertPos = foldPos + foldVals;
|
||||||
boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
|
boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
|
||||||
if (ok && !(combinerType.parameterList()
|
if (ok) {
|
||||||
.equals(targetType.parameterList().subList(afterInsertPos,
|
for (int i = 0; i < foldArgs; i++) {
|
||||||
afterInsertPos + foldArgs))))
|
if (combinerType.parameterType(i) != targetType.parameterType(i + afterInsertPos)) {
|
||||||
ok = false;
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(foldPos))
|
if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(foldPos))
|
||||||
ok = false;
|
ok = false;
|
||||||
if (!ok)
|
if (!ok)
|
||||||
|
@ -539,10 +539,10 @@ class MethodType implements java.io.Serializable {
|
|||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
// insert after (if need be), then before
|
// insert after (if need be), then before
|
||||||
if (pos < parameterList().size() - 1) {
|
if (pos < ptypes.length - 1) {
|
||||||
res = res.insertParameterTypes(arrayLength, parameterList().subList(pos + 1, parameterList().size()));
|
res = res.insertParameterTypes(arrayLength, Arrays.copyOfRange(ptypes, pos + 1, ptypes.length));
|
||||||
}
|
}
|
||||||
return res.insertParameterTypes(0, parameterList().subList(0, pos));
|
return res.insertParameterTypes(0, Arrays.copyOf(ptypes, pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,7 +285,7 @@ public final class StringConcatFactory {
|
|||||||
el.add(new RecipeElement(argC++));
|
el.add(new RecipeElement(argC++));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Not a special characters, this is a constant embedded into
|
// Not a special character, this is a constant embedded into
|
||||||
// the recipe itself.
|
// the recipe itself.
|
||||||
acc.append(c);
|
acc.append(c);
|
||||||
}
|
}
|
||||||
@ -321,30 +321,32 @@ public final class StringConcatFactory {
|
|||||||
private static final class RecipeElement {
|
private static final class RecipeElement {
|
||||||
private final Object value;
|
private final Object value;
|
||||||
private final int argPos;
|
private final int argPos;
|
||||||
|
private final char tag;
|
||||||
|
|
||||||
public RecipeElement(Object cnst) {
|
public RecipeElement(Object cnst) {
|
||||||
this.value = Objects.requireNonNull(cnst);
|
this.value = Objects.requireNonNull(cnst);
|
||||||
this.argPos = -1;
|
this.argPos = -1;
|
||||||
|
this.tag = TAG_CONST;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecipeElement(int arg) {
|
public RecipeElement(int arg) {
|
||||||
this.value = null;
|
this.value = null;
|
||||||
assert (arg >= 0);
|
|
||||||
this.argPos = arg;
|
this.argPos = arg;
|
||||||
|
this.tag = TAG_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getValue() {
|
public Object getValue() {
|
||||||
assert (isConst());
|
assert (tag == TAG_CONST);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getArgPos() {
|
public int getArgPos() {
|
||||||
assert (!isConst());
|
assert (tag == TAG_ARG);
|
||||||
return argPos;
|
return argPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConst() {
|
public char getTag() {
|
||||||
return argPos == -1;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -354,16 +356,15 @@ public final class StringConcatFactory {
|
|||||||
|
|
||||||
RecipeElement that = (RecipeElement) o;
|
RecipeElement that = (RecipeElement) o;
|
||||||
|
|
||||||
boolean isConst = isConst();
|
if (this.tag != that.tag) return false;
|
||||||
if (isConst != that.isConst()) return false;
|
if (this.tag == TAG_CONST && (!value.equals(that.value))) return false;
|
||||||
if (isConst && (!value.equals(that.value))) return false;
|
if (this.tag == TAG_ARG && (argPos != that.argPos)) return false;
|
||||||
if (!isConst && (argPos != that.argPos)) return false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return argPos;
|
return (int)tag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,19 +644,20 @@ public final class StringConcatFactory {
|
|||||||
* @return argument types the strategy is going to use
|
* @return argument types the strategy is going to use
|
||||||
*/
|
*/
|
||||||
private static MethodType adaptType(MethodType args) {
|
private static MethodType adaptType(MethodType args) {
|
||||||
Class<?>[] ptypes = args.parameterArray();
|
Class<?>[] ptypes = null;
|
||||||
boolean changed = false;
|
for (int i = 0; i < args.parameterCount(); i++) {
|
||||||
for (int i = 0; i < ptypes.length; i++) {
|
Class<?> ptype = args.parameterType(i);
|
||||||
Class<?> ptype = ptypes[i];
|
|
||||||
if (!ptype.isPrimitive() &&
|
if (!ptype.isPrimitive() &&
|
||||||
ptype != String.class &&
|
ptype != String.class &&
|
||||||
ptype != Object.class) { // truncate to Object
|
ptype != Object.class) { // truncate to Object
|
||||||
|
if (ptypes == null) {
|
||||||
|
ptypes = args.parameterArray();
|
||||||
|
}
|
||||||
ptypes[i] = Object.class;
|
ptypes[i] = Object.class;
|
||||||
changed = true;
|
|
||||||
}
|
}
|
||||||
// else other primitives or String or Object (unchanged)
|
// else other primitives or String or Object (unchanged)
|
||||||
}
|
}
|
||||||
return changed
|
return (ptypes != null)
|
||||||
? MethodType.methodType(args.returnType(), ptypes)
|
? MethodType.methodType(args.returnType(), ptypes)
|
||||||
: args;
|
: args;
|
||||||
}
|
}
|
||||||
@ -874,24 +876,29 @@ public final class StringConcatFactory {
|
|||||||
|
|
||||||
int off = 0;
|
int off = 0;
|
||||||
for (RecipeElement el : recipe.getElements()) {
|
for (RecipeElement el : recipe.getElements()) {
|
||||||
if (el.isConst()) {
|
switch (el.getTag()) {
|
||||||
// Guaranteed non-null, no null check required.
|
case TAG_CONST:
|
||||||
} else {
|
// Guaranteed non-null, no null check required.
|
||||||
// Null-checks are needed only for String arguments, and when a previous stage
|
break;
|
||||||
// did not do implicit null-checks. If a String is null, we eagerly replace it
|
case TAG_ARG:
|
||||||
// with "null" constant. Note, we omit Objects here, because we don't call
|
// Null-checks are needed only for String arguments, and when a previous stage
|
||||||
// .length() on them down below.
|
// did not do implicit null-checks. If a String is null, we eagerly replace it
|
||||||
int ac = el.getArgPos();
|
// with "null" constant. Note, we omit Objects here, because we don't call
|
||||||
Class<?> cl = arr[ac];
|
// .length() on them down below.
|
||||||
if (cl == String.class && !guaranteedNonNull[ac]) {
|
int ac = el.getArgPos();
|
||||||
Label l0 = new Label();
|
Class<?> cl = arr[ac];
|
||||||
mv.visitIntInsn(ALOAD, off);
|
if (cl == String.class && !guaranteedNonNull[ac]) {
|
||||||
mv.visitJumpInsn(IFNONNULL, l0);
|
Label l0 = new Label();
|
||||||
mv.visitLdcInsn("null");
|
mv.visitIntInsn(ALOAD, off);
|
||||||
mv.visitIntInsn(ASTORE, off);
|
mv.visitJumpInsn(IFNONNULL, l0);
|
||||||
mv.visitLabel(l0);
|
mv.visitLdcInsn("null");
|
||||||
}
|
mv.visitIntInsn(ASTORE, off);
|
||||||
off += getParameterSize(cl);
|
mv.visitLabel(l0);
|
||||||
|
}
|
||||||
|
off += getParameterSize(cl);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new StringConcatException("Unhandled tag: " + el.getTag());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -912,30 +919,35 @@ public final class StringConcatFactory {
|
|||||||
mv.visitInsn(ICONST_0);
|
mv.visitInsn(ICONST_0);
|
||||||
|
|
||||||
for (RecipeElement el : recipe.getElements()) {
|
for (RecipeElement el : recipe.getElements()) {
|
||||||
if (el.isConst()) {
|
switch (el.getTag()) {
|
||||||
Object cnst = el.getValue();
|
case TAG_CONST:
|
||||||
len += cnst.toString().length();
|
Object cnst = el.getValue();
|
||||||
} else {
|
len += cnst.toString().length();
|
||||||
/*
|
break;
|
||||||
If an argument is String, then we can call .length() on it. Sized/Exact modes have
|
case TAG_ARG:
|
||||||
converted arguments for us. If an argument is primitive, we can provide a guess
|
/*
|
||||||
for its String representation size.
|
If an argument is String, then we can call .length() on it. Sized/Exact modes have
|
||||||
*/
|
converted arguments for us. If an argument is primitive, we can provide a guess
|
||||||
Class<?> cl = arr[el.getArgPos()];
|
for its String representation size.
|
||||||
if (cl == String.class) {
|
*/
|
||||||
mv.visitIntInsn(ALOAD, off);
|
Class<?> cl = arr[el.getArgPos()];
|
||||||
mv.visitMethodInsn(
|
if (cl == String.class) {
|
||||||
INVOKEVIRTUAL,
|
mv.visitIntInsn(ALOAD, off);
|
||||||
"java/lang/String",
|
mv.visitMethodInsn(
|
||||||
"length",
|
INVOKEVIRTUAL,
|
||||||
"()I",
|
"java/lang/String",
|
||||||
false
|
"length",
|
||||||
);
|
"()I",
|
||||||
mv.visitInsn(IADD);
|
false
|
||||||
} else if (cl.isPrimitive()) {
|
);
|
||||||
len += estimateSize(cl);
|
mv.visitInsn(IADD);
|
||||||
}
|
} else if (cl.isPrimitive()) {
|
||||||
off += getParameterSize(cl);
|
len += estimateSize(cl);
|
||||||
|
}
|
||||||
|
off += getParameterSize(cl);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new StringConcatException("Unhandled tag: " + el.getTag());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -967,15 +979,20 @@ public final class StringConcatFactory {
|
|||||||
int off = 0;
|
int off = 0;
|
||||||
for (RecipeElement el : recipe.getElements()) {
|
for (RecipeElement el : recipe.getElements()) {
|
||||||
String desc;
|
String desc;
|
||||||
if (el.isConst()) {
|
switch (el.getTag()) {
|
||||||
Object cnst = el.getValue();
|
case TAG_CONST:
|
||||||
mv.visitLdcInsn(cnst);
|
Object cnst = el.getValue();
|
||||||
desc = getSBAppendDesc(cnst.getClass());
|
mv.visitLdcInsn(cnst);
|
||||||
} else {
|
desc = getSBAppendDesc(cnst.getClass());
|
||||||
Class<?> cl = arr[el.getArgPos()];
|
break;
|
||||||
mv.visitVarInsn(getLoadOpcode(cl), off);
|
case TAG_ARG:
|
||||||
off += getParameterSize(cl);
|
Class<?> cl = arr[el.getArgPos()];
|
||||||
desc = getSBAppendDesc(cl);
|
mv.visitVarInsn(getLoadOpcode(cl), off);
|
||||||
|
off += getParameterSize(cl);
|
||||||
|
desc = getSBAppendDesc(cl);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new StringConcatException("Unhandled tag: " + el.getTag());
|
||||||
}
|
}
|
||||||
|
|
||||||
mv.visitMethodInsn(
|
mv.visitMethodInsn(
|
||||||
@ -1245,7 +1262,6 @@ public final class StringConcatFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Class<?>> ptypesList = Arrays.asList(ptypes);
|
|
||||||
MethodHandle[] lengthers = new MethodHandle[pc];
|
MethodHandle[] lengthers = new MethodHandle[pc];
|
||||||
|
|
||||||
// Figure out lengths: constants' lengths can be deduced on the spot.
|
// Figure out lengths: constants' lengths can be deduced on the spot.
|
||||||
@ -1253,24 +1269,29 @@ public final class StringConcatFactory {
|
|||||||
// call the usual String.length(). Primitive values string sizes can be estimated.
|
// call the usual String.length(). Primitive values string sizes can be estimated.
|
||||||
int initial = 0;
|
int initial = 0;
|
||||||
for (RecipeElement el : recipe.getElements()) {
|
for (RecipeElement el : recipe.getElements()) {
|
||||||
if (el.isConst()) {
|
switch (el.getTag()) {
|
||||||
Object cnst = el.getValue();
|
case TAG_CONST:
|
||||||
initial += cnst.toString().length();
|
Object cnst = el.getValue();
|
||||||
} else {
|
initial += cnst.toString().length();
|
||||||
final int i = el.getArgPos();
|
break;
|
||||||
Class<?> type = ptypesList.get(i);
|
case TAG_ARG:
|
||||||
if (type.isPrimitive()) {
|
final int i = el.getArgPos();
|
||||||
MethodHandle est = MethodHandles.constant(int.class, estimateSize(type));
|
Class<?> type = ptypes[i];
|
||||||
est = MethodHandles.dropArguments(est, 0, type);
|
if (type.isPrimitive()) {
|
||||||
lengthers[i] = est;
|
MethodHandle est = MethodHandles.constant(int.class, estimateSize(type));
|
||||||
} else {
|
est = MethodHandles.dropArguments(est, 0, type);
|
||||||
lengthers[i] = STRING_LENGTH;
|
lengthers[i] = est;
|
||||||
}
|
} else {
|
||||||
|
lengthers[i] = STRING_LENGTH;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new StringConcatException("Unhandled tag: " + el.getTag());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create (StringBuilder, <args>) shape for appending:
|
// Create (StringBuilder, <args>) shape for appending:
|
||||||
MethodHandle builder = MethodHandles.dropArguments(MethodHandles.identity(StringBuilder.class), 1, ptypesList);
|
MethodHandle builder = MethodHandles.dropArguments(MethodHandles.identity(StringBuilder.class), 1, ptypes);
|
||||||
|
|
||||||
// Compose append calls. This is done in reverse because the application order is
|
// Compose append calls. This is done in reverse because the application order is
|
||||||
// reverse as well.
|
// reverse as well.
|
||||||
@ -1278,19 +1299,24 @@ public final class StringConcatFactory {
|
|||||||
for (int i = elements.size() - 1; i >= 0; i--) {
|
for (int i = elements.size() - 1; i >= 0; i--) {
|
||||||
RecipeElement el = elements.get(i);
|
RecipeElement el = elements.get(i);
|
||||||
MethodHandle appender;
|
MethodHandle appender;
|
||||||
if (el.isConst()) {
|
switch (el.getTag()) {
|
||||||
Object constant = el.getValue();
|
case TAG_CONST:
|
||||||
MethodHandle mh = appender(adaptToStringBuilder(constant.getClass()));
|
Object constant = el.getValue();
|
||||||
appender = MethodHandles.insertArguments(mh, 1, constant);
|
MethodHandle mh = appender(adaptToStringBuilder(constant.getClass()));
|
||||||
} else {
|
appender = MethodHandles.insertArguments(mh, 1, constant);
|
||||||
int ac = el.getArgPos();
|
break;
|
||||||
appender = appender(ptypesList.get(ac));
|
case TAG_ARG:
|
||||||
|
int ac = el.getArgPos();
|
||||||
|
appender = appender(ptypes[ac]);
|
||||||
|
|
||||||
// Insert dummy arguments to match the prefix in the signature.
|
// Insert dummy arguments to match the prefix in the signature.
|
||||||
// The actual appender argument will be the ac-ith argument.
|
// The actual appender argument will be the ac-ith argument.
|
||||||
if (ac != 0) {
|
if (ac != 0) {
|
||||||
appender = MethodHandles.dropArguments(appender, 1, ptypesList.subList(0, ac));
|
appender = MethodHandles.dropArguments(appender, 1, Arrays.copyOf(ptypes, ac));
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new StringConcatException("Unhandled tag: " + el.getTag());
|
||||||
}
|
}
|
||||||
builder = MethodHandles.foldArguments(builder, appender);
|
builder = MethodHandles.foldArguments(builder, appender);
|
||||||
}
|
}
|
||||||
@ -1460,7 +1486,6 @@ public final class StringConcatFactory {
|
|||||||
ptypes[i] = filter.type().returnType();
|
ptypes[i] = filter.type().returnType();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<Class<?>> ptypesList = Arrays.asList(ptypes);
|
|
||||||
|
|
||||||
// Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes"
|
// Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes"
|
||||||
// with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up,
|
// with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up,
|
||||||
@ -1481,12 +1506,17 @@ public final class StringConcatFactory {
|
|||||||
// *ending* index.
|
// *ending* index.
|
||||||
for (RecipeElement el : recipe.getElements()) {
|
for (RecipeElement el : recipe.getElements()) {
|
||||||
MethodHandle prepender;
|
MethodHandle prepender;
|
||||||
if (el.isConst()) {
|
switch (el.getTag()) {
|
||||||
Object cnst = el.getValue();
|
case TAG_CONST:
|
||||||
prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);
|
Object cnst = el.getValue();
|
||||||
} else {
|
prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);
|
||||||
int pos = el.getArgPos();
|
break;
|
||||||
prepender = selectArgument(prepender(ptypesList.get(pos)), 3, ptypesList, pos);
|
case TAG_ARG:
|
||||||
|
int pos = el.getArgPos();
|
||||||
|
prepender = selectArgument(prepender(ptypes[pos]), 3, ptypes, pos);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new StringConcatException("Unhandled tag: " + el.getTag());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove "old" index from arguments
|
// Remove "old" index from arguments
|
||||||
@ -1507,7 +1537,7 @@ public final class StringConcatFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fold in byte[] instantiation at argument 0.
|
// Fold in byte[] instantiation at argument 0.
|
||||||
MethodHandle combiner = MethodHandles.dropArguments(NEW_ARRAY, 2, ptypesList);
|
MethodHandle combiner = MethodHandles.dropArguments(NEW_ARRAY, 2, ptypes);
|
||||||
mh = MethodHandles.foldArguments(mh, combiner);
|
mh = MethodHandles.foldArguments(mh, combiner);
|
||||||
|
|
||||||
// Start combining length and coder mixers.
|
// Start combining length and coder mixers.
|
||||||
@ -1526,36 +1556,41 @@ public final class StringConcatFactory {
|
|||||||
byte initialCoder = INITIAL_CODER;
|
byte initialCoder = INITIAL_CODER;
|
||||||
int initialLen = 0; // initial length, in characters
|
int initialLen = 0; // initial length, in characters
|
||||||
for (RecipeElement el : recipe.getElements()) {
|
for (RecipeElement el : recipe.getElements()) {
|
||||||
if (el.isConst()) {
|
switch (el.getTag()) {
|
||||||
Object constant = el.getValue();
|
case TAG_CONST:
|
||||||
String s = constant.toString();
|
Object constant = el.getValue();
|
||||||
initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
|
String s = constant.toString();
|
||||||
initialLen += s.length();
|
initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
|
||||||
} else {
|
initialLen += s.length();
|
||||||
int ac = el.getArgPos();
|
break;
|
||||||
|
case TAG_ARG:
|
||||||
|
int ac = el.getArgPos();
|
||||||
|
|
||||||
Class<?> argClass = ptypesList.get(ac);
|
Class<?> argClass = ptypes[ac];
|
||||||
MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypesList, ac);
|
MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypes, ac);
|
||||||
lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*)
|
lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*)
|
||||||
lm = MethodHandles.dropArguments(lm, 2, byte.class);
|
lm = MethodHandles.dropArguments(lm, 2, byte.class);
|
||||||
|
|
||||||
MethodHandle cm = selectArgument(coderMixer(argClass), 1, ptypesList, ac);
|
MethodHandle cm = selectArgument(coderMixer(argClass), 1, ptypes, ac);
|
||||||
cm = MethodHandles.dropArguments(cm, 0, int.class); // (**)
|
cm = MethodHandles.dropArguments(cm, 0, int.class); // (**)
|
||||||
|
|
||||||
// Read this bottom up:
|
// Read this bottom up:
|
||||||
|
|
||||||
// 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
|
// 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
|
||||||
mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
|
mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
|
||||||
|
|
||||||
// 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
|
// 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
|
||||||
// Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*)
|
// Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*)
|
||||||
mh = MethodHandles.foldArguments(mh, lm);
|
mh = MethodHandles.foldArguments(mh, lm);
|
||||||
|
|
||||||
// 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
|
// 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
|
||||||
// Coder mixer ignores the "old-index" arg due to dropArguments above (**)
|
// Coder mixer ignores the "old-index" arg due to dropArguments above (**)
|
||||||
mh = MethodHandles.foldArguments(mh, cm);
|
mh = MethodHandles.foldArguments(mh, cm);
|
||||||
|
|
||||||
// 1. The mh shape here is ("old-index", "old-coder", <args>)
|
// 1. The mh shape here is ("old-index", "old-coder", <args>)
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new StringConcatException("Unhandled tag: " + el.getTag());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1582,14 +1617,14 @@ public final class StringConcatFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Adapts: (...prefix..., parameter[pos])R -> (...prefix..., ...parameters...)R
|
// Adapts: (...prefix..., parameter[pos])R -> (...prefix..., ...parameters...)R
|
||||||
private static MethodHandle selectArgument(MethodHandle mh, int prefix, List<Class<?>> ptypes, int pos) {
|
private static MethodHandle selectArgument(MethodHandle mh, int prefix, Class<?>[] ptypes, int pos) {
|
||||||
if (pos == 0) {
|
if (pos == 0) {
|
||||||
return MethodHandles.dropArguments(mh, prefix + 1, ptypes.subList(1, ptypes.size()));
|
return MethodHandles.dropArguments(mh, prefix + 1, Arrays.copyOfRange(ptypes, 1, ptypes.length));
|
||||||
} else if (pos == ptypes.size() - 1) {
|
} else if (pos == ptypes.length - 1) {
|
||||||
return MethodHandles.dropArguments(mh, prefix, ptypes.subList(0, ptypes.size() - 1));
|
return MethodHandles.dropArguments(mh, prefix, Arrays.copyOf(ptypes, ptypes.length - 1));
|
||||||
} else { // 0 < pos < ptypes.size() - 1
|
} else { // 0 < pos < ptypes.size() - 1
|
||||||
MethodHandle t = MethodHandles.dropArguments(mh, prefix, ptypes.subList(0, pos));
|
MethodHandle t = MethodHandles.dropArguments(mh, prefix, Arrays.copyOf(ptypes, pos));
|
||||||
return MethodHandles.dropArguments(t, prefix + 1 + pos, ptypes.subList(pos + 1, ptypes.size()));
|
return MethodHandles.dropArguments(t, prefix + 1 + pos, Arrays.copyOfRange(ptypes, pos + 1, ptypes.length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1648,8 +1683,8 @@ public final class StringConcatFactory {
|
|||||||
private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
|
private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
|
||||||
private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS;
|
private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS;
|
||||||
private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS;
|
private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS;
|
||||||
private static final Class<?> STRING_HELPER;
|
|
||||||
private static final byte INITIAL_CODER;
|
private static final byte INITIAL_CODER;
|
||||||
|
static final Class<?> STRING_HELPER;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
@ -1751,7 +1786,7 @@ public final class StringConcatFactory {
|
|||||||
|
|
||||||
/* ------------------------------- Common utilities ------------------------------------ */
|
/* ------------------------------- Common utilities ------------------------------------ */
|
||||||
|
|
||||||
private static MethodHandle lookupStatic(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
|
static MethodHandle lookupStatic(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
|
||||||
try {
|
try {
|
||||||
return lookup.findStatic(refc, name, MethodType.methodType(rtype, ptypes));
|
return lookup.findStatic(refc, name, MethodType.methodType(rtype, ptypes));
|
||||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||||
@ -1759,7 +1794,7 @@ public final class StringConcatFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodHandle lookupVirtual(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
|
static MethodHandle lookupVirtual(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
|
||||||
try {
|
try {
|
||||||
return lookup.findVirtual(refc, name, MethodType.methodType(rtype, ptypes));
|
return lookup.findVirtual(refc, name, MethodType.methodType(rtype, ptypes));
|
||||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||||
@ -1767,7 +1802,7 @@ public final class StringConcatFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodHandle lookupConstructor(Lookup lookup, Class<?> refc, Class<?> ptypes) {
|
static MethodHandle lookupConstructor(Lookup lookup, Class<?> refc, Class<?> ptypes) {
|
||||||
try {
|
try {
|
||||||
return lookup.findConstructor(refc, MethodType.methodType(void.class, ptypes));
|
return lookup.findConstructor(refc, MethodType.methodType(void.class, ptypes));
|
||||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||||
@ -1775,7 +1810,7 @@ public final class StringConcatFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int estimateSize(Class<?> cl) {
|
static int estimateSize(Class<?> cl) {
|
||||||
if (cl == Integer.TYPE) {
|
if (cl == Integer.TYPE) {
|
||||||
return 11; // "-2147483648"
|
return 11; // "-2147483648"
|
||||||
} else if (cl == Boolean.TYPE) {
|
} else if (cl == Boolean.TYPE) {
|
||||||
@ -1797,7 +1832,7 @@ public final class StringConcatFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Class<?> adaptToStringBuilder(Class<?> c) {
|
static Class<?> adaptToStringBuilder(Class<?> c) {
|
||||||
if (c.isPrimitive()) {
|
if (c.isPrimitive()) {
|
||||||
if (c == Byte.TYPE || c == Short.TYPE) {
|
if (c == Byte.TYPE || c == Short.TYPE) {
|
||||||
return int.class;
|
return int.class;
|
||||||
|
@ -31,7 +31,6 @@ import java.lang.invoke.VarHandle.AccessMode;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +53,8 @@ final class VarForm {
|
|||||||
List<Class<?>> l = new ArrayList<>();
|
List<Class<?>> l = new ArrayList<>();
|
||||||
if (receiver != null)
|
if (receiver != null)
|
||||||
l.add(receiver);
|
l.add(receiver);
|
||||||
l.addAll(Arrays.asList(intermediate));
|
for (Class<?> c : intermediate)
|
||||||
|
l.add(c);
|
||||||
|
|
||||||
// (Receiver, <Intermediates>)Value
|
// (Receiver, <Intermediates>)Value
|
||||||
methodType_table[VarHandle.AccessType.GET.ordinal()] =
|
methodType_table[VarHandle.AccessType.GET.ordinal()] =
|
||||||
|
@ -28,6 +28,7 @@ package sun.invoke.util;
|
|||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import static java.lang.reflect.Modifier.*;
|
import static java.lang.reflect.Modifier.*;
|
||||||
import java.lang.reflect.Module;
|
import java.lang.reflect.Module;
|
||||||
|
import java.util.Objects;
|
||||||
import jdk.internal.reflect.Reflection;
|
import jdk.internal.reflect.Reflection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -330,15 +331,7 @@ public class VerifyAccess {
|
|||||||
return true;
|
return true;
|
||||||
if (class1.getClassLoader() != class2.getClassLoader())
|
if (class1.getClassLoader() != class2.getClassLoader())
|
||||||
return false;
|
return false;
|
||||||
String name1 = class1.getName(), name2 = class2.getName();
|
return Objects.equals(class1.getPackageName(), class2.getPackageName());
|
||||||
int dot = name1.lastIndexOf('.');
|
|
||||||
if (dot != name2.lastIndexOf('.'))
|
|
||||||
return false;
|
|
||||||
for (int i = 0; i < dot; i++) {
|
|
||||||
if (name1.charAt(i) != name2.charAt(i))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the package name for this class.
|
/** Return the package name for this class.
|
||||||
|
Loading…
Reference in New Issue
Block a user