From 5123c7f12762da479ce264dbed9cf1d72e6f5ed6 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Wed, 27 Aug 2025 14:26:14 +0200 Subject: [PATCH] Automatically check for misplaced @UnknownPrimitiveField and @UnknownObjectField annotations --- .../ameta/FieldValueInterceptionSupport.java | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/FieldValueInterceptionSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/FieldValueInterceptionSupport.java index e6ca85da99e8..6e71b6b755f4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/FieldValueInterceptionSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/FieldValueInterceptionSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, 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 @@ -24,6 +24,7 @@ */ package com.oracle.svm.hosted.ameta; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; @@ -56,6 +57,7 @@ import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; import com.oracle.svm.hosted.substitute.AutomaticUnsafeTransformationSupport; import com.oracle.svm.hosted.substitute.FieldValueTransformation; +import com.oracle.svm.util.ClassUtil; import com.oracle.svm.util.ReflectionUtil; import jdk.graal.compiler.nodes.ValueNode; @@ -376,6 +378,7 @@ private static JavaConstant interceptWordField(AnalysisField field, JavaConstant private static FieldValueComputer createFieldValueComputer(AnalysisField field) { UnknownObjectField unknownObjectField = field.getAnnotation(UnknownObjectField.class); if (unknownObjectField != null) { + checkMisplacedAnnotation(field.getStorageKind().isObject(), field); return new FieldValueComputer( ReflectionUtil.newInstance(unknownObjectField.availability()), extractAnnotationTypes(field, unknownObjectField.types(), unknownObjectField.fullyQualifiedTypes()), @@ -383,6 +386,7 @@ private static FieldValueComputer createFieldValueComputer(AnalysisField field) } UnknownPrimitiveField unknownPrimitiveField = field.getAnnotation(UnknownPrimitiveField.class); if (unknownPrimitiveField != null) { + checkMisplacedAnnotation(field.getStorageKind().isPrimitive(), field); return new FieldValueComputer( ReflectionUtil.newInstance(unknownPrimitiveField.availability()), List.of(field.getType().getJavaClass()), @@ -391,6 +395,31 @@ private static FieldValueComputer createFieldValueComputer(AnalysisField field) return null; } + /** + * For compatibility reasons, we cannot unify {@link UnknownObjectField} and + * {@link UnknownPrimitiveField} into a single annotation, but we can at least notify the + * developers if the annotation is misplaced, e.g. {@link UnknownObjectField} is used on a + * primitive field and vice versa. + */ + private static void checkMisplacedAnnotation(boolean condition, AnalysisField field) { + if (!condition) { + String fieldType; + Class expectedAnnotationType; + Class usedAnnotationType; + if (field.getStorageKind().isObject()) { + fieldType = "object"; + expectedAnnotationType = UnknownObjectField.class; + usedAnnotationType = UnknownPrimitiveField.class; + } else { + fieldType = "primitive"; + expectedAnnotationType = UnknownPrimitiveField.class; + usedAnnotationType = UnknownObjectField.class; + } + throw UserError.abort("@%s should not be used on %s fields, use @%s on %s instead.", ClassUtil.getUnqualifiedName(usedAnnotationType), + fieldType, ClassUtil.getUnqualifiedName(expectedAnnotationType), field.format("%H.%n")); + } + } + private static List> extractAnnotationTypes(AnalysisField field, Class[] types, String[] fullyQualifiedTypes) { List> annotationTypes = new ArrayList<>(Arrays.asList(types)); for (String annotationTypeName : fullyQualifiedTypes) {