aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/infrastructure/rhino1_7R1/toolsrc
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/infrastructure/rhino1_7R1/toolsrc')
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/build.xml103
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/ToolErrorReporter.java225
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java1560
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/GuiCallback.java71
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/Main.java431
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/ScopeProvider.java52
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java3547
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/build.xml126
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/CodePrinter.java212
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/FileBody.java191
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/IdValuePair.java58
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/Main.java612
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/SwitchGenerator.java491
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/jsc/Main.java395
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties268
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/ConsoleTextArea.java300
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Environment.java141
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Global.java1038
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/JSConsole.java225
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/JavaPolicySecurity.java240
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Main.java638
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/QuitAction.java50
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/SecurityProxy.java48
-rw-r--r--trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/ShellContextFactory.java114
24 files changed, 11136 insertions, 0 deletions
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/build.xml b/trunk/infrastructure/rhino1_7R1/toolsrc/build.xml
new file mode 100644
index 0000000..be9a9b7
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/build.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- ***** BEGIN LICENSE BLOCK *****
+ - Version: MPL 1.1/GPL 2.0
+ -
+ - The contents of this file are subject to the Mozilla Public License Version
+ - 1.1 (the "License"); you may not use this file except in compliance with
+ - the License. You may obtain a copy of the License at
+ - http://www.mozilla.org/MPL/
+ -
+ - Software distributed under the License is distributed on an "AS IS" basis,
+ - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ - for the specific language governing rights and limitations under the
+ - License.
+ -
+ - The Original Code is Rhino code, released May 6, 1999.
+ -
+ - The Initial Developer of the Original Code is
+ - Netscape Communications Corporation.
+ - Portions created by the Initial Developer are Copyright (C) 1997-1999
+ - the Initial Developer. All Rights Reserved.
+ -
+ - Contributor(s):
+ -
+ - Alternatively, the contents of this file may be used under the terms of
+ - the GNU General Public License Version 2 or later (the "GPL"), in which
+ - case the provisions of the GPL are applicable instead of those above. If
+ - you wish to allow use of your version of this file only under the terms of
+ - the GPL and not to allow others to use your version of this file under the
+ - MPL, indicate your decision by deleting the provisions above and replacing
+ - them with the notice and other provisions required by the GPL. If you do
+ - not delete the provisions above, a recipient may use your version of this
+ - file under either the MPL or the GPL.
+ -
+ - ***** END LICENSE BLOCK ***** -->
+
+
+<!--
+Build file for Rhino using Ant (see http://jakarta.apache.org/ant/index.html)
+Requires Ant version 1.2
+-->
+<project name="toolsrc" default="compile" basedir="..">
+
+ <target name="properties">
+ <property file="build.properties"/>
+ <property name="debugger"
+ value="org/mozilla/javascript/tools/debugger"/>
+ </target>
+
+ <target name="compile" depends="properties">
+ <javac srcdir="toolsrc"
+ destdir="${classes}"
+ includes="org/**/*.java"
+ excludes="org/**/debugger/*.java"
+ deprecation="on"
+ debug="${debug}"
+ target="${target-jvm}"
+ source="${source-level}"
+ >
+ </javac>
+ <copy todir="${classes}">
+ <fileset dir="toolsrc" includes="org/**/*.properties" />
+ </copy>
+ </target>
+
+ <target name="copy-source" depends="properties">
+ <mkdir dir="${dist.dir}/toolsrc"/>
+ <copy todir="${dist.dir}/toolsrc">
+ <fileset dir="toolsrc"
+ includes="**/*.java,**/*.properties,**/*.xml"
+ excludes="${debugger}/downloaded/**" />
+ </copy>
+ </target>
+
+ <target name="download-debugger" depends="properties">
+ <ant dir="toolsrc/${debugger}" target="download"/>
+ </target>
+
+ <target name="compile-debugger" depends="download-debugger">
+ <mkdir dir="classes"/>
+ <javac srcdir="toolsrc"
+ destdir="classes"
+ includes="org/**/debugger/*.java,org/**/debugger/downloaded/*.java"
+ classpath="js.jar"
+ deprecation="on"
+ target="${target-jvm}"
+ source="${source-level}"/>
+ <copy todir="classes">
+ <fileset dir="toolsrc" includes="org/**/*.properties" />
+ </copy>
+ <jar jarfile="js.jar"
+ basedir="classes"
+ update="yes"
+ />
+ </target>
+
+ <target name="clean" depends="properties">
+ <delete includeEmptyDirs="true">
+ <fileset dir="${classes}"
+ includes="org/mozilla/javascript/tools/**"/>
+ </delete>
+ </target>
+
+</project>
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/ToolErrorReporter.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/ToolErrorReporter.java
new file mode 100644
index 0000000..938f5f2
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/ToolErrorReporter.java
@@ -0,0 +1,225 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Kurt Westerfeld
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.tools;
+
+import org.mozilla.javascript.*;
+
+import java.text.MessageFormat;
+import java.io.*;
+import java.util.*;
+
+/**
+ * Error reporter for tools.
+ *
+ * Currently used by both the shell and the compiler.
+ */
+public class ToolErrorReporter implements ErrorReporter {
+
+ public ToolErrorReporter(boolean reportWarnings) {
+ this(reportWarnings, System.err);
+ }
+
+ public ToolErrorReporter(boolean reportWarnings, PrintStream err) {
+ this.reportWarnings = reportWarnings;
+ this.err = err;
+ }
+
+ /**
+ * Look up the message corresponding to messageId in the
+ * org.mozilla.javascript.tools.shell.resources.Messages property file.
+ * For internationalization support.
+ */
+ public static String getMessage(String messageId) {
+ return getMessage(messageId, (Object []) null);
+ }
+
+ public static String getMessage(String messageId, String argument) {
+ Object[] args = { argument };
+ return getMessage(messageId, args);
+ }
+
+ public static String getMessage(String messageId, Object arg1, Object arg2)
+ {
+ Object[] args = { arg1, arg2 };
+ return getMessage(messageId, args);
+ }
+
+ public static String getMessage(String messageId, Object[] args) {
+ Context cx = Context.getCurrentContext();
+ Locale locale = cx == null ? Locale.getDefault() : cx.getLocale();
+
+ // ResourceBundle does cacheing.
+ ResourceBundle rb = ResourceBundle.getBundle
+ ("org.mozilla.javascript.tools.resources.Messages", locale);
+
+ String formatString;
+ try {
+ formatString = rb.getString(messageId);
+ } catch (java.util.MissingResourceException mre) {
+ throw new RuntimeException("no message resource found for message property "
+ + messageId);
+ }
+
+ if (args == null) {
+ return formatString;
+ } else {
+ MessageFormat formatter = new MessageFormat(formatString);
+ return formatter.format(args);
+ }
+ }
+
+ private static String getExceptionMessage(RhinoException ex)
+ {
+ String msg;
+ if (ex instanceof JavaScriptException) {
+ msg = getMessage("msg.uncaughtJSException", ex.details());
+ } else if (ex instanceof EcmaError) {
+ msg = getMessage("msg.uncaughtEcmaError", ex.details());
+ } else if (ex instanceof EvaluatorException) {
+ msg = ex.details();
+ } else {
+ msg = ex.toString();
+ }
+ return msg;
+ }
+
+ public void warning(String message, String sourceName, int line,
+ String lineSource, int lineOffset)
+ {
+ if (!reportWarnings)
+ return;
+ reportErrorMessage(message, sourceName, line, lineSource, lineOffset,
+ true);
+ }
+
+ public void error(String message, String sourceName, int line,
+ String lineSource, int lineOffset)
+ {
+ hasReportedErrorFlag = true;
+ reportErrorMessage(message, sourceName, line, lineSource, lineOffset,
+ false);
+ }
+
+ public EvaluatorException runtimeError(String message, String sourceName,
+ int line, String lineSource,
+ int lineOffset)
+ {
+ return new EvaluatorException(message, sourceName, line,
+ lineSource, lineOffset);
+ }
+
+ public boolean hasReportedError() {
+ return hasReportedErrorFlag;
+ }
+
+ public boolean isReportingWarnings() {
+ return this.reportWarnings;
+ }
+
+ public void setIsReportingWarnings(boolean reportWarnings) {
+ this.reportWarnings = reportWarnings;
+ }
+
+ public static void reportException(ErrorReporter er, RhinoException ex)
+ {
+ if (er instanceof ToolErrorReporter) {
+ ((ToolErrorReporter)er).reportException(ex);
+ } else {
+ String msg = getExceptionMessage(ex);
+ er.error(msg, ex.sourceName(), ex.lineNumber(),
+ ex.lineSource(), ex.columnNumber());
+ }
+ }
+
+ public void reportException(RhinoException ex)
+ {
+ if (ex instanceof WrappedException) {
+ WrappedException we = (WrappedException)ex;
+ we.printStackTrace(err);
+ } else {
+ String lineSeparator =
+ SecurityUtilities.getSystemProperty("line.separator");
+ String msg = getExceptionMessage(ex) + lineSeparator +
+ ex.getScriptStackTrace();
+ reportErrorMessage(msg, ex.sourceName(), ex.lineNumber(),
+ ex.lineSource(), ex.columnNumber(), false);
+ }
+ }
+
+ private void reportErrorMessage(String message, String sourceName, int line,
+ String lineSource, int lineOffset,
+ boolean justWarning)
+ {
+ if (line > 0) {
+ String lineStr = String.valueOf(line);
+ if (sourceName != null) {
+ Object[] args = { sourceName, lineStr, message };
+ message = getMessage("msg.format3", args);
+ } else {
+ Object[] args = { lineStr, message };
+ message = getMessage("msg.format2", args);
+ }
+ } else {
+ Object[] args = { message };
+ message = getMessage("msg.format1", args);
+ }
+ if (justWarning) {
+ message = getMessage("msg.warning", message);
+ }
+ err.println(messagePrefix + message);
+ if (null != lineSource) {
+ err.println(messagePrefix + lineSource);
+ err.println(messagePrefix + buildIndicator(lineOffset));
+ }
+ }
+
+ private String buildIndicator(int offset){
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < offset-1; i++)
+ sb.append(".");
+ sb.append("^");
+ return sb.toString();
+ }
+
+ private final String messagePrefix = "js: ";
+ private boolean hasReportedErrorFlag;
+ private boolean reportWarnings;
+ private PrintStream err;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java
new file mode 100644
index 0000000..de8fcde
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java
@@ -0,0 +1,1560 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino JavaScript Debugger code, released
+ * November 21, 2000.
+ *
+ * The Initial Developer of the Original Code is
+ * SeeBeyond Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ * Matt Gould
+ * Christopher Oliver
+ * Cameron McCormack
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.mozilla.javascript.tools.debugger;
+
+import org.mozilla.javascript.*;
+import org.mozilla.javascript.debug.*;
+import java.util.*;
+import java.io.*;
+import java.net.URL;
+
+/**
+ * Dim or Debugger Implementation for Rhino.
+ */
+public class Dim {
+
+ // Constants for instructing the debugger what action to perform
+ // to end interruption. Used by 'returnValue'.
+ public static final int STEP_OVER = 0;
+ public static final int STEP_INTO = 1;
+ public static final int STEP_OUT = 2;
+ public static final int GO = 3;
+ public static final int BREAK = 4;
+ public static final int EXIT = 5;
+
+ // Constants for the DimIProxy interface implementation class.
+ private static final int IPROXY_DEBUG = 0;
+ private static final int IPROXY_LISTEN = 1;
+ private static final int IPROXY_COMPILE_SCRIPT = 2;
+ private static final int IPROXY_EVAL_SCRIPT = 3;
+ private static final int IPROXY_STRING_IS_COMPILABLE = 4;
+ private static final int IPROXY_OBJECT_TO_STRING = 5;
+ private static final int IPROXY_OBJECT_PROPERTY = 6;
+ private static final int IPROXY_OBJECT_IDS = 7;
+
+ /**
+ * Interface to the debugger GUI.
+ */
+ private GuiCallback callback;
+
+ /**
+ * Whether the debugger should break.
+ */
+ private boolean breakFlag;
+
+ /**
+ * The ScopeProvider object that provides the scope in which to
+ * evaluate script.
+ */
+ private ScopeProvider scopeProvider;
+
+ /**
+ * The index of the current stack frame.
+ */
+ private int frameIndex = -1;
+
+ /**
+ * Information about the current stack at the point of interruption.
+ */
+ private volatile ContextData interruptedContextData;
+
+ /**
+ * The ContextFactory to listen to for debugging information.
+ */
+ private ContextFactory contextFactory;
+
+ /**
+ * Synchronization object used to allow script evaluations to
+ * happen when a thread is resumed.
+ */
+ private Object monitor = new Object();
+
+ /**
+ * Synchronization object used to wait for valid
+ * {@link #interruptedContextData}.
+ */
+ private Object eventThreadMonitor = new Object();
+
+ /**
+ * The action to perform to end the interruption loop.
+ */
+ private volatile int returnValue = -1;
+
+ /**
+ * Whether the debugger is inside the interruption loop.
+ */
+ private boolean insideInterruptLoop;
+
+ /**
+ * The requested script string to be evaluated when the thread
+ * has been resumed.
+ */
+ private String evalRequest;
+
+ /**
+ * The stack frame in which to evaluate {@link #evalRequest}.
+ */
+ private StackFrame evalFrame;
+
+ /**
+ * The result of evaluating {@link #evalRequest}.
+ */
+ private String evalResult;
+
+ /**
+ * Whether the debugger should break when a script exception is thrown.
+ */
+ private boolean breakOnExceptions;
+
+ /**
+ * Whether the debugger should break when a script function is entered.
+ */
+ private boolean breakOnEnter;
+
+ /**
+ * Whether the debugger should break when a script function is returned
+ * from.
+ */
+ private boolean breakOnReturn;
+
+ /**
+ * Table mapping URLs to information about the script source.
+ */
+ private final Hashtable urlToSourceInfo = new Hashtable();
+
+ /**
+ * Table mapping function names to information about the function.
+ */
+ private final Hashtable functionNames = new Hashtable();
+
+ /**
+ * Table mapping functions to information about the function.
+ */
+ private final Hashtable functionToSource = new Hashtable();
+
+ /**
+ * ContextFactory.Listener instance attached to {@link #contextFactory}.
+ */
+ private DimIProxy listener;
+
+ /**
+ * Sets the GuiCallback object to use.
+ */
+ public void setGuiCallback(GuiCallback callback) {
+ this.callback = callback;
+ }
+
+ /**
+ * Tells the debugger to break at the next opportunity.
+ */
+ public void setBreak() {
+ this.breakFlag = true;
+ }
+
+ /**
+ * Sets the ScopeProvider to be used.
+ */
+ public void setScopeProvider(ScopeProvider scopeProvider) {
+ this.scopeProvider = scopeProvider;
+ }
+
+ /**
+ * Switches context to the stack frame with the given index.
+ */
+ public void contextSwitch(int frameIndex) {
+ this.frameIndex = frameIndex;
+ }
+
+ /**
+ * Sets whether the debugger should break on exceptions.
+ */
+ public void setBreakOnExceptions(boolean breakOnExceptions) {
+ this.breakOnExceptions = breakOnExceptions;
+ }
+
+ /**
+ * Sets whether the debugger should break on function entering.
+ */
+ public void setBreakOnEnter(boolean breakOnEnter) {
+ this.breakOnEnter = breakOnEnter;
+ }
+
+ /**
+ * Sets whether the debugger should break on function return.
+ */
+ public void setBreakOnReturn(boolean breakOnReturn) {
+ this.breakOnReturn = breakOnReturn;
+ }
+
+ /**
+ * Attaches the debugger to the given ContextFactory.
+ */
+ public void attachTo(ContextFactory factory) {
+ detach();
+ this.contextFactory = factory;
+ this.listener = new DimIProxy(this, IPROXY_LISTEN);
+ factory.addListener(this.listener);
+ }
+
+ /**
+ * Detaches the debugger from the current ContextFactory.
+ */
+ public void detach() {
+ if (listener != null) {
+ contextFactory.removeListener(listener);
+ contextFactory = null;
+ listener = null;
+ }
+ }
+
+ /**
+ * Releases resources associated with this debugger.
+ */
+ public void dispose() {
+ detach();
+ }
+
+ /**
+ * Returns the FunctionSource object for the given script or function.
+ */
+ private FunctionSource getFunctionSource(DebuggableScript fnOrScript) {
+ FunctionSource fsource = functionSource(fnOrScript);
+ if (fsource == null) {
+ String url = getNormalizedUrl(fnOrScript);
+ SourceInfo si = sourceInfo(url);
+ if (si == null) {
+ if (!fnOrScript.isGeneratedScript()) {
+ // Not eval or Function, try to load it from URL
+ String source = loadSource(url);
+ if (source != null) {
+ DebuggableScript top = fnOrScript;
+ for (;;) {
+ DebuggableScript parent = top.getParent();
+ if (parent == null) {
+ break;
+ }
+ top = parent;
+ }
+ registerTopScript(top, source);
+ fsource = functionSource(fnOrScript);
+ }
+ }
+ }
+ }
+ return fsource;
+ }
+
+ /**
+ * Loads the script at the given URL.
+ */
+ private String loadSource(String sourceUrl) {
+ String source = null;
+ int hash = sourceUrl.indexOf('#');
+ if (hash >= 0) {
+ sourceUrl = sourceUrl.substring(0, hash);
+ }
+ try {
+ InputStream is;
+ openStream:
+ {
+ if (sourceUrl.indexOf(':') < 0) {
+ // Can be a file name
+ try {
+ if (sourceUrl.startsWith("~/")) {
+ String home = SecurityUtilities.getSystemProperty("user.home");
+ if (home != null) {
+ String pathFromHome = sourceUrl.substring(2);
+ File f = new File(new File(home), pathFromHome);
+ if (f.exists()) {
+ is = new FileInputStream(f);
+ break openStream;
+ }
+ }
+ }
+ File f = new File(sourceUrl);
+ if (f.exists()) {
+ is = new FileInputStream(f);
+ break openStream;
+ }
+ } catch (SecurityException ex) { }
+ // No existing file, assume missed http://
+ if (sourceUrl.startsWith("//")) {
+ sourceUrl = "http:" + sourceUrl;
+ } else if (sourceUrl.startsWith("/")) {
+ sourceUrl = "http://127.0.0.1" + sourceUrl;
+ } else {
+ sourceUrl = "http://" + sourceUrl;
+ }
+ }
+
+ is = (new URL(sourceUrl)).openStream();
+ }
+
+ try {
+ source = Kit.readReader(new InputStreamReader(is));
+ } finally {
+ is.close();
+ }
+ } catch (IOException ex) {
+ System.err.println
+ ("Failed to load source from "+sourceUrl+": "+ ex);
+ }
+ return source;
+ }
+
+ /**
+ * Registers the given script as a top-level script in the debugger.
+ */
+ private void registerTopScript(DebuggableScript topScript, String source) {
+ if (!topScript.isTopLevel()) {
+ throw new IllegalArgumentException();
+ }
+ String url = getNormalizedUrl(topScript);
+ DebuggableScript[] functions = getAllFunctions(topScript);
+ final SourceInfo sourceInfo = new SourceInfo(source, functions, url);
+
+ synchronized (urlToSourceInfo) {
+ SourceInfo old = (SourceInfo)urlToSourceInfo.get(url);
+ if (old != null) {
+ sourceInfo.copyBreakpointsFrom(old);
+ }
+ urlToSourceInfo.put(url, sourceInfo);
+ for (int i = 0; i != sourceInfo.functionSourcesTop(); ++i) {
+ FunctionSource fsource = sourceInfo.functionSource(i);
+ String name = fsource.name();
+ if (name.length() != 0) {
+ functionNames.put(name, fsource);
+ }
+ }
+ }
+
+ synchronized (functionToSource) {
+ for (int i = 0; i != functions.length; ++i) {
+ FunctionSource fsource = sourceInfo.functionSource(i);
+ functionToSource.put(functions[i], fsource);
+ }
+ }
+
+ callback.updateSourceText(sourceInfo);
+ }
+
+ /**
+ * Returns the FunctionSource object for the given function or script.
+ */
+ private FunctionSource functionSource(DebuggableScript fnOrScript) {
+ return (FunctionSource)functionToSource.get(fnOrScript);
+ }
+
+ /**
+ * Returns an array of all function names.
+ */
+ public String[] functionNames() {
+ String[] a;
+ synchronized (urlToSourceInfo) {
+ Enumeration e = functionNames.keys();
+ a = new String[functionNames.size()];
+ int i = 0;
+ while (e.hasMoreElements()) {
+ a[i++] = (String)e.nextElement();
+ }
+ }
+ return a;
+ }
+
+ /**
+ * Returns the FunctionSource object for the function with the given name.
+ */
+ public FunctionSource functionSourceByName(String functionName) {
+ return (FunctionSource)functionNames.get(functionName);
+ }
+
+ /**
+ * Returns the SourceInfo object for the given URL.
+ */
+ public SourceInfo sourceInfo(String url) {
+ return (SourceInfo)urlToSourceInfo.get(url);
+ }
+
+ /**
+ * Returns the source URL for the given script or function.
+ */
+ private String getNormalizedUrl(DebuggableScript fnOrScript) {
+ String url = fnOrScript.getSourceName();
+ if (url == null) { url = "<stdin>"; }
+ else {
+ // Not to produce window for eval from different lines,
+ // strip line numbers, i.e. replace all #[0-9]+\(eval\) by
+ // (eval)
+ // Option: similar teatment for Function?
+ char evalSeparator = '#';
+ StringBuffer sb = null;
+ int urlLength = url.length();
+ int cursor = 0;
+ for (;;) {
+ int searchStart = url.indexOf(evalSeparator, cursor);
+ if (searchStart < 0) {
+ break;
+ }
+ String replace = null;
+ int i = searchStart + 1;
+ while (i != urlLength) {
+ int c = url.charAt(i);
+ if (!('0' <= c && c <= '9')) {
+ break;
+ }
+ ++i;
+ }
+ if (i != searchStart + 1) {
+ // i points after #[0-9]+
+ if ("(eval)".regionMatches(0, url, i, 6)) {
+ cursor = i + 6;
+ replace = "(eval)";
+ }
+ }
+ if (replace == null) {
+ break;
+ }
+ if (sb == null) {
+ sb = new StringBuffer();
+ sb.append(url.substring(0, searchStart));
+ }
+ sb.append(replace);
+ }
+ if (sb != null) {
+ if (cursor != urlLength) {
+ sb.append(url.substring(cursor));
+ }
+ url = sb.toString();
+ }
+ }
+ return url;
+ }
+
+ /**
+ * Returns an array of all functions in the given script.
+ */
+ private static DebuggableScript[] getAllFunctions
+ (DebuggableScript function) {
+ ObjArray functions = new ObjArray();
+ collectFunctions_r(function, functions);
+ DebuggableScript[] result = new DebuggableScript[functions.size()];
+ functions.toArray(result);
+ return result;
+ }
+
+ /**
+ * Helper function for {@link #getAllFunctions(DebuggableScript)}.
+ */
+ private static void collectFunctions_r(DebuggableScript function,
+ ObjArray array) {
+ array.add(function);
+ for (int i = 0; i != function.getFunctionCount(); ++i) {
+ collectFunctions_r(function.getFunction(i), array);
+ }
+ }
+
+ /**
+ * Clears all breakpoints.
+ */
+ public void clearAllBreakpoints() {
+ Enumeration e = urlToSourceInfo.elements();
+ while (e.hasMoreElements()) {
+ SourceInfo si = (SourceInfo)e.nextElement();
+ si.removeAllBreakpoints();
+ }
+ }
+
+ /**
+ * Called when a breakpoint has been hit.
+ */
+ private void handleBreakpointHit(StackFrame frame, Context cx) {
+ breakFlag = false;
+ interrupted(cx, frame, null);
+ }
+
+ /**
+ * Called when a script exception has been thrown.
+ */
+ private void handleExceptionThrown(Context cx, Throwable ex,
+ StackFrame frame) {
+ if (breakOnExceptions) {
+ ContextData cd = frame.contextData();
+ if (cd.lastProcessedException != ex) {
+ interrupted(cx, frame, ex);
+ cd.lastProcessedException = ex;
+ }
+ }
+ }
+
+ /**
+ * Returns the current ContextData object.
+ */
+ public ContextData currentContextData() {
+ return interruptedContextData;
+ }
+
+ /**
+ * Sets the action to perform to end interruption.
+ */
+ public void setReturnValue(int returnValue) {
+ synchronized (monitor) {
+ this.returnValue = returnValue;
+ monitor.notify();
+ }
+ }
+
+ /**
+ * Resumes execution of script.
+ */
+ public void go() {
+ synchronized (monitor) {
+ this.returnValue = GO;
+ monitor.notifyAll();
+ }
+ }
+
+ /**
+ * Evaluates the given script.
+ */
+ public String eval(String expr) {
+ String result = "undefined";
+ if (expr == null) {
+ return result;
+ }
+ ContextData contextData = currentContextData();
+ if (contextData == null || frameIndex >= contextData.frameCount()) {
+ return result;
+ }
+ StackFrame frame = contextData.getFrame(frameIndex);
+ if (contextData.eventThreadFlag) {
+ Context cx = Context.getCurrentContext();
+ result = do_eval(cx, frame, expr);
+ } else {
+ synchronized (monitor) {
+ if (insideInterruptLoop) {
+ evalRequest = expr;
+ evalFrame = frame;
+ monitor.notify();
+ do {
+ try {
+ monitor.wait();
+ } catch (InterruptedException exc) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ } while (evalRequest != null);
+ result = evalResult;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Compiles the given script.
+ */
+ public void compileScript(String url, String text) {
+ DimIProxy action = new DimIProxy(this, IPROXY_COMPILE_SCRIPT);
+ action.url = url;
+ action.text = text;
+ action.withContext();
+ }
+
+ /**
+ * Evaluates the given script.
+ */
+ public void evalScript(final String url, final String text) {
+ DimIProxy action = new DimIProxy(this, IPROXY_EVAL_SCRIPT);
+ action.url = url;
+ action.text = text;
+ action.withContext();
+ }
+
+ /**
+ * Converts the given script object to a string.
+ */
+ public String objectToString(Object object) {
+ DimIProxy action = new DimIProxy(this, IPROXY_OBJECT_TO_STRING);
+ action.object = object;
+ action.withContext();
+ return action.stringResult;
+ }
+
+ /**
+ * Returns whether the given string is syntactically valid script.
+ */
+ public boolean stringIsCompilableUnit(String str) {
+ DimIProxy action = new DimIProxy(this, IPROXY_STRING_IS_COMPILABLE);
+ action.text = str;
+ action.withContext();
+ return action.booleanResult;
+ }
+
+ /**
+ * Returns the value of a property on the given script object.
+ */
+ public Object getObjectProperty(Object object, Object id) {
+ DimIProxy action = new DimIProxy(this, IPROXY_OBJECT_PROPERTY);
+ action.object = object;
+ action.id = id;
+ action.withContext();
+ return action.objectResult;
+ }
+
+ /**
+ * Returns an array of the property names on the given script object.
+ */
+ public Object[] getObjectIds(Object object) {
+ DimIProxy action = new DimIProxy(this, IPROXY_OBJECT_IDS);
+ action.object = object;
+ action.withContext();
+ return action.objectArrayResult;
+ }
+
+ /**
+ * Returns the value of a property on the given script object.
+ */
+ private Object getObjectPropertyImpl(Context cx, Object object,
+ Object id) {
+ Scriptable scriptable = (Scriptable)object;
+ Object result;
+ if (id instanceof String) {
+ String name = (String)id;
+ if (name.equals("this")) {
+ result = scriptable;
+ } else if (name.equals("__proto__")) {
+ result = scriptable.getPrototype();
+ } else if (name.equals("__parent__")) {
+ result = scriptable.getParentScope();
+ } else {
+ result = ScriptableObject.getProperty(scriptable, name);
+ if (result == ScriptableObject.NOT_FOUND) {
+ result = Undefined.instance;
+ }
+ }
+ } else {
+ int index = ((Integer)id).intValue();
+ result = ScriptableObject.getProperty(scriptable, index);
+ if (result == ScriptableObject.NOT_FOUND) {
+ result = Undefined.instance;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns an array of the property names on the given script object.
+ */
+ private Object[] getObjectIdsImpl(Context cx, Object object) {
+ if (!(object instanceof Scriptable) || object == Undefined.instance) {
+ return Context.emptyArgs;
+ }
+
+ Object[] ids;
+ Scriptable scriptable = (Scriptable)object;
+ if (scriptable instanceof DebuggableObject) {
+ ids = ((DebuggableObject)scriptable).getAllIds();
+ } else {
+ ids = scriptable.getIds();
+ }
+
+ Scriptable proto = scriptable.getPrototype();
+ Scriptable parent = scriptable.getParentScope();
+ int extra = 0;
+ if (proto != null) {
+ ++extra;
+ }
+ if (parent != null) {
+ ++extra;
+ }
+ if (extra != 0) {
+ Object[] tmp = new Object[extra + ids.length];
+ System.arraycopy(ids, 0, tmp, extra, ids.length);
+ ids = tmp;
+ extra = 0;
+ if (proto != null) {
+ ids[extra++] = "__proto__";
+ }
+ if (parent != null) {
+ ids[extra++] = "__parent__";
+ }
+ }
+
+ return ids;
+ }
+
+ /**
+ * Interrupts script execution.
+ */
+ private void interrupted(Context cx, final StackFrame frame,
+ Throwable scriptException) {
+ ContextData contextData = frame.contextData();
+ boolean eventThreadFlag = callback.isGuiEventThread();
+ contextData.eventThreadFlag = eventThreadFlag;
+
+ boolean recursiveEventThreadCall = false;
+
+interruptedCheck:
+ synchronized (eventThreadMonitor) {
+ if (eventThreadFlag) {
+ if (interruptedContextData != null) {
+ recursiveEventThreadCall = true;
+ break interruptedCheck;
+ }
+ } else {
+ while (interruptedContextData != null) {
+ try {
+ eventThreadMonitor.wait();
+ } catch (InterruptedException exc) {
+ return;
+ }
+ }
+ }
+ interruptedContextData = contextData;
+ }
+
+ if (recursiveEventThreadCall) {
+ // XXX: For now the following is commented out as on Linux
+ // too deep recursion of dispatchNextGuiEvent causes GUI lockout.
+ // Note: it can make GUI unresponsive if long-running script
+ // will be called on GUI thread while processing another interrupt
+ if (false) {
+ // Run event dispatch until gui sets a flag to exit the initial
+ // call to interrupted.
+ while (this.returnValue == -1) {
+ try {
+ callback.dispatchNextGuiEvent();
+ } catch (InterruptedException exc) {
+ }
+ }
+ }
+ return;
+ }
+
+ if (interruptedContextData == null) Kit.codeBug();
+
+ try {
+ do {
+ int frameCount = contextData.frameCount();
+ this.frameIndex = frameCount -1;
+
+ final String threadTitle = Thread.currentThread().toString();
+ final String alertMessage;
+ if (scriptException == null) {
+ alertMessage = null;
+ } else {
+ alertMessage = scriptException.toString();
+ }
+
+ int returnValue = -1;
+ if (!eventThreadFlag) {
+ synchronized (monitor) {
+ if (insideInterruptLoop) Kit.codeBug();
+ this.insideInterruptLoop = true;
+ this.evalRequest = null;
+ this.returnValue = -1;
+ callback.enterInterrupt(frame, threadTitle,
+ alertMessage);
+ try {
+ for (;;) {
+ try {
+ monitor.wait();
+ } catch (InterruptedException exc) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ if (evalRequest != null) {
+ this.evalResult = null;
+ try {
+ evalResult = do_eval(cx, evalFrame,
+ evalRequest);
+ } finally {
+ evalRequest = null;
+ evalFrame = null;
+ monitor.notify();
+ }
+ continue;
+ }
+ if (this.returnValue != -1) {
+ returnValue = this.returnValue;
+ break;
+ }
+ }
+ } finally {
+ insideInterruptLoop = false;
+ }
+ }
+ } else {
+ this.returnValue = -1;
+ callback.enterInterrupt(frame, threadTitle, alertMessage);
+ while (this.returnValue == -1) {
+ try {
+ callback.dispatchNextGuiEvent();
+ } catch (InterruptedException exc) {
+ }
+ }
+ returnValue = this.returnValue;
+ }
+ switch (returnValue) {
+ case STEP_OVER:
+ contextData.breakNextLine = true;
+ contextData.stopAtFrameDepth = contextData.frameCount();
+ break;
+ case STEP_INTO:
+ contextData.breakNextLine = true;
+ contextData.stopAtFrameDepth = -1;
+ break;
+ case STEP_OUT:
+ if (contextData.frameCount() > 1) {
+ contextData.breakNextLine = true;
+ contextData.stopAtFrameDepth
+ = contextData.frameCount() -1;
+ }
+ break;
+ }
+ } while (false);
+ } finally {
+ synchronized (eventThreadMonitor) {
+ interruptedContextData = null;
+ eventThreadMonitor.notifyAll();
+ }
+ }
+
+ }
+
+ /**
+ * Evaluates script in the given stack frame.
+ */
+ private static String do_eval(Context cx, StackFrame frame, String expr) {
+ String resultString;
+ Debugger saved_debugger = cx.getDebugger();
+ Object saved_data = cx.getDebuggerContextData();
+ int saved_level = cx.getOptimizationLevel();
+
+ cx.setDebugger(null, null);
+ cx.setOptimizationLevel(-1);
+ cx.setGeneratingDebug(false);
+ try {
+ Callable script = (Callable)cx.compileString(expr, "", 0, null);
+ Object result = script.call(cx, frame.scope, frame.thisObj,
+ ScriptRuntime.emptyArgs);
+ if (result == Undefined.instance) {
+ resultString = "";
+ } else {
+ resultString = ScriptRuntime.toString(result);
+ }
+ } catch (Exception exc) {
+ resultString = exc.getMessage();
+ } finally {
+ cx.setGeneratingDebug(true);
+ cx.setOptimizationLevel(saved_level);
+ cx.setDebugger(saved_debugger, saved_data);
+ }
+ if (resultString == null) {
+ resultString = "null";
+ }
+ return resultString;
+ }
+
+ /**
+ * Proxy class to implement debug interfaces without bloat of class
+ * files.
+ */
+ private static class DimIProxy
+ implements ContextAction, ContextFactory.Listener, Debugger {
+
+ /**
+ * The debugger.
+ */
+ private Dim dim;
+
+ /**
+ * The interface implementation type. One of the IPROXY_* constants
+ * defined in {@link Dim}.
+ */
+ private int type;
+
+ /**
+ * The URL origin of the script to compile or evaluate.
+ */
+ private String url;
+
+ /**
+ * The text of the script to compile, evaluate or test for compilation.
+ */
+ private String text;
+
+ /**
+ * The object to convert, get a property from or enumerate.
+ */
+ private Object object;
+
+ /**
+ * The property to look up in {@link #object}.
+ */
+ private Object id;
+
+ /**
+ * The boolean result of the action.
+ */
+ private boolean booleanResult;
+
+ /**
+ * The String result of the action.
+ */
+ private String stringResult;
+
+ /**
+ * The Object result of the action.
+ */
+ private Object objectResult;
+
+ /**
+ * The Object[] result of the action.
+ */
+ private Object[] objectArrayResult;
+
+ /**
+ * Creates a new DimIProxy.
+ */
+ private DimIProxy(Dim dim, int type) {
+ this.dim = dim;
+ this.type = type;
+ }
+
+ // ContextAction
+
+ /**
+ * Performs the action given by {@link #type}.
+ */
+ public Object run(Context cx) {
+ switch (type) {
+ case IPROXY_COMPILE_SCRIPT:
+ cx.compileString(text, url, 1, null);
+ break;
+
+ case IPROXY_EVAL_SCRIPT:
+ {
+ Scriptable scope = null;
+ if (dim.scopeProvider != null) {
+ scope = dim.scopeProvider.getScope();
+ }
+ if (scope == null) {
+ scope = new ImporterTopLevel(cx);
+ }
+ cx.evaluateString(scope, text, url, 1, null);
+ }
+ break;
+
+ case IPROXY_STRING_IS_COMPILABLE:
+ booleanResult = cx.stringIsCompilableUnit(text);
+ break;
+
+ case IPROXY_OBJECT_TO_STRING:
+ if (object == Undefined.instance) {
+ stringResult = "undefined";
+ } else if (object == null) {
+ stringResult = "null";
+ } else if (object instanceof NativeCall) {
+ stringResult = "[object Call]";
+ } else {
+ stringResult = Context.toString(object);
+ }
+ break;
+
+ case IPROXY_OBJECT_PROPERTY:
+ objectResult = dim.getObjectPropertyImpl(cx, object, id);
+ break;
+
+ case IPROXY_OBJECT_IDS:
+ objectArrayResult = dim.getObjectIdsImpl(cx, object);
+ break;
+
+ default:
+ throw Kit.codeBug();
+ }
+ return null;
+ }
+
+ /**
+ * Performs the action given by {@link #type} with the attached
+ * {@link ContextFactory}.
+ */
+ private void withContext() {
+ dim.contextFactory.call(this);
+ }
+
+ // ContextFactory.Listener
+
+ /**
+ * Called when a Context is created.
+ */
+ public void contextCreated(Context cx) {
+ if (type != IPROXY_LISTEN) Kit.codeBug();
+ ContextData contextData = new ContextData();
+ Debugger debugger = new DimIProxy(dim, IPROXY_DEBUG);
+ cx.setDebugger(debugger, contextData);
+ cx.setGeneratingDebug(true);
+ cx.setOptimizationLevel(-1);
+ }
+
+ /**
+ * Called when a Context is destroyed.
+ */
+ public void contextReleased(Context cx) {
+ if (type != IPROXY_LISTEN) Kit.codeBug();
+ }
+
+ // Debugger
+
+ /**
+ * Returns a StackFrame for the given function or script.
+ */
+ public DebugFrame getFrame(Context cx, DebuggableScript fnOrScript) {
+ if (type != IPROXY_DEBUG) Kit.codeBug();
+
+ FunctionSource item = dim.getFunctionSource(fnOrScript);
+ if (item == null) {
+ // Can not debug if source is not available
+ return null;
+ }
+ return new StackFrame(cx, dim, item);
+ }
+
+ /**
+ * Called when compilation is finished.
+ */
+ public void handleCompilationDone(Context cx,
+ DebuggableScript fnOrScript,
+ String source) {
+ if (type != IPROXY_DEBUG) Kit.codeBug();
+
+ if (!fnOrScript.isTopLevel()) {
+ return;
+ }
+ dim.registerTopScript(fnOrScript, source);
+ }
+ }
+
+ /**
+ * Class to store information about a stack.
+ */
+ public static class ContextData {
+
+ /**
+ * The stack frames.
+ */
+ private ObjArray frameStack = new ObjArray();
+
+ /**
+ * Whether the debugger should break at the next line in this context.
+ */
+ private boolean breakNextLine;
+
+ /**
+ * The frame depth the debugger should stop at. Used to implement
+ * "step over" and "step out".
+ */
+ private int stopAtFrameDepth = -1;
+
+ /**
+ * Whether this context is in the event thread.
+ */
+ private boolean eventThreadFlag;
+
+ /**
+ * The last exception that was processed.
+ */
+ private Throwable lastProcessedException;
+
+ /**
+ * Returns the ContextData for the given Context.
+ */
+ public static ContextData get(Context cx) {
+ return (ContextData) cx.getDebuggerContextData();
+ }
+
+ /**
+ * Returns the number of stack frames.
+ */
+ public int frameCount() {
+ return frameStack.size();
+ }
+
+ /**
+ * Returns the stack frame with the given index.
+ */
+ public StackFrame getFrame(int frameNumber) {
+ int num = frameStack.size() - frameNumber - 1;
+ return (StackFrame) frameStack.get(num);
+ }
+
+ /**
+ * Pushes a stack frame on to the stack.
+ */
+ private void pushFrame(StackFrame frame) {
+ frameStack.push(frame);
+ }
+
+ /**
+ * Pops a stack frame from the stack.
+ */
+ private void popFrame() {
+ frameStack.pop();
+ }
+ }
+
+ /**
+ * Object to represent one stack frame.
+ */
+ public static class StackFrame implements DebugFrame {
+
+ /**
+ * The debugger.
+ */
+ private Dim dim;
+
+ /**
+ * The ContextData for the Context being debugged.
+ */
+ private ContextData contextData;
+
+ /**
+ * The scope.
+ */
+ private Scriptable scope;
+
+ /**
+ * The 'this' object.
+ */
+ private Scriptable thisObj;
+
+ /**
+ * Information about the function.
+ */
+ private FunctionSource fsource;
+
+ /**
+ * Array of breakpoint state for each source line.
+ */
+ private boolean[] breakpoints;
+
+ /**
+ * Current line number.
+ */
+ private int lineNumber;
+
+ /**
+ * Creates a new StackFrame.
+ */
+ private StackFrame(Context cx, Dim dim, FunctionSource fsource) {
+ this.dim = dim;
+ this.contextData = ContextData.get(cx);
+ this.fsource = fsource;
+ this.breakpoints = fsource.sourceInfo().breakpoints;
+ this.lineNumber = fsource.firstLine();
+ }
+
+ /**
+ * Called when the stack frame is entered.
+ */
+ public void onEnter(Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args) {
+ contextData.pushFrame(this);
+ this.scope = scope;
+ this.thisObj = thisObj;
+ if (dim.breakOnEnter) {
+ dim.handleBreakpointHit(this, cx);
+ }
+ }
+
+ /**
+ * Called when the current position has changed.
+ */
+ public void onLineChange(Context cx, int lineno) {
+ this.lineNumber = lineno;
+
+ if (!breakpoints[lineno] && !dim.breakFlag) {
+ boolean lineBreak = contextData.breakNextLine;
+ if (lineBreak && contextData.stopAtFrameDepth >= 0) {
+ lineBreak = (contextData.frameCount()
+ <= contextData.stopAtFrameDepth);
+ }
+ if (!lineBreak) {
+ return;
+ }
+ contextData.stopAtFrameDepth = -1;
+ contextData.breakNextLine = false;
+ }
+
+ dim.handleBreakpointHit(this, cx);
+ }
+
+ /**
+ * Called when an exception has been thrown.
+ */
+ public void onExceptionThrown(Context cx, Throwable exception) {
+ dim.handleExceptionThrown(cx, exception, this);
+ }
+
+ /**
+ * Called when the stack frame has been left.
+ */
+ public void onExit(Context cx, boolean byThrow,
+ Object resultOrException) {
+ if (dim.breakOnReturn && !byThrow) {
+ dim.handleBreakpointHit(this, cx);
+ }
+ contextData.popFrame();
+ }
+
+ /**
+ * Called when a 'debugger' statement is executed.
+ */
+ public void onDebuggerStatement(Context cx) {
+ dim.handleBreakpointHit(this, cx);
+ }
+
+ /**
+ * Returns the SourceInfo object for the function.
+ */
+ public SourceInfo sourceInfo() {
+ return fsource.sourceInfo();
+ }
+
+ /**
+ * Returns the ContextData object for the Context.
+ */
+ public ContextData contextData() {
+ return contextData;
+ }
+
+ /**
+ * Returns the scope object for this frame.
+ */
+ public Object scope() {
+ return scope;
+ }
+
+ /**
+ * Returns the 'this' object for this frame.
+ */
+ public Object thisObj() {
+ return thisObj;
+ }
+
+ /**
+ * Returns the source URL.
+ */
+ public String getUrl() {
+ return fsource.sourceInfo().url();
+ }
+
+ /**
+ * Returns the current line number.
+ */
+ public int getLineNumber() {
+ return lineNumber;
+ }
+ }
+
+ /**
+ * Class to store information about a function.
+ */
+ public static class FunctionSource {
+
+ /**
+ * Information about the source of the function.
+ */
+ private SourceInfo sourceInfo;
+
+ /**
+ * Line number of the first line of the function.
+ */
+ private int firstLine;
+
+ /**
+ * The function name.
+ */
+ private String name;
+
+ /**
+ * Creates a new FunctionSource.
+ */
+ private FunctionSource(SourceInfo sourceInfo, int firstLine,
+ String name) {
+ if (name == null) throw new IllegalArgumentException();
+ this.sourceInfo = sourceInfo;
+ this.firstLine = firstLine;
+ this.name = name;
+ }
+
+ /**
+ * Returns the SourceInfo object that describes the source of the
+ * function.
+ */
+ public SourceInfo sourceInfo() {
+ return sourceInfo;
+ }
+
+ /**
+ * Returns the line number of the first line of the function.
+ */
+ public int firstLine() {
+ return firstLine;
+ }
+
+ /**
+ * Returns the name of the function.
+ */
+ public String name() {
+ return name;
+ }
+ }
+
+ /**
+ * Class to store information about a script source.
+ */
+ public static class SourceInfo {
+
+ /**
+ * An empty array of booleans.
+ */
+ private static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0];
+
+ /**
+ * The script.
+ */
+ private String source;
+
+ /**
+ * The URL of the script.
+ */
+ private String url;
+
+ /**
+ * Array indicating which lines can have breakpoints set.
+ */
+ private boolean[] breakableLines;
+
+ /**
+ * Array indicating whether a breakpoint is set on the line.
+ */
+ private boolean[] breakpoints;
+
+ /**
+ * Array of FunctionSource objects for the functions in the script.
+ */
+ private FunctionSource[] functionSources;
+
+ /**
+ * Creates a new SourceInfo object.
+ */
+ private SourceInfo(String source, DebuggableScript[] functions,
+ String normilizedUrl) {
+ this.source = source;
+ this.url = normilizedUrl;
+
+ int N = functions.length;
+ int[][] lineArrays = new int[N][];
+ for (int i = 0; i != N; ++i) {
+ lineArrays[i] = functions[i].getLineNumbers();
+ }
+
+ int minAll = 0, maxAll = -1;
+ int[] firstLines = new int[N];
+ for (int i = 0; i != N; ++i) {
+ int[] lines = lineArrays[i];
+ if (lines == null || lines.length == 0) {
+ firstLines[i] = -1;
+ } else {
+ int min, max;
+ min = max = lines[0];
+ for (int j = 1; j != lines.length; ++j) {
+ int line = lines[j];
+ if (line < min) {
+ min = line;
+ } else if (line > max) {
+ max = line;
+ }
+ }
+ firstLines[i] = min;
+ if (minAll > maxAll) {
+ minAll = min;
+ maxAll = max;
+ } else {
+ if (min < minAll) {
+ minAll = min;
+ }
+ if (max > maxAll) {
+ maxAll = max;
+ }
+ }
+ }
+ }
+
+ if (minAll > maxAll) {
+ // No line information
+ this.breakableLines = EMPTY_BOOLEAN_ARRAY;
+ this.breakpoints = EMPTY_BOOLEAN_ARRAY;
+ } else {
+ if (minAll < 0) {
+ // Line numbers can not be negative
+ throw new IllegalStateException(String.valueOf(minAll));
+ }
+ int linesTop = maxAll + 1;
+ this.breakableLines = new boolean[linesTop];
+ this.breakpoints = new boolean[linesTop];
+ for (int i = 0; i != N; ++i) {
+ int[] lines = lineArrays[i];
+ if (lines != null && lines.length != 0) {
+ for (int j = 0; j != lines.length; ++j) {
+ int line = lines[j];
+ this.breakableLines[line] = true;
+ }
+ }
+ }
+ }
+ this.functionSources = new FunctionSource[N];
+ for (int i = 0; i != N; ++i) {
+ String name = functions[i].getFunctionName();
+ if (name == null) {
+ name = "";
+ }
+ this.functionSources[i]
+ = new FunctionSource(this, firstLines[i], name);
+ }
+ }
+
+ /**
+ * Returns the source text.
+ */
+ public String source() {
+ return this.source;
+ }
+
+ /**
+ * Returns the script's origin URL.
+ */
+ public String url() {
+ return this.url;
+ }
+
+ /**
+ * Returns the number of FunctionSource objects stored in this object.
+ */
+ public int functionSourcesTop() {
+ return functionSources.length;
+ }
+
+ /**
+ * Returns the FunctionSource object with the given index.
+ */
+ public FunctionSource functionSource(int i) {
+ return functionSources[i];
+ }
+
+ /**
+ * Copies the breakpoints from the given SourceInfo object into this
+ * one.
+ */
+ private void copyBreakpointsFrom(SourceInfo old) {
+ int end = old.breakpoints.length;
+ if (end > this.breakpoints.length) {
+ end = this.breakpoints.length;
+ }
+ for (int line = 0; line != end; ++line) {
+ if (old.breakpoints[line]) {
+ this.breakpoints[line] = true;
+ }
+ }
+ }
+
+ /**
+ * Returns whether the given line number can have a breakpoint set on
+ * it.
+ */
+ public boolean breakableLine(int line) {
+ return (line < this.breakableLines.length)
+ && this.breakableLines[line];
+ }
+
+ /**
+ * Returns whether there is a breakpoint set on the given line.
+ */
+ public boolean breakpoint(int line) {
+ if (!breakableLine(line)) {
+ throw new IllegalArgumentException(String.valueOf(line));
+ }
+ return line < this.breakpoints.length && this.breakpoints[line];
+ }
+
+ /**
+ * Sets or clears the breakpoint flag for the given line.
+ */
+ public boolean breakpoint(int line, boolean value) {
+ if (!breakableLine(line)) {
+ throw new IllegalArgumentException(String.valueOf(line));
+ }
+ boolean changed;
+ synchronized (breakpoints) {
+ if (breakpoints[line] != value) {
+ breakpoints[line] = value;
+ changed = true;
+ } else {
+ changed = false;
+ }
+ }
+ return changed;
+ }
+
+ /**
+ * Removes all breakpoints from the script.
+ */
+ public void removeAllBreakpoints() {
+ synchronized (breakpoints) {
+ for (int line = 0; line != breakpoints.length; ++line) {
+ breakpoints[line] = false;
+ }
+ }
+ }
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/GuiCallback.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/GuiCallback.java
new file mode 100644
index 0000000..f9762ec
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/GuiCallback.java
@@ -0,0 +1,71 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov, igor@fastmail.fm
+ * Cameron McCormack
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.mozilla.javascript.tools.debugger;
+
+/**
+ * Interface for communication between the debugger and its GUI. This
+ * should be implemented by the GUI.
+ */
+public interface GuiCallback {
+
+ /**
+ * Called when the source text of some script has been changed.
+ */
+ void updateSourceText(Dim.SourceInfo sourceInfo);
+
+ /**
+ * Called when the interrupt loop has been entered.
+ */
+ void enterInterrupt(Dim.StackFrame lastFrame,
+ String threadTitle,
+ String alertMessage);
+
+ /**
+ * Returns whether the current thread is the GUI's event thread.
+ * This information is required to avoid blocking the event thread
+ * from the debugger.
+ */
+ boolean isGuiEventThread();
+
+ /**
+ * Processes the next GUI event. This manual pumping of GUI events
+ * is necessary when the GUI event thread itself has been stopped.
+ */
+ void dispatchNextGuiEvent() throws InterruptedException;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/Main.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/Main.java
new file mode 100644
index 0000000..3f90915
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/Main.java
@@ -0,0 +1,431 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino JavaScript Debugger code, released
+ * November 21, 2000.
+ *
+ * The Initial Developer of the Original Code is
+ * SeeBeyond Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ * Matt Gould
+ * Christopher Oliver
+ * Cameron McCormack
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.tools.debugger;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+
+import javax.swing.JFrame;
+
+import org.mozilla.javascript.*;
+import org.mozilla.javascript.tools.shell.Global;
+
+/**
+ * Rhino script debugger main class. This class links together a
+ * debugger object ({@link Dim}) and a debugger GUI object ({@link SwingGui}).
+ */
+public class Main {
+
+ /**
+ * The debugger.
+ */
+ private Dim dim;
+
+ /**
+ * The debugger frame.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * Creates a new Main.
+ */
+ public Main(String title) {
+ dim = new Dim();
+ debugGui = new SwingGui(dim, title);
+ }
+
+ /**
+ * Returns the debugger window {@link JFrame}.
+ */
+ public JFrame getDebugFrame() {
+ return debugGui;
+ }
+
+ /**
+ * Breaks execution of the script.
+ */
+ public void doBreak() {
+ dim.setBreak();
+ }
+
+ /**
+ * Sets whether execution should break when a script exception is thrown.
+ */
+ public void setBreakOnExceptions(boolean value) {
+ dim.setBreakOnExceptions(value);
+ debugGui.getMenubar().getBreakOnExceptions().setSelected(value);
+ }
+
+ /**
+ * Sets whether execution should break when a function is entered.
+ */
+ public void setBreakOnEnter(boolean value) {
+ dim.setBreakOnEnter(value);
+ debugGui.getMenubar().getBreakOnEnter().setSelected(value);
+ }
+
+ /**
+ * Sets whether execution should break when a function is left.
+ */
+ public void setBreakOnReturn(boolean value) {
+ dim.setBreakOnReturn(value);
+ debugGui.getMenubar().getBreakOnReturn().setSelected(value);
+ }
+
+ /**
+ * Removes all breakpoints.
+ */
+ public void clearAllBreakpoints() {
+ dim.clearAllBreakpoints();
+ }
+
+ /**
+ * Resumes execution of the script.
+ */
+ public void go() {
+ dim.go();
+ }
+
+ /**
+ * Sets the scope to be used for script evaluation.
+ */
+ public void setScope(Scriptable scope) {
+ setScopeProvider(IProxy.newScopeProvider(scope));
+ }
+
+ /**
+ * Sets the {@link ScopeProvider} that provides a scope to be used
+ * for script evaluation.
+ */
+ public void setScopeProvider(ScopeProvider p) {
+ dim.setScopeProvider(p);
+ }
+
+ /**
+ * Assign a Runnable object that will be invoked when the user
+ * selects "Exit..." or closes the Debugger main window.
+ */
+ public void setExitAction(Runnable r) {
+ debugGui.setExitAction(r);
+ }
+
+ /**
+ * Returns an {@link InputStream} for stdin from the debugger's internal
+ * Console window.
+ */
+ public InputStream getIn() {
+ return debugGui.getConsole().getIn();
+ }
+
+ /**
+ * Returns a {@link PrintStream} for stdout to the debugger's internal
+ * Console window.
+ */
+ public PrintStream getOut() {
+ return debugGui.getConsole().getOut();
+ }
+
+ /**
+ * Returns a {@link PrintStream} for stderr in the Debugger's internal
+ * Console window.
+ */
+ public PrintStream getErr() {
+ return debugGui.getConsole().getErr();
+ }
+
+ /**
+ * Packs the debugger GUI frame.
+ */
+ public void pack() {
+ debugGui.pack();
+ }
+
+ /**
+ * Sets the debugger GUI frame dimensions.
+ */
+ public void setSize(int w, int h) {
+ debugGui.setSize(w, h);
+ }
+
+ /**
+ * Sets the visibility of the debugger GUI frame.
+ */
+ public void setVisible(boolean flag) {
+ debugGui.setVisible(flag);
+ }
+
+ /**
+ * Returns whether the debugger GUI frame is visible.
+ */
+ public boolean isVisible() {
+ return debugGui.isVisible();
+ }
+
+ /**
+ * Frees any resources held by the debugger.
+ */
+ public void dispose() {
+ clearAllBreakpoints();
+ dim.go();
+ debugGui.dispose();
+ dim = null;
+ }
+
+ /**
+ * Attaches the debugger to the given {@link ContextFactory}.
+ */
+ public void attachTo(ContextFactory factory) {
+ dim.attachTo(factory);
+ }
+
+ /**
+ * Detaches from the current {@link ContextFactory}.
+ */
+ public void detach() {
+ dim.detach();
+ }
+
+ /**
+ * Main entry point. Creates a debugger attached to a Rhino
+ * {@link org.mozilla.javascript.tools.shell.Main} shell session.
+ */
+ public static void main(String[] args) {
+ Main main = new Main("Rhino JavaScript Debugger");
+ main.doBreak();
+ main.setExitAction(new IProxy(IProxy.EXIT_ACTION));
+
+ System.setIn(main.getIn());
+ System.setOut(main.getOut());
+ System.setErr(main.getErr());
+
+ Global global = org.mozilla.javascript.tools.shell.Main.getGlobal();
+ global.setIn(main.getIn());
+ global.setOut(main.getOut());
+ global.setErr(main.getErr());
+
+ main.attachTo(
+ org.mozilla.javascript.tools.shell.Main.shellContextFactory);
+
+ main.setScope(global);
+
+ main.pack();
+ main.setSize(600, 460);
+ main.setVisible(true);
+
+ org.mozilla.javascript.tools.shell.Main.exec(args);
+ }
+
+ /**
+ * Entry point for embedded applications. This method attaches
+ * to the global {@link ContextFactory} with a scope of a newly
+ * created {@link Global} object. No I/O redirection is performed
+ * as with {@link #main(String[])}.
+ */
+ public static void mainEmbedded(String title) {
+ ContextFactory factory = ContextFactory.getGlobal();
+ Global global = new Global();
+ global.init(factory);
+ mainEmbedded(factory, global, title);
+ }
+
+ /**
+ * Entry point for embedded applications. This method attaches
+ * to the given {@link ContextFactory} with the given scope. No
+ * I/O redirection is performed as with {@link #main(String[])}.
+ */
+ public static void mainEmbedded(ContextFactory factory,
+ Scriptable scope,
+ String title) {
+ mainEmbeddedImpl(factory, scope, title);
+ }
+
+ /**
+ * Entry point for embedded applications. This method attaches
+ * to the given {@link ContextFactory} with the given scope. No
+ * I/O redirection is performed as with {@link #main(String[])}.
+ */
+ public static void mainEmbedded(ContextFactory factory,
+ ScopeProvider scopeProvider,
+ String title) {
+ mainEmbeddedImpl(factory, scopeProvider, title);
+ }
+
+ /**
+ * Helper method for {@link #mainEmbedded(String)}, etc.
+ */
+ private static void mainEmbeddedImpl(ContextFactory factory,
+ Object scopeProvider,
+ String title) {
+ if (title == null) {
+ title = "Rhino JavaScript Debugger (embedded usage)";
+ }
+ Main main = new Main(title);
+ main.doBreak();
+ main.setExitAction(new IProxy(IProxy.EXIT_ACTION));
+
+ main.attachTo(factory);
+ if (scopeProvider instanceof ScopeProvider) {
+ main.setScopeProvider((ScopeProvider)scopeProvider);
+ } else {
+ Scriptable scope = (Scriptable)scopeProvider;
+ if (scope instanceof Global) {
+ Global global = (Global)scope;
+ global.setIn(main.getIn());
+ global.setOut(main.getOut());
+ global.setErr(main.getErr());
+ }
+ main.setScope(scope);
+ }
+
+ main.pack();
+ main.setSize(600, 460);
+ main.setVisible(true);
+ }
+
+ // Deprecated methods
+
+ /**
+ * @deprecated Use {@link #setSize(int, int)} instead.
+ */
+ public void setSize(java.awt.Dimension dimension) {
+ debugGui.setSize(dimension.width, dimension.height);
+ }
+
+ /**
+ * @deprecated
+ * The method does nothing and is only present for compatibility.
+ */
+ public void setOptimizationLevel(int level) {
+ }
+
+ /**
+ * @deprecated
+ * The method is only present for compatibility and should not be called.
+ */
+ public void contextEntered(Context cx) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @deprecated
+ * The method is only present for compatibility and should not be called.
+ */
+ public void contextExited(Context cx) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @deprecated
+ * The method is only present for compatibility and should not be called.
+ */
+ public void contextCreated(Context cx) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @deprecated
+ * The method is only present for compatibility and should not be called.
+ */
+ public void contextReleased(Context cx)
+ {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * Class to consolidate all internal implementations of interfaces
+ * to avoid class generation bloat.
+ */
+ private static class IProxy implements Runnable, ScopeProvider {
+
+ // Constants for 'type'.
+ public static final int EXIT_ACTION = 1;
+ public static final int SCOPE_PROVIDER = 2;
+
+ /**
+ * The type of interface.
+ */
+ private final int type;
+
+ /**
+ * The scope object to expose when {@link #type} =
+ * {@link #SCOPE_PROVIDER}.
+ */
+ private Scriptable scope;
+
+ /**
+ * Creates a new IProxy.
+ */
+ public IProxy(int type) {
+ this.type = type;
+ }
+
+ /**
+ * Creates a new IProxy that acts as a {@link ScopeProvider}.
+ */
+ public static ScopeProvider newScopeProvider(Scriptable scope) {
+ IProxy scopeProvider = new IProxy(SCOPE_PROVIDER);
+ scopeProvider.scope = scope;
+ return scopeProvider;
+ }
+
+ // ContextAction
+
+ /**
+ * Exit action.
+ */
+ public void run() {
+ if (type != EXIT_ACTION) Kit.codeBug();
+ System.exit(0);
+ }
+
+ // ScopeProvider
+
+ /**
+ * Returns the scope for script evaluations.
+ */
+ public Scriptable getScope() {
+ if (type != SCOPE_PROVIDER) Kit.codeBug();
+ if (scope == null) Kit.codeBug();
+ return scope;
+ }
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/ScopeProvider.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/ScopeProvider.java
new file mode 100644
index 0000000..d8f65b9
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/ScopeProvider.java
@@ -0,0 +1,52 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino JavaScript Debugger code, released
+ * November 21, 2000.
+ *
+ * The Initial Developer of the Original Code is
+ * See Beyond Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Christopher Oliver
+ * Cameron McCormack
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.mozilla.javascript.tools.debugger;
+
+import org.mozilla.javascript.Scriptable;
+
+/**
+ * Interface to provide a scope object for script evaluation to the debugger.
+ */
+public interface ScopeProvider {
+
+ /**
+ * Returns the scope object to be used for script evaluation.
+ */
+ Scriptable getScope();
+} \ No newline at end of file
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java
new file mode 100644
index 0000000..61dc065
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java
@@ -0,0 +1,3547 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino JavaScript Debugger code, released
+ * November 21, 2000.
+ *
+ * The Initial Developer of the Original Code is
+ * SeeBeyond Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ * Matt Gould
+ * Cameron McCormack
+ * Christopher Oliver
+ * Hannes Wallnoefer
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.mozilla.javascript.tools.debugger;
+
+import javax.swing.*;
+import javax.swing.text.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+import java.awt.*;
+import java.awt.event.*;
+
+import java.util.*;
+import java.io.*;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.TreePath;
+import java.lang.reflect.Method;
+
+import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.SecurityUtilities;
+
+import org.mozilla.javascript.tools.shell.ConsoleTextArea;
+
+import org.mozilla.javascript.tools.debugger.downloaded.JTreeTable;
+import org.mozilla.javascript.tools.debugger.downloaded.TreeTableModel;
+import org.mozilla.javascript.tools.debugger.downloaded.TreeTableModelAdapter;
+
+/**
+ * GUI for the Rhino debugger.
+ */
+public class SwingGui extends JFrame implements GuiCallback {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = -8217029773456711621L;
+
+ /**
+ * The debugger.
+ */
+ Dim dim;
+
+ /**
+ * The action to run when the 'Exit' menu item is chosen or the
+ * frame is closed.
+ */
+ private Runnable exitAction;
+
+ /**
+ * The {@link JDesktopPane} that holds the script windows.
+ */
+ private JDesktopPane desk;
+
+ /**
+ * The {@link JPanel} that shows information about the context.
+ */
+ private ContextWindow context;
+
+ /**
+ * The menu bar.
+ */
+ private Menubar menubar;
+
+ /**
+ * The tool bar.
+ */
+ private JToolBar toolBar;
+
+ /**
+ * The console that displays I/O from the script.
+ */
+ private JSInternalConsole console;
+
+ /**
+ * The {@link JSplitPane} that separates {@link #desk} from
+ * {@link org.mozilla.javascript.Context}.
+ */
+ private JSplitPane split1;
+
+ /**
+ * The status bar.
+ */
+ private JLabel statusBar;
+
+ /**
+ * Hash table of internal frame names to the internal frames themselves.
+ */
+ private Hashtable toplevels = new Hashtable();
+
+ /**
+ * Hash table of script URLs to their internal frames.
+ */
+ private Hashtable fileWindows = new Hashtable();
+
+ /**
+ * The {@link FileWindow} that last had the focus.
+ */
+ private FileWindow currentWindow;
+
+ /**
+ * File choose dialog for loading a script.
+ */
+ JFileChooser dlg;
+
+ /**
+ * The AWT EventQueue. Used for manually pumping AWT events from
+ * {@link #dispatchNextGuiEvent()}.
+ */
+ private EventQueue awtEventQueue;
+
+ /**
+ * Creates a new SwingGui.
+ */
+ public SwingGui(Dim dim, String title) {
+ super(title);
+ this.dim = dim;
+ init();
+ dim.setGuiCallback(this);
+ }
+
+ /**
+ * Returns the Menubar of this debugger frame.
+ */
+ public Menubar getMenubar() {
+ return menubar;
+ }
+
+ /**
+ * Sets the {@link Runnable} that will be run when the "Exit" menu
+ * item is chosen.
+ */
+ public void setExitAction(Runnable r) {
+ exitAction = r;
+ }
+
+ /**
+ * Returns the debugger console component.
+ */
+ public JSInternalConsole getConsole() {
+ return console;
+ }
+
+ /**
+ * Sets the visibility of the debugger GUI.
+ */
+ public void setVisible(boolean b) {
+ super.setVisible(b);
+ if (b) {
+ // this needs to be done after the window is visible
+ console.consoleTextArea.requestFocus();
+ context.split.setDividerLocation(0.5);
+ try {
+ console.setMaximum(true);
+ console.setSelected(true);
+ console.show();
+ console.consoleTextArea.requestFocus();
+ } catch (Exception exc) {
+ }
+ }
+ }
+
+ /**
+ * Records a new internal frame.
+ */
+ void addTopLevel(String key, JFrame frame) {
+ if (frame != this) {
+ toplevels.put(key, frame);
+ }
+ }
+
+ /**
+ * Constructs the debugger GUI.
+ */
+ private void init() {
+ menubar = new Menubar(this);
+ setJMenuBar(menubar);
+ toolBar = new JToolBar();
+ JButton button;
+ JButton breakButton, goButton, stepIntoButton,
+ stepOverButton, stepOutButton;
+ String [] toolTips = {"Break (Pause)",
+ "Go (F5)",
+ "Step Into (F11)",
+ "Step Over (F7)",
+ "Step Out (F8)"};
+ int count = 0;
+ button = breakButton = new JButton("Break");
+ button.setToolTipText("Break");
+ button.setActionCommand("Break");
+ button.addActionListener(menubar);
+ button.setEnabled(true);
+ button.setToolTipText(toolTips[count++]);
+
+ button = goButton = new JButton("Go");
+ button.setToolTipText("Go");
+ button.setActionCommand("Go");
+ button.addActionListener(menubar);
+ button.setEnabled(false);
+ button.setToolTipText(toolTips[count++]);
+
+ button = stepIntoButton = new JButton("Step Into");
+ button.setToolTipText("Step Into");
+ button.setActionCommand("Step Into");
+ button.addActionListener(menubar);
+ button.setEnabled(false);
+ button.setToolTipText(toolTips[count++]);
+
+ button = stepOverButton = new JButton("Step Over");
+ button.setToolTipText("Step Over");
+ button.setActionCommand("Step Over");
+ button.setEnabled(false);
+ button.addActionListener(menubar);
+ button.setToolTipText(toolTips[count++]);
+
+ button = stepOutButton = new JButton("Step Out");
+ button.setToolTipText("Step Out");
+ button.setActionCommand("Step Out");
+ button.setEnabled(false);
+ button.addActionListener(menubar);
+ button.setToolTipText(toolTips[count++]);
+
+ Dimension dim = stepOverButton.getPreferredSize();
+ breakButton.setPreferredSize(dim);
+ breakButton.setMinimumSize(dim);
+ breakButton.setMaximumSize(dim);
+ breakButton.setSize(dim);
+ goButton.setPreferredSize(dim);
+ goButton.setMinimumSize(dim);
+ goButton.setMaximumSize(dim);
+ stepIntoButton.setPreferredSize(dim);
+ stepIntoButton.setMinimumSize(dim);
+ stepIntoButton.setMaximumSize(dim);
+ stepOverButton.setPreferredSize(dim);
+ stepOverButton.setMinimumSize(dim);
+ stepOverButton.setMaximumSize(dim);
+ stepOutButton.setPreferredSize(dim);
+ stepOutButton.setMinimumSize(dim);
+ stepOutButton.setMaximumSize(dim);
+ toolBar.add(breakButton);
+ toolBar.add(goButton);
+ toolBar.add(stepIntoButton);
+ toolBar.add(stepOverButton);
+ toolBar.add(stepOutButton);
+
+ JPanel contentPane = new JPanel();
+ contentPane.setLayout(new BorderLayout());
+ getContentPane().add(toolBar, BorderLayout.NORTH);
+ getContentPane().add(contentPane, BorderLayout.CENTER);
+ desk = new JDesktopPane();
+ desk.setPreferredSize(new Dimension(600, 300));
+ desk.setMinimumSize(new Dimension(150, 50));
+ desk.add(console = new JSInternalConsole("JavaScript Console"));
+ context = new ContextWindow(this);
+ context.setPreferredSize(new Dimension(600, 120));
+ context.setMinimumSize(new Dimension(50, 50));
+
+ split1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, desk,
+ context);
+ split1.setOneTouchExpandable(true);
+ SwingGui.setResizeWeight(split1, 0.66);
+ contentPane.add(split1, BorderLayout.CENTER);
+ statusBar = new JLabel();
+ statusBar.setText("Thread: ");
+ contentPane.add(statusBar, BorderLayout.SOUTH);
+ dlg = new JFileChooser();
+
+ javax.swing.filechooser.FileFilter filter =
+ new javax.swing.filechooser.FileFilter() {
+ public boolean accept(File f) {
+ if (f.isDirectory()) {
+ return true;
+ }
+ String n = f.getName();
+ int i = n.lastIndexOf('.');
+ if (i > 0 && i < n.length() -1) {
+ String ext = n.substring(i + 1).toLowerCase();
+ if (ext.equals("js")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String getDescription() {
+ return "JavaScript Files (*.js)";
+ }
+ };
+ dlg.addChoosableFileFilter(filter);
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ exit();
+ }
+ });
+ }
+
+ /**
+ * Runs the {@link #exitAction}.
+ */
+ private void exit() {
+ if (exitAction != null) {
+ SwingUtilities.invokeLater(exitAction);
+ }
+ dim.setReturnValue(Dim.EXIT);
+ }
+
+ /**
+ * Returns the {@link FileWindow} for the given URL.
+ */
+ FileWindow getFileWindow(String url) {
+ if (url == null || url.equals("<stdin>")) {
+ return null;
+ }
+ return (FileWindow)fileWindows.get(url);
+ }
+
+ /**
+ * Returns a short version of the given URL.
+ */
+ static String getShortName(String url) {
+ int lastSlash = url.lastIndexOf('/');
+ if (lastSlash < 0) {
+ lastSlash = url.lastIndexOf('\\');
+ }
+ String shortName = url;
+ if (lastSlash >= 0 && lastSlash + 1 < url.length()) {
+ shortName = url.substring(lastSlash + 1);
+ }
+ return shortName;
+ }
+
+ /**
+ * Closes the given {@link FileWindow}.
+ */
+ void removeWindow(FileWindow w) {
+ fileWindows.remove(w.getUrl());
+ JMenu windowMenu = getWindowMenu();
+ int count = windowMenu.getItemCount();
+ JMenuItem lastItem = windowMenu.getItem(count -1);
+ String name = getShortName(w.getUrl());
+ for (int i = 5; i < count; i++) {
+ JMenuItem item = windowMenu.getItem(i);
+ if (item == null) continue; // separator
+ String text = item.getText();
+ //1 D:\foo.js
+ //2 D:\bar.js
+ int pos = text.indexOf(' ');
+ if (text.substring(pos + 1).equals(name)) {
+ windowMenu.remove(item);
+ // Cascade [0]
+ // Tile [1]
+ // ------- [2]
+ // Console [3]
+ // ------- [4]
+ if (count == 6) {
+ // remove the final separator
+ windowMenu.remove(4);
+ } else {
+ int j = i - 4;
+ for (;i < count -1; i++) {
+ JMenuItem thisItem = windowMenu.getItem(i);
+ if (thisItem != null) {
+ //1 D:\foo.js
+ //2 D:\bar.js
+ text = thisItem.getText();
+ if (text.equals("More Windows...")) {
+ break;
+ } else {
+ pos = text.indexOf(' ');
+ thisItem.setText((char)('0' + j) + " " +
+ text.substring(pos + 1));
+ thisItem.setMnemonic('0' + j);
+ j++;
+ }
+ }
+ }
+ if (count - 6 == 0 && lastItem != item) {
+ if (lastItem.getText().equals("More Windows...")) {
+ windowMenu.remove(lastItem);
+ }
+ }
+ }
+ break;
+ }
+ }
+ windowMenu.revalidate();
+ }
+
+ /**
+ * Shows the line at which execution in the given stack frame just stopped.
+ */
+ void showStopLine(Dim.StackFrame frame) {
+ String sourceName = frame.getUrl();
+ if (sourceName == null || sourceName.equals("<stdin>")) {
+ if (console.isVisible()) {
+ console.show();
+ }
+ } else {
+ showFileWindow(sourceName, -1);
+ int lineNumber = frame.getLineNumber();
+ FileWindow w = getFileWindow(sourceName);
+ if (w != null) {
+ setFilePosition(w, lineNumber);
+ }
+ }
+ }
+
+ /**
+ * Shows a {@link FileWindow} for the given source, creating it
+ * if it doesn't exist yet. if <code>lineNumber</code> is greater
+ * than -1, it indicates the line number to select and display.
+ * @param sourceUrl the source URL
+ * @param lineNumber the line number to select, or -1
+ */
+ protected void showFileWindow(String sourceUrl, int lineNumber) {
+ FileWindow w = getFileWindow(sourceUrl);
+ if (w == null) {
+ Dim.SourceInfo si = dim.sourceInfo(sourceUrl);
+ createFileWindow(si, -1);
+ w = getFileWindow(sourceUrl);
+ }
+ if (lineNumber > -1) {
+ int start = w.getPosition(lineNumber-1);
+ int end = w.getPosition(lineNumber)-1;
+ w.textArea.select(start);
+ w.textArea.setCaretPosition(start);
+ w.textArea.moveCaretPosition(end);
+ }
+ try {
+ if (w.isIcon()) {
+ w.setIcon(false);
+ }
+ w.setVisible(true);
+ w.moveToFront();
+ w.setSelected(true);
+ requestFocus();
+ w.requestFocus();
+ w.textArea.requestFocus();
+ } catch (Exception exc) {
+ }
+ }
+
+ /**
+ * Creates and shows a new {@link FileWindow} for the given source.
+ */
+ protected void createFileWindow(Dim.SourceInfo sourceInfo, int line) {
+ boolean activate = true;
+
+ String url = sourceInfo.url();
+ FileWindow w = new FileWindow(this, sourceInfo);
+ fileWindows.put(url, w);
+ if (line != -1) {
+ if (currentWindow != null) {
+ currentWindow.setPosition(-1);
+ }
+ try {
+ w.setPosition(w.textArea.getLineStartOffset(line-1));
+ } catch (BadLocationException exc) {
+ try {
+ w.setPosition(w.textArea.getLineStartOffset(0));
+ } catch (BadLocationException ee) {
+ w.setPosition(-1);
+ }
+ }
+ }
+ desk.add(w);
+ if (line != -1) {
+ currentWindow = w;
+ }
+ menubar.addFile(url);
+ w.setVisible(true);
+
+ if (activate) {
+ try {
+ w.setMaximum(true);
+ w.setSelected(true);
+ w.moveToFront();
+ } catch (Exception exc) {
+ }
+ }
+ }
+
+ /**
+ * Update the source text for <code>sourceInfo</code>. This returns true
+ * if a {@link FileWindow} for the given source exists and could be updated.
+ * Otherwise, this does nothing and returns false.
+ * @param sourceInfo the source info
+ * @return true if a {@link FileWindow} for the given source exists
+ * and could be updated, false otherwise.
+ */
+ protected boolean updateFileWindow(Dim.SourceInfo sourceInfo) {
+ String fileName = sourceInfo.url();
+ FileWindow w = getFileWindow(fileName);
+ if (w != null) {
+ w.updateText(sourceInfo);
+ w.show();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Moves the current position in the given {@link FileWindow} to the
+ * given line.
+ */
+ private void setFilePosition(FileWindow w, int line) {
+ boolean activate = true;
+ JTextArea ta = w.textArea;
+ try {
+ if (line == -1) {
+ w.setPosition(-1);
+ if (currentWindow == w) {
+ currentWindow = null;
+ }
+ } else {
+ int loc = ta.getLineStartOffset(line-1);
+ if (currentWindow != null && currentWindow != w) {
+ currentWindow.setPosition(-1);
+ }
+ w.setPosition(loc);
+ currentWindow = w;
+ }
+ } catch (BadLocationException exc) {
+ // fix me
+ }
+ if (activate) {
+ if (w.isIcon()) {
+ desk.getDesktopManager().deiconifyFrame(w);
+ }
+ desk.getDesktopManager().activateFrame(w);
+ try {
+ w.show();
+ w.toFront(); // required for correct frame layering (JDK 1.4.1)
+ w.setSelected(true);
+ } catch (Exception exc) {
+ }
+ }
+ }
+
+ /**
+ * Handles script interruption.
+ */
+ void enterInterruptImpl(Dim.StackFrame lastFrame,
+ String threadTitle, String alertMessage) {
+ statusBar.setText("Thread: " + threadTitle);
+
+ showStopLine(lastFrame);
+
+ if (alertMessage != null) {
+ MessageDialogWrapper.showMessageDialog(this,
+ alertMessage,
+ "Exception in Script",
+ JOptionPane.ERROR_MESSAGE);
+ }
+
+ updateEnabled(true);
+
+ Dim.ContextData contextData = lastFrame.contextData();
+
+ JComboBox ctx = context.context;
+ Vector toolTips = context.toolTips;
+ context.disableUpdate();
+ int frameCount = contextData.frameCount();
+ ctx.removeAllItems();
+ // workaround for JDK 1.4 bug that caches selected value even after
+ // removeAllItems() is called
+ ctx.setSelectedItem(null);
+ toolTips.removeAllElements();
+ for (int i = 0; i < frameCount; i++) {
+ Dim.StackFrame frame = contextData.getFrame(i);
+ String url = frame.getUrl();
+ int lineNumber = frame.getLineNumber();
+ String shortName = url;
+ if (url.length() > 20) {
+ shortName = "..." + url.substring(url.length() - 17);
+ }
+ String location = "\"" + shortName + "\", line " + lineNumber;
+ ctx.insertItemAt(location, i);
+ location = "\"" + url + "\", line " + lineNumber;
+ toolTips.addElement(location);
+ }
+ context.enableUpdate();
+ ctx.setSelectedIndex(0);
+ ctx.setMinimumSize(new Dimension(50, ctx.getMinimumSize().height));
+ }
+
+ /**
+ * Returns the 'Window' menu.
+ */
+ private JMenu getWindowMenu() {
+ return menubar.getMenu(3);
+ }
+
+ /**
+ * Displays a {@link JFileChooser} and returns the selected filename.
+ */
+ private String chooseFile(String title) {
+ dlg.setDialogTitle(title);
+ File CWD = null;
+ String dir = SecurityUtilities.getSystemProperty("user.dir");
+ if (dir != null) {
+ CWD = new File(dir);
+ }
+ if (CWD != null) {
+ dlg.setCurrentDirectory(CWD);
+ }
+ int returnVal = dlg.showOpenDialog(this);
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
+ try {
+ String result = dlg.getSelectedFile().getCanonicalPath();
+ CWD = dlg.getSelectedFile().getParentFile();
+ Properties props = System.getProperties();
+ props.put("user.dir", CWD.getPath());
+ System.setProperties(props);
+ return result;
+ } catch (IOException ignored) {
+ } catch (SecurityException ignored) {
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the current selected internal frame.
+ */
+ private JInternalFrame getSelectedFrame() {
+ JInternalFrame[] frames = desk.getAllFrames();
+ for (int i = 0; i < frames.length; i++) {
+ if (frames[i].isShowing()) {
+ return frames[i];
+ }
+ }
+ return frames[frames.length - 1];
+ }
+
+ /**
+ * Enables or disables the menu and tool bars with respect to the
+ * state of script execution.
+ */
+ private void updateEnabled(boolean interrupted) {
+ ((Menubar)getJMenuBar()).updateEnabled(interrupted);
+ for (int ci = 0, cc = toolBar.getComponentCount(); ci < cc; ci++) {
+ boolean enableButton;
+ if (ci == 0) {
+ // Break
+ enableButton = !interrupted;
+ } else {
+ enableButton = interrupted;
+ }
+ toolBar.getComponent(ci).setEnabled(enableButton);
+ }
+ if (interrupted) {
+ toolBar.setEnabled(true);
+ // raise the debugger window
+ int state = getExtendedState();
+ if (state == Frame.ICONIFIED) {
+ setExtendedState(Frame.NORMAL);
+ }
+ toFront();
+ context.enable();
+ } else {
+ if (currentWindow != null) currentWindow.setPosition(-1);
+ context.disable();
+ }
+ }
+
+ /**
+ * Calls {@link JSplitPane#setResizeWeight} via reflection.
+ * For compatibility, since JDK &lt; 1.3 does not have this method.
+ */
+ static void setResizeWeight(JSplitPane pane, double weight) {
+ try {
+ Method m = JSplitPane.class.getMethod("setResizeWeight",
+ new Class[]{double.class});
+ m.invoke(pane, new Object[]{new Double(weight)});
+ } catch (NoSuchMethodException exc) {
+ } catch (IllegalAccessException exc) {
+ } catch (java.lang.reflect.InvocationTargetException exc) {
+ }
+ }
+
+ /**
+ * Reads the file with the given name and returns its contents as a String.
+ */
+ private String readFile(String fileName) {
+ String text;
+ try {
+ Reader r = new FileReader(fileName);
+ try {
+ text = Kit.readReader(r);
+ } finally {
+ r.close();
+ }
+ } catch (IOException ex) {
+ MessageDialogWrapper.showMessageDialog(this,
+ ex.getMessage(),
+ "Error reading "+fileName,
+ JOptionPane.ERROR_MESSAGE);
+ text = null;
+ }
+ return text;
+ }
+
+ // GuiCallback
+
+ /**
+ * Called when the source text for a script has been updated.
+ */
+ public void updateSourceText(Dim.SourceInfo sourceInfo) {
+ RunProxy proxy = new RunProxy(this, RunProxy.UPDATE_SOURCE_TEXT);
+ proxy.sourceInfo = sourceInfo;
+ SwingUtilities.invokeLater(proxy);
+ }
+
+ /**
+ * Called when the interrupt loop has been entered.
+ */
+ public void enterInterrupt(Dim.StackFrame lastFrame,
+ String threadTitle,
+ String alertMessage) {
+ if (SwingUtilities.isEventDispatchThread()) {
+ enterInterruptImpl(lastFrame, threadTitle, alertMessage);
+ } else {
+ RunProxy proxy = new RunProxy(this, RunProxy.ENTER_INTERRUPT);
+ proxy.lastFrame = lastFrame;
+ proxy.threadTitle = threadTitle;
+ proxy.alertMessage = alertMessage;
+ SwingUtilities.invokeLater(proxy);
+ }
+ }
+
+ /**
+ * Returns whether the current thread is the GUI event thread.
+ */
+ public boolean isGuiEventThread() {
+ return SwingUtilities.isEventDispatchThread();
+ }
+
+ /**
+ * Processes the next GUI event.
+ */
+ public void dispatchNextGuiEvent() throws InterruptedException {
+ EventQueue queue = awtEventQueue;
+ if (queue == null) {
+ queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
+ awtEventQueue = queue;
+ }
+ AWTEvent event = queue.getNextEvent();
+ if (event instanceof ActiveEvent) {
+ ((ActiveEvent)event).dispatch();
+ } else {
+ Object source = event.getSource();
+ if (source instanceof Component) {
+ Component comp = (Component)source;
+ comp.dispatchEvent(event);
+ } else if (source instanceof MenuComponent) {
+ ((MenuComponent)source).dispatchEvent(event);
+ }
+ }
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action from the menu or toolbar.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ int returnValue = -1;
+ if (cmd.equals("Cut") || cmd.equals("Copy") || cmd.equals("Paste")) {
+ JInternalFrame f = getSelectedFrame();
+ if (f != null && f instanceof ActionListener) {
+ ((ActionListener)f).actionPerformed(e);
+ }
+ } else if (cmd.equals("Step Over")) {
+ returnValue = Dim.STEP_OVER;
+ } else if (cmd.equals("Step Into")) {
+ returnValue = Dim.STEP_INTO;
+ } else if (cmd.equals("Step Out")) {
+ returnValue = Dim.STEP_OUT;
+ } else if (cmd.equals("Go")) {
+ returnValue = Dim.GO;
+ } else if (cmd.equals("Break")) {
+ dim.setBreak();
+ } else if (cmd.equals("Exit")) {
+ exit();
+ } else if (cmd.equals("Open")) {
+ String fileName = chooseFile("Select a file to compile");
+ if (fileName != null) {
+ String text = readFile(fileName);
+ if (text != null) {
+ RunProxy proxy = new RunProxy(this, RunProxy.OPEN_FILE);
+ proxy.fileName = fileName;
+ proxy.text = text;
+ new Thread(proxy).start();
+ }
+ }
+ } else if (cmd.equals("Load")) {
+ String fileName = chooseFile("Select a file to execute");
+ if (fileName != null) {
+ String text = readFile(fileName);
+ if (text != null) {
+ RunProxy proxy = new RunProxy(this, RunProxy.LOAD_FILE);
+ proxy.fileName = fileName;
+ proxy.text = text;
+ new Thread(proxy).start();
+ }
+ }
+ } else if (cmd.equals("More Windows...")) {
+ MoreWindows dlg = new MoreWindows(this, fileWindows,
+ "Window", "Files");
+ dlg.showDialog(this);
+ } else if (cmd.equals("Console")) {
+ if (console.isIcon()) {
+ desk.getDesktopManager().deiconifyFrame(console);
+ }
+ console.show();
+ desk.getDesktopManager().activateFrame(console);
+ console.consoleTextArea.requestFocus();
+ } else if (cmd.equals("Cut")) {
+ } else if (cmd.equals("Copy")) {
+ } else if (cmd.equals("Paste")) {
+ } else if (cmd.equals("Go to function...")) {
+ FindFunction dlg = new FindFunction(this, "Go to function",
+ "Function");
+ dlg.showDialog(this);
+ } else if (cmd.equals("Tile")) {
+ JInternalFrame[] frames = desk.getAllFrames();
+ int count = frames.length;
+ int rows, cols;
+ rows = cols = (int)Math.sqrt(count);
+ if (rows*cols < count) {
+ cols++;
+ if (rows * cols < count) {
+ rows++;
+ }
+ }
+ Dimension size = desk.getSize();
+ int w = size.width/cols;
+ int h = size.height/rows;
+ int x = 0;
+ int y = 0;
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < cols; j++) {
+ int index = (i*cols) + j;
+ if (index >= frames.length) {
+ break;
+ }
+ JInternalFrame f = frames[index];
+ try {
+ f.setIcon(false);
+ f.setMaximum(false);
+ } catch (Exception exc) {
+ }
+ desk.getDesktopManager().setBoundsForFrame(f, x, y,
+ w, h);
+ x += w;
+ }
+ y += h;
+ x = 0;
+ }
+ } else if (cmd.equals("Cascade")) {
+ JInternalFrame[] frames = desk.getAllFrames();
+ int count = frames.length;
+ int x, y, w, h;
+ x = y = 0;
+ h = desk.getHeight();
+ int d = h / count;
+ if (d > 30) d = 30;
+ for (int i = count -1; i >= 0; i--, x += d, y += d) {
+ JInternalFrame f = frames[i];
+ try {
+ f.setIcon(false);
+ f.setMaximum(false);
+ } catch (Exception exc) {
+ }
+ Dimension dimen = f.getPreferredSize();
+ w = dimen.width;
+ h = dimen.height;
+ desk.getDesktopManager().setBoundsForFrame(f, x, y, w, h);
+ }
+ } else {
+ Object obj = getFileWindow(cmd);
+ if (obj != null) {
+ FileWindow w = (FileWindow)obj;
+ try {
+ if (w.isIcon()) {
+ w.setIcon(false);
+ }
+ w.setVisible(true);
+ w.moveToFront();
+ w.setSelected(true);
+ } catch (Exception exc) {
+ }
+ }
+ }
+ if (returnValue != -1) {
+ updateEnabled(false);
+ dim.setReturnValue(returnValue);
+ }
+ }
+}
+
+/**
+ * Helper class for showing a message dialog.
+ */
+class MessageDialogWrapper {
+
+ /**
+ * Shows a message dialog, wrapping the <code>msg</code> at 60
+ * columns.
+ */
+ public static void showMessageDialog(Component parent, String msg,
+ String title, int flags) {
+ if (msg.length() > 60) {
+ StringBuffer buf = new StringBuffer();
+ int len = msg.length();
+ int j = 0;
+ int i;
+ for (i = 0; i < len; i++, j++) {
+ char c = msg.charAt(i);
+ buf.append(c);
+ if (Character.isWhitespace(c)) {
+ int k;
+ for (k = i + 1; k < len; k++) {
+ if (Character.isWhitespace(msg.charAt(k))) {
+ break;
+ }
+ }
+ if (k < len) {
+ int nextWordLen = k - i;
+ if (j + nextWordLen > 60) {
+ buf.append('\n');
+ j = 0;
+ }
+ }
+ }
+ }
+ msg = buf.toString();
+ }
+ JOptionPane.showMessageDialog(parent, msg, title, flags);
+ }
+}
+
+/**
+ * Extension of JTextArea for script evaluation input.
+ */
+class EvalTextArea
+ extends JTextArea
+ implements KeyListener, DocumentListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = -3918033649601064194L;
+
+ /**
+ * The debugger GUI.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * History of expressions that have been evaluated
+ */
+ private Vector history;
+
+ /**
+ * Index of the selected history item.
+ */
+ private int historyIndex = -1;
+
+ /**
+ * Position in the display where output should go.
+ */
+ private int outputMark;
+
+ /**
+ * Creates a new EvalTextArea.
+ */
+ public EvalTextArea(SwingGui debugGui) {
+ this.debugGui = debugGui;
+ history = new java.util.Vector();
+ Document doc = getDocument();
+ doc.addDocumentListener(this);
+ addKeyListener(this);
+ setLineWrap(true);
+ setFont(new Font("Monospaced", 0, 12));
+ append("% ");
+ outputMark = doc.getLength();
+ }
+
+ /**
+ * Selects a subrange of the text.
+ */
+ public void select(int start, int end) {
+ //requestFocus();
+ super.select(start, end);
+ }
+
+ /**
+ * Called when Enter is pressed.
+ */
+ private synchronized void returnPressed() {
+ Document doc = getDocument();
+ int len = doc.getLength();
+ Segment segment = new Segment();
+ try {
+ doc.getText(outputMark, len - outputMark, segment);
+ } catch (javax.swing.text.BadLocationException ignored) {
+ ignored.printStackTrace();
+ }
+ String text = segment.toString();
+ if (debugGui.dim.stringIsCompilableUnit(text)) {
+ if (text.trim().length() > 0) {
+ history.addElement(text);
+ historyIndex = history.size();
+ }
+ append("\n");
+ String result = debugGui.dim.eval(text);
+ if (result.length() > 0) {
+ append(result);
+ append("\n");
+ }
+ append("% ");
+ outputMark = doc.getLength();
+ } else {
+ append("\n");
+ }
+ }
+
+ /**
+ * Writes output into the text area.
+ */
+ public synchronized void write(String str) {
+ insert(str, outputMark);
+ int len = str.length();
+ outputMark += len;
+ select(outputMark, outputMark);
+ }
+
+ // KeyListener
+
+ /**
+ * Called when a key is pressed.
+ */
+ public void keyPressed(KeyEvent e) {
+ int code = e.getKeyCode();
+ if (code == KeyEvent.VK_BACK_SPACE || code == KeyEvent.VK_LEFT) {
+ if (outputMark == getCaretPosition()) {
+ e.consume();
+ }
+ } else if (code == KeyEvent.VK_HOME) {
+ int caretPos = getCaretPosition();
+ if (caretPos == outputMark) {
+ e.consume();
+ } else if (caretPos > outputMark) {
+ if (!e.isControlDown()) {
+ if (e.isShiftDown()) {
+ moveCaretPosition(outputMark);
+ } else {
+ setCaretPosition(outputMark);
+ }
+ e.consume();
+ }
+ }
+ } else if (code == KeyEvent.VK_ENTER) {
+ returnPressed();
+ e.consume();
+ } else if (code == KeyEvent.VK_UP) {
+ historyIndex--;
+ if (historyIndex >= 0) {
+ if (historyIndex >= history.size()) {
+ historyIndex = history.size() -1;
+ }
+ if (historyIndex >= 0) {
+ String str = (String)history.elementAt(historyIndex);
+ int len = getDocument().getLength();
+ replaceRange(str, outputMark, len);
+ int caretPos = outputMark + str.length();
+ select(caretPos, caretPos);
+ } else {
+ historyIndex++;
+ }
+ } else {
+ historyIndex++;
+ }
+ e.consume();
+ } else if (code == KeyEvent.VK_DOWN) {
+ int caretPos = outputMark;
+ if (history.size() > 0) {
+ historyIndex++;
+ if (historyIndex < 0) {historyIndex = 0;}
+ int len = getDocument().getLength();
+ if (historyIndex < history.size()) {
+ String str = (String)history.elementAt(historyIndex);
+ replaceRange(str, outputMark, len);
+ caretPos = outputMark + str.length();
+ } else {
+ historyIndex = history.size();
+ replaceRange("", outputMark, len);
+ }
+ }
+ select(caretPos, caretPos);
+ e.consume();
+ }
+ }
+
+ /**
+ * Called when a key is typed.
+ */
+ public void keyTyped(KeyEvent e) {
+ int keyChar = e.getKeyChar();
+ if (keyChar == 0x8 /* KeyEvent.VK_BACK_SPACE */) {
+ if (outputMark == getCaretPosition()) {
+ e.consume();
+ }
+ } else if (getCaretPosition() < outputMark) {
+ setCaretPosition(outputMark);
+ }
+ }
+
+ /**
+ * Called when a key is released.
+ */
+ public synchronized void keyReleased(KeyEvent e) {
+ }
+
+ // DocumentListener
+
+ /**
+ * Called when text was inserted into the text area.
+ */
+ public synchronized void insertUpdate(DocumentEvent e) {
+ int len = e.getLength();
+ int off = e.getOffset();
+ if (outputMark > off) {
+ outputMark += len;
+ }
+ }
+
+ /**
+ * Called when text was removed from the text area.
+ */
+ public synchronized void removeUpdate(DocumentEvent e) {
+ int len = e.getLength();
+ int off = e.getOffset();
+ if (outputMark > off) {
+ if (outputMark >= off + len) {
+ outputMark -= len;
+ } else {
+ outputMark = off;
+ }
+ }
+ }
+
+ /**
+ * Attempts to clean up the damage done by {@link #updateUI()}.
+ */
+ public synchronized void postUpdateUI() {
+ //requestFocus();
+ setCaret(getCaret());
+ select(outputMark, outputMark);
+ }
+
+ /**
+ * Called when text has changed in the text area.
+ */
+ public synchronized void changedUpdate(DocumentEvent e) {
+ }
+}
+
+/**
+ * An internal frame for evaluating script.
+ */
+class EvalWindow extends JInternalFrame implements ActionListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = -2860585845212160176L;
+
+ /**
+ * The text area into which expressions can be typed.
+ */
+ private EvalTextArea evalTextArea;
+
+ /**
+ * Creates a new EvalWindow.
+ */
+ public EvalWindow(String name, SwingGui debugGui) {
+ super(name, true, false, true, true);
+ evalTextArea = new EvalTextArea(debugGui);
+ evalTextArea.setRows(24);
+ evalTextArea.setColumns(80);
+ JScrollPane scroller = new JScrollPane(evalTextArea);
+ setContentPane(scroller);
+ //scroller.setPreferredSize(new Dimension(600, 400));
+ pack();
+ setVisible(true);
+ }
+
+ /**
+ * Sets whether the text area is enabled.
+ */
+ public void setEnabled(boolean b) {
+ super.setEnabled(b);
+ evalTextArea.setEnabled(b);
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action on the text area.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ if (cmd.equals("Cut")) {
+ evalTextArea.cut();
+ } else if (cmd.equals("Copy")) {
+ evalTextArea.copy();
+ } else if (cmd.equals("Paste")) {
+ evalTextArea.paste();
+ }
+ }
+}
+
+/**
+ * Internal frame for the console.
+ */
+class JSInternalConsole extends JInternalFrame implements ActionListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = -5523468828771087292L;
+
+ /**
+ * Creates a new JSInternalConsole.
+ */
+ public JSInternalConsole(String name) {
+ super(name, true, false, true, true);
+ consoleTextArea = new ConsoleTextArea(null);
+ consoleTextArea.setRows(24);
+ consoleTextArea.setColumns(80);
+ JScrollPane scroller = new JScrollPane(consoleTextArea);
+ setContentPane(scroller);
+ pack();
+ addInternalFrameListener(new InternalFrameAdapter() {
+ public void internalFrameActivated(InternalFrameEvent e) {
+ // hack
+ if (consoleTextArea.hasFocus()) {
+ consoleTextArea.getCaret().setVisible(false);
+ consoleTextArea.getCaret().setVisible(true);
+ }
+ }
+ });
+ }
+
+ /**
+ * The console text area.
+ */
+ ConsoleTextArea consoleTextArea;
+
+ /**
+ * Returns the input stream of the console text area.
+ */
+ public InputStream getIn() {
+ return consoleTextArea.getIn();
+ }
+
+ /**
+ * Returns the output stream of the console text area.
+ */
+ public PrintStream getOut() {
+ return consoleTextArea.getOut();
+ }
+
+ /**
+ * Returns the error stream of the console text area.
+ */
+ public PrintStream getErr() {
+ return consoleTextArea.getErr();
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action on the text area.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ if (cmd.equals("Cut")) {
+ consoleTextArea.cut();
+ } else if (cmd.equals("Copy")) {
+ consoleTextArea.copy();
+ } else if (cmd.equals("Paste")) {
+ consoleTextArea.paste();
+ }
+ }
+}
+
+/**
+ * Popup menu class for right-clicking on {@link FileTextArea}s.
+ */
+class FilePopupMenu extends JPopupMenu {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 3589525009546013565L;
+
+ /**
+ * The popup x position.
+ */
+ int x;
+
+ /**
+ * The popup y position.
+ */
+ int y;
+
+ /**
+ * Creates a new FilePopupMenu.
+ */
+ public FilePopupMenu(FileTextArea w) {
+ JMenuItem item;
+ add(item = new JMenuItem("Set Breakpoint"));
+ item.addActionListener(w);
+ add(item = new JMenuItem("Clear Breakpoint"));
+ item.addActionListener(w);
+ add(item = new JMenuItem("Run"));
+ item.addActionListener(w);
+ }
+
+ /**
+ * Displays the menu at the given coordinates.
+ */
+ public void show(JComponent comp, int x, int y) {
+ this.x = x;
+ this.y = y;
+ super.show(comp, x, y);
+ }
+}
+
+/**
+ * Text area to display script source.
+ */
+class FileTextArea
+ extends JTextArea
+ implements ActionListener, PopupMenuListener, KeyListener, MouseListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = -25032065448563720L;
+
+ /**
+ * The owning {@link FileWindow}.
+ */
+ private FileWindow w;
+
+ /**
+ * The popup menu.
+ */
+ private FilePopupMenu popup;
+
+ /**
+ * Creates a new FileTextArea.
+ */
+ public FileTextArea(FileWindow w) {
+ this.w = w;
+ popup = new FilePopupMenu(this);
+ popup.addPopupMenuListener(this);
+ addMouseListener(this);
+ addKeyListener(this);
+ setFont(new Font("Monospaced", 0, 12));
+ }
+
+ /**
+ * Moves the selection to the given offset.
+ */
+ public void select(int pos) {
+ if (pos >= 0) {
+ try {
+ int line = getLineOfOffset(pos);
+ Rectangle rect = modelToView(pos);
+ if (rect == null) {
+ select(pos, pos);
+ } else {
+ try {
+ Rectangle nrect =
+ modelToView(getLineStartOffset(line + 1));
+ if (nrect != null) {
+ rect = nrect;
+ }
+ } catch (Exception exc) {
+ }
+ JViewport vp = (JViewport)getParent();
+ Rectangle viewRect = vp.getViewRect();
+ if (viewRect.y + viewRect.height > rect.y) {
+ // need to scroll up
+ select(pos, pos);
+ } else {
+ // need to scroll down
+ rect.y += (viewRect.height - rect.height)/2;
+ scrollRectToVisible(rect);
+ select(pos, pos);
+ }
+ }
+ } catch (BadLocationException exc) {
+ select(pos, pos);
+ //exc.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Checks if the popup menu should be shown.
+ */
+ private void checkPopup(MouseEvent e) {
+ if (e.isPopupTrigger()) {
+ popup.show(this, e.getX(), e.getY());
+ }
+ }
+
+ // MouseListener
+
+ /**
+ * Called when a mouse button is pressed.
+ */
+ public void mousePressed(MouseEvent e) {
+ checkPopup(e);
+ }
+
+ /**
+ * Called when the mouse is clicked.
+ */
+ public void mouseClicked(MouseEvent e) {
+ checkPopup(e);
+ requestFocus();
+ getCaret().setVisible(true);
+ }
+
+ /**
+ * Called when the mouse enters the component.
+ */
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ /**
+ * Called when the mouse exits the component.
+ */
+ public void mouseExited(MouseEvent e) {
+ }
+
+ /**
+ * Called when a mouse button is released.
+ */
+ public void mouseReleased(MouseEvent e) {
+ checkPopup(e);
+ }
+
+ // PopupMenuListener
+
+ /**
+ * Called before the popup menu will become visible.
+ */
+ public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
+ }
+
+ /**
+ * Called before the popup menu will become invisible.
+ */
+ public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
+ }
+
+ /**
+ * Called when the popup menu is cancelled.
+ */
+ public void popupMenuCanceled(PopupMenuEvent e) {
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action.
+ */
+ public void actionPerformed(ActionEvent e) {
+ int pos = viewToModel(new Point(popup.x, popup.y));
+ popup.setVisible(false);
+ String cmd = e.getActionCommand();
+ int line = -1;
+ try {
+ line = getLineOfOffset(pos);
+ } catch (Exception exc) {
+ }
+ if (cmd.equals("Set Breakpoint")) {
+ w.setBreakPoint(line + 1);
+ } else if (cmd.equals("Clear Breakpoint")) {
+ w.clearBreakPoint(line + 1);
+ } else if (cmd.equals("Run")) {
+ w.load();
+ }
+ }
+
+ // KeyListener
+
+ /**
+ * Called when a key is pressed.
+ */
+ public void keyPressed(KeyEvent e) {
+ switch (e.getKeyCode()) {
+ case KeyEvent.VK_BACK_SPACE:
+ case KeyEvent.VK_ENTER:
+ case KeyEvent.VK_DELETE:
+ case KeyEvent.VK_TAB:
+ e.consume();
+ break;
+ }
+ }
+
+ /**
+ * Called when a key is typed.
+ */
+ public void keyTyped(KeyEvent e) {
+ e.consume();
+ }
+
+ /**
+ * Called when a key is released.
+ */
+ public void keyReleased(KeyEvent e) {
+ e.consume();
+ }
+}
+
+/**
+ * Dialog to list the available windows.
+ */
+class MoreWindows extends JDialog implements ActionListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 5177066296457377546L;
+
+ /**
+ * Last selected value.
+ */
+ private String value;
+
+ /**
+ * The list component.
+ */
+ private JList list;
+
+ /**
+ * Our parent frame.
+ */
+ private SwingGui swingGui;
+
+ /**
+ * The "Select" button.
+ */
+ private JButton setButton;
+
+ /**
+ * The "Cancel" button.
+ */
+ private JButton cancelButton;
+
+ /**
+ * Creates a new MoreWindows.
+ */
+ MoreWindows(SwingGui frame, Hashtable fileWindows, String title,
+ String labelText) {
+ super(frame, title, true);
+ this.swingGui = frame;
+ //buttons
+ cancelButton = new JButton("Cancel");
+ setButton = new JButton("Select");
+ cancelButton.addActionListener(this);
+ setButton.addActionListener(this);
+ getRootPane().setDefaultButton(setButton);
+
+ //dim part of the dialog
+ list = new JList(new DefaultListModel());
+ DefaultListModel model = (DefaultListModel)list.getModel();
+ model.clear();
+ //model.fireIntervalRemoved(model, 0, size);
+ Enumeration e = fileWindows.keys();
+ while (e.hasMoreElements()) {
+ String data = e.nextElement().toString();
+ model.addElement(data);
+ }
+ list.setSelectedIndex(0);
+ //model.fireIntervalAdded(model, 0, data.length);
+ setButton.setEnabled(true);
+ list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+ list.addMouseListener(new MouseHandler());
+ JScrollPane listScroller = new JScrollPane(list);
+ listScroller.setPreferredSize(new Dimension(320, 240));
+ //XXX: Must do the following, too, or else the scroller thinks
+ //XXX: it's taller than it is:
+ listScroller.setMinimumSize(new Dimension(250, 80));
+ listScroller.setAlignmentX(LEFT_ALIGNMENT);
+
+ //Create a container so that we can add a title around
+ //the scroll pane. Can't add a title directly to the
+ //scroll pane because its background would be white.
+ //Lay out the label and scroll pane from top to button.
+ JPanel listPane = new JPanel();
+ listPane.setLayout(new BoxLayout(listPane, BoxLayout.Y_AXIS));
+ JLabel label = new JLabel(labelText);
+ label.setLabelFor (list);
+ listPane.add(label);
+ listPane.add(Box.createRigidArea(new Dimension(0,5)));
+ listPane.add(listScroller);
+ listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ //Lay out the buttons from left to right.
+ JPanel buttonPane = new JPanel();
+ buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
+ buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
+ buttonPane.add(Box.createHorizontalGlue());
+ buttonPane.add(cancelButton);
+ buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
+ buttonPane.add(setButton);
+
+ //Put everything together, using the content pane's BorderLayout.
+ Container contentPane = getContentPane();
+ contentPane.add(listPane, BorderLayout.CENTER);
+ contentPane.add(buttonPane, BorderLayout.SOUTH);
+ pack();
+ addKeyListener(new KeyAdapter() {
+ public void keyPressed(KeyEvent ke) {
+ int code = ke.getKeyCode();
+ if (code == KeyEvent.VK_ESCAPE) {
+ ke.consume();
+ value = null;
+ setVisible(false);
+ }
+ }
+ });
+ }
+
+ /**
+ * Shows the dialog.
+ */
+ public String showDialog(Component comp) {
+ value = null;
+ setLocationRelativeTo(comp);
+ setVisible(true);
+ return value;
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ if (cmd.equals("Cancel")) {
+ setVisible(false);
+ value = null;
+ } else if (cmd.equals("Select")) {
+ value = (String)list.getSelectedValue();
+ setVisible(false);
+ swingGui.showFileWindow(value, -1);
+ }
+ }
+
+ /**
+ * MouseListener implementation for {@link #list}.
+ */
+ private class MouseHandler extends MouseAdapter {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ setButton.doClick();
+ }
+ }
+ }
+}
+
+/**
+ * Find function dialog.
+ */
+class FindFunction extends JDialog implements ActionListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 559491015232880916L;
+
+ /**
+ * Last selected function.
+ */
+ private String value;
+
+ /**
+ * List of functions.
+ */
+ private JList list;
+
+ /**
+ * The debug GUI frame.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * The "Select" button.
+ */
+ private JButton setButton;
+
+ /**
+ * The "Cancel" button.
+ */
+ private JButton cancelButton;
+
+ /**
+ * Creates a new FindFunction.
+ */
+ public FindFunction(SwingGui debugGui, String title, String labelText) {
+ super(debugGui, title, true);
+ this.debugGui = debugGui;
+
+ cancelButton = new JButton("Cancel");
+ setButton = new JButton("Select");
+ cancelButton.addActionListener(this);
+ setButton.addActionListener(this);
+ getRootPane().setDefaultButton(setButton);
+
+ list = new JList(new DefaultListModel());
+ DefaultListModel model = (DefaultListModel)list.getModel();
+ model.clear();
+
+ String[] a = debugGui.dim.functionNames();
+ java.util.Arrays.sort(a);
+ for (int i = 0; i < a.length; i++) {
+ model.addElement(a[i]);
+ }
+ list.setSelectedIndex(0);
+
+ setButton.setEnabled(a.length > 0);
+ list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+ list.addMouseListener(new MouseHandler());
+ JScrollPane listScroller = new JScrollPane(list);
+ listScroller.setPreferredSize(new Dimension(320, 240));
+ listScroller.setMinimumSize(new Dimension(250, 80));
+ listScroller.setAlignmentX(LEFT_ALIGNMENT);
+
+ //Create a container so that we can add a title around
+ //the scroll pane. Can't add a title directly to the
+ //scroll pane because its background would be white.
+ //Lay out the label and scroll pane from top to button.
+ JPanel listPane = new JPanel();
+ listPane.setLayout(new BoxLayout(listPane, BoxLayout.Y_AXIS));
+ JLabel label = new JLabel(labelText);
+ label.setLabelFor (list);
+ listPane.add(label);
+ listPane.add(Box.createRigidArea(new Dimension(0,5)));
+ listPane.add(listScroller);
+ listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ //Lay out the buttons from left to right.
+ JPanel buttonPane = new JPanel();
+ buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
+ buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
+ buttonPane.add(Box.createHorizontalGlue());
+ buttonPane.add(cancelButton);
+ buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
+ buttonPane.add(setButton);
+
+ //Put everything together, using the content pane's BorderLayout.
+ Container contentPane = getContentPane();
+ contentPane.add(listPane, BorderLayout.CENTER);
+ contentPane.add(buttonPane, BorderLayout.SOUTH);
+ pack();
+ addKeyListener(new KeyAdapter() {
+ public void keyPressed(KeyEvent ke) {
+ int code = ke.getKeyCode();
+ if (code == KeyEvent.VK_ESCAPE) {
+ ke.consume();
+ value = null;
+ setVisible(false);
+ }
+ }
+ });
+ }
+
+ /**
+ * Shows the dialog.
+ */
+ public String showDialog(Component comp) {
+ value = null;
+ setLocationRelativeTo(comp);
+ setVisible(true);
+ return value;
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ if (cmd.equals("Cancel")) {
+ setVisible(false);
+ value = null;
+ } else if (cmd.equals("Select")) {
+ if (list.getSelectedIndex() < 0) {
+ return;
+ }
+ try {
+ value = (String)list.getSelectedValue();
+ } catch (ArrayIndexOutOfBoundsException exc) {
+ return;
+ }
+ setVisible(false);
+ Dim.FunctionSource item = debugGui.dim.functionSourceByName(value);
+ if (item != null) {
+ Dim.SourceInfo si = item.sourceInfo();
+ String url = si.url();
+ int lineNumber = item.firstLine();
+ debugGui.showFileWindow(url, lineNumber);
+ }
+ }
+ }
+
+ /**
+ * MouseListener implementation for {@link #list}.
+ */
+ class MouseHandler extends MouseAdapter {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ setButton.doClick();
+ }
+ }
+ }
+}
+
+/**
+ * Gutter for FileWindows.
+ */
+class FileHeader extends JPanel implements MouseListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = -2858905404778259127L;
+
+ /**
+ * The line that the mouse was pressed on.
+ */
+ private int pressLine = -1;
+
+ /**
+ * The owning FileWindow.
+ */
+ private FileWindow fileWindow;
+
+ /**
+ * Creates a new FileHeader.
+ */
+ public FileHeader(FileWindow fileWindow) {
+ this.fileWindow = fileWindow;
+ addMouseListener(this);
+ update();
+ }
+
+ /**
+ * Updates the gutter.
+ */
+ public void update() {
+ FileTextArea textArea = fileWindow.textArea;
+ Font font = textArea.getFont();
+ setFont(font);
+ FontMetrics metrics = getFontMetrics(font);
+ int h = metrics.getHeight();
+ int lineCount = textArea.getLineCount() + 1;
+ String dummy = Integer.toString(lineCount);
+ if (dummy.length() < 2) {
+ dummy = "99";
+ }
+ Dimension d = new Dimension();
+ d.width = metrics.stringWidth(dummy) + 16;
+ d.height = lineCount * h + 100;
+ setPreferredSize(d);
+ setSize(d);
+ }
+
+ /**
+ * Paints the component.
+ */
+ public void paint(Graphics g) {
+ super.paint(g);
+ FileTextArea textArea = fileWindow.textArea;
+ Font font = textArea.getFont();
+ g.setFont(font);
+ FontMetrics metrics = getFontMetrics(font);
+ Rectangle clip = g.getClipBounds();
+ g.setColor(getBackground());
+ g.fillRect(clip.x, clip.y, clip.width, clip.height);
+ int ascent = metrics.getMaxAscent();
+ int h = metrics.getHeight();
+ int lineCount = textArea.getLineCount() + 1;
+ String dummy = Integer.toString(lineCount);
+ if (dummy.length() < 2) {
+ dummy = "99";
+ }
+ int startLine = clip.y / h;
+ int endLine = (clip.y + clip.height) / h + 1;
+ int width = getWidth();
+ if (endLine > lineCount) endLine = lineCount;
+ for (int i = startLine; i < endLine; i++) {
+ String text;
+ int pos = -2;
+ try {
+ pos = textArea.getLineStartOffset(i);
+ } catch (BadLocationException ignored) {
+ }
+ boolean isBreakPoint = fileWindow.isBreakPoint(i + 1);
+ text = Integer.toString(i + 1) + " ";
+ int y = i * h;
+ g.setColor(Color.blue);
+ g.drawString(text, 0, y + ascent);
+ int x = width - ascent;
+ if (isBreakPoint) {
+ g.setColor(new Color(0x80, 0x00, 0x00));
+ int dy = y + ascent - 9;
+ g.fillOval(x, dy, 9, 9);
+ g.drawOval(x, dy, 8, 8);
+ g.drawOval(x, dy, 9, 9);
+ }
+ if (pos == fileWindow.currentPos) {
+ Polygon arrow = new Polygon();
+ int dx = x;
+ y += ascent - 10;
+ int dy = y;
+ arrow.addPoint(dx, dy + 3);
+ arrow.addPoint(dx + 5, dy + 3);
+ for (x = dx + 5; x <= dx + 10; x++, y++) {
+ arrow.addPoint(x, y);
+ }
+ for (x = dx + 9; x >= dx + 5; x--, y++) {
+ arrow.addPoint(x, y);
+ }
+ arrow.addPoint(dx + 5, dy + 7);
+ arrow.addPoint(dx, dy + 7);
+ g.setColor(Color.yellow);
+ g.fillPolygon(arrow);
+ g.setColor(Color.black);
+ g.drawPolygon(arrow);
+ }
+ }
+ }
+
+ // MouseListener
+
+ /**
+ * Called when the mouse enters the component.
+ */
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ /**
+ * Called when a mouse button is pressed.
+ */
+ public void mousePressed(MouseEvent e) {
+ Font font = fileWindow.textArea.getFont();
+ FontMetrics metrics = getFontMetrics(font);
+ int h = metrics.getHeight();
+ pressLine = e.getY() / h;
+ }
+
+ /**
+ * Called when the mouse is clicked.
+ */
+ public void mouseClicked(MouseEvent e) {
+ }
+
+ /**
+ * Called when the mouse exits the component.
+ */
+ public void mouseExited(MouseEvent e) {
+ }
+
+ /**
+ * Called when a mouse button is released.
+ */
+ public void mouseReleased(MouseEvent e) {
+ if (e.getComponent() == this
+ && (e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
+ int y = e.getY();
+ Font font = fileWindow.textArea.getFont();
+ FontMetrics metrics = getFontMetrics(font);
+ int h = metrics.getHeight();
+ int line = y/h;
+ if (line == pressLine) {
+ fileWindow.toggleBreakPoint(line + 1);
+ } else {
+ pressLine = -1;
+ }
+ }
+ }
+}
+
+/**
+ * An internal frame for script files.
+ */
+class FileWindow extends JInternalFrame implements ActionListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = -6212382604952082370L;
+
+ /**
+ * The debugger GUI.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * The SourceInfo object that describes the file.
+ */
+ private Dim.SourceInfo sourceInfo;
+
+ /**
+ * The FileTextArea that displays the file.
+ */
+ FileTextArea textArea;
+
+ /**
+ * The FileHeader that is the gutter for {@link #textArea}.
+ */
+ private FileHeader fileHeader;
+
+ /**
+ * Scroll pane for containing {@link #textArea}.
+ */
+ private JScrollPane p;
+
+ /**
+ * The current offset position.
+ */
+ int currentPos;
+
+ /**
+ * Loads the file.
+ */
+ void load() {
+ String url = getUrl();
+ if (url != null) {
+ RunProxy proxy = new RunProxy(debugGui, RunProxy.LOAD_FILE);
+ proxy.fileName = url;
+ proxy.text = sourceInfo.source();
+ new Thread(proxy).start();
+ }
+ }
+
+ /**
+ * Returns the offset position for the given line.
+ */
+ public int getPosition(int line) {
+ int result = -1;
+ try {
+ result = textArea.getLineStartOffset(line);
+ } catch (javax.swing.text.BadLocationException exc) {
+ }
+ return result;
+ }
+
+ /**
+ * Returns whether the given line has a breakpoint.
+ */
+ public boolean isBreakPoint(int line) {
+ return sourceInfo.breakableLine(line) && sourceInfo.breakpoint(line);
+ }
+
+ /**
+ * Toggles the breakpoint on the given line.
+ */
+ public void toggleBreakPoint(int line) {
+ if (!isBreakPoint(line)) {
+ setBreakPoint(line);
+ } else {
+ clearBreakPoint(line);
+ }
+ }
+
+ /**
+ * Sets a breakpoint on the given line.
+ */
+ public void setBreakPoint(int line) {
+ if (sourceInfo.breakableLine(line)) {
+ boolean changed = sourceInfo.breakpoint(line, true);
+ if (changed) {
+ fileHeader.repaint();
+ }
+ }
+ }
+
+ /**
+ * Clears a breakpoint from the given line.
+ */
+ public void clearBreakPoint(int line) {
+ if (sourceInfo.breakableLine(line)) {
+ boolean changed = sourceInfo.breakpoint(line, false);
+ if (changed) {
+ fileHeader.repaint();
+ }
+ }
+ }
+
+ /**
+ * Creates a new FileWindow.
+ */
+ public FileWindow(SwingGui debugGui, Dim.SourceInfo sourceInfo) {
+ super(SwingGui.getShortName(sourceInfo.url()),
+ true, true, true, true);
+ this.debugGui = debugGui;
+ this.sourceInfo = sourceInfo;
+ updateToolTip();
+ currentPos = -1;
+ textArea = new FileTextArea(this);
+ textArea.setRows(24);
+ textArea.setColumns(80);
+ p = new JScrollPane();
+ fileHeader = new FileHeader(this);
+ p.setViewportView(textArea);
+ p.setRowHeaderView(fileHeader);
+ setContentPane(p);
+ pack();
+ updateText(sourceInfo);
+ textArea.select(0);
+ }
+
+ /**
+ * Updates the tool tip contents.
+ */
+ private void updateToolTip() {
+ // Try to set tool tip on frame. On Mac OS X 10.5,
+ // the number of components is different, so try to be safe.
+ int n = getComponentCount() - 1;
+ if (n > 1) {
+ n = 1;
+ } else if (n < 0) {
+ return;
+ }
+ Component c = getComponent(n);
+ // this will work at least for Metal L&F
+ if (c != null && c instanceof JComponent) {
+ ((JComponent)c).setToolTipText(getUrl());
+ }
+ }
+
+ /**
+ * Returns the URL of the source.
+ */
+ public String getUrl() {
+ return sourceInfo.url();
+ }
+
+ /**
+ * Called when the text of the script has changed.
+ */
+ public void updateText(Dim.SourceInfo sourceInfo) {
+ this.sourceInfo = sourceInfo;
+ String newText = sourceInfo.source();
+ if (!textArea.getText().equals(newText)) {
+ textArea.setText(newText);
+ int pos = 0;
+ if (currentPos != -1) {
+ pos = currentPos;
+ }
+ textArea.select(pos);
+ }
+ fileHeader.update();
+ fileHeader.repaint();
+ }
+
+ /**
+ * Sets the cursor position.
+ */
+ public void setPosition(int pos) {
+ textArea.select(pos);
+ currentPos = pos;
+ fileHeader.repaint();
+ }
+
+ /**
+ * Selects a range of characters.
+ */
+ public void select(int start, int end) {
+ int docEnd = textArea.getDocument().getLength();
+ textArea.select(docEnd, docEnd);
+ textArea.select(start, end);
+ }
+
+ /**
+ * Disposes this FileWindow.
+ */
+ public void dispose() {
+ debugGui.removeWindow(this);
+ super.dispose();
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ if (cmd.equals("Cut")) {
+ // textArea.cut();
+ } else if (cmd.equals("Copy")) {
+ textArea.copy();
+ } else if (cmd.equals("Paste")) {
+ // textArea.paste();
+ }
+ }
+}
+
+/**
+ * Table model class for watched expressions.
+ */
+class MyTableModel extends AbstractTableModel {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 2971618907207577000L;
+
+ /**
+ * The debugger GUI.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * Vector of watched expressions.
+ */
+ private Vector expressions;
+
+ /**
+ * Vector of values from evaluated from {@link #expressions}.
+ */
+ private Vector values;
+
+ /**
+ * Creates a new MyTableModel.
+ */
+ public MyTableModel(SwingGui debugGui) {
+ this.debugGui = debugGui;
+ expressions = new Vector();
+ values = new Vector();
+ expressions.addElement("");
+ values.addElement("");
+ }
+
+ /**
+ * Returns the number of columns in the table (2).
+ */
+ public int getColumnCount() {
+ return 2;
+ }
+
+ /**
+ * Returns the number of rows in the table.
+ */
+ public int getRowCount() {
+ return expressions.size();
+ }
+
+ /**
+ * Returns the name of the given column.
+ */
+ public String getColumnName(int column) {
+ switch (column) {
+ case 0:
+ return "Expression";
+ case 1:
+ return "Value";
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether the given cell is editable.
+ */
+ public boolean isCellEditable(int row, int column) {
+ return true;
+ }
+
+ /**
+ * Returns the value in the given cell.
+ */
+ public Object getValueAt(int row, int column) {
+ switch (column) {
+ case 0:
+ return expressions.elementAt(row);
+ case 1:
+ return values.elementAt(row);
+ }
+ return "";
+ }
+
+ /**
+ * Sets the value in the given cell.
+ */
+ public void setValueAt(Object value, int row, int column) {
+ switch (column) {
+ case 0:
+ String expr = value.toString();
+ expressions.setElementAt(expr, row);
+ String result = "";
+ if (expr.length() > 0) {
+ result = debugGui.dim.eval(expr);
+ if (result == null) result = "";
+ }
+ values.setElementAt(result, row);
+ updateModel();
+ if (row + 1 == expressions.size()) {
+ expressions.addElement("");
+ values.addElement("");
+ fireTableRowsInserted(row + 1, row + 1);
+ }
+ break;
+ case 1:
+ // just reset column 2; ignore edits
+ fireTableDataChanged();
+ }
+ }
+
+ /**
+ * Re-evaluates the expressions in the table.
+ */
+ void updateModel() {
+ for (int i = 0; i < expressions.size(); ++i) {
+ Object value = expressions.elementAt(i);
+ String expr = value.toString();
+ String result = "";
+ if (expr.length() > 0) {
+ result = debugGui.dim.eval(expr);
+ if (result == null) result = "";
+ } else {
+ result = "";
+ }
+ result = result.replace('\n', ' ');
+ values.setElementAt(result, i);
+ }
+ fireTableDataChanged();
+ }
+}
+
+/**
+ * A table for evaluated expressions.
+ */
+class Evaluator extends JTable {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 8133672432982594256L;
+
+ /**
+ * The {@link TableModel} for this table.
+ */
+ MyTableModel tableModel;
+
+ /**
+ * Creates a new Evaluator.
+ */
+ public Evaluator(SwingGui debugGui) {
+ super(new MyTableModel(debugGui));
+ tableModel = (MyTableModel)getModel();
+ }
+}
+
+/**
+ * Tree model for script object inspection.
+ */
+class VariableModel implements TreeTableModel {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final String[] cNames = { " Name", " Value" };
+
+ /**
+ * Tree column types.
+ */
+ private static final Class[] cTypes =
+ { TreeTableModel.class, String.class };
+
+ /**
+ * Empty {@link VariableNode} array.
+ */
+ private static final VariableNode[] CHILDLESS = new VariableNode[0];
+
+ /**
+ * The debugger.
+ */
+ private Dim debugger;
+
+ /**
+ * The root node.
+ */
+ private VariableNode root;
+
+ /**
+ * Creates a new VariableModel.
+ */
+ public VariableModel() {
+ }
+
+ /**
+ * Creates a new VariableModel.
+ */
+ public VariableModel(Dim debugger, Object scope) {
+ this.debugger = debugger;
+ this.root = new VariableNode(scope, "this");
+ }
+
+ // TreeTableModel
+
+ /**
+ * Returns the root node of the tree.
+ */
+ public Object getRoot() {
+ if (debugger == null) {
+ return null;
+ }
+ return root;
+ }
+
+ /**
+ * Returns the number of children of the given node.
+ */
+ public int getChildCount(Object nodeObj) {
+ if (debugger == null) {
+ return 0;
+ }
+ VariableNode node = (VariableNode) nodeObj;
+ return children(node).length;
+ }
+
+ /**
+ * Returns a child of the given node.
+ */
+ public Object getChild(Object nodeObj, int i) {
+ if (debugger == null) {
+ return null;
+ }
+ VariableNode node = (VariableNode) nodeObj;
+ return children(node)[i];
+ }
+
+ /**
+ * Returns whether the given node is a leaf node.
+ */
+ public boolean isLeaf(Object nodeObj) {
+ if (debugger == null) {
+ return true;
+ }
+ VariableNode node = (VariableNode) nodeObj;
+ return children(node).length == 0;
+ }
+
+ /**
+ * Returns the index of a node under its parent.
+ */
+ public int getIndexOfChild(Object parentObj, Object childObj) {
+ if (debugger == null) {
+ return -1;
+ }
+ VariableNode parent = (VariableNode) parentObj;
+ VariableNode child = (VariableNode) childObj;
+ VariableNode[] children = children(parent);
+ for (int i = 0; i != children.length; i++) {
+ if (children[i] == child) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns whether the given cell is editable.
+ */
+ public boolean isCellEditable(Object node, int column) {
+ return column == 0;
+ }
+
+ /**
+ * Sets the value at the given cell.
+ */
+ public void setValueAt(Object value, Object node, int column) { }
+
+ /**
+ * Adds a TreeModelListener to this tree.
+ */
+ public void addTreeModelListener(TreeModelListener l) { }
+
+ /**
+ * Removes a TreeModelListener from this tree.
+ */
+ public void removeTreeModelListener(TreeModelListener l) { }
+
+ public void valueForPathChanged(TreePath path, Object newValue) { }
+
+ // TreeTableNode
+
+ /**
+ * Returns the number of columns.
+ */
+ public int getColumnCount() {
+ return cNames.length;
+ }
+
+ /**
+ * Returns the name of the given column.
+ */
+ public String getColumnName(int column) {
+ return cNames[column];
+ }
+
+ /**
+ * Returns the type of value stored in the given column.
+ */
+ public Class getColumnClass(int column) {
+ return cTypes[column];
+ }
+
+ /**
+ * Returns the value at the given cell.
+ */
+ public Object getValueAt(Object nodeObj, int column) {
+ if (debugger == null) { return null; }
+ VariableNode node = (VariableNode)nodeObj;
+ switch (column) {
+ case 0: // Name
+ return node.toString();
+ case 1: // Value
+ String result;
+ try {
+ result = debugger.objectToString(getValue(node));
+ } catch (RuntimeException exc) {
+ result = exc.getMessage();
+ }
+ StringBuffer buf = new StringBuffer();
+ int len = result.length();
+ for (int i = 0; i < len; i++) {
+ char ch = result.charAt(i);
+ if (Character.isISOControl(ch)) {
+ ch = ' ';
+ }
+ buf.append(ch);
+ }
+ return buf.toString();
+ }
+ return null;
+ }
+
+ /**
+ * Returns an array of the children of the given node.
+ */
+ private VariableNode[] children(VariableNode node) {
+ if (node.children != null) {
+ return node.children;
+ }
+
+ VariableNode[] children;
+
+ Object value = getValue(node);
+ Object[] ids = debugger.getObjectIds(value);
+ if (ids == null || ids.length == 0) {
+ children = CHILDLESS;
+ } else {
+ Arrays.sort(ids, new Comparator() {
+ public int compare(Object l, Object r)
+ {
+ if (l instanceof String) {
+ if (r instanceof Integer) {
+ return -1;
+ }
+ return ((String)l).compareToIgnoreCase((String)r);
+ } else {
+ if (r instanceof String) {
+ return 1;
+ }
+ int lint = ((Integer)l).intValue();
+ int rint = ((Integer)r).intValue();
+ return lint - rint;
+ }
+ }
+ });
+ children = new VariableNode[ids.length];
+ for (int i = 0; i != ids.length; ++i) {
+ children[i] = new VariableNode(value, ids[i]);
+ }
+ }
+ node.children = children;
+ return children;
+ }
+
+ /**
+ * Returns the value of the given node.
+ */
+ public Object getValue(VariableNode node) {
+ try {
+ return debugger.getObjectProperty(node.object, node.id);
+ } catch (Exception exc) {
+ return "undefined";
+ }
+ }
+
+ /**
+ * A variable node in the tree.
+ */
+ private static class VariableNode {
+
+ /**
+ * The script object.
+ */
+ private Object object;
+
+ /**
+ * The object name. Either a String or an Integer.
+ */
+ private Object id;
+
+ /**
+ * Array of child nodes. This is filled with the properties of
+ * the object.
+ */
+ private VariableNode[] children;
+
+ /**
+ * Creates a new VariableNode.
+ */
+ public VariableNode(Object object, Object id) {
+ this.object = object;
+ this.id = id;
+ }
+
+ /**
+ * Returns a string representation of this node.
+ */
+ public String toString() {
+ return id instanceof String
+ ? (String) id : "[" + ((Integer) id).intValue() + "]";
+ }
+ }
+}
+
+/**
+ * A tree table for browsing script objects.
+ */
+class MyTreeTable extends JTreeTable {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 3457265548184453049L;
+
+ /**
+ * Creates a new MyTreeTable.
+ */
+ public MyTreeTable(VariableModel model) {
+ super(model);
+ }
+
+ /**
+ * Initializes a tree for this tree table.
+ */
+ public JTree resetTree(TreeTableModel treeTableModel) {
+ tree = new TreeTableCellRenderer(treeTableModel);
+
+ // Install a tableModel representing the visible rows in the tree.
+ super.setModel(new TreeTableModelAdapter(treeTableModel, tree));
+
+ // Force the JTable and JTree to share their row selection models.
+ ListToTreeSelectionModelWrapper selectionWrapper = new
+ ListToTreeSelectionModelWrapper();
+ tree.setSelectionModel(selectionWrapper);
+ setSelectionModel(selectionWrapper.getListSelectionModel());
+
+ // Make the tree and table row heights the same.
+ if (tree.getRowHeight() < 1) {
+ // Metal looks better like this.
+ setRowHeight(18);
+ }
+
+ // Install the tree editor renderer and editor.
+ setDefaultRenderer(TreeTableModel.class, tree);
+ setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor());
+ setShowGrid(true);
+ setIntercellSpacing(new Dimension(1,1));
+ tree.setRootVisible(false);
+ tree.setShowsRootHandles(true);
+ DefaultTreeCellRenderer r = (DefaultTreeCellRenderer)tree.getCellRenderer();
+ r.setOpenIcon(null);
+ r.setClosedIcon(null);
+ r.setLeafIcon(null);
+ return tree;
+ }
+
+ /**
+ * Returns whether the cell under the coordinates of the mouse
+ * in the {@link EventObject} is editable.
+ */
+ public boolean isCellEditable(EventObject e) {
+ if (e instanceof MouseEvent) {
+ MouseEvent me = (MouseEvent)e;
+ // If the modifiers are not 0 (or the left mouse button),
+ // tree may try and toggle the selection, and table
+ // will then try and toggle, resulting in the
+ // selection remaining the same. To avoid this, we
+ // only dispatch when the modifiers are 0 (or the left mouse
+ // button).
+ if (me.getModifiers() == 0 ||
+ ((me.getModifiers() & (InputEvent.BUTTON1_MASK|1024)) != 0 &&
+ (me.getModifiers() &
+ (InputEvent.SHIFT_MASK |
+ InputEvent.CTRL_MASK |
+ InputEvent.ALT_MASK |
+ InputEvent.BUTTON2_MASK |
+ InputEvent.BUTTON3_MASK |
+ 64 | //SHIFT_DOWN_MASK
+ 128 | //CTRL_DOWN_MASK
+ 512 | // ALT_DOWN_MASK
+ 2048 | //BUTTON2_DOWN_MASK
+ 4096 //BUTTON3_DOWN_MASK
+ )) == 0)) {
+ int row = rowAtPoint(me.getPoint());
+ for (int counter = getColumnCount() - 1; counter >= 0;
+ counter--) {
+ if (TreeTableModel.class == getColumnClass(counter)) {
+ MouseEvent newME = new MouseEvent
+ (MyTreeTable.this.tree, me.getID(),
+ me.getWhen(), me.getModifiers(),
+ me.getX() - getCellRect(row, counter, true).x,
+ me.getY(), me.getClickCount(),
+ me.isPopupTrigger());
+ MyTreeTable.this.tree.dispatchEvent(newME);
+ break;
+ }
+ }
+ }
+ if (me.getClickCount() >= 3) {
+ return true;
+ }
+ return false;
+ }
+ if (e == null) {
+ return true;
+ }
+ return false;
+ }
+}
+
+/**
+ * Panel that shows information about the context.
+ */
+class ContextWindow extends JPanel implements ActionListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 2306040975490228051L;
+
+ /**
+ * The debugger GUI.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * The combo box that holds the stack frames.
+ */
+ JComboBox context;
+
+ /**
+ * Tool tips for the stack frames.
+ */
+ Vector toolTips;
+
+ /**
+ * Tabbed pane for "this" and "locals".
+ */
+ private JTabbedPane tabs;
+
+ /**
+ * Tabbed pane for "watch" and "evaluate".
+ */
+ private JTabbedPane tabs2;
+
+ /**
+ * The table showing the "this" object.
+ */
+ private MyTreeTable thisTable;
+
+ /**
+ * The table showing the stack local variables.
+ */
+ private MyTreeTable localsTable;
+
+ /**
+ * The {@link #evaluator}'s table model.
+ */
+ private MyTableModel tableModel;
+
+ /**
+ * The script evaluator table.
+ */
+ private Evaluator evaluator;
+
+ /**
+ * The script evaluation text area.
+ */
+ private EvalTextArea cmdLine;
+
+ /**
+ * The split pane.
+ */
+ JSplitPane split;
+
+ /**
+ * Whether the ContextWindow is enabled.
+ */
+ private boolean enabled;
+
+ /**
+ * Creates a new ContextWindow.
+ */
+ public ContextWindow(final SwingGui debugGui) {
+ this.debugGui = debugGui;
+ enabled = false;
+ JPanel left = new JPanel();
+ JToolBar t1 = new JToolBar();
+ t1.setName("Variables");
+ t1.setLayout(new GridLayout());
+ t1.add(left);
+ JPanel p1 = new JPanel();
+ p1.setLayout(new GridLayout());
+ JPanel p2 = new JPanel();
+ p2.setLayout(new GridLayout());
+ p1.add(t1);
+ JLabel label = new JLabel("Context:");
+ context = new JComboBox();
+ context.setLightWeightPopupEnabled(false);
+ toolTips = new java.util.Vector();
+ label.setBorder(context.getBorder());
+ context.addActionListener(this);
+ context.setActionCommand("ContextSwitch");
+ GridBagLayout layout = new GridBagLayout();
+ left.setLayout(layout);
+ GridBagConstraints lc = new GridBagConstraints();
+ lc.insets.left = 5;
+ lc.anchor = GridBagConstraints.WEST;
+ lc.ipadx = 5;
+ layout.setConstraints(label, lc);
+ left.add(label);
+ GridBagConstraints c = new GridBagConstraints();
+ c.gridwidth = GridBagConstraints.REMAINDER;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.WEST;
+ layout.setConstraints(context, c);
+ left.add(context);
+ tabs = new JTabbedPane(SwingConstants.BOTTOM);
+ tabs.setPreferredSize(new Dimension(500,300));
+ thisTable = new MyTreeTable(new VariableModel());
+ JScrollPane jsp = new JScrollPane(thisTable);
+ jsp.getViewport().setViewSize(new Dimension(5,2));
+ tabs.add("this", jsp);
+ localsTable = new MyTreeTable(new VariableModel());
+ localsTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+ localsTable.setPreferredSize(null);
+ jsp = new JScrollPane(localsTable);
+ tabs.add("Locals", jsp);
+ c.weightx = c.weighty = 1;
+ c.gridheight = GridBagConstraints.REMAINDER;
+ c.fill = GridBagConstraints.BOTH;
+ c.anchor = GridBagConstraints.WEST;
+ layout.setConstraints(tabs, c);
+ left.add(tabs);
+ evaluator = new Evaluator(debugGui);
+ cmdLine = new EvalTextArea(debugGui);
+ //cmdLine.requestFocus();
+ tableModel = evaluator.tableModel;
+ jsp = new JScrollPane(evaluator);
+ JToolBar t2 = new JToolBar();
+ t2.setName("Evaluate");
+ tabs2 = new JTabbedPane(SwingConstants.BOTTOM);
+ tabs2.add("Watch", jsp);
+ tabs2.add("Evaluate", new JScrollPane(cmdLine));
+ tabs2.setPreferredSize(new Dimension(500,300));
+ t2.setLayout(new GridLayout());
+ t2.add(tabs2);
+ p2.add(t2);
+ evaluator.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+ split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
+ p1, p2);
+ split.setOneTouchExpandable(true);
+ SwingGui.setResizeWeight(split, 0.5);
+ setLayout(new BorderLayout());
+ add(split, BorderLayout.CENTER);
+
+ final JToolBar finalT1 = t1;
+ final JToolBar finalT2 = t2;
+ final JPanel finalP1 = p1;
+ final JPanel finalP2 = p2;
+ final JSplitPane finalSplit = split;
+ final JPanel finalThis = this;
+
+ ComponentListener clistener = new ComponentListener() {
+ boolean t2Docked = true;
+ void check(Component comp) {
+ Component thisParent = finalThis.getParent();
+ if (thisParent == null) {
+ return;
+ }
+ Component parent = finalT1.getParent();
+ boolean leftDocked = true;
+ boolean rightDocked = true;
+ boolean adjustVerticalSplit = false;
+ if (parent != null) {
+ if (parent != finalP1) {
+ while (!(parent instanceof JFrame)) {
+ parent = parent.getParent();
+ }
+ JFrame frame = (JFrame)parent;
+ debugGui.addTopLevel("Variables", frame);
+
+ // We need the following hacks because:
+ // - We want an undocked toolbar to be
+ // resizable.
+ // - We are using JToolbar as a container of a
+ // JComboBox. Without this JComboBox's popup
+ // can get left floating when the toolbar is
+ // re-docked.
+ //
+ // We make the frame resizable and then
+ // remove JToolbar's window listener
+ // and insert one of our own that first ensures
+ // the JComboBox's popup window is closed
+ // and then calls JToolbar's window listener.
+ if (!frame.isResizable()) {
+ frame.setResizable(true);
+ frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+ final EventListener[] l =
+ frame.getListeners(WindowListener.class);
+ frame.removeWindowListener((WindowListener)l[0]);
+ frame.addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ context.hidePopup();
+ ((WindowListener)l[0]).windowClosing(e);
+ }
+ });
+ //adjustVerticalSplit = true;
+ }
+ leftDocked = false;
+ } else {
+ leftDocked = true;
+ }
+ }
+ parent = finalT2.getParent();
+ if (parent != null) {
+ if (parent != finalP2) {
+ while (!(parent instanceof JFrame)) {
+ parent = parent.getParent();
+ }
+ JFrame frame = (JFrame)parent;
+ debugGui.addTopLevel("Evaluate", frame);
+ frame.setResizable(true);
+ rightDocked = false;
+ } else {
+ rightDocked = true;
+ }
+ }
+ if (leftDocked && t2Docked && rightDocked && t2Docked) {
+ // no change
+ return;
+ }
+ t2Docked = rightDocked;
+ JSplitPane split = (JSplitPane)thisParent;
+ if (leftDocked) {
+ if (rightDocked) {
+ finalSplit.setDividerLocation(0.5);
+ } else {
+ finalSplit.setDividerLocation(1.0);
+ }
+ if (adjustVerticalSplit) {
+ split.setDividerLocation(0.66);
+ }
+
+ } else if (rightDocked) {
+ finalSplit.setDividerLocation(0.0);
+ split.setDividerLocation(0.66);
+ } else {
+ // both undocked
+ split.setDividerLocation(1.0);
+ }
+ }
+ public void componentHidden(ComponentEvent e) {
+ check(e.getComponent());
+ }
+ public void componentMoved(ComponentEvent e) {
+ check(e.getComponent());
+ }
+ public void componentResized(ComponentEvent e) {
+ check(e.getComponent());
+ }
+ public void componentShown(ComponentEvent e) {
+ check(e.getComponent());
+ }
+ };
+ p1.addContainerListener(new ContainerListener() {
+ public void componentAdded(ContainerEvent e) {
+ Component thisParent = finalThis.getParent();
+ JSplitPane split = (JSplitPane)thisParent;
+ if (e.getChild() == finalT1) {
+ if (finalT2.getParent() == finalP2) {
+ // both docked
+ finalSplit.setDividerLocation(0.5);
+ } else {
+ // left docked only
+ finalSplit.setDividerLocation(1.0);
+ }
+ split.setDividerLocation(0.66);
+ }
+ }
+ public void componentRemoved(ContainerEvent e) {
+ Component thisParent = finalThis.getParent();
+ JSplitPane split = (JSplitPane)thisParent;
+ if (e.getChild() == finalT1) {
+ if (finalT2.getParent() == finalP2) {
+ // right docked only
+ finalSplit.setDividerLocation(0.0);
+ split.setDividerLocation(0.66);
+ } else {
+ // both undocked
+ split.setDividerLocation(1.0);
+ }
+ }
+ }
+ });
+ t1.addComponentListener(clistener);
+ t2.addComponentListener(clistener);
+ disable();
+ }
+
+ /**
+ * Disables the component.
+ */
+ public void disable() {
+ context.setEnabled(false);
+ thisTable.setEnabled(false);
+ localsTable.setEnabled(false);
+ evaluator.setEnabled(false);
+ cmdLine.setEnabled(false);
+ }
+
+ /**
+ * Enables the component.
+ */
+ public void enable() {
+ context.setEnabled(true);
+ thisTable.setEnabled(true);
+ localsTable.setEnabled(true);
+ evaluator.setEnabled(true);
+ cmdLine.setEnabled(true);
+ }
+
+ /**
+ * Disables updating of the component.
+ */
+ public void disableUpdate() {
+ enabled = false;
+ }
+
+ /**
+ * Enables updating of the component.
+ */
+ public void enableUpdate() {
+ enabled = true;
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action.
+ */
+ public void actionPerformed(ActionEvent e) {
+ if (!enabled) return;
+ if (e.getActionCommand().equals("ContextSwitch")) {
+ Dim.ContextData contextData = debugGui.dim.currentContextData();
+ if (contextData == null) { return; }
+ int frameIndex = context.getSelectedIndex();
+ context.setToolTipText(toolTips.elementAt(frameIndex).toString());
+ int frameCount = contextData.frameCount();
+ if (frameIndex >= frameCount) {
+ return;
+ }
+ Dim.StackFrame frame = contextData.getFrame(frameIndex);
+ Object scope = frame.scope();
+ Object thisObj = frame.thisObj();
+ thisTable.resetTree(new VariableModel(debugGui.dim, thisObj));
+ VariableModel scopeModel;
+ if (scope != thisObj) {
+ scopeModel = new VariableModel(debugGui.dim, scope);
+ } else {
+ scopeModel = new VariableModel();
+ }
+ localsTable.resetTree(scopeModel);
+ debugGui.dim.contextSwitch(frameIndex);
+ debugGui.showStopLine(frame);
+ tableModel.updateModel();
+ }
+ }
+}
+
+/**
+ * The debugger frame menu bar.
+ */
+class Menubar extends JMenuBar implements ActionListener {
+
+ /**
+ * Serializable magic number.
+ */
+ private static final long serialVersionUID = 3217170497245911461L;
+
+ /**
+ * Items that are enabled only when interrupted.
+ */
+ private Vector interruptOnlyItems = new Vector();
+
+ /**
+ * Items that are enabled only when running.
+ */
+ private Vector runOnlyItems = new Vector();
+
+ /**
+ * The debugger GUI.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * The menu listing the internal frames.
+ */
+ private JMenu windowMenu;
+
+ /**
+ * The "Break on exceptions" menu item.
+ */
+ private JCheckBoxMenuItem breakOnExceptions;
+
+ /**
+ * The "Break on enter" menu item.
+ */
+ private JCheckBoxMenuItem breakOnEnter;
+
+ /**
+ * The "Break on return" menu item.
+ */
+ private JCheckBoxMenuItem breakOnReturn;
+
+ /**
+ * Creates a new Menubar.
+ */
+ Menubar(SwingGui debugGui) {
+ super();
+ this.debugGui = debugGui;
+ String[] fileItems = {"Open...", "Run...", "", "Exit"};
+ String[] fileCmds = {"Open", "Load", "", "Exit"};
+ char[] fileShortCuts = {'0', 'N', 0, 'X'};
+ int[] fileAccelerators = {KeyEvent.VK_O,
+ KeyEvent.VK_N,
+ 0,
+ KeyEvent.VK_Q};
+ String[] editItems = {"Cut", "Copy", "Paste", "Go to function..."};
+ char[] editShortCuts = {'T', 'C', 'P', 'F'};
+ String[] debugItems = {"Break", "Go", "Step Into", "Step Over", "Step Out"};
+ char[] debugShortCuts = {'B', 'G', 'I', 'O', 'T'};
+ String[] plafItems = {"Metal", "Windows", "Motif"};
+ char [] plafShortCuts = {'M', 'W', 'F'};
+ int[] debugAccelerators = {KeyEvent.VK_PAUSE,
+ KeyEvent.VK_F5,
+ KeyEvent.VK_F11,
+ KeyEvent.VK_F7,
+ KeyEvent.VK_F8,
+ 0, 0};
+
+ JMenu fileMenu = new JMenu("File");
+ fileMenu.setMnemonic('F');
+ JMenu editMenu = new JMenu("Edit");
+ editMenu.setMnemonic('E');
+ JMenu plafMenu = new JMenu("Platform");
+ plafMenu.setMnemonic('P');
+ JMenu debugMenu = new JMenu("Debug");
+ debugMenu.setMnemonic('D');
+ windowMenu = new JMenu("Window");
+ windowMenu.setMnemonic('W');
+ for (int i = 0; i < fileItems.length; ++i) {
+ if (fileItems[i].length() == 0) {
+ fileMenu.addSeparator();
+ } else {
+ JMenuItem item = new JMenuItem(fileItems[i],
+ fileShortCuts[i]);
+ item.setActionCommand(fileCmds[i]);
+ item.addActionListener(this);
+ fileMenu.add(item);
+ if (fileAccelerators[i] != 0) {
+ KeyStroke k = KeyStroke.getKeyStroke(fileAccelerators[i], Event.CTRL_MASK);
+ item.setAccelerator(k);
+ }
+ }
+ }
+ for (int i = 0; i < editItems.length; ++i) {
+ JMenuItem item = new JMenuItem(editItems[i],
+ editShortCuts[i]);
+ item.addActionListener(this);
+ editMenu.add(item);
+ }
+ for (int i = 0; i < plafItems.length; ++i) {
+ JMenuItem item = new JMenuItem(plafItems[i],
+ plafShortCuts[i]);
+ item.addActionListener(this);
+ plafMenu.add(item);
+ }
+ for (int i = 0; i < debugItems.length; ++i) {
+ JMenuItem item = new JMenuItem(debugItems[i],
+ debugShortCuts[i]);
+ item.addActionListener(this);
+ if (debugAccelerators[i] != 0) {
+ KeyStroke k = KeyStroke.getKeyStroke(debugAccelerators[i], 0);
+ item.setAccelerator(k);
+ }
+ if (i != 0) {
+ interruptOnlyItems.add(item);
+ } else {
+ runOnlyItems.add(item);
+ }
+ debugMenu.add(item);
+ }
+ breakOnExceptions = new JCheckBoxMenuItem("Break on Exceptions");
+ breakOnExceptions.setMnemonic('X');
+ breakOnExceptions.addActionListener(this);
+ breakOnExceptions.setSelected(false);
+ debugMenu.add(breakOnExceptions);
+
+ breakOnEnter = new JCheckBoxMenuItem("Break on Function Enter");
+ breakOnEnter.setMnemonic('E');
+ breakOnEnter.addActionListener(this);
+ breakOnEnter.setSelected(false);
+ debugMenu.add(breakOnEnter);
+
+ breakOnReturn = new JCheckBoxMenuItem("Break on Function Return");
+ breakOnReturn.setMnemonic('R');
+ breakOnReturn.addActionListener(this);
+ breakOnReturn.setSelected(false);
+ debugMenu.add(breakOnReturn);
+
+ add(fileMenu);
+ add(editMenu);
+ //add(plafMenu);
+ add(debugMenu);
+ JMenuItem item;
+ windowMenu.add(item = new JMenuItem("Cascade", 'A'));
+ item.addActionListener(this);
+ windowMenu.add(item = new JMenuItem("Tile", 'T'));
+ item.addActionListener(this);
+ windowMenu.addSeparator();
+ windowMenu.add(item = new JMenuItem("Console", 'C'));
+ item.addActionListener(this);
+ add(windowMenu);
+
+ updateEnabled(false);
+ }
+
+ /**
+ * Returns the "Break on exceptions" menu item.
+ */
+ public JCheckBoxMenuItem getBreakOnExceptions() {
+ return breakOnExceptions;
+ }
+
+ /**
+ * Returns the "Break on enter" menu item.
+ */
+ public JCheckBoxMenuItem getBreakOnEnter() {
+ return breakOnEnter;
+ }
+
+ /**
+ * Returns the "Break on return" menu item.
+ */
+ public JCheckBoxMenuItem getBreakOnReturn() {
+ return breakOnReturn;
+ }
+
+ /**
+ * Returns the "Debug" menu.
+ */
+ public JMenu getDebugMenu() {
+ return getMenu(2);
+ }
+
+ // ActionListener
+
+ /**
+ * Performs an action.
+ */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ String plaf_name = null;
+ if (cmd.equals("Metal")) {
+ plaf_name = "javax.swing.plaf.metal.MetalLookAndFeel";
+ } else if (cmd.equals("Windows")) {
+ plaf_name = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
+ } else if (cmd.equals("Motif")) {
+ plaf_name = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
+ } else {
+ Object source = e.getSource();
+ if (source == breakOnExceptions) {
+ debugGui.dim.setBreakOnExceptions(breakOnExceptions.isSelected());
+ } else if (source == breakOnEnter) {
+ debugGui.dim.setBreakOnEnter(breakOnEnter.isSelected());
+ } else if (source == breakOnReturn) {
+ debugGui.dim.setBreakOnReturn(breakOnReturn.isSelected());
+ } else {
+ debugGui.actionPerformed(e);
+ }
+ return;
+ }
+ try {
+ UIManager.setLookAndFeel(plaf_name);
+ SwingUtilities.updateComponentTreeUI(debugGui);
+ SwingUtilities.updateComponentTreeUI(debugGui.dlg);
+ } catch (Exception ignored) {
+ //ignored.printStackTrace();
+ }
+ }
+
+ /**
+ * Adds a file to the window menu.
+ */
+ public void addFile(String url) {
+ int count = windowMenu.getItemCount();
+ JMenuItem item;
+ if (count == 4) {
+ windowMenu.addSeparator();
+ count++;
+ }
+ JMenuItem lastItem = windowMenu.getItem(count -1);
+ boolean hasMoreWin = false;
+ int maxWin = 5;
+ if (lastItem != null &&
+ lastItem.getText().equals("More Windows...")) {
+ hasMoreWin = true;
+ maxWin++;
+ }
+ if (!hasMoreWin && count - 4 == 5) {
+ windowMenu.add(item = new JMenuItem("More Windows...", 'M'));
+ item.setActionCommand("More Windows...");
+ item.addActionListener(this);
+ return;
+ } else if (count - 4 <= maxWin) {
+ if (hasMoreWin) {
+ count--;
+ windowMenu.remove(lastItem);
+ }
+ String shortName = SwingGui.getShortName(url);
+
+ windowMenu.add(item = new JMenuItem((char)('0' + (count-4)) + " " + shortName, '0' + (count - 4)));
+ if (hasMoreWin) {
+ windowMenu.add(lastItem);
+ }
+ } else {
+ return;
+ }
+ item.setActionCommand(url);
+ item.addActionListener(this);
+ }
+
+ /**
+ * Updates the enabledness of menu items.
+ */
+ public void updateEnabled(boolean interrupted) {
+ for (int i = 0; i != interruptOnlyItems.size(); ++i) {
+ JMenuItem item = (JMenuItem)interruptOnlyItems.elementAt(i);
+ item.setEnabled(interrupted);
+ }
+
+ for (int i = 0; i != runOnlyItems.size(); ++i) {
+ JMenuItem item = (JMenuItem)runOnlyItems.elementAt(i);
+ item.setEnabled(!interrupted);
+ }
+ }
+}
+
+/**
+ * Class to consolidate all cases that require to implement Runnable
+ * to avoid class generation bloat.
+ */
+class RunProxy implements Runnable {
+
+ // Constants for 'type'.
+ static final int OPEN_FILE = 1;
+ static final int LOAD_FILE = 2;
+ static final int UPDATE_SOURCE_TEXT = 3;
+ static final int ENTER_INTERRUPT = 4;
+
+ /**
+ * The debugger GUI.
+ */
+ private SwingGui debugGui;
+
+ /**
+ * The type of Runnable this object is. Takes one of the constants
+ * defined in this class.
+ */
+ private int type;
+
+ /**
+ * The name of the file to open or load.
+ */
+ String fileName;
+
+ /**
+ * The source text to update.
+ */
+ String text;
+
+ /**
+ * The source for which to update the text.
+ */
+ Dim.SourceInfo sourceInfo;
+
+ /**
+ * The frame to interrupt in.
+ */
+ Dim.StackFrame lastFrame;
+
+ /**
+ * The name of the interrupted thread.
+ */
+ String threadTitle;
+
+ /**
+ * The message of the exception thrown that caused the thread
+ * interruption, if any.
+ */
+ String alertMessage;
+
+ /**
+ * Creates a new RunProxy.
+ */
+ public RunProxy(SwingGui debugGui, int type) {
+ this.debugGui = debugGui;
+ this.type = type;
+ }
+
+ /**
+ * Runs this Runnable.
+ */
+ public void run() {
+ switch (type) {
+ case OPEN_FILE:
+ try {
+ debugGui.dim.compileScript(fileName, text);
+ } catch (RuntimeException ex) {
+ MessageDialogWrapper.showMessageDialog(
+ debugGui, ex.getMessage(), "Error Compiling "+fileName,
+ JOptionPane.ERROR_MESSAGE);
+ }
+ break;
+
+ case LOAD_FILE:
+ try {
+ debugGui.dim.evalScript(fileName, text);
+ } catch (RuntimeException ex) {
+ MessageDialogWrapper.showMessageDialog(
+ debugGui, ex.getMessage(), "Run error for "+fileName,
+ JOptionPane.ERROR_MESSAGE);
+ }
+ break;
+
+ case UPDATE_SOURCE_TEXT:
+ {
+ String fileName = sourceInfo.url();
+ if (!debugGui.updateFileWindow(sourceInfo) &&
+ !fileName.equals("<stdin>")) {
+ debugGui.createFileWindow(sourceInfo, -1);
+ }
+ }
+ break;
+
+ case ENTER_INTERRUPT:
+ debugGui.enterInterruptImpl(lastFrame, threadTitle, alertMessage);
+ break;
+
+ default:
+ throw new IllegalArgumentException(String.valueOf(type));
+
+ }
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/build.xml b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/build.xml
new file mode 100644
index 0000000..c35fd40
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/debugger/build.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- ***** BEGIN LICENSE BLOCK *****
+ - Version: MPL 1.1/GPL 2.0
+ -
+ - The contents of this file are subject to the Mozilla Public License Version
+ - 1.1 (the "License"); you may not use this file except in compliance with
+ - the License. You may obtain a copy of the License at
+ - http://www.mozilla.org/MPL/
+ -
+ - Software distributed under the License is distributed on an "AS IS" basis,
+ - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ - for the specific language governing rights and limitations under the
+ - License.
+ -
+ - The Original Code is Rhino code, released May 6, 1999.
+ -
+ - The Initial Developer of the Original Code is
+ - Netscape Communications Corporation.
+ - Portions created by the Initial Developer are Copyright (C) 1997-1999
+ - the Initial Developer. All Rights Reserved.
+ -
+ - Contributor(s):
+ -
+ - Alternatively, the contents of this file may be used under the terms of
+ - the GNU General Public License Version 2 or later (the "GPL"), in which
+ - case the provisions of the GPL are applicable instead of those above. If
+ - you wish to allow use of your version of this file only under the terms of
+ - the GPL and not to allow others to use your version of this file under the
+ - MPL, indicate your decision by deleting the provisions above and replacing
+ - them with the notice and other provisions required by the GPL. If you do
+ - not delete the provisions above, a recipient may use your version of this
+ - file under either the MPL or the GPL.
+ -
+ - ***** END LICENSE BLOCK ***** -->
+
+
+<project name="toolsrc" default="help" basedir=".">
+
+ <target name="properties">
+ <property name="swing-ex-url" value="http://java.sun.com/products/jfc/tsc/articles/treetable2/downloads/src.zip"/>
+ <available file="downloaded/AbstractCellEditor.java"
+ property="swing-ex-available"/>
+ </target>
+
+ <target name="get-swing-ex" unless="swing-ex-available">
+ <!-- Download source from Sun's site, unzip it, remove
+ the files we don't need, and change the package
+ -->
+ <mkdir dir="downloaded"/>
+ <get src="${swing-ex-url}" dest="downloaded/swingExSrc.zip"/>
+ <unzip src="downloaded/swingExSrc.zip" dest="downloaded/">
+ <patternset>
+ <include name="AbstractCellEditor.java"/>
+ <include name="JTreeTable.java"/>
+ <include name="TreeTableModel.java"/>
+ <include name="TreeTableModelAdapter.java"/>
+ </patternset>
+ </unzip>
+ <replace file="downloaded/AbstractCellEditor.java">
+ <replacetoken>import java.awt.Component;</replacetoken>
+ <replacevalue>
+package org.mozilla.javascript.tools.debugger.downloaded;
+ </replacevalue>
+ </replace>
+ <replace file="downloaded/AbstractCellEditor.java">
+ <replacetoken>import java.awt.event.*;</replacetoken>
+ <replacevalue></replacevalue>
+ </replace>
+ <replace file="downloaded/AbstractCellEditor.java">
+ <replacetoken>import java.awt.AWTEvent;</replacetoken>
+ <replacevalue></replacevalue>
+ </replace>
+ <replace file="downloaded/AbstractCellEditor.java">
+ <replacetoken>import java.io.Serializable;</replacetoken>
+ <replacevalue></replacevalue>
+ </replace>
+ <replace file="downloaded/JTreeTable.java">
+ <replacetoken>import javax.swing.*;</replacetoken>
+ <replacevalue>
+ package org.mozilla.javascript.tools.debugger.downloaded;
+ import javax.swing.*;
+ </replacevalue>
+ </replace>
+ <replace file="downloaded/JTreeTable.java">
+ <replacetoken>class ListToTreeSelectionModelWrapper</replacetoken>
+ <replacevalue>public class ListToTreeSelectionModelWrapper</replacevalue>
+ </replace>
+ <replace file="downloaded/JTreeTable.java">
+ <replacetoken>ListSelectionModel getListSelectionModel</replacetoken>
+ <replacevalue>public ListSelectionModel getListSelectionModel</replacevalue>
+ </replace>
+ <replace file="downloaded/JTreeTable.java">
+ <replacetoken>import java.awt.Rectangle;</replacetoken>
+ <replacevalue></replacevalue>
+ </replace>
+ <replace file="downloaded/TreeTableModel.java">
+ <replacetoken>import javax.swing.tree.TreeModel;</replacetoken>
+ <replacevalue>
+ package org.mozilla.javascript.tools.debugger.downloaded;
+ import javax.swing.tree.TreeModel;
+ </replacevalue>
+ </replace>
+ <replace file="downloaded/TreeTableModelAdapter.java">
+ <replacetoken>import javax.swing.JTree;</replacetoken>
+ <replacevalue>
+ package org.mozilla.javascript.tools.debugger.downloaded;
+ import javax.swing.JTree;
+ </replacevalue>
+ </replace>
+ <delete file="downloaded/swingExSrc.zip"/>
+ </target>
+
+ <target name="download" depends="properties,get-swing-ex"/>
+
+ <target name="help" depends="properties">
+<echo>The following targets are available with this build file:
+
+ download Download ${swing-ex-url}
+ and extract the necessary files from it.
+
+ help Print this help.
+
+</echo>
+ </target>
+
+</project>
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/CodePrinter.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/CodePrinter.java
new file mode 100644
index 0000000..dd4f689
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/CodePrinter.java
@@ -0,0 +1,212 @@
+/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.mozilla.javascript.tools.idswitch;
+
+class CodePrinter {
+
+// length of u-type escape like \u12AB
+ private static final int LITERAL_CHAR_MAX_SIZE = 6;
+
+ private String lineTerminator = "\n";
+
+ private int indentStep = 4;
+ private int indentTabSize = 8;
+
+ private char[] buffer = new char[1 << 12]; // 4K
+ private int offset;
+
+ public String getLineTerminator() { return lineTerminator; }
+ public void setLineTerminator(String value) { lineTerminator = value; }
+
+ public int getIndentStep() { return indentStep; }
+ public void setIndentStep(int char_count) { indentStep = char_count; }
+
+ public int getIndentTabSize() { return indentTabSize; }
+ public void setIndentTabSize(int tab_size) { indentTabSize = tab_size; }
+
+ public void clear() {
+ offset = 0;
+ }
+
+ private int ensure_area(int area_size) {
+ int begin = offset;
+ int end = begin + area_size;
+ if (end > buffer.length) {
+ int new_capacity = buffer.length * 2;
+ if (end > new_capacity) { new_capacity = end; }
+ char[] tmp = new char[new_capacity];
+ System.arraycopy(buffer, 0, tmp, 0, begin);
+ buffer = tmp;
+ }
+ return begin;
+ }
+
+ private int add_area(int area_size) {
+ int pos = ensure_area(area_size);
+ offset = pos + area_size;
+ return pos;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public int getLastChar() {
+ return offset == 0 ? -1 : buffer[offset - 1];
+ }
+
+ public void p(char c) {
+ int pos = add_area(1);
+ buffer[pos] = c;
+ }
+
+ public void p(String s) {
+ int l = s.length();
+ int pos = add_area(l);
+ s.getChars(0, l, buffer, pos);
+ }
+
+ public final void p(char[] array) {
+ p(array, 0, array.length);
+ }
+
+ public void p(char[] array, int begin, int end) {
+ int l = end - begin;
+ int pos = add_area(l);
+ System.arraycopy(array, begin, buffer, pos, l);
+ }
+
+ public void p(int i) {
+ p(Integer.toString(i));
+ }
+
+ public void qchar(int c) {
+ int pos = ensure_area(2 + LITERAL_CHAR_MAX_SIZE);
+ buffer[pos] = '\'';
+ pos = put_string_literal_char(pos + 1, c, false);
+ buffer[pos] = '\'';
+ offset = pos + 1;
+ }
+
+ public void qstring(String s) {
+ int l = s.length();
+ int pos = ensure_area(2 + LITERAL_CHAR_MAX_SIZE * l);
+ buffer[pos] = '"';
+ ++pos;
+ for (int i = 0; i != l; ++i) {
+ pos = put_string_literal_char(pos, s.charAt(i), true);
+ }
+ buffer[pos] = '"';
+ offset = pos + 1;
+ }
+
+ private int put_string_literal_char(int pos, int c, boolean in_string) {
+ boolean backslash_symbol = true;
+ switch (c) {
+ case '\b': c = 'b'; break;
+ case '\t': c = 't'; break;
+ case '\n': c = 'n'; break;
+ case '\f': c = 'f'; break;
+ case '\r': c = 'r'; break;
+ case '\'': backslash_symbol = !in_string; break;
+ case '"': backslash_symbol = in_string; break;
+ default: backslash_symbol = false;
+ }
+
+ if (backslash_symbol) {
+ buffer[pos] = '\\';
+ buffer[pos + 1] = (char)c;
+ pos += 2;
+ }
+ else if (' ' <= c && c <= 126) {
+ buffer[pos] = (char)c;
+ ++pos;
+ }
+ else {
+ buffer[pos] = '\\';
+ buffer[pos + 1] = 'u';
+ buffer[pos + 2] = digit_to_hex_letter(0xF & (c >> 12));
+ buffer[pos + 3] = digit_to_hex_letter(0xF & (c >> 8));
+ buffer[pos + 4] = digit_to_hex_letter(0xF & (c >> 4));
+ buffer[pos + 5] = digit_to_hex_letter(0xF & c);
+ pos += 6;
+ }
+ return pos;
+ }
+
+ private static char digit_to_hex_letter(int d) {
+ return (char)((d < 10) ? '0' + d : 'A' - 10 + d);
+ }
+
+ public void indent(int level) {
+ int visible_size = indentStep * level;
+ int indent_size, tab_count;
+ if (indentTabSize <= 0) {
+ tab_count = 0; indent_size = visible_size;
+ }
+ else {
+ tab_count = visible_size / indentTabSize;
+ indent_size = tab_count + visible_size % indentTabSize;
+ }
+ int pos = add_area(indent_size);
+ int tab_end = pos + tab_count;
+ int indent_end = pos + indent_size;
+ for (; pos != tab_end; ++pos) { buffer[pos] = '\t'; }
+ for (; pos != indent_end; ++pos) { buffer[pos] = ' '; }
+ }
+
+ public void nl() {
+ p('\n');
+ }
+
+ public void line(int indent_level, String s) {
+ indent(indent_level); p(s); nl();
+ }
+
+ public void erase(int begin, int end) {
+ System.arraycopy(buffer, end, buffer, begin, offset - end);
+ offset -= end - begin;
+ }
+
+ public String toString() {
+ return new String(buffer, 0, offset);
+ }
+
+
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/FileBody.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/FileBody.java
new file mode 100644
index 0000000..60bdfb4
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/FileBody.java
@@ -0,0 +1,191 @@
+/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.mozilla.javascript.tools.idswitch;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+
+public class FileBody {
+
+ private static class ReplaceItem {
+ ReplaceItem next;
+ int begin;
+ int end;
+ String replacement;
+
+ ReplaceItem(int begin, int end, String text) {
+ this.begin = begin;
+ this.end = end;
+ this.replacement = text;
+ }
+ }
+
+ private char[] buffer = new char[1 << 14]; // 16K
+ private int bufferEnd;
+ private int lineBegin;
+ private int lineEnd;
+ private int nextLineStart;
+
+ private int lineNumber;
+
+ ReplaceItem firstReplace;
+ ReplaceItem lastReplace;
+
+
+ public char[] getBuffer() { return buffer; }
+
+ public void readData(Reader r) throws IOException {
+ int capacity = buffer.length;
+ int offset = 0;
+ for (;;) {
+ int n_read = r.read(buffer, offset, capacity - offset);
+ if (n_read < 0) { break; }
+ offset += n_read;
+ if (capacity == offset) {
+ capacity *= 2;
+ char[] tmp = new char[capacity];
+ System.arraycopy(buffer, 0, tmp, 0, offset);
+ buffer = tmp;
+ }
+ }
+ bufferEnd = offset;
+ }
+
+ public void writeInitialData(Writer w) throws IOException {
+ w.write(buffer, 0, bufferEnd);
+ }
+
+ public void writeData(Writer w) throws IOException {
+ int offset = 0;
+ for (ReplaceItem x = firstReplace; x != null; x = x.next) {
+ int before_replace = x.begin - offset;
+ if (before_replace > 0) {
+ w.write(buffer, offset, before_replace);
+ }
+ w.write(x.replacement);
+ offset = x.end;
+ }
+ int tail = bufferEnd - offset;
+ if (tail != 0) {
+ w.write(buffer, offset, tail);
+ }
+ }
+
+ public boolean wasModified() { return firstReplace != null; }
+
+ public boolean setReplacement(int begin, int end, String text) {
+ if (equals(text, buffer, begin, end)) { return false; }
+
+ ReplaceItem item = new ReplaceItem(begin, end, text);
+ if (firstReplace == null) {
+ firstReplace = lastReplace = item;
+ }
+ else if (begin < firstReplace.begin) {
+ item.next = firstReplace;
+ firstReplace = item;
+ }
+ else {
+ ReplaceItem cursor = firstReplace;
+ ReplaceItem next = cursor.next;
+ while (next != null) {
+ if (begin < next.begin) {
+ item.next = next;
+ cursor.next = item;
+ break;
+ }
+ cursor = next;
+ next = next.next;
+ }
+ if (next == null) {
+ lastReplace.next = item;
+ }
+ }
+
+ return true;
+ }
+
+ public int getLineNumber() { return lineNumber; }
+
+ public int getLineBegin() { return lineBegin; }
+
+ public int getLineEnd() { return lineEnd; }
+
+ public void startLineLoop() {
+ lineNumber = 0;
+ lineBegin = lineEnd = nextLineStart = 0;
+ }
+
+ public boolean nextLine() {
+ if (nextLineStart == bufferEnd) {
+ lineNumber = 0; return false;
+ }
+ int i; int c = 0;
+ for (i = nextLineStart; i != bufferEnd; ++i) {
+ c = buffer[i];
+ if (c == '\n' || c == '\r') { break; }
+ }
+ lineBegin = nextLineStart;
+ lineEnd = i;
+ if (i == bufferEnd) {
+ nextLineStart = i;
+ }
+ else if (c == '\r' && i + 1 != bufferEnd && buffer[i + 1] == '\n') {
+ nextLineStart = i + 2;
+ }
+ else {
+ nextLineStart = i + 1;
+ }
+ ++lineNumber;
+ return true;
+ }
+
+ private static boolean equals(String str, char[] array, int begin, int end)
+ {
+ if (str.length() == end - begin) {
+ for (int i = begin, j = 0; i != end; ++i, ++j) {
+ if (array[i] != str.charAt(j)) { return false; }
+ }
+ return true;
+ }
+ return false;
+ }
+
+}
+
+
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/IdValuePair.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/IdValuePair.java
new file mode 100644
index 0000000..69d5065
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/IdValuePair.java
@@ -0,0 +1,58 @@
+/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.mozilla.javascript.tools.idswitch;
+
+public class IdValuePair
+{
+ public final int idLength;
+ public final String id;
+ public final String value;
+
+ private int lineNumber;
+
+ public IdValuePair(String id, String value) {
+ this.idLength = id.length();
+ this.id = id;
+ this.value = value;
+ }
+
+ public int getLineNumber() { return lineNumber; }
+
+ public void setLineNumber(int value) { lineNumber = value; }
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/Main.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/Main.java
new file mode 100644
index 0000000..ae1d038
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/Main.java
@@ -0,0 +1,612 @@
+/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.mozilla.javascript.tools.idswitch;
+
+import java.io.*;
+import java.util.*;
+import java.text.SimpleDateFormat;
+
+import org.mozilla.javascript.EvaluatorException;
+import org.mozilla.javascript.tools.ToolErrorReporter;
+
+public class Main {
+
+ private static final String SWITCH_TAG_STR = "string_id_map";
+ private static final String GENERATED_TAG_STR = "generated";
+ private static final String STRING_TAG_STR = "string";
+
+ private static final int
+ NORMAL_LINE = 0,
+ SWITCH_TAG = 1,
+ GENERATED_TAG = 2,
+ STRING_TAG = 3;
+
+ private final Vector all_pairs = new Vector();
+
+ private ToolErrorReporter R;
+ private CodePrinter P;
+ private FileBody body;
+ private String source_file;
+
+ private int tag_definition_end;
+
+ private int tag_value_start;
+ private int tag_value_end;
+
+ private static boolean is_value_type(int id) {
+ if (id == STRING_TAG) { return true; }
+ return false;
+ }
+
+ private static String tag_name(int id) {
+ switch (id) {
+ case SWITCH_TAG: return SWITCH_TAG_STR;
+ case -SWITCH_TAG: return "/" + SWITCH_TAG_STR;
+ case GENERATED_TAG: return GENERATED_TAG_STR;
+ case -GENERATED_TAG: return "/" + GENERATED_TAG_STR;
+ }
+ return "";
+ }
+
+ void process_file(String file_path) throws IOException {
+ source_file = file_path;
+
+ body = new FileBody();
+
+ InputStream is;
+ if (file_path.equals("-")) {
+ is = System.in;
+ }
+ else {
+ is = new FileInputStream(file_path);
+ }
+ try {
+ Reader r = new InputStreamReader(is, "ASCII");
+ body.readData(r);
+ }
+ finally { is.close(); }
+
+ process_file();
+
+ if (body.wasModified()) {
+ OutputStream os;
+ if (file_path.equals("-")) {
+ os = System.out;
+ }
+ else {
+ os = new FileOutputStream(file_path);
+ }
+
+ try {
+ Writer w = new OutputStreamWriter(os);
+ body.writeData(w);
+ w.flush();
+ }
+ finally { os.close(); }
+ }
+ }
+
+ private void process_file() {
+ int cur_state = 0;
+ char[] buffer = body.getBuffer();
+
+ int generated_begin = -1, generated_end = -1;
+ int time_stamp_begin = -1, time_stamp_end = -1;
+
+ body.startLineLoop();
+ while (body.nextLine()) {
+ int begin = body.getLineBegin();
+ int end = body.getLineEnd();
+
+ int tag_id = extract_line_tag_id(buffer, begin, end);
+ boolean bad_tag = false;
+ switch (cur_state) {
+ case NORMAL_LINE:
+ if (tag_id == SWITCH_TAG) {
+ cur_state = SWITCH_TAG;
+ all_pairs.removeAllElements();
+ generated_begin = -1;
+ }
+ else if (tag_id == -SWITCH_TAG) {
+ bad_tag = true;
+ }
+ break;
+ case SWITCH_TAG:
+ if (tag_id == 0) {
+ look_for_id_definitions(buffer, begin, end, false);
+ }
+ else if (tag_id == STRING_TAG) {
+ look_for_id_definitions(buffer, begin, end, true);
+ }
+ else if (tag_id == GENERATED_TAG) {
+ if (generated_begin >= 0) { bad_tag = true; }
+ else {
+ cur_state = GENERATED_TAG;
+ time_stamp_begin = tag_definition_end;
+ time_stamp_end = end;
+ }
+ }
+ else if (tag_id == -SWITCH_TAG) {
+ cur_state = 0;
+ if (generated_begin >= 0 && !all_pairs.isEmpty()) {
+ generate_java_code();
+ String code = P.toString();
+ boolean different = body.setReplacement
+ (generated_begin, generated_end, code);
+ if (different) {
+ String stamp = get_time_stamp();
+ body.setReplacement
+ (time_stamp_begin, time_stamp_end, stamp);
+ }
+ }
+
+ break;
+ }
+ else {
+ bad_tag = true;
+ }
+ break;
+ case GENERATED_TAG:
+ if (tag_id == 0) {
+ if (generated_begin < 0) { generated_begin = begin; }
+ }
+ else if (tag_id == -GENERATED_TAG) {
+ if (generated_begin < 0) { generated_begin = begin; }
+ cur_state = SWITCH_TAG;
+ generated_end = begin;
+ }
+ else {
+ bad_tag = true;
+ }
+ break;
+ }
+ if (bad_tag) {
+ String text = ToolErrorReporter.getMessage(
+ "msg.idswitch.bad_tag_order", tag_name(tag_id));
+ throw R.runtimeError
+ (text, source_file, body.getLineNumber(), null, 0);
+ }
+ }
+
+ if (cur_state != 0) {
+ String text = ToolErrorReporter.getMessage(
+ "msg.idswitch.file_end_in_switch", tag_name(cur_state));
+ throw R.runtimeError
+ (text, source_file, body.getLineNumber(), null, 0);
+ }
+ }
+
+ private String get_time_stamp() {
+ SimpleDateFormat f = new SimpleDateFormat
+ (" 'Last update:' yyyy-MM-dd HH:mm:ss z");
+ return f.format(new Date());
+ }
+
+ private void generate_java_code() {
+
+ P.clear();
+
+ IdValuePair[] pairs = new IdValuePair[all_pairs.size()];
+ all_pairs.copyInto(pairs);
+
+ SwitchGenerator g = new SwitchGenerator();
+ g.char_tail_test_threshold = 2;
+ g.setReporter(R);
+ g.setCodePrinter(P);
+
+ g.generateSwitch(pairs, "0");
+ }
+
+ private int extract_line_tag_id(char[] array, int cursor, int end) {
+ int id = 0;
+ cursor = skip_white_space(array, cursor, end);
+ int after_leading_white_space = cursor;
+ cursor = look_for_slash_slash(array, cursor, end);
+ if (cursor != end) {
+ boolean at_line_start = (after_leading_white_space + 2 == cursor);
+ cursor = skip_white_space(array, cursor, end);
+ if (cursor != end && array[cursor] == '#') {
+ ++cursor;
+
+ boolean end_tag = false;
+ if (cursor != end && array[cursor] == '/') {
+ ++cursor; end_tag = true;
+ }
+
+ int tag_start = cursor;
+
+ for (; cursor != end; ++cursor) {
+ int c = array[cursor];
+ if (c == '#' || c == '=' ||is_white_space(c)) { break; }
+ }
+
+ if (cursor != end) {
+ int tag_end = cursor;
+ cursor = skip_white_space(array, cursor, end);
+ if (cursor != end) {
+ int c = array[cursor];
+ if (c == '=' || c == '#') {
+ id = get_tag_id
+ (array, tag_start, tag_end, at_line_start);
+ if (id != 0) {
+ String bad = null;
+ if (c == '#') {
+ if (end_tag) {
+ id = -id;
+ if (is_value_type(id)) {
+ bad = "msg.idswitch.no_end_usage";
+ }
+ }
+ tag_definition_end = cursor + 1;
+ }
+ else {
+ if (end_tag) {
+ bad = "msg.idswitch.no_end_with_value";
+ }
+ else if (!is_value_type(id)) {
+ bad = "msg.idswitch.no_value_allowed";
+ }
+ id = extract_tag_value
+ (array, cursor + 1, end, id);
+ }
+ if (bad != null) {
+ String s = ToolErrorReporter.getMessage(
+ bad, tag_name(id));
+ throw R.runtimeError
+ (s, source_file, body.getLineNumber(),
+ null, 0);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return id;
+ }
+
+// Return position after first of // or end if not found
+ private int look_for_slash_slash(char[] array, int cursor, int end) {
+ while (cursor + 2 <= end) {
+ int c = array[cursor++];
+ if (c == '/') {
+ c = array[cursor++];
+ if (c == '/') {
+ return cursor;
+ }
+ }
+ }
+ return end;
+ }
+
+ private int extract_tag_value(char[] array, int cursor, int end, int id) {
+ // cursor points after #[^#=]+=
+ // ALERT: implement support for quoted strings
+ boolean found = false;
+ cursor = skip_white_space(array, cursor, end);
+ if (cursor != end) {
+ int value_start = cursor;
+ int value_end = cursor;
+ while (cursor != end) {
+ int c = array[cursor];
+ if (is_white_space(c)) {
+ int after_space = skip_white_space(array, cursor + 1, end);
+ if (after_space != end && array[after_space] == '#') {
+ value_end = cursor;
+ cursor = after_space;
+ break;
+ }
+ cursor = after_space + 1;
+ }
+ else if (c == '#') {
+ value_end = cursor;
+ break;
+ }
+ else {
+ ++cursor;
+ }
+ }
+ if (cursor != end) {
+ // array[cursor] is '#' here
+ found = true;
+ tag_value_start = value_start;
+ tag_value_end = value_end;
+ tag_definition_end = cursor + 1;
+ }
+ }
+ return (found) ? id : 0;
+ }
+
+ private int get_tag_id
+ (char[] array, int begin, int end, boolean at_line_start)
+ {
+ if (at_line_start) {
+ if (equals(SWITCH_TAG_STR, array, begin, end)) {
+ return SWITCH_TAG;
+ }
+ if (equals(GENERATED_TAG_STR, array, begin, end)) {
+ return GENERATED_TAG;
+ }
+ }
+ if (equals(STRING_TAG_STR, array, begin, end)) {
+ return STRING_TAG;
+ }
+ return 0;
+ }
+
+ private void look_for_id_definitions
+ (char[] array, int begin, int end, boolean use_tag_value_as_string)
+ {
+ // Look for the pattern
+ // '^[ \t]+Id_([a-zA-Z0-9_]+)[ \t]*=.*$'
+ // where \1 gives field or method name
+ int cursor = begin;
+ // Skip tab and spaces at the beginning
+ cursor = skip_white_space(array, cursor, end);
+ int id_start = cursor;
+ int name_start = skip_matched_prefix("Id_", array, cursor, end);
+ if (name_start >= 0) {
+ // Found Id_ prefix
+ cursor = name_start;
+ cursor = skip_name_char(array, cursor, end);
+ int name_end = cursor;
+ if (name_start != name_end) {
+ cursor = skip_white_space(array, cursor, end);
+ if (cursor != end) {
+ if (array[cursor] == '=') {
+ int id_end = name_end;
+ if (use_tag_value_as_string) {
+ name_start = tag_value_start;
+ name_end = tag_value_end;
+ }
+ // Got the match
+ add_id(array, id_start, id_end, name_start, name_end);
+ }
+ }
+ }
+ }
+ }
+
+ private void add_id
+ (char[] array, int id_start, int id_end, int name_start, int name_end)
+ {
+ String name = new String(array, name_start, name_end - name_start);
+ String value = new String(array, id_start, id_end - id_start);
+
+ IdValuePair pair = new IdValuePair(name, value);
+
+ pair.setLineNumber(body.getLineNumber());
+
+ all_pairs.addElement(pair);
+ }
+
+ private static boolean is_white_space(int c) {
+ return c == ' ' || c == '\t';
+ }
+
+ private static int skip_white_space(char[] array, int begin, int end) {
+ int cursor = begin;
+ for (; cursor != end; ++cursor) {
+ int c = array[cursor];
+ if (!is_white_space(c)) { break; }
+ }
+ return cursor;
+ }
+
+ private static int skip_matched_prefix
+ (String prefix, char[] array, int begin, int end)
+ {
+ int cursor = -1;
+ int prefix_length = prefix.length();
+ if (prefix_length <= end - begin) {
+ cursor = begin;
+ for (int i = 0; i != prefix_length; ++i, ++cursor) {
+ if (prefix.charAt(i) != array[cursor]) {
+ cursor = -1; break;
+ }
+ }
+ }
+ return cursor;
+ }
+
+ private static boolean equals(String str, char[] array, int begin, int end)
+ {
+ if (str.length() == end - begin) {
+ for (int i = begin, j = 0; i != end; ++i, ++j) {
+ if (array[i] != str.charAt(j)) { return false; }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private static int skip_name_char(char[] array, int begin, int end) {
+ int cursor = begin;
+ for (; cursor != end; ++cursor) {
+ int c = array[cursor];
+ if (!('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z')) {
+ if (!('0' <= c && c <= '9')) {
+ if (c != '_') {
+ break;
+ }
+ }
+ }
+ }
+ return cursor;
+ }
+
+ public static void main(String[] args) {
+ Main self = new Main();
+ int status = self.exec(args);
+ System.exit(status);
+ }
+
+ private int exec(String[] args) {
+ R = new ToolErrorReporter(true, System.err);
+
+ int arg_count = process_options(args);
+
+ if (arg_count == 0) {
+ option_error(ToolErrorReporter.getMessage(
+ "msg.idswitch.no_file_argument"));
+ return -1;
+ }
+ if (arg_count > 1) {
+ option_error(ToolErrorReporter.getMessage(
+ "msg.idswitch.too_many_arguments"));
+ return -1;
+ }
+
+ P = new CodePrinter();
+ P.setIndentStep(4);
+ P.setIndentTabSize(0);
+
+ try {
+ process_file(args[0]);
+ }
+ catch (IOException ex) {
+ print_error(ToolErrorReporter.getMessage(
+ "msg.idswitch.io_error", ex.toString()));
+ return -1;
+ }
+ catch (EvaluatorException ex) {
+ return -1;
+ }
+ return 0;
+ }
+
+ private int process_options(String[] args) {
+
+ int status = 1;
+
+ boolean show_usage = false;
+ boolean show_version = false;
+
+ int N = args.length;
+ L: for (int i = 0; i != N; ++i) {
+ String arg = args[i];
+ int arg_length = arg.length();
+ if (arg_length >= 2) {
+ if (arg.charAt(0) == '-') {
+ if (arg.charAt(1) == '-') {
+ if (arg_length == 2) {
+ args[i] = null; break;
+ }
+ if (arg.equals("--help")) {
+ show_usage = true;
+ }
+ else if (arg.equals("--version")) {
+ show_version = true;
+ }
+ else {
+ option_error(ToolErrorReporter.getMessage(
+ "msg.idswitch.bad_option", arg));
+ status = -1; break L;
+ }
+ }
+ else {
+ for (int j = 1; j != arg_length; ++j) {
+ char c = arg.charAt(j);
+ switch (c) {
+ case 'h': show_usage = true; break;
+ default:
+ option_error(
+ ToolErrorReporter.getMessage(
+ "msg.idswitch.bad_option_char",
+ String.valueOf(c)));
+ status = -1;
+ break L;
+ }
+
+ }
+ }
+ args[i] = null;
+ }
+ }
+ }
+
+ if (status == 1) {
+ if (show_usage) { show_usage(); status = 0; }
+ if (show_version) { show_version(); status = 0; }
+ }
+
+ if (status != 1) { System.exit(status); }
+
+ return remove_nulls(args);
+ }
+
+ private void show_usage() {
+ System.out.println(
+ ToolErrorReporter.getMessage("msg.idswitch.usage"));
+ System.out.println();
+ }
+
+ private void show_version() {
+ System.out.println(
+ ToolErrorReporter.getMessage("msg.idswitch.version"));
+ }
+
+ private void option_error(String str) {
+ print_error(
+ ToolErrorReporter.getMessage("msg.idswitch.bad_invocation", str));
+ }
+
+ private void print_error(String text) {
+ System.err.println(text);
+ }
+
+ private int remove_nulls(String[] array) {
+ int N = array.length;
+ int cursor = 0;
+ for (; cursor != N; ++cursor) {
+ if (array[cursor] == null) { break; }
+ }
+ int destination = cursor;
+ if (cursor != N) {
+ ++cursor;
+ for (; cursor != N; ++cursor) {
+ String elem = array[cursor];
+ if (elem != null) {
+ array[destination] = elem; ++destination;
+ }
+ }
+ }
+ return destination;
+ }
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/SwitchGenerator.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/SwitchGenerator.java
new file mode 100644
index 0000000..3db99d6
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/idswitch/SwitchGenerator.java
@@ -0,0 +1,491 @@
+/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.mozilla.javascript.tools.idswitch;
+
+import org.mozilla.javascript.EvaluatorException;
+import org.mozilla.javascript.tools.ToolErrorReporter;
+
+public class SwitchGenerator {
+
+ String v_switch_label = "L0";
+ String v_label = "L";
+ String v_s = "s";
+ String v_c = "c";
+ String v_guess = "X";
+ String v_id = "id";
+ String v_length_suffix = "_length";
+
+ int use_if_threshold = 3;
+ int char_tail_test_threshold = 2;
+
+ private IdValuePair[] pairs;
+ private String default_value;
+ private int[] columns;
+ private boolean c_was_defined;
+
+ private CodePrinter P;
+ private ToolErrorReporter R;
+ private String source_file;
+
+ public CodePrinter getCodePrinter() { return P; }
+ public void setCodePrinter(CodePrinter value) { P = value; }
+
+ public ToolErrorReporter getReporter() { return R; }
+ public void setReporter(ToolErrorReporter value) { R = value; }
+
+ public String getSourceFileName() { return source_file; }
+ public void setSourceFileName(String value) { source_file = value; }
+
+ public void generateSwitch(String[] pairs, String default_value) {
+ int N = pairs.length / 2;
+ IdValuePair[] id_pairs = new IdValuePair[N];
+ for (int i = 0; i != N; ++i) {
+ id_pairs[i] = new IdValuePair(pairs[2 * i], pairs[2 * i + 1]);
+ }
+ generateSwitch(id_pairs, default_value);
+
+ }
+
+ public void generateSwitch(IdValuePair[] pairs, String default_value) {
+ int begin = 0;
+ int end = pairs.length;
+ if (begin == end) { return; }
+ this.pairs = pairs;
+ this.default_value = default_value;
+
+ generate_body(begin, end, 2);
+ }
+
+ private void generate_body(int begin, int end, int indent_level) {
+ P.indent(indent_level);
+ P.p(v_switch_label); P.p(": { ");
+ P.p(v_id); P.p(" = "); P.p(default_value);
+ P.p("; String "); P.p(v_guess); P.p(" = null;");
+
+ c_was_defined = false;
+ int c_def_begin = P.getOffset();
+ P.p(" int "); P.p(v_c); P.p(';');
+ int c_def_end = P.getOffset();
+ P.nl();
+
+ generate_length_switch(begin, end, indent_level + 1);
+
+ if (!c_was_defined) {
+ P.erase(c_def_begin, c_def_end);
+ }
+
+ P.indent(indent_level + 1);
+ P.p("if ("); P.p(v_guess); P.p("!=null && ");
+ P.p(v_guess); P.p("!="); P.p(v_s);
+ P.p(" && !"); P.p(v_guess); P.p(".equals("); P.p(v_s); P.p(")) ");
+ P.p(v_id); P.p(" = "); P.p(default_value); P.p(";"); P.nl();
+
+ // Add break at end of block to suppress warning for unused label
+ P.indent(indent_level + 1);
+ P.p("break "); P.p(v_switch_label); P.p(";"); P.nl();
+
+ P.line(indent_level, "}");
+ }
+
+ private void generate_length_switch(int begin, int end, int indent_level) {
+
+ sort_pairs(begin, end, -1);
+
+ check_all_is_different(begin, end);
+
+ int lengths_count = count_different_lengths(begin, end);
+
+ columns = new int[pairs[end - 1].idLength];
+
+ boolean use_if;
+ if (lengths_count <= use_if_threshold) {
+ use_if = true;
+ if (lengths_count != 1) {
+ P.indent(indent_level);
+ P.p("int "); P.p(v_s); P.p(v_length_suffix);
+ P.p(" = "); P.p(v_s); P.p(".length();");
+ P.nl();
+ }
+ }
+ else {
+ use_if = false;
+ P.indent(indent_level);
+ P.p(v_label); P.p(": switch (");
+ P.p(v_s); P.p(".length()) {");
+ P.nl();
+ }
+
+ int same_length_begin = begin;
+ int cur_l = pairs[begin].idLength, l = 0;
+ for (int i = begin;;) {
+ ++i;
+ if (i == end || (l = pairs[i].idLength) != cur_l) {
+ int next_indent;
+ if (use_if) {
+ P.indent(indent_level);
+ if (same_length_begin != begin) { P.p("else "); }
+ P.p("if (");
+ if (lengths_count == 1) {
+ P.p(v_s); P.p(".length()==");
+ }
+ else {
+ P.p(v_s); P.p(v_length_suffix); P.p("==");
+ }
+ P.p(cur_l);
+ P.p(") {");
+ next_indent = indent_level + 1;
+ }
+ else {
+ P.indent(indent_level);
+ P.p("case "); P.p(cur_l); P.p(":");
+ next_indent = indent_level + 1;
+ }
+ generate_letter_switch
+ (same_length_begin, i, next_indent, !use_if, use_if);
+ if (use_if) {
+ P.p("}"); P.nl();
+ }
+ else {
+ P.p("break "); P.p(v_label); P.p(";"); P.nl();
+ }
+
+ if (i == end) { break; }
+ same_length_begin = i;
+ cur_l = l;
+ }
+ }
+
+ if (!use_if) {
+ P.indent(indent_level); P.p("}"); P.nl();
+ }
+
+ }
+
+ private void generate_letter_switch
+ (int begin, int end,
+ int indent_level, boolean label_was_defined, boolean inside_if)
+ {
+ int L = pairs[begin].idLength;
+
+ for (int i = 0; i != L; ++i) {
+ columns[i] = i;
+ }
+
+ generate_letter_switch_r
+ (begin, end, L, indent_level, label_was_defined, inside_if);
+ }
+
+
+ private boolean generate_letter_switch_r
+ (int begin, int end, int L,
+ int indent_level, boolean label_was_defined, boolean inside_if)
+ {
+ boolean next_is_unreachable = false;
+ if (begin + 1 == end) {
+ P.p(' ');
+ IdValuePair pair = pairs[begin];
+ if (L > char_tail_test_threshold) {
+ P.p(v_guess); P.p("="); P.qstring(pair.id); P.p(";");
+ P.p(v_id); P.p("="); P.p(pair.value); P.p(";");
+ }
+ else {
+ if (L == 0) {
+ next_is_unreachable = true;
+ P.p(v_id); P.p("="); P.p(pair.value);
+ P.p("; break "); P.p(v_switch_label); P.p(";");
+ }
+ else {
+ P.p("if (");
+ int column = columns[0];
+ P.p(v_s); P.p(".charAt("); P.p(column); P.p(")==");
+ P.qchar(pair.id.charAt(column));
+ for (int i = 1; i != L; ++i) {
+ P.p(" && ");
+ column = columns[i];
+ P.p(v_s); P.p(".charAt("); P.p(column); P.p(")==");
+ P.qchar(pair.id.charAt(column));
+ }
+ P.p(") {");
+ P.p(v_id); P.p("="); P.p(pair.value);
+ P.p("; break "); P.p(v_switch_label); P.p(";}");
+ }
+ }
+ P.p(' ');
+ return next_is_unreachable;
+ }
+
+ int max_column_index = find_max_different_column(begin, end, L);
+ int max_column = columns[max_column_index];
+ int count = count_different_chars(begin, end, max_column);
+
+ columns[max_column_index] = columns[L - 1];
+
+ if (inside_if) { P.nl(); P.indent(indent_level); }
+ else { P.p(' '); }
+
+ boolean use_if;
+ if (count <= use_if_threshold) {
+ use_if = true;
+ c_was_defined = true;
+ P.p(v_c); P.p("="); P.p(v_s);
+ P.p(".charAt("); P.p(max_column); P.p(");");
+ }
+ else {
+ use_if = false;
+ if (!label_was_defined) {
+ label_was_defined = true;
+ P.p(v_label); P.p(": ");
+ }
+ P.p("switch ("); P.p(v_s);
+ P.p(".charAt("); P.p(max_column); P.p(")) {");
+ }
+
+ int same_char_begin = begin;
+ int cur_ch = pairs[begin].id.charAt(max_column), ch = 0;
+ for (int i = begin;;) {
+ ++i;
+ if (i == end || (ch = pairs[i].id.charAt(max_column)) != cur_ch) {
+ int next_indent;
+ if (use_if) {
+ P.nl(); P.indent(indent_level);
+ if (same_char_begin != begin) { P.p("else "); }
+ P.p("if ("); P.p(v_c); P.p("==");
+ P.qchar(cur_ch); P.p(") {");
+ next_indent = indent_level + 1;
+ }
+ else {
+ P.nl(); P.indent(indent_level);
+ P.p("case "); P.qchar(cur_ch); P.p(":");
+ next_indent = indent_level + 1;
+ }
+ boolean after_unreachable = generate_letter_switch_r
+ (same_char_begin, i, L - 1,
+ next_indent, label_was_defined, use_if);
+ if (use_if) {
+ P.p("}");
+ }
+ else {
+ if (!after_unreachable) {
+ P.p("break "); P.p(v_label); P.p(";");
+ }
+ }
+ if (i == end) { break; }
+ same_char_begin = i;
+ cur_ch = ch;
+ }
+ }
+
+ if (use_if) {
+ P.nl();
+ if (inside_if) { P.indent(indent_level - 1); }
+ else { P.indent(indent_level); }
+ }
+ else {
+ P.nl(); P.indent(indent_level); P.p("}");
+ if (inside_if) { P.nl(); P.indent(indent_level - 1);}
+ else { P.p(' '); }
+ }
+
+ columns[max_column_index] = max_column;
+
+ return next_is_unreachable;
+ }
+
+
+ private int count_different_lengths(int begin, int end) {
+ int lengths_count = 0;
+ int cur_l = -1;
+ for (; begin != end; ++begin) {
+ int l = pairs[begin].idLength;
+ if (cur_l != l) {
+ ++lengths_count; cur_l = l;
+ }
+ }
+ return lengths_count;
+ }
+
+ private int find_max_different_column(int begin, int end, int L) {
+ int max_count = 0;
+ int max_index = 0;
+
+ for (int i = 0; i != L; ++i) {
+ int column = columns[i];
+ sort_pairs(begin, end, column);
+ int count = count_different_chars(begin, end, column);
+ if (count == end - begin) { return i; }
+ if (max_count < count) {
+ max_count = count;
+ max_index = i;
+ }
+ }
+
+ if (max_index != L - 1) {
+ sort_pairs(begin, end, columns[max_index]);
+ }
+
+ return max_index;
+ }
+
+ private int count_different_chars(int begin, int end, int column) {
+ int chars_count = 0;
+ int cur_ch = -1;
+ for (; begin != end; ++begin) {
+ int ch = pairs[begin].id.charAt(column);
+ if (ch != cur_ch) {
+ ++chars_count; cur_ch = ch;
+ }
+ }
+ return chars_count;
+ }
+
+ private void check_all_is_different(int begin, int end) {
+ if (begin != end) {
+ IdValuePair prev = pairs[begin];
+ while (++begin != end) {
+ IdValuePair current = pairs[begin];
+ if (prev.id.equals(current.id)) {
+ throw on_same_pair_fail(prev, current);
+ }
+ prev = current;
+ }
+ }
+ }
+
+ private EvaluatorException on_same_pair_fail(IdValuePair a, IdValuePair b) {
+ int line1 = a.getLineNumber(), line2 = b.getLineNumber();
+ if (line2 > line1) { int tmp = line1; line1 = line2; line2 = tmp; }
+ String error_text = ToolErrorReporter.getMessage(
+ "msg.idswitch.same_string", a.id, new Integer(line2));
+ return R.runtimeError(error_text, source_file, line1, null, 0);
+ }
+
+ private void sort_pairs(int begin, int end, int comparator) {
+ heap4Sort(pairs, begin, end - begin, comparator);
+ }
+
+ private static boolean bigger
+ (IdValuePair a, IdValuePair b, int comparator)
+ {
+ if (comparator < 0) {
+ // For length selection switch it is enough to compare just length,
+ // but to detect same strings full comparison is essential
+ //return a.idLength > b.idLength;
+ int diff = a.idLength - b.idLength;
+ if (diff != 0) { return diff > 0; }
+ return a.id.compareTo(b.id) > 0;
+ }
+ else {
+ return a.id.charAt(comparator) > b.id.charAt(comparator);
+ }
+ }
+
+ private static void heap4Sort
+ (IdValuePair[] array, int offset, int size, int comparator)
+ {
+ if (size <= 1) { return; }
+ makeHeap4(array, offset, size, comparator);
+ while (size > 1) {
+ --size;
+ IdValuePair v1 = array[offset + size];
+ IdValuePair v2 = array[offset + 0];
+ array[offset + size] = v2;
+ array[offset + 0] = v1;
+ heapify4(array, offset, size, 0, comparator);
+ }
+ }
+
+ private static void makeHeap4
+ (IdValuePair[] array, int offset, int size, int comparator)
+ {
+ for (int i = ((size + 2) >> 2); i != 0;) {
+ --i;
+ heapify4(array, offset, size, i, comparator);
+ }
+ }
+
+ private static void heapify4
+ (IdValuePair[] array, int offset, int size, int i, int comparator)
+ {
+ int new_i1, new_i2, new_i3;
+ IdValuePair i_val = array[offset + i];
+ for (;;) {
+ int base = (i << 2);
+ new_i1 = base | 1;
+ new_i2 = base | 2;
+ new_i3 = base | 3;
+ int new_i4 = base + 4;
+ if (new_i4 >= size) { break; }
+ IdValuePair val1 = array[offset + new_i1];
+ IdValuePair val2 = array[offset + new_i2];
+ IdValuePair val3 = array[offset + new_i3];
+ IdValuePair val4 = array[offset + new_i4];
+ if (bigger(val2, val1, comparator)) {
+ val1 = val2; new_i1 = new_i2;
+ }
+ if (bigger(val4, val3, comparator)) {
+ val3 = val4; new_i3 = new_i4;
+ }
+ if (bigger(val3, val1, comparator)) {
+ val1 = val3; new_i1 = new_i3;
+ }
+ if (bigger(i_val, val1, comparator)) { return; }
+ array[offset + i] = val1;
+ array[offset + new_i1] = i_val;
+ i = new_i1;
+ }
+ if (new_i1 < size) {
+ IdValuePair val1 = array[offset + new_i1];
+ if (new_i2 != size) {
+ IdValuePair val2 = array[offset + new_i2];
+ if (bigger(val2, val1, comparator)) {
+ val1 = val2; new_i1 = new_i2;
+ }
+ if (new_i3 != size) {
+ IdValuePair val3 = array[offset + new_i3];
+ if (bigger(val3, val1, comparator)) {
+ val1 = val3; new_i1 = new_i3;
+ }
+ }
+ }
+ if (bigger(val1, i_val, comparator)) {
+ array[offset + i] = val1;
+ array[offset + new_i1] = i_val;
+ }
+ }
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/jsc/Main.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/jsc/Main.java
new file mode 100644
index 0000000..2da4f2f
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/jsc/Main.java
@@ -0,0 +1,395 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Christine Begle
+ * Norris Boyd
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.tools.jsc;
+
+import java.io.*;
+import java.util.*;
+import org.mozilla.javascript.*;
+import org.mozilla.javascript.optimizer.ClassCompiler;
+import org.mozilla.javascript.tools.ToolErrorReporter;
+
+/**
+ * @author Norris Boyd
+ */
+public class Main {
+
+ /**
+ * Main entry point.
+ *
+ * Process arguments as would a normal Java program.
+ * Then set up the execution environment and begin to
+ * compile scripts.
+ */
+ public static void main(String args[])
+ {
+ Main main = new Main();
+ args = main.processOptions(args);
+ if (args == null) {
+ if (main.printHelp) {
+ System.out.println(ToolErrorReporter.getMessage(
+ "msg.jsc.usage", Main.class.getName()));
+ System.exit(0);
+ }
+ System.exit(1);
+ }
+ if (!main.reporter.hasReportedError()) {
+ main.processSource(args);
+ }
+ }
+
+ public Main()
+ {
+ reporter = new ToolErrorReporter(true);
+ compilerEnv = new CompilerEnvirons();
+ compilerEnv.setErrorReporter(reporter);
+ compiler = new ClassCompiler(compilerEnv);
+ }
+
+ /**
+ * Parse arguments.
+ *
+ */
+ public String[] processOptions(String args[])
+ {
+ targetPackage = ""; // default to no package
+ compilerEnv.setGenerateDebugInfo(false); // default to no symbols
+ for (int i=0; i < args.length; i++) {
+ String arg = args[i];
+ if (!arg.startsWith("-")) {
+ int tail = args.length - i;
+ if (targetName != null && tail > 1) {
+ addError("msg.multiple.js.to.file", targetName);
+ return null;
+ }
+ String[] result = new String[tail];
+ for (int j = 0; j != tail; ++j) {
+ result[j] = args[i + j];
+ }
+ return result;
+ }
+ if (arg.equals("-help") || arg.equals("-h")
+ || arg.equals("--help"))
+ {
+ printHelp = true;
+ return null;
+ }
+
+ try {
+ if (arg.equals("-version") && ++i < args.length) {
+ int version = Integer.parseInt(args[i]);
+ compilerEnv.setLanguageVersion(version);
+ continue;
+ }
+ if ((arg.equals("-opt") || arg.equals("-O")) &&
+ ++i < args.length)
+ {
+ int optLevel = Integer.parseInt(args[i]);
+ compilerEnv.setOptimizationLevel(optLevel);
+ continue;
+ }
+ }
+ catch (NumberFormatException e) {
+ badUsage(args[i]);
+ return null;
+ }
+ if (arg.equals("-nosource")) {
+ compilerEnv.setGeneratingSource(false);
+ continue;
+ }
+ if (arg.equals("-debug") || arg.equals("-g")) {
+ compilerEnv.setGenerateDebugInfo(true);
+ continue;
+ }
+ if (arg.equals("-main-method-class") && ++i < args.length) {
+ compiler.setMainMethodClass(args[i]);
+ continue;
+ }
+ if (arg.equals("-o") && ++i < args.length) {
+ String name = args[i];
+ int end = name.length();
+ if (end == 0
+ || !Character.isJavaIdentifierStart(name.charAt(0)))
+ {
+ addError("msg.invalid.classfile.name", name);
+ continue;
+ }
+ for (int j = 1; j < end; j++) {
+ char c = name.charAt(j);
+ if (!Character.isJavaIdentifierPart(c)) {
+ if (c == '.') {
+ // check if it is the dot in .class
+ if (j == end - 6 && name.endsWith(".class")) {
+ name = name.substring(0, j);
+ break;
+ }
+ }
+ addError("msg.invalid.classfile.name", name);
+ break;
+ }
+ }
+ targetName = name;
+ continue;
+ }
+ if (arg.equals("-observe-instruction-count")) {
+ compilerEnv.setGenerateObserverCount(true);
+ }
+ if (arg.equals("-package") && ++i < args.length) {
+ String pkg = args[i];
+ int end = pkg.length();
+ for (int j = 0; j != end; ++j) {
+ char c = pkg.charAt(j);
+ if (Character.isJavaIdentifierStart(c)) {
+ for (++j; j != end; ++j) {
+ c = pkg.charAt(j);
+ if (!Character.isJavaIdentifierPart(c)) {
+ break;
+ }
+ }
+ if (j == end) {
+ break;
+ }
+ if (c == '.' && j != end - 1) {
+ continue;
+ }
+ }
+ addError("msg.package.name", targetPackage);
+ return null;
+ }
+ targetPackage = pkg;
+ continue;
+ }
+ if (arg.equals("-extends") && ++i < args.length) {
+ String targetExtends = args[i];
+ Class superClass;
+ try {
+ superClass = Class.forName(targetExtends);
+ } catch (ClassNotFoundException e) {
+ throw new Error(e.toString()); // TODO: better error
+ }
+ compiler.setTargetExtends(superClass);
+ continue;
+ }
+ if (arg.equals("-implements") && ++i < args.length) {
+ // TODO: allow for multiple comma-separated interfaces.
+ String targetImplements = args[i];
+ StringTokenizer st = new StringTokenizer(targetImplements,
+ ",");
+ Vector v = new Vector();
+ while (st.hasMoreTokens()) {
+ String className = st.nextToken();
+ try {
+ v.addElement(Class.forName(className));
+ } catch (ClassNotFoundException e) {
+ throw new Error(e.toString()); // TODO: better error
+ }
+ }
+ Class[] implementsClasses = new Class[v.size()];
+ v.copyInto(implementsClasses);
+ compiler.setTargetImplements(implementsClasses);
+ continue;
+ }
+ if (arg.equals("-d") && ++i < args.length) {
+ destinationDir = args[i];
+ continue;
+ }
+ badUsage(arg);
+ return null;
+ }
+ // no file name
+ p(ToolErrorReporter.getMessage("msg.no.file"));
+ return null;
+ }
+ /**
+ * Print a usage message.
+ */
+ private static void badUsage(String s) {
+ System.err.println(ToolErrorReporter.getMessage(
+ "msg.jsc.bad.usage", Main.class.getName(), s));
+ }
+
+ /**
+ * Compile JavaScript source.
+ *
+ */
+ public void processSource(String[] filenames)
+ {
+ for (int i = 0; i != filenames.length; ++i) {
+ String filename = filenames[i];
+ if (!filename.endsWith(".js")) {
+ addError("msg.extension.not.js", filename);
+ return;
+ }
+ File f = new File(filename);
+ String source = readSource(f);
+ if (source == null) return;
+
+ String mainClassName = targetName;
+ if (mainClassName == null) {
+ String name = f.getName();
+ String nojs = name.substring(0, name.length() - 3);
+ mainClassName = getClassName(nojs);
+ }
+ if (targetPackage.length() != 0) {
+ mainClassName = targetPackage+"."+mainClassName;
+ }
+
+ Object[] compiled
+ = compiler.compileToClassFiles(source, filename, 1,
+ mainClassName);
+ if (compiled == null || compiled.length == 0) {
+ return;
+ }
+
+ File targetTopDir = null;
+ if (destinationDir != null) {
+ targetTopDir = new File(destinationDir);
+ } else {
+ String parent = f.getParent();
+ if (parent != null) {
+ targetTopDir = new File(parent);
+ }
+ }
+ for (int j = 0; j != compiled.length; j += 2) {
+ String className = (String)compiled[j];
+ byte[] bytes = (byte[])compiled[j + 1];
+ File outfile = getOutputFile(targetTopDir, className);
+ try {
+ FileOutputStream os = new FileOutputStream(outfile);
+ try {
+ os.write(bytes);
+ } finally {
+ os.close();
+ }
+ } catch (IOException ioe) {
+ addFormatedError(ioe.toString());
+ }
+ }
+ }
+ }
+
+ private String readSource(File f)
+ {
+ if (!f.exists()) {
+ addError("msg.jsfile.not.found", f.getAbsolutePath());
+ return null;
+ }
+ try {
+ Reader in = new FileReader(f);
+ try {
+ return Kit.readReader(in);
+ } finally {
+ in.close();
+ }
+ } catch (FileNotFoundException ex) {
+ addError("msg.couldnt.open", f.getAbsolutePath());
+ } catch (IOException ioe) {
+ addFormatedError(ioe.toString());
+ }
+ return null;
+ }
+
+ private File getOutputFile(File parentDir, String className)
+ {
+ String path = className.replace('.', File.separatorChar);
+ path = path.concat(".class");
+ File f = new File(parentDir, path);
+ String dirPath = f.getParent();
+ if (dirPath != null) {
+ File dir = new File(dirPath);
+ if (!dir.exists()) {
+ dir.mkdirs();
+ }
+ }
+ return f;
+ }
+
+ /**
+ * Verify that class file names are legal Java identifiers. Substitute
+ * illegal characters with underscores, and prepend the name with an
+ * underscore if the file name does not begin with a JavaLetter.
+ */
+
+ String getClassName(String name) {
+ char[] s = new char[name.length()+1];
+ char c;
+ int j = 0;
+
+ if (!Character.isJavaIdentifierStart(name.charAt(0))) {
+ s[j++] = '_';
+ }
+ for (int i=0; i < name.length(); i++, j++) {
+ c = name.charAt(i);
+ if ( Character.isJavaIdentifierPart(c) ) {
+ s[j] = c;
+ } else {
+ s[j] = '_';
+ }
+ }
+ return (new String(s)).trim();
+ }
+
+ private static void p(String s) {
+ System.out.println(s);
+ }
+
+ private void addError(String messageId, String arg)
+ {
+ String msg;
+ if (arg == null) {
+ msg = ToolErrorReporter.getMessage(messageId);
+ } else {
+ msg = ToolErrorReporter.getMessage(messageId, arg);
+ }
+ addFormatedError(msg);
+ }
+
+ private void addFormatedError(String message)
+ {
+ reporter.error(message, null, -1, null, -1);
+ }
+
+ private boolean printHelp;
+ private ToolErrorReporter reporter;
+ private CompilerEnvirons compilerEnv;
+ private ClassCompiler compiler;
+ private String targetName;
+ private String targetPackage;
+ private String destinationDir;
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties
new file mode 100644
index 0000000..76615e9
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties
@@ -0,0 +1,268 @@
+#
+# JavaScript tools messages file.
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Rhino code, released
+# May 6, 1999.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1997-1999
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# the GNU General Public License Version 2 or later (the "GPL"), in which
+# case the provisions of the GPL are applicable instead of those above. If
+# you wish to allow use of your version of this file only under the terms of
+# the GPL and not to allow others to use your version of this file under the
+# MPL, indicate your decision by deleting the provisions above and replacing
+# them with the notice and other provisions required by the GPL. If you do
+# not delete the provisions above, a recipient may use your version of this
+# file under either the MPL or the GPL.
+#
+# ***** END LICENSE BLOCK *****
+
+msg.expected.string.arg =\
+ Expected a string argument.
+
+msg.class.not.found =\
+ Class "{0}" not found.
+
+msg.couldnt.open =\
+ Couldn''t open file "{0}".
+
+msg.couldnt.open.url =\
+ Couldn''t open URL "{0}: {1}".
+
+msg.no-opt =\
+ Must have the org.mozilla.javascript.optimizer package available \
+ to compile to class files.
+
+msg.shell.invalid =\
+ Invalid option "{0}"
+
+msg.shell.usage =\
+ Usage: java {0} [options...] [files]\n\
+ Valid options are:\n\
+ \ -?, -help Displays help messages.\n\
+ \ -w Enable warnings.\n\
+ \ -version 100|110|120|130|140|150|160|170\n\
+ \ Set a specific language version.\n\
+ \ -opt [-1|0-9] Set optimization level.\n\
+ \ -f script-filename Execute script file.\n\
+ \ -e script-source Evaluate inline script.\n\
+ \ -debug Generate debug code.\n\
+ \ -strict Enable strict mode warnings.\n\
+ \ -fatal-warnings Treat warnings as errors.
+
+msg.help =\
+ \n\
+ Command Description \n\
+ ======= =========== \n\
+ help() Display usage and help messages. \n\
+ defineClass(className) Define an extension using the Java class \n\
+ \ named with the string argument. \n\
+ \ Uses ScriptableObject.defineClass(). \n\
+ load(["foo.js", ...]) Load JavaScript source files named by \n\
+ \ string arguments. \n\
+ loadClass(className) Load a class named by a string argument. \n\
+ \ The class must be a script compiled to a \n\
+ \ class file. \n\
+ print([expr ...]) Evaluate and print expressions. \n\
+ quit() Quit the shell. \n\
+ version([number]) Get or set the JavaScript version number. \n\
+ gc() Runs the garbage collector.\n\
+ spawn(arg) Evaluate function or scriptname on a new thread \n\
+ sync(function) Creates a synchronized version of the function, \n\
+ \ where the synchronization object is "this" \n\
+ readFile(fileName [, encoding])\n\
+ \ Returns the content of the file as a string. \n\
+ \ Encoding of the string can be optionally specified. \n\
+ readUrl(url [, encoding]) \n\
+ \ Similar to readFile, reads the contents of the url.\n\
+ runCommand(name ...) Runs a specified shell command. Additional arguments are \n\
+ \ passed to the command \n\
+ seal(args ...) Seals the supplied objects \n\
+ toint32(arg) Converts the argument into a 32-bit integer \n\
+ serialize(obj, fileName) \n\
+ \ Serializes an object and saves it to a file \n\
+ deserialise(fileName) Reconstructs a serialized object \n\
+ environment Returns the current environment object \n\
+ history Displays the shell command history \n\
+ \n\
+ For full description of all available commands see shell.html in\n\
+ the docs directory of Rhino distribution.\n\
+
+msg.warning =\
+ warning: {0}
+
+msg.format1 =\
+ {0}
+
+msg.format2 =\
+ line {0}: {1}
+
+msg.format3 =\
+ "{0}", line {1}: {2}
+
+msg.uncaughtJSException =\
+ exception from uncaught JavaScript throw: {0}
+
+msg.uncaughtEcmaError =\
+ uncaught JavaScript runtime exception: {0}
+
+msg.jsc.bad.usage =\
+ Didn''t understand "{1}". \n\
+ For more information, try java {0} -h
+
+msg.jsc.usage =\
+Usage: java {0} [OPTION]... SOURCE...\n\
+Valid options are: \n\
+\ -version VERSION Use the specified language version.\n\
+\ VERSION should be one of 100|110|120|130|140|150|160|170.\n\
+\ -opt LEVEL Use optimization with the specified level.\n\
+\ LEVEL should be one of 0..9.\n\
+\ -debug, -g Include debug information.\n\
+\ -nosource Do not include source to function objects.\n\
+\ It makes f.toString() useless and violates ECMAScript\n\
+\ standard but makes generated classes smaller and\n\
+\ saves memory.\n\
+\ -o CLASSNAME Use specified name as the last component of the main\n\
+\ generated class name. When specified, only one script\n\
+\ SOURCE is allowed. If omitted, it defaults to source\n\
+\ name with stripped .js suffix.\n\
+\ -package PACKAGE Place generated classes in the specified package.\n\
+\ -d DIRECTORY Use DIRECTORY as destination directory for generated\n\
+\ classes. If omitted, it defaults to parent directory\n\
+\ of SOURCE.\n\
+\ -extends CLASS The main generated class will extend the specified\n\
+\ class CLASS.\n\
+\ -implements INTERFACE1,INTERFACE2,... The main generated class will\n\
+\ implement the specified list of interfaces.\n\
+\ -main-method-class CLASS Specify the class name used for main method \n\
+\ implementation. The class must have a method matching\n\
+\ "public static void main(Script sc, String[] args)"\n\
+\ -observe-instruction-count Generate code that contains callbacks to \n\
+\ accumulate counts of executed instructions. Code \n\
+\ compiled with this flag can be monitored using \n\
+\ Context.setInstructionObserverThreshold. \n\
+\ -help, --help, -h Print this help and exit.\n\
+
+
+msg.no.file =\
+ A file name must be specified to compile.
+
+msg.invalid.classfile.name =\
+ File "{0}" is not a valid class file name.
+
+msg.extension.not.js =\
+ File "{0}" is not a valid js file name.
+
+msg.jsfile.not.found=\
+ File "{0}" not found.
+
+msg.multiple.js.to.file =\
+ Cannot compile multiple js files to "{0}".
+
+msg.package.name =\
+ "{0}" is not a valid package name.
+
+msg.spawn.args =\
+ Argument to spawn() must be a function or script.
+
+msg.must.implement.Script =\
+ Argument to loadClass() must be the name of a class that implements \
+ the Script interface. Class files generated by compiling scripts \
+ will implement Script.
+
+msg.runCommand.bad.args =\
+ The first argument to runCommand must be a command name.
+
+msg.runCommand.bad.env =\
+ A value of the env property of option object for runCommnad must be an \
+ object.
+
+msg.shell.seal.not.object =\
+ seal function can only be applied to objects
+
+msg.shell.seal.not.scriptable =\
+ seal function supports only sealing of ScriptableObject instances
+
+msg.shell.readFile.bad.args =\
+ readFile require at least file path to be specified
+
+msg.shell.readUrl.bad.args =\
+ readUrl require at least file path to be specified
+
+msg.shell.bad.function.scope =\
+ Wrong scope object for shell function: {0}
+
+msg.idswitch.same_string =\
+ The string {0} is used second time in the switch code. \
+ Previous occurrence was at line {1}
+
+msg.idswitch.file_end_in_switch =\
+ End of file inside tag {0}
+
+msg.idswitch.bad_tag_order =\
+ String switch tag {0} is not allowed here
+
+msg.idswitch.no_end_with_value =\
+ End for tag {0} can not contain value
+
+msg.idswitch.no_value_allowed =\
+ Tag {0} can not contain value
+
+msg.idswitch.no_end_usage =\
+ Tag {0} can not be used as end tag
+
+msg.idswitch.no_file_argument =\
+ File argument should be given
+
+msg.idswitch.too_many_arguments =\
+ Too many arguments are given
+
+msg.idswitch.bad_option =\
+ Invalid option {0}
+
+msg.idswitch.bad_option_char =\
+ Invalid option letter {0}
+
+msg.idswitch.bad_invocation =\
+StringIdMap: {0}\n\
+For more information, try\n\
+java org.mozilla.javascript.tools.idswitch.StringIdMap --help
+
+msg.idswitch.io_error =\
+StringIdMap: IO error, {0}
+
+msg.idswitch.usage = \
+Usage: java org.mozilla.javascript.tools.idswitch.StringIdMap [OPTIONS] JAVA_SOURCE_FILE\n\
+Generates efficient string dispatch code in JAVA_SOURCE_FILE.\n\
+The resulting Java source fragment replaces the old dispatch code.\n\
+If JAVA_SOURCE_FILE is -, standard input is used for Java source and the\n\
+result is sent to standard output.\n\
+\n\
+\ -h, --help display this help and exit\n\
+\ --version display version information and exit\n\
+\n\
+Note: the original file will be overwritten without any backup actions\n\
+\ and all code inside #generated# tag will be replaced by new one.
+
+msg.idswitch.version = \
+org.mozilla.javascript.tools.idswitch.StringIdMap version 0.2
+
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/ConsoleTextArea.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/ConsoleTextArea.java
new file mode 100644
index 0000000..08cac62
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/ConsoleTextArea.java
@@ -0,0 +1,300 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino JavaScript Debugger code, released
+ * November 21, 2000.
+ *
+ * The Initial Developer of the Original Code is
+ * See Beyond Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Christopher Oliver
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.mozilla.javascript.tools.shell;
+import java.io.*;
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.Document;
+import javax.swing.text.Segment;
+
+class ConsoleWrite implements Runnable {
+ private ConsoleTextArea textArea;
+ private String str;
+
+ public ConsoleWrite(ConsoleTextArea textArea, String str) {
+ this.textArea = textArea;
+ this.str = str;
+ }
+
+ public void run() {
+ textArea.write(str);
+ }
+}
+
+class ConsoleWriter extends java.io.OutputStream {
+
+ private ConsoleTextArea textArea;
+ private StringBuffer buffer;
+
+ public ConsoleWriter(ConsoleTextArea textArea) {
+ this.textArea = textArea;
+ buffer = new StringBuffer();
+ }
+
+ public synchronized void write(int ch) {
+ buffer.append((char)ch);
+ if(ch == '\n') {
+ flushBuffer();
+ }
+ }
+
+ public synchronized void write (char[] data, int off, int len) {
+ for(int i = off; i < len; i++) {
+ buffer.append(data[i]);
+ if(data[i] == '\n') {
+ flushBuffer();
+ }
+ }
+ }
+
+ public synchronized void flush() {
+ if (buffer.length() > 0) {
+ flushBuffer();
+ }
+ }
+
+ public void close () {
+ flush();
+ }
+
+ private void flushBuffer() {
+ String str = buffer.toString();
+ buffer.setLength(0);
+ SwingUtilities.invokeLater(new ConsoleWrite(textArea, str));
+ }
+}
+
+public class ConsoleTextArea
+ extends JTextArea implements KeyListener, DocumentListener
+{
+ static final long serialVersionUID = 8557083244830872961L;
+
+ private ConsoleWriter console1;
+ private ConsoleWriter console2;
+ private PrintStream out;
+ private PrintStream err;
+ private PrintWriter inPipe;
+ private PipedInputStream in;
+ private java.util.Vector history;
+ private int historyIndex = -1;
+ private int outputMark = 0;
+
+ public void select(int start, int end) {
+ requestFocus();
+ super.select(start, end);
+ }
+
+ public ConsoleTextArea(String[] argv) {
+ super();
+ history = new java.util.Vector();
+ console1 = new ConsoleWriter(this);
+ console2 = new ConsoleWriter(this);
+ out = new PrintStream(console1);
+ err = new PrintStream(console2);
+ PipedOutputStream outPipe = new PipedOutputStream();
+ inPipe = new PrintWriter(outPipe);
+ in = new PipedInputStream();
+ try {
+ outPipe.connect(in);
+ } catch(IOException exc) {
+ exc.printStackTrace();
+ }
+ getDocument().addDocumentListener(this);
+ addKeyListener(this);
+ setLineWrap(true);
+ setFont(new Font("Monospaced", 0, 12));
+ }
+
+
+ synchronized void returnPressed() {
+ Document doc = getDocument();
+ int len = doc.getLength();
+ Segment segment = new Segment();
+ try {
+ doc.getText(outputMark, len - outputMark, segment);
+ } catch(javax.swing.text.BadLocationException ignored) {
+ ignored.printStackTrace();
+ }
+ if(segment.count > 0) {
+ history.addElement(segment.toString());
+ }
+ historyIndex = history.size();
+ inPipe.write(segment.array, segment.offset, segment.count);
+ append("\n");
+ outputMark = doc.getLength();
+ inPipe.write("\n");
+ inPipe.flush();
+ console1.flush();
+ }
+
+ public void eval(String str) {
+ inPipe.write(str);
+ inPipe.write("\n");
+ inPipe.flush();
+ console1.flush();
+ }
+
+ public void keyPressed(KeyEvent e) {
+ int code = e.getKeyCode();
+ if(code == KeyEvent.VK_BACK_SPACE || code == KeyEvent.VK_LEFT) {
+ if(outputMark == getCaretPosition()) {
+ e.consume();
+ }
+ } else if(code == KeyEvent.VK_HOME) {
+ int caretPos = getCaretPosition();
+ if(caretPos == outputMark) {
+ e.consume();
+ } else if(caretPos > outputMark) {
+ if(!e.isControlDown()) {
+ if(e.isShiftDown()) {
+ moveCaretPosition(outputMark);
+ } else {
+ setCaretPosition(outputMark);
+ }
+ e.consume();
+ }
+ }
+ } else if(code == KeyEvent.VK_ENTER) {
+ returnPressed();
+ e.consume();
+ } else if(code == KeyEvent.VK_UP) {
+ historyIndex--;
+ if(historyIndex >= 0) {
+ if(historyIndex >= history.size()) {
+ historyIndex = history.size() -1;
+ }
+ if(historyIndex >= 0) {
+ String str = (String)history.elementAt(historyIndex);
+ int len = getDocument().getLength();
+ replaceRange(str, outputMark, len);
+ int caretPos = outputMark + str.length();
+ select(caretPos, caretPos);
+ } else {
+ historyIndex++;
+ }
+ } else {
+ historyIndex++;
+ }
+ e.consume();
+ } else if(code == KeyEvent.VK_DOWN) {
+ int caretPos = outputMark;
+ if(history.size() > 0) {
+ historyIndex++;
+ if(historyIndex < 0) {historyIndex = 0;}
+ int len = getDocument().getLength();
+ if(historyIndex < history.size()) {
+ String str = (String)history.elementAt(historyIndex);
+ replaceRange(str, outputMark, len);
+ caretPos = outputMark + str.length();
+ } else {
+ historyIndex = history.size();
+ replaceRange("", outputMark, len);
+ }
+ }
+ select(caretPos, caretPos);
+ e.consume();
+ }
+ }
+
+ public void keyTyped(KeyEvent e) {
+ int keyChar = e.getKeyChar();
+ if(keyChar == 0x8 /* KeyEvent.VK_BACK_SPACE */) {
+ if(outputMark == getCaretPosition()) {
+ e.consume();
+ }
+ } else if(getCaretPosition() < outputMark) {
+ setCaretPosition(outputMark);
+ }
+ }
+
+ public synchronized void keyReleased(KeyEvent e) {
+ }
+
+ public synchronized void write(String str) {
+ insert(str, outputMark);
+ int len = str.length();
+ outputMark += len;
+ select(outputMark, outputMark);
+ }
+
+ public synchronized void insertUpdate(DocumentEvent e) {
+ int len = e.getLength();
+ int off = e.getOffset();
+ if(outputMark > off) {
+ outputMark += len;
+ }
+ }
+
+ public synchronized void removeUpdate(DocumentEvent e) {
+ int len = e.getLength();
+ int off = e.getOffset();
+ if(outputMark > off) {
+ if(outputMark >= off + len) {
+ outputMark -= len;
+ } else {
+ outputMark = off;
+ }
+ }
+ }
+
+ public synchronized void postUpdateUI() {
+ // this attempts to cleanup the damage done by updateComponentTreeUI
+ requestFocus();
+ setCaret(getCaret());
+ select(outputMark, outputMark);
+ }
+
+ public synchronized void changedUpdate(DocumentEvent e) {
+ }
+
+
+ public InputStream getIn() {
+ return in;
+ }
+
+ public PrintStream getOut() {
+ return out;
+ }
+
+ public PrintStream getErr() {
+ return err;
+ }
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Environment.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Environment.java
new file mode 100644
index 0000000..19904b9
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Environment.java
@@ -0,0 +1,141 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ Environment.java
+
+ Wraps java.lang.System properties.
+
+ by Patrick C. Beard <beard@netscape.com>
+ */
+
+package org.mozilla.javascript.tools.shell;
+
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.ScriptableObject;
+
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.Properties;
+
+/**
+ * Environment, intended to be instantiated at global scope, provides
+ * a natural way to access System properties from JavaScript.
+ *
+ * @author Patrick C. Beard
+ */
+public class Environment extends ScriptableObject
+{
+ static final long serialVersionUID = -430727378460177065L;
+
+ private Environment thePrototypeInstance = null;
+
+ public static void defineClass(ScriptableObject scope) {
+ try {
+ ScriptableObject.defineClass(scope, Environment.class);
+ } catch (Exception e) {
+ throw new Error(e.getMessage());
+ }
+ }
+
+ public String getClassName() {
+ return "Environment";
+ }
+
+ public Environment() {
+ if (thePrototypeInstance == null)
+ thePrototypeInstance = this;
+ }
+
+ public Environment(ScriptableObject scope) {
+ setParentScope(scope);
+ Object ctor = ScriptRuntime.getTopLevelProp(scope, "Environment");
+ if (ctor != null && ctor instanceof Scriptable) {
+ Scriptable s = (Scriptable) ctor;
+ setPrototype((Scriptable) s.get("prototype", s));
+ }
+ }
+
+ public boolean has(String name, Scriptable start) {
+ if (this == thePrototypeInstance)
+ return super.has(name, start);
+
+ return (System.getProperty(name) != null);
+ }
+
+ public Object get(String name, Scriptable start) {
+ if (this == thePrototypeInstance)
+ return super.get(name, start);
+
+ String result = System.getProperty(name);
+ if (result != null)
+ return ScriptRuntime.toObject(getParentScope(), result);
+ else
+ return Scriptable.NOT_FOUND;
+ }
+
+ public void put(String name, Scriptable start, Object value) {
+ if (this == thePrototypeInstance)
+ super.put(name, start, value);
+ else
+ System.getProperties().put(name, ScriptRuntime.toString(value));
+ }
+
+ private Object[] collectIds() {
+ Properties props = System.getProperties();
+ Enumeration names = props.propertyNames();
+ Vector keys = new Vector();
+ while (names.hasMoreElements())
+ keys.addElement(names.nextElement());
+ Object[] ids = new Object[keys.size()];
+ keys.copyInto(ids);
+ return ids;
+ }
+
+ public Object[] getIds() {
+ if (this == thePrototypeInstance)
+ return super.getIds();
+ return collectIds();
+ }
+
+ public Object[] getAllIds() {
+ if (this == thePrototypeInstance)
+ return super.getAllIds();
+ return collectIds();
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Global.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Global.java
new file mode 100644
index 0000000..fdb8f4e
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Global.java
@@ -0,0 +1,1038 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Patrick Beard
+ * Igor Bukanov
+ * Norris Boyd
+ * Rob Ginda
+ * Kurt Westerfeld
+ * Matthias Radestock
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.tools.shell;
+
+import java.io.*;
+import java.net.*;
+import java.lang.reflect.*;
+import org.mozilla.javascript.*;
+import org.mozilla.javascript.tools.ToolErrorReporter;
+import org.mozilla.javascript.serialize.*;
+
+/**
+ * This class provides for sharing functions across multiple threads.
+ * This is of particular interest to server applications.
+ *
+ * @author Norris Boyd
+ */
+public class Global extends ImporterTopLevel
+{
+ static final long serialVersionUID = 4029130780977538005L;
+
+ NativeArray history;
+ private InputStream inStream;
+ private PrintStream outStream;
+ private PrintStream errStream;
+ private boolean sealedStdLib = false;
+ boolean initialized;
+ private QuitAction quitAction;
+ private String[] prompts = { "js> ", " > " };
+
+ public Global()
+ {
+ }
+
+ public Global(Context cx)
+ {
+ init(cx);
+ }
+
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ /**
+ * Set the action to call from quit().
+ */
+ public void initQuitAction(QuitAction quitAction)
+ {
+ if (quitAction == null)
+ throw new IllegalArgumentException("quitAction is null");
+ if (this.quitAction != null)
+ throw new IllegalArgumentException("The method is once-call.");
+
+ this.quitAction = quitAction;
+ }
+
+ public void init(ContextFactory factory)
+ {
+ factory.call(new ContextAction() {
+ public Object run(Context cx)
+ {
+ init(cx);
+ return null;
+ }
+ });
+ }
+
+ public void init(Context cx)
+ {
+ // Define some global functions particular to the shell. Note
+ // that these functions are not part of ECMA.
+ initStandardObjects(cx, sealedStdLib);
+ String[] names = {
+ "defineClass",
+ "deserialize",
+ "gc",
+ "help",
+ "load",
+ "loadClass",
+ "print",
+ "quit",
+ "readFile",
+ "readUrl",
+ "runCommand",
+ "seal",
+ "serialize",
+ "spawn",
+ "sync",
+ "toint32",
+ "version",
+ };
+ defineFunctionProperties(names, Global.class,
+ ScriptableObject.DONTENUM);
+
+ // Set up "environment" in the global scope to provide access to the
+ // System environment variables.
+ Environment.defineClass(this);
+ Environment environment = new Environment(this);
+ defineProperty("environment", environment,
+ ScriptableObject.DONTENUM);
+
+ history = (NativeArray) cx.newArray(this, 0);
+ defineProperty("history", history, ScriptableObject.DONTENUM);
+ initialized = true;
+ }
+
+ /**
+ * Print a help message.
+ *
+ * This method is defined as a JavaScript function.
+ */
+ public static void help(Context cx, Scriptable thisObj,
+ Object[] args, Function funObj)
+ {
+ PrintStream out = getInstance(funObj).getOut();
+ out.println(ToolErrorReporter.getMessage("msg.help"));
+ }
+
+ public static void gc(Context cx, Scriptable thisObj,
+ Object[] args, Function funObj)
+ {
+ System.gc();
+ }
+
+
+ /**
+ * Print the string values of its arguments.
+ *
+ * This method is defined as a JavaScript function.
+ * Note that its arguments are of the "varargs" form, which
+ * allows it to handle an arbitrary number of arguments
+ * supplied to the JavaScript function.
+ *
+ */
+ public static Object print(Context cx, Scriptable thisObj,
+ Object[] args, Function funObj)
+ {
+ PrintStream out = getInstance(funObj).getOut();
+ for (int i=0; i < args.length; i++) {
+ if (i > 0)
+ out.print(" ");
+
+ // Convert the arbitrary JavaScript value into a string form.
+ String s = Context.toString(args[i]);
+
+ out.print(s);
+ }
+ out.println();
+ return Context.getUndefinedValue();
+ }
+
+ /**
+ * Call embedding-specific quit action passing its argument as
+ * int32 exit code.
+ *
+ * This method is defined as a JavaScript function.
+ */
+ public static void quit(Context cx, Scriptable thisObj,
+ Object[] args, Function funObj)
+ {
+ Global global = getInstance(funObj);
+ if (global.quitAction != null) {
+ int exitCode = (args.length == 0 ? 0
+ : ScriptRuntime.toInt32(args[0]));
+ global.quitAction.quit(cx, exitCode);
+ }
+ }
+
+ /**
+ * Get and set the language version.
+ *
+ * This method is defined as a JavaScript function.
+ */
+ public static double version(Context cx, Scriptable thisObj,
+ Object[] args, Function funObj)
+ {
+ double result = cx.getLanguageVersion();
+ if (args.length > 0) {
+ double d = Context.toNumber(args[0]);
+ cx.setLanguageVersion((int) d);
+ }
+ return result;
+ }
+
+ /**
+ * Load and execute a set of JavaScript source files.
+ *
+ * This method is defined as a JavaScript function.
+ *
+ */
+ public static void load(Context cx, Scriptable thisObj,
+ Object[] args, Function funObj)
+ {
+ for (int i = 0; i < args.length; i++) {
+ Main.processFile(cx, thisObj, Context.toString(args[i]));
+ }
+ }
+
+ /**
+ * Load a Java class that defines a JavaScript object using the
+ * conventions outlined in ScriptableObject.defineClass.
+ * <p>
+ * This method is defined as a JavaScript function.
+ * @exception IllegalAccessException if access is not available
+ * to a reflected class member
+ * @exception InstantiationException if unable to instantiate
+ * the named class
+ * @exception InvocationTargetException if an exception is thrown
+ * during execution of methods of the named class
+ * @see org.mozilla.javascript.ScriptableObject#defineClass(Scriptable,Class)
+ */
+ public static void defineClass(Context cx, Scriptable thisObj,
+ Object[] args, Function funObj)
+ throws IllegalAccessException, InstantiationException,
+ InvocationTargetException
+ {
+ Class clazz = getClass(args);
+ ScriptableObject.defineClass(thisObj, clazz);
+ }
+
+ /**
+ * Load and execute a script compiled to a class file.
+ * <p>
+ * This method is defined as a JavaScript function.
+ * When called as a JavaScript function, a single argument is
+ * expected. This argument should be the name of a class that
+ * implements the Script interface, as will any script
+ * compiled by jsc.
+ *
+ * @exception IllegalAccessException if access is not available
+ * to the class
+ * @exception InstantiationException if unable to instantiate
+ * the named class
+ */
+ public static void loadClass(Context cx, Scriptable thisObj,
+ Object[] args, Function funObj)
+ throws IllegalAccessException, InstantiationException
+ {
+ Class clazz = getClass(args);
+ if (!Script.class.isAssignableFrom(clazz)) {
+ throw reportRuntimeError("msg.must.implement.Script");
+ }
+ Script script = (Script) clazz.newInstance();
+ script.exec(cx, thisObj);
+ }
+
+ private static Class getClass(Object[] args) {
+ if (args.length == 0) {
+ throw reportRuntimeError("msg.expected.string.arg");
+ }
+ Object arg0 = args[0];
+ if (arg0 instanceof Wrapper) {
+ Object wrapped = ((Wrapper)arg0).unwrap();
+ if (wrapped instanceof Class)
+ return (Class)wrapped;
+ }
+ String className = Context.toString(args[0]);
+ try {
+ return Class.forName(className);
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw reportRuntimeError("msg.class.not.found", className);
+ }
+ }
+
+ public static void serialize(Context cx, Scriptable thisObj,
+ Object[] args, Function funObj)
+ throws IOException
+ {
+ if (args.length < 2) {
+ throw Context.reportRuntimeError(
+ "Expected an object to serialize and a filename to write " +
+ "the serialization to");
+ }
+ Object obj = args[0];
+ String filename = Context.toString(args[1]);
+ FileOutputStream fos = new FileOutputStream(filename);
+ Scriptable scope = ScriptableObject.getTopLevelScope(thisObj);
+ ScriptableOutputStream out = new ScriptableOutputStream(fos, scope);
+ out.writeObject(obj);
+ out.close();
+ }
+
+ public static Object deserialize(Context cx, Scriptable thisObj,
+ Object[] args, Function funObj)
+ throws IOException, ClassNotFoundException
+ {
+ if (args.length < 1) {
+ throw Context.reportRuntimeError(
+ "Expected a filename to read the serialization from");
+ }
+ String filename = Context.toString(args[0]);
+ FileInputStream fis = new FileInputStream(filename);
+ Scriptable scope = ScriptableObject.getTopLevelScope(thisObj);
+ ObjectInputStream in = new ScriptableInputStream(fis, scope);
+ Object deserialized = in.readObject();
+ in.close();
+ return Context.toObject(deserialized, scope);
+ }
+
+ public String[] getPrompts(Context cx) {
+ if (ScriptableObject.hasProperty(this, "prompts")) {
+ Object promptsJS = ScriptableObject.getProperty(this,
+ "prompts");
+ if (promptsJS instanceof Scriptable) {
+ Scriptable s = (Scriptable) promptsJS;
+ if (ScriptableObject.hasProperty(s, 0) &&
+ ScriptableObject.hasProperty(s, 1))
+ {
+ Object elem0 = ScriptableObject.getProperty(s, 0);
+ if (elem0 instanceof Function) {
+ elem0 = ((Function) elem0).call(cx, this, s,
+ new Object[0]);
+ }
+ prompts[0] = Context.toString(elem0);
+ Object elem1 = ScriptableObject.getProperty(s, 1);
+ if (elem1 instanceof Function) {
+ elem1 = ((Function) elem1).call(cx, this, s,
+ new Object[0]);
+ }
+ prompts[1] = Context.toString(elem1);
+ }
+ }
+ }
+ return prompts;
+ }
+
+ /**
+ * The spawn function runs a given function or script in a different
+ * thread.
+ *
+ * js> function g() { a = 7; }
+ * js> a = 3;
+ * 3
+ * js> spawn(g)
+ * Thread[Thread-1,5,main]
+ * js> a
+ * 3
+ */
+ public static Object spawn(Context cx, Scriptable thisObj, Object[] args,
+ Function funObj)
+ {
+ Scriptable scope = funObj.getParentScope();
+ Runner runner;
+ if (args.length != 0 && args[0] instanceof Function) {
+ Object[] newArgs = null;
+ if (args.length > 1 && args[1] instanceof Scriptable) {
+ newArgs = cx.getElements((Scriptable) args[1]);
+ }
+ if (newArgs == null) { newArgs = ScriptRuntime.emptyArgs; }
+ runner = new Runner(scope, (Function) args[0], newArgs);
+ } else if (args.length != 0 && args[0] instanceof Script) {
+ runner = new Runner(scope, (Script) args[0]);
+ } else {
+ throw reportRuntimeError("msg.spawn.args");
+ }
+ runner.factory = cx.getFactory();
+ Thread thread = new Thread(runner);
+ thread.start();
+ return thread;
+ }
+
+ /**
+ * The sync function creates a synchronized function (in the sense
+ * of a Java synchronized method) from an existing function. The
+ * new function synchronizes on the <code>this</code> object of
+ * its invocation.
+ * js> var o = { f : sync(function(x) {
+ * print("entry");
+ * Packages.java.lang.Thread.sleep(x*1000);
+ * print("exit");
+ * })};
+ * js> spawn(function() {o.f(5);});
+ * Thread[Thread-0,5,main]
+ * entry
+ * js> spawn(function() {o.f(5);});
+ * Thread[Thread-1,5,main]
+ * js>
+ * exit
+ * entry
+ * exit
+ */
+ public static Object sync(Context cx, Scriptable thisObj, Object[] args,
+ Function funObj)
+ {
+ if (args.length == 1 && args[0] instanceof Function) {
+ return new Synchronizer((Function)args[0]);
+ }
+ else {
+ throw reportRuntimeError("msg.sync.args");
+ }
+ }
+
+ /**
+ * Execute the specified command with the given argument and options
+ * as a separate process and return the exit status of the process.
+ * <p>
+ * Usage:
+ * <pre>
+ * runCommand(command)
+ * runCommand(command, arg1, ..., argN)
+ * runCommand(command, arg1, ..., argN, options)
+ * </pre>
+ * All except the last arguments to runCommand are converted to strings
+ * and denote command name and its arguments. If the last argument is a
+ * JavaScript object, it is an option object. Otherwise it is converted to
+ * string denoting the last argument and options objects assumed to be
+ * empty.
+ * Te following properties of the option object are processed:
+ * <ul>
+ * <li><tt>args</tt> - provides an array of additional command arguments
+ * <li><tt>env</tt> - explicit environment object. All its enumeratable
+ * properties define the corresponding environment variable names.
+ * <li><tt>input</tt> - the process input. If it is not
+ * java.io.InputStream, it is converted to string and sent to the process
+ * as its input. If not specified, no input is provided to the process.
+ * <li><tt>output</tt> - the process output instead of
+ * java.lang.System.out. If it is not instance of java.io.OutputStream,
+ * the process output is read, converted to a string, appended to the
+ * output property value converted to string and put as the new value of
+ * the output property.
+ * <li><tt>err</tt> - the process error output instead of
+ * java.lang.System.err. If it is not instance of java.io.OutputStream,
+ * the process error output is read, converted to a string, appended to
+ * the err property value converted to string and put as the new
+ * value of the err property.
+ * </ul>
+ */
+ public static Object runCommand(Context cx, Scriptable thisObj,
+ Object[] args, Function funObj)
+ throws IOException
+ {
+ int L = args.length;
+ if (L == 0 || (L == 1 && args[0] instanceof Scriptable)) {
+ throw reportRuntimeError("msg.runCommand.bad.args");
+ }
+
+ InputStream in = null;
+ OutputStream out = null, err = null;
+ ByteArrayOutputStream outBytes = null, errBytes = null;
+ Object outObj = null, errObj = null;
+ String[] environment = null;
+ Scriptable params = null;
+ Object[] addArgs = null;
+ if (args[L - 1] instanceof Scriptable) {
+ params = (Scriptable)args[L - 1];
+ --L;
+ Object envObj = ScriptableObject.getProperty(params, "env");
+ if (envObj != Scriptable.NOT_FOUND) {
+ if (envObj == null) {
+ environment = new String[0];
+ } else {
+ if (!(envObj instanceof Scriptable)) {
+ throw reportRuntimeError("msg.runCommand.bad.env");
+ }
+ Scriptable envHash = (Scriptable)envObj;
+ Object[] ids = ScriptableObject.getPropertyIds(envHash);
+ environment = new String[ids.length];
+ for (int i = 0; i != ids.length; ++i) {
+ Object keyObj = ids[i], val;
+ String key;
+ if (keyObj instanceof String) {
+ key = (String)keyObj;
+ val = ScriptableObject.getProperty(envHash, key);
+ } else {
+ int ikey = ((Number)keyObj).intValue();
+ key = Integer.toString(ikey);
+ val = ScriptableObject.getProperty(envHash, ikey);
+ }
+ if (val == ScriptableObject.NOT_FOUND) {
+ val = Undefined.instance;
+ }
+ environment[i] = key+'='+ScriptRuntime.toString(val);
+ }
+ }
+ }
+ Object inObj = ScriptableObject.getProperty(params, "input");
+ if (inObj != Scriptable.NOT_FOUND) {
+ in = toInputStream(inObj);
+ }
+ outObj = ScriptableObject.getProperty(params, "output");
+ if (outObj != Scriptable.NOT_FOUND) {
+ out = toOutputStream(outObj);
+ if (out == null) {
+ outBytes = new ByteArrayOutputStream();
+ out = outBytes;
+ }
+ }
+ errObj = ScriptableObject.getProperty(params, "err");
+ if (errObj != Scriptable.NOT_FOUND) {
+ err = toOutputStream(errObj);
+ if (err == null) {
+ errBytes = new ByteArrayOutputStream();
+ err = errBytes;
+ }
+ }
+ Object addArgsObj = ScriptableObject.getProperty(params, "args");
+ if (addArgsObj != Scriptable.NOT_FOUND) {
+ Scriptable s = Context.toObject(addArgsObj,
+ getTopLevelScope(thisObj));
+ addArgs = cx.getElements(s);
+ }
+ }
+ Global global = getInstance(funObj);
+ if (out == null) {
+ out = (global != null) ? global.getOut() : System.out;
+ }
+ if (err == null) {
+ err = (global != null) ? global.getErr() : System.err;
+ }
+ // If no explicit input stream, do not send any input to process,
+ // in particular, do not use System.in to avoid deadlocks
+ // when waiting for user input to send to process which is already
+ // terminated as it is not always possible to interrupt read method.
+
+ String[] cmd = new String[(addArgs == null) ? L : L + addArgs.length];
+ for (int i = 0; i != L; ++i) {
+ cmd[i] = ScriptRuntime.toString(args[i]);
+ }
+ if (addArgs != null) {
+ for (int i = 0; i != addArgs.length; ++i) {
+ cmd[L + i] = ScriptRuntime.toString(addArgs[i]);
+ }
+ }
+
+ int exitCode = runProcess(cmd, environment, in, out, err);
+ if (outBytes != null) {
+ String s = ScriptRuntime.toString(outObj) + outBytes.toString();
+ ScriptableObject.putProperty(params, "output", s);
+ }
+ if (errBytes != null) {
+ String s = ScriptRuntime.toString(errObj) + errBytes.toString();
+ ScriptableObject.putProperty(params, "err", s);
+ }
+
+ return new Integer(exitCode);
+ }
+
+ /**
+ * The seal function seals all supplied arguments.
+ */
+ public static void seal(Context cx, Scriptable thisObj, Object[] args,
+ Function funObj)
+ {
+ for (int i = 0; i != args.length; ++i) {
+ Object arg = args[i];
+ if (!(arg instanceof ScriptableObject) || arg == Undefined.instance)
+ {
+ if (!(arg instanceof Scriptable) || arg == Undefined.instance)
+ {
+ throw reportRuntimeError("msg.shell.seal.not.object");
+ } else {
+ throw reportRuntimeError("msg.shell.seal.not.scriptable");
+ }
+ }
+ }
+
+ for (int i = 0; i != args.length; ++i) {
+ Object arg = args[i];
+ ((ScriptableObject)arg).sealObject();
+ }
+ }
+
+ /**
+ * The readFile reads the given file content and convert it to a string
+ * using the specified character coding or default character coding if
+ * explicit coding argument is not given.
+ * <p>
+ * Usage:
+ * <pre>
+ * readFile(filePath)
+ * readFile(filePath, charCoding)
+ * </pre>
+ * The first form converts file's context to string using the default
+ * character coding.
+ */
+ public static Object readFile(Context cx, Scriptable thisObj, Object[] args,
+ Function funObj)
+ throws IOException
+ {
+ if (args.length == 0) {
+ throw reportRuntimeError("msg.shell.readFile.bad.args");
+ }
+ String path = ScriptRuntime.toString(args[0]);
+ String charCoding = null;
+ if (args.length >= 2) {
+ charCoding = ScriptRuntime.toString(args[1]);
+ }
+
+ return readUrl(path, charCoding, true);
+ }
+
+ /**
+ * The readUrl opens connection to the given URL, read all its data
+ * and converts them to a string
+ * using the specified character coding or default character coding if
+ * explicit coding argument is not given.
+ * <p>
+ * Usage:
+ * <pre>
+ * readUrl(url)
+ * readUrl(url, charCoding)
+ * </pre>
+ * The first form converts file's context to string using the default
+ * charCoding.
+ */
+ public static Object readUrl(Context cx, Scriptable thisObj, Object[] args,
+ Function funObj)
+ throws IOException
+ {
+ if (args.length == 0) {
+ throw reportRuntimeError("msg.shell.readUrl.bad.args");
+ }
+ String url = ScriptRuntime.toString(args[0]);
+ String charCoding = null;
+ if (args.length >= 2) {
+ charCoding = ScriptRuntime.toString(args[1]);
+ }
+
+ return readUrl(url, charCoding, false);
+ }
+
+ /**
+ * Convert the argumnet to int32 number.
+ */
+ public static Object toint32(Context cx, Scriptable thisObj, Object[] args,
+ Function funObj)
+ {
+ Object arg = (args.length != 0 ? args[0] : Undefined.instance);
+ if (arg instanceof Integer)
+ return arg;
+ return ScriptRuntime.wrapInt(ScriptRuntime.toInt32(arg));
+ }
+
+ public InputStream getIn() {
+ return inStream == null ? System.in : inStream;
+ }
+
+ public void setIn(InputStream in) {
+ inStream = in;
+ }
+
+ public PrintStream getOut() {
+ return outStream == null ? System.out : outStream;
+ }
+
+ public void setOut(PrintStream out) {
+ outStream = out;
+ }
+
+ public PrintStream getErr() {
+ return errStream == null ? System.err : errStream;
+ }
+
+ public void setErr(PrintStream err) {
+ errStream = err;
+ }
+
+ public void setSealedStdLib(boolean value)
+ {
+ sealedStdLib = value;
+ }
+
+ private static Global getInstance(Function function)
+ {
+ Scriptable scope = function.getParentScope();
+ if (!(scope instanceof Global))
+ throw reportRuntimeError("msg.bad.shell.function.scope",
+ String.valueOf(scope));
+ return (Global)scope;
+ }
+
+ /**
+ * Runs the given process using Runtime.exec().
+ * If any of in, out, err is null, the corresponding process stream will
+ * be closed immediately, otherwise it will be closed as soon as
+ * all data will be read from/written to process
+ *
+ * @return Exit value of process.
+ * @throws IOException If there was an error executing the process.
+ */
+ private static int runProcess(String[] cmd, String[] environment,
+ InputStream in, OutputStream out,
+ OutputStream err)
+ throws IOException
+ {
+ Process p;
+ if (environment == null) {
+ p = Runtime.getRuntime().exec(cmd);
+ } else {
+ p = Runtime.getRuntime().exec(cmd, environment);
+ }
+
+ try {
+ PipeThread inThread = null;
+ if (in != null) {
+ inThread = new PipeThread(false, in, p.getOutputStream());
+ inThread.start();
+ } else {
+ p.getOutputStream().close();
+ }
+
+ PipeThread outThread = null;
+ if (out != null) {
+ outThread = new PipeThread(true, p.getInputStream(), out);
+ outThread.start();
+ } else {
+ p.getInputStream().close();
+ }
+
+ PipeThread errThread = null;
+ if (err != null) {
+ errThread = new PipeThread(true, p.getErrorStream(), err);
+ errThread.start();
+ } else {
+ p.getErrorStream().close();
+ }
+
+ // wait for process completion
+ for (;;) {
+ try {
+ p.waitFor();
+ if (outThread != null) {
+ outThread.join();
+ }
+ if (inThread != null) {
+ inThread.join();
+ }
+ if (errThread != null) {
+ errThread.join();
+ }
+ break;
+ } catch (InterruptedException ignore) {
+ }
+ }
+
+ return p.exitValue();
+ } finally {
+ p.destroy();
+ }
+ }
+
+ static void pipe(boolean fromProcess, InputStream from, OutputStream to)
+ throws IOException
+ {
+ try {
+ final int SIZE = 4096;
+ byte[] buffer = new byte[SIZE];
+ for (;;) {
+ int n;
+ if (!fromProcess) {
+ n = from.read(buffer, 0, SIZE);
+ } else {
+ try {
+ n = from.read(buffer, 0, SIZE);
+ } catch (IOException ex) {
+ // Ignore exception as it can be cause by closed pipe
+ break;
+ }
+ }
+ if (n < 0) { break; }
+ if (fromProcess) {
+ to.write(buffer, 0, n);
+ to.flush();
+ } else {
+ try {
+ to.write(buffer, 0, n);
+ to.flush();
+ } catch (IOException ex) {
+ // Ignore exception as it can be cause by closed pipe
+ break;
+ }
+ }
+ }
+ } finally {
+ try {
+ if (fromProcess) {
+ from.close();
+ } else {
+ to.close();
+ }
+ } catch (IOException ex) {
+ // Ignore errors on close. On Windows JVM may throw invalid
+ // refrence exception if process terminates too fast.
+ }
+ }
+ }
+
+ private static InputStream toInputStream(Object value)
+ throws IOException
+ {
+ InputStream is = null;
+ String s = null;
+ if (value instanceof Wrapper) {
+ Object unwrapped = ((Wrapper)value).unwrap();
+ if (unwrapped instanceof InputStream) {
+ is = (InputStream)unwrapped;
+ } else if (unwrapped instanceof byte[]) {
+ is = new ByteArrayInputStream((byte[])unwrapped);
+ } else if (unwrapped instanceof Reader) {
+ s = readReader((Reader)unwrapped);
+ } else if (unwrapped instanceof char[]) {
+ s = new String((char[])unwrapped);
+ }
+ }
+ if (is == null) {
+ if (s == null) { s = ScriptRuntime.toString(value); }
+ is = new ByteArrayInputStream(s.getBytes());
+ }
+ return is;
+ }
+
+ private static OutputStream toOutputStream(Object value) {
+ OutputStream os = null;
+ if (value instanceof Wrapper) {
+ Object unwrapped = ((Wrapper)value).unwrap();
+ if (unwrapped instanceof OutputStream) {
+ os = (OutputStream)unwrapped;
+ }
+ }
+ return os;
+ }
+
+ private static String readUrl(String filePath, String charCoding,
+ boolean urlIsFile)
+ throws IOException
+ {
+ int chunkLength;
+ InputStream is = null;
+ try {
+ if (!urlIsFile) {
+ URL urlObj = new URL(filePath);
+ URLConnection uc = urlObj.openConnection();
+ is = uc.getInputStream();
+ chunkLength = uc.getContentLength();
+ if (chunkLength <= 0)
+ chunkLength = 1024;
+ if (charCoding == null) {
+ String type = uc.getContentType();
+ if (type != null) {
+ charCoding = getCharCodingFromType(type);
+ }
+ }
+ } else {
+ File f = new File(filePath);
+
+ long length = f.length();
+ chunkLength = (int)length;
+ if (chunkLength != length)
+ throw new IOException("Too big file size: "+length);
+
+ if (chunkLength == 0) { return ""; }
+
+ is = new FileInputStream(f);
+ }
+
+ Reader r;
+ if (charCoding == null) {
+ r = new InputStreamReader(is);
+ } else {
+ r = new InputStreamReader(is, charCoding);
+ }
+ return readReader(r, chunkLength);
+
+ } finally {
+ if (is != null)
+ is.close();
+ }
+ }
+
+ private static String getCharCodingFromType(String type)
+ {
+ int i = type.indexOf(';');
+ if (i >= 0) {
+ int end = type.length();
+ ++i;
+ while (i != end && type.charAt(i) <= ' ') {
+ ++i;
+ }
+ String charset = "charset";
+ if (charset.regionMatches(true, 0, type, i, charset.length()))
+ {
+ i += charset.length();
+ while (i != end && type.charAt(i) <= ' ') {
+ ++i;
+ }
+ if (i != end && type.charAt(i) == '=') {
+ ++i;
+ while (i != end && type.charAt(i) <= ' ') {
+ ++i;
+ }
+ if (i != end) {
+ // i is at the start of non-empty
+ // charCoding spec
+ while (type.charAt(end -1) <= ' ') {
+ --end;
+ }
+ return type.substring(i, end);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static String readReader(Reader reader)
+ throws IOException
+ {
+ return readReader(reader, 4096);
+ }
+
+ private static String readReader(Reader reader, int initialBufferSize)
+ throws IOException
+ {
+ char[] buffer = new char[initialBufferSize];
+ int offset = 0;
+ for (;;) {
+ int n = reader.read(buffer, offset, buffer.length - offset);
+ if (n < 0) { break; }
+ offset += n;
+ if (offset == buffer.length) {
+ char[] tmp = new char[buffer.length * 2];
+ System.arraycopy(buffer, 0, tmp, 0, offset);
+ buffer = tmp;
+ }
+ }
+ return new String(buffer, 0, offset);
+ }
+
+ static RuntimeException reportRuntimeError(String msgId) {
+ String message = ToolErrorReporter.getMessage(msgId);
+ return Context.reportRuntimeError(message);
+ }
+
+ static RuntimeException reportRuntimeError(String msgId, String msgArg)
+ {
+ String message = ToolErrorReporter.getMessage(msgId, msgArg);
+ return Context.reportRuntimeError(message);
+ }
+}
+
+
+class Runner implements Runnable, ContextAction {
+
+ Runner(Scriptable scope, Function func, Object[] args) {
+ this.scope = scope;
+ f = func;
+ this.args = args;
+ }
+
+ Runner(Scriptable scope, Script script) {
+ this.scope = scope;
+ s = script;
+ }
+
+ public void run()
+ {
+ factory.call(this);
+ }
+
+ public Object run(Context cx)
+ {
+ if (f != null)
+ return f.call(cx, scope, scope, args);
+ else
+ return s.exec(cx, scope);
+ }
+
+ ContextFactory factory;
+ private Scriptable scope;
+ private Function f;
+ private Script s;
+ private Object[] args;
+}
+
+class PipeThread extends Thread {
+
+ PipeThread(boolean fromProcess, InputStream from, OutputStream to) {
+ setDaemon(true);
+ this.fromProcess = fromProcess;
+ this.from = from;
+ this.to = to;
+ }
+
+ public void run() {
+ try {
+ Global.pipe(fromProcess, from, to);
+ } catch (IOException ex) {
+ throw Context.throwAsScriptRuntimeEx(ex);
+ }
+ }
+
+ private boolean fromProcess;
+ private InputStream from;
+ private OutputStream to;
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/JSConsole.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/JSConsole.java
new file mode 100644
index 0000000..f6fe3a1
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/JSConsole.java
@@ -0,0 +1,225 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino JavaScript Debugger code, released
+ * November 21, 2000.
+ *
+ * The Initial Developer of the Original Code is
+ * See Beyond Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Christopher Oliver
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.mozilla.javascript.tools.shell;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.File;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JRadioButtonMenuItem;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+
+import org.mozilla.javascript.SecurityUtilities;
+
+public class JSConsole extends JFrame implements ActionListener
+{
+ static final long serialVersionUID = 2551225560631876300L;
+
+ private File CWD;
+ private JFileChooser dlg;
+ private ConsoleTextArea consoleTextArea;
+
+ public String chooseFile() {
+ if(CWD == null) {
+ String dir = SecurityUtilities.getSystemProperty("user.dir");
+ if(dir != null) {
+ CWD = new File(dir);
+ }
+ }
+ if(CWD != null) {
+ dlg.setCurrentDirectory(CWD);
+ }
+ dlg.setDialogTitle("Select a file to load");
+ int returnVal = dlg.showOpenDialog(this);
+ if(returnVal == JFileChooser.APPROVE_OPTION) {
+ String result = dlg.getSelectedFile().getPath();
+ CWD = new File(dlg.getSelectedFile().getParent());
+ return result;
+ }
+ return null;
+ }
+
+ public static void main(String args[]) {
+ new JSConsole(args);
+ }
+
+ public void createFileChooser() {
+ dlg = new JFileChooser();
+ javax.swing.filechooser.FileFilter filter =
+ new javax.swing.filechooser.FileFilter() {
+ public boolean accept(File f) {
+ if(f.isDirectory()) {
+ return true;
+ }
+ String name = f.getName();
+ int i = name.lastIndexOf('.');
+ if(i > 0 && i < name.length() -1) {
+ String ext = name.substring(i + 1).toLowerCase();
+ if(ext.equals("js")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String getDescription() {
+ return "JavaScript Files (*.js)";
+ }
+ };
+ dlg.addChoosableFileFilter(filter);
+
+ }
+
+ public JSConsole(String[] args) {
+ super("Rhino JavaScript Console");
+ JMenuBar menubar = new JMenuBar();
+ createFileChooser();
+ String[] fileItems = {"Load...", "Exit"};
+ String[] fileCmds = {"Load", "Exit"};
+ char[] fileShortCuts = {'L', 'X'};
+ String[] editItems = {"Cut", "Copy", "Paste"};
+ char[] editShortCuts = {'T', 'C', 'P'};
+ String[] plafItems = {"Metal", "Windows", "Motif"};
+ boolean [] plafState = {true, false, false};
+ JMenu fileMenu = new JMenu("File");
+ fileMenu.setMnemonic('F');
+ JMenu editMenu = new JMenu("Edit");
+ editMenu.setMnemonic('E');
+ JMenu plafMenu = new JMenu("Platform");
+ plafMenu.setMnemonic('P');
+ for(int i = 0; i < fileItems.length; ++i) {
+ JMenuItem item = new JMenuItem(fileItems[i],
+ fileShortCuts[i]);
+ item.setActionCommand(fileCmds[i]);
+ item.addActionListener(this);
+ fileMenu.add(item);
+ }
+ for(int i = 0; i < editItems.length; ++i) {
+ JMenuItem item = new JMenuItem(editItems[i],
+ editShortCuts[i]);
+ item.addActionListener(this);
+ editMenu.add(item);
+ }
+ ButtonGroup group = new ButtonGroup();
+ for(int i = 0; i < plafItems.length; ++i) {
+ JRadioButtonMenuItem item = new JRadioButtonMenuItem(plafItems[i],
+ plafState[i]);
+ group.add(item);
+ item.addActionListener(this);
+ plafMenu.add(item);
+ }
+ menubar.add(fileMenu);
+ menubar.add(editMenu);
+ menubar.add(plafMenu);
+ setJMenuBar(menubar);
+ consoleTextArea = new ConsoleTextArea(args);
+ JScrollPane scroller = new JScrollPane(consoleTextArea);
+ setContentPane(scroller);
+ consoleTextArea.setRows(24);
+ consoleTextArea.setColumns(80);
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ System.exit(0);
+ }
+ });
+ pack();
+ setVisible(true);
+ // System.setIn(consoleTextArea.getIn());
+ // System.setOut(consoleTextArea.getOut());
+ // System.setErr(consoleTextArea.getErr());
+ Main.setIn(consoleTextArea.getIn());
+ Main.setOut(consoleTextArea.getOut());
+ Main.setErr(consoleTextArea.getErr());
+ Main.main(args);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ String plaf_name = null;
+ if(cmd.equals("Load")) {
+ String f = chooseFile();
+ if(f != null) {
+ f = f.replace('\\', '/');
+ consoleTextArea.eval("load(\"" + f + "\");");
+ }
+ } else if(cmd.equals("Exit")) {
+ System.exit(0);
+ } else if(cmd.equals("Cut")) {
+ consoleTextArea.cut();
+ } else if(cmd.equals("Copy")) {
+ consoleTextArea.copy();
+ } else if(cmd.equals("Paste")) {
+ consoleTextArea.paste();
+ } else {
+ if(cmd.equals("Metal")) {
+ plaf_name = "javax.swing.plaf.metal.MetalLookAndFeel";
+ } else if(cmd.equals("Windows")) {
+ plaf_name = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
+ } else if(cmd.equals("Motif")) {
+ plaf_name = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
+ }
+ if(plaf_name != null) {
+ try {
+ UIManager.setLookAndFeel(plaf_name);
+ SwingUtilities.updateComponentTreeUI(this);
+ consoleTextArea.postUpdateUI();
+ // updateComponentTreeUI seems to mess up the file
+ // chooser dialog, so just create a new one
+ createFileChooser();
+ } catch(Exception exc) {
+ JOptionPane.showMessageDialog(this,
+ exc.getMessage(),
+ "Platform",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/JavaPolicySecurity.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/JavaPolicySecurity.java
new file mode 100644
index 0000000..de39a5e
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/JavaPolicySecurity.java
@@ -0,0 +1,240 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.tools.shell;
+
+import java.security.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.mozilla.javascript.*;
+
+public class JavaPolicySecurity extends SecurityProxy
+{
+
+ public Class getStaticSecurityDomainClassInternal() {
+ return ProtectionDomain.class;
+ }
+
+ private static class Loader extends ClassLoader
+ implements GeneratedClassLoader
+ {
+ private ProtectionDomain domain;
+
+ Loader(ClassLoader parent, ProtectionDomain domain) {
+ super(parent != null ? parent : getSystemClassLoader());
+ this.domain = domain;
+ }
+
+ public Class defineClass(String name, byte[] data) {
+ return super.defineClass(name, data, 0, data.length, domain);
+ }
+
+ public void linkClass(Class cl) {
+ resolveClass(cl);
+ }
+ }
+
+ private static class ContextPermissions extends PermissionCollection
+ {
+ static final long serialVersionUID = -1721494496320750721L;
+
+// Construct PermissionCollection that permits an action only
+// if it is permitted by staticDomain and by security context of Java stack on
+// the moment of constructor invocation
+ ContextPermissions(ProtectionDomain staticDomain) {
+ _context = AccessController.getContext();
+ if (staticDomain != null) {
+ _statisPermissions = staticDomain.getPermissions();
+ }
+ setReadOnly();
+ }
+
+ public void add(Permission permission) {
+ throw new RuntimeException("NOT IMPLEMENTED");
+ }
+
+ public boolean implies(Permission permission) {
+ if (_statisPermissions != null) {
+ if (!_statisPermissions.implies(permission)) {
+ return false;
+ }
+ }
+ try {
+ _context.checkPermission(permission);
+ return true;
+ }catch (AccessControlException ex) {
+ return false;
+ }
+ }
+
+ public Enumeration elements()
+ {
+ return new Enumeration() {
+ public boolean hasMoreElements() { return false; }
+ public Object nextElement() { return null; }
+ };
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(getClass().getName());
+ sb.append('@');
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(" (context=");
+ sb.append(_context);
+ sb.append(", static_permitions=");
+ sb.append(_statisPermissions);
+ sb.append(')');
+ return sb.toString();
+ }
+
+ AccessControlContext _context;
+ PermissionCollection _statisPermissions;
+ }
+
+ public JavaPolicySecurity()
+ {
+ // To trigger error on jdk-1.1 with lazy load
+ new CodeSource(null, (java.security.cert.Certificate[])null);
+ }
+
+ protected void callProcessFileSecure(final Context cx,
+ final Scriptable scope,
+ final String filename)
+ {
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ URL url = getUrlObj(filename);
+ ProtectionDomain staticDomain = getUrlDomain(url);
+ Main.processFileSecure(cx, scope, url.toExternalForm(),
+ staticDomain);
+ return null;
+ }
+ });
+ }
+
+ private URL getUrlObj(String url)
+ {
+ URL urlObj;
+ try {
+ urlObj = new URL(url);
+ } catch (MalformedURLException ex) {
+ // Assume as Main.processFileSecure it is file, need to build its
+ // URL
+ String curDir = System.getProperty("user.dir");
+ curDir = curDir.replace('\\', '/');
+ if (!curDir.endsWith("/")) {
+ curDir = curDir+'/';
+ }
+ try {
+ URL curDirURL = new URL("file:"+curDir);
+ urlObj = new URL(curDirURL, url);
+ } catch (MalformedURLException ex2) {
+ throw new RuntimeException
+ ("Can not construct file URL for '"+url+"':"
+ +ex2.getMessage());
+ }
+ }
+ return urlObj;
+ }
+
+ private ProtectionDomain getUrlDomain(URL url)
+ {
+ CodeSource cs;
+ cs = new CodeSource(url, (java.security.cert.Certificate[])null);
+ PermissionCollection pc = Policy.getPolicy().getPermissions(cs);
+ return new ProtectionDomain(cs, pc);
+ }
+
+ public GeneratedClassLoader
+ createClassLoader(ClassLoader parentLoader, Object securityDomain)
+ {
+ ProtectionDomain domain = (ProtectionDomain)securityDomain;
+ return new Loader(parentLoader, domain);
+ }
+
+ public Object getDynamicSecurityDomain(Object securityDomain)
+ {
+ ProtectionDomain staticDomain = (ProtectionDomain)securityDomain;
+ return getDynamicDomain(staticDomain);
+ }
+
+ private ProtectionDomain getDynamicDomain(ProtectionDomain staticDomain) {
+ ContextPermissions p = new ContextPermissions(staticDomain);
+ ProtectionDomain contextDomain = new ProtectionDomain(null, p);
+ return contextDomain;
+ }
+
+ public Object callWithDomain(Object securityDomain,
+ final Context cx,
+ final Callable callable,
+ final Scriptable scope,
+ final Scriptable thisObj,
+ final Object[] args)
+ {
+ ProtectionDomain staticDomain = (ProtectionDomain)securityDomain;
+ // There is no direct way in Java to intersect permitions according
+ // stack context with additional domain.
+ // The following implementation first constructs ProtectionDomain
+ // that allows actions only allowed by both staticDomain and current
+ // stack context, and then constructs AccessController for this dynamic
+ // domain.
+ // If this is too slow, alternative solution would be to generate
+ // class per domain with a proxy method to call to infect
+ // java stack.
+ // Another optimization in case of scripts coming from "world" domain,
+ // that is having minimal default privileges is to construct
+ // one AccessControlContext based on ProtectionDomain
+ // with least possible privileges and simply call
+ // AccessController.doPrivileged with this untrusted context
+
+ ProtectionDomain dynamicDomain = getDynamicDomain(staticDomain);
+ ProtectionDomain[] tmp = { dynamicDomain };
+ AccessControlContext restricted = new AccessControlContext(tmp);
+
+ PrivilegedAction action = new PrivilegedAction() {
+ public Object run() {
+ return callable.call(cx, scope, thisObj, args);
+ }
+ };
+
+ return AccessController.doPrivileged(action, restricted);
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Main.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Main.java
new file mode 100644
index 0000000..9120892
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/Main.java
@@ -0,0 +1,638 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Patrick Beard
+ * Norris Boyd
+ * Igor Bukanov
+ * Rob Ginda
+ * Kurt Westerfeld
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.tools.shell;
+
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.MalformedURLException;
+import java.util.*;
+import org.mozilla.javascript.*;
+import org.mozilla.javascript.tools.ToolErrorReporter;
+
+/**
+ * The shell program.
+ *
+ * Can execute scripts interactively or in batch mode at the command line.
+ * An example of controlling the JavaScript engine.
+ *
+ * @author Norris Boyd
+ */
+public class Main
+{
+ public static ShellContextFactory
+ shellContextFactory = new ShellContextFactory();
+
+ public static Global global = new Global();
+ static protected ToolErrorReporter errorReporter;
+ static protected int exitCode = 0;
+ static private final int EXITCODE_RUNTIME_ERROR = 3;
+ static private final int EXITCODE_FILE_NOT_FOUND = 4;
+ static boolean processStdin = true;
+ static Vector fileList = new Vector(5);
+ private static SecurityProxy securityImpl;
+
+ static {
+ global.initQuitAction(new IProxy(IProxy.SYSTEM_EXIT));
+ }
+
+ /**
+ * Proxy class to avoid proliferation of anonymous classes.
+ */
+ private static class IProxy implements ContextAction, QuitAction
+ {
+ private static final int PROCESS_FILES = 1;
+ private static final int EVAL_INLINE_SCRIPT = 2;
+ private static final int SYSTEM_EXIT = 3;
+
+ private int type;
+ String[] args;
+ String scriptText;
+
+ IProxy(int type)
+ {
+ this.type = type;
+ }
+
+ public Object run(Context cx)
+ {
+ if (type == PROCESS_FILES) {
+ processFiles(cx, args);
+ } else if (type == EVAL_INLINE_SCRIPT) {
+ Script script = loadScriptFromSource(cx, scriptText,
+ "<command>", 1, null);
+ if (script != null) {
+ evaluateScript(script, cx, getGlobal());
+ }
+ } else {
+ throw Kit.codeBug();
+ }
+ return null;
+ }
+
+ public void quit(Context cx, int exitCode)
+ {
+ if (type == SYSTEM_EXIT) {
+ System.exit(exitCode);
+ return;
+ }
+ throw Kit.codeBug();
+ }
+ }
+
+ /**
+ * Main entry point.
+ *
+ * Process arguments as would a normal Java program. Also
+ * create a new Context and associate it with the current thread.
+ * Then set up the execution environment and begin to
+ * execute scripts.
+ */
+ public static void main(String args[]) {
+ try {
+ if (Boolean.getBoolean("rhino.use_java_policy_security")) {
+ initJavaPolicySecuritySupport();
+ }
+ } catch (SecurityException ex) {
+ ex.printStackTrace(System.err);
+ }
+
+ int result = exec(args);
+ if (result != 0) {
+ System.exit(result);
+ }
+ }
+
+ /**
+ * Execute the given arguments, but don't System.exit at the end.
+ */
+ public static int exec(String origArgs[])
+ {
+ errorReporter = new ToolErrorReporter(false, global.getErr());
+ shellContextFactory.setErrorReporter(errorReporter);
+ String[] args = processOptions(origArgs);
+ if (processStdin)
+ fileList.addElement(null);
+
+ if (!global.initialized) {
+ global.init(shellContextFactory);
+ }
+ IProxy iproxy = new IProxy(IProxy.PROCESS_FILES);
+ iproxy.args = args;
+ shellContextFactory.call(iproxy);
+
+ return exitCode;
+ }
+
+ static void processFiles(Context cx, String[] args)
+ {
+ // define "arguments" array in the top-level object:
+ // need to allocate new array since newArray requires instances
+ // of exactly Object[], not ObjectSubclass[]
+ Object[] array = new Object[args.length];
+ System.arraycopy(args, 0, array, 0, args.length);
+ Scriptable argsObj = cx.newArray(global, array);
+ global.defineProperty("arguments", argsObj,
+ ScriptableObject.DONTENUM);
+
+ for (int i=0; i < fileList.size(); i++) {
+ processSource(cx, (String) fileList.elementAt(i));
+ }
+
+ }
+
+ public static Global getGlobal()
+ {
+ return global;
+ }
+
+ /**
+ * Parse arguments.
+ */
+ public static String[] processOptions(String args[])
+ {
+ String usageError;
+ goodUsage: for (int i = 0; ; ++i) {
+ if (i == args.length) {
+ return new String[0];
+ }
+ String arg = args[i];
+ if (!arg.startsWith("-")) {
+ processStdin = false;
+ fileList.addElement(arg);
+ String[] result = new String[args.length - i - 1];
+ System.arraycopy(args, i+1, result, 0, args.length - i - 1);
+ return result;
+ }
+ if (arg.equals("-version")) {
+ if (++i == args.length) {
+ usageError = arg;
+ break goodUsage;
+ }
+ int version;
+ try {
+ version = Integer.parseInt(args[i]);
+ } catch (NumberFormatException ex) {
+ usageError = args[i];
+ break goodUsage;
+ }
+ if (!Context.isValidLanguageVersion(version)) {
+ usageError = args[i];
+ break goodUsage;
+ }
+ shellContextFactory.setLanguageVersion(version);
+ continue;
+ }
+ if (arg.equals("-opt") || arg.equals("-O")) {
+ if (++i == args.length) {
+ usageError = arg;
+ break goodUsage;
+ }
+ int opt;
+ try {
+ opt = Integer.parseInt(args[i]);
+ } catch (NumberFormatException ex) {
+ usageError = args[i];
+ break goodUsage;
+ }
+ if (opt == -2) {
+ // Compatibility with Cocoon Rhino fork
+ opt = -1;
+ } else if (!Context.isValidOptimizationLevel(opt)) {
+ usageError = args[i];
+ break goodUsage;
+ }
+ shellContextFactory.setOptimizationLevel(opt);
+ continue;
+ }
+ if (arg.equals("-strict")) {
+ shellContextFactory.setStrictMode(true);
+ errorReporter.setIsReportingWarnings(true);
+ continue;
+ }
+ if (arg.equals("-fatal-warnings")) {
+ shellContextFactory.setWarningAsError(true);
+ continue;
+ }
+ if (arg.equals("-e")) {
+ processStdin = false;
+ if (++i == args.length) {
+ usageError = arg;
+ break goodUsage;
+ }
+ if (!global.initialized) {
+ global.init(shellContextFactory);
+ }
+ IProxy iproxy = new IProxy(IProxy.EVAL_INLINE_SCRIPT);
+ iproxy.scriptText = args[i];
+ shellContextFactory.call(iproxy);
+ continue;
+ }
+ if (arg.equals("-w")) {
+ errorReporter.setIsReportingWarnings(true);
+ continue;
+ }
+ if (arg.equals("-f")) {
+ processStdin = false;
+ if (++i == args.length) {
+ usageError = arg;
+ break goodUsage;
+ }
+ fileList.addElement(args[i].equals("-") ? null : args[i]);
+ continue;
+ }
+ if (arg.equals("-sealedlib")) {
+ global.setSealedStdLib(true);
+ continue;
+ }
+ if (arg.equals("-debug")) {
+ shellContextFactory.setGeneratingDebug(true);
+ continue;
+ }
+ if (arg.equals("-?") ||
+ arg.equals("-help")) {
+ // print usage message
+ global.getOut().println(
+ ToolErrorReporter.getMessage("msg.shell.usage", Main.class.getName()));
+ System.exit(1);
+ }
+ usageError = arg;
+ break goodUsage;
+ }
+ // print error and usage message
+ global.getOut().println(
+ ToolErrorReporter.getMessage("msg.shell.invalid", usageError));
+ global.getOut().println(
+ ToolErrorReporter.getMessage("msg.shell.usage", Main.class.getName()));
+ System.exit(1);
+ return null;
+ }
+
+ private static void initJavaPolicySecuritySupport()
+ {
+ Throwable exObj;
+ try {
+ Class cl = Class.forName
+ ("org.mozilla.javascript.tools.shell.JavaPolicySecurity");
+ securityImpl = (SecurityProxy)cl.newInstance();
+ SecurityController.initGlobal(securityImpl);
+ return;
+ } catch (ClassNotFoundException ex) {
+ exObj = ex;
+ } catch (IllegalAccessException ex) {
+ exObj = ex;
+ } catch (InstantiationException ex) {
+ exObj = ex;
+ } catch (LinkageError ex) {
+ exObj = ex;
+ }
+ throw Kit.initCause(new IllegalStateException(
+ "Can not load security support: "+exObj), exObj);
+ }
+
+ /**
+ * Evaluate JavaScript source.
+ *
+ * @param cx the current context
+ * @param filename the name of the file to compile, or null
+ * for interactive mode.
+ */
+ public static void processSource(Context cx, String filename)
+ {
+ if (filename == null || filename.equals("-")) {
+ PrintStream ps = global.getErr();
+ if (filename == null) {
+ // print implementation version
+ ps.println(cx.getImplementationVersion());
+ }
+
+ // Use the interpreter for interactive input
+ cx.setOptimizationLevel(-1);
+
+ BufferedReader in = new BufferedReader
+ (new InputStreamReader(global.getIn()));
+ int lineno = 1;
+ boolean hitEOF = false;
+ while (!hitEOF) {
+ String[] prompts = global.getPrompts(cx);
+ if (filename == null)
+ ps.print(prompts[0]);
+ ps.flush();
+ String source = "";
+
+ // Collect lines of source to compile.
+ while (true) {
+ String newline;
+ try {
+ newline = in.readLine();
+ }
+ catch (IOException ioe) {
+ ps.println(ioe.toString());
+ break;
+ }
+ if (newline == null) {
+ hitEOF = true;
+ break;
+ }
+ source = source + newline + "\n";
+ lineno++;
+ if (cx.stringIsCompilableUnit(source))
+ break;
+ ps.print(prompts[1]);
+ }
+ Script script = loadScriptFromSource(cx, source, "<stdin>",
+ lineno, null);
+ if (script != null) {
+ Object result = evaluateScript(script, cx, global);
+ // Avoid printing out undefined or function definitions.
+ if (result != Context.getUndefinedValue() &&
+ !(result instanceof Function &&
+ source.trim().startsWith("function")))
+ {
+ try {
+ ps.println(Context.toString(result));
+ } catch (RhinoException rex) {
+ ToolErrorReporter.reportException(
+ cx.getErrorReporter(), rex);
+ }
+ }
+ NativeArray h = global.history;
+ h.put((int)h.getLength(), h, source);
+ }
+ }
+ ps.println();
+ } else {
+ processFile(cx, global, filename);
+ }
+ System.gc();
+ }
+
+ public static void processFile(Context cx, Scriptable scope,
+ String filename)
+ {
+ if (securityImpl == null) {
+ processFileSecure(cx, scope, filename, null);
+ } else {
+ securityImpl.callProcessFileSecure(cx, scope, filename);
+ }
+ }
+
+ static void processFileSecure(Context cx, Scriptable scope,
+ String path, Object securityDomain)
+ {
+ Script script;
+ if (path.endsWith(".class")) {
+ script = loadCompiledScript(cx, path, securityDomain);
+ } else {
+ String source = (String)readFileOrUrl(path, true);
+ if (source == null) {
+ exitCode = EXITCODE_FILE_NOT_FOUND;
+ return;
+ }
+
+ // Support the executable script #! syntax: If
+ // the first line begins with a '#', treat the whole
+ // line as a comment.
+ if (source.length() > 0 && source.charAt(0) == '#') {
+ for (int i = 1; i != source.length(); ++i) {
+ int c = source.charAt(i);
+ if (c == '\n' || c == '\r') {
+ source = source.substring(i);
+ break;
+ }
+ }
+ }
+ script = loadScriptFromSource(cx, source, path, 1, securityDomain);
+ }
+ if (script != null) {
+ evaluateScript(script, cx, scope);
+ }
+ }
+
+ public static Script loadScriptFromSource(Context cx, String scriptSource,
+ String path, int lineno,
+ Object securityDomain)
+ {
+ try {
+ return cx.compileString(scriptSource, path, lineno,
+ securityDomain);
+ } catch (EvaluatorException ee) {
+ // Already printed message.
+ exitCode = EXITCODE_RUNTIME_ERROR;
+ } catch (RhinoException rex) {
+ ToolErrorReporter.reportException(
+ cx.getErrorReporter(), rex);
+ exitCode = EXITCODE_RUNTIME_ERROR;
+ } catch (VirtualMachineError ex) {
+ // Treat StackOverflow and OutOfMemory as runtime errors
+ ex.printStackTrace();
+ String msg = ToolErrorReporter.getMessage(
+ "msg.uncaughtJSException", ex.toString());
+ exitCode = EXITCODE_RUNTIME_ERROR;
+ Context.reportError(msg);
+ }
+ return null;
+ }
+
+ private static Script loadCompiledScript(Context cx, String path,
+ Object securityDomain)
+ {
+ byte[] data = (byte[])readFileOrUrl(path, false);
+ if (data == null) {
+ exitCode = EXITCODE_FILE_NOT_FOUND;
+ return null;
+ }
+ // XXX: For now extract class name of compiled Script from path
+ // instead of parsing class bytes
+ int nameStart = path.lastIndexOf('/');
+ if (nameStart < 0) {
+ nameStart = 0;
+ } else {
+ ++nameStart;
+ }
+ int nameEnd = path.lastIndexOf('.');
+ if (nameEnd < nameStart) {
+ // '.' does not exist in path (nameEnd < 0)
+ // or it comes before nameStart
+ nameEnd = path.length();
+ }
+ String name = path.substring(nameStart, nameEnd);
+ try {
+ GeneratedClassLoader loader = SecurityController.createLoader(cx.getApplicationClassLoader(), securityDomain);
+ Class clazz = loader.defineClass(name, data);
+ loader.linkClass(clazz);
+ if (!Script.class.isAssignableFrom(clazz)) {
+ throw Context.reportRuntimeError("msg.must.implement.Script");
+ }
+ return (Script) clazz.newInstance();
+ } catch (RhinoException rex) {
+ ToolErrorReporter.reportException(
+ cx.getErrorReporter(), rex);
+ exitCode = EXITCODE_RUNTIME_ERROR;
+ } catch (IllegalAccessException iaex) {
+ exitCode = EXITCODE_RUNTIME_ERROR;
+ Context.reportError(iaex.toString());
+ } catch (InstantiationException inex) {
+ exitCode = EXITCODE_RUNTIME_ERROR;
+ Context.reportError(inex.toString());
+ }
+ return null;
+ }
+
+ public static Object evaluateScript(Script script, Context cx,
+ Scriptable scope)
+ {
+ try {
+ return script.exec(cx, scope);
+ } catch (RhinoException rex) {
+ ToolErrorReporter.reportException(
+ cx.getErrorReporter(), rex);
+ exitCode = EXITCODE_RUNTIME_ERROR;
+ } catch (VirtualMachineError ex) {
+ // Treat StackOverflow and OutOfMemory as runtime errors
+ ex.printStackTrace();
+ String msg = ToolErrorReporter.getMessage(
+ "msg.uncaughtJSException", ex.toString());
+ exitCode = EXITCODE_RUNTIME_ERROR;
+ Context.reportError(msg);
+ }
+ return Context.getUndefinedValue();
+ }
+
+ public static InputStream getIn() {
+ return getGlobal().getIn();
+ }
+
+ public static void setIn(InputStream in) {
+ getGlobal().setIn(in);
+ }
+
+ public static PrintStream getOut() {
+ return getGlobal().getOut();
+ }
+
+ public static void setOut(PrintStream out) {
+ getGlobal().setOut(out);
+ }
+
+ public static PrintStream getErr() {
+ return getGlobal().getErr();
+ }
+
+ public static void setErr(PrintStream err) {
+ getGlobal().setErr(err);
+ }
+
+ /**
+ * Read file or url specified by <tt>path</tt>.
+ * @return file or url content as <tt>byte[]</tt> or as <tt>String</tt> if
+ * <tt>convertToString</tt> is true.
+ */
+ private static Object readFileOrUrl(String path, boolean convertToString)
+ {
+ URL url = null;
+ // Assume path is URL if it contains dot and there are at least
+ // 2 characters in the protocol part. The later allows under Windows
+ // to interpret paths with driver letter as file, not URL.
+ if (path.indexOf(':') >= 2) {
+ try {
+ url = new URL(path);
+ } catch (MalformedURLException ex) {
+ }
+ }
+
+ InputStream is = null;
+ int capacityHint = 0;
+ if (url == null) {
+ File file = new File(path);
+ capacityHint = (int)file.length();
+ try {
+ is = new FileInputStream(file);
+ } catch (IOException ex) {
+ Context.reportError(ToolErrorReporter.getMessage(
+ "msg.couldnt.open", path));
+ return null;
+ }
+ } else {
+ try {
+ URLConnection uc = url.openConnection();
+ is = uc.getInputStream();
+ capacityHint = uc.getContentLength();
+ // Ignore insane values for Content-Length
+ if (capacityHint > (1 << 20)) {
+ capacityHint = -1;
+ }
+ } catch (IOException ex) {
+ Context.reportError(ToolErrorReporter.getMessage(
+ "msg.couldnt.open.url", url.toString(), ex.toString()));
+ return null;
+ }
+ }
+ if (capacityHint <= 0) {
+ capacityHint = 4096;
+ }
+
+ byte[] data;
+ try {
+ try {
+ data = Kit.readStream(is, capacityHint);
+ } finally {
+ is.close();
+ }
+ } catch (IOException ex) {
+ Context.reportError(ex.toString());
+ return null;
+ }
+
+ Object result;
+ if (!convertToString) {
+ result = data;
+ } else {
+ // Convert to String using the default encoding
+ // XXX: Use 'charset=' argument of Content-Type if URL?
+ result = new String(data);
+ }
+ return result;
+ }
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/QuitAction.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/QuitAction.java
new file mode 100644
index 0000000..dcad90e
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/QuitAction.java
@@ -0,0 +1,50 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.tools.shell;
+
+import org.mozilla.javascript.Context;
+
+/**
+ * Defines action to perform in response to quit command.
+ */
+public interface QuitAction
+{
+ public void quit(Context cx, int exitCode);
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/SecurityProxy.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/SecurityProxy.java
new file mode 100644
index 0000000..8f029ea
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/SecurityProxy.java
@@ -0,0 +1,48 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.tools.shell;
+
+import org.mozilla.javascript.*;
+
+public abstract class SecurityProxy extends SecurityController
+{
+ protected abstract void callProcessFileSecure(Context cx, Scriptable scope,
+ String filename);
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/ShellContextFactory.java b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/ShellContextFactory.java
new file mode 100644
index 0000000..ba7e62c
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/toolsrc/org/mozilla/javascript/tools/shell/ShellContextFactory.java
@@ -0,0 +1,114 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ * Bob Jervis
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.tools.shell;
+
+import org.mozilla.javascript.*;
+
+public class ShellContextFactory extends ContextFactory
+{
+ private boolean strictMode;
+ private boolean warningAsError;
+ private int languageVersion;
+ private int optimizationLevel;
+ private boolean generatingDebug;
+ private ErrorReporter errorReporter;
+
+ protected boolean hasFeature(Context cx, int featureIndex)
+ {
+ switch (featureIndex) {
+ case Context.FEATURE_STRICT_VARS:
+ case Context.FEATURE_STRICT_EVAL:
+ case Context.FEATURE_STRICT_MODE:
+ return strictMode;
+
+ case Context.FEATURE_WARNING_AS_ERROR:
+ return warningAsError;
+ }
+ return super.hasFeature(cx, featureIndex);
+ }
+
+ protected void onContextCreated(Context cx)
+ {
+ cx.setLanguageVersion(languageVersion);
+ cx.setOptimizationLevel(optimizationLevel);
+ if (errorReporter != null) {
+ cx.setErrorReporter(errorReporter);
+ }
+ cx.setGeneratingDebug(generatingDebug);
+ super.onContextCreated(cx);
+ }
+
+ public void setStrictMode(boolean flag)
+ {
+ checkNotSealed();
+ this.strictMode = flag;
+ }
+
+ public void setWarningAsError(boolean flag)
+ {
+ checkNotSealed();
+ this.warningAsError = flag;
+ }
+
+ public void setLanguageVersion(int version)
+ {
+ Context.checkLanguageVersion(version);
+ checkNotSealed();
+ this.languageVersion = version;
+ }
+
+ public void setOptimizationLevel(int optimizationLevel)
+ {
+ Context.checkOptimizationLevel(optimizationLevel);
+ checkNotSealed();
+ this.optimizationLevel = optimizationLevel;
+ }
+
+ public void setErrorReporter(ErrorReporter errorReporter)
+ {
+ if (errorReporter == null) throw new IllegalArgumentException();
+ this.errorReporter = errorReporter;
+ }
+
+ public void setGeneratingDebug(boolean generatingDebug)
+ {
+ this.generatingDebug = generatingDebug;
+ }
+}