8319463: ClassSignature should have superclass and superinterfaces as ClassTypeSig

Reviewed-by: asotona
This commit is contained in:
Chen Liang 2024-02-07 07:54:22 +00:00 committed by Adam Sotona
parent e0d98dd301
commit 3bffe223a3
6 changed files with 173 additions and 81 deletions

View File

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -42,10 +42,10 @@ public sealed interface ClassSignature
List<Signature.TypeParam> typeParameters(); List<Signature.TypeParam> typeParameters();
/** {@return the instantiation of the superclass in this signature} */ /** {@return the instantiation of the superclass in this signature} */
Signature.RefTypeSig superclassSignature(); Signature.ClassTypeSig superclassSignature();
/** {@return the instantiation of the interfaces in this signature} */ /** {@return the instantiation of the interfaces in this signature} */
List<Signature.RefTypeSig> superinterfaceSignatures(); List<Signature.ClassTypeSig> superinterfaceSignatures();
/** {@return the raw signature string} */ /** {@return the raw signature string} */
String signatureString(); String signatureString();
@ -55,8 +55,8 @@ public sealed interface ClassSignature
* @param superclassSignature the superclass * @param superclassSignature the superclass
* @param superinterfaceSignatures the interfaces * @param superinterfaceSignatures the interfaces
*/ */
public static ClassSignature of(Signature.RefTypeSig superclassSignature, public static ClassSignature of(Signature.ClassTypeSig superclassSignature,
Signature.RefTypeSig... superinterfaceSignatures) { Signature.ClassTypeSig... superinterfaceSignatures) {
return of(List.of(), superclassSignature, superinterfaceSignatures); return of(List.of(), superclassSignature, superinterfaceSignatures);
} }
@ -67,8 +67,8 @@ public sealed interface ClassSignature
* @param superinterfaceSignatures the interfaces * @param superinterfaceSignatures the interfaces
*/ */
public static ClassSignature of(List<Signature.TypeParam> typeParameters, public static ClassSignature of(List<Signature.TypeParam> typeParameters,
Signature.RefTypeSig superclassSignature, Signature.ClassTypeSig superclassSignature,
Signature.RefTypeSig... superinterfaceSignatures) { Signature.ClassTypeSig... superinterfaceSignatures) {
return new SignaturesImpl.ClassSignatureImpl( return new SignaturesImpl.ClassSignatureImpl(
requireNonNull(typeParameters), requireNonNull(typeParameters),
requireNonNull(superclassSignature), requireNonNull(superclassSignature),
@ -81,6 +81,6 @@ public sealed interface ClassSignature
* @return class signature * @return class signature
*/ */
public static ClassSignature parseFrom(String classSignature) { public static ClassSignature parseFrom(String classSignature) {
return new SignaturesImpl().parseClassSignature(requireNonNull(classSignature)); return new SignaturesImpl(classSignature).parseClassSignature();
} }
} }

View File

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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) { public static MethodSignature parseFrom(String methodSignature) {
return new SignaturesImpl().parseMethodSignature(requireNonNull(methodSignature)); return new SignaturesImpl(methodSignature).parseMethodSignature();
} }
} }

View File

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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 * @return Java type signature
*/ */
public static Signature parseFrom(String javaTypeSignature) { public static Signature parseFrom(String javaTypeSignature) {
return new SignaturesImpl().parseSignature(requireNonNull(javaTypeSignature)); return new SignaturesImpl(javaTypeSignature).parseSignature();
} }
/** /**

View File

@ -291,7 +291,7 @@ public record ClassRemapperImpl(Function<ClassDesc, ClassDesc> mapFunction) impl
return ClassSignature.of(mapTypeParams(signature.typeParameters()), return ClassSignature.of(mapTypeParams(signature.typeParameters()),
mapSignature(signature.superclassSignature()), mapSignature(signature.superclassSignature()),
signature.superinterfaceSignatures().stream() signature.superinterfaceSignatures().stream()
.map(this::mapSignature).toArray(Signature.RefTypeSig[]::new)); .map(this::mapSignature).toArray(Signature.ClassTypeSig[]::new));
} }
MethodSignature mapMethodSignature(MethodSignature signature) { MethodSignature mapMethodSignature(MethodSignature signature) {

View File

@ -26,6 +26,7 @@ package jdk.internal.classfile.impl;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Collections; import java.util.Collections;
import java.lang.classfile.ClassSignature; import java.lang.classfile.ClassSignature;
@ -35,23 +36,23 @@ import java.lang.classfile.Signature.*;
public final class SignaturesImpl { 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; private int sigp;
public ClassSignature parseClassSignature(String signature) { public ClassSignature parseClassSignature() {
this.sig = signature;
sigp = 0;
try { try {
List<TypeParam> typeParamTypes = parseParamTypes(); List<TypeParam> typeParamTypes = parseParamTypes();
RefTypeSig superclass = referenceTypeSig(); ClassTypeSig superclass = classTypeSig();
ArrayList<RefTypeSig> superinterfaces = null; ArrayList<ClassTypeSig> superinterfaces = null;
while (sigp < sig.length()) { while (sigp < sig.length()) {
if (superinterfaces == null) if (superinterfaces == null)
superinterfaces = new ArrayList<>(); superinterfaces = new ArrayList<>();
superinterfaces.add(referenceTypeSig()); superinterfaces.add(classTypeSig());
} }
return new ClassSignatureImpl(typeParamTypes, superclass, null2Empty(superinterfaces)); return new ClassSignatureImpl(typeParamTypes, superclass, null2Empty(superinterfaces));
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
@ -59,25 +60,20 @@ public final class SignaturesImpl {
} }
} }
public MethodSignature parseMethodSignature(String signature) { public MethodSignature parseMethodSignature() {
this.sig = signature;
sigp = 0;
try { try {
List<TypeParam> typeParamTypes = parseParamTypes(); List<TypeParam> typeParamTypes = parseParamTypes();
if (sig.charAt(sigp) != '(') throw error("Expected ( at possition %d of signature".formatted(sigp)); require('(');
sigp++;
ArrayList<Signature> paramTypes = null; ArrayList<Signature> paramTypes = null;
while (sig.charAt(sigp) != ')') { while (!match(')')) {
if (paramTypes == null) if (paramTypes == null)
paramTypes = new ArrayList<>(); paramTypes = new ArrayList<>();
paramTypes.add(typeSig()); paramTypes.add(typeSig());
} }
sigp++;
Signature returnType = typeSig(); Signature returnType = typeSig();
ArrayList<ThrowableSig> throwsTypes = null; ArrayList<ThrowableSig> throwsTypes = null;
while (sigp < sig.length()) { while (sigp < sig.length()) {
if (sig.charAt(sigp) != '^') throw error("Expected ^ at possition %d of signature".formatted(sigp)); require('^');
sigp++;
if (throwsTypes == null) if (throwsTypes == null)
throwsTypes = new ArrayList<>(); throwsTypes = new ArrayList<>();
var t = referenceTypeSig(); var t = referenceTypeSig();
@ -92,12 +88,10 @@ public final class SignaturesImpl {
} }
} }
public Signature parseSignature(String signature) { public Signature parseSignature() {
this.sig = signature;
sigp = 0;
try { try {
var s = typeSig(); var s = typeSig();
if (sigp == signature.length()) if (sigp == sig.length())
return s; return s;
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
} }
@ -106,26 +100,23 @@ public final class SignaturesImpl {
private List<TypeParam> parseParamTypes() { private List<TypeParam> parseParamTypes() {
ArrayList<TypeParam> typeParamTypes = null; ArrayList<TypeParam> typeParamTypes = null;
if (sig.charAt(sigp) == '<') { if (match('<')) {
sigp++;
typeParamTypes = new ArrayList<>(); typeParamTypes = new ArrayList<>();
while (sig.charAt(sigp) != '>') { // cannot have empty <>
int sep = sig.indexOf(":", sigp); do {
String name = sig.substring(sigp, sep); String name = sig.substring(sigp, requireIdentifier());
RefTypeSig classBound = null; RefTypeSig classBound = null;
ArrayList<RefTypeSig> interfaceBounds = null; ArrayList<RefTypeSig> interfaceBounds = null;
sigp = sep + 1; require(':');
if (sig.charAt(sigp) != ':') if (sig.charAt(sigp) != ':')
classBound = referenceTypeSig(); classBound = referenceTypeSig();
while (sig.charAt(sigp) == ':') { while (match(':')) {
sigp++;
if (interfaceBounds == null) if (interfaceBounds == null)
interfaceBounds = new ArrayList<>(); interfaceBounds = new ArrayList<>();
interfaceBounds.add(referenceTypeSig()); interfaceBounds.add(referenceTypeSig());
} }
typeParamTypes.add(new TypeParamImpl(name, Optional.ofNullable(classBound), null2Empty(interfaceBounds))); typeParamTypes.add(new TypeParamImpl(name, Optional.ofNullable(classBound), null2Empty(interfaceBounds)));
} } while (!match('>'));
sigp++;
} }
return null2Empty(typeParamTypes); return null2Empty(typeParamTypes);
} }
@ -141,38 +132,20 @@ public final class SignaturesImpl {
} }
private RefTypeSig referenceTypeSig() { private RefTypeSig referenceTypeSig() {
char c = sig.charAt(sigp++); return switch (sig.charAt(sigp)) {
switch (c) { case 'L' -> classTypeSig();
case 'L': case 'T' -> {
StringBuilder sb = new StringBuilder(); sigp++;
ArrayList<TypeArg> argTypes = null; var ty = Signature.TypeVarSig.of(sig.substring(sigp, requireIdentifier()));
Signature.ClassTypeSig t = null; require(';');
char sigch ; yield ty;
do { }
switch (sigch = sig.charAt(sigp++)) { case '[' -> {
case '<' -> { sigp++;
argTypes = new ArrayList<>(); yield ArrayTypeSig.of(typeSig());
while (sig.charAt(sigp) != '>') }
argTypes.add(typeArg()); default -> throw unexpectedError("a type signature");
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));
} }
private TypeArg typeArg() { 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<TypeArg> 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 { public static record BaseTypeSigImpl(char baseType) implements Signature.BaseTypeSig {
@Override @Override
@ -271,8 +320,8 @@ public final class SignaturesImpl {
return sb; return sb;
} }
public static record ClassSignatureImpl(List<TypeParam> typeParameters, RefTypeSig superclassSignature, public static record ClassSignatureImpl(List<TypeParam> typeParameters, ClassTypeSig superclassSignature,
List<RefTypeSig> superinterfaceSignatures) implements ClassSignature { List<ClassTypeSig> superinterfaceSignatures) implements ClassSignature {
@Override @Override
public String signatureString() { public String signatureString() {
@ -308,6 +357,12 @@ public final class SignaturesImpl {
return l == null ? List.of() : Collections.unmodifiableList(l); 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) { private IllegalArgumentException error(String message) {
return new IllegalArgumentException("%s: %s".formatted(message, sig)); return new IllegalArgumentException("%s: %s".formatted(message, sig));
} }

View File

@ -24,7 +24,7 @@
/* /*
* @test * @test
* @summary Testing Signatures. * @summary Testing Signatures.
* @bug 8321540 * @bug 8321540 8319463
* @run junit SignaturesTest * @run junit SignaturesTest
*/ */
import java.io.IOException; import java.io.IOException;
@ -183,7 +183,7 @@ class SignaturesTest {
void testClassSignatureClassDesc() throws IOException { void testClassSignatureClassDesc() throws IOException {
var observerCf = ClassFile.of().parse(Path.of(System.getProperty("test.classes"), "SignaturesTest$Observer.class")); var observerCf = ClassFile.of().parse(Path.of(System.getProperty("test.classes"), "SignaturesTest$Observer.class"));
var sig = observerCf.findAttribute(Attributes.SIGNATURE).orElseThrow().asClassSignature(); var sig = observerCf.findAttribute(Attributes.SIGNATURE).orElseThrow().asClassSignature();
var innerSig = (ClassTypeSig) ((ClassTypeSig) sig.superclassSignature()) // ArrayList var innerSig = (ClassTypeSig) sig.superclassSignature() // ArrayList
.typeArgs().getFirst() // Outer<String>.Inner<Long> .typeArgs().getFirst() // Outer<String>.Inner<Long>
.boundType().orElseThrow(); // assert it's exact bound .boundType().orElseThrow(); // assert it's exact bound
assertEquals("Inner", innerSig.className(), "simple name in signature"); assertEquals("Inner", innerSig.className(), "simple name in signature");
@ -213,9 +213,37 @@ class SignaturesTest {
LSet<+Kind<**>;>; LSet<+Kind<**>;>;
LSet<?Kind<*>;>; LSet<?Kind<*>;>;
()V ()V
Ljava/util/Opt<Ljava/lang/Integer;>ional;
Lcom/example/Outer<Ljava/lang/String;>.package/Inner<[I>;
LSample>;
LSample:Other;
LOuter<[JTT;>.[Inner;
TA:J;
LEmpty<>;
L
Lcom
Lcom/example/
Lcom/example/Outer<
Lcom/example/Outer<Ljava/
Lcom/example/Outer<Ljava/lang/String
Lcom/example/Outer<Ljava/lang/String;
Lcom/example/Outer<Ljava/lang/String;>
Lcom/example/Outer<Ljava/lang/String;>.
Lcom/example/Outer<Ljava/lang/String;>.Inner<[I>
""".lines().forEach(assertThrows(Signature::parseFrom)); """.lines().forEach(assertThrows(Signature::parseFrom));
} }
@Test
void testGoodTypeSignatures() {
"""
Ljava/util/Optional<Ljava/lang/Integer;>;
Lcom/example/Outer<Ljava/lang/Integer;>.Inner<[I>;
LSample;
LOuter<[JTT;>.Inner;
LOuter.Inner;
""".lines().forEach(Signature::parseFrom);
}
@Test @Test
void testBadClassSignatures() { void testBadClassSignatures() {
""" """
@ -234,6 +262,14 @@ class SignaturesTest {
<K:LObject;>>LFoo<TK;>; <K:LObject;>>LFoo<TK;>;
<K:LObject;>LFoo<+>; <K:LObject;>LFoo<+>;
()V ()V
<K:Ljava/lang/Object;>Ljava/lang/Object;TK;
Ljava/lang/Object;[Ljava/lang/Object;
[Ljava/util/Optional<[I>;
[I
<K:Ljava/lang/Object;>TK;
<K;Q:Ljava/lang/Object;>Ljava/lang/Object;
<:Ljava/lang/Object;>Ljava/lang/Object;
<>Ljava/lang/Object;
""".lines().forEach(assertThrows(ClassSignature::parseFrom)); """.lines().forEach(assertThrows(ClassSignature::parseFrom));
} }
@ -259,6 +295,7 @@ class SignaturesTest {
()LSet<+Kind<**>;>; ()LSet<+Kind<**>;>;
(LSet<?Kind<*>;>;)V (LSet<?Kind<*>;>;)V
<T::LA>()V <T::LA>()V
(TT;I)VI
""".lines().forEach(assertThrows(MethodSignature::parseFrom)); """.lines().forEach(assertThrows(MethodSignature::parseFrom));
} }