Skip to content

Commit 4d1a0ae

Browse files
committed
Merge remote-tracking branch 'origin/main' into impl_for_wraps
2 parents e1fcaa7 + 48016e0 commit 4d1a0ae

File tree

62 files changed

+973
-434
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+973
-434
lines changed

.github/workflows/verify-changelog-and-set-milestone.yml

+2-4
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ jobs:
4545
# Add and fetch full history of the base branch
4646
BASE_BRANCH=${{ github.event.pull_request.base.ref }}
4747
# We need this to ensure we will be able to find the merge base
48-
git fetch --unshallow || true
49-
git fetch upstream $BASE_BRANCH
48+
git fetch upstream $BASE_BRANCH --unshallow
5049
5150
# Find merge base commit to diff against
5251
BASE_COMMIT=$(git merge-base HEAD upstream/$BASE_BRANCH)
@@ -57,8 +56,7 @@ jobs:
5756
git log --oneline -n 3 HEAD
5857
5958
echo "Diff:"
60-
git diff --name-only $BASE_COMMIT HEAD -- ${{ env.CHANGE_LOG_FILE }}
61-
if ! git diff --name-only $BASE_COMMIT HEAD -- ${{ env.CHANGE_LOG_FILE }}; then
59+
if git diff --exit-code --name-only $BASE_COMMIT HEAD -- ${{ env.CHANGE_LOG_FILE }}; then
6260
echo "Change log file:${{ env.CHANGE_LOG_FILE }} does not contains an entry corresponding to changes introduced in PR. Please add a changelog entry."
6361
exit 0
6462
else

gradle/generation/extract-jdk-apis.gradle

+3-11
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,10 @@ configure(project(":lucene:core")) {
2323
mrjarJavaVersions = [ 24 ]
2424
}
2525

26-
configurations {
27-
apiextractor
28-
}
29-
30-
dependencies {
31-
apiextractor deps.asm.core
32-
}
33-
3426
plugins.withType(JavaPlugin) {
3527
mrjarJavaVersions.each { jdkVersion ->
3628
def task = tasks.create(name: "generateJdkApiJar${jdkVersion}", type: JavaExec) {
37-
description "Regenerate the API-only JAR file with public Panama Foreign & Vector API from JDK ${jdkVersion}"
29+
description "Regenerate the API-only JAR file with public Panama Vector API from JDK ${jdkVersion}"
3830
group "generation"
3931

4032
javaLauncher = javaToolchains.launcherFor {
@@ -46,20 +38,20 @@ configure(project(":lucene:core")) {
4638
javaLauncher.get()
4739
return true
4840
} catch (Exception e) {
49-
logger.warn('Launcher for Java {} is not available; skipping regeneration of Panama Foreign & Vector API JAR.', jdkVersion)
41+
logger.warn('Launcher for Java {} is not available; skipping regeneration of Panama Vector API JAR.', jdkVersion)
5042
logger.warn('Error: {}', e.cause?.message)
5143
logger.warn("Please make sure to point env 'JAVA{}_HOME' to exactly JDK version {} or enable Gradle toolchain auto-download.", jdkVersion, jdkVersion)
5244
return false
5345
}
5446
}
5547

56-
classpath = configurations.apiextractor
5748
mainClass = file("${resources}/ExtractJdkApis.java") as String
5849
systemProperties = [
5950
'user.timezone': 'UTC',
6051
'file.encoding': 'UTF-8',
6152
]
6253
args = [
54+
rootProject.minJavaVersion.toString(),
6355
jdkVersion,
6456
apijars.file("jdk${jdkVersion}.apijar"),
6557
]

gradle/generation/extract-jdk-apis/ExtractJdkApis.java

+112-105
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,26 @@
1515
* limitations under the License.
1616
*/
1717
import java.io.IOException;
18+
import java.lang.classfile.AccessFlags;
19+
import java.lang.classfile.Attributes;
20+
import java.lang.classfile.ClassFile;
21+
import java.lang.classfile.ClassFileBuilder;
22+
import java.lang.classfile.ClassFileElement;
23+
import java.lang.classfile.ClassFileVersion;
24+
import java.lang.classfile.ClassModel;
25+
import java.lang.classfile.ClassTransform;
26+
import java.lang.classfile.CodeModel;
27+
import java.lang.classfile.FieldModel;
28+
import java.lang.classfile.MethodModel;
29+
import java.lang.classfile.MethodTransform;
30+
import java.lang.classfile.attribute.InnerClassesAttribute;
31+
import java.lang.classfile.attribute.RuntimeInvisibleAnnotationsAttribute;
32+
import java.lang.classfile.attribute.SourceFileAttribute;
33+
import java.lang.classfile.constantpool.ClassEntry;
34+
import java.lang.constant.ClassDesc;
35+
import java.lang.invoke.MethodHandles;
36+
import java.lang.reflect.AccessFlag;
37+
import java.lang.reflect.ClassFileFormatVersion;
1838
import java.net.URI;
1939
import java.nio.file.Files;
2040
import java.nio.file.Path;
@@ -23,7 +43,6 @@
2343
import java.nio.file.attribute.FileTime;
2444
import java.time.Instant;
2545
import java.util.Arrays;
26-
import java.util.HashMap;
2746
import java.util.HashSet;
2847
import java.util.List;
2948
import java.util.Map;
@@ -36,42 +55,43 @@
3655
import java.util.zip.ZipEntry;
3756
import java.util.zip.ZipOutputStream;
3857

39-
import org.objectweb.asm.AnnotationVisitor;
40-
import org.objectweb.asm.ClassReader;
41-
import org.objectweb.asm.ClassVisitor;
42-
import org.objectweb.asm.ClassWriter;
43-
import org.objectweb.asm.FieldVisitor;
44-
import org.objectweb.asm.MethodVisitor;
45-
import org.objectweb.asm.Opcodes;
46-
import org.objectweb.asm.Type;
47-
4858
public final class ExtractJdkApis {
4959

50-
private static final FileTime FIXED_FILEDATE = FileTime.from(Instant.parse("2025-04-29T00:00:00Z"));
60+
private static final FileTime FIXED_FILEDATE = FileTime.from(Instant.parse("2025-05-05T00:00:00Z"));
5161

5262
private static final String PATTERN_VECTOR_INCUBATOR = "jdk.incubator.vector/jdk/incubator/vector/*";
5363
private static final String PATTERN_VECTOR_VM_INTERNALS = "java.base/jdk/internal/vm/vector/VectorSupport{,$Vector,$VectorMask,$VectorPayload,$VectorShuffle}";
5464

5565
static final Map<Integer,List<String>> CLASSFILE_PATTERNS = Map.of(
5666
24, List.of(PATTERN_VECTOR_VM_INTERNALS, PATTERN_VECTOR_INCUBATOR)
5767
);
58-
68+
69+
private static final ClassDesc CD_PreviewFeature = ClassDesc.ofInternalName("jdk/internal/javac/PreviewFeature");
70+
5971
public static void main(String... args) throws IOException {
60-
if (args.length != 2) {
61-
throw new IllegalArgumentException("Need two parameters: java version, output file");
72+
if (args.length != 3) {
73+
throw new IllegalArgumentException("Need two parameters: target java version, extract java version, output file");
6274
}
63-
Integer jdk = Integer.valueOf(args[0]);
64-
if (jdk.intValue() != Runtime.version().feature()) {
65-
throw new IllegalStateException("Incorrect java version: " + Runtime.version().feature());
75+
int targetJdk = Integer.parseInt(args[0]);
76+
Integer extractJdk = Integer.valueOf(args[1]);
77+
int runtimeJdk = Runtime.version().feature();
78+
if (extractJdk.intValue() != runtimeJdk) {
79+
throw new IllegalStateException("Incorrect runtime java version: " + runtimeJdk);
6680
}
67-
if (!CLASSFILE_PATTERNS.containsKey(jdk)) {
68-
throw new IllegalArgumentException("No support to extract stubs from java version: " + jdk);
81+
if (extractJdk.intValue() < targetJdk) {
82+
throw new IllegalStateException("extract java version " + extractJdk + " < target java version " + targetJdk);
6983
}
70-
var outputPath = Paths.get(args[1]);
84+
if (!CLASSFILE_PATTERNS.containsKey(extractJdk)) {
85+
throw new IllegalArgumentException("No support to extract stubs from java version: " + extractJdk);
86+
}
87+
var outputPath = Paths.get(args[2]);
88+
89+
// the output class files need to be compatible with the targetJdk of our compilation, so we need to adapt them:
90+
var classFileVersion = ClassFileVersion.of(ClassFileFormatVersion.valueOf("RELEASE_" + targetJdk).major(), 0);
7191

7292
// create JRT filesystem and build a combined FileMatcher:
7393
var jrtPath = Paths.get(URI.create("jrt:/")).toRealPath();
74-
var patterns = CLASSFILE_PATTERNS.get(jdk).stream()
94+
var patterns = CLASSFILE_PATTERNS.get(extractJdk).stream()
7595
.map(pattern -> jrtPath.getFileSystem().getPathMatcher("glob:" + pattern + ".class"))
7696
.toArray(PathMatcher[]::new);
7797
PathMatcher pattern = p -> Arrays.stream(patterns).anyMatch(matcher -> matcher.matches(p));
@@ -84,107 +104,94 @@ public static void main(String... args) throws IOException {
84104

85105
// Process all class files:
86106
try (var out = new ZipOutputStream(Files.newOutputStream(outputPath))) {
87-
process(filesToExtract, out);
107+
process(filesToExtract, out, classFileVersion);
88108
}
89109
}
90110

91-
private static void process(List<Path> filesToExtract, ZipOutputStream out) throws IOException {
111+
private static void process(List<Path> filesToExtract, ZipOutputStream out, ClassFileVersion classFileVersion) throws IOException {
112+
System.out.println("Loading and analyzing " + filesToExtract.size() + " class files...");
92113
var classesToInclude = new HashSet<String>();
93-
var references = new HashMap<String, String[]>();
94-
var processed = new TreeMap<String, byte[]>();
95-
System.out.println("Transforming " + filesToExtract.size() + " class files...");
114+
var toProcess = new TreeMap<String, ClassModel>();
115+
var cc = ClassFile.of(ClassFile.ConstantPoolSharingOption.NEW_POOL, ClassFile.DebugElementsOption.DROP_DEBUG,
116+
ClassFile.LineNumbersOption.DROP_LINE_NUMBERS, ClassFile.StackMapsOption.DROP_STACK_MAPS);
96117
for (Path p : filesToExtract) {
97-
try (var in = Files.newInputStream(p)) {
98-
var reader = new ClassReader(in);
99-
var cw = new ClassWriter(0);
100-
var cleaner = new Cleaner(cw, classesToInclude, references);
101-
reader.accept(cleaner, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
102-
processed.put(reader.getClassName(), cw.toByteArray());
118+
ClassModel parsed = cc.parse(p);
119+
String internalName = parsed.thisClass().asInternalName();
120+
toProcess.put(internalName, parsed);
121+
if (isVisible(parsed.flags())) {
122+
classesToInclude.add(internalName);
103123
}
104124
}
105-
// recursively add all superclasses / interfaces of visible classes to classesToInclude:
125+
// recursively add all superclasses / interfaces / outer classes of visible classes to classesToInclude:
106126
for (Set<String> a = classesToInclude; !a.isEmpty();) {
107-
a = a.stream().map(references::get).filter(Objects::nonNull).flatMap(Arrays::stream).collect(Collectors.toSet());
108-
classesToInclude.addAll(a);
127+
classesToInclude.addAll(a = a.stream().map(toProcess::get).filter(Objects::nonNull).flatMap(ExtractJdkApis::getReferences).collect(Collectors.toSet()));
109128
}
110129
// remove all non-visible or not referenced classes:
111-
processed.keySet().removeIf(Predicate.not(classesToInclude::contains));
112-
System.out.println("Writing " + processed.size() + " visible classes...");
113-
for (var cls : processed.entrySet()) {
114-
String cn = cls.getKey();
115-
System.out.println("Writing stub for class: " + cn);
116-
out.putNextEntry(new ZipEntry(cn.concat(".class")).setLastModifiedTime(FIXED_FILEDATE));
117-
out.write(cls.getValue());
130+
toProcess.keySet().removeIf(Predicate.not(classesToInclude::contains));
131+
// transformation of class files:
132+
System.out.println("Writing " + toProcess.size() + " visible classes...");
133+
for (var parsed : toProcess.values()) {
134+
String internalName = parsed.thisClass().asInternalName();
135+
System.out.println("Writing stub for class: " + internalName);
136+
ClassTransform ct = ClassTransform.dropping(ce -> switch (ce) {
137+
case MethodModel e -> !isVisible(e.flags());
138+
case FieldModel e -> !isVisible(e.flags());
139+
case SourceFileAttribute _ -> true;
140+
default -> false;
141+
}).andThen((builder, ce) -> {
142+
switch (ce) {
143+
case ClassFileVersion _ -> builder.with(classFileVersion);
144+
// the PreviewFeature annotation may refer to its own inner classes and therefore we must get rid of the inner class entry:
145+
case InnerClassesAttribute a -> builder.with(InnerClassesAttribute.of(a.classes().stream()
146+
.filter(c -> !CD_PreviewFeature.equals(c.outerClass().map(ClassEntry::asSymbol).orElse(null)))
147+
.toList()));
148+
default -> builder.with(ce);
149+
}
150+
}).andThen(ExtractJdkApis::dropPreview)
151+
.andThen(ClassTransform.transformingMethods(MethodTransform.dropping(CodeModel.class::isInstance).andThen(ExtractJdkApis::dropPreview))
152+
.andThen(ClassTransform.transformingFields(ExtractJdkApis::dropPreview)));
153+
out.putNextEntry(new ZipEntry(internalName.concat(".class")).setLastModifiedTime(FIXED_FILEDATE));
154+
out.write(cc.transformClass(parsed, ct));
118155
out.closeEntry();
119156
}
120-
classesToInclude.removeIf(processed.keySet()::contains);
121-
System.out.println("Referenced classes not included: " + classesToInclude);
157+
// make sure that no classes are left over except those which are in java.base module
158+
classesToInclude.removeIf(toProcess.keySet()::contains);
159+
var missingClasses = classesToInclude.stream().filter(internalName -> {
160+
try {
161+
return ClassDesc.ofInternalName(internalName).resolveConstantDesc(MethodHandles.publicLookup()).getModule() != Object.class.getModule();
162+
} catch (ReflectiveOperationException _) {
163+
return true;
164+
}
165+
}).sorted().toList();
166+
if (!missingClasses.isEmpty()) {
167+
throw new IllegalStateException("Some referenced classes are not publicly available in java.base module: " + missingClasses);
168+
}
122169
}
123170

124-
static boolean isVisible(int access) {
125-
return (access & (Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) != 0;
171+
/** returns all superclasses, interfaces, and outer classes of the parsed class as stream of internal names */
172+
private static Stream<String> getReferences(ClassModel parsed) {
173+
var parents = Stream.concat(parsed.superclass().stream(), parsed.interfaces().stream())
174+
.map(ClassEntry::asInternalName).collect(Collectors.toSet());
175+
var outerClasses = parsed.findAttributes(Attributes.innerClasses()).stream()
176+
.flatMap(a -> a.classes().stream())
177+
.filter(i -> parents.contains(i.innerClass().asInternalName()))
178+
.flatMap(i -> i.outerClass().stream())
179+
.map(ClassEntry::asInternalName);
180+
return Stream.concat(parents.stream(), outerClasses);
126181
}
127182

128-
static class Cleaner extends ClassVisitor {
129-
private static final String PREVIEW_ANN = "jdk/internal/javac/PreviewFeature";
130-
private static final String PREVIEW_ANN_DESCR = Type.getObjectType(PREVIEW_ANN).getDescriptor();
131-
132-
private final Set<String> classesToInclude;
133-
private final Map<String, String[]> references;
134-
135-
Cleaner(ClassWriter out, Set<String> classesToInclude, Map<String, String[]> references) {
136-
super(Opcodes.ASM9, out);
137-
this.classesToInclude = classesToInclude;
138-
this.references = references;
139-
}
140-
141-
@Override
142-
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
143-
super.visit(Opcodes.V21, access, name, signature, superName, interfaces);
144-
if (isVisible(access)) {
145-
classesToInclude.add(name);
146-
}
147-
references.put(name, Stream.concat(Stream.of(superName), Arrays.stream(interfaces)).toArray(String[]::new));
148-
}
149-
150-
@Override
151-
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
152-
return Objects.equals(descriptor, PREVIEW_ANN_DESCR) ? null : super.visitAnnotation(descriptor, visible);
153-
}
154-
155-
@Override
156-
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
157-
if (!isVisible(access)) {
158-
return null;
159-
}
160-
return new FieldVisitor(Opcodes.ASM9, super.visitField(access, name, descriptor, signature, value)) {
161-
@Override
162-
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
163-
return Objects.equals(descriptor, PREVIEW_ANN_DESCR) ? null : super.visitAnnotation(descriptor, visible);
164-
}
165-
};
183+
@SuppressWarnings("unchecked") // no idea how to get generics correct!?!
184+
private static <E extends ClassFileElement, B extends ClassFileBuilder<E, B>> void dropPreview(ClassFileBuilder<E, B> builder, E ele) {
185+
switch (ele) {
186+
case RuntimeInvisibleAnnotationsAttribute att -> builder.with((E) RuntimeInvisibleAnnotationsAttribute.of(att.annotations().stream()
187+
.filter(ann -> !CD_PreviewFeature.equals(ann.classSymbol()))
188+
.toList()));
189+
default -> builder.with(ele);
166190
}
167-
168-
@Override
169-
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
170-
if (!isVisible(access)) {
171-
return null;
172-
}
173-
return new MethodVisitor(Opcodes.ASM9, super.visitMethod(access, name, descriptor, signature, exceptions)) {
174-
@Override
175-
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
176-
return Objects.equals(descriptor, PREVIEW_ANN_DESCR) ? null : super.visitAnnotation(descriptor, visible);
177-
}
178-
};
179-
}
180-
181-
@Override
182-
public void visitInnerClass(String name, String outerName, String innerName, int access) {
183-
if (!Objects.equals(outerName, PREVIEW_ANN)) {
184-
super.visitInnerClass(name, outerName, innerName, access);
185-
}
186-
}
187-
191+
}
192+
193+
private static boolean isVisible(AccessFlags access) {
194+
return access.has(AccessFlag.PUBLIC) || access.has(AccessFlag.PROTECTED);
188195
}
189196

190197
}

gradle/libs.versions.toml

+1-4
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
[versions]
33
# expressions/ grammars
44
antlr = "4.13.2"
5-
# expressions/ bytecode, gradle extract-jdk-apis
6-
asm = "9.8"
75
# test assertions
86
assertj = "3.27.3"
97
# analysis/phonetic/ algorithms, gradle build checksums
@@ -61,12 +59,11 @@ spatial4j = "0.8"
6159
# benchmark/ XML parsing
6260
xerces = "2.12.2"
6361
# external datasets decompression
64-
zstd = "1.5.7-2"
62+
zstd = "1.5.7-3"
6563

6664
[libraries]
6765
antlr-core = { module = "org.antlr:antlr4", version.ref = "antlr" }
6866
antlr-runtime = { module = "org.antlr:antlr4-runtime", version.ref = "antlr" }
69-
asm-core = { module = "org.ow2.asm:asm", version.ref = "asm" }
7067
assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" }
7168
commons-codec = { module = "commons-codec:commons-codec", version.ref = "commons-codec" }
7269
commons-compress = { module = "org.apache.commons:commons-compress", version.ref = "commons-compress" }

gradlew

+1-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
222222
fi
223223

224224
GRADLE_WRAPPER_JAR="$APP_HOME/gradle/wrapper/gradle-wrapper.jar"
225-
if ! ( cd $APP_HOME/gradle/wrapper && sha256sum --status -c ${GRADLE_WRAPPER_JAR}.sha256 ); then
225+
if ! ( cd "$APP_HOME/gradle/wrapper" && sha256sum --status -c "${GRADLE_WRAPPER_JAR}.sha256" ); then
226226
"$JAVACMD" $JAVA_OPTS "$APP_HOME/build-tools/build-infra/src/main/java/org/apache/lucene/gradle/WrapperDownloader.java" "$GRADLE_WRAPPER_JAR"
227227
WRAPPER_STATUS=$?
228228
if [ "$WRAPPER_STATUS" -eq 1 ]; then

gradlew.bat

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ set GRADLE_WRAPPER_JAR=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
8484
set GRADLE_WRAPPER_CHECKSUM=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar.sha256
8585

8686
@rem Read the expected hash from .sha256 file
87-
for /f "tokens=1" %%A in ("%GRADLE_WRAPPER_CHECKSUM%") do (
87+
for /f "tokens=1 usebackq" %%A in ("%GRADLE_WRAPPER_CHECKSUM%") do (
8888
set "EXPECTED=%%A"
8989
)
9090
@rem Get actual SHA-256 hash using certutil

0 commit comments

Comments
 (0)