Skip to content

Commit 8632c2a

Browse files
committed
JS: Factor out VirtualSourceRoot
1 parent bfedcb0 commit 8632c2a

File tree

6 files changed

+96
-48
lines changed

6 files changed

+96
-48
lines changed

javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ public class AutoBuild {
211211
private volatile boolean seenCode = false;
212212
private boolean installDependencies = false;
213213
private int installDependenciesTimeout;
214+
private final VirtualSourceRoot virtualSourceRoot;
214215

215216
/** The default timeout when running <code>yarn</code>, in milliseconds. */
216217
public static final int INSTALL_DEPENDENCIES_DEFAULT_TIMEOUT = 10 * 60 * 1000; // 10 minutes
@@ -228,6 +229,7 @@ public AutoBuild() {
228229
Env.systemEnv()
229230
.getInt(
230231
"LGTM_INDEX_TYPESCRIPT_INSTALL_DEPS_TIMEOUT", INSTALL_DEPENDENCIES_DEFAULT_TIMEOUT);
232+
this.virtualSourceRoot = new VirtualSourceRoot(LGTM_SRC, toRealPath(Paths.get(EnvironmentVariables.getScratchDir())));
231233
setupFileTypes();
232234
setupXmlMode();
233235
setupMatchers();
@@ -758,7 +760,6 @@ public static Path tryRelativize(Path from, Path to) {
758760
*/
759761
protected DependencyInstallationResult preparePackagesAndDependencies(Set<Path> filesToExtract) {
760762
final Path sourceRoot = LGTM_SRC;
761-
final Path virtualSourceRoot = toRealPath(Paths.get(EnvironmentVariables.getScratchDir()));
762763

763764
// Read all package.json files and index them by name.
764765
Map<Path, JsonObject> packageJsonFiles = new LinkedHashMap<>();
@@ -845,8 +846,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set<Path>
845846

846847
// Write the new package.json files to disk
847848
for (Path file : packageJsonFiles.keySet()) {
848-
Path relativePath = sourceRoot.relativize(file);
849-
Path virtualFile = virtualSourceRoot.resolve(relativePath);
849+
Path virtualFile = virtualSourceRoot.toVirtualFile(file);
850850

851851
try {
852852
Files.createDirectories(virtualFile.getParent());
@@ -861,7 +861,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set<Path>
861861
// Install dependencies
862862
if (this.installDependencies && verifyYarnInstallation()) {
863863
for (Path file : packageJsonFiles.keySet()) {
864-
Path virtualFile = virtualSourceRoot.resolve(sourceRoot.relativize(file));
864+
Path virtualFile = virtualSourceRoot.toVirtualFile(file);
865865
System.out.println("Installing dependencies from " + virtualFile);
866866
ProcessBuilder pb =
867867
new ProcessBuilder(
@@ -887,7 +887,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set<Path>
887887
}
888888
}
889889

890-
return new DependencyInstallationResult(sourceRoot, virtualSourceRoot, packageMainFile, packagesInRepo);
890+
return new DependencyInstallationResult(packageMainFile, packagesInRepo);
891891
}
892892

893893
/**
@@ -958,6 +958,7 @@ private ExtractorConfig mkExtractorConfig() {
958958
ExtractorConfig config = new ExtractorConfig(true);
959959
config = config.withSourceType(getSourceType());
960960
config = config.withTypeScriptMode(typeScriptMode);
961+
config = config.withVirtualSourceRoot(virtualSourceRoot);
961962
if (defaultEncoding != null) config = config.withDefaultEncoding(defaultEncoding);
962963
return config;
963964
}
@@ -979,15 +980,15 @@ private Set<Path> extractTypeScript(
979980
Set<File> explicitlyIncludedFiles = new LinkedHashSet<>();
980981
if (tsconfig.size() > 1) { // No prioritization needed if there's only one tsconfig.
981982
for (Path projectPath : tsconfig) {
982-
explicitlyIncludedFiles.addAll(tsParser.getOwnFiles(projectPath.toFile(), deps));
983+
explicitlyIncludedFiles.addAll(tsParser.getOwnFiles(projectPath.toFile(), deps, virtualSourceRoot));
983984
}
984985
}
985986

986987
// Extract TypeScript projects
987988
for (Path projectPath : tsconfig) {
988989
File projectFile = projectPath.toFile();
989990
long start = logBeginProcess("Opening project " + projectFile);
990-
ParsedProject project = tsParser.openProject(projectFile, deps);
991+
ParsedProject project = tsParser.openProject(projectFile, deps, virtualSourceRoot);
991992
logEndProcess(start, "Done opening project " + projectFile);
992993
// Extract all files belonging to this project which are also matched
993994
// by our include/exclude filters.

javascript/extractor/src/com/semmle/js/extractor/DependencyInstallationResult.java

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,46 +6,19 @@
66

77
/** Contains the results of installing dependencies. */
88
public class DependencyInstallationResult {
9-
private Path sourceRoot;
10-
private Path virtualSourceRoot;
119
private Map<String, Path> packageEntryPoints;
1210
private Map<String, Path> packageJsonFiles;
1311

1412
public static final DependencyInstallationResult empty =
15-
new DependencyInstallationResult(null, null, Collections.emptyMap(), Collections.emptyMap());
13+
new DependencyInstallationResult(Collections.emptyMap(), Collections.emptyMap());
1614

1715
public DependencyInstallationResult(
18-
Path sourceRoot,
19-
Path virtualSourceRoot,
2016
Map<String, Path> packageEntryPoints,
2117
Map<String, Path> packageJsonFiles) {
22-
this.sourceRoot = sourceRoot;
23-
this.virtualSourceRoot = virtualSourceRoot;
2418
this.packageEntryPoints = packageEntryPoints;
2519
this.packageJsonFiles = packageJsonFiles;
2620
}
2721

28-
/**
29-
* Returns the source root mirrored by {@link #getVirtualSourceRoot()} or <code>null</code>
30-
* if no virtual source root exists.
31-
* <p>
32-
* When invoked from the AutoBuilder, this corresponds to the source root. When invoked
33-
* from ODASA, there is no notion of source root, so this is always <code>null</code> in that context.
34-
*/
35-
public Path getSourceRoot() {
36-
return sourceRoot;
37-
}
38-
39-
/**
40-
* Returns the virtual source root or <code>null</code> if no virtual source root exists.
41-
* <p>
42-
* The virtual source root is a directory hierarchy that mirrors the real source
43-
* root, where dependencies are installed.
44-
*/
45-
public Path getVirtualSourceRoot() {
46-
return virtualSourceRoot;
47-
}
48-
4922
/**
5023
* Returns the mapping from package names to the TypeScript file that should
5124
* act as its main entry point.

javascript/extractor/src/com/semmle/js/extractor/ExtractorConfig.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
package com.semmle.js.extractor;
22

3-
import com.semmle.js.parser.JcornWrapper;
4-
import com.semmle.util.data.StringUtil;
5-
import com.semmle.util.exception.UserError;
63
import java.nio.charset.Charset;
74
import java.nio.charset.IllegalCharsetNameException;
85
import java.nio.charset.StandardCharsets;
@@ -12,6 +9,10 @@
129
import java.util.LinkedHashSet;
1310
import java.util.Set;
1411

12+
import com.semmle.js.parser.JcornWrapper;
13+
import com.semmle.util.data.StringUtil;
14+
import com.semmle.util.exception.UserError;
15+
1516
/**
1617
* Configuration options that affect the behaviour of the extractor.
1718
*
@@ -235,6 +236,8 @@ public String toString() {
235236

236237
/** The default character encoding to use for parsing source files. */
237238
private String defaultEncoding;
239+
240+
private VirtualSourceRoot virtualSourceRoot;
238241

239242
public ExtractorConfig(boolean experimental) {
240243
this.ecmaVersion = experimental ? ECMAVersion.ECMA2020 : ECMAVersion.ECMA2019;
@@ -252,6 +255,7 @@ public ExtractorConfig(boolean experimental) {
252255
this.typescriptMode = TypeScriptMode.NONE;
253256
this.e4x = experimental;
254257
this.defaultEncoding = StandardCharsets.UTF_8.name();
258+
this.virtualSourceRoot = VirtualSourceRoot.none;
255259
}
256260

257261
public ExtractorConfig(ExtractorConfig that) {
@@ -272,6 +276,7 @@ public ExtractorConfig(ExtractorConfig that) {
272276
this.typescriptMode = that.typescriptMode;
273277
this.typescriptRam = that.typescriptRam;
274278
this.defaultEncoding = that.defaultEncoding;
279+
this.virtualSourceRoot = that.virtualSourceRoot;
275280
}
276281

277282
public ECMAVersion getEcmaVersion() {
@@ -452,6 +457,16 @@ public ExtractorConfig withDefaultEncoding(String defaultEncoding) {
452457
return res;
453458
}
454459

460+
public VirtualSourceRoot getVirtualSourceRoot() {
461+
return virtualSourceRoot;
462+
}
463+
464+
public ExtractorConfig withVirtualSourceRoot(VirtualSourceRoot virtualSourceRoot) {
465+
ExtractorConfig res = new ExtractorConfig(this);
466+
res.virtualSourceRoot = virtualSourceRoot;
467+
return res;
468+
}
469+
455470
@Override
456471
public String toString() {
457472
return "ExtractorConfig [ecmaVersion="
@@ -486,6 +501,8 @@ public String toString() {
486501
+ typescriptMode
487502
+ ", defaultEncoding="
488503
+ defaultEncoding
504+
+ ", virtualSourceRoot="
505+
+ virtualSourceRoot
489506
+ "]";
490507
}
491508
}

javascript/extractor/src/com/semmle/js/extractor/Main.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public void run(String[] args) {
152152
for (File projectFile : projectFiles) {
153153

154154
long start = verboseLogStartTimer(ap, "Opening project " + projectFile);
155-
ParsedProject project = tsParser.openProject(projectFile, DependencyInstallationResult.empty);
155+
ParsedProject project = tsParser.openProject(projectFile, DependencyInstallationResult.empty, VirtualSourceRoot.none);
156156
verboseLogEndTimer(ap, start);
157157
// Extract all files belonging to this project which are also matched
158158
// by our include/exclude filters.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.semmle.js.extractor;
2+
3+
import java.nio.file.Path;
4+
5+
public class VirtualSourceRoot {
6+
private Path sourceRoot;
7+
private Path virtualSourceRoot;
8+
9+
public static final VirtualSourceRoot none = new VirtualSourceRoot(null, null);
10+
11+
public VirtualSourceRoot(Path sourceRoot, Path virtualSourceRoot) {
12+
this.sourceRoot = sourceRoot;
13+
this.virtualSourceRoot = virtualSourceRoot;
14+
}
15+
16+
/**
17+
* Returns the source root mirrored by {@link #getVirtualSourceRoot()} or <code>null</code> if no
18+
* virtual source root exists.
19+
*
20+
* <p>When invoked from the AutoBuilder, this corresponds to the source root. When invoked from
21+
* ODASA, there is no notion of source root, so this is always <code>null</code> in that context.
22+
*/
23+
public Path getSourceRoot() {
24+
return sourceRoot;
25+
}
26+
27+
/**
28+
* Returns the virtual source root or <code>null</code> if no virtual source root exists.
29+
*
30+
* <p>The virtual source root is a directory hierarchy that mirrors the real source root, where
31+
* dependencies are installed.
32+
*/
33+
public Path getVirtualSourceRoot() {
34+
return virtualSourceRoot;
35+
}
36+
37+
private static Path translate(Path oldRoot, Path newRoot, Path file) {
38+
if (oldRoot == null || newRoot == null) return null;
39+
Path relative = oldRoot.relativize(file);
40+
if (relative.startsWith("..") || relative.isAbsolute()) return null;
41+
return newRoot.resolve(relative);
42+
}
43+
44+
public Path toVirtualFile(Path file) {
45+
return translate(sourceRoot, virtualSourceRoot, file);
46+
}
47+
48+
public Path fromVirtualFile(Path file) {
49+
return translate(virtualSourceRoot, sourceRoot, file);
50+
}
51+
52+
@Override
53+
public String toString() {
54+
return "[sourceRoot=" + sourceRoot + ", virtualSourceRoot=" + virtualSourceRoot + "]";
55+
}
56+
}

javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.semmle.js.extractor.DependencyInstallationResult;
3232
import com.semmle.js.extractor.EnvironmentVariables;
3333
import com.semmle.js.extractor.ExtractionMetrics;
34+
import com.semmle.js.extractor.VirtualSourceRoot;
3435
import com.semmle.js.parser.JSParser.Result;
3536
import com.semmle.ts.extractor.TypeTable;
3637
import com.semmle.util.data.StringUtil;
@@ -501,8 +502,8 @@ private static Set<File> getFilesFromJsonArray(JsonArray array) {
501502
/**
502503
* Returns the set of files included by the inclusion pattern in the given tsconfig.json file.
503504
*/
504-
public Set<File> getOwnFiles(File tsConfigFile, DependencyInstallationResult deps) {
505-
JsonObject request = makeLoadCommand("get-own-files", tsConfigFile, deps);
505+
public Set<File> getOwnFiles(File tsConfigFile, DependencyInstallationResult deps, VirtualSourceRoot vroot) {
506+
JsonObject request = makeLoadCommand("get-own-files", tsConfigFile, deps, vroot);
506507
JsonObject response = talkToParserWrapper(request);
507508
try {
508509
checkResponseType(response, "file-list");
@@ -521,8 +522,8 @@ public Set<File> getOwnFiles(File tsConfigFile, DependencyInstallationResult dep
521522
*
522523
* <p>Only one project should be opened at once.
523524
*/
524-
public ParsedProject openProject(File tsConfigFile, DependencyInstallationResult deps) {
525-
JsonObject request = makeLoadCommand("open-project", tsConfigFile, deps);
525+
public ParsedProject openProject(File tsConfigFile, DependencyInstallationResult deps, VirtualSourceRoot vroot) {
526+
JsonObject request = makeLoadCommand("open-project", tsConfigFile, deps, vroot);
526527
JsonObject response = talkToParserWrapper(request);
527528
try {
528529
checkResponseType(response, "project-opened");
@@ -536,18 +537,18 @@ public ParsedProject openProject(File tsConfigFile, DependencyInstallationResult
536537
}
537538
}
538539

539-
private JsonObject makeLoadCommand(String command, File tsConfigFile, DependencyInstallationResult deps) {
540+
private JsonObject makeLoadCommand(String command, File tsConfigFile, DependencyInstallationResult deps, VirtualSourceRoot vroot) {
540541
JsonObject request = new JsonObject();
541542
request.add("command", new JsonPrimitive(command));
542543
request.add("tsConfig", new JsonPrimitive(tsConfigFile.getPath()));
543544
request.add("packageEntryPoints", mapToArray(deps.getPackageEntryPoints()));
544545
request.add("packageJsonFiles", mapToArray(deps.getPackageJsonFiles()));
545-
request.add("sourceRoot", deps.getSourceRoot() == null
546+
request.add("sourceRoot", vroot.getSourceRoot() == null
546547
? JsonNull.INSTANCE
547-
: new JsonPrimitive(deps.getSourceRoot().toString()));
548-
request.add("virtualSourceRoot", deps.getVirtualSourceRoot() == null
548+
: new JsonPrimitive(vroot.getSourceRoot().toString()));
549+
request.add("virtualSourceRoot", vroot.getVirtualSourceRoot() == null
549550
? JsonNull.INSTANCE
550-
: new JsonPrimitive(deps.getVirtualSourceRoot().toString()));
551+
: new JsonPrimitive(vroot.getVirtualSourceRoot().toString()));
551552
return request;
552553
}
553554

0 commit comments

Comments
 (0)