diff options
11 files changed, 565 insertions, 49 deletions
diff --git a/Android.bp b/Android.bp index 67d9d24a4..fffaeb0f6 100644 --- a/Android.bp +++ b/Android.bp @@ -29,7 +29,16 @@ java_library_host { name: "javac_extractor", srcs: [ "kythe/java/com/google/devtools/kythe/extractors/java/standalone/AbstractJavacWrapper.java", - "kythe/java/com/google/devtools/kythe/extractors/java/standalone/Javac9Wrapper.java", + "kythe/java/com/google/devtools/kythe/extractors/java/standalone/JavacWrapper.java", + "kythe/java/com/google/devtools/kythe/extractors/java/standalone/JdkCompatibilityShims.java", + // Currently it is not possible to pick a specific JDK version implementation of + // JdkCompatibilityShim, so only use the fallback (reflective) one. Otherwise, if it becomes + // possible, either through bp file configuration, or once JDK 21 is used consistently, we + // could also select one the options below. + "kythe/java/com/google/devtools/kythe/extractors/java/standalone/ReflectiveJdkCompatibilityShims.java", + // "kythe/java/com/google/devtools/kythe/extractors/java/standalone/9/JdkCompatibilityShimsImpl.java", + // "kythe/java/com/google/devtools/kythe/extractors/java/standalone/15/JdkCompatibilityShimsImpl.java", + // "kythe/java/com/google/devtools/kythe/extractors/java/standalone/21/JdkCompatibilityShimsImpl.java", "kythe/java/com/google/devtools/kythe/extractors/java/*.java", "kythe/java/com/google/devtools/kythe/extractors/shared/*.java", "kythe/java/com/google/devtools/kythe/platform/kzip/*.java", @@ -38,6 +47,7 @@ java_library_host { "kythe/java/com/google/devtools/kythe/platform/shared/CompilationUnits.java", "kythe/java/com/google/devtools/kythe/util/DeleteRecursively.java", "kythe/java/com/google/devtools/kythe/util/JsonUtil.java", + "kythe/java/com/google/devtools/kythe/util/OrderedCompatibilityService.java", ":kythe_protos", "kythe/proto/java.proto", ], @@ -48,7 +58,10 @@ java_library_host { "--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", "--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", "--add-modules java.compiler", + "--add-modules jdk.internal.opt", ], manifest: "javac_extractor_manifest.txt", proto: { @@ -65,6 +78,10 @@ java_library_host { "libprotobuf-java-util-full", "guava", "jsr305", + "auto_service_annotations", + ], + plugins: [ + "auto_service_plugin", ], java_version: "1.9", } diff --git a/javac_extractor_manifest.txt b/javac_extractor_manifest.txt index 7e039cc35..d30b75ecc 100644 --- a/javac_extractor_manifest.txt +++ b/javac_extractor_manifest.txt @@ -1 +1 @@ -Main-Class: com.google.devtools.kythe.extractors.java.standalone.Javac9Wrapper +Main-Class: com.google.devtools.kythe.extractors.java.standalone.JavacWrapper diff --git a/kythe/java/com/google/devtools/kythe/extractors/java/standalone/15/JdkCompatibilityShimsImpl.java b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/15/JdkCompatibilityShimsImpl.java new file mode 100644 index 000000000..e0be7547f --- /dev/null +++ b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/15/JdkCompatibilityShimsImpl.java @@ -0,0 +1,49 @@ +/* + * Copyright 2022 The Kythe Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.devtools.kythe.extractors.java.standalone; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Lists; +import com.sun.tools.javac.main.Arguments; +import com.sun.tools.javac.main.CommandLine; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +/** JdkCompatibilityShims implementation for JDK15-compatible releases. */ +@AutoService(JdkCompatibilityShims.class) +public final class JdkCompatibilityShimsImpl implements JdkCompatibilityShims { + private static final Runtime.Version minVersion = Runtime.Version.parse("15"); + private static final Runtime.Version maxVersion = Runtime.Version.parse("21"); + + public JdkCompatibilityShimsImpl() {} + + @Override + public CompatibilityRange getCompatibleRange() { + return new CompatibilityRange(minVersion, maxVersion); + } + + @Override + public List<String> parseCompilerArguments(String[] args) throws IOException { + return Lists.newArrayList(CommandLine.parse(Arrays.asList(args))); + } + + @Override + public void initializeArguments(Arguments arguments, String[] args) { + arguments.init("kythe_javac", Arrays.asList(args)); + } +} diff --git a/kythe/java/com/google/devtools/kythe/extractors/java/standalone/21/JdkCompatibilityShimsImpl.java b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/21/JdkCompatibilityShimsImpl.java new file mode 100644 index 000000000..a90082ed4 --- /dev/null +++ b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/21/JdkCompatibilityShimsImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright 2023 The Kythe Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.devtools.kythe.extractors.java.standalone; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Lists; +import com.sun.tools.javac.main.Arguments; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import jdk.internal.opt.CommandLine; + +/** JdkCompatibilityShims implementation for JDK21-compatible releases. */ +@AutoService(JdkCompatibilityShims.class) +public final class JdkCompatibilityShimsImpl implements JdkCompatibilityShims { + private static final Runtime.Version minVersion = Runtime.Version.parse("21"); + + public JdkCompatibilityShimsImpl() {} + + @Override + public CompatibilityRange getCompatibleRange() { + return new CompatibilityRange(minVersion); + } + + @Override + public List<String> parseCompilerArguments(String[] args) throws IOException { + return Lists.newArrayList(CommandLine.parse(Arrays.asList(args))); + } + + @Override + public void initializeArguments(Arguments arguments, String[] args) { + arguments.init("kythe_javac", Arrays.asList(args)); + } +} diff --git a/kythe/java/com/google/devtools/kythe/extractors/java/standalone/9/JdkCompatibilityShimsImpl.java b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/9/JdkCompatibilityShimsImpl.java new file mode 100644 index 000000000..45f41ee47 --- /dev/null +++ b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/9/JdkCompatibilityShimsImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright 2022 The Kythe Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.devtools.kythe.extractors.java.standalone; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Lists; +import com.sun.tools.javac.main.Arguments; +import com.sun.tools.javac.main.CommandLine; +import java.io.IOException; +import java.util.List; + +/** JdkCompatibilityShims implementation for JDK9-compatible releases. */ +@AutoService(JdkCompatibilityShims.class) +public final class JdkCompatibilityShimsImpl implements JdkCompatibilityShims { + private static final Runtime.Version minVersion = Runtime.Version.parse("9"); + private static final Runtime.Version maxVersion = Runtime.Version.parse("15"); + + public JdkCompatibilityShimsImpl() {} + + @Override + public CompatibilityRange getCompatibleRange() { + return new CompatibilityRange(minVersion, maxVersion); + } + + @Override + public List<String> parseCompilerArguments(String[] args) throws IOException { + return Lists.newArrayList(CommandLine.parse(args)); + } + + @Override + public void initializeArguments(Arguments arguments, String[] args) { + arguments.init("kythe_javac", args); + } +} diff --git a/kythe/java/com/google/devtools/kythe/extractors/java/standalone/AbstractJavacWrapper.java b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/AbstractJavacWrapper.java index 0c883ac6d..3a84d4a05 100644 --- a/kythe/java/com/google/devtools/kythe/extractors/java/standalone/AbstractJavacWrapper.java +++ b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/AbstractJavacWrapper.java @@ -31,7 +31,6 @@ import com.google.devtools.kythe.extractors.shared.IndexInfoUtils; import com.google.devtools.kythe.proto.Analysis.CompilationUnit; import com.google.devtools.kythe.proto.Analysis.FileData; import com.google.devtools.kythe.util.JsonUtil; -import com.sun.tools.javac.main.CommandLine; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -66,6 +65,7 @@ import java.util.Optional; */ public abstract class AbstractJavacWrapper { private static final Logger logger = Logger.getLogger(AbstractJavacWrapper.class.getName()); + protected static final JdkCompatibilityShims shims = JdkCompatibilityShims.loadBest().get(); protected abstract Collection<CompilationDescription> processCompilation( String[] arguments, JavaCompilationUnitExtractor javaCompilationUnitExtractor) @@ -150,26 +150,7 @@ public abstract class AbstractJavacWrapper { private static String[] getCleanedUpArguments(String[] args) throws IOException { // Expand all @file arguments - // JDK changed the signature of the CommandLine.parse method in JDK15, - // support both versions - List<String> expandedArgs = null; - try { - try { - // JDK15+: the signature is - // List<String> parse(List<String> args); - expandedArgs = (List<String>)CommandLine.class.getMethod("parse", List.class).invoke(null, (Object)Arrays.asList(args)); - } catch (NoSuchMethodException nsme) { - // pre-JDK15: - // String[] parse(String[]); - expandedArgs = Arrays.asList((String[])CommandLine.class.getMethod("parse", String[].class).invoke(null, (Object)args)); - } - } catch (NoSuchMethodException ns2) { - System.err.printf("Cannot locate com.sun.tools.javac.main.CommandLine.parse method, JDK version %s\n", System.getProperty("java.version")); - System.exit(2); - } catch (InvocationTargetException|IllegalAccessException ex) { - System.err.printf("Cannot call com.sun.tools.javac.main.CommandLine.parse (JDK version %s):\n%s\n", System.getProperty("java.version"), ex); - System.exit(2); - } + List<String> expandedArgs = shims.parseCompilerArguments(args); // We skip some arguments that would normally be passed to javac: // -J, these are flags to the java environment running javac. diff --git a/kythe/java/com/google/devtools/kythe/extractors/java/standalone/BUILD b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/BUILD index 8b1a2f9f4..c118ef17a 100644 --- a/kythe/java/com/google/devtools/kythe/extractors/java/standalone/BUILD +++ b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/BUILD @@ -1,5 +1,6 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load("@rules_java//java:defs.bzl", "java_binary", "java_library") +load("//tools:build_rules/selects.bzl", select_with_or = "with_or") package(default_visibility = ["//kythe:default_visibility"]) @@ -7,13 +8,21 @@ exports_files(["javac-wrapper.sh"]) java_binary( name = "javac_extractor", - srcs = ["Javac9Wrapper.java"], + srcs = ["JavacWrapper.java"], javacopts = [ "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", ], - main_class = "com.google.devtools.kythe.extractors.java.standalone.Javac9Wrapper", + jvm_flags = [ + "--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", + ], + main_class = "com.google.devtools.kythe.extractors.java.standalone.JavacWrapper", visibility = ["//visibility:public"], deps = [ ":abstract_javac_wrapper", @@ -31,7 +40,22 @@ java_library( javacopts = [ "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", ], + runtime_deps = [ + ":reflective_jdk_compatibility_shims", + ] + select_with_or({ + ( + "//buildenv/java:language_version_default", + "//buildenv/java:language_version_11", + ): [":jdk9_compatibility_shims"], + ( + "//buildenv/java:language_version_17", + "//buildenv/java:language_version_19", + "//buildenv/java:language_version_20", + "//buildenv/java:language_version_21", + ): [":jdk15_compatibility_shims"], + }), deps = [ + ":jdk_compatibility_shims", "//kythe/java/com/google/devtools/kythe/extractors/java", "//kythe/java/com/google/devtools/kythe/extractors/shared", "//kythe/java/com/google/devtools/kythe/extractors/shared:environment", @@ -43,7 +67,115 @@ java_library( ], ) +java_library( + name = "jdk_compatibility_shims", + srcs = ["JdkCompatibilityShims.java"], + javacopts = [ + "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", + ], + visibility = ["//visibility:private"], + deps = ["//kythe/java/com/google/devtools/kythe/util:ordered_compatibility_service"], +) + +java_library( + name = "jdk9_compatibility_shims", + srcs = ["9/JdkCompatibilityShimsImpl.java"], + javacopts = [ + "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", + "-Xep:PackageLocation:OFF", + ], + target_compatible_with = select_with_or({ + ( + "//buildenv/java:language_version_default", + "//buildenv/java:language_version_11", + ): [], + "//conditions:default": ["@platforms//:incompatible"], + }), + visibility = ["//visibility:private"], + deps = [ + ":jdk_compatibility_shims", + "//kythe/java/com/google/devtools/kythe/common:autoservice", + "//kythe/java/com/google/devtools/kythe/util:ordered_compatibility_service", + "//third_party/guava", + ], +) + +java_library( + name = "jdk15_compatibility_shims", + srcs = ["15/JdkCompatibilityShimsImpl.java"], + javacopts = [ + "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", + "-Xep:PackageLocation:OFF", + ], + # This is incompatible with the default test configuration and + # bazel cquery doesn't work with objc_library targets. + tags = ["manual"], + target_compatible_with = select_with_or({ + ( + "//buildenv/java:language_version_17", + "//buildenv/java:language_version_19", + "//buildenv/java:language_version_20", + ): [], + "//conditions:default": ["@platforms//:incompatible"], + }), + visibility = ["//visibility:private"], + deps = [ + ":jdk_compatibility_shims", + "//kythe/java/com/google/devtools/kythe/common:autoservice", + "//kythe/java/com/google/devtools/kythe/util:ordered_compatibility_service", + "//third_party/guava", + ], +) + +java_library( + name = "jdk21_compatibility_shims", + srcs = ["21/JdkCompatibilityShimsImpl.java"], + javacopts = [ + "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED", + "-Xep:PackageLocation:OFF", + ], + # This is incompatible with the default test configuration and + # bazel cquery doesn't work with objc_library targets. + tags = ["manual"], + target_compatible_with = select_with_or({ + ( + "//buildenv/java:language_version_21", + ): [], + "//conditions:default": ["@platforms//:incompatible"], + }), + visibility = ["//visibility:private"], + deps = [ + ":jdk_compatibility_shims", + "//kythe/java/com/google/devtools/kythe/common:autoservice", + "//kythe/java/com/google/devtools/kythe/util:ordered_compatibility_service", + "//third_party/guava", + ], +) + +java_library( + name = "reflective_jdk_compatibility_shims", + srcs = ["ReflectiveJdkCompatibilityShims.java"], + javacopts = [ + "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", + ] + select_with_or({ + ( + "//buildenv/java:language_version_21", + ): ["--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED"], + "//conditions:default": [], + }), + visibility = ["//visibility:private"], + deps = [ + ":jdk_compatibility_shims", + "//kythe/java/com/google/devtools/kythe/common:autoservice", + "//kythe/java/com/google/devtools/kythe/util:ordered_compatibility_service", + "//third_party/guava", + ], +) + bzl_library( name = "aspect_bzl", srcs = ["aspect.bzl"], + deps = ["@rules_java//java:utils"], ) diff --git a/kythe/java/com/google/devtools/kythe/extractors/java/standalone/Javac9Wrapper.java b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/JavacWrapper.java index 31bc66a7b..291145129 100644 --- a/kythe/java/com/google/devtools/kythe/extractors/java/standalone/Javac9Wrapper.java +++ b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/JavacWrapper.java @@ -37,8 +37,12 @@ import java.util.List; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; -/** A class that wraps javac to extract compilation information and write it to an index file. */ -public class Javac9Wrapper extends AbstractJavacWrapper { +/** + * A class that wraps javac to extract compilation information and write it to an index file. + * + * <p>This class works for Java 9 and later. + */ +public class JavacWrapper extends AbstractJavacWrapper { @Override protected Collection<CompilationDescription> processCompilation( String[] arguments, JavaCompilationUnitExtractor javaCompilationUnitExtractor) @@ -48,27 +52,7 @@ public class Javac9Wrapper extends AbstractJavacWrapper { JavacFileManager fileManager = new JavacFileManager(context, true, null); Arguments args = Arguments.instance(context); - // JDK changed the signature of Arguments.init method, support both - try { - try { - // JDK15+: the signature is - // init(String, Iterable<String> args); - Arguments.class.getMethod("init", String.class, Iterable.class) - .invoke(args, "kythe_javac", (Object) Arrays.asList(arguments)); - } catch (NoSuchMethodException nsme) { - // pre-JDK15: - // init(String, String...); - Arguments.class.getMethod("init", String.class,String[].class) - .invoke(args, "kythe_javac", (Object)arguments); - } - } catch (NoSuchMethodException ns2) { - System.err.printf("Cannot locate com.sun.tools.javac.main.Arguments.init method, JDK version %s\n", System.getProperty("java.version")); - System.exit(2); - } catch (InvocationTargetException|IllegalAccessException ex) { - System.err.printf("Cannot call com.sun.tools.javac.main.Arguments.init (JDK version %s):\n%s\n", System.getProperty("java.version"), ex); - System.exit(2); - } - + shims.initializeArguments(args, arguments); fileManager.handleOptions(args.getDeferredFileManagerOptions()); Options options = Options.instance(context); @@ -173,6 +157,6 @@ public class Javac9Wrapper extends AbstractJavacWrapper { } public static void main(String[] args) { - new Javac9Wrapper().process(args); + new JavacWrapper().process(args); } } diff --git a/kythe/java/com/google/devtools/kythe/extractors/java/standalone/JdkCompatibilityShims.java b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/JdkCompatibilityShims.java new file mode 100644 index 000000000..3c042c5cf --- /dev/null +++ b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/JdkCompatibilityShims.java @@ -0,0 +1,37 @@ +/* + * Copyright 2022 The Kythe Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.devtools.kythe.extractors.java.standalone; + +import com.google.devtools.kythe.util.OrderedCompatibilityService; +import com.sun.tools.javac.main.Arguments; +import java.io.IOException; +import java.util.List; +import java.util.Optional; + +/** Shims for providing source-level compatibility between JDK versions. */ +public interface JdkCompatibilityShims extends OrderedCompatibilityService { + /** Loads the best service provider for this interface, if any. */ + public static Optional<JdkCompatibilityShims> loadBest() { + return OrderedCompatibilityService.loadBest(JdkCompatibilityShims.class); + } + + /** Parse the compiler arguments and returns them as an expanded list. */ + List<String> parseCompilerArguments(String[] args) throws IOException; + + /** Initialize the Arguments instance from the list of args. */ + void initializeArguments(Arguments arguments, String[] args); +} diff --git a/kythe/java/com/google/devtools/kythe/extractors/java/standalone/ReflectiveJdkCompatibilityShims.java b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/ReflectiveJdkCompatibilityShims.java new file mode 100644 index 000000000..80dacbe2f --- /dev/null +++ b/kythe/java/com/google/devtools/kythe/extractors/java/standalone/ReflectiveJdkCompatibilityShims.java @@ -0,0 +1,105 @@ +/* + * Copyright 2022 The Kythe Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.devtools.kythe.extractors.java.standalone; + +import com.google.auto.service.AutoService; +import com.google.common.collect.ImmutableList; +import com.sun.tools.javac.main.Arguments; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +/** JdkCompatibilityShims implementation for JDK9-compatible releases. */ +@AutoService(JdkCompatibilityShims.class) +public final class ReflectiveJdkCompatibilityShims implements JdkCompatibilityShims { + private static final Runtime.Version minVersion = Runtime.Version.parse("9"); + + public ReflectiveJdkCompatibilityShims() {} + + @Override + public CompatibilityRange getCompatibleRange() { + return new CompatibilityRange(minVersion, Optional.empty(), CompatibilityLevel.FALLBACK); + } + + @Override + @SuppressWarnings({"CheckedExceptionNotThrown", "unchecked"}) // Safe by specification. + public List<String> parseCompilerArguments(String[] args) throws IOException { + try { + Class<?> commandLine = loadCommandLineClass(); + try { + // JDK15+: the signature is + // List<String> parse(List<String> args) + return (List<String>) + commandLine.getMethod("parse", List.class).invoke(null, (Object) Arrays.asList(args)); + } catch (NoSuchMethodException nsme) { + // 9 <= JDK < 15: the signature is + // String[] parse(String[] args) + return Arrays.asList( + (String[]) commandLine.getMethod("parse", String[].class).invoke(null, (Object) args)); + } + } catch (ClassCastException + | ClassNotFoundException + | InvocationTargetException + | IllegalAccessException + | NoSuchMethodException ex) { + throw new LinkageError(ex.getMessage(), ex); + } + } + + @Override + public void initializeArguments(Arguments arguments, String[] args) { + try { + try { + // JDK15+: the signature is + // init(String, Iterable<String> args); + arguments + .getClass() + .getMethod("init", String.class, Iterable.class) + .invoke(arguments, "kythe_javac", (Object) Arrays.asList(args)); + } catch (NoSuchMethodException nsme) { + // pre-JDK15: + // init(String, String...); + arguments + .getClass() + .getMethod("init", String.class, String[].class) + .invoke(arguments, "kythe_javac", (Object) args); + } + } catch (ClassCastException + | InvocationTargetException + | IllegalAccessException + | NoSuchMethodException ex) { + throw new LinkageError(ex.getMessage(), ex); + } + } + + private static Class<?> loadCommandLineClass() throws ClassNotFoundException { + ImmutableList<String> classNames = + // < JDK21 the class is known as com.sun.tools.java.main.CommandLine + // >= JDK21 it's known as jdk.internal.opt.CommandLine + ImmutableList.of("com.sun.tools.javac.main.CommandLine", "jdk.internal.opt.CommandLine"); + for (String name : classNames) { + try { + return ReflectiveJdkCompatibilityShims.class.getClassLoader().loadClass(name); + } catch (ClassNotFoundException unused) { + continue; + } + } + throw new ClassNotFoundException("unable to load JDK CommandLine class"); + } +} diff --git a/kythe/java/com/google/devtools/kythe/util/OrderedCompatibilityService.java b/kythe/java/com/google/devtools/kythe/util/OrderedCompatibilityService.java new file mode 100644 index 000000000..84db8c4cd --- /dev/null +++ b/kythe/java/com/google/devtools/kythe/util/OrderedCompatibilityService.java @@ -0,0 +1,115 @@ +/* + * Copyright 2022 The Kythe Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.devtools.kythe.util; + +import com.google.common.collect.Streams; +import java.util.Comparator; +import java.util.Optional; +import java.util.ServiceLoader; + +/** + * OrderedCompatibilityService defines shared functionality between between JdkCompatibilityShim + * interfaces. + */ +public interface OrderedCompatibilityService { + + /** The compatibility level of the provider. */ + public enum CompatibilityLevel { + /** This provider is incompatible with the current runtime. */ + INCOMPATIBLE, + /** + * This provider is compatible with the current runtime, but should only be used if a COMPATIBLE + * provider can't be found. + */ + FALLBACK, + /** + * This provider is compatible with the current runtime and should be preferred over a fallback, + * if any. + */ + COMPATIBLE + } + + /** Indicates the range of runtime versions with which these shims are compatible. */ + public static class CompatibilityRange { + private final Runtime.Version minVersion; + private final Optional<Runtime.Version> maxVersion; + private final CompatibilityLevel level; + + public CompatibilityRange(Runtime.Version minVersion) { + this(minVersion, Optional.empty(), CompatibilityLevel.COMPATIBLE); + } + + public CompatibilityRange(Runtime.Version minVersion, Runtime.Version maxVersion) { + this(minVersion, Optional.of(maxVersion), CompatibilityLevel.COMPATIBLE); + } + + public CompatibilityRange( + Runtime.Version minVersion, + Optional<Runtime.Version> maxVersion, + CompatibilityLevel level) { + this.minVersion = minVersion; + this.maxVersion = maxVersion; + this.level = level; + } + + /** Returns the minimum known compatible runtime version. */ + public Runtime.Version getMinVersion() { + return minVersion; + } + + /** Returns the maximum known compatible runtime version, if any. */ + public Optional<Runtime.Version> getMaxVersion() { + return maxVersion; + } + + /** Returns the compatibility level. */ + public CompatibilityLevel getCompatibilityLevel() { + return level; + } + } + + /** Returns the compatibility level of the service provider. */ + CompatibilityRange getCompatibleRange(); + + public static <S extends OrderedCompatibilityService> Optional<S> loadBest(Class<S> klass) { + Runtime.Version version = Runtime.version(); + return Streams.stream(ServiceLoader.load(klass)) + // Filter out incompatible providers. + .filter(p -> isCompatible(version, p.getCompatibleRange())) + // Then find the best. + .max(OrderedCompatibilityService::compareProviders); + } + + private static boolean isCompatible(Runtime.Version version, CompatibilityRange range) { + return range.getCompatibilityLevel() != CompatibilityLevel.INCOMPATIBLE + && version.compareToIgnoreOptional(range.getMinVersion()) >= 0 + && range.getMaxVersion().map(max -> version.compareToIgnoreOptional(max) < 0).orElse(true); + } + + private static int compareCompatibilityRanges(CompatibilityRange lhs, CompatibilityRange rhs) { + return Comparator.comparing(CompatibilityRange::getCompatibilityLevel) + .thenComparing(CompatibilityRange::getMinVersion, Runtime.Version::compareToIgnoreOptional) + .compare(lhs, rhs); + } + + private static <S extends OrderedCompatibilityService> int compareProviders(S lhs, S rhs) { + return Comparator.comparing( + OrderedCompatibilityService::getCompatibleRange, + OrderedCompatibilityService::compareCompatibilityRanges) + .compare(lhs, rhs); + } +} |