RFR: JDK-8225056 VM support for sealed classes

Harold Seigel harold.seigel at oracle.com
Mon Jun 1 13:07:04 UTC 2020


Hi David,

Thanks for reviewing the latest changes.

I'll create the follow on RFE's once the sealed classes code is in mainline.

Harold

On 5/31/2020 9:34 PM, David Holmes wrote:
> Hi Harold,
>
> On 1/06/2020 8:57 am, Harold Seigel wrote:
>> Thanks for the comments.
>>
>> Here's version 3 of the JDK and VM changes for sealed classes.
>>
>> full webrev: 
>> http://cr.openjdk.java.net/~hseigel/sealedClasses.8225056.3/webrev/
>>
>> The new webrev contains just the following three changes:
>>
>>  1. The sealed classes API's in Class.java (permittedSubclasses() and
>>     isSealed()) were revised and, in particular, API
>>     permittedSubclasses() no longer uses reflection.
>
> For those following along we have presently abandoned the attempt to 
> cache the array in ReflectionData.
>
> Current changes look okay. But I note from the CSR there appears to be 
> a further minor update to the javadoc coming.
>
>>  2. An unneeded 'if' statement was removed from
>>     JVM_GetPermittedSubclasses() (pointed out by David.)
>
> Looks good.
>
>>  3. VM runtime test files SealedUnnamedModuleIntfTest.java and
>>     Permitted.java were changed to add a test case for a non-public
>>     permitted subclass and its sealed superclass being in the same
>>     module and package.
>
> Looks good.
>
>> Additionally, two follow on RFE's will be filed.  One to add 
>> additional VM sealed classes tests 
>
> Thanks. I think there is a more mechanical approach to testing here 
> that will allow the complete matrix to be easily covered with minimal 
> duplication between testing for named and unnamed modules.
>
>> and one to improve the implementations of the sealed classes API's in 
>> Class.java.
>
> Thanks.
>
> David
> -----
>
>> Thanks, Harold
>>
>> On 5/28/2020 8:30 PM, David Holmes wrote:
>>>
>>> Hi Harold,
>>>
>>> Sorry Mandy's comment raised a couple of issues ...
>>>
>>> On 29/05/2020 7:12 am, Mandy Chung wrote:
>>>> Hi Harold,
>>>>
>>>> On 5/27/20 1:35 PM, Harold Seigel wrote:
>>>>>
>>>>> Incremental webrev: 
>>>>> http://cr.openjdk.java.net/~hseigel/sealedClasses.8225056.incr.2/
>>>>>
>>>>> full webrev: 
>>>>> http://cr.openjdk.java.net/~hseigel/sealedClasses.8225056.2/webrev/
>>>>>
>>>> Class.java
>>>>
>>>> 4406 ReflectionData<T> rd = reflectionData();
>>>> 4407 ClassDesc[] tmp = rd.permittedSubclasses;
>>>> 4408 if (tmp != null) {
>>>> 4409 return tmp;
>>>> 4410 }
>>>> 4411
>>>> 4412 if (isArray() || isPrimitive()) {
>>>> 4413 rd.permittedSubclasses = new ClassDesc[0];
>>>> 4414 return rd.permittedSubclasses;
>>>> 4415 }
>>>>
>>>> This causes an array class or primitive type to create a 
>>>> ReflectionData.   It should first check if this is non-sealed class 
>>>> and returns a constant empty array.
>>>
>>> It can't check if this is a non-sealed class as the isSealed() check 
>>> calls the above code! But for arrays and primitives which can't be 
>>> sealed we should just do:
>>>
>>> 4412 if (isArray() || isPrimitive()) {
>>> 4413 return new ClassDesc[0];
>>> 4414 }
>>>
>>> But this then made me realize that we need to be creating defensive 
>>> copies of the returned arrays, as happens with other APIs that use 
>>> ReflectionData.
>>>
>>> Backing up a bit I complained that:
>>>
>>> public boolean isSealed() {
>>> return permittedSubclasses().length != 0;
>>> }
>>>
>>> is a very inefficient way to answer the question as to whether a 
>>> class is sealed, so I suggested that the result of 
>>> permittedSubclasses() be cached. Caching is not without its own 
>>> issues as we are discovering, and when you add in defensive copies 
>>> this seems to be trading one inefficiency for another. For nestmates 
>>> we don't cache getNestMembers() because we don;t think it will be 
>>> called often - it is there to complete the introspection API of 
>>> Class rather than being anticipated as used in a regular 
>>> programmatic sense. I expect the same is true for 
>>> permittedSubclasses(). Do we expect isSealed() to be used often or 
>>> is it too just there for completeness? If just for completeness then 
>>> perhaps a VM query would be a better compromise on the efficiency 
>>> front? Otherwise I can accept the current implementation of 
>>> isSealed(), and a non-caching permittedClasses() for this initial 
>>> implementation of sealed classes. If efficiency turns out to be a 
>>> problem for isSealed() then we can revisit it then.
>>>
>>> Thanks,
>>> David
>>>
>>>
>>>> In fact, ReflectionData caches the derived names and reflected 
>>>> members for performance and also they may be invalidated when the 
>>>> class is redefined.   It might be okay to add 
>>>> ReflectionData::permittedSubclasses while `PermittedSubclasses` 
>>>> attribute can't be redefined and getting this attribute is not 
>>>> performance sensitive.   For example, the result of 
>>>> `getNestMembers` is not cached in ReflectionData.  It may be better 
>>>> not to add it in ReflectionData for modifiable and 
>>>> performance-sensitive data.
>>>>
>>>>
>>>> 4421 tmp = new ClassDesc[subclassNames.length];
>>>> 4422 int i = 0;
>>>> 4423 for (String subclassName : subclassNames) {
>>>> 4424 try {
>>>> 4425 tmp[i++] = ClassDesc.of(subclassName.replace('/', '.'));
>>>> 4426 } catch (IllegalArgumentException iae) {
>>>> 4427 throw new InternalError("Invalid type in permitted subclasses 
>>>> information: " + subclassName, iae);
>>>> 4428 }
>>>> 4429 }
>>>> Nit: rename tmp to some other name e.g. descs
>>>>
>>>> I read the JVMS but it isn't clear to me that the VM will validate 
>>>> the names in `PermittedSubclasses`attribute are valid class 
>>>> descriptors.   I see ConstantPool::is_klass_or_reference check but 
>>>> can't find where it validates the name is a valid class descriptor 
>>>> - can you point me there?   (otherwise, maybe define it to be 
>>>> unspecified?)
>>>>
>>>>
>>>> W.r.t. the APIs. I don't want to delay this review.  I see that you 
>>>> renamed the method to new API style as I brought up.  OTOH,  I 
>>>> expect more discussion is needed.  Below is a recent comment from 
>>>> John on this topic [1]
>>>>
>>>>> One comment, really for the future, regarding the shape of the 
>>>>> Java API here: It uses Optional and omits the "get" prefix on 
>>>>> accessors. This is the New Style, as opposed to the Classic Style 
>>>>> using null (for "empty" results) and a "get" prefix 
>>>>> ("getComponentType") to get a related type. We may choose to to 
>>>>> use the New Style for new reflection API points, and if so let's 
>>>>> not choose N different New Styles, but one New Style. Personally I 
>>>>> like removing "get"; I accept Optional instead of null; and I also 
>>>>> suggest that arrays (if any) be replaced by (immutable) Lists in 
>>>>> the New Style
>>>>
>>>> There are a few existing Class APIs that use the new API style:
>>>> Class<?> arrayClass();
>>>> Optional<ClassDesc> describeConstable();
>>>> String descriptorString();
>>>>
>>>> This will set up a precedence of the new API style in this class.  
>>>> Should this new permittedSubclasses method return a List instead of 
>>>> an array?  It's okay with me if you prefer to revert back to the 
>>>> old API style and follow this up after integration.
>>>>
>>>> 4442 * Returns true if this {@linkplain Class} is sealed.
>>>> 4443 *
>>>> 4444 * @return returns true if this class is sealed
>>>>
>>>> NIt: {@code true} instead of true -  consistent with the style this 
>>>> class uses (in most methods)
>>>>
>>>> test/jdk/java/lang/reflect/sealed_classes/SealedClassesReflectionTest.java 
>>>>
>>>>
>>>> Nit: s/sealed_classes/sealedClasses/
>>>> - the test directory/file naming convention use camel case or java 
>>>> variable name convention.
>>>>
>>>> Thanks
>>>> [1] https://github.com/openjdk/valhalla/pull/53#issuecomment-633116043


More information about the serviceability-dev mailing list