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