--- old/src/java.base/share/classes/java/lang/Class.java 2014-10-30 16:11:40.000000000 -0400 +++ new/src/java.base/share/classes/java/lang/Class.java 2014-10-30 16:11:39.000000000 -0400 @@ -67,8 +67,10 @@ import sun.reflect.generics.repository.ConstructorRepository; import sun.reflect.generics.scope.ClassScope; import sun.security.util.SecurityConstants; + import java.lang.annotation.Annotation; import java.lang.reflect.Proxy; + import sun.reflect.annotation.*; import sun.reflect.misc.ReflectUtil; @@ -2462,6 +2464,153 @@ } /** + * ClassData + */ + private static class ClassData { + /** + * This needs to be a simple data structure because we need to access + * and update its elements from the JVM. Note that the Java side controls + * the allocation and order of elements in the array; the JVM modifies + * fields of those elements during class redefinition. + */ + private volatile Comparable[] elementData; + private volatile int size; + + /** + * Interns a member name in the member name table. + * Returns null if a race with the jvm occurred. Races are detected + * by checking for changes in the class redefinition count that occur + * before an intern is complete. + * + * @param klass class whose redefinition count is checked. + * @param memberName member name to be interned + * @param redefined_count the value of classRedefinedCount() observed before + * creation of the MemberName that is being interned. + * @return null if a race occurred, otherwise the interned MemberName. + */ + @SuppressWarnings({"unchecked","rawtypes"}) + public > E intern(Class klass, E memberName, int redefined_count) { + if (elementData == null) { + synchronized (this) { + if (elementData == null) { + elementData = new Comparable[1]; + } + } + } + synchronized (elementData) { + final int index = Arrays.binarySearch(elementData, 0, size, memberName); + if (index >= 0) { + return (E) elementData[index]; + } + // Not found, add carefully. + return add(klass, ~index, memberName, redefined_count); + } + } + + /** + * Appends the specified element at the specified index. + * + * @param klass the klass for this ClassData. + * @param index index at which insertion should occur + * @param e element to be insert into this list + * @param redefined_count value of klass.classRedefinedCount() before e was created. + * @return null if insertion failed because of redefinition race, otherwise e. + */ + private > E add(Class klass, int index, E e, int redefined_count) { + int oldCapacity = size; + Comparable[] element_data = elementData; + if (oldCapacity + 1 > element_data.length ) { + // Replacing array with a copy is safe; elements are identical. + grow(oldCapacity + 1); + element_data = elementData; + } + /* + * Careful dance to insert an element. + * + * Wish to ensure that we do not hide an element of + * the array; must also ensure that visible-to-jvm + * portion of array never contains nulls; also ensure + * that array remains sorted. + * + * Note that taking a safepoint is also a barrier. + */ + if (oldCapacity > 0) { + element_data[oldCapacity] = element_data[oldCapacity - 1]; + // all array elements are non-null and sorted, increase size. + // if store to element_data above floats below + // store to size on the next line, that will be + // inconsistent to the VM if a safepoint occurs here. + size += 1; + for (int i = oldCapacity; i > index; i--) { + // pre: element_data[i] is duplicated at [i+1] + element_data[i] = element_data[i - 1]; + // post: element_data[i-1] is duplicated at [i] + } + // element_data[index] is duplicated at [index+1] + element_data[index] = (Comparable) e; + } else { + element_data[index] = (Comparable) e; + // Don't want store to element_data[index] above + // to float past store to size below, otherwise + // VM might see inconsistent state. + size += 1; + } + if (redefined_count == klass.classRedefinedCount()) { + return e; + } + /* The race was lost, must undo insertion and retry the intern + * with a new resolution. + */ + for (int i = index; i < oldCapacity; i++) { + element_data[i] = element_data[i+1]; + } + size -= 1; + return null; + } + + /** + * Increases the capacity to ensure that it can hold at least the + * number of elements specified by the minimum capacity argument. + * + * @param minCapacity the desired minimum capacity + */ + private void grow(int minCapacity) { + // overflow-conscious code + int oldCapacity = elementData.length; + int newCapacity = oldCapacity + (oldCapacity >> 1); + if (newCapacity - minCapacity < 0) + newCapacity = minCapacity; + // minCapacity is usually close to size, so this is a win: + elementData = Arrays.copyOf(elementData, newCapacity); + } + } + + private volatile transient ClassData classData; + + /** + * Lazily create {@link ClassData}. + */ + private ClassData classData() { + if (this.classData != null) { + return this.classData; + } + synchronized (this) { + if (this.classData == null) { + this.classData = new ClassData<>(); + } + } + return this.classData; + } + + /* package */ > E internMemberName(E memberName, int redefined_count) { + return classData().intern(this, memberName, redefined_count); + } + + /* package */ int classRedefinedCount() { + return classRedefinedCount; + } + + /** * Reflection support. */ --- old/src/java.base/share/classes/java/lang/System.java 2014-10-30 16:11:41.000000000 -0400 +++ new/src/java.base/share/classes/java/lang/System.java 2014-10-30 16:11:41.000000000 -0400 @@ -1269,6 +1269,12 @@ public void formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) { Integer.formatUnsignedInt(val, shift, buf, offset, len); } + public > E internMemberName(Class klass, E memberName, int transaction_token) { + return klass.internMemberName(memberName, transaction_token); + } + public int getClassRedefinedCount(Class klass) { + return klass.classRedefinedCount(); + } }); } } --- old/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java 2014-10-30 16:11:42.000000000 -0400 +++ new/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java 2014-10-30 16:11:42.000000000 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -109,10 +109,10 @@ return make(member.getDeclaringClass(), member); } static DirectMethodHandle make(Method method) { - return make(method.getDeclaringClass(), new MemberName(method)); + return make(method.getDeclaringClass(), MemberName.make(method)); } static DirectMethodHandle make(Field field) { - return make(field.getDeclaringClass(), new MemberName(field)); + return make(field.getDeclaringClass(), MemberName.make(field)); } private static DirectMethodHandle makeAllocator(MemberName ctor) { assert(ctor.isConstructor() && ctor.getName().equals("")); --- old/src/java.base/share/classes/java/lang/invoke/InfoFromMemberName.java 2014-10-30 16:11:43.000000000 -0400 +++ new/src/java.base/share/classes/java/lang/invoke/InfoFromMemberName.java 2014-10-30 16:11:42.000000000 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -133,12 +133,12 @@ private static MemberName convertToMemberName(byte refKind, Member mem) throws IllegalAccessException { if (mem instanceof Method) { boolean wantSpecial = (refKind == REF_invokeSpecial); - return new MemberName((Method) mem, wantSpecial); + return MemberName.make((Method) mem, wantSpecial); } else if (mem instanceof Constructor) { - return new MemberName((Constructor) mem); + return MemberName.make((Constructor) mem); } else if (mem instanceof Field) { boolean isSetter = (refKind == REF_putField || refKind == REF_putStatic); - return new MemberName((Field) mem, isSetter); + return MemberName.make((Field) mem, isSetter); } throw new InternalError(mem.getClass().getName()); } --- old/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2014-10-30 16:11:43.000000000 -0400 +++ new/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2014-10-30 16:11:43.000000000 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -286,14 +286,11 @@ private static MemberName resolveInvokerMember(Class invokerClass, String name, MethodType type) { MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic); - //System.out.println("resolveInvokerMember => "+member); - //for (Method m : invokerClass.getDeclaredMethods()) System.out.println(" "+m); try { member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class); } catch (ReflectiveOperationException e) { throw newInternalError(e); } - //System.out.println("resolveInvokerMember => "+member); return member; } --- old/src/java.base/share/classes/java/lang/invoke/LambdaForm.java 2014-10-30 16:11:45.000000000 -0400 +++ new/src/java.base/share/classes/java/lang/invoke/LambdaForm.java 2014-10-30 16:11:45.000000000 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1034,10 +1034,10 @@ // This is OK, since all such LFs are prepared with special primitive vmentry points. // And even without the resolvedHandle, the name can still be compiled and optimized. NamedFunction(Method method) { - this(new MemberName(method)); + this(MemberName.make(method)); } NamedFunction(Field field) { - this(new MemberName(field)); + this(MemberName.make(field)); } NamedFunction(MemberName member) { this.member = member; --- old/src/java.base/share/classes/java/lang/invoke/MemberName.java 2014-10-30 16:11:46.000000000 -0400 +++ new/src/java.base/share/classes/java/lang/invoke/MemberName.java 2014-10-30 16:11:46.000000000 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,8 +38,13 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; + import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleStatics.*; + +import sun.misc.JavaLangAccess; +import sun.misc.SharedSecrets; + import java.util.Objects; /** @@ -69,7 +74,8 @@ * and those seven fields omit much of the information in Method. * @author jrose */ -/*non-public*/ final class MemberName implements Member, Cloneable { +@SuppressWarnings("rawtypes") //Comparable in next line +/*non-public*/ final class MemberName implements Member, Comparable, Cloneable { private Class clazz; // class in which the method is defined private String name; // may be null if not yet materialized private Object type; // may be null if not yet materialized @@ -78,9 +84,297 @@ //@Injected int vmindex; private Object resolution; // if null, this guy is resolved + private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); + + // bare-bones constructor; the JVM will fill it in + MemberName() { } + + /** Create a name for the given class. The resulting name will be in a resolved state. */ + public MemberName(Class type) { + init(type.getDeclaringClass(), type.getSimpleName(), type, + flagsMods(IS_TYPE, type.getModifiers(), REF_NONE)); + initResolved(true); + } + + // Construction from symbolic parts, for queries: + /** Create a field or type name from the given components: + * Declaring class, name, type, reference kind. + * The declaring class may be supplied as null if this is to be a bare name and type. + * The resulting name will in an unresolved state. + */ + public MemberName(Class defClass, String name, Class type, byte refKind) { + init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind)); + initResolved(false); + } + + /** Create a field or type name from the given components: Declaring class, name, type. + * The declaring class may be supplied as null if this is to be a bare name and type. + * The modifier flags default to zero. + * The resulting name will in an unresolved state. + */ + public MemberName(Class defClass, String name, Class type, Void unused) { + this(defClass, name, type, REF_NONE); + initResolved(false); + } + + /** Create a method or constructor name from the given components: Declaring class, name, type, modifiers. + * It will be a constructor if and only if the name is {@code "<init>"}. + * The declaring class may be supplied as null if this is to be a bare name and type. + * The last argument is optional, a boolean which requests REF_invokeSpecial. + * The resulting name will in an unresolved state. + */ + public MemberName(Class defClass, String name, MethodType type, byte refKind) { + int initFlags = (name != null && name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD); + init(defClass, name, type, flagsMods(initFlags, 0, refKind)); + initResolved(false); + } + + /** Create a method, constructor, or field name from the given components: + * Reference kind, declaring class, name, type. + */ + public MemberName(byte refKind, Class defClass, String name, Object type) { + int kindFlags; + if (MethodHandleNatives.refKindIsField(refKind)) { + kindFlags = IS_FIELD; + if (!(type instanceof Class)) + throw newIllegalArgumentException("not a field type"); + } else if (MethodHandleNatives.refKindIsMethod(refKind)) { + kindFlags = IS_METHOD; + if (!(type instanceof MethodType)) + throw newIllegalArgumentException("not a method type"); + } else if (refKind == REF_newInvokeSpecial) { + kindFlags = IS_CONSTRUCTOR; + if (!(type instanceof MethodType) || + !CONSTRUCTOR_NAME.equals(name)) + throw newIllegalArgumentException("not a constructor type or name"); + } else { + throw newIllegalArgumentException("bad reference kind "+refKind); + } + init(defClass, name, type, flagsMods(kindFlags, 0, refKind)); + initResolved(false); + } + + /** Create a name for the given reflected method. The resulting name will be in a resolved state. */ + public static MemberName make(Method m) { + return make(m, false); + } + + @SuppressWarnings("LeakingThisInConstructor") + private MemberName(Method m, boolean wantSpecial) { + m.getClass(); // NPE check + // fill in vmtarget, vmindex while we have m in hand: + MethodHandleNatives.init(this, m); + if (clazz == null) { // MHN.init failed + if (m.getDeclaringClass() == MethodHandle.class && + isMethodHandleInvokeName(m.getName())) { + // The JVM did not reify this signature-polymorphic instance. + // Need a special case here. + // See comments on MethodHandleNatives.linkMethod. + MethodType type = MethodType.methodType(m.getReturnType(), m.getParameterTypes()); + int flags = flagsMods(IS_METHOD, m.getModifiers(), REF_invokeVirtual); + init(MethodHandle.class, m.getName(), type, flags); + if (isMethodHandleInvoke()) { + return; + } + } + throw new LinkageError(m.toString()); + } + assert(isResolved() && this.clazz != null); + this.name = m.getName(); + if (this.type == null) { + this.type = new Object[] { m.getReturnType(), m.getParameterTypes() }; + } + if (wantSpecial) { + if (isAbstract()) { + throw new AbstractMethodError(this.toString()); + } + if (getReferenceKind() == REF_invokeVirtual) { + changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual); + } else if (getReferenceKind() == REF_invokeInterface) { + // invokeSpecial on a default method + changeReferenceKind(REF_invokeSpecial, REF_invokeInterface); + } + } + } + + public static MemberName make(Method m, boolean wantSpecial) { + // Unreflected member names are resolved so intern them here. + MemberName tmp0 = null; + InternTransaction tx = new InternTransaction(m.getDeclaringClass()); + while (tmp0 == null) { + MemberName tmp = new MemberName(m, wantSpecial); + tmp0 = tx.tryIntern(tmp); + } + return tmp0; + } + + /** + * Create a name for the given reflected constructor. The resulting name + * will be in a resolved state. + */ + @SuppressWarnings("LeakingThisInConstructor") + private MemberName(Constructor ctor) { + ctor.getClass(); // NPE check + // fill in vmtarget, vmindex while we have ctor in hand: + MethodHandleNatives.init(this, ctor); + assert(isResolved() && this.clazz != null); + this.name = CONSTRUCTOR_NAME; + if (this.type == null) { + this.type = new Object[] { void.class, ctor.getParameterTypes() }; + } + } + + public static MemberName make(Constructor ctor) { + // Unreflected member names are resolved so intern them here. + MemberName tmp0 = null; + InternTransaction tx = new InternTransaction(ctor.getDeclaringClass()); + while (tmp0 == null) { + MemberName tmp = new MemberName(ctor); + tmp0 = tx.tryIntern(tmp); + } + return tmp0; + } + + /** Create a name for the given reflected field. The resulting name will be in a resolved state. + */ + public static MemberName make(Field field) { + return make(field, false); + } + + @SuppressWarnings("LeakingThisInConstructor") + private MemberName(Field field, boolean makeSetter) { + field.getClass(); // NPE check + // fill in vmtarget, vmindex while we have field in hand: + MethodHandleNatives.init(this, field); + assert(isResolved() && this.clazz != null); + this.name = field.getName(); + this.type = field.getType(); + byte refKind = this.getReferenceKind(); + assert(refKind == (isStatic() ? REF_getStatic : REF_getField)); + if (makeSetter) { + assert((REF_putStatic - REF_getStatic) == (REF_putField - REF_getField)); + changeReferenceKind((byte)(refKind + (REF_putStatic - REF_getStatic)), refKind); + } + } + + public static MemberName make(Field field, boolean makeSetter) { + // Unreflected member names are resolved so intern them here. + MemberName tmp0 = null; + InternTransaction tx = new InternTransaction(field.getDeclaringClass()); + while (tmp0 == null) { + MemberName tmp = new MemberName(field, makeSetter); + tmp0 = tx.tryIntern(tmp); + } + return tmp0; + } + + /** Initialize a query. It is not resolved. */ + private void init(Class defClass, String name, Object type, int flags) { + // defining class is allowed to be null (for a naked name/type pair) + //name.toString(); // null check + //type.equals(type); // null check + // fill in fields: + this.clazz = defClass; + this.name = name; + this.type = type; + this.flags = flags; + assert(testAnyFlags(ALL_KINDS)); + assert(this.resolution == null); // nobody should have touched this yet + //assert(referenceKindIsConsistent()); // do this after resolution + } + + private void initResolved(boolean isResolved) { + assert(this.resolution == null); // not initialized yet! + if (!isResolved) + this.resolution = this; + assert(isResolved() == isResolved); + } + + /** + * Helper method to intern this member name in the declaring class' member name table. + */ + @SuppressWarnings("unchecked") + private MemberName intern(int transaction_token) { + return jla.internMemberName(clazz, this, transaction_token); + } + + /** + * Helper method to obtain the transaction token for interning, + * given Class clazz. + * @param clazz + * @return + */ + static private int internTxnToken(Class clazz) { + return clazz == null ? 0 : jla.getClassRedefinedCount(clazz); + } + + /** + * Helper method to obtain the transaction token for interning, given this. + * @return + */ + private int internTxnToken() { + return internTxnToken(clazz); + } + + /** + * Helper method to conditionally intern cloned+modified member names. + */ + private MemberName intern(MemberName likeThis, int transaction_token) { + if (likeThis.isResolved()) + return intern(transaction_token); + else + return this; + } + + /** + * Helper class to handle the case where the class checked for no + * redefinition may not be correct; resolution can end up with a different + * class. + */ + static class InternTransaction { + Class tx_class; + int txn_token; + + InternTransaction(Class tx_class) { + this.tx_class = tx_class; + this.txn_token = internTxnToken(tx_class); + } + + /** + * If member_name is not resolved, returns member_name; if is resolved, + * attempts to intern member_name (once) while checking for races. + * + * @param member_name + * @return member_name if not resolved, null if racing, otherwise + * value of interned member_name. + */ + MemberName tryIntern(MemberName member_name) { + if (member_name.isResolved()) { + if (member_name.getClass() != tx_class) { + Class prev_tx_class = tx_class; + int prev_txn_token = txn_token; + tx_class = member_name.getClass(); + txn_token = internTxnToken(tx_class); + // Zero is a special case. + if (txn_token != 0 || + prev_txn_token != internTxnToken(prev_tx_class)) { + // Resolved class is different and at least one + // redef of it occurred, therefore repeat with + // proper class for race consistency checking. + return null; + } + } + return member_name.intern(txn_token); + } else { + return member_name; + } + } + } + /** Return the declaring class of this member. * In the case of a bare name and type, the declaring class will be null. */ + @Override public Class getDeclaringClass() { return clazz; } @@ -95,6 +389,7 @@ * For a method or field, it is the simple name of the member. * For a constructor, it is always {@code "<init>"}. */ + @Override public String getName() { if (name == null) { expandFromVM(); @@ -240,6 +535,7 @@ /** Return the modifier flags of this member. * @see java.lang.reflect.Modifier */ + @Override public int getModifiers() { return (flags & RECOGNIZED_MODIFIERS); } @@ -417,6 +713,7 @@ return testAllFlags(VARARGS) && isInvocable(); } /** Utility method to query the modifier flags of this member; returns false if the member is not a method. */ + @Override public boolean isSynthetic() { return testAllFlags(SYNTHETIC); } @@ -479,21 +776,6 @@ lookupClass, ALL_ACCESS|MethodHandles.Lookup.PACKAGE); } - /** Initialize a query. It is not resolved. */ - private void init(Class defClass, String name, Object type, int flags) { - // defining class is allowed to be null (for a naked name/type pair) - //name.toString(); // null check - //type.equals(type); // null check - // fill in fields: - this.clazz = defClass; - this.name = name; - this.type = type; - this.flags = flags; - assert(testAnyFlags(ALL_KINDS)); - assert(this.resolution == null); // nobody should have touched this yet - //assert(referenceKindIsConsistent()); // do this after resolution - } - /** * Calls down to the VM to fill in the fields. This method is * synchronized to avoid racing calls. @@ -515,49 +797,15 @@ assert((refKind & ~MN_REFERENCE_KIND_MASK) == 0); return flags | mods | (refKind << MN_REFERENCE_KIND_SHIFT); } - /** Create a name for the given reflected method. The resulting name will be in a resolved state. */ - public MemberName(Method m) { - this(m, false); - } - @SuppressWarnings("LeakingThisInConstructor") - public MemberName(Method m, boolean wantSpecial) { - m.getClass(); // NPE check - // fill in vmtarget, vmindex while we have m in hand: - MethodHandleNatives.init(this, m); - if (clazz == null) { // MHN.init failed - if (m.getDeclaringClass() == MethodHandle.class && - isMethodHandleInvokeName(m.getName())) { - // The JVM did not reify this signature-polymorphic instance. - // Need a special case here. - // See comments on MethodHandleNatives.linkMethod. - MethodType type = MethodType.methodType(m.getReturnType(), m.getParameterTypes()); - int flags = flagsMods(IS_METHOD, m.getModifiers(), REF_invokeVirtual); - init(MethodHandle.class, m.getName(), type, flags); - if (isMethodHandleInvoke()) - return; - } - throw new LinkageError(m.toString()); - } - assert(isResolved() && this.clazz != null); - this.name = m.getName(); - if (this.type == null) - this.type = new Object[] { m.getReturnType(), m.getParameterTypes() }; - if (wantSpecial) { - if (isAbstract()) - throw new AbstractMethodError(this.toString()); - if (getReferenceKind() == REF_invokeVirtual) - changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual); - else if (getReferenceKind() == REF_invokeInterface) - // invokeSpecial on a default method - changeReferenceKind(REF_invokeSpecial, REF_invokeInterface); - } - } + + + public MemberName asSpecial() { switch (getReferenceKind()) { case REF_invokeSpecial: return this; - case REF_invokeVirtual: return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual); - case REF_invokeInterface: return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeInterface); - case REF_newInvokeSpecial: return clone().changeReferenceKind(REF_invokeSpecial, REF_newInvokeSpecial); + case REF_invokeVirtual: return cloneAndChangeReferenceKindAndIntern(REF_invokeSpecial, REF_invokeVirtual); + case REF_invokeInterface: return cloneAndChangeReferenceKindAndIntern(REF_invokeSpecial, REF_invokeInterface); + case REF_newInvokeSpecial: return cloneAndChangeReferenceKindAndIntern(REF_invokeSpecial, REF_newInvokeSpecial); } throw new IllegalArgumentException(this.toString()); } @@ -566,7 +814,7 @@ */ public MemberName asConstructor() { switch (getReferenceKind()) { - case REF_invokeSpecial: return clone().changeReferenceKind(REF_newInvokeSpecial, REF_invokeSpecial); + case REF_invokeSpecial: return cloneAndChangeReferenceKindAndIntern(REF_newInvokeSpecial, REF_invokeSpecial); case REF_newInvokeSpecial: return this; } throw new IllegalArgumentException(this.toString()); @@ -593,41 +841,20 @@ } if (newRefKind == refKind) return this; - result = clone().changeReferenceKind(newRefKind, refKind); + result = cloneAndChangeReferenceKindAndIntern(newRefKind, refKind); assert(this.referenceKindIsConsistentWith(result.getReferenceKind())); return result; } - /** Create a name for the given reflected constructor. The resulting name will be in a resolved state. */ - @SuppressWarnings("LeakingThisInConstructor") - public MemberName(Constructor ctor) { - ctor.getClass(); // NPE check - // fill in vmtarget, vmindex while we have ctor in hand: - MethodHandleNatives.init(this, ctor); - assert(isResolved() && this.clazz != null); - this.name = CONSTRUCTOR_NAME; - if (this.type == null) - this.type = new Object[] { void.class, ctor.getParameterTypes() }; - } - /** Create a name for the given reflected field. The resulting name will be in a resolved state. - */ - public MemberName(Field fld) { - this(fld, false); - } - @SuppressWarnings("LeakingThisInConstructor") - public MemberName(Field fld, boolean makeSetter) { - fld.getClass(); // NPE check - // fill in vmtarget, vmindex while we have fld in hand: - MethodHandleNatives.init(this, fld); - assert(isResolved() && this.clazz != null); - this.name = fld.getName(); - this.type = fld.getType(); - assert((REF_putStatic - REF_getStatic) == (REF_putField - REF_getField)); - byte refKind = this.getReferenceKind(); - assert(refKind == (isStatic() ? REF_getStatic : REF_getField)); - if (makeSetter) { - changeReferenceKind((byte)(refKind + (REF_putStatic - REF_getStatic)), refKind); + + private MemberName cloneAndChangeReferenceKindAndIntern(byte refKind, byte oldKind) { + MemberName tmp0 = null; + while (tmp0 == null) { + int tx_token = internTxnToken(); + tmp0 = clone().changeReferenceKind(refKind, oldKind).intern(this, tx_token); } + return tmp0; } + public boolean isGetter() { return MethodHandleNatives.refKindIsGetter(getReferenceKind()); } @@ -639,15 +866,8 @@ assert(MethodHandleNatives.refKindIsGetter(refKind)); assert((REF_putStatic - REF_getStatic) == (REF_putField - REF_getField)); byte setterRefKind = (byte)(refKind + (REF_putField - REF_getField)); - return clone().changeReferenceKind(setterRefKind, refKind); - } - /** Create a name for the given class. The resulting name will be in a resolved state. */ - public MemberName(Class type) { - init(type.getDeclaringClass(), type.getSimpleName(), type, - flagsMods(IS_TYPE, type.getModifiers(), REF_NONE)); - initResolved(true); + return cloneAndChangeReferenceKindAndIntern(setterRefKind, refKind); } - /** * Create a name for a signature-polymorphic invoker. * This is a placeholder for a signature-polymorphic instance @@ -664,9 +884,6 @@ return mem; } - // bare-bones constructor; the JVM will fill it in - MemberName() { } - // locally useful cloner @Override protected MemberName clone() { try { @@ -715,52 +932,67 @@ && Objects.equals(this.getType(), that.getType()); } - // Construction from symbolic parts, for queries: - /** Create a field or type name from the given components: - * Declaring class, name, type, reference kind. - * The declaring class may be supplied as null if this is to be a bare name and type. - * The resulting name will in an unresolved state. - */ - public MemberName(Class defClass, String name, Class type, byte refKind) { - init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind)); - initResolved(false); - } - /** Create a method or constructor name from the given components: - * Declaring class, name, type, reference kind. - * It will be a constructor if and only if the name is {@code "<init>"}. - * The declaring class may be supplied as null if this is to be a bare name and type. - * The last argument is optional, a boolean which requests REF_invokeSpecial. - * The resulting name will in an unresolved state. - */ - public MemberName(Class defClass, String name, MethodType type, byte refKind) { - int initFlags = (name != null && name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD); - init(defClass, name, type, flagsMods(initFlags, 0, refKind)); - initResolved(false); - } - /** Create a method, constructor, or field name from the given components: - * Reference kind, declaring class, name, type. - */ - public MemberName(byte refKind, Class defClass, String name, Object type) { - int kindFlags; - if (MethodHandleNatives.refKindIsField(refKind)) { - kindFlags = IS_FIELD; - if (!(type instanceof Class)) - throw newIllegalArgumentException("not a field type"); - } else if (MethodHandleNatives.refKindIsMethod(refKind)) { - kindFlags = IS_METHOD; - if (!(type instanceof MethodType)) - throw newIllegalArgumentException("not a method type"); - } else if (refKind == REF_newInvokeSpecial) { - kindFlags = IS_CONSTRUCTOR; - if (!(type instanceof MethodType) || - !CONSTRUCTOR_NAME.equals(name)) - throw newIllegalArgumentException("not a constructor type or name"); - } else { - throw newIllegalArgumentException("bad reference kind "+refKind); + @Override + public int compareTo(Object o) { + MemberName that = (MemberName) o; + + /* First test equals. This make the ordering checks easier because we + * don't have to be precise and can use hash codes. + */ + if (equals(that)) { + return 0; } - init(defClass, name, type, flagsMods(kindFlags, 0, refKind)); - initResolved(false); + + int diff = Integer.compare(this.name.hashCode(), that.name.hashCode()); + if (diff != 0) { + return diff; + } + + diff = this.getReferenceKind() - that.getReferenceKind(); + if (diff != 0) { + return diff; + } + + diff = Integer.compare(this.getType().hashCode(), that.getType().hashCode()); + if (diff != 0) { + return diff; + } + + // Hashcodes apparently collided, try more detail. + diff = this.name.compareTo(that.name); + if (diff != 0) { + return diff; + } + + // The classes ought to all be equal anyway in the usual use of + // compareTo, so check these later, + // but before comparing getType.toString(). + diff = Integer.compare(this.clazz.hashCode(),that.clazz.hashCode()); + if (diff != 0) { + return diff; + } + + diff = this.getType().toString().compareTo(that.getType().toString()); + if (diff != 0) { + return diff; + } + + diff = this.clazz.getName().compareTo(that.clazz.getName()); + if (diff != 0) { + return diff; + } + + ClassLoader thisCl = this.clazz.getClassLoader(); + ClassLoader thatCl = that.clazz.getClassLoader(); + + if (thisCl == thatCl) return 0; + + diff = Integer.compare(thisCl == null ? 0 : thisCl.hashCode(), + thatCl == null ? 0 : thatCl.hashCode()); + + return diff; } + /** Query whether this member name is resolved to a non-static, non-final method. */ public boolean hasReceiverTypeDispatch() { @@ -776,13 +1008,6 @@ return resolution == null; } - private void initResolved(boolean isResolved) { - assert(this.resolution == null); // not initialized yet! - if (!isResolved) - this.resolution = this; - assert(isResolved() == isResolved); - } - void checkForTypeAlias() { if (isInvocable()) { MethodType type; @@ -889,7 +1114,7 @@ private Factory() { } // singleton pattern static Factory INSTANCE = new Factory(); - private static int ALLOWED_FLAGS = ALL_KINDS; + private final static int ALLOWED_FLAGS = ALL_KINDS; /// Queries List getMembers(Class defc, @@ -955,7 +1180,7 @@ * If lookup fails or access is not permitted, null is returned. * Otherwise a fresh copy of the given member is returned, with modifier bits filled in. */ - private MemberName resolve(byte refKind, MemberName ref, Class lookupClass) { + private static MemberName resolve(byte refKind, MemberName ref, Class lookupClass) { MemberName m = ref.clone(); // JVM will side-effect the ref assert(refKind == m.getReferenceKind()); try { @@ -971,6 +1196,7 @@ assert(m.referenceKindIsConsistent()); m.initResolved(true); assert(m.vminfoIsConsistent()); + // m = m.intern(); // do not intern -- caller should intern instead. return m; } /** Produce a resolved version of the given member. @@ -981,15 +1207,20 @@ */ public - MemberName resolveOrFail(byte refKind, MemberName m, Class lookupClass, - Class nsmClass) + MemberName resolveOrFail(byte refKind, MemberName m, Class lookupClass, + Class nsmClass) throws IllegalAccessException, NoSuchMemberException { - MemberName result = resolve(refKind, m, lookupClass); - if (result.isResolved()) + MemberName result = resolveOrNot(refKind, m, lookupClass); + + if (result.isResolved()) { return result; - ReflectiveOperationException ex = result.makeAccessException(); - if (ex instanceof IllegalAccessException) throw (IllegalAccessException) ex; - throw nsmClass.cast(ex); + } else { // not resolved, throw exception + ReflectiveOperationException ex = result.makeAccessException(); + if (ex instanceof IllegalAccessException) { + throw (IllegalAccessException) ex; + } + throw nsmClass.cast(ex); + } } /** Produce a resolved version of the given member. * Super types are searched (for inherited members) if {@code searchSupers} is true. @@ -999,11 +1230,42 @@ */ public MemberName resolveOrNull(byte refKind, MemberName m, Class lookupClass) { - MemberName result = resolve(refKind, m, lookupClass); - if (result.isResolved()) - return result; - return null; + MemberName result = resolveOrNot(refKind, m, lookupClass); + return result.isResolved() ? result : null; + } + + private static + MemberName resolveOrNot(byte refKind, MemberName m, Class lookupClass) { + MemberName result = null; + Class tx_class = m.getClass(); + while (result == null) { // iterate till no race. + int txn_token = internTxnToken(tx_class); + + MemberName result0 = resolve(refKind, m, lookupClass); + + if (result0.isResolved()) { + if (result0.getClass() != tx_class) { + Class prev_tx_class = tx_class; + int prev_txn_token = txn_token; + tx_class = result0.getClass(); + txn_token = internTxnToken(tx_class); + // Zero is a special case. + if (txn_token != 0 || + prev_txn_token != internTxnToken(prev_tx_class)) { + // Resolved class is different and at least one + // redef of it occurred, therefore repeat with + // proper class for race consistency checking. + continue; + } + } + result = result0.intern(txn_token); + } else { + return result0; + } + } + return result; } + /** Return a list of all methods defined by the given class. * Super types are searched (for inherited members) if {@code searchSupers} is true. * Access checking is performed on behalf of the given {@code lookupClass}. @@ -1069,9 +1331,4 @@ return buf; } } - -// static { -// System.out.println("Hello world! My methods are:"); -// System.out.println(Factory.INSTANCE.getMethods(MemberName.class, true, null)); -// } } --- old/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2014-10-30 16:11:47.000000000 -0400 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2014-10-30 16:11:47.000000000 -0400 @@ -1178,7 +1178,7 @@ MethodHandle mh = unreflectForMH(m); if (mh != null) return mh; } - MemberName method = new MemberName(m); + MemberName method = MemberName.make(m); byte refKind = method.getReferenceKind(); if (refKind == REF_invokeSpecial) refKind = REF_invokeVirtual; @@ -1189,7 +1189,7 @@ private MethodHandle unreflectForMH(Method m) { // these names require special lookups because they throw UnsupportedOperationException if (MemberName.isMethodHandleInvokeName(m.getName())) - return MethodHandleImpl.fakeMethodHandleInvoke(new MemberName(m)); + return MethodHandleImpl.fakeMethodHandleInvoke(MemberName.make(m)); return null; } @@ -1225,7 +1225,7 @@ public MethodHandle unreflectSpecial(Method m, Class specialCaller) throws IllegalAccessException { checkSpecialCaller(specialCaller); Lookup specialLookup = this.in(specialCaller); - MemberName method = new MemberName(m, true); + MemberName method = MemberName.make(m, true); assert(method.isMethod()); // ignore m.isAccessible: this is a new kind of access return specialLookup.getDirectMethodNoSecurityManager(REF_invokeSpecial, method.getDeclaringClass(), method, findBoundCallerClass(method)); @@ -1256,7 +1256,7 @@ * @throws NullPointerException if the argument is null */ public MethodHandle unreflectConstructor(Constructor c) throws IllegalAccessException { - MemberName ctor = new MemberName(c); + MemberName ctor = MemberName.make(c); assert(ctor.isConstructor()); Lookup lookup = c.isAccessible() ? IMPL_LOOKUP : this; return lookup.getDirectConstructorNoSecurityManager(ctor.getDeclaringClass(), ctor); @@ -1284,7 +1284,7 @@ return unreflectField(f, false); } private MethodHandle unreflectField(Field f, boolean isSetter) throws IllegalAccessException { - MemberName field = new MemberName(f, isSetter); + MemberName field = MemberName.make(f, isSetter); assert(isSetter ? MethodHandleNatives.refKindIsSetter(field.getReferenceKind()) : MethodHandleNatives.refKindIsGetter(field.getReferenceKind())); --- old/src/java.base/share/classes/sun/misc/JavaLangAccess.java 2014-10-30 16:11:48.000000000 -0400 +++ new/src/java.base/share/classes/sun/misc/JavaLangAccess.java 2014-10-30 16:11:48.000000000 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -142,4 +142,22 @@ * Invokes Integer.formatUnsignedInt(long val, int shift, char[] buf, int offset, int len) */ void formatUnsignedInt(int val, int shift, char[] buf, int offset, int len); + + /** + * Intern memberName in klass's memberNameTable, guarding against concurrent + * redefinition of klass. + * + * @param + * @param klass + * @param memberName + * @param redefined_count + * @return interned member name or null if a race is lost. + */ + > E internMemberName(Class klass, E memberName, int redefined_count); + + /** + * @param klass Class whose redefinition count is queried. + * @return redefinition count for klass + */ + int getClassRedefinedCount(Class klass); }