2002/03/21 01:19:33
authormegacz <megacz@xwt.org>
Fri, 30 Jan 2004 06:45:07 +0000 (06:45 +0000)
committermegacz <megacz@xwt.org>
Fri, 30 Jan 2004 06:45:07 +0000 (06:45 +0000)
darcs-hash:20040130064507-2ba56-7d21fb55b62acf96e73e8e49eb17fa8f14b05b87.gz

112 files changed:
src/org/mozilla/javascript/ErrorReporter.java [new file with mode: 0644]
src/org/mozilla/javascript/EvaluatorException.java [new file with mode: 0644]
src/org/mozilla/javascript/FlattenedObject.java [new file with mode: 0644]
src/org/mozilla/javascript/Function.java [new file with mode: 0644]
src/org/mozilla/javascript/FunctionNode.java [new file with mode: 0644]
src/org/mozilla/javascript/FunctionObject.java [new file with mode: 0644]
src/org/mozilla/javascript/IRFactory.java [new file with mode: 0644]
src/org/mozilla/javascript/IdFunction.java [new file with mode: 0644]
src/org/mozilla/javascript/IdFunctionMaster.java [new file with mode: 0644]
src/org/mozilla/javascript/IdScriptable.java [new file with mode: 0644]
src/org/mozilla/javascript/ImporterTopLevel.java [new file with mode: 0644]
src/org/mozilla/javascript/InterpretedFunction.java [new file with mode: 0644]
src/org/mozilla/javascript/InterpretedScript.java [new file with mode: 0644]
src/org/mozilla/javascript/Interpreter.java [new file with mode: 0644]
src/org/mozilla/javascript/InterpreterData.java [new file with mode: 0644]
src/org/mozilla/javascript/InterpreterFrame.java [new file with mode: 0644]
src/org/mozilla/javascript/Invoker.java [new file with mode: 0644]
src/org/mozilla/javascript/JavaMembers.java [new file with mode: 0644]
src/org/mozilla/javascript/JavaScriptException.java [new file with mode: 0644]
src/org/mozilla/javascript/Label.java [new file with mode: 0644]
src/org/mozilla/javascript/LabelTable.java [new file with mode: 0644]
src/org/mozilla/javascript/LazilyLoadedCtor.java [new file with mode: 0644]
src/org/mozilla/javascript/LineBuffer.java [new file with mode: 0644]
src/org/mozilla/javascript/ListenerArray.java [new file with mode: 0644]
src/org/mozilla/javascript/LocalVariable.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeArray.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeBoolean.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeCall.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeDate.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeError.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeFunction.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeGlobal.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeJavaArray.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeJavaClass.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeJavaConstructor.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeJavaMethod.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeJavaObject.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeJavaPackage.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeMath.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeNumber.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeObject.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeScript.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeString.java [new file with mode: 0644]
src/org/mozilla/javascript/NativeWith.java [new file with mode: 0644]
src/org/mozilla/javascript/Node.java [new file with mode: 0644]
src/org/mozilla/javascript/NodeTransformer.java [new file with mode: 0644]
src/org/mozilla/javascript/NotAFunctionException.java [new file with mode: 0644]
src/org/mozilla/javascript/Parser.java [new file with mode: 0644]
src/org/mozilla/javascript/PreorderNodeIterator.java [new file with mode: 0644]
src/org/mozilla/javascript/PropertyException.java [new file with mode: 0644]
src/org/mozilla/javascript/RegExpProxy.java [new file with mode: 0644]
src/org/mozilla/javascript/Script.java [new file with mode: 0644]
src/org/mozilla/javascript/ScriptRuntime.java [new file with mode: 0644]
src/org/mozilla/javascript/Scriptable.java [new file with mode: 0644]
src/org/mozilla/javascript/ScriptableObject.java [new file with mode: 0644]
src/org/mozilla/javascript/SecuritySupport.java [new file with mode: 0644]
src/org/mozilla/javascript/ShallowNodeIterator.java [new file with mode: 0644]
src/org/mozilla/javascript/SourceTextItem.java [new file with mode: 0644]
src/org/mozilla/javascript/SourceTextManager.java [new file with mode: 0644]
src/org/mozilla/javascript/Synchronizer.java [new file with mode: 0644]
src/org/mozilla/javascript/TokenStream.java [new file with mode: 0644]
src/org/mozilla/javascript/UintMap.java [new file with mode: 0644]
src/org/mozilla/javascript/Undefined.java [new file with mode: 0644]
src/org/mozilla/javascript/VariableTable.java [new file with mode: 0644]
src/org/mozilla/javascript/WrapHandler.java [new file with mode: 0644]
src/org/mozilla/javascript/WrappedException.java [new file with mode: 0644]
src/org/mozilla/javascript/Wrapper.java [new file with mode: 0644]
src/org/mozilla/javascript/debug/DebugFrame.java [new file with mode: 0644]
src/org/mozilla/javascript/debug/DebugReader.java [new file with mode: 0644]
src/org/mozilla/javascript/debug/DebuggableEngine.java [new file with mode: 0644]
src/org/mozilla/javascript/debug/DebuggableScript.java [new file with mode: 0644]
src/org/mozilla/javascript/debug/Debugger.java [new file with mode: 0644]
src/org/mozilla/javascript/regexp/NativeRegExp.java [new file with mode: 0644]
src/org/mozilla/javascript/regexp/NativeRegExpCtor.java [new file with mode: 0644]
src/org/mozilla/javascript/regexp/RegExpImpl.java [new file with mode: 0644]
src/org/mozilla/javascript/regexp/SubString.java [new file with mode: 0644]
src/org/xwt/Box.java [new file with mode: 0644]
src/org/xwt/DoubleBuffer.java [new file with mode: 0644]
src/org/xwt/GIF.java [new file with mode: 0644]
src/org/xwt/ImageDecoder.java [new file with mode: 0644]
src/org/xwt/Main.java [new file with mode: 0644]
src/org/xwt/Message.java [new file with mode: 0644]
src/org/xwt/MessageQueue.java [new file with mode: 0644]
src/org/xwt/PNG.java [new file with mode: 0644]
src/org/xwt/Picture.java [new file with mode: 0644]
src/org/xwt/Platform.java [new file with mode: 0644]
src/org/xwt/Resources.java [new file with mode: 0644]
src/org/xwt/SOAP.java [new file with mode: 0644]
src/org/xwt/SpecialBoxProperty.java [new file with mode: 0644]
src/org/xwt/Static.java [new file with mode: 0644]
src/org/xwt/Surface.java [new file with mode: 0644]
src/org/xwt/Template.java [new file with mode: 0644]
src/org/xwt/ThreadMessage.java [new file with mode: 0644]
src/org/xwt/TinySSL.java [new file with mode: 0644]
src/org/xwt/Trap.java [new file with mode: 0644]
src/org/xwt/Weak.java [new file with mode: 0644]
src/org/xwt/XML.java [new file with mode: 0644]
src/org/xwt/XMLRPC.java [new file with mode: 0644]
src/org/xwt/XWF.java [new file with mode: 0644]
src/org/xwt/XWT.java [new file with mode: 0644]
src/org/xwt/package.html [new file with mode: 0644]
src/org/xwt/plat/AWT.java [new file with mode: 0644]
src/org/xwt/plat/GCJ.java [new file with mode: 0644]
src/org/xwt/plat/GCJ.xml [new file with mode: 0644]
src/org/xwt/plat/Java2.java [new file with mode: 0644]
src/org/xwt/plat/Java2.xml [new file with mode: 0644]
src/org/xwt/plat/Win32.cc [new file with mode: 0644]
src/org/xwt/plat/Win32.def [new file with mode: 0644]
src/org/xwt/plat/Win32.inf [new file with mode: 0644]
src/org/xwt/plat/Win32.java [new file with mode: 0644]
src/org/xwt/plat/Win32.xml [new file with mode: 0644]
src/org/xwt/plat/package.html [new file with mode: 0644]

diff --git a/src/org/mozilla/javascript/ErrorReporter.java b/src/org/mozilla/javascript/ErrorReporter.java
new file mode 100644 (file)
index 0000000..b945e7e
--- /dev/null
@@ -0,0 +1,103 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This is interface defines a protocol for the reporting of\r
+ * errors during JavaScript translation or execution.\r
+ *\r
+ * @author Norris Boyd\r
+ */\r
+\r
+public interface ErrorReporter {\r
+\r
+    /**\r
+     * Report a warning.\r
+     *\r
+     * The implementing class may choose to ignore the warning\r
+     * if it desires.\r
+     *\r
+     * @param message a String describing the warning\r
+     * @param sourceName a String describing the JavaScript source\r
+     * where the warning occured; typically a filename or URL\r
+     * @param line the line number associated with the warning\r
+     * @param lineSource the text of the line (may be null)\r
+     * @param lineOffset the offset into lineSource where problem was detected\r
+     */\r
+    void warning(String message, String sourceName, int line,\r
+                 String lineSource, int lineOffset);\r
+\r
+    /**\r
+     * Report an error.\r
+     *\r
+     * The implementing class is free to throw an exception if\r
+     * it desires.\r
+     *\r
+     * If execution has not yet begun, the JavaScript engine is\r
+     * free to find additional errors rather than terminating\r
+     * the translation. It will not execute a script that had\r
+     * errors, however.\r
+     *\r
+     * @param message a String describing the error\r
+     * @param sourceName a String describing the JavaScript source\r
+     * where the error occured; typically a filename or URL\r
+     * @param line the line number associated with the error\r
+     * @param lineSource the text of the line (may be null)\r
+     * @param lineOffset the offset into lineSource where problem was detected\r
+     */\r
+    void error(String message, String sourceName, int line,\r
+               String lineSource, int lineOffset);\r
+\r
+    /**\r
+     * Creates an EvaluatorException that may be thrown.\r
+     *\r
+     * runtimeErrors, unlike errors, will always terminate the\r
+     * current script.\r
+     *\r
+     * @param message a String describing the error\r
+     * @param sourceName a String describing the JavaScript source\r
+     * where the error occured; typically a filename or URL\r
+     * @param line the line number associated with the error\r
+     * @param lineSource the text of the line (may be null)\r
+     * @param lineOffset the offset into lineSource where problem was detected\r
+     * @return an EvaluatorException that will be thrown.\r
+     */\r
+    EvaluatorException runtimeError(String message, String sourceName, \r
+                                    int line, String lineSource, \r
+                                    int lineOffset);\r
+}\r
diff --git a/src/org/mozilla/javascript/EvaluatorException.java b/src/org/mozilla/javascript/EvaluatorException.java
new file mode 100644 (file)
index 0000000..5f31201
--- /dev/null
@@ -0,0 +1,56 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * The class of exceptions thrown by the JavaScript engine.\r
+ */\r
+public class EvaluatorException extends RuntimeException {\r
+\r
+    /**\r
+     * Create an exception with the specified detail message.\r
+     *\r
+     * Errors internal to the JavaScript engine will simply throw a\r
+     * RuntimeException.\r
+     *\r
+     * @param detail a message with detail about the exception\r
+     */\r
+    public EvaluatorException(String detail) {\r
+        super(detail);\r
+    }\r
+\r
+}\r
diff --git a/src/org/mozilla/javascript/FlattenedObject.java b/src/org/mozilla/javascript/FlattenedObject.java
new file mode 100644 (file)
index 0000000..85c5f0b
--- /dev/null
@@ -0,0 +1,341 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Roger Lawrence\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.Hashtable;\r
+import java.util.Enumeration;\r
+\r
+/**\r
+ * Manipulate a Scriptable object as if its prototype chain were flattened.\r
+ * <p>\r
+ * This class has been deprecated in favor of the static methods \r
+ * <code>getProperty</code>, <code>putProperty</code>, and \r
+ * <code>deleteProperty</code> of ScripableObject. Those methods provide the\r
+ * same functionality without the confusing and inefficient need to construct\r
+ * a new object instance.\r
+ *\r
+ * @see org.mozilla.javascript.ScriptableObject\r
+ * @deprecated\r
+ * @author Norris Boyd\r
+ */\r
+\r
+public class FlattenedObject {\r
+\r
+    /**\r
+     * Construct a new FlattenedObject.\r
+     *\r
+     * @param object the object to be viewed with flattened properties\r
+     * @deprecated\r
+     */\r
+    public FlattenedObject(Scriptable object) {\r
+        this.obj = object;\r
+    }\r
+\r
+    /**\r
+     * Get the associated Scriptable object.\r
+     * @deprecated\r
+     */\r
+    public Scriptable getObject() {\r
+        return obj;\r
+    }\r
+\r
+    /**\r
+     * Determine if a property exists in an object.\r
+     *\r
+     * This is a more convenient (and less efficient) form than\r
+     * <code>Scriptable.has()</code>.\r
+     * It returns true if and only if the property\r
+     * exists in this object or any of the objects in its prototype\r
+     * chain.\r
+     *\r
+     * @param id the property index, which may be either a String or a\r
+     *           Number\r
+     * @return true if and only if the property exists in the prototype\r
+     *         chain\r
+     * @see org.mozilla.javascript.Scriptable#has\r
+     * @deprecated As of 1.5R2, replaced by ScriptableObject.getProperty\r
+     */\r
+    public boolean hasProperty(Object id) {\r
+        String stringId = ScriptRuntime.toString(id);\r
+        String s = ScriptRuntime.getStringId(stringId);\r
+        if (s == null)\r
+            return getBase(obj, ScriptRuntime.getIntId(stringId)) != null;\r
+        return getBase(obj, s) != null;\r
+    }\r
+\r
+    /**\r
+     * Get a property of an object.\r
+     * <p>\r
+     * This is a more convenient (and less efficient) form than\r
+     * <code>Scriptable.get()</code>. It corresponds exactly to the\r
+     * expression <code>obj[id]</code> in JavaScript. This method\r
+     * will traverse the prototype chain of an object to find the\r
+     * property.<p>\r
+     *\r
+     * If the property does not exist in the object or its prototype\r
+     * chain, the undefined value will be returned.\r
+     *\r
+     * @param id the property index; can be a String or a Number; the\r
+     *           String may contain characters representing a number\r
+     * @return the value of the property or the undefined value\r
+     * @see org.mozilla.javascript.Scriptable#get\r
+     * @see org.mozilla.javascript.Context#getUndefinedValue\r
+     * @deprecated As of 1.5R2, replaced by ScriptableObject.getProperty\r
+     */\r
+    public Object getProperty(Object id) {\r
+        String s = ScriptRuntime.getStringId(id);\r
+        int index = s == null ? ScriptRuntime.getIntId(id) : 0;\r
+        Scriptable m = obj;\r
+        Object result;\r
+        for(;;) {\r
+            result = s == null ? m.get(index, obj) : m.get(s, obj);\r
+            if (result != Scriptable.NOT_FOUND)\r
+                break;\r
+            m = m.getPrototype();\r
+            if (m == null)\r
+                return Undefined.instance;\r
+        }\r
+        if (result instanceof Scriptable)\r
+            return new FlattenedObject((Scriptable) result);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Set a property of an object.\r
+     *\r
+     * This is a more convenient (and less efficient) form than that\r
+     * provided in Scriptable. It corresponds exactly to the\r
+     * expression <code>obj[id] = val</code> in JavaScript.<p>\r
+     *\r
+     * @param id the property index, which may be either a String or\r
+     *           a Number\r
+     * @param value the value of the property\r
+     * @see org.mozilla.javascript.Scriptable#put\r
+     * @deprecated As of 1.5R2, replaced by ScriptableObject.putProperty\r
+     */\r
+    public void putProperty(Object id, Object value) {\r
+        String s = ScriptRuntime.getStringId(id);\r
+        if (value instanceof FlattenedObject)\r
+            value = ((FlattenedObject) value).getObject();\r
+        Scriptable x;\r
+        if (s == null) {\r
+            int index = ScriptRuntime.getIntId(id);\r
+            x = getBase(obj, index);\r
+            if (x == null)\r
+                x = obj;\r
+            x.put(index, obj, value);\r
+            return;\r
+        }\r
+        x = getBase(obj, s);\r
+        if (x == null)\r
+            x = obj;\r
+        x.put(s, obj, value);\r
+    }\r
+\r
+    /**\r
+     * Remove a property.\r
+     *\r
+     * This method provides the functionality of the <code>delete</code>\r
+     * operator in JavaScript.\r
+     *\r
+     * @param id the property index, which may be either a String or\r
+     *           a Number\r
+     * @return true if the property didn't exist, or existed and was removed\r
+     * @see org.mozilla.javascript.Scriptable#delete\r
+     * @deprecated as of 1.5R2, replaced by ScriptableObject.deleteProperty\r
+     */\r
+    public boolean deleteProperty(Object id) {\r
+        String s = ScriptRuntime.getStringId(id);\r
+        if (s == null) {\r
+            int index = ScriptRuntime.getIntId(id);\r
+            Scriptable base = getBase(obj, index);\r
+            if (base == null)\r
+                return true;\r
+            base.delete(index);\r
+            return !base.has(index, base);\r
+        }\r
+        Scriptable base = getBase(obj, s);\r
+        if (base == null)\r
+            return true;\r
+        base.delete(s);\r
+        return !base.has(s, base);\r
+    }\r
+\r
+    /**\r
+     * Return an array that contains the ids of the properties.\r
+     *\r
+     * <p>This method will walk the prototype chain and collect the\r
+     * ids of all objects in the prototype chain.<p>\r
+     *\r
+     * If an id appears in more than one object in the prototype chain,\r
+     * it will only be in the array once. (So all the entries in the\r
+     * array will be unique respective to equals().)\r
+     *\r
+     * @see org.mozilla.javascript.Scriptable#getIds\r
+     * @deprecated\r
+     */\r
+    public Object[] getIds() {\r
+        Hashtable h = new Hashtable(11);\r
+        Scriptable m = obj;\r
+        while (m != null) {\r
+            Object[] e = m.getIds();\r
+            for (int i=0; i < e.length; i++) {\r
+                h.put(e[i], Boolean.TRUE);\r
+            }\r
+            m = m.getPrototype();\r
+        }\r
+        Enumeration keys = h.keys();\r
+        Object elem;\r
+        Object[] result = new Object[h.size()];\r
+        int index = 0;\r
+        while (keys.hasMoreElements()) {\r
+            elem = keys.nextElement();\r
+            result[index++] = elem;\r
+        }\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Consider this object to be a function, and call it.\r
+     *\r
+     * @param cx the current Context for this thread\r
+     * @param thisObj the JavaScript 'this' for the call\r
+     * @param args the arguments for the call\r
+     * @return the result of the JavaScript function call\r
+     * @exception NotAFunctionException if this object is not a function\r
+     * @exception JavaScriptException if an uncaught JavaScript exception\r
+     *            occurred while executing the function\r
+     * @see org.mozilla.javascript.Function#call\r
+     * @deprecated\r
+     */\r
+    public Object call(Context cx, Scriptable thisObj, Object[] args)\r
+        throws NotAFunctionException,\r
+               JavaScriptException\r
+    {\r
+        if (!(obj instanceof Function)) {\r
+            throw new NotAFunctionException();\r
+        }\r
+        return ScriptRuntime.call(cx, obj, thisObj, args, (Function) obj);\r
+    }\r
+\r
+    /**\r
+     * Consider this object to be a function, and invoke it as a\r
+     * constructor call.\r
+     *\r
+     * @param cx the current Context for this thread\r
+     * @param args the arguments for the constructor call\r
+     * @return the allocated object\r
+     * @exception NotAFunctionException if this object is not a function\r
+     * @exception JavaScriptException if an uncaught JavaScript exception\r
+     *            occurred while executing the constructor\r
+     * @see org.mozilla.javascript.Function#construct\r
+     * @deprecated\r
+     */\r
+    public Scriptable construct(Context cx, Object[] args)\r
+        throws NotAFunctionException,\r
+               JavaScriptException\r
+    {\r
+        if (!(obj instanceof Function)) {\r
+            throw new NotAFunctionException();\r
+        }\r
+        return ScriptRuntime.newObject(cx, obj, args, null);\r
+    }\r
+\r
+    /**\r
+     * Get the property indicated by the id, and invoke it with the\r
+     * specified arguments.\r
+     * <p>\r
+     * For example, for a FlattenedObject <code>obj</code>,\r
+     * and a Java array <code>a</code> consisting of a single string\r
+     * <code>"hi"</code>, the call <pre>\r
+     * obj.callMethod("m", a)</pre>\r
+     * is equivalent to the JavaScript code <code>obj.m("hi")</code>.<p>\r
+     *\r
+     * If the property is not found or is not a function, an\r
+     * exception will be thrown.\r
+     *\r
+     * @param id the Number or String to use to find the function property\r
+     *           to call\r
+     * @param args the arguments for the constructor call\r
+     * @return the result of the call\r
+     * @exception PropertyException if the designated property\r
+     *            was not found\r
+     * @exception NotAFunctionException if this object is not a function\r
+     * @exception JavaScriptException if an uncaught JavaScript exception\r
+     *            occurred while executing the method\r
+     * @see org.mozilla.javascript.Function#call\r
+     * @deprecated\r
+     */\r
+    public Object callMethod(Object id, Object[] args)\r
+        throws PropertyException,\r
+               NotAFunctionException,\r
+               JavaScriptException\r
+    {\r
+        if (!hasProperty(id)) {\r
+            throw PropertyException.withMessage0("msg.prop.not.found");\r
+        }\r
+        Object o = getProperty(id);\r
+        if (o instanceof FlattenedObject)\r
+            return ((FlattenedObject) o).call(Context.getContext(), obj, args);\r
+        throw new NotAFunctionException();\r
+    }\r
+\r
+    /****** End of API *******/\r
+\r
+    private static Scriptable getBase(Scriptable obj, String s) {\r
+        Scriptable m = obj;\r
+        while (m != null) {\r
+            if (m.has(s, obj))\r
+                return m;\r
+            m = m.getPrototype();\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private static Scriptable getBase(Scriptable obj, int index) {\r
+        Scriptable m = obj;\r
+        while (m != null) {\r
+            if (m.has(index, obj))\r
+                return m;\r
+            m = m.getPrototype();\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private Scriptable obj;\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/Function.java b/src/org/mozilla/javascript/Function.java
new file mode 100644 (file)
index 0000000..c958d5b
--- /dev/null
@@ -0,0 +1,86 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This is interface that all functions in JavaScript must implement.\r
+ * The interface provides for calling functions and constructors.\r
+ *\r
+ * @see org.mozilla.javascript.Scriptable\r
+ * @author Norris Boyd\r
+ */\r
+\r
+public interface Function extends Scriptable {\r
+    /**\r
+     * Call the function.\r
+     *\r
+     * Note that the array of arguments is not guaranteed to have\r
+     * length greater than 0.\r
+     *\r
+     * @param cx the current Context for this thread\r
+     * @param scope the scope to execute the function relative to. This is\r
+     *              set to the value returned by getParentScope() except\r
+     *              when the function is called from a closure.\r
+     * @param thisObj the JavaScript <code>this</code> object\r
+     * @param args the array of arguments\r
+     * @return the result of the call\r
+     * @exception JavaScriptException if an uncaught exception\r
+     *            occurred while executing the function\r
+     */\r
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj,\r
+                       Object[] args)\r
+        throws JavaScriptException;\r
+\r
+    /**\r
+     * Call the function as a constructor.\r
+     *\r
+     * This method is invoked by the runtime in order to satisfy a use\r
+     * of the JavaScript <code>new</code> operator.  This method is\r
+     * expected to create a new object and return it.\r
+     *\r
+     * @param cx the current Context for this thread\r
+     * @param scope an enclosing scope of the caller except\r
+     *              when the function is called from a closure.\r
+     * @param args the array of arguments\r
+     * @return the allocated object\r
+     * @exception JavaScriptException if an uncaught exception\r
+     *            occurred while executing the constructor\r
+     */\r
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args)\r
+        throws JavaScriptException;\r
+}\r
diff --git a/src/org/mozilla/javascript/FunctionNode.java b/src/org/mozilla/javascript/FunctionNode.java
new file mode 100644 (file)
index 0000000..1584bc4
--- /dev/null
@@ -0,0 +1,103 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Roger Lawrence\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.*;\r
+\r
+public class FunctionNode extends Node {\r
+\r
+    public FunctionNode(String name, Node left, Node right) {\r
+        super(TokenStream.FUNCTION, left, right, name);\r
+        itsVariableTable = new VariableTable();\r
+    }\r
+\r
+    public String getFunctionName() {\r
+        return getString();\r
+    }\r
+\r
+    public VariableTable getVariableTable() {\r
+        return itsVariableTable;\r
+    }\r
+\r
+    public boolean requiresActivation() {\r
+        return itsNeedsActivation;\r
+    }\r
+\r
+    public boolean setRequiresActivation(boolean b) {\r
+        return itsNeedsActivation = b;\r
+    }\r
+    \r
+    public boolean getCheckThis() {\r
+        return itsCheckThis;\r
+    }\r
+\r
+    public void setCheckThis(boolean b) {\r
+        itsCheckThis = b;\r
+    }\r
+    \r
+    /**\r
+     * There are three types of functions that can be defined. The first\r
+     * is a function statement. This is a function appearing as a top-level\r
+     * statement (i.e., not nested inside some other statement) in either a\r
+     * script or a function.\r
+     * \r
+     * The second is a function expression, which is a function appearing in\r
+     * an expression except for the third type, which is...\r
+     * \r
+     * The third type is a function expression where the expression is the \r
+     * top-level expression in an expression statement.\r
+     * \r
+     * The three types of functions have different treatment and must be \r
+     * distinquished.\r
+     */\r
+    public static final byte FUNCTION_STATEMENT            = 1;\r
+    public static final byte FUNCTION_EXPRESSION           = 2;\r
+    public static final byte FUNCTION_EXPRESSION_STATEMENT = 3;\r
+    \r
+    public byte getFunctionType() {\r
+        return itsFunctionType;\r
+    }\r
+\r
+    public void setFunctionType(byte functionType) {\r
+        itsFunctionType = functionType;\r
+    }\r
+\r
+    protected VariableTable itsVariableTable;\r
+    protected boolean itsNeedsActivation;\r
+    protected boolean itsCheckThis;\r
+    protected byte itsFunctionType;\r
+}\r
diff --git a/src/org/mozilla/javascript/FunctionObject.java b/src/org/mozilla/javascript/FunctionObject.java
new file mode 100644 (file)
index 0000000..50a3e93
--- /dev/null
@@ -0,0 +1,626 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Igor Bukanov\r
+ * David C. Navas\r
+ * Ted Neward\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.Vector;\r
+import java.lang.reflect.Constructor;\r
+import java.lang.reflect.Member;\r
+import java.lang.reflect.Method;\r
+import java.lang.reflect.Modifier;\r
+import java.lang.reflect.InvocationTargetException;\r
+\r
+public class FunctionObject extends NativeFunction {\r
+\r
+    /**\r
+     * Create a JavaScript function object from a Java method.\r
+     *\r
+     * <p>The <code>member</code> argument must be either a java.lang.reflect.Method\r
+     * or a java.lang.reflect.Constructor and must match one of two forms.<p>\r
+     *\r
+     * The first form is a member with zero or more parameters\r
+     * of the following types: Object, String, boolean, Scriptable,\r
+     * byte, short, int, float, or double. The Long type is not supported\r
+     * because the double representation of a long (which is the \r
+     * EMCA-mandated storage type for Numbers) may lose precision.\r
+     * If the member is a Method, the return value must be void or one \r
+     * of the types allowed for parameters.<p>\r
+     *\r
+     * The runtime will perform appropriate conversions based\r
+     * upon the type of the parameter. A parameter type of\r
+     * Object specifies that no conversions are to be done. A parameter\r
+     * of type String will use Context.toString to convert arguments.\r
+     * Similarly, parameters of type double, boolean, and Scriptable\r
+     * will cause Context.toNumber, Context.toBoolean, and\r
+     * Context.toObject, respectively, to be called.<p>\r
+     *\r
+     * If the method is not static, the Java 'this' value will\r
+     * correspond to the JavaScript 'this' value. Any attempt\r
+     * to call the function with a 'this' value that is not\r
+     * of the right Java type will result in an error.<p>\r
+     *\r
+     * The second form is the variable arguments (or "varargs")\r
+     * form. If the FunctionObject will be used as a constructor,\r
+     * the member must have the following parameters\r
+     * <pre>\r
+     *      (Context cx, Object[] args, Function ctorObj,\r
+     *       boolean inNewExpr)</pre>\r
+     * and if it is a Method, be static and return an Object result.<p>\r
+     *\r
+     * Otherwise, if the FunctionObject will <i>not</i> be used to define a\r
+     * constructor, the member must be a static Method with parameters\r
+     *      (Context cx, Scriptable thisObj, Object[] args,\r
+     *       Function funObj) </pre>\r
+     * <pre>\r
+     * and an Object result.<p>\r
+     *\r
+     * When the function varargs form is called as part of a function call,\r
+     * the <code>args</code> parameter contains the\r
+     * arguments, with <code>thisObj</code>\r
+     * set to the JavaScript 'this' value. <code>funObj</code>\r
+     * is the function object for the invoked function.<p>\r
+     *\r
+     * When the constructor varargs form is called or invoked while evaluating\r
+     * a <code>new</code> expression, <code>args</code> contains the\r
+     * arguments, <code>ctorObj</code> refers to this FunctionObject, and\r
+     * <code>inNewExpr</code> is true if and only if  a <code>new</code>\r
+     * expression caused the call. This supports defining a function that\r
+     * has different behavior when called as a constructor than when\r
+     * invoked as a normal function call. (For example, the Boolean\r
+     * constructor, when called as a function,\r
+     * will convert to boolean rather than creating a new object.)<p>\r
+     *\r
+     * @param name the name of the function\r
+     * @param methodOrConstructor a java.lang.reflect.Method or a java.lang.reflect.Constructor\r
+     *                            that defines the object\r
+     * @param scope enclosing scope of function\r
+     * @see org.mozilla.javascript.Scriptable\r
+     */\r
+    public FunctionObject(String name, Member methodOrConstructor,\r
+                          Scriptable scope)\r
+    {\r
+        String methodName;\r
+        if (methodOrConstructor instanceof Constructor) {\r
+            ctor = (Constructor) methodOrConstructor;\r
+            isStatic = true; // well, doesn't take a 'this'\r
+            types = ctor.getParameterTypes();\r
+            methodName = ctor.getName();\r
+        } else {\r
+            method = (Method) methodOrConstructor;\r
+            isStatic = Modifier.isStatic(method.getModifiers());\r
+            types = method.getParameterTypes();\r
+            methodName = method.getName();\r
+        }\r
+        this.functionName = name;\r
+        int length;\r
+        if (types.length == 4 && (types[1].isArray() || types[2].isArray())) {\r
+            // Either variable args or an error.\r
+            if (types[1].isArray()) {\r
+                if (!isStatic ||\r
+                    types[0] != Context.class ||\r
+                    types[1].getComponentType() != ScriptRuntime.ObjectClass ||\r
+                    types[2] != ScriptRuntime.FunctionClass ||\r
+                    types[3] != Boolean.TYPE)\r
+                {\r
+                    throw Context.reportRuntimeError1(\r
+                        "msg.varargs.ctor", methodName);\r
+                }\r
+                parmsLength = VARARGS_CTOR;\r
+            } else {\r
+                if (!isStatic ||\r
+                    types[0] != Context.class ||\r
+                    types[1] != ScriptRuntime.ScriptableClass ||\r
+                    types[2].getComponentType() != ScriptRuntime.ObjectClass ||\r
+                    types[3] != ScriptRuntime.FunctionClass)\r
+                {\r
+                    throw Context.reportRuntimeError1(\r
+                        "msg.varargs.fun", methodName);\r
+                }\r
+                parmsLength = VARARGS_METHOD;\r
+            }\r
+            // XXX check return type\r
+            length = 1;\r
+        } else {\r
+            parmsLength = (short) types.length;\r
+            for (int i=0; i < parmsLength; i++) {\r
+                Class type = types[i];\r
+                if (type != ScriptRuntime.ObjectClass &&\r
+                    type != ScriptRuntime.StringClass &&\r
+                    type != ScriptRuntime.BooleanClass &&\r
+                    !ScriptRuntime.NumberClass.isAssignableFrom(type) &&\r
+                    !Scriptable.class.isAssignableFrom(type) &&\r
+                    type != Boolean.TYPE &&\r
+                    type != Byte.TYPE &&\r
+                    type != Short.TYPE &&\r
+                    type != Integer.TYPE &&\r
+                    type != Float.TYPE && \r
+                    type != Double.TYPE)\r
+                {\r
+                    // Note that long is not supported.\r
+                    throw Context.reportRuntimeError1("msg.bad.parms", \r
+                                                      methodName);\r
+                }\r
+            }\r
+            length = parmsLength;\r
+        }\r
+\r
+        // Initialize length property\r
+        lengthPropertyValue = (short) length;\r
+\r
+        hasVoidReturn = method != null && method.getReturnType() == Void.TYPE;\r
+        this.argCount = (short) length;\r
+\r
+        setParentScope(scope);\r
+        setPrototype(getFunctionPrototype(scope));\r
+        Context cx = Context.getCurrentContext();\r
+        useDynamicScope = cx != null && \r
+                          cx.hasCompileFunctionsWithDynamicScope();\r
+    }\r
+\r
+    /**\r
+     * Return the value defined by  the method used to construct the object\r
+     * (number of parameters of the method, or 1 if the method is a "varargs"\r
+     * form), unless setLength has been called with a new value.\r
+     * Overrides getLength in BaseFunction.\r
+     *\r
+     * @see org.mozilla.javascript.FunctionObject#setLength\r
+     * @see org.mozilla.javascript.BaseFunction#getLength\r
+     */\r
+    public int getLength() {\r
+        return lengthPropertyValue;\r
+    }\r
+\r
+    /**\r
+     * Set the value of the "length" property.\r
+     *\r
+     * <p>Changing the value of the "length" property of a FunctionObject only\r
+     * affects the value retrieved from get() and does not affect the way\r
+     * the method itself is called. <p>\r
+     *\r
+     * The "length" property will be defined by default as the number\r
+     * of parameters of the method used to construct the FunctionObject,\r
+     * unless the method is a "varargs" form, in which case the "length"\r
+     * property will be defined to 1.\r
+     *\r
+     * @param length the new length\r
+     */\r
+    public void setLength(short length) {\r
+        lengthPropertyValue = length;\r
+    }\r
+\r
+    // TODO: Make not public\r
+    /**\r
+     * Finds methods of a given name in a given class.\r
+     *\r
+     * <p>Searches <code>clazz</code> for methods with name\r
+     * <code>name</code>. Maintains a cache so that multiple\r
+     * lookups on the same class are cheap.\r
+     *\r
+     * @param clazz the class to search\r
+     * @param name the name of the methods to find\r
+     * @return an array of the found methods, or null if no methods\r
+     *         by that name were found.\r
+     * @see java.lang.Class#getMethods\r
+     */\r
+    public static Method[] findMethods(Class clazz, String name) {\r
+        return findMethods(getMethodList(clazz), name);\r
+    }\r
+    \r
+    static Method[] findMethods(Method[] methods, String name) {\r
+        // Usually we're just looking for a single method, so optimize\r
+        // for that case.\r
+        Vector v = null;\r
+        Method first = null;\r
+        for (int i=0; i < methods.length; i++) {\r
+            if (methods[i] == null)\r
+                continue;\r
+            if (methods[i].getName().equals(name)) {\r
+                if (first == null) {\r
+                    first = methods[i];\r
+                } else {\r
+                    if (v == null) {\r
+                        v = new Vector(5);\r
+                        v.addElement(first);\r
+                    }\r
+                    v.addElement(methods[i]);\r
+                }\r
+            }\r
+        }\r
+        if (v == null) {\r
+            if (first == null)\r
+                return null;\r
+            Method[] single = { first };\r
+            return single;\r
+        }\r
+        Method[] result = new Method[v.size()];\r
+        v.copyInto(result);\r
+        return result;\r
+    }\r
+\r
+    static Method[] getMethodList(Class clazz) {\r
+        Method[] cached = methodsCache; // get once to avoid synchronization\r
+        if (cached != null && cached[0].getDeclaringClass() == clazz)\r
+            return cached;\r
+        Method[] methods = null;\r
+        try {\r
+            // getDeclaredMethods may be rejected by the security manager\r
+            // but getMethods is more expensive\r
+            if (!sawSecurityException) \r
+                methods = clazz.getDeclaredMethods();\r
+        } catch (SecurityException e) {\r
+            // If we get an exception once, give up on getDeclaredMethods\r
+            sawSecurityException = true;\r
+        }\r
+        if (methods == null) {\r
+            methods = clazz.getMethods();\r
+        }\r
+        int count = 0;\r
+        for (int i=0; i < methods.length; i++) {\r
+            if (sawSecurityException \r
+                ? methods[i].getDeclaringClass() != clazz\r
+                : !Modifier.isPublic(methods[i].getModifiers()))\r
+            {\r
+                methods[i] = null;\r
+            } else {\r
+                count++;\r
+            }\r
+        }\r
+        Method[] result = new Method[count];\r
+        int j=0;\r
+        for (int i=0; i < methods.length; i++) {\r
+            if (methods[i] != null)\r
+                result[j++] = methods[i];\r
+        }\r
+        if (result.length > 0 && Context.isCachingEnabled)\r
+            methodsCache = result;\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Define this function as a JavaScript constructor.\r
+     * <p>\r
+     * Sets up the "prototype" and "constructor" properties. Also\r
+     * calls setParent and setPrototype with appropriate values.\r
+     * Then adds the function object as a property of the given scope, using\r
+     *      <code>prototype.getClassName()</code>\r
+     * as the name of the property.\r
+     *\r
+     * @param scope the scope in which to define the constructor (typically\r
+     *              the global object)\r
+     * @param prototype the prototype object\r
+     * @see org.mozilla.javascript.Scriptable#setParentScope\r
+     * @see org.mozilla.javascript.Scriptable#setPrototype\r
+     * @see org.mozilla.javascript.Scriptable#getClassName\r
+     */\r
+    public void addAsConstructor(Scriptable scope, Scriptable prototype) {\r
+        setParentScope(scope);\r
+        setPrototype(getFunctionPrototype(scope));\r
+        setImmunePrototypeProperty(prototype);\r
+\r
+        prototype.setParentScope(this);\r
+        \r
+        final int attr = ScriptableObject.DONTENUM  |\r
+                         ScriptableObject.PERMANENT |\r
+                         ScriptableObject.READONLY;\r
+        defineProperty(prototype, "constructor", this, attr);\r
+\r
+        String name = prototype.getClassName();\r
+        defineProperty(scope, name, this, ScriptableObject.DONTENUM);\r
+\r
+        setParentScope(scope);\r
+    }\r
+\r
+    static public Object convertArg(Scriptable scope,\r
+                                    Object arg, Class desired)\r
+    {\r
+        if (desired == ScriptRuntime.StringClass) \r
+            return ScriptRuntime.toString(arg);\r
+        if (desired == ScriptRuntime.IntegerClass || \r
+            desired == Integer.TYPE)\r
+        {\r
+            return new Integer(ScriptRuntime.toInt32(arg));\r
+        }\r
+        if (desired == ScriptRuntime.BooleanClass || \r
+            desired == Boolean.TYPE)\r
+        {\r
+            return ScriptRuntime.toBoolean(arg) ? Boolean.TRUE \r
+                                                : Boolean.FALSE;\r
+        }\r
+        if (desired == ScriptRuntime.DoubleClass || \r
+            desired == Double.TYPE)\r
+        {\r
+            return new Double(ScriptRuntime.toNumber(arg));\r
+        }\r
+        if (desired == ScriptRuntime.ScriptableClass)\r
+            return ScriptRuntime.toObject(scope, arg);\r
+        if (desired == ScriptRuntime.ObjectClass)\r
+            return arg;\r
+        \r
+        // Note that the long type is not supported; see the javadoc for\r
+        // the constructor for this class\r
+        throw Context.reportRuntimeError1\r
+            ("msg.cant.convert", desired.getName());\r
+    }\r
+\r
+    /**\r
+     * Performs conversions on argument types if needed and\r
+     * invokes the underlying Java method or constructor.\r
+     * <p>\r
+     * Implements Function.call.\r
+     *\r
+     * @see org.mozilla.javascript.Function#call\r
+     * @exception JavaScriptException if the underlying Java method or \r
+     *            constructor threw an exception\r
+     */\r
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj,\r
+                       Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (parmsLength < 0) {\r
+            return callVarargs(cx, thisObj, args, false);\r
+        }\r
+        if (!isStatic) {\r
+            // OPT: cache "clazz"?\r
+            Class clazz = method != null ? method.getDeclaringClass()\r
+                                         : ctor.getDeclaringClass();\r
+            while (!clazz.isInstance(thisObj)) {\r
+                thisObj = thisObj.getPrototype();\r
+                if (thisObj == null || !useDynamicScope) {\r
+                    // Couldn't find an object to call this on.\r
+                    throw NativeGlobal.typeError1\r
+                        ("msg.incompat.call", functionName, scope);\r
+                }\r
+            }\r
+        }\r
+        Object[] invokeArgs;\r
+        int i;\r
+        if (parmsLength == args.length) {\r
+            invokeArgs = args;\r
+            // avoid copy loop if no conversions needed\r
+            i = (types == null) ? parmsLength : 0;\r
+        } else {\r
+            invokeArgs = new Object[parmsLength];\r
+            i = 0;\r
+        }\r
+        for (; i < parmsLength; i++) {\r
+            Object arg = (i < args.length)\r
+                         ? args[i]\r
+                         : Undefined.instance;\r
+            if (types != null) {\r
+                arg = convertArg(this, arg, types[i]);\r
+            }\r
+            invokeArgs[i] = arg;\r
+        }\r
+        try {\r
+            Object result = method == null ? ctor.newInstance(invokeArgs)\r
+                                           : doInvoke(thisObj, invokeArgs);\r
+            return hasVoidReturn ? Undefined.instance : result;\r
+        }\r
+        catch (InvocationTargetException e) {\r
+            throw JavaScriptException.wrapException(scope, e);\r
+        }\r
+        catch (IllegalAccessException e) {\r
+            throw WrappedException.wrapException(e);\r
+        }\r
+        catch (InstantiationException e) {\r
+            throw WrappedException.wrapException(e);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Performs conversions on argument types if needed and\r
+     * invokes the underlying Java method or constructor\r
+     * to create a new Scriptable object.\r
+     * <p>\r
+     * Implements Function.construct.\r
+     *\r
+     * @param cx the current Context for this thread\r
+     * @param scope the scope to execute the function relative to. This\r
+     *              set to the value returned by getParentScope() except\r
+     *              when the function is called from a closure.\r
+     * @param args arguments to the constructor\r
+     * @see org.mozilla.javascript.Function#construct\r
+     * @exception JavaScriptException if the underlying Java method or constructor\r
+     *            threw an exception\r
+     */\r
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (method == null || parmsLength == VARARGS_CTOR) {\r
+            Scriptable result;\r
+            if (method != null) {\r
+                result = (Scriptable) callVarargs(cx, null, args, true);\r
+            } else {\r
+                result = (Scriptable) call(cx, scope, null, args);\r
+            }\r
+\r
+            if (result.getPrototype() == null)\r
+                result.setPrototype(getClassPrototype());\r
+            if (result.getParentScope() == null) {\r
+                Scriptable parent = getParentScope();\r
+                if (result != parent)\r
+                    result.setParentScope(parent);\r
+            }\r
+\r
+            return result;\r
+        } else if (method != null && !isStatic) {\r
+            Scriptable result;\r
+            try {\r
+                result = (Scriptable) method.getDeclaringClass().newInstance();\r
+            } catch (IllegalAccessException e) {\r
+                throw WrappedException.wrapException(e);\r
+            } catch (InstantiationException e) {\r
+                throw WrappedException.wrapException(e);\r
+            }\r
+\r
+            result.setPrototype(getClassPrototype());\r
+            result.setParentScope(getParentScope());\r
+\r
+            Object val = call(cx, scope, result, args);\r
+            if (val != null && val != Undefined.instance &&\r
+                val instanceof Scriptable)\r
+            {\r
+                return (Scriptable) val;\r
+            }\r
+            return result;\r
+        }\r
+\r
+        return super.construct(cx, scope, args);\r
+    }\r
+        \r
+    private final Object doInvoke(Object thisObj, Object[] args) \r
+        throws IllegalAccessException, InvocationTargetException\r
+    {\r
+        Invoker master = invokerMaster;\r
+        if (master != null) {\r
+            if (invoker == null) {\r
+                invoker = master.createInvoker(method, types);\r
+            }\r
+            try {\r
+                return invoker.invoke(thisObj, args);\r
+            } catch (RuntimeException e) {\r
+                throw new InvocationTargetException(e);\r
+            }\r
+        } \r
+        return method.invoke(thisObj, args);\r
+    }\r
+\r
+    private Object callVarargs(Context cx, Scriptable thisObj, Object[] args,\r
+                               boolean inNewExpr)\r
+        throws JavaScriptException\r
+    {\r
+        try {\r
+            Object[] invokeArgs;\r
+            Object ret;\r
+            if (cx.arrayCache.size() > 0) invokeArgs = (Object[])cx.arrayCache.lastElement();\r
+            else invokeArgs = new Object[4];\r
+            \r
+            if (parmsLength == VARARGS_METHOD) {\r
+                invokeArgs[0] = cx;\r
+                invokeArgs[1] = thisObj;\r
+                invokeArgs[2] = args;\r
+                invokeArgs[3] = this;\r
+                Object result = doInvoke(null, invokeArgs);\r
+                ret = hasVoidReturn ? Undefined.instance : result;\r
+            } else {\r
+                Boolean b = inNewExpr ? Boolean.TRUE : Boolean.FALSE;\r
+                invokeArgs[0] = cx;\r
+                invokeArgs[1] = args;\r
+                invokeArgs[2] = this;\r
+                invokeArgs[3] = b;\r
+                ret = (method == null)\r
+                    ? ctor.newInstance(invokeArgs)\r
+                    : doInvoke(null, invokeArgs);\r
+            }\r
+            \r
+            cx.arrayCache.addElement(invokeArgs);\r
+            return ret;\r
+        }\r
+        catch (InvocationTargetException e) {\r
+            Throwable target = e.getTargetException();\r
+            if (target instanceof EvaluatorException)\r
+                throw (EvaluatorException) target;\r
+            if (target instanceof EcmaError)\r
+                throw (EcmaError) target;\r
+            Scriptable scope = thisObj == null ? this : thisObj;\r
+            throw JavaScriptException.wrapException(scope, target);\r
+        }\r
+        catch (IllegalAccessException e) {\r
+            throw WrappedException.wrapException(e);\r
+        }\r
+        catch (InstantiationException e) {\r
+            throw WrappedException.wrapException(e);\r
+        }\r
+    }\r
+    \r
+    boolean isVarArgsMethod() { \r
+        return parmsLength == VARARGS_METHOD;\r
+    }\r
+\r
+    boolean isVarArgsConstructor() { \r
+        return parmsLength == VARARGS_CTOR;\r
+    }\r
+    \r
+    static void setCachingEnabled(boolean enabled) {\r
+        if (!enabled) {\r
+            methodsCache = null;\r
+            invokerMaster = null;\r
+        } else if (invokerMaster == null) {\r
+            invokerMaster = newInvokerMaster();\r
+        }\r
+    }\r
+\r
+    /** Get default master implementation or null if not available */\r
+    private static Invoker newInvokerMaster() {\r
+        /*\r
+        try {\r
+            Class cl = ScriptRuntime.loadClassName(INVOKER_MASTER_CLASS);\r
+            return (Invoker)cl.newInstance();\r
+        }\r
+        catch (ClassNotFoundException ex) {}\r
+        catch (IllegalAccessException ex) {}\r
+        catch (InstantiationException ex) {}\r
+        catch (SecurityException ex) {}\r
+        */\r
+        return null;\r
+    }\r
+\r
+    private static final String \r
+        INVOKER_MASTER_CLASS = "org.mozilla.javascript.optimizer.InvokerImpl";\r
+\r
+    static Invoker invokerMaster = newInvokerMaster();\r
+    \r
+    private static final short VARARGS_METHOD = -1;\r
+    private static final short VARARGS_CTOR =   -2;\r
+    \r
+    private static boolean sawSecurityException;\r
+\r
+    static Method[] methodsCache;\r
+\r
+    Method method;\r
+    Constructor ctor;\r
+    private Class[] types;\r
+    Invoker invoker;\r
+    private short parmsLength;\r
+    private short lengthPropertyValue;\r
+    private boolean hasVoidReturn;\r
+    private boolean isStatic;\r
+    private boolean useDynamicScope;\r
+}\r
diff --git a/src/org/mozilla/javascript/IRFactory.java b/src/org/mozilla/javascript/IRFactory.java
new file mode 100644 (file)
index 0000000..948ac10
--- /dev/null
@@ -0,0 +1,1065 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This class allows the creation of nodes, and follows the Factory pattern.\r
+ *\r
+ * @see Node\r
+ * @author Mike McCabe\r
+ * @author Norris Boyd\r
+ */\r
+public class IRFactory {\r
+    \r
+    public IRFactory(TokenStream ts, Scriptable scope) {\r
+        this.ts = ts;\r
+        this.scope = scope;\r
+    }\r
+\r
+    /**\r
+     * Script (for associating file/url names with toplevel scripts.)\r
+     */\r
+    public Object createScript(Object body, String sourceName, \r
+                               int baseLineno, int endLineno, Object source)\r
+    {\r
+        Node result = new Node(TokenStream.SCRIPT, sourceName);\r
+        Node children = ((Node) body).getFirstChild();\r
+        if (children != null)\r
+            result.addChildrenToBack(children);\r
+        result.putProp(Node.SOURCENAME_PROP, sourceName);\r
+        result.putProp(Node.BASE_LINENO_PROP, new Integer(baseLineno));\r
+        result.putProp(Node.END_LINENO_PROP, new Integer(endLineno));\r
+        if (source != null)\r
+            result.putProp(Node.SOURCE_PROP, source);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Leaf\r
+     */\r
+    public Object createLeaf(int nodeType) {\r
+            return new Node(nodeType);\r
+    }\r
+\r
+    public Object createLeaf(int nodeType, String id) {\r
+        return new Node(nodeType, id);\r
+    }\r
+\r
+    public Object createLeaf(int nodeType, int nodeOp) {\r
+        return new Node(nodeType, new Integer(nodeOp));\r
+    }\r
+\r
+    /**\r
+     * Statement leaf nodes.\r
+     */\r
+\r
+    public Object createSwitch(int lineno) {\r
+        return new Node(TokenStream.SWITCH, new Integer(lineno));\r
+    }\r
+\r
+    public Object createVariables(int lineno) {\r
+        return new Node(TokenStream.VAR, new Integer(lineno));\r
+    }\r
+\r
+    public Object createExprStatement(Object expr, int lineno) {\r
+        return new Node(TokenStream.EXPRSTMT, (Node) expr, new Integer(lineno));\r
+    }\r
+\r
+    /**\r
+     * Name\r
+     */\r
+    public Object createName(String name) {\r
+        return new Node(TokenStream.NAME, name);\r
+    }\r
+\r
+    /**\r
+     * String (for literals)\r
+     */\r
+    public Object createString(String string) {\r
+        return new Node(TokenStream.STRING, string);\r
+    }\r
+\r
+    /**\r
+     * Number (for literals)\r
+     */\r
+    public Object createNumber(Number number) {\r
+        return new Node(TokenStream.NUMBER, number);\r
+    }\r
+\r
+    /**\r
+     * Catch clause of try/catch/finally\r
+     * @param varName the name of the variable to bind to the exception\r
+     * @param catchCond the condition under which to catch the exception.\r
+     *                  May be null if no condition is given.\r
+     * @param stmts the statements in the catch clause\r
+     * @param lineno the starting line number of the catch clause\r
+     */\r
+    public Object createCatch(String varName, Object catchCond, Object stmts,\r
+                              int lineno)\r
+    {\r
+        if (catchCond == null)\r
+            catchCond = new Node(TokenStream.PRIMARY, \r
+                                 new Integer(TokenStream.TRUE));\r
+        Node result = new Node(TokenStream.CATCH, (Node)createName(varName), \r
+                               (Node)catchCond, (Node)stmts);\r
+        result.setDatum(new Integer(lineno));\r
+        return result;\r
+    }\r
+    \r
+    /**\r
+     * Throw\r
+     */\r
+    public Object createThrow(Object expr, int lineno) {\r
+        return new Node(TokenStream.THROW, (Node)expr, new Integer(lineno));\r
+    }\r
+\r
+    /**\r
+     * Return\r
+     */\r
+    public Object createReturn(Object expr, int lineno) {\r
+        return expr == null\r
+            ? new Node(TokenStream.RETURN, new Integer(lineno))\r
+            : new Node(TokenStream.RETURN, (Node)expr, new Integer(lineno));\r
+    }\r
+\r
+    /**\r
+     * Assert\r
+     */\r
+    public Object createAssert(Object expr, int lineno) {\r
+        return expr == null\r
+            ? new Node(TokenStream.ASSERT, new Integer(lineno))\r
+            : new Node(TokenStream.ASSERT, (Node)expr, new Integer(lineno));\r
+    }\r
+\r
+    /**\r
+     * Label\r
+     */\r
+    public Object createLabel(String label, int lineno) {\r
+        Node result = new Node(TokenStream.LABEL, new Integer(lineno));\r
+        Node name = new Node(TokenStream.NAME, label);\r
+        result.addChildToBack(name);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Break (possibly labeled)\r
+     */\r
+    public Object createBreak(String label, int lineno) {\r
+        Node result = new Node(TokenStream.BREAK, new Integer(lineno));\r
+        if (label == null) {\r
+            return result;\r
+        } else {\r
+            Node name = new Node(TokenStream.NAME, label);\r
+            result.addChildToBack(name);\r
+            return result;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Continue (possibly labeled)\r
+     */\r
+    public Object createContinue(String label, int lineno) {\r
+        Node result = new Node(TokenStream.CONTINUE, new Integer(lineno));\r
+        if (label == null) {\r
+            return result;\r
+        } else {\r
+            Node name = new Node(TokenStream.NAME, label);\r
+            result.addChildToBack(name);\r
+            return result;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Statement block\r
+     * Creates the empty statement block\r
+     * Must make subsequent calls to add statements to the node\r
+     */\r
+    public Object createBlock(int lineno) {\r
+        return new Node(TokenStream.BLOCK, new Integer(lineno));\r
+    }\r
+    \r
+    public Object createFunctionNode(String name, Object args, \r
+                                     Object statements) \r
+    {\r
+        if (name == null)\r
+            name = "";\r
+        return new FunctionNode(name, (Node) args, (Node) statements);\r
+    }\r
+\r
+    public Object createFunction(String name, Object args, Object statements,\r
+                                 String sourceName, int baseLineno, \r
+                                 int endLineno, Object source,\r
+                                 boolean isExpr)\r
+    {\r
+        FunctionNode f = (FunctionNode) createFunctionNode(name, args, \r
+                                                           statements);\r
+        f.setFunctionType(isExpr ? FunctionNode.FUNCTION_EXPRESSION\r
+                                 : FunctionNode.FUNCTION_STATEMENT);\r
+        f.putProp(Node.SOURCENAME_PROP, sourceName);\r
+        f.putProp(Node.BASE_LINENO_PROP, new Integer(baseLineno));\r
+        f.putProp(Node.END_LINENO_PROP, new Integer(endLineno));\r
+        if (source != null)\r
+            f.putProp(Node.SOURCE_PROP, source);\r
+        Node result = new Node(TokenStream.FUNCTION, name);\r
+        result.putProp(Node.FUNCTION_PROP, f);\r
+        return result;\r
+    }\r
+    \r
+    public void setFunctionExpressionStatement(Object o) {\r
+        Node n = (Node) o;\r
+        FunctionNode f = (FunctionNode) n.getProp(Node.FUNCTION_PROP);\r
+        f.setFunctionType(FunctionNode.FUNCTION_EXPRESSION_STATEMENT);\r
+    }\r
+\r
+    /**\r
+     * Add a child to the back of the given node.  This function\r
+     * breaks the Factory abstraction, but it removes a requirement\r
+     * from implementors of Node.\r
+     */\r
+    public void addChildToBack(Object parent, Object child) {\r
+        ((Node)parent).addChildToBack((Node)child);\r
+    }\r
+\r
+    /**\r
+     * While\r
+     */\r
+    public Object createWhile(Object cond, Object body, int lineno) {\r
+        // Just add a GOTO to the condition in the do..while\r
+        Node result = (Node) createDoWhile(body, cond, lineno);\r
+        Node condTarget = (Node) result.getProp(Node.CONTINUE_PROP);\r
+        Node GOTO = new Node(TokenStream.GOTO);\r
+        GOTO.putProp(Node.TARGET_PROP, condTarget);\r
+        result.addChildToFront(GOTO);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * DoWhile\r
+     */\r
+    public Object createDoWhile(Object body, Object cond, int lineno) {\r
+        Node result = new Node(TokenStream.LOOP, new Integer(lineno));\r
+        Node bodyTarget = new Node(TokenStream.TARGET);\r
+        Node condTarget = new Node(TokenStream.TARGET);\r
+        Node IFEQ = new Node(TokenStream.IFEQ, (Node)cond);\r
+        IFEQ.putProp(Node.TARGET_PROP, bodyTarget);\r
+        Node breakTarget = new Node(TokenStream.TARGET);\r
+\r
+        result.addChildToBack(bodyTarget);\r
+        result.addChildrenToBack((Node)body);\r
+        result.addChildToBack(condTarget);\r
+        result.addChildToBack(IFEQ);\r
+        result.addChildToBack(breakTarget);\r
+\r
+        result.putProp(Node.BREAK_PROP, breakTarget);\r
+        result.putProp(Node.CONTINUE_PROP, condTarget);\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * For\r
+     */\r
+    public Object createFor(Object init, Object test, Object incr,\r
+                            Object body, int lineno)\r
+    {\r
+        if (((Node) test).getType() == TokenStream.VOID) {\r
+            test = new Node(TokenStream.PRIMARY, \r
+                            new Integer(TokenStream.TRUE));\r
+        }\r
+        Node result = (Node)createWhile(test, body, lineno);\r
+        Node initNode = (Node) init;\r
+        if (initNode.getType() != TokenStream.VOID) {\r
+            if (initNode.getType() != TokenStream.VAR)\r
+                initNode = new Node(TokenStream.POP, initNode);\r
+            result.addChildToFront(initNode);\r
+        }\r
+        Node condTarget = (Node)result.getProp(Node.CONTINUE_PROP);\r
+        Node incrTarget = new Node(TokenStream.TARGET);\r
+        result.addChildBefore(incrTarget, condTarget);\r
+        if (((Node) incr).getType() != TokenStream.VOID) {\r
+            incr = createUnary(TokenStream.POP, incr);\r
+            result.addChildAfter((Node)incr, incrTarget);\r
+        }\r
+        result.putProp(Node.CONTINUE_PROP, incrTarget);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * For .. In\r
+     *\r
+     */\r
+    public Object createForIn(Object lhs, Object obj, Object body, int lineno) {\r
+        String name;\r
+        Node lhsNode = (Node) lhs;\r
+        Node objNode = (Node) obj;\r
+        int type = lhsNode.getType();\r
+\r
+        Node lvalue = lhsNode;\r
+        switch (type) {\r
+\r
+          case TokenStream.NAME:\r
+          case TokenStream.GETPROP:\r
+          case TokenStream.GETELEM:\r
+            break;\r
+\r
+          case TokenStream.VAR:\r
+            /*\r
+             * check that there was only one variable given.\r
+             * we can't do this in the parser, because then the\r
+             * parser would have to know something about the\r
+             * 'init' node of the for-in loop.\r
+             */\r
+            Node lastChild = lhsNode.getLastChild();\r
+            if (lhsNode.getFirstChild() != lastChild) {\r
+                reportError("msg.mult.index");\r
+            }\r
+            lvalue = new Node(TokenStream.NAME, lastChild.getString());\r
+            break;\r
+\r
+          default:\r
+            reportError("msg.bad.for.in.lhs");\r
+            return objNode;\r
+        }\r
+\r
+        Node init = new Node(TokenStream.ENUMINIT, objNode);\r
+        Node next = new Node(TokenStream.ENUMNEXT);\r
+        next.putProp(Node.ENUM_PROP, init);\r
+        Node temp = createNewTemp(next);\r
+        Node cond = new Node(TokenStream.EQOP, new Integer(TokenStream.NE));\r
+        cond.addChildToBack(temp);\r
+        cond.addChildToBack(new Node(TokenStream.PRIMARY,\r
+                                     new Integer(TokenStream.NULL)));\r
+        Node newBody = new Node(TokenStream.BLOCK);\r
+        Node assign = (Node) createAssignment(TokenStream.NOP, lvalue,\r
+                                              createUseTemp(temp), null,\r
+                                              false);\r
+        newBody.addChildToBack(new Node(TokenStream.POP, assign));\r
+        newBody.addChildToBack((Node) body);\r
+        Node result = (Node) createWhile(cond, newBody, lineno);\r
+\r
+        result.addChildToFront(init);\r
+        if (type == TokenStream.VAR)\r
+            result.addChildToFront(lhsNode);\r
+\r
+        Node done = new Node(TokenStream.ENUMDONE);\r
+        done.putProp(Node.ENUM_PROP, init);\r
+        result.addChildToBack(done);\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Try/Catch/Finally\r
+     *\r
+     * The IRFactory tries to express as much as possible in the tree;\r
+     * the responsibilities remaining for Codegen are to add the Java\r
+     * handlers: (Either (but not both) of TARGET and FINALLY might not\r
+     * be defined)\r
+\r
+     * - a catch handler for javascript exceptions that unwraps the\r
+     * exception onto the stack and GOTOes to the catch target -\r
+     * TARGET_PROP in the try node.\r
+\r
+     * - a finally handler that catches any exception, stores it to a\r
+     * temporary, and JSRs to the finally target - FINALLY_PROP in the\r
+     * try node - before re-throwing the exception.\r
+\r
+     * ... and a goto to GOTO around these handlers.\r
+     */\r
+    public Object createTryCatchFinally(Object tryblock, Object catchblocks,\r
+                                        Object finallyblock, int lineno)\r
+    {\r
+        Node trynode = (Node)tryblock;\r
+\r
+        // short circuit\r
+        if (trynode.getType() == TokenStream.BLOCK && !trynode.hasChildren())\r
+            return trynode;\r
+\r
+        Node pn = new Node(TokenStream.TRY, trynode, new Integer(lineno));\r
+        Node catchNodes = (Node)catchblocks;\r
+        boolean hasCatch = catchNodes.hasChildren();\r
+        boolean hasFinally = false;\r
+        Node finallyNode = null;\r
+        Node finallyTarget = null;\r
+        if (finallyblock != null) {\r
+            finallyNode = (Node)finallyblock;\r
+            hasFinally = (finallyNode.getType() != TokenStream.BLOCK\r
+                          || finallyNode.hasChildren());\r
+            if (hasFinally) {\r
+                // make a TARGET for the finally that the tcf node knows about\r
+                finallyTarget = new Node(TokenStream.TARGET);\r
+                pn.putProp(Node.FINALLY_PROP, finallyTarget);\r
+\r
+                // add jsr finally to the try block\r
+                Node jsrFinally = new Node(TokenStream.JSR);\r
+                jsrFinally.putProp(Node.TARGET_PROP, finallyTarget);\r
+                pn.addChildToBack(jsrFinally);\r
+            }\r
+        }\r
+\r
+        // short circuit\r
+        if (!hasFinally && !hasCatch)  // bc finally might be an empty block...\r
+            return trynode;\r
+\r
+        Node endTarget = new Node(TokenStream.TARGET);\r
+        Node GOTOToEnd = new Node(TokenStream.GOTO);\r
+        GOTOToEnd.putProp(Node.TARGET_PROP, endTarget);\r
+        pn.addChildToBack(GOTOToEnd);\r
+       \r
+        if (hasCatch) {\r
+            /*\r
+             *\r
+               Given\r
+               \r
+                try {\r
+                        throw 3;\r
+                } catch (e: e instanceof Object) {\r
+                        print("object");\r
+                } catch (e2) {\r
+                        print(e2);\r
+                }\r
+\r
+               rewrite as\r
+\r
+                try {\r
+                        throw 3;\r
+                } catch (x) {\r
+                        o = newScope();\r
+                        o.e = x;\r
+                        with (o) {\r
+                                if (e instanceof Object) {\r
+                                        print("object");\r
+                                }\r
+                        }\r
+                        o2 = newScope();\r
+                        o2.e2 = x;\r
+                        with (o2) {\r
+                                if (true) {\r
+                                        print(e2);\r
+                                }\r
+                        }\r
+                }\r
+            */\r
+            // make a TARGET for the catch that the tcf node knows about\r
+            Node catchTarget = new Node(TokenStream.TARGET);\r
+            pn.putProp(Node.TARGET_PROP, catchTarget);\r
+            // mark it\r
+            pn.addChildToBack(catchTarget);\r
+            \r
+            // get the exception object and store it in a temp\r
+            Node exn = createNewLocal(new Node(TokenStream.VOID));\r
+            pn.addChildToBack(new Node(TokenStream.POP, exn));\r
+            \r
+            Node endCatch = new Node(TokenStream.TARGET);\r
+\r
+            // add [jsr finally?] goto end to each catch block\r
+            // expects catchNode children to be (cond block) pairs.\r
+            Node cb = catchNodes.getFirstChild();\r
+            while (cb != null) {\r
+                Node catchStmt = new Node(TokenStream.BLOCK);\r
+                int catchLineNo = ((Integer)cb.getDatum()).intValue();\r
+                \r
+                Node name = cb.getFirstChild();\r
+                Node cond = name.getNextSibling();\r
+                Node catchBlock = cond.getNextSibling();\r
+                cb.removeChild(name);\r
+                cb.removeChild(cond);\r
+                cb.removeChild(catchBlock);\r
+                \r
+                Node newScope = createNewLocal(new Node(TokenStream.NEWSCOPE));\r
+                Node initScope = new Node(TokenStream.SETPROP, newScope, \r
+                                          new Node(TokenStream.STRING, \r
+                                                   name.getString()), \r
+                                          createUseLocal(exn));\r
+                catchStmt.addChildToBack(new Node(TokenStream.POP, initScope));\r
+                \r
+                catchBlock.addChildToBack(new Node(TokenStream.LEAVEWITH));\r
+                Node GOTOToEndCatch = new Node(TokenStream.GOTO);\r
+                GOTOToEndCatch.putProp(Node.TARGET_PROP, endCatch);\r
+                catchBlock.addChildToBack(GOTOToEndCatch);\r
+                \r
+                Node ifStmt = (Node) createIf(cond, catchBlock, null, catchLineNo);\r
+                // Try..catch produces "with" code in order to limit \r
+                // the scope of the exception object.\r
+                // OPT: We should be able to figure out the correct\r
+                //      scoping at compile-time and avoid the\r
+                //      runtime overhead.\r
+                Node withStmt = (Node) createWith(createUseLocal(newScope), \r
+                                                  ifStmt, catchLineNo);\r
+                catchStmt.addChildToBack(withStmt);\r
+                \r
+                pn.addChildToBack(catchStmt);\r
+                \r
+                // move to next cb \r
+                cb = cb.getNextSibling();\r
+            }\r
+            \r
+            // Generate code to rethrow if no catch clause was executed\r
+            Node rethrow = new Node(TokenStream.THROW, createUseLocal(exn));\r
+            pn.addChildToBack(rethrow);\r
+\r
+            pn.addChildToBack(endCatch);\r
+            // add a JSR finally if needed\r
+            if (hasFinally) {\r
+                Node jsrFinally = new Node(TokenStream.JSR);\r
+                jsrFinally.putProp(Node.TARGET_PROP, finallyTarget);\r
+                pn.addChildToBack(jsrFinally);\r
+                Node GOTO = new Node(TokenStream.GOTO);\r
+                GOTO.putProp(Node.TARGET_PROP, endTarget);\r
+                pn.addChildToBack(GOTO);\r
+            }\r
+        }\r
+\r
+        if (hasFinally) {\r
+            pn.addChildToBack(finallyTarget);\r
+            Node returnTemp = createNewLocal(new Node(TokenStream.VOID));\r
+            Node popAndMake = new Node(TokenStream.POP, returnTemp);\r
+            pn.addChildToBack(popAndMake);\r
+            pn.addChildToBack(finallyNode);\r
+            Node ret = createUseLocal(returnTemp);\r
+\r
+            // add the magic prop that makes it output a RET\r
+            ret.putProp(Node.TARGET_PROP, Boolean.TRUE);\r
+            pn.addChildToBack(ret);\r
+        }\r
+        pn.addChildToBack(endTarget);\r
+        return pn;\r
+    }\r
+\r
+    /**\r
+     * Throw, Return, Label, Break and Continue are defined in ASTFactory.\r
+     */\r
+\r
+    /**\r
+     * With\r
+     */\r
+    public Object createWith(Object obj, Object body, int lineno) {\r
+        Node result = new Node(TokenStream.BLOCK, new Integer(lineno));\r
+        result.addChildToBack(new Node(TokenStream.ENTERWITH, (Node)obj));\r
+        Node bodyNode = new Node(TokenStream.WITH, (Node) body,\r
+                                 new Integer(lineno));\r
+        result.addChildrenToBack(bodyNode);\r
+        result.addChildToBack(new Node(TokenStream.LEAVEWITH));\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Array Literal\r
+     * <BR>createArrayLiteral rewrites its argument as array creation\r
+     * plus a series of array element entries, so later compiler\r
+     * stages don't need to know about array literals.\r
+     */\r
+    public Object createArrayLiteral(Object obj) {\r
+        Node array;\r
+        Node result;\r
+        array = result = new Node(TokenStream.NEW,\r
+                                  new Node(TokenStream.NAME, "Array"));\r
+        Node temp = createNewTemp(result);\r
+        result = temp;\r
+\r
+        java.util.Enumeration children = ((Node) obj).getChildIterator();\r
+\r
+        Node elem = null;\r
+        int i = 0;\r
+        while (children.hasMoreElements()) {\r
+            elem = (Node) children.nextElement();\r
+            if (elem.getType() == TokenStream.PRIMARY &&\r
+                elem.getInt() == TokenStream.UNDEFINED)\r
+            {\r
+                i++;\r
+                continue;\r
+            }\r
+            Node addelem = new Node(TokenStream.SETELEM, createUseTemp(temp),\r
+                                    new Node(TokenStream.NUMBER,\r
+                                             new Integer(i)),\r
+                                    elem);\r
+            i++;\r
+            result = new Node(TokenStream.COMMA, result, addelem);\r
+        }\r
+\r
+        /*\r
+         * If the version is 120, then new Array(4) means create a new\r
+         * array with 4 as the first element.  In this case, we might\r
+         * need to explicitly check against trailing undefined\r
+         * elements in the array literal, and set the length manually\r
+         * if these occur.  Otherwise, we can add an argument to the\r
+         * node specifying new Array() to provide the array length.\r
+         * (Which will make Array optimizations involving allocating a\r
+         * Java array to back the javascript array work better.)\r
+         */\r
+        if (Context.getContext().getLanguageVersion() == Context.VERSION_1_2) {\r
+            /* When last array element is empty, we need to set the\r
+             * length explicitly, because we can't depend on SETELEM\r
+             * to do it for us - because empty [,,] array elements\r
+             * never set anything at all. */\r
+            if (elem != null &&\r
+                elem.getType() == TokenStream.PRIMARY &&\r
+                elem.getInt() == TokenStream.UNDEFINED)\r
+            {\r
+                Node setlength = new Node(TokenStream.SETPROP,\r
+                                          createUseTemp(temp),\r
+                                          new Node(TokenStream.STRING,\r
+                                                   "length"),\r
+                                          new Node(TokenStream.NUMBER,\r
+                                                   new Integer(i)));\r
+                result = new Node(TokenStream.COMMA, result, setlength);\r
+            }\r
+        } else {\r
+            array.addChildToBack(new Node(TokenStream.NUMBER,\r
+                                          new Integer(i)));\r
+        }\r
+        return new Node(TokenStream.COMMA, result, createUseTemp(temp));\r
+    }\r
+\r
+    /**\r
+     * Object Literals\r
+     * <BR> createObjectLiteral rewrites its argument as object\r
+     * creation plus object property entries, so later compiler\r
+     * stages don't need to know about object literals.\r
+     */\r
+    public Object createObjectLiteral(Object obj) {\r
+        Node result = new Node(TokenStream.NEW, new Node(TokenStream.NAME,\r
+                                                         "Object"));\r
+        Node temp = createNewTemp(result);\r
+        result = temp;\r
+\r
+        java.util.Enumeration children = ((Node) obj).getChildIterator();\r
+\r
+        while (children.hasMoreElements()) {\r
+            Node elem = (Node)children.nextElement();\r
+\r
+            int op = (elem.getType() == TokenStream.NAME)\r
+                   ? TokenStream.SETPROP\r
+                   : TokenStream.SETELEM;\r
+            Node addelem = new Node(op, createUseTemp(temp),\r
+                                    elem, (Node)children.nextElement());\r
+            result = new Node(TokenStream.COMMA, result, addelem);\r
+        }\r
+        return new Node(TokenStream.COMMA, result, createUseTemp(temp));\r
+    }\r
+\r
+    /**\r
+     * Regular expressions\r
+     */\r
+    public Object createRegExp(String string, String flags) {\r
+        return flags.length() == 0\r
+               ? new Node(TokenStream.OBJECT,\r
+                          new Node(TokenStream.STRING, string))\r
+               : new Node(TokenStream.OBJECT,\r
+                          new Node(TokenStream.STRING, string),\r
+                          new Node(TokenStream.STRING, flags));\r
+    }\r
+\r
+    /**\r
+     * If statement\r
+     */\r
+    public Object createIf(Object cond, Object ifTrue, Object ifFalse,\r
+                           int lineno)\r
+    {\r
+        Node result = new Node(TokenStream.BLOCK, new Integer(lineno));\r
+        Node ifNotTarget = new Node(TokenStream.TARGET);\r
+        Node IFNE = new Node(TokenStream.IFNE, (Node) cond);\r
+        IFNE.putProp(Node.TARGET_PROP, ifNotTarget);\r
+\r
+        result.addChildToBack(IFNE);\r
+        result.addChildrenToBack((Node)ifTrue);\r
+\r
+        if (ifFalse != null) {\r
+            Node GOTOToEnd = new Node(TokenStream.GOTO);\r
+            Node endTarget = new Node(TokenStream.TARGET);\r
+            GOTOToEnd.putProp(Node.TARGET_PROP, endTarget);\r
+            result.addChildToBack(GOTOToEnd);\r
+            result.addChildToBack(ifNotTarget);\r
+            result.addChildrenToBack((Node)ifFalse);\r
+            result.addChildToBack(endTarget);\r
+        } else {\r
+            result.addChildToBack(ifNotTarget);\r
+        }\r
+\r
+       return result;\r
+    }\r
+\r
+    public Object createTernary(Object cond, Object ifTrue, Object ifFalse) {\r
+        return createIf(cond, ifTrue, ifFalse, -1);\r
+    }\r
+\r
+    /**\r
+     * Unary\r
+     */\r
+    public Object createUnary(int nodeType, Object child) {\r
+        Node childNode = (Node) child;\r
+        if (nodeType == TokenStream.DELPROP) {\r
+            int childType = childNode.getType();\r
+            Node left;\r
+            Node right;\r
+            if (childType == TokenStream.NAME) {\r
+                // Transform Delete(Name "a")\r
+                //  to Delete(Bind("a"), String("a"))\r
+                childNode.setType(TokenStream.BINDNAME);\r
+                left = childNode;\r
+                right = childNode.cloneNode();\r
+                right.setType(TokenStream.STRING);\r
+            } else if (childType == TokenStream.GETPROP ||\r
+                       childType == TokenStream.GETELEM)\r
+            {\r
+                left = childNode.getFirstChild();\r
+                right = childNode.getLastChild();\r
+                childNode.removeChild(left);\r
+                childNode.removeChild(right);\r
+            } else {\r
+                return new Node(TokenStream.PRIMARY,\r
+                                new Integer(TokenStream.TRUE));\r
+            }\r
+            return new Node(nodeType, left, right);\r
+        }\r
+       return new Node(nodeType, childNode);\r
+    }\r
+\r
+    public Object createUnary(int nodeType, int nodeOp, Object child) {\r
+        Node childNode = (Node) child;\r
+        int childType = childNode.getType();\r
+        if (nodeOp == TokenStream.TYPEOF &&\r
+            childType == TokenStream.NAME)\r
+        {\r
+            childNode.setType(TokenStream.TYPEOF);\r
+            return childNode;\r
+        }\r
+\r
+        if (nodeType == TokenStream.INC || nodeType == TokenStream.DEC) {\r
+\r
+            if (!hasSideEffects(childNode)\r
+                && (nodeOp == TokenStream.POST)\r
+                && (childType == TokenStream.NAME \r
+                    || childType == TokenStream.GETPROP\r
+                    || childType == TokenStream.GETELEM))\r
+            {\r
+                // if it's not a LHS type, createAssignment (below) will throw\r
+                // an exception.\r
+                return new Node(nodeType, childNode);\r
+            }\r
+\r
+            /*\r
+             * Transform INC/DEC ops to +=1, -=1,\r
+             * expecting later optimization of all +/-=1 cases to INC, DEC.\r
+             */\r
+            // we have to use Double for now, because\r
+            // 0.0 and 1.0 are stored as dconst_[01],\r
+            // and using a Float creates a stack mismatch.\r
+            Node rhs = (Node) createNumber(new Double(1.0));\r
+\r
+            return createAssignment(nodeType == TokenStream.INC\r
+                                        ? TokenStream.ADD\r
+                                        : TokenStream.SUB,\r
+                                    childNode,\r
+                                    rhs,\r
+                                    ScriptRuntime.NumberClass,\r
+                                    nodeOp == TokenStream.POST);\r
+        }\r
+\r
+        Node result = new Node(nodeType, new Integer(nodeOp));\r
+        result.addChildToBack((Node)child);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Binary\r
+     */\r
+    public Object createBinary(int nodeType, Object left, Object right) {\r
+        Node temp;\r
+        switch (nodeType) {\r
+\r
+          case TokenStream.DOT:\r
+            nodeType = TokenStream.GETPROP;\r
+            Node idNode = (Node) right;\r
+            idNode.setType(TokenStream.STRING);\r
+            String id = idNode.getString();\r
+            if (id.equals("__proto__") || id.equals("__parent__")) {\r
+                Node result = new Node(nodeType, (Node) left);\r
+                result.putProp(Node.SPECIAL_PROP_PROP, id);\r
+                return result;\r
+            }\r
+            break;\r
+\r
+          case TokenStream.LB:\r
+            // OPT: could optimize to GETPROP iff string can't be a number\r
+            nodeType = TokenStream.GETELEM;\r
+            break;\r
+/*\r
+          case TokenStream.AND:\r
+            temp = createNewTemp((Node) left);\r
+            return createTernary(temp, right, createUseTemp(temp));\r
+\r
+          case TokenStream.OR:\r
+            temp = createNewTemp((Node) left);\r
+            return createTernary(temp, createUseTemp(temp), right);\r
+*/            \r
+        }\r
+       return new Node(nodeType, (Node)left, (Node)right);\r
+    }\r
+\r
+    public Object createBinary(int nodeType, int nodeOp, Object left,\r
+                               Object right)\r
+    {\r
+        if (nodeType == TokenStream.ASSIGN) {\r
+            return createAssignment(nodeOp, (Node) left, (Node) right,\r
+                                    null, false);\r
+        }\r
+        return new Node(nodeType, (Node) left, (Node) right,\r
+                        new Integer(nodeOp));\r
+    }\r
+\r
+    public Object createAssignment(int nodeOp, Node left, Node right,\r
+                                   Class convert, boolean postfix)\r
+    {\r
+        int nodeType = left.getType();\r
+        String idString;\r
+        Node id = null;\r
+        switch (nodeType) {\r
+          case TokenStream.NAME:\r
+            return createSetName(nodeOp, left, right, convert, postfix);\r
+\r
+          case TokenStream.GETPROP:\r
+            idString = (String) left.getProp(Node.SPECIAL_PROP_PROP);\r
+            if (idString != null)\r
+                id = new Node(TokenStream.STRING, idString);\r
+            /* fall through */\r
+          case TokenStream.GETELEM:\r
+            if (id == null)\r
+                id = left.getLastChild();\r
+            return createSetProp(nodeType, nodeOp, left.getFirstChild(),\r
+                                 id, right, convert, postfix);\r
+          default:\r
+            // TODO: This should be a ReferenceError--but that's a runtime \r
+            //  exception. Should we compile an exception into the code?\r
+            reportError("msg.bad.lhs.assign");\r
+            return left;\r
+        }\r
+    }\r
+\r
+    private Node createConvert(Class toType, Node expr) {\r
+        if (toType == null)\r
+            return expr;\r
+        Node result = new Node(TokenStream.CONVERT, expr);\r
+        result.putProp(Node.TYPE_PROP, ScriptRuntime.NumberClass);\r
+        return result;\r
+    }\r
+\r
+    private Object createSetName(int nodeOp, Node left, Node right,\r
+                                 Class convert, boolean postfix)\r
+    {\r
+        if (nodeOp == TokenStream.NOP) {\r
+            left.setType(TokenStream.BINDNAME);\r
+            return new Node(TokenStream.SETNAME, left, right);\r
+        }\r
+\r
+        String s = left.getString();\r
+\r
+        if (s.equals("__proto__") || s.equals("__parent__")) {\r
+            Node result = new Node(TokenStream.SETPROP, left, right);\r
+            result.putProp(Node.SPECIAL_PROP_PROP, s);\r
+            return result;\r
+        }\r
+\r
+        Node opLeft = new Node(TokenStream.NAME, s);\r
+        if (convert != null)\r
+            opLeft = createConvert(convert, opLeft);\r
+        if (postfix)\r
+            opLeft = createNewTemp(opLeft);\r
+        Node op = new Node(nodeOp, opLeft, right);\r
+\r
+        Node lvalueLeft = new Node(TokenStream.BINDNAME, s);\r
+        Node result = new Node(TokenStream.SETNAME, lvalueLeft, op);\r
+        if (postfix) {\r
+            result = new Node(TokenStream.COMMA, result,\r
+                              createUseTemp(opLeft));\r
+        }\r
+        return result;\r
+    }\r
+\r
+    public Node createNewTemp(Node n) {\r
+        int type = n.getType();\r
+        if (type == TokenStream.STRING || type == TokenStream.NUMBER) {\r
+            // Optimization: clone these values rather than storing\r
+            // and loading from a temp\r
+            return n;\r
+        }\r
+        Node result = new Node(TokenStream.NEWTEMP, n);\r
+        return result;\r
+    }\r
+\r
+    public Node createUseTemp(Node newTemp) {\r
+        int type = newTemp.getType();\r
+        if (type == TokenStream.NEWTEMP) {\r
+            Node result = new Node(TokenStream.USETEMP);\r
+            result.putProp(Node.TEMP_PROP, newTemp);\r
+            Integer n = (Integer) newTemp.getProp(Node.USES_PROP);\r
+            if (n == null) {\r
+                n = new Integer(1);\r
+            } else {\r
+                if (n.intValue() < Integer.MAX_VALUE)\r
+                    n = new Integer(n.intValue() + 1);\r
+            }\r
+            newTemp.putProp(Node.USES_PROP, n);\r
+            return result;\r
+        }\r
+        return newTemp.cloneNode();\r
+    }\r
+\r
+    public Node createNewLocal(Node n) {\r
+        Node result = new Node(TokenStream.NEWLOCAL, n);\r
+        return result;\r
+    }\r
+\r
+    public Node createUseLocal(Node newLocal) {\r
+        int type = newLocal.getType();\r
+        if (type == TokenStream.NEWLOCAL) {\r
+            Node result = new Node(TokenStream.USELOCAL);\r
+            result.putProp(Node.LOCAL_PROP, newLocal);\r
+            return result;\r
+        }\r
+        return newLocal.cloneNode();    // what's this path for ?\r
+    }\r
+\r
+    public static boolean hasSideEffects(Node exprTree) {\r
+        switch (exprTree.getType()) {\r
+            case TokenStream.INC:\r
+            case TokenStream.DEC:\r
+            case TokenStream.SETPROP:\r
+            case TokenStream.SETELEM:\r
+            case TokenStream.SETNAME:\r
+            case TokenStream.CALL:\r
+            case TokenStream.NEW:\r
+                return true;\r
+            default:\r
+                Node child = exprTree.getFirstChild();\r
+                while (child != null) {\r
+                    if (hasSideEffects(child))\r
+                        return true;\r
+                    else\r
+                        child = child.getNextSibling();\r
+                }\r
+                break;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    private Node createSetProp(int nodeType, int nodeOp, Node obj, Node id,\r
+                               Node expr, Class convert, boolean postfix)\r
+    {\r
+        int type = nodeType == TokenStream.GETPROP\r
+                   ? TokenStream.SETPROP\r
+                   : TokenStream.SETELEM;\r
+\r
+        Object datum = id.getDatum();\r
+        if (type == TokenStream.SETPROP && datum != null &&\r
+            datum instanceof String)\r
+        {\r
+            String s = (String) datum;\r
+            if (s.equals("__proto__") || s.equals("__parent__")) {\r
+                Node result = new Node(type, obj, expr);\r
+                result.putProp(Node.SPECIAL_PROP_PROP, s);\r
+                return result;\r
+            }\r
+        }\r
+\r
+        if (nodeOp == TokenStream.NOP)\r
+            return new Node(type, obj, id, expr);\r
+/*\r
+*    If the RHS expression could modify the LHS we have\r
+*    to construct a temporary to hold the LHS context\r
+*    prior to running the expression. Ditto, if the id\r
+*    expression has side-effects.\r
+*\r
+*    XXX If the hasSideEffects tests take too long, we\r
+*       could make this an optimizer-only transform\r
+*       and always do the temp assignment otherwise.\r
+*\r
+*/\r
+        Node tmp1, tmp2, opLeft;\r
+        if (hasSideEffects(expr)\r
+                || hasSideEffects(id)\r
+                || (obj.getType() != TokenStream.NAME))  {\r
+            tmp1 = createNewTemp(obj);\r
+            Node useTmp1 = createUseTemp(tmp1);\r
+\r
+            tmp2 = createNewTemp(id);\r
+            Node useTmp2 = createUseTemp(tmp2);\r
+\r
+            opLeft = new Node(nodeType, useTmp1, useTmp2);\r
+        } else {\r
+            tmp1 = obj.cloneNode();\r
+            tmp2 = id.cloneNode();\r
+            opLeft = new Node(nodeType, obj, id);\r
+        }\r
+\r
+        if (convert != null)\r
+            opLeft = createConvert(convert, opLeft);\r
+        if (postfix)\r
+            opLeft = createNewTemp(opLeft);\r
+        Node op = new Node(nodeOp, opLeft, expr);\r
+\r
+        Node result = new Node(type, tmp1, tmp2, op);\r
+        if (postfix) {\r
+            result = new Node(TokenStream.COMMA, result,\r
+                              createUseTemp(opLeft));\r
+        }\r
+\r
+        return result;\r
+    }\r
+    \r
+    private void reportError(String msgResource) {\r
+\r
+        if (scope != null)\r
+            throw NativeGlobal.constructError(\r
+                        Context.getContext(), "SyntaxError",\r
+                        ScriptRuntime.getMessage0(msgResource),\r
+                        scope);\r
+        else {\r
+            String message = Context.getMessage0(msgResource);\r
+            Context.reportError(message, ts.getSourceName(), ts.getLineno(), \r
+                                ts.getLine(), ts.getOffset());\r
+        }\r
+    }\r
+    \r
+    // Only needed to get file/line information. Could create an interface\r
+    // that TokenStream implements if we want to make the connection less\r
+    // direct.\r
+    private TokenStream ts;\r
+    \r
+    // Only needed to pass to the Erorr exception constructors\r
+    private Scriptable scope;\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/IdFunction.java b/src/org/mozilla/javascript/IdFunction.java
new file mode 100644 (file)
index 0000000..ec5dc3e
--- /dev/null
@@ -0,0 +1,164 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Igor Bukanov\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+public class IdFunction extends BaseFunction\r
+{\r
+    public static final int FUNCTION_ONLY = 0;\r
+\r
+    public static final int CONSTRUCTOR_ONLY = 1;\r
+\r
+    public static final int FUNCTION_AND_CONSTRUCTOR = 2;\r
+\r
+    public IdFunction(IdFunctionMaster master, String name, int id) {\r
+        this.functionName = name;\r
+        this.master = master;\r
+        this.methodId = id;\r
+    }\r
+    \r
+    public final int functionType() {\r
+        return functionType;\r
+    }\r
+    \r
+    public void setFunctionType(int type) {\r
+        functionType = type;\r
+    }\r
+    \r
+    public Scriptable getPrototype() {\r
+        // Lazy initialization of prototype: for native functions this\r
+        // may not be called at all\r
+        Scriptable proto = super.getPrototype(); \r
+        if (proto == null) {\r
+            proto = getFunctionPrototype(getParentScope());\r
+            setPrototype(proto);\r
+        }\r
+        return proto;\r
+    }\r
+\r
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj, \r
+                       Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (functionType != CONSTRUCTOR_ONLY) {\r
+            return master.execMethod(methodId, this, cx, scope, thisObj, args);\r
+        }\r
+        else {\r
+            return Undefined.instance;\r
+        }\r
+    }\r
+\r
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (functionType != FUNCTION_ONLY) {\r
+            // It is program error not to return Scriptable from constructor\r
+            Scriptable result = (Scriptable)master.execMethod(methodId, this,\r
+                                                              cx, scope, \r
+                                                              null, args);\r
+            postConstruction(result);\r
+            return result;\r
+        }\r
+        else {\r
+            return Undefined.instance;\r
+        }\r
+    }\r
+\r
+    public String decompile(Context cx, int indent, boolean justbody) {\r
+        StringBuffer sb = new StringBuffer();\r
+        if (!justbody) {\r
+            sb.append("function ");\r
+            sb.append(getFunctionName());\r
+            sb.append("() { ");\r
+        }\r
+        sb.append("[native code for ");\r
+        if (master instanceof Scriptable) {\r
+            Scriptable smaster = (Scriptable)master;\r
+            sb.append(smaster.getClassName());\r
+            sb.append('.');\r
+        }\r
+        sb.append(getFunctionName());\r
+        sb.append(", arity=");\r
+        sb.append(getArity());\r
+        sb.append(justbody ? "]\n" : "] }\n");\r
+        return sb.toString();\r
+    }\r
+    \r
+    public int getArity() {\r
+        int arity = master.methodArity(methodId);\r
+        if (arity < 0) { \r
+            throw onBadMethodId(master, methodId);\r
+        }\r
+        return arity;\r
+    }\r
+    \r
+    public int getLength() { return getArity(); }\r
+\r
+    /** Prepare to be used as constructor .\r
+     ** @param scope constructor scope\r
+     ** @param prototype DontEnum, DontDelete, ReadOnly prototype property \r
+     ** of the constructor */\r
+    public void initAsConstructor(Scriptable scope, Scriptable prototype) {\r
+        setFunctionType(FUNCTION_AND_CONSTRUCTOR);\r
+        setParentScope(scope);\r
+        setImmunePrototypeProperty(prototype);\r
+    }\r
+    \r
+    static RuntimeException onBadMethodId(IdFunctionMaster master, int id) {\r
+        // It is program error to call id-like methods for unknown or \r
+        // non-function id\r
+        return new RuntimeException("BAD FUNCTION ID="+id+" MASTER="+master);\r
+    }\r
+\r
+    // Copied from NativeFunction.construct\r
+    private void postConstruction(Scriptable newObj) {\r
+        if (newObj.getPrototype() == null) {\r
+            newObj.setPrototype(getClassPrototype());\r
+        }\r
+        if (newObj.getParentScope() == null) {\r
+            Scriptable parent = getParentScope();\r
+            if (newObj != parent) {\r
+                newObj.setParentScope(parent);\r
+            }\r
+        }\r
+    }\r
+\r
+    protected IdFunctionMaster master;\r
+    protected int methodId;\r
+\r
+    protected int functionType = FUNCTION_ONLY;\r
+}\r
diff --git a/src/org/mozilla/javascript/IdFunctionMaster.java b/src/org/mozilla/javascript/IdFunctionMaster.java
new file mode 100644 (file)
index 0000000..0a04e58
--- /dev/null
@@ -0,0 +1,54 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Igor Bukanov\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/** Master for id-based functions that knows their properties and how to\r
+ ** execute them\r
+ */\r
+public interface IdFunctionMaster {\r
+    /** 'thisObj' will be null if invoked as constructor, in which case\r
+     ** instance of Scriptable should be returned */\r
+    public Object execMethod(int methodId, IdFunction function,\r
+                             Context cx, Scriptable scope,\r
+                             Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException;\r
+\r
+    /** Get arity or defined argument count for method with given id.\r
+     ** Should return -1 if methodId is not known or can not be used\r
+     ** with execMethod call */\r
+    public int methodArity(int methodId);\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/IdScriptable.java b/src/org/mozilla/javascript/IdScriptable.java
new file mode 100644 (file)
index 0000000..84fbc92
--- /dev/null
@@ -0,0 +1,577 @@
+/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Igor Bukanov\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+Base class for native object implementation that uses IdFunction to export its methods to script via <class-name>.prototype object.\r
+\r
+Any descendant should implement at least the following methods:\r
+    mapNameToId\r
+    getIdName\r
+    execMethod\r
+    methodArity\r
+\r
+To define non-function properties, the descendant should customize\r
+    getIdValue\r
+    setIdValue\r
+    getIdDefaultAttributes\r
+    maxInstanceId\r
+to get/set property value and provide its default attributes.\r
+\r
+To customize initializition of constructor and protype objects, descendant\r
+may override scopeInit or fillConstructorProperties methods.\r
+\r
+*/\r
+public abstract class IdScriptable extends ScriptableObject\r
+    implements IdFunctionMaster\r
+{\r
+    /** NULL_TAG can be used to distinguish between uninitialized and null\r
+     ** values\r
+     */\r
+    protected static final Object NULL_TAG = new Object();\r
+\r
+    public IdScriptable() {\r
+        activateIdMap(maxInstanceId());\r
+    }\r
+    \r
+    public boolean has(String name, Scriptable start) {\r
+        if (maxId != 0) {\r
+            int id = mapNameToId(name);\r
+            if (id != 0) {\r
+                return hasValue(id);\r
+            }\r
+        }\r
+        return super.has(name, start);\r
+    }\r
+\r
+    public Object get(String name, Scriptable start) {\r
+        if (CACHE_NAMES) {\r
+            int maxId = this.maxId;\r
+            L:if (maxId != 0) {\r
+                Object[] data = idMapData;\r
+                if (data == null) { \r
+                    int id = mapNameToId(name);\r
+                    if (id != 0) {\r
+                        return getIdValue(id);\r
+                    }\r
+                }\r
+                else {\r
+                    int id = lastIdCache;\r
+                    if (data[id - 1 + maxId] != name) {\r
+                        id = mapNameToId(name);\r
+                        if (id == 0) { break L; }\r
+                           data[id - 1 + maxId] = name;\r
+                           lastIdCache = id;\r
+                    }\r
+                    Object value = data[id - 1];\r
+                    if (value == null) {\r
+                        value = getIdValue(id);\r
+                    }\r
+                    else if (value == NULL_TAG) {\r
+                        value = null;\r
+                    }\r
+                    return value;\r
+                }\r
+            }\r
+        }\r
+        else {\r
+            if (maxId != 0) {\r
+                int id = mapNameToId(name);\r
+                if (id != 0) {\r
+                    Object[] data = idMapData;\r
+                    if (data == null) { \r
+                        return getIdValue(id);\r
+                    }\r
+                    else {\r
+                        Object value = data[id - 1];\r
+                        if (value == null) {\r
+                            value = getIdValue(id);\r
+                        }\r
+                        else if (value == NULL_TAG) {\r
+                            value = null;\r
+                        }\r
+                        return value;\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        return super.get(name, start);\r
+    }\r
+\r
+    public void put(String name, Scriptable start, Object value) {\r
+        if (maxId != 0) {\r
+            int id = mapNameToId(name);\r
+            if (id != 0) {\r
+                int attr = getAttributes(id);\r
+                if ((attr & READONLY) == 0) {\r
+                    if (start == this) {\r
+                        setIdValue(id, value);\r
+                    }\r
+                    else {\r
+                        start.put(name, start, value);\r
+                    }\r
+                }\r
+                return;\r
+            }\r
+        }\r
+        super.put(name, start, value);\r
+    }\r
+\r
+    public void delete(String name) {\r
+        if (maxId != 0) {\r
+            int id = mapNameToId(name);\r
+            if (id != 0) {\r
+                // Let the super class to throw exceptions for sealed objects\r
+                if (!isSealed()) {\r
+                    int attr = getAttributes(id);\r
+                    if ((attr & PERMANENT) == 0) {\r
+                        deleteIdValue(id);\r
+                    }\r
+                    return;\r
+                }\r
+            }\r
+        }\r
+        super.delete(name);\r
+    }\r
+\r
+    public int getAttributes(String name, Scriptable start)\r
+        throws PropertyException\r
+    {\r
+        if (maxId != 0) {\r
+            int id = mapNameToId(name);\r
+            if (id != 0) {\r
+                if (hasValue(id)) {\r
+                    return getAttributes(id);\r
+                }\r
+                // For ids with deleted values super will throw exceptions\r
+            }\r
+        }\r
+        return super.getAttributes(name, start);\r
+    }\r
+\r
+    public void setAttributes(String name, Scriptable start,\r
+                              int attributes)\r
+        throws PropertyException\r
+    {\r
+        if (maxId != 0) {\r
+            int id = mapNameToId(name);\r
+            if (id != 0) {\r
+                if (hasValue(id)) {\r
+                    synchronized (this) {\r
+                        setAttributes(id, attributes);\r
+                    }\r
+                    return;\r
+                }\r
+                // For ids with deleted values super will throw exceptions\r
+            }\r
+        }\r
+        super.setAttributes(name, start, attributes);\r
+    }\r
+\r
+    synchronized void addPropertyAttribute(int attribute) {\r
+        extraIdAttributes |= (byte)attribute;\r
+        super.addPropertyAttribute(attribute);\r
+    }\r
+\r
+    /**\r
+     * Redefine ScriptableObject.defineProperty to allow changing\r
+     * values/attributes of id-based properties unless \r
+     * getIdDefaultAttributes contains the READONLY attribute.\r
+     * @see #getIdDefaultAttributes\r
+     * @see org.mozilla.javascript.ScriptableObject#defineProperty\r
+     */\r
+    public void defineProperty(String propertyName, Object value,\r
+                               int attributes)\r
+    {\r
+        if (maxId != 0) {\r
+            int id = mapNameToId(propertyName);\r
+            if (id != 0) {\r
+                int default_attributes = getIdDefaultAttributes(id);\r
+                if ((default_attributes & READONLY) != 0) {\r
+                    // It is a bug to redefine id with readonly attributes\r
+                    throw new RuntimeException\r
+                        ("Attempt to redefine read-only id " + propertyName);\r
+                }\r
+                setAttributes(id, attributes);\r
+                setIdValue(id, value);\r
+                return;\r
+            }\r
+        }\r
+        super.defineProperty(propertyName, value, attributes);\r
+    }\r
+\r
+    Object[] getIds(boolean getAll) {\r
+        Object[] result = super.getIds(getAll);\r
+        \r
+        if (maxId != 0) {\r
+            Object[] ids = null;\r
+            int count = 0;\r
+            \r
+            for (int id = maxId; id != 0; --id) {\r
+                if (hasValue(id)) {\r
+                    if (getAll || (getAttributes(id) & DONTENUM) == 0) {\r
+                        if (count == 0) {\r
+                            // Need extra room for nor more then [1..id] names\r
+                            ids = new Object[id];\r
+                        }\r
+                        ids[count++] = getIdName(id);\r
+                    }\r
+                }\r
+            }\r
+            if (count != 0) {\r
+                if (result.length == 0 && ids.length == count) {\r
+                    result = ids;\r
+                }\r
+                else {\r
+                    Object[] tmp = new Object[result.length + count];\r
+                    System.arraycopy(result, 0, tmp, 0, result.length);\r
+                    System.arraycopy(ids, 0, tmp, result.length, count);\r
+                    result = tmp;\r
+                }\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+\r
+    /** Return maximum id number that should be present in each instance. */\r
+    protected int maxInstanceId() { return 0; }\r
+\r
+    /**\r
+     * Map name to id of prototype or instance property.\r
+     * Should return 0 if not found\r
+     */\r
+    protected abstract int mapNameToId(String name);\r
+\r
+    /** Map id back to property name it defines.\r
+     */\r
+    protected abstract String getIdName(int id);\r
+\r
+    /** Get default attributes for id. \r
+     ** Default implementation return DONTENUM that is the standard attribute \r
+     ** for core EcmaScript function. Typically descendants need to overwrite\r
+     ** this for non-function attributes like length to return\r
+     ** DONTENUM | READONLY | PERMANENT or DONTENUM | PERMANENT\r
+     */\r
+    protected int getIdDefaultAttributes(int id) {\r
+        return DONTENUM;\r
+    }\r
+\r
+    /** Check if id value exists.\r
+     ** Default implementation always returns true */\r
+    protected boolean hasIdValue(int id) {\r
+        return true;\r
+    }\r
+\r
+    /** Get id value. \r
+     ** If id value is constant, descendant can call cacheIdValue to store\r
+     ** value in the permanent cache.\r
+     ** Default implementation creates IdFunction instance for given id\r
+     ** and cache its value\r
+     */\r
+    protected Object getIdValue(int id) {\r
+        IdFunction f = newIdFunction(id);\r
+        f.setParentScope(getParentScope());\r
+        return cacheIdValue(id, f);\r
+    }\r
+\r
+    /**\r
+     * Set id value. \r
+     * IdScriptable never calls this method if result of\r
+     * <code>getIdDefaultAttributes(id)</code> contains READONLY attribute.\r
+     * Descendants can overwrite this method to provide custom handler for\r
+     * property assignments.\r
+     */\r
+    protected void setIdValue(int id, Object value) {\r
+        synchronized (this) {\r
+            ensureIdData()[id - 1] = (value != null) ? value : NULL_TAG;\r
+        }\r
+    }\r
+    \r
+    /**\r
+     * Store value in permanent cache unless value was already assigned to id.\r
+     * After this call IdScriptable never calls hasIdValue and getIdValue \r
+     * for the given id.\r
+     */\r
+    protected Object cacheIdValue(int id, Object value) {\r
+        synchronized (this) {\r
+            Object[] data = ensureIdData();\r
+            Object curValue = data[id - 1];\r
+            if (curValue == null) {\r
+                data[id - 1] = (value != null) ? value : NULL_TAG;\r
+            }\r
+            else {\r
+                value = curValue;\r
+            }\r
+        }\r
+        return value;\r
+    }\r
+    \r
+    /**\r
+     * Delete value represented by id so hasIdValue return false. \r
+     * IdScriptable never calls this method if result of\r
+     * <code>getIdDefaultAttributes(id)</code> contains PERMANENT attribute.\r
+     * Descendants can overwrite this method to provide custom handler for\r
+     * property delete.\r
+     */\r
+    protected void deleteIdValue(int id) {\r
+        synchronized (this) {\r
+            ensureIdData()[id - 1] = NOT_FOUND;\r
+        }\r
+    }\r
+    \r
+    /** 'thisObj' will be null if invoked as constructor, in which case\r
+     ** instance of Scriptable should be returned. */\r
+    public Object execMethod(int methodId, IdFunction function,\r
+                             Context cx, Scriptable scope,\r
+                             Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        throw IdFunction.onBadMethodId(this, methodId);\r
+    }\r
+\r
+    /** Get arity or defined argument count for method with given id. \r
+     ** Should return -1 if methodId is not known or can not be used\r
+     ** with execMethod call. */\r
+    public int methodArity(int methodId) {\r
+        return -1;\r
+    }\r
+    \r
+    /** Activate id support with the given maximum id */\r
+    protected void activateIdMap(int maxId) {\r
+        this.maxId = maxId;\r
+    }\r
+    \r
+    /** Sets whether newly constructed function objects should be sealed */\r
+    protected void setSealFunctionsFlag(boolean sealed) {\r
+        setSetupFlag(SEAL_FUNCTIONS_FLAG, sealed);\r
+    }\r
+    \r
+    /** \r
+     * Set parameters of function properties. \r
+     * Currently only determines whether functions should use dynamic scope.\r
+     * @param cx context to read function parameters.\r
+     * \r
+     * @see org.mozilla.javascript.Context#hasCompileFunctionsWithDynamicScope\r
+     */\r
+    protected void setFunctionParametrs(Context cx) {\r
+        setSetupFlag(USE_DYNAMIC_SCOPE_FLAG,\r
+                     cx.hasCompileFunctionsWithDynamicScope());\r
+    }\r
+    \r
+    private void setSetupFlag(int flag, boolean value) {\r
+        setupFlags = (byte)(value ? setupFlags | flag : setupFlags & ~flag);\r
+    }\r
+\r
+    /** \r
+     * Prepare this object to serve as the prototype property of constructor \r
+     * object with name <code>getClassName()<code> defined in\r
+     * <code>scope</code>.\r
+     * @param maxId maximum id available in prototype object\r
+     * @param cx current context\r
+     * @param scope object to define constructor in.\r
+     * @param sealed indicates whether object and all its properties should \r
+     *        be sealed \r
+     */ \r
+    public void addAsPrototype(int maxId, Context cx, Scriptable scope, \r
+                               boolean sealed) \r
+    {\r
+        activateIdMap(maxId);\r
+\r
+        setSealFunctionsFlag(sealed);\r
+        setFunctionParametrs(cx);\r
+        \r
+        int constructorId = mapNameToId("constructor");\r
+        if (constructorId == 0) {\r
+            // It is a bug to call this function without id for constructor \r
+            throw new RuntimeException("No id for constructor property");\r
+        }\r
+\r
+        IdFunction ctor = newIdFunction(constructorId);\r
+        ctor.initAsConstructor(scope, this);\r
+        fillConstructorProperties(cx, ctor, sealed);\r
+        if (sealed) {\r
+            ctor.sealObject();\r
+            ctor.addPropertyAttribute(READONLY);\r
+        }\r
+\r
+        setParentScope(ctor);\r
+        setPrototype(getObjectPrototype(scope));\r
+        cacheIdValue(constructorId, ctor);\r
+\r
+        if (sealed) {\r
+            sealObject();\r
+        }\r
+\r
+        defineProperty(scope, getClassName(), ctor, ScriptableObject.DONTENUM);\r
+    }\r
+\r
+    protected void fillConstructorProperties\r
+        (Context cx, IdFunction ctor, boolean sealed)\r
+    {\r
+    }\r
+\r
+    protected void addIdFunctionProperty\r
+        (Scriptable obj, int id, boolean sealed)\r
+    {\r
+        IdFunction f = newIdFunction(id);\r
+        if (sealed) { f.sealObject(); }\r
+        defineProperty(obj, getIdName(id), f, DONTENUM);\r
+    }\r
+\r
+    /** \r
+     * Utility method for converting target object into native this.\r
+     * Possible usage would be to have a private function like realThis:\r
+     * <pre>\r
+       private NativeSomething realThis(Scriptable thisObj,\r
+                                        IdFunction f, boolean readOnly)\r
+       {\r
+           while (!(thisObj instanceof NativeSomething)) {\r
+               thisObj = nextInstanceCheck(thisObj, f, readOnly);\r
+           }\r
+           return (NativeSomething)thisObj;\r
+       }\r
+    * </pre>\r
+    * Note that although such function can be implemented universally via\r
+    * java.lang.Class.isInstance(), it would be much more slower.\r
+    * @param readOnly specify if the function f does not change state of object.\r
+    * @return Scriptable object suitable for a check by the instanceof operator.\r
+    * @throws RuntimeException if no more instanceof target can be found\r
+    */\r
+    protected Scriptable nextInstanceCheck(Scriptable thisObj,\r
+                                           IdFunction f,\r
+                                           boolean readOnly)\r
+    {\r
+        if (readOnly && 0 != (setupFlags & USE_DYNAMIC_SCOPE_FLAG)) {\r
+            // for read only functions under dynamic scope look prototype chain\r
+            thisObj = thisObj.getPrototype();\r
+            if (thisObj != null) { return thisObj; }\r
+        }\r
+        throw NativeGlobal.typeError1("msg.incompat.call", \r
+                                      f.getFunctionName(), f);\r
+    }\r
+\r
+    protected IdFunction newIdFunction(int id) {\r
+        IdFunction f = new IdFunction(this, getIdName(id), id);\r
+        if (0 != (setupFlags & SEAL_FUNCTIONS_FLAG)) { f.sealObject(); }\r
+        return f;\r
+    }\r
+\r
+    protected final Object wrap_double(double x) {\r
+        return (x == x) ? new Double(x) : ScriptRuntime.NaNobj;\r
+    }\r
+\r
+    protected final Object wrap_int(int x) {\r
+        byte b = (byte)x;\r
+        if (b == x) { return new Byte(b); }\r
+        return new Integer(x);\r
+    }\r
+\r
+    protected final Object wrap_long(long x) {\r
+        int i = (int)x;\r
+        if (i == x) { return wrap_int(i); }\r
+        return new Long(x);\r
+    }\r
+\r
+    protected final Object wrap_boolean(boolean x) {\r
+        return x ? Boolean.TRUE : Boolean.FALSE;\r
+    }\r
+    \r
+    private boolean hasValue(int id) {\r
+        Object value;\r
+        Object[] data = idMapData;\r
+        if (data == null || (value = data[id - 1]) == null) {\r
+            return hasIdValue(id);\r
+        }\r
+        else {\r
+            return value != NOT_FOUND;\r
+        }\r
+    }\r
+\r
+    // Must be called only from synchronized (this)\r
+    private Object[] ensureIdData() {\r
+        Object[] data = idMapData;\r
+        if (data == null) { \r
+            idMapData = data = new Object[CACHE_NAMES ? maxId * 2 : maxId];\r
+        }\r
+        return data;\r
+    }\r
+    \r
+    private int getAttributes(int id) {\r
+        int attributes = getIdDefaultAttributes(id) | extraIdAttributes;\r
+        byte[] array = attributesArray;\r
+        if (array != null) {\r
+            attributes |= 0xFF & array[id - 1];\r
+        }\r
+        return attributes;\r
+    }\r
+\r
+    private void setAttributes(int id, int attributes) {\r
+        int defaultAttrs = getIdDefaultAttributes(id);\r
+        if ((attributes & defaultAttrs) != defaultAttrs) {\r
+            // It is a bug to set attributes to less restrictive values \r
+            // then given by defaultAttrs\r
+            throw new RuntimeException("Attempt to unset default attributes");\r
+        }\r
+        // Store only additional bits\r
+        attributes &= ~defaultAttrs;\r
+        byte[] array = attributesArray;\r
+        if (array == null && attributes != 0) {\r
+            synchronized (this) {\r
+                array = attributesArray;\r
+                if (array == null) {\r
+                    attributesArray = array = new byte[maxId];\r
+                }\r
+            }\r
+        }\r
+        if (array != null) {\r
+            array[id - 1] = (byte)attributes;\r
+        }\r
+    }\r
+\r
+    private int maxId;\r
+    private Object[] idMapData;\r
+    private byte[] attributesArray;\r
+\r
+    private static final boolean CACHE_NAMES = true;\r
+    private int lastIdCache;\r
+\r
+    private static final int USE_DYNAMIC_SCOPE_FLAG = 1 << 0;\r
+    private static final int SEAL_FUNCTIONS_FLAG    = 1 << 1;\r
+    \r
+    private byte setupFlags;\r
+    private byte extraIdAttributes;\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/ImporterTopLevel.java b/src/org/mozilla/javascript/ImporterTopLevel.java
new file mode 100644 (file)
index 0000000..ea4540d
--- /dev/null
@@ -0,0 +1,176 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Matthias Radestock\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.Vector;\r
+\r
+/**\r
+ * Class ImporterTopLevel\r
+ * \r
+ * This class defines a ScriptableObject that can be instantiated \r
+ * as a top-level ("global") object to provide functionality similar\r
+ * to Java's "import" statement.\r
+ * <p>\r
+ * This class can be used to create a top-level scope using the following code: \r
+ * <pre>\r
+ *  Scriptable scope = new ImporterTopLevel(cx);\r
+ * </pre>\r
+ * Then JavaScript code will have access to the following methods:\r
+ * <ul>\r
+ * <li>importClass - will "import" a class by making its unqualified name \r
+ *                   available as a property of the top-level scope\r
+ * <li>importPackage - will "import" all the classes of the package by \r
+ *                     searching for unqualified names as classes qualified\r
+ *                     by the given package.\r
+ * </ul>\r
+ * The following code from the shell illustrates this use:\r
+ * <pre>\r
+ * js> importClass(java.io.File)\r
+ * js> f = new File('help.txt')\r
+ * help.txt\r
+ * js> importPackage(java.util)\r
+ * js> v = new Vector()\r
+ * []\r
+ * \r
+ * @author Norris Boyd\r
+ */\r
+public class ImporterTopLevel extends ScriptableObject {\r
+    \r
+    /**\r
+     * @deprecated\r
+     */\r
+    public ImporterTopLevel() {\r
+        init();\r
+    }\r
+\r
+    public ImporterTopLevel(Context cx) {\r
+        cx.initStandardObjects(this);\r
+        init();\r
+    }\r
+    \r
+    private void init() {\r
+        String[] names = { "importClass", "importPackage" };\r
+\r
+        try {\r
+            this.defineFunctionProperties(names, ImporterTopLevel.class,\r
+                                          ScriptableObject.DONTENUM);\r
+        } catch (PropertyException e) {\r
+            throw new Error();  // should never happen\r
+        }\r
+    }\r
+\r
+    public String getClassName() { \r
+        return "global";\r
+    }\r
+    \r
+    public Object get(String name, Scriptable start) {\r
+        Object result = super.get(name, start);\r
+        if (result != NOT_FOUND) \r
+            return result;\r
+        if (name.equals("_packages_")) \r
+            return result;\r
+        Object plist = ScriptableObject.getProperty(start,"_packages_");\r
+        if (plist == NOT_FOUND) \r
+            return result;\r
+        Context cx = Context.enter();\r
+        Object[] elements = cx.getElements((Scriptable)plist);\r
+        Context.exit();\r
+        for (int i=0; i < elements.length; i++) {\r
+            NativeJavaPackage p = (NativeJavaPackage) elements[i];\r
+            Object v = p.getPkgProperty(name, start, false);\r
+            if (v != null && !(v instanceof NativeJavaPackage)) {\r
+                if (result == NOT_FOUND) {\r
+                    result = v;\r
+                } else {\r
+                    throw Context.reportRuntimeError2(\r
+                        "msg.ambig.import", result.toString(), v.toString());\r
+                }\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+    \r
+    public static void importClass(Context cx, Scriptable thisObj,\r
+                                   Object[] args, Function funObj) {\r
+        for (int i=0; i<args.length; i++) {\r
+            Object cl = args[i];\r
+            if (!(cl instanceof NativeJavaClass)) {\r
+                throw Context.reportRuntimeError1(\r
+                    "msg.not.class", Context.toString(cl));\r
+            }\r
+            String s = ((NativeJavaClass) cl).getClassObject().getName();\r
+            String n = s.substring(s.lastIndexOf('.')+1);\r
+            Object val = thisObj.get(n, thisObj);\r
+            if (val != NOT_FOUND && val != cl) {\r
+                throw Context.reportRuntimeError1("msg.prop.defined", n);\r
+            }\r
+            //thisObj.defineProperty(n, cl, DONTENUM);\r
+            thisObj.put(n,thisObj,cl);\r
+        }\r
+    }\r
+    \r
+    public static void importPackage(Context cx, Scriptable thisObj,\r
+                                   Object[] args, Function funObj) {\r
+        Scriptable importedPackages;\r
+        Object plist = thisObj.get("_packages_", thisObj);\r
+        if (plist == NOT_FOUND) {\r
+            importedPackages = cx.newArray(thisObj,0);\r
+            thisObj.put("_packages_", thisObj, importedPackages);\r
+        }\r
+        else {\r
+            importedPackages = (Scriptable)plist;\r
+        }\r
+        for (int i=0; i<args.length; i++) {\r
+            Object pkg = args[i];\r
+            if (!(pkg instanceof NativeJavaPackage)) {\r
+                throw Context.reportRuntimeError1(\r
+                    "msg.not.pkg", Context.toString(pkg));\r
+            }\r
+            Object[] elements = cx.getElements(importedPackages);\r
+            for (int j=0; j < elements.length; j++) {\r
+                if (pkg == elements[j]) {\r
+                    pkg = null;\r
+                    break;\r
+                }\r
+            }\r
+            if (pkg != null)\r
+                importedPackages.put(elements.length,importedPackages,pkg);\r
+        }\r
+    }\r
+}\r
diff --git a/src/org/mozilla/javascript/InterpretedFunction.java b/src/org/mozilla/javascript/InterpretedFunction.java
new file mode 100644 (file)
index 0000000..7979d78
--- /dev/null
@@ -0,0 +1,135 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Roger Lawrence\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.*;\r
+import org.mozilla.javascript.debug.DebuggableScript;\r
+\r
+public class InterpretedFunction extends NativeFunction implements DebuggableScript {\r
+    \r
+    InterpretedFunction(Context cx,\r
+                        InterpreterData theData, \r
+                        String[] argNames, short argCount)\r
+    {\r
+        itsData = theData;\r
+        this.argNames = argNames;\r
+        this.argCount = argCount;\r
+        init(cx);\r
+    }\r
+    \r
+    void init(Context cx)\r
+    {\r
+        functionName = itsData.itsName;\r
+        source = itsData.itsSource;\r
+        nestedFunctions = itsData.itsNestedFunctions;\r
+        if (cx != null)\r
+            version = (short)cx.getLanguageVersion();\r
+    }\r
+    \r
+    InterpretedFunction(InterpretedFunction theOther,\r
+                        Scriptable theScope, Context cx)\r
+    {\r
+        itsData = theOther.itsData;\r
+        this.argNames = theOther.argNames;\r
+        this.argCount = theOther.argCount;\r
+        itsClosure = theScope;\r
+        init(cx);\r
+    }\r
+    \r
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj,\r
+                       Object[] args)\r
+        throws JavaScriptException\r
+    {            \r
+        Function temp = cx.currentFunction;\r
+        if (cx.stackDepth++ > 200) {\r
+            NativeError ne = new NativeError();\r
+            ne.put("message", ne, "maximum interpreter stack depth limit exceeded");\r
+            cx.stackDepth--;\r
+            throw new EcmaError(ne, cx.interpreterSourceFile, cx.interpreterLine, 0, "");\r
+        }\r
+        cx.currentFunction = this;\r
+        cx.interpreterSourceFile = itsData.itsSourceFile;\r
+        if (itsClosure != null)\r
+            scope = itsClosure;\r
+        else if (!itsData.itsUseDynamicScope)\r
+            scope = getParentScope();\r
+\r
+        if (itsData.itsCheckThis) \r
+            thisObj = ScriptRuntime.getThis(thisObj);\r
+        \r
+        if (itsData.itsNeedsActivation) {\r
+            scope = ScriptRuntime.initVarObj(cx, scope, this, thisObj, args);\r
+        }\r
+        try {\r
+            return Interpreter.interpret(cx, scope, thisObj, args, this,\r
+                                         itsData);\r
+        } finally {\r
+            if (itsData.itsNeedsActivation) {\r
+                ScriptRuntime.popActivation(cx);\r
+            }\r
+            cx.currentFunction = temp;\r
+            cx.stackDepth--;\r
+        }\r
+    }\r
+    \r
+    public boolean isFunction() {\r
+        return true;\r
+    }\r
+    \r
+    public Scriptable getScriptable() {\r
+        return this;\r
+    }\r
+    \r
+    public String getSourceName() {\r
+        return itsData.itsSourceFile;\r
+    }\r
+    \r
+    public int[] getLineNumbers() { \r
+        return itsData.itsLineNumberTable.getKeys();\r
+    }\r
+    \r
+    public boolean placeBreakpoint(int line) { // XXX throw exn?\r
+        return itsData.placeBreakpoint(line);\r
+    }\r
+    \r
+    public boolean removeBreakpoint(int line) {\r
+        return itsData.removeBreakpoint(line);\r
+    }\r
+    \r
+    InterpreterData itsData;\r
+    Scriptable itsClosure;\r
+}\r
+    \r
diff --git a/src/org/mozilla/javascript/InterpretedScript.java b/src/org/mozilla/javascript/InterpretedScript.java
new file mode 100644 (file)
index 0000000..2c7ef2e
--- /dev/null
@@ -0,0 +1,102 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Roger Lawrence\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import org.mozilla.javascript.debug.*;\r
+\r
+import java.util.*;\r
+\r
+public class InterpretedScript extends NativeScript implements DebuggableScript {\r
+\r
+    InterpretedScript(Context cx,\r
+                      InterpreterData theData, \r
+                      String[] argNames, short argCount)\r
+    {\r
+        itsData = theData;\r
+        this.argNames = argNames;\r
+        this.argCount = argCount;\r
+        functionName = "";\r
+        nestedFunctions = itsData.itsNestedFunctions;\r
+        version = (short)cx.getLanguageVersion();   \r
+    }\r
+    \r
+    public Object exec(Context cx, Scriptable scope)\r
+        throws JavaScriptException\r
+    {\r
+        return call(cx, scope, scope, null);    \r
+    }\r
+\r
+    public Object call(Context cx, Scriptable scope, \r
+                       Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        Function temp = cx.currentFunction;\r
+        cx.currentFunction = this;\r
+        cx.interpreterSourceFile = itsData.itsSourceFile;\r
+        scope = ScriptRuntime.initScript(cx, scope, this, thisObj, \r
+                                         itsData.itsFromEvalCode);\r
+        Object ret = Interpreter.interpret(cx, scope, thisObj, args, this, itsData);    \r
+        cx.currentFunction = temp;\r
+        return ret;\r
+    }\r
+    \r
+    public boolean isFunction() {\r
+        return false;\r
+    }\r
+    \r
+    public Scriptable getScriptable() {\r
+        return this;\r
+    }\r
+    \r
+    public String getSourceName() {\r
+        return itsData.itsSourceFile;\r
+    }\r
+    \r
+    public int[] getLineNumbers() {\r
+        return itsData.itsLineNumberTable.getKeys();\r
+    }\r
+    \r
+    public boolean placeBreakpoint(int line) { // XXX throw exn?\r
+        return itsData.placeBreakpoint(line);\r
+    }\r
+    \r
+    public boolean removeBreakpoint(int line) {\r
+        return itsData.removeBreakpoint(line);\r
+    }\r
+    \r
+    InterpreterData itsData;\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java
new file mode 100644 (file)
index 0000000..37fbc73
--- /dev/null
@@ -0,0 +1,2514 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Patrick Beard\r
+ * Norris Boyd\r
+ * Igor Bukanov\r
+ * Roger Lawrence\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.io.*;\r
+import java.util.Vector;\r
+import java.util.Enumeration;\r
+\r
+import org.mozilla.javascript.debug.*;\r
+\r
+public class Interpreter extends LabelTable {\r
+    \r
+    public static final boolean printICode = false;\r
+    \r
+    public IRFactory createIRFactory(TokenStream ts, \r
+                                     ClassNameHelper nameHelper, Scriptable scope) \r
+    {\r
+        return new IRFactory(ts, scope);\r
+    }\r
+    \r
+    public Node transform(Node tree, TokenStream ts, Scriptable scope) {\r
+        return (new NodeTransformer()).transform(tree, null, ts, scope);\r
+    }\r
+    \r
+    public Object compile(Context cx, Scriptable scope, Node tree, \r
+                          Object securityDomain,\r
+                          SecuritySupport securitySupport,\r
+                          ClassNameHelper nameHelper)\r
+        throws IOException\r
+    {\r
+        version = cx.getLanguageVersion();\r
+        itsData = new InterpreterData(0, 0, 0, securityDomain, \r
+                    cx.hasCompileFunctionsWithDynamicScope(), false);\r
+        if (tree instanceof FunctionNode) {\r
+            FunctionNode f = (FunctionNode) tree;\r
+            InterpretedFunction result = \r
+                generateFunctionICode(cx, scope, f, securityDomain);\r
+            result.itsData.itsFunctionType = f.getFunctionType();\r
+            createFunctionObject(result, scope);\r
+            return result;\r
+        }\r
+        return generateScriptICode(cx, scope, tree, securityDomain);\r
+    }\r
+       \r
+    private void generateICodeFromTree(Node tree, \r
+                                       VariableTable varTable, \r
+                                       boolean needsActivation,\r
+                                       Object securityDomain)\r
+    {\r
+        int theICodeTop = 0;\r
+        itsVariableTable = varTable;\r
+        itsData.itsNeedsActivation = needsActivation;\r
+        theICodeTop = generateICode(tree, theICodeTop);\r
+        itsData.itsICodeTop = theICodeTop;\r
+        if (itsEpilogLabel != -1)\r
+            markLabel(itsEpilogLabel, theICodeTop);\r
+        for (int i = 0; i < itsLabelTableTop; i++)\r
+            itsLabelTable[i].fixGotos(itsData.itsICode);            \r
+    }\r
+\r
+    private Object[] generateRegExpLiterals(Context cx,\r
+                                            Scriptable scope,\r
+                                            Vector regexps)\r
+    {\r
+        Object[] result = new Object[regexps.size()];\r
+        RegExpProxy rep = cx.getRegExpProxy();\r
+        for (int i = 0; i < regexps.size(); i++) {\r
+            Node regexp = (Node) regexps.elementAt(i);\r
+            Node left = regexp.getFirstChild();\r
+            Node right = regexp.getLastChild();\r
+            result[i] = rep.newRegExp(cx, scope, left.getString(), \r
+                                (left != right) ? right.getString() : null, false);\r
+            regexp.putProp(Node.REGEXP_PROP, new Integer(i));\r
+        }\r
+        return result;\r
+    }\r
+        \r
+    private InterpretedScript generateScriptICode(Context cx, \r
+                                                  Scriptable scope, \r
+                                                  Node tree,\r
+                                                  Object securityDomain)\r
+    {        \r
+        itsSourceFile = (String) tree.getProp(Node.SOURCENAME_PROP);\r
+        itsData.itsSourceFile = itsSourceFile;\r
+        itsFunctionList = (Vector) tree.getProp(Node.FUNCTION_PROP);        \r
+        debugSource = (StringBuffer) tree.getProp(Node.DEBUGSOURCE_PROP);\r
+        if (itsFunctionList != null)\r
+            generateNestedFunctions(scope, cx, securityDomain);\r
+        Object[] regExpLiterals = null;\r
+        Vector regexps = (Vector)tree.getProp(Node.REGEXP_PROP);\r
+        if (regexps != null) \r
+            regExpLiterals = generateRegExpLiterals(cx, scope, regexps);\r
+        \r
+        VariableTable varTable = (VariableTable)tree.getProp(Node.VARS_PROP);\r
+        // The default is not to generate debug information\r
+        boolean activationNeeded = cx.isGeneratingDebugChanged() && \r
+                                   cx.isGeneratingDebug();\r
+        generateICodeFromTree(tree, varTable, activationNeeded, securityDomain);\r
+        itsData.itsNestedFunctions = itsNestedFunctions;\r
+        itsData.itsRegExpLiterals = regExpLiterals;\r
+        if (printICode) dumpICode(itsData);\r
+                                                               \r
+        String[] argNames = itsVariableTable.getAllNames();\r
+        short argCount = (short)itsVariableTable.getParameterCount();\r
+        InterpretedScript\r
+            result = new InterpretedScript(cx, itsData, argNames, argCount);\r
+        if (cx.debugger != null) {\r
+            cx.debugger.handleCompilationDone(cx, result, debugSource);\r
+        }\r
+        return result;\r
+    }\r
+    \r
+    private void generateNestedFunctions(Scriptable scope,\r
+                                         Context cx, \r
+                                         Object securityDomain)\r
+    {\r
+        itsNestedFunctions = new InterpretedFunction[itsFunctionList.size()];\r
+        for (short i = 0; i < itsFunctionList.size(); i++) {\r
+            FunctionNode def = (FunctionNode)itsFunctionList.elementAt(i);\r
+            Interpreter jsi = new Interpreter();\r
+            jsi.itsSourceFile = itsSourceFile;\r
+            jsi.itsData = new InterpreterData(0, 0, 0, securityDomain,\r
+                            cx.hasCompileFunctionsWithDynamicScope(),\r
+                            def.getCheckThis());\r
+            jsi.itsData.itsFunctionType = def.getFunctionType();\r
+            jsi.itsInFunctionFlag = true;\r
+            jsi.debugSource = debugSource;\r
+            itsNestedFunctions[i] = jsi.generateFunctionICode(cx, scope, def, \r
+                                                              securityDomain);\r
+            def.putProp(Node.FUNCTION_PROP, new Short(i));\r
+        }\r
+    }        \r
+    \r
+    private InterpretedFunction \r
+    generateFunctionICode(Context cx, Scriptable scope, \r
+                          FunctionNode theFunction, Object securityDomain)\r
+    {\r
+        itsFunctionList = (Vector) theFunction.getProp(Node.FUNCTION_PROP);\r
+        if (itsFunctionList != null) \r
+            generateNestedFunctions(scope, cx, securityDomain);\r
+        Object[] regExpLiterals = null;\r
+        Vector regexps = (Vector)theFunction.getProp(Node.REGEXP_PROP);\r
+        if (regexps != null) \r
+            regExpLiterals = generateRegExpLiterals(cx, scope, regexps);\r
+\r
+        VariableTable varTable = theFunction.getVariableTable();\r
+        boolean needsActivation = theFunction.requiresActivation() ||\r
+                                  (cx.isGeneratingDebugChanged() && \r
+                                   cx.isGeneratingDebug());\r
+        generateICodeFromTree(theFunction.getLastChild(), \r
+                              varTable, needsActivation,\r
+                              securityDomain);\r
+            \r
+        itsData.itsName = theFunction.getFunctionName();\r
+        itsData.itsSourceFile = (String) theFunction.getProp(\r
+                                    Node.SOURCENAME_PROP);\r
+        itsData.itsSource = (String)theFunction.getProp(Node.SOURCE_PROP);\r
+        itsData.itsNestedFunctions = itsNestedFunctions;\r
+        itsData.itsRegExpLiterals = regExpLiterals;\r
+        if (printICode) dumpICode(itsData);            \r
+            \r
+        String[] argNames = itsVariableTable.getAllNames();\r
+        short argCount = (short)itsVariableTable.getParameterCount();\r
+        InterpretedFunction \r
+            result = new InterpretedFunction(cx, itsData, argNames, argCount); \r
+        if (cx.debugger != null) {\r
+            cx.debugger.handleCompilationDone(cx, result, debugSource);\r
+        }\r
+        return result;\r
+    }\r
+    \r
+    boolean itsInFunctionFlag;\r
+    Vector itsFunctionList;\r
+    \r
+    InterpreterData itsData;\r
+    VariableTable itsVariableTable;\r
+    int itsTryDepth = 0;\r
+    int itsStackDepth = 0;\r
+    int itsEpilogLabel = -1;\r
+    String itsSourceFile;\r
+    int itsLineNumber = 0;\r
+    InterpretedFunction[] itsNestedFunctions = null;\r
+    \r
+    private int updateLineNumber(Node node, int iCodeTop)\r
+    {\r
+        Object datum = node.getDatum();\r
+        if (datum == null || !(datum instanceof Number))\r
+            return iCodeTop;\r
+        short lineNumber = ((Number) datum).shortValue(); \r
+        if (lineNumber != itsLineNumber) {\r
+            itsLineNumber = lineNumber;\r
+            if (itsData.itsLineNumberTable == null && \r
+                Context.getCurrentContext().isGeneratingDebug())\r
+            {\r
+                itsData.itsLineNumberTable = new UintMap();\r
+            }\r
+            if (itsData.itsLineNumberTable != null) {\r
+                itsData.itsLineNumberTable.put(lineNumber, iCodeTop);\r
+            }\r
+            iCodeTop = addByte((byte) TokenStream.LINE, iCodeTop);\r
+            iCodeTop = addByte((byte)(lineNumber >> 8), iCodeTop);\r
+            iCodeTop = addByte((byte)(lineNumber & 0xff), iCodeTop);\r
+            \r
+        }\r
+        \r
+        return iCodeTop;\r
+    }\r
+    \r
+    private void badTree(Node node)\r
+    {\r
+        try {\r
+            out = new PrintWriter(new FileOutputStream("icode.txt", true));\r
+            out.println("Un-handled node : " + node.toString());\r
+            out.close();\r
+        }\r
+        catch (IOException x) {}\r
+        throw new RuntimeException("Un-handled node : "\r
+                                        + node.toString());\r
+    }\r
+    \r
+    private int generateICode(Node node, int iCodeTop) {\r
+        int type = node.getType();\r
+        Node child = node.getFirstChild();\r
+        Node firstChild = child;\r
+        switch (type) {\r
+            \r
+            case TokenStream.FUNCTION : {                                        \r
+                    iCodeTop = addByte((byte) TokenStream.CLOSURE, iCodeTop);\r
+                    Node fn = (Node) node.getProp(Node.FUNCTION_PROP);\r
+                    Short index = (Short) fn.getProp(Node.FUNCTION_PROP);\r
+                    iCodeTop = addByte((byte)(index.shortValue() >> 8), iCodeTop);\r
+                    iCodeTop = addByte((byte)(index.shortValue() & 0xff), iCodeTop);                    \r
+                    itsStackDepth++;\r
+                    if (itsStackDepth > itsData.itsMaxStack)\r
+                        itsData.itsMaxStack = itsStackDepth;\r
+                }\r
+                break;\r
+\r
+            case TokenStream.SCRIPT :\r
+                iCodeTop = updateLineNumber(node, iCodeTop);\r
+                while (child != null) {\r
+                    if (child.getType() != TokenStream.FUNCTION) \r
+                        iCodeTop = generateICode(child, iCodeTop);\r
+                    child = child.getNextSibling();\r
+                }\r
+                break;\r
+\r
+            case TokenStream.CASE :\r
+                iCodeTop = updateLineNumber(node, iCodeTop);\r
+                child = child.getNextSibling();\r
+                while (child != null) {\r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                    child = child.getNextSibling();\r
+                }\r
+                break;\r
+                \r
+            case TokenStream.LABEL :\r
+            case TokenStream.WITH :\r
+            case TokenStream.LOOP :\r
+            case TokenStream.DEFAULT :\r
+            case TokenStream.BLOCK :\r
+            case TokenStream.VOID :\r
+            case TokenStream.NOP :\r
+                iCodeTop = updateLineNumber(node, iCodeTop);\r
+                while (child != null) {\r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                    child = child.getNextSibling();\r
+                }\r
+                break;\r
+\r
+            case TokenStream.COMMA :\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                iCodeTop = addByte((byte) TokenStream.POP, iCodeTop);\r
+                itsStackDepth--;\r
+                child = child.getNextSibling();\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                break;\r
+               \r
+            case TokenStream.SWITCH : {\r
+                    iCodeTop = updateLineNumber(node, iCodeTop);\r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                    int theLocalSlot = itsData.itsMaxLocals++;\r
+                    iCodeTop = addByte((byte) TokenStream.NEWTEMP, iCodeTop);\r
+                    iCodeTop = addByte((byte)theLocalSlot, iCodeTop);\r
+                    iCodeTop = addByte((byte) TokenStream.POP, iCodeTop);\r
+                    itsStackDepth--;\r
+         /*\r
+            reminder - below we construct new GOTO nodes that aren't\r
+            linked into the tree just for the purpose of having a node\r
+            to pass to the addGoto routine. (Parallels codegen here).\r
+            Seems unnecessary.        \r
+         */\r
+                    Vector cases = (Vector) node.getProp(Node.CASES_PROP);\r
+                    for (int i = 0; i < cases.size(); i++) {\r
+                        Node thisCase = (Node)cases.elementAt(i);\r
+                        Node first = thisCase.getFirstChild();\r
+                        // the case expression is the firstmost child\r
+                        // the rest will be generated when the case\r
+                        // statements are encountered as siblings of\r
+                        // the switch statement.\r
+                        iCodeTop = generateICode(first, iCodeTop);                   \r
+                        iCodeTop = addByte((byte) TokenStream.USETEMP, iCodeTop);\r
+                        itsStackDepth++;\r
+                        if (itsStackDepth > itsData.itsMaxStack)\r
+                            itsData.itsMaxStack = itsStackDepth;\r
+                        iCodeTop = addByte((byte) theLocalSlot, iCodeTop);\r
+                        iCodeTop = addByte((byte) TokenStream.SHEQ, iCodeTop);\r
+                        Node target = new Node(TokenStream.TARGET);\r
+                        thisCase.addChildAfter(target, first);\r
+                        Node branch = new Node(TokenStream.IFEQ);\r
+                        branch.putProp(Node.TARGET_PROP, target);\r
+                        iCodeTop = addGoto(branch, TokenStream.IFEQ, \r
+                                           iCodeTop);\r
+                        itsStackDepth--;\r
+                    }\r
+\r
+                    Node defaultNode = (Node) node.getProp(Node.DEFAULT_PROP);\r
+                    if (defaultNode != null) {\r
+                        Node defaultTarget = new Node(TokenStream.TARGET);\r
+                        defaultNode.getFirstChild().addChildToFront(defaultTarget);\r
+                        Node branch = new Node(TokenStream.GOTO);\r
+                        branch.putProp(Node.TARGET_PROP, defaultTarget);\r
+                        iCodeTop = addGoto(branch, TokenStream.GOTO,\r
+                                                            iCodeTop);                    \r
+                    }\r
+\r
+                    Node breakTarget = (Node) node.getProp(Node.BREAK_PROP);\r
+                    Node branch = new Node(TokenStream.GOTO);\r
+                    branch.putProp(Node.TARGET_PROP, breakTarget);\r
+                    iCodeTop = addGoto(branch, TokenStream.GOTO, \r
+                                       iCodeTop);                    \r
+                }\r
+                break;\r
+                                \r
+            case TokenStream.TARGET : { \r
+                    Object lblObect = node.getProp(Node.LABEL_PROP);\r
+                    if (lblObect == null) {\r
+                        int label = markLabel(acquireLabel(), iCodeTop);\r
+                        node.putProp(Node.LABEL_PROP, new Integer(label));\r
+                    }\r
+                    else {\r
+                        int label = ((Integer)lblObect).intValue();\r
+                        markLabel(label, iCodeTop);\r
+                    }\r
+                    // if this target has a FINALLY_PROP, it is a JSR target\r
+                    // and so has a PC value on the top of the stack\r
+                    if (node.getProp(Node.FINALLY_PROP) != null) {\r
+                        itsStackDepth = 1;\r
+                        if (itsStackDepth > itsData.itsMaxStack)\r
+                            itsData.itsMaxStack = itsStackDepth;\r
+                    }\r
+                }\r
+                break;\r
+                \r
+            case TokenStream.EQOP :\r
+            case TokenStream.RELOP : {\r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                    child = child.getNextSibling();\r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                    int op = node.getInt();\r
+                    if (version == Context.VERSION_1_2) {\r
+                        if (op == TokenStream.EQ)\r
+                            op = TokenStream.SHEQ;\r
+                        else if (op == TokenStream.NE)\r
+                            op = TokenStream.SHNE;\r
+                    }\r
+                    iCodeTop = addByte((byte) op, iCodeTop);\r
+                    itsStackDepth--;\r
+                }\r
+                break;\r
+                \r
+            case TokenStream.NEW :\r
+            case TokenStream.CALL : {\r
+                    if (itsSourceFile != null && (itsData.itsSourceFile == null || ! itsSourceFile.equals(itsData.itsSourceFile))) \r
+                        itsData.itsSourceFile = itsSourceFile;\r
+                    iCodeTop = addByte((byte)TokenStream.SOURCEFILE, iCodeTop);\r
+                    \r
+                    int childCount = 0;\r
+                    short nameIndex = -1;\r
+                    while (child != null) {\r
+                        iCodeTop = generateICode(child, iCodeTop);\r
+                        if (nameIndex == -1) {\r
+                            if (child.getType() == TokenStream.NAME)\r
+                                nameIndex = (short)(itsData.itsStringTableIndex - 1);\r
+                            else if (child.getType() == TokenStream.GETPROP)\r
+                                nameIndex = (short)(itsData.itsStringTableIndex - 1);\r
+                        }\r
+                        child = child.getNextSibling();\r
+                        childCount++;\r
+                    }\r
+                    if (node.getProp(Node.SPECIALCALL_PROP) != null) {\r
+                        // embed line number and source filename\r
+                        iCodeTop = addByte((byte) TokenStream.CALLSPECIAL, iCodeTop);\r
+                        iCodeTop = addByte((byte)(itsLineNumber >> 8), iCodeTop);\r
+                        iCodeTop = addByte((byte)(itsLineNumber & 0xff), iCodeTop);\r
+                        iCodeTop = addString(itsSourceFile, iCodeTop);\r
+                    } else {\r
+                        iCodeTop = addByte((byte) type, iCodeTop);\r
+                        iCodeTop = addByte((byte)(nameIndex >> 8), iCodeTop);\r
+                        iCodeTop = addByte((byte)(nameIndex & 0xFF), iCodeTop);\r
+                    }\r
+                    \r
+                    itsStackDepth -= (childCount - 1);  // always a result value\r
+                    // subtract from child count to account for [thisObj &] fun\r
+                    if (type == TokenStream.NEW)\r
+                        childCount -= 1;\r
+                    else\r
+                        childCount -= 2;\r
+                    iCodeTop = addByte((byte)(childCount >> 8), iCodeTop);\r
+                    iCodeTop = addByte((byte)(childCount & 0xff), iCodeTop);\r
+                    if (childCount > itsData.itsMaxArgs)\r
+                        itsData.itsMaxArgs = childCount;\r
+                    \r
+                    iCodeTop = addByte((byte)TokenStream.SOURCEFILE, iCodeTop);\r
+                }\r
+                break;\r
+                \r
+            case TokenStream.NEWLOCAL :\r
+            case TokenStream.NEWTEMP : {\r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                    iCodeTop = addByte((byte) TokenStream.NEWTEMP, iCodeTop);\r
+                    iCodeTop = addLocalRef(node, iCodeTop);\r
+                }\r
+                break;                \r
+                   \r
+            case TokenStream.USELOCAL : {\r
+                    if (node.getProp(Node.TARGET_PROP) != null) \r
+                        iCodeTop = addByte((byte) TokenStream.RETSUB, iCodeTop);\r
+                    else {\r
+                        iCodeTop = addByte((byte) TokenStream.USETEMP, iCodeTop);\r
+                        itsStackDepth++;\r
+                        if (itsStackDepth > itsData.itsMaxStack)\r
+                            itsData.itsMaxStack = itsStackDepth;\r
+                    }\r
+                    Node temp = (Node) node.getProp(Node.LOCAL_PROP);\r
+                    iCodeTop = addLocalRef(temp, iCodeTop);\r
+                }\r
+                break;                \r
+\r
+            case TokenStream.USETEMP : {\r
+                    iCodeTop = addByte((byte) TokenStream.USETEMP, iCodeTop);\r
+                    Node temp = (Node) node.getProp(Node.TEMP_PROP);\r
+                    iCodeTop = addLocalRef(temp, iCodeTop);\r
+                    itsStackDepth++;\r
+                    if (itsStackDepth > itsData.itsMaxStack)\r
+                        itsData.itsMaxStack = itsStackDepth;\r
+                }\r
+                break;                \r
+                \r
+            case TokenStream.IFEQ :\r
+            case TokenStream.IFNE :\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                itsStackDepth--;    // after the conditional GOTO, really\r
+                    // fall thru...\r
+            case TokenStream.GOTO :\r
+                iCodeTop = addGoto(node, (byte) type, iCodeTop);\r
+                break;\r
+\r
+            case TokenStream.JSR : {\r
+                /*\r
+                    mark the target with a FINALLY_PROP to indicate\r
+                    that it will have an incoming PC value on the top\r
+                    of the stack.\r
+                    !!! \r
+                    This only works if the target follows the JSR\r
+                    in the tree.\r
+                    !!!\r
+                */\r
+                    Node target = (Node)(node.getProp(Node.TARGET_PROP));\r
+                    target.putProp(Node.FINALLY_PROP, node);\r
+                    iCodeTop = addGoto(node, TokenStream.GOSUB, iCodeTop);\r
+                }\r
+                break;\r
+            \r
+            case TokenStream.AND : {            \r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                    iCodeTop = addByte((byte) TokenStream.DUP, iCodeTop);                \r
+                    itsStackDepth++;\r
+                    if (itsStackDepth > itsData.itsMaxStack)\r
+                        itsData.itsMaxStack = itsStackDepth;\r
+                    int falseTarget = acquireLabel();\r
+                    iCodeTop = addGoto(falseTarget, TokenStream.IFNE, \r
+                                                    iCodeTop);\r
+                    iCodeTop = addByte((byte) TokenStream.POP, iCodeTop);\r
+                    itsStackDepth--;\r
+                    child = child.getNextSibling();\r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                    markLabel(falseTarget, iCodeTop);\r
+                }\r
+                break;\r
+\r
+            case TokenStream.OR : {\r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                    iCodeTop = addByte((byte) TokenStream.DUP, iCodeTop);                \r
+                    itsStackDepth++;\r
+                    if (itsStackDepth > itsData.itsMaxStack)\r
+                        itsData.itsMaxStack = itsStackDepth;\r
+                    int trueTarget = acquireLabel();\r
+                    iCodeTop = addGoto(trueTarget, TokenStream.IFEQ,\r
+                                       iCodeTop);\r
+                    iCodeTop = addByte((byte) TokenStream.POP, iCodeTop);                \r
+                    itsStackDepth--;\r
+                    child = child.getNextSibling();\r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                    markLabel(trueTarget, iCodeTop);\r
+                }\r
+                break;\r
+\r
+            case TokenStream.GETPROP : {\r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                    String s = (String) node.getProp(Node.SPECIAL_PROP_PROP);\r
+                    if (s != null) {\r
+                        if (s.equals("__proto__"))\r
+                            iCodeTop = addByte((byte) TokenStream.GETPROTO, iCodeTop);\r
+                        else\r
+                            if (s.equals("__parent__"))\r
+                                iCodeTop = addByte((byte) TokenStream.GETSCOPEPARENT, iCodeTop);\r
+                            else\r
+                                badTree(node);\r
+                    }\r
+                    else {\r
+                        child = child.getNextSibling();\r
+                        iCodeTop = generateICode(child, iCodeTop);\r
+                        iCodeTop = addByte((byte) TokenStream.GETPROP, iCodeTop);\r
+                        itsStackDepth--;\r
+                    }\r
+                }\r
+                break;\r
+\r
+            case TokenStream.DELPROP :                \r
+            case TokenStream.BITAND :                \r
+            case TokenStream.BITOR :\r
+            case TokenStream.BITXOR :\r
+            case TokenStream.LSH :\r
+            case TokenStream.RSH :\r
+            case TokenStream.URSH :\r
+            case TokenStream.ADD :\r
+            case TokenStream.SUB :\r
+            case TokenStream.MOD :\r
+            case TokenStream.DIV :\r
+            case TokenStream.MUL :\r
+            case TokenStream.GETELEM :\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                child = child.getNextSibling();\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                iCodeTop = addByte((byte) type, iCodeTop);\r
+                itsStackDepth--;\r
+                break;\r
+\r
+            case TokenStream.CONVERT : {\r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                    Object toType = node.getProp(Node.TYPE_PROP);\r
+                    if (toType == ScriptRuntime.NumberClass)\r
+                        iCodeTop = addByte((byte) TokenStream.POS, iCodeTop);\r
+                    else\r
+                        badTree(node);\r
+                }\r
+                break;\r
+\r
+            case TokenStream.UNARYOP :\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                switch (node.getInt()) {\r
+                    case TokenStream.VOID :\r
+                        iCodeTop = addByte((byte) TokenStream.POP, iCodeTop);\r
+                        iCodeTop = addByte((byte) TokenStream.UNDEFINED, iCodeTop);\r
+                        break;\r
+                    case TokenStream.NOT : {\r
+                            int trueTarget = acquireLabel();\r
+                            int beyond = acquireLabel();\r
+                            iCodeTop = addGoto(trueTarget, TokenStream.IFEQ,\r
+                                                        iCodeTop);\r
+                            iCodeTop = addByte((byte) TokenStream.TRUE, iCodeTop);\r
+                            iCodeTop = addGoto(beyond, TokenStream.GOTO, \r
+                                                        iCodeTop);\r
+                            markLabel(trueTarget, iCodeTop);\r
+                            iCodeTop = addByte((byte) TokenStream.FALSE, iCodeTop);\r
+                            markLabel(beyond, iCodeTop);\r
+                        }\r
+                        break;\r
+                    case TokenStream.BITNOT :\r
+                        iCodeTop = addByte((byte) TokenStream.BITNOT, iCodeTop);\r
+                        break;\r
+                    case TokenStream.TYPEOF :\r
+                        iCodeTop = addByte((byte) TokenStream.TYPEOF, iCodeTop);\r
+                        break;\r
+                    case TokenStream.SUB :\r
+                        iCodeTop = addByte((byte) TokenStream.NEG, iCodeTop);\r
+                        break;\r
+                    case TokenStream.ADD :\r
+                        iCodeTop = addByte((byte) TokenStream.POS, iCodeTop);\r
+                        break;\r
+                    default:\r
+                        badTree(node);\r
+                        break;\r
+                }\r
+                break;\r
+\r
+            case TokenStream.SETPROP : {\r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                    child = child.getNextSibling();\r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                    String s = (String) node.getProp(Node.SPECIAL_PROP_PROP);\r
+                    if (s != null) {\r
+                        if (s.equals("__proto__"))\r
+                            iCodeTop = addByte((byte) TokenStream.SETPROTO, iCodeTop);\r
+                        else\r
+                            if (s.equals("__parent__"))\r
+                                iCodeTop = addByte((byte) TokenStream.SETPARENT, iCodeTop);\r
+                            else\r
+                                badTree(node);\r
+                    }\r
+                    else {\r
+                        child = child.getNextSibling();\r
+                        iCodeTop = generateICode(child, iCodeTop);\r
+                        iCodeTop = addByte((byte) TokenStream.SETPROP, iCodeTop);\r
+                        itsStackDepth -= 2;\r
+                    }\r
+                }\r
+                break;            \r
+\r
+            case TokenStream.SETELEM :\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                child = child.getNextSibling();\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                child = child.getNextSibling();\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                iCodeTop = addByte((byte) type, iCodeTop);\r
+                itsStackDepth -= 2;\r
+                break;\r
+\r
+            case TokenStream.SETNAME :\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                child = child.getNextSibling();\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                iCodeTop = addByte((byte) TokenStream.SETNAME, iCodeTop);                \r
+                iCodeTop = addString(firstChild.getString(), iCodeTop);\r
+                itsStackDepth--;\r
+                break;\r
+                \r
+            case TokenStream.TYPEOF : {\r
+                    String name = node.getString();\r
+                    int index = -1;\r
+                    // use typeofname if an activation frame exists\r
+                    // since the vars all exist there instead of in jregs\r
+                    if (itsInFunctionFlag && !itsData.itsNeedsActivation)\r
+                        index = itsVariableTable.getOrdinal(name);\r
+                    if (index == -1) {                    \r
+                        iCodeTop = addByte((byte) TokenStream.TYPEOFNAME, iCodeTop);\r
+                        iCodeTop = addString(name, iCodeTop);\r
+                    }\r
+                    else {\r
+                        iCodeTop = addByte((byte) TokenStream.GETVAR, iCodeTop);\r
+                        iCodeTop = addByte((byte) index, iCodeTop);\r
+                        iCodeTop = addByte((byte) TokenStream.TYPEOF, iCodeTop);\r
+                    }\r
+                    itsStackDepth++;\r
+                    if (itsStackDepth > itsData.itsMaxStack)\r
+                        itsData.itsMaxStack = itsStackDepth;\r
+                }\r
+                break;\r
+\r
+            case TokenStream.PARENT :\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                iCodeTop = addByte((byte) TokenStream.GETPARENT, iCodeTop);\r
+                break;\r
+\r
+            case TokenStream.GETBASE :\r
+            case TokenStream.BINDNAME :\r
+            case TokenStream.NAME :\r
+            case TokenStream.STRING :\r
+                iCodeTop = addByte((byte) type, iCodeTop);\r
+                iCodeTop = addString(node.getString(), iCodeTop);\r
+                itsStackDepth++;\r
+                if (itsStackDepth > itsData.itsMaxStack)\r
+                    itsData.itsMaxStack = itsStackDepth;\r
+                break;\r
+\r
+            case TokenStream.INC :\r
+            case TokenStream.DEC : {\r
+                    int childType = child.getType();\r
+                    switch (childType) {\r
+                        case TokenStream.GETVAR : {\r
+                                String name = child.getString();\r
+                                if (itsData.itsNeedsActivation) {\r
+                                    iCodeTop = addByte((byte) TokenStream.SCOPE, iCodeTop);\r
+                                    iCodeTop = addByte((byte) TokenStream.STRING, iCodeTop);\r
+                                    iCodeTop = addString(name, iCodeTop);\r
+                                    itsStackDepth += 2;\r
+                                    if (itsStackDepth > itsData.itsMaxStack)\r
+                                        itsData.itsMaxStack = itsStackDepth;\r
+                                    iCodeTop = addByte((byte)\r
+                                                (type == TokenStream.INC\r
+                                                    ? TokenStream.PROPINC \r
+                                                    : TokenStream.PROPDEC),\r
+                                                 iCodeTop);\r
+                                    itsStackDepth--;                                        \r
+                                }\r
+                                else {\r
+                                    iCodeTop = addByte((byte)\r
+                                                (type == TokenStream.INC\r
+                                                    ? TokenStream.VARINC\r
+                                                    : TokenStream.VARDEC),\r
+                                                iCodeTop);\r
+                                    int i = itsVariableTable.getOrdinal(name);\r
+                                    iCodeTop = addByte((byte)i, iCodeTop);\r
+                                    itsStackDepth++;\r
+                                    if (itsStackDepth > itsData.itsMaxStack)\r
+                                        itsData.itsMaxStack = itsStackDepth;\r
+                                }\r
+                            }\r
+                            break;\r
+                        case TokenStream.GETPROP :\r
+                        case TokenStream.GETELEM : {\r
+                                Node getPropChild = child.getFirstChild();\r
+                                iCodeTop = generateICode(getPropChild,\r
+                                                              iCodeTop);\r
+                                getPropChild = getPropChild.getNextSibling();\r
+                                iCodeTop = generateICode(getPropChild,\r
+                                                              iCodeTop);\r
+                                if (childType == TokenStream.GETPROP)\r
+                                    iCodeTop = addByte((byte)\r
+                                                    (type == TokenStream.INC\r
+                                                        ? TokenStream.PROPINC \r
+                                                        : TokenStream.PROPDEC),\r
+                                                    iCodeTop);\r
+                                else                                                        \r
+                                    iCodeTop = addByte((byte)\r
+                                                    (type == TokenStream.INC\r
+                                                        ? TokenStream.ELEMINC \r
+                                                        : TokenStream.ELEMDEC),\r
+                                                    iCodeTop);\r
+                                itsStackDepth--;                                        \r
+                            }\r
+                            break;\r
+                        default : {\r
+                                iCodeTop = addByte((byte)\r
+                                                    (type == TokenStream.INC \r
+                                                        ? TokenStream.NAMEINC \r
+                                                        : TokenStream.NAMEDEC),\r
+                                                    iCodeTop);\r
+                                iCodeTop = addString(child.getString(), \r
+                                                            iCodeTop);\r
+                                itsStackDepth++;\r
+                                if (itsStackDepth > itsData.itsMaxStack)\r
+                                    itsData.itsMaxStack = itsStackDepth;\r
+                            }\r
+                            break;\r
+                    }\r
+                }\r
+                break;\r
+\r
+            case TokenStream.NUMBER : {\r
+                double num = node.getDouble();\r
+                if (num == 0.0) {\r
+                    iCodeTop = addByte((byte) TokenStream.ZERO, iCodeTop);\r
+                }\r
+                else if (num == 1.0) {\r
+                    iCodeTop = addByte((byte) TokenStream.ONE, iCodeTop);\r
+                }\r
+                else {\r
+                    iCodeTop = addByte((byte) TokenStream.NUMBER, iCodeTop);\r
+                    iCodeTop = addNumber(num, iCodeTop);\r
+                }\r
+                itsStackDepth++;\r
+                if (itsStackDepth > itsData.itsMaxStack)\r
+                    itsData.itsMaxStack = itsStackDepth;\r
+                break;\r
+            }\r
+\r
+            case TokenStream.POP :\r
+            case TokenStream.POPV :\r
+                iCodeTop = updateLineNumber(node, iCodeTop);\r
+            case TokenStream.ENTERWITH :\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                iCodeTop = addByte((byte) type, iCodeTop);\r
+                itsStackDepth--;\r
+                break;\r
+\r
+            case TokenStream.GETTHIS :\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                iCodeTop = addByte((byte) type, iCodeTop);\r
+                break;\r
+                \r
+            case TokenStream.NEWSCOPE :\r
+                iCodeTop = addByte((byte) type, iCodeTop);\r
+                itsStackDepth++;\r
+                if (itsStackDepth > itsData.itsMaxStack)\r
+                    itsData.itsMaxStack = itsStackDepth;\r
+                break;\r
+\r
+            case TokenStream.LEAVEWITH :\r
+                iCodeTop = addByte((byte) type, iCodeTop);\r
+                break;\r
+\r
+            case TokenStream.TRY : {\r
+                    itsTryDepth++;\r
+                    if (itsTryDepth > itsData.itsMaxTryDepth)\r
+                        itsData.itsMaxTryDepth = itsTryDepth;\r
+                    Node catchTarget = (Node)node.getProp(Node.TARGET_PROP);\r
+                    Node finallyTarget = (Node)node.getProp(Node.FINALLY_PROP);\r
+                    if (catchTarget == null) {\r
+                        iCodeTop = addByte((byte) TokenStream.TRY, iCodeTop);\r
+                        iCodeTop = addByte((byte)0, iCodeTop);\r
+                        iCodeTop = addByte((byte)0, iCodeTop);\r
+                    }\r
+                    else\r
+                        iCodeTop = \r
+                            addGoto(node, TokenStream.TRY, iCodeTop);\r
+                    int finallyHandler = 0;\r
+                    if (finallyTarget != null) {\r
+                        finallyHandler = acquireLabel();\r
+                        int theLabel = finallyHandler & 0x7FFFFFFF;\r
+                        itsLabelTable[theLabel].addFixup(iCodeTop);\r
+                    }\r
+                    iCodeTop = addByte((byte)0, iCodeTop);\r
+                    iCodeTop = addByte((byte)0, iCodeTop);\r
+                    \r
+                    Node lastChild = null;\r
+                    /*\r
+                        when we encounter the child of the catchTarget, we\r
+                        set the stackDepth to 1 to account for the incoming\r
+                        exception object.\r
+                    */\r
+                    boolean insertedEndTry = false;\r
+                    while (child != null) {\r
+                        if (catchTarget != null && lastChild == catchTarget) {\r
+                            itsStackDepth = 1;\r
+                            if (itsStackDepth > itsData.itsMaxStack)\r
+                                itsData.itsMaxStack = itsStackDepth;\r
+                        }\r
+                        /*\r
+                            When the following child is the catchTarget\r
+                            (or the finallyTarget if there are no catches),\r
+                            the current child is the goto at the end of\r
+                            the try statemets, we need to emit the endtry\r
+                            before that goto.\r
+                        */\r
+                        Node nextSibling = child.getNextSibling();\r
+                        if (!insertedEndTry && nextSibling != null &&\r
+                            (nextSibling == catchTarget ||\r
+                             nextSibling == finallyTarget))\r
+                        {\r
+                            iCodeTop = addByte((byte) TokenStream.ENDTRY,\r
+                                               iCodeTop);\r
+                            insertedEndTry = true;\r
+                        }\r
+                        iCodeTop = generateICode(child, iCodeTop);\r
+                        lastChild = child;\r
+                        child = child.getNextSibling();\r
+                    }\r
+                    itsStackDepth = 0;\r
+                    if (finallyTarget != null) {\r
+                        // normal flow goes around the finally handler stublet\r
+                        int skippy = acquireLabel();\r
+                        iCodeTop = \r
+                            addGoto(skippy, TokenStream.GOTO, iCodeTop);\r
+                        // on entry the stack will have the exception object\r
+                        markLabel(finallyHandler, iCodeTop);\r
+                        itsStackDepth = 1;\r
+                        if (itsStackDepth > itsData.itsMaxStack)\r
+                            itsData.itsMaxStack = itsStackDepth;\r
+                        int theLocalSlot = itsData.itsMaxLocals++;\r
+                        iCodeTop = addByte((byte) TokenStream.NEWTEMP, iCodeTop);\r
+                        iCodeTop = addByte((byte)theLocalSlot, iCodeTop);\r
+                        iCodeTop = addByte((byte) TokenStream.POP, iCodeTop);\r
+                        Integer finallyLabel \r
+                           = (Integer)(finallyTarget.getProp(Node.LABEL_PROP));\r
+                        iCodeTop = addGoto(finallyLabel.intValue(), \r
+                                         TokenStream.GOSUB, iCodeTop);\r
+                        iCodeTop = addByte((byte) TokenStream.USETEMP, iCodeTop);\r
+                        iCodeTop = addByte((byte)theLocalSlot, iCodeTop);\r
+                        iCodeTop = addByte((byte) TokenStream.JTHROW, iCodeTop);\r
+                        itsStackDepth = 0;\r
+                        markLabel(skippy, iCodeTop);\r
+                    }\r
+                    itsTryDepth--;\r
+                }\r
+                break;\r
+                \r
+            case TokenStream.THROW :\r
+                iCodeTop = updateLineNumber(node, iCodeTop);\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                iCodeTop = addByte((byte) TokenStream.THROW, iCodeTop);\r
+                itsStackDepth--;\r
+                break;\r
+                \r
+            case TokenStream.ASSERT:\r
+                iCodeTop = updateLineNumber(node, iCodeTop);\r
+                if (child != null) \r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                else {\r
+                    iCodeTop = addByte((byte) TokenStream.UNDEFINED, iCodeTop);\r
+                    itsStackDepth++;\r
+                    if (itsStackDepth > itsData.itsMaxStack)\r
+                        itsData.itsMaxStack = itsStackDepth;\r
+                }\r
+                iCodeTop = addGoto(node, TokenStream.ASSERT, iCodeTop);\r
+                itsStackDepth--;\r
+                break;\r
+                \r
+            case TokenStream.RETURN :\r
+                iCodeTop = updateLineNumber(node, iCodeTop);\r
+                if (child != null) \r
+                    iCodeTop = generateICode(child, iCodeTop);\r
+                else {\r
+                    iCodeTop = addByte((byte) TokenStream.UNDEFINED, iCodeTop);\r
+                    itsStackDepth++;\r
+                    if (itsStackDepth > itsData.itsMaxStack)\r
+                        itsData.itsMaxStack = itsStackDepth;\r
+                }\r
+                iCodeTop = addGoto(node, TokenStream.RETURN, iCodeTop);\r
+                itsStackDepth--;\r
+                break;\r
+                \r
+            case TokenStream.GETVAR : {\r
+                    String name = node.getString();\r
+                    if (itsData.itsNeedsActivation) {\r
+                        // SETVAR handled this by turning into a SETPROP, but\r
+                        // we can't do that to a GETVAR without manufacturing\r
+                        // bogus children. Instead we use a special op to\r
+                        // push the current scope.\r
+                        iCodeTop = addByte((byte) TokenStream.SCOPE, iCodeTop);\r
+                        iCodeTop = addByte((byte) TokenStream.STRING, iCodeTop);\r
+                        iCodeTop = addString(name, iCodeTop);\r
+                        itsStackDepth += 2;\r
+                        if (itsStackDepth > itsData.itsMaxStack)\r
+                            itsData.itsMaxStack = itsStackDepth;\r
+                        iCodeTop = addByte((byte) TokenStream.GETPROP, iCodeTop);\r
+                        itsStackDepth--;\r
+                    }\r
+                    else {\r
+                        int index = itsVariableTable.getOrdinal(name);\r
+                        iCodeTop = addByte((byte) TokenStream.GETVAR, iCodeTop);\r
+                        iCodeTop = addByte((byte)index, iCodeTop);\r
+                        itsStackDepth++;\r
+                        if (itsStackDepth > itsData.itsMaxStack)\r
+                            itsData.itsMaxStack = itsStackDepth;\r
+                    }\r
+                }\r
+                break;\r
+                \r
+            case TokenStream.SETVAR : {\r
+                    if (itsData.itsNeedsActivation) {\r
+                        child.setType(TokenStream.BINDNAME);\r
+                        node.setType(TokenStream.SETNAME);\r
+                        iCodeTop = generateICode(node, iCodeTop);\r
+                    }\r
+                    else {\r
+                        String name = child.getString();\r
+                        child = child.getNextSibling();\r
+                        iCodeTop = generateICode(child, iCodeTop);\r
+                        int index = itsVariableTable.getOrdinal(name);\r
+                        iCodeTop = addByte((byte) TokenStream.SETVAR, iCodeTop);\r
+                        iCodeTop = addByte((byte)index, iCodeTop);\r
+                    }\r
+                }\r
+                break;\r
+                \r
+            case TokenStream.PRIMARY:\r
+                iCodeTop = addByte((byte) node.getInt(), iCodeTop);\r
+                itsStackDepth++;\r
+                if (itsStackDepth > itsData.itsMaxStack)\r
+                    itsData.itsMaxStack = itsStackDepth;\r
+                break;\r
+\r
+            case TokenStream.ENUMINIT :\r
+                iCodeTop = generateICode(child, iCodeTop);\r
+                iCodeTop = addByte((byte) TokenStream.ENUMINIT, iCodeTop);\r
+                iCodeTop = addLocalRef(node, iCodeTop);\r
+                itsStackDepth--;\r
+                break;\r
+                \r
+            case TokenStream.ENUMNEXT : {\r
+                    iCodeTop = addByte((byte) TokenStream.ENUMNEXT, iCodeTop);\r
+                    Node init = (Node)node.getProp(Node.ENUM_PROP);\r
+                    iCodeTop = addLocalRef(init, iCodeTop);\r
+                    itsStackDepth++;\r
+                    if (itsStackDepth > itsData.itsMaxStack)\r
+                        itsData.itsMaxStack = itsStackDepth;\r
+                }                        \r
+                break;\r
+                \r
+            case TokenStream.ENUMDONE :\r
+                // could release the local here??\r
+                break;\r
+                \r
+            case TokenStream.OBJECT : {\r
+                    Node regexp = (Node) node.getProp(Node.REGEXP_PROP);\r
+                    int index = ((Integer)(regexp.getProp(\r
+                                            Node.REGEXP_PROP))).intValue();\r
+                    iCodeTop = addByte((byte) TokenStream.OBJECT, iCodeTop);\r
+                    iCodeTop = addByte((byte)(index >> 8), iCodeTop);\r
+                    iCodeTop = addByte((byte)(index & 0xff), iCodeTop);\r
+                    itsStackDepth++;\r
+                    if (itsStackDepth > itsData.itsMaxStack)\r
+                        itsData.itsMaxStack = itsStackDepth;\r
+                }\r
+                break;\r
+                \r
+            default : \r
+                badTree(node);\r
+                break;\r
+        }\r
+        return iCodeTop;\r
+    }\r
+    \r
+    private int addLocalRef(Node node, int iCodeTop)\r
+    {\r
+        int theLocalSlot;\r
+        Integer localProp = (Integer)node.getProp(Node.LOCAL_PROP);\r
+        if (localProp == null) {\r
+            theLocalSlot = itsData.itsMaxLocals++;\r
+            node.putProp(Node.LOCAL_PROP, new Integer(theLocalSlot));\r
+        }\r
+        else\r
+            theLocalSlot = localProp.intValue();\r
+        iCodeTop = addByte((byte)theLocalSlot, iCodeTop);\r
+        if (theLocalSlot >= itsData.itsMaxLocals)\r
+            itsData.itsMaxLocals = theLocalSlot + 1;\r
+        return iCodeTop;            \r
+    }\r
+    \r
+    private int addGoto(Node node, int gotoOp, int iCodeTop)\r
+    {\r
+        int targetLabel;\r
+        if (node.getType() == TokenStream.RETURN || node.getType() == TokenStream.ASSERT) {\r
+            if (itsEpilogLabel == -1)\r
+                itsEpilogLabel = acquireLabel();\r
+            targetLabel = itsEpilogLabel;\r
+        }\r
+        else {\r
+            Node target = (Node)(node.getProp(Node.TARGET_PROP));\r
+            Object lblObect = target.getProp(Node.LABEL_PROP);\r
+            if (lblObect == null) {\r
+                targetLabel = acquireLabel();\r
+                target.putProp(Node.LABEL_PROP, new Integer(targetLabel));\r
+            }\r
+            else\r
+                targetLabel = ((Integer)lblObect).intValue();\r
+        }\r
+        iCodeTop = addGoto(targetLabel, (byte) gotoOp, iCodeTop);\r
+        return iCodeTop;\r
+    }\r
+    \r
+    private int addGoto(int targetLabel, int gotoOp, int iCodeTop)\r
+    {\r
+        int gotoPC = iCodeTop;\r
+        iCodeTop = addByte((byte) gotoOp, iCodeTop);\r
+        int theLabel = targetLabel & 0x7FFFFFFF;\r
+        int targetPC = itsLabelTable[theLabel].getPC();\r
+        if (targetPC != -1) {\r
+            short offset = (short)(targetPC - gotoPC);\r
+            iCodeTop = addByte((byte)(offset >> 8), iCodeTop);\r
+            iCodeTop = addByte((byte)offset, iCodeTop);\r
+        }\r
+        else {\r
+            itsLabelTable[theLabel].addFixup(gotoPC + 1);\r
+            iCodeTop = addByte((byte)0, iCodeTop);\r
+            iCodeTop = addByte((byte)0, iCodeTop);\r
+        }\r
+        return iCodeTop;\r
+    }\r
+    \r
+    private final int addByte(byte b, int iCodeTop) {\r
+        if (itsData.itsICode.length == iCodeTop) {\r
+            byte[] ba = new byte[iCodeTop * 2];\r
+            System.arraycopy(itsData.itsICode, 0, ba, 0, iCodeTop);\r
+            itsData.itsICode = ba;\r
+        }\r
+        itsData.itsICode[iCodeTop++] = b;\r
+        return iCodeTop;\r
+    }\r
+    \r
+    private final int addString(String str, int iCodeTop)\r
+    {\r
+        int index = itsData.itsStringTableIndex;\r
+        if (itsData.itsStringTable.length == index) {\r
+            String[] sa = new String[index * 2];\r
+            System.arraycopy(itsData.itsStringTable, 0, sa, 0, index);\r
+            itsData.itsStringTable = sa;\r
+        }\r
+        itsData.itsStringTable[index] = str;\r
+        itsData.itsStringTableIndex = index + 1;\r
+\r
+        iCodeTop = addByte((byte)(index >> 8), iCodeTop);\r
+        iCodeTop = addByte((byte)(index & 0xFF), iCodeTop);\r
+        return iCodeTop;\r
+    }\r
+    \r
+    private final int addNumber(double num, int iCodeTop)\r
+    {\r
+        int index = itsData.itsNumberTableIndex;\r
+        if (itsData.itsNumberTable.length == index) {\r
+            double[] na = new double[index * 2];\r
+            System.arraycopy(itsData.itsNumberTable, 0, na, 0, index);\r
+            itsData.itsNumberTable = na;\r
+        }\r
+        itsData.itsNumberTable[index] = num;\r
+        itsData.itsNumberTableIndex = index + 1;\r
+\r
+        iCodeTop = addByte((byte)(index >> 8), iCodeTop);\r
+        iCodeTop = addByte((byte)(index & 0xFF), iCodeTop);\r
+        return iCodeTop;\r
+    }\r
+    \r
+    private static String getString(String[] theStringTable, byte[] iCode, \r
+                                    int pc)\r
+    {\r
+        int index = (iCode[pc] << 8) + (iCode[pc + 1] & 0xFF);\r
+        return theStringTable[index];\r
+    }\r
+    \r
+    private static double getNumber(double[] theNumberTable, byte[] iCode, \r
+                                    int pc)\r
+    {\r
+        int index = (iCode[pc] << 8) + (iCode[pc + 1] & 0xFF);\r
+        return theNumberTable[index];\r
+    }\r
+    \r
+    private static int getTarget(byte[] iCode, int pc)\r
+    {\r
+        int displacement = (iCode[pc] << 8) + (iCode[pc + 1] & 0xFF);\r
+        return pc - 1 + displacement;\r
+    }\r
+    \r
+    static PrintWriter out;\r
+    static {\r
+        if (printICode) {\r
+            try {\r
+                out = new PrintWriter(new FileOutputStream("icode.txt"));\r
+                out.close();\r
+            }\r
+            catch (IOException x) {\r
+            }\r
+        }\r
+    }   \r
+    \r
+    private static void dumpICode(InterpreterData theData) {\r
+        if (printICode) {\r
+            try {\r
+                int iCodeLength = theData.itsICodeTop;\r
+                byte iCode[] = theData.itsICode;\r
+                \r
+                out = new PrintWriter(new FileOutputStream("icode.txt", true));\r
+                out.println("ICode dump, for " + theData.itsName + ", length = " + iCodeLength);\r
+                out.println("MaxStack = " + theData.itsMaxStack);\r
+                \r
+                for (int pc = 0; pc < iCodeLength; ) {\r
+                    out.print("[" + pc + "] ");\r
+                    switch ((int)(iCode[pc] & 0xff)) {\r
+                        case TokenStream.SCOPE :\r
+                        case TokenStream.GETPROTO :\r
+                        case TokenStream.GETPARENT :\r
+                        case TokenStream.GETSCOPEPARENT :\r
+                        case TokenStream.SETPROTO :\r
+                        case TokenStream.SETPARENT :\r
+                        case TokenStream.DELPROP :\r
+                        case TokenStream.TYPEOF :\r
+                        case TokenStream.NEWSCOPE :\r
+                        case TokenStream.ENTERWITH :\r
+                        case TokenStream.LEAVEWITH :\r
+                        case TokenStream.ENDTRY :\r
+                        case TokenStream.THROW :\r
+                        case TokenStream.JTHROW :\r
+                        case TokenStream.GETTHIS :\r
+                        case TokenStream.SETELEM :\r
+                        case TokenStream.GETELEM :\r
+                        case TokenStream.SETPROP :\r
+                        case TokenStream.GETPROP :\r
+                        case TokenStream.PROPINC :\r
+                        case TokenStream.PROPDEC :\r
+                        case TokenStream.ELEMINC :\r
+                        case TokenStream.ELEMDEC :\r
+                        case TokenStream.BITNOT :                \r
+                        case TokenStream.BITAND :                \r
+                        case TokenStream.BITOR :\r
+                        case TokenStream.BITXOR :\r
+                        case TokenStream.LSH :\r
+                        case TokenStream.RSH :\r
+                        case TokenStream.URSH :\r
+                        case TokenStream.NEG :\r
+                        case TokenStream.POS :\r
+                        case TokenStream.SUB :\r
+                        case TokenStream.MUL :\r
+                        case TokenStream.DIV :\r
+                        case TokenStream.MOD :\r
+                        case TokenStream.ADD :\r
+                        case TokenStream.POPV :\r
+                        case TokenStream.POP :\r
+                        case TokenStream.DUP :\r
+                        case TokenStream.LT :\r
+                        case TokenStream.GT :\r
+                        case TokenStream.LE :\r
+                        case TokenStream.GE :\r
+                        case TokenStream.IN :\r
+                        case TokenStream.INSTANCEOF :\r
+                        case TokenStream.EQ :\r
+                        case TokenStream.NE :\r
+                        case TokenStream.SHEQ :\r
+                        case TokenStream.SHNE :\r
+                        case TokenStream.ZERO :\r
+                        case TokenStream.ONE :\r
+                        case TokenStream.NULL :\r
+                        case TokenStream.THIS :\r
+                        case TokenStream.THISFN :\r
+                        case TokenStream.FALSE :\r
+                        case TokenStream.TRUE :\r
+                        case TokenStream.UNDEFINED :\r
+                        case TokenStream.SOURCEFILE : \r
+                            out.println(TokenStream.tokenToName(iCode[pc] & 0xff));\r
+                            break;\r
+                        case TokenStream.GOSUB :\r
+                        case TokenStream.RETURN :\r
+                        case TokenStream.GOTO :\r
+                        case TokenStream.IFEQ :\r
+                        case TokenStream.IFNE : {\r
+                                int newPC = getTarget(iCode, pc + 1);                    \r
+                                out.println(\r
+                                    TokenStream.tokenToName(iCode[pc] & 0xff) +\r
+                                    " " + newPC);\r
+                                pc += 2;\r
+                            }\r
+                            break;\r
+                        case TokenStream.TRY : {\r
+                                int newPC1 = getTarget(iCode, pc + 1);                    \r
+                                int newPC2 = getTarget(iCode, pc + 3);                    \r
+                                out.println(\r
+                                    TokenStream.tokenToName(iCode[pc] & 0xff) +\r
+                                    " " + newPC1 +\r
+                                    " " + newPC2);\r
+                                pc += 4;\r
+                            }\r
+                            break;\r
+                        case TokenStream.RETSUB :                        \r
+                        case TokenStream.ENUMINIT :\r
+                        case TokenStream.ENUMNEXT :\r
+                        case TokenStream.VARINC :\r
+                        case TokenStream.VARDEC :\r
+                        case TokenStream.GETVAR :\r
+                        case TokenStream.SETVAR :\r
+                        case TokenStream.NEWTEMP :\r
+                        case TokenStream.USETEMP : {\r
+                                int slot = (iCode[pc + 1] & 0xFF);\r
+                                out.println(\r
+                                    TokenStream.tokenToName(iCode[pc] & 0xff) +\r
+                                    " " + slot);\r
+                                pc++;\r
+                            }\r
+                            break;\r
+                        case TokenStream.CALLSPECIAL : {\r
+                                int line = (iCode[pc + 1] << 8) \r
+                                                        | (iCode[pc + 2] & 0xFF);\r
+                                String name = getString(theData.itsStringTable,\r
+                                                                  iCode, pc + 3);\r
+                                int count = (iCode[pc + 5] << 8) | (iCode[pc + 6] & 0xFF);\r
+                                out.println(\r
+                                    TokenStream.tokenToName(iCode[pc] & 0xff) +\r
+                                    " " + count + " " + line + " " + name);\r
+                                pc += 6;\r
+                            }\r
+                            break;\r
+                        case TokenStream.OBJECT :\r
+                        case TokenStream.CLOSURE :\r
+                        case TokenStream.NEW :\r
+                        case TokenStream.CALL : {\r
+                                int count = (iCode[pc + 3] << 8) | (iCode[pc + 4] & 0xFF);\r
+                                out.println(\r
+                                    TokenStream.tokenToName(iCode[pc] & 0xff) +\r
+                                    " " + count + " \"" + \r
+                                    getString(theData.itsStringTable, iCode, \r
+                                              pc + 1) + "\"");\r
+                                pc += 5;\r
+                            }\r
+                            break;\r
+                        case TokenStream.NUMBER :\r
+                            out.println(\r
+                                TokenStream.tokenToName(iCode[pc] & 0xff) + \r
+                                " " + getNumber(theData.itsNumberTable,\r
+                                                iCode, pc + 1));\r
+                            pc += 2;\r
+                            break;\r
+                        case TokenStream.TYPEOFNAME :\r
+                        case TokenStream.GETBASE :\r
+                        case TokenStream.BINDNAME :\r
+                        case TokenStream.SETNAME :\r
+                        case TokenStream.NAME :\r
+                        case TokenStream.NAMEINC :\r
+                        case TokenStream.NAMEDEC :\r
+                        case TokenStream.STRING :\r
+                            out.println(\r
+                                TokenStream.tokenToName(iCode[pc] & 0xff) +\r
+                                " \"" +\r
+                                getString(theData.itsStringTable, iCode, pc + 1) +\r
+                                "\"");\r
+                            pc += 2;\r
+                            break;\r
+                        case TokenStream.LINE : {\r
+                                int line = (iCode[pc + 1] << 8) | (iCode[pc + 2] & 0xFF);\r
+                                out.println(\r
+                                    TokenStream.tokenToName(iCode[pc] & 0xff) + " : " + line);\r
+                                pc += 2;\r
+                            }\r
+                            break;\r
+                        default :\r
+                            out.close();\r
+                            throw new RuntimeException("Unknown icode : "\r
+                                                    + (iCode[pc] & 0xff)  + " @ pc : " + pc);\r
+                    }\r
+                    pc++;\r
+                }\r
+                out.close();\r
+            }\r
+            catch (IOException x) {}\r
+        }\r
+    }\r
+    \r
+    private static void createFunctionObject(InterpretedFunction fn, \r
+                                             Scriptable scope)\r
+    {\r
+        fn.setPrototype(ScriptableObject.getClassPrototype(scope, "Function"));\r
+        fn.setParentScope(scope);\r
+        InterpreterData id = fn.itsData;\r
+        if (id.itsName.length() == 0)\r
+            return;\r
+        if ((id.itsFunctionType == FunctionNode.FUNCTION_STATEMENT &&\r
+             fn.itsClosure == null) ||\r
+            (id.itsFunctionType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT &&\r
+             fn.itsClosure != null))\r
+        {\r
+            ScriptRuntime.setProp(scope, fn.itsData.itsName, fn, scope);\r
+        }\r
+    }\r
+    \r
+    public static Object interpret(Context cx, Scriptable scope, \r
+                                   Scriptable thisObj, Object[] args, \r
+                                   NativeFunction fnOrScript,\r
+                                   InterpreterData theData)\r
+        throws JavaScriptException\r
+    {\r
+        int i;\r
+        Object lhs;\r
+\r
+        final int maxStack = theData.itsMaxStack;\r
+        final int maxVars = (fnOrScript.argNames == null) \r
+                            ? 0 : fnOrScript.argNames.length;\r
+        final int maxLocals = theData.itsMaxLocals;\r
+        final int maxTryDepth = theData.itsMaxTryDepth;\r
+        \r
+        final int VAR_SHFT = maxStack; \r
+        final int LOCAL_SHFT = VAR_SHFT + maxVars; \r
+        final int TRY_SCOPE_SHFT = LOCAL_SHFT + maxLocals;\r
+\r
+// stack[0 <= i < VAR_SHFT]: stack data\r
+// stack[VAR_SHFT <= i < LOCAL_SHFT]: variables\r
+// stack[LOCAL_SHFT <= i < TRY_SCOPE_SHFT]: used for newtemp/usetemp\r
+// stack[TRY_SCOPE_SHFT <= i]: try scopes\r
+// when 0 <= i < LOCAL_SHFT and stack[x] == DBL_MRK, \r
+// sDbl[i]  gives the number value\r
+\r
+        final Object DBL_MRK = Interpreter.DBL_MRK;\r
+        \r
+        Object[] stack = new Object[TRY_SCOPE_SHFT + maxTryDepth];\r
+        double[] sDbl = new double[TRY_SCOPE_SHFT];\r
+        int stackTop = -1;\r
+        byte[] iCode = theData.itsICode;        \r
+        int pc = 0;\r
+        int iCodeLength = theData.itsICodeTop;\r
+        \r
+        final Scriptable undefined = Undefined.instance;\r
+        if (maxVars != 0) {\r
+            int definedArgs = fnOrScript.argCount;\r
+            if (definedArgs != 0) {\r
+                if (definedArgs > args.length) { definedArgs = args.length;    }\r
+                for (i = 0; i != definedArgs; ++i) {\r
+                    stack[VAR_SHFT + i] = args[i];    \r
+                }\r
+            }\r
+            for (i = definedArgs; i != maxVars; ++i) {\r
+                stack[VAR_SHFT + i] = undefined;\r
+            }\r
+        }\r
+        \r
+        if (theData.itsNestedFunctions != null) {\r
+            for (i = 0; i < theData.itsNestedFunctions.length; i++)\r
+                createFunctionObject(theData.itsNestedFunctions[i], scope);\r
+        }        \r
+\r
+        Object id;\r
+        Object rhs, val;\r
+        double valDbl;\r
+        boolean valBln;\r
+\r
+        int count;\r
+        int slot;\r
+                \r
+        String name = null;\r
+               \r
+        Object[] outArgs;\r
+\r
+        int lIntValue;\r
+        long lLongValue;\r
+        double lDbl;\r
+        int rIntValue;\r
+        double rDbl;\r
+                \r
+        int[] catchStack = null;\r
+        int tryStackTop = 0;\r
+        InterpreterFrame frame = null;\r
+        \r
+        if (cx.debugger != null) {\r
+            frame = new InterpreterFrame(scope, theData, fnOrScript);\r
+            cx.pushFrame(frame);\r
+        }\r
+            \r
+        if (maxTryDepth != 0) {\r
+            // catchStack[2 * i]: starting pc of catch block\r
+            // catchStack[2 * i + 1]: starting pc of finally block\r
+            catchStack = new int[maxTryDepth * 2];\r
+        }\r
+        \r
+        /* Save the security domain. Must restore upon normal exit. \r
+         * If we exit the interpreter loop by throwing an exception,\r
+         * set cx.interpreterSecurityDomain to null, and require the\r
+         * catching function to restore it.\r
+         */\r
+        Object savedSecurityDomain = cx.interpreterSecurityDomain;\r
+        cx.interpreterSecurityDomain = theData.securityDomain;\r
+        Object result = undefined;\r
+        \r
+        int pcPrevBranch = pc;\r
+        final int instructionThreshold = cx.instructionThreshold;\r
+        // During function call this will be set to -1 so catch can properly\r
+        // adjust it\r
+        int instructionCount = cx.instructionCount;\r
+        // arbitrary number to add to instructionCount when calling \r
+        // other functions\r
+        final int INVOCATION_COST = 100;\r
+        \r
+        while (pc < iCodeLength) {\r
+            try {\r
+                switch (iCode[pc] & 0xff) {\r
+                    case TokenStream.ENDTRY :\r
+                        tryStackTop--;\r
+                        break;\r
+                    case TokenStream.TRY :\r
+                        i = getTarget(iCode, pc + 1);\r
+                        if (i == pc) i = 0;\r
+                        catchStack[tryStackTop * 2] = i;\r
+                        i = getTarget(iCode, pc + 3);\r
+                        if (i == (pc + 2)) i = 0;\r
+                        catchStack[tryStackTop * 2 + 1] = i;\r
+                        stack[TRY_SCOPE_SHFT + tryStackTop] = scope;\r
+                        ++tryStackTop;\r
+                        pc += 4;\r
+                        break;\r
+                    case TokenStream.GE :\r
+                        --stackTop;\r
+                        rhs = stack[stackTop + 1];    \r
+                        lhs = stack[stackTop];\r
+                        if (rhs == DBL_MRK || lhs == DBL_MRK) {\r
+                            rDbl = stack_double(stack, sDbl, stackTop + 1);\r
+                            lDbl = stack_double(stack, sDbl, stackTop);\r
+                            valBln = (rDbl == rDbl && lDbl == lDbl \r
+                                      && rDbl <= lDbl);\r
+                        }\r
+                        else {\r
+                            valBln = (1 == ScriptRuntime.cmp_LE(rhs, lhs));\r
+                        }\r
+                        stack[stackTop] = valBln ? Boolean.TRUE : Boolean.FALSE;\r
+                        break;\r
+                    case TokenStream.LE :\r
+                        --stackTop;\r
+                        rhs = stack[stackTop + 1];    \r
+                        lhs = stack[stackTop];\r
+                        if (rhs == DBL_MRK || lhs == DBL_MRK) {\r
+                            rDbl = stack_double(stack, sDbl, stackTop + 1);\r
+                            lDbl = stack_double(stack, sDbl, stackTop);\r
+                            valBln = (rDbl == rDbl && lDbl == lDbl \r
+                                      && lDbl <= rDbl);\r
+                        }\r
+                        else {\r
+                            valBln = (1 == ScriptRuntime.cmp_LE(lhs, rhs));\r
+                        }\r
+                        stack[stackTop] = valBln ? Boolean.TRUE : Boolean.FALSE;\r
+                        break;\r
+                    case TokenStream.GT :\r
+                        --stackTop;\r
+                        rhs = stack[stackTop + 1];    \r
+                        lhs = stack[stackTop];\r
+                        if (rhs == DBL_MRK || lhs == DBL_MRK) {\r
+                            rDbl = stack_double(stack, sDbl, stackTop + 1);\r
+                            lDbl = stack_double(stack, sDbl, stackTop);\r
+                            valBln = (rDbl == rDbl && lDbl == lDbl \r
+                                      && rDbl < lDbl);\r
+                        }\r
+                        else {\r
+                            valBln = (1 == ScriptRuntime.cmp_LT(rhs, lhs));\r
+                        }\r
+                        stack[stackTop] = valBln ? Boolean.TRUE : Boolean.FALSE;\r
+                        break;\r
+                    case TokenStream.LT :\r
+                        --stackTop;\r
+                        rhs = stack[stackTop + 1];    \r
+                        lhs = stack[stackTop];\r
+                        if (rhs == DBL_MRK || lhs == DBL_MRK) {\r
+                            rDbl = stack_double(stack, sDbl, stackTop + 1);\r
+                            lDbl = stack_double(stack, sDbl, stackTop);\r
+                            valBln = (rDbl == rDbl && lDbl == lDbl \r
+                                      && lDbl < rDbl);\r
+                        }\r
+                        else {\r
+                            valBln = (1 == ScriptRuntime.cmp_LT(lhs, rhs));\r
+                        }\r
+                        stack[stackTop] = valBln ? Boolean.TRUE : Boolean.FALSE;\r
+                        break;\r
+                    case TokenStream.IN :\r
+                        rhs = stack[stackTop];    \r
+                        if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        valBln = ScriptRuntime.in(lhs, rhs, scope);\r
+                        stack[stackTop] = valBln ? Boolean.TRUE : Boolean.FALSE;\r
+                        break;\r
+                    case TokenStream.INSTANCEOF :\r
+                        rhs = stack[stackTop];    \r
+                        if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        valBln = ScriptRuntime.instanceOf(scope, lhs, rhs);\r
+                        stack[stackTop] = valBln ? Boolean.TRUE : Boolean.FALSE;\r
+                        break;\r
+                    case TokenStream.EQ :\r
+                        --stackTop;\r
+                        valBln = do_eq(stack, sDbl, stackTop);\r
+                        stack[stackTop] = valBln ? Boolean.TRUE : Boolean.FALSE;\r
+                        break;\r
+                    case TokenStream.NE :\r
+                        --stackTop;\r
+                        valBln = !do_eq(stack, sDbl, stackTop);\r
+                        stack[stackTop] = valBln ? Boolean.TRUE : Boolean.FALSE;\r
+                        break;\r
+                    case TokenStream.SHEQ :\r
+                        --stackTop;\r
+                        valBln = do_sheq(stack, sDbl, stackTop);\r
+                        stack[stackTop] = valBln ? Boolean.TRUE : Boolean.FALSE;\r
+                        break;\r
+                    case TokenStream.SHNE :\r
+                        --stackTop;\r
+                        valBln = !do_sheq(stack, sDbl, stackTop);\r
+                        stack[stackTop] = valBln ? Boolean.TRUE : Boolean.FALSE;\r
+                        break;\r
+                    case TokenStream.IFNE :\r
+                        val = stack[stackTop];\r
+                        if (val != DBL_MRK) {\r
+                            valBln = !ScriptRuntime.toBoolean(val);\r
+                        }\r
+                        else {\r
+                            valDbl = sDbl[stackTop];\r
+                            valBln = !(valDbl == valDbl && valDbl != 0.0);\r
+                        }\r
+                        --stackTop;\r
+                        if (valBln) {\r
+                            if (instructionThreshold != 0) {\r
+                                instructionCount += pc + 3 - pcPrevBranch;\r
+                                if (instructionCount > instructionThreshold) {\r
+                                    cx.observeInstructionCount\r
+                                        (instructionCount);\r
+                                    instructionCount = 0;\r
+                                }\r
+                            }\r
+                            pcPrevBranch = pc = getTarget(iCode, pc + 1);\r
+                            continue;\r
+                        }\r
+                        pc += 2;\r
+                        break;\r
+                    case TokenStream.IFEQ :\r
+                        val = stack[stackTop];\r
+                        if (val != DBL_MRK) {\r
+                            valBln = ScriptRuntime.toBoolean(val);\r
+                        }\r
+                        else {\r
+                            valDbl = sDbl[stackTop];\r
+                            valBln = (valDbl == valDbl && valDbl != 0.0);\r
+                        }\r
+                        --stackTop;\r
+                        if (valBln) {\r
+                            if (instructionThreshold != 0) {\r
+                                instructionCount += pc + 3 - pcPrevBranch;\r
+                                if (instructionCount > instructionThreshold) {\r
+                                    cx.observeInstructionCount\r
+                                        (instructionCount);\r
+                                    instructionCount = 0;\r
+                                }\r
+                            }\r
+                            pcPrevBranch = pc = getTarget(iCode, pc + 1);\r
+                            continue;\r
+                        }\r
+                        pc += 2;\r
+                        break;\r
+                    case TokenStream.GOTO :\r
+                        if (instructionThreshold != 0) {\r
+                            instructionCount += pc + 3 - pcPrevBranch;\r
+                            if (instructionCount > instructionThreshold) {\r
+                                cx.observeInstructionCount(instructionCount);\r
+                                instructionCount = 0;\r
+                            }\r
+                        }\r
+                        pcPrevBranch = pc = getTarget(iCode, pc + 1);\r
+                        continue;\r
+                    case TokenStream.GOSUB :\r
+                        sDbl[++stackTop] = pc + 3;\r
+                        if (instructionThreshold != 0) {\r
+                            instructionCount += pc + 3 - pcPrevBranch;\r
+                            if (instructionCount > instructionThreshold) {\r
+                                cx.observeInstructionCount(instructionCount);\r
+                                instructionCount = 0;\r
+                            }\r
+                        }\r
+                        pcPrevBranch = pc = getTarget(iCode, pc + 1);                                    continue;\r
+                    case TokenStream.RETSUB :\r
+                        slot = (iCode[pc + 1] & 0xFF);\r
+                        if (instructionThreshold != 0) {\r
+                            instructionCount += pc + 2 - pcPrevBranch;\r
+                            if (instructionCount > instructionThreshold) {\r
+                                cx.observeInstructionCount(instructionCount);\r
+                                instructionCount = 0;\r
+                            }\r
+                        }\r
+                        pcPrevBranch = pc = (int)sDbl[LOCAL_SHFT + slot];\r
+                        continue;\r
+                    case TokenStream.POP :\r
+                        stackTop--;\r
+                        break;\r
+                    case TokenStream.DUP :\r
+                        stack[stackTop + 1] = stack[stackTop];\r
+                        sDbl[stackTop + 1] = sDbl[stackTop];\r
+                        stackTop++;\r
+                        break;\r
+                    case TokenStream.POPV :\r
+                        result = stack[stackTop];    \r
+                        if (result == DBL_MRK) \r
+                            result = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        break;\r
+                    case TokenStream.RETURN :\r
+                        result = stack[stackTop];    \r
+                        if (result == DBL_MRK) \r
+                            result = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        pc = getTarget(iCode, pc + 1);\r
+                        break;\r
+                    case TokenStream.ASSERT :\r
+                        val = stack[stackTop];\r
+                        if (val != DBL_MRK) {\r
+                            valBln = ScriptRuntime.toBoolean(val);\r
+                        } else {\r
+                            valDbl = sDbl[stackTop];\r
+                            valBln = (valDbl == valDbl && valDbl != 0.0);\r
+                        }\r
+                        --stackTop;\r
+                        if (!valBln) {\r
+                            System.out.println("assertion failed");\r
+                            System.exit(-1);\r
+                        }\r
+                        pc += 2;\r
+                        break;\r
+                    case TokenStream.BITNOT :\r
+                        rIntValue = stack_int32(stack, sDbl, stackTop);\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = ~rIntValue;\r
+                        break;\r
+                    case TokenStream.BITAND :                \r
+                        rIntValue = stack_int32(stack, sDbl, stackTop);\r
+                        --stackTop;\r
+                        lIntValue = stack_int32(stack, sDbl, stackTop);\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = lIntValue & rIntValue;\r
+                        break;\r
+                    case TokenStream.BITOR :\r
+                        rIntValue = stack_int32(stack, sDbl, stackTop);\r
+                        --stackTop;\r
+                        lIntValue = stack_int32(stack, sDbl, stackTop);\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = lIntValue | rIntValue;\r
+                        break;\r
+                    case TokenStream.BITXOR :\r
+                        rIntValue = stack_int32(stack, sDbl, stackTop);\r
+                        --stackTop;\r
+                        lIntValue = stack_int32(stack, sDbl, stackTop);\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = lIntValue ^ rIntValue;\r
+                        break;\r
+                    case TokenStream.LSH :\r
+                        rIntValue = stack_int32(stack, sDbl, stackTop);\r
+                        --stackTop;\r
+                        lIntValue = stack_int32(stack, sDbl, stackTop);\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = lIntValue << rIntValue;\r
+                        break;\r
+                    case TokenStream.RSH :\r
+                        rIntValue = stack_int32(stack, sDbl, stackTop);\r
+                        --stackTop;\r
+                        lIntValue = stack_int32(stack, sDbl, stackTop);\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = lIntValue >> rIntValue;\r
+                        break;\r
+                    case TokenStream.URSH :\r
+                        rIntValue = stack_int32(stack, sDbl, stackTop) & 0x1F;\r
+                        --stackTop;\r
+                        lLongValue = stack_uint32(stack, sDbl, stackTop);\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = lLongValue >>> rIntValue;\r
+                        break;\r
+                    case TokenStream.ADD :\r
+                        --stackTop;\r
+                        do_add(stack, sDbl, stackTop);\r
+                        break;\r
+                    case TokenStream.SUB :\r
+                        rDbl = stack_double(stack, sDbl, stackTop);\r
+                        --stackTop;\r
+                        lDbl = stack_double(stack, sDbl, stackTop);\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = lDbl - rDbl;\r
+                        break;\r
+                    case TokenStream.NEG :\r
+                        rDbl = stack_double(stack, sDbl, stackTop);\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = -rDbl;\r
+                        break;\r
+                    case TokenStream.POS :\r
+                        rDbl = stack_double(stack, sDbl, stackTop);\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = rDbl;\r
+                        break;\r
+                    case TokenStream.MUL :\r
+                        rDbl = stack_double(stack, sDbl, stackTop);\r
+                        --stackTop;\r
+                        lDbl = stack_double(stack, sDbl, stackTop);\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = lDbl * rDbl;\r
+                        break;\r
+                    case TokenStream.DIV :\r
+                        rDbl = stack_double(stack, sDbl, stackTop);\r
+                        --stackTop;\r
+                        lDbl = stack_double(stack, sDbl, stackTop);\r
+                        stack[stackTop] = DBL_MRK;\r
+                        // Detect the divide by zero or let Java do it ?\r
+                        sDbl[stackTop] = lDbl / rDbl;\r
+                        break;\r
+                    case TokenStream.MOD :\r
+                        rDbl = stack_double(stack, sDbl, stackTop);\r
+                        --stackTop;\r
+                        lDbl = stack_double(stack, sDbl, stackTop);\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = lDbl % rDbl;\r
+                        break;\r
+                    case TokenStream.BINDNAME :\r
+                        stack[++stackTop] = \r
+                                ScriptRuntime.bind(scope, \r
+                                         getString(theData.itsStringTable, \r
+                                                   iCode, pc + 1));\r
+                        pc += 2;\r
+                        break;\r
+                    case TokenStream.GETBASE :\r
+                        stack[++stackTop] =\r
+                                ScriptRuntime.getBase(scope, \r
+                                         getString(theData.itsStringTable,\r
+                                                                iCode, pc + 1));\r
+                        pc += 2;\r
+                        break;\r
+                    case TokenStream.SETNAME :\r
+                        rhs = stack[stackTop];    \r
+                        if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        // what about class cast exception here ?\r
+                        stack[stackTop] = ScriptRuntime.setName\r
+                            ((Scriptable)lhs, rhs, scope, \r
+                             getString(theData.itsStringTable, iCode, pc + 1));\r
+                        pc += 2;\r
+                        break;\r
+                    case TokenStream.DELPROP :\r
+                        rhs = stack[stackTop];    \r
+                        if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] = ScriptRuntime.delete(lhs, rhs);\r
+                        break;\r
+                    case TokenStream.GETPROP :\r
+                        name = (String)stack[stackTop];\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] \r
+                                = ScriptRuntime.getProp(lhs, name, scope);\r
+                        break;\r
+                    case TokenStream.SETPROP :\r
+                        rhs = stack[stackTop];    \r
+                        if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        name = (String)stack[stackTop];\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] \r
+                                = ScriptRuntime.setProp(lhs, name, rhs, scope);\r
+                        break;\r
+                    case TokenStream.GETELEM :\r
+                        id = stack[stackTop];    \r
+                        if (id == DBL_MRK) id = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] \r
+                                = ScriptRuntime.getElem(lhs, id, scope);\r
+                        break;\r
+                    case TokenStream.SETELEM :\r
+                        rhs = stack[stackTop];    \r
+                        if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        id = stack[stackTop];    \r
+                        if (id == DBL_MRK) id = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] \r
+                                = ScriptRuntime.setElem(lhs, id, rhs, scope);\r
+                        break;\r
+                    case TokenStream.PROPINC :\r
+                        name = (String)stack[stackTop];\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] \r
+                                = ScriptRuntime.postIncrement(lhs, name, scope);\r
+                        break;\r
+                    case TokenStream.PROPDEC :\r
+                        name = (String)stack[stackTop];\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] \r
+                                = ScriptRuntime.postDecrement(lhs, name, scope);\r
+                        break;\r
+                    case TokenStream.ELEMINC :\r
+                        rhs = stack[stackTop];    \r
+                        if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] \r
+                           = ScriptRuntime.postIncrementElem(lhs, rhs, scope);\r
+                        break;\r
+                    case TokenStream.ELEMDEC :\r
+                        rhs = stack[stackTop];    \r
+                        if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] \r
+                           = ScriptRuntime.postDecrementElem(lhs, rhs, scope);\r
+                        break;\r
+                    case TokenStream.GETTHIS :\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] \r
+                            = ScriptRuntime.getThis((Scriptable)lhs);\r
+                        break;\r
+                    case TokenStream.NEWTEMP :\r
+                        slot = (iCode[++pc] & 0xFF);\r
+                        stack[LOCAL_SHFT + slot] = stack[stackTop];\r
+                        sDbl[LOCAL_SHFT + slot] = sDbl[stackTop];\r
+                        break;\r
+                    case TokenStream.USETEMP :\r
+                        slot = (iCode[++pc] & 0xFF);\r
+                        ++stackTop;\r
+                        stack[stackTop] = stack[LOCAL_SHFT + slot];\r
+                        sDbl[stackTop] = sDbl[LOCAL_SHFT + slot];\r
+                        break;\r
+                    case TokenStream.CALLSPECIAL :\r
+                        if (instructionThreshold != 0) {\r
+                            instructionCount += INVOCATION_COST;\r
+                            cx.instructionCount = instructionCount;\r
+                            instructionCount = -1;\r
+                        }\r
+                        int lineNum = (iCode[pc + 1] << 8) \r
+                                      | (iCode[pc + 2] & 0xFF);   \r
+                        name = getString(theData.itsStringTable, iCode, pc + 3);\r
+                        count = (iCode[pc + 5] << 8) | (iCode[pc + 6] & 0xFF);\r
+                        outArgs = new Object[count];\r
+                        for (i = count - 1; i >= 0; i--) {\r
+                            val = stack[stackTop];    \r
+                            if (val == DBL_MRK) \r
+                                val = doubleWrap(sDbl[stackTop]);\r
+                            outArgs[i] = val;\r
+                            --stackTop;\r
+                        }\r
+                        rhs = stack[stackTop];    \r
+                        if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] = ScriptRuntime.callSpecial(\r
+                                            cx, lhs, rhs, outArgs, \r
+                                            thisObj, scope, name, lineNum);\r
+                        pc += 6;\r
+                        instructionCount = cx.instructionCount;\r
+                        break;\r
+                    case TokenStream.CALL :\r
+                        if (instructionThreshold != 0) {\r
+                            instructionCount += INVOCATION_COST;\r
+                            cx.instructionCount = instructionCount;\r
+                            instructionCount = -1;\r
+                        }\r
+                        cx.instructionCount = instructionCount;\r
+                        count = (iCode[pc + 3] << 8) | (iCode[pc + 4] & 0xFF);\r
+                        outArgs = new Object[count];\r
+                        for (i = count - 1; i >= 0; i--) {\r
+                            val = stack[stackTop];    \r
+                            if (val == DBL_MRK) \r
+                                val = doubleWrap(sDbl[stackTop]);\r
+                            outArgs[i] = val;\r
+                            --stackTop;\r
+                        }\r
+                        rhs = stack[stackTop];    \r
+                        if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        if (lhs == undefined) {\r
+                            lhs = getString(theData.itsStringTable, iCode, \r
+                                            pc + 1);\r
+                        }\r
+                        Scriptable calleeScope = scope;\r
+                        if (theData.itsNeedsActivation) {\r
+                            calleeScope = ScriptableObject.\r
+                                getTopLevelScope(scope);\r
+                        }\r
+                        stack[stackTop] = ScriptRuntime.call(cx, lhs, rhs, \r
+                                                             outArgs, \r
+                                                             calleeScope);\r
+                        pc += 4;                                                                         instructionCount = cx.instructionCount;\r
+                        break;\r
+                    case TokenStream.NEW :\r
+                        if (instructionThreshold != 0) {\r
+                            instructionCount += INVOCATION_COST;\r
+                            cx.instructionCount = instructionCount;\r
+                            instructionCount = -1;\r
+                        }\r
+                        count = (iCode[pc + 3] << 8) | (iCode[pc + 4] & 0xFF);\r
+                        outArgs = new Object[count];\r
+                        for (i = count - 1; i >= 0; i--) {\r
+                            val = stack[stackTop];    \r
+                            if (val == DBL_MRK) \r
+                                val = doubleWrap(sDbl[stackTop]);\r
+                            outArgs[i] = val;\r
+                            --stackTop;\r
+                        }\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        if (lhs == undefined && \r
+                            (iCode[pc+1] << 8) + (iCode[pc+2] & 0xFF) != -1) \r
+                        {\r
+                            // special code for better error message for call \r
+                            //  to undefined\r
+                            lhs = getString(theData.itsStringTable, iCode, \r
+                                            pc + 1);\r
+                        }\r
+                        stack[stackTop] = ScriptRuntime.newObject(cx, lhs, \r
+                                                                  outArgs, \r
+                                                                  scope);\r
+                        pc += 4;                                                                         instructionCount = cx.instructionCount;\r
+                        break;\r
+                    case TokenStream.TYPEOF :\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] = ScriptRuntime.typeof(lhs);\r
+                        break;\r
+                    case TokenStream.TYPEOFNAME :\r
+                        name = getString(theData.itsStringTable, iCode, pc + 1);\r
+                        stack[++stackTop] \r
+                                    = ScriptRuntime.typeofName(scope, name);\r
+                        pc += 2;\r
+                        break;\r
+                    case TokenStream.STRING :\r
+                        stack[++stackTop] = getString(theData.itsStringTable,\r
+                                                                iCode, pc + 1);\r
+                        pc += 2;\r
+                        break;\r
+                    case TokenStream.NUMBER :\r
+                        ++stackTop;\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = getNumber(theData.itsNumberTable,\r
+                                                   iCode, pc + 1);\r
+                        pc += 2;\r
+                        break;\r
+                    case TokenStream.NAME :\r
+                        stack[++stackTop] = ScriptRuntime.name(scope,\r
+                                       getString(theData.itsStringTable,\r
+                                                                iCode, pc + 1));\r
+                        pc += 2;\r
+                        break;\r
+                    case TokenStream.NAMEINC :\r
+                        stack[++stackTop] = ScriptRuntime.postIncrement(scope,\r
+                                       getString(theData.itsStringTable,\r
+                                                                iCode, pc + 1));\r
+                        pc += 2;\r
+                        break;\r
+                    case TokenStream.NAMEDEC :\r
+                        stack[++stackTop] = ScriptRuntime.postDecrement(scope,\r
+                                       getString(theData.itsStringTable,\r
+                                                                iCode, pc + 1));\r
+                        pc += 2;\r
+                        break;\r
+                    case TokenStream.SETVAR :\r
+                        slot = (iCode[++pc] & 0xFF);\r
+                        stack[VAR_SHFT + slot] = stack[stackTop];\r
+                        sDbl[VAR_SHFT + slot] = sDbl[stackTop];\r
+                        break;\r
+                    case TokenStream.GETVAR :\r
+                        slot = (iCode[++pc] & 0xFF);\r
+                        ++stackTop;\r
+                        stack[stackTop] = stack[VAR_SHFT + slot];\r
+                        sDbl[stackTop] = sDbl[VAR_SHFT + slot];\r
+                        break;\r
+                    case TokenStream.VARINC :\r
+                        slot = (iCode[++pc] & 0xFF);\r
+                        ++stackTop;\r
+                        stack[stackTop] = stack[VAR_SHFT + slot];\r
+                        sDbl[stackTop] = sDbl[VAR_SHFT + slot];\r
+                        stack[VAR_SHFT + slot] = DBL_MRK;\r
+                        sDbl[VAR_SHFT + slot] \r
+                            = stack_double(stack, sDbl, stackTop) + 1.0;\r
+                        break;\r
+                    case TokenStream.VARDEC :\r
+                        slot = (iCode[++pc] & 0xFF);\r
+                        ++stackTop;\r
+                        stack[stackTop] = stack[VAR_SHFT + slot];\r
+                        sDbl[stackTop] = sDbl[VAR_SHFT + slot];\r
+                        stack[VAR_SHFT + slot] = DBL_MRK;\r
+                        sDbl[VAR_SHFT + slot] \r
+                            = stack_double(stack, sDbl, stackTop) - 1.0;\r
+                        break;\r
+                    case TokenStream.ZERO :\r
+                        ++stackTop;\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = 0;\r
+                        break;\r
+                    case TokenStream.ONE :\r
+                        ++stackTop;\r
+                        stack[stackTop] = DBL_MRK;\r
+                        sDbl[stackTop] = 1;\r
+                        break;\r
+                    case TokenStream.NULL :\r
+                        stack[++stackTop] = null;\r
+                        break;\r
+                    case TokenStream.THIS :\r
+                        stack[++stackTop] = thisObj;\r
+                        break;\r
+                    case TokenStream.THISFN :\r
+                        stack[++stackTop] = fnOrScript;\r
+                        break;\r
+                    case TokenStream.FALSE :\r
+                        stack[++stackTop] = Boolean.FALSE;\r
+                        break;\r
+                    case TokenStream.TRUE :\r
+                        stack[++stackTop] = Boolean.TRUE;\r
+                        break;\r
+                    case TokenStream.UNDEFINED :\r
+                        stack[++stackTop] = Undefined.instance;\r
+                        break;\r
+                    case TokenStream.THROW :\r
+                        result = stack[stackTop];\r
+                        if (result == DBL_MRK) \r
+                            result = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        throw new JavaScriptException(result);\r
+                    case TokenStream.JTHROW :\r
+                        result = stack[stackTop];\r
+                        // No need to check for DBL_MRK: result is Exception\r
+                        --stackTop;\r
+                        if (result instanceof JavaScriptException)\r
+                            throw (JavaScriptException)result;\r
+                        else\r
+                            throw (RuntimeException)result;\r
+                    case TokenStream.ENTERWITH :\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        scope = ScriptRuntime.enterWith(lhs, scope);\r
+                        break;\r
+                    case TokenStream.LEAVEWITH :\r
+                        scope = ScriptRuntime.leaveWith(scope);\r
+                        break;\r
+                    case TokenStream.NEWSCOPE :\r
+                        stack[++stackTop] = ScriptRuntime.newScope();\r
+                        break;\r
+                    case TokenStream.ENUMINIT :\r
+                        slot = (iCode[++pc] & 0xFF);\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        stack[LOCAL_SHFT + slot] \r
+                            = ScriptRuntime.initEnum(lhs, scope);\r
+                        break;\r
+                    case TokenStream.ENUMNEXT :\r
+                        slot = (iCode[++pc] & 0xFF);\r
+                        val = stack[LOCAL_SHFT + slot];    \r
+                        ++stackTop;\r
+                        stack[stackTop] = ScriptRuntime.\r
+                            nextEnum((Enumeration)val);\r
+                        break;\r
+                    case TokenStream.GETPROTO :\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] = ScriptRuntime.getProto(lhs, scope);\r
+                        break;\r
+                    case TokenStream.GETPARENT :\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] = ScriptRuntime.getParent(lhs);\r
+                        break;\r
+                    case TokenStream.GETSCOPEPARENT :\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop] = ScriptRuntime.getParent(lhs, scope);\r
+                        break;\r
+                    case TokenStream.SETPROTO :\r
+                        rhs = stack[stackTop];    \r
+                        if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop]\r
+                                = ScriptRuntime.setProto(lhs, rhs, scope);\r
+                        break;\r
+                    case TokenStream.SETPARENT :\r
+                        rhs = stack[stackTop];    \r
+                        if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);\r
+                        --stackTop;\r
+                        lhs = stack[stackTop];    \r
+                        if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);\r
+                        stack[stackTop]\r
+                                = ScriptRuntime.setParent(lhs, rhs, scope);\r
+                        break;\r
+                    case TokenStream.SCOPE :\r
+                        stack[++stackTop] = scope;\r
+                        break;\r
+                    case TokenStream.CLOSURE :\r
+                        i = (iCode[pc + 1] << 8) | (iCode[pc + 2] & 0xFF);\r
+                        stack[++stackTop] \r
+                            = new InterpretedFunction(\r
+                                    theData.itsNestedFunctions[i],\r
+                                    scope, cx);\r
+                        createFunctionObject(\r
+                              (InterpretedFunction)stack[stackTop], scope);\r
+                        pc += 2;\r
+                        break;\r
+                    case TokenStream.OBJECT :\r
+                        i = (iCode[pc + 1] << 8) | (iCode[pc + 2] & 0xFF);                    \r
+                        stack[++stackTop] = theData.itsRegExpLiterals[i];\r
+                        pc += 2;\r
+                        break;\r
+                    case TokenStream.SOURCEFILE :    \r
+                        cx.interpreterSourceFile = theData.itsSourceFile;\r
+                        break;\r
+                    case TokenStream.LINE :    \r
+                    case TokenStream.BREAKPOINT :\r
+                        i = (iCode[pc + 1] << 8) | (iCode[pc + 2] & 0xFF);                    \r
+                        cx.interpreterLine = i;\r
+                        if (frame != null)\r
+                            frame.setLineNumber(i);\r
+                        if ((iCode[pc] & 0xff) == TokenStream.BREAKPOINT ||\r
+                            cx.inLineStepMode) \r
+                        {\r
+                            cx.getDebuggableEngine().\r
+                                getDebugger().handleBreakpointHit(cx);\r
+                        }\r
+                        pc += 2;\r
+                        break;\r
+                    default :\r
+                        dumpICode(theData);\r
+                        throw new RuntimeException("Unknown icode : "\r
+                                     + (iCode[pc] & 0xff) + " @ pc : " + pc);\r
+                }\r
+                pc++;\r
+            }\r
+            catch (Throwable ex) {\r
+                cx.interpreterSecurityDomain = null;\r
+            \r
+                if (instructionThreshold != 0) {\r
+                    if (instructionCount < 0) {\r
+                        // throw during function call\r
+                        instructionCount = cx.instructionCount;\r
+                    }\r
+                    else {\r
+                        // throw during any other operation\r
+                        instructionCount += pc - pcPrevBranch;\r
+                        cx.instructionCount = instructionCount;\r
+                    }\r
+                }\r
+\r
+                final int SCRIPT_THROW = 0, ECMA = 1, RUNTIME = 2, OTHER = 3;\r
+\r
+                int exType;\r
+                Object errObj; // Object seen by catch\r
+                if (ex instanceof JavaScriptException) {\r
+                    errObj = ScriptRuntime.\r
+                        unwrapJavaScriptException((JavaScriptException)ex);\r
+                    exType = SCRIPT_THROW;\r
+                }\r
+                else if (ex instanceof EcmaError) {\r
+                    // an offical ECMA error object,\r
+                    errObj = ((EcmaError)ex).getErrorObject();\r
+                    exType = ECMA;\r
+                }\r
+                else if (ex instanceof RuntimeException) {\r
+                    errObj = ex;\r
+                    exType = RUNTIME;\r
+                }\r
+                else {\r
+                    errObj = ex; // Error instance\r
+                    exType = OTHER;\r
+                }\r
+\r
+                if (exType != OTHER && cx.debugger != null) {\r
+                    cx.debugger.handleExceptionThrown(cx, errObj);\r
+                }\r
+\r
+                boolean rethrow = true;\r
+                if (exType != OTHER && tryStackTop > 0) {\r
+                    --tryStackTop;\r
+                    if (exType == SCRIPT_THROW || exType == ECMA) {\r
+                        // Check for catch only for \r
+                        // JavaScriptException and EcmaError\r
+                        pc = catchStack[tryStackTop * 2];\r
+                        if (pc != 0) {\r
+                            // Has catch block\r
+                            rethrow = false;\r
+                        }\r
+                    }\r
+                    if (rethrow) {\r
+                        pc = catchStack[tryStackTop * 2 + 1];\r
+                        if (pc != 0) {\r
+                            // has finally block\r
+                            rethrow = false;\r
+                            errObj = ex;\r
+                        }\r
+                    }\r
+                }\r
+\r
+                if (rethrow) {\r
+                    if (frame != null)\r
+                        cx.popFrame();\r
+                    \r
+                    if (exType == SCRIPT_THROW)\r
+                        throw (JavaScriptException)ex;\r
+                    if (exType == ECMA || exType == RUNTIME) \r
+                        throw (RuntimeException)ex;\r
+                    throw (Error)ex;\r
+                }\r
+            \r
+                // We caught an exception,\r
+\r
+                // Notify instruction observer if necessary\r
+                // and point pcPrevBranch to start of catch/finally block\r
+                if (instructionThreshold != 0) {\r
+                    if (instructionCount > instructionThreshold) {\r
+                        // Note: this can throw Error \r
+                        cx.observeInstructionCount(instructionCount);\r
+                        instructionCount = 0;\r
+                    }\r
+                }\r
+                pcPrevBranch = pc;\r
+\r
+                // prepare stack and restore this function's security domain.\r
+                scope = (Scriptable)stack[TRY_SCOPE_SHFT + tryStackTop];\r
+                stackTop = 0;\r
+                stack[0] = errObj;\r
+                cx.interpreterSecurityDomain = theData.securityDomain;\r
+            }\r
+        }\r
+        cx.interpreterSecurityDomain = savedSecurityDomain;\r
+        if (frame != null)\r
+            cx.popFrame();\r
+\r
+        if (instructionThreshold != 0) {\r
+            if (instructionCount > instructionThreshold) {\r
+                cx.observeInstructionCount(instructionCount);\r
+                instructionCount = 0;\r
+            }\r
+            cx.instructionCount = instructionCount;\r
+        }\r
+\r
+        return result;    \r
+    }\r
+    \r
+    private static Object doubleWrap(double x) {\r
+        return new Double(x);\r
+    }\r
+\r
+    private static int stack_int32(Object[] stack, double[] stackDbl, int i) {\r
+        Object x = stack[i];\r
+        return (x != DBL_MRK)\r
+            ? ScriptRuntime.toInt32(x)\r
+            : ScriptRuntime.toInt32(stackDbl[i]);\r
+    }\r
+    \r
+    private static long stack_uint32(Object[] stack, double[] stackDbl, int i) {\r
+        Object x = stack[i];\r
+        return (x != DBL_MRK)\r
+            ? ScriptRuntime.toUint32(x)\r
+            : ScriptRuntime.toUint32(stackDbl[i]);\r
+    }\r
+    \r
+    private static double stack_double(Object[] stack, double[] stackDbl, \r
+                                       int i) \r
+    {\r
+        Object x = stack[i];\r
+        return (x != DBL_MRK) ? ScriptRuntime.toNumber(x) : stackDbl[i];\r
+    }\r
+    \r
+    private static void do_add(Object[] stack, double[] stackDbl, int stackTop)\r
+    {\r
+        Object rhs = stack[stackTop + 1];    \r
+        Object lhs = stack[stackTop];\r
+        if (rhs == DBL_MRK) {\r
+            double rDbl = stackDbl[stackTop + 1];\r
+            if (lhs == DBL_MRK) {\r
+                stackDbl[stackTop] += rDbl;\r
+            }\r
+            else {\r
+                do_add(lhs, rDbl, stack, stackDbl, stackTop, true);\r
+            }\r
+        }\r
+        else if (lhs == DBL_MRK) {\r
+            do_add(rhs, stackDbl[stackTop], stack, stackDbl, stackTop, false);\r
+        }\r
+        else {\r
+            if (lhs instanceof Scriptable)\r
+                lhs = ((Scriptable) lhs).getDefaultValue(null);\r
+            if (rhs instanceof Scriptable)\r
+                rhs = ((Scriptable) rhs).getDefaultValue(null);\r
+            if (lhs instanceof String || rhs instanceof String) {\r
+                stack[stackTop] = ScriptRuntime.toString(lhs)\r
+                                   + ScriptRuntime.toString(rhs);\r
+            }\r
+            else {\r
+                double lDbl = (lhs instanceof Number)\r
+                    ? ((Number)lhs).doubleValue() : ScriptRuntime.toNumber(lhs);\r
+                double rDbl = (rhs instanceof Number)\r
+                    ? ((Number)rhs).doubleValue() : ScriptRuntime.toNumber(rhs);\r
+                stack[stackTop] = DBL_MRK;\r
+                stackDbl[stackTop] = lDbl + rDbl;\r
+            }\r
+        }\r
+    }\r
+    \r
+    // x + y when x is Number, see \r
+    private static void do_add\r
+        (Object lhs, double rDbl, \r
+         Object[] stack, double[] stackDbl, int stackTop, \r
+         boolean left_right_order) \r
+    {\r
+        if (lhs instanceof Scriptable) {\r
+            if (lhs == Undefined.instance) { lhs = ScriptRuntime.NaNobj; }\r
+            lhs = ((Scriptable)lhs).getDefaultValue(null);\r
+        }\r
+        if (lhs instanceof String) {\r
+            if (left_right_order) {\r
+                stack[stackTop] = (String)lhs + ScriptRuntime.toString(rDbl);\r
+            }\r
+            else {\r
+                stack[stackTop] = ScriptRuntime.toString(rDbl) + (String)lhs;\r
+            }\r
+        }\r
+        else {\r
+            double lDbl = (lhs instanceof Number) \r
+                ? ((Number)lhs).doubleValue() : ScriptRuntime.toNumber(lhs);\r
+            stack[stackTop] = DBL_MRK;\r
+            stackDbl[stackTop] = lDbl + rDbl;\r
+        }\r
+    }\r
+\r
+\r
+    \r
+    private static boolean do_eq(Object[] stack, double[] stackDbl,\r
+                                 int stackTop)\r
+    {\r
+        boolean result;\r
+        Object rhs = stack[stackTop + 1];    \r
+        Object lhs = stack[stackTop];\r
+        if (rhs == DBL_MRK) {\r
+            if (lhs == DBL_MRK) {\r
+                result = (stackDbl[stackTop] == stackDbl[stackTop + 1]);\r
+            }\r
+            else {\r
+                result = do_eq(stackDbl[stackTop + 1], lhs);\r
+            }\r
+        }\r
+        else {\r
+            if (lhs == DBL_MRK) {\r
+                result = do_eq(stackDbl[stackTop], rhs);\r
+            }\r
+            else {\r
+                result = ScriptRuntime.eq(lhs, rhs);\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+    \r
+// Optimized version of ScriptRuntime.eq if x is a Number    \r
+    private static boolean do_eq(double x, Object y) {\r
+        for (;;) {\r
+            if (y instanceof Number) {\r
+                return x == ((Number) y).doubleValue();\r
+            }\r
+            if (y instanceof String) {\r
+                return x == ScriptRuntime.toNumber((String)y);\r
+            }\r
+            if (y instanceof Boolean) {\r
+                return x == (((Boolean)y).booleanValue() ? 1 : 0);\r
+            }\r
+            if (y instanceof Scriptable) {\r
+                if (y == Undefined.instance) { return false; }\r
+                y = ScriptRuntime.toPrimitive(y);\r
+                continue;\r
+            }\r
+            return false;\r
+        }\r
+    }\r
+\r
+    private static boolean do_sheq(Object[] stack, double[] stackDbl,\r
+                                   int stackTop)\r
+    {\r
+        boolean result;\r
+        Object rhs = stack[stackTop + 1];    \r
+        Object lhs = stack[stackTop];\r
+        if (rhs == DBL_MRK) {\r
+            double rDbl = stackDbl[stackTop + 1];\r
+            if (lhs == DBL_MRK) {\r
+                result = (stackDbl[stackTop] == rDbl);\r
+            }\r
+            else {\r
+                result = (lhs instanceof Number);\r
+                if (result) {\r
+                    result = (((Number)lhs).doubleValue() == rDbl);\r
+                }\r
+            }\r
+        }\r
+        else if (rhs instanceof Number) {\r
+            double rDbl = ((Number)rhs).doubleValue();\r
+            if (lhs == DBL_MRK) {\r
+                result = (stackDbl[stackTop] == rDbl);\r
+            }\r
+            else {\r
+                result = (lhs instanceof Number);\r
+                if (result) {\r
+                    result = (((Number)lhs).doubleValue() == rDbl);\r
+                }\r
+            }\r
+        }\r
+        else {\r
+            result = ScriptRuntime.shallowEq(lhs, rhs);\r
+        }\r
+        return result;\r
+    }\r
+    \r
+    private int version;\r
+    private boolean inLineStepMode;\r
+    private StringBuffer debugSource;\r
+\r
+    private static final Object DBL_MRK = new Object();\r
+}\r
diff --git a/src/org/mozilla/javascript/InterpreterData.java b/src/org/mozilla/javascript/InterpreterData.java
new file mode 100644 (file)
index 0000000..cdf4e45
--- /dev/null
@@ -0,0 +1,130 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Roger Lawrence\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.Vector;\r
+\r
+class InterpreterData {\r
+    \r
+    static final int INITIAL_MAX_ICODE_LENGTH = 1024;\r
+    static final int INITIAL_STRINGTABLE_SIZE = 64;\r
+    static final int INITIAL_NUMBERTABLE_SIZE = 64;\r
+    \r
+    InterpreterData(int lastICodeTop, int lastStringTableIndex, \r
+                    int lastNumberTableIndex, Object securityDomain,\r
+                    boolean useDynamicScope, boolean checkThis)\r
+    {\r
+        itsICodeTop = lastICodeTop == 0 \r
+                      ? INITIAL_MAX_ICODE_LENGTH\r
+                      : lastICodeTop * 2;\r
+        itsICode = new byte[itsICodeTop];\r
+\r
+        itsStringTable = new String[lastStringTableIndex == 0\r
+                                    ? INITIAL_STRINGTABLE_SIZE\r
+                                    : lastStringTableIndex * 2];\r
+\r
+        itsNumberTable = new double[lastNumberTableIndex == 0\r
+                                    ? INITIAL_NUMBERTABLE_SIZE\r
+                                    : lastNumberTableIndex * 2];\r
+        \r
+        itsUseDynamicScope = useDynamicScope;\r
+        itsCheckThis = checkThis;\r
+        if (securityDomain == null)\r
+            Context.checkSecurityDomainRequired();\r
+        this.securityDomain = securityDomain;\r
+    }\r
+    \r
+    public boolean placeBreakpoint(int line) { // XXX throw exn?\r
+        int offset = getOffset(line);\r
+        if (offset != -1 && (itsICode[offset] == (byte)TokenStream.LINE ||\r
+                             itsICode[offset] == (byte)TokenStream.BREAKPOINT))\r
+        {\r
+            itsICode[offset] = (byte) TokenStream.BREAKPOINT;\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+    \r
+    public boolean removeBreakpoint(int line) {\r
+        int offset = getOffset(line);\r
+        if (offset != -1 && itsICode[offset] == (byte) TokenStream.BREAKPOINT)\r
+        {\r
+            itsICode[offset] = (byte) TokenStream.LINE;\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+    \r
+    private int getOffset(int line) {\r
+        int offset = itsLineNumberTable.getInt(line, -1);\r
+        if (0 <= offset && offset <= itsICode.length) {\r
+            return offset;\r
+        }\r
+        return -1;\r
+    }    \r
+    \r
+    String itsName;\r
+    String itsSource;\r
+    String itsSourceFile;\r
+    boolean itsNeedsActivation;\r
+    boolean itsFromEvalCode;\r
+    boolean itsUseDynamicScope;\r
+    boolean itsCheckThis;\r
+    byte itsFunctionType;\r
+\r
+    String[] itsStringTable;\r
+    int itsStringTableIndex;\r
+\r
+    double[] itsNumberTable;\r
+    int itsNumberTableIndex;\r
+    \r
+    InterpretedFunction[] itsNestedFunctions;\r
+    \r
+    Object[] itsRegExpLiterals;\r
+\r
+    byte[] itsICode;\r
+    int itsICodeTop;\r
+    \r
+    int itsMaxLocals;\r
+    int itsMaxArgs;\r
+    int itsMaxStack;\r
+    int itsMaxTryDepth;\r
+    \r
+    UintMap itsLineNumberTable;\r
+\r
+    Object securityDomain;\r
+}\r
diff --git a/src/org/mozilla/javascript/InterpreterFrame.java b/src/org/mozilla/javascript/InterpreterFrame.java
new file mode 100644 (file)
index 0000000..3df4fce
--- /dev/null
@@ -0,0 +1,77 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.Vector;\r
+\r
+import org.mozilla.javascript.debug.*;\r
+\r
+class InterpreterFrame implements DebugFrame {\r
+    \r
+    InterpreterFrame(Scriptable scope, InterpreterData data, Scriptable obj) {\r
+        this.scope = scope;\r
+        this.data = data;\r
+        this.lineNumber = -1;\r
+        this.obj = obj;\r
+    }\r
+\r
+    public Scriptable getVariableObject() {\r
+        return scope;\r
+    }\r
+    \r
+    public String getSourceName() {\r
+        return data.itsSourceFile;\r
+    }\r
+    \r
+    public void setLineNumber(int lineNumber) {\r
+        this.lineNumber = lineNumber;\r
+    }\r
+    \r
+    public int getLineNumber() {\r
+        return lineNumber;\r
+    }\r
+    \r
+    public DebuggableScript getScript() {\r
+        if (obj instanceof DebuggableScript)\r
+            return (DebuggableScript) obj;\r
+        return null;\r
+    }\r
+    \r
+    private Scriptable scope;\r
+    private InterpreterData data;\r
+    private Scriptable obj;\r
+    private int lineNumber;\r
+}\r
diff --git a/src/org/mozilla/javascript/Invoker.java b/src/org/mozilla/javascript/Invoker.java
new file mode 100644 (file)
index 0000000..1d877d1
--- /dev/null
@@ -0,0 +1,54 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Norris Boyd\r
+ * David C. Navas\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.lang.reflect.Method;\r
+\r
+/**\r
+ * Avoid cost of java.lang.reflect.Method.invoke() by compiling a class to\r
+ * perform the method call directly.\r
+ */\r
+public abstract class Invoker {\r
+\r
+    public abstract Object invoke(Object that, Object [] args);\r
+\r
+    /** Factory method to get invoker for given method */\r
+    public Invoker createInvoker(Method method, Class[] types) {\r
+        return null;\r
+    }\r
+\r
+}\r
diff --git a/src/org/mozilla/javascript/JavaMembers.java b/src/org/mozilla/javascript/JavaMembers.java
new file mode 100644 (file)
index 0000000..fbe225c
--- /dev/null
@@ -0,0 +1,591 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Frank Mitchell\r
+ * Mike Shaver\r
+ * Kurt Westerfeld\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.lang.reflect.*;\r
+import java.util.Hashtable;\r
+import java.util.Enumeration;\r
+\r
+/**\r
+ *\r
+ * @author Mike Shaver\r
+ * @author Norris Boyd\r
+ * @see NativeJavaObject\r
+ * @see NativeJavaClass\r
+ */\r
+class JavaMembers {\r
+\r
+    JavaMembers(Scriptable scope, Class cl) {\r
+        this.members = new Hashtable(23);\r
+        this.staticMembers = new Hashtable(7);\r
+        this.cl = cl;\r
+        reflect(scope, cl);\r
+    }\r
+\r
+    boolean has(String name, boolean isStatic) {\r
+        Hashtable ht = isStatic ? staticMembers : members;\r
+        Object obj = ht.get(name);\r
+        if (obj != null) {\r
+            return true;\r
+        } else {\r
+            Member member = this.findExplicitFunction(name, isStatic);\r
+            return member != null;\r
+        }\r
+    }\r
+\r
+    Object get(Scriptable scope, String name, Object javaObject,\r
+               boolean isStatic)\r
+    {\r
+        Hashtable ht = isStatic ? staticMembers : members;\r
+        Object member = ht.get(name);\r
+        if (!isStatic && member == null) {\r
+            // Try to get static member from instance (LC3)\r
+            member = staticMembers.get(name);\r
+        }\r
+        if (member == null) {\r
+            member = this.getExplicitFunction(scope, name, \r
+                                              javaObject, isStatic);\r
+            if (member == null)\r
+                return Scriptable.NOT_FOUND;\r
+        }\r
+        if (member instanceof Scriptable)\r
+            return member;      // why is this here?\r
+        Object rval;\r
+        Class type;\r
+        try {\r
+            if (member instanceof BeanProperty) {\r
+                BeanProperty bp = (BeanProperty) member;\r
+                rval = bp.getter.invoke(javaObject, ScriptRuntime.emptyArgs);\r
+                type = bp.getter.getReturnType();\r
+            } else {\r
+                Field field = (Field) member;\r
+                rval = field.get(isStatic ? null : javaObject);\r
+                type = field.getType();\r
+            }\r
+        } catch (IllegalAccessException accEx) {\r
+            throw new RuntimeException("unexpected IllegalAccessException "+\r
+                                       "accessing Java field");\r
+        } catch (InvocationTargetException e) {\r
+            throw WrappedException.wrapException(\r
+                JavaScriptException.wrapException(scope, e));\r
+        }\r
+        // Need to wrap the object before we return it.\r
+        scope = ScriptableObject.getTopLevelScope(scope);\r
+        return NativeJavaObject.wrap(scope, rval, type);\r
+    }\r
+\r
+    Member findExplicitFunction(String name, boolean isStatic) {\r
+        Hashtable ht = isStatic ? staticMembers : members;\r
+        int sigStart = name.indexOf('(');\r
+        Member[] methodsOrCtors = null;\r
+        NativeJavaMethod method = null;\r
+        boolean isCtor = (isStatic && sigStart == 0);\r
+\r
+        if (isCtor) {\r
+            // Explicit request for an overloaded constructor\r
+            methodsOrCtors = ctors;\r
+        }\r
+        else if (sigStart > 0) {\r
+            // Explicit request for an overloaded method\r
+            String trueName = name.substring(0,sigStart);\r
+            Object obj = ht.get(trueName);\r
+            if (!isStatic && obj == null) {\r
+                // Try to get static member from instance (LC3)\r
+                obj = staticMembers.get(trueName);\r
+            }\r
+            if (obj != null && obj instanceof NativeJavaMethod) {\r
+                method = (NativeJavaMethod)obj;\r
+                methodsOrCtors = method.getMethods();\r
+            }\r
+        }\r
+\r
+        if (methodsOrCtors != null) {\r
+            for (int i = 0; i < methodsOrCtors.length; i++) {\r
+                String nameWithSig = \r
+                    NativeJavaMethod.signature(methodsOrCtors[i]);\r
+                if (name.equals(nameWithSig)) {\r
+                    return methodsOrCtors[i];\r
+                }\r
+            }\r
+        }\r
+\r
+        return null;\r
+    }\r
+\r
+    Object getExplicitFunction(Scriptable scope, String name, \r
+                               Object javaObject, boolean isStatic) \r
+    {\r
+        Hashtable ht = isStatic ? staticMembers : members;\r
+        Object member = null;\r
+        Member methodOrCtor = this.findExplicitFunction(name, isStatic);\r
+\r
+        if (methodOrCtor != null) {\r
+            Scriptable prototype = \r
+                ScriptableObject.getFunctionPrototype(scope);\r
+\r
+            if (methodOrCtor instanceof Constructor) {\r
+                NativeJavaConstructor fun = \r
+                    new NativeJavaConstructor((Constructor)methodOrCtor);\r
+                fun.setPrototype(prototype);\r
+                member = fun;\r
+                ht.put(name, fun);\r
+            } else {\r
+                String trueName = methodOrCtor.getName();\r
+                member = ht.get(trueName);\r
+\r
+                if (member instanceof NativeJavaMethod &&\r
+                    ((NativeJavaMethod)member).getMethods().length > 1 ) {\r
+                    NativeJavaMethod fun = \r
+                        new NativeJavaMethod((Method)methodOrCtor, name);\r
+                    fun.setPrototype(prototype);\r
+                    ht.put(name, fun);\r
+                    member = fun;\r
+                }\r
+            }\r
+        }\r
+\r
+        return member;\r
+    }\r
+\r
+\r
+    public void put(Scriptable scope, String name, Object javaObject, \r
+                    Object value, boolean isStatic)\r
+    {\r
+        Hashtable ht = isStatic ? staticMembers : members;\r
+        Object member = ht.get(name);\r
+        if (!isStatic && member == null) {\r
+            // Try to get static member from instance (LC3)\r
+            member = staticMembers.get(name);\r
+        }\r
+        if (member == null)\r
+            throw reportMemberNotFound(name);\r
+        if (member instanceof FieldAndMethods) {\r
+            FieldAndMethods fam = (FieldAndMethods) ht.get(name);\r
+            member = fam.getField();\r
+        }\r
+        \r
+        // Is this a bean property "set"?\r
+        if (member instanceof BeanProperty) { \r
+            try {\r
+                Method method = ((BeanProperty) member).setter;\r
+                if (method == null)\r
+                    throw reportMemberNotFound(name);\r
+                Class[] types = method.getParameterTypes();\r
+                Object[] params = { NativeJavaObject.coerceType(types[0], value) };\r
+                method.invoke(javaObject, params);\r
+            } catch (IllegalAccessException accessEx) {\r
+                throw new RuntimeException("unexpected IllegalAccessException " +\r
+                                           "accessing Java field");\r
+            } catch (InvocationTargetException e) {\r
+                throw WrappedException.wrapException(\r
+                    JavaScriptException.wrapException(scope, e));\r
+            }\r
+        }\r
+        else {\r
+            Field field = null;\r
+            try {\r
+                field = (Field) member;\r
+                if (field == null) {\r
+                    throw Context.reportRuntimeError1(\r
+                        "msg.java.internal.private", name);\r
+                }\r
+                field.set(javaObject,\r
+                          NativeJavaObject.coerceType(field.getType(), value));\r
+            } catch (ClassCastException e) {\r
+                throw Context.reportRuntimeError1(\r
+                    "msg.java.method.assign", name);\r
+            } catch (IllegalAccessException accessEx) {\r
+                throw new RuntimeException("unexpected IllegalAccessException "+\r
+                                           "accessing Java field");\r
+            } catch (IllegalArgumentException argEx) {\r
+                throw Context.reportRuntimeError3(\r
+                    "msg.java.internal.field.type", \r
+                    value.getClass().getName(), field,\r
+                    javaObject.getClass().getName());\r
+            }\r
+        }\r
+    }\r
+\r
+    Object[] getIds(boolean isStatic) {\r
+        Hashtable ht = isStatic ? staticMembers : members;\r
+        int len = ht.size();\r
+        Object[] result = new Object[len];\r
+        Enumeration keys = ht.keys();\r
+        for (int i=0; i < len; i++)\r
+            result[i] = keys.nextElement();\r
+        return result;\r
+    }\r
+    \r
+    Class getReflectedClass() {\r
+        return cl;\r
+    }\r
+    \r
+    void reflectField(Scriptable scope, Field field) {\r
+        int mods = field.getModifiers();\r
+        if (!Modifier.isPublic(mods))\r
+            return;\r
+        boolean isStatic = Modifier.isStatic(mods);\r
+        Hashtable ht = isStatic ? staticMembers : members;\r
+        String name = field.getName();\r
+        Object member = ht.get(name);\r
+        if (member != null) {\r
+            if (member instanceof NativeJavaMethod) {\r
+                NativeJavaMethod method = (NativeJavaMethod) member;\r
+                FieldAndMethods fam = new FieldAndMethods(method.getMethods(),\r
+                                                          field,\r
+                                                          null);\r
+                fam.setPrototype(ScriptableObject.getFunctionPrototype(scope));\r
+                getFieldAndMethodsTable(isStatic).put(name, fam);\r
+                ht.put(name, fam);\r
+                return;\r
+            }\r
+            if (member instanceof Field) {\r
+               Field oldField = (Field) member;\r
+               // If this newly reflected field shadows an inherited field, \r
+                // then replace it. Otherwise, since access to the field \r
+                // would be ambiguous from Java, no field should be reflected.\r
+               // For now, the first field found wins, unless another field \r
+                // explicitly shadows it.\r
+               if (oldField.getDeclaringClass().isAssignableFrom(field.getDeclaringClass()))\r
+                       ht.put(name, field);\r
+               return;\r
+            }\r
+            throw new RuntimeException("unknown member type");\r
+        }\r
+        ht.put(name, field);\r
+    }\r
+\r
+    void reflectMethod(Scriptable scope, Method method) {\r
+        int mods = method.getModifiers();\r
+        if (!Modifier.isPublic(mods))\r
+            return;\r
+        boolean isStatic = Modifier.isStatic(mods);\r
+        Hashtable ht = isStatic ? staticMembers : members;\r
+        String name = method.getName();\r
+        NativeJavaMethod fun = (NativeJavaMethod) ht.get(name);\r
+        if (fun == null) {\r
+            fun = new NativeJavaMethod();\r
+            if (scope != null)\r
+                fun.setPrototype(ScriptableObject.getFunctionPrototype(scope));\r
+            ht.put(name, fun);\r
+            fun.add(method);\r
+        } else {\r
+            fun.add(method);\r
+        }\r
+    }\r
+\r
+    void reflect(Scriptable scope, Class cl) {\r
+        // We reflect methods first, because we want overloaded field/method\r
+        // names to be allocated to the NativeJavaMethod before the field\r
+        // gets in the way.\r
+        Method[] methods = cl.getMethods();\r
+        for (int i = 0; i < methods.length; i++)\r
+            reflectMethod(scope, methods[i]);\r
+        \r
+        Field[] fields = cl.getFields();\r
+        for (int i = 0; i < fields.length; i++)\r
+            reflectField(scope, fields[i]);\r
+\r
+        makeBeanProperties(scope, false);\r
+        makeBeanProperties(scope, true);\r
+        \r
+        ctors = cl.getConstructors();\r
+    }\r
+    \r
+    Hashtable getFieldAndMethodsTable(boolean isStatic) {\r
+        Hashtable fmht = isStatic ? staticFieldAndMethods\r
+                                  : fieldAndMethods;\r
+        if (fmht == null) {\r
+            fmht = new Hashtable(11);\r
+            if (isStatic)\r
+                staticFieldAndMethods = fmht;\r
+            else\r
+                fieldAndMethods = fmht;\r
+        }\r
+        \r
+        return fmht;\r
+    }\r
+\r
+    void makeBeanProperties(Scriptable scope, boolean isStatic) {\r
+        Hashtable ht = isStatic ? staticMembers : members;\r
+        Hashtable toAdd = new Hashtable();\r
+        \r
+        // Now, For each member, make "bean" properties.\r
+        for (Enumeration e = ht.keys(); e.hasMoreElements(); ) {\r
+            \r
+            // Is this a getter?\r
+            String name = (String) e.nextElement();\r
+            boolean memberIsGetMethod = name.startsWith("get");\r
+            boolean memberIsIsMethod = name.startsWith("is");\r
+            if (memberIsGetMethod || memberIsIsMethod) {\r
+                // Double check name component.\r
+                String nameComponent = name.substring(memberIsGetMethod ? 3 : 2);\r
+                if (nameComponent.length() == 0) \r
+                    continue;\r
+                \r
+                // Make the bean property name.\r
+                String beanPropertyName = nameComponent;\r
+                if (Character.isUpperCase(nameComponent.charAt(0))) {\r
+                    if (nameComponent.length() == 1) {\r
+                        beanPropertyName = nameComponent.substring(0, 1).toLowerCase();\r
+                    } else if (!Character.isUpperCase(nameComponent.charAt(1))) {\r
+                        beanPropertyName = Character.toLowerCase(nameComponent.charAt(0)) + \r
+                                           nameComponent.substring(1);\r
+                    }\r
+                }\r
+                \r
+                // If we already have a member by this name, don't do this\r
+                // property.\r
+                if (ht.containsKey(beanPropertyName))\r
+                    continue;\r
+                \r
+                // Get the method by this name.\r
+                Object method = ht.get(name);\r
+                if (!(method instanceof NativeJavaMethod))\r
+                    continue;\r
+                NativeJavaMethod getJavaMethod = (NativeJavaMethod) method;\r
+                \r
+                // Grab and inspect the getter method; does it have an empty parameter list,\r
+                // with a return value (eg. a getSomething() or isSomething())?\r
+                Class[] params;\r
+                Method[] getMethods = getJavaMethod.getMethods();\r
+                Class type;\r
+                if (getMethods != null && \r
+                    getMethods.length == 1 && \r
+                    (type = getMethods[0].getReturnType()) != null &&\r
+                    (params = getMethods[0].getParameterTypes()) != null && \r
+                    params.length == 0) \r
+                { \r
+                    \r
+                    // Make sure the method static-ness is preserved for this property.\r
+                    if (isStatic && !Modifier.isStatic(getMethods[0].getModifiers()))\r
+                        continue;\r
+                        \r
+                    // We have a getter.  Now, do we have a setter?\r
+                    Method setMethod = null;\r
+                    String setter = "set" + nameComponent;\r
+                    if (ht.containsKey(setter)) { \r
+\r
+                        // Is this value a method?\r
+                        method = ht.get(setter);\r
+                        if (method instanceof NativeJavaMethod) {\r
+                        \r
+                            //\r
+                            // Note: it may be preferable to allow NativeJavaMethod.findFunction()\r
+                            //       to find the appropriate setter; unfortunately, it requires an\r
+                            //       instance of the target arg to determine that.\r
+                            //\r
+\r
+                            // Make two passes: one to find a method with direct type assignment, \r
+                            // and one to find a widening conversion.\r
+                            NativeJavaMethod setJavaMethod = (NativeJavaMethod) method;\r
+                            Method[] setMethods = setJavaMethod.getMethods();\r
+                            for (int pass = 1; pass <= 2 && setMethod == null; ++pass) {\r
+                                for (int i = 0; i < setMethods.length; ++i) {\r
+                                    if (setMethods[i].getReturnType() == void.class &&\r
+                                        (!isStatic || Modifier.isStatic(setMethods[i].getModifiers())) &&\r
+                                        (params = setMethods[i].getParameterTypes()) != null && \r
+                                        params.length == 1 ) { \r
+                                        \r
+                                        if ((pass == 1 && params[0] == type) ||\r
+                                            (pass == 2 && params[0].isAssignableFrom(type))) { \r
+                                            setMethod = setMethods[i];\r
+                                            break;\r
+                                        }\r
+                                    }\r
+                                }\r
+                            }\r
+                        }\r
+                    }\r
+                            \r
+                    // Make the property.\r
+                    BeanProperty bp = new BeanProperty(getMethods[0], setMethod);\r
+                    toAdd.put(beanPropertyName, bp);\r
+                }\r
+            }\r
+        }           \r
+        \r
+        // Add the new bean properties.\r
+        for (Enumeration e = toAdd.keys(); e.hasMoreElements();) {\r
+            String key = (String) e.nextElement();\r
+            Object value = toAdd.get(key);\r
+            ht.put(key, value);\r
+        }\r
+    }\r
+\r
+    Hashtable getFieldAndMethodsObjects(Scriptable scope, Object javaObject,\r
+                                        boolean isStatic) \r
+    {\r
+        Hashtable ht = isStatic ? staticFieldAndMethods : fieldAndMethods;\r
+        if (ht == null)\r
+            return null;\r
+        int len = ht.size();\r
+        Hashtable result = new Hashtable(Math.max(len,1));\r
+        Enumeration e = ht.elements();\r
+        while (len-- > 0) {\r
+            FieldAndMethods fam = (FieldAndMethods) e.nextElement();\r
+            fam = (FieldAndMethods) fam.clone();\r
+            fam.setJavaObject(javaObject);\r
+            result.put(fam.getName(), fam);\r
+        }\r
+        return result;\r
+    }\r
+\r
+    Constructor[] getConstructors() {\r
+        return ctors;\r
+    }\r
+\r
+    static JavaMembers lookupClass(Scriptable scope, Class dynamicType,\r
+                                   Class staticType)\r
+    {\r
+        Class cl = dynamicType;\r
+        Hashtable ct = classTable;  // use local reference to avoid synchronize\r
+        JavaMembers members = (JavaMembers) ct.get(cl);\r
+        if (members != null)\r
+            return members;\r
+        if (staticType != null && staticType != dynamicType &&\r
+            !Modifier.isPublic(dynamicType.getModifiers()) &&\r
+            Modifier.isPublic(staticType.getModifiers()))\r
+        {\r
+            cl = staticType;\r
+            \r
+            // We can use the static type, and that is OK, but we'll trace\r
+            // back the java class chain here to look for something more suitable.\r
+            for (Class parentType = dynamicType; \r
+                 parentType != null && parentType != ScriptRuntime.ObjectClass;\r
+                 parentType = parentType.getSuperclass())\r
+            {\r
+                if (Modifier.isPublic(parentType.getModifiers())) {\r
+                   cl = parentType;\r
+                   break;\r
+                }\r
+            }\r
+        }\r
+        try {\r
+            members = new JavaMembers(scope, cl);\r
+        } catch (SecurityException e) {\r
+            // Reflection may fail for objects that are in a restricted \r
+            // access package (e.g. sun.*).  If we get a security\r
+            // exception, try again with the static type. Otherwise, \r
+            // rethrow the exception.\r
+            if (cl != staticType)\r
+                members = new JavaMembers(scope, staticType);\r
+            else\r
+                throw e;\r
+        }\r
+        if (Context.isCachingEnabled) \r
+            ct.put(cl, members);\r
+        return members;\r
+    }\r
+\r
+    RuntimeException reportMemberNotFound(String memberName) {\r
+        return Context.reportRuntimeError2(\r
+            "msg.java.member.not.found", cl.getName(), memberName);\r
+    }\r
+\r
+    static Hashtable classTable = new Hashtable();\r
+\r
+    private Class cl;\r
+    private Hashtable members;\r
+    private Hashtable fieldAndMethods;\r
+    private Hashtable staticMembers;\r
+    private Hashtable staticFieldAndMethods;\r
+    private Constructor[] ctors;\r
+}\r
+\r
+class BeanProperty {\r
+    BeanProperty(Method getter, Method setter) {\r
+        this.getter = getter;\r
+        this.setter = setter;\r
+    }\r
+    Method getter;\r
+    Method setter;\r
+}\r
+\r
+class FieldAndMethods extends NativeJavaMethod {\r
+\r
+    FieldAndMethods(Method[] methods, Field field, String name) {\r
+        super(methods);\r
+        this.field = field;\r
+        this.name = name;\r
+    }\r
+\r
+    void setJavaObject(Object javaObject) {\r
+        this.javaObject = javaObject;\r
+    }\r
+\r
+    String getName() {\r
+        if (field == null)\r
+            return name;\r
+        return field.getName();\r
+    }\r
+\r
+    Field getField() {\r
+        return field;\r
+    }\r
+    \r
+    public Object getDefaultValue(Class hint) {\r
+        if (hint == ScriptRuntime.FunctionClass)\r
+            return this;\r
+        Object rval;\r
+        Class type;\r
+        try {\r
+            rval = field.get(javaObject);\r
+            type = field.getType();\r
+        } catch (IllegalAccessException accEx) {\r
+            throw Context.reportRuntimeError1(\r
+                "msg.java.internal.private", getName());\r
+        }\r
+        rval = NativeJavaObject.wrap(this, rval, type);\r
+        if (rval instanceof Scriptable) {\r
+            rval = ((Scriptable) rval).getDefaultValue(hint);\r
+        }\r
+        return rval;\r
+    }\r
+\r
+    public Object clone() {\r
+        FieldAndMethods result = new FieldAndMethods(methods, field, name);\r
+        result.javaObject = javaObject;\r
+        return result;\r
+    }\r
+    \r
+    private Field field;\r
+    private Object javaObject;\r
+    private String name;\r
+}\r
diff --git a/src/org/mozilla/javascript/JavaScriptException.java b/src/org/mozilla/javascript/JavaScriptException.java
new file mode 100644 (file)
index 0000000..d7ca4f5
--- /dev/null
@@ -0,0 +1,114 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.lang.reflect.InvocationTargetException;\r
+\r
+/**\r
+ * Java reflection of JavaScript exceptions.  (Possibly wrapping a Java exception.)\r
+ *\r
+ * @author Mike McCabe\r
+ */\r
+public class JavaScriptException extends Exception {\r
+\r
+    public int line;\r
+    public String sourceFile;\r
+\r
+    /**\r
+     * Create a JavaScript exception wrapping the given JavaScript value.\r
+     *\r
+     * Instances of this class are thrown by the JavaScript 'throw' keyword.\r
+     *\r
+     * @param value the JavaScript value thrown.\r
+     */\r
+    public JavaScriptException(Object value) {\r
+        super(ScriptRuntime.toString(value));\r
+        line = Context.enter().interpreterLine;\r
+        sourceFile = Context.enter().interpreterSourceFile;\r
+        this.value = value;\r
+    }\r
+\r
+    /**\r
+     * Get the exception message.\r
+     *\r
+     * <p>Will just convert the wrapped exception to a string.\r
+     */\r
+    public String getMessage() {\r
+        return ScriptRuntime.toString(value);\r
+    }\r
+\r
+    static JavaScriptException wrapException(Scriptable scope,\r
+                                             Throwable exn)\r
+    {\r
+        if (exn instanceof InvocationTargetException)\r
+            exn = ((InvocationTargetException)exn).getTargetException();\r
+        if (exn instanceof JavaScriptException)\r
+            return (JavaScriptException)exn;\r
+        Object wrapper = NativeJavaObject.wrap(scope, exn, Throwable.class);\r
+        return new JavaScriptException(wrapper);\r
+    }\r
+\r
+    /**\r
+     * Get the exception value originally thrown.  This may be a\r
+     * JavaScript value (null, undefined, Boolean, Number, String,\r
+     * Scriptable or Function) or a Java exception value thrown from a\r
+     * host object or from Java called through LiveConnect.\r
+     *\r
+     * @return the value wrapped by this exception\r
+     */\r
+    public Object getValue() {\r
+        if (value != null && value instanceof Wrapper)\r
+            // this will also catch NativeStrings...\r
+            return ((Wrapper)value).unwrap();\r
+        else\r
+            return value;\r
+    }\r
+\r
+    /**\r
+     * The JavaScript exception value.  This value is not\r
+     * intended for general use; if the JavaScriptException wraps a\r
+     * Java exception, getScriptableValue may return a Scriptable\r
+     * wrapping the original Java exception object.\r
+     *\r
+     * We would prefer to go through a getter to encapsulate the value,\r
+     * however that causes the bizarre error "nanosecond timeout value \r
+     * out of range" on the MS JVM. \r
+     * @serial \r
+     */\r
+    Object value;\r
+}\r
diff --git a/src/org/mozilla/javascript/Label.java b/src/org/mozilla/javascript/Label.java
new file mode 100644 (file)
index 0000000..8e42d46
--- /dev/null
@@ -0,0 +1,106 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Roger Lawrence\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+public class Label {\r
+    \r
+    private static final int FIXUPTABLE_SIZE = 8;\r
+\r
+    private static final boolean DEBUG = true;\r
+\r
+    public Label()\r
+    {\r
+        itsPC = -1;\r
+    }\r
+\r
+    public short getPC()\r
+    {\r
+        return itsPC;\r
+    }\r
+    \r
+    public void fixGotos(byte theCodeBuffer[])\r
+    {\r
+        if (DEBUG) {\r
+            if ((itsPC == -1) && (itsFixupTable != null))\r
+                throw new RuntimeException("Unlocated label");\r
+        }\r
+        if (itsFixupTable != null) {\r
+            for (int i = 0; i < itsFixupTableTop; i++) {\r
+                int fixupSite = itsFixupTable[i];\r
+                // -1 to get delta from instruction start\r
+                short offset = (short)(itsPC - (fixupSite - 1));\r
+                theCodeBuffer[fixupSite++] = (byte)(offset >> 8);\r
+                theCodeBuffer[fixupSite] = (byte)offset;\r
+            }\r
+        }\r
+        itsFixupTable = null;\r
+    }\r
+\r
+    public void setPC(short thePC)\r
+    {\r
+        if (DEBUG) {\r
+            if ((itsPC != -1) && (itsPC != thePC)) {\r
+                throw new RuntimeException("Duplicate label");\r
+            }\r
+        }\r
+\r
+        itsPC = thePC;\r
+    }\r
+\r
+    public void addFixup(int fixupSite)\r
+    {\r
+        if (itsFixupTable == null) {\r
+            itsFixupTableTop = 1;\r
+            itsFixupTable = new int[FIXUPTABLE_SIZE];\r
+            itsFixupTable[0] = fixupSite;            \r
+        }\r
+        else {\r
+            if (itsFixupTableTop == itsFixupTable.length) {\r
+                int oldLength = itsFixupTable.length;\r
+                int newTable[] = new int[oldLength + FIXUPTABLE_SIZE];\r
+                System.arraycopy(itsFixupTable, 0, newTable, 0, oldLength);\r
+                itsFixupTable = newTable;\r
+            }\r
+            itsFixupTable[itsFixupTableTop++] = fixupSite;            \r
+        }\r
+    }\r
+\r
+    private short itsPC = -1;\r
+    private int itsFixupTable[];\r
+    private int itsFixupTableTop;\r
+\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/LabelTable.java b/src/org/mozilla/javascript/LabelTable.java
new file mode 100644 (file)
index 0000000..43dd9ae
--- /dev/null
@@ -0,0 +1,80 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Roger Lawrence\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+public class LabelTable {\r
+\r
+    private static final boolean DEBUGLABELS = false;\r
+    \r
+    private static final int LabelTableSize = 32;\r
+    protected Label itsLabelTable[];\r
+    protected int itsLabelTableTop;\r
+\r
+    public int acquireLabel()\r
+    {\r
+        if (itsLabelTable == null) {\r
+            itsLabelTable = new Label[LabelTableSize];\r
+            itsLabelTable[0] = new Label();\r
+            itsLabelTableTop = 1;\r
+            return 0x80000000;\r
+        }\r
+        else {\r
+            if (itsLabelTableTop == itsLabelTable.length) {\r
+                Label oldTable[] = itsLabelTable;\r
+                itsLabelTable = new Label[itsLabelTableTop * 2];\r
+                System.arraycopy(oldTable, 0, itsLabelTable, 0, itsLabelTableTop);\r
+            }\r
+            itsLabelTable[itsLabelTableTop] = new Label();\r
+            int result = itsLabelTableTop++;\r
+            return result | 0x80000000;\r
+        }\r
+    }\r
+\r
+    public int markLabel(int theLabel, int pc)\r
+    {\r
+        if (DEBUGLABELS) {\r
+            if ((theLabel & 0x80000000) != 0x80000000)\r
+                throw new RuntimeException("Bad label, no biscuit");\r
+        }\r
+        theLabel &= 0x7FFFFFFF;\r
+        if (DEBUGLABELS) {\r
+            System.out.println("Marking label " + theLabel + " at " + pc);\r
+        }\r
+        itsLabelTable[theLabel].setPC((short)pc);\r
+        return theLabel | 0x80000000;\r
+    }\r
+\r
+}\r
diff --git a/src/org/mozilla/javascript/LazilyLoadedCtor.java b/src/org/mozilla/javascript/LazilyLoadedCtor.java
new file mode 100644 (file)
index 0000000..37fd30c
--- /dev/null
@@ -0,0 +1,133 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.lang.reflect.*;\r
+\r
+/**\r
+ * Avoid loading classes unless they are used.\r
+ *\r
+ * <p> This improves startup time and average memory usage.\r
+ */\r
+public final class LazilyLoadedCtor {\r
+\r
+    public LazilyLoadedCtor(ScriptableObject scope,\r
+                     String ctorName, String className, boolean sealed)\r
+    {\r
+\r
+        this.className = className;\r
+        this.ctorName = ctorName;\r
+        this.sealed = sealed;\r
+\r
+        if (getter == null) {\r
+            Method[] all = FunctionObject.getMethodList(getClass());\r
+            getter = FunctionObject.findMethods(all, "getProperty")[0];\r
+            setter = FunctionObject.findMethods(all, "setProperty")[0];\r
+        }\r
+\r
+        try {\r
+            scope.defineProperty(ctorName, this, getter, setter,\r
+                                 ScriptableObject.DONTENUM);\r
+        }\r
+        catch (PropertyException e) {\r
+            throw WrappedException.wrapException(e);\r
+        }\r
+    }\r
+\r
+    public Object getProperty(ScriptableObject obj) {\r
+        synchronized (obj) {\r
+            if (!isReplaced) {\r
+                boolean removeOnError = false;\r
+\r
+                // Treat security exceptions as absence of object.\r
+                // They can be due to the following reasons:\r
+                //  java.lang.RuntimePermission createClassLoader\r
+                //  java.util.PropertyPermission\r
+                //        org.mozilla.javascript.JavaAdapter read\r
+\r
+                Class cl = null;\r
+                try { cl = Class.forName(className); }\r
+                catch (ClassNotFoundException ex) { removeOnError = true; }\r
+                catch (SecurityException ex) { removeOnError = true; }\r
+\r
+                if (cl != null) {\r
+                    try {\r
+                        ScriptableObject.defineClass(obj, cl, sealed);\r
+                        isReplaced = true;\r
+                    }\r
+                    catch (InstantiationException e) {\r
+                        throw WrappedException.wrapException(e);\r
+                    }\r
+                    catch (IllegalAccessException e) {\r
+                        throw WrappedException.wrapException(e);\r
+                    }\r
+                    catch (InvocationTargetException e) {\r
+                        throw WrappedException.wrapException(e);\r
+                    }\r
+                    catch (ClassDefinitionException e) {\r
+                        throw WrappedException.wrapException(e);\r
+                    }\r
+                    catch (PropertyException e) {\r
+                        throw WrappedException.wrapException(e);\r
+                    }\r
+                    catch (SecurityException ex) {\r
+                        removeOnError = true;\r
+                    }\r
+                }\r
+                if (removeOnError) {\r
+                    obj.delete(ctorName);\r
+                    return Scriptable.NOT_FOUND;\r
+                }\r
+            }\r
+        }\r
+        // Get just added object\r
+        return obj.get(ctorName, obj);\r
+    }\r
+\r
+    public Object setProperty(ScriptableObject obj, Object val) {\r
+        synchronized (obj) {\r
+            isReplaced = true;\r
+            return val;\r
+        }\r
+    }\r
+\r
+    private static Method getter, setter;\r
+\r
+    private String ctorName;\r
+    private String className;\r
+    private boolean sealed;\r
+    private boolean isReplaced;\r
+}\r
diff --git a/src/org/mozilla/javascript/LineBuffer.java b/src/org/mozilla/javascript/LineBuffer.java
new file mode 100644 (file)
index 0000000..d3f0ec4
--- /dev/null
@@ -0,0 +1,412 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.io.Reader;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * An input buffer that combines fast character-based access with\r
+ * (slower) support for retrieving the text of the current line.  It\r
+ * also supports building strings directly out of the internal buffer\r
+ * to support fast scanning with minimal object creation.\r
+ *\r
+ * Note that it is customized in several ways to support the\r
+ * TokenStream class, and should not be considered general.\r
+ *\r
+ * Credits to Kipp Hickman and John Bandhauer.\r
+ *\r
+ * @author Mike McCabe\r
+ */\r
+final class LineBuffer {\r
+    /*\r
+     * for smooth operation of getLine(), this should be greater than\r
+     * the length of any expected line.  Currently, 256 is 3% slower\r
+     * than 4096 for large compiles, but seems safer given evaluateString.\r
+     * Strings for the scanner are are built with StringBuffers\r
+     * instead of directly out of the buffer whenever a string crosses\r
+     * a buffer boundary, so small buffer sizes will mean that more\r
+     * objects are created.\r
+     */\r
+    static final int BUFLEN = 256;\r
+\r
+    LineBuffer(Reader in, int lineno) {\r
+        this.in = in;\r
+        this.lineno = lineno;\r
+    }\r
+\r
+    int read() throws IOException {\r
+               for(;;) {\r
+                       if (end == offset && !fill())\r
+                           return -1;\r
+\r
+                       // Do only a bitmask + branch per character, at the cost of\r
+                       // three branches per low-bits-only (or 2028/9) character.\r
+            if ((buffer[offset] & '\udfd0') == 0) {\r
+                           if (buffer[offset] == '\r') {\r
+                               // if the next character is a newline, skip past it.\r
+                               if ((offset + 1) < end) {\r
+                                   if (buffer[offset + 1] == '\n')\r
+                                       offset++;\r
+                               } else {\r
+                                   // set a flag for fill(), in case the first char of the\r
+                                   // next fill is a newline.\r
+                                   lastWasCR = true;\r
+                               }\r
+                           }\r
+                else \r
+                    if ((buffer[offset] != '\n') \r
+                        && (buffer[offset] != '\u2028')\r
+                        && (buffer[offset] != '\u2029'))\r
+                    { \r
+                        if (Character.getType(buffer[offset])\r
+                                                    == Character.FORMAT) {\r
+                                           hadCFSinceStringStart = true;\r
+                                           offset++;\r
+                            continue;\r
+                        }\r
+                        return (int) buffer[offset++];\r
+                               }\r
+                           offset++;\r
+                           prevStart = lineStart;\r
+                           lineStart = offset;\r
+                           lineno++;\r
+                           return '\n';\r
+                       }\r
+                       if ((buffer[offset] >= 128) \r
+                                 && (Character.getType(buffer[offset]) == Character.FORMAT)) {\r
+                               hadCFSinceStringStart = true;\r
+                               offset++;\r
+                       }\r
+                       else\r
+                               break;\r
+               }\r
+               \r
+        return (int) buffer[offset++];\r
+    }\r
+\r
+    void unread() {\r
+        if (offset == 0)\r
+            // We can get here when we're asked to unread() an\r
+            // implicit EOF_CHAR.\r
+            \r
+            // This would also be wrong behavior in the general case,\r
+            // because a peek() could map a buffer.length offset to 0\r
+            // in the process of a fill(), and leave it there.  But\r
+            // the scanner never calls peek() or a failed match()\r
+            // followed by unread()... this would violate 1-character\r
+            // lookahead.  So we're OK.\r
+            return;\r
+        offset--;\r
+        if ((buffer[offset] & '\ufff0') == 0\r
+            && (buffer[offset] == '\r' || buffer[offset] == '\n')) {\r
+            // back off from the line start we presumably just registered...\r
+            lineStart = prevStart;\r
+            lineno--;\r
+        }\r
+    }\r
+\r
+    int peek() throws IOException {\r
+        if (end == offset && !fill())\r
+            return -1;\r
+\r
+        if (buffer[offset] == '\r')\r
+            return '\n';\r
+\r
+        return buffer[offset];\r
+    }\r
+\r
+    boolean match(char c) throws IOException {\r
+        if (end == offset && !fill())\r
+            return false;\r
+\r
+        // This'd be a place where we'd need to map '\r' to '\n' and\r
+        // do other updates, but TokenStream never looks ahead for\r
+        // '\n', so we don't bother.\r
+        if (buffer[offset] == c) {\r
+            offset++;\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    // Reconstruct a source line from the buffers.  This can be slow...\r
+    String getLine() {\r
+        StringBuffer result = new StringBuffer();\r
+\r
+        int start = lineStart;\r
+        if (start >= offset) {\r
+            // the line begins somewhere in the other buffer; get that first.\r
+            if (otherStart < otherEnd)\r
+                // if a line ending was seen in the other buffer... otherwise\r
+                // just ignore this strange case.\r
+                result.append(otherBuffer, otherStart,\r
+                              otherEnd - otherStart);\r
+            start = 0;\r
+        }\r
+\r
+        // get the part of the line in the current buffer.\r
+        result.append(buffer, start, offset - start);\r
+\r
+        // Get the remainder of the line.\r
+        int i = offset;\r
+        while(true) {\r
+            if (i == buffer.length) {\r
+                // we're out of buffer, let's just expand it.  We do\r
+                // this instead of reading into a StringBuffer to\r
+                // preserve the stream for later reads.\r
+                char[] newBuffer = new char[buffer.length * 2];\r
+                System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);\r
+                buffer = newBuffer;\r
+                int charsRead = 0;\r
+                try {\r
+                    charsRead = in.read(buffer, end, buffer.length - end);\r
+                } catch (IOException ioe) {\r
+                    // ignore it, we're already displaying an error...\r
+                }\r
+                if (charsRead < 0)\r
+                    break;\r
+                end += charsRead;\r
+            }\r
+            if (buffer[i] == '\r' || buffer[i] == '\n')\r
+                break;\r
+            i++;\r
+        }\r
+\r
+        result.append(buffer, offset, i - offset);\r
+        return result.toString();\r
+    }\r
+\r
+    // Get the offset of the current character, relative to\r
+    // the line that getLine() returns.\r
+    int getOffset() {\r
+        if (lineStart >= offset)\r
+            // The line begins somewhere in the other buffer.\r
+            return offset + (otherEnd - otherStart);\r
+        else\r
+            return offset - lineStart;\r
+    }\r
+\r
+    // Set a mark to indicate that the reader should begin\r
+    // accumulating characters for getString().  The string begins\r
+    // with the last character read.\r
+    void startString() {\r
+        if (offset == 0) {\r
+            // We can get here if startString is called after a peek()\r
+            // or failed match() with offset past the end of the\r
+            // buffer.\r
+\r
+            // We're at the beginning of the buffer, and the previous character\r
+            // (which we want to include) is at the end of the last one, so\r
+            // we just go to StringBuffer mode.\r
+            stringSoFar = new StringBuffer();\r
+                       \r
+            stringSoFar.append(otherBuffer, otherEnd - 1, 1);\r
+\r
+            stringStart = -1; // Set sentinel value.\r
+                       hadCFSinceStringStart = ((otherBuffer[otherEnd - 1] >= 128) \r
+                                       && Character.getType(otherBuffer[otherEnd - 1])\r
+                                                                                                       == Character.FORMAT);\r
+        } else {\r
+            // Support restarting strings\r
+            stringSoFar = null;\r
+            stringStart = offset - 1;\r
+                       hadCFSinceStringStart = ((buffer[stringStart] >= 128) \r
+                                       && Character.getType(buffer[stringStart]) == Character.FORMAT);\r
+        }\r
+               \r
+    }\r
+\r
+    // Get a string consisting of the characters seen since the last\r
+    // startString.\r
+    String getString() {\r
+        String result;\r
+\r
+        /*\r
+         * There's one strange case here:  If the character offset currently\r
+         * points to (which we never want to include in the string) is\r
+         * a newline, then if the previous character is a carriage return,\r
+         * we probably want to exclude that as well.  If the offset is 0,\r
+         * then we hope that fill() handled excluding it from stringSoFar.\r
+         */\r
+        int loseCR = (offset > 0 &&\r
+                      buffer[offset] == '\n' && buffer[offset - 1] == '\r') ?\r
+            1 : 0;\r
+\r
+        if (stringStart != -1) {\r
+            // String mark is valid, and in this buffer.\r
+\r
+            result = new String(buffer, stringStart, \r
+                                offset - stringStart - loseCR);\r
+        } else {\r
+            if (stringSoFar == null) \r
+                stringSoFar = new StringBuffer();\r
+            // Exclude cr as well as nl of newline.  If offset is 0, then\r
+            // hopefully fill() did the right thing.\r
+            result = (stringSoFar.append(buffer, 0, offset - loseCR)).toString();\r
+        }\r
+        \r
+        stringStart = -1;\r
+        stringSoFar = null;\r
+               \r
+               if (hadCFSinceStringStart) {\r
+                       char c[] = result.toCharArray();\r
+                       StringBuffer x = null;\r
+                       for (int i = 0; i < c.length; i++) {\r
+                               if (Character.getType(c[i]) == Character.FORMAT) {\r
+                                       if (x == null) {\r
+                                               x = new StringBuffer();\r
+                                               x.append(c, 0, i);\r
+                                       }\r
+                               }\r
+                               else\r
+                                       if (x != null) x.append(c[i]);\r
+                       }\r
+                       if (x != null) result = x.toString();   \r
+               }\r
+               \r
+        return result;\r
+    }            \r
+\r
+    boolean fill() throws IOException {\r
+        // not sure I care...\r
+        if (end - offset != 0) \r
+            throw new IOException("fill of non-empty buffer");\r
+\r
+        // If there's a string currently being accumulated, save\r
+        // off the progress.\r
+\r
+        /*\r
+         * Exclude an end-of-buffer carriage return.  NOTE this is not\r
+         * fully correct in the general case, because we really only\r
+         * want to exclude the carriage return if it's followed by a\r
+         * linefeed at the beginning of the next buffer.  But we fudge\r
+         * because the scanner doesn't do this.\r
+         */\r
+        int loseCR = (offset > 0 && lastWasCR) ? 1 : 0;\r
+\r
+        if (stringStart != -1) {\r
+            // The mark is in the current buffer, save off from the mark to the\r
+            // end.\r
+            stringSoFar = new StringBuffer();\r
+\r
+            stringSoFar.append(buffer, stringStart, end - stringStart - loseCR);\r
+            stringStart = -1;\r
+        } else if (stringSoFar != null) {\r
+            // the string began prior to the current buffer, so save the\r
+            // whole current buffer.\r
+            stringSoFar.append(buffer, 0, end - loseCR);\r
+        }\r
+\r
+        // swap buffers\r
+        char[] tempBuffer = buffer;\r
+        buffer = otherBuffer;\r
+        otherBuffer = tempBuffer;\r
+\r
+        // allocate the buffers lazily, in case we're handed a short string.\r
+        if (buffer == null) {\r
+            buffer = new char[BUFLEN];\r
+        }\r
+\r
+        // buffers have switched, so move the newline marker.\r
+        otherStart = lineStart;\r
+        otherEnd = end;\r
+\r
+        // set lineStart to a sentinel value, unless this is the first\r
+        // time around.\r
+        prevStart = lineStart = (otherBuffer == null) ? 0 : buffer.length + 1;\r
+        \r
+        offset = 0;\r
+        end = in.read(buffer, 0, buffer.length);\r
+        if (end < 0) {\r
+            end = 0;\r
+\r
+            // can't null buffers here, because a string might be retrieved\r
+            // out of the other buffer, and a 0-length string might be\r
+            // retrieved out of this one.\r
+\r
+            hitEOF = true;\r
+            return false;\r
+        }\r
+\r
+        // If the last character of the previous fill was a carriage return,\r
+        // then ignore a newline.\r
+\r
+        // There's another bizzare special case here.  If lastWasCR is\r
+        // true, and we see a newline, and the buffer length is\r
+        // 1... then we probably just read the last character of the\r
+        // file, and returning after advancing offset is not the right\r
+        // thing to do.  Instead, we try to ignore the newline (and\r
+        // likely get to EOF for real) by doing yet another fill().\r
+        if (lastWasCR) {\r
+            if (buffer[0] == '\n') {\r
+              offset++;\r
+              if (end == 1)\r
+                  return fill();\r
+            }\r
+            lineStart = offset;\r
+            lastWasCR = false;\r
+        }\r
+        return true;\r
+    }\r
+\r
+    int getLineno() { return lineno; }\r
+    boolean eof() { return hitEOF; }\r
+    \r
+    private Reader in;\r
+    private char[] otherBuffer = null;\r
+    private char[] buffer = null;\r
+\r
+    // Yes, there are too too many of these.\r
+    private int offset = 0;\r
+    private int end = 0;\r
+    private int otherEnd;\r
+    private int lineno;\r
+\r
+    private int lineStart = 0;\r
+    private int otherStart = 0;\r
+    private int prevStart = 0;\r
+    \r
+    private boolean lastWasCR = false;\r
+    private boolean hitEOF = false;\r
+\r
+    private int stringStart = -1;\r
+    private StringBuffer stringSoFar = null;\r
+       private boolean hadCFSinceStringStart = false;\r
+\r
+}\r
+\r
+\r
diff --git a/src/org/mozilla/javascript/ListenerArray.java b/src/org/mozilla/javascript/ListenerArray.java
new file mode 100644 (file)
index 0000000..3687b10
--- /dev/null
@@ -0,0 +1,131 @@
+/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Igor Bukanov\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+/* Helper class to add/remove listeners from Object array */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * Utility class to manage listeners array.\r
+ * A possible usage would be:\r
+<pre>\r
+    private Object[] listeners;\r
+    ...\r
+    void addListener(ListenerType listener) {\r
+        synchronized (this) {\r
+            listeners = ListenerArray.add(listeners, listener);\r
+        }\r
+    }\r
+\r
+    void removeListener(ListenerType listener) {\r
+        synchronized (this) {\r
+            listeners = ListenerArray.remove(listeners, listener);\r
+        }\r
+    }\r
+</pre>\r
+  * Here is a thread safe while synchronization free example of event firing\r
+<pre>\r
+    void fireEvent(EventType event) {\r
+        Object[] array = listeners;\r
+        if (array != null) {\r
+            for (int i = array.length; i-- != 0;) {\r
+                ((ListenerType)array[i]).onEvent(event);\r
+            }\r
+        }\r
+\r
+    }\r
+</pre>\r
+\r
+ * or if listeners of different types can present in listeners array:\r
+<pre>\r
+    void fireEvent(EventType event) {\r
+        Object[] array = listeners;\r
+        if (array != null) {\r
+            for (int i = array.length; i-- != 0;) {\r
+                Object obj = array[i];\r
+                if (obj instanceof ListenerType) {\r
+                    ((ListenerType)obj).onEvent(event);\r
+                }\r
+            }\r
+        }\r
+\r
+    }\r
+</pre>\r
+ */\r
+public class ListenerArray {\r
+\r
+    /** Return newly allocated array that contains listener and all elements\r
+     ** from data array.\r
+     ** Note: listener is added to resulting array even if it is already\r
+     ** present in data */\r
+    public static Object[] add(Object[] data, Object listener) {\r
+        if (data == null) {\r
+            data = new Object[1];\r
+        }\r
+        else {\r
+            int N = data.length;\r
+            Object[] tmp = new Object[N + 1];\r
+            System.arraycopy(data, 0, tmp, 1, N);\r
+            data = tmp;\r
+        }\r
+        data[0] = listener;\r
+        return data;\r
+    }\r
+\r
+    /** Return a copy of data array with the first occurrence of listener\r
+     ** removed.\r
+     ** If listener is not present in data, simply return data.\r
+     ** Note: return <code>null</code> if listener is the single element\r
+     ** of data. */\r
+    public static Object[] remove(Object[] data, Object listener) {\r
+        if (data != null) {\r
+            int N = data.length;\r
+            for (int i = 0; i != N; ++i) {\r
+                if (data[i] == listener) {\r
+                    if (N == 1) { data = null; }\r
+                    else {\r
+                        Object[] tmp = new Object[N - 1];\r
+                        System.arraycopy(data, 0, tmp, 0, i);\r
+                        System.arraycopy(data, i + 1, tmp, i, N - 1 - i);\r
+                        data = tmp;\r
+                    }\r
+                    break;\r
+                }\r
+            }\r
+        }\r
+        return data;\r
+    }\r
+\r
+}\r
diff --git a/src/org/mozilla/javascript/LocalVariable.java b/src/org/mozilla/javascript/LocalVariable.java
new file mode 100644 (file)
index 0000000..bb673c1
--- /dev/null
@@ -0,0 +1,79 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Roger Lawrence\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+public class LocalVariable {\r
+    \r
+    public LocalVariable(String name, boolean isParameter) {\r
+        itsName = name;\r
+        itsIsParameter = isParameter; \r
+    }\r
+    \r
+    public void setIndex(int index){ itsIndex = index; }\r
+    public int getIndex()          { return itsIndex; }\r
+        \r
+    public void setIsParameter()   { itsIsParameter = true; }\r
+    public boolean isParameter()   { return itsIsParameter; }\r
+            \r
+    public String getName()        { return itsName; }\r
+        \r
+    /**\r
+     * Return the starting PC where this variable is live, or -1\r
+     * if it is not a Java register.\r
+     */\r
+    public int getStartPC() {\r
+        return -1;\r
+    }\r
+\r
+    /**\r
+     * Return the Java register number or -1 if it is not a Java register.\r
+     */\r
+    public short getJRegister() {\r
+        return -1;\r
+    }\r
+\r
+    /**\r
+     * Return true if the local variable is a Java register with double type.\r
+     */\r
+    public boolean isNumber() {\r
+        return false;\r
+    }\r
+\r
+    private String itsName;\r
+    private int itsIndex = -1;\r
+    \r
+    private boolean itsIsParameter;\r
+}\r
diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java
new file mode 100644 (file)
index 0000000..37d81f8
--- /dev/null
@@ -0,0 +1,1147 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Mike McCabe\r
+ * Igor Bukanov\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+import java.util.Hashtable;\r
+\r
+/**\r
+ * This class implements the Array native object.\r
+ * @author Norris Boyd\r
+ * @author Mike McCabe\r
+ */\r
+public class NativeArray extends IdScriptable {\r
+\r
+    /*\r
+     * Optimization possibilities and open issues:\r
+     * - Long vs. double schizophrenia.  I suspect it might be better\r
+     * to use double throughout.\r
+\r
+     * - Most array operations go through getElem or setElem (defined\r
+     * in this file) to handle the full 2^32 range; it might be faster\r
+     * to have versions of most of the loops in this file for the\r
+     * (infinitely more common) case of indices < 2^31.\r
+\r
+     * - Functions that need a new Array call "new Array" in the\r
+     * current scope rather than using a hardwired constructor;\r
+     * "Array" could be redefined.  It turns out that js calls the\r
+     * equivalent of "new Array" in the current scope, except that it\r
+     * always gets at least an object back, even when Array == null.\r
+     */\r
+\r
+    public static void init(Context cx, Scriptable scope, boolean sealed) {\r
+        NativeArray obj = new NativeArray();\r
+        obj.prototypeFlag = true;\r
+        obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);\r
+    }\r
+\r
+    /**\r
+     * Zero-parameter constructor: just used to create Array.prototype\r
+     */\r
+    public NativeArray() {\r
+        dense = null;\r
+        this.length = 0;\r
+    }\r
+\r
+    public NativeArray(long length) {\r
+        int intLength = (int) length;\r
+        if (intLength == length && intLength > 0) {\r
+            if (intLength > maximumDenseLength)\r
+                intLength = maximumDenseLength;\r
+            dense = new Object[intLength];\r
+            for (int i=0; i < intLength; i++)\r
+                dense[i] = NOT_FOUND;\r
+        }\r
+        this.length = length;\r
+    }\r
+\r
+    public NativeArray(Object[] array) {\r
+        dense = array;\r
+        this.length = array.length;\r
+    }\r
+\r
+    public String getClassName() {\r
+        return "Array";\r
+    }\r
+\r
+    protected int getIdDefaultAttributes(int id) {\r
+        if (id == Id_length) {\r
+            return DONTENUM | PERMANENT;\r
+        }\r
+        return super.getIdDefaultAttributes(id);\r
+    }\r
+\r
+    protected Object getIdValue(int id) {\r
+        if (id == Id_length) {\r
+            return wrap_double(length);\r
+        }\r
+        return super.getIdValue(id);\r
+    }\r
+    \r
+    protected void setIdValue(int id, Object value) {\r
+        if (id == Id_length) {\r
+            jsSet_length(value); return;\r
+        }\r
+        super.setIdValue(id, value);\r
+    }\r
+    \r
+    public int methodArity(int methodId) {\r
+        if (prototypeFlag) {\r
+            switch (methodId) {\r
+                case Id_constructor:     return 1;\r
+                case Id_toString:        return 0;\r
+                case Id_toLocaleString:  return 1;\r
+                case Id_join:            return 1;\r
+                case Id_reverse:         return 0;\r
+                case Id_sort:            return 1;\r
+                case Id_push:            return 1;\r
+                case Id_pop:             return 1;\r
+                case Id_shift:           return 1;\r
+                case Id_unshift:         return 1;\r
+                case Id_splice:          return 1;\r
+                case Id_concat:          return 1;\r
+                case Id_slice:           return 1;\r
+            }\r
+        }\r
+        return super.methodArity(methodId);\r
+    }\r
+\r
+    public Object execMethod\r
+        (int methodId, IdFunction f,\r
+         Context cx, Scriptable scope, Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (prototypeFlag) {\r
+            switch (methodId) {\r
+                case Id_constructor:\r
+                    return jsConstructor(cx, scope, args, f, thisObj == null);\r
+\r
+                case Id_toString:\r
+                    return jsFunction_toString(cx, thisObj, args);\r
+\r
+                case Id_toLocaleString:\r
+                    return jsFunction_toLocaleString(cx, thisObj, args);\r
+\r
+                case Id_join:\r
+                    return jsFunction_join(cx, thisObj, args);\r
+\r
+                case Id_reverse:\r
+                    return jsFunction_reverse(cx, thisObj, args);\r
+\r
+                case Id_sort:\r
+                    return jsFunction_sort(cx, scope, thisObj, args);\r
+\r
+                case Id_push:\r
+                    return jsFunction_push(cx, thisObj, args);\r
+\r
+                case Id_pop:\r
+                    return jsFunction_pop(cx, thisObj, args);\r
+\r
+                case Id_shift:\r
+                    return jsFunction_shift(cx, thisObj, args);\r
+\r
+                case Id_unshift:\r
+                    return jsFunction_unshift(cx, thisObj, args);\r
+\r
+                case Id_splice:\r
+                    return jsFunction_splice(cx, scope, thisObj, args);\r
+\r
+                case Id_concat:\r
+                    return jsFunction_concat(cx, scope, thisObj, args);\r
+\r
+                case Id_slice:\r
+                    return jsFunction_slice(cx, thisObj, args);\r
+            }\r
+        }\r
+        return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
+    }\r
+\r
+    public Object get(int index, Scriptable start) {\r
+        if (dense != null && 0 <= index && index < dense.length)\r
+            return dense[index];\r
+        return super.get(index, start);\r
+    }\r
+\r
+    public boolean has(int index, Scriptable start) {\r
+        if (dense != null && 0 <= index && index < dense.length)\r
+            return dense[index] != NOT_FOUND;\r
+        return super.has(index, start);\r
+    }\r
+\r
+    public void put(String id, Scriptable start, Object value) {\r
+        if (start == this) {\r
+            // only set the array length if given an array index (ECMA 15.4.0)\r
+\r
+            // try to get an array index from id\r
+            double d = ScriptRuntime.toNumber(id);\r
+\r
+            if (ScriptRuntime.toUint32(d) == d &&\r
+                ScriptRuntime.numberToString(d, 10).equals(id) &&\r
+                this.length <= d && d != 4294967295.0)\r
+            {\r
+                this.length = (long)d + 1;\r
+            }\r
+        }\r
+        super.put(id, start, value);\r
+    }\r
+\r
+    public void put(int index, Scriptable start, Object value) {\r
+        if (start == this) {\r
+            // only set the array length if given an array index (ECMA 15.4.0)\r
+            if (this.length <= index) {\r
+                // avoid overflowing index!\r
+                this.length = (long)index + 1;\r
+            }\r
+\r
+            if (dense != null && 0 <= index && index < dense.length) {\r
+                dense[index] = value;\r
+                return;\r
+            }\r
+        }\r
+        super.put(index, start, value);\r
+    }\r
+\r
+    public void delete(int index) {\r
+        if (!isSealed()) {\r
+            if (dense != null && 0 <= index && index < dense.length) {\r
+                dense[index] = NOT_FOUND;\r
+                return;\r
+            }\r
+        }\r
+        super.delete(index);\r
+    }\r
+\r
+    public Object[] getIds() {\r
+        Object[] superIds = super.getIds();\r
+        if (dense == null)\r
+            return superIds;\r
+        int count = 0;\r
+        int last = dense.length;\r
+        if (last > length)\r
+            last = (int) length;\r
+        for (int i=last-1; i >= 0; i--) {\r
+            if (dense[i] != NOT_FOUND)\r
+                count++;\r
+        }\r
+        count += superIds.length;\r
+        Object[] result = new Object[count];\r
+        System.arraycopy(superIds, 0, result, 0, superIds.length);\r
+        for (int i=last-1; i >= 0; i--) {\r
+            if (dense[i] != NOT_FOUND)\r
+                result[--count] = new Integer(i);\r
+        }\r
+        return result;\r
+    }\r
+    \r
+    public Object getDefaultValue(Class hint) {\r
+        if (hint == ScriptRuntime.NumberClass) {\r
+            Context cx = Context.getContext();\r
+            if (cx.getLanguageVersion() == Context.VERSION_1_2)\r
+                return new Long(length);\r
+        }\r
+        return super.getDefaultValue(hint);\r
+    }\r
+\r
+    /**\r
+     * See ECMA 15.4.1,2\r
+     */\r
+    private static Object jsConstructor(Context cx, Scriptable scope, \r
+                                        Object[] args, IdFunction ctorObj,\r
+                                        boolean inNewExpr)\r
+        throws JavaScriptException\r
+    {\r
+        if (!inNewExpr) {\r
+            // FunctionObject.construct will set up parent, proto\r
+            return ctorObj.construct(cx, scope, args);\r
+        }\r
+        if (args.length == 0)\r
+            return new NativeArray();\r
+\r
+        // Only use 1 arg as first element for version 1.2; for\r
+        // any other version (including 1.3) follow ECMA and use it as\r
+        // a length.\r
+        if (cx.getLanguageVersion() == cx.VERSION_1_2) {\r
+            return new NativeArray(args);\r
+        }\r
+        else {\r
+            if ((args.length > 1) || (!(args[0] instanceof Number)))\r
+                return new NativeArray(args);\r
+            else {\r
+                long len = ScriptRuntime.toUint32(args[0]);\r
+                if (len != (((Number)(args[0])).doubleValue()))\r
+                    throw Context.reportRuntimeError0("msg.arraylength.bad");\r
+                return new NativeArray(len);\r
+            }\r
+        }\r
+    }\r
+\r
+    public long jsGet_length() {\r
+        return length;\r
+    }\r
+\r
+    private void jsSet_length(Object val) {\r
+        /* XXX do we satisfy this?\r
+         * 15.4.5.1 [[Put]](P, V):\r
+         * 1. Call the [[CanPut]] method of A with name P.\r
+         * 2. If Result(1) is false, return.\r
+         * ?\r
+         */\r
+\r
+        if (!(val instanceof Number))\r
+            throw Context.reportRuntimeError0("msg.arraylength.bad");\r
+        \r
+        long longVal = ScriptRuntime.toUint32(val);\r
+        if (longVal != (((Number)val).doubleValue()))\r
+            throw Context.reportRuntimeError0("msg.arraylength.bad");\r
+\r
+        if (longVal < length) {\r
+            // remove all properties between longVal and length\r
+            if (length - longVal > 0x1000) {\r
+                // assume that the representation is sparse\r
+                Object[] e = getIds(); // will only find in object itself\r
+                for (int i=0; i < e.length; i++) {\r
+                    if (e[i] instanceof String) {\r
+                        // > MAXINT will appear as string\r
+                        String id = (String) e[i];\r
+                        double d = ScriptRuntime.toNumber(id);\r
+                        if (d == d && d < length)\r
+                            delete(id);\r
+                        continue;\r
+                    }\r
+                    int index = ((Number) e[i]).intValue();\r
+                    if (index >= longVal)\r
+                        delete(index);\r
+                }\r
+            } else {\r
+                // assume a dense representation\r
+                for (long i=longVal; i < length; i++) {\r
+                    // only delete if defined in the object itself\r
+                    if (hasElem(this, i))\r
+                        ScriptRuntime.delete(this, new Long(i));\r
+                }\r
+            }\r
+        }\r
+        length = longVal;\r
+    }\r
+\r
+    /* Support for generic Array-ish objects.  Most of the Array\r
+     * functions try to be generic; anything that has a length\r
+     * property is assumed to be an array.  hasLengthProperty is\r
+     * needed in addition to getLengthProperty, because\r
+     * getLengthProperty always succeeds - tries to convert strings, etc.\r
+     */\r
+    static double getLengthProperty(Scriptable obj) {\r
+        // These will both give numeric lengths within Uint32 range.\r
+        if (obj instanceof NativeString)\r
+            return (double)((NativeString)obj).jsGet_length();\r
+        if (obj instanceof NativeArray)\r
+            return (double)((NativeArray)obj).jsGet_length();\r
+        return ScriptRuntime.toUint32(ScriptRuntime\r
+                                      .getProp(obj, "length", obj));\r
+    }\r
+\r
+    static boolean hasLengthProperty(Object obj) {\r
+        if (!(obj instanceof Scriptable) || obj == Context.getUndefinedValue())\r
+            return false;\r
+        if (obj instanceof NativeString || obj instanceof NativeArray)\r
+            return true;\r
+        Scriptable sobj = (Scriptable)obj;\r
+\r
+        // XXX some confusion as to whether or not to walk to get the length\r
+        // property.  Pending review of js/[new ecma submission] treatment\r
+        // of 'arrayness'.\r
+\r
+        Object property = ScriptRuntime.getProp(sobj, "length", sobj);\r
+        return property instanceof Number;\r
+    }\r
+\r
+    /* Utility functions to encapsulate index > Integer.MAX_VALUE\r
+     * handling.  Also avoids unnecessary object creation that would\r
+     * be necessary to use the general ScriptRuntime.get/setElem\r
+     * functions... though this is probably premature optimization.\r
+     */\r
+    private static boolean hasElem(Scriptable target, long index) {\r
+        return index > Integer.MAX_VALUE\r
+               ? target.has(Long.toString(index), target)\r
+               : target.has((int)index, target);\r
+    }\r
+\r
+    private static Object getElem(Scriptable target, long index) {\r
+        if (index > Integer.MAX_VALUE) {\r
+            String id = Long.toString(index);\r
+            return ScriptRuntime.getElem(target, id, target);\r
+        } else {\r
+            return ScriptRuntime.getElem(target, (int)index);\r
+        }\r
+    }\r
+\r
+    private static void setElem(Scriptable target, long index, Object value) {\r
+        if (index > Integer.MAX_VALUE) {\r
+            String id = Long.toString(index);\r
+            ScriptRuntime.setElem(target, id, value, target);\r
+        } else {\r
+            ScriptRuntime.setElem(target, (int)index, value);\r
+        }\r
+    }\r
+\r
+    private static String jsFunction_toString(Context cx, Scriptable thisObj,\r
+                                              Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        return toStringHelper(cx, thisObj, \r
+                              cx.getLanguageVersion() == cx.VERSION_1_2,\r
+                              false);\r
+    }\r
+    \r
+    private static String jsFunction_toLocaleString(Context cx, \r
+                                                    Scriptable thisObj,\r
+                                                    Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        return toStringHelper(cx, thisObj, false, true);\r
+    }\r
+    \r
+    private static String toStringHelper(Context cx, Scriptable thisObj,\r
+                                         boolean toSource, boolean toLocale)\r
+        throws JavaScriptException\r
+    {\r
+        /* It's probably redundant to handle long lengths in this\r
+         * function; StringBuffers are limited to 2^31 in java.\r
+         */\r
+\r
+        long length = (long)getLengthProperty(thisObj);\r
+\r
+        StringBuffer result = new StringBuffer();\r
+\r
+        if (cx.iterating == null)\r
+            cx.iterating = new Hashtable(31);\r
+        boolean iterating = cx.iterating.get(thisObj) == Boolean.TRUE;\r
+\r
+        // whether to return '4,unquoted,5' or '[4, "quoted", 5]'\r
+        String separator;\r
+\r
+        if (toSource) {\r
+            result.append('[');\r
+            separator = ", ";\r
+        } else {\r
+            separator = ",";\r
+        }\r
+\r
+        boolean haslast = false;\r
+        long i = 0;\r
+\r
+        if (!iterating) {\r
+            for (i = 0; i < length; i++) {\r
+                if (i > 0)\r
+                    result.append(separator);\r
+                Object elem = getElem(thisObj, i);\r
+                if (elem == null || elem == Undefined.instance) {\r
+                    haslast = false;\r
+                    continue;\r
+                }\r
+                haslast = true;\r
+\r
+                if (elem instanceof String) {\r
+                    if (toSource) {\r
+                        result.append('\"');\r
+                        result.append(ScriptRuntime.escapeString\r
+                                      (ScriptRuntime.toString(elem)));\r
+                        result.append('\"');\r
+                    } else {\r
+                        result.append(ScriptRuntime.toString(elem));\r
+                    }\r
+                } else {\r
+                    /* wrap changes to cx.iterating in a try/finally\r
+                     * so that the reference always gets removed, and\r
+                     * we don't leak memory.  Good place for weak\r
+                     * references, if we had them.  */\r
+                    try {\r
+                        // stop recursion.\r
+                        cx.iterating.put(thisObj, Boolean.TRUE);\r
+                        if (toLocale && elem != Undefined.instance && \r
+                            elem != null) \r
+                        {\r
+                            Scriptable obj = cx.toObject(elem, thisObj);\r
+                            Object tls = ScriptRuntime.getProp(obj, \r
+                                             "toLocaleString", thisObj);\r
+                            elem = ScriptRuntime.call(cx, tls, elem, \r
+                                                      ScriptRuntime.emptyArgs);\r
+                        }\r
+                        result.append(ScriptRuntime.toString(elem));\r
+                    } finally {\r
+                        cx.iterating.remove(thisObj);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        if (toSource) {\r
+            //for [,,].length behavior; we want toString to be symmetric.\r
+            if (!haslast && i > 0)\r
+                result.append(", ]");\r
+            else\r
+                result.append(']');\r
+        }\r
+        return result.toString();\r
+    }\r
+\r
+    /**\r
+     * See ECMA 15.4.4.3\r
+     */\r
+    private static String jsFunction_join(Context cx, Scriptable thisObj,\r
+                                          Object[] args) \r
+    {\r
+        StringBuffer result = new StringBuffer();\r
+        String separator;\r
+\r
+        double length = getLengthProperty(thisObj);\r
+\r
+        // if no args, use "," as separator\r
+        if (args.length < 1) {\r
+            separator = ",";\r
+        } else {\r
+            separator = ScriptRuntime.toString(args[0]);\r
+        }\r
+        for (long i=0; i < length; i++) {\r
+            if (i > 0)\r
+                result.append(separator);\r
+            Object temp = getElem(thisObj, i);\r
+            if (temp == null || temp == Undefined.instance)\r
+                continue;\r
+            result.append(ScriptRuntime.toString(temp));\r
+        }\r
+        return result.toString();\r
+    }\r
+\r
+    /**\r
+     * See ECMA 15.4.4.4\r
+     */\r
+    private static Scriptable jsFunction_reverse(Context cx, \r
+                                                 Scriptable thisObj, \r
+                                                 Object[] args) \r
+    {\r
+        long len = (long)getLengthProperty(thisObj);\r
+\r
+        long half = len / 2;\r
+        for(long i=0; i < half; i++) {\r
+            long j = len - i - 1;\r
+            Object temp1 = getElem(thisObj, i);\r
+            Object temp2 = getElem(thisObj, j);\r
+            setElem(thisObj, i, temp2);\r
+            setElem(thisObj, j, temp1);\r
+        }\r
+        return thisObj;\r
+    }\r
+\r
+    /**\r
+     * See ECMA 15.4.4.5\r
+     */\r
+    private static Scriptable jsFunction_sort(Context cx, Scriptable scope,\r
+                                              Scriptable thisObj, \r
+                                              Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        long length = (long)getLengthProperty(thisObj);\r
+\r
+        Object compare;\r
+        if (args.length > 0 && Undefined.instance != args[0])\r
+            // sort with given compare function\r
+            compare = args[0];\r
+        else\r
+            // sort with default compare\r
+            compare = null;\r
+\r
+\r
+        // OPT: Would it make sense to use the extended sort for very small\r
+        // arrays?\r
+\r
+        // Should we use the extended sort function, or the faster one?\r
+        if (length >= Integer.MAX_VALUE) {\r
+            qsort_extended(cx, compare, thisObj, 0, length - 1);\r
+        } else {\r
+            // copy the JS array into a working array, so it can be\r
+            // sorted cheaply.\r
+            Object[] working = new Object[(int)length];\r
+            for (int i=0; i<length; i++) {\r
+                working[i] = getElem(thisObj, i);\r
+            }\r
+\r
+            qsort(cx, compare, working, 0, (int)length - 1, scope);\r
+\r
+            // copy the working array back into thisObj\r
+            for (int i=0; i<length; i++) {\r
+                setElem(thisObj, i, working[i]);\r
+            }\r
+        }\r
+        return thisObj;\r
+    }\r
+\r
+    private static double qsortCompare(Context cx, Object jsCompare, Object x,\r
+                                       Object y, Scriptable scope)\r
+        throws JavaScriptException\r
+    {\r
+        Object undef = Undefined.instance;\r
+\r
+        // sort undefined to end\r
+        if (undef == x || undef == y) {\r
+            if (undef != x)\r
+                return -1;\r
+            if (undef != y)\r
+                return 1;\r
+            return 0;\r
+        }\r
+\r
+        if (jsCompare == null) {\r
+            // if no compare function supplied, sort lexicographically\r
+            String a = ScriptRuntime.toString(x);\r
+            String b = ScriptRuntime.toString(y);\r
+\r
+            return a.compareTo(b);\r
+        } else {\r
+            // assemble args and call supplied JS compare function\r
+            // OPT: put this argument creation in the caller and reuse it.\r
+            // XXX what to do when compare function returns NaN?  ECMA states\r
+            // that it's then not a 'consistent compararison function'... but\r
+            // then what do we do?  Back out and start over with the generic\r
+            // compare function when we see a NaN?  Throw an error?\r
+            Object[] args = {x, y};\r
+//             return ScriptRuntime.toNumber(ScriptRuntime.call(jsCompare, null,\r
+//                                                              args));\r
+            // for now, just ignore it:\r
+            double d = ScriptRuntime.\r
+                toNumber(ScriptRuntime.call(cx, jsCompare, null, args, scope));\r
+\r
+            return (d == d) ? d : 0;\r
+        }\r
+    }\r
+\r
+    private static void qsort(Context cx, Object jsCompare, Object[] working,\r
+                              int lo, int hi, Scriptable scope)\r
+        throws JavaScriptException\r
+    {\r
+        Object pivot;\r
+        int i, j;\r
+        int a, b;\r
+\r
+        while (lo < hi) {\r
+            i = lo;\r
+            j = hi;\r
+            a = i;\r
+            pivot = working[a];\r
+            while (i < j) {\r
+                for(;;) {\r
+                    b = j;\r
+                    if (qsortCompare(cx, jsCompare, working[j], pivot, \r
+                                     scope) <= 0)\r
+                        break;\r
+                    j--;\r
+                }\r
+                working[a] = working[b];\r
+                while (i < j && qsortCompare(cx, jsCompare, working[a],\r
+                                             pivot, scope) <= 0)\r
+                {\r
+                    i++;\r
+                    a = i;\r
+                }\r
+                working[b] = working[a];\r
+            }\r
+            working[a] = pivot;\r
+            if (i - lo < hi - i) {\r
+                qsort(cx, jsCompare, working, lo, i - 1, scope);\r
+                lo = i + 1;\r
+            } else {\r
+                qsort(cx, jsCompare, working, i + 1, hi, scope);\r
+                hi = i - 1;\r
+            }\r
+        }\r
+    }\r
+\r
+    // A version that knows about long indices and doesn't use\r
+    // a working array.  Probably will never be used.\r
+    private static void qsort_extended(Context cx, Object jsCompare,\r
+                                       Scriptable target, long lo, long hi)\r
+        throws JavaScriptException\r
+    {\r
+        Object pivot;\r
+        long i, j;\r
+        long a, b;\r
+\r
+        while (lo < hi) {\r
+            i = lo;\r
+            j = hi;\r
+            a = i;\r
+            pivot = getElem(target, a);\r
+            while (i < j) {\r
+                for(;;) {\r
+                    b = j;\r
+                    if (qsortCompare(cx, jsCompare, getElem(target, j),\r
+                                     pivot, target) <= 0)\r
+                        break;\r
+                    j--;\r
+                }\r
+                setElem(target, a, getElem(target, b));\r
+                while (i < j && qsortCompare(cx, jsCompare,\r
+                                             getElem(target, a), \r
+                                             pivot, target) <= 0)\r
+                {\r
+                    i++;\r
+                    a = i;\r
+                }\r
+                setElem(target, b, getElem(target, a));\r
+            }\r
+            setElem(target, a, pivot);\r
+            if (i - lo < hi - i) {\r
+                qsort_extended(cx, jsCompare, target, lo, i - 1);\r
+                lo = i + 1;\r
+            } else {\r
+                qsort_extended(cx, jsCompare, target, i + 1, hi);\r
+                hi = i - 1;\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Non-ECMA methods.\r
+     */\r
+\r
+    private static Object jsFunction_push(Context cx, Scriptable thisObj,\r
+                                          Object[] args)\r
+    {\r
+        double length = getLengthProperty(thisObj);\r
+        for (int i = 0; i < args.length; i++) {\r
+            setElem(thisObj, (long)length + i, args[i]);\r
+        }\r
+\r
+        length += args.length;\r
+        ScriptRuntime.setProp(thisObj, "length", new Double(length), thisObj);\r
+\r
+        /*\r
+         * If JS1.2, follow Perl4 by returning the last thing pushed.\r
+         * Otherwise, return the new array length.\r
+         */\r
+        if (cx.getLanguageVersion() == Context.VERSION_1_2)\r
+            // if JS1.2 && no arguments, return undefined.\r
+            return args.length == 0\r
+                ? Context.getUndefinedValue()\r
+                : args[args.length - 1];\r
+\r
+        else\r
+            return new Long((long)length);\r
+    }\r
+\r
+    private static Object jsFunction_pop(Context cx, Scriptable thisObj,\r
+                                         Object[] args) {\r
+        Object result;\r
+        double length = getLengthProperty(thisObj);\r
+        if (length > 0) {\r
+            length--;\r
+\r
+            // Get the to-be-deleted property's value.\r
+            result = getElem(thisObj, (long)length);\r
+\r
+            // We don't need to delete the last property, because\r
+            // setLength does that for us.\r
+        } else {\r
+            result = Context.getUndefinedValue();\r
+        }\r
+        // necessary to match js even when length < 0; js pop will give a\r
+        // length property to any target it is called on.\r
+        ScriptRuntime.setProp(thisObj, "length", new Double(length), thisObj);\r
+\r
+        return result;\r
+    }\r
+\r
+    private static Object jsFunction_shift(Context cx, Scriptable thisObj,\r
+                                           Object[] args)\r
+    {\r
+        Object result;\r
+        double length = getLengthProperty(thisObj);\r
+        if (length > 0) {\r
+            long i = 0;\r
+            length--;\r
+\r
+            // Get the to-be-deleted property's value.\r
+            result = getElem(thisObj, i);\r
+\r
+            /*\r
+             * Slide down the array above the first element.  Leave i\r
+             * set to point to the last element.\r
+             */\r
+            if (length > 0) {\r
+                for (i = 1; i <= length; i++) {\r
+                    Object temp = getElem(thisObj, i);\r
+                    setElem(thisObj, i - 1, temp);\r
+                }\r
+            }\r
+            // We don't need to delete the last property, because\r
+            // setLength does that for us.\r
+        } else {\r
+            result = Context.getUndefinedValue();\r
+        }\r
+        ScriptRuntime.setProp(thisObj, "length", new Double(length), thisObj);\r
+        return result;\r
+    }\r
+\r
+    private static Object jsFunction_unshift(Context cx, Scriptable thisObj,\r
+                                             Object[] args)\r
+    {\r
+        Object result;\r
+        double length = (double)getLengthProperty(thisObj);\r
+        int argc = args.length;\r
+\r
+        if (args.length > 0) {\r
+            /*  Slide up the array to make room for args at the bottom */\r
+            if (length > 0) {\r
+                for (long last = (long)length - 1; last >= 0; last--) {\r
+                    Object temp = getElem(thisObj, last);\r
+                    setElem(thisObj, last + argc, temp);\r
+                }\r
+            }\r
+\r
+            /* Copy from argv to the bottom of the array. */\r
+            for (int i = 0; i < args.length; i++) {\r
+                setElem(thisObj, i, args[i]);\r
+            }\r
+\r
+            /* Follow Perl by returning the new array length. */\r
+            length += args.length;\r
+            ScriptRuntime.setProp(thisObj, "length",\r
+                                  new Double(length), thisObj);\r
+        }\r
+        return new Long((long)length);\r
+    }\r
+\r
+    private static Object jsFunction_splice(Context cx, Scriptable scope,\r
+                                            Scriptable thisObj, Object[] args)\r
+    {\r
+        /* create an empty Array to return. */\r
+        scope = getTopLevelScope(scope);\r
+        Object result = ScriptRuntime.newObject(cx, scope, "Array", null);\r
+        int argc = args.length;\r
+        if (argc == 0)\r
+            return result;\r
+        double length = getLengthProperty(thisObj);\r
+\r
+        /* Convert the first argument into a starting index. */\r
+        double begin = ScriptRuntime.toInteger(args[0]);\r
+        double end;\r
+        double delta;\r
+        double count;\r
+\r
+        if (begin < 0) {\r
+            begin += length;\r
+            if (begin < 0)\r
+                begin = 0;\r
+        } else if (begin > length) {\r
+            begin = length;\r
+        }\r
+        argc--;\r
+\r
+        /* Convert the second argument from a count into a fencepost index. */\r
+        delta = length - begin;\r
+\r
+        if (args.length == 1) {\r
+            count = delta;\r
+            end = length;\r
+        } else {\r
+            count = ScriptRuntime.toInteger(args[1]);\r
+            if (count < 0)\r
+                count = 0;\r
+            else if (count > delta)\r
+                count = delta;\r
+            end = begin + count;\r
+\r
+            argc--;\r
+        }\r
+\r
+        long lbegin = (long)begin;\r
+        long lend = (long)end;\r
+\r
+        /* If there are elements to remove, put them into the return value. */\r
+        if (count > 0) {\r
+            if (count == 1\r
+                && (cx.getLanguageVersion() == Context.VERSION_1_2))\r
+            {\r
+                /*\r
+                 * JS lacks "list context", whereby in Perl one turns the\r
+                 * single scalar that's spliced out into an array just by\r
+                 * assigning it to @single instead of $single, or by using it\r
+                 * as Perl push's first argument, for instance.\r
+                 *\r
+                 * JS1.2 emulated Perl too closely and returned a non-Array for\r
+                 * the single-splice-out case, requiring callers to test and\r
+                 * wrap in [] if necessary.  So JS1.3, default, and other\r
+                 * versions all return an array of length 1 for uniformity.\r
+                 */\r
+                result = getElem(thisObj, lbegin);\r
+            } else {\r
+                for (long last = lbegin; last < lend; last++) {\r
+                    Scriptable resultArray = (Scriptable)result;\r
+                    Object temp = getElem(thisObj, last);\r
+                    setElem(resultArray, last - lbegin, temp);\r
+                }\r
+            }\r
+        } else if (count == 0\r
+                   && cx.getLanguageVersion() == Context.VERSION_1_2)\r
+        {\r
+            /* Emulate C JS1.2; if no elements are removed, return undefined. */\r
+            result = Context.getUndefinedValue();\r
+        }\r
+\r
+        /* Find the direction (up or down) to copy and make way for argv. */\r
+        delta = argc - count;\r
+\r
+        if (delta > 0) {\r
+            for (long last = (long)length - 1; last >= lend; last--) {\r
+                Object temp = getElem(thisObj, last);\r
+                setElem(thisObj, last + (long)delta, temp);\r
+            }\r
+        } else if (delta < 0) {\r
+            for (long last = lend; last < length; last++) {\r
+                Object temp = getElem(thisObj, last);\r
+                setElem(thisObj, last + (long)delta, temp);\r
+            }\r
+        }\r
+\r
+        /* Copy from argv into the hole to complete the splice. */\r
+        int argoffset = args.length - argc;\r
+        for (int i = 0; i < argc; i++) {\r
+            setElem(thisObj, lbegin + i, args[i + argoffset]);\r
+        }\r
+\r
+        /* Update length in case we deleted elements from the end. */\r
+        ScriptRuntime.setProp(thisObj, "length",\r
+                              new Double(length + delta), thisObj);\r
+        return result;\r
+    }\r
+\r
+    /*\r
+     * Python-esque sequence operations.\r
+     */\r
+    private static Scriptable jsFunction_concat(Context cx, Scriptable scope, \r
+                                                Scriptable thisObj,\r
+                                                Object[] args)\r
+    {\r
+        /* Concat tries to keep the definition of an array as general\r
+         * as possible; if it finds that an object has a numeric\r
+         * 'length' property, then it treats that object as an array.\r
+         * This treats string atoms and string objects differently; as\r
+         * string objects have a length property and are accessible by\r
+         * index, they get exploded into arrays when added, while\r
+         * atomic strings are just added as strings.\r
+         */\r
+\r
+        // create an empty Array to return.\r
+        scope = getTopLevelScope(scope);\r
+        Scriptable result = ScriptRuntime.newObject(cx, scope, "Array", null);\r
+        double length;\r
+        long slot = 0;\r
+\r
+        /* Put the target in the result array; only add it as an array\r
+         * if it looks like one.\r
+         */\r
+        if (hasLengthProperty(thisObj)) {\r
+            length = getLengthProperty(thisObj);\r
+\r
+            // Copy from the target object into the result\r
+            for (slot = 0; slot < length; slot++) {\r
+                Object temp = getElem(thisObj, slot);\r
+                setElem(result, slot, temp);\r
+            }\r
+        } else {\r
+            setElem(result, slot++, thisObj);\r
+        }\r
+\r
+        /* Copy from the arguments into the result.  If any argument\r
+         * has a numeric length property, treat it as an array and add\r
+         * elements separately; otherwise, just copy the argument.\r
+         */\r
+        for (int i = 0; i < args.length; i++) {\r
+            if (hasLengthProperty(args[i])) {\r
+                // hasLengthProperty => instanceOf Scriptable.\r
+                Scriptable arg = (Scriptable)args[i];\r
+                length = getLengthProperty(arg);\r
+                for (long j = 0; j < length; j++, slot++) {\r
+                    Object temp = getElem(arg, j);\r
+                    setElem(result, slot, temp);\r
+                }\r
+            } else {\r
+                setElem(result, slot++, args[i]);\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+\r
+    private Scriptable jsFunction_slice(Context cx, Scriptable thisObj,\r
+                                        Object[] args)\r
+    {\r
+        Scriptable scope = getTopLevelScope(this);\r
+        Scriptable result = ScriptRuntime.newObject(cx, scope, "Array", null);\r
+        double length = getLengthProperty(thisObj);\r
+\r
+        double begin = 0;\r
+        double end = length;\r
+\r
+        if (args.length > 0) {\r
+            begin = ScriptRuntime.toInteger(args[0]);\r
+            if (begin < 0) {\r
+                begin += length;\r
+                if (begin < 0)\r
+                    begin = 0;\r
+            } else if (begin > length) {\r
+                begin = length;\r
+            }\r
+\r
+            if (args.length > 1) {\r
+                end = ScriptRuntime.toInteger(args[1]);\r
+                if (end < 0) {\r
+                    end += length;\r
+                    if (end < 0)\r
+                        end = 0;\r
+                } else if (end > length) {\r
+                    end = length;\r
+                }\r
+            }\r
+        }\r
+\r
+        long lbegin = (long)begin;\r
+        long lend = (long)end;\r
+        for (long slot = lbegin; slot < lend; slot++) {\r
+            Object temp = getElem(thisObj, slot);\r
+            setElem(result, slot - lbegin, temp);\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    protected int maxInstanceId() { return MAX_INSTANCE_ID; }\r
+\r
+    protected String getIdName(int id) {\r
+        if (id == Id_length) { return "length"; }\r
+\r
+        if (prototypeFlag) {\r
+            switch (id) {\r
+                case Id_constructor:     return "constructor";\r
+                case Id_toString:        return "toString";\r
+                case Id_toLocaleString:  return "toLocaleString";\r
+                case Id_join:            return "join";\r
+                case Id_reverse:         return "reverse";\r
+                case Id_sort:            return "sort";\r
+                case Id_push:            return "push";\r
+                case Id_pop:             return "pop";\r
+                case Id_shift:           return "shift";\r
+                case Id_unshift:         return "unshift";\r
+                case Id_splice:          return "splice";\r
+                case Id_concat:          return "concat";\r
+                case Id_slice:           return "slice";\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private static final int\r
+        Id_length        =  1,\r
+        MAX_INSTANCE_ID  =  1;\r
+\r
+    protected int mapNameToId(String s) {\r
+        if (s.equals("length")) { return Id_length; }\r
+        else if (prototypeFlag) { \r
+            return toPrototypeId(s); \r
+        }\r
+        return 0;\r
+    }\r
+\r
+// #string_id_map#\r
+\r
+    private static int toPrototypeId(String s) {\r
+        int id;\r
+// #generated# Last update: 2001-04-23 11:46:01 GMT+02:00\r
+        L0: { id = 0; String X = null; int c;\r
+            L: switch (s.length()) {\r
+            case 3: X="pop";id=Id_pop; break L;\r
+            case 4: c=s.charAt(0);\r
+                if (c=='j') { X="join";id=Id_join; }\r
+                else if (c=='p') { X="push";id=Id_push; }\r
+                else if (c=='s') { X="sort";id=Id_sort; }\r
+                break L;\r
+            case 5: c=s.charAt(1);\r
+                if (c=='h') { X="shift";id=Id_shift; }\r
+                else if (c=='l') { X="slice";id=Id_slice; }\r
+                break L;\r
+            case 6: c=s.charAt(0);\r
+                if (c=='c') { X="concat";id=Id_concat; }\r
+                else if (c=='l') { X="length";id=Id_length; }\r
+                else if (c=='s') { X="splice";id=Id_splice; }\r
+                break L;\r
+            case 7: c=s.charAt(0);\r
+                if (c=='r') { X="reverse";id=Id_reverse; }\r
+                else if (c=='u') { X="unshift";id=Id_unshift; }\r
+                break L;\r
+            case 8: X="toString";id=Id_toString; break L;\r
+            case 11: X="constructor";id=Id_constructor; break L;\r
+            case 14: X="toLocaleString";id=Id_toLocaleString; break L;\r
+            }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+        return id;\r
+    }\r
+\r
+    private static final int\r
+        Id_constructor          = MAX_INSTANCE_ID + 1,\r
+        Id_toString             = MAX_INSTANCE_ID + 2,\r
+        Id_toLocaleString       = MAX_INSTANCE_ID + 3,\r
+        Id_join                 = MAX_INSTANCE_ID + 4,\r
+        Id_reverse              = MAX_INSTANCE_ID + 5,\r
+        Id_sort                 = MAX_INSTANCE_ID + 6,\r
+        Id_push                 = MAX_INSTANCE_ID + 7,\r
+        Id_pop                  = MAX_INSTANCE_ID + 8,\r
+        Id_shift                = MAX_INSTANCE_ID + 9,\r
+        Id_unshift              = MAX_INSTANCE_ID + 10,\r
+        Id_splice               = MAX_INSTANCE_ID + 11,\r
+        Id_concat               = MAX_INSTANCE_ID + 12,\r
+        Id_slice                = MAX_INSTANCE_ID + 13,\r
+\r
+        MAX_PROTOTYPE_ID        = MAX_INSTANCE_ID + 13;\r
+\r
+// #/string_id_map#\r
+\r
+    private long length;\r
+    private Object[] dense;\r
+    private static final int maximumDenseLength = 10000;\r
+    \r
+    private boolean prototypeFlag;\r
+}\r
diff --git a/src/org/mozilla/javascript/NativeBoolean.java b/src/org/mozilla/javascript/NativeBoolean.java
new file mode 100644 (file)
index 0000000..9841b5f
--- /dev/null
@@ -0,0 +1,168 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Igor Bukanov\r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This class implements the Boolean native object.\r
+ * See ECMA 15.6.\r
+ * @author Norris Boyd\r
+ */\r
+public class NativeBoolean extends IdScriptable {\r
+\r
+    public static void init(Context cx, Scriptable scope, boolean sealed) {\r
+        NativeBoolean obj = new NativeBoolean();\r
+        obj.prototypeFlag = true;\r
+        obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);\r
+    }\r
+\r
+    /**\r
+     * Zero-parameter constructor: just used to create Boolean.prototype\r
+     */\r
+    public NativeBoolean() {\r
+    }\r
+\r
+    public NativeBoolean(boolean b) {\r
+        booleanValue = b;\r
+    }\r
+\r
+    public String getClassName() {\r
+        return "Boolean";\r
+    }\r
+\r
+    public Object getDefaultValue(Class typeHint) {\r
+        // This is actually non-ECMA, but will be proposed\r
+        // as a change in round 2.\r
+        if (typeHint == ScriptRuntime.BooleanClass)\r
+            return wrap_boolean(booleanValue);\r
+        return super.getDefaultValue(typeHint);\r
+    }\r
+\r
+    public int methodArity(int methodId) {\r
+        if (prototypeFlag) {\r
+            if (methodId == Id_constructor) return 1;\r
+            if (methodId == Id_toString) return 0;\r
+            if (methodId == Id_valueOf) return 0;\r
+        }\r
+        return super.methodArity(methodId);\r
+    }\r
+\r
+    public Object execMethod\r
+        (int methodId, IdFunction f,\r
+         Context cx, Scriptable scope, Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (prototypeFlag) {\r
+            if (methodId == Id_constructor) {\r
+                return jsConstructor(args, thisObj == null);\r
+            }\r
+            else if (methodId == Id_toString) {\r
+                return realThis(thisObj, f).jsFunction_toString();\r
+            }\r
+            else if (methodId == Id_valueOf) {\r
+                return wrap_boolean(realThis(thisObj, f).jsFunction_valueOf());\r
+            }\r
+        }\r
+\r
+        return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
+    }\r
+\r
+    private NativeBoolean realThis(Scriptable thisObj, IdFunction f) {\r
+        while (!(thisObj instanceof NativeBoolean)) {\r
+            thisObj = nextInstanceCheck(thisObj, f, true);\r
+        }\r
+        return (NativeBoolean)thisObj;\r
+    }\r
+\r
+\r
+    private Object jsConstructor(Object[] args, boolean inNewExpr) {\r
+        boolean b = ScriptRuntime.toBoolean(args, 0);\r
+        if (inNewExpr) {\r
+            // new Boolean(val) creates a new boolean object.\r
+            return new NativeBoolean(b);\r
+        }\r
+\r
+        // Boolean(val) converts val to a boolean.\r
+        return wrap_boolean(b);\r
+    }\r
+\r
+    private String jsFunction_toString() {\r
+        return booleanValue ? "true" : "false";\r
+    }\r
+\r
+    private boolean jsFunction_valueOf() {\r
+        return booleanValue;\r
+    }\r
+\r
+    protected String getIdName(int id) {\r
+        if (prototypeFlag) {\r
+            if (id == Id_constructor) return "constructor";\r
+            if (id == Id_toString) return "toString";\r
+            if (id == Id_valueOf) return "valueOf";\r
+        }\r
+        return null;        \r
+    }\r
+\r
+// #string_id_map#\r
+\r
+    protected int mapNameToId(String s) {\r
+        if (!prototypeFlag) { return 0; }\r
+        int id;\r
+// #generated# Last update: 2001-04-23 10:38:18 CEST\r
+        L0: { id = 0; String X = null;\r
+            int s_length = s.length();\r
+            if (s_length==7) { X="valueOf";id=Id_valueOf; }\r
+            else if (s_length==8) { X="toString";id=Id_toString; }\r
+            else if (s_length==11) { X="constructor";id=Id_constructor; }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+        return id;\r
+    }\r
+\r
+    private static final int\r
+        Id_constructor          = 1,\r
+        Id_toString             = 2,\r
+        Id_valueOf              = 3,\r
+        MAX_PROTOTYPE_ID        = 3;\r
+\r
+// #/string_id_map#\r
+\r
+    private boolean booleanValue;\r
+\r
+    private boolean prototypeFlag;\r
+}\r
diff --git a/src/org/mozilla/javascript/NativeCall.java b/src/org/mozilla/javascript/NativeCall.java
new file mode 100644 (file)
index 0000000..fdab7b6
--- /dev/null
@@ -0,0 +1,171 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This class implements the activation object.\r
+ *\r
+ * See ECMA 10.1.6\r
+ *\r
+ * @see org.mozilla.javascript.Arguments\r
+ * @author Norris Boyd\r
+ */\r
+public final class NativeCall extends IdScriptable {\r
+\r
+    static void init(Context cx, Scriptable scope, boolean sealed) {\r
+        NativeCall obj = new NativeCall();\r
+        obj.prototypeFlag = true;\r
+        obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);\r
+    }\r
+\r
+    NativeCall(Context cx, Scriptable scope, NativeFunction funObj, \r
+               Scriptable thisObj, Object[] args)\r
+    {\r
+        this.funObj = funObj;\r
+        this.thisObj = thisObj;\r
+        \r
+        setParentScope(scope);\r
+        // leave prototype null\r
+        \r
+        // save current activation\r
+        this.caller = cx.currentActivation;\r
+        cx.currentActivation = this;\r
+\r
+        this.originalArgs = (args == null) ? ScriptRuntime.emptyArgs : args;\r
+        \r
+        // initialize values of arguments\r
+        String[] argNames = funObj.argNames;\r
+        if (argNames != null) {\r
+            for (int i=0; i < funObj.argCount; i++) {\r
+                Object val = i < args.length ? args[i] \r
+                                             : Undefined.instance;\r
+                super.put(argNames[i], this, val);\r
+            }\r
+        }\r
+        \r
+        // initialize "arguments" property\r
+        super.put("arguments", this, new Arguments(this));\r
+    }\r
+    \r
+    private NativeCall() {\r
+    }\r
+\r
+    public String getClassName() {\r
+        return "Call";\r
+    }\r
+    \r
+    private static Object jsConstructor(Context cx, Object[] args, \r
+                                        Function ctorObj, boolean inNewExpr)\r
+    {\r
+        if (!inNewExpr) {\r
+            throw Context.reportRuntimeError1("msg.only.from.new", "Call");\r
+        }\r
+        ScriptRuntime.checkDeprecated(cx, "Call");\r
+        NativeCall result = new NativeCall();\r
+        result.setPrototype(getObjectPrototype(ctorObj));\r
+        return result;\r
+    }\r
+    \r
+    NativeCall getActivation(Function f) {\r
+        NativeCall x = this;\r
+        do {\r
+            if (x.funObj == f)\r
+                return x;\r
+            x = x.caller;\r
+        } while (x != null);\r
+        return null;\r
+    }\r
+        \r
+    public Function getFunctionObject() {\r
+        return funObj;\r
+    }\r
+\r
+    public Object[] getOriginalArguments() {\r
+        return originalArgs;\r
+    }\r
+    \r
+    public NativeCall getCaller() {\r
+        return caller;\r
+    }\r
+        \r
+    public Scriptable getThisObj() {\r
+        return thisObj;\r
+    }\r
+    \r
+    public int methodArity(int methodId) {\r
+        if (prototypeFlag) {\r
+            if (methodId == Id_constructor) return 1;\r
+        }\r
+        return super.methodArity(methodId);\r
+    }\r
+\r
+    public Object execMethod\r
+        (int methodId, IdFunction f,\r
+         Context cx, Scriptable scope, Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (prototypeFlag) {\r
+            if (methodId == Id_constructor) {\r
+                return jsConstructor(cx, args, f, thisObj == null);\r
+            }\r
+        }\r
+        return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
+    }\r
+\r
+    protected String getIdName(int id) {\r
+        if (prototypeFlag) {\r
+            if (id == Id_constructor) return "constructor";\r
+        }\r
+        return null;        \r
+    }\r
+    \r
+    protected int mapNameToId(String s) {\r
+        if (!prototypeFlag) { return 0; }\r
+        return s.equals("constructor") ? Id_constructor : 0;\r
+    }\r
+\r
+    private static final int\r
+        Id_constructor   = 1,\r
+        MAX_PROTOTYPE_ID = 1;\r
+\r
+    NativeCall caller;\r
+    NativeFunction funObj;\r
+    Scriptable thisObj;\r
+    Object[] originalArgs;\r
+    public int debugPC;\r
+\r
+    private boolean prototypeFlag;\r
+}\r
diff --git a/src/org/mozilla/javascript/NativeDate.java b/src/org/mozilla/javascript/NativeDate.java
new file mode 100644 (file)
index 0000000..77615ea
--- /dev/null
@@ -0,0 +1,1707 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Norris Boyd\r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.Date;\r
+import java.util.TimeZone;\r
+import java.util.Locale;\r
+import java.text.NumberFormat;\r
+import java.text.DateFormat;\r
+import java.text.SimpleDateFormat;\r
+\r
+/**\r
+ * This class implements the Date native object.\r
+ * See ECMA 15.9.\r
+ * @author Mike McCabe\r
+ */\r
+public class NativeDate extends IdScriptable {\r
+\r
+    public static void init(Context cx, Scriptable scope, boolean sealed) {\r
+        NativeDate obj = new NativeDate();\r
+        obj.prototypeFlag = true;\r
+        \r
+        // Set the value of the prototype Date to NaN ('invalid date');\r
+        obj.date = ScriptRuntime.NaN;\r
+\r
+        obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);\r
+    }\r
+    \r
+    public NativeDate() {\r
+        if (thisTimeZone == null) {\r
+            // j.u.TimeZone is synchronized, so setting class statics from it\r
+            // should be OK.\r
+            thisTimeZone = java.util.TimeZone.getDefault();\r
+            LocalTZA = thisTimeZone.getRawOffset();\r
+        }\r
+    }\r
+\r
+    public String getClassName() {\r
+        return "Date";\r
+    }\r
+\r
+    public Object getDefaultValue(Class typeHint) {\r
+        if (typeHint == null)\r
+            typeHint = ScriptRuntime.StringClass;\r
+        return super.getDefaultValue(typeHint);\r
+    }\r
+\r
+    protected void fillConstructorProperties\r
+        (Context cx, IdFunction ctor, boolean sealed)\r
+    {\r
+        addIdFunctionProperty(ctor, ConstructorId_UTC, sealed);\r
+        addIdFunctionProperty(ctor, ConstructorId_parse, sealed);\r
+        super.fillConstructorProperties(cx, ctor, sealed);\r
+    }\r
+\r
+    public int methodArity(int methodId) {\r
+        if (prototypeFlag) {\r
+            switch (methodId) {\r
+                case ConstructorId_UTC:     return 1;\r
+                case ConstructorId_parse:   return 1;\r
+                case Id_constructor:        return 1; \r
+                case Id_toString:           return 0;\r
+                case Id_toTimeString:       return 0;\r
+                case Id_toDateString:       return 0;\r
+                case Id_toLocaleString:     return 0;\r
+                case Id_toLocaleTimeString: return 0;\r
+                case Id_toLocaleDateString: return 0;\r
+                case Id_toUTCString:        return 0;\r
+                case Id_valueOf:            return 0;\r
+                case Id_getTime:            return 0;\r
+                case Id_getYear:            return 0;\r
+                case Id_getFullYear:        return 0;\r
+                case Id_getUTCFullYear:     return 0;\r
+                case Id_getMonth:           return 0;\r
+                case Id_getUTCMonth:        return 0;\r
+                case Id_getDate:            return 0;\r
+                case Id_getUTCDate:         return 0;\r
+                case Id_getDay:             return 0;\r
+                case Id_getUTCDay:          return 0;\r
+                case Id_getHours:           return 0;\r
+                case Id_getUTCHours:        return 0;\r
+                case Id_getMinutes:         return 0;\r
+                case Id_getUTCMinutes:      return 0;\r
+                case Id_getSeconds:         return 0;\r
+                case Id_getUTCSeconds:      return 0;\r
+                case Id_getMilliseconds:    return 0;\r
+                case Id_getUTCMilliseconds: return 0;\r
+                case Id_getTimezoneOffset:  return 0;\r
+                case Id_setTime:            return 1;\r
+                case Id_setMilliseconds:    return 1;\r
+                case Id_setUTCMilliseconds: return 1;\r
+                case Id_setSeconds:         return 2;\r
+                case Id_setUTCSeconds:      return 2;\r
+                case Id_setMinutes:         return 3;\r
+                case Id_setUTCMinutes:      return 3;\r
+                case Id_setHours:           return 4;\r
+                case Id_setUTCHours:        return 4;\r
+                case Id_setDate:            return 1;\r
+                case Id_setUTCDate:         return 1;\r
+                case Id_setMonth:           return 2;\r
+                case Id_setUTCMonth:        return 2;\r
+                case Id_setFullYear:        return 3;\r
+                case Id_setUTCFullYear:     return 3;\r
+                case Id_setYear:            return 1;\r
+            }\r
+        }\r
+        return super.methodArity(methodId);\r
+    }\r
+\r
+    public Object execMethod\r
+        (int methodId, IdFunction f,\r
+         Context cx, Scriptable scope, Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (prototypeFlag) {\r
+            switch (methodId) {\r
+                case ConstructorId_UTC: \r
+                    return wrap_double(jsStaticFunction_UTC(args));\r
+\r
+                case ConstructorId_parse: \r
+                    return wrap_double(jsStaticFunction_parse\r
+                        (ScriptRuntime.toString(args, 0)));\r
+\r
+                case Id_constructor:\r
+                    return jsConstructor(args, thisObj == null);\r
+\r
+                case Id_toString: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    return date_format(t, FORMATSPEC_FULL);\r
+                }\r
+\r
+                case Id_toTimeString: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    return date_format(t, FORMATSPEC_TIME);\r
+                }\r
+\r
+                case Id_toDateString: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    return date_format(t, FORMATSPEC_DATE);\r
+                }\r
+\r
+                case Id_toLocaleString: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    return jsFunction_toLocaleString(t);\r
+                }\r
+\r
+                case Id_toLocaleTimeString: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    return jsFunction_toLocaleTimeString(t);\r
+                }\r
+\r
+                case Id_toLocaleDateString: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    return jsFunction_toLocaleDateString(t);\r
+                }\r
+\r
+                case Id_toUTCString: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { return jsFunction_toUTCString(t); }\r
+                    return jsFunction_NaN_date_str;\r
+                }\r
+\r
+                case Id_valueOf: \r
+                    return wrap_double(realThis(thisObj, f, true).date);\r
+\r
+                case Id_getTime: \r
+                    return wrap_double(realThis(thisObj, f, true).date);\r
+\r
+                case Id_getYear: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = jsFunction_getYear(cx, t); }\r
+                    return wrap_double(t);\r
+                }\r
+\r
+                case Id_getFullYear: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = YearFromTime(LocalTime(t)); }\r
+                    return wrap_double(t);\r
+                }\r
+\r
+                case Id_getUTCFullYear: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = YearFromTime(t); }\r
+                    return wrap_double(t);\r
+                }\r
+\r
+                case Id_getMonth: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = MonthFromTime(LocalTime(t)); }\r
+                    return wrap_double(t);\r
+                }\r
+                \r
+                case Id_getUTCMonth: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = MonthFromTime(t); }\r
+                    return wrap_double(t);\r
+                }\r
+\r
+                case Id_getDate: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = DateFromTime(LocalTime(t)); }\r
+                    return wrap_double(t);\r
+                }\r
+\r
+                case Id_getUTCDate: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = DateFromTime(t); }\r
+                    return wrap_double(t);\r
+                } \r
+\r
+                case Id_getDay: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = WeekDay(LocalTime(t)); }\r
+                    return wrap_double(t);\r
+                }\r
+\r
+                case Id_getUTCDay: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = WeekDay(t); }\r
+                    return wrap_double(t);\r
+                } \r
+\r
+                case Id_getHours: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = HourFromTime(LocalTime(t)); }\r
+                    return wrap_double(t);\r
+                } \r
+\r
+                case Id_getUTCHours: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = HourFromTime(t); }\r
+                    return wrap_double(t);\r
+                }\r
+\r
+                case Id_getMinutes: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = MinFromTime(LocalTime(t)); }\r
+                    return wrap_double(t);\r
+                } \r
+\r
+                case Id_getUTCMinutes: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = MinFromTime(t); }\r
+                    return wrap_double(t);\r
+                } \r
+\r
+                case Id_getSeconds: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = SecFromTime(LocalTime(t)); }\r
+                    return wrap_double(t);\r
+                }\r
+\r
+                case Id_getUTCSeconds: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = SecFromTime(t); }\r
+                    return wrap_double(t);\r
+                }\r
+                \r
+                case Id_getMilliseconds: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = msFromTime(LocalTime(t)); }\r
+                    return wrap_double(t);\r
+                }\r
+\r
+                case Id_getUTCMilliseconds: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = msFromTime(t); }\r
+                    return wrap_double(t);\r
+                }\r
+                \r
+                case Id_getTimezoneOffset: {\r
+                    double t = realThis(thisObj, f, true).date;\r
+                    if (t == t) { t = jsFunction_getTimezoneOffset(t); }\r
+                    return wrap_double(t);\r
+                }\r
+\r
+                case Id_setTime: \r
+                    return wrap_double(realThis(thisObj, f, true).\r
+                        jsFunction_setTime(ScriptRuntime.toNumber(args, 0)));\r
+\r
+                case Id_setMilliseconds: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        makeTime(args, 1, true));\r
+\r
+                case Id_setUTCMilliseconds: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        makeTime(args, 1, false));\r
+\r
+                case Id_setSeconds: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        makeTime(args, 2, true));\r
+\r
+                case Id_setUTCSeconds: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        makeTime(args, 2, false));\r
+\r
+                case Id_setMinutes: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        makeTime(args, 3, true));\r
+\r
+                case Id_setUTCMinutes: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        makeTime(args, 3, false));\r
+\r
+                case Id_setHours: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        makeTime(args, 4, true));\r
+\r
+                case Id_setUTCHours: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        makeTime(args, 4, false));\r
+\r
+                case Id_setDate: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        makeDate(args, 1, true));\r
+\r
+                case Id_setUTCDate: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        makeDate(args, 1, false));\r
+\r
+                case Id_setMonth: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        makeDate(args, 2, true));\r
+\r
+                case Id_setUTCMonth: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        makeDate(args, 2, false));\r
+\r
+                case Id_setFullYear: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        makeDate(args, 3, true));\r
+\r
+                case Id_setUTCFullYear: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        makeDate(args, 3, false));\r
+\r
+                case Id_setYear: \r
+                    return wrap_double(realThis(thisObj, f, false).\r
+                        jsFunction_setYear(ScriptRuntime.toNumber(args, 0)));\r
+            }\r
+        }\r
+\r
+        return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
+    }\r
+\r
+    private NativeDate realThis(Scriptable thisObj, IdFunction f, \r
+                                boolean readOnly)\r
+    {\r
+        while (!(thisObj instanceof NativeDate)) {\r
+            thisObj = nextInstanceCheck(thisObj, f, readOnly);\r
+        }\r
+        return (NativeDate)thisObj;\r
+    }\r
+\r
+    /* ECMA helper functions */\r
+\r
+    private static final double HalfTimeDomain = 8.64e15;\r
+    private static final double HoursPerDay    = 24.0;\r
+    private static final double MinutesPerHour = 60.0;\r
+    private static final double SecondsPerMinute = 60.0;\r
+    private static final double msPerSecond    = 1000.0;\r
+    private static final double MinutesPerDay  = (HoursPerDay * MinutesPerHour);\r
+    private static final double SecondsPerDay  = (MinutesPerDay * SecondsPerMinute);\r
+    private static final double SecondsPerHour = (MinutesPerHour * SecondsPerMinute);\r
+    private static final double msPerDay       = (SecondsPerDay * msPerSecond);\r
+    private static final double msPerHour      = (SecondsPerHour * msPerSecond);\r
+    private static final double msPerMinute    = (SecondsPerMinute * msPerSecond);\r
+\r
+    private static double Day(double t) {\r
+        return Math.floor(t / msPerDay);\r
+    }\r
+\r
+    private static double TimeWithinDay(double t) {\r
+        double result;\r
+        result = t % msPerDay;\r
+        if (result < 0)\r
+            result += msPerDay;\r
+        return result;\r
+    }\r
+\r
+    private static int DaysInYear(int y) {\r
+        if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))\r
+            return 366;\r
+        else\r
+            return 365;\r
+    }\r
+\r
+\r
+    /* math here has to be f.p, because we need\r
+     *  floor((1968 - 1969) / 4) == -1\r
+     */\r
+    private static double DayFromYear(double y) {\r
+        return ((365 * ((y)-1970) + Math.floor(((y)-1969)/4.0)\r
+                 - Math.floor(((y)-1901)/100.0) + Math.floor(((y)-1601)/400.0)));\r
+    }\r
+\r
+    private static double TimeFromYear(double y) {\r
+        return DayFromYear(y) * msPerDay;\r
+    }\r
+\r
+    private static int YearFromTime(double t) {\r
+        int lo = (int) Math.floor((t / msPerDay) / 366) + 1970;\r
+        int hi = (int) Math.floor((t / msPerDay) / 365) + 1970;\r
+        int mid;\r
+\r
+        /* above doesn't work for negative dates... */\r
+        if (hi < lo) {\r
+            int temp = lo;\r
+            lo = hi;\r
+            hi = temp;\r
+        }\r
+\r
+        /* Use a simple binary search algorithm to find the right\r
+           year.  This seems like brute force... but the computation\r
+           of hi and lo years above lands within one year of the\r
+           correct answer for years within a thousand years of\r
+           1970; the loop below only requires six iterations\r
+           for year 270000. */\r
+        while (hi > lo) {\r
+            mid = (hi + lo) / 2;\r
+            if (TimeFromYear(mid) > t) {\r
+                hi = mid - 1;\r
+            } else {\r
+                if (TimeFromYear(mid) <= t) {\r
+                    int temp = mid + 1;\r
+                    if (TimeFromYear(temp) > t) {\r
+                        return mid;\r
+                    }\r
+                    lo = mid + 1;\r
+                }\r
+            }\r
+        }\r
+        return lo;\r
+    }\r
+\r
+    private static boolean InLeapYear(double t) {\r
+        return DaysInYear(YearFromTime(t)) == 366;\r
+    }\r
+\r
+    private static int DayWithinYear(double t) {\r
+        int year = YearFromTime(t);\r
+        return (int) (Day(t) - DayFromYear(year));\r
+    }\r
+    /*\r
+     * The following array contains the day of year for the first day of\r
+     * each month, where index 0 is January, and day 0 is January 1.\r
+     */\r
+\r
+    private static double DayFromMonth(int m, boolean leap) {\r
+        int day = m * 30;\r
+\r
+        if (m >= 7) { day += m / 2 - 1; }\r
+        else if (m >= 2) { day += (m - 1) / 2 - 1; }\r
+        else { day += m; }\r
+\r
+        if (leap && m >= 2) { ++day; }\r
+\r
+        return day;\r
+    }\r
+\r
+    private static int MonthFromTime(double t) {\r
+        int d, step;\r
+\r
+        d = DayWithinYear(t);\r
+\r
+        if (d < (step = 31))\r
+            return 0;\r
+\r
+        // Originally coded as step += (InLeapYear(t) ? 29 : 28);\r
+        // but some jits always returned 28!\r
+        if (InLeapYear(t))\r
+            step += 29;\r
+        else\r
+            step += 28;\r
+\r
+        if (d < step)\r
+            return 1;\r
+        if (d < (step += 31))\r
+            return 2;\r
+        if (d < (step += 30))\r
+            return 3;\r
+        if (d < (step += 31))\r
+            return 4;\r
+        if (d < (step += 30))\r
+            return 5;\r
+        if (d < (step += 31))\r
+            return 6;\r
+        if (d < (step += 31))\r
+            return 7;\r
+        if (d < (step += 30))\r
+            return 8;\r
+        if (d < (step += 31))\r
+            return 9;\r
+        if (d < (step += 30))\r
+            return 10;\r
+        return 11;\r
+    }\r
+\r
+    private static int DateFromTime(double t) {\r
+        int d, step, next;\r
+\r
+        d = DayWithinYear(t);\r
+        if (d <= (next = 30))\r
+            return d + 1;\r
+        step = next;\r
+\r
+        // Originally coded as next += (InLeapYear(t) ? 29 : 28);\r
+        // but some jits always returned 28!\r
+        if (InLeapYear(t))\r
+            next += 29;\r
+        else\r
+            next += 28;\r
+\r
+        if (d <= next)\r
+            return d - step;\r
+        step = next;\r
+        if (d <= (next += 31))\r
+            return d - step;\r
+        step = next;\r
+        if (d <= (next += 30))\r
+            return d - step;\r
+        step = next;\r
+        if (d <= (next += 31))\r
+            return d - step;\r
+        step = next;\r
+        if (d <= (next += 30))\r
+            return d - step;\r
+        step = next;\r
+        if (d <= (next += 31))\r
+            return d - step;\r
+        step = next;\r
+        if (d <= (next += 31))\r
+            return d - step;\r
+        step = next;\r
+        if (d <= (next += 30))\r
+            return d - step;\r
+        step = next;\r
+        if (d <= (next += 31))\r
+            return d - step;\r
+        step = next;\r
+        if (d <= (next += 30))\r
+            return d - step;\r
+        step = next;\r
+\r
+        return d - step;\r
+    }\r
+\r
+    private static int WeekDay(double t) {\r
+        double result;\r
+        result = Day(t) + 4;\r
+        result = result % 7;\r
+        if (result < 0)\r
+            result += 7;\r
+        return (int) result;\r
+    }\r
+\r
+    private static double Now() {\r
+        return (double) System.currentTimeMillis();\r
+    }\r
+\r
+    /* Should be possible to determine the need for this dynamically\r
+     * if we go with the workaround... I'm not using it now, because I\r
+     * can't think of any clean way to make toLocaleString() and the\r
+     * time zone (comment) in toString match the generated string\r
+     * values.  Currently it's wrong-but-consistent in all but the\r
+     * most recent betas of the JRE - seems to work in 1.1.7.\r
+     */\r
+    private final static boolean TZO_WORKAROUND = false;\r
+    private static double DaylightSavingTA(double t) {\r
+        if (!TZO_WORKAROUND) {\r
+            Date date = new Date((long) t);\r
+            if (thisTimeZone.inDaylightTime(date))\r
+                return msPerHour;\r
+            else\r
+                return 0;\r
+        } else {\r
+            /* Use getOffset if inDaylightTime() is broken, because it\r
+             * seems to work acceptably.  We don't switch over to it\r
+             * entirely, because it requires (expensive) exploded date arguments,\r
+             * and the api makes it impossible to handle dst\r
+             * changeovers cleanly.\r
+             */\r
+\r
+            // Hardcode the assumption that the changeover always\r
+            // happens at 2:00 AM:\r
+            t += LocalTZA + (HourFromTime(t) <= 2 ? msPerHour : 0);\r
+\r
+            int year = YearFromTime(t);\r
+            double offset = thisTimeZone.getOffset(year > 0 ? 1 : 0,\r
+                                                   year,\r
+                                                   MonthFromTime(t),\r
+                                                   DateFromTime(t),\r
+                                                   WeekDay(t),\r
+                                                   (int)TimeWithinDay(t));\r
+\r
+            if ((offset - LocalTZA) != 0)\r
+                return msPerHour;\r
+            else\r
+                return 0;\r
+            //         return offset - LocalTZA;\r
+        }\r
+    }\r
+\r
+    private static double LocalTime(double t) {\r
+        return t + LocalTZA + DaylightSavingTA(t);\r
+    }\r
+\r
+    public static double internalUTC(double t) {\r
+        return t - LocalTZA - DaylightSavingTA(t - LocalTZA);\r
+    }\r
+\r
+    private static int HourFromTime(double t) {\r
+        double result;\r
+        result = Math.floor(t / msPerHour) % HoursPerDay;\r
+        if (result < 0)\r
+            result += HoursPerDay;\r
+        return (int) result;\r
+    }\r
+\r
+    private static int MinFromTime(double t) {\r
+        double result;\r
+        result = Math.floor(t / msPerMinute) % MinutesPerHour;\r
+        if (result < 0)\r
+            result += MinutesPerHour;\r
+        return (int) result;\r
+    }\r
+\r
+    private static int SecFromTime(double t) {\r
+        double result;\r
+        result = Math.floor(t / msPerSecond) % SecondsPerMinute;\r
+        if (result < 0)\r
+            result += SecondsPerMinute;\r
+        return (int) result;\r
+    }\r
+\r
+    private static int msFromTime(double t) {\r
+        double result;\r
+        result =  t % msPerSecond;\r
+        if (result < 0)\r
+            result += msPerSecond;\r
+        return (int) result;\r
+    }\r
+\r
+    private static double MakeTime(double hour, double min,\r
+                                   double sec, double ms)\r
+    {\r
+        return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec)\r
+            * msPerSecond + ms;\r
+    }\r
+\r
+    private static double MakeDay(double year, double month, double date) {\r
+        double result;\r
+        boolean leap;\r
+        double yearday;\r
+        double monthday;\r
+\r
+        year += Math.floor(month / 12);\r
+\r
+        month = month % 12;\r
+        if (month < 0)\r
+            month += 12;\r
+\r
+        leap = (DaysInYear((int) year) == 366);\r
+\r
+        yearday = Math.floor(TimeFromYear(year) / msPerDay);\r
+        monthday = DayFromMonth((int) month, leap);\r
+\r
+        result = yearday\r
+            + monthday\r
+            + date - 1;\r
+        return result;\r
+    }\r
+\r
+    private static double MakeDate(double day, double time) {\r
+        return day * msPerDay + time;\r
+    }\r
+\r
+    private static double TimeClip(double d) {\r
+        if (d != d ||\r
+            d == Double.POSITIVE_INFINITY ||\r
+            d == Double.NEGATIVE_INFINITY ||\r
+            Math.abs(d) > HalfTimeDomain)\r
+        {\r
+            return ScriptRuntime.NaN;\r
+        }\r
+        if (d > 0.0)\r
+            return Math.floor(d + 0.);\r
+        else\r
+            return Math.ceil(d + 0.);\r
+    }\r
+\r
+    /* end of ECMA helper functions */\r
+\r
+    /* find UTC time from given date... no 1900 correction! */\r
+    public static double date_msecFromDate(double year, double mon,\r
+                                            double mday, double hour,\r
+                                            double min, double sec,\r
+                                            double msec)\r
+    {\r
+        double day;\r
+        double time;\r
+        double result;\r
+\r
+        day = MakeDay(year, mon, mday);\r
+        time = MakeTime(hour, min, sec, msec);\r
+        result = MakeDate(day, time);\r
+        return result;\r
+    }\r
+\r
+\r
+    private static final int MAXARGS = 7;\r
+    private static double jsStaticFunction_UTC(Object[] args) {\r
+        double array[] = new double[MAXARGS];\r
+        int loop;\r
+        double d;\r
+\r
+        for (loop = 0; loop < MAXARGS; loop++) {\r
+            if (loop < args.length) {\r
+                d = ScriptRuntime.toNumber(args[loop]);\r
+                if (d != d || Double.isInfinite(d)) {\r
+                    return ScriptRuntime.NaN;\r
+                }\r
+                array[loop] = ScriptRuntime.toInteger(args[loop]);\r
+            } else {\r
+                array[loop] = 0;\r
+            }\r
+        }\r
+\r
+        /* adjust 2-digit years into the 20th century */\r
+        if (array[0] >= 0 && array[0] <= 99)\r
+            array[0] += 1900;\r
+\r
+            /* if we got a 0 for 'date' (which is out of range)\r
+             * pretend it's a 1.  (So Date.UTC(1972, 5) works) */\r
+        if (array[2] < 1)\r
+            array[2] = 1;\r
+\r
+        d = date_msecFromDate(array[0], array[1], array[2],\r
+                              array[3], array[4], array[5], array[6]);\r
+        d = TimeClip(d);\r
+        return d;\r
+        //        return new Double(d);\r
+    }\r
+\r
+    /*\r
+     * Use ported code from jsdate.c rather than the locale-specific\r
+     * date-parsing code from Java, to keep js and rhino consistent.\r
+     * Is this the right strategy?\r
+     */\r
+\r
+    /* for use by date_parse */\r
+\r
+    /* replace this with byte arrays?  Cheaper? */\r
+    private static String wtb[] = {\r
+        "am", "pm",\r
+        "monday", "tuesday", "wednesday", "thursday", "friday",\r
+        "saturday", "sunday",\r
+        "january", "february", "march", "april", "may", "june",\r
+        "july", "august", "september", "october", "november", "december",\r
+        "gmt", "ut", "utc", "est", "edt", "cst", "cdt",\r
+        "mst", "mdt", "pst", "pdt"\r
+        /* time zone table needs to be expanded */\r
+    };\r
+\r
+    private static int ttb[] = {\r
+        -1, -2, 0, 0, 0, 0, 0, 0, 0,     /* AM/PM */\r
+        2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,\r
+        10000 + 0, 10000 + 0, 10000 + 0, /* UT/UTC */\r
+        10000 + 5 * 60, 10000 + 4 * 60,  /* EDT */\r
+        10000 + 6 * 60, 10000 + 5 * 60,\r
+        10000 + 7 * 60, 10000 + 6 * 60,\r
+        10000 + 8 * 60, 10000 + 7 * 60\r
+    };\r
+\r
+    /* helper for date_parse */\r
+    private static boolean date_regionMatches(String s1, int s1off,\r
+                                              String s2, int s2off,\r
+                                              int count)\r
+    {\r
+        boolean result = false;\r
+        /* return true if matches, otherwise, false */\r
+        int s1len = s1.length();\r
+        int s2len = s2.length();\r
+\r
+        while (count > 0 && s1off < s1len && s2off < s2len) {\r
+            if (Character.toLowerCase(s1.charAt(s1off)) !=\r
+                Character.toLowerCase(s2.charAt(s2off)))\r
+                break;\r
+            s1off++;\r
+            s2off++;\r
+            count--;\r
+        }\r
+\r
+        if (count == 0) {\r
+            result = true;\r
+        }\r
+        return result;\r
+    }\r
+\r
+    private static double date_parseString(String s) {\r
+        double msec;\r
+\r
+        int year = -1;\r
+        int mon = -1;\r
+        int mday = -1;\r
+        int hour = -1;\r
+        int min = -1;\r
+        int sec = -1;\r
+        char c = 0;\r
+        char si = 0;\r
+        int i = 0;\r
+        int n = -1;\r
+        double tzoffset = -1;\r
+        char prevc = 0;\r
+        int limit = 0;\r
+        boolean seenplusminus = false;\r
+\r
+        if (s == null)  // ??? Will s be null?\r
+            return ScriptRuntime.NaN;\r
+        limit = s.length();\r
+        while (i < limit) {\r
+            c = s.charAt(i);\r
+            i++;\r
+            if (c <= ' ' || c == ',' || c == '-') {\r
+                if (i < limit) {\r
+                    si = s.charAt(i);\r
+                    if (c == '-' && '0' <= si && si <= '9') {\r
+                        prevc = c;\r
+                    }\r
+                }\r
+                continue;\r
+            }\r
+            if (c == '(') { /* comments) */\r
+                int depth = 1;\r
+                while (i < limit) {\r
+                    c = s.charAt(i);\r
+                    i++;\r
+                    if (c == '(')\r
+                        depth++;\r
+                    else if (c == ')')\r
+                        if (--depth <= 0)\r
+                            break;\r
+                }\r
+                continue;\r
+            }\r
+            if ('0' <= c && c <= '9') {\r
+                n = c - '0';\r
+                while (i < limit && '0' <= (c = s.charAt(i)) && c <= '9') {\r
+                    n = n * 10 + c - '0';\r
+                    i++;\r
+                }\r
+\r
+                /* allow TZA before the year, so\r
+                 * 'Wed Nov 05 21:49:11 GMT-0800 1997'\r
+                 * works */\r
+\r
+                /* uses of seenplusminus allow : in TZA, so Java\r
+                 * no-timezone style of GMT+4:30 works\r
+                 */\r
+                if ((prevc == '+' || prevc == '-')/*  && year>=0 */) {\r
+                    /* make ':' case below change tzoffset */\r
+                    seenplusminus = true;\r
+\r
+                    /* offset */\r
+                    if (n < 24)\r
+                        n = n * 60; /* EG. "GMT-3" */\r
+                    else\r
+                        n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */\r
+                    if (prevc == '+')       /* plus means east of GMT */\r
+                        n = -n;\r
+                    if (tzoffset != 0 && tzoffset != -1)\r
+                        return ScriptRuntime.NaN;\r
+                    tzoffset = n;\r
+                } else if (n >= 70  ||\r
+                           (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {\r
+                    if (year >= 0)\r
+                        return ScriptRuntime.NaN;\r
+                    else if (c <= ' ' || c == ',' || c == '/' || i >= limit)\r
+                        year = n < 100 ? n + 1900 : n;\r
+                    else\r
+                        return ScriptRuntime.NaN;\r
+                } else if (c == ':') {\r
+                    if (hour < 0)\r
+                        hour = /*byte*/ n;\r
+                    else if (min < 0)\r
+                        min = /*byte*/ n;\r
+                    else\r
+                        return ScriptRuntime.NaN;\r
+                } else if (c == '/') {\r
+                    if (mon < 0)\r
+                        mon = /*byte*/ n-1;\r
+                    else if (mday < 0)\r
+                        mday = /*byte*/ n;\r
+                    else\r
+                        return ScriptRuntime.NaN;\r
+                } else if (i < limit && c != ',' && c > ' ' && c != '-') {\r
+                    return ScriptRuntime.NaN;\r
+                } else if (seenplusminus && n < 60) {  /* handle GMT-3:30 */\r
+                    if (tzoffset < 0)\r
+                        tzoffset -= n;\r
+                    else\r
+                        tzoffset += n;\r
+                } else if (hour >= 0 && min < 0) {\r
+                    min = /*byte*/ n;\r
+                } else if (min >= 0 && sec < 0) {\r
+                    sec = /*byte*/ n;\r
+                } else if (mday < 0) {\r
+                    mday = /*byte*/ n;\r
+                } else {\r
+                    return ScriptRuntime.NaN;\r
+                }\r
+                prevc = 0;\r
+            } else if (c == '/' || c == ':' || c == '+' || c == '-') {\r
+                prevc = c;\r
+            } else {\r
+                int st = i - 1;\r
+                int k;\r
+                while (i < limit) {\r
+                    c = s.charAt(i);\r
+                    if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))\r
+                        break;\r
+                    i++;\r
+                }\r
+                if (i <= st + 1)\r
+                    return ScriptRuntime.NaN;\r
+                for (k = wtb.length; --k >= 0;)\r
+                    if (date_regionMatches(wtb[k], 0, s, st, i-st)) {\r
+                        int action = ttb[k];\r
+                        if (action != 0) {\r
+                            if (action < 0) {\r
+                                /*\r
+                                 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as\r
+                                 * 12:30, instead of blindly adding 12 if PM.\r
+                                 */\r
+                                if (hour > 12 || hour < 0) {\r
+                                    return ScriptRuntime.NaN;\r
+                                } else {\r
+                                    if (action == -1 && hour == 12) { // am\r
+                                        hour = 0;\r
+                                    } else if (action == -2 && hour != 12) {// pm\r
+                                        hour += 12;\r
+                                    }\r
+                                }\r
+                            } else if (action <= 13) { /* month! */\r
+                                if (mon < 0) {\r
+                                    mon = /*byte*/ (action - 2);\r
+                                } else {\r
+                                    return ScriptRuntime.NaN;\r
+                                }\r
+                            } else {\r
+                                tzoffset = action - 10000;\r
+                            }\r
+                        }\r
+                        break;\r
+                    }\r
+                if (k < 0)\r
+                    return ScriptRuntime.NaN;\r
+                prevc = 0;\r
+            }\r
+        }\r
+        if (year < 0 || mon < 0 || mday < 0)\r
+            return ScriptRuntime.NaN;\r
+        if (sec < 0)\r
+            sec = 0;\r
+        if (min < 0)\r
+            min = 0;\r
+        if (hour < 0)\r
+            hour = 0;\r
+        if (tzoffset == -1) { /* no time zone specified, have to use local */\r
+            double time;\r
+            time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);\r
+            return internalUTC(time);\r
+        }\r
+\r
+        msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);\r
+        msec += tzoffset * msPerMinute;\r
+        return msec;\r
+    }\r
+\r
+    private static double jsStaticFunction_parse(String s) {\r
+        return date_parseString(s);\r
+    }\r
+\r
+    private static final int FORMATSPEC_FULL = 0;\r
+    private static final int FORMATSPEC_DATE = 1;\r
+    private static final int FORMATSPEC_TIME = 2;\r
+\r
+    private static String date_format(double t, int format) {\r
+        if (t != t)\r
+            return jsFunction_NaN_date_str;\r
+\r
+        StringBuffer result = new StringBuffer(60);\r
+        double local = LocalTime(t);\r
+\r
+        /* offset from GMT in minutes.  The offset includes daylight savings,\r
+           if it applies. */\r
+        int minutes = (int) Math.floor((LocalTZA + DaylightSavingTA(t))\r
+                                       / msPerMinute);\r
+        /* map 510 minutes to 0830 hours */\r
+        int offset = (minutes / 60) * 100 + minutes % 60;\r
+\r
+        String dateStr = Integer.toString(DateFromTime(local));\r
+        String hourStr = Integer.toString(HourFromTime(local));\r
+        String minStr = Integer.toString(MinFromTime(local));\r
+        String secStr = Integer.toString(SecFromTime(local));\r
+        String offsetStr = Integer.toString(offset > 0 ? offset : -offset);\r
+        int year = YearFromTime(local);\r
+        String yearStr = Integer.toString(year > 0 ? year : -year);\r
+\r
+        /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */\r
+        /* Tue Oct 31 2000 */\r
+        /* 09:41:40 GMT-0800 (PST) */\r
+\r
+        if (format != FORMATSPEC_TIME) {\r
+            result.append(days[WeekDay(local)]);\r
+            result.append(' ');\r
+            result.append(months[MonthFromTime(local)]);\r
+            if (dateStr.length() == 1)\r
+                result.append(" 0");\r
+            else\r
+                result.append(' ');\r
+            result.append(dateStr);\r
+            result.append(' ');\r
+        }\r
+\r
+        if (format != FORMATSPEC_DATE) {\r
+            if (hourStr.length() == 1)\r
+                result.append('0');\r
+            result.append(hourStr);\r
+            if (minStr.length() == 1)\r
+                result.append(":0");\r
+            else\r
+                result.append(':');\r
+            result.append(minStr);\r
+            if (secStr.length() == 1)\r
+                result.append(":0");\r
+            else\r
+                result.append(':');\r
+            result.append(secStr);\r
+            if (offset > 0)\r
+                result.append(" GMT+");\r
+            else\r
+                result.append(" GMT-");\r
+            for (int i = offsetStr.length(); i < 4; i++)\r
+                result.append('0');\r
+            result.append(offsetStr);\r
+\r
+            if (timeZoneFormatter == null)\r
+                timeZoneFormatter = new java.text.SimpleDateFormat("zzz");\r
+\r
+            if (timeZoneFormatter != null) {\r
+                result.append(" (");\r
+                java.util.Date date = new Date((long) t);\r
+                result.append(timeZoneFormatter.format(date));\r
+                result.append(')');\r
+            }\r
+            if (format != FORMATSPEC_TIME)\r
+                result.append(' ');\r
+        }\r
+\r
+        if (format != FORMATSPEC_TIME) {\r
+            if (year < 0)\r
+                result.append('-');\r
+            for (int i = yearStr.length(); i < 4; i++)\r
+                result.append('0');\r
+            result.append(yearStr);\r
+        }\r
+\r
+        return result.toString();\r
+    }\r
+\r
+    /* the javascript constructor */\r
+    private static Object jsConstructor(Object[] args, boolean inNewExpr) {\r
+        // if called as a function, just return a string\r
+        // representing the current time.\r
+        if (!inNewExpr)\r
+            return date_format(Now(), FORMATSPEC_FULL);\r
+\r
+        NativeDate obj = new NativeDate();\r
+\r
+        // if called as a constructor with no args,\r
+        // return a new Date with the current time.\r
+        if (args.length == 0) {\r
+            obj.date = Now();\r
+            return obj;\r
+        }\r
+\r
+        // if called with just one arg -\r
+        if (args.length == 1) {\r
+            double date;\r
+            if (args[0] instanceof Scriptable)\r
+                args[0] = ((Scriptable) args[0]).getDefaultValue(null);\r
+            if (!(args[0] instanceof String)) {\r
+                // if it's not a string, use it as a millisecond date\r
+                date = ScriptRuntime.toNumber(args[0]);\r
+            } else {\r
+                // it's a string; parse it.\r
+                String str = (String) args[0];\r
+                date = date_parseString(str);\r
+            }\r
+            obj.date = TimeClip(date);\r
+            return obj;\r
+        }\r
+\r
+        // multiple arguments; year, month, day etc.\r
+        double array[] = new double[MAXARGS];\r
+        int loop;\r
+        double d;\r
+\r
+        for (loop = 0; loop < MAXARGS; loop++) {\r
+            if (loop < args.length) {\r
+                d = ScriptRuntime.toNumber(args[loop]);\r
+\r
+                if (d != d || Double.isInfinite(d)) {\r
+                    obj.date = ScriptRuntime.NaN;\r
+                    return obj;\r
+                }\r
+                array[loop] = ScriptRuntime.toInteger(args[loop]);\r
+            } else {\r
+                array[loop] = 0;\r
+            }\r
+        }\r
+\r
+        /* adjust 2-digit years into the 20th century */\r
+        if (array[0] >= 0 && array[0] <= 99)\r
+            array[0] += 1900;\r
+\r
+        /* if we got a 0 for 'date' (which is out of range)\r
+         * pretend it's a 1 */\r
+        if (array[2] < 1)\r
+            array[2] = 1;\r
+\r
+        double day = MakeDay(array[0], array[1], array[2]);\r
+        double time = MakeTime(array[3], array[4], array[5], array[6]);\r
+        time = MakeDate(day, time);\r
+        time = internalUTC(time);\r
+        obj.date = TimeClip(time);\r
+\r
+        return obj;\r
+    }\r
+\r
+    /* constants for toString, toUTCString */\r
+    private static String jsFunction_NaN_date_str = "Invalid Date";\r
+\r
+    private static String[] days = {\r
+        "Sun","Mon","Tue","Wed","Thu","Fri","Sat"\r
+    };\r
+\r
+    private static String[] months = {\r
+        "Jan", "Feb", "Mar", "Apr", "May", "Jun",\r
+        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"\r
+    };\r
+\r
+    private static String toLocale_helper(double t,\r
+                                          java.text.DateFormat formatter)\r
+    {\r
+        if (t != t)\r
+            return jsFunction_NaN_date_str;\r
+\r
+        java.util.Date tempdate = new Date((long) t);\r
+        return formatter.format(tempdate);\r
+    }\r
+\r
+    private static String jsFunction_toLocaleString(double date) {\r
+        if (localeDateTimeFormatter == null)\r
+            localeDateTimeFormatter =\r
+                DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);\r
+\r
+        return toLocale_helper(date, localeDateTimeFormatter);\r
+    }\r
+\r
+    private static String jsFunction_toLocaleTimeString(double date) {\r
+        if (localeTimeFormatter == null)\r
+            localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG);\r
+\r
+        return toLocale_helper(date, localeTimeFormatter);\r
+    }\r
+\r
+    private static String jsFunction_toLocaleDateString(double date) {\r
+        if (localeDateFormatter == null)\r
+            localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG);\r
+\r
+        return toLocale_helper(date, localeDateFormatter);\r
+    }\r
+\r
+    private static String jsFunction_toUTCString(double date) {\r
+        StringBuffer result = new StringBuffer(60);\r
+\r
+        String dateStr = Integer.toString(DateFromTime(date));\r
+        String hourStr = Integer.toString(HourFromTime(date));\r
+        String minStr = Integer.toString(MinFromTime(date));\r
+        String secStr = Integer.toString(SecFromTime(date));\r
+        int year = YearFromTime(date);\r
+        String yearStr = Integer.toString(year > 0 ? year : -year);\r
+\r
+        result.append(days[WeekDay(date)]);\r
+        result.append(", ");\r
+        if (dateStr.length() == 1)\r
+            result.append('0');\r
+        result.append(dateStr);\r
+        result.append(' ');\r
+        result.append(months[MonthFromTime(date)]);\r
+        if (year < 0)\r
+            result.append(" -");\r
+        else\r
+            result.append(' ');\r
+        int i;\r
+        for (i = yearStr.length(); i < 4; i++)\r
+            result.append('0');\r
+        result.append(yearStr);\r
+\r
+        if (hourStr.length() == 1)\r
+            result.append(" 0");\r
+        else\r
+            result.append(' ');\r
+        result.append(hourStr);\r
+        if (minStr.length() == 1)\r
+            result.append(":0");\r
+        else\r
+            result.append(':');\r
+        result.append(minStr);\r
+        if (secStr.length() == 1)\r
+            result.append(":0");\r
+        else\r
+            result.append(':');\r
+        result.append(secStr);\r
+\r
+        result.append(" GMT");\r
+        return result.toString();\r
+    }\r
+\r
+    private static double jsFunction_getYear(Context cx, double date) {\r
+\r
+        int result = YearFromTime(LocalTime(date));\r
+\r
+        if (cx.hasFeature(Context.FEATURE_NON_ECMA_GET_YEAR)) {\r
+            if (result >= 1900 && result < 2000) {\r
+                result -= 1900;\r
+            }\r
+        } \r
+        else {\r
+            result -= 1900;\r
+        }\r
+        return result;\r
+    }\r
+\r
+    private static double jsFunction_getTimezoneOffset(double date) {\r
+        return (date - LocalTime(date)) / msPerMinute;\r
+    }\r
+\r
+    public double jsFunction_setTime(double time) {\r
+        this.date = TimeClip(time);\r
+        return this.date;\r
+    }\r
+\r
+    private double makeTime(Object[] args, int maxargs, boolean local) {\r
+        int i;\r
+        double conv[] = new double[4];\r
+        double hour, min, sec, msec;\r
+        double lorutime; /* Local or UTC version of date */\r
+\r
+        double time;\r
+        double result;\r
+\r
+        double date = this.date;\r
+\r
+        /* just return NaN if the date is already NaN */\r
+        if (date != date)\r
+            return date;\r
+\r
+        /* Satisfy the ECMA rule that if a function is called with\r
+         * fewer arguments than the specified formal arguments, the\r
+         * remaining arguments are set to undefined.  Seems like all\r
+         * the Date.setWhatever functions in ECMA are only varargs\r
+         * beyond the first argument; this should be set to undefined\r
+         * if it's not given.  This means that "d = new Date();\r
+         * d.setMilliseconds()" returns NaN.  Blech.\r
+         */\r
+        if (args.length == 0)\r
+            args = ScriptRuntime.padArguments(args, 1);\r
+\r
+        for (i = 0; i < args.length && i < maxargs; i++) {\r
+            conv[i] = ScriptRuntime.toNumber(args[i]);\r
+\r
+            // limit checks that happen in MakeTime in ECMA.\r
+            if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {\r
+                this.date = ScriptRuntime.NaN;\r
+                return this.date;\r
+            }\r
+            conv[i] = ScriptRuntime.toInteger(conv[i]);\r
+        }\r
+\r
+        if (local)\r
+            lorutime = LocalTime(date);\r
+        else\r
+            lorutime = date;\r
+\r
+        i = 0;\r
+        int stop = args.length;\r
+\r
+        if (maxargs >= 4 && i < stop)\r
+            hour = conv[i++];\r
+        else\r
+            hour = HourFromTime(lorutime);\r
+\r
+        if (maxargs >= 3 && i < stop)\r
+            min = conv[i++];\r
+        else\r
+            min = MinFromTime(lorutime);\r
+\r
+        if (maxargs >= 2 && i < stop)\r
+            sec = conv[i++];\r
+        else\r
+            sec = SecFromTime(lorutime);\r
+\r
+        if (maxargs >= 1 && i < stop)\r
+            msec = conv[i++];\r
+        else\r
+            msec = msFromTime(lorutime);\r
+\r
+        time = MakeTime(hour, min, sec, msec);\r
+        result = MakeDate(Day(lorutime), time);\r
+\r
+        if (local)\r
+            result = internalUTC(result);\r
+        date = TimeClip(result);\r
+\r
+        this.date = date;\r
+        return date;\r
+    }\r
+\r
+    private double jsFunction_setHours(Object[] args) {\r
+        return makeTime(args, 4, true);\r
+    }\r
+\r
+    private double jsFunction_setUTCHours(Object[] args) {\r
+        return makeTime(args, 4, false);\r
+    }\r
+\r
+    private double makeDate(Object[] args, int maxargs, boolean local) {\r
+        int i;\r
+        double conv[] = new double[3];\r
+        double year, month, day;\r
+        double lorutime; /* local or UTC version of date */\r
+        double result;\r
+\r
+        double date = this.date;\r
+\r
+        /* See arg padding comment in makeTime.*/\r
+        if (args.length == 0)\r
+            args = ScriptRuntime.padArguments(args, 1);\r
+\r
+        for (i = 0; i < args.length && i < maxargs; i++) {\r
+            conv[i] = ScriptRuntime.toNumber(args[i]);\r
+\r
+            // limit checks that happen in MakeDate in ECMA.\r
+            if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {\r
+                this.date = ScriptRuntime.NaN;\r
+                return this.date;\r
+            }\r
+            conv[i] = ScriptRuntime.toInteger(conv[i]);\r
+        }\r
+\r
+        /* return NaN if date is NaN and we're not setting the year,\r
+         * If we are, use 0 as the time. */\r
+        if (date != date) {\r
+            if (args.length < 3) {\r
+                return ScriptRuntime.NaN;\r
+            } else {\r
+                lorutime = 0;\r
+            }\r
+        } else {\r
+            if (local)\r
+                lorutime = LocalTime(date);\r
+            else\r
+                lorutime = date;\r
+        }\r
+\r
+        i = 0;\r
+        int stop = args.length;\r
+\r
+        if (maxargs >= 3 && i < stop)\r
+            year = conv[i++];\r
+        else\r
+            year = YearFromTime(lorutime);\r
+\r
+        if (maxargs >= 2 && i < stop)\r
+            month = conv[i++];\r
+        else\r
+            month = MonthFromTime(lorutime);\r
+\r
+        if (maxargs >= 1 && i < stop)\r
+            day = conv[i++];\r
+        else\r
+            day = DateFromTime(lorutime);\r
+\r
+        day = MakeDay(year, month, day); /* day within year */\r
+        result = MakeDate(day, TimeWithinDay(lorutime));\r
+\r
+        if (local)\r
+            result = internalUTC(result);\r
+\r
+        date = TimeClip(result);\r
+\r
+        this.date = date;\r
+        return date;\r
+    }\r
+\r
+    private double jsFunction_setYear(double year) {\r
+        double day, result;\r
+        if (year != year || Double.isInfinite(year)) {\r
+            this.date = ScriptRuntime.NaN;\r
+            return this.date;\r
+        }\r
+\r
+        if (this.date != this.date) {\r
+            this.date = 0;\r
+        } else {\r
+            this.date = LocalTime(this.date);\r
+        }\r
+\r
+        if (year >= 0 && year <= 99)\r
+            year += 1900;\r
+\r
+        day = MakeDay(year, MonthFromTime(this.date), DateFromTime(this.date));\r
+        result = MakeDate(day, TimeWithinDay(this.date));\r
+        result = internalUTC(result);\r
+\r
+        this.date = TimeClip(result);\r
+        return this.date;\r
+    }\r
+\r
+    protected String getIdName(int id) {\r
+        if (prototypeFlag) {\r
+            switch (id) {\r
+                case ConstructorId_UTC:     return "UTC";\r
+                case ConstructorId_parse:   return "parse";\r
+                case Id_constructor:        return "constructor"; \r
+                case Id_toString:           return "toString";\r
+                case Id_toTimeString:       return "toTimeString";\r
+                case Id_toDateString:       return "toDateString";\r
+                case Id_toLocaleString:     return "toLocaleString";\r
+                case Id_toLocaleTimeString: return "toLocaleTimeString";\r
+                case Id_toLocaleDateString: return "toLocaleDateString";\r
+                case Id_toUTCString:        return "toUTCString";\r
+                case Id_valueOf:            return "valueOf";\r
+                case Id_getTime:            return "getTime";\r
+                case Id_getYear:            return "getYear";\r
+                case Id_getFullYear:        return "getFullYear";\r
+                case Id_getUTCFullYear:     return "getUTCFullYear";\r
+                case Id_getMonth:           return "getMonth";\r
+                case Id_getUTCMonth:        return "getUTCMonth";\r
+                case Id_getDate:            return "getDate";\r
+                case Id_getUTCDate:         return "getUTCDate";\r
+                case Id_getDay:             return "getDay";\r
+                case Id_getUTCDay:          return "getUTCDay";\r
+                case Id_getHours:           return "getHours";\r
+                case Id_getUTCHours:        return "getUTCHours";\r
+                case Id_getMinutes:         return "getMinutes";\r
+                case Id_getUTCMinutes:      return "getUTCMinutes";\r
+                case Id_getSeconds:         return "getSeconds";\r
+                case Id_getUTCSeconds:      return "getUTCSeconds";\r
+                case Id_getMilliseconds:    return "getMilliseconds";\r
+                case Id_getUTCMilliseconds: return "getUTCMilliseconds";\r
+                case Id_getTimezoneOffset:  return "getTimezoneOffset";\r
+                case Id_setTime:            return "setTime";\r
+                case Id_setMilliseconds:    return "setMilliseconds";\r
+                case Id_setUTCMilliseconds: return "setUTCMilliseconds";\r
+                case Id_setSeconds:         return "setSeconds";\r
+                case Id_setUTCSeconds:      return "setUTCSeconds";\r
+                case Id_setMinutes:         return "setMinutes";\r
+                case Id_setUTCMinutes:      return "setUTCMinutes";\r
+                case Id_setHours:           return "setHours";\r
+                case Id_setUTCHours:        return "setUTCHours";\r
+                case Id_setDate:            return "setDate";\r
+                case Id_setUTCDate:         return "setUTCDate";\r
+                case Id_setMonth:           return "setMonth";\r
+                case Id_setUTCMonth:        return "setUTCMonth";\r
+                case Id_setFullYear:        return "setFullYear";\r
+                case Id_setUTCFullYear:     return "setUTCFullYear";\r
+                case Id_setYear:            return "setYear";\r
+            }\r
+        }\r
+        return null;        \r
+    }\r
+\r
+// #string_id_map#\r
+\r
+    protected int mapNameToId(String s) {\r
+        if (!prototypeFlag) { return 0; }\r
+        int id;\r
+// #generated# Last update: 2001-04-22 23:46:59 CEST\r
+        L0: { id = 0; String X = null; int c;\r
+            L: switch (s.length()) {\r
+            case 6: X="getDay";id=Id_getDay; break L;\r
+            case 7: switch (s.charAt(3)) {\r
+                case 'D': c=s.charAt(0);\r
+                    if (c=='g') { X="getDate";id=Id_getDate; }\r
+                    else if (c=='s') { X="setDate";id=Id_setDate; }\r
+                    break L;\r
+                case 'T': c=s.charAt(0);\r
+                    if (c=='g') { X="getTime";id=Id_getTime; }\r
+                    else if (c=='s') { X="setTime";id=Id_setTime; }\r
+                    break L;\r
+                case 'Y': c=s.charAt(0);\r
+                    if (c=='g') { X="getYear";id=Id_getYear; }\r
+                    else if (c=='s') { X="setYear";id=Id_setYear; }\r
+                    break L;\r
+                case 'u': X="valueOf";id=Id_valueOf; break L;\r
+                } break L;\r
+            case 8: c=s.charAt(0);\r
+                if (c=='g') {\r
+                    c=s.charAt(7);\r
+                    if (c=='h') { X="getMonth";id=Id_getMonth; }\r
+                    else if (c=='s') { X="getHours";id=Id_getHours; }\r
+                }\r
+                else if (c=='s') {\r
+                    c=s.charAt(7);\r
+                    if (c=='h') { X="setMonth";id=Id_setMonth; }\r
+                    else if (c=='s') { X="setHours";id=Id_setHours; }\r
+                }\r
+                else if (c=='t') { X="toString";id=Id_toString; }\r
+                break L;\r
+            case 9: X="getUTCDay";id=Id_getUTCDay; break L;\r
+            case 10: c=s.charAt(3);\r
+                if (c=='M') {\r
+                    c=s.charAt(0);\r
+                    if (c=='g') { X="getMinutes";id=Id_getMinutes; }\r
+                    else if (c=='s') { X="setMinutes";id=Id_setMinutes; }\r
+                }\r
+                else if (c=='S') {\r
+                    c=s.charAt(0);\r
+                    if (c=='g') { X="getSeconds";id=Id_getSeconds; }\r
+                    else if (c=='s') { X="setSeconds";id=Id_setSeconds; }\r
+                }\r
+                else if (c=='U') {\r
+                    c=s.charAt(0);\r
+                    if (c=='g') { X="getUTCDate";id=Id_getUTCDate; }\r
+                    else if (c=='s') { X="setUTCDate";id=Id_setUTCDate; }\r
+                }\r
+                break L;\r
+            case 11: switch (s.charAt(3)) {\r
+                case 'F': c=s.charAt(0);\r
+                    if (c=='g') { X="getFullYear";id=Id_getFullYear; }\r
+                    else if (c=='s') { X="setFullYear";id=Id_setFullYear; }\r
+                    break L;\r
+                case 'M': X="toGMTString";id=Id_toGMTString; break L;\r
+                case 'T': X="toUTCString";id=Id_toUTCString; break L;\r
+                case 'U': c=s.charAt(0);\r
+                    if (c=='g') {\r
+                        c=s.charAt(9);\r
+                        if (c=='r') { X="getUTCHours";id=Id_getUTCHours; }\r
+                        else if (c=='t') { X="getUTCMonth";id=Id_getUTCMonth; }\r
+                    }\r
+                    else if (c=='s') {\r
+                        c=s.charAt(9);\r
+                        if (c=='r') { X="setUTCHours";id=Id_setUTCHours; }\r
+                        else if (c=='t') { X="setUTCMonth";id=Id_setUTCMonth; }\r
+                    }\r
+                    break L;\r
+                case 's': X="constructor";id=Id_constructor; break L;\r
+                } break L;\r
+            case 12: c=s.charAt(2);\r
+                if (c=='D') { X="toDateString";id=Id_toDateString; }\r
+                else if (c=='T') { X="toTimeString";id=Id_toTimeString; }\r
+                break L;\r
+            case 13: c=s.charAt(0);\r
+                if (c=='g') {\r
+                    c=s.charAt(6);\r
+                    if (c=='M') { X="getUTCMinutes";id=Id_getUTCMinutes; }\r
+                    else if (c=='S') { X="getUTCSeconds";id=Id_getUTCSeconds; }\r
+                }\r
+                else if (c=='s') {\r
+                    c=s.charAt(6);\r
+                    if (c=='M') { X="setUTCMinutes";id=Id_setUTCMinutes; }\r
+                    else if (c=='S') { X="setUTCSeconds";id=Id_setUTCSeconds; }\r
+                }\r
+                break L;\r
+            case 14: c=s.charAt(0);\r
+                if (c=='g') { X="getUTCFullYear";id=Id_getUTCFullYear; }\r
+                else if (c=='s') { X="setUTCFullYear";id=Id_setUTCFullYear; }\r
+                else if (c=='t') { X="toLocaleString";id=Id_toLocaleString; }\r
+                break L;\r
+            case 15: c=s.charAt(0);\r
+                if (c=='g') { X="getMilliseconds";id=Id_getMilliseconds; }\r
+                else if (c=='s') { X="setMilliseconds";id=Id_setMilliseconds; }\r
+                break L;\r
+            case 17: X="getTimezoneOffset";id=Id_getTimezoneOffset; break L;\r
+            case 18: c=s.charAt(0);\r
+                if (c=='g') { X="getUTCMilliseconds";id=Id_getUTCMilliseconds; }\r
+                else if (c=='s') { X="setUTCMilliseconds";id=Id_setUTCMilliseconds; }\r
+                else if (c=='t') {\r
+                    c=s.charAt(8);\r
+                    if (c=='D') { X="toLocaleDateString";id=Id_toLocaleDateString; }\r
+                    else if (c=='T') { X="toLocaleTimeString";id=Id_toLocaleTimeString; }\r
+                }\r
+                break L;\r
+            }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+        return id;\r
+    }\r
+\r
+    private static final int\r
+        ConstructorId_UTC       = -2,\r
+        ConstructorId_parse     = -1,\r
+\r
+        Id_constructor          =  1,\r
+        Id_toString             =  2,\r
+        Id_toTimeString         =  3,\r
+        Id_toDateString         =  4,\r
+        Id_toLocaleString       =  5,\r
+        Id_toLocaleTimeString   =  6,\r
+        Id_toLocaleDateString   =  7,\r
+        Id_toUTCString          =  8,\r
+        Id_valueOf              =  9,\r
+        Id_getTime              = 10,\r
+        Id_getYear              = 11,\r
+        Id_getFullYear          = 12,\r
+        Id_getUTCFullYear       = 13,\r
+        Id_getMonth             = 14,\r
+        Id_getUTCMonth          = 15,\r
+        Id_getDate              = 16,\r
+        Id_getUTCDate           = 17,\r
+        Id_getDay               = 18,\r
+        Id_getUTCDay            = 19,\r
+        Id_getHours             = 20,\r
+        Id_getUTCHours          = 21,\r
+        Id_getMinutes           = 22,\r
+        Id_getUTCMinutes        = 23,\r
+        Id_getSeconds           = 24,\r
+        Id_getUTCSeconds        = 25,\r
+        Id_getMilliseconds      = 26,\r
+        Id_getUTCMilliseconds   = 27,\r
+        Id_getTimezoneOffset    = 28,\r
+        Id_setTime              = 29,\r
+        Id_setMilliseconds      = 30,\r
+        Id_setUTCMilliseconds   = 31,\r
+        Id_setSeconds           = 32,\r
+        Id_setUTCSeconds        = 33,\r
+        Id_setMinutes           = 34,\r
+        Id_setUTCMinutes        = 35,\r
+        Id_setHours             = 36,\r
+        Id_setUTCHours          = 37,\r
+        Id_setDate              = 38,\r
+        Id_setUTCDate           = 39,\r
+        Id_setMonth             = 40,\r
+        Id_setUTCMonth          = 41,\r
+        Id_setFullYear          = 42,\r
+        Id_setUTCFullYear       = 43,\r
+        Id_setYear              = 44,\r
+\r
+        MAX_PROTOTYPE_ID        = 44;\r
+\r
+    private static final int\r
+        Id_toGMTString  =  Id_toUTCString; // Alias, see Ecma B.2.6\r
+// #/string_id_map#\r
+\r
+    /* cached values */\r
+    private static java.util.TimeZone thisTimeZone;\r
+    private static double LocalTZA;\r
+    private static java.text.DateFormat timeZoneFormatter;\r
+    private static java.text.DateFormat localeDateTimeFormatter;\r
+    private static java.text.DateFormat localeDateFormatter;\r
+    private static java.text.DateFormat localeTimeFormatter;\r
+\r
+    private double date;\r
+\r
+    private boolean prototypeFlag;\r
+}\r
+\r
+\r
diff --git a/src/org/mozilla/javascript/NativeError.java b/src/org/mozilla/javascript/NativeError.java
new file mode 100644 (file)
index 0000000..982d0fc
--- /dev/null
@@ -0,0 +1,204 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Igor Bukanov\r
+ * Roger Lawrence\r
+ * \r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ *\r
+ * The class of error objects\r
+ *\r
+ *  ECMA 15.11\r
+ */\r
+public class NativeError extends IdScriptable {\r
+\r
+    public static void init(Context cx, Scriptable scope, boolean sealed) {\r
+        NativeError obj = new NativeError();\r
+        obj.prototypeFlag = true;\r
+        obj.messageValue = "";\r
+        obj.nameValue = "Error";\r
+        obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);\r
+    }\r
+    \r
+    protected int getIdDefaultAttributes(int id) {\r
+        if (id == Id_message || id == Id_name) { return EMPTY; }\r
+        return super.getIdDefaultAttributes(id);\r
+    }\r
+\r
+    protected boolean hasIdValue(int id) {\r
+        if (id == Id_message) { return messageValue != NOT_FOUND; }\r
+        if (id == Id_name) { return nameValue != NOT_FOUND; }\r
+        return super.hasIdValue(id);\r
+    }\r
+\r
+    protected Object getIdValue(int id) {\r
+        if (id == Id_message) { return messageValue; }\r
+        if (id == Id_name) { return nameValue; }\r
+        return super.getIdValue(id);\r
+    }\r
+\r
+    protected void setIdValue(int id, Object value) {\r
+        if (id == Id_message) { messageValue = value; return; }\r
+        if (id == Id_name) { nameValue = value; return; }\r
+        super.setIdValue(id, value);\r
+    }\r
+\r
+    protected void deleteIdValue(int id) {\r
+        if (id == Id_message) { messageValue = NOT_FOUND; return; }\r
+        if (id == Id_name) { nameValue = NOT_FOUND; return; }\r
+        super.deleteIdValue(id);\r
+    }\r
+\r
+    public int methodArity(int methodId) {\r
+        if (prototypeFlag) {\r
+            if (methodId == Id_constructor) return 1;\r
+            if (methodId == Id_toString) return 0;\r
+        }\r
+        return super.methodArity(methodId);\r
+    }\r
+\r
+    public Object execMethod\r
+        (int methodId, IdFunction f,\r
+         Context cx, Scriptable scope, Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (prototypeFlag) {\r
+            if (methodId == Id_constructor) {\r
+                return jsConstructor(cx, args, f, thisObj == null);\r
+            }\r
+            else if (methodId == Id_toString) {\r
+                return realThis(thisObj, f).toString();\r
+            }\r
+        }\r
+        return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
+    }\r
+\r
+    private NativeError realThis(Scriptable thisObj, IdFunction f) {\r
+        while (!(thisObj instanceof NativeError)) {\r
+            thisObj = nextInstanceCheck(thisObj, f, true);\r
+        }\r
+        return (NativeError)thisObj;\r
+    }\r
+\r
+    private static Object jsConstructor(Context cx, Object[] args, \r
+                                        Function funObj, boolean inNewExpr)\r
+    {\r
+        NativeError result = new NativeError();\r
+        if (args.length >= 1) \r
+            result.messageValue = ScriptRuntime.toString(args[0]);\r
+        result.setPrototype(getClassPrototype(funObj, "Error"));\r
+        return result;\r
+    }\r
+    \r
+    public String getClassName() { \r
+        return "Error"; \r
+    }\r
+\r
+    public String toString() {\r
+        return getName() + ": " + getMessage();\r
+    }\r
+    \r
+    public String getName() {\r
+        Object val = nameValue;\r
+        return ScriptRuntime.toString(val != NOT_FOUND ? val \r
+                                                       : Undefined.instance);\r
+    }\r
+    \r
+    public String getMessage() {\r
+        Object val = messageValue;\r
+        return ScriptRuntime.toString(val != NOT_FOUND ? val \r
+                                                       : Undefined.instance);\r
+    }    \r
+\r
+    protected int maxInstanceId() { return MAX_INSTANCE_ID; }\r
+\r
+    protected String getIdName(int id) {\r
+        if (id == Id_message) { return "message"; }\r
+        if (id == Id_name) { return "name"; }\r
+        if (prototypeFlag) {\r
+            if (id == Id_constructor) return "constructor";\r
+            if (id == Id_toString) return "toString";\r
+        }\r
+        return null;        \r
+    }\r
+    \r
+// #string_id_map#\r
+\r
+    private static final int\r
+        Id_message               = 1,\r
+        Id_name                  = 2,\r
+        \r
+        MAX_INSTANCE_ID          = 2;\r
+\r
+    protected int mapNameToId(String s) {\r
+        int id;\r
+// #generated# Last update: 2001-05-19 21:55:23 CEST\r
+        L0: { id = 0; String X = null;\r
+            int s_length = s.length();\r
+            if (s_length==4) { X="name";id=Id_name; }\r
+            else if (s_length==7) { X="message";id=Id_message; }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+// #/string_id_map#\r
+\r
+        if (id != 0 || !prototypeFlag) { return id; }\r
+\r
+// #string_id_map#\r
+// #generated# Last update: 2001-05-19 21:55:23 CEST\r
+        L0: { id = 0; String X = null;\r
+            int s_length = s.length();\r
+            if (s_length==8) { X="toString";id=Id_toString; }\r
+            else if (s_length==11) { X="constructor";id=Id_constructor; }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+        return id;\r
+    }\r
+\r
+    private static final int\r
+        Id_constructor    = MAX_INSTANCE_ID + 1,\r
+        Id_toString       = MAX_INSTANCE_ID + 2,\r
+        \r
+        MAX_PROTOTYPE_ID  = MAX_INSTANCE_ID + 2;\r
+\r
+// #/string_id_map#\r
+    \r
+    private Object messageValue = NOT_FOUND;\r
+    private Object nameValue = NOT_FOUND;\r
+\r
+    private boolean prototypeFlag;\r
+}\r
diff --git a/src/org/mozilla/javascript/NativeFunction.java b/src/org/mozilla/javascript/NativeFunction.java
new file mode 100644 (file)
index 0000000..dbc2e77
--- /dev/null
@@ -0,0 +1,798 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Igor Bukanov\r
+ * Roger Lawrence\r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.Hashtable;\r
+\r
+/**\r
+ * This class implements the Function native object.\r
+ * See ECMA 15.3.\r
+ * @author Norris Boyd\r
+ */\r
+public class NativeFunction extends BaseFunction {\r
+\r
+    private boolean nextIs(int i, int token) {\r
+        if (i + 1 < source.length())\r
+            return source.charAt(i + 1) == token;\r
+        return false;\r
+    }\r
+\r
+    // how much to indent\r
+    private final static int OFFSET = 4;\r
+\r
+    // less how much for case labels\r
+    private final static int SETBACK = 2;\r
+\r
+    // whether to do a debug print of the source information, when\r
+    // decompiling.\r
+    private static final boolean printSource = false;\r
+\r
+    /**\r
+     * Decompile the source information associated with this js\r
+     * function/script back into a string.  For the most part, this\r
+     * just means translating tokens back to their string\r
+     * representations; there's a little bit of lookahead logic to\r
+     * decide the proper spacing/indentation.  Most of the work in\r
+     * mapping the original source to the prettyprinted decompiled\r
+     * version is done by the parser.\r
+     *\r
+     * Note that support for Context.decompileFunctionBody is hacked\r
+     * on through special cases; I suspect that js makes a distinction\r
+     * between function header and function body that rhino\r
+     * decompilation does not.\r
+     *\r
+     * @param cx Current context\r
+     *\r
+     * @param indent How much to indent the decompiled result\r
+     *\r
+     * @param justbody Whether the decompilation should omit the\r
+     * function header and trailing brace.\r
+     */\r
+\r
+    public String decompile(Context cx, int indent, boolean justbody) {\r
+        StringBuffer result = new StringBuffer();\r
+        decompile(indent, true, justbody, result);\r
+        return result.toString(); \r
+        \r
+    }\r
+    \r
+    private void decompile(int indent, boolean toplevel, boolean justbody,\r
+                           StringBuffer result) \r
+    {\r
+        if (source == null) {\r
+            if (!justbody) {\r
+                result.append("function ");\r
+                result.append(getFunctionName());\r
+                result.append("() {\n\t");\r
+            }\r
+            result.append("[native code]\n");\r
+            if (!justbody) {\r
+                result.append("}\n");\r
+            }\r
+            return;\r
+        }\r
+\r
+        // Spew tokens in source, for debugging.\r
+        // as TYPE number char\r
+        if (printSource) {\r
+            System.err.println("length:" + source.length());\r
+            for (int i = 0; i < source.length(); i++) {\r
+                // Note that tokenToName will fail unless Context.printTrees\r
+                // is true.\r
+                String tokenname = TokenStream.tokenToName(source.charAt(i));\r
+                if (tokenname == null)\r
+                    tokenname = "---";\r
+                String pad = tokenname.length() > 7\r
+                    ? "\t"\r
+                    : "\t\t";\r
+                System.err.println\r
+                    (tokenname\r
+                     + pad + (int)source.charAt(i)\r
+                     + "\t'" + ScriptRuntime.escapeString\r
+                     (source.substring(i, i+1))\r
+                     + "'");\r
+            }\r
+            System.err.println();\r
+        }\r
+\r
+        int i = 0;\r
+\r
+        if (source.length() > 0) {\r
+            /* special-case FUNCTION as the first token; if it is,\r
+             * (and it's not followed by a NAME or LP) then we're\r
+             * decompiling a function (and not the toplevel script.)\r
+\r
+             * FUNCTION appearing elsewhere is an escape that means we'll\r
+             * need to call toString of the given function (object).\r
+\r
+             * If not at the top level, don't add an initial indent;\r
+             * let the caller do it, so functions as expressions look\r
+             * reasonable.  */\r
+\r
+            if (toplevel) {\r
+                // add an initial newline to exactly match js.\r
+                if (!justbody)\r
+                    result.append('\n');\r
+                for (int j = 0; j < indent; j++)\r
+                    result.append(' ');\r
+            }\r
+\r
+            if (source.charAt(0) == TokenStream.FUNCTION\r
+                // make sure it's not a script that begins with a\r
+                // reference to a function definition.\r
+                && source.length() > 1\r
+                && (source.charAt(1) == TokenStream.NAME\r
+                    || source.charAt(1) == TokenStream.LP))\r
+            {\r
+                if (!justbody) {\r
+                    result.append("function ");\r
+\r
+                    /* version != 1.2 Function constructor behavior - if\r
+                     * there's no function name in the source info, and\r
+                     * the names[0] entry is the empty string, then it must\r
+                     * have been created by the Function constructor;\r
+                     * print 'anonymous' as the function name if the\r
+                     * version (under which the function was compiled) is\r
+                     * less than 1.2... or if it's greater than 1.2, because\r
+                     * we need to be closer to ECMA.  (ToSource, please?)\r
+                     */\r
+                    if (nextIs(i, TokenStream.LP)\r
+                        && this.version != Context.VERSION_1_2\r
+                        && this.functionName != null \r
+                        && this.functionName.equals("anonymous"))\r
+                        result.append("anonymous");\r
+                    i++;\r
+                } else {\r
+                    /* Skip past the entire function header to the next EOL.\r
+                     * Depends on how NAMEs are encoded.\r
+                     */\r
+                    while (i < source.length()\r
+                           && (source.charAt(i) != TokenStream.EOL\r
+                               // the length char of a NAME sequence\r
+                               // can look like an EOL.\r
+                               || (i > 0\r
+                                   && source.charAt(i-1) == TokenStream.NAME)))\r
+                    {\r
+                        i++;\r
+                    }\r
+                    // Skip past the EOL, too.\r
+                    i++;\r
+                }\r
+            }\r
+        }\r
+\r
+        while (i < source.length()) {\r
+            int stop;\r
+            switch(source.charAt(i)) {\r
+            case TokenStream.NAME:\r
+            case TokenStream.OBJECT:  // re-wrapped in '/'s in parser...\r
+                /* NAMEs are encoded as NAME, (char) length, string...\r
+                 * Note that lookahead for detecting labels depends on\r
+                 * this encoding; change there if this changes.\r
+\r
+                 * Also change function-header skipping code above,\r
+                 * used when decompling under decompileFunctionBody.\r
+                 */\r
+                i++;\r
+                stop = i + (int)source.charAt(i);\r
+                result.append(source.substring(i + 1, stop + 1));\r
+                i = stop;\r
+                break;\r
+\r
+            case TokenStream.NUMBER:\r
+                i++;\r
+                long lbits = 0;\r
+                switch(source.charAt(i)) {\r
+                case 'S':\r
+                    i++;\r
+                    result.append((int)source.charAt(i));\r
+                    break;\r
+\r
+                case 'J':\r
+                    i++;\r
+                    lbits |= (long)source.charAt(i++) << 48;\r
+                    lbits |= (long)source.charAt(i++) << 32;\r
+                    lbits |= (long)source.charAt(i++) << 16;\r
+                    lbits |= (long)source.charAt(i);\r
+\r
+                    result.append(lbits);\r
+                    break;\r
+                case 'D':\r
+                    i++;\r
+\r
+                    lbits |= (long)source.charAt(i++) << 48;\r
+                    lbits |= (long)source.charAt(i++) << 32;\r
+                    lbits |= (long)source.charAt(i++) << 16;\r
+                    lbits |= (long)source.charAt(i);\r
+\r
+                    double dval = Double.longBitsToDouble(lbits);\r
+                    result.append(ScriptRuntime.numberToString(dval, 10));\r
+                    break;\r
+                }\r
+                break;\r
+\r
+            case TokenStream.STRING:\r
+                i++;\r
+                stop = i + (int)source.charAt(i);\r
+                result.append('"');\r
+                result.append(ScriptRuntime.escapeString\r
+                              (source.substring(i + 1, stop + 1)));\r
+                result.append('"');\r
+                i = stop;\r
+                break;\r
+\r
+            case TokenStream.PRIMARY:\r
+                i++;\r
+                switch(source.charAt(i)) {\r
+                case TokenStream.TRUE:\r
+                    result.append("true");\r
+                    break;\r
+\r
+                case TokenStream.FALSE:\r
+                    result.append("false");\r
+                    break;\r
+\r
+                case TokenStream.NULL:\r
+                    result.append("null");\r
+                    break;\r
+\r
+                case TokenStream.THIS:\r
+                    result.append("this");\r
+                    break;\r
+\r
+                case TokenStream.TYPEOF:\r
+                    result.append("typeof");\r
+                    break;\r
+\r
+                case TokenStream.VOID:\r
+                    result.append("void");\r
+                    break;\r
+\r
+                case TokenStream.UNDEFINED:\r
+                    result.append("undefined");\r
+                    break;\r
+                }\r
+                break;\r
+\r
+            case TokenStream.FUNCTION: {\r
+                /* decompile a FUNCTION token as an escape; call\r
+                 * toString on the nth enclosed nested function,\r
+                 * where n is given by the byte that follows.\r
+                 */\r
+\r
+                i++;\r
+                int functionNumber = source.charAt(i);\r
+                if (nestedFunctions == null\r
+                    || functionNumber > nestedFunctions.length)\r
+                {\r
+                    String message;\r
+                    if (functionName != null && functionName.length() > 0) {\r
+                        message = Context.getMessage2\r
+                            ("msg.no.function.ref.found.in", \r
+                             new Integer((int)source.charAt(i)), functionName);\r
+                    } else {\r
+                        message = Context.getMessage1\r
+                            ("msg.no.function.ref.found", \r
+                             new Integer((int)source.charAt(i)));\r
+                    }\r
+                    throw Context.reportRuntimeError(message);\r
+                }\r
+                nestedFunctions[functionNumber].\r
+                    decompile(indent, false, false, result);\r
+                break;\r
+            }\r
+            case TokenStream.COMMA:\r
+                result.append(", ");\r
+                break;\r
+\r
+            case TokenStream.LC:\r
+                if (nextIs(i, TokenStream.EOL))\r
+                    indent += OFFSET;\r
+                result.append('{');\r
+                break;\r
+\r
+            case TokenStream.RC:\r
+                /* don't print the closing RC if it closes the\r
+                 * toplevel function and we're called from\r
+                 * decompileFunctionBody.\r
+                 */\r
+                if (justbody && toplevel && i + 1 == source.length())\r
+                    break;\r
+\r
+                if (nextIs(i, TokenStream.EOL))\r
+                    indent -= OFFSET;\r
+                if (nextIs(i, TokenStream.WHILE)\r
+                    || nextIs(i, TokenStream.ELSE)) {\r
+                    indent -= OFFSET;\r
+                    result.append("} ");\r
+                }\r
+                else\r
+                    result.append('}');\r
+                break;\r
+\r
+            case TokenStream.LP:\r
+                result.append('(');\r
+                break;\r
+\r
+            case TokenStream.RP:\r
+                if (nextIs(i, TokenStream.LC))\r
+                    result.append(") ");\r
+                else\r
+                    result.append(')');\r
+                break;\r
+\r
+            case TokenStream.LB:\r
+                result.append('[');\r
+                break;\r
+\r
+            case TokenStream.RB:\r
+                result.append(']');\r
+                break;\r
+\r
+            case TokenStream.EOL:\r
+                result.append('\n');\r
+\r
+                /* add indent if any tokens remain,\r
+                 * less setback if next token is\r
+                 * a label, case or default.\r
+                 */\r
+                if (i + 1 < source.length()) {\r
+                    int less = 0;\r
+                    if (nextIs(i, TokenStream.CASE)\r
+                        || nextIs(i, TokenStream.DEFAULT))\r
+                        less = SETBACK;\r
+                    else if (nextIs(i, TokenStream.RC))\r
+                        less = OFFSET;\r
+\r
+                    /* elaborate check against label... skip past a\r
+                     * following inlined NAME and look for a COLON.\r
+                     * Depends on how NAME is encoded.\r
+                     */\r
+                    else if (nextIs(i, TokenStream.NAME)) {\r
+                        int skip = source.charAt(i + 2);\r
+                        if (source.charAt(i + skip + 3) == TokenStream.COLON)\r
+                            less = OFFSET;\r
+                    }\r
+\r
+                    for (; less < indent; less++)\r
+                        result.append(' ');\r
+                }\r
+                break;\r
+\r
+            case TokenStream.DOT:\r
+                result.append('.');\r
+                break;\r
+\r
+            case TokenStream.NEW:\r
+                result.append("new ");\r
+                break;\r
+\r
+            case TokenStream.DELPROP:\r
+                result.append("delete ");\r
+                break;\r
+\r
+            case TokenStream.IF:\r
+                result.append("if ");\r
+                break;\r
+\r
+            case TokenStream.ELSE:\r
+                result.append("else ");\r
+                break;\r
+\r
+            case TokenStream.FOR:\r
+                result.append("for ");\r
+                break;\r
+\r
+            case TokenStream.IN:\r
+                result.append(" in ");\r
+                break;\r
+\r
+            case TokenStream.WITH:\r
+                result.append("with ");\r
+                break;\r
+\r
+            case TokenStream.WHILE:\r
+                result.append("while ");\r
+                break;\r
+\r
+            case TokenStream.DO:\r
+                result.append("do ");\r
+                break;\r
+\r
+            case TokenStream.TRY:\r
+                result.append("try ");\r
+                break;\r
+\r
+            case TokenStream.CATCH:\r
+                result.append("catch ");\r
+                break;\r
+\r
+            case TokenStream.FINALLY:\r
+                result.append("finally ");\r
+                break;\r
+\r
+            case TokenStream.THROW:\r
+                result.append("throw ");\r
+                break;\r
+\r
+            case TokenStream.SWITCH:\r
+                result.append("switch ");\r
+                break;\r
+\r
+            case TokenStream.BREAK:\r
+                if (nextIs(i, TokenStream.NAME))\r
+                    result.append("break ");\r
+                else\r
+                    result.append("break");\r
+                break;\r
+\r
+            case TokenStream.CONTINUE:\r
+                if (nextIs(i, TokenStream.NAME))\r
+                    result.append("continue ");\r
+                else\r
+                    result.append("continue");\r
+                break;\r
+\r
+            case TokenStream.CASE:\r
+                result.append("case ");\r
+                break;\r
+\r
+            case TokenStream.DEFAULT:\r
+                result.append("default");\r
+                break;\r
+\r
+            case TokenStream.RETURN:\r
+                if (nextIs(i, TokenStream.SEMI))\r
+                    result.append("return");\r
+                else\r
+                    result.append("return ");\r
+                break;\r
+\r
+            case TokenStream.VAR:\r
+                result.append("var ");\r
+                break;\r
+\r
+            case TokenStream.SEMI:\r
+                if (nextIs(i, TokenStream.EOL))\r
+                    // statement termination\r
+                    result.append(';');\r
+                else\r
+                    // separators in FOR\r
+                    result.append("; ");\r
+                break;\r
+\r
+            case TokenStream.ASSIGN:\r
+                i++;\r
+                switch(source.charAt(i)) {\r
+                case TokenStream.NOP:\r
+                    result.append(" = ");\r
+                    break;\r
+\r
+                case TokenStream.ADD:\r
+                    result.append(" += ");\r
+                    break;\r
+\r
+                case TokenStream.SUB:\r
+                    result.append(" -= ");\r
+                    break;\r
+\r
+                case TokenStream.MUL:\r
+                    result.append(" *= ");\r
+                    break;\r
+\r
+                case TokenStream.DIV:\r
+                    result.append(" /= ");\r
+                    break;\r
+\r
+                case TokenStream.MOD:\r
+                    result.append(" %= ");\r
+                    break;\r
+\r
+                case TokenStream.BITOR:\r
+                    result.append(" |= ");\r
+                    break;\r
+\r
+                case TokenStream.BITXOR:\r
+                    result.append(" ^= ");\r
+                    break;\r
+\r
+                case TokenStream.BITAND:\r
+                    result.append(" &= ");\r
+                    break;\r
+\r
+                case TokenStream.LSH:\r
+                    result.append(" <<= ");\r
+                    break;\r
+\r
+                case TokenStream.RSH:\r
+                    result.append(" >>= ");\r
+                    break;\r
+\r
+                case TokenStream.URSH:\r
+                    result.append(" >>>= ");\r
+                    break;\r
+                }\r
+                break;\r
+\r
+            case TokenStream.HOOK:\r
+                result.append(" ? ");\r
+                break;\r
+\r
+            case TokenStream.OBJLIT:\r
+                // pun OBJLIT to mean colon in objlit property initialization.\r
+                // this needs to be distinct from COLON in the general case\r
+                // to distinguish from the colon in a ternary... which needs\r
+                // different spacing.\r
+                result.append(':');\r
+                break;\r
+\r
+            case TokenStream.COLON:\r
+                if (nextIs(i, TokenStream.EOL))\r
+                    // it's the end of a label\r
+                    result.append(':');\r
+                else\r
+                    // it's the middle part of a ternary\r
+                    result.append(" : ");\r
+                break;\r
+\r
+            case TokenStream.OR:\r
+                result.append(" || ");\r
+                break;\r
+\r
+            case TokenStream.AND:\r
+                result.append(" && ");\r
+                break;\r
+\r
+            case TokenStream.BITOR:\r
+                result.append(" | ");\r
+                break;\r
+\r
+            case TokenStream.BITXOR:\r
+                result.append(" ^ ");\r
+                break;\r
+\r
+            case TokenStream.BITAND:\r
+                result.append(" & ");\r
+                break;\r
+\r
+            case TokenStream.EQOP:\r
+                i++;\r
+                switch(source.charAt(i)) {\r
+                case TokenStream.SHEQ:\r
+                    /*\r
+                     * Emulate the C engine; if we're under version\r
+                     * 1.2, then the == operator behaves like the ===\r
+                     * operator (and the source is generated by\r
+                     * decompiling a === opcode), so print the ===\r
+                     * operator as ==.\r
+                     */\r
+                    result.append(this.version == Context.VERSION_1_2 ? " == "\r
+                                  : " === ");\r
+                    break;\r
+\r
+                case TokenStream.SHNE:\r
+                    result.append(this.version == Context.VERSION_1_2 ? " != "\r
+                                  : " !== ");\r
+                    break;\r
+\r
+                case TokenStream.EQ:\r
+                    result.append(" == ");\r
+                    break;\r
+\r
+                case TokenStream.NE:\r
+                    result.append(" != ");\r
+                    break;\r
+                }\r
+                break;\r
+\r
+            case TokenStream.RELOP:\r
+                i++;\r
+                switch(source.charAt(i)) {\r
+                case TokenStream.LE:\r
+                    result.append(" <= ");\r
+                    break;\r
+\r
+                case TokenStream.LT:\r
+                    result.append(" < ");\r
+                    break;\r
+\r
+                case TokenStream.GE:\r
+                    result.append(" >= ");\r
+                    break;\r
+\r
+                case TokenStream.GT:\r
+                    result.append(" > ");\r
+                    break;\r
+\r
+                case TokenStream.INSTANCEOF:\r
+                    result.append(" instanceof ");\r
+                    break;\r
+                }\r
+                break;\r
+\r
+            case TokenStream.SHOP:\r
+                i++;\r
+                switch(source.charAt(i)) {\r
+                case TokenStream.LSH:\r
+                    result.append(" << ");\r
+                    break;\r
+\r
+                case TokenStream.RSH:\r
+                    result.append(" >> ");\r
+                    break;\r
+\r
+                case TokenStream.URSH:\r
+                    result.append(" >>> ");\r
+                    break;\r
+                }\r
+                break;\r
+\r
+            case TokenStream.UNARYOP:\r
+                i++;\r
+                switch(source.charAt(i)) {\r
+                case TokenStream.TYPEOF:\r
+                    result.append("typeof ");\r
+                    break;\r
+\r
+                case TokenStream.VOID:\r
+                    result.append("void ");\r
+                    break;\r
+\r
+                case TokenStream.NOT:\r
+                    result.append('!');\r
+                    break;\r
+\r
+                case TokenStream.BITNOT:\r
+                    result.append('~');\r
+                    break;\r
+\r
+                case TokenStream.ADD:\r
+                    result.append('+');\r
+                    break;\r
+\r
+                case TokenStream.SUB:\r
+                    result.append('-');\r
+                    break;\r
+                }\r
+                break;\r
+\r
+            case TokenStream.INC:\r
+                result.append("++");\r
+                break;\r
+\r
+            case TokenStream.DEC:\r
+                result.append("--");\r
+                break;\r
+\r
+            case TokenStream.ADD:\r
+                result.append(" + ");\r
+                break;\r
+\r
+            case TokenStream.SUB:\r
+                result.append(" - ");\r
+                break;\r
+\r
+            case TokenStream.MUL:\r
+                result.append(" * ");\r
+                break;\r
+\r
+            case TokenStream.DIV:\r
+                result.append(" / ");\r
+                break;\r
+\r
+            case TokenStream.MOD:\r
+                result.append(" % ");\r
+                break;\r
+\r
+            default: \r
+                // If we don't know how to decompile it, raise an exception.\r
+                throw new RuntimeException("Unknown token " + \r
+                                           source.charAt(i));\r
+            }\r
+            i++;\r
+        }\r
+\r
+        // add that trailing newline if it's an outermost function.\r
+        if (toplevel && !justbody)\r
+            result.append('\n');\r
+    }\r
+\r
+    public int getLength() {\r
+        Context cx = Context.getContext();\r
+        if (cx != null && cx.getLanguageVersion() != Context.VERSION_1_2)\r
+            return argCount;\r
+        NativeCall activation = getActivation(cx);\r
+        if (activation == null)\r
+            return argCount;\r
+        return activation.getOriginalArguments().length;\r
+    }\r
+\r
+    public int getArity() {\r
+        return argCount;\r
+    }\r
+\r
+    public String getFunctionName() {\r
+        if (functionName == null)\r
+            return "";\r
+        if (functionName.equals("anonymous")) {\r
+            Context cx = Context.getCurrentContext();\r
+            if (cx != null && cx.getLanguageVersion() == Context.VERSION_1_2)\r
+                return "";\r
+        }\r
+        return functionName;\r
+    }\r
+\r
+    /**\r
+     * For backwards compatibility keep an old method name used by\r
+     * Batik and possibly others.\r
+     */\r
+    public String jsGet_name() {\r
+        return getFunctionName();\r
+    }\r
+\r
+    /**\r
+     * The "argsNames" array has the following information:\r
+     * argNames[0] through argNames[argCount - 1]: the names of the parameters\r
+     * argNames[argCount] through argNames[args.length-1]: the names of the\r
+     * variables declared in var statements\r
+     */\r
+    protected String[] argNames;\r
+    protected short argCount;\r
+    protected short version;\r
+\r
+    /**\r
+     * An encoded representation of the function source, for\r
+     * decompiling.  Needs to be visible (only) to generated\r
+     * subclasses of NativeFunction.\r
+     */\r
+    protected String source;\r
+\r
+    /**\r
+     * An array of NativeFunction values for each nested function.\r
+     * Used internally, and also for decompiling nested functions.\r
+     */\r
+    public NativeFunction[] nestedFunctions;\r
+\r
+    // For all generated subclass objects debug_level is set to 0 or higher.\r
+    // So, if debug_level remains -1 in some object, then that object is\r
+    // known to have not been generated.\r
+    public int debug_level = -1;\r
+    public String debug_srcName;\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/NativeGlobal.java b/src/org/mozilla/javascript/NativeGlobal.java
new file mode 100644 (file)
index 0000000..6f7df59
--- /dev/null
@@ -0,0 +1,843 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Norris Boyd\r
+ * Igor Bukanov\r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.io.StringReader;\r
+import java.io.IOException;\r
+import java.lang.reflect.Method;\r
+\r
+/**\r
+ * This class implements the global native object (function and value\r
+ * properties only).\r
+ *\r
+ * See ECMA 15.1.[12].\r
+ *\r
+ * @author Mike Shaver\r
+ */\r
+\r
+public class NativeGlobal implements IdFunctionMaster {\r
+\r
+    public static void init(Context cx, Scriptable scope, boolean sealed) {\r
+        NativeGlobal obj = new NativeGlobal();\r
+        obj.scopeSlaveFlag = true;\r
+\r
+        for (int id = 1; id <= LAST_SCOPE_FUNCTION_ID; ++id) {\r
+            String name = getMethodName(id);\r
+            IdFunction f = new IdFunction(obj, name, id);\r
+            f.setParentScope(scope);\r
+            if (sealed) { f.sealObject(); }\r
+            ScriptableObject.defineProperty(scope, name, f,\r
+                                            ScriptableObject.DONTENUM);\r
+        }\r
+\r
+        ScriptableObject.defineProperty(scope, "NaN",\r
+                                        ScriptRuntime.NaNobj,\r
+                                        ScriptableObject.DONTENUM);\r
+        ScriptableObject.defineProperty(scope, "Infinity",\r
+                                        new Double(Double.POSITIVE_INFINITY),\r
+                                        ScriptableObject.DONTENUM);\r
+        /*\r
+        ScriptableObject.defineProperty(scope, "undefined",\r
+                                        Undefined.instance,\r
+                                        ScriptableObject.DONTENUM);\r
+        */\r
+\r
+        String[] errorMethods = { "ConversionError",\r
+                                  "EvalError",\r
+                                  "RangeError",\r
+                                  "ReferenceError",\r
+                                  "SyntaxError",\r
+                                  "TypeError",\r
+                                  "URIError"\r
+                                };\r
+\r
+        /*\r
+            Each error constructor gets its own Error object as a prototype,\r
+            with the 'name' property set to the name of the error.\r
+        */\r
+        for (int i = 0; i < errorMethods.length; i++) {\r
+            String name = errorMethods[i];\r
+            IdFunction ctor = new IdFunction(obj, name, Id_new_CommonError);\r
+            ctor.setFunctionType(IdFunction.FUNCTION_AND_CONSTRUCTOR);\r
+            ctor.setParentScope(scope);\r
+            ScriptableObject.defineProperty(scope, name, ctor,\r
+                                            ScriptableObject.DONTENUM);\r
+\r
+            Scriptable errorProto = ScriptRuntime.newObject\r
+                (cx, scope, "Error", ScriptRuntime.emptyArgs);\r
+\r
+            errorProto.put("name", errorProto, name);\r
+            ctor.put("prototype", ctor, errorProto);\r
+            if (sealed) {\r
+                ctor.sealObject();\r
+                if (errorProto instanceof ScriptableObject) {\r
+                    ((ScriptableObject)errorProto).sealObject();\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    public Object execMethod(int methodId, IdFunction function, Context cx,\r
+                             Scriptable scope, Scriptable thisObj,\r
+                             Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (scopeSlaveFlag) {\r
+            switch (methodId) {\r
+                case Id_decodeURI:\r
+                    return js_decodeURI(cx, args);\r
+\r
+                case Id_decodeURIComponent:\r
+                    return js_decodeURIComponent(cx, args);\r
+\r
+                case Id_encodeURI:\r
+                    return js_encodeURI(cx, args);\r
+\r
+                case Id_encodeURIComponent:\r
+                    return js_encodeURIComponent(cx, args);\r
+\r
+                case Id_escape:\r
+                    return js_escape(cx, args);\r
+\r
+                case Id_eval:\r
+                    return js_eval(cx, scope, args);\r
+\r
+                case Id_isFinite:\r
+                    return js_isFinite(cx, args);\r
+\r
+                case Id_isNaN:\r
+                    return js_isNaN(cx, args);\r
+\r
+                case Id_parseFloat:\r
+                    return js_parseFloat(cx, args);\r
+\r
+                case Id_parseInt:\r
+                    return js_parseInt(cx, args);\r
+\r
+                case Id_unescape:\r
+                    return js_unescape(cx, args);\r
+\r
+                case Id_new_CommonError:\r
+                    return new_CommonError(function, cx, scope, args);\r
+            }\r
+        }\r
+        throw IdFunction.onBadMethodId(this, methodId);\r
+    }\r
+\r
+    public int methodArity(int methodId) {\r
+        if (scopeSlaveFlag) {\r
+            switch (methodId) {\r
+                case Id_decodeURI:           return 1;\r
+                case Id_decodeURIComponent:  return 1;\r
+                case Id_encodeURI:           return 1;\r
+                case Id_encodeURIComponent:  return 1;\r
+                case Id_escape:              return 1;\r
+                case Id_eval:                return 1;\r
+                case Id_isFinite:            return 1;\r
+                case Id_isNaN:               return 1;\r
+                case Id_parseFloat:          return 1;\r
+                case Id_parseInt:            return 2;\r
+                case Id_unescape:            return 1;\r
+\r
+                case Id_new_CommonError:     return 1;\r
+            }\r
+        }\r
+        return -1;\r
+    }\r
+\r
+    private static String getMethodName(int methodId) {\r
+        switch (methodId) {\r
+            case Id_decodeURI:           return "decodeURI";\r
+            case Id_decodeURIComponent:  return "decodeURIComponent";\r
+            case Id_encodeURI:           return "encodeURI";\r
+            case Id_encodeURIComponent:  return "encodeURIComponent";\r
+            case Id_escape:              return "escape";\r
+            case Id_eval:                return "eval";\r
+            case Id_isFinite:            return "isFinite";\r
+            case Id_isNaN:               return "isNaN";\r
+            case Id_parseFloat:          return "parseFloat";\r
+            case Id_parseInt:            return "parseInt";\r
+            case Id_unescape:            return "unescape";\r
+        }\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * The global method parseInt, as per ECMA-262 15.1.2.2.\r
+     */\r
+    private Object js_parseInt(Context cx, Object[] args) {\r
+        String s = ScriptRuntime.toString(args, 0);\r
+        int radix = ScriptRuntime.toInt32(args, 1);\r
+\r
+        int len = s.length();\r
+        if (len == 0)\r
+            return ScriptRuntime.NaNobj;\r
+\r
+        boolean negative = false;\r
+        int start = 0;\r
+        char c;\r
+        do {\r
+            c = s.charAt(start);\r
+            if (!Character.isWhitespace(c))\r
+                break;\r
+            start++;\r
+        } while (start < len);\r
+\r
+        if (c == '+' || (negative = (c == '-')))\r
+            start++;\r
+\r
+        final int NO_RADIX = -1;\r
+        if (radix == 0) {\r
+            radix = NO_RADIX;\r
+        } else if (radix < 2 || radix > 36) {\r
+            return ScriptRuntime.NaNobj;\r
+        } else if (radix == 16 && len - start > 1 && s.charAt(start) == '0') {\r
+            c = s.charAt(start+1);\r
+            if (c == 'x' || c == 'X')\r
+                start += 2;\r
+        }\r
+\r
+        if (radix == NO_RADIX) {\r
+            radix = 10;\r
+            if (len - start > 1 && s.charAt(start) == '0') {\r
+                c = s.charAt(start+1);\r
+                if (c == 'x' || c == 'X') {\r
+                    radix = 16;\r
+                    start += 2;\r
+                } else if (c != '.') {\r
+                    radix = 8;\r
+                    start++;\r
+                }\r
+            }\r
+        }\r
+\r
+        double d = ScriptRuntime.stringToNumber(s, start, radix);\r
+        return new Double(negative ? -d : d);\r
+    }\r
+\r
+    /**\r
+     * The global method parseFloat, as per ECMA-262 15.1.2.3.\r
+     *\r
+     * @param cx unused\r
+     * @param thisObj unused\r
+     * @param args the arguments to parseFloat, ignoring args[>=1]\r
+     * @param funObj unused\r
+     */\r
+    private Object js_parseFloat(Context cx, Object[] args) {\r
+        if (args.length < 1)\r
+            return ScriptRuntime.NaNobj;\r
+        String s = ScriptRuntime.toString(args[0]);\r
+        int len = s.length();\r
+        if (len == 0)\r
+            return ScriptRuntime.NaNobj;\r
+\r
+        int i;\r
+        char c;\r
+        // Scan forward to the first digit or .\r
+        for (i=0; TokenStream.isJSSpace(c = s.charAt(i)) && i+1 < len; i++)\r
+            /* empty */\r
+            ;\r
+\r
+        int start = i;\r
+\r
+        if (c == '+' || c == '-')\r
+            c = s.charAt(++i);\r
+\r
+        if (c == 'I') {\r
+            // check for "Infinity"\r
+            double d;\r
+            if (i+8 <= len && s.substring(i, i+8).equals("Infinity"))\r
+                d = s.charAt(start) == '-' ? Double.NEGATIVE_INFINITY\r
+                                           : Double.POSITIVE_INFINITY;\r
+            else\r
+                return ScriptRuntime.NaNobj;\r
+            return new Double(d);\r
+        }\r
+\r
+        // Find the end of the legal bit\r
+        int decimal = -1;\r
+        int exponent = -1;\r
+        for (; i < len; i++) {\r
+            switch (s.charAt(i)) {\r
+              case '.':\r
+                if (decimal != -1) // Only allow a single decimal point.\r
+                    break;\r
+                decimal = i;\r
+                continue;\r
+\r
+              case 'e':\r
+              case 'E':\r
+                if (exponent != -1)\r
+                    break;\r
+                exponent = i;\r
+                continue;\r
+\r
+              case '+':\r
+              case '-':\r
+                 // Only allow '+' or '-' after 'e' or 'E'\r
+                if (exponent != i-1)\r
+                    break;\r
+                continue;\r
+\r
+              case '0': case '1': case '2': case '3': case '4':\r
+              case '5': case '6': case '7': case '8': case '9':\r
+                continue;\r
+\r
+              default:\r
+                break;\r
+            }\r
+            break;\r
+        }\r
+        s = s.substring(start, i);\r
+        try {\r
+            return Double.valueOf(s);\r
+        }\r
+        catch (NumberFormatException ex) {\r
+            return ScriptRuntime.NaNobj;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * The global method escape, as per ECMA-262 15.1.2.4.\r
+\r
+     * Includes code for the 'mask' argument supported by the C escape\r
+     * method, which used to be part of the browser imbedding.  Blame\r
+     * for the strange constant names should be directed there.\r
+     */\r
+\r
+    private Object js_escape(Context cx, Object[] args) {\r
+        final int\r
+            URL_XALPHAS = 1,\r
+            URL_XPALPHAS = 2,\r
+            URL_PATH = 4;\r
+\r
+        String s = ScriptRuntime.toString(args, 0);\r
+\r
+        int mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;\r
+        if (args.length > 1) { // the 'mask' argument.  Non-ECMA.\r
+            double d = ScriptRuntime.toNumber(args[1]);\r
+            if (d != d || ((mask = (int) d) != d) ||\r
+                0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)))\r
+            {\r
+                String message = Context.getMessage0("msg.bad.esc.mask");\r
+                cx.reportError(message);\r
+                // do the ecma thing, in case reportError returns.\r
+                mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;\r
+            }\r
+        }\r
+\r
+        StringBuffer R = new StringBuffer();\r
+        for (int k = 0; k < s.length(); k++) {\r
+            int c = s.charAt(k), d;\r
+            if (mask != 0 && ((c >= '0' && c <= '9') ||\r
+                (c >= 'A' && c <= 'Z') ||\r
+                (c >= 'a' && c <= 'z') ||\r
+                c == '@' || c == '*' || c == '_' ||\r
+                c == '-' || c == '.' ||\r
+                ((c == '/' || c == '+') && mask > 3)))\r
+                R.append((char)c);\r
+            else if (c < 256) {\r
+                if (c == ' ' && mask == URL_XPALPHAS) {\r
+                    R.append('+');\r
+                } else {\r
+                    R.append('%');\r
+                    R.append(hex_digit_to_char(c >>> 4));\r
+                    R.append(hex_digit_to_char(c & 0xF));\r
+                }\r
+            } else {\r
+                R.append('%');\r
+                R.append('u');\r
+                R.append(hex_digit_to_char(c >>> 12));\r
+                R.append(hex_digit_to_char((c & 0xF00) >>> 8));\r
+                R.append(hex_digit_to_char((c & 0xF0) >>> 4));\r
+                R.append(hex_digit_to_char(c & 0xF));\r
+            }\r
+        }\r
+        return R.toString();\r
+    }\r
+\r
+    private static char hex_digit_to_char(int x) {\r
+        return (char)(x <= 9 ? x + '0' : x + ('A' - 10));\r
+    }\r
+\r
+    /**\r
+     * The global unescape method, as per ECMA-262 15.1.2.5.\r
+     */\r
+\r
+    private Object js_unescape(Context cx, Object[] args)\r
+    {\r
+        String s = ScriptRuntime.toString(args, 0);\r
+        int firstEscapePos = s.indexOf('%');\r
+        if (firstEscapePos >= 0) {\r
+            int L = s.length();\r
+            char[] buf = s.toCharArray();\r
+            int destination = firstEscapePos;\r
+            for (int k = firstEscapePos; k != L;) {\r
+                char c = buf[k];\r
+                ++k;\r
+                if (c == '%' && k != L) {\r
+                    int end, start;\r
+                    if (buf[k] == 'u') {\r
+                        start = k + 1;\r
+                        end = k + 5;\r
+                    } else {\r
+                        start = k;\r
+                        end = k + 2;\r
+                    }\r
+                    if (end <= L) {\r
+                        int x = 0;\r
+                        for (int i = start; i != end; ++i) {\r
+                            x = (x << 4) | TokenStream.xDigitToInt(buf[i]);\r
+                        }\r
+                        if (x >= 0) {\r
+                            c = (char)x;\r
+                            k = end;\r
+                        }\r
+                    }\r
+                }\r
+                buf[destination] = c;\r
+                ++destination;\r
+            }\r
+            s = new String(buf, 0, destination); \r
+        }\r
+        return s;\r
+    }\r
+\r
+    /**\r
+     * The global method isNaN, as per ECMA-262 15.1.2.6.\r
+     */\r
+\r
+    private Object js_isNaN(Context cx, Object[] args) {\r
+        if (args.length < 1)\r
+            return Boolean.TRUE;\r
+        double d = ScriptRuntime.toNumber(args[0]);\r
+        return (d != d) ? Boolean.TRUE : Boolean.FALSE;\r
+    }\r
+\r
+    private Object js_isFinite(Context cx, Object[] args) {\r
+        if (args.length < 1)\r
+            return Boolean.FALSE;\r
+        double d = ScriptRuntime.toNumber(args[0]);\r
+        return (d != d || d == Double.POSITIVE_INFINITY ||\r
+                d == Double.NEGATIVE_INFINITY)\r
+               ? Boolean.FALSE\r
+               : Boolean.TRUE;\r
+    }\r
+\r
+    private Object js_eval(Context cx, Scriptable scope, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        String m = ScriptRuntime.getMessage1("msg.cant.call.indirect", "eval");\r
+        throw NativeGlobal.constructError(cx, "EvalError", m, scope);\r
+    }\r
+\r
+    /**\r
+     * The eval function property of the global object.\r
+     *\r
+     * See ECMA 15.1.2.1\r
+     */\r
+    public static Object evalSpecial(Context cx, Scriptable scope,\r
+                                     Object thisArg, Object[] args,\r
+                                     String filename, int lineNumber)\r
+        throws JavaScriptException\r
+    {\r
+        throw NativeGlobal.constructError(cx, "EvalError", "XWT does not allow eval()", scope);\r
+        /*\r
+        if (args.length < 1)\r
+            return Undefined.instance;\r
+        Object x = args[0];\r
+        if (!(x instanceof String)) {\r
+            String message = Context.getMessage0("msg.eval.nonstring");\r
+            Context.reportWarning(message);\r
+            return x;\r
+        }\r
+        int[] linep = { lineNumber };\r
+        if (filename == null) {\r
+            filename = Context.getSourcePositionFromStack(linep);\r
+            if (filename == null) {\r
+                filename = "";\r
+                linep[0] = 1;\r
+            } \r
+        } \r
+        filename += "(eval)";\r
+\r
+        try {\r
+            StringReader in = new StringReader((String) x);\r
+            Object securityDomain = cx.getSecurityDomainForStackDepth(3);\r
+\r
+            // Compile the reader with opt level of -1 to force interpreter\r
+            // mode.\r
+            int oldOptLevel = cx.getOptimizationLevel();\r
+            cx.setOptimizationLevel(-1);\r
+            Script script = cx.compileReader(scope, in, filename, linep[0],\r
+                                             securityDomain);\r
+            cx.setOptimizationLevel(oldOptLevel);\r
+\r
+            // if the compile fails, an error has been reported by the\r
+            // compiler, but we need to stop execution to avoid\r
+            // infinite looping on while(true) { eval('foo bar') } -\r
+            // so we throw an EvaluatorException.\r
+            if (script == null) {\r
+                String message = Context.getMessage0("msg.syntax");\r
+                throw new EvaluatorException(message);\r
+            }\r
+\r
+            InterpretedScript is = (InterpretedScript) script;\r
+            is.itsData.itsFromEvalCode = true;\r
+            Object result = is.call(cx, scope, (Scriptable) thisArg, null);\r
+\r
+            return result;\r
+        }\r
+        catch (IOException ioe) {\r
+            // should never happen since we just made the Reader from a String\r
+            throw new RuntimeException("unexpected io exception");\r
+        }\r
+        */\r
+    }\r
+\r
+\r
+    /**\r
+     * The NativeError functions\r
+     *\r
+     * See ECMA 15.11.6\r
+     */\r
+    public static EcmaError constructError(Context cx,\r
+                                           String error,\r
+                                           String message,\r
+                                           Object scope)\r
+    {\r
+        int[] linep = { 0 };\r
+        String filename = cx.getSourcePositionFromStack(linep);\r
+        return constructError(cx, error, message, scope,\r
+                              filename, linep[0], 0, null);\r
+    }\r
+\r
+    static EcmaError typeError0(String messageId, Object scope) {\r
+        return constructError(Context.getContext(), "TypeError",\r
+            ScriptRuntime.getMessage0(messageId), scope);\r
+    }\r
+\r
+    static EcmaError typeError1(String messageId, Object arg1, Object scope) {\r
+        return constructError(Context.getContext(), "TypeError",\r
+            ScriptRuntime.getMessage1(messageId, arg1), scope);\r
+    }\r
+\r
+    /**\r
+     * The NativeError functions\r
+     *\r
+     * See ECMA 15.11.6\r
+     */\r
+    public static EcmaError constructError(Context cx,\r
+                                           String error,\r
+                                           String message,\r
+                                           Object scope,\r
+                                           String sourceName,\r
+                                           int lineNumber,\r
+                                           int columnNumber,\r
+                                           String lineSource)\r
+    {\r
+        Scriptable scopeObject;\r
+        try {\r
+            scopeObject = (Scriptable) scope;\r
+        }\r
+        catch (ClassCastException x) {\r
+            throw new RuntimeException(x.toString());\r
+        }\r
+\r
+        Object args[] = { message };\r
+        try {\r
+            Object errorObject = cx.newObject(scopeObject, error, args);\r
+            return new EcmaError((NativeError)errorObject, sourceName,\r
+                                 lineNumber, columnNumber, lineSource);\r
+        }\r
+        catch (PropertyException x) {\r
+            throw new RuntimeException(x.toString());\r
+        }\r
+        catch (JavaScriptException x) {\r
+            throw new RuntimeException(x.toString());\r
+        }\r
+        catch (NotAFunctionException x) {\r
+            throw new RuntimeException(x.toString());\r
+        }\r
+    }\r
+\r
+    /**\r
+     * The implementation of all the ECMA error constructors (SyntaxError,\r
+     * TypeError, etc.)\r
+     */\r
+    private Object new_CommonError(IdFunction ctorObj, Context cx,\r
+                                   Scriptable scope, Object[] args)\r
+    {\r
+        Scriptable newInstance = new NativeError();\r
+        newInstance.setPrototype((Scriptable)(ctorObj.get("prototype", ctorObj)));\r
+        newInstance.setParentScope(scope);\r
+        if (args.length > 0)\r
+            newInstance.put("message", newInstance, args[0]);\r
+        return newInstance;\r
+    }\r
+\r
+    /*\r
+    *   ECMA 3, 15.1.3 URI Handling Function Properties\r
+    *\r
+    *   The following are implementations of the algorithms\r
+    *   given in the ECMA specification for the hidden functions\r
+    *   'Encode' and 'Decode'.\r
+    */\r
+    private static String encode(Context cx, String str, String unescapedSet) {\r
+        int j, k = 0, L;\r
+        char C, C2;\r
+        int V;\r
+        char utf8buf[] = new char[6];\r
+        StringBuffer R;\r
+\r
+        R = new StringBuffer();\r
+\r
+        while (k < str.length()) {\r
+            C = str.charAt(k);\r
+            if (unescapedSet.indexOf(C) != -1) {\r
+                R.append(C);\r
+            } else {\r
+                if ((C >= 0xDC00) && (C <= 0xDFFF)) {\r
+                    throw cx.reportRuntimeError0("msg.bad.uri");\r
+                }\r
+                if ((C < 0xD800) || (C > 0xDBFF))\r
+                    V = C;\r
+                else {\r
+                    k++;\r
+                    if (k == str.length()) {\r
+                        throw cx.reportRuntimeError0("msg.bad.uri");\r
+                    }\r
+                    C2 = str.charAt(k);\r
+                    if ((C2 < 0xDC00) || (C2 > 0xDFFF)) {\r
+                        throw cx.reportRuntimeError0("msg.bad.uri");\r
+                    }\r
+                    V = ((C - 0xD800) << 10) + (C2 - 0xDC00) + 0x10000;\r
+                }\r
+                L = oneUcs4ToUtf8Char(utf8buf, V);\r
+                for (j = 0; j < L; j++) {\r
+                    R.append('%');\r
+                    if (utf8buf[j] < 16)\r
+                        R.append('0');\r
+                    R.append(Integer.toHexString(utf8buf[j]));\r
+                }\r
+            }\r
+            k++;\r
+        }\r
+        return R.toString();\r
+    }\r
+\r
+    private static boolean isHex(char c) {\r
+        return ((c >= '0' && c <= '9')\r
+                || (c >= 'a' && c <= 'f')\r
+                || (c >= 'A' && c <= 'F'));\r
+    }\r
+\r
+    private static int unHex(char c) {\r
+        if (c >= '0' && c <= '9')\r
+            return c - '0';\r
+        else\r
+            if (c >= 'a' && c <= 'f')\r
+                return c - 'a' + 10;\r
+            else\r
+                return c - 'A' +10;\r
+    }\r
+\r
+    private static String decode(Context cx, String str, String reservedSet) {\r
+        int start, k = 0;\r
+        char C, H;\r
+        int V;\r
+        int B;\r
+        char[] octets = new char[6];\r
+        StringBuffer R;\r
+        int j, n;\r
+\r
+        R = new StringBuffer();\r
+\r
+        while (k < str.length()) {\r
+            C = str.charAt(k);\r
+            if (C == '%') {\r
+                start = k;\r
+                if ((k + 2) >= str.length())\r
+                    throw cx.reportRuntimeError0("msg.bad.uri");\r
+                if (!isHex(str.charAt(k + 1)) || !isHex(str.charAt(k + 2)))\r
+                    throw cx.reportRuntimeError0("msg.bad.uri");\r
+                B = unHex(str.charAt(k + 1)) * 16 + unHex(str.charAt(k + 2));\r
+                k += 2;\r
+                if ((B & 0x80) == 0)\r
+                    C = (char)B;\r
+                else {\r
+                    n = 1;\r
+                    while ((B & (0x80 >>> n)) != 0) n++;\r
+                    if ((n == 1) || (n > 6))\r
+                        throw cx.reportRuntimeError0("msg.bad.uri");\r
+                    octets[0] = (char)B;\r
+                    if ((k + 3 * (n - 1)) >= str.length())\r
+                        throw cx.reportRuntimeError0("msg.bad.uri");\r
+                    for (j = 1; j < n; j++) {\r
+                        k++;\r
+                        if (str.charAt(k) != '%')\r
+                            throw cx.reportRuntimeError0("msg.bad.uri");\r
+                        if (!isHex(str.charAt(k + 1))\r
+                            || !isHex(str.charAt(k + 2)))\r
+                            throw cx.reportRuntimeError0("msg.bad.uri");\r
+                        B = unHex(str.charAt(k + 1)) * 16\r
+                            + unHex(str.charAt(k + 2));\r
+                        if ((B & 0xC0) != 0x80)\r
+                            throw cx.reportRuntimeError0("msg.bad.uri");\r
+                        k += 2;\r
+                        octets[j] = (char)B;\r
+                    }\r
+                    V = utf8ToOneUcs4Char(octets, n);\r
+                    if (V >= 0x10000) {\r
+                        V -= 0x10000;\r
+                        if (V > 0xFFFFF)\r
+                            throw cx.reportRuntimeError0("msg.bad.uri");\r
+                        C = (char)((V & 0x3FF) + 0xDC00);\r
+                        H = (char)((V >>> 10) + 0xD800);\r
+                        R.append(H);\r
+                    }\r
+                    else\r
+                        C = (char)V;\r
+                }\r
+                if (reservedSet.indexOf(C) != -1) {\r
+                    for (int x = 0; x < (k - start + 1); x++)\r
+                        R.append(str.charAt(start + x));\r
+                }\r
+                else\r
+                    R.append(C);\r
+            }\r
+            else\r
+                R.append(C);\r
+            k++;\r
+        }\r
+        return R.toString();\r
+    }\r
+\r
+    private static String uriReservedPlusPound = ";/?:@&=+$,#";\r
+    private static String uriUnescaped =\r
+                                        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.!~*'()";\r
+\r
+    private String js_decodeURI(Context cx, Object[] args) {\r
+        String str = ScriptRuntime.toString(args, 0);\r
+        return decode(cx, str, uriReservedPlusPound);\r
+    }\r
+\r
+    private String js_decodeURIComponent(Context cx, Object[] args) {\r
+        String str = ScriptRuntime.toString(args, 0);\r
+        return decode(cx, str, "");\r
+    }\r
+\r
+    private Object js_encodeURI(Context cx, Object[] args) {\r
+        String str = ScriptRuntime.toString(args, 0);\r
+        return encode(cx, str, uriReservedPlusPound + uriUnescaped);\r
+    }\r
+\r
+    private String js_encodeURIComponent(Context cx, Object[] args) {\r
+        String str = ScriptRuntime.toString(args, 0);\r
+        return encode(cx, str, uriUnescaped);\r
+    }\r
+\r
+    /* Convert one UCS-4 char and write it into a UTF-8 buffer, which must be\r
+    * at least 6 bytes long.  Return the number of UTF-8 bytes of data written.\r
+    */\r
+    private static int oneUcs4ToUtf8Char(char[] utf8Buffer, int ucs4Char) {\r
+        int utf8Length = 1;\r
+\r
+        //JS_ASSERT(ucs4Char <= 0x7FFFFFFF);\r
+        if ((ucs4Char & ~0x7F) == 0)\r
+            utf8Buffer[0] = (char)ucs4Char;\r
+        else {\r
+            int i;\r
+            int a = ucs4Char >>> 11;\r
+            utf8Length = 2;\r
+            while (a != 0) {\r
+                a >>>= 5;\r
+                utf8Length++;\r
+            }\r
+            i = utf8Length;\r
+            while (--i > 0) {\r
+                utf8Buffer[i] = (char)((ucs4Char & 0x3F) | 0x80);\r
+                ucs4Char >>>= 6;\r
+            }\r
+            utf8Buffer[0] = (char)(0x100 - (1 << (8-utf8Length)) + ucs4Char);\r
+        }\r
+        return utf8Length;\r
+    }\r
+\r
+\r
+    /* Convert a utf8 character sequence into a UCS-4 character and return that\r
+    * character.  It is assumed that the caller already checked that the sequence is valid.\r
+    */\r
+    private static int utf8ToOneUcs4Char(char[] utf8Buffer, int utf8Length) {\r
+        int ucs4Char;\r
+        int k = 0;\r
+        //JS_ASSERT(utf8Length >= 1 && utf8Length <= 6);\r
+        if (utf8Length == 1) {\r
+            ucs4Char = utf8Buffer[0];\r
+            //            JS_ASSERT(!(ucs4Char & 0x80));\r
+        } else {\r
+            //JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == (0x100 - (1 << (8-utf8Length))));\r
+            ucs4Char = utf8Buffer[k++] & ((1<<(7-utf8Length))-1);\r
+            while (--utf8Length > 0) {\r
+                //JS_ASSERT((*utf8Buffer & 0xC0) == 0x80);\r
+                ucs4Char = ucs4Char<<6 | (utf8Buffer[k++] & 0x3F);\r
+            }\r
+        }\r
+        return ucs4Char;\r
+    }\r
+\r
+    private static final int\r
+        Id_decodeURI           =  1,\r
+        Id_decodeURIComponent  =  2,\r
+        Id_encodeURI           =  3,\r
+        Id_encodeURIComponent  =  4,\r
+        Id_escape              =  5,\r
+        Id_eval                =  6,\r
+        Id_isFinite            =  7,\r
+        Id_isNaN               =  8,\r
+        Id_parseFloat          =  9,\r
+        Id_parseInt            = 10,\r
+        Id_unescape            = 11,\r
+\r
+        LAST_SCOPE_FUNCTION_ID = 11,\r
+\r
+        Id_new_CommonError     = 12;\r
+\r
+    private boolean scopeSlaveFlag;\r
+\r
+}\r
diff --git a/src/org/mozilla/javascript/NativeJavaArray.java b/src/org/mozilla/javascript/NativeJavaArray.java
new file mode 100644 (file)
index 0000000..42e2d2e
--- /dev/null
@@ -0,0 +1,155 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Frank Mitchell\r
+ * Mike Shaver\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.lang.reflect.Array;\r
+\r
+/**\r
+ * This class reflects Java arrays into the JavaScript environment.\r
+ *\r
+ * @author Mike Shaver\r
+ * @see NativeJavaClass\r
+ * @see NativeJavaObject\r
+ * @see NativeJavaPackage\r
+ */\r
+\r
+public class NativeJavaArray extends NativeJavaObject {\r
+\r
+    public String getClassName() {\r
+        return "JavaArray";\r
+    }\r
+\r
+    public static NativeJavaArray wrap(Scriptable scope, Object array) {\r
+        return new NativeJavaArray(scope, array);\r
+    }\r
+\r
+    public Object unwrap() {\r
+        return array;\r
+    }\r
+\r
+    public NativeJavaArray(Scriptable scope, Object array) {\r
+        super(scope, null, ScriptRuntime.ObjectClass);\r
+        Class cl = array.getClass();\r
+        if (!cl.isArray()) {\r
+            throw new RuntimeException("Array expected");\r
+        }\r
+        this.array = array;\r
+        this.length = Array.getLength(array);\r
+        this.cls = cl.getComponentType();\r
+    }\r
+\r
+    public boolean has(String id, Scriptable start) {\r
+        return id.equals("length") || super.has(id, start);\r
+    }\r
+\r
+    public boolean has(int index, Scriptable start) {\r
+        return 0 <= index && index < length;\r
+    }\r
+\r
+    public Object get(String id, Scriptable start) {\r
+        if (id.equals("length"))\r
+            return new Integer(length);\r
+        Object result = super.get(id, start);\r
+        if (result == NOT_FOUND && \r
+            !ScriptRuntime.hasProp(getPrototype(), id)) \r
+        {\r
+            throw Context.reportRuntimeError2(\r
+                "msg.java.member.not.found", array.getClass().getName(), id);\r
+        }\r
+        return result;  \r
+    }\r
+\r
+    public Object get(int index, Scriptable start) {\r
+        if (0 <= index && index < length)\r
+            return NativeJavaObject.wrap(this, Array.get(array, index), cls);\r
+        return Undefined.instance;\r
+    }\r
+\r
+    public void put(String id, Scriptable start, Object value) {\r
+        // Ignore assignments to "length"--it's readonly.\r
+        if (!id.equals("length"))\r
+            super.put(id, start, value);\r
+    }\r
+    \r
+    public void put(int index, Scriptable start, Object value) {\r
+        if (0 <= index && index < length) {\r
+            Array.set(array, index, NativeJavaObject.coerceType(cls, value));\r
+            return;\r
+        }\r
+        super.put(index, start, value);\r
+    }\r
+\r
+    public Object getDefaultValue(Class hint) {\r
+        if (hint == null || hint == ScriptRuntime.StringClass) \r
+            return array.toString();\r
+        if (hint == ScriptRuntime.BooleanClass)\r
+            return Boolean.TRUE;\r
+        if (hint == ScriptRuntime.NumberClass)\r
+            return ScriptRuntime.NaNobj;\r
+        return this;\r
+    }\r
+    \r
+    public Object[] getIds() {\r
+        Object[] result = new Object[length];\r
+        int i = length;\r
+        while (--i >= 0)\r
+            result[i] = new Integer(i);\r
+        return result;\r
+    }\r
+\r
+    public boolean hasInstance(Scriptable value) {\r
+        if (!(value instanceof Wrapper))\r
+            return false;\r
+        Object instance = ((Wrapper)value).unwrap();\r
+        return cls.isInstance(instance);\r
+    }\r
+\r
+    public Scriptable getPrototype() {\r
+        if (prototype == null) {\r
+            prototype = \r
+                ScriptableObject.getClassPrototype(this.getParentScope(),\r
+                                                   "Array");\r
+        }\r
+        return prototype;\r
+    }\r
+\r
+    Object array;\r
+    int length;\r
+    Class cls;\r
+    Scriptable prototype;\r
+}\r
diff --git a/src/org/mozilla/javascript/NativeJavaClass.java b/src/org/mozilla/javascript/NativeJavaClass.java
new file mode 100644 (file)
index 0000000..8b46c94
--- /dev/null
@@ -0,0 +1,275 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Frank Mitchell\r
+ * Mike Shaver\r
+ * Kurt Westerfeld\r
+ * \r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.lang.reflect.*;\r
+import java.util.Hashtable;\r
+\r
+/**\r
+ * This class reflects Java classes into the JavaScript environment, mainly\r
+ * for constructors and static members.  We lazily reflect properties,\r
+ * and currently do not guarantee that a single j.l.Class is only\r
+ * reflected once into the JS environment, although we should.\r
+ * The only known case where multiple reflections\r
+ * are possible occurs when a j.l.Class is wrapped as part of a\r
+ * method return or property access, rather than by walking the\r
+ * Packages/java tree.\r
+ *\r
+ * @author Mike Shaver\r
+ * @see NativeJavaArray\r
+ * @see NativeJavaObject\r
+ * @see NativeJavaPackage\r
+ */\r
+\r
+public class NativeJavaClass extends NativeJavaObject implements Function {\r
+\r
+    public NativeJavaClass(Scriptable scope, Class cl) {\r
+        super(scope, cl, JavaMembers.lookupClass(scope, cl, cl));\r
+        fieldAndMethods = members.getFieldAndMethodsObjects(this, javaObject, \r
+                                                            true);\r
+    }\r
+\r
+    public String getClassName() {\r
+        return "JavaClass";\r
+    }\r
+    \r
+    public boolean has(String name, Scriptable start) {\r
+        return members.has(name, true);\r
+    }\r
+        \r
+    public Object get(String name, Scriptable start) {\r
+        // When used as a constructor, ScriptRuntime.newObject() asks\r
+        // for our prototype to create an object of the correct type.\r
+        // We don't really care what the object is, since we're returning\r
+        // one constructed out of whole cloth, so we return null.\r
+\r
+        if (name.equals("prototype"))\r
+            return null;\r
+        \r
+        Object result = Scriptable.NOT_FOUND;\r
+        \r
+        if (fieldAndMethods != null) {\r
+            result = fieldAndMethods.get(name);\r
+            if (result != null)\r
+                return result;\r
+        }\r
+        \r
+        if (members.has(name, true)) {\r
+            result = members.get(this, name, javaObject, true);\r
+        } else {\r
+            // experimental:  look for nested classes by appending $name to current class' name.\r
+            try {\r
+                String nestedName = getClassObject().getName() + '$' + name;\r
+                Class nestedClass = ScriptRuntime.loadClassName(nestedName);\r
+                Scriptable nestedValue = wrap(ScriptableObject.getTopLevelScope(this), nestedClass);\r
+                nestedValue.setParentScope(this);\r
+                result = nestedValue;\r
+            } catch (ClassNotFoundException ex) {\r
+                throw members.reportMemberNotFound(name);\r
+            } catch (IllegalArgumentException e) {\r
+                throw members.reportMemberNotFound(name);\r
+            }\r
+        }\r
+               \r
+       return result;\r
+    }\r
+\r
+    public void put(String name, Scriptable start, Object value) {\r
+        members.put(this, name, javaObject, value, true);\r
+    }\r
+\r
+    public Object[] getIds() {\r
+        return members.getIds(true);\r
+    }\r
+    \r
+    public Class getClassObject() { \r
+        return (Class) super.unwrap();\r
+    }\r
+\r
+    // XXX ??\r
+    public static NativeJavaClass wrap(Scriptable scope, Class cls) {\r
+        return new NativeJavaClass(scope, cls);\r
+    }\r
+\r
+    public Object getDefaultValue(Class hint) {\r
+        if (hint == null || hint == ScriptRuntime.StringClass)\r
+            return this.toString();\r
+        if (hint == ScriptRuntime.BooleanClass)\r
+            return Boolean.TRUE;\r
+        if (hint == ScriptRuntime.NumberClass)\r
+            return ScriptRuntime.NaNobj;\r
+        return this;\r
+    }\r
+\r
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj,\r
+                       Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        // If it looks like a "cast" of an object to this class type,\r
+        // walk the prototype chain to see if there's a wrapper of a \r
+        // object that's an instanceof this class.\r
+        if (args.length == 1 && args[0] instanceof Scriptable) {\r
+            Class c = getClassObject();\r
+            Scriptable p = (Scriptable) args[0];\r
+            do {\r
+                if (p instanceof Wrapper) {\r
+                    Object o = ((Wrapper) p).unwrap();\r
+                    if (c.isInstance(o))\r
+                        return p;\r
+                }\r
+                p = p.getPrototype();\r
+            } while (p != null);\r
+        }\r
+        return construct(cx, scope, args);\r
+    }\r
+\r
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+       Class classObject = getClassObject();\r
+       int modifiers = classObject.getModifiers();\r
+       if (! (Modifier.isInterface(modifiers) || \r
+               Modifier.isAbstract(modifiers))) \r
+        {\r
+               Constructor[] ctors = members.getConstructors();\r
+               Member member = NativeJavaMethod.findFunction(ctors, args);\r
+               Constructor ctor = (Constructor) member;\r
+               if (ctor == null) {\r
+                   String sig = NativeJavaMethod.scriptSignature(args);\r
+                throw Context.reportRuntimeError2(\r
+                    "msg.no.java.ctor", classObject.getName(), sig);\r
+               }\r
+\r
+               // Found the constructor, so try invoking it.\r
+                return NativeJavaClass.constructSpecific(cx, scope, \r
+                                                         this, ctor, args);\r
+        } else {\r
+                Scriptable topLevel = ScriptableObject.getTopLevelScope(this);\r
+                String msg = "";\r
+                try {\r
+                    // trying to construct an interface; use JavaAdapter to \r
+                    // construct a new class on the fly that implements this \r
+                    // interface.\r
+                    Object v = topLevel.get("JavaAdapter", topLevel);\r
+                    if (v != NOT_FOUND) {\r
+                        Function f = (Function) v;\r
+                        Object[] adapterArgs = { this, args[0] };\r
+                        return (Scriptable) f.construct(cx, topLevel, \r
+                                                        adapterArgs);\r
+                    }\r
+                } catch (Exception ex) {\r
+                    // fall through to error\r
+                    String m = ex.getMessage();\r
+                    if (m != null)\r
+                        msg = m;\r
+                }\r
+                  throw Context.reportRuntimeError2(\r
+                      "msg.cant.instantiate", msg, classObject.getName());\r
+        }\r
+    }\r
+\r
+    public static Scriptable constructSpecific(Context cx, \r
+                                               Scriptable scope, \r
+                                               Scriptable thisObj, \r
+                                               Constructor ctor,\r
+                                               Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        Scriptable topLevel = ScriptableObject.getTopLevelScope(thisObj);\r
+        Class classObject = ctor.getDeclaringClass();\r
+\r
+        Class[] paramTypes = ctor.getParameterTypes();\r
+        for (int i = 0; i < args.length; i++) {\r
+            args[i] = NativeJavaObject.coerceType(paramTypes[i], args[i]);\r
+        }\r
+        try {\r
+            // we need to force this to be wrapped, because construct _has_\r
+            // to return a scriptable \r
+            return \r
+                (Scriptable) NativeJavaObject.wrap(topLevel, \r
+                                                   ctor.newInstance(args),\r
+                                                   classObject);\r
+\r
+        } catch (InstantiationException instEx) {\r
+            throw Context.reportRuntimeError2(\r
+                "msg.cant.instantiate", \r
+                instEx.getMessage(), classObject.getName());\r
+        } catch (IllegalArgumentException argEx) {\r
+            String signature = NativeJavaMethod.scriptSignature(args);\r
+            String ctorString = ctor.toString();\r
+            throw Context.reportRuntimeError3(\r
+                "msg.bad.ctor.sig", argEx.getMessage(), ctorString, signature);\r
+        } catch (InvocationTargetException e) {\r
+            throw JavaScriptException.wrapException(scope, e);\r
+        } catch (IllegalAccessException accessEx) {\r
+            throw Context.reportRuntimeError1(\r
+                "msg.java.internal.private", accessEx.getMessage());\r
+        }\r
+    }\r
+\r
+    public String toString() {\r
+        return "[JavaClass " + getClassObject().getName() + "]";\r
+    }\r
+\r
+    /**\r
+     * Determines if prototype is a wrapped Java object and performs\r
+     * a Java "instanceof".\r
+     * Exception: if value is an instance of NativeJavaClass, it isn't \r
+     * considered an instance of the Java class; this forestalls any \r
+     * name conflicts between java.lang.Class's methods and the \r
+     * static methods exposed by a JavaNativeClass.\r
+     */\r
+    public boolean hasInstance(Scriptable value) {\r
+\r
+        if (value instanceof Wrapper && \r
+            !(value instanceof NativeJavaClass)) {\r
+            Object instance = ((Wrapper)value).unwrap();\r
+\r
+            return getClassObject().isInstance(instance);\r
+        }\r
+\r
+        // value wasn't something we understand\r
+        return false;\r
+    }\r
+\r
+    private Hashtable fieldAndMethods;\r
+\r
+    // beard: need a scope for finding top-level prototypes.\r
+    private Scriptable parent;\r
+}\r
diff --git a/src/org/mozilla/javascript/NativeJavaConstructor.java b/src/org/mozilla/javascript/NativeJavaConstructor.java
new file mode 100644 (file)
index 0000000..4f2fbfc
--- /dev/null
@@ -0,0 +1,86 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Frank Mitchell\r
+ * Mike Shaver\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.lang.reflect.*;\r
+\r
+/**\r
+ * This class reflects a single Java constructor into the JavaScript \r
+ * environment.  It satisfies a request for an overloaded constructor,\r
+ * as introduced in LiveConnect 3.\r
+ * All NativeJavaConstructors behave as JSRef `bound' methods, in that they\r
+ * always construct the same NativeJavaClass regardless of any reparenting \r
+ * that may occur.\r
+ *\r
+ * @author Frank Mitchell\r
+ * @see NativeJavaMethod\r
+ * @see NativeJavaPackage\r
+ * @see NativeJavaClass\r
+ */\r
+\r
+public class NativeJavaConstructor extends NativeFunction implements Function {\r
+\r
+    public NativeJavaConstructor(Constructor ctor) {\r
+        this.constructor = ctor;\r
+        this.functionName = "<init>" + NativeJavaMethod.signature(ctor);\r
+    }\r
+\r
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj,\r
+                       Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        // Find a method that matches the types given.\r
+        if (constructor == null) {\r
+            throw new RuntimeException("No constructor defined for call");\r
+        }\r
+\r
+        return NativeJavaClass.constructSpecific(cx, scope, \r
+                                                 this, constructor, args);\r
+    }\r
+\r
+    public String toString() {\r
+        return "[JavaConstructor " + constructor.getName() + "]";\r
+    }\r
+\r
+    Constructor getConstructor() {\r
+        return constructor; \r
+    }\r
+\r
+    Constructor constructor;\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/NativeJavaMethod.java b/src/org/mozilla/javascript/NativeJavaMethod.java
new file mode 100644 (file)
index 0000000..8d3e88d
--- /dev/null
@@ -0,0 +1,517 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Frank Mitchell\r
+ * Mike Shaver\r
+ * \r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.lang.reflect.*;\r
+\r
+/**\r
+ * This class reflects Java methods into the JavaScript environment.  It\r
+ * handles overloading of methods, and method/field name conflicts.\r
+ * All NativeJavaMethods behave as JSRef `bound' methods, in that they\r
+ * always operate on the object underlying the original NativeJavaObject\r
+ * parent regardless of any reparenting that may occur.\r
+ *\r
+ * @author Mike Shaver\r
+ * @see NativeJavaArray\r
+ * @see NativeJavaPackage\r
+ * @see NativeJavaClass\r
+ */\r
+\r
+public class NativeJavaMethod extends NativeFunction implements Function {\r
+\r
+    public NativeJavaMethod() {\r
+        this.functionName = null;\r
+    }\r
+\r
+    public NativeJavaMethod(Method[] methods) {\r
+        this.methods = methods;\r
+        this.functionName = methods[0].getName();\r
+    }\r
+\r
+    public NativeJavaMethod(Method method, String name) {\r
+        this.methods = new Method[1];\r
+        this.methods[0] = method;\r
+        this.functionName = name;\r
+    }\r
+\r
+    public void add(Method method) {\r
+        if (functionName == null) {\r
+            functionName = method.getName();\r
+        } else if (!functionName.equals(method.getName())) {\r
+            throw new RuntimeException("internal method name mismatch");\r
+        }                \r
+        // XXX a more intelligent growth algorithm would be nice\r
+        int len = methods == null ? 0 : methods.length;\r
+        Method[] newMeths = new Method[len + 1];\r
+        for (int i = 0; i < len; i++)\r
+            newMeths[i] = methods[i];\r
+        newMeths[len] = method;\r
+        methods = newMeths;\r
+    }\r
+\r
+    static String scriptSignature(Object value) {\r
+        if (value == null) {\r
+            return "null";\r
+        }\r
+        else {\r
+            Class type = value.getClass();\r
+            if (type == ScriptRuntime.UndefinedClass)\r
+                return "undefined";\r
+            if (type == ScriptRuntime.BooleanClass)\r
+                return "boolean";\r
+            if (type == ScriptRuntime.StringClass)\r
+                return "string";\r
+            if (ScriptRuntime.NumberClass.isAssignableFrom(type))\r
+                return "number";\r
+            if (value instanceof Wrapper) {\r
+                return ((Wrapper)value).unwrap().getClass().getName();\r
+            }\r
+            if (value instanceof Scriptable) {\r
+                if (value instanceof Function)\r
+                    return "function";\r
+                return "object";\r
+            }\r
+            return javaSignature(type);\r
+        }\r
+    }\r
+\r
+    static String scriptSignature(Object[] values) {\r
+        StringBuffer sig = new StringBuffer();\r
+        for (int i = 0; i < values.length; i++) {\r
+            if (i != 0)\r
+                sig.append(',');\r
+            sig.append(scriptSignature(values[i]));\r
+        }\r
+        return sig.toString();\r
+    }\r
+\r
+    static String javaSignature(Class type) {\r
+        if (type == null) {\r
+            return "null";\r
+        }\r
+        else if (type.isArray()) {\r
+            return javaSignature(type.getComponentType()) + "[]";\r
+        }\r
+        return type.getName();\r
+    }\r
+\r
+    static String javaSignature(Class[] types) {\r
+        StringBuffer sig = new StringBuffer();\r
+        for (int i = 0; i < types.length; i++) {\r
+            if (i != 0)\r
+                sig.append(',');\r
+            sig.append(javaSignature(types[i]));\r
+        }\r
+        return sig.toString();\r
+    }\r
+\r
+    static String signature(Member member) {\r
+        Class paramTypes[];\r
+\r
+        if (member instanceof Method) {\r
+            paramTypes = ((Method) member).getParameterTypes();\r
+            return member.getName() + "(" + javaSignature(paramTypes) + ")";\r
+        }\r
+        else {\r
+            paramTypes = ((Constructor) member).getParameterTypes();\r
+            return "(" + javaSignature(paramTypes) + ")";\r
+        }\r
+    }\r
+    \r
+    public String decompile(Context cx, int indent, boolean justbody) {\r
+        StringBuffer sb = new StringBuffer();\r
+        if (!justbody) {\r
+            sb.append("function ");\r
+            sb.append(getFunctionName());\r
+            sb.append("() {");\r
+        }\r
+        sb.append("/*\n");\r
+        toString(sb);\r
+        sb.append(justbody ? "*/\n" : "*/}\n");\r
+        return sb.toString();\r
+    }\r
+    \r
+    public String toString() {\r
+        StringBuffer sb = new StringBuffer();\r
+        toString(sb);\r
+        return sb.toString();\r
+    }\r
+\r
+    private void toString(StringBuffer sb) {\r
+        for (int i=0; i < methods.length; i++) {\r
+            sb.append(javaSignature(methods[i].getReturnType()));\r
+            sb.append(' ');\r
+            sb.append(signature(methods[i]));\r
+            sb.append('\n');\r
+        }\r
+    }\r
+\r
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj,\r
+                       Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        // Find a method that matches the types given.\r
+        if (methods.length == 0) {\r
+            throw new RuntimeException("No methods defined for call");\r
+        }\r
+\r
+        Method meth = (Method) findFunction(methods, args);\r
+        if (meth == null) {\r
+            Class c = methods[0].getDeclaringClass();\r
+            String sig = c.getName() + "." + functionName + "(" +\r
+                         scriptSignature(args) + ")";\r
+            throw Context.reportRuntimeError1("msg.java.no_such_method", sig);\r
+        }\r
+\r
+        // OPT: already retrieved in findFunction, so we should inline that\r
+        // OPT: or pass it back somehow\r
+        Class paramTypes[] = meth.getParameterTypes();\r
+\r
+        // First, we marshall the args.\r
+        for (int i = 0; i < args.length; i++) {\r
+            args[i] = NativeJavaObject.coerceType(paramTypes[i], args[i]);\r
+        }\r
+        Object javaObject;\r
+        if (Modifier.isStatic(meth.getModifiers())) {\r
+            javaObject = null;  // don't need an object\r
+        } else {\r
+            Scriptable o = thisObj;\r
+            while (!(o instanceof Wrapper)) {\r
+                o = o.getPrototype();\r
+                if (o == null) {\r
+                    throw Context.reportRuntimeError1(\r
+                        "msg.nonjava.method", functionName);\r
+                }\r
+            }\r
+            javaObject = ((Wrapper) o).unwrap();        \r
+        }\r
+        try {\r
+            if (debug) {\r
+                printDebug("Calling ", meth, args);\r
+            }\r
+\r
+            Object retval = meth.invoke(javaObject, args);\r
+            Class staticType = meth.getReturnType();\r
+\r
+            if (debug) {\r
+                Class actualType = (retval == null) ? null : retval.getClass();\r
+                System.err.println(" ----- Returned " + retval + \r
+                                   " actual = " + actualType +\r
+                                   " expect = " + staticType);\r
+            }\r
+\r
+            Object wrapped = NativeJavaObject.wrap(scope, retval, staticType);\r
+\r
+            if (debug) {\r
+                Class actualType = (wrapped == null) ? null : wrapped.getClass();\r
+                System.err.println(" ----- Wrapped as " + wrapped + \r
+                                   " class = " + actualType);\r
+            }\r
+\r
+            if (wrapped == Undefined.instance)\r
+                return wrapped;\r
+            if (wrapped == null && staticType == Void.TYPE)\r
+                return Undefined.instance;\r
+            return wrapped;\r
+        } catch (IllegalAccessException accessEx) {\r
+            throw Context.reportRuntimeError(accessEx.getMessage());\r
+        } catch (InvocationTargetException e) {\r
+            throw JavaScriptException.wrapException(scope, e);\r
+        }\r
+    }\r
+\r
+    /** \r
+     * Find the correct function to call given the set of methods\r
+     * or constructors and the arguments.\r
+     * If no function can be found to call, return null.\r
+     */\r
+    static Member findFunction(Member[] methodsOrCtors, Object[] args) {\r
+        if (methodsOrCtors.length == 0)\r
+            return null;\r
+        boolean hasMethods = methodsOrCtors[0] instanceof Method;\r
+        if (Context.useJSObject && \r
+            NativeJavaObject.jsObjectClass != null) \r
+        {\r
+            try {\r
+                for (int i = 0; i < args.length; i++) {\r
+                    if (NativeJavaObject.jsObjectClass.isInstance(args[i]))\r
+                        args[i] = NativeJavaObject.jsObjectGetScriptable.invoke(\r
+                                    args[i], ScriptRuntime.emptyArgs);\r
+                }\r
+            }\r
+            catch (InvocationTargetException e) {\r
+                // Just abandon conversion from JSObject\r
+            }\r
+            catch (IllegalAccessException e) {\r
+                // Just abandon conversion from JSObject\r
+            }\r
+        }\r
+\r
+        Member  bestFit = null;\r
+        Class[] bestFitTypes = null;\r
+\r
+        java.util.Vector ambiguousMethods = null;\r
+\r
+        for (int i = 0; i < methodsOrCtors.length; i++) {\r
+            Member member = methodsOrCtors[i];\r
+            Class paramTypes[] = hasMethods\r
+                                 ? ((Method) member).getParameterTypes()\r
+                                 : ((Constructor) member).getParameterTypes();\r
+            if (paramTypes.length != args.length) {\r
+                continue;\r
+            }\r
+            if (bestFitTypes == null) {\r
+                int j;\r
+                for (j = 0; j < paramTypes.length; j++) {\r
+                    if (!NativeJavaObject.canConvert(args[j], paramTypes[j])) {\r
+                        if (debug) printDebug("Rejecting (args can't convert) ", \r
+                                              member, args);\r
+                        break;\r
+                    }\r
+                }\r
+                if (j == paramTypes.length) {\r
+                    if (debug) printDebug("Found ", member, args);\r
+                    bestFit = member;\r
+                    bestFitTypes = paramTypes;\r
+                }\r
+            }\r
+            else {\r
+                int preference = \r
+                    NativeJavaMethod.preferSignature(args, \r
+                                                     paramTypes, \r
+                                                     bestFitTypes);\r
+                if (preference == PREFERENCE_AMBIGUOUS) {\r
+                    if (debug) printDebug("Deferring ", member, args);\r
+                    // add to "ambiguity list"\r
+                    if (ambiguousMethods == null)\r
+                        ambiguousMethods = new java.util.Vector();\r
+                    ambiguousMethods.addElement(member);\r
+                }\r
+                else if (preference == PREFERENCE_FIRST_ARG) {\r
+                    if (debug) printDebug("Substituting ", member, args);\r
+                    bestFit = member;\r
+                    bestFitTypes = paramTypes;\r
+                }\r
+                else {\r
+                    if (preference == PREFERENCE_EQUAL &&\r
+                        Modifier.isStatic(bestFit.getModifiers()) &&\r
+                        bestFit.getDeclaringClass().isAssignableFrom(\r
+                            member.getDeclaringClass()))                                          \r
+                    {\r
+                        // On some JVMs, Class.getMethods will return all\r
+                        // static methods of the class heirarchy, even if\r
+                        // a derived class's parameters match exactly.\r
+                        // We want to call the dervied class's method.\r
+                        if (debug) printDebug("Rejecting (overridden static)",\r
+                                              member, args);\r
+                        bestFit = member;\r
+                        bestFitTypes = paramTypes;\r
+                    } else {\r
+                        if (debug) printDebug("Rejecting ", member, args);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        \r
+        if (ambiguousMethods == null)\r
+            return bestFit;\r
+\r
+        // Compare ambiguous methods with best fit, in case \r
+        // the current best fit removes the ambiguities.\r
+        for (int i = ambiguousMethods.size() - 1; i >= 0 ; i--) {\r
+            Member member = (Member)ambiguousMethods.elementAt(i);\r
+            Class paramTypes[] = hasMethods\r
+                                 ? ((Method) member).getParameterTypes()\r
+                                 : ((Constructor) member).getParameterTypes();\r
+            int preference = \r
+                NativeJavaMethod.preferSignature(args, \r
+                                                 paramTypes, \r
+                                                 bestFitTypes);\r
+\r
+            if (preference == PREFERENCE_FIRST_ARG) {\r
+                if (debug) printDebug("Substituting ", member, args);\r
+                bestFit = member;\r
+                bestFitTypes = paramTypes;\r
+                ambiguousMethods.removeElementAt(i);\r
+            }\r
+            else if (preference == PREFERENCE_SECOND_ARG) {\r
+                if (debug) printDebug("Rejecting ", member, args);\r
+                ambiguousMethods.removeElementAt(i);\r
+            }\r
+            else {\r
+                if (debug) printDebug("UNRESOLVED: ", member, args);\r
+            }\r
+        }\r
+\r
+        if (ambiguousMethods.size() > 0) {\r
+            // PENDING: report remaining ambiguity\r
+            StringBuffer buf = new StringBuffer();\r
+            boolean isCtor = (bestFit instanceof Constructor);\r
+\r
+            ambiguousMethods.addElement(bestFit);\r
+\r
+            for (int i = 0; i < ambiguousMethods.size(); i++) {\r
+                if (i != 0) {\r
+                    buf.append(", ");\r
+                }\r
+                Member member = (Member)ambiguousMethods.elementAt(i);\r
+                if (!isCtor) {\r
+                    Class rtnType = ((Method)member).getReturnType();\r
+                    buf.append(rtnType);\r
+                    buf.append(' ');\r
+                }\r
+                buf.append(NativeJavaMethod.signature(member));\r
+            }\r
+\r
+            String errMsg;\r
+            if (isCtor) {\r
+                Object errArgs[] = { \r
+                    bestFit.getName(), \r
+                    NativeJavaMethod.scriptSignature(args),\r
+                    buf.toString()\r
+                };\r
+                errMsg = \r
+                    Context.getMessage("msg.constructor.ambiguous", errArgs);\r
+            }\r
+            else {\r
+                Object errArgs[] = { \r
+                    bestFit.getDeclaringClass().getName(), \r
+                    bestFit.getName(), \r
+                    NativeJavaMethod.scriptSignature(args),\r
+                    buf.toString()\r
+                };\r
+                errMsg = Context.getMessage("msg.method.ambiguous", errArgs);\r
+            }\r
+\r
+            throw \r
+                Context.reportRuntimeError(errMsg);\r
+        }\r
+\r
+        return bestFit;\r
+    }\r
+    \r
+    /** Types are equal */\r
+    static final int PREFERENCE_EQUAL      = 0;\r
+    static final int PREFERENCE_FIRST_ARG  = 1;\r
+    static final int PREFERENCE_SECOND_ARG = 2;\r
+    /** No clear "easy" conversion */\r
+    static final int PREFERENCE_AMBIGUOUS  = 3;\r
+\r
+    /**\r
+     * Determine which of two signatures is the closer fit.\r
+     * Returns one of PREFERENCE_EQUAL, PREFERENCE_FIRST_ARG, \r
+     * PREFERENCE_SECOND_ARG, or PREFERENCE_AMBIGUOUS.\r
+     */\r
+    public static int preferSignature(Object[] args, \r
+                                      Class[] sig1, Class[] sig2) \r
+    {\r
+        int preference = 0;\r
+\r
+        for (int j = 0; j < args.length; j++) {\r
+            Class type1 = sig1[j];\r
+            Class type2 = sig2[j];\r
+\r
+            if (type1 == type2) {\r
+                continue;\r
+            }\r
+\r
+            preference |=\r
+                NativeJavaMethod.preferConversion(args[j], \r
+                                                  type1,\r
+                                                  type2);\r
+\r
+            if (preference == PREFERENCE_AMBIGUOUS) {\r
+                break;\r
+            }\r
+        }\r
+        return preference;\r
+    }\r
+\r
+\r
+    /**\r
+     * Determine which of two types is the easier conversion.\r
+     * Returns one of PREFERENCE_EQUAL, PREFERENCE_FIRST_ARG, \r
+     * PREFERENCE_SECOND_ARG, or PREFERENCE_AMBIGUOUS.\r
+     */\r
+    public static int preferConversion(Object fromObj, \r
+                                       Class toClass1, Class toClass2) {\r
+\r
+        int rank1  = \r
+            NativeJavaObject.getConversionWeight(fromObj, toClass1);\r
+        int rank2 = \r
+            NativeJavaObject.getConversionWeight(fromObj, toClass2);\r
+\r
+        if (rank1 == NativeJavaObject.CONVERSION_NONTRIVIAL && \r
+            rank2 == NativeJavaObject.CONVERSION_NONTRIVIAL) {\r
+\r
+            if (toClass1.isAssignableFrom(toClass2)) {\r
+                return PREFERENCE_SECOND_ARG;\r
+            }\r
+            else if (toClass2.isAssignableFrom(toClass1)) {\r
+                return PREFERENCE_FIRST_ARG;\r
+            }\r
+        }\r
+        else {\r
+            if (rank1 < rank2) {\r
+                return PREFERENCE_FIRST_ARG;\r
+            }\r
+            else if (rank1 > rank2) {\r
+                return PREFERENCE_SECOND_ARG;\r
+            }\r
+        }\r
+        return PREFERENCE_AMBIGUOUS;\r
+    }\r
+\r
+    Method[] getMethods() {\r
+        return methods; \r
+    }\r
+\r
+    private static final boolean debug = false;\r
+\r
+    private static void printDebug(String msg, Member member, Object[] args) {\r
+        if (debug) {\r
+            System.err.println(" ----- " + msg + \r
+                               member.getDeclaringClass().getName() +\r
+                               "." + signature(member) +\r
+                               " for arguments (" + scriptSignature(args) + ")");\r
+        }\r
+    }\r
+\r
+    Method methods[];\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/NativeJavaObject.java b/src/org/mozilla/javascript/NativeJavaObject.java
new file mode 100644 (file)
index 0000000..283252f
--- /dev/null
@@ -0,0 +1,926 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Igor Bukanov\r
+ * Frank Mitchell\r
+ * Mike Shaver\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.lang.reflect.*;\r
+import java.util.Hashtable;\r
+import java.util.Enumeration;\r
+\r
+/**\r
+ * This class reflects non-Array Java objects into the JavaScript environment.  It\r
+ * reflect fields directly, and uses NativeJavaMethod objects to reflect (possibly\r
+ * overloaded) methods.<p>\r
+ *\r
+ * @author Mike Shaver\r
+ * @see NativeJavaArray\r
+ * @see NativeJavaPackage\r
+ * @see NativeJavaClass\r
+ */\r
+\r
+public class NativeJavaObject implements Scriptable, Wrapper {\r
+\r
+    public NativeJavaObject(Scriptable scope, Object javaObject, \r
+                            JavaMembers members) \r
+    {\r
+        this.parent = scope;\r
+        this.javaObject = javaObject;\r
+        this.members = members;\r
+    }\r
+    \r
+    public NativeJavaObject(Scriptable scope, Object javaObject, \r
+                            Class staticType) \r
+    {\r
+        this.parent = scope;\r
+        this.javaObject = javaObject;\r
+        Class dynamicType = javaObject != null ? javaObject.getClass()\r
+            : staticType;\r
+        members = JavaMembers.lookupClass(scope, dynamicType, staticType);\r
+        fieldAndMethods = members.getFieldAndMethodsObjects(this, javaObject, false);\r
+    }\r
+    \r
+    public boolean has(String name, Scriptable start) {\r
+        return members.has(name, false);\r
+    }\r
+        \r
+    public boolean has(int index, Scriptable start) {\r
+        return false;\r
+    }\r
+        \r
+    public Object get(String name, Scriptable start) {\r
+        if (fieldAndMethods != null) {\r
+            Object result = fieldAndMethods.get(name);\r
+            if (result != null) {\r
+                return result;\r
+            }\r
+        }\r
+        // TODO: passing 'this' as the scope is bogus since it has \r
+        //  no parent scope\r
+        return members.get(this, name, javaObject, false);\r
+    }\r
+\r
+    public Object get(int index, Scriptable start) {\r
+        throw members.reportMemberNotFound(Integer.toString(index));\r
+    }\r
+    \r
+    public void put(String name, Scriptable start, Object value) {\r
+        // We could be asked to modify the value of a property in the \r
+        // prototype. Since we can't add a property to a Java object,\r
+        // we modify it in the prototype rather than copy it down.\r
+        if (prototype == null || members.has(name, false))\r
+            members.put(this, name, javaObject, value, false);\r
+        else\r
+            prototype.put(name, prototype, value);\r
+    }\r
+\r
+    public void put(int index, Scriptable start, Object value) {\r
+        throw members.reportMemberNotFound(Integer.toString(index));\r
+    }\r
+\r
+    public boolean hasInstance(Scriptable value) {\r
+        // This is an instance of a Java class, so always return false\r
+        return false;\r
+    }\r
+    \r
+    public void delete(String name) {\r
+    }\r
+    \r
+    public void delete(int index) {\r
+    }\r
+    \r
+    public Scriptable getPrototype() {\r
+       if (prototype == null && javaObject.getClass() == ScriptRuntime.StringClass) {\r
+            return ScriptableObject.getClassPrototype(parent, "String");\r
+        }\r
+        return prototype;\r
+    }\r
+\r
+    /**\r
+     * Sets the prototype of the object.\r
+     */\r
+    public void setPrototype(Scriptable m) {\r
+       prototype = m;\r
+    }\r
+\r
+    /**\r
+     * Returns the parent (enclosing) scope of the object.\r
+     */\r
+    public Scriptable getParentScope() {\r
+        return parent;\r
+    }\r
+\r
+    /**\r
+     * Sets the parent (enclosing) scope of the object.\r
+     */\r
+    public void setParentScope(Scriptable m) {\r
+        parent = m;\r
+    }\r
+\r
+    public Object[] getIds() {\r
+        return members.getIds(false);\r
+    }\r
+    \r
+    public static Object wrap(Scriptable scope, Object obj, Class staticType) \r
+    {\r
+        if (obj == null)\r
+            return obj;\r
+        Context cx = Context.getCurrentContext();\r
+        if (cx != null && cx.wrapHandler != null) {\r
+            Object result = cx.wrapHandler.wrap(scope, obj, staticType);\r
+            if (result != null)\r
+                return result;\r
+        }\r
+        Class cls = obj.getClass();\r
+        if (staticType != null && staticType.isPrimitive()) {\r
+            if (staticType == Void.TYPE)\r
+                return Undefined.instance;\r
+            if (staticType == Character.TYPE)\r
+                return new Integer((int) ((Character) obj).charValue());\r
+            return obj;\r
+        }\r
+        if (cls.isArray())\r
+            return NativeJavaArray.wrap(scope, obj);\r
+        if (obj instanceof Scriptable)\r
+            return obj;\r
+        if (Context.useJSObject && jsObjectClass != null && \r
+            staticType != jsObjectClass && jsObjectClass.isInstance(obj)) \r
+        {\r
+            try {\r
+                return jsObjectGetScriptable.invoke(obj, ScriptRuntime.emptyArgs);\r
+            }\r
+            catch (InvocationTargetException e) {\r
+                // Just abandon conversion from JSObject\r
+            }\r
+            catch (IllegalAccessException e) {\r
+                // Just abandon conversion from JSObject\r
+            }\r
+        }\r
+        return new NativeJavaObject(scope, obj, staticType);\r
+    }\r
+\r
+    public Object unwrap() {\r
+        return javaObject;\r
+    }\r
+\r
+    public String getClassName() {\r
+        return "JavaObject";\r
+    }\r
+\r
+    Function getConverter(String converterName) {\r
+        Object converterFunction = get(converterName, this);\r
+        if (converterFunction instanceof Function) {\r
+            return (Function) converterFunction;\r
+        }\r
+        return null;\r
+    }\r
+\r
+    Object callConverter(Function converterFunction)\r
+        throws JavaScriptException\r
+    {\r
+        Function f = (Function) converterFunction;\r
+        return f.call(Context.getContext(), f.getParentScope(),\r
+                      this, ScriptRuntime.emptyArgs);\r
+    }\r
+\r
+    Object callConverter(String converterName)\r
+        throws JavaScriptException\r
+    {\r
+        Function converter = getConverter(converterName);\r
+        if (converter == null) {\r
+            return javaObject.toString();\r
+        }\r
+        return callConverter(converter);\r
+    }\r
+\r
+    public Object getDefaultValue(Class hint) {\r
+        if (hint == null || hint == ScriptRuntime.StringClass)\r
+            return javaObject.toString();\r
+        try {\r
+            if (hint == ScriptRuntime.BooleanClass)\r
+                return callConverter("booleanValue");\r
+            if (hint == ScriptRuntime.NumberClass) {\r
+                return callConverter("doubleValue");\r
+            }\r
+            // fall through to error message\r
+        } catch (JavaScriptException jse) {\r
+            // fall through to error message\r
+        }\r
+        throw Context.reportRuntimeError0("msg.default.value");\r
+    }\r
+\r
+\r
+    /**\r
+     * Determine whether we can/should convert between the given type and the\r
+     * desired one.  This should be superceded by a conversion-cost calculation\r
+     * function, but for now I'll hide behind precedent.\r
+     */\r
+    public static boolean canConvert(Object fromObj, Class to) {\r
+        int weight = NativeJavaObject.getConversionWeight(fromObj, to);\r
+\r
+        return (weight < CONVERSION_NONE);\r
+    }\r
+\r
+    static final int JSTYPE_UNDEFINED   = 0; // undefined type\r
+    static final int JSTYPE_NULL        = 1; // null\r
+    static final int JSTYPE_BOOLEAN     = 2; // boolean\r
+    static final int JSTYPE_NUMBER      = 3; // number\r
+    static final int JSTYPE_STRING      = 4; // string\r
+    static final int JSTYPE_JAVA_CLASS  = 5; // JavaClass\r
+    static final int JSTYPE_JAVA_OBJECT = 6; // JavaObject\r
+    static final int JSTYPE_JAVA_ARRAY  = 7; // JavaArray\r
+    static final int JSTYPE_OBJECT      = 8; // Scriptable\r
+\r
+    public static final byte CONVERSION_TRIVIAL      = 1;\r
+    public static final byte CONVERSION_NONTRIVIAL   = 0;\r
+    public static final byte CONVERSION_NONE         = 99;\r
+\r
+    /**\r
+     * Derive a ranking based on how "natural" the conversion is.\r
+     * The special value CONVERSION_NONE means no conversion is possible, \r
+     * and CONVERSION_NONTRIVIAL signals that more type conformance testing \r
+     * is required.\r
+     * Based on \r
+     * <a href="http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">\r
+     * "preferred method conversions" from Live Connect 3</a>\r
+     */\r
+    public static int getConversionWeight(Object fromObj, Class to) {\r
+        int fromCode = NativeJavaObject.getJSTypeCode(fromObj);\r
+\r
+        int result = CONVERSION_NONE;\r
+\r
+        switch (fromCode) {\r
+\r
+        case JSTYPE_UNDEFINED:\r
+            if (to == ScriptRuntime.StringClass || \r
+                to == ScriptRuntime.ObjectClass) {\r
+                result = 1;\r
+            }\r
+            break;\r
+\r
+        case JSTYPE_NULL:\r
+            if (!to.isPrimitive()) {\r
+                result = 1;\r
+            }\r
+            break;\r
+\r
+        case JSTYPE_BOOLEAN:\r
+            // "boolean" is #1\r
+            if (to == Boolean.TYPE) {\r
+                result = 1;\r
+            }\r
+            else if (to == ScriptRuntime.BooleanClass) {\r
+                result = 2;\r
+            }\r
+            else if (to == ScriptRuntime.ObjectClass) {\r
+                result = 3;\r
+            }\r
+            else if (to == ScriptRuntime.StringClass) {\r
+                result = 4;\r
+            }\r
+            break;\r
+\r
+        case JSTYPE_NUMBER:\r
+            if (to.isPrimitive()) {\r
+                if (to == Double.TYPE) {\r
+                    result = 1;\r
+                }\r
+                else if (to != Boolean.TYPE) {\r
+                    result = 1 + NativeJavaObject.getSizeRank(to);\r
+                }\r
+            }\r
+            else {\r
+                if (to == ScriptRuntime.StringClass) {\r
+                    // native numbers are #1-8\r
+                    result = 9;\r
+                }\r
+                else if (to == ScriptRuntime.ObjectClass) {\r
+                    result = 10;\r
+                }\r
+                else if (ScriptRuntime.NumberClass.isAssignableFrom(to)) {\r
+                    // "double" is #1\r
+                    result = 2;\r
+                }\r
+            }\r
+            break;\r
+\r
+        case JSTYPE_STRING:\r
+            if (to == ScriptRuntime.StringClass) {\r
+                result = 1;\r
+            }\r
+            else if (to == ScriptRuntime.ObjectClass) {\r
+                result = 2;\r
+            }\r
+            else if (to.isPrimitive() && to != Boolean.TYPE) {\r
+                if (to == Character.TYPE) {\r
+                    result = 3;\r
+                }\r
+                else {\r
+                    result = 4;\r
+                }\r
+            }\r
+            break;\r
+\r
+        case JSTYPE_JAVA_CLASS:\r
+            if (to == ScriptRuntime.ClassClass) {\r
+                result = 1;\r
+            }\r
+            else if (Context.useJSObject && jsObjectClass != null && \r
+                jsObjectClass.isAssignableFrom(to)) {\r
+                result = 2;\r
+            }\r
+            else if (to == ScriptRuntime.ObjectClass) {\r
+                result = 3;\r
+            }\r
+            else if (to == ScriptRuntime.StringClass) {\r
+                result = 4;\r
+            }\r
+            break;\r
+\r
+        case JSTYPE_JAVA_OBJECT:\r
+        case JSTYPE_JAVA_ARRAY:\r
+            if (to == ScriptRuntime.StringClass) {\r
+                result = 2;\r
+            }\r
+            else if (to.isPrimitive() && to != Boolean.TYPE) {\r
+                result = \r
+                    (fromCode == JSTYPE_JAVA_ARRAY) ?\r
+                    CONVERSION_NONTRIVIAL :\r
+                    2 + NativeJavaObject.getSizeRank(to);\r
+            }\r
+            else {\r
+                Object javaObj = fromObj;\r
+                if (javaObj instanceof Wrapper) {\r
+                    javaObj = ((Wrapper)javaObj).unwrap();\r
+                }\r
+                if (to.isInstance(javaObj)) {\r
+                    result = CONVERSION_NONTRIVIAL;\r
+                }\r
+            }\r
+            break;\r
+\r
+        case JSTYPE_OBJECT:\r
+            // Other objects takes #1-#3 spots\r
+            if (Context.useJSObject && jsObjectClass != null && \r
+                jsObjectClass.isAssignableFrom(to)) {\r
+                result = 1;\r
+            }\r
+            else if (fromObj instanceof NativeArray && to.isArray()) {\r
+                // This is a native array conversion to a java array\r
+                // Array conversions are all equal, and preferable to object \r
+                // and string conversion, per LC3.\r
+                result = 1;\r
+            }\r
+            else if (to == ScriptRuntime.ObjectClass) {\r
+                result = 2;\r
+            }\r
+            else if (to == ScriptRuntime.StringClass) {\r
+                result = 3;\r
+            }\r
+            else if (to.isPrimitive() || to != Boolean.TYPE) {\r
+                result = 3 + NativeJavaObject.getSizeRank(to);\r
+            }\r
+            break;\r
+        }\r
+\r
+        return result;\r
+    \r
+    }\r
+\r
+    static int getSizeRank(Class aType) {\r
+        if (aType == Double.TYPE) {\r
+            return 1;\r
+        }\r
+        else if (aType == Float.TYPE) {\r
+            return 2;\r
+        }\r
+        else if (aType == Long.TYPE) {\r
+            return 3;\r
+        }\r
+        else if (aType == Integer.TYPE) {\r
+            return 4;\r
+        }\r
+        else if (aType == Short.TYPE) {\r
+            return 5;\r
+        }\r
+        else if (aType == Character.TYPE) {\r
+            return 6;\r
+        }\r
+        else if (aType == Byte.TYPE) {\r
+            return 7;\r
+        }\r
+        else if (aType == Boolean.TYPE) {\r
+            return CONVERSION_NONE;\r
+        }\r
+        else {\r
+            return 8;\r
+        }\r
+    }\r
+\r
+    static int getJSTypeCode(Object value) {\r
+        if (value == null) {\r
+            return JSTYPE_NULL;\r
+        }\r
+        else if (value == Undefined.instance) {\r
+            return JSTYPE_UNDEFINED;\r
+        }\r
+        else if (value instanceof Scriptable) {\r
+            if (value instanceof NativeJavaClass) {\r
+                return JSTYPE_JAVA_CLASS;\r
+            }\r
+            else if (value instanceof NativeJavaArray) {\r
+                return JSTYPE_JAVA_ARRAY;\r
+            }\r
+            else if (value instanceof NativeJavaObject) {\r
+                return JSTYPE_JAVA_OBJECT;\r
+            }\r
+            else {\r
+                return JSTYPE_OBJECT;\r
+            }\r
+        }\r
+        else {\r
+            Class valueClass = value.getClass();\r
+\r
+            if (valueClass == ScriptRuntime.StringClass) {\r
+                return JSTYPE_STRING;\r
+            }\r
+            else if (valueClass == ScriptRuntime.BooleanClass) {\r
+                return JSTYPE_BOOLEAN;\r
+            }\r
+            else if (value instanceof Number) {\r
+                return JSTYPE_NUMBER;\r
+            }\r
+            else if (valueClass == ScriptRuntime.ClassClass) {\r
+                return JSTYPE_JAVA_CLASS;\r
+            }\r
+            else if (valueClass.isArray()) {\r
+                return JSTYPE_JAVA_ARRAY;\r
+            }\r
+            else {\r
+                return JSTYPE_JAVA_OBJECT;\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Type-munging for field setting and method invocation.\r
+     * Conforms to LC3 specification\r
+     */\r
+    public static Object coerceType(Class type, Object value) {\r
+        if (value != null && value.getClass() == type) {\r
+            return value;\r
+        }\r
+        \r
+        switch (NativeJavaObject.getJSTypeCode(value)) {\r
+\r
+        case JSTYPE_NULL:\r
+            // raise error if type.isPrimitive()\r
+            if (type.isPrimitive()) {\r
+                reportConversionError(value, type);\r
+            }\r
+            return null;\r
+\r
+        case JSTYPE_UNDEFINED:\r
+            if (type == ScriptRuntime.StringClass || \r
+                type == ScriptRuntime.ObjectClass) {\r
+                return "undefined";\r
+            }\r
+            else {\r
+                reportConversionError("undefined", type);\r
+            }\r
+            break;\r
+\r
+        case JSTYPE_BOOLEAN:\r
+            // Under LC3, only JS Booleans can be coerced into a Boolean value\r
+            if (type == Boolean.TYPE || \r
+                type == ScriptRuntime.BooleanClass || \r
+                type == ScriptRuntime.ObjectClass) {\r
+                return value;\r
+            }\r
+            else if (type == ScriptRuntime.StringClass) {\r
+                return value.toString();\r
+            }\r
+            else {\r
+                reportConversionError(value, type);\r
+            }\r
+            break;\r
+\r
+        case JSTYPE_NUMBER:\r
+            if (type == ScriptRuntime.StringClass) {\r
+                return ScriptRuntime.toString(value);\r
+            }\r
+            else if (type == ScriptRuntime.ObjectClass) {\r
+                return coerceToNumber(Double.TYPE, value);\r
+            }\r
+            else if ((type.isPrimitive() && type != Boolean.TYPE) || \r
+                     ScriptRuntime.NumberClass.isAssignableFrom(type)) {\r
+                return coerceToNumber(type, value);\r
+            }\r
+            else {\r
+                reportConversionError(value, type);\r
+            }\r
+            break;\r
+\r
+        case JSTYPE_STRING:\r
+            if (type == ScriptRuntime.StringClass ||\r
+                type == ScriptRuntime.ObjectClass) {\r
+                return value;\r
+            }\r
+            else if (type == Character.TYPE || \r
+                     type == ScriptRuntime.CharacterClass) {\r
+                // Special case for converting a single char string to a \r
+                // character\r
+                // Placed here because it applies *only* to JS strings, \r
+                // not other JS objects converted to strings\r
+                if (((String)value).length() == 1) {\r
+                    return new Character(((String)value).charAt(0));\r
+                }\r
+                else {\r
+                    return coerceToNumber(type, value);\r
+                }\r
+            }\r
+            else if ((type.isPrimitive() && type != Boolean.TYPE) || \r
+                     ScriptRuntime.NumberClass.isAssignableFrom(type)) {\r
+                return coerceToNumber(type, value);\r
+            }\r
+            else {\r
+                reportConversionError(value, type);\r
+            }\r
+            break;\r
+\r
+        case JSTYPE_JAVA_CLASS:\r
+            if (Context.useJSObject && jsObjectClass != null && \r
+                (type == ScriptRuntime.ObjectClass || \r
+                 jsObjectClass.isAssignableFrom(type))) {\r
+                return coerceToJSObject(type, (Scriptable)value);\r
+            }\r
+            else {\r
+                if (value instanceof Wrapper) {\r
+                    value = ((Wrapper)value).unwrap();\r
+                }\r
+\r
+                if (type == ScriptRuntime.ClassClass ||\r
+                    type == ScriptRuntime.ObjectClass) {\r
+                    return value;\r
+                }\r
+                else if (type == ScriptRuntime.StringClass) {\r
+                    return value.toString();\r
+                }\r
+                else {\r
+                    reportConversionError(value, type);\r
+                }\r
+            }\r
+            break;\r
+\r
+        case JSTYPE_JAVA_OBJECT:\r
+        case JSTYPE_JAVA_ARRAY:\r
+            if (type.isPrimitive()) {\r
+                if (type == Boolean.TYPE) {\r
+                    reportConversionError(value, type);\r
+                }\r
+                return coerceToNumber(type, value);\r
+            }\r
+            else {\r
+                if (value instanceof Wrapper) {\r
+                    value = ((Wrapper)value).unwrap();\r
+                }\r
+                if (type == ScriptRuntime.StringClass) {\r
+                    return value.toString();\r
+                }\r
+                else {\r
+                    if (type.isInstance(value)) {\r
+                        return value;\r
+                    }\r
+                    else {\r
+                        reportConversionError(value, type);\r
+                    }\r
+                }\r
+            }\r
+            break;\r
+\r
+        case JSTYPE_OBJECT:\r
+            if (Context.useJSObject && jsObjectClass != null && \r
+                (type == ScriptRuntime.ObjectClass || \r
+                 jsObjectClass.isAssignableFrom(type))) {\r
+                return coerceToJSObject(type, (Scriptable)value);\r
+            }\r
+            else if (type == ScriptRuntime.StringClass) {\r
+                return ScriptRuntime.toString(value);\r
+            }\r
+            else if (type.isPrimitive()) {\r
+                if (type == Boolean.TYPE) {\r
+                    reportConversionError(value, type);\r
+                }\r
+                return coerceToNumber(type, value);\r
+            }\r
+            else if (type.isInstance(value)) {\r
+                return value;\r
+            }\r
+            else if (type.isArray() && value instanceof NativeArray) {\r
+                // Make a new java array, and coerce the JS array components \r
+                // to the target (component) type.\r
+                NativeArray array = (NativeArray) value;\r
+                long length = array.jsGet_length();\r
+                Class arrayType = type.getComponentType();\r
+                Object Result = Array.newInstance(arrayType, (int)length);\r
+                for (int i = 0 ; i < length ; ++i) {\r
+                    try  {\r
+                        Array.set(Result, i, coerceType(arrayType, \r
+                                                        array.get(i, array)));\r
+                    }\r
+                    catch (EvaluatorException ee) {\r
+                        reportConversionError(value, type);\r
+                    }\r
+                }\r
+\r
+                return Result;\r
+            }\r
+            else if (value instanceof Wrapper) {\r
+                value = ((Wrapper)value).unwrap();\r
+                if (type.isInstance(value))\r
+                    return value;\r
+                reportConversionError(value, type);\r
+            }\r
+            else {\r
+                reportConversionError(value, type);\r
+            }\r
+            break;\r
+        }\r
+\r
+        return value;\r
+    }\r
+\r
+    static Object coerceToJSObject(Class type, Scriptable value) {\r
+        // If JSObject compatibility is enabled, and the method wants it,\r
+        // wrap the Scriptable value in a JSObject.\r
+\r
+        if (ScriptRuntime.ScriptableClass.isAssignableFrom(type))\r
+            return value;\r
+\r
+        try {\r
+            Object ctorArgs[] = { value };\r
+            return jsObjectCtor.newInstance(ctorArgs);\r
+        } catch (InstantiationException instEx) {\r
+            throw new EvaluatorException("error generating JSObject wrapper for " +\r
+                                         value);\r
+        } catch (IllegalArgumentException argEx) {\r
+            throw new EvaluatorException("JSObject constructor doesn't want [Scriptable]!");\r
+        } catch (InvocationTargetException e) {\r
+            throw WrappedException.wrapException(e.getTargetException());\r
+        } catch (IllegalAccessException accessEx) {\r
+            throw new EvaluatorException("JSObject constructor is protected/private!");\r
+        }\r
+    }\r
+\r
+    static Object coerceToNumber(Class type, Object value) {\r
+        Class valueClass = value.getClass();\r
+\r
+        // Character\r
+        if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) {\r
+            if (valueClass == ScriptRuntime.CharacterClass) {\r
+                return value;\r
+            }\r
+            return new Character((char)toInteger(value, \r
+                                                 ScriptRuntime.CharacterClass,\r
+                                                 (double)Character.MIN_VALUE,\r
+                                                 (double)Character.MAX_VALUE));\r
+        }\r
+\r
+        // Double, Float\r
+        if (type == ScriptRuntime.ObjectClass || \r
+            type == ScriptRuntime.DoubleClass || type == Double.TYPE) {\r
+            return valueClass == ScriptRuntime.DoubleClass\r
+                ? value\r
+                : new Double(toDouble(value));\r
+        }\r
+\r
+        if (type == ScriptRuntime.FloatClass || type == Float.TYPE) {\r
+            if (valueClass == ScriptRuntime.FloatClass) {\r
+                return value;\r
+            }\r
+            else {\r
+                double number = toDouble(value);\r
+                if (Double.isInfinite(number) || Double.isNaN(number)\r
+                    || number == 0.0) {\r
+                    return new Float((float)number);\r
+                }\r
+                else {\r
+                    double absNumber = Math.abs(number);\r
+                    if (absNumber < (double)Float.MIN_VALUE) {\r
+                        return new Float((number > 0.0) ? +0.0 : -0.0);\r
+                    }\r
+                    else if (absNumber > (double)Float.MAX_VALUE) {\r
+                        return new Float((number > 0.0) ?\r
+                                         Float.POSITIVE_INFINITY : \r
+                                         Float.NEGATIVE_INFINITY);\r
+                    }\r
+                    else {\r
+                        return new Float((float)number);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        // Integer, Long, Short, Byte\r
+        if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE) {\r
+            if (valueClass == ScriptRuntime.IntegerClass) {\r
+                return value;\r
+            }\r
+            else {\r
+                return new Integer((int)toInteger(value, \r
+                                                  ScriptRuntime.IntegerClass,\r
+                                                  (double)Integer.MIN_VALUE,\r
+                                                  (double)Integer.MAX_VALUE));\r
+            }\r
+        }\r
+\r
+        if (type == ScriptRuntime.LongClass || type == Long.TYPE) {\r
+            if (valueClass == ScriptRuntime.LongClass) {\r
+                return value;\r
+            }\r
+            else {\r
+                /* Long values cannot be expressed exactly in doubles.\r
+                 * We thus use the largest and smallest double value that \r
+                 * has a value expressible as a long value. We build these \r
+                 * numerical values from their hexidecimal representations\r
+                 * to avoid any problems caused by attempting to parse a\r
+                 * decimal representation.\r
+                 */\r
+                final double max = Double.longBitsToDouble(0x43dfffffffffffffL);\r
+                final double min = Double.longBitsToDouble(0xc3e0000000000000L);\r
+                return new Long(toInteger(value, \r
+                                          ScriptRuntime.LongClass,\r
+                                          min,\r
+                                          max));\r
+            }\r
+        }\r
+\r
+        if (type == ScriptRuntime.ShortClass || type == Short.TYPE) {\r
+            if (valueClass == ScriptRuntime.ShortClass) {\r
+                return value;\r
+            }\r
+            else {\r
+                return new Short((short)toInteger(value, \r
+                                                  ScriptRuntime.ShortClass,\r
+                                                  (double)Short.MIN_VALUE,\r
+                                                  (double)Short.MAX_VALUE));\r
+            }\r
+        }\r
+\r
+        if (type == ScriptRuntime.ByteClass || type == Byte.TYPE) {\r
+            if (valueClass == ScriptRuntime.ByteClass) {\r
+                return value;\r
+            }\r
+            else {\r
+                return new Byte((byte)toInteger(value, \r
+                                                ScriptRuntime.ByteClass,\r
+                                                (double)Byte.MIN_VALUE,\r
+                                                (double)Byte.MAX_VALUE));\r
+            }\r
+        }\r
+\r
+        return new Double(toDouble(value));\r
+    }\r
+\r
+\r
+    static double toDouble(Object value) {\r
+        if (value instanceof Number) {\r
+            return ((Number)value).doubleValue();\r
+        }\r
+        else if (value instanceof String) {\r
+            return ScriptRuntime.toNumber((String)value);\r
+        }\r
+        else if (value instanceof Scriptable) {\r
+            if (value instanceof Wrapper) {\r
+                // XXX: optimize tail-recursion?\r
+                return toDouble(((Wrapper)value).unwrap());\r
+            }\r
+            else {\r
+                return ScriptRuntime.toNumber(value);\r
+            }\r
+        }\r
+        else {\r
+            Method meth;\r
+            try {\r
+                meth = value.getClass().getMethod("doubleValue", null);\r
+            }\r
+            catch (NoSuchMethodException e) {\r
+                meth = null;\r
+            }\r
+            catch (SecurityException e) {\r
+                meth = null;\r
+            }\r
+            if (meth != null) {\r
+                try {\r
+                    return ((Number)meth.invoke(value, null)).doubleValue();\r
+                }\r
+                catch (IllegalAccessException e) {\r
+                    // XXX: ignore, or error message?\r
+                    reportConversionError(value, Double.TYPE);\r
+                }\r
+                catch (InvocationTargetException e) {\r
+                    // XXX: ignore, or error message?\r
+                    reportConversionError(value, Double.TYPE);\r
+                }\r
+            }\r
+            return ScriptRuntime.toNumber(value.toString());\r
+        }\r
+    }\r
+\r
+    static long toInteger(Object value, Class type, double min, double max) {\r
+        double d = toDouble(value);\r
+\r
+        if (Double.isInfinite(d) || Double.isNaN(d)) {\r
+            // Convert to string first, for more readable message\r
+            reportConversionError(ScriptRuntime.toString(value), type);\r
+        }\r
+\r
+        if (d > 0.0) {\r
+            d = Math.floor(d);\r
+        }\r
+        else {\r
+            d = Math.ceil(d);\r
+        }\r
+\r
+        if (d < min || d > max) {\r
+            // Convert to string first, for more readable message\r
+            reportConversionError(ScriptRuntime.toString(value), type);\r
+        }\r
+        return (long)d;\r
+    }\r
+\r
+    static void reportConversionError(Object value, Class type) {\r
+        throw Context.reportRuntimeError2\r
+            ("msg.conversion.not.allowed", \r
+             value.toString(), NativeJavaMethod.javaSignature(type));\r
+    }\r
+\r
+    public static void initJSObject() {\r
+        // if netscape.javascript.JSObject is in the CLASSPATH, enable JSObject\r
+        // compatability wrappers\r
+        jsObjectClass = null;\r
+        try {\r
+            jsObjectClass = Class.forName("netscape.javascript.JSObject");\r
+            Class ctorParms[] = { ScriptRuntime.ScriptableClass };\r
+            jsObjectCtor = jsObjectClass.getConstructor(ctorParms);\r
+            jsObjectGetScriptable = jsObjectClass.getMethod("getScriptable", \r
+                                                            new Class[0]);\r
+        } catch (ClassNotFoundException classEx) {\r
+            // jsObjectClass already null\r
+        } catch (NoSuchMethodException methEx) {\r
+            // jsObjectClass already null\r
+        }\r
+    }\r
+        \r
+    /**\r
+     * The prototype of this object.\r
+     */\r
+    protected Scriptable prototype;\r
+\r
+    /**\r
+     * The parent scope of this object.\r
+     */\r
+    protected Scriptable parent;\r
+\r
+    protected Object javaObject;\r
+    protected JavaMembers members;\r
+    private Hashtable fieldAndMethods;\r
+    static Class jsObjectClass;\r
+    static Constructor jsObjectCtor;\r
+    static Method jsObjectGetScriptable;\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/NativeJavaPackage.java b/src/org/mozilla/javascript/NativeJavaPackage.java
new file mode 100644 (file)
index 0000000..5a8c652
--- /dev/null
@@ -0,0 +1,240 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Frank Mitchell\r
+ * Mike Shaver\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.lang.reflect.*;\r
+\r
+/**\r
+ * This class reflects Java packages into the JavaScript environment.  We \r
+ * lazily reflect classes and subpackages, and use a caching/sharing \r
+ * system to ensure that members reflected into one JavaPackage appear\r
+ * in all other references to the same package (as with Packages.java.lang \r
+ * and java.lang).\r
+ *\r
+ * @author Mike Shaver\r
+ * @see NativeJavaArray\r
+ * @see NativeJavaObject\r
+ * @see NativeJavaClass\r
+ */\r
+\r
+public class NativeJavaPackage extends ScriptableObject {\r
+\r
+    // we know these are packages so we can skip the class check\r
+    // note that this is ok even if the package isn't present.\r
+    static final String[] commonPackages = {\r
+        "java.lang",\r
+        "java.lang.reflect",\r
+        "java.io",\r
+        "java.math",\r
+        "java.util",\r
+        "java.util.zip",\r
+        "java.text",\r
+        "java.text.resources",\r
+        "java.applet",\r
+    };\r
+\r
+    public static Scriptable init(Scriptable scope) \r
+        throws PropertyException\r
+    {\r
+        NativeJavaPackage packages = new NativeJavaPackage("");\r
+        packages.setPrototype(getObjectPrototype(scope));\r
+        packages.setParentScope(scope);\r
+\r
+        // We want to get a real alias, and not a distinct JavaPackage\r
+        // with the same packageName, so that we share classes and packages\r
+        // that are underneath.\r
+        NativeJavaPackage javaAlias = (NativeJavaPackage)packages.get("java",\r
+                                                                      packages);\r
+\r
+        // It's safe to downcast here since initStandardObjects takes\r
+        // a ScriptableObject.\r
+        ScriptableObject global = (ScriptableObject) scope;\r
+\r
+        global.defineProperty("Packages", packages, ScriptableObject.DONTENUM);\r
+        global.defineProperty("java", javaAlias, ScriptableObject.DONTENUM);\r
+\r
+        for (int i = 0; i < commonPackages.length; i++)\r
+            packages.forcePackage(commonPackages[i]);\r
+\r
+        if (Context.useJSObject)\r
+            NativeJavaObject.initJSObject();\r
+        \r
+        Method[] m = FunctionObject.findMethods(NativeJavaPackage.class, \r
+                                                "jsFunction_getClass");\r
+        FunctionObject f = new FunctionObject("getClass", m[0], global);\r
+        global.defineProperty("getClass", f, ScriptableObject.DONTENUM);\r
+\r
+        // I think I'm supposed to return the prototype, but I don't have one.\r
+        return packages;\r
+    }\r
+\r
+    // set up a name which is known to be a package so we don't\r
+    // need to look for a class by that name\r
+    void forcePackage(String name) {\r
+        NativeJavaPackage pkg;\r
+        int end = name.indexOf('.');\r
+        if (end == -1)\r
+            end = name.length();\r
+\r
+        String id = name.substring(0, end);\r
+        Object cached = super.get(id, this);\r
+        if (cached != null && cached instanceof NativeJavaPackage) {\r
+            pkg = (NativeJavaPackage) cached;\r
+        } else {\r
+            String newPackage = packageName.length() == 0 \r
+                                ? id \r
+                                : packageName + "." + id;\r
+            pkg = new NativeJavaPackage(newPackage);\r
+            pkg.setParentScope(this);\r
+            pkg.setPrototype(this.prototype);\r
+            super.put(id, this, pkg);\r
+        }\r
+        if (end < name.length())\r
+            pkg.forcePackage(name.substring(end+1));\r
+    }\r
+\r
+    public NativeJavaPackage(String packageName) {\r
+        this.packageName = packageName;\r
+    }\r
+\r
+    public String getClassName() {\r
+        return "JavaPackage";\r
+    }\r
+\r
+    public boolean has(String id, int index, Scriptable start) {\r
+        return true;\r
+    }\r
+\r
+    public void put(String id, Scriptable start, Object value) {\r
+        // Can't add properties to Java packages.  Sorry.\r
+    }\r
+\r
+    public void put(int index, Scriptable start, Object value) {\r
+        throw Context.reportRuntimeError0("msg.pkg.int");\r
+    }\r
+\r
+    public Object get(String id, Scriptable start) {\r
+        return getPkgProperty(id, start, true);\r
+    }\r
+\r
+    public Object get(int index, Scriptable start) {\r
+        return NOT_FOUND;\r
+    }\r
+\r
+    synchronized Object getPkgProperty(String name, Scriptable start,\r
+                                       boolean createPkg) \r
+    {\r
+        Object cached = super.get(name, start);\r
+        if (cached != NOT_FOUND)\r
+            return cached;\r
+        \r
+        String newPackage = packageName.length() == 0\r
+                            ? name \r
+                            : packageName + "." + name;\r
+        Context cx = Context.getContext();\r
+        SecuritySupport ss = cx.getSecuritySupport();\r
+        Scriptable newValue;\r
+        try {\r
+            /*\r
+            if (ss != null && !ss.visibleToScripts(newPackage))\r
+            */\r
+                throw new ClassNotFoundException();\r
+                /*\r
+            Class newClass = ScriptRuntime.loadClassName(newPackage);\r
+            newValue =  NativeJavaClass.wrap(getTopLevelScope(this), newClass);\r
+            newValue.setParentScope(this);\r
+            newValue.setPrototype(this.prototype);\r
+                */\r
+        } catch (ClassNotFoundException ex) {\r
+            if (createPkg) {\r
+                NativeJavaPackage pkg = new NativeJavaPackage(newPackage);\r
+                pkg.setParentScope(this);\r
+                pkg.setPrototype(this.prototype);\r
+                newValue = pkg;\r
+            } else {\r
+                newValue = null;\r
+            }\r
+        }\r
+        if (newValue != null) {\r
+            // Make it available for fast lookup and sharing of \r
+            // lazily-reflected constructors and static members.\r
+            super.put(name, start, newValue);\r
+        }\r
+        return newValue;\r
+    }\r
+\r
+    public Object getDefaultValue(Class ignored) {\r
+        return toString();\r
+    }\r
+\r
+    public String toString() {\r
+        return "[JavaPackage " + packageName + "]";\r
+    }\r
+    \r
+    public static Scriptable jsFunction_getClass(Context cx, \r
+                                                 Scriptable thisObj,\r
+                                                 Object[] args, \r
+                                                 Function funObj)\r
+    {\r
+        if (args.length > 0  && args[0] instanceof Wrapper) {\r
+            Scriptable result = getTopLevelScope(thisObj);\r
+            Class cl = ((Wrapper) args[0]).unwrap().getClass();\r
+            // Evaluate the class name by getting successive properties of \r
+            // the string to find the appropriate NativeJavaClass object\r
+            String name = "Packages." + cl.getName();\r
+            int offset = 0;\r
+            for (;;) {\r
+                int index = name.indexOf('.', offset);\r
+                String propName = index == -1\r
+                                  ? name.substring(offset)\r
+                                  : name.substring(offset, index);\r
+                Object prop = result.get(propName, result);\r
+                if (!(prop instanceof Scriptable)) \r
+                    break;  // fall through to error\r
+                result = (Scriptable) prop;\r
+                if (index == -1)\r
+                    return result;\r
+                offset = index+1;\r
+            }\r
+        }\r
+        throw Context.reportRuntimeError(\r
+            Context.getMessage0("msg.not.java.obj"));\r
+    }\r
+\r
+    private String packageName;\r
+}\r
diff --git a/src/org/mozilla/javascript/NativeMath.java b/src/org/mozilla/javascript/NativeMath.java
new file mode 100644 (file)
index 0000000..a6e58c6
--- /dev/null
@@ -0,0 +1,405 @@
+/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Norris Boyd\r
+ * Igor Bukanov\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This class implements the Math native object.\r
+ * See ECMA 15.8.\r
+ * @author Norris Boyd\r
+ */\r
+\r
+public class NativeMath extends IdScriptable\r
+{\r
+    public static void init(Context cx, Scriptable scope, boolean sealed) {\r
+        NativeMath obj = new NativeMath();\r
+        obj.setSealFunctionsFlag(sealed);\r
+        obj.setFunctionParametrs(cx);\r
+        obj.setPrototype(getObjectPrototype(scope));\r
+        obj.setParentScope(scope);\r
+        if (sealed) { obj.sealObject(); }\r
+        ScriptableObject.defineProperty(scope, "Math", obj,\r
+                                        ScriptableObject.DONTENUM);\r
+    }\r
+\r
+    public String getClassName() { return "Math"; }\r
+\r
+    protected int getIdDefaultAttributes(int id) {\r
+        if (id > LAST_METHOD_ID) {\r
+            return DONTENUM | READONLY | PERMANENT;\r
+        }\r
+        return super.getIdDefaultAttributes(id);\r
+    }\r
+\r
+    protected Object getIdValue(int id) {\r
+        if (id > LAST_METHOD_ID) {\r
+            return cacheIdValue(id, wrap_double(getField(id)));\r
+        }\r
+        return super.getIdValue(id);\r
+    }\r
+\r
+    private double getField(int fieldId) {\r
+        switch (fieldId) {\r
+            case Id_E:       return E;\r
+            case Id_PI:      return PI;\r
+            case Id_LN10:    return LN10;\r
+            case Id_LN2:     return LN2;\r
+            case Id_LOG2E:   return LOG2E;\r
+            case Id_LOG10E:  return LOG10E;\r
+            case Id_SQRT1_2: return SQRT1_2;\r
+            case Id_SQRT2:   return SQRT2;\r
+        }\r
+        return 0; // Unreachable\r
+    }\r
+\r
+    public int methodArity(int methodId) {\r
+        switch (methodId) {\r
+            case Id_abs:      return 1;\r
+            case Id_acos:     return 1;\r
+            case Id_asin:     return 1;\r
+            case Id_atan:     return 1;\r
+            case Id_atan2:    return 2;\r
+            case Id_ceil:     return 1;\r
+            case Id_cos:      return 1;\r
+            case Id_exp:      return 1;\r
+            case Id_floor:    return 1;\r
+            case Id_log:      return 1;\r
+            case Id_max:      return 2;\r
+            case Id_min:      return 2;\r
+            case Id_pow:      return 2;\r
+            case Id_random:   return 0;\r
+            case Id_round:    return 1;\r
+            case Id_sin:      return 1;\r
+            case Id_sqrt:     return 1;\r
+            case Id_tan:      return 1;\r
+        }\r
+        return super.methodArity(methodId);\r
+    }\r
+\r
+    public Object execMethod\r
+        (int methodId, IdFunction f,\r
+         Context cx, Scriptable scope, Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        switch (methodId) {\r
+            case Id_abs: return wrap_double\r
+                (js_abs(ScriptRuntime.toNumber(args, 0)));\r
+\r
+            case Id_acos: return wrap_double\r
+                (js_acos(ScriptRuntime.toNumber(args, 0)));\r
+\r
+            case Id_asin: return wrap_double\r
+                (js_asin(ScriptRuntime.toNumber(args, 0)));\r
+\r
+            case Id_atan: return wrap_double\r
+                (js_atan(ScriptRuntime.toNumber(args, 0)));\r
+\r
+            case Id_atan2: return wrap_double\r
+                (js_atan2(ScriptRuntime.toNumber(args, 0),\r
+                          ScriptRuntime.toNumber(args, 1)));\r
+\r
+            case Id_ceil: return wrap_double\r
+                (js_ceil(ScriptRuntime.toNumber(args, 0)));\r
+\r
+            case Id_cos: return wrap_double\r
+                (js_cos(ScriptRuntime.toNumber(args, 0)));\r
+\r
+            case Id_exp: return wrap_double\r
+                (js_exp(ScriptRuntime.toNumber(args, 0)));\r
+\r
+            case Id_floor: return wrap_double\r
+                (js_floor(ScriptRuntime.toNumber(args, 0)));\r
+\r
+            case Id_log: return wrap_double\r
+                (js_log(ScriptRuntime.toNumber(args, 0)));\r
+\r
+            case Id_max: return wrap_double\r
+                (js_max(args));\r
+\r
+            case Id_min: return wrap_double\r
+                (js_min(args));\r
+\r
+            case Id_pow: return wrap_double\r
+                (js_pow(ScriptRuntime.toNumber(args, 0),\r
+                        ScriptRuntime.toNumber(args, 1)));\r
+\r
+            case Id_random: return wrap_double\r
+                (js_random());\r
+\r
+            case Id_round: return wrap_double\r
+                (js_round(ScriptRuntime.toNumber(args, 0)));\r
+\r
+            case Id_sin: return wrap_double\r
+                (js_sin(ScriptRuntime.toNumber(args, 0)));\r
+\r
+            case Id_sqrt: return wrap_double\r
+                (js_sqrt(ScriptRuntime.toNumber(args, 0)));\r
+\r
+            case Id_tan: return wrap_double\r
+                (js_tan(ScriptRuntime.toNumber(args, 0)));\r
+        }\r
+        return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
+    }\r
+\r
+    private double js_abs(double x) {\r
+        // abs(-0.0) should be 0.0, but -0.0 < 0.0 == false\r
+        return (x == 0.0) ? 0.0 : (x < 0.0) ? -x : x;\r
+    }\r
+\r
+    private double js_acos(double x) {\r
+        return (x == x && -1.0 <= x && x <= 1.0) ? Math.acos(x) : Double.NaN;\r
+    }\r
+\r
+    private double js_asin(double x) {\r
+        return (x == x && -1.0 <= x && x <= 1.0) ? Math.asin(x) : Double.NaN;\r
+    }\r
+\r
+    private double js_atan(double x) { return Math.atan(x); }\r
+\r
+    private double js_atan2(double x, double y) { return Math.atan2(x, y); }\r
+\r
+    private double js_ceil(double x) { return Math.ceil(x); }\r
+\r
+    private double js_cos(double x) { return Math.cos(x); }\r
+\r
+    private double js_exp(double x) {\r
+        return (x == Double.POSITIVE_INFINITY) ? x\r
+            : (x == Double.NEGATIVE_INFINITY) ? 0.0\r
+            : Math.exp(x);\r
+    }\r
+\r
+    private double js_floor(double x) { return Math.floor(x); }\r
+\r
+    private double js_log(double x) {\r
+        // Java's log(<0) = -Infinity; we need NaN\r
+        return (x < 0) ? Double.NaN : Math.log(x);\r
+    }\r
+\r
+    private double js_max(Object[] args) {\r
+        double result = Double.NEGATIVE_INFINITY;\r
+        if (args.length == 0)\r
+            return result;\r
+        for (int i = 0; i < args.length; i++) {\r
+            double d = ScriptRuntime.toNumber(args[i]);\r
+            if (d != d) return d;\r
+            // if (result < d) result = d; does not work due to -0.0 >= +0.0\r
+            result = Math.max(result, d);\r
+        }\r
+        return result;\r
+    }\r
+\r
+    private double js_min(Object[] args) {\r
+        double result = Double.POSITIVE_INFINITY;\r
+        if (args.length == 0)\r
+            return result;\r
+        for (int i = 0; i < args.length; i++) {\r
+            double d = ScriptRuntime.toNumber(args[i]);\r
+            if (d != d) return d;\r
+            // if (result > d) result = d; does not work due to -0.0 >= +0.0\r
+            result = Math.min(result, d);\r
+        }\r
+        return result;\r
+    }\r
+\r
+    private double js_pow(double x, double y) {\r
+        if (y == 0) return 1.0;   // Java's pow(NaN, 0) = NaN; we need 1\r
+        if ((x == 0) && (y < 0)) {\r
+            if (1 / x > 0) {\r
+                // x is +0, Java is -oo, we need +oo\r
+                return Double.POSITIVE_INFINITY;\r
+            }\r
+            /* if x is -0 and y is an odd integer, -oo */\r
+            int y_int = (int)y;\r
+            if (y_int == y && (y_int & 0x1) != 0)\r
+                return Double.NEGATIVE_INFINITY;\r
+            return Double.POSITIVE_INFINITY;\r
+        }\r
+        return Math.pow(x, y);\r
+    }\r
+\r
+    private double js_random() { return Math.random(); }\r
+\r
+    private double js_round(double d) {\r
+        if (d != d)\r
+            return d;   // NaN\r
+        if (d == Double.POSITIVE_INFINITY || d == Double.NEGATIVE_INFINITY)\r
+            return d;\r
+        long l = Math.round(d);\r
+        if (l == 0) {\r
+            // We must propagate the sign of d into the result\r
+            if (d < 0.0)\r
+                return ScriptRuntime.negativeZero;\r
+            return d == 0.0 ? d : 0.0;\r
+        }\r
+        return (double) l;\r
+    }\r
+\r
+    private double js_sin(double x) { return Math.sin(x); }\r
+\r
+    private double js_sqrt(double x) { return Math.sqrt(x); }\r
+\r
+    private double js_tan(double x) { return Math.tan(x); }\r
+\r
+    protected int maxInstanceId() { return MAX_INSTANCE_ID; }\r
+\r
+    protected String getIdName(int id) {\r
+        switch (id) {\r
+            case Id_abs:      return "abs";\r
+            case Id_acos:     return "acos";\r
+            case Id_asin:     return "asin";\r
+            case Id_atan:     return "atan";\r
+            case Id_atan2:    return "atan2";\r
+            case Id_ceil:     return "ceil";\r
+            case Id_cos:      return "cos";\r
+            case Id_exp:      return "exp";\r
+            case Id_floor:    return "floor";\r
+            case Id_log:      return "log";\r
+            case Id_max:      return "max";\r
+            case Id_min:      return "min";\r
+            case Id_pow:      return "pow";\r
+            case Id_random:   return "random";\r
+            case Id_round:    return "round";\r
+            case Id_sin:      return "sin";\r
+            case Id_sqrt:     return "sqrt";\r
+            case Id_tan:      return "tan";\r
+\r
+            case Id_E:        return "E";\r
+            case Id_PI:       return "PI";\r
+            case Id_LN10:     return "LN10";\r
+            case Id_LN2:      return "LN2";\r
+            case Id_LOG2E:    return "LOG2E";\r
+            case Id_LOG10E:   return "LOG10E";\r
+            case Id_SQRT1_2:  return "SQRT1_2";\r
+            case Id_SQRT2:    return "SQRT2";\r
+        }\r
+        return null;\r
+    }\r
+\r
+// #string_id_map#\r
+\r
+    protected int mapNameToId(String s) {\r
+        int id;\r
+// #generated# Last update: 2001-03-23 13:50:14 GMT+01:00\r
+        L0: { id = 0; String X = null; int c;\r
+            L: switch (s.length()) {\r
+            case 1: if (s.charAt(0)=='E') {id=Id_E; break L0;} break L;\r
+            case 2: if (s.charAt(0)=='P' && s.charAt(1)=='I') {id=Id_PI; break L0;} break L;\r
+            case 3: switch (s.charAt(0)) {\r
+                case 'L': if (s.charAt(2)=='2' && s.charAt(1)=='N') {id=Id_LN2; break L0;} break L;\r
+                case 'a': if (s.charAt(2)=='s' && s.charAt(1)=='b') {id=Id_abs; break L0;} break L;\r
+                case 'c': if (s.charAt(2)=='s' && s.charAt(1)=='o') {id=Id_cos; break L0;} break L;\r
+                case 'e': if (s.charAt(2)=='p' && s.charAt(1)=='x') {id=Id_exp; break L0;} break L;\r
+                case 'l': if (s.charAt(2)=='g' && s.charAt(1)=='o') {id=Id_log; break L0;} break L;\r
+                case 'm': c=s.charAt(2);\r
+                    if (c=='n') { if (s.charAt(1)=='i') {id=Id_min; break L0;} }\r
+                    else if (c=='x') { if (s.charAt(1)=='a') {id=Id_max; break L0;} }\r
+                    break L;\r
+                case 'p': if (s.charAt(2)=='w' && s.charAt(1)=='o') {id=Id_pow; break L0;} break L;\r
+                case 's': if (s.charAt(2)=='n' && s.charAt(1)=='i') {id=Id_sin; break L0;} break L;\r
+                case 't': if (s.charAt(2)=='n' && s.charAt(1)=='a') {id=Id_tan; break L0;} break L;\r
+                } break L;\r
+            case 4: switch (s.charAt(1)) {\r
+                case 'N': X="LN10";id=Id_LN10; break L;\r
+                case 'c': X="acos";id=Id_acos; break L;\r
+                case 'e': X="ceil";id=Id_ceil; break L;\r
+                case 'q': X="sqrt";id=Id_sqrt; break L;\r
+                case 's': X="asin";id=Id_asin; break L;\r
+                case 't': X="atan";id=Id_atan; break L;\r
+                } break L;\r
+            case 5: switch (s.charAt(0)) {\r
+                case 'L': X="LOG2E";id=Id_LOG2E; break L;\r
+                case 'S': X="SQRT2";id=Id_SQRT2; break L;\r
+                case 'a': X="atan2";id=Id_atan2; break L;\r
+                case 'f': X="floor";id=Id_floor; break L;\r
+                case 'r': X="round";id=Id_round; break L;\r
+                } break L;\r
+            case 6: c=s.charAt(0);\r
+                if (c=='L') { X="LOG10E";id=Id_LOG10E; }\r
+                else if (c=='r') { X="random";id=Id_random; }\r
+                break L;\r
+            case 7: X="SQRT1_2";id=Id_SQRT1_2; break L;\r
+            }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+        return id;\r
+    }\r
+\r
+    private static final int\r
+        Id_abs          =  1,\r
+        Id_acos         =  2,\r
+        Id_asin         =  3,\r
+        Id_atan         =  4,\r
+        Id_atan2        =  5,\r
+        Id_ceil         =  6,\r
+        Id_cos          =  7,\r
+        Id_exp          =  8,\r
+        Id_floor        =  9,\r
+        Id_log          = 10,\r
+        Id_max          = 11,\r
+        Id_min          = 12,\r
+        Id_pow          = 13,\r
+        Id_random       = 14,\r
+        Id_round        = 15,\r
+        Id_sin          = 16,\r
+        Id_sqrt         = 17,\r
+        Id_tan          = 18,\r
+\r
+        LAST_METHOD_ID  = 18,\r
+\r
+        Id_E            = 19,\r
+        Id_PI           = 20,\r
+        Id_LN10         = 21,\r
+        Id_LN2          = 22,\r
+        Id_LOG2E        = 23,\r
+        Id_LOG10E       = 24,\r
+        Id_SQRT1_2      = 25,\r
+        Id_SQRT2        = 26,\r
+\r
+        MAX_INSTANCE_ID = 26;\r
+\r
+// #/string_id_map#\r
+\r
+    private static final double\r
+        E       = Math.E,\r
+        PI      = Math.PI,\r
+        LN10    = 2.302585092994046,\r
+        LN2     = 0.6931471805599453,\r
+        LOG2E   = 1.4426950408889634,\r
+        LOG10E  = 0.4342944819032518,\r
+        SQRT1_2 = 0.7071067811865476,\r
+        SQRT2   = 1.4142135623730951;\r
+}\r
diff --git a/src/org/mozilla/javascript/NativeNumber.java b/src/org/mozilla/javascript/NativeNumber.java
new file mode 100644 (file)
index 0000000..4ec2dcb
--- /dev/null
@@ -0,0 +1,280 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Norris Boyd\r
+ * Igor Bukanov\r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This class implements the Number native object.\r
+ *\r
+ * See ECMA 15.7.\r
+ *\r
+ * @author Norris Boyd\r
+ */\r
+public class NativeNumber extends IdScriptable {\r
+\r
+    private static final int MAX_PRECISION = 100;\r
+\r
+    public static void init(Context cx, Scriptable scope, boolean sealed) {\r
+        NativeNumber obj = new NativeNumber();\r
+        obj.prototypeFlag = true;\r
+        obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);\r
+    }\r
+\r
+    /**\r
+     * Zero-parameter constructor: just used to create Number.prototype\r
+     */\r
+    public NativeNumber() {\r
+        doubleValue = defaultValue;\r
+    }\r
+\r
+    public NativeNumber(double number) {\r
+        doubleValue = number;\r
+    }\r
+\r
+    public String getClassName() {\r
+        return "Number";\r
+    }\r
+\r
+    protected void fillConstructorProperties\r
+        (Context cx, IdFunction ctor, boolean sealed)\r
+    {\r
+        final int attr = ScriptableObject.DONTENUM |\r
+                         ScriptableObject.PERMANENT |\r
+                         ScriptableObject.READONLY;\r
+\r
+        ctor.defineProperty("NaN", wrap_double(ScriptRuntime.NaN), attr);\r
+        ctor.defineProperty("POSITIVE_INFINITY",\r
+                            wrap_double(Double.POSITIVE_INFINITY), attr);\r
+        ctor.defineProperty("NEGATIVE_INFINITY",\r
+                            wrap_double(Double.NEGATIVE_INFINITY), attr);\r
+        ctor.defineProperty("MAX_VALUE", wrap_double(Double.MAX_VALUE), attr);\r
+        ctor.defineProperty("MIN_VALUE", wrap_double(Double.MIN_VALUE), attr);\r
+\r
+        super.fillConstructorProperties(cx, ctor, sealed);\r
+    }\r
+    \r
+    public int methodArity(int methodId) {\r
+        if (prototypeFlag) {\r
+            switch (methodId) {\r
+                case Id_constructor:     return 1; \r
+                case Id_toString:        return 1; \r
+                case Id_valueOf:         return 0; \r
+                case Id_toLocaleString:  return 1; \r
+                case Id_toFixed:         return 1;\r
+                case Id_toExponential:   return 1;\r
+                case Id_toPrecision:     return 1;\r
+            }\r
+        }\r
+        return super.methodArity(methodId);\r
+    }\r
+\r
+    public Object execMethod\r
+        (int methodId, IdFunction f,\r
+         Context cx, Scriptable scope, Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (prototypeFlag) {\r
+            switch (methodId) {\r
+                case Id_constructor:\r
+                    return jsConstructor(args, thisObj == null);\r
+\r
+                case Id_toString: return realThis(thisObj, f).\r
+                    jsFunction_toString(toBase(args, 0));\r
+\r
+                case Id_valueOf: return wrap_double(realThis(thisObj, f).\r
+                    jsFunction_valueOf());\r
+\r
+                case Id_toLocaleString: return realThis(thisObj, f).\r
+                    jsFunction_toLocaleString(toBase(args, 0));\r
+\r
+                case Id_toFixed: return realThis(thisObj, f).\r
+                    jsFunction_toFixed(cx, args);\r
+\r
+                case Id_toExponential: return realThis(thisObj, f).\r
+                    jsFunction_toExponential(cx, args);\r
+\r
+                case Id_toPrecision:return realThis(thisObj, f).\r
+                    jsFunction_toPrecision(cx, args);\r
+            }\r
+        }\r
+        return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
+    }\r
+\r
+    private NativeNumber realThis(Scriptable thisObj, IdFunction f) {\r
+        while (!(thisObj instanceof NativeNumber)) {\r
+            thisObj = nextInstanceCheck(thisObj, f, true);\r
+        }\r
+        return (NativeNumber)thisObj;\r
+    }\r
+\r
+    private static int toBase(Object[] args, int index) {\r
+        return (index < args.length) ? ScriptRuntime.toInt32(args[index]) : 10;\r
+    }\r
+\r
+    private Object jsConstructor(Object[] args, boolean inNewExpr) {\r
+        double d = args.length >= 1\r
+                   ? ScriptRuntime.toNumber(args[0])\r
+                   : defaultValue;\r
+        if (inNewExpr) {\r
+            // new Number(val) creates a new Number object.\r
+            return new NativeNumber(d);\r
+        }\r
+        // Number(val) converts val to a number value.\r
+        return wrap_double(d);\r
+    }\r
+\r
+    public String toString() {\r
+        return jsFunction_toString(10);\r
+    }\r
+\r
+    private String jsFunction_toString(int base) {\r
+        return ScriptRuntime.numberToString(doubleValue, base);\r
+    }\r
+\r
+    private double jsFunction_valueOf() {\r
+        return doubleValue;\r
+    }\r
+\r
+    private String jsFunction_toLocaleString(int base) {\r
+        return jsFunction_toString(base);\r
+    }\r
+\r
+    private String jsFunction_toFixed(Context cx, Object[] args) {\r
+        /* We allow a larger range of precision than\r
+           ECMA requires; this is permitted by ECMA. */\r
+        return num_to(cx, args, DToA.DTOSTR_FIXED, DToA.DTOSTR_FIXED,\r
+                      -20, MAX_PRECISION, 0);\r
+    }\r
+\r
+    private String jsFunction_toExponential(Context cx, Object[] args) {\r
+        /* We allow a larger range of precision than\r
+           ECMA requires; this is permitted by ECMA. */\r
+        return num_to(cx, args,\r
+                      DToA.DTOSTR_STANDARD_EXPONENTIAL,\r
+                      DToA.DTOSTR_EXPONENTIAL,\r
+                      0, MAX_PRECISION, 1);\r
+    }\r
+\r
+    private String jsFunction_toPrecision(Context cx, Object[] args) {\r
+        /* We allow a larger range of precision than\r
+           ECMA requires; this is permitted by ECMA. */\r
+        return num_to(cx, args, DToA.DTOSTR_STANDARD, DToA.DTOSTR_PRECISION,\r
+                      1, MAX_PRECISION, 0);\r
+    }\r
+\r
+    private String num_to(Context cx, Object[] args,\r
+                          int zeroArgMode, int oneArgMode,\r
+                          int precisionMin, int precisionMax,\r
+                          int precisionOffset)\r
+    {\r
+        int precision;\r
+\r
+        if (args.length == 0) {\r
+            precision = 0;\r
+            oneArgMode = zeroArgMode;\r
+        } else {\r
+            precision = ScriptRuntime.toInt32(args[0]);\r
+            if (precision < precisionMin || precision > precisionMax) {\r
+                String msg = ScriptRuntime.getMessage1(\r
+                    "msg.bad.precision", ScriptRuntime.toString(args[0]));\r
+                throw NativeGlobal.constructError(cx, "RangeError", msg, this);\r
+            }\r
+        }\r
+        StringBuffer result = new StringBuffer();\r
+        DToA.JS_dtostr(result, oneArgMode, precision + precisionOffset,\r
+                       doubleValue);\r
+        return result.toString();\r
+    }\r
+\r
+    protected String getIdName(int id) {\r
+        if (prototypeFlag) {\r
+            switch (id) {\r
+                case Id_constructor:     return "constructor"; \r
+                case Id_toString:        return "toString"; \r
+                case Id_valueOf:         return "valueOf"; \r
+                case Id_toLocaleString:  return "toLocaleString"; \r
+                case Id_toFixed:         return "toFixed";\r
+                case Id_toExponential:   return "toExponential";\r
+                case Id_toPrecision:     return "toPrecision";\r
+            }\r
+        }\r
+        return null;        \r
+    }\r
+\r
+// #string_id_map#\r
+\r
+    protected int mapNameToId(String s) {\r
+        if (!prototypeFlag) { return 0; }\r
+        int id;\r
+// #generated# Last update: 2001-04-23 10:40:45 CEST\r
+        L0: { id = 0; String X = null; int c;\r
+            L: switch (s.length()) {\r
+            case 7: c=s.charAt(0);\r
+                if (c=='t') { X="toFixed";id=Id_toFixed; }\r
+                else if (c=='v') { X="valueOf";id=Id_valueOf; }\r
+                break L;\r
+            case 8: X="toString";id=Id_toString; break L;\r
+            case 11: c=s.charAt(0);\r
+                if (c=='c') { X="constructor";id=Id_constructor; }\r
+                else if (c=='t') { X="toPrecision";id=Id_toPrecision; }\r
+                break L;\r
+            case 13: X="toExponential";id=Id_toExponential; break L;\r
+            case 14: X="toLocaleString";id=Id_toLocaleString; break L;\r
+            }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+        return id;\r
+    }\r
+\r
+    private static final int\r
+        Id_constructor           = 1,\r
+        Id_toString              = 2,\r
+        Id_valueOf               = 3,\r
+        Id_toLocaleString        = 4,\r
+        Id_toFixed               = 5,\r
+        Id_toExponential         = 6,\r
+        Id_toPrecision           = 7,\r
+        MAX_PROTOTYPE_ID         = 7;\r
+\r
+// #/string_id_map#\r
+\r
+    private static final double defaultValue = +0.0;\r
+    private double doubleValue;\r
+\r
+    private boolean prototypeFlag;\r
+}\r
diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java
new file mode 100644 (file)
index 0000000..a2b8dee
--- /dev/null
@@ -0,0 +1,291 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Norris Boyd\r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.Hashtable;\r
+\r
+/**\r
+ * This class implements the Object native object.\r
+ * See ECMA 15.2.\r
+ * @author Norris Boyd\r
+ */\r
+public class NativeObject extends IdScriptable {\r
+\r
+    public static void init(Context cx, Scriptable scope, boolean sealed) {\r
+        NativeObject obj = new NativeObject();\r
+        obj.prototypeFlag = true;\r
+        obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);\r
+    }\r
+\r
+    public String getClassName() {\r
+        return "Object";\r
+    }\r
+\r
+    public int methodArity(int methodId) {\r
+        if (prototypeFlag) {\r
+            switch (methodId) {\r
+                case Id_constructor:           return 1;\r
+                case Id_toString:              return 0;\r
+                case Id_toLocaleString:        return 0;\r
+                case Id_valueOf:               return 0;\r
+                case Id_hasOwnProperty:        return 1;\r
+                case Id_propertyIsEnumerable:  return 1;\r
+                case Id_isPrototypeOf:         return 1;\r
+            }\r
+        }\r
+        return super.methodArity(methodId);\r
+    }\r
+\r
+    public Object execMethod\r
+        (int methodId, IdFunction f,\r
+         Context cx, Scriptable scope, Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (prototypeFlag) {\r
+            switch (methodId) {\r
+                case Id_constructor:\r
+                    return jsConstructor(cx, args, f, thisObj == null);\r
+\r
+                case Id_toString:\r
+                    return jsFunction_toString(cx, thisObj);\r
+\r
+                case Id_toLocaleString:\r
+                    return jsFunction_toLocaleString(cx, thisObj);\r
+\r
+                case Id_valueOf:\r
+                    return jsFunction_valueOf(thisObj);\r
+\r
+                case Id_hasOwnProperty:\r
+                    return jsFunction_hasOwnProperty(thisObj, args);\r
+\r
+                case Id_propertyIsEnumerable:\r
+                    return jsFunction_propertyIsEnumerable(cx, thisObj, args);\r
+\r
+                case Id_isPrototypeOf:\r
+                    return jsFunction_isPrototypeOf(cx, thisObj, args);\r
+            }\r
+        }\r
+        return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
+    }\r
+\r
+    private static Object jsConstructor(Context cx, Object[] args,\r
+                                        Function ctorObj, boolean inNewExpr)\r
+        throws JavaScriptException\r
+    {\r
+        if (!inNewExpr) {\r
+            // FunctionObject.construct will set up parent, proto\r
+            return ctorObj.construct(cx, ctorObj.getParentScope(), args);\r
+        }\r
+        if (args.length == 0 || args[0] == null ||\r
+            args[0] == Undefined.instance)\r
+        {\r
+            return new NativeObject();\r
+        }\r
+        return ScriptRuntime.toObject(ctorObj.getParentScope(), args[0]);\r
+    }\r
+\r
+    public String toString() {\r
+        Context cx = Context.getCurrentContext();\r
+        if (cx != null)\r
+            return jsFunction_toString(cx, this);\r
+        else\r
+            return "[object " + getClassName() + "]";\r
+    }\r
+\r
+    private static String jsFunction_toString(Context cx, Scriptable thisObj)\r
+    {\r
+        if (cx.getLanguageVersion() != cx.VERSION_1_2)\r
+            return "[object " + thisObj.getClassName() + "]";\r
+\r
+        return toSource(cx, thisObj);\r
+    }\r
+\r
+    private static String jsFunction_toLocaleString(Context cx,\r
+                                                    Scriptable thisObj)\r
+    {\r
+        return jsFunction_toString(cx, thisObj);\r
+    }\r
+\r
+    private static String toSource(Context cx, Scriptable thisObj)\r
+    {\r
+        Scriptable m = thisObj;\r
+\r
+        if (cx.iterating == null)\r
+            cx.iterating = new Hashtable(31);\r
+\r
+        if (cx.iterating.get(m) == Boolean.TRUE) {\r
+            return "{}";  // stop recursion\r
+        } else {\r
+            StringBuffer result = new StringBuffer("{");\r
+            Object[] ids = m.getIds();\r
+\r
+            for(int i=0; i < ids.length; i++) {\r
+                if (i > 0)\r
+                    result.append(", ");\r
+\r
+                Object id = ids[i];\r
+                String idString = ScriptRuntime.toString(id);\r
+                Object p = (id instanceof String)\r
+                    ? m.get((String) id, m)\r
+                    : m.get(((Number) id).intValue(), m);\r
+                if (p instanceof String) {\r
+                    result.append(idString + ":\""\r
+                        + ScriptRuntime\r
+                          .escapeString(ScriptRuntime.toString(p))\r
+                        + "\"");\r
+                } else {\r
+                    /* wrap changes to cx.iterating in a try/finally\r
+                     * so that the reference always gets removed, and\r
+                     * we don't leak memory.  Good place for weak\r
+                     * references, if we had them.\r
+                     */\r
+                    try {\r
+                        cx.iterating.put(m, Boolean.TRUE); // stop recursion.\r
+                        result.append(idString + ":" + ScriptRuntime.toString(p));\r
+                    } finally {\r
+                        cx.iterating.remove(m);\r
+                    }\r
+                }\r
+            }\r
+            result.append('}');\r
+            return result.toString();\r
+        }\r
+    }\r
+\r
+    private static Object jsFunction_valueOf(Scriptable thisObj) {\r
+        return thisObj;\r
+    }\r
+\r
+    private static Object jsFunction_hasOwnProperty(Scriptable thisObj,\r
+                                                    Object[] args)\r
+    {\r
+        if (args.length != 0) {\r
+            if (thisObj.has(ScriptRuntime.toString(args[0]), thisObj))\r
+                return Boolean.TRUE;\r
+        }\r
+        return Boolean.FALSE;\r
+    }\r
+\r
+    private static Object jsFunction_propertyIsEnumerable(Context cx,\r
+                                                          Scriptable thisObj,\r
+                                                          Object[] args)\r
+    {\r
+        try {\r
+            if (args.length != 0) {\r
+                String name = ScriptRuntime.toString(args[0]);\r
+                if (thisObj.has(name, thisObj)) {\r
+                    int a = ((ScriptableObject)thisObj).getAttributes(name, thisObj);\r
+                    if ((a & ScriptableObject.DONTENUM) == 0)\r
+                        return Boolean.TRUE;\r
+                }\r
+            }\r
+        }\r
+        catch (PropertyException x) {\r
+        }\r
+        catch (ClassCastException x) {\r
+        }\r
+        return Boolean.FALSE;\r
+    }\r
+\r
+    private static Object jsFunction_isPrototypeOf(Context cx,\r
+                                                   Scriptable thisObj,\r
+                                                   Object[] args)\r
+    {\r
+        if (args.length != 0 && args[0] instanceof Scriptable) {\r
+            Scriptable v = (Scriptable) args[0];\r
+            do {\r
+                v = v.getPrototype();\r
+                if (v == thisObj)\r
+                    return Boolean.TRUE;\r
+            } while (v != null);\r
+        }\r
+        return Boolean.FALSE;\r
+    }\r
+\r
+    protected String getIdName(int id) {\r
+        if (prototypeFlag) {\r
+            switch (id) {\r
+                case Id_constructor:          return "constructor";\r
+                case Id_toString:             return "toString";\r
+                case Id_toLocaleString:       return "toLocaleString";\r
+                case Id_valueOf:              return "valueOf";\r
+                case Id_hasOwnProperty:       return "hasOwnProperty";\r
+                case Id_propertyIsEnumerable: return "propertyIsEnumerable";\r
+                case Id_isPrototypeOf:        return "isPrototypeOf";\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+// #string_id_map#\r
+\r
+    protected int mapNameToId(String s) {\r
+        if (!prototypeFlag) { return 0; }\r
+        int id;\r
+// #generated# Last update: 2001-04-24 12:37:03 GMT+02:00\r
+        L0: { id = 0; String X = null; int c;\r
+            L: switch (s.length()) {\r
+            case 7: X="valueOf";id=Id_valueOf; break L;\r
+            case 8: X="toString";id=Id_toString; break L;\r
+            case 11: X="constructor";id=Id_constructor; break L;\r
+            case 13: X="isPrototypeOf";id=Id_isPrototypeOf; break L;\r
+            case 14: c=s.charAt(0);\r
+                if (c=='h') { X="hasOwnProperty";id=Id_hasOwnProperty; }\r
+                else if (c=='t') { X="toLocaleString";id=Id_toLocaleString; }\r
+                break L;\r
+            case 20: X="propertyIsEnumerable";id=Id_propertyIsEnumerable; break L;\r
+            }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+        return id;\r
+    }\r
+\r
+    private static final int\r
+        Id_constructor           = 1,\r
+        Id_toString              = 2,\r
+        Id_toLocaleString        = 3,\r
+        Id_valueOf               = 4,\r
+        Id_hasOwnProperty        = 5,\r
+        Id_propertyIsEnumerable  = 6,\r
+        Id_isPrototypeOf         = 7,\r
+        MAX_PROTOTYPE_ID         = 7;\r
+\r
+// #/string_id_map#\r
+\r
+    private boolean prototypeFlag;\r
+}\r
diff --git a/src/org/mozilla/javascript/NativeScript.java b/src/org/mozilla/javascript/NativeScript.java
new file mode 100644 (file)
index 0000000..cb6d855
--- /dev/null
@@ -0,0 +1,286 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Roger Lawrence\r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.io.StringReader;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * The JavaScript Script object.\r
+ *\r
+ * Note that the C version of the engine uses XDR as the format used\r
+ * by freeze and thaw. Since this depends on the internal format of\r
+ * structures in the C runtime, we cannot duplicate it.\r
+ *\r
+ * Since we cannot replace 'this' as a result of the compile method,\r
+ * this class has a dual nature. Generated scripts will have a null\r
+ * 'script' field and will override 'exec' and 'call'. Scripts created\r
+ * using the JavaScript constructor will forward requests to the\r
+ * nonnull 'script' field.\r
+ *\r
+ * @since 1.3\r
+ * @author Norris Boyd\r
+ */\r
+\r
+public class NativeScript extends NativeFunction implements Script {\r
+\r
+    public static void init(Context cx, Scriptable scope, boolean sealed) {\r
+        NativeScript obj = new NativeScript();\r
+        obj.scopeInit(cx, scope, sealed);\r
+    }\r
+\r
+    public NativeScript() {\r
+    }\r
+\r
+    private void scopeInit(Context cx, Scriptable scope, boolean sealed) {\r
+        // prototypeIdShift != 0 serves as indicator of prototype instance\r
+        // and as id offset to take into account ids present in each instance\r
+        // of the base class NativeFunction. \r
+        // Not to depend on the assumption NativeFunction.maxInstanceId() != 0,\r
+        // 1 is added super.maxInstanceId() to make sure that \r
+        // prototypeIdShift != 0 in the NativeScript prototype.\r
+        // In a similar way the following methods use \r
+        // methodId - prototypeIdShift + 1, not methodId - prototypeIdShift\r
+        // to unshift prototype id to [1 .. MAX_PROTOTYPE_ID] interval\r
+        prototypeIdShift = super.maxInstanceId() + 1;\r
+        addAsPrototype(MAX_PROTOTYPE_ID + prototypeIdShift - 1, \r
+                       cx, scope, sealed);\r
+    }\r
+\r
+    /**\r
+     * Returns the name of this JavaScript class, "Script".\r
+     */\r
+    public String getClassName() {\r
+        return "Script";\r
+    }\r
+\r
+    /**\r
+     * Initialize script.\r
+     *\r
+     * Does nothing here, but scripts will override with code\r
+     * to initialize contained functions, regexp literals, etc.\r
+     */\r
+    public void initScript(Scriptable scope) {\r
+    }\r
+\r
+    public int methodArity(int methodId) {\r
+        if (prototypeIdShift != 0) {\r
+            switch (methodId - prototypeIdShift + 1) {\r
+                case Id_constructor: return 1;\r
+                case Id_toString:    return 0;\r
+                case Id_exec:        return 0;\r
+                case Id_compile:     return 1;\r
+            }\r
+        }\r
+        return super.methodArity(methodId);\r
+    }\r
+\r
+    public Object execMethod(int methodId, IdFunction f, Context cx,\r
+                             Scriptable scope, Scriptable thisObj, \r
+                             Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (prototypeIdShift != 0) {\r
+            switch (methodId - prototypeIdShift + 1) {\r
+                case Id_constructor:\r
+                    return jsConstructor(cx, scope, args);\r
+\r
+                case Id_toString:\r
+                    return realThis(thisObj, f, true).\r
+                        jsFunction_toString(cx, args);\r
+\r
+                case Id_exec:\r
+                    return realThis(thisObj, f, true).jsFunction_exec();\r
+\r
+                case Id_compile:\r
+                    return realThis(thisObj, f, false).\r
+                        jsFunction_compile(ScriptRuntime.toString(args, 0));\r
+            }\r
+        }\r
+\r
+        return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
+    }\r
+\r
+    private NativeScript realThis(Scriptable thisObj, IdFunction f, \r
+                                  boolean readOnly)\r
+    {\r
+        while (!(thisObj instanceof NativeScript)) {\r
+            thisObj = nextInstanceCheck(thisObj, f, readOnly);\r
+        }\r
+        return (NativeScript)thisObj;\r
+    }\r
+\r
+    /**\r
+     * The Java method defining the JavaScript Script constructor.\r
+     *\r
+     */\r
+    private static Object jsConstructor(Context cx, Scriptable scope, \r
+                                        Object[] args)\r
+    {\r
+        String source = args.length == 0\r
+                        ? ""\r
+                        : ScriptRuntime.toString(args[0]);\r
+        return compile(scope, source);\r
+    }\r
+\r
+    public static Script compile(Scriptable scope, String source) {\r
+        Context cx = Context.getContext();\r
+        StringReader reader = new StringReader(source);\r
+        try {\r
+            int[] linep = { 0 };\r
+            String filename = Context.getSourcePositionFromStack(linep);\r
+            if (filename == null) {\r
+                filename = "<Script object>";\r
+                linep[0] = 1;\r
+            }\r
+            Object securityDomain = \r
+                cx.getSecurityDomainForStackDepth(5);\r
+            return cx.compileReader(scope, reader, filename, linep[0], \r
+                                    securityDomain);\r
+        }\r
+        catch (IOException e) {\r
+            throw new RuntimeException("Unexpected IOException");\r
+        }\r
+    }\r
+\r
+    private Scriptable jsFunction_compile(String source) {\r
+        script = compile(null, source);\r
+        return this;\r
+    }\r
+\r
+    private Object jsFunction_exec() throws JavaScriptException {\r
+        throw Context.reportRuntimeError1\r
+            ("msg.cant.call.indirect", "exec");\r
+    }\r
+\r
+    private Object jsFunction_toString(Context cx, Object[] args)\r
+    {\r
+        Script thisScript = script;\r
+        if (thisScript == null) { thisScript = this; }\r
+        Scriptable scope = getTopLevelScope(this);\r
+        return cx.decompileScript(thisScript, scope, 0);\r
+    }\r
+    \r
+    /*\r
+     * Override method in NativeFunction to avoid ever returning "anonymous"\r
+     */\r
+    public String getFunctionName() {\r
+        return "";\r
+    }\r
+\r
+    /**\r
+     * Execute the script.\r
+     *\r
+     * Will be overridden by generated scripts; needed to implement Script.\r
+     */\r
+    public Object exec(Context cx, Scriptable scope)\r
+        throws JavaScriptException\r
+    {\r
+        return script == null ? Undefined.instance : script.exec(cx, scope);\r
+    }\r
+\r
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj,\r
+                       Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        return exec(cx, scope);\r
+    }\r
+\r
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        throw Context.reportRuntimeError0("msg.script.is.not.constructor");\r
+    }\r
+\r
+    protected String getIdName(int id) {\r
+        if (prototypeIdShift != 0) {\r
+            switch (id - prototypeIdShift + 1) {\r
+                case Id_constructor: return "constructor";\r
+                case Id_toString:    return "toString";\r
+                case Id_exec:        return "exec";\r
+                case Id_compile:     return "compile";\r
+            }\r
+        }\r
+        return super.getIdName(id);\r
+    }\r
+\r
+    protected int mapNameToId(String s) {\r
+        if (prototypeIdShift != 0) {\r
+            int id = toPrototypeId(s);\r
+            if (id != 0) { \r
+                // Shift [1, MAX_PROTOTYPE_ID] to\r
+                // [super.maxInstanceId() + 1,\r
+                //    super.maxInstanceId() + MAX_PROTOTYPE_ID]\r
+                return id + prototypeIdShift - 1; \r
+            }\r
+        }\r
+        return super.mapNameToId(s);\r
+    }\r
+\r
+// #string_id_map#\r
+\r
+    private static int toPrototypeId(String s) {\r
+        int id;\r
+// #generated# Last update: 2001-05-23 13:25:01 GMT+02:00\r
+        L0: { id = 0; String X = null;\r
+            L: switch (s.length()) {\r
+            case 4: X="exec";id=Id_exec; break L;\r
+            case 7: X="compile";id=Id_compile; break L;\r
+            case 8: X="toString";id=Id_toString; break L;\r
+            case 11: X="constructor";id=Id_constructor; break L;\r
+            }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+        return id;\r
+    }\r
+\r
+    private static final int\r
+        Id_constructor    = 1,\r
+        Id_toString       = 2,\r
+        Id_compile        = 3,\r
+        Id_exec           = 4,\r
+        MAX_PROTOTYPE_ID  = 4;\r
+\r
+// #/string_id_map#\r
+\r
+    private Script script;\r
+\r
+    private int prototypeIdShift;\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/NativeString.java b/src/org/mozilla/javascript/NativeString.java
new file mode 100644 (file)
index 0000000..17aa769
--- /dev/null
@@ -0,0 +1,950 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Tom Beauvais\r
+ * Norris Boyd\r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.lang.reflect.Method;\r
+import java.util.Vector;\r
+\r
+/**\r
+ * This class implements the String native object.\r
+ *\r
+ * See ECMA 15.5.\r
+ *\r
+ * String methods for dealing with regular expressions are\r
+ * ported directly from C. Latest port is from version 1.40.12.19\r
+ * in the JSFUN13_BRANCH.\r
+ *\r
+ * @author Mike McCabe\r
+ * @author Norris Boyd\r
+ */\r
+public class NativeString extends IdScriptable {\r
+\r
+    public static void init(Context cx, Scriptable scope, boolean sealed) {\r
+        NativeString obj = new NativeString();\r
+        obj.prototypeFlag = true;\r
+        obj.addAsPrototype(MAX_PROTOTYPE_ID, cx, scope, sealed);\r
+    }\r
+\r
+    /**\r
+     * Zero-parameter constructor: just used to create String.prototype\r
+     */\r
+    public NativeString() {\r
+        string = defaultValue;\r
+    }\r
+\r
+    public NativeString(String s) {\r
+        string = s;\r
+    }\r
+\r
+    public String getClassName() {\r
+        return "String";\r
+    }\r
+\r
+    protected void fillConstructorProperties\r
+        (Context cx, IdFunction ctor, boolean sealed)\r
+    {\r
+        addIdFunctionProperty(ctor, ConstructorId_fromCharCode, sealed);\r
+        super.fillConstructorProperties(cx, ctor, sealed);\r
+    }\r
+\r
+    protected int getIdDefaultAttributes(int id) {\r
+        if (id == Id_length) {\r
+            return DONTENUM | READONLY | PERMANENT;\r
+        }\r
+        return super.getIdDefaultAttributes(id);\r
+    }\r
+\r
+    protected Object getIdValue(int id) {\r
+        if (id == Id_length) {\r
+            return wrap_int(string.length());\r
+        }\r
+        return super.getIdValue(id);\r
+    }\r
+\r
+    public int methodArity(int methodId) {\r
+        if (prototypeFlag) {\r
+            switch (methodId) {\r
+                case ConstructorId_fromCharCode:   return 1;\r
+\r
+                case Id_constructor:               return 1;\r
+                case Id_toString:                  return 0;\r
+                case Id_valueOf:                   return 0;\r
+                case Id_charAt:                    return 1;\r
+                case Id_charCodeAt:                return 1;\r
+                case Id_indexOf:                   return 2;\r
+                case Id_lastIndexOf:               return 2;\r
+                case Id_split:                     return 1;\r
+                case Id_substring:                 return 2;\r
+                case Id_toLowerCase:               return 0;\r
+                case Id_toUpperCase:               return 0;\r
+                case Id_substr:                    return 2;\r
+                case Id_concat:                    return 1;\r
+                case Id_slice:                     return 2;\r
+                case Id_bold:                      return 0;\r
+                case Id_italics:                   return 0;\r
+                case Id_fixed:                     return 0;\r
+                case Id_strike:                    return 0;\r
+                case Id_small:                     return 0;\r
+                case Id_big:                       return 0;\r
+                case Id_blink:                     return 0;\r
+                case Id_sup:                       return 0;\r
+                case Id_sub:                       return 0;\r
+                case Id_fontsize:                  return 0;\r
+                case Id_fontcolor:                 return 0;\r
+                case Id_link:                      return 0;\r
+                case Id_anchor:                    return 0;\r
+                case Id_equals:                    return 1;\r
+                case Id_equalsIgnoreCase:          return 1;\r
+                case Id_match:                     return 1;\r
+                case Id_search:                    return 1;\r
+                case Id_replace:                   return 1;\r
+            }\r
+        }\r
+        return super.methodArity(methodId);\r
+    }\r
+\r
+    public Object execMethod\r
+        (int methodId, IdFunction f,\r
+         Context cx, Scriptable scope, Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (prototypeFlag) {\r
+            switch (methodId) {\r
+                case ConstructorId_fromCharCode: \r
+                    return jsStaticFunction_fromCharCode(args);\r
+\r
+                case Id_constructor:\r
+                    return jsConstructor(args, thisObj == null);\r
+\r
+                case Id_toString:\r
+                    return realThis(thisObj, f).jsFunction_toString();\r
+\r
+                case Id_valueOf:\r
+                    return realThis(thisObj, f).jsFunction_valueOf();\r
+\r
+                case Id_charAt: \r
+                    return jsFunction_charAt\r
+                        (ScriptRuntime.toString(thisObj), args);\r
+\r
+                case Id_charCodeAt: \r
+                    return wrap_double(jsFunction_charCodeAt\r
+                        (ScriptRuntime.toString(thisObj), args));\r
+\r
+                case Id_indexOf:\r
+                    return wrap_int(jsFunction_indexOf\r
+                        (ScriptRuntime.toString(thisObj), args));\r
+\r
+                case Id_lastIndexOf: \r
+                    return wrap_int(jsFunction_lastIndexOf\r
+                        (ScriptRuntime.toString(thisObj), args));\r
+\r
+                case Id_split: \r
+                    return jsFunction_split\r
+                        (cx, scope, ScriptRuntime.toString(thisObj), args);\r
+\r
+                case Id_substring:\r
+                    return jsFunction_substring\r
+                        (cx, ScriptRuntime.toString(thisObj), args);\r
+\r
+                case Id_toLowerCase:\r
+                    return jsFunction_toLowerCase\r
+                        (ScriptRuntime.toString(thisObj));\r
+\r
+                case Id_toUpperCase:\r
+                    return jsFunction_toUpperCase\r
+                        (ScriptRuntime.toString(thisObj));\r
+\r
+                case Id_substr: \r
+                    return jsFunction_substr\r
+                        (ScriptRuntime.toString(thisObj), args);\r
+\r
+                case Id_concat:\r
+                    return jsFunction_concat\r
+                        (ScriptRuntime.toString(thisObj), args);\r
+\r
+                case Id_slice:\r
+                    return jsFunction_slice\r
+                     (ScriptRuntime.toString(thisObj), args);\r
+\r
+                case Id_bold:\r
+                    return realThis(thisObj, f).tagify("b", null, null);\r
+\r
+                case Id_italics:\r
+                    return realThis(thisObj, f).tagify("i", null, null);\r
+\r
+                case Id_fixed:\r
+                    return realThis(thisObj, f).tagify("tt", null, null);\r
+\r
+                case Id_strike:\r
+                    return realThis(thisObj, f).tagify("strike", null, null);\r
+\r
+                case Id_small:\r
+                    return realThis(thisObj, f).tagify("small", null, null);\r
+\r
+                case Id_big:\r
+                    return realThis(thisObj, f).tagify("big", null, null);\r
+\r
+                case Id_blink:\r
+                    return realThis(thisObj, f).tagify("blink", null, null);\r
+\r
+                case Id_sup:\r
+                    return realThis(thisObj, f).tagify("sup", null, null);\r
+\r
+                case Id_sub:\r
+                    return realThis(thisObj, f).tagify("sub", null, null);\r
+\r
+                case Id_fontsize:\r
+                    return realThis(thisObj, f).\r
+                        tagify("font size", "font", \r
+                               ScriptRuntime.toString(args, 0));\r
+\r
+                case Id_fontcolor:\r
+                    return realThis(thisObj, f).\r
+                        tagify("font color", "font",\r
+                               ScriptRuntime.toString(args, 0));\r
+\r
+                case Id_link:\r
+                    return realThis(thisObj, f).\r
+                        tagify("a href", "a", ScriptRuntime.toString(args, 0));\r
+\r
+                case Id_anchor:\r
+                    return realThis(thisObj, f).\r
+                        tagify("a name", "a", ScriptRuntime.toString(args, 0));\r
+\r
+                case Id_equals:\r
+                    return wrap_boolean(jsFunction_equals\r
+                        (ScriptRuntime.toString(thisObj),\r
+                         ScriptRuntime.toString(args, 0)));\r
+\r
+                case Id_equalsIgnoreCase:\r
+                    return wrap_boolean(jsFunction_equalsIgnoreCase\r
+                        (ScriptRuntime.toString(thisObj),\r
+                         ScriptRuntime.toString(args, 0)));\r
+\r
+                case Id_match:\r
+                    return checkReProxy(cx).match(cx, scope, thisObj, args);\r
+\r
+                case Id_search:\r
+                    return checkReProxy(cx).search(cx, scope, thisObj, args);\r
+\r
+                case Id_replace:\r
+                    return checkReProxy(cx).replace(cx, scope, thisObj, args);\r
+            }\r
+        }\r
+        return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
+    }\r
+\r
+    private NativeString realThis(Scriptable thisObj, IdFunction f) {\r
+        while (!(thisObj instanceof NativeString)) {\r
+            thisObj = nextInstanceCheck(thisObj, f, true);\r
+        }\r
+        return (NativeString)thisObj;\r
+    }\r
+\r
+    private static RegExpProxy checkReProxy(Context cx) {\r
+        RegExpProxy result = cx.getRegExpProxy();\r
+        if (result == null) {\r
+            throw cx.reportRuntimeError0("msg.no.regexp");\r
+        }\r
+        return result;\r
+    }\r
+\r
+    /*\r
+     * HTML composition aids.\r
+     */\r
+    private String tagify(String begin, String end, String value) {\r
+        StringBuffer result = new StringBuffer();\r
+        result.append('<');\r
+        result.append(begin);\r
+        if (value != null) {\r
+            result.append("=\"");\r
+            result.append(value);\r
+            result.append('"');\r
+        }\r
+        result.append('>');\r
+        result.append(this.string);\r
+        result.append("</");\r
+        result.append((end == null) ? begin : end);\r
+        result.append('>');\r
+        return result.toString();\r
+    }\r
+\r
+    private static String jsStaticFunction_fromCharCode(Object[] args) {\r
+        int N = args.length;\r
+        if (N < 1)\r
+            return "";\r
+        StringBuffer s = new java.lang.StringBuffer(N);\r
+        for (int i=0; i < N; i++) {\r
+            s.append(ScriptRuntime.toUint16(args[i]));\r
+        }\r
+        return s.toString();\r
+    }\r
+\r
+    private static Object jsConstructor(Object[] args, boolean inNewExpr) {\r
+        String s = args.length >= 1\r
+            ? ScriptRuntime.toString(args[0])\r
+            : defaultValue;\r
+        if (inNewExpr) {\r
+            // new String(val) creates a new String object.\r
+            return new NativeString(s);\r
+        }\r
+        // String(val) converts val to a string value.\r
+        return s;\r
+    }\r
+\r
+    public String toString() {\r
+        return string;\r
+    }\r
+\r
+    /* ECMA 15.5.4.2: 'the toString function is not generic.' */\r
+    private String jsFunction_toString() {\r
+        return string;\r
+    }\r
+\r
+    private String jsFunction_valueOf() {\r
+        return string;\r
+    }\r
+\r
+    /* Make array-style property lookup work for strings.\r
+     * XXX is this ECMA?  A version check is probably needed. In js too.\r
+     */\r
+    public Object get(int index, Scriptable start) {\r
+        if (index >= 0 && index < string.length())\r
+            return string.substring(index, index + 1);\r
+        return super.get(index, start);\r
+    }\r
+\r
+    public void put(int index, Scriptable start, Object value) {\r
+        if (index >= 0 && index < string.length())\r
+            return;\r
+        super.put(index, start, value);\r
+    }\r
+\r
+    /*\r
+     *\r
+     * See ECMA 15.5.4.[4,5]\r
+     */\r
+    private static String jsFunction_charAt(String target, Object[] args)\r
+    {\r
+        // this'll return 0 if undefined... seems\r
+        // to be ECMA.\r
+        double pos = ScriptRuntime.toInteger(args, 0);\r
+\r
+        if (pos < 0 || pos >= target.length())\r
+            return "";\r
+\r
+        return target.substring((int)pos, (int)pos + 1);\r
+    }\r
+\r
+    private static double jsFunction_charCodeAt(String target, Object[] args)\r
+    {\r
+        double pos = ScriptRuntime.toInteger(args, 0);\r
+\r
+        if (pos < 0 || pos >= target.length()) {\r
+            return ScriptRuntime.NaN;\r
+        }\r
+\r
+        return target.charAt((int)pos);\r
+    }\r
+\r
+    /*\r
+     *\r
+     * See ECMA 15.5.4.6.  Uses Java String.indexOf()\r
+     * OPT to add - BMH searching from jsstr.c.\r
+     */\r
+    private static int jsFunction_indexOf(String target, Object[] args) {\r
+        String search = ScriptRuntime.toString(args, 0);\r
+        double begin = ScriptRuntime.toInteger(args, 1);\r
+\r
+        if (begin > target.length()) {\r
+            return -1;\r
+        } else {\r
+            if (begin < 0)\r
+                begin = 0;\r
+            return target.indexOf(search, (int)begin);\r
+        }\r
+    }\r
+\r
+    /*\r
+     *\r
+     * See ECMA 15.5.4.7\r
+     *\r
+     */\r
+    private static int jsFunction_lastIndexOf(String target, Object[] args) {\r
+        String search = ScriptRuntime.toString(args, 0);\r
+        double end = ScriptRuntime.toNumber(args, 1);\r
+\r
+        if (end != end || end > target.length())\r
+            end = target.length();\r
+        else if (end < 0)\r
+            end = 0;\r
+\r
+        return target.lastIndexOf(search, (int)end);\r
+    }\r
+\r
+    /*\r
+     * Used by js_split to find the next split point in target,\r
+     * starting at offset ip and looking either for the given\r
+     * separator substring, or for the next re match.  ip and\r
+     * matchlen must be reference variables (assumed to be arrays of\r
+     * length 1) so they can be updated in the leading whitespace or\r
+     * re case.\r
+     *\r
+     * Return -1 on end of string, >= 0 for a valid index of the next\r
+     * separator occurrence if found, or the string length if no\r
+     * separator is found.\r
+     */\r
+    private static int find_split(Scriptable scope, String target,\r
+                                  String separator, Object re,\r
+                                  int[] ip, int[] matchlen, boolean[] matched,\r
+                                  String[][] parensp)\r
+    {\r
+        int i = ip[0];\r
+        int length = target.length();\r
+        Context cx = Context.getContext();\r
+        int version = cx.getLanguageVersion();\r
+\r
+        /*\r
+         * Perl4 special case for str.split(' '), only if the user has selected\r
+         * JavaScript1.2 explicitly.  Split on whitespace, and skip leading w/s.\r
+         * Strange but true, apparently modeled after awk.\r
+         */\r
+        if (version == Context.VERSION_1_2 &&\r
+            re == null && separator.length() == 1 && separator.charAt(0) == ' ')\r
+        {\r
+            /* Skip leading whitespace if at front of str. */\r
+            if (i == 0) {\r
+                while (i < length && Character.isWhitespace(target.charAt(i)))\r
+                    i++;\r
+                ip[0] = i;\r
+            }\r
+\r
+            /* Don't delimit whitespace at end of string. */\r
+            if (i == length)\r
+                return -1;\r
+\r
+            /* Skip over the non-whitespace chars. */\r
+            while (i < length\r
+                   && !Character.isWhitespace(target.charAt(i)))\r
+                i++;\r
+\r
+            /* Now skip the next run of whitespace. */\r
+            int j = i;\r
+            while (j < length && Character.isWhitespace(target.charAt(j)))\r
+                j++;\r
+\r
+            /* Update matchlen to count delimiter chars. */\r
+            matchlen[0] = j - i;\r
+            return i;\r
+        }\r
+\r
+        /*\r
+         * Stop if past end of string.  If at end of string, we will\r
+         * return target length, so that\r
+         *\r
+         *  "ab,".split(',') => new Array("ab", "")\r
+         *\r
+         * and the resulting array converts back to the string "ab,"\r
+         * for symmetry.  NB: This differs from perl, which drops the\r
+         * trailing empty substring if the LIMIT argument is omitted.\r
+         */\r
+        if (i > length)\r
+            return -1;\r
+\r
+        /*\r
+         * Match a regular expression against the separator at or\r
+         * above index i.  Return -1 at end of string instead of\r
+         * trying for a match, so we don't get stuck in a loop.\r
+         */\r
+        if (re != null) {\r
+            return cx.getRegExpProxy().find_split(scope, target,\r
+                                                  separator, re,\r
+                                                  ip, matchlen, matched,\r
+                                                  parensp);\r
+        }\r
+\r
+        /*\r
+         * Deviate from ECMA by never splitting an empty string by any separator\r
+         * string into a non-empty array (an array of length 1 that contains the\r
+         * empty string).\r
+         */\r
+        if (version != Context.VERSION_DEFAULT && version < Context.VERSION_1_3\r
+            && length == 0)\r
+            return -1;\r
+\r
+        /*\r
+         * Special case: if sep is the empty string, split str into\r
+         * one character substrings.  Let our caller worry about\r
+         * whether to split once at end of string into an empty\r
+         * substring.\r
+         *\r
+         * For 1.2 compatibility, at the end of the string, we return the length as\r
+         * the result, and set the separator length to 1 -- this allows the caller\r
+         * to include an additional null string at the end of the substring list.\r
+         */\r
+        if (separator.length() == 0) {\r
+            if (version == Context.VERSION_1_2) {\r
+                if (i == length) {\r
+                    matchlen[0] = 1;\r
+                    return i;\r
+                }\r
+                return i + 1;\r
+            }\r
+            return (i == length) ? -1 : i + 1;\r
+        }\r
+\r
+        /* Punt to j.l.s.indexOf; return target length if seperator is\r
+         * not found.\r
+         */\r
+        if (ip[0] >= length)\r
+            return length;\r
+\r
+        i = target.indexOf(separator, ip[0]);\r
+\r
+        return (i != -1) ? i : length;\r
+    }\r
+\r
+    /*\r
+     * See ECMA 15.5.4.8.  Modified to match JS 1.2 - optionally takes\r
+     * a limit argument and accepts a regular expression as the split\r
+     * argument.\r
+     */\r
+    private static Object jsFunction_split(Context cx, Scriptable scope,\r
+                                           String target, Object[] args)\r
+    {\r
+        // create an empty Array to return;\r
+        Scriptable top = getTopLevelScope(scope);\r
+        Scriptable result = ScriptRuntime.newObject(cx, top, "Array", null);\r
+\r
+        // return an array consisting of the target if no separator given\r
+        // don't check against undefined, because we want\r
+        // 'fooundefinedbar'.split(void 0) to split to ['foo', 'bar']\r
+        if (args.length < 1) {\r
+            result.put(0, result, target);\r
+            return result;\r
+        }\r
+\r
+        // Use the second argument as the split limit, if given.\r
+        boolean limited = (args.length > 1) && (args[1] != Undefined.instance);\r
+        long limit = 0;  // Initialize to avoid warning.\r
+        if (limited) {\r
+            /* Clamp limit between 0 and 1 + string length. */\r
+            limit = ScriptRuntime.toUint32(args[1]);\r
+            if (limit > target.length())\r
+                limit = 1 + target.length();\r
+        }\r
+\r
+        String separator = null;\r
+        int[] matchlen = { 0 };\r
+        Object re = null;\r
+        RegExpProxy reProxy = cx.getRegExpProxy();\r
+        if (reProxy != null && reProxy.isRegExp(args[0])) {\r
+            re = args[0];\r
+        } else {\r
+            separator = ScriptRuntime.toString(args[0]);\r
+            matchlen[0] = separator.length();\r
+        }\r
+\r
+        // split target with separator or re\r
+        int[] ip = { 0 };\r
+        int match;\r
+        int len = 0;\r
+        boolean[] matched = { false };\r
+        String[][] parens = { null };\r
+        while ((match = find_split(scope, target, separator, re, ip,\r
+                                   matchlen, matched, parens)) >= 0)\r
+        {\r
+            if ((limited && len >= limit) || (match > target.length()))\r
+                break;\r
+\r
+            String substr;\r
+            if (target.length() == 0)\r
+                substr = target;\r
+            else\r
+                substr = target.substring(ip[0], match);\r
+\r
+            result.put(len, result, substr);\r
+            len++;\r
+        /*\r
+         * Imitate perl's feature of including parenthesized substrings\r
+         * that matched part of the delimiter in the new array, after the\r
+         * split substring that was delimited.\r
+         */\r
+            if (re != null && matched[0] == true) {\r
+                int size = parens[0].length;\r
+                for (int num = 0; num < size; num++) {\r
+                    if (limited && len >= limit)\r
+                        break;\r
+                    result.put(len, result, parens[0][num]);\r
+                    len++;\r
+                }\r
+                matched[0] = false;\r
+            }\r
+            ip[0] = match + matchlen[0];\r
+\r
+            if (cx.getLanguageVersion() < Context.VERSION_1_3\r
+                && cx.getLanguageVersion() != Context.VERSION_DEFAULT)\r
+            {\r
+        /*\r
+         * Deviate from ECMA to imitate Perl, which omits a final\r
+         * split unless a limit argument is given and big enough.\r
+         */\r
+                if (!limited && ip[0] == target.length())\r
+                    break;\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+\r
+    /*\r
+     * See ECMA 15.5.4.15\r
+     */\r
+    private static String jsFunction_substring(Context cx, String target,\r
+                                               Object[] args)\r
+    {\r
+        int length = target.length();\r
+        double start = ScriptRuntime.toInteger(args, 0);\r
+        double end;\r
+\r
+        if (start < 0)\r
+            start = 0;\r
+        else if (start > length)\r
+            start = length;\r
+\r
+        if (args.length <= 1 || args[1] == Undefined.instance) {\r
+            end = length;\r
+        } else {\r
+            end = ScriptRuntime.toInteger(args[1]);\r
+            if (end < 0)\r
+                end = 0;\r
+            else if (end > length)\r
+                end = length;\r
+\r
+            // swap if end < start\r
+            if (end < start) {\r
+                if (cx.getLanguageVersion() != Context.VERSION_1_2) {\r
+                    double temp = start;\r
+                    start = end;\r
+                    end = temp;\r
+                } else {\r
+                    // Emulate old JDK1.0 java.lang.String.substring()\r
+                    end = start;\r
+                }\r
+            }\r
+        }\r
+        return target.substring((int)start, (int)end);\r
+    }\r
+\r
+    /*\r
+     *\r
+     * See ECMA 15.5.4.[11,12]\r
+     */\r
+    private static String jsFunction_toLowerCase(String target) {\r
+        return target.toLowerCase();\r
+    }\r
+\r
+    private static String jsFunction_toUpperCase(String target) {\r
+        return target.toUpperCase();\r
+    }\r
+\r
+    public double jsGet_length() {\r
+        return (double) string.length();\r
+    }\r
+\r
+    /*\r
+     * Non-ECMA methods.\r
+     */\r
+    private static String jsFunction_substr(String target, Object[] args) {\r
+        if (args.length < 1)\r
+            return target;\r
+\r
+        double begin = ScriptRuntime.toInteger(args[0]);\r
+        double end;\r
+        int length = target.length();\r
+\r
+        if (begin < 0) {\r
+            begin += length;\r
+            if (begin < 0)\r
+                begin = 0;\r
+        } else if (begin > length) {\r
+            begin = length;\r
+        }\r
+\r
+        if (args.length == 1) {\r
+            end = length;\r
+        } else {\r
+            end = ScriptRuntime.toInteger(args[1]);\r
+            if (end < 0)\r
+                end = 0;\r
+            end += begin;\r
+            if (end > length)\r
+                end = length;\r
+        }\r
+\r
+        return target.substring((int)begin, (int)end);\r
+    }\r
+\r
+    /*\r
+     * Python-esque sequence operations.\r
+     */\r
+    private static String jsFunction_concat(String target, Object[] args) {\r
+        int N = args.length;\r
+        if (N == 0) { return target; }\r
+\r
+        StringBuffer result = new StringBuffer();\r
+        result.append(target);\r
+\r
+        for (int i = 0; i < N; i++)\r
+            result.append(ScriptRuntime.toString(args[i]));\r
+\r
+        return result.toString();\r
+    }\r
+\r
+    private static String jsFunction_slice(String target, Object[] args) {\r
+        if (args.length != 0) {\r
+            double begin = ScriptRuntime.toInteger(args[0]);\r
+            double end;\r
+            int length = target.length();\r
+            if (begin < 0) {\r
+                begin += length;\r
+                if (begin < 0)\r
+                    begin = 0;\r
+            } else if (begin > length) {\r
+                begin = length;\r
+            }\r
+\r
+            if (args.length == 1) {\r
+                end = length;\r
+            } else {\r
+                end = ScriptRuntime.toInteger(args[1]);\r
+                if (end < 0) {\r
+                    end += length;\r
+                    if (end < 0)\r
+                        end = 0;\r
+                } else if (end > length) {\r
+                    end = length;\r
+                }\r
+                if (end < begin)\r
+                    end = begin;\r
+            }\r
+            return target.substring((int)begin, (int)end);\r
+        }\r
+        return target;\r
+    }\r
+\r
+    private static boolean jsFunction_equals(String target, String strOther) {\r
+        return target.equals(strOther);\r
+    }\r
+\r
+\r
+    private static boolean jsFunction_equalsIgnoreCase(String target,\r
+                                                       String strOther)\r
+    {\r
+        return target.equalsIgnoreCase(strOther);\r
+    }\r
+\r
+    protected int maxInstanceId() { return MAX_INSTANCE_ID; }\r
+\r
+    protected String getIdName(int id) {\r
+        if (id == Id_length) { return "length"; }\r
+        \r
+        if (prototypeFlag) {\r
+            switch (id) {\r
+                case ConstructorId_fromCharCode: return "fromCharCode";\r
+\r
+                case Id_constructor:             return "constructor";\r
+                case Id_toString:                return "toString";\r
+                case Id_valueOf:                 return "valueOf";\r
+                case Id_charAt:                  return "charAt";\r
+                case Id_charCodeAt:              return "charCodeAt";\r
+                case Id_indexOf:                 return "indexOf";\r
+                case Id_lastIndexOf:             return "lastIndexOf";\r
+                case Id_split:                   return "split";\r
+                case Id_substring:               return "substring";\r
+                case Id_toLowerCase:             return "toLowerCase";\r
+                case Id_toUpperCase:             return "toUpperCase";\r
+                case Id_substr:                  return "substr";\r
+                case Id_concat:                  return "concat";\r
+                case Id_slice:                   return "slice";\r
+                case Id_bold:                    return "bold";\r
+                case Id_italics:                 return "italics";\r
+                case Id_fixed:                   return "fixed";\r
+                case Id_strike:                  return "strike";\r
+                case Id_small:                   return "small";\r
+                case Id_big:                     return "big";\r
+                case Id_blink:                   return "blink";\r
+                case Id_sup:                     return "sup";\r
+                case Id_sub:                     return "sub";\r
+                case Id_fontsize:                return "fontsize";\r
+                case Id_fontcolor:               return "fontcolor";\r
+                case Id_link:                    return "link";\r
+                case Id_anchor:                  return "anchor";\r
+                case Id_equals:                  return "equals";\r
+                case Id_equalsIgnoreCase:        return "equalsIgnoreCase";\r
+                case Id_match:                   return "match";\r
+                case Id_search:                  return "search";\r
+                case Id_replace:                 return "replace";\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private static final int\r
+        ConstructorId_fromCharCode   = -1,\r
+        Id_length                    =  1,\r
+        MAX_INSTANCE_ID              =  1;\r
+        \r
+\r
+    protected int mapNameToId(String s) {\r
+        if (s.equals("length")) { return Id_length; }\r
+        else if (prototypeFlag) { \r
+            return toPrototypeId(s); \r
+        }\r
+        return 0;\r
+    }\r
+\r
+// #string_id_map#\r
+\r
+    private static int toPrototypeId(String s) {\r
+        int id;\r
+// #generated# Last update: 2001-04-23 12:50:07 GMT+02:00\r
+        L0: { id = 0; String X = null; int c;\r
+            L: switch (s.length()) {\r
+            case 3: c=s.charAt(2);\r
+                if (c=='b') { if (s.charAt(0)=='s' && s.charAt(1)=='u') {id=Id_sub; break L0;} }\r
+                else if (c=='g') { if (s.charAt(0)=='b' && s.charAt(1)=='i') {id=Id_big; break L0;} }\r
+                else if (c=='p') { if (s.charAt(0)=='s' && s.charAt(1)=='u') {id=Id_sup; break L0;} }\r
+                break L;\r
+            case 4: c=s.charAt(0);\r
+                if (c=='b') { X="bold";id=Id_bold; }\r
+                else if (c=='l') { X="link";id=Id_link; }\r
+                break L;\r
+            case 5: switch (s.charAt(4)) {\r
+                case 'd': X="fixed";id=Id_fixed; break L;\r
+                case 'e': X="slice";id=Id_slice; break L;\r
+                case 'h': X="match";id=Id_match; break L;\r
+                case 'k': X="blink";id=Id_blink; break L;\r
+                case 'l': X="small";id=Id_small; break L;\r
+                case 't': X="split";id=Id_split; break L;\r
+                } break L;\r
+            case 6: switch (s.charAt(1)) {\r
+                case 'e': c=s.charAt(0);\r
+                    if (c=='l') { X="length";id=Id_length; }\r
+                    else if (c=='s') { X="search";id=Id_search; }\r
+                    break L;\r
+                case 'h': X="charAt";id=Id_charAt; break L;\r
+                case 'n': X="anchor";id=Id_anchor; break L;\r
+                case 'o': X="concat";id=Id_concat; break L;\r
+                case 'q': X="equals";id=Id_equals; break L;\r
+                case 't': X="strike";id=Id_strike; break L;\r
+                case 'u': X="substr";id=Id_substr; break L;\r
+                } break L;\r
+            case 7: switch (s.charAt(1)) {\r
+                case 'a': X="valueOf";id=Id_valueOf; break L;\r
+                case 'e': X="replace";id=Id_replace; break L;\r
+                case 'n': X="indexOf";id=Id_indexOf; break L;\r
+                case 't': X="italics";id=Id_italics; break L;\r
+                } break L;\r
+            case 8: c=s.charAt(0);\r
+                if (c=='f') { X="fontsize";id=Id_fontsize; }\r
+                else if (c=='t') { X="toString";id=Id_toString; }\r
+                break L;\r
+            case 9: c=s.charAt(0);\r
+                if (c=='f') { X="fontcolor";id=Id_fontcolor; }\r
+                else if (c=='s') { X="substring";id=Id_substring; }\r
+                break L;\r
+            case 10: X="charCodeAt";id=Id_charCodeAt; break L;\r
+            case 11: switch (s.charAt(2)) {\r
+                case 'L': X="toLowerCase";id=Id_toLowerCase; break L;\r
+                case 'U': X="toUpperCase";id=Id_toUpperCase; break L;\r
+                case 'n': X="constructor";id=Id_constructor; break L;\r
+                case 's': X="lastIndexOf";id=Id_lastIndexOf; break L;\r
+                } break L;\r
+            case 16: X="equalsIgnoreCase";id=Id_equalsIgnoreCase; break L;\r
+            }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+        return id;\r
+    }\r
+\r
+    private static final int\r
+        Id_constructor               = MAX_INSTANCE_ID + 1,\r
+        Id_toString                  = MAX_INSTANCE_ID + 2,\r
+        Id_valueOf                   = MAX_INSTANCE_ID + 3,\r
+        Id_charAt                    = MAX_INSTANCE_ID + 4,\r
+        Id_charCodeAt                = MAX_INSTANCE_ID + 5,\r
+        Id_indexOf                   = MAX_INSTANCE_ID + 6,\r
+        Id_lastIndexOf               = MAX_INSTANCE_ID + 7,\r
+        Id_split                     = MAX_INSTANCE_ID + 8,\r
+        Id_substring                 = MAX_INSTANCE_ID + 9,\r
+        Id_toLowerCase               = MAX_INSTANCE_ID + 10,\r
+        Id_toUpperCase               = MAX_INSTANCE_ID + 11,\r
+        Id_substr                    = MAX_INSTANCE_ID + 12,\r
+        Id_concat                    = MAX_INSTANCE_ID + 13,\r
+        Id_slice                     = MAX_INSTANCE_ID + 14,\r
+        Id_bold                      = MAX_INSTANCE_ID + 15,\r
+        Id_italics                   = MAX_INSTANCE_ID + 16,\r
+        Id_fixed                     = MAX_INSTANCE_ID + 17,\r
+        Id_strike                    = MAX_INSTANCE_ID + 18,\r
+        Id_small                     = MAX_INSTANCE_ID + 19,\r
+        Id_big                       = MAX_INSTANCE_ID + 20,\r
+        Id_blink                     = MAX_INSTANCE_ID + 21,\r
+        Id_sup                       = MAX_INSTANCE_ID + 22,\r
+        Id_sub                       = MAX_INSTANCE_ID + 23,\r
+        Id_fontsize                  = MAX_INSTANCE_ID + 24,\r
+        Id_fontcolor                 = MAX_INSTANCE_ID + 25,\r
+        Id_link                      = MAX_INSTANCE_ID + 26,\r
+        Id_anchor                    = MAX_INSTANCE_ID + 27,\r
+        Id_equals                    = MAX_INSTANCE_ID + 28,\r
+        Id_equalsIgnoreCase          = MAX_INSTANCE_ID + 29,\r
+        Id_match                     = MAX_INSTANCE_ID + 30,\r
+        Id_search                    = MAX_INSTANCE_ID + 31,\r
+        Id_replace                   = MAX_INSTANCE_ID + 32,\r
+\r
+        MAX_PROTOTYPE_ID             = MAX_INSTANCE_ID + 32;\r
+\r
+// #/string_id_map#\r
+\r
+    private static final String defaultValue = "";\r
+\r
+    private String string;\r
+    \r
+    private boolean prototypeFlag;\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/NativeWith.java b/src/org/mozilla/javascript/NativeWith.java
new file mode 100644 (file)
index 0000000..5fb8407
--- /dev/null
@@ -0,0 +1,195 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.lang.reflect.Method;\r
+\r
+/**\r
+ * This class implements the object lookup required for the\r
+ * <code>with</code> statement.\r
+ * It simply delegates every action to its prototype except\r
+ * for operations on its parent.\r
+ */\r
+public class NativeWith implements Scriptable, IdFunctionMaster { \r
+    \r
+    public static void init(Context cx, Scriptable scope, boolean sealed) {\r
+        NativeWith obj = new NativeWith();\r
+        obj.prototypeFlag = true;\r
+\r
+        IdFunction ctor = new IdFunction(obj, "constructor", Id_constructor);\r
+        ctor.initAsConstructor(scope, obj);\r
+        if (sealed) { ctor.sealObject(); }\r
+        \r
+        obj.setParentScope(ctor);\r
+        obj.setPrototype(ScriptableObject.getObjectPrototype(scope));\r
+        \r
+        ScriptableObject.defineProperty(scope, "With", ctor,\r
+                                        ScriptableObject.DONTENUM);\r
+    }\r
+\r
+    public NativeWith() {\r
+    }\r
+\r
+    public NativeWith(Scriptable parent, Scriptable prototype) {\r
+        this.parent = parent;\r
+        this.prototype = prototype;\r
+    }\r
+\r
+    public String getClassName() {\r
+        return "With";\r
+    }\r
+\r
+    public boolean has(String id, Scriptable start) {\r
+        if (start == this)\r
+            start = prototype;\r
+        return prototype.has(id, start);\r
+    }\r
+\r
+    public boolean has(int index, Scriptable start) {\r
+        if (start == this)\r
+            start = prototype;\r
+        return prototype.has(index, start);\r
+    }\r
+\r
+    public Object get(String id, Scriptable start) {\r
+        if (start == this)\r
+            start = prototype;\r
+        return prototype.get(id, start);\r
+    }\r
+\r
+    public Object get(int index, Scriptable start) {\r
+        if (start == this)\r
+            start = prototype;\r
+        return prototype.get(index, start);\r
+    }\r
+\r
+    public void put(String id, Scriptable start, Object value) {\r
+        if (start == this)\r
+            start = prototype;\r
+        prototype.put(id, start, value);\r
+    }\r
+\r
+    public void put(int index, Scriptable start, Object value) {\r
+        if (start == this)\r
+            start = prototype;\r
+        prototype.put(index, start, value);\r
+    }\r
+\r
+    public void delete(String id) {\r
+        prototype.delete(id);\r
+    }\r
+\r
+    public void delete(int index) {\r
+        prototype.delete(index);\r
+    }\r
+\r
+    public Scriptable getPrototype() {\r
+        return prototype;\r
+    }\r
+\r
+    public void setPrototype(Scriptable prototype) {\r
+        this.prototype = prototype;\r
+    }\r
+\r
+    public Scriptable getParentScope() {\r
+        return parent;\r
+    }\r
+\r
+    public void setParentScope(Scriptable parent) {\r
+        this.parent = parent;\r
+    }\r
+\r
+    public Object[] getIds() {\r
+        return prototype.getIds();\r
+    }\r
+\r
+    public Object getDefaultValue(Class typeHint) {\r
+        return prototype.getDefaultValue(typeHint);\r
+    }\r
+\r
+    public boolean hasInstance(Scriptable value) {\r
+        return prototype.hasInstance(value);\r
+    }\r
+\r
+    public Object execMethod(int methodId, IdFunction function, Context cx,\r
+                             Scriptable scope, Scriptable thisObj, \r
+                             Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (prototypeFlag) {\r
+            if (methodId == Id_constructor) {\r
+                throw Context.reportRuntimeError1\r
+                    ("msg.cant.call.indirect", "With");\r
+            }\r
+        }\r
+        throw IdFunction.onBadMethodId(this, methodId);\r
+    }\r
+    \r
+    public int methodArity(int methodId) {\r
+        if (prototypeFlag) {\r
+            if (methodId == Id_constructor) { return 0; }\r
+        }\r
+        return -1;\r
+    }\r
+\r
+    public static Object newWithSpecial(Context cx, Object[] args, \r
+                                        Function ctorObj, boolean inNewExpr)\r
+    {\r
+        if (!inNewExpr) {\r
+            throw Context.reportRuntimeError1("msg.only.from.new", "With");\r
+        }\r
+        \r
+        ScriptRuntime.checkDeprecated(cx, "With");\r
+        \r
+        Scriptable scope = ScriptableObject.getTopLevelScope(ctorObj);\r
+        NativeWith thisObj = new NativeWith();\r
+        thisObj.setPrototype(args.length == 0\r
+                             ? ScriptableObject.getClassPrototype(scope,\r
+                                                                  "Object")\r
+                             : ScriptRuntime.toObject(scope, args[0]));\r
+        thisObj.setParentScope(scope);\r
+        return thisObj;\r
+    }\r
+    \r
+    private static final int    \r
+        Id_constructor = 1;\r
+\r
+    private Scriptable prototype;\r
+    private Scriptable parent;\r
+    private Scriptable constructor;\r
+\r
+    private boolean prototypeFlag;\r
+}\r
diff --git a/src/org/mozilla/javascript/Node.java b/src/org/mozilla/javascript/Node.java
new file mode 100644 (file)
index 0000000..446ef0a
--- /dev/null
@@ -0,0 +1,489 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Roger Lawrence\r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This class implements the root of the intermediate representation.\r
+ *\r
+ * @author Norris Boyd\r
+ * @author Mike McCabe\r
+ */\r
+\r
+public class Node implements Cloneable {\r
+\r
+    public Node(int nodeType) {\r
+        type = nodeType;\r
+    }\r
+\r
+    public Node(int nodeType, Node child) {\r
+        type = nodeType;\r
+        first = last = child;\r
+        child.next = null;\r
+    }\r
+\r
+    public Node(int nodeType, Node left, Node right) {\r
+        type = nodeType;\r
+        first = left;\r
+        last = right;\r
+        left.next = right;\r
+        right.next = null;\r
+    }\r
+\r
+    public Node(int nodeType, Node left, Node mid, Node right) {\r
+        type = nodeType;\r
+        first = left;\r
+        last = right;\r
+        left.next = mid;\r
+        mid.next = right;\r
+        right.next = null;\r
+    }\r
+\r
+    public Node(int nodeType, Object datum) {\r
+        type = nodeType;\r
+        this.datum = datum;\r
+    }\r
+\r
+    public Node(int nodeType, Node child, Object datum) {\r
+        this(nodeType, child);\r
+        this.datum = datum;\r
+    }\r
+\r
+    public Node(int nodeType, Node left, Node right, Object datum) {\r
+        this(nodeType, left, right);\r
+        this.datum = datum;\r
+    }\r
+\r
+    public int getType() {\r
+        return type;\r
+    }\r
+\r
+    public void setType(int type) {\r
+        this.type = type;\r
+    }\r
+\r
+    public boolean hasChildren() {\r
+        return first != null;\r
+    }\r
+\r
+    public Node getFirstChild() {\r
+        return first;\r
+    }\r
+\r
+    public Node getLastChild() {\r
+        return last;\r
+    }\r
+\r
+    public Node getNextSibling() {\r
+        return next;\r
+    }\r
+\r
+    public Node getChildBefore(Node child) {\r
+        if (child == first)\r
+            return null;\r
+        Node n = first;\r
+        while (n.next != child) {\r
+            n = n.next;\r
+            if (n == null)\r
+                throw new RuntimeException("node is not a child");\r
+        }\r
+        return n;\r
+    }\r
+\r
+    public Node getLastSibling() {\r
+        Node n = this;\r
+        while (n.next != null) {\r
+            n = n.next;\r
+        }\r
+        return n;\r
+    }\r
+\r
+    public ShallowNodeIterator getChildIterator() {\r
+        return new ShallowNodeIterator(first);\r
+    }\r
+\r
+    public PreorderNodeIterator getPreorderIterator() {\r
+        return new PreorderNodeIterator(this);\r
+    }\r
+\r
+    public void addChildToFront(Node child) {\r
+        child.next = first;\r
+        first = child;\r
+        if (last == null) {\r
+            last = child;\r
+        }\r
+    }\r
+\r
+    public void addChildToBack(Node child) {\r
+        child.next = null;\r
+        if (last == null) {\r
+            first = last = child;\r
+            return;\r
+        }\r
+        last.next = child;\r
+        last = child;\r
+    }\r
+\r
+    public void addChildrenToFront(Node children) {\r
+        Node lastSib = children.getLastSibling();\r
+        lastSib.next = first;\r
+        first = children;\r
+        if (last == null) {\r
+            last = lastSib;\r
+        }\r
+    }\r
+\r
+    public void addChildrenToBack(Node children) {\r
+        if (last != null) {\r
+            last.next = children;\r
+        }\r
+        last = children.getLastSibling();\r
+        if (first == null) {\r
+            first = children;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Add 'child' before 'node'.\r
+     */\r
+    public void addChildBefore(Node newChild, Node node) {\r
+        if (newChild.next != null)\r
+            throw new RuntimeException(\r
+                      "newChild had siblings in addChildBefore");\r
+        if (first == node) {\r
+            newChild.next = first;\r
+            first = newChild;\r
+            return;\r
+        }\r
+        Node prev = getChildBefore(node);\r
+        addChildAfter(newChild, prev);\r
+    }\r
+\r
+    /**\r
+     * Add 'child' after 'node'.\r
+     */\r
+    public void addChildAfter(Node newChild, Node node) {\r
+        if (newChild.next != null)\r
+            throw new RuntimeException(\r
+                      "newChild had siblings in addChildAfter");\r
+        newChild.next = node.next;\r
+        node.next = newChild;\r
+        if (last == node)\r
+            last = newChild;\r
+    }\r
+\r
+    public void removeChild(Node child) {\r
+        Node prev = getChildBefore(child);\r
+        if (prev == null)\r
+            first = first.next;\r
+        else\r
+            prev.next = child.next;\r
+        if (child == last) last = prev;\r
+        child.next = null;\r
+    }\r
+\r
+    public void replaceChild(Node child, Node newChild) {\r
+        newChild.next = child.next;\r
+        if (child == first) {\r
+            first = newChild;\r
+        } else {\r
+            Node prev = getChildBefore(child);\r
+            prev.next = newChild;\r
+        }\r
+        if (child == last)\r
+            last = newChild;\r
+        child.next = null;\r
+    }\r
+\r
+    public static final int\r
+        TARGET_PROP       =  1,\r
+        BREAK_PROP        =  2,\r
+        CONTINUE_PROP     =  3,\r
+        ENUM_PROP         =  4,\r
+        FUNCTION_PROP     =  5,\r
+        TEMP_PROP         =  6,\r
+        LOCAL_PROP        =  7,\r
+        CODEOFFSET_PROP   =  8,\r
+        FIXUPS_PROP       =  9,\r
+        VARS_PROP         = 10,\r
+        USES_PROP         = 11,\r
+        REGEXP_PROP       = 12,\r
+        CASES_PROP        = 13,\r
+        DEFAULT_PROP      = 14,\r
+        CASEARRAY_PROP    = 15,\r
+        SOURCENAME_PROP   = 16,\r
+        SOURCE_PROP       = 17,\r
+        TYPE_PROP         = 18,\r
+        SPECIAL_PROP_PROP = 19,\r
+        LABEL_PROP        = 20,\r
+        FINALLY_PROP      = 21,\r
+        LOCALCOUNT_PROP   = 22,\r
+    /*\r
+        the following properties are defined and manipulated by the\r
+        optimizer -\r
+        TARGETBLOCK_PROP - the block referenced by a branch node\r
+        VARIABLE_PROP - the variable referenced by a BIND or NAME node\r
+        LASTUSE_PROP - that variable node is the last reference before\r
+                        a new def or the end of the block\r
+        ISNUMBER_PROP - this node generates code on Number children and\r
+                        delivers a Number result (as opposed to Objects)\r
+        DIRECTCALL_PROP - this call node should emit code to test the function\r
+                          object against the known class and call diret if it\r
+                          matches.\r
+    */\r
+\r
+        TARGETBLOCK_PROP  = 23,\r
+        VARIABLE_PROP     = 24,\r
+        LASTUSE_PROP      = 25,\r
+        ISNUMBER_PROP     = 26,\r
+        DIRECTCALL_PROP   = 27,\r
+\r
+        BASE_LINENO_PROP  = 28,\r
+        END_LINENO_PROP   = 29,\r
+        SPECIALCALL_PROP  = 30,\r
+        DEBUGSOURCE_PROP  = 31;\r
+\r
+    public static final int    // this value of the ISNUMBER_PROP specifies\r
+        BOTH = 0,               // which of the children are Number types\r
+        LEFT = 1,\r
+        RIGHT = 2;\r
+\r
+    private static String propNames[];\r
+    \r
+    private static final String propToString(int propType) {\r
+        if (Context.printTrees && propNames == null) {\r
+            // If Context.printTrees is false, the compiler\r
+            // can remove all these strings.\r
+            String[] a = {\r
+                "target",\r
+                "break",\r
+                "continue",\r
+                "enum",\r
+                "function",\r
+                "temp",\r
+                "local",\r
+                "codeoffset",\r
+                "fixups",\r
+                "vars",\r
+                "uses",\r
+                "regexp",\r
+                "cases",\r
+                "default",\r
+                "casearray",\r
+                "sourcename",\r
+                "source",\r
+                "type",\r
+                "special_prop",\r
+                "label",\r
+                "finally",\r
+                "localcount",\r
+                "targetblock",\r
+                "variable",\r
+                "lastuse",\r
+                "isnumber",\r
+                "directcall",\r
+                "base_lineno",\r
+                "end_lineno",\r
+                "specialcall"\r
+            };\r
+            propNames = a;\r
+        }\r
+        return propNames[propType-1];\r
+    }\r
+\r
+    public Object getProp(int propType) {\r
+        if (props == null)\r
+            return null;\r
+        return props.getObject(propType);\r
+    }\r
+\r
+    public int getIntProp(int propType, int defaultValue) {\r
+        if (props == null)\r
+            return defaultValue;\r
+        return props.getInt(propType, defaultValue);\r
+    }\r
+\r
+    public int getExistingIntProp(int propType) {\r
+        return props.getExistingInt(propType);\r
+    }\r
+\r
+    public void putProp(int propType, Object prop) {\r
+        if (props == null)\r
+            props = new UintMap(2);\r
+        if (prop == null)\r
+            props.remove(propType);\r
+        else\r
+            props.put(propType, prop);\r
+    }\r
+\r
+    public void putIntProp(int propType, int prop) {\r
+        if (props == null)\r
+            props = new UintMap(2);\r
+        props.put(propType, prop);\r
+    }\r
+\r
+    public Object getDatum() {\r
+        return datum;\r
+    }\r
+\r
+    public void setDatum(Object datum) {\r
+        this.datum = datum;\r
+    }\r
+\r
+    public int getInt() {\r
+        return ((Number) datum).intValue();\r
+    }\r
+\r
+    public double getDouble() {\r
+        return ((Number) datum).doubleValue();\r
+    }\r
+\r
+    public long getLong() {\r
+        return ((Number) datum).longValue();\r
+    }\r
+\r
+    public String getString() {\r
+        return (String) datum;\r
+    }\r
+\r
+    public Node cloneNode() {\r
+        Node result;\r
+        try {\r
+            result = (Node) super.clone();\r
+            result.next = null;\r
+            result.first = null;\r
+            result.last = null;\r
+        }\r
+        catch (CloneNotSupportedException e) {\r
+            throw new RuntimeException(e.getMessage());\r
+        }\r
+        return result;\r
+    }\r
+    public String toString() {\r
+        return super.toString();\r
+    }\r
+    /*\r
+    public String toString() {\r
+        if (Context.printTrees) {\r
+            StringBuffer sb = new StringBuffer(TokenStream.tokenToName(type));\r
+            if (type == TokenStream.TARGET) {\r
+                sb.append(' ');\r
+                sb.append(hashCode());\r
+            }\r
+            if (datum != null) {\r
+                sb.append(' ');\r
+                sb.append(datum.toString());\r
+            }\r
+            if (props == null)\r
+                return sb.toString();\r
+\r
+            int[] keys = props.getKeys();\r
+            for (int i = 0; i != keys.length; ++i) {\r
+                int key = keys[i];\r
+                sb.append(" [");\r
+                sb.append(propToString(key));\r
+                sb.append(": ");\r
+                switch (key) {\r
+                    case FIXUPS_PROP :      // can't add this as it recurses\r
+                        sb.append("fixups property");\r
+                        break;\r
+                    case SOURCE_PROP :      // can't add this as it has unprintables\r
+                        sb.append("source property");\r
+                        break;\r
+                    case TARGETBLOCK_PROP : // can't add this as it recurses\r
+                        sb.append("target block property");\r
+                        break;\r
+                    case LASTUSE_PROP :     // can't add this as it is dull\r
+                        sb.append("last use property");\r
+                        break;\r
+                    default :\r
+                        if (props.isObjectType(key)) {\r
+                            sb.append(props.getObject(key).toString());\r
+                        }\r
+                        else {\r
+                            sb.append(props.getExistingInt(key));\r
+                        }\r
+                        break;\r
+                }\r
+                sb.append(']');\r
+            }\r
+            return sb.toString();\r
+        }\r
+        return null;\r
+    }\r
+    */\r
+    public String toStringTree() {\r
+        return toStringTreeHelper(0);\r
+    }\r
+    \r
+\r
+    private String toStringTreeHelper(int level) {\r
+        if (Context.printTrees) {\r
+            StringBuffer s = new StringBuffer();\r
+            for (int i=0; i < level; i++) {\r
+                s.append("    ");\r
+            }\r
+            s.append(toString());\r
+            s.append('\n');\r
+            ShallowNodeIterator iterator = getChildIterator();\r
+            if (iterator != null) {\r
+                while (iterator.hasMoreElements()) {\r
+                    Node n = (Node) iterator.nextElement();\r
+                    if (n.getType() == TokenStream.FUNCTION) {\r
+                        Node p = (Node) n.getProp(Node.FUNCTION_PROP);\r
+                        if (p != null)\r
+                            n = p;\r
+                    }\r
+                    s.append(n.toStringTreeHelper(level+1));\r
+                }\r
+            }\r
+            return s.toString();\r
+        }\r
+        return "";\r
+    }\r
+\r
+    public Node getFirst()  { return first; }\r
+    public Node getNext()   { return next; }\r
+\r
+    protected int type;         // type of the node; TokenStream.NAME for example\r
+    protected Node next;        // next sibling\r
+    protected Node first;       // first element of a linked list of children\r
+    protected Node last;        // last element of a linked list of children\r
+    protected UintMap props;\r
+    protected Object datum;     // encapsulated data; depends on type\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/NodeTransformer.java b/src/org/mozilla/javascript/NodeTransformer.java
new file mode 100644 (file)
index 0000000..e73fdc5
--- /dev/null
@@ -0,0 +1,743 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Roger Lawrence\r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.Hashtable;\r
+import java.util.Stack;\r
+import java.util.Vector;\r
+\r
+/**\r
+ * This class transforms a tree to a lower-level representation for codegen.\r
+ *\r
+ * @see Node\r
+ * @author Norris Boyd\r
+ */\r
+\r
+public class NodeTransformer {\r
+    \r
+    /**\r
+     * Return new instance of this class. So that derived classes\r
+     * can override methods of the transformer.\r
+     */\r
+    public NodeTransformer newInstance() {\r
+        return new NodeTransformer();\r
+    }\r
+    \r
+    public IRFactory createIRFactory(TokenStream ts, Scriptable scope) {\r
+        return new IRFactory(ts, scope);\r
+    }\r
+\r
+    public Node transform(Node tree, Node enclosing, TokenStream ts,\r
+                          Scriptable scope) \r
+    {\r
+        loops = new Stack();\r
+        loopEnds = new Stack();\r
+        inFunction = tree.getType() == TokenStream.FUNCTION;\r
+        if (!inFunction) {\r
+            addVariables(tree, getVariableTable(tree));\r
+        }\r
+        irFactory = createIRFactory(ts, scope);\r
+\r
+        // to save against upchecks if no finally blocks are used.\r
+        boolean hasFinally = false;\r
+\r
+        PreorderNodeIterator iterator = tree.getPreorderIterator();\r
+        Node node;\r
+        while ((node = iterator.nextNode()) != null) {\r
+            int type = node.getType();\r
+\r
+          typeswitch:\r
+            switch (type) {\r
+\r
+              case TokenStream.FUNCTION:\r
+                if (node == tree) {\r
+                    // Add the variables to variable table, the\r
+                    // parameters were added earlier.\r
+                    VariableTable vars = getVariableTable(tree);\r
+                    addVariables(tree, vars);\r
+\r
+                    // Add return to end if needed.\r
+                    Node stmts = node.getLastChild();\r
+                    Node lastStmt = stmts.getLastChild();\r
+                    if (lastStmt == null ||\r
+                        lastStmt.getType() != TokenStream.RETURN)\r
+                    {\r
+                        stmts.addChildToBack(new Node(TokenStream.RETURN));\r
+                    }\r
+\r
+                } else {\r
+                    FunctionNode fnNode = (FunctionNode)\r
+                                          node.getProp(Node.FUNCTION_PROP);\r
+                    if (inFunction) {\r
+                        // Functions containing other functions require \r
+                        //  activation objects \r
+                        ((FunctionNode) tree).setRequiresActivation(true);\r
+\r
+                        // Nested functions must check their 'this' value to\r
+                        //  insure it is not an activation object:\r
+                        //  see 10.1.6 Activation Object\r
+                        fnNode.setCheckThis(true);\r
+                    }\r
+                    addParameters(fnNode);\r
+                    NodeTransformer inner = newInstance();\r
+                    fnNode = (FunctionNode) \r
+                            inner.transform(fnNode, tree, ts, scope);\r
+                    node.putProp(Node.FUNCTION_PROP, fnNode);\r
+                    Vector fns = (Vector) tree.getProp(Node.FUNCTION_PROP);\r
+                    if (fns == null) {\r
+                        fns = new Vector(7);\r
+                        tree.putProp(Node.FUNCTION_PROP, fns);\r
+                    }\r
+                    fns.addElement(fnNode);\r
+                }\r
+                break;\r
+\r
+              case TokenStream.LABEL:\r
+              {\r
+                Node child = node.getFirstChild();\r
+                node.removeChild(child);\r
+                String id = child.getString();\r
+\r
+                // check against duplicate labels...\r
+                for (int i=loops.size()-1; i >= 0; i--) {\r
+                    Node n = (Node) loops.elementAt(i);\r
+                    if (n.getType() == TokenStream.LABEL) {\r
+                        String otherId = (String)n.getProp(Node.LABEL_PROP);\r
+                        if (id.equals(otherId)) {\r
+                            String message = Context.getMessage1(\r
+                                "msg.dup.label", id);\r
+                            reportMessage(Context.getContext(), message, node, \r
+                                          tree, true, scope);\r
+                            break typeswitch;\r
+                        }\r
+                    }\r
+                }\r
+\r
+                node.putProp(Node.LABEL_PROP, id);\r
+\r
+                /* Make a target and put it _after_ the following\r
+                 * node.  And in the LABEL node, so breaks get the\r
+                 * right target.\r
+                 */\r
+                Node breakTarget = new Node(TokenStream.TARGET);\r
+                Node parent = iterator.getCurrentParent();\r
+                Node next = node.getNextSibling();\r
+                while (next != null &&\r
+                       (next.getType() == TokenStream.LABEL ||\r
+                        next.getType() == TokenStream.TARGET))\r
+                    next = next.getNextSibling();\r
+                if (next == null)\r
+                    break;\r
+                parent.addChildAfter(breakTarget, next);\r
+                node.putProp(Node.BREAK_PROP, breakTarget);\r
+                \r
+                if (next.getType() == TokenStream.LOOP) {\r
+                    node.putProp(Node.CONTINUE_PROP, \r
+                                 next.getProp(Node.CONTINUE_PROP));\r
+                }   \r
+\r
+                loops.push(node);\r
+                loopEnds.push(breakTarget);\r
+\r
+                break;\r
+              }\r
+\r
+              case TokenStream.SWITCH:\r
+              {\r
+                Node breakTarget = new Node(TokenStream.TARGET);\r
+                Node parent = iterator.getCurrentParent();\r
+                parent.addChildAfter(breakTarget, node);\r
+\r
+                // make all children siblings except for selector\r
+                Node sib = node;\r
+                Node child = node.getFirstChild().next;\r
+                while (child != null) {\r
+                    Node next = child.next;\r
+                    node.removeChild(child);\r
+                    parent.addChildAfter(child, sib);\r
+                    sib = child;\r
+                    child = next;\r
+                }\r
+\r
+                node.putProp(Node.BREAK_PROP, breakTarget);\r
+                loops.push(node);\r
+                loopEnds.push(breakTarget);\r
+                node.putProp(Node.CASES_PROP, new Vector(13));\r
+                break;\r
+              }\r
+\r
+              case TokenStream.DEFAULT:\r
+              case TokenStream.CASE:\r
+              {\r
+                Node sw = (Node) loops.peek();\r
+                if (type == TokenStream.CASE) {\r
+                    Vector cases = (Vector) sw.getProp(Node.CASES_PROP);\r
+                    cases.addElement(node);\r
+                } else {\r
+                    sw.putProp(Node.DEFAULT_PROP, node);\r
+                }\r
+                break;\r
+              }\r
+\r
+              case TokenStream.NEWLOCAL : {\r
+                    Integer localCount\r
+                            = (Integer)(tree.getProp(Node.LOCALCOUNT_PROP));\r
+                    if (localCount == null) {\r
+                        tree.putProp(Node.LOCALCOUNT_PROP, new Integer(1));\r
+                    }\r
+                    else {\r
+                        tree.putProp(Node.LOCALCOUNT_PROP,\r
+                                        new Integer(localCount.intValue() + 1));\r
+                    }\r
+                }\r
+                break;\r
+\r
+              case TokenStream.LOOP:\r
+                loops.push(node);\r
+                loopEnds.push(node.getProp(Node.BREAK_PROP));\r
+                break;\r
+\r
+              case TokenStream.WITH:\r
+              {\r
+                if (inFunction) {\r
+                    // With statements require an activation object.\r
+                    ((FunctionNode) tree).setRequiresActivation(true);\r
+                }\r
+                loops.push(node);\r
+                Node leave = node.getNextSibling();\r
+                if (leave.getType() != TokenStream.LEAVEWITH) {\r
+                    throw new RuntimeException("Unexpected tree");\r
+                }\r
+                loopEnds.push(leave);\r
+                break;\r
+              }\r
+\r
+              case TokenStream.TRY:\r
+              {\r
+                Node finallytarget = (Node)node.getProp(Node.FINALLY_PROP);\r
+                if (finallytarget != null) {\r
+                    hasFinally = true;\r
+                    loops.push(node);\r
+                    loopEnds.push(finallytarget);\r
+                }\r
+                Integer localCount\r
+                        = (Integer)(tree.getProp(Node.LOCALCOUNT_PROP));\r
+                if (localCount == null) {\r
+                    tree.putProp(Node.LOCALCOUNT_PROP, new Integer(1));\r
+                }\r
+                else {\r
+                    tree.putProp(Node.LOCALCOUNT_PROP,\r
+                                 new Integer(localCount.intValue() + 1));\r
+                }\r
+                break;\r
+              }\r
+\r
+              case TokenStream.TARGET:\r
+              case TokenStream.LEAVEWITH:\r
+                if (!loopEnds.empty() && loopEnds.peek() == node) {\r
+                    loopEnds.pop();\r
+                    loops.pop();\r
+                }\r
+                break;\r
+\r
+              case TokenStream.RETURN:\r
+              {\r
+                /* If we didn't support try/finally, it wouldn't be\r
+                 * necessary to put LEAVEWITH nodes here... but as\r
+                 * we do need a series of JSR FINALLY nodes before\r
+                 * each RETURN, we need to ensure that each finally\r
+                 * block gets the correct scope... which could mean\r
+                 * that some LEAVEWITH nodes are necessary.\r
+                 */\r
+                if (!hasFinally)\r
+                    break;     // skip the whole mess.\r
+\r
+                Node parent = iterator.getCurrentParent();\r
+                for (int i=loops.size()-1; i >= 0; i--) {\r
+                    Node n = (Node) loops.elementAt(i);\r
+                    int elemtype = n.getType();\r
+                    if (elemtype == TokenStream.TRY) {\r
+                        Node jsrnode = new Node(TokenStream.JSR);\r
+                        Object jsrtarget = n.getProp(Node.FINALLY_PROP);\r
+                        jsrnode.putProp(Node.TARGET_PROP, jsrtarget);\r
+                        parent.addChildBefore(jsrnode, node);\r
+                    } else if (elemtype == TokenStream.WITH) {\r
+                        parent.addChildBefore(new Node(TokenStream.LEAVEWITH),\r
+                                              node);\r
+                    }\r
+                }\r
+                break;\r
+              }\r
+\r
+              case TokenStream.BREAK:\r
+              case TokenStream.CONTINUE:\r
+              {\r
+                Node loop = null;\r
+                boolean labelled = node.hasChildren();\r
+                String id = null;\r
+                if (labelled) {\r
+                    /* get the label */\r
+                    Node child = node.getFirstChild();\r
+                    id = child.getString();\r
+                    node.removeChild(child);\r
+                }\r
+\r
+                int i;\r
+                Node parent = iterator.getCurrentParent();\r
+                for (i=loops.size()-1; i >= 0; i--) {\r
+                    Node n = (Node) loops.elementAt(i);\r
+                    int elemtype = n.getType();\r
+                    if (elemtype == TokenStream.WITH) {\r
+                        parent.addChildBefore(new Node(TokenStream.LEAVEWITH),\r
+                                              node);\r
+                    } else if (elemtype == TokenStream.TRY) {\r
+                        Node jsrFinally = new Node(TokenStream.JSR);\r
+                        Object jsrTarget = n.getProp(Node.FINALLY_PROP);\r
+                        jsrFinally.putProp(Node.TARGET_PROP, jsrTarget);\r
+                        parent.addChildBefore(jsrFinally, node);\r
+                    } else if (!labelled &&\r
+                               (elemtype == TokenStream.LOOP ||\r
+                                (elemtype == TokenStream.SWITCH &&\r
+                                 type == TokenStream.BREAK)))\r
+                    {\r
+                        /* if it's a simple break/continue, break from the\r
+                         * nearest enclosing loop or switch\r
+                         */\r
+                        loop = n;\r
+                        break;\r
+                    } else if (labelled &&\r
+                               elemtype == TokenStream.LABEL &&\r
+                               id.equals((String)n.getProp(Node.LABEL_PROP)))\r
+                    {\r
+                        loop = n;\r
+                        break;\r
+                    }\r
+                }\r
+                int propType = type == TokenStream.BREAK\r
+                               ? Node.BREAK_PROP\r
+                               : Node.CONTINUE_PROP;\r
+                Node target = loop == null \r
+                              ? null\r
+                              : (Node) loop.getProp(propType);\r
+                if (loop == null || target == null) {\r
+                    String message;\r
+                    if (!labelled) {\r
+                        // didn't find an appropriate target\r
+                        if (type == TokenStream.CONTINUE) {\r
+                            message = Context.getMessage\r
+                                ("msg.continue.outside", null);\r
+                        } else {\r
+                            message = Context.getMessage\r
+                                ("msg.bad.break", null);\r
+                        }\r
+                    } else if (loop != null) {\r
+                        message = Context.getMessage0("msg.continue.nonloop");\r
+                    } else {\r
+                        Object[] errArgs = { id };\r
+                        message = Context.getMessage\r
+                            ("msg.undef.label", errArgs);\r
+                    }\r
+                    reportMessage(Context.getContext(), message, node, \r
+                                  tree, true, scope);\r
+                    node.setType(TokenStream.NOP);\r
+                    break;\r
+                }\r
+                node.setType(TokenStream.GOTO);\r
+                node.putProp(Node.TARGET_PROP, target);\r
+                break;\r
+              }\r
+\r
+              case TokenStream.CALL:\r
+                if (isSpecialCallName(tree, node))\r
+                    node.putProp(Node.SPECIALCALL_PROP, Boolean.TRUE);\r
+                visitCall(node, tree);\r
+                break;\r
+\r
+              case TokenStream.NEW:\r
+                if (isSpecialCallName(tree, node))\r
+                    node.putProp(Node.SPECIALCALL_PROP, Boolean.TRUE);\r
+                visitNew(node, tree);\r
+                break;\r
+\r
+              case TokenStream.DOT:\r
+              {\r
+                Node right = node.getLastChild();\r
+                right.setType(TokenStream.STRING);\r
+                break;\r
+              }\r
+\r
+              case TokenStream.EXPRSTMT:\r
+                node.setType(inFunction ? TokenStream.POP : TokenStream.POPV);\r
+                break;\r
+\r
+              case TokenStream.OBJECT:\r
+              {\r
+                Vector regexps = (Vector) tree.getProp(Node.REGEXP_PROP);\r
+                if (regexps == null) {\r
+                    regexps = new Vector(3);\r
+                    tree.putProp(Node.REGEXP_PROP, regexps);\r
+                }\r
+                regexps.addElement(node);\r
+                Node n = new Node(TokenStream.OBJECT);\r
+                iterator.replaceCurrent(n);\r
+                n.putProp(Node.REGEXP_PROP, node);\r
+                break;\r
+              }\r
+\r
+              case TokenStream.VAR:\r
+              {\r
+                ShallowNodeIterator i = node.getChildIterator();\r
+                Node result = new Node(TokenStream.BLOCK);\r
+                while (i.hasMoreElements()) {\r
+                    Node n = i.nextNode();\r
+                    if (!n.hasChildren())\r
+                        continue;\r
+                    Node init = n.getFirstChild();\r
+                    n.removeChild(init);\r
+                    Node asn = (Node) irFactory.createAssignment(\r
+                                        TokenStream.NOP, n, init, null,\r
+                                        false);\r
+                    Node pop = new Node(TokenStream.POP, asn, node.getDatum());\r
+                    result.addChildToBack(pop);\r
+                }\r
+                iterator.replaceCurrent(result);\r
+                break;\r
+              }\r
+\r
+              case TokenStream.DELPROP:\r
+              case TokenStream.SETNAME:\r
+              {\r
+                if (!inFunction || inWithStatement())\r
+                    break;\r
+                Node bind = node.getFirstChild();\r
+                if (bind == null || bind.getType() != TokenStream.BINDNAME)\r
+                    break;\r
+                String name = bind.getString();\r
+                Context cx = Context.getCurrentContext();\r
+                if (cx != null && cx.isActivationNeeded(name)) {\r
+                    // use of "arguments" requires an activation object.\r
+                    ((FunctionNode) tree).setRequiresActivation(true);\r
+                }\r
+                VariableTable vars = getVariableTable(tree);\r
+                if (vars.getVariable(name) != null) {\r
+                    if (type == TokenStream.SETNAME) {\r
+                        node.setType(TokenStream.SETVAR);\r
+                        bind.setType(TokenStream.STRING);\r
+                    } else {\r
+                        // Local variables are by definition permanent\r
+                        Node n = new Node(TokenStream.PRIMARY,\r
+                                          new Integer(TokenStream.FALSE));\r
+                        iterator.replaceCurrent(n);\r
+                    }\r
+                }\r
+                break;\r
+              }\r
+              \r
+              case TokenStream.GETPROP:\r
+                if (inFunction) {\r
+                    Node n = node.getFirstChild().getNextSibling();\r
+                    String name = n == null ? "" : n.getString();\r
+                    Context cx = Context.getCurrentContext();\r
+                    if ((cx != null && cx.isActivationNeeded(name)) ||\r
+                        (name.equals("length") && \r
+                         Context.getContext().getLanguageVersion() == \r
+                         Context.VERSION_1_2))\r
+                    {\r
+                        // Use of "arguments" or "length" in 1.2 requires \r
+                        // an activation object.\r
+                        ((FunctionNode) tree).setRequiresActivation(true);\r
+                    }\r
+                }\r
+                break;\r
+\r
+              case TokenStream.NAME:\r
+              {\r
+                if (!inFunction || inWithStatement())\r
+                    break;\r
+                String name = node.getString();\r
+                Context cx = Context.getCurrentContext();\r
+                if (cx != null && cx.isActivationNeeded(name)) {\r
+                    // Use of "arguments" requires an activation object.\r
+                    ((FunctionNode) tree).setRequiresActivation(true);\r
+                }\r
+                VariableTable vars = getVariableTable(tree);\r
+                if (vars.getVariable(name) != null) {\r
+                    node.setType(TokenStream.GETVAR);\r
+                }\r
+                break;\r
+              }\r
+            }\r
+        }\r
+\r
+        return tree;\r
+    }\r
+\r
+    protected void addVariables(Node tree, VariableTable vars) {\r
+        // OPT: a whole pass to collect variables seems expensive.\r
+        // Could special case to go into statements only.\r
+        boolean inFunction = tree.getType() == TokenStream.FUNCTION;\r
+        PreorderNodeIterator iterator = tree.getPreorderIterator();\r
+        Hashtable ht = null;\r
+        Node node;\r
+        while ((node = iterator.nextNode()) != null) {\r
+            int nodeType = node.getType();\r
+            if (inFunction && nodeType == TokenStream.FUNCTION &&\r
+                node != tree && \r
+                ((FunctionNode) node.getProp(Node.FUNCTION_PROP)).getFunctionType() == \r
+                    FunctionNode.FUNCTION_EXPRESSION_STATEMENT) \r
+            {\r
+                // In a function with both "var x" and "function x",\r
+                // disregard the var statement, independent of order.\r
+                String name = node.getString();\r
+                if (name == null)\r
+                    continue;\r
+                vars.removeLocal(name);\r
+                if (ht == null)\r
+                    ht = new Hashtable();\r
+                ht.put(name, Boolean.TRUE);\r
+            }\r
+            if (nodeType != TokenStream.VAR)\r
+                continue;\r
+            ShallowNodeIterator i = node.getChildIterator();\r
+            while (i.hasMoreElements()) {\r
+                Node n = i.nextNode();\r
+                if (ht == null || ht.get(n.getString()) == null)\r
+                    vars.addLocal(n.getString());\r
+            }\r
+        }\r
+        String name = (String) tree.getDatum();\r
+        if (inFunction && ((FunctionNode) tree).getFunctionType() ==\r
+                          FunctionNode.FUNCTION_EXPRESSION &&\r
+            name != null && name.length() > 0 &&\r
+            vars.getVariable(name) == null)\r
+        {\r
+            // A function expression needs to have its name as a variable\r
+            // (if it isn't already allocated as a variable). See \r
+            // ECMA Ch. 13.  We add code to the beginning of the function\r
+            // to initialize a local variable of the function's name\r
+            // to the function value.\r
+            vars.addLocal(name);\r
+            Node block = tree.getLastChild();\r
+            Node setFn = new Node(TokenStream.POP,\r
+                            new Node(TokenStream.SETVAR,\r
+                                new Node(TokenStream.STRING, name),\r
+                                new Node(TokenStream.PRIMARY,\r
+                                    new Integer(TokenStream.THISFN))));\r
+            block.addChildrenToFront(setFn);\r
+        }\r
+    }\r
+\r
+    protected void addParameters(FunctionNode fnNode) {\r
+        VariableTable vars = fnNode.getVariableTable();\r
+        Node args = fnNode.getFirstChild();\r
+        if (args.getType() == TokenStream.LP && vars.getParameterCount() == 0)\r
+        {\r
+            // Add parameters\r
+            ShallowNodeIterator i = args.getChildIterator();\r
+            while (i.hasMoreElements()) {\r
+                Node n = i.nextNode();\r
+                String arg = n.getString();\r
+                vars.addParameter(arg);\r
+            }\r
+        }\r
+    }\r
+    \r
+    protected void visitNew(Node node, Node tree) {\r
+    }\r
+\r
+    protected void visitCall(Node node, Node tree) {\r
+        /*\r
+         * For\r
+         *      Call(GetProp(a, b), c, d)   // or GetElem...\r
+         * we wish to evaluate as\r
+         *      Call(GetProp(tmp=a, b), tmp, c, d)\r
+         *\r
+         * for\r
+         *      Call(Name("a"), b, c)\r
+         * we wish to evaluate as\r
+         *      Call(GetProp(tmp=GetBase("a"), "a"), tmp, b, c)\r
+         *\r
+         * and for\r
+         *      Call(a, b, c);\r
+         * we wish to evaluate as\r
+         *      Call(tmp=a, Parent(tmp), c, d)\r
+         */\r
+        Node left = node.getFirstChild();\r
+        // count the arguments\r
+        int argCount = 0;\r
+        Node arg = left.getNextSibling();\r
+        while (arg != null) {\r
+            arg = arg.getNextSibling();\r
+            argCount++;\r
+        }\r
+        boolean addGetThis = false;\r
+        if (left.getType() == TokenStream.NAME) {\r
+            VariableTable vars = getVariableTable(tree);\r
+            String name = left.getString();\r
+            if (inFunction && vars.getVariable(name) != null && \r
+                !inWithStatement()) \r
+            {\r
+                // call to a var. Transform to Call(GetVar("a"), b, c)\r
+                left.setType(TokenStream.GETVAR);\r
+                // fall through to code to add GetParent\r
+            } else {\r
+                // transform to Call(GetProp(GetBase("a"), "a"), b, c)\r
+\r
+                node.removeChild(left);\r
+                left.setType(TokenStream.GETBASE);\r
+                Node str = left.cloneNode();\r
+                str.setType(TokenStream.STRING);\r
+                Node getProp = new Node(TokenStream.GETPROP, left, str);\r
+                node.addChildToFront(getProp);\r
+                left = getProp;\r
+\r
+                // Conditionally set a flag to add a GETTHIS node.\r
+                // The getThis entry in the runtime will take a\r
+                // Scriptable object intended to be used as a 'this'\r
+                // and make sure that it is neither a With object or\r
+                // an activation object.\r
+                // Executing getThis requires at least two instanceof\r
+                // tests, so we only include it if we are currently\r
+                // inside a 'with' statement, or if we are executing\r
+                // a script (to protect against an eval inside a with).\r
+                addGetThis = inWithStatement() || !inFunction;\r
+                // fall through to GETPROP code\r
+            }\r
+        }\r
+        if (left.getType() != TokenStream.GETPROP &&\r
+            left.getType() != TokenStream.GETELEM)\r
+        {\r
+            node.removeChild(left);\r
+            Node tmp = irFactory.createNewTemp(left);\r
+            Node use = irFactory.createUseTemp(tmp);\r
+            use.putProp(Node.TEMP_PROP, tmp);\r
+            Node parent = new Node(TokenStream.PARENT, use);\r
+            node.addChildToFront(parent);\r
+            node.addChildToFront(tmp);\r
+            return;\r
+        }\r
+        Node leftLeft = left.getFirstChild();\r
+        left.removeChild(leftLeft);\r
+        Node tmp = irFactory.createNewTemp(leftLeft);\r
+        left.addChildToFront(tmp);\r
+        Node use = irFactory.createUseTemp(tmp);\r
+        use.putProp(Node.TEMP_PROP, tmp);\r
+        if (addGetThis)\r
+            use = new Node(TokenStream.GETTHIS, use);\r
+        node.addChildAfter(use, left);\r
+    }\r
+\r
+    protected boolean inWithStatement() {\r
+        for (int i=loops.size()-1; i >= 0; i--) {\r
+            Node n = (Node) loops.elementAt(i);\r
+            if (n.getType() == TokenStream.WITH)\r
+                return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Return true if the node is a call to a function that requires \r
+     * access to the enclosing activation object.\r
+     */\r
+    private boolean isSpecialCallName(Node tree, Node node) {\r
+        Node left = node.getFirstChild();\r
+        boolean isSpecial = false;\r
+        if (left.getType() == TokenStream.NAME) {\r
+            String name = left.getString();\r
+            isSpecial = name.equals("eval") || name.equals("With");\r
+        } else {\r
+            if (left.getType() == TokenStream.GETPROP) {\r
+                String name = left.getLastChild().getString();\r
+                isSpecial = name.equals("exec");\r
+            }\r
+        }\r
+        if (isSpecial) {\r
+            // Calls to these functions require activation objects.\r
+            if (inFunction)\r
+                ((FunctionNode) tree).setRequiresActivation(true);\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    protected VariableTable createVariableTable() {\r
+        return new VariableTable();\r
+    }\r
+\r
+    protected VariableTable getVariableTable(Node tree) {\r
+        if (inFunction) {\r
+            return ((FunctionNode)tree).getVariableTable();\r
+        }\r
+        VariableTable result = (VariableTable)(tree.getProp(Node.VARS_PROP));\r
+        if (result == null) {\r
+            result = createVariableTable();\r
+            tree.putProp(Node.VARS_PROP, result);\r
+        }\r
+        return result;\r
+    }\r
+    \r
+    protected void reportMessage(Context cx, String msg, Node stmt, \r
+                                 Node tree, boolean isError,\r
+                                 Scriptable scope)\r
+    {\r
+        Object obj = stmt.getDatum();\r
+        int lineno = 0;\r
+        if (obj != null && obj instanceof Integer)\r
+            lineno = ((Integer) obj).intValue();\r
+        Object prop = tree == null \r
+                      ? null\r
+                      : tree.getProp(Node.SOURCENAME_PROP);\r
+        if (isError) {\r
+            if (scope != null)\r
+            throw NativeGlobal.constructError(\r
+                        cx, "SyntaxError", msg, scope, \r
+                        (String) prop, lineno, 0, null);\r
+            else\r
+                cx.reportError(msg, (String) prop, lineno, null, 0);\r
+        }\r
+        else\r
+            cx.reportWarning(msg, (String) prop, lineno, null, 0);        \r
+    }\r
+\r
+    protected Stack loops;\r
+    protected Stack loopEnds;\r
+    protected boolean inFunction;\r
+    protected IRFactory irFactory;\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/NotAFunctionException.java b/src/org/mozilla/javascript/NotAFunctionException.java
new file mode 100644 (file)
index 0000000..5f1179a
--- /dev/null
@@ -0,0 +1,52 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * Thrown if call is attempted on an object that is not a function.\r
+ */\r
+public class NotAFunctionException extends Exception {\r
+\r
+    public NotAFunctionException() {\r
+    }\r
+\r
+    public NotAFunctionException(String detail) {\r
+        super(detail);\r
+    }\r
+\r
+}\r
diff --git a/src/org/mozilla/javascript/Parser.java b/src/org/mozilla/javascript/Parser.java
new file mode 100644 (file)
index 0000000..9e23a8c
--- /dev/null
@@ -0,0 +1,1516 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Mike Ang\r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import org.mozilla.javascript.ErrorReporter;\r
+import org.mozilla.javascript.Context;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * This class implements the JavaScript parser.\r
+ *\r
+ * It is based on the C source files jsparse.c and jsparse.h\r
+ * in the jsref package.\r
+ *\r
+ * @see TokenStream\r
+ *\r
+ * @author Mike McCabe\r
+ * @author Brendan Eich\r
+ */\r
+\r
+class Parser {\r
+\r
+    public Parser(IRFactory nf) {\r
+        this.nf = nf;\r
+    }\r
+\r
+    private void mustMatchToken(TokenStream ts, int toMatch, String messageId)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        int tt;\r
+        if ((tt = ts.getToken()) != toMatch) {\r
+            reportError(ts, messageId);\r
+            ts.ungetToken(tt); // In case the parser decides to continue\r
+        }\r
+    }\r
+    \r
+    private void reportError(TokenStream ts, String messageId) \r
+        throws JavaScriptException\r
+    {\r
+        this.ok = false;\r
+        ts.reportSyntaxError(messageId, null);\r
+        \r
+        /* Throw an exception to unwind the recursive descent parse. \r
+         * We use JavaScriptException here even though it is really \r
+         * a different use of the exception than it is usually used\r
+         * for.\r
+         */\r
+        throw new JavaScriptException(messageId);\r
+    }\r
+\r
+    /*\r
+     * Build a parse tree from the given TokenStream.  \r
+     *\r
+     * @param ts the TokenStream to parse\r
+     *\r
+     * @return an Object representing the parsed\r
+     * program.  If the parse fails, null will be returned.  (The\r
+     * parse failure will result in a call to the current Context's\r
+     * ErrorReporter.)\r
+     */\r
+    public Object parse(TokenStream ts)\r
+        throws IOException\r
+    {\r
+        this.ok = true;\r
+        Source source = new Source();\r
+\r
+        int tt;          // last token from getToken();\r
+        int baseLineno = ts.getLineno();  // line number where source starts\r
+\r
+        /* so we have something to add nodes to until\r
+         * we've collected all the source */\r
+        Object tempBlock = nf.createLeaf(TokenStream.BLOCK);\r
+\r
+        while (true) {\r
+            ts.flags |= ts.TSF_REGEXP;\r
+            tt = ts.getToken();\r
+            ts.flags &= ~ts.TSF_REGEXP;\r
+\r
+            if (tt <= ts.EOF) {\r
+                break;\r
+            }\r
+\r
+            if (tt == ts.FUNCTION) {\r
+                try {\r
+                    nf.addChildToBack(tempBlock, function(ts, source, false));\r
+                    /* function doesn't add its own final EOL,\r
+                     * because it gets SEMI + EOL from Statement when it's\r
+                     * a nested function; so we need to explicitly add an\r
+                     * EOL here.\r
+                     */\r
+                    source.append((char)ts.EOL);\r
+                    wellTerminated(ts, ts.FUNCTION);\r
+                } catch (JavaScriptException e) {\r
+                    this.ok = false;\r
+                    break;\r
+                }\r
+            } else {\r
+                ts.ungetToken(tt);\r
+                nf.addChildToBack(tempBlock, statement(ts, source));\r
+            }\r
+        }\r
+\r
+        if (!this.ok) {\r
+            // XXX ts.clearPushback() call here?\r
+            return null;\r
+        }\r
+\r
+        Object pn = nf.createScript(tempBlock, ts.getSourceName(),\r
+                                    baseLineno, ts.getLineno(),\r
+                                    source.buf.toString());\r
+        return pn;\r
+    }\r
+\r
+    /*\r
+     * The C version of this function takes an argument list,\r
+     * which doesn't seem to be needed for tree generation...\r
+     * it'd only be useful for checking argument hiding, which\r
+     * I'm not doing anyway...\r
+     */\r
+    private Object parseFunctionBody(TokenStream ts, Source source)\r
+        throws IOException\r
+    {\r
+        int oldflags = ts.flags;\r
+        ts.flags &= ~(TokenStream.TSF_RETURN_EXPR\r
+                      | TokenStream.TSF_RETURN_VOID);\r
+        ts.flags |= TokenStream.TSF_FUNCTION;\r
+\r
+        Object pn = nf.createBlock(ts.getLineno());\r
+        try {\r
+            int tt;\r
+            while((tt = ts.peekToken()) > ts.EOF && tt != ts.RC) {\r
+                if (tt == TokenStream.FUNCTION) {\r
+                    ts.getToken();\r
+                    nf.addChildToBack(pn, function(ts, source, false));\r
+                    /* function doesn't add its own final EOL,\r
+                     * because it gets SEMI + EOL from Statement when it's\r
+                     * a nested function; so we need to explicitly add an\r
+                     * EOL here.\r
+                     */\r
+                    source.append((char)ts.EOL);\r
+                    wellTerminated(ts, ts.FUNCTION);       \r
+                } else {\r
+                    nf.addChildToBack(pn, statement(ts, source));\r
+                }\r
+            }\r
+        } catch (JavaScriptException e) {\r
+            this.ok = false;\r
+        } finally {\r
+            // also in finally block:\r
+            // flushNewLines, clearPushback.\r
+\r
+            ts.flags = oldflags;\r
+        }\r
+\r
+        return pn;\r
+    }\r
+\r
+    private Object function(TokenStream ts, Source source, boolean isExpr)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        String name = null;\r
+        Object args = nf.createLeaf(ts.LP);\r
+        Object body;\r
+        int baseLineno = ts.getLineno();  // line number where source starts\r
+\r
+        // save a reference to the function in the enclosing source.\r
+        source.append((char) ts.FUNCTION);\r
+        source.append(source.functionNumber);\r
+        source.functionNumber++;\r
+\r
+        // make a new Source for the enclosed function\r
+        source = new Source();\r
+\r
+        // FUNCTION as the first token in a Source means it's a function\r
+        // definition, and not a reference.\r
+        source.append((char) ts.FUNCTION);\r
+\r
+        if (ts.matchToken(ts.NAME)) {\r
+            name = ts.getString();\r
+            source.addString(ts.NAME, name);\r
+        }\r
+        else\r
+            ; // it's an anonymous function\r
+\r
+        mustMatchToken(ts, ts.LP, "msg.no.paren.parms");\r
+        source.append((char) ts.LP);\r
+\r
+        if (!ts.matchToken(ts.RP)) {\r
+            boolean first = true;\r
+            do {\r
+                if (!first)\r
+                    source.append((char)ts.COMMA);\r
+                first = false;\r
+                mustMatchToken(ts, ts.NAME, "msg.no.parm");\r
+                String s = ts.getString();\r
+                nf.addChildToBack(args, nf.createName(s));\r
+\r
+                source.addString(ts.NAME, s);\r
+            } while (ts.matchToken(ts.COMMA));\r
+\r
+            mustMatchToken(ts, ts.RP, "msg.no.paren.after.parms");\r
+        }\r
+        source.append((char)ts.RP);\r
+\r
+        mustMatchToken(ts, ts.LC, "msg.no.brace.body");\r
+        source.append((char)ts.LC);\r
+        source.append((char)ts.EOL);\r
+        body = parseFunctionBody(ts, source);\r
+        mustMatchToken(ts, ts.RC, "msg.no.brace.after.body");\r
+        source.append((char)ts.RC);\r
+        // skip the last EOL so nested functions work...\r
+\r
+        // name might be null;\r
+        return nf.createFunction(name, args, body,\r
+                                 ts.getSourceName(),\r
+                                 baseLineno, ts.getLineno(),\r
+                                 source.buf.toString(),\r
+                                 isExpr);\r
+    }\r
+\r
+    private Object statements(TokenStream ts, Source source)\r
+        throws IOException\r
+    {\r
+        Object pn = nf.createBlock(ts.getLineno());\r
+\r
+        int tt;\r
+        while((tt = ts.peekToken()) > ts.EOF && tt != ts.RC) {\r
+            nf.addChildToBack(pn, statement(ts, source));\r
+        }\r
+\r
+        return pn;\r
+    }\r
+\r
+    private Object condition(TokenStream ts, Source source)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Object pn;\r
+        mustMatchToken(ts, ts.LP, "msg.no.paren.cond");\r
+        source.append((char)ts.LP);\r
+        pn = expr(ts, source, false);\r
+        mustMatchToken(ts, ts.RP, "msg.no.paren.after.cond");\r
+        source.append((char)ts.RP);\r
+\r
+        // there's a check here in jsparse.c that corrects = to ==\r
+\r
+        return pn;\r
+    }\r
+\r
+    private boolean wellTerminated(TokenStream ts, int lastExprType)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        int tt = ts.peekTokenSameLine();\r
+        if (tt == ts.ERROR) {\r
+            return false;\r
+        }\r
+\r
+        if (tt != ts.EOF && tt != ts.EOL\r
+            && tt != ts.SEMI && tt != ts.RC)\r
+            {\r
+                int version = Context.getContext().getLanguageVersion();\r
+                if ((tt == ts.FUNCTION || lastExprType == ts.FUNCTION) &&\r
+                    (version < Context.VERSION_1_2)) {\r
+                    /*\r
+                     * Checking against version < 1.2 and version >= 1.0\r
+                     * in the above line breaks old javascript, so we keep it\r
+                     * this way for now... XXX warning needed?\r
+                     */\r
+                    return true;\r
+                } else {\r
+                    reportError(ts, "msg.no.semi.stmt");\r
+                }\r
+            }\r
+        return true;\r
+    }\r
+\r
+    // match a NAME; return null if no match.\r
+    private String matchLabel(TokenStream ts)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        int lineno = ts.getLineno();\r
+\r
+        String label = null;\r
+        int tt;\r
+        tt = ts.peekTokenSameLine();\r
+        if (tt == ts.NAME) {\r
+            ts.getToken();\r
+            label = ts.getString();\r
+        }\r
+\r
+        if (lineno == ts.getLineno())\r
+            wellTerminated(ts, ts.ERROR);\r
+\r
+        return label;\r
+    }\r
+\r
+    private Object statement(TokenStream ts, Source source) \r
+        throws IOException\r
+    {\r
+        try {\r
+            return statementHelper(ts, source);\r
+        } catch (JavaScriptException e) {\r
+            // skip to end of statement\r
+            int lineno = ts.getLineno();\r
+            int t;\r
+            do {\r
+                t = ts.getToken();\r
+            } while (t != TokenStream.SEMI && t != TokenStream.EOL && \r
+                     t != TokenStream.EOF && t != TokenStream.ERROR);\r
+            return nf.createExprStatement(nf.createName("error"), lineno);\r
+        }\r
+    }\r
+    \r
+    /**\r
+     * Whether the "catch (e: e instanceof Exception) { ... }" syntax\r
+     * is implemented.\r
+     */\r
+   \r
+    private Object statementHelper(TokenStream ts, Source source)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Object pn = null;\r
+\r
+        // If skipsemi == true, don't add SEMI + EOL to source at the\r
+        // end of this statment.  For compound statements, IF/FOR etc.\r
+        boolean skipsemi = false;\r
+\r
+        int tt;\r
+\r
+        int lastExprType = 0;  // For wellTerminated.  0 to avoid warning.\r
+\r
+        tt = ts.getToken();\r
+\r
+        switch(tt) {\r
+        case TokenStream.IF: {\r
+            skipsemi = true;\r
+\r
+            source.append((char)ts.IF);\r
+            int lineno = ts.getLineno();\r
+            Object cond = condition(ts, source);\r
+            source.append((char)ts.LC);\r
+            source.append((char)ts.EOL);\r
+            Object ifTrue = statement(ts, source);\r
+            Object ifFalse = null;\r
+            if (ts.matchToken(ts.ELSE)) {\r
+                source.append((char)ts.RC);\r
+                source.append((char)ts.ELSE);\r
+                source.append((char)ts.LC);\r
+                source.append((char)ts.EOL);\r
+                ifFalse = statement(ts, source);\r
+            }\r
+            source.append((char)ts.RC);\r
+            source.append((char)ts.EOL);\r
+            pn = nf.createIf(cond, ifTrue, ifFalse, lineno);\r
+            break;\r
+        }\r
+\r
+        case TokenStream.SWITCH: {\r
+            skipsemi = true;\r
+\r
+            source.append((char)ts.SWITCH);\r
+            pn = nf.createSwitch(ts.getLineno());\r
+\r
+            Object cur_case = null;  // to kill warning\r
+            Object case_statements;\r
+\r
+            mustMatchToken(ts, ts.LP, "msg.no.paren.switch");\r
+            source.append((char)ts.LP);\r
+            nf.addChildToBack(pn, expr(ts, source, false));\r
+            mustMatchToken(ts, ts.RP, "msg.no.paren.after.switch");\r
+            source.append((char)ts.RP);\r
+            mustMatchToken(ts, ts.LC, "msg.no.brace.switch");\r
+            source.append((char)ts.LC);\r
+            source.append((char)ts.EOL);\r
+\r
+            while ((tt = ts.getToken()) != ts.RC && tt != ts.EOF) {\r
+                switch(tt) {\r
+                case TokenStream.CASE:\r
+                    source.append((char)ts.CASE);\r
+                    cur_case = nf.createUnary(ts.CASE, expr(ts, source, false));\r
+                    source.append((char)ts.COLON);\r
+                    source.append((char)ts.EOL);\r
+                    break;\r
+\r
+                case TokenStream.DEFAULT:\r
+                    cur_case = nf.createLeaf(ts.DEFAULT);\r
+                    source.append((char)ts.DEFAULT);\r
+                    source.append((char)ts.COLON);\r
+                    source.append((char)ts.EOL);\r
+                    // XXX check that there isn't more than one default\r
+                    break;\r
+\r
+                default:\r
+                    reportError(ts, "msg.bad.switch");\r
+                    break;\r
+                }\r
+                mustMatchToken(ts, ts.COLON, "msg.no.colon.case");\r
+\r
+                case_statements = nf.createLeaf(TokenStream.BLOCK);\r
+\r
+                while ((tt = ts.peekToken()) != ts.RC && tt != ts.CASE &&\r
+                        tt != ts.DEFAULT && tt != ts.EOF) \r
+                {\r
+                    nf.addChildToBack(case_statements, statement(ts, source));\r
+                }\r
+                // assert cur_case\r
+                nf.addChildToBack(cur_case, case_statements);\r
+\r
+                nf.addChildToBack(pn, cur_case);\r
+            }\r
+            source.append((char)ts.RC);\r
+            source.append((char)ts.EOL);\r
+            break;\r
+        }\r
+\r
+        case TokenStream.WHILE: {\r
+            skipsemi = true;\r
+\r
+            source.append((char)ts.WHILE);\r
+            int lineno = ts.getLineno();\r
+            Object cond = condition(ts, source);\r
+            source.append((char)ts.LC);\r
+            source.append((char)ts.EOL);\r
+            Object body = statement(ts, source);\r
+            source.append((char)ts.RC);\r
+            source.append((char)ts.EOL);\r
+\r
+            pn = nf.createWhile(cond, body, lineno);\r
+            break;\r
+\r
+        }\r
+\r
+        case TokenStream.DO: {\r
+            source.append((char)ts.DO);\r
+            source.append((char)ts.LC);\r
+            source.append((char)ts.EOL);\r
+\r
+            int lineno = ts.getLineno();\r
+\r
+            Object body = statement(ts, source);\r
+\r
+            source.append((char)ts.RC);\r
+            mustMatchToken(ts, ts.WHILE, "msg.no.while.do");\r
+            source.append((char)ts.WHILE);\r
+            Object cond = condition(ts, source);\r
+\r
+            pn = nf.createDoWhile(body, cond, lineno);\r
+            break;\r
+        }\r
+\r
+        case TokenStream.FOR: {\r
+            skipsemi = true;\r
+\r
+            source.append((char)ts.FOR);\r
+            int lineno = ts.getLineno();\r
+\r
+            Object init;  // Node init is also foo in 'foo in Object'\r
+            Object cond;  // Node cond is also object in 'foo in Object'\r
+            Object incr = null; // to kill warning\r
+            Object body;\r
+\r
+            mustMatchToken(ts, ts.LP, "msg.no.paren.for");\r
+            source.append((char)ts.LP);\r
+            tt = ts.peekToken();\r
+            if (tt == ts.SEMI) {\r
+                init = nf.createLeaf(ts.VOID);\r
+            } else {\r
+                if (tt == ts.VAR) {\r
+                    // set init to a var list or initial\r
+                    ts.getToken();    // throw away the 'var' token\r
+                    init = variables(ts, source, true);\r
+                }\r
+                else {\r
+                    init = expr(ts, source, true);\r
+                }\r
+            }\r
+\r
+            tt = ts.peekToken();\r
+            if (tt == ts.RELOP && ts.getOp() == ts.IN) {\r
+                ts.matchToken(ts.RELOP);\r
+                source.append((char)ts.IN);\r
+                // 'cond' is the object over which we're iterating\r
+                cond = expr(ts, source, false);\r
+            } else {  // ordinary for loop\r
+                mustMatchToken(ts, ts.SEMI,\r
+                               "msg.no.semi.for");\r
+                source.append((char)ts.SEMI);\r
+                if (ts.peekToken() == ts.SEMI) {\r
+                    // no loop condition\r
+                    cond = nf.createLeaf(ts.VOID);\r
+                } else {\r
+                    cond = expr(ts, source, false);\r
+                }\r
+\r
+                mustMatchToken(ts, ts.SEMI,\r
+                               "msg.no.semi.for.cond");\r
+                source.append((char)ts.SEMI);\r
+                if (ts.peekToken() == ts.RP) {\r
+                    incr = nf.createLeaf(ts.VOID);\r
+                } else {\r
+                    incr = expr(ts, source, false);\r
+                }\r
+            }\r
+\r
+            mustMatchToken(ts, ts.RP, "msg.no.paren.for.ctrl");\r
+            source.append((char)ts.RP);\r
+            source.append((char)ts.LC);\r
+            source.append((char)ts.EOL);\r
+            body = statement(ts, source);\r
+            source.append((char)ts.RC);\r
+            source.append((char)ts.EOL);\r
+\r
+            if (incr == null) {\r
+                // cond could be null if 'in obj' got eaten by the init node.\r
+                pn = nf.createForIn(init, cond, body, lineno);\r
+            } else {\r
+                pn = nf.createFor(init, cond, incr, body, lineno);\r
+            }\r
+            break;\r
+        }\r
+\r
+        case TokenStream.TRY: {\r
+            int lineno = ts.getLineno();\r
+\r
+            Object tryblock;\r
+            Object catchblocks = null;\r
+            Object finallyblock = null;\r
+\r
+            skipsemi = true;\r
+            source.append((char)ts.TRY);\r
+            source.append((char)ts.LC);\r
+            source.append((char)ts.EOL);\r
+            tryblock = statement(ts, source);\r
+            source.append((char)ts.RC);\r
+            source.append((char)ts.EOL);\r
+\r
+            catchblocks = nf.createLeaf(TokenStream.BLOCK);\r
+\r
+            boolean sawDefaultCatch = false;\r
+            int peek = ts.peekToken();\r
+            if (peek == ts.CATCH) {\r
+                while (ts.matchToken(ts.CATCH)) {\r
+                    if (sawDefaultCatch) {\r
+                        reportError(ts, "msg.catch.unreachable");\r
+                    }\r
+                    source.append((char)ts.CATCH);\r
+                    mustMatchToken(ts, ts.LP, "msg.no.paren.catch");\r
+                    source.append((char)ts.LP);\r
+\r
+                    mustMatchToken(ts, ts.NAME, "msg.bad.catchcond");\r
+                    String varName = ts.getString();\r
+                    source.addString(ts.NAME, varName);\r
+                    \r
+                    Object catchCond = null;\r
+                    if (ts.matchToken(ts.IF)) {\r
+                        source.append((char)ts.IF);\r
+                        catchCond = expr(ts, source, false);\r
+                    } else {\r
+                        sawDefaultCatch = true;\r
+                    }\r
+\r
+                    mustMatchToken(ts, ts.RP, "msg.bad.catchcond");\r
+                    source.append((char)ts.RP);\r
+                    mustMatchToken(ts, ts.LC, "msg.no.brace.catchblock");\r
+                    source.append((char)ts.LC);\r
+                    source.append((char)ts.EOL);\r
+                    \r
+                    nf.addChildToBack(catchblocks, \r
+                        nf.createCatch(varName, catchCond, \r
+                                       statements(ts, source), \r
+                                       ts.getLineno()));\r
+\r
+                    mustMatchToken(ts, ts.RC, "msg.no.brace.after.body");\r
+                    source.append((char)ts.RC);\r
+                    source.append((char)ts.EOL);\r
+                }\r
+            } else if (peek != ts.FINALLY) {\r
+                mustMatchToken(ts, ts.FINALLY, "msg.try.no.catchfinally");\r
+            }\r
+\r
+            if (ts.matchToken(ts.FINALLY)) {\r
+                source.append((char)ts.FINALLY);\r
+\r
+                source.append((char)ts.LC);\r
+                source.append((char)ts.EOL);\r
+                finallyblock = statement(ts, source);\r
+                source.append((char)ts.RC);\r
+                source.append((char)ts.EOL);\r
+            }\r
+\r
+            pn = nf.createTryCatchFinally(tryblock, catchblocks,\r
+                                          finallyblock, lineno);\r
+\r
+            break;\r
+        }\r
+        case TokenStream.THROW: {\r
+            int lineno = ts.getLineno();\r
+            source.append((char)ts.THROW);\r
+            pn = nf.createThrow(expr(ts, source, false), lineno);\r
+            if (lineno == ts.getLineno())\r
+                wellTerminated(ts, ts.ERROR);\r
+            break;\r
+        }\r
+        case TokenStream.BREAK: {\r
+            int lineno = ts.getLineno();\r
+\r
+            source.append((char)ts.BREAK);\r
+\r
+            // matchLabel only matches if there is one\r
+            String label = matchLabel(ts);\r
+            if (label != null) {\r
+                source.addString(ts.NAME, label);\r
+            }\r
+            pn = nf.createBreak(label, lineno);\r
+            break;\r
+        }\r
+        case TokenStream.CONTINUE: {\r
+            int lineno = ts.getLineno();\r
+\r
+            source.append((char)ts.CONTINUE);\r
+\r
+            // matchLabel only matches if there is one\r
+            String label = matchLabel(ts);\r
+            if (label != null) {\r
+                source.addString(ts.NAME, label);\r
+            }\r
+            pn = nf.createContinue(label, lineno);\r
+            break;\r
+        }\r
+        case TokenStream.WITH: {\r
+            skipsemi = true;\r
+\r
+            source.append((char)ts.WITH);\r
+            int lineno = ts.getLineno();\r
+            mustMatchToken(ts, ts.LP, "msg.no.paren.with");\r
+            source.append((char)ts.LP);\r
+            Object obj = expr(ts, source, false);\r
+            mustMatchToken(ts, ts.RP, "msg.no.paren.after.with");\r
+            source.append((char)ts.RP);\r
+            source.append((char)ts.LC);\r
+            source.append((char)ts.EOL);\r
+\r
+            Object body = statement(ts, source);\r
+\r
+            source.append((char)ts.RC);\r
+            source.append((char)ts.EOL);\r
+\r
+            pn = nf.createWith(obj, body, lineno);\r
+            break;\r
+        }\r
+        case TokenStream.VAR: {\r
+            int lineno = ts.getLineno();\r
+            pn = variables(ts, source, false);\r
+            if (ts.getLineno() == lineno)\r
+                wellTerminated(ts, ts.ERROR);\r
+            break;\r
+        }\r
+        case TokenStream.ASSERT: {\r
+            Object retExpr = null;\r
+            int lineno = 0;\r
+            source.append((char)ts.ASSERT);\r
+\r
+            // bail if we're not in a (toplevel) function\r
+            if ((ts.flags & ts.TSF_FUNCTION) == 0)\r
+                reportError(ts, "msg.bad.return");\r
+\r
+            /* This is ugly, but we don't want to require a semicolon. */\r
+            ts.flags |= ts.TSF_REGEXP;\r
+            tt = ts.peekTokenSameLine();\r
+            ts.flags &= ~ts.TSF_REGEXP;\r
+            /*\r
+            if (tt != ts.EOF && tt != ts.EOL && tt != ts.SEMI && tt != ts.RC) {\r
+                lineno = ts.getLineno();\r
+                retExpr = expr(ts, source, false);\r
+                if (ts.getLineno() == lineno)\r
+                    wellTerminated(ts, ts.ERROR);\r
+                ts.flags |= ts.TSF_RETURN_EXPR;\r
+            } else {\r
+                ts.flags |= ts.TSF_RETURN_VOID;\r
+            }\r
+            */\r
+            // XXX ASSERT pn\r
+            pn = nf.createAssert(retExpr, lineno);\r
+            break;\r
+        }\r
+\r
+        case TokenStream.RETURN: {\r
+            Object retExpr = null;\r
+            int lineno = 0;\r
+\r
+            source.append((char)ts.RETURN);\r
+\r
+            // bail if we're not in a (toplevel) function\r
+            if ((ts.flags & ts.TSF_FUNCTION) == 0)\r
+                reportError(ts, "msg.bad.return");\r
+\r
+            /* This is ugly, but we don't want to require a semicolon. */\r
+            ts.flags |= ts.TSF_REGEXP;\r
+            tt = ts.peekTokenSameLine();\r
+            ts.flags &= ~ts.TSF_REGEXP;\r
+\r
+            if (tt != ts.EOF && tt != ts.EOL && tt != ts.SEMI && tt != ts.RC) {\r
+                lineno = ts.getLineno();\r
+                retExpr = expr(ts, source, false);\r
+                if (ts.getLineno() == lineno)\r
+                    wellTerminated(ts, ts.ERROR);\r
+                ts.flags |= ts.TSF_RETURN_EXPR;\r
+            } else {\r
+                ts.flags |= ts.TSF_RETURN_VOID;\r
+            }\r
+\r
+            // XXX ASSERT pn\r
+            pn = nf.createReturn(retExpr, lineno);\r
+            break;\r
+        }\r
+        case TokenStream.LC:\r
+            skipsemi = true;\r
+\r
+            pn = statements(ts, source);\r
+            mustMatchToken(ts, ts.RC, "msg.no.brace.block");\r
+            break;\r
+\r
+        case TokenStream.ERROR:\r
+            // Fall thru, to have a node for error recovery to work on\r
+        case TokenStream.EOL:\r
+        case TokenStream.SEMI:\r
+            pn = nf.createLeaf(ts.VOID);\r
+            skipsemi = true;\r
+            break;\r
+\r
+        default: {\r
+                lastExprType = tt;\r
+                int tokenno = ts.getTokenno();\r
+                ts.ungetToken(tt);\r
+                int lineno = ts.getLineno();\r
+\r
+                pn = expr(ts, source, false);\r
+\r
+                if (ts.peekToken() == ts.COLON) {\r
+                    /* check that the last thing the tokenizer returned was a\r
+                     * NAME and that only one token was consumed.\r
+                     */\r
+                    if (lastExprType != ts.NAME || (ts.getTokenno() != tokenno))\r
+                        reportError(ts, "msg.bad.label");\r
+\r
+                    ts.getToken();  // eat the COLON\r
+\r
+                    /* in the C source, the label is associated with the\r
+                     * statement that follows:\r
+                     *                nf.addChildToBack(pn, statement(ts));\r
+                     */\r
+                    String name = ts.getString();\r
+                    pn = nf.createLabel(name, lineno);\r
+\r
+                    // depend on decompiling lookahead to guess that that\r
+                    // last name was a label.\r
+                    source.append((char)ts.COLON);\r
+                    source.append((char)ts.EOL);\r
+                    return pn;\r
+                }\r
+\r
+                if (lastExprType == ts.FUNCTION)\r
+                    nf.setFunctionExpressionStatement(pn);\r
+\r
+                pn = nf.createExprStatement(pn, lineno);\r
+                \r
+                /*\r
+                 * Check explicitly against (multi-line) function\r
+                 * statement.\r
+\r
+                 * lastExprEndLine is a hack to fix an\r
+                 * automatic semicolon insertion problem with function\r
+                 * expressions; the ts.getLineno() == lineno check was\r
+                 * firing after a function definition even though the\r
+                 * next statement was on a new line, because\r
+                 * speculative getToken calls advanced the line number\r
+                 * even when they didn't succeed.\r
+                 */\r
+                if (ts.getLineno() == lineno ||\r
+                    (lastExprType == ts.FUNCTION &&\r
+                     ts.getLineno() == lastExprEndLine))\r
+                {\r
+                    wellTerminated(ts, lastExprType);\r
+                }\r
+                break;\r
+            }\r
+        }\r
+        ts.matchToken(ts.SEMI);\r
+        if (!skipsemi) {\r
+            source.append((char)ts.SEMI);\r
+            source.append((char)ts.EOL);\r
+        }\r
+\r
+        return pn;\r
+    }\r
+\r
+    private Object variables(TokenStream ts, Source source, boolean inForInit)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Object pn = nf.createVariables(ts.getLineno());\r
+        boolean first = true;\r
+\r
+        source.append((char)ts.VAR);\r
+\r
+        for (;;) {\r
+            Object name;\r
+            Object init;\r
+            mustMatchToken(ts, ts.NAME, "msg.bad.var");\r
+            String s = ts.getString();\r
+\r
+            if (!first)\r
+                source.append((char)ts.COMMA);\r
+            first = false;\r
+\r
+            source.addString(ts.NAME, s);\r
+            name = nf.createName(s);\r
+\r
+            // omitted check for argument hiding\r
+\r
+            if (ts.matchToken(ts.ASSIGN)) {\r
+                if (ts.getOp() != ts.NOP)\r
+                    reportError(ts, "msg.bad.var.init");\r
+\r
+                source.append((char)ts.ASSIGN);\r
+                source.append((char)ts.NOP);\r
+\r
+                init = assignExpr(ts, source, inForInit);\r
+                nf.addChildToBack(name, init);\r
+            }\r
+            nf.addChildToBack(pn, name);\r
+            if (!ts.matchToken(ts.COMMA))\r
+                break;\r
+        }\r
+        return pn;\r
+    }\r
+\r
+    private Object expr(TokenStream ts, Source source, boolean inForInit)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Object pn = assignExpr(ts, source, inForInit);\r
+        while (ts.matchToken(ts.COMMA)) {\r
+            source.append((char)ts.COMMA);\r
+            pn = nf.createBinary(ts.COMMA, pn, assignExpr(ts, source,\r
+                                                          inForInit));\r
+        }\r
+        return pn;\r
+    }\r
+\r
+    private Object assignExpr(TokenStream ts, Source source, boolean inForInit)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Object pn = condExpr(ts, source, inForInit);\r
+\r
+        if (ts.matchToken(ts.ASSIGN)) {\r
+            // omitted: "invalid assignment left-hand side" check.\r
+            source.append((char)ts.ASSIGN);\r
+            source.append((char)ts.getOp());\r
+            pn = nf.createBinary(ts.ASSIGN, ts.getOp(), pn,\r
+                                 assignExpr(ts, source, inForInit));\r
+        }\r
+\r
+        return pn;\r
+    }\r
+\r
+    private Object condExpr(TokenStream ts, Source source, boolean inForInit)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Object ifTrue;\r
+        Object ifFalse;\r
+\r
+        Object pn = orExpr(ts, source, inForInit);\r
+\r
+        if (ts.matchToken(ts.HOOK)) {\r
+            source.append((char)ts.HOOK);\r
+            ifTrue = assignExpr(ts, source, false);\r
+            mustMatchToken(ts, ts.COLON, "msg.no.colon.cond");\r
+            source.append((char)ts.COLON);\r
+            ifFalse = assignExpr(ts, source, inForInit);\r
+            return nf.createTernary(pn, ifTrue, ifFalse);\r
+        }\r
+\r
+        return pn;\r
+    }\r
+\r
+    private Object orExpr(TokenStream ts, Source source, boolean inForInit)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Object pn = andExpr(ts, source, inForInit);\r
+        if (ts.matchToken(ts.OR)) {\r
+            source.append((char)ts.OR);\r
+            pn = nf.createBinary(ts.OR, pn, orExpr(ts, source, inForInit));\r
+        }\r
+\r
+        return pn;\r
+    }\r
+\r
+    private Object andExpr(TokenStream ts, Source source, boolean inForInit)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Object pn = bitOrExpr(ts, source, inForInit);\r
+        if (ts.matchToken(ts.AND)) {\r
+            source.append((char)ts.AND);\r
+            pn = nf.createBinary(ts.AND, pn, andExpr(ts, source, inForInit));\r
+        }\r
+\r
+        return pn;\r
+    }\r
+\r
+    private Object bitOrExpr(TokenStream ts, Source source, boolean inForInit)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Object pn = bitXorExpr(ts, source, inForInit);\r
+        while (ts.matchToken(ts.BITOR)) {\r
+            source.append((char)ts.BITOR);\r
+            pn = nf.createBinary(ts.BITOR, pn, bitXorExpr(ts, source,\r
+                                                          inForInit));\r
+        }\r
+        return pn;\r
+    }\r
+\r
+    private Object bitXorExpr(TokenStream ts, Source source, boolean inForInit)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Object pn = bitAndExpr(ts, source, inForInit);\r
+        while (ts.matchToken(ts.BITXOR)) {\r
+            source.append((char)ts.BITXOR);\r
+            pn = nf.createBinary(ts.BITXOR, pn, bitAndExpr(ts, source,\r
+                                                           inForInit));\r
+        }\r
+        return pn;\r
+    }\r
+\r
+    private Object bitAndExpr(TokenStream ts, Source source, boolean inForInit)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Object pn = eqExpr(ts, source, inForInit);\r
+        while (ts.matchToken(ts.BITAND)) {\r
+            source.append((char)ts.BITAND);\r
+            pn = nf.createBinary(ts.BITAND, pn, eqExpr(ts, source, inForInit));\r
+        }\r
+        return pn;\r
+    }\r
+\r
+    private Object eqExpr(TokenStream ts, Source source, boolean inForInit)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Object pn = relExpr(ts, source, inForInit);\r
+        while (ts.matchToken(ts.EQOP)) {\r
+            source.append((char)ts.EQOP);\r
+            source.append((char)ts.getOp());\r
+            pn = nf.createBinary(ts.EQOP, ts.getOp(), pn,\r
+                                 relExpr(ts, source, inForInit));\r
+        }\r
+        return pn;\r
+    }\r
+\r
+    private Object relExpr(TokenStream ts, Source source, boolean inForInit)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Object pn = shiftExpr(ts, source);\r
+        while (ts.matchToken(ts.RELOP)) {\r
+            int op = ts.getOp();\r
+            if (inForInit && op == ts.IN) {\r
+                ts.ungetToken(ts.RELOP);\r
+                break;\r
+            }\r
+            source.append((char)ts.RELOP);\r
+            source.append((char)op);\r
+            pn = nf.createBinary(ts.RELOP, op, pn,\r
+                                 shiftExpr(ts, source));\r
+        }\r
+        return pn;\r
+    }\r
+\r
+    private Object shiftExpr(TokenStream ts, Source source)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        Object pn = addExpr(ts, source);\r
+        while (ts.matchToken(ts.SHOP)) {\r
+            source.append((char)ts.SHOP);\r
+            source.append((char)ts.getOp());\r
+            pn = nf.createBinary(ts.getOp(), pn, addExpr(ts, source));\r
+        }\r
+        return pn;\r
+    }\r
+\r
+    private Object addExpr(TokenStream ts, Source source)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        int tt;\r
+        Object pn = mulExpr(ts, source);\r
+\r
+        while ((tt = ts.getToken()) == ts.ADD || tt == ts.SUB) {\r
+            source.append((char)tt);\r
+            // flushNewLines\r
+            pn = nf.createBinary(tt, pn, mulExpr(ts, source));\r
+        }\r
+        ts.ungetToken(tt);\r
+\r
+        return pn;\r
+    }\r
+\r
+    private Object mulExpr(TokenStream ts, Source source)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        int tt;\r
+\r
+        Object pn = unaryExpr(ts, source);\r
+\r
+        while ((tt = ts.peekToken()) == ts.MUL ||\r
+               tt == ts.DIV ||\r
+               tt == ts.MOD) {\r
+            tt = ts.getToken();\r
+            source.append((char)tt);\r
+            pn = nf.createBinary(tt, pn, unaryExpr(ts, source));\r
+        }\r
+\r
+\r
+        return pn;\r
+    }\r
+\r
+    private Object unaryExpr(TokenStream ts, Source source)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        int tt;\r
+\r
+        ts.flags |= ts.TSF_REGEXP;\r
+        tt = ts.getToken();\r
+        ts.flags &= ~ts.TSF_REGEXP;\r
+\r
+        switch(tt) {\r
+        case TokenStream.UNARYOP:\r
+            source.append((char)ts.UNARYOP);\r
+            source.append((char)ts.getOp());\r
+            return nf.createUnary(ts.UNARYOP, ts.getOp(),\r
+                                  unaryExpr(ts, source));\r
+\r
+        case TokenStream.ADD:\r
+        case TokenStream.SUB:\r
+            source.append((char)ts.UNARYOP);\r
+            source.append((char)tt);\r
+            return nf.createUnary(ts.UNARYOP, tt, unaryExpr(ts, source));\r
+\r
+        case TokenStream.INC:\r
+        case TokenStream.DEC:\r
+            source.append((char)tt);\r
+            return nf.createUnary(tt, ts.PRE, memberExpr(ts, source, true));\r
+\r
+        case TokenStream.DELPROP:\r
+            source.append((char)ts.DELPROP);\r
+            return nf.createUnary(ts.DELPROP, unaryExpr(ts, source));\r
+\r
+        case TokenStream.ERROR:\r
+            break;\r
+\r
+        default:\r
+            ts.ungetToken(tt);\r
+\r
+            int lineno = ts.getLineno();\r
+\r
+            Object pn = memberExpr(ts, source, true);\r
+\r
+            /* don't look across a newline boundary for a postfix incop.\r
+\r
+             * the rhino scanner seems to work differently than the js\r
+             * scanner here; in js, it works to have the line number check\r
+             * precede the peekToken calls.  It'd be better if they had\r
+             * similar behavior...\r
+             */\r
+            int peeked;\r
+            if (((peeked = ts.peekToken()) == ts.INC ||\r
+                 peeked == ts.DEC) &&\r
+                ts.getLineno() == lineno)\r
+            {\r
+                int pf = ts.getToken();\r
+                source.append((char)pf);\r
+                return nf.createUnary(pf, ts.POST, pn);\r
+            }\r
+            return pn;\r
+        }\r
+        return nf.createName("err"); // Only reached on error.  Try to continue.\r
+        \r
+    }\r
+\r
+    private Object argumentList(TokenStream ts, Source source, Object listNode)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        boolean matched;\r
+        ts.flags |= ts.TSF_REGEXP;\r
+        matched = ts.matchToken(ts.RP);\r
+        ts.flags &= ~ts.TSF_REGEXP;\r
+        if (!matched) {\r
+            boolean first = true;\r
+            do {\r
+                if (!first)\r
+                    source.append((char)ts.COMMA);\r
+                first = false;\r
+                nf.addChildToBack(listNode, assignExpr(ts, source, false));\r
+            } while (ts.matchToken(ts.COMMA));\r
+            \r
+            mustMatchToken(ts, ts.RP, "msg.no.paren.arg");\r
+        }\r
+        source.append((char)ts.RP);\r
+        return listNode;\r
+    }\r
+\r
+    private Object memberExpr(TokenStream ts, Source source,\r
+                              boolean allowCallSyntax)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        int tt;\r
+\r
+        Object pn;\r
+        \r
+        /* Check for new expressions. */\r
+        ts.flags |= ts.TSF_REGEXP;\r
+        tt = ts.peekToken();\r
+        ts.flags &= ~ts.TSF_REGEXP;\r
+        if (tt == ts.NEW) {\r
+            /* Eat the NEW token. */\r
+            ts.getToken();\r
+            source.append((char)ts.NEW);\r
+\r
+            /* Make a NEW node to append to. */\r
+            pn = nf.createLeaf(ts.NEW);\r
+            nf.addChildToBack(pn, memberExpr(ts, source, false));\r
+\r
+            if (ts.matchToken(ts.LP)) {\r
+                source.append((char)ts.LP);\r
+                /* Add the arguments to pn, if any are supplied. */\r
+                pn = argumentList(ts, source, pn);\r
+            }\r
+\r
+            /* XXX there's a check in the C source against\r
+             * "too many constructor arguments" - how many\r
+             * do we claim to support?\r
+             */\r
+            \r
+            /* Experimental syntax:  allow an object literal to follow a new expression,\r
+             * which will mean a kind of anonymous class built with the JavaAdapter.\r
+             * the object literal will be passed as an additional argument to the constructor.\r
+             */\r
+            tt = ts.peekToken();\r
+            if (tt == ts.LC) {\r
+               nf.addChildToBack(pn, primaryExpr(ts, source));\r
+            }\r
+        } else {\r
+            pn = primaryExpr(ts, source);\r
+        }\r
+\r
+        lastExprEndLine = ts.getLineno();\r
+        while ((tt = ts.getToken()) > ts.EOF) {\r
+            if (tt == ts.DOT) {\r
+                source.append((char)ts.DOT);\r
+                mustMatchToken(ts, ts.NAME, "msg.no.name.after.dot");\r
+                String s = ts.getString();\r
+                source.addString(ts.NAME, s);\r
+                pn = nf.createBinary(ts.DOT, pn,\r
+                                     nf.createName(ts.getString()));\r
+                /* pn = nf.createBinary(ts.DOT, pn, memberExpr(ts))\r
+                 * is the version in Brendan's IR C version.  Not in ECMA...\r
+                 * does it reflect the 'new' operator syntax he mentioned?\r
+                 */\r
+                lastExprEndLine = ts.getLineno();\r
+            } else if (tt == ts.LB) {\r
+                source.append((char)ts.LB);\r
+                pn = nf.createBinary(ts.LB, pn, expr(ts, source, false));\r
+\r
+                mustMatchToken(ts, ts.RB, "msg.no.bracket.index");\r
+                source.append((char)ts.RB);\r
+                lastExprEndLine = ts.getLineno();\r
+            } else if (allowCallSyntax && tt == ts.LP) {\r
+                /* make a call node */\r
+\r
+                pn = nf.createUnary(ts.CALL, pn);\r
+                source.append((char)ts.LP);\r
+                \r
+                /* Add the arguments to pn, if any are supplied. */\r
+                pn = argumentList(ts, source, pn);\r
+                lastExprEndLine = ts.getLineno();\r
+            } else {\r
+                ts.ungetToken(tt);\r
+\r
+                break;\r
+            }\r
+        }\r
+\r
+        return pn;\r
+    }\r
+\r
+    private Object primaryExpr(TokenStream ts, Source source)\r
+        throws IOException, JavaScriptException\r
+    {\r
+        int tt;\r
+\r
+        Object pn;\r
+\r
+        ts.flags |= ts.TSF_REGEXP;\r
+        tt = ts.getToken();\r
+        ts.flags &= ~ts.TSF_REGEXP;\r
+\r
+        switch(tt) {\r
+\r
+        case TokenStream.FUNCTION:\r
+            return function(ts, source, true);\r
+\r
+        case TokenStream.LB:\r
+            {\r
+                source.append((char)ts.LB);\r
+                pn = nf.createLeaf(ts.ARRAYLIT);\r
+\r
+                ts.flags |= ts.TSF_REGEXP;\r
+                boolean matched = ts.matchToken(ts.RB);\r
+                ts.flags &= ~ts.TSF_REGEXP;\r
+\r
+                if (!matched) {\r
+                    boolean first = true;\r
+                    do {\r
+                        ts.flags |= ts.TSF_REGEXP;\r
+                        tt = ts.peekToken();\r
+                        ts.flags &= ~ts.TSF_REGEXP;\r
+\r
+                        if (!first)\r
+                            source.append((char)ts.COMMA);\r
+                        else\r
+                            first = false;\r
+\r
+                        if (tt == ts.RB) {  // to fix [,,,].length behavior...\r
+                            break;\r
+                        }\r
+\r
+                        if (tt == ts.COMMA) {\r
+                            nf.addChildToBack(pn, nf.createLeaf(ts.PRIMARY,\r
+                                                                ts.UNDEFINED));\r
+                        } else {\r
+                            nf.addChildToBack(pn, assignExpr(ts, source, false));\r
+                        }\r
+\r
+                    } while (ts.matchToken(ts.COMMA));\r
+                    mustMatchToken(ts, ts.RB, "msg.no.bracket.arg");\r
+                }\r
+                source.append((char)ts.RB);\r
+                return nf.createArrayLiteral(pn);\r
+            }\r
+\r
+        case TokenStream.LC: {\r
+            pn = nf.createLeaf(ts.OBJLIT);\r
+\r
+            source.append((char)ts.LC);\r
+            if (!ts.matchToken(ts.RC)) {\r
+\r
+                boolean first = true;\r
+            commaloop:\r
+                do {\r
+                    Object property;\r
+\r
+                    if (!first)\r
+                        source.append((char)ts.COMMA);\r
+                    else\r
+                        first = false;\r
+\r
+                    tt = ts.getToken();\r
+                    switch(tt) {\r
+                        // map NAMEs to STRINGs in object literal context.\r
+                    case TokenStream.NAME:\r
+                    case TokenStream.STRING:\r
+                        String s = ts.getString();\r
+                        source.addString(ts.NAME, s);\r
+                        property = nf.createString(ts.getString());\r
+                        break;\r
+                    case TokenStream.NUMBER:\r
+                        Number n = ts.getNumber();\r
+                        source.addNumber(n);\r
+                        property = nf.createNumber(n);\r
+                        break;\r
+                    case TokenStream.RC:\r
+                        // trailing comma is OK.\r
+                        ts.ungetToken(tt);\r
+                        break commaloop;\r
+                    default:\r
+                        reportError(ts, "msg.bad.prop");\r
+                        break commaloop;\r
+                    }\r
+                    mustMatchToken(ts, ts.COLON, "msg.no.colon.prop");\r
+\r
+                    // OBJLIT is used as ':' in object literal for\r
+                    // decompilation to solve spacing ambiguity.\r
+                    source.append((char)ts.OBJLIT);\r
+                    nf.addChildToBack(pn, property);\r
+                    nf.addChildToBack(pn, assignExpr(ts, source, false));\r
+\r
+                } while (ts.matchToken(ts.COMMA));\r
+\r
+                mustMatchToken(ts, ts.RC, "msg.no.brace.prop");\r
+            }\r
+            source.append((char)ts.RC);\r
+            return nf.createObjectLiteral(pn);\r
+        }\r
+\r
+        case TokenStream.LP:\r
+\r
+            /* Brendan's IR-jsparse.c makes a new node tagged with\r
+             * TOK_LP here... I'm not sure I understand why.  Isn't\r
+             * the grouping already implicit in the structure of the\r
+             * parse tree?  also TOK_LP is already overloaded (I\r
+             * think) in the C IR as 'function call.'  */\r
+            source.append((char)ts.LP);\r
+            pn = expr(ts, source, false);\r
+            source.append((char)ts.RP);\r
+            mustMatchToken(ts, ts.RP, "msg.no.paren");\r
+            return pn;\r
+\r
+        case TokenStream.NAME:\r
+            String name = ts.getString();\r
+            source.addString(ts.NAME, name);\r
+            return nf.createName(name);\r
+\r
+        case TokenStream.NUMBER:\r
+            Number n = ts.getNumber();\r
+            source.addNumber(n);\r
+            return nf.createNumber(n);\r
+\r
+        case TokenStream.STRING:\r
+            String s = ts.getString();\r
+            source.addString(ts.STRING, s);\r
+            return nf.createString(s);\r
+\r
+        case TokenStream.OBJECT:\r
+        {\r
+            String flags = ts.regExpFlags;\r
+            ts.regExpFlags = null;\r
+            String re = ts.getString();\r
+            source.addString(ts.OBJECT, '/' + re + '/' + flags);\r
+            return nf.createRegExp(re, flags);\r
+        }\r
+\r
+        case TokenStream.PRIMARY:\r
+            source.append((char)ts.PRIMARY);\r
+            source.append((char)ts.getOp());\r
+            return nf.createLeaf(ts.PRIMARY, ts.getOp());\r
+\r
+        case TokenStream.RESERVED:\r
+            reportError(ts, "msg.reserved.id");\r
+            break;\r
+\r
+        case TokenStream.ERROR:\r
+            /* the scanner or one of its subroutines reported the error. */\r
+            break;\r
+\r
+        default:\r
+            reportError(ts, "msg.syntax");\r
+            break;\r
+\r
+        }\r
+        return null;    // should never reach here\r
+    }\r
+\r
+    private int lastExprEndLine; // Hack to handle function expr termination.\r
+    private IRFactory nf;\r
+    private ErrorReporter er;\r
+    private boolean ok; // Did the parse encounter an error?\r
+}\r
+\r
+/**\r
+ * This class saves decompilation information about the source.\r
+ * Source information is returned from the parser as a String\r
+ * associated with function nodes and with the toplevel script.  When\r
+ * saved in the constant pool of a class, this string will be UTF-8\r
+ * encoded, and token values will occupy a single byte.\r
+\r
+ * Source is saved (mostly) as token numbers.  The tokens saved pretty\r
+ * much correspond to the token stream of a 'canonical' representation\r
+ * of the input program, as directed by the parser.  (There were a few\r
+ * cases where tokens could have been left out where decompiler could\r
+ * easily reconstruct them, but I left them in for clarity).  (I also\r
+ * looked adding source collection to TokenStream instead, where I\r
+ * could have limited the changes to a few lines in getToken... but\r
+ * this wouldn't have saved any space in the resulting source\r
+ * representation, and would have meant that I'd have to duplicate\r
+ * parser logic in the decompiler to disambiguate situations where\r
+ * newlines are important.)  NativeFunction.decompile expands the\r
+ * tokens back into their string representations, using simple\r
+ * lookahead to correct spacing and indentation.\r
+\r
+ * Token types with associated ops (ASSIGN, SHOP, PRIMARY, etc.) are\r
+ * saved as two-token pairs.  Number tokens are stored inline, as a\r
+ * NUMBER token, a character representing the type, and either 1 or 4\r
+ * characters representing the bit-encoding of the number.  String\r
+ * types NAME, STRING and OBJECT are currently stored as a token type,\r
+ * followed by a character giving the length of the string (assumed to\r
+ * be less than 2^16), followed by the characters of the string\r
+ * inlined into the source string.  Changing this to some reference to\r
+ * to the string in the compiled class' constant pool would probably\r
+ * save a lot of space... but would require some method of deriving\r
+ * the final constant pool entry from information available at parse\r
+ * time.\r
+\r
+ * Nested functions need a similar mechanism... fortunately the nested\r
+ * functions for a given function are generated in source order.\r
+ * Nested functions are encoded as FUNCTION followed by a function\r
+ * number (encoded as a character), which is enough information to\r
+ * find the proper generated NativeFunction instance.\r
+\r
+ * OPT source info collection is a potential performance bottleneck;\r
+ * Source wraps a java.lang.StringBuffer, which is synchronized.  It\r
+ * might be faster to implement Source with its own char buffer and\r
+ * toString method.\r
+\r
+ */\r
+\r
+final class Source {\r
+    Source() {\r
+        // OPT the default 16 is probably too small, but it's not\r
+        // clear just what size would work best for most javascript.\r
+        // It'd be nice to know what characterizes the javascript\r
+        // that's out there.\r
+        buf = new StringBuffer(64);\r
+    }\r
+\r
+    void append(char c) {\r
+        buf.append(c);\r
+    }\r
+\r
+    void addString(int type, String str) {\r
+        buf.append((char)type);\r
+        // java string length < 2^16?\r
+        buf.append((char)str.length());\r
+        buf.append(str);\r
+    }\r
+\r
+    void addNumber(Number n) {\r
+        buf.append((char)TokenStream.NUMBER);\r
+\r
+        /* encode the number in the source stream.\r
+         * Save as NUMBER type (char | char char char char)\r
+         * where type is\r
+         * 'D' - double, 'S' - short, 'J' - long.\r
+\r
+         * We need to retain float vs. integer type info to keep the\r
+         * behavior of liveconnect type-guessing the same after\r
+         * decompilation.  (Liveconnect tries to present 1.0 to Java\r
+         * as a float/double)\r
+         * OPT: This is no longer true. We could compress the format.\r
+\r
+         * This may not be the most space-efficient encoding;\r
+         * the chars created below may take up to 3 bytes in\r
+         * constant pool UTF-8 encoding, so a Double could take\r
+         * up to 12 bytes.\r
+         */\r
+\r
+        if (n instanceof Double || n instanceof Float) {\r
+            // if it's floating point, save as a Double bit pattern.\r
+            // (12/15/97 our scanner only returns Double for f.p.)\r
+            buf.append('D');\r
+            long lbits = Double.doubleToLongBits(n.doubleValue());\r
+\r
+            buf.append((char)((lbits >> 48) & 0xFFFF));\r
+            buf.append((char)((lbits >> 32) & 0xFFFF));\r
+            buf.append((char)((lbits >> 16) & 0xFFFF));\r
+            buf.append((char)(lbits & 0xFFFF));\r
+        } else {\r
+            long lbits = n.longValue();\r
+            // will it fit in a char?\r
+            // (we can ignore negative values, bc they're already prefixed\r
+            //  by UNARYOP SUB)\r
+            // this gives a short encoding for integer values up to 2^16.\r
+            if (lbits <= Character.MAX_VALUE) {\r
+                buf.append('S');\r
+                buf.append((char)lbits);\r
+            } else { // Integral, but won't fit in a char. Store as a long.\r
+                buf.append('J');\r
+                buf.append((char)((lbits >> 48) & 0xFFFF));\r
+                buf.append((char)((lbits >> 32) & 0xFFFF));\r
+                buf.append((char)((lbits >> 16) & 0xFFFF));\r
+                buf.append((char)(lbits & 0xFFFF));\r
+            }\r
+        }\r
+    }\r
+\r
+    char functionNumber;\r
+    StringBuffer buf;\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/PreorderNodeIterator.java b/src/org/mozilla/javascript/PreorderNodeIterator.java
new file mode 100644 (file)
index 0000000..76990e4
--- /dev/null
@@ -0,0 +1,92 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Roger Lawrence\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.Stack;\r
+\r
+/**\r
+ * This class implements a preorder tree iterator for the Node class.\r
+ *\r
+ * @see Node\r
+ * @author Norris Boyd\r
+ */\r
+public class PreorderNodeIterator {\r
+    public PreorderNodeIterator(Node n) {\r
+       start = n;\r
+       stack = new Stack();\r
+    }\r
+\r
+    public Node currentNode() {\r
+        return current;\r
+    }\r
+\r
+    public Node getCurrentParent() {\r
+       return currentParent;\r
+    }\r
+\r
+    public Node nextNode() {\r
+        if (current == null)\r
+            return current = start;\r
+       if (current.first != null) {\r
+           stack.push(current);\r
+           currentParent = current;\r
+           current = current.first;\r
+       } else {\r
+           current = current.next;\r
+           boolean isEmpty;\r
+            for (;;) {\r
+                isEmpty = stack.isEmpty();\r
+                if (isEmpty || current != null)\r
+                       break;\r
+                current = (Node) stack.pop();\r
+                current = current.next;\r
+           }\r
+           currentParent = isEmpty ? null : (Node) stack.peek();\r
+       }\r
+       return current;\r
+    }\r
+\r
+    public void replaceCurrent(Node newNode) {\r
+        currentParent.replaceChild(current, newNode);\r
+        current = newNode;\r
+    }\r
+\r
+    private Node start;\r
+    private Node current;\r
+    private Node currentParent;\r
+    private Stack stack;\r
+}\r
diff --git a/src/org/mozilla/javascript/PropertyException.java b/src/org/mozilla/javascript/PropertyException.java
new file mode 100644 (file)
index 0000000..592c3c8
--- /dev/null
@@ -0,0 +1,65 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * Thrown if errors are detected while attempting to define a property of\r
+ * a host object from a Java class or method, or if a property is not found.\r
+ */\r
+public class PropertyException extends Exception {\r
+\r
+    public PropertyException(String detail) {\r
+        super(detail);\r
+    }\r
+\r
+    static PropertyException withMessage0(String messageId) {\r
+        return new PropertyException(Context.getMessage0(messageId));\r
+    }\r
+\r
+    static PropertyException withMessage1(String messageId, Object arg1) {\r
+        return new PropertyException(Context.getMessage1(messageId, arg1));\r
+    }\r
+\r
+    static PropertyException withMessage2\r
+        (String messageId, Object arg1, Object arg2) \r
+    {\r
+        return new PropertyException\r
+            (Context.getMessage2(messageId, arg1, arg2));\r
+    }\r
+\r
+}\r
diff --git a/src/org/mozilla/javascript/RegExpProxy.java b/src/org/mozilla/javascript/RegExpProxy.java
new file mode 100644 (file)
index 0000000..b12523f
--- /dev/null
@@ -0,0 +1,68 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Roger Lawrence\r
+ *\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * A proxy for the regexp package, so that the regexp package can be\r
+ * loaded optionally.\r
+ * \r
+ * @author Norris Boyd\r
+ */\r
+public interface RegExpProxy {\r
+    \r
+    public boolean isRegExp(Object obj);\r
+    \r
+    public Object newRegExp(Context cx, Scriptable scope, String source, \r
+                            String global, boolean flat);\r
+    \r
+    public Object match(Context cx, Scriptable scope,\r
+                        Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException;\r
+\r
+    public Object search(Context cx, Scriptable scope, \r
+                         Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException;\r
+\r
+    public Object replace(Context cx, Scriptable scope, \r
+                          Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException;\r
\r
+    public int find_split(Scriptable scope, String target, String separator, \r
+                          Object re, int[] ip, int[] matchlen, \r
+                          boolean[] matched, String[][] parensp);\r
+}\r
diff --git a/src/org/mozilla/javascript/Script.java b/src/org/mozilla/javascript/Script.java
new file mode 100644 (file)
index 0000000..f867aeb
--- /dev/null
@@ -0,0 +1,73 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * All compiled scripts implement this interface.\r
+ * <p>\r
+ * This class encapsulates script execution relative to an\r
+ * object scope.\r
+ * @since 1.3\r
+ * @author Norris Boyd\r
+ */\r
+\r
+public interface Script {\r
+\r
+    /**\r
+     * Execute the script.\r
+     * <p>\r
+     * The script is executed in a particular runtime Context, which\r
+     * must be associated with the current thread.\r
+     * The script is executed relative to a scope--definitions and\r
+     * uses of global top-level variables and functions will access\r
+     * properties of the scope object. For compliant ECMA\r
+     * programs, the scope must be an object that has been initialized\r
+     * as a global object using <code>Context.initStandardObjects</code>.\r
+     * <p>\r
+     *\r
+     * @param cx the Context associated with the current thread\r
+     * @param scope the scope to execute relative to\r
+     * @return the result of executing the script\r
+     * @see org.mozilla.javascript.Context#initStandardObjects\r
+     * @exception JavaScriptException if an uncaught JavaScript exception\r
+     *            occurred while executing the script\r
+     */\r
+    public Object exec(Context cx, Scriptable scope)\r
+        throws JavaScriptException;\r
+\r
+}\r
diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java
new file mode 100644 (file)
index 0000000..e1d6821
--- /dev/null
@@ -0,0 +1,2168 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Patrick Beard\r
+ * Norris Boyd\r
+ * Igor Bukanov \r
+ * Roger Lawrence\r
+ * Frank Mitchell\r
+ * Andrew Wason\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.*;\r
+import java.lang.reflect.*;\r
+//import org.mozilla.classfile.DefiningClassLoader;\r
+\r
+/**\r
+ * This is the class that implements the runtime.\r
+ *\r
+ * @author Norris Boyd\r
+ */\r
+\r
+public class ScriptRuntime {\r
+\r
+    /**\r
+     * No instances should be created.\r
+     */\r
+    protected ScriptRuntime() {\r
+    }\r
+\r
+    /*\r
+     * There's such a huge space (and some time) waste for the Foo.class\r
+     * syntax: the compiler sticks in a test of a static field in the\r
+     * enclosing class for null and the code for creating the class value.\r
+     * It has to do this since the reference has to get pushed off til\r
+     * executiontime (i.e. can't force an early load), but for the\r
+     * 'standard' classes - especially those in java.lang, we can trust\r
+     * that they won't cause problems by being loaded early.\r
+     */\r
+\r
+    public final static Class UndefinedClass = Undefined.class;\r
+    public final static Class ScriptableClass = Scriptable.class;\r
+    public final static Class StringClass = String.class;\r
+    public final static Class NumberClass = Number.class;\r
+    public final static Class BooleanClass = Boolean.class;\r
+    public final static Class ByteClass = Byte.class;\r
+    public final static Class ShortClass = Short.class;\r
+    public final static Class IntegerClass = Integer.class;\r
+    public final static Class LongClass = Long.class;\r
+    public final static Class FloatClass = Float.class;\r
+    public final static Class DoubleClass = Double.class;\r
+    public final static Class CharacterClass = Character.class;\r
+    public final static Class ObjectClass = Object.class;\r
+    public final static Class FunctionClass = Function.class;\r
+    public final static Class ClassClass = Class.class;\r
+\r
+    /**\r
+     * Convert the value to a boolean.\r
+     *\r
+     * See ECMA 9.2.\r
+     */\r
+    public static boolean toBoolean(Object val) {\r
+        if (val == null)\r
+            return false;\r
+        if (val instanceof Scriptable) {\r
+            if (Context.getContext().isVersionECMA1()) {\r
+                // pure ECMA\r
+                return val != Undefined.instance;\r
+            }\r
+            // ECMA extension\r
+            val = ((Scriptable) val).getDefaultValue(BooleanClass);\r
+            if (val instanceof Scriptable)\r
+                throw errorWithClassName("msg.primitive.expected", val);\r
+            // fall through\r
+        }\r
+        if (val instanceof String)\r
+            return ((String) val).length() != 0;\r
+        if (val instanceof Number) {\r
+            double d = ((Number) val).doubleValue();\r
+            return (d == d && d != 0.0);\r
+        }\r
+        if (val instanceof Boolean)\r
+            return ((Boolean) val).booleanValue();\r
+        throw errorWithClassName("msg.invalid.type", val);\r
+    }\r
+\r
+    public static boolean toBoolean(Object[] args, int index) {\r
+        return (index < args.length) ? toBoolean(args[index]) : false;\r
+    }\r
+    /**\r
+     * Convert the value to a number.\r
+     *\r
+     * See ECMA 9.3.\r
+     */\r
+    public static double toNumber(Object val) {\r
+        if (val == null) \r
+            return +0.0;\r
+        if (val instanceof Scriptable) {\r
+            val = ((Scriptable) val).getDefaultValue(NumberClass);\r
+            if (val != null && val instanceof Scriptable)\r
+                throw errorWithClassName("msg.primitive.expected", val);\r
+            // fall through\r
+        }\r
+        if (val instanceof String)\r
+            return toNumber((String) val);\r
+        if (val instanceof Number)\r
+            return ((Number) val).doubleValue();\r
+        if (val instanceof Boolean)\r
+            return ((Boolean) val).booleanValue() ? 1 : +0.0;\r
+        throw errorWithClassName("msg.invalid.type", val);\r
+    }\r
+\r
+    public static double toNumber(Object[] args, int index) {\r
+        return (index < args.length) ? toNumber(args[index]) : NaN;\r
+    }\r
+    \r
+    // This definition of NaN is identical to that in java.lang.Double\r
+    // except that it is not final. This is a workaround for a bug in\r
+    // the Microsoft VM, versions 2.01 and 3.0P1, that causes some uses\r
+    // (returns at least) of Double.NaN to be converted to 1.0.\r
+    // So we use ScriptRuntime.NaN instead of Double.NaN.\r
+    public static double NaN = 0.0d / 0.0;\r
+    public static Double NaNobj = new Double(0.0d / 0.0);\r
+\r
+    // A similar problem exists for negative zero.\r
+    public static double negativeZero = -0.0;\r
+\r
+    /*\r
+     * Helper function for toNumber, parseInt, and TokenStream.getToken.\r
+     */\r
+    static double stringToNumber(String s, int start, int radix) {\r
+        char digitMax = '9';\r
+        char lowerCaseBound = 'a';\r
+        char upperCaseBound = 'A';\r
+        int len = s.length();\r
+        if (radix < 10) {\r
+            digitMax = (char) ('0' + radix - 1);\r
+        }\r
+        if (radix > 10) {\r
+            lowerCaseBound = (char) ('a' + radix - 10);\r
+            upperCaseBound = (char) ('A' + radix - 10);\r
+        }\r
+        int end;\r
+        double sum = 0.0;\r
+        for (end=start; end < len; end++) {\r
+            char c = s.charAt(end);\r
+            int newDigit;\r
+            if ('0' <= c && c <= digitMax)\r
+                newDigit = c - '0';\r
+            else if ('a' <= c && c < lowerCaseBound)\r
+                newDigit = c - 'a' + 10;\r
+            else if ('A' <= c && c < upperCaseBound)\r
+                newDigit = c - 'A' + 10;\r
+            else\r
+                break;\r
+            sum = sum*radix + newDigit;\r
+        }\r
+        if (start == end) {\r
+            return NaN;\r
+        }\r
+        if (sum >= 9007199254740992.0) {\r
+            if (radix == 10) {\r
+                /* If we're accumulating a decimal number and the number\r
+                 * is >= 2^53, then the result from the repeated multiply-add\r
+                 * above may be inaccurate.  Call Java to get the correct\r
+                 * answer.\r
+                 */\r
+                try {\r
+                    return Double.valueOf(s.substring(start, end)).doubleValue();\r
+                } catch (NumberFormatException nfe) {\r
+                    return NaN;\r
+                }\r
+            } else if (radix == 2 || radix == 4 || radix == 8 ||\r
+                       radix == 16 || radix == 32)\r
+            {\r
+                /* The number may also be inaccurate for one of these bases.\r
+                 * This happens if the addition in value*radix + digit causes\r
+                 * a round-down to an even least significant mantissa bit\r
+                 * when the first dropped bit is a one.  If any of the\r
+                 * following digits in the number (which haven't been added\r
+                 * in yet) are nonzero then the correct action would have\r
+                 * been to round up instead of down.  An example of this\r
+                 * occurs when reading the number 0x1000000000000081, which\r
+                 * rounds to 0x1000000000000000 instead of 0x1000000000000100.\r
+                 */\r
+                BinaryDigitReader bdr = new BinaryDigitReader(radix, s, start, end);\r
+                int bit;\r
+                sum = 0.0;\r
+\r
+                /* Skip leading zeros. */\r
+                do {\r
+                    bit = bdr.getNextBinaryDigit();\r
+                } while (bit == 0);\r
+\r
+                if (bit == 1) {\r
+                    /* Gather the 53 significant bits (including the leading 1) */\r
+                    sum = 1.0;\r
+                    for (int j = 52; j != 0; j--) {\r
+                        bit = bdr.getNextBinaryDigit();\r
+                        if (bit < 0)\r
+                            return sum;\r
+                        sum = sum*2 + bit;\r
+                    }\r
+                    /* bit54 is the 54th bit (the first dropped from the mantissa) */\r
+                    int bit54 = bdr.getNextBinaryDigit();\r
+                    if (bit54 >= 0) {\r
+                        double factor = 2.0;\r
+                        int sticky = 0;  /* sticky is 1 if any bit beyond the 54th is 1 */\r
+                        int bit3;\r
+\r
+                        while ((bit3 = bdr.getNextBinaryDigit()) >= 0) {\r
+                            sticky |= bit3;\r
+                            factor *= 2;\r
+                        }\r
+                        sum += bit54 & (bit | sticky);\r
+                        sum *= factor;\r
+                    }\r
+                }\r
+            }\r
+            /* We don't worry about inaccurate numbers for any other base. */\r
+        }\r
+        return sum;\r
+    }\r
+\r
+\r
+    /**\r
+     * ToNumber applied to the String type\r
+     *\r
+     * See ECMA 9.3.1\r
+     */\r
+    public static double toNumber(String s) {\r
+        int len = s.length();\r
+        int start = 0;\r
+        char startChar;\r
+        for (;;) {\r
+            if (start == len) {\r
+                // Empty or contains only whitespace\r
+                return +0.0;\r
+            }\r
+            startChar = s.charAt(start);\r
+            if (!Character.isWhitespace(startChar))\r
+                break;\r
+            start++;\r
+        }\r
+\r
+        if (startChar == '0' && start+2 < len &&\r
+            Character.toLowerCase(s.charAt(start+1)) == 'x')\r
+            // A hexadecimal number\r
+            return stringToNumber(s, start + 2, 16);\r
+\r
+        if ((startChar == '+' || startChar == '-') && start+3 < len &&\r
+            s.charAt(start+1) == '0' &&\r
+            Character.toLowerCase(s.charAt(start+2)) == 'x') {\r
+            // A hexadecimal number\r
+            double val = stringToNumber(s, start + 3, 16);\r
+            return startChar == '-' ? -val : val;\r
+        }\r
+\r
+        int end = len - 1;\r
+        char endChar;\r
+        while (Character.isWhitespace(endChar = s.charAt(end)))\r
+            end--;\r
+        if (endChar == 'y') {\r
+            // check for "Infinity"\r
+            if (startChar == '+' || startChar == '-')\r
+                start++;\r
+            if (start + 7 == end && s.regionMatches(start, "Infinity", 0, 8))\r
+                return startChar == '-'\r
+                    ? Double.NEGATIVE_INFINITY\r
+                    : Double.POSITIVE_INFINITY;\r
+            return NaN;\r
+        }\r
+        // A non-hexadecimal, non-infinity number:\r
+        // just try a normal floating point conversion\r
+        String sub = s.substring(start, end+1);\r
+        if (MSJVM_BUG_WORKAROUNDS) {\r
+            // The MS JVM will accept non-conformant strings\r
+            // rather than throwing a NumberFormatException\r
+            // as it should.\r
+            for (int i=sub.length()-1; i >= 0; i--) {\r
+                char c = sub.charAt(i);\r
+                if (('0' <= c && c <= '9') || c == '.' ||\r
+                    c == 'e' || c == 'E'  ||\r
+                    c == '+' || c == '-')\r
+                    continue;\r
+                return NaN;\r
+            }\r
+        }\r
+        try {\r
+            return Double.valueOf(sub).doubleValue();\r
+        } catch (NumberFormatException ex) {\r
+            return NaN;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Helper function for builtin objects that use the varargs form.\r
+     * ECMA function formal arguments are undefined if not supplied;\r
+     * this function pads the argument array out to the expected\r
+     * length, if necessary.\r
+     */\r
+    public static Object[] padArguments(Object[] args, int count) {\r
+        if (count < args.length)\r
+            return args;\r
+\r
+        int i;\r
+        Object[] result = new Object[count];\r
+        for (i = 0; i < args.length; i++) {\r
+            result[i] = args[i];\r
+        }\r
+\r
+        for (; i < count; i++) {\r
+            result[i] = Undefined.instance;\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /* Work around Microsoft Java VM bugs. */\r
+    private final static boolean MSJVM_BUG_WORKAROUNDS = true;\r
+\r
+    /**\r
+     * For escaping strings printed by object and array literals; not quite\r
+     * the same as 'escape.'\r
+     */\r
+    public static String escapeString(String s) {\r
+        // ack!  Java lacks \v.\r
+        String escapeMap = "\bb\ff\nn\rr\tt\u000bv\"\"''";\r
+        StringBuffer result = new StringBuffer(s.length());\r
+\r
+        for(int i=0; i < s.length(); i++) {\r
+            char c = s.charAt(i);\r
+\r
+            // an ordinary print character\r
+            if (c >= ' ' && c <= '~'     // string.h isprint()\r
+                && c != '"')\r
+            {\r
+                result.append(c);\r
+                continue;\r
+            }\r
+\r
+            // an \escaped sort of character\r
+            int index;\r
+            if ((index = escapeMap.indexOf(c)) >= 0) {\r
+                result.append("\\");\r
+                result.append(escapeMap.charAt(index + 1));\r
+                continue;\r
+            }\r
+\r
+            // 2-digit hex?\r
+            if (c < 256) {\r
+                String hex = Integer.toHexString((int) c);\r
+                if (hex.length() == 1) {\r
+                    result.append("\\x0");\r
+                    result.append(hex);\r
+                } else {\r
+                    result.append("\\x");\r
+                    result.append(hex);\r
+                }\r
+                continue;\r
+            }\r
+\r
+            // nope.  Unicode.\r
+            String hex = Integer.toHexString((int) c);\r
+            // cool idiom courtesy Shaver.\r
+            result.append("\\u");\r
+            for (int l = hex.length(); l < 4; l++)\r
+                result.append('0');\r
+            result.append(hex);\r
+        }\r
+\r
+        return result.toString();\r
+    }\r
+\r
+\r
+    /**\r
+     * Convert the value to a string.\r
+     *\r
+     * See ECMA 9.8.\r
+     */\r
+    public static String toString(Object val) {\r
+        for (;;) {\r
+            if (val == null)\r
+                return "null";\r
+            if (val instanceof Scriptable) {\r
+                val = ((Scriptable) val).getDefaultValue(StringClass);\r
+                if (val != Undefined.instance && val instanceof Scriptable) {\r
+                    throw errorWithClassName("msg.primitive.expected", val);\r
+                }\r
+                continue;\r
+            }\r
+            if (val instanceof Number) {\r
+                // XXX should we just teach NativeNumber.stringValue()\r
+                // about Numbers?\r
+                return numberToString(((Number) val).doubleValue(), 10);\r
+            }\r
+            return val.toString();\r
+        }\r
+    }\r
+\r
+    public static String toString(Object[] args, int index) {\r
+        return (index < args.length) ? toString(args[index]) : "undefined";\r
+    }\r
+\r
+    /**\r
+     * Optimized version of toString(Object) for numbers.\r
+     */\r
+    public static String toString(double val) {\r
+        return numberToString(val, 10);\r
+    }\r
+    \r
+    public static String numberToString(double d, int base) {\r
+        if (d != d)\r
+            return "NaN";\r
+        if (d == Double.POSITIVE_INFINITY)\r
+            return "Infinity";\r
+        if (d == Double.NEGATIVE_INFINITY)\r
+            return "-Infinity";\r
+        if (d == 0.0)\r
+            return "0";\r
+\r
+        if ((base < 2) || (base > 36)) {\r
+            throw Context.reportRuntimeError1(\r
+                "msg.bad.radix", Integer.toString(base));\r
+        }\r
+\r
+        if (base != 10) {\r
+            return DToA.JS_dtobasestr(base, d);\r
+        } else {\r
+            StringBuffer result = new StringBuffer();\r
+            DToA.JS_dtostr(result, DToA.DTOSTR_STANDARD, 0, d);\r
+            return result.toString();\r
+        }\r
+    \r
+    }\r
+\r
+    /**\r
+     * Convert the value to an object.\r
+     *\r
+     * See ECMA 9.9.\r
+     */\r
+    public static Scriptable toObject(Scriptable scope, Object val) {\r
+        return toObject(scope, val, null);\r
+    }\r
+\r
+    public static Scriptable toObject(Scriptable scope, Object val,\r
+                                      Class staticClass)\r
+    {\r
+        if (val == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        if (val instanceof Scriptable) {\r
+            if (val == Undefined.instance) {\r
+                throw NativeGlobal.typeError0("msg.undef.to.object", scope);\r
+            }\r
+            return (Scriptable) val;\r
+        }\r
+        String className = val instanceof String ? "String" :\r
+                           val instanceof Number ? "Number" :\r
+                           val instanceof Boolean ? "Boolean" :\r
+                           null;\r
+\r
+        if (className == null) {\r
+            // Extension: Wrap as a LiveConnect object.\r
+            Object wrapped = NativeJavaObject.wrap(scope, val, staticClass);\r
+            if (wrapped instanceof Scriptable)\r
+                return (Scriptable) wrapped;\r
+            throw errorWithClassName("msg.invalid.type", val);\r
+        }\r
+\r
+        Object[] args = { val };\r
+        scope = ScriptableObject.getTopLevelScope(scope);\r
+        Scriptable result = newObject(Context.getContext(), org.xwt.util.JSObject.defaultObjects, className, args);\r
+        return result;\r
+    }\r
+\r
+    public static Scriptable newObject(Context cx, Scriptable scope,\r
+                                       String constructorName, Object[] args)\r
+    {\r
+        Exception re = null;\r
+        try {\r
+            return cx.newObject(scope, constructorName, args);\r
+        }\r
+        catch (NotAFunctionException e) {\r
+            re = e;\r
+        }\r
+        catch (PropertyException e) {\r
+            re = e;\r
+        }\r
+        catch (JavaScriptException e) {\r
+            re = e;\r
+        }\r
+        throw cx.reportRuntimeError(re.getMessage());\r
+    }\r
+\r
+    /**\r
+     *\r
+     * See ECMA 9.4.\r
+     */\r
+    public static double toInteger(Object val) {\r
+        return toInteger(toNumber(val));\r
+    }\r
+\r
+    // convenience method\r
+    public static double toInteger(double d) {\r
+        // if it's NaN\r
+        if (d != d)\r
+            return +0.0;\r
+\r
+        if (d == 0.0 ||\r
+            d == Double.POSITIVE_INFINITY ||\r
+            d == Double.NEGATIVE_INFINITY)\r
+            return d;\r
+\r
+        if (d > 0.0)\r
+            return Math.floor(d);\r
+        else\r
+            return Math.ceil(d);\r
+    }\r
+\r
+    public static double toInteger(Object[] args, int index) {\r
+        return (index < args.length) ? toInteger(args[index]) : +0.0;\r
+    }\r
+\r
+    /**\r
+     *\r
+     * See ECMA 9.5.\r
+     */\r
+    public static int toInt32(Object val) {\r
+        // 0x100000000 gives me a numeric overflow...\r
+        double two32 = 4294967296.0;\r
+        double two31 = 2147483648.0;\r
+\r
+        // short circuit for common small values; TokenStream\r
+        // returns them as Bytes.\r
+        if (val instanceof Byte)\r
+            return ((Number)val).intValue();\r
+\r
+        double d = toNumber(val);\r
+        if (d != d || d == 0.0 ||\r
+            d == Double.POSITIVE_INFINITY ||\r
+            d == Double.NEGATIVE_INFINITY)\r
+            return 0;\r
+\r
+        d = Math.IEEEremainder(d, two32);\r
+\r
+        d = d >= 0\r
+            ? d\r
+            : d + two32;\r
+\r
+        if (d >= two31)\r
+            return (int)(d - two32);\r
+        else\r
+            return (int)d;\r
+    }\r
+\r
+    public static int toInt32(Object[] args, int index) {\r
+        return (index < args.length) ? toInt32(args[index]) : 0;\r
+    }\r
+\r
+    public static int toInt32(double d) {\r
+        // 0x100000000 gives me a numeric overflow...\r
+        double two32 = 4294967296.0;\r
+        double two31 = 2147483648.0;\r
+\r
+        if (d != d || d == 0.0 ||\r
+            d == Double.POSITIVE_INFINITY ||\r
+            d == Double.NEGATIVE_INFINITY)\r
+            return 0;\r
+\r
+        d = Math.IEEEremainder(d, two32);\r
+\r
+        d = d >= 0\r
+            ? d\r
+            : d + two32;\r
+\r
+        if (d >= two31)\r
+            return (int)(d - two32);\r
+        else\r
+            return (int)d;\r
+    }\r
+\r
+    /**\r
+     *\r
+     * See ECMA 9.6.\r
+     */\r
+\r
+    // must return long to hold an _unsigned_ int\r
+    public static long toUint32(double d) {\r
+        // 0x100000000 gives me a numeric overflow...\r
+        double two32 = 4294967296.0;\r
+\r
+        if (d != d || d == 0.0 ||\r
+            d == Double.POSITIVE_INFINITY ||\r
+            d == Double.NEGATIVE_INFINITY)\r
+            return 0;\r
+\r
+        if (d > 0.0)\r
+            d = Math.floor(d);\r
+        else\r
+            d = Math.ceil(d);\r
+\r
+    d = Math.IEEEremainder(d, two32);\r
+\r
+    d = d >= 0\r
+            ? d\r
+            : d + two32;\r
+\r
+        return (long) Math.floor(d);\r
+    }\r
+\r
+    public static long toUint32(Object val) {\r
+        return toUint32(toNumber(val));\r
+    }\r
+\r
+    /**\r
+     *\r
+     * See ECMA 9.7.\r
+     */\r
+    public static char toUint16(Object val) {\r
+    long int16 = 0x10000;\r
+\r
+    double d = toNumber(val);\r
+    if (d != d || d == 0.0 ||\r
+        d == Double.POSITIVE_INFINITY ||\r
+        d == Double.NEGATIVE_INFINITY)\r
+    {\r
+        return 0;\r
+    }\r
+\r
+    d = Math.IEEEremainder(d, int16);\r
+\r
+    d = d >= 0\r
+            ? d\r
+        : d + int16;\r
+\r
+    return (char) Math.floor(d);\r
+    }\r
+\r
+    /**\r
+     * Unwrap a JavaScriptException.  Sleight of hand so that we don't\r
+     * javadoc JavaScriptException.getRuntimeValue().\r
+     */\r
+    public static Object unwrapJavaScriptException(JavaScriptException jse) {\r
+        return jse.value;\r
+    }\r
+\r
+    /**\r
+     * Check a WrappedException. Unwrap a JavaScriptException and return\r
+     * the value, otherwise rethrow.\r
+     */\r
+    public static Object unwrapWrappedException(WrappedException we) {\r
+        Throwable t = we.getWrappedException();\r
+        if (t instanceof JavaScriptException)\r
+            return ((JavaScriptException) t).value;\r
+        throw we;\r
+    }\r
+\r
+    public static Object getProp(Object obj, String id, Scriptable scope) {\r
+        Scriptable start;\r
+        if (obj instanceof Scriptable) {\r
+            start = (Scriptable) obj;\r
+        } else {\r
+            start = toObject(scope, obj);\r
+        }\r
+        if (start == null || start == Undefined.instance) {\r
+            String msg = start == null ? "msg.null.to.object"\r
+                                       : "msg.undefined";\r
+            throw NativeGlobal.constructError(\r
+                        Context.getContext(), "ConversionError",\r
+                        ScriptRuntime.getMessage0(msg),\r
+                        scope);\r
+        }\r
+        Scriptable m = start;\r
+        do {\r
+            Object result = m.get(id, start);\r
+            if (result != Scriptable.NOT_FOUND)\r
+                return result;\r
+            m = m.getPrototype();\r
+        } while (m != null);\r
+        return Undefined.instance;\r
+    }\r
+\r
+    public static Object getTopLevelProp(Scriptable scope, String id) {\r
+        Scriptable s = ScriptableObject.getTopLevelScope(scope);\r
+        Object v;\r
+        do {\r
+            v = s.get(id, s);\r
+            if (v != Scriptable.NOT_FOUND)\r
+                return v;\r
+            s = s.getPrototype();\r
+        } while (s != null);\r
+        return v;\r
+    }\r
+\r
+\r
+\r
+/***********************************************************************/\r
+\r
+    public static Scriptable getProto(Object obj, Scriptable scope) {\r
+        Scriptable s;\r
+        if (obj instanceof Scriptable) {\r
+            s = (Scriptable) obj;\r
+        } else {\r
+            s = toObject(scope, obj);\r
+        }\r
+        if (s == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        return s.getPrototype();\r
+    }\r
+\r
+    public static Scriptable getParent(Object obj) {\r
+        Scriptable s;\r
+        try {\r
+            s = (Scriptable) obj;\r
+        }\r
+        catch (ClassCastException e) {\r
+            return null;\r
+        }\r
+        if (s == null) {\r
+            return null;\r
+        }\r
+        return getThis(s.getParentScope());\r
+    }\r
+\r
+   public static Scriptable getParent(Object obj, Scriptable scope) {\r
+        Scriptable s;\r
+        if (obj instanceof Scriptable) {\r
+            s = (Scriptable) obj;\r
+        } else {\r
+            s = toObject(scope, obj);\r
+        }\r
+        if (s == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        return s.getParentScope();\r
+    }\r
+\r
+    public static Object setProto(Object obj, Object value, Scriptable scope) {\r
+        Scriptable start;\r
+        if (obj instanceof Scriptable) {\r
+            start = (Scriptable) obj;\r
+        } else {\r
+            start = toObject(scope, obj);\r
+        }\r
+        Scriptable result = value == null ? null : toObject(scope, value);\r
+        Scriptable s = result;\r
+        while (s != null) {\r
+            if (s == start) {\r
+                throw Context.reportRuntimeError1(\r
+                    "msg.cyclic.value", "__proto__");\r
+            }\r
+            s = s.getPrototype();\r
+        }\r
+        if (start == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        start.setPrototype(result);\r
+        return result;\r
+    }\r
+\r
+    public static Object setParent(Object obj, Object value, Scriptable scope) {\r
+        Scriptable start;\r
+        if (obj instanceof Scriptable) {\r
+            start = (Scriptable) obj;\r
+        } else {\r
+            start = toObject(scope, obj);\r
+        }\r
+        Scriptable result = value == null ? null : toObject(scope, value);\r
+        Scriptable s = result;\r
+        while (s != null) {\r
+            if (s == start) {\r
+                throw Context.reportRuntimeError1(\r
+                    "msg.cyclic.value", "__parent__");\r
+            }\r
+            s = s.getParentScope();\r
+        }\r
+        if (start == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        start.setParentScope(result);\r
+        return result;\r
+    }\r
+\r
+    public static Object setProp(Object obj, String id, Object value,\r
+                                 Scriptable scope)\r
+    {\r
+        Scriptable start;\r
+        if (obj instanceof Scriptable) {\r
+            start = (Scriptable) obj;\r
+        } else {\r
+            start = toObject(scope, obj);\r
+        }\r
+        if (start == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        Scriptable m = start;\r
+        do {\r
+            if (m.has(id, start)) {\r
+                m.put(id, start, value);\r
+                return value;\r
+            }\r
+            m = m.getPrototype();\r
+        } while (m != null);\r
+\r
+        start.put(id, start, value);\r
+        return value;\r
+    }\r
+\r
+    // Return -1L if str is not an index or the index value as lower 32 \r
+    // bits of the result\r
+    private static long indexFromString(String str) {\r
+        // It must be a string.\r
+\r
+        // The length of the decimal string representation of \r
+        //  Integer.MAX_VALUE, 2147483647\r
+        final int MAX_VALUE_LENGTH = 10;\r
+        \r
+        int len = str.length();\r
+        if (len > 0) {\r
+            int i = 0;\r
+            boolean negate = false;\r
+            int c = str.charAt(0);\r
+            if (c == '-') {\r
+                if (len > 1) {\r
+                    c = str.charAt(1); \r
+                    i = 1; \r
+                    negate = true;\r
+                }\r
+            }\r
+            c -= '0';\r
+            if (0 <= c && c <= 9 \r
+                && len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH))\r
+            {\r
+                // Use negative numbers to accumulate index to handle\r
+                // Integer.MIN_VALUE that is greater by 1 in absolute value\r
+                // then Integer.MAX_VALUE\r
+                int index = -c;\r
+                int oldIndex = 0;\r
+                i++;\r
+                if (index != 0) {\r
+                    // Note that 00, 01, 000 etc. are not indexes\r
+                    while (i != len && 0 <= (c = str.charAt(i) - '0') && c <= 9)\r
+                    {\r
+                        oldIndex = index;\r
+                        index = 10 * index - c;\r
+                        i++;\r
+                    }\r
+                }\r
+                // Make sure all characters were consumed and that it couldn't\r
+                // have overflowed.\r
+                if (i == len &&\r
+                    (oldIndex > (Integer.MIN_VALUE / 10) ||\r
+                     (oldIndex == (Integer.MIN_VALUE / 10) &&\r
+                      c <= (negate ? -(Integer.MIN_VALUE % 10) \r
+                                   : (Integer.MAX_VALUE % 10)))))\r
+                {\r
+                    return 0xFFFFFFFFL & (negate ? index : -index);\r
+                }\r
+            }\r
+        }\r
+        return -1L;\r
+    }\r
+\r
+    static String getStringId(Object id) {\r
+        if (id instanceof Number) {\r
+            double d = ((Number) id).doubleValue();\r
+            int index = (int) d;\r
+            if (((double) index) == d)\r
+                return null;\r
+            return toString(id);\r
+        }\r
+        String s = toString(id);\r
+        long indexTest = indexFromString(s);\r
+        if (indexTest >= 0)\r
+            return null;\r
+        return s;\r
+    }\r
+\r
+    static int getIntId(Object id) {\r
+        if (id instanceof Number) {\r
+            double d = ((Number) id).doubleValue();\r
+            int index = (int) d;\r
+            if (((double) index) == d)\r
+                return index;\r
+            return 0;\r
+        }\r
+        String s = toString(id);\r
+        long indexTest = indexFromString(s);\r
+        if (indexTest >= 0)\r
+            return (int)indexTest;\r
+        return 0;\r
+    }\r
+\r
+    public static Object getElem(Object obj, Object id, Scriptable scope) {\r
+        int index;\r
+        String s;\r
+        if (id instanceof Number) {\r
+            double d = ((Number) id).doubleValue();\r
+            index = (int) d;\r
+            s = ((double) index) == d ? null : toString(id);\r
+        } else {\r
+            s = toString(id);\r
+            long indexTest = indexFromString(s);\r
+            if (indexTest >= 0) {\r
+                index = (int)indexTest;\r
+                s = null;\r
+            } else {\r
+                index = 0;\r
+            }                \r
+        }\r
+        Scriptable start = obj instanceof Scriptable\r
+                           ? (Scriptable) obj\r
+                           : toObject(scope, obj);\r
+        Scriptable m = start;\r
+        if (s != null) {\r
+            if (s.equals("__proto__"))\r
+                return start.getPrototype();\r
+            if (s.equals("__parent__"))\r
+                return start.getParentScope();\r
+            while (m != null) {\r
+                Object result = m.get(s, start);\r
+                if (result != Scriptable.NOT_FOUND)\r
+                    return result;\r
+                m = m.getPrototype();\r
+            }\r
+            return Undefined.instance;\r
+        }\r
+        while (m != null) {\r
+            Object result = m.get(index, start);\r
+            if (result != Scriptable.NOT_FOUND)\r
+                return result;\r
+            m = m.getPrototype();\r
+        }\r
+        return Undefined.instance;\r
+    }\r
+\r
+\r
+    /*\r
+     * A cheaper and less general version of the above for well-known argument\r
+     * types.\r
+     */\r
+    public static Object getElem(Scriptable obj, int index)\r
+    {\r
+        Scriptable m = obj;\r
+        while (m != null) {\r
+            Object result = m.get(index, obj);\r
+            if (result != Scriptable.NOT_FOUND)\r
+                return result;\r
+            m = m.getPrototype();\r
+        }\r
+        return Undefined.instance;\r
+    }\r
+\r
+    public static Object setElem(Object obj, Object id, Object value,\r
+                                 Scriptable scope)\r
+    {\r
+        int index;\r
+        String s;\r
+        if (id instanceof Number) {\r
+            double d = ((Number) id).doubleValue();\r
+            index = (int) d;\r
+            s = ((double) index) == d ? null : toString(id);\r
+        } else {\r
+            s = toString(id);\r
+            long indexTest = indexFromString(s);\r
+            if (indexTest >= 0) {\r
+                index = (int)indexTest;\r
+                s = null;\r
+            } else {\r
+                index = 0;\r
+            }\r
+        }\r
+\r
+        Scriptable start = obj instanceof Scriptable\r
+                     ? (Scriptable) obj\r
+                     : toObject(scope, obj);\r
+        Scriptable m = start;\r
+        if (s != null) {\r
+            if (s.equals("__proto__"))\r
+                return setProto(obj, value, scope);\r
+            if (s.equals("__parent__"))\r
+                return setParent(obj, value, scope);\r
+\r
+            do {\r
+                if (m.has(s, start)) {\r
+                    m.put(s, start, value);\r
+                    return value;\r
+                }\r
+                m = m.getPrototype();\r
+            } while (m != null);\r
+            start.put(s, start, value);\r
+            return value;\r
+       }\r
+\r
+        do {\r
+            if (m.has(index, start)) {\r
+                m.put(index, start, value);\r
+                return value;\r
+            }\r
+            m = m.getPrototype();\r
+        } while (m != null);\r
+        start.put(index, start, value);\r
+        return value;\r
+    }\r
+\r
+    /*\r
+     * A cheaper and less general version of the above for well-known argument\r
+     * types.\r
+     */\r
+    public static Object setElem(Scriptable obj, int index, Object value)\r
+    {\r
+        Scriptable m = obj;\r
+        do {\r
+            if (m.has(index, obj)) {\r
+                m.put(index, obj, value);\r
+                return value;\r
+            }\r
+            m = m.getPrototype();\r
+        } while (m != null);\r
+        obj.put(index, obj, value);\r
+        return value;\r
+    }\r
+\r
+    /**\r
+     * The delete operator\r
+     *\r
+     * See ECMA 11.4.1\r
+     *\r
+     * In ECMA 0.19, the description of the delete operator (11.4.1)\r
+     * assumes that the [[Delete]] method returns a value. However,\r
+     * the definition of the [[Delete]] operator (8.6.2.5) does not\r
+     * define a return value. Here we assume that the [[Delete]]\r
+     * method doesn't return a value.\r
+     */\r
+    public static Object delete(Object obj, Object id) {\r
+        String s = getStringId(id);\r
+        boolean result = s != null\r
+            ? ScriptableObject.deleteProperty((Scriptable) obj, s)\r
+            : ScriptableObject.deleteProperty((Scriptable) obj, getIntId(id));\r
+        return result ? Boolean.TRUE : Boolean.FALSE;\r
+    }\r
+\r
+    /**\r
+     * Looks up a name in the scope chain and returns its value.\r
+     */\r
+    public static Object name(Scriptable scopeChain, String id) {\r
+        Scriptable obj = scopeChain;\r
+        Object prop;\r
+        while (obj != null) {\r
+            Scriptable m = obj;\r
+            do {\r
+                Object result = m.get(id, obj);\r
+                if (result != Scriptable.NOT_FOUND)\r
+                    return result;\r
+                m = m.getPrototype();\r
+            } while (m != null);\r
+            obj = obj.getParentScope();\r
+        }\r
+        throw NativeGlobal.constructError\r
+            (Context.getContext(), "ReferenceError",\r
+             ScriptRuntime.getMessage1("msg.is.not.defined", id.toString()),\r
+                        scopeChain);\r
+    }\r
+\r
+    /**\r
+     * Returns the object in the scope chain that has a given property.\r
+     *\r
+     * The order of evaluation of an assignment expression involves\r
+     * evaluating the lhs to a reference, evaluating the rhs, and then\r
+     * modifying the reference with the rhs value. This method is used\r
+     * to 'bind' the given name to an object containing that property\r
+     * so that the side effects of evaluating the rhs do not affect\r
+     * which property is modified.\r
+     * Typically used in conjunction with setName.\r
+     *\r
+     * See ECMA 10.1.4\r
+     */\r
+    public static Scriptable bind(Scriptable scope, String id) {\r
+        Scriptable obj = scope;\r
+        Object prop;\r
+        while (obj != null) {\r
+            Scriptable m = obj;\r
+            do {\r
+                if (m.has(id, obj))\r
+                    return obj;\r
+                m = m.getPrototype();\r
+            } while (m != null);\r
+            obj = obj.getParentScope();\r
+        }\r
+        return null;\r
+    }\r
+\r
+    public static Scriptable getBase(Scriptable scope, String id) {\r
+        Scriptable obj = scope;\r
+        Object prop;\r
+        while (obj != null) {\r
+            Scriptable m = obj;\r
+            do {\r
+                if (m.get(id, obj) != Scriptable.NOT_FOUND)\r
+                    return obj;\r
+                m = m.getPrototype();\r
+            } while (m != null);\r
+            obj = obj.getParentScope();\r
+        }\r
+        throw NativeGlobal.constructError(\r
+                    Context.getContext(), "ReferenceError",\r
+                    ScriptRuntime.getMessage1("msg.is.not.defined", id),\r
+                    scope);\r
+    }\r
+\r
+    public static Scriptable getThis(Scriptable base) {\r
+        while (base instanceof NativeWith)\r
+            base = base.getPrototype();\r
+        if (base instanceof NativeCall)\r
+            base = ScriptableObject.getTopLevelScope(base);\r
+        return base;\r
+    }\r
+\r
+    public static Object setName(Scriptable bound, Object value,\r
+                                 Scriptable scope, String id)\r
+    {\r
+        if (bound == null) {\r
+            // "newname = 7;", where 'newname' has not yet\r
+            // been defined, creates a new property in the\r
+            // global object. Find the global object by\r
+            // walking up the scope chain.\r
+            Scriptable next = scope;\r
+            do {\r
+                bound = next;\r
+                next = bound.getParentScope();\r
+            } while (next != null);\r
+\r
+            bound.put(id, bound, value);\r
+            /*\r
+            This code is causing immense performance problems in\r
+            scripts that assign to the variables as a way of creating them.\r
+            XXX need strict mode\r
+            String message = getMessage1("msg.assn.create", id);\r
+            Context.reportWarning(message);\r
+            */\r
+            return value;\r
+        }\r
+        return setProp(bound, id, value, scope);\r
+    }\r
+\r
+    public static Enumeration initEnum(Object value, Scriptable scope) {\r
+        Scriptable m = toObject(scope, value);\r
+        return new IdEnumeration(m);\r
+    }\r
+\r
+    public static Object nextEnum(Enumeration enum) {\r
+        // OPT this could be more efficient; should junk the Enumeration\r
+        // interface\r
+        if (!enum.hasMoreElements())\r
+            return null;\r
+        return enum.nextElement();\r
+    }\r
+\r
+    // Form used by class files generated by 1.4R3 and earlier.\r
+    public static Object call(Context cx, Object fun, Object thisArg,\r
+                              Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        Scriptable scope = null;\r
+        if (fun instanceof Scriptable) \r
+            scope = ((Scriptable) fun).getParentScope();\r
+        return call(cx, fun, thisArg, args, scope);\r
+    }\r
+    \r
+    public static Object call(Context cx, Object fun, Object thisArg,\r
+                              Object[] args, Scriptable scope)\r
+        throws JavaScriptException\r
+    {\r
+        Function function;\r
+        try {\r
+            function = (Function) fun;\r
+        }\r
+        catch (ClassCastException e) {\r
+            throw NativeGlobal.typeError1\r
+                ("msg.isnt.function", toString(fun), scope);\r
+        }\r
+\r
+        Scriptable thisObj;\r
+        if (thisArg instanceof Scriptable || thisArg == null) {\r
+            thisObj = (Scriptable) thisArg;\r
+        } else {\r
+            thisObj = ScriptRuntime.toObject(scope, thisArg);\r
+        }\r
+        if (function == null) throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        return function.call(cx, scope, thisObj, args);\r
+    }\r
+\r
+    private static Object callOrNewSpecial(Context cx, Scriptable scope,\r
+                                           Object fun, Object jsThis, \r
+                                           Object thisArg,\r
+                                           Object[] args, boolean isCall,\r
+                                           String filename, int lineNumber)\r
+        throws JavaScriptException\r
+    {\r
+        if (fun instanceof IdFunction) {\r
+            IdFunction f = (IdFunction)fun;\r
+            String name = f.getFunctionName();\r
+            if (name.length() == 4) {\r
+                if (name.equals("eval")) {\r
+                    if (f.master.getClass() == NativeGlobal.class) {\r
+                        return NativeGlobal.evalSpecial(cx, scope, \r
+                                                        thisArg, args,\r
+                                                        filename, lineNumber);\r
+                    }\r
+                }\r
+                else if (name.equals("With")) {\r
+                    if (f.master.getClass() == NativeWith.class) {\r
+                        return NativeWith.newWithSpecial(cx, args, f, !isCall);\r
+                    }\r
+                }\r
+                else if (name.equals("exec")) {\r
+                    if (f.master.getClass() == NativeScript.class) {\r
+                        return ((NativeScript)jsThis).\r
+                            exec(cx, ScriptableObject.getTopLevelScope(scope));\r
+                    }\r
+                    else {\r
+                        RegExpProxy proxy = cx.getRegExpProxy();\r
+                        if (proxy != null && proxy.isRegExp(jsThis)) {\r
+                            return call(cx, fun, jsThis, args, scope);\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        else    // could've been <java>.XXX.exec() that was re-directed here\r
+            if (fun instanceof NativeJavaMethod)\r
+                return call(cx, fun, jsThis, args, scope);\r
+\r
+        if (isCall)\r
+            return call(cx, fun, thisArg, args, scope);\r
+        return newObject(cx, fun, args, scope);\r
+    }\r
+\r
+    public static Object callSpecial(Context cx, Object fun,\r
+                                     Object thisArg, Object[] args,\r
+                                     Scriptable enclosingThisArg,\r
+                                     Scriptable scope, String filename,\r
+                                     int lineNumber)\r
+        throws JavaScriptException\r
+    {\r
+        return callOrNewSpecial(cx, scope, fun, thisArg,\r
+                                enclosingThisArg, args, true,\r
+                                filename, lineNumber);\r
+    }\r
+\r
+    /**\r
+     * Operator new.\r
+     *\r
+     * See ECMA 11.2.2\r
+     */\r
+    public static Scriptable newObject(Context cx, Object fun,\r
+                                       Object[] args, Scriptable scope)\r
+        throws JavaScriptException\r
+    {\r
+        Function f;\r
+        try {\r
+            f = (Function) fun;\r
+            if (f != null) {\r
+                return f.construct(cx, scope, args);\r
+           }\r
+            // else fall through to error\r
+        } catch (ClassCastException e) {\r
+            // fall through to error\r
+        }\r
+        throw NativeGlobal.typeError1\r
+            ("msg.isnt.function", toString(fun), scope);\r
+    }\r
+\r
+    public static Scriptable newObjectSpecial(Context cx, Object fun,\r
+                                              Object[] args, Scriptable scope)\r
+        throws JavaScriptException\r
+    {\r
+        return (Scriptable) callOrNewSpecial(cx, scope, fun, null, null, args,\r
+                                             false, null, -1);\r
+    }\r
+\r
+    /**\r
+     * The typeof operator\r
+     */\r
+    public static String typeof(Object value) {\r
+        if (value == Undefined.instance)\r
+            return "undefined";\r
+        if (value == null)\r
+            return "object";\r
+        if (value instanceof Scriptable)\r
+            return (value instanceof Function) ? "function" : "object";\r
+        if (value instanceof String)\r
+            return "string";\r
+        if (value instanceof Number)\r
+            return "number";\r
+        if (value instanceof Boolean)\r
+            return "boolean";\r
+        throw errorWithClassName("msg.invalid.type", value);\r
+    }\r
+\r
+    /**\r
+     * The typeof operator that correctly handles the undefined case\r
+     */\r
+    public static String typeofName(Scriptable scope, String id) {\r
+        Object val = bind(scope, id);\r
+        if (val == null)\r
+            return "undefined";\r
+        return typeof(getProp(val, id, scope));\r
+    }\r
+\r
+    // neg:\r
+    // implement the '-' operator inline in the caller\r
+    // as "-toNumber(val)"\r
+\r
+    // not:\r
+    // implement the '!' operator inline in the caller\r
+    // as "!toBoolean(val)"\r
+\r
+    // bitnot:\r
+    // implement the '~' operator inline in the caller\r
+    // as "~toInt32(val)"\r
+\r
+    public static Object add(Object val1, Object val2) {\r
+        if (val1 instanceof Scriptable)\r
+            val1 = ((Scriptable) val1).getDefaultValue(null);\r
+        if (val2 instanceof Scriptable)\r
+            val2 = ((Scriptable) val2).getDefaultValue(null);\r
+        if (!(val1 instanceof String) && !(val2 instanceof String))\r
+            if ((val1 instanceof Number) && (val2 instanceof Number))\r
+                return new Double(((Number)val1).doubleValue() +\r
+                                  ((Number)val2).doubleValue());\r
+            else\r
+                return new Double(toNumber(val1) + toNumber(val2));\r
+        return toString(val1) + toString(val2);\r
+    }\r
+\r
+    public static Object postIncrement(Object value) {\r
+        if (value instanceof Number)\r
+            value = new Double(((Number)value).doubleValue() + 1.0);\r
+        else\r
+            value = new Double(toNumber(value) + 1.0);\r
+        return value;\r
+    }\r
+\r
+    public static Object postIncrement(Scriptable scopeChain, String id) {\r
+        Scriptable obj = scopeChain;\r
+        Object prop;\r
+        while (obj != null) {\r
+            Scriptable m = obj;\r
+            do {\r
+                Object result = m.get(id, obj);\r
+                if (result != Scriptable.NOT_FOUND) {\r
+                    Object newValue = result;\r
+                    if (newValue instanceof Number) {\r
+                        newValue = new Double(\r
+                                    ((Number)newValue).doubleValue() + 1.0);\r
+                        m.put(id, obj, newValue);\r
+                        return result;\r
+                    }\r
+                    else {\r
+                        newValue = new Double(toNumber(newValue) + 1.0);\r
+                        m.put(id, obj, newValue);\r
+                        return new Double(toNumber(result));\r
+                    }\r
+                }\r
+                m = m.getPrototype();\r
+            } while (m != null);\r
+            obj = obj.getParentScope();\r
+        }\r
+        throw NativeGlobal.constructError\r
+            (Context.getContext(), "ReferenceError",\r
+             ScriptRuntime.getMessage1("msg.is.not.defined", id),\r
+                    scopeChain);\r
+    }\r
+\r
+    public static Object postIncrement(Object obj, String id, Scriptable scope) {\r
+        Scriptable start;\r
+        if (obj instanceof Scriptable) {\r
+            start = (Scriptable) obj;\r
+        } else {\r
+            start = toObject(scope, obj);\r
+        }\r
+        if (start == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        Scriptable m = start;\r
+        do {\r
+            Object result = m.get(id, start);\r
+            if (result != Scriptable.NOT_FOUND) {\r
+                Object newValue = result;\r
+                if (newValue instanceof Number) {\r
+                    newValue = new Double(\r
+                                ((Number)newValue).doubleValue() + 1.0);\r
+                    m.put(id, start, newValue);\r
+                    return result;\r
+                }\r
+                else {\r
+                    newValue = new Double(toNumber(newValue) + 1.0);\r
+                    m.put(id, start, newValue);\r
+                    return new Double(toNumber(result));\r
+                }\r
+            }\r
+            m = m.getPrototype();\r
+        } while (m != null);\r
+        return Undefined.instance;\r
+    }\r
+\r
+    public static Object postIncrementElem(Object obj,\r
+                                            Object index, Scriptable scope) {\r
+        Object oldValue = getElem(obj, index, scope);\r
+        if (oldValue == Undefined.instance)\r
+            return Undefined.instance;\r
+        double resultValue = toNumber(oldValue);\r
+        Double newValue = new Double(resultValue + 1.0);\r
+        setElem(obj, index, newValue, scope);\r
+        return new Double(resultValue);\r
+    }\r
+\r
+    public static Object postDecrementElem(Object obj,\r
+                                            Object index, Scriptable scope) {\r
+        Object oldValue = getElem(obj, index, scope);\r
+        if (oldValue == Undefined.instance)\r
+            return Undefined.instance;\r
+        double resultValue = toNumber(oldValue);\r
+        Double newValue = new Double(resultValue - 1.0);\r
+        setElem(obj, index, newValue, scope);\r
+        return new Double(resultValue);\r
+    }\r
+\r
+    public static Object postDecrement(Object value) {\r
+        if (value instanceof Number)\r
+            value = new Double(((Number)value).doubleValue() - 1.0);\r
+        else\r
+            value = new Double(toNumber(value) - 1.0);\r
+        return value;\r
+    }\r
+\r
+    public static Object postDecrement(Scriptable scopeChain, String id) {\r
+        Scriptable obj = scopeChain;\r
+        Object prop;\r
+        while (obj != null) {\r
+            Scriptable m = obj;\r
+            do {\r
+                Object result = m.get(id, obj);\r
+                if (result != Scriptable.NOT_FOUND) {\r
+                    Object newValue = result;\r
+                    if (newValue instanceof Number) {\r
+                        newValue = new Double(\r
+                                    ((Number)newValue).doubleValue() - 1.0);\r
+                        m.put(id, obj, newValue);\r
+                        return result;\r
+                    }\r
+                    else {\r
+                        newValue = new Double(toNumber(newValue) - 1.0);\r
+                        m.put(id, obj, newValue);\r
+                        return new Double(toNumber(result));\r
+                    }\r
+                }\r
+                m = m.getPrototype();\r
+            } while (m != null);\r
+            obj = obj.getParentScope();\r
+        }\r
+        throw NativeGlobal.constructError\r
+            (Context.getContext(), "ReferenceError",\r
+             ScriptRuntime.getMessage1("msg.is.not.defined", id),\r
+                    scopeChain);\r
+    }\r
+\r
+    public static Object postDecrement(Object obj, String id, Scriptable scope) {\r
+        Scriptable start;\r
+        if (obj instanceof Scriptable) {\r
+            start = (Scriptable) obj;\r
+        } else {\r
+            start = toObject(scope, obj);\r
+        }\r
+        if (start == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        Scriptable m = start;\r
+        do {\r
+            Object result = m.get(id, start);\r
+            if (result != Scriptable.NOT_FOUND) {\r
+                Object newValue = result;\r
+                if (newValue instanceof Number) {\r
+                    newValue = new Double(\r
+                                ((Number)newValue).doubleValue() - 1.0);\r
+                    m.put(id, start, newValue);\r
+                    return result;\r
+                }\r
+                else {\r
+                    newValue = new Double(toNumber(newValue) - 1.0);\r
+                    m.put(id, start, newValue);\r
+                    return new Double(toNumber(result));\r
+                }\r
+            }\r
+            m = m.getPrototype();\r
+        } while (m != null);\r
+        return Undefined.instance;\r
+    }\r
+\r
+    public static Object toPrimitive(Object val) {\r
+        if (val == null || !(val instanceof Scriptable)) {\r
+            return val;\r
+        }\r
+        Object result = ((Scriptable) val).getDefaultValue(null);\r
+        if (result != null && result instanceof Scriptable)\r
+            throw NativeGlobal.typeError0("msg.bad.default.value", val);\r
+        return result;\r
+    }\r
+\r
+    private static Class getTypeOfValue(Object obj) {\r
+        if (obj == null)\r
+            return ScriptableClass;\r
+        if (obj == Undefined.instance)\r
+            return UndefinedClass;\r
+        if (obj instanceof Scriptable)\r
+            return ScriptableClass;\r
+        if (obj instanceof Number)\r
+            return NumberClass;\r
+        return obj.getClass();\r
+    }\r
+\r
+    /**\r
+     * Equality\r
+     *\r
+     * See ECMA 11.9\r
+     */\r
+    public static boolean eq(Object x, Object y) {\r
+        Object xCopy = x;                                       // !!! JIT bug in Cafe 2.1\r
+        Object yCopy = y;                                       // need local copies, otherwise their values get blown below\r
+        for (;;) {\r
+            Class typeX = getTypeOfValue(x);\r
+            Class typeY = getTypeOfValue(y);\r
+            if (typeX == typeY) {\r
+                if (typeX == UndefinedClass)\r
+                    return true;\r
+                if (typeX == NumberClass)\r
+                    return ((Number) x).doubleValue() ==\r
+                           ((Number) y).doubleValue();\r
+                if (typeX == StringClass || typeX == BooleanClass)\r
+                    return xCopy.equals(yCopy);                                 // !!! JIT bug in Cafe 2.1\r
+                if (typeX == ScriptableClass) {\r
+                    if (x == y)\r
+                        return true;\r
+                    if (x instanceof Wrapper &&\r
+                        y instanceof Wrapper)\r
+                    {\r
+                        return ((Wrapper) x).unwrap() ==\r
+                               ((Wrapper) y).unwrap();\r
+                    }\r
+                    return false;\r
+                }\r
+                throw new RuntimeException(); // shouldn't get here\r
+            }\r
+            if (x == null && y == Undefined.instance)\r
+                return true;\r
+            if (x == Undefined.instance && y == null)\r
+                return true;\r
+            if (typeX == NumberClass &&\r
+                typeY == StringClass)\r
+            {\r
+                return ((Number) x).doubleValue() == toNumber(y);\r
+            }\r
+            if (typeX == StringClass &&\r
+                typeY == NumberClass)\r
+            {\r
+                return toNumber(x) == ((Number) y).doubleValue();\r
+            }\r
+            if (typeX == BooleanClass) {\r
+                x = new Double(toNumber(x));\r
+                xCopy = x;                                 // !!! JIT bug in Cafe 2.1\r
+                continue;\r
+            }\r
+            if (typeY == BooleanClass) {\r
+                y = new Double(toNumber(y));\r
+                yCopy = y;                                 // !!! JIT bug in Cafe 2.1\r
+                continue;\r
+            }\r
+            if ((typeX == StringClass ||\r
+                 typeX == NumberClass) &&\r
+                typeY == ScriptableClass && y != null)\r
+            {\r
+                y = toPrimitive(y);\r
+                yCopy = y;                                 // !!! JIT bug in Cafe 2.1\r
+                continue;\r
+            }\r
+            if (typeX == ScriptableClass && x != null &&\r
+                (typeY == StringClass ||\r
+                 typeY == NumberClass))\r
+            {\r
+                x = toPrimitive(x);\r
+                xCopy = x;                                 // !!! JIT bug in Cafe 2.1\r
+                continue;\r
+            }\r
+            return false;\r
+        }\r
+    }\r
+\r
+    public static Boolean eqB(Object x, Object y) {\r
+        if (eq(x,y))\r
+            return Boolean.TRUE;\r
+        else\r
+            return Boolean.FALSE;\r
+    }\r
+\r
+    public static Boolean neB(Object x, Object y) {\r
+        if (eq(x,y))\r
+            return Boolean.FALSE;\r
+        else\r
+            return Boolean.TRUE;\r
+    }\r
+\r
+    public static boolean shallowEq(Object x, Object y) {\r
+        Class type = getTypeOfValue(x);\r
+        if (type != getTypeOfValue(y))\r
+            return false;\r
+        if (type == StringClass || type == BooleanClass)\r
+            return x.equals(y);\r
+        if (type == NumberClass)\r
+            return ((Number) x).doubleValue() ==\r
+                   ((Number) y).doubleValue();\r
+        if (type == ScriptableClass) {\r
+            if (x == y)\r
+                return true;\r
+            if (x instanceof Wrapper && y instanceof Wrapper)\r
+                return ((Wrapper) x).unwrap() ==\r
+                       ((Wrapper) y).unwrap();\r
+            return false;\r
+        }\r
+        if (type == UndefinedClass)\r
+            return true;\r
+        return false;\r
+    }\r
+\r
+    public static Boolean seqB(Object x, Object y) {\r
+        if (shallowEq(x,y))\r
+            return Boolean.TRUE;\r
+        else\r
+            return Boolean.FALSE;\r
+    }\r
+\r
+    public static Boolean sneB(Object x, Object y) {\r
+        if (shallowEq(x,y))\r
+            return Boolean.FALSE;\r
+        else\r
+            return Boolean.TRUE;\r
+    }\r
+\r
+    /**\r
+     * The instanceof operator.\r
+     *\r
+     * @return a instanceof b\r
+     */\r
+    public static boolean instanceOf(Scriptable scope, Object a, Object b) {\r
+        // Check RHS is an object\r
+        if (! (b instanceof Scriptable)) {\r
+            throw NativeGlobal.typeError0("msg.instanceof.not.object", scope);\r
+        }\r
+\r
+        // for primitive values on LHS, return false\r
+        // XXX we may want to change this so that\r
+        // 5 instanceof Number == true\r
+        if (! (a instanceof Scriptable))\r
+            return false;\r
+\r
+        return ((Scriptable)b).hasInstance((Scriptable)a);\r
+    }\r
+\r
+    /**\r
+     * Delegates to\r
+     *\r
+     * @return true iff rhs appears in lhs' proto chain\r
+     */\r
+    protected static boolean jsDelegatesTo(Scriptable lhs, Scriptable rhs) {\r
+        Scriptable proto = lhs.getPrototype();\r
+\r
+        while (proto != null) {\r
+            if (proto.equals(rhs)) return true;\r
+            proto = proto.getPrototype();\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * The in operator.\r
+     *\r
+     * This is a new JS 1.3 language feature.  The in operator mirrors\r
+     * the operation of the for .. in construct, and tests whether the\r
+     * rhs has the property given by the lhs.  It is different from the \r
+     * for .. in construct in that:\r
+     * <BR> - it doesn't perform ToObject on the right hand side\r
+     * <BR> - it returns true for DontEnum properties.\r
+     * @param a the left hand operand\r
+     * @param b the right hand operand\r
+     *\r
+     * @return true if property name or element number a is a property of b\r
+     */\r
+    public static boolean in(Object a, Object b, Scriptable scope) {\r
+        if (!(b instanceof Scriptable)) {\r
+            throw NativeGlobal.typeError0("msg.instanceof.not.object", scope);\r
+        }\r
+        String s = getStringId(a);\r
+        return s != null\r
+            ? ScriptableObject.hasProperty((Scriptable) b, s)\r
+            : ScriptableObject.hasProperty((Scriptable) b, getIntId(a));\r
+    }\r
+\r
+    public static Boolean cmp_LTB(Object val1, Object val2) {\r
+        if (cmp_LT(val1, val2) == 1)\r
+            return Boolean.TRUE;\r
+        else\r
+            return Boolean.FALSE;\r
+    }\r
+\r
+    public static int cmp_LT(Object val1, Object val2) {\r
+        if (val1 instanceof Scriptable)\r
+            val1 = ((Scriptable) val1).getDefaultValue(NumberClass);\r
+        if (val2 instanceof Scriptable)\r
+            val2 = ((Scriptable) val2).getDefaultValue(NumberClass);\r
+        if (!(val1 instanceof String) || !(val2 instanceof String)) {\r
+            double d1 = toNumber(val1);\r
+            if (d1 != d1)\r
+                return 0;\r
+            double d2 = toNumber(val2);\r
+            if (d2 != d2)\r
+                return 0;\r
+            return d1 < d2 ? 1 : 0;\r
+        }\r
+        return toString(val1).compareTo(toString(val2)) < 0 ? 1 : 0;\r
+    }\r
+\r
+    public static Boolean cmp_LEB(Object val1, Object val2) {\r
+        if (cmp_LE(val1, val2) == 1)\r
+            return Boolean.TRUE;\r
+        else\r
+            return Boolean.FALSE;\r
+    }\r
+\r
+    public static int cmp_LE(Object val1, Object val2) {\r
+        if (val1 instanceof Scriptable)\r
+            val1 = ((Scriptable) val1).getDefaultValue(NumberClass);\r
+        if (val2 instanceof Scriptable)\r
+            val2 = ((Scriptable) val2).getDefaultValue(NumberClass);\r
+        if (!(val1 instanceof String) || !(val2 instanceof String)) {\r
+            double d1 = toNumber(val1);\r
+            if (d1 != d1)\r
+                return 0;\r
+            double d2 = toNumber(val2);\r
+            if (d2 != d2)\r
+                return 0;\r
+            return d1 <= d2 ? 1 : 0;\r
+        }\r
+        return toString(val1).compareTo(toString(val2)) <= 0 ? 1 : 0;\r
+    }\r
+\r
+    // lt:\r
+    // implement the '<' operator inline in the caller\r
+    // as "compare(val1, val2) == 1"\r
+\r
+    // le:\r
+    // implement the '<=' operator inline in the caller\r
+    // as "compare(val2, val1) == 0"\r
+\r
+    // gt:\r
+    // implement the '>' operator inline in the caller\r
+    // as "compare(val2, val1) == 1"\r
+\r
+    // ge:\r
+    // implement the '>=' operator inline in the caller\r
+    // as "compare(val1, val2) == 0"\r
+\r
+    // ------------------\r
+    // Statements\r
+    // ------------------\r
+\r
+    private static final String GLOBAL_CLASS = \r
+        "org.mozilla.javascript.tools.shell.Global";\r
+\r
+    private static ScriptableObject getGlobal(Context cx) {\r
+        try {\r
+            Class globalClass = loadClassName(GLOBAL_CLASS);\r
+            Class[] parm = { Context.class };\r
+            Constructor globalClassCtor = globalClass.getConstructor(parm);\r
+            Object[] arg = { cx };\r
+            return (ScriptableObject) globalClassCtor.newInstance(arg);\r
+        } catch (ClassNotFoundException e) {\r
+            // fall through...\r
+        } catch (NoSuchMethodException e) {\r
+            // fall through...\r
+        } catch (InvocationTargetException e) {\r
+            // fall through...\r
+        } catch (IllegalAccessException e) {\r
+            // fall through...\r
+        } catch (InstantiationException e) {\r
+            // fall through...\r
+        }\r
+        return new ImporterTopLevel(cx);\r
+    }\r
+\r
+    public static void main(String scriptClassName, String[] args)\r
+        throws JavaScriptException\r
+    {\r
+        Context cx = Context.enter();\r
+        ScriptableObject global = getGlobal(cx);\r
+\r
+        // get the command line arguments and define "arguments" \r
+        // array in the top-level object\r
+        Scriptable argsObj = cx.newArray(global, args);\r
+        global.defineProperty("arguments", argsObj,\r
+                              ScriptableObject.DONTENUM);\r
+        \r
+        try {\r
+            Class cl = loadClassName(scriptClassName);\r
+            Script script = (Script) cl.newInstance();\r
+            script.exec(cx, global);\r
+            return;\r
+        }\r
+        catch (ClassNotFoundException e) {\r
+        }\r
+        catch (InstantiationException e) {\r
+        }\r
+        catch (IllegalAccessException e) {\r
+        }\r
+        finally {\r
+            Context.exit();\r
+        }\r
+        throw new RuntimeException("Error creating script object");\r
+    }\r
+\r
+    public static Scriptable initScript(Context cx, Scriptable scope,\r
+                                        NativeFunction funObj,\r
+                                        Scriptable thisObj,\r
+                                        boolean fromEvalCode)\r
+    {\r
+        String[] argNames = funObj.argNames;\r
+        if (argNames != null) {\r
+            ScriptableObject so;\r
+            try {\r
+                /* Global var definitions are supposed to be DONTDELETE\r
+                 * so we try to create them that way by hoping that the\r
+                 * scope is a ScriptableObject which provides access to\r
+                 * setting the attributes.\r
+                 */\r
+                so = (ScriptableObject) scope;\r
+            } catch (ClassCastException x) {\r
+                // oh well, we tried.\r
+                so = null;\r
+            }\r
+\r
+            Scriptable varScope = scope;\r
+            if (fromEvalCode) {\r
+                // When executing an eval() inside a with statement,\r
+                // define any variables resulting from var statements\r
+                // in the first non-with scope. See bug 38590.\r
+                varScope = scope;\r
+                while (varScope instanceof NativeWith)\r
+                    varScope = varScope.getParentScope();\r
+            }\r
+            for (int i = argNames.length; i-- != 0;) {\r
+                String name = argNames[i];\r
+                // Don't overwrite existing def if already defined in object\r
+                // or prototypes of object.\r
+                if (!hasProp(scope, name)) {\r
+                    if (so != null && !fromEvalCode)\r
+                        so.defineProperty(name, Undefined.instance,\r
+                                          ScriptableObject.PERMANENT);\r
+                    else \r
+                        varScope.put(name, varScope, Undefined.instance);\r
+                }\r
+            }\r
+        }\r
+\r
+        return scope;\r
+    }\r
+\r
+    public static Scriptable runScript(Script script) {\r
+        Context cx = Context.enter();\r
+        ScriptableObject global = getGlobal(cx);\r
+        try {\r
+            script.exec(cx, global);\r
+        } catch (JavaScriptException e) {\r
+            throw new Error(e.toString());\r
+        } finally {\r
+            Context.exit();\r
+        }\r
+        return global;\r
+    }\r
+\r
+    public static Scriptable initVarObj(Context cx, Scriptable scope,\r
+                                        NativeFunction funObj,\r
+                                        Scriptable thisObj, Object[] args)\r
+    {\r
+        NativeCall result = new NativeCall(cx, scope, funObj, thisObj, args);\r
+        String[] argNames = funObj.argNames;\r
+        if (argNames != null) {\r
+            for (int i = funObj.argCount; i != argNames.length; i++) {\r
+                String name = argNames[i];\r
+                result.put(name, result, Undefined.instance);\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+\r
+    public static void popActivation(Context cx) {\r
+        NativeCall current = cx.currentActivation;\r
+        if (current != null) {\r
+            cx.currentActivation = current.caller;\r
+            current.caller = null;\r
+        }\r
+    }\r
+\r
+    public static Scriptable newScope() {\r
+        return new NativeObject();\r
+    }\r
+\r
+    public static Scriptable enterWith(Object value, Scriptable scope) {\r
+        return new NativeWith(scope, toObject(scope, value));\r
+    }\r
+\r
+    public static Scriptable leaveWith(Scriptable scope) {\r
+        return scope.getParentScope();\r
+    }\r
+\r
+    public static NativeFunction initFunction(NativeFunction fn,\r
+                                              Scriptable scope,\r
+                                              String fnName,\r
+                                              Context cx,\r
+                                              boolean doSetName)\r
+    {\r
+        fn.setPrototype(ScriptableObject.getClassPrototype(scope, "Function"));\r
+        fn.setParentScope(scope);\r
+        if (doSetName)\r
+            setName(scope, fn, scope, fnName);\r
+        return fn;\r
+    }\r
+\r
+    public static NativeFunction createFunctionObject(Scriptable scope,\r
+                                                      Class functionClass,\r
+                                                      Context cx,\r
+                                                      boolean setName)\r
+    {\r
+        Constructor[] ctors = functionClass.getConstructors();\r
+\r
+        NativeFunction result = null;\r
+        Object[] initArgs = { scope, cx };\r
+        try {\r
+            result = (NativeFunction) ctors[0].newInstance(initArgs);\r
+        }\r
+        catch (InstantiationException e) {\r
+            throw WrappedException.wrapException(e);\r
+        }\r
+        catch (IllegalAccessException e) {\r
+            throw WrappedException.wrapException(e);\r
+        }\r
+        catch (IllegalArgumentException e) {\r
+            throw WrappedException.wrapException(e);\r
+        }\r
+        catch (InvocationTargetException e) {\r
+            throw WrappedException.wrapException(e);\r
+        }\r
+\r
+        result.setPrototype(ScriptableObject.getClassPrototype(scope, "Function"));\r
+        result.setParentScope(scope);\r
+\r
+        String fnName = result.getFunctionName();\r
+        if (setName && fnName != null && fnName.length() != 0 && \r
+            !fnName.equals("anonymous"))\r
+        {\r
+            setProp(scope, fnName, result, scope);\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    static void checkDeprecated(Context cx, String name) {\r
+        int version = cx.getLanguageVersion();\r
+        if (version >= Context.VERSION_1_4 || version == Context.VERSION_DEFAULT) {\r
+            String msg = getMessage1("msg.deprec.ctor", name);\r
+            if (version == Context.VERSION_DEFAULT)\r
+                Context.reportWarning(msg);\r
+            else\r
+                throw Context.reportRuntimeError(msg);\r
+        }\r
+    }\r
+\r
+    public static String getMessage0(String messageId) {\r
+        return Context.getMessage0(messageId);\r
+    }\r
+\r
+    public static String getMessage1(String messageId, Object arg1) {\r
+        return Context.getMessage1(messageId, arg1);\r
+    }\r
+\r
+    public static String getMessage2\r
+        (String messageId, Object arg1, Object arg2) \r
+    {\r
+        return Context.getMessage2(messageId, arg1, arg2);\r
+    }\r
+\r
+    public static String getMessage(String messageId, Object[] arguments) {\r
+        return Context.getMessage(messageId, arguments);\r
+    }\r
+\r
+    public static RegExpProxy getRegExpProxy(Context cx) {\r
+        return cx.getRegExpProxy();\r
+    }\r
+\r
+    public static NativeCall getCurrentActivation(Context cx) {\r
+        return cx.currentActivation;\r
+    }\r
+\r
+    public static void setCurrentActivation(Context cx,\r
+                                            NativeCall activation)\r
+    {\r
+        cx.currentActivation = activation;\r
+    }\r
+\r
+    public static Class loadClassName(String className) \r
+        throws ClassNotFoundException\r
+    {\r
+        /*\r
+        try {\r
+            ClassLoader cl = DefiningClassLoader.getContextClassLoader();\r
+            if (cl != null)\r
+                return cl.loadClass(className);\r
+        } catch (SecurityException e) {\r
+            // fall through...\r
+        } catch (ClassNotFoundException e) {\r
+            // Rather than just letting the exception propagate\r
+            // we'll try Class.forName as well. The results could be\r
+            // different if this class was loaded on a different\r
+            // thread than the current thread.\r
+            // So fall through...\r
+        }\r
+        */\r
+        return Class.forName(className);                \r
+    }\r
+\r
+    static boolean hasProp(Scriptable start, String name) {\r
+        Scriptable m = start;\r
+        do {\r
+            if (m.has(name, start))\r
+                return true;\r
+            m = m.getPrototype();\r
+        } while (m != null);\r
+        return false;\r
+    }\r
+\r
+    private static RuntimeException errorWithClassName(String msg, Object val)\r
+    {\r
+        return Context.reportRuntimeError1(msg, val.getClass().getName());\r
+    }\r
+\r
+    public static final Object[] emptyArgs = new Object[0];\r
+\r
+}\r
+\r
+\r
+/**\r
+ * This is the enumeration needed by the for..in statement.\r
+ *\r
+ * See ECMA 12.6.3.\r
+ *\r
+ * IdEnumeration maintains a Hashtable to make sure a given\r
+ * id is enumerated only once across multiple objects in a\r
+ * prototype chain.\r
+ *\r
+ * XXX - ECMA delete doesn't hide properties in the prototype,\r
+ * but js/ref does. This means that the js/ref for..in can\r
+ * avoid maintaining a hash table and instead perform lookups\r
+ * to see if a given property has already been enumerated.\r
+ *\r
+ */\r
+class IdEnumeration implements Enumeration {\r
+    IdEnumeration(Scriptable m) {\r
+        used = new Hashtable(27);\r
+        changeObject(m);\r
+        next = getNext();\r
+    }\r
+\r
+    public boolean hasMoreElements() {\r
+        return next != null;\r
+    }\r
+\r
+    public Object nextElement() {\r
+        Object result = next;\r
+\r
+        // only key used; 'next' as value for convenience\r
+        used.put(next, next);\r
+\r
+        next = getNext();\r
+        return result;\r
+    }\r
+\r
+    private void changeObject(Scriptable m) {\r
+        obj = m;\r
+        if (obj != null) {\r
+            array = m.getIds();\r
+            if (array.length == 0)\r
+                changeObject(obj.getPrototype());\r
+        }\r
+        index = 0;\r
+    }\r
+\r
+    private Object getNext() {\r
+        if (obj == null)\r
+            return null;\r
+        Object result;\r
+        for (;;) {\r
+            if (index == array.length) {\r
+                changeObject(obj.getPrototype());\r
+                if (obj == null)\r
+                    return null;\r
+            }\r
+            result = array[index++];\r
+            if (result instanceof String) {\r
+                if (!obj.has((String) result, obj))\r
+                    continue;   // must have been deleted\r
+            } else {\r
+                if (!obj.has(((Number) result).intValue(), obj))\r
+                    continue;   // must have been deleted\r
+            }\r
+            if (!used.containsKey(result)) {\r
+                break;\r
+            }\r
+        }\r
+        return ScriptRuntime.toString(result);\r
+    }\r
+\r
+    private Object next;\r
+    private Scriptable obj;\r
+    private int index;\r
+    private Object[] array;\r
+    private Hashtable used;\r
+}\r
diff --git a/src/org/mozilla/javascript/Scriptable.java b/src/org/mozilla/javascript/Scriptable.java
new file mode 100644 (file)
index 0000000..29cd458
--- /dev/null
@@ -0,0 +1,339 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This is interface that all objects in JavaScript must implement.\r
+ * The interface provides for the management of properties and for\r
+ * performing conversions.\r
+ * <p>\r
+ * Host system implementors may find it easier to extend the ScriptableObject\r
+ * class rather than implementing Scriptable when writing host objects.\r
+ * <p>\r
+ * There are many static methods defined in ScriptableObject that perform\r
+ * the multiple calls to the Scriptable interface needed in order to  \r
+ * manipulate properties in prototype chains.\r
+ * <p>\r
+ *\r
+ * @see org.mozilla.javascript.ScriptableObject\r
+ * @author Norris Boyd\r
+ * @author Nick Thompson\r
+ * @author Brendan Eich\r
+ */\r
+\r
+public interface Scriptable {\r
+\r
+    /**\r
+     * Get the name of the set of objects implemented by this Java class.\r
+     * This corresponds to the [[Class]] operation in ECMA and is used\r
+     * by Object.prototype.toString() in ECMA.<p>\r
+     * See ECMA 8.6.2 and 15.2.4.2.\r
+     */\r
+    public String getClassName();\r
+\r
+    /**\r
+     * Value returned from <code>get</code> if the property is not\r
+     * found.\r
+     */\r
+    public static final Object NOT_FOUND = new Object();\r
+\r
+    /**\r
+     * Get a named property from the object.\r
+     *\r
+     * Looks property up in this object and returns the associated value\r
+     * if found. Returns NOT_FOUND if not found.\r
+     * Note that this method is not expected to traverse the prototype\r
+     * chain. This is different from the ECMA [[Get]] operation.\r
+     *\r
+     * Depending on the property selector, the runtime will call\r
+     * this method or the form of <code>get</code> that takes an\r
+     * integer:\r
+     * <table>\r
+     * <tr><th>JavaScript code</th><th>Java code</th></tr>\r
+     * <tr><td>a.b      </td><td>a.get("b", a)</td></tr>\r
+     * <tr><td>a["foo"] </td><td>a.get("foo", a)</td></tr>\r
+     * <tr><td>a[3]     </td><td>a.get(3, a)</td></tr>\r
+     * <tr><td>a["3"]   </td><td>a.get(3, a)</td></tr>\r
+     * <tr><td>a[3.0]   </td><td>a.get(3, a)</td></tr>\r
+     * <tr><td>a["3.0"] </td><td>a.get("3.0", a)</td></tr>\r
+     * <tr><td>a[1.1]   </td><td>a.get("1.1", a)</td></tr>\r
+     * <tr><td>a[-4]    </td><td>a.get(-4, a)</td></tr>\r
+     * </table>\r
+     * <p>\r
+     * The values that may be returned are limited to the following:\r
+     * <UL>\r
+     * <LI>java.lang.Boolean objects</LI>\r
+     * <LI>java.lang.String objects</LI>\r
+     * <LI>java.lang.Number objects</LI>\r
+     * <LI>org.mozilla.javascript.Scriptable objects</LI>\r
+     * <LI>null</LI>\r
+     * <LI>The value returned by Context.getUndefinedValue()</LI>\r
+     * <LI>NOT_FOUND</LI>\r
+     * </UL>\r
+     * @param name the name of the property\r
+     * @param start the object in which the lookup began\r
+     * @return the value of the property (may be null), or NOT_FOUND\r
+     * @see org.mozilla.javascript.Context#getUndefinedValue\r
+     */\r
+    public Object get(String name, Scriptable start);\r
+\r
+    /**\r
+     * Get a property from the object selected by an integral index.\r
+     *\r
+     * Identical to <code>get(String, Scriptable)</code> except that\r
+     * an integral index is used to select the property.\r
+     *\r
+     * @param index the numeric index for the property\r
+     * @param start the object in which the lookup began\r
+     * @return the value of the property (may be null), or NOT_FOUND\r
+     * @see org.mozilla.javascript.Scriptable#get(String,Scriptable)\r
+     */\r
+    public Object get(int index, Scriptable start);\r
+\r
+    /**\r
+     * Indicates whether or not a named property is defined in an object.\r
+     *\r
+     * Does not traverse the prototype chain.<p>\r
+     *\r
+     * The property is specified by a String name\r
+     * as defined for the <code>get</code> method.<p>\r
+     *\r
+     * @param name the name of the property\r
+     * @param start the object in which the lookup began\r
+     * @return true if and only if the named property is found in the object\r
+     * @see org.mozilla.javascript.Scriptable#get\r
+     * @see org.mozilla.javascript.ScriptableObject#getProperty\r
+     */\r
+    public boolean has(String name, Scriptable start);\r
+\r
+    /**\r
+     * Indicates whether or not an indexed  property is defined in an object.\r
+     *\r
+     * Does not traverse the prototype chain.<p>\r
+     *\r
+     * The property is specified by an integral index\r
+     * as defined for the <code>get</code> method.<p>\r
+     *\r
+     * @param index the numeric index for the property\r
+     * @param start the object in which the lookup began\r
+     * @return true if and only if the indexed property is found in the object\r
+     * @see org.mozilla.javascript.Scriptable#get\r
+     * @see org.mozilla.javascript.ScriptableObject#getProperty\r
+     */\r
+    public boolean has(int index, Scriptable start);\r
+\r
+    /**\r
+     * Sets a named property in this object.\r
+     * <p>\r
+     * The property is specified by a string name \r
+     * as defined for <code>get</code>.\r
+     * <p>\r
+     * The possible values that may be passed in are as defined for\r
+     * <code>get</code>. A class that implements this method may choose\r
+     * to ignore calls to set certain properties, in which case those\r
+     * properties are effectively read-only.<p>\r
+     * For properties defined in a prototype chain,\r
+     * use <code>putProperty</code> in ScriptableObject. <p>\r
+     * Note that if a property <i>a</i> is defined in the prototype <i>p</i>\r
+     * of an object <i>o</i>, then evaluating <code>o.a = 23</code> will cause\r
+     * <code>set</code> to be called on the prototype <i>p</i> with\r
+     * <i>o</i> as the  <i>start</i> parameter.\r
+     * To preserve JavaScript semantics, it is the Scriptable\r
+     * object's responsibility to modify <i>o</i>. <p>\r
+     * This design allows properties to be defined in prototypes and implemented\r
+     * in terms of getters and setters of Java values without consuming slots\r
+     * in each instance.<p>\r
+     * <p>\r
+     * The values that may be set are limited to the following:\r
+     * <UL>\r
+     * <LI>java.lang.Boolean objects</LI>\r
+     * <LI>java.lang.String objects</LI>\r
+     * <LI>java.lang.Number objects</LI>\r
+     * <LI>org.mozilla.javascript.Scriptable objects</LI>\r
+     * <LI>null</LI>\r
+     * <LI>The value returned by Context.getUndefinedValue()</LI> \r
+     * </UL><p> \r
+     * Arbitrary Java objects may be wrapped in a Scriptable by first calling\r
+     * <code>Context.toObject</code>. This allows the property of a JavaScript\r
+     * object to contain an arbitrary Java object as a value.<p> \r
+     * Note that <code>has</code> will be called by the runtime first before\r
+     * <code>set</code> is called to determine in which object the\r
+     * property is defined.\r
+     * Note that this method is not expected to traverse the prototype chain,\r
+     * which is different from the ECMA [[Put]] operation.\r
+     * @param name the name of the property\r
+     * @param start the object whose property is being set\r
+     * @param value value to set the property to\r
+     * @see org.mozilla.javascript.Scriptable#has\r
+     * @see org.mozilla.javascript.Scriptable#get\r
+     * @see org.mozilla.javascript.ScriptableObject#putProperty\r
+     * @see org.mozilla.javascript.Context#toObject\r
+     */\r
+    public void put(String name, Scriptable start, Object value);\r
+\r
+    /**\r
+     * Sets an indexed property in this object.\r
+     * <p>\r
+     * The property is specified by an integral index\r
+     * as defined for <code>get</code>.<p>\r
+     *\r
+     * Identical to <code>put(String, Scriptable, Object)</code> except that\r
+     * an integral index is used to select the property.\r
+     *\r
+     * @param index the numeric index for the property\r
+     * @param start the object whose property is being set\r
+     * @param value value to set the property to\r
+     * @see org.mozilla.javascript.Scriptable#has\r
+     * @see org.mozilla.javascript.Scriptable#get\r
+     * @see org.mozilla.javascript.Scriptable#put(String,Scriptable,Object)\r
+     * @see org.mozilla.javascript.ScriptableObject#putProperty\r
+     */\r
+    public void put(int index, Scriptable start, Object value);\r
+\r
+    /**\r
+     * Removes a property from this object.\r
+     * This operation corresponds to the ECMA [[Delete]] except that\r
+     * the no result is returned. The runtime will guarantee that this\r
+     * method is called only if the property exists. After this method\r
+     * is called, the runtime will call Scriptable.has to see if the\r
+     * property has been removed in order to determine the boolean\r
+     * result of the delete operator as defined by ECMA 11.4.1.\r
+     * <p>\r
+     * A property can be made permanent by ignoring calls to remove\r
+     * it.<p>\r
+     * The property is specified by a String name\r
+     * as defined for <code>get</code>.\r
+     * <p>\r
+     * To delete properties defined in a prototype chain,\r
+     * see deleteProperty in ScriptableObject.\r
+     * @param name the identifier for the property\r
+     * @see org.mozilla.javascript.Scriptable#get\r
+     * @see org.mozilla.javascript.ScriptableObject#deleteProperty\r
+     */\r
+    public void delete(String name);\r
+\r
+    /**\r
+     * Removes a property from this object.\r
+     *\r
+     * The property is specified by an integral index\r
+     * as defined for <code>get</code>.\r
+     * <p>\r
+     * To delete properties defined in a prototype chain,\r
+     * see deleteProperty in ScriptableObject.\r
+     *\r
+     * Identical to <code>delete(String)</code> except that\r
+     * an integral index is used to select the property.\r
+     *\r
+     * @param index the numeric index for the property\r
+     * @see org.mozilla.javascript.Scriptable#get\r
+     * @see org.mozilla.javascript.ScriptableObject#deleteProperty\r
+     */\r
+    public void delete(int index);\r
+\r
+    /**\r
+     * Get the prototype of the object.\r
+     * @return the prototype\r
+     */\r
+    public Scriptable getPrototype();\r
+\r
+    /**\r
+     * Set the prototype of the object.\r
+     * @param prototype the prototype to set\r
+     */\r
+    public void setPrototype(Scriptable prototype);\r
+\r
+    /**\r
+     * Get the parent scope of the object.\r
+     * @return the parent scope\r
+     */\r
+    public Scriptable getParentScope();\r
+\r
+    /**\r
+     * Set the parent scope of the object.\r
+     * @param parent the parent scope to set\r
+     */\r
+    public void setParentScope(Scriptable parent);\r
+\r
+    /**\r
+     * Get an array of property ids.\r
+     *\r
+     * Not all property ids need be returned. Those properties\r
+     * whose ids are not returned are considered non-enumerable.\r
+     *\r
+     * @return an array of Objects. Each entry in the array is either\r
+     *         a java.lang.String or a java.lang.Number\r
+     */\r
+    public Object[] getIds();\r
+\r
+    /**\r
+     * Get the default value of the object with a given hint.\r
+     * The hints are String.class for type String, Number.class for type\r
+     * Number, Scriptable.class for type Object, and Boolean.class for\r
+     * type Boolean. <p>\r
+     *\r
+     * A <code>hint</code> of null means "no hint".\r
+     *\r
+     * See ECMA 8.6.2.6.\r
+     *\r
+     * @param hint the type hint\r
+     * @return the default value\r
+     */\r
+    public Object getDefaultValue(Class hint);\r
+\r
+    /**\r
+     * The instanceof operator.\r
+     *\r
+     * <p>\r
+     * The JavaScript code "lhs instanceof rhs" causes rhs.hasInstance(lhs) to\r
+     * be called.\r
+     *\r
+     * <p>\r
+     * The return value is implementation dependent so that embedded host objects can\r
+     * return an appropriate value.  See the JS 1.3 language documentation for more\r
+     * detail.\r
+     *\r
+     * <p>This operator corresponds to the proposed EMCA [[HasInstance]] operator.\r
+     *\r
+     * @param instance The value that appeared on the LHS of the instanceof\r
+     *              operator\r
+     *\r
+     * @return an implementation dependent value\r
+     */\r
+    public boolean hasInstance(Scriptable instance);\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java
new file mode 100644 (file)
index 0000000..7c2bf9c
--- /dev/null
@@ -0,0 +1,1807 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Igor Bukanov\r
+ * Roger Lawrence\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.lang.reflect.*;\r
+import java.util.Hashtable;\r
+\r
+/**\r
+ * This is the default implementation of the Scriptable interface. This\r
+ * class provides convenient default behavior that makes it easier to\r
+ * define host objects.\r
+ * <p>\r
+ * Various properties and methods of JavaScript objects can be conveniently\r
+ * defined using methods of ScriptableObject.\r
+ * <p>\r
+ * Classes extending ScriptableObject must define the getClassName method.\r
+ *\r
+ * @see org.mozilla.javascript.Scriptable\r
+ * @author Norris Boyd\r
+ */\r
+\r
+public abstract class ScriptableObject implements Scriptable {\r
+\r
+    /**\r
+     * The empty property attribute.\r
+     *\r
+     * Used by getAttributes() and setAttributes().\r
+     *\r
+     * @see org.mozilla.javascript.ScriptableObject#getAttributes\r
+     * @see org.mozilla.javascript.ScriptableObject#setAttributes\r
+     */\r
+    public static final int EMPTY =     0x00;\r
+\r
+    /**\r
+     * Property attribute indicating assignment to this property is ignored.\r
+     *\r
+     * @see org.mozilla.javascript.ScriptableObject#put\r
+     * @see org.mozilla.javascript.ScriptableObject#getAttributes\r
+     * @see org.mozilla.javascript.ScriptableObject#setAttributes\r
+     */\r
+    public static final int READONLY =  0x01;\r
+\r
+    /**\r
+     * Property attribute indicating property is not enumerated.\r
+     *\r
+     * Only enumerated properties will be returned by getIds().\r
+     *\r
+     * @see org.mozilla.javascript.ScriptableObject#getIds\r
+     * @see org.mozilla.javascript.ScriptableObject#getAttributes\r
+     * @see org.mozilla.javascript.ScriptableObject#setAttributes\r
+     */\r
+    public static final int DONTENUM =  0x02;\r
+\r
+    /**\r
+     * Property attribute indicating property cannot be deleted.\r
+     *\r
+     * @see org.mozilla.javascript.ScriptableObject#delete\r
+     * @see org.mozilla.javascript.ScriptableObject#getAttributes\r
+     * @see org.mozilla.javascript.ScriptableObject#setAttributes\r
+     */\r
+    public static final int PERMANENT = 0x04;\r
+\r
+    /**\r
+     * Return the name of the class.\r
+     *\r
+     * This is typically the same name as the constructor.\r
+     * Classes extending ScriptableObject must implement this abstract\r
+     * method.\r
+     */\r
+    public abstract String getClassName();\r
+\r
+    /**\r
+     * Returns true if the named property is defined.\r
+     *\r
+     * @param name the name of the property\r
+     * @param start the object in which the lookup began\r
+     * @return true if and only if the property was found in the object\r
+     */\r
+    public boolean has(String name, Scriptable start) {\r
+        return getSlot(name, name.hashCode(), false) != null;\r
+    }\r
+\r
+    /**\r
+     * Returns true if the property index is defined.\r
+     *\r
+     * @param index the numeric index for the property\r
+     * @param start the object in which the lookup began\r
+     * @return true if and only if the property was found in the object\r
+     */\r
+    public boolean has(int index, Scriptable start) {\r
+        return getSlot(null, index, false) != null;\r
+    }\r
+\r
+    /**\r
+     * Returns the value of the named property or NOT_FOUND.\r
+     *\r
+     * If the property was created using defineProperty, the\r
+     * appropriate getter method is called.\r
+     *\r
+     * @param name the name of the property\r
+     * @param start the object in which the lookup began\r
+     * @return the value of the property (may be null), or NOT_FOUND\r
+     */\r
+    public Object get(String name, Scriptable start) {\r
+        Slot slot = lastAccess; // Get local copy\r
+        if (name == slot.stringKey) {\r
+            if (slot.wasDeleted == 0) { return slot.value; }\r
+        } \r
+        int hashCode = name.hashCode();\r
+        slot = getSlot(name, hashCode, false);\r
+        if (slot == null)\r
+            return Scriptable.NOT_FOUND;\r
+        if ((slot.flags & Slot.HAS_GETTER) != 0) {\r
+            GetterSlot getterSlot = (GetterSlot) slot;\r
+            try {\r
+                if (getterSlot.delegateTo == null) {\r
+                    // Walk the prototype chain to find an appropriate\r
+                    // object to invoke the getter on.\r
+                    Class clazz = getterSlot.getter.getDeclaringClass();\r
+                    while (!clazz.isInstance(start)) {\r
+                        start = start.getPrototype();\r
+                        if (start == null) {\r
+                            start = this;\r
+                            break;\r
+                        }\r
+                    }\r
+                    return getterSlot.getter.invoke(start, ScriptRuntime.emptyArgs);\r
+                }\r
+                Object[] args = { this };\r
+                return getterSlot.getter.invoke(getterSlot.delegateTo, args);\r
+            }\r
+            catch (InvocationTargetException e) {\r
+                throw WrappedException.wrapException(e);\r
+            }\r
+            catch (IllegalAccessException e) {\r
+                throw WrappedException.wrapException(e);\r
+            }\r
+        }\r
+        // Here stringKey.equals(name) holds, but it can be that \r
+        // slot.stringKey != name. To make last name cache work, need\r
+        // to change the key\r
+        slot.stringKey = name;\r
+\r
+        // Update cache. \r
+        lastAccess = slot;\r
+        return slot.value;\r
+    }\r
+\r
+    /**\r
+     * Returns the value of the indexed property or NOT_FOUND.\r
+     *\r
+     * @param index the numeric index for the property\r
+     * @param start the object in which the lookup began\r
+     * @return the value of the property (may be null), or NOT_FOUND\r
+     */\r
+    public Object get(int index, Scriptable start) {\r
+        Slot slot = getSlot(null, index, false);\r
+        if (slot == null)\r
+            return Scriptable.NOT_FOUND;\r
+        return slot.value;\r
+    }\r
+    \r
+    /**\r
+     * Sets the value of the named property, creating it if need be.\r
+     *\r
+     * If the property was created using defineProperty, the\r
+     * appropriate setter method is called. <p>\r
+     *\r
+     * If the property's attributes include READONLY, no action is\r
+     * taken.\r
+     * This method will actually set the property in the start\r
+     * object.\r
+     *\r
+     * @param name the name of the property\r
+     * @param start the object whose property is being set\r
+     * @param value value to set the property to\r
+     */\r
+    public void put(String name, Scriptable start, Object value) {\r
+        int hash = name.hashCode();\r
+        Slot slot = getSlot(name, hash, false);\r
+        if (slot == null) {\r
+            if (start != this) {\r
+                start.put(name, start, value);\r
+                return;\r
+            }\r
+            slot = getSlotToSet(name, hash, false);\r
+        }\r
+        if ((slot.attributes & ScriptableObject.READONLY) != 0)\r
+            return;\r
+        if ((slot.flags & Slot.HAS_SETTER) != 0) {\r
+            GetterSlot getterSlot = (GetterSlot) slot;\r
+            try {\r
+                Class pTypes[] = getterSlot.setter.getParameterTypes();\r
+                Class desired = pTypes[pTypes.length - 1];\r
+                Object actualArg\r
+                        = FunctionObject.convertArg(start, value, desired);\r
+                if (getterSlot.delegateTo == null) {\r
+                    // Walk the prototype chain to find an appropriate\r
+                    // object to invoke the setter on.\r
+                    Object[] arg = { actualArg };\r
+                    Class clazz = getterSlot.setter.getDeclaringClass();\r
+                    while (!clazz.isInstance(start)) {\r
+                        start = start.getPrototype();\r
+                        if (start == null) {\r
+                            start = this;\r
+                            break;\r
+                        }\r
+                    }\r
+                    Object v = getterSlot.setter.invoke(start, arg);\r
+                    if (getterSlot.setterReturnsValue) {\r
+                        slot.value = v;\r
+                        if (!(v instanceof Method))\r
+                            slot.flags = 0;\r
+                    }\r
+                    return;\r
+                }\r
+                Object[] args = { this, actualArg };\r
+                Object v = getterSlot.setter.invoke(getterSlot.delegateTo, args);\r
+                if (getterSlot.setterReturnsValue) {\r
+                    slot.value = v;\r
+                    if (!(v instanceof Method))\r
+                        slot.flags = 0;\r
+                }\r
+                return;\r
+            }\r
+            catch (InvocationTargetException e) {\r
+                throw WrappedException.wrapException(e);\r
+            }\r
+            catch (IllegalAccessException e) {\r
+                throw WrappedException.wrapException(e);\r
+            }\r
+        }\r
+        if (this == start) {\r
+            slot.value = value;\r
+            // Make cache work\r
+            slot.stringKey = name;\r
+            lastAccess = slot;\r
+        } else {\r
+            start.put(name, start, value);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Sets the value of the indexed property, creating it if need be.\r
+     *\r
+     * @param index the numeric index for the property\r
+     * @param start the object whose property is being set\r
+     * @param value value to set the property to\r
+     */\r
+    public void put(int index, Scriptable start, Object value) {\r
+        Slot slot = getSlot(null, index, false);\r
+        if (slot == null) {\r
+            if (start != this) {\r
+                start.put(index, start, value);\r
+                return;\r
+            }\r
+            slot = getSlotToSet(null, index, false);\r
+        }\r
+        if ((slot.attributes & ScriptableObject.READONLY) != 0)\r
+            return;\r
+        if (this == start) {\r
+            slot.value = value;\r
+        } else {\r
+            start.put(index, start, value);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Removes a named property from the object.\r
+     *\r
+     * If the property is not found, or it has the PERMANENT attribute,\r
+     * no action is taken.\r
+     *\r
+     * @param name the name of the property\r
+     */\r
+    public void delete(String name) {\r
+        removeSlot(name, name.hashCode());\r
+    }\r
+\r
+    /**\r
+     * Removes the indexed property from the object.\r
+     *\r
+     * If the property is not found, or it has the PERMANENT attribute,\r
+     * no action is taken.\r
+     *\r
+     * @param index the numeric index for the property\r
+     */\r
+    public void delete(int index) {\r
+        removeSlot(null, index);\r
+    }\r
+\r
+    /**\r
+     * Get the attributes of a named property.\r
+     *\r
+     * The property is specified by <code>name</code>\r
+     * as defined for <code>has</code>.<p>\r
+     *\r
+     * @param name the identifier for the property\r
+     * @param start the object in which the lookup began\r
+     * @return the bitset of attributes\r
+     * @exception PropertyException if the named property\r
+     *            is not found\r
+     * @see org.mozilla.javascript.ScriptableObject#has\r
+     * @see org.mozilla.javascript.ScriptableObject#READONLY\r
+     * @see org.mozilla.javascript.ScriptableObject#DONTENUM\r
+     * @see org.mozilla.javascript.ScriptableObject#PERMANENT\r
+     * @see org.mozilla.javascript.ScriptableObject#EMPTY\r
+     */\r
+    public int getAttributes(String name, Scriptable start)\r
+        throws PropertyException\r
+    {\r
+        Slot slot = getSlot(name, name.hashCode(), false);\r
+        if (slot == null) {\r
+            throw PropertyException.withMessage0("msg.prop.not.found");\r
+        }\r
+        return slot.attributes;\r
+    }\r
+\r
+    /**\r
+     * Get the attributes of an indexed property.\r
+     *\r
+     * @param index the numeric index for the property\r
+     * @param start the object in which the lookup began\r
+     * @exception PropertyException if the indexed property\r
+     *            is not found\r
+     * @return the bitset of attributes\r
+     * @see org.mozilla.javascript.ScriptableObject#has\r
+     * @see org.mozilla.javascript.ScriptableObject#READONLY\r
+     * @see org.mozilla.javascript.ScriptableObject#DONTENUM\r
+     * @see org.mozilla.javascript.ScriptableObject#PERMANENT\r
+     * @see org.mozilla.javascript.ScriptableObject#EMPTY\r
+     */\r
+    public int getAttributes(int index, Scriptable start)\r
+        throws PropertyException\r
+    {\r
+        Slot slot = getSlot(null, index, false);\r
+        if (slot == null) {\r
+            throw PropertyException.withMessage0("msg.prop.not.found");\r
+        }\r
+        return slot.attributes;\r
+    }\r
+\r
+    /**\r
+     * Set the attributes of a named property.\r
+     *\r
+     * The property is specified by <code>name</code>\r
+     * as defined for <code>has</code>.<p>\r
+     *\r
+     * The possible attributes are READONLY, DONTENUM,\r
+     * and PERMANENT. Combinations of attributes\r
+     * are expressed by the bitwise OR of attributes.\r
+     * EMPTY is the state of no attributes set. Any unused\r
+     * bits are reserved for future use.\r
+     *\r
+     * @param name the name of the property\r
+     * @param start the object in which the lookup began\r
+     * @param attributes the bitset of attributes\r
+     * @exception PropertyException if the named property\r
+     *            is not found\r
+     * @see org.mozilla.javascript.Scriptable#has\r
+     * @see org.mozilla.javascript.ScriptableObject#READONLY\r
+     * @see org.mozilla.javascript.ScriptableObject#DONTENUM\r
+     * @see org.mozilla.javascript.ScriptableObject#PERMANENT\r
+     * @see org.mozilla.javascript.ScriptableObject#EMPTY\r
+     */\r
+    public void setAttributes(String name, Scriptable start,\r
+                              int attributes)\r
+        throws PropertyException\r
+    {\r
+        final int mask = READONLY | DONTENUM | PERMANENT;\r
+        attributes &= mask; // mask out unused bits\r
+        Slot slot = getSlot(name, name.hashCode(), false);\r
+        if (slot == null) {\r
+            throw PropertyException.withMessage0("msg.prop.not.found");\r
+        }\r
+        slot.attributes = (short) attributes;\r
+    }\r
+\r
+    /**\r
+     * Set the attributes of an indexed property.\r
+     *\r
+     * @param index the numeric index for the property\r
+     * @param start the object in which the lookup began\r
+     * @param attributes the bitset of attributes\r
+     * @exception PropertyException if the indexed property\r
+     *            is not found\r
+     * @see org.mozilla.javascript.Scriptable#has\r
+     * @see org.mozilla.javascript.ScriptableObject#READONLY\r
+     * @see org.mozilla.javascript.ScriptableObject#DONTENUM\r
+     * @see org.mozilla.javascript.ScriptableObject#PERMANENT\r
+     * @see org.mozilla.javascript.ScriptableObject#EMPTY\r
+     */\r
+    public void setAttributes(int index, Scriptable start,\r
+                              int attributes)\r
+        throws PropertyException\r
+    {\r
+        Slot slot = getSlot(null, index, false);\r
+        if (slot == null) {\r
+            throw PropertyException.withMessage0("msg.prop.not.found");\r
+        }\r
+        slot.attributes = (short) attributes;\r
+    }\r
+\r
+    /**\r
+     * Returns the prototype of the object.\r
+     */\r
+    public Scriptable getPrototype() {\r
+        return prototype;\r
+    }\r
+\r
+    /**\r
+     * Sets the prototype of the object.\r
+     */\r
+    public void setPrototype(Scriptable m) {\r
+        prototype = m;\r
+    }\r
+\r
+    /**\r
+     * Returns the parent (enclosing) scope of the object.\r
+     */\r
+    public Scriptable getParentScope() {\r
+        return parent;\r
+    }\r
+\r
+    /**\r
+     * Sets the parent (enclosing) scope of the object.\r
+     */\r
+    public void setParentScope(Scriptable m) {\r
+        parent = m;\r
+    }\r
+\r
+    /**\r
+     * Returns an array of ids for the properties of the object.\r
+     *\r
+     * <p>Any properties with the attribute DONTENUM are not listed. <p>\r
+     *\r
+     * @return an array of java.lang.Objects with an entry for every\r
+     * listed property. Properties accessed via an integer index will \r
+     * have a corresponding\r
+     * Integer entry in the returned array. Properties accessed by\r
+     * a String will have a String entry in the returned array.\r
+     */\r
+    public Object[] getIds() {\r
+        return getIds(false);\r
+    }\r
+    \r
+    /**\r
+     * Returns an array of ids for the properties of the object.\r
+     *\r
+     * <p>All properties, even those with attribute DONTENUM, are listed. <p>\r
+     *\r
+     * @return an array of java.lang.Objects with an entry for every\r
+     * listed property. Properties accessed via an integer index will \r
+     * have a corresponding\r
+     * Integer entry in the returned array. Properties accessed by\r
+     * a String will have a String entry in the returned array.\r
+     */\r
+    public Object[] getAllIds() {\r
+        return getIds(true);\r
+    }\r
+    \r
+    /**\r
+     * Implements the [[DefaultValue]] internal method.\r
+     *\r
+     * <p>Note that the toPrimitive conversion is a no-op for\r
+     * every type other than Object, for which [[DefaultValue]]\r
+     * is called. See ECMA 9.1.<p>\r
+     *\r
+     * A <code>hint</code> of null means "no hint".\r
+     *\r
+     * @param typeHint the type hint\r
+     * @return the default value for the object\r
+     *\r
+     * See ECMA 8.6.2.6.\r
+     */\r
+    public Object getDefaultValue(Class typeHint) {\r
+        Object val;\r
+        Context cx = null;\r
+        try {\r
+            for (int i=0; i < 2; i++) {\r
+                if (typeHint == ScriptRuntime.StringClass ? i == 0 : i == 1) {\r
+                    Object v = getProperty(this, "toString");\r
+                    if (!(v instanceof Function))\r
+                        continue;\r
+                    Function fun = (Function) v;\r
+                    if (cx == null)\r
+                        cx = Context.getContext();\r
+                    val = fun.call(cx, fun.getParentScope(), this,\r
+                                   ScriptRuntime.emptyArgs);\r
+                } else {\r
+                    String hint;\r
+                    if (typeHint == null)\r
+                        hint = "undefined";\r
+                    else if (typeHint == ScriptRuntime.StringClass)\r
+                        hint = "string";\r
+                    else if (typeHint == ScriptRuntime.ScriptableClass)\r
+                        hint = "object";\r
+                    else if (typeHint == ScriptRuntime.FunctionClass)\r
+                        hint = "function";\r
+                    else if (typeHint == ScriptRuntime.BooleanClass || \r
+                                                typeHint == Boolean.TYPE)\r
+                        hint = "boolean";\r
+                    else if (typeHint == ScriptRuntime.NumberClass ||\r
+                             typeHint == ScriptRuntime.ByteClass || \r
+                             typeHint == Byte.TYPE ||\r
+                             typeHint == ScriptRuntime.ShortClass || \r
+                             typeHint == Short.TYPE ||\r
+                             typeHint == ScriptRuntime.IntegerClass || \r
+                             typeHint == Integer.TYPE ||\r
+                             typeHint == ScriptRuntime.FloatClass || \r
+                             typeHint == Float.TYPE ||\r
+                             typeHint == ScriptRuntime.DoubleClass || \r
+                             typeHint == Double.TYPE)\r
+                        hint = "number";\r
+                    else {\r
+                        throw Context.reportRuntimeError1(\r
+                            "msg.invalid.type", typeHint.toString());\r
+                    }\r
+                    Object v = getProperty(this, "valueOf");\r
+                    if (!(v instanceof Function))\r
+                        continue;\r
+                    Function fun = (Function) v;\r
+                    Object[] args = { hint };\r
+                    if (cx == null)\r
+                        cx = Context.getContext();\r
+                    val = fun.call(cx, fun.getParentScope(), this, args);\r
+                }\r
+                if (val != null && (val == Undefined.instance ||\r
+                                    !(val instanceof Scriptable) ||\r
+                                    typeHint == Scriptable.class ||\r
+                                    typeHint == Function.class))\r
+                {\r
+                    return val;\r
+                }\r
+                if (val instanceof NativeJavaObject) {\r
+                    // Let a wrapped java.lang.String pass for a primitive \r
+                    // string.\r
+                    Object u = ((Wrapper) val).unwrap();\r
+                    if (u instanceof String)\r
+                        return u;\r
+                }\r
+            }\r
+            // fall through to error \r
+        }\r
+        catch (JavaScriptException jse) {\r
+            // fall through to error \r
+        }\r
+        Object arg = (typeHint == null) ? "undefined" : typeHint.toString();\r
+        throw NativeGlobal.typeError1("msg.default.value", arg, this);\r
+    }\r
+\r
+    /**\r
+     * Implements the instanceof operator.\r
+     *\r
+     * <p>This operator has been proposed to ECMA.\r
+     *\r
+     * @param instance The value that appeared on the LHS of the instanceof\r
+     *              operator\r
+     * @return true if "this" appears in value's prototype chain\r
+     *\r
+     */\r
+    public boolean hasInstance(Scriptable instance) {\r
+        // Default for JS objects (other than Function) is to do prototype\r
+        // chasing.  This will be overridden in NativeFunction and non-JS objects.\r
+\r
+        return ScriptRuntime.jsDelegatesTo(instance, this);\r
+    }\r
+\r
+    /**\r
+     * Defines JavaScript objects from a Java class that implements Scriptable.\r
+     *\r
+     * If the given class has a method\r
+     * <pre>\r
+     * static void init(Context cx, Scriptable scope, boolean sealed);</pre>\r
+     *\r
+     * or its compatibility form \r
+     * <pre>\r
+     * static void init(Scriptable scope);</pre>\r
+     *\r
+     * then it is invoked and no further initialization is done.<p>\r
+     *\r
+     * However, if no such a method is found, then the class's constructors and\r
+     * methods are used to initialize a class in the following manner.<p>\r
+     *\r
+     * First, the zero-parameter constructor of the class is called to\r
+     * create the prototype. If no such constructor exists,\r
+     * a ClassDefinitionException is thrown. <p>\r
+     *\r
+     * Next, all methods are scanned for special prefixes that indicate that they\r
+     * have special meaning for defining JavaScript objects.\r
+     * These special prefixes are\r
+     * <ul>\r
+     * <li><code>jsFunction_</code> for a JavaScript function\r
+     * <li><code>jsStaticFunction_</code> for a JavaScript function that \r
+     *           is a property of the constructor\r
+     * <li><code>jsGet_</code> for a getter of a JavaScript property\r
+     * <li><code>jsSet_</code> for a setter of a JavaScript property\r
+     * <li><code>jsConstructor</code> for a JavaScript function that \r
+     *           is the constructor\r
+     * </ul><p>\r
+     *\r
+     * If the method's name begins with "jsFunction_", a JavaScript function \r
+     * is created with a name formed from the rest of the Java method name \r
+     * following "jsFunction_". So a Java method named "jsFunction_foo" will\r
+     * define a JavaScript method "foo". Calling this JavaScript function \r
+     * will cause the Java method to be called. The parameters of the method\r
+     * must be of number and types as defined by the FunctionObject class.\r
+     * The JavaScript function is then added as a property\r
+     * of the prototype. <p>\r
+     * \r
+     * If the method's name begins with "jsStaticFunction_", it is handled\r
+     * similarly except that the resulting JavaScript function is added as a \r
+     * property of the constructor object. The Java method must be static.\r
+     * \r
+     * If the method's name begins with "jsGet_" or "jsSet_", the method is\r
+     * considered to define a property. Accesses to the defined property\r
+     * will result in calls to these getter and setter methods. If no\r
+     * setter is defined, the property is defined as READONLY.<p>\r
+     *\r
+     * If the method's name is "jsConstructor", the method is\r
+     * considered to define the body of the constructor. Only one \r
+     * method of this name may be defined. \r
+     * If no method is found that can serve as constructor, a Java\r
+     * constructor will be selected to serve as the JavaScript\r
+     * constructor in the following manner. If the class has only one\r
+     * Java constructor, that constructor is used to define\r
+     * the JavaScript constructor. If the the class has two constructors,\r
+     * one must be the zero-argument constructor (otherwise an\r
+     * ClassDefinitionException would have already been thrown\r
+     * when the prototype was to be created). In this case\r
+     * the Java constructor with one or more parameters will be used\r
+     * to define the JavaScript constructor. If the class has three\r
+     * or more constructors, an ClassDefinitionException\r
+     * will be thrown.<p>\r
+     *\r
+     * Finally, if there is a method\r
+     * <pre>\r
+     * static void finishInit(Scriptable scope, FunctionObject constructor,\r
+     *                        Scriptable prototype)</pre>\r
+     *\r
+     * it will be called to finish any initialization. The <code>scope</code>\r
+     * argument will be passed, along with the newly created constructor and\r
+     * the newly created prototype.<p>\r
+     *\r
+     * @param scope The scope in which to define the constructor\r
+     * @param clazz The Java class to use to define the JavaScript objects\r
+     *              and properties\r
+     * @exception IllegalAccessException if access is not available\r
+     *            to a reflected class member\r
+     * @exception InstantiationException if unable to instantiate\r
+     *            the named class\r
+     * @exception InvocationTargetException if an exception is thrown\r
+     *            during execution of methods of the named class\r
+     * @exception ClassDefinitionException if an appropriate\r
+     *            constructor cannot be found to create the prototype\r
+     * @exception PropertyException if getter and setter\r
+     *            methods do not conform to the requirements of the\r
+     *            defineProperty method\r
+     * @see org.mozilla.javascript.Function\r
+     * @see org.mozilla.javascript.FunctionObject\r
+     * @see org.mozilla.javascript.ScriptableObject#READONLY\r
+     * @see org.mozilla.javascript.ScriptableObject#defineProperty\r
+     */\r
+    public static void defineClass(Scriptable scope, Class clazz)\r
+        throws IllegalAccessException, InstantiationException,\r
+               InvocationTargetException, ClassDefinitionException,\r
+               PropertyException\r
+    {\r
+        defineClass(scope, clazz, false);\r
+    }\r
+    \r
+    /**\r
+     * Defines JavaScript objects from a Java class, optionally \r
+     * allowing sealing.\r
+     *\r
+     * Similar to <code>defineClass(Scriptable scope, Class clazz)</code>\r
+     * except that sealing is allowed. An object that is sealed cannot have \r
+     * properties added or removed. Note that sealing is not allowed in\r
+     * the current ECMA/ISO language specification, but is likely for\r
+     * the next version.\r
+     * \r
+     * @param scope The scope in which to define the constructor\r
+     * @param clazz The Java class to use to define the JavaScript objects\r
+     *              and properties. The class must implement Scriptable.\r
+     * @param sealed whether or not to create sealed standard objects that\r
+     *               cannot be modified. \r
+     * @exception IllegalAccessException if access is not available\r
+     *            to a reflected class member\r
+     * @exception InstantiationException if unable to instantiate\r
+     *            the named class\r
+     * @exception InvocationTargetException if an exception is thrown\r
+     *            during execution of methods of the named class\r
+     * @exception ClassDefinitionException if an appropriate\r
+     *            constructor cannot be found to create the prototype\r
+     * @exception PropertyException if getter and setter\r
+     *            methods do not conform to the requirements of the\r
+     *            defineProperty method\r
+     * @since 1.4R3\r
+     */\r
+    public static void defineClass(Scriptable scope, Class clazz, \r
+                                   boolean sealed)\r
+        throws IllegalAccessException, InstantiationException,\r
+               InvocationTargetException, ClassDefinitionException,\r
+               PropertyException\r
+    {\r
+        Method[] methods = FunctionObject.getMethodList(clazz);\r
+        for (int i=0; i < methods.length; i++) {\r
+            Method method = methods[i];\r
+            if (!method.getName().equals("init"))\r
+                continue;\r
+            Class[] parmTypes = method.getParameterTypes();\r
+            if (parmTypes.length == 3 &&\r
+                parmTypes[0] == ContextClass &&\r
+                parmTypes[1] == ScriptRuntime.ScriptableClass &&\r
+                parmTypes[2] == Boolean.TYPE &&\r
+                Modifier.isStatic(method.getModifiers()))\r
+            {\r
+                Object args[] = { Context.getContext(), scope, \r
+                                  sealed ? Boolean.TRUE : Boolean.FALSE };\r
+                method.invoke(null, args);\r
+                return;\r
+            }\r
+            if (parmTypes.length == 1 &&\r
+                parmTypes[0] == ScriptRuntime.ScriptableClass &&\r
+                Modifier.isStatic(method.getModifiers()))\r
+            {\r
+                Object args[] = { scope };\r
+                method.invoke(null, args);\r
+                return;\r
+            }\r
+            \r
+        }\r
+\r
+        // If we got here, there isn't an "init" method with the right\r
+        // parameter types.\r
+        Hashtable exclusionList = getExclusionList();\r
+\r
+        Constructor[] ctors = clazz.getConstructors();\r
+        Constructor protoCtor = null;\r
+        for (int i=0; i < ctors.length; i++) {\r
+            if (ctors[i].getParameterTypes().length == 0) {\r
+                protoCtor = ctors[i];\r
+                break;\r
+            }\r
+        }\r
+        if (protoCtor == null) {\r
+            throw new ClassDefinitionException(\r
+                    Context.getMessage1("msg.zero.arg.ctor", clazz.getName()));\r
+        }\r
+\r
+        Scriptable proto = (Scriptable) \r
+                        protoCtor.newInstance(ScriptRuntime.emptyArgs);\r
+        proto.setPrototype(getObjectPrototype(scope));\r
+        String className = proto.getClassName();\r
+\r
+        // Find out whether there are any methods that begin with\r
+        // "js". If so, then only methods that begin with special\r
+        // prefixes will be defined as JavaScript entities.\r
+        // The prefixes "js_" and "jsProperty_" are deprecated.\r
+        final String genericPrefix = "js_";\r
+        final String functionPrefix = "jsFunction_";\r
+        final String staticFunctionPrefix = "jsStaticFunction_";\r
+        final String propertyPrefix = "jsProperty_";\r
+        final String getterPrefix = "jsGet_";\r
+        final String setterPrefix = "jsSet_";\r
+        final String ctorName = "jsConstructor";\r
+\r
+        boolean hasPrefix = false;\r
+        Method[] ctorMeths = FunctionObject.findMethods(clazz, ctorName);\r
+        Member ctorMember = null;\r
+        if (ctorMeths != null) {\r
+            if (ctorMeths.length > 1) {\r
+                throw new ClassDefinitionException(\r
+                    Context.getMessage2("msg.multiple.ctors", \r
+                                        ctorMeths[0], ctorMeths[1]));\r
+            }\r
+            ctorMember = ctorMeths[0];\r
+            hasPrefix = true;\r
+        }\r
+\r
+        // Deprecated: look for functions with the same name as the class\r
+        // and consider them constructors.\r
+        for (int i=0; i < methods.length; i++) {\r
+            String name = methods[i].getName();\r
+            String prefix = null;\r
+            if (!name.startsWith("js")) // common start to all prefixes\r
+                prefix = null;\r
+            else if (name.startsWith(genericPrefix))\r
+                prefix = genericPrefix;\r
+            else if (name.startsWith(functionPrefix))\r
+                prefix = functionPrefix;\r
+            else if (name.startsWith(staticFunctionPrefix))\r
+                prefix = staticFunctionPrefix;\r
+            else if (name.startsWith(propertyPrefix))\r
+                prefix = propertyPrefix;\r
+            else if (name.startsWith(getterPrefix))\r
+                prefix = getterPrefix;\r
+            else if (name.startsWith(setterPrefix))\r
+                prefix = setterPrefix;\r
+            if (prefix != null) {\r
+                hasPrefix = true;\r
+                name = name.substring(prefix.length());\r
+            }\r
+            if (name.equals(className)) {\r
+                if (ctorMember != null) {\r
+                    throw new ClassDefinitionException(\r
+                        Context.getMessage2("msg.multiple.ctors", \r
+                                            ctorMember, methods[i]));\r
+                }\r
+                ctorMember = methods[i];\r
+            }\r
+        }\r
+\r
+        if (ctorMember == null) {\r
+            if (ctors.length == 1) {\r
+                ctorMember = ctors[0];\r
+            } else if (ctors.length == 2) {\r
+                if (ctors[0].getParameterTypes().length == 0)\r
+                    ctorMember = ctors[1];\r
+                else if (ctors[1].getParameterTypes().length == 0)\r
+                    ctorMember = ctors[0];\r
+            }\r
+            if (ctorMember == null) {\r
+                throw new ClassDefinitionException(\r
+                    Context.getMessage1("msg.ctor.multiple.parms",\r
+                                        clazz.getName()));\r
+            }\r
+        }\r
+\r
+        FunctionObject ctor = new FunctionObject(className, ctorMember, scope);\r
+        if (ctor.isVarArgsMethod()) {\r
+            throw Context.reportRuntimeError1\r
+                ("msg.varargs.ctor", ctorMember.getName());\r
+        }\r
+        ctor.addAsConstructor(scope, proto);\r
+\r
+        if (!hasPrefix && exclusionList == null)\r
+            exclusionList = getExclusionList();\r
+        Method finishInit = null;\r
+        for (int i=0; i < methods.length; i++) {\r
+            if (!hasPrefix && methods[i].getDeclaringClass() != clazz)\r
+                continue;\r
+            String name = methods[i].getName();\r
+            if (name.equals("finishInit")) {\r
+                Class[] parmTypes = methods[i].getParameterTypes();\r
+                if (parmTypes.length == 3 &&\r
+                    parmTypes[0] == ScriptRuntime.ScriptableClass &&\r
+                    parmTypes[1] == FunctionObject.class &&\r
+                    parmTypes[2] == ScriptRuntime.ScriptableClass &&\r
+                    Modifier.isStatic(methods[i].getModifiers()))\r
+                {\r
+                    finishInit = methods[i];\r
+                    continue;\r
+                }\r
+            }\r
+            // ignore any compiler generated methods.\r
+            if (name.indexOf('$') != -1)\r
+                continue;\r
+            if (name.equals(ctorName))\r
+                continue;\r
+            String prefix = null;\r
+            if (hasPrefix) {\r
+                if (name.startsWith(genericPrefix)) {\r
+                    prefix = genericPrefix;\r
+                } else if (name.startsWith(functionPrefix)) {\r
+                    prefix = functionPrefix;\r
+                } else if (name.startsWith(staticFunctionPrefix)) {\r
+                    prefix = staticFunctionPrefix;\r
+                    if (!Modifier.isStatic(methods[i].getModifiers())) {\r
+                        throw new ClassDefinitionException(\r
+                            "jsStaticFunction must be used with static method.");\r
+                    }\r
+                } else if (name.startsWith(propertyPrefix)) {\r
+                    prefix = propertyPrefix;\r
+                } else if (name.startsWith(getterPrefix)) {\r
+                    prefix = getterPrefix;\r
+                } else if (name.startsWith(setterPrefix)) {\r
+                    prefix = setterPrefix;\r
+                } else {\r
+                    continue;\r
+                }\r
+                name = name.substring(prefix.length());\r
+            } else if (exclusionList.get(name) != null)\r
+                continue;\r
+            if (methods[i] == ctorMember) {\r
+                continue;\r
+            }\r
+            if (prefix != null && prefix.equals(setterPrefix))\r
+                continue;   // deal with set when we see get\r
+            if (prefix != null && prefix.equals(getterPrefix)) {\r
+                if (!(proto instanceof ScriptableObject)) {\r
+                    throw PropertyException.withMessage2\r
+                        ("msg.extend.scriptable",                                                         proto.getClass().toString(), name);\r
+                }\r
+                Method[] setter = FunctionObject.findMethods(\r
+                                    clazz,\r
+                                    setterPrefix + name);\r
+                if (setter != null && setter.length != 1) {\r
+                    throw PropertyException.withMessage2\r
+                        ("msg.no.overload", name, clazz.getName());\r
+                }\r
+                int attr = ScriptableObject.PERMANENT |\r
+                           ScriptableObject.DONTENUM  |\r
+                           (setter != null ? 0\r
+                                           : ScriptableObject.READONLY);\r
+                Method m = setter == null ? null : setter[0];\r
+                ((ScriptableObject) proto).defineProperty(name, null,\r
+                                                          methods[i], m,\r
+                                                          attr);\r
+                continue;\r
+            }\r
+            if ((name.startsWith("get") || name.startsWith("set")) &&\r
+                name.length() > 3 &&\r
+                !(hasPrefix && (prefix.equals(functionPrefix) ||\r
+                                prefix.equals(staticFunctionPrefix))))\r
+            {\r
+                if (!(proto instanceof ScriptableObject)) {\r
+                    throw PropertyException.withMessage2\r
+                        ("msg.extend.scriptable",\r
+                         proto.getClass().toString(), name);\r
+                }\r
+                if (name.startsWith("set"))\r
+                    continue;   // deal with set when we see get\r
+                StringBuffer buf = new StringBuffer();\r
+                char c = name.charAt(3);\r
+                buf.append(Character.toLowerCase(c));\r
+                if (name.length() > 4)\r
+                    buf.append(name.substring(4));\r
+                String propertyName = buf.toString();\r
+                buf.setCharAt(0, c);\r
+                buf.insert(0, "set");\r
+                String setterName = buf.toString();\r
+                Method[] setter = FunctionObject.findMethods(\r
+                                    clazz,\r
+                                    hasPrefix ? genericPrefix + setterName\r
+                                              : setterName);\r
+                if (setter != null && setter.length != 1) {\r
+                    throw PropertyException.withMessage2\r
+                        ("msg.no.overload", name, clazz.getName());\r
+                }\r
+                if (setter == null && hasPrefix)\r
+                    setter = FunctionObject.findMethods(\r
+                                clazz,\r
+                                propertyPrefix + setterName);\r
+                int attr = ScriptableObject.PERMANENT |\r
+                           ScriptableObject.DONTENUM  |\r
+                           (setter != null ? 0\r
+                                           : ScriptableObject.READONLY);\r
+                Method m = setter == null ? null : setter[0];\r
+                ((ScriptableObject) proto).defineProperty(propertyName, null,\r
+                                                          methods[i], m,\r
+                                                          attr);\r
+                continue;\r
+            }\r
+            FunctionObject f = new FunctionObject(name, methods[i], proto);\r
+            if (f.isVarArgsConstructor()) {\r
+                throw Context.reportRuntimeError1\r
+                    ("msg.varargs.fun", ctorMember.getName());\r
+            }\r
+            Scriptable dest = prefix == staticFunctionPrefix\r
+                              ? ctor\r
+                              : proto;\r
+            defineProperty(dest, name, f, DONTENUM);\r
+            if (sealed) {\r
+                f.sealObject();\r
+                f.addPropertyAttribute(READONLY);\r
+            }\r
+        }\r
+\r
+        if (finishInit != null) {\r
+            // call user code to complete the initialization\r
+            Object[] finishArgs = { scope, ctor, proto };\r
+            finishInit.invoke(null, finishArgs);\r
+        }\r
+        \r
+        if (sealed) {\r
+            ctor.sealObject();\r
+            ctor.addPropertyAttribute(READONLY);\r
+            if (proto instanceof ScriptableObject) {\r
+                ((ScriptableObject) proto).sealObject();\r
+                ((ScriptableObject) proto).addPropertyAttribute(READONLY);\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Define a JavaScript property.\r
+     *\r
+     * Creates the property with an initial value and sets its attributes.\r
+     *\r
+     * @param propertyName the name of the property to define.\r
+     * @param value the initial value of the property\r
+     * @param attributes the attributes of the JavaScript property\r
+     * @see org.mozilla.javascript.Scriptable#put\r
+     */\r
+    public void defineProperty(String propertyName, Object value,\r
+                               int attributes)\r
+    {\r
+        put(propertyName, this, value);\r
+        try {\r
+            setAttributes(propertyName, this, attributes);\r
+        }\r
+        catch (PropertyException e) {\r
+            throw new RuntimeException("Cannot create property");\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Utility method to add properties to arbitrary Scriptable object.\r
+     * If destination is instance of ScriptableObject, calls \r
+     * defineProperty there, otherwise calls put in destination \r
+     * ignoring attributes\r
+     */\r
+    public static void defineProperty(Scriptable destination, \r
+                                      String propertyName, Object value,\r
+                                      int attributes)\r
+    {\r
+        if (destination instanceof ScriptableObject) {\r
+            ScriptableObject obj = (ScriptableObject)destination;\r
+            obj.defineProperty(propertyName, value, attributes);\r
+        }\r
+        else {\r
+            destination.put(propertyName, destination, value);\r
+        }\r
+    }\r
\r
+    /**\r
+     * Define a JavaScript property with getter and setter side effects.\r
+     *\r
+     * If the setter is not found, the attribute READONLY is added to\r
+     * the given attributes. <p>\r
+     *\r
+     * The getter must be a method with zero parameters, and the setter, if\r
+     * found, must be a method with one parameter.<p>\r
+     *\r
+     * @param propertyName the name of the property to define. This name\r
+     *                    also affects the name of the setter and getter\r
+     *                    to search for. If the propertyId is "foo", then\r
+     *                    <code>clazz</code> will be searched for "getFoo"\r
+     *                    and "setFoo" methods.\r
+     * @param clazz the Java class to search for the getter and setter\r
+     * @param attributes the attributes of the JavaScript property\r
+     * @exception PropertyException if multiple methods\r
+     *            are found for the getter or setter, or if the getter\r
+     *            or setter do not conform to the forms described in\r
+     *            <code>defineProperty(String, Object, Method, Method,\r
+     *            int)</code>\r
+     * @see org.mozilla.javascript.Scriptable#put\r
+     */\r
+    public void defineProperty(String propertyName, Class clazz,\r
+                               int attributes)\r
+        throws PropertyException\r
+    {\r
+        StringBuffer buf = new StringBuffer(propertyName);\r
+        buf.setCharAt(0, Character.toUpperCase(propertyName.charAt(0)));\r
+        String s = buf.toString();\r
+        Method[] getter = FunctionObject.findMethods(clazz, "get" + s);\r
+        Method[] setter = FunctionObject.findMethods(clazz, "set" + s);\r
+        if (setter == null)\r
+            attributes |= ScriptableObject.READONLY;\r
+        if (getter.length != 1 || (setter != null && setter.length != 1)) {\r
+            throw PropertyException.withMessage2\r
+                ("msg.no.overload", propertyName, clazz.getName());\r
+        }\r
+        defineProperty(propertyName, null, getter[0],\r
+                       setter == null ? null : setter[0], attributes);\r
+    }\r
+\r
+    /**\r
+     * Define a JavaScript property.\r
+     *\r
+     * Use this method only if you wish to define getters and setters for\r
+     * a given property in a ScriptableObject. To create a property without\r
+     * special getter or setter side effects, use\r
+     * <code>defineProperty(String,int)</code>.\r
+     *\r
+     * If <code>setter</code> is null, the attribute READONLY is added to\r
+     * the given attributes.<p>\r
+     *\r
+     * Several forms of getters or setters are allowed. In all cases the\r
+     * type of the value parameter can be any one of the following types: \r
+     * Object, String, boolean, Scriptable, byte, short, int, long, float,\r
+     * or double. The runtime will perform appropriate conversions based\r
+     * upon the type of the parameter (see description in FunctionObject).\r
+     * The first forms are nonstatic methods of the class referred to\r
+     * by 'this':\r
+     * <pre>\r
+     * Object getFoo();\r
+     * void setFoo(SomeType value);</pre>\r
+     * Next are static methods that may be of any class; the object whose\r
+     * property is being accessed is passed in as an extra argument:\r
+     * <pre>\r
+     * static Object getFoo(ScriptableObject obj);\r
+     * static void setFoo(ScriptableObject obj, SomeType value);</pre>\r
+     * Finally, it is possible to delegate to another object entirely using\r
+     * the <code>delegateTo</code> parameter. In this case the methods are\r
+     * nonstatic methods of the class delegated to, and the object whose\r
+     * property is being accessed is passed in as an extra argument:\r
+     * <pre>\r
+     * Object getFoo(ScriptableObject obj);\r
+     * void setFoo(ScriptableObject obj, SomeType value);</pre>\r
+     *\r
+     * @param propertyName the name of the property to define.\r
+     * @param delegateTo an object to call the getter and setter methods on,\r
+     *                   or null, depending on the form used above.\r
+     * @param getter the method to invoke to get the value of the property\r
+     * @param setter the method to invoke to set the value of the property\r
+     * @param attributes the attributes of the JavaScript property\r
+     * @exception PropertyException if the getter or setter\r
+     *            do not conform to the forms specified above\r
+     */\r
+    public void defineProperty(String propertyName, Object delegateTo,\r
+                               Method getter, Method setter, int attributes)\r
+        throws PropertyException\r
+    {\r
+        int flags = Slot.HAS_GETTER;\r
+        if (delegateTo == null && (Modifier.isStatic(getter.getModifiers())))\r
+            delegateTo = HAS_STATIC_ACCESSORS;\r
+        Class[] parmTypes = getter.getParameterTypes();\r
+        if (parmTypes.length != 0) {\r
+            if (parmTypes.length != 1 ||\r
+                parmTypes[0] != ScriptableObject.class)\r
+            {\r
+                throw PropertyException.withMessage1\r
+                    ("msg.bad.getter.parms", getter.toString());\r
+            }\r
+        } else if (delegateTo != null) {\r
+            throw PropertyException.withMessage1\r
+                ("msg.obj.getter.parms", getter.toString());\r
+        }\r
+        if (setter != null) {\r
+            flags |= Slot.HAS_SETTER;\r
+            if ((delegateTo == HAS_STATIC_ACCESSORS) !=\r
+                (Modifier.isStatic(setter.getModifiers())))\r
+            {\r
+                throw PropertyException.withMessage0("msg.getter.static");\r
+            }\r
+            parmTypes = setter.getParameterTypes();\r
+            if (parmTypes.length == 2) {\r
+                if (parmTypes[0] != ScriptableObject.class) {\r
+                    throw PropertyException.withMessage0("msg.setter2.parms");\r
+                }\r
+                if (delegateTo == null) {\r
+                    throw PropertyException.withMessage1\r
+                        ("msg.setter1.parms", setter.toString());\r
+                }\r
+            } else if (parmTypes.length == 1) {\r
+                if (delegateTo != null) {\r
+                    throw PropertyException.withMessage1\r
+                        ("msg.setter2.expected", setter.toString());\r
+                }\r
+            } else {\r
+                throw PropertyException.withMessage0("msg.setter.parms");\r
+            }\r
+        }\r
+        GetterSlot slot = (GetterSlot)getSlotToSet(propertyName,\r
+                                                   propertyName.hashCode(),\r
+                                                   true);\r
+        slot.delegateTo = delegateTo;\r
+        slot.getter = getter;\r
+        slot.setter = setter;\r
+        slot.setterReturnsValue = setter != null && setter.getReturnType() != Void.TYPE;\r
+        slot.value = null;\r
+        slot.attributes = (short) attributes;\r
+        slot.flags = (byte)flags;\r
+    }\r
+\r
+    /**\r
+     * Search for names in a class, adding the resulting methods\r
+     * as properties.\r
+     *\r
+     * <p> Uses reflection to find the methods of the given names. Then\r
+     * FunctionObjects are constructed from the methods found, and\r
+     * are added to this object as properties with the given names.\r
+     *\r
+     * @param names the names of the Methods to add as function properties\r
+     * @param clazz the class to search for the Methods\r
+     * @param attributes the attributes of the new properties\r
+     * @exception PropertyException if any of the names\r
+     *            has no corresponding method or more than one corresponding\r
+     *            method in the class\r
+     * @see org.mozilla.javascript.FunctionObject\r
+     */\r
+    public void defineFunctionProperties(String[] names, Class clazz,\r
+                                         int attributes)\r
+        throws PropertyException\r
+    {\r
+        for (int i=0; i < names.length; i++) {\r
+            String name = names[i];\r
+            Method[] m = FunctionObject.findMethods(clazz, name);\r
+            if (m == null) {\r
+                throw PropertyException.withMessage2\r
+                    ("msg.method.not.found", name, clazz.getName());\r
+            }\r
+            if (m.length > 1) {\r
+                throw PropertyException.withMessage2\r
+                    ("msg.no.overload", name, clazz.getName());\r
+            }\r
+            FunctionObject f = new FunctionObject(name, m[0], this);\r
+            defineProperty(name, f, attributes);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Get the Object.prototype property.\r
+     * See ECMA 15.2.4.\r
+     */\r
+    public static Scriptable getObjectPrototype(Scriptable scope) {\r
+        return getClassPrototype(scope, "Object");\r
+    }\r
+\r
+    /**\r
+     * Get the Function.prototype property.\r
+     * See ECMA 15.3.4.\r
+     */\r
+    public static Scriptable getFunctionPrototype(Scriptable scope) {\r
+        return getClassPrototype(scope, "Function");\r
+    }\r
+\r
+    /**\r
+     * Get the prototype for the named class.\r
+     *\r
+     * For example, <code>getClassPrototype(s, "Date")</code> will first\r
+     * walk up the parent chain to find the outermost scope, then will\r
+     * search that scope for the Date constructor, and then will\r
+     * return Date.prototype. If any of the lookups fail, or\r
+     * the prototype is not a JavaScript object, then null will\r
+     * be returned.\r
+     *\r
+     * @param scope an object in the scope chain\r
+     * @param className the name of the constructor\r
+     * @return the prototype for the named class, or null if it\r
+     *         cannot be found.\r
+     */\r
+    public static Scriptable getClassPrototype(Scriptable scope,\r
+                                               String className)\r
+    {\r
+        scope = getTopLevelScope(scope);\r
+        Object ctor = ScriptRuntime.getTopLevelProp(scope, className);\r
+        if (ctor == NOT_FOUND || !(ctor instanceof Scriptable))\r
+            return null;\r
+        Scriptable ctorObj = (Scriptable) ctor;\r
+        if (!ctorObj.has("prototype", ctorObj))\r
+            return null;\r
+        Object proto = ctorObj.get("prototype", ctorObj);\r
+        if (!(proto instanceof Scriptable))\r
+            return null;\r
+        return (Scriptable) proto;\r
+    }\r
+\r
+    /**\r
+     * Get the global scope.\r
+     *\r
+     * <p>Walks the parent scope chain to find an object with a null\r
+     * parent scope (the global object).\r
+     *\r
+     * @param obj a JavaScript object\r
+     * @return the corresponding global scope\r
+     */\r
+    public static Scriptable getTopLevelScope(Scriptable obj) {\r
+        Scriptable next = obj;\r
+        do {\r
+            obj = next;\r
+            next = obj.getParentScope();\r
+        } while (next != null);\r
+        return obj;\r
+    }\r
+    \r
+    /**\r
+     * Seal this object.\r
+     * \r
+     * A sealed object may not have properties added or removed. Once\r
+     * an object is sealed it may not be unsealed.\r
+     * \r
+     * @since 1.4R3\r
+     */\r
+    public void sealObject() {\r
+        count = -1;\r
+    }\r
+    \r
+    /**\r
+     * Return true if this object is sealed.\r
+     * \r
+     * It is an error to attempt to add or remove properties to \r
+     * a sealed object.\r
+     * \r
+     * @return true if sealed, false otherwise.\r
+     * @since 1.4R3\r
+     */\r
+    public boolean isSealed() {\r
+        return count == -1;\r
+    }\r
+\r
+    /**\r
+     * Gets a named property from an object or any object in its prototype chain.\r
+     * <p>\r
+     * Searches the prototype chain for a property named <code>name</code>.\r
+     * <p>\r
+     * @param obj a JavaScript object \r
+     * @param name a property name \r
+     * @return the value of a property with name <code>name</code> found in \r
+     *         <code>obj</code> or any object in its prototype chain, or \r
+     *         <code>Scriptable.NOT_FOUND</code> if not found\r
+     * @since 1.5R2\r
+     */\r
+    public static Object getProperty(Scriptable obj, String name) {\r
+        Scriptable start = obj;\r
+        Object result;\r
+        do {\r
+            result = obj.get(name, start);\r
+            if (result != Scriptable.NOT_FOUND)\r
+                break;\r
+            obj = obj.getPrototype();\r
+        } while (obj != null);\r
+        return result;\r
+    }\r
+    \r
+    /**\r
+     * Gets an indexed property from an object or any object in its prototype chain.\r
+     * <p>\r
+     * Searches the prototype chain for a property with integral index \r
+     * <code>index</code>. Note that if you wish to look for properties with numerical\r
+     * but non-integral indicies, you should use getProperty(Scriptable,String) with\r
+     * the string value of the index.\r
+     * <p>\r
+     * @param obj a JavaScript object \r
+     * @param index an integral index \r
+     * @return the value of a property with index <code>index</code> found in \r
+     *         <code>obj</code> or any object in its prototype chain, or \r
+     *         <code>Scriptable.NOT_FOUND</code> if not found\r
+     * @since 1.5R2\r
+     */\r
+    public static Object getProperty(Scriptable obj, int index) {\r
+        Scriptable start = obj;\r
+        Object result;\r
+        do {\r
+            result = obj.get(index, start);\r
+            if (result != Scriptable.NOT_FOUND)\r
+                break;\r
+            obj = obj.getPrototype();\r
+        } while (obj != null);\r
+        return result;\r
+    }\r
+    \r
+    /**\r
+     * Returns whether a named property is defined in an object or any object \r
+     * in its prototype chain.\r
+     * <p>\r
+     * Searches the prototype chain for a property named <code>name</code>.\r
+     * <p>\r
+     * @param obj a JavaScript object \r
+     * @param name a property name \r
+     * @return the true if property was found\r
+     * @since 1.5R2\r
+     */\r
+    public static boolean hasProperty(Scriptable obj, String name) {\r
+        Scriptable start = obj;\r
+        do {\r
+            if (obj.has(name, start))\r
+                return true;\r
+            obj = obj.getPrototype();\r
+        } while (obj != null);\r
+        return false;\r
+    }\r
+    \r
+    /**\r
+     * Returns whether an indexed property is defined in an object or any object \r
+     * in its prototype chain.\r
+     * <p>\r
+     * Searches the prototype chain for a property with index <code>index</code>.\r
+     * <p>\r
+     * @param obj a JavaScript object \r
+     * @param index a property index \r
+     * @return the true if property was found\r
+     * @since 1.5R2\r
+     */\r
+    public static boolean hasProperty(Scriptable obj, int index) {\r
+        Scriptable start = obj;\r
+        do {\r
+            if (obj.has(index, start))\r
+                return true;\r
+            obj = obj.getPrototype();\r
+        } while (obj != null);\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Puts a named property in an object or in an object in its prototype chain.\r
+     * <p>\r
+     * Seaches for the named property in the prototype chain. If it is found,\r
+     * the value of the property is changed. If it is not found, a new\r
+     * property is added in <code>obj</code>.\r
+     * @param obj a JavaScript object \r
+     * @param name a property name\r
+     * @param value any JavaScript value accepted by Scriptable.put \r
+     * @since 1.5R2\r
+     */\r
+    public static void putProperty(Scriptable obj, String name, Object value) {\r
+        Scriptable base = getBase(obj, name);\r
+        if (base == null)\r
+            base = obj;\r
+        base.put(name, obj, value);\r
+    }\r
+\r
+    /**\r
+     * Puts an indexed property in an object or in an object in its prototype chain.\r
+     * <p>\r
+     * Seaches for the indexed property in the prototype chain. If it is found,\r
+     * the value of the property is changed. If it is not found, a new\r
+     * property is added in <code>obj</code>.\r
+     * @param obj a JavaScript object \r
+     * @param index a property index\r
+     * @param value any JavaScript value accepted by Scriptable.put \r
+     * @since 1.5R2\r
+     */\r
+    public static void putProperty(Scriptable obj, int index, Object value) {\r
+        Scriptable base = getBase(obj, index);\r
+        if (base == null)\r
+            base = obj;\r
+        base.put(index, obj, value);\r
+    }\r
+\r
+    /**\r
+     * Removes the property from an object or its prototype chain.\r
+     * <p>\r
+     * Searches for a property with <code>name</code> in obj or\r
+     * its prototype chain. If it is found, the object's delete\r
+     * method is called. \r
+     * @param obj a JavaScript object\r
+     * @param name a property name\r
+     * @return true if the property doesn't exist or was successfully removed\r
+     * @since 1.5R2\r
+     */\r
+    public static boolean deleteProperty(Scriptable obj, String name) {\r
+        Scriptable base = getBase(obj, name);\r
+        if (base == null)\r
+            return true;\r
+        base.delete(name);\r
+        return base.get(name, obj) == NOT_FOUND;\r
+    }\r
+                \r
+    /**\r
+     * Removes the property from an object or its prototype chain.\r
+     * <p>\r
+     * Searches for a property with <code>index</code> in obj or\r
+     * its prototype chain. If it is found, the object's delete\r
+     * method is called. \r
+     * @param obj a JavaScript object\r
+     * @param index a property index\r
+     * @return true if the property doesn't exist or was successfully removed\r
+     * @since 1.5R2\r
+     */\r
+    public static boolean deleteProperty(Scriptable obj, int index) {\r
+        Scriptable base = getBase(obj, index);\r
+        if (base == null)\r
+            return true;\r
+        base.delete(index);\r
+        return base.get(index, obj) == NOT_FOUND;\r
+    }\r
+    \r
+    /**\r
+     * Returns an array of all ids from an object and its prototypes.\r
+     * <p>\r
+     * @param obj a JavaScript object\r
+     * @return an array of all ids from all object in the prototype chain.\r
+     *         If a given id occurs multiple times in the prototype chain,\r
+     *         it will occur only once in this list.\r
+     * @since 1.5R2\r
+     */\r
+    public static Object[] getPropertyIds(Scriptable obj) {\r
+        Hashtable h = new Hashtable();  // JDK1.2: use HashSet\r
+        while (obj != null) {\r
+            Object[] ids = obj.getIds();\r
+            for (int i=0; i < ids.length; i++) {\r
+                h.put(ids[i], ids[i]);\r
+            }\r
+            obj = (Scriptable)obj.getPrototype();\r
+        }\r
+        Object[] result = new Object[h.size()];\r
+        java.util.Enumeration e = h.elements();\r
+        int n = 0;\r
+        while (e.hasMoreElements()) {\r
+            result[n++] = e.nextElement();\r
+        }\r
+        return result;\r
+    }\r
+    \r
+    /**\r
+     * Call a method of an object.\r
+     * <p>\r
+     * @param obj the JavaScript object\r
+     * @param methodName the name of the function property\r
+     * @param args the arguments for the call\r
+     * @exception JavaScriptException thrown if there were errors in the call\r
+     */\r
+    public static Object callMethod(Scriptable obj, String methodName, \r
+                                    Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        Context cx = Context.enter();\r
+        try {\r
+            Object fun = getProperty(obj, methodName);\r
+            if (fun == NOT_FOUND)\r
+                fun = Undefined.instance;\r
+            return ScriptRuntime.call(cx, fun, obj, args, getTopLevelScope(obj));\r
+        } finally {\r
+          Context.exit();\r
+        }\r
+    }\r
+                \r
+    private static Scriptable getBase(Scriptable obj, String s) {\r
+        Scriptable m = obj;\r
+        while (m != null) {\r
+            if (m.has(s, obj))\r
+                return m;\r
+            m = m.getPrototype();\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private static Scriptable getBase(Scriptable obj, int index) {\r
+        Scriptable m = obj;\r
+        while (m != null) {\r
+            if (m.has(index, obj))\r
+                return m;\r
+            m = m.getPrototype();\r
+        }\r
+        return null;\r
+    }\r
+    \r
+    /**\r
+     * Adds a property attribute to all properties.\r
+     */\r
+    synchronized void addPropertyAttribute(int attribute) {\r
+        if (slots == null)\r
+            return;\r
+        for (int i=0; i < slots.length; i++) {\r
+            Slot slot = slots[i];\r
+            if (slot == null || slot == REMOVED)\r
+                continue;\r
+            if ((slot.flags & slot.HAS_SETTER) != 0 && attribute == READONLY)\r
+                continue;\r
+            slot.attributes |= attribute;\r
+        }\r
+    }\r
+    \r
+    private Slot getSlot(String id, int index, boolean shouldDelete) {\r
+        Slot[] slots = this.slots;\r
+        if (slots == null)\r
+            return null;\r
+        int start = (index & 0x7fffffff) % slots.length;\r
+        int i = start;\r
+        do {\r
+            Slot slot = slots[i];\r
+            if (slot == null)\r
+                return null;\r
+            if (slot != REMOVED && slot.intKey == index && \r
+                (slot.stringKey == id || (id != null && \r
+                                          id.equals(slot.stringKey))))\r
+            {\r
+                if (shouldDelete) {\r
+                    if ((slot.attributes & PERMANENT) == 0) {\r
+                        // Mark the slot as removed to handle a case when\r
+                        // another thread manages to put just removed slot\r
+                        // into lastAccess cache.\r
+                        slot.wasDeleted = (byte)1;\r
+                        slots[i] = REMOVED;\r
+                        count--;\r
+                        if (slot == lastAccess)\r
+                            lastAccess = REMOVED;\r
+                    }\r
+                }\r
+                return slot;\r
+            }\r
+            if (++i == slots.length)\r
+                i = 0;\r
+        } while (i != start);\r
+        return null;\r
+    }\r
+\r
+    private Slot getSlotToSet(String id, int index, boolean getterSlot) {\r
+        if (slots == null)\r
+            slots = new Slot[5];\r
+        int start = (index & 0x7fffffff) % slots.length;\r
+        boolean sawRemoved = false;\r
+        int i = start;\r
+        do {\r
+            Slot slot = slots[i];\r
+            if (slot == null) {\r
+                return addSlot(id, index, getterSlot);\r
+            }\r
+            if (slot == REMOVED) {\r
+                sawRemoved = true;\r
+            } else if (slot.intKey == index && \r
+                       (slot.stringKey == id || \r
+                        (id != null && id.equals(slot.stringKey))))\r
+            {\r
+                return slot;\r
+            }\r
+            if (++i == slots.length)\r
+                i = 0;\r
+        } while (i != start);\r
+        if (sawRemoved) {\r
+            // Table could be full, but with some REMOVED elements. \r
+            // Call to addSlot will use a slot currently taken by \r
+            // a REMOVED.\r
+            return addSlot(id, index, getterSlot);\r
+        }\r
+        throw new RuntimeException("Hashtable internal error");\r
+    }\r
+\r
+    /**\r
+     * Add a new slot to the hash table.\r
+     *\r
+     * This method must be synchronized since it is altering the hash\r
+     * table itself. Note that we search again for the slot to set\r
+     * since another thread could have added the given property or\r
+     * caused the table to grow while this thread was searching.\r
+     */\r
+    private synchronized Slot addSlot(String id, int index, boolean getterSlot)\r
+    {\r
+        if (count == -1)\r
+            throw Context.reportRuntimeError0("msg.add.sealed");\r
+        int start = (index & 0x7fffffff) % slots.length;\r
+        int i = start;\r
+        do {\r
+            Slot slot = slots[i];\r
+            if (slot == null || slot == REMOVED) {\r
+                if ((4 * (count+1)) > (3 * slots.length)) {\r
+                    grow();\r
+                    return getSlotToSet(id, index, getterSlot);\r
+                }\r
+                slot = getterSlot ? new GetterSlot() : new Slot();\r
+                slot.stringKey = id;\r
+                slot.intKey = index;\r
+                slots[i] = slot;\r
+                count++;\r
+                return slot;\r
+            }\r
+            if (slot.intKey == index && \r
+                (slot.stringKey == id || (id != null && \r
+                                          id.equals(slot.stringKey)))) \r
+            {\r
+                return slot;\r
+            }\r
+            if (++i == slots.length)\r
+                i = 0;\r
+        } while (i != start);\r
+        throw new RuntimeException("Hashtable internal error");\r
+    }\r
+\r
+    /**\r
+     * Remove a slot from the hash table.\r
+     *\r
+     * This method must be synchronized since it is altering the hash\r
+     * table itself. We might be able to optimize this more, but\r
+     * deletes are not common.\r
+     */\r
+    private synchronized void removeSlot(String name, int index) {\r
+        if (count == -1)\r
+            throw Context.reportRuntimeError0("msg.remove.sealed");\r
+        getSlot(name, index, true);\r
+    }\r
+\r
+    /**\r
+     * Grow the hash table to accommodate new entries.\r
+     *\r
+     * Note that by assigning the new array back at the end we\r
+     * can continue reading the array from other threads.\r
+     */\r
+    private synchronized void grow() {\r
+        Slot[] newSlots = new Slot[slots.length*2 + 1];\r
+        for (int j=slots.length-1; j >= 0 ; j--) {\r
+            Slot slot = slots[j];\r
+            if (slot == null || slot == REMOVED)\r
+                continue;\r
+            int k = (slot.intKey & 0x7fffffff) % newSlots.length;\r
+            while (newSlots[k] != null)\r
+                if (++k == newSlots.length)\r
+                    k = 0;\r
+            // The end of the "synchronized" statement will cause the memory\r
+            // writes to be propagated on a multiprocessor machine. We want\r
+            // to make sure that the new table is prepared to be read.\r
+            // XXX causes the 'this' pointer to be null in calling stack frames\r
+            // on the MS JVM\r
+            //synchronized (slot) { }\r
+            newSlots[k] = slot;\r
+        }\r
+        slots = newSlots;\r
+    }\r
+\r
+    private static Hashtable getExclusionList() {\r
+        if (exclusionList != null)\r
+            return exclusionList;\r
+        Hashtable result = new Hashtable(17);\r
+        Method[] methods = ScriptRuntime.FunctionClass.getMethods();\r
+        for (int i=0; i < methods.length; i++) {\r
+            result.put(methods[i].getName(), Boolean.TRUE);\r
+        }\r
+        exclusionList = result;\r
+        return result;\r
+    }\r
+        \r
+    Object[] getIds(boolean getAll) {\r
+        Slot[] s = slots;\r
+        Object[] a = ScriptRuntime.emptyArgs;\r
+        if (s == null)\r
+            return a;\r
+        int c = 0;\r
+        for (int i=0; i < s.length; i++) {\r
+            Slot slot = s[i];\r
+            if (slot == null || slot == REMOVED)\r
+                continue;\r
+            if (getAll || (slot.attributes & DONTENUM) == 0) {\r
+                if (c == 0)\r
+                    a = new Object[s.length - i];\r
+                a[c++] = slot.stringKey != null\r
+                             ? (Object) slot.stringKey\r
+                             : new Integer(slot.intKey);\r
+            }\r
+        }\r
+        if (c == a.length)\r
+            return a;\r
+        Object[] result = new Object[c];\r
+        System.arraycopy(a, 0, result, 0, c);\r
+        return result;\r
+    }\r
+\r
+    \r
+    /**\r
+     * The prototype of this object.\r
+     */\r
+    protected Scriptable prototype;\r
+    \r
+    /**\r
+     * The parent scope of this object.\r
+     */\r
+    protected Scriptable parent;\r
+\r
+    private static final Object HAS_STATIC_ACCESSORS = Void.TYPE;\r
+    private static final Slot REMOVED = new Slot();\r
+    private static Hashtable exclusionList = null;\r
+    \r
+    private Slot[] slots;\r
+    private int count;\r
+\r
+    // cache; may be removed for smaller memory footprint\r
+    private Slot lastAccess = REMOVED;\r
+\r
+    private static class Slot {\r
+        static final int HAS_GETTER  = 0x01;\r
+        static final int HAS_SETTER  = 0x02;\r
+        \r
+        int intKey;\r
+        String stringKey;\r
+        Object value;\r
+        short attributes;\r
+        byte flags;\r
+        byte wasDeleted;\r
+    }\r
+\r
+    private static class GetterSlot extends Slot {\r
+        Object delegateTo;  // OPT: merge with "value"\r
+        Method getter;\r
+        Method setter;\r
+        boolean setterReturnsValue;\r
+    }\r
+\r
+    private static final Class ContextClass = Context.class;\r
+}\r
diff --git a/src/org/mozilla/javascript/SecuritySupport.java b/src/org/mozilla/javascript/SecuritySupport.java
new file mode 100644 (file)
index 0000000..2c11d1b
--- /dev/null
@@ -0,0 +1,148 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This class describes the support needed to implement security.\r
+ * <p>\r
+ * Three main pieces of functionality are required to implement\r
+ * security for JavaScript. First, it must be possible to define\r
+ * classes with an associated security context. (This security \r
+ * context may be any object that has meaning to an embedding;\r
+ * for a client-side JavaScript embedding this would typically\r
+ * be an origin URL and/or a digital certificate.) Next it \r
+ * must be possible to get the current class context so that\r
+ * the implementation can determine securely which class is\r
+ * requesting a privileged action. And finally, it must be \r
+ * possible to map a class back into a security context so that\r
+ * additional classes may be defined with that security context.\r
+ * <p>\r
+ * These three pieces of functionality are encapsulated in the\r
+ * SecuritySupport class.\r
+ * <p>\r
+ * Additionally, an embedding may provide filtering on the \r
+ * Java classes that are visible to scripts through the \r
+ * <code>visibleToScripts</code> method.\r
+ *\r
+ * @see org.mozilla.javascript.Context\r
+ * @see java.lang.ClassLoader\r
+ * @since 1.4 Release 2\r
+ * @author Norris Boyd\r
+ */\r
+public interface SecuritySupport {\r
+\r
+    /**\r
+     * Define and load a Java class.\r
+     * <p>\r
+     * In embeddings that care about security, the securityDomain \r
+     * must be associated with the defined class such that a call to\r
+     * <code>getSecurityDomain</code> with that class will return this security\r
+     * context.\r
+     * <p>\r
+     * @param name the name of the class\r
+     * @param data the bytecode of the class\r
+     * @param securityDomain some object specifying the security\r
+     *        context of the code that is defining this class.\r
+     *        Embeddings that don't care about security may allow\r
+     *        null here. This value propagated from the values passed\r
+     *        into methods of Context that evaluate scripts.\r
+     */\r
+    public Class defineClass(String name, byte[] data, \r
+                             Object securityDomain);\r
+        \r
+    /**\r
+     * Get the current class Context.\r
+     * <p> \r
+     * This functionality is supplied by SecurityManager.getClassContext,\r
+     * but only one SecurityManager may be instantiated in a single JVM\r
+     * at any one time. So implementations that care about security must\r
+     * provide access to this functionality through this interface.\r
+     * <p>\r
+     * Note that the 0th entry of the returned array should be the class\r
+     * of the caller of this method. So if this method is implemented by\r
+     * calling SecurityManager.getClassContext, this method must allocate\r
+     * a new, shorter array to return.\r
+     */\r
+    public Class[] getClassContext();\r
+    \r
+    /**\r
+     * Return the security context associated with the given class. \r
+     * <p>\r
+     * If <code>cl</code> is a class defined through a call to \r
+     * SecuritySupport.defineClass, then return the security \r
+     * context from that call. Otherwise return null.\r
+     * @param cl a class potentially defined by defineClass\r
+     * @return a security context object previously passed to defineClass\r
+     */\r
+    public Object getSecurityDomain(Class cl);\r
+    \r
+    /**\r
+     * Return true iff the Java class with the given name should be exposed\r
+     * to scripts.\r
+     * <p>\r
+     * An embedding may filter which Java classes are exposed through \r
+     * LiveConnect to JavaScript scripts.\r
+     * <p>\r
+     * Due to the fact that there is no package reflection in Java,\r
+     * this method will also be called with package names. There\r
+     * is no way for Rhino to tell if "Packages.a.b" is a package name \r
+     * or a class that doesn't exist. What Rhino does is attempt\r
+     * to load each segment of "Packages.a.b.c": It first attempts to \r
+     * load class "a", then attempts to load class "a.b", then\r
+     * finally attempts to load class "a.b.c". On a Rhino installation \r
+     * without any SecuritySupport set, and without any of the\r
+     * above classes, the expression "Packages.a.b.c" will result in \r
+     * a [JavaPackage a.b.c] and not an error.\r
+     * <p>\r
+     * With SecuritySupport supplied, Rhino will first call \r
+     * visibleToScripts before attempting to look up the class name. If\r
+     * visibleToScripts returns false, the class name lookup is not \r
+     * performed and subsequent Rhino execution assumes the class is\r
+     * not present. So for "java.lang.System.out.println" the lookup \r
+     * of "java.lang.System" is skipped and thus Rhino assumes that\r
+     * "java.lang.System" doesn't exist. So then for "java.lang.System.out",\r
+     * Rhino attempts to load the class "java.lang.System.out" because \r
+     * it assumes that "java.lang.System" is a package name.\r
+     * <p>\r
+     * @param fullClassName the full name of the class (including the package\r
+     *                      name, with '.' as a delimiter). For example the \r
+     *                      standard string class is "java.lang.String"\r
+     * @return whether or not to reveal this class to scripts\r
+     */\r
+    public boolean visibleToScripts(String fullClassName);\r
+}\r
diff --git a/src/org/mozilla/javascript/ShallowNodeIterator.java b/src/org/mozilla/javascript/ShallowNodeIterator.java
new file mode 100644 (file)
index 0000000..f0beaa9
--- /dev/null
@@ -0,0 +1,69 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Roger Lawrence\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.Enumeration;\r
+\r
+/**\r
+ * This class implements a child iterator for the Node class.\r
+ *\r
+ * @see Node\r
+ * @author Norris Boyd\r
+ */\r
+public class ShallowNodeIterator implements Enumeration {\r
+\r
+    public ShallowNodeIterator(Node n) {\r
+        current = n;\r
+    }\r
+\r
+    public boolean hasMoreElements() {\r
+        return current != null;\r
+    }\r
+\r
+    public Object nextElement() {\r
+        return nextNode();\r
+    }\r
+\r
+    public Node nextNode() {\r
+        Node result = current;\r
+        current = current.next;\r
+        return result;\r
+    }\r
+\r
+    private Node current;\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/SourceTextItem.java b/src/org/mozilla/javascript/SourceTextItem.java
new file mode 100644 (file)
index 0000000..27c707b
--- /dev/null
@@ -0,0 +1,222 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * John Bandhauer\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// DEBUG API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This interface supports managing incrementally updated source text items.\r
+ * <p>\r
+ * Items have immutable names. They can be valid or invalid. They can \r
+ * accumulate text, but existing text can not be overwritten. They can be \r
+ * marked as 'complete', 'aborted', etc. They can be cleared of all text. \r
+ * See status flags for details.\r
+ *\r
+ * @see org.mozilla.javascript.SourceTextManager\r
+ * @author John Bandhauer\r
+ */\r
+\r
+public interface SourceTextItem\r
+{\r
+\r
+    /**\r
+     * Possible status values...\r
+     */\r
+\r
+    /**\r
+     * Item just created, no text added yet.\r
+     */\r
+    public static final int INITED    = 0;\r
+\r
+    /**\r
+     * Item has some text, likely that more will be added.\r
+     */\r
+    public static final int PARTIAL   = 1;\r
+\r
+    /**\r
+     * Item has all the text it is going to get.\r
+     */\r
+    public static final int COMPLETED = 2;\r
+\r
+    /**\r
+     * User aborted loading of text, some text may be in item.\r
+     */\r
+    public static final int ABORTED   = 3;\r
+\r
+    /**\r
+     * Loading of text failed, some text may be in item.\r
+     */\r
+    public static final int FAILED    = 4;\r
+\r
+    /**\r
+     * Whatever text was in item has been cleared by a consumer.\r
+     */\r
+    public static final int CLEARED   = 5;\r
+\r
+    /**\r
+     * Item has be marked as invalid and has no useful information.\r
+     */\r
+    public static final int INVALID   = 6;\r
+\r
+    /**\r
+     * Append some text.\r
+     * <p>\r
+     * This only succeeds if status is INITED or PARTIAL.\r
+     *\r
+     * @param text String to append\r
+     * @return true if succeeded, false if failed\r
+     */\r
+    public boolean append(String text);\r
+\r
+    /**\r
+     * Append a char.\r
+     * <p>\r
+     * This only succeeds if status is INITED or PARTIAL.\r
+     *\r
+     * @param c char to append\r
+     * @return true if succeeded, false if failed\r
+     */\r
+    public boolean append(char c);\r
+\r
+    /**\r
+     * Append a char from a char[].\r
+     * <p>\r
+     * This only succeeds if status is INITED or PARTIAL.\r
+     *\r
+     * @param buf char[] from which to append\r
+     * @param offset offset into char[] from which to append\r
+     * @param count count of chars to append\r
+     * @return true if succeeded, false if failed\r
+     */\r
+    public boolean append(char[] buf, int offset, int count);\r
+\r
+    /**\r
+     * Set status to COMPLETED.\r
+     * <p>\r
+     * meaning: all the text is there, it is complete, no problems.\r
+     * This only succeeds if status is INITED or PARTIAL.\r
+     *\r
+     * @return true if succeeded, false if failed\r
+     */\r
+    public boolean complete();\r
+\r
+    /**\r
+     * Set status to ABORTED.\r
+     * <p>\r
+     * meaning: some text might be there, but user aborted text loading.\r
+     * This only succeeds if status is INITED or PARTIAL.\r
+     *\r
+     * @return true if succeeded, false if failed\r
+     */\r
+    public boolean abort();\r
+\r
+    /**\r
+     * Set status to FAILED.\r
+     * <p>\r
+     * meaning: some text might be there, but loading failed.\r
+     * This only succeeds if status is INITED or PARTIAL.\r
+     *\r
+     * @return true if succeeded, false if failed\r
+     */\r
+    public boolean fail();\r
+\r
+    /**\r
+     * Clear the text and set status to CLEARED.\r
+     * <p>\r
+     * meaning: consumer of the text has what he wants, leave this \r
+     * as an emptly placeholder.\r
+     * This succeeds unless status is INVALID.\r
+     *\r
+     * @return true if succeeded, false if failed\r
+     */\r
+    public boolean clear();\r
+\r
+    /**\r
+     * Clear the text and set status to INVALID.\r
+     * <p>\r
+     * meaning: This item is not to be used, likely the SourceTextManager \r
+     * has been asked to create a new item (with potentially different \r
+     * text) in its place.\r
+     * This succeeds unless status is INVALID.\r
+     *\r
+     * @return true if succeeded, false if failed\r
+     */\r
+    public boolean invalidate();\r
+\r
+    /**\r
+     * Get the Current Text.\r
+     *\r
+     * @return the text, null if INVALID\r
+     */\r
+    public String  getText();\r
+\r
+    /**\r
+     * Get the name.\r
+     *\r
+     * @return the name (immutable).\r
+     */\r
+    public String  getName();\r
+\r
+    /**\r
+     * Get the status.\r
+     *\r
+     * @return the current status\r
+     */\r
+    public int     getStatus();\r
+\r
+    /**\r
+     * Get the validity status.\r
+     *\r
+     * @return true if item is valid, false if not\r
+     */\r
+    public boolean isValid();\r
+\r
+    /**\r
+     * Get a counter representing the modification count of the item.\r
+     * <p>\r
+     * Any consumer of the item may look at this value and store it at one\r
+     * point in time and then later look at the value again. If the \r
+     * value has increased, then the consumer can know that the item has \r
+     * been modified in some way and can then take the appropriate action.\r
+     * If the count has not changed from one point in time to another, \r
+     * then the item is guarenteed to not have changed in any way.\r
+     *\r
+     * NOTE: this value is not guaranteed to start at 0;\r
+     *\r
+     * @return the alter count\r
+     */\r
+    public int     getAlterCount();\r
+}\r
diff --git a/src/org/mozilla/javascript/SourceTextManager.java b/src/org/mozilla/javascript/SourceTextManager.java
new file mode 100644 (file)
index 0000000..9046175
--- /dev/null
@@ -0,0 +1,82 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * John Bandhauer\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// DEBUG API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.Enumeration;\r
+\r
+/**\r
+ * This interface supports managing incrementally updated source text.\r
+ *\r
+ * @see org.mozilla.javascript.SourceTextItem\r
+ * @see org.mozilla.javascript.Context#setSourceTextManager\r
+ * @author John Bandhauer\r
+ */\r
+\r
+public interface SourceTextManager\r
+{\r
+    /**\r
+     * Create a new item.\r
+     * <p>\r
+     * A new item is always created. If an item with this name already exists, \r
+     * then that preexisting iten is is set as invalid. \r
+     *\r
+     * @param name item name - in most embedings this will be a URL or filename\r
+     * @return new item\r
+     */\r
+    public SourceTextItem newItem(String name);\r
+\r
+    /**\r
+     * Get an existing item.\r
+     * <p>\r
+     * If an item with this name already exists, then return it. Otherwise, \r
+     * return null.\r
+     *\r
+     * @param name item name - in most embedings this will be a URL or filename\r
+     * @return existing item (or null if none)\r
+     */\r
+    public SourceTextItem getItem(String name);\r
+\r
+    /**\r
+     * Get all items.\r
+     * <p>\r
+     * Takes a snapshot of the list of all items and returns an Enumeration.\r
+     *\r
+     * @return snapshot Enumeration of all items\r
+     */\r
+    public Enumeration     getAllItems();\r
+}    \r
diff --git a/src/org/mozilla/javascript/Synchronizer.java b/src/org/mozilla/javascript/Synchronizer.java
new file mode 100644 (file)
index 0000000..3f35750
--- /dev/null
@@ -0,0 +1,77 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ * The contents of this file are subject to the Mozilla Public License\r
+ * Version 1.1 (the "License"); you may not use this file except in\r
+ * compliance with the License. You may obtain a copy of the License at\r
+ * http://www.mozilla.org/MPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS IS"\r
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing rights and limitations\r
+ * under the License.\r
+ *\r
+ * The Original Code is Synchronizer.java code, released Sep 27, 2000.\r
+ *\r
+ * The Initial Developer of the Original Code is Matthias Radestock\r
+ * <matthias@sorted.org>. Portions created by Matthias Radestock are\r
+ * Copyright (C) 2000 Matthias Radestock. All Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ *     Redfig Ltd (http://www.redfig.com)\r
+ *     LShift Ltd (http://www.lshift.net)\r
+ *\r
+ * Alternatively, the contents of this file may be used under the terms\r
+ * of the GNU Public License (the  "GPL License"), in which case the\r
+ * provisions of the GPL License are applicable instead of those\r
+ * above.  If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL License and not to allow others to use\r
+ * your version of this file under the MPL, indicate your decision by\r
+ * deleting  the provisions above and replace  them with the notice and\r
+ * other provisions required by the GPL License.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this file\r
+ * under either the MPL or the GPL License.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This class provides support for implementing Java-style synchronized\r
+ * methods in Javascript.\r
+ *\r
+ * Synchronized functions are created from ordinary Javascript\r
+ * functions by the <code>Synchronizer</code> constructor, e.g.\r
+ * <code>new Packages.org.mozilla.javascript.Synchronizer(fun)</code>.\r
+ * The resulting object is a function that establishes an exclusive\r
+ * lock on the <code>this</code> object of its invocation.\r
+ *\r
+ * The Rhino shell provides a short-cut for the creation of\r
+ * synchronized methods: <code>sync(fun)</code> has the same effect as\r
+ * calling the above constructor.\r
+ *\r
+ * @see org.mozilla.javascript.Delegator\r
+ * @author Matthias Radestock\r
+ */\r
+\r
+public class Synchronizer extends Delegator {\r
+\r
+    /**\r
+     * Create a new synchronized function from an existing one.\r
+     *\r
+     * @param obj the existing function\r
+     */\r
+    public Synchronizer(Scriptable obj) {\r
+       super(obj);\r
+    }\r
+\r
+    /**\r
+     * @see org.mozilla.javascript.Function#call\r
+     */\r
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj,\r
+                      Object[] args)\r
+       throws JavaScriptException {\r
+       synchronized(thisObj) {\r
+           return ((Function)obj).call(cx,scope,thisObj,args);\r
+       }\r
+    }\r
+}\r
diff --git a/src/org/mozilla/javascript/TokenStream.java b/src/org/mozilla/javascript/TokenStream.java
new file mode 100644 (file)
index 0000000..25b5fc3
--- /dev/null
@@ -0,0 +1,1432 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Roger Lawrence\r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.io.*;\r
+\r
+/**\r
+ * This class implements the JavaScript scanner.\r
+ *\r
+ * It is based on the C source files jsscan.c and jsscan.h\r
+ * in the jsref package.\r
+ *\r
+ * @see org.mozilla.javascript.Parser\r
+ *\r
+ * @author Mike McCabe\r
+ * @author Brendan Eich\r
+ */\r
+\r
+public class TokenStream {\r
+    /*\r
+     * JSTokenStream flags, mirroring those in jsscan.h.  These are used\r
+     * by the parser to change/check the state of the scanner.\r
+     */\r
+\r
+    public final static int\r
+        TSF_NEWLINES    = 0x0001,  // tokenize newlines\r
+        TSF_FUNCTION    = 0x0002,  // scanning inside function body\r
+        TSF_RETURN_EXPR = 0x0004,  // function has 'return expr;'\r
+        TSF_RETURN_VOID = 0x0008,  // function has 'return;'\r
+        TSF_REGEXP      = 0x0010;  // looking for a regular expression\r
+\r
+    /*\r
+     * For chars - because we need something out-of-range\r
+     * to check.  (And checking EOF by exception is annoying.)\r
+     * Note distinction from EOF token type!\r
+     */\r
+    private final static int\r
+        EOF_CHAR = -1;\r
+\r
+    /**\r
+     * Token types.  These values correspond to JSTokenType values in\r
+     * jsscan.c.\r
+     */\r
+\r
+    public final static int\r
+    // start enum\r
+        ERROR       = -1, // well-known as the only code < EOF\r
+        EOF         = 0,  // end of file token - (not EOF_CHAR)\r
+        EOL         = 1,  // end of line\r
+        // Beginning here are interpreter bytecodes. Their values\r
+        // must not exceed 127.\r
+        POPV        = 2,\r
+        ENTERWITH   = 3,\r
+        LEAVEWITH   = 4,\r
+        RETURN      = 5,\r
+        GOTO        = 6,\r
+        IFEQ        = 7,\r
+        IFNE        = 8,\r
+        DUP         = 9,\r
+        SETNAME     = 10,\r
+        BITOR       = 11,\r
+        BITXOR      = 12,\r
+        BITAND      = 13,\r
+        EQ          = 14,\r
+        NE          = 15,\r
+        LT          = 16,\r
+        LE          = 17,\r
+        GT          = 18,\r
+        GE          = 19,\r
+        LSH         = 20,\r
+        RSH         = 21,\r
+        URSH        = 22,\r
+        ADD         = 23,\r
+        SUB         = 24,\r
+        MUL         = 25,\r
+        DIV         = 26,\r
+        MOD         = 27,\r
+        BITNOT      = 28,\r
+        NEG         = 29,\r
+        NEW         = 30,\r
+        DELPROP     = 31,\r
+        TYPEOF      = 32,\r
+        NAMEINC     = 33,\r
+        PROPINC     = 34,\r
+        ELEMINC     = 35,\r
+        NAMEDEC     = 36,\r
+        PROPDEC     = 37,\r
+        ELEMDEC     = 38,\r
+        GETPROP     = 39,\r
+        SETPROP     = 40,\r
+        GETELEM     = 41,\r
+        SETELEM     = 42,\r
+        CALL        = 43,\r
+        NAME        = 44,\r
+        NUMBER      = 45,\r
+        STRING      = 46,\r
+        ZERO        = 47,\r
+        ONE         = 48,\r
+        NULL        = 49,\r
+        THIS        = 50,\r
+        FALSE       = 51,\r
+        TRUE        = 52,\r
+        SHEQ        = 53,   // shallow equality (===)\r
+        SHNE        = 54,   // shallow inequality (!==)\r
+        CLOSURE     = 55,\r
+        OBJECT      = 56,\r
+        POP         = 57,\r
+        POS         = 58,\r
+        VARINC      = 59,\r
+        VARDEC      = 60,\r
+        BINDNAME    = 61,\r
+        THROW       = 62,\r
+        IN          = 63,\r
+        INSTANCEOF  = 64,\r
+        GOSUB       = 65,\r
+        RETSUB      = 66,\r
+        CALLSPECIAL = 67,\r
+        GETTHIS     = 68,\r
+        NEWTEMP     = 69,\r
+        USETEMP     = 70,\r
+        GETBASE     = 71,\r
+        GETVAR      = 72,\r
+        SETVAR      = 73,\r
+        UNDEFINED   = 74,\r
+        TRY         = 75,\r
+        ENDTRY      = 76,\r
+        NEWSCOPE    = 77,\r
+        TYPEOFNAME  = 78,\r
+        ENUMINIT    = 79,\r
+        ENUMNEXT    = 80,\r
+        GETPROTO    = 81,\r
+        GETPARENT   = 82,\r
+        SETPROTO    = 83,\r
+        SETPARENT   = 84,\r
+        SCOPE       = 85,\r
+        GETSCOPEPARENT = 86,\r
+        THISFN      = 87,\r
+        JTHROW      = 88,\r
+        // End of interpreter bytecodes\r
+        SEMI        = 89,  // semicolon\r
+        LB          = 90,  // left and right brackets\r
+        RB          = 91,\r
+        LC          = 92,  // left and right curlies (braces)\r
+        RC          = 93,\r
+        LP          = 94,  // left and right parentheses\r
+        RP          = 95,\r
+        COMMA       = 96,  // comma operator\r
+        ASSIGN      = 97, // assignment ops (= += -= etc.)\r
+        HOOK        = 98, // conditional (?:)\r
+        COLON       = 99,\r
+        OR          = 100, // logical or (||)\r
+        AND         = 101, // logical and (&&)\r
+        EQOP        = 102, // equality ops (== !=)\r
+        RELOP       = 103, // relational ops (< <= > >=)\r
+        SHOP        = 104, // shift ops (<< >> >>>)\r
+        UNARYOP     = 105, // unary prefix operator\r
+        INC         = 106, // increment/decrement (++ --)\r
+        DEC         = 107,\r
+        DOT         = 108, // member operator (.)\r
+        PRIMARY     = 109, // true, false, null, this\r
+        FUNCTION    = 110, // function keyword\r
+        EXPORT      = 111, // export keyword\r
+        IMPORT      = 112, // import keyword\r
+        IF          = 113, // if keyword\r
+        ELSE        = 114, // else keyword\r
+        SWITCH      = 115, // switch keyword\r
+        CASE        = 116, // case keyword\r
+        DEFAULT     = 117, // default keyword\r
+        WHILE       = 118, // while keyword\r
+        DO          = 119, // do keyword\r
+        FOR         = 120, // for keyword\r
+        BREAK       = 121, // break keyword\r
+        CONTINUE    = 122, // continue keyword\r
+        VAR         = 123, // var keyword\r
+        WITH        = 124, // with keyword\r
+        CATCH       = 125, // catch keyword\r
+        FINALLY     = 126, // finally keyword\r
+        RESERVED    = 127, // reserved keywords\r
+\r
+        /** Added by Mike - these are JSOPs in the jsref, but I\r
+         * don't have them yet in the java implementation...\r
+         * so they go here.  Also whatever I needed.\r
+\r
+         * Most of these go in the 'op' field when returning\r
+         * more general token types, eg. 'DIV' as the op of 'ASSIGN'.\r
+         */\r
+        NOP         = 128, // NOP\r
+        NOT         = 129, // etc.\r
+        PRE         = 130, // for INC, DEC nodes.\r
+        POST        = 131,\r
+\r
+        /**\r
+         * For JSOPs associated with keywords...\r
+         * eg. op = THIS; token = PRIMARY\r
+         */\r
+\r
+        VOID        = 132,\r
+\r
+        /* types used for the parse tree - these never get returned\r
+         * by the scanner.\r
+         */\r
+        BLOCK       = 133, // statement block\r
+        ARRAYLIT    = 134, // array literal\r
+        OBJLIT      = 135, // object literal\r
+        LABEL       = 136, // label\r
+        TARGET      = 137,\r
+        LOOP        = 138,\r
+        ENUMDONE    = 139,\r
+        EXPRSTMT    = 140,\r
+        PARENT      = 141,\r
+        CONVERT     = 142,\r
+        JSR         = 143,\r
+        NEWLOCAL    = 144,\r
+        USELOCAL    = 145,\r
+        SCRIPT      = 146,   // top-level node for entire script\r
+        \r
+        /**\r
+         * For the interpreted mode indicating a line number change in icodes.\r
+         */\r
+        LINE        = 147,\r
+        SOURCEFILE  = 148,\r
+        \r
+        // For debugger\r
+        \r
+        BREAKPOINT  = 149,\r
+        ASSERT      = 150; // XWT assert hack\r
+    // end enum\r
+\r
+\r
+    /* for mapping int token types to printable strings.\r
+     * make sure to add 1 to index before using these!\r
+     */\r
+    private static String names[];\r
+    private static void checkNames() {\r
+        if (Context.printTrees && names == null) {\r
+            String[] a = {\r
+                "error",\r
+                "eof",\r
+                "eol",\r
+                "popv",\r
+                "enterwith",\r
+                "leavewith",\r
+                "return",\r
+                "goto",\r
+                "ifeq",\r
+                "ifne",\r
+                "dup",\r
+                "setname",\r
+                "bitor",\r
+                "bitxor",\r
+                "bitand",\r
+                "eq",\r
+                "ne",\r
+                "lt",\r
+                "le",\r
+                "gt",\r
+                "ge",\r
+                "lsh",\r
+                "rsh",\r
+                "ursh",\r
+                "add",\r
+                "sub",\r
+                "mul",\r
+                "div",\r
+                "mod",\r
+                "bitnot",\r
+                "neg",\r
+                "new",\r
+                "delprop",\r
+                "typeof",\r
+                "nameinc",\r
+                "propinc",\r
+                "eleminc",\r
+                "namedec",\r
+                "propdec",\r
+                "elemdec",\r
+                "getprop",\r
+                "setprop",\r
+                "getelem",\r
+                "setelem",\r
+                "call",\r
+                "name",\r
+                "number",\r
+                "string",\r
+                "zero",\r
+                "one",\r
+                "null",\r
+                "this",\r
+                "false",\r
+                "true",\r
+                "sheq",\r
+                "shne",\r
+                "closure",\r
+                "object",\r
+                "pop",\r
+                "pos",\r
+                "varinc",\r
+                "vardec",\r
+                "bindname",\r
+                "throw",\r
+                "in",\r
+                "instanceof",\r
+                "gosub",\r
+                "retsub",\r
+                "callspecial",\r
+                "getthis",\r
+                "newtemp",\r
+                "usetemp",\r
+                "getbase",\r
+                "getvar",\r
+                "setvar",\r
+                "undefined",\r
+                "try",\r
+                "endtry",\r
+                "newscope",\r
+                "typeofname",\r
+                "enuminit",\r
+                "enumnext",\r
+                "getproto",\r
+                "getparent",\r
+                "setproto",\r
+                "setparent",\r
+                "scope",\r
+                "getscopeparent",\r
+                "thisfn",\r
+                "jthrow",\r
+                "semi",\r
+                "lb",\r
+                "rb",\r
+                "lc",\r
+                "rc",\r
+                "lp",\r
+                "rp",\r
+                "comma",\r
+                "assign",\r
+                "hook",\r
+                "colon",\r
+                "or",\r
+                "and",\r
+                "eqop",\r
+                "relop",\r
+                "shop",\r
+                "unaryop",\r
+                "inc",\r
+                "dec",\r
+                "dot",\r
+                "primary",\r
+                "function",\r
+                "export",\r
+                "import",\r
+                "if",\r
+                "else",\r
+                "switch",\r
+                "case",\r
+                "default",\r
+                "while",\r
+                "do",\r
+                "for",\r
+                "break",\r
+                "continue",\r
+                "var",\r
+                "with",\r
+                "catch",\r
+                "finally",\r
+                "reserved",\r
+                "nop",\r
+                "not",\r
+                "pre",\r
+                "post",\r
+                "void",\r
+                "block",\r
+                "arraylit",\r
+                "objlit",\r
+                "label",\r
+                "target",\r
+                "loop",\r
+                "enumdone",\r
+                "exprstmt",\r
+                "parent",\r
+                "convert",\r
+                "jsr",\r
+                "newlocal",\r
+                "uselocal",\r
+                "script",\r
+                "line",\r
+                "sourcefile",\r
+            };\r
+            names = a;\r
+        }\r
+    }\r
+\r
+    /* This function uses the cached op, string and number fields in\r
+     * TokenStream; if getToken has been called since the passed token\r
+     * was scanned, the op or string printed may be incorrect.\r
+     */\r
+    public String tokenToString(int token) {\r
+        if (Context.printTrees) {\r
+            checkNames();\r
+            if (token + 1 >= names.length)\r
+                return null;\r
+\r
+            if (token == UNARYOP ||\r
+                token == ASSIGN ||\r
+                token == PRIMARY ||\r
+                token == EQOP ||\r
+                token == SHOP ||\r
+                token == RELOP) {\r
+                return names[token + 1] + " " + names[this.op + 1];\r
+            }\r
+\r
+            if (token == STRING || token == OBJECT || token == NAME)\r
+                return names[token + 1] + " `" + this.string + "'";\r
+\r
+            if (token == NUMBER)\r
+                return "NUMBER " + this.number;\r
+\r
+            return names[token + 1];\r
+        }\r
+        return "";\r
+    }\r
+\r
+    public static String tokenToName(int type) {\r
+        checkNames();\r
+        return names == null ? "" : names[type + 1];\r
+    }\r
+\r
+\r
+    private int stringToKeyword(String name) {\r
+// #string_id_map#\r
+// The following assumes that EOF == 0\r
+        final int    \r
+            Id_break         = BREAK,\r
+            Id_case          = CASE,\r
+            Id_continue      = CONTINUE,\r
+            Id_default       = DEFAULT,\r
+            Id_delete        = DELPROP,\r
+            Id_do            = DO,\r
+            Id_else          = ELSE,\r
+            Id_export        = EXPORT,\r
+            Id_false         = PRIMARY | (FALSE << 8),\r
+            Id_for           = FOR,\r
+            Id_function      = FUNCTION,\r
+            Id_if            = IF,\r
+            Id_in            = RELOP | (IN << 8),\r
+            Id_new           = NEW,\r
+            Id_null          = PRIMARY | (NULL << 8),\r
+            Id_return        = RETURN,\r
+            Id_switch        = SWITCH,\r
+            Id_this          = PRIMARY | (THIS << 8),\r
+            Id_true          = PRIMARY | (TRUE << 8),\r
+            Id_typeof        = UNARYOP | (TYPEOF << 8),\r
+            Id_var           = VAR,\r
+            Id_void          = UNARYOP | (VOID << 8),\r
+            Id_while         = WHILE,\r
+            Id_with          = WITH,\r
+\r
+            // the following are #ifdef RESERVE_JAVA_KEYWORDS in jsscan.c\r
+            Id_abstract      = RESERVED,\r
+            Id_boolean       = RESERVED,\r
+            Id_byte          = RESERVED,\r
+            Id_catch         = CATCH,\r
+            Id_char          = RESERVED,\r
+            Id_class         = RESERVED,\r
+            Id_const         = RESERVED,\r
+            Id_debugger      = RESERVED,\r
+            Id_double        = RESERVED,\r
+            Id_enum          = RESERVED,\r
+            Id_extends       = RESERVED,\r
+            Id_final         = RESERVED,\r
+            Id_finally       = FINALLY,\r
+            Id_float         = RESERVED,\r
+            Id_goto          = RESERVED,\r
+            Id_implements    = RESERVED,\r
+            Id_import        = IMPORT,\r
+            Id_instanceof    = RELOP | (INSTANCEOF << 8),\r
+            Id_int           = RESERVED,\r
+            Id_interface     = RESERVED,\r
+            Id_long          = RESERVED,\r
+            Id_native        = RESERVED,\r
+            Id_package       = RESERVED,\r
+            Id_private       = RESERVED,\r
+            Id_protected     = RESERVED,\r
+            Id_public        = RESERVED,\r
+            Id_assert        = ASSERT,\r
+            Id_short         = RESERVED,\r
+            Id_static        = RESERVED,\r
+            Id_super         = RESERVED,\r
+            Id_synchronized  = RESERVED,\r
+            Id_throw         = THROW,\r
+            Id_throws        = RESERVED,\r
+            Id_transient     = RESERVED,\r
+            Id_try           = TRY,\r
+            Id_volatile      = RESERVED;\r
+        \r
+        int id;\r
+        String s = name;\r
+// #generated# Last update: 2001-06-01 17:45:01 CEST\r
+        L0: { id = 0; String X = null; int c;\r
+            L: switch (s.length()) {\r
+            case 2: c=s.charAt(1);\r
+                if (c=='f') { if (s.charAt(0)=='i') {id=Id_if; break L0;} }\r
+                else if (c=='n') { if (s.charAt(0)=='i') {id=Id_in; break L0;} }\r
+                else if (c=='o') { if (s.charAt(0)=='d') {id=Id_do; break L0;} }\r
+                break L;\r
+            case 3: switch (s.charAt(0)) {\r
+                case 'f': if (s.charAt(2)=='r' && s.charAt(1)=='o') {id=Id_for; break L0;} break L;\r
+                case 'i': if (s.charAt(2)=='t' && s.charAt(1)=='n') {id=Id_int; break L0;} break L;\r
+                case 'n': if (s.charAt(2)=='w' && s.charAt(1)=='e') {id=Id_new; break L0;} break L;\r
+                case 't': if (s.charAt(2)=='y' && s.charAt(1)=='r') {id=Id_try; break L0;} break L;\r
+                case 'v': if (s.charAt(2)=='r' && s.charAt(1)=='a') {id=Id_var; break L0;} break L;\r
+                } break L;\r
+            case 4: switch (s.charAt(0)) {\r
+                case 'b': X="byte";id=Id_byte; break L;\r
+                case 'c': c=s.charAt(3);\r
+                    if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='a') {id=Id_case; break L0;} }\r
+                    else if (c=='r') { if (s.charAt(2)=='a' && s.charAt(1)=='h') {id=Id_char; break L0;} }\r
+                    break L;\r
+                case 'e': c=s.charAt(3);\r
+                    if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='l') {id=Id_else; break L0;} }\r
+                    else if (c=='m') { if (s.charAt(2)=='u' && s.charAt(1)=='n') {id=Id_enum; break L0;} }\r
+                    break L;\r
+                case 'g': X="goto";id=Id_goto; break L;\r
+                case 'l': X="long";id=Id_long; break L;\r
+                case 'n': X="null";id=Id_null; break L;\r
+                case 't': c=s.charAt(3);\r
+                    if (c=='e') { if (s.charAt(2)=='u' && s.charAt(1)=='r') {id=Id_true; break L0;} }\r
+                    else if (c=='s') { if (s.charAt(2)=='i' && s.charAt(1)=='h') {id=Id_this; break L0;} }\r
+                    break L;\r
+                case 'v': X="void";id=Id_void; break L;\r
+                case 'w': X="with";id=Id_with; break L;\r
+                } break L;\r
+            case 5: switch (s.charAt(2)) {\r
+                case 'a': X="class";id=Id_class; break L;\r
+                case 'e': X="break";id=Id_break; break L;\r
+                case 'i': X="while";id=Id_while; break L;\r
+                case 'l': X="false";id=Id_false; break L;\r
+                case 'n': c=s.charAt(0);\r
+                    if (c=='c') { X="const";id=Id_const; }\r
+                    else if (c=='f') { X="final";id=Id_final; }\r
+                    break L;\r
+                case 'o': c=s.charAt(0);\r
+                    if (c=='f') { X="float";id=Id_float; }\r
+                    else if (c=='s') { X="short";id=Id_short; }\r
+                    break L;\r
+                case 'p': X="super";id=Id_super; break L;\r
+                case 'r': X="throw";id=Id_throw; break L;\r
+                case 't': X="catch";id=Id_catch; break L;\r
+                } break L;\r
+            case 6: switch (s.charAt(1)) {\r
+                case 'a': X="native";id=Id_native; break L;\r
+                case 'e': c=s.charAt(0);\r
+                    if (c=='d') { X="delete";id=Id_delete; }\r
+                    else if (c=='r') { X="return";id=Id_return; }\r
+                    break L;\r
+                case 'h': X="throws";id=Id_throws; break L;\r
+                case 'm': X="import";id=Id_import; break L;\r
+                case 'o': X="double";id=Id_double; break L;\r
+\r
+                    // commented out by Adam Megacz for XWT\r
+                    /*\r
+                case 't': X="static";id=Id_static; break L;\r
+                    */\r
+\r
+                case 's': X="assert";id=Id_assert; break L;\r
+                case 'u': X="public";id=Id_public; break L;\r
+                case 'w': X="switch";id=Id_switch; break L;\r
+                case 'x': X="export";id=Id_export; break L;\r
+                case 'y': X="typeof";id=Id_typeof; break L;\r
+                } break L;\r
+            case 7: switch (s.charAt(1)) {\r
+                case 'a': X="package";id=Id_package; break L;\r
+                case 'e': X="default";id=Id_default; break L;\r
+                case 'i': X="finally";id=Id_finally; break L;\r
+                case 'o': X="boolean";id=Id_boolean; break L;\r
+                case 'r': X="private";id=Id_private; break L;\r
+                case 'x': X="extends";id=Id_extends; break L;\r
+                } break L;\r
+            case 8: switch (s.charAt(0)) {\r
+                case 'a': X="abstract";id=Id_abstract; break L;\r
+                case 'c': X="continue";id=Id_continue; break L;\r
+                case 'd': X="debugger";id=Id_debugger; break L;\r
+                case 'f': X="function";id=Id_function; break L;\r
+                case 'v': X="volatile";id=Id_volatile; break L;\r
+                } break L;\r
+            case 9: c=s.charAt(0);\r
+                if (c=='i') { X="interface";id=Id_interface; }\r
+                else if (c=='p') { X="protected";id=Id_protected; }\r
+                else if (c=='t') { X="transient";id=Id_transient; }\r
+                break L;\r
+            case 10: c=s.charAt(1);\r
+                if (c=='m') { X="implements";id=Id_implements; }\r
+                else if (c=='n') { X="instanceof";id=Id_instanceof; }\r
+                break L;\r
+            case 12: X="synchronized";id=Id_synchronized; break L;\r
+            }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+            if (id == Id_const) Context.reportError("The 'const' keyword is not allowed in XWT scripts",\r
+                                                    getSourceName(), getLineno(), getLine(), getOffset());\r
+            if (id == Id_instanceof) Context.reportError("The 'instanceof' keyword is not allowed in XWT scripts",\r
+                                                         getSourceName(), getLineno(), getLine(), getOffset());\r
+            if (id == Id_delete) Context.reportError("The 'delete' keyword is not allowed in XWT scripts",\r
+                                                     getSourceName(), getLineno(), getLine(), getOffset());\r
+\r
+        }\r
+        if (id == Id_new) Context.reportError("The 'new' keyword is not allowed in XWT scripts",\r
+                                               getSourceName(), getLineno(), getLine(), getOffset());\r
+// #/generated#\r
+// #/string_id_map#\r
+        if (id == 0) { return EOF; }\r
+        this.op = id >> 8;\r
+        return id & 0xff;\r
+    }\r
+\r
+    public TokenStream(Reader in, Scriptable scope,\r
+                       String sourceName, int lineno)\r
+    {\r
+        this.in = new LineBuffer(in, lineno);\r
+        this.scope = scope;\r
+        this.pushbackToken = EOF;\r
+        this.sourceName = sourceName;\r
+        flags = 0;\r
+    }\r
+    \r
+    public Scriptable getScope() { \r
+        return scope;\r
+    }\r
+\r
+    /* return and pop the token from the stream if it matches...\r
+     * otherwise return null\r
+     */\r
+    public boolean matchToken(int toMatch) throws IOException {\r
+        int token = getToken();\r
+        if (token == toMatch)\r
+            return true;\r
+\r
+        // didn't match, push back token\r
+        tokenno--;\r
+        this.pushbackToken = token;\r
+        return false;\r
+    }\r
+\r
+    public void clearPushback() {\r
+        this.pushbackToken = EOF;\r
+    }\r
+\r
+    public void ungetToken(int tt) {\r
+        if (this.pushbackToken != EOF && tt != ERROR) {\r
+            String message = Context.getMessage2("msg.token.replaces.pushback",\r
+                tokenToString(tt), tokenToString(this.pushbackToken));\r
+            throw new RuntimeException(message);\r
+        }\r
+        this.pushbackToken = tt;\r
+        tokenno--;\r
+    }\r
+\r
+    public int peekToken() throws IOException {\r
+        int result = getToken();\r
+\r
+        this.pushbackToken = result;\r
+        tokenno--;\r
+        return result;\r
+    }\r
+\r
+    public int peekTokenSameLine() throws IOException {\r
+        int result;\r
+\r
+        flags |= TSF_NEWLINES;          // SCAN_NEWLINES from jsscan.h\r
+        result = peekToken();\r
+        flags &= ~TSF_NEWLINES;         // HIDE_NEWLINES from jsscan.h\r
+        if (this.pushbackToken == EOL)\r
+            this.pushbackToken = EOF;\r
+        return result;\r
+    }\r
+\r
+    protected static boolean isJSIdentifier(String s) {\r
+\r
+        int length = s.length();\r
+\r
+        if (length == 0 || !Character.isJavaIdentifierStart(s.charAt(0)))\r
+            return false;\r
+\r
+        for (int i=1; i<length; i++) {\r
+            char c = s.charAt(i);\r
+            if (!Character.isJavaIdentifierPart(c))\r
+                if (c == '\\')\r
+                    if (! ((i + 5) < length)\r
+                            && (s.charAt(i + 1) == 'u')\r
+                            && 0 <= xDigitToInt(s.charAt(i + 2))\r
+                            && 0 <= xDigitToInt(s.charAt(i + 3))\r
+                            && 0 <= xDigitToInt(s.charAt(i + 4))\r
+                            && 0 <= xDigitToInt(s.charAt(i + 5)))\r
+                    \r
+                return false;\r
+        }\r
+\r
+        return true;\r
+    }\r
+\r
+    private static boolean isAlpha(int c) {\r
+        return ((c >= 'a' && c <= 'z')\r
+                || (c >= 'A' && c <= 'Z'));\r
+    }\r
+\r
+    static boolean isDigit(int c) {\r
+        return (c >= '0' && c <= '9');\r
+    }\r
+\r
+    static int xDigitToInt(int c) {\r
+        if ('0' <= c && c <= '9') { return c - '0'; }\r
+        if ('a' <= c && c <= 'f') { return c - ('a' - 10); }\r
+        if ('A' <= c && c <= 'F') { return c - ('A' - 10); }\r
+        return -1;\r
+    }\r
+\r
+    /* As defined in ECMA.  jsscan.c uses C isspace() (which allows\r
+     * \v, I think.)  note that code in in.read() implicitly accepts\r
+     * '\r' == \u000D as well.\r
+     */\r
+    public static boolean isJSSpace(int c) {\r
+        return (c == '\u0020' || c == '\u0009'\r
+                || c == '\u000C' || c == '\u000B'\r
+                || c == '\u00A0' \r
+                || Character.getType((char)c) == Character.SPACE_SEPARATOR);\r
+    }\r
+\r
+    public static boolean isJSLineTerminator(int c) {\r
+        return (c == '\n' || c == '\r'\r
+                || c == 0x2028 || c == 0x2029);\r
+    }\r
+    \r
+    public int getToken() throws IOException {\r
+        int c;\r
+        tokenno++;\r
+\r
+        // Check for pushed-back token\r
+        if (this.pushbackToken != EOF) {\r
+            int result = this.pushbackToken;\r
+            this.pushbackToken = EOF;\r
+            return result;\r
+        }\r
+\r
+        // Eat whitespace, possibly sensitive to newlines.\r
+        do {\r
+            c = in.read();\r
+            if (c == '\n')\r
+                if ((flags & TSF_NEWLINES) != 0)\r
+                    break;\r
+        } while (isJSSpace(c) || c == '\n');\r
+\r
+        if (c == EOF_CHAR)\r
+            return EOF;\r
+\r
+        // identifier/keyword/instanceof?\r
+        // watch out for starting with a <backslash>\r
+        boolean isUnicodeEscapeStart = false;\r
+        if (c == '\\') {\r
+            c = in.read();\r
+            if (c == 'u')\r
+                isUnicodeEscapeStart = true;\r
+            else\r
+                c = '\\';\r
+            // always unread the 'u' or whatever, we need \r
+            // to start the string below at the <backslash>.\r
+            in.unread();\r
+        }\r
+        if (isUnicodeEscapeStart ||\r
+                    Character.isJavaIdentifierStart((char)c)) {\r
+            in.startString();\r
+\r
+            boolean containsEscape = isUnicodeEscapeStart;            \r
+            do {\r
+                c = in.read();\r
+                if (c == '\\') {\r
+                    c = in.read();\r
+                    containsEscape = (c == 'u');\r
+                }                    \r
+            } while (Character.isJavaIdentifierPart((char)c));\r
+            in.unread();\r
+\r
+            int result;\r
+\r
+            String str = in.getString();\r
+            // OPT we shouldn't have to make a string (object!) to\r
+            // check if it's a keyword.\r
+            \r
+            // strictly speaking we should probably push-back\r
+            // all the bad characters if the <backslash>uXXXX  \r
+            // sequence is malformed. But since there isn't a  \r
+            // correct context(is there?) for a bad Unicode  \r
+            // escape sequence after an identifier, we can report\r
+            // an error here.\r
+            if (containsEscape) {\r
+                char ca[] = str.toCharArray();\r
+                int L = str.length();\r
+                int destination = 0;\r
+                for (int i = 0; i != L;) {\r
+                    c = ca[i];\r
+                    ++i;\r
+                    if (c == '\\' && i != L && ca[i] == 'u') {\r
+                        boolean goodEscape = false;\r
+                        if (i + 4 < L) {\r
+                            int val = xDigitToInt(ca[i + 1]);\r
+                            if (val >= 0) {\r
+                                val = (val << 4) | xDigitToInt(ca[i + 2]);\r
+                                if (val >= 0) {\r
+                                    val = (val << 4) | xDigitToInt(ca[i + 3]);\r
+                                    if (val >= 0) {\r
+                                        val = (val << 4) | xDigitToInt(ca[i + 4]);\r
+                                        if (val >= 0) {\r
+                                            c = (char)val;\r
+                                            i += 5;\r
+                                            goodEscape = true;\r
+                                        }\r
+                                    }\r
+                                }\r
+                            }\r
+                        }\r
+                        if (!goodEscape) {\r
+                            reportSyntaxError("msg.invalid.escape", null);\r
+                            return ERROR;\r
+                        }\r
+                    }\r
+                    ca[destination] = (char)c;\r
+                    ++destination;\r
+                }\r
+                str = new String(ca, 0, destination);\r
+            }\r
+            else\r
+                // Return the corresponding token if it's a keyword\r
+                if ((result = stringToKeyword(str)) != EOF) {\r
+                    return result;\r
+                }\r
+\r
+            this.string = str;\r
+            return NAME;\r
+        }\r
+\r
+        // is it a number?\r
+        if (isDigit(c) || (c == '.' && isDigit(in.peek()))) {\r
+            int base = 10;\r
+            in.startString();\r
+\r
+            double dval = ScriptRuntime.NaN;\r
+            long longval = 0;\r
+            boolean isInteger = true;\r
+\r
+            if (c == '0') {\r
+                c = in.read();\r
+                if (c == 'x' || c == 'X') {\r
+                    c = in.read();\r
+                    base = 16;\r
+                    // restart the string, losing leading 0x\r
+                    in.startString();\r
+                } else if (isDigit(c)) {\r
+                    base = 8;\r
+                }\r
+            }\r
+\r
+            while (0 <= xDigitToInt(c)) {\r
+                if (base < 16) {\r
+                    if (isAlpha(c))\r
+                        break;\r
+                    /*\r
+                     * We permit 08 and 09 as decimal numbers, which\r
+                     * makes our behavior a superset of the ECMA\r
+                     * numeric grammar.  We might not always be so\r
+                     * permissive, so we warn about it.\r
+                     */\r
+                    if (base == 8 && c >= '8') {\r
+                        Object[] errArgs = { c == '8' ? "8" : "9" };\r
+                        Context.reportWarning(\r
+                            Context.getMessage("msg.bad.octal.literal",\r
+                                               errArgs),\r
+                            getSourceName(),\r
+                            in.getLineno(), getLine(), getOffset());\r
+                        base = 10;\r
+                    }\r
+                }\r
+                c = in.read();\r
+            }\r
+\r
+            if (base == 10 && (c == '.' || c == 'e' || c == 'E')) {\r
+                isInteger = false;\r
+                if (c == '.') {\r
+                    do {\r
+                        c = in.read();\r
+                    } while (isDigit(c));\r
+                }\r
+\r
+                if (c == 'e' || c == 'E') {\r
+                    c = in.read();\r
+                    if (c == '+' || c == '-') {\r
+                        c = in.read();\r
+                    }\r
+                    if (!isDigit(c)) {\r
+                        in.getString(); // throw away string in progress\r
+                        reportSyntaxError("msg.missing.exponent", null);\r
+                        return ERROR;\r
+                    }\r
+                    do {\r
+                        c = in.read();\r
+                    } while (isDigit(c));\r
+                }\r
+            }\r
+            in.unread();\r
+            String numString = in.getString();\r
+\r
+            if (base == 10 && !isInteger) {\r
+                try {\r
+                    // Use Java conversion to number from string...\r
+                    dval = (Double.valueOf(numString)).doubleValue();\r
+                }\r
+                catch (NumberFormatException ex) {\r
+                    Object[] errArgs = { ex.getMessage() };\r
+                    reportSyntaxError("msg.caught.nfe", errArgs);\r
+                    return ERROR;\r
+                }\r
+            } else {\r
+                dval = ScriptRuntime.stringToNumber(numString, 0, base);\r
+                longval = (long) dval;\r
+\r
+                // is it an integral fits-in-a-long value?\r
+                if (longval != dval)\r
+                    isInteger = false;\r
+            }\r
+\r
+            if (!isInteger) {\r
+                /* Can't handle floats right now, because postfix INC/DEC\r
+                   generate Doubles, but I would generate a Float through this\r
+                   path, and it causes a stack mismatch. FIXME (MS)\r
+                   if (Float.MIN_VALUE <= dval && dval <= Float.MAX_VALUE)\r
+                   this.number = new Xloat((float) dval);\r
+                   else\r
+                */\r
+                this.number = new Double(dval);\r
+            } else {\r
+                // We generate the smallest possible type here\r
+                if (Byte.MIN_VALUE <= longval && longval <= Byte.MAX_VALUE)\r
+                    this.number = new Byte((byte)longval);\r
+                else if (Short.MIN_VALUE <= longval &&\r
+                         longval <= Short.MAX_VALUE)\r
+                    this.number = new Short((short)longval);\r
+                else if (Integer.MIN_VALUE <= longval &&\r
+                         longval <= Integer.MAX_VALUE)\r
+                    this.number = new Integer((int)longval);\r
+                else {\r
+                    // May lose some precision here, but that's the \r
+                    // appropriate semantics.\r
+                    this.number = new Double(longval);\r
+                }\r
+            }\r
+            return NUMBER;\r
+        }\r
+\r
+        // is it a string?\r
+        if (c == '"' || c == '\'') {\r
+            // We attempt to accumulate a string the fast way, by\r
+            // building it directly out of the reader.  But if there\r
+            // are any escaped characters in the string, we revert to\r
+            // building it out of a StringBuffer.\r
+\r
+            StringBuffer stringBuf = null;\r
+\r
+            int quoteChar = c;\r
+            int val = 0;\r
+\r
+            c = in.read();\r
+            in.startString(); // start after the first "\r
+            while(c != quoteChar) {\r
+                if (c == '\n' || c == EOF_CHAR) {\r
+                    in.unread();\r
+                    in.getString(); // throw away the string in progress\r
+                    reportSyntaxError("msg.unterminated.string.lit", null);\r
+                    return ERROR;\r
+                }\r
+\r
+                if (c == '\\') {\r
+                    // We've hit an escaped character; revert to the\r
+                    // slow method of building a string.\r
+                    if (stringBuf == null) {\r
+                        // Don't include the backslash\r
+                        in.unread();\r
+                        stringBuf = new StringBuffer(in.getString());\r
+                        in.read();\r
+                    }\r
+\r
+                    switch (c = in.read()) {\r
+                    case 'b': c = '\b'; break;\r
+                    case 'f': c = '\f'; break;\r
+                    case 'n': c = '\n'; break;\r
+                    case 'r': c = '\r'; break;\r
+                    case 't': c = '\t'; break;\r
+                    case 'v': c = '\u000B'; break;\r
+                        // \v a late addition to the ECMA spec.\r
+                        // '\v' doesn't seem to be valid Java.\r
+\r
+                    default:\r
+                        if (isDigit(c) && c < '8') {\r
+                            val = c - '0';\r
+                            c = in.read();\r
+                            if (isDigit(c) && c < '8') {\r
+                                val = 8 * val + c - '0';\r
+                                c = in.read();\r
+                                if (isDigit(c) && c < '8') {\r
+                                    val = 8 * val + c - '0';\r
+                                    c = in.read();\r
+                                }\r
+                            }\r
+                            in.unread();\r
+                            if (val > 0377) {\r
+                                reportSyntaxError("msg.oct.esc.too.large", null);\r
+                                return ERROR;\r
+                            }\r
+                            c = val;\r
+                        } else if (c == 'u') {\r
+                            /*\r
+                             * Get 4 hex digits; if the u escape is not\r
+                             * followed by 4 hex digits, use 'u' + the literal\r
+                             * character sequence that follows.  Do some manual\r
+                             * match (OK because we're in a string) to avoid\r
+                             * multi-char match on the underlying stream.\r
+                             */\r
+                            int c1 = in.read();\r
+                            c = xDigitToInt(c1);\r
+                            if (c < 0) {\r
+                                in.unread();\r
+                                c = 'u';\r
+                            } else {\r
+                                int c2 = in.read();\r
+                                c = (c << 4) | xDigitToInt(c2);\r
+                                if (c < 0) {\r
+                                    in.unread();\r
+                                    stringBuf.append('u');\r
+                                    c = c1;\r
+                                } else {\r
+                                    int c3 = in.read();\r
+                                    c = (c << 4) | xDigitToInt(c3);\r
+                                    if (c < 0) {\r
+                                        in.unread();\r
+                                        stringBuf.append('u');\r
+                                        stringBuf.append((char)c1);\r
+                                        c = c2;\r
+                                    } else {\r
+                                        int c4 = in.read();\r
+                                        c = (c << 4) | xDigitToInt(c4);\r
+                                        if (c < 0) {\r
+                                            in.unread();\r
+                                            stringBuf.append('u');\r
+                                            stringBuf.append((char)c1);\r
+                                            stringBuf.append((char)c2);\r
+                                            c = c3;\r
+                                        } else {\r
+                                            // got 4 hex digits! Woo Hoo!\r
+                                        }\r
+                                    }\r
+                                }\r
+                            }\r
+                        } else if (c == 'x') {\r
+                            /* Get 2 hex digits, defaulting to 'x' + literal\r
+                             * sequence, as above.\r
+                             */\r
+                            int c1 = in.read();\r
+                            c = xDigitToInt(c1);\r
+                            if (c < 0) {\r
+                                in.unread();\r
+                                c = 'x';\r
+                            } else {\r
+                                int c2 = in.read();\r
+                                c = (c << 4) | xDigitToInt(c2);\r
+                                if (c < 0) {\r
+                                    in.unread();\r
+                                    stringBuf.append('x');\r
+                                    c = c1;\r
+                                } else {\r
+                                    // got 2 hex digits\r
+                                }\r
+                            }\r
+                        }\r
+                    }\r
+                }\r
+                \r
+                if (stringBuf != null)\r
+                    stringBuf.append((char) c);\r
+                c = in.read();\r
+            }\r
+\r
+            if (stringBuf != null)\r
+                this.string = stringBuf.toString();\r
+            else {\r
+                in.unread(); // miss the trailing "\r
+                this.string = in.getString();\r
+                in.read();\r
+            }\r
+            return STRING;\r
+        }\r
+\r
+        switch (c)\r
+        {\r
+        case '\n': return EOL;\r
+        case ';': return SEMI;\r
+        case '[': return LB;\r
+        case ']': return RB;\r
+        case '{': return LC;\r
+        case '}': return RC;\r
+        case '(': return LP;\r
+        case ')': return RP;\r
+        case ',': return COMMA;\r
+        case '?': return HOOK;\r
+        case ':': return COLON;\r
+        case '.': return DOT;\r
+\r
+        case '|':\r
+            if (in.match('|')) {\r
+                return OR;\r
+            } else if (in.match('=')) {\r
+                this.op = BITOR;\r
+                return ASSIGN;\r
+            } else {\r
+                return BITOR;\r
+            }\r
+\r
+        case '^':\r
+            if (in.match('=')) {\r
+                this.op = BITXOR;\r
+                return ASSIGN;\r
+            } else {\r
+                return BITXOR;\r
+            }\r
+\r
+        case '&':\r
+            if (in.match('&')) {\r
+                return AND;\r
+            } else if (in.match('=')) {\r
+                this.op = BITAND;\r
+                return ASSIGN;\r
+            } else {\r
+                return BITAND;\r
+            }\r
+\r
+        case '=':\r
+            if (in.match('=')) {\r
+                if (in.match('='))\r
+                    this.op = SHEQ;\r
+                else\r
+                    this.op = EQ;\r
+                return EQOP;\r
+            } else {\r
+                this.op = NOP;\r
+                return ASSIGN;\r
+            }\r
+\r
+        case '!':\r
+            if (in.match('=')) {\r
+                if (in.match('='))\r
+                    this.op = SHNE;\r
+                else\r
+                    this.op = NE;\r
+                return EQOP;\r
+            } else {\r
+                this.op = NOT;\r
+                return UNARYOP;\r
+            }\r
+\r
+        case '<':\r
+            /* NB:treat HTML begin-comment as comment-till-eol */\r
+            if (in.match('!')) {\r
+                if (in.match('-')) {\r
+                    if (in.match('-')) {\r
+                        while ((c = in.read()) != EOF_CHAR && c != '\n')\r
+                            /* skip to end of line */;\r
+                        in.unread();\r
+                        return getToken();  // in place of 'goto retry'\r
+                    }\r
+                    in.unread();\r
+                }\r
+                in.unread();\r
+            }\r
+            if (in.match('<')) {\r
+                if (in.match('=')) {\r
+                    this.op = LSH;\r
+                    return ASSIGN;\r
+                } else {\r
+                    this.op = LSH;\r
+                    return SHOP;\r
+                }\r
+            } else {\r
+                if (in.match('=')) {\r
+                    this.op = LE;\r
+                    return RELOP;\r
+                } else {\r
+                    this.op = LT;\r
+                    return RELOP;\r
+                }\r
+            }\r
+\r
+        case '>':\r
+            if (in.match('>')) {\r
+                if (in.match('>')) {\r
+                    if (in.match('=')) {\r
+                        this.op = URSH;\r
+                        return ASSIGN;\r
+                    } else {\r
+                        this.op = URSH;\r
+                        return SHOP;\r
+                    }\r
+                } else {\r
+                    if (in.match('=')) {\r
+                        this.op = RSH;\r
+                        return ASSIGN;\r
+                    } else {\r
+                        this.op = RSH;\r
+                        return SHOP;\r
+                    }\r
+                }\r
+            } else {\r
+                if (in.match('=')) {\r
+                    this.op = GE;\r
+                    return RELOP;\r
+                } else {\r
+                    this.op = GT;\r
+                    return RELOP;\r
+                }\r
+            }\r
+\r
+        case '*':\r
+            if (in.match('=')) {\r
+                this.op = MUL;\r
+                return ASSIGN;\r
+            } else {\r
+                return MUL;\r
+            }\r
+\r
+        case '/':\r
+            // is it a // comment?\r
+            if (in.match('/')) {\r
+                while ((c = in.read()) != EOF_CHAR && c != '\n')\r
+                    /* skip to end of line */;\r
+                in.unread();\r
+                return getToken();\r
+            }\r
+            if (in.match('*')) {\r
+                while ((c = in.read()) != -1\r
+                       && !(c == '*' && in.match('/'))) {\r
+                    if (c == '\n') {\r
+                    } else if (c == '/' && in.match('*')) {\r
+                        if (in.match('/'))\r
+                            return getToken();\r
+                        reportSyntaxError("msg.nested.comment", null);\r
+                        return ERROR;\r
+                    }\r
+                }\r
+                if (c == EOF_CHAR) {\r
+                    reportSyntaxError("msg.unterminated.comment", null);\r
+                    return ERROR;\r
+                }\r
+                return getToken();  // `goto retry'\r
+            }\r
+\r
+            // is it a regexp?\r
+            if ((flags & TSF_REGEXP) != 0) {\r
+                // We don't try to use the in.startString/in.getString\r
+                // approach, because escaped characters (which break it)\r
+                // seem likely to be common.\r
+                StringBuffer re = new StringBuffer();\r
+                while ((c = in.read()) != '/') {\r
+                    if (c == '\n' || c == EOF_CHAR) {\r
+                        in.unread();\r
+                        reportSyntaxError("msg.unterminated.re.lit", null);\r
+                        return ERROR;\r
+                    }\r
+                    if (c == '\\') {\r
+                        re.append((char) c);\r
+                        c = in.read();\r
+                    }\r
+\r
+                    re.append((char) c);\r
+                }\r
+\r
+                StringBuffer flagsBuf = new StringBuffer();\r
+                while (true) {\r
+                    if (in.match('g'))\r
+                        flagsBuf.append('g');\r
+                    else if (in.match('i'))\r
+                        flagsBuf.append('i');\r
+                    else if (in.match('m'))\r
+                        flagsBuf.append('m');\r
+                    else\r
+                        break;\r
+                }\r
+\r
+                if (isAlpha(in.peek())) {\r
+                    reportSyntaxError("msg.invalid.re.flag", null);\r
+                    return ERROR;\r
+                }\r
+\r
+                this.string = re.toString();\r
+                this.regExpFlags = flagsBuf.toString();\r
+                return OBJECT;\r
+            }\r
+\r
+\r
+            if (in.match('=')) {\r
+                this.op = DIV;\r
+                return ASSIGN;\r
+            } else {\r
+                return DIV;\r
+            }\r
+\r
+        case '%':\r
+            this.op = MOD;\r
+            if (in.match('=')) {\r
+                return ASSIGN;\r
+            } else {\r
+                return MOD;\r
+            }\r
+\r
+        case '~':\r
+            this.op = BITNOT;\r
+            return UNARYOP;\r
+\r
+        case '+':\r
+        case '-':\r
+            if (in.match('=')) {\r
+                if (c == '+') {\r
+                    this.op = ADD;\r
+                    return ASSIGN;\r
+                } else {\r
+                    this.op = SUB;\r
+                    return ASSIGN;\r
+                }\r
+            } else if (in.match((char) c)) {\r
+                if (c == '+') {\r
+                    return INC;\r
+                } else {\r
+                    return DEC;\r
+                }\r
+            } else if (c == '-') {\r
+                return SUB;\r
+            } else {\r
+                return ADD;\r
+            }\r
+\r
+        default:\r
+            reportSyntaxError("msg.illegal.character", null);\r
+            return ERROR;\r
+        }\r
+    }\r
+\r
+    public void reportSyntaxError(String messageProperty, Object[] args) {\r
+        String message = Context.getMessage(messageProperty, args);\r
+        if (scope != null) {\r
+            // We're probably in an eval. Need to throw an exception.\r
+            throw NativeGlobal.constructError(\r
+                            Context.getContext(), "SyntaxError",\r
+                            message, scope, getSourceName(),\r
+                            getLineno(), getOffset(), getLine());\r
+        } else {\r
+            Context.reportError(message, getSourceName(),\r
+                                getLineno(), getLine(), getOffset());\r
+        }\r
+    }\r
+\r
+    public String getSourceName() { return sourceName; }\r
+    public int getLineno() { return in.getLineno(); }\r
+    public int getOp() { return op; }\r
+    public String getString() { return string; }\r
+    public Number getNumber() { return number; }\r
+    public String getLine() { return in.getLine(); }\r
+    public int getOffset() { return in.getOffset(); }\r
+    public int getTokenno() { return tokenno; }\r
+    public boolean eof() { return in.eof(); }\r
+\r
+    // instance variables\r
+    private LineBuffer in;\r
+\r
+\r
+    /* for TSF_REGEXP, etc.\r
+     * should this be manipulated by gettor/settor functions?\r
+     * should it be passed to getToken();\r
+     */\r
+    public int flags;\r
+    public String regExpFlags;\r
+\r
+    private String sourceName;\r
+    private String line;\r
+    private Scriptable scope;\r
+    private int pushbackToken;\r
+    private int tokenno;\r
+\r
+    private int op;\r
+\r
+    // Set this to an inital non-null value so that the Parser has\r
+    // something to retrieve even if an error has occured and no\r
+    // string is found.  Fosters one class of error, but saves lots of\r
+    // code.\r
+    private String string = "";\r
+    private Number number;\r
+}\r
diff --git a/src/org/mozilla/javascript/UintMap.java b/src/org/mozilla/javascript/UintMap.java
new file mode 100644 (file)
index 0000000..ddbe531
--- /dev/null
@@ -0,0 +1,468 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Igor Bukanov\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * Map to associate non-negative integers to objects or integers.\r
+ * The map does not synchronize any of its operation, so either use\r
+ * it from a single thread or do own synchronization or perform all mutation\r
+ * operations on one thread before passing the map to others\r
+ *\r
+ * @author Igor Bukanov\r
+ *\r
+ */\r
+\r
+class UintMap {\r
+\r
+// Map implementation via hashtable,\r
+// follows "The Art of Computer Programming" by Donald E. Knuth\r
+\r
+    public UintMap() {\r
+        this(4);\r
+    }\r
+\r
+    public UintMap(int initialCapacity) {\r
+        if (checkWorld) check(initialCapacity >= 0);\r
+        // Table grow when number of stored keys >= 3/4 of max capacity\r
+        int minimalCapacity = initialCapacity * 4 / 3;\r
+        int i;\r
+        for (i = 2; (1 << i) < minimalCapacity; ++i) { }\r
+        minimalPower = i;\r
+        if (checkSelf) check(minimalPower >= 2);\r
+    }\r
+\r
+    public boolean isEmpty() {\r
+        return keyCount == 0;\r
+    }\r
+\r
+    public int size() {\r
+        return keyCount;\r
+    }\r
+\r
+    public boolean has(int key) {\r
+        if (checkWorld) check(key >= 0);\r
+        return 0 <= findIndex(key);\r
+    }\r
+\r
+    public boolean isObjectType(int key) {\r
+        if (checkWorld) check(key >= 0);\r
+        int index = findIndex(key);\r
+        return index >= 0 && isObjectTypeValue(index);\r
+    }\r
+\r
+    public boolean isIntType(int key) {\r
+        if (checkWorld) check(key >= 0);\r
+        int index = findIndex(key);\r
+        return index >= 0 && !isObjectTypeValue(index);\r
+    }\r
+\r
+    /**\r
+     * Get object value assigned with key.\r
+     * @return key object value or null if key is absent or does\r
+     * not have object value\r
+     */\r
+    public Object getObject(int key) {\r
+        if (checkWorld) check(key >= 0);\r
+        if (values != null) {\r
+            int index = findIndex(key);\r
+            if (0 <= index) {\r
+                return values[index];\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * Get integer value assigned with key.\r
+     * @return key integer value or defaultValue if key is absent or does\r
+     * not have int value\r
+     */\r
+    public int getInt(int key, int defaultValue) {\r
+        if (checkWorld) check(key >= 0);\r
+        if (ivaluesShift != 0) {\r
+            int index = findIndex(key);\r
+            if (0 <= index) {\r
+                if (!isObjectTypeValue(index)) {\r
+                    return keys[ivaluesShift + index];\r
+                }\r
+            }\r
+        }\r
+        return defaultValue;\r
+    }\r
+\r
+    /**\r
+     * Get integer value assigned with key.\r
+     * @return key integer value or defaultValue if key does not exist or does\r
+     * not have int value\r
+     * @throws RuntimeException if key does not exist or does\r
+     * not have int value\r
+     */\r
+    public int getExistingInt(int key) {\r
+        if (checkWorld) check(key >= 0);\r
+        if (ivaluesShift != 0) {\r
+            int index = findIndex(key);\r
+            if (0 <= index) {\r
+                if (!isObjectTypeValue(index)) {\r
+                    return keys[ivaluesShift + index];\r
+                }\r
+            }\r
+        }\r
+        // Key must exist\r
+        if (checkWorld) check(false);\r
+        return 0;\r
+    }\r
+\r
+    public void put(int key, Object value) {\r
+        if (checkWorld) check(key >= 0 && value != null);\r
+        int index = ensureIndex(key, false);\r
+        if (values == null) {\r
+            values = new Object[1 << power];\r
+        }\r
+        values[index] = value;\r
+    }\r
+\r
+    public void put(int key, int value) {\r
+        if (checkWorld) check(key >= 0);\r
+        int index = ensureIndex(key, true);\r
+        if (ivaluesShift == 0) {\r
+            int N = 1 << power;\r
+            int[] tmp = new int[N * 2];\r
+            System.arraycopy(keys, 0, tmp, 0, N);\r
+            keys = tmp;\r
+            ivaluesShift = N;\r
+        }\r
+        keys[ivaluesShift + index] = value;\r
+        if (values != null) { values[index] = null; }\r
+    }\r
+\r
+    public void remove(int key) {\r
+        if (checkWorld) check(key >= 0);\r
+        int index = findIndex(key);\r
+        if (0 <= index) {\r
+            keys[index] = DELETED;\r
+            --keyCount;\r
+            if (values != null) { values[index] = null; }\r
+        }\r
+    }\r
+\r
+    public void clear() {\r
+        power = 0;\r
+        keys = null;\r
+        values = null;\r
+        ivaluesShift = 0;\r
+        keyCount = 0;\r
+        occupiedCount = 0;\r
+    }\r
+\r
+    /** Return array of present keys */\r
+    public int[] getKeys() {\r
+        int[] keys = this.keys;\r
+        int n = keyCount;\r
+        int[] result = new int[n];\r
+        for (int i = 0; n != 0; ++i) {\r
+            int entry = keys[i];\r
+            if (entry != EMPTY && entry != DELETED) {\r
+                result[--n] = entry;\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+\r
+    private static int tableLookupStep(int fraction, int mask, int power) {\r
+        int shift = 32 - 2 * power;\r
+        if (shift >= 0) {\r
+            return ((fraction >>> shift) & mask) | 1;\r
+        }\r
+        else {\r
+            return (fraction & (mask >>> -shift)) | 1;\r
+        }\r
+    }\r
+\r
+    private int findIndex(int key) {\r
+        int[] keys = this.keys;\r
+        if (keys != null) {\r
+            int fraction = key * A;\r
+            int index = fraction >>> (32 - power);\r
+            int entry = keys[index];\r
+            if (entry == key) { return index; }\r
+            if (entry != EMPTY) {\r
+                // Search in table after first failed attempt\r
+                int mask = (1 << power) - 1;\r
+                int step = tableLookupStep(fraction, mask, power);\r
+                int n = 0;\r
+                do {\r
+                    if (checkSelf) check(n++ < occupiedCount);\r
+                    index = (index + step) & mask;\r
+                    entry = keys[index];\r
+                    if (entry == key) { return index; }\r
+                } while (entry != EMPTY);\r
+            }\r
+        }\r
+        return -1;\r
+    }\r
+\r
+    private int getFreeIndex(int key) {\r
+        int[] keys = this.keys;\r
+        int fraction = key * A;\r
+        int index = fraction >>> (32 - power);\r
+        if (keys[index] != EMPTY) {\r
+            int mask = (1 << power) - 1;\r
+            int step = tableLookupStep(fraction, mask, power);\r
+            int firstIndex = index;\r
+            do {\r
+                if (checkSelf) check(keys[index] != DELETED);\r
+                index = (index + step) & mask;\r
+                if (checkSelf) check(firstIndex != index);\r
+            } while (keys[index] != EMPTY);\r
+        }\r
+        return index;\r
+    }\r
+\r
+    private void rehashTable(boolean ensureIntSpace) {\r
+        if (keys == null) { power = minimalPower; }\r
+        else {\r
+            // Check if removing deleted entries would free enough space\r
+            if (keyCount * 2 >= occupiedCount) {\r
+                // Need to grow: less then half of deleted entries\r
+                ++power;\r
+            }\r
+        }\r
+        int N = 1 << power;\r
+        int[] old = keys;\r
+        int oldShift = ivaluesShift;\r
+        if (oldShift == 0 && !ensureIntSpace) {\r
+            keys = new int[N];\r
+        }\r
+        else {\r
+            ivaluesShift = N; keys = new int[N * 2];\r
+        }\r
+        for (int i = 0; i != N; ++i) { keys[i] = EMPTY; }\r
+\r
+        Object[] oldValues = values;\r
+        if (oldValues != null) { values = new Object[N]; }\r
+\r
+        if (old != null) {\r
+            for (int i = 0, remaining = keyCount; remaining != 0; ++i) {\r
+                int entry = old[i];\r
+                if (entry != EMPTY && entry != DELETED) {\r
+                    int index = getFreeIndex(entry);\r
+                    keys[index] = entry;\r
+                    if (oldValues != null) {\r
+                        values[index] = oldValues[i];\r
+                    }\r
+                    if (oldShift != 0) {\r
+                        keys[ivaluesShift + index] = old[oldShift + i];\r
+                    }\r
+                    --remaining;\r
+                }\r
+            }\r
+        }\r
+        occupiedCount = keyCount;\r
+    }\r
+\r
+// Ensure key index creating one if necessary\r
+    private int ensureIndex(int key, boolean intType) {\r
+        int index = -1;\r
+        int firstDeleted = -1;\r
+        int[] keys = this.keys;\r
+        if (keys != null) {\r
+            int fraction = key * A;\r
+            index = fraction >>> (32 - power);\r
+            int entry = keys[index];\r
+            if (entry == key) { return index; }\r
+            if (entry != EMPTY) {\r
+                if (entry == DELETED) { firstDeleted = index; }\r
+                // Search in table after first failed attempt\r
+                int mask = (1 << power) - 1;\r
+                int step = tableLookupStep(fraction, mask, power);\r
+                int n = 0;\r
+                do {\r
+                    if (checkSelf) check(n++ < occupiedCount);\r
+                    index = (index + step) & mask;\r
+                    entry = keys[index];\r
+                    if (entry == key) { return index; }\r
+                    if (entry == DELETED && firstDeleted < 0) {\r
+                        firstDeleted = index;\r
+                    }\r
+                } while (entry != EMPTY);\r
+            }\r
+        }\r
+        // Inserting of new key\r
+        if (checkSelf) check(keys == null || keys[index] == EMPTY);\r
+        if (firstDeleted >= 0) {\r
+            index = firstDeleted;\r
+        }\r
+        else {\r
+            // Need to consume empty entry: check occupation level\r
+            if (keys == null || occupiedCount * 4 >= (1 << power) * 3) {\r
+                // Too litle unused entries: rehash\r
+                rehashTable(intType);\r
+                keys = this.keys;\r
+                index = getFreeIndex(key);\r
+            }\r
+            ++occupiedCount;\r
+        }\r
+        keys[index] = key;\r
+        ++keyCount;\r
+        return index;\r
+    }\r
+\r
+    private boolean isObjectTypeValue(int index) {\r
+        if (checkSelf) check(index >= 0 && index < (1 << power));\r
+        return values != null && values[index] != null;\r
+    }\r
+\r
+    private static void check(boolean condition) {\r
+        if (!condition) { throw new RuntimeException(); }\r
+    }\r
+\r
+// Rudimentary support for Design-by-Contract\r
+    private static final boolean checkWorld = true;\r
+    private static final boolean checkSelf = checkWorld && false;\r
+\r
+// A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32)\r
+// See Knuth etc.\r
+    private static final int A = 0x9e3779b9;\r
+\r
+    private static final int EMPTY = -1;\r
+    private static final int DELETED = -2;\r
+\r
+// Structure of kyes and values arrays (N == 1 << power):\r
+// keys[0 <= i < N]: key value or EMPTY or DELETED mark\r
+// values[0 <= i < N]: value of key at keys[i]\r
+// keys[N <= i < 2N]: int values of keys at keys[i - N]\r
+\r
+    private int[] keys;\r
+    private Object[] values;\r
+\r
+    private int minimalPower;\r
+    private int power;\r
+    private int keyCount;\r
+    private int occupiedCount; // == keyCount + deleted_count\r
+\r
+    // If ivaluesShift != 0, keys[ivaluesShift + index] contains integer\r
+    // values associated with keys\r
+    private int ivaluesShift;\r
+\r
+/*\r
+    public static void main(String[] args) {\r
+        UintMap map;\r
+        map = new UintMap();\r
+        testHash(map, 10 * 1000);\r
+        map = new UintMap(30 * 1000);\r
+        testHash(map, 10 * 100);\r
+        map.clear();\r
+        testHash(map, 4);\r
+        map = new UintMap(0);\r
+        testHash(map, 10 * 100);\r
+    }\r
+\r
+    private static void testHash(UintMap map, int N) {\r
+        System.out.print("."); System.out.flush();\r
+        for (int i = 0; i != N; ++i) {\r
+            map.put(i, i);\r
+            check(i == map.getInt(i, -1));\r
+        }\r
+\r
+        System.out.print("."); System.out.flush();\r
+        for (int i = 0; i != N; ++i) {\r
+            map.put(i, i);\r
+            check(i == map.getInt(i, -1));\r
+        }\r
+\r
+        System.out.print("."); System.out.flush();\r
+        for (int i = 0; i != N; ++i) {\r
+            map.put(i, new Integer(i));\r
+            check(-1 == map.getInt(i, -1));\r
+            Integer obj = (Integer)map.getObject(i);\r
+            check(obj != null && i == obj.intValue());\r
+        }\r
+\r
+        check(map.size() == N);\r
+\r
+        System.out.print("."); System.out.flush();\r
+        int[] keys = map.getKeys();\r
+        check(keys.length == N);\r
+        for (int i = 0; i != N; ++i) {\r
+            int key = keys[i];\r
+            check(map.has(key));\r
+            check(!map.isIntType(key));\r
+            check(map.isObjectType(key));\r
+            Integer obj = (Integer) map.getObject(key);\r
+            check(obj != null && key == obj.intValue());\r
+        }\r
+\r
+\r
+        System.out.print("."); System.out.flush();\r
+        for (int i = 0; i != N; ++i) {\r
+            check(-1 == map.getInt(i, -1));\r
+        }\r
+\r
+        System.out.print("."); System.out.flush();\r
+        for (int i = 0; i != N; ++i) {\r
+            map.put(i * i, i);\r
+            check(i == map.getInt(i * i, -1));\r
+        }\r
+\r
+        System.out.print("."); System.out.flush();\r
+        for (int i = 0; i != N; ++i) {\r
+            check(i == map.getInt(i * i, -1));\r
+        }\r
+\r
+        System.out.print("."); System.out.flush();\r
+        for (int i = 0; i != N; ++i) {\r
+            map.put(i * i, new Integer(i));\r
+            check(-1 == map.getInt(i * i, -1));\r
+            map.remove(i * i);\r
+            check(!map.has(i * i));\r
+            map.put(i * i, i);\r
+            check(map.isIntType(i * i));\r
+            check(null == map.getObject(i * i));\r
+            map.remove(i * i);\r
+            check(!map.isObjectType(i * i));\r
+            check(!map.isIntType(i * i));\r
+        }\r
+\r
+        int old_size = map.size();\r
+        for (int i = 0; i != N; ++i) {\r
+            map.remove(i * i);\r
+            check(map.size() == old_size);\r
+        }\r
+\r
+        System.out.println(); System.out.flush();\r
+    }\r
+//*/\r
+}\r
diff --git a/src/org/mozilla/javascript/Undefined.java b/src/org/mozilla/javascript/Undefined.java
new file mode 100644 (file)
index 0000000..eb31853
--- /dev/null
@@ -0,0 +1,138 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * This class implements the Undefined value in JavaScript.\r
+ */\r
+public class Undefined implements Scriptable {\r
+\r
+    static final public Scriptable instance = new Undefined();\r
+\r
+    public String getClassName() {\r
+        return "undefined";\r
+    }\r
+\r
+    public boolean has(String id, Scriptable start) {\r
+        return false;\r
+    }\r
+\r
+    public boolean has(int index, Scriptable start) {\r
+        return false;\r
+    }\r
+\r
+    public Object get(String id, Scriptable start) {\r
+        throw reportError();\r
+    }\r
+\r
+    public Object get(int index, Scriptable start) {\r
+        throw reportError();\r
+    }\r
+\r
+    public void put(String id,Scriptable start, Object value) {\r
+        throw reportError();\r
+    }\r
+\r
+    public void put(int index, Scriptable start, Object value) {\r
+        throw reportError();\r
+    }\r
+\r
+    public void delete(String id) {\r
+        throw reportError();\r
+    }\r
+\r
+    public void delete(int index) {\r
+        throw reportError();\r
+    }\r
+\r
+    public short getAttributes(String id, Scriptable start) {\r
+        throw reportError();\r
+    }\r
+\r
+    public short getAttributes(int index, Scriptable start) {\r
+        throw reportError();\r
+    }\r
+\r
+    public void setAttributes(String id, Scriptable start, short attributes) {\r
+        throw reportError();\r
+    }\r
+\r
+    public void setAttributes(int index, Scriptable start, short attributes) {\r
+        throw reportError();\r
+    }\r
+\r
+    public Scriptable getPrototype() {\r
+        throw reportError();\r
+    }\r
+\r
+    public void setPrototype(Scriptable prototype) {\r
+        throw reportError();\r
+    }\r
+\r
+    public Scriptable getParentScope() {\r
+        throw reportError();\r
+    }\r
+\r
+    public void setParentScope(Scriptable parent) {\r
+        throw reportError();\r
+    }\r
+\r
+    public Object[] getIds() {\r
+        throw reportError();\r
+    }\r
+\r
+    public Object getDefaultValue(Class cl) {\r
+        if (cl == ScriptRuntime.StringClass)\r
+            return "undefined";\r
+        if (cl == ScriptRuntime.NumberClass)\r
+            return ScriptRuntime.NaNobj;\r
+        if (cl == ScriptRuntime.BooleanClass)\r
+            return Boolean.FALSE;\r
+        return this;\r
+    }\r
+\r
+    public boolean hasInstance(Scriptable value) {\r
+        throw reportError();\r
+    }\r
+\r
+    public boolean instanceOf(Scriptable prototype) {\r
+        return false;\r
+    }\r
+\r
+    private RuntimeException reportError() {\r
+        return Context.reportRuntimeError0("msg.undefined");\r
+    }\r
+}\r
diff --git a/src/org/mozilla/javascript/VariableTable.java b/src/org/mozilla/javascript/VariableTable.java
new file mode 100644 (file)
index 0000000..8d97e0e
--- /dev/null
@@ -0,0 +1,158 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Roger Lawrence\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.io.*;\r
+import java.util.*;\r
+\r
+public class VariableTable {\r
+\r
+    public int size() {\r
+        return itsVariables.size();\r
+    }\r
+\r
+    public int getParameterCount(){\r
+        return varStart;\r
+    }\r
+    \r
+    public LocalVariable createLocalVariable(String name, boolean isParameter) \r
+    {\r
+        return new LocalVariable(name, isParameter);\r
+    }\r
+\r
+    public LocalVariable getVariable(int index) {\r
+        return (LocalVariable)(itsVariables.elementAt(index));\r
+    }\r
+\r
+    public LocalVariable getVariable(String name) {\r
+        Integer vIndex = (Integer)(itsVariableNames.get(name));\r
+        if (vIndex != null)\r
+            return (LocalVariable)(itsVariables.elementAt(vIndex.intValue()));\r
+        else\r
+            return null;\r
+    }\r
+\r
+    public int getOrdinal(String name) {\r
+        Integer vIndex = (Integer)(itsVariableNames.get(name));\r
+        if (vIndex != null)\r
+            return vIndex.intValue();\r
+        else\r
+            return -1;\r
+    }\r
+\r
+    public String getName(int index) {\r
+        return ((LocalVariable)(itsVariables.elementAt(index))).getName();\r
+    }\r
+\r
+    public String[] getAllNames() {    \r
+        int N = size();\r
+        String[] result = null;\r
+        if (N != 0) {\r
+            result = new String[N];\r
+            for (int i = 0; i != N; i++) {\r
+                result[i] = getName(i);\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+\r
+    public void establishIndices() {\r
+        for (int i = 0; i < itsVariables.size(); i++) {\r
+            LocalVariable lVar = (LocalVariable)(itsVariables.elementAt(i));\r
+            lVar.setIndex(i);\r
+        }\r
+    }\r
+\r
+    public void addParameter(String pName) {\r
+        Integer pIndex = (Integer)(itsVariableNames.get(pName));\r
+        if (pIndex != null) {\r
+            LocalVariable p = (LocalVariable)\r
+                                (itsVariables.elementAt(pIndex.intValue()));\r
+            if (p.isParameter()) {\r
+                String message = Context.getMessage1("msg.dup.parms", pName);\r
+                Context.reportWarning(message, null, 0, null, 0);\r
+            }\r
+            else {  // there's a local variable with this name, blow it off\r
+                itsVariables.removeElementAt(pIndex.intValue());\r
+            }\r
+        }\r
+        int curIndex = varStart++;\r
+        LocalVariable lVar = createLocalVariable(pName, true);\r
+        itsVariables.insertElementAt(lVar, curIndex);\r
+        itsVariableNames.put(pName, new Integer(curIndex));\r
+    }\r
+\r
+    public void addLocal(String vName) {\r
+        Integer vIndex = (Integer)(itsVariableNames.get(vName));\r
+        if (vIndex != null) {\r
+            // There's already a variable or parameter with this name.\r
+            return;\r
+        }\r
+        int index = itsVariables.size();\r
+        LocalVariable lVar = createLocalVariable(vName, false);\r
+        itsVariables.addElement(lVar);\r
+        itsVariableNames.put(vName, new Integer(index));\r
+    }\r
+    \r
+    // This should only be called very early in compilation\r
+    public void removeLocal(String name) {\r
+        Integer i = (Integer) itsVariableNames.get(name);\r
+        if (i != null) {\r
+            itsVariables.removeElementAt(i.intValue());\r
+            itsVariableNames.remove(name);\r
+            Hashtable ht = new Hashtable(11);\r
+            Enumeration e = itsVariableNames.keys();\r
+            while (e.hasMoreElements()) {\r
+                Object k = e.nextElement();\r
+                Integer v = (Integer) itsVariableNames.get(k);\r
+                int v2 = v.intValue();\r
+                if (v2 > i.intValue())\r
+                    v = new Integer(v2 - 1);\r
+                ht.put(k, v);\r
+            }\r
+            itsVariableNames = ht;\r
+        }\r
+    }\r
+    \r
+    // a list of the formal parameters and local variables\r
+    protected Vector itsVariables = new Vector();    \r
+\r
+    // mapping from name to index in list\r
+    protected Hashtable itsVariableNames = new Hashtable(11);   \r
+\r
+    protected int varStart;               // index in list of first variable\r
+\r
+}\r
diff --git a/src/org/mozilla/javascript/WrapHandler.java b/src/org/mozilla/javascript/WrapHandler.java
new file mode 100644 (file)
index 0000000..2f319ef
--- /dev/null
@@ -0,0 +1,68 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Marshall Cline\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * Embeddings that wish to provide their own custom wrappings for Java\r
+ * objects may implement this interface and call Context.setWrapHandler.\r
+ * @see org.mozilla.javascript.Context#setWrapHandler\r
+ */\r
+public interface WrapHandler {\r
+\r
+    /**\r
+     * Wrap the object.\r
+     * <p>\r
+     * The value returned must be one of \r
+     * <UL>\r
+     * <LI>java.lang.Boolean</LI>\r
+     * <LI>java.lang.String</LI>\r
+     * <LI>java.lang.Number</LI>\r
+     * <LI>org.mozilla.javascript.Scriptable objects</LI>\r
+     * <LI>The value returned by Context.getUndefinedValue()</LI>\r
+     * <LI>null</LI>\r
+     * <p>\r
+     * If null is returned, the value obj will be wrapped as if \r
+     * no WrapHandler had been called.\r
+     * </UL>\r
+     * @param scope the scope of the executing script\r
+     * @param obj the object to be wrapped\r
+     * @staticType the static type of the object to be wrapped\r
+     * @return the wrapped value.\r
+     */\r
+    public Object wrap(Scriptable scope, Object obj, Class staticType);\r
+}\r
diff --git a/src/org/mozilla/javascript/WrappedException.java b/src/org/mozilla/javascript/WrappedException.java
new file mode 100644 (file)
index 0000000..669ea07
--- /dev/null
@@ -0,0 +1,115 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+import java.lang.reflect.InvocationTargetException;\r
+\r
+/**\r
+ * A wrapper for runtime exceptions.\r
+ *\r
+ * Used by the JavaScript runtime to wrap and propagate exceptions that occur\r
+ * during runtime.\r
+ *\r
+ * @author Norris Boyd\r
+ */\r
+public class WrappedException extends EvaluatorException implements Wrapper {\r
+\r
+    /**\r
+     * Create a new exception wrapped around an existing exception.\r
+     *\r
+     * @param exception the exception to wrap\r
+     */\r
+    public WrappedException(Throwable exception) {\r
+        super(exception.getMessage());\r
+        this.exception = exception.fillInStackTrace();\r
+    }\r
+\r
+    /**\r
+     * Get the message for the exception.\r
+     *\r
+     * Delegates to the wrapped exception.\r
+     */\r
+    public String getMessage() {\r
+        return "WrappedException of " + exception.toString();\r
+    }\r
+\r
+    /**\r
+     * Gets the localized message.\r
+     *\r
+     * Delegates to the wrapped exception.\r
+     */\r
+    public String getLocalizedMessage() {\r
+        return "WrappedException of " + exception.getLocalizedMessage();\r
+    }\r
+\r
+    /**\r
+     * Get the wrapped exception.\r
+     *\r
+     * @return the exception that was presented as a argument to the\r
+     *         constructor when this object was created\r
+     */\r
+    public Throwable getWrappedException() {\r
+        return exception;\r
+    }\r
+\r
+    /**\r
+     * Get the wrapped exception.\r
+     *\r
+     * @return the exception that was presented as a argument to the\r
+     *         constructor when this object was created\r
+     */\r
+    public Object unwrap() {\r
+        return exception;\r
+    }\r
+\r
+    /**\r
+     * Wrap an exception.\r
+     *\r
+     * Provides special cases for EvaluatorExceptions (which are returned\r
+     * as-is), and InvocationTargetExceptions (which are unwrapped and\r
+     * passed to a recursive call to wrapException).<p>\r
+     *\r
+     * Otherwise the exception is simply wrapped in a WrappedException.\r
+     */\r
+    public static EvaluatorException wrapException(Throwable e) {\r
+        if ((e instanceof InvocationTargetException))\r
+            e = ((InvocationTargetException) e).getTargetException();\r
+        if (e instanceof EvaluatorException)\r
+            return (EvaluatorException) e;\r
+        return new WrappedException(e);\r
+    }\r
+\r
+    private Throwable exception;\r
+}\r
diff --git a/src/org/mozilla/javascript/Wrapper.java b/src/org/mozilla/javascript/Wrapper.java
new file mode 100644 (file)
index 0000000..ef9421c
--- /dev/null
@@ -0,0 +1,55 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript;\r
+\r
+/**\r
+ * Objects that can wrap other values for reflection in the JS environment\r
+ * will implement Wrapper. \r
+ * \r
+ * Wrapper defines a single method that can be called to unwrap the object.\r
+ */\r
+\r
+public interface Wrapper {\r
+\r
+    /**\r
+     * Unwrap the object by returning the wrapped value.\r
+     * \r
+     * @return a wrapped value\r
+     */\r
+    public Object unwrap();\r
+}\r
diff --git a/src/org/mozilla/javascript/debug/DebugFrame.java b/src/org/mozilla/javascript/debug/DebugFrame.java
new file mode 100644 (file)
index 0000000..5f7687c
--- /dev/null
@@ -0,0 +1,51 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript.debug;\r
+\r
+import org.mozilla.javascript.*;\r
+\r
+public interface DebugFrame {\r
+\r
+    public Scriptable getVariableObject();\r
+    \r
+    public String getSourceName();\r
+    \r
+    public int getLineNumber();\r
+    \r
+    public DebuggableScript getScript();\r
+}\r
diff --git a/src/org/mozilla/javascript/debug/DebugReader.java b/src/org/mozilla/javascript/debug/DebugReader.java
new file mode 100644 (file)
index 0000000..e88e46a
--- /dev/null
@@ -0,0 +1,101 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1998.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript.debug;\r
+\r
+import java.io.*;\r
+\r
+public class DebugReader extends Reader {\r
+\r
+    public DebugReader(Reader reader) {\r
+        this.reader = new BufferedReader(reader);  \r
+        this.saved = new StringBuffer();\r
+    }\r
+    \r
+    public StringBuffer getSaved() {\r
+        return saved;\r
+    }\r
+\r
+    public int read() throws IOException {\r
+        int c = reader.read();\r
+        if (c != -1)\r
+            saved.append((char)c);\r
+        return c;\r
+    }\r
+\r
+    public int read(char cbuf[]) throws IOException {\r
+        int i = reader.read(cbuf);\r
+        if (i != -1) \r
+            saved.append(cbuf, 0, i);\r
+        return i;\r
+    }\r
+\r
+    public int read(char cbuf[], int off, int len) throws IOException {\r
+        int i = reader.read(cbuf, off, len);\r
+        if (i > 0) \r
+            saved.append(cbuf, off, i);\r
+        return i;\r
+    }\r
+\r
+    public long skip(long n) throws IOException {\r
+        return reader.skip(n);\r
+    }\r
+\r
+    public boolean ready() throws IOException {\r
+        return reader.ready();\r
+    }\r
+\r
+    public boolean markSupported() {\r
+        return reader.markSupported();\r
+    }\r
+\r
+    public void mark(int readAheadLimit) throws IOException {\r
+        reader.mark(readAheadLimit);\r
+    }\r
+\r
+    public void reset() throws IOException {\r
+        reader.reset();\r
+    }\r
+\r
+    public void close() throws IOException {\r
+        reader.close();\r
+    }\r
+\r
+    protected void finalize() throws Throwable {\r
+        reader = null;    \r
+    }\r
+    \r
+    private BufferedReader reader;\r
+    private StringBuffer saved;\r
+}\r
diff --git a/src/org/mozilla/javascript/debug/DebuggableEngine.java b/src/org/mozilla/javascript/debug/DebuggableEngine.java
new file mode 100644 (file)
index 0000000..351cd8a
--- /dev/null
@@ -0,0 +1,92 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript.debug;\r
+\r
+\r
+public interface DebuggableEngine {\r
+\r
+    /**\r
+     * Set whether the engine should break when it encounters\r
+     * the next line.\r
+     * <p>\r
+     * The engine will call the attached debugger's handleBreakpointHit\r
+     * method on the next line it executes if isLineStep is true.\r
+     * May be used from another thread to interrupt execution.\r
+     * \r
+     * @param isLineStep if true, break next line\r
+     */\r
+    public void setBreakNextLine(boolean isLineStep);\r
+    \r
+    /**\r
+     * Return the value of the breakNextLine flag.\r
+     * @return true if the engine will break on execution of the \r
+     * next line.\r
+     */\r
+    public boolean getBreakNextLine();\r
+    \r
+    /**\r
+     * Set the associated debugger.\r
+     * @param debugger the debugger to be used on callbacks from\r
+     * the engine.\r
+     */\r
+    public void setDebugger(Debugger debugger);\r
+    \r
+    /**\r
+     * Return the current debugger.\r
+     * @return the debugger, or null if none is attached.\r
+     */\r
+    public Debugger getDebugger();\r
+    \r
+    /**\r
+     * Return the number of frames in current execution.\r
+     * @return the count of current frames\r
+     */\r
+    public int getFrameCount();\r
+    \r
+    /**\r
+     * Return a frame from the current execution.\r
+     * Frames are numbered starting from 0 for the innermost\r
+     * frame.\r
+     * @param frameNumber the number of the frame in the range\r
+     *        [0,frameCount-1]\r
+     * @return the relevant Frame, or null if frameNumber is out\r
+     *         of range or the engine isn't currently saving \r
+     *         frames\r
+     */\r
+    public DebugFrame getFrame(int frameNumber);\r
+}\r
diff --git a/src/org/mozilla/javascript/debug/DebuggableScript.java b/src/org/mozilla/javascript/debug/DebuggableScript.java
new file mode 100644 (file)
index 0000000..ff26115
--- /dev/null
@@ -0,0 +1,84 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript.debug;\r
+\r
+import org.mozilla.javascript.*;\r
+\r
+import java.util.Enumeration;\r
+\r
+/**\r
+ * This interface exposes debugging information from executable \r
+ * code (either functions or top-level scripts).\r
+ */\r
+public interface DebuggableScript {\r
+  \r
+    /**\r
+     * Returns true if this is a function, false if it is a script.\r
+     */\r
+    public boolean isFunction();\r
+    \r
+    /**\r
+     * Get the Scriptable object (Function or Script) that is \r
+     * described by this DebuggableScript object.\r
+     */\r
+    public Scriptable getScriptable();\r
+\r
+    /**\r
+     * Get the name of the source (usually filename or URL)\r
+     * of the script.\r
+     */\r
+    public String getSourceName();\r
+    \r
+    /**\r
+     * Get array containing the line numbers that \r
+     * can have breakpoints placed on them.\r
+     */\r
+    public int[] getLineNumbers();\r
+    \r
+    /**\r
+     * Place a breakpoint at the given line.\r
+     * @return true if the breakpoint was successfully set.\r
+     */\r
+    public boolean placeBreakpoint(int line);\r
+    \r
+    /**\r
+     * Remove a breakpoint from the given line.\r
+     * @return true if there was a breakpoint at the given line.\r
+     */\r
+    public boolean removeBreakpoint(int line);\r
+}\r
diff --git a/src/org/mozilla/javascript/debug/Debugger.java b/src/org/mozilla/javascript/debug/Debugger.java
new file mode 100644 (file)
index 0000000..bb0febb
--- /dev/null
@@ -0,0 +1,51 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+// API class\r
+\r
+package org.mozilla.javascript.debug;\r
+\r
+import org.mozilla.javascript.Context;\r
+\r
+public interface Debugger {\r
+    \r
+    void handleCompilationDone(Context cx, DebuggableScript fnOrScript, \r
+                               StringBuffer source);\r
+\r
+    void handleBreakpointHit(Context cx);\r
+    \r
+    void handleExceptionThrown(Context cx, Object exception);\r
+    \r
+}\r
diff --git a/src/org/mozilla/javascript/regexp/NativeRegExp.java b/src/org/mozilla/javascript/regexp/NativeRegExp.java
new file mode 100644 (file)
index 0000000..d8b6337
--- /dev/null
@@ -0,0 +1,2502 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1998.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Brendan Eich\r
+ * Matthias Radestock\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript.regexp;\r
+\r
+import org.mozilla.javascript.*;\r
+import java.lang.reflect.Method;\r
+\r
+/**\r
+ * This class implements the RegExp native object.\r
+ *\r
+ * Revision History:\r
+ * Implementation in C by Brendan Eich\r
+ * Initial port to Java by Norris Boyd from jsregexp.c version 1.36\r
+ * Merged up to version 1.38, which included Unicode support.\r
+ * Merged bug fixes in version 1.39.\r
+ * Merged JSFUN13_BRANCH changes up to 1.32.2.13\r
+ *\r
+ * @author Brendan Eich\r
+ * @author Norris Boyd\r
+ */\r
+public class NativeRegExp extends IdScriptable implements Function {\r
+\r
+    public static final int GLOB = 0x1;       // 'g' flag: global\r
+    public static final int FOLD = 0x2;       // 'i' flag: fold\r
+    public static final int MULTILINE = 0x4;  // 'm' flag: multiline\r
+\r
+    //type of match to perform\r
+    public static final int TEST = 0;\r
+    public static final int MATCH = 1;\r
+    public static final int PREFIX = 2;\r
+\r
+    private static final boolean debug = false;\r
+\r
+    public static void init(Context cx, Scriptable scope, boolean sealed) {\r
+        \r
+        NativeRegExp proto = new NativeRegExp();\r
+        proto.prototypeFlag = true;\r
+        proto.activateIdMap(MAX_PROTOTYPE_ID);\r
+        proto.setSealFunctionsFlag(sealed);\r
+        proto.setFunctionParametrs(cx);\r
+        proto.setParentScope(scope);\r
+        proto.setPrototype(getObjectPrototype(scope));\r
+\r
+\r
+        NativeRegExpCtor ctor = new NativeRegExpCtor();\r
+\r
+        ctor.setPrototype(getClassPrototype(scope, "Function"));\r
+        ctor.setParentScope(scope);\r
+\r
+        ctor.setImmunePrototypeProperty(proto);\r
+        \r
+        if (sealed) {\r
+            proto.sealObject();\r
+            ctor.sealObject();\r
+        }\r
+\r
+        defineProperty(scope, "RegExp", ctor, ScriptableObject.DONTENUM);\r
+    }\r
+\r
+    public NativeRegExp(Context cx, Scriptable scope, String source, \r
+                        String global, boolean flat) {\r
+        init(cx, scope, source, global, flat);\r
+    }\r
+\r
+    public void init(Context cx, Scriptable scope, String source, \r
+                     String global, boolean flat) {\r
+        this.source = source;\r
+        flags = 0;\r
+        if (global != null) {\r
+            for (int i=0; i < global.length(); i++) {\r
+                char c = global.charAt(i);\r
+                if (c == 'g') {\r
+                    flags |= GLOB;\r
+                } else if (c == 'i') {\r
+                    flags |= FOLD;\r
+                } else if (c == 'm') {\r
+                    flags |= MULTILINE;\r
+                } else {\r
+                    Object[] errArgs = { new Character(c) };\r
+                    throw NativeGlobal.constructError(cx, "SyntaxError",\r
+                                                      ScriptRuntime.getMessage(\r
+                                                                               "msg.invalid.re.flag", errArgs),\r
+                                                      scope);\r
+                }\r
+            }\r
+        }\r
+\r
+        CompilerState state = new CompilerState(source, flags, cx, scope);\r
+        if (flat) {\r
+            ren = null;\r
+            int sourceLen = source.length();\r
+            int index = 0;\r
+            while (sourceLen > 0) {\r
+                int len = sourceLen;\r
+                if (len > REOP_FLATLEN_MAX) {\r
+                    len = REOP_FLATLEN_MAX;\r
+                }\r
+                RENode ren2 = new RENode(state, len == 1 ? REOP_FLAT1 : REOP_FLAT,\r
+                                         new Integer(index));                                 \r
+                ren2.flags = RENode.NONEMPTY;\r
+                if (len > 1) {\r
+                    ren2.kid2 = index + len;\r
+                } else {\r
+                    ren2.flags |= RENode.SINGLE;\r
+                    ren2.chr = state.source[index];                    \r
+                }\r
+                index += len;\r
+                sourceLen -= len;\r
+                if (ren == null)\r
+                    ren = ren2;\r
+                else\r
+                    setNext(state, ren, ren2);\r
+            }\r
+        }\r
+        else\r
+            this.ren = parseRegExp(state);\r
+        if (ren == null) return;\r
+        RENode end = new RENode(state, REOP_END, null);\r
+        setNext(state, ren, end);\r
+        if (debug)\r
+            dumpRegExp(state, ren);\r
+        this.lastIndex = 0;\r
+        this.parenCount = state.parenCount;\r
+        this.flags = flags;\r
+\r
+        scope = org.xwt.util.JSObject.defaultObjects;\r
+        setPrototype(getClassPrototype(scope, "RegExp"));\r
+        setParentScope(scope);\r
+    }\r
+\r
+    public String getClassName() {\r
+        return "RegExp";\r
+    }\r
+\r
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj,\r
+                       Object[] args) {\r
+        return execSub(cx, scope, args, MATCH);\r
+    }\r
+\r
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args) {\r
+        return (Scriptable) call(cx, scope, null, args);\r
+    }\r
+\r
+    Scriptable compile(Context cx, Scriptable scope, Object[] args) {\r
+        if (args.length > 0 && args[0] instanceof NativeRegExp) {\r
+            if (args.length > 1 && args[1] != Undefined.instance) {\r
+                // report error\r
+                throw NativeGlobal.constructError(\r
+                                                  cx, "TypeError",\r
+                                                  "only one argument may be specified " +\r
+                                                  "if the first argument is a RegExp object",\r
+                                                  scope);\r
+            }\r
+            NativeRegExp thatObj = (NativeRegExp) args[0];\r
+            source = thatObj.source; \r
+            lastIndex = thatObj.lastIndex;\r
+            parenCount = thatObj.parenCount;\r
+            flags = thatObj.flags;\r
+            program = thatObj.program;\r
+            ren = thatObj.ren;\r
+            return this;\r
+        }\r
+        String s = args.length == 0 ? "" : ScriptRuntime.toString(args[0]);\r
+        String global = args.length > 1 && args[1] != Undefined.instance\r
+            ? ScriptRuntime.toString(args[1])\r
+            : null;\r
+        init(cx, scope, s, global, false);\r
+        return this;\r
+    }\r
+\r
+    public String toString() {\r
+        StringBuffer buf = new StringBuffer();\r
+        buf.append('/');\r
+        buf.append(source);\r
+        buf.append('/');\r
+        if ((flags & GLOB) != 0)\r
+            buf.append('g');\r
+        if ((flags & FOLD) != 0)\r
+            buf.append('i');\r
+        if ((flags & MULTILINE) != 0)\r
+            buf.append('m');\r
+        return buf.toString();\r
+    }\r
+\r
+    public NativeRegExp() {\r
+    }\r
+    \r
+    private static RegExpImpl getImpl(Context cx) {\r
+        return (RegExpImpl) ScriptRuntime.getRegExpProxy(cx);\r
+    }\r
+\r
+    private Object execSub(Context cx, Scriptable scopeObj,\r
+                           Object[] args, int matchType)\r
+    {\r
+        RegExpImpl reImpl = getImpl(cx);\r
+        String str;\r
+        if (args.length == 0) {\r
+            str = reImpl.input;\r
+            if (str == null) {\r
+                Object[] errArgs = { toString() };\r
+                throw NativeGlobal.constructError(\r
+                                                  cx, "SyntaxError",\r
+                                                  ScriptRuntime.getMessage\r
+                                                  ("msg.no.re.input.for", errArgs),\r
+                                                  scopeObj);\r
+            }\r
+        } else {\r
+            str = ScriptRuntime.toString(args[0]);\r
+        }\r
+        int i = ((flags & GLOB) != 0) ? lastIndex : 0;\r
+        int indexp[] = { i };\r
+        Object rval = executeRegExp(cx, scopeObj, \r
+                                    reImpl, str, indexp, matchType);\r
+        if ((flags & GLOB) != 0) {\r
+            lastIndex = (rval == null || rval == Undefined.instance) \r
+                        ? 0 : indexp[0];\r
+        }\r
+        return rval;\r
+    }\r
+\r
+    private Object exec(Context cx, Scriptable scopeObj, Object[] args) {\r
+        return execSub(cx, scopeObj, args, MATCH);\r
+    }\r
+\r
+    private Object test(Context cx, Scriptable scopeObj, Object[] args) {\r
+        Object rval = execSub(cx, scopeObj, args, TEST);\r
+        if (rval == null || !rval.equals(Boolean.TRUE))\r
+            rval = Boolean.FALSE;\r
+        return rval;\r
+    }\r
+\r
+    private Object prefix(Context cx, Scriptable scopeObj, Object[] args) {\r
+        return execSub(cx, scopeObj, args, PREFIX);\r
+    }\r
+\r
+    static final int JS_BITS_PER_BYTE = 8;\r
+\r
+    private static final byte REOP_EMPTY      = 0;  /* match rest of input against rest of r.e. */\r
+    private static final byte REOP_ALT        = 1;  /* alternative subexpressions in kid and next */\r
+    private static final byte REOP_BOL        = 2;  /* beginning of input (or line if multiline) */\r
+    private static final byte REOP_EOL        = 3;  /* end of input (or line if multiline) */\r
+    private static final byte REOP_WBDRY      = 4;  /* match "" at word boundary */\r
+    private static final byte REOP_WNONBDRY   = 5;  /* match "" at word non-boundary */\r
+    private static final byte REOP_QUANT      = 6;  /* quantified atom: atom{1,2} */\r
+    private static final byte REOP_STAR       = 7;  /* zero or more occurrences of kid */\r
+    private static final byte REOP_PLUS       = 8;  /* one or more occurrences of kid */\r
+    private static final byte REOP_OPT        = 9;  /* optional subexpression in kid */\r
+    private static final byte REOP_LPAREN     = 10; /* left paren bytecode: kid is u.num'th sub-regexp */\r
+    private static final byte REOP_RPAREN     = 11; /* right paren bytecode */\r
+    private static final byte REOP_DOT        = 12; /* stands for any character */\r
+    private static final byte REOP_CCLASS     = 13; /* character class: [a-f] */\r
+    private static final byte REOP_DIGIT      = 14; /* match a digit char: [0-9] */\r
+    private static final byte REOP_NONDIGIT   = 15; /* match a non-digit char: [^0-9] */\r
+    private static final byte REOP_ALNUM      = 16; /* match an alphanumeric char: [0-9a-z_A-Z] */\r
+    private static final byte REOP_NONALNUM   = 17; /* match a non-alphanumeric char: [^0-9a-z_A-Z] */\r
+    private static final byte REOP_SPACE      = 18; /* match a whitespace char */\r
+    private static final byte REOP_NONSPACE   = 19; /* match a non-whitespace char */\r
+    private static final byte REOP_BACKREF    = 20; /* back-reference (e.g., \1) to a parenthetical */\r
+    private static final byte REOP_FLAT       = 21; /* match a flat string */\r
+    private static final byte REOP_FLAT1      = 22; /* match a single char */\r
+    private static final byte REOP_JUMP       = 23; /* for deoptimized closure loops */\r
+    private static final byte REOP_DOTSTAR    = 24; /* optimize .* to use a single opcode */\r
+    private static final byte REOP_ANCHOR     = 25; /* like .* but skips left context to unanchored r.e. */\r
+    private static final byte REOP_EOLONLY    = 26; /* $ not preceded by any pattern */\r
+    private static final byte REOP_UCFLAT     = 27; /* flat Unicode string; len immediate counts chars */\r
+    private static final byte REOP_UCFLAT1    = 28; /* single Unicode char */\r
+    private static final byte REOP_UCCLASS    = 29; /* Unicode character class, vector of chars to match */\r
+    private static final byte REOP_NUCCLASS   = 30; /* negated Unicode character class */\r
+    private static final byte REOP_BACKREFi   = 31; /* case-independent REOP_BACKREF */\r
+    private static final byte REOP_FLATi      = 32; /* case-independent REOP_FLAT */\r
+    private static final byte REOP_FLAT1i     = 33; /* case-independent REOP_FLAT1 */\r
+    private static final byte REOP_UCFLATi    = 34; /* case-independent REOP_UCFLAT */\r
+    private static final byte REOP_UCFLAT1i   = 35; /* case-independent REOP_UCFLAT1 */\r
+    private static final byte REOP_ANCHOR1    = 36; /* first-char discriminating REOP_ANCHOR */\r
+    private static final byte REOP_NCCLASS    = 37; /* negated 8-bit character class */\r
+    private static final byte REOP_DOTSTARMIN = 38; /* ungreedy version of REOP_DOTSTAR */\r
+    private static final byte REOP_LPARENNON  = 39; /* non-capturing version of REOP_LPAREN */\r
+    private static final byte REOP_RPARENNON  = 40; /* non-capturing version of REOP_RPAREN */\r
+    private static final byte REOP_ASSERT     = 41; /* zero width positive lookahead assertion */\r
+    private static final byte REOP_ASSERT_NOT = 42; /* zero width negative lookahead assertion */\r
+    private static final byte REOP_END        = 43;\r
+\r
+    /* maximum length of FLAT string */\r
+    private static final int REOP_FLATLEN_MAX = 255;\r
+\r
+    /* not thread safe, used only for debugging */\r
+    private static int level;\r
+\r
+    private static String[] reopname = null;\r
+    static {\r
+        if (debug) {\r
+            String a[] = {\r
+                "empty",\r
+                "alt",\r
+                "bol",\r
+                "eol",\r
+                "wbdry",\r
+                "wnonbdry",\r
+                "quant",\r
+                "star",\r
+                "plus",\r
+                "opt",\r
+                "lparen",\r
+                "rparen",\r
+                "dot",\r
+                "cclass",\r
+                "digit",\r
+                "nondigit",\r
+                "alnum",\r
+                "nonalnum",\r
+                "space",\r
+                "nonspace",\r
+                "backref",\r
+                "flat",\r
+                "flat1",\r
+                "jump",\r
+                "dotstar",\r
+                "anchor",\r
+                "eolonly",\r
+                "ucflat",\r
+                "ucflat1",\r
+                "ucclass",\r
+                "nucclass",\r
+                "backrefi",\r
+                "flati",\r
+                "flat1i",\r
+                "ucflati",\r
+                "ucflat1i",\r
+                "anchor1",\r
+                "ncclass",\r
+                "dotstar_min",\r
+                "lparen_non",\r
+                "rparen_non",\r
+                "end"\r
+            };\r
+            reopname = a;\r
+        }\r
+    }\r
+\r
+    private String getPrintableString(String str) {\r
+        if (debug) {\r
+            StringBuffer buf = new StringBuffer(str.length());\r
+            for (int i = 0; i < str.length(); i++) {\r
+                int c = str.charAt(i);\r
+                if ((c < 0x20) || (c > 0x7F)) {\r
+                    if (c == '\n')\r
+                        buf.append("\\n");\r
+                    else\r
+                        buf.append("\\u" + Integer.toHexString(c));\r
+                }\r
+                else\r
+                    buf.append((char)c);\r
+            }\r
+            return buf.toString();\r
+        } else {\r
+            return "";\r
+        }\r
+    }\r
+\r
+    private void dumpRegExp(CompilerState state, RENode ren) {\r
+        if (debug) {\r
+            if (level == 0)\r
+                System.out.print("level offset  flags  description\n");\r
+            level++;\r
+            do {\r
+                char[] source = ren.s != null ? ren.s : state.source;\r
+                System.out.print(level);\r
+                System.out.print(" ");\r
+                System.out.print(ren.offset);\r
+                System.out.print(" " +\r
+                                 ((ren.flags & RENode.ANCHORED) != 0 ? "A" : "-") +\r
+                                 ((ren.flags & RENode.SINGLE)   != 0 ? "S" : "-") +\r
+                                 ((ren.flags & RENode.NONEMPTY) != 0 ? "F" : "-") + // F for full\r
+                                 ((ren.flags & RENode.ISNEXT)   != 0 ? "N" : "-") + // N for next\r
+                                 ((ren.flags & RENode.GOODNEXT) != 0 ? "G" : "-") +\r
+                                 ((ren.flags & RENode.ISJOIN)   != 0 ? "J" : "-") +\r
+                                 ((ren.flags & RENode.MINIMAL)  != 0 ? "M" : "-") +\r
+                                 "  " +\r
+                                 reopname[ren.op]);\r
+\r
+                switch (ren.op) {\r
+                case REOP_ALT:\r
+                    System.out.print(" ");\r
+                    System.out.println(ren.next.offset);\r
+                    dumpRegExp(state, (RENode) ren.kid);\r
+                    break;\r
+\r
+                case REOP_STAR:\r
+                case REOP_PLUS:\r
+                case REOP_OPT:\r
+                case REOP_ANCHOR1:\r
+                case REOP_ASSERT:\r
+                case REOP_ASSERT_NOT:\r
+                    System.out.println();\r
+                    dumpRegExp(state, (RENode) ren.kid);\r
+                    break;\r
+\r
+                case REOP_QUANT:\r
+                    System.out.print(" next ");\r
+                    System.out.print(ren.next.offset);\r
+                    System.out.print(" min ");\r
+                    System.out.print(ren.min);\r
+                    System.out.print(" max ");\r
+                    System.out.println(ren.max);\r
+                    dumpRegExp(state, (RENode) ren.kid);\r
+                    break;\r
+\r
+                case REOP_LPAREN:\r
+                    System.out.print(" num ");\r
+                    System.out.println(ren.num);\r
+                    dumpRegExp(state, (RENode) ren.kid);\r
+                    break;\r
+\r
+                case REOP_LPARENNON:\r
+                    System.out.println();\r
+                    dumpRegExp(state, (RENode) ren.kid);\r
+                   break;\r
+\r
+                case REOP_BACKREF:\r
+                case REOP_RPAREN:\r
+                    System.out.print(" num ");\r
+                    System.out.println(ren.num);\r
+                    break;\r
+\r
+                case REOP_CCLASS: {\r
+                    int index = ((Integer) ren.kid).intValue();\r
+                    int index2 = ren.kid2;\r
+                    int len = index2 - index;\r
+                    System.out.print(" [");\r
+                    System.out.print(getPrintableString(new String(source, index, len)));\r
+                    System.out.println("]");\r
+                    break;\r
+                    }\r
+\r
+                case REOP_FLAT: {\r
+                    int index = ((Integer) ren.kid).intValue();\r
+                    int index2 = ren.kid2;\r
+                    int len = index2 - index;\r
+                    System.out.print(" ");\r
+                    System.out.print(getPrintableString(new String(source, index, len)));\r
+                    System.out.print(" (");\r
+                    System.out.print(len);\r
+                    System.out.println(")");\r
+                    break;\r
+                    }\r
+\r
+                case REOP_FLAT1:\r
+                    System.out.print(" ");\r
+                    System.out.print(ren.chr);\r
+                    System.out.print(" ('\\");\r
+                    System.out.print(Integer.toString(ren.chr, 8));\r
+                    System.out.println("')");\r
+                    break;\r
+\r
+                case REOP_JUMP:\r
+                    System.out.print(" ");\r
+                    System.out.println(ren.next.offset);\r
+                    break;\r
+\r
+                case REOP_UCFLAT: {\r
+                    int index = ((Integer) ren.kid).intValue();\r
+                    int len = ren.kid2 - index;\r
+                    for (int i = 0; i < len; i++)\r
+                        System.out.print("\\u" + \r
+                                         Integer.toHexString(source[index+i]));\r
+                    System.out.println();\r
+                    break;\r
+                    }\r
+\r
+                case REOP_UCFLAT1:\r
+                    System.out.print("\\u" + \r
+                                     Integer.toHexString(ren.chr));\r
+                    System.out.println();\r
+                    break;\r
+\r
+                case REOP_UCCLASS: {\r
+                    int index = ((Integer) ren.kid).intValue();\r
+                    int len = ren.kid2 - index;\r
+                    System.out.print(" [");\r
+                    for (int i = 0; i < len; i++)\r
+                        System.out.print("\\u" + \r
+                                         Integer.toHexString(source[index+i]));\r
+                    System.out.println("]");\r
+                    break;\r
+                    }\r
+\r
+                default:\r
+                    System.out.println();\r
+                    break;\r
+                }\r
+\r
+                if ((ren.flags & RENode.GOODNEXT) == 0)\r
+                    break;\r
+            } while ((ren = ren.next) != null);\r
+            level--;\r
+        }\r
+    }\r
+\r
+    private void fixNext(CompilerState state, RENode ren1, RENode ren2,\r
+                         RENode oldnext) {\r
+        boolean goodnext;\r
+        RENode next, kid, ren;\r
+\r
+        goodnext = ren2 != null && (ren2.flags & RENode.ISNEXT) == 0;\r
+\r
+        /*\r
+         * Find the final node in a list of alternatives, or concatenations, or\r
+         * even a concatenation of alternatives followed by non-alternatives (e.g.\r
+         * ((x|y)z)w where ((x|y)z) is ren1 and w is ren2).\r
+         */\r
+        for (; (next = ren1.next) != null && next != oldnext; ren1 = next) {\r
+            if (ren1.op == REOP_ALT) {\r
+                /* Find the end of this alternative's operand list. */\r
+                kid = (RENode) ren1.kid;\r
+                if (kid.op == REOP_JUMP)\r
+                    continue;\r
+                for (ren = kid; ren.next != null; ren = ren.next) {\r
+                    if (ren.op == REOP_ALT)\r
+                        throw new RuntimeException("REOP_ALT not expected");\r
+                }\r
+\r
+                /* Append a jump node to all but the last alternative. */\r
+                ren.next = new RENode(state, REOP_JUMP, null);\r
+                ren.next.flags |= RENode.ISNEXT;\r
+                ren.flags |= RENode.GOODNEXT;\r
+\r
+                /* Recur to fix all descendent nested alternatives. */\r
+                fixNext(state, kid, ren2, oldnext);\r
+            }\r
+        }\r
+\r
+        /*\r
+         * Now ren1 points to the last alternative, or to the final node on a\r
+         * concatenation list.  Set its next link to ren2, flagging a join point\r
+         * if appropriate.\r
+         */\r
+        if (ren2 != null) {\r
+            if ((ren2.flags & RENode.ISNEXT) == 0)\r
+                ren2.flags |= RENode.ISNEXT;\r
+            else\r
+                ren2.flags |= RENode.ISJOIN;\r
+        }\r
+        ren1.next = ren2;\r
+        if (goodnext)\r
+            ren1.flags |= RENode.GOODNEXT;\r
+\r
+        /*\r
+         * The following ops have a kid subtree through which to recur.  Here is\r
+         * where we fix the next links under the final ALT node's kid.\r
+         */\r
+        switch (ren1.op) {\r
+        case REOP_ALT:\r
+        case REOP_QUANT:\r
+        case REOP_STAR:\r
+        case REOP_PLUS:\r
+        case REOP_OPT:\r
+        case REOP_LPAREN:\r
+        case REOP_LPARENNON:\r
+        case REOP_ASSERT:\r
+        case REOP_ASSERT_NOT:\r
+            fixNext(state, (RENode) ren1.kid, ren2, oldnext);\r
+            break;\r
+        default:;\r
+        }\r
+    }\r
+\r
+    private void setNext(CompilerState state, RENode ren1, RENode ren2) {\r
+        fixNext(state, ren1, ren2, null);\r
+    }\r
+\r
+    /*\r
+     * Top-down regular expression grammar, based closely on Perl 4.\r
+     *\r
+     *  regexp:     altern                  A regular expression is one or more\r
+     *              altern '|' regexp       alternatives separated by vertical bar.\r
+     */\r
+    private RENode parseRegExp(CompilerState state) {\r
+        RENode ren = parseAltern(state);\r
+        if (ren == null)\r
+            return null;\r
+        char[] source = state.source;\r
+        int index = state.index;\r
+        if (index < source.length && source[index] == '|') {\r
+            RENode kid = ren;\r
+            ren = new RENode(state, REOP_ALT, kid);\r
+            if (ren == null)\r
+                return null;\r
+            ren.flags = (byte) (kid.flags & (RENode.ANCHORED | RENode.NONEMPTY));\r
+            RENode ren1 = ren;\r
+            do {\r
+                /* (balance: */\r
+                state.index = ++index;\r
+                if (index < source.length && (source[index] == '|' ||\r
+                                              source[index] == ')'))\r
+                    {\r
+                        kid = new RENode(state, REOP_EMPTY, null);\r
+                    } else {\r
+                        kid = parseAltern(state);\r
+                        index = state.index;\r
+                    }\r
+                if (kid == null)\r
+                    return null;\r
+                RENode ren2 = new RENode(state, REOP_ALT, kid);\r
+                if (ren2 == null)\r
+                    return null;\r
+                ren1.next = ren2;\r
+                ren1.flags |= RENode.GOODNEXT;\r
+                ren2.flags = (byte) ((kid.flags & (RENode.ANCHORED |\r
+                                                   RENode.NONEMPTY))\r
+                                     | RENode.ISNEXT);\r
+                ren1 = ren2;\r
+            } while (index < source.length && source[index] == '|');\r
+        }\r
+        return ren;\r
+    }\r
+\r
+    /*\r
+     *  altern:     item                    An alternative is one or more items,\r
+     *              item altern             concatenated together.\r
+     */\r
+    private RENode parseAltern(CompilerState state) {\r
+        RENode ren = parseItem(state);\r
+        if (ren == null)\r
+            return null;\r
+        RENode ren1 = ren;\r
+        int flags = 0;\r
+        char[] source = state.source;\r
+        int index = state.index;\r
+        char c;\r
+        /* (balance: */\r
+        while (index != source.length && (c = source[index]) != '|' &&\r
+               c != ')')\r
+            {\r
+                RENode ren2 = parseItem(state);\r
+                if (ren2 == null)\r
+                    return null;\r
+                setNext(state, ren1, ren2);\r
+                flags |= ren2.flags;\r
+                ren1 = ren2;\r
+                index = state.index;\r
+            }\r
+\r
+        /*\r
+         * Propagate NONEMPTY to the front of a concatenation list, so that the\r
+         * first alternative in (^a|b) is considered non-empty.  The first node\r
+         * in a list may match the empty string (as ^ does), but if the list is\r
+         * non-empty, then the first node's NONEMPTY flag must be set.\r
+         */\r
+        ren.flags |= flags & RENode.NONEMPTY;\r
+        return ren;\r
+    }\r
+\r
+    /*\r
+     *  item:       assertion               An item is either an assertion or\r
+     *              quantatom               a quantified atom.\r
+     *\r
+     *  assertion:  '^'                     Assertions match beginning of string\r
+     *                                      (or line if the class static property\r
+     *                                      RegExp.multiline is true).\r
+     *              '$'                     End of string (or line if the class\r
+     *                                      static property RegExp.multiline is\r
+     *                                      true).\r
+     *              '\b'                    Word boundary (between \w and \W).\r
+     *              '\B'                    Word non-boundary.\r
+     */\r
+    RENode parseItem(CompilerState state) {\r
+        RENode ren;\r
+        byte op;\r
+\r
+        char[] source = state.source;\r
+        int index = state.index;\r
+        switch (index < source.length ? source[index] : '\0') {\r
+        case '^':\r
+            state.index = index + 1;\r
+            ren = new RENode(state, REOP_BOL, null);\r
+            ren.flags |= RENode.ANCHORED;\r
+            return ren;\r
+\r
+        case '$':\r
+            state.index = index + 1;\r
+            return new RENode(state,\r
+                              (index == state.indexBegin ||\r
+                               ((source[index-1] == '(' ||\r
+                                 source[index-1] == '|') && /*balance)*/\r
+                                (index - 1 == state.indexBegin ||\r
+                                 source[index-2] != '\\')))\r
+                              ? REOP_EOLONLY\r
+                              : REOP_EOL,\r
+                              null);\r
+\r
+        case '\\':\r
+            switch (++index < source.length ? source[index] : '\0') {\r
+            case 'b':\r
+                op = REOP_WBDRY;\r
+                break;\r
+            case 'B':\r
+                op = REOP_WNONBDRY;\r
+                break;\r
+            default:\r
+                return parseQuantAtom(state);\r
+            }\r
+\r
+            /*\r
+             * Word boundaries and non-boundaries are flagged as non-empty\r
+             * so they will be prefixed by an anchoring node.\r
+             */\r
+            state.index = index + 1;\r
+            ren = new RENode(state, op, null);\r
+            ren.flags |= RENode.NONEMPTY;\r
+            return ren;\r
+\r
+        default:;\r
+        }\r
+        return parseQuantAtom(state);\r
+    }\r
+\r
+    /*\r
+     *  quantatom:  atom                    An unquantified atom.\r
+     *              quantatom '{' n ',' m '}'\r
+     *                                      Atom must occur between n and m times.\r
+     *              quantatom '{' n ',' '}' Atom must occur at least n times.\r
+     *              quantatom '{' n '}'     Atom must occur exactly n times.\r
+     *              quantatom '*'           Zero or more times (same as {0,}).\r
+     *              quantatom '+'           One or more times (same as {1,}).\r
+     *              quantatom '?'           Zero or one time (same as {0,1}).\r
+     *\r
+     *              any of which can be optionally followed by '?' for ungreedy\r
+     */\r
+    RENode parseQuantAtom(CompilerState state) {\r
+        RENode ren = parseAtom(state);\r
+        if (ren == null)\r
+            return null;\r
+\r
+        int up;\r
+        char c;\r
+        RENode ren2;\r
+        int min, max;\r
+        char[] source = state.source;\r
+        int index = state.index;\r
+        loop:\r
+        while (index < source.length) {\r
+            switch (source[index]) {\r
+            case '{':\r
+                if (++index == source.length || !isDigit(c = source[index])) {\r
+                    reportError("msg.bad.quant",\r
+                                String.valueOf(source[state.index]), state);\r
+                    return null;\r
+                }\r
+                min = unDigit(c);\r
+                while (++index < source.length && isDigit(c = source[index])) {\r
+                    min = 10 * min + unDigit(c);\r
+                    if ((min >> 16) != 0) {\r
+                        reportError("msg.overlarge.max", tail(source, index),\r
+                                    state);\r
+                        return null;\r
+                    }\r
+                }\r
+                if (source[index] == ',') {\r
+                    up = ++index;\r
+                    if (isDigit(source[index])) {\r
+                        max = unDigit(source[index]);\r
+                        while (isDigit(c = source[++index])) {\r
+                            max = 10 * max + unDigit(c);\r
+                            if ((max >> 16) != 0) {\r
+                                reportError("msg.overlarge.max",\r
+                                            String.valueOf(source[up]), state);\r
+                                return null;\r
+                            }\r
+                        }\r
+                        if (max == 0) {\r
+                            reportError("msg.zero.quant",\r
+                                        tail(source, state.index), state);\r
+                            return null;\r
+                        }\r
+                        if (min > max) {\r
+                            reportError("msg.max.lt.min", tail(source, up), state);\r
+                            return null;\r
+                        }\r
+                    } else {\r
+                        /* 0 means no upper bound. */\r
+                        max = 0;\r
+                    }\r
+                } else {\r
+                    /* Exactly n times. */\r
+                    if (min == 0) {\r
+                        reportError("msg.zero.quant",\r
+                                    tail(source, state.index), state);\r
+                        return null;\r
+                    }\r
+                    max = min;\r
+                }\r
+                if (source[index] != '}') {\r
+                    reportError("msg.unterm.quant",\r
+                                String.valueOf(source[state.index]), state);\r
+                    return null;\r
+                }\r
+                index++;\r
+\r
+                ren2 = new RENode(state, REOP_QUANT, ren);\r
+                if (min > 0 && (ren.flags & RENode.NONEMPTY) != 0)\r
+                    ren2.flags |= RENode.NONEMPTY;\r
+                ren2.min = (short) min;\r
+                ren2.max = (short) max;\r
+                ren = ren2;\r
+                break;\r
+\r
+            case '*':\r
+                index++;\r
+                ren = new RENode(state, REOP_STAR, ren);\r
+                break;\r
+\r
+            case '+':\r
+                index++;\r
+                ren2 = new RENode(state, REOP_PLUS, ren);\r
+                if ((ren.flags & RENode.NONEMPTY) != 0)\r
+                    ren2.flags |= RENode.NONEMPTY;\r
+                ren = ren2;\r
+                break;\r
+\r
+            case '?':\r
+                index++;\r
+                ren = new RENode(state, REOP_OPT, ren);\r
+                break;\r
+\r
+            default:\r
+                break loop;\r
+            }\r
+            if ((index < source.length) && (source[index] == '?')) {\r
+                ren.flags |= RENode.MINIMAL;\r
+                index++;\r
+            }\r
+        }\r
+\r
+        state.index = index;\r
+        return ren;\r
+    }\r
+\r
+    /*\r
+     *  atom:       '(' regexp ')'          A parenthesized regexp (what matched\r
+     *                                      can be addressed using a backreference,\r
+     *                                      see '\' n below).\r
+     *              '.'                     Matches any char except '\n'.\r
+     *              '[' classlist ']'       A character class.\r
+     *              '[' '^' classlist ']'   A negated character class.\r
+     *              '\f'                    Form Feed.\r
+     *              '\n'                    Newline (Line Feed).\r
+     *              '\r'                    Carriage Return.\r
+     *              '\t'                    Horizontal Tab.\r
+     *              '\v'                    Vertical Tab.\r
+     *              '\d'                    A digit (same as [0-9]).\r
+     *              '\D'                    A non-digit.\r
+     *              '\w'                    A word character, [0-9a-z_A-Z].\r
+     *              '\W'                    A non-word character.\r
+     *              '\s'                    A whitespace character, [ \b\f\n\r\t\v].\r
+     *              '\S'                    A non-whitespace character.\r
+     *              '\' n                   A backreference to the nth (n decimal\r
+     *                                      and positive) parenthesized expression.\r
+     *              '\' octal               An octal escape sequence (octal must be\r
+     *                                      two or three digits long, unless it is\r
+     *                                      0 for the null character).\r
+     *              '\x' hex                A hex escape (hex must be two digits).\r
+     *              '\c' ctrl               A control character, ctrl is a letter.\r
+     *              '\' literalatomchar     Any character except one of the above\r
+     *                                      that follow '\' in an atom.\r
+     *              otheratomchar           Any character not first among the other\r
+     *                                      atom right-hand sides.\r
+     */\r
+    static final String metachars    = "|^${*+?().[\\";\r
+    static final String closurechars = "{*+?";\r
+\r
+    RENode parseAtom(CompilerState state) {\r
+        int num = 0, len;\r
+        RENode ren = null;\r
+        RENode ren2;\r
+        char c;\r
+        byte op;\r
+\r
+        boolean skipCommon = false;\r
+        boolean doFlat = false;\r
+\r
+        char[] source = state.source;\r
+        int index = state.index;\r
+        int ocp = index;\r
+        if (index == source.length) {\r
+            state.index = index;\r
+            return new RENode(state, REOP_EMPTY, null);\r
+        }\r
+        switch (source[index]) {\r
+            /* handle /|a/ by returning an empty node for the leftside */\r
+        case '|':\r
+            return new RENode(state, REOP_EMPTY, null);\r
+\r
+        case '(':\r
+            op = REOP_END;\r
+            if (source[index + 1] == '?') {\r
+                switch (source[index + 2]) {\r
+                case ':' :\r
+                    op = REOP_LPARENNON;\r
+                    break;\r
+                case '=' :\r
+                    op = REOP_ASSERT;\r
+                    break;\r
+                case '!' :\r
+                    op = REOP_ASSERT_NOT;\r
+                    break;\r
+                }\r
+            }\r
+            if (op == REOP_END) {\r
+                op = REOP_LPAREN;\r
+                num = state.parenCount++;      /* \1 is numbered 0, etc. */\r
+                index++;\r
+            }\r
+            else\r
+                index += 3;\r
+            state.index = index;\r
+            /* Handle empty paren */\r
+            if (source[index] == ')') {\r
+                ren2 = new RENode(state, REOP_EMPTY, null);\r
+            }\r
+            else {\r
+                ren2 = parseRegExp(state);\r
+                if (ren2 == null)\r
+                    return null;\r
+                index = state.index;\r
+                if (index >= source.length || source[index] != ')') {\r
+                    reportError("msg.unterm.paren", tail(source, ocp), state);\r
+                    return null;\r
+                }\r
+            }\r
+            index++;\r
+            ren = new RENode(state, op, ren2);\r
+            ren.flags = (byte) (ren2.flags & (RENode.ANCHORED |\r
+                                              RENode.NONEMPTY));\r
+            ren.num = num;\r
+            if ((op == REOP_LPAREN) || (op == REOP_LPARENNON)) {\r
+                /* Assume RPAREN ops immediately succeed LPAREN ops */\r
+                ren2 = new RENode(state, (byte)(op + 1), null);\r
+                setNext(state, ren, ren2);\r
+                ren2.num = num;\r
+            }\r
+            break;\r
+\r
+        case '.':\r
+            ++index;\r
+            op = REOP_DOT;\r
+            if ((index < source.length) && (source[index] == '*')) {\r
+               index++;\r
+                op = REOP_DOTSTAR;\r
+                if ((index < source.length) && (source[index] == '?')) {\r
+                    index++;\r
+                    op = REOP_DOTSTARMIN;\r
+                }\r
+            }\r
+            ren = new RENode(state, op, null);\r
+            if (ren.op == REOP_DOT)\r
+                ren.flags = RENode.SINGLE | RENode.NONEMPTY;\r
+            break;\r
+\r
+        case '[':\r
+            /* A char class must have at least one char in it. */\r
+            if (++index == source.length) {\r
+                reportError("msg.unterm.class", tail(source, ocp), state);\r
+                return null;\r
+            }\r
+            c = source[index];\r
+            ren = new RENode(state, REOP_CCLASS, new Integer(index));\r
+\r
+            /* A negated class must have at least one char in it after the ^. */\r
+            if (c == '^' && ++index == source.length) {\r
+                reportError("msg.unterm.class", tail(source, ocp), state);\r
+                return null;\r
+            }\r
+\r
+            for (;;) {\r
+                if (++index == source.length) {\r
+                    reportError("msg.unterm.paren", tail(source, ocp), state);\r
+                    return null;\r
+                }\r
+                c = source[index];\r
+                if (c == ']')\r
+                    break;\r
+                if (c == '\\' && index+1 != source.length)\r
+                    index++;\r
+            }\r
+            ren.kid2 = index++;\r
+\r
+            /* Since we rule out [] and [^], we can set the non-empty flag. */\r
+            ren.flags = RENode.SINGLE | RENode.NONEMPTY;\r
+            break;\r
+\r
+        case '\\':\r
+            if (++index == source.length) {\r
+                Context.reportError(ScriptRuntime.getMessage("msg.trail.backslash",\r
+                                                             null));\r
+                return null;\r
+            }\r
+            c = source[index];\r
+            switch (c) {\r
+            case 'f':\r
+            case 'n':\r
+            case 'r':\r
+            case 't':\r
+            case 'v':\r
+                c = getEscape(c);\r
+                ren = new RENode(state, REOP_FLAT1, null);\r
+                break;\r
+            case 'd':\r
+                ren = new RENode(state, REOP_DIGIT, null);\r
+                break;\r
+            case 'D':\r
+                ren = new RENode(state, REOP_NONDIGIT, null);\r
+                break;\r
+            case 'w':\r
+                ren = new RENode(state, REOP_ALNUM, null);\r
+                break;\r
+            case 'W':\r
+                ren = new RENode(state, REOP_NONALNUM, null);\r
+                break;\r
+            case 's':\r
+                ren = new RENode(state, REOP_SPACE, null);\r
+                break;\r
+            case 'S':\r
+                ren = new RENode(state, REOP_NONSPACE, null);\r
+                break;\r
+\r
+            case '0':\r
+            case '1':\r
+            case '2':\r
+            case '3':\r
+            case '4':\r
+            case '5':\r
+            case '6':\r
+            case '7':\r
+            case '8':\r
+            case '9':\r
+                /*\r
+                  Yuk. Keeping the old style \n interpretation for 1.2\r
+                  compatibility.\r
+                */\r
+                if ((state.cx.getLanguageVersion() != Context.VERSION_DEFAULT)\r
+                    && (state.cx.getLanguageVersion() <= Context.VERSION_1_4)) {\r
+                    switch (c) {                    \r
+                    case '0':\r
+                        state.index = index;\r
+                        num = doOctal(state);\r
+                        index = state.index;\r
+                        ren = new RENode(state, REOP_FLAT1, null);\r
+                        c = (char) num;\r
+                        break;\r
+\r
+                    case '1':\r
+                    case '2':\r
+                    case '3':\r
+                    case '4':\r
+                    case '5':\r
+                    case '6':\r
+                    case '7':\r
+                    case '8':\r
+                    case '9':\r
+                        num = unDigit(c);\r
+                        len = 1;\r
+                        while (++index < source.length\r
+                               && isDigit(c = source[index])) {\r
+                            num = 10 * num + unDigit(c);\r
+                            len++;\r
+                        }\r
+                        /* n in [8-9] and > count of parenetheses, then revert to\r
+                           '8' or '9', ignoring the '\' */\r
+                        if (((num == 8) || (num == 9)) && (num > state.parenCount)) {\r
+                            ocp = --index;  /* skip beyond the '\' */\r
+                            doFlat = true;\r
+                            skipCommon = true;\r
+                            break;                        \r
+                        }\r
+                        /* more than 1 digit, or a number greater than\r
+                           the count of parentheses => it's an octal */\r
+                        if ((len > 1) || (num > state.parenCount)) {\r
+                            state.index = ocp;\r
+                            num = doOctal(state);\r
+                            index = state.index;\r
+                            ren = new RENode(state, REOP_FLAT1, null);\r
+                            c = (char) num;\r
+                            break;\r
+                        }\r
+                        index--;\r
+                        ren = new RENode(state, REOP_BACKREF, null);\r
+                        ren.num = num - 1;       /* \1 is numbered 0, etc. */\r
+\r
+                        /* Avoid common chr- and flags-setting \r
+                           code after switch. */\r
+                        ren.flags = RENode.NONEMPTY;\r
+                        skipCommon = true;\r
+                        break;\r
+                    }\r
+                }\r
+                else {\r
+                    if (c == '0') {\r
+                        ren = new RENode(state, REOP_FLAT1, null);\r
+                        c = 0;\r
+                    }\r
+                    else {\r
+                        num = unDigit(c);\r
+                        len = 1;\r
+                        while (++index < source.length\r
+                               && isDigit(c = source[index])) {\r
+                            num = 10 * num + unDigit(c);\r
+                            len++;\r
+                        }\r
+                        index--;\r
+                        ren = new RENode(state, REOP_BACKREF, null);\r
+                        ren.num = num - 1;       /* \1 is numbered 0, etc. */\r
+                        /* Avoid common chr- and flags-setting \r
+                           code after switch. */\r
+                        ren.flags = RENode.NONEMPTY;\r
+                        skipCommon = true;\r
+                    }\r
+                }\r
+                break;\r
+                \r
+            case 'x':\r
+                ocp = index;\r
+                if (++index < source.length && isHex(c = source[index])) {\r
+                    num = unHex(c);\r
+                    if (++index < source.length && isHex(c = source[index])) {\r
+                        num <<= 4;\r
+                        num += unHex(c);\r
+                    } else {\r
+                        if ((state.cx.getLanguageVersion()\r
+                             != Context.VERSION_DEFAULT)\r
+                            && (state.cx.getLanguageVersion() \r
+                                <= Context.VERSION_1_4)) \r
+                            index--; /* back up so index points to last hex char */\r
+                        else { /* ecma 2 requires pairs of hex digits. */\r
+                            index = ocp;\r
+                            num = 'x';\r
+                        }\r
+                    }\r
+                } else {\r
+                    index = ocp;       /* \xZZ is xZZ (Perl does \0ZZ!) */\r
+                    num = 'x';\r
+                }\r
+                ren = new RENode(state, REOP_FLAT1, null);\r
+                c = (char)num;\r
+                break;\r
+\r
+            case 'c':\r
+                c = source[++index];\r
+                if (!('A' <= c && c <= 'Z') && !('a' <= c && c <= 'z')) {\r
+                    index -= 2;\r
+                    ocp = index;\r
+                    doFlat = true;\r
+                    skipCommon = true;\r
+                    break;\r
+                }\r
+                c = Character.toUpperCase(c);\r
+                c = (char) (c ^ 64); // JS_TOCTRL\r
+                ren = new RENode(state, REOP_FLAT1, null);\r
+                break;\r
+\r
+            case 'u':\r
+                if (index+4 < source.length &&\r
+                    isHex(source[index+1]) && isHex(source[index+2]) &&\r
+                    isHex(source[index+3]) && isHex(source[index+4]))\r
+                    {\r
+                        num = (((((unHex(source[index+1]) << 4) +\r
+                                  unHex(source[index+2])) << 4) +\r
+                                unHex(source[index+3])) << 4) +\r
+                            unHex(source[index+4]);\r
+                        c = (char) num;\r
+                        index += 4;\r
+                        ren = new RENode(state, REOP_FLAT1, null);\r
+                        break;\r
+                    }\r
+\r
+                /* Unlike Perl \\xZZ, we take \\uZZZ to be literal-u then ZZZ. */\r
+                ocp = index;\r
+                doFlat = true;\r
+                skipCommon = true;\r
+                break;\r
+\r
+            default:\r
+                ocp = index;\r
+                doFlat = true;\r
+                skipCommon = true;\r
+                break;\r
+            }\r
+\r
+            /* Common chr- and flags-setting code for escape opcodes. */\r
+            if (ren != null && !skipCommon) {\r
+                ren.chr = c;\r
+                ren.flags = RENode.SINGLE | RENode.NONEMPTY;\r
+            }\r
+            skipCommon = false;\r
+\r
+            if (!doFlat) {\r
+                /* Skip to next unparsed char. */\r
+                index++;\r
+                break;\r
+            }\r
+\r
+            /* fall through since doFlat was true */\r
+            doFlat = false;\r
+\r
+        default:\r
+            while (++index != source.length &&\r
+                   metachars.indexOf(source[index]) == -1)\r
+                ;\r
+            len = (int)(index - ocp);\r
+            if (index != source.length && len > 1 &&\r
+                closurechars.indexOf(source[index]) != -1)\r
+                {\r
+                    index--;\r
+                    len--;\r
+                }\r
+            if (len > REOP_FLATLEN_MAX) {\r
+                len = REOP_FLATLEN_MAX;\r
+                index = ocp + len;\r
+            }\r
+            ren = new RENode(state, len == 1 ? REOP_FLAT1 : REOP_FLAT,\r
+                             new Integer(ocp));\r
+            ren.flags = RENode.NONEMPTY;\r
+            if (len > 1) {\r
+                ren.kid2 = index;\r
+            } else {\r
+                ren.flags |= RENode.SINGLE;\r
+                ren.chr = source[ocp];\r
+            }\r
+            break;\r
+        }\r
+\r
+        state.index = index;\r
+        return ren;\r
+    }\r
+\r
+    private int doOctal(CompilerState state) {\r
+        char[] source = state.source;\r
+        int index = state.index;\r
+        int tmp, num = 0;\r
+        char c;\r
+        while (++index < source.length && '0' <= (c = source[index]) &&\r
+               c <= '7')\r
+            {\r
+                tmp = 8 * num + (int)(c - '0');\r
+                if (tmp > 0377) {\r
+                    break;\r
+                }\r
+                num = tmp;\r
+            }\r
+        index--;\r
+        state.index = index;\r
+        return num;\r
+    }\r
+\r
+    static char getEscape(char c) {\r
+        switch (c) {\r
+        case 'b':\r
+            return '\b';\r
+        case 'f':\r
+            return '\f';\r
+        case 'n':\r
+            return '\n';\r
+        case 'r':\r
+            return '\r';\r
+        case 't':\r
+            return '\t';\r
+        case 'v':\r
+            return (char) 11; // '\v' is not vtab in Java\r
+        }\r
+        throw new RuntimeException();\r
+    }\r
+\r
+    static public boolean isDigit(char c) {\r
+        return '0' <= c && c <= '9';\r
+    }\r
+\r
+    static int unDigit(char c) {\r
+        return c - '0';\r
+    }\r
+\r
+    static boolean isHex(char c) {\r
+        return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') ||\r
+            ('A' <= c && c <= 'F');\r
+    }\r
+\r
+    static int unHex(char c) {\r
+        if ('0' <= c && c <= '9')\r
+            return c - '0';\r
+        return 10 + Character.toLowerCase(c) - 'a';\r
+    }\r
+\r
+    static boolean isWord(char c) {\r
+        return Character.isLetter(c) || isDigit(c) || c == '_';\r
+    }\r
+\r
+    private String tail(char[] a, int i) {\r
+        return new String(a, i, a.length - i);\r
+    }\r
+\r
+    private static boolean matchChar(int flags, char c, char c2) {\r
+        if (c == c2)\r
+            return true;\r
+        else\r
+            if ((flags & FOLD) != 0) {\r
+                c = Character.toUpperCase(c);\r
+                c2 = Character.toUpperCase(c2);\r
+                return c == c2 ||\r
+                    Character.toLowerCase(c) == Character.toLowerCase(c2);\r
+            }\r
+            else\r
+                return false;\r
+    }\r
+    \r
+    \r
+    int greedyRecurse(GreedyState grState, int index, int previousKid) {\r
+        int kidMatch;\r
+        int match;\r
+        int num;\r
+\r
+       /*\r
+         *    when the kid match fails, we reset the parencount and run any \r
+         *    previously succesful kid in order to restablish it's paren\r
+         *    contents.\r
+         */\r
+\r
+        num = grState.state.parenCount;\r
+        kidMatch = matchRENodes(grState.state, grState.kid, grState.next, index);\r
+        if (kidMatch == -1) {\r
+            match = matchRENodes(grState.state, grState.next, grState.stop, index);\r
+            if (match != -1) {\r
+                grState.state.parenCount = num;\r
+                if (previousKid != -1)\r
+                    matchRENodes(grState.state, grState.kid, grState.next, previousKid);\r
+                return index;\r
+            }\r
+            else\r
+                return -1;\r
+        }\r
+        else {\r
+            if (kidMatch == index) {\r
+                if (previousKid != -1)\r
+                    matchRENodes(grState.state, grState.kid, grState.next, previousKid);\r
+                return kidMatch;    /* no point pursuing an empty match forever */\r
+            }\r
+            if ((grState.maxKid == 0) || (++grState.kidCount < grState.maxKid)) {\r
+                match = greedyRecurse(grState, kidMatch, index);\r
+                if (match != -1) return match;\r
+                if (grState.maxKid != 0) --grState.kidCount;\r
+            }\r
+            grState.state.parenCount = num;\r
+            match = matchRENodes(grState.state, grState.next, grState.stop, kidMatch);\r
+            if (match != -1) {\r
+                matchRENodes(grState.state, grState.kid, grState.next, index);\r
+                return kidMatch;\r
+            }\r
+            else\r
+                return -1;\r
+        }\r
+    }\r
+\r
+    int matchGreedyKid(MatchState state, RENode ren, RENode stop,\r
+                       int kidCount, int index, int previousKid) {\r
+        GreedyState grState = new GreedyState();\r
+        grState.state = state;\r
+        grState.kid = (RENode)ren.kid;\r
+        grState.next = ren.next;\r
+        grState.maxKid = (ren.op == REOP_QUANT) ? ren.max : 0;\r
+        /*\r
+         * We try to match the sub-tree to completion first, and if that\r
+         * doesn't work, match only up to the given end of the sub-tree.\r
+         */\r
+        grState.stop = null;\r
+        grState.kidCount = kidCount;\r
+        int match = greedyRecurse(grState, index, previousKid);\r
+        if (match != -1 || stop == null) return match;\r
+        grState.kidCount = kidCount;\r
+        grState.stop = stop;\r
+        return greedyRecurse(grState, index, previousKid);\r
+    }\r
+\r
+    int matchNonGreedyKid(MatchState state, RENode ren,\r
+                          int kidCount, int maxKid,\r
+                          int index) {\r
+        int kidMatch;\r
+        int match;\r
+\r
+        match = matchRENodes(state, ren.next, null, index);\r
+        if (match != -1) return index;\r
+        kidMatch = matchRENodes(state, (RENode)ren.kid, ren.next, index);\r
+        if (kidMatch == -1) return -1;\r
+        if (kidMatch == index) return kidMatch;    /* no point pursuing an empty match forever */\r
+        return matchNonGreedyKid(state, ren, kidCount, maxKid, kidMatch);\r
+    }\r
+\r
+    int matchRENodes(MatchState state, RENode ren, RENode stop, int index) {\r
+        int num;\r
+        char[] input = state.input;\r
+        while ((ren != stop) && (ren != null)) {\r
+                switch (ren.op) {\r
+                case REOP_EMPTY:\r
+                    break;\r
+                case REOP_ALT: {\r
+                    if (ren.next.op != REOP_ALT) {\r
+                        ren = (RENode)ren.kid;\r
+                        continue;\r
+                    }\r
+                    else {\r
+                        num = state.parenCount;\r
+                        int kidMatch = matchRENodes(state, (RENode)ren.kid,\r
+                                                    stop, index);\r
+                        if (kidMatch != -1) return kidMatch;\r
+                        for (int i = num; i < state.parenCount; i++)\r
+                            state.parens[i] = null;\r
+                        state.parenCount = num;\r
+                    }\r
+                }\r
+                    break;\r
+                case REOP_QUANT: {\r
+                    int lastKid = -1;\r
+                    for (num = 0; num < ren.min; num++) {\r
+                        int kidMatch = matchRENodes(state, (RENode)ren.kid,\r
+                                                    ren.next, index);\r
+                        if (kidMatch == -1)\r
+                            return -1;\r
+                        else {\r
+                            lastKid = index;\r
+                            index = kidMatch;\r
+                        }\r
+                    }\r
+                    if (num == ren.max)\r
+                        // Have matched the exact count required, \r
+                        // need to match the rest of the regexp.\r
+                        break;\r
+                    if ((ren.flags & RENode.MINIMAL) == 0) {\r
+                        int kidMatch = matchGreedyKid(state, ren, stop, num,\r
+                                                      index, lastKid);\r
+                        if (kidMatch == -1)\r
+                            index = matchRENodes(state, (RENode)ren.kid,\r
+                                                 ren.next, index);\r
+                        else \r
+                            index = kidMatch;\r
+                    }        \r
+                    else {\r
+                        index = matchNonGreedyKid(state, ren, num,\r
+                                                  ren.max, index);\r
+                    }                          \r
+                    if (index == -1) return -1;\r
+                }\r
+                    break;\r
+                case REOP_PLUS: {\r
+                    int kidMatch = matchRENodes(state, (RENode)ren.kid, \r
+                                                ren.next, index);\r
+                    if (kidMatch == -1)\r
+                        return -1;\r
+                    if ((ren.flags & RENode.MINIMAL) == 0) {\r
+                        kidMatch = matchGreedyKid(state, ren, stop, 1, \r
+                                                  kidMatch, index);\r
+                        if (kidMatch == -1)\r
+                            index = matchRENodes(state,(RENode)ren.kid,\r
+                                                 ren.next, index);\r
+                        else\r
+                            index = kidMatch;\r
+                    }\r
+                    else\r
+                        index = matchNonGreedyKid(state, ren, 1, 0, kidMatch);\r
+                    if (index == -1) return -1;\r
+                }\r
+                    break;\r
+                case REOP_STAR:                                        \r
+                    if ((ren.flags & RENode.MINIMAL) == 0) {\r
+                        int kidMatch = matchGreedyKid(state, ren, stop, 0, index, -1);\r
+                        if (kidMatch != -1)\r
+                            index = kidMatch;\r
+                    }\r
+                    else {\r
+                        index = matchNonGreedyKid(state, ren, 0, 0, index);\r
+                        if (index == -1) return -1;\r
+                    }\r
+                    break;\r
+                case REOP_OPT: {\r
+                    int saveNum = state.parenCount;\r
+                    if (((ren.flags & RENode.MINIMAL) != 0)) {\r
+                        int restMatch = matchRENodes(state, ren.next,\r
+                                                     stop, index);\r
+                        if (restMatch != -1) return restMatch;\r
+                    }\r
+                    int kidMatch = matchRENodes(state, (RENode)ren.kid,\r
+                                                ren.next, index);\r
+                    if (kidMatch == -1) {\r
+                        state.parenCount = saveNum;\r
+                        break;\r
+                    }\r
+                    else {\r
+                        int restMatch = matchRENodes(state, ren.next,\r
+                                                     stop, kidMatch);\r
+                        if (restMatch == -1) {\r
+                            // need to undo the result of running the kid\r
+                            state.parenCount = saveNum;\r
+                            break;\r
+                        }\r
+                        else\r
+                            return restMatch;\r
+                    }\r
+                }\r
+                case REOP_LPARENNON:\r
+                    ren = (RENode)ren.kid;\r
+                    continue;\r
+                case REOP_RPARENNON:\r
+                    break;\r
+                case REOP_LPAREN: {\r
+                    num = ren.num;\r
+                    ren = (RENode)ren.kid;\r
+                    SubString parsub = state.parens[num];\r
+                    if (parsub == null) {\r
+                        parsub = state.parens[num] = new SubString();\r
+                        parsub.charArray = input;\r
+                    }\r
+                    parsub.index = index;\r
+                    parsub.length = 0;\r
+                    if (num >= state.parenCount)\r
+                        state.parenCount = num + 1;\r
+                    continue;\r
+                }\r
+                case REOP_RPAREN: {\r
+                    num = ren.num;\r
+                    SubString parsub = state.parens[num];\r
+                    if (parsub == null)\r
+                        throw new RuntimeException("Paren problem");\r
+                    parsub.length = index - parsub.index;\r
+                    break;\r
+                }\r
+                case REOP_ASSERT: {\r
+                    int kidMatch = matchRENodes(state, (RENode)ren.kid,\r
+                                                ren.next, index);\r
+                    if (kidMatch == -1) return -1;\r
+                    break;\r
+                }\r
+                case REOP_ASSERT_NOT: {\r
+                    int kidMatch = matchRENodes(state, (RENode)ren.kid,\r
+                                                ren.next, index);\r
+                    if (kidMatch != -1) return -1;\r
+                    break;\r
+                }\r
+                case REOP_BACKREF: {\r
+                    num = ren.num;\r
+                    if (num >= state.parens.length) {\r
+                        Context.reportError(\r
+                                            ScriptRuntime.getMessage(\r
+                                                                     "msg.bad.backref", null));\r
+                        return -1;\r
+                    }\r
+                    SubString parsub = state.parens[num];\r
+                    if (parsub == null)\r
+                        parsub = state.parens[num] = new SubString();\r
+                    int length = parsub.length;\r
+                    for (int i = 0; i < length; i++, index++) {\r
+                        if (index >= input.length) {\r
+                            return state.noMoreInput();\r
+                        }\r
+                        if (!matchChar(state.flags, input[index],\r
+                                       parsub.charArray[parsub.index + i]))\r
+                            return -1;\r
+                    }\r
+                }\r
+                    break;\r
+                case REOP_CCLASS:\r
+                    if (index >= input.length) {\r
+                        return state.noMoreInput();\r
+                    }\r
+                    if (ren.bitmap == null) {\r
+                        char[] source = (ren.s != null) \r
+                            ? ren.s \r
+                            : this.source.toCharArray();\r
+                        ren.buildBitmap(state, source, ((state.flags & FOLD) != 0));\r
+                    }\r
+                    char c = input[index];\r
+                    int b = (c >>> 3);\r
+                    if (b >= ren.bmsize) {\r
+                        if (ren.kid2 == -1) // a ^ class\r
+                            index++;\r
+                        else\r
+                            return -1;\r
+                    } else {\r
+                        int bit = c & 7;\r
+                        bit = 1 << bit;\r
+                        if ((ren.bitmap[b] & bit) != 0)\r
+                            index++;\r
+                        else\r
+                            return -1;\r
+                    }\r
+                    break;\r
+                case REOP_DOT:\r
+                    if (index >= input.length) {\r
+                        return state.noMoreInput();\r
+                    }\r
+                    if (input[index] != '\n')\r
+                        index++;\r
+                    else\r
+                        return -1;\r
+                    break;\r
+                case REOP_DOTSTARMIN: {\r
+                    int cp2;\r
+                    for (cp2 = index; cp2 < input.length; cp2++) {\r
+                        int cp3 = matchRENodes(state, ren.next, stop, cp2);\r
+                        if (cp3 != -1) return cp3;\r
+                        if (input[cp2] == '\n')\r
+                            return -1;\r
+                    }\r
+                    return state.noMoreInput();\r
+                }\r
+                case REOP_DOTSTAR: {\r
+                    int cp2;\r
+                    for (cp2 = index; cp2 < input.length; cp2++)\r
+                        if (input[cp2] == '\n')\r
+                            break;\r
+                    while (cp2 >= index) {\r
+                        int cp3 = matchRENodes(state, ren.next, stop, cp2);\r
+                        if (cp3 != -1) {\r
+                            index = cp2;\r
+                            break;\r
+                        }\r
+                        cp2--;\r
+                    }\r
+                    break;\r
+                }\r
+                case REOP_WBDRY:\r
+                    if (index == 0 || !isWord(input[index-1])) {\r
+                        if (index >= input.length)\r
+                            return state.noMoreInput();\r
+                        if (!isWord(input[index]))\r
+                            return -1;\r
+                    }\r
+                    else {\r
+                        if (index < input.length && isWord(input[index]))\r
+                            return -1;\r
+                    }\r
+                    break;\r
+                case REOP_WNONBDRY:\r
+                    if (index == 0 || !isWord(input[index-1])) {\r
+                        if (index < input.length && isWord(input[index]))\r
+                            return -1;\r
+                    }\r
+                    else {\r
+                        if (index >= input.length)\r
+                            return state.noMoreInput();\r
+                        if (!isWord(input[index]))\r
+                            return -1;\r
+                    }\r
+                    break;\r
+                case REOP_EOLONLY:\r
+                case REOP_EOL: {\r
+                    if (index == input.length)\r
+                        ; // leave index;\r
+                    else {\r
+                        Context cx = Context.getCurrentContext();\r
+                        RegExpImpl reImpl = getImpl(cx);\r
+                        if ((reImpl.multiline)\r
+                            || ((state.flags & MULTILINE) != 0))\r
+                            if (input[index] == '\n')\r
+                                ;// leave index\r
+                            else\r
+                                return -1;\r
+                        else\r
+                            return -1;\r
+                    }\r
+                }\r
+                    break;\r
+                case REOP_BOL: {\r
+                    Context cx = Context.getCurrentContext();\r
+                    RegExpImpl reImpl = getImpl(cx);\r
+                    if (index != 0) {\r
+                        if (reImpl.multiline ||\r
+                            ((state.flags & MULTILINE) != 0)) {\r
+                            if (index >= input.length) {\r
+                                return state.noMoreInput();\r
+                            }\r
+                            if (input[index - 1] == '\n') {\r
+                                break;\r
+                            }\r
+                        }\r
+                        return -1;\r
+                    }\r
+                    // leave index\r
+                }\r
+                    break;\r
+                case REOP_DIGIT:\r
+                    if (index >= input.length) {\r
+                        return state.noMoreInput();\r
+                    }\r
+                    if (!isDigit(input[index])) return -1;\r
+                    index++;\r
+                    break;\r
+                case REOP_NONDIGIT:\r
+                    if (index >= input.length) {\r
+                        return state.noMoreInput();\r
+                    }\r
+                    if (isDigit(input[index])) return -1;\r
+                    index++;\r
+                    break;\r
+                case REOP_ALNUM:\r
+                    if (index >= input.length) {\r
+                        return state.noMoreInput();\r
+                    }\r
+                    if (!isWord(input[index])) return -1;\r
+                    index++;\r
+                    break;\r
+                case REOP_NONALNUM:\r
+                    if (index >= input.length) {\r
+                        return state.noMoreInput();\r
+                    }\r
+                    if (isWord(input[index])) return -1;\r
+                    index++;\r
+                    break;\r
+                case REOP_SPACE:\r
+                    if (index >= input.length) {\r
+                        return state.noMoreInput();\r
+                    }\r
+                    if (!(TokenStream.isJSSpace(input[index]) ||\r
+                          TokenStream.isJSLineTerminator(input[index])))\r
+                        return -1;\r
+                    index++;\r
+                    break;\r
+                case REOP_NONSPACE:\r
+                    if (index >= input.length) {\r
+                        return state.noMoreInput();\r
+                    }\r
+                    if (TokenStream.isJSSpace(input[index]) ||\r
+                        TokenStream.isJSLineTerminator(input[index]))\r
+                        return -1;\r
+                    index++;\r
+                    break;\r
+                case REOP_FLAT1:\r
+                    if (index >= input.length) {\r
+                        return state.noMoreInput();\r
+                    }\r
+                    if (!matchChar(state.flags, ren.chr, input[index]))\r
+                        return -1;\r
+                    index++;\r
+                    break;\r
+                case REOP_FLAT: {\r
+                    char[] source = (ren.s != null)\r
+                        ? ren.s\r
+                        : this.source.toCharArray();\r
+                    int start = ((Integer)ren.kid).intValue();\r
+                    int length = ren.kid2 - start;\r
+                    for (int i = 0; i < length; i++, index++) {\r
+                        if (index >= input.length) {\r
+                            return state.noMoreInput();\r
+                        }\r
+                        if (!matchChar(state.flags, input[index],\r
+                                       source[start + i]))\r
+                            return -1;\r
+                    }\r
+                }\r
+                    break;\r
+                case REOP_JUMP:\r
+                    break;\r
+                case REOP_END:\r
+                    break;\r
+                default :\r
+                    throw new RuntimeException("Unsupported by node matcher");\r
+                }\r
+                ren = ren.next;\r
+            }\r
+        return index;\r
+    }\r
+\r
+    int matchRegExp(MatchState state, RENode ren, int index) {\r
+        // have to include the position beyond the last character\r
+        // in order to detect end-of-input/line condition\r
+        for (int i = index; i <= state.input.length; i++) {            \r
+            state.skipped = i - index;\r
+            state.parenCount = 0;\r
+            int result = matchRENodes(state, ren, null, i);\r
+            if (result != -1)\r
+                return result;\r
+        }\r
+        return -1;\r
+    }\r
+\r
+    /*\r
+     * indexp is assumed to be an array of length 1\r
+     */\r
+    Object executeRegExp(Context cx, Scriptable scopeObj, RegExpImpl res,\r
+                         String str, int indexp[], int matchType) \r
+    {\r
+        NativeRegExp re = this;\r
+        /*\r
+         * Initialize a CompilerState to minimize recursive argument traffic.\r
+         */\r
+        MatchState state = new MatchState();\r
+        state.inputExhausted = false;\r
+        state.anchoring = false;\r
+        state.flags = re.flags;\r
+        state.scope = scopeObj;\r
+\r
+        char[] charArray = str.toCharArray();\r
+        int start = indexp[0];\r
+        if (start > charArray.length)\r
+            start = charArray.length;\r
+        int index = start;\r
+        state.cpbegin = 0;\r
+        state.cpend = charArray.length;\r
+        state.start = start;\r
+        state.skipped = 0;\r
+        state.input = charArray;\r
+\r
+        state.parenCount = 0;\r
+        state.maybeParens = new SubString[re.parenCount];\r
+        state.parens = new SubString[re.parenCount];\r
+        // We allocate the elements of "parens" and "maybeParens" lazily in\r
+        // the Java port since we don't have arenas.\r
+\r
+        /*\r
+         * Call the recursive matcher to do the real work.  Return null on mismatch\r
+         * whether testing or not.  On match, return an extended Array object.\r
+         */\r
+        index = matchRegExp(state, ren, index);\r
+        if (index == -1) {\r
+            if (matchType != PREFIX || !state.inputExhausted) return null;\r
+            return Undefined.instance;\r
+        }\r
+        int i = index - state.cpbegin;\r
+        indexp[0] = i;\r
+        int matchlen = i - (start + state.skipped);\r
+        int ep = index; \r
+        index -= matchlen;\r
+        Object result;\r
+        Scriptable obj;\r
+\r
+        if (matchType == TEST) {\r
+            /*\r
+             * Testing for a match and updating cx.regExpImpl: don't allocate\r
+             * an array object, do return true.\r
+             */\r
+            result = Boolean.TRUE;\r
+            obj = null;\r
+        }\r
+        else {\r
+            /*\r
+             * The array returned on match has element 0 bound to the matched\r
+             * string, elements 1 through state.parenCount bound to the paren\r
+             * matches, an index property telling the length of the left context,\r
+             * and an input property referring to the input string.\r
+             */\r
+            Scriptable scope = getTopLevelScope(scopeObj);\r
+            result = ScriptRuntime.newObject(cx, scope, "Array", null);\r
+            obj = (Scriptable) result;\r
+\r
+            String matchstr = new String(charArray, index, matchlen);\r
+            obj.put(0, obj, matchstr);\r
+        }\r
+\r
+        if (state.parenCount > re.parenCount)\r
+            throw new RuntimeException();\r
+        if (state.parenCount == 0) {\r
+            res.parens.setSize(0);\r
+            res.lastParen = SubString.emptySubString;\r
+        } else {\r
+            SubString parsub = null;\r
+            int num;\r
+            res.parens.setSize(state.parenCount);\r
+            for (num = 0; num < state.parenCount; num++) {\r
+                parsub = state.parens[num];\r
+                res.parens.setElementAt(parsub, num);\r
+                if (matchType == TEST) continue;\r
+                String parstr = parsub == null ? "": parsub.toString();\r
+                obj.put(num+1, obj, parstr);\r
+            }\r
+            res.lastParen = parsub;\r
+        }\r
+\r
+        if (! (matchType == TEST)) {\r
+            /*\r
+             * Define the index and input properties last for better for/in loop\r
+             * order (so they come after the elements).\r
+             */\r
+            obj.put("index", obj, new Integer(start + state.skipped));\r
+            obj.put("input", obj, str);\r
+        }\r
+\r
+        if (res.lastMatch == null) {\r
+            res.lastMatch = new SubString();\r
+            res.leftContext = new SubString();\r
+            res.rightContext = new SubString();\r
+        }\r
+        res.lastMatch.charArray = charArray;\r
+        res.lastMatch.index = index;\r
+        res.lastMatch.length = matchlen;\r
+\r
+        res.leftContext.charArray = charArray;\r
+        if (cx.getLanguageVersion() == Context.VERSION_1_2) {\r
+            /*\r
+             * JS1.2 emulated Perl4.0.1.8 (patch level 36) for global regexps used\r
+             * in scalar contexts, and unintentionally for the string.match "list"\r
+             * psuedo-context.  On "hi there bye", the following would result:\r
+             *\r
+             * Language     while(/ /g){print("$`");}   s/ /$`/g\r
+             * perl4.036    "hi", "there"               "hihitherehi therebye"\r
+             * perl5        "hi", "hi there"            "hihitherehi therebye"\r
+             * js1.2        "hi", "there"               "hihitheretherebye"\r
+             *\r
+             * Insofar as JS1.2 always defined $` as "left context from the last\r
+             * match" for global regexps, it was more consistent than perl4.\r
+             */\r
+            res.leftContext.index = start;\r
+            res.leftContext.length = state.skipped;\r
+        } else {\r
+            /*\r
+             * For JS1.3 and ECMAv2, emulate Perl5 exactly:\r
+             *\r
+             * js1.3        "hi", "hi there"            "hihitherehi therebye"\r
+             */\r
+            res.leftContext.index = 0;\r
+            res.leftContext.length = start + state.skipped;\r
+        }\r
+\r
+        res.rightContext.charArray = charArray;\r
+        res.rightContext.index = ep;\r
+        res.rightContext.length = state.cpend - ep;\r
+\r
+        return result;\r
+    }\r
+\r
+    public byte getFlags() {\r
+        return flags;\r
+    }\r
+\r
+    private void reportError(String msg, String arg, CompilerState state) {\r
+        Object[] args = { arg };\r
+        throw NativeGlobal.constructError(\r
+                                          state.cx, "SyntaxError",\r
+                                          ScriptRuntime.getMessage(msg, args),\r
+                                          state.scope);\r
+    }\r
+\r
+    protected int getIdDefaultAttributes(int id) {\r
+        switch (id) {\r
+            case Id_lastIndex:\r
+                return ScriptableObject.PERMANENT;\r
+            case Id_source:     \r
+            case Id_global:     \r
+            case Id_ignoreCase: \r
+            case Id_multiline:  \r
+                return ScriptableObject.PERMANENT | ScriptableObject.READONLY;\r
+        }\r
+        return super.getIdDefaultAttributes(id);\r
+    }\r
+    \r
+    protected Object getIdValue(int id) {\r
+        switch (id) {\r
+            case Id_lastIndex:  return wrap_long(0xffffffffL & lastIndex);\r
+            case Id_source:     return source;\r
+            case Id_global:     return wrap_boolean((flags & GLOB) != 0);\r
+            case Id_ignoreCase: return wrap_boolean((flags & FOLD) != 0);\r
+            case Id_multiline:  return wrap_boolean((flags & MULTILINE) != 0);\r
+        }\r
+        return super.getIdValue(id);\r
+    }\r
+    \r
+    protected void setIdValue(int id, Object value) {\r
+        if (id == Id_lastIndex) {\r
+            setLastIndex(ScriptRuntime.toInt32(value));\r
+            return;\r
+        }\r
+        super.setIdValue(id, value);\r
+    }\r
+\r
+    void setLastIndex(int value) {\r
+        lastIndex = value;\r
+    }\r
+    \r
+    public int methodArity(int methodId) {\r
+        if (prototypeFlag) {\r
+            switch (methodId) {\r
+                case Id_compile:  return 1;\r
+                case Id_toString: return 0;\r
+                case Id_exec:     return 1;\r
+                case Id_test:     return 1;\r
+                case Id_prefix:   return 1;\r
+            }\r
+        }\r
+        return super.methodArity(methodId);\r
+    }\r
+\r
+    public Object execMethod(int methodId, IdFunction f, Context cx,\r
+                             Scriptable scope, Scriptable thisObj, \r
+                             Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        if (prototypeFlag) {\r
+            switch (methodId) {\r
+                case Id_compile:\r
+                    return realThis(thisObj, f, false).compile(cx, scope, args);\r
+                \r
+                case Id_toString:\r
+                    return realThis(thisObj, f, true).toString();\r
+                \r
+                case Id_exec:\r
+                    return realThis(thisObj, f, false).exec(cx, scope, args);\r
+                \r
+                case Id_test:\r
+                    return realThis(thisObj, f, false).test(cx, scope, args);\r
+                \r
+                case Id_prefix:\r
+                    return realThis(thisObj, f, false).prefix(cx, scope, args);\r
+            }\r
+        }\r
+        return super.execMethod(methodId, f, cx, scope, thisObj, args);\r
+    }\r
+\r
+    private NativeRegExp realThis(Scriptable thisObj, IdFunction f, \r
+                                  boolean readOnly)\r
+    {\r
+        while (!(thisObj instanceof NativeRegExp)) {\r
+            thisObj = nextInstanceCheck(thisObj, f, readOnly);\r
+        }\r
+        return (NativeRegExp)thisObj;\r
+    }\r
+\r
+    protected String getIdName(int id) {\r
+        switch (id) {\r
+            case Id_lastIndex:  return "lastIndex";\r
+            case Id_source:     return "source";\r
+            case Id_global:     return "global";\r
+            case Id_ignoreCase: return "ignoreCase";\r
+            case Id_multiline:  return "multiline";\r
+        }\r
+        \r
+        if (prototypeFlag) {\r
+            switch (id) {\r
+                case Id_compile:  return "compile";\r
+                case Id_toString: return "toString";\r
+                case Id_exec:     return "exec";\r
+                case Id_test:     return "test";\r
+                case Id_prefix:   return "prefix";\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    protected int maxInstanceId() { return MAX_INSTANCE_ID; }\r
+\r
+// #string_id_map#\r
+\r
+    private static final int\r
+        Id_lastIndex    = 1,\r
+        Id_source       = 2,\r
+        Id_global       = 3,\r
+        Id_ignoreCase   = 4,\r
+        Id_multiline    = 5,\r
+        \r
+        MAX_INSTANCE_ID = 5;\r
+\r
+    protected int mapNameToId(String s) {\r
+        int id;\r
+// #generated# Last update: 2001-05-24 12:01:22 GMT+02:00\r
+        L0: { id = 0; String X = null; int c;\r
+            int s_length = s.length();\r
+            if (s_length==6) {\r
+                c=s.charAt(0);\r
+                if (c=='g') { X="global";id=Id_global; }\r
+                else if (c=='s') { X="source";id=Id_source; }\r
+            }\r
+            else if (s_length==9) {\r
+                c=s.charAt(0);\r
+                if (c=='l') { X="lastIndex";id=Id_lastIndex; }\r
+                else if (c=='m') { X="multiline";id=Id_multiline; }\r
+            }\r
+            else if (s_length==10) { X="ignoreCase";id=Id_ignoreCase; }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+// #/string_id_map#\r
+\r
+        if (id != 0 || !prototypeFlag) { return id; }\r
+\r
+// #string_id_map#\r
+// #generated# Last update: 2001-05-24 12:01:22 GMT+02:00\r
+        L0: { id = 0; String X = null; int c;\r
+            L: switch (s.length()) {\r
+            case 4: c=s.charAt(0);\r
+                if (c=='e') { X="exec";id=Id_exec; }\r
+                else if (c=='t') { X="test";id=Id_test; }\r
+                break L;\r
+            case 6: X="prefix";id=Id_prefix; break L;\r
+            case 7: X="compile";id=Id_compile; break L;\r
+            case 8: X="toString";id=Id_toString; break L;\r
+            }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+        return id;\r
+    }\r
+\r
+    private static final int\r
+        Id_compile       = MAX_INSTANCE_ID + 1,\r
+        Id_toString      = MAX_INSTANCE_ID + 2,\r
+        Id_exec          = MAX_INSTANCE_ID + 3,\r
+        Id_test          = MAX_INSTANCE_ID + 4,\r
+        Id_prefix        = MAX_INSTANCE_ID + 5,\r
+        \r
+        MAX_PROTOTYPE_ID = MAX_INSTANCE_ID + 5;\r
+\r
+// #/string_id_map#\r
+    private boolean prototypeFlag;\r
+\r
+    private String source;      /* locked source string, sans // */\r
+    private int lastIndex;      /* index after last match, for //g iterator */\r
+    private int parenCount;     /* number of parenthesized submatches */\r
+    private byte flags;         /* flags  */\r
+    private byte[] program;     /* regular expression bytecode */\r
+\r
+    RENode ren;\r
+}\r
+\r
+class CompilerState {\r
+    CompilerState(String source, int flags, Context cx, Scriptable scope) {\r
+        this.source = source.toCharArray();\r
+        this.scope = scope;\r
+        this.flags = flags;\r
+        this.cx = cx;\r
+    }\r
+    Context     cx;\r
+    Scriptable  scope;\r
+    char[]      source;\r
+    int         indexBegin;\r
+    int         index;\r
+    int         flags;\r
+    int         parenCount;\r
+    int         progLength;\r
+    byte[]      prog;\r
+}\r
+\r
+\r
+class RENode {\r
+    public static final int ANCHORED = 0x01;    /* anchored at the front */\r
+    public static final int SINGLE   = 0x02;    /* matches a single char */\r
+    public static final int NONEMPTY = 0x04;    /* does not match empty string */\r
+    public static final int ISNEXT   = 0x08;    /* ren is next after at least one node */\r
+    public static final int GOODNEXT = 0x10;    /* ren.next is a tree-like edge in the graph */\r
+    public static final int ISJOIN   = 0x20;    /* ren is a join point in the graph */\r
+    public static final int REALLOK  = 0x40;    /* REOP_FLAT owns tempPool space to realloc */\r
+    public static final int MINIMAL  = 0x80;    /* un-greedy matching for ? * + {} */\r
+\r
+    RENode(CompilerState state, byte op, Object kid) {\r
+        this.op = op;\r
+        this.kid = kid;\r
+    }\r
+    \r
+    private void calcBMSize(char[] s, int index, int cp2, boolean fold) {\r
+        char maxc = 0;\r
+        while (index < cp2) {\r
+            char c = s[index++];\r
+            if (c == '\\') {\r
+                if (index + 5 <= cp2 && s[index] == 'u'\r
+                    && NativeRegExp.isHex(s[index+1]) \r
+                    && NativeRegExp.isHex(s[index+2])\r
+                    && NativeRegExp.isHex(s[index+3]) \r
+                    && NativeRegExp.isHex(s[index+4]))\r
+                    {\r
+                        int x = (((((NativeRegExp.unHex(s[index+0]) << 4) +\r
+                                    NativeRegExp.unHex(s[index+1])) << 4) +\r
+                                  NativeRegExp.unHex(s[index+2])) << 4) +\r
+                            NativeRegExp.unHex(s[index+3]);\r
+                        c = (char) x;\r
+                        index += 5;\r
+                    } else {\r
+                        /*\r
+                         * Octal and hex escapes can't be > 255.  Skip this\r
+                         * backslash and let the loop pass over the remaining\r
+                         * escape sequence as if it were text to match.\r
+                         */\r
+                        if (maxc < 255) maxc = 255;\r
+                        continue;\r
+                    }\r
+            }\r
+            if (fold) {\r
+                /*\r
+                 * Don't assume that lowercase are above uppercase, or\r
+                 * that c is either even when c has upper and lowercase\r
+                 * versions.\r
+                 */\r
+                char c2;\r
+                if ((c2 = Character.toUpperCase(c)) > maxc)\r
+                    maxc = c2;\r
+                if ((c2 = Character.toLowerCase(c2)) > maxc)\r
+                    maxc = c2;\r
+            }\r
+            if (c > maxc)\r
+                maxc = c;\r
+        }\r
+        bmsize = (short)((maxc + NativeRegExp.JS_BITS_PER_BYTE) \r
+                         / NativeRegExp.JS_BITS_PER_BYTE);\r
+    }\r
+    \r
+    private void matchBit(char c, int fill) {\r
+        int i = (c) >> 3;\r
+        byte b = (byte) (c & 7);\r
+        b = (byte) (1 << b);\r
+        if (fill != 0)\r
+            bitmap[i] &= ~b;\r
+        else\r
+            bitmap[i] |= b;\r
+    }\r
+\r
+    private void checkRange(char lastc, int fill) {\r
+        matchBit(lastc, fill);\r
+        matchBit('-', fill);\r
+    }\r
+\r
+    void buildBitmap(MatchState state, char[] s, boolean fold) {\r
+        int index = ((Integer) kid).intValue();\r
+        int end = kid2;\r
+        byte fill = 0;\r
+        int i,n,ocp;\r
+        \r
+        boolean not = false;\r
+        kid2 = 0;\r
+        if (s[index] == '^') {\r
+            not = true;\r
+            kid2 = -1;\r
+            index++;\r
+        }\r
+        \r
+        calcBMSize(s, index, end, fold);\r
+        bitmap = new byte[bmsize];\r
+        if (not) {\r
+            fill = (byte)0xff;\r
+            for (i = 0; i < bmsize; i++)\r
+                bitmap[i] = (byte)0xff;\r
+            bitmap[0] = (byte)0xfe;\r
+        }\r
+        int nchars = bmsize * NativeRegExp.JS_BITS_PER_BYTE;\r
+        char lastc = (char)nchars;\r
+        boolean inrange = false;\r
+        \r
+        while (index < end) {\r
+            char c = s[index++];\r
+            if (c == '\\') {\r
+                c = s[index++];\r
+                switch (c) {\r
+                case 'b':\r
+                case 'f':\r
+                case 'n':\r
+                case 'r':\r
+                case 't':\r
+                case 'v':\r
+                    c = NativeRegExp.getEscape(c);\r
+                    break;\r
+\r
+                case 'd':\r
+                    if (inrange)\r
+                        checkRange(lastc, fill);\r
+                    lastc = (char) nchars;\r
+                    for (c = '0'; c <= '9'; c++)\r
+                        matchBit(c, fill);\r
+                    continue;\r
+\r
+                case 'D':\r
+                    if (inrange)\r
+                        checkRange(lastc, fill);\r
+                    lastc = (char) nchars;\r
+                    for (c = 0; c < '0'; c++)\r
+                        matchBit(c, fill);\r
+                    for (c = '9' + 1; c < nchars; c++)\r
+                        matchBit(c, fill);\r
+                    continue;\r
+\r
+                case 'w':\r
+                    if (inrange)\r
+                        checkRange(lastc, fill);\r
+                    lastc = (char) nchars;\r
+                    for (c = 0; c < nchars; c++)\r
+                        if (NativeRegExp.isWord(c))\r
+                            matchBit(c, fill);\r
+                    continue;\r
+\r
+                case 'W':\r
+                    if (inrange)\r
+                        checkRange(lastc, fill);\r
+                    lastc = (char) nchars;\r
+                    for (c = 0; c < nchars; c++)\r
+                        if (!NativeRegExp.isWord(c))\r
+                            matchBit(c, fill);\r
+                    continue;\r
+\r
+                case 's':\r
+                    if (inrange)\r
+                        checkRange(lastc, fill);\r
+                    lastc = (char) nchars;\r
+                    for (c = 0; c < nchars; c++)\r
+                        if (Character.isWhitespace(c))\r
+                            matchBit(c, fill);\r
+                    continue;\r
+\r
+                case 'S':\r
+                    if (inrange)\r
+                        checkRange(lastc, fill);\r
+                    lastc = (char) nchars;\r
+                    for (c = 0; c < nchars; c++)\r
+                        if (!Character.isWhitespace(c))\r
+                            matchBit(c, fill);\r
+                    continue;\r
+\r
+                case '0':\r
+                case '1':\r
+                case '2':\r
+                case '3':\r
+                case '4':\r
+                case '5':\r
+                case '6':\r
+                case '7':\r
+                    n = NativeRegExp.unDigit(c);\r
+                    ocp = index - 2;\r
+                    c = s[index];\r
+                    if ('0' <= c && c <= '7') {\r
+                        index++;\r
+                        n = 8 * n + NativeRegExp.unDigit(c);\r
+\r
+                        c = s[index];\r
+                        if ('0' <= c && c <= '7') {\r
+                            index++;\r
+                            i = 8 * n + NativeRegExp.unDigit(c);\r
+                            if (i <= 0377)\r
+                                n = i;\r
+                            else\r
+                                index--;\r
+                        }\r
+                    }\r
+                    c = (char) n;\r
+                    break;\r
+\r
+                case 'x':\r
+                    ocp = index;\r
+                    if (index < s.length &&\r
+                        NativeRegExp.isHex(c = s[index++]))\r
+                        {\r
+                            n = NativeRegExp.unHex(c);\r
+                            if (index < s.length &&\r
+                                NativeRegExp.isHex(c = s[index++]))\r
+                                {\r
+                                    n <<= 4;\r
+                                    n += NativeRegExp.unHex(c);\r
+                                }\r
+                        } else {\r
+                            index = ocp;       /* \xZZ is xZZ (Perl does \0ZZ!) */\r
+                            n = 'x';\r
+                        }\r
+                    c = (char) n;\r
+                    break;\r
+\r
+                case 'u':\r
+                    if (s.length > index+3\r
+                        && NativeRegExp.isHex(s[index+0])\r
+                        && NativeRegExp.isHex(s[index+1]) \r
+                        && NativeRegExp.isHex(s[index+2])\r
+                        && NativeRegExp.isHex(s[index+3])) {\r
+                        n = (((((NativeRegExp.unHex(s[index+0]) << 4) +\r
+                                NativeRegExp.unHex(s[index+1])) << 4) +\r
+                              NativeRegExp.unHex(s[index+2])) << 4) +\r
+                            NativeRegExp.unHex(s[index+3]);\r
+                        c = (char) n;\r
+                        index += 4;\r
+                    }\r
+                    break;\r
+\r
+                case 'c':\r
+                    c = s[index++];\r
+                    c = Character.toUpperCase(c);\r
+                    c = (char) (c ^ 64); // JS_TOCTRL\r
+                    break;\r
+                }\r
+            }\r
+\r
+            if (inrange) {\r
+                if (lastc > c) {\r
+                    throw NativeGlobal.constructError(\r
+                                                      Context.getCurrentContext(), "RangeError",\r
+                                                      ScriptRuntime.getMessage(\r
+                                                                               "msg.bad.range", null),\r
+                                                      state.scope);\r
+                }\r
+                inrange = false;\r
+            } else {\r
+                // Set lastc so we match just c's bit in the for loop.\r
+                lastc = c;\r
+\r
+                // [balance:\r
+                if (index + 1 < end && s[index] == '-' &&\r
+                    s[index+1] != ']')\r
+                    {\r
+                        index++;\r
+                        inrange = true;\r
+                        continue;\r
+                    }\r
+            }\r
+\r
+            // Match characters in the range [lastc, c].\r
+            for (; lastc <= c; lastc++) {\r
+                matchBit(lastc, fill);\r
+                if (fold) {\r
+                    /*\r
+                     * Must do both upper and lower for Turkish dotless i,\r
+                     * Georgian, etc.\r
+                     */\r
+                    char foldc = Character.toUpperCase(lastc);\r
+                    matchBit(foldc, fill);\r
+                    foldc = Character.toLowerCase(foldc);\r
+                    matchBit(foldc, fill);\r
+                }\r
+            }\r
+            lastc = c;\r
+        }\r
+    }\r
+    byte            op;         /* packed r.e. op bytecode */\r
+    byte            flags;      /* flags, see below */\r
+    short           offset;     /* bytecode offset */\r
+    RENode          next;       /* next in concatenation order */\r
+    Object          kid;        /* first operand */\r
+    int             kid2;       /* second operand */\r
+    int             num;        /* could be a number */\r
+    char            chr;        /* or a char */\r
+    short           min,max;    /* or a range */\r
+    short           kidlen;     /* length of string at kid, in chars */\r
+    short           bmsize;     /* bitmap size, based on max char code */\r
+    char[]          s;          /* if null, use state.source */\r
+    byte[]          bitmap;     /* cclass bitmap */\r
+}\r
+\r
+\r
+class MatchState {\r
+    boolean    inputExhausted;         /* did we run out of input chars ? */\r
+    boolean     anchoring;              /* true if multiline anchoring ^/$ */\r
+    int         pcend;                  /* pc limit (fencepost) */\r
+    int         cpbegin, cpend;         /* cp base address and limit */\r
+    int         start;                  /* offset from cpbegin to start at */\r
+    int         skipped;                /* chars skipped anchoring this r.e. */\r
+    byte        flags;                  /* pennants  */\r
+    int         parenCount;             /* number of paren substring matches */\r
+    SubString[] maybeParens;            /* possible paren substring pointers */\r
+    SubString[] parens;                 /* certain paren substring matches */\r
+    Scriptable  scope;\r
+    char[]             input;\r
+\r
+    public int noMoreInput() {\r
+        inputExhausted = true;\r
+        /*\r
+        try {\r
+            throw new Exception();\r
+        }\r
+        catch (Exception e) {\r
+            e.printStackTrace();\r
+        }\r
+        */\r
+        return -1;\r
+    }\r
+}\r
+\r
+class GreedyState {\r
+    MatchState state;\r
+    RENode kid;\r
+    RENode next;\r
+    RENode stop;\r
+    int kidCount;\r
+    int maxKid;\r
+}\r
+\r
diff --git a/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java b/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java
new file mode 100644 (file)
index 0000000..3b8e7c0
--- /dev/null
@@ -0,0 +1,271 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1998.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript.regexp;\r
+\r
+import org.mozilla.javascript.*;\r
+import java.lang.reflect.Method;\r
+\r
+/**\r
+ * This class implements the RegExp constructor native object.\r
+ *\r
+ * Revision History:\r
+ * Implementation in C by Brendan Eich\r
+ * Initial port to Java by Norris Boyd from jsregexp.c version 1.36\r
+ * Merged up to version 1.38, which included Unicode support.\r
+ * Merged bug fixes in version 1.39.\r
+ * Merged JSFUN13_BRANCH changes up to 1.32.2.11\r
+ *\r
+ * @author Brendan Eich\r
+ * @author Norris Boyd\r
+ */\r
+public class NativeRegExpCtor extends NativeFunction {\r
+\r
+    public NativeRegExpCtor() {\r
+        functionName = "RegExp";\r
+    }\r
+    \r
+    public String getClassName() {\r
+        return "Function";\r
+    }\r
+\r
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj,\r
+                       Object[] args)\r
+    {\r
+        if (args.length > 0 && args[0] instanceof NativeRegExp &&\r
+            (args.length == 1 || args[1] == Undefined.instance))\r
+          {\r
+            return args[0];\r
+        }\r
+        return construct(cx, parent, args);\r
+    }\r
+\r
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args) {\r
+        NativeRegExp re = new NativeRegExp();\r
+        re.compile(cx, scope, args);\r
+        re.setPrototype(getClassPrototype(scope, "RegExp"));\r
+        re.setParentScope(getParentScope());\r
+        return re;\r
+    }\r
+\r
+    static RegExpImpl getImpl() {\r
+        Context cx = Context.getCurrentContext();\r
+        return (RegExpImpl) ScriptRuntime.getRegExpProxy(cx);\r
+    }\r
+\r
+    protected int getIdDefaultAttributes(int id) {\r
+        int shifted = id - idBase;\r
+        if (1 <= shifted && shifted <= MAX_INSTANCE_ID) { \r
+            switch (shifted) {\r
+                case Id_multiline:\r
+                case Id_STAR:\r
+                case Id_input:\r
+                case Id_UNDERSCORE:\r
+                    return PERMANENT;\r
+            }\r
+            return PERMANENT | READONLY;\r
+        }\r
+        return super.getIdDefaultAttributes(id);\r
+    }\r
+    \r
+    private static String stringResult(Object obj) {\r
+        return (obj == null) ? "" : obj.toString();\r
+    }\r
+\r
+    protected Object getIdValue(int id) {\r
+        int shifted = id - idBase;\r
+        if (1 <= shifted && shifted <= MAX_INSTANCE_ID) { \r
+            RegExpImpl impl = getImpl();\r
+            switch (shifted) {\r
+                case Id_multiline:\r
+                case Id_STAR:\r
+                    return wrap_boolean(impl.multiline);\r
+\r
+                case Id_input:\r
+                case Id_UNDERSCORE: \r
+                    return stringResult(impl.input);\r
+\r
+                case Id_lastMatch:\r
+                case Id_AMPERSAND:\r
+                    return stringResult(impl.lastMatch);\r
+\r
+                case Id_lastParen:\r
+                case Id_PLUS:\r
+                    return stringResult(impl.lastParen);\r
+\r
+                case Id_leftContext:\r
+                case Id_BACK_QUOTE:\r
+                    return stringResult(impl.leftContext);\r
+\r
+                case Id_rightContext:\r
+                case Id_QUOTE:\r
+                    return stringResult(impl.rightContext);\r
+            }\r
+            // Must be one of $1..$9, convert to 0..8\r
+            int substring_number = shifted - DOLLAR_ID_BASE - 1;\r
+            return impl.getParenSubString(substring_number).toString();\r
+        }\r
+        return super.getIdValue(id);\r
+    }\r
+    \r
+    protected void setIdValue(int id, Object value) {\r
+        switch (id - idBase) {\r
+            case Id_multiline:\r
+            case Id_STAR:\r
+                getImpl().multiline = ScriptRuntime.toBoolean(value);\r
+                return;\r
+\r
+            case Id_input:\r
+            case Id_UNDERSCORE: \r
+                getImpl().input = ScriptRuntime.toString(value); \r
+                return;\r
+        }\r
+        super.setIdValue(id, value);\r
+    }\r
+\r
+    protected String getIdName(int id) {\r
+        int shifted = id - idBase;\r
+        if (1 <= shifted && shifted <= MAX_INSTANCE_ID) { \r
+            switch (shifted) {\r
+                case Id_multiline:    return "multiline";\r
+                case Id_STAR:         return "$*";\r
+\r
+                case Id_input:        return "input";\r
+                case Id_UNDERSCORE:   return "$_";\r
+\r
+                case Id_lastMatch:    return "lastMatch";\r
+                case Id_AMPERSAND:    return "$&";\r
+\r
+                case Id_lastParen:    return "lastParen";\r
+                case Id_PLUS:         return "$+";\r
+\r
+                case Id_leftContext:  return "leftContext";\r
+                case Id_BACK_QUOTE:   return "$`";\r
+\r
+                case Id_rightContext: return "rightContext";\r
+                case Id_QUOTE:        return "$'";\r
+            }\r
+            // Must be one of $1..$9, convert to 0..8\r
+            int substring_number = shifted - DOLLAR_ID_BASE - 1;\r
+            char[] buf = { '$', (char)('1' + substring_number) };\r
+            return new String(buf);\r
+        }\r
+        return super.getIdName(id);\r
+    }\r
+\r
+    protected int maxInstanceId() {\r
+        // Note: check for idBase == 0 can not be done in constructor, \r
+        // because IdScriptable calls maxInstanceId in its constructor\r
+        // before NativeRegExpCtor constructor gets chance to run any code\r
+        if (idBase == 0) { idBase = super.maxInstanceId(); }\r
+        return idBase + MAX_INSTANCE_ID; \r
+    }\r
+\r
+// #string_id_map#\r
+\r
+    private static final int\r
+        Id_multiline     = 1,\r
+        Id_STAR          = 2,  // #string=$*#\r
+\r
+        Id_input         = 3,\r
+        Id_UNDERSCORE    = 4,  // #string=$_#\r
+\r
+        Id_lastMatch     = 5,\r
+        Id_AMPERSAND     = 6,  // #string=$&#\r
+\r
+        Id_lastParen     = 7,\r
+        Id_PLUS          = 8,  // #string=$+#\r
+\r
+        Id_leftContext   = 9,\r
+        Id_BACK_QUOTE    = 10, // #string=$`#\r
+\r
+        Id_rightContext  = 11,\r
+        Id_QUOTE         = 12, // #string=$'#\r
+        \r
+        DOLLAR_ID_BASE   = 12;\r
+        \r
+    private static final int\r
+        Id_DOLLAR_1 = DOLLAR_ID_BASE + 1, // #string=$1#\r
+        Id_DOLLAR_2 = DOLLAR_ID_BASE + 2, // #string=$2#\r
+        Id_DOLLAR_3 = DOLLAR_ID_BASE + 3, // #string=$3#\r
+        Id_DOLLAR_4 = DOLLAR_ID_BASE + 4, // #string=$4#\r
+        Id_DOLLAR_5 = DOLLAR_ID_BASE + 5, // #string=$5#\r
+        Id_DOLLAR_6 = DOLLAR_ID_BASE + 6, // #string=$6#\r
+        Id_DOLLAR_7 = DOLLAR_ID_BASE + 7, // #string=$7#\r
+        Id_DOLLAR_8 = DOLLAR_ID_BASE + 8, // #string=$8#\r
+        Id_DOLLAR_9 = DOLLAR_ID_BASE + 9, // #string=$9#\r
+\r
+        MAX_INSTANCE_ID = DOLLAR_ID_BASE + 9;\r
+\r
+    protected int mapNameToId(String s) {\r
+        int id;\r
+// #generated# Last update: 2001-05-24 16:09:31 GMT+02:00\r
+        L0: { id = 0; String X = null; int c;\r
+            L: switch (s.length()) {\r
+            case 2: switch (s.charAt(1)) {\r
+                case '&': if (s.charAt(0)=='$') {id=Id_AMPERSAND; break L0;} break L;\r
+                case '\'': if (s.charAt(0)=='$') {id=Id_QUOTE; break L0;} break L;\r
+                case '*': if (s.charAt(0)=='$') {id=Id_STAR; break L0;} break L;\r
+                case '+': if (s.charAt(0)=='$') {id=Id_PLUS; break L0;} break L;\r
+                case '1': if (s.charAt(0)=='$') {id=Id_DOLLAR_1; break L0;} break L;\r
+                case '2': if (s.charAt(0)=='$') {id=Id_DOLLAR_2; break L0;} break L;\r
+                case '3': if (s.charAt(0)=='$') {id=Id_DOLLAR_3; break L0;} break L;\r
+                case '4': if (s.charAt(0)=='$') {id=Id_DOLLAR_4; break L0;} break L;\r
+                case '5': if (s.charAt(0)=='$') {id=Id_DOLLAR_5; break L0;} break L;\r
+                case '6': if (s.charAt(0)=='$') {id=Id_DOLLAR_6; break L0;} break L;\r
+                case '7': if (s.charAt(0)=='$') {id=Id_DOLLAR_7; break L0;} break L;\r
+                case '8': if (s.charAt(0)=='$') {id=Id_DOLLAR_8; break L0;} break L;\r
+                case '9': if (s.charAt(0)=='$') {id=Id_DOLLAR_9; break L0;} break L;\r
+                case '_': if (s.charAt(0)=='$') {id=Id_UNDERSCORE; break L0;} break L;\r
+                case '`': if (s.charAt(0)=='$') {id=Id_BACK_QUOTE; break L0;} break L;\r
+                } break L;\r
+            case 5: X="input";id=Id_input; break L;\r
+            case 9: c=s.charAt(4);\r
+                if (c=='M') { X="lastMatch";id=Id_lastMatch; }\r
+                else if (c=='P') { X="lastParen";id=Id_lastParen; }\r
+                else if (c=='i') { X="multiline";id=Id_multiline; }\r
+                break L;\r
+            case 11: X="leftContext";id=Id_leftContext; break L;\r
+            case 12: X="rightContext";id=Id_rightContext; break L;\r
+            }\r
+            if (X!=null && X!=s && !X.equals(s)) id = 0;\r
+        }\r
+// #/generated#\r
+// #/string_id_map#\r
+\r
+        return (id != 0) ? idBase + id : super.mapNameToId(s); \r
+    }\r
+    \r
+    private static int idBase;\r
+}\r
diff --git a/src/org/mozilla/javascript/regexp/RegExpImpl.java b/src/org/mozilla/javascript/regexp/RegExpImpl.java
new file mode 100644 (file)
index 0000000..dcba755
--- /dev/null
@@ -0,0 +1,567 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1998.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript.regexp;\r
+\r
+import org.mozilla.javascript.*;\r
+import java.util.Vector;\r
+\r
+/**\r
+ * \r
+ */\r
+public class RegExpImpl implements RegExpProxy {\r
+    \r
+    public RegExpImpl() {\r
+        parens = new Vector(9);\r
+    }\r
+    \r
+    public boolean isRegExp(Object obj) {\r
+        return obj instanceof NativeRegExp;\r
+    }\r
+\r
+    public Object newRegExp(Context cx, Scriptable scope, String source, \r
+                                                String global, boolean flat)\r
+    {\r
+        return new NativeRegExp(cx, scope, source, global, flat);\r
+    }\r
+    \r
+    public Object match(Context cx, Scriptable scope, \r
+                        Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        MatchData mdata = new MatchData();\r
+        mdata.optarg = 1;\r
+        mdata.mode = GlobData.GLOB_MATCH;\r
+        mdata.parent = ScriptableObject.getTopLevelScope(scope);\r
+        Object rval = matchOrReplace(cx, scope, thisObj, args,\r
+                                     this, mdata, false);\r
+        return mdata.arrayobj == null ? rval : mdata.arrayobj;\r
+    }\r
+\r
+    public Object search(Context cx, Scriptable scope,\r
+                         Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        MatchData mdata = new MatchData();\r
+        mdata.optarg = 1;\r
+        mdata.mode = GlobData.GLOB_SEARCH;\r
+        mdata.parent = ScriptableObject.getTopLevelScope(scope);\r
+        return matchOrReplace(cx, scope, thisObj, args, this, mdata, false);\r
+    }\r
+\r
+    public Object replace(Context cx, Scriptable scope, \r
+                          Scriptable thisObj, Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        Object arg1 = args.length < 2 ? Undefined.instance : args[1];\r
+        String repstr = null;\r
+        Function lambda = null;\r
+        if (arg1 instanceof Function) {\r
+            lambda = (Function) arg1;\r
+        } else {\r
+            repstr = ScriptRuntime.toString(arg1);\r
+        }\r
+\r
+        ReplaceData rdata = new ReplaceData();\r
+        rdata.optarg = 2;\r
+        rdata.mode = GlobData.GLOB_REPLACE;\r
+        rdata.lambda = lambda;\r
+        rdata.repstr = repstr == null ? null : repstr.toCharArray();\r
+        rdata.dollar = repstr == null ? -1 : repstr.indexOf('$');\r
+        rdata.charArray = null;\r
+        rdata.length = 0;\r
+        rdata.index = 0;\r
+        rdata.leftIndex = 0;\r
+        Object val = matchOrReplace(cx, scope, thisObj, args,\r
+                                    this, rdata, true);\r
+        char[] charArray;\r
+\r
+        if (rdata.charArray == null) {\r
+            if (rdata.global || val == null || !val.equals(Boolean.TRUE)) {\r
+                /* Didn't match even once. */\r
+                return rdata.str;\r
+            }\r
+            int leftlen = this.leftContext.length;\r
+            int length = leftlen + rdata.findReplen(cx, this);\r
+            charArray = new char[length];\r
+            SubString leftContext = this.leftContext;\r
+            System.arraycopy(leftContext.charArray, leftContext.index,\r
+                             charArray, 0, leftlen);\r
+            rdata.doReplace(cx, this, charArray, leftlen);\r
+            rdata.charArray = charArray;\r
+            rdata.length = length;\r
+        }\r
+\r
+        SubString rc = this.rightContext;\r
+        int rightlen = rc.length;\r
+        int length = rdata.length + rightlen;\r
+        charArray = new char[length];\r
+        System.arraycopy(rdata.charArray, 0,\r
+                         charArray, 0, rdata.charArray.length);\r
+        System.arraycopy(rc.charArray, rc.index, charArray,\r
+                         rdata.length, rightlen);\r
+        return new String(charArray, 0, length);\r
+    }\r
+\r
+    /**\r
+     * Analog of C match_or_replace.\r
+     */\r
+    private static Object matchOrReplace(Context cx, Scriptable scope,\r
+                                         Scriptable thisObj, Object[] args, \r
+                                         RegExpImpl reImpl,\r
+                                         GlobData data, boolean forceFlat)\r
+        throws JavaScriptException\r
+    {\r
+        NativeRegExp re;\r
+\r
+        String str = ScriptRuntime.toString(thisObj);\r
+        data.str = str;\r
+        Scriptable topScope = ScriptableObject.getTopLevelScope(scope);\r
+        \r
+        if (args.length == 0)\r
+            re = new NativeRegExp(cx, topScope, "", "", false);\r
+        else\r
+            if (args[0] instanceof NativeRegExp) {\r
+                re = (NativeRegExp) args[0];\r
+            } else {\r
+                String src = ScriptRuntime.toString(args[0]);\r
+                String opt;\r
+                if (data.optarg < args.length) {\r
+                    args[0] = src;\r
+                    opt = ScriptRuntime.toString(args[data.optarg]);\r
+                } else {\r
+                    opt = null;\r
+                }                \r
+                re = new NativeRegExp(cx, topScope, src, opt, forceFlat);\r
+            }\r
+        data.regexp = re;\r
+\r
+        data.global = (re.getFlags() & NativeRegExp.GLOB) != 0;\r
+        int[] indexp = { 0 };\r
+        Object result = null;\r
+        if (data.mode == GlobData.GLOB_SEARCH) {\r
+            result = re.executeRegExp(cx, scope, reImpl,\r
+                                      str, indexp, NativeRegExp.TEST);\r
+            if (result != null && result.equals(Boolean.TRUE))\r
+                result = new Integer(reImpl.leftContext.length);\r
+            else\r
+                result = new Integer(-1);\r
+        } else if (data.global) {\r
+            re.setLastIndex(0);\r
+            for (int count = 0; indexp[0] <= str.length(); count++) {\r
+                result = re.executeRegExp(cx, scope, reImpl,\r
+                                          str, indexp, NativeRegExp.TEST);\r
+                if (result == null || !result.equals(Boolean.TRUE))\r
+                    break;\r
+                data.doGlobal(cx, scope, count, reImpl);\r
+                if (reImpl.lastMatch.length == 0) {\r
+                    if (indexp[0] == str.length())\r
+                        break;\r
+                    indexp[0]++;\r
+                }\r
+            }\r
+        } else {\r
+            result = re.executeRegExp(cx, scope, reImpl, str, indexp,\r
+                                      ((data.mode == GlobData.GLOB_REPLACE) \r
+                                       ? NativeRegExp.TEST \r
+                                       : NativeRegExp.MATCH));\r
+        }\r
+\r
+        return result;\r
+    } \r
+    \r
+    \r
+    \r
+    public int find_split(Scriptable scope, String target, String separator, \r
+                          Object reObj, int[] ip, int[] matchlen, \r
+                          boolean[] matched, String[][] parensp)\r
+    {\r
+        int i = ip[0];\r
+        int length = target.length();\r
+        int result;\r
+        Context cx = Context.getCurrentContext();\r
\r
+        int version = cx.getLanguageVersion();\r
+        NativeRegExp re = (NativeRegExp) reObj;\r
+        again:\r
+        while (true) {  // imitating C label\r
+            /* JS1.2 deviated from Perl by never matching at end of string. */\r
+            int ipsave = ip[0]; // reuse ip to save object creation\r
+            ip[0] = i;\r
+            Object ret = re.executeRegExp(cx, scope, this, target, ip,\r
+                                          NativeRegExp.TEST);\r
+            if (ret != Boolean.TRUE) {\r
+                // Mismatch: ensure our caller advances i past end of string.\r
+                ip[0] = ipsave;\r
+                matchlen[0] = 1;\r
+                matched[0] = false;\r
+                return length;\r
+            }\r
+            i = ip[0];\r
+            ip[0] = ipsave;\r
+            matched[0] = true;\r
+\r
+            SubString sep = this.lastMatch;\r
+            matchlen[0] = sep.length;\r
+            if (matchlen[0] == 0) {\r
+                /*\r
+                 * Empty string match: never split on an empty\r
+                 * match at the start of a find_split cycle.  Same\r
+                 * rule as for an empty global match in\r
+                 * match_or_replace.\r
+                 */\r
+                if (i == ip[0]) {\r
+                    /*\r
+                     * "Bump-along" to avoid sticking at an empty\r
+                     * match, but don't bump past end of string --\r
+                     * our caller must do that by adding\r
+                     * sep->length to our return value.\r
+                     */\r
+                    if (i == length) {\r
+                        if (version == Context.VERSION_1_2) {\r
+                            matchlen[0] = 1;\r
+                            result = i;\r
+                        }\r
+                        else\r
+                            result = -1;\r
+                        break;\r
+                    }\r
+                    i++;\r
+                    continue again; // imitating C goto\r
+                }\r
+            }\r
+            // PR_ASSERT((size_t)i >= sep->length);\r
+            result = i - matchlen[0];\r
+            break;\r
+        }\r
+        int size = parens.size();\r
+        parensp[0] = new String[size];\r
+        for (int num = 0; num < size; num++) {\r
+            SubString parsub = getParenSubString(num);\r
+            parensp[0][num] = parsub.toString();\r
+        }\r
+        return result;\r
+    }\r
+    \r
+    /**\r
+     * Analog of REGEXP_PAREN_SUBSTRING in C jsregexp.h.\r
+     * Assumes zero-based; i.e., for $3, i==2\r
+     */\r
+    SubString getParenSubString(int i) {\r
+        if (i >= parens.size())\r
+            return SubString.emptySubString;\r
+        return (SubString) parens.elementAt(i);\r
+    }\r
+\r
+    String          input;         /* input string to match (perl $_, GC root) */\r
+    boolean         multiline;     /* whether input contains newlines (perl $*) */\r
+    Vector          parens;        /* Vector of SubString; last set of parens\r
+                                      matched (perl $1, $2) */\r
+    SubString       lastMatch;     /* last string matched (perl $&) */\r
+    SubString       lastParen;     /* last paren matched (perl $+) */\r
+    SubString       leftContext;   /* input to left of last match (perl $`) */\r
+    SubString       rightContext;  /* input to right of last match (perl $') */\r
+}\r
+\r
+\r
+abstract class GlobData {\r
+    static final int GLOB_MATCH =      1;\r
+    static final int GLOB_REPLACE =    2;\r
+    static final int GLOB_SEARCH =     3;\r
+\r
+    abstract void doGlobal(Context cx, Scriptable scope, int count, \r
+                           RegExpImpl reImpl) \r
+        throws JavaScriptException;\r
+\r
+    byte     mode;      /* input: return index, match object, or void */\r
+    int      optarg;    /* input: index of optional flags argument */\r
+    boolean  global;    /* output: whether regexp was global */\r
+    String   str;       /* output: 'this' parameter object as string */\r
+    NativeRegExp regexp;/* output: regexp parameter object private data */\r
+    Scriptable parent;\r
+}\r
+\r
+\r
+class MatchData extends GlobData {\r
+\r
+    /*\r
+     * Analog of match_glob() in jsstr.c\r
+     */\r
+    void doGlobal(Context cx, Scriptable scope, int count, RegExpImpl reImpl) \r
+        throws JavaScriptException \r
+    {\r
+        MatchData mdata;\r
+        Object v;\r
+\r
+        mdata = this;\r
+        if (arrayobj == null) {\r
+            Scriptable s = ScriptableObject.getTopLevelScope(scope);\r
+            arrayobj = ScriptRuntime.newObject(cx, s, "Array", null);\r
+        }\r
+        SubString matchsub = reImpl.lastMatch;\r
+        String matchstr = matchsub.toString();\r
+        arrayobj.put(count, arrayobj, matchstr);\r
+    }\r
+\r
+    Scriptable arrayobj;\r
+}\r
+\r
+\r
+class ReplaceData extends GlobData {\r
+\r
+    ReplaceData() {\r
+        dollar = -1;\r
+    }\r
+\r
+    /*\r
+     * Analog of replace_glob() in jsstr.c\r
+     */\r
+    void doGlobal(Context cx, Scriptable scope, int count, RegExpImpl reImpl) \r
+        throws JavaScriptException\r
+    {\r
+        ReplaceData rdata = this;\r
+\r
+        SubString lc = reImpl.leftContext;\r
+\r
+        char[] leftArray = lc.charArray;\r
+        int leftIndex = rdata.leftIndex;\r
+        \r
+        int leftlen = reImpl.lastMatch.index - leftIndex;\r
+        rdata.leftIndex = reImpl.lastMatch.index + reImpl.lastMatch.length;\r
+        int replen = findReplen(cx, reImpl);\r
+        int growth = leftlen + replen;\r
+        char[] charArray;\r
+        if (rdata.charArray != null) {\r
+            charArray = new char[rdata.length + growth];\r
+            System.arraycopy(rdata.charArray, 0, charArray, 0, rdata.length);\r
+        } else {\r
+            charArray = new char[growth];\r
+        }\r
+\r
+        rdata.charArray = charArray;\r
+        rdata.length += growth;\r
+        int index = rdata.index;\r
+        rdata.index += growth;\r
+        System.arraycopy(leftArray, leftIndex, charArray, index, leftlen);\r
+        index += leftlen;\r
+        doReplace(cx, reImpl, charArray, index);\r
+    }\r
+\r
+    static SubString dollarStr = new SubString("$");\r
+\r
+    static SubString interpretDollar(Context cx, RegExpImpl res, \r
+                                     char[] da, int dp, int bp, int[] skip)\r
+    {\r
+        char[] ca;\r
+        int cp;\r
+        char dc;\r
+        int num, tmp;\r
+\r
+        /* Allow a real backslash (literal "\\") to escape "$1" etc. */\r
+        if (da[dp] != '$')\r
+            throw new RuntimeException();\r
+        if ((cx.getLanguageVersion() != Context.VERSION_DEFAULT)\r
+                 && (cx.getLanguageVersion() <= Context.VERSION_1_4))\r
+            if (dp > bp && da[dp-1] == '\\')\r
+                return null;\r
+\r
+        /* Interpret all Perl match-induced dollar variables. */\r
+        dc = da[dp+1];\r
+        if (NativeRegExp.isDigit(dc)) {            \r
+            if ((cx.getLanguageVersion() != Context.VERSION_DEFAULT)\r
+                     && (cx.getLanguageVersion() <= Context.VERSION_1_4)) {\r
+                if (dc == '0')\r
+                    return null;\r
+                /* Check for overflow to avoid gobbling arbitrary decimal digits. */\r
+                num = 0;\r
+                ca = da;\r
+                cp = dp;\r
+                while (++cp < ca.length && NativeRegExp.isDigit(dc = ca[cp])) {\r
+                    tmp = 10 * num + NativeRegExp.unDigit(dc);\r
+                    if (tmp < num)\r
+                        break;\r
+                    num = tmp;\r
+                }\r
+            }\r
+            else {  /* ECMA 3, 1-9 or 01-99 */\r
+                num = NativeRegExp.unDigit(dc);\r
+                cp = dp + 2;\r
+                if ((dp + 2) < da.length) {\r
+                    dc = da[dp + 2];\r
+                    if (NativeRegExp.isDigit(dc)) {\r
+                        num = 10 * num + NativeRegExp.unDigit(dc);\r
+                        cp++;\r
+                    }\r
+                }\r
+                if (num == 0) return null;  /* $0 or $00 is not valid */\r
+            }\r
+            /* Adjust num from 1 $n-origin to 0 array-index-origin. */\r
+            num--;\r
+            skip[0] = cp - dp;\r
+            return res.getParenSubString(num);\r
+        }\r
+\r
+        skip[0] = 2;\r
+        switch (dc) {\r
+          case '$':\r
+            return dollarStr;\r
+          case '&':\r
+            return res.lastMatch;\r
+          case '+':\r
+            return res.lastParen;\r
+          case '`':\r
+            if (cx.getLanguageVersion() == Context.VERSION_1_2) {\r
+                /*\r
+                 * JS1.2 imitated the Perl4 bug where left context at each step\r
+                 * in an iterative use of a global regexp started from last match,\r
+                 * not from the start of the target string.  But Perl4 does start\r
+                 * $` at the beginning of the target string when it is used in a\r
+                 * substitution, so we emulate that special case here.\r
+                 */\r
+                res.leftContext.index = 0;\r
+                res.leftContext.length = res.lastMatch.index;\r
+            }\r
+            return res.leftContext;\r
+          case '\'':\r
+            return res.rightContext;\r
+        }\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * Corresponds to find_replen in jsstr.c. rdata is 'this', and\r
+     * the result parameter sizep is the return value (errors are\r
+     * propagated with exceptions).\r
+     */\r
+    int findReplen(Context cx, RegExpImpl reImpl)\r
+        throws JavaScriptException\r
+    {\r
+        if (lambda != null) {\r
+            // invoke lambda function with args lastMatch, $1, $2, ... $n,\r
+            // leftContext.length, whole string.\r
+            Vector parens = reImpl.parens;\r
+            int parenCount = parens.size();\r
+            Object[] args = new Object[parenCount + 3];\r
+            args[0] = reImpl.lastMatch.toString();\r
+            for (int i=0; i < parenCount; i++) {\r
+                SubString sub = (SubString) parens.elementAt(i);\r
+                args[i+1] = sub.toString();\r
+            }\r
+            args[parenCount+1] = new Integer(reImpl.leftContext.length);\r
+            args[parenCount+2] = str;\r
+            Scriptable parent = lambda.getParentScope();\r
+            Object result = lambda.call(cx, parent, parent, args);\r
+\r
+            this.repstr = ScriptRuntime.toString(result).toCharArray();\r
+            return this.repstr.length;\r
+        }\r
+\r
+        int replen = this.repstr.length;\r
+        if (dollar == -1)\r
+            return replen;\r
+\r
+        int bp = 0;\r
+        for (int dp = dollar; dp < this.repstr.length ; ) {\r
+            char c = this.repstr[dp];\r
+            if (c != '$') {\r
+                dp++;\r
+                continue;\r
+            }\r
+            int[] skip = { 0 };\r
+            SubString sub = interpretDollar(cx, reImpl, this.repstr, dp,\r
+                                            bp, skip);\r
+            if (sub != null) {\r
+                replen += sub.length - skip[0];\r
+                dp += skip[0];\r
+            }\r
+            else\r
+                dp++;\r
+        }\r
+        return replen;\r
+    }\r
+\r
+    /**\r
+     * Analog of do_replace in jsstr.c\r
+     */\r
+    void doReplace(Context cx, RegExpImpl regExpImpl, char[] charArray,\r
+                   int arrayIndex)\r
+    {\r
+        int cp = 0;\r
+        char[] da = repstr;\r
+        int dp = this.dollar;\r
+        int bp = cp;\r
+        if (dp != -1) {\r
+          outer:\r
+            for (;;) {\r
+                int len = dp - cp;\r
+                System.arraycopy(repstr, cp, charArray, arrayIndex,\r
+                                 len);\r
+                arrayIndex += len;\r
+                cp = dp;\r
+                int[] skip = { 0 };\r
+                SubString sub = interpretDollar(cx, regExpImpl, da,\r
+                                                dp, bp, skip);\r
+                if (sub != null) {\r
+                    len = sub.length;\r
+                    if (len > 0) {\r
+                        System.arraycopy(sub.charArray, sub.index, charArray,\r
+                                         arrayIndex, len);\r
+                    }\r
+                    arrayIndex += len;\r
+                    cp += skip[0];\r
+                    dp += skip[0];\r
+                }\r
+                else\r
+                    dp++;\r
+                if (dp >= repstr.length) break;\r
+                while (repstr[dp] != '$') {\r
+                    dp++;\r
+                    if (dp >= repstr.length) break outer;\r
+                }\r
+            }\r
+        }\r
+        if (repstr.length > cp) {\r
+            System.arraycopy(repstr, cp, charArray, arrayIndex,\r
+                             repstr.length - cp);\r
+        }\r
+    }\r
+\r
+    Function    lambda;        /* replacement function object or null */\r
+    char[]      repstr;        /* replacement string */\r
+    int         dollar;        /* -1 or index of first $ in repstr */\r
+    char[]      charArray;     /* result characters, null initially */\r
+    int         length;        /* result length, 0 initially */\r
+    int         index;         /* index in result of next replacement */\r
+    int         leftIndex;     /* leftContext index, always 0 for JS1.2 */\r
+}\r
diff --git a/src/org/mozilla/javascript/regexp/SubString.java b/src/org/mozilla/javascript/regexp/SubString.java
new file mode 100644 (file)
index 0000000..90e0979
--- /dev/null
@@ -0,0 +1,62 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1998.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript.regexp;\r
+\r
+class SubString {\r
+\r
+    public SubString()\r
+    {\r
+    }\r
+    \r
+    public SubString(String str)\r
+    {\r
+        index = 0;\r
+        charArray = str.toCharArray();\r
+        length = str.length();\r
+    }\r
+\r
+    public String toString() {\r
+        return charArray == null\r
+               ? ""\r
+               : new String(charArray, index, length);\r
+    }\r
+\r
+    static final SubString emptySubString = new SubString();\r
+\r
+    char[] charArray;\r
+    int    index;\r
+    int    length;\r
+}\r
+\r
diff --git a/src/org/xwt/Box.java b/src/org/xwt/Box.java
new file mode 100644 (file)
index 0000000..1fc2e82
--- /dev/null
@@ -0,0 +1,1454 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import org.xwt.util.*;
+import org.mozilla.javascript.*;
+
+/**
+ *  <p>
+ *  Encapsulates the data for a single XWT box as well as all layout
+ *  rendering logic.
+ *  </p>
+ *
+ *  <p>
+ *  This is the real meat of XWT. Part of its monolithic design is for
+ *  performance reasons: deep inheritance heirarchies are slow, and
+ *  neither javago nor GCJ can inline across class boundaries.
+ *  </p>
+ *
+ *  <p>The rendering process consists of two phases:</p>
+ *
+ *  <ol><li> <b>Prerendering pipeline</b>: a set of methods that
+ *           traverse the tree (DFS, postorder) immediately before
+ *           rendering. These methods calculate the appropriate size
+ *           and position for all boxes. If no changes requiring a
+ *           re-prerender have been made to a box or any of its
+ *           descendants, <tt>needs_prerender</tt> will be false, and the box
+ *           and its descendants will be skipped.
+ *
+ *      <li> <b>Rendering pipeline</b>: a second set of methods that
+ *           traverses the tree (DFS, preorder) and renders each Box
+ *           onto the associated Surface. The render() method takes a
+ *           region (x,y,w,h) as an argument, and will only render
+ *           onto pixels within that region. Boxes which lie
+ *           completely outside that region will be skipped.
+ *  </ol>
+ *
+ *  <p>
+ *  Either of these two phases may <i>abort</i> at any time by setting
+ *  <tt>surface.abort</tt> to true. Currently, there are two reasons
+ *  for aborting: a <tt>SizeChange</tt> or <tt>PosChange</tt> was
+ *  triggered in the prerender phase (which ran scripts which modified
+ *  the boxtree's composition, requiring another prerender pass), or
+ *  else the user resized the surface. In either case, the
+ *  [pre]rendering process is halted as soon as possible and
+ *  re-started from the beginning.
+ *  </p>
+ *
+ *  <p>Why are these done as seperate passes over the box tree?</p>
+ * 
+ *  <ol><li> Sometimes we need to make several trips through
+ *           <tt>prerender()</tt?, as a result of <tt>SizeChange</tt>
+ *           or <tt>PosChange</tt>. Calling <tt>prerender()</tt> is
+ *           cheap; calling <tt>render()</tt> is expensive.
+ *
+ *      <li> Even if no <tt>SizeChange</tt> or <tt>PosChange</tt> traps are
+ *           triggered, updates to the size and position of boxes in
+ *           the prerender phase can cause additional regions to be
+ *           added to the dirty list. If the prerender and render
+ *           passes were integrated, this might cause some screen
+ *           regions to be painted twice (or more) in a single pass,
+ *           which hurts performance.
+ *  </ol>
+ *
+ *  <p>
+ *  A box's <tt>_cmin_*</tt> variables hold the minimum possible
+ *  dimensions of this box, taking into account the size of its
+ *  children (and their children). The cmin variables must be kept in
+ *  sync with its other geometric properties and the geometric
+ *  properties of its children at all times -- they are not
+ *  periodically recomputed during the [pre]rendering process, as size
+ *  and pos are. If any data changes which might invalidate the cmin,
+ *  the method <tt>sync_cmin_to_children()</tt> must be invoked
+ *  immediately.
+ *  </p>
+ *
+ *  <p>
+ *  A note on coordinates: the Box class represents regions
+ *  internally as x,y,w,h tuples, even though the DoubleBuffer class
+ *  uses x1,y1,x2,y2 tuples.
+ * </p>
+ */
+public final class Box extends JSObject {
+
+
+    // Static Data //////////////////////////////////////////////////////////////
+
+    /** a pool of one-element Object[]'s */
+    private static Queue singleObjects = new Queue(100);
+
+    /** caches images, keyed on resource name or url */
+    public static Hash pictureCache = new Hash();
+
+    /** cache of border objects */
+    static Hash bordercache = new Hash();
+
+    /** stores image names, keyed on image object */
+    static Hash imageToNameMap = new Hash();
+
+    /** the empty object, used for get-traps */
+    private static Object[] emptyobj = new Object[] { };
+
+
+    // Instance Data: Templates ////////////////////////////////////////////////////////
+
+    /** the unresolved name of the first template applied to this node [ignoring preapply's], or null if this template was specified anonymously */
+    String templatename = null;
+
+    /** the importlist which was used to resolve <tt>templatename</tt>, or null if this template was specified anonymously */
+    String[] importlist = Template.defaultImportList;
+
+    /** the template instance that resulted from resolving <tt>templatename</tt> using <tt>importlist</tt>, or the anonymous template applied */
+    Template template = null;
+
+
+    // Instance Data: Geometry ////////////////////////////////////////////////////////
+
+    /** The number of elements in the geom array */
+    public static final int NUMINTS = 10;
+
+    /** The maximum <i>defined</i> width and height of this box */
+    public static final int dmax = 0;     
+    private short _dmax_0 = 0;
+    private short _dmax_1 = 0;
+    public final short dmax(int axis) { return axis == 0 ? _dmax_0 : _dmax_1; }
+
+    /** The minimum <i>defined</i> width and height of this box */
+    public static final int dmin = 1;     
+    private short _dmin_0 = 0;
+    private short _dmin_1 = 0;
+    public final short dmin(int axis) { return axis == 0 ? _dmin_0 : _dmin_1; }
+    
+    /** The minimum <i>calculated</i> width and height of this box -- unlike dmin, this takes childrens' sizes into account */
+    public static final int cmin = 2;     
+    private short _cmin_0 = 0;
+    private short _cmin_1 = 0;
+    public final short cmin(int axis) { return axis == 0 ? _cmin_0 : _cmin_1; }
+
+    /** The position of this box, relitave to the parent */
+    public static final int abs = 3;      
+    private short _abs_0 = 0;
+    private short _abs_1 = 0;
+    public final short abs(int axis) { return axis == 0 ? _abs_0 : _abs_1; }
+
+    /** The absolute position of this box (ie relitave to the root); set by the parent */
+    public static final int pos = 4;      
+    private short _pos_0 = 0;
+    private short _pos_1 = 0;
+    public final short pos(int axis) { return axis == 0 ? _pos_0 : _pos_1; }
+
+    /** The actual size of this box; set by the parent. */
+    public static final int size = 5;     
+    short _size_0 = 0;
+    short _size_1 = 0;
+    public final short size(int axis) { return axis == 0 ? _size_0 : _size_1; }
+
+    /** The old actual absolute position of this box (ie relitave to the root) */
+    public static final int oldpos = 6;   
+    private short _oldpos_0 = 0;
+    private short _oldpos_1 = 0;
+    public final short oldpos(int axis) { return axis == 0 ? _oldpos_0 : _oldpos_1; }
+
+    /** The old actual size of this box */
+    public static final int oldsize = 7;  
+    private short _oldsize_0 = 0;
+    private short _oldsize_1 = 0;
+    public final short oldsize(int axis) { return axis == 0 ? _oldsize_0 : _oldsize_1; }
+
+    /** The padding along each edge for this box */
+    public static final int pad = 8;      
+    private short _pad_0 = 0;
+    private short _pad_1 = 0;
+    public final short pad(int axis) { return axis == 0 ? _pad_0 : _pad_1; }
+
+    /** The dimensions of the text in this box */
+    public static final int textdim = 9;
+    private short _textdim_0 = 0;
+    private short _textdim_1 = 0;
+    public final short textdim(int axis) { return axis == 0 ? _textdim_0 : _textdim_1; }
+
+
+    // Instance Data /////////////////////////////////////////////////////////////////
+
+    /** This box's mouseinside property, as defined in the reference */
+    boolean mouseinside = false;
+
+    /** If redirect is enabled, this holds the Box redirected to */
+    Box redirect = this;
+
+    /** the Box's font -- you must call textupdate() after changing this */
+    String font = Platform.getDefaultFont();
+
+    /** The surface for us to render on; null if none; INVARIANT: surface == getParent().surface */
+    Surface surface = null;
+
+    /** Our alignment: top/left == -1, center == 0, bottom/right == +1 */
+    byte align = 0;
+
+    /** Our background image; to set this, use setImage() */
+    public Picture image;
+
+    /** The flex for this box */
+    int flex = 1;
+
+    /** The orientation, horizontal == 0, vertical == 1 */
+    byte o = 0;
+
+    /** The opposite of the orientation */
+    byte xo = 1;
+
+    /** The id of this Box */
+    public String id = "";
+
+    /** The text of this Box -- you must call textupdate() after changing this */
+    String text = "";
+
+    /** The color of the text in this Box in 00RRGGBB form -- default is black */
+    int textcolor = 0xFF000000;
+
+    /** The background color of this box in AARRGGBB form -- default is clear; alpha is all-or-nothing */
+    int color = 0x00000000;
+
+    /** Holds four "strip images" -- 0=top, 1=bottom, 2=left, 3=right, 4=all */
+    Picture[] border = null;
+        
+    /**
+     *  The dirty() method walks up the tree, clipping the region being dirtied to each parent -- if this value is less than or
+     *  equal to surface.dirtiedTimeStamp, it means that the entire screen region covered by this box has been dirtied, so dirty()'s on
+     *  children can short-circuit.
+     */
+    int dirtiedTimeStamp = 0;
+
+    /** true iff the box's background image should be tiled */
+    boolean tile = false;
+
+    /** True iff the Box is invisible */
+    public boolean invisible = false;
+
+    /** If true, the Box will force its own size to the natural size of its background image */
+    boolean sizetoimage = false;
+
+    /** If true, the box will shrink to the smallest vertical size possible */
+    boolean vshrink = false;
+
+    /** If true, the box will shrink to the smallest horizontal size possible */
+    boolean hshrink = false;
+
+    /** If true, the box will be positioned absolutely */
+    boolean absolute = false;
+
+    /** True iff the Box must be run through the prerender() pipeline before render()ing;<br>
+     *  INVARIANT: if (needs_prerender) then getParent().needs_prerender. **/
+    boolean needs_prerender = true;
+
+    /** The cursor for this Box -- only meaningful on the root Box */
+    public String cursor = null;
+
+    /** Any traps placed on this Box */
+    public Hash traps = null;
+
+
+    // Instance Data: IndexOf  ////////////////////////////////////////////////////////////
+
+    /** The indexof() Function; created lazily */
+    public Function indexof = null;
+    public Function indexof() {
+        if (indexof == null) indexof = new IndexOf();
+        return indexof;
+    }
+
+    /** a trivial private class to serve as the box.indexof function object */
+    private class IndexOf extends JSObject implements Function {
+        public IndexOf() { this.setSeal(true); }
+        public Scriptable construct(Context cx, Scriptable scope, java.lang.Object[] args) { return null; }
+        public Object call(Context cx, Scriptable scope, Scriptable thisObj, java.lang.Object[] args) throws JavaScriptException {
+            if (args == null || args.length != 1 || args[0] == null || !(args[0] instanceof Box)) return new Integer(-1);
+            Box b = (Box)args[0];
+            if (b.getParent() != Box.this) {
+                if (redirect == null || redirect == Box.this) return new Integer(-1);
+                return Box.this.redirect.indexof().call(cx, scope, thisObj, args);
+            }
+            return new Integer(b.getIndexInParent());
+        }
+    }
+
+
+    // Methods which enforce/preserve invariants ////////////////////////////////////////////
+
+    /** This method MUST be used to change geometry values -- it ensures that certain invariants are preserved. */
+    public final void set(int which, int axis, int newvalue) { set(which, axis, (short)newvalue); }
+    public final void set(int which, int axis, short newvalue) {
+
+        // if this Box is the root of the Surface, notify the Surface of size changes
+        if (getParent() == null && surface != null && which == size)
+            surface._setSize(axis == 0 ? newvalue : size(0), axis == 1 ? newvalue : size(1));
+
+        switch(which) {
+        case dmin: if (dmin(axis) == newvalue) return; if (axis == 0) _dmin_0 = newvalue; else _dmin_1 = newvalue; break;
+        case dmax: if (dmax(axis) == newvalue) return; if (axis == 0) _dmax_0 = newvalue; else _dmax_1 = newvalue; break;
+        case cmin: if (cmin(axis) == newvalue) return; if (axis == 0) _cmin_0 = newvalue; else _cmin_1 = newvalue; break;
+        case abs: if (abs(axis) == newvalue) return; if (axis == 0) _abs_0 = newvalue; else _abs_1 = newvalue; break;
+        case pos: if (pos(axis) == newvalue) return; if (axis == 0) _pos_0 = newvalue; else _pos_1 = newvalue; break;
+        case size: if (size(axis) == newvalue) return; if (axis == 0) _size_0 = newvalue; else _size_1 = newvalue; break;
+        case oldpos: if (oldpos(axis) == newvalue) return; if (axis == 0) _oldpos_0 = newvalue; else _oldpos_1 = newvalue; break;
+        case oldsize: if (oldsize(axis) == newvalue) return; if (axis == 0) _oldsize_0 = newvalue; else _oldsize_1 = newvalue; break;
+        case pad: if (pad(axis) == newvalue) return; if (axis == 0) _pad_0 = newvalue; else _pad_1 = newvalue; break;
+        case textdim: if (textdim(axis) == newvalue) return; if (axis == 0) _textdim_0 = newvalue; else _textdim_1 = newvalue; break;
+        default: return;
+        }
+
+        // size must always agree with dmin/dmax
+        if (which == dmin) set(size, axis, max(size(axis), newvalue));
+        if (which == dmax) set(size, axis, min(size(axis), newvalue));
+
+        // keep obedience to shrink directives
+        if (which == cmin || which == textdim || which == pad || which == dmin)
+            if ((hshrink && axis == 0) || (vshrink && axis == 1))
+                set(dmax, axis, max(cmin(axis), (textdim(axis) + 2 * pad(axis)), dmin(axis)));
+
+        // keep cmin in line with dmin/dmax/textdim
+        if (which == dmax || which == dmin || which == textdim || which == pad || which == cmin)
+            set(cmin, axis,
+                max(
+                    min(cmin(axis), dmax(axis)),
+                    dmin(axis),
+                    min(dmax(axis), textdim(axis) + 2 * pad(axis))
+                    )
+                );
+        
+        // if the pad changes, update cmin
+        if (which == pad) sync_cmin_to_children();
+
+        // if the cmin changes, we need to be re-prerendered
+        if (which == cmin) mark_for_prerender(); 
+
+        // if the absolute position of a box changes, its parent needs to be re-prerendered (to update the child's position)
+        if (which == abs && getParent() != null) getParent().mark_for_prerender();
+
+        // if our cmin changes, then our parent's needs to be recalculated
+        if (getParent() != null && which == cmin) {
+            mark_for_prerender();
+            getParent().sync_cmin_to_children();
+        }
+
+    }
+
+    /** Marks this node and all its ancestors so that they will be prerender()ed */
+    public final void mark_for_prerender() {
+        if (needs_prerender) return;
+        needs_prerender = true;
+        if (getParent() != null) getParent().mark_for_prerender();
+    }
+
+    /** Ensures that cmin is in sync with the cmin's of our children. This should be called whenever a child is added or
+     *  removed, as well as when our pad is changed. */
+    final void sync_cmin_to_children() {
+        short co = (short)(2 * pad(o));
+        short cxo = (short)(2 * pad(xo));
+        
+        for(Box bt = getChild(0); bt != null; bt = bt.nextSibling()) {
+            if (bt.invisible || bt.absolute) continue;
+            co += bt.cmin(o);
+            cxo = (short)max(bt.cmin(xo) + 2 * pad(xo), cxo);
+        }
+        
+        set(cmin, o, co);
+        set(cmin, xo, cxo);
+    }
+
+    /** must be called after changes to <tt>image</tt> or <tt>border</tt> if sizetoimage is true */
+    public void syncSizeToImage() {
+        set(dmax, 0, (image == null ? 0 : image.getWidth()) + (border == null ? 0 : border[2].getWidth()) * 2);
+        set(dmax, 1, (image == null ? 0 : image.getHeight()) + (border == null ? 0 : border[0].getHeight()) * 2);
+        set(dmin, 0, (image == null ? 0 : image.getWidth()) + (border == null ? 0 : border[2].getWidth()) * 2);
+        set(dmin, 1, (image == null ? 0 : image.getHeight()) + (border == null ? 0 : border[0].getHeight()) * 2);
+    }
+
+    /** This must be called when font or text is changed */
+    void textupdate() {
+        if (text.equals("")) {
+            set(textdim, 0, 0);
+            set(textdim, 1, 0);
+        } else {
+            XWF xwf = XWF.getXWF(font);
+            if (xwf == null) {
+                set(textdim, 0, Platform.stringWidth(font, text));
+                set(textdim, 1, (Platform.getMaxAscent(font) + Platform.getMaxDescent(font)));
+            } else {
+                set(textdim, 0, xwf.stringWidth(text));
+                set(textdim, 1, (xwf.getMaxAscent() + xwf.getMaxDescent()));
+            }
+        }
+    }
+
+
+    // Instance Methods /////////////////////////////////////////////////////////////////////
+
+    /** Changes the Surface that this Box draws on. */    
+    protected void setSurface(Surface newSurface) {
+        if (surface == newSurface) return;
+        mouseinside = false;
+        if ((is_trapped("KeyPressed") || is_trapped("KeyReleased")) && surface != null)
+            surface.keywatchers.removeElement(this);
+        surface = newSurface;
+        if ((is_trapped("KeyPressed") || is_trapped("KeyReleased")) && surface != null)
+            surface.keywatchers.addElement(this);
+        for(Box i = getChild(0); i != null; i = i.nextSibling()) i.setSurface(surface);
+        if (numChildren() == 0) mark_for_prerender();
+    }
+
+    /** loads the image described by string str, possibly blocking for a network load */
+    private static ImageDecoder getImage(String str) {
+        ImageDecoder ret = null;
+        boolean ispng = false;
+
+        if (str.indexOf(':') == -1) {
+            String s = str;
+            byte[] b = Resources.getResource(Resources.resolve(s + ".png", null));
+            if (b == null) return null;
+            return PNG.decode(new ByteArrayInputStream(b), str);
+            
+        } else {
+            Thread thread = Thread.currentThread();
+            if (!(thread instanceof ThreadMessage)) {
+                if (Log.on) Log.log(Box.class, "HTTP images can not be loaded from the foreground thread");
+                return null;
+            }
+            ThreadMessage mythread = (ThreadMessage)thread;
+            mythread.setPriority(Thread.MIN_PRIORITY);
+            mythread.done.release();
+            try {
+                if (str.endsWith(".png")) ret = PNG.decode(Platform.urlToInputStream(new URL(str)), str);
+                else ret = GIF.decode(Platform.urlToInputStream(new URL(str)), str);
+                return ret;
+
+            } catch (IOException e) {
+                if (Log.on) Log.log(Box.class, "error while trying to load an image from " + str);
+                if (Log.on) Log.log(Box.class, e);
+                return null;
+
+            } finally {
+                MessageQueue.add(mythread);
+                mythread.setPriority(Thread.NORM_PRIORITY);
+                mythread.go.block();
+            }
+        }
+    }
+
+    /** gets an Image using getImage(), adds it to the cache, and creates a Picture from it */
+    public static Picture getPicture(String os) {
+        Picture ret = null;
+        ret = (Picture)pictureCache.get(os);
+        if (ret != null) return ret;
+        ImageDecoder id = getImage(os);
+        if (id == null) return null;
+        ret = Platform.createPicture(id);
+        pictureCache.put(os, ret);
+        imageToNameMap.put(ret, os);
+        return ret;
+    }
+
+    /** Sets the image; argument should be a fully qualified s name or an URL */
+    void setImage(String s) {
+        if ((s == null && image == null) || (s != null && image != null && s.equals(imageToNameMap.get(image)))) return;
+        if (s == null || s.equals("")) {
+            image = null;
+            if (sizetoimage) syncSizeToImage();
+            dirty();
+        } else {
+            image = getPicture(s);
+            if (image == null) {
+                if (Log.on) Log.log(Box.class, "unable to load image " + s + " at " +
+                                    Context.enter().interpreterSourceFile + ":" + Context.enter().interpreterLine);
+                return;
+            }
+            if (sizetoimage) syncSizeToImage();
+            dirty();
+        }
+    }
+    
+    /** Sets the border; argument should be a fully qualified resource name or an URL */
+    void setBorder(String s) {
+        if ((s == null && border == null) || (s != null && border != null && s.equals(imageToNameMap.get(border)))) return;
+        if (s == null || s.equals("")) {
+            border = null;
+            if (sizetoimage) syncSizeToImage();
+            dirty();
+
+        } else {
+            border = (Picture[])bordercache.get(s);
+            if (border == null) {
+                ImageDecoder id = getImage(s);
+                if (id == null) {
+                    if (Log.on) Log.log(this, "unable to load border image " + s + " at " +
+                                    Context.enter().interpreterSourceFile + ":" + Context.enter().interpreterLine);
+                    return;
+                }
+                int[] data = id.getData();
+                int w = id.getWidth();
+                int h = id.getHeight();
+                int hpad = w / 2;
+                int vpad = h / 2;
+
+                int[][] dat = new int[4][];
+                dat[0] = new int[100 * vpad];
+                dat[1] = new int[100 * vpad];
+                dat[2] = new int[100 * hpad];
+                dat[3] = new int[100 * hpad];
+
+                for(int i=0; i<100; i++) {
+                    for(int j=0; j<vpad; j++) {
+                        dat[0][i + j * 100] = data[hpad + j * w];
+                        dat[1][i + (vpad - j - 1) * 100] = data[hpad + (h - j - 1) * w];
+                    }
+                    for(int j=0; j<hpad; j++) {
+                        dat[2][j + i * hpad] = data[j + vpad * w];
+                        dat[3][hpad - j - 1 + i * hpad] = data[w - j - 1 + vpad * w];
+                    }
+                }
+
+                border = new Picture[5];
+                border[0] = Platform.createPicture(dat[0], 100, vpad);
+                border[1] = Platform.createPicture(dat[1], 100, vpad);
+                border[2] = Platform.createPicture(dat[2], hpad, 100);
+                border[3] = Platform.createPicture(dat[3], hpad, 100);
+                border[4] = (Picture)pictureCache.get(s);
+                if (border[4] == null) {
+                    border[4] = Platform.createPicture(data, w, h);
+                    pictureCache.put(s, border[4]);
+                    imageToNameMap.put(border[4], s);
+                }
+                bordercache.put(s, border);
+            }
+            if (sizetoimage) syncSizeToImage();
+            dirty();
+        }
+    }
+
+    /** returns true if the property has a trap on it */
+    boolean is_trapped(String property) {
+        if (traps == null) {
+            return false;
+        } else {
+            Object gc = traps.get(property);
+            return (gc != null &&
+                    !(gc instanceof org.mozilla.javascript.Undefined) &&
+                    gc != org.mozilla.javascript.Scriptable.NOT_FOUND);
+        }
+    }
+    
+    /** Adds the node's current actual geometry to the Surface's dirty list */
+    void dirty() {
+        dirty(pos(0), pos(1), size(0), size(1));
+        if (surface != null) dirtiedTimeStamp = surface.dirtiedTimeStamp + 1;
+    }
+
+    /** Adds the intersection of (x,y,w,h) and the node's current actual geometry to the Surface's dirty list */
+    final void dirty(int x, int y, int w, int h) {
+        for(Box cur = this; cur != null; cur = cur.getParent()) {
+            if (surface != null && cur.dirtiedTimeStamp > surface.dirtiedTimeStamp && cur.dirtiedTimeStamp < Integer.MAX_VALUE - 1) return;
+            w = min(x + w, cur.pos(0) + cur.size(0)) - max(x, cur.pos(0));
+            h = min(y + h, cur.pos(1) + cur.size(1)) - max(y, cur.pos(1));
+            x = max(x, cur.pos(0));
+            y = max(y, cur.pos(1));
+            if (w <= 0 || h <= 0) return;
+        }
+        if (surface != null) surface.dirty(x, y, w, h);
+    }
+
+    /**
+     *  Given an old and new mouse position, this will update <tt>mouseinside</tt> and check
+     *  to see if this node requires any Enter, Leave, or Move notifications.
+     *
+     *  @param forceleave set to true by the box's parent if the mouse is inside an older
+     *                    sibling, which is covering up this box.
+     */
+    void Move(int oldmousex, int oldmousey, int mousex, int mousey) { Move(oldmousex, oldmousey, mousex, mousey, false); }
+    void Move(int oldmousex, int oldmousey, int mousex, int mousey, boolean forceleave) {
+
+        boolean wasinside = mouseinside;
+        boolean isinside = !invisible && inside(mousex, mousey) && !forceleave;
+        mouseinside = isinside;
+
+        if (!wasinside && !isinside) return;
+        
+        if (!wasinside && isinside && is_trapped("Enter")) put("Enter", null, this);
+        else if (wasinside && !isinside && is_trapped("Leave")) put("Leave", null, this);
+        else if (wasinside && isinside && (mousex != oldmousex || mousey != oldmousey) && is_trapped("Move")) put("Move", null, this);
+
+        if (isinside && cursor != null && surface != null) surface.cursor = cursor;
+
+        // if the mouse has moved into our padding region, it is considered 'outside' all our children
+        if (!(mousex >= pos(0) + pad(0) && mousey >= pos(1) + pad(1) &&
+              mousex < pos(0) + size(0) - pad(0) && mousey < pos(1) + size(1) + pad(1))) forceleave = true;
+
+        for(Box b = getChild(numChildren() - 1); b != null; b = b.prevSibling()) {
+            b.Move(oldmousex, oldmousey, mousex, mousey, forceleave);
+            if (b.inside(mousex, mousey)) forceleave = true;
+        }
+    }
+
+    /** creates a new box from an anonymous template; <tt>ids</tt> is passed through to Template.apply() */
+    Box(Template anonymous, Vec pboxes, Vec ptemplates) {
+        super(true);
+        set(dmax, 0, Short.MAX_VALUE);
+        set(dmax, 1, Short.MAX_VALUE);
+        template = anonymous;
+        template.apply(this, pboxes, ptemplates);
+        templatename = null;
+        importlist = null;
+    }
+
+    /** creates a new box from an unresolved templatename and an importlist; use "box" for an untemplatized box */
+    public Box(String templatename, String[] importlist) {
+        super(true);
+        set(dmax, 0, Short.MAX_VALUE);
+        set(dmax, 1, Short.MAX_VALUE);
+        this.importlist = importlist;
+        template = "box".equals(templatename) ? null : Template.getTemplate(templatename, importlist);
+        this.templatename = templatename;
+        if (template != null) {
+            template.apply(this, null, null);
+            if (redirect == this && !"self".equals(template.redirect)) redirect = null;
+        }
+    }
+    
+
+    // Prerendering Pipeline ///////////////////////////////////////////////////////////////
+
+    /** Checks if the Box's size has changed, dirties it if necessary, and makes sure childrens' sizes are up to date */
+    void prerender() {
+
+        if (getParent() == null) {
+            set(pos, 0, 0);
+            set(pos, 1, 0);
+        }
+
+        if (pos(0) != oldpos(0) || pos(1) != oldpos(1) || size(0) != oldsize(0) || size(1) != oldsize(1)) {
+            needs_prerender = true;
+            check_geometry_changes();
+        }
+
+        if (!needs_prerender) return; 
+        needs_prerender = false;
+        if (numChildren() == 0) return;
+
+        int sumchildren = sizeChildren();
+        positionChildren(pos(o) + pad(o) + max(0, size(o) - 2 * pad(o) - sumchildren) / 2);
+
+        for(Box b = getChild(0); b != null; b = b.nextSibling()) {
+            b.prerender();
+            if (surface.abort) {
+                mark_for_prerender();
+                return;
+            }
+        }
+    }
+
+    /** if the size or position of a box has changed, dirty() the appropriate regions and possibly send Enter/Leave/SizeChange/PosChange */
+    private void check_geometry_changes() {
+
+        // FASTPATH: if we haven't moved position (just changed size), and we're not a stretched image:
+        if (oldpos(0) == pos(0) && oldpos(1) == pos(1) && (image == null || tile)) {
+            
+            int bw = border == null ? 0 : border[2].getWidth();
+            int bh = border == null ? 0 : border[0].getHeight();
+            
+            // dirty only the *change* in the area we cover, both on ourselves and on our parent
+            for(Box cur = this; cur != null && (cur == this || cur == this.getParent()); cur = cur.getParent()) {
+                cur.dirty(pos(0) + min(oldsize(0) - bw, size(0) - bw),
+                          pos(1),
+                          Math.abs(oldsize(0) - size(0)) + bw,
+                          max(oldsize(1), size(1)));
+                cur.dirty(pos(0),
+                          pos(1) + min(oldsize(1) - bh, size(1) - bh),
+                          max(oldsize(0), size(0)),
+                          Math.abs(oldsize(1) - size(1)) + bh);
+            }
+            
+        // SLOWPATH: dirty ourselves, as well as our former position on our parent
+        } else {
+            dirty();
+            if (getParent() != null) getParent().dirty(oldpos(0), oldpos(1), oldsize(0), oldsize(1));
+            
+        }
+        
+        boolean sizechange = false;
+        boolean poschange = false;
+        if ((oldsize(0) != size(0) || oldsize(1) != size(1)) && is_trapped("SizeChange")) sizechange = true;
+        if ((oldpos(0) != pos(0) || oldpos(1) != pos(1)) && is_trapped("PosChange")) poschange = true;
+        
+        set(oldsize, 0, size(0));
+        set(oldsize, 1, size(1));
+        set(oldpos, 0, pos(0));
+        set(oldpos, 1, pos(1));
+
+        if (sizechange || poschange)
+            if (surface.sizePosChangesSinceLastRender++ > 500) {
+                if (Log.on) Log.log(this, "Warning, more than 500 SizeChange/PosChange traps triggered since last complete render");
+                try {
+                    Trap t = sizechange ? Trap.getTrap(this, "SizeChange") : Trap.getTrap(this, "PosChange");
+                    InterpretedFunction f = (InterpretedFunction)t.f;
+                    if (Log.on) Log.log(this, "Current trap is at " + f.getSourceName() + ":" + f.getLineNumbers()[0]);
+                } catch (Throwable t) { }
+            }
+
+        if (sizechange) put("SizeChange", null, Boolean.TRUE);
+        if (poschange) put("PosChange", null, Boolean.TRUE);
+        if (sizechange || poschange) {
+            surface.abort = true;
+            return;
+        }
+    }
+    
+
+    /** sets our childrens' sizes */
+    int sizeChildren() {
+
+        // Set sizes along minor axis, as well as sizes for absolute-positioned children
+        for(Box bt = getChild(0); bt != null; bt = bt.nextSibling()) {
+            if (bt.invisible) continue;
+            if (bt.absolute) {
+                bt.set(size, o, max(bt.cmin(o), min(size(o) - bt.abs(o) - pad(o), bt.dmax(o))));
+                bt.set(size, xo, max(bt.cmin(xo), min(size(xo) - bt.abs(xo) - pad(xo), bt.dmax(xo))));
+            } else if (xo == 0 && bt.hshrink || xo == 1 && bt.vshrink) {
+                bt.set(size, xo, bt.cmin(xo));
+            } else {
+                bt.set(size, xo, bound(bt.cmin(xo), size(xo) - 2 * pad(xo), bt.dmax(xo)));
+            }
+        }
+
+        // the ideal size of our children, along our own major axis
+        int goal = (o == 0 && hshrink) || (o == 1 && vshrink) ? cmin(o) - 2 * pad(o) : size(o) - 2 * pad(o);
+
+        // the current sum of the sizes of all children
+        int total = 0;
+
+        // each box is set to bound(box.cmin, box.flex * factor, box.dmax)
+        int factor = 0;
+
+        // Algorithm: we set the sizes of all boxes to bound(cmin, flex * factor, dmax) for some global value
+        //            'factor'. We figure out what 'factor' should be by starting at zero, and slowly increasing
+        //            it. After each pass, 'factor' is set to the smaller of two values: ((goal - total) /
+        //            remaining_flex) or the next largest value of factor which will cause some box to exceed its
+        //            cmin or dmax (thereby changing remaining_flex).
+
+        while(true) {
+            total = 0;
+
+            // the sum of the flexes of all boxes which are not pegged at either cmin or dmax
+            int remaining_flex = 0;
+
+            // this is the next largest value of factor at which some box exceeds its cmin/dmax
+            int nextjoint = Integer.MAX_VALUE;
+            
+            for(Box bt = getChild(0); bt != null; bt = bt.nextSibling()) {
+                if (bt.absolute || bt.invisible) continue;
+
+                bt.set(size, o, bound(bt.cmin(o), factor * bt.flex, bt.dmax(o)));
+                total += bt.size(o);
+
+                if (factor * bt.flex < bt.cmin(o) && bt.size(o) == bt.cmin(o)) {
+                    nextjoint = min(nextjoint, divide_round_up(bt.cmin(o), bt.flex));
+
+                } else if (bt.size(o) < bt.dmax(o)) {
+                    remaining_flex += bt.flex;
+                    nextjoint = min(nextjoint, divide_round_up(bt.dmax(o), bt.flex));
+
+                }
+            }
+
+            if (remaining_flex == 0) {
+                if (nextjoint <= factor) break;
+                factor = nextjoint;
+            } else {
+                factor = min((goal - total + factor * remaining_flex) / remaining_flex, nextjoint);
+            }
+
+            if (goal - total <= remaining_flex) break;
+        }
+
+        // arbitrarily distribute out any leftovers resulting from rounding errors
+        int last = 0;
+        while(goal > total && total != last) {
+            last = total;
+            for(Box bt = getChild(0); bt != null; bt = bt.nextSibling()) {
+                int newsize = bound(bt.cmin(o), bt.size(o) + 1, bt.dmax(o));
+                total += newsize - bt.size(o);
+                bt.set(size, o, newsize);
+            }
+        }
+
+        return total;
+    }
+    
+    /** positions this Box's children; cur is the starting position along this' major axis */
+    void positionChildren(int cur) {
+        for(Box bt = getChild(0); bt != null; bt = bt.nextSibling()) {
+            if (bt.invisible) continue;
+            if (bt.absolute) {
+                bt.set(pos, 0, pos(0) + bt.abs(0));
+                bt.set(pos, 1, pos(1) + bt.abs(1));
+            } else {
+                bt.set(pos, xo, pos(xo) + pad(xo) + max(0, ((size(xo) - 2 * pad(xo) - bt.size(xo)) * (bt.align + 1)) / 2));
+                bt.set(pos, o, cur);
+                bt.set(abs, 0, bt.pos(0) - pos(0));
+                bt.set(abs, 1, bt.pos(1) - pos(1));
+                cur += bt.size(o);
+            }
+        }
+    }
+
+
+    // Rendering Pipeline /////////////////////////////////////////////////////////////////////
+
+    /** Renders self and children within the specified region. All rendering operations are clipped to xIn,yIn,wIn,hIn */
+    void render(int xIn, int yIn, int wIn, int hIn, DoubleBuffer buf) {
+        dirtiedTimeStamp = surface.dirtiedTimeStamp;
+        if (surface.abort || invisible) return;
+
+        // intersect the x,y,w,h rendering window with ourselves; quit if it's empty
+        int x = max(xIn, getParent() == null ? 0 : pos(0));
+        int y = max(yIn, getParent() == null ? 0 : pos(1));
+        int w = min(xIn + wIn, (getParent() == null ? 0 : pos(0)) + size(0)) - x;
+        int h = min(yIn + hIn, (getParent() == null ? 0 : pos(1)) + size(1)) - y;
+        if (w <= 0 || h <= 0) return;
+
+        if (border != null) renderBorder(x, y, w, h, buf);
+
+        if ((color & 0xFF000000) != 0x00000000 || getParent() == null) {
+            int bw = border == null ? 0 : border[2].getWidth();
+            int bh = border == null ? 0 : border[0].getHeight();
+            buf.setClip(0, 0, buf.getWidth(), buf.getHeight());
+            buf.fillRect(max(x, pos(0) + bw),
+                         max(y, pos(1) + bh),
+                         min(x + w, pos(0) + size(0) - bw),
+                         min(y + h, pos(1) + size(1) - bh),
+                         (color & 0xFF000000) != 0 ? color : SpecialBoxProperty.lightGray);
+        }
+
+        if (image != null) {
+            if (tile) renderTiledImage(x, y, w, h, buf);
+            else renderStretchedImage(x, y, w, h, buf);
+        }
+
+        if (text != null && !text.equals("")) renderText(x, y, w, h, buf);
+
+        // now subtract the pad region from the clip region before proceeding
+        int x2 = max(x, pos(0) + pad(0));
+        int y2 = max(y, pos(1) + pad(1));
+        int w2 = min(x + w, pos(0) + size(0) - pad(0)) - x2;
+        int h2 = min(y + h, pos(1) + size(1) - pad(1)) - y2;
+
+        for(Box b = getChild(0); b != null; b = b.nextSibling())
+            b.render(x2, y2, w2, h2, buf);   
+    }
+
+    private void renderBorder(int x, int y, int w, int h, DoubleBuffer buf) {
+        int bw = border[4].getWidth();
+        int bh = border[4].getHeight();
+        buf.setClip(x, y, w + x, h + y);
+
+        if ((color & 0xFF000000) != 0xFF000000) {
+
+            // if the color is null, we have to be very careful about drawing the corners
+            if (Log.verbose) Log.log(this, "WARNING: (color == null && border != null) on box with border " + imageToNameMap.get(border[4]));
+
+            // upper left corner
+            buf.drawPicture(border[4],
+                            pos(0), pos(1), pos(0) + bw / 2, pos(1) + bh / 2,
+                            0, 0, bw / 2, bh / 2);
+            
+            // upper right corner
+            buf.drawPicture(border[4],
+                            pos(0) + size(0) - bw / 2, pos(1), pos(0) + size(0), pos(1) + bh / 2,
+                            bw - bw / 2, 0, bw, bh / 2);
+
+            // lower left corner
+            buf.drawPicture(border[4],
+                            pos(0), pos(1) + size(1) - bh / 2, pos(0) + bw / 2, pos(1) + size(1),
+                            0, bh - bh / 2, bw / 2, bh);
+
+            // lower right corner
+            buf.drawPicture(border[4],
+                            pos(0) + size(0) - bw / 2, pos(1) + size(1) - bh / 2, pos(0) + size(0), pos(1) + size(1),
+                            bw - bw / 2, bh - bh / 2, bw, bh);
+
+        } else {
+            buf.drawPicture(border[4], pos(0), pos(1));                                // upper left corner
+            buf.drawPicture(border[4], pos(0) + size(0) - bw, pos(1));                 // upper right corner
+            buf.drawPicture(border[4], pos(0), pos(1) + size(1) - bh);                 // lower left corner
+            buf.drawPicture(border[4], pos(0) + size(0) - bw, pos(1) + size(1) - bh);  // lower right corner                                     
+
+        }
+
+        // top and bottom edges
+        buf.setClip(max(x, pos(0) + bw / 2), y, min(x + w, pos(0) + size(0) - bw / 2), h + y);
+        for(int i = max(x, pos(0) + bw / 2); i + 100 < min(x + w, pos(0) + size(0) - bw / 2); i += 100) {
+            buf.drawPicture(border[0], i, pos(1));
+            buf.drawPicture(border[1], i, pos(1) + size(1) - bh / 2);
+        }
+        buf.drawPicture(border[0], min(x + w, pos(0) + size(0) - bw / 2) - 100, pos(1));
+        buf.drawPicture(border[1], min(x + w, pos(0) + size(0) - bw / 2) - 100, pos(1) + size(1) - bh / 2);
+
+        // left and right edges
+        buf.setClip(x, max(y, pos(1) + bh / 2), w + x, min(y + h, pos(1) + size(1) - bh / 2));
+        for(int i = max(y, pos(1) + bh / 2); i + 100 < min(y + h, pos(1) + size(1) - bh / 2); i += 100) {
+            buf.drawPicture(border[2], pos(0), i);
+            buf.drawPicture(border[3], pos(0) + size(0) - bw / 2, i);
+        }
+        buf.drawPicture(border[2], pos(0), min(y + h, pos(1) + size(1) - bh / 2) - 100);
+        buf.drawPicture(border[3], pos(0) + size(0) - bw / 2, min(y + h, pos(1) + size(1) - bh / 2) - 100);
+
+        buf.setClip(0, 0, buf.getWidth(), buf.getHeight());
+    }
+
+    void renderStretchedImage(int x, int y, int w, int h, DoubleBuffer buf) {
+        buf.setClip(x, y, w + x, h + y);
+        int bw = border == null ? 0 : border[4].getHeight();
+        int bh = border == null ? 0 : border[4].getWidth();
+        buf.drawPicture(image,
+                        pos(0) + bw / 2,
+                        pos(1) + bh / 2,
+                        pos(0) + size(0) - bw / 2,
+                        pos(1) + size(1) - bh / 2,
+                        0, 0, image.getWidth(), image.getHeight());
+        buf.setClip(0, 0, buf.getWidth(), buf.getHeight());
+    }
+
+    void renderTiledImage(int x, int y, int w, int h, DoubleBuffer buf) {
+        int iw = image.getWidth();
+        int ih = image.getHeight();
+        int bh = border == null ? 0 : border[4].getWidth();
+        int bw = border == null ? 0 : border[4].getHeight();
+
+        for(int i=(x - pos(0) - bw)/iw; i <= (x + w - pos(0) - bw)/iw; i++) {
+            for(int j=(y - pos(1) - bh)/ih; j<= (y + h - pos(1) - bh)/ih; j++) {
+                
+                int dx1 = max(i * iw + pos(0), x);
+                int dy1 = max(j * ih + pos(1), y);
+                int dx2 = min((i+1) * iw + pos(0), x + w);
+                int dy2 = min((j+1) * ih + pos(1), y + h);
+                
+                int sx1 = dx1 - (i*iw) - pos(0);
+                int sy1 = dy1 - (j*ih) - pos(1);
+                int sx2 = dx2 - (i*iw) - pos(0);
+                int sy2 = dy2 - (j*ih) - pos(1);
+
+                if (dx2 - dx1 > 0 && dy2 - dy1 > 0 && sx2 - sx1 > 0 && sy2 - sy1 > 0)
+                    buf.drawPicture(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2);
+            }
+        }
+
+    }
+
+    void renderText(int x, int y, int w, int h, DoubleBuffer buf) {
+
+        if ((textcolor & 0xFF000000) == 0x00000000) return;
+        buf.setClip(x, y, w + x, h + y);
+
+        XWF xwf = XWF.getXWF(font);
+        if (xwf != null) {
+            xwf.drawString(buf, text,
+                           pos(0) + pad(0),
+                           pos(1) + pad(1) + xwf.getMaxAscent() - 1,
+                           textcolor);
+        } else {
+            buf.drawString(font, text,
+                           pos(0) + pad(0),
+                           pos(1) + pad(1) + Platform.getMaxAscent(font) - 1,
+                           textcolor);
+        }
+
+        buf.setClip(0, 0, buf.getWidth(), buf.getHeight());
+        
+        int i=0; while(i<font.length() && !Character.isDigit(font.charAt(i))) i++;
+
+        if (font.lastIndexOf('d') > i) {
+            for(int j = pos(0) + pad(0); j < pos(0) + pad(0) + textdim(0); j += 2)
+                buf.fillRect(j, pos(1) + pad(1) + (xwf == null ? Platform.getMaxAscent(font) : xwf.getMaxAscent()) + 3,
+                             j + 1, pos(1) + pad(1) + (xwf == null ? Platform.getMaxAscent(font) : xwf.getMaxAscent()) + 3 + 1,
+                             textcolor);
+
+        } else if (font.lastIndexOf('u') > i) {
+            buf.fillRect(pos(0) + pad(0),
+                        pos(1) + pad(1) + (xwf == null ? Platform.getMaxAscent(font) : xwf.getMaxAscent()) + 3,
+                        pos(0) + pad(0) + textdim(0),
+                        pos(1) + pad(1) + (xwf == null ? Platform.getMaxAscent(font) : xwf.getMaxAscent()) + 3 + 1,
+                        textcolor);
+        }
+
+    }
+
+
+    // Methods to implement org.mozilla.javascript.Scriptable //////////////////////////////////////
+
+    /** Returns the i_th child */
+    public Object get(int i, Scriptable start) {
+        if (redirect == null) return null;
+        if (redirect != this) return redirect.get(i, start);
+        return i >= numChildren() ? null : getChild(i);
+    }
+
+    /**
+     *  Inserts value as child i; calls remove() if necessary.
+     *  This method handles "reinserting" one of your children properly.
+     *  INVARIANT: after completion, getChild(min(i, numChildren())) == newnode
+     *  WARNING: O(n) runtime, unless i == numChildren()
+     */
+    public void put(int i, Scriptable start, Object value) {
+        if (value == null) {
+            if (i > 0 && i < numChildren()) getChild(i).remove();
+            return;
+        }
+        if (value instanceof RootProxy) {
+            if (Log.on) Log.log(this, "attempt to reparent a box via its proxy object at " +
+                                Context.enter().interpreterSourceFile + ":" + Context.enter().interpreterLine);
+            return;
+        } else if (!(value instanceof Box)) {
+            if (Log.on) Log.log(this, "attempt to set a numerical property on a box to anything other than a box at " +
+                                Context.enter().interpreterSourceFile + ":" + Context.enter().interpreterLine);
+            return;
+        }
+        Box newnode = (Box)value;
+        if (redirect == null) {
+            if (Log.on) Log.log(this, "attempt to add a child to a node with a null redirect at " + 
+                                Context.enter().interpreterSourceFile + ":" + Context.enter().interpreterLine);
+            return;
+        } else if (redirect != this) redirect.put(i, null, newnode);
+        else {
+            if (numKids > 15 && children == null) convert_to_array();
+            if (newnode.parent != null) newnode.remove();
+            newnode.parent = this;
+            
+            if (children == null) {
+                if (firstKid == null) {
+                    firstKid = newnode;
+                    newnode.prevSibling = newnode;
+                    newnode.nextSibling = newnode;
+                } else if (i >= numKids) {
+                    newnode.prevSibling = firstKid.prevSibling;
+                    newnode.nextSibling = firstKid;
+                    firstKid.prevSibling.nextSibling = newnode;
+                    firstKid.prevSibling = newnode;
+                } else {
+                    Box cur = firstKid;
+                    for(int j=0; j<i; j++) cur = cur.nextSibling;
+                    newnode.prevSibling = cur.prevSibling;
+                    newnode.nextSibling = cur;
+                    cur.prevSibling.nextSibling = newnode;
+                    cur.prevSibling = newnode;
+                    if (i == 0) firstKid = newnode;
+                }
+                numKids++;
+                
+            } else {
+                if (i >= children.size()) {
+                    newnode.indexInParent = children.size();
+                    children.addElement(newnode);
+                } else {
+                    children.insertElementAt(newnode, i);
+                    for(int j=i; j<children.size(); j++)
+                        getChild(j).indexInParent = j;
+                }
+            }
+            newnode.setSurface(surface);
+
+            // need both of these in case child was already uncalc'ed
+            newnode.mark_for_prerender();
+            mark_for_prerender(); 
+
+            newnode.dirty();
+            sync_cmin_to_children();
+        }
+
+        // note that JavaScript box[0] will invoke put(int i), not put(String s)
+        put("0", null, newnode);
+    }
+    
+    public Object get(String name, Scriptable start) { return get(name, start, false); }
+    public Object get(String name, Scriptable start, boolean ignoretraps) {
+
+        if (name == null || name.equals("")) return null;
+
+        // hack since Rhino needs to be able to grab these functions to create new objects
+        if (name.equals("Object")) return JSObject.defaultObjects.get("Object", null);
+        if (name.equals("Array")) return JSObject.defaultObjects.get("Array", null);
+        if (name.equals("Function")) return JSObject.defaultObjects.get("Function", null);
+        if (name.equals("TypeError")) return JSObject.defaultObjects.get("TypeError", null);
+
+        // See if we're reading back the function value of a trap
+        if (name.charAt(0) == '_') {
+            if (name.charAt(1) == '_') name = name.substring(2);
+            else name = name.substring(1);
+            Trap t = Trap.getTrap(this, name);
+            return t == null ? null : t.f;
+        }
+        
+        // See if we're triggering a trap
+        Trap t = traps == null || ignoretraps ? null : (Trap)traps.get(name);
+        if (t != null && t.isreadtrap) return t.perform(emptyobj);
+
+        // Check for a special handler
+        SpecialBoxProperty gph = (SpecialBoxProperty)SpecialBoxProperty.specialBoxProperties.get(name);
+        if (gph != null) return gph.get(this);
+
+        return super.get(name, start);
+    }
+
+    /** indicate that we don't want JSObject trying to handle these */
+    public boolean has(String name, Scriptable start) {
+        if (name.equals("")) return false;
+        if (traps != null && traps.get(name) != null) return true;
+        if (name.charAt(0) == '_') return true;
+        if (SpecialBoxProperty.specialBoxProperties.get(name) != null) return true;
+        if (name.equals("Function") || name.equals("Array") || name.equals("Object") || name.equals("TypeError")) return true;
+        return super.has(name, start);
+    }
+
+    public void put(String name, Scriptable start, Object value) { put(name, start, value, false, null); }
+    public void put(String name, Scriptable start, Object value, boolean ignoretraps) { put(name, start, value, ignoretraps, null); }
+
+    /**
+     *  Scriptable.put()
+     *  @param ignoretraps if set, no traps will be triggered (set when 'cascade' reaches the bottom of the trap stack)
+     *  @param rp if this put is being performed via a root proxy, rp is the root proxy.
+     */
+    public void put(String name, Scriptable start, Object value, boolean ignoretraps, RootProxy rp) {
+        if (name == null) return;
+        if (name.startsWith("xwt_")) {
+            if (Log.on) Log.log(this, "attempt to set reserved property " + name + " at " +
+                                Context.enter().interpreterSourceFile + ":" + Context.enter().interpreterLine);
+            return;
+        }
+
+        if (!ignoretraps && traps != null) {
+            Trap t = (Trap)traps.get(name);
+            if (t != null) {
+                Object[] arg = (Object[])singleObjects.remove(false);
+                if (arg == null) arg = new Object[] { value };
+                else arg[0] = value;
+                t.perform(arg);
+                arg[0] = null;
+                singleObjects.append(arg);
+                return;
+            }
+        }
+
+        // don't want to really cascade down to the box on this one
+        if (name.equals("0")) return;
+
+        SpecialBoxProperty gph = (SpecialBoxProperty)SpecialBoxProperty.specialBoxProperties.get(name);
+        if (gph != null) {
+            gph.put(name, this, value);
+            return;
+        }
+
+        if (name.charAt(0) == '_') {
+            if (value != null && !(value instanceof Function)) {
+                if (Log.on) Log.log(this, "attempt to put a non-function value to " + name + " at " + 
+                                    Context.enter().interpreterSourceFile + ":" + Context.enter().interpreterLine);
+            } else if (name.charAt(1) == '_') {
+                name = name.substring(2).intern();
+                Trap t = Trap.getTrap(this, name);
+                if (t != null) t.delete();
+                if (value != null) Trap.addTrap(this, name, ((Function)value), true, rp);
+            } else {
+                name = name.substring(1).intern();
+                Trap t = Trap.getTrap(this, name);
+                if (t != null) t.delete();
+                if (value != null) Trap.addTrap(this, name, ((Function)value), false, rp);
+            }
+            return;
+        }
+
+        if (ignoretraps) {
+            // traps always cascade to the global property, not the local one
+            putGlobally(name, start, value);
+        } else {
+            super.put(name, start, value);
+        }
+
+        // a bit of a hack, since titlebar is the only 'special' property stored in JSObject
+        if (getParent() == null && surface != null) {
+            if (name.equals("titlebar")) surface.setTitleBarText(value.toString());
+            if (name.equals("icon")) {
+                Picture pic = Box.getPicture(value.toString());
+                if (pic != null) surface.setIcon(pic);
+                else if (Log.on) Log.log(this, "unable to load icon " + value);
+            }
+        }
+    }
+
+    /** the <tt>delete</tt> keyword is not valid in XWT scripts */
+    public void delete(int i) { }
+
+
+    // Tree Manipulation /////////////////////////////////////////////////////////////////////
+
+    /** The parent of this node */
+    private Box parent = null;
+    
+    // Variables used in Vector mode */
+    /** INVARIANT: if (parent != null) parent.children.elementAt(indexInParent) == this */
+    private int indexInParent;
+    private Vec children = null;
+
+    // Variables used in linked-list mode
+    private int numKids = 0;
+    private Box nextSibling = null;
+    private Box prevSibling = null;
+    private Box firstKid = null;
+    
+    // when we get more than 15 children, we switch to array-mode
+    private void convert_to_array() {
+        children = new Vec(numKids);
+        Box cur = firstKid;
+        do {
+            children.addElement(cur);
+            cur.indexInParent = children.size() - 1;
+            cur = cur.nextSibling;
+        } while (cur != firstKid);
+    }
+    
+    /** remove this node from its parent; INVARIANT: whenever the parent of a node is changed, remove() gets called. */
+    public void remove() {
+        if (parent == null) {
+            if (surface != null) surface.dispose();
+            return;
+        }
+        Box oldparent = getParent();
+        if (oldparent == null) return;
+        mark_for_prerender();
+        dirty();
+        mouseinside = false;
+
+        if (parent.children != null) {
+            parent.children.removeElementAt(indexInParent);
+            for(int j=indexInParent; j<parent.children.size(); j++)
+                (parent.getChild(j)).indexInParent = j;
+
+        } else {
+            if (parent.firstKid == this) {
+                if (nextSibling == this) parent.firstKid = null;
+                else parent.firstKid = nextSibling;
+            }
+            parent.numKids--;
+            prevSibling.nextSibling = nextSibling;
+            nextSibling.prevSibling = prevSibling;
+            prevSibling = null;
+            nextSibling = null;
+        }
+        parent = null;
+
+        if (oldparent != null) oldparent.sync_cmin_to_children();
+        setSurface(null);
+
+        // note that JavaScript box[0] will invoke put(int i), not put(String s)
+        if (oldparent != null) oldparent.put("0", null, this);
+    }
+
+    /** returns our next sibling (parent[ourindex + 1]) */
+    public final Box nextSibling() {
+        if (parent == null) return null;
+        if (parent.children == null) {
+            if (nextSibling == parent.firstKid) return null;
+            return nextSibling;
+        } else {
+            if (indexInParent >= parent.children.size() - 1) return null;
+            return (Box)parent.children.elementAt(indexInParent + 1);
+        }
+    }
+    
+    /** returns our next sibling (parent[ourindex + 1]) */
+    public final Box prevSibling() {
+        if (parent == null) return null;
+        if (parent.children == null) {
+            if (this == parent.firstKid) return null;
+            return prevSibling;
+        } else {
+            if (indexInParent == 0) return null;
+            return (Box)parent.children.elementAt(indexInParent - 1);
+        }
+    }
+    
+    /** Returns the parent of this node */
+    public Box getParent() { return parent; }
+    
+    /** Returns ith child */
+    public Box getChild(int i) {
+        if (children == null) {
+            if (firstKid == null) return null;
+            if (i >= numKids) return null;
+            if (i == numKids - 1) return firstKid.prevSibling;
+            Box cur = firstKid;
+            for(int j=0; j<i; j++) cur = cur.nextSibling;
+            return cur;
+        } else {
+            if (i >= children.size() || i < 0) return null;
+            return (Box)children.elementAt(i);
+        }
+    }
+    
+    /** Returns the number of children */
+    public int numChildren() {
+        if (children == null) {
+            if (firstKid == null) return 0;
+            int i=1;
+            for(Box cur = firstKid.nextSibling; cur != firstKid; i++) cur = cur.nextSibling;
+            return i;
+        } else {
+            return children.size();
+        }
+    }
+    
+    /** Returns our index in our parent */
+    public int getIndexInParent() {
+        if (parent == null) return 0;
+        if (parent.children == null) {
+            int i = 0;
+            for(Box cur = this; cur != parent.firstKid; i++) cur = cur.prevSibling;
+            return i;
+        } else {
+            return indexInParent;
+        }
+    }
+
+    /** returns the root of the surface that this box belongs to */
+    public final Box getRoot() {
+        if (getParent() == null && surface != null) return this;
+        if (getParent() == null) return null;
+        return getParent().getRoot();
+    }
+
+
+    // Root Proxy ///////////////////////////////////////////////////////////////////////////////
+
+    RootProxy myproxy = null;
+    public Scriptable getRootProxy() {
+        if (myproxy == null) myproxy = new RootProxy(this);
+        return myproxy;
+    }
+
+    private static class RootProxy implements Scriptable {
+
+        Box box;
+        RootProxy(Box b) { this.box = b; }
+
+        public void delete(String name) { box.delete(name); }
+        public Scriptable getParentScope() { return box.getParentScope(); }
+        public void setParentScope(Scriptable p) { box.setParentScope(p); }
+        public boolean hasInstance(Scriptable value) { return box.hasInstance(value); }
+        public Scriptable getPrototype() { return box.getPrototype(); }
+        public void setPrototype(Scriptable p) { box.setPrototype(p); }
+        public void delete(int i) { box.delete(i); }
+        public String getClassName() { return box.getClassName(); }
+        public Object getDefaultValue(Class hint) { return box.getDefaultValue(hint); }
+
+        public void put(int i, Scriptable start, Object value) { if (value != null) box.put(i, start, value); }
+        public Object get(String name, Scriptable start) { return box.get(name, start); }
+        public Object get(int i, Scriptable start) { return null; }
+
+        public void put(String name, Scriptable start, Object value) { box.put(name, start, value, false, this); }
+        public boolean has(String name, Scriptable start) { return box.has(name, start); }
+        public boolean has(int i, Scriptable start) { return box.has(i, start); }
+        public Object[] getIds() { return box.getIds(); }
+
+    }
+
+
+    // Trivial Helper Methods (should be inlined) /////////////////////////////////////////
+
+    /** helper, included in this class so it can be inlined */
+    static final int min(int a, int b) {
+        if (a<b) return a;
+        else return b;
+    }
+    
+    /** helper, included in this class so it can be inlined */
+    static final double min(double a, double b) {
+        if (a<b) return a;
+        else return b;
+    }
+    
+    /** helper, included in this class so it can be inlined */
+    static final int max(int a, int b) {
+        if (a>b) return a;
+        else return b;
+    }
+    
+    /** helper, included in this class so it can be inlined */
+    static final int min(int a, int b, int c) {
+        if (a<=b && a<=c) return a;
+        else if (b<=c && b<=a) return b;
+        else return c;
+    }
+    
+    /** helper, included in this class so it can be inlined */
+    static final int max(int a, int b, int c) {
+        if (a>=b && a>=c) return a;
+        else if (b>=c && b>=a) return b;
+        else return c;
+    }
+    
+    /** helper, included in this class so it can be inlined */
+    static final int bound(int a, int b, int c) {
+        if (c < b) return c;
+        if (a > b) return a;
+        return b;
+    }
+
+    /** returns numerator/denominator, but rounds <i>up</i> instead of down */
+    static final int divide_round_up(int numerator, int denominator) {
+        int ret = numerator / denominator;
+        if (ret * denominator < numerator) return ret + 1;
+        return ret;
+    }
+
+    /** Simple helper function to determine if the point x,y falls within this node's actual current geometry */
+    boolean inside(int x, int y) {
+        if (invisible) return false;
+        return (x >= pos(0) && y >= pos(1) && x < pos(0) + size(0) && y < pos(1) + size(1));
+    }
+    
+    /** figures out what box in this subtree of the Box owns the pixel at x,y relitave to the Surface
+     *
+     *  IMPORTANT: this method gets called from the event-queueing thread, since we need to determine which box is
+     *             underneath the mouse as early as possible. Because of this, we have to do some extra checks, as the
+     *             Box tree may be in flux.
+     */
+    Box whoIs(int x, int y) {
+        if (invisible) return null;
+        if (!inside(x,y)) return getParent() == null ? this : null;
+
+        // We do this because whoIs is unsynchronized (for
+        // speed), yet is sometimes called while the structure of the
+        // Box is in flux.
+        for(int i=numChildren() - 1; i>=0; i--) {
+
+            Box child = getChild(i);
+            if (child == null) continue;
+
+            Box bt = child.whoIs(x,y);
+            if (bt != null) return bt;
+        }
+        return this;
+    }
+    
+}
+
+
+
diff --git a/src/org/xwt/DoubleBuffer.java b/src/org/xwt/DoubleBuffer.java
new file mode 100644 (file)
index 0000000..eebd830
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+/**
+ *  <p>
+ *  A block of pixels which can be drawn on and rapidly copied to the
+ *  screen. Drawing operations are performed on this class; it is
+ *  then rendered to the screen with Surface.blit().
+ *  </p>
+ *
+ *  <p>
+ *  Implementations of the Platform class should return objects
+ *  supporting this interface from the _createDoubleBuffer()
+ *  method. These implementations may choose to use off-screen video
+ *  ram for this purpose (for example, a Pixmap on X11).
+ *  </p>
+ *
+ *  <p>
+ *  A note on coordinates: all members on DoubleBuffer specify
+ *  coordinates in terms of x1,y1,x2,y2 even though the Box class
+ *  represents regions internally as x,y,w,h.
+ *  </p>
+ */
+public interface DoubleBuffer {
+
+    /** Draw the region of source within (sx1, sy1, sx2, sy2) onto the region of this DoubleBuffer within (dx1, dy1, dx2, dy2), scaling as needed. */
+    public void drawPicture(Picture source, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2);
+
+    /** Draw source onto this DoubleBuffer at (x,y) */
+    public void drawPicture(Picture source, int x, int y);
+
+    /** Draw <tt>text</tt> in <tt>font</tt> and <tt>color</tt> on this DoubleBuffer, with the upper left corner of the text at (x, y) */
+    public void drawString(String font, String text, int x, int y, int color);
+
+    /** Fill the region (x1, y1, x2, y2) with <tt>color</tt> (AARRGGBB format); the alpha channel component is ignored */
+    public void fillRect(int x1, int y1, int x2, int y2, int color);
+
+    /** Sets the clip region for this DoubleBuffer to (x,y,x2,y2) */
+    public void setClip(int x, int y, int x2, int y2);
+
+    public int getHeight();
+    public int getWidth();
+}
diff --git a/src/org/xwt/GIF.java b/src/org/xwt/GIF.java
new file mode 100644 (file)
index 0000000..75ae93a
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * This file was adapted from D J Hagberg's GifDecoder.java
+ *
+ * This software is copyrighted by D. J. Hagberg, Jr., and other parties.
+ * The following terms apply to all files associated with the software
+ * unless explicitly disclaimed in individual files.
+ * 
+ * The authors hereby grant permission to use, copy, modify, distribute,
+ * and license this software and its documentation for any purpose, provided
+ * that existing copyright notices are retained in all copies and that this
+ * notice is included verbatim in any distributions. No written agreement,
+ * license, or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their authors
+ * and need not follow the licensing terms described here, provided that
+ * the new terms are clearly indicated on the first page of each file where
+ * they apply.
+ * 
+ * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+ * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+ * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
+ * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+ * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+ * MODIFICATIONS.
+ * 
+ * GOVERNMENT USE: If you are acquiring this software on behalf of the
+ * U.S. government, the Government shall have only "Restricted Rights"
+ * in the software and related documentation as defined in the Federal 
+ * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
+ * are acquiring the software on behalf of the Department of Defense, the
+ * software shall be classified as "Commercial Computer Software" and the
+ * Government shall have only "Restricted Rights" as defined in Clause
+ * 252.227-7013 (c) (1) of DFARs.  Notwithstanding the foregoing, the
+ * authors grant the U.S. Government and others acting in its behalf
+ * permission to use and distribute the software in accordance with the
+ * terms specified in this license.
+ *
+ */
+package org.xwt;
+
+import org.xwt.util.*;
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/** Converts an InputStream carrying a GIF image into an ARGB int[] */
+public class GIF implements ImageDecoder {
+
+    // Public Methods /////////////////////////////////////////////////////////
+
+    public int[] getData() { return data; }
+    public int getWidth() { return width; }
+    public int getHeight() { return height; }
+
+    /** Processes an image from InputStream is; returns null if there is an error
+        @param name A string describing the image; used for error reporting.
+     */
+    public static GIF decode(InputStream is, String name) {
+        try {
+            return new GIF(is, name);
+        } catch (Exception e) {
+            if (Log.on) Log.log(GIF.class, e);
+            return null;
+        }
+    }
+
+    private GIF(InputStream is, String name) throws IOException {
+        if (is instanceof BufferedInputStream) _in = (BufferedInputStream)is;
+        else _in = new BufferedInputStream(is);
+        decodeAsBufferedImage(0);
+    }
+
+    // Private Methods /////////////////////////////////////////////////////////
+
+    private int[] data = null;
+
+    /** Decode a particular frame from the GIF file. Frames must be decoded in order, starting with 0. */
+    private void decodeAsBufferedImage(int page) throws IOException, IOException {
+
+        // If the user requested a page already decoded, we cannot go back.
+        if (page <= index ) return;
+
+        // If we just started reading this stream, initialize the global info.
+        if (index < 0 ) {
+            if (!readIntoBuf(6) || _buf[0] != 'G' || _buf[1] != 'I' || _buf[2] != 'F' ||
+                _buf[3] != '8' || !(_buf[4] == '7' || _buf[4] == '9') || _buf[5] != 'a')
+                throw new IOException("Not a GIF8Xa file.");
+            if (!readGlobalImageDescriptor()) throw new IOException("Unable to read GIF header.");
+        }
+        
+        // Loop through the blocks in the image.
+        int block_identifier;
+        while (true) {
+            if(!readIntoBuf(1)) throw new IOException("Unexpected EOF(1)");
+            block_identifier = _buf[0];
+            if (block_identifier == ';') throw new IOException("No image data");
+            if (block_identifier == '!') {
+                if (!readExtensionBlock()) throw new IOException("Unexpected EOF(2)");
+                continue;
+            }
+
+            // not a valid start character -- ignore it.
+            if (block_identifier != ',') continue;
+
+            if (!readLocalImageDescriptor()) throw new IOException("Unexpected EOF(3)");
+            data = new int[width * height];
+            readImage();
+
+            // If we did not decode the requested index, need to go on
+            // to the next one (future implementations should consider
+            // caching already-decoded images here to allow for random
+            // access to animated GIF pages).
+            index++;
+            if (index < page) continue;
+            
+            // If we did decode the requested index, we can return.
+            break;
+        }
+        
+        // Return the image thus-far decoded
+        return;
+    }
+
+    /** Actually read the image data */
+    private void readImage() 
+        throws IOException, IOException {
+        int len = width;
+        int rows = height;
+        int initialCodeSize;
+        int v;
+        int xpos = 0, ypos = 0, pass = 0, i;
+        int prefix[] = new int[(1 << MAX_LWZ_BITS)];
+        int append[] = new int[(1 << MAX_LWZ_BITS)];
+        int stack[] = new int[(1 << MAX_LWZ_BITS)*2];
+        int top_idx;
+        int codeSize, clearCode, inCode, endCode, oldCode, maxCode, code, firstCode;
+        
+        // Initialize the decoder
+        if (!readIntoBuf(1)) throw new IOException("Unexpected EOF decoding image");
+        initialCodeSize = _buf[0];
+
+        // Look at the right color map, setting up transparency if called for.
+        int[] cmap = global_color_map;
+        if (hascmap) cmap = color_map;
+        if (trans_idx >= 0) cmap[trans_idx] = 0x00000000;
+
+        /* Initialize the decoder */
+        /* Set values for "special" numbers:
+         * clear code  reset the decoder
+         * end code            stop decoding
+         * code size   size of the next code to retrieve
+         * max code            next available table position
+         */
+        clearCode   = 1 << initialCodeSize;
+        endCode     = clearCode + 1;
+        codeSize    = initialCodeSize + 1;
+        maxCode     = clearCode + 2;
+        oldCode     = -1;
+        firstCode   = -1;
+        
+        for (i = 0; i < clearCode; i++) append[i] = i;
+        top_idx = 0; // top of stack.
+        
+        bitsInWindow = 0;
+        bytes = 0;
+        window = 0L;
+        done = false;
+        c = -1;
+        
+        /* Read until we finish the image */
+        ypos = 0;
+        for (i = 0; i < rows; i++) {
+            for (xpos = 0; xpos < len;) {
+                
+                if (top_idx == 0) {
+                    /* Bummer -- our stack is empty.  Now we have to work! */
+                    code = getCode(codeSize);
+                    if (code < 0) return;
+                    
+                    if (code > maxCode || code == endCode) return;
+                    if (code == clearCode) {
+                        codeSize    = initialCodeSize + 1;
+                        maxCode     = clearCode + 2;
+                        oldCode     = -1;
+                        continue;
+                    }
+                    
+                    // Last pass reset the decoder, so the first code we
+                    // see must be a singleton.  Seed the stack with it,
+                    // and set up the old/first code pointers for
+                    // insertion into the string table.  We can't just
+                    // roll this into the clearCode test above, because
+                    // at that point we have not yet read the next code.
+                    if (oldCode == -1) {
+                        stack[top_idx++] = append[code];
+                        oldCode = code;
+                        firstCode = code;
+                        continue;
+                    }
+                    
+                    inCode = code;
+
+                    // maxCode is always one bigger than our
+                    // highest assigned code.  If the code we see
+                    // is equal to maxCode, then we are about to
+                    // add a new string to the table. ???  
+                    if (code == maxCode) {
+                        stack[top_idx++] = firstCode;
+                        code = oldCode;
+                    }
+                    
+                    // Populate the stack by tracing the string in the
+                    // string table from its tail to its head
+                    while (code > clearCode) {
+                        stack[top_idx++] = append[code];
+                        code = prefix[code];
+                    }
+                    firstCode = append[code];
+                    
+                    // If there's no more room in our string table, quit.
+                    // Otherwise, add a new string to the table
+                    if (maxCode >= (1 << MAX_LWZ_BITS)) return;
+                    
+                    // Push the head of the string onto the stack
+                    stack[top_idx++] = firstCode;
+                    
+                    // Add a new string to the string table
+                    prefix[maxCode] = oldCode;
+                    append[maxCode] = firstCode;
+                    maxCode++;
+                    
+                    // maxCode tells us the maximum code value we can accept.
+                    // If we see that we need more bits to represent it than
+                    // we are requesting from the unpacker, we need to increase
+                    // the number we ask for.
+                    if ((maxCode >= (1 << codeSize)) && (maxCode < (1<<MAX_LWZ_BITS))) codeSize++;
+                    oldCode = inCode;
+                }
+                
+                // Pop the next color index off the stack
+                v = stack[--top_idx];
+                if (v < 0) return;
+                
+                // Finally, we can set a pixel!  Joy!
+                data[xpos + ypos * width] = cmap[v];
+                xpos++;
+            }
+            
+            // If interlacing, the next ypos is not just +1
+            if (interlaced) {
+                ypos += _interlaceStep[pass];
+                while (ypos >= rows) {
+                    pass++;
+                    if (pass > 3) return;
+                    ypos = _interlaceStart[pass];
+                }
+            } else ypos++;
+        }
+        return;
+    }
+
+    /** Extract the next compression code from the file. */
+    private int getCode(int code_size) throws IOException {
+        int ret;
+        
+        while (bitsInWindow < code_size) {
+            // Not enough bits in our window to cover the request
+            if (done) return -1;
+            
+            if (bytes == 0) {
+                // Not enough bytes in our buffer to add to the window
+                bytes = getDataBlock();
+                c = 0;
+                if (bytes <= 0) {
+                    done = true;
+                    break;
+                }
+            }
+            // Tack another byte onto the window, see if that's enough
+            window += (_buf[c]) << bitsInWindow;
+            ++c;
+            bitsInWindow += 8;
+            bytes--;
+        }
+        
+        
+        // The next code will always be the last code_size bits of the window
+        ret = ((int)window) & ((1 << code_size) - 1);
+        
+        // Shift data in the window to put the next code at the end
+        window >>= code_size;
+        bitsInWindow -= code_size;
+        return ret;
+    }
+    
+    /** Read the global image descriptor and optional global color map. Sets global_* variables. */
+    private boolean readGlobalImageDescriptor() throws IOException {
+        int packed;
+        int aspect; // we ignore this.
+        int ofs;
+        
+        if (!readIntoBuf(7) ) return false;
+        global_width     = _buf[0] | (_buf[1] << 8);
+        global_height    = _buf[2] | (_buf[3] << 8);
+        packed       = _buf[4];
+        global_bgcolor   = _buf[5];
+        aspect       = _buf[6];
+        global_cmapsize  = 2 << (packed & 0x07);
+        global_hascmap   = (packed & GLOBALCOLORMAP) == GLOBALCOLORMAP;
+        global_color_map = null;
+        
+        // Read the color map, if we have one.
+        if (global_hascmap) {
+            if (!readColorMap(global_cmapsize,true)) {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+    
+    /** Read a local image descriptor and optional local color map. */
+    private boolean readLocalImageDescriptor() throws IOException {
+        int packed;
+        
+        if (!readIntoBuf(9) ) return false;
+        
+        left       = _buf[0] | (_buf[1] << 8);
+        top        = _buf[2] | (_buf[3] << 8);
+        width      = _buf[4] | (_buf[5] << 8);
+        height     = _buf[6] | (_buf[7] << 8);
+        packed        = _buf[8];
+        hascmap    = (packed & LOCALCOLORMAP) == LOCALCOLORMAP;
+        cmapsize   = 2 << (packed & 0x07);
+        interlaced = (packed & INTERLACE) == INTERLACE;
+        color_map  = null;
+        
+        // Read the local color table, if there is one.
+        return !(hascmap && !readColorMap(cmapsize,false));
+    }
+
+    /** Read a color map (global or local). */
+    private boolean readColorMap(int nColors, boolean isGlobal)
+        throws IOException {
+        int[] map = new int[nColors];
+        for( int i=0; i < nColors; ++i) {
+            if (!readIntoBuf(3) ) return false;
+            map[i] = (_buf[0] << 16) | (_buf[1] << 8) | _buf[2] | 0xFF000000;
+        }
+        if (isGlobal) global_color_map = map;
+        else color_map = map;
+        return true;
+    }
+
+    /** Read the contents of a GIF89a Graphical Extension Block. */
+    private boolean readExtensionBlock() throws IOException {
+        if (!readIntoBuf(1) ) return false;
+        int label = _buf[0];
+        int count = -1;
+        switch (label) {
+           case 0x01:      // Plain Text Extension
+           case 0xff:      // Application Extension
+           case 0xfe:      // Comment Extension
+            break;
+           case 0xf9:      // Graphic Control Extension
+               count = getDataBlock();
+               if (count < 0) return true;
+            // Check for transparency setting.
+               if ((_buf[0] & HASTRANSPARENCY) != 0) trans_idx = _buf[3];
+            else trans_idx = -1;
+        }
+        do { count = getDataBlock(); } while (count > 0);
+        return true;
+    }
+
+    /** Read a block of data from the GIF file. */
+    private int getDataBlock() throws IOException {
+        if (!readIntoBuf(1) ) return -1;
+        int count = _buf[0];
+        if (count != 0) if (!readIntoBuf(count) ) return -1;
+        return count;
+    }
+    
+    /** Read the indicated number of bytes into _buf, our instance-wide buffer. */
+    private boolean readIntoBuf(int count) throws IOException {
+        for(int i = 0; i < count; i++) if ((_buf[i] = _in.read()) == -1) return false;
+        return true;
+    }
+
+    // Private Data //////////////////////////////////////////////////////////
+
+    // State management stuff
+    private int index = -1;
+    private BufferedInputStream _in = null;
+    private int[] _buf = new int[BUFSIZE];
+
+    // Transparency settings
+    private int trans_idx = -1;
+
+    // Global image descriptor contents
+    private int global_width = 0;
+    private int global_height = 0;
+    private int global_bgcolor = 0;
+    private int global_cmapsize = 0;
+    private boolean global_hascmap = false;
+    private int[] global_color_map = null;
+
+    // Local image descriptor contents
+    private int left = 0;
+    private int top = 0;
+    private int width = 0;
+    private int height = 0;
+    private int cmapsize = 0;
+    private boolean hascmap = false;
+    private boolean interlaced = false;
+    private int[] color_map = null;
+
+    // Variables used in getCode(...) to track sliding bit-window.
+    private int     bytes = 0;
+    private boolean done;
+    private int     c;
+    private long    window;
+    private int     bitsInWindow = 0;
+
+    // Class-wide constants.
+    private static final int INTERLACE                 = 0x40;
+    private static final int GLOBALCOLORMAP    = 0x80;
+    private static final int LOCALCOLORMAP     = 0x80;
+    private static final int HASTRANSPARENCY   = 0x01;
+    private static final int MAX_LWZ_BITS      = 12;
+    private static final int BUFSIZE           = 280;
+    private static final int[] _interlaceStep  = { 8, 8, 4, 2 };
+    private static final int[] _interlaceStart = { 0, 4, 2, 1 };
+}
+
+
+
diff --git a/src/org/xwt/ImageDecoder.java b/src/org/xwt/ImageDecoder.java
new file mode 100644 (file)
index 0000000..05d292c
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+import java.io.*;
+
+/** Interface implemented by classes capable of decoding an image file into an int[] */
+public interface ImageDecoder {
+    
+    /** returns the width of the image */
+    public abstract int getWidth();
+
+    /** returns the height of the image */
+    public abstract int getHeight();
+
+    /** returns the data of the image, as an array of 32-bit AARRGGBB samples */
+    public abstract int[] getData();
+
+}
diff --git a/src/org/xwt/Main.java b/src/org/xwt/Main.java
new file mode 100644 (file)
index 0000000..d5e482f
--- /dev/null
@@ -0,0 +1,1935 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import org.xwt.util.*;
+import java.lang.reflect.*;
+import java.applet.*;
+import java.net.*;
+import java.util.*;
+import java.io.*;
+import java.awt.*;
+import org.bouncycastle.util.encoders.Base64;
+
+/** Entry point for the XWT Engine; handles splash screen, initial xwar loading, and argument processing */
+public class Main extends Applet {
+
+    /** true after the initial template has been fully instantiated */
+    public static boolean initializationComplete = false;
+
+    /** scripts are allowed to make XMLRPC/SOAP calls to their origin address, even if it is firewalled */
+    public static java.net.InetAddress originAddr = null;
+
+    /** see Template.numUnits() for a description */
+    public static int instantiatedUnits = 0;
+
+    /** the initial Template */
+    static Template initialTemplate;
+
+    /** true once the initial xwar has been fully downloaded */
+    static boolean doneDownloading = false;
+
+    /** true iff the user asked for rendered regions to be shown with a red rectangle */
+    public static boolean showRenders = false;
+
+    public Main() { }
+    public void paint(Graphics g) { }    // do-nothing method to avoid repaints
+    public void update(Graphics g) { }   // do-nothing method, to avoid repaints
+
+    /** applet entry point */
+    public void init() {
+        if ("true".equals(getParameter("showRenders"))) showRenders = true;
+        if ("true".equals(getParameter("verbose"))) Log.verbose = true;
+        main(new String[] { getParameter("xwar-url"), "main" });
+    }
+
+    /** common entry point */
+    public static void main(String[] args) {
+        try {
+            int startargs = 0;
+            while (true) {
+                if (startargs > args.length - 1) {
+                    System.out.println("Usage: xwt [-sv] source-location [initial-template]");
+                    System.out.println("");
+                    System.out.println("Options:");
+                    System.out.println("    -s show rendering activity with red rectangles");
+                    System.out.println("    -v verbose logging");
+                    System.out.println("");
+                    System.out.println("Source-location is one of the following:");
+                    System.out.println("    - the path to an xwar file");
+                    System.out.println("    - the path to a directory to be used as an xwar");
+                    System.out.println("    - the http url of an xwar");
+                    System.out.println("");
+                    System.out.println("Initial-template is the resource name of the template to load; defaults to 'main'");
+                    Runtime.getRuntime().exit(-1);
+                }
+                else if (args[startargs].equals("-s")) showRenders = true;
+                else if (args[startargs].equals("-v")) Log.verbose = true;
+                else break;
+                startargs++;
+            }
+            final String instancename = args.length > startargs + 1 ? args[startargs + 1] : "main";
+
+            Platform.forceLoad();
+            if (Log.on) for(int i=0; i<args.length; i++) Log.log(Main.class, "argument " + i + ": " + args[i]);
+
+            // we do this here instead of in a static initializer to avoid threading hazards
+            if (Log.on) Log.log(Main.class, "loading scar image");
+            PNG png = PNG.decode(new ByteArrayInputStream(Base64.decode(scarPicture_png_base64)), "bundled scar image");
+            Surface.scarPicture = Platform.createPicture(png.getData(), png.getWidth(), png.getHeight());
+
+            SplashScreen.create();
+
+            if (args.length > startargs) {
+                if (args[startargs].startsWith("http://")) {
+                    if (Log.on) Log.log(Main.class, "downloading xwar");
+                    URL u = new URL(args[startargs]);
+                    originAddr = InetAddress.getByName(u.getHost());
+                    Resources.loadArchive(Platform.urlToInputStream(u));
+                    
+                } else {
+
+                    if (Log.on) Log.log(Main.class, "loading xwar from local filesystem");
+                    // HACK because MSIE turns \'s into /'s in URLs... argh!!
+                    if (Platform.platform.getClass().getName().endsWith("Win32"))
+                        args[startargs] = args[startargs].replace('/', '\\');
+
+                    File f = new File(args[startargs]);
+                    if (f.isDirectory()) Resources.loadDirectory(f);
+                    else Resources.loadArchive(new FileInputStream(f));
+                }
+            }
+
+            if (Log.on) Log.log(Main.class, "done loading initial xwar");
+            doneDownloading = true;
+            initialTemplate = Template.getTemplate(instancename, null);
+
+            MessageQueue.add(new Message() {
+                    public void perform() {
+                        if (Log.on) Log.log(Main.class, "instantiating intial template " + instancename);
+                        new Box(instancename, null);
+                        if (Log.on) Log.log(Main.class, "done instantiating");
+                        Surface.Refresh();
+                        initializationComplete = true;
+                        SplashScreen.dispose();
+                    }
+                });
+            
+            // gcj-win32 exit()'s when the original thread dies, so we have to deadlock ourselves
+            if (Log.on) Log.log(Main.class, "main thread blocking on new semaphore");
+            new org.xwt.util.Semaphore().block();
+
+        } catch (Throwable e) {
+            Platform.criticalAbort("exception in Main.main(): " + e);
+        }
+    }
+
+    /** this is how Template.apply(), Resource.loadArchive(), and Resource.loadDirectory() signal the splash screen to update its progress bar */
+    public static void updateSplashScreen() { SplashScreen.update(); }
+
+    /** encapsulates the splash screen */
+    private static class SplashScreen {
+        static Surface surf;
+        
+        // the position of the progress bar on the splash screen
+        static int barX = 10;
+        static int barY = 0;
+        static int barW = 0;
+        static int barH = 10;
+
+        static void create() {
+            if (Log.on) Log.log(Main.class, "loading splash screen...");
+            GIF gif = 
+                GIF.decode(new SequenceInputStream(new SequenceInputStream(new ByteArrayInputStream(Base64.decode(splashScreen_gif_base64_1)),
+                                                                           new ByteArrayInputStream(Base64.decode(splashScreen_gif_base64_2))),
+                                                   new SequenceInputStream(new ByteArrayInputStream(Base64.decode(splashScreen_gif_base64_3)),
+                                                                           new ByteArrayInputStream(Base64.decode(splashScreen_gif_base64_4)))),
+                           "bundled splash screen image");
+            
+            if (gif == null) Platform.criticalAbort("error loading splash screen image");
+            splashScreenPicture = Platform.createPicture(gif.getData(), gif.getWidth(), gif.getHeight());
+            barY = splashScreenPicture.getHeight() - 20;
+            barW = splashScreenPicture.getWidth() - 20;            
+            
+            Box box = new Box("box", null);
+            surf = Platform.createSurface(box, false, false);
+            box.set(Box.size, 0, splashScreenPicture.getWidth());
+            box.set(Box.size, 1, splashScreenPicture.getHeight());
+            surf.setLocation((Platform.getScreenWidth() - splashScreenPicture.getWidth()) / 2,
+                             (Platform.getScreenHeight() - splashScreenPicture.getHeight()) / 2);
+            
+            surf.backbuffer = Platform.createDoubleBuffer(splashScreenPicture.getWidth(), splashScreenPicture.getHeight(), surf);
+            surf.backbuffer.drawPicture(splashScreenPicture, 0, 0);
+            surf.blit(surf.backbuffer, 0, 0, 0, 0, splashScreenPicture.getWidth(), splashScreenPicture.getHeight());
+        }
+        
+        static void update() {
+            if (surf == null) return;
+            int barPos;
+            if (!doneDownloading) {
+                float z = (float)barW / (float)2.0;
+                float x = (float) (  ((float)Resources.bytesDownloaded) / 5000000.0   );
+                barPos = (int)   (  z - (1.0 / ( x + 1.0 / z ))  );
+            } else {
+                barPos = barW / 2 + (instantiatedUnits * barW) / (initialTemplate.numUnits() * 2);
+            }
+            if (barPos > barW) barPos = barW;
+            
+            surf.backbuffer.fillRect(barX, barY, barPos + barX, barH + barY, 0xFF363a86);
+            surf.backbuffer.fillRect(barX + 1, barY + 1, barPos - 1 + barX + 1, barH - 1 + barY + 1, 0xFF000036);
+            surf.backbuffer.fillRect(barX + 1, barY + 1, barPos - 2 + barX + 1, barH - 2 + barY + 1, 0xFF101350);
+            surf.blit(surf.backbuffer, barX, barY, barX, barY, barPos + barX, barH + barY);
+        }
+
+        static void dispose() {
+            surf.dispose();
+            surf = null;
+        }
+
+        private static Picture splashScreenPicture;
+
+        private static String splashScreen_gif_base64_1 =
+            "R0lGODlhigEUAecAAAICBhYaFjI2MkpOSh4iHgICJkZKRj5GPiYqJjpCOgoGSgoKChYaVjI6" +
+            "MiYuJgYCLhIaUhISKjYyLj46Mg4SEgYKMioyKhISZlZaVioiIg4SRiIiUxYWJgICEgoONjY2" +
+            "LiImIlJWUjIuKhoaMjo+OhESVg4SJiIeHj4+Ni4qJhoeGg4WSj5CPkJCOgICFg4OIhISLjY2" +
+            "Ni4uMioqRjY6Ni4yMjo6MkpGRk5SThIWEgYGFgoKHgoOOiIiGi4uJjo6Ni4yLkpKQjIyKjo2" +
+            "Ng4OJkI+NioqJiYmKiIiMgoOPkJGQjo6OkZGOz4+OgYKKgoSQioqKiYmIkZCQg4OQj4+PhYa" +
+            "bgoODgYGNhYWTi4uKjo2Mi4uLkJCPgYKNjIyMhIWUiYiIi4uVh4eIgICHjY2SxISSkZGQkZG" +
+            "RgoKNkJCQk5OSiIiHiIiIjIySg4KVk5KSiYmJhoaKhISNh4eGgYGJhIWSjYyMhoaFg4SMg4S" +
+            "SiouTgoOQgIGHkpKRlJSTjY6OgoKGhYeWioqLhISIkpKSjo6TiUmPAYGBjIyQhYWEiYqOjIu" +
+            "LiIiJjY2MjIyLhYaRjI2NjY2OhIOXiYuLj46NhIWaT46Ok5OTj4+ViYqKlZWUh4eHhoaZjYy" +
+            "Ujo+PhoaGh4eLgIGJgIGIi4uQi4qKj5CQgYKOiImJg4SQgICGgYGQjIyNgoOHiIiRhASWkZG" +
+            "SgICDgYGKjo6PgoOVjY6UkI+OkI+PhYWcgYGLh4eXAYGEgoKOh4iIh4iYhISEkZKSlJSUhYa" +
+            "UhYaJj4+QioqOkpOTg4ODhIWThIWWgoKUlpaWg4OXhYWLlpeXhYSagYKLhYaWhoeHh4aGh4e" +
+            "Pi4uPhoaJhYWFjY2RhISJhoeXw4OWk5SUioqUgYGPg4KUlZWVjY2WiIiLh4eah4aWh4eMg4S" +
+            "YQoKDkJCVg4SUhoWFhYaGiYmMhIWPBoaOlJWViYmahISYhYadhoeShoWdiomJioqXiYmViou" +
+            "KiouLh4eKjo6RhIWYRISajIyVi4mJhIWQiwAAAAAigEUAQAI/gA3CRTDi41AXgXZKFSYMOHC" +
+            "hKcUbnrocKFFNnBAwNnI8RSjUyBPwYEC5SOviSIlGowGB6NBNtE2neK1EeNEKCNxkmSzxQsC" +
+            "klC8eIGyBco9oV4ECI0RQ2gNAUyXAPECZMmPLUW9pFmyJYujJlSYUqEiFMgWsD9ipDkTtVFP" +
+            "R14bNXLkZckSRwKopGlKha2XGFSaNI2RtqmjGEua1l1CdYngw1QaCQ37l0phKlZ/YEa811Ea" +
+            "Llsa9QG95BJZGmfSBOai14xdKn2aePGEgxBUYMAQ+/mW+BKwJk0IqWHRmovqtccJmel7aasa" +
+            "YFy4ADPdRxOhNLjNnPFzRomaPmnU/ijvA/6MgTRKzKxVn6a9eyXp2bdnPXYsfdbt698PPJYF" +
+            "C7BLkNCEXQNaZRdhjtkV4BJiELTGCQKxcYJCjCCEEBsFgaGQR4xMyAYYFZ7EyCYnnNAQSBaB" +
+            "BAYIICGgIhxgxAhGQVAYoeFLp+C0yUmbfMIGFDORyIaPULgkkkyOIPDTSHDwssgWcGSCUyMx" +
+            "jITPFmn1tBQVi3hRg2VUbUEDU16IyUJTW8igBBeOTAUJC3/YQUMaLdhBpVM1CBKgDKu0F0Mk" +
+            "NCjxw1//zeUFXmUt5YggXtBQ5haExUClZXbJ8oMsf6T1w6Y0NHGZpls1KugPJHjyQyQkYEbF" +
+            "fz80IcUZ/mOacQkNWZRmhiONtNfID2cEYUka/6UxQBokCGsZdmHd4AcXMZwBDAs0/DIAcH5c" +
+            "Et0lqYWnngGX9PEKDn6IV5sShPRxwxmEnHGGGuqWawYhBpzxihnGnWeGGSyo15cSn6nHhSdU" +
+            "cPEfawJ7Eh1wAY+1BH8kkGAJZq1aYtWApBL2g4KONRygCp98wks0IDfICwFiQNjjJiFHIwYB" +
+            "BGwiBsotCzQHyAMJBOFJC53AiIQTaljQKUcE+QnIp8wYTcc+8gJGTJ+os8kdJ4gx9CcCWfPj" +
+            "hErvDEcNp4Cg0BH3nEIPAkwisIgRCAgCRSYC1NBTDGZtgQ8kiG1BChSNcCGA/t2OcEFCDTLY" +
+            "8ZcAWai1xCJmXbmFEVssYQdWUpTC5xJuLUJKV1jhA4UMjtxDj1db3BOUYXYo9qdSB9ZFQgxb" +
+            "DLEEDVP9ILgXktFlA1ZywSVp4J4sQYpajpBChQGNSUEDXpbs+gddgZllCyF/3NPEs47Y4o4Z" +
+            "AliCgxJ2+aGEJyScYRwLtlAhRWBrjWVGbE30oUQMLIxGAyGmceGHFGlcJwu5Aad2Rh96yQ8X" +
+            "SuEJ4liCPuVrAhds8TDMNEEWtaCCLCT2uj/8wRKywJgGY/CHxPyBKY5iigglJUKhKMULpWMK" +
+            "CSCksgp9BEYY4cXO2MAIMbABBIxYw4ZAQJOWzKRo/hi6oQxpohAWgUEkR2xJJuAQNCiQjR5w" +
+            "OIUN10aPG8GEDQiAIgFMxos5wOgEYNjRKdYgiEwk8QikMEIWtpAjG20BEiRR2yrc5oV7MGoV" +
+            "dAECBx1Xpjt5KQZcKJ0A/pAGWwjBETVYwhkEIynAeUEGgKEBJGhghh9kASoS8AoQspCFGtQA" +
+            "CvgonVBo8AeqROJwi0BNE4SwilI0AVedGsJfmkCDRgjATjFwxJgsQYOerA4IjshgT0rxhy2s" +
+            "IixbkAALGpGFMdFAADSggeuoYAnazYsu/wsLCwZgiT94Qg2sGUYfpPCLM5hBCn0wA3AIMYAf" +
+            "WEINN0AMDgwACVvgIJ4s/lhWDPrgh8QIBzhviA0TLqEEKrAzDX24RBDSgK1S3AB/5eqLOtPA" +
+            "hCaghwT58o9qVvWZ+rBgGCigj8OsIiBVEWc1Y5EYCWigsQpahQYXq9iYhhADUrrOLptgGckY" +
+            "EhEm0nBkbHhZUGf4ERruzCMoWohGGLIRkZwiE5kQCUei6EOc2AipOdrZCU4hkCIFCSYF2UQm" +
+            "TgIGMawBSFm4BxzwsREvbAStRjhFIzRXFEEMqihQIMUfIgFML63iD6tAjCeGUAOhnEEKksne" +
+            "GQa1sB/Q5ZJpGJNeEhODvW0BLllAIed+QAkvLIKQgwKkbHqilJ5EorJUoVtT9OioRdgheUBo" +
+            "/oRVEBkgASxiCZaQlGMaYYchRMYLqWqKLbhAuzQMIQu2SAOuUlNZKriSC0sgzg2okKl0DWEI" +
+            "l1CDXC7hB9kVAxiU+ICyZGeAASxhCFLAVxNYoIQbREc9CgTPr5SzBPqZ4XmE4MIA3qAdbLHg" +
+            "DQagAnu60x4WrEUJyY2cFKRgYPLNRzUoOClYLDGMBgqINckdC1joQ03cKmgIpCRQZlyHmN7+" +
+            "IZqEKVnUdjSRBnmtIA2aCCO8tiEKJQQMUWgJQxQi1CgwIgo1MgJHyAYHBDDiCBUSAyOgIAiP" +
+            "KAQMTYUxzuiBER2yQR1woLIR2ODjTRAFClmAAj2AhDfNbYQUgqDH/ibxBmbWvRErNIUUFxzr" +
+            "JRrUwA4yEAC9kFKKPlAOhaXYC2CUIMsyyUAGXhAEJCwBCUil4QcCgMTCGkG3tAjFEY5AXFcK" +
+            "i1kQexamZaqLJDn4B5pKM1K7WikNIvEfpswZuJGJQXBjYIslaCFVCrJMIyxxhsMFhwuLEM0b" +
+            "BGcGNfxBAOQCGBemyx8liO8PSviFLGLwLkn+0xIDaGcTBqAEQjaHBIS4DhW6tZZ0pQEHvzDA" +
+            "G9RgBgP0gQVSuI4ZzqWEN0iBC304z2HRQzD/6CVhq/KEJ4DTMI0NXIGrYkHDAGYJSwzcEw+j" +
+            "4MVISQMFifDEVBpCIyIRjWp07A7qoBrH/qLxsTnMTAUqeJnLDCJUl6lMIJ+ohjQEQvNPNIgA" +
+            "jJihhNggRRCAIBP0IIDKZNgSMdBs5xzrWDSscWRpnGAOH2O6FE/hRTB8InRPxTFbjQAEjByB" +
+            "F2mCaiaOIAN8ZAJKR5C0AIAAiUeW1hEWlEFPdvXIwJqKbuiJgQwWcaU1MwVwhFwFPmoQg0Wo" +
+            "TXRGwUkmBKFWBFyOJFuowSoOTabS9ZIpdkCkXKiCmC79YC64EhxpgTA3twBB44KwAxUgIYgY" +
+            "eOJJ/4ECJLqNgFU0oRFPIdXGn5mvJWRhCDewxBao4L1GAeMMq5DFJcxQl2Iwqwk3oGUahqHh" +
+            "/DwsDetFqALL/rmqRcoiNbZ4VSnM2R4lGPgMpVg2v6afhuUEjIFpkEVgSPADFnhCFgy0RQYv" +
+            "qKAJZpAEGgRi0URTE1dTMWAHpUMloqQUmSYUXSI4NLAJKkBzLhMhOrQGDUJDGtI1H3IKURAR" +
+            "RdMSF4KBBMAGOlQQXsMIRbaCmUBkpyAIWZBmQXQEQHEEGDIHOQJlQeUjJqhWYDQRILAJQGAE" +
+            "KpgJlwMHknF2OFEDpFAD+IAAQAAFUdAUl2MERpAWl1QmdsAY8CMLXtB2hwIVesQYjbAKPzBd" +
+            "jTAEAjAEnmADAtAIBkY5AiMCh+QFWZBGJNFJWPEXUdhMdbQFJ3YohCEE0GQLg0EC/rIkaYmR" +
+            "FCNkgI2wCJDwQbgiC49oB01gB/ggAcyyBTZwPI5Af6XzAxq3KXYgAU2AApDTa47wTWngBZbg" +
+            "BwPgerDIGgMQBPwiPn3wC8TRB0FACVQwHH9gC5dgC41QbLykbqaSb4DEHUtwAzhQC5aQLj+w" +
+            "Puw1ABF0A8tBCDcQPqMhL6XwLu13HeiRfmaAAlSQQKrRWJTwHwLSMGjRBJYAj68BgJZwSohI" +
+            "ApTgMENgQUtQamNCJRunGHQDCXIBEyhDgQixYiVIALwgEh/hkD7WNRzYJGvQAxEyESYIB1GQ" +
+            "Y0CHE0ewEVA0NviAD0HDIkUGBTY4IjqzRFHwElgWVfjg/hKbQA+ZEAVFISUnQA92IHskQQpS" +
+            "0giMU0aZhQ8xcA9HsAhN0Qhy5wWRUQO2hA9DUAq2BD+8BE0YdWISlBjZQwiGKFioZRYykFa3" +
+            "lFaLkBZAUBVwgw804IX4IACJsQWsJQJdYku2FVu2dJbn5QhbYAefFxokUEp2UGtvSAUkIACO" +
+            "QE1tNxZiYQm3pQSGGWj4sAQGUHi/8kHTSHC20AQGIAWbElCC8RyR8AOEEAJLIADPMSiEgANp" +
+            "EVAd5G4xoJnlyALGwRp9cAbORQhKYFEGoE7aIWDqRC7pBx4sAC8s8ApKIAW2iE5n0AIG8Atp" +
+            "8CrJZQsnZQktUCrrFY8Dd47r/jUWApcqGtMwpGIJCyee9LcpICYppSYpb2hCPcGXYgB1UcMy" +
+            "DWIiFLJzG0hDGMGBKrgQcDBDNcEGJagRQBMlPzFmUBU0IPFlS8UI9xBmMZQJa3AKMYkhGvIJ" +
+            "HAEFYOAin4APWQAH98AGmYAPYrCXRCElWRAFpGAHapQJbOAFTQAFZgEHkEAFSpEnNdACVIAr" +
+            "iRFYTRFYdqcFH9AU7DUgJGAGpeQInBMJj+MFmXJZr7MFXUI5cwkXIoBpl0UKcVNHglBYZYKU" +
+            "ZQIFgeVZAhAJSUF4FjcEghFNjkEYY9EIFZc8jQCAkQaAspUYNEAJ50UC3eYINnAeUHEDNwAE" +
+            "MqAs/kOwBfqVGPolfW9QSE1gBvziKeYBgP8TIODRCEpQDIbYAlypSIRQf+zSjOfBBexSCuki" +
+            "YGqAHqeaBptpBrZ4YMixFt3BAqVQCgZGBbVQUAEjm+dYC6WQKvaHUfvxMGCBGb3TBJRQcbJA" +
+            "U24aAzQlgLW0CnbyF3+yBEhzNEijDh4XEzEGMtJgcxHyEiIzMgQxIi+hM/RJYzDic7xwBOyK" +
+            "ZGIANILQJDQ3EkG1Bj2CIVkUDVjTMWsDIirIC59wOR+5BkW2CavgViXBCDIgCIKwBSooA0e5" +
+            "CmIDsbRDD/RQA39QWDVgBFAZCZEHGNBqQZHQdkvgCYcSlohRJq5nB4Kw/giREAmkcGhbsHgk" +
+            "oTmCQDaCcDc5hg9pRA+ksAg12zlXsjdZikKPRApUAkyylWm7ViY1QAJDAQSDMnirQwrBZHg/" +
+            "4Amk4AUssJM0wAJYQSxmYwO2cCi900G19AOFBAngVrbRSBae0BxluQ2taFDEZVC2AAlrcV2F" +
+            "hBnEMiDP2QTJVQriiX3mMwzDYH6ecFhNQH6BRn2vEDDqFDDD2nAZZCAxIH+4JQtvOhYkkKwS" +
+            "w38GglskNgRpAVPoOSYH2HYCYJgHWwNmYbSS8QMw0TIEsSO8sAa8YEO8AAI2VCE2tjNHFAVe" +
+            "oyFeAwIfeENLxBFjo3hiNjb3EIUIgENQ8CQd/kquHmoEgsAImeAyOLEILNqSGgkGqyCFNmIE" +
+            "mQACPwAECECERgEHX3gE+BChPyABNgkCRkCjiIS8p3QP0AoFQ2AGSrkEpUBxS3Bae4UeafFB" +
+            "SyALkRDAZ2AD0nooi9NJWZAC9wCXdSQDjvJIkAAJlLe+l0Q3QMCjXcIUeokrbYJpbXJZZWi0" +
+            "H3QocrEIWfADxyYCeHooH+QDAoAChUc3W3hekmIJH3BbpXAXQ0AIVLAFlBACyPcDONAHNoBd" +
+            "fUB9nilOBHYvv7CdA4A+4mMJv0AI0UQIvwAYXUxthJAqb8A96LIEtgBABiUFmol95tSowPKc" +
+            "8od9XOB+xIICtgCA/oHxA7ZQCySAAvnoCZTQBIDMcApHPvxhKrk2FjdFSj8gTbIjGYQoAUvx" +
+            "hs+0CjdHABh4kUF0IQtJADjEIlK1ETuTYyDCcwoRoRxJDzkWRS4iEmB2NwhwBIt3aC5iECPS" +
+            "FTgRBV93dnAAJVEUDVbIBqwzEmBQvXDAOqeQAieAAF5ghTXJZFAQA1nAOD55gBm8NlCxNrKw" +
+            "F2mxCIuQW7SVFgvDrHVhAEywV3+ZeW3yhWUSWCdkF1F6Jo6wCvTXJpFgCTLwOwIgAZjWdnqJ" +
+            "FW1yDxl8KERhS6QgBBezSYAxFAKwOnspGHpEXWWJTAFDFZHFtcTVLFRgJ/cWTR3VqHoh/sew" +
+            "MQD/YQB+sDr8ZAlDUAxq4AU/0C0CoAXf8ZrD8ommIQDodF6Omio3oATFcgYA6KjAgX0toAYt" +
+            "gFD1EmhnQBzLUauywAVgoRqpcmIDRwKlgBm4RQP/1jB/4G/cmSoQN3DE8UCKvCmWcDGhWXEk" +
+            "hEu54wXQ+hcDcXMSEhEJcQIYCCE2NCE5p8obgQCzHBFQFiMtMWQqSNg0GCUhugXTqyReQAo+" +
+            "FDVAczYrSA+b0BNYMRKncHXVPBSGvQlbawRCEFX0MAdGEANCBgeb4AiWdA9jRQptA5cgAAVp" +
+            "8AdZYCdYQgJy8wcFHAO/4gly8X1hQQJSQAKGMht/AAmqd3ts/nqwshYDsGu0QOADhkLDl+U2" +
+            "XKeXWTA3rIMPXooVrRu7duBIp/aGAGknlkYDyTNNkOCWkRWnmxIgF0OYhJGbMPUqH+AIsoJp" +
+            "BhACJJAFZgAMaQAE9tQEAiCMjskFtok+qmFRBYWmr6CGa1FxRe2i10ED5iLI59EXSH0vnuBs" +
+            "7SGO3WEL9CJg6NEdelFQEuYw4ONAqaJhmYkwAAOPDSSeDmcfDYOnJWsxiIG6TPFBpWMHhhkD" +
+            "H9AI0dCtMTc0GSgGHGMNIHM0t6tDAsEyNOQRMsQyuFsREgHKQQQ0ICIGi0cPSNaQTAKgJxAA" +
+            "/UkUroxDm+BZwryumaAOU8FEDYkA/iogCKvQktIQDSVBFDJ0BGIAg1FCZYpGCvLrBXPjCGAg" +
+            "CLbwB3yyCpOHD8l3sHq0Jo/ksrJAF0swDJEAOJZ+aG6TJjIgOqRgdlEgCAu7eICDzWmyNjKQ" +
+            "VzCql6eOOTlbA47AoZASOq0TA5tTU+ENU0yGGFkgA9AISv1oBDKQo/m7BESZBvjw7MSFAE3w" +
+            "CtYdCdQlFw/DBd1kCwjmssUwDK91A8Pw7ITwStvkhdGWFra5K0jNz9AVCY/qKqWQxw8jBcMw" +
+            "zrYwDNO3BBelF9++MH+7FZYgBRKjzv2IpvX4OkPQTalyXdR0fxkEYptiKVbBrOd5gG3xF39h" +
+            "J3ZSWE0x/gRTgdckk1MTwZCbgOUAG2MXojUaAmUf6YEEq4ItuSEuQlWtDAZkZ3Y0RA9zUxQ0" +
+            "BFVfWjQRUdlYwa5RgAAza4dwYIUjsQaHMmYfIoUyUANLNQdCsc0SghVOnwkEYASRIHeLcApb" +
+            "wALSTmlUohRxWoZLwBqIYQlfLUkssFgSgBiFdyhOCcI1MN8Hy6SPswUjOxVu2QhniXkoZJih" +
+            "ISm4MhdrdCgNyviX1YBJ4QWHRBdygWiJpeRUQXeOEJpQISmNEKSyBV2IgRmf5T6O8AHLRzuX" +
+            "oJoxEPvgk132l1/RMRrRwWBcUAstoARM4BipARYVpUhMoBn5RQJBIMdp8AsF/jQaAl+dTGB+" +
+            "9FIsy4EeHJX9/jEgvyoxhFnjr0MJKkWYVDABp9gwDicLDvPWpAKA9DcmlSzkpAb/kSYBt7QU" +
+            "K7MJc7ByDIEQO2KvAEHgxIlTbECAYAOG10E2cBjBAQOHzcMocCBGlHgKBKNTjKIYEXQPyik4" +
+            "UGTU2IJAYklSMTLBOZWpoJd7WwTBuccGipedW6CQAnNkCxwvWzJtmbOGJ70oD+EQyIQAhJEj" +
+            "YKBsoYcvC4ItkKgu+uPFiwwjMSx5ibFkiQBHspQ0EQDpB6EbTbTQiESDrZ0/KLw4+tOEhqMY" +
+            "JJrE8ALJ05ItWwTEQCngR1EvjQYDceQFyJbNixZx/pbhCMhPL4uyZHkM5B6QJTGuts6ypQkK" +
+            "R1maUPFSg0uTLXa4DCGcppGXNFRkpxEQqY+UH3b6KPkRI8gZS0vSmKHChcuZFti53Hiz+8aA" +
+            "H3MNxKBy6YwES5e4eBmmZkkjMzeG0LjBpdF3GzEMmFmiCQOo+IEJ5swIYgkqzqCCCgA9ScMT" +
+            "EgwoxZLiqEjDFhLSsMSSBofo8AcCQbyNBRKoIIGEJTxpogkWWVyChB+aoCRFtcrrkIohYohB" +
+            "Cx5j+GGJ8niMBDhI7KBhlRgI22QTApwkwCBe2DiBlyanPIGAJycCgw1ewFgDTDAQIomNKECA" +
+            "A02SEKiIDYM4YuSIkvDB/geKI4y4ShB6HOIFH3q28MIinU6BgoQsoLhJEJ1cy+2ohgKlB4GH" +
+            "NhFDpCgQeBIOI3xySBBB8JGBHjZWGa4rev4QbpG4vIgEkhhjgMRCApdogYUf2KKhQhpoaEKW" +
+            "4YCUQKwYGtmMs81O20I0zkRwxKYUNDsUiKKywAclKHayA4pMHIGkBiiyoCGGz4AcrpHoEOPx" +
+            "jx5/GAKStNRtpAl2f1CxCRVn3I0FLn75wQYp1BiikTT8WAIfLvygIgsWNHlPCU2U8KKUS6iI" +
+            "QYkboksjCBJoYCGNH3g1g4UlWFCiBRrSUEKtMw7DkARLDGCBCiXSaOI6mZXgwhOczQjZwTS4" +
+            "/kC54zRYKIWKjku5TbA/PInZRUta9JAKDZsILMDbUKBCyBGXoEHIG4EEuzyvP/5x2EYEaOQT" +
+            "tav5pBppNolGDLnFoHTuJjfhxcpoGCGAkYkY2WSNusV4W3C6ozyB7ok6EgMMQY5g/Igj8Enz" +
+            "BIXY2AJPRnhh5BPJj4gBpimP2AQMO17KhBc4NmFDBoc2seaEzcEQgxFGxODlCI4y0V2QTBjJ" +
+            "BJ8jaogEHyNkkaUGGQRZJZJVIKFiGDtkiOQM4Y5EfhU7YvAkkpOUR2n5kEipsyZSBAFUfBmy" +
+            "8H19mwRZZPyaroUCn/OvtUkGUkgBIoYsyLdDUbZQpC3cwwtN4MkB/gUABUjUwguZiARvBBEg" +
+            "fGyBCwIgxYqgULFGQOEHl5DFPeYSA3wswQy2UFLNtueJBpFgCMNY2SqG0QdZeEEJfQiLEs4Q" +
+            "g0gQIg2rsEQfmgCJNOxwCL+gwiqoIIXWDMNjtmAQhiJUClvIohQ3GAaGZCGFNIyMCh2K0G2o" +
+            "QEV2DYMGsrDFEECklg7JIkCWsMUfLCGLOQ5hCZGIgSzUZYdGtAZI4ZJMDP4AiUbw6AeRaAQi" +
+            "efQrL9DAC0NAy9zmIAYCyK1KbCDAGtiwhoK4CQRdOtNKQHCKUBrkkxPZyEEQEgVSxuknUICJ" +
+            "IGqwFSOAIApbqCDvGsIGQXiBHrCEAhgI/lAbI7gOClHoE0l4YoTVZaIpG9FW6U5xhKzYzieC" +
+            "IAVCcImPVWRTFlxYhB2AUIPDeOFjrFKCAUggmZ9Z4j41i05mgGQHVcUAM5hp1UkCtr4l1DAL" +
+            "lVnFZiTgv3s4ohFbIEUNhrWFfzbLB14QQBZ84AjRLEIsjTFnUYSAmCwYlC2OCGSwDNoIkpJ0" +
+            "CXbYXruAdEi1LOFpt7HEB2zwijMMZwADoMEWCHEJGzRCDWoggQ38QIgUXUKdtlADE9PQB1uh" +
+            "jAmlkAIXWqAgJVQIZSmyBRd+MDPtnMGJNGsC0KDYMS5w0QDF4QILWHCGE6ahFpTgQtFidqKi" +
+            "GW1FCbhNikhg/gsq2AsF94qRWv7AtXtZIkiHFVF5/lBIG/ygkEO4ixrL9iReJIWyrPMS7jTp" +
+            "JY5wySAJAUMUMNIlEKxhDSdwk5u65EmOCMIIMoHD8GRwLQS46TMiia1OQMAZBBiBJFAQwwce" +
+            "WKehfAIBI8nCRVQABkkdIXXBBAE9QHBclWSCHlEhBRSAAIkthLMGi81CJDZGql3ZoQln4MKw" +
+            "PHGGlAlJFiRoBKlKwYIkLS0SAUyLI1cBSZsUsoBbaIQQqAXRxggCCBWkU7IOVZNDAapZLWmE" +
+            "IwrKo9MIcjNmWUQJ05uFP7wnCz8oRQ0EgCIgnCwSi+irF7jQhx/U4KY0sMMv3rCE/g/0gRAt" +
+            "+MG/tFrEDKXhBmmQQguacAMz1IIKA4AOF95ABS18pzU3+IUAljCxGEihDzSwQR8uaMJGWOIM" +
+            "trABz5ZgixssAQVmaBF2SKAEW6w1O1FdEBdkxoKw2qJFdI7ZyEpEBVnMxjAuWwKIniak6gSa" +
+            "EuyyUWuq07UmFHJINGhXkn4ACUc6NgafiEaT6BaNT2AWtW7S5GbdtBBPpnINeZuIl3hBgCmx" +
+            "4RRl2tyrCeU7BPzJJ7rjCFGOAEuYMAIfYJABVvBhEDnZwREqgYOeonGVmJz2FCdAUxSulbuN" +
+            "oI8RIPiUIGQgEx5yRitSSGIitfCYNLTLhGcgQSSWoAQm/vhxO0uwQdXEIgE7CAEI975HsO+B" +
+            "j31n4R5GyPe16IGSe9DjNP9+qBdIkYVgG8EIqNnKFoYwmi3AahElJgFKXHXQRmxMLIaNAV/C" +
+            "lRa18GpHMeoLitZciuJkyABp5YIamrCiSwinCQNowX5CwAVHKCEE6zSAHxx5hku0hhBmgMQf" +
+            "foGcJixnCE1ggiVO9psl9AHHQKPBhFC0Mp0pYYxU5ALOpMBmW3id5UZLw4lsYQk6s9DId+5r" +
+            "jlwUM0/UPWsSktHMqyMhFzXBEpSYDQsLLaTDthTeWL7PHwQgALWp4xPquMPj28bpT4ghGiog" +
+            "wBykcflNJK6Sm0ycpinVpE9I/kManXZSpkffuThBjtXWZRMbosELMeCEF4Jjg9we59zYzv4I" +
+            "J0jJNBN3CtbpTiGfsB1HTlE75fOCIK/Gh0yO8LhVLI941ZcBJCJRA8V4IjeyOGF8I5GGYUTi" +
+            "DyQYxipys4oaeIFOntKfnrYwW0ThQ08Gj34m0He+62JTEN8SiToJFU/Jrk/BKIYCgkXwn9iw" +
+            "g9hwBEcihUXgmoKqkIQahh8wAjsYBpqwAyogBRnImuOouJrDB0sAhjTAh/SgArA4q4p7hR+y" +
+            "jnL7geI4pG+yBEFCr0SysxiwhVf4gVV4wVX4gxuQBTtIg1eIhEgYkEaQmjySglKQhTRIgygs" +
+            "N5aD/qNSGAaSATwuCJCssYVhkIXF+ANZ4DNK0CN6oaPFsIRasARPWDsWkpG0kAVSsYwhmJf5" +
+            "yLoiiYFdIZJFigEBaIJIYJcl8IIlIAy0UJtPUIFExLRJur0mYYQTaJKB+LxrS61WExyEiBI2" +
+            "uJu+qZKHkBxXcwRYMj77iy0w4JuCiA1NOYXaWoNRhIJ7KIlNOAIBoAcjQIBMMIIugaWYQEUE" +
+            "UJ3fgoKGkBxQYQTTWZ4tAIF7oIJGqAE6iQ5WkQU8KgUudIQmIIQE+YAl0A4XChgeMTZ1WwVH" +
+            "WAW1gISToIFGYLjHwCXu8wI86agGMw0H/IssEAIB8AF8EAAhuIcUEILS/gACAYBAufCC2DAL" +
+            "hAKS06ABS8gMGyABLxABGwgYAWis7GsNIWgQOxiCWggSqWoCfWmBFpCRBYGORvgPyxiAV2iE" +
+            "P9iGPrCDWjhBR6ACHBAOM8ABAjGAlTkydGsBr6MEnFErM2ACFoAimtGOmokqKTCDn7EeDCkF" +
+            "TzADIZPCIuLCBmmCWrGE3UCBrPIEFKgOe/GEpyEBCUGB8gC8BumrmUsRw9KzGPkie0EssQkS" +
+            "SrDDspQRGogOvIwByLMGteE0a1CBTuREzcobggA9XtCILmmSTUIIVfMS1Go1QrmHTACDU9g3" +
+            "bVm+2DowiFgdWiwJI4iCKGCDe3iIZjkuQiEJ/p/QFjh4CTW5CigoHTDIBCiQFJI4AgTwnTox" +
+            "gnMMHnzwgumhNFiZkZMqhRNCi6WyBR75M3UBMfoCEhJYEnOyhAiTAY7qDUf4DP5hKPzhx8ZI" +
+            "Fu9ECe/MzsbwtyxYBOysAYsqCpCKDGMLSC04KPQgFgUhoSYYBrZQC7TQDwFQAohhARwIOTUI" +
+            "gZNSAxxYpz4IgXK7AWCQlyBwDy97OSp4Oa/bDfS6yhs4NOsJEOqIgTMwIvSCBCkwACYkBIpZ" +
+            "Sl4xgA01mj7Imi5aMzl7C0/gAsOgGRFxIxRhEUsAtMLYGCFZjBSZuRihs7hrKRnxBHqRl7xj" +
+            "Fx7ZFa4pjBgIxB+c/ozoaMYY6MtNSETA/IQ5wFLUUwFJzL3b6wFQ86RWQ5zF5MQq6TwxOAXm" +
+            "sgiL2LXMOQIQUB1tSQGEUB0C2K031T82mE3MuRaZWM3Y0oo2PYKGOMaYIADZZAQ8ebVFuIf/" +
+            "WwNBiAGEgqUf+AOKaiTueQvoMYP0KsQnjA7rANU/oITW+AABCKCx4AxHqIED44zPyALlCZX6" +
+            "GY36KciayI1rCSdq+SefwAfE2J9wqRO0GMUtmI9NoYI/yAIRGMtF6LKwiIEZ3REWuEFupAFU" +
+            "SYNIYDkWIgQl6Jg3oMqSeblaYIEBwKs0UAMTYQIccCwDUIMP8Kk+cIQfwAEzaAt1og81/ugP" +
+            "60GPGyABIiq3JdBQLjiAAFECLbAOAsnB/rQEMeOrE+EQFkmRZTUMvwPLxXBDFGgCW1gRl1IR" +
+            "T/gDowkroxlLWSgPlpKRGPmBDrGEwXIpr8m7dIQ3O/yR8rCGnM0BX7AGns3ZxlMbFeA0LpUG" +
+            "y7sbtcFSwqGbu4Ebx4ObK5EdxIyThZg+3WkSaUg+hzgB5MMbykRFjkC+3ztGyYkbN2k9BDiF" +
+            "apC9hGCEvdmcqgCedTiFx4kJX1uFTxkL5TE/6imOVViFKIyBk/imJXieYVkeOrGD6kOUAvuU" +
+            "6jsf8vETegi25ZFcgfuUhLqH0yCFfbsfBJu/k5ABsXAEUvDO/u8ElP8K3VW4OAH4g3AS2czo" +
+            "EHK8oP2iAgEgjiUBmr9VgzTwAk8ABinYAkoABiUghSG4BCkQhEi4AVuoTgNoAgXc1kfis0YY" +
+            "ggZpDfLLozQIi1JQAhizhR8ioh3hgjSyjjgqmj/IIuvwBCviszZD0vPLGksovzkqEjBco5iV" +
+            "IzSqhROxo+qVhf6lIw+xI0jbwx0ppCX4g0EyG0T63xjo31uBFXSRMslQGzHoNMr72US8Gy9R" +
+            "tSqxEkYQnFOzklZLiNTaxN0pCEYQBJXgCETJgmk6hYWAJZCwCNVxLm9hJk2Zg6PIBISwCouA" +
+            "pfkhADiovZjYtWnKTRlYgyOgAWyC/gIQCJACE5lVoAGKiUIqsAPWlZfMEJF18gIhjEY7KIWH" +
+            "xIw/cCy0+AEteFV3+Yx7KyAfqAEJk6WzmSXRBYIAqoGH+gAgyAIJGIx6EoBkGYwsAIK8iCgp" +
+            "EwEjEABLWCAepAkU0A9H6JjK6FgRoAElGAIhALctOFcS+ABCIIQfgGQ6G5m0UpGLDRA068AS" +
+            "9YIWAKpG+AVCUBe6AJLdpYHvIAEt6IO0Y4GXs44o8iET6qLroASSiZmobAHtYAFws5Dx5YJh" +
+            "qJmYYQIUYBqs6aufJMq0whApSJofoIS/+t7i6JUTyRq7vI0AKcsgEZKuccgaCRIXwktCG4I/" +
+            "GAJP+IRE/mDETkvEyBu9znMTvHG+NYiSpd0kT3K11Eoczpk+O4EceiAFicik7LoHXDtUy9SW" +
+            "XROVFb6K2Vy+ggATy1SUU8iCXWMEKDACfICTTJgtAsjFyamT9hkCbJo/CJYFSKDkRoCEv1qS" +
+            "KoavLJALSiAp8+JC7QG5cdpDAbAotFENUFGeFDgNGVgEk44NUmAW0SBdBzvA+YOEzGi/CGsW" +
+            "i9oCZhEBSCIFkPqAi0MpY3MEjeSjtEiL29jDFhmxM+ialdGCpfqYS7gESBqAN5iMPpAPAegD" +
+            "HGgCRzAAA/iYNzCDE/EhuLKeWpFCEekrl5kZD0mDQwu7W0Y3FvhUbtQQtxqz/hPCELWKkAVp" +
+            "EDZDAToLKzGaOROpYg9hAU+ghDQohbrykLXsK21ugb6y3kJL0hsVG+ptDRmRjLz4kdYgkLzD" +
+            "tHyOPMpbxMCEm0xLiiqhG1vihR5wEkrxGxNutTjRRdpkzTqBgzMpaZ+gLtVhzcyBpf/jJZP+" +
+            "P5UQg9SBCGk7lIY4rtFgAzmhTQQINjZAgCFYhatgAwHwBGo5BQGQBUeQgV2RgT8ohXCho6IQ" +
+            "pPk4zxZBjEb6GFcVi2lJwMQolmm5h6v+PzcmXc3AKAGCKAGKMEJWEowSFyiogYfclHApKEJM" +
+            "ARkoj5pIkfabL4hCESlDMy+gAkKojgFIgxjogwFY/rNvUAJZgFcWMNcBkTP0yiqgQTMuYIIz" +
+            "KA9C0I8lwAFP+AAlUIN0PAN6nYsWqAFbIAQeeQ4BWLH5+NRGYLkGMCFLgIRqbAQW4Bep2Q8q" +
+            "OJmUoW0F8bu6IwE5cymPZSHelpB8iW0VGaPYllGvQ8sUgZGWpYSvbJHywMtB/Bh51QK57JpB" +
+            "5Fl16MucJfW+9FlrCABTr+C6wVLTi4Zq2AQubRKBbhIurSTZST7OiQnesRI3QRSI0B2stZ3o" +
+            "0x3OEQNEAWEQOIEjqAbayx3JYQR6iJPWyoTagRxpz4Rtl4GFTt5GoAdtqx8eqoHm6ZYxvNst" +
+            "UBLsg4RVYNxFOBJQ6YrZ/sKHRcA+GcB3bLKf/9uf87E/bcv3WISCZhGEf9o39Jm/ggw2d0co" +
+            "N4ZxO7gHA7JpGSjHg1qEgeIRyTjgmlaQYRgC9OPCDfmiH7OOS5AeKlCDWiCMpGoLQmgCfJCF" +
+            "S/CeNCCEIQBBM4gvLnKESJCCh4SEKeZGTyiMEbEEfbAjjxmCtYsEOIIENErcsHTw9W3D6q2X" +
+            "tZOFWkCeL+qnFfkiWRiGExmGDlk7qemzObpADxn7rElZWfARQQpYmD0XBx4WdxkCtv4DJEEL" +
+            "tl6nJehLtbEGytNgtYm8La28aGA1TRIDVZv11ArN7f4sSiKJh7CuTGADhy6gm4gtBIi+ybQu" +
+            "/l7AH/yRCnyoiKAwHgLgPUWpCRmwzChwLdqknMrFdzboXbaAA91oMXxwFxlKN0q7L3pRR8fg" +
+            "msyoOL3YrhMxxKRjMmE5m3tEGw0P5Plrv//ZAiEQDYMSIFLBjEagJyAQAZJ6qJ6iqBjBzj6S" +
+            "cEOsuGYsxCHaAl5pBAlsRkmWArTpAzNYBC4ABnapOQE4AzWwA0pQgyAACAEkCJ2JseSMkj82" +
+            "0pyZYKNJCxaUSCgx02SJGVt2ZN1YssrWmSVezhgw2IeLHS5n/tAIoqRRqZU/KspKkIYGRSo/" +
+            "0tRqIkUJixulliix1eQMCyppmjSxRWJJizSU0iRA0UIKFypcmrBI/sMlTQsqLFrUYsHCVhol" +
+            "abRyYWEJBYm4TH80+UGCytMlJH4sQbHkz5LAjWgM+UFjCY3Dha0x/pToE+TIn6xJ3nTn050T" +
+            "BAis2XSCDZvObEAwYgQCDpwooNkwOpIJAZxT+EhBOXUKzpYt+KBAASNIEBzeCFwf2XQPym9B" +
+            "9xCwyeQ6tRFScEBAYZMlk5Ej+BAIyoTPSw1eCPBtAbIFSpYtMZpkieFlSaNVf2I0uhsj0g8B" +
+            "qyLR+CNgUQ1LeNJIDEIENoQEdjC1hARe2PFHI+ABIcEqIoiwhReLbLGIF16YRx4QQkBxTxaO" +
+            "eOGIhlksIoQXGAKRQg2OXOgIhiYC4cUQ/gIA4YgWLXpBQwxZeFEXhk3QAEQjfNFnyRIf0GDL" +
+            "Ej9QocRTbVHRlBJNkPCVlH1I0cgSAxjwQwwDEDLYJSVRgcMNQxLCBQ1KDMDCH2bccKUZWaLw" +
+            "lSVKUbEEFbb8YAlPS3DVBBVmsEDRVl3tJEUTtZyxpRmWdNUEFy14IhYJfVKBgllUaJWopl55" +
+            "kpYtW4lliy1jmfFTLaU4NRddVz7EF5N80fADr4PFQMIqObpHgxfuLRHDD+pARllk6jA72bLW" +
+            "qODZJmKcsAYbvPDS2RqMrIYaa96ewkhwtR0hCD67nQICPenhk4lzbAiCQCZbGGEvHPBuQWIW" +
+            "RwTHBj1snEsv/gj4RAGcIF7IAMdxgkBx7m5A/FEDJLuVIkuLqzQRiRdUeAKJHZDs98cqQHxg" +
+            "aCMCePEDJT80UkOHAqjshcozYwizDDGu0iIpO+KTxT0eotfhFiksEoMAujmiIynqJY1hDMcV" +
+            "6wUUjgxhB3l2RI1kI4tk0cgH6hG2iBZ1OdLInzVosZYXafzyhx2EEDKkH6V4ckkaMZxxiSxD" +
+            "9IEDFXrjvVMffyohRUa2KEGIqGn0gcIfiONkQBp0BbGEAGb84vKdi1BRCrJZNoJVDFzc8IMW" +
+            "ZiyhhS2l0JBGGn/ADmjeZtFOVxN/IEoCWksM40moXJAAPOyNc2GlLQlwoUREx7Mq/qoteC2R" +
+            "Kwk02BDYX1EeS5gNkNAXwxCEkXmsLzn4Yo35OTC2PvvWnO8sZNVAJk01m3wSzSdiqDAtL5v4" +
+            "Lw0jeFGa0vDiFPQQBD3AcAoxHOEURziXbY4ghlNkojSZEERp6HEEC0IBDtEYFxuspcEj8KKB" +
+            "F3SgDFrjGmJkghcyeGEkjkAKkU2sBjVYxSosMYQayAAfQAiZDAQhg0gMIRIAuuEq9nUPUqyC" +
+            "h02TgcJksIVVSPGGPOwhFGYzm9+QQohVTFfTSDEb3QiiaenZggw0lEVH1OAe+FhFDHTjhUic" +
+            "xwt/8IIgILGELZAiBpawwyKGgDEh0CUSkOiTQaRAhSFQ/uEMjStFGoahFESlxRJouYEOLfGL" +
+            "YdBgGDeQxSqUoAQOEcIMWVgFIWxxjxjcoBFZaEIa7FAD2BVrGMAiSgzs8JRF/MEWRLTIEtLg" +
+            "iY0kxBKWABKU/mCfRQJqCZTwhDGpIIvpeWIJTqmFJ6JniWE8zxKbit5aSCArWTSBm3oZ3kU6" +
+            "ZQlZAGYIQzAIfQoTiVUswRE0EAB9IhSfJUBiCEkSwA8Y4yzKrG9ZA7WGsiDjv03MYRNsYCgv" +
+            "QLOJiK4GBATQlsAYcQo2ROEIYMjEPX7DmyiUKxNQkMHDgOOae7zmXRQ8RXfw4bDTxAY4qNlC" +
+            "cC6om9scxxGrWAQ+jhBMAdQA/gpJWQUkZHHPP8xHinYkExvB9AGQwfFYJ4oBsny6CiAJwBF/" +
+            "MEzMhkAjmImAPJBA0RbOJoA0xkACuXFQi7ZA1PRAohFCuAcQfuDWQObIRPlxhB3yozUSII0v" +
+            "AoCEMoHUBE90iHeJeRXacLCoPgygEVxQg2G2cYa2fUMJNWiCHwxQAypcgglesIFFINFIEgBG" +
+            "CUP5ARekwBe08KUoaBvTB5ACCUqcwRNDyFN9ShGXM5RiGGkpJxMkiRQSrCVRWcoSLFnQBFEu" +
+            "4Xij4l2qYMcCKQyjFlmphaIqcrzjdQUrLMhKVpryFBR8igR14cuBCEQmArmnrjGgQSPeidXF" +
+            "sC8y/giF37IUir9PTGsT0SDAJghwrc9k6wQOJhcIOKoaclUwNtzpzm1uypt0NRAOYHigEWSa" +
+            "Gjbggw0IOA+5SAgHAjAMCiegRxbhQI8akEI2/FzFiCJEhUZAEUw0GIoXcOgeKGrtP1PsZ1sH" +
+            "cxGazVdmEoiPe7wwmA+wMURoXQSWgQAEmGUhCx+IUA3So2VHdDkFjpBiiRzhiBRsoa5AwMd6" +
+            "vCCkJdgAQ5b4AYZIQAkZCMASOrJDk7JgBxbEkQay9cIwXqI3lDRSS2eo3HTTIAsumKG5eWKS" +
+            "UkjgCJX8wA6OG4IXzHCG/BAiDYc0QC0awZC6lNoSPmEBDVowFCoo0gal/pACoAyQlTNcRAkG" +
+            "aIKdSHAVWitSK5SMXltYUJQWHO/WZkmDWUrRllKwQNpKMUNWAmWWWkD7UNBcb99E9YcP0Ae/" +
+            "EcolqLsGCST9CKCD+ZFA16es9lHGfpGJhgoI7D+FEmAO/d4EtnghhjUIkIKvaY4gjBAaRsCL" +
+            "HvTIxCmMAK8R8aY2p4jCKR6WGziwoTZggMMRoJCJFcNBhbKpASOgsK900WMRmYiEJWAGhZFQ" +
+            "gqhAnqYP8RmDM9egEXYwjwDswFUByDdDHcqlCGL0gRMBYRGOgIScSQEz3eSsRWYO+iJkUNcW" +
+            "mSiOUFgEJLZQIjvI4B7qiataAeseO9AsBpDo/qoEJLArt/v86EgDNApSNiXCFEULmrxIqdtG" +
+            "CN8OQA1/UcMlDHOJIMyn1MiqdJSywkgubEqcwuuTGdyrBJ1QYUw0yNQheR2nP6Ul1mkAlBlI" +
+            "v5aiEOX0pdAJ8GiAqD94AteK29RQWHCGVEmhBYfDNlag3RUqoaD4KPCEe5tgib245U+LXZQW" +
+            "SECgRgyGQImZPn2xijTtD8EXvqCA98vnvvOF/3z0bswnpIF+yIhBwGKohhjEQMBoBNA2AhdD" +
+            "AeWv0QmWxoH0KI0EMQIDPVDHaYsYsMu4ONAHaZRr8EIF+Ua6wIEY3NALCYINbUwkyMI8QcIi" +
+            "EAMU1IB+BJEM4FAN/vwGPqRREIkdgLwQUnmBGHkRGb0QKchAF0GBGMUgDeIDzCzCztRAbnCI" +
+            "I5xgEMHBd2TdG4HaSRVRetAHAuEI1azCMAkCsmCZHURC2Q2BJ/BgJG2BHdwAY1EBIRzLGXDB" +
+            "HyjBGQiSIknSDXBXUEiBLWSTqBgE20RC7ARIGhiRJUhBg0AbhpjBEJACFbxCh6SBJXiBLEjB" +
+            "PZXCHx0iYEkBKP0EJJTCDQxBMEWCNclCDBCa79jCEEBJNr1hJGUTpHCBJFGBpaRBq7jhWVBB" +
+            "KVqSJ3iCLAxDXcSAUumXJ+AHES3BO4UM1O2Me+RSVxELlCWJHaBIhwQU+TGG97FPQg1Y/r1B" +
+            "Bv4YWDSIwTT6T0MlGGgQAGushre0BsjBQQHxAscdAYxlAhuAXANpBzgKwikgwCmYnXTAwXBk" +
+            "Ahh00T2cIzmylBARYQ2coL7cQxqQQNL8wTDcx7FAQiT8QdIJgH90lZRpgRA4QtNFgntsQQ0k" +
+            "pHs8GdLozM453c691YnkBtLImclAQnocRiOoGX10SLE0gg8Igc+dGT4JwY3QhwA4CX14z/fM" +
+            "l8fooiA1QAMYhtAtyJ1pyQ+gBQnoXkAuzmIZQAvQwBmowU0ogR9QQQMsTiFyQWUJQRqYwQcI" +
+            "gFfYU6UIAA0kRWBJAQtECVD8gO/phFrInhRYggScxU4kBAn0/kFE3MCoiVJXZIV0BcqjJcrx" +
+            "YNswMIEtDEOlecIBKNJXKNurfMXyIM4ZMJtanNeVMEWf2ACTRMkP+AVc1EViaIEWpAz1mRv2" +
+            "QcKXYVUj2Fcj/MH6mM/5IdSzQAtknEA0zIG+qUA0LJT/WMtEcVQINQcbnAAjGAECqMYJFBAI" +
+            "+B/EkRQ7PtCI3IOMnYu+4BQcCII6qlwWnAIvOIzICREjqIcUIcA98IpIIA2QhJkAIAaPPWQM" +
+            "bJke0UCDtCavqJmU2YDKNEIkdA3NFMsi3EiViZ1FoogIZEgaOd0idBUgBVlKphV+5QbRkVn1" +
+            "3UiUdBkNyIJLCoBOqIdO2IGGeoIA/jDSH2SBaoEaRbgHVuSVGlCBFyjBJQyBFrwBF+jNDdAA" +
+            "CqiBGfzKG9xADFDBScTA2qyFQXCBRizBDbCAI1gCr22BGVKfAZxBI1gCIbBAI5xBSAAbnFTE" +
+            "EPxBH2RJH/RBML1JE6gBF1jCDdzAdDnScZlBEEzJAYxFltgJC2Db5+yeSsDOAUCStEESLHlF" +
+            "bLXKYXqMlvyJefLKruCFMSUJnUUImdCA0HnBWbEdfcDNPf3HEngfbJpfY8zmJ9hbpxqYQl2G" +
+            "Qn3qJvSAQ62BbXDnbZwCAchfFLSGALHGESwnOZoQuXScEWDnPbyjjJ3Hw+wqFITneRyBDIQc" +
+            "D6ULCFjk/pdhCBTgBXgkpMhgCGJ1ZCQEjgQsAn3WDIGYCEtCXYyYyNtpCKR6TVpJZNOwJIQ2" +
+            "wpbZU8k8HX1sWYTQCM1oyHpGSIc4gpNAqqLmEoF8pezJJw1YAtIYih00gpZYgMFGSANUU5II" +
+            "j5QoAfJ9haFI5Q9EZRpYlhpQqeN4giM4DgmE2pzEgBmylhl4gu6YQQswRRpIgbC9yl1AShNA" +
+            "kpS8nuOYRSk1EqpJwaRwgQFciRJ8BbZ9RQJYU1a05QFkBbQtgbLB0u6lynmhIkN8BVomQClo" +
+            "iXTxCiXuBbLcF6DQhWEEhg2QST8RiC7mF2LsJF9Q4j35gjG0LduyrTGoj/qw/o/6OAuAScZk" +
+            "VIOoqkM0qAP86R9xbtAaREP9xF8AyZ/8BJAAlQb9CZD8YWcBcecRkBgCnQAGQZx24EMJJYd2" +
+            "gAEv/EEkyBQ9UNEqvJG3CsIiTJMdoNFFQgIP5UYS9dAV+WAa/ZQUoVHWnUcPOcwFZdE90MPK" +
+            "vRAKOhVQyQAQuEcWJAzZ4QMvuaDW8UxCZt3RkJkMYFXyEtFP/QElNJ0OBVlgVK8GtpktLIIg" +
+            "bBMXQtsqpAEhWCIhlMIQnMEv6NAl/MIiCcUfHKIwAQorDgGh3OESSMEwEQrJyMIruJ0ibcEf" +
+            "3EAkgKUtDIkXvm8adCImHQQV0KFvRU8MwE4MGFcM/nhCKcCcLaxTXMbiNqWBIpbpJjoFNymf" +
+            "LSHKMAhPoISwKQKG2wUdVuGTHcARDUBCP/FwJGjNauaTfgDBEGxVsSyNPRUsh4QfY8CmMj7L" +
+            "vEXGHYjBZfCmNe6m/YiB/2jjGoBAAwmMOYIAL7CYhyGAEZzGcnbcEQwHArSGRqWGwyTQrvIu" +
+            "D4EAAs3GSd0DI9wIGunUFsQcD80MPRGW1jHFPd0IkEjAmolNSnZIMaoZ1M1MXXWViaSHEExd" +
+            "ekAqDXJIi+wI0cCMz+3IKsyMI8xnDMxHwRIdPJ3NLr7HHyXJn+CTJ7gMJHhCEwhE3tgBq/zA" +
+            "EEAbFq7FUd7A8Vjp8dyA/mvlyea5jRJMD47SABXI6CI0wQCwaBO8AQrYgRLcQONt1g90aX0U" +
+            "zhAoG6A4UsW+gXRdwjAswS8ssxkUju6ZhSP9L1p+hS3orAkXhVk0z2BGUqWlRVJIASqi7PJE" +
+            "RFeuhS1LF1Mgi5ZECSXqohZwJg20zEVwJlYBicvsSpKcm/ZJWSOUH/mpz2RQRhRLxrRghmVA" +
+            "4xzcAW4yVIIRJy+cgBevizyiBncWkDc6h0MBpxHwRsR11E0FFRhsQRSM3IgAxz24hnK4nG/A" +
+            "AT7lRibk0onQzJb5EWJAKg1XmWp2SLD0iCOMpId4wdKZiATUTNudWXqcmRSpmXkIiZEhiQBk" +
+            "/kGBOqrTFey+5FUj3MMi2AANLIIIIAtpMtUO67X30BkNWE0TXI3u8DIlCArawFoMUNsQxESU" +
+            "pEUjsMCb/AAh3ABYrugS/A1W9QEhuIwZYNbncUEjaIEZkmET6Bok/AC2VbYZuMyXTk4aSEAa" +
+            "DIAtlxoJnMGcsMAAGGYf8BoTvEGs6KUlVBoJmEHqEQKVGEDJ/myi3ABamEEt+BpzMYFSnEEC" +
+            "HADEVhpamJeqBAqR1oUxLQhi7Mrc6eJeIAaZ8EWnaYFBWA0QuxKZvXWLxMATW0MijN9r3gFl" +
+            "3EG+eSpkqED7QWM0LhT+CZC2RIHCRVRpfBxxssFGoYY8soE2+gsc/nRZO6LG/2VCCfKCuQTH" +
+            "hlwnckBCzjyMSABBFbkRxXgPhvwBFfhHVwsGYCWGeyxN9ckZt8oZPphIhHDISG6IyniNJ+9L" +
+            "gnqBD/hADRQjXINdzLSIfnQN1HBrjniBEOyKlN1ZwaKMANiAb4FdXNSVdDnCQWBOcv8AEIBX" +
+            "ft1AC6yCJOqElRpKllTXqP0vE1yEZefNQUhBxzCOF3BBKa0CoL/EGbzCsaxzYGQ2RsAOsHlh" +
+            "GjBBC5CAAfya3PAsDvRsMfTspCf3mPzEWEhBT+wesLWALZhBAsASJN35pWwJIXrFygakdLka" +
+            "C8BFXKTlOTUTkNKH1hiG+Lj3sdBXYpDJ/mG5R9A5iIkQiyOYQ9u2LfiZj/e5bfnJmzoAGEF9" +
+            "wkFZA/xN4wkY7ilsgjRQY/8IUG9S4wk0kPzJnwDRI8MFEACCHAU5IMM5kHcw0CqgywVlAj3I" +
+            "AvnKlBDlcAzwEBEVcZvpJxXZkA0lnBACCG/cgwzSwwuJzAtxCFCRgoY4DHkojEzJILpkHW+M" +
+            "4CKI0SqwnQWuQkgtQiTQkZAYEYYscErOjH5yDKT+lNZcjda8HfgEDhHpkB28omrhPOxYYvxG" +
+            "guPZwhvIggycASbN0uCpGsTGwCtIk+ycAZPUs/OFMKCd3h+cRSN4wisMAyQAdCSAhCW5IUio" +
+            "jhTQryfJAqEE/k7rrIfrrOwQKBL/DkMvCdMwOCKryILam+Lp9Yl73RlghDCTCBJn+pM/nc1+" +
+            "8nCxSOqDQOoSyILL/IAXaAGgSdnZCF1Xc4hcbUEjjEEqFADoF8AYhP4DFMADlP7po77pp/4V" +
+            "FMAVPMDrwz7sv343nH433L4qnL4qXEHt3z7vdwPvq0LuP4DwX4HwE78qKIDyP0AyqIIpGL/y" +
+            "K3/yq0IyKH8yXH8yuAH1KwD2L4MbXL8CLIP3X78baMMyeAP6J8MsXH/6o78bcD/7K0D7qz/4" +
+            "z4IbfD/3fz/2W7/8u4E3JANAeEvmRkEygwcNenPj5uBCgdqWaUumDaJBbZIYurmg/s2NJIze" +
+            "JF1YNusiv1mz+F1ws8yZM4i3Rrb0eGujs1vw4DmrJInfPH7lKt3it6wcP2eSytlcJskZv4u3" +
+            "XC6jCXFeRH63SEIFGQ/jrXmS4Pl0c3Qs1nLznLGseiGtpFs8jzIdapRnuaIXPDoTKRQe2Asp" +
+            "2eJ1Vg4eP55Ey4X0uDQxxYsRMUpyDNHNY4qVJS5z401g5YUnOSp046JD6Q4uXKRCzUeXKNeu" +
+            "U4kaM5tPqAKhxoSKLVtUKN+hYv323aXCs1C4jOP6XTwU8QpXnoly8sx4l2fFiXfBZeoKdh48" +
+            "QnH3kKTLHut79pjaxWNXFzRPzPMwtSfJlCnj96Ain6T+/pMkpqbggT4ekkAjiV2SINADNNhL" +
+            "4okFv+PPAwILZHBAU0xJUL7xTHnGQPsI3AO+XVAJMML6eJgClRRVROUJDWC0L48Zy1ghjyk0" +
+            "KGOKJ2gs8Ylj6viijGPyWOGLL/IoQ0gNjkFSgyOn+AKLJBn4Iok6oMnjiS+ymaKLY7LRwIM6" +
+            "IHiCh2MY0GAPLNJ8og4sXATSxTqO2WMFBspAhQFoNMgjEHQ0YGAFDbCAZgUIsgmmjmBWWAEa" +
+            "CMhk9AtovihB0EORhOCYMiD4gkkI8jjmGCY3bVSDQ1coI9QvjklVVVWT1ABHWafI4x819bNv" +
+            "Cgdl5c/L9KbIjz8A6cuwCy///ks2NdJSSyWVMZhFjVk+XKC2Nj74oCOU3nLrLRU+XKvAt9uU" +
+            "8w2XLkK54rhYrsAFlwpcW+6Kbo5TV1xTnqvAui6y4xdfDDM8sIsrkkiPhy7mCzZg//jbY0b+" +
+            "dtlx1wQpNqUL/pKY0AMEC5avYIzZW49CizMkMGT5uuABjZQjZHDiLkY0UD8EUVmRB1R0TCJH" +
+            "/4rUoL4v6oAPSRnrnOJQoVeoNZAV9oAAgpsZ+OcJLMrMA9Q9jtQAlWCwqNXIY2qtUsUqHcbi" +
+            "mCccLSOJYyD4GRocoekagkCIDCTNL5Z+IpAvVgimCg3mPrKXKe22GotD6zASC06rxALJr+ks" +
+            "44kX/lXNscYZTy1jyE1nNBVGGFHRgMcZVXRw8idKnDhBidFDT3T/6CtxvBS/42GMDpw9zQVo" +
+            "nUXtWdVUG8PZMQpIpYDjCyCeeOQLiIX5B55P94rnH6i+AOhjcb7dUB64IhZ0r+AOlyuuqD58" +
+            "U8IbOD0Mz8dQ/T12CX8X9NTbZT77yQeYO+52sZ878s93Bfv5z3/tu4J84Bc/9egPfuEbmP70" +
+            "N0D6zWeC6zMFfOjjsP7NIg/p2UMJpgAgdATLPvCbAjrQU4IbASgPG+zgHka4ixKM0E5lmB8y" +
+            "SpCEEnwhhOgAlSmKhA4gIiMPScgDMoKVqmDN8EZfQGGRXAHDUkGjDCVA/gYyJFGHeQwJSZJo" +
+            "1Q6RUQZ0OLEM8JjCLHQ0hRKU4INsrBQMaQhC9JDQPmVAjyRENMcbBWsPtWrdHye4wP5lsI/0" +
+            "2cUTTKEBU6ACQLtoYQiBhaAFJgF3sCiN8GAhvN1Baza7A17virebAriGD8kbpXBQKRxRqEs4" +
+            "FRDXcX7TnSsQh3vN4Re/0qe/JITvCq3DEHrkIyANCIiCE+xYyQqGBlN47D/2C9EeEBiij5ks" +
+            "Q/Q54CELFiwMHihB1UzP/PjosIntQU0XhE/BZvQEo2kpdIMyIuaKlAcYGWlyh6KPktBzpBl5" +
+            "KglHchgEkgYNaGwJoE9qkqP0KSokNclTQRoU/tuShgwIqMppPPoCBLCWJQ1Qymq9WEEdGFAH" +
+            "VAB0a5wYFCe+8IS45YFNU5vopIq0JL7lAWhKiieSZiQqG4VKcvvcaarQUYY+zQhzqOhgnwoZ" +
+            "LDVlcKl9zFUeE7RNaH7sQDxAzWxggZpLLotZuhMebWAjCj4Q7zWiaF5vfBMc8oXiGeVyAluN" +
+            "8xt2xTWu28KXvrrjLw+k7Bld6N8vPQhNwKJsPhRLEDR5UAGO3XJ+/yFQwDxQsgZRCA0ROxCC" +
+            "TOSBOdqvdY0NVjAhJqCGkRM98IFPjGblsyTdKEfyhFEdYNQkHC2JnENTUk9F9QRY8bRGQjIS" +
+            "QBFKUwaczWoNzdKZ/qBxjF2A6Rg88FMTB7qH5E6hDJTqUxVURbVQxS1QwRgpnxaVuGyE9BjB" +
+            "aBIDNOU0JXWKVSVAFafqYLU+HQ5wfEtpHgR1OVf1tkiXk6c8Q/c5Nb1ucgWbIwUFlJ4D8TFi" +
+            "+VnPMjO0zAPuTne6c9aFr/q7Z2HrW2OQjYf5sBu00qE3yomFdMqlnN6wslxsRSV2mlMBUzjB" +
+            "ldSB8CxDpMtkjSeEF5vmLwOkL4Q91mQTkvGEhgywAylzmfwxWcr+qtnamc48IkIke6CZsqR+" +
+            "rmAYjNiL6rMC2MUXRjMKHVFrhSQHGUlWRtISmyeX0i0JDQI7OtIOYMAOIryAHSZ4AQyY/uFn" +
+            "GHAgWGzST5NuBFKhfgEbrHgBB7DxAlYwgwOs2AGkAWGCEUTgRwONwAhgwAoYjIAVn46ACZjB" +
+            "DFawIxA0pRuYJhpoTZvABBzws6xP/Y/DNQoLW1oBEYgQASLA4NSDiLQJiMAKZBfpBS+ItKMt" +
+            "LelmT3sH1a52szP2hBcAwtLVdrS3rQ2IagMiZQyCmFZTM4asdoDD2KrW7jicCt0sL8TEexbz" +
+            "rpdvfOv7et07HvSORz5c4Jt83ZuebRwYv+55cH/xA2ADZ7nABQqQyfzjJQABqJ6My3hgaIh4" +
+            "xhl4vv7F76/k42z+ugGwPZxvCt6YH3r+Gixv8HE+spojOuY3/qP05KEE/Zsh/PIQwxNusOe7" +
+            "0Jx60CGJiJVhB9iIQxywwQxQMCMCzCDHpkWtyD0QEYgQwPkXkIFzIqTa6uRINTnMTg4kkIMD" +
+            "+ZjSEUuwgxGsfQSGuDrdyWEIZhABGirs1BF/PWoOXJ0ZcQAFB0YwgjhwQNiSKIEk1KjGMgRb" +
+            "8XEYAaVHwAHLc4ADO/DGE6rNeWxgA9KjJz2kGQ8D0mPDF9X+zw5eUA3NY0MYHKhGBKqxemGQ" +
+            "Xgf220+GTtOB2+Gud6dx1ljH+izlhbLeozxebHxjVrayspXN8Q283BXXWJBYFLhM1236ir5Z" +
+            "NgeC82lfxvgFv4Npecn8Ig9xPOSB/grsNbIak8/KiLNXU+z1OuzBV8r+A0P+Y0U6q3WCJZsO" +
+            "q2AQxMr66EU6qFY6CHMIBKlKS1Z4hGlqhUiMZlBQ4UjsKOheKEl2hajcDG1AJQ+YYQSWQAr6" +
+            "4AdqoQ/M4AfMQA1KYR0igB36RFNU5UgwigGwQPVGgRBsYRh+4QyaYBgIgRBkIQ36QAlmgB8Y" +
+            "QJ6CARuGwA+CoBZmkAtuwA/MoAXUABHgYVKEZKG+4NHOYBiWgBC44AfOQApsoBakYBgMoRJQ" +
+            "IXGSZFTy4NOagAWoAAWaoAlqYQ0twRaoAB5ShQhAgQr6sAmW4Ad+wAYooRF+gBKGQAsoQQs+" +
+            "IAZigBFg/m8KXmAQaqARGoEGGiEGIGEIaIAGSOAHMBEIZuE7+idApgBahG93PEnDvmqsaKN4" +
+            "fEP5nqWsbiM4tiUUxIWV1CX7fCOuxEU5bsM3vMeV3ipdyA9fZqk85CNgIkTBEsvJ9sr9DgYN" +
+            "nuEas+P+umCvhIwHPCAd6e+W9I8HPEQ+diEdqcnARMvJqCqDIqREIkZ0doRHTudF8oNHXMe1" +
+            "hip0bOpTmugLUMFOPCXNJGcFiOSd0GMFSsConCYPbqEKTmEY1MAIzaAP+vAjpYAcTIAd7OhJ" +
+            "IjC5foQZBsEOzOANNPEXzKARSGAAuCAGzuAVKmEZAqFMvoAZ6CENisEMmmAA/gyADXGAC7jg" +
+            "HCoBv0DFB1kqAgSBCc4ABaggDaiABc5ACUhgK8mgEpAEbUIFVAYBH7igCcyAC2yhFBSRD7mA" +
+            "DCTho14AFGyhCfzQEprgB/5gCWLgD36ABEgxBn6ALxtA1egAHbQhGlZhCOwgEobgA+zgByIT" +
+            "ErxgERxBAT5mmVKmFlOD+FKj3oJnw0TBWcTqW7DlND1sDOhAGOXqXYDDGa8vlagjldRFlqzv" +
+            "/xDGPMoRYd6vPAJwyN5vPIasGyFkZZbJHG/JlfDlOtxjQXjzYOavY/yKPijmO5qMfgzEggSE" +
+            "QiYHfgpsfjBImxowT2qmiMhJVYxGthjKH21kzhww/lUcROd0Sz/ELM1WABWqAAsWQQoGoAm6" +
+            "0gD+IAaUYAAiwQQGoQ6C5Ri65kkYYApYgQMgIRUNgAlsoAl+IQ20oBYIQQqWoBcGowx88AmY" +
+            "wRCGQQlwwAzMYADUsA+akAXigQNYKku+gAFo7Q+awAAIAS39gAS4wAi1sAnKwQRsxGyU5BiI" +
+            "YAjOgAuwkgWwsgnSoBRYQA/4YUicjgVIQBEToAn+gAa2VBUFQAsgIQYEYEztYBXwYAeSYBa6" +
+            "4Ag0sRQhwRFpQAAcwRG8oAaSAUMOhjx2oVluJ8M8k5Pq7TSNh3h0MTaW53hwAXqm598KYFGR" +
+            "B3oG7lGvZ1GlUXrqpXwu/u5/eEB+QA5DnqM8niNdBgbjPhXj/qddrgANpGeWAOg5Mq5TB4Y9" +
+            "5gVgumN+Gq6Bfil/0EOAIsYUumHk1APm6meQDLCDRKiDZsGJpmAXYsgbZoSziq5W4oeDrsBZ" +
+            "Ww4VlGsPKgEbZGAYbMEOhiAN0mAIIEEJpAARdIAVkASOQqgEjmEHiCANXmEJhuAGbGEILOEc" +
+            "WCAGXrIT3OAWkOgJ9kFHRuASLOEV3gAO+0AKLOEN3sAScqEKooSIWCrYbmAQpcAWbOFi4dAW" +
+            "liANbGEcqOuERgiNjtQWLIEKPFYRLaFlIXYZRogIRqBlPSFl7xVc7QBcIUECasALRIAUvEAA" +
+            "/uwAH3RgB3ZhFojAArygEewgBhqBMWnAae1AADATwpAFAPoUw7hqDAp1NpQvF2dDeYQnXm7j" +
+            "ClZpW3oDrXpDrc62xZ4DOcy2fMDn/wDGX9LjmiBMyCpgZcRRX1wpHYvjOlwpcOXPOp4hHfml" +
+            "r/hF/iJr/fQFHfcKQfyql8yEQazTwDKIwcQTnYCukNCjiOaDhvwRnTpoSzwFbRpyt2hqBOUJ" +
+            "R9xJt7SEB06lYBplImGoEpjhB6hACejVDM5gMA3gDGZgB/DgC0TkomYk1bygK9PgDzyhGLiy" +
+            "BfogDTzhF26hHPxkoiCAUmAgDZRgQG+ABJSAEJRAKNNgA27BRgwF/m2wARRKASRZoBRsFECb" +
+            "oHw9QQnCYBZsBHBGBQ9YYRgOgAVagAoCUSlLwXv3gR/cywTigAXUkgpW0RGXoBQdkQRE0Qti" +
+            "IGgxGAnE7QlmARtq4A+2NAYagASGwBQdYRWSgUUiZApq8aua5VtKg1rCdni4dqySRzZuozZC" +
+            "QVtSaVx8eBiPY8WSMV6YgxivjzuyY/9cqRy5owIM5pbQYK+uwDn5xW+Z02/nb6+IUXFdafqI" +
+            "Axz5ZWU0JrL8pZnGQ7H4w68ay0Dqw7FIyz7U6XNNa47pA0dEBCIXco4KxqjyQ1R2EAI+h1Xy" +
+            "WG1OpXRtpI+YZEUaBUESOUieAB2q4Agi/uEjf6AJlHIJeNQMpoEI8GBUsIZpiAAJfpcKzuAM" +
+            "tIAKDIAKYqBHIVYnoiRLVAoLdgARgoAESsEPbOEHCMEIX6EYuIEfsgFnsqQMOAAfWOAXlEAp" +
+            "+dAMlCArpSAr9aEc8GC1WCUCkOAVuOB7SyEtWUAKlKAUbIEf9iGosCEf0qAFzKAuqaARlyBq" +
+            "h2AI4LQU7QAISGEV8KEd0jUZXOETtKAvh4Avf6AUg1YB5COEDmYPpIWrUgF3pAUWQgxbvLY0" +
+            "x1be6u2HYSn6ciMWBk44kGN8YPMYB4YY8+pxPUBx5WP+eFPILib/5i+xmnhwP7UYq5GtnMAc" +
+            "qYM60IUY90/+/mSMZIYlHfH0YCZEQ7aTP85JQGZmR0qkPxTshUTkRhyGdPpkj4uEPrSLpsTs" +
+            "n0SnUdRmSI6qVXhEqOyjT1BHc9Cm18rhEVZBFpSABWrAErbSDmjADNLABAChSVIEAvA6EsjX" +
+            "C1qAEKhAALggCGhABq9hJUAlShgAHVYgECIgHGSSSQ3grYuQBQiBFsqhvDYFGpBNCtKABdSg";
+
+        private static String splashScreen_gif_base64_2 =
+            "D7TSDEw5LYOXCsyAEz5ZkMtrENbBANLgDJhASc8gtKWAC8CBHyCyDtrXSkmABTxhFRtgLwWA" +
+            "BhpAE2MgToPWERqhTs90D2ZhCo7gaen1BwRATO3AThNkPVZHqxpa3YKvWXTnM3Vx/pQiWt7M" +
+            "ynhu49+g573/7VD3bXwQdVKhw3xkCeNGdf/y+1QxDuM8rjYttVGdkaMxrlEJ/L//+5byJ39G" +
+            "FeTyh30IZj72x4IuSwF6lYJi0T6CVcFKSFemO0oeaYYOpAR0xJGYBkBAKGJWqMG6oY+W9YMi" +
+            "ho26YRmwQRCGQAr+oAZ+QC0xmAoiQQd0QFrhFQm8IA3vVWNtYRWOcAlqoAqUTo1KwBSwgI1K" +
+            "QA5mtg8IIccTNg2AgQmo4A34YU2S5hawIQIy1hJu4AzU3F7b0GYHcQP4QY1upASIwB/SPA2a" +
+            "4CoJ0RbOVxKCavI4wGWXgBLodQiYdhXsQAvsQAJylmkh/mER7MAyZWAHdCAJtOEFtqARBEAy" +
+            "hyBonVYBDnA+LqYD1o1aMKyTqqV3wnZsW914zIrEHkA5jCOs0LZ40Ao40lZd+soZV+kBnEPG" +
+            "/NZ9mphVOS4dqWOLxWVvaYl8nsEJnGCWrqOtYOmVjIM6zHY6ioPGiOMbl4k9TEZkUCY3r9HA" +
+            "usFXMuhGOGtA8gN+RASSHMa0mGas4YOmJgfw0ENVztOO9kBVCuZUPpdp7AS2HMk8dS6dnsAV" +
+            "8sELaEAtnbt8s/sV+mEH1PULmk4JzCAGqOAXDqABmFAJYqAFzmAD3CKlYEqlPEUcIoAE1MAm" +
+            "leAS4pd8m+AVxEFJDse3kYAE/qZXfJnAEyqbBIKAd5/UEzqhKR3qC0wAEkpBCRLgKtOACcJZ" +
+            "SXOBH1qF0cjBE/zT0CGYBCb4B2gAEpbgD5ZWABB9TL2gTg2B4mcBHUwgFKO2AVCRXJPhnJB6" +
+            "FrMqWirsdoIHNLlWeDaseDwsh1+TleigALTllESB+9CK+tSFXxofr2wpcBHGVREm//4vcLtD" +
+            "X2YT/p4BOp59OoaYOt7FlWrzCkB/X674lvwFwvbqsaqTYlRnVygEPZD6nLpzAhnGYVonD1LH" +
+            "3/udnWIFZzTFiMQMPhoFdRKHPxqlRIiqjxolPndlS/okpWZhH+ghBlAgmhsBBZSy021hGpwA" +
+            "WxkY/rOloBHS4AYgeM0dkQycYRbc83Dwqz1NwAu4IHiDOwiyUg1Y4A/eoRKYhIcAgkOkJUrO" +
+            "NPFkhosSQqXSKGExTAkXT5U4fDm2IkKENMPSpGHRohQXMxK58LuFZcWKQfk8sWDRZElMS0t+" +
+            "LFlCI8YPSI0ExGgUg4adRlto/POwx5W2XI5+2BEqYFUMBXv2TDHFo8ueDi46pILlwgWsVFw7" +
+            "jHFx1kWqsHzGuBXlNhWfUKJC0aFboECoWKH6ivpLN1RbXH1xVeh7JRSuKxUqdOlSgTFkD467" +
+            "8Gj8zLEHHqYee67wLPPjK6G7POvixMmVxKFIO3lG9xnjUK+fvQ7VJbGT/senT3fx8NtUEuGm" +
+            "9iQ5nsS4KVNPUPHg4eHJ8CQ8qi7foyFrkid7eEypWvXJEx5JpuTJo+FJHvF78pRBlQfdl/Mr" +
+            "jh3TUGa++i8rqp5Pfp54x+i3wnx7YLRHGSs8MQU6eSRRzgqrQNKAElR4sUQaJNQACQvsAILK" +
+            "LS8cQcUBssSghBIxkGCGGY5wIol6WHyxxwpYaLAHNPNFQM0PVFySRhNnKGFJKYSYwU0VGEFg" +
+            "AhFpMNGEEjcopIYUTKhhCxN9mJFGMWfkAgEEX2ARQRwp1nKGFGn0kUYLZxxABj8qYVFGHpVk" +
+            "Eww0EOTCwDjBQICFn480QoNNNGhBwxAxQPJD/gwxGFLOHk/MkgwWll6KBQPLHMfgFEmgAssY" +
+            "AJAFllqmdsVVKnKZykerfMCVilt6jZFXrbXSamutD+hawAN9+arXaq2tRuwDws4GWWLHGtva" +
+            "YQVcwZewfbUWyq695oXYrlf0qm1ivhI7bG7iXpHEFct1YYq5u+yy2i7LsXuVKetOwe4uVu3S" +
+            "zR7umlvVFbvMEu8es+g7xXeeWmXVF+h8F98U3mggScGS5EHvLhEXXAK9DTo8RRllLGeeN2Us" +
+            "3OAeV8wSgR2LWDKMHfgMYcsqggyBiA46TMHPDqTYYosdMUxpxx9nbCCJJPBYVUIJe6BTxhSz" +
+            "eLzDCFLAfMMNVPRx/oMlr1wySiX7TFFCmWm8YQkVZ1hiySVS2KKELWkQwgUVUkhhjzMlaFAC" +
+            "EeQ0IcUwVJTCsy3D8GyPJOg8UYLHF7ghiRuOJ5OMAm4oQPkO1AwxhM9PrdKIHZlD0o42u4hH" +
+            "aeSmK5CMG/o+wZwpqHQl1llembXWGKqyFVfuqcCV1xiipFJAq3711WsqdIiSV13T4mZKKM90" +
+            "E0pkkV3RhfOrReY8ZI+F29hjjoVWgROYWe8ELpBhn1gslVWwmGjRQ5aZbKZdAZxnlv1m7y5Z" +
+            "FffcLsjtURx9Tec7VcFRcsBTlePs4jsaKA972nMeSS2oPSsoA3YgYMEDLWgKK4DAdvKw/gLn" +
+            "qKcqHPQUKo4xHhAaB2/SWZDrVpCHPWhjbzX4AQu0IIAftAAFXqBEGF6wgy9UIQ40SNGKCGGA" +
+            "JcRgHss4ITRWUAcGHCMPYULFFwKRh0EMIA0/wMEvKGEAuC3EE/x4BBSZIQNPPIQGtVBCAhxC" +
+            "BYfYggtnYEIpBqCPcvzDYxyQgS0MYAYWSIELpUjAGRySi3Jc5AtPqCAM47Ow80gyDy+QAQ1o" +
+            "8IcGFMoGjcpJDFoxC1QkBxXYSaCnxCMe73gqCbDrQFm6chZVxQUtaOFDrGilKlXNpQCx0ste" +
+            "REGHV4UiFYLhAx18ubzqiUJ6kRFN+GqTG9U8QxRXcEJrTBMa/vD1pQLLxAVsrMm+9MEGMaLg" +
+            "yzhbk83TSFM1h3mGB07TmSSgC10JRNdxyEOeJHjAXsYhj3GSsEBTktKf1MknDxZYBg2gwjx1" +
+            "KFge5jMF/BTMPjEE4Qqm8IQvvMc8E1QPg85DLw1QLAkB8kBET5mHXcBHA7N4giB+4IgmcGEI" +
+            "XqACF5bgCE+0ghU88NoJlsAEJdBgTWlgwC0YuQJooFBMIOQPFaHxgj9Y6EpcaAEgSfCKV5QD" +
+            "BhAgggnSYFMzJGAYZ2AB25hQojOYrQ9mM8A+mCEmE0iVC1JgQSm4pJA2VeECGJmPRp+g0ElK" +
+            "6kFTeAEQtNCoP6xiCJGgQSMg4bNp/iTDf1ghl/70Jxw0XOUJXbjKVsKyFlW50iy0DEtX5BKr" +
+            "3cXKVX3xHWv/MhfAwGUMyzOMsqh1GtqITzayaYz4oscYdoKGNYnJDDcPwz7efjMzinEebEAT" +
+            "vdCoJnrdi99vuvebzVjGA9TpAnI4888ACueA1BEPpzRwlXx2Byn/ZM923uOdjKJCA05tJAT6" +
+            "UwaMoEJBIWxqehLHHRUCdj0kvegU7iMd9ThUOhpY0Cw0EAkqQIIKSvhBI0iAAgHQYAnsYMUT" +
+            "KrECQZitFj84Qz8kkQwxTcGpeWDAF1aMhT2UAAKskAEhzkCCX7zhD31QAxfSYIBcVIJMEVCC" +
+            "AZpw17iZ/sEAb2NTi6jABAMspB77CNMLCqKQQC55GFJoQRsuUAmM1EEl6IEiKi6anvA84QVe" +
+            "aNQQfvCDnHRSUdOYBUCRU9B7dqc8GGUQKlwJC1i40lSjTZUuudIqudjOLW/hAy9pBWlGF4BW" +
+            "w8wVLr6Vl25lulqh2PS2OE2aat32Wtb69LPysq1N4yIvl37AqlnTml4Ni1jPUhaxiGWZfaVL" +
+            "1/W6ThfWpS9/Lcdf1rnOuuSFrytUxV2mQIe+lmZBDsZwgchAWsZElrGlXRRpV/FGDMFTsfUU" +
+            "x2n2Khiw03UvUxBhCIuQwRKoQAov8AwKEhiCB3Swi0oQgRRSIEQMFnELZ1hl/h9NK0GMH8od" +
+            "LFAMGTAAgy2qJjdCyMIWb1hbP/jxBDJZouIUX9sNziCLV9zAbWnwhBRGXooTl2EfRLBEGsom" +
+            "hVrYggUyZ8IMDCcxiZUbYLvwRsEMtgMZDOEPQ6ABJISSuQbEIBLtSIa+sEKcg9JrOFmZuimm" +
+            "EGiuoGoMXLEdaGVHWtS2xXaTdnQve5cX4Dm603qJBaxhs8y+VG9ac9/mbOTnPF/m1i6hfsZd" +
+            "lsfc8yEGMakhzRW6Qb3KmEK6FaCM9iiTGcoMezn7PE5xkuPPBB5QOeCpzr2s0sDRMag6/4ko" +
+            "eiH40S8wUgNjBmwdGqhB86geo/ltoHtW6MIn1Ml//hcd3bfr5B0DzSIOJBCAI1iwBAE0ggpN" +
+            "mHAYIkAEIY6ABr94RS74EQ+Fd/ALVmRAHrCQC3SsIBs3egQS/mCGEJDgDMBQSAh+kYZCVOIL" +
+            "4mDGDwzAVgMYgAsHMEMhuFFWLJAGgxRIw1AKlQADWIANqyCAMlUKS2ALCUACXFAPy6B69bEe" +
+            "kqR7jaRQH/UEO4APl1QolPABRwcUjgJKPDAe3HVQ+KSC98QpkhIWWjcGpRIWcRF2sENLi3ZL" +
+            "kjZ2v7Nof1EXtMUX00IYV+BN0UMty9MXTiAK55RMfSEbomBN1rQXilGE01IXynUYrWFNz4AG" +
+            "wiUa26MZxvUYwOEBwIEV/lSHLvjEXQV1ec+RHPqEBvh0HJJyhxqwLtVxFbuQHncYQ0mgIOhV" +
+            "BgrXMQonIA9CX03TYCHEA6jQUBCkMXXgHzCkggY0QmigAQ1EUsfgbClQCkFBBVQABEvABU3Q" +
+            "CE1gCHjAA1WQDetABZgwD4oUCFjwBNkQCBnFAB2ERWUQCF+gARXhCWmgBkrQBH1gEGZACEpQ" +
+            "CPNQHyPwByxACG2iBGZgNnogD0rQB1zABb8ggFySBr1QQdiwDk1gV8w3ii/BBdnAD6+3Aqz3" +
+            "BMeQUPDRZ+eBXhqwA4t1E0T3A16QKDGgKKFDWZxRLgSJBgkUdcJRaAAgaGtBWjMIFrYTWqvl" +
+            "/iptsTui0BavcpF/4UvKoxiwRhdWqISF0TyEwYRGiJJ84ZG4UE51QRhFCC1uR00jWU2pYVyN" +
+            "cRjnYxoVsHiT8RvewxneBXVYwRlESR3gQR3bsWdVAV54yB3S8SnNQR4PIh7pkR708VC72Eir" +
+            "Vx/iUR8QICD9sR0hVGAZOEUHZomN9AR+CJX/QV8rMDB2QAV2gAIsQAIfkAZmYAmR8Aft4GGV" +
+            "UAWKgH3nESbnIUV5EAwX9QVgmQc6kgd4YAkkIAV98BI+RgI4wBAVUQcwgAh1hIwD2AcHUH9c" +
+            "xiVawgU3wBD1EA8rUAIj0DZMwAUs0DaBcwEX8FANlgf34R55cGbo/kFgzcFmNrEERZcTfxAU" +
+            "DQAJraAA/kN1dghQSHFPFZMEo3VaqNIBNhgr10k7O5haYzB2vmM7obBowfMAHJlp1pIr2vIs" +
+            "1oKe3GJq1VIAsSBr8mktuMIrl6YYelFrSyhr0qIt1ZIut2ZruIZs1KNs7EJs6WIKergcyNYN" +
+            "xfEu+eMuT+cuV2EuA1NukrAuZZAxDQJDS4MMSpM0GONsIFoGC5Qx/0Ix9pI0q/Niu0AxBJMH" +
+            "8jIwV/cdVwA2eZAML+AJkbAyUhADNTCKdmAHnUAEO7AL/CAJCnAL6OANe4AMzlYG1SYJEDAF" +
+            "kjAjU4AMdMIK9PAKtiAFavUjUkAFhEAI/pxwCwzwAkgwjaj5Nm9gC5IgB6PQjWpyA6VwBjeQ" +
+            "Bp1gm7PwAktgCWuTIYJDBYjgBolTME1jMFbhcz+3B97ghztQA0OwBJCwBHbwA0OgBTFwqY0A" +
+            "SsgGQOVyHVixQFAHUDwAaDMIS6KlFtcJS2axaONpkb0jaZBWAKLwAOWpF7ClF3cxacQTC8F0" +
+            "kn5hTPrpkXPhF3RAW9NCBywJd4D3hLKReNPTGJLxa9XjTjj5G6SRXZGBLmiYTx9kHABEQO0x" +
+            "HuU2oRjlQDFkCuvxT5voPw12lfqRH/3xUMfQMS6WBIuIHaqnL7lpezD0KQaSHzF0YBpDMY/4" +
+            "HjVSBmupAfsw/ilxYAY5UQosYAcCoARpAAQ0EAZEwAoPFUNf4H1l4GKIOUVfkA0wBA2BoAEQ" +
+            "kA1EEA44IBGaYACeAAxs9QYDsAFVAA0mAArbaABAIgWX0A+3wAzrkAZncAYKwQRZogQoUAl4" +
+            "UAeDQA3DkACl0AIuQQIsoAfLAEN1Eo+I41GAdTgMUpUvUBQ5UXRD1wCI5VjykAynVB5MaTAF" +
+            "RS9yyEqthBZdx3WmtbfXKZG3pCq8NLi9tFpkhzwYGRgPIEx/ZxeBcYVGGAscaTx/dxeXyxfH" +
+            "c7mRO07BWhjOQ02pQV3ho1zgAxzXAz+N5xiOYQpoKJTjtRzJ0UB8iDCap5T55Cmr/kRK+2oV" +
+            "x0ExzaF7/jEf5yGPEaUSXkkxrEdYdeCuHoNe+yUdBSsp8zGWeaCC6EF63+FULxAB7FAJT0AM" +
+            "ZkACQ6AEtdAINpQGGrYBJuABxwABdfAEDBAMGhAIvZCb9nuLwAgN0EBF2YAOzFCMTRAEaHVX" +
+            "xmgAenABWFAJrFB9VzMAePoOzvAFHEBIhyQFBtAQabIBlZAH2BAOM+cQfDMMlrABXouB6iFS" +
+            "7gG/gFUnIrUHqJCPdIk5MfABmqMoHyBZywZAByVACckZP6yD1DlaXNd1oKVLQdhpFykYgnGR" +
+            "ctEqx/MqsUUXvtMXkxuefcEHvhILKrmrgaGSijsrdAFM/sYUucuzxbFAGLZRXM7TGGzsGKML" +
+            "GqqLGd1TAVlBGf5kx+SBP/riT9KxQNwBUNwBQCMkHL27rwo1WEmZHlX5jhqAEVaZElMAif3F" +
+            "vOXxjgLCfRlFIBCFEdIBQqyzgaiQUYA1wS+ADRFwDOWAB39wAz/QBL9gCzXQRpTgBSzglw/b" +
+            "HwOyYu9LRTeymM3RyRAADTvwB5dgC57QB8YYBMYoBedQDgPCDkPABAOgf0HABdAwfxyAEGkw" +
+            "AGUlZSxgAGFQDrtoAhlLV0SiEPvAD4w0Re7xjqiwUPYIWPBsHpUEZ0qXKEOAnH/wB3ZQZ/8U" +
+            "nakkSlnRguuyC11RKtf5FTPo/pBFHCto8Rbg6Z3emUsUHSt/Uau2MgYPADz6CTyRZp+4Ahe4" +
+            "Omloh572qSvtmSu18mnDMmu3lhvdcj3EEhkK2qARai5XwDrIdmw9zU/xskD8JF61azAL1DEE" +
+            "ozTt2jQyhgUaQyO7MDL6gqjtUQI0AjYZpHooWjGJ4w3eMQsVOgtdsANRAwY7QAR5sAzYkAZL" +
+            "sAgmFwkSQAW1QKSRQAQvsGL+UQIwatWcEENYkDFY+h3QEAGLICVzoya2cAYDYAuEcAtEUAYj" +
+            "4EcV9zZkIAlNSgSjIAWW8AtmUAsx1wRmIAvLIAfwsANDQAVwlgCAGtrLIAmCGG30ci+QSkKj" +
+            "s6BB/icV51t0PiMBEqB0TYdZVjcdwoEu8nJ1lpEqOeiQbpEqaqFoTrwW30lbqyVLjCYrtPIq" +
+            "Y0xbiZaRjzZ2vBQ8141LcqEXu6SRWlgXW3yF6tOFRwgb49Q+z9A8p+Fb4oMZ9vMb0EE/VYEG" +
+            "69IpySHI4wYeDzRC2yGP4NEfCwq2K0YjS8NI7fFiJGWIHNQfHHQ4D1UGyTFmVaF60lEH/LEd" +
+            "/gpChOUgnuJCpeSHDaUD2MACfzACMMAB6KANP0sCjUASXhADJQcJlgAOJvAC+VEj70tfuzh+" +
+            "CQUBwfBQ2SCy02AItuBFTYADl9AEBuAHDvEO+cEMhqAEQKIQYeC1ZSAH/owgEWYwJQpBSFKw" +
+            "D+zwBUSwDlxAAnGNAlTQCS21iR4VHpIiymPLIOnxAjVQExsmAJBAw8qHgtoA0CqYu3g7HP0E" +
+            "UHqrqoW2t2PBFrAjS4UrkbG0FmrhOxjJaLYEhBJtO9pdF4rrO2oHnrbkxG1RhMTkuFVchNNl" +
+            "kofBGNA108zVGTppG42nk0H5hjzMlP7zT5LyT+hhtiA1WKJkHg9yyHYOtijsHwQGH53CH4MV" +
+            "WOfBA47csLqHQh3zV/A4WA9yZmXwiO9YMP0BoxHQpTZFCexgAuxQDnlADUHQBChyBg3QBEn7" +
+            "BySQDdiAB8KcH9kgUil7iyi0shkFASfUDuTQ/gLUXKY+BmR1FQZVkEVIQAItQCSekA3L8FDl" +
+            "YAIN8RJKMIyxmQYbsA91gAdx0ASjSAUt0ASFI0kvLEkpjCMFA272sgNegDkYMhR2AAk8IQMx" +
+            "0HRXF3W0PQXmYqqegao6aFpmAWiyY1oTmUvMvZ1eYcSM9tCusjvZ3RZiDN2qMlvTYktuIUwj" +
+            "PTx0UIR3oTy3sU208QxoTBtFaBuvAT9xDxreExpB+RjPsRwEmXl65l3YIUokhDARVDAPtFDU" +
+            "cWDoBUKMiB/jIVJPaY8qgWYbnpsUgwr3JXvz4cgQEENlML8qiEEJsiCCCJbVDr87EAHk8Ac0" +
+            "EOUC8AIcAAGqbGQk/tAEA8AFN14MSkC+IxB9uRi/64FBZ+ZIXSmPD8sBVLDYBhASIEcFBsAC" +
+            "tHALIMQMYhUlTeAMHPwEXbUITMACcjNIYEUCbdDulRABlvBjsYkC47AMgXWPgDXnn+J5pARY" +
+            "e0AEgnBJjbITPOEFT9EIkvUcz/EdhQ8QPHh4QJMkCY9dSUx16eDCRQdYsDpMfOgi4hgXqVLx" +
+            "6aDxIUY+fBxutOjQRciQG/loTFVgo8uWY8akkimTzxg+BUQV4ClTVCiNYwoIlcnTqFCjD5I+" +
+            "UIqrwIMrBaIWCKVUqdRQV65kzarVq1ZTaLp4AIvmiilTZ3ft2rPLVBK3bk1NWbtnyiy6/nbR" +
+            "7pqSp+0ewIH9ss2Td0oJtlPQtS2BbMphLFPm7iuzJw+EPKYa59m1GXCZwSUK70KXufOUtn7R" +
+            "9tVBxJKlITFspTFEBEaZZBxs2dJiS8qSGFLM/PDUhsgOZCW8aSjTuQTdEs2jT3mCbDGRIbZu" +
+            "vPrd54wlKYTI8NtXgohvS2fCuPnyHBkRclJs/eCShhKVNLL0OSOyhwipUqiwRBYS3Fjmscf4" +
+            "2sUbhPKyizrA+Nohk0VisEOAGGiwIwYBPvBCgGmSMYWHERN6y5Q9ruDhCrOSGKsChiDC6CFY" +
+            "Kqrxo4poGgMWjFJ5SKSPUploRiFrGmommYLSsSeWkpwppptu/hJKlJl4EkWUkER5IBRR6AgF" +
+            "lwpC4fInMcv8MpQKrqjgmQraZLNNNbto04M2eXBRzrTsJJGHv/YwCDAH99AAsDz8SmKFQvd4" +
+            "Ap0VCP3iCbtAswwLzJ74Apoypljh0T1WwPSJYyBYYQoNkEFUgzoezeOLL1BBZdMnnti0sFX9" +
+            "muIYDXjQAIsv7NLA1joye4IIIhj5QwkSvKCilCZGwIadL2YJ54wlaDCAEC3S+MU+M8LAxoRA" +
+            "VtDgizJQKQOaY/JgADMIoMnjGEyJ0IeQFgYIgoo+CGHBjG1K4WeEY0yQJQ0SpKhnmTJwTZgD" +
+            "/DyhgguHbbEEBWhQOYaVdZpogQUW/q4pZwVE84g1CQ0gzaPkwAQlFZU9diAFEgEEgEQLLWig" +
+            "AZIaILFjmlnc+lPXKQxCSCCDdkFjFw8aeqghiExyaCIhXdhR6Q529NEkicaYSGkhO9LxJp18" +
+            "ojKoK8fYicor+aCjpVRCQZIPLG8ik8tYQnGCyzOvuPuZL99sU0xRtgqF7y6uYFPOMMfioQs5" +
+            "PWDcIMc9QHEPD/hUyIMkIBTUL0KfSELWRqco4wvOQfOr0JJXLeOJVZ+grgzQ+iJd9kc1OKZR" +
+            "izVAZdwSUHkCC1xRgSDdJL4QNYk8xCU52MzLWIGHW6fY4YVwLGlkXxoaSUOJP5iJAItK0MFH" +
+            "iSVQOMOA/iaUGKCPJsxo5QUNAkkeGtZ7CSYJaLJZ1d0VRDXBECWYLwgcI0QpamEGJWTjGF/A" +
+            "xjrMkIAm3OJAt8vDPkwACS6wgAQc22ATqGAP0eSBYU2wRRO4IYlChSxWrDsZala4i8LY5Qk7" +
+            "EMSFbGgHCTTCDjuERDtmgYYVPcFECtnDiVykkMLtAiJVo8jWOvK0p3WEakzjkUkaAourjcQm" +
+            "PsKJRsq2kbTVZCOpiFsoctKSk6SESlwag5fqFgov2S0WTngG3/g2RzHhInC42IoT/hYmO8ai" +
+            "joyDEYy6gBaFIHJEdkKLByr3Fj8FTVGKipUGEOQ8SGlgUDxg3RNQgTxIxeoL/i5klaJWsDpz" +
+            "IUpTWBjU6FQFDQboDgIM6AsEgsEcVpXsC43aA6s8lzxTIooHq3rBC5bAgiZogQtvoAIJqHAG" +
+            "SLCCA9AoBx5sYYA/mEETJSTEAP5ggD4MYhDQABlmXiUuVK3uXb2EwBM44B0SqOENaWCCGkrB" +
+            "hT7kohxYYEU40mAGWZSjEojC1QoiIAMqUKEWTWgCCmzAsTZUEx1EcBgKZLGBcpThVycTGalc" +
+            "mCvU+CkJRUwCDQWAwxrUwAteWMRKa7CKEBHtLQKZAtEEsqcR7YEHEImIRJZmEaUBlSIVgVoU" +
+            "fcS0mszIISKpGpWMVJNQDCUnQgFKTX5CE6khhUpJ/ilALLhqFJ4wxatPMUrdnALWrXxFrWm9" +
+            "QhfYgha0gGUtXkELQtpiirXQpS4R8gZq8JogvvSML1PQBl8Kg6DHAKo0h3FML+k3hS8gozLo" +
+            "cBdpMEOaUZkiD+hojujyAhq8ziJCJSiiKYh1BDvYYhiQ+IF8tDCENEihFbYpgTaYIYXXmEEK" +
+            "stCOLZZghlHsYAedRQdfogPDEgSLXI2BBxEiQYg0sOANl7CFGW7QvvGYhwPp4cYyFluG5xAh" +
+            "DpSQhSViIIslDIESS4jEBQS1A2r8oQmRkEQyJJFYukzBGyNdS38lM1KE7EIH9PACJFqaBS84" +
+            "QgBbEAApvMCOZKgIaVdQ/ohkVrSLK1jYFE9AC9MmIhEQNzGKDqniQ466xB1hZGs8etJJkpok" +
+            "jRxJRz9BElFUkiSYSDVtOxmTmfJ4JbqJ6W5i2grhwjQ4P9KxjoVsUwUW8pa1fI5EIwUMn+Sy" +
+            "4ZBaxnWKElnq0BFKv0AKZHuwWK86ZeZaIQ8znYJGr/KABfppIBi3XBUr9zA6nY6OZePiHGZQ" +
+            "VCjoJaqXZZgeKIZRgz8g0wsJKMUfIJGGM7BAHpU4BjJcgQQlQMITZ6DCD8xghkbIghC1MQHp" +
+            "LqWB/rUzGO76QiCOcYxAHFQNfUBBH/zAhTP8ggrDwEQ5jlEFbFiCChtYxqtRrck8YGMJnvDE" +
+            "/hIs4Ykf/IAKS3jEBcrwgnw0YQlLWEY5npA61MUKMHCxy5+S8MmSZiLBQPBCDRxRgy3UYBGO" +
+            "2EJMBQLlou2CRIBBpGh5EMUOAIBpJF4iiG8kESd6eEg+PfFIHOIkLM1EFG07W9hgvBKcJIkP" +
+            "eDObmbyECzqIYsh0GHKZcJHHL4mCb2HCxTPSSjjENZlxM+9Cvg2ShMpBKmif85xlLGkXVbLw" +
+            "Mb/isGXKkLmTAQZkJmsUn3RnyXd1GVGsw0KjnlAHCPgOAhAo2QJFdrtP5qEOo1rB7Uhm9k79" +
+            "cgWsIMIPCNYIFLCg09trAg1ukIZ9zCJ55ciDDNJAgyac4QwkUEIf/lr7hnaYQB7zA002oKEB" +
+            "BjDgXVhYVTYiMK0zJIALSmgBFwyQhoTyAw9lMMEqqFAJeBTqZCwkgheG0MxlJ+AHKGjBDMpR" +
+            "h2MMogk/QMQySvYrQWlyCr4TGhEpbKKWTUIGjkjwFhyxiC2QQt5bgHBbjXgikqLoLIChMMIR" +
+            "vsSEN0TgCSfx0pRWo6txUSYZmUkapURjn9CEbFeaeE1QQka3caltcXyjG9/4xpNzgliom7q5" +
+            "I1wwqyVDk2eIOZdbwAoYiEVanLdIpA0zCBShiy6gpJH6NtdJHg3IHNQwCJZBN1n5JE2KpEHJ" +
+            "wET5nBXwnVAplxVggNUBFdLZlS/QpK17/oyqQx5ecZ11mQI02IM6CJp1QoUIOCgaUAIzIIFG" +
+            "wJdmkoI+oAFP6Id4+BhRKQc5kAJkCgEDGIJL8INlK4YYMAEOEBeQ+RXS2QNRuQz5WYF2GAES" +
+            "OIMboAIzMAAukII0yI9KcB4TqIEZkIRyQJV0eRUNYAUwIIGHQoEfaAISoIE/+INZwAN0eAFE" +
+            "a4VZcJ1BWSFFobKboik+0RM6oAcEo7caIAUgcAQg2IJ7AIJpUIXFOZouIJENw6vACLQ9qIAk" +
+            "+D6eWiLzcyKhaiIRq4isygj1C4kaEwqKc6qo2gm4KZuh0BGagImdSIUHkAk6ACuwugqx8iqn" +
+            "iCps3AqjAMct/uEJregKXHgAOaEruMIrDFuNtYCrtcALtwCUvforuUiMwdIvvuA3u5jHO4OO" +
+            "yFCM9uiMgbyUEkCeMigudHiUzqgMzZCEIvIGSMEwu7iCwNKBHRgBS1iE1BqGRhgCKZACAZAF" +
+            "KaCCUZAEZ5CEXUCGQSkHZoCNGzgD1boBS9CCaasNIlAMLMgUTzmM55iCHuSHCKCC2aBJKbiB" +
+            "2XiDWoCGW5CEHSAGe5AEdHiMgXyMLsAGWRiC1xgCO4iEHRoGbfCAKSiWSCgDN6gLvdIrtpCL" +
+            "FVkRROILHaCQexCBLMiCRRCELdgCIfAHH2gHVRALGFERi+Q+1bjAtNiDFQu4gvs+/p9STA8D" +
+            "qhrhRfPrEZbIiJtgCfmDMYlzqqOAm6JAEp7IiY1bxrkBHFGIBfojE8ERpL65GzAZHDT5mwRc" +
+            "MjhZiAWksrWAlJRRiMwZN1SIIS0DjEpyocJgodNBNc9RJ+RJwzKAgFY5F1a6jFaxFK5Tw3bq" +
+            "FK6DLE7pnzzQlZ1Ehb5YgW9bFUUhtBf4ASVoAiBYAtCrAU8wgDOwATMgBHGQhK37wDZ7gn1Q" +
+            "BEZ8AyWwgVIwg4Q6gzRoBRgYAecJl3PJhjqABsz4gmzAgjpgBgIhBF1Tn1KQggGgAm4YqBfI" +
+            "BU4oNpGJukJxBSJ4RCmMgT9gL0hohEcQLSLggCFYhmRQ/hQ+CSndDAyfQZHMOQgeGLB7yAJ8" +
+            "WAQLsABUzAJVhIJ2EJFDUogroCRC6c5dkBVTqIAn+LAP877xcyKDC7ErGiqEi5qOqAn2k7iV" +
+            "aAmL40xpzAksUUa4URssoYNmhKNrDLky+YnSHEDX9DFRSBMiQ5MyYZxn8CNDYhwSqblYeYvH" +
+            "4BORAsGRGqnEsoxR2ZwYQhTAMB4OhIewK5Rv0yRIIZ08eJUXbE6ukzP5URdauhVcsYxgyJQG" +
+            "badQGaUyMBXLgICjI4IIEIQYYAHQE4CC2b35pAIb6AQJisFe2bovKAd2GIID4AI1CALCU4P0" +
+            "kdY3RAUIxQwGCAZLYQBEMZ5j/uAAfKACFuACKvAEJTgAFjAAKdADdIiMfeg2VKsD5vAkT9oB" +
+            "hPqBP4ANG/iBGPgBasCLWXgBYvCGWbAcQZGhzHEduEAID7gyCtOBIyCFLRhSByMFR5DYRYCC" +
+            "6jvMtLiCzHCyL3ALjz3IJ+2pnko4LQ0x7/PFkqgaLbKJy7QJJBkbL6qSMjIjsDEbPBWjJOES" +
+            "ncgSMakbOrBTHysTvskKJ6CDAwRUJ3ACNTGcNUmTK3CkNlkkghi3t4CUgxA3gSgZOylBPikU" +
+            "1ECdvlgdo+tOUvk26igZzHGekeJOWfmCY3iC5qQ8T5HBVQmEknFQVIOAQKjVyLAU8RRCQzkZ" +
+            "VsA2/lsYgiWgQ0pAzwGouzMYAFkogVs4Bk1hAFllAP2cBlmIgUsgBBqI3CXApyUQAA6IgOLB" +
+            "AiFEl1V5lC+oghXYASRIg/RJA0uwhTPgAkrgAn1Yhk0pB8rIA7cFPkHBNm37ARL4AUgAjiGI" +
+            "BG3YgSeogEdIBjtpVINAjcXxAA47CB/Vt7CA2Cygt0UAghqQAXzAB0iwACiQh4LNwCdgnETt" +
+            "AvLkpBusADTQAADoKYE72YjIX5MNv/G7kaBqP6eRmjHaCZdoRpeYxpmtv5UYirLBmwLICZdw" +
+            "m6Egq7ACqzG4iq+yCqOIqqjYkrR6CqioCjZBAz4CC62Yq7pIi7dCkQWJ/gu7+Iu7AEHOmIvB" +
+            "GKy1KJS1uC9AyZTEKAHS2gNkCNkSgAZk2IVzqYx9gAbSSkj9mgLHGFvO2IPFwKvnGCzhWoUm" +
+            "2MghKAVbsAOQtIVGwN1cUIBbyJQ94IQSeAIhBgx+4IAauF1K8AIzCONasIU/GAUYIIJVKQ1e" +
+            "4awS2MlzmQIYwMNSOIM+8I0z+APfkAQTmEpLQY1H6Yv8kh7YiAFIGIJVaN5FWIVjeNJdSAYR" +
+            "2QW3eke8SmUKO4uzGJGpdQsdOIWWojdT/ORFiAR8kIEl1TdFsUjUQEfNsshdGKUiOoYaCWBd" +
+            "jIgsVVnyQ7ge4ZH085EmoYkzGhuYcJurcgGe/jAjBAZNMsKJqZq4n03NKxla1DxNwBGyORK5" +
+            "NYmFLojNrXjnBRwLxjmLmcOcvPoTnWIL3WQd4RQ0yxDOYFGU0kBD0IgVRuGcuV3OURqXY1AU" +
+            "BjiG32yXQmEAcsoDvoWsdBHCXdoDeDmdG+SBc6mdMXuBgxIAGkgAEhCAJuACR8umJmiEa5iH" +
+            "ZXgCcwqVx1igRbmFcGiA7ZE2JUiAPzgDQuDQEWCFY9AfjOZWBmjDQHgPT7gBSjCDM2ifqvYE" +
+            "M6iCctgl04Gd1VEM6pioGiCBGBiCH2iERoAEDuGZwMArhLi5tqAynTKFynGkXagAxxmLWMaH" +
+            "vIy3doMESFiFTKiB/g0QkV7i5S+THOBji0YxiCfY0i5d5sXcXwAwv4zYmoYLipnAuIyLqpYQ" +
+            "5/wTigfwCJq4EprAZpdwiTZqIzbikrUhkzklQD0NBTwSnDNpQPt1QMSh2lh8pAnUk+p9ENQg" +
+            "FTJTlL4QGS5zIVgxzrjNg5EiHUjRqHMLFUX5FdQIllHBaAgQFF7JHLk9mdvRqDLIBtdlUDX8" +
+            "2ylgAMpDhaqLXVA4A0+IAcJbAgzigiaIgWVZAiyo3F66nSdYF0vypX2SgRiItB+4ATNoAEX2" +
+            "hFd4BzzIg2BYAayLTue8jGMgglVAlnTNoPNJgBbAqFpRPeEVmal7ghfgBUZshCb4g0aI/oFG" +
+            "EABiSIY/aVgXGREgsivMQYO0SIu8nrBdcAFGEAQLkIFVsINVeJkhkAFS+AN7UIUrYJ3DRI22" +
+            "6hRAWQHty4PIloj97dKTzd9d/DBhvGwyxYms0gigYJuNozE0V4meIJMuYe0uMSM5hZuirfM3" +
+            "+jEhqwBcODlAGpwFPLlnoJM6cZw2sUA7EZoN28RO+hxgolt5HVuJ1pQWVJRHie7fQ7WgqSSe" +
+            "WwGkE09d+YI6qNUbTJ7hsR29RYV12Z0fVJdWebM64JP+ofRjeIF1uAYyIAZiIINroIZR6IRC" +
+            "oAZEoAU96LZg4LqmBs/hEdxYqYJKUARbJ4ZCKIQ2uAZaQARq/ggDflAuHOS6FYiMy2CGXsAE" +
+            "aS8ETNAHMigEXbeHC+gfbt2lXwmdwtAdLBgFRBiFUSAGRaAGYkCCgZWpQ/eT7B0IHSckOXkG" +
+            "wDwkHtiBaZgBJOB3PTAEQxgFPWAEQ2iDXJDx5CEiv/AAyzANzerOd/Ffk+3ykd9yZW6an5Ia" +
+            "YlwqHRmSzRSTMQAKnVhg+uNmnTAjMsKSmb/5p6gKaiQKbGQKpMBgDI6qqugKqaAKPpIKrSic" +
+            "uGplsEALsxCiImILwYKh0rJku5BHb+ALb7CL+0KLGW7Iuiiux5CE672LtpgFcqEd0eGEL/Du" +
+            "H9qDEiiuXViBEvgzxOh4uzgu0lgG/gVQAElwAwVwA0mQhMJfBjcw/MHXBtCShISBIck4DLp4" +
+            "An4g5cFn/GU4/M33LhT6AnRAB8nC6H14jErY/HhwhmW4haZ0Btfnh6YU3sOYyjsDlL5ahllQ" +
+            "AFImfN0nfMLHq0SaK1buWDTwAFwgCLJwucKRnLUg5WRQABpVBehXAOnHfVXoBtWY0sz4Kytv" +
+            "i+0tWfCLCBfwX56CBS/3UioaqiDRIvXLOJ6tYJZwqhd7YKLYkqHtkqHQP2aM85CjgwcgWoB4" +
+            "FmpgqCu4nAy88gyXqCuhngnE9cxhhQpdKlzp0sWUBw9oeOzZkyTkyCt7nkzZM2VKmScaypTJ" +
+            "EzKPzCR5/lY8SaLhWJ0pqI7l0TAlT0yUK1aU0ZAHwrEkT1Z8cckAGioN0CBo0PAlapIVx57w" +
+            "KPMlD5oygZ48yXPsy0oIDPbkyfZl1wpoK5RufYIOGTq46J7sQcd2ytiuwTQ8gRB1ClO4W6eg" +
+            "WzGlLjp0gb7sgYbsybFgqOpkO3YsFzS1EG5C+1Im9RdoqSEgy7MPy4o8X1Z8pklzSk6nKTXs" +
+            "mRVy+EiRxnkk4YGcRxceHio+r/isAkSLIk0BNrVnhUjbe7rsmdt8aBLtJZ50sbnHlM08KUcC" +
+            "6AALlnwALmDFp9+hvnz98ve70J8LAXYwYCoupJJggi7wMcYYBTT44BgEiVLA/oMFFEBQAalk" +
+            "GEqFoozBhyiihJJKKAWIQseEfNDBooipkIhQLANV4IQTouCCy0B0hIJQQRAd9EwXV1RkkZFd" +
+            "eNAcSFMkkURKe+ySB0polQFUWnXIxBlueyj1JFR5VIUUWrepdJSTXzQ1BRZUJVFlGXuIhQVg" +
+            "x2AxUmcaoJLYCjZBI+cXgRzDGDRZpTZUalIqhudaaK0AwVC2DbVCCT8do0FdjuYRzJsr9PKE" +
+            "WTSdZhVQtIGaFU+NYkHTWjfx9EUwNzGAJporYAEBVDHVltZdgIUEmAY85LkHKk0q12RySaTH" +
+            "gymmMKcRkRldES1GQ0K57FwV7AHPFcuikxFcy5qS/scuQ5pSggbYeVOeKVc8sctM/c1HX7zz" +
+            "AjBvB/X6BwuBBd7n4Bip/OvCv//yoaAoDVoY4r8KNvhAiHSM6CHEBPEIMcR8hMIjxhh7SNCM" +
+            "B8VC4kNOLNSjQj2GUiPJFXgAEZIvI2dKc74xm9UePFgKHFySgQRUSse0NIWlaGlgq0oxhZRU" +
+            "r0qhRVNWZTDFQx1YPQUBFko1KlmjTgZzFipXoZIHbTxw9sVItvJwE3cuaaBTHWXYFNV2dv3E" +
+            "QFqK7UKnrjghdVJQacm0ZlolfFFVVKvVpphYDCAlqW2xHsNAMCsEgwytW0WVRx1jOY0YSiPt" +
+            "AlISu0zBLHHrqascRxol/lmjRhYpq92ypW8rpXZoKQsX6lIii0pIu6DhElwyhYTzfPjih3y8" +
+            "+cX7n370DVjggQGOMWD1DyboL8AjYggwxgc7yAeKBE1Y4YkoxoIhiBiyL+GDDxQAf/zs019A" +
+            "tBnab/+JD8gf7TMPVAAX0crIuqJlitKtZz27MAXphiMcKK0ESip50kpSMpThqGQXUUKHN6JU" +
+            "himQ7gt9gUxtiFICC+ZiHyrZx1zQURpTSAImpFsBOki3jxN6ai4sEVcMS3ASHx5QXFESV3jc" +
+            "Q5gpeAMLc5mFEWeBRJuAcAo+JMx6YhITdJywBJIpQWzywIAP3mYKDGCAbTgBE2gwoASp0UAJ" +
+            "/sowizJMMQ9O0mAHF7iL0ZniIwy8AvDQILM+omFIaLgCGioCrQoEMiPNiVJG5BiKIHYDSnm4" +
+            "3R4GSMQo8WaB2jFJuFJiknnhZz/LuxcAkCeffQGIXx34l/QE5i9X8iFCBCtYwkJEou89iA7s" +
+            "I1Es6KDLMaTIfBAjEf5CIaKIeWhGTiDS6xKCixoZkjrUqQ6RQoGkioDHOMNBA5f6EpIL+kQ3" +
+            "TftmWhwjGcfMBB00KUMdfKbO2oRnLBrAQhWkFCtQQSMmtQKOWuoQFjJt5S5fCwRjejGWNVql" +
+            "NFPYnEzKcJdHhSRVWsGNbQDzBaxMhjt5QEZLFtUamlxlKaf5wtXq/gA5CEDgCcigTWdaVbVj" +
+            "0AoCMFEVUbA0FM/xKiQMXCCxZKaRQG5kIx5QlpE6kqSLgGRZOQEPetKTB+ZESYFXkKMHSMJT" +
+            "XqFkOLtomktK6dWvgjV5YB3rWPEjVrLWK6z0Oh4sdFFKXfgHAHDtgFvpKte2tvWucNWFC3Sh" +
+            "Cz7AQgd07QAf/KoLHfyVr4mNZWIPW1hAPBYQkJXsDipbWUDs4AVE2CwrWEGEF7DiBaD97A48" +
+            "SwTTvqCyEYgAaE1AhNKywgQm6CwMTABaPOBhtthgxmexEYHTEiEC7ADEC5jBDNsyYwTYIC4H" +
+            "ZBsBYcCACDAYwWeNu9kRcOAFJuAAB1gR/gETRIC43/WsCbChXWZEgBW5jS4zhvsC6u4ADxwA" +
+            "BCs4UFoOnBYb0cWGeWUr2hfg9wX7Xa1rYaDZ4wbXuTDAxg4Q3NzyEgEbsyXChFNb2h1g1sIY" +
+            "vqxlJethyfIBs3zQAWBHvAMSV9YFleVDByqrAxWn2LI6OPEOUlFjDLNCB05gkIpdkOMd0OHG" +
+            "Mz6ELg5hZCIfuchFNvKSD2GOJ5tDF1CGshWsYAxjUIACOciBL7a8ZV9gecvWGEQO1JGDAOQg" +
+            "Hb6wRjqs4eZPBKAaKvhENO6wiU9sQgV6nkM05jBndXziE3MWgwoIsAkCIPrQhyYAHEAAgjWA" +
+            "4BSMgDQIEICA/ihEAQE+cDQBLA2CTG8hC/g4RSbuIQIH3EMAkBCAI7zwhyV8QAA/oEIkVkGD" +
+            "JgzjBzUgARX+EIMlpKEUMRBAGpRAAkh4ohR/gEQMmkCCVXiBCqWIRCNY4Ik//KAWTKjFD2KQ" +
+            "gCAk4ANKIAQXSMACHJzhB54gxBuC3YdLlIIEBsAAFZbAbhuw4BdmGAIV1GCGJXiiGMr2xC9Y" +
+            "0IgmcKEUNWiCEqjQiGAv4QeEGAAJpKAGJVBiAGaIgRKKQQUtSIEEB6/3KpIdiSaYoQl/aIES" +
+            "LGGLIFCCBKU4gycO0AcuNEEKUrCFFAyQhjTcQAksuDkVgs6FcZeiFEpYQhNY8Acq/vzgB9im" +
+            "QSO8EAMgNKIRq6iBqh0BhK9/vQZbwAfZ8VEDsjsiCwhIuxdWgQ9I/AEKjaDCFugBCUtkAQiR" +
+            "gEQmjgAEAUABAYpYwh9kYPVIOAIKZseHDJj9gyV4QRCLEETjIxGDP9iB8Ve2gjkW4PnOLwD0" +
+            "njdG543xeStbmQJWoIAvWu96a7R+y4NYMzbcvGZr3KH1sLcGoHkfaGsE+g6fEP7wNyEGaczh" +
+            "E+mQhhjsfAdpBEAam7jDCZhffeZ/4gTVF8MJwAAG7bMBDNzvPhjYEH4j0IMe3qcHFOhxBFLA" +
+            "P/1bgD8pFiGDVi+iBquwwyIisYQh5N8Q/AEA2oElxIAd/tydLQyBAAyBJViCHQxBA9oB/y2B" +
+            "JexfAw4BBNqCJXgBz3nCEHiCztWbJaTBvqXBG9xADJDAJdzAH5zgDQyBLRCCGdjBw3GBALLA" +
+            "GQyDFPwCDALdKyCCLaSBFESCLdzAMNiBzi2BHdiCAi6BLWiBJUjBDzTgGciCHUibI8TAMETg" +
+            "E8qCy6VBJMgCFcgCsO1bKcCgLNyAFAzBGZwBCaTBKzzhG0jbGTSBFNbCE5ogFzigJfyfJYjh" +
+            "BGZg/nnBItjfB/CfI9SAIS6CBLRaFjBiFpDC/JGCIMCfIf4BKdQAApKC/yEAPSxBJEABFHjB" +
+            "EAABPXjBItIDJI4iKf4BPmRB/irKgAhM4iLEgBdAgiHC3xFsXSTUACRc2QIE4wIcgud9XugJ" +
+            "o+hZgTAag5Vd2ZW5nu4NgjW8QCLkgDEkApjtnpup2TQGQKABWqApnwqkwydIQ6DNwSZEgzVI" +
+            "QzTcWTqogzrSmQrwAp7NgRjwAhuowCaYHyOwAQiY3ylEwSl8GgjAwUEe5CfCgRFkQiZkgSDc" +
+            "gyA4QKltgREIArRlwRZoYSMIgNWtWiNEwh9QXQwMwRIMmwBU4BBYHRUMAUjaAgl4gQ38gNM1" +
+            "Ar/ZACQsAQnkJMBRwTCQAAmYASFYgrz1wQ/QQB8QQhP8wAD0gcGVArnRwBkQghLQgBRM5RJQ" +
+            "wRsM/oAWcAEw9EEMqMElkEATvIEaNIEtXMIb6KQZ2CEVGMANOJsSKMFM+hzDmQEc3twPcMG+" +
+            "NUExKEEMcEEKUgEh6OQNQF3DuWAaOFsavGQJzqSxNYEBcIEN1IJiUoHTSRsV/NwwDAMLLEHE" +
+            "UWHWxQANCIAdeAEC2oEjmKYXNIIidqQXCIEdAEHeLYIjiMAW3MMWpCI+/IEECEIkDEMNeIEn" +
+            "xAAcNF4NWFoMyAAjMAIUCMA94IMF0EAmAoHYQcEWkGIMrJoX3AMcAIH9QcKqyYARQIEteoEw" +
+            "GqPpGaMxVtkCLKN7CqOVMSM0GkM0+kI2JsLu5YCbWcOXlRnuudkdfOPv/gHfJ6hDANiZ7xEf" +
+            "nuHZnanACfDjJvBjFPTAGrDBGqwBAUTDhTKCo7HBKZQfI5zCQZ4CQZaoEZwCFJACFBgBKeTm" +
+            "PUCBBdSAI9zDIngBEIiA1Q2BDcRmDPzAB3zAr9FADIwmSXqBAPwBCVDdH/xBEzBgEywBDWjB" +
+            "rZFADfwaCThCI1jCXEKCAaTcH/SBASyBADRdGmClXP4kE9wACkyAGQRBC9iAGVyCAfzAGWgC" +
+            "naqBGiwBF1yCGcyaG9IAFZyBsB3AGXBBDARbAkTC0LFAVfaBszHlEsgplF7CX3rCGcSADZwB" +
+            "CkACC5gBDfxAC5DAB3BByJEAF0SdGXABqKbB/qqewVw2QQL8QcKxQNN5Zgvc4BtagqgugSxo" +
+            "56/GwCZWnSIeqRd4gWqmIhA4whZsgWzeaBZYwCpsARDgwz1YHSmsQiScAilQwSJkwiKQgAzA" +
+            "gSA0AikcwRE0ghbgAz0sgh3UHbsCwW+uQgwsQvutKz6sAiRMID58ogwIQttFnrtuQeeZQzEa" +
+            "LDEemZERY8Ee7HoKI+thWZZ12X3upzVc2e7p3prtp+ulw35+gjUEwJuZ2Sf4gjoAmjvuIy+o" +
+            "4ybk4wmwAQHkYz4SgMvyAhgQABvAgfmZHwJkgoWyAQJ8Ghgw5IiWWqlNQgoYq9gdaf7JKA14" +
+            "XQ0MQQxo4dMdYLOh/gCrAZwNcJ1SGisNOF0NNICyeYEWeIInfEANoEATaIEAkIAtNMHBKQEf" +
+            "/oEtcIGv1cIAcAGZnoEZWJsB+MEBWMIbFEPZ+sEA5NwAtCAV/IIULAETEEIa0EAaGEApVJsB" +
+            "tMAQFBsJ/IAZ3EAL/EDcUkECNOrnlsISJAATUB0LcMEHkMABkABKssDlJUALHBwKLEG1NcEQ" +
+            "qC6TBlsD/IBiSiqUnoEU/IEUpAEJ4pwQLoESOBsJaEG3sVojxJoABOsqZF3WOYIMdKQFiAAs" +
+            "et3fhZ0MLEJ0fmfjyUAmLAEkQEEDsAA+kKIs1IAD2KIggAEckJ0RmF14ykANWMAo3oMR/iyb" +
+            "cOLm2Cme5QXrKgKBbrJtDGyB51nBISyj6UUwMqYn6annM2bZ7HHZC3TZ7MGeMRQofwJfxr7Z" +
+            "xwZayXqsCY9wOX4s8KmDNMCwOUqo9pmjNGgf9zGCNPDCg3ao+Hmfy55AFBwBHJxABqhfBjAC" +
+            "PWyBP2SCEi/CipanDMAfMVjp2G0BRpaictqB5XlBFpQkAGbx/hXgIE6gHVwgAPZfBmZBEVrC" +
+            "IgxBKRjgKtgCFVjCEMiCFMCgLaBgEQLDDUDCDbzBGcTg3sYACvQBzUXmLwDbJZxBJEjBJSig" +
+            "FLwBF6bBAFCBHl+CZupcDEqBAw6DLTChLZQhHDfgHDMhG2Lu/hUOQynYgSwQwhKsgmEWIPH+" +
+            "wTCgwBDcgAKmASXEQL1FoCWMIQtowRcjXiOYsW4yIv/pprF6wQfUIj7QnyFuwRMvQgpMc/3Z" +
+            "gSD8n+SlweQhAhX4Q3n+QC2a5DR7wbkqsSBQXv8twSLMXxNbADGcZjvLwBFkAik0gvia5hGM" +
+            "oheE5AXDp8NWcHwS9DOaXn3aZyKQGez5wiBMoy+YsJqJsIGKMESnw0X/noEqHzi248pGgzqo" +
+            "QDwa34bmI4aa3wkQgPd92hGAQCYMJD0YAQKM6LhmwohCQQKP4owyKyx2sQ/YqCOQwv4a8xaE" +
+            "55gy4GdSbxNQQQ1qAQogXgyUAq05/sIffGAk8NofCEAjKIEnkKZSH5sWmIES2ABZGgAk9OUZ" +
+            "UAEXEAK5LcEZGMAUBsEbiCkT4IAflIIZaIJa5inTqUEQ2EC/ccHy+ltba4KfGoAa2O4lpIEn" +
+            "8GkakEAQEIInsIABpJsZGEDT9cHGKUEfUAEVDIAU0ICq/gDDRRwXFMFMmiklePUBMG+gyiQL" +
+            "kGEC/CQKhO5Yd9sPSIAjFuuRCkBv16CxspqyeoEMJLBwe4EP6Kb+/YFwLvYWhO391aoMTAIN" +
+            "+GIm4EMDVOsWxADi4WIW3MP5hpp2s5q0kucVt2R1vqIg1EB1rgIQXOcSCnR8S/BBXzCWgVl9" +
+            "ahmX2V7F/ubAJyRCAOTnfvNnC39C8pGsm4lZC7tZyAYaCwfaPp6ACc8ZntEjL7ijnaE0LzDC" +
+            "ESAAHEQBHLDfEaSfp31iJsBBJty0FzAkPuAmFGSB4b34FrAactuasn6A5UGCFwwpSTYC4VEd" +
+            "bd8uDfDaD3iBud3urJFA1NJteA4hqKoq1e1tklNBx9HxABhuEwRBH4QcIbjDK7+BJpxBC/iB" +
+            "WUrBACwd0Gm2AVBBA5jBozKcAbBADhqboTq2EtiCXqp545qBy9nhrIbhqSZAtTk2sDUBqDrb" +
+            "TCpl09WCZlIBw9nCEkT1TJKAavcqlH6mThIekXZkbx/rBCqrswrAIgCBDwhB/hcz6z3AqHXb" +
+            "ARSY5D0AmwzIwBBAgiAgbQOQgtVBXil6ggxsgXc253VGYP41pOTRQ2nSQCrCQbRK4hdGHimA" +
+            "QdtBwhaQQLAeGcImrOcVI8JiOwWPHpgJ43233uytmS+o2bjfnu2tWblDNPCxsDpYNH+abIEK" +
+            "moHvo6CBtEY36EfzwikY2iaAAD18+CnI9IcjwCSwKMCX2kIKQgL/r4xvgXW3pqhptxcwqyMc" +
+            "ew30uNNGGxU4rdf+QSqKbgZSAguInBY4m3BygcMJAAu8pAQsAcvLGhe8YWiH3FFaueqqgR+Y" +
+            "Gw7gAAkoATBQXBrk/GT7gR0ywRtQpQFI5hLcAAv+/sEvFIMUcME2XALD+YGxEYIadPYBmGll" +
+            "Nh2cm5uZ/kEpxK7M37mn/iQfzhoXmHXISSoLEFtgc4FsU4FOosCrxQAvN2kTeEKjDwENUN2w" +
+            "Zmlrcnqop50FuGgWKH4WQMGJZwIQLIGMDkO0U3UmOAJXEzWRhppwCsI0m7GMfi89lN1pWt0P" +
+            "4MOK46KOe4L6QqcjUKEFZAGRxiLh2YExih56oueVlR5Cux7vt57vZ6MvYIM13kF/c2M5+p7v" +
+            "ESjvRcPvlWybmRnsGWh+3gGbKd8cXDQ7xnANn+ML27A0nDQbdB8cgKgRpx8j1K/7gQH7kYIR" +
+            "RIE/4MMTnytQj2L+qXO7/kbeFrQk/+GiSkoAIAKEJTtDhg2pMXBJpEV2LFla+EegF0tpBP6R" +
+            "YmnVkDQ3LGmx9OaGHSlq0ti5cYnisBs3CALrY+nXpTMkmGw7IwvYpSVNXt34YQvYmyVS3F0k" +
+            "dKOJFJRDXr2xZekVoWFpCJ2hwoWjRilDbAm1VKpkmjeyhvyyVMOWrVVULC1Z8vQHFVtD2jb8" +
+            "MWRIpB9LSLCNMaSRHQFDlgiwE3hRFhlbsiy6B8XRvS2kFlUmhY8eHDD0BNFbZefIqiUyFg35" +
+            "AWULXlIZLeHzsurPqho1snA+IgP2okZQvEDaQq/GouBbFsVYBWURjbyLoOD7IzzGklVb4ACp" +
+            "/rFkQXZjxrJnt+KduzEr4bf7MmZ++3lfvqz5omDMWgD2FHx9Ym9N3SdrnxJZs5bjk0/u2E+a" +
+            "T9I5cBMB8zswwBzSCVCdTVTAL8AEI8xvkztUYGMTMU7ghQBeTtgEBDZOiYINODJB4IgVM4EC" +
+            "DiigyCSTLbbwwgt8ssjRRlIcEUQyEQSYzAIvgNjCEUiWiMGLGEjQwotGlrBkiBgEIOEHAQTw" +
+            "ZIlGGomEBBqi7Eu5Pw5g4YMfDFCikWGKUaIBKi4xgAQU0jCjCRL6IIQLGwwARokm1NhmgFpw" +
+            "0MQMKvz4hYQbzJBiCTOUUOIPQtSohSo8zegDqTSkSOMqq9I4gwsU/sw4o4k0lOBiiRtm4kIJ" +
+            "Kn6QlAQuzGjhjyZYWBJLSDxJYwkqKPWklGG40BUFt2ohgS8U8vyBhmh/sMSGKgVwxBEvHBEA" +
+            "R2wFWMRI6/BxAAwEQLjHiy2gWCJdt/CJgQUaePMEkkyAkMUCKIwAopEaMqJhCUcYqxESfGjw" +
+            "ZIjFYpzOi2C9gEIQUhr44QNIbPFEBihqkFLMhrHrzrvuvttugfLSU2+99RLxJQf3BunPGpj9" +
+            "8+8TdXK4Wb/9AghQv/zUUSeaT6LZj+cAH9SvwU0SVJqNOQJ0mpellQ7wBALYEGONFEFghEUH" +
+            "8HmRHlJkzAKKexCoMYYt7vEhiyy2AEII/iO3EMGLGkhBshFtgRCABhtiiHaJISAhoQktHLFj" +
+            "Si+X8IQGJZVIw4sfuEigkSFWbUCAT1H4oS8SGqHihjSkNKAPFlAY4BIqKAnCDxbS8GOApPwg" +
+            "RHYDWFDijDRGxSHQAUhSYnZPcNgG1kvU8OQANQywxYA0SrGljzRu54IEFnKXPAG5PqWiCS64" +
+            "+AHTIa4aogkzSPAETxZaILyvJhLwpBYW0mqCfhTaypMv5Wiw8gM77LDADo4AwgAFdg+ygQAO" +
+            "RoDC3hxwnbdRIhI1iIQtIrEFSMgiBqSABEGKA4ks4AMfYMDNa2wABDtAIWz4kEFsTLgKMGQi" +
+            "C/6igQy8EAkv/iRHfKtwBByktIQt6GoJADjEEA8hRF0MEWQLOER3ljie8ZhsEOYwxspktp72" +
+            "qGM9AbLGHQaRsvvwTBpOU4d/jsYzmn3CQjzbRA6CtoloKC0aQIvGHFRAgE3IMRoEsOMp1hAF" +
+            "MJwCClkghREy8TUjGMFG6kJSjmrkBQvgYwv+AwKQcGQjLzRgOha0RN4ORoMaQKIUJBAAECzB" +
+            "gkY4ggaykkAMHtcIASwhfgL4QRpsEYNGpIF6S1hVKWjQhAMoIViXEEoaBmCGBlAFKWe4xA1Y" +
+            "0AcM4IAFQdCEFFgwAE3IDhgJuAQGWsCFbRCCCgMgRBqaMAAusAAmN/jVL8zQzEs1/tMATVDC" +
+            "L4bBAimsjwXH06UsllCKA/yhc1exhC2kRz+5xE8uedIVFVjAAip4gktNiJYlflA5SLxmlAJ0" +
+            "xCK2IAR10cMIUViDATPhhSwIYgmQ0JgnHIGPH3gCCPg4DD5qgA9uQSISbhkCKWpqLyDI4DC9" +
+            "gWQjJgMJGsQ0C3BwxA8W8lBIHGlbXYoBpkwaLE8ksWQk444VRmYMKUoxPe7xYsraA58z4geL" +
+            "HLiDL9JxxvwQLQdEs1A6fBHHmQWgPdK4gzQ28SBpkPFA1gjsX//6CWkcSBqJHZEYNgEGx8JB" +
+            "EFA4wiFRSA96IAdiESOFIGpEisuAazL0WMQqFhEx/y1C/gQHIa1uhmCH0rTFCwOJhGsR19pV" +
+            "1MISMRgIlbzAlVJoIQa22EoMmvKX7cXgDL+ggkZuYIs/nOENkHoFSmwhTCqMRAq2+MUN5AIS" +
+            "T/VEd0ephRReYQlZ2OIM5qVlE2zBhT9gxRKg44gU0qtLW8hCFlKQBSXMQEE72MItbpGFJ2wh" +
+            "lwJTQRZsSbCBv9IQhFmCEjGgUiM2aUsJ3JA4W8AHtjwLhRM4djOZEK0M6EGNIYh4FbIQwY2o" +
+            "lOKH2gESNUDSSev2mdnGwAj4OEUmBOEFAVSuBvQQcWx1i7BJrAZxkSCNJSK4ClyekDFUwKrI" +
+            "FmCOrG5nPO9BzwK6mAP6mCcR/sagQHtmZiCaCfYTAcArgBIxRv2I4UDpuAOEkFYzdRxtZz1D" +
+            "4xzu+AkVbGJESlvsJnjxRkKLIWpsMNER4JBAfcHhsviYxD1SkK0sGAFdAqOHDLhlASGlCx9C" +
+            "4JaGoXVDAbRFAGuR6OCoUBhI7OoHDSOBJWjwBxaU4gc1+IMSbCAAJdyABPCynRfk2WoqHIUE" +
+            "egoCFzxBiGJACgc40JUaBhCpAaDq12bAVDFYwIU3bGNUwDADF/yAAy78CZjKVIKpfmGLX6sT" +
+            "LLXyXvmUkJZSfM9WXCpFE6iQhr004Qc24HeeWECCIqCAe1yI774TgHBPUOIHf5iYDX5QcUgY" +
+            "RjDa/krXIhaDAAREAQQIOIUKc7yEyPJNRkwSBBwEAInHKMkOG4sOJFaRIyC0NEm9SYwMHIHC" +
+            "3sTA5YKIQQ2g8NLYOMAIAvgDJH5wBgNQgRRZaAREY+AIXSqRiSA7WXq+80Qxu+c8K0tZfwax" +
+            "H/8ASECfiOuZzW4fvP6sZkKbGhrtw56c7UwFQgvQGvIutBHdwdBLm0PT+LwGEBAgGlFAABRO" +
+            "0ejmjA2EmUBX3iw59BsZx0YWSBuSYjB0RzjJShL+wWcYOgRU2qIFEtiSKWuQAC6IyRIJaEIj" +
+            "SACqGPwEUo1oAhMKxwImmAEFyetD90Lgh1mqQQk/6IMfmDAU5TXBAO5Q/gMJgKEJFpjhG0rw" +
+            "hBqGX4o+jPsGk0Kv+TwhhVIsgQt9kAIVDKCGWxvgDMR6ejiV24dXmWEvtki2rJawF2jRj36S" +
+            "7X1QIEw4Z+CWhQqqZX8qjgY2yZUuzEqwBQiyAORAoPEy4x5k4AjwYQkyIQUiIW2y4PbsZra+" +
+            "ZYZKC7K2QBBSbQn+wLWygBGE4yBaS7LggBRigOMWpwZETBAGQgYkrAkEwQIiIQ1kgQYEYRGW" +
+            "IA1WQXLUQIiG6AmHyMqmzByQqGSu7DxyAD7YI8x8IREGIWfizkDoiq7YY4wEaz/G6A4eJGZ2" +
+            "Rh3uIADOcIzcTAXU4c8iZA6Axo3Ujs9OwM7c/ogXeAHkooAA4AAEjMBcJM9sBGELasAR7sYR" +
+            "Ly1KJAAffIQGFsEBUCmT+iamaoAGfuyVwsSo3AtHHCrWYqAJpAr1CmMJmk/3zmkniolZSsEA" +
+            "SqERlKAYmmAICGEAlIAEfsEAPEFygiBRLuES2gkHgIELpCAECIEEBsDc0k9SzkAJfmdOmiAm" +
+            "uk2cmqAUqJELVGVVqKl8QsndAE4JWIAbzUAYqWBwdoUAkw1/TIMvUpFKaCBMCNAS8oRwCHAI" +
+            "lKMBosVLBGA2bk4CUSgKEugEOINGYsBsBggBDEYA8OEegIDH8ME6BAEImuSi6mYC9YWjIAES" +
+            "YuADIijq8OHi0uUP/uxmIqukBvZlCWRsCXLHN6QOBWQhCamRWyzBE4hIiZaoO8DKq0xGPNTj" +
+            "PIqyi9YDPlomB/pDP+jKPihk7dxKHWAmB87w7NoqrtajDNNBPprysKrhBObMr6yBr04gsfzq" +
+            "BDzEsczyBBgBDBTNBi3LsyDJH0hBBmRAEECIo8SGNPzBH4hjESijMgJztOyANL6kMuxgWhYi" +
+            "EqikOPRhCDLCE6jkv+6LLpRACk4tuxjiBoZhSkrhIiyBEF7hIqSgMy3hBoChFJrtBmShDy5h" +
+            "K6RANn/gJD4lJ6oLGGxhGN7gDCZACl4iv6RgGGTBVcJLKoQrLXTnK9KALkAFNe+nIXIL/i9i" +
+            "IBIi4Q8AprUQpwVbCy+qRDGjAy+WAMbsAEeYpDJEYC89i9GOgBEyQDNSgBREyA4sayHWYQuq" +
+            "k4ZWQQK2gIYYkR4QQEUEYaNwUMMk60d6AyPwQRAQYBUmScIggTngIAvIcxEgwgvo4afuwg4E" +
+            "4Q8ADB9SxRboMgaYaIl8UquwMKwOoSi/DmWs4TxcFA1rRhrq42fczIzYg63s7j/aw+4MRIsS" +
+            "wUCwiGd8Zg6cxmk+gbFUII4EhBfYIGo2gRHg4BQYrUlPgQ3AQEphKEYWAQjyJQsmMGwa0UZo" +
+            "qgYmoTlmY8M6j0li4A9sABIUs7WUZDUnhgTOIKUoYVVo4Ace/gcSdo1VGuEVlIDWbocFwMR6" +
+            "qID9BoAFauENWqcFQmAAZKEazSAGmIAQWkD5cMAMtAkDVCUE2s0P3gBWXmHc5ukG7KQPzolU" +
+            "UCB3VpMQzCcNiiENSEANfoHACKEULCFQbqd9PCFMlqDWKIEEbIDiBKABlk4LaEALIi7irlNP" +
+            "/eYPfuD2Kg4wtEBLuOXm7oGQosAI4CAKMuEUEOBhkGMHV6gle4tsjqClQIjpYswOVEgAABQB" +
+            "0GUVQlIGyIYe3qXqcqoRjKBtosMLhCAhkKMFgS6QuAQSHEEKDGAJsgCVuEQA7iESrDA7fFJF" +
+            "r5CrSuaJ0KMol9IaxsyL2mPM/gNA/trOjAaEaPKjzrSozNLw7IjmjE7AaeZuztSBzwhNadYg" +
+            "Gk4BBJq0SeEga6JARjLhWyXSCBjRg8pGgCTjIEQgkDovMhwhb3aES9JlqhqA07DklORCPBug" +
+            "FFpgf0Alwn6FBZwkAX5rllJ1KMbtLZZpT9+ABWzgDKKnVYIgDf5gU9BxAMQtAULgDf7gNWOl" +
+            "D4pJCYDB/V5BDVBlAH6heOKvD/pgVR73cewPVkBlHGVBemqBC+SCnKgAU3+AfmxgCbSg/24P" +
+            "MKClEaIjdaelSkJST5fABvSUc7KkESTgA2hAYKAAAf6z8aLgROBgEiL2XXKkYbzgHphOANzG" +
+            "C8TmCCbj/qcOJ4NWoTOOAAjA4B5Aq+eypagiARIWIwmH4F1YAF9Ayjm41HJk4acWxxJkIAV+" +
+            "SBYCiAugsIieMAp9EqymUKvQAz2+TIrMo2Xqg2fi7K+IBo3qo2TdDmV3dGYU2GSBRgyiYUnf" +
+            "CII5hAAihGk2gQ2sBkR4Fg4ULVwzA4Y0TIEaMQXowQskQAbugae8gJAccTK2AOi0xQ5o4AP2" +
+            "pvPyRkmWhPZaUDEd6vaUMy5gxQZqzwAAihD8gAqekddIYE0c6hwf6gZ+wRdJpwWoQA1ugAbO" +
+            "AFGGwAAMAP9QRwqUINq4AIzTACV06RJipTiVgBDO0fukJ7mGwYrNQJdwaXO5/kdXAO726mdK" +
+            "/u8u+lFaQ9JL2PRv9qcRiDVasARatOB2XWmADEhFPg4MfBcKyoVuCsk3qsN4UcNuhBAkvUAE" +
+            "aAAE7+FF4MCmLOg5MsEQe8NhlwTp4GALUqoGfMmDSKFy0kboLOGGdIkEHGMLJIBLUPEmbIoK" +
+            "StRipyyswI4oyxApiRJHnxkNa9SM1EholrStAsSKcoADzMOK5MyL2oqMwCgdEksazLKcpQEM" +
+            "ytlDToCdGYERFO3DIM2yGJEU/EFjZIBGOG4LjMCz6vkIwIUUwkZbKrEGIvN6HWEIUBJYDqMh" +
+            "WmugECYSllEgbKG87AB0agl6tkJVyOkHZLMt2Dgu/kAiBqjgFYbBC0ATg6LivlIzv4BBv0Ci" +
+            "wG6AEPJrJSxBmUBlJUDzUzA3DQgsDYZhGF6BLYbBEuQYvxqiBZuASswrcCJBcIzqL+xAt5rk";
+
+        private static String splashScreen_gif_base64_3 =
+            "NbQgMPBCCwpaAgaCLp56IFwroFU5M9gAAQAtA6AgA+gBrIlhP0WL4zJhESQyoCu5EmNLIQSA" +
+            "nivZLm/IC2RgCFaukBbhorIgErSAp7gi5sAAOXAwBmShCUCIFGDsB97GcnQoUhwCChrBCpHI" +
+            "J7XjZDjWi+ijKN2jf4vSPwokHaqyaIqmrca5jICGrmbGCxNhgFO7Pgg4AEYk0AJEDB5YHRgB" +
+            "8aKh0E6hnaUUBKIARRCA/hF4AQQEgdEwTa5pSGygwAeOxGtmIwsAycfiWgb6RTgABkfeYgi0" +
+            "wNbKFhIeZ0mUwHx66QYOgOmu4gdk4QBIhQQWNr1+sw9+IAFwQAq0YCM8QQsI4enQr950NY1R" +
+            "4BKGrxjU4KF+gXBxwA/MYACUwRfFqVZq1abV4FO2QVSV6QxKgZ1up7+4oBQaqk5I4DoB7g88" +
+            "IVfekVqT1ZZur/P6JnWjJVq0QJDZFCCldW/U5dJCKjMy4xTM5hRkIGF5w25oqntl4JMiQTas" +
+            "IwbweWEujzZUWVwh6W+AAEag4A+W10k8waQ0hgTSBZRcDob/AAdhGUukNnfSphE4JxIcQQaI" +
+            "/qhEe7Jiw8MXrICbz+PLnnmsyuNlzW6L4gM/1izu8mPOFETQ/SMR1s6K9mPO8kzu7sBpVGBD" +
+            "1kBpmtRqCE2PouGdO9hqABQMQs4BYOQhLy0KrvceLAtHZACRtOUh/8YR/jUEF0cALGDXSOAD" +
+            "VoHfPMkeSSAk7akwuCAIUMUTgAfAzOB4rNj9luANzIBKfokvToUtmAAHVAcHqo0JjpEGLKVW" +
+            "+kANNtcPviGcUEJQSocqwEkJ1uROcqcP3kB3lEdYlvF2UIALWoAS1qcJKC7ZogV/+qYJKIEG" +
+            "GsBLoiUGgGuG/yZ1G6FeXYkG3tyW8OLhv9RtDmlFFM+P2IA6TsFG/srm1kFoC9hGbB5jC/yh" +
+            "hi5xFWzZDjSDRfjzNS4UBE4gL+8yBmYVhmoA8+gBpypRwnBsd8HlSOI7Bo5gMJqggjKhEf5g" +
+            "NIIIAJq+foUoO6qwCrVDO/SXKMMKPr5uR7voPDigZfYDzvTOPwBYi25m7FMmK99K7fRuDt3I" +
+            "gsVASQcvgwERBNaAAE6BjzgkRVi+bESsbD5KhfChOQL2RWyEOd6GSVDjlBbBESjOlUR3CVZJ" +
+            "flLKEo7lL4pNfAZAdNIAB4YBlKjxBw6geUggAeaWBX7gF6bvdAyABgwgwmmVdqz4DFogn96g" +
+            "/FyCCooBnDzBADTVDELgEkrhiFelGKDp/gxkdRi2IVXNQNvOgBrN4Ew21xPYYi/6zzT24m+m" +
+            "5Fmq5PY+gG8aYX8EIDoM+TAkwEsOuZAFZp/PBkaMgB5QBARAIBPEBpa/hjdkIOo+yZEkQDiy" +
+            "QBHwASAQkPICBNIfAQgygdniBQE9hviyZFq0JIuPIVT+bIFCSoCXj1SoZIECxZEXGSmAeFqy" +
+            "xZGlNDG2PIwRQ4AjT2aoOFqy4NChnj59Lhi6wJjRo0eL+jJKgakvX4meWrO21JqxdL6mJrL2" +
+            "icMdazmmTs36VN0na+nOpqP6tKqxHE/XcpWGFq00aemkiTmh9+5evif4forGZtOmE2DgZDhB" +
+            "zx8UeoyOHMEH/icFlEWC6JEiBeXI5kWkQHtZhHnRqtGr7NipsWWIJUtZ7FiyFWORpWFDvAxJ" +
+            "Y2uIHRZpKDVK08TSECk3bC1pckNKDFvJ7Zh5Q+W4mddKzrDg8oqQrTPf1MgidMMTFSlSZr95" +
+            "I0vKtzNNCPWhYktKKVtppAxDfobKrzT7KSFFGiHZYgl9VCwhiyWULBEDDUsM0UgMkWgRSSQx" +
+            "2DHEEBlyuEoME3qhRSMSSFiDaYt8pJodWWyRhRGk0CMjHFFEwUYUYDACRiaPwbGZjJplxsgp" +
+            "9CiCD0NALJIFPgL4YwQYggiCT4p2IAQCG1vIsEgNNsRAyhFGLKLFIj78NkQWpGQo/hMcFAmS" +
+            "xRJKDIFPJibR4KIgJCwhgR1pEMjQIQAINdRPRBVlhVG+LNAWBUsZs9RTjlYlFVpZ5RAWVVF9" +
+            "lRWlYlmjjlSffJLOpVQZQ9WpXJ1lTQCbhBqqGLxEc8Imp2wC6xpwsMELL5sQMMcm0txIAAhH" +
+            "SHlKJpnck0UUAlmWwiQS1DAJFDUIgSyTq9xTEA1AMPmDF6TUEKEX+PzhyRA1/JDGDzV4QYUn" +
+            "NEDShBJNNGKJFIQ0EQMXfSRggxKEoEAFDgaQ4IkSZkhBQnYskEBIMfddAgwVfWhiAAvA3NBE" +
+            "GsX4wYUBmuBAxQDFKJGGAX1IwQIhvyihxDeEcKwGFwjv/neGGUoYkF9OVKThSQJNNIECvDT8" +
+            "8EMMS9DQSJdG/xCvAAL8AWIjPwxhtEdQC9AI1EA4kjUQUGyRCT73QGEEAlHQAwcIcDwGBgJZ" +
+            "oI1PDUYoAkkDG8nkRRZwrO1I2V7UYIEXUJyyithQWGCaEUaACLcXAkByDwgisISPugJMAgcC" +
+            "kOMDBAtmCEDnD6s4sgUcWYx4DwkDtrvc0Y4ACtRPQhllRVGIInXUo4gyiqqlnrK11R1RXeq7" +
+            "WAEk8ok6X6VjljrBs9XpqnOoOtgm0aiwyRqtfjIHAWz8usas0RwGBi9riMEGGwScckThcGSC" +
+            "gBH0IIDPkXG7SdIWQNxjBJON/hDBFlbRCC/AaAsCcJEAQASEDyzBaQLwBAs80Ygl1MIMJKBB" +
+            "KfrABQEo4RJciAEJblCKJrDgDAYICQq54IkgAMMSKBiAAZZgBj/kxA8DoIIZ+qAGA5zBDziw" +
+            "RR++oYQEAKMYaeCCGnCQhjPggBD8egULpKAGJdiiGGf4mMvSEAQz4Mxk22GBLZpAhSYsgQQk" +
+            "0MIPHtiAIURIaWtcwoQaEaJGfIAGH5iQ0WhAQDtCzhE1AIIX+LeZFDAOClHgBQLAwIa+gSAK" +
+            "PDoCAqCQCSNEIWzJ2gJDtnCPKcXgCGw4ARRk4IhFJGkLR8gREASxBZrIIApHoIcMwAWJYaSB" +
+            "FD6C/sSW4OAAO9CAFI2QRRoWAQUw3GMRMYBCFlZBhVX4IAalkIUAwiaDJZwmUIECVDaFQqih" +
+            "GMMKtzNKURbQlLaYwyjWYJRU2tIWR53KF58IS1bWkpU7LC8H6giAqsTSlqlAzxp3eN4ntMeq" +
+            "XpWvfLqKBmEOygv1bY8N83sk/XAEpXskhh74gAIcjOAF/2WilYO8hwwgsbcPxeQeD2LNAmMw" +
+            "yyZwwQZesIQZmhC1JNqhCTfLoMvMsIQTEoIFpbjEAJagxDSQYABvCBoh3iAFLpjhEmbgYgic" +
+            "GgI1JEANytDZUs1ggBCEYBgDgGp2LoEwNbwiAQYIQgKk4ENbBIEJ2fEi/s5awIW6siBBPyAB" +
+            "JSyxhCXYoYJJIwFNkHY0EGUtQ1X6AR1jMCLIecEkjvCBknwwki04AAEI6BuNroSAe2QiA1m4" +
+            "R9mOJNolbUEQa0PdFhZB0i0cCRIxyETbjnRaGcjACG1riQwoaRMolCsGhPORTbIQgzRooWxe" +
+            "IykUGuGJQXrhhF5qSQyAMBKMAHebQekJUYxhju7uLnfiJKcVnkIBuDSqn6h6J1z4ud5OhSou" +
+            "67SKLyjwzrjIJS3SOIth+IIXYInhLicQQ6sCfBhGyIgROgIDKBmRiSPAwcAwAoOMZBIuEWyG" +
+            "lXZYRApaaYfGSIgjGnIEKeygD0p4wQ7DsMQq/haxG1vYwRP1oQRGbmAJPt3AjTdIji2YShwu" +
+            "XOIGw/igFKjwDTPI4hIgpMIbmmMLYHyDCoQAhhSgw9Qdx+wNUi6FGoCMnvtIwYr5sWV+BmSL" +
+            "YaTYEj+IxBAiEaEYHO2vWkiNHR5LwAwvQjVeO1HpRmMHCfB5EaelByI5Qj9/0EPBp2DkjXK0" +
+            "IwmrTUYZEERo7EBJapICDMW8hyCStIRF3IMebCgJPbLwkSNEQQxw0CQ9GpEbHGFpFWiKECky" +
+            "0MraPMYLkQAXFbjQCFZ6wZqrGLUvR7qEYXohKILqpjFwxzvbLdsYhxAnpB4VX+mxhZ2DmNSl" +
+            "JnWqeOIlK8b451QG/hOqHHziBMtL9ybu8KtoqMMwbFgDAXhBo+qpALfrOxaOePFRKDTSCMvi" +
+            "kWjhMAkggK1xf3CEEWQANXwIAhKRIJsXkGYBIPzhDw0QgA0MRgMNSuEPzFUCCRrBLxY0ggpK" +
+            "KIUn5vWKmRrgGz7DwSVIwAUMGMAGfRgAC1jQhz6cIQ2E0MQN0vCNAXjCyUx4qiZ+joNfoPXH" +
+            "Z9hGH9JghjOc4QBVd9nPX9EHM9CMZgTiQi3Q2AQ0dvwPa6RBTZZgAxo0QGkf8EIjaiCAGpjE" +
+            "Jo+FRNa+BgTKkmKSrEyIZxkHhrSpzUenxQcpPiqRSYjtFKcwgiYDadssTOIj8UMAEGqg/q0f" +
+            "/AFt9FDWFozgCBoY4RSkMLg/Mjs33y6BCjWQHxQi5whHUIEFAlgEc3/gA8s44iCt5IIlHPEB" +
+            "M2oE2cn25rKVMm2kHKr57FQnXMwLFelJZSticRVV8jsVS21bK19JlasCM5jytSoan+jeHAiD" +
+            "vTWsrzAgAAEvdLW5ta2B8GA4RQrgcIq2cUQz1MURQNAIWeAmehdaJjFdQMAhCegJP7AFWpAG" +
+            "tfBXLJQ09NJxtgAcfXUGhHBGfnAJf2AAONACTXAJ2sECStAxacBDP6cJatAEwNAMOFMMaoBC" +
+            "GOAOHBMCOvMGapBEAyAgSnADXtQHhJBFVvdlLYAwAnIATmVF/tsRNCjwA1FoA0kzBLBVNDTx" +
+            "ATHAIVATA3lkEh9Qe44wZzUgA35GXRHhACNhBA5gNpmwf4xzCpilWWZDSZwhCPfwES7yP7YW" +
+            "BSCgOFlSG4vQNovgCPRwIl4gCAhwAkCwCkYgCF6wCqTgPmBQAxu1BRKwCLiVCZCoiItAAjLA" +
+            "EAZiAW4yUrC2BY0QCY0ABUvgCSTASgwxcQAwi9ilXUJxTrejKENBAboAKc/WKE0RFU+Rbbqz" +
+            "TokQFW9RfakCPWERT2KxXpsybqGyCWYRDQTQPdczjdEwB+pQGGyQfjcCAobBC364PggAAmAg" +
+            "UZlwJclCD7LlAEDgWlsQSPtDEHAg/gRaEAM+AAU04AmOkAKO0AgrRjU18WtlVFNpwFdV11dp" +
+            "MFRDkIRUQAJ9YAA8xQUDcAYo8EMZqAx+MAG/4ActwHM4wwQDEAJmcAPKcAm2cAk4lAaacAkt" +
+            "YABFyEFm0EU+pHVJxISlEFVJ1JN9wgVAQwWWEDRNsEffQgM28C0/YANzRANeAAmNQIalRHdR" +
+            "CTWQpUkRQRIvUjabQxJEklqdMT+ZwBnycyyapWlbMGfwGDYRFwU0Ekit9Af4AEpGsHmZAARU" +
+            "QAM0kgJZUAOkEAWAhAD6FziIFAWk5iJDYAsI4QOO8CGVxSGiWAt7YwFRGRONM3LcVCjKhxRP" +
+            "oSi8MxTU/tZP7oQoxWNeYEEWZFEqqHkWw4gVS5EDjoIo9pUOzoMWeUEX+IUXa8FfdyENhiEN" +
+            "88dfOSINJ4BgYvAJbBBLgeEZh5YBMhAjbSMCKfA+W/IlaQkaaXkiabmFd8YnSyADWtAbi0AJ" +
+            "Z2ALzHQDS6Ab+LEEsyEFESIFv+AahDAMPwAdr2F17HEGVTQe8akGPyZExTBlS7Zjb5AGzQF0" +
+            "+fFz6GEL+DEM+MEFtlAKCXIgB8KetiALlEAFW4ghEaJmHGIHWnBicyZIH6Aao3FnApBhNZAF" +
+            "IpAFiyAEiucP/vAlmpFKkJYBmQAZcAAGYlB4OYJRpGBom+EYEwZqbAAlmsEh/pwBB6qBSpcB" +
+            "BDIAB/SQiXBwBFC6BYwQJl4gaFFJDyAgBqi3CGBgBENgA6SADx+yCFGKAKYReo6Ae325BJCw" +
+            "Cv4gCJngEXtjCSBCKMu3XcbQFBTAbLzzKNawAHARm+yUFcLIFpaSCIi6KfNFFbU5PZxiKqQS" +
+            "KgEQAGYxB2JQjbYST+qgAipwPWvgbrvCBtVwpA82fzRyCuuTGDZCcIKAW/GzeJq3GQS3EUZw" +
+            "TAYHBRA3Jq3hCZJDA38QCRawBKUgHJCQII1AA0+4cTcTAzwnBQJQCgPAawmjBFygQ2dwMAPQ" +
+            "BEUwAH2AU4QgBU0wHUrAAmqgBqWQBpegM2HlMhTZ/jJMcAMGYAA3wAXb0SdRJVdKQIL6entp" +
+            "cFdUgAIk8AcksJQ0kEEbVzUZ1whvFyGNAAkScA91B0iN4AgxkGd24AhAQApi04ZGAAX30Dfu" +
+            "mFleCgIaxSyTlBDxIwN2Gj/IshHT4iaOIFuSVwMisAor4QN+SHqe5QUkYIkSRln34Ah5gg+8" +
+            "QA8WIFtsUJf4YAQ1QAJycgoqaxKCUDk/cA8+sAQydkCkZkC6t7HQxAKRkF2ayRRKoTvlBJtL" +
+            "sahR8YzVF43WNhZ024xZ8QnHWF7j9hTYl32hYg1zcAIqUD32hI3d043c0ytRsAbytgaw1FBp" +
+            "Y7VsAwWL5Fn3MEnTkgL0/uAAmlQ4oscjjrC19/B2AnC0aOaxkDAENCAECcsCipUAaUCsMfkH" +
+            "NCAfSxCCCTkEw2AApfADXOAxkWBD53EGv4ByXOUfPcgCS0YFBzAAMcMyNDN0PacEA4AzOIN1" +
+            "XHAy2hoEKccFpVBXPUOUZUcFnocCZZewe9Ssf7AhjaAFeKR2ELs1eOo1KSp3AuBwobU/W5AQ" +
+            "RgACPFJ/h4ZZbvl3dWgEFuA/oiUCUMALjyRaqxU4DgAHJ0ESNQBr/nAEuOalR2A6FrUFqqjB" +
+            "p/ElJ3APpPAksIUPiSEDXmIEolQlmUADw0AFgjBKkegF78MQysJMpTMQsggoyDYoPnFO4/Rs" +
+            "/k7RTmxRTsKIbegUFqbCTo4ibqkiFlI8PeFXbmehAmZhT+injexTYOxzAnOwK7MCUX44JKeA" +
+            "I4zwtEawBgB8wnAgUvjgjl1jScqUAkeLN2EDW3bQNX1ViMAbE3/wcwLQU8O3BFm0L0BnCYP8" +
+            "Cw9CVj+AgjsXH2cQgn5ARmqQE/gyH6UARbawrsjhQwaAMD8YBDdghAnzMRSJvWbQAsPQAkkU" +
+            "NCsBvGW3cmu0RpaQQUdTNE6TQQTZJSCiBVogACVKR1oTjy5yD5xEsoyDS5lHI2dTI31zejVS" +
+            "I48RNlmyzKlmB4kEAj5cAzHAeTWSJZOADzFgA6fDSC5yOSzgCIIJ/nANAQVD8AePsZ2IxKbg" +
+            "YgSQ4AmUtgVQOUi7+hEcQawdZQc1sRHKdGyFUjvi9E0NHSmPssTqVCqfQpt2S6jT44unWSm+" +
+            "kAPpMAisaX2n4pppIamjkm20WdLW0JuAIQYEkBabEErBkgFgQADF6aoNRQ9HgBg/kn+CEKVg" +
+            "EBo0Kgig4Q+FOASgUQNKSgqyYAcjZgmR0Gck0Bt2sAS2wM9DkBxeYAveIQOzoadDcAY3sApc" +
+            "raFnkGLtcQML0idQdglS4Al98h0mw2Q6hh5ScIR3zRv4MWUMSmZUIAsPRANutIUytiGQsLoa" +
+            "YtCUYAdV86FDQEBqRhN/tScLdBKbASOc/hsjMlJMfbPTMpJ/jCRhufIljFDDh6YZahPUWxAa" +
+            "J+EPkZFhxYQmkqixjpCO9PCxWUAPjiALPnAKpuHUOyrQy7UKDnBRpSRoNGAHq02QMoIlaKLb" +
+            "kAA2i7BGoSh5CESyIvADRKFs26UUTeEouIOMqpm3nGIpbUEq7QTRvkhfvgDSYHEHAUBPgBsq" +
+            "6nCc4yYG7109YFE+6PYJLh0NvNB/AhbTujKOvMBIj7RI/Eeym5MJLSJogCY2+NA51BIJYLMF" +
+            "enIPKQBcCJg0NVADKMACNcBcZsAt29FxLnOwXNBUXuAJ8wEJJmMwQki1ZvALTXAAFOkwxTAM" +
+            "wWsASYgzFOlU/sDxdVxVClaH40xwrybDhAeTBi1gC1wQNGhHAxmaJ2hEAjZACU4zyz/QBAvb" +
+            "BKx7RmzEIWoXLzQgAY4gBJ1ECktCsg2GLJRkSVFgBJmQNtHsloV5eGPz4Z51lyKQCf3X5kBQ" +
+            "d2sDBaORh7WR2yqbQBjeBFsaBSjizwkQA6w3QPyXBXWnLTEACTQSNkKgcKPWUbOnWFGLinuT" +
+            "0MqNTGdLO4QSTg69tuqNmv0EFc3IT4sqFfRFfXZ7bbT+FJeyT8qzPMqjAloBuOVjFoWLPds4" +
+            "PgSAKzU9B25cK7iCNoww52cDB26CLPy2BT4wSQJUNrr3sfewGvjgABkCCZpER45g/s5yhIr6" +
+            "4gjLKkco0ER9JQV94Ip9oATNur1GIwVMQAPMYUYWKQXjMTNUdDNHeDPbm65pkK4fg4JMcDJB" +
+            "cDL5mgYR7+QZaAYSuq0Gklcbx3a6vLBYnrAkUHYPVPJIWTReHjSCPSEJFDaeVjZm0+0AhzYb" +
+            "JZiCmTZmMz9t445vswVRkMYkcSQW8CIC4SWJwRElW3pewAh9cxKrveklO+GJuFxpQDgU0ceF" +
+            "5w9AcASZ8GIyQA9Sslr0AEs1QLJQEAOHo9t3BtRC8Fh0UmoM8QPaxOq0s91FHJqhKV+bkgiu" +
+            "yU4AlcSDeinCGBX0FT2uYk+h0sXcqCo5sAlT8RX2NG7R/nC468M98QcGhHEjjVu5/tZIywJJ" +
+            "IuaWI4tbk3BnG7EKEoCGJ0YKk7BA7jxx4OIFCxtIhQwSLK5DTfAB7WoL/VgfNYATS1ADG7ME" +
+            "HjQALYDINs68F2MAOqcE+X4G2moGEc8FLdAzNMNWdPVFTGgyNLNzdxVGLYBGEem1GbRXD9Q0" +
+            "eYU0Wp7yTjPPjN1xP9AAjbAl1IXQujoSgqZRobc2DkEjAIGAHhw4UYwwQhAFgb8s+ODgq7Fl" +
+            "IBwoW2T8gOQDDhs4NaDcs+BlS5YomWo0guLIUwwoRujho0EKCBcDNKB8lOAFCr4YMUgZiWHJ" +
+            "EQIw9LZEvOdoSY0jW3puoYjP/guQe1u8QNqSIsafRlnu+fCyQOzYscaMmTO7wKwxK77M+vKV" +
+            "I0fcANbg2rWWN4e1T74o+Mp7B65Zu8bgwqWQ6PDhdHYB+/WVTpq0dJGtTR6UKN1ma43zSj4x" +
+            "WdonXtKsRQtN+QQYMScYRQGDgNEJekXBQCG1CARFUv6iOiKVaVGkRfSgeCFl1MuiLYIWOZLg" +
+            "b9GQIYugWKIkw4slKjHsLAEfw9YbS6te3YixypKtSHaG2bIjKw2VP5ak0E/DZb0UW6XOpLGF" +
+            "C1mGAdCWWmqxJUBb0gDwBilu4KK/BA2spQkpfrBEllowtAQ8DJcYwrsl7KAuBhRo0II6Eoew" +
+            "AxI7/mxgcRE77FilhtwEySIFOOjx4Qh6kgMjiigKggMMIzMwciN6joiCI4KKzMAfUmRwBAp6" +
+            "TjhikSNOocckQehxxA5SwPDRCy+ysMOTITKhJynksoiEBNwWGTEL40hxhB4BZLFhIDDuWcQC" +
+            "MIxYJIaXGrGlSjiysEoQI6TaAoqLYqgBgUVIEUCGSIEYyxyx0DJGLLfMooACw9xyay5jHDts" +
+            "kLw++eQOaxLJC6/BbD2MAmvuMGYzy9KZKy91YP0kgE+GPfbYO+oKQJ1NPuEL1juGjSYadVrj" +
+            "hQ0CwACBF295oYceNtgQJMhTMsFHBiNO2WKL5BYFAopFvcCHlHvsAMIH/igWEUCAdiERQIh7" +
+            "rrIgkxhCXASIJWi4J4ZalOi3FC4a+UOKM0j4wQwpHKEiDU8gacKMNIZo4oxaSEjAjBa4OIML" +
+            "JQxQggszamEhjRZYuNkTLhIgQQoplIhZiTOCNoMLnFlgIUAUuGiCC09seZoFEppoAgWMf7CB" +
+            "hj9o+OEHShrQmgaxf9BiCUi2bgQSRxxZRe2pHDDiHjgQmMQBBBCwEm+ojDDi7r4TQmBuUtxF" +
+            "4BQZHCqpIacEgAIMNrYQwoeB/W2Jp3Zj+KAGIFwSoJEtGmmCii0yechfQSAZptJMJFgFKoMi" +
+            "QiCLJkjAh1ABvBABiiiA8HeLD5o4c5FGvLMX/h87HNnCkUXIsiKttcxq/i1fEpHrrlrzSkSx" +
+            "va7PKy5aE/mE+mC3t0uaHP5S5/pjuR8WWWhjfT+AO1SYA1Yxiv2EgDXq32Q1XsQYFxtOwItT" +
+            "ROEUbIjCTQiCgHqd4hT4uEdwoKC5mzjidpuzAKWgIAhHeIEeWYiBFyIYHxF64Qd2wAfo6NUI" +
+            "EtiAYmkwgwCW0AcueKFmS2gEF9LwAxrokAo/qFktWMYFHRIxZmk4QBpiBjUdekIKaWDBATTG" +
+            "BJUpUQoGKBoVWkCFBCBtQSygQtVIgIKuzc4GlCDBErDWtSVgjAYu1MIPYsDDH3BFAB+IAQ0+" +
+            "sLZ2kQIKCMiES/xo/oSd2IlvCkGAEeBgBAPSwyWAI11VvvSoLJyib0agihGWEAM4uOY4gvDC" +
+            "H/7gSDBswQ4iocEwaGAUp4jEHzWIBOkEMAQPBi6CmQCDI2jJCDT5JBOPi8gRUvAd59jBFpAA" +
+            "glFkwJKGUGoLKTwEAA6xgENUUyyhIssCKKCWwxgGV3Kh1V08s6q9rKp7eHlfrcjnC1kBpi7E" +
+            "+kQ0NnGHO8BvE8Pi3ydOAKs5RIMAbHjWJsa1hgGeAgEAhUMmTjHA2B3UkkYAw6JGQkgZWAAK" +
+            "p0hBFhyRBSM4wnOnwBQQtjCwGngEhCLcAg8bsjUhiCAGJNCCCdOghUVM7Q8xmM8fljA0/hrM" +
+            "0ACWIAEYmXazAFGBCweoBRU0VjSmdUyISmABEWtGRAa9TApmwCoTlJAfKljCaFyoxRZn1wTw" +
+            "6JQEPcWYGn8wvCVkbSs0IKsNlqCFGDTCCwKww3NkoNEtGCEri/xISxLJtz/uJhM3+QnfQEAA" +
+            "BNzDkbu5Bz6s0tchZYEUUciCFyKSCVKIxAhZEIIEaoCPlPxgKi9dQryMYIdGAMELTfiBIjtq" +
+            "V4rEQACMCqNOYueIeP2ItZLCoSPwkQW8CpcUYGlXI064hUWEJZvNW0D03rKWBaAFLrSqlVsG" +
+            "4YvJ5CB9fHnMOa0xiL4YA1qVAW9lKjOXvlivM+n4xGbgS5nG/khGGuoYzSdMI5lNjEYavKif" +
+            "Pot0gtWAAAyedAAbWnMEK+1oJDvCTW2aQgof4WMRi8gAKWb0JRHY4UsykgiN7GDZIdgCOTqV" +
+            "kSxIMKMEkegGlvBCiUH0gzQMwxKhowIVoKbiJVgiPwsqUI6TZolarCdBOi4FDBOUhiYwiD9p" +
+            "eLItmiALT1iCBpawRCQoQYMYSGAIm+zJwVahtbIN4QcsWkVPWJRXL5zSXRy9kVEUGK4o1MZx" +
+            "UThCkcBwQC6dghGNY8MHa8MGUozJSPQQxJRsxAg2RAk5MdhkchjBXAv3WCcbjMFGs7CESLgr" +
+            "hHaiSCNkMMwzDMFKrkUJQS68hSMA/mEVVboHlTR1hFh7OHkxcNcq/tCuimRTLaEC9qgWgCpU" +
+            "WWNVnumMLz7hXVhZQx3Gau9hEiGr9OYAVoBBVmfwEphnQUsd1kqfOk5wv2ox4n7x1KcKNrEJ" +
+            "b21CDKeAw7p50a0o7MYImcCzI+uNtywELrNZIMgWLBApKIgU35l1gEKQd4+CO8ICjAJYR0ng" +
+            "CEFEAmCupcISGpYGGnjBEwbwhAAIdNYlcIEEQ2CBFKLKBSoE1WhFzU9UY6bUlEMVhgwygy2i" +
+            "agYl8BxoRhSZFFfWghYAdaxN+AMlssZDG2SNBg3QQiO0AIkfuBFrw+sX74CAPHzcDh9AoMo9" +
+            "tt5Yr1iJ/uG44Vvs6FFnRi5SSBFlEylqkIVMrAEECYMCo2qgEwtAAgog6KgjIgIEOQocEjQY" +
+            "QqSUIpIflGIIDrAXKSCBDwcswRMfyALFMs2mlOhECCQgAUeTJ5JEtrkGPkgpDbYeJ5FwcCqR" +
+            "KkvsRzVdUx1GneoMH7TqqYLswtP3fDFWZ5ptDWv7HlrWqJ/8nKUCWKnjn5tQgQp6AOB4yrNa" +
+            "c1jDQjfxpHEbMM8EIAhsFoVvOJyiITdpl45e4gUjsCELzDkCA9uVCTfB4V4WGG6/NmpChmuB" +
+            "CwK4BxlajiVQAtNqMtD7AR0KKiZDAa5qAVtYgibYIjCaqpojopVhgaFRIh3q/rkmwyqNOQOf" +
+            "I6KfUYJSQBrQo5omqIWy4aEG6BqsoY4fWJinw5o8Uq6suZ1G6CABWBvgSB4h2KAs8AEfGK64" +
+            "sZd7YyTASyQEcICWIKS/AwEosBskPCwLQCE4WAO/EoQtgAQRAAKCYAR6EZ40iARQwwcb0Cgq" +
+            "UIJISQG8qgF/uBef2IIhoIIagDwHEAQPMwIZEArgWoIsYARCshEr6Rz2gzQbwJGUkgEh+KB2" +
+            "YZRpsqZpwqZfMwzDUIxiM4ZE4ABrm5U74IDKcC/xeh9poCf3eRX5EZbj8z1nMT51mIN/OgH+" +
+            "6Z9hWbcTWLdNAIE12ITsi4I1EIPwY4OIugk28Ku+/mKTgRGXTLAov6EsyRKAIskER2CkLegt" +
+            "QGGJLViYQnst3pojCQChkGsEMAKPHiKroNKxCDEqJqMCFiCrdbSFFmiCqKIZKGIBkbGFpWIC" +
+            "pBkaJUgiFtCqFogZlwEaMxgGItK5HFuCP5idOgIRFuIyrjkhLUiRl2o6GfSORuCyqZBGqyCk" +
+            "5fgKkfoJKNARebmkRAKBgUgghXAghQCTfmMDBOgrigAURtkCG2DERxGJFGoCgamBzhmxe2iE" +
+            "1vECErCFIagSNGmEGrCD+Ti9LKgB77iJu4qbHzgAATACH+CtzXEk1vEBAaAES8A1IeCyLLiJ" +
+            "LAACxfECz9mCX7um56mu/ulSC+2plezxhcwAjGg5vlfhC1kJt8Y4r3RIn8gYhMbIAfhCtryg" +
+            "L8kAFvuajNAQg9FojdSYg8nIn01ghHVrjUFjgyNghDVgg0wQhFMAA0ZItCOgDceqjUJLDkKT" +
+            "gQygDRk4gkMrtBOYEn8AA3+QAVJINA0iBUsQE1JYAioQgUKZkZ7Asi+zhCHAMjmiAk+QBSDq" +
+            "MVvIOFv4SgNRkAQJHR1qmhx7ogjZjzTgjvnYjyN7wIWRSBnUgkiwhDOjjhAhES+DhEbAqxCJ" +
+            "gfZYBc36gQvLqwvLzXuIIFKoF7IMjtxgEy7ZEUaoDXFxoIFYu9vIAIgqCivJDXrIADHAsyMQ" +
+            "/gRNSY4okLsUaLM/eD8Gaxt6aIQcEwQoyAQBiE9S+AFbaIQApIQfqAHHEoJX8wcLMZT3Kw8K" +
+            "WxK6o4dFoIFLwQdIyDQcKbi9OgIR2Cs4OCWWcAQrUIsojdK1QJVMrNJVuctdAYwcCD6+QL6+" +
+            "UAfb04v3Uodh8Yx6wi/8ioYA+4Ruax9yYzcx4IVoYDSAYjdeWLdomDc2kAY22I0oIIB5QwCC" +
+            "ig0oCEbcIElBqAqCELuLEoQaOCw4OIov7KhKMolMQAAQcEp8sJRdM4IaMC2IAD3XwggZFIB4" +
+            "pIGgAg9PQEFWBSMUMCoU8IQEaLKdQRkW6CKZyw8lYAIGSZpzZAEl/tC5FlAZLgBIJViZqYnH" +
+            "JkAjqpmArQERG0ijGIgjGoAESBiCRhgCLrOBulKb4akBR0AmiPAIB/AcJswK5uKsdUFResAb" +
+            "g2gJt6uNz3KkdgnNfVEkvMEH0mKttcMsC1gEGdAjQcgEB1iEPxCArpy4ipBBzyEFbYWIPyiF" +
+            "N/yJnlgEEPoBkaKBUqABsmQUIciKFMgJr4AEs7mHFMg7kbiJ5yAFp8SKzEpL5JGubRqMv0CM" +
+            "xQiWuJAW8pGLvTwv+AFab2O2+HKWeoI26Gu2ZtsE6/uEOVi3NvVMNlC3fwIBW5w3MHDaYMTT" +
+            "cdmIQC2sNSASgsgEszzJgoMCBM07QTAI/rYREuaiB8BjFEc6JRQ9CsdyiotN1a6YpbS5K7Ki" +
+            "OthKyB/4ACxjVqqBVRx7QC6QghYQomJNuQO4wJaLmVpIgJtJAKpamaZBARUMVhaAVYyZnTZK" +
+            "SKuxgTPLmtJ9o0YoXeXa1rbqMtuSAEoRghhYBCHgrcG5WFIQgknIBCAQAroTUJe4pCiMm77h" +
+            "vMC5JL6BhEVQqFPwz52QAZGCA0YgBQswDhoZiWgMIX+YJS8gkkbAFxngAkoAjnsQgbnLBH/A" +
+            "CBEQhCpjCkDJqyy4DRE6gk0bAuNorsFZEjjwXiMQBLtSvOcwyy3UqGwCtuchtsE4Fce4nrpc" +
+            "jC21i+bjC/jy/janVbdqObc2laendVr3uYMNJgB224R/mgN1OwE73YQDiob8ESB2eztusb+N" +
+            "SCCNiIJ2+ZubWBcQ+LqWwBERKJJJsIDCwYeNihuBc6S524KuxLXMy0EBqAG6soMYaALlYgEc" +
+            "ktY2SgDLg0AgmhqhWtx0ZIEkC5CfyTGxAiOaIaKmAaOOaYIbo4IKMaMfSEHQsxqp4xoXVC5t" +
+            "5RqxUS6y6Yl+sSvc6SAdNKWWXYSqYDghjBuO2hcHGFuyxJt7EIjPKmIpLInkIFjJ+qxToocC" +
+            "8oqkWF54A4GBeY4IXAQ4SAEg6DgOWgIB0DAZ7KsfEbUsgATwHC4gaABHSIFHCYmR/oI0L0AA" +
+            "R3gpdaEIXhsY5NHdhWEURmmui+2gLThgBT6VbprmLGXgyEgE86mVVfkuYnE2aaiGZ+kMf6Iv" +
+            "AlOHdOg29UlM/5IGWwyNcWtn0YhFd1uNaJCGNXXMohg314CDeeOFI6Cw1bgUMFgDKxk06cCl" +
+            "LDkC1/wR15TUBF2OJdHf3Cg05ugsCVgECbgdGVmF72CRRgAR5cxWkFbO6OyQH8Ky5PQxorSF" +
+            "NJIFLPtKLCuyG8OQN5aFjOuxNu6QNj4zStACxKPWIZgl6jibMyORHgNf75CBvIqILRCAS7mw" +
+            "wQECQcCRRfCHRIMKRCs0NlkSBIWSiHKSPcMNH7GRMSG0/nC5sHqBgywRhEDjID8iBRbxI4wQ" +
+            "AH+gBxHIWKto5UpKWXtZhCY45FUgAVyza0EAAn8wAlL4g7lFPCFgEplo2QyYIA8S5iEgrYqg" +
+            "kg86jj6yqydFFbWw2cEgtlOhgPFJNuIjvnjSS/jqDGlQAVt0xcfE4Hu+RXtWAQrehDnw2Wap" +
+            "FgKQp3/KFq71FhUIVEYTKNHcHzEAAwJQAQIiHcfx39pAgK1zFPvDhwKSFChYAwbygbsJZqiI" +
+            "ikwAAZNAAB9B5ExInhpAphoIkTAJIQFYqxrgmhhwqzyyBFZtgp5SozAiAU8ohTCSgj+4XKNh" +
+            "RxaIGjOgGlYFPamBrSag1S6S/kCkISKjqhq16hqna4BshTSJ7AmMAaohuCkeBLuuMJMsMMsZ" +
+            "tQr/VNRJgDOyBLvNsWH/peS+4qi+MQi+cVcHEL3WmQS9YRSM7CsuOZyH/ZjaSYlIsIB7WII0" +
+            "WAS/sqt4QQDbugcjiARPOJMUaC4gBMolsACZoIQ/AA6ZYD9FWgR8oAcH8IJWvpyOc8KUaJf5" +
+            "Th47MC0qrb1uCi+4mIssVYe92J6klRVgOZYyjRYObtMTiAYAmlMxWOH+6bZoYJY2RRZe8O1X" +
+            "7JYAIgiCXoMgAag9u8wQHpf+yb72K2gZuCjrBgIE1c17qLPRis2q2AIhMYIUOKiKGIjPcgD7" +
+            "EwHU/vMCH/BQh2utvePBGBuCGviAHxCAP7iaBohAEvihqqs6SyC6lXljqmF2aqcaKUOa2RGb" +
+            "r1zWqmmCCYBVFGBHqukxGqhjbM1wak0jOrKBODKz0gURIl0uj2KU3vld0Mp1E88oTMWTFOjd" +
+            "Q55yG/e3mxiIvqoIr2ADKcwK/2QU9BahHxFXgROBEWETV7ODipBrCxBsdcFf9tsOQ9kRvAJE" +
+            "I7grubvpVSi0p7bKIREJfdECMYEDXbIJgAOOFACDLDDsisBIM7GKtMSmSXSLYRs2xTCVSlwM" +
+            "Z0uE7YqMx7CW+IqW05CGZ1GBaNBFBFqDaNCWHugn53tavnCf/tkngTqB/js4ASGJ2oTq51zM" +
+            "VAI4Ac9cN7uDg1wEgVPQHRCInZ8YlC2QgINql0zdFxHAJVs/BRDQ8UxtE4A7rEzIqNrxz3ug" +
+            "ycwSvLQUSxoYnuGxA60JCrKiBKlJIxK4MaphsnjkMRkMo80Vd3jsGkpAo6AaK88HD3akBNFd" +
+            "q574gQxHPIisfbKRoz3mIdZ6IyItxI1KGJESLiB4jqQA0IzCl5FyBMkxX0L6LIqIHVlPpAXi" +
+            "VOW4B3zDJKuQz2cyvyEQAUdQSL8zCjzKgiEohT8gdb9yISP4ABZohMHJoylXjl2XoRjYOjF7" +
+            "3pOACuHZHJUAiAZAoNwDsmiLEXpQgMjYkkWA/p0si2IM+WBnyxZHXnxxNGaMI4UcHH2Z+2ht" +
+            "pLWUvgYl8pUunbUcKdX5kqnuU7RPc6ypS/fpzqZP0qKJkXZCWjppm6R9Sin0ky9pSK35tIZU" +
+            "6glGTE9wXZMVzAkxJ8CcYqRz01FpYhlxFXPkFBsxYI7go0dWIZwjcKDYPUJvERy29OiFY5TB" +
+            "HxwwCu2SonfEn19/MgYvEpQl4yIZXiQu6mzn848hMexYWtJIyxKKNP5YorKEShNLsodQomLD" +
+            "liVPVHDPXjJkiZYhQ36UtjQk0vEloSlR8t3IkZ0Yqz5/ni48RgzoQz7/iRRdwvTOMvA5WkSK" +
+            "1CI7pO5tIZUJij9B/qT4Lvp73h8UQUaMwGlMj01d9DS2BRhwLObPeY0tssp/Am5RWQxLkGLE" +
+            "EfcsAoQ/v8WQyYMxLIIPFNgBIcgSqSFghD92AHFPBo0soZ4Nrq1Hj4X4+JOFZhN6YYkMUJCy" +
+            "yo+kwJGJFzLcA4UdXgyUmQAFsQeEF6QIAoUAHX3kUUc5GKNSRxxZ01IiH+Ug0ydlpnQSVS99" +
+            "clNOQm1CAC8n8MIIUdEMdUI00WzCyxw/qZDnJyowwsYpYvDCCxtR7AnnKbysAQccURx6wolB" +
+            "8dLoHNGcEsWgJ9CzxT0nQrEFPmBEYYQMRhQoAxAIRIGAj/ScYoQICGRSwz1DZgKHEYrg/oMA" +
+            "PlmQQh4+93hRniMxAOFII15sEQMNNTTSQCOWkPDDDzQI8AMJlvwA2w8JlMJFLVywQAJsJKBg" +
+            "CQssNMFCC7a0AG8T6pJw7Q9LUEKDvtySsAQN/2L7R7aNNIJtttjaQIIWlHi7Gg0/xPBHktpa" +
+            "IIAAIjgiQBYWHJvRFjX4AIQAGGXhSEFG3HNZe1BkAUUU99BjBBS8InCPD1AYsUUmPd9TAz41" +
+            "HPvyKUDYYSwJf0AyYXoXksBCDPzdI0AMPjgiCxXtZWEHJIu8V4MdiwjgCRcCoPdBI4ukAEcK" +
+            "zp6yRWoIJZudzkAI0Z4IPwjg8mpROlJeFiKQktFzG9sxkpgc/onkSyIzndnUlzLFZE1Td6hz" +
+            "k0/qjHkT5SoEldNSa2yySaabEBXnCaN/EhRPn/BCugoBsLEGAaazcekabNADB6FRDBbFWHCA" +
+            "sAbxp8Ah+hqvHh/FQkaAAMWomUQRRSb7JQpF9TdngQAC2+98T0KhqiwCzUC87NCyAjgChAQa" +
+            "2VF4I5DYIbEW/C4RG7dUkNCELbXI6665WHAAdLmLCi1oAhUSABvXwMZb/7oXCu5FCWyhoF4o" +
+            "oIQNEmawBmhwWtm61w8m6JuBNUICMfCCENCGjy0IYBEnW2HgXLgQIBihBhlZoUOywJ5MZCET" +
+            "91jZy3JFM/50r1ZZaMTLoGCr9XmB/gR22M9CLkKKBkQCH0fAiIfQ44khOCQ0QIBUCmgggkyI" +
+            "YAkkaM8qJNAIENGjZDKjgSXMRjIByIcNO3PEfAThCA45whKN2AI9NOIFQWzhCAdpjyO24KxF" +
+            "SMsjC/iILyDpi5lAxRg0OYkv7nCmlYyEI2WCyk2a8gmfrM50YjDUGjw3h02sYQ4qIEDtgmI6" +
+            "tJxgDaZKXe7AAALT8Yp3IIADXAi1BSUSgA3Yy90cwMALBLxuLzWLAim8cI8oKEYEPgABCLhH" +
+            "s18iBAHt8d0wR+WDE+0nBZfJAhBWhKMHCcALYnPEyY41PxrEABINoEEkrnU/S1CCBa9hARVQ" +
+            "wIUWoIAF/hVsQi3c1YR31QuhC0UBCu63BGtRYn8TWNjAfqCFbClnODQITmiGsFFsTbA5JMBO" +
+            "DLZFAyDE4Afu84IImrWxLQhhERtTUhaEIIIcZkFYPb1HCmjmMued6GUp2AIcTnSK9UABH16g" +
+            "GxS8EKWT1SALM7NDA0TwVIjw51ko+4EUPCGfLbCPfF5Ywiqy0MQmCAABPmDkFoAlAEj4IAtD" +
+            "oIIdTiQlUBkhCyfk0A+6ptZG2CGRRrCpI6AggkUM7X0SEEE8F+eRlnCEsp50SlM6+ZLGfakp" +
+            "ZNLkmaKRjhzoKXW1U4tUpKEOtHzyE2Kwk1SWIoZPKIYNRxmLbaWxhgyAwbZZ/skAG0BwBDCw" +
+            "BSxgAINseXepTbBBEIxghGJAUChG2GUwxz0PCEjhj7mQwi8TElB3B1OzFNjIH39ZhAjwgZ4R" +
+            "Ce0e6IlSdNIj0uCI5gd/OI0dhlAaWXhioq0pzg9iYwkBU4Gfs2kYc4wjmxi4SKP6fbAWLPEZ" +
+            "SIwGpdsZwh/q+aIhrIIEXshv2D5DCqG1sGkPMsgifLQFGZxsVDIYnCCiVJftDWYwbGDbL/nC" +
+            "Fws1xkDoEU99qMsqifBXZoM1Anog4Y/DakF9BPFCFe3KAhcObhGNoEcW1FhlStjBAfTAB2N1" +
+            "huX5HCEGxqmyIwgJByCQAo+XWeM9ZJAdpGbGfIvo/qsxwOQLCkTSS1R53CTVQZVJhoSzlMMc" +
+            "VNRUJj0hZQ4nKBMB5IQnpWxCDKRjJXGjcajbGWpIhxLDjaPQqFNkwlUgoF4mTAWCLAgCDpvm" +
+            "BRy4kqjdJQp8spoZ92p2M0hdRiH4qB4+fGCE9/gMeivEXnrJs6KMKMkLzVIfJEq2rA8IoBHV" +
+            "hlbErjUEGmTbjAslQQIeKtCGUqFdKPiWBUkQsIo24QeeYMEPMpitBrQbYOrG1rZp8AFo5SuD" +
+            "WnBRA0iwnQZQotp/8EI73UmyFR0rkVv4wNCcCs9lrYyxPnBvUBGwPiMktjF9hWINEhsFRbpX" +
+            "kTWwACmAQAM7kIfb6oGC/gQusoV8WWBXmdjYIiRAtsRu7AMyS8HBgFADLlCBYyytwSrcW3Ej" +
+            "AB0SwmpEAmjgAAQMrqfQK4/5TpNIITw1kffAh1YHsgXG1YRxmHTc45zCuU9QQO3WuEM6Qjmm" +
+            "T1qjTzkJAJ7UQYA8ReF2o2MD4EENAsCbrgevOgVzs0mAOfDCd64CA7F5AQahCu8UoTpFK4NL" +
+            "TQKcCgFgWMMpZOUjSCHALg64R6lJtR/wxVWvOlvPsH9KCh3+9CHudATXEX5TJSVpWhGzQcQs" +
+            "EbHfhNBhTeDCuwC6PxK0YKBFMBfy71cvdavroRMokXKwRYPhACw03A4hwppgA48GTKMoWPn2" +
+            "/qUlMdy3UKvHqgHJNLK+k8XcCyM7md1ymgWVwWqY97A8DTnEkYCBNx1EtQGBA6iMihyRJzSC" +
+            "BfhD2zTAIkCCJ1jCkUTThzCCX5mNHSyBLSRWBuCDHQhAJkACP6HHb7gQI/iIIzAdKdDAFjgA" +
+            "3AyBACBZZ0gJHFwGDXkBhaHHKmjBKmAEIrkMD7VZm/VZR7RESmhSOkwSTDiFU7jESYgEKdkJ" +
+            "3QXFJ5DWnvCCOswBG6hApoAaGyzeJhhPFGTeLwHejZ3a4PXW8IjBe9TMjUUVPvQdRhiB580M" +
+            "PWBT4vVWopwaHICPzkQBARiBIJTeeQRizhyJErXMykCBA1gGFBmB/gO8TBLdQyI1G9dJgPo8" +
+            "hxfck7NJDLY0wvhhW0txy7vUQi3MiwU1XyriDwlU1AQB30TZQr4gzPZlCw1QwgTkSwPUT8HQ" +
+            "QAZxG3akxhCQgCzUzx/o28EYDA1ciO4tiwv9DZSgE3mUR0b0lFTpEEakHHnxyv7FDEbMTDrp" +
+            "kNVsRiYwFqhsgcF4gQPUgHAsS2qkWFS51I9QwQe41ckgUVQBzEQMwxLwDCZ6SF81Qgyg00R5" +
+            "ARRYGSRIwDDdXFz5VQzczcHQgJIchFr11LJIhMZgnRecHZoszpq83U3chEtATkwI2prEXUy0" +
+            "Fk8kBedIw2kZBRhEAwF8QlxY2in5FleA/gVbyAk9ZIBYjMVxgUV/5IVi4IMMNAocZECNgcUJ" +
+            "jKFMFlNWMIJXTA8bKIapQAqpQQEYxIpfGAgeDobHnUcmpIBfLMZ5CIgIAAFGeEZnMJZ5UIca" +
+            "fUb8uE9+/YZB6pfDzIZ+vYYtBCYVMEeBUYJ+RVhzGAc9ScAQENZwiIYWfEZk5pcW0Nd2jMYi" +
+            "NKYX4FPYbEd62EFDzFmbhY0XSIB5fNiHJVJlJAgppAApyMCSJch+NEbN8IXu0Mx5vBhjmMcW" +
+            "cI2QQNMNNoAfTQgUNMIqAEEZ2cIHqNUqxIAgpEBUOYLg8M8P2FQN/IAEWJVdCQE97MgfDE4N" +
+            "LMFmQNOoJEjJ/hjiD0RCIwhCZ8RARrSZU72eNGFERAiLI0xSTYiENViSSqKJL6BkTZyJ2qlD" +
+            "URSFUugJTogW5aCFWoiBOiCKWOiW7xAPG3DPGBIKME3PMuUFGzACNp3CKRQTFATVKZiKeJ2C" +
+            "dJFaJgAeMAVPVnKlXgjPiezFhCBZJpDC1MFBAg6JEmXCJOhMEXJIDOJhsIAKmDEWwr2PAJBM" +
+            "Sm2MtWFMM8Ybt0GCMJYI8O0Pu3nC/jRBBNULC5BLLSRARJEAF5CADZhRRYFQSN1LS9EAJMTb" +
+            "D1BRAzQAdnwAt63G/DRLxPAgByULv4RcdiBRDbSTaW6GlS0LdKwPybSl/d1NOq7M/uzVlbHU" +
+            "nEL4gIwxmFWl43OEjHjewypsiztlgWlo1R+kQQzoDDvCkyOUiAVYmR8NUzS55xb8gcBlRImY" +
+            "T1u2j8scS7EAwQ9EhAi8z/qsjNEIAf0xUUo5hJWljVs2jp9BTg6YiUoc2hIK2iRRDlSUyeXc" +
+            "hKOpwx3MgaXlhN7lzgmsEqzNTjFxilhcyqzkTl7AgU0SQDYN3o1BShRIl0IYwb1CSqLAAQGs" +
+            "QekBHghYJYgmj77yymDMyr3uRwKq13zsTulBz37kjBFMAvgM0c74QCYYhEbalAQgKu6hDcao" +
+            "z1PRU2MOR7z9S0uJn79QQrupy4A1wRKcWwIdEELZQpc2/kERmOn95Mu92MC+0EAvHuMf/IaG" +
+            "GORzGKQAfMAPfIAEDGqzxIAWOALaUJtBhpywSpNUtdnfeIEFOEJ2CkEjLIsImg08BYuDmJNN" +
+            "WQD1nJMFUFirGEG12aofpcApCEK0PBUVDMF5WIAiMU0MvJgE2MIQ2NDPCIDIzKBBNIEs0MCE" +
+            "YCAgSZwRGOQPVFl19tCEiMCU0EMjaIYMaAsXGYQWZMSoSMTgSFJPfMm14ucUAppLNOFLRIMK" +
+            "qIM0eA4WfsIJ3MQdlFI03EG8doWjDEqlEQ+inMCQQAordagysVIgnmGF9kykRIHwuOGeDB4v" +
+            "YFMP2BI1AeIcMg8UGA8CQMrM/uwFIw4R7TVVCnBIFqRAem1PT7GasAghOq2PEAhBsoxgs6AN" +
+            "RVCEF3wA8ZVUCF3fgNUClxbwu/zBwtRCBVlLLVjLu1RUBRXBQinU+M3sRenLvzQCPUVCwVGN" +
+            "QUaMY0JmY9aP+rDQcwyq/NkUWU1tyRSWQXhBDUiAF1yjI9RACsATKSDR17lXxLoHyVSVQpDM" +
+            "yfQXRqJmm7mUXwUWDRmBVC0LvS0nC8XAB0wjdrIjQKmIs40tqMxUFtiAhD1EbGQMqWTjyvxN" +
+            "FjAtCQiA/Q7BsUxjzNnnx2aBlwSoMcCE5AioS9AETGRhFuaAT0RDKK0OTiRF71JFFzKFVUQD" +
+            "G+hJ/jWoQFiYklxApVgcgVX25HFxRVUWF1kgwFqAxVKMBVcYBZ4wV29B1+yQBSNQE1oAE3X5" +
+            "2OvxxXeBF3idpXzQw5Sch4/4AwIcwXwMzo+tAlyGDWH1b7RN8YfZwSrEz28ERxHcD24AjGw0" +
+            "jIIJh3I0RxMwR4lQQS0QprUYh3BQQmRuB4h9hgysgjvZARBCQg1g5miETWccs2fYgdBElU+V" +
+            "rlp1xhZIgAyYR1yax3xAU2NYlXwo1ovBgT/E5Xl8BnvSoHkggAzUgAstARe8GCHZFCkIQBMM" +
+            "gVp5wcFtwY104LEMAQuk1aoyGKiQAnZIxB+IJymo7OC0sSPMjB59Sg0A/iFZGYcWYOTfIFVP" +
+            "IZFGuGcW+KeX6OcUkp1K4AQfC3K0poMKpFYprQ5TzAGibQJyvZZt1Q6igAAYOJoYyOumDI/0" +
+            "ZlO9wgWKgkEPJAa59h02EUAPcNovwQV0SS/E6gz20EP43vXunEpCZAKIZIJ+lB733Cg+nJ4l" +
+            "8hWs7N/pQcHIQNaFwN8HbIy2OBvJ7Fv7cJsN/EEDAF/C3EuJtCIltMCWRsy33IstLF8T6AYX" +
+            "2AIXcMEPoED/vAtoLwEkLOPENIA9EZbITowWSIDeJMtGSZVp/E07VVsjGOtzqFVGaIQiQQKU" +
+            "sKNDrI90olM86RCWmc/PuFMKUB3CTQ0VKMuw/mxbOkXMZazqcwzOEqwIFfRBLXiB2iIceZQb" +
+            "aC6BFPxAlKTTZqRADdhA2AhAmSaSI3zUZhxWyURTkphPDKibVCXcZlwGeinSCfXmMHsBVAjo" +
+            "mTgrFNpEtG6CoGFrOmwCT1xOmfiuCizaUgCy7bS16XCFo9CO6MAJonRo9IIAolxv37GSVheP" +
+            "8OQO8iDKGmRv8mDTLyEACCTEENn1fhiIXZ9KkDPdFgjbe7jKzLzMfEAPXQuL+AzLOWEEe/RU" +
+            "10ZH4RA3PTmbFP+bRv3bEDxUzAbYD4D2lrY2cxyfBHNp881LQnFBAtC5ctwLm54pYYnnZiat" +
+            "m34AtS1phJDi9lGY/tYJABtbJMggFu41mUVYm0aUIxDosA5dSJaBig6RzBaHTV85AhuPjMTk" +
+            "cGl8CBw4AiRoAUoPwxBozPapyM39Ww7bAgvYgd9+8OyJhgyIwBAkwBCUhyWo5+B4lVt2HZXk" +
+            "S5Pn8CrgEbNX2R8lVtSSVaUrkpdsCeSYBH5e6x0kwiC8Lgc0YYIyBVFUQ1OogyI/mmutARfK" +
+            "EvEQgOdEJVyIQe7kdfIUU1aOIbkmzxnyApwsnmnNQTFR6IvfGD1I76u8SviOJVFxz6nVtVXB" +
+            "is6cCI2cSCVeBh6mbxDRjCW6TFuyBylojJU12ceqjxBAhNUazL+BFLZwKXFwqRnZALwJ/p+e" +
+            "78+56E8FIV8RoIAUUMFqS4ElFEG7cCk/7UuAQUIkNEC1vYgG/wA7/sDYxgAKOC1qLinGOJsX" +
+            "RKaVUWM8jaAMC4AWBOvW8J5n2OewLMt89JXJWNtAqEyzvI8n0PdBQIsECEGE4N4P2MIS1MCo" +
+            "QIsdiEATKIGyAMH9PGNBMPqBpzFHl6k7pdAJWVkT/MHcX7MASABvW9tFbIxpHlF2WBkNNEJH" +
+            "YiZGbIwAnMnipERI4DFLpMMdSENMmM4orclqzYHs3wRS6IlYjLhUmElTVFqbMIWhYLIytcVS" +
+            "SMMcGIWBUhotMVeliYGjYbKsMT+Ps0EGQIrwmDJ1+WFeZ4BW/vMF8xg0PWSCU5JljfnDKfiD" +
+            "eVkXGEAgFBzBc7amdr1mNpbHTp2HW0IWsyUJRAhHfmlUI9CGcAgHQEwYMkSLliGULNWitIRS" +
+            "rSJNPFEpMtGWpRa1GC6xNESjwSV2HNlZVcPLj0WkZHyQsWiRFpZDVi2KZMPOy5BbvGyB4sVO" +
+            "li01WAL14sWRlxpZSKWwQypLlntCTsrI4o8emCwp/JFa5IUUKX9bavpbZMcLlEVDSMQAQkpC" +
+            "zUUQITFd9KORBEdphjXasiSNSZ+NhiwFrJdGLS6OHC2CZIfGIiFDqNgRsITLEJ40LDWSsUXQ" +
+            "0EZsIQkg1chSE8SLYhCF4rTGT1KI/r1YS6cuXQ51vqxZw437U4A7HHJ8sv3pzicx0Tb5+vRJ" +
+            "mjQVnzaxYTT7UzQC0j6pmBNdDBvs0dmE78HoFC8QBNYQ2LQ92okTm1SoQB7t05oTc06w2bQm" +
+            "SngQbHhhI4r//gODjVPgQMAIOOiBAoooFDTCiCjgcBAOBo0AoUIEHFQwEwQySQGKe7IQxIEt" +
+            "7skkCyhUxMcBFY0gMYUs8LEAMREcaQSIoiTwopFGHBGgkQ+G8oKGRmigwcgfKKGBEhJ+sIEF" +
+            "EppoAgUoa6myiVpYqIWKJqioRUwWmiCBBSm4oCLNJhKgkgRISKBBAEiWGPKHPyyIwYbUBKAh" +
+            "hhrm/DHH/kYEiGEgCT6I4YMPHLkHsaIECHLIDwTQ4oc5hZAACBK3cAAIIRC7556cVvNCAgFY" +
+            "lKARL4AQgASiYpRskUbS4EIAIAZSCwEgfkjtBwOU+MERID4YorG3mgCikSZYWMIREWj4QUl8" +
+            "hKDBshiUYIKERlow449Gslhk0EV8kEBJRzAbopFVlqgzp0WA8GKRLESQgCUg7IihkRysCc6a" +
+            "5QD+RB1rErmjOGvU+cSa4gJ4bhP4VFAHv/UC2EQMAuaQJhrkNuGlOzbWWIMNAgAUD0D1Ht7v" +
+            "kzlUgC8allVwr2PkQmbjhB5sNq8/MKIAAwyR4YgCQXpOoRAOCY04BYoJo4AC/gE4QHBawiwy" +
+            "cdCIpZ122ocsro5xC6pX5Prqe3Ta8WuieLKLKNgQm/QDCQiyQW5LbWAySkoo+YHKCUigkgsu" +
+            "a2mBBTJRaOHKL6lIgItaEyCTBjOXwIyuGCgBKYYfhooSkh/f3lMAEfLVAlwJIEFXgLEciQEx" +
+            "IbWYMws7JFA1R3wc0UIIIZsiZYtIfRp2XrNudUTanLIQIoYYtpjLDmU9kWUIUSX4yJEhuHiX" +
+            "ErQWSf4HSICIhAUqvJCBoyZq2iIGO2riiwV9SzFjCV9TQ1EQ1eHdU966UxPCfB+h6AqILb5G" +
+            "g5C8bhU0SMQgliOGT6QDN+nITXc0ZrMTSOMEYnAP/hsQQA8EbOEUbDhC0HhBDwF5UEBruJCD" +
+            "HACHIxyhQY4QhBEEIQis+CMFEdLPetRzAoDNAWNzUEfLNnEekRXNQKf4z4BAwDMKEQAEPYgC" +
+            "04xQNSNoUEEJChqElnaPLMboRE5BAClihBSkMKUpViMFifQnAhwRBQg1AIkATBU7spiqT1E6" +
+            "EpL+EKW+VelKtmgCF64USBRMSQq1KFzfWtA4FnBhCSyoSJp+YLgfMEtfffOTDYC0rgZ8oBGb" +
+            "g+MH7PAjPy2CBn9IlAgWEak58mQIArBDtTYpJ8wFaQuviRe4tuCIrqTgHq3agg8i9ZM/UKER" +
+            "8ypJpIZJguzZIW+x29Kq/pYwjG1tAQh/aFK1WEAJwCxyCYuwwGSWwBO9FcUMA5CC6CbZBAE6" +
+            "4gPOAoINUBDO8WWyJI0Aye2CVygvFKpdqdlcIwzmQHWoIxpiSJgYQECjoGUiE0fIRBQzAQdi" +
+            "ZBAOnaImHASBDy+IQBD0SFFDzYcUyfgOCpzMAqUsUAN8LIJsK9KPd5ozQQcupzl3uM4mKGhB" +
+            "AYGhggE6AQgs5rMT8GITTITDKcAgwp5BDQ4i8xkj6IGhI1CFKvSgB1UcZFUW0oMUXG1QFOgR" +
+            "hRRgpSv4wIdTRNAUpLAkl0DwASrRJwT02WEgBomBQfBmCUpsyRJ6TcgSwmSLhlzvB36lhC1s" +
+            "/pEGS3hiCUOwRWAsEZkhWCJfhgrJ+bYASvRRliWpGQop5mrPD+DIMnYQARA2tQgZoG8sLBmL" +
+            "S2Angq6AtgY+UK0MgEAVpAhiEUtwie7U1VnKhkQAkfgAKQTwh3BCQgpmGAIQRNArG7yGI1sZ" +
+            "ggHSIBov6Mm1TVCXHdKQhsbQwI+BIcUPDHKW0iziA5Zo1unQF4lVOGILjXCjDNryg/pGwiSs" +
+            "So3qitkI2Qw0HQDbxCeMxgY4EOAE0TjFCaAG4TWURzonyIQDsnDUI5z1QkWDQ0SPIKoLMWgL" +
+            "CboHCMIKBwcgAAxRNEKB2aCCO6ggANmZw8Qe9gmShScKAwIZEdkA/lQQDKjHF4rq0YzgAyMo" +
+            "yKsMOsUpMiSh1dwjimAk0dWWlgkf3AMfPsAHippCIqeISgASwNFphbCjSAmAUTnapxAaEQMa" +
+            "RO4HV0onCh5HAhSggFlVItPfrNS3KVFikH02HJmo4IlSLEEWjFyCJ+JECSpsCxI0aICP1FWp" +
+            "SAiALpxE0vaGgCi1DClSPAklELSwuRisYm32tYAX8AGoLLQxBToRAVRGdatZO0LWIihUq5YQ" +
+            "Jy+MKp6kYQIXGqG3NunIC0143xKUIAWQkEAKltACYq6kOiUowTQ/UEIpakEUAThbAJymAg3s" +
+            "sAQ0KYkGVKDLsDjthdtNMgYS0FsMhBTP/iNtBY5F8kJwjiOGOdCnwE/T4WwiNjBrbOI9C5eG" +
+            "OtaQsOqcYA3dAUHGxPAwaQw8Y/fRzsCbAx0V1NQazSFADhKhn+34cIcEYPAFQbCGmEOoqRri" +
+            "mc8mfAoNIjlGD6XyghQUIQlF9B4iuoeSr5YJLX4tbFlwACn69zUyAjCtiEHto7BelKLE2U5a" +
+            "sEHfmKRnhDAJBVQoTUP+ZsgWXKRKE+DzlcjUhD80gRKMZFac/lAEGsBzWx/oVaGiNITtEQoF" +
+            "QzgfkkCZowbUYM7JrvePGgG6TApgCMb7iKK88IHi2QVROWpUkBwkLpBAIVPLiwEJlrCjkhjk" +
+            "vLJQUhPCawlX/v6gFkPYgizMwIJ0S6EUgZGBACJihx+gyVC26AP4FmGDIaBAXF4gQWCGoAQu" +
+            "sABde7UMKeQVg9UKfyt2aMESMPeDH4QTMVow0o5SJwNxIqc40iiwxh52CjH4ggLGSMcJCAAd" +
+            "+uQ4AAVmjnqigRcAUGPuQx0CIGNWRsbmADq2Yz04hj7mIMjmQDmiQD1Y5sa2A2NkTmfWwEDA" +
+            "QIhCRmigBgSMqMc0aGmWrEKiyAEkJKoWhB6QJkaMYEWU7irO6B6ebgbvgRS2xghSgCm2oMs+" +
+            "BV4SIzGAQlwEACpAiXMAQ/xsYAImYJKupG/4jND0jFkCpwkEp0yysBZIIA2oYErC/hBOBmcJ" +
+            "bEFaymTOqAAFGqBKzqewbGB30kJI6KIRGiuUvEBdACNfjGVyjocGVEUI1oYGGOUDtuAHTEst" +
+            "Zq14gtB40koIoICavCALvOAPIkXw0GsRQOdVGsETDODcjKRvAINLaqARuCDa5GQyyGT8lIAK" +
+            "IIEKCOEM6EJvqoRIfsDdSMMM0uBy0uAMmoAutgsSQCV6YkAEms0V7Y0EkA19GgGT5CUPB2IJ" +
+            "nC3O5iwd0mETCKrBCCAapCGI1sCBcoBjHiYaCqzAsLHALGgTrME9GmwOGmY7DIbjcOrGunEN" +
+            "XuY9eGhlEGBkHCgDmkM2mAOnmiNk3iM8huoEngpkmMgg/hEyyPrjFKAqrKyKHljIyPwhopKK" +
+            "Ir1iIq0qCqgiA6CAQagCDpLiQxYElX5wC2pNjNRoB7WiNVALX8gizuiqsSZrCARisvQKIahg" +
+            "AhAC9QarCSqCIUpDsLSgrzjieqiACgjCEiYAMPoKElZhIOhqJqBxrv6kJyAhMEgDMO5BC1Sn" +
+            "3iRgd9amJT7DDmRgJV4yC+wCLEhBEJ7lCKAAuualQWYLbgIDdGQFCDzBFurCEvwIJOzA7GIA" +
+            "2tyHXf7gsXjCFqjABijBDAghDSrNFkrBEyBhuyKrt6TAANTFFgZACT6isRqrEVIgUTSjsKjA" +
+            "ESAhBqig3M4iX7aCLJFn/D7i/gp/xHwITD/UIWWiIQPgYBOi4Q66AwyWIx3EQAAJKiBXRuDW" +
+            "QwXS4TnuwBqrYzvWgAEpZhsFksGyIxqSShpy4A7OQwUIcg1gBqdCxmeG7OZAYMjggKdMEAFM" +
+            "EAZjUGx4SQYRwAHCakQQwAdAhAVTAGkcYDUQYEBFxSmg4D+95mtQiy1BBbruQVwQA1TQp0dy" +
+            "xAaiJVrEzgsPiQS+5BfNJJsChwa4wEOrhARK4w9IoBZoMXKo5BYTQJ3A7iCERQ4HIy1ooAkq" +
+            "Bc4uZVXy0AeEgAQstBEaQEiGhCQ4CV/k7QPQb9bOqi2KQkmFYEXq64wIhQinZ/GA4PkoT3AQ" +
+            "UQh+/oAL0qIJlMAT5oQK/KgRfMAOqqQZzSAIWCASPJGRyILufkACUIDbaIAFBiAyf+SwtsXe" +
+            "nO0DRDQNhqCdSoEF5uty1GVY7sqeGmEhtEACbGFEJ2spf0APA2D/omPjqgMEtoANbuMTjoAN" +
+            "dOzApOEb52MBa2wBVyYg92PjVOA63CMaqtM7/u865sA+KA4BTmEO/sUIKE4AQSY/0gNkeqAH" +
+            "foY/QnBAgoZnhgwElkzonAbJQsRDpigGl4ZDYLBqng5B6QFBV0MExMwHhcACoAtHBJGNgkQI" +
+            "QEVcFiVShoQgBE9uHuft6ozP+GjPUCBLxKQFuACQWkAL/YYGPGFK0rAR/sJw/PrST6BE/Kjg" +
+            "eLYHMaQF35BEXyhBUC8lK5egWD5CWtCHEEPpVEQALBAjBsZCL3yNXp4llCiRVcJlTuzgKFDt" +
+            "RxppCU7HUBB1MpwrMS/hBkyCn/KFCgzAFrbyFwhBCfJmCDDifJTgDL3ADN5ACpDEEpo2NRoh" +
+            "EnDUWlrRBiyhD5TAEoRnCJqA1bKAJIaAJbwg9Y6nBfzECyxBGvPFDjZtVewAOAMyHR5QwXjh" +
+            "E3whHS7kYRqwOrADYO7gX55DHTTmxqxDIHloDnqAF0KGAHpgDQRQPZJVQKLgBNIhAEYGIUUG" +
+            "ZM4TdAWEF/qDdGOOZ6Cmx8BqQrpmyWAwQRBA/ouwJsmY4udqSAaRQotadzW2RswACK2agr4c" +
+            "AVGWp16Et9RMhZMMz0LFz0p+IAHeDm+K4Ar9SAsHpwVqhQmkgDEn7Xuotl3+VfxQAA1/0Qac" +
+            "rVfKBJMM5Xj+QHQkoFGHQAQERQtowCDA0gZUpRGurVKOxzPKTABwonjeRgQU5VRG5G1KU4Am" +
+            "EbnsqSQ+gjH6ZggcwUvfBxKUgBBIoLhqIQ1sQSTURLkumAUsrQmkQAoeljLihAXeoA88ATKU" +
+            "wAy06XLEDzCmjy6UALvAD3ubQF+OJHZawt3Q5Qc84UhY89yExPCUpCQCI1+qQz4KquSQY6dq" +
+            "QwX+FqdGTsYQlz5a/nU51mPg0GPkGI7BLNc+RIbB2OAD7ePG7gOo0uEOomA9KE6CouM9AqQD" +
+            "zzgD3EPgZM6pELI9P/CMO7I9qyIkrQoMWAisUkCFqqpBoqqRSYFBuKp/RCRG+qcrfAIpvgaV" +
+            "RKB/sieXGhR9Ms9QCoKuLnUg3vZtJ6CvzE5FbaEIbKEWEAtNxAQhssQWYPhyKItyasIOHssO" +
+            "3jYGImEj1KUJJICyBsILUm2f0C3UCCKVyIIuOgvd7AAnzAx9VqJe7EAQaqKiusIOPiAFhqKW";
+
+        private static String splashScreen_gif_base64_4 =
+            "tGIrRCAFJFgrBqKYWIIx86W87HB70yIGbCFYYI8Q2mWebWEY8oUEvnYJXuESzpBqpYCY/i+V" +
+            "KRuD2iwBElrgBs7AEgqLCWyBBEJpKk2lgIRkC4ogDZrgZC3heOhK+Hxkem7zDyLBZIfgBOSj" +
+            "G8fRgRSMETaBAj4BCkAAHBHGF+6AU0fOxuYgEWjK/W4MQODDc81DP2IO/94DPWrGPmQjA0LQ" +
+            "P9ZT5nrs5n5mQNYAZ2ymZ4SmyDJIg6QMBr2ayl7ECPCzhq6iQJ2m6EjEBx7Ka9TKEeglSmeN" +
+            "6j6FmkSgFOkrU4YCUthsCCw0SeDpSfRVb8QvTvSIBdaOBQzgDJTgADzh2/g5Af5g+toF0uYs" +
+            "j6jkF+eOCy52MpLNBhrzRjHJlQwiXwbPC+SGzfKl3LiuLpRE/lA+IM10JEiURUeUBTGIODWy" +
+            "IEfuQbmgORTr9HHUiS+CJfPUzRaWoBQGoKGp4BLUwAzoQkxboG+kwJ5JoA+AwQA6OtiooE4a" +
+            "QAp0b/hyTwtsQbGBFI++y3zBcgIY80dY4AyICY5kGPI4gq62W1+YJTO8AHBx6gEf5o0ZyBoO" +
+            "RAX61v1wo+SsAToDyhjugBtx6sBy9WeA8z4IUgV6oKg2oTgyzj/WoMagAHOzWol6BgHWk0I0" +
+            "ZB87sGeCbKqrIj5BoD4HVEGK7gdJ4cK2iD5jV8qUjMqobEcY8WvuAV2hawtGVsjvehLbZlFs" +
+            "QFD1V/yihOz0BgWSTVrcrkoeIku4/sAMzOAGdLEVSVidakEJUOBJdliIk6RFwS9YzNdaSnEJ" +
+            "JsBPUODaZtgGVudU6AIs9SVf5BdIaOB2dMRPmtHMdMkR8FdVJrRtzG8wJMOyBSALxo8uqI18" +
+            "XAmxlmAVziQBKOEM3uAMCpMy2JkKeG8IVHgAHLprKZUx0sB5WEANfFYLDsAAvs9anm+ji1me" +
+            "TrFOfiD3TMLP0ed1km276O14WMB5SuJOisL99hZjNoZlGGENBNwKHE5hPoENAsAXCCA+JjA6" +
+            "fcEXAsAaxGANrME3CIanDywR2vgOcGrAvvFfZixk5oAXfFUFJqTFkkqJTrwD13PmkpVAemzI" +
+            "iOyJlsx1/ofMq6NAVMKodb/o58AozCSEKYxACETAB3wglxbhrHg8rUY2zXh8GNsCJCZYfwsV" +
+            "nt5OX/MGBSyUbxziIWwhAbatBTZzcbz7FM2kFLbbTJmEC1pgfMCvDSNHziJHbOHwRtHrUuLK" +
+            "BtLMZOEo2QzvfRFRUUKCnWxASFQHEbNAXbJASEjp2uyNKPSGr4ciSeJMTT7CEhIgDSTaCxrH" +
+            "Sn7hFwZaTMOrCUyxFZfADC6hDziUEPrADNTpB8JL0vqAELZbCi4hCNRJTcCwIJilTlggW6Sl" +
+            "F4fhcsBkllLnfOyAQ0kALFmgFDJ4kuIkzoTERwwKOKuBU30jiBSQHrbgjXPg/gAZqDpXRjmk" +
+            "wdwRYA4+UBpA4Dl4IcICwISGjA22w2bswzijIFd7jPcTYQ5SAHJPfBN4Sj1GhqcQ8mfwkd19" +
+            "xmeGHz2mfw0yAAwIwGfoIQMyAKx6pkHgwB+MICu6oiOvqkHI/wiM4C2xpitOgikE4ZfUSLZm" +
+            "Cx+AYNbGYpqBABL2n64AYojAIUuWDLGkxQ5Bg0M82XpoK80NLk24SKEixdYQjEsaUbIkwM4S" +
+            "KnZEWjrY5McQO5YakYjBcuXBGJEWhVxU0ouWDxJiZGmE04sjOxKE4NQCc9WWRV6Yxlg0FOci" +
+            "ID2zCNCipZGILFp+aJHAkmiMJiwolbTTZIhKM8NO/jK5oWQIJBJpqFAZ8uZGiyFKLr1pQsOS" +
+            "kjMohhi4pMRSmjc40qhMY+muliVMqNAYwsWWJTtmCBFSa8mMwUUSvNBY9cERlz+X09ii4ijG" +
+            "Dyo/GnmxsSrGFi9bfgxV8UnFnBNzpH0yvubECWliEBihx8aaNTYE5jy/8+nTmk1z7qWYg+AE" +
+            "AXrS1mRJcSJ8lEak1txjYx2EESMEJJBis8UIGDAnrH3KBEYUCIAAQhRsRBHgfmAUuMYaUYCw" +
+            "HxtrEIhAhfpFQU8UGkZhhIb00IMAHBYaAUUWUDh3jxH3QJHJPfdkMt89WdzjnA/4yOgIEEI4" +
+            "4ogIQGwhghBAOFKDI1sI/iCEF0Mu0ogdjgggAE8fNNLID0v8gCUlKFCCZQM2kGADJSSQwAIX" +
+            "pbCgBAtoeoJmLVQYwEITKLBAwmxpkdAEJR9g2cIPAuAZw5i1UNIIYFqgQAMNTUjwQxOQJOqI" +
+            "BKUNYYNXVUowRCNANJLFBzQA0cAHnj65hJCNPGnDqTQ0JYEADdAAiQSP2oano4380YQnNLhE" +
+            "5x9c3MAECz/8kcYrZbXQhxlmqjHAMH94ksYZVCxBAyF9cEECIe5c0sQSB0x0UgxUNGEJCQao" +
+            "ccYPXAzwi2NvYlubAJXGEIMlKTXSwhmi/WCLEml0pFYMSzjZSAy2FdpEI3fMkY5w3M0B3Ca8" +
+            "/hBwhwoWzxGNNdLcA4Y0IX5yh4CbJMKLOr5ssgYBDqjzCQgBpEMPHAamc4cRp4AABRslrrEF" +
+            "PTUDsQYbJyRyxz0IYiizhkmD0CAIbDzN9An7Mb0hHBlqSHWHD9JjRIVwGJGCc1y72HXZPkCR" +
+            "QokpmCjjIudl4UiJ9wghpBA+DiWACDwW5YhQQtlhcCO7NmIDDVhiSYOYalVKwwRNyNlCE1Kk" +
+            "yYUZTZhxQAuVk7AEC0WQEBgKVDb6Qy0koKAFJbag8MOeJIhp2Q+popDqTo1IAMQPDTjSgABU" +
+            "Cn57lFTWcOoHSIoqRKpTOpKFHadWmagWjrjkkek0KITnwKs0OkQM/v3aEsMqVKTBxRAk2CIt" +
+            "FWYAcwMVvqbRhyWBKWFL6Wq8wQUlnf1SkBZq3jm5JUzgF5roAwpCIwUuLIEmtfnBIn5Qp96k" +
+            "QQqy+AMJfsEFT3ihIC9ZVQwcgZuudKRgLKBCDLKzBoixbBPSiMYmosCdGG7iDokAAT2iIY0M" +
+            "RCER0hBEFoLWsTsg4Gn0IIByPsEGC2QBQtEgANzokYUMCKFDIkDAGoxAigKx4ThZYEOFNnQK" +
+            "BBEgCmuAjoN6AIY19IBABSLjhh40IQdFgRcF6tBz5jMfKMQIbPfwgQ/WloIUOEBFMSJFinyQ" +
+            "BagIAW5QccQi6Fa3SAmAR0+SgKhoMCUb/oiqSjboJJW+ZAMULMESWmpCpbgwGwXWQllcOAO2" +
+            "lGC5JbSABZFpwrSW0AR60aBOY6rTodLiidqkZAhxuhKPTmWHHwBuKA2UQCMm2YiEaOEnH/TK" +
+            "juxWMEcsjxRA8B2hCKcFAdAABU2oxRKu0gTQqcsyozRTwoYgBSmQoBQDaMwSuGCAX5jhJFy4" +
+            "1pveYAYqsEANOHBfo7jABUv8QQqE+FcaDDAAOt1ADX1IyxD+YAmVIKwr0aINCcxggL1YwhZL" +
+            "sAG9mnTS2SiTBJP7g0vSIKxNACcdc9hENOYAsU2cgA3S6CnKNtEyIRCADUaAgzUIAIRMnIAX" +
+            "mcgCKbxAj0z4/gNCpBBAp1KGIChIYBEpmKSkoBC3KPiAFBpiQwCkkQUFBSgDJ1gDGBgBB17s" +
+            "5wRi4AVdycgf5WwCZUETGhjEcAIEsSFAH8oQPY5ADyh8iBSk8EdjY0YKBCQWDqQIJAL8kYLE" +
+            "GkEEi+isF+yQBSDIQAZu28oiRLAFGWQhtKQpzQdgMoSE2AErsT2LQk4ySoVGhhIOWUItpDDS" +
+            "NJDrNYWhAkLUUgSVWKIlkTlJI2IrEBJMYLYf0UL3vJCpg4gFJ3aogR0+awcBUAIsPWqKVKRi" +
+            "B0gkshFLeUo0s7CFJThyCJSg7SJk072RUmIVQ7BFQhtgByrYIpcteMUZhlCKNxRD/gkksAMJ" +
+            "xieLJTA0MpcIwUUigREWdG8IZ5DCEmRxCcRERgpnkEWdWDDg2P4gDZ5ohCUIwYXLKCFZyZQF" +
+            "KkkQG7Rk6gdKUIkduDAMFjh4fB1xxBC80Ii9bqI6nzgBUDcRIBiqAGU9deuDnAaGaNDDH2yI" +
+            "hoOgAAcwhIeNUdhCDZRKAKt9YJrOKZGIUmAENqTABwsCwyc2kQUECIiMB9rQhwrURi2msUBR" +
+            "NpCh36hnOMisayDS2nxScA8gLOIei/CBA1Lwta7dgxQqQkAWEDkkuvmIKDwaUjeHlBohSOBJ" +
+            "UEKKp6CUKN+9ikpaON0uy/UDSpQCdNjyxJlIkIBkeYIE/i1YgieocOJpUQpMGR0TDbjAAgvm" +
+            "+sSmPF0jEGUHSmjyej+4XpMyBYRwIjkGl4kNEIDwgXPHCggxkIAIBDC3aDLKUUJwFVl2xb1C" +
+            "mSEBTWgACaAtC3UJdwlnIMQZhHU5M2jbEmdgwr9x0BgqcIEQBnBMQ24QTCmoIaQT30Ya/nBP" +
+            "LighLbpOw5jO0AcpXI5dlFAU+VKiEs0YDNq4vMEb0rWEfinwcDFYifN+UB4nb4cAwlEBCOCw" +
+            "nSkTgABLFoNPvbyJQaoxZSlbsk4llkY2wGFtP6CXI+hRoDRrKEAQkmxyFraIBynaQITeUIC0" +
+            "9sapBa2NUTgFGGL2oShkmkN6/sbjfByQBREo0QIigLPh/67H1a6WFPDN0Y6mt2qh8I1uTEnN" +
+            "UKaE+U5aN5qVKlx9UTAmRP2AnMQeFwsMkBkWEIYEnqgFKTlXqXT+oQi2YAENbFAEFAyqCX2S" +
+            "3LRj0PnSFc53w7dBlHZFgqyQpt3P3JsQYoBNo1BSVEyygTZf50sBNMpwAuVSKpvwWiXUAqPt" +
+            "4kIkWJqRPwQ4DcMwzC/O8AdbaJzFxLyBGWSBgolawhNmwAEhqCALDwEZkLAKtVBCMcAE1lJ7" +
+            "rzAAFmcGadACgXMSQxAJaUA+McAChKAGsqAmbyAs2jcS9LIIqeIbAtEEa6ACBOBCc9BkVSYG" +
+            "IDAH/im0ZGzACxPCC2zQA0E3V8oRNMohNExTIFxzCvQABF7waQiyHwhyMwhiNWAAByAgHI7w" +
+            "HBgiNmMUaEezIdRxZQpiZXs2IM4RI5nwIUaQBXfkHfcAeKsFBSmCIoaneIonI3DDI52lNzZh" +
+            "eY7kTK61SV0xJZJSOImCX2LyYIWDJQ9mJRBITlwwAWUycg+Bf3hiA1diXIVYLuk0YD9QbOSC" +
+            "AuTkP1LgFRaVKt2CAtM1JVWSKZ9EAwbTh0PRJITDJBJgFUPwJI0ACQLxblzSCDlnA870OCkh" +
+            "GygwLSRwA3XBf33gLxDkL/51A30gLGZQDGowYE0wQQMmBQNwcGeAA5eQ/gY0MAyE4C+AITkJ" +
+            "8AMZCGNUcAbbcAlxYhEJZRA/YC+59oCK0gc4YAD80gcDUGyUECeX0RVWgiXjciWWgFA9NQcq" +
+            "6GRLd0UGIg2bIDTJIR5gMAfUMXQHcgqMcCAE4DRD1SBK02hegyAMAiEfCQIjsgYgox+EBgaD" +
+            "xQYZEGZw0FZPswlMAwZGRBwNkjJpxFdZt1ZXQw9goFgIsmWOxVhQ4A9RAAWkUFic1lhwgDYp" +
+            "QAqcxVmrhQ+B51mSUgP4wCOcRRRCYRRgMQSkYV3OBAklEQkEMQSyED4HoQXx136vcRC1QFJL" +
+            "UBixNUpYQQkr0RHWlUy5phmWkADWtRlasFwR/maKs3USGXUQq+AFYCEU7SYCYCESUGEbiVQD" +
+            "mLIFUcF4eoMlemMHTKFclvBZsuB6liALdgGMvzARBEEFQfAaXPALGcEFIZYGkdAEttAHdTEE" +
+            "BzAAXFALagAMarB/NzAACRRgEdE9tkAIwDUM21AMZgBPN3AGJCUwspAWK2YGAlONnvgDhHAD" +
+            "BoBhlsACS0ACjOISuBIak0g+VCAABcEC29FT4uFky3EgbLBXyjEcwzEhO6VT8KFTNdWCYwQG" +
+            "NyMxDaIcESJmbFAgiWZDH/kcbLAwW4AhBfI1HBKhCyKhcKBDSThofeYgRWVYYrY1VjOSZTNn" +
+            "CAAFPuAdM7I2MbJp/pDWR4j0aS4CN9fUSEIQTlRRiqoYA1NiMFAiTolSOFowAUD6Es72JZwz" +
+            "Ab2kJsAIU6GELV8Cc4aDJ5YwErloXKwBQViCSjYAfn/SFWZQiI4AQTYQTKnyJ5XiG6tiGs8U" +
+            "LwJQmQKgI62yBaLCKD9QGqzxOrc3LoZTBFRgA8RCCJZDJpMzTvgkfjfgB7spfyDFBJQQm2dQ" +
+            "C2cADNpYCn1wCddSLg+FLbUwAH0gUAMADP/XBDegjb1EFilRCwZgCzTQAp4RnmZwqcN1BmRh" +
+            "JfaSUUtgBgpHCYdxA7k2GJ6wY0OwVwV5AimoAi70NAiwZEPVA0YkBha6dAepAumQCIng/gsa" +
+            "4yBJyILWkAMBcAdrwJTzoWhco0MI8HZi1pPW0B13JFlwN3ZJozR6NiFH44QGAgYd0kZlVDYI" +
+            "AGcUagRNNSMo6kdoE4dZYAEpulpbAAT34AiL1Hg8kgWtkhp0Oj2GQ25YYTw7ISpa4HkNEAOE" +
+            "ohbJR06Ek064ZFwVYUpkgWxjQhuVMkosYDpoCjlNUASUQAktIIiJkhI2UAsqQS2BoSi0UQT9" +
+            "qBAw0RFWsic0wCOpgSVG9gEi0Ah081zysqq3M1tx8ih5Ii5KwAW0oybEeT6i8StqIAUTNADe" +
+            "aBdS8AvDQAKv4AeEwAJS4Ad+YAAChT4ZMQTFIhqWegkjdQM4/pAm5zcMrvE6fdA+S6AGauCc" +
+            "5vgGY9IESjBsWDIELRBh8GQXfHEDhGAvXFAMHoYW5QITIlFTRqQCvLCQSwcGghAF1cF0cqVG" +
+            "EoKfGOmEBJAdn4AA56ZnFbIF1MEdmbCYP2IEGaBnIyozCMCS8xEFIAMEw5shNkQgZCRHEeJG" +
+            "gkWvbcQ1GgKGIPIh7TofnqaUn7ZaIgB4RsA85+FHcPOGU0FJEuADT+IkW7U3vrMTWrArPKF9" +
+            "0TQlWoI45EYCxoNLgsM6EAR6kkMRVDBLtvQ5kZgSaTFKQSsno4dKxYQoQoolD8wCjEKIw9Rt" +
+            "PRukmNRzBbMEe2IDsyWL9Ps7WyAE/jQQWpXSFQIgAeNEAi7WQRUhXMmXqnHCAq/JAqVgAAbH" +
+            "b2kASyyQBhrIBWlwCRiwDZVxBjbnGMHGBNJ4CcBgLFIwqWlSCqUQTwfEBMnSBISgCZpgBgMV" +
+            "AsXgpztMLkjxjuoCLBWhBpcwq+vCjP0lBSU0TFRaEFwwusphRMVBAEbgD0OEVUslIYOVHG7V" +
+            "IEawHVGAbuyRAncnVvRwAvQgAnMiAYllrvIRIlDENXfUIT3FaTpkyGkkNWkUoHGUkjyIkQ0S" +
+            "IQTgVhLSVlGgQxiyBqSgQ4VFIkPpD0fJaZillKQABXrklP6wBaRQaUaAD1twHsQsFDJQA3az" +
+            "I6QRXs7j/pUDMRm3JQDYtWFDsAoxYQeu53qqQxv0JZeRcBLgXF8fwRKA2QgOsVz5hRWaMRMK" +
+            "oUztrBIkQJcfEFsgNAQSMMIJgRORMBTjVQOchQ/txiRA8V2OkDoJMYEuRi6yQBCWMAwFMUEJ" +
+            "ZQb2pzqCIQWlQAlScAk3gD5q4AcGCD/DyAKesC6u8QvbcAMolo19MGzoI39DcDlSkLeEAAwJ" +
+            "dAPbIgUxEE8ats+kmRYoEBG4JKmvYBd9kCwuURHjQi/c0wTDEFDkQggz9AlMRxzSgFMHAsnK" +
+            "cQJaZEQEAAa8wGcaogJ3cAL+YIQVUiFpk0IgsAXsxSFhg7x29CFh9CB6pg4n/gA05oohJyIg" +
+            "cIDXGhLL2AuUMlNHMfNGee0cYIgihISiJwoEiJQCXmABWLQF94DMW5EiQEC+WQm1xgMlOaLQ" +
+            "u+I8HzBrpbgrrHErJGBBfxADk5GKnrBAkUADvUInNCBxLYACaVIE9sIlCpVOcUtOu3QXeUoF" +
+            "rBMnxNa/nXTbprQlE1AYlNAtWRLDuUYv5LYEvTM4kAAJMTABNgAENqAFQ2IHrrMrPfoHxcS/" +
+            "FPEH44gtp1dxtjQYJkcDM+Yv9cSMSiCcShA5aHIGilEMtzlQwIAD0NZPXBtjUgAntjAA2wAn" +
+            "Z1Bh/0cFcEEmWhpM6sIEnvi3agBTIu2p/wZTtsAl/rzXBFpwOWdgcofRBw9xBk4WAAVJADfY" +
+            "ViHCND1QgylZZ2MErgxiZ3eAH6rFrwFqNWugDgQABbejdyIyH1czogLSk88RhY2mNT2pNG/0" +
+            "NRkCrxti187xIFjTNf36aIcENkAgAnojsKuVIgq7FXZTvjNCSX1TFKlBFKpGJXT6FbQ2O2pB" +
+            "LgWTJ8atSXJiAwKGbHECju7ojsNGFqGXEeLZKBPgqDCLOnnyA0VQFpKeTofC2xD0J4kyAR8A" +
+            "JqXzAxOwBBy7E43DsTYwBKgTG/ucz1SyCB+wi3WKSYKDJ5nybxmFEajkqJWzf5ZgAGliC2oA" +
+            "F7Zgc9Boz2YQnfDUB6p6/gZvcAke1i/QSVLImRG24Be0RAghJnEPdSYmWD+1+QZt4Qn3Ez/L" +
+            "6OEAyASlUAvjtE6jZH/lg3KXsDpcoAZc4KdcUB3xqR1PU0YZcoMjOa/0KSELIjRsIAafYA2z" +
+            "LAScpkeNDQLpcAJGACXaSw8sKSBhVFhj+CB3sAmO0K50NyB+BuXyoSHIiyFXA+UY4mccHyJk" +
+            "WKKr5QM7ElqBJ/OfthXwBQRws/NuTjd7E14N2zedAk3i5ClhYheIfntkAiuNIk54giVq0iiG" +
+            "ThFLQN3dQt6IUgQtMEtkkhLkhAIt8G/YklA8CxiekCfkUs+YYQmssz1csolaAurgN3qJciqv" +
+            "/rNmWSEEGZsqyTQEHzCCiZNLhyJ6pQBwImcGI7cEpXAAMHsGQQDSZrDSDOZr+oJsKMe1xbAN" +
+            "cJsGv6AGSnARQPz5ZoCOxXB6wAAM+M5QrkSlFQ5ctrmbA+UHb2DAv0CPcSIFynKJuW9L96gs" +
+            "QeAHsdp/n28XaVAK1b1X29EgBSkGBCAjRgACPfDVKal3+yFXLNlGmxAABGATSPkckpUBIPCt" +
+            "RpDZIDKSAnJ3Hdk1hrYG6fAJZMXycOBXGZCSQaPKN2mTE5J1VtYgOkT/hAUQUej5o2eE3sAU" +
+            "UPxBISWiIUNSBEklxGckBSmMGLdk2bJFhKNFQD7W8CLEkcdFEkQI/gEiYcmQIZaWxGg0BJId" +
+            "LT9gWrJkZ0iTlzxhxoRp5+UPS00ixRhi50cMLZR88rQ1YYgtn7VgehpSSyslSzSXNGoi09KE" +
+            "RpQoabE05KVbGxK0QJXK1k4jLXZErJJgJ8YiL3YkCGg0wQsNvT51EvXpqRaVsEtslbJky5YS" +
+            "S2mk4CDEwpaUG324kLBloBQVKZcImbn85oatsmeUpLF05g0hKWaAKSNExdaAG2aoUInBxVNb" +
+            "AzcOWCJ06ZIULjf8mElDhYUUzE1+XLa1pMWrG02UqAF2Q7MaQmeW1OKivkmjH0uaxKAyh400" +
+            "EGDEgGBDgA0cfKBg44Qo2OghvyiiOAWO/jVAgAOMKMCYI501tmjEEQROoQdCBE6Q5hQgGqEB" +
+            "gSiMuIceEkkEA4QIo1jDiAIDmCMLEAoSqMQoSEywRSMQOEggegRykB44EiwyQQR6zNEgBLIw" +
+            "EYF7SLkniyyAyMKRLHyAwkoq7wHCByOq9GHMj7AsUwAJvPBCCy3w+qCRRggb4gca6hSAhhiA" +
+            "iIGGIUhbwgZISGiCBhKoWILQ0f7w5A86m0ABT7V+IKGIJuYkAU5LCK2TBUFJoOEHFn4AtZYm" +
+            "pKDBhiZs+IGKH4oIFSgb6iThBxErrTTWRj74wQayILmTpV0Jo+HCD2pAqoE7KxXAExJIjS8B" +
+            "JkIlIY0WWFCC/hDlWDDjjT5eoYKEG86QwpZfsG3hADXU8LaWy1hjoY83uGDhkhCAme0SYAhR" +
+            "ggRt0yOBC0L6YCENYLbp7AxggKkuASWUMKMIFK6rTgr0mAA4BBzM4GKAbQagoonPbHlsCSq4" +
+            "0M4M6v4lgJcGIyRwjSh8ECILOE4Bg5ETQDACBBAaBAEBCEE4JZpPeBGBhkUyQIDEINeQ5gQh" +
+            "+jICRjiUpgdmJMFgY8c1ZFwkQRhhROBIOMQ+qMcek5w6yCjgcBsOFqNgEcYc1UYgBSMcSOGe" +
+            "MbMQAZ8vfZhScBFSoDKLexZxRKSUHDHpAwE+kPxNvLQYVkRabeAVVTiXuLQBQ2Od/vUDFFCY" +
+            "lYSkTp2VUSoebSKnNYddQlSdUAiLEs1t0AIFJorgYl8bUEDVDEpanYAESmIgddLSUfgBBalE" +
+            "DZ5W7XS3QYDnNRdMcgFssKQRL5CC72Na42vC2TTSYAGFbc3ITArcpLBEiTcMqIX+AdQ/4xIl" +
+            "DC1FCgNYwhNUMMAZPgOM51jiF29QFxeGoQQpnMESVEhDMdRHsDewwBLOIQQJSpEGAw5jCFQY" +
+            "RgBtcQNgvCINZtAEMN5wwlcMgAWOSoMsykIJT9jCElyoRR+oMwRCGOANq5LGGk7wHwSwyG2L" +
+            "WAQpkrSjU2ziBGCgIhxydkSnfQIMJJpDDqyRjgAE4A7W/rBGFEgBpyBloEdXSyMYsLaGBq0h" +
+            "HXfwwY50xLYdIWlq9zBIClA0NboJpEc6g1FBAFlIQGbBcKSgkg9E0KUwFU6RfqtSFoRwSQEI" +
+            "wQsScIQEYuCIN0nuco34E5xoZYdT0YQEH1DVoD7VBEFJqgmz5IINKMEqUREqBj9wypyE1yZH" +
+            "lM8SakHd6xrBwyG0YAI/MBUlWoCCJlDiDywIVen49DwpFE87yGsETz5FibRoQS60ikGqfqAF" +
+            "R9Qkc0NoBCpVRapa/QEFXLAYyM4wgCD0rxSv0Fd0BvCLUpxhG5cwABfSoIQbSIEKlFDCGVjg" +
+            "CdtwgQpmUIMfDOCZAxSQNvDr/kMaDuAHNZyBCmfwgwUpmL40wOZ3Cr0BDn5hBiXgIAR+QKga" +
+            "QpA/etqiZJaoBXWoKYU+KMEWffDDNhSqhAJ+LAq82FrO2KBEIDiCFGA4hY7aRgChQQgOPgoS" +
+            "AVQAhhR4wQhHAlrbxACHLDRiEVtV0dKmliAWsYGKCZKGNO4BhgdFIQMRehDMxgZHvUJoR1dz" +
+            "kFzhSMU1gIEeNdLrXuFwEMiSAg4TYcgiHOIPjOxtaheJCCkWgY+N1EAEW1CcCBaxBUfYISVA" +
+            "8IkXIrGKocTADrKIQWCWYAe/tMUtbYmBJeYClaH4JC8T0AI7a+sTOLFpCDGQAHGKaxQqsCVT" +
+            "PNFC/hHaYovkaU550WxLWYaghSUUNwbQLK5UBOOXc0ZlTXZI520PAwkJLAIS34WJI1bxkhiQ" +
+            "LBJDkMUZ0mcLzySUBT2UQkoJ8QYDtqAPaghXZs5gABK0wAwDMAMLYmiANHjif9UBWQEligPV" +
+            "XIYQmrgBF2TxC1twYQgDFCkFOSOFJrwBGH7QqRkypoQPXuY0SzDoDA3Qhz40QcHAMGAa1DAA" +
+            "JfyEX3PaRII2QaIHYdVCQDiSzY4kJB0B8gTpAIEjWNAC1KXhJ5zyAQIsRANSGERuQVpRiwbr" +
+            "RnWo4GuCZWzdGCvYpSFpsQbBo2LxGCR63IOQf0yB3SwwpXvcYws+AEKi/v12DyjwbUqKc0Sl" +
+            "I5faSrfzMLtswh9I2YhADUtSHyBUEyBRJ1V1jgaH+sMuPfUDAdAETm7RFRVUJYDlOmKXNjjU" +
+            "D/5wGHCeigbVaoB8gocC72inCROwJQ0iZgN+kcBTHyiepiiBAi0IQDsC8NQyZw0nUlsuV3ei" +
+            "RKxJkG0tfNk4njsoq0hghhbMbwDFMIN4+pCe4QRxNgYo4GUuYVRYnkHg2lGCBQ/KYBbcoBju" +
+            "2MZs+nADDM9SCa9IABUWbADxXALJBPwnC4YjhQLXYp4JrQUT0uVfNbjDDwclaWdCBuAhUMIW" +
+            "JBgCG2A2oChMcUWovatcd+Q2KBuySD27wxpI/mEHyAHhklUywhzgAIQPCGGNU2vZKSLEBrYd" +
+            "ZA2fmIMQ2kaknxWSbTpibNbzqDO5JRFJS9NZkHyUgigUOgU+mLsR6C64uXcpzQnJwiIsIIIy" +
+            "pUkAd4o6XuY0pzpdGz5h+UGnO0fOOp16ULr6E6zzawegPAUSsJ7TnmggAErU6QcS0J0AShc8" +
+            "Vf1ACEMoghak/YNaXIoStZjADEEFTtM1QncQA4vuJkBqWB57Vrg13Q/Uchc47ZISjnCLrGcV" +
+            "H5KxQMWSYQJPIigFFgxD4BKsTB+gk5o39I8QaiiGJf5HiOqMMIIpfsMAbGEbd6C/hzfADRf+" +
+            "cNBxHVikaSAEMH5h/uBwgY3/koIl8AQQmh9CcCkzeIUQuIQ+GKnxo6D5ATBJIY0m+BiBaQL9" +
+            "AIMDORC4OQEt8YexkZtN4IUEIZIcYQQjoYc5eBrHEQEfyRBSoAecI4U5oQe8MQKsUSy0i5Cf" +
+            "cSNpmIN7SJE8wiOs+ZEcEQirGju5AZuDUKwSASS1cRIjYAgtORwfaKRG4hIgWIRLEgEJ+AAJ" +
+            "GEM4QTpSu5Nzc71d8pxYyZ7nwRNdASdH+JQ5AQpI2KVP8YIhaAD40An4Eie5oIGoICUbcATi" +
+            "SgtR0Q7jiwFWIgsloITam4nrQYFaoISymAAUID2dkBRKMBmoIKdGGK9BeR5REpW26KW7/pgl" +
+            "G4AJ1gk9GhgZFDiDV2gCLigFA9AYHlohFkgAA/gFJZAFQpgp/9qf2/gWbTGZCdOYV1CDS/g/" +
+            "+nkD6iiZG0Cx/bmEAyiFNwAxj0Mh8/CEYSiFWhSZoJKCWqgoNXAYP9CES+CC1JiOFMM/iQIg" +
+            "4eACM+goFjAAQuiNtpErrYkbRvABC8iCPCqQJGSrTEBBOFABdWADkSAFgYACgzACAggAMMiC" +
+            "D2iAMjubqfERFnmjjvwEaUiBw0KsNSCABskACEksI4KjxDKs/EgsKsoAlNxBeuiBCDmbg4AC" +
+            "eiAFKHCInbyIFPCjqckIjDgt1DqclQBDL4CEVQCfmogBprgt/js4RaKASvqCibnArVVQraYQ" +
+            "rq60g8BIE0CBBKnsJC1YhJoorp34izFchFmJCXFSi/nyHkuQgJgQAKdQDD20iVU8vJgTr8Co" +
+            "S6bwC9VahD6pAaOQrTG0g1ZriiX4g6k4pxjIIQmCDM9ggT9gR/k5IYJiRz+4BDUwKBB6jSYQ" +
+            "lxlqjgHAjtAIF09ggVK4gW+8gWaEKQ7yBPrBDcgQqjSQDxBiKU3IR0rogxmjpxF7BSawBepQ" +
+            "ggu0BOowmXsLDzMwgATigo5KAxcpkqUZkCLZAi9wSJthmrJamz/qka0jAFIQAEfYGbEZkCJK" +
+            "K0fAwSdiAx+JwgTJALbZujlIzxWh/puy2iIiLBFDSpKyOsEfyTNCSpKCAIGgRLRESzQhcLQp" +
+            "yYR7SIFHApMpEQEvuBBMAwLCcATdIYEY4LXPa4RZgYQGIAE+rJNQlLY4EYA73JPr8QISiJwY" +
+            "6NBl0Yl2sgHke5NhQVH4AJ8GAAIa8ATLeQ9ekYA/oII54QK8oJ7i0ZzGIzVesgFSE546ybbP" +
+            "AxU6mVFGxB6ZcFE+JIy7WAITVR1SqwU6oYELRB3rSAOT2Y5SgI2CO4Mm0JZLeIOUSgMg04w0" +
+            "QLD/KRiRUoJLKIb+oYIboLDPIISXOoNiyBcz2B+DISEDLIUBOyjhEFQ1uCg1+IYQCAI95Qxx" +
+            "sVSU4YIg/kCwidpTLviFbVCD6jgDfBsgkzGIrVKjpSGR84QCrUKRLeqRXe2RspGQdOCFe/gA" +
+            "C6CaEkGAObgDELgHLxAAoXsrI9CattmRFdk6FaijneEqrgESFoEQCAE0uNKjJeEzH5katBNQ" +
+            "vOGbw2Ek03qkw8kSEQACkTCJSgOlMdwTmkCBDAWKOHEeUtIOOImm7/IuWGonSIATwtgTIPiD" +
+            "YXGEZq3RPcGLDG0ECfgUGgg9+ICcWJEAXmq87hEexBOe8SKBW6suRwHZGBCC4tod62mTCfge" +
+            "EVGVITgMOFlFqKhRL0CVOVkCSviuVqm2H2hNZFuCNIi9YUgfJdUMA1Co+YG4/gmSTXqUMf7h" +
+            "Ag4CMFvADc84sAcMgkvwA+wIRoErsOvjgtJ4BSUYBhy4jfcjhG1AsP35BQMYBoMyA3AcAlkk" +
+            "hBNixjNoDwSSoAhyj1VZjzpMg7RBGz5jBCH4ACDgDzbQkIOIQW+FEKXZBBVgAxFoBIfcqm/d" +
+            "BHU4gTNKTxjxo4IggBtpGTdyozuQBidBkSTckTqDKzzDETsKGz7byKkBE8PdrLvTQplRJEbK" +
+            "QnkVAkZzHAlIXFAitSH4lKcYnVD8E59YrnL6k0+ZiRS9k9lppzgZwzfhFVH5AEZcBMgRgPD6" +
+            "FJwQw3PSvXMqROwZw1yiUlIigQmYnbIwPhLAifw6/je36B7LeR44IVkd1TV2GgLN4SQ04RPB" +
+            "iA9VMR3lJYEEmBUaWB+g+BegaAKYSilaLLAE+IMluEUuMAAcYNU0MIAGdBiE0hcDvLeDKqoz" +
+            "qIWWcof/A5hi6IMPIoSN+4WuFZeUa7jouA0lkD6gshg97ajx8AM/gDhgCAGBcdXnsIUWkBfI" +
+            "6EQMs5YjA4NozYRMoJoMARwR2Fa+2ioUgQKlgQIokIZ02ISe3II8gpkTSIQ5iIKQKAi3KpG4" +
+            "ct0osEk2SN1otU83UskpWoM5mKIegCMC4MDDmklDss8b8YfBygCC8Ad/MAh/SAGJmIg0q8Kh" +
+            "DMqL2Agm2gIZ2AKwxC0a/nBYSPiA1CJTvxiCVegtqrytJfAt3AJEsOzKrmwiJgoMnGAvLzAt" +
+            "LbBlu4yERchKwdgC37JLLdgCNkHFmSDLqYi5IUCBIQCMpmiKdvpKsGwExFyClPiLRViFVG4K" +
+            "C0gJLwCCorCDrWyL2xoKNrEFTygeS0gAS6A5j6MNEvAEM1CokUKwq32DbTioYQgNoaICJfA+" +
+            "bCwG87CFQU1NzcAWzKC/1CyX1JSCN/jMPtAwfjqooQqOYbAwCzIDQvgG4BDUI14pVgWZ0aAC" +
+            "WeDZQrWE2yAejgYGBIACJxGITEgSKGCiaEUSEGCEIxmrHwGDTUiHHKBIRgCBE0isAgGBOUiE" +
+            "/gDAuiIxaggxAjVSsw2ZmiIBg0+IhkJTQphBEJ5JkEE+wR2ZGsUCpL2BEgQAk7ux3R7ho1qF" +
+            "EhOBArVxtMOptI9ALUcQATsgjDuhlc6jk/MVNxGZUWnztGZ9ij7MlSXwAkdATwGoAV+hCcXm" +
+            "k3SiFcjh2MGgAcgJZTxJp89LJ+wRgC1oQ7dovB8ItVihgXdWXlj7AC3whGFJXkFsBCEQERHB" +
+            "EzRZbBqArz/gw2zDnjqRladYglAJ2g+alQawhIHhE9GMsF9QKHocAPSrxUN9hfuBnxBWjfT5" +
+            "hYLJsDRgghtQAi6gToMR1LTtU2D4hr0VVPSLRvqDDlW9hHoEBnWk/ihlAIZbpGH/go2JYlrs" +
+            "cFOOFg0lGADVmCgGQhF6gIIrtghF5lALSMI/EiRzbWsqMj1L0FcoOAKo/g96yIIli4ETyTOd" +
+            "jAI+kl2g0cE7UIEsyLOBXPEltCMBNVwbsYi0zsEyGxNAsvF3pVC8sSRLMomZUZzE3ZM3Gbxd" +
+            "ijVZkx7C0L040RVS45MaWOwh0Ot2WmwBSKtw/gAv2CX4kAA9EQAgEJW0CMOaNdLzbQQg0MMP" +
+            "iLkafRM2lBQ1lVLN8SVe+p68FgzN2Tw4IcOcoNhcqQEh0AmmyHLNeZPuFRVpy64BWl6d6i3p" +
+            "S4qLLou5HYZF4WgIAjHcEM7yINvkMCAq/igFhJaCgXqDUsAXYLi+1DTp9BGXFiiFboEgPzji" +
+            "1LiEb0iPjXmFM8ihCdyoX5hoirqENADHZjSZAiLAC0wDSMuCGSyII3hpR/BlONgaJUSRp9oa" +
+            "uKlCIU23yRCeRugRRtM9mEiA0rnrgnAASEuBrSo7NXsaIEg7/kRQJVEb23U7uzkkvEmSHBxC" +
+            "uxMBE8HBoLS7L9HCL+lCwBOJK0FPNfmALMhyUOKVu0ABWctzv/4uqMRlxtbyj4iTLH+TTMtQ" +
+            "dgIfR8i2o4mTcsMe9jrfsSALtZKcONEch28EFGAlKp0AgUU2y9ml5FUV1hYCXGmTXeEV+BDE" +
+            "TxGARWCvDxgL/lI7ciqwbcxZAu/NlPigIFZhR5Ei2gOYjQbGDuqA6CCIIDXYBhyYDVfVFxK4" +
+            "JwM4gD51QCXQ7m+gt+Yohv97TQ8L+/SR9W14MFlf++hIj+roKZCjoPpBzmJ4MRv2gwesxxLz" +
+            "DfYwGUowAwRPtLHpET8SgiqPVhjZIiARiF8tmxzZog3fKihwmxj3o0woG4uAyJgpEbRW6yhI" +
+            "XRF4QjeiB5TsGT0WLCqqVj0+CPt8ZHAtEUfGmxTYAkmuQp2MiIuIZIgs/qDMLFLYguUnLS9g" +
+            "IiYSBChATyb6JBnoStPCraFnLc+Kr4boOyZSrecnBW3WfhGoaU7KgsVUifjyAa+k/ti++OXv" +
+            "YacMZaLOc4ua8Aup+ADcCi+fIEyAsBNjyBA7loZ4sbPIzpAYdh46WrRIQqMYi2rQcMgwxiqN" +
+            "BB/a0VIwRhNLNIaQsEUl0o8maYZRsUTFjBRLLF69ssVFyaViSs7QJPQqDRUlN87YUnOJUBoD" +
+            "l4DdeCVlKdM0Un6l4WLr11Eqvy7heHWGELClUvrcEJomjRKrsrj06ZOGxdRXVKS8uTTA1pkb" +
+            "BlQuYVGUCgkScrk0UWMkCz1/9IxAvkfPTiNHUBAgOGUkChTIKeBsNnIPgRF6KeiRhmyEVOd7" +
+            "kB2Uhgw6i78Ui0XUtl36XgoEc05IKI2Z3hoQCOihJp2a/nQUelFiG0nhmrRr6KN9pIAiPbXo" +
+            "e/eyiM6SRUgWIPcsABEhwlGWRR+8bBHgBQiQRl4cOfoxnwYNL/N/CFBDDJAIUN4Q8tVXmSON" +
+            "CEBfDY3Y4YUAEgAhgCM+VNhIA/I1kkUjEjgSQwMLNlKhfDSQ0AgNS3zAoBc0UFJRIxVltMQf" +
+            "NjSRUSMsLfHDDzH4eN8PNDRw0hBaMKgjEDFAGIMAW3wwRCM2TKCgAEOgwGAMK9rgRWFbavGD" +
+            "Jz4GhtgSMbBgBgsspMGULaUQEmcpUhDyxhm1sMBTH2bc4McASvAEjB83pPEUIWesRcgliBLi" +
+            "h52vXLLNoYoOEIQZZwRx6BlF/hEyQCmF4oBVH5f40QcXZ6gxgBlcmKFqGpb8oRMVP8D1BhdU" +
+            "9FGWFHSSco8DytEDBgJHLLIEefS4RkoGkHXWXBTL+kAPFFk8F5pq/ngWnWqZgEYPaItlcY8P" +
+            "khlBGj0qzBGcautG0S5y627W7nPPYVZtCr0Z4UO+5WbhgA/jiuaDCEBk8a94BwsswndZeCEE" +
+            "lELgI98iM+Jnxw/2tSjEi0w64p8AdvBXWcQxeDGQgo58IIQdAnzgCBBeSCCCEC2+5zIQMw/x" +
+            "QZgt2rDgDza01BINAgiRkQ1LhJkzDT9AsoQNJDQBdBND/NyIFilSPaMAAqDY4oyVSfiBABW9" +
+            "t4gA/jYsbZ+PWug8xBKNQEJFC0s0YMcSlpBgSQyRcFGYLWbYwgJfUihhSxpjmaHEMEb9Ykka" +
+            "N1xygxJTvcFUH3mdwYUUr0SV6CWvGADMN8AY0CcwwLzCFVaFa07IrpAOVahewxjQqORp9DFM" +
+            "GkuodGtJBiBqSRA4hKDVDSFsA3gfRnT22GNQZOLhD4v0KhocCMCRSXMIOJBJJmCAhkAUPnQb" +
+            "BQLblfaYbNHR48A9pPiAgGvS+sPbvQhIQ4AQDugGmXPwtgsyzXFOuzBTGt1gJgX6Ele+sJOC" +
+            "LNjGBwXLV3nS4wMhYLA863GEw+6DjwUJ4UQioExlMtQwF3nBajFw2ZNS/miHCjnCDu+xDxDw" +
+            "A4QaOEIEX7MDiBqBQRt8AGRbW6EdYiQAS7RIC0D7wQQ+QAImMWkJKPjBKoaEpCmFiRJLLNmR" +
+            "fEhCAWghjBSZEZIkECaQXEwAZmvEE20whD80wm0k4A/QluAJS9ynCVRYESVisoRTTY4KTTlD" +
+            "5ooCFKMQQgmnitQA2kSIYqiBUK+4XB9KMYBQ/SIvmiAE5EZ3gz4QApQ3KAooz3CANAzgF0r4" +
+            "BSSLEZRivIEmP9lUTMyQFRTQyQCsuoQa+nCWp9zgUkt5hWCygJnrGdAImfCSDO6FGihAoXr0" +
+            "EIT6LmM+epBCWtGBwmOEJRzTLNA294CDdMJ1/kERiCdaRpDGHERwnAHKCzkgGCABQQAC54Ah" +
+            "A/pEDj3lVZsssIY1jkmBPwbqD1KQwjadSQEpRNDQRYigBvhIwRYksgUvkAIIEvACPragUREs" +
+            "4j6kEIR67FDRkEokpVmQwCKyoJCHUE8ipIApSxexipA2QiIvBYlCFmIHEYDIID/9wRAWcpCP" +
+            "uC1KIqGEFuxAgoeswgsE8cISoIrTjd5nIQ59CCRi+FNHbOEhPwCJFhYRCbf9gYsEaYjbLGEH" +
+            "TwzjqX+QRSn0xrs3EcUSbZnaWrhQCiWkSgoHeKTkpHAGKRSDLWlwVOlOd4lfqIEQ3ziUGSL7" +
+            "CymsRQ126lOd8BKp/rYo4Q2vKNwZbiI4T/T1Dz95xV/fMKi2DKBTOmkKFRCjFTPU9hTL4ub1" +
+            "/ucILQggE0dYjHe+8x1/mM8BnYGm/BDggyisIYCQEd8JvDWt0kShB6uxjXrEc49pPfc36jIC" +
+            "CLKVzNEwEDqp0ddxuPMv83hXPO7jnneg4B19NRSsFfTCPbYQkQalB0Ev+tqBPBqi+tRAAP2t" +
+            "j0YbkbIs4KdFDRjQzSDRiC04xBFDq1DLGCQAOKqoMj7SENFc9IM/QAISJ/7DQHD0AxIgDUBn" +
+            "q9GJaTAjo/GnASUqWQwkIIEtpTjEEhDIB4iYnwFxbWwfOLElgLiiMwnAE1RoyB9rQYIf/qBg" +
+            "JyzwxBJK4btTpYEEgL2BFFpwhrwowZZp+AUhZoKDYkwKGFwhS1kmFVm5rPkSbbkE8kAJjE2y" +
+            "+RKfW4sSBFfXNAQBUWfogy65cIMBxPIqwMABUBZNqDTYwhaKtIUUEu1X7DkPNcNlQxY+IIJr" +
+            "3Us05eLFCcAAGQiqBgRrOAEICLAGMAQruqe452pkjRzxpYBgFoAChvJ1jyioYA0ScI10XKMv" +
+            "bUFbgdjJl3LkpxrpREeC0lGYBMXzQAnadzwEWwR+Ulgh/whhQY7A4dDI7RD/zIgiP8JgDVKW" +
+            "QkcAmEG/hRCSPmADMNLgAx+QwHhSRgMbOBhEQ/sACigxMxqo/gwhcaQIwq3m4CzFgBJLQ8FJ" +
+            "LhYDS1h8yT6SUlVbdLGvDU0CQ7CBBFoUAyZFCN8Cb8QPQliRJQwBZTRowsVIwIKl2eCOUyPB" +
+            "WmRxppK0IAZUsAUTZOEJW/TBFk3wRO0QlahdcdovtngKMDCrhG+IVgqlGzSq3mCAs7xhG69Q" +
+            "CjDM3qalnEEqZxhGTfCK2jqt9lBc8AQT+jKTtBCOCoJHlC2GQUgDsCBObzAcHFADgupgzwgC" +
+            "Ac9uEJAJ04wGDAS4B0Y7Qy0jwOETIFAPGDJB3mikAAW9gUIUThAFJ76aFFuAX0JFc5oTzOED" +
+            "CoTMv/Il7XEpUFw+gI0C/SUc0RwH/mDSUSA67/VAIYjggilQj8xi2LB7owwIkPgAfchthwUB" +
+            "Fz9ASiHN5YOkRdSwRAKxSBbwcW+a3zuE9yl/j0vmhSWHMQZaINGPeyYQ+2RElPyWJZjE2tgA" +
+            "DzHEiPmIQPxAElHCAy4NyQhEA1yMhSFJQtgA0FSGGonfxHyfwOlfCAqAj1BBE6jIDyRAE6RI" +
+            "YKDAlaUB4DQBncQVFcBJWxgOIUlBAqwZIbCAJRXDaHEdIVzKI/1gm/hBZJHFNvRBJjnW2qlB" +
+            "MZyBEgwFTqRZ6rBFKEXhWMCZFDgaU2gOoMyFJ+TJDXABEwyAqtBFH+AAmZ3BmyEK6JXG9TAP" +
+            "9CxCDAiB/vls1/uYBhTokwA0gGhg0/mswRzcAwkIQQ9EE601guoZgYKtARxQyxpsAQp8QBbI" +
+            "jHjYV/z0wBp8gHMRlGfohm2IorTYBgI4hmlsBnIQkGmYBkMxj/M5lENNX3dNiwgUhAh4wSJs" +
+            "QQ3IwGpIBD4sxBYIY8w4ghmBVUIshPWllASQwkTEDFBFBEiQjFbxVIQwjBbIQEhsBEh9n8o5" +
+            "xP5FhE5ZBEMwxFqllY0NWQyFycsRyJBtyUNIVQxwYyQMmTQqhBdAgkIMgQzIXjTeIyTkIiSc" +
+            "BEjIBM79wRDYwlpRgd00AeHZEU00QUlkmhnIBFuIxVWcARO0SV4cxQ38oCT5/sVVNAohsJkf" +
+            "AMMA3MBXmAVf3AChHJ4UlMIrvMFlSaFM6k4avMFL0JXhDUMtFAVf/M0klULlqMEZtECbFMMv" +
+            "yAKnmUETcMF3lMs9YMt0jaAMQBPlTUs0xQ8IaAHBbYF3kIIR8AIbLMIQkMImIIAAzAEcDAkU" +
+            "nIBILMY6gYEA1ALBhcvBkAJ4zMEaNEJ2+Ip3fQd5CIEFiAfBfMe/YMeznY9qSNDC3IN60Ad6" +
+            "0Ee6NQi82UENAIFHvRAY/ZcAYIgXjIiGVcZFsQzNbZiEEM2V3MfAcIgAkAzBsMyCbAnRxJB8" +
+            "eNyMQEmACAAJfAyDhJCFBAnXqIiNbY0FAElFSMkf/ryHHzrJCKINBUoJy7hmDPwBDSDJ1qiY" +
+            "Iwgky9VQffSY1lSI/smHVGmBJ3zND1gCzjVBKdhIbVHBEqwnIUUkFdCFLRBdW7BJ6mCWAUQS" +
+            "o8AZISkBIeCAAfTBACwFZIkOMKiBU1zCTCpBH1jWL6CKpjjFofSSUBCOAQzFp5QCfbLAH3HB" +
+            "WpwZGj5STgbBG2iWGdRCS0qBHvlc4YhHZHSGKTrAve2GdmTCAzkAPbDBGkxEXiJMcWhBA7gl" +
+            "PdjAGjjADzjCPcyBFihBvjSCdlTISXVU8LnGJnAi86UTfUgQ9AlBeqCTeogLbPCe8EnQv3Cb" +
+            "BaCpzABVuv1mEFkIaloI/oS1TIg0ggiMTZdkQQ3chxf8wBJIAHkEHNf8QA14VApZgB0gHH7o" +
+            "0IwUmYelECQAgY3FEMQhictogRAkmIR8iHvg1MX4SA852JBwTct8zBuhiCX8wHVGghut6o98" +
+            "iNhQxNjYWJEN3HW2nMCNDWUA18soWWWsgthURMYRVazMiphgGntOnR8NQV0tgSCxwNw5mhqs" +
+            "habRSYjSSUci4VQAU5y0nS/FXeTE3WS1JEm6zpm1HaPYoC3IAqYNQ7tSQS04Duh8SoAeSiks" +
+            "ISH8VSmcgSfg1q2oCRdYguScwTgBFEBF03cIAApIgHRICzmNhgLBwSZwDcAAAT6IQBSAAcuJ" +
+            "/oB2NUEUkIItAEEUgACW3RMKMIEAhGU5iUAKkEdCkUKr+VCYqofAjAcGCcEF2WzBFIy+/AsD" +
+            "jYu4gGmYWgjOhhCIBKp8MEn1rceMAIF7dJUQfAgukiODzIiXxIgQmNH3eQGSrMzK5GK8OYKU" +
+            "2EBXUszWio0I+JfGkIwLpRu7PcSUSMCuegiI/ICPZcSWTMzFJOAS2JgWUEKPnKAWxEDFQYhS" +
+            "NQLSXC3e5iKTNEKC6d8H0EDLxQD3jeCAoEjeUI0n3MqVNYHgvBgX7N0fAYrUjUUfKBLhBJMn" +
+            "lMIvyIUZFINPwO7phBJZgAUhqWEfSEUfFAOZXYImAINQCJofSGin/ozWWqTBUHAuW4RoppzB" +
+            "p8QJU5gBIRiAGbRAW9iS1EVr5qQZIXFB7RzKwYhLf73PPahIDUyLAnGTD0DTc7FBy/jDFlii" +
+            "fZEXCXwAFKyBI6TBtBSBBIABV6IAAQABzr0HCahHugkCeyyCBdyDreWUCNjX7DGPD8AsBKHT" +
+            "PeADKRxUY0RBQflPYyTUveQl9TgUREEUzHDUKuRQSqUUFFxUReUcQq2CAKwHnzZjiOTi2CrE" +
+            "FjSVTtHUh4xtQWDUQkwESMBHSG1BRYQUSEEBSsGMDGiVCKiwj92HSFSjf1iM1UhJQXyMHUDC" +
+            "QbrVQCCET+mff6zCkHVxI0DxQmBx1/rY/kOASB16gdomBALGgCwkwBCchB9FgtQ1AUn8TSk8" +
+            "XRRimmHdwDCgVu8CjlAgBV5ARe0sReRwzlL0wVg80ipFyi9IxekYQJuMBUxq2jkogSdg1lAI" +
+            "UuwMA07M3VrgRBrQVaZZgh4lji38gBm8AiEEpWjZUpycQbjwxsEYEykcp/iKBzaFC3Q5wh9E" +
+            "D/5Ggcs+kAywQRQMgRncoQDgwxoYgSVQgmlIgA9gcwtMQA2lGz2MR1i2WgzcA30o0M3gR3nQ" +
+            "8NZYUMGoB2wwkDkRzAUXDH3ckAXwqYXQB8psTYNc1MVOXLiMJ9d8n4f8G80BUUJYDaHihxBU" +
+            "kYYcSdFuTTLr/o18uNTjbg2IrFDF3MPW4KIWmIiD4JuKAAHTjA2TQALbSADCbY0W0A0F/oGO" +
+            "qUh1/oiNqXD5DacW0IAjoE3OTUidFmNFlIyN3Ydr3scq2BgUleCqQgIJcBkKAAphpEECZM6t" +
+            "aI6aCJIB/MQNvEEj8YQfEMIBTOgAVDIoRRYTNApVAIM7UEXsYqG5ysVYgNYvBIFU3DJTfEpU" +
+            "YFbiZEWIim4aOOUBxE6rRFqnkOSDDkDsVDIX+IMLZwLwyS8UZNyFEcxVdo94PEeYqEYzesHz" +
+            "maUAzI0lRokIkAAT9EyLhKUXHAAKeNR6XGzBNHAUTIAlusx6OMzMtFygYtDAjIsF/qGpESgM" +
+            "BTXmbw9cx6QbyuRUCj2IzayQAilImBSIbKbQqopfA+7cTy9pZWBEilAG+aFMzzQng+jzekSn" +
+            "w9hQUd8M0UAJhF3MSYcIgLzQg3yMjjTE1iAck/jIxQDNGGUgkAwE2/j001jNxczt/R1uN8qm" +
+            "ffAqRWgnJJDMlRjkEDTBEpzJ4UgBFQyD4agESugOfD6OFCQOLAEOm92rLRgAim7kZWWSJuiF" +
+            "I7ddVCgaXyjKWkAOiwrFLyzO47zEp+SJLMDEKj8r3VkCZhHCMHzKNhCSLUQFUxSOLXGBLExd" +
+            "H8QXkB7MbgYHN5VLZ6QP500ROtUHzzkAKWiYFv3bRV0N/ooUlYdQwg9QwkE4DGtOXw75g+ux" +
+            "AAalx83gbDd/6QEDgQ9YgJ5jUDpHnwLdZXfdrNSKTVLvaYlswVilAMoMjFTZkA2kG/5tVAws" +
+            "QQzBOUsB9cBxesowN4M0QEFUhAwFast1zOGmW2XIwG/pR0Tj2wix1Ahx34IQDQwNjX90jI8J" +
+            "59jMiAwByZAsjbAHDdLY2ModHJIgHIQP2RXdW1eO2G9WLg5djM4swRxRDYdPDXyywDA0wR8M" +
+            "g2D8QVGYgZgVqCaPBVcokl2zRVh3cjFsQwgMwBkUAzBIigGw2X8iFpzVTjGg9Wg9koS6Lk7w" +
+            "hVR0KI7v3aUgxeHcqxLEFhn2/kVVdK8UqERfAIoZgJJ4bEEmkEJH3cOSZkGDfHBzTBdoCEIW" +
+            "NA9pRAsCgEEUvA8cOEa5/IvsWVsmyA8QnMa6hMtjIAAI3MEajIvAhKXs6aIuhil2yCIsdvN3" +
+            "GX3L3kt7VNQcN6MuPgTDmLBFIJQdIFQNeL3smZT85nZ95Bw+xsDTMkRCaAT1wIxEAFfY+xRM" +
+            "7TAbz9SdJkRNHVTS81BLIRQpxExIgBRIFXEM+ZePFbGiQtXHLIIMuFXjw1QxsuOQFdX38VQj" +
+            "rIIdyIAaIeB69FRIrbFAeAFGDIGUkYAWNAGVy/QSSIGVWYJWvKdO3EBF8jtSUAHozAXkvIHu" +
+            "Vs4l/mxF2tkJCySa6yRv78YJDsQJnShKnKQk4RiO4ZUCIk+OFMjC5hD/KyTlWiwhxicvVnB4" +
+            "TLyCJbwr8rZJSxreGVwCbzQm8PHPx2+CNdxBANzBJ6iDNdT//KvAHajAJ3zCHNyBNQDEJ2uf" +
+            "7hD8FEBFwTvS5tyxlu5OxHTp1CVUt1CatIR30s0BI0LEontARAARIiTLSUcSTAoQkRKIjyw+" +
+            "ToKcSVLEPUeLLAABIgBfjRpAttSwIESAF0dAGtWApGWVUkcfgHiJEUPEFgk/7HgZ8qORACGO" +
+            "GkmA9COGIyFM7Tii0ajRUqZAHAmgQdeLgEZ07QhYCrdRjL2LBBSNm+Wn/gW/PoFa/fB4KdlF" +
+            "Q2J4TeqlhtchgW1Q1ivAbyNIcLt+1XLVLo0YNEj8gDRaLWgBkLqGFesoBtjXV+3EgGTpT2Ua" +
+            "KEhA+pMmwZ8lTdIo4cJciZI0npqYuWGGyplfhMxoV3OpOaE3v86kOTP+VZo0fQj9+uVnW7EB" +
+            "hIB9k//m0iVCr5SUv8G/PCk8SaO9/n4x4Bcz0jCjD/LOeCU99UrhgkISqIiuOTMMOKM7Qi4Z" +
+            "YMP8oDjiHihISWELUrbIIqcootlkDhg3OeGEO+aIEcYb5zhhR4ZupPFHGdfgcZMZCZijhxlp" +
+            "JCDJOYZcYw0E7pFJiJKqdGQpEYS4JwsJJBCL/sq1qHyJJB8cyAIfohzJwhEvhPDCC5Mc0Sou" +
+            "poTwAR8hPliJBgleqouwRkhwpK1G2vrgrbX08gLRq1iqQYBFRBDsS7h8eomleyiTQCmkGsmi" +
+            "hriQwvIlEXxYcxEv4vJrKS9E+CAGv/SEswYaCg0sqUZs0NW2RX/Qq5EPGtGCWC1+MPaHPof9" +
+            "4Nhgw1LqMThxe0sCGr7azAsaKLF1CVma+MqWNCwhwRIWbOHCE0uGOUMKLmxRUL0gbiBEijNu" +
+            "KOYNQmxRQgpCbiil3/JKEc/fPt4AhpD9Lsl33lfEe8UWh/VV7xUpbIl4XlukmDeNYdSzhZA0" +
+            "qJDilWFskSUNWZjw/oRdiNX75RUFC1YvOy798QEIxHzaYgs7bFiikQQosaSWcSmhZIijmyDh" +
+            "qwQSIIGFHygpIoEfrKahBRYoIaEFEmzJuoUiLCmCBRZasMSMdrlgooUfvKzJB5DGEkKCLGbS" +
+            "cq28RTAiBRbXMvXSFKEAaQssVXVEBBrihAuflHbSa84uG7EgWKoWIQsuq/eU4AMbCB3W0GwF" +
+            "IEkLOyq1o5GQ3rYq9aWECHYLfLRQ1dAPvEgR51TbEgFLLIXY4nABIAWtES+0AEwA1CkNDPXi" +
+            "P9iMLDtQ70wLG2gQgIRgjd3Mjg+aP00LCQzPC7AYhqhBgtysXYKSJbT4w0IuqLBkQflJ/miC" +
+            "hTSaaOKMMyjs7gZKaMEZipEwKRiAEAP4ly0IaIB6qeEG5XlFMYDxhlfgp4A32E57IESIYvTh" +
+            "DB7aTxoMAMIHBVAJ6dnXx6SQQiWU4hX6UUJ3yMMFkm0oDfUygBm4YB5/lUcJWbCAIDJhIrv5" +
+            "QCZbiAkpuJQCIKTgHvdgogWgkAIVZYEUpDACTbbgRJTcAwEnwZmVRGBFOZkKS2L50haqRAqU" +
+            "gIRLjcvCPfBBikUc8USlOtEWSmQ3SWERJCqSARYXAQU7LAKRi7CjHURgR0Si6HYyWEQMFOlI" +
+            "I7CJJYtoixfwgchDmqUrKUjBIRdhKCAsQgZQ0GRIDkmKRlaS/meSjNVO7LAFQXhBRVuQlAi8" +
+            "oMguXjJVEuhKqkKiyFL6SVJwMl5XktKVnVQGkavwE5wEEANhSs8OUbHDEKCiyUZoUnqX82Qj" +
+            "ZOCFbQ4hL7lZzRCaQAUujItosrAEO9PAAktwoRQno0Jz0lAKJTiwFMNwVwqHUQsltKcULZMC" +
+            "wDKYhu0UQztpuMG9SObBV4RwYRA96CVecQMC8TMN+xoGwPZTioGlAWJKuMElbiAFicaMZRqs" +
+            "5xJceoYL2cIAC1oPl5BYxCxAIU1r8gEfifJLI2wRi1nYQk/IJARSOIKOPnBjIC+nJQk8VS2L" +
+            "IEkW3NQlljxmC1RZkQXwkRU22e0l/qdikUmkagQgTEl8bRrVW5X41qDkDAhGGR5o5oiPvXA1" +
+            "WCzpSuMQtZPjrckRkLCVXdSCDztooS4x8NycDLUIpIxOKHlZBD7uodm6tEl6dXkJaAhlByBY" +
+            "oAZ1FUJQRoczv0TKeHFV5mz88gHQ7Gk2vPLLUMjyA242ogHXo8EfaGADLdAAuWEx1OjsEgOg" +
+            "MUUwqBPAEJYwBBKUojU08AQVbEGCCfzhACwggScm5C6Jlue6/THDxcxTCip4gj/I0c5/Dtqe" +
+            "M6SQEDk1z3b656E3PKgY+gGhBwnxoDP0Ib4H6GgpCAQhGBYDZh0tIIQGEJ8DmKc8FAppD/HX" +
+            "HX79Yl4c/iKEEeBAj5m4UpF0zCIWSYGYOpICCnO0WxbzJEULsLGRIoAC4rCkJlYphSa+YxSo" +
+            "vLQTOSEOJUJAJIuQiJi/3aMkcTul4+ZkWrrkDEtsogus3FIZIAjrSnPSkxaIQrkvCaB0RDEn" +
+            "aIzrFxEEpi4kwK0A9sQz0kgALliCsgWyckou8Y7OKZIL56B6O99loS2I2+QWSuKI4zWCWo4I" +
+            "ykqmQmmyqCUGqxDAroT1F6A9Gi7Pm4BxifUYwEQLzbtqxLF6uwRLoMUSTbAEFSD9gyZEIl3t" +
+            "lLUND1Auf3LhbCFNwx/yZzEKBcgSEiNZBDNWMXqpVD8PukSAJ/oGHOynhf1y/mkpDHAOG/5i" +
+            "YySb4Csc+IaNXfQVAzjDhSu20I6dgcEeo1nCPHrfAt+3DzCeyRx9csqhNhEIRoDCWNRyRCiz" +
+            "qXGddMRT17ITtSg5ysyli1YF4CXxfUDJauHlSUxykobLhK1VqlLOeIYYaflkTVxVFM92sgXB" +
+            "eKURSmZUpZZyjy20TleIQ0xhvLCrlVRLC0L4wa68tOpEx+AnqvELyxn5piylBCVoZdFbdJ4q" +
+            "RyARcYXzQlxzJgNnesECjkgtZqZSvFM+TnosKUubsHQV0AoLMFaz2hI6Z4Nt0n0RxHIT56Sn" +
+            "KxocbzUs8MQPLLEEwxde8LK2BYOpwIJhSOd+tegh/gv2OR2NoTCHfYjggsgTsoFNuL4Vk1gB" +
+            "i6EGP7QnYdvhVwhPCEKH/aIYxTCAw9RACCW4Zz8bMsPt09MvcHNhXig0QATZowaIpu0MOeXC" +
+            "GbKQCXpwyW8+0Goj73HURTzflmxqMZuqpKXIWGkppH3d5fwEEix5CTSvE6bwXIJnRm/WbqKM" +
+            "8S5j3GJ/LGJFKjKCzbOISEfeH5d6CZFu5x4OSQQoYxGsyP8WYRUgIYvsSAZURM8GJyR6yVBq" +
+            "YJvsQJUmSQSESQsayQh4JpFIyf9MhB4ekE3sIAtSIJEEIUUWEJeySBBcyY78xI5eyZWU6Y4c" +
+            "6anwL1UQCQj8xA68Lu1E/sMLJCkD7QAS0mKbroIyvimcrCIGpMecImEIMrB9rOUP/kAWhiCk" +
+            "lGAJ0kUJwoUKZCFcqGsJ1ENkFKwUZAFg2OXCpINfNMb2TMZf7tBDHOY8GiZkWGqDQkgKtgNm" +
+            "/OUStqNfGgalbiBiUAqG9iX4+OM/BoCECOgN+uCAzKDA6OUMPGYYXMpizsCtSmRObC7Gmizq" +
+            "ZGIRkAgfoOgl+OysGG1O5OoDEKlwHkN84KLIzo8q0O8Diq4w1oIksGRF/Kbh5ugllOonfqdN" +
+            "fmJOfuInGgF4wG54QsUGYgAf8EEwtuC23up1lKxWlEyT7ghz/KIzKMkCbIturkfsSktN5AI0" +
+            "/moATbgK5MQHH8iuxfgmGJNCBpKCZ+IEMVaELmpgJ8gvKVjllIgCUq4HNMaKMIag4n6CznrO" +
+            "eiChASBBL47rB7Jn05IHLkZDmGKgc07jKlgjIz1hCUjgflqACuaH8tJFnhJgGNypbFByPc7A" +
+            "8aSgXhRECZhAOvapP2CoPRLkoGLvP6bN9rSDEGqvX+IjYfTDg6YNGAaA9opBg6TjKF+Bv/qH" +
+            "PQZxG/IjgRbmFfrgEvzAQSYqCIKAQ3DIoRAsOpgIi1bErd7SB0ykxYDAjlYkE+zGLguHKE5p" +
+            "LLSCjYIly9yEUMgCUoRlFsmMzvRECBrA4iRAjOoyS/KEJThJZy4n/k60cSdS6yduJ1h8oi2Y" +
+            "wna8ACxe5y06kJFewlJWAunYJC5uDiQRK1h6Ayy2DBIoLgjbJObIhJekj5fwYakKjhROZUXw" +
+            "QRj7DP9SgHdOpS9SDhgDcpfk4pRcBZGwxA7ehFOUqFOEZTTyonr6YtWIBSyKq7i4onu0ILge" +
+            "7QeWoLiuYj3xh7pmjTrkSRbkR/Go4FuGgX5OkgpaSkHKcA4xZhPP495uQA0uql8S5qJW6g2s" +
+            "MmHqq4KKIYb0Y14IwQ8eRKJUqoWGoQ9QKtxsz4LMDcQKiKM2iIQc5gb0q1/6g0D6o14M8Qxa" +
+            "bKmGyi59gv8cYYgQIyVSYC3Kqi9xZi2q/ootbisrZvG2Zm4qEGXVbItzHuO2PA4x5sQHjCDs" +
+            "tCpulsoL7CZOLsduyKIqYjEGdGlPNAmyMKdTtEAcBZNR7CAFfMB4xsLolMp2EO0DUCAuAitn" +
+            "DqcuXpN34sKyDCX8Uk4CXWws2ETjoAAfG62SjMwRoECSlApSfOJ27Mh3hJEmxoJT/IQx4ATP" +
+            "HkMI3gLN3mJYkKt0GmEI7K7ivKd64OLvcuPVBMDVUIDyDA8F9Med8JML8scMhoEEHk8KJK85" +
+            "4sud7uuF+qAPeGhe2MMA8MMoHaaAHAph/CX28kMJLgEYtuEG2MNDtsNY8+ui3KOjxOMSbi/2" +
+            "BNGDCihhIOwX/qKjfzhEg/CtW5dVhEroOwrsZuymS+MK/6SoTKRoKUSJd14JZ3xCT9QiCFXC" +
+            "SE9iEfAMz8LiMYSlL8KRc4LwqrZg/1AkAu2IxWhxs0LCRGzJlQjpBVWQFFopHBnwqrKol3Qp" +
+            "lOjhBxkQ/7LgkqY0HFm2l7LoNkUg7RqpBv2IkQZQkfzhAWvQCPxB0qTKH+jBZVfQlUTpAQXB" +
+            "H5xWlxbQH4QWCoCnkvrvlHaQBY1TUnRJAA9pFchUmCQgtfxEAoZgAI/lkNqibXujEVbB1arr" +
+            "DyLhB6iABCJh1hIAP89l1vbJDNZrGNzQbz1h8fLHPLINxHIKZiBoEN91O/wgLC30/g2wFT/E" +
+            "4z+24w0SkSphRioRjD38IF8cZnQJMWYOEcR+gUBsAffQ7Q5fz3FFKGTMACvNg4p0zAj6rBl9" +
+            "B//YCAjqKErZ6I6ywB9monAuxy8UibmEAHVowHsMxXSO5wO0YHrtIi5Cwih0ycgKZ01SCx/4" +
+            "Uas2S1L5Epe8TomEBx9Gox7/IHk/tQGvp/ogki4cE8s+5ebiYk8NMi6UdC++bOmcETdGh0vu" +
+            "4U18YCSyVIlWxAGkymLpKLVszq4QFQHs5hqH4hi5dKl4puEYA0dFADiV4lKkxbKesS3wzCLp" +
+            "bDRsR3oaIFlWrSIfSzAoYz1jgARYY1y+Yn9+AAUYzBby/ocFTOq9FMQm6+mFyKt/jC2E+GVg" +
+            "VG8o7ctDQOg7LoE9+oCC9OMX6nVDPOSDEKgQNYg98uMStoEp88M9+qeDkDJh2mPC0lUN+sAA" +
+            "LsEdUC9hEChhWE8KmCATpUipoMAuJWUsnmpneEcuAtZ3c+JyYlEryq516MzUZMPUDOXU9EQv" +
+            "xnR0CodnykxN3moptBOxFk6JUnF0siDPeOl2fOB4gICbPkDHCpYsSgdxaCONyjTPZIJz9GIC" +
+            "bKviKi4r4IxQtMBiYTElwirRCC4nskCRunQFtaSQsGgkusgkWESpYMI432qE8W8DFwED1aLl" +
+            "JMXhRiIITwlYAAPOhOWUUed5/kzVNu5OcdZT7lxtfsbF8IagBeolJldSClrAW8plCWyBCtLG" +
+            "FizBwNRD285ghfwlh6gSwX7hDSAohIDhPzSIY+olWu9lXqRAgRamKD3kBvJFQtPjP0JmQesF" +
+            "K+8Fg7BVP4xypVjKofqHgRioRS8qE5kIFknhrbiKS3k0JBBnhN/qtNaiLCyrLqoELlzCdiol" +
+            "LKpEVRwy1GwAz/rCtoSlMvREIFXZ5gjujn6CTT7AOC3NERxAid6kstyCKqbQsgDry/IsjVDi" +
+            "y2aRU8TicobgcUDSF+8ECA4LNSKj0voUS4rnxWCiilAECk4l5fpoBYNMqXgn/kBlRUCjVNbK" +
+            "iXwi/siUyHfoQpsbruJOoqgbNgNFgzRqJ1uGejayKzfW8ySpgwSWwH4ojwskjwpklfKaQBYS" +
+            "QELOi4cWhEPUgwt2yIcuQUHeeBsqMYwF7Fz7Z6V+gT22AQeA4aTdwR1wIPYGYDuyNSkHMV9C" +
+            "SKEJwY1jD1/oYxs8RIyNb1oLKIXOYACM9cAQZPYqdKU85BLoAhWlrJrX5FL8VDndemG/hC5O" +
+            "YnTe5HhMB1K0eegQpQGMhXnIUXo+YGzhRAZkIEUY3I5gTJJ0KZckvCjoMouGyXSMSVLsaGUl" +
+            "IHw8Cf9caRXsaAq9wERCAsraBJeU7GqR0Ju64iVSAJmszpGydpf6r5GA/vcIlNZlTcSKXjAk" +
+            "smhwFIl3XOXHH/BjlVNFVizFXvCpQmJqSSnhqhMSJGBsf7oraiPLSWmzk+cKN4PwmMYSxtwW" +
+            "tkvW5sddGs9d2EU94C2kALcUEsCfWghkVopDpMAps/UG5sNe0JW88QPEDuZaYygP/WVdi4FA" +
+            "fuESQkh2LWphTnqCCIyjmtK67zD46KVBaq/AoHs8KJ0QtmSQpqwk5GitgLHR0sh2doKLdmKN" +
+            "OMVhZ/HOykILhkBU97GariJuxQ4S0sm0emcpdIZ7VeTKlIJnJIAnaAUIrucZGQOXR2NytLEw" +
+            "hkcIjACQMc0lbs4RLunmPkCU7c6uBGDfkgeq/kKFk7w6dq52S2xO3TPhYqHoHmI2TYDiki3A" +
+            "At59KE4L2LnEKIBzRe4hL6+MKHSiKMJO7K4TL/CCUN7RBoCFU6aCU66w3/DsauDCupTD8JaA" +
+            "8hrgfuIcn/TnnqLDn1nADGCmCTyhXhrv8QI3EAsIgWLPXhSIv27v9BJIjEvvEnDgEmrPWgOs" +
+            "KfGFRL1yWW9+2vIjwAIMGL4jwNQgX/IluhU9P/IFB9RgAJxbDaI+iucDjKvbQvugJPjIp3CG" +
+            "iVYLRUAiN3dCBEAjk4SxMCYWMoIwLuzgeiDt/KqRBsxUuVQlWj7rCEElk0MQKLRMsomikm/u" +
+            "d0QFN+CaLsTidUxn/iwWHpCy9CgkYP8QLWcGTpUxR5U1s0qyYHRSJSnWhOHciHlnAkuYyAjo" +
+            "4ahQ3wh07pmHqi79CP/oYi9ivKaxbHjr3XCGQkcRQ/pwSatA+FQswPPBU7Zo58q+pE06n1Xb" +
+            "ZwhOtTXQorosIRKWIBL4eSWVwGPoZ0J6GGW4wGRyiLvyp6UgJnU3JlsPxDzITc9P+g1CwFrZ" +
+            "e9JfIYod5uYr92Csdb2Vu7uv7XULkaOmDSC2/QJ26VKxS7+2FTu4reANg5cevoo44JUaJcUI" +
+            "GThD6KAXR45S+LiXJUsKICL8+XAkoqSjlY6A1HAkAYgAIDKEZBEiQMTHD3YkfIjx4QMN/i02" +
+            "HDVaGgMSDQmNJAigIQCSHUeQPioVEaORo5lavdwD4sWHj0ZbREhwlAWI25iOPjbyssgRjaWL" +
+            "vHiN8YOGl7ZRfQhopMXRIgEChJASYTiLCK+OQbLlKQRk1Jik7rHdAmQR57wgs2Rqm8IIAh9G" +
+            "jKTYYjaLESgljWRpBGTLlo9AfGRhCQSKFyG2S24hmYWU57SGSaXYXTlujcqVF80U6qhy0Lkf" +
+            "GglYW1fqFkcClDYa8mMICUs0aPzoO2TJkj9LKLHgkiYNl/tKuFChX6qULS5K2FefEmcowYQU" +
+            "hJxxRn0H3PDLLzeoQcgNBhBCyBsWXgKMH9sQZNAbSgxwiRq//vQBDDBq+IEhMB0SdGJBMGL4" +
+            "BoaE9NFRQWoUVAyLwHwDDA7AhMCiht98c4kfOQ7whoSXvEHQG5coeYmPIehU0khbmESKBZ4B" +
+            "kcUidixSV3SMCUFdYlJ9oEUMAthgxwc2rNWmDT+MN55Uq8Rghw1gClCDBHYIYpsMtkEh5nGL" +
+            "GCfBIoKAiQ8+XmyhKCmk0EWKDBJImkKmedlhhwhb2BFDWotIIAGodImgXKrfpYBAJqRs8Zpx" +
+            "NRxKiiCLiZDCrodmsUWiUPhj2xb0UEqKP8WRAgc9cBxBz7P0QHGscWKK4BkpxaYgZm1ibgHF" +
+            "s6RAsYUAWRpLChDKiZsoPsniE2us/lvIkMVH1lKLKUteLLbFB15Ym5cXdnjhxV12DGHJEEPE" +
+            "sAQV7VmiHwu2LGFLLfTZ0kIaSthihhQLnsECgUq8okR/aZxhS8lvSPHLK33cMKEZhFxCyC8x" +
+            "O/jQG9tgeOQlDZ3ooZMGxUzINq9kaOEvxTip9DaE+AyMOxpeogmMUDdZEDBCv5HRKwOIyHOO" +
+            "BTVNyDf4AOEIZ6SU5BZjid0UkxBjSnauDx8d9gFLbw4mgB2Q0GnHDwUPsRQNdiAWgxeD2aFd" +
+            "DUt54VZ44Jkdg9mR28TWIo3sFikU4OFjwRZekTVYdXPZlN3eiNVmdnhTeYWPCOQaAaptQnzQ" +
+            "UxZjvfSV/h1eginWIr5mAcU9UNTWliP3wDo8FMvCIfxrjN3znQjSI3APPjVIb5sIUEAhhA8i" +
+            "uOUWccORxOX1hg0HKkl1laSttRIAXIPZ9P+6Vg2IC+AF49pB0vp6f6CEJzxBAhs0gQQsIAEV" +
+            "SFALTzTBE/XhQilKhjGMmWFkUqiPFG6goD4UKA1m6MOIisYiHAygI8VYmQgJcpAXQckdRdrR" +
+            "NzTRkB15SENPOshB1DAAHaqhRZeAoYs6BKMOaY1qPeIRiohEEHe8KGYFgdciqIcus93DOJGL" +
+            "i2EOgxLDbAZNhduOVLTQJsL8gS+RIAEkGiEq7VRFL34hiiOIkhevCOwjo3OO/nT8pJRIxe9s" +
+            "WegJYjJXEtvp5QcxEEIjjtIZL9guO7aLixbuVp27tUUIVtoN+0ByD9jxDneL0A0QKuMl8GUy" +
+            "NqoRwj0QkDYfWO8emsHdc7IkAn9lyWw6+U7yHKMreOWGJLopzhTPZpyd7OY46YvLohwRlJpY" +
+            "AAhRqYxU+rdILWjhB+6ZQAN+QAVKLGEINJDYEppAhXJSAYJSMEMaNohBFmwsDSeTwitsYQCV" +
+            "3WAAUqhQk/rANQwV7WYZmhGUNBSzNxgJGFByWkKxVhAjyexnV7MQIao2UKs17SA8i9qJMNCj" +
+            "qF1ChFNqEiFMKDOd1cYHW7CAEE5yNvGxxEwSIKVN/iy3BSGELy4fMJVQTGWDwf0gEkugwR/A" +
+            "ORUaHKZ/g/nBIBEnKgtMBadewMdgFmEBqfjAJqBCTGZOxRrwVOcqIhCCXj6wCCAYpXZaiMoi" +
+            "hBCUyiwFJIpbRCeF852XHK+WLuHebuzgK5CIwAjhepRs1EISL6UADAh4TWpS4xZSxEUxaRNf" +
+            "ljhz09hIyyRry1JxfECKq5YSlpoFnpeq0xnJgIRca2HMR27zqYEdhSnrcQ8JmsCCJvyBnAco" +
+            "JwRLQR8lHECdLCjFK6TAggwaQGRn6GGJ+qAGHLxhIwUtxnEJoQYbZc0gNdyRE3n2ohb+CBjF" +
+            "2IYTcaAhIV2tIRliIXlP/tQQDfVsQyfykTu2UaQiwQhFMfMQRjXkB/Oi1Fe3qWUNRJAdcQks" +
+            "q2RljGndUhlIIkZx5WlEJH5gAxp8oC9L+MEHEGYHLSwiYTUo2HcgcY/4GWcIdriHDOZCioBV" +
+            "Kl+NQstiEkUPMRkKTIvxQqas5akWh2nGVwnVFgEmASjIBl3GoYukjOWPWKmqllOs5V988C1p" +
+            "gYtSxfoVtpxlBDDAAQ4Z8AcYilVLSokJW/RIgbEUs4UmL5kUgL2HP1aDZXdtAR/aWgQUOHur" +
+            "W3VrybLzc5gONeAsCGJeeTGV4vSUl1UgrD1DkEUTLNEEKWQMnrX4jy023Qf7WFplJVPCDdLw" +
+            "/gqYvYJmGVHSGW5QjBvcAEEuu8Eb7nmQI25D1lC60Bv8MKU3rLdHb9DEiS40NofITGYDuMEN" +
+            "XfSiE0ENak2cEovEG1I1FIPXMDpS0wgaX9gEU3ti1U5M1mIToWSHJYmpKWPmEj8BCM7d27QB" +
+            "eSQguPJ4AZyLjEF6BEaDENOArfqrAWKS+m9INCAmQUEJVXzCpkWwCXLhyYIF5ljWp6KEMI/z" +
+            "iu2GEJ5xoURgWhCCTK6iG6V4lqxfQgzwSMIWBFjgHkbIhANig4Ca/zWxsHHN8BDw5evh4x4O" +
+            "sIDQzSYW62UpNZ3UjfSkJ7x7WMBXMvASqLLgANvcY+WqdAxnLsfM/g+MUid1eWZcbDKYpdjE" +
+            "Tj+AT1DLiUAuSMEWDlRgKQL0Ct7S5xUumxDNflEgB1F3QgZ4Q9GK0TWJbqO6CyEIFPWLw200" +
+            "REjtra8TfeQh/TZNITwikjuUQaUkPu1p4n3aN1r0eaf5gSB+8MMMvxGCIvlBE8qQVrionKXk" +
+            "OCKS2wHYIsETt0XxRABF0Q7HY8AXxEACEktwxA+8sARLCGAJeoqTHZaQFOI7rk6Ym0sMhqCX" +
+            "t0LzKh32ig0+cNO7dZL3Y/1qeKAJ/CwoLinhu1t2IAOSv7SEX3HRDtzKGpp51UopLGEtrGEE" +
+            "UWAaRrAsu1EcbmEWphEuRkAPslEcX/JX/rLxEuFSEveQAmORFgroJbsxE9ZCOb/SG9SzG8tx" +
+            "PcCBGBbwG3XzHaMFHjlFFl3BRnMECebRF+dBAudBBUvAAp5ABZaQBlQAMfbxCq9wBun0CvX0" +
+            "C/IkBS2DhGYQaxZSERNSEDiQeNvAEaxWM7/gEIQwERplX03iIhqFXljDUDgDNDdUXzCCAUR0" +
+            "Xs42JUXSUbxGeU1zImpwIn5ACJowAJpgBLBUHHuWGZpxNmexFrUDfMC3Fo3wSEFRFFdBA4cz" +
+            "foMRCY2gVPrWCCTgFyigHUvQiCSgF5YgfHehb08xFTHgCEMgOg2gPxbGL40wOVKhY+DRCDYF" +
+            "HnIxfvEjAX6B/kngwROQtFc25ROFMUpL0RYfIXJfd3WO0xJaRDwwlwXRsmeo4QP4ICupAYFw" +
+            "UICpUXMl0T2ThYFbIAjWiDY5VxIlkTY21T5GMEqGUUstgY62ATwlVxuSURe1JwIB8xEyADC2" +
+            "I3BrJADY9AOzRQnn4U1UIAueQAn0kQbFpQQBoiC+dYTIBUIlUyB90AcnpAQXEiWp912XACRP" +
+            "AiMLMQB+ADU2NIdyOCKEoHrCdno+MnqfpyHuQEPx1Wye1yPKAEMy6Q7u0HrugAHNoAzKwFHf" +
+            "gAE+gkNOwyIf+Q2+kgJlYxurITyM8SsCRxiAYhc9NQSnczeC0wBvYgOH4wVE0Qg2/vCJdxFh" +
+            "5lEwNAAEWrAEMxEJpYJiALOPa5IXMQA8ecEucgk3qdiPlGApnuIZ3GEcimN/h+IptSImEpA2" +
+            "t+GBzFQvWkYtaVMtqoFXcAYFatZkWFYsaJZm/rAsCHAKy5IBDxhmYLAr3+KZYEYP3pIC2RIu" +
+            "yiGbVyR7SUYpw4ObkKWZ4sKZ3ZMspHIbYvIli9CPYNJMq2AHq7AKepGcCWMHf/AHQMgClFAK" +
+            "lpAxb4eE9UQgRpgfRrggAUIzXyhRrLYk92Qhu/YQTaM1D/EhVzNQeqhRPLNrUiM1MxQ1QsJQ" +
+            "8tkjByVtPqIMwMBRHMVewMB5RYKUAxpSWGMkIXB6qgcM/prgB7FnLeDTPrsBBBaQGSpVi9W0" +
+            "FEaxJ/wyBAKnBelBfEthB02wCLLgcAkTizFAArEYinZwF5rYU/BxE33BOH+wONrRRor0Vtnx" +
+            "G9MEHncRHvdWFDYYFQJgfRgaHiQxP3Bxe1/iVbj4EmIVE1lwVToBG55xY45gBA5wPfdAD6xU" +
+            "c2YKc1GwWSAABZlgpjyXCTFXc7BkPcMDiJxhAS0xP7DkFluQCZ1kE1uHPOwiA0/XFqxBKLVh" +
+            "G44zHFoKF28hHkAgiUPQAI0oKtjkHvBhCSSQAChABbRFBRCZAB9EIGZgBsdFIQ8CISdEIUbz" +
+            "C0rQhRnhMiZ5JCdZk0zpBzsS/noF8XrCRpMnAiQz5DOcp0QyI0Ofd6AYgJRFiZTfQJROFJSn" +
+            "x5PfAEM8CaFzWKCcpwkYoAnN6iOasCWOsCVmAzwg4Y5nFhOwQ29sZANeYAM2MBViWWFO8QOU" +
+            "sKTb9AOOQAUDQwLKJziW8IkUNkckcDjpET+UkFZK5aGWQYN3MXZLgRgaZh3rN0dv0gAcd1of" +
+            "AFPaMUqFAzy0OC9GkVNSAUijhDthJRmfI1lAcICA5QBk6hqpIS4wmwJGBgJR8GUIEC3WY4Cq" +
+            "wXNGcHRZgA9Yio6cpTZUZxz7Vxe6IRszR47lQwqY1Ba58Y7nQhY8cXvCdxVqQnw0QALYhHw/" +
+            "gAI7/igLS0C2lgCEtmAJb5cxeFdPhIAgMzIhrHYDw1U0EzJcF5KRODBSUPIGTkSGCBURqNee" +
+            "STk2PiNsWCMhRsI0PhNEIUV5BTqtAUp5HAWgRjKgPumfQlQkzbB5kdsMlFuUGjKuZhNWlFMX" +
+            "w2Q2z4E4XpFvbNSIaeU364FNH/AHdrCJmvh8jVAD6+EIBQQE6yEE6jEYFbakbDKjc1FNU1EY" +
+            "NMFGbfJvjOgVEhADWiAUdTIXdjFJRwEUMAiPidFgh1MdGsYWu/E2bAQqX3E27JKlpQE+mZEC" +
+            "OpFYJHEPBegaCIBYKfBlz2ISpgGBppFYWQqIPiAIGvglvtISnqGBNqU2/mWyE2zxm2mzHFI3" +
+            "WZqVqCK3Vohze4xzOFWxpI0QVJrqtZdqCQH0QJTQBE3QkCETIBvxC1DCdyJ0XHs3IjYSkieE" +
+            "QwURekpyk1cDXiPVItoVkwdKeaf3IkHkIfR1oH+rkxjgkx1yrTBkoD5ZlEVClEQ5lEQplKAr" +
+            "ocqgCRFaX0WyGOFqGMBRF1tUiNZiJg0GFbwIJ0dBA+AEx8f3ByR6iconb5QwvSRQA6sQih8M" +
+            "CXNUGMcJJloABBIQmAAjmDVACvhjKWDCmGHiBVYhAHpGbzo1BHgzmIZZLdVCKeFqLILgLiIg" +
+            "A9QSKYlCKbrim/TAmf6gmYsgZhkwZvQQy4zg/g9dBgewCQZrcArRkgGzjACxTA+wAi3C4w/+" +
+            "UCyLEGeZ0crGchKJkiytLI1bcATYIizdc83Sopl9FoLdYgchFimOkJyQkCfj4U2RFgmyUB6W" +
+            "QAkMY53rdIRmcDIKgiCnlgYctBEVkhEHsV4F4SS1VqxdCDQ5Q4ZNImya4CRPAyOUx13KsA3K" +
+            "IJ80iUN22BDyJdE5+Z/NGpTfMJRICboG3az8+Q1+gAFHHJMSJ7V4KnJ0IROLQEm8N653Ex6l" +
+            "IgHbVEDtIZZ/4ylLwXEd/E0fgALJRwMsoBQoADh34VQ0AAnT+663wRSEM1o2lVb6h3zjthQS" +
+            "QKLhIQGQADjThBgDxxNxHic9piVwt7gFMZV0QACVUmE2uMMuguAAP7dKsEI8bQqnpxAFrmKA" +
+            "cFpzEBgFr+IAaWMarmKmUPBzuPPAxBPXsCQ9jMGnWVADhXpFVzU8JsFZ4YM7KdUZ1JMlYwEw" +
+            "lmEUMdAAMXCWmLoEm3hASzBAQ8hAE6QEFwRCR3gGIjMhyUVdMsNq98VCPLMN2OZ4OnJsl9B6" +
+            "06pRBNEhHfJrSHk1kqeSouc0MRlfRlnFRNmsnHfFCIoB1Y3d1D2U3Kp60wp7nKfdAQEAOw==";
+
+    }
+
+    /** a base64-encoded png image of the scar */
+    public static final String scarPicture_png_base64 = 
+        "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAA" +
+        "CXBIWXMAAAsRAAALEQF/ZF+RAAAAB3RJTUUH0gIYAgsIkuS+AwAAA7lJREFUeJzF1l9MW1Uc" +
+        "wPHvvb3lz/oHOssd0hXo+NNONow102xDSXzoJGiyGBIyDNMlJvjn0USNL74ZY3wnxOgeWIxo" +
+        "9uAmZBrMiNkYYw5xRGBFQdiuzgtl/Gmht7S9PsBM162lGwN+yU3uPTf3nM895/c7OXwOfSKI" +
+        "23Z1yDJt0Ms2hQhwARa3CyHeudkuhJj4sB0IMblhqxH3ALYacV/AViJSArYKkRawFYh1AZuN" +
+        "yAiwmQgpzTsBMMzVv14V00PWQGDhJpd/UNugdwBebIOFRwFINwOGwJHXPH/d9BdYrbXZpaV1" +
+        "ZUPeZ8RuiHnhXAvYNxMghEEMaXO79rjqyLeV6c5iD01Nrz4bevnYwYFVxJkWsG4WQM+B+I6e" +
+        "7/q0SH98srXuYufpk2NGo8Tx5jcF6/unDwyA5oVzmwUAiBlhReg622eG6L7xDuWbjp+iJSVO" +
+        "Kisfl2Z8p8QLENxoYqatgjzQAeFy4QvOs0ZX5bGmeunvWxK//jaNQQpLsPHqSJkDgKT6GrxD" +
+        "xZ6DkimrzFffUFS020dX50B8efaPyeyuN5ZZBW4IkQogqb6Gp0b9IxZJcmKzubCYvfT0KAjx" +
+        "ES3r1LtTQDzxg4dFiA5V/UCRZUtio+p7xTtyfdgqiEXIBW5cew6xv/p5ap8zBVbi8ez5hg9d" +
+        "O8DA6kxtCCG2w8/JiPngrMUgObE/VoHDeYAn9taytBxDRw+/VH9YlHJFR7Dxo4o1xF3xoAiD" +
+        "AMI1mKwJhcYUWa63hkKRyXx7saZliU9WH8XjqSUciWHLs3HrX6x2e9a0212yqEXthWGPNzdy" +
+        "9cdZkpZjCiLvwNvfwxcZAQASEe6JMWXGUeCM6TaKiqoxm8xMB26zM9+OOq2b9lYVWsrLyoWJ" +
+        "G1FzvKo8L3r1/D/JHWeKuCsJE5ZDfPr3/l6BYX14tJvgUhBbfh7TgQDuCkvYarKg3BCIRu1E" +
+        "V3bFUnWeyXLcUwVriE8UWTYUn//2UjTi1/3+iwSDi+zft5NSV27O6EiUwaE/CS8NaMaTLaPp" +
+        "BlgPcd8ybIczDlX9VJFlg73zs0va8qA+PtGHxWTGfz3O4NA483O/aHrriStLEEkHWA/xfw4k" +
+        "xzUYrQmF5hVZPlw42D216CrdPXfbKMzMRliYv6LReqJ/CVZISsBUkSonBHGd7bgZjiqy/F6F" +
+        "qsZCjR87c0w2DF++Nbn25xkNnhg1YGmBQxkD1hDFiix/7VDVxQcdcD1ERkeydphyqOrx5B3z" +
+        "YSMxJzKagTvRDKWKLH/VqKqPwgHAf3HDlnr6flmoAAAAAElFTkSuQmCC";
+
+}
+
+
+
diff --git a/src/org/xwt/Message.java b/src/org/xwt/Message.java
new file mode 100644 (file)
index 0000000..53e26cc
--- /dev/null
@@ -0,0 +1,11 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+/** A simple interface that must be supported by any object inserted into the MessageQueue. */
+public interface Message {
+
+    /** Invoked when the Message is dequeued. */
+    public void perform();
+
+}
+
diff --git a/src/org/xwt/MessageQueue.java b/src/org/xwt/MessageQueue.java
new file mode 100644 (file)
index 0000000..54c2a99
--- /dev/null
@@ -0,0 +1,146 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import java.util.*;
+import org.xwt.util.*;
+import org.mozilla.javascript.*;
+
+/** 
+ *  A singleton class (one instance per JVM) that implements a queue
+ *  for XWT events and threads; this is the main action-scheduling
+ *  loop for the engine.
+ *
+ *  This <i>is</i> the foreground thread -- it
+ *  dequeues Messages when they arrive in the queue. Using this
+ *  thread ensures that the messages are executed in a synchronous,
+ *  in-order fashion.
+ */
+public class MessageQueue extends Thread {
+
+    /** a do-nothing message enqueued to trigger Surfaces to refresh themselves */
+    private static Message refreshMessage = new Message() { public void perform() { } };
+
+    /** enqueues a do-nothing message to get the Surfaces to refresh themselves */
+    public static void refresh() { add(refreshMessage); }
+
+    /** true iff latency-sensitive UI work is being done; signals the networking code to yield */
+    public static volatile boolean working = false;
+
+    private MessageQueue() { start(); }
+
+    /** pending events */
+    private static Queue events = new Queue(50);
+
+    /** the number of objects in the queue that are not subclasses of ThreadMessage */
+    private static volatile int nonThreadEventsInQueue = 0;
+
+    /** the message currently being performed */    
+    static Message currentlyPerforming = null;
+
+    private static MessageQueue singleton = new MessageQueue();
+    private static MessageQueueWatcher watcher = new MessageQueueWatcher();
+
+    // HACK for debugging purposes
+    Function lastfunc = null;
+    Message lastmessage = null;
+
+    /**
+     *  The message loop. Note that non-ThreadMessage Messages get
+     *  priority, and that the queue is emptied in passes -- first we
+     *  look at how many events are in the queue, then perform that
+     *  many events, then render. This has the effect of throttling
+     *  render requests to the appropriate frequency -- when many
+     *  messages are in the queue, refreshes happen less frequently.
+     */
+    public void run() {
+        Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
+        while(true) {
+            try {
+                int size = events.size();
+                for(int i=0; i<Math.max(1, size); i++) {
+                    Message e = (Message)events.remove();
+
+                    // if there are non-ThreadMessage events, perform them first
+                    // we check against Thread instead of ThreadMessage so we don't get link failures in the Shoehorn
+                    if (!(e instanceof Thread)) nonThreadEventsInQueue--;
+                    else if (nonThreadEventsInQueue > 0) {
+                        add(e);
+                        i--;
+                        continue;
+                    }
+                    working = true;
+                    currentlyPerforming = e;
+
+                    // for debugging purposes
+                    lastmessage = e;
+                    if (e != null && e instanceof ThreadMessage) lastfunc = ((ThreadMessage)e).f;
+
+                    e.perform();
+                    currentlyPerforming = null;
+                    working = false;
+                }
+                working = true;
+                for(int i=0; i<Surface.allSurfaces.size(); i++)
+                    ((Surface)Surface.allSurfaces.elementAt(i)).render();
+                working = false;
+
+            } catch (Throwable t) {
+                if (Log.on) Log.log(this, "caught throwable in MessageQueue.run(); this should never happen");
+
+                /* FIXME: causes crashes on Win32
+                if (Log.on) Log.log(this, "    currentlyPerforming == " + currentlyPerforming);
+                if (Log.on) Log.log(this, "    working             == " + working);
+                if (Log.on) Log.log(this, "    lastfunc            == " + lastfunc);
+                if (Log.on) Log.log(this, "    lastmessage         == " + lastmessage);
+                */
+
+                if (Log.on) Log.log(this, "resuming MessageQueue loop");
+            }
+        }
+    }
+
+    /** Adds an event to the queue */
+    public static void add(Message e) {
+
+        // Even though we don't synchronize around these two
+        // statements, it is not a race condition. In the worst case,
+        // nonThreadEventsInQueue undercounts -- it never
+        // overcounts.
+
+        events.append(e);
+        if (!(e instanceof Thread)) nonThreadEventsInQueue++;
+    }
+
+    /** a simple thread that logs a warning if too much time is spent in any given message -- helpful for debugging infinite loops */
+    private static class MessageQueueWatcher extends Thread {
+        long t = System.currentTimeMillis();
+        Message m = null;
+        public MessageQueueWatcher() { start(); }
+        public void run() {
+            while(true) {
+                if (m != null && m == MessageQueue.currentlyPerforming) {
+                    Context cx;
+                    String what;
+                    if (m instanceof ThreadMessage) {
+                        ThreadMessage tm = (ThreadMessage)m;
+                        cx = Context.getContextForThread(tm);
+                        what = "background thread";
+                    } else {
+                        cx = Context.getContextForThread(MessageQueue.singleton);
+                        what = Main.initializationComplete ? "trap" : "script";
+                    }
+                    if (Log.on) Log.log(this, "WARNING: executing same " + what + " for " + (System.currentTimeMillis() - t) / 1000 + "s" +
+                                        " at " + cx.interpreterSourceFile + ":" + cx.interpreterLine);
+                } else {
+                    m = MessageQueue.currentlyPerforming;
+                    t = System.currentTimeMillis();
+                }
+                try { Thread.sleep(Main.initializationComplete ? 1000 : 15000); } catch (Exception e) { }
+            }
+        }
+    }
+    
+}
+
+
+
diff --git a/src/org/xwt/PNG.java b/src/org/xwt/PNG.java
new file mode 100644 (file)
index 0000000..f0d9b7e
--- /dev/null
@@ -0,0 +1,695 @@
+/*
+ * This file was adapted from Jason Marshall's PNGImageProducer.java
+ *
+ * Copyright (c) 1997, Jason Marshall.  All Rights Reserved
+ *
+ * The author makes no representations or warranties regarding the suitability,
+ * reliability or stability of this code.  This code is provided AS IS.  The
+ * author shall not be liable for any damages suffered as a result of using,
+ * modifying or redistributing this software or any derivitives thereof.
+ * Permission to use, reproduce, modify and/or (re)distribute this software is
+ * hereby granted.
+ */
+
+package org.xwt;
+
+import org.xwt.util.*;
+import java.io.*;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.zip.*;
+
+/** Converts an InputStream carrying a PNG image into an ARGB int[] */
+public class PNG implements ImageDecoder {
+
+    // Public Methods ///////////////////////////////////////////////////////////////////////////////
+
+    /** returns the ARGB int[] representing the last image processed */
+    public final int[] getData() { return data; }
+
+    /** returns the width of the last image processed */
+    public final int getWidth() { return width; }
+
+    /** returns the height of the last image processed */
+    public final int getHeight() { return height; }
+
+    /** process a PNG as an inputstream; returns null if there is an error
+        @param name A string describing the image, to be used when logging errors
+    */
+    public static PNG decode(InputStream is, String name) {
+        try {
+            return new PNG(is, name);
+        } catch (Exception e) {
+            if (Log.on) Log.log(PNG.class, e);
+            return null;
+        }
+    }
+
+    private PNG(InputStream is, String name) throws IOException {
+        underlyingStream = is;
+        target_offset = 0;
+        inputStream = new DataInputStream(underlyingStream);
+        
+        // consume the header
+        if ((inputStream.read() != 137) || (inputStream.read() != 80) || (inputStream.read() != 78) || (inputStream.read() != 71) ||
+            (inputStream.read() != 13) || (inputStream.read() != 10) || (inputStream.read() != 26) || (inputStream.read() != 10)) {
+            System.out.println("PNG: error: input file " + name + " is not a PNG file");
+            data = new int[] { };
+            width = height = 0;
+            return;
+        }
+        
+        DONE: while (!error) {
+            if (needChunkInfo) {
+                chunkLength = inputStream.readInt();
+                chunkType = inputStream.readInt();
+                needChunkInfo = false;
+            }
+            
+            switch (chunkType) {
+            case CHUNK_bKGD: inputStream.skip(chunkLength); break;
+            case CHUNK_cHRM: inputStream.skip(chunkLength); break;
+            case CHUNK_gAMA: inputStream.skip(chunkLength); break;
+            case CHUNK_hIST: inputStream.skip(chunkLength); break;
+            case CHUNK_pHYs: inputStream.skip(chunkLength); break;
+            case CHUNK_sBIT: inputStream.skip(chunkLength); break;
+            case CHUNK_tEXt: inputStream.skip(chunkLength); break;
+            case CHUNK_zTXt: inputStream.skip(chunkLength); break;
+            case CHUNK_tIME: inputStream.skip(chunkLength); break;
+                
+            case CHUNK_IHDR: handleIHDR(); break;
+            case CHUNK_PLTE: handlePLTE(); break;
+            case CHUNK_tRNS: handletRNS(); break;
+                
+            case CHUNK_IDAT: handleIDAT(); break;
+                
+            case CHUNK_IEND: break DONE;
+            default:
+                System.err.println("unrecognized chunk type " +
+                                   Integer.toHexString(chunkType) + ". skipping");
+                inputStream.skip(chunkLength);
+            }
+            
+            int crc = inputStream.readInt();
+            needChunkInfo = true;
+        }
+    }
+
+    // Chunk Handlers ///////////////////////////////////////////////////////////////////////
+
+    /** handle data chunk */
+    private void handleIDAT() throws IOException {
+        if (width == -1 || height == -1) throw new IOException("never got image width/height");
+        switch (depth) {
+        case 1: mask = 0x1; break;
+        case 2: mask = 0x3; break;
+        case 4: mask = 0xf; break;
+        case 8: case 16: mask = 0xff; break;
+        default: mask = 0x0; break;
+        }
+        if (depth < 8) smask = mask << depth;
+        else smask = mask << 8;
+
+        int count = width * height;
+
+        switch (colorType) {
+        case 0:
+        case 2:
+        case 6:
+        case 4:
+            ipixels = new int[count];
+            pixels = ipixels;
+            break;
+        case 3:
+            bpixels = new byte[count];
+            pixels = bpixels;
+            break;
+        default:
+            throw new IOException("Image has unknown color type");
+        }
+        if (interlaceMethod != 0) multipass = true;
+        readImageData();
+    }
+
+    /** handle header chunk */
+    private void handleIHDR() throws IOException {
+        if (headerFound) throw new IOException("Extraneous IHDR chunk encountered.");
+        if (chunkLength != 13) throw new IOException("IHDR chunk length wrong: " + chunkLength);
+        width = inputStream.readInt();
+        height = inputStream.readInt();
+        depth = inputStream.read();
+        colorType = inputStream.read();
+        compressionMethod = inputStream.read();
+        filterMethod = inputStream.read();
+        interlaceMethod = inputStream.read();
+    }
+
+    /** handle pallette chunk */
+    private void handlePLTE() throws IOException {
+        if (colorType == 3) {
+            palette = new byte[chunkLength];
+            inputStream.readFully(palette);
+        } else {
+            // Ignore suggested palette
+            inputStream.skip(chunkLength);
+        }
+    }
+
+    /** handle transparency chunk; modifies palette */
+    private void handletRNS() throws IOException {
+        int chunkLen = chunkLength;
+               if (palette == null) throw new IOException("tRNS chunk encountered before pLTE");
+               int len = palette.length;
+        if (colorType == 3) {
+            transparency = true;
+
+            int transLength = len/3;
+            byte[] trans = new byte[transLength];
+            for (int i = 0; i < transLength; i++) trans[i] = (byte) 0xff;
+            inputStream.readFully(trans, 0, chunkLength);
+
+            byte[] newPalette = new byte[len + transLength];
+            for (int i = newPalette.length; i > 0;) {
+                newPalette[--i] = trans[--transLength];
+                newPalette[--i] = palette[--len];
+                newPalette[--i] = palette[--len];
+                newPalette[--i] = palette[--len];
+            }
+            palette = newPalette;
+
+        } else {
+            inputStream.skip(chunkLength);
+        }
+    }
+
+    /// Helper functions for IDAT ///////////////////////////////////////////////////////////////////////////////////////////
+
+    /** Read Image data in off of a compression stream */
+    private void readImageData() throws IOException {
+        InputStream dataStream = new SequenceInputStream(new IDATEnumeration(this));
+        DataInputStream dis = new DataInputStream(new BufferedInputStream(new InflaterInputStream(dataStream, new Inflater())));
+        int bps, filterOffset;
+        switch (colorType) {
+          case 0: case 3: bps = depth; break;
+          case 2: bps = 3 * depth; break;
+          case 4: bps = depth<<1; break;
+          case 6: bps = depth<<2; break;
+          default: throw new IOException("Unknown color type encountered.");
+        }
+
+        filterOffset = (bps + 7) >> 3;
+
+        for (pass = (multipass ? 1 : 0); pass < 8; pass++) {
+            int pass = this.pass;
+            int rInc = rowInc[pass];
+            int cInc = colInc[pass];
+            int sCol = startingCol[pass];
+            int val = (width - sCol + cInc - 1) / cInc;
+            int samples = val * filterOffset;
+            int rowSize = (val * bps)>>3;
+            int sRow = startingRow[pass];
+            if (height <= sRow || rowSize == 0) continue;
+            int sInc = rInc * width;
+            byte inbuf[] = new byte[rowSize];
+            int pix[] = new int[rowSize];
+            int upix[] = null;
+            int temp[] = new int[rowSize];
+            int nextY = sRow;               // next Y value and number of rows to report to sendPixels
+            int rows = 0;
+            int rowStart = sRow * width;
+
+            for (int y = sRow; y < height; y += rInc, rowStart += sInc) {
+                rows += rInc;
+                int rowFilter = dis.read();
+                dis.readFully(inbuf);
+                if (!filterRow(inbuf, pix, upix, rowFilter, filterOffset)) throw new IOException("Unknown filter type: " + rowFilter);
+                insertPixels(pix, rowStart + sCol, samples);
+                if (multipass && (pass < 6)) blockFill(rowStart);
+                upix = pix;
+                pix = temp;
+                temp = upix;
+            }
+            if (!multipass) break;
+        }
+        while(dis.read() != -1) System.err.println("Leftover data encountered.");
+
+        // 24-bit color is our native format
+        if (colorType == 2 || colorType == 6) {
+            data = (int[])pixels;
+            if (colorType == 2) {
+                for(int i=0; i<data.length; i++)
+                    data[i] |= 0xFF000000;
+            }
+
+        } else if (colorType == 3) {
+            byte[] pix = (byte[])pixels;
+            data = new int[pix.length];
+            for(int i=0; i<pix.length; i++) {
+                if (transparency) {
+                    data[i] =
+                        ((palette[4 * (pix[i] & 0xff) + 3] & 0xff) << 24) |
+                        ((palette[4 * (pix[i] & 0xff) + 0] & 0xff) << 16) |
+                        ((palette[4 * (pix[i] & 0xff) + 1] & 0xff) << 8) |
+                        (palette[4 * (pix[i] & 0xff) + 2] & 0xff);
+                } else {
+                    data[i] =
+                        0xFF000000 |
+                        ((palette[3 * pix[i] + 0] & 0xff) << 16) |
+                        ((palette[3 * pix[i] + 1] & 0xff) << 8) |
+                        (palette[3 * pix[i] + 2] & 0xff);
+                }
+            }
+
+        } else if (colorType == 0 || colorType == 4) {
+            if (depth == 16) depth = 8;
+            int[] pix = (int[])pixels;
+            data = new int[pix.length];
+            for(int i=0; i<pix.length; i ++) {
+                if (colorType == 0) {
+                    int val = (pix[i] & 0xff) << (8 - depth);
+                    data[i] =
+                        0xFF000000 | 
+                        (val << 16) | 
+                        (val << 8) | 
+                        val;
+                } else {
+                    int alpha = (pix[i] & mask) << (8 - depth);
+                    int val = ((pix[i] & smask) >> depth) << (8 - depth);
+                    data[i] =
+                        (alpha << 24) |
+                        (val << 16) | 
+                        (val << 8) | 
+                        val;
+                }
+            }
+        }
+
+    }
+
+    private void insertGreyPixels(int pix[], int offset, int samples) {
+        int p = pix[0];
+        int ipix[] = ipixels;
+        int cInc = colInc[pass];
+        int rs = 0;
+
+        if (colorType == 0) {
+            switch (depth) {
+            case 1:
+                for (int j = 0; j < samples; j++, offset += cInc) {
+                    if (rs != 0) rs--;
+                    else { rs = 7; p = pix[j>>3]; }
+                    ipix[offset] = (p>>rs) & 0x1;
+                }
+                break;
+            case 2:
+                for (int j = 0; j < samples; j++, offset += cInc) {
+                    if (rs != 0) rs -= 2;
+                    else { rs = 6; p = pix[j>>2]; }
+                    ipix[offset] = (p>>rs) & 0x3;
+                }
+                break;
+            case 4:
+                for (int j = 0; j < samples; j++, offset += cInc) {
+                    if (rs != 0) rs = 0;
+                    else { rs = 4; p = pix[j>>1]; }
+                    ipix[offset] = (p>>rs) & 0xf;
+                }
+                break;
+            case 8:
+                for (int j = 0; j < samples; offset += cInc) ipix[offset] = (byte) pix[j++];
+                break;
+            case 16:
+                samples = samples<<1;
+                for (int j = 0; j < samples; j += 2, offset += cInc) ipix[offset] = pix[j];
+                break;
+            default: break;
+            }
+        } else if (colorType == 4) {
+            if (depth == 8) {
+                for (int j = 0; j < samples; offset += cInc) ipix[offset] = (pix[j++]<<8) | pix[j++];
+            } else {
+                samples = samples<<1;
+                for (int j = 0; j < samples; j += 2, offset += cInc) ipix[offset] = (pix[j]<<8) | pix[j+=2];
+            }
+        }
+    }
+
+    private void insertPalettedPixels(int pix[], int offset, int samples) {
+        int rs = 0;
+        int p = pix[0];
+        byte bpix[] = bpixels;
+        int cInc = colInc[pass];
+
+        switch (depth) {
+          case 1:
+            for (int j = 0; j < samples; j++, offset += cInc) {
+                if (rs != 0) rs--;
+                else { rs = 7; p = pix[j>>3]; }
+                bpix[offset] = (byte) ((p>>rs) & 0x1);
+            }
+            break;
+          case 2:
+            for (int j = 0; j < samples; j++, offset += cInc) {
+                if (rs != 0) rs -= 2;
+                else { rs = 6; p = pix[j>>2]; }
+                bpix[offset] = (byte) ((p>>rs) & 0x3);
+            }
+            break;
+          case 4:
+            for (int j = 0; j < samples; j++, offset += cInc) {
+                if (rs != 0) rs = 0;
+                else { rs = 4; p = pix[j>>1]; }
+                bpix[offset] = (byte) ((p>>rs) & 0xf);
+            }
+            break;
+          case 8:
+            for (int j = 0; j < samples; j++, offset += cInc) bpix[offset] = (byte) pix[j];
+            break;
+        }
+    }
+
+    private void insertPixels(int pix[], int offset, int samples) {
+        switch (colorType) {
+        case 0:
+        case 4:
+            insertGreyPixels(pix, offset, samples);
+            break;
+        case 2: {
+            int j = 0;
+            int ipix[] = ipixels;
+            int cInc = colInc[pass];
+            if (depth == 8) {
+                for (j = 0; j < samples; offset += cInc)
+                    ipix[offset] = (pix[j++]<<16) | (pix[j++]<<8) | pix[j++];
+            } else {
+                samples = samples<<1;
+                for (j = 0; j < samples; j += 2, offset += cInc)
+                    ipix[offset] = (pix[j]<<16) | (pix[j+=2]<<8) | pix[j+=2];
+            }
+            break; }
+        case 3:
+            insertPalettedPixels(pix, offset, samples);
+            break;
+        case 6: {
+            int j = 0;
+            int ipix[] = ipixels;
+            int cInc = colInc[pass];
+            if (depth == 8) {
+                for (j = 0; j < samples; offset += cInc) {
+                    ipix[offset] = (pix[j++]<<16) | (pix[j++]<<8) | pix[j++] |
+                                    (pix[j++]<<24);
+                }
+            } else {
+                samples = samples<<1;
+                for (j = 0; j < samples; j += 2, offset += cInc) {
+                    ipix[offset] = (pix[j]<<16) | (pix[j+=2]<<8) | pix[j+=2] |
+                                    (pix[j+=2]<<24);
+                }
+            }
+            break; }
+          default:
+            break;
+        }
+    }
+
+    private void blockFill(int rowStart) {
+        int counter;
+        int dw = width;
+        int pass = this.pass;
+        int w = blockWidth[pass];
+        int sCol = startingCol[pass];
+        int cInc = colInc[pass];
+        int wInc = cInc - w;
+        int maxW = rowStart + dw - w;
+        int len;
+        int h = blockHeight[pass];
+        int maxH = rowStart + (dw * h);
+        int startPos = rowStart + sCol;
+        counter = startPos;
+
+        if (colorType == 3) {
+            byte bpix[] = bpixels;
+            byte pixel;
+            len = bpix.length;
+            for (; counter <= maxW;) {
+                int end = counter + w;
+                pixel = bpix[counter++];
+                for (; counter < end; counter++) bpix[counter] = pixel;
+                counter += wInc;
+            }
+            maxW += w;
+            if (counter < maxW)
+                for (pixel = bpix[counter++]; counter < maxW; counter++)
+                    bpix[counter] = pixel;
+            if (len < maxH) maxH = len;
+            for (counter = startPos + dw; counter < maxH; counter += dw)
+                System.arraycopy(bpix, startPos, bpix, counter, dw - sCol);
+        } else {
+            int ipix[] = ipixels;
+            int pixel;
+            len = ipix.length;
+            for (; counter <= maxW;) {
+                int end = counter + w;
+                pixel = ipix[counter++];
+                for (; counter < end; counter++)
+                    ipix[counter] = pixel;
+                counter += wInc;
+            }
+            maxW += w;
+            if (counter < maxW)
+                for (pixel = ipix[counter++]; counter < maxW; counter++)
+                    ipix[counter] = pixel;
+            if (len < maxH) maxH = len;
+            for (counter = startPos + dw; counter < maxH; counter += dw)
+                System.arraycopy(ipix, startPos, ipix, counter, dw - sCol);
+        }
+    }
+
+    private boolean filterRow(byte inbuf[], int pix[], int upix[], int rowFilter, int boff) {
+        int rowWidth = pix.length;
+        switch (rowFilter) {
+        case 0: {
+            for (int x = 0; x < rowWidth; x++) pix[x] = 0xff & inbuf[x];
+            break; }
+        case 1: {
+            int x = 0;
+            for ( ; x < boff; x++) pix[x] = 0xff & inbuf[x];
+            for ( ; x < rowWidth; x++) pix[x] = 0xff & (inbuf[x] + pix[x - boff]);
+            break; }
+        case 2: {
+            if (upix != null) {
+                for (int x = 0; x < rowWidth; x++)
+                    pix[x] = 0xff & (upix[x] + inbuf[x]);
+            } else {
+                for (int x = 0; x < rowWidth; x++)
+                    pix[x] = 0xff & inbuf[x];
+            }
+            break; }
+        case 3: {
+            if (upix != null) {
+                int x = 0;
+                for ( ; x < boff; x++) {
+                    int rval = upix[x];
+                    pix[x] = 0xff & ((rval>>1) + inbuf[x]);
+                }
+                for ( ; x < rowWidth; x++) {
+                    int rval = upix[x] + pix[x - boff];
+                    pix[x] = 0xff & ((rval>>1) + inbuf[x]);
+                }
+            } else {
+                int x = 0;
+                for ( ; x < boff; x++) pix[x] = 0xff & inbuf[x];
+                for ( ; x < rowWidth; x++) {
+                    int rval = pix[x - boff];
+                    pix[x] = 0xff & ((rval>>1) + inbuf[x]);
+                }
+            }
+            break; }
+        case 4: {
+            if (upix != null) {
+                int x = 0;
+                for ( ; x < boff; x++) pix[x] = 0xff & (upix[x] + inbuf[x]);
+                for ( ; x < rowWidth; x++) {
+                    int a, b, c, p, pa, pb, pc, rval;
+                    a = pix[x - boff];
+                    b = upix[x];
+                    c = upix[x - boff];
+                    p = a + b - c;
+                    pa = p > a ? p - a : a - p;
+                    pb = p > b ? p - b : b - p;
+                    pc = p > c ? p - c : c - p;
+                    if ((pa <= pb) && (pa <= pc)) rval = a;
+                    else if (pb <= pc) rval = b;
+                    else rval = c;
+                    pix[x] = 0xff & (rval + inbuf[x]);
+                }
+            } else {
+                int x = 0;
+                for ( ; x < boff; x++) pix[x] = 0xff & inbuf[x];
+                for ( ; x < rowWidth; x++) {
+                    int rval = pix[x - boff];
+                    pix[x] = 0xff & (rval + inbuf[x]);
+                }
+            }
+            break; }
+        default: return false;
+        }
+        return true;
+    }
+
+    // Private Data ///////////////////////////////////////////////////////////////////////////////////////
+    
+    private int target_offset = 0;
+    private int width = -1;
+    private int height = -1;
+    private int sigmask = 0xffff;
+    private Object pixels = null;
+    private int ipixels[] = null;
+    private byte bpixels[] = null;
+    private boolean multipass = false;
+    private boolean complete = false;
+    private boolean error = false;
+
+    private int[] data = null;
+
+    private InputStream underlyingStream = null;
+    private DataInputStream inputStream = null;
+    private Thread controlThread = null;
+    private boolean infoAvailable = false;
+    private int updateDelay = 750;
+
+    // Image decoding state variables
+    private boolean headerFound = false;
+    private int compressionMethod = -1;
+    private int depth = -1;
+    private int colorType = -1;
+    private int filterMethod = -1;
+    private int interlaceMethod = -1;
+    private int pass = 0;
+    private byte palette[] = null;
+    private int mask = 0x0;
+    private int smask = 0x0;
+    private boolean transparency = false;
+
+    private int chunkLength = 0;
+    private int chunkType = 0;
+    private boolean needChunkInfo = true;
+
+    private static final int CHUNK_bKGD = 0x624B4744;   // "bKGD"
+    private static final int CHUNK_cHRM = 0x6348524D;   // "cHRM"
+    private static final int CHUNK_gAMA = 0x67414D41;   // "gAMA"
+    private static final int CHUNK_hIST = 0x68495354;   // "hIST"
+    private static final int CHUNK_IDAT = 0x49444154;   // "IDAT"
+    private static final int CHUNK_IEND = 0x49454E44;   // "IEND"
+    private static final int CHUNK_IHDR = 0x49484452;   // "IHDR"
+    private static final int CHUNK_PLTE = 0x504C5445;   // "PLTE"
+    private static final int CHUNK_pHYs = 0x70485973;   // "pHYs"
+    private static final int CHUNK_sBIT = 0x73424954;   // "sBIT"
+    private static final int CHUNK_tEXt = 0x74455874;   // "tEXt"
+    private static final int CHUNK_tIME = 0x74494D45;   // "tIME"
+    private static final int CHUNK_tRNS = 0x74524E53;   // "tIME"
+    private static final int CHUNK_zTXt = 0x7A545874;   // "zTXt"
+
+    private static final int startingRow[]  =  { 0, 0, 0, 4, 0, 2, 0, 1 };
+    private static final int startingCol[]  =  { 0, 0, 4, 0, 2, 0, 1, 0 };
+    private static final int rowInc[]       =  { 1, 8, 8, 8, 4, 4, 2, 2 };
+    private static final int colInc[]       =  { 1, 8, 8, 4, 4, 2, 2, 1 };
+    private static final int blockHeight[]  =  { 1, 8, 8, 4, 4, 2, 2, 1 };
+    private static final int blockWidth[]   =  { 1, 8, 4, 4, 2, 2, 1, 1 };
+
+    // Helper Classes ////////////////////////////////////////////////////////////////////
+
+    private static class MeteredInputStream extends FilterInputStream {
+        int bytesLeft;
+        int marked;
+        
+        public MeteredInputStream(InputStream in, int size) {
+            super(in);
+            bytesLeft = size;
+        }
+        
+        public final int read() throws IOException {
+            if (bytesLeft > 0) {
+                int val = in.read();
+                if (val != -1) bytesLeft--;
+                return val;
+            }
+            return -1;
+        }
+        
+        public final int read(byte b[]) throws IOException {
+            return read(b, 0, b.length);
+        }
+        
+        public final int read(byte b[], int off, int len) throws IOException {
+            if (bytesLeft > 0) {
+                len = (len > bytesLeft ? bytesLeft : len);
+                int read = in.read(b, off, len);
+                if (read > 0) bytesLeft -= read;
+                return read;
+            }
+            return -1;
+        }
+        
+        public final long skip(long n) throws IOException {
+            n = (n > bytesLeft ? bytesLeft : n);
+            long skipped = in.skip(n);
+            if (skipped > 0) bytesLeft -= skipped;
+            return skipped;
+        }
+        
+        public final int available() throws IOException {
+            int n = in.available();
+            return (n > bytesLeft ? bytesLeft : n);
+        }
+        
+        public final void close() throws IOException { /* Eat this */ }
+
+        public final void mark(int readlimit) {
+            marked = bytesLeft;
+            in.mark(readlimit);
+        }
+        
+        public final void reset() throws IOException {
+            in.reset();
+            bytesLeft = marked;
+        }
+        
+        public final boolean markSupported() { return in.markSupported(); }
+    }
+
+    /** Support class, used to eat the IDAT headers dividing up the deflated stream */
+    private static class IDATEnumeration implements Enumeration {
+        InputStream underlyingStream;
+        PNG owner;
+        boolean firstStream = true;
+        
+        public IDATEnumeration(PNG owner) {
+            this.owner = owner;
+            this.underlyingStream = owner.underlyingStream;
+        }
+        
+        public Object nextElement() {
+            firstStream = false;
+            return new MeteredInputStream(underlyingStream, owner.chunkLength);
+        }
+        
+        public boolean hasMoreElements() {
+            DataInputStream dis = new DataInputStream(underlyingStream);
+            if (!firstStream) {
+                try {
+                    int crc = dis.readInt();
+                    owner.needChunkInfo = false;
+                    owner.chunkLength = dis.readInt();
+                    owner.chunkType = dis.readInt();
+                } catch (IOException ioe) {
+                    return false;
+                }
+            }
+            if (owner.chunkType == PNG.CHUNK_IDAT) return true;
+            return false;
+        }
+    }
+
+}
diff --git a/src/org/xwt/Picture.java b/src/org/xwt/Picture.java
new file mode 100644 (file)
index 0000000..3f60854
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+/** 
+ *    <p>
+ *    The in-memory representation of a PNG or GIF image. It is
+ *    read-only. It is usually passed to DoubleBuffer.drawPicture()
+ *    </p>
+ *
+ *    <p>
+ *    Implementations of the Platform class should return objects
+ *    supporting this interface from the createPicture() method. These
+ *    implementations may choose to implement caching strategies (for
+ *    example, using a Pixmap on X11).
+ *    </p>
+ */
+public interface Picture {
+    public int getHeight();
+    public int getWidth();
+}
diff --git a/src/org/xwt/Platform.java b/src/org/xwt/Platform.java
new file mode 100644 (file)
index 0000000..173ae0c
--- /dev/null
@@ -0,0 +1,308 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import java.lang.reflect.*;
+import java.net.*;
+import java.io.*;
+import java.util.*;
+import org.xwt.util.*;
+
+/** 
+ *  Abstracts away the small irregularities in JVM implementations.
+ *
+ *  The default Platform class supports a vanilla JDK 1.1
+ *  JVM. Subclasses are provided for other VMs. Methods whose names
+ *  start with an underscore are meant to be overridden by
+ *  subclasses. If you create a subclass of Platform, you should put
+ *  it in the org.xwt.plat package, and add code to this file's static
+ *  block to detect the new platform.
+ */
+public class Platform {
+
+    // Static Data /////////////////////////////////////////////////////////////////////////////////////
+
+    /** set to true during the delivery of a KeyPressed:C-v/A-v or Press3; it is safe to use a 'global' here,
+     *  since message delivery is single-threaded and non-preemptable
+     */
+    static boolean clipboardReadEnabled = false;
+
+    /** The appropriate Platform object for this JVM */
+    static Platform platform = null;
+
+
+    // VM Detection Logic /////////////////////////////////////////////////////////////////////
+
+    /** do-nothing method that forces &lt;clinit&gt; to run */
+    public static void forceLoad() { }
+
+    // If you create a new subclass of Platform, you should add logic
+    // here to detect it. Do not reference your class directly -- use
+    // reflection.
+
+    static {
+        System.out.println("Detecting JVM...");
+        try {
+            String vendor = System.getProperty("java.vendor", "");
+            String version = System.getProperty("java.version", "");
+            String os_name = System.getProperty("os.name", "");
+            String platform_class = null;
+            
+            if (os_name.startsWith("Mac OS X")) platform_class = "MacOSX";
+            else if (vendor.startsWith("Free Software Foundation")) platform_class = "Win32";
+            else if (version.startsWith("1.1") && vendor.startsWith("Netscape")) platform_class = "Netscape";
+            else if (version.startsWith("1.1") && vendor.startsWith("Microsoft")) platform_class = "Microsoft";
+            else if (!version.startsWith("1.0") && !version.startsWith("1.1")) platform_class = "Java2";
+
+            if (platform_class != null) {
+                platform = (Platform)Class.forName("org.xwt.plat." + platform_class).newInstance();
+                platform.init();
+            }
+            if (Log.on) Log.log(Platform.class, "XWT VM detection:   vendor = " + vendor);
+            if (Log.on) Log.log(Platform.class, "                   version = " + version);
+            if (Log.on) Log.log(Platform.class, "                        os = " + os_name);
+
+            if (platform_class == null) {
+                if (Log.on) Log.log(Platform.class, "Unable to detect JVM");
+                System.exit(-1);
+            }
+
+            if (Log.on) Log.log(Platform.class, "                  platform = " + platform.getDescriptiveName());
+            if (Log.on) Log.log(Platform.class, "                     class = " + platform.getClass().getName());
+
+        } catch (Exception e) {
+            if (Log.on) Log.log(Platform.class, "Exception while trying to detect JVM");
+            if (Log.on) Log.log(Platform.class, e);
+            System.exit(-1);
+        }
+
+    }
+
+
+    // Methods to be Overridden ////////////////////////////////////////////////////////////////////
+
+    /** a string describing the VM */
+    protected String getDescriptiveName() { return "Generic Java 1.1 VM"; }
+
+    /** this initializes the platform; code in here can invoke methods on Platform since Platform.platform has already been set */
+    protected void init() { }
+
+    /** creates and returns a doublebuffer 'belonging' to <tt>owner</tt>; we need to associate DoubleBuffers to surfaces
+     *  due to AWT 1.1 requirements (definately for Navigator, possibly also for MSJVM).
+     */
+    protected DoubleBuffer _createDoubleBuffer(int w, int h, Surface owner) { return null; }
+    
+    /** creates and returns a new surface */
+    protected Surface _createSurface(Box b, boolean framed) { return null; }
+
+    /** creates a socket object */
+    protected Socket _getSocket(String host, int port, boolean ssl) throws IOException {
+        return ssl ? new TinySSL(host, port) : new Socket(java.net.InetAddress.getByName(host), port);
+    }
+
+    /** creates and returns a picture */
+    protected Picture _createPicture(int[] b, int w, int h) { return null; }
+    
+    /** should return true if it is safe to supress full-surface dirties immediately after a window resize */
+    protected boolean _supressDirtyOnResize() { return true; }
+
+    /** the human-readable name of the key mapped to XWT's 'alt' key */
+    protected String _altKeyName() { return "alt"; }
+
+    /** opens a connection to the resource identified by URL u, and returns an InputStream */
+    protected InputStream _urlToInputStream(URL u) throws IOException { return u.openStream(); }
+
+    /** returns the contents of the clipboard */    
+    protected String _getClipBoard() { return null; }
+
+    /** sets the contents of the clipboard */
+    protected void _setClipBoard(String s) { }
+
+    /** returns the width of the screen, in pixels */
+    protected int _getScreenWidth() { return 640; }
+
+    /** returns the height of the screen, in pixels */
+    protected int _getScreenHeight() { return 480; }
+
+    /** returns the width of a string in a platform-specific font */
+    protected int _stringWidth(String font, String text) { return 10 * text.length(); }
+
+    /** returns the maximum ascent of all glyphs in a given platform-specific font */
+    protected int _getMaxAscent(String font) { return 10; }
+
+    /** returns the maximum descent of all glyphs in a given platform-specific font */
+    protected int _getMaxDescent(String font) { return 2; }
+
+    /** returns a list of all platform-specific fonts available */
+    protected String[] _listFonts() { return new String[] { }; }
+
+    /** returns the maximum number of threads that the XWT engine can create without adversely affecting the host OS */
+    protected int _maxThreads() { return 25; }
+
+    /** creates a weak reference */
+    protected org.xwt.Weak _getWeak(final Object o) {
+        return new org.xwt.Weak() {
+                public Object get() { return o; }
+            };
+    }
+    
+    /** quits XWT */
+    protected void _exit() { System.exit(0); }
+
+    /** used to notify the user of very serious failures; usually used when logging is not working or unavailable */
+    protected void _criticalAbort(String message) { System.exit(-1); }
+
+    /** used to notify the user of very serious failures; usually used when logging is not working or unavailable */
+    protected String _getDefaultFont() { return "sansserif10"; }
+
+    protected boolean _needsAutoClick() { return false; }
+
+    // Static methods -- thunk to the instance /////////////////////////////////////////////////////////////////////////
+
+    /** if true, org.xwt.Surface should generate Click messages automatically when a Release happens after a Press and the mouse has not moved much */
+    public static boolean needsAutoClick() { return platform._needsAutoClick(); }
+
+    /** should return true if it is safe to supress full-surface dirties immediately after a window resize */
+    public static String getDefaultFont() { return platform._getDefaultFont(); }
+
+    /** should return true if it is safe to supress full-surface dirties immediately after a window resize */
+    public static boolean supressDirtyOnResize() { return platform._supressDirtyOnResize(); }
+
+    /** returns the width of a string in a platform-specific font */
+    public static int stringWidth(String font, String text) { return platform._stringWidth(font, text); }
+
+    /** returns the maximum ascent of all glyphs in a given platform-specific font */
+    public static int getMaxAscent(String font) { return platform._getMaxAscent(font); }
+
+    /** returns the maximum descent of all glyphs in a given platform-specific font */
+    public static int getMaxDescent(String font) { return platform._getMaxDescent(font); }
+
+    /** returns the maximum number of threads that the XWT engine can create without adversely affecting the host OS */
+    public static int maxThreads() { return platform._maxThreads(); }
+
+    /** returns a list of all platform-specific fonts available */
+    public static String[] listFonts() { return platform._listFonts(); }
+
+    /** creates a weak reference */
+    public static org.xwt.Weak getWeak(Object o) { return platform._getWeak(o); }
+
+    /** opens a connection to the resource identified by URL u, and returns an InputStream */
+    public static InputStream urlToInputStream(URL u) throws IOException { return platform._urlToInputStream(u); }
+
+    /** returns the contents of the clipboard */    
+    public static Object getClipBoard() { return clipboardReadEnabled ? platform._getClipBoard() : null; }
+
+    /** sets the contents of the clipboard */
+    public static void setClipBoard(String s) { platform._setClipBoard(s); }
+
+    /** creates a socket object, with or without ssl encryption */
+    public static Socket getSocket(String host, int port, boolean ssl) throws IOException { return platform._getSocket(host, port, ssl); }
+
+    /** returns the width of the screen, in pixels */
+    public static int getScreenWidth() { return platform._getScreenWidth(); }
+
+    /** returns the height of the screen, in pixels */
+    public static int getScreenHeight() { return platform._getScreenHeight(); }
+
+    /** creates and returns a doublebuffer 'belonging' to <tt>owner</tt> */
+    public static DoubleBuffer createDoubleBuffer(int w, int h, Surface s) { return platform._createDoubleBuffer(w, h, s); }
+
+    /** creates and returns a picture */
+    public static Picture createPicture(int[] data, int w, int h) { return platform._createPicture(data, w, h); }
+
+    /** creates and returns a picture */
+    public static Picture createPicture(ImageDecoder i) { return platform._createPicture(i.getData(), i.getWidth(), i.getHeight()); }
+
+    /** quits XWT */
+    public static void exit() {
+        Log.log(Platform.class, "exiting via Platform.exit()");
+        platform._exit();
+    }
+
+    /** the human-readable name of the key mapped to XWT's 'alt' key */
+    public static String altKeyName() { return platform._altKeyName(); }
+
+    /** used to notify the user of very serious failures; usually used when logging is not working or unavailable */
+    public static void criticalAbort(String message) {
+        if (Log.on) Log.log(Platform.class, "Critical Abort:");
+        if (Log.on) Log.log(Platform.class, message);
+        platform._criticalAbort(message);
+    }
+
+    /** this method invokes the platform _createSurface() method and then enforces a few post-call invariants */
+    public static Surface createSurface(Box b, boolean framed, boolean refreshable) {
+        Surface ret = platform._createSurface(b, framed);
+        ret.setInvisible(b.invisible);
+        b.set(Box.size, 0, ret.width);
+        b.set(Box.size, 1, ret.height);
+
+        Object titlebar = b.get("titlebar", null, true);
+        if (titlebar != null) ret.setTitleBarText(titlebar.toString());
+
+        Object icon = b.get("icon", null, true);
+        if (icon != null && !"".equals(icon)) {
+            Picture pic = Box.getPicture(icon.toString());
+            if (pic != null) ret.setIcon(pic);
+            else if (Log.on) Log.log(Platform.class, "unable to load icon " + icon);
+        }
+
+        if (refreshable) {
+            Surface.refreshableSurfaceWasCreated = true;
+            Surface.allSurfaces.addElement(ret);
+            ret.dirty(0, 0, ret.width, ret.height);
+            ret.Refresh();
+        }
+        return ret;
+    }
+
+    // Helpful font parsing stuff //////////////////////////////////////////////////////
+
+    public static class ParsedFont {
+        public ParsedFont() { }
+        public ParsedFont(String s) { parse(s); }
+        public int size = 10;
+        public String name = "";
+
+        public boolean italic = false;
+        public boolean bold = false;
+        public boolean underline = false;
+        public boolean dotted_underline = false;
+
+        private static int stoi(Object o) {
+            if (o == null) return 0;
+            if (o instanceof Integer) return ((Integer)o).intValue();
+            
+            String s = o.toString(); 
+            try { return Integer.parseInt(s.indexOf('.') == -1 ? s : s.substring(0, s.indexOf('.'))); }
+            catch (NumberFormatException e) { return 0; }
+        }
+
+        public void parse(String font) {
+            int i = 0;
+            while(i < font.length() && !Character.isDigit(font.charAt(i))) i++;
+            name = font.substring(0, i).toLowerCase().replace('_', ' ');
+            size = 10;
+            italic = false;
+            bold = false;
+            underline = false;
+            dotted_underline = false;
+            if (i != font.length()) {
+                int j = i;
+                while (j < font.length() && Character.isDigit(font.charAt(j))) j++;
+                if (i != j) size = stoi(font.substring(i, j));
+                i = j;
+                while(i < font.length()) {
+                    switch (font.charAt(i)) {
+                    case 'b': bold = true;
+                    case 'i': italic = true;
+                    case 'd': dotted_underline = true;
+                    case 'u': underline = true;
+                    }
+                    i++;
+                }
+            }
+        }
+
+    }
+
+}
+
diff --git a/src/org/xwt/Resources.java b/src/org/xwt/Resources.java
new file mode 100644 (file)
index 0000000..be058d3
--- /dev/null
@@ -0,0 +1,194 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import jazz.*;
+import java.lang.*;
+import java.applet.*;
+import org.mozilla.javascript.*;
+import org.xwt.util.*;
+
+/**
+ *  A singleton class that acts as a repository for files obtained
+ *  from xwar archives or the local filesystem.
+ *
+ *  All names are converted to resource names (dots instead of
+ *  slashes) when they are loaded into this repository; however,
+ *  filename extensions are left on, so queries (resolveResource(),
+ *  getResource()) should include the extension when querying for
+ *  resources.
+ */
+public class Resources {
+
+    /** Holds resources added at runtime. Initialized to hold 2000 to work around a NetscapeJVM bug. */
+    private static Hash bytes = new Hash(2000, 3);
+
+    /** The number of bytes read from the initial-xwar stream; used to display a progress bar on the splash screen */
+    public static int bytesDownloaded = 0;
+
+    /** Returns true iff <tt>name</tt> is a valid resource name */
+    private static boolean validResourceName(String name) {
+        if (name == null || name.equals("")) return false;
+        if (name.endsWith("/box.xwt") || name.endsWith("/svg.xwt")) return false;
+        if (name.equals("box.xwt") || name.equals("svg.xwt")) return false;
+        if (!((name.charAt(0) >= 'A' && name.charAt(0) <= 'Z') ||
+              (name.charAt(0) >= 'a' && name.charAt(0) <= 'z'))) return false;
+        for(int i=1; i<name.length(); i++) {
+            char c = name.charAt(i);
+            if (!((c >= 'A' && c <= 'Z') ||
+                  (c >= 'a' && c <= 'z') ||
+                  c == '_' ||
+                  (c >= '0' && c <= '9') ||
+                  (c == '.' && i == name.length() - 4))) return false;
+        }
+        return true;
+    }
+
+    /** Load a directory as if it were an archive */
+    public static synchronized void loadDirectory(File dir) throws IOException { loadDirectory(dir, ""); }
+    private static synchronized void loadDirectory(File dir, String prefix) throws IOException {
+        new Static(prefix.replace(File.separatorChar, '.'));
+        String[] subfiles = dir.list();
+        for(int i=0; i<subfiles.length; i++) {
+
+            if (subfiles[i].equals("CVS")) {
+                if (Log.on) Log.log(Resources.class, "ignoring CVS/ directory");
+                continue;
+            }
+            if (!validResourceName(subfiles[i])) {
+                if (subfiles[i].charAt(subfiles[i].length() - 1) != '~')
+                    if (Log.on) Log.log(Resources.class, "WARNING: ignoring file entry with invalid name: " + subfiles[i]);
+                continue;
+            }
+
+            String name = prefix + subfiles[i];
+            File file = new File(dir.getPath() + File.separatorChar + subfiles[i]);
+            if (file.isDirectory()) loadDirectory(file, name + File.separatorChar);
+            else {
+                bytes.put(name.replace(File.separatorChar, '.'), file);
+                bytesDownloaded += file.length();
+                Main.updateSplashScreen();
+            }
+        }
+    }
+
+    /** Load an archive from an inputstream. */
+    public static synchronized void loadArchive(InputStream is) throws IOException {
+        ZipInputStream zis = new ZipInputStream(new FilterInputStream(is) {
+                public int read() throws IOException {
+                    bytesDownloaded++;
+                    return super.read();
+                }
+                public int read(byte[] b, int off, int len) throws IOException {
+                    bytesDownloaded += len;
+                    Main.updateSplashScreen();
+                    return super.read(b, off, len);
+                }
+            });
+        for(ZipEntry ze = zis.getNextEntry(); ze != null; ze = zis.getNextEntry()) {
+            String name = ze.getName();
+            if (!validResourceName(name.substring(name.lastIndexOf('/') + 1))) {
+                if (Log.on) Log.log(Resources.class, "WARNING: ignoring xwar entry with invalid name: " + name);
+                continue;
+            }
+            if (ze.isDirectory()) {
+                new Static(name.replace('/', '.'));
+                continue;
+            }
+            if (name.endsWith(".xwt")) {
+                if (Log.verbose) Log.log(Resources.class, "building template " + name);
+                Template.buildTemplate(zis, name.substring(0, name.length() - 4).replace('/', '.'));
+                if (Log.verbose) Log.log(Resources.class, "   done building template " + name);
+                bytes.put(name.replace('/', '.'), new byte[] { });                          // placeholder so resolveResource() works properly
+            } else {
+                bytes.put(name.replace('/', '.'), isToByteArray(zis));
+            }
+        }
+        if (Log.verbose) Log.log(Resources.class, "done loading archive");
+    }
+
+    /** holds the current theme mappings */
+    static Vector mapFrom = new Vector();
+
+    /** holds the current theme mappings */
+    static Vector mapTo = new Vector();
+
+    /**
+     *  Resolves the partial resource name <tt>name</tt> to a fully
+     *  resolved resource name, using <tt>importlist</tt> as a search
+     *  list, or null if no resource was found.
+     *
+     *  Both the arguments and return values from this function SHOULD
+     *  include extensions (".xwt", ".xwf", etc) and SHOULD use dots
+     *  (".") instead of slashes ("/").
+     */
+    public static String resolve(String name, String[] importlist) {
+        final int imax = importlist == null ? 0 : importlist.length;
+        for(int i=-1; i < imax; i++) {
+            String resolved = i == -1 ? name : (importlist[i] + '.' + name);
+            for(int j=mapFrom.size() - 1; j>=0; j--) {
+                String from = mapFrom.elementAt(j).toString();
+                if (resolved.startsWith(from) && (resolved.endsWith(".xwt") || resolved.endsWith(".xwf"))) {
+                    String tryme = mapTo.elementAt(j) + resolved.substring(from.length());
+                    if (bytes.get(tryme) != null) return tryme;
+                }
+            }
+            if (bytes.get(resolved) != null) return resolved;
+        }
+        return null;
+    }
+
+    /** Returns the named resource as a byte[].
+     *  @param name A fully resolved resource name, using slashes
+     *              instead of periods. If it is null, this function
+     *              will return null.
+     */
+    public static byte[] getResource(String name) {
+        if (name == null) return null;
+        synchronized(bytes) {
+            Object o = bytes.get(name);
+            if (o == null) return null;
+            if (o instanceof byte[]) return ((byte[])o);
+            if (o instanceof File) {
+                try {
+                    FileInputStream fi = new FileInputStream((File)o);
+                    byte[] b = isToByteArray(fi);
+                    bytes.put(name, b);
+                    return b;
+                } catch (Exception e) {
+                    if (Log.on) Log.log(Resources.class, "Exception while reading from file " + o);
+                    if (Log.on) Log.log(Resources.class, e);
+                    return null;
+                }
+            }
+            return null;
+        }
+    }
+    
+    /** scratch space for isToByteArray() */
+    private static byte[] workspace = new byte[16 * 1024];
+
+    /** Trivial method to completely read an InputStream */
+    public static synchronized byte[] isToByteArray(InputStream is) throws IOException {
+        int pos = 0;
+        while (true) {
+            int numread = is.read(workspace, pos, workspace.length - pos);
+            if (numread == -1) break;
+            else if (pos + numread < workspace.length) pos += numread;
+            else {
+                pos += numread;
+                byte[] temp = new byte[workspace.length * 2];
+                System.arraycopy(workspace, 0, temp, 0, workspace.length);
+                workspace = temp;
+            }
+        }
+        byte[] ret = new byte[pos];
+        System.arraycopy(workspace, 0, ret, 0, pos);
+        return ret;
+    }
+}
+
+
+
diff --git a/src/org/xwt/SOAP.java b/src/org/xwt/SOAP.java
new file mode 100644 (file)
index 0000000..e055809
--- /dev/null
@@ -0,0 +1,262 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import org.mozilla.javascript.*;
+import org.xwt.util.*;
+
+/**
+ *  A partial RPC-style SOAP 1.1 client. Implemented from the SOAP 1.1
+ *  Spec and Dave Winer's "SOAP for Busy Developers". This class
+ *  extends XMLRPC in order to share some networking logic.
+ *
+ *  Currently unsupported features/hacks:
+ *  <ul><li> Multi-ref data and circular references
+ *      <li> 'Document Style'
+ *      <li> WSDL support
+ *  </ul>
+ */
+class SOAP extends XMLRPC {
+
+    /** the desired content of the SOAPAction header */
+    String action = null;
+
+    /** the namespace to use */
+    String nameSpace = null;
+
+    /** When you get a property from an SOAP, it just returns another SOAP with the property name tacked onto methodname. */
+    public Object get(String name, Scriptable start) {
+        return new SOAP(url.toString(), (methodname.equals("") ? "" : methodname + ".") + name, action, nameSpace);
+    }
+
+
+    // Methods to Recieve and parse SOAP Responses ////////////////////////////////////////////////////
+
+    public void startElement(String name, String[] keys, Object[] vals, int line, int col) {
+
+        if (name.equals("SOAP-ENV:Envelope")) return;
+        if (name.equals("SOAP-ENV:Body")) return;
+        if (name.equals("SOAP-ENV:Fault")) fault = true;
+
+        // add a generic struct; we'll change this if our type is different
+        objects.addElement(new JSObject(false));
+
+        for(int i=0; i<keys.length; i++) {
+            String key = keys[i];
+            String value = vals[i].toString();
+            if (key.endsWith("ype")) {
+                if (value.endsWith("boolean")) {
+                    objects.removeElementAt(objects.size() - 1);
+                    objects.addElement(Boolean.FALSE);
+                } else if (value.endsWith("int")) {
+                    objects.removeElementAt(objects.size() - 1);
+                    objects.addElement(new Integer(0));
+                } else if (value.endsWith("double")) {
+                    objects.removeElementAt(objects.size() - 1);
+                    objects.addElement(new Double(0.0));
+                } else if (value.endsWith("string")) {
+                    objects.removeElementAt(objects.size() - 1);
+                    objects.addElement("");
+                } else if (value.endsWith("null")) {
+                    objects.removeElementAt(objects.size() - 1);
+                    objects.addElement(null);
+                } else if (value.endsWith("arrayType") || value.endsWith("Array") || key.endsWith("arrayType")) {
+                    objects.removeElementAt(objects.size() - 1);
+                    objects.addElement(Context.enter().newArray(org.xwt.util.JSObject.defaultObjects, new Object[] { } ));
+                }
+            }
+        }
+    }
+
+    public void endElement(String name, int line, int col) {
+
+        if (name.equals("SOAP-ENV:Envelope")) return;
+        if (name.equals("SOAP-ENV:Body")) return;
+
+        if (content.size() > 0 && content.toString().trim().length() > 0) {
+
+            // remove ourselves
+            Object me = objects.elementAt(objects.size() - 1);
+
+            if (fault || me instanceof String) {
+                objects.removeElementAt(objects.size() - 1);
+                objects.addElement(new String(content.getBuf(), 0, content.size()).intern());
+                content.reset();
+                
+            } else if (me instanceof Integer) {
+                objects.removeElementAt(objects.size() - 1);
+                objects.addElement(new Integer(new String(content.getBuf(), 0, content.size())));
+                content.reset();
+                
+            } else if (me instanceof Boolean) {
+                objects.removeElementAt(objects.size() - 1);
+                String s = new String(content.getBuf(), 0, content.size()).trim();
+                if (s.equals("1") || s.equals("true")) objects.addElement(Boolean.TRUE);
+                else objects.addElement(Boolean.FALSE);
+                content.reset();
+                
+            } else if (me instanceof Double) {
+                objects.removeElementAt(objects.size() - 1);
+                objects.addElement(new Double(new String(content.getBuf(), 0, content.size())));
+                content.reset();
+                
+            } else {
+                // okay, we got PCDATA for what is supposedly a
+                // struct... somebody's not adding their type info...
+                String s = new String(content.getBuf(), 0, content.size()).trim();
+                boolean hasdot = false;
+                for(int i=0; i<s.length(); i++) {
+                    if (s.charAt(i) == '.') hasdot = true;
+                    if (!Character.isDigit(s.charAt(i))) {
+                        objects.removeElementAt(objects.size() - 1);
+                        objects.addElement(s);
+                        return;
+                    }
+                }
+                if (hasdot) {
+                    objects.removeElementAt(objects.size() - 1);
+                    objects.addElement(new Double(s));
+                } else {
+                    objects.removeElementAt(objects.size() - 1);
+                    objects.addElement(new Integer(s));
+                }
+                content.reset();
+            }
+
+        }
+        
+        // remove ourselves
+        Object me = objects.elementAt(objects.size() - 1);
+
+        // find our parent
+        Object parent = objects.size() > 1 ? objects.elementAt(objects.size() - 2) : null;
+
+        // we want to fold stuff back into the fault object
+        if (objects.size() < 2) return;
+
+        // our parent "should" be an aggregate type -- add ourselves to it.
+        if (parent != null && parent instanceof NativeArray) {
+            objects.removeElementAt(objects.size() - 1);
+            ((NativeArray)parent).put((int)((NativeArray)parent).jsGet_length(), (NativeArray)parent, me);
+
+        } else if (parent != null && parent instanceof Scriptable) {
+            objects.removeElementAt(objects.size() - 1);
+            ((Scriptable)parent).put(name, (Scriptable)parent, me);
+
+        }
+
+    }
+
+    /** Appends the SOAP representation of <code>o</code> to <code>sb</code> */
+    void appendObject(String name, Object o, StringBuffer sb) {
+        if (o instanceof Number) {
+            if ((double)((Number)o).intValue() == ((Number)o).doubleValue()) {
+                sb.append("                <" + name + " xsi:type=\"xsd:int\">");
+                sb.append(((Number)o).intValue());
+                sb.append("</" + name + ">\n");
+            } else {
+                sb.append("                <" + name + " xsi:type=\"xsd:double\">");
+                sb.append(o);
+                sb.append("</" + name + ">\n");
+            }
+
+        } else if (o instanceof Boolean) {
+            sb.append("                <" + name + " xsi:type=\"xsd:boolean\">");
+            sb.append(((Boolean)o).booleanValue() ? "true" : "false");
+            sb.append("</" + name + ">\n");
+
+        } else if (o instanceof String) {
+            sb.append("                <" + name + " xsi:type=\"xsd:string\">");
+            String s = (String)o;
+            if (s.indexOf('<') == -1 && s.indexOf('&') == -1) {
+                sb.append(s);
+            } else {
+                char[] cbuf = s.toCharArray();
+                while(true) {
+                    int oldi = 0, i=0;
+                    while(i < cbuf.length && cbuf[i] != '<' && cbuf[i] != '&') i++;
+                    sb.append(cbuf, oldi, i);
+                    if (i == cbuf.length) break;
+                    if (cbuf[i] == '<') sb.append("&lt;");
+                    else if (cbuf[i] == '&') sb.append("&amp;");
+                    i = oldi = i + 1;
+                }
+            }
+            sb.append("</" + name + ">\n");
+
+        } else if (o instanceof NativeArray) {
+            NativeArray na = (NativeArray)o;
+            sb.append("                <" + name + " SOAP-ENC:arrayType=\"xsd:ur-type[" + na.jsGet_length() + "]\">");
+            for(int i=0; i<na.jsGet_length(); i++)
+                appendObject("item", na.get(i, na), sb);
+            sb.append("</" + name + ">\n");
+
+        } else if (o instanceof Scriptable && !(o instanceof Undefined)) {
+            Scriptable s = (Scriptable)o;
+            sb.append("                <" + name + ">");
+            Object[] ids = s.getIds();
+            for(int i=0; i<ids.length; i++)
+                appendObject(ids[i].toString(), s.get(ids[i].toString(), s), sb);
+            sb.append("</" + name + ">\n");
+        }
+    }
+
+    protected Object send(Object[] args, BufferedReader br, PrintWriter ps) throws IOException, JavaScriptException {
+
+        // build up the request
+        StringBuffer content = new StringBuffer();
+        content.append("<?xml version=\"1.0\"?>\n");
+        content.append("<SOAP-ENV:Envelope SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"\n");
+        content.append("                   xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\"\n");
+        content.append("                   xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"\n");
+        content.append("                   xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\"\n");
+        content.append("                   xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\">\n");
+        content.append("<SOAP-ENV:Body>\n");
+        content.append("    <");
+        content.append(methodname);
+        content.append(nameSpace != null ? " xmlns=\"" + nameSpace + "\"" : "");
+        content.append(">\n");
+        if (args.length > 0) {
+            Object[] o = ((Scriptable)args[0]).getIds();
+            for(int i=0; i<o.length; i++)
+                appendObject(o[i].toString(), ((Scriptable)args[0]).get(o[i].toString(), (Scriptable)args[0]), content);
+        }
+        content.append("    </" + methodname + "></SOAP-ENV:Body></SOAP-ENV:Envelope>");
+        
+        // send it
+        ps.print("POST " + filename + " HTTP/1.0\r\n");
+        ps.print("Host: " + host + "\r\n");
+        ps.print("User-Agent: XWT (http://www.xwt.org/)\r\n");
+        ps.print("Content-Type: text/xml; charset=\"us-ascii\"\r\n");
+        ps.print("Content-length: " + (content.length() + 1) + "\r\n");
+        ps.print("SOAPAction: " + (action == null ? filename : action) + "\r\n");
+        ps.print("\r\n");
+        ps.print(content);
+        ps.print("\n");
+        ps.flush();
+        
+        // throw away HTTP reply headers
+        while(!br.readLine().equals("")) { }
+
+        // parse XML reply
+        try {
+            parse(br);
+        } catch (XML.SAXException e) {
+            if (Log.on) Log.log(this, "reply from server was not well-formed XML: " + e);
+            throw new JavaScriptException("reply from server was not well-formed XML: " + e);
+        }
+
+        if (fault) throw new JavaScriptException((Scriptable)objects.elementAt(0));
+        if (objects.size() == 0) return null;
+        return objects.elementAt(0);
+    }
+
+    SOAP(String urlstr, String methodname, String action, String nameSpace) {
+        super(urlstr, methodname);
+        this.action = action;
+        this.nameSpace = nameSpace;
+    }
+
+}
diff --git a/src/org/xwt/SpecialBoxProperty.java b/src/org/xwt/SpecialBoxProperty.java
new file mode 100644 (file)
index 0000000..205b8e6
--- /dev/null
@@ -0,0 +1,671 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import org.mozilla.javascript.*;
+import org.xwt.util.*;
+import java.util.*;
+import java.net.*;
+import java.text.*;
+
+/** 
+ *  A helper class for properties of Box which require special
+ *  handling.
+ *
+ *  To avoid excessive use of String.equals(), the Box.get() and
+ *  Box.put() methods employ a Hash keyed on property names that
+ *  require special handling. The value stored in the Hash is an
+ *  instance of an anonymous subclass of SpecialBoxProperty, which knows
+ *  how to handle get()s and put()s for that property name. There
+ *  should be one anonymous subclass of SpecialBoxProperty for each
+ *  specially-handled property on Box.
+ */
+class SpecialBoxProperty {
+
+    SpecialBoxProperty() { }
+    private static final int NUMINTS = Box.NUMINTS;
+    private static final int dmax = Box.dmax;
+    private static final int dmin = Box.dmin;
+    private static final int cmin = Box.cmin;
+    private static final int abs = Box.abs;
+    private static final int pos = Box.pos;
+    private static final int size = Box.size;
+    private static final int oldpos = Box.oldpos;
+    private static final int oldsize = Box.oldsize;
+    private static final int pad = Box.pad;
+
+    /** stores instances of SpecialBoxProperty; keyed on property name */
+    static Hash specialBoxProperties = new Hash(200, 3);
+
+    /** this method defines the behavior when the property is get()ed from b */
+    Object get(Box b) { return null; }
+
+    /** this method defines the behavior when the property is put() to b */
+    void put(Box b, Object value) { }
+
+    /** this method defines the behavior when the property is put() to b, allows a single SpecialBoxProperty to serve multiple properties */
+    void put(String name, Box b, Object value) { put(b, value); }
+
+    // 'ye olde IBM CGA colours', as defined in the XWT reference
+    static final int black = 0xFF000000;
+    static final int blue = 0xFF000088;
+    static final int green = 0xFF008800;
+    static final int cyan = 0xFF008888;
+    static final int red = 0xFF880000;
+    static final int magenta = 0xFF880088;
+    static final int brown = 0xFF888800;
+    static final int lightGray = 0xFFBDBEBD;
+    static final int darkGray = 0xFF242424;
+    static final int brightBlue = 0xFF0000FF;
+    static final int brightGreen = 0xFF00FF00;
+    static final int brightCyan = 0xFF00FFFF;
+    static final int brightRed = 0xFFFF0000;
+    static final int pink = 0xFFFF00FF;
+    static final int yellow = 0xFFFFFF00;
+    static final int white = 0xFFFFFFFF;
+
+    static {
+
+        specialBoxProperties.put("color", new SpecialBoxProperty() {
+                public Object get(Box b) {
+                    if ((b.color & 0xFF000000) == 0) return null;
+                    String red = Integer.toHexString((b.color & 0x00FF0000) >> 16);
+                    String green = Integer.toHexString((b.color & 0x0000FF00) >> 8);
+                    String blue = Integer.toHexString(b.color & 0x000000FF);
+                    if (red.length() < 2) red = "0" + red;
+                    if (blue.length() < 2) blue = "0" + blue;
+                    if (green.length() < 2) green = "0" + green;
+                    return "#" + red + green + blue;
+                }
+                public void put(Box b, Object value) {
+                    int newcolor = b.color;
+                    String s = value == null ? null : value.toString();
+                    if (value == null) newcolor = 0x00000000;
+                    else if (s.length() > 0 && s.charAt(0) == '#')
+                        newcolor = 0xFF000000 |
+                            (Integer.parseInt(s.substring(1, 3), 16) << 16) |
+                            (Integer.parseInt(s.substring(3, 5), 16) << 8) |
+                            Integer.parseInt(s.substring(5, 7), 16);
+                    else if (s.equals("black")) newcolor = black;
+                    else if (s.equals("blue")) newcolor = blue;
+                    else if (s.equals("green")) newcolor = green;
+                    else if (s.equals("cyan")) newcolor = cyan;
+                    else if (s.equals("red")) newcolor = red;
+                    else if (s.equals("magenta")) newcolor = magenta;
+                    else if (s.equals("brown")) newcolor = brown;
+                    else if (s.equals("lightGray")) newcolor = lightGray;
+                    else if (s.equals("darkGray")) newcolor = darkGray;
+                    else if (s.equals("brightBlue")) newcolor = brightBlue;
+                    else if (s.equals("brightGreen")) newcolor = brightGreen;
+                    else if (s.equals("brightCyan")) newcolor = brightCyan;
+                    else if (s.equals("brightRed")) newcolor = brightRed;
+                    else if (s.equals("pink")) newcolor = pink;
+                    else if (s.equals("yellow")) newcolor = yellow;
+                    else if (s.equals("white")) newcolor = white;
+                    else if (Log.on) Log.log(this, "invalid color " + s);
+
+                    if (newcolor == b.color) return;
+                    b.color = newcolor;
+                    b.dirty();
+                } });
+        
+        
+        specialBoxProperties.put("textcolor", new SpecialBoxProperty() {
+                public Object get(Box b) {
+                    if ((b.textcolor & 0xFF000000) == 0) return null;
+                    String red = Integer.toHexString((b.textcolor & 0x00FF0000) >> 16);
+                    String green = Integer.toHexString((b.textcolor & 0x0000FF00) >> 8);
+                    String blue = Integer.toHexString(b.textcolor & 0x000000FF);
+                    if (red.length() < 2) red = "0" + red;
+                    if (blue.length() < 2) blue = "0" + blue;
+                    if (green.length() < 2) green = "0" + green;
+                    return "#" + red + green + blue;
+                }
+                public void put(Box b, Object value) {
+                    int newtextcolor = b.color;
+                    String s = value == null ? null : value.toString();
+                    if (s.length() > 0 && s.charAt(0) == '#')
+                        newtextcolor = 0xFF000000 |
+                            (Integer.parseInt(s.substring(1, 3), 16) << 16) |
+                            (Integer.parseInt(s.substring(3, 5), 16) << 8) |
+                            Integer.parseInt(s.substring(5, 7), 16);
+                    else if (s.equals("black")) newtextcolor = black;
+                    else if (s.equals("blue")) newtextcolor = blue;
+                    else if (s.equals("green")) newtextcolor = green;
+                    else if (s.equals("cyan")) newtextcolor = cyan;
+                    else if (s.equals("red")) newtextcolor = red;
+                    else if (s.equals("magenta")) newtextcolor = magenta;
+                    else if (s.equals("brown")) newtextcolor = brown;
+                    else if (s.equals("lightGray")) newtextcolor = lightGray;
+                    else if (s.equals("darkGray")) newtextcolor = darkGray;
+                    else if (s.equals("brightBlue")) newtextcolor = brightBlue;
+                    else if (s.equals("brightGreen")) newtextcolor = brightGreen;
+                    else if (s.equals("brightCyan")) newtextcolor = brightCyan;
+                    else if (s.equals("brightRed")) newtextcolor = brightRed;
+                    else if (s.equals("pink")) newtextcolor = pink;
+                    else if (s.equals("yellow")) newtextcolor = yellow;
+                    else if (s.equals("white")) newtextcolor = white;
+                    else if (Log.on) Log.log(this, "invalid textcolor " + s);
+
+                    if (newtextcolor == b.textcolor) return;
+                    b.textcolor = newtextcolor;
+                    b.dirty();
+                } });
+        
+        specialBoxProperties.put("text", new SpecialBoxProperty() {
+                public Object get(Box b) { return b.text; }
+                public void put(Box b, Object value) {
+                    String t = value == null ? "null" : value.toString();
+                    if (t.equals(b.text)) return;
+
+                    for(int i=0; i<t.length(); i++)
+                        if (Character.isISOControl(t.charAt(i))) {
+                            if (Log.on) Log.log(this, 
+                                                "ISO Control characters are not permitted in box text strings; offending character is ASCII " +
+                                               ((int)t.charAt(i)));
+                            /* FIXME: reinstate
+                            return;
+                            */
+                        }
+
+                    b.text = t;
+                    b.textupdate();
+                    b.dirty();
+                } });
+        
+        specialBoxProperties.put("thisbox", new SpecialBoxProperty() {
+                public Object get(Box b) { return b; }
+                public void put(Box b, Object value) {
+                    if (value == null) b.remove();
+                    else if (value.equals("window")) Platform.createSurface(b, false, true);
+                    else if (value.equals("frame")) Platform.createSurface(b, true, true);
+                    else if (Log.on) Log.log(this, "put invalid value to 'thisbox' property: " + value);
+                }
+            });
+
+        specialBoxProperties.put("orient", new SpecialBoxProperty() {
+                public Object get(Box b) {
+                    if (b.redirect == null) return "horizontal";
+                    else if (b.redirect != b) return get(b.redirect);
+                    else if (b.o == 1) return "vertical";
+                    else return "horizontal";
+                }
+                public void put(Box b, Object value) {
+                    if (value == null) return;
+                    if (b.redirect == null) return;
+                    if (b.redirect != b) {
+                        put(b.redirect, value);
+                        return;
+                    }
+                    if (value.equals("vertical")) {
+                        if (b.o == 1) return;
+                        b.o = 1;
+                        b.xo = 0;
+                    } else if (value.equals("horizontal")) {
+                        if (b.o == 0) return;
+                        b.o = 0;
+                        b.xo = 1;
+                    } else if (Log.on)
+                        Log.log(this, "invalid value put to orient property: " + value);
+                    b.mark_for_prerender();
+                    b.sync_cmin_to_children();
+                } });
+
+        specialBoxProperties.put("static", new SpecialBoxProperty() {
+                public Object get(Box b) {
+                    String cfsn = JSObject.getCurrentFunctionSourceName();
+                    for(int i=0; i<cfsn.length() - 1; i++)
+                        if (cfsn.charAt(i) == '.' && (cfsn.charAt(i+1) == '_' || Character.isDigit(cfsn.charAt(i+1)))) {
+                            cfsn = cfsn.substring(0, i);
+                            break;
+                        }
+                    return Static.getStatic(cfsn);
+                }
+                public void put(Box b, Object value) { }
+            });
+
+        specialBoxProperties.put("sizetoimage", new SpecialBoxProperty() {
+                public Object get(Box b) { return b.sizetoimage ? Boolean.TRUE : Boolean.FALSE; }
+                public void put(Box b, Object value) {
+                    if (stob(value)) {
+                        if (b.sizetoimage) return;
+                        b.sizetoimage = true;
+                        b.syncSizeToImage();
+                    } else {
+                        b.sizetoimage = false;
+                    }
+                } });
+        
+        specialBoxProperties.put("shrink", new SpecialBoxProperty() {
+                public Object get(Box b) { return (b.vshrink && b.hshrink) ? Boolean.TRUE : Boolean.FALSE; }
+                public void put(Box b, Object value) {
+                    boolean newshrink = stob(value);
+                    if (b.hshrink == newshrink && b.vshrink == newshrink) return;
+                    b.hshrink = b.vshrink = newshrink;
+                    if (b.hshrink) b.set(b.dmax, 0, Box.max(b.cmin(0), b.textdim(0) + 2 * b.pad(0), b.dmin(0)));
+                    if (b.vshrink) b.set(b.dmax, 1, Box.max(b.cmin(1), b.textdim(1) + 2 * b.pad(1), b.dmin(1)));
+                } });
+        
+        specialBoxProperties.put("hshrink", new SpecialBoxProperty() {
+                public Object get(Box b) { return new Boolean(b.hshrink); }
+                public void put(Box b, Object value) {
+                    boolean newshrink = stob(value);
+                    if (b.hshrink == newshrink) return;
+                    b.hshrink = newshrink;
+                    if (b.hshrink) b.set(b.dmax, 0, Box.max(b.cmin(0), b.textdim(0) + 2 * b.pad(0), b.dmin(0)));
+                }
+            });
+        
+        specialBoxProperties.put("vshrink", new SpecialBoxProperty() {
+                public Object get(Box b) { return b.vshrink ? Boolean.TRUE : Boolean.FALSE; }
+                public void put(Box b, Object value) {
+                    boolean newshrink = stob(value);
+                    if (b.vshrink == newshrink) return;
+                    b.vshrink = newshrink;
+                    if (b.vshrink) b.set(b.dmax, 1, Box.max(b.cmin(1), b.textdim(1) + 2 * b.pad(1), b.dmin(1)));
+                }
+            });
+        
+        specialBoxProperties.put("x", new SpecialBoxProperty() {
+                public Object get(Box b) {
+                    if (b.surface == null) return new Integer(0);
+                    if (b.invisible) return new Integer(0);
+                    return new Integer(b.abs(0));
+                }
+                public void put(Box b, Object value) {
+                    if (b.getParent() == null && b.surface != null) {
+                        b.surface.setLocation(stoi(value), b.abs(1));
+                        b.surface.centerSurfaceOnRender = false;
+                    }
+                    b.set(abs, 0, stosh(value));
+                }
+            });
+        
+        specialBoxProperties.put("y", new SpecialBoxProperty() {
+                public Object get(Box b) {
+                    if (b.surface == null) return new Integer(0);
+                    if (b.invisible) return new Integer(0);
+                    return new Integer(b.abs(1));
+                }
+                public void put(Box b, Object value) {
+                    if (b.getParent() == null && b.surface != null) {
+                        b.surface.setLocation(b.abs(0), stoi(value));
+                        b.surface.centerSurfaceOnRender = false;
+                    }
+                    b.set(abs, 1, stosh(value));
+                }
+            });
+
+        specialBoxProperties.put("width", new SpecialBoxProperty() {
+                public Object get(Box b) { return new Integer(b.size(0)); }
+                public void put(Box b, Object value) {
+                    if (b.sizetoimage) return;
+                    if (b.getParent() == null && b.surface != null) {
+                        b.set(size, 0, Box.max(Surface.scarPicture.getWidth(), stosh(value)));
+                        b.mark_for_prerender();
+                    } else {
+                        b.set(dmax, 0, stosh(value));
+                        b.set(dmin, 0, stosh(value));
+                    }
+                } });
+        
+        specialBoxProperties.put("height", new SpecialBoxProperty() {
+                public Object get(Box b) { return new Integer(b.size(1)); }
+                public void put(Box b, Object value) {
+                    if (b.sizetoimage) return;
+                    if (b.getParent() == null && b.surface != null) {
+                        b.set(size, 1, Box.max(Surface.scarPicture.getHeight(), stosh(value)));
+                        b.mark_for_prerender();
+                    } else {
+                        b.set(dmax, 1, stosh(value));
+                        b.set(dmin, 1, stosh(value));
+                    }
+                } });
+
+        specialBoxProperties.put("flex", new SpecialBoxProperty() {
+                public Object get(Box b) { return new Double(b.flex); }
+                public void put(Box b, Object value) {
+                    if (value == null) return;
+                    int newflex = stoi(value);
+                    if (newflex == b.flex) return;
+                    b.flex = newflex;
+                    b.mark_for_prerender();
+                } });
+        
+        specialBoxProperties.put("tile", new SpecialBoxProperty() {
+                public Object get(Box b) { return b.tile ? Boolean.TRUE : Boolean.FALSE; }
+                public void put(Box b, Object value) {
+                    boolean newtile = stob(value);
+                    if (newtile == b.tile) return;
+                    b.tile = newtile;
+                    b.dirty();
+                } });
+        
+        specialBoxProperties.put("align", new SpecialBoxProperty() {
+                public Object get(Box b) {
+                    if (b.align == -1) return "topleft";
+                    else if (b.align == 1) return "bottomright";
+                    else return "center";
+                }
+                public void put(Box b, Object value) {
+                    String s = value == null ? "" : value.toString();
+                    byte newalign = b.align;
+                    if (s.equals("topleft")) newalign = -1;
+                    else if (s.equals("bottomright")) newalign = 1;
+                    else if (s.equals("center")) newalign = 0;
+                    else if (Log.on) Log.log(this, "invalid value put to align property: " + value);
+                    if (newalign == b.align) return;
+                    b.align = newalign;
+                    if (b.getParent() != null) b.getParent().mark_for_prerender();
+                } });
+        
+        specialBoxProperties.put("invisible", new SpecialBoxProperty() {
+                public Object get(Box b) { return b.invisible ? Boolean.TRUE : Boolean.FALSE; }
+                public void put(Box b, Object value) {
+                    boolean newinvisible = stob(value);
+                    if (newinvisible == b.invisible) return;
+                    b.invisible = newinvisible;
+                    if (b.getParent() == null) {
+                        if (b.surface != null) b.surface.setInvisible(newinvisible);
+                    } else {
+                        b.dirty();
+                        b.getParent().mark_for_prerender();
+                        b.getParent().sync_cmin_to_children();
+                        b.getParent().dirty(b.pos(0), b.pos(1), b.size(0), b.size(1));
+                        b.getParent().dirty(b.oldpos(0), b.oldpos(1), b.oldsize(0), b.oldsize(1));
+                    }
+                }});
+        
+        specialBoxProperties.put("absolute", new SpecialBoxProperty() {
+                public Object get(Box b) { return b.absolute ? Boolean.TRUE : Boolean.FALSE; }
+                public void put(Box b, Object value) {
+                    boolean newabsolute = stob(value);
+                    if (newabsolute == b.absolute) return;
+                    b.absolute = newabsolute;
+                    if (b.getParent() != null) {
+                        b.getParent().mark_for_prerender();
+                        b.getParent().sync_cmin_to_children();
+                    }
+                } });
+        
+        specialBoxProperties.put("image", new SpecialBoxProperty() {
+                public Object get(Box b) { return b.image == null ? null : Box.imageToNameMap.get(b.image); }
+                public void put(Box b, Object value) { b.setImage(value == null ? null : value.toString()); }
+            });
+
+        specialBoxProperties.put("border", new SpecialBoxProperty() {
+                public Object get(Box b) { return b.border == null ? null : Box.imageToNameMap.get(b.border); }
+                public void put(Box b, Object value) { b.setBorder(value == null ? null : value.toString()); }
+            });
+
+        specialBoxProperties.put("font", new SpecialBoxProperty() {
+                public Object get(Box b) { return b.font; }
+                public void put(Box b, Object value) {
+                    if (value == null) value = Platform.getDefaultFont();
+                    b.font = value.toString();
+                    b.textupdate();
+                    b.dirty();
+                } });
+        
+        specialBoxProperties.put("globalx", new SpecialBoxProperty() {
+                public Object get(Box b) { return new Integer(b.getParent() == null || b.surface == null ? 0 : b.pos(0)); }
+                public void put(Box b, Object value) {
+                    if (b.surface == null || b.getParent() == null) return;
+                    b.put("x", null, new Integer(stoi(value) - stoi(get(b.getParent()))));
+                }
+            });
+        
+        specialBoxProperties.put("globaly", new SpecialBoxProperty() {
+                public Object get(Box b) { return new Integer(b.getParent() == null || b.surface == null ? 0 : b.pos(1)); }
+                public void put(Box b, Object value) {
+                    if (b.surface == null || b.getParent() == null) return;
+                    b.put("y", null, new Integer(stoi(value) - stoi(get(b.getParent()))));
+                }
+            });
+        
+        specialBoxProperties.put("cursor", new SpecialBoxProperty() {
+                public Object get(Box b) { return b.cursor; } 
+                public void put(Box b, Object value) {
+                    b.cursor = (String)value;
+                    if (b.surface == null) return;
+
+                    // see if we need to update the surface cursor
+                    String tempcursor = b.surface.cursor;
+                    b.Move(b.surface.mousex, b.surface.mousey, b.surface.mousex, b.surface.mousey);
+                    if (b.surface.cursor != tempcursor) b.surface.syncCursor();
+                } 
+            });
+        
+        specialBoxProperties.put("mousex", new SpecialBoxProperty() {
+                public Object get(Box b) { return new Integer(b.surface == null ? 0 : b.surface.mousex - b.pos(0)); }
+            });
+        
+        specialBoxProperties.put("mousey", new SpecialBoxProperty() {
+                public Object get(Box b) { return new Integer(b.surface == null ? 0 : b.surface.mousey - b.pos(1)); }
+            });
+        
+        specialBoxProperties.put("xwt", new SpecialBoxProperty() {
+                public Object get(Box b) { return XWT.singleton; }
+            });
+        
+        specialBoxProperties.put("mouseinside", new SpecialBoxProperty() {
+                public Object get(Box b) { return b.mouseinside ? Boolean.TRUE : Boolean.FALSE; }
+            });
+        
+        specialBoxProperties.put("numchildren", new SpecialBoxProperty() {
+                public Object get(Box b) {
+                    if (b.redirect == null) return new Integer(0);
+                    if (b.redirect != b) return get(b.redirect);
+                    return new Integer(b.numChildren());
+                } });
+        
+        SpecialBoxProperty mouseEventHandler = new SpecialBoxProperty() {
+                public void put(String name, Box b, Object value) {
+                    if (b.surface == null) return;
+                    if (b.getParent() == null) {
+                        if (b.surface.boxContainingMouse.getParent() != null)
+                            b.surface.boxContainingMouse.put(name, b.surface.boxContainingMouse, value);
+                    } else {
+                        // check siblings
+                        for(Box c = b.prevSibling(); c != null; c = c.prevSibling())
+                            if (c.inside(c.surface.mousex, c.surface.mousey)) {
+                                c.put(name, c, value);
+                                return;
+                            }
+                        // move up a level
+                        if (b.getParent() != null && b.getParent().getParent() != null)
+                            b.getParent().put(name, b.getParent(), value);
+                    }
+                }};
+
+        specialBoxProperties.put("Press1", mouseEventHandler);
+        specialBoxProperties.put("Press2", mouseEventHandler);
+        specialBoxProperties.put("Press3", mouseEventHandler);
+        specialBoxProperties.put("Release1", mouseEventHandler);
+        specialBoxProperties.put("Release2", mouseEventHandler);
+        specialBoxProperties.put("Release3", mouseEventHandler);
+        specialBoxProperties.put("Click1", mouseEventHandler);
+        specialBoxProperties.put("Click2", mouseEventHandler);
+        specialBoxProperties.put("Click3", mouseEventHandler);
+        specialBoxProperties.put("DoubleClick1", mouseEventHandler);
+        specialBoxProperties.put("DoubleClick2", mouseEventHandler);
+        specialBoxProperties.put("DoubleClick3", mouseEventHandler);
+
+        specialBoxProperties.put("root", new SpecialBoxProperty() {
+                public Object get(Box b) {
+                    if (b.surface == null) return null;
+                    else if (b.getRoot() == null) return null;
+                    else return b.getRoot().getRootProxy();
+                } });
+
+        specialBoxProperties.put("Minimized", new SpecialBoxProperty() {
+                public Object get(Box b) {
+                    if (b.getParent() == null && b.surface != null) return b.surface.minimized ? Boolean.TRUE : Boolean.FALSE;
+                    else return null;
+                }
+                public void put(Box b, Object value) {
+                    if (b.surface == null) return;
+                    boolean val = stob(value);
+                    if (b.getParent() == null && b.surface.minimized != val) b.surface.setMinimized(val);
+                }
+            });
+
+        specialBoxProperties.put("Maximized", new SpecialBoxProperty() {
+                public Object get(Box b) {
+                    if (b.getParent() == null && b.surface != null) return b.surface.maximized ? Boolean.TRUE : Boolean.FALSE;
+                    else return null;
+                }
+                public void put(Box b, Object value) {
+                    if (b.surface == null) return;
+                    boolean val = stob(value);
+                    if (b.getParent() == null && b.surface.maximized != val) b.surface.setMaximized(val);
+                }
+            });
+
+        specialBoxProperties.put("toback", new SpecialBoxProperty() {
+                public void put(Box b, Object value) {
+                    if (b.getParent() == null && stob(value) && b.surface != null) b.surface.toBack();
+                }
+            });
+
+        specialBoxProperties.put("tofront", new SpecialBoxProperty() {
+                public void put(Box b, Object value) {
+                    if (b.getParent() == null && stob(value) && b.surface != null) b.surface.toFront();
+                }
+            });
+
+        specialBoxProperties.put("hscar", new SpecialBoxProperty() {
+                public void put(Box b, Object value) {
+                    if (b.getParent() == null && b.surface != null) {
+                        b.surface.hscar = stoi(value);
+                        b.surface.dirty(0, 0, b.surface.width, b.surface.height);
+                        b.surface.Refresh();
+                    }
+                }
+            });
+
+        specialBoxProperties.put("vscar", new SpecialBoxProperty() {
+                public void put(Box b, Object value) {
+                    if (b.getParent() == null && b.surface != null) {
+                        b.surface.vscar = stoi(value);
+                        b.surface.dirty(0, 0, b.surface.width, b.surface.height);
+                        b.surface.Refresh();
+                    }
+                }
+            });
+
+        specialBoxProperties.put("Close", new SpecialBoxProperty() {
+                public void put(Box b, Object value) {
+                    if (b.getParent() == null && b.surface != null) b.surface.dispose();
+                }
+            });
+
+        // these are all do-nothings; just to prevent space from getting taken up in the params Hash.
+        specialBoxProperties.put("KeyPressed", new SpecialBoxProperty());
+        specialBoxProperties.put("KeyReleased", new SpecialBoxProperty());
+        specialBoxProperties.put("PosChange", new SpecialBoxProperty());
+        specialBoxProperties.put("SizeChange", new SpecialBoxProperty());
+
+        specialBoxProperties.put("hpad", new SpecialBoxProperty() {
+                public Object get(Box b) {
+                    if (b.redirect == null) return new Integer(0);
+                    if (b.redirect != b) return get(b.redirect);
+                    return new Integer(b.pad(0));
+                }
+                public void put(Box b, Object value) {
+                    if (b.redirect == null) return;
+                    if (b.redirect != b) { put(b.redirect, value); return; }
+                    short newval = stosh(value);
+                    if (newval == b.pad(0)) return;
+                    b.set(pad, 0, newval);
+                }
+            });
+
+        specialBoxProperties.put("vpad", new SpecialBoxProperty() {
+                public Object get(Box b) {
+                    if (b.redirect == null) return new Integer(0);
+                    if (b.redirect != b) return get(b.redirect);
+                    return new Integer(b.pad(1));
+                }
+                public void put(Box b, Object value) {
+                    if (b.redirect == null) return;
+                    if (b.redirect != b) { put(b.redirect, value); return; }
+                    short newval = stosh(value);
+                    if (newval == b.pad(1)) return;
+                    b.set(pad, 1, newval);
+                }
+            });
+
+        specialBoxProperties.put("indexof", new SpecialBoxProperty() {
+                public Object get(Box b) { return b.indexof(); }
+            });
+
+        specialBoxProperties.put("minwidth", new SpecialBoxProperty() {
+                public Object get(Box b) { return new Integer(b.dmin(0)); }
+                public void put(Box b, Object value) {
+                    if (b.sizetoimage) return;
+                    b.set(dmin, 0, stosh(value));
+                }
+            });
+
+        specialBoxProperties.put("maxwidth", new SpecialBoxProperty() {
+                public Object get(Box b) { return new Integer(b.dmax(0)); }
+                public void put(Box b, Object value) {
+                    if (b.sizetoimage) return;
+                    b.set(dmax, 0, stosh(value));
+                }
+            });
+
+        specialBoxProperties.put("minheight", new SpecialBoxProperty() {
+                public Object get(Box b) { return new Integer(b.dmin(1)); }
+                public void put(Box b, Object value) {
+                    if (b.sizetoimage) return;
+                    b.set(dmin, 1, stosh(value));
+                }
+            });
+
+        specialBoxProperties.put("maxheight", new SpecialBoxProperty() {
+                public Object get(Box b) { return new Integer(b.dmax(1)); }
+                public void put(Box b, Object value) {
+                    if (b.sizetoimage) return;
+                    b.set(dmax, 1, stosh(value));
+                }
+            });
+    }
+
+        
+    /** helper that converts a String to a boolean according to JavaScript coercion rules */
+    public static boolean stob(Object o) {
+        if (o == null) return false;
+        return Boolean.TRUE.equals(o) || "true".equals(o);
+    }
+
+    /** helper that converts a String to an int according to JavaScript coercion rules */
+    public static int stoi(Object o) {
+        if (o == null) return 0;
+        if (o instanceof Integer) return ((Integer)o).intValue();
+
+        String s;
+        if (!(o instanceof String)) s = o.toString();
+        else s = (String)o;
+
+        try { return Integer.parseInt(s.indexOf('.') == -1 ? s : s.substring(0, s.indexOf('.'))); }
+        catch (NumberFormatException e) { return 0; }
+    }
+        
+    /** helper that converts a String to a short according to JavaScript coercion rules */
+    public static short stosh(Object o) {
+        if (o == null) return 0;
+        if (o instanceof Number) return ((Number)o).shortValue();
+
+        String s;
+        if (!(o instanceof String)) s = o.toString();
+        else s = (String)o;
+
+        try { return Short.parseShort(s.indexOf('.') == -1 ? s : s.substring(0, s.indexOf('.'))); }
+        catch (NumberFormatException e) { return 0; }
+    }
+        
+}
+
+        
+
diff --git a/src/org/xwt/Static.java b/src/org/xwt/Static.java
new file mode 100644 (file)
index 0000000..59450a7
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import org.xwt.util.*;
+import org.mozilla.javascript.*;
+
+/** implements objects in the xwt.static.* namespace */
+public class Static extends JSObject {
+
+    public static Static getStatic(String resourcename) {
+        Static ret = (Static)cache.get(resourcename);
+        if (ret == null) {
+            Template t = Template.getTemplate(resourcename, null);
+            if (t == null) return null;
+            ret = new Static(resourcename, false);
+            t.link();
+            return ret;
+        }
+        return ret;
+    }
+
+    private static Hash cache = new Hash();
+
+    /** the resource name that this Static object corresponds to */
+    private String resourcename = null;
+
+    /** true iff this represents a directory (rather than an actual xwt) */
+    public boolean ispackage = false;
+
+    private Static(String resourcename, boolean ispackage) {
+        super(true);
+        cache.put(resourcename, this);
+        this.resourcename = resourcename;
+        this.ispackage = ispackage;
+        setSeal(ispackage);
+    }
+    
+    /** creates a new static representing a package */
+    public Static(String resourcename) {
+        this(resourcename, true);
+    }
+
+    public Object get(String name, Scriptable start) {
+        if (name == null) return null;
+
+        // hack since Rhino needs to be able to grab these functions to create new objects
+        if (name.equals("Object")) return JSObject.defaultObjects.get("Object", null);
+        if (name.equals("Array")) return JSObject.defaultObjects.get("Array", null);
+        if (name.equals("Function")) return JSObject.defaultObjects.get("Function", null);
+        if (name.equals("TypeError")) return JSObject.defaultObjects.get("TypeError", null);
+
+        if ("xwt".equals(name)) return XWT.singleton;
+        if (!ispackage) return super.get(name, start);
+        return getStatic(resourcename + (resourcename.length() == 0 ? "" : ".") + name);
+    }
+
+}
diff --git a/src/org/xwt/Surface.java b/src/org/xwt/Surface.java
new file mode 100644 (file)
index 0000000..4cc5f59
--- /dev/null
@@ -0,0 +1,580 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import org.bouncycastle.util.encoders.Base64;
+import org.xwt.util.*;
+import java.io.*;
+import java.util.*;
+
+/** 
+ *  A Surface, as described in the XWT Reference.
+ *
+ *  Platform subclasses should include an inner class subclass of
+ *  Surface to return from the Platform._createSurface() method
+ *
+ *  Note that the members in the section 'state variables' are either
+ *  in real-time (the actual size/position/state), or in
+ *  MessageQueue-time (the size/position/state at the time that the
+ *  now-executing message was enqueued). This distinction is important.
+ */
+public abstract class Surface {
+
+    // Static Data ////////////////////////////////////////////////////////////////////////////////
+
+    /** true iff a user-created surface was created */
+    static boolean refreshableSurfaceWasCreated = false;
+
+    /** a reference to the most recently enqueued Move message; used to throttle the message rate */
+    private static Message lastMoveMessage = null;
+
+    /** all instances of Surface which need to be refreshed by the MessageQueue */
+    public static Vec allSurfaces = new Vec();
+
+    /** true iff the alt button is pressed down, in real time */
+    public static boolean alt = false;
+    
+    /** true iff the control button is pressed down, in real time */
+    public static boolean control = false;
+
+    /** true iff the shift button is pressed down, in real time */
+    public static boolean shift = false;
+
+    /** true iff button 1 is depressed, in MessageQueue-time */
+    public static boolean button1 = false;
+
+    /** true iff button 2 is depressed, in MessageQueue-time */
+    public static boolean button2 = false;
+
+    /** true iff button 3 is depressed, in MessageQueue-time */
+    public static boolean button3 = false;
+
+
+    // Public Members and State Variables /////////////////////////////////////////////////////////
+
+    /** this is the box on this surface which the mouse was inside at the time that the currently-executing event was enqueued */
+    public Box boxContainingMouse = null;
+
+    /** false if the surface has never been rendered; used to determine if the surface should be repositioned to be centered on the screen */
+    public boolean centerSurfaceOnRender = true;
+
+    /** the x position of the mouse, relative to this Surface, in MessageQueue-time */
+    public int mousex;
+
+    /** the y position of the mouse, relative to this Surface, in MessageQueue-time */
+    public int mousey;
+
+    /** True iff this surface is minimized, in real time */
+    public boolean minimized = false;
+
+    /** True iff this surface is maximized, in real time */
+    public boolean maximized = false;
+
+    /** The name of the cursor on this surface -- this value fluctuates during rendering, so it may not be accurate;
+     *  syncCursor() is called once the value is stable, to prevent the "flickering cursor" phenomenon
+     */
+    public String cursor = "default";
+
+    /** The width of the surface's drawable area, in real time */
+    public int width = 0;
+
+    /** The height of the surface's drawable area, in real time */
+    public int height = 0;
+
+    /** The Box at the root of this surface */
+    public Box root;
+
+    /** The number of SizeChange/PosChange traps triggered since the last successful render -- used to detect infinite loops */
+    public int sizePosChangesSinceLastRender = 0;
+
+    /** the x-position of the mouse the last time a Press message was enqueued */
+    int last_press_x = Integer.MAX_VALUE;
+
+    /** the y-position of the mouse the last time a Press message was enqueued */
+    int last_press_y = Integer.MAX_VALUE;
+
+
+    
+    // Methods to be overridden by subclasses ///////////////////////////////////////////////////////
+
+    /** when this method is invoked, the surface should push itself to the back of the stacking order */
+    public abstract void toBack();
+
+    /** when this method is invoked, the surface should pull itself to the front of the stacking order */
+    public abstract void toFront();
+
+    /** sets the <i>actual</i> cursor for this surface to the cursor referenced by <tt>cursor</tt> */
+    public abstract void syncCursor();
+
+    /** If <tt>b == true</tt>, make the window invisible; otherwise, make it non-invisible. */
+    public abstract void setInvisible(boolean b);
+
+    /** If <tt>b == true</tt>, maximize the surface; otherwise, un-maximize it. */
+    protected abstract void _setMaximized(boolean b);
+
+    /** If <tt>b == true</tt>, minimize the surface; otherwise, un-minimize it. */
+    protected abstract void _setMinimized(boolean b);
+
+    /** Sets the surface's width and height. */
+    protected abstract void setSize(int width, int height);
+
+    /** Sets the surface's x and y position. */
+    public abstract void setLocation(int x, int y);
+
+    /** Sets the surface's title bar text, if applicable */
+    public abstract void setTitleBarText(String s);
+
+    /** Sets the surface's title bar text, if applicable */
+    public abstract void setIcon(Picture i);
+
+    /** copies a region from the doublebuffer to this surface */
+    public abstract void blit(DoubleBuffer source, int sx, int sy, int dx, int dy, int dx2, int dy2);
+
+    /** Destroy the surface */
+    public abstract void _dispose();
+
+
+    // Helper methods for subclasses ////////////////////////////////////////////////////////////
+
+    protected final void Press(final int button) {
+        last_press_x = mousex;
+        last_press_y = mousey;
+
+        if (button == 1) button1 = true;
+        else if (button == 2) button2 = true;
+        else if (button == 3) button3 = true;
+
+        if (button == 1) new SimpleMessage("Press1", Boolean.TRUE, root.whoIs(mousex, mousey));
+        else if (button == 2) new SimpleMessage("Press2", Boolean.TRUE, root.whoIs(mousex, mousey));
+        else if (button == 3) {
+            final Box who = root.whoIs(mousex, mousey);
+            MessageQueue.add(new Message() { public void perform() {
+                Surface.this.boxContainingMouse = who;
+                Platform.clipboardReadEnabled = true;
+                root.put("Press1", null, Boolean.TRUE);
+                Platform.clipboardReadEnabled = false;
+            }});
+        }
+    }
+
+    protected final void Release(int button) {
+        if (button == 1) button1 = false;
+        else if (button == 2) button2 = false;
+        else if (button == 3) button3 = false;
+
+        if (button == 1) new SimpleMessage("Release1", Boolean.TRUE, root.whoIs(mousex, mousey));
+        else if (button == 2) new SimpleMessage("Release2", Boolean.TRUE, root.whoIs(mousex, mousey));
+        else if (button == 3) new SimpleMessage("Release3", Boolean.TRUE, root.whoIs(mousex, mousey));
+
+        if (Platform.needsAutoClick() && Math.abs(last_press_x - mousex) < 5 && Math.abs(last_press_y - mousey) < 5) Click(button);
+        last_press_x = Integer.MAX_VALUE;
+        last_press_y = Integer.MAX_VALUE;
+    }
+
+    protected final void Click(int button) {
+        if (button == 1) new SimpleMessage("Click1", Boolean.TRUE, root.whoIs(mousex, mousey));
+        else if (button == 2) new SimpleMessage("Click2", Boolean.TRUE, root.whoIs(mousex, mousey));
+        else if (button == 3) new SimpleMessage("Click3", Boolean.TRUE, root.whoIs(mousex, mousey));
+    }
+
+    protected final void DoubleClick(int button) {
+        if (button == 1) new SimpleMessage("DoubleClick1", Boolean.TRUE, root.whoIs(mousex, mousey));
+        else if (button == 2) new SimpleMessage("DoubleClick2", Boolean.TRUE, root.whoIs(mousex, mousey));
+        else if (button == 3) new SimpleMessage("DoubleClick3", Boolean.TRUE, root.whoIs(mousex, mousey));
+    }
+
+    /** sends a KeyPressed message; subclasses should not add the C- or A- prefixes, nor should they capitalize alphabet characters */
+    protected final void KeyPressed(String key) {
+        if (key == null) return;
+        
+        if (key.equals("alt")) alt = true;
+        else if (alt) key = "A-" + key;
+
+        if (key.endsWith("control")) control = true;
+        else if (control) key = "C-" + key;
+
+        if (key.endsWith("shift")) shift = true;
+        else if (shift) key = key.toUpperCase();
+
+        final String fkey = key;
+        MessageQueue.add(new KMessage(key));
+    }
+
+    // This is implemented as a private static class instead of an anonymous class to work around a GCJ bug
+    private class KMessage implements Message {
+        String key = null;
+        public KMessage(String k) { key = k; }
+        public void perform() {
+            if (key.equals("C-v") || key.equals("A-v")) Platform.clipboardReadEnabled = true;
+            for(int i=0; i<keywatchers.size(); i++)
+                ((Box)keywatchers.elementAt(i)).put("KeyPressed", null, key);
+            Platform.clipboardReadEnabled = false;
+        }
+    }
+
+    /** sends a KeyReleased message; subclasses should not add the C- or A- prefixes, nor should they capitalize alphabet characters */
+    protected final void KeyReleased(final String key) {
+        if (key == null) return;
+        if (key.equals("alt")) alt = false;
+        else if (key.equals("control")) control = false;
+        else if (key.equals("shift")) shift = false;
+        MessageQueue.add(new Message() { public void perform() {
+            for(int i=0; i<keywatchers.size(); i++)
+                ((Box)keywatchers.elementAt(i)).put("KeyReleased", null, key);
+        }});
+    }
+
+    /**
+     *  Notify XWT that the mouse has moved. If the mouse leaves the
+     *  surface, but the host windowing system does not provide its new
+     *  position (for example, a Java MouseListener.mouseExited()
+     *  message), the subclass should use (-1,-1).
+     */
+    protected final void Move(final int newmousex, final int newmousey) {
+        MessageQueue.add(lastMoveMessage = new Message() { public void perform() {
+            synchronized(Surface.this) {
+
+                // if move messages are arriving faster than we can process them, we just start ignoring them
+                if (lastMoveMessage != this) return;
+
+                int oldmousex = mousex;
+                int oldmousey = mousey;
+                mousex = newmousex;
+                mousey = newmousey;
+
+                String oldcursor = cursor;
+                cursor = "default";
+
+                // Root gets motion events outside itself (if trapped, of course)
+                if (root.is_trapped("Move") && !root.inside(oldmousex, oldmousey) && !root.inside(mousex, mousey) && (button1 || button2 || button3))
+                    root.put("Move", null, Boolean.TRUE);
+
+                root.Move(oldmousex, oldmousey, mousex, mousey);
+                if (!cursor.equals(oldcursor)) syncCursor();
+            }
+        }});
+    }
+
+    protected final void SizeChange(int width, int height) {
+        this.width = width;
+        this.height = height;
+        abort = true;
+        lastResizeTime = System.currentTimeMillis();
+        Refresh();
+    }
+
+    protected final void PosChange(int x, int y) {
+        root.set(Box.abs, 0, x);
+        root.set(Box.abs, 1, y);
+        new SimpleMessage("PosChange", Boolean.TRUE, null);
+    }
+
+    protected final void Close() { new SimpleMessage("Close", Boolean.TRUE, null); }
+    protected final void Minimized(boolean b) { minimized = b; new SimpleMessage("Minimized", b ? Boolean.TRUE : Boolean.FALSE, null); }
+    protected final void Maximized(boolean b) { maximized = b; new SimpleMessage("Maximized", b ? Boolean.TRUE : Boolean.FALSE, null); }
+    protected final void Focused(boolean b) { new SimpleMessage("Focused", b ? Boolean.TRUE : Boolean.FALSE, null); }
+    public static void Refresh() { MessageQueue.refresh(); }
+
+    /** used in conjunction with Platform.supressDirtyOnResize() */
+    private long lastResizeTime = 0;
+
+    /** This is how subclasses signal a 'shallow dirty', indicating that although the backbuffer is valid, the screen is not */
+    public final void Dirty(int x, int y, int w, int h) {
+        if (Platform.supressDirtyOnResize() && System.currentTimeMillis() - lastResizeTime < 100 && (w >= width - 1 || h >= height - 1)) return;
+        screenDirtyRegions.dirty(x, y, w, h);
+        blitDirtyScreenRegions();
+    }
+
+
+    // Private Instance Data /////////////////////////////////////////////////////////////////////////////////////////////
+
+    /** The automatic double buffer for the root box */
+    DoubleBuffer backbuffer = null;
+
+    /** Dirty regions on the backbuffer which need to be rebuilt using Box.render() */
+    private DirtyList backbufferDirtyRegions = new DirtyList();
+
+    /** Dirty regions on the screen which need to be rebuilt using Surface.blit() */
+    private DirtyList screenDirtyRegions = new DirtyList();
+
+    /** A list of all the Boxes on this Surface that should be notified of keyboard events */
+    Vec keywatchers = new Vec();
+
+    /**
+     *  this is incremented every time we render; it acts as a sort of 'timestamp' to let Boxes to know if they have
+     *  been dirtied since the last rendering began (and hence should not propagate up dirty() requests from their children)
+     */
+    volatile int dirtiedTimeStamp = 0;
+
+    /** When set to true, render() should abort as soon as possible and restart the rendering process */
+    volatile boolean abort = false;
+
+    /** a solid red 10x10 double buffer */
+    private DoubleBuffer showRenderBuf = null;
+
+    /** a striped 100x100 double buffer */
+    private DoubleBuffer showRenderBuf2 = null;
+
+
+
+    // Other Methods ///////////////////////////////////////////////////////////////////////////////
+
+    /** If <tt>b == true</tt>, maximize the surface; otherwise, un-maximize it. */
+    public final void setMaximized(boolean b) {
+        if (b == maximized) return;
+        _setMaximized(b);
+        maximized = b;
+    }
+
+    /** If <tt>b == true</tt>, minimize the surface; otherwise, un-minimize it. */
+    public final void setMinimized(boolean b) {
+        if (b == minimized) return;
+        _setMinimized(b);
+        minimized = b;
+    }
+
+    /** wrapper for setSize() which makes sure to dirty the place where the scar used to be */
+    void _setSize(int width, int height) {
+        dirty(hscar,
+              root.size(1) - vscar - scarPicture.getHeight(),
+              scarPicture.getWidth(), scarPicture.getHeight());
+        setSize(width, height);
+        this.width = width;
+        this.height = height;
+    }
+
+    /** Indicates that the Surface is no longer needed */
+    public final void dispose() {
+        if (root == null) return;
+        if (Log.on) Log.log(this, "disposing " + this);
+        allSurfaces.removeElement(this);
+        _dispose();
+
+        // quit when all windows are closed
+        if (allSurfaces.size() == 0) {
+            if (Log.on) {
+                if (refreshableSurfaceWasCreated) Log.log(this, "exiting because last remaining surface was disposed");
+                else Log.log(this, "exiting because no surface was ever created");
+            }
+            Platform.exit();
+        }
+    }
+
+    /** Indicates that the backbuffer region x,y,w,h is no longer correct and must be regenerated */
+    public void dirty(int x, int y, int w, int h) {
+        backbufferDirtyRegions.dirty(x, y, w, h);
+        Refresh();
+    }
+
+    public Surface(Box root) {
+        this.root = root;
+        if (root.surface != null && root.surface.root == root) root.surface.dispose();
+        root.remove();
+        root.setSurface(this);
+
+        // make sure the root is properly sized
+        while (root.needs_prerender || abort) {
+            abort = false;
+            root.prerender();
+        }
+
+        // this is a bit dangerous since we're passing ourselves to another method before subclasses' ctors have run...        
+        backbuffer = Platform.createDoubleBuffer(Platform.getScreenWidth(), Platform.getScreenHeight(), this);
+
+        root.mark_for_prerender();
+        root.dirty();
+        Refresh();
+    }
+
+    /** runs the prerender() and render() pipelines in the root Box to regenerate the backbuffer, then blits it to the screen */
+    public synchronized void render() {
+
+        if (++dirtiedTimeStamp == Integer.MAX_VALUE - 1) dirtiedTimeStamp = 0;
+
+        // if the window size changed as a result of a user action, we have to update the root box's size
+        if (root.size(0) != width || root.size(1) != height) {
+
+            // since the scar will be moving, dirty the place it used to be
+            dirty(hscar,
+                  root.size(1) - vscar - scarPicture.getHeight(),
+                  scarPicture.getWidth(), scarPicture.getHeight());
+
+            // sort of ugly; we can't use set() here because it will cause an infinite mutual recursion
+            root._size_0 = (short)width;
+            root._size_1 = (short)height;
+
+            root.mark_for_prerender();
+            root.put("SizeChange", null, Boolean.TRUE);
+        }
+
+        while (root.needs_prerender || abort) {
+            abort = false;
+            root.prerender();
+
+            // update mouseinside and trigger Enter/Leave as a result of box size/position changes
+            String oldcursor = cursor;
+            cursor = "default";
+            root.Move(mousex, mousey, mousex, mousey);
+            if (!cursor.equals(oldcursor)) syncCursor();
+        }
+
+        if (centerSurfaceOnRender) {
+            centerSurfaceOnRender = false;
+            int x = (Platform.getScreenWidth() - width) / 2;
+            int y = (Platform.getScreenHeight() - height) / 2;
+            setLocation(x, y);
+            root.set(Box.abs, 0, x);
+            root.set(Box.abs, 1, y);
+        }
+
+        sizePosChangesSinceLastRender = 0;
+        int[][] dirt = backbufferDirtyRegions.flush();
+        for(int i = 0; dirt != null && i < dirt.length; i++) {
+            if (dirt[i] == null) continue;
+            int x = dirt[i][0];
+            int y = dirt[i][1];
+            int w = dirt[i][2];
+            int h = dirt[i][3];
+            if (x < 0) x = 0;
+            if (y < 0) y = 0;
+            if (x+w > width) w = width - x;
+            if (y+h > height) h = height - y;
+            if (w <= 0 || h <= 0) continue;
+
+            root.render(x, y, w, h, backbuffer);
+            
+            // if any area under the scar was repainted, rescar that area
+            if (x < hscar + scarPicture.getWidth() &&
+                y + h > height - scarPicture.getHeight() - vscar) {
+                int _x1 = Math.max(x, hscar);
+                int _x2 = Math.min(x + w, hscar + scarPicture.getWidth());
+                int _y1 = Math.max(y, height - scarPicture.getHeight() - vscar);
+                int _y2 = Math.min(y + h, height - vscar);
+                
+                backbuffer.drawPicture(scarPicture, _x1, _y1, _x2, _y2,
+                                       _x1 - (hscar),
+                                       _y1 - (height - scarPicture.getHeight() - vscar),
+                                       _x2 - (hscar),
+                                       _y2 - (height - scarPicture.getHeight() - vscar)
+                                       );
+            }
+
+            if (abort) {
+
+                // x,y,w,h is only partially reconstructed, so we must be careful not to re-blit it
+                blitDirtyScreenRegions(x, y, w, h);
+                screenDirtyRegions.dirty(x, y, w, h);
+
+                // put back all the dirty regions we haven't yet processed (including the current one)
+                for(int j=i; j<dirt.length; j++)
+                    if (dirt[j] != null)
+                        backbufferDirtyRegions.dirty(dirt[j][0], dirt[j][1], dirt[j][2], dirt[j][3]);
+
+                // tail-recurse
+                render();
+                return;
+            }
+
+            // now that we've reconstructed this region in the backbuffer, queue it to be reblitted
+            screenDirtyRegions.dirty(x, y, w, h);
+        }
+
+        // blit out all the areas we've just reconstructed
+        blitDirtyScreenRegions();
+    }
+
+    /** blits from the backbuffer to the screen for all regions of the screen which have become dirty */
+    public synchronized void blitDirtyScreenRegions() { blitDirtyScreenRegions(-1, -1, 0, 0); }
+
+    /** same as blitDirtyScreenRegions(), except that it will skip any regions within a,b,c,d */
+    private synchronized void blitDirtyScreenRegions(int a, int b, int c, int d) {
+
+        int[][] dirt = screenDirtyRegions.flush();
+        if (Main.showRenders && dirt != null && dirt.length > 0 && a == -1)
+            blit(backbuffer, 0, 0, 0, 0, width, height);
+
+        for(int i = 0; dirt != null && i < dirt.length; i++) {
+            if (dirt[i] == null) continue;
+            int x = dirt[i][0];
+            int y = dirt[i][1];
+            int w = dirt[i][2];
+            int h = dirt[i][3];
+            if (x < 0) x = 0;
+            if (y < 0) y = 0;
+            if (x+w > root.size(0)) w = root.size(0) - x;
+            if (y+h > root.size(1)) h = root.size(1) - y;
+            if (w <= 0 || h <= 0) continue;
+
+            // if any part of this region falls within the "bad region", just skip it
+            boolean hhit = (x >= a && x <= a + c) || (x+w >= a && x+w <= a + c);
+            boolean vhit = (y >= b && y <= b + d) || (y+h >= b && y+h <= b + d);
+            if (hhit && vhit) {
+                screenDirtyRegions.dirty(x, y, w, h);
+                continue;
+            }
+
+            blit(backbuffer, x, y, x, y, w + x, h + y);
+            
+            if (Main.showRenders) {
+                if (showRenderBuf == null) {
+                    showRenderBuf = Platform.createDoubleBuffer(10, 10, this);
+                    showRenderBuf.fillRect(0, 0, 10, 10, 0x00FF0000);
+                    showRenderBuf2 = Platform.createDoubleBuffer(100, 100, this);
+                    for(int y1 = 0; y1<100; y1++)
+                        for(int x1 = 0; x1<100; x1++)
+                            if ((x1 + y1) % 5 == 0)
+                                showRenderBuf2.fillRect(x1, y1, x1 + 1, y1 + 1, 0x00FF0000);
+                }
+                for(int x1 = x; x1<x + w; x1 += 100)
+                    for(int y1 = y; y1< y + h; y1 += 100) {
+                        blit(showRenderBuf2, 0, 0, x1, y1, Math.min(x1 + 100, x + w), Math.min(y1 + 100, y + h));
+                    }
+                for(int j=x; j<x + w; j += 10) {
+                    blit(showRenderBuf, 0, 0, j, y, Math.min(j+ 10, x + w), y + 1);
+                    blit(showRenderBuf, 0, 0, j, y + h, Math.min(j + 10, x + w), y + h + 1);
+                }
+                for(int j=y; j<y + h; j += 10) {
+                    blit(showRenderBuf, 0, 0, x, j, x + 1, Math.min(j + 10, y + h));
+                    blit(showRenderBuf, 0, 0, x + w, j, x + w + 1, Math.min(j + 10, y + h));
+                }
+            }
+
+        }
+    }
+
+    // FEATURE: reinstate recycler
+    private class SimpleMessage implements Message {
+        
+        private Box boxContainingMouse;
+        private Object value;
+        private String name;
+        
+        SimpleMessage(String name, Object value, Box boxContainingMouse) {
+            this.boxContainingMouse = boxContainingMouse;
+            this.name = name;
+            this.value = value;
+            MessageQueue.add(this);
+        }
+        
+        public void perform() {
+            Surface.this.boxContainingMouse = this.boxContainingMouse;
+            root.put(name, root, value);
+        }
+
+        public String toString() {
+            return "SimpleMessage [name=" + name + ", value=" + value + "]";
+        }
+        
+    }
+
+    // Scar-Related Stuff ////////////////////////////////////////////////////////////////////
+
+    /** The scar's horizontal offset */
+    int hscar = 0;
+
+    /** The scar's vertical offset */
+    int vscar = 0;
+
+    /** the scar image drawn on the bottom right hand corner */
+    static Picture scarPicture = null;
+
+}
diff --git a/src/org/xwt/Template.java b/src/org/xwt/Template.java
new file mode 100644 (file)
index 0000000..0af2e0a
--- /dev/null
@@ -0,0 +1,745 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import java.io.*;
+import java.util.zip.*;
+import java.util.*;
+import java.lang.*;
+import org.mozilla.javascript.*;
+import org.xwt.util.*;
+
+/**
+ *  Encapsulates a template node (the &lt;template/&gt; element of a
+ *  .xwt file, or any child element thereof). Each instance of
+ *  Template has a <tt>nodeName</tt> -- this is the resource name of
+ *  the file that the template node occurs in, concatenated with the
+ *  path from the root element to this node, each step of which is in
+ *  the form .n for some integer n. Static nodes use the string "._"
+ *  as a path.
+ *
+ *  Note that the Template instance corresponding to the
+ *  &lt;template/&gt; node carries all the header information -- hence
+ *  some of the instance members are not meaningful on non-root
+ *  Template instances. We refer to these non-root instances as
+ *  <i>anonymous templates</i>.
+ *
+ *  See the XWT reference for information on the order in which
+ *  templates are applied, attributes are put, and scripts are run.
+ */
+public class Template {
+
+    // Instance Members ///////////////////////////////////////////////////////
+
+    /** this instance's nodeName */
+    String nodeName;
+
+    /** the id of the redirect target; only meaningful on a root node */
+    String redirect = null;
+
+    /** templates that should be preapplied (in the order of application); only meaningful on a root node */
+    private String[] preapply;
+
+    /** 'linked' form of preapply -- the String references have been resolved into instance references */
+    private Template[] _preapply = null;
+
+    /** templates that should be postapplied (in the order of application); only meaningful on a root node */
+    private String[] postapply;
+
+    /** 'linked' form of postapply -- the String references have been resolved into instance references */
+    private Template[] _postapply = null;
+
+    /** keys to be "put" to instances of this template; elements correspond to those of vals */
+    private String[] keys;
+
+    /** values to be "put" to instances of this template; elements correspond to those of keys */
+    private Object[] vals;
+
+    /** array of strings representing the importlist for this template */
+    private String[] importlist;
+
+    /** child template objects */
+    private Template[] children;
+
+    /** an array of the names of properties to be preserved when retheming; only meaningful on a root node */
+    private String[] preserve = null;
+    
+    /** the <tt>id</tt> attribute on this node */
+    private String id = "";
+
+    /** see numUnits(); -1 means that this value has not yet been computed */
+    private int numunits = -1;
+
+    /** true iff the resolution of this template's preapply/postapply sets changed as a result of the most recent call to retheme() */
+    private boolean changed = false;
+
+    /** the script on the static node of this template, null if it has already been executed */
+    private Script staticscript = null;
+
+    /** the script on this node */
+    private Script script = null;
+
+    /** during XML parsing, this holds the list of currently-parsed children; null otherwise */
+    private Vec childvect = new Vec();
+
+    /** during XML parsing, this holds partially-read character data; null otherwise */
+    private StringBuffer content = null;
+
+    /** line number of the first line of <tt>content</tt> */
+    private int content_start = 0;
+
+    /** number of lines in <tt>content</tt> */
+    private int content_lines = 0;
+
+
+    // Static data/methods ///////////////////////////////////////////////////////////////////
+
+    /** a template cache so that only one Template object is created for each xwt */
+    private static Hashtable cache = new Hashtable(1000);
+
+    /** The default importlist; in future revisions this will contain "xwt.*" */
+    public static final String[] defaultImportList = new String[] { };
+
+    /** returns the appropriate template, resolving and theming as needed */
+    public static Template getTemplate(String name, String[] importlist) {
+        String resolved = Resources.resolve(name + ".xwt", importlist);
+        Template t = resolved == null ? null : (Template)cache.get(resolved.substring(0, resolved.length() - 4));
+        if (t != null) return t;
+        if (resolved == null) return null;
+
+        // note that Templates in xwar's are instantiated as read in via loadStream() --
+        // the following code only runs when XWT is reading templates from a filesystem.
+        ByteArrayInputStream bais = new ByteArrayInputStream(Resources.getResource(resolved));
+        return buildTemplate(bais, resolved.substring(0, resolved.length() - 4));
+    }
+
+    public static Template buildTemplate(InputStream is, String nodeName) {
+        try {
+            return new Template(is, nodeName);
+        } catch (XML.SAXParseException e) {
+            if (Log.on) Log.log(Template.class, "error parsing template at " + nodeName + ":" + e.getLineNumber() + "," + e.getColumnNumber());
+            if (Log.on) Log.log(Template.class, e);
+            return null;
+        } catch (XML.SAXException e) {
+            if (Log.on) Log.log(Template.class, "error parsing template " + nodeName);
+            if (Log.on) Log.log(Template.class, e);
+            return null;
+        } catch (TemplateException te) {
+            if (Log.on) Log.log(Template.class, "error parsing template " + nodeName);
+            if (Log.on) Log.log(Template.class, te);
+            return null;
+        } catch (IOException e) {
+            if (Log.on) Log.log(Template.class, "IOException while parsing template " + nodeName + " -- this should never happen");
+            if (Log.on) Log.log(Template.class, e);
+            return null;
+        }
+    }
+
+
+    // Methods to apply templates ////////////////////////////////////////////////////////
+
+    private Template() { } 
+    private Template(InputStream is, String nodeName) throws XML.SAXException, IOException {
+        this.nodeName = nodeName;
+        cache.put(nodeName, this);
+        new TemplateHelper().parseit(is, this);
+    }
+
+    /** calculates, caches, and returns an integer approximation of how long it will take to apply this template, including pre/post and children */
+    int numUnits() {
+        link();
+        if (numunits != -1) return numunits;
+        numunits = 1;
+        for(int i=0; _preapply != null && i<_preapply.length; i++) if (_preapply[i] != null) numunits += _preapply[i].numUnits();
+        for(int i=0; _postapply != null && i<_postapply.length; i++) if (_postapply[i] != null) numunits += _postapply[i].numUnits();
+        if (script != null) numunits += 10;
+        numunits += keys == null ? 0 : keys.length;
+        for(int i=0; children != null && i<children.length; i++) numunits += children[i].numUnits();
+        return numunits;
+    }
+    
+    /** Applies the template to Box b
+     *  @param pboxes a vector of all box parents on which to put $-references
+     *  @param ptemplates a vector of the nodeNames to recieve private references on the pboxes
+     */
+    void apply(Box b, Vec pboxes, Vec ptemplates) {
+
+        if (pboxes == null) {
+            pboxes = new Vec();
+            ptemplates = new Vec();
+        }
+
+        if (id != null && !id.equals(""))
+            for(int i=0; i<pboxes.size(); i++) {
+                Box parent = (Box)pboxes.elementAt(i);
+                String parentNodeName = (String)ptemplates.elementAt(i);
+                parent.putPrivately("$" + id, b, parentNodeName);
+            }
+
+        if (script != null) {
+            pboxes.addElement(b);
+            ptemplates.addElement(nodeName);
+        }
+
+        int numids = pboxes.size();
+        
+        link();
+
+        for(int i=0; _preapply != null && i<_preapply.length; i++)
+            if (_preapply[i] != null) _preapply[i].apply(b, null, null);
+
+        for (int i=0; children != null && i<children.length; i++)
+            b.put(Integer.MAX_VALUE, null, new Box(children[i], pboxes, ptemplates));
+
+        // whom to redirect to; doesn't take effect until after script runs
+        Box redir = null;
+        if (redirect != null && !"self".equals(redirect))
+            redir = (Box)b.getPrivately("$" + redirect, nodeName);
+
+        if (script != null) try {
+            Context cx = Context.enter();
+            script.exec(cx, b);
+        } catch (EcmaError e) {
+            if (Log.on) Log.log(this, "WARNING: uncaught interpreter exception: " + e.getMessage());
+            if (Log.on) Log.log(this, "         thrown while instantiating " + nodeName + " at " + e.getSourceName() + ":" + e.getLineNumber());
+        } catch (JavaScriptException e) {
+            if (Log.on) Log.log(this, "WARNING: uncaught ecmascript exception: " + e.getMessage());
+            if (Log.on) Log.log(this, "         thrown while instantiating " + nodeName + " at " + e.sourceFile + ":" + e.line);
+        }
+
+        for(int i=0; keys != null && i<keys.length; i++)
+            if (keys[i] == null) { }
+            else if (keys[i].equals("border") || keys[i].equals("image")) {
+                String s = Resources.resolve(vals[i].toString() + ".png", importlist);
+                if (s != null) b.put(keys[i], null, s.substring(0, s.length() - 4));
+                else if (Log.on) Log.log(this, "unable to resolve image " + vals[i].toString() + " referenced in attributes of " + nodeName); 
+            }
+            else b.put(keys[i], null, vals[i]);
+
+        if (redirect != null && !"self".equals(redirect)) b.redirect = redir;
+
+        for(int i=0; _postapply != null && i<_postapply.length; i++)
+            if (_postapply[i] != null) _postapply[i].apply(b, null, null);
+
+        pboxes.setSize(numids);
+        ptemplates.setSize(numids);
+
+        Main.instantiatedUnits += 1 + (script == null ? 0 : 10) + (keys == null ? 0 : keys.length);
+        Main.updateSplashScreen();
+    }
+
+
+    // Theming Logic ////////////////////////////////////////////////////////////
+
+    /** helper method to recursively gather up the list of keys to be preserved */
+    private void gatherPreserves(Vec v) {
+        for(int i=0; preserve != null && i<preserve.length; i++) v.addElement(preserve[i]);
+        for(int i=0; _preapply != null && i<_preapply.length; i++) if (_preapply[i] != null) _preapply[i].gatherPreserves(v);
+        for(int i=0; _postapply != null && i<_postapply.length; i++) if (_postapply[i] != null) _postapply[i].gatherPreserves(v);
+    }
+
+    /** adds a theme mapping, retemplatizing as needed */
+    public static void retheme(String from, String to) {
+        if (Log.on) Log.log(Template.class, "retheming from " + from + " to " + to);
+        XWF.flushXWFs();
+        Resources.mapFrom.addElement(from);
+        Resources.mapTo.addElement(to);
+
+        // clear changed marker and relink
+        Template[] t = new Template[cache.size()];
+        Enumeration e = cache.elements();
+        for(int i=0; e.hasMoreElements(); i++) t[i] = (Template)e.nextElement();
+        for(int i=0; i<t.length; i++) {
+            t[i].changed = false;
+            t[i].numunits = -1;
+            t[i].link(true);
+        }
+
+        for(int i=0; i<Surface.allSurfaces.size(); i++) {
+            Box b = ((Surface)Surface.allSurfaces.elementAt(i)).root;
+            if (b != null) reapply(b);
+        }
+    }
+
+    /** template reapplication procedure */
+    private static void reapply(Box b) {
+
+        // Ref 7.5.1: check if we need to retemplatize
+        boolean retemplatize = false;
+        if (b.templatename != null) {
+            Template t = getTemplate(b.templatename, b.importlist);
+            if (t != b.template) retemplatize = true;
+            b.template = t;
+        }
+        if (b.template != null && b.template.changed)
+            retemplatize = true;
+
+        if (retemplatize) {
+
+            // Ref 7.5.2: "Preserve all properties on the box mentioned in the <preserve> elements of any
+            //             of the templates which would be applied in step 7."
+            Vec keys = new Vec();
+            b.template.gatherPreserves(keys);
+            Object[] vals = new Object[keys.size()];
+            for(int i=0; i<keys.size(); i++) vals[i] = b.get(((String)keys.elementAt(i)), null);
+            
+            // Ref 7.5.3: "Remove and save all children of the box, or its redirect target, if it has one"
+            Box[] kids = null;
+            if (b.redirect != null) {
+                kids = new Box[b.redirect.numChildren()];
+                for(int i=b.redirect.numChildren() - 1; i >= 0; i--) {
+                    kids[i] = b.redirect.getChild(i);
+                    kids[i].remove();
+                }
+            }
+            
+            // Ref 7.5.4: "Set the box's redirect target to self"
+            b.redirect = b;
+            
+            // Ref 7.5.5: "Remove all of the box's immediate children"
+            for(Box cur = b.getChild(b.numChildren() - 1); cur != null;) {
+                Box oldcur = cur;
+                cur = cur.prevSibling();
+                oldcur.remove();
+            }
+            
+            // Ref 7.5.6: "Remove all traps set by scripts run during the application of any template to this box"
+            Trap.removeAllTrapsByBox(b);
+            
+            // Ref 7.5.7: "Apply the template to the box according to the usual application procedure"
+            b.template.apply(b, null, null);
+            
+            // Ref 7.5.8: "Re-add the saved children which were removed in step 3"
+            for(int i=0; kids != null && i<kids.length; i++) b.put(Integer.MAX_VALUE, null, kids[i]);
+            
+            // Ref 7.5.9: "Re-put any property values which were preserved in step 2"
+            for(int i=0; i<keys.size(); i++) b.put((String)keys.elementAt(i), null, vals[i]);
+        }        
+
+        // Recurse
+        for(Box j = b.getChild(0); j != null; j = j.nextSibling()) reapply(j);
+    }
+
+    /** runs statics, resolves string references to other templates into actual Template instance references, and sets <tt>change</tt> as needed */
+    void link() { link(false); }
+
+    /** same as link(), except that with a true value, it will force a re-link */
+    private void link(boolean force) {
+
+        if (staticscript != null) try { 
+            Scriptable s = Static.getStatic(nodeName);
+            if (staticscript != null) {
+                Script temp = staticscript;
+                staticscript = null;
+                temp.exec(Context.enter(), s);
+            }
+        } catch (EcmaError e) {
+            if (Log.on) Log.log(this, "WARNING: uncaught interpreter exception: " + e.getMessage());
+            if (Log.on) Log.log(this, "         thrown while executing <static/> block for " + nodeName +
+                                      " at " + e.getSourceName() + ":" + e.getLineNumber());
+        } catch (JavaScriptException e) {
+            if (Log.on) Log.log(this, "WARNING: uncaught ecmascript exception: " + e.getMessage());
+            if (Log.on) Log.log(this, "         thrown while executing <static/> block for " + nodeName + " at " + e.sourceFile + ":" + e.line);
+        }
+
+        if (!(force || (preapply != null && _preapply == null) || (postapply != null && _postapply == null))) return;
+        
+        if (preapply != null) {
+            if (_preapply == null) _preapply = new Template[preapply.length];
+            for(int i=0; i<_preapply.length; i++) {
+                Template t = getTemplate(preapply[i], importlist);
+                if (t != _preapply[i]) changed = true;
+                _preapply[i] = t;
+            }
+        }
+        if (postapply != null) {
+            if (_postapply == null) _postapply = new Template[postapply.length];
+            for(int i=0; i<_postapply.length; i++) {
+                Template t = getTemplate(postapply[i], importlist);
+                if (t != _postapply[i]) changed = true;
+                _postapply[i] = t;
+            }
+        }
+
+        for(int i=0; children != null && i<children.length; i++) children[i].link(force);
+    }
+
+
+    // XML Parsing /////////////////////////////////////////////////////////////////
+
+    /** handles XML parsing; builds a Template tree as it goes */
+    private static class TemplateHelper extends XML {
+
+        TemplateHelper() {
+            for(int i=0; i<defaultImportList.length; i++) importlist.addElement(defaultImportList[i]);
+        }
+
+        /** parse an XML input stream, building a Template tree off of <tt>root</tt> */
+        void parseit(InputStream is, Template root) throws XML.SAXException, IOException {
+            t = root;
+            parse(new TabAndMaxColumnEnforcingReader(new InputStreamReader(is), root.nodeName)); 
+        }
+
+        /** parsing state: true iff we have already encountered the <xwt> open-tag */
+        boolean rootNodeHasBeenEncountered = false;
+
+        /** parsing state: true iff we have already encountered the <template> open-tag */
+        boolean templateNodeHasBeenEncountered = false;
+
+        /** parsing state: true iff we have already encountered the <static> open-tag */
+        boolean staticNodeHasBeenEncountered = false;
+
+        /** parsing state: true iff we have already encountered the <template> close-tag */
+        boolean templateNodeHasBeenFinished = false;
+
+        /** parsing state: If we have encountered the open tag of a header node, but not the close tag, this is the name of
+         *  that tag; otherwise, it is null. */
+        String nameOfHeaderNodeBeingProcessed = null;
+
+        /** stack of Templates whose XML elements we have seen open-tags for but not close-tags */
+        Vec nodeStack = new Vec();
+
+        /** builds up the list of imports */
+        Vec importlist = new Vec();
+
+        /** builds up the list of preapplies */
+        Vec preapply = new Vec();
+
+        /** builds up the list of postapplies */
+        Vec postapply = new Vec();
+
+        /** the template we're currently working on */
+        Template t = null;
+
+        public void startElement(String name, String[] keys, Object[] vals, int line, int col) throws XML.SAXException {
+
+            if (templateNodeHasBeenFinished) {
+                throw new XML.SAXException("no elements may appear after the <template> node");
+
+            } else if (!rootNodeHasBeenEncountered) {
+                if (!"xwt".equals(name)) throw new XML.SAXException("root element was not <xwt>");
+                if (keys.length != 0) throw new XML.SAXException("root element must not have attributes");
+                rootNodeHasBeenEncountered = true;
+                return;
+        
+            } else if (!templateNodeHasBeenEncountered) {
+                if (nameOfHeaderNodeBeingProcessed != null) throw new XML.SAXException("can't nest header nodes");
+                nameOfHeaderNodeBeingProcessed = name;
+
+                if (name.equals("import")) {
+                    if (keys.length != 1 || !keys[0].equals("name"))
+                        throw new XML.SAXException("<import> node must have exactly one attribute, which must be called 'name'");
+                    String importpackage = vals[0].toString();
+                    if (importpackage.endsWith(".*")) importpackage = importpackage.substring(0, importpackage.length() - 2);
+                    importlist.addElement(importpackage);
+                    return;
+
+                } else if (name.equals("redirect")) {
+                    if (keys.length != 1 || !keys[0].equals("target"))
+                        throw new XML.SAXException("<redirect> node must have exactly one attribute, which must be called 'target'");
+                    if (t.redirect != null)
+                        throw new XML.SAXException("the <redirect> header element may not appear more than once");
+                    t.redirect = vals[0].toString();
+                    return;
+
+                } else if (name.equals("preapply")) {
+                    if (keys.length != 1 || !keys[0].equals("name"))
+                        throw new XML.SAXException("<preapply> node must have exactly one attribute, which must be called 'name'");
+                    preapply.addElement(vals[0]);
+                    return;
+
+                } else if (name.equals("postapply")) {
+                    if (keys.length != 1 || !keys[0].equals("name"))
+                        throw new XML.SAXException("<postapply> node must have exactly one attribute, which must be called 'name'");
+                    postapply.addElement(vals[0]);
+                    return;
+
+                } else if (name.equals("static")) {
+                    if (staticNodeHasBeenEncountered)
+                        throw new XML.SAXException("the <static> header node may not appear more than once");
+                    if (keys.length > 0)
+                        throw new XML.SAXException("the <static> node may not have attributes");
+                    staticNodeHasBeenEncountered = true;
+                    return;
+
+                } else if (name.equals("preserve")) {
+                    if (keys.length != 1 || !keys[0].equals("attributes"))
+                        throw new XML.SAXException("<preserve> node must have exactly one attribute, which must be called 'attributes'");
+                    if (t.preserve != null)
+                        throw new XML.SAXException("<preserve> header element may not appear more than once");
+
+                    StringTokenizer tok = new StringTokenizer(vals[0].toString(), ",", false);
+                    t.preserve = new String[tok.countTokens()];
+                    for(int i=0; i<t.preserve.length; i++) t.preserve[i] = tok.nextToken();
+                    return;
+
+                } else if (name.equals("template")) {
+                    // finalize importlist/preapply/postapply, since they can't change from here on
+                    importlist.toArray(t.importlist = new String[importlist.size()]);
+                    if (preapply.size() > 0) preapply.copyInto(t.preapply = new String[preapply.size()]);
+                    if (postapply.size() > 0) postapply.copyInto(t.postapply = new String[postapply.size()]);
+                    importlist = preapply = postapply = null;
+                    templateNodeHasBeenEncountered = true;
+
+                } else {
+                    throw new XML.SAXException("unrecognized header node \"" + name + "\"");
+
+                }
+
+            } else {
+
+                // push the last node we were in onto the stack
+                nodeStack.addElement(t);
+
+                // instantiate a new node, and set its nodeName/importlist/preapply
+                Template t2 = new Template();
+                t2.nodeName = t.nodeName + "." + t.childvect.size();
+                t2.importlist = t.importlist;
+                if (!name.equals("box")) t2.preapply = new String[] { name };
+
+                // make the new node the current node
+                t = t2;
+
+            }
+
+            t.keys = keys;
+            t.vals = vals;
+
+            quickSortAttributes(0, t.keys.length - 1);
+
+            for(int i=0; i<t.keys.length; i++) {
+                if (t.keys[i].equals("id")) {
+                    t.id = vals[i].toString().intern();
+                    t.keys[i] = null;
+                    continue;
+                }
+
+                t.keys[i] = t.keys[i].intern();
+
+                String valString = vals[i].toString();
+                
+                if (valString.equals("true")) t.vals[i] = Boolean.TRUE;
+                else if (valString.equals("false")) t.vals[i] = Boolean.FALSE;
+                else if (valString.equals("null")) t.vals[i] = null;
+                else {
+                    boolean hasNonNumeral = false;
+                    boolean periodUsed = false;
+                    for(int j=0; j<valString.length(); j++)
+                        if (j == 0 && valString.charAt(j) == '-') {
+                        } else if (valString.charAt(j) == '.' && !periodUsed && j != valString.length() - 1)
+                            periodUsed = true;
+                        else if (valString.charAt(j) == '0' && (j == 0 || (j == 1 && valString.charAt(0) == '-'))) {
+                            hasNonNumeral = true;
+                            break;
+                        } else if (!Character.isDigit(valString.charAt(j))) {
+                            hasNonNumeral = true;
+                            break;
+                        }
+                    if (valString.length() > 0 && !hasNonNumeral) vals[i] = new Double(valString);
+                    else vals[i] = valString.intern();
+                }
+
+                // bump thisbox to the front of the pack
+                if (t.keys[i].equals("thisbox")) {
+                    t.keys[i] = t.keys[0];
+                    t.keys[0] = "thisbox";
+                    Object o = t.vals[0];
+                    t.vals[0] = t.vals[i];
+                    t.vals[i] = o;
+                }
+            }
+        }
+
+        /** simple quicksort, from http://sourceforge.net/snippet/detail.php?type=snippet&id=100240 */
+        private int partitionAttributes(int left, int right) {
+            int i, j, middle;
+            middle = (left + right) / 2;
+            String s = t.keys[right]; t.keys[right] = t.keys[middle]; t.keys[middle] = s;
+            Object o = t.vals[right]; t.vals[right] = t.vals[middle]; t.vals[middle] = o;
+            for (i = left - 1, j = right; ; ) {
+                while (t.keys[++i].compareTo(t.keys[right]) < 0);
+                while (j > left && t.keys[--j].compareTo(t.keys[right]) > 0);
+                if (i >= j) break;
+                s = t.keys[i]; t.keys[i] = t.keys[j]; t.keys[j] = s;
+                o = t.vals[i]; t.vals[i] = t.vals[j]; t.vals[j] = o;
+            }
+            s = t.keys[right]; t.keys[right] = t.keys[i]; t.keys[i] = s;
+            o = t.vals[right]; t.vals[right] = t.vals[i]; t.vals[i] = o;
+            return i;
+        }
+        
+        /** simple quicksort, from http://sourceforge.net/snippet/detail.php?type=snippet&id=100240 */
+        private void quickSortAttributes(int left, int right) {
+            if (left >= right) return;
+            int p = partitionAttributes(left, right);
+            quickSortAttributes(left, p - 1);
+            quickSortAttributes(p + 1, right);
+        }
+        
+        public void endElement(String name, int line, int col) throws XML.SAXException {
+
+            boolean hasNonWhitespace = false;
+
+            int len = t == null || t.content == null ? 0 : t.content.length();
+            for(int i=0; t.content != null && i<len; i++)
+                
+                // ignore double-slash comment blocks
+                if (t.content.charAt(i) == '/' && t.content.charAt(i + 1) == '/') {
+                    while(t.content.charAt(i) != '\n' && i<len) i++;
+                    i--;
+
+                // ignore /* .. */ comment blocks
+                } else if (i<len - 1 && t.content.charAt(i) == '/' && t.content.charAt(i + 1) == '*') {
+                    i += 2;
+                    while(i<len - 1 && !(t.content.charAt(i) == '*' && t.content.charAt(i + 1) == '/')) i++;
+                    if (i<len - 1 && t.content.charAt(i) == '*' && t.content.charAt(i + 1) == '/') i += 2;
+                    i--;
+
+                // check for named functions
+                } else if (i + 8 <= len && t.content.charAt(i) == 'f' && t.content.charAt(i+1) == 'u' &&
+                           t.content.charAt(i+2) == 'n' && t.content.charAt(i+3) == 'c' && t.content.charAt(i+4) == 't' &&
+                           t.content.charAt(i+5) == 'i' && t.content.charAt(i+6) == 'o' && t.content.charAt(i+7) == 'n') {
+                    int j = i + 8;
+                    while(j<len && Character.isWhitespace(t.content.charAt(j))) j++;
+                    if (j<len && t.content.charAt(j) != '(')
+                        throw new XML.SAXException("named functions are not permitted in XWT -- instead of \"function foo() { ... }\"," +
+                                        " use \"foo = function() { ... }\"");
+
+                // replace " and " with " && "
+                } else if (i + 5 < len && Character.isWhitespace(t.content.charAt(i)) &&
+                           t.content.charAt(i+1) == 'a' && t.content.charAt(i+2) == 'n' && t.content.charAt(i+3) == 'd' &&
+                           Character.isWhitespace(t.content.charAt(i + 4))) {
+                    t.content.setCharAt(i+1, '&');
+                    t.content.setCharAt(i+2, '&');
+                    t.content.setCharAt(i+3, ' ');
+                    hasNonWhitespace = true;
+
+                // generic check for nonwhitespace
+                } else if (!Character.isWhitespace(t.content.charAt(i))) {
+                    hasNonWhitespace = true;
+
+                }
+            
+            if (rootNodeHasBeenEncountered && !templateNodeHasBeenEncountered) {
+                if ("static".equals(nameOfHeaderNodeBeingProcessed) && hasNonWhitespace) t.staticscript = genscript(true);
+                nameOfHeaderNodeBeingProcessed = null;
+
+            } else if (templateNodeHasBeenEncountered && !templateNodeHasBeenFinished) {
+
+                // turn our childvect into a Template[]
+                t.childvect.copyInto(t.children = new Template[t.childvect.size()]);
+                t.childvect = null;
+                if (hasNonWhitespace) t.script = genscript(false);
+                
+                if (nodeStack.size() == 0) {
+                    // </template>
+                    templateNodeHasBeenFinished = true;
+
+                } else {
+                    // add this template as a child of its parent
+                    Template oldt = t;
+                    t = (Template)nodeStack.lastElement();
+                    nodeStack.setSize(nodeStack.size() - 1);
+                    t.childvect.addElement(oldt);
+                }
+
+            }
+        }
+
+        private Script genscript(boolean isstatic) {
+            Script thisscript = null;
+            Context cx = Context.enter();
+            cx.setOptimizationLevel(-1);
+
+            try {
+                thisscript = cx.compileReader(null, new StringReader(t.content.toString()), t.nodeName + (isstatic ? "._" : ""), t.content_start, null);
+            } catch (EcmaError ee) {
+                if (Log.on) Log.log(this, ee.getMessage() + " at " + ee.getSourceName() + ":" + ee.getLineNumber());
+                thisscript = null;
+            } catch (EvaluatorException ee) {
+                if (Log.on) Log.log(this, "  ERROR: " + ee.getMessage());
+                thisscript = null;
+            } catch (IOException ioe) {
+                if (Log.on) Log.log(this, "IOException while compiling script; this should never happen");
+                if (Log.on) Log.log(this, ioe);
+                thisscript = null;
+            }
+
+            t.content = null;
+            t.content_start = 0;
+            t.content_lines = 0;
+            return thisscript;
+        }
+
+        public void content(char[] ch, int start, int length, int line, int col) throws XML.SAXException {
+            if ("static".equals(nameOfHeaderNodeBeingProcessed) || templateNodeHasBeenEncountered) {
+                int contentlines = 0;
+                for(int i=start; i<start + length; i++) if (ch[i] == '\n') contentlines++;
+                line -= contentlines;
+
+                if (t.content == null) {
+                    t.content_start = line;
+                    t.content_lines = 0;
+                    t.content = new StringBuffer();
+                }
+
+                for(int i=t.content_start + t.content_lines; i<line; i++) {
+                    t.content.append('\n');
+                    t.content_lines++;
+                }
+
+                t.content.append(ch, start, length);
+                t.content_lines += contentlines;
+
+            } else if (nameOfHeaderNodeBeingProcessed != null) {
+                throw new XML.SAXException("header node <" + nameOfHeaderNodeBeingProcessed + "> cannot have text content");
+
+            }
+
+        }
+
+    }
+
+    /** a filtering reader that watches for tabs and long lines */
+    private static class TabAndMaxColumnEnforcingReader extends FilterReader {
+        private int MAX_COLUMN = 150;
+        private int column = 0;
+        private int line = 1;
+        private boolean lastCharWasCR = false;
+        private String filename;
+        public TabAndMaxColumnEnforcingReader(Reader r, String filename) { super(r); this.filename = filename; }
+        public int read() {
+            if (Log.on) Log.log(this, this.getClass().getName() + ".read() not supported, this should never happen");
+            return -1;
+        }
+        public long skip(long numskip) {
+            if (Log.on) Log.log(this, this.getClass().getName() + ".skip() not supported; this should never happen");
+            return numskip;
+        }
+        public int read(char[] buf, int off, int len) throws IOException {
+            int ret = super.read(buf, off, len);
+            for(int i=off; i<off + ret; i++)
+                if (buf[i] == '\t') {
+                    throw new TemplateException(filename + ":" + line + "," + column + ": tabs are not allowed in XWT files");
+                } else if (buf[i] == '\r') {
+                    column = 0;
+                    line++;
+                    lastCharWasCR = true;
+                } else if (buf[i] == '\n') {
+                    column = 0;
+                    if (!lastCharWasCR) line++;
+                } else if (++column > MAX_COLUMN) {
+                    throw new TemplateException(filename + ":" + line + ": lines longer than " + MAX_COLUMN + " characters not allowed");
+                } else {
+                    lastCharWasCR = false;
+                }
+            return ret;
+        }
+    }
+
+    private static class TemplateException extends IOException {
+        TemplateException(String s) { super(s); }
+    }
+
+}
+
+
diff --git a/src/org/xwt/ThreadMessage.java b/src/org/xwt/ThreadMessage.java
new file mode 100644 (file)
index 0000000..558c49d
--- /dev/null
@@ -0,0 +1,96 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import org.xwt.util.*;
+import java.util.*;
+import org.mozilla.javascript.*;
+
+/**
+ *  A background thread. All threads created with <tt>xwt.thread</tt>
+ *  are instances of this class. ThreadMessage objects can be enqueued
+ *  in MessageQueue.
+ *
+ *  INVARIANT: only one thread can be executing JavaScript scripts or
+ *             accessing Box instances at any given time. For
+ *             performance reasons, no locks are employed to enforce
+ *             this invariant.
+ */
+public class ThreadMessage extends Thread implements Message {
+    
+    private volatile static int threadcount = 0;
+
+    /** the JavaScript function that we are executing */
+    volatile Function f;
+
+    /** the ThreadMessage thread blocks on this before executing any JavaScript */
+    Semaphore go = new Semaphore();
+
+    /** The MessageQueue (main) thread blocks on this while the ThreadMessage thread is running JavaScript code */
+    Semaphore done = new Semaphore();
+
+    /** used to pool ThreadMessages that are not in use */
+    private static Queue spare = new Queue(50);
+
+    /** queue of functions waiting to be spawned; used if threadcount == Platform.maxThreads() */
+    private static Queue waiting = new Queue(50);
+
+    private ThreadMessage() { start(); }
+    private static Object[] emptyobj = new Object[] { };
+
+    /** creates a new thread to execute function <tt>f</tt> */
+    public static void newthread(Function f) {
+        ThreadMessage ret = (ThreadMessage)spare.remove(false);
+        if (ret == null) {
+            if (threadcount < Platform.maxThreads()) ret = new ThreadMessage();
+            else {
+                waiting.append(f);
+                return;
+            }
+        }
+        ret.f = f;
+        MessageQueue.add(ret);
+    }
+    
+    public void run() {
+        try {
+            threadcount++;
+            Context cx = Context.enter();
+            while (true) {
+                try {
+                    go.block();
+                    f.call(cx, f.getParentScope(), f.getParentScope(), emptyobj);
+                } catch (EcmaError e) {
+                    if (Log.on) Log.log(this, "WARNING: uncaught interpreter exception: " + e.getMessage());
+                    if (Log.on) Log.log(this, "         thrown from background thread at " + e.getSourceName() + ":" + e.getLineNumber());
+                } catch (JavaScriptException e) {
+                    if (Log.on) Log.log(this, "WARNING: uncaught ecmascript exception: " + e.getMessage());
+                    if (Log.on) Log.log(this, "         thrown from background thread at " + e.sourceFile + ":" + e.line);
+                }
+                done.release();
+                synchronized(waiting) {
+                    if (waiting.size() > 0) {
+                        f = (Function)waiting.remove(false);
+                        MessageQueue.add(this);
+                    } else if (spare.size() < 10) {
+                        spare.append(this);
+                    } else {
+                        threadcount--;
+                        return;
+                    }
+                }
+            }
+        } catch (Throwable t) {
+            if (Log.on) Log.log(this, "caught throwable at thread entry point; this should never happen");
+            if (Log.on) Log.log(this, t);
+            if (Log.on) Log.log(this, "reaping thread");
+            return;
+        }
+    }
+
+    /** this is invoked in the MessageQueue thread */
+    public void perform() {
+        go.release();
+        done.block();
+    }
+
+}
diff --git a/src/org/xwt/TinySSL.java b/src/org/xwt/TinySSL.java
new file mode 100644 (file)
index 0000000..3d38660
--- /dev/null
@@ -0,0 +1,1549 @@
+// Copyright (C) 2001 Adam Megacz <adam@xwt.org> all rights reserved.
+//
+// You may modify, copy, and redistribute this code under the terms of
+// the GNU Library Public License version 2.1, with the exception of
+// the portion of clause 6a after the semicolon (aka the "obnoxious
+// relink clause")
+
+package org.xwt;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.MD5Digest;
+import org.bouncycastle.crypto.digests.MD2Digest;
+import org.bouncycastle.crypto.engines.RSAEngine;
+import org.bouncycastle.crypto.engines.RC4Engine;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.asn1.DERInputStream;
+import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.DERConstructedSequence;
+import org.bouncycastle.asn1.DERObject;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.BERInputStream;
+import org.bouncycastle.asn1.x509.X509CertificateStructure;
+import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.TBSCertificateStructure;
+import org.bouncycastle.asn1.x509.X509Name;
+import org.xwt.util.Log;
+import java.net.*;
+import java.io.*;
+import java.util.*;
+import java.math.*;
+import java.text.*;
+
+/**
+
+   TinySSL: a tiny SSL implementation in Java, built on the
+            bouncycastle.org lightweight crypto library.
+
+   This class implements an SSLv3 client-side socket, with the
+   SSL_RSA_EXPORT_WITH_RC4_40_MD5 and SSL_RSA_WITH_RC4_128_MD5 cipher
+   suites, as well as certificate chain verification against a
+   collection of 93 built-in Trusted Root CA public keys (the same 93
+   included with Microsoft Internet Explorer 5.5 SP2).
+
+   As of 07-Dec-01, the zipped bytecode for this class is 43k, and the
+   subset of bouncycastle it requires is 82k.
+
+   This class should work correctly on any Java 1.1 compliant
+   platform. The java.security.* classes are not used.
+
+   The main design goal for this class was the smallest possible body
+   of code capable of connecting to 99% of all active HTTPS
+   servers. Although this class is useful in many other situations
+   (IMAPS, Secure SMTP, etc), the author will refuse all feature
+   requests and submitted patches which go beyond this scope.
+
+   Because of the limited goals of this class, certain abstractions
+   have been avoided, and certain parameters have been
+   hard-coded. "Magic numbers" are often used instead of "static final
+   int"'s, although they are usually accompanied by a descriptive
+   comment. Numeric offsets into byte arrays are also favored over
+   DataInputStream(ByteArrayInputStream(foo))'s.
+
+   Much thanks and credit go to the BouncyCastle team for producing
+   such a first-class library, and for helping me out on the
+   dev-crypto mailing list while I was writing this.
+
+   Revision History:
+   1.0  07-Dec-01  Initial Release
+   1.01 15-Mar-02  Added PKCS1 class to avoid dependancy on java.security.SecureRandom
+
+*/
+
+public class TinySSL extends Socket {
+
+    // Simple Test //////////////////////////////////////////////
+
+    public static void main(String[] args) {
+        Log.on = true;
+        try {
+            Socket s = new TinySSL("www.paypal.com", 443);
+            PrintWriter pw = new PrintWriter(s.getOutputStream());
+            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
+            pw.println("GET / HTTP/1.0");
+            pw.println("");
+            pw.flush();
+            
+            while(true) {
+                String s2 = br.readLine();
+                if (s2 == null) return;
+                System.out.println(s2);
+            }
+            
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    // Static Data //////////////////////////////////////////////
+
+    public static class SSLException extends IOException { public SSLException(String s) { super(s); } }
+    static SubjectPublicKeyInfo[] trusted_CA_public_keys;
+    public static byte[] pad1 = new byte[48];
+    public static byte[] pad2 = new byte[48];
+    public static byte[] pad1_sha = new byte[40];
+    public static byte[] pad2_sha = new byte[40];
+    static byte[] randpool;
+    static long randcnt = 0;
+
+    // Cipher State //////////////////////////////////////////////
+
+    public byte[] server_random = new byte[32];
+    public byte[] client_random = new byte[32];
+    public byte[] client_write_MAC_secret = new byte[16];        
+    public byte[] server_write_MAC_secret = new byte[16];        
+    public byte[] client_write_key = null;
+    public byte[] server_write_key = null;
+    public byte[] master_secret = null;
+
+    /** the bytes of the ServerKeyExchangeMessage, null if none recieved */
+    public byte[] serverKeyExchange = null;
+
+    /** true iff the server asked for a certificate */
+    public boolean cert_requested = false;
+
+    public X509CertificateStructure server_cert = null;
+
+    public SSLOutputStream os;
+    public SSLInputStream is;
+
+    String hostname;
+
+    /** the concatenation of all the bytes of all handshake messages sent or recieved */
+    public byte[] handshakes = new byte[] { };
+
+    /** true iff we're using SSL_RSA_EXPORT_WITH_RC4_40_MD5 */
+    boolean export = false;
+
+    public InputStream getInputStream() { return is; }
+    public OutputStream getOutputStream() { return os; }
+
+    public TinySSL(String host, int port) throws IOException {
+        super(host, port);
+        hostname = host;
+        os = new SSLOutputStream(super.getOutputStream());
+        is = new SSLInputStream(super.getInputStream());
+            
+        os.writeClientHello();
+        is.readServerHandshakes();
+        os.sendClientHandshakes();
+        is.readServerFinished();
+    }
+
+    class SSLInputStream extends InputStream {
+        
+        /** the underlying inputstream */
+        DataInputStream raw;
+
+        /** the server's sequence number */
+        public int seq_num = 0;
+
+        /** the decryption engine */
+        public RC4Engine rc4 = null;
+        
+        /** pending bytes -- decrypted, but not yet fed to consumer */
+        byte[] pend = null;
+        int pendstart = 0;
+        int pendlen = 0;
+
+        public void mark() { }
+        public void reset() { }
+        public boolean markSupported() { return false; }
+        public long skip(long l) throws IOException { for(long i=0; i<l; i++) read(); return l; }
+        public SSLInputStream(InputStream raw) { this.raw = new DataInputStream(raw); }
+        public int available() throws IOException { return pendlen; }
+
+        public int read() throws IOException {
+            byte[] singlebyte = new byte[1];
+            int numread = read(singlebyte);
+            if (numread != 1) return -1;
+            return (int)singlebyte[0];
+        }
+       
+        public int read(byte[] b, int off, int len) throws IOException {
+            if (pendlen == 0) {
+                pend = readRecord(false);
+                if (pend == null) return -1;
+                pendstart = 0;
+                pendlen = pend.length;
+            }
+            int ret = Math.min(len, pendlen);
+            System.arraycopy(pend, pendstart, b, off, ret);
+            pendlen -= ret;
+            pendstart += ret;
+            return ret;
+        }
+
+        /** reads and decrypts exactly one record; blocks if unavailable */        
+        public byte[] readRecord(boolean returnHandshakes) throws IOException {
+
+            // we only catch EOFException here, because anywhere else
+            // would be "unusual", and we *want* and EOFException in
+            // those cases
+            byte type;
+            try { type = raw.readByte(); } catch (EOFException e) { return null; }
+
+            byte ver_major = raw.readByte();
+            byte ver_minor = raw.readByte();
+            short len = raw.readShort();
+            if (Log.on) Log.log(this, "got record of type " + type + ", SSLv" + ver_major + "." + ver_minor + ", length=" + len);
+
+            byte[] ret = new byte[len];
+            raw.readFully(ret);
+            
+            // simply ignore ChangeCipherSpec messages -- we change as soon as we send ours
+            if (type == 20) { seq_num = 0; return readRecord(returnHandshakes); }
+
+            byte[] decrypted_payload;
+
+            // if crypto hasn't been enabled yet; skip crypt and hash
+            if (rc4 == null) decrypted_payload = ret;
+            else {
+                // decrypt the payload
+                decrypted_payload = new byte[len - 16];
+                rc4.processBytes(ret, 0, len - 16, decrypted_payload, 0);
+                
+                // check the MAC
+                byte[] MAC = new byte[16];
+                rc4.processBytes(ret, len - 16, 16, MAC, 0);
+                byte[] ourMAC = computeMAC(type, decrypted_payload, 0, decrypted_payload.length, server_write_MAC_secret, seq_num++);
+                for(int i=0; i<MAC.length; i++)
+                    if (MAC[i] != ourMAC[i])
+                        throw new SSLException("MAC mismatch on byte " + i + ": got " + MAC[i] + ", expecting " + ourMAC[i]);
+            }
+
+            if (type == 21) {
+                if (decrypted_payload[1] != 0)
+                    throw new SSLException("got SSL ALERT message, level=" + decrypted_payload[0] + " code=" + decrypted_payload[1]);
+                return null;
+
+            } else if (type == 22) {
+                if (!returnHandshakes) {
+                    if (Log.on) Log.log(this, "after completion of handshake, server sent another handshake message!");
+                    return readRecord(false);
+                }
+
+            } else if (type != 23) {
+                if (Log.on) Log.log(this, "unexpected record type: " + type + "; skipping");
+                return readRecord(returnHandshakes);
+
+            }
+                
+            if (Log.on) Log.log(this, "  returning " + decrypted_payload.length + " byte record payload");
+            return decrypted_payload;
+        }
+        
+        /** This reads the ServerHello, Certificate, and ServerHelloDone handshake messages */
+        public void readServerHandshakes() throws IOException {
+            for(;;) {
+                byte[] rec = readRecord(true);
+                handshakes = concat(new byte[][] { handshakes, rec });
+                DataInputStream stream = new DataInputStream(new ByteArrayInputStream(rec, 4, rec.length - 4));
+
+                switch(rec[0]) {
+                case 2: // ServerHello
+                    byte ver_major = rec[4];
+                    byte ver_minor = rec[5];
+                    System.arraycopy(rec, 6, server_random, 0, server_random.length);
+                    short cipher_high = rec[6 + server_random.length + rec[6 + server_random.length] + 1];
+                    short cipher_low = rec[6 + server_random.length + rec[6 + server_random.length] + 2];
+
+                    if (cipher_low == 0x04 || cipher_high != 0x00) {
+                        export = false;
+                        if (Log.on) Log.log(this, "using SSL_RSA_WITH_RC4_128_MD5");
+
+                    } else if (cipher_low == 0x03 || cipher_high != 0x00) {
+                        export = true;
+                        if (Log.on) Log.log(this, "using SSL_RSA_EXPORT_WITH_RC4_40_MD5");
+
+                    } else throw new SSLException("server asked for cipher " + ((cipher_high << 8) | cipher_low) +
+                                                " but we only do SSL_RSA_WITH_RC4_128_MD5 (0x0004) and " +
+                                                "SSL_RSA_EXPORT_WITH_RC4_40_MD5 (0x0003)");
+
+                    byte compressionMethod = rec[6 + server_random.length + rec[6 + server_random.length] + 3];
+                    if (compressionMethod != 0x0) throw new SSLException("server asked for compression method " + compressionMethod +
+                                                                         " but we don't support compression");
+                    break;
+                    
+                case 11: // Server's certificate(s)
+                    int numcertbytes = ((rec[4] & 0xff) << 16) | ((rec[5] & 0xff) << 8) | (rec[6] & 0xff);
+                    int numcerts = 0;
+                    X509CertificateStructure last_cert = null;
+                    X509CertificateStructure this_cert = null;
+
+                    for(int i=0; i<numcertbytes;) {
+                        int certlen = ((rec[7 + i] & 0xff) << 16) | ((rec[7 + i + 1] & 0xff) << 8) | (rec[7 + i + 2] & 0xff);
+                        try {
+                            DERInputStream dIn = new DERInputStream(new ByteArrayInputStream(rec, 7 + i + 3, certlen));
+                            this_cert = new X509CertificateStructure((DERConstructedSequence)dIn.readObject());
+                        } catch (Exception e) {
+                            SSLException t = new SSLException("error decoding server certificate: " + e);
+                            t.fillInStackTrace();
+                            throw t;
+                        }
+
+                        if (server_cert == null) {
+                            server_cert = this_cert;
+                            TBSCertificateStructure tbs = server_cert.getTBSCertificate();
+                            X509Name subject = tbs.getSubject();
+
+                            // gross hack to extract the Common Name so we can compare it to the server hostname
+                            String CN = tbs.getSubject().toString() + " ";
+                            boolean good = false;
+                            for(int j=0; j<CN.length() - 3; j++)
+                                if (CN.substring(j, j+3).equals("CN=")) {
+                                    good = true;
+                                    CN = CN.substring(j+3, CN.indexOf(' ', j+3));
+                                    break;
+                                }
+
+                            if (!good) throw new SSLException("server certificate does not seem to have a CN: " + CN);
+                            if (!CN.equals(hostname))
+                                throw new SSLException("connecting to host " + hostname + " but server certificate was issued for " + CN);
+
+                            SimpleDateFormat dateF = new SimpleDateFormat("MM-dd-yy-HH-mm-ss-z");
+
+                            // the following idiocy is a result of the brokenness of the GNU Classpath's SimpleDateFormat
+                            String s = tbs.getStartDate().getTime();
+                            s = s.substring(2, 4) + "-" + s.substring(4, 6) + "-" + s.substring(0, 2) + "-" + s.substring(6, 8) + "-" +
+                                s.substring(8, 10) + "-" + s.substring(10, 12) + "-" + s.substring(12);
+                            Date startDate = dateF.parse(s, new ParsePosition(0));
+
+                            s = tbs.getEndDate().getTime();
+                            s = s.substring(2, 4) + "-" + s.substring(4, 6) + "-" + s.substring(0, 2) + "-" + s.substring(6, 8) + "-" +
+                                s.substring(8, 10) + "-" + s.substring(10, 12) + "-" + s.substring(12);
+                            Date endDate = dateF.parse(s, new ParsePosition(0));
+
+                            Date now = new Date();
+                            if (now.after(endDate)) throw new SSLException("server certificate expired on " + endDate);
+                            if (now.before(startDate)) throw new SSLException("server certificate will not be valid until " + startDate);
+
+                            Log.log(this, "server cert (name, validity dates) checks out okay");
+                            
+                        } else if (!isSignedBy(last_cert, this_cert.getSubjectPublicKeyInfo()))
+                            throw new SSLException("certificate chain discontinuity");
+
+                        last_cert = this_cert;
+                        i += certlen + 3;
+                        numcerts++;
+                    }
+                    if (Log.on) Log.log(this, "  Certificate (" + numcerts + " certificates)");
+
+                    boolean good = false;
+                    for(int i=0; i<trusted_CA_public_keys.length; i++) {
+                        if (isSignedBy(this_cert, trusted_CA_public_keys[i])) {
+                            if (Log.on) Log.log(this, "server cert was signed by trusted CA " + i);
+                            good = true;
+                            break;
+                        }
+                    }
+                    if (!good) throw new SSLException("server cert was not signed by a trusted CA");
+                    break;
+
+                case 12: 
+                    serverKeyExchange = rec;
+                    break;
+
+                case 13: cert_requested = true; break;
+                case 14: if (Log.on) Log.log(this, "  ServerHelloDone"); return;
+                default: throw new SSLException("unknown handshake of type " + rec[0]);
+                }
+            }
+        }
+     
+        public void readServerFinished() throws IOException {
+            byte[] rec = readRecord(true);
+            if (rec[0] != 20) throw new SSLException("expecting server Finished message, but got message of type " + rec[0]);
+
+            byte[] expectedFinished = concat(new byte[][] {
+                md5(new byte[][] { master_secret, pad2,
+                                   md5(new byte[][] { handshakes, new byte[] { (byte)0x53, (byte)0x52, (byte)0x56, (byte)0x52 },
+                                                      master_secret, pad1 }) }),
+                sha(new byte[][] { master_secret, pad2_sha,
+                                   sha(new byte[][] { handshakes, new byte[] { (byte)0x53, (byte)0x52, (byte)0x56, (byte)0x52 },
+                                                      master_secret, pad1_sha } ) } ) } );
+
+            for(int i=0; i<expectedFinished.length; i++)
+                if (expectedFinished[i] != rec[i + 4])
+                    throw new SSLException("server Finished message mismatch!");
+
+            if (Log.on) Log.log(this, "server finished message checked out okay!");
+        }
+   
+    }
+    
+    class SSLOutputStream extends OutputStream {
+        
+        /** the underlying outputstream */
+        DataOutputStream raw;
+        
+        /** the sequence number for sending */
+        public long seq_num = 0;
+
+        /** the encryption engine for sending */
+        RC4Engine rc4 = null;
+        
+        public SSLOutputStream(OutputStream raw) { this.raw = new DataOutputStream(raw); }
+        public void flush() throws IOException { raw.flush(); }
+        public void write(int b) throws IOException { write(new byte[] { (byte)b }, 0, 1); }
+        public void write(byte[] b, int off, int len) throws IOException { write(b, off, len, (byte)23); }
+        public void close() throws IOException {
+            write(new byte[] { 0x1, 0x0 }, 0, 2, (byte)21);
+            raw.close();
+        }
+        
+        /** writes a single SSL Record */
+        public void write(byte[] payload, int off, int len, byte type) throws IOException {
+
+            // largest permissible frame is 2^14 octets
+            if (len > 1 << 14) {
+                write(payload, off, 1 << 14, type);
+                write(payload, off + 1 << 14, len - 1 << 14, type);
+                return;
+            }
+
+            raw.writeByte(type);
+            raw.writeShort(0x0300);
+
+            if (rc4 == null) {
+                raw.writeShort(len);
+                raw.write(payload, off, len);
+
+            } else {
+                byte[] MAC = computeMAC(type, payload, off, len, client_write_MAC_secret, seq_num);
+                byte[] encryptedPayload = new byte[MAC.length + len];
+                rc4.processBytes(payload, off, len, encryptedPayload, 0);
+                rc4.processBytes(MAC, 0, MAC.length, encryptedPayload, len);
+                raw.writeShort(encryptedPayload.length);
+                raw.write(encryptedPayload);
+
+            }
+
+            seq_num++;
+        }
+
+        /** tacks a handshake header onto payload before sending it */        
+        public void writeHandshake(int type, byte[] payload) throws IOException {
+            byte[] real_payload = new byte[payload.length + 4];
+            System.arraycopy(payload, 0, real_payload, 4, payload.length);
+            real_payload[0] = (byte)(type & 0xFF);
+            intToBytes(payload.length, real_payload, 1, 3);
+            handshakes = concat(new byte[][] { handshakes, real_payload });
+            write(real_payload, 0, real_payload.length, (byte)22);
+        }
+
+        public void sendClientHandshakes() throws IOException {
+            
+            if (Log.on) Log.log(this, "shaking hands");
+            if (cert_requested) {
+                if (Log.on) Log.log(this, "telling the server we have no certificates");
+                writeHandshake(11, new byte[] { 0x0, 0x0, 0x0 });
+            }
+            
+            // generate the premaster secret
+            byte[] pre_master_secret = new byte[48];
+            pre_master_secret[0] = 0x03;                            // first two bytes of premaster secret are our version number
+            pre_master_secret[1] = 0x00;
+            getRandomBytes(pre_master_secret, 2, pre_master_secret.length - 2);
+
+            // encrypt and send the pre_master_secret            
+            try {
+                byte[] encrypted_pre_master_secret;
+
+                SubjectPublicKeyInfo pki = server_cert.getSubjectPublicKeyInfo();
+                RSAPublicKeyStructure rsa_pks = new RSAPublicKeyStructure((DERConstructedSequence)pki.getPublicKey());
+                BigInteger modulus = rsa_pks.getModulus();
+                BigInteger exponent = rsa_pks.getPublicExponent();
+
+                if (serverKeyExchange != null) {
+
+                    AsymmetricBlockCipher rsa = new PKCS1(new RSAEngine());
+                    rsa.init(false, new RSAKeyParameters(false, modulus, exponent));
+
+                    int modulus_size = ((serverKeyExchange[4] & 0xff) << 8) | (serverKeyExchange[5] & 0xff);
+                    byte[] b_modulus = new byte[modulus_size];
+                    System.arraycopy(serverKeyExchange, 6, b_modulus, 0, modulus_size);
+                    modulus = new BigInteger(1, b_modulus);
+
+                    int exponent_size = ((serverKeyExchange[6 + modulus_size] & 0xff) << 8) | (serverKeyExchange[7 + modulus_size] & 0xff);
+                    byte[] b_exponent = new byte[exponent_size];
+                    System.arraycopy(serverKeyExchange, 8 + modulus_size, b_exponent, 0, exponent_size);
+                    exponent = new BigInteger(1, b_exponent);
+
+                    byte[] server_params = new byte[modulus_size + exponent_size + 4];
+                    System.arraycopy(serverKeyExchange, 4, server_params, 0, server_params.length);
+
+                    byte[] expectedSignature = concat(new byte[][] { md5(new byte[][] { client_random, server_random, server_params } ),
+                                                                     sha(new byte[][] { client_random, server_random, server_params } ) } );
+
+                    byte[] recievedSignature = rsa.processBlock(serverKeyExchange, 6 + server_params.length,
+                                                                serverKeyExchange.length - 6 - server_params.length);
+
+                    for(int i=0; i<expectedSignature.length; i++)
+                        if (expectedSignature[i] != recievedSignature[i])
+                            throw new SSLException("ServerKeyExchange message had invalid signature " + i);
+
+                    if (Log.on) Log.log(this, "ServerKeyExchange successfully processed");
+                }
+
+                AsymmetricBlockCipher rsa = new PKCS1(new RSAEngine());
+                rsa.init(true, new RSAKeyParameters(false, modulus, exponent));
+
+                encrypted_pre_master_secret = rsa.processBlock(pre_master_secret, 0, pre_master_secret.length);
+                writeHandshake(16, encrypted_pre_master_secret);
+
+            } catch (Exception e) {
+                SSLException t = new SSLException("exception encrypting premaster secret");
+                t.fillInStackTrace();
+                throw t;
+            }
+            
+            // ChangeCipherSpec
+            if (Log.on) Log.log(this, "Handshake complete; sending ChangeCipherSpec");
+            write(new byte[] { 0x01 }, 0, 1, (byte)20);
+            seq_num = 0;
+
+            // compute master_secret
+            master_secret = concat(new byte[][] {
+                md5(new byte[][] { pre_master_secret,
+                                   sha(new byte[][] { new byte[] { 0x41 }, pre_master_secret, client_random, server_random })}),
+                md5(new byte[][] { pre_master_secret,
+                                   sha(new byte[][] { new byte[] { 0x42, 0x42 }, pre_master_secret, client_random, server_random })}),
+                md5(new byte[][] { pre_master_secret,
+                                   sha(new byte[][] { new byte[] { 0x43, 0x43, 0x43 }, pre_master_secret, client_random, server_random })})
+                } );
+            
+            // construct the key material
+            byte[] key_material = new byte[] { };
+            for(int i=0; key_material.length < 72; i++) {
+                byte[] crap = new byte[i + 1];
+                for(int j=0; j<crap.length; j++) crap[j] = (byte)(((byte)0x41) + ((byte)i));
+                key_material = concat(new byte[][] { key_material,
+                                                   md5(new byte[][] { master_secret,
+                                                                      sha(new byte[][] { crap, master_secret, server_random, client_random }) }) });
+            }
+
+            client_write_key = new byte[export ? 5 : 16];
+            server_write_key = new byte[export ? 5 : 16];
+
+            System.arraycopy(key_material, 0,  client_write_MAC_secret, 0, 16);
+            System.arraycopy(key_material, 16, server_write_MAC_secret, 0, 16);
+            System.arraycopy(key_material, 32, client_write_key, 0, export ? 5 : 16);
+            System.arraycopy(key_material, export ? 37 : 48, server_write_key, 0, export ? 5 : 16);
+            
+            if (export) {
+                // see SSLv3 spec, 6.2.2 for explanation
+                byte[] client_untrimmed = md5(new byte[][] { concat(new byte[][] { client_write_key, client_random, server_random } ) });
+                byte[] server_untrimmed = md5(new byte[][] { concat(new byte[][] { server_write_key, server_random, client_random } ) });
+                client_write_key = new byte[16];
+                server_write_key = new byte[16];
+                System.arraycopy(client_untrimmed, 0, client_write_key, 0, 16);
+                System.arraycopy(server_untrimmed, 0, server_write_key, 0, 16);
+            }
+
+            rc4 = new RC4Engine();
+            rc4.init(true, new KeyParameter(client_write_key));
+            is.rc4 = new RC4Engine();
+            is.rc4.init(false, new KeyParameter(server_write_key));
+            
+            // send Finished
+            writeHandshake(20, concat(new byte[][] { 
+                md5(new byte[][] { master_secret, pad2, 
+                                   md5(new byte[][] { handshakes, new byte[] { (byte)0x43, (byte)0x4C, (byte)0x4E, (byte)0x54 },
+                                                      master_secret, pad1 }) }),
+                sha(new byte[][] { master_secret, pad2_sha,
+                                   sha(new byte[][] { handshakes, new byte[] { (byte)0x43, (byte)0x4C, (byte)0x4E, (byte)0x54 },
+                                                      master_secret, pad1_sha } ) })
+            }));
+            raw.flush();
+            if (Log.on) Log.log(this, "wrote Finished message");
+
+        }
+        
+        public void writeClientHello() throws IOException {
+            
+            if (Log.on) Log.log(this, "sending ClientHello");
+            int unixtime = (int)(System.currentTimeMillis() / (long)1000);
+            
+            byte[] out = new byte[] {
+                0x03, 0x00,                     // client version (SSLv3.0)
+                
+                // space for random bytes
+                0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+                0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+                0x0, 0x0, 0x0, 0x0,
+                
+                0x0,                            // empty vector for sessionid
+                0x0, 0x4, 0x0, 0x4, 0x0, 0x3,   // we support two ciphersuites: SSL_RSA_WITH_RC4_128_MD5 and SSL_RSA_EXPORT_WITH_RC4_40_MD5
+                0x1, 0x0                        // we only support one compression method: none
+            };
+            
+            // don't need to use secure random here since it's sent in the clear
+            Random rand = new Random(System.currentTimeMillis());
+            rand.nextBytes(client_random);
+            intToBytes(unixtime, client_random, 0, 4);
+            System.arraycopy(client_random, 0, out, 2, client_random.length);
+            
+            writeHandshake(1, out);
+            flush();
+        }
+    }
+
+    // Static Helpers ////////////////////////////////////////////////////////////////////
+
+    /** copy the least significant num bytes of val into byte array b, startint at offset */
+    public static void intToBytes(long val, byte[] b, int offset, int num) {
+        for(int i=0; i<num; i++)
+            b[offset + num - i - 1] = (byte)((val & (0xFFL << (i * 8))) >> (i * 8));
+    }
+
+    /** fills b with random bytes */
+    public static synchronized void getRandomBytes(byte[] b, int offset, int len) {
+        MD5Digest md5 = new MD5Digest();
+        byte[] b2 = new byte[16];
+        while(len > 0) {
+            md5.reset();
+            md5.update(randpool, 0, randpool.length);
+            intToBytes(randcnt++, b2, 0, 8);
+            md5.update(b2, 0, 8);
+            md5.doFinal(b2, 0);
+            int n = len < 16 ? len : 16;
+            System.arraycopy(b2, 0, b, offset, n);
+            len -= n;
+            offset += n;
+        }
+    }
+
+    public static byte[] computeMAC(byte type, byte[] payload, int off, int len, byte[] MAC_secret, long seq_num) {
+        byte[] MAC = new byte[16];
+        MD5Digest md5 = new MD5Digest();
+        md5.update(MAC_secret, 0, MAC_secret.length);
+        md5.update(pad1, 0, pad1.length);
+
+        byte[] b = new byte[11];
+        intToBytes(seq_num, b, 0, 8);
+        b[8] = type;
+        intToBytes(len, b, 9, 2);
+        md5.update(b, 0, b.length);
+
+        md5.update(payload, off, len);
+        md5.doFinal(MAC, 0);
+        md5.reset();
+        md5.update(MAC_secret, 0, MAC_secret.length);
+        md5.update(pad2, 0, pad2.length);
+        md5.update(MAC, 0, MAC.length);
+        md5.doFinal(MAC, 0);
+
+        return MAC;
+    }
+
+    public static byte[] concat(byte[][] inputs) {
+        int total = 0;
+        for(int i=0; i<inputs.length; i++) total += inputs[i].length;
+        byte[] ret = new byte[total];
+        int pos = 0;
+        for(int i=0; i<inputs.length; i++) {
+            System.arraycopy(inputs[i], 0, ret, pos, inputs[i].length);
+            pos += inputs[i].length;
+        }
+        return ret;
+    }
+    
+    SHA1Digest master_sha1 = new SHA1Digest();
+    public byte[] sha(byte[][] inputs) {
+        master_sha1.reset();
+        for(int i=0; i<inputs.length; i++) master_sha1.update(inputs[i], 0, inputs[i].length);
+        byte[] ret = new byte[master_sha1.getDigestSize()];
+        master_sha1.doFinal(ret, 0);
+        return ret;
+    }
+    
+    MD5Digest master_md5 = new MD5Digest();
+    public byte[] md5(byte[][] inputs) {
+        master_md5.reset();
+        for(int i=0; i<inputs.length; i++) master_md5.update(inputs[i], 0, inputs[i].length);
+        byte[] ret = new byte[master_md5.getDigestSize()];
+        master_md5.doFinal(ret, 0);
+        return ret;
+    }
+
+    // FEATURE: improve error reporting in here
+    /** returns true iff certificate "signee" is signed by public key "signer" */
+    public static boolean isSignedBy(X509CertificateStructure signee, SubjectPublicKeyInfo signer) throws SSLException {
+
+        Digest hash = null;
+
+        String signature_algorithm_oid = signee.getSignatureAlgorithm().getObjectId().getId();
+        if (signature_algorithm_oid.equals("1.2.840.113549.1.1.4")) hash = new MD5Digest();
+        else if (signature_algorithm_oid.equals("1.2.840.113549.1.1.2")) hash = new MD2Digest();
+        else if (signature_algorithm_oid.equals("1.2.840.113549.1.1.5")) hash = new SHA1Digest();
+        else throw new SSLException("unsupported signing algorithm: " + signature_algorithm_oid);
+
+        try {
+            // decrypt the signature using the signer's public key
+            byte[] ED = signee.getSignature().getBytes();
+            SubjectPublicKeyInfo pki = signer;
+            RSAPublicKeyStructure rsa_pks = new RSAPublicKeyStructure((DERConstructedSequence)pki.getPublicKey());
+            BigInteger modulus = rsa_pks.getModulus();
+            BigInteger exponent = rsa_pks.getPublicExponent();
+            AsymmetricBlockCipher rsa = new PKCS1(new RSAEngine());
+            rsa.init(false, new RSAKeyParameters(false, modulus, exponent));
+            
+            // Decode the embedded octet string
+            byte[] D = rsa.processBlock(ED, 0, ED.length);
+            BERInputStream beris = new BERInputStream(new ByteArrayInputStream(D));
+            DERObject derob = beris.readObject();
+            DERConstructedSequence dercs = (DERConstructedSequence)derob;
+            DEROctetString deros = (DEROctetString)dercs.getObjectAt(1);
+            byte[] MD = deros.getOctets();
+            
+            // generate our own hash
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            DEROutputStream dos = new DEROutputStream(baos);
+            dos.writeObject(signee.getTBSCertificate());
+            dos.flush();
+            byte[] b = baos.toByteArray();
+            hash.update(b, 0, b.length);
+            byte[] md_out = new byte[MD.length];
+            hash.doFinal(md_out, 0);
+            
+            // compare our hash to the signed hash
+            for(int j=0; j<MD.length; j++) if (md_out[j] != MD[j]) return false;
+            return true;
+
+        } catch (Exception e) {
+            return false;
+
+        }
+    }
+
+    // Embedded Trusted Public Keys //////////////////////////////////////////////
+
+    /** base64-encoded sequence of DER-encoded PKCS7 certs for all the "trusted root CA's" included with IE5.5 */
+    static String[] base64_encoded_trusted_CA_public_keys = new String[] {
+
+        // CN=ABA.ECOM Root CA
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdMR4HlVQwcITMsFQgDiDYNGPe" +
+        "STurYG0w1ZvT7BzkNnAYohqO+8zNCizLBVllOEZgUA2kRJgNhUCqUlhpTtY1b/cGyjoRnS" +
+        "eL5oKkReL8/MGF5HvDqxRj0e8LksNF+MfEwIKZ1AVes8fYPetfD3ioMOoUy0OqWzX1oil+" +
+        "wZm8EFaP3mt6mRlCzkeEgkGiUZOuuVnDkKis9CsvAc1V/7a+1oVns5LHI4sO6TqdN7dzzr" +
+        "cQOpOEoWbIkqytozE3nCVYztnLvyy1sQ+C5hNcYpTCrQKmPRZVm0+M359ACEtldChZ0yqP" +
+        "kqVPv/eEG8vXEo9LuQvP+WNATjRZ6hRihAgQIDAQAB",
+
+        // O=ViaCode
+        "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCws2enlrV2g+kWA9kaoHbVdOecBdJRP9" +
+        "tPGaGoOU7LJH1hxB5qIK4Pgd7quDn9Gx9rNkDtTSEl8qPZoVHYbMAblvjUQpTUp84bj9NU" +
+        "JqKE7zKFr0o/8TI2rz3mOifrA8IlfvRhK62KGkvmmzZo1C/l0oiU3Baq2sIVTGzD4RmRyQ" +
+        "IBAw==",
+
+        // CN=Xcert EZ by DST
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArVQY3rS/963odKrti3yPwtR1Gt" +
+        "WEubZi/Inv5JdhkvsduOFaRzSengYi+9PqOMu4iwf3GqAXdwdaMBzUKTgg1ydA2FCTQ7/S" +
+        "GKIpdgVyqmu2aZireR4cZfVqi/zFFqqictpg7U5uGSV6Ch0w41CbQjxE66GwIB7bAn7+PR" +
+        "+/0ACK20B2philFadXtlLCAReYd4+KgcYatGoq5q+p1gCsz9gVSXzbG6H+gfqH+dOQwQLA" +
+        "+dBC6ZFoJV/Gv4c56ZUAYCi/gyzA51621zYW52CHdujnJ7IlDYt65aod5VnNzgsOb8bInO" +
+        "MQ2YU507eb+sa6fHTSXXVWq3SkolG/UnzucQIDAQAB",
+
+        // CN=Certiposte Classe A Personne
+        "MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQAox3xaJSN48rMAR0Biy2+MQlCfnl" +
+        "7UXA5lC1hWlSvjRtBhNuAtRpuCy5Hu0pV8mpKvBAp+pp/g17HDRfmYQRs5redW19m2f867" +
+        "OS4sO8+2cwODzhNdMmpjottb+Esz6FBsy6gX7J6TuWwGSyYLdx6e+eWMiTfS0bv9qYwrLJ" +
+        "wQMdhLjM23cX44LCnjF7JP6FK245I80v3hAtphEHTSGvPI0dFmB1/EhGNpva5s3GUjHLf7" +
+        "98YTLoN+P6nlCyBtAQo34lzait4icOkN4HQ9xOtxm2Eq4g0Ui0xGN0wm0mjWVsNXqqJgN6" +
+        "9fnaCzgILmQypMgAAJUNmoanNtA/5ec5LlAgMBAAE=",
+
+        // CN=Certiposte Serveur
+        "MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQA+p3gzOJHiylaV0ZFGsiPcpVZ/D8" +
+        "eXuOKekS4oFi6O80e2XIPE8Ob+ZxqTZH1ACdgdaADs1BHu2GOJAyPphF/HVQ5K4nK7KcFV" +
+        "ZHao45LN9/ZuQlYYUjOJ+YAUqBlRfsd3v3qoMcB9F25DTtVmyQU+S+Ll4lUbdKpRHarMmB" +
+        "F3pOvbKg4nx9XNSOzcfk5J50HNmQvRS14YGw06CpstmznHQAzQdgd8fI9+XHKOh9W+8qa5" +
+        "3r/dnxJ5R3zFyZdARgCS0xNak0+dfthfTMFdSEnZLZg8/MynhyHwPo5yfVk4NhYaDEi+of" +
+        "LVPqgWDCBZz84PM4M9rav1/93X/WkIiADvAgMBAAE=",
+
+        // OU=Certisign - Autoridade Certificadora - AC2
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC5MMyl65DWVpRnM4mDbUa+cJeTF04KJ3" +
+        "DOycXyxdIt0RGcdzJsdNOSb/rp1bhhmqpMEz41OvDuCTbZ0Zcxx16sQUm/SG1OIFPJe2qj" +
+        "ljFrsm6ozy9yTAatMs9aCPN9EJyqu7pz+fPwuCRvqGW2Iv4FWxBVRMIDHa3RIswIbfuMyw" +
+        "IDAQAB",
+
+        // OU=Certisign - Autoridade Certificadora - AC4
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDsg9TMg5A/X+y+wenQx1hGWR/xk0qyFx" +
+        "MLzymZqwRFM+PRXr68jiV3Yt2bkpsxCkBFedXys91suUD9mH9Aoi3pspO9S9XB3unR+nH3" +
+        "P0G89BSvzWvIOUqdYGW0hNBqQeljrptp6rlGHNsYCDtiTN5B156GfxNyEdTc6t5gpbvdGw" +
+        "IDAQAB",
+
+        // OU=Certisign Autoridade Certificadora AC1S
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwwJXro8VB+JtvcWOOkRFX+QPHaJoanG" +
+        "Hwww8Ml2KIfiYBNX398W9PF5WqfvK7vO/idnNhlTZRgz6E6D+6VzY3lBNskmQflA3rVC9R" +
+        "WuUoXvCShufkbSF6XzcL51u9LQKogfk/yxTIvKTF49HLN9yr5Yeq8guYLnrPzB7Cf+j9AQ" +
+        "IDAQAB",
+
+        // OU=Certisign Autoridade Certificadora AC3S
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOZE7Wz658mCeY7yjvujTDNRqd0mYecf" +
+        "Hkli0nFzmQRY8t7+bVR6nhg4F8Pihx+oC7XfhDaxkQwZhvFZ4trklkROyEGmlZFleyPZLY" +
+        "Zku/ma1DGMc4yYuOLAQus0trk/adH4SyzeYAwr42pbxZtZ+LGSD/5agopFW2irayxddE4w" +
+        "IDAQAB",
+
+        // O=Certplus, CN=Class 1 Primary CA
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw2spyC7HrnxSBemTiVYKWnnJzN" +
+        "wl74eKLQXYgRcEGzpF+HkODUnUgUHIq0X7dcgV8uLQvNlhbISkExmn2fnySdxMD8Z9V7QT" +
+        "3B4JcSk2nYBY9BvYiRTr09KTSyrxd+dqZb0Z5ar9DEpj4cKZtA8EtlobNjw3PL/F5V7xX1" +
+        "cOH8f9LOfkb2qbYpY5EZtm8Cy2UtzhJ//bbf7rq2MUHWOIY+IWDPkgVA+b3RVqdoNPvSeL" +
+        "U6Y30ofyR1BSO2bp0XgaG7I7afBZPDhb0SpMM14Oylal7S1bgoNN1jhOila2ai8kaxIwpi" +
+        "rerwy7qkQSHBPFZQ/j/dgaMUvkPwx8RegWMwIDAQAB",
+
+        // O=Certplus, CN=Class 2 Primary CA
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3FCW0BL4NdIIeHq2UnD9b+7PuR" +
+        "HLXXfh7Ol+BI3WzG9zQ1dgrDMKROwDXxyAJJHlqJFWEoL34Cv0265hLokQjWtsurMCvdU2" +
+        "xUg3I+LwWjdSMxcS4tFgTb4vQRHj9hclDIuRwBuZe5lWDa/u0rxHV+N5SXs0iSckhN6x7O" +
+        "lYTv5O31q+Qa2sCMUYDu/SU+5s0J0SARON3IBi95WpRIhKcU5gVZ7bIxl5VgcMP2MLXLDi" +
+        "vn4V/JQzWEE4dMThj4vfJqwftYs7t0NZa7Akpm2Qi8Ry6l0zmLfL3l5775TxGz7KySHBxZ" +
+        "gCqqL2W3eb9X6WVTQcZ2nA8ULjR6z8KBxmVQIDAQAB",
+
+        // O=Certplus, CN=Class 3 Primary CA
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt5QwbtBM2X5eYOVvuybKUm9rbI" +
+        "WZpQvvABpvG01YtRgH+t17+MssUt38nLiNUum5sgt89Y9bmUgJWN7NSJWGJYkFNCcwC1e2" +
+        "DHdjKctsqj65mVESDZhwdkdM+UmWIgj5a+qqADajFaccHp+Mylp5eyHaiR9jfnmSUAkEKK" +
+        "3O420ESUqZsT9FCXhYIO+N/oDIBO0pLKBYjYQCJZc/oBPXe4sj45+4x7hCQDgbkkq9SpRV" +
+        "x1YVDYF3zJ+iN4krW4UNi3f4xIv7EMuUx+kaVhKXZhTEu9d9bQIbv3FiJhjpSYr6o97hhK" +
+        "2AykriIoxqCGGDsiLHCYg4Vl3RMavwCZ8TWQIDAQAB",
+
+        // CN=Autoridad Certificadora de la Asociacion Nacional del Notariado Mexicano
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7tlrVYRxJvaOrUG71tLeY+ryP2" +
+        "XyOxPBrlEm9L94j8ZMSay/Qd71KMco55/XgOXU7iMrk5U9yY9q9coA6RDHiIIabqNf8DRS" +
+        "ISVoKPiV8ICVoiyxP2r2KNbihP0WZ5wluXXb5cZZA7SrQgeI1VxIRaIJA8muZ5KoolPHyq" +
+        "t+mhKVWgVXjRBklicRsOYyMFvNPQygGxMtuxqr3TnOkmuiBNQTX213Z1Q5qHtpisZfeMoH" +
+        "GGlu+cDT0IqOrx4waO742KhmDIR9I2qJPGJNFHSs25uc/LCD/gcw8factEjI5jpCJQko91" +
+        "bCsdejmHcCh+qKwV3axIonB4VeSExVKEDtCQIDAQAB",
+
+        // O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhA" +
+        "wL0TPZ2RHP7gJYHyX3KqhEBarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lw" +
+        "dd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpw" +
+        "IDAQAB",
+
+        // C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhA" +
+        "wL0TPZ2RHP7gJYHyX3KqhEBarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lw" +
+        "dd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpw" +
+        "IDAQAB",
+
+        // C=FR, O=Certplus, CN=Class 3P Primary CA
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqzf/62CbQXhp9UlYsN4fcWmmK+" +
+        "OuUMapvJPpIL7kxBOCVu/wQzIJypt1A498T+HgT3aeC61kehQ6mp2/LxYLZRyp7py84xpl" +
+        "y0+F6pJWdWbWVUDv+8zWOD+rHO9CjRmJ9reVhsKnHen3KfEq2WV5/Cv1jsoad36e6Kz5Zr" +
+        "9F++gTnV+2c+V9e477EnRdHwZehRumXhhEALq8027RUg4GrevutbTBu7zrOA9IIpHHb9K4" +
+        "cju6f8CNbLe8R3MhKoX/rNYoohnVl2o6uaxtRezmTcPbqF3FXYKYrEpaquYrCAwQdLxi9j" +
+        "pJBGbYURwmpth1n5y/rmBRPVy8ok97iWfNUwIDAQAB",
+
+        // C=FR, O=Certplus, CN=Class 3TS Primary CA
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvWWaI0MAPPklAUOYW0Y0N2c39F" +
+        "tXjqPezYwvQbMVPeWYi/LMXKfHrzXHs6dPxxApV+kDiYNyBnZSwXACN0Dt8M6LsbGJrAKo" +
+        "W93c1UNFBtwotulRG2ru83tIxZ0Rro2mcpPAJUKRqD5G4mhMgUCwQtN6vntH0kdQDKQSps" +
+        "rkEtDAfDo8AanKApbeglrF+xm6PJzYD3QfmBiulFAyB1IQEUpL7FhVLNSeS5R7BdJy3wbw" +
+        "jcsInuTutEStgvEbYWrxs/gWMTZCJLqQv7V+YW7CWQxUebRMiCgezBvfhIsjyL6vB/KRst" +
+        "qNyoxffCg8fIlsBlm9Ps7FgtNqyaxoVe7FrwIDAQAB",
+
+        // C=US, O=RSA Data Security, Inc., OU=Commercial Certification Authority
+        "MIGbMA0GCSqGSIb3DQEBAQUAA4GJADCBhQJ+AKT7gWJ7zhAn3ej3vmxuxnCZ27jVBQNpKI" +
+        "Kccn+WP47srCmSP4oU+EJ2vr1dA7mQ1NC8BrJRM1/Ewr+2i4+ZtmIiYN3b3yCCtMqiLy1Q" +
+        "7ZQy3uBVjdRo4uBM0s0FFi6VZlxhUjgeUaiCocTvJekK5osrjjFm2fjZ/b07adnrAgMBAA" +
+        "E=",
+
+        // C=DE, O=Deutsche Telekom AG, OU=T-TeleSec Trust Center, CN=Deutsche Telekom Root CA 1
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQ3ZsMoBdERA+vIUBzZ1bwPmloEbrZN/" +
+        "KBrsMkrGmhzfymGFVW/4ufMsHb53gsOdtggUGl79PNgI0YPOJSDAuf92Se5aDwuGFi9L/g" +
+        "o9pYK/0VBGu9Op58nfI92OSVw+xOwvFlqwxL7EeCW+LhUHXY9mG0GFztM6BLHoP7T4S8eQ" +
+        "IDAQAB",
+
+        // C=DE, O=Deutsche Telekom AG, OU=T-TeleSec Trust Center, CN=Deutsche Telekom Root CA 2
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqwujNeCLKRSxFIWvPBDkOW81XU" +
+        "qu3ephjZVJ9G9koxpgZqSpQCKE2dSl5XiTDmgBrblNXDrO07ioQkDfz6O6gllqkhusHJra" +
+        "CCslJ/lpI0fx4Ossepv1EwLQfjR8wp48AFmr9doM9TI8K6xQ2tbD3oOUyqgMmTIOCEhWW2" +
+        "r72uFYWAFJX3JBPBUGAY5draq4k7TNnuun6GotUjTbOu9cdVHa2/Mx+e5xmDLEVBVEDPmb" +
+        "Ve2t3xgIoKOGiknuUwWPGUzV3lh5m9JqHEKrxdWnz2gPluThYZh2YciRfNY+AOKRUIfhnQ" +
+        "rmrZfSHcY6fcu82gM01Y5bAfVqB7cWtm5KfwIDAQAB",
+
+        // C=US, O=Digital Signature Trust Co., OU=DST (ANX Network) CA
+        "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC0SBGAWKDVpZkP9jcsRLZu0XzzKmueEb" +
+        "aIIwRccSWeahJ3EW6/aDllqPay9qIYsokVoGe3eowiSGv2hDQftsr3G3LL8ltI04ceInYT" +
+        "BLSsbJZ/5w4IyTJRMC3VgOghZ7rzXggkLAdZnZAa7kbJtaQelrRBkdR/0o04JrBvQ24JfQ" +
+        "IBAw==",
+
+        // OU=National Retail Federation, CN=DST (NRF) RootCA
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2aybd/pQ08zcuUCsuXJqAIcj/A" +
+        "+WIdAmr+TitV/606Z9ITAuzBeCj5h0/Gekpt+Il6JCKfWn2xGT+14jMMKqvCLnQRvl7SXe" +
+        "yD/b3ldFeEBGg7LVGj3fD0Vt1WMCddgvxm6rlZF0Nw3LTQlc0dRbOtrdDshrmdjVOczfhV" +
+        "XEklMCo+H3gMlwo9rcM8R/okcIHDWWH6EDHDCD9MTM/5jDsEZEosC/rdvSgfZMmCynXiTz" +
+        "hspj1bp98JrAStAbWO7sqWfPaQJsIsBgLCzRyCDqyC373Zy7y1FM3OdXBDtUmxGlMnTsdA" +
+        "HzkBVbL3wsk2W5Zme0gYg15Z6RGH+BqEHIywIDAQAB",
+
+        // OU=United Parcel Service, CN=DST (UPS) RootCA
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7xfsrynm2SsnwNt7JJ9m9ASjwq" +
+        "0KyrDNhCuqN/OAoWDvQo/lXXdfV0JU3SvbYbJxXpN7b1/rJCvnpPLr8XOzC431Wdcy36yQ" +
+        "jk4xuiVNtgym8eWvDOHlb1IDFcHfvn5KpqYYRnA/76dNqNz1dNlhekA8oZQo6sKUiMs3FQ" +
+        "UZPJViuhwt+yiM0ciekjxbEVQ7eNlHO5stSuY+e2vf9PYFzyj2upg2AJ48N4UKnN63pIXF" +
+        "Y/23YhRtFx7MioCFQjIRsCHinXfJgBZBnuvlFIl/t8O8T8Gfh5uW7GP2+ZBWDpWjIwqMZN" +
+        "qbuxx3sExd5sjo9X15LVckP8zjPSyYzxKfFwIDAQAB",
+
+        // CN=Autoridad Certificadora del Colegio Nacional de Correduria Publica Mexicana
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmO0dhYH7/jd0viOAJ18bQX6856" +
+        "WK2HNdlkjqq1iqfaUdz/4gCtnydQnts9X9+JMqGaleqLEU8tZChkFBXk/FVqeaokJvLihI" +
+        "6i6r2cHZmvClnotdEWeaNzdTYGbxIv93d0fp3dwYRu4u3+LBluDqWN6H65OIaZmwPm52KU" +
+        "Bhwyhmc3+sMXb0OM3WMo9zMhAVNNJ8RND8eQwAnX0P4+P3RPWedEknrRvXMshTrm8qsNe1" +
+        "LRgsbjs6TUzb9Wi1L7AMkPk93HU2msLgv7uWiMJr7hjXTlA/V4tnaKS+AzNdWRI0if52yN" +
+        "kVdgFUZP2s41DvEMjQ7l/sHd9PBZg8tBReAQIDAQAB",
+
+        // CN=DST RootCA X1
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0sYmtuelPcHEaNVQb1PFb0kTCb" +
+        "ivLEiNFGqjF19a+dMudS/YKGLRky/8TdSrh+UIx5nnkj91vesltBXBmxk90kSN13QgbTcC" +
+        "j2mTW4rEGZ30sg78Fmy5sQWSg9GFLGCUPkVVoNmrCCHmYOg7dPKZUFFo0AMtsYC+o9hSsE" +
+        "TNQ0pwjliFleFOLNYtQW/WhOfImETKR9ssJKVpJs9ruCdiw/TJepIj7RNngq5FLkXlfnI/" +
+        "hZ2UYhDmPJGhrXcA4BXs84SAcnqObmCXxyRZEDSDW+GlpGm2VzUceFnG0y86c2fulMoEEw" +
+        "ViBnAjs/R87kXZZAtbSaqkQ84mxEQSbLjdeQIDAQAB",
+
+        // OU=DSTCA X2, CN=DST RootCA X2
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3HXwjMB1lprAYh8m98ThmurgVn" +
+        "Nbmc0BRKgIttWn2hoEGDmSSnijgcL1d3pQtHD/mqvGx8pug09CmPsmC9rcbdapmVVSZ+ko" +
+        "A5Lc5bAFmg8V+WtZclby+jn8qmjuDx8Qgy/8nfoXlt2C4+ZFfcBLgEQf7SzghP2RXJJUaS" +
+        "XlYmnc5e4AUr0zC611AoWnZFAtxRkZMMAm28nT/S6ZrVm1C03UQa6FSENZ3Leo4qLew4/X" +
+        "uKFipmhQUuTPMaeUhdqfRjIXVuXy62Y9Ev9D25jvd8/LgY00scZQSibR5D5BUK9sriI0Lt" +
+        "VrboO6ebh2ZUjaCSlkYyK5+0d2hYyGRMsJ2wIDAQAB",
+
+        // C=US, O=Digital Signature Trust Co., OU=DST-Entrust GTI CA
+        "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC2HfdLjQ8T4xL1Cf4GMg6vTEH1fdRHPS" +
+        "oK34MF3t595gMW9lE6y0caSq1+xP0dtL50injdC4OOtIQTxPv4bSmuoeEPD0PjtV5gafqD" +
+        "lPx55tx27dFEK479Erv+F3cXDIntp+9RfcTtOMM7o3r74k2gYLXy/RNl08bsP741nD0i7w" +
+        "IBAw==",
+
+        // C=US, O=Digital Signature Trust Co., OU=DSTCA E1
+        "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCgbIGpzzQeJN3+hijM3oMv+V7UQtLodG" +
+        "BmE5gGHKlREmlvMVW5SXIACH7TpWJENySZj9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+Lth" +
+        "zfNHwJmm8fOR6Hh8AMthyUQncWlVSn5JTe2io74CTADKAqjuAQIxZA9SLRN0dja1erQtcQ" +
+        "IBAw==",
+
+        // C=US, O=Digital Signature Trust Co., OU=DSTCA E2
+        "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC/k48Xku8zExjrEH9OFr//Bo8qhbxe+S" +
+        "SmJIi2A7fBw18DW9Fvrn5C6mYjuGODVvsoLeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87e" +
+        "ZfCocfdPJmyMvMa1795JJ/9IKn3oTQPMx7JSxhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQ" +
+        "IBAw==",
+
+        // CN=Entrust.net Certification Authority (2048)
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvw" +
+        "tKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQesYGpjX24zGtL" +
+        "A/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuXMlBvPci6Zgzj/L24Sc" +
+        "F2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzWnLLPKQP5L6RQstRIzgUy" +
+        "VYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUiVBcAkCaTvA5JaJ" +
+        "G/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQAB",
+
+        // CN=Entrust.net Client Certification Authority
+        "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDIOpleMRffrCdvkHvkGf9FozTC28GoT/" +
+        "Bo6oT9n3V5z8GKUZSvx1cDR2SerYIbWtp/N3hHuzeYEpbOxhN979IMMFGpOZ5V+Pux5zDe" +
+        "g7K6PvHViTs7hbqqdCz+PzFur5GVbgbUB01LLFZHGARS2g4Qk79jkJvh34zmAqTmT173iw" +
+        "IBAw==",
+
+        // CN=Entrust.net Secure Server Certification Authority
+        "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO" +
+        "2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc" +
+        "1lB5gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQw" +
+        "IBAw==",
+
+        // C=US, O=Equifax, OU=Equifax Secure Certificate Authority
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBXbFYZwhi7qCaLR8IbZEUaJgKHv7aBG" +
+        "8ThGIhw9F8zp8F4LgB8E407OKKlQRkrPFrU18Fs8tngL9CAo7+3QEJ7OEAFE/8+/AM3UO6" +
+        "WyvhH4BwmRVXkxbxD5dqt8JoIxzMTVkwrFEeO68r1u5jRXvF2V9Q0uNQDzqI578U/eDHuQ" +
+        "IDAQAB",
+
+        // C=US, O=Equifax Secure Inc., CN=Equifax Secure eBusiness CA-1
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOLxm8F7d33pOpX1oNF080GgyY9CLZWd" +
+        "TEaEbwtDXFhQMgxq9FpSFRRUHrFlg2Mm/iUGJk+f1RnKok2fSdgyqHCiHTEjg0bI0Ablqg" +
+        "2ULuGiGV+VJMVVrFDzhPRvpt+C411h186+LwsHWAyKkTrL6I7zpuq18qOGICsBJ7/o+mAw" +
+        "IDAQAB",
+
+        // CN=Baltimore EZ by DST
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvMyzPUN5uEf5FbduJrFMkph57c" +
+        "Vw8zrp1d0D9Co/YIyW5UcWAvc2svGeJoj1nkJlng+uf+PMsW4h9fGIInTWH7J3BDkyuke1" +
+        "NcATXQFyowVDzE7aJpqHqGFj9GanwxVG6tHR6jDDu3Fqm8FDhsE5H8ZWYAIb/Ig6oJm7jN" +
+        "d4YdBeV4+RO4CLbv/JZYEKObuQEyA1SD+l4b8twXGDhSDtIIfLtv4ZjATd7Sld3woSzolW" +
+        "8h9aGTFYtv1jNurJI96nkZcnZXKZbMd6RMRfvpsfHsqeWBymqiNq4wYbkiTYVyIJUBWQRv" +
+        "CDXraATBKBPWZvBFU6iGvQ71aHUKC51lUbnQIDAQAB",
+
+        // C=US, O=Equifax Secure, OU=Equifax Secure eBusiness CA-2
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkOTmTHlIGGyg2+LKjKcXtjrIRvf7r57" +
+        "R0wo//BefZnQa/Esg/DvLW0SSyEd7RcwmK1LEsmAkNHlBGsoOmRY1iaLuFGyBwMqpAzaaW" +
+        "X8RxNz8E87dBJDkHGh4uYVigEgvlpd/Fq+o3ccwcyDc6uZdSp6zFaiSUTpx7z8Bq1t8hvQ" +
+        "IDAQAB",
+
+        // C=US, O=Equifax Secure Inc., CN=Equifax Secure Global eBusiness CA-1
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC65xeQAmWxNFU8ScJR1d+n0TeP0eeBc0" +
+        "FSYJudoRcmeK3HsegmlDK13jONOi/b8pp6WnOYo1zp+4pzG1znw7+AbM2p9NYrwPf5mapj" +
+        "orFHAg/U5FE6EjxsilpUhHDbwcWQz3JFy6hZwM0znT+jluuFMyEcPh4+YG52nGeFxcjDYQ" +
+        "IDAQAB",
+
+        // O=EUnet International, CN=EUnet International Root CA
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCeQTvZQUmLKJxZFPdQaCh7TQhcZ/+FHg" +
+        "umzzoyArB8fEqftokCIQxKmYvLZFF+eFq2XqlTt+/vx9+lIVmXTuIH5S18GdUqysgz05YQ" +
+        "Lt2gAJ/9yuhhqVPKth0YPpwR4GPnKmdbyESV8BNVSLu+VbhnN83LABMN/E9pFGpRlOy8Jw" +
+        "IDAQAB",
+
+        // CN=FESTE, Public Notary Certs, EmailAddress=feste@feste.org
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDhg/ObLsnn4cf0VAXNdkD+tLMTvucVXo" +
+        "Ym6EB3GlU/0QMmjPqHX6TF+f61MonGf0GR2BVATnBS8PHa+GI1mV4clFNhzD5iwINdWNH4" +
+        "SBFxbPewd+EYl7QHKDCRMcdPVPOEnsxZiUVtfrTJ245ClWbU3x4YTfylD9YahDnEyvK98w" +
+        "IDAQAB",
+
+        // CN=FESTE, Verified Certs
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDqY58fOBqEBISzS5MZhKJ7YsOnqyzsYE" +
+        "5VEeIEMicgNfkaeB8nZ6fggrAF6Capm4pEVr9LhFOjIqYOFlO5f68QyDMYVNnGTHzRW1ZS" +
+        "U4amWz8T8sMB0jGhM1y8XeTcYjzKI5dPcPuBjrDZnq+T6raxJI0ELVFDPDjsJ0Nxh+g8xw" +
+        "IDAQAB",
+
+        // CN=First Data Digital Certificates Inc. Certification Authority
+        "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDfHBQeCbm/pEByIJl5toQi9NeFksUEJO" +
+        "gHLgLkF5UFN5V2Pfyx5Q+HDmK5LDCXJuELFWcAphXe6I3LlewCWFLAR2UzTFafCh8EwDdQ" +
+        "gVe63/rya2fry9CAD9lXlRBlewZFWOuutF7jkxUrmby2KS/7Qp9HKy5M6zQoMpkO7/9voQ" +
+        "IBAw==",
+
+        // C=ES, O=FNMT, OU=FNMT Clase 2 CA
+        "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCYP60ZNpM9Pv52QhT9NW/x+q0ieljjRt" +
+        "Bdxlr5Yi2PMV7+tDD+UHSs1p0d4GLGSd0UEn1xC6wGwT/XBofgkInW5eMDsvInsZ8zyKpr" +
+        "NkqjxD95QZ2JRi8rPmPUOFaRqh2xDUJ1TfOHTuMPTcy0bL9iE4fq0JuOtuL/GfSUCdWWYQ" +
+        "IBAw==",
+
+        // CN=Belgacom E-Trust Primary CA
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq2bmz1U9qTcVB0HsEYWqLcYEH2mTjWG" +
+        "4nVcKtzhew/PqSjQjwHHL/ssMx/uBqh5dMzENXpyh5OrWDXaQdavFqxT4UIh1ZBm/wpjF3" +
+        "3LBJOObLDA/+qnI0iNooOiFa7nQrG6TbWxMWtXNfw66M0sA+PbDL8OyLhgvCwUQYWmOo1Q" +
+        "IDAQAB",
+
+        // C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2g7mmY3Oo+NPin778YuDJWvqSB" +
+        "/xKrC5lREEvfBj0eJnZs8c3c8bSCvujYmOmq8pgGWr6cctEsurHExwB6E9CjDNFY1P+N3U" +
+        "jFAVHO9Q7sQu9/zpUvKRfeBt1TUwjl5Dc/JB6dVq47KJOlY5OG8GPIhpWypNxadUuGyJzJ" +
+        "v5PMrl/Yn1EjySeJbW3HRuk0Rh0Y3HRrJ1DoboGYrVbWzVeBaVounICjjr8iQTT3NUkxOF" +
+        "Ohu8HjS1iwWMuXeLsdsfIJGrCVNukM57N3S5cEeRIlFjFnmusa5BJgjIGSvRRqpI1mQq14" +
+        "M0/ywqwWwZQ0oHhefTfPYhaO/q8lKff5OQzwIDAQAB",
+
+        // CN=GTE CyberTrust Global Root
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4usJTQGz0O9p" +
+        "TAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcqlHHK6" +
+        "XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQ" +
+        "IDAQAB",
+
+        // CN=GTE CyberTrust Root
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6jr11kBL65Xl0stn3JtQOQR3pNgdWct" +
+        "W4adpU1LHWeG2q4zs9o4Q3JcevrwTcsyKx6W2+gm3rjS+9tK5wHqLWbiAxUeZWXHNSsiNQ" +
+        "Trz7mmdAxIYRRsdDIrrqAE9scs1hnN7L+u4w0ub6W53Fmdwg+Dm/ZIwHVju93Gxe9r/h2Q" +
+        "IDAQAB",
+
+        // C=US, O=GTE Corporation, CN=GTE CyberTrust Root
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC45k+625h8cXyvRLfTD0bZZOWTwUKOx7" +
+        "pJjTUteueLveUFMVnGsS8KDPufpz+iCWaEVh43KRuH6X4MypqfpX/1FZSj1aJGgthoTNE3" +
+        "FQZor734sLPwKfWVWgkWYXcKIiXUT0Wqx73llt/51KiOQswkwB6RJ0q1bQaAYznEol44Aw" +
+        "IDAQAB",
+
+        // OU=ValiCert Class 3 Policy Validation Authority
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFl" +
+        "LWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2" +
+        "bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPw" +
+        "IDAQAB",
+
+        // OU=ValiCert Class 1 Policy Validation Authority
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSL" +
+        "wxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+Fiw" +
+        "nRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQ" +
+        "IDAQAB",
+
+        // OU=ValiCert Class 2 Policy Validation Authority
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZU" +
+        "cOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XB" +
+        "hVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9w" +
+        "IDAQAB",
+
+        // C=hk, O=C&W HKT SecureNet CA Class A
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtBuiCqVMc2NGUUh0Y6i0jBbb9M" +
+        "hn3qFIAv/Lo8+n39mxMeDjLihxBKZkWsZc/tCnuOo+Ctr7EX9/JCheyIqsbniqyKIYOZ5M" +
+        "UNHwmLXvpLIbYGu/+XO0C3X5Irvp5YGgldJ2THzTp/5dlRXtB9TH3mAwAO7yLpTxhjLlWV" +
+        "Ho34CiKgDvPIhdEeMAX1TkDEcQbLD1+DN2HDRmW9S7NGM502aUOuzNIinz9hK71CEpN6VE" +
+        "Td+JDAQMfUF7h/MWwUMpZLTWRWerhkxljwG36mOMTnhUREcaU4aMaxgnIQvFVmYOJfbgea" +
+        "xoAHTpmmQ8SU6e4B3IiBtQBvddCfiNixP9XQIDAQAB",
+
+        // CN=IPS SERVIDORES
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsT1J0nznqjtwlxLyYXZhkJAk8IbPMGb" +
+        "WOlI6H0fg3PqHILVikgDVboXVsHUUMH2Fjal5vmwpMwci4YSM1gf/+rHhwLWjhOgeYlQJU" +
+        "3c0jt4BT18g3RXIGJBK6E2Ehim51KODFDzT9NthFf+G4Nu+z4cYgjui0OLzhPvYR3oydAQ" +
+        "IDAQAB",
+
+        // CN=Microsoft Root Authority
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQK9wXDmO/JOGyifl3heMOqiqY" +
+        "0lX/j+lUyjt/6doiA+fFGim6KPYDJr0UJkee6sdslU2vLrnIYcj5+EZrPFa3piI9YdPN4P" +
+        "AZLolsS/LWaammgmmdA6LL8MtVgmwUbnCj44liypKDmo7EmDQuOED7uabFVhrIJ8oWAtd0" +
+        "zpmbRkO5pQHDEIJBSfqeeRKxjmPZhjFGBYBWWfHTdSh/en75QCxhvTv1VFs4mAvzrsVJRO" +
+        "rv2nem10Tq8YzJYJKCEAV5BgaTe7SxIHPFb/W/ukZgoIptKBVlfvtjteFoF3BNr2vq6Alf" +
+        "6wzX/WpxpyXDzKvPAIoyIwswaFybMgdxOF3wIDAQAB",
+
+        // CN=Microsoft Root Certificate Authority
+        "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA8136gGfUWqepDCyQINA1CDx1hM" +
+        "23B4mcidrezsNg+pFoWp6UcSkYdnzC4MgldpQOWPoENDbm36/3gLrpWAsrk+WdBeN3IpH3" +
+        "NGQ8IpEdXuEJkLwU/vx1WBnhebcHkqOuiFkI2J8HygNY/GgpbTLX0qjLS/zhC0gyT+bruK" +
+        "1P5FxvE5SZ25XVdduoGreUkbR3W/VIDI9qeX0UcAR9ba+Q9dpw2Ee3v5svbOcFt+ERYKx5" +
+        "kRR8xdam5OF+1cN+5ZLSPAC1NoLeeeFt87Vu+J8zyctSfXOYNtuLoWuilZebo97CTSb/Bp" +
+        "ZnJQbI56zk7hIzlTGZyDUITjTKeVPVtb5jMllANsClTgRNPdtbBzPkWL/vP1Nk2EJZNVf9" +
+        "D0V8JARNntY4dBGXIpDOaER0km/VS2+whuPHNkKg0PzBwFr5o2G5MEdxlgoWsJHAQpXvEH" +
+        "8oauMqH7HkzQM/d3EExyD8SQ8dRYik18t+iK2OLexF28RRBMkq/OyGnpoRl1vezlOI5uK3" +
+        "/ayVwihA2+8EkN+BMznZskWlI4cGpVWJMbsGLWAOQRh9Hy61l8sR6xXVJKWU7xUUif1Lc/" +
+        "oyW/zRMwD5WWJwBzLqLqtALXvK3SFnGzCZjxaqI6hB0bBuEZs2xN5AdJzhWGXBYB56WzjI" +
+        "j7sEJnzUFkDltmtsqob9AL/OwTUCAwEAAQ==",
+
+        // CN=NetLock Expressz (Class C) Tanusitvanykiado
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDr7LBsYYojJa9gIOPZn/yTC9tdjbChs0" +
+        "A6gs79deB4MgOGWoaVke1T+p1A/Obo3dlbegO9XfM7DMNReZutVaDp0AMQrwq6FELZUiYR" +
+        "IsfSIMyCpJqp/riBdp1qt9I2dT6xhgn2bm1+Trd67K5xhPYEMwglMut0rBZExuRAkx1/rQ" +
+        "IDAQAB",
+
+        // CN=NetLock Kozjegyzoi (Class A) Tanusitvanykiado
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeL" +
+        "Vu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX" +
+        "9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8Wg" +
+        "D/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7tqyF" +
+        "/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCo" +
+        "R64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQAB",
+
+        // OU=Tanusitvanykiadok, CN=NetLock Uzleti (Class B) Tanusitvanykiado
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBN" +
+        "wcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX" +
+        "iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvcQ7GhaQ" +
+        "IDAQAB",
+
+        // CN=Post.Trust Root CA
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1n8T5A0k2Nj76bbDsVKjTty3O+" +
+        "L3Dl+B5gHwpuY2cNgTc6H/UgiQ8hW88jIcqNfhBhB7QaiUxz89RBXcgFHnMP5TSPWQX21t" +
+        "JeBgu6D71sYp+E1wUBo3oA7NeCq2aPOZ1AyOXhJi/8JfWporiEequ6HZdfAsXP5twrFbMc" +
+        "yDhxqnvpAO6BBUU1ILnEnzgAL+byemo1cwuNu40AAEA+Tl1EMG66toTWgm0pk0ueASln9L" +
+        "u2tuIXHmCEVKHWYNN8kD4dHK3LEvcPa3gWKWG2Sn/rvhhutBn6ic2Mqg4dYv+A/hukA492" +
+        "3RpcpMGciW3MxJHAq206iROvna7B3Nc0okPwIDAQAB",
+
+        // CN=PTT Post Root CA, 0.9.2342.19200300.100.1.3=ca@ptt-post.nl
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsH7iOgHxSK1T1HHO276A4FCtma" +
+        "KEeto6JyQ6EYE2Eg3mo5mOpMwmtQ5hxu4oq22G3y6XYfpAacmNjMQxe/pSXlZMIJ5gGl9s" +
+        "SnjJiTyflYasd2cOpg5C6CxiSTJLBD4yQ5AOCiLKyHQOhe+DgcVb8ttshQhvTialBqt245" +
+        "iiTl7EgODo+8zpMGzycmGuJ35T1BWUD9KPeYLZ9o+rxhPmHJh0SwBhDnlpVPKQsqMJAWX3" +
+        "BEdsTvopK/AOBheT3ILAEd6PsDBGWUhKZs42r8fPMdGSdBQj1aq64InbEtHs1GkjuAsWST" +
+        "POGvninF98aB13uwGqZ+Ixxv/WOmn9DBt8IwIDAQAB",
+
+        // CN=Saunalahden Serveri CA, EmailAddress=gold-certs@saunalahti.fi
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5wQp3NbgUtPWTwCvHIGIvzxUcv" +
+        "OeeWP9y2DaDHxyL8obqeIQaWd6OZ/CoCXMg4ONgxEcuP3n26mIowySIVfBquLqM35KZgO8" +
+        "c43SHCn9x39D7Y/rV3uhQb9NczFKNyi0GFdYPGhwUJO6EB14zZPDwoLvuN8PDFjVMFdDOh" +
+        "QlKjhZBrREzdvJXkbyS7gcQ0GB0j5Dsq4hnhtKgHymyrP0JqkuLPi39zwYD5sybxEJc8TN" +
+        "L+jT7Ek284GN2ML/0Bpt3dgUvzLQ6cMNPgiv7dpLnWrPE4uQgmn612cjYUtb/aWAZB1696" +
+        "XT2ncceLtR++dGgJBxcbYW+EO0Gb0Yq952ewIDAQAB",
+
+        // CN=Saunalahden Serveri CA, EmailAddress=silver-certs@saunalahti.fi
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0neMvIdsNk5TqmhgRbgjd2fj7k" +
+        "mC5mx/XpJdtRxhUteoYEsW+ut5fp1MkulXe16GMKkoPH030SHidhoZw++q2u74AZ4aOSov" +
+        "k3UDZj9uKU2NhGOMpx8VlLQ0SbTk00GruvvEXLWecvUoyjKCY0zHRPi0HcSKCldVkK8wiV" +
+        "QOp2gm00AHIrPOPKP7mNckPN58gkm0NIx9JNtkbmSy6f+GyKx+q1Pk0kH0EYTuR0wIHUTm" +
+        "Vk0AfNqJQjnveAjRhea+XJ4zuTX/HM70g7XyZMUxSKm0rMXYPIwabab/Qq3z+EvOrNrFir" +
+        "APAyPB9fPHWX8w8d9mHVoxBaJGHTnkVbOtDwIDAQAB",
+
+        // C=hk, O=C&W HKT SecureNet CA Class B
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn+AlkQ8EV8LHXLFlAmYPqP3YMQ" +
+        "5vgmz5wx6w46C9OERSx4x2EnhMfsIrjIrk+dwK4JVF3+seftJE+AMVAOzEsTx6tk22lgp3" +
+        "vAdg7/C3N/6J/bLYB6tS/oI/vDVnM9n7LNy1WGGiDLF9lNGohGkkPZfNmwhMUImBmh/Swi" +
+        "BvzD8OZcThSEncO/nlKjEHbqZrR6gZWq7ToXS1vMLbOT36q7DwySIJ1DxGaGwuLh/4qIwR" +
+        "oXY1UpLXq4gh3L3pnNn4Pt4wMUwCIi9XZrtWcjk3UJmvV9D0S9Qp7alvxtOyhpGLHRBtaB" +
+        "Zk8Q5tv15n/bKOcGXnb3K8RHWrAXb/N2RFIQIDAQAB",
+
+        // C=US, O=RSA Data Security, Inc., OU=Secure Server Certification Authority
+        "MIGbMA0GCSqGSIb3DQEBAQUAA4GJADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6OLDfO6z" +
+        "V4ZFQD5YRAUcm/jwjiioII0haGN1XpsSECrXZogZoFokvJSyVmIlZsiAeP94FZbYQHZXAT" +
+        "cXY+m3dM41CJVphIuR2nKRoTLkoRWZweFdVJVCxzOmmCsZc5nG1wZ0jl3S3WyB57AgMBAA" +
+        "E=",
+
+        // C=au, O=SecureNet CA Class A
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqaN8+JCzjoRM4Aq+qxIqjQw+L7" +
+        "XdmxCIuWq3h3Ugt0vvIiMG6/BWMvfLLXDFA2+3wdDDZhMCvVVJh4fpLZ6l5XY2q+JkJViI" +
+        "wxsbAvBdsY+fE03CUim0EDVPNoivCy2BCCRhw2iNWm0x6FQZUxf9pxP2QJmmqCnAn0J7Jy" +
+        "nB7tvvjQNkJYGx/pUaHtoQQWIbVn8YGEiY0k1LwRhot2lna2RMbo8CvxRpe/ZEIxDpLrxe" +
+        "Ys1bnMyjjoxRgbSiorG8qMnoKpiqu0sVoeHpkHqef+hlBegRcXpv43XeVT/L2OrIAM0llH" +
+        "JkHu99ED5NL5F5vQLq15DBSWhuWRQl4t3dCQIDAQAB",
+
+        // C=au, O=SecureNet CA Class B
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApmPZxhVadudGZcc0kfl73Va7+J" +
+        "Y1LinKp30KHvcxUuhayNPPOQFOW/AfsbhK0rNHQ2Y/AUBOMEnhD/3rEmN4zPYWYhj1b2n9" +
+        "fm4zdiGjwIgP6uYl/KmXzBhyxzG2C5vNwsV4YWNFrDSmJ3hoxL1SaM6ETdIkpShsgObK5s" +
+        "/mmp5QeM7zNtKjQ1ocBq/LIO7QLMREGJBssZFkZbm3hYNLqJGZxeCc97hQ19OwT5rtY/tN" +
+        "9NQoJDqAW3uTjMUFhK87hv6BMce2nV8a6pB7sEZesghSAFcNVVKDeJVK/WiPntlQtktT+v" +
+        "KFApVOOPWDp5bUMT8/p8o3U9zFL20adKbMvwIDAQAB",
+
+        // C=au, O=SecureNet CA Root
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApyi02Dz1v3oGkb2lQkyzfJ6IZp" +
+        "nF2xfURVTDe8DwJFZmmL9E4HkTdmiu3Zp0z6Lpl+bBwKnD9yzVNjtzna+C2twOX1Ov625Q" +
+        "16jwqo6rY9Kbdf5VCnzRs8BZk1Eqh2mKGe3k19eOFKu1GVizzmzgTYLTA4TBqwAYekmoFX" +
+        "0IyQFgJ5To+wlgntE/Ts0To3j9ZfcRX/abADCMIu0oiWUb0x9he8Mjo+PGgPmD8/e63oZ4" +
+        "X/aVw4xqSCJlhdMiefb9RBboD2EENip1xtviZRQnYtyCXJYSMw5MGNX2PJ2xzWEcsYX5A9" +
+        "G69kzW7p990ZIh8PYKFqQ0h/dWj5O+l69SpwIDAQAB",
+
+        // C=au, O=SecureNet CA SGC Root
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp1uxDYpTIbpSiDiQQmVE/Vbrc8" +
+        "WF8wYx5Qj8jLHVescLIwq8WgkiAfinwN5XdDGLrTbMXnP39kTwMcr1LKIF8wocMHqGM+JG" +
+        "U/Zk1kersVOUY3fEYtMvC+pfsHUCXvgrzybz3tKt62V/vC5BhPyZmumBG6ecZsf49bKEGy" +
+        "B1ciHHhP8CRswPpmmFfVkh1Q6nXVYVT8wfQSx/Zhuv691Bo+yp5lZK/h6nxFwiny/gC3QB" +
+        "cMhzgwoHpGie5FEOjXQxL6LG2ggQK+8lPmyGtUbnl4PAq96wrgYa58j7736tjrCaRfGb9b" +
+        "HoMbtkAL9/kWbNqK+V6hM6Akxb68CT5EH8rQIDAQAB",
+
+        // C=JP, O=Japan Certification Services, Inc., CN=SecureSign RootCA1
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlJAMS3EpHNr2aHl6pLrn0syNr+" +
+        "hHkJkfxirql2PoH84XV8Yas6jHfIftNTWAurpubb4X/swtG2zvigBJFuHuBl5KB12rPdFQ" +
+        "uJFG1NTaFdiUXA7K19q/oPdJPMi7zuomgQoULZwNN0VrQcpXizjwJh8x/M80jo93wT/jq1" +
+        "Q8J7TOMkxVE2L8/joWJc8ba6Ijt+DqAmm79yJxbXwLGZOhl5zjkWkfaOQvfRBtj2euwRCi" +
+        "sF5jSpf35niprSa7VMnftO7FntMl3RNoU/mP6Ozl3oHWeD7uUEC0ATysFcGCOy5/8VIni3" +
+        "Lg59v5iynDw0orM4mrXCoH/HwjHitPCCL+wQIDAQAB",
+
+        // C=JP, O=Japan Certification Services, Inc., CN=SecureSign RootCA1
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlJAMS3EpHNr2aHl6pLrn0syNr+" +
+        "hHkJkfxirql2PoH84XV8Yas6jHfIftNTWAurpubb4X/swtG2zvigBJFuHuBl5KB12rPdFQ" +
+        "uJFG1NTaFdiUXA7K19q/oPdJPMi7zuomgQoULZwNN0VrQcpXizjwJh8x/M80jo93wT/jq1" +
+        "Q8J7TOMkxVE2L8/joWJc8ba6Ijt+DqAmm79yJxbXwLGZOhl5zjkWkfaOQvfRBtj2euwRCi" +
+        "sF5jSpf35niprSa7VMnftO7FntMl3RNoU/mP6Ozl3oHWeD7uUEC0ATysFcGCOy5/8VIni3" +
+        "Lg59v5iynDw0orM4mrXCoH/HwjHitPCCL+wQIDAQAB",
+
+        // C=JP, O=Japan Certification Services, Inc., CN=SecureSign RootCA2
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlnuSIz9g3wk8WIAI42MJl+jkC3" +
+        "Vh1M0Oo/LjHkO6g/+6gVwvyN6Qi0wOLyn5B9aOs6Yor4Iqe8K0Zkxx9Ax0GrjbGuhoN6n5" +
+        "oaJuHCjNbCY8jyoznp3LtHnE2WQ9lcYzqEf75QcJ3PZtuCVCTMP7Su1bLtQHqOWTECSTWG" +
+        "59wdAez+kp19C8X0zwFRbD2MLO41sXW5SLKGsUZyQ79FLsDW58TrSZAtvJ8w+CqwH0jN4W" +
+        "cMa8Fwdh/xFAhOosG3o6sANhB6qWjdDauYOO5J1RaXVxZIG0iFXcEIPOLaX1MJZhLjsK/I" +
+        "dfnFyCdRMe05jR7cntchYcDAbcWSB+8F3v9wIDAQAB",
+
+        // C=JP, O=Japan Certification Services, Inc., CN=SecureSign RootCA2
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlnuSIz9g3wk8WIAI42MJl+jkC3" +
+        "Vh1M0Oo/LjHkO6g/+6gVwvyN6Qi0wOLyn5B9aOs6Yor4Iqe8K0Zkxx9Ax0GrjbGuhoN6n5" +
+        "oaJuHCjNbCY8jyoznp3LtHnE2WQ9lcYzqEf75QcJ3PZtuCVCTMP7Su1bLtQHqOWTECSTWG" +
+        "59wdAez+kp19C8X0zwFRbD2MLO41sXW5SLKGsUZyQ79FLsDW58TrSZAtvJ8w+CqwH0jN4W" +
+        "cMa8Fwdh/xFAhOosG3o6sANhB6qWjdDauYOO5J1RaXVxZIG0iFXcEIPOLaX1MJZhLjsK/I" +
+        "dfnFyCdRMe05jR7cntchYcDAbcWSB+8F3v9wIDAQAB",
+
+        // C=JP, O=Japan Certification Services, Inc., CN=SecureSign RootCA3
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmV4egJZmI2TOnIwAPgqvCOm4BO" +
+        "CEuG1TdU02qLXg14xOYFW2A5ebWhqn87o92ZqUMXZ0I8n37BJd2CDUHekbojd2BA8+rBZp" +
+        "O+H/EC9WJeQzUBMJzE4Oq/Dkddtx1fxKze3bDzUFFdWwZntCeyblWeK1x8Cyx6FD/Q8vC4" +
+        "MlJVeBu7vRNTB0kZCyj59o1dJDt7JFqSPAVtiHEtNz/stZ6q/85x9eVEUcqm2Vk2JHQkFe" +
+        "T+s2Bw4oeFQKfMDDJBOGAwK5rHaSSlrdxdzs+LPbK7UbNud4gkyVfiBWsnUcfZfvf5Q4Ka" +
+        "IA4tHqseM0NjFAWLiqt86BGgwXgQ3967jTvQIDAQAB",
+
+        // C=hk, O=C&W HKT SecureNet CA Root
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtBiikFaM1l2/RliRJ+qddeCk66" +
+        "JQcIdFSUmSa7c5AEt7qNpA4eYNouA3AUhNznLhXJPTw/mSDSTvSM5HKsutkjqq1pWy8hme" +
+        "PpV8j2ACdJMWKGn+O+5deJMcejwj6WE5bMUwLR+EkgVx53TBQkfpMLGjFww2Y89Q0DKoh6" +
+        "VAYhQROPvOL40zsIvpjnD7sJ7HXQPu9uWNcjzIvFSSz8qQ38jbrwXx61DK0QWsBbQBFZb1" +
+        "6zihafeDQ+g8pl2lLLokFi/7DjJwphLWmTb3axuj5/zHG8jYL3XRNbPpwtaPBB3BtX4EOz" +
+        "iJ5KMj8P3KvczrnRcGFXLt0Ob71m+z8Z0+uwIDAQAB",
+
+        // C=JP, O=Japan Certification Services, Inc., CN=SecureSign RootCA3
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmV4egJZmI2TOnIwAPgqvCOm4BO" +
+        "CEuG1TdU02qLXg14xOYFW2A5ebWhqn87o92ZqUMXZ0I8n37BJd2CDUHekbojd2BA8+rBZp" +
+        "O+H/EC9WJeQzUBMJzE4Oq/Dkddtx1fxKze3bDzUFFdWwZntCeyblWeK1x8Cyx6FD/Q8vC4" +
+        "MlJVeBu7vRNTB0kZCyj59o1dJDt7JFqSPAVtiHEtNz/stZ6q/85x9eVEUcqm2Vk2JHQkFe" +
+        "T+s2Bw4oeFQKfMDDJBOGAwK5rHaSSlrdxdzs+LPbK7UbNud4gkyVfiBWsnUcfZfvf5Q4Ka" +
+        "IA4tHqseM0NjFAWLiqt86BGgwXgQ3967jTvQIDAQAB",
+        
+        // CN=SERVICIOS DE CERTIFICACION - A.N.C.
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsiov7CtZakOTiUYqiuXs+gX64s" +
+        "jeQWuvA9sAWu9IN89XifvdyZIQ3ncDlRyQPse2ZyU7VZjv2Tz+JuSKO0SpdDeDCncndLip" +
+        "ca3dlxPSyqIuuLqdyb5Z6Nly8oqFZhxHXrSHgtYP32cmpr02sfNdkFBRdjIsOy+qX2Fe41" +
+        "TVEl3/DY0Rx4J6Nt/hTBbEdN0tau/QsfAzp/6/N2dDEi55SpSvhPsHEQhOMJN16QFUzsXe" +
+        "FIbwrq6bciUPRHfi82yveZwuSceemHYyFpq8AN7gtCAFkRfdgBUU7jZBxCGP7tkAShnGcW" +
+        "GlEV0AO+SndGw6Sm6D4HoxXCFl+AiHQodn5QIDAQAB",
+
+        // CN=SIA Secure Client CA
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDS/LBAYGpmY1Jm5mkJcY2BmB4dHfPgSQ" +
+        "3IK2/Qd1FFxZ1uo1xw3hV4Fh5f4MJi9H0yQ3cI19/S9X83glLGfpOd8U1naMIvwiWIHXHm" +
+        "2ArQeORRQjlVBvOAYv6WpW3FRsdB5QASm2bB4o2VPtXHDFj3yGCknHhxlYzeegm/HNX8ow" +
+        "IDAQAB",
+
+        // C=IT, O=SIA S.p.A., L=Milano, CN=SIA Secure Server CA
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA28ELzCfTEiIuuWQWdKxZJ+IqkA" +
+        "CSntWYXCtRbhsTb1RvShCihethC+ztnH7Of2WTbsxsQZzILarGs5v7THCcEXXzcom6iQCt" +
+        "xy5J53PagLIs/vKXmfQCGzQvOaqL5u8F/Ln1ulR/ob+OHkg2Mwl0Yac9x5skx8OJzcpOKD" +
+        "EjBhxiFY7fTxtrLUri9LDczvOQ/XmBE8E+Lma8+SJNCy9iM42oK+rpb3OnN5QEL+leTQ3p" +
+        "7XwyP3lK5jp2KSBQ84+CRHJsMDRIWKpdGz8B6yHs6n6oK4Rd9sExlU8pe7U1t/60BlewFN" +
+        "fyVVmMupu5MT/lqqrvJXCVkjZB8VWfwQhEnQIDAQAB",
+
+        // OU=Public CA Services
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwOeC2xUTrnnCtF+SjyO8uvfG0Q" +
+        "Cv1lRp8V2mYvhh0Zzeyjss6VwWJzTmuNHKdO8leGRt/hzoiXMxU2dnhsStamjnClZEgzpY" +
+        "R4l3Gtpv8vkHQMk9Ae9q0dlrhJ7FaytOtyz4pGpXq2gxuhlmuuwbV/vOStZLeMPBgT1Llj" +
+        "CZqcMt4uQSJgqkYxIc1HfIgdSnVUMt/ARWndwLrrdsCtozkIgFyX5UgujSMtDXAUkqNZB5" +
+        "OXPWi7xhzYdtUBUFTKnoSkcxiwXM5flC1xJg+Do/o6k2GqWGNiymBIMJ9lLFsH0fiEGQmM" +
+        "VlaJYQshPJFkm9Kr6wSKfC/S1eVtA3TVhR+wIDAQAB",
+
+        // OU=TC TrustCenter Class 1 CA
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwKeu0drOu17ZbtF7nveOxnEkEV1uhq9" +
+        "l/Exv9umGr2Odx3y0AlF1RSH0j73VihJA8Ch9ZEXQvjoCl/TACPSlSzXIaSSGcvMtSjkih" +
+        "Y5bIEIUwaVd0RcBahsbVPeBoV30xaiSNRZc+MX5oZjJuJG3sMjbJQcrwMUTIo2HKG6A2Hw" +
+        "IDAQAB",
+
+        // OU=TC TrustCenter Class 2 CA
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDaOOjtMgApcYMBDb+MAdzaxq05pKmKL9" +
+        "WLXGhfUMZi9Wa9ypEi7KodUdc9s1Gyg05dy0mw8ExV5Wstx4ULMBySToLUygLt92++3ODj" +
+        "FLgFU/Ka9FaLWp6Fk9G0glauTbuoS1cWvP74WJ74KY2we814yU+si2cM8Zz7/FebV1xPDQ" +
+        "IDAQAB",
+
+        // OU=TC TrustCenter Class 3 CA
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2tME1BS4NjeygQGocDiemUJJrUBsH3i" +
+        "7ndszg2vyEqF6MY2orTdlOAnYRwQvyjXnKALbxsA7X+6QXPa+raXqWJ7+vM6GaKlmqxLU3" +
+        "CPISpTG2Q/UylnEoKKuNKIbfu+7jDH0w1sNSq49dJ5xrwKPnBWtXSUSzbupkz9KOelB3dw" +
+        "IDAQAB",
+
+        // OU=TC TrustCenter Class 4 CA
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/L2PWNnuyDdNV9WRs5iVdxrTIFLolOI" +
+        "PrVmKlVallo/QjmcJLudDNVGemo6CjqTMrduS9rXey7VwSdMPFtg9SmnKTQ5BiZhUPRaXd" +
+        "4N24b0BuV8F5cqNgqrp2HRKJU1r8Ar7hCRPFSi/cPYsZrdeLJEX7TPTNXDUdKUxR8/JsVQ" +
+        "IDAQAB",
+
+        // CN=Thawte Premium Server CA
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDSNjZqi9fCW57agUFijzjuSQRV1tDvHB" +
+        "uVFkfvGEg1OlL0K2oGjzsv6lbjr4aNnhf3nrRldQJN78sJoiFR2JvQZ9C6DZIGFHPUk8uX" +
+        "KgCcXE4MvPoVUvzyRG7aEUpuCJ8vLeP5qjqGc7ZGU1jIiQW9gxG4cz+qB430Qk3nQJ0cNw" +
+        "IDAQAB",
+
+        // C=hk, O=C&W HKT SecureNet CA SGC Root
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqFNPj0Pdr+zBtA0bX7cIoprIQu" +
+        "Nt1yUa3+DKvC8iJPlpIr0arVHncfe1dtTzPsg+EdBNe5keGLeezT5hG0URS1sm3Ck8AE0R" +
+        "2h2Pnh903hVAvDDJD9/4LXzYjZ2g4J+wzydgzzgRCO82L3xONh0mAqf01FBDgUnr3beWFD" +
+        "BjMtEDzSG8N5EePmWuFoL2FWBLUTuW5RnowvemBYE6qH8YWD53w1kAg/T1eUlgpy4DPgH9" +
+        "heLfoZqJ2fhkCiuEzUPNJTUAXjBmdKHHCHWsSSeC17CVNW4dmYDrkqAtWtY4u7VHJ6sazL" +
+        "9TU8FGsm/o101XEd2wNUgfqybqVg24CjC22wIDAQAB",
+
+        // CN=Thawte Server CA
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDTpFBuyP9Wa+bPXbbqDGh1R6KqwtqEJf" +
+        "yo9EdR2oW1IHSUhh4PdcnpCGH1Bm0wbhUZAulSwGLbTZme4moMRDjN/r7jZAlwxf6xaym2" +
+        "L0nIO9QnBCUQly/nkG3AKEKZ10xD3sP1IW1Un13DWOHA5NlbsLjctHvfNjrCtWYiEtaHDQ" +
+        "IDAQAB",
+
+        // CN=UTN - DATACorp SGC
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLi" +
+        "t6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZD0/W" +
+        "w5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK4ESGoE1O1k" +
+        "duSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykqlXvY8qdOD1R8oQ2A" +
+        "swkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv33i+Ybqypa4ETLyorG" +
+        "kVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB",
+
+        // CN=UTN-USERFirst-Hardware
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsffDOD+0qH/POYJRZ9Btn9L/WP" +
+        "PnnyvsDYlUmbk4mRb34CF5SMK7YXQSlh08anLVPBBnOjntKxPNZuuVCTOkbJex6MbswXV5" +
+        "nEZejavQav25KlUXEFSzGfCa9vGxXbanbfvgcRdrooj7AN/+GjF3DJoBerEy4ysBBzhuw6" +
+        "VeI7xFm3tQwckwj9vlK3rTW/szQB6g1ZgXvIuHw4nTXaCOsqqq9o5piAbF+okh8widaS4J" +
+        "M5spDUYPjMxJNLBpUb35Bs1orWZMvD6sYb0KiA7I3z3ufARMnQpea5HW7sftKI2rTYeJc9" +
+        "BupNAeFosU4XZEA39jrOTNSZzFkvSrMqFIWwIDAQAB",
+
+        // CN=UTN-USERFirst-Network Applications
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/uRoeQ2VYWsBjRboJpYsvi1Dw" +
+        "V3g64ysXaSaOwjSsl2P+Octjd5A7mraY0HJbYZZ+SwGxhzYUrofs3TL2TjpnwM+heAow1H" +
+        "iU9RcS/u/D/5uBaAh4mTJSCaQ4JpJHYoWTWhHcB/gwZkFiAs00mkhbTAYX9RCPhoFZGAy6" +
+        "XV7js69IQEXmBZp4w0cu64eMXROxJKb35lJ7mkVcW5b0OkxR0smcBSpHhMFbNAmAhrQ8YB" +
+        "sHp79WscIj/L7/+o0DpLdhWe0tHGLuPbVxsyorhv6IamP3Cr5XCSq0QeQFD7nKNi5GxuoM" +
+        "je4oBC+ukv6M4yBI98jbccozU8Fd2ew66XpQIDAQAB",
+
+        // CN=VeriSign Class 3 Public Primary Certification Authority - G3
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy7qcUvx4Hxoebxs3c734yWuUEj" +
+        "BP8DZH9dCRCvUXyKVhwRZATfuKYZDldiDBEQZ9qyxupvURQY76La0qYVmkZyZM0Oi8Ultw" +
+        "IARY0XrJpGm8gxdkrQWLvNBYzo2M9evwQkkLnZcnZzJu4a6TFRxwvCBNLxjekojobIVXER" +
+        "rpfuMmEVSiRZZVg8owiejc2KPtKoA/f3llVz4VIGYIL5WTv6pHL6hGl/AS4v7CCitR5nbm" +
+        "t0a34g2mzKjDTFlVieboU1wc6p3wYhYLp8lfDPDewnbOr/dq8vpBpqIzFMnlemPTnmI31Y" +
+        "Vlng7mUyR0G14dElNbxyzng0k7Fa6KaLlXlwIDAQAB",
+
+        // CN=VeriSign Class 4 Public Primary Certification Authority - G3
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArculEWnGWavxj7UZD1bOzLUfIO" +
+        "SeJiVL4HNliVne0IPk9Q+1u63xfOgh/OToDO58RSIZdpK0E7cgWwn6Ya6o8qWNhcIq1t5m" +
+        "NtKbAvSokmB8nGm0jyQe0IZS9jKcQVgeIr3NRWKVCG7QZt1ToszwENxUc4sEoUYzM1wXQL" +
+        "meTdPzvlWD6LGJjlp8mpYikDuIJfLSU4gCDAt48uY3F0swRgfkgG2m2JYu6Cz4EbM4DWam" +
+        "m+rJI1vbjuLzE44aWS2qAvDspIdm3ME/9di59OyCxtI9lR3lwE+EydmjRCgGatdFrPBrau" +
+        "9OX/gRgh44YzRmUNQ+k3P6MMNmrf+TLZfvAwIDAQAB",
+
+        // OU=VeriSign Trust Network
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC68OTP+cSuhVS5B1f5j8V/aBH4xBewRN" +
+        "zjMHPVKmIquNDMHO0oW369atyzkSTKQWI8/AIBvxwWMZQFl3Zuoq29YRdsTjCG8FE3KlDH" +
+        "qGKB3FtKqsGgtG7rL+VXxbErQHDbWk2hjh+9Ax/YA9SPTJlxvOKCzFjomDqG04Y48wApHw" +
+        "IDAQAB",
+
+        // OU=VeriSign Trust Network
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC68OTP+cSuhVS5B1f5j8V/aBH4xBewRN" +
+        "zjMHPVKmIquNDMHO0oW369atyzkSTKQWI8/AIBvxwWMZQFl3Zuoq29YRdsTjCG8FE3KlDH" +
+        "qGKB3FtKqsGgtG7rL+VXxbErQHDbWk2hjh+9Ax/YA9SPTJlxvOKCzFjomDqG04Y48wApHw" +
+        "IDAQAB",
+
+        // OU=VeriSign Trust Network
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm" +
+        "1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71" +
+        "lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZw" +
+        "IDAQAB",
+
+        // OU=VeriSign Trust Network
+        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm" +
+        "1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71" +
+        "lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZw" +
+        "IDAQAB"
+
+    };
+
+    static class entropySpinner extends Thread {
+        volatile boolean stop = false;
+        byte counter = 0;
+        entropySpinner() { start(); }
+        public void run() { while (!stop) counter++; }
+    }
+    
+    static { 
+
+        entropySpinner[] spinners = new entropySpinner[10];
+        for(int i=0; i<spinners.length; i++) spinners[i] = new entropySpinner();
+
+        for(int i=0; i<pad1.length; i++) pad1[i] = (byte)0x36;
+        for(int i=0; i<pad2.length; i++) pad2[i] = (byte)0x5C;
+        for(int i=0; i<pad1_sha.length; i++) pad1_sha[i] = (byte)0x36;
+        for(int i=0; i<pad2_sha.length; i++) pad2_sha[i] = (byte)0x5C;
+
+        try { 
+            if (Log.on) Log.log(TinySSL.class, "reading in trusted root public keys..."); 
+            trusted_CA_public_keys = new SubjectPublicKeyInfo[base64_encoded_trusted_CA_public_keys.length];
+            for(int i=0; i<base64_encoded_trusted_CA_public_keys.length; i++) {
+                byte[] b = Base64.decode(base64_encoded_trusted_CA_public_keys[i]);
+                DERInputStream dIn = new DERInputStream(new ByteArrayInputStream(b)); 
+                trusted_CA_public_keys[i] = new SubjectPublicKeyInfo((DERConstructedSequence)dIn.readObject());
+            }
+
+        } catch (Exception e) { 
+            if (Log.on) Log.log(TinySSL.class, e);
+        } 
+        
+        randpool = new byte[10];
+        try { Thread.sleep(100); } catch (Exception e) { }
+        for(int i=0; i<spinners.length; i++) {
+            spinners[i].stop = true;
+            randpool[i] = spinners[i].counter;
+        }
+        
+        MD5Digest md5 = new MD5Digest();
+        md5.update(randpool, 0, randpool.length);
+        intToBytes(System.currentTimeMillis(), randpool, 0, 4); md5.update(randpool, 0, 4);
+        intToBytes(Runtime.getRuntime().freeMemory(), randpool, 0, 4); md5.update(randpool, 0, 4);
+        intToBytes(Runtime.getRuntime().totalMemory(), randpool, 0, 4); md5.update(randpool, 0, 4);
+        intToBytes(System.identityHashCode(TinySSL.class), randpool, 0, 4); md5.update(randpool, 0, 4);
+        Properties p = System.getProperties();
+        for(Enumeration e = p.propertyNames(); e.hasMoreElements();) {
+            String s = (String)e.nextElement();
+            byte[] b = s.getBytes();
+            md5.update(b, 0, b.length);
+            b = p.getProperty(s).getBytes();
+            md5.update(b, 0, b.length);
+        }
+        randpool = new byte[md5.getDigestSize()];
+        md5.doFinal(randpool, 0);
+
+    } 
+
+
+    /**
+     *  A PKCS1 encoder which uses TinySSL's built-in PRNG instead of java.security.SecureRandom.
+     *  This code was derived from BouncyCastle's org.bouncycastle.crypto.encoding.PKCS1Encoding.
+     */
+    private static class PKCS1 implements AsymmetricBlockCipher {
+        private static int HEADER_LENGTH = 10;
+        private AsymmetricBlockCipher engine;
+        private boolean forEncryption;
+        private boolean forPrivateKey;
+        
+        public PKCS1(AsymmetricBlockCipher cipher) { this.engine = cipher; }   
+        public AsymmetricBlockCipher getUnderlyingCipher() { return engine; }
+
+        public void init(boolean forEncryption, CipherParameters param) {
+            engine.init(forEncryption, (AsymmetricKeyParameter)param);
+            this.forPrivateKey = ((AsymmetricKeyParameter)param).isPrivate();
+            this.forEncryption = forEncryption;
+        }
+
+        public int getInputBlockSize() { return engine.getInputBlockSize() - (forEncryption ? HEADER_LENGTH : 0); }
+        public int getOutputBlockSize() { return engine.getInputBlockSize() - (forEncryption ? 0 : HEADER_LENGTH); }
+
+        public byte[] processBlock(byte[] in, int inOff, int inLen) throws InvalidCipherTextException {
+            return forEncryption ? encodeBlock(in, inOff, inLen) : decodeBlock(in, inOff, inLen);
+        }
+
+        private byte[] encodeBlock(byte[] in, int inOff, int inLen) throws InvalidCipherTextException {
+            byte[]  block = new byte[engine.getInputBlockSize()];
+            if (forPrivateKey) {
+                block[0] = 0x01;                        // type code 1
+                for (int i = 1; i != block.length - inLen - 1; i++)
+                    block[i] = (byte)0xFF;
+            } else {
+                getRandomBytes(block, 0, block.length);
+                block[0] = 0x02;                        // type code 2
+
+                // a zero byte marks the end of the padding, so all
+                // the pad bytes must be non-zero.
+                for (int i = 1; i != block.length - inLen - 1; i++)
+                    while (block[i] == 0)
+                        getRandomBytes(block, i, 1);
+            }
+
+            block[block.length - inLen - 1] = 0x00;       // mark the end of the padding
+            System.arraycopy(in, inOff, block, block.length - inLen, inLen);
+            return engine.processBlock(block, 0, block.length);
+        }
+
+        private byte[] decodeBlock(byte[] in, int inOff, int inLen) throws InvalidCipherTextException {
+            byte[]  block = engine.processBlock(in, inOff, inLen);
+            if (block.length < getOutputBlockSize())
+                throw new InvalidCipherTextException("block truncated");
+            if (block[0] != 1 && block[0] != 2)
+                throw new InvalidCipherTextException("unknown block type");
+
+            // find and extract the message block.
+            int start;
+            for (start = 1; start != block.length; start++)
+                if (block[start] == 0)
+                    break;
+            start++;           // data should start at the next byte
+
+            if (start >= block.length || start < HEADER_LENGTH)
+                throw new InvalidCipherTextException("no data in block");
+
+            byte[]  result = new byte[block.length - start];
+            System.arraycopy(block, start, result, 0, result.length);
+            return result;
+        }
+    }
+
+}
+
diff --git a/src/org/xwt/Trap.java b/src/org/xwt/Trap.java
new file mode 100644 (file)
index 0000000..d2b3954
--- /dev/null
@@ -0,0 +1,252 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import org.xwt.util.*;
+import java.util.*;
+import org.mozilla.javascript.*;
+
+/**
+ *  This class encapsulates a single trap placed on a given node. The
+ *  traps for a given property name on a given box are maintained as a
+ *  linked list stack, with the most recently placed trap at the head
+ *  of the list.
+ */
+public class Trap {
+
+    // Static Data //////////////////////////////////////////////////////////////
+
+    /** a vector of weak references to all instances of Trap; used for retheming */
+    private static Vec allTraps = new Vec(1000);
+
+    /** List of properties that cannot be trapped */
+    private static final Hash PROHIBITED = new Hash(120, 3);
+
+    static {
+        String[] p = new String[] {
+            "sizetoimage", "shrink", "hshrink", "vshrink", "x", "y", "width", "height",
+            "flex", "align", "invisible", "absolute", "globalx", "globaly",
+            "minwidth", "minheight", "height", "width", "maxwidth", "maxheight", 
+            "numchildren", "hpad", "vpad", "doublebuffered", "cursor",
+            "mousex", "mousey", "xwt", "static", "mouseinside", "root", "thisbox", "indexof", "svg"
+        };
+        for(int i=0; i<p.length; i++) PROHIBITED.put(p[i], Boolean.TRUE);
+    };
+
+    // Instance Members ////////////////////////////////////////////////////////
+
+    /** the box on which this trap was placed */
+    private Box trapee = null;
+
+    /** If this trap was actually placed on a root proxy, this is a reference to the proxy */
+    private Scriptable rp = null;
+    
+    /** the function for this trap */
+    Function f = null;
+
+    /** the name of the property that this trap was placed on */
+    private String name = null;
+
+    /** the next trap down the trap stack */
+    private Trap next = null;
+
+    /** true if this is a read-trap */
+    boolean isreadtrap = false;
+
+    /** The nodeName of the Template whose script placed the trap. See Template.nodeName for more detail. */
+    private String placerNodeName = null;
+
+    /** the index of this Trap in the allTraps vector */
+    private int indexInAllTraps = 0;
+
+    // Static Methods //////////////////////////////////////////////////////////////////////////
+
+    /**
+     *  adds a trap.
+     *  @param trapee the box to place the trap on
+     *  @param name the name of the property to trap on
+     *  @param f the function to place as a trap
+     *  @param isreadtrap true iff this is a read (double-underscore) trap
+     *  @param rp if this trap is being placed via a rootproxy, this is that proxy object.
+     */
+    static void addTrap(Box trapee, String name, Function f, boolean isreadtrap, Scriptable rp) {
+
+        if (PROHIBITED.get(name) != null || name.startsWith("xwt_")) {
+            System.out.println("Error: you cannot place traps on special property \"" + name + "\"");
+            return;
+        }
+
+        // find out what script is currently running
+        String placerNodeName = JSObject.getCurrentFunctionSourceName();
+
+        // check if this script has already placed a trap on this property
+        if (trapee.traps == null) trapee.traps = new Hash(10, 3);
+        for(Trap t = (Trap)trapee.traps.get(name); t != null; t = t.next) if (t.placerNodeName.equals(placerNodeName)) return;
+        
+        // actually place the trap
+        Trap t = new Trap();
+        t.next = (Trap)trapee.traps.get(name);
+        trapee.traps.put(name, t);
+        t.trapee = trapee;
+        t.f = f;
+        t.placerNodeName = placerNodeName;
+        t.rp = rp;
+        t.name = name;
+        t.isreadtrap = isreadtrap;
+
+        // make sure that the surface knows when we place traps on these two properties (slightly inelegant)
+        if (trapee.surface != null && (name.equals("KeyPressed") || name.equals("KeyReleased"))) {
+            trapee.surface.keywatchers.removeElement(trapee);
+            trapee.surface.keywatchers.addElement(trapee);
+        }
+    }
+
+    /** returns the function placed as a trap on property <code>name</code> of box <code>b</code> by the currently-running script */
+    public static Trap getTrap(Box b, String name) {
+        if (b.traps == null) return null;
+
+        String currentFunctionNodeName = JSObject.getCurrentFunctionSourceName();
+        for(Trap cur = (Trap)b.traps.get(name); cur != null; cur = cur.next)
+            if (cur.placerNodeName.equals(currentFunctionNodeName))
+                return cur;
+
+        return null;
+    }
+
+    /** Called by Rhino's arguments.cascade. Note: cx will be null if this was invoked from perform() rather than from a script. */
+    public static final Function cascadeFunction = new CascadeFunction();
+    private static class CascadeFunction extends JSObject implements Function {
+        CascadeFunction() { setSeal(true); }
+        public Scriptable construct(Context cx, Scriptable scope, java.lang.Object[] args) { return null; }
+        public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) {
+            Trap currentTrap = TrapContext.get().currentTrap;
+            if (currentTrap == null || (cx != null && Context.getCurrentContext().currentFunction != currentTrap.f)) {
+                if (Log.on) Log.log(this, "attempt to cascade() by a function that was not invoked as a trap at " +
+                                    cx.interpreterSourceFile + ":" + cx.interpreterLine);
+                return null;
+            }
+            if (args.length != 0) TrapContext.get().putCascadeHappened = true;
+            Trap t = currentTrap.next;
+            // if we've hit the end of the trap stack, just do a put(,,,true)
+            if (t == null) {
+                if (args.length == 0) return currentTrap.trapee.get(currentTrap.name, currentTrap.trapee, true);
+                currentTrap.trapee.put(currentTrap.name, currentTrap.trapee, args[0], true);
+                return null;
+            }
+            return t.perform(args);
+        }
+    };
+
+    /** called by Rhino's arguments.trapee hack */
+    public static Object currentTrapee() {
+        Trap current = TrapContext.get().currentTrap;
+        if (current == null) return null;
+        else if (current.rp != null) return current.rp;
+        else return current.trapee;
+    }
+
+    /** removes all traps whose function's ultimate parent scope is <tt>b</tt>. Used for retheming */
+    public static void removeAllTrapsByBox(Box b) {
+        for(int i=0; i<allTraps.size(); i++) {
+            Trap t = (Trap)((Weak)allTraps.elementAt(i)).get();
+            if (t == null) continue;
+            for(Scriptable cur = t.f; cur != null; cur = cur.getParentScope()) {
+                if (cur == b) {
+                    t.delete();
+                    i--;
+                    break;
+                }
+            }
+        }
+    }
+
+    // Instance Methods //////////////////////////////////////////////////////////////////////////
+
+    private Trap() {
+        indexInAllTraps = allTraps.size();
+        allTraps.addElement(Platform.getWeak(this));
+    }
+
+    /** perform this trap -- arg.length == 0 if this is a get; otherwise it contains a single element to be put */
+    public Object perform(Object[] arg) {
+        TrapContext tc = TrapContext.get();
+
+        // save both thread-locals on the stack and update their values
+        Trap save_currentTrap = tc.currentTrap;
+        tc.currentTrap = this;
+
+        boolean save_putCascadeHappened = tc.putCascadeHappened; 
+        tc.putCascadeHappened = false;
+
+        // invoke the trap function
+        try {
+            if (!isreadtrap && arg.length == 0) return cascadeFunction.call(null, null, null, arg);
+
+            if (f == null) {
+                if (Log.verbose) Log.log(this, "debug: reclaimed a dangling trap on property " + name);
+                Object ret = cascadeFunction.call(null, null, null, arg);
+                delete();
+                return ret;
+            }
+            
+            Object ret = f.call(Context.enter(), f.getParentScope(), f.getParentScope(), arg);
+            
+            // autocascade if required
+            if (arg.length > 0 && !isreadtrap && !tc.putCascadeHappened) cascadeFunction.call(null, null, null, arg);
+            
+            return ret;
+
+        } catch (EcmaError e) {
+            if (Log.on) Log.log(this, "WARNING: uncaught interpreter exception: " + e.getMessage());
+            if (Log.on) Log.log(this, "         thrown from within trap '" + name + "' at " + e.getSourceName() + ":" + e.getLineNumber());
+        } catch (JavaScriptException e) {
+            if (Log.on) Log.log(this, "WARNING: uncaught ecmascript exception: " + e.getMessage());
+            if (Log.on) Log.log(this, "         thrown from within trap '" + name + "' at " + e.sourceFile + ":" + e.line);
+        } finally {
+            // restore the thread-locals
+            tc.putCascadeHappened = save_putCascadeHappened;
+            tc.currentTrap = save_currentTrap;
+            tc.trapDepth--;
+        }
+        return null;
+    }
+
+    /** removes this trap */
+    public void delete() {
+        for(Trap last = null, cur = (Trap)trapee.traps.get(name); cur != null; last = cur, cur = cur.next) {
+            if (cur != this) continue;
+            else if (last != null) last.next = cur.next;
+            else if (cur.next == null) trapee.traps.remove(name);
+            else trapee.traps.put(name, cur.next);
+        }
+        if (trapee.surface != null && !trapee.is_trapped("KeyPressed") && !trapee.is_trapped("KeyReleased"))
+            trapee.surface.keywatchers.removeElement(trapee);
+        if (allTraps.size() == 1) allTraps.setSize(0);
+        else {
+            allTraps.setElementAt(allTraps.elementAt(allTraps.size() - 1), indexInAllTraps);
+            allTraps.setSize(allTraps.size() - 1);
+        }
+    }
+
+    /** per-thread storage for Traps */
+    private static class TrapContext {
+
+        private static Hash trapContextByThread = new Hash();
+        private TrapContext() { }
+
+        private boolean putCascadeHappened = false;
+        private Trap currentTrap = null;
+        private int trapDepth = 0;
+
+        /** returns the TrapContext for the current thread */
+        static TrapContext get() {
+            TrapContext ret = (TrapContext)trapContextByThread.get(Thread.currentThread());
+            if (ret == null) {
+                ret = new TrapContext();
+                trapContextByThread.put(Thread.currentThread(), ret);
+            }
+            return ret;
+        }
+
+    }
+
+}
diff --git a/src/org/xwt/Weak.java b/src/org/xwt/Weak.java
new file mode 100644 (file)
index 0000000..1963061
--- /dev/null
@@ -0,0 +1,9 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+/** Encapsulates a weak reference; MSJVM and NSJVM don't use the standard Java2 java.lang.ref.WeakReference class */
+public interface Weak {
+
+    public Object get();
+
+}
diff --git a/src/org/xwt/XML.java b/src/org/xwt/XML.java
new file mode 100644 (file)
index 0000000..7376510
--- /dev/null
@@ -0,0 +1,973 @@
+package org.xwt;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import org.xwt.util.*;
+
+/** an event-driven XML parser, derived from MinML (http://www.wilson.co.uk/xml/minml.htm) */
+public abstract class XML {
+    
+    /////////////////////////////////////////////////////////////////////////////////////////////
+    // The following code was copied from the w3c's org.xml.sax.* classes
+    /////////////////////////////////////////////////////////////////////////////////////////////
+    
+    protected static interface AttributeList {
+        public abstract int getLength ();
+        public abstract String getName (int i);
+        public abstract String getType (int i);
+        public abstract String getValue (int i);
+        public abstract String getType (String name);
+        public abstract String getValue (String name);
+    }
+    
+    protected static interface DTDHandler {
+        public abstract void notationDecl (String name, String publicId, String systemId) throws SAXException;
+        public abstract void unparsedEntityDecl (String name, String publicId, String systemId, String notationName) throws SAXException;
+    }
+    
+    protected static interface EntityResolver {
+        public abstract InputSource resolveEntity (String publicId, String systemId) throws SAXException, IOException;
+    }
+    
+    protected static interface ErrorHandler {
+        public abstract void warning (SAXParseException exception) throws SAXException;
+        public abstract void error (SAXParseException exception) throws SAXException;
+        public abstract void fatalError (SAXParseException exception) throws SAXException;
+    }
+    
+    private static class InputSource {
+        public InputSource () { }
+        public InputSource (String systemId) { setSystemId(systemId); }
+        public InputSource (InputStream byteStream) { setByteStream(byteStream); }
+        public InputSource (Reader characterStream) { setCharacterStream(characterStream); }
+        public void setPublicId (String publicId) { this.publicId = publicId; }
+        public String getPublicId () { return publicId; }
+        public void setSystemId (String systemId) { this.systemId = systemId; }
+        public String getSystemId() { return systemId; }
+        public void setByteStream (InputStream byteStream) { this.byteStream = byteStream; }
+        public InputStream getByteStream() { return byteStream; }
+        public void setEncoding (String encoding) { this.encoding = encoding; }
+        public String getEncoding() { return encoding; }
+        public void setCharacterStream (Reader characterStream) { this.characterStream = characterStream; }
+        public Reader getCharacterStream () { return characterStream; }
+        private String publicId;
+        private String systemId;
+        private InputStream byteStream;
+        private String encoding;
+        private Reader characterStream;
+    }
+    
+    protected static interface Locator {
+        public abstract String getPublicId ();
+        public abstract String getSystemId ();
+        public abstract int getLineNumber ();
+        public abstract int getColumnNumber ();
+    }
+    
+    protected static interface Parser {
+        public abstract void setLocale (Locale locale) throws SAXException;
+        public abstract void setEntityResolver (EntityResolver resolver);
+        public abstract void setDTDHandler (DTDHandler handler);
+        public abstract void setDocumentHandler (DocumentHandler handler);
+        public abstract void setErrorHandler (ErrorHandler handler);
+        public abstract void parse (InputSource source) throws SAXException, IOException;
+        public abstract void parse (String systemId) throws SAXException, IOException;
+    }
+    
+    public static class SAXException extends Exception {
+        public SAXException (String message) {
+            super();
+            this.message = message;
+            this.exception = null;
+        }
+        
+        public SAXException (Exception e) {
+            super();
+            this.message = null;
+            this.exception = e;
+        }
+        
+        public SAXException (String message, Exception e) {
+            super();
+            this.message = message;
+            this.exception = e;
+        }
+        
+        public String getMessage () {
+            if (message == null && exception != null) {
+                return exception.getMessage();
+            } else {
+                return this.message;
+            }
+        }
+        
+        public Exception getException () { return exception; }
+        public String toString () { return getMessage(); }
+        private String message;
+        private Exception exception;
+    }
+    
+    static class SAXParseException extends SAXException {
+        public SAXParseException (String message, Locator locator) {
+            super(message);
+            this.publicId = locator.getPublicId();
+            this.systemId = locator.getSystemId();
+            this.lineNumber = locator.getLineNumber();
+            this.columnNumber = locator.getColumnNumber();
+        }
+        public SAXParseException (String message, Locator locator, Exception e) {
+            super(message, e);
+            this.publicId = locator.getPublicId();
+            this.systemId = locator.getSystemId();
+            this.lineNumber = locator.getLineNumber();
+            this.columnNumber = locator.getColumnNumber();
+        }
+        public SAXParseException (String message, String publicId, String systemId, int lineNumber, int columnNumber) {
+            super(message);
+            this.publicId = publicId;
+            this.systemId = systemId;
+            this.lineNumber = lineNumber;
+            this.columnNumber = columnNumber;
+        }
+        public SAXParseException (String message, String publicId, String systemId, int lineNumber, int columnNumber, Exception e) {
+            super(message, e);
+            this.publicId = publicId;
+            this.systemId = systemId;
+            this.lineNumber = lineNumber;
+            this.columnNumber = columnNumber;
+        }
+        public String getPublicId() { return this.publicId; }
+        public String getSystemId() { return this.systemId; }
+        public int getLineNumber () { return this.lineNumber; }
+        public int getColumnNumber () { return this.columnNumber; }
+        private String publicId;
+        private String systemId;
+        private int lineNumber;
+        private int columnNumber;
+    }
+    
+    protected static interface DocumentHandler {
+        public abstract void setDocumentLocator (Locator locator);
+        public abstract void startDocument() throws SAXException;
+        public abstract void endDocument() throws SAXException;
+        public abstract void startElement(String name, AttributeList atts) throws SAXException;
+        public abstract void endElement(String name) throws SAXException;
+        public abstract void characters(char ch[], int start, int length) throws SAXException;
+        public abstract void ignorableWhitespace(char ch[], int start, int length) throws SAXException;
+        public abstract void processingInstruction (String target, String data) throws SAXException;
+        Writer startDocument(final Writer writer) throws SAXException;
+        Writer startElement(final String name, final AttributeList attributes, final Writer writer) throws SAXException;
+    }
+    
+    
+    
+    /////////////////////////////////////////////////////////////////////////////////////////////
+    // Everything from here down is copied verbatim from the MinML 1.7
+    // distribution, except for these modifications:
+    //   - some classes have been changed from 'public' to 'private static'
+    //   - extraneous import and package declarations have been removed
+    //   - the advertising clause of the copyright notice has been removed
+    //     as approved by John Wilson in an email to Adam Megacz.
+    /////////////////////////////////////////////////////////////////////////////////////////////
+    
+    // Copyright (c) 2000, 2001, 2002 The Wilson Partnership.
+    // All Rights Reserved.
+    // @(#)MinML.java, 1.8(provisional), 2nd March 2002
+    // Author: John Wilson - tug@wilson.co.uk
+    
+    /*
+      Copyright (c) 2000, 2001 John Wilson (tug@wilson.co.uk).
+      All rights reserved.
+      Redistribution and use in source and binary forms,
+      with or without modification, are permitted provided
+      that the following conditions are met:
+      
+      Redistributions of source code must retain the above
+      copyright notice, this list of conditions and the
+      following disclaimer.
+      
+      Redistributions in binary form must reproduce the
+      above copyright notice, this list of conditions and
+      the following disclaimer in the documentation and/or
+      other materials provided with the distribution.
+      
+      THIS SOFTWARE IS PROVIDED BY JOHN WILSON ``AS IS'' AND ANY
+      EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+      THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+      PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JOHN WILSON
+      BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+      EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+      TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+      DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+      ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+      LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+      IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+      OF THE POSSIBILITY OF SUCH DAMAGE
+    */
+    
+    private static class MinML implements Parser, Locator, DocumentHandler, ErrorHandler {
+        public static final int endStartName = 0;
+        public static final int emitStartElement = 1;
+        public static final int emitEndElement = 2;
+        public static final int possiblyEmitCharacters = 3;
+        public static final int emitCharacters = 4;
+        public static final int emitCharactersSave = 5;
+        public static final int saveAttributeName = 6;
+        public static final int saveAttributeValue = 7;
+        public static final int startComment = 8;
+        public static final int endComment = 9;
+        public static final int incLevel = 10;
+        public static final int decLevel = 11;
+        public static final int startCDATA = 12;
+        public static final int endCDATA = 13;
+        public static final int processCharRef = 14;
+        public static final int writeCdata = 15;
+        public static final int exitParser = 16;
+        public static final int parseError = 17;
+        public static final int discardAndChange = 18;
+        public static final int discardSaveAndChange = 19;
+        public static final int saveAndChange = 20;
+        public static final int change = 21;
+        
+        public static final int inSkipping = 0;
+        public static final int inSTag = 1;
+        public static final int inPossiblyAttribute = 2;
+        public static final int inNextAttribute = 3;
+        public static final int inAttribute = 4;
+        public static final int inAttribute1 = 5;
+        public static final int inAttributeValue = 6;
+        public static final int inAttributeQuoteValue = 7;
+        public static final int inAttributeQuotesValue = 8;
+        public static final int inETag = 9;
+        public static final int inETag1 = 10;
+        public static final int inMTTag = 11;
+        public static final int inTag = 12;
+        public static final int inTag1 = 13;
+        public static final int inPI = 14;
+        public static final int inPI1 = 15;
+        public static final int inPossiblySkipping = 16;
+        public static final int inCharData = 17;
+        public static final int inCDATA = 18;
+        public static final int inCDATA1 = 19;
+        public static final int inComment =20;
+        public static final int inDTD = 21;
+        
+        public MinML(final int initialBufferSize, final int bufferIncrement) {
+            this.initialBufferSize = initialBufferSize;
+            this.bufferIncrement = bufferIncrement;
+        }
+        
+        public MinML() {
+            this(256, 128);
+        }
+        
+        public void parse(final Reader in) throws SAXException, IOException {
+            final Vector attributeNames = new Vector();
+            final Vector attributeValues = new Vector();
+            
+            final AttributeList attrs = new AttributeList() {
+                    public int getLength() {
+                        return attributeNames.size();
+                    }
+                    
+                    public String getName(final int i) {
+                        return (String)attributeNames.elementAt(i);
+                    }
+                    
+                    public String getType(final int i) {
+                        return "CDATA";
+                    }
+                    
+                    public String getValue(final int i) {
+                        return (String)attributeValues.elementAt(i);
+                    }
+                    
+                    public String getType(final String name) {
+                        return "CDATA";
+                    }
+                    
+                    public String getValue(final String name) {
+                        final int index = attributeNames.indexOf(name);
+                        
+                        return (index == -1) ? null : (String)attributeValues.elementAt(index);
+                    }
+                };
+            
+            final MinMLBuffer buffer = new MinMLBuffer(in);
+            int currentChar = 0, charCount = 0;
+            int level = 0;
+            int mixedContentLevel = -1;
+            String elementName = null;
+            String state = operands[inSkipping];
+            
+            this.lineNumber = 1;
+            this.columnNumber = 0;
+            
+            try {
+                while(true) {
+                    charCount++;
+                    
+                    //
+                    // this is to try and make the loop a bit faster
+                    // currentChar = buffer.read(); is simpler but is a bit slower.
+                    //
+                    currentChar = (buffer.nextIn == buffer.lastIn) ? buffer.read() : buffer.chars[buffer.nextIn++];
+                    
+                    final int transition;
+                    
+                    if (currentChar > ']') {
+                        transition = state.charAt(14);
+                    } else {
+                        final int charClass = charClasses[currentChar + 1];
+                        
+                        if (charClass == -1) fatalError("Document contains illegal control character with value " + currentChar, this.lineNumber, this.columnNumber);
+                        
+                        if (charClass == 12) {
+                            if (currentChar == '\r') {
+                                currentChar = '\n';
+                                charCount = -1;
+                            }
+                            
+                            if (currentChar == '\n') {
+                                if (charCount == 0) continue;  // preceeded by '\r' so ignore
+                                
+                                if (charCount != -1) charCount = 0;
+                                
+                                this.lineNumber++;
+                                this.columnNumber = 0;
+                            }
+                        }
+                        
+                        transition = state.charAt(charClass);
+                    }
+                    
+                    this.columnNumber++;
+                    
+                    final String operand = operands[transition >>> 8];
+                    
+                    switch (transition & 0XFF) {
+                    case endStartName:
+                        // end of start element name
+                        elementName = buffer.getString();
+                        if (currentChar != '>' && currentChar != '/') break;  // change state to operand
+                        // drop through to emit start element (we have no attributes)
+                        
+                    case emitStartElement:
+                        // emit start element
+                        
+                        final Writer newWriter = this.extDocumentHandler.startElement(elementName, attrs,
+                                                                                      (this.tags.empty()) ?
+                                                                                      this.extDocumentHandler.startDocument(buffer)
+                                                                                      :
+                                                                                      buffer.getWriter());
+                        
+                        buffer.pushWriter(newWriter);
+                        this.tags.push(elementName);
+                        
+                        attributeValues.removeAllElements();
+                        attributeNames.removeAllElements();
+                        
+                        if (mixedContentLevel != -1) mixedContentLevel++;
+                        
+                        if (currentChar != '/') break;  // change state to operand
+                        
+                        // <element/> drop through
+                        
+                    case emitEndElement:
+                        // emit end element
+                        
+                        try {
+                            final String begin = (String)this.tags.pop();
+                            
+                            buffer.popWriter();
+                            elementName = buffer.getString();
+                            
+                            if (currentChar != '/' && !elementName.equals(begin)) {
+                                fatalError("end tag </" + elementName + "> does not match begin tag <" + begin + ">",
+                                           this.lineNumber, this.columnNumber);
+                            } else {
+                                this.documentHandler.endElement(begin);
+                                
+                                if (this.tags.empty()) {
+                                    this.documentHandler.endDocument();
+                                    return;
+                                }
+                            }
+                        }
+                        catch (final EmptyStackException e) {
+                            fatalError("end tag at begining of document", this.lineNumber, this.columnNumber);
+                        }
+                        
+                        if (mixedContentLevel != -1) --mixedContentLevel;
+                        
+                        break;  // change state to operand
+                        
+                    case emitCharacters:
+                        // emit characters
+                        
+                        buffer.flush();
+                        break;  // change state to operand
+                        
+                    case emitCharactersSave:
+                        // emit characters and save current character
+                        
+                        if (mixedContentLevel == -1) mixedContentLevel = 0;
+                        
+                        buffer.flush();
+                        
+                        buffer.saveChar((char)currentChar);
+                        
+                        break;  // change state to operand
+                        
+                    case possiblyEmitCharacters:
+                        // write any skipped whitespace if in mixed content
+                        
+                        if (mixedContentLevel != -1) buffer.flush();
+                        break;  // change state to operand
+                        
+                    case saveAttributeName:
+                        // save attribute name
+                        
+                        attributeNames.addElement(buffer.getString());
+                        break;  // change state to operand
+                        
+                    case saveAttributeValue:
+                        // save attribute value
+                        
+                        attributeValues.addElement(buffer.getString());
+                        break;  // change state to operand
+                        
+                    case startComment:
+                        // change state if we have found "<!--"
+                        
+                        if (buffer.read() != '-') continue; // not "<!--"
+
+                        char[] lastthree = new char[3];
+                        int pos = 0;
+                        while(true) {
+                            currentChar = buffer.read();
+                            lastthree[pos] = (char)currentChar; 
+                            if (lastthree[pos] == '>' && lastthree[(pos + 2) % 3] == '-' && lastthree[(pos + 1) % 3] == '-') break;
+                            pos = (pos + 1) % 3;
+                        }
+
+                        state = operands[inCharData];
+                        continue;  // change state to operand
+
+                    case endComment:
+                        // change state if we find "-->"
+                        
+                        if ((currentChar = buffer.read()) == '-') {
+                            // deal with the case where we might have "------->"
+                            while ((currentChar = buffer.read()) == '-');
+                            
+                            if (currentChar == '>') break;  // end of comment, change state to operand
+                        }
+                        
+                        continue;   // not end of comment, don't change state
+                        
+                    case incLevel:
+                        
+                        level++;
+                        
+                        break;
+                        
+                    case decLevel:
+                        
+                        if (level == 0) break; // outer level <> change state
+                        
+                        level--;
+                        
+                        continue; // in nested <>, don't change state
+                        
+                    case startCDATA:
+                        // change state if we have found "<![CDATA["
+                        
+                        if (buffer.read() != 'C') continue;   // don't change state
+                        if (buffer.read() != 'D') continue;   // don't change state
+                        if (buffer.read() != 'A') continue;   // don't change state
+                        if (buffer.read() != 'T') continue;   // don't change state
+                        if (buffer.read() != 'A') continue;   // don't change state
+                        if (buffer.read() != '[') continue;   // don't change state
+                        break;  // change state to operand
+                        
+                    case endCDATA:
+                        // change state if we find "]]>"
+                        
+                        if ((currentChar = buffer.read()) == ']') {
+                            // deal with the case where we might have "]]]]]]]>"
+                            while ((currentChar = buffer.read()) == ']') buffer.write(']');
+                            
+                            if (currentChar == '>') break;  // end of CDATA section, change state to operand
+                            
+                            buffer.write(']');
+                        }
+                        
+                        buffer.write(']');
+                        buffer.write(currentChar);
+                        continue;   // not end of CDATA section, don't change state
+                        
+                    case processCharRef:
+                        // process character entity
+                        
+                        int crefState = 0;
+                        
+                        currentChar = buffer.read();
+                        
+                        while (true) {
+                            if ("#amp;&pos;'quot;\"gt;>lt;<".charAt(crefState) == currentChar) {
+                                crefState++;
+                                
+                                if (currentChar == ';') {
+                                    buffer.write("#amp;&pos;'quot;\"gt;>lt;<".charAt(crefState));
+                                    break;
+                                    
+                                } else if (currentChar == '#') {
+                                    final int radix;
+                                    
+                                    currentChar = buffer.read();
+                                    
+                                    if (currentChar == 'x') {
+                                        radix = 16;
+                                        currentChar = buffer.read();
+                                    } else {
+                                        radix = 10;
+                                    }
+                                    
+                                    int charRef = Character.digit((char)currentChar, radix);
+                                    
+                                    while (true) {
+                                        currentChar = buffer.read();
+                                        
+                                        final int digit = Character.digit((char)currentChar, radix);
+                                        
+                                        if (digit == -1) break;
+                                        
+                                        charRef = (char)((charRef * radix) + digit);
+                                    }
+                                    
+                                    if (currentChar == ';' && charRef != -1) {
+                                        buffer.write(charRef);
+                                        break;
+                                    }
+                                    
+                                    fatalError("invalid Character Entitiy", this.lineNumber, this.columnNumber);
+                                } else {
+                                    currentChar = buffer.read();
+                                }
+                            } else {
+                                crefState = ("\u0001\u000b\u0006\u00ff\u00ff\u00ff\u00ff\u00ff\u00ff\u00ff\u00ff" +
+                                             //                               #     a     m     p     ;     &     p     o     s     ;     '
+                                             //                               0     1     2     3     4     5     6     7     8     9     a
+                                             "\u0011\u00ff\u00ff\u00ff\u00ff\u00ff\u0015\u00ff\u00ff\u00ff" +
+                                             //                               q     u     o     t     ;     "     g     t     ;     >
+                                             //                               b     b     d     e     f     10    11    12    13    14
+                                             "\u00ff\u00ff\u00ff").charAt(crefState);
+                                //                               l     t     ;
+                                //                               15    16    17
+                                
+                                if (crefState == 255) fatalError("invalid Character Entitiy", this.lineNumber, this.columnNumber);
+                            }
+                        }
+                        
+                        break;
+                        
+                    case parseError:
+                        // report fatal error
+                        
+                        fatalError(operand, this.lineNumber, this.columnNumber);
+                        // drop through to exit parser
+                        
+                    case exitParser:
+                        // exit parser
+                        
+                        return;
+                        
+                    case writeCdata:
+                        // write character data
+                        // this will also write any skipped whitespace
+                        
+                        buffer.write(currentChar);
+                        break;  // change state to operand
+                        
+                    case discardAndChange:
+                        // throw saved characters away and change state
+                        
+                        buffer.reset();
+                        break;  // change state to operand
+                        
+                    case discardSaveAndChange:
+                        // throw saved characters away, save character and change state
+                        
+                        buffer.reset();
+                        // drop through to save character and change state
+                        
+                    case saveAndChange:
+                        // save character and change state
+                        
+                        buffer.saveChar((char)currentChar);
+                        break;  // change state to operand
+                        
+                    case change:
+                        // change state to operand
+                        
+                        break;  // change state to operand
+                    }
+                    
+                    state = operand;
+                }
+            }
+            catch (final IOException e) {
+                this.errorHandler.fatalError(new SAXParseException(e.toString(), null, null, this.lineNumber, this.columnNumber, e));
+            }
+            finally {
+                this.errorHandler = this;
+                this.documentHandler = this.extDocumentHandler = this;
+                this.tags.removeAllElements();
+            }
+        }
+        
+        public void parse(final InputSource source) throws SAXException, IOException {
+            if (source.getCharacterStream() != null)
+                parse(source.getCharacterStream());
+            else if (source.getByteStream() != null)
+                parse(new InputStreamReader(source.getByteStream()));
+            else
+                parse(new InputStreamReader(new URL(source.getSystemId()).openStream()));
+        }
+        
+        public void parse(final String systemId) throws SAXException, IOException {
+            parse(new InputSource(systemId));
+        }
+        
+        public void setLocale(final Locale locale) throws SAXException {
+            throw new SAXException("Not supported");
+        }
+        
+        public void setEntityResolver(final EntityResolver resolver) {
+            // not supported
+        }
+        
+        public void setDTDHandler(final DTDHandler handler) {
+            // not supported
+        }
+        
+        public void setDocumentHandler(final DocumentHandler handler) {
+            this.documentHandler = (handler == null) ? this : handler;
+            if (handler != null) handler.setDocumentLocator(this);
+            this.extDocumentHandler = this;
+        }
+
+        public void setErrorHandler(final ErrorHandler handler) {
+            this.errorHandler = (handler == null) ? this : handler;
+        }
+        
+        public void setDocumentLocator(final Locator locator) {
+        }
+        
+        public void startDocument() throws SAXException {
+        }
+        
+        public Writer startDocument(final Writer writer) throws SAXException {
+            this.documentHandler.startDocument();
+            return writer;
+        }
+        
+        public void endDocument() throws SAXException {
+        }
+        
+        public void startElement(final String name, final AttributeList attributes) throws SAXException {
+        }
+        
+        public Writer startElement(final String name, final AttributeList attributes, final Writer writer)
+            throws SAXException
+        {
+            this.documentHandler.startElement(name, attributes);
+            return writer;
+        }
+        
+        public void endElement(final String name) throws SAXException {
+        }
+        
+        public void characters(final char ch[], final int start, final int length) throws SAXException {
+        }
+        
+        public void ignorableWhitespace(final char ch[], final int start, final int length) throws SAXException {
+        }
+        
+        public void processingInstruction(final String target, final String data) throws SAXException {
+        }
+        
+        public void warning(final SAXParseException e) throws SAXException {
+        }
+        
+        public void error(final SAXParseException e) throws SAXException {
+        }
+        
+        public void fatalError(final SAXParseException e) throws SAXException {
+            throw e;
+        }
+        
+        public String getPublicId() {
+            return "";
+        }
+        
+        
+        public String getSystemId() {
+            return "";
+        }
+        
+        public int getLineNumber () {
+            return this.lineNumber;
+        }
+        
+        public int getColumnNumber () {
+            return this.columnNumber;
+        }
+        
+        private void fatalError(final String msg, final int lineNumber, final int columnNumber) throws SAXException {
+            this.errorHandler.fatalError(new SAXParseException(msg, null, null, lineNumber, columnNumber));
+        }
+        
+        private class MinMLBuffer extends Writer {
+            public MinMLBuffer(final Reader in) {
+                this.in = in;
+            }
+            
+            public void close() throws IOException {
+                flush();
+            }
+            
+            public void flush() throws IOException {
+                try {
+                    _flush();
+                    if (writer != this) writer.flush();
+                }
+                finally {
+                    flushed = true;
+                }
+            }
+            
+            public void write(final int c) throws IOException {
+                written = true;
+                chars[count++] = (char)c;
+            }
+            
+            public void write(final char[] cbuf, final int off, final int len) throws IOException {
+                written = true;
+                System.arraycopy(cbuf, off, chars, count, len);
+                count += len;
+            }
+            
+            public void saveChar(final char c) {
+                written = false;
+                chars[count++] = c;
+            }
+            
+            public void pushWriter(final Writer writer) {
+                MinML.this.tags.push(this.writer);
+                
+                this.writer = (writer == null) ? this : writer;
+                
+                flushed = written = false;
+            }
+            
+            public Writer getWriter() {
+                return writer;
+            }
+            
+            public void popWriter() throws IOException {
+                try {
+                    if (!flushed && writer != this) writer.flush();
+                }
+                finally {
+                    writer = (Writer)MinML.this.tags.pop();
+                    flushed = written = false;
+                }
+            }
+            
+            public String getString() {
+                final String result = new String(chars, 0, count);
+                
+                count = 0;
+                return result;
+            }
+            
+            public void reset() {
+                count = 0;
+            }
+            
+            public int read() throws IOException {
+                if (nextIn == lastIn) {
+                    if (count != 0) {
+                        if (written) {
+                            _flush();
+                        } else if (count >= (chars.length - MinML.this.bufferIncrement)) {
+                            final char[] newChars = new char[chars.length + MinML.this.bufferIncrement];
+                            
+                            System.arraycopy(chars, 0, newChars, 0, count);
+                            chars = newChars;
+                        }
+                    }
+                    
+                    final int numRead = in.read(chars, count, chars.length - count);
+                    
+                    if (numRead == -1) return -1;
+                    
+                    nextIn = count;
+                    lastIn = count + numRead;
+                }
+                
+                return chars[nextIn++];
+            }
+            
+            private void _flush() throws IOException {
+                if (count != 0) {
+                    try {
+                        if (writer == this) {
+                            try {
+                                MinML.this.documentHandler.characters(chars, 0, count);
+                            }
+                            catch (final SAXException e) {
+                                throw new IOException(e.toString());
+                            }
+                        } else {
+                            writer.write(chars, 0, count);
+                        }
+                    }
+                    finally {
+                        count = 0;
+                    }
+                }
+            }
+            
+            private int nextIn = 0, lastIn = 0;
+            private char[] chars = new char[MinML.this.initialBufferSize];
+            private final Reader in;
+            private int count = 0;
+            private Writer writer = this;
+            private boolean flushed = false;
+            private boolean written = false;
+        }
+        
+        private DocumentHandler extDocumentHandler = this;
+        private DocumentHandler documentHandler = this;
+        private ErrorHandler errorHandler = this;
+        private final Stack tags = new Stack();
+        private int lineNumber = 1;
+        private int columnNumber = 0;
+        private final int initialBufferSize;
+        private final int bufferIncrement;
+        
+        private static final byte[] charClasses = {
+            //  EOF
+            13,
+            //                                      \t  \n          \r
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 12, -1, -1, 12, -1, -1,
+            //
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            //  SP   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /
+            12,  8,  7, 14, 14, 14,  3,  6, 14, 14, 14, 14, 14, 11, 14,  2,
+            //   0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?
+            14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,  0,  5,  1,  4,
+            //
+            14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+            //                                               [   \   ]
+            14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,  9, 14, 10
+        };
+        
+        private static final String[] operands = {
+            "\u0d15\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u1611\u0015\u0010\u1611",
+            "\u1711\u1000\u0b00\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u0114\u0200\u1811\u0114",
+            "\u1711\u1001\u0b01\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u0215\u1811\u0414",
+            "\u1711\u1001\u0b01\u1711\u1911\u1911\u1911\u1911\u1911\u1911\u1911\u1911\u0315\u1811\u0414",
+            "\u1911\u1911\u1911\u1911\u1911\u0606\u1911\u1911\u1911\u1911\u1911\u0414\u0515\u1811\u0414",
+            "\u1911\u1911\u1911\u1911\u1911\u0606\u1911\u1911\u1911\u1911\u1911\u1911\u0515\u1811\u1911",
+            "\u1a11\u1a11\u1a11\u1a11\u1a11\u1a11\u0715\u0815\u1a11\u1a11\u1a11\u1a11\u0615\u1811\u1a11",
+            "\u0714\u0714\u0714\u070e\u0714\u0714\u0307\u0714\u0714\u0714\u0714\u0714\u0714\u1811\u0714",
+            "\u0814\u0814\u0814\u080e\u0814\u0814\u0814\u0307\u0814\u0814\u0814\u0814\u0814\u1811\u0814",
+            "\u1711\u1002\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u0914\u0915\u1811\u0914",
+            "\u1b11\u1b11\u0904\u1b11\u1b11\u1b11\u1b11\u1b11\u1215\u1b11\u1b11\u1b11\u1b11\u1811\u0105",
+            "\u1711\u1012\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1711\u1811\u1711",
+            "\u1711\u1c11\u0912\u1711\u0e12\u1711\u1711\u1711\u1212\u1711\u1711\u1711\u1711\u1811\u0113",
+            "\u1711\u1c11\u0912\u1711\u0e12\u1711\u1711\u1711\u1212\u1711\u1711\u1711\u1711\u1811\u0113",
+            "\u0e15\u0e15\u0e15\u0e15\u0f15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u1811\u0e15",
+            "\u0e15\u0015\u0e15\u0e15\u0f15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u0e15\u1811\u0e15",
+            "\u0c03\u110f\u110f\u110e\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u1014\u1811\u110f",
+            "\u0a15\u110f\u110f\u110e\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u110f\u1811\u110f",
+            "\u1d11\u1d11\u1d11\u1d11\u1d11\u1d11\u1d11\u1d11\u1d11\u130c\u1d11\u1408\u1d11\u1811\u1515",
+            "\u130f\u130f\u130f\u130f\u130f\u130f\u130f\u130f\u130f\u130f\u110d\u130f\u130f\u1811\u130f",
+            "\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u1415\u0009\u1415\u1811\u1415",
+            "\u150a\u000b\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1515\u1811\u1515",
+            "expected Element",
+            "unexpected character in tag",
+            "unexpected end of file found",
+            "attribute name not followed by '='",
+            "invalid attribute value",
+            "expecting end tag",
+            "empty tag",
+            "unexpected character after <!"
+        };
+    }
+    ///////////////////////////////////////////////////////////////////////////////
+
+    private class XMLHelper implements DocumentHandler {
+        private MinML minml = new MinML();
+        XMLHelper() { }
+        public void parse(Reader r) throws IOException, XML.SAXException {
+            minml.setDocumentHandler(this);
+            minml.parse(new InputSource(r));
+        }
+        
+        public void startDocument() throws SAXException { }
+        public void endDocument() throws SAXException { }
+        public void processingInstruction (String target, String data) throws SAXException { }
+        public Writer startDocument(final Writer writer) throws SAXException { return null; }
+        public Writer startElement(final String name, final AttributeList attributes, final Writer writer) throws SAXException { return null; }
+
+        public void setDocumentLocator (Locator locator) {
+            this.locator = locator;
+        }
+
+        private Locator locator = null;
+
+        public void startElement(String name, AttributeList atts) throws SAXException {
+            String[] keys = new String[atts.getLength()];
+            Object[] vals = new Object[atts.getLength()];
+            for (int i=0; i <atts.getLength(); i++) {
+                keys[i] = atts.getName(i);
+                vals[i] = atts.getValue(i).toString();
+            }
+            XML.this.startElement(name, keys, vals,
+                                  locator == null ? 0 : locator.getLineNumber(), locator == null ? 0 : locator.getColumnNumber());
+        }
+
+        public void endElement(String name) throws SAXException {
+            XML.this.endElement(name, locator == null ? 0 : locator.getLineNumber(), locator == null ? 0 : locator.getColumnNumber());
+        }
+
+        public void characters(char ch[], int start, int length) throws SAXException {
+            XML.this.content(ch, start, length, locator == null ? 0 : locator.getLineNumber(), locator == null ? 0 : locator.getColumnNumber());
+        }
+        public void ignorableWhitespace(char ch[], int start, int length) throws SAXException {
+            XML.this.content(ch, start, length, locator == null ? 0 : locator.getLineNumber(), locator == null ? 0 : locator.getColumnNumber());
+        }
+    }
+
+    public XML() { }
+    public void parse(Reader r) throws IOException, XML.SAXException {
+        XMLHelper helper = new XMLHelper();
+        helper.parse(r);
+        helper = null;
+    }
+
+    /** indicates the start of an element with name <tt>name</tt>, and attributes <tt>attributes</tt>, starting on line <tt>line</tt> */
+    public abstract void startElement(String name, String[] keys, Object[] vals, int line, int col) throws SAXException;
+
+    /** indicates the end of an element with name <tt>name</tt>, starting on line <tt>line</tt> */
+    public abstract void endElement(String name, int line, int col) throws SAXException;
+
+    /** indicates a chunk of CDATA content, starting on line <tt>line</tt> */
+    public abstract void content(char[] content, int start, int length, int line, int col) throws SAXException; 
+}
diff --git a/src/org/xwt/XMLRPC.java b/src/org/xwt/XMLRPC.java
new file mode 100644 (file)
index 0000000..ca5b54f
--- /dev/null
@@ -0,0 +1,472 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import org.mozilla.javascript.*;
+import org.xwt.util.*;
+
+/**
+ *  An XML-RPC client implemented as a Rhino JavaScript Host
+ *  Object. See the XWT spec for information on its behavior.
+ *
+ *  NOTE: this client is EXTREMELY lenient in the responses it will
+ *  accept; there are many, many invalid responses that it will
+ *  successfully parse and return. Do NOT use this to determine the
+ *  validity of your server.
+ *
+ *  This client conforms to <a href="http://www.xmlrpc.com/spec">The
+ *  XML-RPC Spec</a>, subject to these limitations:
+ *  <ol>
+ *    <li> XMLRPC cannot invoke methods that require a <base64/> argument
+ *    <li> if a return value contains a <base64/>, it will be returned as a string
+ *    <li> The decision to pass a number as <i4/> or <double/> is based
+ *         entirely on whether or not the argument is fractional. Thus, it
+ *         is impossible to pass a non-fractional number to an xmlrpc
+ *         method that insists on being called with a <double/> element. We
+ *         hope that most xml-rpc servers will be able to automatically
+ *         convert.
+ *  </ol>
+ */
+class XMLRPC extends XML implements Function {
+
+    /** should we use SSL? */
+    protected boolean ssl = false;
+
+    /** the url to connect to */
+    protected URL url = null;
+
+    /** the method name to invoke on the remove server */
+    protected String methodname = null;
+
+    /** the host portion of the url; not calculated until first call() */
+    protected String host = null;
+
+    /** the filename portion of the url; not calculated until first call() */
+    protected String filename = null;
+
+    /** the port to connect to; not calculated until the first call() */
+    protected int port = -1;
+
+    /** this holds character content as we read it in -- since there is only one per instance, we don't support mixed content */
+    protected AccessibleCharArrayWriter content = new AccessibleCharArrayWriter(100);
+
+    /** The object stack. As we process xml elements, pieces of the
+     *  return value are pushed onto and popped off of this stack.
+     *
+     *  The general protocol is that any time a &lt;value&gt; tag is
+     *  encountered, an empty String ("") is pushed onto the stack. If
+     *  the &lt;value/&gt; node has content (either an anonymous
+     *  string or some other XML node), that content replaces the
+     *  empty string.
+     *
+     *  If an &lt;array&gt; tag is encountered, a null is pushed onto the
+     *  stack. When a &lt;/data&gt; is encountered, we search back on the
+     *  stack to the last null, replace it with a NativeArray, and
+     *  insert into it all elements above it on the stack.
+     *
+     *  If a &lt;struct&gt; tag is encountered, a JSObject is pushed
+     *  onto the stack. If a &lt;name&gt; tag is encountered, its CDATA is
+     *  pushed onto the stack. When a &lt;/member&gt; is encountered, the
+     *  name (second element on stack) and value (top of stack) are
+     *  popped off the stack and inserted into the struct (third
+     *  element on stack).
+     */
+    protected Vec objects = null;
+
+    /** used to detect multi-ref data */
+    private Hash tracker;
+
+    /** True iff the return value is a fault (and should be thrown as an exception) */
+    protected boolean fault = false;
+
+
+    // Methods to Recieve and parse XML-RPC Response ////////////////////////////////////////////////////
+
+    public void startElement(String name, String[] keys, Object[] vals, int line, int col) {
+        if (name.equals("fault")) fault = true;
+        else if (name.equals("struct")) objects.setElementAt(new JSObject(false), objects.size() - 1);
+        else if (name.equals("array")) objects.setElementAt(null, objects.size() - 1);
+        else if (name.equals("value")) objects.addElement("");
+    }
+
+    public void endElement(String name, int line, int col) {
+
+        if (name.equals("int") || name.equals("i4"))
+            objects.setElementAt(new Integer(new String(content.getBuf(), 0, content.size())), objects.size() - 1);
+
+        else if (name.equals("boolean"))
+            objects.setElementAt(content.getBuf()[0] == '1' ? Boolean.TRUE : Boolean.FALSE, objects.size() - 1);
+
+        else if (name.equals("string"))
+            objects.setElementAt(new String(content.getBuf(), 0, content.size()), objects.size() - 1);
+
+        else if (name.equals("double"))
+            objects.setElementAt(new Double(new String(content.getBuf(), 0, content.size())), objects.size() - 1);
+
+        else if (name.equals("base64"))
+            objects.setElementAt(new String(content.getBuf(), 0, content.size()), objects.size() - 1);
+
+        else if (name.equals("name"))
+            objects.addElement(new String(content.getBuf(), 0, content.size()));
+
+        else if (name.equals("value") && "".equals(objects.lastElement()))
+            objects.setElementAt(new String(content.getBuf(), 0, content.size()), objects.size() - 1);
+
+        else if (name.equals("dateTime.iso8601")) {
+            String s = new String(content.getBuf(), 0, content.size());
+
+            // strip whitespace
+            int i=0;
+            while(Character.isWhitespace(s.charAt(i))) i++;
+            if (i > 0) s = s.substring(i);
+
+            try {
+                NativeDate nd = (NativeDate)Context.enter().newObject(org.xwt.util.JSObject.defaultObjects, "Date");
+                double date = NativeDate.date_msecFromDate(Double.valueOf(s.substring(0, 4)).doubleValue(),
+                                                           Double.valueOf(s.substring(4, 6)).doubleValue() - 1,
+                                                           Double.valueOf(s.substring(6, 8)).doubleValue(),
+                                                           Double.valueOf(s.substring(9, 11)).doubleValue(),
+                                                           Double.valueOf(s.substring(12, 14)).doubleValue(),
+                                                           Double.valueOf(s.substring(15, 17)).doubleValue(),
+                                                           (double)0
+                                                           );
+                nd.jsFunction_setTime(NativeDate.internalUTC(date));
+                objects.setElementAt(nd, objects.size() - 1);
+
+            } catch (Exception e) {
+                if (Log.on) Log.log(this, "error parsing date : " + s);
+                if (Log.on) Log.log(this, e);
+            }
+
+        } else if (name.equals("member")) {
+            Object memberValue = objects.elementAt(objects.size() - 1);
+            String memberName = (String)objects.elementAt(objects.size() - 2);
+            Scriptable struct = (Scriptable)objects.elementAt(objects.size() - 3);
+            struct.put(memberName, struct, memberValue);
+            objects.setSize(objects.size() - 2);
+
+        } else if (name.equals("data")) {
+            int i;
+            for(i=objects.size() - 1; objects.elementAt(i) != null; i--);
+            Object[] arr = new Object[objects.size() - i - 1];
+            for(int j = i + 1; j<objects.size(); j++) arr[j - i - 1] = objects.elementAt(j);
+            objects.setElementAt(Context.enter().newArray(org.xwt.util.JSObject.defaultObjects, arr), i);
+            objects.setSize(i + 1);
+
+        }
+
+        content.reset();
+    }
+
+    public void content(char[] ch, int start, int length, int line, int col) {
+        try { content.write(ch, start, length); }
+        catch (Exception e) { 
+            if (Log.on) Log.log(this, "Exception in XMLRPC.content() -- this should never happen");
+            if (Log.on) Log.log(this, e);
+        }
+    }
+
+    // Methods to make outbound XML-RPC request ///////////////////////////////////////////////////
+
+    /** Appends the XML-RPC representation of <code>o</code> to <code>sb</code> */
+    void appendObject(Object o, StringBuffer sb) throws JavaScriptException {
+
+        if (o == null) {
+            throw new JavaScriptException("attempted to send a null value via XML-RPC");
+
+        } else if (o instanceof Number) {
+            if ((double)((Number)o).intValue() == ((Number)o).doubleValue()) {
+                sb.append("                <value><i4>");
+                sb.append(((Number)o).intValue());
+                sb.append("</i4></value>\n");
+            } else {
+                sb.append("                <value><double>");
+                sb.append(o);
+                sb.append("</double></value>\n");
+            }
+
+        } else if (o instanceof Boolean) {
+            sb.append("                <value><boolean>");
+            sb.append(((Boolean)o).booleanValue() ? "1" : "0");
+            sb.append("</boolean></value>\n");
+
+        } else if (o instanceof String) {
+            sb.append("                <value><string>");
+            String s = (String)o;
+            if (s.indexOf('<') == -1 && s.indexOf('&') == -1) {
+                sb.append(s);
+            } else {
+                char[] cbuf = s.toCharArray();
+                int oldi = 0, i=0;
+                while(true) {
+                    while(i < cbuf.length && cbuf[i] != '<' && cbuf[i] != '&') i++;
+                    sb.append(cbuf, oldi, i - oldi);
+                    if (i >= cbuf.length) break;
+                    if (cbuf[i] == '<') sb.append("&lt;");
+                    else if (cbuf[i] == '&') sb.append("&amp;");
+                    i = oldi = i + 1;
+                    if (i >= cbuf.length) break;
+                }
+            }
+            sb.append("</string></value>\n");
+
+        } else if (o instanceof NativeArray) {
+            if (tracker.get(o) != null) throw new JavaScriptException("attempted to send multi-ref data structure via XML-RPC");
+            tracker.put(o, Boolean.TRUE);
+            sb.append("                <value><array><data>\n");
+            NativeArray na = (NativeArray)o;
+            for(int i=0; i<na.jsGet_length(); i++)
+                appendObject(na.get(i, na), sb);
+            sb.append("                </data></array></value>\n");
+
+        } else if (o instanceof Scriptable && !(o instanceof Undefined)) {
+            if (tracker.get(o) != null) throw new JavaScriptException("attempted to send multi-ref data structure via XML-RPC");
+            tracker.put(o, Boolean.TRUE);
+            Scriptable s = (Scriptable)o;
+            sb.append("                <value><struct>\n");
+            Object[] ids = s.getIds();
+            for(int i=0; i<ids.length; i++) {
+                sb.append("                <member><name>" + ids[i] + "</name>\n");
+                appendObject(s.get(ids[i].toString(), s), sb);
+                sb.append("                </member>\n");
+            }
+            sb.append("                </struct></value>\n");
+
+        } else {
+            throw new JavaScriptException("attempt to send object of type " + o.getClass().getName() + " via XML-RPC");
+
+        }
+    }
+
+    private Object connect(Object[] args) throws JavaScriptException, IOException {
+        if (filename == null) {
+            filename = url.getFile();
+            host = url.getHost();
+            port = url.getPort();
+
+            InetAddress addr;
+            try { addr = InetAddress.getByName(host); }
+            catch (UnknownHostException uhe) { throw new JavaScriptException("could not resolve hostname \"" + host + "\""); }
+            byte[] quadbyte = addr.getAddress();
+            
+            if (quadbyte[0] == 10 ||
+                (quadbyte[0] == 192 && quadbyte[1] == 168) ||
+                (quadbyte[0] == 172 && (quadbyte[1] & 0xF0) == 16) &&
+                !addr.equals(Main.originAddr)) {
+                filename = null;
+                throw new JavaScriptException("security violation: " + host + " [" + addr.getHostAddress() + "] is in a firewalled netblock");
+            }
+        }
+
+        Socket s;
+        if (ssl) s = Platform.getSocket(host, port == -1 ? 443 : port, true);
+        else if (url.getProtocol().equals("http")) s = Platform.getSocket(host, port == -1 ? 80 : port, false);
+        else throw new JavaScriptException("only http[s] is supported");
+
+        s.setTcpNoDelay(true);
+        OutputStream os = new BufferedOutputStream(s.getOutputStream(), 4000);
+        InputStream is = new BufferedInputStream(new Filter(s.getInputStream()));
+        
+        PrintWriter ps;
+        if (!Log.verbose) ps = new PrintWriter(os);
+        else ps = new PrintWriter(new FilterWriter(new OutputStreamWriter(os)) {
+                public void write(int i) throws IOException {
+                    super.write(i);
+                    if (Log.on) Log.log(this, "send: " + ((char)i));
+                }
+                public void write(String s, int start, int len) throws IOException {
+                    super.write(s, start, len);
+                    if (Log.on) Log.log(this, "send: " + s.substring(start, start + len));
+                }
+                public void write(char[] c, int start, int len) throws IOException {
+                    super.write(c, start, len);
+                    if (Log.on) Log.log(this, "send: " + new String(c, start, len));
+                }
+            });
+        
+        BufferedReader br;
+        if (!Log.verbose) br = new BufferedReader(new InputStreamReader(is));
+        else br = new BufferedReader(new FilterReader(new InputStreamReader(is)) {
+                public int read() throws IOException {
+                    int i = super.read();
+                    if (Log.on) Log.log(this, "recv: " + ((char)i));
+                    return i;
+                }
+                public int read(char[] c, int off, int len) throws IOException {
+                    int ret = super.read(c, off, len);
+                    if (ret == -1) return ret;
+                    if (Log.on) Log.log(this, "recv: " + new String(c, off, ret));
+                    return ret;
+                }
+            });
+        
+        if (Log.verbose) Log.log(this, "call to " + url + " : " + methodname);
+        return send(args, br, ps);
+    }
+
+    protected Object send(Object[] args, BufferedReader br, PrintWriter ps) throws JavaScriptException, IOException {
+
+        // Construct payload
+        StringBuffer content = new StringBuffer();
+        content.append("<?xml version=\"1.0\"?>\n");
+        content.append("    <methodCall>\n");
+        content.append("        <methodName>");
+        content.append(methodname);
+        content.append("</methodName>\n");
+        if (args.length > 0) {
+            content.append("        <params>\n");
+            for(int i=0; i<args.length; i++) {
+                content.append("            <param>\n");
+                appendObject(args[i], content);
+                content.append("            </param>\n");
+            }
+            content.append("        </params>\n");
+        }
+        content.append("    </methodCall>");
+        
+        // Header
+        ps.print("POST " + filename + " HTTP/1.0\r\n");
+        ps.print("Host: " + host + "\r\n");
+        ps.print("User-Agent: XWT (http://www.xwt.org/)\r\n");
+        ps.print("Content-Type: text/xml\r\n");
+        ps.print("Content-length: " + (content.length() + 1) + "\r\n");
+        ps.print("\r\n");
+        ps.print(content);
+        ps.print("\n");
+        ps.flush();
+
+        // throw away HTTP reply headers
+        while(!br.readLine().equals("")) { }
+
+        // parse XML reply
+        try {
+            parse(br);
+        } catch (XML.SAXException e) {
+            if (Log.on) Log.log(this, "reply from server was not well-formed XML: " + e);
+            throw new JavaScriptException("reply from server was not well-formed XML: " + e);
+        }
+        
+        if (fault) throw new JavaScriptException(objects.elementAt(0));
+        
+        if (objects.size() == 0) return null;
+        return objects.elementAt(0);
+
+    }
+
+    public final Object call(Context cx, Scriptable scope, Scriptable thisObj, java.lang.Object[] args) throws JavaScriptException {
+
+        if (tracker == null) tracker = new Hash();
+        else tracker.clear();
+
+        if (objects == null) objects = new Vec();
+        else objects.setSize(0);
+
+        // put ourselves in the background
+        Thread thread = Thread.currentThread();
+        if (!(thread instanceof ThreadMessage)) {
+            if (Log.on) Log.log(this, "RPC calls may only be made from background threads");
+            return null;
+        }
+        ThreadMessage mythread = (ThreadMessage)thread;
+        mythread.setPriority(Thread.MIN_PRIORITY);
+        mythread.done.release();
+
+        try {
+            return connect(args);
+
+        } catch (IOException se) {
+            if (Log.on) Log.log(this, se);
+            if (Log.on) Log.log(this, " at " + cx.interpreterSourceFile + ":" + cx.interpreterLine);
+            throw new JavaScriptException("socket exception: " + se);
+
+        } catch (JavaScriptException jse) {
+            Object val = jse.getValue();
+            if (val instanceof String) {
+                if (Log.on) Log.log(this, val.toString());
+                if (Log.on) Log.log(this, " at " + cx.interpreterSourceFile + ":" + cx.interpreterLine);
+            }
+            throw jse;
+
+        } finally {
+            // okay, let ourselves be brought to the foreground
+            MessageQueue.add(mythread);
+            mythread.setPriority(Thread.NORM_PRIORITY);
+            mythread.go.block();
+        }
+
+    }
+
+    /** When you get a property from an XMLRPC, it just returns another XMLRPC with the property name tacked onto methodname. */
+    public Object get(String name, Scriptable start) {
+        return new XMLRPC(url.toString(), (methodname.equals("") ? "" : methodname + ".") + name, ssl);
+    }
+
+    public XMLRPC(String urlstr, String methodname) { this(urlstr, methodname, false); }
+    public XMLRPC(String urlstr, String methodname, boolean ssl) {
+        this.ssl = ssl;
+        try {
+            if (urlstr.startsWith("https:")) {
+                urlstr = "http" + urlstr.substring(5);
+                this.ssl = true;
+            }
+            URL url = new URL(urlstr);
+            if (methodname == null) methodname = "";
+            this.methodname = methodname;
+            this.url = url;
+
+        } catch (MalformedURLException e) {
+            if (Log.on) Log.log(this, e);
+
+        }
+    }
+
+    // Helper Classes ///////////////////////////////////////////////////////////////////////////////////
+
+    /** CharArrayWriter that lets us touch its buffer */
+    protected static class AccessibleCharArrayWriter extends CharArrayWriter {
+        public char[] getBuf() { return buf; }
+        public AccessibleCharArrayWriter(int i) { super(i); }
+    }
+
+    /** private filter class to make sure that network transfers don't interfere with UI responsiveness */
+    private static class Filter extends FilterInputStream {
+        public Filter(InputStream is) { super(is); }
+        public int read() throws IOException {
+            Thread.yield();
+            while(MessageQueue.working) try { Thread.sleep(100); } catch (Exception e) { };
+            return super.read();
+        }
+        public int read(byte[] b) throws IOException {
+            Thread.yield();
+            while(MessageQueue.working) try { Thread.sleep(100); } catch (Exception e) { };
+            return super.read(b);
+        }
+        public int read(byte[] b, int i, int j) throws IOException {
+            Thread.yield();
+            while(MessageQueue.working) try { Thread.sleep(100); } catch (Exception e) { };
+            return super.read(b, i, j);
+        }
+    }
+
+    // Methods Required by Rhino ////////////////////////////////////////////////////////
+
+    public String getClassName() { return "XMLRPC"; }
+    public Scriptable construct(Context cx, Scriptable scope, java.lang.Object[] args) { return null; }
+    public void delete(String name) { }
+    public Scriptable getParentScope() { return null; }
+    public void setParentScope(Scriptable p) { }
+    public boolean hasInstance(Scriptable value) { return false; }
+    public Scriptable getPrototype() { return null; }
+    public void setPrototype(Scriptable p) { }
+    public void delete(int i) { }
+    public Object getDefaultValue(Class hint) { return "XML-RPC"; }
+    public void put(int i, Scriptable start, Object value) { }
+    public Object get(int i, Scriptable start) { return null; }
+    public void put(String name, Scriptable start, Object value) { }
+    public boolean has(String name, Scriptable start) { return true; }
+    public boolean has(int i, Scriptable start) { return false; }
+    public Object[] getIds() { return new Object[] { }; }
+
+}
diff --git a/src/org/xwt/XWF.java b/src/org/xwt/XWF.java
new file mode 100644 (file)
index 0000000..7b6f8e2
--- /dev/null
@@ -0,0 +1,222 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import java.io.*;
+import java.util.*;
+import org.xwt.util.*;
+
+/** encapsulates a single XWF font */
+public class XWF {
+
+    /** all instances of XWF */
+    private static Hash xwtfonts = new Hash();
+
+    /** the int array comprising the XWF */
+    int[] data = null;
+
+    /** the width of the XWF's PNG */
+    int w = 0;
+
+    /** the height of the XWF's PNG */
+    int h = 0;
+
+    /** the full resource name of this font */
+    String name = null;
+
+    /** minimum descent of all glyphs */
+    int maxascent = 0;
+
+    /** maximum descent of all glyphs */
+    int maxdescent = 0;
+
+    /** hash containing all Picture instances created, keyed on an Integer object containing the argb color of the font */
+    Hash pictures = new Hash();
+
+    /** each element corresponds to a single glyph; <br>
+     *  metrics[glyphnum][0] == x position of left edge of glyph<br>
+     *  metrics[glyphnum][1] == x position of right edge of glyph<br>
+     *  metrics[glyphnum][2] == y position of top edge of glyph<br>
+     *  metrics[glyphnum][3] == y position of bottom edge of glyph<br>
+     *  metrics[glyphnum][4] == advance amount<br>
+     *  metrics[glyphnum][5] == baseline
+     */
+    int[][] metrics = null;
+
+    /** drop all cached fonts when the theme mapping changes */
+    public static void flushXWFs() { xwtfonts.clear(); }
+
+    /** retrieve an XWF instance, creating it if needed */
+    public static XWF getXWF(String resourcename) {
+        XWF ret = (XWF)xwtfonts.get(resourcename);
+        if (ret != null) return ret;
+
+        String resolved = Resources.resolve(resourcename + ".xwf", null);
+        byte[] bytes = Resources.getResource(resolved);
+        if (bytes != null) {
+            PNG png = PNG.decode(new ByteArrayInputStream(bytes), resourcename);
+            if (png != null) return new XWF(resourcename, png);
+        }
+        return null;
+    }
+
+    public int getMaxAscent() { return maxascent; }
+    public int getMaxDescent() { return maxdescent; }
+
+    /** draws <tt>text</tt> on <tt>buf</tt> in this font, with color <tt>argb</tt> */
+    public void drawString(DoubleBuffer buf, String text, int x, int y, int argb) {
+
+        Integer color = new Integer(argb | 0xFF000000);
+        Picture pg = (Picture)pictures.get(color);
+        if (pg == null) {
+            for(int i=0; i<data.length; i++)
+                data[i] = (data[i] & 0xFF000000) | (argb & 0x00FFFFFF);
+            pg = Platform.createPicture(data, w, h);
+            pictures.put(color, pg);
+        }
+
+        int left = x;
+        for(int i=0; i<text.length(); i++) {
+            int c = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".indexOf(text.charAt(i));
+            if (c == -1 || metrics[c] == null) { left += metrics[64][4]; continue; }
+            buf.drawPicture(pg,
+                            left,
+                            y - (metrics[c][5] - metrics[c][2]),
+                            left + metrics[c][1] - metrics[c][0],
+                            y - (metrics[c][5] - metrics[c][2]) + metrics[c][3] - metrics[c][2],
+                            metrics[c][0], metrics[c][2], metrics[c][1], metrics[c][3]);
+            
+            left += metrics[c][4];
+        }
+    }
+
+    /** returns the width of <tt>text</tt> when rendered in this font */
+    public int stringWidth(String text) {
+        int ret = 0;
+        for(int i=0; i<text.length(); i++) {
+            // what a hack...
+            int c = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".indexOf(text.charAt(i));
+            if (c == -1 || metrics[c] == null) { ret += metrics[64][4]; continue; }
+            ret += metrics[c][4];
+        }
+        return ret;
+    }
+
+    private XWF(String name, PNG png) {
+        this.name = name;
+        xwtfonts.put(name, this);
+
+        data = png.getData();
+        w = png.getWidth();
+        h = png.getHeight();
+
+        int x, y, y1;
+        boolean breakout;
+
+        int start_x, start_y, end_x, end_y, advance;
+
+        int[] rows = new int[120];
+
+        int baseline = 0;
+
+        metrics = new int[96][];
+        int numglyphs = 0;
+
+        for(y=0; y<h; y++) {
+
+            // search for the next non-empty row
+            for(breakout = false; y<h && !breakout; y++)
+                for(x = 0; x<w; x++)
+                    if ((data[x + y * w] & 0x00FFFFFF) != 0x00FFFFFF) breakout = true;
+
+            start_y = y - 2;
+
+            // search for the next empty row
+            for(breakout = false; y<h && !breakout; y++)
+                for(x = 0, breakout = true; x<w; x++)
+                    if ((data[x + y * w] & 0x00FFFFFF) != 0x00FFFFFF) breakout = false;
+
+            end_y = y;
+
+            if (start_y == end_y) continue;
+
+            for(x=0; x<w; x++) {
+
+                // search for the next column with a non-grayscale pixel in it
+                for(breakout = false; x<w && !breakout; x++)
+                    for(y1 = start_y; y1<end_y; y1++)
+                        if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
+                            Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
+                            breakout = true;
+                            baseline = y1;
+                        }
+                
+                // search for the next column without a non-grayscale pixel in it
+                for(breakout = false; x<w && !breakout; x++)
+                    for(y1 = start_y, breakout = true; y1<end_y; y1++)
+                        if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
+                            Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
+                            breakout = false;
+                        }
+
+                x--;
+                start_x = x;
+
+                // search for the next column with a non-grayscale pixel in it
+                for(breakout = false; x<w && !breakout; x++)
+                    for(y1 = start_y; y1<end_y; y1++)
+                        if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
+                            Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
+                            breakout = true;
+                            baseline = y1;
+                        }
+                
+                x--;
+                advance = x - start_x;
+
+                // search for the next column without a grayscale pixel in it
+                for(breakout = false; x<w && !breakout; x++)
+                    for(y1 = start_y, breakout = true; y1<end_y; y1++)
+                        if (Math.abs(((data[x + y1 * w] & 0x00FF0000) >> 16) - ((data[x + y1 * w] & 0x0000FF00) >> 8)) > 10 ||
+                            Math.abs(((data[x + y1 * w] & 0x0000FF00) >> 8) - ((data[x + y1 * w] & 0x000000FF))) > 10) {
+                            breakout = false;
+                        }
+
+                x--;
+                end_x = x;
+
+                if (start_x == end_x) break;
+
+                metrics[numglyphs] = new int[6];
+                metrics[numglyphs][0] = start_x;
+                metrics[numglyphs][1] = end_x;
+                metrics[numglyphs][2] = start_y;
+                metrics[numglyphs][3] = end_y;
+                metrics[numglyphs][4] = advance;
+                metrics[numglyphs][5] = baseline;
+                numglyphs++;
+
+                if (numglyphs >= metrics.length) break;
+
+            }
+
+            if (numglyphs >= metrics.length) break;
+        }
+
+        for(int i=0; i<data.length; i++)
+            if (Math.abs(((data[i] & 0x00FF0000) >> 16) - ((data[i] & 0x0000FF00) >> 8)) > 10 ||
+                Math.abs(((data[i] & 0x0000FF00) >> 8) - ((data[i] & 0x000000FF))) > 10)
+                data[i] = 0x00;
+            else
+                data[i] = (0xFF - (data[i] & 0xFF)) << 24;
+
+        for(int i=33; i<=126; i++)
+            if (metrics[i - 33] != null)
+                maxascent = Math.max(maxascent, metrics[i - 33][5] - metrics[i - 33][2]);
+
+        for(int i=33; i<=126; i++)
+            if (metrics[i - 33] != null)
+                maxdescent = Math.max(maxdescent, metrics[i - 33][3] - metrics[i - 33][5]);
+       
+    }
+
+}
diff --git a/src/org/xwt/XWT.java b/src/org/xwt/XWT.java
new file mode 100644 (file)
index 0000000..fb958e4
--- /dev/null
@@ -0,0 +1,204 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import java.io.*;
+import java.net.*;
+import java.text.*;
+import java.util.*;
+import org.xwt.util.*;
+import org.mozilla.javascript.*;
+
+/** Singleton class that provides all functionality in the xwt.* namespace */
+public final class XWT extends JSObject {
+
+    public static final XWT singleton = new XWT();
+    public String getClassName() { return "XWT"; }
+    private XWT() { setSeal(true); }
+
+    public Object get(String name, Scriptable start) {
+        if (name == null) return null;
+        else if (name.equals("maxdim")) return new Integer(Short.MAX_VALUE);
+        else if (name.equals("parseFloat")) return JSObject.defaultObjects.get("parseFloat", null);
+        else if (name.equals("parseInt")) return JSObject.defaultObjects.get("parseInt", null);
+        else if (name.equals("alt")) return Surface.alt ? Boolean.TRUE : Boolean.FALSE;
+        else if (name.equals("control")) return Surface.control ? Boolean.TRUE : Boolean.FALSE;
+        else if (name.equals("shift")) return Surface.shift ? Boolean.TRUE : Boolean.FALSE;
+        else if (name.equals("date")) return date;
+        else if (name.equals("listfonts")) return listfonts;
+        else if (name.equals("regexp")) return regexp;
+        else if (name.equals("sleep")) return sleep;
+        else if (name.equals("yield")) return yield;
+        else if (name.equals("textwidth")) return textwidth;
+        else if (name.equals("textheight")) return textheight;
+        else if (name.equals("newBox")) return newBox;
+        else if (name.equals("soap")) return soap;
+        else if (name.equals("xmlrpc")) return xmlrpc;
+        else if (name.equals("clipboard")) return Platform.getClipBoard();
+        else if (name.equals("altKeyName")) return Platform.altKeyName();
+        else if (name.equals("static")) return Static.getStatic("");
+        else if (name.equals("theme")) return theme;
+        else if (name.equals("button")) {
+            if (Surface.button1 && !Surface.button2 && !Surface.button3) return new Integer(1);
+            else if (!Surface.button1 && Surface.button2 && !Surface.button3) return new Integer(1);
+            else if (!Surface.button1 && !Surface.button2 && Surface.button3) return new Integer(1);
+            else return new Integer(0);
+        }
+        else if (name.equals("println")) return println;
+        else if (name.equals("math")) return org.xwt.util.JSObject.defaultObjects.get("Math", null);
+        else return super.get(name, start);
+    }
+
+    public void put(String name, Scriptable start, Object value) {
+        if (name == null) return;
+        else if (name.equals("thread") && value != null && value instanceof Function) ThreadMessage.newthread((Function)value);
+        else if (name.equals("clipboard")) Platform.setClipBoard(value.toString());
+        else super.put(name, start, value);
+    }
+
+
+    // JSFunction Instances ///////////////////////////////////////////////////////////////////
+
+    /** Helper class for defining functions. */
+    private static abstract class JSFunction extends JSObject implements Function {
+        JSFunction() { setSeal(true); }
+        public Scriptable construct(Context cx, Scriptable scope, java.lang.Object[] args) { return null; }
+    }
+
+    private static final JSFunction yield = new JSFunction() {
+            public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                sleep.call(cx, null, null, null);
+                return null;
+            }
+        };
+
+    private static final JSFunction println = new JSFunction() {
+            public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                if (args.length == 1)
+                    if (Log.on)
+                        Log.log(cx.interpreterSourceFile, args[0] == null ? "null" : args[0].toString());
+                return null;
+            }
+        };
+
+    private static final JSFunction date = new JSFunction() {
+            public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                try { return Context.enter().newObject(org.xwt.util.JSObject.defaultObjects, "Date", args);
+                } catch (Exception e) {
+                    if (Log.on) Log.log(this, "Exception in Context.newObject() -- this should never happen");
+                    if (Log.on) Log.log(this, e);
+                    return null;
+                }
+            }
+        };
+
+    private static final JSFunction regexp = new JSFunction() {
+            public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                try { return Context.enter().newObject(org.xwt.util.JSObject.defaultObjects, "Regexp", args);
+                } catch (Exception e) {
+                    if (Log.on) Log.log(this, "Exception in Context.newObject() -- this should never happen");
+                    if (Log.on) Log.log(this, e);
+                    return null;
+                }
+            }
+        };
+
+    private static final JSFunction listfonts = new JSFunction() {
+            public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                return Context.enter().newArray(org.xwt.util.JSObject.defaultObjects,  Platform.listFonts());
+            }
+        };
+
+    private static final JSFunction theme = new JSFunction() {
+            public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                if (args.length != 2) return null;
+                if (args[0] == null || args[1] == null) return null;
+                Template.retheme(args[0].toString(), args[1].toString());
+                return null;
+            }
+        };
+
+    private static final JSFunction xmlrpc = new JSFunction() {
+            public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                if (args.length != 1 || args[0] == null) return null;
+                return new XMLRPC(args[0].toString(), "");
+            }
+        };
+
+    private static final JSFunction soap = new JSFunction() {
+            public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                if (args.length == 1 && args[0] != null) return new SOAP(args[0].toString(), "", null, null);
+                else if (args.length == 2 && args[0] != null && args[1] != null)
+                    return new SOAP(args[0].toString(), "", args[1].toString(), null);
+                else if (args.length == 3 && args[0] != null && args[1] != null && args[2] != null)
+                    return new SOAP(args[0].toString(), "", args[1].toString(), args[2].toString());
+                else return null;
+            }
+        };
+
+    private static final JSFunction textwidth = new JSFunction() {
+            public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) throws JavaScriptException {
+                if (args.length < 1 || args.length > 2) return null;
+                if (args[0] == null || (args.length == 2 && args[1] == null)) return null;
+                String font = args.length == 1 ? Platform.getDefaultFont() : args[0].toString();
+                String text = args.length == 1 ? args[0].toString() : args[1].toString();
+                XWF xwf = XWF.getXWF(font);
+                if (xwf == null) return new Integer(Platform.stringWidth(font, text));
+                else return new Integer(xwf.stringWidth(text));
+            }
+        };
+
+
+    private static final JSFunction textheight = new JSFunction() {
+            public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                if (args.length > 1) return null;
+                if (args.length == 1 && args[0] == null) return null;
+                String font = args.length == 0 || args[0] == null ? Platform.getDefaultFont() : args[0].toString();
+                XWF xwf = XWF.getXWF(font);
+                if (xwf == null) return new Integer(Platform.getMaxAscent(font) + Platform.getMaxDescent(font));
+                else return new Integer(xwf.getMaxAscent() + xwf.getMaxDescent());
+            }
+        };
+
+    private static final JSFunction newBox = new JSFunction() {
+            public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                Box ret = new Box(args.length == 0 || args[0] == null ? "box" : args[0].toString(), Template.defaultImportList);
+                for(int i=1; i<args.length; i++)
+                    if (args[i] instanceof Box)
+                        ret.put(ret.numChildren(), null, (Box)args[i]);
+                for(int i=1; i<args.length; i++)
+                    if (args[i] instanceof Scriptable && !(args[i] instanceof Box)) {
+                        Scriptable s = (Scriptable)args[i];
+                        Object[] keys = s.getIds();
+                        for(int j=0; j<keys.length; j++) ret.put(keys[j].toString(), null, s.get(keys[j].toString(), s));
+                    }
+                return ret;
+            }
+        };
+
+    private static final JSFunction sleep = new JSFunction() {
+            public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                if (args != null && (args.length != 1 || args[0] == null)) return null;
+                int i = args == null ? 0 : SpecialBoxProperty.stoi(args[0].toString());
+
+                Thread thread = Thread.currentThread();
+                if (!(thread instanceof ThreadMessage)) {
+                    if (Log.on) Log.log(this, "cannot sleep() or yield() in the foreground thread");
+                    return null;
+                }
+                ThreadMessage mythread = (ThreadMessage)thread;
+                mythread.done.release();
+
+                if (i > 0) try { Thread.sleep(i); } catch (Exception e) { }
+                
+                MessageQueue.add(mythread);
+                mythread.go.block();
+                return null;
+            }
+        };
+
+}
+
+
+
+
+
diff --git a/src/org/xwt/package.html b/src/org/xwt/package.html
new file mode 100644 (file)
index 0000000..65c3d91
--- /dev/null
@@ -0,0 +1,40 @@
+
+<body>
+
+<p>
+The core XWT engine classes.
+</p>
+
+<h3>Interfacing with other code</h3>
+
+<p>
+This engine is not meant to be used as a library or to interface with
+any other code. Because of this, all classes and methods have package
+(nonpublic) access, except where interpackage calls or reflection
+demand that they be public. Authors of other software should NEVER
+depend on any of these APIs.
+</p>
+
+<h3>An important note about Threads</h3>
+
+<p>
+All operations are single-threaded, except sleep(), yield(), xmlrpc
+calls, and MessageQueue message loop. That means that while one thread
+is rendering or executing a script, no other thread can be rendering
+or executing a script.
+</p>
+
+<p>
+For performance reasons, we do <i>not</i> enforce this
+single-threadedness with Java synchronization primitives. If you
+change the engine code, be very careful not to violate this
+invariant. In general, you should only render or execute JavaScripts
+if (Thread.currentThread() == MessageQueue.singleton). The only
+exception is instances of ThreadMessage -- they include logic to block
+the MessageQueue thread on a semaphore, execute some javascript, and
+unblock the MessageQueue thread once the javascript has returned or
+performed some blocking operation. Be especially careful not to
+manipulate instances of Box from within the AWT thread.  @see
+MessageQueue ThreadMessage
+</p>
+
diff --git a/src/org/xwt/plat/AWT.java b/src/org/xwt/plat/AWT.java
new file mode 100644 (file)
index 0000000..6a55e92
--- /dev/null
@@ -0,0 +1,359 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt.plat;
+
+import org.xwt.*;
+import org.xwt.util.*;
+import org.mozilla.javascript.*;
+import java.net.*;
+import java.io.*;
+import java.util.*;
+import java.awt.*;
+import java.awt.datatransfer.*;
+import java.awt.image.*;
+import java.awt.event.*;
+import java.applet.*;
+
+/** Platform subclass for all VM's providing AWT 1.1 functionality */
+public class AWT extends Platform {
+
+    protected String getDescriptiveName() { return "Generic JDK 1.1+ with AWT"; }
+    protected DoubleBuffer _createDoubleBuffer(int w, int h, Surface owner) { return new AWTDoubleBuffer(w, h); }
+    protected Picture _createPicture(int[] b, int w, int h) { return new AWTPicture(b, w, h); }
+    protected int _getScreenWidth() { return Toolkit.getDefaultToolkit().getScreenSize().width; }
+    protected int _getScreenHeight() { return Toolkit.getDefaultToolkit().getScreenSize().height; }
+    protected Surface _createSurface(Box b, boolean framed) { return new AWTSurface(b, framed); }
+    protected int _stringWidth(String font, String text) { return getFont(font).metrics.stringWidth(text); }
+    protected int _getMaxAscent(String font) { return getFont(font).metrics.getMaxAscent(); }
+    protected int _getMaxDescent(String font) { return getFont(font).metrics.getMaxDescent(); }
+
+    protected String _getClipBoard() {
+        Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
+        if (cb == null) return null;
+        Transferable clipdata = cb.getContents(null);
+        try { return (String)clipdata.getTransferData(DataFlavor.stringFlavor); } catch (Exception ex) { return null; }
+    }
+
+    protected void _setClipBoard(String s) {
+        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+        if (clipboard == null) return;
+        StringSelection clipString = new StringSelection(s);
+        clipboard.setContents(clipString, clipString);
+    }
+
+    /** some platforms (cough, cough, NetscapeVM) have totally broken modifier masks; they will need to override this */
+    protected static int modifiersToButtonNumber(int modifiers) {
+        if ((modifiers & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) return 1;
+        if ((modifiers & InputEvent.BUTTON2_MASK) == InputEvent.BUTTON2_MASK) return 3;
+        if ((modifiers & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK) return 2;
+        return 0;
+    }
+
+    // Inner Classes /////////////////////////////////////////////////////////////////////////////////////
+
+    protected static class AWTPicture implements Picture {
+        public int getHeight() { return i.getHeight(null); }
+        public int getWidth() { return i.getWidth(null); } 
+        public int[] getData() { return data; }
+
+        int[] data = null;
+        public Image i = null;
+        private static MediaTracker mediatracker = new MediaTracker(new Canvas());
+        private static ColorModel cmodel = new DirectColorModel(32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
+        
+        public AWTPicture(int[] b, int w, int h) {
+            data = b;
+            Image img = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(w, h, cmodel, b, 0, w));
+            mediatracker.addImage(img, 1);
+            try { mediatracker.waitForAll(); } catch (InterruptedException e) { }
+            mediatracker.removeImage(img);
+            this.i = img;
+        }
+    }
+    
+    protected static class AWTDoubleBuffer implements DoubleBuffer {
+        
+        protected Image i = null;
+        protected Graphics g = null;
+        
+        /** JDK1.1 platforms require that a component be associated with each off-screen buffer */
+        static Component component = null;
+        static {
+             component = new Frame();
+             component.setVisible(false);
+             component.addNotify();
+        }
+
+        public AWTDoubleBuffer(int w, int h) {
+            i = component.createImage(w, h);
+            g = i.getGraphics();
+        }
+        
+        public int getHeight() { return i == null ? 0 : i.getHeight(null); }
+        public int getWidth() { return i == null ? 0 : i.getWidth(null); }
+        public void setClip(int x, int y, int x2, int y2) { g.setClip(x, y, x2 - x, y2 - y); }
+
+        public void drawPicture(Picture source, int x, int y) {
+            drawPicture(source, x, y, x + source.getWidth(), y + source.getHeight(), 0, 0, source.getWidth(), source.getHeight());
+        }
+
+        public void drawPicture(Picture source, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2) {
+            g.drawImage(((AWTPicture)source).i, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null);
+        }
+        
+        public void drawString(String font, String text, int x, int y, int argb) {
+            // FEATURE: use an LRU cache for Color objects
+            g.setColor(new Color((argb & 0x00FF0000) >> 16, (argb & 0x0000FF00) >> 8, (argb & 0x000000FF)));
+            g.setFont(getFont(font));
+            g.drawString(text, x, y + 2);
+        }
+        
+        public void fillRect(int x, int y, int x2, int y2, int argb) {
+            // FEATURE: use an LRU cache for Color objects
+            g.setColor(new Color((argb & 0x00FF0000) >> 16, (argb & 0x0000FF00) >> 8, (argb & 0x000000FF)));
+            g.fillRect(x, y, x2 - x, y2 - y);
+        }
+
+    }
+    
+    
+    protected static class AWTSurface extends Surface
+        implements MouseListener, MouseMotionListener, KeyListener, ComponentListener, WindowListener {
+
+        public void blit(DoubleBuffer s, int sx, int sy, int dx, int dy, int dx2, int dy2) {
+            if (ourGraphics == null) ourGraphics = window.getGraphics();
+            ourGraphics.drawImage(((AWTDoubleBuffer)s).i, dx + insets.left, dy + insets.top, dx2 + insets.left, dy2 + insets.top,
+                                  sx, sy, sx + (dx2 - dx), sy + (dy2 - dy), null);
+        }
+        
+        /** if (component instanceof Frame) then frame == window else frame == null */
+        Frame frame = null;
+        Window window = null;
+        
+        /** our component's insets */
+        protected Insets insets = new Insets(0, 0, 0, 0);
+        
+        /** a Graphics context on <code>window</code> */
+        protected Graphics ourGraphics = null;
+        
+        /** some JDKs let us recycle a single Dimension object when calling getSize() */
+        Dimension singleSize = new Dimension();
+        
+        public void toBack() { if (window != null) window.toBack(); }
+        public void toFront() { if (window != null) window.toFront(); }
+        public void setLocation(int x, int y) { window.setLocation(x, y); }
+        public void setTitleBarText(String s) { if (frame != null) frame.setTitle(s); }
+        public void setIcon(Picture i) { if (frame != null) frame.setIconImage(((AWTPicture)i).i); }
+        public void setSize(int width, int height) { window.setSize(width + (insets.left + insets.right), height + (insets.top + insets.bottom)); }
+        public void setInvisible(boolean b) { window.setVisible(!b); }
+        protected void _setMinimized(boolean b) { if (Log.on) Log.log(this, "JDK 1.1 platforms cannot minimize or unminimize windows"); }
+        protected void _setMaximized(boolean b) {
+            if (!b) {
+                if (Log.on) Log.log(this, "JDK 1.1 platforms cannot unmaximize windows");
+                return;
+            }
+            window.setLocation(new Point(0, 0));
+            window.setSize(Toolkit.getDefaultToolkit().getScreenSize());
+        }
+        
+        AWTSurface(Box root, boolean framed) {
+            super(root);
+            
+            if (framed) window = frame = new Frame() {
+                    public void update(Graphics gr) { paint(gr); }
+                    public void paint(Graphics gr) {
+                        Rectangle r = gr.getClipBounds();
+                        Dirty(r.x - insets.left, r.y - insets.top, r.width, r.height);
+                    } };
+            else window = new Window(new Frame()) {
+                    public void update(Graphics gr) { paint(gr); }
+                    public void paint(Graphics gr) {
+                        Rectangle r = gr.getClipBounds();
+                        Dirty(r.x - insets.left, r.y - insets.top, r.width, r.height);
+                    } };
+            
+            window.addMouseListener(this);
+            window.addKeyListener(this);
+            window.addComponentListener(this);
+            window.addMouseMotionListener(this);
+            window.addWindowListener(this);
+
+            // IMPORTANT: this must be called before render() to ensure
+            // that our peer has been created
+            window.setVisible(true);
+
+            insets = window.getInsets();
+        }
+        
+        public void _dispose() {
+            window.removeMouseListener(this);
+
+            // removed to work around a jdk1.3 bug
+            /* window.removeKeyListener(this); */
+
+            window.removeComponentListener(this);
+            window.removeMouseMotionListener(this);
+            window.removeWindowListener(this);
+            window.dispose();
+        }
+
+        public void syncCursor() {
+            if (cursor.equals("crosshair")) window.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
+            else if (cursor.equals("east")) window.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
+            else if (cursor.equals("move")) window.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+            else if (cursor.equals("north")) window.setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
+            else if (cursor.equals("northeast")) window.setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR));
+            else if (cursor.equals("northwest")) window.setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR));
+            else if (cursor.equals("south")) window.setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
+            else if (cursor.equals("southeast")) window.setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
+            else if (cursor.equals("southwest")) window.setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR));
+            else if (cursor.equals("text")) window.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
+            else if (cursor.equals("west")) window.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
+            else if (cursor.equals("wait")) window.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+            else if (cursor.equals("hand")) window.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+            else window.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+        }
+        
+        // AWT Message translation ////////////////////////////////////////////////////////////////
+        
+        // these functions are all executed in the AWT thread, not the
+        // MessageQueue thread. As a result, they must be *extremely*
+        // careful about invoking methods on instances of Box. Currently,
+        // they should only enqueue messages, use Box.whoIs()
+        // (unsynchronized but thought to be safe), and modify members of
+        // Surface.
+        
+        public void componentHidden(ComponentEvent e) { }
+        public void componentShown(ComponentEvent e) { }
+        public void windowOpened(WindowEvent e) { }
+        public void windowClosed(WindowEvent e) { }
+        public void windowClosing(WindowEvent e) { Close(); }
+        public void windowIconified(WindowEvent e) { Minimized(true); }
+        public void windowDeiconified(WindowEvent e) { dirty(0, 0, width, height); Minimized(false); }
+        public void windowActivated(WindowEvent e) { Focused(true); }
+        public void windowDeactivated(WindowEvent e) { Focused(false); }
+        public void componentMoved(ComponentEvent e) { PosChange(window.getLocation().x, window.getLocation().y); }
+        public void componentResized(ComponentEvent e) {
+            // we have to periodically do this; I don't know why
+            insets = window.getInsets();
+            SizeChange(window.getWidth(), window.getHeight());
+            ourGraphics = null;
+        }
+        
+        public void keyTyped(KeyEvent k) { }
+        public void keyPressed(KeyEvent k) { KeyPressed(translateKey(k)); }
+        public void keyReleased(KeyEvent k) { KeyReleased(translateKey(k)); }
+        public void mouseExited(MouseEvent m) { mouseMoved(m); }
+        public void mouseEntered(MouseEvent m) { mouseMoved(m); }
+        public void mouseDragged(MouseEvent m) { mouseMoved(m); }
+        public void mouseMoved(MouseEvent m) { Move(m.getX() - insets.left, m.getY() - insets.top); }
+        public void mousePressed(MouseEvent m) { Press(modifiersToButtonNumber(m.getModifiers())); }
+        public void mouseReleased(MouseEvent m) { Release(modifiersToButtonNumber(m.getModifiers())); }
+        public void mouseClicked(MouseEvent m) {
+            if (m.getClickCount() == 2) DoubleClick(modifiersToButtonNumber(m.getModifiers()));
+            else Click(modifiersToButtonNumber(m.getModifiers()));
+        }
+        
+        String translateKey(KeyEvent k) {
+            switch (k.getKeyCode()) {
+            case KeyEvent.VK_ALT: return "alt";
+            case KeyEvent.VK_BACK_SPACE: return "back_space";
+            case KeyEvent.VK_CONTROL: return "control";
+            case KeyEvent.VK_DELETE: return "delete";
+            case KeyEvent.VK_DOWN: return "down";
+            case KeyEvent.VK_END: return "end";
+            case KeyEvent.VK_ENTER: return "enter";
+            case KeyEvent.VK_ESCAPE: return "escape";
+            case KeyEvent.VK_F1: return "f1";
+            case KeyEvent.VK_F10: return "f10";
+            case KeyEvent.VK_F11: return "f11";
+            case KeyEvent.VK_F12: return "f12";
+            case KeyEvent.VK_F2: return "f2";
+            case KeyEvent.VK_F3: return "f3";
+            case KeyEvent.VK_F4: return "f4";
+            case KeyEvent.VK_F5: return "f5";
+            case KeyEvent.VK_F6: return "f6";
+            case KeyEvent.VK_F7: return "f7";
+            case KeyEvent.VK_F8: return "f8";
+            case KeyEvent.VK_F9: return "f9";
+            case KeyEvent.VK_HOME: return "home";
+            case KeyEvent.VK_INSERT: return "insert";
+            case KeyEvent.VK_LEFT: return "left";
+            case KeyEvent.VK_META: return "alt";
+            case KeyEvent.VK_PAGE_DOWN: return "page_down";
+            case KeyEvent.VK_PAGE_UP: return "page_up";
+            case KeyEvent.VK_PAUSE: return "pause";
+            case KeyEvent.VK_PRINTSCREEN: return "printscreen";
+            case KeyEvent.VK_RIGHT: return "right";
+            case KeyEvent.VK_SHIFT: return "shift";
+            case KeyEvent.VK_TAB: return "tab";
+            case KeyEvent.VK_UP: return "up";
+
+            // we special-case letters since (C-a).getKeyChar() != 'a'
+            case KeyEvent.VK_A: return "a";
+            case KeyEvent.VK_B: return "b";
+            case KeyEvent.VK_C: return "c";
+            case KeyEvent.VK_D: return "d";
+            case KeyEvent.VK_E: return "e";
+            case KeyEvent.VK_F: return "f";
+            case KeyEvent.VK_G: return "g";
+            case KeyEvent.VK_H: return "h";
+            case KeyEvent.VK_I: return "i";
+            case KeyEvent.VK_J: return "j";
+            case KeyEvent.VK_K: return "k";
+            case KeyEvent.VK_L: return "l";
+            case KeyEvent.VK_M: return "m";
+            case KeyEvent.VK_N: return "n";
+            case KeyEvent.VK_O: return "o";
+            case KeyEvent.VK_P: return "p";
+            case KeyEvent.VK_Q: return "q";
+            case KeyEvent.VK_R: return "r";
+            case KeyEvent.VK_S: return "s";
+            case KeyEvent.VK_T: return "t";
+            case KeyEvent.VK_U: return "u";
+            case KeyEvent.VK_V: return "v";
+            case KeyEvent.VK_W: return "w";
+            case KeyEvent.VK_X: return "x";
+            case KeyEvent.VK_Y: return "y";
+            case KeyEvent.VK_Z: return "z";
+            default: return String.valueOf(k.getKeyChar());
+            }
+        }
+    }
+
+    // Font Handling Stuff //////////////////////////////////////////////////////////
+
+    protected String[] _listFonts() { return fontList; }
+    private static String[] fontList;
+    static {
+        String[] awtfonts = Toolkit.getDefaultToolkit().getFontList();
+        fontList = new String[awtfonts.length * 4];
+        for(int i=0; i<awtfonts.length; i++) {
+            fontList[i * 4] = awtfonts[i] + "*";
+            fontList[i * 4 + 1] = awtfonts[i] + "*b";
+            fontList[i * 4 + 2] = awtfonts[i] + "*i";
+            fontList[i * 4 + 3] = awtfonts[i] + "*bi";
+        }
+    }
+
+    private static Hash fontCache = new Hash();
+    private static ParsedFont pf = new ParsedFont();
+    private static MetricatedFont getFont(String font) {
+        MetricatedFont ret = (MetricatedFont)fontCache.get(font);
+        if (ret == null) {
+            pf.parse(font);
+            if (pf.name.equals("tty")) pf.name = "monospace";
+            ret = new MetricatedFont(pf.name, (pf.bold ? Font.BOLD : 0) | (pf.italic ? Font.ITALIC : 0), pf.size);
+            fontCache.put(font, ret);
+        }
+        return ret;
+    }
+    
+    private static class MetricatedFont extends Font {
+        public FontMetrics metrics = null;
+        public MetricatedFont(String name, int size, int style) {
+            super(name, size, style);
+            metrics = Toolkit.getDefaultToolkit().getFontMetrics(this);
+        }
+    }
+            
+}
diff --git a/src/org/xwt/plat/GCJ.java b/src/org/xwt/plat/GCJ.java
new file mode 100644 (file)
index 0000000..34c89ea
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt.plat;
+
+import org.xwt.*;
+
+/** common superclass for all platforms that use GCJ to compile a native binary */
+public abstract class GCJ extends Platform {
+
+    // static references to these classes ensure that the linker will include them
+    private static Class c1 = gnu.java.locale.Calendar.class;
+    private static Class c2 = java.util.GregorianCalendar.class;
+    private static Class c3 = gnu.gcj.convert.Input_ASCII.class;
+    private static Class c4 = gnu.gcj.convert.Input_UTF8.class;
+    private static Class c5 = gnu.gcj.convert.Input_8859_1.class;
+    private static Class c6 = gnu.java.locale.LocaleInformation.class;
+    private static Class c7 = gnu.gcj.convert.Output_ASCII.class;
+
+    protected org.xwt.Weak _getWeak(Object o) { return new Java2Weak(o); }
+    private static class Java2Weak extends java.lang.ref.WeakReference implements org.xwt.Weak {
+        public Java2Weak(Object o) { super(o); }
+    }
+        
+}
+
diff --git a/src/org/xwt/plat/GCJ.xml b/src/org/xwt/plat/GCJ.xml
new file mode 100644 (file)
index 0000000..e1cd97d
--- /dev/null
@@ -0,0 +1,72 @@
+<!-- Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL] -->
+<!--
+
+    Subclasses of org.xwt.plat.GCJ should include a .xml file which
+    sets the following properties, then invokes <ant antfile="src/org/xwt/plat/GCJ.xml"/>
+
+        gcc-target    - the gcc target name to use (for example, i686-pc-mingw32)
+        linkflags     - extra flags to pass to gcj during the link phase, if any
+        binaryname    - the name to give to the final binary
+
+-->
+
+<project name="GCJ" default="build">
+
+    <target name="build">
+
+        <echo message='extracting .class -> .h'/>
+        <taskdef name='gcjh' classname='org.xwt.tasks.GCJHTask'>
+            <classpath path='bin/'/>                                                                               
+        </taskdef>
+        <gcjh out='bin-${plat}' classpath='bin'/>
+
+        <echo message='compiling   .java -> .o'/>
+        <apply failonerror='true' executable='${gcc-path}/bin/${gcc-target}-gcj' dest='bin-${plat}'>
+            <arg value='-fCLASSPATH=${gcc-path}/share/libgcj.jar:src/'/>
+            <arg value='-O9'/>
+            <arg value='-g'/>
+            <arg value='-Isrc/'/>
+            <arg value='-c'/>
+            <srcfile/>
+            <arg value='-o'/>
+            <targetfile/>
+            <fileset dir='src/'>
+                <include name='jazz/**/*.java'/>
+                <include name='org/xwt/*.java'/>
+                <include name='org/xwt/util/*.java'/>
+                <include name='org/xwt/plat/GCJ.java'/>
+                <include name='org/xwt/plat/${plat}.java'/>
+                <include name='org/bouncycastle/**/*.java'/>
+                <include name='org/mozilla/**/*.java'/>
+            </fileset>
+            <mapper type='glob' from='*.java' to='*.o'/>
+        </apply>
+
+        <echo message='compiling     .cc -> .o'/>
+            <apply failonerror='true' executable='${gcc-path}/bin/${gcc-target}-gcj' dest='bin-${plat}/'>
+            <arg value='-g'/>
+            <arg value='-fno-rtti'/>
+            <arg value='-Ibin-${plat}'/>
+            <arg value='-c'/>
+            <srcfile/>
+            <arg value='-o'/>
+            <targetfile/>
+            <fileset dir='src/' includes='org/xwt/plat/${plat}.cc'/>
+            <mapper type='glob' from='*.cc' to='*-nat.o'/>
+        </apply>
+                                                                              
+        <echo message='linking        .o -> ${binaryname}'/>
+        <apply failonerror='true' executable='${gcc-path}/bin/${gcc-target}-gcj' parallel='true'>
+            <fileset dir='bin-${plat}/' includes='**/*.o' excludes='*.o'/>
+            <arg value='-fCLASSPATH=${gcc-path}/share/libgcj.jar'/>
+            <arg value='--main=org.xwt.Main'/>
+            <arg line='-o www/html/dist/${binaryname}'/>
+            <srcfile/>
+            <arg line='${linkflags}'/>
+       </apply>
+
+    </target>
+
+</project>
+
+                                                                         
diff --git a/src/org/xwt/plat/Java2.java b/src/org/xwt/plat/Java2.java
new file mode 100644 (file)
index 0000000..f7451c3
--- /dev/null
@@ -0,0 +1,178 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt.plat;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import java.awt.datatransfer.*;
+import java.net.*;
+import java.io.*;
+import java.util.*;
+import org.xwt.util.*;
+import org.xwt.*;
+
+// FEATURE: Java 1.4 allows undecorated frames and can maximize windows
+
+/** Platform class for most reasonable Java1.2+ JVMs */
+public class Java2 extends AWT {
+
+    protected Socket __getSocket(String host, int port, boolean ssl) throws IOException { return super._getSocket(host, port, ssl); }
+    protected Socket _getSocket(final String host, final int port, final boolean ssl) throws IOException {
+        return (Socket)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() {
+                public Object run() {
+                    try {
+                        return __getSocket(host, port, ssl);
+                    } catch (Exception e) {
+                        if (Log.on) Log.log(Java2.class, "Error attempting to create socket");
+                        if (Log.on) Log.log(Java2.class, e);
+                        return null;
+                    }
+                }
+            });
+    }
+    
+    protected DoubleBuffer _createDoubleBuffer(int w, int h, Surface owner) { return new Java2DoubleBuffer(w, h); }
+    protected Surface _createSurface(final Box root, final boolean framed) {
+        return (Surface)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() {
+                public Object run() { return new Java2Surface(root, framed); }
+            });
+    }
+
+    // Inner Classes //////////////////////////////////////////////////////////////////
+
+    protected static class Java2Surface extends AWTSurface {
+        
+        public Java2Surface(Box root, boolean framed) { super(root, framed); }
+        public void blit(DoubleBuffer s, int sx, int sy, int dx, int dy, int dx2, int dy2) {
+            if (ourGraphics == null) ourGraphics = window.getGraphics();
+            _doDrawImage(ourGraphics, ((AWTDoubleBuffer)s).i, dx + insets.left, dy + insets.top, dx2 + insets.left, dy2 + insets.top,
+                         sx, sy, sx + (dx2 - dx), sy + (dy2 - dy), null);
+        }
+
+        protected void _setMinimized(boolean b) {
+            if (frame == null) {
+                if (Log.on) Log.log(this, "JDK 1.2 can only minimize frames, not windows");
+                return;
+            }
+            if (b) frame.setState(java.awt.Frame.ICONIFIED);
+            else frame.setState(java.awt.Frame.NORMAL);
+        }
+    }
+
+    protected static class Java2DoubleBuffer extends AWTDoubleBuffer {
+        private static ColorModel cm = Toolkit.getDefaultToolkit().getColorModel();
+        private static Hashtable emptyHashtable = new Hashtable();
+        private static short[] sbank = null;
+        private static int[] ibank = null;
+        private static byte[] bbank = null;
+        private static int bank_start = 0;
+        
+        public void drawPicture(Picture source, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2) {
+            _doDrawImage(g, ((AWTPicture)source).i, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null);
+        }
+        
+        public Java2DoubleBuffer(int w, int h) {
+            super(w, h);
+            SampleModel sm = cm.createCompatibleSampleModel(w, h);
+            DataBuffer buf = null;
+            if (sm.getDataType() == DataBuffer.TYPE_USHORT) {
+                if (sbank == null || w * h > 512 * 512 / 3) {
+                    buf = new DataBufferUShort(w * h);
+                } else {
+                    if (w * h > sbank.length - bank_start) {
+                        bank_start = 0;
+                        sbank = new short[512 * 512];
+                    }
+                    buf = new DataBufferUShort(sbank, w * h, bank_start);
+                    bank_start += w * h;
+                }
+            } else if (sm.getDataType() == DataBuffer.TYPE_BYTE) {
+                if (bbank == null || w * h > 512 * 512 / 3) {
+                    buf = new DataBufferByte(w * h);
+                } else {
+                    if (w * h > bbank.length - bank_start) {
+                        bank_start = 0;
+                        bbank = new byte[512 * 512];
+                    }
+                    buf = new DataBufferByte(bbank, w * h, bank_start);
+                    bank_start += w * h;
+                }
+            } else if (sm.getDataType() == DataBuffer.TYPE_INT) {
+                if (ibank == null || w * h > 512 * 512 / 3) {
+                    buf = new DataBufferInt(w * h);
+                } else {
+                    if (w * h > ibank.length - bank_start) {
+                        bank_start = 0;
+                        ibank = new int[512 * 512];
+                    }
+                    buf = new DataBufferInt(ibank, w * h, bank_start);
+                    bank_start += w * h;
+                }
+            }
+            i = new BufferedImage(cm, Raster.createWritableRaster(sm, buf, null), false,  emptyHashtable);
+            g = i.getGraphics();
+        }
+    }
+
+    /** used to avoid garbage creation with getClipBounds() */
+    private static Rectangle clipBounds = new Rectangle();
+    
+    // FEATURE: performance hits here are a result of getSubimage() -- if we don't call it, Graphics2D will. It creates tons of int[]s, as well as
+    // BufferedImage instances which get stored in an array deep inside Graphics2D, which the JDK does a LINEAR SCAN through. Aarrgh.
+    // This is rumored to be fixed in JDK 1.4.
+    protected static void _doDrawImage(Graphics g, Image i, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver o) {
+        
+        if (dx2 - dx1 != sx2 - sx1 || dy2 - dy1 != sy2 - sy1)
+            g.drawImage(i, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, o);
+        else {
+            // fastpath for images that do not need to be scaled
+            if (i instanceof BufferedImage) {
+                BufferedImage b = (BufferedImage)i;
+                
+                if (dx2 - dx1 != b.getWidth(null) || dy2 - dy1 != b.getHeight(null)) {
+                    b = b.getSubimage(sx1, sy1, sx2 - sx1, sy2 - sy1);
+                    sx2 = sx2 - sx1;
+                    sy2 = sy2 - sy1;
+                    sx1 = 0;
+                    sy1 = 0;
+                }
+                g.drawImage(b, dx1, dy1, o);
+                
+            } else {
+
+                // workaround for a wierd JDK bug
+                boolean skip = false;
+                try { g.getClipBounds(clipBounds); } catch (NullPointerException n) { skip = true; }
+
+                g.clipRect(dx1, dy1, dx2 - dx1, dy2 - dy1);
+                g.drawImage(i, dx1 - sx1, dy1 - sy1, o);
+
+                if (!skip) g.setClip(clipBounds.x, clipBounds.y, clipBounds.x + clipBounds.width, clipBounds.y + clipBounds.height);
+            }
+        }
+    }
+
+    protected org.xwt.Weak _getWeak(Object o) { return new Java2Weak(o); }
+    private static class Java2Weak extends java.lang.ref.WeakReference implements org.xwt.Weak {
+        public Java2Weak(Object o) { super(o); }
+    }
+
+    private String __getClipBoard() { return super._getClipBoard(); }
+    protected String _getClipBoard() {
+        return (String)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() {
+                public Object run() { return __getClipBoard();  }
+            });
+    }
+
+    private void __setClipBoard(String s) { super._setClipBoard(s); }
+    protected void _setClipBoard(final String s) {
+        java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() {
+                public Object run() {
+                    __setClipBoard(s);
+                    return null;
+                }
+            });
+    }
+
+    protected String getDescriptiveName() { return "Java 1.2+ JVM"; }
+}
diff --git a/src/org/xwt/plat/Java2.xml b/src/org/xwt/plat/Java2.xml
new file mode 100644 (file)
index 0000000..2af97c6
--- /dev/null
@@ -0,0 +1,33 @@
+<!-- Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL] -->
+
+<project name="Java2" default="run" basedir=".">
+
+    <target name="build"/>
+
+    <target name="run">
+        <java classname='org.xwt.Main'>
+            <classpath>
+                <pathelement path='bin/'/>
+                <fileset dir='lib'>
+                    <include name='**/*.jar'/>
+                </fileset>
+            </classpath>
+            <arg line='src org.xwt.demo.main'/>
+        </java>
+    </target>
+
+    <target name="dist">
+        <jar update="true" jarfile='www/html/dist/xwt.jar' basedir='bin/' includes=''>
+            <patternset>
+                <include name='org/xwt/*.class'/>
+                <include name='org/xwt/util/*.class'/>
+                <include name='org/mozilla/**/*.class'/>
+                <include name='org/bouncycastle/**/*.class'/>
+                <include name='jazz/**/*.class'/>
+                <include name='org/xwt/plat/AWT*.class'/>
+                <include name='org/xwt/plat/Java2*.class'/>
+                <include name='org/xwt/plat/MacOSX*.class'/>
+            </patternset>
+        </jar>
+    </target>
+
diff --git a/src/org/xwt/plat/Win32.cc b/src/org/xwt/plat/Win32.cc
new file mode 100644 (file)
index 0000000..c99ec19
--- /dev/null
@@ -0,0 +1,1117 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [LGPL]
+
+// kinda ugly; we use -DCOMPILE_DLL to combine two .cc files into one
+#ifndef COMPILE_DLL
+
+// this has to precede the others so we don't get collisions on min/max
+#include <org/xwt/Box.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <windows.h>
+#include <mmsystem.h>
+#undef STRICT
+
+#include <gcj/cni.h>
+
+#include <java/lang/Integer.h>
+#include <org/xwt/util/Hash.h>
+#include <org/xwt/Box.h>
+#include <org/xwt/Surface.h>
+#include <org/xwt/DoubleBuffer.h>
+#include <org/xwt/Picture.h>
+#include <org/xwt/Platform.h>
+#include <org/xwt/Platform$ParsedFont.h>
+#include <org/xwt/plat/Win32.h>
+#include <org/xwt/plat/Win32$Win32Font.h>
+#include <org/xwt/plat/Win32$Win32Surface.h>
+#include <org/xwt/plat/Win32$Win32DoubleBuffer.h>
+#include <org/xwt/plat/Win32$Win32Picture.h>
+#include <org/xwt/util/Semaphore.h>
+
+// for debugging
+#include <java/lang/System.h>
+#include <java/io/PrintStream.h>
+
+#define WM_USER_SETCURSOR WM_USER
+#define WM_USER_DISPOSE (WM_USER + 1)
+#define WM_USER_CREATEWINDOW (WM_USER + 2)
+#define WS_NORMAL (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
+
+// FEATURE: there are lots of places where HANDLE's get casted to jint's -- this will break on Win64
+//          a clean way to do this would be to '#define jraw (gnu::gcj::RawData*)'
+
+// Callbacks ////////////////////////////////////////////////////////////////////
+
+LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
+    org::xwt::plat::Win32$Win32Surface* surface =
+        (org::xwt::plat::Win32$Win32Surface*)org::xwt::plat::Win32::hwndToWin32SurfaceMap->get(new java::lang::Integer((jint)hwnd));
+
+    if (surface != NULL) {
+        return (LRESULT)surface->WndProc((jint)hwnd, (jint)iMsg, (jint)wParam, (jint)lParam);
+
+    } else {
+        // this is really lame -- Win32 insists on being able to call your WndProc BEFORE CreateWindow returns...
+        return DefWindowProc(hwnd, iMsg, wParam, lParam);
+    }
+}
+
+// This function iterates over each family (lparam == 0), and then over each size (lparam == 1)
+int CALLBACK fontproc(const LOGFONTA* enumlogfont, const TEXTMETRICA* tm, long unsigned int type, LPARAM lparam) {
+
+    if (lparam == 0) {
+        LOGFONT lf;
+        lf.lfCharSet = ANSI_CHARSET;
+        strncpy(lf.lfFaceName, enumlogfont->lfFaceName, 32);
+        lf.lfPitchAndFamily = 0;
+        EnumFontFamiliesEx((HDC)org::xwt::plat::Win32::desktop_dc, &lf, fontproc, 1, 0);
+
+    } else {
+        org::xwt::plat::Win32::addFont(JvNewStringLatin1(enumlogfont->lfFaceName),
+                                       ((type & RASTER_FONTTYPE) == 0) ? 0 : tm->tmHeight,
+                                       tm->tmItalic == 0 ? 0 : 1, 
+                                       tm->tmWeight <= 400 ? 0 : 1);
+    }
+    return 1;
+}
+
+
+// Initialization ////////////////////////////////////////////////////////////////////
+
+static int window_class_counter = 0;
+
+jstring org::xwt::plat::Win32::getTempPath() {
+    char buf[1024];
+    DWORD ret = GetTempPath(1024, buf);
+    if (ret == 0) criticalAbort(JvNewStringLatin1("GetTempPath() failed"));
+    return JvNewStringLatin1(buf);
+}
+
+// XOR mask for the hand cursor
+static unsigned char hand_cursor_xor[32 * 4] = {
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x0C, 0x00, 0x00,
+  0x00, 0x0C, 0x00, 0x00,
+  0x00, 0x0C, 0x00, 0x00,
+  0x00, 0x0C, 0x00, 0x00,
+  0x00, 0x0C, 0x00, 0x00,
+  0x00, 0x0D, 0x80, 0x00,
+  0x00, 0x0D, 0xB0, 0x00,
+  0x00, 0x0D, 0xB4, 0x00,
+  0x00, 0x0D, 0xB6, 0x00,
+  0x00, 0xCF, 0xF6, 0x00,
+  0x00, 0xEF, 0xFE, 0x00,
+  0x00, 0x6F, 0xFE, 0x00,
+  0x00, 0x2F, 0xFE, 0x00,
+  0x00, 0x3F, 0xFE, 0x00,
+  0x00, 0x1F, 0xFE, 0x00,
+  0x00, 0x1F, 0xFC, 0x00,
+  0x00, 0x0F, 0xFC, 0x00,
+  0x00, 0x0F, 0xFC, 0x00,
+  0x00, 0x07, 0xF8, 0x00,
+  0x00, 0x07, 0xF8, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00
+};
+
+// AND mask for the hand cursor
+static unsigned char hand_cursor_and[32 * 4] = {
+  0xFF, 0xF3, 0xFF, 0xFF,
+  0xFF, 0xE1, 0xFF, 0xFF,
+  0xFF, 0xE1, 0xFF, 0xFF,
+  0xFF, 0xE1, 0xFF, 0xFF,
+  0xFF, 0xE1, 0xFF, 0xFF,
+  0xFF, 0xE0, 0x7F, 0xFF,
+  0xFF, 0xE0, 0x0F, 0xFF,
+  0xFF, 0xE0, 0x03, 0xFF,
+  0xFF, 0xE0, 0x01, 0xFF,
+  0xFF, 0x20, 0x00, 0xFF,
+  0xFE, 0x00, 0x00, 0xFF,
+  0xFE, 0x00, 0x00, 0xFF,
+  0xFF, 0x00, 0x00, 0xFF,
+  0xFF, 0x80, 0x00, 0xFF,
+  0xFF, 0x80, 0x00, 0xFF,
+  0xFF, 0xC0, 0x00, 0xFF,
+  0xFF, 0xC0, 0x01, 0xFF,
+  0xFF, 0xE0, 0x01, 0xFF,
+  0xFF, 0xE0, 0x01, 0xFF,
+  0xFF, 0xF0, 0x03, 0xFF,
+  0xFF, 0xF0, 0x03, 0xFF,
+  0xFF, 0xF0, 0x03, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF
+};
+
+void org::xwt::plat::Win32::natInit() {
+
+    // grab desktop dc/handle
+    desktop_handle = (jint)GetDesktopWindow();
+    desktop_dc = (jint)GetDC((HWND)desktop_handle);
+
+    // create cursors
+    org::xwt::plat::Win32::wait_cursor = (jint)LoadCursor(NULL, IDC_WAIT);
+    org::xwt::plat::Win32::default_cursor = (jint)LoadCursor(NULL, IDC_ARROW);
+    org::xwt::plat::Win32::crosshair_cursor = (jint)LoadCursor(NULL, IDC_CROSS);
+    org::xwt::plat::Win32::text_cursor = (jint)LoadCursor(NULL, IDC_IBEAM);
+    org::xwt::plat::Win32::move_cursor = (jint)LoadCursor(NULL, IDC_SIZEALL);
+    org::xwt::plat::Win32::sizenesw_cursor = (jint)LoadCursor(NULL, IDC_SIZENESW);
+    org::xwt::plat::Win32::sizens_cursor = (jint)LoadCursor(NULL, IDC_SIZENS);
+    org::xwt::plat::Win32::sizenwse_cursor = (jint)LoadCursor(NULL, IDC_SIZENWSE);
+    org::xwt::plat::Win32::sizewe_cursor = (jint)LoadCursor(NULL, IDC_SIZEWE);
+    org::xwt::plat::Win32::hand_cursor = (jint)CreateCursor(GetModuleHandle(NULL), 14, 1, 32, 32, hand_cursor_and, hand_cursor_xor);
+
+    // enumerate fonts
+    LOGFONT lf;
+    lf.lfCharSet = ANSI_CHARSET;
+    lf.lfFaceName[0] = 0;
+    lf.lfPitchAndFamily = 0;
+    EnumFontFamiliesEx((HDC)desktop_dc, &lf, fontproc, 0, 0);
+
+    messagePumpThread = (jint)GetCurrentThreadId();
+    messagePumpStarted->release();
+
+    MSG msg;
+    while(GetMessage(&msg, (HWND)NULL, 0, 0) > 0) {
+
+        if (msg.message == WM_USER_CREATEWINDOW) {
+            org::xwt::plat::Win32$Win32Surface *surface = (org::xwt::plat::Win32$Win32Surface*)msg.lParam;
+
+            // we must create a unique window class name for each
+            // window so that minimization icons can be set independantly
+            char buf[255];
+            sprintf(buf, "XWT_WINDOW_CLASS_%i", window_class_counter++);
+
+            WNDCLASSEX wc;
+            wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
+            wc.lpfnWndProc = WndProc;
+            wc.cbClsExtra = 0;
+            wc.cbSize = sizeof(WNDCLASSEX);
+            wc.cbWndExtra = 0;
+            wc.hInstance = GetModuleHandle(NULL);
+            wc.hIcon = NULL;
+            wc.hIconSm = NULL;
+            wc.hCursor = NULL;
+            wc.hbrBackground = (HBRUSH)(COLOR_SCROLLBAR + 1);
+            wc.lpszMenuName = NULL;
+            wc.lpszClassName = buf;
+            RegisterClassEx(&wc);
+            
+            surface->hwnd = (jint)CreateWindow(wc.lpszClassName, TEXT(""), msg.wParam ? WS_NORMAL : WS_POPUP, 200, 200, 100, 100,
+                                               (HWND__*)NULL, (HMENU__*)NULL, GetModuleHandle(NULL), (LPVOID)NULL);
+            SetFocus((HWND)surface->hwnd);
+            surface->hwndCreated->release();
+            
+        } else {
+            TranslateMessage(&msg);
+            if (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN || msg.message == WM_MBUTTONDOWN) SetFocus(msg.hwnd);
+            DispatchMessage(&msg);
+        }
+
+    }
+    java::lang::System::exit(-1);
+}
+
+
+// Platform Methods ///////////////////////////////////////////////////////////////////
+
+jstring org::xwt::plat::Win32::_getClipBoard() {
+    OpenClipboard((HWND)desktop_handle);
+    HGLOBAL hmem = GetClipboardData(CF_TEXT);
+    if (hmem == NULL) return NULL;
+    char* buf = (char*)GlobalLock(hmem);
+    if (buf == NULL) return NULL;
+    jstring ret = JvNewStringLatin1(buf);
+    GlobalUnlock(hmem);
+    CloseClipboard();
+    return ret;
+}
+
+void org::xwt::plat::Win32::_setClipBoard(jstring s) {
+    OpenClipboard((HWND)desktop_handle);
+    HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, JvGetStringUTFLength(s) + 1);
+    if (hmem == NULL) return;
+    char* buf = (char*)GlobalLock(hmem);
+    if (buf == NULL) return;
+    JvGetStringUTFRegion(s, 0, JvGetStringUTFLength(s), buf);
+    buf[JvGetStringUTFLength(s)] = '\0';
+    GlobalUnlock(hmem);
+    SetClipboardData(CF_TEXT, hmem);
+    CloseClipboard();
+}
+
+void org::xwt::plat::Win32::_criticalAbort(jstring message) {
+    char buf[JvGetStringUTFLength(message) + 1];
+    buf[JvGetStringUTFLength(message)] = '\0';
+    JvGetStringUTFRegion(message, 0, JvGetStringUTFLength(message), buf);
+    MessageBox (NULL, buf, "XWT Critical Abort", MB_OK | MB_ICONSTOP | MB_TASKMODAL | MB_SETFOREGROUND);
+    java::lang::System::exit(-1);
+}
+
+jint org::xwt::plat::Win32::_getScreenWidth() {
+    RECT rect;
+    SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
+    return rect.right - rect.left;
+}
+
+jint org::xwt::plat::Win32::_getScreenHeight() {
+    RECT rect;
+    SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
+    return rect.bottom - rect.top;
+}
+
+org::xwt::plat::Win32$Win32Font* org::xwt::plat::Win32::mapFont(org::xwt::Platform$ParsedFont* pf) {
+    org::xwt::plat::Win32$Win32Font* ret = new org::xwt::plat::Win32$Win32Font();
+    LOGFONT logfont;
+    memset(&logfont, 0, sizeof(LOGFONT));
+    logfont.lfHeight = -MulDiv(pf->size, GetDeviceCaps((HDC)org::xwt::plat::Win32::desktop_dc, LOGPIXELSY), 72);
+    if (pf->italic) logfont.lfItalic = 1;
+    if (pf->bold) logfont.lfWeight = FW_BOLD;
+    logfont.lfCharSet = ANSI_CHARSET;
+
+    JvGetStringUTFRegion(pf->name, 0, min(31, JvGetStringUTFLength(pf->name)), logfont.lfFaceName);
+    logfont.lfFaceName[min(31, JvGetStringUTFLength(pf->name))] = 0;
+
+    ret->hfont = (jint)CreateFontIndirect(&logfont);
+    SelectObject((HDC)desktop_dc, (HFONT)(ret->hfont));
+
+    TEXTMETRIC tm;
+    GetTextMetrics((HDC)desktop_dc, &tm);
+    POINT p;
+    p.x = 0; p.y = tm.tmAscent;
+    LPtoDP((HDC)desktop_dc, &p, 1); 
+    ret->maxAscent = p.y;
+
+    p.x = 0; p.y = tm.tmDescent;
+    LPtoDP((HDC)desktop_dc, &p, 1);
+    ret->maxDescent = p.y;
+
+    return ret;
+}
+
+jint org::xwt::plat::Win32::_stringWidth(jstring font, jstring text) {
+
+    HFONT hfont = (HFONT)(getFont(font)->hfont);
+    SelectObject((HDC)org::xwt::plat::Win32::desktop_dc, hfont);
+
+    int len = min(1024, JvGetStringUTFLength(text));
+    char buf[len + 1];
+    buf[len] = '\0';
+    JvGetStringUTFRegion(text, 0, len, buf);
+    
+    SIZE size;
+    GetTextExtentPoint32((HDC)org::xwt::plat::Win32::desktop_dc, buf, len, &size);
+    return size.cx;
+}
+
+
+
+// Win32DoubleBuffer /////////////////////////////////////////////////////////////////////////
+
+// This is a scratch area; when blitting a translucent image, we copy the underlying data here first.
+// Since all drawing operations are single-threaded, it's safe to use a global here.
+static HBITMAP scratch = NULL;
+static HDC scratch_dc = NULL;
+static jint* scratch_bits = NULL;
+static jint scratch_w = 0;
+static jint scratch_h = 0;
+
+#define BLT(dest, dx1, dy1, dx2, dy2, src, sx1, sy1, sx2, sy2, op)                                   \
+    if ((dx2 - dx1 == sx2 - sx1) && (dy2 - dy1 == sy2 - sy1))                                        \
+        BitBlt(dest, dx1, dy1, dx2 - dx1, dy2 - dy1, src, sx1, sy1, op);                             \
+    else                                                                                             \
+        StretchBlt(dest, dx1, dy1, dx2 - dx1, dy2 - dy1, src, sx1, sy1, sx2 - sx1, sy2 - sy1, op);
+        
+void org::xwt::plat::Win32$Win32DoubleBuffer::drawPicture(org::xwt::Picture* source0,
+                                                          jint dx1, jint dy1, jint dx2, jint dy2,
+                                                          jint sx1, jint sy1, jint sx2, jint sy2) {
+
+    org::xwt::plat::Win32$Win32Picture* source = (org::xwt::plat::Win32$Win32Picture*)source0;
+
+    if (source->hasalpha) {
+
+        if (scratch == NULL || scratch_w < dx2 - dx1 || scratch_h < dy2 - dy1) {
+            if (scratch_dc != NULL) DeleteDC(scratch_dc);
+            if (scratch != NULL) DeleteObject(scratch);
+            scratch_w = max(dx2 - dx1, scratch_w);
+            scratch_h = max(dy2 - dy1, scratch_h);
+
+            BITMAPINFO bitmapinfo;
+            memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
+            bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+            bitmapinfo.bmiHeader.biWidth = scratch_w;
+            bitmapinfo.bmiHeader.biHeight = -1 * scratch_h;
+            bitmapinfo.bmiHeader.biPlanes = 1;
+            bitmapinfo.bmiHeader.biBitCount = 32;
+            bitmapinfo.bmiHeader.biCompression = BI_RGB;
+
+            // create section DIB
+            scratch = CreateDIBSection(NULL, &bitmapinfo, DIB_RGB_COLORS, (void**)&scratch_bits, NULL, 0);
+            scratch_dc = CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
+            SelectObject(scratch_dc, scratch);
+        }
+
+        // copy from screen to scratch
+        BitBlt((HDC)scratch_dc, 0, 0, dx2 - dx1, dy2 - dy1, (HDC)hdc, dx1, dy1, SRCCOPY);
+
+        // apply alpha-blending to scratch
+        jint* dat = elements(source->data);
+
+        for(int x = max(clipx1, dx1) - dx1; x < min(clipx2, dx2) - dx1; x++)
+            for(int y = max(clipy1, dy1) - dy1; y < min(clipy2, dy2) - dy1; y++) {
+                int sx = (x * (sx2 - sx1)) / (dx2 - dx1) + sx1;
+                int sy = (y * (sy2 - sy1)) / (dy2 - dy1) + sy1;
+                jint dst = scratch_bits[y * scratch_w + x];
+                jint src = dat[sy * source->getWidth() + sx];
+                jint alpha = (src & 0xFF000000) >> 24;
+                jint r = (((src & 0x00FF0000) >> 16) * alpha + ((dst & 0x00FF0000) >> 16) * (0xFF - alpha)) / 0xFF;
+                jint g = (((src & 0x0000FF00) >> 8)  * alpha + ((dst & 0x0000FF00) >> 8)  * (0xFF - alpha)) / 0xFF;
+                jint b = (((src & 0x000000FF))       * alpha + ((dst & 0x000000FF))       * (0xFF - alpha)) / 0xFF;
+                scratch_bits[y * scratch_w + x] = (r << 16) | (g << 8) | b;
+            }
+
+        // copy back from scratch to screen
+        BitBlt((HDC)hdc, dx1, dy1, dx2 - dx1, dy2 - dy1, (HDC)scratch_dc, 0, 0, SRCCOPY);
+
+    } else {
+        if (source->hasmask) {
+            BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->maskdc, sx1, sy1, sx2, sy2, SRCAND);
+            BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->hdc, sx1, sy1, sx2, sy2, SRCPAINT);
+        } else {
+            BLT((HDC)hdc, dx1, dy1, dx2, dy2, (HDC)source->hdc, sx1, sy1, sx2, sy2, SRCCOPY);
+        }
+
+    }
+
+}
+
+void org::xwt::plat::Win32$Win32DoubleBuffer::drawString(jstring font, jstring text, jint x, jint y, jint color) {
+
+    org::xwt::plat::Win32$Win32Font* wf = org::xwt::plat::Win32::getFont(font);
+    SelectObject((HDC)hdc, (HFONT)(wf->hfont));
+
+    // Platform API passes us the y-pos of the bottom of the text; we need the top
+    y -= wf->maxAscent;
+
+    int len = min(1024, JvGetStringUTFLength(text));
+    char buf[len + 1];
+    buf[len] = '\0';
+    JvGetStringUTFRegion(text, 0, len, buf);
+
+    SetTextColor((HDC)hdc, PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
+    TextOut((HDC)hdc, x, y, buf, len);
+}
+
+void org::xwt::plat::Win32$Win32DoubleBuffer::fillRect(jint x, jint y, jint x2, jint y2, jint color) {
+    jint w = x2 - x;
+    jint h = y2 - y;
+
+    // sadly, the ability to change the color of a brush didn't arrive until Win2k...
+    HBRUSH brush = CreateSolidBrush(PALETTERGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
+    if (!brush) return;
+    RECT rect = { x, y, x + w, y + h };
+    FillRect((HDC)hdc, &rect, brush);
+    DeleteObject(brush);
+}
+
+void org::xwt::plat::Win32$Win32Surface::blit(org::xwt::DoubleBuffer* s, jint sx, jint sy, jint dx, jint dy, jint dx2, jint dy2) {
+    BitBlt((HDC)hdc, dx, dy, dx2 - dx, dy2 - dy, (HDC)(((org::xwt::plat::Win32$Win32DoubleBuffer*)s)->hdc), sx, sy, SRCCOPY);
+}
+
+void org::xwt::plat::Win32$Win32DoubleBuffer::natInit() {
+    hbitmap = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
+    hdc = (jint)CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
+    SetBkMode((HDC)hdc, TRANSPARENT);
+    SelectObject((HDC)hdc, (HBITMAP)hbitmap);
+}
+
+void org::xwt::plat::Win32$Win32DoubleBuffer::setClip(jint x, jint y, jint x2, jint y2) {
+    clipx1 = x; clipx2 = x2; clipy1 = y; clipy2 = y2;
+    HRGN hrgn = CreateRectRgn(x, y, x2, y2);
+    SelectClipRgn((HDC)hdc, hrgn);
+    DeleteObject(hrgn);
+}
+
+void org::xwt::plat::Win32$Win32DoubleBuffer::finalize() {
+    DeleteObject((void*)hdc);
+    DeleteObject((void*)hbitmap);
+}
+
+
+
+// Win32Picture /////////////////////////////////////////////////////////////////////////
+
+void org::xwt::plat::Win32$Win32Picture::natInit() {
+
+    BITMAPINFO bitmapinfo;
+    memset(&bitmapinfo, 0, sizeof(BITMAPINFO));
+    bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
+    bitmapinfo.bmiHeader.biWidth = w;
+    bitmapinfo.bmiHeader.biHeight = -1 * h;
+    bitmapinfo.bmiHeader.biPlanes = 1;
+    bitmapinfo.bmiHeader.biBitCount = 32;
+    bitmapinfo.bmiHeader.biCompression = BI_RGB;
+
+    hbitmap = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
+    hdc = (jint)CreateCompatibleDC((HDC)org::xwt::plat::Win32::desktop_dc);
+    SelectObject((HDC)hdc, (HBITMAP)hbitmap);
+    uint32_t* dat = (uint32_t*)elements(data);
+    for(int i=0; i<data->length; i++) if ((dat[i] & 0xFF000000) == 0x00000000) dat[i] = 0x00000000;
+    StretchDIBits((HDC)hdc, 0, 0, w, h, 0, 0, w, h, elements(data), &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
+
+    jint _copy[min(1024, data->length)];
+    jint* copy = data->length > 1024 ? (jint*)malloc(data->length * 4) : _copy;
+
+    memcpy(copy, elements(data), data->length * 4);
+    for(int i=0; i<data->length; i++)
+        if ((copy[i] & 0xFF000000) == 0x00000000) {
+            hasmask = 1;
+            copy[i] = 0x00FFFFFF;
+        } else if ((copy[i] & 0xFF000000) == 0xFF000000) {
+            copy[i] = 0x00000000;
+        } else {
+            hasalpha = 1;
+            hasmask = 0;
+            if (data->length > 1024) free(copy);
+            return;
+        }
+
+    if (!hasmask) {
+        if (data->length > 1024) free(copy);
+        return;
+    }
+
+    //    hmask = (jint)CreateBitmap(w, h, 1, 1, NULL);
+    hmask = (jint)CreateCompatibleBitmap((HDC)org::xwt::plat::Win32::desktop_dc, w, h);
+    maskdc = (jint)CreateCompatibleDC(NULL);
+    SelectObject((HDC)maskdc, (HBITMAP)hmask);
+    StretchDIBits((HDC)maskdc, 0, 0, w, h, 0, 0, w, h, copy, &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
+    if (data->length > 1024) free(copy);
+}
+
+
+
+// Win32Surface /////////////////////////////////////////////////////////////////////////////
+
+void org::xwt::plat::Win32$Win32Surface::natInit(jboolean framed) {
+
+    // Ask the message-handling thread to create a window for us
+    PostThreadMessage((DWORD)org::xwt::plat::Win32::messagePumpThread, WM_USER + 2, (WPARAM)framed, (LPARAM)this);
+    hwndCreated->block();
+
+    // turn on incremental GC now that we have a user-visible interface
+    // [[this is causing segfaults; enable it later...]]
+    // GC_enable_incremental();
+
+    ShowWindow ((HWND)hwnd, SW_SHOWDEFAULT);
+    hdc = (jint)GetDC((HWND)hwnd);
+}
+
+void org::xwt::plat::Win32$Win32Surface::finalize() { DeleteObject((void*)hwnd); }
+void org::xwt::plat::Win32$Win32Surface::toFront() { BringWindowToTop((HWND)hwnd); }
+void org::xwt::plat::Win32$Win32Surface::toBack() { SetWindowPos((HWND)hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); }
+void org::xwt::plat::Win32$Win32Surface::_dispose() { PostMessage((HWND)hwnd, WM_USER_DISPOSE, 0, 0); }
+void org::xwt::plat::Win32$Win32Surface::setInvisible(jboolean h) { ShowWindow((HWND)hwnd, h ? SW_HIDE : SW_SHOWNORMAL); }
+void org::xwt::plat::Win32$Win32Surface::_setMinimized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMINIMIZED : SW_SHOWNORMAL); }
+void org::xwt::plat::Win32$Win32Surface::_setMaximized(jboolean m) { ShowWindow((HWND)hwnd, m ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); }
+void org::xwt::plat::Win32$Win32Surface::postCursorChange() { PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0); }
+
+void org::xwt::plat::Win32$Win32Surface::setLocation(jint x, jint y) {
+    POINT point;
+    RECT rect;
+    point.x = 0;
+    point.y = 0;
+    ClientToScreen((HWND)hwnd, &point);
+    GetWindowRect((HWND)hwnd, &rect);
+    SetWindowPos((HWND)hwnd, NULL, x - (point.x - rect.left), y - (point.y - rect.top), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
+}
+
+void org::xwt::plat::Win32$Win32Surface::setSize(jint w, jint h) {
+    RECT client_rect, window_rect;
+    GetClientRect((HWND)hwnd, &client_rect);
+    GetWindowRect((HWND)hwnd, &window_rect);
+    int addwidth = (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left);
+    int addheight = (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top);
+    SetWindowPos((HWND)hwnd, NULL, 0, 0, w + addwidth, h + addheight, SWP_NOZORDER | SWP_NOMOVE);
+}
+
+void org::xwt::plat::Win32$Win32Surface::setTitleBarText(java::lang::String* title) {
+    int len = min(1024, JvGetStringUTFLength(title));
+    char buf[len + 1];
+    buf[len] = '\0';
+    JvGetStringUTFRegion(title, 0, len, buf);
+    SetWindowText((HWND)hwnd, buf);
+}
+
+void org::xwt::plat::Win32$Win32Surface::setIcon(org::xwt::Picture* p0) {
+
+    org::xwt::plat::Win32$Win32Picture* p = (org::xwt::plat::Win32$Win32Picture*)p0;
+    int icon_width = GetSystemMetrics(SM_CXSMICON);
+    int icon_height = GetSystemMetrics(SM_CYSMICON);
+
+    // create the bitmap
+    HBITMAP bit = CreateCompatibleBitmap((HDC)hdc, icon_width, icon_height);
+    HDC memdc = CreateCompatibleDC((HDC)hdc);
+    SelectObject(memdc, bit);
+    BLT((HDC)memdc, 0, 0, icon_width, icon_height, (HDC)(p->hdc), 0, 0, p->getWidth(), p->getHeight(), SRCCOPY);
+
+    // create the mask
+    jint* dat = elements(p->data);
+    HBITMAP bit_mask = CreateBitmap(icon_width, icon_height * 2, 1, 1, NULL);
+    SelectObject(memdc, bit_mask);
+    for(int x=0; x<icon_width; x++)
+        for(int y=0; y<icon_height; y++) {
+            int source = dat[(x * p->getWidth()) / icon_width + ((y * p->getHeight()) / icon_height) * (p->getWidth())];
+            if ((source & 0xFF000000) != 0x00000000) SetPixel(memdc, x, y, PALETTERGB(0, 0, 0));
+            else SetPixel(memdc, x, y, PALETTERGB(255, 255, 255));
+        }
+
+    // instantiate the icon and assign it to the window class
+    ICONINFO ici;
+    ici.fIcon = 1;
+    ici.hbmMask = bit_mask;
+    ici.hbmColor = bit;
+    HICON hicon = CreateIconIndirect(&ici);
+    HICON oldicon = (HICON)SetClassLong((HWND)hwnd, GCL_HICONSM, (LONG)hicon);
+    if (oldicon != NULL) DestroyIcon(oldicon);
+    DeleteObject(memdc);
+}
+
+static jstring keyToString(WPARAM wParam);
+jint org::xwt::plat::Win32$Win32Surface::WndProc(jint _hwnd, jint _iMsg, jint _wParam, jint _lParam) {
+
+    UINT iMsg = (UINT)_iMsg;
+    WPARAM wParam = (WPARAM)_wParam;
+    LPARAM lParam = (LPARAM)_lParam;
+    int oldmousex, oldmousey;
+    MINMAXINFO* mmi;
+    POINT point;
+    HWND hwnd2;
+    RECT rect, rect2;
+    jboolean newinside;
+    int16_t mouse_x;
+    int16_t mouse_y;
+
+    switch(iMsg) {
+    case WM_DEVMODECHANGE: break;    // FEATURE: color depth changed
+    case WM_DISPLAYCHANGE: break;    // FEATURE: screen size changed
+    case WM_FONTCHANGE: break;       // FEATURE: set of fonts changed
+    case WM_MOUSEWHEEL: break;       // FEATURE: Mouse Wheel
+
+    case WM_SYSKEYDOWN: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
+    case WM_KEYDOWN:
+        KeyPressed(keyToString(wParam));
+        return 0;
+
+    case WM_SYSKEYUP: if (GetKeyState(VK_MENU) >> (sizeof(SHORT) * 8 - 1) == 0) return 0;
+    case WM_KEYUP:
+        KeyReleased(keyToString(wParam));
+        return 0;
+
+    case WM_SETFOCUS: Focused(true); return 0;
+    case WM_KILLFOCUS: Focused(false); return 0;
+    case WM_LBUTTONDBLCLK: DoubleClick(1); return 0;
+    case WM_RBUTTONDBLCLK: DoubleClick(2); return 0;
+    case WM_MBUTTONDBLCLK: DoubleClick(3); return 0;
+    case WM_LBUTTONDOWN: Press(1); return 0;
+    case WM_RBUTTONDOWN: Press(2); return 0;
+    case WM_MBUTTONDOWN: Press(3); return 0;
+    case WM_CLOSE: Close(); return 0;
+    case WM_ERASEBKGND: return 0;
+
+    case WM_LBUTTONUP:
+    case WM_RBUTTONUP:
+    case WM_MBUTTONUP:
+        Release(iMsg == WM_LBUTTONUP ? 1 : (iMsg == WM_RBUTTONUP ? 2 : 3));
+        if (captured && !inside) {
+            ReleaseCapture();
+            SetCursor((HCURSOR)previous_cursor);
+        }
+        return 0;
+
+    case WM_MOVING:
+        GetClientRect((HWND)hwnd, &rect);
+        PosChange(rect.left, rect.top);
+        return 0;
+
+    case WM_SIZE:
+        if (wParam == SIZE_MINIMIZED) {
+            if (maximized) Maximized(false);
+            Minimized(true);
+        } else if (wParam == SIZE_MAXIMIZED) {
+            if (minimized) Minimized(false); 
+            Maximized(true);
+        } else {
+            if (minimized) Minimized(false); 
+            if (maximized) Maximized(false);
+        }
+        // deliberately fall through to WM_SIZING
+
+    case WM_SIZING:
+        GetClientRect((HWND)hwnd, &rect);
+        SizeChange(rect.right - rect.left, rect.bottom - rect.top);
+        return 0;
+
+    case WM_MOUSEMOVE:
+        if (mousex == lParam & 0xFFFF && mousey == (lParam & 0xFFFF0000) >> 16) return 0;
+
+        GetClientRect((HWND)hwnd, &rect);
+        point.x = mouse_x = lParam & 0xFFFF;
+        point.y = mouse_y = (lParam & 0xFFFF0000) >> 16;
+        ClientToScreen((HWND)hwnd, &point);
+        hwnd2 = WindowFromPoint(point);
+
+        newinside = hwnd2 == (HWND)hwnd && mouse_x > 0 && mouse_y > 0 && mouse_x < (rect.right - rect.left) && mouse_y < (rect.bottom - rect.top) ? 1 : 0;
+
+        Move(mouse_x, mouse_y);
+
+        if (newinside && !inside) {
+            inside = 1;
+            SetCapture((HWND)hwnd);
+            captured = 1;
+            previous_cursor = (jint)GetCursor();
+            PostMessage((HWND)hwnd, WM_USER_SETCURSOR, 0, 0);
+
+        } else if (!newinside && inside) {
+            inside = 0;
+            if (!button1 && !button2 && !button3) {
+                ReleaseCapture();
+                captured = 0;
+                SetCursor((HCURSOR)previous_cursor);
+            }
+        }
+
+        return 0;
+
+    case WM_USER_SETCURSOR:
+        // I can't find documentation of this anywhere, but in my experience, an event must be received between an invocation
+        // of SetCapture() and SetCursor(). Furthermore, it seems that after calling SetCursor(), the cursor will not change until a
+        // return from WndProc (ie the completion of the processing of some event).  Hence, we use WM_USER to indicate that the screen
+        // cursor should be re-set to "current_cursor".
+        SetCursor((HCURSOR)current_cursor);
+        return 0;
+
+    case WM_USER_DISPOSE:
+        // used to signal that we should destroy ourselves
+        DestroyWindow((HWND)hwnd);
+        return 0;
+
+    case WM_GETMINMAXINFO:
+        mmi = (MINMAXINFO*)lParam;
+        mmi->ptMinTrackSize.x = root->dmin(0);
+        mmi->ptMinTrackSize.y = root->dmin(1);
+        mmi->ptMaxTrackSize.x = root->dmax(0);
+        mmi->ptMaxTrackSize.y = root->dmax(1);
+        return 0;
+        
+    case WM_PAINT: 
+        PAINTSTRUCT ps;
+        BeginPaint((HWND)org::xwt::plat::Win32$Win32Surface::hwnd, &ps);
+        Dirty(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top);
+        Refresh();
+        EndPaint((HWND)org::xwt::plat::Win32$Win32Surface::hwnd, &ps);
+        return 0;
+
+    default: break;
+    }  
+
+    return DefWindowProc((HWND)hwnd, iMsg, wParam, lParam);
+}
+
+
+// Key Translator Helper /////////////////////////////////////////////////////////////////////
+
+static char keyarr [256] = { 0 };
+static jstring keyToString(WPARAM wParam) {
+    char arr[4];
+    keyarr[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
+    keyarr[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
+    keyarr[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
+    keyarr[VK_SHIFT] = GetKeyState(VK_SHIFT);
+    
+    if (ToAscii(wParam, 0, (BYTE*)keyarr, (WORD*)arr, 0) == 1) {
+        switch (arr[0]) {
+        case '\t': return JvNewStringLatin1("tab");
+        case 0x1b: return JvNewStringLatin1("escape");
+        case '\n': return JvNewStringLatin1("enter");
+        case '\r': return JvNewStringLatin1("enter");
+        case 0x08: return JvNewStringLatin1("back_space");
+        default: return JvNewStringLatin1(arr, 1);
+        }
+        
+    } else switch (wParam) {
+    case VK_CLEAR: return JvNewStringLatin1("clear");
+    case VK_SHIFT: return JvNewStringLatin1("shift");
+    case VK_CONTROL: return JvNewStringLatin1("control");
+    case VK_CAPITAL: return JvNewStringLatin1("caps_lock");
+    case VK_MENU: return JvNewStringLatin1("alt");
+    case VK_PAUSE: return JvNewStringLatin1("pause");
+    case VK_PRIOR: return JvNewStringLatin1("page_up");
+    case VK_NEXT: return JvNewStringLatin1("page_down");
+    case VK_END: return JvNewStringLatin1("end");
+    case VK_HOME: return JvNewStringLatin1("home");
+    case VK_LEFT: return JvNewStringLatin1("left");
+    case VK_UP: return JvNewStringLatin1("up");
+    case VK_RIGHT: return JvNewStringLatin1("right");
+    case VK_DOWN: return JvNewStringLatin1("down");
+    case VK_INSERT: return JvNewStringLatin1("insert");
+    case VK_DELETE: return JvNewStringLatin1("delete");
+    case VK_NUMPAD0: return JvNewStringLatin1("numpad0");
+    case VK_NUMPAD1: return JvNewStringLatin1("numpad1");
+    case VK_NUMPAD2: return JvNewStringLatin1("numpad2");
+    case VK_NUMPAD3: return JvNewStringLatin1("numpad3");
+    case VK_NUMPAD4: return JvNewStringLatin1("numpad4");
+    case VK_NUMPAD5: return JvNewStringLatin1("numpad5");
+    case VK_NUMPAD6: return JvNewStringLatin1("numpad6");
+    case VK_NUMPAD7: return JvNewStringLatin1("numpad7");
+    case VK_NUMPAD8: return JvNewStringLatin1("numpad8");
+    case VK_NUMPAD9: return JvNewStringLatin1("numpad9");
+    case VK_F1: return JvNewStringLatin1("f1");
+    case VK_F2: return JvNewStringLatin1("f2");
+    case VK_F3: return JvNewStringLatin1("f3");
+    case VK_F4: return JvNewStringLatin1("f4");
+    case VK_F5: return JvNewStringLatin1("f5");
+    case VK_F6: return JvNewStringLatin1("f6");
+    case VK_F7: return JvNewStringLatin1("f7");
+    case VK_F8: return JvNewStringLatin1("f8");
+    case VK_F9: return JvNewStringLatin1("f9");
+    case VK_F10: return JvNewStringLatin1("f10");
+    case VK_F11: return JvNewStringLatin1("f11");
+    case VK_F12: return JvNewStringLatin1("f12");
+    case VK_NUMLOCK: return JvNewStringLatin1("num_lock");
+    case VK_SCROLL: return JvNewStringLatin1("scroll_lock");
+    case VK_LSHIFT: return JvNewStringLatin1("shift");
+    case VK_RSHIFT: return JvNewStringLatin1("shift");
+    case VK_LCONTROL: return JvNewStringLatin1("control");
+    case VK_RCONTROL: return JvNewStringLatin1("control");
+    }
+    return NULL;
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+#else /* COMPILE_DLL */
+
+
+//
+// A simple DLL to invoke xwt.exe and pass it any arguments found in the <object/> tag
+//
+
+#include <windows.h>
+#include <initguid.h>
+#include <objbase.h>
+#include <oaidl.h>
+#include <oleauto.h>
+#include <olectl.h>
+
+
+// Globals ////////////////////////////////////////////////////////////////////////
+
+using namespace std;
+
+#define CLSID_STRING_SIZE 39
+
+const char XWT_friendlyName[] = "XWT ActiveX Control (Hydrogen)";
+const char XWT_versionIndependantProgramID[] = "XWT.ActiveX";
+const char XWT_programID[] = "XWT.ActiveX.Hydrogen";
+extern "C" const CLSID XWT_clsid = { 0xc60d6d23, 0x3a7d, 0x11d6, { 0x82, 0xf9, 0x0, 0x50, 0x56, 0xca, 0x92, 0x50 } };
+
+static HMODULE g_hModule = NULL;    //DLL handle
+
+
+
+
+// Superclasses ////////////////////////////////////////////////////////////////////////
+
+// Option bit definitions for IObjectSafety:
+#define INTERFACESAFE_FOR_UNTRUSTED_CALLER  0x00000001  // Caller of interface may be untrusted
+#define INTERFACESAFE_FOR_UNTRUSTED_DATA    0x00000002  // Data passed into interface may be untrusted
+
+// {CB5BDC81-93C1-11cf-8F20-00805F2CD064}
+DEFINE_GUID(IID_IObjectSafety, 0xcb5bdc81, 0x93c1, 0x11cf, 0x8f, 0x20, 0x0, 0x80, 0x5f, 0x2c, 0xd0, 0x64);
+
+interface IObjectSafety : public IUnknown {
+ public:
+    virtual HRESULT __stdcall GetInterfaceSafetyOptions(REFIID riid, DWORD __RPC_FAR *pdwSupportedOptions, DWORD __RPC_FAR *pdwEnabledOptions) = 0;
+    virtual HRESULT __stdcall SetInterfaceSafetyOptions(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions) = 0;
+};
+
+interface IShoeHorn : IPersistPropertyBag, IObjectSafety { };
+
+
+
+// Entry Points ////////////////////////////////////////////////////////////////////////
+
+// to get mingw to stop nagging me
+int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { }
+
+// determines whether or not the DLL can be unloaded; always allow this since we don't do too much
+STDAPI __declspec(dllexport) DllCanUnloadNow(void) { return S_OK; }
+
+extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID /*lpReserved*/) {
+    if (dwReason == DLL_PROCESS_ATTACH) g_hModule = (HINSTANCE)hModule;
+    return TRUE;
+}
+
+
+
+// Other ///////////////////////////////////////////////////////////////////////////////////
+
+// simple assert() replacement that pops open a message box if there are errors
+void check(int val, char* message) {
+    if (!val) {
+        MessageBox (NULL, message, "XWT Critical Abort", MB_OK | MB_ICONSTOP | MB_TASKMODAL | MB_SETFOREGROUND);
+        exit(-1);
+    }
+}
+
+void clsidToString(const CLSID& clsid, char* str, int length) {
+    check(length >= CLSID_STRING_SIZE, "clsidToString(): string too short");
+       LPOLESTR wide_str = NULL;
+       HRESULT hr = StringFromCLSID(clsid, &wide_str);
+       check(SUCCEEDED(hr), "StringFromCLSID() failed in clsidToString()");
+       wcstombs(str, wide_str, length);
+       CoTaskMemFree(wide_str);
+}
+
+void setRegistryKey(const char* key, const char* subkey, const char* value) {
+       HKEY hKey;
+       char keyBuf[1024];
+       strcpy(keyBuf, key);
+       if (subkey != NULL) {
+               strcat(keyBuf, "\\");
+               strcat(keyBuf, subkey );
+       }
+       long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, keyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL);
+       if (value != NULL)
+        check(RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)value, strlen(value) + 1) == ERROR_SUCCESS,
+              "RegSetValueEx() failed in setRegistryKey()");
+       RegCloseKey(hKey);
+}
+
+void deleteRegistryKey(HKEY parent, const char* target) {
+       HKEY hKeyChild;
+       RegOpenKeyEx(parent, target, 0, KEY_ALL_ACCESS, &hKeyChild);
+
+       // Iterate over children, deleting them
+       FILETIME time;
+       char szBuffer[256];
+       DWORD dwSize = 256;
+       while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL, NULL, NULL, &time) == S_OK) {
+               deleteRegistryKey(hKeyChild, szBuffer);
+               dwSize = 256;
+       }
+
+       RegCloseKey(hKeyChild);
+       RegDeleteKey(parent, target);
+}
+
+STDAPI __declspec(dllexport) DllRegisterServer(void) {
+       char moduleName[512];
+    HRESULT result = GetModuleFileName(g_hModule, moduleName, sizeof(moduleName)/sizeof(char));
+       check(result, "GetModuleFileName() failed in RegisterServer()");
+
+       char clsidString[CLSID_STRING_SIZE];
+       clsidToString(XWT_clsid, clsidString, sizeof(clsidString));
+
+       // build the key
+       char key[64];
+       strcpy(key, "CLSID\\");
+       strcat(key, clsidString);
+  
+       setRegistryKey(key, NULL, XWT_friendlyName);
+       setRegistryKey(key, "InprocServer32", moduleName);
+       setRegistryKey(key, "ProgID", XWT_programID);
+       setRegistryKey(key, "VersionIndependentProgID", XWT_versionIndependantProgramID);
+       setRegistryKey(XWT_versionIndependantProgramID, NULL, XWT_friendlyName); 
+       setRegistryKey(XWT_versionIndependantProgramID, "CLSID", clsidString);
+       setRegistryKey(XWT_versionIndependantProgramID, "CurVer", XWT_programID);
+       setRegistryKey(XWT_programID, NULL, XWT_friendlyName); 
+       setRegistryKey(XWT_programID, "CLSID", clsidString);
+       return S_OK;
+}
+
+STDAPI __declspec(dllexport) DllUnregisterServer(void) {
+       char clsidString[CLSID_STRING_SIZE];
+       clsidToString(XWT_clsid, clsidString, sizeof(clsidString));
+
+       // build the key
+       char key[64];
+       strcpy(key, "CLSID\\");
+       strcat(key, clsidString);
+
+       deleteRegistryKey(HKEY_CLASSES_ROOT, key);
+       deleteRegistryKey(HKEY_CLASSES_ROOT, XWT_versionIndependantProgramID);
+       deleteRegistryKey(HKEY_CLASSES_ROOT, XWT_programID);
+       return S_OK;
+}
+
+
+
+// ShoeHorn //////////////////////////////////////////////////////////////////////////////////
+
+class ShoeHorn : public IShoeHorn {
+    public:
+    virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) {
+        if(iid == IID_IUnknown) *ppv = static_cast<IShoeHorn*>(this);
+        else if(iid == XWT_clsid) *ppv = static_cast<IShoeHorn*>(this);
+        else if(iid == IID_IPersistPropertyBag) *ppv = static_cast<IPersistPropertyBag*>(this);
+        else if(iid == IID_IObjectSafety) *ppv = static_cast<IObjectSafety*>(this);
+        else { *ppv = NULL; return E_NOINTERFACE; }
+        reinterpret_cast<IUnknown*>(*ppv)->AddRef();
+        return S_OK;
+    }
+    virtual ULONG __stdcall AddRef() { return InterlockedIncrement(&m_cRef); }
+    virtual ULONG __stdcall Release() {
+        if(InterlockedDecrement(&m_cRef) == 0) { delete this; return 0; }
+        return m_cRef;
+    }
+    virtual HRESULT __stdcall GetClassID(CLSID*) { return S_OK; }
+    virtual HRESULT __stdcall InitNew() { return S_OK; }
+    virtual HRESULT __stdcall Save(IPropertyBag*, int, int) { return S_OK; }
+    virtual HRESULT __stdcall SetInterfaceSafetyOptions(REFIID riid, DWORD pdwSupportedOptions, DWORD pdwEnabledOptions) { return S_OK; }
+    virtual HRESULT __stdcall GetInterfaceSafetyOptions(REFIID riid, DWORD* pdwSupportedOptions, DWORD* pdwEnabledOptions) {
+        if (pdwSupportedOptions != NULL) *pdwSupportedOptions |= INTERFACESAFE_FOR_UNTRUSTED_DATA;
+        if (pdwEnabledOptions != NULL) *pdwSupportedOptions |= INTERFACESAFE_FOR_UNTRUSTED_DATA;
+        return S_OK;
+    }
+
+    virtual HRESULT __stdcall Load(IPropertyBag* pPropBag, IErrorLog* pErrorLog) {
+        VARIANT v;
+        v.vt = VT_BSTR;
+        HRESULT hrRead;
+        
+        WCHAR wc[100];
+        char url[100];
+        
+        MultiByteToWideChar(CP_ACP, 0, "initial-xwar-url", -1, wc, 100);
+        pPropBag->Read(wc, &v, pErrorLog);
+        check(WideCharToMultiByte(CP_ACP, 0, v.bstrVal, -1, url, 100, NULL, NULL),
+              "WideCharToMultiByte() failed in ShoeHorn::Load()");
+        
+        HKEY hkey;
+        LONG result = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\ActiveX Cache", &hkey);
+        check(result == ERROR_SUCCESS, "RegOpenKey() failed in ShoeHorn::Load()");
+        
+        // iterate over all the activex cache locations until we find ourselves
+        for(int i=0; i<9; i++) {
+            DWORD buflen = 999;
+            char buf[1000];
+            VALENT valents[20];
+            DWORD type;
+            char which[2];
+
+            which[0] = '0' + i;
+            which[1] = '\0';
+            result = RegQueryValueEx(hkey, which, NULL, &type, (BYTE*)buf, &buflen);
+            check(result == ERROR_SUCCESS, "RegQueryValueEx() failed in ShoeHorn::Load()");
+            buf[buflen] = '\0';
+            
+            char cmdline[200];
+            for(int i=0; i<200; i++) cmdline[i] = '\0';
+            strncpy(cmdline, buf, 200);
+            strncpy(cmdline + strlen(cmdline), "\\xwt.exe", 200 - strlen(cmdline));
+            strncpy(cmdline + strlen(cmdline), " ", 200 - strlen(cmdline));
+            strncpy(cmdline + strlen(cmdline), url, 200 - strlen(cmdline));
+            
+            PROCESS_INFORMATION pInfo;
+            STARTUPINFO sInfo;
+            sInfo.cb = sizeof(STARTUPINFO);
+            sInfo.lpReserved = NULL;
+            sInfo.lpReserved2 = NULL;
+            sInfo.cbReserved2 = 0;
+            sInfo.lpDesktop = NULL;
+            sInfo.lpTitle = NULL;
+            sInfo.dwFlags = 0;
+            sInfo.dwX = 0;
+            sInfo.dwY = 0;
+            sInfo.dwFillAttribute = 0;
+            sInfo.wShowWindow = SW_SHOW;
+            BOOL b = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &sInfo, &pInfo);
+            if (b) return S_OK;
+        }
+
+        check(0, "unable to locate xwt.exe in ActiveX cache folders");
+    }
+
+    ShoeHorn() : m_cRef(1) { };
+    ~ShoeHorn() { };
+    private: long m_cRef;
+};
+
+
+
+
+// ClassFactory //////////////////////////////////////////////////////////////////////////
+
+class ClassFactory : public IClassFactory {
+    public:
+    virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) {
+        if(iid == IID_IUnknown) *ppv = static_cast<IClassFactory*>(this);
+        else if(iid == IID_IClassFactory) *ppv = static_cast<IClassFactory*>(this);
+        else {
+            *ppv = NULL;
+            return E_NOINTERFACE;
+        }
+        reinterpret_cast<IUnknown*>(*ppv)->AddRef();
+        return S_OK;
+    }
+
+    ClassFactory() : m_cRef(1) { }
+    ~ClassFactory() { }
+    virtual HRESULT __stdcall LockServer(BOOL bLock) { return S_OK; }
+    virtual ULONG __stdcall AddRef() { return InterlockedIncrement(&m_cRef); }
+    virtual ULONG __stdcall Release() {
+        if(InterlockedDecrement(&m_cRef) == 0) {
+            delete this;
+            return 0;
+        }
+        return m_cRef;
+    }
+
+    virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv) {
+        if(pUnknownOuter != NULL) return CLASS_E_NOAGGREGATION;
+        ShoeHorn* s = new ShoeHorn;
+        if(s == NULL) return E_OUTOFMEMORY;
+        HRESULT hr = s->QueryInterface(iid, ppv);
+        s->Release();
+        return hr;
+    }
+    
+    private: long m_cRef;
+};
+
+
+extern "C" __stdcall HRESULT DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv) {
+    if(clsid != XWT_clsid) return CLASS_E_CLASSNOTAVAILABLE;
+    ClassFactory* pFactory = new ClassFactory; 
+    if(pFactory == NULL) return E_OUTOFMEMORY;
+    HRESULT hr = pFactory->QueryInterface(iid, ppv);
+    pFactory->Release();
+    return hr;
+}
+
+#endif
diff --git a/src/org/xwt/plat/Win32.def b/src/org/xwt/plat/Win32.def
new file mode 100644 (file)
index 0000000..0baddd2
--- /dev/null
@@ -0,0 +1,8 @@
+; xwt.def : Declares the module parameters.
+
+EXPORTS
+                DllGetClassObject = DllGetClassObject@12
+                DllCanUnloadNow = DllCanUnloadNow@0
+                DllRegisterServer = DllRegisterServer@0
+                DllUnregisterServer = DllUnregisterServer@0
+
diff --git a/src/org/xwt/plat/Win32.inf b/src/org/xwt/plat/Win32.inf
new file mode 100644 (file)
index 0000000..55f1fec
--- /dev/null
@@ -0,0 +1,22 @@
+;; This file will be copied to bin-Win32/xwt.inf and then packed up
+;; into the .cab file for distribution
+
+[version]
+    signature="$CHICAGO$"
+    AdvancedINF=2.0
+ [Add.Code]
+    xwt.dll=xwt.dll
+    xwt.exe=xwt.exe
+ [xwt.dll]
+    file-win32-x86=thiscab
+    clsid={C60D6D23-3A7D-11d6-82F9-005056CA9250}
+    FileVersion=7,0,0,0
+    RegisterServer=yes
+ [xwt.exe]
+    file-win32-x86=thiscab
+    clsid={FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFE}
+    FileVersion=7,0,0,0
+    RegisterServer=no
+
+
+
diff --git a/src/org/xwt/plat/Win32.java b/src/org/xwt/plat/Win32.java
new file mode 100644 (file)
index 0000000..33efa69
--- /dev/null
@@ -0,0 +1,253 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [LGPL]
+package org.xwt.plat;
+
+import org.xwt.*;
+import org.xwt.util.*;
+import java.util.*;
+import java.io.*;
+
+/** Platform specific code for GCJ-compiled Win32 binaries */
+public class Win32 extends GCJ {
+
+    // Initialization ////////////////////////////////////////////////////////////////////////////
+
+    // Win32 often asks for a DC/Handle when it doesn't really need one
+    static int desktop_handle = 0;
+    static int desktop_dc = 0;
+
+    // Cursors
+    static int wait_cursor = 0;
+    static int default_cursor = 0;
+    static int crosshair_cursor = 0;
+    static int text_cursor = 0;
+    static int move_cursor = 0;
+    static int sizenesw_cursor = 0;
+    static int sizens_cursor = 0;
+    static int sizenwse_cursor = 0;
+    static int sizewe_cursor = 0;
+    static int hand_cursor = 0;
+    
+    /** reverse lookup from hwnd to Win32Surface */
+    public static Hash hwndToWin32SurfaceMap = new Hash();
+
+    /** lets us know that natInit() is finished */
+    public static Semaphore messagePumpStarted = new Semaphore();
+
+    /** ThreadId of the message pump thread, used to send it messages */
+    public static int messagePumpThread = 0;
+
+    public static native String getTempPath();
+    public static native void natInit();
+
+    public Win32() { }
+
+    public void init() {
+        String logfile = getTempPath() + "xwt-log.txt";
+        try {
+            PrintStream ps = new PrintStream(new FileOutputStream(logfile));
+            System.setOut(ps);
+            System.setErr(ps);
+        } catch (Throwable e) {
+            criticalAbort("Exception while attempting to redirect logging to " + logfile + " -- " + e);
+        }
+
+        new Thread() { public void run() { natInit(); } }.start();
+        messagePumpStarted.block();
+        fontList = new String[fontListVec.size()];
+        fontListVec.toArray(fontList);
+        fontListVec = null;
+    }
+
+
+    // Font Handling ////////////////////////////////////////////////////////////////////////////
+
+    // FEATURE: query the registry for the user's default font
+    protected String _getDefaultFont() { return "dialog8"; }
+    protected int _getMaxAscent(String font) { return getFont(font).maxAscent; }
+    protected int _getMaxDescent(String font) { return getFont(font).maxDescent; }
+    protected native int _stringWidth(String font, String text);
+
+    // methods/members used to enumerate platform fonts on startup
+    public static Vector fontListVec = new Vector();
+    public static String[] fontList = null;
+    protected String[] _listFonts() { return fontList; }
+    public static void addFont(String name, int height, boolean italic, boolean bold) {
+        fontListVec.addElement(name.replace(' ', '_').toLowerCase() + "" + height + (italic ? "i" : "") + (bold ? "b" : ""));
+    }
+
+    static Hash fontCache = new Hash();
+    public static class Win32Font {
+        int hfont;
+        int maxAscent;
+        int maxDescent;
+    }
+
+    /** takes a parsed font and finds the closest platform-specific font */
+    static native Win32Font mapFont(Platform.ParsedFont pf);
+
+    /** takes an unparsed font and finds the closest platform-specific font */
+    static Win32Font getFont(String font) {
+        Win32Font ret = (Win32Font)fontCache.get(font);
+        if (ret != null) return ret;
+
+        Platform.ParsedFont pf = new Platform.ParsedFont(font);
+        if (pf.name.equals("serif")) pf.name = "Times New Roman";
+        else if (pf.name.equals("sansserif")) pf.name = "Arial";
+        else if (pf.name.equals("monospace")) pf.name = "Courier New";
+        else if (pf.name.equals("dialog")) pf.name = "Arial";
+        else if (pf.name.equals("tty")) pf.name = "FixedSys";
+
+        ret = mapFont(pf);
+        fontCache.put(font, ret);
+        return ret;
+    }
+
+
+    // Implementation of Platform methods /////////////////////////////////////////////////////////
+
+    protected boolean _needsAutoClick() { return true; }
+    protected String getDescriptiveName() { return "GCJ Win32 Binary"; }
+    protected Surface _createSurface(Box b, boolean framed) { return new Win32Surface(b, framed); }
+    protected DoubleBuffer _createDoubleBuffer(int w, int h, Surface owner) { return new Win32DoubleBuffer(w, h, (Win32Surface)owner); }
+    protected Picture _createPicture(int[] b, int w, int h) { return new Win32Picture(b, w, h); }
+    protected native int _getScreenWidth();
+    protected native int _getScreenHeight();
+    protected boolean _supressDirtyOnResize() { return false; }
+    protected native void _criticalAbort(String message);
+    protected native String _getClipBoard();
+    protected native void _setClipBoard(String s);
+
+
+    // Win32Surface ////////////////////////////////////////////////////////////////////////////
+
+    public static class Win32Surface extends Surface {
+
+        /** used to block while waiting for the message pump thread to create a hwnd for us */
+        public Semaphore hwndCreated = new Semaphore();
+
+        /** nothing more than a method version of WndProc, so we can access instance members/methods */
+        public native int WndProc(int hwnd, int imsg, int wparam, int lparam);
+
+        /** true iff the mouse is inside this window; used to determine if we should capture the mouse */
+        boolean inside = false;
+
+        /** true iff we have 'captured' the mouse with SetCapture() */
+        boolean captured = false;
+
+        public int hwnd = -1;
+        public int hdc = -1;
+
+        public int current_cursor = default_cursor;
+
+        /** used to restore the cursor immediately before ReleaseCapture() */
+        public int previous_cursor = 0;
+
+        public native void natInit(boolean framed);
+        public Win32Surface(Box b, final boolean framed) {
+            super(b);
+            natInit(framed);
+            hwndToWin32SurfaceMap.put(new Integer(hwnd), this);
+        }
+
+        public void syncCursor() {
+            if (cursor.equals("default")) current_cursor = default_cursor;
+            else if (cursor.equals("wait")) current_cursor = wait_cursor;
+            else if (cursor.equals("crosshair")) current_cursor = crosshair_cursor;
+            else if (cursor.equals("text")) current_cursor = text_cursor;
+            else if (cursor.equals("move")) current_cursor = move_cursor;
+            else if (cursor.equals("hand")) current_cursor = hand_cursor;
+            else if (cursor.equals("east") || cursor.equals("west")) current_cursor = sizewe_cursor;
+            else if (cursor.equals("north") || cursor.equals("south")) current_cursor = sizens_cursor;
+            else if (cursor.equals("northwest") || cursor.equals("southeast")) current_cursor = sizenwse_cursor;
+            else if (cursor.equals("northeast") || cursor.equals("southwest")) current_cursor = sizenesw_cursor;
+            postCursorChange();
+        }
+
+        public native void finalize();
+        public native void postCursorChange();
+        public native void toBack();
+        public native void toFront();
+        public native void _setMinimized(boolean m);
+        public native void setInvisible(boolean i);
+        public native void _setMaximized(boolean m);
+        public native void setSize(int w, int h);
+        public native void setLocation(int x, int y);
+        public native void setTitleBarText(String s);
+        public native void setIcon(Picture p);
+        public native void _dispose();
+        public native void blit(DoubleBuffer s, int sx, int sy, int dx, int dy, int dx2, int dy2);
+    }
+
+
+    // Win32Picture ////////////////////////////////////////////////////////////////////////////
+
+    public static class Win32Picture implements Picture {
+        int w = 0, h = 0;
+        int[] data = null;
+
+        /** the Win32 bitmap version of this Picture */
+        int hbitmap = -1;
+
+        /** dc of the bitmap */
+        int hdc = -1;
+
+        /** true iff this Picture has translucent regions */
+        boolean hasalpha = false;
+
+        /** true iff this Picture has transparent regions but no translucent regions */
+        boolean hasmask = true;
+
+        /** if hasmask, this mask indicates which regions are transparent */
+        int hmask = -1;
+
+        /** dc of the mask */
+        int maskdc = -1;
+
+        public int getWidth() { return w; };
+        public int getHeight() { return h; };
+        public int[] getData() { return data; }
+        public native void natInit();
+        public Win32Picture(int[] data, int w, int h) { this.w = w; this.h = h; this.data = data; natInit(); }
+    }
+    
+
+    // Win32DoubleBuffer //////////////////////////////////////////////////////////////////////////
+
+    public static class Win32DoubleBuffer implements DoubleBuffer {
+
+        int w = 0;
+        int h = 0;
+
+        int clipx1 = 0;
+        int clipy1 = 0;
+        int clipx2 = 0;
+        int clipy2 = 0;
+
+        int hdc = -1;
+        int hbitmap = -1;
+
+        public int getHeight() { return h; }
+        public int getWidth() { return w; }
+
+        public native void natInit();
+        public Win32DoubleBuffer(int w, int h, Win32Surface owner) {
+            this.w = w;
+            this.h = h;
+            clipx2 = w;
+            clipy2 = h;
+            natInit();
+        }
+
+        public native void setClip(int x, int y, int x2, int y2);
+        public native void fillRect(int x, int y, int x2, int y2, int color);
+        public native void drawString(String font, String text, int x, int y, int color);
+        public native void drawPicture(Picture source, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2);
+        public native void finalize();
+        public void drawPicture(Picture source, int x, int y) {
+            drawPicture(source, x, y, x + source.getWidth(), y + source.getHeight(), 0, 0, source.getWidth(), source.getHeight());
+        }
+
+    }
+
+}
+                                    
diff --git a/src/org/xwt/plat/Win32.xml b/src/org/xwt/plat/Win32.xml
new file mode 100644 (file)
index 0000000..ee301d8
--- /dev/null
@@ -0,0 +1,48 @@
+<!-- Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL] -->
+
+<project name="Win32" default="build">
+
+    <target name="build">
+        <property name="gcc-target" value="i686-pc-mingw32"/>
+        <property name="linkflags" value="-Wl,--subsystem,windows"/>
+        <property name="binaryname" value="xwt.exe"/>
+        <ant target="build" antfile="src/org/xwt/plat/GCJ.xml"/>
+    </target>
+                                                                         
+    <target name="run">
+        <echo message='launching .exe'/>
+        <exec dir='.' executable='/usr/bin/ssh'>
+            <arg value='${cygwin-host}'/>
+            <arg value='cd ${cygwin-path}; www/html/dist/xwt.exe src org.xwt.demo.main'/>
+        </exec>
+    </target>
+
+    <target name="dist">
+        <echo message='creating ActiveX dll...'/>
+        <bash>
+            /usr/local/gcc/bin/i686-pc-mingw32-g++     -DCOMPILE_DLL -c src/org/xwt/plat/Win32.cc -o bin-Win32/Win32-dll.o
+            /usr/local/gcc/bin/i686-pc-mingw32-g++     -Wl,--base-file,/tmp/basefile -mdll -Wl,-e,_DllMainCRTStartup@12 \
+                                                       -o bin-Win32/xwt.dll bin-Win32/Win32-dll.o -lole32 -luuid
+            /usr/local/gcc/bin/i686-pc-mingw32-dlltool --base-file /tmp/basefile --output-exp bin-Win32/xwt.exp \
+                                                       --def src/org/xwt/plat/Win32.def
+            /usr/local/gcc/bin/i686-pc-mingw32-g++     -Wl,--base-file,/tmp/basefile bin-Win32/xwt.exp -mdll \
+                                                       -Wl,-e,_DllMainCRTStartup@12 -o bin-Win32/xwt.dll bin-Win32/Win32-dll.o -lole32 -luuid
+            /usr/local/gcc/bin/i686-pc-mingw32-dlltool --base-file /tmp/basefile --output-exp bin-Win32/xwt.exp --def src/org/xwt/plat/Win32.def
+            /usr/local/gcc/bin/i686-pc-mingw32-g++     bin-Win32/xwt.exp -mdll -Wl,-e,_DllMainCRTStartup@12 -o bin-Win32/xwt.dll \
+                                                       bin-Win32/Win32-dll.o -lole32 -luuid
+        </bash>
+
+        <echo message='collecting files to archive in a cab...'/>
+        <copy file='src/org/xwt/plat/Win32.inf' tofile='bin-Win32/xwt.inf'/>
+        <copy file='www/html/dist/xwt.exe' tofile='bin-Win32/xwt.exe'/>
+        <bash> /usr/local/gcc/bin/i686-pc-mingw32-strip bin-Win32/xwt.exe </bash>
+
+        <echo message='building cab file...'/>
+        <exec dir='.' executable='/usr/bin/ssh'>
+           <arg value='${cygwin-host}'/>
+           <arg value='cd ${cygwin-path}/bin-Win32; ../lib/cabarc.exe -s 6144 N xwt.cab xwt.dll xwt.exe xwt.inf'/>
+        </exec>
+        <copy file='bin-Win32/xwt.cab' tofile='www/html/dist/xwt.cab'/>
+    </target>
+
+</project>
diff --git a/src/org/xwt/plat/package.html b/src/org/xwt/plat/package.html
new file mode 100644 (file)
index 0000000..272a116
--- /dev/null
@@ -0,0 +1,41 @@
+<body>
+
+<p>
+Each platform which XWT is ported to should have a subclass of {@link
+org.xwt.Platform} in this package as well as an ant buildfile.
+</p>
+
+<p>
+The subclass of {@link org.xwt.Platform} should be named
+<i>plat</i>.java, where <i>plat</i> is the descriptive name for the
+platform. The ant buildfile should be named <i>plat</i>.xml, and
+should contain three targets:
+</p>
+
+<ul><li> <b><tt>build</tt></b>: compiles all code required for the XWT
+         Engine and creates an executable in
+         <tt>xwt/www/html/dist/</tt>. If applicable, all debugging
+         symbols should be left in this executable.
+
+    <li> <b><tt>run</tt></b>: runs the executable, loading the
+         <tt>org.xwt.demo.main</tt> widget demo. This is useful for
+         testing.
+
+    <li> <b><tt>dist</tt></b>: prepares any files required to
+         distribute the engine (for example, a jar or cab). This step
+         should remove debugging symbols, if applicable.
+</ul>
+
+<p>
+Other platform-specific files can be included in this directory, as
+long as they share the same filename as the main class, and differ
+only in extension (for example, <i>plat</i>.cc).
+</p>
+
+<p>
+Classes in this package may subclass each other; this is useful when
+two classes are very similar -- they can share a superclass containing
+the common functionality.
+</p>
+
+</body>