Skip to content

Commit 71de47c

Browse files
committed
Use the correct JDK version when indexing Java packages.
Previously, PackageHub was not able to index Java 11 libraries because it always ran the `index-semanticdb` step on Java 8. Now, we shell out to `cs launch lsif-java --jvm JVM_VERSION` to ensure that the `index-semanticdb` command always uses the correct Java version.
1 parent c9124dc commit 71de47c

File tree

9 files changed

+162
-100
lines changed

9 files changed

+162
-100
lines changed

lsif-java/src/main/scala/com/sourcegraph/lsif_java/Dependencies.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ case class Dependencies(
2828
}
2929

3030
object Dependencies {
31+
val empty = Dependencies(Nil, Fetch.Result(), Fetch.Result())
3132
private val cache: FileCache[Task] = FileCache[Task]()
3233
.noCredentials
3334
.withCachePolicies(List(CachePolicy.LocalOnly, CachePolicy.Update))

lsif-java/src/main/scala/com/sourcegraph/lsif_java/buildtools/BuildTool.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ abstract class BuildTool(val name: String, index: IndexCommand) {
1414

1515
def isHidden: Boolean = false
1616

17-
def indexJdk(): Boolean = true
18-
1917
final def sourceroot: Path = index.workingDirectory
2018
final def targetroot: Path =
2119
AbsolutePath

lsif-java/src/main/scala/com/sourcegraph/lsif_java/buildtools/LsifBuildTool.scala

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ import os.ProcessOutput.Readlines
5050
class LsifBuildTool(index: IndexCommand) extends BuildTool("LSIF", index) {
5151

5252
protected def defaultTargetroot: Path = Paths.get("target")
53-
private def configFile = index.workingDirectory.resolve("lsif-java.json")
53+
private def configFile =
54+
index.workingDirectory.resolve(LsifBuildTool.ConfigFileName)
5455
def usedInCurrentDirectory(): Boolean = Files.isRegularFile(configFile)
55-
override def indexJdk(): Boolean = parsedConfig.getOrElse(Config()).indexJdk
5656
override def isHidden: Boolean = true
5757
def generateSemanticdb(): CommandResult = {
5858
parsedConfig match {
@@ -132,7 +132,7 @@ class LsifBuildTool(index: IndexCommand) extends BuildTool("LSIF", index) {
132132
arguments += actualClasspath.mkString(File.pathSeparator)
133133
arguments +=
134134
s"-Xplugin:semanticdb -targetroot:$targetroot -sourceroot:$sourceroot"
135-
if (!config.indexJdk && moduleInfos.nonEmpty) {
135+
if (config.kind == "jdk" && moduleInfos.nonEmpty) {
136136
moduleInfos.foreach { module =>
137137
arguments += "--module"
138138
arguments += module.getParent.getFileName.toString
@@ -192,7 +192,8 @@ class LsifBuildTool(index: IndexCommand) extends BuildTool("LSIF", index) {
192192
private def clean(): Unit = {
193193
Files.walkFileTree(targetroot, new DeleteVisitor)
194194
}
195-
val moduleInfo = Paths.get("module-info.java")
195+
196+
private val moduleInfo = Paths.get("module-info.java")
196197

197198
/** Recursively collects all Java files in the working directory */
198199
private def collectAllJavaFiles(dir: Path): List[Path] = {
@@ -280,10 +281,14 @@ class LsifBuildTool(index: IndexCommand) extends BuildTool("LSIF", index) {
280281
private case class Config(
281282
dependencies: List[Dependency] = Nil,
282283
jvm: String = "8",
283-
indexJdk: Boolean = true
284+
kind: String = ""
284285
)
285286
private object Config {
286287
implicit lazy val codec = moped.macros.deriveCodec(Config())
287288
}
288289

289290
}
291+
292+
object LsifBuildTool {
293+
val ConfigFileName = "lsif-java.json"
294+
}

lsif-java/src/main/scala/com/sourcegraph/lsif_java/commands/IndexCommand.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ case class IndexCommand(
157157
generateSemanticdbResult.exitCode
158158
} else {
159159
IndexSemanticdbCommand(
160-
indexJdk = tool.indexJdk(),
161160
output = finalOutput,
162161
targetroot = List(tool.targetroot),
163162
packagehub = packagehub,

lsif-java/src/main/scala/com/sourcegraph/lsif_java/commands/IndexSemanticdbCommand.scala

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ final case class IndexSemanticdbCommand(
3333
@Description(
3434
"Whether to process the SemanticDB files in parallel"
3535
) parallel: Boolean = true,
36-
@Description(
37-
"Whether to index against symbols in the JDK codebase. " +
38-
"If false, no 'packageInformation' LSIF edges will be emitted for JDK symbols."
39-
) indexJdk: Boolean = true,
4036
@Description("URL to a PackageHub instance")
4137
@Hidden
4238
packagehub: Option[String] = None,
@@ -76,7 +72,6 @@ final case class IndexSemanticdbCommand(
7672
"java",
7773
format,
7874
parallel,
79-
indexJdk,
8075
packages.map(_.toPackageInformation).asJava
8176
)
8277
LsifSemanticdb.run(options)

lsif-semanticdb/src/main/java/com/sourcegraph/lsif_semanticdb/JavaVersion.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,28 @@
11
package com.sourcegraph.lsif_semanticdb;
22

3+
import java.io.DataInputStream;
4+
import java.io.IOException;
5+
import java.io.InputStream;
6+
import java.nio.file.FileSystems;
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
9+
import java.nio.file.PathMatcher;
10+
import java.util.Enumeration;
11+
import java.util.Optional;
12+
import java.util.jar.JarEntry;
13+
import java.util.jar.JarFile;
14+
315
public class JavaVersion {
416
public final boolean isJava8;
517
public final JdkPackage pkg;
18+
private static PathMatcher CLASS_PATTERN =
19+
FileSystems.getDefault().getPathMatcher("glob:**.class");
20+
private static PathMatcher JAR_PATTERN = FileSystems.getDefault().getPathMatcher("glob:**.jar");
21+
22+
public static int JAVA8_VERSION = 8;
23+
public static int JAVA11_VERSION = 11;
24+
public static int DEFAULT_JAVA_VERSION = JAVA8_VERSION;
25+
private static int JAVA0_MAJOR_VERSION = 44;
626

727
public JavaVersion() {
828
this(System.getProperty("java.version"));
@@ -19,4 +39,55 @@ private String javaVersion(String version) {
1939
if (parts.length > 0) return parts[0];
2040
else return version;
2141
}
42+
43+
public static int roundToNearestStableRelease(int version) {
44+
if (version <= JAVA8_VERSION) return JAVA8_VERSION;
45+
if (version <= JAVA11_VERSION) return JAVA11_VERSION;
46+
return version;
47+
}
48+
49+
/**
50+
* Return the JVM version of the given jar/class file.
51+
*
52+
* <p>The JVM version is determined by reading the 5-8th bytes of classfiles, according to the
53+
* Java Language spec. See
54+
* https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-4.html#jvms-4.1
55+
*
56+
* @return the JVM version such as <code>8</code> for Java 8 and <code>11</code> for Java 11.
57+
*/
58+
public static Optional<Integer> classfileJvmVersion(Path file) {
59+
try {
60+
int major = classfileMajorVersion(file);
61+
return major < 0 ? Optional.empty() : Optional.of(major - JAVA0_MAJOR_VERSION);
62+
} catch (IOException e) {
63+
return Optional.empty();
64+
}
65+
}
66+
67+
private static int classfileMajorVersion(Path file) throws IOException {
68+
if (CLASS_PATTERN.matches(file)) {
69+
return classfileMajorVersion(Files.newInputStream(file));
70+
} else if (JAR_PATTERN.matches(file)) {
71+
try (JarFile jar = new JarFile(file.toFile())) {
72+
Enumeration<JarEntry> entries = jar.entries();
73+
while (entries.hasMoreElements()) {
74+
JarEntry entry = entries.nextElement();
75+
if (entry.getName().endsWith(".class")) {
76+
return classfileMajorVersion(jar.getInputStream(entry));
77+
}
78+
}
79+
}
80+
}
81+
82+
return -1;
83+
}
84+
85+
private static int classfileMajorVersion(InputStream classfileBytes) throws IOException {
86+
DataInputStream in = new DataInputStream(classfileBytes);
87+
// See https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-4.html#jvms-4.1
88+
int magic = in.readInt(); // u4 magic
89+
if (magic != 0xCAFEBABE) return -1;
90+
in.readUnsignedShort(); // u2 minor_version
91+
return in.readUnsignedShort(); // u2 major_version
92+
}
2293
}

lsif-semanticdb/src/main/java/com/sourcegraph/lsif_semanticdb/LsifSemanticdbOptions.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ public class LsifSemanticdbOptions {
1515
public final String language;
1616
public final LsifOutputFormat format;
1717
public final boolean parallel;
18-
public final boolean indexJdk;
1918
public final List<MavenPackage> packages;
2019

2120
public LsifSemanticdbOptions(
@@ -27,7 +26,6 @@ public LsifSemanticdbOptions(
2726
String language,
2827
LsifOutputFormat format,
2928
boolean parallel,
30-
boolean indexJdk,
3129
List<MavenPackage> packages) {
3230
this.targetroots = targetroots;
3331
this.output = output;
@@ -37,7 +35,6 @@ public LsifSemanticdbOptions(
3735
this.language = language;
3836
this.format = format;
3937
this.parallel = parallel;
40-
this.indexJdk = indexJdk;
4138
this.packages = packages;
4239
}
4340
}

lsif-semanticdb/src/main/java/com/sourcegraph/lsif_semanticdb/PackageTable.java

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ public class PackageTable implements Function<Package, Integer> {
2626
private final Set<String> cachedJdkSymbols = new HashSet<>();
2727
private final Map<Package, Integer> lsif = new ConcurrentHashMap<>();
2828
private final JavaVersion javaVersion;
29-
private final boolean indexJdk;
3029
private final LsifWriter writer;
3130

3231
private static final PathMatcher JAR_PATTERN =
@@ -37,13 +36,10 @@ public class PackageTable implements Function<Package, Integer> {
3736
public PackageTable(LsifSemanticdbOptions options, LsifWriter writer) throws IOException {
3837
this.writer = writer;
3938
this.javaVersion = new JavaVersion();
40-
this.indexJdk = options.indexJdk;
4139
for (MavenPackage pkg : options.packages) {
4240
indexPackage(pkg);
4341
}
44-
if (indexJdk) {
45-
indexJdk();
46-
}
42+
indexJdk();
4743
}
4844

4945
public void writeImportedSymbol(String symbol, int monikerId) {
@@ -106,7 +102,6 @@ private void indexJdk() throws IOException {
106102
* under the URL "jrt:/".
107103
*/
108104
private boolean isJrtClassfile(String classfile) {
109-
if (!indexJdk) return false;
110105
if (cachedJdkSymbols.contains(classfile)) return true;
111106
URL resource = getClass().getResource("/" + classfile);
112107
boolean isJrt = resource != null && "jrt".equals(resource.getProtocol());
@@ -116,26 +111,6 @@ private boolean isJrtClassfile(String classfile) {
116111
return isJrt;
117112
}
118113

119-
/**
120-
* Returns the equivalent of `Path.toString()` but uses forward slashes on all operating systems
121-
* (including Windows).
122-
*/
123-
private String relativePathToString(Path path) {
124-
StringBuilder out = new StringBuilder();
125-
Iterator<Path> it = path.iterator();
126-
boolean first = true;
127-
while (it.hasNext()) {
128-
if (first) {
129-
first = false;
130-
} else {
131-
out.append('/');
132-
}
133-
String filename = it.next().toString();
134-
out.append(filename);
135-
}
136-
return out.toString();
137-
}
138-
139114
/**
140115
* The boot classpath contains jar files for the JDK in Java 8.
141116
*

0 commit comments

Comments
 (0)