allow the scope to be passed in, so we can support narwhal more easily
[rhinodom.git] / Shell.java
blobdf17ced90bcd55be6cde7676127502dc14f8d5e0
1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is Rhino code, released
17 * May 6, 1998.
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1997-1999
22 * the Initial Developer. All Rights Reserved.
24 * Contributor(s):
26 * Alternatively, the contents of this file may be used under the terms of
27 * the GNU General Public License Version 2 or later (the "GPL"), in which
28 * case the provisions of the GPL are applicable instead of those above. If
29 * you wish to allow use of your version of this file only under the terms of
30 * the GPL and not to allow others to use your version of this file under the
31 * MPL, indicate your decision by deleting the provisions above and replacing
32 * them with the notice and other provisions required by the GPL. If you do
33 * not delete the provisions above, a recipient may use your version of this
34 * file under either the MPL or the GPL.
36 * ***** END LICENSE BLOCK ***** */
38 import org.mozilla.javascript.*;
39 import java.io.*;
40 import com.metaweb.util.javascript.DOM.*;
42 /**
43 * The shell program.
45 * Can execute scripts interactively or in batch mode at the command line.
46 * An example of controlling the JavaScript engine.
48 * @author Norris Boyd
50 public class Shell extends ScriptableObject
52 private static final long serialVersionUID = -5638074146250193112L;
54 @Override
55 public String getClassName()
57 return "global";
60 /**
61 * Main entry point.
63 * Process arguments as would a normal Java program. Also
64 * create a new Context and associate it with the current thread.
65 * Then set up the execution environment and begin to
66 * execute scripts.
68 public static void main(String args[]) throws Exception {
69 // Associate a new Context with this thread
70 Context cx = Context.enter();
71 try {
72 // Initialize the standard objects (Object, Function, etc.)
73 // This must be done before scripts can be executed.
74 Shell shell = new Shell();
75 cx.setOptimizationLevel(-1);
76 cx.initStandardObjects(shell);
77 ScriptableObject.defineClass(shell, JSAttr.class, false, true);
78 ScriptableObject.defineClass(shell, JSCDATASection.class, false, true);
79 ScriptableObject.defineClass(shell, JSCharacterData.class, false, true);
80 ScriptableObject.defineClass(shell, JSComment.class, false, true);
81 ScriptableObject.defineClass(shell, JSDOMException.class, false, true);
82 ScriptableObject.defineClass(shell, JSDOMImplementation.class, false, true);
83 ScriptableObject.defineClass(shell, JSDocument.class, false, true);
84 ScriptableObject.defineClass(shell, JSDocumentFragment.class, false, true);
85 ScriptableObject.defineClass(shell, JSDocumentType.class, false, true);
86 ScriptableObject.defineClass(shell, JSElement.class, false, true);
87 ScriptableObject.defineClass(shell, JSEntity.class, false, true);
88 ScriptableObject.defineClass(shell, JSEntityReference.class, false, true);
89 ScriptableObject.defineClass(shell, JSNamedNodeMap.class, false, true);
90 ScriptableObject.defineClass(shell, JSNode.class, false, true);
91 ScriptableObject.defineClass(shell, JSNodeList.class, false, true);
92 ScriptableObject.defineClass(shell, JSNotation.class, false, true);
93 ScriptableObject.defineClass(shell, JSProcessingInstruction.class, false, true);
94 ScriptableObject.defineClass(shell, JSText.class, false, true);
95 ScriptableObject.defineClass(shell, JSDOMParser.class, false, true);
98 // Define some global functions particular to the shell. Note
99 // that these functions are not part of ECMA.
100 String[] names = { "print", "quit", "version", "_load", "help" };
101 shell.defineFunctionProperties(names, Shell.class,
102 ScriptableObject.DONTENUM);
104 args = processOptions(cx, args);
106 // Set up "arguments" in the global shell to contain the command
107 // line arguments after the name of the script to execute
108 Object[] array;
109 if (args.length == 0) {
110 array = new Object[0];
111 } else {
112 int length = args.length - 1;
113 array = new Object[length];
114 System.arraycopy(args, 1, array, 0, length);
116 Scriptable argsObj = cx.newArray(shell, array);
117 shell.defineProperty("arguments", argsObj,
118 ScriptableObject.DONTENUM);
120 shell.processSource(cx, args.length == 0 ? null : args[0]);
121 } finally {
122 Context.exit();
127 * Parse arguments.
129 public static String[] processOptions(Context cx, String args[]) {
130 for (int i=0; i < args.length; i++) {
131 String arg = args[i];
132 if (!arg.startsWith("-")) {
133 String[] result = new String[args.length - i];
134 for (int j=i; j < args.length; j++)
135 result[j-i] = args[j];
136 return result;
138 if (arg.equals("-version")) {
139 if (++i == args.length)
140 usage(arg);
141 double d = Context.toNumber(args[i]);
142 if (d != d)
143 usage(arg);
144 cx.setLanguageVersion((int) d);
145 continue;
147 usage(arg);
149 return new String[0];
153 * Print a usage message.
155 private static void usage(String s) {
156 p("Didn't understand \"" + s + "\".");
157 p("Valid arguments are:");
158 p("-version 100|110|120|130|140|150|160|170");
159 System.exit(1);
163 * Print a help message.
165 * This method is defined as a JavaScript function.
167 public void help() {
168 p("");
169 p("Command Description");
170 p("======= ===========");
171 p("help() Display usage and help messages. ");
172 p("defineClass(className) Define an extension using the Java class");
173 p(" named with the string argument. ");
174 p(" Uses ScriptableObject.defineClass(). ");
175 p("load(['foo.js', ...]) Load JavaScript source files named by ");
176 p(" string arguments. ");
177 p("loadClass(className) Load a class named by a string argument.");
178 p(" The class must be a script compiled to a");
179 p(" class file. ");
180 p("print([expr ...]) Evaluate and print expressions. ");
181 p("quit() Quit the shell. ");
182 p("version([number]) Get or set the JavaScript version number.");
183 p("");
187 * Print the string values of its arguments.
189 * This method is defined as a JavaScript function.
190 * Note that its arguments are of the "varargs" form, which
191 * allows it to handle an arbitrary number of arguments
192 * supplied to the JavaScript function.
195 public static void print(Context cx, Scriptable thisObj,
196 Object[] args, Function funObj)
198 for (int i=0; i < args.length; i++) {
199 if (i > 0)
200 System.out.print(" ");
202 // Convert the arbitrary JavaScript value into a string form.
203 String s = Context.toString(args[i]);
205 System.out.print(s);
207 System.out.println();
211 * Quit the shell.
213 * This only affects the interactive mode.
215 * This method is defined as a JavaScript function.
217 public void quit()
219 quitting = true;
223 * Get and set the language version.
225 * This method is defined as a JavaScript function.
227 public static double version(Context cx, Scriptable thisObj,
228 Object[] args, Function funObj)
230 double result = cx.getLanguageVersion();
231 if (args.length > 0) {
232 double d = Context.toNumber(args[0]);
233 cx.setLanguageVersion((int) d);
235 return result;
239 * Load and execute a set of JavaScript source files.
241 * This method is defined as a JavaScript function.
244 public static void _load(Context cx, Scriptable thisObj,
245 Object[] args, Function funObj)
247 Shell shell = (Shell)getTopLevelScope(thisObj);
248 for (int i = 0; i < args.length; i++) {
249 shell.processSource(cx, Context.toString(args[i]));
255 * Evaluate JavaScript source.
257 * @param cx the current context
258 * @param filename the name of the file to compile, or null
259 * for interactive mode.
261 private void processSource(Context cx, String filename)
263 if (filename == null) {
264 BufferedReader in = new BufferedReader
265 (new InputStreamReader(System.in));
266 String sourceName = "<stdin>";
267 int lineno = 1;
268 boolean hitEOF = false;
269 do {
270 int startline = lineno;
271 System.err.print("js> ");
272 System.err.flush();
273 try {
274 String source = "";
275 // Collect lines of source to compile.
276 while(true) {
277 String newline;
278 newline = in.readLine();
279 if (newline == null) {
280 hitEOF = true;
281 break;
283 source = source + newline + "\n";
284 lineno++;
285 // Continue collecting as long as more lines
286 // are needed to complete the current
287 // statement. stringIsCompilableUnit is also
288 // true if the source statement will result in
289 // any error other than one that might be
290 // resolved by appending more source.
291 if (cx.stringIsCompilableUnit(source))
292 break;
294 Object result = cx.evaluateString(this, source,
295 sourceName, startline,
296 null);
297 if (result != Context.getUndefinedValue()) {
298 System.err.println(Context.toString(result));
301 catch (WrappedException we) {
302 // Some form of exception was caught by JavaScript and
303 // propagated up.
304 System.err.println(we.getWrappedException().toString());
305 we.printStackTrace();
307 catch (EvaluatorException ee) {
308 // Some form of JavaScript error.
309 System.err.println("js: " + ee.getMessage());
311 catch (JavaScriptException jse) {
312 // Some form of JavaScript error.
313 System.err.println("js: " + jse.getMessage());
315 catch (IOException ioe) {
316 System.err.println(ioe.toString());
318 if (quitting) {
319 // The user executed the quit() function.
320 break;
322 } while (!hitEOF);
323 System.err.println();
324 } else {
325 FileReader in = null;
326 try {
327 in = new FileReader(filename);
329 catch (FileNotFoundException ex) {
330 Context.reportError("Couldn't open file \"" + filename + "\".");
331 return;
334 try {
335 // Here we evalute the entire contents of the file as
336 // a script. Text is printed only if the print() function
337 // is called.
338 cx.evaluateReader(this, in, filename, 1, null);
340 catch (WrappedException we) {
341 System.err.println(we.getWrappedException().toString());
342 we.printStackTrace();
344 catch (EvaluatorException ee) {
345 System.err.println("js: " + ee.getMessage());
347 catch (JavaScriptException jse) {
348 System.err.println("js: " + jse.getMessage());
350 catch (IOException ioe) {
351 System.err.println(ioe.toString());
353 finally {
354 try {
355 in.close();
357 catch (IOException ioe) {
358 System.err.println(ioe.toString());
364 private static void p(String s) {
365 System.out.println(s);
368 private boolean quitting;