diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
index 8eadda7e63a14..101b8094fdd24 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
@@ -86,8 +86,6 @@
* XML
is also supported
*
LGTM_INDEX_XML_MODE
: whether to extract XML files
* LGTM_THREADS
: the maximum number of files to extract in parallel
- * LGTM_TRAP_CACHE
: the path of a directory to use for trap caching
- * LGTM_TRAP_CACHE_BOUND
: the size to bound the trap cache to
*
*
* It extracts the following:
@@ -220,7 +218,7 @@ public AutoBuild() {
this.LGTM_SRC = toRealPath(getPathFromEnvVar("LGTM_SRC"));
this.SEMMLE_DIST = Paths.get(EnvironmentVariables.getExtractorRoot());
this.outputConfig = new ExtractorOutputConfig(LegacyLanguage.JAVASCRIPT);
- this.trapCache = mkTrapCache();
+ this.trapCache = ITrapCache.fromExtractorOptions();
this.typeScriptMode =
getEnumFromEnvVar("LGTM_INDEX_TYPESCRIPT", TypeScriptMode.class, TypeScriptMode.FULL);
this.defaultEncoding = getEnvVar("LGTM_INDEX_DEFAULT_ENCODING");
@@ -281,28 +279,6 @@ private Path toRealPath(Path p) {
}
}
- /**
- * Set up TRAP cache based on environment variables LGTM_TRAP_CACHE
and
- * LGTM_TRAP_CACHE_BOUND
.
- */
- private ITrapCache mkTrapCache() {
- ITrapCache trapCache;
- String trapCachePath = getEnvVar("LGTM_TRAP_CACHE");
- if (trapCachePath != null) {
- Long sizeBound = null;
- String trapCacheBound = getEnvVar("LGTM_TRAP_CACHE_BOUND");
- if (trapCacheBound != null) {
- sizeBound = DefaultTrapCache.asFileSize(trapCacheBound);
- if (sizeBound == null)
- throw new UserError("Invalid TRAP cache size bound: " + trapCacheBound);
- }
- trapCache = new DefaultTrapCache(trapCachePath, sizeBound, Main.EXTRACTOR_VERSION);
- } else {
- trapCache = new DummyTrapCache();
- }
- return trapCache;
- }
-
private void setupFileTypes() {
for (String spec : Main.NEWLINE.split(getEnvVar("LGTM_INDEX_FILETYPES", ""))) {
spec = spec.trim();
@@ -513,14 +489,13 @@ private void extractExterns() throws IOException {
SEMMLE_DIST.resolve(".cache").resolve("trap-cache").resolve("javascript");
if (Files.isDirectory(trapCachePath)) {
trapCache =
- new DefaultTrapCache(trapCachePath.toString(), null, Main.EXTRACTOR_VERSION) {
+ new DefaultTrapCache(trapCachePath.toString(), null, Main.EXTRACTOR_VERSION, false) {
boolean warnedAboutCacheMiss = false;
@Override
public File lookup(String source, ExtractorConfig config, FileType type) {
File f = super.lookup(source, config, type);
- // only return `f` if it exists; this has the effect of making the cache read-only
- if (f.exists()) return f;
+ if (f != null) return f;
// warn on first failed lookup
if (!warnedAboutCacheMiss) {
warn("Trap cache lookup for externs failed.");
diff --git a/javascript/extractor/src/com/semmle/js/extractor/ExtractorOptionsUtil.java b/javascript/extractor/src/com/semmle/js/extractor/ExtractorOptionsUtil.java
new file mode 100644
index 0000000000000..c58f27b2c8ab8
--- /dev/null
+++ b/javascript/extractor/src/com/semmle/js/extractor/ExtractorOptionsUtil.java
@@ -0,0 +1,12 @@
+package com.semmle.js.extractor;
+
+import com.semmle.util.process.Env;
+
+public class ExtractorOptionsUtil {
+ public static String readExtractorOption(String... option) {
+ StringBuilder name = new StringBuilder("CODEQL_EXTRACTOR_JAVASCRIPT_OPTION");
+ for (String segment : option)
+ name.append("_").append(segment.toUpperCase());
+ return Env.systemEnv().getNonEmpty(name.toString());
+ }
+}
diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java
index c001d22dfa937..9929195bb7725 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/Main.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java
@@ -15,8 +15,6 @@
import com.semmle.js.extractor.ExtractorConfig.Platform;
import com.semmle.js.extractor.ExtractorConfig.SourceType;
import com.semmle.js.extractor.FileExtractor.FileType;
-import com.semmle.js.extractor.trapcache.DefaultTrapCache;
-import com.semmle.js.extractor.trapcache.DummyTrapCache;
import com.semmle.js.extractor.trapcache.ITrapCache;
import com.semmle.js.parser.ParsedProject;
import com.semmle.ts.extractor.TypeExtractor;
@@ -61,8 +59,6 @@ public class Main {
private static final String P_PLATFORM = "--platform";
private static final String P_QUIET = "--quiet";
private static final String P_SOURCE_TYPE = "--source-type";
- private static final String P_TRAP_CACHE = "--trap-cache";
- private static final String P_TRAP_CACHE_BOUND = "--trap-cache-bound";
private static final String P_TYPESCRIPT = "--typescript";
private static final String P_TYPESCRIPT_FULL = "--typescript-full";
private static final String P_TYPESCRIPT_RAM = "--typescript-ram";
@@ -112,22 +108,7 @@ public void run(String[] args) {
ap.parse();
extractorConfig = parseJSOptions(ap);
- ITrapCache trapCache;
- if (ap.has(P_TRAP_CACHE)) {
- Long sizeBound = null;
- if (ap.has(P_TRAP_CACHE_BOUND)) {
- String tcb = ap.getString(P_TRAP_CACHE_BOUND);
- sizeBound = DefaultTrapCache.asFileSize(tcb);
- if (sizeBound == null) ap.error("Invalid TRAP cache size bound: " + tcb);
- }
- trapCache = new DefaultTrapCache(ap.getString(P_TRAP_CACHE), sizeBound, EXTRACTOR_VERSION);
- } else {
- if (ap.has(P_TRAP_CACHE_BOUND))
- ap.error(
- P_TRAP_CACHE_BOUND + " should only be specified together with " + P_TRAP_CACHE + ".");
- trapCache = new DummyTrapCache();
- }
- fileExtractor = new FileExtractor(extractorConfig, extractorOutputConfig, trapCache);
+ fileExtractor = new FileExtractor(extractorConfig, extractorOutputConfig, ITrapCache.fromExtractorOptions());
setupMatchers(ap);
@@ -432,12 +413,6 @@ private ArgsParser addArgs(ArgsParser argsParser) {
argsParser.addToleratedFlag(P_TOLERATE_PARSE_ERRORS, 0);
argsParser.addFlag(
P_ABORT_ON_PARSE_ERRORS, 0, "Abort extraction if a parse error is encountered.");
- argsParser.addFlag(P_TRAP_CACHE, 1, "Use the given directory as the TRAP cache.");
- argsParser.addFlag(
- P_TRAP_CACHE_BOUND,
- 1,
- "A (soft) upper limit on the size of the TRAP cache, "
- + "in standard size units (e.g., 'g' for gigabytes).");
argsParser.addFlag(P_DEFAULT_ENCODING, 1, "The encoding to use; default is UTF-8.");
argsParser.addFlag(P_TYPESCRIPT, 0, "Enable basic TypesScript support.");
argsParser.addFlag(
diff --git a/javascript/extractor/src/com/semmle/js/extractor/trapcache/DefaultTrapCache.java b/javascript/extractor/src/com/semmle/js/extractor/trapcache/DefaultTrapCache.java
index 6caa29e8f638e..c96be38c7d217 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/trapcache/DefaultTrapCache.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/trapcache/DefaultTrapCache.java
@@ -26,9 +26,15 @@ public class DefaultTrapCache implements ITrapCache {
*/
private final String extractorVersion;
- public DefaultTrapCache(String trapCache, Long sizeBound, String extractorVersion) {
+ /**
+ * Whether this cache supports write operations.
+ */
+ private final boolean writeable;
+
+ public DefaultTrapCache(String trapCache, Long sizeBound, String extractorVersion, boolean writeable) {
this.trapCache = new File(trapCache);
this.extractorVersion = extractorVersion;
+ this.writeable = writeable;
try {
initCache(sizeBound);
} catch (ResourceError | SecurityException e) {
@@ -135,6 +141,8 @@ public File lookup(String source, ExtractorConfig config, FileType type) {
digestor.write(type.toString());
digestor.write(config);
digestor.write(source);
- return new File(trapCache, digestor.getDigest() + ".trap.gz");
+ File result = new File(trapCache, digestor.getDigest() + ".trap.gz");
+ if (!writeable && !result.exists()) return null; // If the cache isn't writable, only return the file if it exists
+ return result;
}
}
diff --git a/javascript/extractor/src/com/semmle/js/extractor/trapcache/ITrapCache.java b/javascript/extractor/src/com/semmle/js/extractor/trapcache/ITrapCache.java
index 745e930e75e16..728768aa749cf 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/trapcache/ITrapCache.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/trapcache/ITrapCache.java
@@ -1,7 +1,11 @@
package com.semmle.js.extractor.trapcache;
+import static com.semmle.js.extractor.ExtractorOptionsUtil.readExtractorOption;
+
import com.semmle.js.extractor.ExtractorConfig;
import com.semmle.js.extractor.FileExtractor;
+import com.semmle.js.extractor.Main;
+import com.semmle.util.exception.UserError;
import java.io.File;
/** Generic TRAP cache interface. */
@@ -18,4 +22,29 @@ public interface ITrapCache {
* cached information), or does not yet exist (and should be populated by the extractor)
*/
public File lookup(String source, ExtractorConfig config, FileExtractor.FileType type);
+
+ /**
+ * Build a TRAP cache as defined by the extractor options, which are read from the corresponding
+ * environment variables as defined in
+ * https://github.com/github/codeql-core/blob/main/design/spec/codeql-extractors.md
+ *
+ * @return a TRAP cache
+ */
+ public static ITrapCache fromExtractorOptions() {
+ String trapCachePath = readExtractorOption("trap", "cache", "dir");
+ if (trapCachePath != null) {
+ Long sizeBound = null;
+ String trapCacheBound = readExtractorOption("trap", "cache", "bound");
+ if (trapCacheBound != null) {
+ sizeBound = DefaultTrapCache.asFileSize(trapCacheBound);
+ if (sizeBound == null)
+ throw new UserError("Invalid TRAP cache size bound: " + trapCacheBound);
+ }
+ boolean writeable = true;
+ String trapCacheWrite = readExtractorOption("trap", "cache", "write");
+ if (trapCacheWrite != null) writeable = trapCacheWrite.equalsIgnoreCase("TRUE");
+ return new DefaultTrapCache(trapCachePath, sizeBound, Main.EXTRACTOR_VERSION, writeable);
+ }
+ return new DummyTrapCache();
+ }
}