/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-2000 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ // API class package org.mozilla.javascript; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.CharArrayWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Hashtable; import java.util.Locale; import org.mozilla.javascript.debug.DebuggableScript; import org.mozilla.javascript.debug.Debugger; import org.mozilla.javascript.xml.XMLLib; /** * This class represents the runtime context of an executing script. * * Before executing a script, an instance of Context must be created * and associated with the thread that will be executing the script. * The Context will be used to store information about the executing * of the script such as the call stack. Contexts are associated with * the current thread using the {@link #call(ContextAction)} * or {@link #enter()} methods.
* * Different forms of script execution are supported. Scripts may be * evaluated from the source directly, or first compiled and then later * executed. Interactive execution is also supported.
* * Some aspects of script execution, such as type conversions and * object creation, may be accessed directly through methods of * Context. * * @see Scriptable * @author Norris Boyd * @author Brendan Eich */ public class Context { /** * Language versions. * * All integral values are reserved for future version numbers. */ /** * The unknown version. */ public static final int VERSION_UNKNOWN = -1; /** * The default version. */ public static final int VERSION_DEFAULT = 0; /** * JavaScript 1.0 */ public static final int VERSION_1_0 = 100; /** * JavaScript 1.1 */ public static final int VERSION_1_1 = 110; /** * JavaScript 1.2 */ public static final int VERSION_1_2 = 120; /** * JavaScript 1.3 */ public static final int VERSION_1_3 = 130; /** * JavaScript 1.4 */ public static final int VERSION_1_4 = 140; /** * JavaScript 1.5 */ public static final int VERSION_1_5 = 150; /** * JavaScript 1.6 */ public static final int VERSION_1_6 = 160; /** * JavaScript 1.7 */ public static final int VERSION_1_7 = 170; /** * Controls behaviour of Date.prototype.getYear(). * If hasFeature(FEATURE_NON_ECMA_GET_YEAR) returns true, * Date.prototype.getYear subtructs 1900 only if 1900 <= date < 2000. * The default behavior of {@link #hasFeature(int)} is always to subtruct * 1900 as rquired by ECMAScript B.2.4. */ public static final int FEATURE_NON_ECMA_GET_YEAR = 1; /** * Control if member expression as function name extension is available. * If hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME) returns * true, allow function memberExpression(args) { body } to be * syntax sugar for memberExpression = function(args) { body }, * when memberExpression is not a simple identifier. * See ECMAScript-262, section 11.2 for definition of memberExpression. * By default {@link #hasFeature(int)} returns false. */ public static final int FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME = 2; /** * Control if reserved keywords are treated as identifiers. * If hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER) returns true, * treat future reserved keyword (see Ecma-262, section 7.5.3) as ordinary * identifiers but warn about this usage. * * By default {@link #hasFeature(int)} returns false. */ public static final int FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER = 3; /** * Control if toString() should returns the same result * as toSource() when applied to objects and arrays. * If hasFeature(FEATURE_TO_STRING_AS_SOURCE) returns true, * calling toString() on JS objects gives the same result as * calling toSource(). That is it returns JS source with code * to create an object with all enumeratable fields of the original object * instead of printing [object result of * {@link Scriptable#getClassName()}]. *
* By default {@link #hasFeature(int)} returns true only if * the current JS version is set to {@link #VERSION_1_2}. */ public static final int FEATURE_TO_STRING_AS_SOURCE = 4; /** * Control if properties __proto__ and __parent__ * are treated specially. * If hasFeature(FEATURE_PARENT_PROTO_PROPERTIES) returns true, * treat __parent__ and __proto__ as special properties. *
* The properties allow to query and set scope and prototype chains for the * objects. The special meaning of the properties is available * only when they are used as the right hand side of the dot operator. * For example, while x.__proto__ = y changes the prototype * chain of the object x to point to y, * x["__proto__"] = y simply assigns a new value to the property * __proto__ in x even when the feature is on. * * By default {@link #hasFeature(int)} returns true. */ public static final int FEATURE_PARENT_PROTO_PROPERTIES = 5; /** * @deprecated In previous releases, this name was given to * FEATURE_PARENT_PROTO_PROPERTIES. */ public static final int FEATURE_PARENT_PROTO_PROPRTIES = 5; /** * Control if support for E4X(ECMAScript for XML) extension is available. * If hasFeature(FEATURE_E4X) returns true, the XML syntax is available. *
* By default {@link #hasFeature(int)} returns true if * the current JS version is set to {@link #VERSION_DEFAULT} * or is at least {@link #VERSION_1_6}. * @since 1.6 Release 1 */ public static final int FEATURE_E4X = 6; /** * Control if dynamic scope should be used for name access. * If hasFeature(FEATURE_DYNAMIC_SCOPE) returns true, then the name lookup * during name resolution will use the top scope of the script or function * which is at the top of JS execution stack instead of the top scope of the * script or function from the current stack frame if the top scope of * the top stack frame contains the top scope of the current stack frame * on its prototype chain. *
* This is useful to define shared scope containing functions that can * be called from scripts and functions using private scopes. *
* By default {@link #hasFeature(int)} returns false. * @since 1.6 Release 1 */ public static final int FEATURE_DYNAMIC_SCOPE = 7; /** * Control if strict variable mode is enabled. * When the feature is on Rhino reports runtime errors if assignment * to a global variable that does not exist is executed. When the feature * is off such assignments creates new variable in the global scope as * required by ECMA 262. *
* By default {@link #hasFeature(int)} returns false. * @since 1.6 Release 1 */ public static final int FEATURE_STRICT_VARS = 8; /** * Control if strict eval mode is enabled. * When the feature is on Rhino reports runtime errors if non-string * argument is passed to the eval function. When the feature is off * eval simply return non-string argument as is without performing any * evaluation as required by ECMA 262. *
* By default {@link #hasFeature(int)} returns false. * @since 1.6 Release 1 */ public static final int FEATURE_STRICT_EVAL = 9; /** * When the feature is on Rhino will add a "fileName" and "lineNumber" * properties to Error objects automatically. When the feature is off, you * have to explicitly pass them as the second and third argument to the * Error constructor. Note that neither behaviour is fully ECMA 262 * compliant (as 262 doesn't specify a three-arg constructor), but keeping * the feature off results in Error objects that don't have * additional non-ECMA properties when constructed using the ECMA-defined * single-arg constructor and is thus desirable if a stricter ECMA * compliance is desired, specifically adherence to the point 15.11.5. of * the standard. *
* By default {@link #hasFeature(int)} returns false. * @since 1.6 Release 6 */ public static final int FEATURE_LOCATION_INFORMATION_IN_ERROR = 10; /** * Controls whether JS 1.5 'strict mode' is enabled. * When the feature is on, Rhino reports more than a dozen different * warnings. When the feature is off, these warnings are not generated. * FEATURE_STRICT_MODE implies FEATURE_STRICT_VARS and FEATURE_STRICT_EVAL. *
* By default {@link #hasFeature(int)} returns false. * @since 1.6 Release 6 */ public static final int FEATURE_STRICT_MODE = 11; /** * Controls whether a warning should be treated as an error. * @since 1.6 Release 6 */ public static final int FEATURE_WARNING_AS_ERROR = 12; /** * Enables enhanced access to Java. * Specifically, controls whether private and protected members can be * accessed, and whether scripts can catch all Java exceptions. *
* Note that this feature should only be enabled for trusted scripts. *
* By default {@link #hasFeature(int)} returns false. * @since 1.7 Release 1 */ public static final int FEATURE_ENHANCED_JAVA_ACCESS = 13; public static final String languageVersionProperty = "language version"; public static final String errorReporterProperty = "error reporter"; /** * Convenient value to use as zero-length array of objects. */ public static final Object[] emptyArgs = ScriptRuntime.emptyArgs; /** * Create a new Context. * * Note that the Context must be associated with a thread before * it can be used to execute a script. * @deprecated use {@link ContextFactory#enter()} or * {@link ContextFactory#call(ContextAction)} instead. */ public Context() { this(ContextFactory.getGlobal()); } Context(ContextFactory factory) { assert factory != null; this.factory = factory; setLanguageVersion(VERSION_DEFAULT); optimizationLevel = codegenClass != null ? 0 : -1; maximumInterpreterStackDepth = Integer.MAX_VALUE; } /** * Get the current Context. * * The current Context is per-thread; this method looks up * the Context associated with the current thread.
* * @return the Context associated with the current thread, or * null if no context is associated with the current * thread. * @see ContextFactory#enterContext() * @see ContextFactory#call(ContextAction) */ public static Context getCurrentContext() { Object helper = VMBridge.instance.getThreadContextHelper(); return VMBridge.instance.getContext(helper); } /** * Same as calling {@link ContextFactory#enterContext()} on the global * ContextFactory instance. * @deprecated use {@link ContextFactory#enter()} or * {@link ContextFactory#call(ContextAction)} instead as this method relies * on usage of a static singleton "global" ContextFactory. * @return a Context associated with the current thread * @see #getCurrentContext() * @see #exit() * @see #call(ContextAction) */ public static Context enter() { return enter(null); } /** * Get a Context associated with the current thread, using * the given Context if need be. *
* The same as enter()
except that cx
* is associated with the current thread and returned if
* the current thread has no associated context and cx
* is not associated with any other thread.
* @param cx a Context to associate with the thread if possible
* @return a Context associated with the current thread
* @deprecated use {@link ContextFactory#enterContext(Context)} instead as
* this method relies on usage of a static singleton "global" ContextFactory.
* @see ContextFactory#enterContext(Context)
* @see ContextFactory#call(ContextAction)
*/
public static Context enter(Context cx)
{
return enter(cx, ContextFactory.getGlobal());
}
static final Context enter(Context cx, ContextFactory factory)
{
Object helper = VMBridge.instance.getThreadContextHelper();
Context old = VMBridge.instance.getContext(helper);
if (old != null) {
cx = old;
} else {
if (cx == null) {
cx = factory.makeContext();
if (cx.enterCount != 0) {
throw new IllegalStateException("factory.makeContext() returned Context instance already associated with some thread");
}
factory.onContextCreated(cx);
if (factory.isSealed() && !cx.isSealed()) {
cx.seal(null);
}
} else {
if (cx.enterCount != 0) {
throw new IllegalStateException("can not use Context instance already associated with some thread");
}
}
VMBridge.instance.setContext(helper, cx);
}
++cx.enterCount;
return cx;
}
/**
* Exit a block of code requiring a Context.
*
* Calling exit()
will remove the association between
* the current thread and a Context if the prior call to
* {@link ContextFactory#enterContext()} on this thread newly associated a
* Context with this thread. Once the current thread no longer has an
* associated Context, it cannot be used to execute JavaScript until it is
* again associated with a Context.
* @see ContextFactory#enterContext()
*/
public static void exit()
{
Object helper = VMBridge.instance.getThreadContextHelper();
Context cx = VMBridge.instance.getContext(helper);
if (cx == null) {
throw new IllegalStateException(
"Calling Context.exit without previous Context.enter");
}
if (cx.enterCount < 1) Kit.codeBug();
if (--cx.enterCount == 0) {
VMBridge.instance.setContext(helper, null);
cx.factory.onContextReleased(cx);
}
}
/**
* Call {@link ContextAction#run(Context cx)}
* using the Context instance associated with the current thread.
* If no Context is associated with the thread, then
* ContextFactory.getGlobal().makeContext() will be called to
* construct new Context instance. The instance will be temporary
* associated with the thread during call to
* {@link ContextAction#run(Context)}.
* @deprecated use {@link ContextFactory#call(ContextAction)} instead as
* this method relies on usage of a static singleton "global"
* ContextFactory.
* @return The result of {@link ContextAction#run(Context)}.
*/
public static Object call(ContextAction action)
{
return call(ContextFactory.getGlobal(), action);
}
/**
* Call {@link
* Callable#call(Context cx, Scriptable scope, Scriptable thisObj,
* Object[] args)}
* using the Context instance associated with the current thread.
* If no Context is associated with the thread, then
* {@link ContextFactory#makeContext()} will be called to construct
* new Context instance. The instance will be temporary associated
* with the thread during call to {@link ContextAction#run(Context)}.
*
* It is allowed but not advisable to use null for factory * argument in which case the global static singleton ContextFactory * instance will be used to create new context instances. * @see ContextFactory#call(ContextAction) */ public static Object call(ContextFactory factory, final Callable callable, final Scriptable scope, final Scriptable thisObj, final Object[] args) { if(factory == null) { factory = ContextFactory.getGlobal(); } return call(factory, new ContextAction() { public Object run(Context cx) { return callable.call(cx, scope, thisObj, args); } }); } /** * The method implements {@links ContextFactory#call(ContextAction)} logic. */ static Object call(ContextFactory factory, ContextAction action) { Context cx = enter(null, factory); try { return action.run(cx); } finally { exit(); } } /** * @deprecated * @see ContextFactory#addListener(ContextFactory.Listener) * @see ContextFactory#getGlobal() */ public static void addContextListener(ContextListener listener) { // Special workaround for the debugger String DBG = "org.mozilla.javascript.tools.debugger.Main"; if (DBG.equals(listener.getClass().getName())) { Class cl = listener.getClass(); Class factoryClass = Kit.classOrNull( "org.mozilla.javascript.ContextFactory"); Class[] sig = { factoryClass }; Object[] args = { ContextFactory.getGlobal() }; try { Method m = cl.getMethod("attachTo", sig); m.invoke(listener, args); } catch (Exception ex) { RuntimeException rex = new RuntimeException(); Kit.initCause(rex, ex); throw rex; } return; } ContextFactory.getGlobal().addListener(listener); } /** * @deprecated * @see ContextFactory#removeListener(ContextFactory.Listener) * @see ContextFactory#getGlobal() */ public static void removeContextListener(ContextListener listener) { ContextFactory.getGlobal().addListener(listener); } /** * Return {@link ContextFactory} instance used to create this Context. */ public final ContextFactory getFactory() { return factory; } /** * Checks if this is a sealed Context. A sealed Context instance does not * allow to modify any of its properties and will throw an exception * on any such attempt. * @see #seal(Object sealKey) */ public final boolean isSealed() { return sealed; } /** * Seal this Context object so any attempt to modify any of its properties * including calling {@link #enter()} and {@link #exit()} methods will * throw an exception. *
* If sealKey is not null, calling * {@link #unseal(Object sealKey)} with the same key unseals * the object. If sealKey is null, unsealing is no longer possible. * * @see #isSealed() * @see #unseal(Object) */ public final void seal(Object sealKey) { if (sealed) onSealedMutation(); sealed = true; this.sealKey = sealKey; } /** * Unseal previously sealed Context object. * The sealKey argument should not be null and should match * sealKey suplied with the last call to * {@link #seal(Object)} or an exception will be thrown. * * @see #isSealed() * @see #seal(Object sealKey) */ public final void unseal(Object sealKey) { if (sealKey == null) throw new IllegalArgumentException(); if (this.sealKey != sealKey) throw new IllegalArgumentException(); if (!sealed) throw new IllegalStateException(); sealed = false; this.sealKey = null; } static void onSealedMutation() { throw new IllegalStateException(); } /** * Get the current language version. *
* The language version number affects JavaScript semantics as detailed * in the overview documentation. * * @return an integer that is one of VERSION_1_0, VERSION_1_1, etc. */ public final int getLanguageVersion() { return version; } /** * Set the language version. * *
* Setting the language version will affect functions and scripts compiled * subsequently. See the overview documentation for version-specific * behavior. * * @param version the version as specified by VERSION_1_0, VERSION_1_1, etc. */ public void setLanguageVersion(int version) { if (sealed) onSealedMutation(); checkLanguageVersion(version); Object listeners = propertyListeners; if (listeners != null && version != this.version) { firePropertyChangeImpl(listeners, languageVersionProperty, new Integer(this.version), new Integer(version)); } this.version = version; } public static boolean isValidLanguageVersion(int version) { switch (version) { case VERSION_DEFAULT: case VERSION_1_0: case VERSION_1_1: case VERSION_1_2: case VERSION_1_3: case VERSION_1_4: case VERSION_1_5: case VERSION_1_6: case VERSION_1_7: return true; } return false; } public static void checkLanguageVersion(int version) { if (isValidLanguageVersion(version)) { return; } throw new IllegalArgumentException("Bad language version: "+version); } /** * Get the implementation version. * *
* The implementation version is of the form *
* "name langVer release
relNum date"
*
* where name is the name of the product, langVer is
* the language version, relNum is the release number, and
* date is the release date for that specific
* release in the form "yyyy mm dd".
*
* @return a string that encodes the product, language version, release
* number, and date.
*/
public final String getImplementationVersion()
{
// XXX Probably it would be better to embed this directly into source
// with special build preprocessing but that would require some ant
// tweaking and then replacing token in resource files was simpler
if (implementationVersion == null) {
implementationVersion
= ScriptRuntime.getMessage0("implementation.version");
}
return implementationVersion;
}
/**
* Get the current error reporter.
*
* @see org.mozilla.javascript.ErrorReporter
*/
public final ErrorReporter getErrorReporter()
{
if (errorReporter == null) {
return DefaultErrorReporter.instance;
}
return errorReporter;
}
/**
* Change the current error reporter.
*
* @return the previous error reporter
* @see org.mozilla.javascript.ErrorReporter
*/
public final ErrorReporter setErrorReporter(ErrorReporter reporter)
{
if (sealed) onSealedMutation();
if (reporter == null) throw new IllegalArgumentException();
ErrorReporter old = getErrorReporter();
if (reporter == old) {
return old;
}
Object listeners = propertyListeners;
if (listeners != null) {
firePropertyChangeImpl(listeners, errorReporterProperty,
old, reporter);
}
this.errorReporter = reporter;
return old;
}
/**
* Get the current locale. Returns the default locale if none has
* been set.
*
* @see java.util.Locale
*/
public final Locale getLocale()
{
if (locale == null)
locale = Locale.getDefault();
return locale;
}
/**
* Set the current locale.
*
* @see java.util.Locale
*/
public final Locale setLocale(Locale loc)
{
if (sealed) onSealedMutation();
Locale result = locale;
locale = loc;
return result;
}
/**
* Register an object to receive notifications when a bound property
* has changed
* @see java.beans.PropertyChangeEvent
* @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
* @param l the listener
*/
public final void addPropertyChangeListener(PropertyChangeListener l)
{
if (sealed) onSealedMutation();
propertyListeners = Kit.addListener(propertyListeners, l);
}
/**
* Remove an object from the list of objects registered to receive
* notification of changes to a bounded property
* @see java.beans.PropertyChangeEvent
* @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
* @param l the listener
*/
public final void removePropertyChangeListener(PropertyChangeListener l)
{
if (sealed) onSealedMutation();
propertyListeners = Kit.removeListener(propertyListeners, l);
}
/**
* Notify any registered listeners that a bounded property has changed
* @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
* @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
* @see java.beans.PropertyChangeListener
* @see java.beans.PropertyChangeEvent
* @param property the bound property
* @param oldValue the old value
* @param newValue the new value
*/
final void firePropertyChange(String property, Object oldValue,
Object newValue)
{
Object listeners = propertyListeners;
if (listeners != null) {
firePropertyChangeImpl(listeners, property, oldValue, newValue);
}
}
private void firePropertyChangeImpl(Object listeners, String property,
Object oldValue, Object newValue)
{
for (int i = 0; ; ++i) {
Object l = Kit.getListener(listeners, i);
if (l == null)
break;
if (l instanceof PropertyChangeListener) {
PropertyChangeListener pcl = (PropertyChangeListener)l;
pcl.propertyChange(new PropertyChangeEvent(
this, property, oldValue, newValue));
}
}
}
/**
* Report a warning using the error reporter for the current thread.
*
* @param message the warning message to report
* @param sourceName a string describing the source, such as a filename
* @param lineno the starting line number
* @param lineSource the text of the line (may be null)
* @param lineOffset the offset into lineSource where problem was detected
* @see org.mozilla.javascript.ErrorReporter
*/
public static void reportWarning(String message, String sourceName,
int lineno, String lineSource,
int lineOffset)
{
Context cx = Context.getContext();
if (cx.hasFeature(FEATURE_WARNING_AS_ERROR))
reportError(message, sourceName, lineno, lineSource, lineOffset);
else
cx.getErrorReporter().warning(message, sourceName, lineno,
lineSource, lineOffset);
}
/**
* Report a warning using the error reporter for the current thread.
*
* @param message the warning message to report
* @see org.mozilla.javascript.ErrorReporter
*/
public static void reportWarning(String message)
{
int[] linep = { 0 };
String filename = getSourcePositionFromStack(linep);
Context.reportWarning(message, filename, linep[0], null, 0);
}
public static void reportWarning(String message, Throwable t)
{
int[] linep = { 0 };
String filename = getSourcePositionFromStack(linep);
Writer sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.println(message);
t.printStackTrace(pw);
pw.flush();
Context.reportWarning(sw.toString(), filename, linep[0], null, 0);
}
/**
* Report an error using the error reporter for the current thread.
*
* @param message the error message to report
* @param sourceName a string describing the source, such as a filename
* @param lineno the starting line number
* @param lineSource the text of the line (may be null)
* @param lineOffset the offset into lineSource where problem was detected
* @see org.mozilla.javascript.ErrorReporter
*/
public static void reportError(String message, String sourceName,
int lineno, String lineSource,
int lineOffset)
{
Context cx = getCurrentContext();
if (cx != null) {
cx.getErrorReporter().error(message, sourceName, lineno,
lineSource, lineOffset);
} else {
throw new EvaluatorException(message, sourceName, lineno,
lineSource, lineOffset);
}
}
/**
* Report an error using the error reporter for the current thread.
*
* @param message the error message to report
* @see org.mozilla.javascript.ErrorReporter
*/
public static void reportError(String message)
{
int[] linep = { 0 };
String filename = getSourcePositionFromStack(linep);
Context.reportError(message, filename, linep[0], null, 0);
}
/**
* Report a runtime error using the error reporter for the current thread.
*
* @param message the error message to report
* @param sourceName a string describing the source, such as a filename
* @param lineno the starting line number
* @param lineSource the text of the line (may be null)
* @param lineOffset the offset into lineSource where problem was detected
* @return a runtime exception that will be thrown to terminate the
* execution of the script
* @see org.mozilla.javascript.ErrorReporter
*/
public static EvaluatorException reportRuntimeError(String message,
String sourceName,
int lineno,
String lineSource,
int lineOffset)
{
Context cx = getCurrentContext();
if (cx != null) {
return cx.getErrorReporter().
runtimeError(message, sourceName, lineno,
lineSource, lineOffset);
} else {
throw new EvaluatorException(message, sourceName, lineno,
lineSource, lineOffset);
}
}
static EvaluatorException reportRuntimeError0(String messageId)
{
String msg = ScriptRuntime.getMessage0(messageId);
return reportRuntimeError(msg);
}
static EvaluatorException reportRuntimeError1(String messageId,
Object arg1)
{
String msg = ScriptRuntime.getMessage1(messageId, arg1);
return reportRuntimeError(msg);
}
static EvaluatorException reportRuntimeError2(String messageId,
Object arg1, Object arg2)
{
String msg = ScriptRuntime.getMessage2(messageId, arg1, arg2);
return reportRuntimeError(msg);
}
static EvaluatorException reportRuntimeError3(String messageId,
Object arg1, Object arg2,
Object arg3)
{
String msg = ScriptRuntime.getMessage3(messageId, arg1, arg2, arg3);
return reportRuntimeError(msg);
}
static EvaluatorException reportRuntimeError4(String messageId,
Object arg1, Object arg2,
Object arg3, Object arg4)
{
String msg
= ScriptRuntime.getMessage4(messageId, arg1, arg2, arg3, arg4);
return reportRuntimeError(msg);
}
/**
* Report a runtime error using the error reporter for the current thread.
*
* @param message the error message to report
* @see org.mozilla.javascript.ErrorReporter
*/
public static EvaluatorException reportRuntimeError(String message)
{
int[] linep = { 0 };
String filename = getSourcePositionFromStack(linep);
return Context.reportRuntimeError(message, filename, linep[0], null, 0);
}
/**
* Initialize the standard objects.
*
* Creates instances of the standard objects and their constructors
* (Object, String, Number, Date, etc.), setting up 'scope' to act
* as a global object as in ECMA 15.1.* * This method must be called to initialize a scope before scripts * can be evaluated in that scope.
* * This method does not affect the Context it is called upon. * * @return the initialized scope */ public final ScriptableObject initStandardObjects() { return initStandardObjects(null, false); } /** * Initialize the standard objects. * * Creates instances of the standard objects and their constructors * (Object, String, Number, Date, etc.), setting up 'scope' to act * as a global object as in ECMA 15.1.
* * This method must be called to initialize a scope before scripts * can be evaluated in that scope.
* * This method does not affect the Context it is called upon. * * @param scope the scope to initialize, or null, in which case a new * object will be created to serve as the scope * @return the initialized scope. The method returns the value of the scope * argument if it is not null or newly allocated scope object which * is an instance {@link ScriptableObject}. */ public final Scriptable initStandardObjects(ScriptableObject scope) { return initStandardObjects(scope, false); } /** * Initialize the standard objects. * * Creates instances of the standard objects and their constructors * (Object, String, Number, Date, etc.), setting up 'scope' to act * as a global object as in ECMA 15.1.
* * This method must be called to initialize a scope before scripts * can be evaluated in that scope.
* * This method does not affect the Context it is called upon.
* * This form of the method also allows for creating "sealed" standard * objects. An object that is sealed cannot have properties added, changed, * or removed. This is useful to create a "superglobal" that can be shared * among several top-level objects. Note that sealing is not allowed in * the current ECMA/ISO language specification, but is likely for * the next version. * * @param scope the scope to initialize, or null, in which case a new * object will be created to serve as the scope * @param sealed whether or not to create sealed standard objects that * cannot be modified. * @return the initialized scope. The method returns the value of the scope * argument if it is not null or newly allocated scope object. * @since 1.4R3 */ public ScriptableObject initStandardObjects(ScriptableObject scope, boolean sealed) { return ScriptRuntime.initStandardObjects(this, scope, sealed); } /** * Get the singleton object that represents the JavaScript Undefined value. */ public static Object getUndefinedValue() { return Undefined.instance; } /** * Evaluate a JavaScript source string. * * The provided source name and line number are used for error messages * and for producing debug information. * * @param scope the scope to execute in * @param source the JavaScript source * @param sourceName a string describing the source, such as a filename * @param lineno the starting line number * @param securityDomain an arbitrary object that specifies security * information about the origin or owner of the script. For * implementations that don't care about security, this value * may be null. * @return the result of evaluating the string * @see org.mozilla.javascript.SecurityController */ public final Object evaluateString(Scriptable scope, String source, String sourceName, int lineno, Object securityDomain) { Script script = compileString(source, sourceName, lineno, securityDomain); if (script != null) { return script.exec(this, scope); } else { return null; } } /** * Evaluate a reader as JavaScript source. * * All characters of the reader are consumed. * * @param scope the scope to execute in * @param in the Reader to get JavaScript source from * @param sourceName a string describing the source, such as a filename * @param lineno the starting line number * @param securityDomain an arbitrary object that specifies security * information about the origin or owner of the script. For * implementations that don't care about security, this value * may be null. * @return the result of evaluating the source * * @exception IOException if an IOException was generated by the Reader */ public final Object evaluateReader(Scriptable scope, Reader in, String sourceName, int lineno, Object securityDomain) throws IOException { Script script = compileReader(scope, in, sourceName, lineno, securityDomain); if (script != null) { return script.exec(this, scope); } else { return null; } } /** * Check whether a string is ready to be compiled. *
* stringIsCompilableUnit is intended to support interactive compilation of * javascript. If compiling the string would result in an error * that might be fixed by appending more source, this method * returns false. In every other case, it returns true. *
* Interactive shells may accumulate source lines, using this * method after each new line is appended to check whether the * statement being entered is complete. * * @param source the source buffer to check * @return whether the source is ready for compilation * @since 1.4 Release 2 */ public final boolean stringIsCompilableUnit(String source) { boolean errorseen = false; CompilerEnvirons compilerEnv = new CompilerEnvirons(); compilerEnv.initFromContext(this); // no source name or source text manager, because we're just // going to throw away the result. compilerEnv.setGeneratingSource(false); /*APPJET*/ Parser p = InformativeParser.makeParser(compilerEnv, DefaultErrorReporter.instance); try { p.parse(source, null, 1); } catch (EvaluatorException ee) { errorseen = true; } // Return false only if an error occurred as a result of reading past // the end of the file, i.e. if the source could be fixed by // appending more source. if (errorseen && p.eof()) return false; else return true; } /** * @deprecated * @see #compileReader(Reader in, String sourceName, int lineno, * Object securityDomain) */ public final Script compileReader(Scriptable scope, Reader in, String sourceName, int lineno, Object securityDomain) throws IOException { return compileReader(in, sourceName, lineno, securityDomain); } /** * Compiles the source in the given reader. *
* Returns a script that may later be executed. * Will consume all the source in the reader. * * @param in the input reader * @param sourceName a string describing the source, such as a filename * @param lineno the starting line number for reporting errors * @param securityDomain an arbitrary object that specifies security * information about the origin or owner of the script. For * implementations that don't care about security, this value * may be null. * @return a script that may later be executed * @exception IOException if an IOException was generated by the Reader * @see org.mozilla.javascript.Script */ public final Script compileReader(Reader in, String sourceName, int lineno, Object securityDomain) throws IOException { if (lineno < 0) { // For compatibility IllegalArgumentException can not be thrown here lineno = 0; } return (Script) compileImpl(null, in, null, sourceName, lineno, securityDomain, false, null, null); } /** * Compiles the source in the given string. *
* Returns a script that may later be executed. * * @param source the source string * @param sourceName a string describing the source, such as a filename * @param lineno the starting line number for reporting errors * @param securityDomain an arbitrary object that specifies security * information about the origin or owner of the script. For * implementations that don't care about security, this value * may be null. * @return a script that may later be executed * @see org.mozilla.javascript.Script */ public final Script compileString(String source, String sourceName, int lineno, Object securityDomain) { if (lineno < 0) { // For compatibility IllegalArgumentException can not be thrown here lineno = 0; } return compileString(source, null, null, sourceName, lineno, securityDomain); } final Script compileString(String source, Evaluator compiler, ErrorReporter compilationErrorReporter, String sourceName, int lineno, Object securityDomain) { try { return (Script) compileImpl(null, null, source, sourceName, lineno, securityDomain, false, compiler, compilationErrorReporter); } catch (IOException ex) { // Should not happen when dealing with source as string throw new RuntimeException(); } } /** * Compile a JavaScript function. *
* The function source must be a function definition as defined by * ECMA (e.g., "function f(a) { return a; }"). * * @param scope the scope to compile relative to * @param source the function definition source * @param sourceName a string describing the source, such as a filename * @param lineno the starting line number * @param securityDomain an arbitrary object that specifies security * information about the origin or owner of the script. For * implementations that don't care about security, this value * may be null. * @return a Function that may later be called * @see org.mozilla.javascript.Function */ public final Function compileFunction(Scriptable scope, String source, String sourceName, int lineno, Object securityDomain) { return compileFunction(scope, source, null, null, sourceName, lineno, securityDomain); } final Function compileFunction(Scriptable scope, String source, Evaluator compiler, ErrorReporter compilationErrorReporter, String sourceName, int lineno, Object securityDomain) { try { return (Function) compileImpl(scope, null, source, sourceName, lineno, securityDomain, true, compiler, compilationErrorReporter); } catch (IOException ioe) { // Should never happen because we just made the reader // from a String throw new RuntimeException(); } } /** * Decompile the script. *
* The canonical source of the script is returned. * * @param script the script to decompile * @param indent the number of spaces to indent the result * @return a string representing the script source */ public final String decompileScript(Script script, int indent) { NativeFunction scriptImpl = (NativeFunction) script; return scriptImpl.decompile(indent, 0); } /** * Decompile a JavaScript Function. *
* Decompiles a previously compiled JavaScript function object to * canonical source. *
* Returns function body of '[native code]' if no decompilation * information is available. * * @param fun the JavaScript function to decompile * @param indent the number of spaces to indent the result * @return a string representing the function source */ public final String decompileFunction(Function fun, int indent) { if (fun instanceof BaseFunction) return ((BaseFunction)fun).decompile(indent, 0); else return "function " + fun.getClassName() + "() {\n\t[native code]\n}\n"; } /** * Decompile the body of a JavaScript Function. *
* Decompiles the body a previously compiled JavaScript Function
* object to canonical source, omitting the function header and
* trailing brace.
*
* Returns '[native code]' if no decompilation information is available.
*
* @param fun the JavaScript function to decompile
* @param indent the number of spaces to indent the result
* @return a string representing the function body source.
*/
public final String decompileFunctionBody(Function fun, int indent)
{
if (fun instanceof BaseFunction) {
BaseFunction bf = (BaseFunction)fun;
return bf.decompile(indent, Decompiler.ONLY_BODY_FLAG);
}
// ALERT: not sure what the right response here is.
return "[native code]\n";
}
/**
* Create a new JavaScript object.
*
* Equivalent to evaluating "new Object()".
* @param scope the scope to search for the constructor and to evaluate
* against
* @return the new object
*/
public final Scriptable newObject(Scriptable scope)
{
return newObject(scope, "Object", ScriptRuntime.emptyArgs);
}
/**
* Create a new JavaScript object by executing the named constructor.
*
* The call newObject(scope, "Foo")
is equivalent to
* evaluating "new Foo()".
*
* @param scope the scope to search for the constructor and to evaluate against
* @param constructorName the name of the constructor to call
* @return the new object
*/
public final Scriptable newObject(Scriptable scope, String constructorName)
{
return newObject(scope, constructorName, ScriptRuntime.emptyArgs);
}
/**
* Creates a new JavaScript object by executing the named constructor.
*
* Searches scope
for the named constructor, calls it with
* the given arguments, and returns the result.
* * The code *
* Object[] args = { "a", "b" }; * newObject(scope, "Foo", args)* is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo * constructor has been defined in
scope
.
*
* @param scope The scope to search for the constructor and to evaluate
* against
* @param constructorName the name of the constructor to call
* @param args the array of arguments for the constructor
* @return the new object
*/
public final Scriptable newObject(Scriptable scope, String constructorName,
Object[] args)
{
scope = ScriptableObject.getTopLevelScope(scope);
Function ctor = ScriptRuntime.getExistingCtor(this, scope,
constructorName);
if (args == null) { args = ScriptRuntime.emptyArgs; }
return ctor.construct(this, scope, args);
}
/**
* Create an array with a specified initial length.
* * @param scope the scope to create the object in * @param length the initial length (JavaScript arrays may have * additional properties added dynamically). * @return the new array object */ public final Scriptable newArray(Scriptable scope, int length) { NativeArray result = new NativeArray(length); ScriptRuntime.setObjectProtoAndParent(result, scope); return result; } /** * Create an array with a set of initial elements. * * @param scope the scope to create the object in. * @param elements the initial elements. Each object in this array * must be an acceptable JavaScript type and type * of array should be exactly Object[], not * SomeObjectSubclass[]. * @return the new array object. */ public final Scriptable newArray(Scriptable scope, Object[] elements) { if (elements.getClass().getComponentType() != ScriptRuntime.ObjectClass) throw new IllegalArgumentException(); NativeArray result = new NativeArray(elements); ScriptRuntime.setObjectProtoAndParent(result, scope); return result; } /** * Get the elements of a JavaScript array. *
* If the object defines a length property convertible to double number, * then the number is converted Uint32 value as defined in Ecma 9.6 * and Java array of that size is allocated. * The array is initialized with the values obtained by * calling get() on object for each value of i in [0,length-1]. If * there is not a defined value for a property the Undefined value * is used to initialize the corresponding element in the array. The * Java array is then returned. * If the object doesn't define a length property or it is not a number, * empty array is returned. * @param object the JavaScript array or array-like object * @return a Java array of objects * @since 1.4 release 2 */ public final Object[] getElements(Scriptable object) { return ScriptRuntime.getArrayElements(object); } /** * Convert the value to a JavaScript boolean value. *
* See ECMA 9.2. * * @param value a JavaScript value * @return the corresponding boolean value converted using * the ECMA rules */ public static boolean toBoolean(Object value) { return ScriptRuntime.toBoolean(value); } /** * Convert the value to a JavaScript Number value. *
* Returns a Java double for the JavaScript Number. *
* See ECMA 9.3. * * @param value a JavaScript value * @return the corresponding double value converted using * the ECMA rules */ public static double toNumber(Object value) { return ScriptRuntime.toNumber(value); } /** * Convert the value to a JavaScript String value. *
* See ECMA 9.8. *
* @param value a JavaScript value * @return the corresponding String value converted using * the ECMA rules */ public static String toString(Object value) { return ScriptRuntime.toString(value); } /** * Convert the value to an JavaScript object value. *
* Note that a scope must be provided to look up the constructors * for Number, Boolean, and String. *
* See ECMA 9.9. *
* Additionally, arbitrary Java objects and classes will be * wrapped in a Scriptable object with its Java fields and methods * reflected as JavaScript properties of the object. * * @param value any Java object * @param scope global scope containing constructors for Number, * Boolean, and String * @return new JavaScript object */ public static Scriptable toObject(Object value, Scriptable scope) { return ScriptRuntime.toObject(scope, value); } /** * @deprecated * @see #toObject(Object, Scriptable) */ public static Scriptable toObject(Object value, Scriptable scope, Class staticType) { return ScriptRuntime.toObject(scope, value); } /** * Convenient method to convert java value to its closest representation * in JavaScript. *
* If value is an instance of String, Number, Boolean, Function or * Scriptable, it is returned as it and will be treated as the corresponding * JavaScript type of string, number, boolean, function and object. *
* Note that for Number instances during any arithmetic operation in * JavaScript the engine will always use the result of * Number.doubleValue() resulting in a precision loss if * the number can not fit into double. *
* If value is an instance of Character, it will be converted to string of * length 1 and its JavaScript type will be string. *
* The rest of values will be wrapped as LiveConnect objects * by calling {@link WrapFactory#wrap(Context cx, Scriptable scope, * Object obj, Class staticType)} as in: *
* Context cx = Context.getCurrentContext(); * return cx.getWrapFactory().wrap(cx, scope, value, null); ** * @param value any Java object * @param scope top scope object * @return value suitable to pass to any API that takes JavaScript values. */ public static Object javaToJS(Object value, Scriptable scope) { if (value instanceof String || value instanceof Number || value instanceof Boolean || value instanceof Scriptable) { return value; } else if (value instanceof Character) { return String.valueOf(((Character)value).charValue()); } else { Context cx = Context.getContext(); return cx.getWrapFactory().wrap(cx, scope, value, null); } } /** * Convert a JavaScript value into the desired type. * Uses the semantics defined with LiveConnect3 and throws an * Illegal argument exception if the conversion cannot be performed. * @param value the JavaScript value to convert * @param desiredType the Java type to convert to. Primitive Java * types are represented using the TYPE fields in the corresponding * wrapper class in java.lang. * @return the converted value * @throws EvaluatorException if the conversion cannot be performed */ public static Object jsToJava(Object value, Class desiredType) throws EvaluatorException { return NativeJavaObject.coerceTypeImpl(desiredType, value); } /** * @deprecated * @see #jsToJava(Object, Class) * @throws IllegalArgumentException if the conversion cannot be performed. * Note that {@link #jsToJava(Object, Class)} throws * {@link EvaluatorException} instead. */ public static Object toType(Object value, Class desiredType) throws IllegalArgumentException { try { return jsToJava(value, desiredType); } catch (EvaluatorException ex) { IllegalArgumentException ex2 = new IllegalArgumentException(ex.getMessage()); Kit.initCause(ex2, ex); throw ex2; } } /** * Rethrow the exception wrapping it as the script runtime exception. * Unless the exception is instance of {@link EcmaError} or * {@link EvaluatorException} it will be wrapped as * {@link WrappedException}, a subclass of {@link EvaluatorException}. * The resulting exception object always contains * source name and line number of script that triggered exception. *
* This method always throws an exception, its return value is provided * only for convenience to allow a usage like: *
* throw Context.throwAsScriptRuntimeEx(ex); ** to indicate that code after the method is unreachable. * @throws EvaluatorException * @throws EcmaError */ public static RuntimeException throwAsScriptRuntimeEx(Throwable e) { while ((e instanceof InvocationTargetException)) { e = ((InvocationTargetException) e).getTargetException(); } // special handling of Error so scripts would not catch them if (e instanceof Error) { Context cx = getContext(); if (cx == null || !cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) { throw (Error)e; } } if (e instanceof RhinoException) { throw (RhinoException)e; } throw new WrappedException(e); } /** * Tell whether debug information is being generated. * @since 1.3 */ public final boolean isGeneratingDebug() { return generatingDebug; } /** * Specify whether or not debug information should be generated. *
* Setting the generation of debug information on will set the * optimization level to zero. * @since 1.3 */ public final void setGeneratingDebug(boolean generatingDebug) { if (sealed) onSealedMutation(); generatingDebugChanged = true; if (generatingDebug && getOptimizationLevel() > 0) setOptimizationLevel(0); this.generatingDebug = generatingDebug; } /** * Tell whether source information is being generated. * @since 1.3 */ public final boolean isGeneratingSource() { return generatingSource; } /** * Specify whether or not source information should be generated. *
* Without source information, evaluating the "toString" method * on JavaScript functions produces only "[native code]" for * the body of the function. * Note that code generated without source is not fully ECMA * conformant. * @since 1.3 */ public final void setGeneratingSource(boolean generatingSource) { if (sealed) onSealedMutation(); this.generatingSource = generatingSource; } /** * Get the current optimization level. *
* The optimization level is expressed as an integer between -1 and * 9. * @since 1.3 * */ public final int getOptimizationLevel() { return optimizationLevel; } /** * Set the current optimization level. *
* The optimization level is expected to be an integer between -1 and * 9. Any negative values will be interpreted as -1, and any values * greater than 9 will be interpreted as 9. * An optimization level of -1 indicates that interpretive mode will * always be used. Levels 0 through 9 indicate that class files may * be generated. Higher optimization levels trade off compile time * performance for runtime performance. * The optimizer level can't be set greater than -1 if the optimizer * package doesn't exist at run time. * @param optimizationLevel an integer indicating the level of * optimization to perform * @since 1.3 * */ public final void setOptimizationLevel(int optimizationLevel) { if (sealed) onSealedMutation(); if (optimizationLevel == -2) { // To be compatible with Cocoon fork optimizationLevel = -1; } checkOptimizationLevel(optimizationLevel); if (codegenClass == null) optimizationLevel = -1; this.optimizationLevel = optimizationLevel; } public static boolean isValidOptimizationLevel(int optimizationLevel) { return -1 <= optimizationLevel && optimizationLevel <= 9; } public static void checkOptimizationLevel(int optimizationLevel) { if (isValidOptimizationLevel(optimizationLevel)) { return; } throw new IllegalArgumentException( "Optimization level outside [-1..9]: "+optimizationLevel); } /** * Returns the maximum stack depth (in terms of number of call frames) * allowed in a single invocation of interpreter. If the set depth would be * exceeded, the interpreter will throw an EvaluatorException in the script. * Defaults to Integer.MAX_VALUE. The setting only has effect for * interpreted functions (those compiled with optimization level set to -1). * As the interpreter doesn't use the Java stack but rather manages its own * stack in the heap memory, a runaway recursion in interpreted code would * eventually consume all available memory and cause OutOfMemoryError * instead of a StackOverflowError limited to only a single thread. This * setting helps prevent such situations. * * @return The current maximum interpreter stack depth. */ public final int getMaximumInterpreterStackDepth() { return maximumInterpreterStackDepth; } /** * Sets the maximum stack depth (in terms of number of call frames) * allowed in a single invocation of interpreter. If the set depth would be * exceeded, the interpreter will throw an EvaluatorException in the script. * Defaults to Integer.MAX_VALUE. The setting only has effect for * interpreted functions (those compiled with optimization level set to -1). * As the interpreter doesn't use the Java stack but rather manages its own * stack in the heap memory, a runaway recursion in interpreted code would * eventually consume all available memory and cause OutOfMemoryError * instead of a StackOverflowError limited to only a single thread. This * setting helps prevent such situations. * * @param max the new maximum interpreter stack depth * @throws IllegalStateException if this context's optimization level is not * -1 * @throws IllegalArgumentException if the new depth is not at least 1 */ public final void setMaximumInterpreterStackDepth(int max) { if(sealed) onSealedMutation(); if(optimizationLevel != -1) { throw new IllegalStateException("Cannot set maximumInterpreterStackDepth when optimizationLevel != -1"); } if(max < 1) { throw new IllegalArgumentException("Cannot set maximumInterpreterStackDepth to less than 1"); } maximumInterpreterStackDepth = max; } /** * Set the security controller for this context. *
SecurityController may only be set if it is currently null * and {@link SecurityController#hasGlobal()} is false. * Otherwise a SecurityException is thrown. * @param controller a SecurityController object * @throws SecurityException if there is already a SecurityController * object for this Context or globally installed. * @see SecurityController#initGlobal(SecurityController controller) * @see SecurityController#hasGlobal() */ public final void setSecurityController(SecurityController controller) { if (sealed) onSealedMutation(); if (controller == null) throw new IllegalArgumentException(); if (securityController != null) { throw new SecurityException("Can not overwrite existing SecurityController object"); } if (SecurityController.hasGlobal()) { throw new SecurityException("Can not overwrite existing global SecurityController object"); } securityController = controller; } /** * Set the LiveConnect access filter for this context. *
{@link ClassShutter} may only be set if it is currently null. * Otherwise a SecurityException is thrown. * @param shutter a ClassShutter object * @throws SecurityException if there is already a ClassShutter * object for this Context */ public final void setClassShutter(ClassShutter shutter) { if (sealed) onSealedMutation(); if (shutter == null) throw new IllegalArgumentException(); if (classShutter != null) { throw new SecurityException("Cannot overwrite existing " + "ClassShutter object"); } classShutter = shutter; } final ClassShutter getClassShutter() { return classShutter; } /** * Get a value corresponding to a key. *
* Since the Context is associated with a thread it can be * used to maintain values that can be later retrieved using * the current thread. *
* Note that the values are maintained with the Context, so * if the Context is disassociated from the thread the values * cannot be retrieved. Also, if private data is to be maintained * in this manner the key should be a java.lang.Object * whose reference is not divulged to untrusted code. * @param key the key used to lookup the value * @return a value previously stored using putThreadLocal. */ public final Object getThreadLocal(Object key) { if (hashtable == null) return null; return hashtable.get(key); } /** * Put a value that can later be retrieved using a given key. *
* @param key the key used to index the value * @param value the value to save */ public final void putThreadLocal(Object key, Object value) { if (sealed) onSealedMutation(); if (hashtable == null) hashtable = new Hashtable(); hashtable.put(key, value); } /** * Remove values from thread-local storage. * @param key the key for the entry to remove. * @since 1.5 release 2 */ public final void removeThreadLocal(Object key) { if (sealed) onSealedMutation(); if (hashtable == null) return; hashtable.remove(key); } /** * @deprecated * @see #FEATURE_DYNAMIC_SCOPE * @see #hasFeature(int) */ public final boolean hasCompileFunctionsWithDynamicScope() { return compileFunctionsWithDynamicScopeFlag; } /** * @deprecated * @see #FEATURE_DYNAMIC_SCOPE * @see #hasFeature(int) */ public final void setCompileFunctionsWithDynamicScope(boolean flag) { if (sealed) onSealedMutation(); compileFunctionsWithDynamicScopeFlag = flag; } /** * @deprecated * @see ClassCache#get(Scriptable) * @see ClassCache#setCachingEnabled(boolean) */ public static void setCachingEnabled(boolean cachingEnabled) { } /** * Set a WrapFactory for this Context. *
* The WrapFactory allows custom object wrapping behavior for * Java object manipulated with JavaScript. * @see WrapFactory * @since 1.5 Release 4 */ public final void setWrapFactory(WrapFactory wrapFactory) { if (sealed) onSealedMutation(); if (wrapFactory == null) throw new IllegalArgumentException(); this.wrapFactory = wrapFactory; } /** * Return the current WrapFactory, or null if none is defined. * @see WrapFactory * @since 1.5 Release 4 */ public final WrapFactory getWrapFactory() { if (wrapFactory == null) { wrapFactory = new WrapFactory(); } return wrapFactory; } /** * Return the current debugger. * @return the debugger, or null if none is attached. */ public final Debugger getDebugger() { return debugger; } /** * Return the debugger context data associated with current context. * @return the debugger data, or null if debugger is not attached */ public final Object getDebuggerContextData() { return debuggerData; } /** * Set the associated debugger. * @param debugger the debugger to be used on callbacks from * the engine. * @param contextData arbitrary object that debugger can use to store * per Context data. */ public final void setDebugger(Debugger debugger, Object contextData) { if (sealed) onSealedMutation(); this.debugger = debugger; debuggerData = contextData; } /** * Return DebuggableScript instance if any associated with the script. * If callable supports DebuggableScript implementation, the method * returns it. Otherwise null is returned. */ public static DebuggableScript getDebuggableView(Script script) { if (script instanceof NativeFunction) { return ((NativeFunction)script).getDebuggableView(); } return null; } /** * Controls certain aspects of script semantics. * Should be overwritten to alter default behavior. *
* The default implementation calls
* {@link ContextFactory#hasFeature(Context cx, int featureIndex)}
* that allows to customize Context behavior without introducing
* Context subclasses. {@link ContextFactory} documentation gives
* an example of hasFeature implementation.
*
* @param featureIndex feature index to check
* @return true if the featureIndex
feature is turned on
* @see #FEATURE_NON_ECMA_GET_YEAR
* @see #FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME
* @see #FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER
* @see #FEATURE_TO_STRING_AS_SOURCE
* @see #FEATURE_PARENT_PROTO_PROPRTIES
* @see #FEATURE_E4X
* @see #FEATURE_DYNAMIC_SCOPE
* @see #FEATURE_STRICT_VARS
* @see #FEATURE_STRICT_EVAL
* @see #FEATURE_LOCATION_INFORMATION_IN_ERROR
* @see #FEATURE_STRICT_MODE
* @see #FEATURE_WARNING_AS_ERROR
* @see #FEATURE_ENHANCED_JAVA_ACCESS
*/
public boolean hasFeature(int featureIndex)
{
ContextFactory f = getFactory();
return f.hasFeature(this, featureIndex);
}
/**
Returns an object which specifies an E4X implementation to use within
this Context
. Note
that the XMLLib.Factory interface should be considered experimental.
The default implementation uses the implementation provided by this
Context
's {@link ContextFactory}.
@return An XMLLib.Factory. Should not return null
if
{@link #FEATURE_E4X} is enabled. See {@link #hasFeature}.
*/
public XMLLib.Factory getE4xImplementationFactory() {
return getFactory().getE4xImplementationFactory();
}
/**
* Get threshold of executed instructions counter that triggers call to
* observeInstructionCount()
.
* When the threshold is zero, instruction counting is disabled,
* otherwise each time the run-time executes at least the threshold value
* of script instructions, observeInstructionCount()
will
* be called.
*/
public final int getInstructionObserverThreshold()
{
return instructionThreshold;
}
/**
* Set threshold of executed instructions counter that triggers call to
* observeInstructionCount()
.
* When the threshold is zero, instruction counting is disabled,
* otherwise each time the run-time executes at least the threshold value
* of script instructions, observeInstructionCount()
will
* be called.
threshold
is greater than zero, false otherwise.
* @param threshold The instruction threshold
*/
public final void setInstructionObserverThreshold(int threshold)
{
if (sealed) onSealedMutation();
if (threshold < 0) throw new IllegalArgumentException();
instructionThreshold = threshold;
setGenerateObserverCount(threshold > 0);
}
/**
* Turn on or off generation of code with callbacks to
* track the count of executed instructions.
* Currently only affects JVM byte code generation: this slows down the
* generated code, but code generated without the callbacks will not
* be counted toward instruction thresholds. Rhino's interpretive
* mode does instruction counting without inserting callbacks, so
* there is no requirement to compile code differently.
* @param generateObserverCount if true, generated code will contain
* calls to accumulate an estimate of the instructions executed.
*/
public void setGenerateObserverCount(boolean generateObserverCount) {
this.generateObserverCount = generateObserverCount;
}
/**
* Allow application to monitor counter of executed script instructions
* in Context subclasses.
* Run-time calls this when instruction counting is enabled and the counter
* reaches limit set by setInstructionObserverThreshold()
.
* The method is useful to observe long running scripts and if necessary
* to terminate them.
* * The instruction counting support is available only for interpreted * scripts generated when the optimization level is set to -1. *
* The default implementation calls
* {@link ContextFactory#observeInstructionCount(Context cx,
* int instructionCount)}
* that allows to customize Context behavior without introducing
* Context subclasses.
*
* @param instructionCount amount of script instruction executed since
* last call to observeInstructionCount
* @throws Error to terminate the script
* @see #setOptimizationLevel(int)
*/
protected void observeInstructionCount(int instructionCount)
{
ContextFactory f = getFactory();
f.observeInstructionCount(this, instructionCount);
}
/**
* Create class loader for generated classes.
* The method calls {@link ContextFactory#createClassLoader(ClassLoader)}
* using the result of {@link #getFactory()}.
*/
public GeneratedClassLoader createClassLoader(ClassLoader parent)
{
ContextFactory f = getFactory();
return f.createClassLoader(parent);
}
public final ClassLoader getApplicationClassLoader()
{
if (applicationClassLoader == null) {
ContextFactory f = getFactory();
ClassLoader loader = f.getApplicationClassLoader();
if (loader == null) {
ClassLoader threadLoader
= VMBridge.instance.getCurrentThreadClassLoader();
if (threadLoader != null
&& Kit.testIfCanLoadRhinoClasses(threadLoader))
{
// Thread.getContextClassLoader is not cached since
// its caching prevents it from GC which may lead to
// a memory leak and hides updates to
// Thread.getContextClassLoader
return threadLoader;
}
// Thread.getContextClassLoader can not load Rhino classes,
// try to use the loader of ContextFactory or Context
// subclasses.
Class fClass = f.getClass();
if (fClass != ScriptRuntime.ContextFactoryClass) {
loader = fClass.getClassLoader();
} else {
loader = getClass().getClassLoader();
}
}
applicationClassLoader = loader;
}
return applicationClassLoader;
}
public final void setApplicationClassLoader(ClassLoader loader)
{
if (sealed) onSealedMutation();
if (loader == null) {
// restore default behaviour
applicationClassLoader = null;
return;
}
if (!Kit.testIfCanLoadRhinoClasses(loader)) {
throw new IllegalArgumentException(
"Loader can not resolve Rhino classes");
}
applicationClassLoader = loader;
}
/********** end of API **********/
/**
* Internal method that reports an error for missing calls to
* enter().
*/
static Context getContext()
{
Context cx = getCurrentContext();
if (cx == null) {
throw new RuntimeException(
"No Context associated with current Thread");
}
return cx;
}
private Object compileImpl(Scriptable scope,
Reader sourceReader, String sourceString,
String sourceName, int lineno,
Object securityDomain, boolean returnFunction,
Evaluator compiler,
ErrorReporter compilationErrorReporter)
throws IOException
{
if(sourceName == null) {
sourceName = "unnamed script";
}
if (securityDomain != null && getSecurityController() == null) {
throw new IllegalArgumentException(
"securityDomain should be null if setSecurityController() was never called");
}
// One of sourceReader or sourceString has to be null
if (!(sourceReader == null ^ sourceString == null)) Kit.codeBug();
// scope should be given if and only if compiling function
if (!(scope == null ^ returnFunction)) Kit.codeBug();
CompilerEnvirons compilerEnv = new CompilerEnvirons();
compilerEnv.initFromContext(this);
if (compilationErrorReporter == null) {
compilationErrorReporter = compilerEnv.getErrorReporter();
}
if (debugger != null) {
if (sourceReader != null) {
sourceString = Kit.readReader(sourceReader);
sourceReader = null;
}
}
/*APPJET*/
Parser p = InformativeParser.makeParser(compilerEnv,
compilationErrorReporter);
if (returnFunction) {
p.calledByCompileFunction = true;
}
ScriptOrFnNode tree;
if (sourceString != null) {
tree = p.parse(sourceString, sourceName, lineno);
} else {
tree = p.parse(sourceReader, sourceName, lineno);
}
if (returnFunction) {
if (!(tree.getFunctionCount() == 1
&& tree.getFirstChild() != null
&& tree.getFirstChild().getType() == Token.FUNCTION))
{
// XXX: the check just look for the first child
// and allows for more nodes after it for compatibility
// with sources like function() {};;;
throw new IllegalArgumentException(
"compileFunction only accepts source with single JS function: "+sourceString);
}
}
if (compiler == null) {
compiler = createCompiler();
}
String encodedSource = p.getEncodedSource();
Object bytecode = compiler.compile(compilerEnv,
tree, encodedSource,
returnFunction);
if (debugger != null) {
if (sourceString == null) Kit.codeBug();
if (bytecode instanceof DebuggableScript) {
DebuggableScript dscript = (DebuggableScript)bytecode;
notifyDebugger_r(this, dscript, sourceString);
} else {
throw new RuntimeException("NOT SUPPORTED");
}
}
Object result;
if (returnFunction) {
result = compiler.createFunctionObject(this, scope, bytecode, securityDomain);
} else {
result = compiler.createScriptObject(bytecode, securityDomain);
}
return result;
}
private static void notifyDebugger_r(Context cx, DebuggableScript dscript,
String debugSource)
{
cx.debugger.handleCompilationDone(cx, dscript, debugSource);
for (int i = 0; i != dscript.getFunctionCount(); ++i) {
notifyDebugger_r(cx, dscript.getFunction(i), debugSource);
}
}
private static Class codegenClass = Kit.classOrNull(
"org.mozilla.javascript.optimizer.Codegen");
private static Class interpreterClass = Kit.classOrNull(
"org.mozilla.javascript.Interpreter");
private Evaluator createCompiler()
{
Evaluator result = null;
if (optimizationLevel >= 0 && codegenClass != null) {
result = (Evaluator)Kit.newInstanceOrNull(codegenClass);
}
if (result == null) {
result = createInterpreter();
}
return result;
}
static Evaluator createInterpreter()
{
return (Evaluator)Kit.newInstanceOrNull(interpreterClass);
}
static String getSourcePositionFromStack(int[] linep)
{
Context cx = getCurrentContext();
if (cx == null)
return null;
if (cx.lastInterpreterFrame != null) {
Evaluator evaluator = createInterpreter();
if (evaluator != null)
return evaluator.getSourcePositionFromStack(cx, linep);
}
/**
* A bit of a hack, but the only way to get filename and line
* number from an enclosing frame.
*/
CharArrayWriter writer = new CharArrayWriter();
RuntimeException re = new RuntimeException();
re.printStackTrace(new PrintWriter(writer));
String s = writer.toString();
int open = -1;
int close = -1;
int colon = -1;
for (int i=0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == ':')
colon = i;
else if (c == '(')
open = i;
else if (c == ')')
close = i;
else if (c == '\n' && open != -1 && close != -1 && colon != -1 &&
open < colon && colon < close)
{
String fileStr = s.substring(open + 1, colon);
if (!fileStr.endsWith(".java")) {
String lineStr = s.substring(colon + 1, close);
try {
linep[0] = Integer.parseInt(lineStr);
if (linep[0] < 0) {
linep[0] = 0;
}
return fileStr;
}
catch (NumberFormatException e) {
// fall through
}
}
open = close = colon = -1;
}
}
return null;
}
RegExpProxy getRegExpProxy()
{
if (regExpProxy == null) {
Class cl = Kit.classOrNull(
"org.mozilla.javascript.regexp.RegExpImpl");
if (cl != null) {
regExpProxy = (RegExpProxy)Kit.newInstanceOrNull(cl);
}
}
return regExpProxy;
}
final boolean isVersionECMA1()
{
return version == VERSION_DEFAULT || version >= VERSION_1_3;
}
// The method must NOT be public or protected
SecurityController getSecurityController()
{
SecurityController global = SecurityController.global();
if (global != null) {
return global;
}
return securityController;
}
public final boolean isGeneratingDebugChanged()
{
return generatingDebugChanged;
}
/**
* Add a name to the list of names forcing the creation of real
* activation objects for functions.
*
* @param name the name of the object to add to the list
*/
public void addActivationName(String name)
{
if (sealed) onSealedMutation();
if (activationNames == null)
activationNames = new Hashtable(5);
activationNames.put(name, name);
}
/**
* Check whether the name is in the list of names of objects
* forcing the creation of activation objects.
*
* @param name the name of the object to test
*
* @return true if an function activation object is needed.
*/
public final boolean isActivationNeeded(String name)
{
return activationNames != null && activationNames.containsKey(name);
}
/**
* Remove a name from the list of names forcing the creation of real
* activation objects for functions.
*
* @param name the name of the object to remove from the list
*/
public void removeActivationName(String name)
{
if (sealed) onSealedMutation();
if (activationNames != null)
activationNames.remove(name);
}
private static String implementationVersion;
private final ContextFactory factory;
private boolean sealed;
private Object sealKey;
Scriptable topCallScope;
NativeCall currentActivationCall;
XMLLib cachedXMLLib;
// for Objects, Arrays to tag themselves as being printed out,
// so they don't print themselves out recursively.
// Use ObjToIntMap instead of java.util.HashSet for JDK 1.1 compatibility
ObjToIntMap iterating;
Object interpreterSecurityDomain;
int version;
private SecurityController securityController;
private ClassShutter classShutter;
private ErrorReporter errorReporter;
RegExpProxy regExpProxy;
private Locale locale;
private boolean generatingDebug;
private boolean generatingDebugChanged;
private boolean generatingSource=true;
boolean compileFunctionsWithDynamicScopeFlag;
boolean useDynamicScope;
private int optimizationLevel;
private int maximumInterpreterStackDepth;
private WrapFactory wrapFactory;
Debugger debugger;
private Object debuggerData;
private int enterCount;
private Object propertyListeners;
private Hashtable hashtable;
private ClassLoader applicationClassLoader;
/**
* This is the list of names of objects forcing the creation of
* function activation records.
*/
Hashtable activationNames;
// For the interpreter to store the last frame for error reports etc.
Object lastInterpreterFrame;
// For the interpreter to store information about previous invocations
// interpreter invocations
ObjArray previousInterpreterInvocations;
// For instruction counting (interpreter only)
int instructionCount;
int instructionThreshold;
// It can be used to return the second index-like result from function
int scratchIndex;
// It can be used to return the second uint32 result from function
long scratchUint32;
// It can be used to return the second Scriptable result from function
Scriptable scratchScriptable;
// Generate an observer count on compiled code
public boolean generateObserverCount = false;
}