8331027: JDK's ct.sym file packs corrupted module-info classes
Reviewed-by: asotona
This commit is contained in:
parent
553d45b114
commit
8771015d7e
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2006, 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
|
||||
@ -970,7 +970,7 @@ public class CreateSymbols {
|
||||
}
|
||||
if (header.moduleMainClass != null) {
|
||||
int attrIdx = addString(cp, Attribute.ModuleMainClass);
|
||||
int targetIdx = addString(cp, header.moduleMainClass);
|
||||
int targetIdx = addClassName(cp, header.moduleMainClass);
|
||||
attributes.put(Attribute.ModuleMainClass,
|
||||
new ModuleMainClass_attribute(attrIdx, targetIdx));
|
||||
}
|
||||
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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
|
||||
* @bug 8331027
|
||||
* @summary Verify classfile inside ct.sym
|
||||
* @enablePreview
|
||||
* @library /tools/lib
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* jdk.compiler/com.sun.tools.javac.platform
|
||||
* jdk.compiler/com.sun.tools.javac.util:+open
|
||||
* @build toolbox.ToolBox VerifyCTSymClassFiles
|
||||
* @run main VerifyCTSymClassFiles
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.attribute.ModuleMainClassAttribute;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class VerifyCTSymClassFiles {
|
||||
|
||||
public static void main(String... args) throws IOException, URISyntaxException {
|
||||
VerifyCTSymClassFiles t = new VerifyCTSymClassFiles();
|
||||
|
||||
t.checkClassFiles();
|
||||
}
|
||||
|
||||
void checkClassFiles() throws IOException {
|
||||
Path ctSym = Paths.get(System.getProperty("java.home"), "lib", "ct.sym");
|
||||
|
||||
if (!Files.exists(ctSym)) {
|
||||
//no ct.sym, nothing to check:
|
||||
return ;
|
||||
}
|
||||
try (FileSystem fs = FileSystems.newFileSystem(ctSym)) {
|
||||
Files.walk(fs.getRootDirectories().iterator().next())
|
||||
.filter(p -> Files.isRegularFile(p))
|
||||
.forEach(p -> checkClassFile(p));
|
||||
}
|
||||
}
|
||||
|
||||
void checkClassFile(Path p) {
|
||||
if (!"module-info.sig".equals(p.getFileName().toString())) {
|
||||
return ;
|
||||
}
|
||||
try {
|
||||
ClassFile.of().parse(p).attributes().forEach(attr -> {
|
||||
if (attr instanceof ModuleMainClassAttribute mmca) {
|
||||
mmca.mainClass();
|
||||
}
|
||||
});
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 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
|
||||
@ -23,8 +23,9 @@
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8072480 8277106
|
||||
* @bug 8072480 8277106 8331027
|
||||
* @summary Unit test for CreateSymbols
|
||||
* @enablePreview
|
||||
* @modules java.compiler
|
||||
* jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.jvm
|
||||
@ -101,6 +102,9 @@ public class CreateSymbolsTest {
|
||||
null,
|
||||
List.of("-d",
|
||||
compileDir.toAbsolutePath().toString(),
|
||||
"--enable-preview",
|
||||
"--source",
|
||||
"" + System.getProperty("java.specification.version"),
|
||||
"-g",
|
||||
"--add-modules", "jdk.jdeps",
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
|
||||
|
@ -26,6 +26,11 @@ import java.io.InputStream;
|
||||
import java.io.Writer;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.ClassModel;
|
||||
import java.lang.classfile.attribute.ModuleAttribute;
|
||||
import java.lang.classfile.attribute.ModulePackagesAttribute;
|
||||
import java.lang.constant.PackageDesc;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
@ -58,14 +63,11 @@ import build.tools.symbolgenerator.CreateSymbols.ClassDescription;
|
||||
import build.tools.symbolgenerator.CreateSymbols.ClassList;
|
||||
import build.tools.symbolgenerator.CreateSymbols.ExcludeIncludeList;
|
||||
import build.tools.symbolgenerator.CreateSymbols.VersionDescription;
|
||||
import com.sun.tools.classfile.Attribute;
|
||||
import com.sun.tools.classfile.Attributes;
|
||||
import com.sun.tools.classfile.ClassFile;
|
||||
import com.sun.tools.classfile.ClassWriter;
|
||||
import com.sun.tools.classfile.ConstantPool;
|
||||
import com.sun.tools.classfile.ConstantPool.CPInfo;
|
||||
import com.sun.tools.classfile.ConstantPool.CONSTANT_Utf8_info;
|
||||
import com.sun.tools.classfile.ModulePackages_attribute;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.classfile.attribute.ModuleMainClassAttribute;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class CreateSymbolsTestImpl {
|
||||
|
||||
@ -981,14 +983,19 @@ public class CreateSymbolsTestImpl {
|
||||
}
|
||||
|
||||
Path prepareVersionedCTSym(String[] code7, String[] code8) throws Exception {
|
||||
return prepareVersionedCTSym(code7, code8, _ -> {});
|
||||
}
|
||||
|
||||
Path prepareVersionedCTSym(String[] code7, String[] code8,
|
||||
Consumer<Path> adjustClassFiles) throws Exception {
|
||||
String testClasses = System.getProperty("test.classes");
|
||||
Path output = Paths.get(testClasses, "test-data" + i++);
|
||||
deleteRecursively(output);
|
||||
Files.createDirectories(output);
|
||||
Path ver7Jar = output.resolve("7.jar");
|
||||
compileAndPack(output, ver7Jar, code7);
|
||||
compileAndPack(output, ver7Jar, adjustClassFiles, code7);
|
||||
Path ver8Jar = output.resolve("8.jar");
|
||||
compileAndPack(output, ver8Jar, code8);
|
||||
compileAndPack(output, ver8Jar, adjustClassFiles, code8);
|
||||
|
||||
Path classes = output.resolve("classes.zip");
|
||||
|
||||
@ -1048,7 +1055,112 @@ public class CreateSymbolsTestImpl {
|
||||
new CreateSymbols().createSymbols(null, symbolsDesc.toAbsolutePath().toString(), classDest, 0, "8", "", modules.toString(), modulesList.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testModuleMainClass() throws Exception {
|
||||
ClassFile cf = ClassFile.of();
|
||||
ToolBox tb = new ToolBox();
|
||||
String testClasses = System.getProperty("test.classes");
|
||||
Path output = Paths.get(testClasses, "test-data" + i++);
|
||||
deleteRecursively(output);
|
||||
Files.createDirectories(output);
|
||||
Path ver9Jar = output.resolve("9.jar");
|
||||
compileAndPack(output,
|
||||
ver9Jar,
|
||||
classesDir -> {
|
||||
try {
|
||||
Path moduleInfo = classesDir.resolve("module-info.class");
|
||||
byte[] newClassData =
|
||||
cf.transform(cf.parse(moduleInfo),
|
||||
(builder, element) -> {
|
||||
builder.with(element);
|
||||
if (element instanceof ModuleAttribute) {
|
||||
builder.with(ModuleMainClassAttribute.of(ClassDesc.of("main.Main")));
|
||||
}
|
||||
});
|
||||
try (OutputStream out = Files.newOutputStream(moduleInfo)) {
|
||||
out.write(newClassData);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
},
|
||||
"""
|
||||
module m {
|
||||
}
|
||||
""",
|
||||
"""
|
||||
package main;
|
||||
public class Main {}
|
||||
""");
|
||||
|
||||
|
||||
Path ctSym = output.resolve("ct.sym");
|
||||
|
||||
deleteRecursively(ctSym);
|
||||
|
||||
CreateSymbols.ALLOW_NON_EXISTING_CLASSES = true;
|
||||
CreateSymbols.EXTENSION = ".class";
|
||||
|
||||
List<VersionDescription> versions =
|
||||
Arrays.asList(new VersionDescription(ver9Jar.toAbsolutePath().toString(), "9", null));
|
||||
|
||||
ExcludeIncludeList acceptAll = new ExcludeIncludeList(null, null) {
|
||||
@Override public boolean accepts(String className, boolean includePrivateClasses) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
new CreateSymbols().createBaseLine(versions, acceptAll, ctSym, new String[0]);
|
||||
Path symbolsDesc = ctSym.resolve("symbols");
|
||||
Path modules = ctSym.resolve("modules");
|
||||
Path modulesList = ctSym.resolve("modules-list");
|
||||
|
||||
Files.createDirectories(modules);
|
||||
try (Writer w = Files.newBufferedWriter(modulesList)) {}
|
||||
|
||||
Path classesZip = output.resolve("classes.zip");
|
||||
Path classesDir = output.resolve("classes");
|
||||
|
||||
new CreateSymbols().createSymbols(null, symbolsDesc.toAbsolutePath().toString(), classesZip.toAbsolutePath().toString(), 0, "9", "", modules.toString(), modulesList.toString());
|
||||
|
||||
try (JarFile jf = new JarFile(classesZip.toFile())) {
|
||||
Enumeration<JarEntry> en = jf.entries();
|
||||
|
||||
while (en.hasMoreElements()) {
|
||||
JarEntry je = en.nextElement();
|
||||
if (je.isDirectory()) continue;
|
||||
Path target = classesDir.resolve(je.getName());
|
||||
Files.createDirectories(target.getParent());
|
||||
Files.copy(jf.getInputStream(je), target);
|
||||
}
|
||||
}
|
||||
|
||||
Path moduleInfo = classesDir.resolve("9")
|
||||
.resolve("m")
|
||||
.resolve("module-info.class");
|
||||
|
||||
cf.parse(moduleInfo)
|
||||
.attributes()
|
||||
.stream()
|
||||
.filter(attr -> attr instanceof ModuleMainClassAttribute)
|
||||
.forEach(attr -> {
|
||||
String expectedMain = "Lmain/Main;";
|
||||
String mainClass =
|
||||
((ModuleMainClassAttribute) attr).mainClass()
|
||||
.asSymbol()
|
||||
.descriptorString();
|
||||
if (!Objects.equals(expectedMain, mainClass)) {
|
||||
throw new AssertionError("Expected " + expectedMain + " as a main class, " +
|
||||
"but got: " + mainClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void compileAndPack(Path output, Path outputFile, String... code) throws Exception {
|
||||
compileAndPack(output, outputFile, _ -> {}, code);
|
||||
}
|
||||
|
||||
void compileAndPack(Path output, Path outputFile,
|
||||
Consumer<Path> adjustClassFiles, String... code) throws Exception {
|
||||
ToolBox tb = new ToolBox();
|
||||
Path scratch = output.resolve("temp");
|
||||
deleteRecursively(scratch);
|
||||
@ -1065,27 +1177,21 @@ public class CreateSymbolsTestImpl {
|
||||
packages.add(cf.substring(0, sep));
|
||||
}
|
||||
}
|
||||
ClassFile cf = ClassFile.read(moduleInfo);
|
||||
List<CPInfo> cp = new ArrayList<>();
|
||||
cp.add(null);
|
||||
cf.constant_pool.entries().forEach(cp::add);
|
||||
Map<String, Attribute> attrs = new HashMap<>(cf.attributes.map);
|
||||
int[] encodedPackages = new int[packages.size()];
|
||||
int i = 0;
|
||||
for (String p : packages) {
|
||||
int nameIndex = cp.size();
|
||||
cp.add(new CONSTANT_Utf8_info(p));
|
||||
encodedPackages[i++] = cp.size();
|
||||
cp.add(new ConstantPool.CONSTANT_Package_info(null, nameIndex));
|
||||
}
|
||||
int attrName = cp.size();
|
||||
cp.add(new CONSTANT_Utf8_info(Attribute.ModulePackages));
|
||||
attrs.put(Attribute.ModulePackages, new ModulePackages_attribute(attrName, encodedPackages));
|
||||
ClassFile newFile = new ClassFile(cf.magic, cf.minor_version, cf.major_version, new ConstantPool(cp.toArray(new CPInfo[0])), cf.access_flags, cf.this_class, cf.super_class, cf.interfaces, cf.fields, cf.methods, new Attributes(attrs));
|
||||
ClassFile cf = ClassFile.of();
|
||||
ClassModel cm = cf.parse(moduleInfo);
|
||||
byte[] newData = cf.transform(cm, (builder, element) -> {
|
||||
builder.with(element);
|
||||
if (element instanceof ModuleAttribute) {
|
||||
builder.with(ModulePackagesAttribute.ofNames(packages.stream()
|
||||
.map(pack -> PackageDesc.of(pack))
|
||||
.toList()));
|
||||
}
|
||||
});
|
||||
try (OutputStream out = Files.newOutputStream(moduleInfo)) {
|
||||
new ClassWriter().write(newFile, out);
|
||||
out.write(newData);
|
||||
}
|
||||
}
|
||||
adjustClassFiles.accept(scratch);
|
||||
try (Writer out = Files.newBufferedWriter(outputFile)) {
|
||||
for (String classFile : classFiles) {
|
||||
try (InputStream in = Files.newInputStream(scratch.resolve(classFile))) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user