From 3bffe223a34e8077cb1ce11f64fc34fcb0751ac7 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Wed, 7 Feb 2024 07:54:22 +0000 Subject: [PATCH] 8319463: ClassSignature should have superclass and superinterfaces as ClassTypeSig Reviewed-by: asotona --- .../java/lang/classfile/ClassSignature.java | 16 +- .../java/lang/classfile/MethodSignature.java | 4 +- .../java/lang/classfile/Signature.java | 4 +- .../classfile/impl/ClassRemapperImpl.java | 2 +- .../classfile/impl/SignaturesImpl.java | 187 +++++++++++------- test/jdk/jdk/classfile/SignaturesTest.java | 41 +++- 6 files changed, 173 insertions(+), 81 deletions(-) diff --git a/src/java.base/share/classes/java/lang/classfile/ClassSignature.java b/src/java.base/share/classes/java/lang/classfile/ClassSignature.java index 10fbdd2045f..57d899eb590 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassSignature.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassSignature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -42,10 +42,10 @@ public sealed interface ClassSignature List typeParameters(); /** {@return the instantiation of the superclass in this signature} */ - Signature.RefTypeSig superclassSignature(); + Signature.ClassTypeSig superclassSignature(); /** {@return the instantiation of the interfaces in this signature} */ - List superinterfaceSignatures(); + List superinterfaceSignatures(); /** {@return the raw signature string} */ String signatureString(); @@ -55,8 +55,8 @@ public sealed interface ClassSignature * @param superclassSignature the superclass * @param superinterfaceSignatures the interfaces */ - public static ClassSignature of(Signature.RefTypeSig superclassSignature, - Signature.RefTypeSig... superinterfaceSignatures) { + public static ClassSignature of(Signature.ClassTypeSig superclassSignature, + Signature.ClassTypeSig... superinterfaceSignatures) { return of(List.of(), superclassSignature, superinterfaceSignatures); } @@ -67,8 +67,8 @@ public sealed interface ClassSignature * @param superinterfaceSignatures the interfaces */ public static ClassSignature of(List typeParameters, - Signature.RefTypeSig superclassSignature, - Signature.RefTypeSig... superinterfaceSignatures) { + Signature.ClassTypeSig superclassSignature, + Signature.ClassTypeSig... superinterfaceSignatures) { return new SignaturesImpl.ClassSignatureImpl( requireNonNull(typeParameters), requireNonNull(superclassSignature), @@ -81,6 +81,6 @@ public sealed interface ClassSignature * @return class signature */ public static ClassSignature parseFrom(String classSignature) { - return new SignaturesImpl().parseClassSignature(requireNonNull(classSignature)); + return new SignaturesImpl(classSignature).parseClassSignature(); } } diff --git a/src/java.base/share/classes/java/lang/classfile/MethodSignature.java b/src/java.base/share/classes/java/lang/classfile/MethodSignature.java index 4c331ec5156..0d5a77faa6c 100644 --- a/src/java.base/share/classes/java/lang/classfile/MethodSignature.java +++ b/src/java.base/share/classes/java/lang/classfile/MethodSignature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -109,6 +109,6 @@ public sealed interface MethodSignature */ public static MethodSignature parseFrom(String methodSignature) { - return new SignaturesImpl().parseMethodSignature(requireNonNull(methodSignature)); + return new SignaturesImpl(methodSignature).parseMethodSignature(); } } diff --git a/src/java.base/share/classes/java/lang/classfile/Signature.java b/src/java.base/share/classes/java/lang/classfile/Signature.java index e5ed92acb83..ad2528fd713 100644 --- a/src/java.base/share/classes/java/lang/classfile/Signature.java +++ b/src/java.base/share/classes/java/lang/classfile/Signature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -51,7 +51,7 @@ public sealed interface Signature { * @return Java type signature */ public static Signature parseFrom(String javaTypeSignature) { - return new SignaturesImpl().parseSignature(requireNonNull(javaTypeSignature)); + return new SignaturesImpl(javaTypeSignature).parseSignature(); } /** diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java index 9795a2374cf..8f35b85b5de 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java @@ -291,7 +291,7 @@ public record ClassRemapperImpl(Function mapFunction) impl return ClassSignature.of(mapTypeParams(signature.typeParameters()), mapSignature(signature.superclassSignature()), signature.superinterfaceSignatures().stream() - .map(this::mapSignature).toArray(Signature.RefTypeSig[]::new)); + .map(this::mapSignature).toArray(Signature.ClassTypeSig[]::new)); } MethodSignature mapMethodSignature(MethodSignature signature) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/SignaturesImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/SignaturesImpl.java index f3efffb5b56..3447e40b258 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/SignaturesImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/SignaturesImpl.java @@ -26,6 +26,7 @@ package jdk.internal.classfile.impl; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Collections; import java.lang.classfile.ClassSignature; @@ -35,23 +36,23 @@ import java.lang.classfile.Signature.*; public final class SignaturesImpl { - public SignaturesImpl() { + public SignaturesImpl(String signature) { + this.sig = Objects.requireNonNull(signature); + this.sigp = 0; } - private String sig; + private final String sig; private int sigp; - public ClassSignature parseClassSignature(String signature) { - this.sig = signature; - sigp = 0; + public ClassSignature parseClassSignature() { try { List typeParamTypes = parseParamTypes(); - RefTypeSig superclass = referenceTypeSig(); - ArrayList superinterfaces = null; + ClassTypeSig superclass = classTypeSig(); + ArrayList superinterfaces = null; while (sigp < sig.length()) { if (superinterfaces == null) superinterfaces = new ArrayList<>(); - superinterfaces.add(referenceTypeSig()); + superinterfaces.add(classTypeSig()); } return new ClassSignatureImpl(typeParamTypes, superclass, null2Empty(superinterfaces)); } catch (IndexOutOfBoundsException e) { @@ -59,25 +60,20 @@ public final class SignaturesImpl { } } - public MethodSignature parseMethodSignature(String signature) { - this.sig = signature; - sigp = 0; + public MethodSignature parseMethodSignature() { try { List typeParamTypes = parseParamTypes(); - if (sig.charAt(sigp) != '(') throw error("Expected ( at possition %d of signature".formatted(sigp)); - sigp++; + require('('); ArrayList paramTypes = null; - while (sig.charAt(sigp) != ')') { + while (!match(')')) { if (paramTypes == null) - paramTypes = new ArrayList<>(); + paramTypes = new ArrayList<>(); paramTypes.add(typeSig()); } - sigp++; Signature returnType = typeSig(); ArrayList throwsTypes = null; while (sigp < sig.length()) { - if (sig.charAt(sigp) != '^') throw error("Expected ^ at possition %d of signature".formatted(sigp)); - sigp++; + require('^'); if (throwsTypes == null) throwsTypes = new ArrayList<>(); var t = referenceTypeSig(); @@ -92,12 +88,10 @@ public final class SignaturesImpl { } } - public Signature parseSignature(String signature) { - this.sig = signature; - sigp = 0; + public Signature parseSignature() { try { var s = typeSig(); - if (sigp == signature.length()) + if (sigp == sig.length()) return s; } catch (IndexOutOfBoundsException e) { } @@ -106,26 +100,23 @@ public final class SignaturesImpl { private List parseParamTypes() { ArrayList typeParamTypes = null; - if (sig.charAt(sigp) == '<') { - sigp++; + if (match('<')) { typeParamTypes = new ArrayList<>(); - while (sig.charAt(sigp) != '>') { - int sep = sig.indexOf(":", sigp); - String name = sig.substring(sigp, sep); + // cannot have empty <> + do { + String name = sig.substring(sigp, requireIdentifier()); RefTypeSig classBound = null; ArrayList interfaceBounds = null; - sigp = sep + 1; + require(':'); if (sig.charAt(sigp) != ':') classBound = referenceTypeSig(); - while (sig.charAt(sigp) == ':') { - sigp++; + while (match(':')) { if (interfaceBounds == null) interfaceBounds = new ArrayList<>(); interfaceBounds.add(referenceTypeSig()); } typeParamTypes.add(new TypeParamImpl(name, Optional.ofNullable(classBound), null2Empty(interfaceBounds))); - } - sigp++; + } while (!match('>')); } return null2Empty(typeParamTypes); } @@ -141,38 +132,20 @@ public final class SignaturesImpl { } private RefTypeSig referenceTypeSig() { - char c = sig.charAt(sigp++); - switch (c) { - case 'L': - StringBuilder sb = new StringBuilder(); - ArrayList argTypes = null; - Signature.ClassTypeSig t = null; - char sigch ; - do { - switch (sigch = sig.charAt(sigp++)) { - case '<' -> { - argTypes = new ArrayList<>(); - while (sig.charAt(sigp) != '>') - argTypes.add(typeArg()); - sigp++; - } - case '.',';' -> { - t = new ClassTypeSigImpl(Optional.ofNullable(t), sb.toString(), null2Empty(argTypes)); - sb.setLength(0); - argTypes = null; - } - default -> sb.append(sigch); - } - } while (sigch != ';'); - return t; - case 'T': - int sep = sig.indexOf(';', sigp); - var ty = Signature.TypeVarSig.of(sig.substring(sigp, sep)); - sigp = sep + 1; - return ty; - case '[': return ArrayTypeSig.of(typeSig()); - } - throw error("Unexpected character %c at possition %d of signature".formatted(c, sigp - 1)); + return switch (sig.charAt(sigp)) { + case 'L' -> classTypeSig(); + case 'T' -> { + sigp++; + var ty = Signature.TypeVarSig.of(sig.substring(sigp, requireIdentifier())); + require(';'); + yield ty; + } + case '[' -> { + sigp++; + yield ArrayTypeSig.of(typeSig()); + } + default -> throw unexpectedError("a type signature"); + }; } private TypeArg typeArg() { @@ -187,6 +160,82 @@ public final class SignaturesImpl { } } + private ClassTypeSig classTypeSig() { + require('L'); + Signature.ClassTypeSig t = null; + + do { + int start = sigp; + requireIdentifier(); + if (t == null) { + while (match('/')) { + requireIdentifier(); + } + } + String className = sig.substring(start, sigp); + + ArrayList argTypes; + if (match('<')) { + // cannot have empty <> + argTypes = new ArrayList<>(); + do { + argTypes.add(typeArg()); + } while (!match('>')); + } else { + argTypes = null; + } + + boolean end = match(';'); + if (end || match('.')) { + t = new ClassTypeSigImpl(Optional.ofNullable(t), className, null2Empty(argTypes)); + if (end) + return t; + } else { + throw unexpectedError(". or ;"); + } + } while (true); + } + + /** + * Tries to match a character, and moves pointer if it matches. + */ + private boolean match(char c) { + if (sigp < sig.length() && sig.charAt(sigp) == c) { + sigp++; + return true; + } + return false; + } + + /** + * Requires a character and moves past it, failing otherwise. + */ + private void require(char c) { + if (!match(c)) + throw unexpectedError(String.valueOf(c)); + } + + /** + * Requires an identifier, moving pointer to next illegal character and returning + * its position. Fails if the identifier is empty. + */ + private int requireIdentifier() { + int start = sigp; + l: + while (sigp < sig.length()) { + switch (sig.charAt(sigp)) { + case '.', ';', '[', '/', '<', '>', ':' -> { + break l; + } + } + sigp++; + } + if (start == sigp) { + throw unexpectedError("an identifier"); + } + return sigp; + } + public static record BaseTypeSigImpl(char baseType) implements Signature.BaseTypeSig { @Override @@ -271,8 +320,8 @@ public final class SignaturesImpl { return sb; } - public static record ClassSignatureImpl(List typeParameters, RefTypeSig superclassSignature, - List superinterfaceSignatures) implements ClassSignature { + public static record ClassSignatureImpl(List typeParameters, ClassTypeSig superclassSignature, + List superinterfaceSignatures) implements ClassSignature { @Override public String signatureString() { @@ -308,6 +357,12 @@ public final class SignaturesImpl { return l == null ? List.of() : Collections.unmodifiableList(l); } + private IllegalArgumentException unexpectedError(String expected) { + return error(sigp < sig.length() ? "Unexpected character %c at position %d, expected %s" + .formatted(sig.charAt(sigp), sigp, expected) + : "Unexpected end of signature at position %d, expected %s".formatted(sigp, expected)); + } + private IllegalArgumentException error(String message) { return new IllegalArgumentException("%s: %s".formatted(message, sig)); } diff --git a/test/jdk/jdk/classfile/SignaturesTest.java b/test/jdk/jdk/classfile/SignaturesTest.java index 2c64bacee41..cfd3201c95a 100644 --- a/test/jdk/jdk/classfile/SignaturesTest.java +++ b/test/jdk/jdk/classfile/SignaturesTest.java @@ -24,7 +24,7 @@ /* * @test * @summary Testing Signatures. - * @bug 8321540 + * @bug 8321540 8319463 * @run junit SignaturesTest */ import java.io.IOException; @@ -183,7 +183,7 @@ class SignaturesTest { void testClassSignatureClassDesc() throws IOException { var observerCf = ClassFile.of().parse(Path.of(System.getProperty("test.classes"), "SignaturesTest$Observer.class")); var sig = observerCf.findAttribute(Attributes.SIGNATURE).orElseThrow().asClassSignature(); - var innerSig = (ClassTypeSig) ((ClassTypeSig) sig.superclassSignature()) // ArrayList + var innerSig = (ClassTypeSig) sig.superclassSignature() // ArrayList .typeArgs().getFirst() // Outer.Inner .boundType().orElseThrow(); // assert it's exact bound assertEquals("Inner", innerSig.className(), "simple name in signature"); @@ -213,9 +213,37 @@ class SignaturesTest { LSet<+Kind<**>;>; LSet;>; ()V + Ljava/util/Optional; + Lcom/example/Outer.package/Inner<[I>; + LSample>; + LSample:Other; + LOuter<[JTT;>.[Inner; + TA:J; + LEmpty<>; + L + Lcom + Lcom/example/ + Lcom/example/Outer< + Lcom/example/Outer + Lcom/example/Outer. + Lcom/example/Outer.Inner<[I> """.lines().forEach(assertThrows(Signature::parseFrom)); } + @Test + void testGoodTypeSignatures() { + """ + Ljava/util/Optional; + Lcom/example/Outer.Inner<[I>; + LSample; + LOuter<[JTT;>.Inner; + LOuter.Inner; + """.lines().forEach(Signature::parseFrom); + } + @Test void testBadClassSignatures() { """ @@ -234,6 +262,14 @@ class SignaturesTest { >LFoo; LFoo<+>; ()V + Ljava/lang/Object;TK; + Ljava/lang/Object;[Ljava/lang/Object; + [Ljava/util/Optional<[I>; + [I + TK; + Ljava/lang/Object; + <:Ljava/lang/Object;>Ljava/lang/Object; + <>Ljava/lang/Object; """.lines().forEach(assertThrows(ClassSignature::parseFrom)); } @@ -259,6 +295,7 @@ class SignaturesTest { ()LSet<+Kind<**>;>; (LSet;>;)V ()V + (TT;I)VI """.lines().forEach(assertThrows(MethodSignature::parseFrom)); }