Skip to content

Commit c55dcf8

Browse files
committed
JS: Improve error reporting
1 parent 549d4e9 commit c55dcf8

File tree

1 file changed

+50
-25
lines changed

1 file changed

+50
-25
lines changed

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

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.util.Collections;
1818
import java.util.List;
1919
import java.util.Map;
20+
import java.util.concurrent.TimeUnit;
2021

2122
import com.google.gson.JsonArray;
2223
import com.google.gson.JsonElement;
@@ -37,7 +38,6 @@
3738
import com.semmle.util.exception.InterruptedError;
3839
import com.semmle.util.exception.ResourceError;
3940
import com.semmle.util.exception.UserError;
40-
import com.semmle.util.io.WholeIO;
4141
import com.semmle.util.logging.LogbackUtils;
4242
import com.semmle.util.process.AbstractProcessBuilder;
4343
import com.semmle.util.process.Builder;
@@ -114,6 +114,18 @@ public class TypeScriptParser {
114114
*/
115115
public static final String TYPESCRIPT_NODE_FLAGS = "SEMMLE_TYPESCRIPT_NODE_FLAGS";
116116

117+
/**
118+
* Exit code for Node.js in case of a fatal error from V8. This exit code sometimes occurs
119+
* when the process runs out of memory.
120+
*/
121+
private static final int NODEJS_EXIT_CODE_FATAL_ERROR = 5;
122+
123+
/**
124+
* Exit code for Node.js in case it exits due to <code>SIGABRT</code>. This exit code sometimes occurs
125+
* when the process runs out of memory.
126+
*/
127+
private static final int NODEJS_EXIT_CODE_SIG_ABORT = 128 + 6;
128+
117129
/** The Node.js parser wrapper process, if it has been started already. */
118130
private Process parserWrapperProcess;
119131

@@ -318,15 +330,7 @@ private JsonObject talkToParserWrapper(JsonObject request) {
318330
if (parserWrapperProcess == null) setupParserWrapper();
319331

320332
if (!parserWrapperProcess.isAlive()) {
321-
int exitCode = 0;
322-
try {
323-
exitCode = parserWrapperProcess.waitFor();
324-
} catch (InterruptedException e) {
325-
Exceptions.ignore(e, "This is for diagnostic purposes only.");
326-
}
327-
String err = new WholeIO().strictReadString(parserWrapperProcess.getErrorStream());
328-
throw new CatastrophicError(
329-
"TypeScript parser wrapper terminated with exit code " + exitCode + "; stderr: " + err);
333+
throw getExceptionFromMalformedResponse(null, null);
330334
}
331335

332336
String response = null;
@@ -335,31 +339,52 @@ private JsonObject talkToParserWrapper(JsonObject request) {
335339
toParserWrapper.newLine();
336340
toParserWrapper.flush();
337341
response = fromParserWrapper.readLine();
338-
if (response == null)
339-
throw new CatastrophicError(
340-
"Could not communicate with TypeScript parser wrapper "
341-
+ "(command: "
342-
+ parserWrapperCommand
343-
+ ").");
344-
return new JsonParser().parse(response).getAsJsonObject();
342+
if (response == null || response.isEmpty()) {
343+
throw getExceptionFromMalformedResponse(response, null);
344+
}
345+
try {
346+
return new JsonParser().parse(response).getAsJsonObject();
347+
} catch (JsonParseException | IllegalStateException e) {
348+
throw getExceptionFromMalformedResponse(response, e);
349+
}
345350
} catch (IOException e) {
346351
throw new CatastrophicError(
347352
"Could not communicate with TypeScript parser wrapper "
348353
+ "(command: ."
349354
+ parserWrapperCommand
350355
+ ").",
351356
e);
352-
} catch (JsonParseException | IllegalStateException e) {
353-
throw new CatastrophicError(
354-
"TypeScript parser wrapper sent unexpected response: "
355-
+ response
356-
+ " (command: "
357-
+ parserWrapperCommand
358-
+ ").",
359-
e);
360357
}
361358
}
362359

360+
/**
361+
* Creates an exception object describing the best known reason for the TypeScript parser wrapper
362+
* failing to behave as expected.
363+
*
364+
* Note that the stderr stream is redirected to our stderr so a more descriptive error is likely
365+
* to be found in the log, but we try to make the Java exception descriptive as well.
366+
*/
367+
private RuntimeException getExceptionFromMalformedResponse(String response, Exception e) {
368+
try {
369+
Integer exitCode = null;
370+
if (parserWrapperProcess.waitFor(1L, TimeUnit.SECONDS)) {
371+
exitCode = parserWrapperProcess.waitFor();
372+
}
373+
if (exitCode != null && (exitCode == NODEJS_EXIT_CODE_FATAL_ERROR || exitCode == NODEJS_EXIT_CODE_SIG_ABORT)) {
374+
return new ResourceError("The TypeScript parser wrapper crashed, possibly from running out of memory.", e);
375+
}
376+
if (exitCode != null) {
377+
return new CatastrophicError("The TypeScript parser wrapper crashed with exit code " + exitCode);
378+
}
379+
} catch (InterruptedException e1) {
380+
Exceptions.ignore(e, "This is for diagnostic purposes only.");
381+
}
382+
if (response == null) {
383+
return new CatastrophicError("No response from TypeScript parser wrapper", e);
384+
}
385+
return new CatastrophicError("Unexpected response from TypeScript parser wrapper:\n" + response, e);
386+
}
387+
363388
/**
364389
* Returns the AST for a given source file.
365390
*

0 commit comments

Comments
 (0)