From ba6a26ebdf74c2d9462e2ff6f6ac79bcb401d693 Mon Sep 17 00:00:00 2001 From: Tobi Ajila Date: Tue, 16 Mar 2021 17:25:02 -0400 Subject: [PATCH] JIT cthelpers to query annotations Details are in the issues below. Fixes: #11993 Fixes: #11990 Most of the work was done by Sharon Wang. Creating this PR on her behalf. Signed-off-by: Tobi Ajila --- runtime/jit_vm/cthelpers.cpp | 57 ++- runtime/makelib/targets.mk.linux.inc.ftl | 26 +- runtime/oti/j9nonbuilder.h | 3 + runtime/oti/j9protos.h | 4 - runtime/oti/util_api.h | 33 ++ runtime/oti/vm_api.h | 6 + runtime/tests/annotationtests/CMakeLists.txt | 43 ++ runtime/tests/annotationtests/annhelp_tests.c | 84 ++++ runtime/tests/annotationtests/module.xml | 45 +++ runtime/util/annhelp.c | 191 ++++++++- runtime/util/vmutil.tdf | 5 +- runtime/vm/intfunc.c | 7 +- runtime/vm/resolvefield.cpp | 3 +- test/functional/Java8andUp/playlist.xml | 25 ++ .../ContainsRuntimeAnnotationTest.java | 368 +++++++++++++++++ .../ContainsRuntimeAnnotationTest.java | 372 ++++++++++++++++++ test/functional/Java8andUp/testng.xml | 7 +- 17 files changed, 1252 insertions(+), 27 deletions(-) create mode 100644 runtime/tests/annotationtests/CMakeLists.txt create mode 100644 runtime/tests/annotationtests/annhelp_tests.c create mode 100644 runtime/tests/annotationtests/module.xml create mode 100644 test/functional/Java8andUp/src_110_up/org/openj9/test/annotation/ContainsRuntimeAnnotationTest.java create mode 100644 test/functional/Java8andUp/src_80/org/openj9/test/annotation/ContainsRuntimeAnnotationTest.java diff --git a/runtime/jit_vm/cthelpers.cpp b/runtime/jit_vm/cthelpers.cpp index f0741d9cc0d..b8bd0ba6b0b 100644 --- a/runtime/jit_vm/cthelpers.cpp +++ b/runtime/jit_vm/cthelpers.cpp @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 1991, 2019 IBM Corp. and others + * Copyright (c) 1991, 2021 IBM Corp. and others * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this @@ -29,6 +29,18 @@ extern "C" { +#if JAVA_SPEC_VERSION >= 16 +J9_DECLARE_CONSTANT_UTF8(ojdk_intrinsicCandidate, "Ljdk/internal/vm/annotation/IntrinsicCandidate;"); +#endif /* JAVA_SPEC_VERSION >= 16 */ + +#if JAVA_SPEC_VERSION >= 11 +J9_DECLARE_CONSTANT_UTF8(ojdk_stable, "Ljdk/internal/vm/annotation/Stable;"); +J9_DECLARE_CONSTANT_UTF8(ojdk_forceInline, "Ljdk/internal/vm/annotation/ForceInline;"); +#else /* JAVA_SPEC_VERSION >= 11 */ +J9_DECLARE_CONSTANT_UTF8(ojdk_stable, "Ljava/lang/invoke/Stable;"); +J9_DECLARE_CONSTANT_UTF8(ojdk_forceInline, "Ljava/lang/invoke/ForceInline;"); +#endif /* JAVA_SPEC_VERSION >= 11 */ + void* jitGetCountingSendTarget(J9VMThread *vmThread, J9Method *ramMethod) { @@ -181,4 +193,47 @@ jitGetConstantDynamicTypeFromCP(J9VMThread *currentThread, J9ConstantPool *const return sigUTF; } +/** + * Queries if the fieldref at the specified cpIndex contains the @Stable annotation + * + * @param clazz J9Class + * @param cpIndex fieldref cp index + * @return true if fieldref contains @Stable, false otherwise + */ +bool +jitIsFieldStable(J9VMThread *currentThread, J9Class *clazz, UDATA cpIndex, bool isStatic) +{ + return FALSE != fieldContainsRuntimeAnnotation(currentThread, clazz, cpIndex, (J9UTF8 *)&ojdk_stable); +} + +/** + * Queries if the methodref at the specified cpIndex contains the @ForceInline annotation + * + * @param clazz J9Class + * @param cpIndex methodref cp index + * @return true if methodref contains @ForceInline, false otherwise + */ +bool +jitIsMethodTaggedWithForceInline(J9VMThread *currentThread, J9Class *clazz, UDATA cpIndex, UDATA type) +{ + return FALSE != methodContainsRuntimeAnnotation(currentThread, clazz, cpIndex, (J9UTF8 *)&ojdk_forceInline, type); +} + +/** + * Queries if the methodref at the specified cpIndex contains the @IntrinsicCandidate annotation + * + * @param clazz J9Class + * @param cpIndex methodref cp index + * @return true if methodref contains @IntrinsicCandidate, false otherwise + */ +bool +jitIsMethodTaggedWithIntrinsicCandidate(J9VMThread *currentThread, J9Class *clazz, UDATA cpIndex, UDATA type) +{ +#if JAVA_SPEC_VERSION >= 16 + return FALSE != methodContainsRuntimeAnnotation(currentThread, clazz, cpIndex, (J9UTF8 *)&ojdk_intrinsicCandidate, type); +#else /* JAVA_SPEC_VERSION >= 16 */ + return false; +#endif /* JAVA_SPEC_VERSION >= 16 */ +} + } diff --git a/runtime/makelib/targets.mk.linux.inc.ftl b/runtime/makelib/targets.mk.linux.inc.ftl index 43ac2c4c657..6e40dae154c 100644 --- a/runtime/makelib/targets.mk.linux.inc.ftl +++ b/runtime/makelib/targets.mk.linux.inc.ftl @@ -48,7 +48,7 @@ $(UMA_DLLTARGET) : $(UMA_OBJECTS) $(UMA_TARGET_LIBRARIES) $(UMA_DLL_LINK_POSTFLAGS) ifdef j9vm_uma_gnuDebugSymbols $(OBJCOPY) --only-keep-debug $@ $(@:$(UMA_DOT_DLL)=.debuginfo) - $(OBJCOPY) --strip-debug $@ + $(OBJCOPY) $@ $(OBJCOPY) --add-gnu-debuglink=$(@:$(UMA_DOT_DLL)=.debuginfo) $@ endif @@ -64,7 +64,7 @@ $(UMA_EXETARGET) : $(UMA_OBJECTS) $(UMA_TARGET_LIBRARIES) -o $@ $(UMA_EXE_POSTFIX_FLAGS) ifdef j9vm_uma_gnuDebugSymbols $(OBJCOPY) --only-keep-debug $@ $(@:$(UMA_DOT_EXE)=.debuginfo) - $(OBJCOPY) --strip-debug $@ + $(OBJCOPY) $@ $(OBJCOPY) --add-gnu-debuglink=$(@:$(UMA_DOT_EXE)=.debuginfo) $@ endif @@ -111,13 +111,13 @@ ifndef UMA_DO_NOT_OPTIMIZE_CCODE UMA_OPTIMIZATION_CFLAGS += ${uma.spec.properties.uma_optimization_cflags.value} <#else> <#if uma.spec.processor.amd64 || uma.spec.processor.riscv64> - UMA_OPTIMIZATION_CFLAGS += -O3 -fno-strict-aliasing + UMA_OPTIMIZATION_CFLAGS += -O0 -fno-strict-aliasing <#elseif uma.spec.processor.x86> - UMA_OPTIMIZATION_CFLAGS += -O3 -fno-strict-aliasing -march=pentium4 -mtune=prescott -mpreferred-stack-boundary=4 + UMA_OPTIMIZATION_CFLAGS += -O0 -fno-strict-aliasing -march=pentium4 -mtune=prescott -mpreferred-stack-boundary=4 <#elseif uma.spec.processor.arm> - UMA_OPTIMIZATION_CFLAGS += -g -O3 -fno-strict-aliasing $(ARM_ARCH_FLAGS) -Wno-unused-but-set-variable + UMA_OPTIMIZATION_CFLAGS += -g3 -O0 -fno-strict-aliasing $(ARM_ARCH_FLAGS) -Wno-unused-but-set-variable <#elseif uma.spec.processor.ppc> - UMA_OPTIMIZATION_CFLAGS += -O3 + UMA_OPTIMIZATION_CFLAGS += -O0 <#if uma.spec.flags.env_gcc.enabled> UMA_OPTIMIZATION_CFLAGS += -fno-strict-aliasing @@ -128,7 +128,7 @@ ifndef UMA_DO_NOT_OPTIMIZE_CCODE endif <#elseif uma.spec.processor.s390> - UMA_OPTIMIZATION_CFLAGS += -O3 -mtune=z10 -march=z9-109 -mzarch + UMA_OPTIMIZATION_CFLAGS += -O0 -mtune=z10 -march=z9-109 -mzarch <#else> UMA_OPTIMIZATION_CFLAGS += -O @@ -137,13 +137,13 @@ ifndef UMA_DO_NOT_OPTIMIZE_CCODE UMA_OPTIMIZATION_CXXFLAGS += ${uma.spec.properties.uma_optimization_cxxflags.value} <#else> <#if uma.spec.processor.amd64 || uma.spec.processor.riscv64> - UMA_OPTIMIZATION_CXXFLAGS += -O3 -fno-strict-aliasing + UMA_OPTIMIZATION_CXXFLAGS += -O0 -fno-strict-aliasing <#elseif uma.spec.processor.x86> - UMA_OPTIMIZATION_CXXFLAGS += -O3 -fno-strict-aliasing -march=pentium4 -mtune=prescott -mpreferred-stack-boundary=4 + UMA_OPTIMIZATION_CXXFLAGS += -O0 -fno-strict-aliasing -march=pentium4 -mtune=prescott -mpreferred-stack-boundary=4 <#elseif uma.spec.processor.arm> - UMA_OPTIMIZATION_CXXFLAGS += -g -O3 -fno-strict-aliasing $(ARM_ARCH_FLAGS) -Wno-unused-but-set-variable + UMA_OPTIMIZATION_CXXFLAGS += -g3 -O0 -fno-strict-aliasing $(ARM_ARCH_FLAGS) -Wno-unused-but-set-variable <#elseif uma.spec.processor.ppc> - UMA_OPTIMIZATION_CXXFLAGS += -O3 + UMA_OPTIMIZATION_CXXFLAGS += -O0 <#if uma.spec.flags.env_gcc.enabled> UMA_OPTIMIZATION_CXXFLAGS += -fno-strict-aliasing @@ -151,7 +151,7 @@ ifndef UMA_DO_NOT_OPTIMIZE_CCODE UMA_OPTIMIZATION_CXXFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 <#elseif uma.spec.processor.s390> - UMA_OPTIMIZATION_CXXFLAGS += -O3 -mtune=z10 -march=z9-109 -mzarch + UMA_OPTIMIZATION_CXXFLAGS += -O0 -mtune=z10 -march=z9-109 -mzarch <#else> UMA_OPTIMIZATION_CXXFLAGS += -O @@ -165,7 +165,7 @@ CFLAGS += $(UMA_OPTIMIZATION_CFLAGS) CXXFLAGS += $(UMA_OPTIMIZATION_CXXFLAGS) <#if uma.spec.processor.ppc> ifdef USE_PPC_GCC - PPC_GCC_CXXFLAGS += -O3 -fno-strict-aliasing + PPC_GCC_CXXFLAGS += -O0 -fno-strict-aliasing endif diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index 12278fa46d9..1b7577ac9f6 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -4779,6 +4779,9 @@ typedef struct J9InternalVMFunctions { UDATA ( *jniIsInternalClassRef)(struct J9JavaVM *vm, jobject ref); BOOLEAN (*objectIsBeingWaitedOn)(struct J9VMThread *currentThread, struct J9VMThread *targetThread, j9object_t obj); BOOLEAN (*areValueBasedMonitorChecksEnabled)(struct J9JavaVM *vm); + BOOLEAN (*fieldContainsRuntimeAnnotation)(struct J9VMThread *currentThread, J9Class *clazz, UDATA cpIndex, J9UTF8 *annotationName); + BOOLEAN (*methodContainsRuntimeAnnotation)(struct J9VMThread *currentThread, J9Class *clazz, UDATA cpIndex, J9UTF8 *annotationName, UDATA type); + J9ROMFieldShape* (*findFieldAndCheckVisibility)(struct J9VMThread *currentThread, J9Class *clazz, U_8 *fieldName, UDATA fieldNameLength, U_8 *signature, UDATA signatureLength, J9Class **definingClass, UDATA *offsetOrAddress, UDATA options, J9Class *sourceClass); } J9InternalVMFunctions; /* Jazz 99339: define a new structure to replace JavaVM so as to pass J9NativeLibrary to JVMTIEnv */ diff --git a/runtime/oti/j9protos.h b/runtime/oti/j9protos.h index bf0af274c97..41192c331eb 100644 --- a/runtime/oti/j9protos.h +++ b/runtime/oti/j9protos.h @@ -1382,10 +1382,6 @@ extern J9_CFUNC void jitAddPicToPatchOnClassUnload (void *classPointer, void *a #endif /* J9VM_INTERP_NATIVE_SUPPORT */ #endif /* _J9VMNATIVEHELPERSLARGE_ */ -/* Runtime annotation handling */ -extern J9_CFUNC I_32 getAnnotationByType(J9ROMConstantPoolItem const *constantPool, J9UTF8 const *searchString, - U_32 const numAnnotations, U_8 const *data, U_8 const **pIndex, U_8 const *dataEnd); - #ifdef __cplusplus } #endif diff --git a/runtime/oti/util_api.h b/runtime/oti/util_api.h index 2dd14a3b6ac..5ac6c6b253b 100644 --- a/runtime/oti/util_api.h +++ b/runtime/oti/util_api.h @@ -68,6 +68,39 @@ alignedMemcpy(J9VMThread *vmStruct, void *dest, void *source, UDATA bytes, UDATA void alignedBackwardsMemcpy(J9VMThread *vmStruct, void *dest, void *source, UDATA bytes, UDATA alignment); +/* ---------------- annhelp.c ---------------- */ + +/** + * Check if a fieldref contains the specified Runtime Visible annotation. Fieldref + * must be resolved. + * + * @param currentThread Thread token + * @param clazz The class the field belongs to. + * @param cpIndex The constant pool index of the fieldref. + * @param annotationName The name of the annotation to check for. + * @return TRUE if the annotation is found, FALSE otherwise. + */ +BOOLEAN +fieldContainsRuntimeAnnotation(J9VMThread *currentThread, J9Class *clazz, UDATA cpIndex, J9UTF8 *annotationName); + +#define J9VM_METHOD_CONTAINS_ANN_VIRTUAL 1 +#define J9VM_METHOD_CONTAINS_ANN_STATIC 2 +#define J9VM_METHOD_CONTAINS_ANN_SPECIAL 4 +#define J9VM_METHOD_CONTAINS_ANN_INTERFACE 8 + +/** + * Check if a methodref contains the specified Runtime Visible annotation. Methodref + * must be resolved. + * + * @param currentThread Thread token + * @param clazz The class the method belongs to. + * @param cpIndex The constant pool index of the methodref. + * @param annotationName The name of the annotation to check for. + * @param type the type of method, static|virtual|special|interface + * @return TRUE if the annotation is found, FALSE otherwise. + */ +BOOLEAN +methodContainsRuntimeAnnotation(J9VMThread *currentThread, J9Class *clazz, UDATA cpIndex, J9UTF8 *annotationName, UDATA type); /* ---------------- argbits.c ---------------- */ diff --git a/runtime/oti/vm_api.h b/runtime/oti/vm_api.h index bc03b08159c..64ab65b5958 100644 --- a/runtime/oti/vm_api.h +++ b/runtime/oti/vm_api.h @@ -2603,6 +2603,12 @@ putFlattenableField(J9VMThread *currentThread, J9RAMFieldRef *cpEntry, j9object_ void * staticFieldAddress(J9VMThread *vmStruct, J9Class *clazz, U_8 *fieldName, UDATA fieldNameLength, U_8 *signature, UDATA signatureLength, J9Class **definingClass, UDATA *staticField, UDATA options, J9Class *sourceClass); +/** + * + */ +J9ROMFieldShape* +findFieldAndCheckVisibility (J9VMThread *currentThread, J9Class *clazz, U_8 *fieldName, UDATA fieldNameLength, U_8 *signature, UDATA signatureLength, J9Class **definingClass, UDATA *offsetOrAddress, UDATA options, J9Class *sourceClass); + /** * @brief * @param *vm: Reference to the VM, used to locate the table. diff --git a/runtime/tests/annotationtests/CMakeLists.txt b/runtime/tests/annotationtests/CMakeLists.txt new file mode 100644 index 00000000000..ba86ca606fb --- /dev/null +++ b/runtime/tests/annotationtests/CMakeLists.txt @@ -0,0 +1,43 @@ +################################################################################ +# Copyright (c) 2021, 2021 IBM Corp. and others +# +# This program and the accompanying materials are made available under +# the terms of the Eclipse Public License 2.0 which accompanies this +# distribution and is available at https://www.eclipse.org/legal/epl-2.0/ +# or the Apache License, Version 2.0 which accompanies this distribution and +# is available at https://www.apache.org/licenses/LICENSE-2.0. +# +# This Source Code may also be made available under the following +# Secondary Licenses when the conditions for such availability set +# forth in the Eclipse Public License, v. 2.0 are satisfied: GNU +# General Public License, version 2 with the GNU Classpath +# Exception [1] and GNU General Public License, version 2 with the +# OpenJDK Assembly Exception [2]. +# +# [1] https://www.gnu.org/software/classpath/license.html +# [2] http://openjdk.java.net/legal/assembly-exception.html +# +# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception +################################################################################ + +set(OMR_ENHANCED_WARNINGS OFF) + +j9vm_add_library(anntests SHARED + annhelp_tests.c +) + +target_link_libraries(anntests + PRIVATE + j9vm_interface + jvm +) + +omr_add_exports(annotationtests + Java_org_openj9_test_annotation_ContainsRuntimeAnnotationTest_containsRuntimeAnnotation +) + +install( + TARGETS anntests + LIBRARY DESTINATION ${j9vm_SOURCE_DIR} + RUNTIME DESTINATION ${j9vm_SOURCE_DIR} +) diff --git a/runtime/tests/annotationtests/annhelp_tests.c b/runtime/tests/annotationtests/annhelp_tests.c new file mode 100644 index 00000000000..b6f02c62acb --- /dev/null +++ b/runtime/tests/annotationtests/annhelp_tests.c @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2021, 2021 IBM Corp. and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] http://openjdk.java.net/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception + *******************************************************************************/ + +#include "vmaccess.h" + +/** + * Checks if a field or method contains the runtime annotation specified. + * + * @param env The JNI environment. + * @param jlClass The class that the fieldref or method belongs to. + * @param cpIndex The constant pool index of the fieldref or methodref. + * @param annotationNameString The name of the annotation to check for. + * @param isField True if checking for a field, false if checking a method. + * @return JNI_TRUE if the annotation is found, JNI_FALSE otherwise. + */ +jboolean JNICALL +Java_org_openj9_test_annotation_ContainsRuntimeAnnotationTest_containsRuntimeAnnotation(JNIEnv *env, jclass unused, jclass jlClass, jint cpIndex, jstring annotationNameString, jboolean isField, jint type) +{ + jboolean result = JNI_FALSE; + J9VMThread *currentThread = (J9VMThread *)env; + J9JavaVM *vm = currentThread->javaVM; + J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions; + PORT_ACCESS_FROM_JAVAVM(vm); + + if (NULL == annotationNameString) { + vmFuncs->setCurrentExceptionUTF(currentThread, J9VMCONSTANTPOOL_JAVALANGNULLPOINTEREXCEPTION, "annotation name is null"); + } else { + vmFuncs->internalEnterVMFromJNI(currentThread); + + j9object_t annotationNameObj = J9_JNI_UNWRAP_REFERENCE(annotationNameString); + char annotationNameStackBuffer[J9VM_PACKAGE_NAME_BUFFER_LENGTH] = {0}; + J9UTF8 *annotationNameUTF8 = vmFuncs->copyStringToJ9UTF8WithMemAlloc( + currentThread, + annotationNameObj, + J9_STR_NULL_TERMINATE_RESULT, + "", + 0, + annotationNameStackBuffer, + 0); + + if (NULL == annotationNameUTF8) { + vmFuncs->setNativeOutOfMemoryError(currentThread, 0, 0); + } else { + J9Class *clazz = J9VM_J9CLASS_FROM_HEAPCLASS(currentThread, J9_JNI_UNWRAP_REFERENCE(jlClass)); + + if (NULL == clazz) { + vmFuncs->setCurrentExceptionUTF(currentThread, J9VMCONSTANTPOOL_JAVALANGNULLPOINTEREXCEPTION, "class cannot be found"); + } else { + if (isField) { + result = (jboolean)vmFuncs->fieldContainsRuntimeAnnotation(currentThread, clazz, (UDATA)cpIndex, annotationNameUTF8); + } else { + result = (jboolean)vmFuncs->methodContainsRuntimeAnnotation(currentThread, clazz, (UDATA)cpIndex, annotationNameUTF8, (UDATA)type); + } + } + + if ((J9UTF8 *)annotationNameStackBuffer != annotationNameUTF8) { + j9mem_free_memory(annotationNameUTF8); + } + } + + vmFuncs->internalExitVMToJNI(currentThread); + } + + return result; +} diff --git a/runtime/tests/annotationtests/module.xml b/runtime/tests/annotationtests/module.xml new file mode 100644 index 00000000000..c7a16bfb505 --- /dev/null +++ b/runtime/tests/annotationtests/module.xml @@ -0,0 +1,45 @@ + + + + + + + + + core j2se + + + + + + + + + + + + + + + + diff --git a/runtime/util/annhelp.c b/runtime/util/annhelp.c index b57f1c7a414..d4ce2da5b36 100644 --- a/runtime/util/annhelp.c +++ b/runtime/util/annhelp.c @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015, 2015 IBM Corp. and others + * Copyright (c) 2015, 2021 IBM Corp. and others * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this @@ -22,9 +22,13 @@ #include "cfreader.h" #include "j9protos.h" +#include "rommeth.h" +#include "ut_j9vmutil.h" -static I_32 -skipAnnotationElement(J9ROMConstantPoolItem const * constantPool, U_8 const *data, U_8 const **pIndex, U_8 const * dataEnd); +static I_32 skipAnnotationElement(J9ROMConstantPoolItem const *constantPool, U_8 const *data, U_8 const **pIndex, U_8 const *dataEnd); +static I_32 getAnnotationByType(J9ROMConstantPoolItem const *constantPool, J9UTF8 const *searchString, U_32 const numAnnotations, U_8 const *data, U_8 const **pIndex, U_8 const *dataEnd); +static J9ROMMethod *getMethodFromMethodRef(J9ROMClass *romClass, J9ROMMethodRef *romMethodRef); +static BOOLEAN findRuntimeVisibleAnnotation(U_8 *data, U_32 length, J9UTF8 *annotationName, J9ROMConstantPoolItem *constantPool); /** * @param (in) constantPool pointer to ROM class constant pool @@ -139,3 +143,184 @@ skipAnnotationElement(J9ROMConstantPoolItem const * constantPool, U_8 const *dat *pIndex = index; return -1; } + +/** + * Check if a field contains the specified Runtime Visible annotation. + * + * @param clazz The class the field belongs to. + * @param cpIndex The constant pool index of the field. + * @param annotationName The name of the annotation to check for. + * @return TRUE if the annotation is found, FALSE otherwise. + */ +BOOLEAN +fieldContainsRuntimeAnnotation(J9VMThread *currentThread, J9Class *clazz, UDATA cpIndex, J9UTF8 *annotationName) +{ + BOOLEAN annotationFound = FALSE; + J9ROMClass *romClass = NULL; + J9ROMConstantPoolItem *constantPool = NULL; + J9ROMFieldRef *romFieldRef = NULL; + J9ROMFieldShape *romFieldShape = NULL; + J9Class *declaringClass = NULL; + J9ROMNameAndSignature *nameAndSig = NULL; + J9UTF8 *name = NULL; + J9UTF8 *signature = NULL; + J9Class *definingClass = NULL; + J9ConstantPool *ramCP = (J9ConstantPool *)clazz->ramConstantPool; + + Assert_VMUtil_true(NULL != clazz); + Assert_VMUtil_true(NULL != annotationName); + + romClass = clazz->romClass; + constantPool = ramCP->romConstantPool; + romFieldRef = (J9ROMFieldRef *)&constantPool[cpIndex]; + + declaringClass = ((J9RAMClassRef *)&ramCP[romFieldRef->classRefCPIndex])->value; + nameAndSig = J9ROMFIELDREF_NAMEANDSIGNATURE(romFieldRef); + name = J9ROMNAMEANDSIGNATURE_NAME(nameAndSig); + signature = J9ROMNAMEANDSIGNATURE_SIGNATURE(nameAndSig); + + if (NULL != declaringClass) { + romFieldShape = currentThread->javaVM->internalVMFunctions->findFieldAndCheckVisibility(currentThread, + declaringClass, + J9UTF8_DATA(name), + J9UTF8_LENGTH(name), + J9UTF8_DATA(signature), + J9UTF8_LENGTH(signature), + &definingClass, + NULL, + J9_LOOK_NO_JAVA, + clazz); + + if (NULL != romFieldShape) { + U_32 *fieldAnnotationData = getFieldAnnotationsDataFromROMField(romFieldShape); + + if (NULL != fieldAnnotationData) { + U_32 len = *fieldAnnotationData; + U_8 *data = (U_8 *)(fieldAnnotationData + 1); + + annotationFound = findRuntimeVisibleAnnotation(data, len, annotationName, ((J9ConstantPool *)definingClass->ramConstantPool)->romConstantPool); + } + } + } + + Trc_Util_annhelp_SearchForFieldAnnotation(J9UTF8_DATA(annotationName), cpIndex, clazz, romFieldShape, (UDATA) annotationFound); + + return annotationFound; +} + +/** + * Check if a method contains the specified Runtime Visible annotation. + * + * @param clazz The class the method belongs to. + * @param cpIndex The constant pool index of the method. + * @param annotationName The name of the annotation to check for. + * @param isStatic TRUE if underlying method is static + * @return TRUE if the annotation is found, FALSE otherwise. + */ +BOOLEAN +methodContainsRuntimeAnnotation(J9VMThread *currentThread, J9Class *clazz, UDATA cpIndex, J9UTF8 *annotationName, UDATA type) +{ + BOOLEAN annotationFound = FALSE; + J9ROMClass *romClass = NULL; + J9ROMMethod *romMethod = NULL; + J9ConstantPool *ramCP = NULL; + J9Method *method = NULL; + + Assert_VMUtil_true(NULL != clazz); + Assert_VMUtil_true(NULL != annotationName); + + romClass = clazz->romClass; + ramCP = (J9ConstantPool *)clazz->ramConstantPool; + + if (J9_ARE_ANY_BITS_SET(type, J9VM_METHOD_CONTAINS_ANN_STATIC|J9VM_METHOD_CONTAINS_ANN_SPECIAL)) { + method = ((J9RAMMethodRef *)&ramCP[cpIndex])->method; + } else if (J9_ARE_ANY_BITS_SET(type, J9VM_METHOD_CONTAINS_ANN_VIRTUAL)) { + J9ROMConstantPoolItem *romConstantPool = ramCP->romConstantPool; + J9ROMMethodRef *romMethodRef = (J9ROMMethodRef *)&romConstantPool[cpIndex]; + J9Class *declaringClass = ((J9RAMClassRef *)&ramCP[romMethodRef->classRefCPIndex])->value; + UDATA methodAndArgsCount = ((J9RAMVirtualMethodRef *)&ramCP[cpIndex])->methodIndexAndArgCount; + UDATA vtableIndex = methodAndArgsCount >> 8; + + method = *(J9Method**)((UDATA)declaringClass + vtableIndex); + } else if (J9_ARE_ANY_BITS_SET(type, J9VM_METHOD_CONTAINS_ANN_INTERFACE)) { + + } else { + Assert_VMUtil_ShouldNeverHappen(); + } + + if (NULL != method) { + romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(method); + + if (NULL != romMethod) { + U_32 *methodAnnotationData = getMethodAnnotationsDataFromROMMethod(romMethod); + + if (NULL != methodAnnotationData) { + J9ConstantPool *ramCP = (J9ConstantPool *)clazz->ramConstantPool; + U_32 len = *methodAnnotationData; + U_8 *data = (U_8 *)(methodAnnotationData + 1); + + annotationFound = findRuntimeVisibleAnnotation(data, len, annotationName, method->constantPool->romConstantPool); + } + } + } + + Trc_Util_annhelp_SearchForMethodAnnotation(J9UTF8_DATA(annotationName), cpIndex, clazz, romMethod, (UDATA) annotationFound); + + return annotationFound; +} + +/** + * Check if the provided Runtime Visible annotation data contains the specified annotation. + * + * @param data The Runtime Visible annotation data. + * @param length the length of the annotation data + * @param annotationName The annotation to check for. + * @param constantPool The constant pool where the data is located. + * @return TRUE if the annotation is found, FALSE otherwise. + */ +static BOOLEAN +findRuntimeVisibleAnnotation(U_8 *data, U_32 length, J9UTF8 *annotationName, J9ROMConstantPoolItem *constantPool) +{ + U_8 *dataEnd = (U_8*)data + length; + U_32 errorCode = 0; /* used by CHECK_EOF */ + U_32 offset = 0; /* used by CHECK_EOF */ + U_8 const *index = data; /* used by CHECK_EOF */ + U_32 attributeLength = 0; + U_32 numAnnotations = 0; + I_32 getAnnotationResult = -1; + BOOLEAN found = FALSE; + U_16 numOfAnnotations = 0; + U_16 i = 0; + + CHECK_EOF(2); + NEXT_U16(numOfAnnotations, index); + + for (; i < numOfAnnotations; i++) { + U_16 annotationIndex = 0; + U_16 numOfMembers = 0; + U_16 j = 0; + J9UTF8 *searchAnnotation = NULL; + + CHECK_EOF(2); + NEXT_U16(annotationIndex, index); + searchAnnotation = J9ROMSTRINGREF_UTF8DATA((J9ROMStringRef *)&constantPool[annotationIndex]); + + if (J9UTF8_EQUALS(searchAnnotation, annotationName)) { + found = TRUE; + break; + } + + CHECK_EOF(2); + NEXT_U16(numOfMembers, index); + + for (; j < numOfMembers; j++) { + U_16 memberIndex = 0; + CHECK_EOF(2); + /* Disregard value and only iterate through the members. */ + NEXT_U16(memberIndex, index); + } + } + +_errorFound: + return found; +} diff --git a/runtime/util/vmutil.tdf b/runtime/util/vmutil.tdf index 9b9ad92bd93..d5856f9746e 100644 --- a/runtime/util/vmutil.tdf +++ b/runtime/util/vmutil.tdf @@ -1,4 +1,4 @@ -// Copyright (c) 2006, 2017 IBM Corp. and others +// Copyright (c) 2006, 2021 IBM Corp. and others // // This program and the accompanying materials are made available under // the terms of the Eclipse Public License 2.0 which accompanies this @@ -56,3 +56,6 @@ TraceEntry=Trc_VMUtil_getOriginalROMMethodUnchecked_Entry Overhead=1 Level=3 Noe TraceExit=Trc_VMUtil_getOriginalROMMethodUnchecked_Exit Overhead=1 Level=3 Noenv Template="getOriginalROMMethodUnchecked (result=%p)" TraceException=Trc_VMUtil_getOriginalROMMethodUnchecked_getMethodIndexFailed Overhead=1 Level=1 Noenv Template="getOriginalROMMethodUnchecked: Unable to get methodIndex for method=%p" + +TraceEvent=Trc_Util_annhelp_SearchForMethodAnnotation Noenv Overhead=1 Level=5 Template="Search for method annotation: annotation=%s cpIndex=%zu clazz=%p romMethod=%p result=%zu" +TraceEvent=Trc_Util_annhelp_SearchForFieldAnnotation Noenv Overhead=1 Level=5 Template="Search for field annotation: annotation=%s cpIndex=%zu clazz=%p romFieldShape=%p result=%zu" diff --git a/runtime/vm/intfunc.c b/runtime/vm/intfunc.c index 9c30fe62a52..cd131294e3e 100644 --- a/runtime/vm/intfunc.c +++ b/runtime/vm/intfunc.c @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 1991, 2020 IBM Corp. and others + * Copyright (c) 1991, 2021 IBM Corp. and others * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this @@ -396,5 +396,8 @@ J9InternalVMFunctions J9InternalFunctions = { loadFlattenableArrayElement, jniIsInternalClassRef, objectIsBeingWaitedOn, - areValueBasedMonitorChecksEnabled + areValueBasedMonitorChecksEnabled, + fieldContainsRuntimeAnnotation, + methodContainsRuntimeAnnotation, + findFieldAndCheckVisibility }; diff --git a/runtime/vm/resolvefield.cpp b/runtime/vm/resolvefield.cpp index a2e2a6ec692..cfe48f1ed00 100644 --- a/runtime/vm/resolvefield.cpp +++ b/runtime/vm/resolvefield.cpp @@ -62,7 +62,6 @@ static J9ROMFieldShape* findFieldInTable(J9VMThread *vmThread, J9Class *clazz, U #define SUPERCLASS(clazz) (((clazz)->superclasses[ J9CLASS_DEPTH(clazz) - 1 ])) static J9ROMFieldShape * findFieldInClass (J9VMThread *vmStruct, J9Class *clazz, U_8 *fieldName, UDATA fieldNameLength, U_8 *signature, UDATA signatureLength, UDATA *offsetOrAddress, J9Class **definingClass); -static J9ROMFieldShape* findFieldAndCheckVisibility (J9VMThread *vmStruct, J9Class *clazz, U_8 *fieldName, UDATA fieldNameLength, U_8 *signature, UDATA signatureLength, J9Class **definingClass, UDATA *offsetOrAddress, UDATA options, J9Class *sourceClass); static J9ROMFieldShape* findField (J9VMThread *vmStruct, J9Class *clazz, U_8 *fieldName, UDATA fieldNameLength, U_8 *signature, UDATA signatureLength, J9Class **definingClass, UDATA *offsetOrAddress, UDATA options); VMINLINE static UDATA calculateJ9UTFSize(UDATA stringLength); @@ -286,7 +285,7 @@ instanceFieldOffsetWithSourceClass(J9VMThread *vmStruct, J9Class *clazz, U_8 *fi definingClass is set to the class containing the field, if it is not NULL. offsetOrAddress is set to the instance offset (for instance field found) or static address (for static field found) if it is not NULL." */ -static J9ROMFieldShape* +J9ROMFieldShape* findFieldAndCheckVisibility (J9VMThread *vmStruct, J9Class *clazz, U_8 *fieldName, UDATA fieldNameLength, U_8 *signature, UDATA signatureLength, J9Class **definingClass, UDATA *offsetOrAddress, UDATA options, J9Class *sourceClass) { J9Class* defClass; diff --git a/test/functional/Java8andUp/playlist.xml b/test/functional/Java8andUp/playlist.xml index e88775fdb54..d407ec176e6 100644 --- a/test/functional/Java8andUp/playlist.xml +++ b/test/functional/Java8andUp/playlist.xml @@ -2612,4 +2612,29 @@ ibm + + RuntimeAnnotationTests + $(ADD_JVM_LIB_DIR_TO_LIBPATH) $(JAVA_COMMAND) $(JVM_OPTIONS) \ + $(ADD_EXPORTS_JDK_INTERNAL_REFLECT) $(ADD_EXPORTS_JDK_INTERNAL_MISC) $(ADD_EXPORTS_JDK_INTERNAL_ACCESS) \ + -cp $(Q)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(TEST_RESROOT)$(D)GeneralTest.jar$(Q) \ + org.testng.TestNG -d $(REPORTDIR) $(Q)$(TEST_RESROOT)$(D)testng.xml$(Q) \ + -testnames RuntimeAnnotationTests \ + -groups $(TEST_GROUP) \ + -excludegroups $(DEFAULT_EXCLUDE); \ + $(TEST_STATUS) + + + sanity + + + functional + + + native + + + openj9 + ibm + + diff --git a/test/functional/Java8andUp/src_110_up/org/openj9/test/annotation/ContainsRuntimeAnnotationTest.java b/test/functional/Java8andUp/src_110_up/org/openj9/test/annotation/ContainsRuntimeAnnotationTest.java new file mode 100644 index 00000000000..f2895a1312c --- /dev/null +++ b/test/functional/Java8andUp/src_110_up/org/openj9/test/annotation/ContainsRuntimeAnnotationTest.java @@ -0,0 +1,368 @@ +package org.openj9.test.annotation; + +/******************************************************************************* + * Copyright (c) 2021, 2021 IBM Corp. and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] http://openjdk.java.net/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception + *******************************************************************************/ + +import org.testng.Assert; +import org.testng.AssertJUnit; +import org.testng.annotations.Test; +import org.testng.log4testng.Logger; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; + +import java.util.Arrays; + +import jdk.internal.misc.SharedSecrets; +import jdk.internal.reflect.ConstantPool; +import jdk.internal.vm.annotation.Stable; + +@Test(groups = { "level.sanity" }) +public class ContainsRuntimeAnnotationTest { + private static final String THIS_CLASS_NAME = "org/openj9/test/annotation/ContainsRuntimeAnnotationTest"; + private final ConstantPool thisClassConstantPool; + + static { + try { + System.loadLibrary("anntests"); + } catch (UnsatisfiedLinkError e) { + Assert.fail(e.getMessage() + "\nlibrary path = " + System.getProperty("java.library.path")); + } + } + + @MyFieldAnnotation + int myField = 0; + + @MyMethodAnnotation + void myMethod() {} + + int myField2 = 0; + + @MyFieldAnnotation + @MyFieldAnnotation2 + @Stable + @MyFieldAnnotation3 + @MyFieldAnnotation4 + int myField3 = 0; + + void myMethod2() {} + + public ContainsRuntimeAnnotationTest() { + thisClassConstantPool = SharedSecrets.getJavaLangAccess().getConstantPool(this.getClass()); + } + + private static native boolean containsRuntimeAnnotation(Class clazz, int cpIndex, String annotationName, boolean isField, boolean isStatic); + + @Test + public void test_field_annotation() throws Exception { + boolean annotationFound = false; + + /* resolve fields */ + int field1 = myField; + int field2 = myField2; + int field3 = myField3; + + int cpIndex = getMemberCPIndex(thisClassConstantPool, THIS_CLASS_NAME, "myField", "I", true); + Assert.assertTrue(-1 != cpIndex, "could fine myField"); + final String annotationName = "Lorg/openj9/test/annotation/MyFieldAnnotation;"; + final String stableAnnotation = "Ljdk/internal/vm/annotation/Stable;"; + annotationFound = containsRuntimeAnnotation(ContainsRuntimeAnnotationTest.class, cpIndex, annotationName, true, false); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(thisClassConstantPool, THIS_CLASS_NAME, "myField2", "I", true); + Assert.assertTrue(-1 != cpIndex, "could fine myField2"); + + annotationFound = containsRuntimeAnnotation(ContainsRuntimeAnnotationTest.class, cpIndex, annotationName, true, false); + Assert.assertFalse(annotationFound, "detected non-existing annatation"); + + cpIndex = getMemberCPIndex(thisClassConstantPool, THIS_CLASS_NAME, "myField3", "I", true); + Assert.assertTrue(-1 != cpIndex, "could fine myField3"); + + annotationFound = containsRuntimeAnnotation(ContainsRuntimeAnnotationTest.class, cpIndex, stableAnnotation, true, false); + Assert.assertTrue(annotationFound, "didn't find @stable"); + } + + @Test + public void test_method_annotation() throws Exception { + boolean annotationFound = false; + + /* resolve methods */ + myMethod(); + myMethod2(); + + int cpIndex = getMemberCPIndex(thisClassConstantPool, THIS_CLASS_NAME, "myMethod", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method myMethod()V"); + + final String annotationName = "Lorg/openj9/test/annotation/MyMethodAnnotation;"; + annotationFound = containsRuntimeAnnotation(ContainsRuntimeAnnotationTest.class, cpIndex, annotationName, false, false); + Assert.assertTrue(annotationFound, "didnt detect annotation"); + + cpIndex = getMemberCPIndex(thisClassConstantPool, THIS_CLASS_NAME, "myMethod2", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method myMethod2()V"); + annotationFound = containsRuntimeAnnotation(ContainsRuntimeAnnotationTest.class, cpIndex, annotationName, false, false); + Assert.assertFalse(annotationFound, "detected non-existing annatation"); + } + + @Test + public void test_field_in_external_class() throws Exception { + boolean annotationFound = false; + + /* resolve fields */ + C c = new C(); + c.method1(new B()); + + ConstantPool constantPool = SharedSecrets.getJavaLangAccess().getConstantPool(C.class); + final String cClassName = "org/openj9/test/annotation/C"; + final String bClassName = "org/openj9/test/annotation/B"; + + int cpIndex = getMemberCPIndex(constantPool, bClassName, "field1", "I", true); + Assert.assertTrue(-1 != cpIndex, "couldn't find field1 fref"); + + final String annotationName = "Lorg/openj9/test/annotation/MyFieldAnnotation;"; + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, true, false); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, bClassName, "field2", "I", true); + Assert.assertTrue(-1 != cpIndex, "couldn't find field2 fref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, true, false); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, cClassName, "field3", "I", true); + Assert.assertTrue(-1 != cpIndex, "couldn't find field3 fref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, true, false); + Assert.assertTrue(annotationFound, "did not detect annotation"); + } + + @Test + public void test_static_field_in_external_class() throws Exception { + boolean annotationFound = false; + + /* resolve fields */ + C.sMethod1(); + + ConstantPool constantPool = SharedSecrets.getJavaLangAccess().getConstantPool(C.class); + final String cClassName = "org/openj9/test/annotation/C"; + final String bClassName = "org/openj9/test/annotation/B"; + + int cpIndex = getMemberCPIndex(constantPool, bClassName, "sfield1", "I", true); + Assert.assertTrue(-1 != cpIndex, "couldn't find sfield1 fref"); + + final String annotationName = "Lorg/openj9/test/annotation/MyFieldAnnotation;"; + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, true, true); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, bClassName, "sfield2", "I", true); + Assert.assertTrue(-1 != cpIndex, "couldn't find sfield2 fref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, true, true); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, cClassName, "sfield3", "I", true); + Assert.assertTrue(-1 != cpIndex, "couldn't find sfield3 fref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, true, true); + Assert.assertTrue(annotationFound, "did not detect annotation"); + } + + @Test + public void test_method_in_external_class() throws Exception { + boolean annotationFound = false; + + /* resolve methods */ + C c = new C(); + c.method4(new B()); + + ConstantPool constantPool = SharedSecrets.getJavaLangAccess().getConstantPool(C.class); + final String cClassName = "org/openj9/test/annotation/C"; + final String bClassName = "org/openj9/test/annotation/B"; + + int cpIndex = getMemberCPIndex(constantPool, bClassName, "method1", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method1 mref"); + + final String annotationName = "Lorg/openj9/test/annotation/MyMethodAnnotation;"; + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, false, false); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, bClassName, "method2", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method2 mref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, false, false); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, cClassName, "method3", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method3 mref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, false, false); + Assert.assertTrue(annotationFound, "did not detect annotation"); + } + + @Test + public void test_static_method_in_external_class() throws Exception { + boolean annotationFound = false; + + /* resolve methods */ + C.sMethod4(); + + ConstantPool constantPool = SharedSecrets.getJavaLangAccess().getConstantPool(C.class); + final String cClassName = "org/openj9/test/annotation/C"; + final String bClassName = "org/openj9/test/annotation/B"; + + int cpIndex = getMemberCPIndex(constantPool, bClassName, "sMethod1", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method1 mref"); + + final String annotationName = "Lorg/openj9/test/annotation/MyMethodAnnotation;"; + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, false, true); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, bClassName, "sMethod2", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method2 mref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, false, true); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, cClassName, "sMethod3", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method3 mref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, false, true); + Assert.assertTrue(annotationFound, "did not detect annotation"); + } + + private int getMemberCPIndex(ConstantPool constantPool, String className, String memberName, String memberType, boolean isField) { + int cpIndex = -1; + int size = constantPool.getSize(); + Assert.assertTrue(size > 1, "error with constantPool"); + + for (int i = size - 1; i > 0; i--) { + try { + /* Returns 3-element array of class name, member name and type */ + String [] cpMemberInfo = constantPool.getMemberRefInfoAt(i); + if (className.equals(cpMemberInfo[0]) + && memberName.equals(cpMemberInfo[1]) + && memberType.equals(cpMemberInfo[2]) + ) { + cpIndex = i; + break; + } + } catch (Throwable ignored) { + /* Ignore errors if the constant pool entry doesn't exist */ + } + } + + return cpIndex; + } +} +class A { + A() {} + + @MyFieldAnnotation + int field1; + + @MyFieldAnnotation + static int sfield1; + + @MyMethodAnnotation + void method1() {} + + @MyMethodAnnotation + static void sMethod1() {} +} + +class B extends A { + B() {} + + @MyFieldAnnotation + int field2; + + @MyFieldAnnotation + static int sfield2; + + @MyMethodAnnotation + void method2() {} + + @MyMethodAnnotation + static void sMethod2() {} +} + +class C { + @MyFieldAnnotation + int field3; + + @MyFieldAnnotation + static int sfield3; + + @MyMethodAnnotation + void method3() {} + + @MyMethodAnnotation + static void sMethod3() {} + + C() {} + + void method1(B b) { + int field1 = b.field1; + int field2 = b.field2; + int field3 = this.field3; + } + + static void sMethod1() { + int field1 = B.sfield1; + int field2 = B.sfield2; + int field3 = C.sfield3; + } + + static void sMethod4() { + B.sMethod1(); + B.sMethod2(); + C.sMethod3(); + } + + void method4(B b) { + b.method1(); + b.method2(); + this.method3(); + } +} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@interface MyFieldAnnotation {} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@interface MyFieldAnnotation2 {} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@interface MyFieldAnnotation3 {} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@interface MyFieldAnnotation4 {} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@interface MyMethodAnnotation {} diff --git a/test/functional/Java8andUp/src_80/org/openj9/test/annotation/ContainsRuntimeAnnotationTest.java b/test/functional/Java8andUp/src_80/org/openj9/test/annotation/ContainsRuntimeAnnotationTest.java new file mode 100644 index 00000000000..559f9589993 --- /dev/null +++ b/test/functional/Java8andUp/src_80/org/openj9/test/annotation/ContainsRuntimeAnnotationTest.java @@ -0,0 +1,372 @@ +package org.openj9.test.annotation; + +/******************************************************************************* + * Copyright (c) 2021, 2021 IBM Corp. and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] http://openjdk.java.net/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception + *******************************************************************************/ + +import org.testng.Assert; +import org.testng.AssertJUnit; +import org.testng.annotations.Test; +import org.testng.log4testng.Logger; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; + +import java.util.Arrays; + +import sun.misc.SharedSecrets; +import sun.reflect.ConstantPool; + +@Test(groups = { "level.sanity" }) +public class ContainsRuntimeAnnotationTest { + private static final String THIS_CLASS_NAME = "org/openj9/test/annotation/ContainsRuntimeAnnotationTest"; + private final ConstantPool thisClassConstantPool; + + static { + try { + System.loadLibrary("anntests"); + } catch (UnsatisfiedLinkError e) { + Assert.fail(e.getMessage() + "\nlibrary path = " + System.getProperty("java.library.path")); + } + } + + @MyFieldAnnotation + int myField = 0; + + @MyMethodAnnotation + void myMethod() {} + + int myField2 = 0; + + @MyFieldAnnotation + @MyFieldAnnotation2 + @Stable + @MyFieldAnnotation3 + @MyFieldAnnotation4 + int myField3 = 0; + + void myMethod2() {} + + public ContainsRuntimeAnnotationTest() { + thisClassConstantPool = SharedSecrets.getJavaLangAccess().getConstantPool(this.getClass()); + } + + private static native boolean containsRuntimeAnnotation(Class clazz, int cpIndex, String annotationName, boolean isField, boolean isStatic); + + @Test + public void test_field_annotation() throws Exception { + boolean annotationFound = false; + + /* resolve fields */ + int field1 = myField; + int field2 = myField2; + int field3 = myField3; + + int cpIndex = getMemberCPIndex(thisClassConstantPool, THIS_CLASS_NAME, "myField", "I", true); + Assert.assertTrue(-1 != cpIndex, "could fine myField"); + final String annotationName = "Lorg/openj9/test/annotation/MyFieldAnnotation;"; + final String stableAnnotation = "Lorg/openj9/test/annotation/MyStableAnnotation;"; + annotationFound = containsRuntimeAnnotation(ContainsRuntimeAnnotationTest.class, cpIndex, annotationName, true, false); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(thisClassConstantPool, THIS_CLASS_NAME, "myField2", "I", true); + Assert.assertTrue(-1 != cpIndex, "could fine myField2"); + + annotationFound = containsRuntimeAnnotation(ContainsRuntimeAnnotationTest.class, cpIndex, annotationName, true, false); + Assert.assertFalse(annotationFound, "detected non-existing annatation"); + + cpIndex = getMemberCPIndex(thisClassConstantPool, THIS_CLASS_NAME, "myField3", "I", true); + Assert.assertTrue(-1 != cpIndex, "could fine myField3"); + + annotationFound = containsRuntimeAnnotation(ContainsRuntimeAnnotationTest.class, cpIndex, stableAnnotation, true, false); + Assert.assertTrue(annotationFound, "didn't find @stable"); + } + + @Test + public void test_method_annotation() throws Exception { + boolean annotationFound = false; + + /* resolve methods */ + myMethod(); + myMethod2(); + + int cpIndex = getMemberCPIndex(thisClassConstantPool, THIS_CLASS_NAME, "myMethod", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method myMethod()V"); + + final String annotationName = "Lorg/openj9/test/annotation/MyMethodAnnotation;"; + annotationFound = containsRuntimeAnnotation(ContainsRuntimeAnnotationTest.class, cpIndex, annotationName, false, false); + Assert.assertTrue(annotationFound, "didnt detect annotation"); + + cpIndex = getMemberCPIndex(thisClassConstantPool, THIS_CLASS_NAME, "myMethod2", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method myMethod2()V"); + annotationFound = containsRuntimeAnnotation(ContainsRuntimeAnnotationTest.class, cpIndex, annotationName, false, false); + Assert.assertFalse(annotationFound, "detected non-existing annatation"); + } + + @Test + public void test_field_in_external_class() throws Exception { + boolean annotationFound = false; + + /* resolve fields */ + C c = new C(); + c.method1(new B()); + + ConstantPool constantPool = SharedSecrets.getJavaLangAccess().getConstantPool(C.class); + final String cClassName = "org/openj9/test/annotation/C"; + final String bClassName = "org/openj9/test/annotation/B"; + + int cpIndex = getMemberCPIndex(constantPool, bClassName, "field1", "I", true); + Assert.assertTrue(-1 != cpIndex, "couldn't find field1 fref"); + + final String annotationName = "Lorg/openj9/test/annotation/MyFieldAnnotation;"; + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, true, false); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, bClassName, "field2", "I", true); + Assert.assertTrue(-1 != cpIndex, "couldn't find field2 fref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, true, false); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, cClassName, "field3", "I", true); + Assert.assertTrue(-1 != cpIndex, "couldn't find field3 fref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, true, false); + Assert.assertTrue(annotationFound, "did not detect annotation"); + } + + @Test + public void test_static_field_in_external_class() throws Exception { + boolean annotationFound = false; + + /* resolve fields */ + C.sMethod1(); + + ConstantPool constantPool = SharedSecrets.getJavaLangAccess().getConstantPool(C.class); + final String cClassName = "org/openj9/test/annotation/C"; + final String bClassName = "org/openj9/test/annotation/B"; + + int cpIndex = getMemberCPIndex(constantPool, bClassName, "sfield1", "I", true); + Assert.assertTrue(-1 != cpIndex, "couldn't find sfield1 fref"); + + final String annotationName = "Lorg/openj9/test/annotation/MyFieldAnnotation;"; + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, true, true); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, bClassName, "sfield2", "I", true); + Assert.assertTrue(-1 != cpIndex, "couldn't find sfield2 fref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, true, true); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, cClassName, "sfield3", "I", true); + Assert.assertTrue(-1 != cpIndex, "couldn't find sfield3 fref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, true, true); + Assert.assertTrue(annotationFound, "did not detect annotation"); + } + + @Test + public void test_method_in_external_class() throws Exception { + boolean annotationFound = false; + + /* resolve methods */ + C c = new C(); + c.method4(new B()); + + ConstantPool constantPool = SharedSecrets.getJavaLangAccess().getConstantPool(C.class); + final String cClassName = "org/openj9/test/annotation/C"; + final String bClassName = "org/openj9/test/annotation/B"; + + int cpIndex = getMemberCPIndex(constantPool, bClassName, "method1", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method1 mref"); + + final String annotationName = "Lorg/openj9/test/annotation/MyMethodAnnotation;"; + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, false, false); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, bClassName, "method2", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method2 mref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, false, false); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, cClassName, "method3", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method3 mref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, false, false); + Assert.assertTrue(annotationFound, "did not detect annotation"); + } + + @Test + public void test_static_method_in_external_class() throws Exception { + boolean annotationFound = false; + + /* resolve methods */ + C.sMethod4(); + + ConstantPool constantPool = SharedSecrets.getJavaLangAccess().getConstantPool(C.class); + final String cClassName = "org/openj9/test/annotation/C"; + final String bClassName = "org/openj9/test/annotation/B"; + + int cpIndex = getMemberCPIndex(constantPool, bClassName, "sMethod1", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method1 mref"); + + final String annotationName = "Lorg/openj9/test/annotation/MyMethodAnnotation;"; + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, false, true); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, bClassName, "sMethod2", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method2 mref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, false, true); + Assert.assertTrue(annotationFound, "did not detect annotation"); + + cpIndex = getMemberCPIndex(constantPool, cClassName, "sMethod3", "()V", false); + Assert.assertTrue(-1 != cpIndex, "couldn't find method3 mref"); + + annotationFound = containsRuntimeAnnotation(C.class, cpIndex, annotationName, false, true); + Assert.assertTrue(annotationFound, "did not detect annotation"); + } + + private int getMemberCPIndex(ConstantPool constantPool, String className, String memberName, String memberType, boolean isField) { + int cpIndex = -1; + int size = constantPool.getSize(); + Assert.assertTrue(size > 1, "error with constantPool"); + + for (int i = size - 1; i > 0; i--) { + try { + /* Returns 3-element array of class name, member name and type */ + String [] cpMemberInfo = constantPool.getMemberRefInfoAt(i); + if (className.equals(cpMemberInfo[0]) + && memberName.equals(cpMemberInfo[1]) + && memberType.equals(cpMemberInfo[2]) + ) { + cpIndex = i; + break; + } + } catch (Throwable ignored) { + /* Ignore errors if the constant pool entry doesn't exist */ + } + } + + return cpIndex; + } +} +class A { + A() {} + + @MyFieldAnnotation + int field1; + + @MyFieldAnnotation + static int sfield1; + + @MyMethodAnnotation + void method1() {} + + @MyMethodAnnotation + static void sMethod1() {} +} + +class B extends A { + B() {} + + @MyFieldAnnotation + int field2; + + @MyFieldAnnotation + static int sfield2; + + @MyMethodAnnotation + void method2() {} + + @MyMethodAnnotation + static void sMethod2() {} +} + +class C { + @MyFieldAnnotation + int field3; + + @MyFieldAnnotation + static int sfield3; + + @MyMethodAnnotation + void method3() {} + + @MyMethodAnnotation + static void sMethod3() {} + + C() {} + + void method1(B b) { + int field1 = b.field1; + int field2 = b.field2; + int field3 = this.field3; + } + + static void sMethod1() { + int field1 = B.sfield1; + int field2 = B.sfield2; + int field3 = C.sfield3; + } + + static void sMethod4() { + B.sMethod1(); + B.sMethod2(); + C.sMethod3(); + } + + void method4(B b) { + b.method1(); + b.method2(); + this.method3(); + } +} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@interface MyFieldAnnotation {} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@interface MyFieldAnnotation2 {} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@interface MyFieldAnnotation3 {} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@interface MyFieldAnnotation4 {} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@interface MyMethodAnnotation {} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@interface MyStableAnnotation {} + diff --git a/test/functional/Java8andUp/testng.xml b/test/functional/Java8andUp/testng.xml index 9ec0a5f47bb..b45ed508fc8 100644 --- a/test/functional/Java8andUp/testng.xml +++ b/test/functional/Java8andUp/testng.xml @@ -1,7 +1,7 @@