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