--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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
--- /dev/null
+// 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;
+ }
+
+}
+
+
+
--- /dev/null
+// 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();
+}
--- /dev/null
+/*
+ * 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 };
+}
+
+
+
--- /dev/null
+// 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();
+
+}
--- /dev/null
+// 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 =
+ "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";
+
+ private static String splashScreen_gif_base64_3 =
+ "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";
+
+ private static String splashScreen_gif_base64_4 =
+ "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";
+
+ }
+
+ /** 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";
+
+}
+
+
+
--- /dev/null
+// 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();
+
+}
+
--- /dev/null
+// 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) { }
+ }
+ }
+ }
+
+}
+
+
+
--- /dev/null
+/*
+ * 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;
+ }
+ }
+
+}
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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 <clinit> 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++;
+ }
+ }
+ }
+
+ }
+
+}
+
--- /dev/null
+// 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;
+ }
+}
+
+
+
--- /dev/null
+// 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("<");
+ else if (cbuf[i] == '&') sb.append("&");
+ 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;
+ }
+
+}
--- /dev/null
+// 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; }
+ }
+
+}
+
+
+
--- /dev/null
+// 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);
+ }
+
+}
--- /dev/null
+// 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;
+
+}
--- /dev/null
+// 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 <template/> 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
+ * <template/> 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); }
+ }
+
+}
+
+
--- /dev/null
+// 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();
+ }
+
+}
--- /dev/null
+// 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;
+ }
+ }
+
+}
+
--- /dev/null
+// 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;
+ }
+
+ }
+
+}
--- /dev/null
+// 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();
+
+}
--- /dev/null
+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;
+}
--- /dev/null
+// 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 <value> tag is
+ * encountered, an empty String ("") is pushed onto the stack. If
+ * the <value/> node has content (either an anonymous
+ * string or some other XML node), that content replaces the
+ * empty string.
+ *
+ * If an <array> tag is encountered, a null is pushed onto the
+ * stack. When a </data> 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 <struct> tag is encountered, a JSObject is pushed
+ * onto the stack. If a <name> tag is encountered, its CDATA is
+ * pushed onto the stack. When a </member> 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("<");
+ else if (cbuf[i] == '&') sb.append("&");
+ 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[] { }; }
+
+}
--- /dev/null
+// 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]);
+
+ }
+
+}
--- /dev/null
+// 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;
+ }
+ };
+
+}
+
+
+
+
+
--- /dev/null
+
+<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>
+
--- /dev/null
+// 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);
+ }
+ }
+
+}
--- /dev/null
+// 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); }
+ }
+
+}
+
--- /dev/null
+<!-- 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>
+
+
--- /dev/null
+// 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"; }
+}
--- /dev/null
+<!-- 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>
+
--- /dev/null
+// 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
--- /dev/null
+; xwt.def : Declares the module parameters.
+
+EXPORTS
+ DllGetClassObject = DllGetClassObject@12
+ DllCanUnloadNow = DllCanUnloadNow@0
+ DllRegisterServer = DllRegisterServer@0
+ DllUnregisterServer = DllUnregisterServer@0
+
--- /dev/null
+;; 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
+
+
+
--- /dev/null
+// 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());
+ }
+
+ }
+
+}
+
--- /dev/null
+<!-- 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>
--- /dev/null
+<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>