1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 import org.mozilla.javascript.*; 8 import java.io.*; 9 10 /** 11 * The shell program. 12 * 13 * Can execute scripts interactively or in batch mode at the command line. 14 * An example of controlling the JavaScript engine. 15 * 16 * @author Norris Boyd 17 */ 18 public class Shell extends ScriptableObject 19 { 20 private static final long serialVersionUID = -5638074146250193112L; 21 22 @Override getClassName()23 public String getClassName() 24 { 25 return "global"; 26 } 27 28 /** 29 * Main entry point. 30 * 31 * Process arguments as would a normal Java program. Also 32 * create a new Context and associate it with the current thread. 33 * Then set up the execution environment and begin to 34 * execute scripts. 35 */ main(String args[])36 public static void main(String args[]) { 37 // Associate a new Context with this thread 38 Context cx = Context.enter(); 39 try { 40 // Initialize the standard objects (Object, Function, etc.) 41 // This must be done before scripts can be executed. 42 Shell shell = new Shell(); 43 cx.initStandardObjects(shell); 44 45 // Define some global functions particular to the shell. Note 46 // that these functions are not part of ECMA. 47 String[] names = { "print", "quit", "version", "load", "help" }; 48 shell.defineFunctionProperties(names, Shell.class, 49 ScriptableObject.DONTENUM); 50 51 args = processOptions(cx, args); 52 53 // Set up "arguments" in the global scope to contain the command 54 // line arguments after the name of the script to execute 55 Object[] array; 56 if (args.length == 0) { 57 array = new Object[0]; 58 } else { 59 int length = args.length - 1; 60 array = new Object[length]; 61 System.arraycopy(args, 1, array, 0, length); 62 } 63 Scriptable argsObj = cx.newArray(shell, array); 64 shell.defineProperty("arguments", argsObj, 65 ScriptableObject.DONTENUM); 66 67 shell.processSource(cx, args.length == 0 ? null : args[0]); 68 } finally { 69 Context.exit(); 70 } 71 } 72 73 /** 74 * Parse arguments. 75 */ processOptions(Context cx, String args[])76 public static String[] processOptions(Context cx, String args[]) { 77 for (int i=0; i < args.length; i++) { 78 String arg = args[i]; 79 if (!arg.startsWith("-")) { 80 String[] result = new String[args.length - i]; 81 for (int j=i; j < args.length; j++) 82 result[j-i] = args[j]; 83 return result; 84 } 85 if (arg.equals("-version")) { 86 if (++i == args.length) 87 usage(arg); 88 double d = Context.toNumber(args[i]); 89 if (d != d) 90 usage(arg); 91 cx.setLanguageVersion((int) d); 92 continue; 93 } 94 usage(arg); 95 } 96 return new String[0]; 97 } 98 99 /** 100 * Print a usage message. 101 */ usage(String s)102 private static void usage(String s) { 103 p("Didn't understand \"" + s + "\"."); 104 p("Valid arguments are:"); 105 p("-version 100|110|120|130|140|150|160|170"); 106 System.exit(1); 107 } 108 109 /** 110 * Print a help message. 111 * 112 * This method is defined as a JavaScript function. 113 */ help()114 public void help() { 115 p(""); 116 p("Command Description"); 117 p("======= ==========="); 118 p("help() Display usage and help messages. "); 119 p("defineClass(className) Define an extension using the Java class"); 120 p(" named with the string argument. "); 121 p(" Uses ScriptableObject.defineClass(). "); 122 p("load(['foo.js', ...]) Load JavaScript source files named by "); 123 p(" string arguments. "); 124 p("loadClass(className) Load a class named by a string argument."); 125 p(" The class must be a script compiled to a"); 126 p(" class file. "); 127 p("print([expr ...]) Evaluate and print expressions. "); 128 p("quit() Quit the shell. "); 129 p("version([number]) Get or set the JavaScript version number."); 130 p(""); 131 } 132 133 /** 134 * Print the string values of its arguments. 135 * 136 * This method is defined as a JavaScript function. 137 * Note that its arguments are of the "varargs" form, which 138 * allows it to handle an arbitrary number of arguments 139 * supplied to the JavaScript function. 140 * 141 */ print(Context cx, Scriptable thisObj, Object[] args, Function funObj)142 public static void print(Context cx, Scriptable thisObj, 143 Object[] args, Function funObj) 144 { 145 for (int i=0; i < args.length; i++) { 146 if (i > 0) 147 System.out.print(" "); 148 149 // Convert the arbitrary JavaScript value into a string form. 150 String s = Context.toString(args[i]); 151 152 System.out.print(s); 153 } 154 System.out.println(); 155 } 156 157 /** 158 * Quit the shell. 159 * 160 * This only affects the interactive mode. 161 * 162 * This method is defined as a JavaScript function. 163 */ quit()164 public void quit() 165 { 166 quitting = true; 167 } 168 169 /** 170 * Get and set the language version. 171 * 172 * This method is defined as a JavaScript function. 173 */ version(Context cx, Scriptable thisObj, Object[] args, Function funObj)174 public static double version(Context cx, Scriptable thisObj, 175 Object[] args, Function funObj) 176 { 177 double result = cx.getLanguageVersion(); 178 if (args.length > 0) { 179 double d = Context.toNumber(args[0]); 180 cx.setLanguageVersion((int) d); 181 } 182 return result; 183 } 184 185 /** 186 * Load and execute a set of JavaScript source files. 187 * 188 * This method is defined as a JavaScript function. 189 * 190 */ load(Context cx, Scriptable thisObj, Object[] args, Function funObj)191 public static void load(Context cx, Scriptable thisObj, 192 Object[] args, Function funObj) 193 { 194 Shell shell = (Shell)getTopLevelScope(thisObj); 195 for (int i = 0; i < args.length; i++) { 196 shell.processSource(cx, Context.toString(args[i])); 197 } 198 } 199 200 201 /** 202 * Evaluate JavaScript source. 203 * 204 * @param cx the current context 205 * @param filename the name of the file to compile, or null 206 * for interactive mode. 207 */ processSource(Context cx, String filename)208 private void processSource(Context cx, String filename) 209 { 210 if (filename == null) { 211 BufferedReader in = new BufferedReader 212 (new InputStreamReader(System.in)); 213 String sourceName = "<stdin>"; 214 int lineno = 1; 215 boolean hitEOF = false; 216 do { 217 int startline = lineno; 218 System.err.print("js> "); 219 System.err.flush(); 220 try { 221 String source = ""; 222 // Collect lines of source to compile. 223 while(true) { 224 String newline; 225 newline = in.readLine(); 226 if (newline == null) { 227 hitEOF = true; 228 break; 229 } 230 source = source + newline + "\n"; 231 lineno++; 232 // Continue collecting as long as more lines 233 // are needed to complete the current 234 // statement. stringIsCompilableUnit is also 235 // true if the source statement will result in 236 // any error other than one that might be 237 // resolved by appending more source. 238 if (cx.stringIsCompilableUnit(source)) 239 break; 240 } 241 Object result = cx.evaluateString(this, source, 242 sourceName, startline, 243 null); 244 if (result != Context.getUndefinedValue()) { 245 System.err.println(Context.toString(result)); 246 } 247 } 248 catch (WrappedException we) { 249 // Some form of exception was caught by JavaScript and 250 // propagated up. 251 System.err.println(we.getWrappedException().toString()); 252 we.printStackTrace(); 253 } 254 catch (EvaluatorException ee) { 255 // Some form of JavaScript error. 256 System.err.println("js: " + ee.getMessage()); 257 } 258 catch (JavaScriptException jse) { 259 // Some form of JavaScript error. 260 System.err.println("js: " + jse.getMessage()); 261 } 262 catch (IOException ioe) { 263 System.err.println(ioe.toString()); 264 } 265 if (quitting) { 266 // The user executed the quit() function. 267 break; 268 } 269 } while (!hitEOF); 270 System.err.println(); 271 } else { 272 FileReader in = null; 273 try { 274 in = new FileReader(filename); 275 } 276 catch (FileNotFoundException ex) { 277 Context.reportError("Couldn't open file \"" + filename + "\"."); 278 return; 279 } 280 281 try { 282 // Here we evalute the entire contents of the file as 283 // a script. Text is printed only if the print() function 284 // is called. 285 cx.evaluateReader(this, in, filename, 1, null); 286 } 287 catch (WrappedException we) { 288 System.err.println(we.getWrappedException().toString()); 289 we.printStackTrace(); 290 } 291 catch (EvaluatorException ee) { 292 System.err.println("js: " + ee.getMessage()); 293 } 294 catch (JavaScriptException jse) { 295 System.err.println("js: " + jse.getMessage()); 296 } 297 catch (IOException ioe) { 298 System.err.println(ioe.toString()); 299 } 300 finally { 301 try { 302 in.close(); 303 } 304 catch (IOException ioe) { 305 System.err.println(ioe.toString()); 306 } 307 } 308 } 309 } 310 p(String s)311 private static void p(String s) { 312 System.out.println(s); 313 } 314 315 private boolean quitting; 316 } 317 318