Skip to content

Commit 2b9c4fc

Browse files
committed
Inject IdentityInterface to "heavy" abstract classes when necessary
1. Detect "heavy" abstract classes (i.e. abstract classes that have non-static fields, non-static synchronized methods or non-empty constructor). Ensure all such classes implement IdentityInterface. Inject IdentityInterface when necessary. 2. Superclass of VT must not implement IdentityInterface. Throw IncompatibleClassChangeError if this is violated. 3. Add test cases. 4. Current JCL code enforces annotation type to have java.lang.annotation.Annotation.class as its only super interface. This is not true anymore. This is causing PackageAnnotation tests to fail. Exclude them in x86-64_linux_vt_standard build for now. Fixes #12039 Signed-off-by: Hang Shao <hangshao@ca.ibm.com>
1 parent ca98b1d commit 2b9c4fc

16 files changed

+390
-29
lines changed

runtime/bcutil/BuildResult.hpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2001, 2020 IBM Corp. and others
2+
* Copyright (c) 2001, 2021 IBM Corp. and others
33
*
44
* This program and the accompanying materials are made available under
55
* the terms of the Eclipse Public License 2.0 which accompanies this
@@ -49,6 +49,9 @@ enum BuildResult {
4949
LineNumberTableDecompressFailed = BCT_ERR_LINE_NUMBER_TABLE_DECOMPRESS_FAILED,
5050
InvalidBytecodeSize = BCT_ERR_INVALID_BYTECODE_SIZE,
5151
InvalidClassType = BCT_ERR_INVALID_CLASS_TYPE,
52+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
53+
InvalidValueType = BCT_ERR_INVALID_VALUE_TYPE,
54+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
5255
};
5356

5457
#endif /* BUILDRESULT_HPP_ */

runtime/bcutil/ClassFileOracle.cpp

+113-8
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,12 @@ ClassFileOracle::ClassFileOracle(BufferManager *bufferManager, J9CfrClassFile *c
192192
_needsStaticConstantInit(false),
193193
_isRecord(false),
194194
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
195+
_hasIdentityInterface(false),
195196
_isIdentityInterfaceNeeded(false),
196197
_isValueType(false),
198+
_hasNonStaticSynchronizedMethod(false),
199+
_hasNonStaticFields(false),
200+
_hasNonEmptyConstructor(false),
197201
#endif /* J9VM_OPT_VALHALLA_VALUE_TYPES */
198202
_recordComponentCount(0),
199203
_permittedSubclassesAttribute(NULL),
@@ -226,6 +230,17 @@ ClassFileOracle::ClassFileOracle(BufferManager *bufferManager, J9CfrClassFile *c
226230
_buildResult = _constantPoolMap->getBuildResult();
227231
return;
228232
}
233+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
234+
if (J9_ARE_ALL_BITS_SET(_classFile->accessFlags, CFR_ACC_VALUE_TYPE)
235+
|| J9_ARE_NO_BITS_SET(_classFile->accessFlags, CFR_ACC_ABSTRACT | CFR_ACC_INTERFACE)
236+
) {
237+
/**
238+
* We care about whether there is a non-empty constructor only for non-value type abstract classes.
239+
* Simply set _hasNonEmptyConstructor to true for value types, or concrete classes.
240+
*/
241+
_hasNonEmptyConstructor = true;
242+
}
243+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
229244

230245
/* analyze class file */
231246

@@ -252,7 +267,12 @@ ClassFileOracle::ClassFileOracle(BufferManager *bufferManager, J9CfrClassFile *c
252267
if (OK == _buildResult) {
253268
walkFields();
254269
}
255-
270+
271+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
272+
if (OK == _buildResult) {
273+
checkAndRecordIsIdentityInterfaceNeeded();
274+
}
275+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
256276
if (OK == _buildResult) {
257277
_constantPoolMap->computeConstantPoolMapAndSizes();
258278
if (!constantPoolMap->isOK()) {
@@ -338,6 +358,9 @@ ClassFileOracle::walkFields()
338358
*/
339359
_hasFinalFields = true;
340360
}
361+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
362+
_hasNonStaticFields = true;
363+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
341364
}
342365

343366
for (U_16 attributeIndex = 0; (OK == _buildResult) && (attributeIndex < field->attributesCount); ++attributeIndex) {
@@ -732,16 +755,45 @@ ClassFileOracle::walkInterfaces()
732755
if (J9_ARE_ALL_BITS_SET(_classFile->accessFlags, CFR_ACC_VALUE_TYPE)) {
733756
_isValueType = true;
734757
}
735-
if (!isValueType()
736-
&& !interfaceVisitor.wasIdentityInterfaceSeen()
737-
&& (getSuperClassNameIndex() != 0) /* j.l.Object has no superClass */
738-
&& (J9_ARE_NO_BITS_SET(_classFile->accessFlags, CFR_ACC_ABSTRACT | CFR_ACC_INTERFACE))
739-
) {
740-
_isIdentityInterfaceNeeded = true;
741-
}
758+
_hasIdentityInterface = interfaceVisitor.wasIdentityInterfaceSeen();
742759
#endif /* J9VM_OPT_VALHALLA_VALUE_TYPES */
743760
}
744761

762+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
763+
void
764+
ClassFileOracle::checkAndRecordIsIdentityInterfaceNeeded()
765+
{
766+
if (isValueType()) {
767+
if (_hasIdentityInterface
768+
|| J9_ARE_ANY_BITS_SET(_classFile->accessFlags, CFR_ACC_ABSTRACT | CFR_ACC_INTERFACE)
769+
|| _hasNonStaticSynchronizedMethod
770+
) {
771+
_buildResult = InvalidValueType;
772+
}
773+
} else {
774+
if (!_hasIdentityInterface
775+
&& (getSuperClassNameIndex() != 0) /* j.l.Object has no superClass */
776+
) {
777+
if (J9_ARE_NO_BITS_SET(_classFile->accessFlags, CFR_ACC_ABSTRACT | CFR_ACC_INTERFACE)) {
778+
/* For concrete classes, IdentityInterface is needed. */
779+
_isIdentityInterfaceNeeded = true;
780+
} else {
781+
/**
782+
* For abstract classes, IdentityInterface is needed only when it has non-static fields,
783+
* non-static synchronized method or non-empty constructor.
784+
*/
785+
if ((_hasNonStaticFields)
786+
|| (_hasNonStaticSynchronizedMethod)
787+
|| (_hasNonEmptyConstructor)
788+
) {
789+
_isIdentityInterfaceNeeded = true;
790+
}
791+
}
792+
}
793+
}
794+
}
795+
#endif /* J9VM_OPT_VALHALLA_VALUE_TYPES */
796+
745797
void
746798
ClassFileOracle::walkMethods()
747799
{
@@ -777,6 +829,17 @@ ClassFileOracle::walkMethods()
777829
if (methodIsObjectConstructor(methodIndex)) {
778830
_methodsInfo[methodIndex].modifiers |= J9AccMethodObjectConstructor;
779831
}
832+
833+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
834+
if (!_hasNonEmptyConstructor) {
835+
if (methodIsConstructor(methodIndex)) {
836+
/* Do not record constructor forwarded to its superclass. */
837+
if (J9_ARE_NO_BITS_SET(_methodsInfo[methodIndex].modifiers, J9AccForwarderMethod)) {
838+
_hasNonEmptyConstructor = true;
839+
}
840+
}
841+
}
842+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
780843

781844
/* does the method belong in vtables? */
782845
if (methodIsVirtual(methodIndex)) {
@@ -796,6 +859,11 @@ ClassFileOracle::walkMethods()
796859
_hasEmptyFinalizeMethod = true;
797860
}
798861
}
862+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
863+
if (!_hasNonStaticSynchronizedMethod) {
864+
_hasNonStaticSynchronizedMethod = methodIsNonStaticSynchronized(methodIndex);
865+
}
866+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
799867

800868
computeSendSlotCount(methodIndex);
801869

@@ -2175,6 +2243,29 @@ ClassFileOracle::methodIsObjectConstructor(U_16 methodIndex)
21752243
return false;
21762244
}
21772245

2246+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
2247+
/**
2248+
* Determine if a method is constructor of a non-Value Type (Value Type constructors are static).
2249+
*
2250+
* @param methodIndex[in] the method index
2251+
*
2252+
* @returns true if the method is a constructor of a non-Value Type, false if not.
2253+
*/
2254+
bool
2255+
ClassFileOracle::methodIsConstructor(U_16 methodIndex)
2256+
{
2257+
ROMCLASS_VERBOSE_PHASE_HOT(_context, MethodIsConstructor);
2258+
2259+
if (J9_ARE_NO_BITS_SET(_classFile->methods[methodIndex].accessFlags, (CFR_ACC_ABSTRACT | CFR_ACC_STATIC | CFR_ACC_SYNCHRONIZED))
2260+
&& ('<' == getUTF8Data(_classFile->methods[methodIndex].nameIndex)[0])
2261+
) {
2262+
return true;
2263+
}
2264+
2265+
return false;
2266+
}
2267+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
2268+
21782269
/**
21792270
* Determine if a method is <clinit>.
21802271
*
@@ -2419,6 +2510,20 @@ ClassFileOracle::methodIsNonStaticNonAbstract(U_16 methodIndex)
24192510
return J9_ARE_NO_BITS_SET(_classFile->methods[methodIndex].accessFlags, (CFR_ACC_STATIC | CFR_ACC_ABSTRACT));
24202511
}
24212512

2513+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
2514+
bool
2515+
ClassFileOracle::methodIsNonStaticSynchronized(U_16 methodIndex)
2516+
{
2517+
ROMCLASS_VERBOSE_PHASE_HOT(_context, MethodIsNonStaticSynchronized);
2518+
if (J9_ARE_NO_BITS_SET(_classFile->methods[methodIndex].accessFlags, CFR_ACC_STATIC)
2519+
&& J9_ARE_ALL_BITS_SET(_classFile->methods[methodIndex].accessFlags, CFR_ACC_SYNCHRONIZED)
2520+
) {
2521+
return true;
2522+
}
2523+
return false;
2524+
}
2525+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
2526+
24222527
/**
24232528
* Method to determine if an invokevirtual instruction should be re-written to be an
24242529
* invokehandle or invokehandlegeneric bytecode. Modifications as follows:

runtime/bcutil/ClassFileOracle.hpp

+12
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,7 @@ class RecordComponentIterator
992992
bool isValueBased() const { return _isClassValueBased; }
993993
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
994994
bool needsIdentityInterface() const { return _isIdentityInterfaceNeeded; }
995+
bool hasIdentityInterface() const { return _hasIdentityInterface; }
995996
bool isValueType() const { return _isValueType; }
996997
#endif /* J9VM_OPT_VALHALLA_VALUE_TYPES */
997998

@@ -1085,8 +1086,12 @@ class RecordComponentIterator
10851086
bool _isSealed;
10861087
bool _isClassValueBased;
10871088
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
1089+
bool _hasIdentityInterface;
10881090
bool _isIdentityInterfaceNeeded;
10891091
bool _isValueType;
1092+
bool _hasNonStaticSynchronizedMethod;
1093+
bool _hasNonStaticFields;
1094+
bool _hasNonEmptyConstructor;
10901095
#endif /* J9VM_OPT_VALHALLA_VALUE_TYPES */
10911096

10921097
FieldInfo *_fieldsInfo;
@@ -1111,6 +1116,9 @@ class RecordComponentIterator
11111116
void walkAttributes();
11121117
void checkHiddenClass();
11131118
void walkInterfaces();
1119+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
1120+
void checkAndRecordIsIdentityInterfaceNeeded();
1121+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
11141122
void walkMethods();
11151123
void walkRecordComponents(J9CfrAttributeRecord *attrib);
11161124

@@ -1139,6 +1147,10 @@ class RecordComponentIterator
11391147
bool methodIsObjectConstructor(U_16 methodIndex);
11401148
bool methodIsClinit(U_16 methodIndex);
11411149
bool methodIsNonStaticNonAbstract(U_16 methodIndex);
1150+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
1151+
bool methodIsConstructor(U_16 methodIndex);
1152+
bool methodIsNonStaticSynchronized(U_16 methodIndex);
1153+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
11421154

11431155
bool shouldConvertInvokeVirtualToInvokeSpecialForMethodRef(U_16 methodRefCPIndex);
11441156
UDATA shouldConvertInvokeVirtualToMethodHandleBytecodeForMethodRef(U_16 methodRefCPIndex);

runtime/bcutil/ROMClassBuilder.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -1150,7 +1150,7 @@ ROMClassBuilder::finishPrepareAndLaydown(
11501150
* + UNUSED
11511151
*
11521152
* + UNUSED
1153-
* + UNUSED
1153+
* + J9AccClassHasIdentity
11541154
* + J9AccClassIsValueBased
11551155
* + J9AccClassHiddenOptionNestmate
11561156
*
@@ -1238,6 +1238,14 @@ ROMClassBuilder::computeExtraModifiers(ClassFileOracle *classFileOracle, ROMClas
12381238
modifiers |= J9AccClassIsValueBased;
12391239
}
12401240

1241+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
1242+
if (classFileOracle->hasIdentityInterface()
1243+
|| classFileOracle->needsIdentityInterface()
1244+
) {
1245+
modifiers |= J9AccClassHasIdentity;
1246+
}
1247+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
1248+
12411249
U_32 classNameindex = classFileOracle->getClassNameIndex();
12421250

12431251
#define WEAK_NAME "java/lang/ref/WeakReference"

runtime/bcutil/ROMClassCreationContext.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2001, 2020 IBM Corp. and others
2+
* Copyright (c) 2001, 2021 IBM Corp. and others
33
*
44
* This program and the accompanying materials are made available under
55
* the terms of the Eclipse Public License 2.0 which accompanies this
@@ -81,6 +81,10 @@ ROMClassCreationContext::verbosePrintPhase(ROMClassCreationPhase phase, bool *pr
8181
"MethodIsGetter",
8282
"MethodIsVirtual",
8383
"MethodIsObjectConstructor",
84+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
85+
"MethodIsConstructor",
86+
"MethodIsNonStaticSynchronized",
87+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
8488
"ShouldConvertInvokeVirtualToInvokeSpecial",
8589
"ParseClassFile",
8690
"ParseClassFileConstantPool",

runtime/bcutil/defineclass.c

+3
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,9 @@ createROMClassFromClassFile(J9VMThread *currentThread, J9LoadROMClassData *loadD
863863
case BCT_ERR_VERIFY_ERROR_INLINING:
864864
case BCT_ERR_BYTECODE_TRANSLATION_FAILED:
865865
case BCT_ERR_UNKNOWN_ANNOTATION:
866+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
867+
case BCT_ERR_INVALID_VALUE_TYPE:
868+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
866869
exceptionNumber = J9VMCONSTANTPOOL_JAVALANGVERIFYERROR;
867870
break;
868871

runtime/bcutil/romphase.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2001, 2020 IBM Corp. and others
2+
* Copyright (c) 2001, 2021 IBM Corp. and others
33
*
44
* This program and the accompanying materials are made available under
55
* the terms of the Eclipse Public License 2.0 which accompanies this
@@ -73,6 +73,10 @@ enum ROMClassCreationPhase {
7373
MethodIsVirtual,
7474
MethodIsNonStaticNonAbstract,
7575
MethodIsObjectConstructor,
76+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
77+
MethodIsConstructor,
78+
MethodIsNonStaticSynchronized,
79+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
7680
ShouldConvertInvokeVirtualToInvokeSpecial,
7781
ParseClassFile,
7882
ParseClassFileConstantPool,

runtime/nls/j9vm/j9vm.nls

+17
Original file line numberDiff line numberDiff line change
@@ -2064,3 +2064,20 @@ J9NLS_VM_DEFAULT_METHOD_CONFLICT_GENERIC.explanation=Default method conflict rai
20642064
J9NLS_VM_DEFAULT_METHOD_CONFLICT_GENERIC.system_action=The JVM will throw an IncompatibleClassChangeError.
20652065
J9NLS_VM_DEFAULT_METHOD_CONFLICT_GENERIC.user_response=Investigate the source of the exception.
20662066
# END NON-TRANSLATABLE
2067+
2068+
#Message used when JVM detects a value type that has an illegal super class
2069+
# argument 1 is the length of value type name
2070+
# argument 2 is the string of value type name
2071+
# argument 3 is the length of super class name
2072+
# argument 4 is the string of super class name
2073+
J9NLS_VM_VALUETYPE_HAS_WRONG_SUPERCLASS=Value type class %2$.*1$s has an invalid super class %4$.*3$s
2074+
# START NON-TRANSLATABLE
2075+
J9NLS_VM_VALUETYPE_HAS_WRONG_SUPERCLASS.sample_input_1=3
2076+
J9NLS_VM_VALUETYPE_HAS_WRONG_SUPERCLASS.sample_input_2=Foo
2077+
J9NLS_VM_VALUETYPE_HAS_WRONG_SUPERCLASS.sample_input_3=3
2078+
J9NLS_VM_VALUETYPE_HAS_WRONG_SUPERCLASS.sample_input_4=Var
2079+
2080+
J9NLS_VM_VALUETYPE_HAS_WRONG_SUPERCLASS.explanation=The specified class cannot be a super class of a value type.
2081+
J9NLS_VM_VALUETYPE_HAS_WRONG_SUPERCLASS.system_action=The JVM will throw IncompatibleClassChangeError.
2082+
J9NLS_VM_VALUETYPE_HAS_WRONG_SUPERCLASS.user_response=Contact the provider of the classfile for a corrected version.
2083+
# END NON-TRANSLATABLE

runtime/oti/bcutil.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2011, 2020 IBM Corp. and others
2+
* Copyright (c) 2011, 2021 IBM Corp. and others
33
*
44
* This program and the accompanying materials are made available under
55
* the terms of the Eclipse Public License 2.0 which accompanies this
@@ -40,4 +40,7 @@
4040
#define BCT_ERR_INVALID_CLASS_TYPE -17
4141
#define BCT_ERR_INVALID_ANNOTATION_BAD_CP_INDEX_OUT_OF_RANGE -18
4242
#define BCT_ERR_INVALID_ANNOTATION_BAD_CP_UTF8_STRING -19
43+
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
44+
#define BCT_ERR_INVALID_VALUE_TYPE -20
45+
#endif /* defined(J9VM_OPT_VALHALLA_VALUE_TYPES) */
4346

runtime/oti/j9javaaccessflags.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2019, 2020 IBM Corp. and others
2+
* Copyright (c) 2019, 2021 IBM Corp. and others
33
*
44
* This program and the accompanying materials are made available under
55
* the terms of the Eclipse Public License 2.0 which accompanies this
@@ -35,6 +35,7 @@
3535
#define J9AccAnnotation 0x2000
3636
#define J9AccBridge 0x40
3737

38+
#define J9AccClassHasIdentity 0x20
3839
#define J9AccClassIsValueBased 0x40
3940
#define J9AccClassHiddenOptionNestmate 0x80
4041
#define J9AccClassHiddenOptionStrong 0x100

runtime/oti/j9modifiers_api.h

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
#define J9ROMCLASS_IS_RECORD(romClass) (_J9ROMCLASS_J9MODIFIER_IS_SET((romClass), J9AccRecord) && J9ROMCLASS_IS_FINAL(romClass) && !J9ROMCLASS_IS_ABSTRACT(romClass))
7272
#define J9ROMCLASS_IS_SEALED(romClass) _J9ROMCLASS_J9MODIFIER_IS_SET((romClass), J9AccSealed)
7373
#define J9ROMCLASS_IS_VALUEBASED(romClass) _J9ROMCLASS_J9MODIFIER_IS_SET((romClass), J9AccClassIsValueBased)
74+
#define J9ROMCLASS_HAS_IDENTITY(romClass) _J9ROMCLASS_J9MODIFIER_IS_SET((romClass), J9AccClassHasIdentity)
7475

7576
/*
7677
* Note that resolvefield ignores this flag if the cache line size cannot be determined.

runtime/oti/j9nonbuilder.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
#define J9ClassHasReferences 0x10000
8585
#define J9ClassRequiresPrePadding 0x20000
8686
#define J9ClassIsValueBased 0x40000
87-
87+
#define J9ClassHasIdentity 0x80000
8888

8989
/* @ddr_namespace: map_to_type=J9FieldFlags */
9090

0 commit comments

Comments
 (0)