33
33
import java .io .OutputStreamWriter ;
34
34
import java .lang .ProcessBuilder .Redirect ;
35
35
import java .util .ArrayList ;
36
+ import java .util .Arrays ;
37
+ import java .util .Collections ;
36
38
import java .util .List ;
37
39
38
40
/**
39
41
* The Java half of our wrapper for invoking the TypeScript parser.
40
42
*
41
43
* <p>The Node.js half of the wrapper is expected to live at {@code
42
44
* $SEMMLE_DIST/tools/typescript-parser-wrapper/main.js}; non-standard locations can be configured
43
- * using the property {@link #PARSER_WRAPPER_PATH_ENV_VAR}.
45
+ * using the property {@value #PARSER_WRAPPER_PATH_ENV_VAR}.
46
+ *
47
+ * <p>The script launches the Node.js wrapper in the Node.js runtime, looking for {@code node}
48
+ * on the {@code PATH} by default. Non-standard locations can be configured using the property
49
+ * {@value #TYPESCRIPT_NODE_RUNTIME_VAR}, and additional arguments can be configured using the
50
+ * property {@value #TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS_VAR}.
44
51
*
45
52
* <p>The script is started upon parsing the first TypeScript file and then is kept running in the
46
53
* background, passing it requests for parsing files and getting JSON-encoded ASTs as responses.
@@ -52,6 +59,18 @@ public class TypeScriptParser {
52
59
*/
53
60
public static final String PARSER_WRAPPER_PATH_ENV_VAR = "SEMMLE_TYPESCRIPT_PARSER_WRAPPER" ;
54
61
62
+ /**
63
+ * An environment variable that can be set to indicate the ___location of the Node.js runtime,
64
+ * as an alternative to adding Node to the PATH.
65
+ */
66
+ public static final String TYPESCRIPT_NODE_RUNTIME_VAR = "SEMMLE_TYPESCRIPT_NODE_RUNTIME" ;
67
+
68
+ /**
69
+ * An environment variable that can be set to provide additional arguments to the Node.js runtime
70
+ * each time it is invoked. Arguments should be separated by spaces.
71
+ */
72
+ public static final String TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS_VAR = "SEMMLE_TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS" ;
73
+
55
74
/**
56
75
* An environment variable that can be set to specify a timeout to use when verifying the
57
76
* TypeScript installation, in milliseconds. Default is 10000.
@@ -91,6 +110,15 @@ public class TypeScriptParser {
91
110
92
111
private String nodeJsVersionString ;
93
112
113
+ /** Command to launch the Node.js runtime. Initialised by {@link #verifyNodeInstallation}. */
114
+ private String nodeJsRuntime ;
115
+
116
+ /**
117
+ * Arguments to pass to the Node.js runtime each time it is invoked.
118
+ * Initialised by {@link #verifyNodeInstallation}.
119
+ */
120
+ private List <String > nodeJsRuntimeExtraArgs = Collections .emptyList ();
121
+
94
122
/** If non-zero, we use this instead of relying on the corresponding environment variable. */
95
123
private int typescriptRam = 0 ;
96
124
@@ -102,12 +130,16 @@ public void setTypescriptRam(int megabytes) {
102
130
/**
103
131
* Verifies that Node.js and TypeScript are installed and throws an exception otherwise.
104
132
*
105
- * @param verbose if true, log the version strings and NODE_PATH .
133
+ * @param verbose if true, log the Node.js executable path, version strings, and any additional arguments .
106
134
*/
107
135
public void verifyInstallation (boolean verbose ) {
108
136
verifyNodeInstallation ();
109
137
if (verbose ) {
138
+ System .out .println ("Found Node.js at: " + nodeJsRuntime );
110
139
System .out .println ("Found Node.js version: " + nodeJsVersionString );
140
+ if (!nodeJsRuntimeExtraArgs .isEmpty ()) {
141
+ System .out .println ("Additional arguments for Node.js: " + nodeJsRuntimeExtraArgs );
142
+ }
111
143
}
112
144
}
113
145
@@ -117,7 +149,24 @@ public String verifyNodeInstallation() {
117
149
118
150
ByteArrayOutputStream out = new ByteArrayOutputStream ();
119
151
ByteArrayOutputStream err = new ByteArrayOutputStream ();
120
- Builder b = new Builder (out , err , getParserWrapper ().getParentFile (), "node" , "--version" );
152
+
153
+ // Determine where to find the Node.js runtime.
154
+ String explicitNodeJsRuntime = Env .systemEnv ().get (TYPESCRIPT_NODE_RUNTIME_VAR );
155
+ if (explicitNodeJsRuntime != null ) {
156
+ // Use the specified Node.js executable.
157
+ nodeJsRuntime = explicitNodeJsRuntime ;
158
+ } else {
159
+ // Look for `node` on the PATH.
160
+ nodeJsRuntime = "node" ;
161
+ }
162
+
163
+ // Determine any additional arguments to be passed to Node.js each time it's called.
164
+ String extraArgs = Env .systemEnv ().get (TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS_VAR );
165
+ if (extraArgs != null ) {
166
+ nodeJsRuntimeExtraArgs = Arrays .asList (extraArgs .split ("\\ s+" ));
167
+ }
168
+
169
+ Builder b = new Builder (getNodeJsRuntimeInvocation ("--version" ), out , err , getParserWrapper ().getParentFile ());
121
170
b .expectFailure (); // We want to do our own logging in case of an error.
122
171
123
172
int timeout = Env .systemEnv ().getInt (TYPESCRIPT_TIMEOUT_VAR , 10000 );
@@ -144,6 +193,21 @@ public String verifyNodeInstallation() {
144
193
}
145
194
}
146
195
196
+ /**
197
+ * Gets a command line to invoke the Node.js runtime.
198
+ * Any arguments in {@link TypeScriptParser#nodeJsRuntimeExtraArgs}
199
+ * are passed first, followed by those in {@code args}.
200
+ */
201
+ private List <String > getNodeJsRuntimeInvocation (String ...args ) {
202
+ List <String > result = new ArrayList <>();
203
+ result .add (nodeJsRuntime );
204
+ result .addAll (nodeJsRuntimeExtraArgs );
205
+ for (String arg : args ) {
206
+ result .add (arg );
207
+ }
208
+ return result ;
209
+ }
210
+
147
211
private static int getMegabyteCountFromPrefixedEnv (String suffix , int defaultValue ) {
148
212
String envVar = "SEMMLE_" + suffix ;
149
213
String value = Env .systemEnv ().get (envVar );
@@ -172,10 +236,11 @@ private void setupParserWrapper() {
172
236
int reserveMemoryMb = getMegabyteCountFromPrefixedEnv (TYPESCRIPT_RAM_RESERVE_SUFFIX , 400 );
173
237
174
238
File parserWrapper = getParserWrapper ();
175
- List <String > cmd = new ArrayList <>();
176
- cmd .add ("node" );
177
- cmd .add ("--max_old_space_size=" + (mainMemoryMb + reserveMemoryMb ));
178
- cmd .add (parserWrapper .getAbsolutePath ());
239
+
240
+ List <String > cmd = getNodeJsRuntimeInvocation (
241
+ "--max_old_space_size=" + (mainMemoryMb + reserveMemoryMb ),
242
+ parserWrapper .getAbsolutePath ()
243
+ );
179
244
ProcessBuilder pb = new ProcessBuilder (cmd );
180
245
parserWrapperCommand = StringUtil .glue (" " , cmd );
181
246
pb .environment ().put ("SEMMLE_TYPESCRIPT_MEMORY_THRESHOLD" , "" + mainMemoryMb );
0 commit comments