0001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
0002: *
0003: * ***** BEGIN LICENSE BLOCK *****
0004: * Version: MPL 1.1/GPL 2.0
0005: *
0006: * The contents of this file are subject to the Mozilla Public License Version
0007: * 1.1 (the "License"); you may not use this file except in compliance with
0008: * the License. You may obtain a copy of the License at
0009: * http://www.mozilla.org/MPL/
0010: *
0011: * Software distributed under the License is distributed on an "AS IS" basis,
0012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0013: * for the specific language governing rights and limitations under the
0014: * License.
0015: *
0016: * The Original Code is Rhino code, released
0017: * May 6, 1999.
0018: *
0019: * The Initial Developer of the Original Code is
0020: * Netscape Communications Corporation.
0021: * Portions created by the Initial Developer are Copyright (C) 1997-2000
0022: * the Initial Developer. All Rights Reserved.
0023: *
0024: * Contributor(s):
0025: * Bob Jervis
0026: *
0027: * Alternatively, the contents of this file may be used under the terms of
0028: * the GNU General Public License Version 2 or later (the "GPL"), in which
0029: * case the provisions of the GPL are applicable instead of those above. If
0030: * you wish to allow use of your version of this file only under the terms of
0031: * the GPL and not to allow others to use your version of this file under the
0032: * MPL, indicate your decision by deleting the provisions above and replacing
0033: * them with the notice and other provisions required by the GPL. If you do
0034: * not delete the provisions above, a recipient may use your version of this
0035: * file under either the MPL or the GPL.
0036: *
0037: * ***** END LICENSE BLOCK ***** */
0038:
0039: // API class
0040: package org.mozilla.javascript;
0041:
0042: import java.beans.PropertyChangeEvent;
0043: import java.beans.PropertyChangeListener;
0044: import java.io.CharArrayWriter;
0045: import java.io.IOException;
0046: import java.io.PrintWriter;
0047: import java.io.Reader;
0048: import java.io.StringWriter;
0049: import java.io.Writer;
0050: import java.lang.reflect.InvocationTargetException;
0051: import java.lang.reflect.Method;
0052: import java.util.Hashtable;
0053: import java.util.Locale;
0054:
0055: import org.mozilla.javascript.debug.DebuggableScript;
0056: import org.mozilla.javascript.debug.Debugger;
0057: import org.mozilla.javascript.xml.XMLLib;
0058:
0059: /**
0060: * This class represents the runtime context of an executing script.
0061: *
0062: * Before executing a script, an instance of Context must be created
0063: * and associated with the thread that will be executing the script.
0064: * The Context will be used to store information about the executing
0065: * of the script such as the call stack. Contexts are associated with
0066: * the current thread using the {@link #call(ContextAction)}
0067: * or {@link #enter()} methods.<p>
0068: *
0069: * Different forms of script execution are supported. Scripts may be
0070: * evaluated from the source directly, or first compiled and then later
0071: * executed. Interactive execution is also supported.<p>
0072: *
0073: * Some aspects of script execution, such as type conversions and
0074: * object creation, may be accessed directly through methods of
0075: * Context.
0076: *
0077: * @see Scriptable
0078: * @author Norris Boyd
0079: * @author Brendan Eich
0080: */
0081:
0082: public class Context {
0083: /**
0084: * Language versions.
0085: *
0086: * All integral values are reserved for future version numbers.
0087: */
0088:
0089: /**
0090: * The unknown version.
0091: */
0092: public static final int VERSION_UNKNOWN = -1;
0093:
0094: /**
0095: * The default version.
0096: */
0097: public static final int VERSION_DEFAULT = 0;
0098:
0099: /**
0100: * JavaScript 1.0
0101: */
0102: public static final int VERSION_1_0 = 100;
0103:
0104: /**
0105: * JavaScript 1.1
0106: */
0107: public static final int VERSION_1_1 = 110;
0108:
0109: /**
0110: * JavaScript 1.2
0111: */
0112: public static final int VERSION_1_2 = 120;
0113:
0114: /**
0115: * JavaScript 1.3
0116: */
0117: public static final int VERSION_1_3 = 130;
0118:
0119: /**
0120: * JavaScript 1.4
0121: */
0122: public static final int VERSION_1_4 = 140;
0123:
0124: /**
0125: * JavaScript 1.5
0126: */
0127: public static final int VERSION_1_5 = 150;
0128:
0129: /**
0130: * JavaScript 1.6
0131: */
0132: public static final int VERSION_1_6 = 160;
0133:
0134: /**
0135: * JavaScript 1.7
0136: */
0137: public static final int VERSION_1_7 = 170;
0138:
0139: /**
0140: * Controls behaviour of <tt>Date.prototype.getYear()</tt>.
0141: * If <tt>hasFeature(FEATURE_NON_ECMA_GET_YEAR)</tt> returns true,
0142: * Date.prototype.getYear subtructs 1900 only if 1900 <= date < 2000.
0143: * The default behavior of {@link #hasFeature(int)} is always to subtruct
0144: * 1900 as rquired by ECMAScript B.2.4.
0145: */
0146: public static final int FEATURE_NON_ECMA_GET_YEAR = 1;
0147:
0148: /**
0149: * Control if member expression as function name extension is available.
0150: * If <tt>hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME)</tt> returns
0151: * true, allow <tt>function memberExpression(args) { body }</tt> to be
0152: * syntax sugar for <tt>memberExpression = function(args) { body }</tt>,
0153: * when memberExpression is not a simple identifier.
0154: * See ECMAScript-262, section 11.2 for definition of memberExpression.
0155: * By default {@link #hasFeature(int)} returns false.
0156: */
0157: public static final int FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME = 2;
0158:
0159: /**
0160: * Control if reserved keywords are treated as identifiers.
0161: * If <tt>hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER)</tt> returns true,
0162: * treat future reserved keyword (see Ecma-262, section 7.5.3) as ordinary
0163: * identifiers but warn about this usage.
0164: *
0165: * By default {@link #hasFeature(int)} returns false.
0166: */
0167: public static final int FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER = 3;
0168:
0169: /**
0170: * Control if <tt>toString()</tt> should returns the same result
0171: * as <tt>toSource()</tt> when applied to objects and arrays.
0172: * If <tt>hasFeature(FEATURE_TO_STRING_AS_SOURCE)</tt> returns true,
0173: * calling <tt>toString()</tt> on JS objects gives the same result as
0174: * calling <tt>toSource()</tt>. That is it returns JS source with code
0175: * to create an object with all enumeratable fields of the original object
0176: * instead of printing <tt>[object <i>result of
0177: * {@link Scriptable#getClassName()}</i>]</tt>.
0178: * <p>
0179: * By default {@link #hasFeature(int)} returns true only if
0180: * the current JS version is set to {@link #VERSION_1_2}.
0181: */
0182: public static final int FEATURE_TO_STRING_AS_SOURCE = 4;
0183:
0184: /**
0185: * Control if properties <tt>__proto__</tt> and <tt>__parent__</tt>
0186: * are treated specially.
0187: * If <tt>hasFeature(FEATURE_PARENT_PROTO_PROPERTIES)</tt> returns true,
0188: * treat <tt>__parent__</tt> and <tt>__proto__</tt> as special properties.
0189: * <p>
0190: * The properties allow to query and set scope and prototype chains for the
0191: * objects. The special meaning of the properties is available
0192: * only when they are used as the right hand side of the dot operator.
0193: * For example, while <tt>x.__proto__ = y</tt> changes the prototype
0194: * chain of the object <tt>x</tt> to point to <tt>y</tt>,
0195: * <tt>x["__proto__"] = y</tt> simply assigns a new value to the property
0196: * <tt>__proto__</tt> in <tt>x</tt> even when the feature is on.
0197: *
0198: * By default {@link #hasFeature(int)} returns true.
0199: */
0200: public static final int FEATURE_PARENT_PROTO_PROPERTIES = 5;
0201:
0202: /**
0203: * @deprecated In previous releases, this name was given to
0204: * FEATURE_PARENT_PROTO_PROPERTIES.
0205: */
0206: public static final int FEATURE_PARENT_PROTO_PROPRTIES = 5;
0207:
0208: /**
0209: * Control if support for E4X(ECMAScript for XML) extension is available.
0210: * If hasFeature(FEATURE_E4X) returns true, the XML syntax is available.
0211: * <p>
0212: * By default {@link #hasFeature(int)} returns true if
0213: * the current JS version is set to {@link #VERSION_DEFAULT}
0214: * or is at least {@link #VERSION_1_6}.
0215: * @since 1.6 Release 1
0216: */
0217: public static final int FEATURE_E4X = 6;
0218:
0219: /**
0220: * Control if dynamic scope should be used for name access.
0221: * If hasFeature(FEATURE_DYNAMIC_SCOPE) returns true, then the name lookup
0222: * during name resolution will use the top scope of the script or function
0223: * which is at the top of JS execution stack instead of the top scope of the
0224: * script or function from the current stack frame if the top scope of
0225: * the top stack frame contains the top scope of the current stack frame
0226: * on its prototype chain.
0227: * <p>
0228: * This is useful to define shared scope containing functions that can
0229: * be called from scripts and functions using private scopes.
0230: * <p>
0231: * By default {@link #hasFeature(int)} returns false.
0232: * @since 1.6 Release 1
0233: */
0234: public static final int FEATURE_DYNAMIC_SCOPE = 7;
0235:
0236: /**
0237: * Control if strict variable mode is enabled.
0238: * When the feature is on Rhino reports runtime errors if assignment
0239: * to a global variable that does not exist is executed. When the feature
0240: * is off such assignments creates new variable in the global scope as
0241: * required by ECMA 262.
0242: * <p>
0243: * By default {@link #hasFeature(int)} returns false.
0244: * @since 1.6 Release 1
0245: */
0246: public static final int FEATURE_STRICT_VARS = 8;
0247:
0248: /**
0249: * Control if strict eval mode is enabled.
0250: * When the feature is on Rhino reports runtime errors if non-string
0251: * argument is passed to the eval function. When the feature is off
0252: * eval simply return non-string argument as is without performing any
0253: * evaluation as required by ECMA 262.
0254: * <p>
0255: * By default {@link #hasFeature(int)} returns false.
0256: * @since 1.6 Release 1
0257: */
0258: public static final int FEATURE_STRICT_EVAL = 9;
0259:
0260: /**
0261: * When the feature is on Rhino will add a "fileName" and "lineNumber"
0262: * properties to Error objects automatically. When the feature is off, you
0263: * have to explicitly pass them as the second and third argument to the
0264: * Error constructor. Note that neither behaviour is fully ECMA 262
0265: * compliant (as 262 doesn't specify a three-arg constructor), but keeping
0266: * the feature off results in Error objects that don't have
0267: * additional non-ECMA properties when constructed using the ECMA-defined
0268: * single-arg constructor and is thus desirable if a stricter ECMA
0269: * compliance is desired, specifically adherence to the point 15.11.5. of
0270: * the standard.
0271: * <p>
0272: * By default {@link #hasFeature(int)} returns false.
0273: * @since 1.6 Release 6
0274: */
0275: public static final int FEATURE_LOCATION_INFORMATION_IN_ERROR = 10;
0276:
0277: /**
0278: * Controls whether JS 1.5 'strict mode' is enabled.
0279: * When the feature is on, Rhino reports more than a dozen different
0280: * warnings. When the feature is off, these warnings are not generated.
0281: * FEATURE_STRICT_MODE implies FEATURE_STRICT_VARS and FEATURE_STRICT_EVAL.
0282: * <p>
0283: * By default {@link #hasFeature(int)} returns false.
0284: * @since 1.6 Release 6
0285: */
0286: public static final int FEATURE_STRICT_MODE = 11;
0287:
0288: /**
0289: * Controls whether a warning should be treated as an error.
0290: * @since 1.6 Release 6
0291: */
0292: public static final int FEATURE_WARNING_AS_ERROR = 12;
0293:
0294: /**
0295: * Enables enhanced access to Java.
0296: * Specifically, controls whether private and protected members can be
0297: * accessed, and whether scripts can catch all Java exceptions.
0298: * <p>
0299: * Note that this feature should only be enabled for trusted scripts.
0300: * <p>
0301: * By default {@link #hasFeature(int)} returns false.
0302: * @since 1.7 Release 1
0303: */
0304: public static final int FEATURE_ENHANCED_JAVA_ACCESS = 13;
0305:
0306: public static final String languageVersionProperty = "language version";
0307: public static final String errorReporterProperty = "error reporter";
0308:
0309: /**
0310: * Convenient value to use as zero-length array of objects.
0311: */
0312: public static final Object[] emptyArgs = ScriptRuntime.emptyArgs;
0313:
0314: /**
0315: * Create a new Context.
0316: *
0317: * Note that the Context must be associated with a thread before
0318: * it can be used to execute a script.
0319: * @deprecated use {@link ContextFactory#enter()} or
0320: * {@link ContextFactory#call(ContextAction)} instead.
0321: */
0322: public Context() {
0323: this (ContextFactory.getGlobal());
0324: }
0325:
0326: Context(ContextFactory factory) {
0327: assert factory != null;
0328: this .factory = factory;
0329: setLanguageVersion(VERSION_DEFAULT);
0330: optimizationLevel = codegenClass != null ? 0 : -1;
0331: maximumInterpreterStackDepth = Integer.MAX_VALUE;
0332: }
0333:
0334: /**
0335: * Get the current Context.
0336: *
0337: * The current Context is per-thread; this method looks up
0338: * the Context associated with the current thread. <p>
0339: *
0340: * @return the Context associated with the current thread, or
0341: * null if no context is associated with the current
0342: * thread.
0343: * @see ContextFactory#enterContext()
0344: * @see ContextFactory#call(ContextAction)
0345: */
0346: public static Context getCurrentContext() {
0347: Object helper = VMBridge.instance.getThreadContextHelper();
0348: return VMBridge.instance.getContext(helper);
0349: }
0350:
0351: /**
0352: * Same as calling {@link ContextFactory#enterContext()} on the global
0353: * ContextFactory instance.
0354: * @deprecated use {@link ContextFactory#enter()} or
0355: * {@link ContextFactory#call(ContextAction)} instead as this method relies
0356: * on usage of a static singleton "global" ContextFactory.
0357: * @return a Context associated with the current thread
0358: * @see #getCurrentContext()
0359: * @see #exit()
0360: * @see #call(ContextAction)
0361: */
0362: public static Context enter() {
0363: return enter(null);
0364: }
0365:
0366: /**
0367: * Get a Context associated with the current thread, using
0368: * the given Context if need be.
0369: * <p>
0370: * The same as <code>enter()</code> except that <code>cx</code>
0371: * is associated with the current thread and returned if
0372: * the current thread has no associated context and <code>cx</code>
0373: * is not associated with any other thread.
0374: * @param cx a Context to associate with the thread if possible
0375: * @return a Context associated with the current thread
0376: * @deprecated use {@link ContextFactory#enterContext(Context)} instead as
0377: * this method relies on usage of a static singleton "global" ContextFactory.
0378: * @see ContextFactory#enterContext(Context)
0379: * @see ContextFactory#call(ContextAction)
0380: */
0381: public static Context enter(Context cx) {
0382: return enter(cx, ContextFactory.getGlobal());
0383: }
0384:
0385: static final Context enter(Context cx, ContextFactory factory) {
0386: Object helper = VMBridge.instance.getThreadContextHelper();
0387: Context old = VMBridge.instance.getContext(helper);
0388: if (old != null) {
0389: cx = old;
0390: } else {
0391: if (cx == null) {
0392: cx = factory.makeContext();
0393: if (cx.enterCount != 0) {
0394: throw new IllegalStateException(
0395: "factory.makeContext() returned Context instance already associated with some thread");
0396: }
0397: factory.onContextCreated(cx);
0398: if (factory.isSealed() && !cx.isSealed()) {
0399: cx.seal(null);
0400: }
0401: } else {
0402: if (cx.enterCount != 0) {
0403: throw new IllegalStateException(
0404: "can not use Context instance already associated with some thread");
0405: }
0406: }
0407: VMBridge.instance.setContext(helper, cx);
0408: }
0409: ++cx.enterCount;
0410: return cx;
0411: }
0412:
0413: /**
0414: * Exit a block of code requiring a Context.
0415: *
0416: * Calling <code>exit()</code> will remove the association between
0417: * the current thread and a Context if the prior call to
0418: * {@link ContextFactory#enterContext()} on this thread newly associated a
0419: * Context with this thread. Once the current thread no longer has an
0420: * associated Context, it cannot be used to execute JavaScript until it is
0421: * again associated with a Context.
0422: * @see ContextFactory#enterContext()
0423: */
0424: public static void exit() {
0425: Object helper = VMBridge.instance.getThreadContextHelper();
0426: Context cx = VMBridge.instance.getContext(helper);
0427: if (cx == null) {
0428: throw new IllegalStateException(
0429: "Calling Context.exit without previous Context.enter");
0430: }
0431: if (cx.enterCount < 1)
0432: Kit.codeBug();
0433: if (--cx.enterCount == 0) {
0434: VMBridge.instance.setContext(helper, null);
0435: cx.factory.onContextReleased(cx);
0436: }
0437: }
0438:
0439: /**
0440: * Call {@link ContextAction#run(Context cx)}
0441: * using the Context instance associated with the current thread.
0442: * If no Context is associated with the thread, then
0443: * <tt>ContextFactory.getGlobal().makeContext()</tt> will be called to
0444: * construct new Context instance. The instance will be temporary
0445: * associated with the thread during call to
0446: * {@link ContextAction#run(Context)}.
0447: * @deprecated use {@link ContextFactory#call(ContextAction)} instead as
0448: * this method relies on usage of a static singleton "global"
0449: * ContextFactory.
0450: * @return The result of {@link ContextAction#run(Context)}.
0451: */
0452: public static Object call(ContextAction action) {
0453: return call(ContextFactory.getGlobal(), action);
0454: }
0455:
0456: /**
0457: * Call {@link
0458: * Callable#call(Context cx, Scriptable scope, Scriptable thisObj,
0459: * Object[] args)}
0460: * using the Context instance associated with the current thread.
0461: * If no Context is associated with the thread, then
0462: * {@link ContextFactory#makeContext()} will be called to construct
0463: * new Context instance. The instance will be temporary associated
0464: * with the thread during call to {@link ContextAction#run(Context)}.
0465: * <p>
0466: * It is allowed but not advisable to use null for <tt>factory</tt>
0467: * argument in which case the global static singleton ContextFactory
0468: * instance will be used to create new context instances.
0469: * @see ContextFactory#call(ContextAction)
0470: */
0471: public static Object call(ContextFactory factory,
0472: final Callable callable, final Scriptable scope,
0473: final Scriptable this Obj, final Object[] args) {
0474: if (factory == null) {
0475: factory = ContextFactory.getGlobal();
0476: }
0477: return call(factory, new ContextAction() {
0478: public Object run(Context cx) {
0479: return callable.call(cx, scope, this Obj, args);
0480: }
0481: });
0482: }
0483:
0484: /**
0485: * The method implements {@links ContextFactory#call(ContextAction)} logic.
0486: */
0487: static Object call(ContextFactory factory, ContextAction action) {
0488: Context cx = enter(null, factory);
0489: try {
0490: return action.run(cx);
0491: } finally {
0492: exit();
0493: }
0494: }
0495:
0496: /**
0497: * @deprecated
0498: * @see ContextFactory#addListener(ContextFactory.Listener)
0499: * @see ContextFactory#getGlobal()
0500: */
0501: public static void addContextListener(ContextListener listener) {
0502: // Special workaround for the debugger
0503: String DBG = "org.mozilla.javascript.tools.debugger.Main";
0504: if (DBG.equals(listener.getClass().getName())) {
0505: Class cl = listener.getClass();
0506: Class factoryClass = Kit
0507: .classOrNull("org.mozilla.javascript.ContextFactory");
0508: Class[] sig = { factoryClass };
0509: Object[] args = { ContextFactory.getGlobal() };
0510: try {
0511: Method m = cl.getMethod("attachTo", sig);
0512: m.invoke(listener, args);
0513: } catch (Exception ex) {
0514: RuntimeException rex = new RuntimeException();
0515: Kit.initCause(rex, ex);
0516: throw rex;
0517: }
0518: return;
0519: }
0520:
0521: ContextFactory.getGlobal().addListener(listener);
0522: }
0523:
0524: /**
0525: * @deprecated
0526: * @see ContextFactory#removeListener(ContextFactory.Listener)
0527: * @see ContextFactory#getGlobal()
0528: */
0529: public static void removeContextListener(ContextListener listener) {
0530: ContextFactory.getGlobal().addListener(listener);
0531: }
0532:
0533: /**
0534: * Return {@link ContextFactory} instance used to create this Context.
0535: */
0536: public final ContextFactory getFactory() {
0537: return factory;
0538: }
0539:
0540: /**
0541: * Checks if this is a sealed Context. A sealed Context instance does not
0542: * allow to modify any of its properties and will throw an exception
0543: * on any such attempt.
0544: * @see #seal(Object sealKey)
0545: */
0546: public final boolean isSealed() {
0547: return sealed;
0548: }
0549:
0550: /**
0551: * Seal this Context object so any attempt to modify any of its properties
0552: * including calling {@link #enter()} and {@link #exit()} methods will
0553: * throw an exception.
0554: * <p>
0555: * If <tt>sealKey</tt> is not null, calling
0556: * {@link #unseal(Object sealKey)} with the same key unseals
0557: * the object. If <tt>sealKey</tt> is null, unsealing is no longer possible.
0558: *
0559: * @see #isSealed()
0560: * @see #unseal(Object)
0561: */
0562: public final void seal(Object sealKey) {
0563: if (sealed)
0564: onSealedMutation();
0565: sealed = true;
0566: this .sealKey = sealKey;
0567: }
0568:
0569: /**
0570: * Unseal previously sealed Context object.
0571: * The <tt>sealKey</tt> argument should not be null and should match
0572: * <tt>sealKey</tt> suplied with the last call to
0573: * {@link #seal(Object)} or an exception will be thrown.
0574: *
0575: * @see #isSealed()
0576: * @see #seal(Object sealKey)
0577: */
0578: public final void unseal(Object sealKey) {
0579: if (sealKey == null)
0580: throw new IllegalArgumentException();
0581: if (this .sealKey != sealKey)
0582: throw new IllegalArgumentException();
0583: if (!sealed)
0584: throw new IllegalStateException();
0585: sealed = false;
0586: this .sealKey = null;
0587: }
0588:
0589: static void onSealedMutation() {
0590: throw new IllegalStateException();
0591: }
0592:
0593: /**
0594: * Get the current language version.
0595: * <p>
0596: * The language version number affects JavaScript semantics as detailed
0597: * in the overview documentation.
0598: *
0599: * @return an integer that is one of VERSION_1_0, VERSION_1_1, etc.
0600: */
0601: public final int getLanguageVersion() {
0602: return version;
0603: }
0604:
0605: /**
0606: * Set the language version.
0607: *
0608: * <p>
0609: * Setting the language version will affect functions and scripts compiled
0610: * subsequently. See the overview documentation for version-specific
0611: * behavior.
0612: *
0613: * @param version the version as specified by VERSION_1_0, VERSION_1_1, etc.
0614: */
0615: public void setLanguageVersion(int version) {
0616: if (sealed)
0617: onSealedMutation();
0618: checkLanguageVersion(version);
0619: Object listeners = propertyListeners;
0620: if (listeners != null && version != this .version) {
0621: firePropertyChangeImpl(listeners, languageVersionProperty,
0622: new Integer(this .version), new Integer(version));
0623: }
0624: this .version = version;
0625: }
0626:
0627: public static boolean isValidLanguageVersion(int version) {
0628: switch (version) {
0629: case VERSION_DEFAULT:
0630: case VERSION_1_0:
0631: case VERSION_1_1:
0632: case VERSION_1_2:
0633: case VERSION_1_3:
0634: case VERSION_1_4:
0635: case VERSION_1_5:
0636: case VERSION_1_6:
0637: case VERSION_1_7:
0638: return true;
0639: }
0640: return false;
0641: }
0642:
0643: public static void checkLanguageVersion(int version) {
0644: if (isValidLanguageVersion(version)) {
0645: return;
0646: }
0647: throw new IllegalArgumentException("Bad language version: "
0648: + version);
0649: }
0650:
0651: /**
0652: * Get the implementation version.
0653: *
0654: * <p>
0655: * The implementation version is of the form
0656: * <pre>
0657: * "<i>name langVer</i> <code>release</code> <i>relNum date</i>"
0658: * </pre>
0659: * where <i>name</i> is the name of the product, <i>langVer</i> is
0660: * the language version, <i>relNum</i> is the release number, and
0661: * <i>date</i> is the release date for that specific
0662: * release in the form "yyyy mm dd".
0663: *
0664: * @return a string that encodes the product, language version, release
0665: * number, and date.
0666: */
0667: public final String getImplementationVersion() {
0668: // XXX Probably it would be better to embed this directly into source
0669: // with special build preprocessing but that would require some ant
0670: // tweaking and then replacing token in resource files was simpler
0671: if (implementationVersion == null) {
0672: implementationVersion = ScriptRuntime
0673: .getMessage0("implementation.version");
0674: }
0675: return implementationVersion;
0676: }
0677:
0678: /**
0679: * Get the current error reporter.
0680: *
0681: * @see org.mozilla.javascript.ErrorReporter
0682: */
0683: public final ErrorReporter getErrorReporter() {
0684: if (errorReporter == null) {
0685: return DefaultErrorReporter.instance;
0686: }
0687: return errorReporter;
0688: }
0689:
0690: /**
0691: * Change the current error reporter.
0692: *
0693: * @return the previous error reporter
0694: * @see org.mozilla.javascript.ErrorReporter
0695: */
0696: public final ErrorReporter setErrorReporter(ErrorReporter reporter) {
0697: if (sealed)
0698: onSealedMutation();
0699: if (reporter == null)
0700: throw new IllegalArgumentException();
0701: ErrorReporter old = getErrorReporter();
0702: if (reporter == old) {
0703: return old;
0704: }
0705: Object listeners = propertyListeners;
0706: if (listeners != null) {
0707: firePropertyChangeImpl(listeners, errorReporterProperty,
0708: old, reporter);
0709: }
0710: this .errorReporter = reporter;
0711: return old;
0712: }
0713:
0714: /**
0715: * Get the current locale. Returns the default locale if none has
0716: * been set.
0717: *
0718: * @see java.util.Locale
0719: */
0720:
0721: public final Locale getLocale() {
0722: if (locale == null)
0723: locale = Locale.getDefault();
0724: return locale;
0725: }
0726:
0727: /**
0728: * Set the current locale.
0729: *
0730: * @see java.util.Locale
0731: */
0732: public final Locale setLocale(Locale loc) {
0733: if (sealed)
0734: onSealedMutation();
0735: Locale result = locale;
0736: locale = loc;
0737: return result;
0738: }
0739:
0740: /**
0741: * Register an object to receive notifications when a bound property
0742: * has changed
0743: * @see java.beans.PropertyChangeEvent
0744: * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
0745: * @param l the listener
0746: */
0747: public final void addPropertyChangeListener(PropertyChangeListener l) {
0748: if (sealed)
0749: onSealedMutation();
0750: propertyListeners = Kit.addListener(propertyListeners, l);
0751: }
0752:
0753: /**
0754: * Remove an object from the list of objects registered to receive
0755: * notification of changes to a bounded property
0756: * @see java.beans.PropertyChangeEvent
0757: * @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
0758: * @param l the listener
0759: */
0760: public final void removePropertyChangeListener(
0761: PropertyChangeListener l) {
0762: if (sealed)
0763: onSealedMutation();
0764: propertyListeners = Kit.removeListener(propertyListeners, l);
0765: }
0766:
0767: /**
0768: * Notify any registered listeners that a bounded property has changed
0769: * @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
0770: * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
0771: * @see java.beans.PropertyChangeListener
0772: * @see java.beans.PropertyChangeEvent
0773: * @param property the bound property
0774: * @param oldValue the old value
0775: * @param newValue the new value
0776: */
0777: final void firePropertyChange(String property, Object oldValue,
0778: Object newValue) {
0779: Object listeners = propertyListeners;
0780: if (listeners != null) {
0781: firePropertyChangeImpl(listeners, property, oldValue,
0782: newValue);
0783: }
0784: }
0785:
0786: private void firePropertyChangeImpl(Object listeners,
0787: String property, Object oldValue, Object newValue) {
0788: for (int i = 0;; ++i) {
0789: Object l = Kit.getListener(listeners, i);
0790: if (l == null)
0791: break;
0792: if (l instanceof PropertyChangeListener) {
0793: PropertyChangeListener pcl = (PropertyChangeListener) l;
0794: pcl.propertyChange(new PropertyChangeEvent(this ,
0795: property, oldValue, newValue));
0796: }
0797: }
0798: }
0799:
0800: /**
0801: * Report a warning using the error reporter for the current thread.
0802: *
0803: * @param message the warning message to report
0804: * @param sourceName a string describing the source, such as a filename
0805: * @param lineno the starting line number
0806: * @param lineSource the text of the line (may be null)
0807: * @param lineOffset the offset into lineSource where problem was detected
0808: * @see org.mozilla.javascript.ErrorReporter
0809: */
0810: public static void reportWarning(String message, String sourceName,
0811: int lineno, String lineSource, int lineOffset) {
0812: Context cx = Context.getContext();
0813: if (cx.hasFeature(FEATURE_WARNING_AS_ERROR))
0814: reportError(message, sourceName, lineno, lineSource,
0815: lineOffset);
0816: else
0817: cx.getErrorReporter().warning(message, sourceName, lineno,
0818: lineSource, lineOffset);
0819: }
0820:
0821: /**
0822: * Report a warning using the error reporter for the current thread.
0823: *
0824: * @param message the warning message to report
0825: * @see org.mozilla.javascript.ErrorReporter
0826: */
0827: public static void reportWarning(String message) {
0828: int[] linep = { 0 };
0829: String filename = getSourcePositionFromStack(linep);
0830: Context.reportWarning(message, filename, linep[0], null, 0);
0831: }
0832:
0833: public static void reportWarning(String message, Throwable t) {
0834: int[] linep = { 0 };
0835: String filename = getSourcePositionFromStack(linep);
0836: Writer sw = new StringWriter();
0837: PrintWriter pw = new PrintWriter(sw);
0838: pw.println(message);
0839: t.printStackTrace(pw);
0840: pw.flush();
0841: Context.reportWarning(sw.toString(), filename, linep[0], null,
0842: 0);
0843: }
0844:
0845: /**
0846: * Report an error using the error reporter for the current thread.
0847: *
0848: * @param message the error message to report
0849: * @param sourceName a string describing the source, such as a filename
0850: * @param lineno the starting line number
0851: * @param lineSource the text of the line (may be null)
0852: * @param lineOffset the offset into lineSource where problem was detected
0853: * @see org.mozilla.javascript.ErrorReporter
0854: */
0855: public static void reportError(String message, String sourceName,
0856: int lineno, String lineSource, int lineOffset) {
0857: Context cx = getCurrentContext();
0858: if (cx != null) {
0859: cx.getErrorReporter().error(message, sourceName, lineno,
0860: lineSource, lineOffset);
0861: } else {
0862: throw new EvaluatorException(message, sourceName, lineno,
0863: lineSource, lineOffset);
0864: }
0865: }
0866:
0867: /**
0868: * Report an error using the error reporter for the current thread.
0869: *
0870: * @param message the error message to report
0871: * @see org.mozilla.javascript.ErrorReporter
0872: */
0873: public static void reportError(String message) {
0874: int[] linep = { 0 };
0875: String filename = getSourcePositionFromStack(linep);
0876: Context.reportError(message, filename, linep[0], null, 0);
0877: }
0878:
0879: /**
0880: * Report a runtime error using the error reporter for the current thread.
0881: *
0882: * @param message the error message to report
0883: * @param sourceName a string describing the source, such as a filename
0884: * @param lineno the starting line number
0885: * @param lineSource the text of the line (may be null)
0886: * @param lineOffset the offset into lineSource where problem was detected
0887: * @return a runtime exception that will be thrown to terminate the
0888: * execution of the script
0889: * @see org.mozilla.javascript.ErrorReporter
0890: */
0891: public static EvaluatorException reportRuntimeError(String message,
0892: String sourceName, int lineno, String lineSource,
0893: int lineOffset) {
0894: Context cx = getCurrentContext();
0895: if (cx != null) {
0896: return cx.getErrorReporter().runtimeError(message,
0897: sourceName, lineno, lineSource, lineOffset);
0898: } else {
0899: throw new EvaluatorException(message, sourceName, lineno,
0900: lineSource, lineOffset);
0901: }
0902: }
0903:
0904: static EvaluatorException reportRuntimeError0(String messageId) {
0905: String msg = ScriptRuntime.getMessage0(messageId);
0906: return reportRuntimeError(msg);
0907: }
0908:
0909: static EvaluatorException reportRuntimeError1(String messageId,
0910: Object arg1) {
0911: String msg = ScriptRuntime.getMessage1(messageId, arg1);
0912: return reportRuntimeError(msg);
0913: }
0914:
0915: static EvaluatorException reportRuntimeError2(String messageId,
0916: Object arg1, Object arg2) {
0917: String msg = ScriptRuntime.getMessage2(messageId, arg1, arg2);
0918: return reportRuntimeError(msg);
0919: }
0920:
0921: static EvaluatorException reportRuntimeError3(String messageId,
0922: Object arg1, Object arg2, Object arg3) {
0923: String msg = ScriptRuntime.getMessage3(messageId, arg1, arg2,
0924: arg3);
0925: return reportRuntimeError(msg);
0926: }
0927:
0928: static EvaluatorException reportRuntimeError4(String messageId,
0929: Object arg1, Object arg2, Object arg3, Object arg4) {
0930: String msg = ScriptRuntime.getMessage4(messageId, arg1, arg2,
0931: arg3, arg4);
0932: return reportRuntimeError(msg);
0933: }
0934:
0935: /**
0936: * Report a runtime error using the error reporter for the current thread.
0937: *
0938: * @param message the error message to report
0939: * @see org.mozilla.javascript.ErrorReporter
0940: */
0941: public static EvaluatorException reportRuntimeError(String message) {
0942: int[] linep = { 0 };
0943: String filename = getSourcePositionFromStack(linep);
0944: return Context.reportRuntimeError(message, filename, linep[0],
0945: null, 0);
0946: }
0947:
0948: /**
0949: * Initialize the standard objects.
0950: *
0951: * Creates instances of the standard objects and their constructors
0952: * (Object, String, Number, Date, etc.), setting up 'scope' to act
0953: * as a global object as in ECMA 15.1.<p>
0954: *
0955: * This method must be called to initialize a scope before scripts
0956: * can be evaluated in that scope.<p>
0957: *
0958: * This method does not affect the Context it is called upon.
0959: *
0960: * @return the initialized scope
0961: */
0962: public final ScriptableObject initStandardObjects() {
0963: return initStandardObjects(null, false);
0964: }
0965:
0966: /**
0967: * Initialize the standard objects.
0968: *
0969: * Creates instances of the standard objects and their constructors
0970: * (Object, String, Number, Date, etc.), setting up 'scope' to act
0971: * as a global object as in ECMA 15.1.<p>
0972: *
0973: * This method must be called to initialize a scope before scripts
0974: * can be evaluated in that scope.<p>
0975: *
0976: * This method does not affect the Context it is called upon.
0977: *
0978: * @param scope the scope to initialize, or null, in which case a new
0979: * object will be created to serve as the scope
0980: * @return the initialized scope. The method returns the value of the scope
0981: * argument if it is not null or newly allocated scope object which
0982: * is an instance {@link ScriptableObject}.
0983: */
0984: public final Scriptable initStandardObjects(ScriptableObject scope) {
0985: return initStandardObjects(scope, false);
0986: }
0987:
0988: /**
0989: * Initialize the standard objects.
0990: *
0991: * Creates instances of the standard objects and their constructors
0992: * (Object, String, Number, Date, etc.), setting up 'scope' to act
0993: * as a global object as in ECMA 15.1.<p>
0994: *
0995: * This method must be called to initialize a scope before scripts
0996: * can be evaluated in that scope.<p>
0997: *
0998: * This method does not affect the Context it is called upon.<p>
0999: *
1000: * This form of the method also allows for creating "sealed" standard
1001: * objects. An object that is sealed cannot have properties added, changed,
1002: * or removed. This is useful to create a "superglobal" that can be shared
1003: * among several top-level objects. Note that sealing is not allowed in
1004: * the current ECMA/ISO language specification, but is likely for
1005: * the next version.
1006: *
1007: * @param scope the scope to initialize, or null, in which case a new
1008: * object will be created to serve as the scope
1009: * @param sealed whether or not to create sealed standard objects that
1010: * cannot be modified.
1011: * @return the initialized scope. The method returns the value of the scope
1012: * argument if it is not null or newly allocated scope object.
1013: * @since 1.4R3
1014: */
1015: public ScriptableObject initStandardObjects(ScriptableObject scope,
1016: boolean sealed) {
1017: return ScriptRuntime.initStandardObjects(this , scope, sealed);
1018: }
1019:
1020: /**
1021: * Get the singleton object that represents the JavaScript Undefined value.
1022: */
1023: public static Object getUndefinedValue() {
1024: return Undefined.instance;
1025: }
1026:
1027: /**
1028: * Evaluate a JavaScript source string.
1029: *
1030: * The provided source name and line number are used for error messages
1031: * and for producing debug information.
1032: *
1033: * @param scope the scope to execute in
1034: * @param source the JavaScript source
1035: * @param sourceName a string describing the source, such as a filename
1036: * @param lineno the starting line number
1037: * @param securityDomain an arbitrary object that specifies security
1038: * information about the origin or owner of the script. For
1039: * implementations that don't care about security, this value
1040: * may be null.
1041: * @return the result of evaluating the string
1042: * @see org.mozilla.javascript.SecurityController
1043: */
1044: public final Object evaluateString(Scriptable scope, String source,
1045: String sourceName, int lineno, Object securityDomain) {
1046: Script script = compileString(source, sourceName, lineno,
1047: securityDomain);
1048: if (script != null) {
1049: return script.exec(this , scope);
1050: } else {
1051: return null;
1052: }
1053: }
1054:
1055: /**
1056: * Evaluate a reader as JavaScript source.
1057: *
1058: * All characters of the reader are consumed.
1059: *
1060: * @param scope the scope to execute in
1061: * @param in the Reader to get JavaScript source from
1062: * @param sourceName a string describing the source, such as a filename
1063: * @param lineno the starting line number
1064: * @param securityDomain an arbitrary object that specifies security
1065: * information about the origin or owner of the script. For
1066: * implementations that don't care about security, this value
1067: * may be null.
1068: * @return the result of evaluating the source
1069: *
1070: * @exception IOException if an IOException was generated by the Reader
1071: */
1072: public final Object evaluateReader(Scriptable scope, Reader in,
1073: String sourceName, int lineno, Object securityDomain)
1074: throws IOException {
1075: Script script = compileReader(scope, in, sourceName, lineno,
1076: securityDomain);
1077: if (script != null) {
1078: return script.exec(this , scope);
1079: } else {
1080: return null;
1081: }
1082: }
1083:
1084: /**
1085: * Check whether a string is ready to be compiled.
1086: * <p>
1087: * stringIsCompilableUnit is intended to support interactive compilation of
1088: * javascript. If compiling the string would result in an error
1089: * that might be fixed by appending more source, this method
1090: * returns false. In every other case, it returns true.
1091: * <p>
1092: * Interactive shells may accumulate source lines, using this
1093: * method after each new line is appended to check whether the
1094: * statement being entered is complete.
1095: *
1096: * @param source the source buffer to check
1097: * @return whether the source is ready for compilation
1098: * @since 1.4 Release 2
1099: */
1100: public final boolean stringIsCompilableUnit(String source) {
1101: boolean errorseen = false;
1102: CompilerEnvirons compilerEnv = new CompilerEnvirons();
1103: compilerEnv.initFromContext(this );
1104: // no source name or source text manager, because we're just
1105: // going to throw away the result.
1106: compilerEnv.setGeneratingSource(false);
1107: Parser p = new Parser(compilerEnv,
1108: DefaultErrorReporter.instance);
1109: try {
1110: p.parse(source, null, 1);
1111: } catch (EvaluatorException ee) {
1112: errorseen = true;
1113: }
1114: // Return false only if an error occurred as a result of reading past
1115: // the end of the file, i.e. if the source could be fixed by
1116: // appending more source.
1117: if (errorseen && p.eof())
1118: return false;
1119: else
1120: return true;
1121: }
1122:
1123: /**
1124: * @deprecated
1125: * @see #compileReader(Reader in, String sourceName, int lineno,
1126: * Object securityDomain)
1127: */
1128: public final Script compileReader(Scriptable scope, Reader in,
1129: String sourceName, int lineno, Object securityDomain)
1130: throws IOException {
1131: return compileReader(in, sourceName, lineno, securityDomain);
1132: }
1133:
1134: /**
1135: * Compiles the source in the given reader.
1136: * <p>
1137: * Returns a script that may later be executed.
1138: * Will consume all the source in the reader.
1139: *
1140: * @param in the input reader
1141: * @param sourceName a string describing the source, such as a filename
1142: * @param lineno the starting line number for reporting errors
1143: * @param securityDomain an arbitrary object that specifies security
1144: * information about the origin or owner of the script. For
1145: * implementations that don't care about security, this value
1146: * may be null.
1147: * @return a script that may later be executed
1148: * @exception IOException if an IOException was generated by the Reader
1149: * @see org.mozilla.javascript.Script
1150: */
1151: public final Script compileReader(Reader in, String sourceName,
1152: int lineno, Object securityDomain) throws IOException {
1153: if (lineno < 0) {
1154: // For compatibility IllegalArgumentException can not be thrown here
1155: lineno = 0;
1156: }
1157: return (Script) compileImpl(null, in, null, sourceName, lineno,
1158: securityDomain, false, null, null);
1159: }
1160:
1161: /**
1162: * Compiles the source in the given string.
1163: * <p>
1164: * Returns a script that may later be executed.
1165: *
1166: * @param source the source string
1167: * @param sourceName a string describing the source, such as a filename
1168: * @param lineno the starting line number for reporting errors
1169: * @param securityDomain an arbitrary object that specifies security
1170: * information about the origin or owner of the script. For
1171: * implementations that don't care about security, this value
1172: * may be null.
1173: * @return a script that may later be executed
1174: * @see org.mozilla.javascript.Script
1175: */
1176: public final Script compileString(String source, String sourceName,
1177: int lineno, Object securityDomain) {
1178: if (lineno < 0) {
1179: // For compatibility IllegalArgumentException can not be thrown here
1180: lineno = 0;
1181: }
1182: return compileString(source, null, null, sourceName, lineno,
1183: securityDomain);
1184: }
1185:
1186: final Script compileString(String source, Evaluator compiler,
1187: ErrorReporter compilationErrorReporter, String sourceName,
1188: int lineno, Object securityDomain) {
1189: try {
1190: return (Script) compileImpl(null, null, source, sourceName,
1191: lineno, securityDomain, false, compiler,
1192: compilationErrorReporter);
1193: } catch (IOException ex) {
1194: // Should not happen when dealing with source as string
1195: throw new RuntimeException();
1196: }
1197: }
1198:
1199: /**
1200: * Compile a JavaScript function.
1201: * <p>
1202: * The function source must be a function definition as defined by
1203: * ECMA (e.g., "function f(a) { return a; }").
1204: *
1205: * @param scope the scope to compile relative to
1206: * @param source the function definition source
1207: * @param sourceName a string describing the source, such as a filename
1208: * @param lineno the starting line number
1209: * @param securityDomain an arbitrary object that specifies security
1210: * information about the origin or owner of the script. For
1211: * implementations that don't care about security, this value
1212: * may be null.
1213: * @return a Function that may later be called
1214: * @see org.mozilla.javascript.Function
1215: */
1216: public final Function compileFunction(Scriptable scope,
1217: String source, String sourceName, int lineno,
1218: Object securityDomain) {
1219: return compileFunction(scope, source, null, null, sourceName,
1220: lineno, securityDomain);
1221: }
1222:
1223: final Function compileFunction(Scriptable scope, String source,
1224: Evaluator compiler, ErrorReporter compilationErrorReporter,
1225: String sourceName, int lineno, Object securityDomain) {
1226: try {
1227: return (Function) compileImpl(scope, null, source,
1228: sourceName, lineno, securityDomain, true, compiler,
1229: compilationErrorReporter);
1230: } catch (IOException ioe) {
1231: // Should never happen because we just made the reader
1232: // from a String
1233: throw new RuntimeException();
1234: }
1235: }
1236:
1237: /**
1238: * Decompile the script.
1239: * <p>
1240: * The canonical source of the script is returned.
1241: *
1242: * @param script the script to decompile
1243: * @param indent the number of spaces to indent the result
1244: * @return a string representing the script source
1245: */
1246: public final String decompileScript(Script script, int indent) {
1247: NativeFunction scriptImpl = (NativeFunction) script;
1248: return scriptImpl.decompile(indent, 0);
1249: }
1250:
1251: /**
1252: * Decompile a JavaScript Function.
1253: * <p>
1254: * Decompiles a previously compiled JavaScript function object to
1255: * canonical source.
1256: * <p>
1257: * Returns function body of '[native code]' if no decompilation
1258: * information is available.
1259: *
1260: * @param fun the JavaScript function to decompile
1261: * @param indent the number of spaces to indent the result
1262: * @return a string representing the function source
1263: */
1264: public final String decompileFunction(Function fun, int indent) {
1265: if (fun instanceof BaseFunction)
1266: return ((BaseFunction) fun).decompile(indent, 0);
1267: else
1268: return "function " + fun.getClassName()
1269: + "() {\n\t[native code]\n}\n";
1270: }
1271:
1272: /**
1273: * Decompile the body of a JavaScript Function.
1274: * <p>
1275: * Decompiles the body a previously compiled JavaScript Function
1276: * object to canonical source, omitting the function header and
1277: * trailing brace.
1278: *
1279: * Returns '[native code]' if no decompilation information is available.
1280: *
1281: * @param fun the JavaScript function to decompile
1282: * @param indent the number of spaces to indent the result
1283: * @return a string representing the function body source.
1284: */
1285: public final String decompileFunctionBody(Function fun, int indent) {
1286: if (fun instanceof BaseFunction) {
1287: BaseFunction bf = (BaseFunction) fun;
1288: return bf.decompile(indent, Decompiler.ONLY_BODY_FLAG);
1289: }
1290: // ALERT: not sure what the right response here is.
1291: return "[native code]\n";
1292: }
1293:
1294: /**
1295: * Create a new JavaScript object.
1296: *
1297: * Equivalent to evaluating "new Object()".
1298: * @param scope the scope to search for the constructor and to evaluate
1299: * against
1300: * @return the new object
1301: */
1302: public final Scriptable newObject(Scriptable scope) {
1303: return newObject(scope, "Object", ScriptRuntime.emptyArgs);
1304: }
1305:
1306: /**
1307: * Create a new JavaScript object by executing the named constructor.
1308: *
1309: * The call <code>newObject(scope, "Foo")</code> is equivalent to
1310: * evaluating "new Foo()".
1311: *
1312: * @param scope the scope to search for the constructor and to evaluate against
1313: * @param constructorName the name of the constructor to call
1314: * @return the new object
1315: */
1316: public final Scriptable newObject(Scriptable scope,
1317: String constructorName) {
1318: return newObject(scope, constructorName,
1319: ScriptRuntime.emptyArgs);
1320: }
1321:
1322: /**
1323: * Creates a new JavaScript object by executing the named constructor.
1324: *
1325: * Searches <code>scope</code> for the named constructor, calls it with
1326: * the given arguments, and returns the result.<p>
1327: *
1328: * The code
1329: * <pre>
1330: * Object[] args = { "a", "b" };
1331: * newObject(scope, "Foo", args)</pre>
1332: * is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo
1333: * constructor has been defined in <code>scope</code>.
1334: *
1335: * @param scope The scope to search for the constructor and to evaluate
1336: * against
1337: * @param constructorName the name of the constructor to call
1338: * @param args the array of arguments for the constructor
1339: * @return the new object
1340: */
1341: public final Scriptable newObject(Scriptable scope,
1342: String constructorName, Object[] args) {
1343: scope = ScriptableObject.getTopLevelScope(scope);
1344: Function ctor = ScriptRuntime.getExistingCtor(this , scope,
1345: constructorName);
1346: if (args == null) {
1347: args = ScriptRuntime.emptyArgs;
1348: }
1349: return ctor.construct(this , scope, args);
1350: }
1351:
1352: /**
1353: * Create an array with a specified initial length.
1354: * <p>
1355: * @param scope the scope to create the object in
1356: * @param length the initial length (JavaScript arrays may have
1357: * additional properties added dynamically).
1358: * @return the new array object
1359: */
1360: public final Scriptable newArray(Scriptable scope, int length) {
1361: NativeArray result = new NativeArray(length);
1362: ScriptRuntime.setObjectProtoAndParent(result, scope);
1363: return result;
1364: }
1365:
1366: /**
1367: * Create an array with a set of initial elements.
1368: *
1369: * @param scope the scope to create the object in.
1370: * @param elements the initial elements. Each object in this array
1371: * must be an acceptable JavaScript type and type
1372: * of array should be exactly Object[], not
1373: * SomeObjectSubclass[].
1374: * @return the new array object.
1375: */
1376: public final Scriptable newArray(Scriptable scope, Object[] elements) {
1377: if (elements.getClass().getComponentType() != ScriptRuntime.ObjectClass)
1378: throw new IllegalArgumentException();
1379: NativeArray result = new NativeArray(elements);
1380: ScriptRuntime.setObjectProtoAndParent(result, scope);
1381: return result;
1382: }
1383:
1384: /**
1385: * Get the elements of a JavaScript array.
1386: * <p>
1387: * If the object defines a length property convertible to double number,
1388: * then the number is converted Uint32 value as defined in Ecma 9.6
1389: * and Java array of that size is allocated.
1390: * The array is initialized with the values obtained by
1391: * calling get() on object for each value of i in [0,length-1]. If
1392: * there is not a defined value for a property the Undefined value
1393: * is used to initialize the corresponding element in the array. The
1394: * Java array is then returned.
1395: * If the object doesn't define a length property or it is not a number,
1396: * empty array is returned.
1397: * @param object the JavaScript array or array-like object
1398: * @return a Java array of objects
1399: * @since 1.4 release 2
1400: */
1401: public final Object[] getElements(Scriptable object) {
1402: return ScriptRuntime.getArrayElements(object);
1403: }
1404:
1405: /**
1406: * Convert the value to a JavaScript boolean value.
1407: * <p>
1408: * See ECMA 9.2.
1409: *
1410: * @param value a JavaScript value
1411: * @return the corresponding boolean value converted using
1412: * the ECMA rules
1413: */
1414: public static boolean toBoolean(Object value) {
1415: return ScriptRuntime.toBoolean(value);
1416: }
1417:
1418: /**
1419: * Convert the value to a JavaScript Number value.
1420: * <p>
1421: * Returns a Java double for the JavaScript Number.
1422: * <p>
1423: * See ECMA 9.3.
1424: *
1425: * @param value a JavaScript value
1426: * @return the corresponding double value converted using
1427: * the ECMA rules
1428: */
1429: public static double toNumber(Object value) {
1430: return ScriptRuntime.toNumber(value);
1431: }
1432:
1433: /**
1434: * Convert the value to a JavaScript String value.
1435: * <p>
1436: * See ECMA 9.8.
1437: * <p>
1438: * @param value a JavaScript value
1439: * @return the corresponding String value converted using
1440: * the ECMA rules
1441: */
1442: public static String toString(Object value) {
1443: return ScriptRuntime.toString(value);
1444: }
1445:
1446: /**
1447: * Convert the value to an JavaScript object value.
1448: * <p>
1449: * Note that a scope must be provided to look up the constructors
1450: * for Number, Boolean, and String.
1451: * <p>
1452: * See ECMA 9.9.
1453: * <p>
1454: * Additionally, arbitrary Java objects and classes will be
1455: * wrapped in a Scriptable object with its Java fields and methods
1456: * reflected as JavaScript properties of the object.
1457: *
1458: * @param value any Java object
1459: * @param scope global scope containing constructors for Number,
1460: * Boolean, and String
1461: * @return new JavaScript object
1462: */
1463: public static Scriptable toObject(Object value, Scriptable scope) {
1464: return ScriptRuntime.toObject(scope, value);
1465: }
1466:
1467: /**
1468: * @deprecated
1469: * @see #toObject(Object, Scriptable)
1470: */
1471: public static Scriptable toObject(Object value, Scriptable scope,
1472: Class staticType) {
1473: return ScriptRuntime.toObject(scope, value);
1474: }
1475:
1476: /**
1477: * Convenient method to convert java value to its closest representation
1478: * in JavaScript.
1479: * <p>
1480: * If value is an instance of String, Number, Boolean, Function or
1481: * Scriptable, it is returned as it and will be treated as the corresponding
1482: * JavaScript type of string, number, boolean, function and object.
1483: * <p>
1484: * Note that for Number instances during any arithmetic operation in
1485: * JavaScript the engine will always use the result of
1486: * <tt>Number.doubleValue()</tt> resulting in a precision loss if
1487: * the number can not fit into double.
1488: * <p>
1489: * If value is an instance of Character, it will be converted to string of
1490: * length 1 and its JavaScript type will be string.
1491: * <p>
1492: * The rest of values will be wrapped as LiveConnect objects
1493: * by calling {@link WrapFactory#wrap(Context cx, Scriptable scope,
1494: * Object obj, Class staticType)} as in:
1495: * <pre>
1496: * Context cx = Context.getCurrentContext();
1497: * return cx.getWrapFactory().wrap(cx, scope, value, null);
1498: * </pre>
1499: *
1500: * @param value any Java object
1501: * @param scope top scope object
1502: * @return value suitable to pass to any API that takes JavaScript values.
1503: */
1504: public static Object javaToJS(Object value, Scriptable scope) {
1505: if (value instanceof String || value instanceof Number
1506: || value instanceof Boolean
1507: || value instanceof Scriptable) {
1508: return value;
1509: } else if (value instanceof Character) {
1510: return String.valueOf(((Character) value).charValue());
1511: } else {
1512: Context cx = Context.getContext();
1513: return cx.getWrapFactory().wrap(cx, scope, value, null);
1514: }
1515: }
1516:
1517: /**
1518: * Convert a JavaScript value into the desired type.
1519: * Uses the semantics defined with LiveConnect3 and throws an
1520: * Illegal argument exception if the conversion cannot be performed.
1521: * @param value the JavaScript value to convert
1522: * @param desiredType the Java type to convert to. Primitive Java
1523: * types are represented using the TYPE fields in the corresponding
1524: * wrapper class in java.lang.
1525: * @return the converted value
1526: * @throws EvaluatorException if the conversion cannot be performed
1527: */
1528: public static Object jsToJava(Object value, Class desiredType)
1529: throws EvaluatorException {
1530: return NativeJavaObject.coerceTypeImpl(desiredType, value);
1531: }
1532:
1533: /**
1534: * @deprecated
1535: * @see #jsToJava(Object, Class)
1536: * @throws IllegalArgumentException if the conversion cannot be performed.
1537: * Note that {@link #jsToJava(Object, Class)} throws
1538: * {@link EvaluatorException} instead.
1539: */
1540: public static Object toType(Object value, Class desiredType)
1541: throws IllegalArgumentException {
1542: try {
1543: return jsToJava(value, desiredType);
1544: } catch (EvaluatorException ex) {
1545: IllegalArgumentException ex2 = new IllegalArgumentException(
1546: ex.getMessage());
1547: Kit.initCause(ex2, ex);
1548: throw ex2;
1549: }
1550: }
1551:
1552: /**
1553: * Rethrow the exception wrapping it as the script runtime exception.
1554: * Unless the exception is instance of {@link EcmaError} or
1555: * {@link EvaluatorException} it will be wrapped as
1556: * {@link WrappedException}, a subclass of {@link EvaluatorException}.
1557: * The resulting exception object always contains
1558: * source name and line number of script that triggered exception.
1559: * <p>
1560: * This method always throws an exception, its return value is provided
1561: * only for convenience to allow a usage like:
1562: * <pre>
1563: * throw Context.throwAsScriptRuntimeEx(ex);
1564: * </pre>
1565: * to indicate that code after the method is unreachable.
1566: * @throws EvaluatorException
1567: * @throws EcmaError
1568: */
1569: public static RuntimeException throwAsScriptRuntimeEx(Throwable e) {
1570: while ((e instanceof InvocationTargetException)) {
1571: e = ((InvocationTargetException) e).getTargetException();
1572: }
1573: // special handling of Error so scripts would not catch them
1574: if (e instanceof Error) {
1575: Context cx = getContext();
1576: if (cx == null
1577: || !cx
1578: .hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) {
1579: throw (Error) e;
1580: }
1581: }
1582: if (e instanceof RhinoException) {
1583: throw (RhinoException) e;
1584: }
1585: throw new WrappedException(e);
1586: }
1587:
1588: /**
1589: * Tell whether debug information is being generated.
1590: * @since 1.3
1591: */
1592: public final boolean isGeneratingDebug() {
1593: return generatingDebug;
1594: }
1595:
1596: /**
1597: * Specify whether or not debug information should be generated.
1598: * <p>
1599: * Setting the generation of debug information on will set the
1600: * optimization level to zero.
1601: * @since 1.3
1602: */
1603: public final void setGeneratingDebug(boolean generatingDebug) {
1604: if (sealed)
1605: onSealedMutation();
1606: generatingDebugChanged = true;
1607: if (generatingDebug && getOptimizationLevel() > 0)
1608: setOptimizationLevel(0);
1609: this .generatingDebug = generatingDebug;
1610: }
1611:
1612: /**
1613: * Tell whether source information is being generated.
1614: * @since 1.3
1615: */
1616: public final boolean isGeneratingSource() {
1617: return generatingSource;
1618: }
1619:
1620: /**
1621: * Specify whether or not source information should be generated.
1622: * <p>
1623: * Without source information, evaluating the "toString" method
1624: * on JavaScript functions produces only "[native code]" for
1625: * the body of the function.
1626: * Note that code generated without source is not fully ECMA
1627: * conformant.
1628: * @since 1.3
1629: */
1630: public final void setGeneratingSource(boolean generatingSource) {
1631: if (sealed)
1632: onSealedMutation();
1633: this .generatingSource = generatingSource;
1634: }
1635:
1636: /**
1637: * Get the current optimization level.
1638: * <p>
1639: * The optimization level is expressed as an integer between -1 and
1640: * 9.
1641: * @since 1.3
1642: *
1643: */
1644: public final int getOptimizationLevel() {
1645: return optimizationLevel;
1646: }
1647:
1648: /**
1649: * Set the current optimization level.
1650: * <p>
1651: * The optimization level is expected to be an integer between -1 and
1652: * 9. Any negative values will be interpreted as -1, and any values
1653: * greater than 9 will be interpreted as 9.
1654: * An optimization level of -1 indicates that interpretive mode will
1655: * always be used. Levels 0 through 9 indicate that class files may
1656: * be generated. Higher optimization levels trade off compile time
1657: * performance for runtime performance.
1658: * The optimizer level can't be set greater than -1 if the optimizer
1659: * package doesn't exist at run time.
1660: * @param optimizationLevel an integer indicating the level of
1661: * optimization to perform
1662: * @since 1.3
1663: *
1664: */
1665: public final void setOptimizationLevel(int optimizationLevel) {
1666: if (sealed)
1667: onSealedMutation();
1668: if (optimizationLevel == -2) {
1669: // To be compatible with Cocoon fork
1670: optimizationLevel = -1;
1671: }
1672: checkOptimizationLevel(optimizationLevel);
1673: if (codegenClass == null)
1674: optimizationLevel = -1;
1675: this .optimizationLevel = optimizationLevel;
1676: }
1677:
1678: public static boolean isValidOptimizationLevel(int optimizationLevel) {
1679: return -1 <= optimizationLevel && optimizationLevel <= 9;
1680: }
1681:
1682: public static void checkOptimizationLevel(int optimizationLevel) {
1683: if (isValidOptimizationLevel(optimizationLevel)) {
1684: return;
1685: }
1686: throw new IllegalArgumentException(
1687: "Optimization level outside [-1..9]: "
1688: + optimizationLevel);
1689: }
1690:
1691: /**
1692: * Returns the maximum stack depth (in terms of number of call frames)
1693: * allowed in a single invocation of interpreter. If the set depth would be
1694: * exceeded, the interpreter will throw an EvaluatorException in the script.
1695: * Defaults to Integer.MAX_VALUE. The setting only has effect for
1696: * interpreted functions (those compiled with optimization level set to -1).
1697: * As the interpreter doesn't use the Java stack but rather manages its own
1698: * stack in the heap memory, a runaway recursion in interpreted code would
1699: * eventually consume all available memory and cause OutOfMemoryError
1700: * instead of a StackOverflowError limited to only a single thread. This
1701: * setting helps prevent such situations.
1702: *
1703: * @return The current maximum interpreter stack depth.
1704: */
1705: public final int getMaximumInterpreterStackDepth() {
1706: return maximumInterpreterStackDepth;
1707: }
1708:
1709: /**
1710: * Sets the maximum stack depth (in terms of number of call frames)
1711: * allowed in a single invocation of interpreter. If the set depth would be
1712: * exceeded, the interpreter will throw an EvaluatorException in the script.
1713: * Defaults to Integer.MAX_VALUE. The setting only has effect for
1714: * interpreted functions (those compiled with optimization level set to -1).
1715: * As the interpreter doesn't use the Java stack but rather manages its own
1716: * stack in the heap memory, a runaway recursion in interpreted code would
1717: * eventually consume all available memory and cause OutOfMemoryError
1718: * instead of a StackOverflowError limited to only a single thread. This
1719: * setting helps prevent such situations.
1720: *
1721: * @param max the new maximum interpreter stack depth
1722: * @throws IllegalStateException if this context's optimization level is not
1723: * -1
1724: * @throws IllegalArgumentException if the new depth is not at least 1
1725: */
1726: public final void setMaximumInterpreterStackDepth(int max) {
1727: if (sealed)
1728: onSealedMutation();
1729: if (optimizationLevel != -1) {
1730: throw new IllegalStateException(
1731: "Cannot set maximumInterpreterStackDepth when optimizationLevel != -1");
1732: }
1733: if (max < 1) {
1734: throw new IllegalArgumentException(
1735: "Cannot set maximumInterpreterStackDepth to less than 1");
1736: }
1737: maximumInterpreterStackDepth = max;
1738: }
1739:
1740: /**
1741: * Set the security controller for this context.
1742: * <p> SecurityController may only be set if it is currently null
1743: * and {@link SecurityController#hasGlobal()} is <tt>false</tt>.
1744: * Otherwise a SecurityException is thrown.
1745: * @param controller a SecurityController object
1746: * @throws SecurityException if there is already a SecurityController
1747: * object for this Context or globally installed.
1748: * @see SecurityController#initGlobal(SecurityController controller)
1749: * @see SecurityController#hasGlobal()
1750: */
1751: public final void setSecurityController(
1752: SecurityController controller) {
1753: if (sealed)
1754: onSealedMutation();
1755: if (controller == null)
1756: throw new IllegalArgumentException();
1757: if (securityController != null) {
1758: throw new SecurityException(
1759: "Can not overwrite existing SecurityController object");
1760: }
1761: if (SecurityController.hasGlobal()) {
1762: throw new SecurityException(
1763: "Can not overwrite existing global SecurityController object");
1764: }
1765: securityController = controller;
1766: }
1767:
1768: /**
1769: * Set the LiveConnect access filter for this context.
1770: * <p> {@link ClassShutter} may only be set if it is currently null.
1771: * Otherwise a SecurityException is thrown.
1772: * @param shutter a ClassShutter object
1773: * @throws SecurityException if there is already a ClassShutter
1774: * object for this Context
1775: */
1776: public final void setClassShutter(ClassShutter shutter) {
1777: if (sealed)
1778: onSealedMutation();
1779: if (shutter == null)
1780: throw new IllegalArgumentException();
1781: if (classShutter != null) {
1782: throw new SecurityException("Cannot overwrite existing "
1783: + "ClassShutter object");
1784: }
1785: classShutter = shutter;
1786: }
1787:
1788: final ClassShutter getClassShutter() {
1789: return classShutter;
1790: }
1791:
1792: /**
1793: * Get a value corresponding to a key.
1794: * <p>
1795: * Since the Context is associated with a thread it can be
1796: * used to maintain values that can be later retrieved using
1797: * the current thread.
1798: * <p>
1799: * Note that the values are maintained with the Context, so
1800: * if the Context is disassociated from the thread the values
1801: * cannot be retrieved. Also, if private data is to be maintained
1802: * in this manner the key should be a java.lang.Object
1803: * whose reference is not divulged to untrusted code.
1804: * @param key the key used to lookup the value
1805: * @return a value previously stored using putThreadLocal.
1806: */
1807: public final Object getThreadLocal(Object key) {
1808: if (hashtable == null)
1809: return null;
1810: return hashtable.get(key);
1811: }
1812:
1813: /**
1814: * Put a value that can later be retrieved using a given key.
1815: * <p>
1816: * @param key the key used to index the value
1817: * @param value the value to save
1818: */
1819: public final void putThreadLocal(Object key, Object value) {
1820: if (sealed)
1821: onSealedMutation();
1822: if (hashtable == null)
1823: hashtable = new Hashtable();
1824: hashtable.put(key, value);
1825: }
1826:
1827: /**
1828: * Remove values from thread-local storage.
1829: * @param key the key for the entry to remove.
1830: * @since 1.5 release 2
1831: */
1832: public final void removeThreadLocal(Object key) {
1833: if (sealed)
1834: onSealedMutation();
1835: if (hashtable == null)
1836: return;
1837: hashtable.remove(key);
1838: }
1839:
1840: /**
1841: * @deprecated
1842: * @see #FEATURE_DYNAMIC_SCOPE
1843: * @see #hasFeature(int)
1844: */
1845: public final boolean hasCompileFunctionsWithDynamicScope() {
1846: return compileFunctionsWithDynamicScopeFlag;
1847: }
1848:
1849: /**
1850: * @deprecated
1851: * @see #FEATURE_DYNAMIC_SCOPE
1852: * @see #hasFeature(int)
1853: */
1854: public final void setCompileFunctionsWithDynamicScope(boolean flag) {
1855: if (sealed)
1856: onSealedMutation();
1857: compileFunctionsWithDynamicScopeFlag = flag;
1858: }
1859:
1860: /**
1861: * @deprecated
1862: * @see ClassCache#get(Scriptable)
1863: * @see ClassCache#setCachingEnabled(boolean)
1864: */
1865: public static void setCachingEnabled(boolean cachingEnabled) {
1866: }
1867:
1868: /**
1869: * Set a WrapFactory for this Context.
1870: * <p>
1871: * The WrapFactory allows custom object wrapping behavior for
1872: * Java object manipulated with JavaScript.
1873: * @see WrapFactory
1874: * @since 1.5 Release 4
1875: */
1876: public final void setWrapFactory(WrapFactory wrapFactory) {
1877: if (sealed)
1878: onSealedMutation();
1879: if (wrapFactory == null)
1880: throw new IllegalArgumentException();
1881: this .wrapFactory = wrapFactory;
1882: }
1883:
1884: /**
1885: * Return the current WrapFactory, or null if none is defined.
1886: * @see WrapFactory
1887: * @since 1.5 Release 4
1888: */
1889: public final WrapFactory getWrapFactory() {
1890: if (wrapFactory == null) {
1891: wrapFactory = new WrapFactory();
1892: }
1893: return wrapFactory;
1894: }
1895:
1896: /**
1897: * Return the current debugger.
1898: * @return the debugger, or null if none is attached.
1899: */
1900: public final Debugger getDebugger() {
1901: return debugger;
1902: }
1903:
1904: /**
1905: * Return the debugger context data associated with current context.
1906: * @return the debugger data, or null if debugger is not attached
1907: */
1908: public final Object getDebuggerContextData() {
1909: return debuggerData;
1910: }
1911:
1912: /**
1913: * Set the associated debugger.
1914: * @param debugger the debugger to be used on callbacks from
1915: * the engine.
1916: * @param contextData arbitrary object that debugger can use to store
1917: * per Context data.
1918: */
1919: public final void setDebugger(Debugger debugger, Object contextData) {
1920: if (sealed)
1921: onSealedMutation();
1922: this .debugger = debugger;
1923: debuggerData = contextData;
1924: }
1925:
1926: /**
1927: * Return DebuggableScript instance if any associated with the script.
1928: * If callable supports DebuggableScript implementation, the method
1929: * returns it. Otherwise null is returned.
1930: */
1931: public static DebuggableScript getDebuggableView(Script script) {
1932: if (script instanceof NativeFunction) {
1933: return ((NativeFunction) script).getDebuggableView();
1934: }
1935: return null;
1936: }
1937:
1938: /**
1939: * Controls certain aspects of script semantics.
1940: * Should be overwritten to alter default behavior.
1941: * <p>
1942: * The default implementation calls
1943: * {@link ContextFactory#hasFeature(Context cx, int featureIndex)}
1944: * that allows to customize Context behavior without introducing
1945: * Context subclasses. {@link ContextFactory} documentation gives
1946: * an example of hasFeature implementation.
1947: *
1948: * @param featureIndex feature index to check
1949: * @return true if the <code>featureIndex</code> feature is turned on
1950: * @see #FEATURE_NON_ECMA_GET_YEAR
1951: * @see #FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME
1952: * @see #FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER
1953: * @see #FEATURE_TO_STRING_AS_SOURCE
1954: * @see #FEATURE_PARENT_PROTO_PROPRTIES
1955: * @see #FEATURE_E4X
1956: * @see #FEATURE_DYNAMIC_SCOPE
1957: * @see #FEATURE_STRICT_VARS
1958: * @see #FEATURE_STRICT_EVAL
1959: * @see #FEATURE_LOCATION_INFORMATION_IN_ERROR
1960: * @see #FEATURE_STRICT_MODE
1961: * @see #FEATURE_WARNING_AS_ERROR
1962: * @see #FEATURE_ENHANCED_JAVA_ACCESS
1963: */
1964: public boolean hasFeature(int featureIndex) {
1965: ContextFactory f = getFactory();
1966: return f.hasFeature(this , featureIndex);
1967: }
1968:
1969: /**
1970: Returns an object which specifies an E4X implementation to use within
1971: this <code>Context</code>. Note
1972: that the XMLLib.Factory interface should be considered experimental.
1973:
1974: The default implementation uses the implementation provided by this
1975: <code>Context</code>'s {@link ContextFactory}.
1976:
1977: @return An XMLLib.Factory. Should not return <code>null</code> if
1978: {@link #FEATURE_E4X} is enabled. See {@link #hasFeature}.
1979: */
1980: public XMLLib.Factory getE4xImplementationFactory() {
1981: return getFactory().getE4xImplementationFactory();
1982: }
1983:
1984: /**
1985: * Get threshold of executed instructions counter that triggers call to
1986: * <code>observeInstructionCount()</code>.
1987: * When the threshold is zero, instruction counting is disabled,
1988: * otherwise each time the run-time executes at least the threshold value
1989: * of script instructions, <code>observeInstructionCount()</code> will
1990: * be called.
1991: */
1992: public final int getInstructionObserverThreshold() {
1993: return instructionThreshold;
1994: }
1995:
1996: /**
1997: * Set threshold of executed instructions counter that triggers call to
1998: * <code>observeInstructionCount()</code>.
1999: * When the threshold is zero, instruction counting is disabled,
2000: * otherwise each time the run-time executes at least the threshold value
2001: * of script instructions, <code>observeInstructionCount()</code> will
2002: * be called.<p/>
2003: * Note that the meaning of "instruction" is not guaranteed to be
2004: * consistent between compiled and interpretive modes: executing a given
2005: * script or function in the different modes will result in different
2006: * instruction counts against the threshold.
2007: * {@link #setGenerateObserverCount} is called with true if
2008: * <code>threshold</code> is greater than zero, false otherwise.
2009: * @param threshold The instruction threshold
2010: */
2011: public final void setInstructionObserverThreshold(int threshold) {
2012: if (sealed)
2013: onSealedMutation();
2014: if (threshold < 0)
2015: throw new IllegalArgumentException();
2016: instructionThreshold = threshold;
2017: setGenerateObserverCount(threshold > 0);
2018: }
2019:
2020: /**
2021: * Turn on or off generation of code with callbacks to
2022: * track the count of executed instructions.
2023: * Currently only affects JVM byte code generation: this slows down the
2024: * generated code, but code generated without the callbacks will not
2025: * be counted toward instruction thresholds. Rhino's interpretive
2026: * mode does instruction counting without inserting callbacks, so
2027: * there is no requirement to compile code differently.
2028: * @param generateObserverCount if true, generated code will contain
2029: * calls to accumulate an estimate of the instructions executed.
2030: */
2031: public void setGenerateObserverCount(boolean generateObserverCount) {
2032: this .generateObserverCount = generateObserverCount;
2033: }
2034:
2035: /**
2036: * Allow application to monitor counter of executed script instructions
2037: * in Context subclasses.
2038: * Run-time calls this when instruction counting is enabled and the counter
2039: * reaches limit set by <code>setInstructionObserverThreshold()</code>.
2040: * The method is useful to observe long running scripts and if necessary
2041: * to terminate them.
2042: * <p>
2043: * The instruction counting support is available only for interpreted
2044: * scripts generated when the optimization level is set to -1.
2045: * <p>
2046: * The default implementation calls
2047: * {@link ContextFactory#observeInstructionCount(Context cx,
2048: * int instructionCount)}
2049: * that allows to customize Context behavior without introducing
2050: * Context subclasses.
2051: *
2052: * @param instructionCount amount of script instruction executed since
2053: * last call to <code>observeInstructionCount</code>
2054: * @throws Error to terminate the script
2055: * @see #setOptimizationLevel(int)
2056: */
2057: protected void observeInstructionCount(int instructionCount) {
2058: ContextFactory f = getFactory();
2059: f.observeInstructionCount(this , instructionCount);
2060: }
2061:
2062: /**
2063: * Create class loader for generated classes.
2064: * The method calls {@link ContextFactory#createClassLoader(ClassLoader)}
2065: * using the result of {@link #getFactory()}.
2066: */
2067: public GeneratedClassLoader createClassLoader(ClassLoader parent) {
2068: ContextFactory f = getFactory();
2069: return f.createClassLoader(parent);
2070: }
2071:
2072: public final ClassLoader getApplicationClassLoader() {
2073: if (applicationClassLoader == null) {
2074: ContextFactory f = getFactory();
2075: ClassLoader loader = f.getApplicationClassLoader();
2076: if (loader == null) {
2077: ClassLoader threadLoader = VMBridge.instance
2078: .getCurrentThreadClassLoader();
2079: if (threadLoader != null
2080: && Kit.testIfCanLoadRhinoClasses(threadLoader)) {
2081: // Thread.getContextClassLoader is not cached since
2082: // its caching prevents it from GC which may lead to
2083: // a memory leak and hides updates to
2084: // Thread.getContextClassLoader
2085: return threadLoader;
2086: }
2087: // Thread.getContextClassLoader can not load Rhino classes,
2088: // try to use the loader of ContextFactory or Context
2089: // subclasses.
2090: Class fClass = f.getClass();
2091: if (fClass != ScriptRuntime.ContextFactoryClass) {
2092: loader = fClass.getClassLoader();
2093: } else {
2094: loader = getClass().getClassLoader();
2095: }
2096: }
2097: applicationClassLoader = loader;
2098: }
2099: return applicationClassLoader;
2100: }
2101:
2102: public final void setApplicationClassLoader(ClassLoader loader) {
2103: if (sealed)
2104: onSealedMutation();
2105: if (loader == null) {
2106: // restore default behaviour
2107: applicationClassLoader = null;
2108: return;
2109: }
2110: if (!Kit.testIfCanLoadRhinoClasses(loader)) {
2111: throw new IllegalArgumentException(
2112: "Loader can not resolve Rhino classes");
2113: }
2114: applicationClassLoader = loader;
2115: }
2116:
2117: /********** end of API **********/
2118:
2119: /**
2120: * Internal method that reports an error for missing calls to
2121: * enter().
2122: */
2123: static Context getContext() {
2124: Context cx = getCurrentContext();
2125: if (cx == null) {
2126: throw new RuntimeException(
2127: "No Context associated with current Thread");
2128: }
2129: return cx;
2130: }
2131:
2132: private Object compileImpl(Scriptable scope, Reader sourceReader,
2133: String sourceString, String sourceName, int lineno,
2134: Object securityDomain, boolean returnFunction,
2135: Evaluator compiler, ErrorReporter compilationErrorReporter)
2136: throws IOException {
2137: if (sourceName == null) {
2138: sourceName = "unnamed script";
2139: }
2140: if (securityDomain != null && getSecurityController() == null) {
2141: throw new IllegalArgumentException(
2142: "securityDomain should be null if setSecurityController() was never called");
2143: }
2144:
2145: // One of sourceReader or sourceString has to be null
2146: if (!(sourceReader == null ^ sourceString == null))
2147: Kit.codeBug();
2148: // scope should be given if and only if compiling function
2149: if (!(scope == null ^ returnFunction))
2150: Kit.codeBug();
2151:
2152: CompilerEnvirons compilerEnv = new CompilerEnvirons();
2153: compilerEnv.initFromContext(this );
2154: if (compilationErrorReporter == null) {
2155: compilationErrorReporter = compilerEnv.getErrorReporter();
2156: }
2157:
2158: if (debugger != null) {
2159: if (sourceReader != null) {
2160: sourceString = Kit.readReader(sourceReader);
2161: sourceReader = null;
2162: }
2163: }
2164:
2165: Parser p = new Parser(compilerEnv, compilationErrorReporter);
2166: if (returnFunction) {
2167: p.calledByCompileFunction = true;
2168: }
2169: ScriptOrFnNode tree;
2170: if (sourceString != null) {
2171: tree = p.parse(sourceString, sourceName, lineno);
2172: } else {
2173: tree = p.parse(sourceReader, sourceName, lineno);
2174: }
2175: if (returnFunction) {
2176: if (!(tree.getFunctionCount() == 1
2177: && tree.getFirstChild() != null && tree
2178: .getFirstChild().getType() == Token.FUNCTION)) {
2179: // XXX: the check just look for the first child
2180: // and allows for more nodes after it for compatibility
2181: // with sources like function() {};;;
2182: throw new IllegalArgumentException(
2183: "compileFunction only accepts source with single JS function: "
2184: + sourceString);
2185: }
2186: }
2187:
2188: if (compiler == null) {
2189: compiler = createCompiler();
2190: }
2191:
2192: String encodedSource = p.getEncodedSource();
2193:
2194: Object bytecode = compiler.compile(compilerEnv, tree,
2195: encodedSource, returnFunction);
2196:
2197: if (debugger != null) {
2198: if (sourceString == null)
2199: Kit.codeBug();
2200: if (bytecode instanceof DebuggableScript) {
2201: DebuggableScript dscript = (DebuggableScript) bytecode;
2202: notifyDebugger_r(this , dscript, sourceString);
2203: } else {
2204: throw new RuntimeException("NOT SUPPORTED");
2205: }
2206: }
2207:
2208: Object result;
2209: if (returnFunction) {
2210: result = compiler.createFunctionObject(this , scope,
2211: bytecode, securityDomain);
2212: } else {
2213: result = compiler.createScriptObject(bytecode,
2214: securityDomain);
2215: }
2216:
2217: return result;
2218: }
2219:
2220: private static void notifyDebugger_r(Context cx,
2221: DebuggableScript dscript, String debugSource) {
2222: cx.debugger.handleCompilationDone(cx, dscript, debugSource);
2223: for (int i = 0; i != dscript.getFunctionCount(); ++i) {
2224: notifyDebugger_r(cx, dscript.getFunction(i), debugSource);
2225: }
2226: }
2227:
2228: private static Class codegenClass = Kit
2229: .classOrNull("org.mozilla.javascript.optimizer.Codegen");
2230: private static Class interpreterClass = Kit
2231: .classOrNull("org.mozilla.javascript.Interpreter");
2232:
2233: private Evaluator createCompiler() {
2234: Evaluator result = null;
2235: if (optimizationLevel >= 0 && codegenClass != null) {
2236: result = (Evaluator) Kit.newInstanceOrNull(codegenClass);
2237: }
2238: if (result == null) {
2239: result = createInterpreter();
2240: }
2241: return result;
2242: }
2243:
2244: static Evaluator createInterpreter() {
2245: return (Evaluator) Kit.newInstanceOrNull(interpreterClass);
2246: }
2247:
2248: static String getSourcePositionFromStack(int[] linep) {
2249: Context cx = getCurrentContext();
2250: if (cx == null)
2251: return null;
2252: if (cx.lastInterpreterFrame != null) {
2253: Evaluator evaluator = createInterpreter();
2254: if (evaluator != null)
2255: return evaluator.getSourcePositionFromStack(cx, linep);
2256: }
2257: /**
2258: * A bit of a hack, but the only way to get filename and line
2259: * number from an enclosing frame.
2260: */
2261: CharArrayWriter writer = new CharArrayWriter();
2262: RuntimeException re = new RuntimeException();
2263: re.printStackTrace(new PrintWriter(writer));
2264: String s = writer.toString();
2265: int open = -1;
2266: int close = -1;
2267: int colon = -1;
2268: for (int i = 0; i < s.length(); i++) {
2269: char c = s.charAt(i);
2270: if (c == ':')
2271: colon = i;
2272: else if (c == '(')
2273: open = i;
2274: else if (c == ')')
2275: close = i;
2276: else if (c == '\n' && open != -1 && close != -1
2277: && colon != -1 && open < colon && colon < close) {
2278: String fileStr = s.substring(open + 1, colon);
2279: if (!fileStr.endsWith(".java")) {
2280: String lineStr = s.substring(colon + 1, close);
2281: try {
2282: linep[0] = Integer.parseInt(lineStr);
2283: if (linep[0] < 0) {
2284: linep[0] = 0;
2285: }
2286: return fileStr;
2287: } catch (NumberFormatException e) {
2288: // fall through
2289: }
2290: }
2291: open = close = colon = -1;
2292: }
2293: }
2294:
2295: return null;
2296: }
2297:
2298: RegExpProxy getRegExpProxy() {
2299: if (regExpProxy == null) {
2300: Class cl = Kit
2301: .classOrNull("org.mozilla.javascript.regexp.RegExpImpl");
2302: if (cl != null) {
2303: regExpProxy = (RegExpProxy) Kit.newInstanceOrNull(cl);
2304: }
2305: }
2306: return regExpProxy;
2307: }
2308:
2309: final boolean isVersionECMA1() {
2310: return version == VERSION_DEFAULT || version >= VERSION_1_3;
2311: }
2312:
2313: // The method must NOT be public or protected
2314: SecurityController getSecurityController() {
2315: SecurityController global = SecurityController.global();
2316: if (global != null) {
2317: return global;
2318: }
2319: return securityController;
2320: }
2321:
2322: public final boolean isGeneratingDebugChanged() {
2323: return generatingDebugChanged;
2324: }
2325:
2326: /**
2327: * Add a name to the list of names forcing the creation of real
2328: * activation objects for functions.
2329: *
2330: * @param name the name of the object to add to the list
2331: */
2332: public void addActivationName(String name) {
2333: if (sealed)
2334: onSealedMutation();
2335: if (activationNames == null)
2336: activationNames = new Hashtable(5);
2337: activationNames.put(name, name);
2338: }
2339:
2340: /**
2341: * Check whether the name is in the list of names of objects
2342: * forcing the creation of activation objects.
2343: *
2344: * @param name the name of the object to test
2345: *
2346: * @return true if an function activation object is needed.
2347: */
2348: public final boolean isActivationNeeded(String name) {
2349: return activationNames != null
2350: && activationNames.containsKey(name);
2351: }
2352:
2353: /**
2354: * Remove a name from the list of names forcing the creation of real
2355: * activation objects for functions.
2356: *
2357: * @param name the name of the object to remove from the list
2358: */
2359: public void removeActivationName(String name) {
2360: if (sealed)
2361: onSealedMutation();
2362: if (activationNames != null)
2363: activationNames.remove(name);
2364: }
2365:
2366: private static String implementationVersion;
2367:
2368: private final ContextFactory factory;
2369: private boolean sealed;
2370: private Object sealKey;
2371:
2372: Scriptable topCallScope;
2373: NativeCall currentActivationCall;
2374: XMLLib cachedXMLLib;
2375:
2376: // for Objects, Arrays to tag themselves as being printed out,
2377: // so they don't print themselves out recursively.
2378: // Use ObjToIntMap instead of java.util.HashSet for JDK 1.1 compatibility
2379: ObjToIntMap iterating;
2380:
2381: Object interpreterSecurityDomain;
2382:
2383: int version;
2384:
2385: private SecurityController securityController;
2386: private ClassShutter classShutter;
2387: private ErrorReporter errorReporter;
2388: RegExpProxy regExpProxy;
2389: private Locale locale;
2390: private boolean generatingDebug;
2391: private boolean generatingDebugChanged;
2392: private boolean generatingSource = true;
2393: boolean compileFunctionsWithDynamicScopeFlag;
2394: boolean useDynamicScope;
2395: private int optimizationLevel;
2396: private int maximumInterpreterStackDepth;
2397: private WrapFactory wrapFactory;
2398: Debugger debugger;
2399: private Object debuggerData;
2400: private int enterCount;
2401: private Object propertyListeners;
2402: private Hashtable hashtable;
2403: private ClassLoader applicationClassLoader;
2404:
2405: /**
2406: * This is the list of names of objects forcing the creation of
2407: * function activation records.
2408: */
2409: Hashtable activationNames;
2410:
2411: // For the interpreter to store the last frame for error reports etc.
2412: Object lastInterpreterFrame;
2413:
2414: // For the interpreter to store information about previous invocations
2415: // interpreter invocations
2416: ObjArray previousInterpreterInvocations;
2417:
2418: // For instruction counting (interpreter only)
2419: int instructionCount;
2420: int instructionThreshold;
2421:
2422: // It can be used to return the second index-like result from function
2423: int scratchIndex;
2424:
2425: // It can be used to return the second uint32 result from function
2426: long scratchUint32;
2427:
2428: // It can be used to return the second Scriptable result from function
2429: Scriptable scratchScriptable;
2430:
2431: // Generate an observer count on compiled code
2432: public boolean generateObserverCount = false;
2433: }
|