mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-07 00:48:38 +00:00
8342206: Convenience method to check if a constant pool entry matches nominal descriptors
Reviewed-by: asotona
This commit is contained in:
parent
7bde2bb571
commit
e4cb49fc85
@ -104,8 +104,22 @@ public sealed interface ClassEntry
|
||||
* returned descriptor is never {@linkplain ClassDesc#isPrimitive()
|
||||
* primitive}.
|
||||
*
|
||||
* @apiNote
|
||||
* If only symbol equivalence is desired, {@link #matches(ClassDesc)
|
||||
* matches} should be used. It requires reduced parsing and can
|
||||
* improve {@code class} file reading performance.
|
||||
*
|
||||
* @see ConstantPoolBuilder#classEntry(ClassDesc)
|
||||
* ConstantPoolBuilder::classEntry(ClassDesc)
|
||||
*/
|
||||
ClassDesc asSymbol();
|
||||
|
||||
/**
|
||||
* {@return whether this entry describes the given reference type} Returns
|
||||
* {@code false} if {@code desc} is primitive.
|
||||
*
|
||||
* @param desc the reference type
|
||||
* @since 25
|
||||
*/
|
||||
boolean matches(ClassDesc desc);
|
||||
}
|
||||
|
||||
@ -70,6 +70,19 @@ public sealed interface MethodTypeEntry
|
||||
/**
|
||||
* {@return a symbolic descriptor for the {@linkplain #descriptor() method
|
||||
* type}}
|
||||
*
|
||||
* @apiNote
|
||||
* If only symbol equivalence is desired, {@link #matches(MethodTypeDesc)
|
||||
* matches} should be used. It requires reduced parsing and can
|
||||
* improve {@code class} file reading performance.
|
||||
*/
|
||||
MethodTypeDesc asSymbol();
|
||||
|
||||
/**
|
||||
* {@return whether this entry describes the given method type}
|
||||
*
|
||||
* @param desc the method type descriptor
|
||||
* @since 25
|
||||
*/
|
||||
boolean matches(MethodTypeDesc desc);
|
||||
}
|
||||
|
||||
@ -55,6 +55,19 @@ public sealed interface ModuleEntry extends PoolEntry
|
||||
|
||||
/**
|
||||
* {@return a symbolic descriptor for the {@linkplain #name() module name}}
|
||||
*
|
||||
* @apiNote
|
||||
* If only symbol equivalence is desired, {@link #matches(ModuleDesc)
|
||||
* matches} should be used. It requires reduced parsing and can
|
||||
* improve {@code class} file reading performance.
|
||||
*/
|
||||
ModuleDesc asSymbol();
|
||||
|
||||
/**
|
||||
* {@return whether this entry describes the given module}
|
||||
*
|
||||
* @param desc the module descriptor
|
||||
* @since 25
|
||||
*/
|
||||
boolean matches(ModuleDesc desc);
|
||||
}
|
||||
|
||||
@ -58,6 +58,19 @@ public sealed interface PackageEntry extends PoolEntry
|
||||
|
||||
/**
|
||||
* {@return a symbolic descriptor for the {@linkplain #name() package name}}
|
||||
*
|
||||
* @apiNote
|
||||
* If only symbol equivalence is desired, {@link #matches(PackageDesc)
|
||||
* matches} should be used. It requires reduced parsing and can
|
||||
* improve {@code class} file reading performance.
|
||||
*/
|
||||
PackageDesc asSymbol();
|
||||
|
||||
/**
|
||||
* {@return whether this entry describes the given package}
|
||||
*
|
||||
* @param desc the package descriptor
|
||||
* @since 25
|
||||
*/
|
||||
boolean matches(PackageDesc desc);
|
||||
}
|
||||
|
||||
@ -56,7 +56,22 @@ public sealed interface StringEntry
|
||||
/**
|
||||
* {@return the string value for this entry}
|
||||
*
|
||||
* @apiNote
|
||||
* A {@code Utf8Entry} can be used directly as a {@link CharSequence} if
|
||||
* {@code String} functionalities are not strictly desired. If only string
|
||||
* equivalence is desired, {@link #equalsString(String) equalsString} should
|
||||
* be used. Reduction of string processing can significantly improve {@code
|
||||
* class} file reading performance.
|
||||
*
|
||||
* @see ConstantPoolBuilder#stringEntry(String)
|
||||
*/
|
||||
String stringValue();
|
||||
|
||||
/**
|
||||
* {@return whether this entry describes the same string as the provided string}
|
||||
*
|
||||
* @param value the string to compare to
|
||||
* @since 25
|
||||
*/
|
||||
boolean equalsString(String value);
|
||||
}
|
||||
|
||||
@ -84,4 +84,22 @@ public sealed interface Utf8Entry
|
||||
* @param s the string to compare to
|
||||
*/
|
||||
boolean equalsString(String s);
|
||||
|
||||
/**
|
||||
* {@return whether this entry describes the descriptor string of this
|
||||
* field type}
|
||||
*
|
||||
* @param desc the field type
|
||||
* @since 25
|
||||
*/
|
||||
boolean isFieldType(ClassDesc desc);
|
||||
|
||||
/**
|
||||
* {@return whether this entry describes the descriptor string of this
|
||||
* method type}
|
||||
*
|
||||
* @param desc the method type
|
||||
* @since 25
|
||||
*/
|
||||
boolean isMethodType(MethodTypeDesc desc);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 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
|
||||
@ -32,6 +32,8 @@ import java.util.Arrays;
|
||||
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.constant.ClassOrInterfaceDescImpl;
|
||||
import jdk.internal.constant.PrimitiveClassDescImpl;
|
||||
import jdk.internal.util.ArraysSupport;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
@ -73,11 +75,6 @@ public abstract sealed class AbstractPoolEntry {
|
||||
return hash1(PoolEntry.TAG_CLASS, descriptorHash);
|
||||
}
|
||||
|
||||
static boolean isArrayDescriptor(Utf8EntryImpl cs) {
|
||||
// Do not throw out-of-bounds for empty strings
|
||||
return !cs.isEmpty() && cs.charAt(0) == '[';
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends PoolEntry> T maybeClone(ConstantPoolBuilder cp, T entry) {
|
||||
if (cp.canWriteDirect(entry.constantPool()))
|
||||
@ -438,6 +435,79 @@ public abstract sealed class AbstractPoolEntry {
|
||||
typeSym = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFieldType(ClassDesc desc) {
|
||||
var sym = typeSym;
|
||||
if (sym != null) {
|
||||
return sym instanceof ClassDesc cd && cd.equals(desc);
|
||||
}
|
||||
|
||||
// In parsing, Utf8Entry is not even inflated by this point
|
||||
// We can operate on the raw byte arrays, as all ascii are compatible
|
||||
var ret = state == State.RAW
|
||||
? rawEqualsSym(desc)
|
||||
: equalsString(desc.descriptorString());
|
||||
if (ret)
|
||||
this.typeSym = desc;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private boolean rawEqualsSym(ClassDesc desc) {
|
||||
int len = rawLen;
|
||||
if (len < 1) {
|
||||
return false;
|
||||
}
|
||||
int c = rawBytes[offset];
|
||||
if (len == 1) {
|
||||
return desc instanceof PrimitiveClassDescImpl pd && pd.wrapper().basicTypeChar() == c;
|
||||
} else if (c == 'L') {
|
||||
return desc.isClassOrInterface() && equalsString(desc.descriptorString());
|
||||
} else if (c == '[') {
|
||||
return desc.isArray() && equalsString(desc.descriptorString());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boolean mayBeArrayDescriptor() {
|
||||
if (state == State.RAW) {
|
||||
return rawLen > 0 && rawBytes[offset] == '[';
|
||||
} else {
|
||||
return charLen > 0 && charAt(0) == '[';
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMethodType(MethodTypeDesc desc) {
|
||||
var sym = typeSym;
|
||||
if (sym != null) {
|
||||
return sym instanceof MethodTypeDesc mtd && mtd.equals(desc);
|
||||
}
|
||||
|
||||
// In parsing, Utf8Entry is not even inflated by this point
|
||||
// We can operate on the raw byte arrays, as all ascii are compatible
|
||||
var ret = state == State.RAW
|
||||
? rawEqualsSym(desc)
|
||||
: equalsString(desc.descriptorString());
|
||||
if (ret)
|
||||
this.typeSym = desc;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private boolean rawEqualsSym(MethodTypeDesc desc) {
|
||||
if (rawLen < 3) {
|
||||
return false;
|
||||
}
|
||||
var bytes = rawBytes;
|
||||
int index = offset;
|
||||
int c = bytes[index] | (bytes[index + 1] << Byte.SIZE);
|
||||
if ((desc.parameterCount() == 0) != (c == ('(' | (')' << Byte.SIZE)))) {
|
||||
// heuristic - avoid inflation for no-arg status mismatch
|
||||
return false;
|
||||
}
|
||||
return (c & 0xFF) == '(' && equalsString(desc.descriptorString());
|
||||
}
|
||||
}
|
||||
|
||||
abstract static sealed class AbstractRefEntry<T extends PoolEntry> extends AbstractPoolEntry {
|
||||
@ -538,7 +608,7 @@ public abstract sealed class AbstractPoolEntry {
|
||||
return sym;
|
||||
}
|
||||
|
||||
if (isArrayDescriptor(ref1)) {
|
||||
if (ref1.mayBeArrayDescriptor()) {
|
||||
sym = ref1.fieldTypeSymbol(); // array, symbol already available
|
||||
} else {
|
||||
sym = ClassDesc.ofInternalName(asInternalName()); // class or interface
|
||||
@ -546,6 +616,28 @@ public abstract sealed class AbstractPoolEntry {
|
||||
return this.sym = sym;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(ClassDesc desc) {
|
||||
var sym = this.sym;
|
||||
if (sym != null) {
|
||||
return sym.equals(desc);
|
||||
}
|
||||
|
||||
var ret = rawEqualsSymbol(desc);
|
||||
if (ret)
|
||||
this.sym = desc;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private boolean rawEqualsSymbol(ClassDesc desc) {
|
||||
if (ref1.mayBeArrayDescriptor()) {
|
||||
return desc.isArray() && ref1.isFieldType(desc);
|
||||
} else {
|
||||
return desc instanceof ClassOrInterfaceDescImpl coid
|
||||
&& ref1.equalsString(coid.internalName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
@ -571,7 +663,7 @@ public abstract sealed class AbstractPoolEntry {
|
||||
if (hash != 0)
|
||||
return hash;
|
||||
|
||||
return this.hash = hashClassFromUtf8(isArrayDescriptor(ref1), ref1);
|
||||
return this.hash = hashClassFromUtf8(ref1.mayBeArrayDescriptor(), ref1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -596,6 +688,11 @@ public abstract sealed class AbstractPoolEntry {
|
||||
return PackageDesc.ofInternalName(asInternalName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(PackageDesc desc) {
|
||||
return ref1.equalsString(desc.internalName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
@ -627,6 +724,11 @@ public abstract sealed class AbstractPoolEntry {
|
||||
return ModuleDesc.of(asInternalName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(ModuleDesc desc) {
|
||||
return ref1.equalsString(desc.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
@ -983,6 +1085,11 @@ public abstract sealed class AbstractPoolEntry {
|
||||
return ref1.methodTypeSymbol();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(MethodTypeDesc desc) {
|
||||
return ref1.isMethodType(desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
@ -1016,6 +1123,11 @@ public abstract sealed class AbstractPoolEntry {
|
||||
return ref1.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalsString(String value) {
|
||||
return ref1.equalsString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConstantDesc constantValue() {
|
||||
return stringValue();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 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
|
||||
@ -497,7 +497,7 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
|
||||
@Override
|
||||
public AbstractPoolEntry.ClassEntryImpl classEntry(Utf8Entry nameEntry) {
|
||||
var ne = maybeCloneUtf8Entry(nameEntry);
|
||||
return classEntry(ne, AbstractPoolEntry.isArrayDescriptor(ne));
|
||||
return classEntry(ne, ne.mayBeArrayDescriptor());
|
||||
}
|
||||
|
||||
AbstractPoolEntry.ClassEntryImpl classEntry(AbstractPoolEntry.Utf8EntryImpl ne, boolean isArray) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2024, 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
|
||||
@ -30,22 +30,25 @@
|
||||
* @run junit ConstantDescSymbolsTest
|
||||
*/
|
||||
|
||||
import java.lang.classfile.constantpool.ConstantPoolBuilder;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.DynamicConstantDesc;
|
||||
import java.lang.constant.MethodHandleDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.classfile.constantpool.*;
|
||||
import java.lang.constant.*;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.classfile.impl.AbstractPoolEntry;
|
||||
import jdk.internal.classfile.impl.Util;
|
||||
import jdk.internal.constant.ConstantUtils;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
|
||||
@ -88,7 +91,8 @@ final class ConstantDescSymbolsTest {
|
||||
// Tests that condy symbols with non-static-method bootstraps are using the right lookup descriptor.
|
||||
@Test
|
||||
void testConstantDynamicNonStaticBootstrapMethod() throws Throwable {
|
||||
record CondyBoot(MethodHandles.Lookup lookup, String name, Class<?> type) {}
|
||||
record CondyBoot(MethodHandles.Lookup lookup, String name, Class<?> type) {
|
||||
}
|
||||
var bootClass = CondyBoot.class.describeConstable().orElseThrow();
|
||||
var bootMhDesc = MethodHandleDesc.ofConstructor(bootClass, CD_MethodHandles_Lookup, CD_String, CD_Class);
|
||||
var condyDesc = DynamicConstantDesc.of(bootMhDesc);
|
||||
@ -165,4 +169,216 @@ final class ConstantDescSymbolsTest {
|
||||
assertSame(utf8, ce.name(), "Reusing existing utf8 entry");
|
||||
assertEquals(cd, ce.asSymbol(), "Symbol propagation on create with utf8");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("equalityCases")
|
||||
<T, P extends PoolEntry> void testAsSymbolEquality(ValidSymbolCase<T, P> validSymbolCase, String entryState, P p) {
|
||||
var asSymbol = validSymbolCase.translator.extractor.apply(p);
|
||||
assertEquals(validSymbolCase.sym, asSymbol, "asSym vs sym");
|
||||
assertEquals(validSymbolCase.other, asSymbol, "asSym vs other sym");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("equalityCases")
|
||||
<T, P extends PoolEntry> void testMatchesOriginalEquality(ValidSymbolCase<T, P> validSymbolCase, String entryState, P p) {
|
||||
assertTrue(validSymbolCase.translator.tester.test(p, validSymbolCase.sym));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("equalityCases")
|
||||
<T, P extends PoolEntry> void testMatchesEquivalentEquality(ValidSymbolCase<T, P> validSymbolCase, String entryState, P p) {
|
||||
assertTrue(validSymbolCase.translator.tester.test(p, validSymbolCase.other));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("inequalityCases")
|
||||
<T, P extends PoolEntry> void testAsSymbolInequality(ValidSymbolCase<T, P> validSymbolCase, String stateName, P p) {
|
||||
var asSymbol = validSymbolCase.translator.extractor.apply(p);
|
||||
assertEquals(validSymbolCase.sym, asSymbol, "asSymbol vs original");
|
||||
assertNotEquals(validSymbolCase.other, asSymbol, "asSymbol vs inequal");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("inequalityCases")
|
||||
<T, P extends PoolEntry> void testMatchesOriginalInequality(ValidSymbolCase<T, P> validSymbolCase, String stateName, P p) {
|
||||
assertTrue(validSymbolCase.translator.tester.test(p, validSymbolCase.sym));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("inequalityCases")
|
||||
<T, P extends PoolEntry> void testMatchesNonEquivalentInequality(ValidSymbolCase<T, P> validSymbolCase, String stateName, P p) {
|
||||
assertFalse(validSymbolCase.translator.tester.test(p, validSymbolCase.other));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("malformedCases")
|
||||
<T, P extends PoolEntry> void testAsSymbolMalformed(InvalidSymbolCase<T, P> baseCase, String entryState, P p) {
|
||||
assertThrows(IllegalArgumentException.class, () -> baseCase.translator.extractor.apply(p));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("malformedCases")
|
||||
<T, P extends PoolEntry> void testMatchesMalformed(InvalidSymbolCase<T, P> baseCase, String entryState, P p) {
|
||||
assertFalse(baseCase.translator.tester.test(p, baseCase.target));
|
||||
}
|
||||
|
||||
// Support for complex pool entry creation with different inflation states.
|
||||
// Inflation states include:
|
||||
// - bound/unbound,
|
||||
// - asSymbol()
|
||||
// - matches() resulting in match
|
||||
// - matches() resulting in mismatch
|
||||
|
||||
// a pool entry, suitable for testing lazy behaviors and has descriptive name
|
||||
record StatefulPoolEntry<P>(String desc, Supplier<P> factory) {
|
||||
}
|
||||
|
||||
// Test pool entry <-> nominal descriptor, also the equals methods
|
||||
record SymbolicTranslator<T, P extends PoolEntry>(String name, BiFunction<ConstantPoolBuilder, T, P> writer,
|
||||
BiPredicate<P, T> tester, Function<P, T> extractor) {
|
||||
private P createUnboundEntry(T symbol) {
|
||||
ConstantPoolBuilder cpb = ConstantPoolBuilder.of(); // Temp pool does not support some entries
|
||||
return writer.apply(cpb, symbol);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private P toBoundEntry(P unboundEntry) {
|
||||
ConstantPoolBuilder cpb = (ConstantPoolBuilder) unboundEntry.constantPool();
|
||||
int index = unboundEntry.index();
|
||||
var bytes = ClassFile.of().build(cpb.classEntry(ClassDesc.of("Test")), cpb, _ -> {
|
||||
});
|
||||
return (P) ClassFile.of().parse(bytes).constantPool().entryByIndex(index);
|
||||
}
|
||||
|
||||
// Spawn entries to test from a nominal descriptor
|
||||
public Stream<StatefulPoolEntry<P>> entriesSpawner(T original) {
|
||||
return spawnBounded(() -> this.createUnboundEntry(original));
|
||||
}
|
||||
|
||||
// Spawn additional bound entries to test from an initial unbound entry
|
||||
public Stream<StatefulPoolEntry<P>> spawnBounded(Supplier<P> original) {
|
||||
return Stream.of(new StatefulPoolEntry<>(original.get().toString(), original))
|
||||
.mapMulti((s, sink) -> {
|
||||
sink.accept(s); // unbound
|
||||
sink.accept(new StatefulPoolEntry<>(s.desc + "+lazy", () -> toBoundEntry(s.factory.get()))); // bound
|
||||
});
|
||||
}
|
||||
|
||||
// Add extra stage of entry spawn to "inflate" entries via positive/negative tests
|
||||
public StatefulPoolEntry<P> inflateByMatching(StatefulPoolEntry<P> last, T arg, String msg) {
|
||||
return new StatefulPoolEntry<>("+matches(" + msg + ")", () -> {
|
||||
var ret = last.factory.get();
|
||||
tester.test(ret, arg);
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
|
||||
// Add extra stage of entry spawn to "inflate" entries via descriptor computation
|
||||
// This should not be used if the pool entry may be invalid (i.e. throws IAE)
|
||||
public StatefulPoolEntry<P> inflateByComputeSymbol(StatefulPoolEntry<P> last) {
|
||||
return new StatefulPoolEntry<>(last.desc + "+asSymbol()", () -> {
|
||||
var ret = last.factory.get();
|
||||
extractor.apply(ret);
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name; // don't include lambda garbage in failure reports
|
||||
}
|
||||
}
|
||||
|
||||
// A case testing valid symbol sym; other is another symbol that may match or mismatch.
|
||||
record ValidSymbolCase<T, P extends PoolEntry>(SymbolicTranslator<T, P> translator, T sym, T other) {
|
||||
}
|
||||
|
||||
// Current supported conversions
|
||||
static final SymbolicTranslator<String, Utf8Entry> UTF8_STRING_TRANSLATOR = new SymbolicTranslator<>("Utf8", ConstantPoolBuilder::utf8Entry, Utf8Entry::equalsString, Utf8Entry::stringValue);
|
||||
static final SymbolicTranslator<ClassDesc, Utf8Entry> UTF8_CLASS_TRANSLATOR = new SymbolicTranslator<>("FieldTypeUtf8", ConstantPoolBuilder::utf8Entry, Utf8Entry::isFieldType, Util::fieldTypeSymbol);
|
||||
static final SymbolicTranslator<MethodTypeDesc, Utf8Entry> UTF8_METHOD_TYPE_TRANSLATOR = new SymbolicTranslator<>("MethodTypeUtf8", ConstantPoolBuilder::utf8Entry, Utf8Entry::isMethodType, Util::methodTypeSymbol);
|
||||
static final SymbolicTranslator<ClassDesc, ClassEntry> CLASS_ENTRY_TRANSLATOR = new SymbolicTranslator<>("ClassEntry", ConstantPoolBuilder::classEntry, ClassEntry::matches, ClassEntry::asSymbol);
|
||||
static final SymbolicTranslator<MethodTypeDesc, MethodTypeEntry> METHOD_TYPE_ENTRY_TRANSLATOR = new SymbolicTranslator<>("MethodTypeEntry", ConstantPoolBuilder::methodTypeEntry, MethodTypeEntry::matches, MethodTypeEntry::asSymbol);
|
||||
static final SymbolicTranslator<String, StringEntry> STRING_ENTRY_TRANSLATOR = new SymbolicTranslator<>("StringEntry", ConstantPoolBuilder::stringEntry, StringEntry::equalsString, StringEntry::stringValue);
|
||||
static final SymbolicTranslator<PackageDesc, PackageEntry> PACKAGE_ENTRY_TRANSLATOR = new SymbolicTranslator<>("PackageEntry", ConstantPoolBuilder::packageEntry, PackageEntry::matches, PackageEntry::asSymbol);
|
||||
static final SymbolicTranslator<ModuleDesc, ModuleEntry> MODULE_ENTRY_TRANSLATOR = new SymbolicTranslator<>("ModuleEntry", ConstantPoolBuilder::moduleEntry, ModuleEntry::matches, ModuleEntry::asSymbol);
|
||||
|
||||
// Create arguments of tuple (ValidSymbolCase, entryState, PoolEntry) to verify symbolic behavior of pool entries
|
||||
// with particular inflation states
|
||||
static <T, P extends PoolEntry> void specializeInflation(ValidSymbolCase<T, P> validSymbolCase, Consumer<Arguments> callArgs) {
|
||||
validSymbolCase.translator.entriesSpawner(validSymbolCase.sym)
|
||||
.<StatefulPoolEntry<P>>mapMulti((src, sink) -> {
|
||||
sink.accept(src);
|
||||
sink.accept(validSymbolCase.translator.inflateByMatching(src, validSymbolCase.sym, "same symbol"));
|
||||
sink.accept(validSymbolCase.translator.inflateByMatching(src, validSymbolCase.other, "another symbol"));
|
||||
sink.accept(validSymbolCase.translator.inflateByComputeSymbol(src));
|
||||
})
|
||||
.forEach(stateful -> callArgs.accept(Arguments.of(validSymbolCase, stateful.desc, stateful.factory.get())));
|
||||
}
|
||||
|
||||
static Stream<Arguments> equalityCases() {
|
||||
return Stream.of(
|
||||
new ValidSymbolCase<>(CLASS_ENTRY_TRANSLATOR, CD_Object, ClassDesc.ofInternalName("java/lang/Object")), // class or interface
|
||||
new ValidSymbolCase<>(CLASS_ENTRY_TRANSLATOR, CD_Object.arrayType(), ClassDesc.ofDescriptor("[Ljava/lang/Object;")), // array
|
||||
new ValidSymbolCase<>(UTF8_CLASS_TRANSLATOR, CD_int, ClassDesc.ofDescriptor("I")), // primitive
|
||||
new ValidSymbolCase<>(UTF8_CLASS_TRANSLATOR, CD_Object, ClassDesc.ofInternalName("java/lang/Object")), // class or interface
|
||||
new ValidSymbolCase<>(UTF8_CLASS_TRANSLATOR, CD_Object.arrayType(), ClassDesc.ofDescriptor("[Ljava/lang/Object;")), // array
|
||||
new ValidSymbolCase<>(UTF8_STRING_TRANSLATOR, "Ab\u0000c", "Ab\u0000c"),
|
||||
new ValidSymbolCase<>(UTF8_METHOD_TYPE_TRANSLATOR, MTD_void, MethodTypeDesc.ofDescriptor("()V")),
|
||||
new ValidSymbolCase<>(UTF8_METHOD_TYPE_TRANSLATOR, MethodTypeDesc.of(CD_int, CD_Long), MethodTypeDesc.ofDescriptor("(Ljava/lang/Long;)I")),
|
||||
new ValidSymbolCase<>(METHOD_TYPE_ENTRY_TRANSLATOR, MethodTypeDesc.of(CD_Object), MethodTypeDesc.ofDescriptor("()Ljava/lang/Object;")),
|
||||
new ValidSymbolCase<>(STRING_ENTRY_TRANSLATOR, "Ape", new String("Ape".getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8)),
|
||||
new ValidSymbolCase<>(PACKAGE_ENTRY_TRANSLATOR, PackageDesc.of("java.lang"), PackageDesc.ofInternalName("java/lang")),
|
||||
new ValidSymbolCase<>(MODULE_ENTRY_TRANSLATOR, ModuleDesc.of("java.base"), ModuleDesc.of(new String("java.base".getBytes(StandardCharsets.US_ASCII), StandardCharsets.US_ASCII)))
|
||||
).mapMulti(ConstantDescSymbolsTest::specializeInflation);
|
||||
}
|
||||
|
||||
static Stream<Arguments> inequalityCases() {
|
||||
return Stream.of(
|
||||
new ValidSymbolCase<>(CLASS_ENTRY_TRANSLATOR, CD_Object, ClassDesc.ofInternalName("java/io/Object")), // class or interface
|
||||
new ValidSymbolCase<>(CLASS_ENTRY_TRANSLATOR, CD_Object.arrayType(), ClassDesc.ofDescriptor("[Ljava/lang/String;")), // array
|
||||
new ValidSymbolCase<>(UTF8_CLASS_TRANSLATOR, CD_int, ClassDesc.ofDescriptor("S")), // primitive
|
||||
new ValidSymbolCase<>(UTF8_CLASS_TRANSLATOR, CD_Object, ClassDesc.ofInternalName("java/lang/String")), // class or interface
|
||||
new ValidSymbolCase<>(UTF8_CLASS_TRANSLATOR, CD_Object.arrayType(), ClassDesc.ofDescriptor("[Ljava/lang/System;")), // array
|
||||
new ValidSymbolCase<>(UTF8_STRING_TRANSLATOR, "Ab\u0000c", "Abdc"),
|
||||
new ValidSymbolCase<>(UTF8_METHOD_TYPE_TRANSLATOR, MTD_void, MethodTypeDesc.ofDescriptor("()I")),
|
||||
new ValidSymbolCase<>(UTF8_METHOD_TYPE_TRANSLATOR, MethodTypeDesc.of(CD_int, CD_Short), MethodTypeDesc.ofDescriptor("(Ljava/lang/Long;)I")),
|
||||
new ValidSymbolCase<>(METHOD_TYPE_ENTRY_TRANSLATOR, MethodTypeDesc.of(CD_String), MethodTypeDesc.ofDescriptor("()Ljava/lang/Object;")),
|
||||
new ValidSymbolCase<>(STRING_ENTRY_TRANSLATOR, "Cat", new String("Ape".getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8)),
|
||||
new ValidSymbolCase<>(PACKAGE_ENTRY_TRANSLATOR, PackageDesc.of("java.lang"), PackageDesc.ofInternalName("java/util")),
|
||||
new ValidSymbolCase<>(MODULE_ENTRY_TRANSLATOR, ModuleDesc.of("java.base"), ModuleDesc.of(new String("java.desktop".getBytes(StandardCharsets.US_ASCII), StandardCharsets.US_ASCII)))
|
||||
).mapMulti(ConstantDescSymbolsTest::specializeInflation);
|
||||
}
|
||||
|
||||
record InvalidSymbolCase<T, P extends PoolEntry>(SymbolicTranslator<T, P> translator, Supplier<P> factory, T target) {
|
||||
}
|
||||
|
||||
// Type hint function
|
||||
private static <P extends PoolEntry> Supplier<P> badFactory(Function<ConstantPoolBuilder, P> func) {
|
||||
return () -> func.apply(ConstantPoolBuilder.of());
|
||||
}
|
||||
|
||||
static <T, P extends PoolEntry> void specializeInflation(InvalidSymbolCase<T, P> invalidSymbolCase, Consumer<Arguments> callArgs) {
|
||||
invalidSymbolCase.translator.spawnBounded(invalidSymbolCase.factory)
|
||||
.<StatefulPoolEntry<P>>mapMulti((src, sink) -> {
|
||||
sink.accept(src);
|
||||
sink.accept(invalidSymbolCase.translator.inflateByMatching(src, invalidSymbolCase.target, "target"));
|
||||
})
|
||||
.forEach(stateful -> callArgs.accept(Arguments.of(invalidSymbolCase, stateful.desc, stateful.factory.get())));
|
||||
}
|
||||
|
||||
static Stream<Arguments> malformedCases() {
|
||||
return Stream.of(
|
||||
new InvalidSymbolCase<>(CLASS_ENTRY_TRANSLATOR, badFactory(b -> b.classEntry(b.utf8Entry("java.lang.Object"))), CD_Object), // class or interface
|
||||
new InvalidSymbolCase<>(CLASS_ENTRY_TRANSLATOR, badFactory(b -> b.classEntry(b.utf8Entry("[Ljava/lang/String"))), CD_String.arrayType()), // array
|
||||
new InvalidSymbolCase<>(UTF8_CLASS_TRANSLATOR, badFactory(b -> b.utf8Entry("int")), ClassDesc.ofDescriptor("I")), // primitive
|
||||
new InvalidSymbolCase<>(UTF8_CLASS_TRANSLATOR, badFactory(b -> b.utf8Entry("Ljava/lang/String")), CD_String), // class or interface
|
||||
new InvalidSymbolCase<>(UTF8_CLASS_TRANSLATOR, badFactory(b -> b.utf8Entry("[Ljava/lang/String")), CD_String.arrayType()), // array
|
||||
new InvalidSymbolCase<>(METHOD_TYPE_ENTRY_TRANSLATOR, badFactory(b -> b.methodTypeEntry(b.utf8Entry("()"))), MTD_void),
|
||||
new InvalidSymbolCase<>(METHOD_TYPE_ENTRY_TRANSLATOR, badFactory(b -> b.methodTypeEntry(b.utf8Entry("(V)"))), MTD_void),
|
||||
new InvalidSymbolCase<>(UTF8_METHOD_TYPE_TRANSLATOR, badFactory(b -> b.utf8Entry("()Ljava/lang/String")), MethodTypeDesc.of(CD_String)),
|
||||
new InvalidSymbolCase<>(PACKAGE_ENTRY_TRANSLATOR, badFactory(b -> b.packageEntry(b.utf8Entry("java.lang"))), PackageDesc.of("java.lang")),
|
||||
new InvalidSymbolCase<>(MODULE_ENTRY_TRANSLATOR, badFactory(b -> b.moduleEntry(b.utf8Entry("java@base"))), ModuleDesc.of("java.base"))
|
||||
).mapMulti(ConstantDescSymbolsTest::specializeInflation);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user