8306729: Add nominal descriptors of modules and packages to Constants API

Reviewed-by: mchung
This commit is contained in:
Adam Sotona 2023-05-03 06:34:58 +00:00
parent 0b5b6429a0
commit c8f37564bf
8 changed files with 467 additions and 0 deletions

View File

@ -75,6 +75,66 @@ class ConstantUtils {
return name;
}
/**
* Validates the correctness of a binary package name.
* In particular checks for the presence of invalid characters in the name.
* Empty package name is allowed.
*
* @param name the package name
* @return the package name passed if valid
* @throws IllegalArgumentException if the package name is invalid
* @throws NullPointerException if the package name is {@code null}
*/
public static String validateBinaryPackageName(String name) {
for (int i=0; i<name.length(); i++) {
char ch = name.charAt(i);
if (ch == ';' || ch == '[' || ch == '/')
throw new IllegalArgumentException("Invalid package name: " + name);
}
return name;
}
/**
* Validates the correctness of an internal package name.
* In particular checks for the presence of invalid characters in the name.
* Empty package name is allowed.
*
* @param name the package name
* @return the package name passed if valid
* @throws IllegalArgumentException if the package name is invalid
* @throws NullPointerException if the package name is {@code null}
*/
public static String validateInternalPackageName(String name) {
for (int i=0; i<name.length(); i++) {
char ch = name.charAt(i);
if (ch == ';' || ch == '[' || ch == '.')
throw new IllegalArgumentException("Invalid package name: " + name);
}
return name;
}
/**
* Validates the correctness of a module name.
* In particular checks for the presence of invalid characters in the name.
* Empty module name is allowed.
*
* {@jvms 4.2.3} Module and Package Names
*
* @param name the module name
* @return the module name passed if valid
* @throws IllegalArgumentException if the module name is invalid
* @throws NullPointerException if the module name is {@code null}
*/
public static String validateModuleName(String name) {
for (int i=name.length() - 1; i >= 0; i--) {
char ch = name.charAt(i);
if ((ch >= '\u0000' && ch <= '\u001F')
|| ((ch == '\\' || ch == ':' || ch =='@') && (i == 0 || name.charAt(--i) != '\\')))
throw new IllegalArgumentException("Invalid module name: " + name);
}
return name;
}
/**
* Validates a member name
*

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2023, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package java.lang.constant;
import static java.util.Objects.requireNonNull;
/**
* A nominal descriptor for a {@code Module} constant.
*
* <p>
* To create a {@link ModuleDesc} for a module, use the {@link #of(String)}
* method.
*
* @jvms 4.4.11 The CONSTANT_Module_info Structure
* @since 21
*/
public sealed interface ModuleDesc
permits ModuleDescImpl {
/**
* Returns a {@link ModuleDesc} for a module,
* given the name of the module.
*
* @param name the module name
* @return a {@link ModuleDesc} describing the desired module
* @throws NullPointerException if the argument is {@code null}
* @throws IllegalArgumentException if the name string is not in the
* correct format
* @jvms 4.2.3 Module and Package Names
*/
static ModuleDesc of(String name) {
ConstantUtils.validateModuleName(requireNonNull(name));
return new ModuleDescImpl(name);
}
/**
* Returns the module name of this {@link ModuleDesc}.
*
* @return the module name
*/
String name();
/**
* Compare the specified object with this descriptor for equality.
* Returns {@code true} if and only if the specified object is
* also a {@link ModuleDesc} and both describe the same module.
*
* @param o the other object
* @return whether this descriptor is equal to the other object
*/
@Override
boolean equals(Object o);
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2023, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package java.lang.constant;
/*
* Implementation of {@code ModuleDesc}
* @param name must have been validated
*/
record ModuleDescImpl(String name) implements ModuleDesc {
@Override
public String toString() {
return String.format("ModuleDesc[%s]", name());
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (c) 2023, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package java.lang.constant;
import static java.util.Objects.requireNonNull;
/**
* A nominal descriptor for a {@code Package} constant.
*
* <p>
* To create a {@link PackageDesc} for a package,
* use the {@link #of(String)} or {@link #ofInternalName(String)} method.
*
* @jvms 4.4.12 The CONSTANT_Package_info Structure
* @since 21
*/
public sealed interface PackageDesc
permits PackageDescImpl {
/**
* Returns a {@link PackageDesc} for a package,
* given the name of the package, such as {@code "java.lang"}.
*
* @param name the fully qualified (dot-separated) package name
* @return a {@link PackageDesc} describing the desired package
* @throws NullPointerException if the argument is {@code null}
* @throws IllegalArgumentException if the name string is not in the
* correct format
* @jls 6.5.3 Module Names and Package Names
* @see PackageDesc#ofInternalName(String)
*/
static PackageDesc of(String name) {
ConstantUtils.validateBinaryPackageName(requireNonNull(name));
return new PackageDescImpl(ConstantUtils.binaryToInternal(name));
}
/**
* Returns a {@link PackageDesc} for a package,
* given the name of the package in internal form,
* such as {@code "java/lang"}.
*
* @param name the fully qualified package name, in internal
* (slash-separated) form
* @return a {@link PackageDesc} describing the desired package
* @throws NullPointerException if the argument is {@code null}
* @throws IllegalArgumentException if the name string is not in the
* correct format
* @jvms 4.2.1 Binary Class and Interface Names
* @jvms 4.2.3 Module and Package Names
* @see PackageDesc#of(String)
*/
static PackageDesc ofInternalName(String name) {
ConstantUtils.validateInternalPackageName(requireNonNull(name));
return new PackageDescImpl(name);
}
/**
* Returns the fully qualified (slash-separated) package name in internal form
* of this {@link PackageDesc}.
*
* @return the package name in internal form, or the empty string for the
* unnamed package
* @see PackageDesc#name()
*/
String internalName();
/**
* Returns the fully qualified (dot-separated) package name
* of this {@link PackageDesc}.
*
* @return the package name, or the empty string for the
* unnamed package
* @see PackageDesc#internalName()
*/
default String name() {
return ConstantUtils.internalToBinary(internalName());
}
/**
* Compare the specified object with this descriptor for equality.
* Returns {@code true} if and only if the specified object is
* also a {@link PackageDesc} and both describe the same package.
*
* @param o the other object
* @return whether this descriptor is equal to the other object
*/
@Override
boolean equals(Object o);
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2023, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package java.lang.constant;
/*
* Implementation of {@code PackageDesc}
* @param internalName must have been validated
*/
record PackageDescImpl(String internalName) implements PackageDesc {
@Override
public String toString() {
return String.format("PackageDesc[%s]", name());
}
}

View File

@ -89,6 +89,11 @@
* It is also suitable for describing {@code invokedynamic} call sites in bytecode
* reading and writing APIs.
*
* <p>Other members of this package are {@link ModuleDesc}
* and {@link PackageDesc}. They represent module and package
* info structures, suitable for describing modules and their content in bytecode
* reading and writing APIs.
*
* @jvms 4.4 The Constant Pool
*
* @since 12

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2023, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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
* @summary Testing ModuleDesc.
* @run junit ModuleDescTest
*/
import java.lang.constant.ModuleDesc;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.*;
class ModuleDescTest {
@ParameterizedTest
@ValueSource(strings = {
"abc\\", "ab\\c", "\u0001", "\u001e",
":", ":foo", "foo:", "foo:bar",
"@", "@foo", "foo@", "foo@bar",
"\\", "\\foo", "foo\\", "foo\\bar",
"\u0000", "\u0000foo", "foo\u0000", "foo\u0000bar",
"\u001f", "\u001ffoo", "foo\u001f", "foo\u001fbar"})
public void testInvalidModuleNames(String mdl) {
assertThrows(IllegalArgumentException.class, () -> ModuleDesc.of(mdl));
}
@Test
public void testNullModuleName() {
assertThrows(NullPointerException.class, () -> ModuleDesc.of(null));
}
@ParameterizedTest
@ValueSource(strings = {
"", "a\\\\b", "a.b/c", "a\\@b\\: c",
".", ".foo", "foo.", "foo.bar",
"..", "..foo", "foo..", "foo..bar",
"[", "[foo", "foo[", "foo[bar",
";", ";foo", "foo;", "foo;bar",
"\\\\", "\\\\foo", "foo\\\\", "foo\\\\bar",
"\\\\\\\\", "\\\\\\\\foo", "foo\\\\\\\\", "foo\\\\\\\\bar",
"\\:", "\\:foo", "foo\\:", "foo\\:bar",
"\\:\\:", "\\:\\:foo", "foo\\:\\:", "foo\\:\\:bar",
"\\@", "\\@foo", "foo\\@", "foo\\@bar",
"\\@\\@", "\\@\\@foo", "foo\\@\\@", "foo\\@\\@bar"})
public void testValidModuleNames(String mdl) {
assertEquals(ModuleDesc.of(mdl), ModuleDesc.of(mdl));
assertEquals(ModuleDesc.of(mdl).name(), mdl);
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2023, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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
* @summary Testing PackageDesc.
* @run junit PackageDescTest
*/
import java.lang.constant.PackageDesc;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class PackageDescTest {
@ParameterizedTest
@ValueSource(strings = {"a/b.d", "a[]", "a;"})
void testInvalidPackageNames(String pkg) {
assertThrows(IllegalArgumentException.class, () -> PackageDesc.of(pkg));
}
@ParameterizedTest
@ValueSource(strings = {"a/b.d", "a[]", "a;"})
void testInvalidInternalPackageNames(String pkg) {
assertThrows(IllegalArgumentException.class, () -> PackageDesc.ofInternalName(pkg));
}
@Test
void testNullInternalPackageNames() {
assertThrows(NullPointerException.class, () -> PackageDesc.ofInternalName(null));
}
@Test
void testValidPackageNames() {
assertEquals(PackageDesc.of(""), PackageDesc.ofInternalName(""));
assertEquals(PackageDesc.of("a"), PackageDesc.ofInternalName("a"));
assertEquals(PackageDesc.of("a.b"), PackageDesc.ofInternalName("a/b"));
assertEquals(PackageDesc.of("a.b.c"), PackageDesc.ofInternalName("a/b/c"));
assertEquals(PackageDesc.of("a").name(), PackageDesc.ofInternalName("a").name());
assertEquals(PackageDesc.of("a.b").name(), PackageDesc.ofInternalName("a/b").name());
assertEquals(PackageDesc.of("a.b.c").name(), PackageDesc.ofInternalName("a/b/c").name());
assertEquals(PackageDesc.of("a").internalName(), PackageDesc.ofInternalName("a").internalName());
assertEquals(PackageDesc.of("a.b").internalName(), PackageDesc.ofInternalName("a/b").internalName());
assertEquals(PackageDesc.of("a.b.c").internalName(), PackageDesc.ofInternalName("a/b/c").internalName());
}
}