diff --git a/src/java.base/share/classes/java/lang/TypeNotPresentException.java b/src/java.base/share/classes/java/lang/TypeNotPresentException.java index fdf42548c03..c5a899ef285 100644 --- a/src/java.base/share/classes/java/lang/TypeNotPresentException.java +++ b/src/java.base/share/classes/java/lang/TypeNotPresentException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, 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 @@ -28,9 +28,9 @@ package java.lang; /** * Thrown when an application tries to access a type using a string * representing the type's name, but no definition for the type with - * the specified name can be found. This exception differs from - * {@link ClassNotFoundException} in that {@code ClassNotFoundException} is a - * checked exception, whereas this exception is unchecked. + * the specified name can be found. This exception differs from + * {@link ClassNotFoundException} in that {@code ClassNotFoundException} + * is a checked exception, whereas this exception is unchecked. * *

Note that this exception may be used when undefined type variables * are accessed as well as when types (e.g., classes, interfaces or @@ -48,15 +48,15 @@ public class TypeNotPresentException extends RuntimeException { private static final long serialVersionUID = -5101214195716534496L; /** - * The type name. + * The type name or the name of a type variable. */ private String typeName; /** - * Constructs a {@code TypeNotPresentException} for the named type - * with the specified cause. + * Constructs a {@code TypeNotPresentException} for the named type or + * type variable with the specified cause. * - * @param typeName the fully qualified name of the unavailable type + * @param typeName the fully qualified name of the unavailable type or type variable * @param cause the exception that was thrown when the system attempted to * load the named type, or {@code null} if unavailable or inapplicable */ @@ -66,9 +66,9 @@ public class TypeNotPresentException extends RuntimeException { } /** - * Returns the fully qualified name of the unavailable type. + * Returns the fully qualified name of the unavailable type or type variable name. * - * @return the fully qualified name of the unavailable type + * @return the fully qualified name of the unavailable type or type variable name */ public String typeName() { return typeName;} } diff --git a/src/java.base/share/classes/sun/reflect/generics/factory/CoreReflectionFactory.java b/src/java.base/share/classes/sun/reflect/generics/factory/CoreReflectionFactory.java index 0759c83be78..bad68a07cea 100644 --- a/src/java.base/share/classes/sun/reflect/generics/factory/CoreReflectionFactory.java +++ b/src/java.base/share/classes/sun/reflect/generics/factory/CoreReflectionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, 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 @@ -106,7 +106,11 @@ public class CoreReflectionFactory implements GenericsFactory { } public TypeVariable findTypeVariable(String name){ - return getScope().lookup(name); + TypeVariable variable = getScope().lookup(name); + if (variable == null) { + throw new TypeNotPresentException(name, null); + } + return variable; } public Type makeNamedType(String name){ diff --git a/test/jdk/java/lang/reflect/Generics/TestMissingTypeVariable.java b/test/jdk/java/lang/reflect/Generics/TestMissingTypeVariable.java new file mode 100644 index 00000000000..64f1457bdf7 --- /dev/null +++ b/test/jdk/java/lang/reflect/Generics/TestMissingTypeVariable.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @library /test/lib + * @bug 8337302 + * @enablePreview + * @summary Tests that an exception is thrown if a type variable is not declared + */ + +import jdk.test.lib.ByteCodeLoader; + +import java.lang.classfile.ClassFile; +import java.lang.classfile.Signature; +import java.lang.classfile.attribute.SignatureAttribute; +import java.lang.constant.ClassDesc; +import java.lang.reflect.AccessFlag; +import java.lang.reflect.Type; + +public class TestMissingTypeVariable { + + public static void main(String[] args) throws Exception { + ClassFile cf = ClassFile.of(); + byte[] bytes = cf.build( + ClassDesc.of("sample.MissingVariable"), + classBuilder -> { + classBuilder.withSuperclass(ClassDesc.of("java.lang.Object")); + classBuilder.withFlags(AccessFlag.PUBLIC); + classBuilder.withField("f", + ClassDesc.of("java.lang.Object"), + fieldBuilder -> fieldBuilder.withFlags(AccessFlag.PUBLIC).with(SignatureAttribute.of(Signature.parseFrom("TA;")))); + }); + /* + package sample; + public class MissingVariable { + public A f; // undeclared type variable + } + */ + Class missing = ByteCodeLoader.load("sample.MissingVariable", bytes); + try { + Type type = missing.getField("f").getGenericType(); + throw new IllegalStateException("Expected TypeNotPresentException but got: " + type); + } catch (TypeNotPresentException e) { + if (!"A".equals(e.typeName())) { + throw new IllegalStateException("Unexpected name: " + e.typeName()); + } + } + } +}