1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 package net.alchim31.maven.yuicompressor;
56
57 import java.io.BufferedReader;
58 import java.io.IOException;
59 import java.io.InputStreamReader;
60 import java.nio.charset.StandardCharsets;
61 import java.nio.file.Files;
62 import java.nio.file.Path;
63
64 import org.mozilla.javascript.Context;
65 import org.mozilla.javascript.ErrorReporter;
66 import org.mozilla.javascript.EvaluatorException;
67 import org.mozilla.javascript.Function;
68 import org.mozilla.javascript.JavaScriptException;
69 import org.mozilla.javascript.Scriptable;
70 import org.mozilla.javascript.ScriptableObject;
71 import org.mozilla.javascript.WrappedException;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
74
75
76
77
78
79
80
81
82
83
84
85 public class BasicRhinoShell extends ScriptableObject {
86
87
88 private static final long serialVersionUID = 1L;
89
90
91 private static final Logger logger = LoggerFactory.getLogger(BasicRhinoShell.class);
92
93
94 private boolean quitting;
95
96 @Override
97 public String getClassName() {
98 return "global";
99 }
100
101
102
103
104
105
106
107
108
109
110
111
112 public static void exec(String[] args, ErrorReporter reporter) {
113
114 Context cx = Context.enter();
115 cx.setErrorReporter(reporter);
116 try {
117
118
119 BasicRhinoShell basicRhinoShell = new BasicRhinoShell();
120 cx.initStandardObjects(basicRhinoShell);
121
122
123
124
125 String[] names = { "print", "quit", "version", "load", "help", "readFile", "warn" };
126 basicRhinoShell.defineFunctionProperties(names, BasicRhinoShell.class, ScriptableObject.DONTENUM);
127
128 args = processOptions(cx, args);
129
130
131
132 Object[] array;
133 if (args.length == 0) {
134 array = new Object[0];
135 } else {
136 int length = args.length - 1;
137 array = new Object[length];
138 System.arraycopy(args, 1, array, 0, length);
139 }
140 Scriptable argsObj = cx.newArray(basicRhinoShell, array);
141 basicRhinoShell.defineProperty("arguments", argsObj, ScriptableObject.DONTENUM);
142
143 basicRhinoShell.processSource(cx, args.length == 0 ? null : args[0]);
144 } finally {
145 Context.exit();
146 }
147 }
148
149
150
151
152
153
154
155
156
157
158
159 public static String[] processOptions(Context cx, String[] args) {
160 for (int i = 0; i < args.length; i++) {
161 String arg = args[i];
162 if (!arg.startsWith("-")) {
163 String[] result = new String[args.length - i];
164 for (int j = i; j < args.length; j++) {
165 result[j - i] = args[j];
166 }
167 return result;
168 }
169 if (arg.equals("-version")) {
170 i++;
171 if (i == args.length) {
172 usage(arg);
173 }
174 double d = Context.toNumber(args[i]);
175 if (Double.isNaN(d)) {
176 usage(arg);
177 }
178 cx.setLanguageVersion((int) d);
179 continue;
180 }
181 usage(arg);
182 }
183 return new String[0];
184 }
185
186
187
188
189
190
191
192 private static void usage(String s) {
193 p("Didn't understand \"" + s + "\".");
194 p("Valid arguments are:");
195 p("-version 100|110|120|130|140|150|160|170");
196 System.exit(1);
197 }
198
199
200
201
202
203
204 public void help() {
205 p("");
206 p("Command Description");
207 p("======= ===========");
208 p("help() Display usage and help messages. ");
209 p("defineClass(className) Define an extension using the Java class");
210 p(" named with the string argument. ");
211 p(" Uses ScriptableObject.defineClass(). ");
212 p("load(['foo.js', ...]) Load JavaScript source files named by ");
213 p(" string arguments. ");
214 p("loadClass(className) Load a class named by a string argument.");
215 p(" The class must be a script compiled to a");
216 p(" class file. ");
217 p("print([expr ...]) Evaluate and print expressions. ");
218 p("quit() Quit the BasicRhinoShell. ");
219 p("version([number]) Get or set the JavaScript version number.");
220 p("");
221 }
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238 public static void print(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
239 for (int i = 0; i < args.length; i++) {
240 if (i > 0) {
241 logger.info("");
242 }
243
244
245 String s = Context.toString(args[i]);
246
247 logger.info(s);
248 }
249 logger.info("");
250 }
251
252
253
254
255
256
257
258
259 public void quit() {
260 quitting = true;
261 }
262
263
264
265
266
267
268
269
270
271
272
273
274
275 public static void warn(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
276 String message = Context.toString(args[0]);
277 int line = (int) Context.toNumber(args[1]);
278 String source = Context.toString(args[2]);
279 int column = (int) Context.toNumber(args[3]);
280 cx.getErrorReporter().warning(message, null, line, source, column);
281 }
282
283
284
285
286
287
288
289
290
291 public String readFile(String path) {
292 try {
293 return new String(Files.readAllBytes(Path.of(path)), StandardCharsets.UTF_8);
294 } catch (RuntimeException exc) {
295 throw exc;
296 } catch (IOException exc) {
297 throw new RuntimeException("wrap: " + exc.getMessage(), exc);
298 }
299 }
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317 public static double version(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
318 double result = cx.getLanguageVersion();
319 if (args.length > 0) {
320 double d = Context.toNumber(args[0]);
321 cx.setLanguageVersion((int) d);
322 }
323 return result;
324 }
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340 public static void load(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
341 BasicRhinoShell basicRhinoShell = (BasicRhinoShell) getTopLevelScope(thisObj);
342 for (Object element : args) {
343 basicRhinoShell.processSource(cx, Context.toString(element));
344 }
345 }
346
347
348
349
350
351
352
353
354
355 private void processSource(Context cx, String filename) {
356 if (filename == null) {
357 BufferedReader in = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));
358 String sourceName = "<stdin>";
359 int lineno = 1;
360 boolean hitEOF = false;
361 do {
362 int startline = lineno;
363 logger.info("js> ");
364 try {
365 StringBuilder source = new StringBuilder();
366
367 while (true) {
368 String newline = in.readLine();
369 if (newline == null) {
370 hitEOF = true;
371 break;
372 }
373 source.append(newline).append("\n");
374 lineno++;
375
376
377
378 if (cx.stringIsCompilableUnit(source.toString())) {
379 break;
380 }
381 }
382 Object result = cx.evaluateString(this, source.toString(), sourceName, startline, null);
383 if (result != Context.getUndefinedValue() && logger.isInfoEnabled()) {
384 logger.info("{}", Context.toString(result));
385 }
386 } catch (WrappedException e) {
387
388 logger.info(e.getWrappedException().toString());
389 logger.error("", e);
390 } catch (EvaluatorException | JavaScriptException e) {
391
392 logger.info("js: {}", e.getMessage());
393 } catch (IOException e) {
394 logger.info(e.toString());
395 }
396 if (quitting) {
397
398 break;
399 }
400 } while (!hitEOF);
401 logger.info("");
402 } else {
403 try (BufferedReader in = Files.newBufferedReader(Path.of(filename), StandardCharsets.UTF_8)) {
404
405
406 cx.evaluateReader(this, in, filename, 1, null);
407 } catch (WrappedException e) {
408 logger.info(e.getWrappedException().toString());
409 logger.error("", e);
410 } catch (EvaluatorException | JavaScriptException e) {
411 logger.info("js: {}", e.getMessage());
412 } catch (IOException e) {
413 logger.error("", e);
414 }
415 }
416 }
417
418
419
420
421
422
423
424 private static void p(String s) {
425 logger.info(s);
426 }
427
428 }